Version: dev

BuildingTemperature

Weighted-average internal building temperature from canyon and roof surface temperatures, implementing Oleson et al. (2010, CLM Tech Note Ch. 4) Eqs. 4.37–4.38. The roof length L_roof in an infinite canyon (Eq. 4.38) and the unclamped building temperature T_iB_unclamped (Eq. 4.37) are both algebraic functions of the canyon geometry (H_canyon, H_W, W_roof) and the inner-surface temperatures of the shaded wall, sunlit wall, and roof. T_iB is the area-weighted average with the two walls contributing 2*H_canyon of effective length and the roof contributing L_roof. Migrated from UrbanCanopy.jl@9200edd4 src/roof_wall_road_snow_temperatures.jl:401 (`BuildingTemperature` @component). NOTE: the migration-tracker entry listed `blocking_gap: other:discretization-plan` citing line :1152, but that line belongs to `RoofWallHeatConduction` (a separate non-exported helper function). `BuildingTemperature` itself is a pure two-equation algebraic `@component` with no PDESystem or MOLFiniteDifference — this migration is unblocked.

Domain
urban_canopy
Subdomain
temps
Type
model
.esm schema
1.0.0
Source
components/urban_canopy/temps/building_temperature.esm
Tags
#urban_canopy #temps #model #temperatures #building #canyon_geometry #observed-only #clmu

Description

Weighted-average internal building temperature from canyon and roof surface temperatures, implementing Oleson et al. (2010, CLM Tech Note Ch. 4) Eqs. 4.37–4.38. The roof length L_roof in an infinite canyon (Eq. 4.38) and the unclamped building temperature T_iB_unclamped (Eq. 4.37) are both algebraic functions of the canyon geometry (H_canyon, H_W, W_roof) and the inner-surface temperatures of the shaded wall, sunlit wall, and roof. T_iB is the area-weighted average with the two walls contributing 2*H_canyon of effective length and the roof contributing L_roof. Migrated from UrbanCanopy.jl@9200edd4 src/roof_wall_road_snow_temperatures.jl:401 (BuildingTemperature @component). NOTE: the migration-tracker entry listed blocking_gap: other:discretization-plan citing line :1152, but that line belongs to RoofWallHeatConduction (a separate non-exported helper function). BuildingTemperature itself is a pure two-equation algebraic @component with no PDESystem or MOLFiniteDifference — this migration is unblocked.

Reference

_source_ref: UrbanCanopy.jl@9200edd4f15609f20339d3a7d104de8064544310:src/roof_wall_road_snow_temperatures.jl:401

Upstream BuildingTemperature is an MTK @component with six @parameters (T_inner_shdwall, T_inner_sunwall, T_inner_roof in K; H_canyon in m; W_roof and H_W dimensionless) and two time-indexed @variables (L_roof in m, T_iB_unclamped in K). Two algebraic equations close the system:

Eq. 4.38: L_roof = (H_canyon / H_W) * (W_roof / (1 - W_roof)) Converts the H/W ratio and roof fraction into the roof length in an infinite canyon. This is the canyon geometry length that W_roof road/roof width occupies relative to the street width (1-W_roof).

Eq. 4.37: T_iB_unclamped = [H_canyon * (T_inner_shdwall + T_inner_sunwall) + L_roof * T_inner_roof] / (2H_canyon + L_roof) Area-weighted average of inner surface temperatures. The two walls (shaded + sunlit) each have length H_canyon, contributing 2H_canyon total. The roof contributes L_roof. If all temperatures are equal, T_iB_unclamped = that temperature identically.

Observability classification: observed-only — both outputs are algebraic functions of the six parameters with no time derivatives. Test scenarios use time_span: [0, 1] and sample at t=0 following the convention of sibling observed-only ESM files in this rig.

Tracker note: the migration-tracker entry listed blocking_gap: other:discretization-plan citing line :1152 in the source file (where @named pdesys appears inside RoofWallHeatConduction, a separate private helper function). The BuildingTemperature @component at :401 has no PDESystem, no MOLFiniteDifference, and no spatial discretization — it is purely algebraic and requires no discretization plan for migration. The same mis-classification occurred for PhaseChangeAdjustment (esm-eya8) and SoilWaterEquilibrium (esm-4kir) in the same tracker section.

W_roof constraint: the equation for L_roof has a singularity at W_roof = 1.0 (division by 1 - W_roof = 0). Physical values of W_roof are in (0, 1); for urban canyons the typical range is 0.1–0.7. Test cases use W_roof ∈ (0, 1) strictly.

