Version: dev

ExponentialGrid

Exponential vertical grid discretization for pervious and impervious road columns in the urban canopy thermal solver. Computes node depths (Oleson Eq. 4.8), layer thicknesses (Eq. 4.6), and interface depths (Eq. 4.7) with a scaling factor f_s = 0.025 m that produces finer resolution near the surface — a requirement because urban roads overlie real soil and the near-surface diurnal cycle dominates thermal exchange. Migrated from UrbanCanopy.jl@9200edd4 src/roof_wall_road_snow_temperatures.jl:890 (`ExponentialGrid` @component). Implements Oleson et al. (2010, CLM Tech Note Ch. 4 §4.1, Eqs. 4.6-4.8). Parameterized as observed-only: f_s is the only input parameter; z_node_i, dz_layer_i, z_interface_j are algebraic outputs. Upstream `N` (number of layers) is fixed to its canonical default 15 for serialization (per-N templating lives outside the ESM scalar schema and is not needed for the urban-canopy use case).

Domain
urban_canopy
Subdomain
temps
Type
model
.esm schema
1.0.0
Source
components/urban_canopy/temps/exponential_grid.esm
Tags
#urban_canopy #temps #model #temperatures #grid #discretization #observed-only #clmu

Description

Exponential vertical grid discretization for pervious and impervious road columns in the urban canopy thermal solver. Computes node depths (Oleson Eq. 4.8), layer thicknesses (Eq. 4.6), and interface depths (Eq. 4.7) with a scaling factor f_s = 0.025 m that produces finer resolution near the surface — a requirement because urban roads overlie real soil and the near-surface diurnal cycle dominates thermal exchange. Migrated from UrbanCanopy.jl@9200edd4 src/roof_wall_road_snow_temperatures.jl:890 (ExponentialGrid @component). Implements Oleson et al. (2010, CLM Tech Note Ch. 4 §4.1, Eqs. 4.6-4.8). Parameterized as observed-only: f_s is the only input parameter; z_node_i, dz_layer_i, z_interface_j are algebraic outputs. Upstream N (number of layers) is fixed to its canonical default 15 for serialization (per-N templating lives outside the ESM scalar schema and is not needed for the urban-canopy use case).

Reference

_source_ref: UrbanCanopy.jl@9200edd4f15609f20339d3a7d104de8064544310:src/roof_wall_road_snow_temperatures.jl:890 Upstream ExponentialGrid is an MTK @component parameterized by N (default 15) declaring three arrays of @variables: z_node(t)[1:N], Δz_layer(t)[1:N], z_interface(t)[1:N+1] (with N+1 entries where index j maps to paper i=j-1). The single @constants declaration is f_s = 0.025 m, the scaling factor of Oleson Eq. 4.8.

For ESM serialization N is fixed to the canonical upstream default of 15; the .esm encodes the full unfolded scalar system: 15 z_node states, 15 dz_layer states, 16 z_interface states (46 algebraic equations total). This matches the unfold-to-scalar convention adopted by the other urban-canopy temps migrations in this rig (e.g. snow_melt_no_layers.esm, total_phase_change_energy.esm). The upstream Julia tests (roof_wall_road_snow_temperatures_test.jl:978-1036) exercise multiple N values (3, 5, 15); the inner-layer formulas are identical for any N (only dz_layer_15 and z_interface_16 depend explicitly on N), so the assertion values pinned here at N=15 are also exact for any larger N choice — i.e. moving to a templated representation in the future will not invalidate the per-layer reference values, only extend them.

Unicode Δ in Δz_layer is renamed dz_layer for ASCII identifier names per the urban-canopy convention (matches snow_density.esm renaming ρ → rho); the ESM identifier-pattern allows unicode but the rig-wide ASCII convention keeps editor and tooling support uniform.

Observability classification: this component is functionally observed-only — every output state is given by a single algebraic equation in terms of the parameter f_s and other state outputs (the (t) time index has no role since there is no derivative anywhere). Test scenarios use time_span: [0, 1] and sample at t=0 to mirror the convention used by sibling observed-only files (snow_melt_no_layers.esm, total_phase_change_energy.esm, snow_density.esm).

Tolerance rationale (model-level rel=1e-9): observed-only algebraic system (no ODE integration); the only non-trivial roundoff path is the chain exp(0.5*(i-0.5)) → subtraction by 1 → multiplication by f_s → linear combination — IEEE-754 ULP precision throughout. 1e-9 leaves comfortable headroom above ULP roundoff and matches the convention used by sibling observed-only files in this rig (total_phase_change_energy.esm, etc.). The deepest layer (z_node_15 = ~35.18 m) carries the largest absolute roundoff but the relative error stays well below 1e-9. Tighter abs: 1.0e-12 is asserted on z_interface_1 = 0 (exactly zero by construction) so the assertion does not silently accept a stuck-at-1e-3 regression._

Parameters

NameUnitsDefaultDescription
f_sm0.025Scaling factor for road grid (Oleson et al. 2010 Eq. 4.8)

Observed

