Version: dev

LayerPhaseChangeEnergy

Per-layer phase change energy for the urban canopy snow/soil thermodynamics: the energy required to change a single layer's ice mass from w_ice_n at time n to w_ice_np1 at time n+1 over time step Δt (Oleson et al. 2010 Eq. 4.73). Migrated from UrbanCanopy.jl@9200edd4 src/roof_wall_road_snow_temperatures.jl:1037 (`LayerPhaseChangeEnergy` @component). The column-summed output of this component over all layers is the E_p_layers input of `TotalPhaseChangeEnergy` (Eq. 4.72). Parameterized as observed-only: w_ice_n, w_ice_np1, Δt are inputs (parameters); E_p is the algebraic output.

.esm schema
1.0.0
Source
components/urban_canopy/temps/layer_phase_change_energy.esm

Description

Per-layer phase change energy for the urban canopy snow/soil thermodynamics: the energy required to change a single layer’s ice mass from w_ice_n at time n to w_ice_np1 at time n+1 over time step Δt (Oleson et al. 2010 Eq. 4.73). Migrated from UrbanCanopy.jl@9200edd4 src/roof_wall_road_snow_temperatures.jl:1037 (LayerPhaseChangeEnergy @component). The column-summed output of this component over all layers is the E_p_layers input of TotalPhaseChangeEnergy (Eq. 4.72). Parameterized as observed-only: w_ice_n, w_ice_np1, Δt are inputs (parameters); E_p is the algebraic output.

Reference

_source_ref: UrbanCanopy.jl@9200edd4f15609f20339d3a7d104de8064544310:src/roof_wall_road_snow_temperatures.jl:1037 Upstream LayerPhaseChangeEnergy is an MTK @component whose three inputs (w_ice_n, w_ice_np1, Δt) are @parameters and whose single output (E_p) is a time-indexed @variable with one algebraic equation (Eq. 4.73). Identical equation structure preserved here.

The upstream component declares one @constant: L_f (latent heat of fusion, 3.337e5 J/kg, Oleson Table 1.4). It is encoded here as a parameter with the upstream value as default — the same convention used by sibling component snow_melt_no_layers.esm for its L_f literal. The output is the latent-heat conversion of the per-step ice mass change scaled by Δt — the simplest single-equation algebraic component in the urban canopy temps suite.

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

Tolerance rationale (model-level rel=1e-9): observed-only single-equation algebraic system (no ODE integration); the equation is one subtraction, one product, and one division, so roundoff is at the IEEE-754 ULP level (~1e-16). 1e-9 leaves comfortable headroom above ULP roundoff and matches the convention used by sibling observed-only files (total_phase_change_energy.esm uses 1e-9 for the analogous additive-identity equation of Eq. 4.72). Tighter abs: 1e-12 is asserted on outputs that are exactly zero by construction so the assertion does not silently accept a stuck-at-1e-3 regression._

Parameters

NameUnitsDefaultDescription
w_ice_nkg/m^20.0Ice mass at time n
w_ice_np1kg/m^20.0Ice mass at time n+1
Δts1.0Time step
L_fJ/kg333700.0Latent heat of fusion of water (Oleson et al. 2010 Table 1.4 — 3.337e5 J/kg)

Observed

NameUnitsDefaultDescription
E_pW/m^2Phase change energy for layer (Oleson Eq. 4.73): L_f * (w_ice_n - w_ice_np1) / Δt

Observed expressions

$$ E_{p} = \frac{L_{f} \cdot \left(w_{ice\,n} - w_{ice\,np1}\right)}{Δt} $$

Phase change energy for layer (Oleson Eq. 4.73): L_f * (w_ice_n - w_ice_np1) / Δt

Analyses

E_p_curve_vs_w_ice_np1

Sweep w_ice_np1 across the freezing-through-melting range with w_ice_n = 10 kg/m^2 and Δt = 3600 s held fixed. E_p = L_f * (10 - w_ice_np1) / 3600 — a unit-slope (with sign) line crossing zero at w_ice_np1 = 10 (no change), positive (melting) for w_ice_np1 < 10, and negative (freezing) for w_ice_np1 > 10. Surfaces Eq. 4.73 directly.

