SnowLayerGeometry
Single-layer snow-pack geometry for the CLMU urban canopy thermal stack: given the total snow depth z_sno, computes the bottom-layer thickness, node depth, and top interface depth used by the snow conduction column. Migrated from UrbanCanopy.jl@9200edd4 src/roof_wall_road_snow_temperatures.jl:33 (`SnowLayerGeometry` @component). Implements Oleson et al. (2010, CLM Tech Note Ch. 4) §4.1 Eqs. 4.9-4.10 in the simplest snl = -1 (single snow layer) regime, where Δz_0 = z_sno (pp. 92-93). Parameterized as observed-only: z_sno is the input (parameter); Δz_snow_0, z_node_snow_0, z_interface_snow_neg1 are the algebraic outputs of three identity/linear equations.
Description
Single-layer snow-pack geometry for the CLMU urban canopy thermal stack: given the total snow depth z_sno, computes the bottom-layer thickness, node depth, and top interface depth used by the snow conduction column. Migrated from UrbanCanopy.jl@9200edd4 src/roof_wall_road_snow_temperatures.jl:33 (SnowLayerGeometry @component). Implements Oleson et al. (2010, CLM Tech Note Ch. 4) §4.1 Eqs. 4.9-4.10 in the simplest snl = -1 (single snow layer) regime, where Δz_0 = z_sno (pp. 92-93). Parameterized as observed-only: z_sno is the input (parameter); Δz_snow_0, z_node_snow_0, z_interface_snow_neg1 are the algebraic outputs of three identity/linear equations.
Reference
_source_ref: UrbanCanopy.jl@9200edd4f15609f20339d3a7d104de8064544310:src/roof_wall_road_snow_temperatures.jl:33
Upstream SnowLayerGeometry is an MTK @component with one @parameters input (z_sno), three time-indexed @variables outputs (Δz_snow_0, z_node_snow_0, z_interface_snow_neg1), and one @constants declaration (zero_m = 0.0 m). Three algebraic equations close the system: Δz_snow_0 ~ z_sno, z_node_snow_0 ~ zero_m - 0.5*Δz_snow_0, z_interface_snow_neg1 ~ zero_m - Δz_snow_0.
The upstream @constants begin zero_m = 0.0, [unit = u"m"] end is encoded here as a zero-default parameter (units m). This mirrors the convention used by sibling observed-only ESM files in this rig that translate @constants to fixed-default parameters so the units flow through MTK’s unit checker: snow_soil_blended_heat_capacity.esm encodes C_ice the same way, and snow_melt_no_layers.esm uses zero_kgm2 / zero_m for the same reason.
The component represents the snl = -1 single-snow-layer case from Oleson pp. 92-93: when z_sno spans the single-layer regime (0.01 ≤ z_sno ≤ 0.03), all snow lives in layer 0, so Δz_0 = z_sno, node z_0 sits at the layer midpoint, and the top interface z_{h,-1} sits at the surface offset by Δz_0. Multi-layer snow (snl < -1, requiring procedural branching on snow depth) is not exposed here — the upstream component’s docstring notes that multi-layer packing logic is procedural and typically handled outside the equation system.
Unicode Δz_snow_0 is preserved from upstream — the schema’s identifier pattern allows it and other temps/ ESM components already use unicode delta in identifiers (snow_melt_no_layers.esm uses Δt, snow_soil_blended_heat_capacity.esm uses Δz).
Observability classification: this component is functionally observed-only — each output variable is given by a single algebraic equation in terms of parameters and the other outputs (no derivatives, no time-dependent dynamics). Test scenarios use time_span: [0, 1] and sample at t=0 to mirror the convention used by sibling observed-only ESM files (snow_soil_blended_heat_capacity.esm, snow_melt_no_layers.esm, total_phase_change_energy.esm).
Tolerance rationale (model-level rel=1.0e-10): three algebraic identity/linear equations (no ODE integration). Δz_snow_0 ~ z_sno is exact assignment; z_node_snow_0 and z_interface_snow_neg1 involve one subtraction (and one halving) of the parameter, so roundoff is at the IEEE-754 ULP level (~1e-16). The upstream test uses rtol = 1.0e-10 (test/roof_wall_road_snow_temperatures_test.jl:33-36) and we mirror that for fidelity. Tighter abs: 1e-12 is asserted on outputs that take their full magnitude from z_sno in the zero-input regime so the assertion does not silently accept a stuck-at-zero regression._
Parameters
| Name | Units | Default | Description |
|---|---|---|---|
z_sno | m | 0.0 | Total snow depth (Oleson Ch. 4 §4.1 — single-layer regime input, 0.01 ≤ z_sno ≤ 0.03 m for snl = -1) |
zero_m | m | 0.0 | Zero depth reference for the snow column (upstream @constants zero_m = 0.0 m; encoded as a fixed-default parameter so the units flow through MTK’s unit checker) |
Observed
| Name | Units | Default | Description |
|---|---|---|---|
Δz_snow_0 | m | Thickness of bottom snow layer (layer 0); Eq. 4.9 single-layer case: Δz_0 = z_sno | |
z_node_snow_0 | m | Node depth of bottom snow layer (Oleson Eq. 4.9): z_0 = z_{h,0} - 0.5·Δz_0 with z_{h,0} = 0 (surface), so z_0 = -0.5·Δz_0 (negative = above ground) | |
z_interface_snow_neg1 | m | Interface depth above bottom snow layer (Oleson Eq. 4.10): z_{h,-1} = z_{h,0} - Δz_0 with z_{h,0} = 0, so z_{h,-1} = -Δz_0 (negative = above ground) |
Observed expressions
$$ Δz_{snow\,0} = z_{sno} $$Thickness of bottom snow layer (layer 0); Eq. 4.9 single-layer case: Δz_0 = z_sno
$$ z_{node\,snow\,0} = zero_{m} - 0.5 \cdot Δz_{snow\,0} $$Node depth of bottom snow layer (Oleson Eq. 4.9): z_0 = z{h,0} - 0.5·Δz_0 with z_{h,0} = 0 (surface), so z_0 = -0.5·Δz_0 (negative = above ground)_
$$ z_{interface\,snow\,neg1} = zero_{m} - Δz_{snow\,0} $$Interface depth above bottom snow layer (Oleson Eq. 4.10): z{h,-1} = z_{h,0} - Δz_0 with z_{h,0} = 0, so z_{h,-1} = -Δz_0 (negative = above ground)_
Analyses
single_layer_geometry_sweep
Sweep snow depth z_sno across the single-layer band (0.01-0.03 m per Oleson pp. 92-93) at 11 evenly spaced points. Surfaces the three linear/identity outputs as functions of z_sno: Δz_snow_0 traces the 1:1 identity line, z_node_snow_0 traces a slope -1/2 line through the origin, and z_interface_snow_neg1 traces a slope -1 line. The three lines fan out from the origin and confirm the layer-midpoint placement geometrically.
Time span: 0.0 → 1.0
Swept over: z_sno