NameUnitsDefaultDescription
z_node_1mNode depth of layer 1 (Oleson Eq. 4.8): z_1 = f_s * (exp(0.5*(1 - 0.5)) - 1)
z_node_2mNode depth of layer 2 (Oleson Eq. 4.8): z_2 = f_s * (exp(0.5*(2 - 0.5)) - 1)
z_node_3mNode depth of layer 3 (Oleson Eq. 4.8): z_3 = f_s * (exp(0.5*(3 - 0.5)) - 1)
z_node_4mNode depth of layer 4 (Oleson Eq. 4.8): z_4 = f_s * (exp(0.5*(4 - 0.5)) - 1)
z_node_5mNode depth of layer 5 (Oleson Eq. 4.8): z_5 = f_s * (exp(0.5*(5 - 0.5)) - 1)
z_node_6mNode depth of layer 6 (Oleson Eq. 4.8): z_6 = f_s * (exp(0.5*(6 - 0.5)) - 1)
z_node_7mNode depth of layer 7 (Oleson Eq. 4.8): z_7 = f_s * (exp(0.5*(7 - 0.5)) - 1)
z_node_8mNode depth of layer 8 (Oleson Eq. 4.8): z_8 = f_s * (exp(0.5*(8 - 0.5)) - 1)
z_node_9mNode depth of layer 9 (Oleson Eq. 4.8): z_9 = f_s * (exp(0.5*(9 - 0.5)) - 1)
z_node_10mNode depth of layer 10 (Oleson Eq. 4.8): z_10 = f_s * (exp(0.5*(10 - 0.5)) - 1)
z_node_11mNode depth of layer 11 (Oleson Eq. 4.8): z_11 = f_s * (exp(0.5*(11 - 0.5)) - 1)
z_node_12mNode depth of layer 12 (Oleson Eq. 4.8): z_12 = f_s * (exp(0.5*(12 - 0.5)) - 1)
z_node_13mNode depth of layer 13 (Oleson Eq. 4.8): z_13 = f_s * (exp(0.5*(13 - 0.5)) - 1)
z_node_14mNode depth of layer 14 (Oleson Eq. 4.8): z_14 = f_s * (exp(0.5*(14 - 0.5)) - 1)
z_node_15mNode depth of layer 15 (Oleson Eq. 4.8): z_15 = f_s * (exp(0.5*(15 - 0.5)) - 1)
dz_layer_1mLayer thickness 1 (Oleson Eq. 4.6, top): 0.5 * (z_node_1 + z_node_2)
dz_layer_2mLayer thickness 2 (Oleson Eq. 4.6, interior): 0.5 * (z_node_3 - z_node_1)
dz_layer_3mLayer thickness 3 (Oleson Eq. 4.6, interior): 0.5 * (z_node_4 - z_node_2)
dz_layer_4mLayer thickness 4 (Oleson Eq. 4.6, interior): 0.5 * (z_node_5 - z_node_3)
dz_layer_5mLayer thickness 5 (Oleson Eq. 4.6, interior): 0.5 * (z_node_6 - z_node_4)
dz_layer_6mLayer thickness 6 (Oleson Eq. 4.6, interior): 0.5 * (z_node_7 - z_node_5)
dz_layer_7mLayer thickness 7 (Oleson Eq. 4.6, interior): 0.5 * (z_node_8 - z_node_6)
dz_layer_8mLayer thickness 8 (Oleson Eq. 4.6, interior): 0.5 * (z_node_9 - z_node_7)
dz_layer_9mLayer thickness 9 (Oleson Eq. 4.6, interior): 0.5 * (z_node_10 - z_node_8)
dz_layer_10mLayer thickness 10 (Oleson Eq. 4.6, interior): 0.5 * (z_node_11 - z_node_9)
dz_layer_11mLayer thickness 11 (Oleson Eq. 4.6, interior): 0.5 * (z_node_12 - z_node_10)
dz_layer_12mLayer thickness 12 (Oleson Eq. 4.6, interior): 0.5 * (z_node_13 - z_node_11)
dz_layer_13mLayer thickness 13 (Oleson Eq. 4.6, interior): 0.5 * (z_node_14 - z_node_12)
dz_layer_14mLayer thickness 14 (Oleson Eq. 4.6, interior): 0.5 * (z_node_15 - z_node_13)
dz_layer_15mLayer thickness 15 (Oleson Eq. 4.6, bottom): z_node_15 - z_node_14
z_interface_1mInterface depth z_{h,0} = 0 (Oleson Eq. 4.7, surface)
z_interface_2mInterface depth z_{h,1} = 0.5 * (z_node_1 + z_node_2) (Oleson Eq. 4.7)
z_interface_3mInterface depth z_{h,2} = 0.5 * (z_node_2 + z_node_3) (Oleson Eq. 4.7)
z_interface_4mInterface depth z_{h,3} = 0.5 * (z_node_3 + z_node_4) (Oleson Eq. 4.7)
z_interface_5mInterface depth z_{h,4} = 0.5 * (z_node_4 + z_node_5) (Oleson Eq. 4.7)
z_interface_6mInterface depth z_{h,5} = 0.5 * (z_node_5 + z_node_6) (Oleson Eq. 4.7)
z_interface_7mInterface depth z_{h,6} = 0.5 * (z_node_6 + z_node_7) (Oleson Eq. 4.7)
z_interface_8mInterface depth z_{h,7} = 0.5 * (z_node_7 + z_node_8) (Oleson Eq. 4.7)
z_interface_9mInterface depth z_{h,8} = 0.5 * (z_node_8 + z_node_9) (Oleson Eq. 4.7)
z_interface_10mInterface depth z_{h,9} = 0.5 * (z_node_9 + z_node_10) (Oleson Eq. 4.7)
z_interface_11mInterface depth z_{h,10} = 0.5 * (z_node_10 + z_node_11) (Oleson Eq. 4.7)
z_interface_12mInterface depth z_{h,11} = 0.5 * (z_node_11 + z_node_12) (Oleson Eq. 4.7)
z_interface_13mInterface depth z_{h,12} = 0.5 * (z_node_12 + z_node_13) (Oleson Eq. 4.7)
z_interface_14mInterface depth z_{h,13} = 0.5 * (z_node_13 + z_node_14) (Oleson Eq. 4.7)
z_interface_15mInterface depth z_{h,14} = 0.5 * (z_node_14 + z_node_15) (Oleson Eq. 4.7)
z_interface_16mInterface depth z_{h,15} = z_node_15 + 0.5 * dz_layer_15 (Oleson Eq. 4.7, bottom)