Default parameter values: T_inner_shdwall=293.0 K, T_inner_sunwall=295.0 K, T_inner_roof=290.0 K are representative inner-surface temperatures for a modestly solar-heated urban surface in mid-latitude summer. H_canyon=10.0 m, W_roof=0.3, H_W=1.0 match the upstream BuildingTemperature - Equation Verification test fixture (roof_wall_road_snow_temperatures_test.jl) and represent a medium-density urban canyon.

Upstream tolerances: both upstream @testitem assertions use rtol = 1.0e-10. Reproduced via per-assertion tolerance: { rel: 1.0e-10 } on the upstream-canonical tests; model-level default rel=1.0e-10 (observed-only two-equation system, no ODE integration — roundoff at IEEE-754 ULP level ~1e-16)._

Parameters

NameUnitsDefaultDescription
T_inner_shdwallK293.0Inner-layer temperature of the shaded wall surface (Oleson Eq. 4.37 input — shaded wall faces away from the sun)
T_inner_sunwallK295.0Inner-layer temperature of the sunlit wall surface (Oleson Eq. 4.37 input — sunlit wall faces toward the sun)
T_inner_roofK290.0Inner-layer temperature of the roof surface (Oleson Eq. 4.37 input)
H_canyonm10.0Urban canyon height — the building height (Oleson Eqs. 4.37–4.38 input); each wall contributes H_canyon to the area-weighting denominator
W_roof10.3Roof fraction — fraction of the canyon floor covered by roof (dimensionless, in (0,1)); complement (1−W_roof) is the street/road fraction
H_W11.0Canyon height-to-width ratio H/W (dimensionless, Oleson Eq. 4.38 input); larger values correspond to deeper/narrower canyons

Observed

NameUnitsDefaultDescription
L_roofmRoof length in the infinite-canyon representation (Oleson Eq. 4.38): L = (H/H_W)*(W_roof/(1−W_roof)). Equals the canyon width times the roof-to-street ratio; used as the weighting length for the roof contribution in T_iB.
T_iB_unclampedKUnclamped internal building temperature (Oleson Eq. 4.37): area-weighted average of inner wall (×2H) and roof (×L_roof) temperatures. Called ‘unclamped’ because the downstream model clamps T_iB to [T_iB_min, T_iB_max] (handled by HeatingCoolingFlux).

Observed expressions

$$ L_{roof} = \frac{H_{canyon}}{H_{W}} \cdot \frac{W_{roof}}{1.0 - W_{roof}} $$

Roof length in the infinite-canyon representation (Oleson Eq. 4.38): L = (H/H_W)*(W_roof/(1−W_roof)). Equals the canyon width times the roof-to-street ratio; used as the weighting length for the roof contribution in T_iB.

$$ T_{iB\,unclamped} = \frac{H_{canyon} \cdot \left(T_{inner\,shdwall} + T_{inner\,sunwall}\right) + L_{roof} \cdot T_{inner\,roof}}{2.0 \cdot H_{canyon} + L_{roof}} $$

Unclamped internal building temperature (Oleson Eq. 4.37): area-weighted average of inner wall (×2H) and roof (×L_roof) temperatures. Called ‘unclamped’ because the downstream model clamps T_iB to [T_iB_min, T_iB_max] (handled by HeatingCoolingFlux).

Analyses

T_iB_vs_H_W_sweep

Sweep H_W from 0.2 to 5.0 (shallow to deep canyon) at fixed W_roof=0.3, H_canyon=10 m with three surface temperatures. As H_W increases, L_roof decreases (narrower canyon has smaller roof-to-wall ratio), shifting T_iB toward the wall-temperature average. Surfaces the geometric transition from roof-dominated (low H_W) to wall-dominated (high H_W) weighting.

Time span: 0.0 → 1.0

Swept over: H_W

L_roof (m) vs H_W: decreases as canyon deepens, following L = (H/H_W)*(W/(1-W)).

T_iB_unclamped (K) vs H_W: transitions from roof-temperature-biased (small H_W) to wall-average-biased (large H_W).

T_iB_heatmap_W_roof_H_W

Joint sweep over W_roof ∈ [0.1, 0.8] and H_W ∈ [0.2, 4.0] at fixed temperatures (T_shd=285, T_sun=295, T_roof=305 K). Shows how the two geometric parameters jointly control the weighting: high W_roof and low H_W both increase the roof contribution and raise T_iB toward T_roof=305 K.

Time span: 0.0 → 1.0