Time span: 0.0 → 1.0

Swept over: w_ice_np1

E_p vs w_ice_np1 (w_ice_n = 10, Δt = 3600): linear with negative slope, zero-crossing at w_ice_np1 = w_ice_n.

two_d_sweep_dw_dt

Joint sweep over w_ice_np1 and Δt at fixed w_ice_n = 10 kg/m^2. Surfaces the inverse-Δt scaling of Eq. 4.73: for a given Δw, halving Δt doubles E_p. Iso-energy contours are 1/Δt curves (hyperbolic) in the (Δw, Δt) plane.

Time span: 0.0 → 1.0

Swept over: w_ice_np1, Δt

E_p as a function of w_ice_np1 and Δt: iso-energy contours follow constant L_f * Δw / Δt — hyperbolic in Δt for fixed Δw.

Raw .esm

Raw .esm JSON (this component)
{
  "reference": {
    "notes": "source_ref: UrbanCanopy.jl@9200edd4f15609f20339d3a7d104de8064544310:src/roof_wall_road_snow_temperatures.jl:1037\nUpstream `LayerPhaseChangeEnergy` is an MTK `@component` whose three inputs (w_ice_n, w_ice_np1, Δt) are `@parameters` and whose single output (E_p) is a time-indexed `@variable` with one algebraic equation (Eq. 4.73). Identical equation structure preserved here.\n\nThe upstream component declares one `@constant`: L_f (latent heat of fusion, 3.337e5 J/kg, Oleson Table 1.4). It is encoded here as a parameter with the upstream value as default — the same convention used by sibling component snow_melt_no_layers.esm for its `L_f` literal. The output is the latent-heat conversion of the per-step ice mass change scaled by Δt — the simplest single-equation algebraic component in the urban canopy temps suite.\n\nObservability classification: this component is functionally observed-only — the single output variable is given by a single algebraic equation in terms of the parameters (and the `(t)` time index has no role since there is no derivative). Test scenarios use `time_span: [0, 1]` and sample at t=0 to mirror the convention used by sibling observed-only ESM files in this rig (total_phase_change_energy.esm, snow_melt_no_layers.esm, snow_capping_runoff.esm).\n\nTolerance rationale (model-level rel=1e-9): observed-only single-equation algebraic system (no ODE integration); the equation is one subtraction, one product, and one division, so roundoff is at the IEEE-754 ULP level (~1e-16). 1e-9 leaves comfortable headroom above ULP roundoff and matches the convention used by sibling observed-only files (total_phase_change_energy.esm uses 1e-9 for the analogous additive-identity equation of Eq. 4.72). Tighter `abs: 1e-12` is asserted on outputs that are exactly zero by construction so the assertion does not silently accept a stuck-at-1e-3 regression."
  },
  "tolerance": {
    "rel": 1e-09
  },
  "variables": {
    "w_ice_n": {
      "type": "parameter",
      "units": "kg/m^2",
      "default": 0.0,
      "description": "Ice mass at time n"
    },
    "w_ice_np1": {
      "type": "parameter",
      "units": "kg/m^2",
      "default": 0.0,
      "description": "Ice mass at time n+1"
    },
    "Δt": {
      "type": "parameter",
      "units": "s",
      "default": 1.0,
      "description": "Time step"
    },
    "L_f": {
      "type": "parameter",
      "units": "J/kg",
      "default": 333700.0,
      "description": "Latent heat of fusion of water (Oleson et al. 2010 Table 1.4 — 3.337e5 J/kg)"
    },
    "E_p": {
      "type": "unknown",
      "units": "W/m^2",
      "description": "Phase change energy for layer (Oleson Eq. 4.73): L_f * (w_ice_n - w_ice_np1) / Δt"
    }
  },
  "equations": [
    {
      "lhs": "E_p",
      "rhs": {
        "op": "/",
        "args": [
          {
            "op": "*",
            "args": [
              "L_f",
              {
                "op": "-",
                "args": [
                  "w_ice_n",
                  "w_ice_np1"
                ]
              }
            ]
          },
          "Δt"
        ]
      }
    }
  ],
  "tests": [
    {
      "id": "upstream_melting",
      "description": "Reproduces upstream `LayerPhaseChangeEnergy - Melting` test (UrbanCanopy.jl test/roof_wall_road_snow_temperatures_test.jl:1200). Inputs: w_ice_n = 10, w_ice_np1 = 8 (2 kg/m^2 melted), Δt = 3600 s. E_p = 3.337e5 * 2 / 3600 ≈ 185.389 W/m^2 (positive, melting branch). Anchors fidelity to the upstream Julia component at the same parameter values.",
      "parameter_overrides": {
        "w_ice_n": 10.0,
        "w_ice_np1": 8.0,
        "Δt": 3600.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "E_p",
          "time": 0.0,
          "expected": 185.38888888888889
        }
      ]
    },
    {
      "id": "upstream_no_change",
      "description": "Reproduces upstream `LayerPhaseChangeEnergy - No Change` test (UrbanCanopy.jl test/roof_wall_road_snow_temperatures_test.jl:1224). Inputs: w_ice_n = w_ice_np1 = 10, Δt = 3600 s. E_p = 0 by exact cancellation in the (w_ice_n - w_ice_np1) factor — exercises the no-phase-change limit of Eq. 4.73.",
      "parameter_overrides": {
        "w_ice_n": 10.0,
        "w_ice_np1": 10.0,
        "Δt": 3600.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "E_p",
          "time": 0.0,
          "expected": 0.0,
          "tolerance": {
            "abs": 1e-12
          }
        }
      ]
    },
    {
      "id": "freezing_branch",
      "description": "Freezing-branch sign check: when w_ice_np1 > w_ice_n (water → ice in the layer over Δt), the latent heat is released to the layer and E_p is negative. Inputs: w_ice_n = 10, w_ice_np1 = 12, Δt = 3600 s. E_p = 3.337e5 * (-2) / 3600 ≈ -185.389 W/m^2. Verifies that the equation is sign-preserving and that no spurious clamp/abs has been introduced (the upstream component has no `max(..., 0)` guard on E_p — the floor is applied downstream by SnowMeltNoLayers' M_1S, not here).",
      "parameter_overrides": {
        "w_ice_n": 10.0,
        "w_ice_np1": 12.0,
        "Δt": 3600.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "E_p",
          "time": 0.0,
          "expected": -185.38888888888889
        }
      ]
    },
    {
      "id": "complete_melt_short_dt",
      "description": "Complete melt of a 5 kg/m^2 layer in a 30-minute step (Δt = 1800 s). w_ice_n = 5, w_ice_np1 = 0 → E_p = 3.337e5 * 5 / 1800 ≈ 926.944 W/m^2 — exactly the high-melt threshold value of sibling SnowMeltNoLayers' `exact_full_melt` test (Δt = 3600, W = 10) by symmetry of the L_f * Δw / Δt expression. Exercises the all-melt-in-one-step regime.",
      "parameter_overrides": {
        "w_ice_n": 5.0,
        "w_ice_np1": 0.0,
        "Δt": 1800.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "E_p",
          "time": 0.0,
          "expected": 926.9444444444445
        }
      ]
    },
    {
      "id": "small_mass_short_dt",
      "description": "Sub-cycling regime: thin ice film (w_ice_n = 0.001 kg/m^2 ≈ 1 g/m^2) with half melted in a 1-minute substep. w_ice_n = 1.0e-3, w_ice_np1 = 5.0e-4, Δt = 60 s → E_p = 3.337e5 * 5e-4 / 60 ≈ 2.781 W/m^2. Verifies the small-mass / short-Δt regime that arises when the per-layer phase-change calculation is called from a sub-cycling outer loop.",
      "parameter_overrides": {
        "w_ice_n": 0.001,
        "w_ice_np1": 0.0005,
        "Δt": 60.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "E_p",
          "time": 0.0,
          "expected": 2.7808333333333333
        }
      ]
    },
    {
      "id": "daily_step",
      "description": "Daily-step regime (Δt = 86400 s): a 20 kg/m^2 daily ice loss (w_ice_n = 50, w_ice_np1 = 30) yields a small daily-mean E_p ≈ 77.245 W/m^2 — reflective of the time-averaged latent flux from a sustained warm-spell melt event. Exercises the long-Δt limit at the opposite extreme of `small_mass_short_dt`.",
      "parameter_overrides": {
        "w_ice_n": 50.0,
        "w_ice_np1": 30.0,
        "Δt": 86400.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "E_p",
          "time": 0.0,
          "expected": 77.24537037037037
        }
      ]
    },
    {
      "id": "all_zero",
      "description": "All-zero parameters: w_ice_n = w_ice_np1 = 0 with default Δt = 1 s. E_p = 3.337e5 * 0 / 1 = 0 by construction (no ice, no phase change). Exercises the trivial-mass limit and confirms the equation does not divide-by-zero or produce NaN at the all-zero point of parameter space.",
      "parameter_overrides": {
        "w_ice_n": 0.0,
        "w_ice_np1": 0.0,
        "Δt": 1.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "E_p",
          "time": 0.0,
          "expected": 0.0,
          "tolerance": {
            "abs": 1e-12
          }
        }
      ]
    }
  ],
  "analyses": [
    {
      "id": "E_p_curve_vs_w_ice_np1",
      "description": "Sweep w_ice_np1 across the freezing-through-melting range with w_ice_n = 10 kg/m^2 and Δt = 3600 s held fixed. E_p = L_f * (10 - w_ice_np1) / 3600 — a unit-slope (with sign) line crossing zero at w_ice_np1 = 10 (no change), positive (melting) for w_ice_np1 < 10, and negative (freezing) for w_ice_np1 > 10. Surfaces Eq. 4.73 directly.",
      "initial_state": {},
      "parameters": {
        "w_ice_n": 10.0,
        "Δt": 3600.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "parameter_sweep": {
        "type": "cartesian",
        "dimensions": [
          {
            "parameter": "w_ice_np1",
            "range": {
              "start": 0.0,
              "stop": 20.0,
              "count": 21,
              "scale": "linear"
            }
          }
        ]
      },
      "plots": [
        {
          "id": "E_p_curve",
          "type": "line",
          "description": "E_p vs w_ice_np1 (w_ice_n = 10, Δt = 3600): linear with negative slope, zero-crossing at w_ice_np1 = w_ice_n.",
          "x": {
            "variable": "w_ice_np1",
            "label": "w_ice_{n+1} (kg/m^2)"
          },
          "y": {
            "variable": "E_p",
            "label": "E_p (W/m^2)"
          }
        }
      ]
    },
    {
      "id": "two_d_sweep_dw_dt",
      "description": "Joint sweep over w_ice_np1 and Δt at fixed w_ice_n = 10 kg/m^2. Surfaces the inverse-Δt scaling of Eq. 4.73: for a given Δw, halving Δt doubles E_p. Iso-energy contours are 1/Δt curves (hyperbolic) in the (Δw, Δt) plane.",
      "initial_state": {},
      "parameters": {
        "w_ice_n": 10.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "parameter_sweep": {
        "type": "cartesian",
        "dimensions": [
          {
            "parameter": "w_ice_np1",
            "range": {
              "start": 0.0,
              "stop": 20.0,
              "count": 11,
              "scale": "linear"
            }
          },
          {
            "parameter": "Δt",
            "range": {
              "start": 60.0,
              "stop": 3600.0,
              "count": 11,
              "scale": "linear"
            }
          }
        ]
      },
      "plots": [
        {
          "id": "E_p_heatmap",
          "type": "heatmap",
          "description": "E_p as a function of w_ice_np1 and Δt: iso-energy contours follow constant L_f * Δw / Δt — hyperbolic in Δt for fixed Δw.",
          "x": {
            "variable": "w_ice_np1",
            "label": "w_ice_{n+1} (kg/m^2)"
          },
          "y": {
            "variable": "Δt",
            "label": "Δt (s)"
          },
          "value": {
            "variable": "E_p",
            "reduce": "final"
          }
        }
      ]
    }
  ]
}