Observed expressions

$$ z_{node\,1} = f_{s} \cdot \left(e^{0.25} - 1.0\right) $$

Node depth of layer 1 (Oleson Eq. 4.8): z_1 = f_s * (exp(0.5*(1 - 0.5)) - 1)

$$ z_{node\,2} = f_{s} \cdot \left(e^{0.75} - 1.0\right) $$

Node depth of layer 2 (Oleson Eq. 4.8): z_2 = f_s * (exp(0.5*(2 - 0.5)) - 1)

$$ z_{node\,3} = f_{s} \cdot \left(e^{1.25} - 1.0\right) $$

Node depth of layer 3 (Oleson Eq. 4.8): z_3 = f_s * (exp(0.5*(3 - 0.5)) - 1)

$$ z_{node\,4} = f_{s} \cdot \left(e^{1.75} - 1.0\right) $$

Node depth of layer 4 (Oleson Eq. 4.8): z_4 = f_s * (exp(0.5*(4 - 0.5)) - 1)

$$ z_{node\,5} = f_{s} \cdot \left(e^{2.25} - 1.0\right) $$

Node depth of layer 5 (Oleson Eq. 4.8): z_5 = f_s * (exp(0.5*(5 - 0.5)) - 1)

$$ z_{node\,6} = f_{s} \cdot \left(e^{2.75} - 1.0\right) $$

Node depth of layer 6 (Oleson Eq. 4.8): z_6 = f_s * (exp(0.5*(6 - 0.5)) - 1)

$$ z_{node\,7} = f_{s} \cdot \left(e^{3.25} - 1.0\right) $$

Node depth of layer 7 (Oleson Eq. 4.8): z_7 = f_s * (exp(0.5*(7 - 0.5)) - 1)

$$ z_{node\,8} = f_{s} \cdot \left(e^{3.75} - 1.0\right) $$

Node depth of layer 8 (Oleson Eq. 4.8): z_8 = f_s * (exp(0.5*(8 - 0.5)) - 1)

$$ z_{node\,9} = f_{s} \cdot \left(e^{4.25} - 1.0\right) $$

Node depth of layer 9 (Oleson Eq. 4.8): z_9 = f_s * (exp(0.5*(9 - 0.5)) - 1)

$$ z_{node\,10} = f_{s} \cdot \left(e^{4.75} - 1.0\right) $$

Node depth of layer 10 (Oleson Eq. 4.8): z_10 = f_s * (exp(0.5*(10 - 0.5)) - 1)

$$ z_{node\,11} = f_{s} \cdot \left(e^{5.25} - 1.0\right) $$

Node depth of layer 11 (Oleson Eq. 4.8): z_11 = f_s * (exp(0.5*(11 - 0.5)) - 1)

$$ z_{node\,12} = f_{s} \cdot \left(e^{5.75} - 1.0\right) $$

Node depth of layer 12 (Oleson Eq. 4.8): z_12 = f_s * (exp(0.5*(12 - 0.5)) - 1)

$$ z_{node\,13} = f_{s} \cdot \left(e^{6.25} - 1.0\right) $$

Node depth of layer 13 (Oleson Eq. 4.8): z_13 = f_s * (exp(0.5*(13 - 0.5)) - 1)

$$ z_{node\,14} = f_{s} \cdot \left(e^{6.75} - 1.0\right) $$

Node depth of layer 14 (Oleson Eq. 4.8): z_14 = f_s * (exp(0.5*(14 - 0.5)) - 1)

$$ z_{node\,15} = f_{s} \cdot \left(e^{7.25} - 1.0\right) $$

Node depth of layer 15 (Oleson Eq. 4.8): z_15 = f_s * (exp(0.5*(15 - 0.5)) - 1)

$$ dz_{layer\,1} = 0.5 \cdot \left(z_{node\,1} + z_{node\,2}\right) $$

Layer thickness 1 (Oleson Eq. 4.6, top): 0.5 * (z_node_1 + z_node_2)

$$ dz_{layer\,2} = 0.5 \cdot \left(z_{node\,3} - z_{node\,1}\right) $$

Layer thickness 2 (Oleson Eq. 4.6, interior): 0.5 * (z_node_3 - z_node_1)

$$ dz_{layer\,3} = 0.5 \cdot \left(z_{node\,4} - z_{node\,2}\right) $$

Layer thickness 3 (Oleson Eq. 4.6, interior): 0.5 * (z_node_4 - z_node_2)

$$ dz_{layer\,4} = 0.5 \cdot \left(z_{node\,5} - z_{node\,3}\right) $$

Layer thickness 4 (Oleson Eq. 4.6, interior): 0.5 * (z_node_5 - z_node_3)

$$ dz_{layer\,5} = 0.5 \cdot \left(z_{node\,6} - z_{node\,4}\right) $$

Layer thickness 5 (Oleson Eq. 4.6, interior): 0.5 * (z_node_6 - z_node_4)

$$ dz_{layer\,6} = 0.5 \cdot \left(z_{node\,7} - z_{node\,5}\right) $$

Layer thickness 6 (Oleson Eq. 4.6, interior): 0.5 * (z_node_7 - z_node_5)

$$ dz_{layer\,7} = 0.5 \cdot \left(z_{node\,8} - z_{node\,6}\right) $$