Swept over: W_roof, H_W

T_iB_unclamped as a function of (W_roof, H_W): higher values (toward T_roof=305 K) occur at high W_roof / low H_W; lower values (toward wall average 290 K) occur at low W_roof / high H_W.

Raw .esm

Raw .esm JSON (this component)
{
  "reference": {
    "notes": "source_ref: UrbanCanopy.jl@9200edd4f15609f20339d3a7d104de8064544310:src/roof_wall_road_snow_temperatures.jl:401\n\nUpstream `BuildingTemperature` is an MTK `@component` with six `@parameters` (T_inner_shdwall, T_inner_sunwall, T_inner_roof in K; H_canyon in m; W_roof and H_W dimensionless) and two time-indexed `@variables` (L_roof in m, T_iB_unclamped in K). Two algebraic equations close the system:\n\n  Eq. 4.38: L_roof = (H_canyon / H_W) * (W_roof / (1 - W_roof))\n    Converts the H/W ratio and roof fraction into the roof length in an infinite canyon. This is the canyon geometry length that W_roof road/roof width occupies relative to the street width (1-W_roof).\n\n  Eq. 4.37: T_iB_unclamped = [H_canyon * (T_inner_shdwall + T_inner_sunwall) + L_roof * T_inner_roof] / (2*H_canyon + L_roof)\n    Area-weighted average of inner surface temperatures. The two walls (shaded + sunlit) each have length H_canyon, contributing 2*H_canyon total. The roof contributes L_roof. If all temperatures are equal, T_iB_unclamped = that temperature identically.\n\nObservability classification: observed-only — both outputs are algebraic functions of the six parameters with no time derivatives. Test scenarios use `time_span: [0, 1]` and sample at t=0 following the convention of sibling observed-only ESM files in this rig.\n\nTracker note: the migration-tracker entry listed `blocking_gap: other:discretization-plan` citing line :1152 in the source file (where `@named pdesys` appears inside `RoofWallHeatConduction`, a separate private helper function). The `BuildingTemperature` `@component` at :401 has no PDESystem, no MOLFiniteDifference, and no spatial discretization — it is purely algebraic and requires no discretization plan for migration. The same mis-classification occurred for `PhaseChangeAdjustment` (esm-eya8) and `SoilWaterEquilibrium` (esm-4kir) in the same tracker section.\n\nW_roof constraint: the equation for L_roof has a singularity at W_roof = 1.0 (division by 1 - W_roof = 0). Physical values of W_roof are in (0, 1); for urban canyons the typical range is 0.1–0.7. Test cases use W_roof ∈ (0, 1) strictly.\n\nDefault parameter values: T_inner_shdwall=293.0 K, T_inner_sunwall=295.0 K, T_inner_roof=290.0 K are representative inner-surface temperatures for a modestly solar-heated urban surface in mid-latitude summer. H_canyon=10.0 m, W_roof=0.3, H_W=1.0 match the upstream `BuildingTemperature - Equation Verification` test fixture (roof_wall_road_snow_temperatures_test.jl) and represent a medium-density urban canyon.\n\nUpstream tolerances: both upstream `@testitem` assertions use `rtol = 1.0e-10`. Reproduced via per-assertion `tolerance: { rel: 1.0e-10 }` on the upstream-canonical tests; model-level default rel=1.0e-10 (observed-only two-equation system, no ODE integration — roundoff at IEEE-754 ULP level ~1e-16)."
  },
  "tolerance": {
    "rel": 1e-10
  },
  "variables": {
    "T_inner_shdwall": {
      "type": "parameter",
      "units": "K",
      "default": 293.0,
      "description": "Inner-layer temperature of the shaded wall surface (Oleson Eq. 4.37 input — shaded wall faces away from the sun)"
    },
    "T_inner_sunwall": {
      "type": "parameter",
      "units": "K",
      "default": 295.0,
      "description": "Inner-layer temperature of the sunlit wall surface (Oleson Eq. 4.37 input — sunlit wall faces toward the sun)"
    },
    "T_inner_roof": {
      "type": "parameter",
      "units": "K",
      "default": 290.0,
      "description": "Inner-layer temperature of the roof surface (Oleson Eq. 4.37 input)"
    },
    "H_canyon": {
      "type": "parameter",
      "units": "m",
      "default": 10.0,
      "description": "Urban canyon height — the building height (Oleson Eqs. 4.37–4.38 input); each wall contributes H_canyon to the area-weighting denominator"
    },
    "W_roof": {
      "type": "parameter",
      "units": "1",
      "default": 0.3,
      "description": "Roof fraction — fraction of the canyon floor covered by roof (dimensionless, in (0,1)); complement (1−W_roof) is the street/road fraction"
    },
    "H_W": {
      "type": "parameter",
      "units": "1",
      "default": 1.0,
      "description": "Canyon height-to-width ratio H/W (dimensionless, Oleson Eq. 4.38 input); larger values correspond to deeper/narrower canyons"
    },
    "L_roof": {
      "type": "unknown",
      "units": "m",
      "description": "Roof length in the infinite-canyon representation (Oleson Eq. 4.38): L = (H/H_W)*(W_roof/(1−W_roof)). Equals the canyon width times the roof-to-street ratio; used as the weighting length for the roof contribution in T_iB."
    },
    "T_iB_unclamped": {
      "type": "unknown",
      "units": "K",
      "description": "Unclamped internal building temperature (Oleson Eq. 4.37): area-weighted average of inner wall (×2H) and roof (×L_roof) temperatures. Called 'unclamped' because the downstream model clamps T_iB to [T_iB_min, T_iB_max] (handled by HeatingCoolingFlux)."
    }
  },
  "equations": [
    {
      "lhs": "L_roof",
      "rhs": {
        "op": "*",
        "args": [
          {
            "op": "/",
            "args": [
              "H_canyon",
              "H_W"
            ]
          },
          {
            "op": "/",
            "args": [
              "W_roof",
              {
                "op": "-",
                "args": [
                  1.0,
                  "W_roof"
                ]
              }
            ]
          }
        ]
      },
      "_comment": "Oleson Eq. 4.38: roof length in infinite canyon = (H/H_W) * (W_roof / (1 - W_roof))"
    },
    {
      "lhs": "T_iB_unclamped",
      "rhs": {
        "op": "/",
        "args": [
          {
            "op": "+",
            "args": [
              {
                "op": "*",
                "args": [
                  "H_canyon",
                  {
                    "op": "+",
                    "args": [
                      "T_inner_shdwall",
                      "T_inner_sunwall"
                    ]
                  }
                ]
              },
              {
                "op": "*",
                "args": [
                  "L_roof",
                  "T_inner_roof"
                ]
              }
            ]
          },
          {
            "op": "+",
            "args": [
              {
                "op": "*",
                "args": [
                  2.0,
                  "H_canyon"
                ]
              },
              "L_roof"
            ]
          }
        ]
      },
      "_comment": "Oleson Eq. 4.37: T_iB = [H*(T_shdwall + T_sunwall) + L_roof*T_roof] / (2H + L_roof)"
    }
  ],
  "tests": [
    {
      "id": "upstream_equation_verification",
      "description": "Reproduces upstream `BuildingTemperature - Equation Verification` test (UrbanCanopy.jl test/roof_wall_road_snow_temperatures_test.jl). H=10 m, H_W=1, W_roof=0.3, T_shd=293 K, T_sun=295 K, T_roof=290 K. L_roof = (10/1)*(0.3/0.7) = 30/7 = 4.285714… m. T_iB = (10*(293+295) + (30/7)*290) / (20 + 30/7) = 49860/170 = 4986/17 = 293.294117647… K. Anchors fidelity to the upstream Julia test fixture at rtol=1e-10.",
      "parameter_overrides": {
        "H_canyon": 10.0,
        "H_W": 1.0,
        "W_roof": 0.3,
        "T_inner_shdwall": 293.0,
        "T_inner_sunwall": 295.0,
        "T_inner_roof": 290.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "L_roof",
          "time": 0.0,
          "expected": 4.285714285714286,
          "tolerance": {
            "rel": 1e-10
          }
        },
        {
          "variable": "T_iB_unclamped",
          "time": 0.0,
          "expected": 293.29411764705884,
          "tolerance": {
            "rel": 1e-10
          }
        }
      ]
    },
    {
      "id": "upstream_equal_temperatures",
      "description": "Reproduces upstream `BuildingTemperature - Equal Temperatures` test. All three inner-surface temperatures equal T=295 K. The area-weighted average must equal T regardless of geometry. Anchors the invariant T_iB=T when T_shd=T_sun=T_roof=T at rtol=1e-10.",
      "parameter_overrides": {
        "H_canyon": 10.0,
        "H_W": 1.0,
        "W_roof": 0.3,
        "T_inner_shdwall": 295.0,
        "T_inner_sunwall": 295.0,
        "T_inner_roof": 295.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "T_iB_unclamped",
          "time": 0.0,
          "expected": 295.0,
          "tolerance": {
            "abs": 1e-12
          }
        }
      ]
    },
    {
      "id": "deep_canyon_geometry",
      "description": "Deep, narrow canyon (H_W=5, W_roof=0.2). L_roof = (20/5)*(0.2/0.8) = 4*(0.25) = 1.0 m. T_iB = (20*(285+290) + 1.0*285) / (40 + 1.0) = (11500 + 285)/41 = 11785/41 = 287.439024… K. The tall walls dominate the area weighting.",
      "parameter_overrides": {
        "H_canyon": 20.0,
        "H_W": 5.0,
        "W_roof": 0.2,
        "T_inner_shdwall": 285.0,
        "T_inner_sunwall": 290.0,
        "T_inner_roof": 285.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "L_roof",
          "time": 0.0,
          "expected": 1.0,
          "tolerance": {
            "abs": 1e-12
          }
        },
        {
          "variable": "T_iB_unclamped",
          "time": 0.0,
          "expected": 287.4390243902439,
          "tolerance": {
            "rel": 1e-10
          }
        }
      ]
    },
    {
      "id": "high_roof_fraction_suburban",
      "description": "High roof fraction (W_roof=0.7), shallow canyon (H_W=0.5) — suburban geometry. L_roof = (5/0.5)*(0.7/0.3) = 10*(7/3) = 70/3 = 23.333… m. T_iB = (5*(290+292) + (70/3)*288) / (10 + 70/3) = (2910 + 6720) / (100/3) = 9630*(3/100) = 288.9 K exactly. The large roof dominates the weighting.",
      "parameter_overrides": {
        "H_canyon": 5.0,
        "H_W": 0.5,
        "W_roof": 0.7,
        "T_inner_shdwall": 290.0,
        "T_inner_sunwall": 292.0,
        "T_inner_roof": 288.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "L_roof",
          "time": 0.0,
          "expected": 23.333333333333332,
          "tolerance": {
            "rel": 1e-10
          }
        },
        {
          "variable": "T_iB_unclamped",
          "time": 0.0,
          "expected": 288.9,
          "tolerance": {
            "abs": 1e-10
          }
        }
      ]
    },
    {
      "id": "roof_temp_equals_wall_avg_invariant",
      "description": "When T_roof equals the average of T_shd and T_sun (292.5 K), T_iB must equal that temperature regardless of geometry. With H=10, H_W=2, W_roof=0.4: L_roof=(10/2)*(0.4/0.6)=10/3. T_iB=(10*(290+295)+(10/3)*292.5)/(20+10/3) = 292.5*(20+10/3)/(20+10/3)=292.5 identically. Anchors the weighted-average linearity invariant.",
      "parameter_overrides": {
        "H_canyon": 10.0,
        "H_W": 2.0,
        "W_roof": 0.4,
        "T_inner_shdwall": 290.0,
        "T_inner_sunwall": 295.0,
        "T_inner_roof": 292.5
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "T_iB_unclamped",
          "time": 0.0,
          "expected": 292.5,
          "tolerance": {
            "abs": 1e-10
          }
        }
      ]
    },
    {
      "id": "small_canyon_roof_dominates",
      "description": "Small canyon height (H=1 m, H_W=1, W_roof=0.5): L_roof=(1/1)*(0.5/0.5)=1.0 m. Walls contribute 2*H=2 m, roof contributes 1 m. T_iB=(1*(280+280)+1*300)/(2+1)=860/3=286.666… K. Tests the regime where the roof-to-wall weighting ratio is 1:2.",
      "parameter_overrides": {
        "H_canyon": 1.0,
        "H_W": 1.0,
        "W_roof": 0.5,
        "T_inner_shdwall": 280.0,
        "T_inner_sunwall": 280.0,
        "T_inner_roof": 300.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "L_roof",
          "time": 0.0,
          "expected": 1.0,
          "tolerance": {
            "abs": 1e-12
          }
        },
        {
          "variable": "T_iB_unclamped",
          "time": 0.0,
          "expected": 286.6666666666667,
          "tolerance": {
            "rel": 1e-10
          }
        }
      ]
    },
    {
      "id": "moderate_urban_typical_clmu",
      "description": "Typical CLMU medium-density urban setting: H=8 m, H_W=1.2, W_roof=0.35. L_roof=(8/1.2)*(0.35/0.65)=6.666…*(7/13)=(8/1.2)*(7/13)=56/15.6=280/78=140/39=3.589743… m. T_shd=291, T_sun=294, T_roof=289. T_iB=(8*(291+294)+(140/39)*289)/(16+140/39)=(8*585+40460/39)/(624/39+140/39)=(4680+40460/39)/(764/39). Numerator=182520/39+40460/39=222980/39. T_iB=222980/764=291.859…K.",
      "parameter_overrides": {
        "H_canyon": 8.0,
        "H_W": 1.2,
        "W_roof": 0.35,
        "T_inner_shdwall": 291.0,
        "T_inner_sunwall": 294.0,
        "T_inner_roof": 289.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "L_roof",
          "time": 0.0,
          "expected": 3.58974358974359,
          "tolerance": {
            "rel": 1e-10
          }
        },
        {
          "variable": "T_iB_unclamped",
          "time": 0.0,
          "expected": 291.8586387434555,
          "tolerance": {
            "rel": 1e-10
          }
        }
      ]
    }
  ],
  "analyses": [
    {
      "id": "T_iB_vs_H_W_sweep",
      "description": "Sweep H_W from 0.2 to 5.0 (shallow to deep canyon) at fixed W_roof=0.3, H_canyon=10 m with three surface temperatures. As H_W increases, L_roof decreases (narrower canyon has smaller roof-to-wall ratio), shifting T_iB toward the wall-temperature average. Surfaces the geometric transition from roof-dominated (low H_W) to wall-dominated (high H_W) weighting.",
      "initial_state": {},
      "parameters": {
        "H_canyon": 10.0,
        "W_roof": 0.3,
        "T_inner_shdwall": 285.0,
        "T_inner_sunwall": 295.0,
        "T_inner_roof": 305.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "parameter_sweep": {
        "type": "cartesian",
        "dimensions": [
          {
            "parameter": "H_W",
            "range": {
              "start": 0.2,
              "stop": 5.0,
              "count": 25,
              "scale": "log"
            }
          }
        ]
      },
      "plots": [
        {
          "id": "L_roof_vs_HW",
          "type": "line",
          "description": "L_roof (m) vs H_W: decreases as canyon deepens, following L = (H/H_W)*(W/(1-W)).",
          "x": {
            "variable": "H_W",
            "label": "H/W ratio (dimensionless)"
          },
          "y": {
            "variable": "L_roof",
            "label": "L_roof (m)"
          }
        },
        {
          "id": "T_iB_vs_HW",
          "type": "line",
          "description": "T_iB_unclamped (K) vs H_W: transitions from roof-temperature-biased (small H_W) to wall-average-biased (large H_W).",
          "x": {
            "variable": "H_W",
            "label": "H/W ratio (dimensionless)"
          },
          "y": {
            "variable": "T_iB_unclamped",
            "label": "T_iB_unclamped (K)"
          }
        }
      ]
    },
    {
      "id": "T_iB_heatmap_W_roof_H_W",
      "description": "Joint sweep over W_roof ∈ [0.1, 0.8] and H_W ∈ [0.2, 4.0] at fixed temperatures (T_shd=285, T_sun=295, T_roof=305 K). Shows how the two geometric parameters jointly control the weighting: high W_roof and low H_W both increase the roof contribution and raise T_iB toward T_roof=305 K.",
      "initial_state": {},
      "parameters": {
        "H_canyon": 10.0,
        "T_inner_shdwall": 285.0,
        "T_inner_sunwall": 295.0,
        "T_inner_roof": 305.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "parameter_sweep": {
        "type": "cartesian",
        "dimensions": [
          {
            "parameter": "W_roof",
            "range": {
              "start": 0.1,
              "stop": 0.8,
              "count": 8,
              "scale": "linear"
            }
          },
          {
            "parameter": "H_W",
            "range": {
              "start": 0.2,
              "stop": 4.0,
              "count": 8,
              "scale": "log"
            }
          }
        ]
      },
      "plots": [
        {
          "id": "T_iB_heatmap",
          "type": "heatmap",
          "description": "T_iB_unclamped as a function of (W_roof, H_W): higher values (toward T_roof=305 K) occur at high W_roof / low H_W; lower values (toward wall average 290 K) occur at low W_roof / high H_W.",
          "x": {
            "variable": "W_roof",
            "label": "Roof fraction W_roof (dimensionless)"
          },
          "y": {
            "variable": "H_W",
            "label": "H/W ratio (dimensionless)"
          },
          "value": {
            "variable": "T_iB_unclamped",
            "reduce": "final"
          }
        }
      ]
    }
  ]
}