Raw .esm
Raw .esm JSON (this component)
{
"reference": {
"notes": "source_ref: UrbanCanopy.jl@9200edd4f15609f20339d3a7d104de8064544310:src/roof_wall_road_snow_temperatures.jl:33\nUpstream `SnowLayerGeometry` is an MTK `@component` with one `@parameters` input (z_sno), three time-indexed `@variables` outputs (Δz_snow_0, z_node_snow_0, z_interface_snow_neg1), and one `@constants` declaration (zero_m = 0.0 m). Three algebraic equations close the system: Δz_snow_0 ~ z_sno, z_node_snow_0 ~ zero_m - 0.5*Δz_snow_0, z_interface_snow_neg1 ~ zero_m - Δz_snow_0.\n\nThe upstream `@constants begin zero_m = 0.0, [unit = u\"m\"] end` is encoded here as a zero-default parameter (units m). This mirrors the convention used by sibling observed-only ESM files in this rig that translate `@constants` to fixed-default parameters so the units flow through MTK's unit checker: snow_soil_blended_heat_capacity.esm encodes C_ice the same way, and snow_melt_no_layers.esm uses zero_kgm2 / zero_m for the same reason.\n\nThe component represents the snl = -1 single-snow-layer case from Oleson pp. 92-93: when z_sno spans the single-layer regime (0.01 ≤ z_sno ≤ 0.03), all snow lives in layer 0, so Δz_0 = z_sno, node z_0 sits at the layer midpoint, and the top interface z_{h,-1} sits at the surface offset by Δz_0. Multi-layer snow (snl < -1, requiring procedural branching on snow depth) is not exposed here — the upstream component's docstring notes that multi-layer packing logic is procedural and typically handled outside the equation system.\n\nUnicode `Δz_snow_0` is preserved from upstream — the schema's identifier pattern allows it and other temps/ ESM components already use unicode delta in identifiers (snow_melt_no_layers.esm uses `Δt`, snow_soil_blended_heat_capacity.esm uses `Δz`).\n\nObservability classification: this component is functionally observed-only — each output variable is given by a single algebraic equation in terms of parameters and the other outputs (no derivatives, no time-dependent dynamics). Test scenarios use `time_span: [0, 1]` and sample at t=0 to mirror the convention used by sibling observed-only ESM files (snow_soil_blended_heat_capacity.esm, snow_melt_no_layers.esm, total_phase_change_energy.esm).\n\nTolerance rationale (model-level rel=1.0e-10): three algebraic identity/linear equations (no ODE integration). Δz_snow_0 ~ z_sno is exact assignment; z_node_snow_0 and z_interface_snow_neg1 involve one subtraction (and one halving) of the parameter, so roundoff is at the IEEE-754 ULP level (~1e-16). The upstream test uses `rtol = 1.0e-10` (test/roof_wall_road_snow_temperatures_test.jl:33-36) and we mirror that for fidelity. Tighter `abs: 1e-12` is asserted on outputs that take their full magnitude from z_sno in the zero-input regime so the assertion does not silently accept a stuck-at-zero regression."
},
"tolerance": {
"rel": 1e-10
},
"variables": {
"z_sno": {
"type": "parameter",
"units": "m",
"default": 0.0,
"description": "Total snow depth (Oleson Ch. 4 §4.1 — single-layer regime input, 0.01 ≤ z_sno ≤ 0.03 m for snl = -1)"
},
"zero_m": {
"type": "parameter",
"units": "m",
"default": 0.0,
"description": "Zero depth reference for the snow column (upstream @constants zero_m = 0.0 m; encoded as a fixed-default parameter so the units flow through MTK's unit checker)"
},
"Δz_snow_0": {
"type": "unknown",
"units": "m",
"description": "Thickness of bottom snow layer (layer 0); Eq. 4.9 single-layer case: Δz_0 = z_sno"
},
"z_node_snow_0": {
"type": "unknown",
"units": "m",
"description": "Node depth of bottom snow layer (Oleson Eq. 4.9): z_0 = z_{h,0} - 0.5·Δz_0 with z_{h,0} = 0 (surface), so z_0 = -0.5·Δz_0 (negative = above ground)"
},
"z_interface_snow_neg1": {
"type": "unknown",
"units": "m",
"description": "Interface depth above bottom snow layer (Oleson Eq. 4.10): z_{h,-1} = z_{h,0} - Δz_0 with z_{h,0} = 0, so z_{h,-1} = -Δz_0 (negative = above ground)"
}
},
"equations": [
{
"lhs": "Δz_snow_0",
"rhs": "z_sno"
},
{
"lhs": "z_node_snow_0",
"rhs": {
"op": "-",
"args": [
"zero_m",
{
"op": "*",
"args": [
0.5,
"Δz_snow_0"
]
}
]
}
},
{
"lhs": "z_interface_snow_neg1",
"rhs": {
"op": "-",
"args": [
"zero_m",
"Δz_snow_0"
]
}
}
],
"tests": [
{
"id": "upstream_equation_verification",
"description": "Reproduces upstream `SnowLayerGeometry - Equation Verification` test (UrbanCanopy.jl test/roof_wall_road_snow_temperatures_test.jl:23-40) at the single-layer canonical depth z_sno = 0.02 m (inside the 0.01-0.03 m single-layer band, pp. 92-93). Expected: Δz_snow_0 = 0.02 m (identity), z_node_snow_0 = -0.01 m (midpoint), z_interface_snow_neg1 = -0.02 m (top interface at -Δz_0). Anchors fidelity to the upstream Julia component at the same parameter value with rtol = 1.0e-10.",
"parameter_overrides": {
"z_sno": 0.02
},
"time_span": {
"start": 0.0,
"end": 1.0
},
"assertions": [
{
"variable": "Δz_snow_0",
"time": 0.0,
"expected": 0.02
},
{
"variable": "z_node_snow_0",
"time": 0.0,
"expected": -0.01
},
{
"variable": "z_interface_snow_neg1",
"time": 0.0,
"expected": -0.02
}
]
},
{
"id": "zero_snow_depth",
"description": "Sign-agnostic zero-depth limit: at z_sno = 0 all three outputs collapse to zero (Δz_0 = 0, z_0 = 0, z_{h,-1} = 0). Confirms the equations propagate exact zero and the linear/identity equations are not silently biased; an abs tolerance is asserted so a stuck-at-zero regression cannot pass.",
"parameter_overrides": {
"z_sno": 0.0
},
"time_span": {
"start": 0.0,
"end": 1.0
},
"assertions": [
{
"variable": "Δz_snow_0",
"time": 0.0,
"expected": 0.0,
"tolerance": {
"abs": 1e-12
}
},
{
"variable": "z_node_snow_0",
"time": 0.0,
"expected": 0.0,
"tolerance": {
"abs": 1e-12
}
},
{
"variable": "z_interface_snow_neg1",
"time": 0.0,
"expected": 0.0,
"tolerance": {
"abs": 1e-12
}
}
]
},
{
"id": "thin_snow_lower_bound",
"description": "Lower end of the single-layer band: z_sno = 0.01 m (the threshold for snl = -1 per Oleson pp. 92-93). Δz_snow_0 = 0.01, z_node_snow_0 = -0.005, z_interface_snow_neg1 = -0.01. Sweeps the small-value side of the linear identity to confirm node placement at the layer midpoint.",
"parameter_overrides": {
"z_sno": 0.01
},
"time_span": {
"start": 0.0,
"end": 1.0
},
"assertions": [
{
"variable": "Δz_snow_0",
"time": 0.0,
"expected": 0.01
},
{
"variable": "z_node_snow_0",
"time": 0.0,
"expected": -0.005
},
{
"variable": "z_interface_snow_neg1",
"time": 0.0,
"expected": -0.01
}
]
},
{
"id": "thick_snow_upper_bound",
"description": "Upper end of the single-layer band: z_sno = 0.03 m (the threshold for transition to snl < -1 per Oleson pp. 92-93). Δz_snow_0 = 0.03, z_node_snow_0 = -0.015, z_interface_snow_neg1 = -0.03. Exercises the high end of the band; outside this regime the upstream model would assign more layers (procedural, not modeled here).",
"parameter_overrides": {
"z_sno": 0.03
},
"time_span": {
"start": 0.0,
"end": 1.0
},
"assertions": [
{
"variable": "Δz_snow_0",
"time": 0.0,
"expected": 0.03
},
{
"variable": "z_node_snow_0",
"time": 0.0,
"expected": -0.015
},
{
"variable": "z_interface_snow_neg1",
"time": 0.0,
"expected": -0.03
}
]
},
{
"id": "node_at_layer_midpoint",
"description": "Invariant: the node z_0 always sits at the midpoint of layer 0, halfway between the surface (z = 0) and the top interface z_{h,-1}. Equivalently, z_node_snow_0 = 0.5 · z_interface_snow_neg1. Asserted at z_sno = 0.025 m via both outputs to lock the relationship between Eqs. 4.9 and 4.10.",
"parameter_overrides": {
"z_sno": 0.025
},
"time_span": {
"start": 0.0,
"end": 1.0
},
"assertions": [
{
"variable": "Δz_snow_0",
"time": 0.0,
"expected": 0.025
},
{
"variable": "z_node_snow_0",
"time": 0.0,
"expected": -0.0125
},
{
"variable": "z_interface_snow_neg1",
"time": 0.0,
"expected": -0.025
}
]
}
],
"analyses": [
{
"id": "single_layer_geometry_sweep",
"description": "Sweep snow depth z_sno across the single-layer band (0.01-0.03 m per Oleson pp. 92-93) at 11 evenly spaced points. Surfaces the three linear/identity outputs as functions of z_sno: Δz_snow_0 traces the 1:1 identity line, z_node_snow_0 traces a slope -1/2 line through the origin, and z_interface_snow_neg1 traces a slope -1 line. The three lines fan out from the origin and confirm the layer-midpoint placement geometrically.",
"initial_state": {},
"parameters": {},
"time_span": {
"start": 0.0,
"end": 1.0
},
"parameter_sweep": {
"type": "cartesian",
"dimensions": [
{
"parameter": "z_sno",
"range": {
"start": 0.01,
"stop": 0.03,
"count": 11,
"scale": "linear"
}
}
]
},
"plots": [
{
"id": "layer_thickness",
"type": "line",
"description": "Δz_snow_0 vs z_sno: 1:1 identity line over the single-layer band per Eq. 4.9 (single-layer case).",
"x": {
"variable": "z_sno",
"label": "z_sno (m)"
},
"y": {
"variable": "Δz_snow_0",
"label": "Δz_snow_0 (m)"
}
},
{
"id": "node_depth",
"type": "line",
"description": "z_node_snow_0 vs z_sno: slope -1/2 line per Eq. 4.9 (layer-midpoint node placement).",
"x": {
"variable": "z_sno",
"label": "z_sno (m)"
},
"y": {
"variable": "z_node_snow_0",
"label": "z_node_snow_0 (m)"
}
},
{
"id": "top_interface_depth",
"type": "line",
"description": "z_interface_snow_neg1 vs z_sno: slope -1 line per Eq. 4.10 (top interface at z_{h,0} - Δz_0 with z_{h,0} = 0).",
"x": {
"variable": "z_sno",
"label": "z_sno (m)"
},
"y": {
"variable": "z_interface_snow_neg1",
"label": "z_interface_snow_neg1 (m)"
}
}
]
}
]
}