Layer thickness 7 (Oleson Eq. 4.6, interior): 0.5 * (z_node_8 - z_node_6)

$$ dz_{layer\,8} = 0.5 \cdot \left(z_{node\,9} - z_{node\,7}\right) $$

Layer thickness 8 (Oleson Eq. 4.6, interior): 0.5 * (z_node_9 - z_node_7)

$$ dz_{layer\,9} = 0.5 \cdot \left(z_{node\,10} - z_{node\,8}\right) $$

Layer thickness 9 (Oleson Eq. 4.6, interior): 0.5 * (z_node_10 - z_node_8)

$$ dz_{layer\,10} = 0.5 \cdot \left(z_{node\,11} - z_{node\,9}\right) $$

Layer thickness 10 (Oleson Eq. 4.6, interior): 0.5 * (z_node_11 - z_node_9)

$$ dz_{layer\,11} = 0.5 \cdot \left(z_{node\,12} - z_{node\,10}\right) $$

Layer thickness 11 (Oleson Eq. 4.6, interior): 0.5 * (z_node_12 - z_node_10)

$$ dz_{layer\,12} = 0.5 \cdot \left(z_{node\,13} - z_{node\,11}\right) $$

Layer thickness 12 (Oleson Eq. 4.6, interior): 0.5 * (z_node_13 - z_node_11)

$$ dz_{layer\,13} = 0.5 \cdot \left(z_{node\,14} - z_{node\,12}\right) $$

Layer thickness 13 (Oleson Eq. 4.6, interior): 0.5 * (z_node_14 - z_node_12)

$$ dz_{layer\,14} = 0.5 \cdot \left(z_{node\,15} - z_{node\,13}\right) $$

Layer thickness 14 (Oleson Eq. 4.6, interior): 0.5 * (z_node_15 - z_node_13)

$$ dz_{layer\,15} = z_{node\,15} - z_{node\,14} $$

Layer thickness 15 (Oleson Eq. 4.6, bottom): z_node_15 - z_node_14

$$ z_{interface\,1} = 0.0 $$

Interface depth z{h,0} = 0 (Oleson Eq. 4.7, surface)_

$$ z_{interface\,2} = 0.5 \cdot \left(z_{node\,1} + z_{node\,2}\right) $$

Interface depth z{h,1} = 0.5 * (z_node_1 + z_node_2) (Oleson Eq. 4.7)_

$$ z_{interface\,3} = 0.5 \cdot \left(z_{node\,2} + z_{node\,3}\right) $$

Interface depth z{h,2} = 0.5 * (z_node_2 + z_node_3) (Oleson Eq. 4.7)_

$$ z_{interface\,4} = 0.5 \cdot \left(z_{node\,3} + z_{node\,4}\right) $$

Interface depth z{h,3} = 0.5 * (z_node_3 + z_node_4) (Oleson Eq. 4.7)_

$$ z_{interface\,5} = 0.5 \cdot \left(z_{node\,4} + z_{node\,5}\right) $$

Interface depth z{h,4} = 0.5 * (z_node_4 + z_node_5) (Oleson Eq. 4.7)_

$$ z_{interface\,6} = 0.5 \cdot \left(z_{node\,5} + z_{node\,6}\right) $$

Interface depth z{h,5} = 0.5 * (z_node_5 + z_node_6) (Oleson Eq. 4.7)_

$$ z_{interface\,7} = 0.5 \cdot \left(z_{node\,6} + z_{node\,7}\right) $$

Interface depth z{h,6} = 0.5 * (z_node_6 + z_node_7) (Oleson Eq. 4.7)_

$$ z_{interface\,8} = 0.5 \cdot \left(z_{node\,7} + z_{node\,8}\right) $$

Interface depth z{h,7} = 0.5 * (z_node_7 + z_node_8) (Oleson Eq. 4.7)_

$$ z_{interface\,9} = 0.5 \cdot \left(z_{node\,8} + z_{node\,9}\right) $$

Interface depth z{h,8} = 0.5 * (z_node_8 + z_node_9) (Oleson Eq. 4.7)_

$$ z_{interface\,10} = 0.5 \cdot \left(z_{node\,9} + z_{node\,10}\right) $$

Interface depth z{h,9} = 0.5 * (z_node_9 + z_node_10) (Oleson Eq. 4.7)_

$$ z_{interface\,11} = 0.5 \cdot \left(z_{node\,10} + z_{node\,11}\right) $$

Interface depth z{h,10} = 0.5 * (z_node_10 + z_node_11) (Oleson Eq. 4.7)_

$$ z_{interface\,12} = 0.5 \cdot \left(z_{node\,11} + z_{node\,12}\right) $$

Interface depth z{h,11} = 0.5 * (z_node_11 + z_node_12) (Oleson Eq. 4.7)_

$$ z_{interface\,13} = 0.5 \cdot \left(z_{node\,12} + z_{node\,13}\right) $$

Interface depth z{h,12} = 0.5 * (z_node_12 + z_node_13) (Oleson Eq. 4.7)_

$$ z_{interface\,14} = 0.5 \cdot \left(z_{node\,13} + z_{node\,14}\right) $$

Interface depth z{h,13} = 0.5 * (z_node_13 + z_node_14) (Oleson Eq. 4.7)_

$$ z_{interface\,15} = 0.5 \cdot \left(z_{node\,14} + z_{node\,15}\right) $$

Interface depth z{h,14} = 0.5 * (z_node_14 + z_node_15) (Oleson Eq. 4.7)_

$$ z_{interface\,16} = z_{node\,15} + 0.5 \cdot dz_{layer\,15} $$

Interface depth z{h,15} = z_node_15 + 0.5 * dz_layer_15 (Oleson Eq. 4.7, bottom)_

Raw .esm

Raw .esm JSON (this component)
{
  "reference": {
    "notes": "source_ref: UrbanCanopy.jl@9200edd4f15609f20339d3a7d104de8064544310:src/roof_wall_road_snow_temperatures.jl:890\nUpstream `ExponentialGrid` is an MTK `@component` parameterized by N (default 15) declaring three arrays of `@variables`: z_node(t)[1:N], Δz_layer(t)[1:N], z_interface(t)[1:N+1] (with N+1 entries where index j maps to paper i=j-1). The single `@constants` declaration is f_s = 0.025 m, the scaling factor of Oleson Eq. 4.8.\n\nFor ESM serialization N is fixed to the canonical upstream default of 15; the .esm encodes the full unfolded scalar system: 15 z_node states, 15 dz_layer states, 16 z_interface states (46 algebraic equations total). This matches the unfold-to-scalar convention adopted by the other urban-canopy temps migrations in this rig (e.g. snow_melt_no_layers.esm, total_phase_change_energy.esm). The upstream Julia tests (roof_wall_road_snow_temperatures_test.jl:978-1036) exercise multiple N values (3, 5, 15); the inner-layer formulas are identical for any N (only dz_layer_15 and z_interface_16 depend explicitly on N), so the assertion values pinned here at N=15 are also exact for any larger N choice — i.e. moving to a templated representation in the future will not invalidate the per-layer reference values, only extend them.\n\nUnicode `Δ` in `Δz_layer` is renamed `dz_layer` for ASCII identifier names per the urban-canopy convention (matches snow_density.esm renaming `ρ` → `rho`); the ESM identifier-pattern allows unicode but the rig-wide ASCII convention keeps editor and tooling support uniform.\n\nObservability classification: this component is functionally observed-only — every output state is given by a single algebraic equation in terms of the parameter f_s and other state outputs (the `(t)` time index has no role since there is no derivative anywhere). Test scenarios use `time_span: [0, 1]` and sample at t=0 to mirror the convention used by sibling observed-only files (snow_melt_no_layers.esm, total_phase_change_energy.esm, snow_density.esm).\n\nTolerance rationale (model-level rel=1e-9): observed-only algebraic system (no ODE integration); the only non-trivial roundoff path is the chain `exp(0.5*(i-0.5))` → subtraction by 1 → multiplication by f_s → linear combination — IEEE-754 ULP precision throughout. 1e-9 leaves comfortable headroom above ULP roundoff and matches the convention used by sibling observed-only files in this rig (total_phase_change_energy.esm, etc.). The deepest layer (z_node_15 = ~35.18 m) carries the largest absolute roundoff but the relative error stays well below 1e-9. Tighter `abs: 1.0e-12` is asserted on z_interface_1 = 0 (exactly zero by construction) so the assertion does not silently accept a stuck-at-1e-3 regression."
  },
  "tolerance": {
    "rel": 1e-09
  },
  "variables": {
    "f_s": {
      "type": "parameter",
      "units": "m",
      "default": 0.025,
      "description": "Scaling factor for road grid (Oleson et al. 2010 Eq. 4.8)"
    },
    "z_node_1": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 1 (Oleson Eq. 4.8): z_1 = f_s * (exp(0.5*(1 - 0.5)) - 1)"
    },
    "z_node_2": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 2 (Oleson Eq. 4.8): z_2 = f_s * (exp(0.5*(2 - 0.5)) - 1)"
    },
    "z_node_3": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 3 (Oleson Eq. 4.8): z_3 = f_s * (exp(0.5*(3 - 0.5)) - 1)"
    },
    "z_node_4": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 4 (Oleson Eq. 4.8): z_4 = f_s * (exp(0.5*(4 - 0.5)) - 1)"
    },
    "z_node_5": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 5 (Oleson Eq. 4.8): z_5 = f_s * (exp(0.5*(5 - 0.5)) - 1)"
    },
    "z_node_6": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 6 (Oleson Eq. 4.8): z_6 = f_s * (exp(0.5*(6 - 0.5)) - 1)"
    },
    "z_node_7": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 7 (Oleson Eq. 4.8): z_7 = f_s * (exp(0.5*(7 - 0.5)) - 1)"
    },
    "z_node_8": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 8 (Oleson Eq. 4.8): z_8 = f_s * (exp(0.5*(8 - 0.5)) - 1)"
    },
    "z_node_9": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 9 (Oleson Eq. 4.8): z_9 = f_s * (exp(0.5*(9 - 0.5)) - 1)"
    },
    "z_node_10": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 10 (Oleson Eq. 4.8): z_10 = f_s * (exp(0.5*(10 - 0.5)) - 1)"
    },
    "z_node_11": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 11 (Oleson Eq. 4.8): z_11 = f_s * (exp(0.5*(11 - 0.5)) - 1)"
    },
    "z_node_12": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 12 (Oleson Eq. 4.8): z_12 = f_s * (exp(0.5*(12 - 0.5)) - 1)"
    },
    "z_node_13": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 13 (Oleson Eq. 4.8): z_13 = f_s * (exp(0.5*(13 - 0.5)) - 1)"
    },
    "z_node_14": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 14 (Oleson Eq. 4.8): z_14 = f_s * (exp(0.5*(14 - 0.5)) - 1)"
    },
    "z_node_15": {
      "type": "unknown",
      "units": "m",
      "description": "Node depth of layer 15 (Oleson Eq. 4.8): z_15 = f_s * (exp(0.5*(15 - 0.5)) - 1)"
    },
    "dz_layer_1": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 1 (Oleson Eq. 4.6, top): 0.5 * (z_node_1 + z_node_2)"
    },
    "dz_layer_2": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 2 (Oleson Eq. 4.6, interior): 0.5 * (z_node_3 - z_node_1)"
    },
    "dz_layer_3": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 3 (Oleson Eq. 4.6, interior): 0.5 * (z_node_4 - z_node_2)"
    },
    "dz_layer_4": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 4 (Oleson Eq. 4.6, interior): 0.5 * (z_node_5 - z_node_3)"
    },
    "dz_layer_5": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 5 (Oleson Eq. 4.6, interior): 0.5 * (z_node_6 - z_node_4)"
    },
    "dz_layer_6": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 6 (Oleson Eq. 4.6, interior): 0.5 * (z_node_7 - z_node_5)"
    },
    "dz_layer_7": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 7 (Oleson Eq. 4.6, interior): 0.5 * (z_node_8 - z_node_6)"
    },
    "dz_layer_8": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 8 (Oleson Eq. 4.6, interior): 0.5 * (z_node_9 - z_node_7)"
    },
    "dz_layer_9": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 9 (Oleson Eq. 4.6, interior): 0.5 * (z_node_10 - z_node_8)"
    },
    "dz_layer_10": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 10 (Oleson Eq. 4.6, interior): 0.5 * (z_node_11 - z_node_9)"
    },
    "dz_layer_11": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 11 (Oleson Eq. 4.6, interior): 0.5 * (z_node_12 - z_node_10)"
    },
    "dz_layer_12": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 12 (Oleson Eq. 4.6, interior): 0.5 * (z_node_13 - z_node_11)"
    },
    "dz_layer_13": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 13 (Oleson Eq. 4.6, interior): 0.5 * (z_node_14 - z_node_12)"
    },
    "dz_layer_14": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 14 (Oleson Eq. 4.6, interior): 0.5 * (z_node_15 - z_node_13)"
    },
    "dz_layer_15": {
      "type": "unknown",
      "units": "m",
      "description": "Layer thickness 15 (Oleson Eq. 4.6, bottom): z_node_15 - z_node_14"
    },
    "z_interface_1": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,0} = 0 (Oleson Eq. 4.7, surface)"
    },
    "z_interface_2": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,1} = 0.5 * (z_node_1 + z_node_2) (Oleson Eq. 4.7)"
    },
    "z_interface_3": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,2} = 0.5 * (z_node_2 + z_node_3) (Oleson Eq. 4.7)"
    },
    "z_interface_4": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,3} = 0.5 * (z_node_3 + z_node_4) (Oleson Eq. 4.7)"
    },
    "z_interface_5": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,4} = 0.5 * (z_node_4 + z_node_5) (Oleson Eq. 4.7)"
    },
    "z_interface_6": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,5} = 0.5 * (z_node_5 + z_node_6) (Oleson Eq. 4.7)"
    },
    "z_interface_7": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,6} = 0.5 * (z_node_6 + z_node_7) (Oleson Eq. 4.7)"
    },
    "z_interface_8": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,7} = 0.5 * (z_node_7 + z_node_8) (Oleson Eq. 4.7)"
    },
    "z_interface_9": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,8} = 0.5 * (z_node_8 + z_node_9) (Oleson Eq. 4.7)"
    },
    "z_interface_10": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,9} = 0.5 * (z_node_9 + z_node_10) (Oleson Eq. 4.7)"
    },
    "z_interface_11": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,10} = 0.5 * (z_node_10 + z_node_11) (Oleson Eq. 4.7)"
    },
    "z_interface_12": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,11} = 0.5 * (z_node_11 + z_node_12) (Oleson Eq. 4.7)"
    },
    "z_interface_13": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,12} = 0.5 * (z_node_12 + z_node_13) (Oleson Eq. 4.7)"
    },
    "z_interface_14": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,13} = 0.5 * (z_node_13 + z_node_14) (Oleson Eq. 4.7)"
    },
    "z_interface_15": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,14} = 0.5 * (z_node_14 + z_node_15) (Oleson Eq. 4.7)"
    },
    "z_interface_16": {
      "type": "unknown",
      "units": "m",
      "description": "Interface depth z_{h,15} = z_node_15 + 0.5 * dz_layer_15 (Oleson Eq. 4.7, bottom)"
    }
  },
  "equations": [
    {
      "lhs": "z_node_1",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  0.25
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_node_2",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  0.75
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_node_3",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  1.25
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_node_4",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  1.75
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_node_5",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  2.25
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_node_6",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  2.75
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_node_7",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  3.25
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_node_8",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  3.75
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_node_9",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  4.25
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_node_10",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  4.75
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_node_11",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  5.25
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_node_12",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  5.75
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_node_13",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  6.25
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_node_14",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  6.75
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_node_15",
      "rhs": {
        "op": "*",
        "args": [
          "f_s",
          {
            "op": "-",
            "args": [
              {
                "op": "exp",
                "args": [
                  7.25
                ]
              },
              1.0
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_1",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_1",
              "z_node_2"
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_2",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "-",
            "args": [
              "z_node_3",
              "z_node_1"
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_3",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "-",
            "args": [
              "z_node_4",
              "z_node_2"
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_4",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "-",
            "args": [
              "z_node_5",
              "z_node_3"
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_5",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "-",
            "args": [
              "z_node_6",
              "z_node_4"
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_6",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "-",
            "args": [
              "z_node_7",
              "z_node_5"
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_7",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "-",
            "args": [
              "z_node_8",
              "z_node_6"
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_8",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "-",
            "args": [
              "z_node_9",
              "z_node_7"
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_9",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "-",
            "args": [
              "z_node_10",
              "z_node_8"
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_10",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "-",
            "args": [
              "z_node_11",
              "z_node_9"
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_11",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "-",
            "args": [
              "z_node_12",
              "z_node_10"
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_12",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "-",
            "args": [
              "z_node_13",
              "z_node_11"
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_13",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "-",
            "args": [
              "z_node_14",
              "z_node_12"
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_14",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "-",
            "args": [
              "z_node_15",
              "z_node_13"
            ]
          }
        ]
      }
    },
    {
      "lhs": "dz_layer_15",
      "rhs": {
        "op": "-",
        "args": [
          "z_node_15",
          "z_node_14"
        ]
      }
    },
    {
      "lhs": "z_interface_1",
      "rhs": 0.0
    },
    {
      "lhs": "z_interface_2",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_1",
              "z_node_2"
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_interface_3",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_2",
              "z_node_3"
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_interface_4",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_3",
              "z_node_4"
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_interface_5",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_4",
              "z_node_5"
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_interface_6",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_5",
              "z_node_6"
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_interface_7",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_6",
              "z_node_7"
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_interface_8",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_7",
              "z_node_8"
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_interface_9",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_8",
              "z_node_9"
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_interface_10",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_9",
              "z_node_10"
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_interface_11",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_10",
              "z_node_11"
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_interface_12",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_11",
              "z_node_12"
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_interface_13",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_12",
              "z_node_13"
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_interface_14",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_13",
              "z_node_14"
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_interface_15",
      "rhs": {
        "op": "*",
        "args": [
          0.5,
          {
            "op": "+",
            "args": [
              "z_node_14",
              "z_node_15"
            ]
          }
        ]
      }
    },
    {
      "lhs": "z_interface_16",
      "rhs": {
        "op": "+",
        "args": [
          "z_node_15",
          {
            "op": "*",
            "args": [
              0.5,
              "dz_layer_15"
            ]
          }
        ]
      }
    }
  ],
  "tests": [
    {
      "id": "z_node_full_column",
      "description": "Verifies Oleson Eq. 4.8 across all 15 grid layers: z_i = f_s * (exp(0.5*(i-0.5)) - 1) with f_s = 0.025 m. Reference values computed directly from the closed-form expression and matched to the upstream Julia component which integrates the same equation symbolically. This anchors the layer-node depth profile that drives the road-soil thermal grid in CLMU.",
      "parameter_overrides": {},
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "z_node_1",
          "time": 0.0,
          "expected": 0.007100635417193535
        },
        {
          "variable": "z_node_2",
          "time": 0.0,
          "expected": 0.02792500041531687
        },
        {
          "variable": "z_node_3",
          "time": 0.0,
          "expected": 0.06225857393654604
        },
        {
          "variable": "z_node_4",
          "time": 0.0,
          "expected": 0.11886506690014327
        },
        {
          "variable": "z_node_5",
          "time": 0.0,
          "expected": 0.21219339590896316
        },
        {
          "variable": "z_node_6",
          "time": 0.0,
          "expected": 0.3660657971047043
        },
        {
          "variable": "z_node_7",
          "time": 0.0,
          "expected": 0.6197584979298266
        },
        {
          "variable": "z_node_8",
          "time": 0.0,
          "expected": 1.0380270500015696
        },
        {
          "variable": "z_node_9",
          "time": 0.0,
          "expected": 1.7276353086671965
        },
        {
          "variable": "z_node_10",
          "time": 0.0,
          "expected": 2.8646071131796917
        },
        {
          "variable": "z_node_11",
          "time": 0.0,
          "expected": 4.73915671146575
        },
        {
          "variable": "z_node_12",
          "time": 0.0,
          "expected": 7.829766507142356
        },
        {
          "variable": "z_node_13",
          "time": 0.0,
          "expected": 12.92532061670855
        },
        {
          "variable": "z_node_14",
          "time": 0.0,
          "expected": 21.32646906315379
        },
        {
          "variable": "z_node_15",
          "time": 0.0,
          "expected": 35.17762120511739
        }
      ]
    },
    {
      "id": "z_interface_surface_boundary",
      "description": "Verifies the surface boundary condition: z_interface_1 = z_{h,0} = 0 m exactly (Oleson Eq. 4.7, paper index i=0). Tightened absolute tolerance to catch any stuck-at-nonzero regression in the boundary equation.",
      "parameter_overrides": {},
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "z_interface_1",
          "time": 0.0,
          "expected": 0.0,
          "tolerance": {
            "abs": 1e-12
          }
        }
      ]
    },
    {
      "id": "dz_layer_full_column",
      "description": "Verifies Oleson Eq. 4.6 layer-thickness recurrence across all 15 layers: top layer dz_layer_1 = 0.5 * (z_node_1 + z_node_2), interior layers dz_layer_i = 0.5 * (z_node_{i+1} - z_node_{i-1}), bottom layer dz_layer_15 = z_node_15 - z_node_14. Anchors the layer-thickness profile that feeds heat-flux divergence and phase-change accounting in the urban canopy thermal solver.",
      "parameter_overrides": {},
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "dz_layer_1",
          "time": 0.0,
          "expected": 0.017512817916255204
        },
        {
          "variable": "dz_layer_2",
          "time": 0.0,
          "expected": 0.02757896925967625
        },
        {
          "variable": "dz_layer_3",
          "time": 0.0,
          "expected": 0.0454700332424132
        },
        {
          "variable": "dz_layer_4",
          "time": 0.0,
          "expected": 0.07496741098620856
        },
        {
          "variable": "dz_layer_5",
          "time": 0.0,
          "expected": 0.12360036510228053
        },
        {
          "variable": "dz_layer_6",
          "time": 0.0,
          "expected": 0.20378255101043175
        },
        {
          "variable": "dz_layer_7",
          "time": 0.0,
          "expected": 0.33598062644843263
        },
        {
          "variable": "dz_layer_8",
          "time": 0.0,
          "expected": 0.5539384053686849
        },
        {
          "variable": "dz_layer_9",
          "time": 0.0,
          "expected": 0.9132900315890611
        },
        {
          "variable": "dz_layer_10",
          "time": 0.0,
          "expected": 1.5057607013992766
        },
        {
          "variable": "dz_layer_11",
          "time": 0.0,
          "expected": 2.482579696981332
        },
        {
          "variable": "dz_layer_12",
          "time": 0.0,
          "expected": 4.0930819526214
        },
        {
          "variable": "dz_layer_13",
          "time": 0.0,
          "expected": 6.7483512780057175
        },
        {
          "variable": "dz_layer_14",
          "time": 0.0,
          "expected": 11.12615029420442
        },
        {
          "variable": "dz_layer_15",
          "time": 0.0,
          "expected": 13.851152141963599
        }
      ]
    },
    {
      "id": "z_interface_full_column",
      "description": "Verifies Oleson Eq. 4.7 interface-depth recurrence across all 16 interfaces: surface z_interface_1 = 0, interior z_interface_{i+1} = 0.5 * (z_node_i + z_node_{i+1}) for i = 1..14, and bottom z_interface_16 = z_node_15 + 0.5 * dz_layer_15. Anchors the interface positions used for inter-layer thermal-conductivity coupling.",
      "parameter_overrides": {},
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "z_interface_1",
          "time": 0.0,
          "expected": 0.0,
          "tolerance": {
            "abs": 1e-12
          }
        },
        {
          "variable": "z_interface_2",
          "time": 0.0,
          "expected": 0.017512817916255204
        },
        {
          "variable": "z_interface_3",
          "time": 0.0,
          "expected": 0.04509178717593146
        },
        {
          "variable": "z_interface_4",
          "time": 0.0,
          "expected": 0.09056182041834465
        },
        {
          "variable": "z_interface_5",
          "time": 0.0,
          "expected": 0.16552923140455322
        },
        {
          "variable": "z_interface_6",
          "time": 0.0,
          "expected": 0.28912959650683373
        },
        {
          "variable": "z_interface_7",
          "time": 0.0,
          "expected": 0.4929121475172655
        },
        {
          "variable": "z_interface_8",
          "time": 0.0,
          "expected": 0.8288927739656982
        },
        {
          "variable": "z_interface_9",
          "time": 0.0,
          "expected": 1.382831179334383
        },
        {
          "variable": "z_interface_10",
          "time": 0.0,
          "expected": 2.2961212109234443
        },
        {
          "variable": "z_interface_11",
          "time": 0.0,
          "expected": 3.8018819123227208
        },
        {
          "variable": "z_interface_12",
          "time": 0.0,
          "expected": 6.284461609304053
        },
        {
          "variable": "z_interface_13",
          "time": 0.0,
          "expected": 10.377543561925453
        },
        {
          "variable": "z_interface_14",
          "time": 0.0,
          "expected": 17.12589483993117
        },
        {
          "variable": "z_interface_15",
          "time": 0.0,
          "expected": 28.252045134135592
        },
        {
          "variable": "z_interface_16",
          "time": 0.0,
          "expected": 42.10319727609919
        }
      ]
    },
    {
      "id": "monotonic_increasing_depths",
      "description": "Spot-checks the monotonic-increasing-with-depth invariant of the exponential grid (upstream `ExponentialGrid - Increasing Depth` test, roof_wall_road_snow_temperatures_test.jl:1016): z_node_1 < z_node_7 < z_node_15 and z_interface_1 < z_interface_8 < z_interface_16. Asserted via the spot values at three depths each — the full-column tests above already pin every layer, this test documents the monotonicity contract for downstream consumers.",
      "parameter_overrides": {},
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "z_node_1",
          "time": 0.0,
          "expected": 0.007100635417193535
        },
        {
          "variable": "z_node_7",
          "time": 0.0,
          "expected": 0.6197584979298266
        },
        {
          "variable": "z_node_15",
          "time": 0.0,
          "expected": 35.17762120511739
        },
        {
          "variable": "z_interface_1",
          "time": 0.0,
          "expected": 0.0,
          "tolerance": {
            "abs": 1e-12
          }
        },
        {
          "variable": "z_interface_8",
          "time": 0.0,
          "expected": 0.8288927739656982
        },
        {
          "variable": "z_interface_16",
          "time": 0.0,
          "expected": 42.10319727609919
        }
      ]
    },
    {
      "id": "f_s_doubled_scales_linearly",
      "description": "Verifies the linear scaling of the grid with the f_s parameter: doubling f_s from 0.025 m to 0.05 m doubles every z_node value, since z_i = f_s * (exp(0.5*(i-0.5)) - 1) is linear in f_s. Spot-checks layers 1, 8, and 15 (top, middle, and bottom). Guards against regressions that pin the constant or break the linear coupling.",
      "parameter_overrides": {
        "f_s": 0.05
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "z_node_1",
          "time": 0.0,
          "expected": 0.01420127083438707
        },
        {
          "variable": "z_node_8",
          "time": 0.0,
          "expected": 2.076054100003139
        },
        {
          "variable": "z_node_15",
          "time": 0.0,
          "expected": 70.35524241023478
        }
      ]
    }
  ]
}