Version: dev

SoilWaterEquilibrium

Layer-averaged equilibrium volumetric water content (θ_E, Eq. 5.101) and equilibrium matric potential at the layer node (ψ_E, Eq. 5.106) for a single soil layer, following Oleson et al. (2010, CLM4 Tech Note Ch. 5, §5.3.2.1, Eqs. 5.98–5.106). θ_E is fully saturated (θ_sat) when the water table is above the layer (z_v < z_h_upper), otherwise is computed by integrating the hydrostatic-equilibrium Clapp-Hornberger profile across the layer, clamped to [0, θ_sat]. ψ_E is derived from the Brooks-Corey inverse relation with a 1% saturation floor and a hard lower bound at ψ_min = −10⁵ m to prevent numerical underflow. Migrated from UrbanCanopy.jl@9200edd4 src/hydrology.jl:509 (`SoilWaterEquilibrium` @component). Note: the migration-tracker row for this component carried the blocking_gap `other:discretization-plan` because the row description mentioned an in-file Richards-equation PDE at src/hydrology.jl:674. Inspection shows that PDESystem lives in a separate non-`@component` helper function (`RichardsEquation`) and is not part of `SoilWaterEquilibrium`, which is a plain two-equation algebraic `@component`. No discretization plan is needed.

.esm schema
1.0.0
Source
components/urban_canopy/hydro/soil_water_equilibrium.esm

Description

Layer-averaged equilibrium volumetric water content (θ_E, Eq. 5.101) and equilibrium matric potential at the layer node (ψ_E, Eq. 5.106) for a single soil layer, following Oleson et al. (2010, CLM4 Tech Note Ch. 5, §5.3.2.1, Eqs. 5.98–5.106). θ_E is fully saturated (θ_sat) when the water table is above the layer (z_v < z_h_upper), otherwise is computed by integrating the hydrostatic-equilibrium Clapp-Hornberger profile across the layer, clamped to [0, θ_sat]. ψ_E is derived from the Brooks-Corey inverse relation with a 1% saturation floor and a hard lower bound at ψ_min = −10⁵ m to prevent numerical underflow. Migrated from UrbanCanopy.jl@9200edd4 src/hydrology.jl:509 (SoilWaterEquilibrium @component). Note: the migration-tracker row for this component carried the blocking_gap other:discretization-plan because the row description mentioned an in-file Richards-equation PDE at src/hydrology.jl:674. Inspection shows that PDESystem lives in a separate non-@component helper function (RichardsEquation) and is not part of SoilWaterEquilibrium, which is a plain two-equation algebraic @component. No discretization plan is needed.

Reference

_source_ref: UrbanCanopy.jl@9200edd4f15609f20339d3a7d104de8064544310:src/hydrology.jl:509 Upstream SoilWaterEquilibrium is an MTK @component with six @parameters (θ_sat, ψ_sat, B, z_h_upper, z_h_lower, z_v) and two time-indexed @variables (θ_E, ψ_E) given by two algebraic equations with no derivatives. Identical structure preserved here.

The upstream component declares one @constants block: ψ_min = -1.0e5 m (minimum matric potential, converted from the CLM4 value −10⁸ mm). Per the convention of sibling files in this directory (groundwater_drainage.esm, surface_runoff_infiltration.esm), @constants are promoted to parameters with fixed defaults so MTK’s unit checker is satisfied and the equation AST references them by name.

Tracker blocking_gap clarification: the migration-tracker row for this bead read blocking_gap: other:discretization-plan because the row description mentioned internally builds Richards-eq PDE (:674) via MOLFiniteDifference. Inspecting the source shows that RichardsEquation (the PDESystem builder, lines 597–683) is a separate non-@component helper function that is never called by SoilWaterEquilibrium. SoilWaterEquilibrium itself (lines 509–558) is a plain algebraic @component — two equations, no PDESystem, no MOLFiniteDifference. No discretization plan is needed.

Equation 1 (θ_E, Eqs. 5.101–5.102 simplified): The upstream docstring notes that the code uses z_v < z_h_upper as the only branch point (fully-saturated guard), then applies Eq. 5.101 with Eq. 5.105 clamping for all other cases including the water-table-within-layer zone (z_h_upper ≤ z_v ≤ z_h_lower). The Clapp-Hornberger integral term uses the ratio (ψ_sat − z_v + z_h_x)/ψ_sat raised to the exponent (1 − 1/B). When z_v is in the transition zone near z_h_upper, this ratio can become negative (since ψ_sat is small and negative), making the power non-real; the outer max(0·θ_sat, …) clamp absorbs any NaN result that symbolic evaluators may produce in that region. All inline tests deliberately target the two well-defined branches: z_v < z_h_upper (saturated) and z_v > z_h_lower + |ψ_sat| (fully below the layer, valid real domain).

Equation 2 (ψ_E, Eq. 5.106): ψ_E = max(ψ_sat · (max(θ_E, 0.01·θ_sat)/θ_sat)^(−B), ψ_min). The max(θ_E, 0.01·θ_sat) inner floor prevents division-by-zero or extreme negative potentials when θ_E → 0. The outer max(·, ψ_min) with ψ_min = −10⁵ m clamps ψ_E at the physically plausible floor (the clamping activates when the Brooks-Corey formula would give ψ_raw < −10⁵ m, which occurs when the effective saturation ratio (θ_E/θ_sat) < approximately (|ψ_sat|/|ψ_min|)^(1/B); for typical CLM4 sandy-loam params that threshold is ~0.011).

Unicode parameter and variable names (θ_sat, ψ_sat, ψ_min, θ_E, ψ_E) are preserved verbatim from upstream — sibling files in this directory and the temps/ subdirectory already established unicode identifiers as the rig convention (soil_water_flux.esm, freezing_point_depression.esm, interface_hydraulic_conductivity.esm, water_table_depth.esm).

Default parameter values: θ_sat = 0.434, ψ_sat = −0.036 m, B = 4.05 are CLM4 Table 5.1 sandy-loam soil-texture values. z_h_upper = 0.05 m, z_h_lower = 0.25 m are typical top-of-column layer interfaces; z_v = 1.0 m is a mid-range water-table depth that places the layer in the well-defined unsaturated domain and yields a non-trivial θ_E. The ψ_min constant default matches the upstream @constants value exactly.

Observability classification: this component is functionally observed-only — both outputs are given by algebraic equations in terms of the parameters (the (t) time index has no role since there are no derivatives). Test scenarios use time_span: [0, 1] and sample at t=0 following the convention of sibling observed-only ESM files in this directory.

Tolerance rationale (model-level rel=1e-9): observed-only algebraic system (no ODE integration). The longest evaluation chain involves one power (^) call per bracketed term (≈1–2 ULP relative error), one subtraction, one multiplication, and one max/min clamp — worst-case roundoff ~10×ULP ≈ 1e-15. 1e-9 leaves six orders of headroom. Saturated-branch assertions (θ_E = θ_sat, ψ_E = ψ_sat by exact arithmetic) use abs: 1e-15 to gate stuck-at-zero/stuck-at-constant regressions._

Parameters

NameUnitsDefaultDescription
θ_satm^3/m^30.434Porosity (volumetric saturation, Clapp-Hornberger Eq. 5.71)
ψ_satm-0.036Saturated matric potential (negative by convention, CLM4 Table 5.1 sandy loam, Eq. 5.74)
B14.05Clapp-Hornberger exponent (dimensionless, CLM4 Table 5.1 sandy loam, Eq. 5.72)
z_h_upperm0.05Depth of the upper interface of the soil layer (positive downward from surface)
z_h_lowerm0.25Depth of the lower interface of the soil layer (positive downward from surface)
z_vm1.0Water table depth (positive downward from surface, Eq. 5.101 input)
ψ_minm-100000.0Minimum matric potential floor (converted from −10⁸ mm; upstream @constants, Eq. 5.106 hard lower bound)

Observed

NameUnitsDefaultDescription
θ_Em^3/m^3Layer-averaged equilibrium volumetric water content (Oleson Eq. 5.101)
ψ_EmEquilibrium matric potential at the layer node (Oleson Eq. 5.106)

Observed expressions

$$ θ_{E} = \mathrm{ifelse}\left(\mathrm{<}\left(z_{v}, z_{h\,upper}\right), θ_{sat}, \mathrm{max}\left(0.0 \cdot θ_{sat}, \mathrm{min}\left(θ_{sat}, \frac{θ_{sat} \cdot ψ_{sat}}{\left(z_{h\,lower} - z_{h\,upper}\right) \cdot \left(1.0 - \frac{1.0}{B}\right)} \cdot \left(\frac{ψ_{sat} - z_{v} + z_{h\,lower}}{ψ_{sat}}^{1.0 - \frac{1.0}{B}} - \frac{ψ_{sat} - z_{v} + z_{h\,upper}}{ψ_{sat}}^{1.0 - \frac{1.0}{B}}\right)\right)\right)\right) $$

Layer-averaged equilibrium volumetric water content (Oleson Eq. 5.101)

$$ ψ_{E} = \mathrm{max}\left(ψ_{sat} \cdot \frac{\mathrm{max}\left(θ_{E}, 0.01 \cdot θ_{sat}\right)}{θ_{sat}}^{0.0 - B}, ψ_{min}\right) $$

Equilibrium matric potential at the layer node (Oleson Eq. 5.106)

Analyses

theta_E_and_psi_E_vs_z_v

Drying curves: θ_E and ψ_E vs. water table depth z_v for the standard sandy-loam layer (z_h_upper = 0.05 m, z_h_lower = 0.25 m). Sweep covers z_v ∈ [0.3, 10] m, staying in the real-valued domain of Eq. 5.101 (z_v > z_h_lower − |ψ_sat| ≈ 0.214 m). The saturated plateau (θ_E = θ_sat, z_v < z_h_upper) is not shown because the plot renderer uses eager evaluation of the ifelse else-branch which produces complex-valued intermediate results for z_v < z_h_lower − |ψ_sat|.

Time span: 0.0 → 1.0

Swept over: z_v

θ_E vs z_v unsaturated drying curve — power-law decay as the water table deepens.

ψ_E vs z_v — equilibrium matric potential deepening as the water table drops.

theta_E_soil_type_heatmap

θ_E as a joint function of water table depth z_v and Clapp-Hornberger exponent B, sweeping z_v ∈ [0.3, 10.0] m and B ∈ [3.0, 12.0] (sand to clay range). The heatmap surfaces the strong interaction: at any given z_v, clay soils (high B) retain far more water than sandy soils (low B), while at any given B, θ_E decays monotonically with z_v. Sweep starts at z_v = 0.3 m (real-valued domain for Eq. 5.101; z_v < 0.25 − |ψ_sat| is the complex boundary).

Time span: 0.0 → 1.0

Swept over: z_v, B

θ_E as a function of (z_v, B) — shows the strong soil-texture control on equilibrium water content.

Raw .esm

Raw .esm JSON (this component)
{
  "reference": {
    "notes": "source_ref: UrbanCanopy.jl@9200edd4f15609f20339d3a7d104de8064544310:src/hydrology.jl:509\nUpstream `SoilWaterEquilibrium` is an MTK `@component` with six `@parameters` (θ_sat, ψ_sat, B, z_h_upper, z_h_lower, z_v) and two time-indexed `@variables` (θ_E, ψ_E) given by two algebraic equations with no derivatives. Identical structure preserved here.\n\nThe upstream component declares one `@constants` block: `ψ_min = -1.0e5 m` (minimum matric potential, converted from the CLM4 value −10⁸ mm). Per the convention of sibling files in this directory (groundwater_drainage.esm, surface_runoff_infiltration.esm), `@constants` are promoted to parameters with fixed defaults so MTK's unit checker is satisfied and the equation AST references them by name.\n\nTracker blocking_gap clarification: the migration-tracker row for this bead read `blocking_gap: other:discretization-plan` because the row description mentioned `internally builds Richards-eq PDE (:674) via MOLFiniteDifference`. Inspecting the source shows that `RichardsEquation` (the PDESystem builder, lines 597–683) is a separate non-`@component` helper function that is never called by `SoilWaterEquilibrium`. `SoilWaterEquilibrium` itself (lines 509–558) is a plain algebraic `@component` — two equations, no PDESystem, no MOLFiniteDifference. No discretization plan is needed.\n\nEquation 1 (θ_E, Eqs. 5.101–5.102 simplified): The upstream docstring notes that the code uses `z_v < z_h_upper` as the only branch point (fully-saturated guard), then applies Eq. 5.101 with Eq. 5.105 clamping for all other cases including the water-table-within-layer zone (z_h_upper ≤ z_v ≤ z_h_lower). The Clapp-Hornberger integral term uses the ratio `(ψ_sat − z_v + z_h_x)/ψ_sat` raised to the exponent `(1 − 1/B)`. When z_v is in the transition zone near z_h_upper, this ratio can become negative (since ψ_sat is small and negative), making the power non-real; the outer `max(0·θ_sat, …)` clamp absorbs any NaN result that symbolic evaluators may produce in that region. All inline tests deliberately target the two well-defined branches: z_v < z_h_upper (saturated) and z_v > z_h_lower + |ψ_sat| (fully below the layer, valid real domain).\n\nEquation 2 (ψ_E, Eq. 5.106): `ψ_E = max(ψ_sat · (max(θ_E, 0.01·θ_sat)/θ_sat)^(−B), ψ_min)`. The `max(θ_E, 0.01·θ_sat)` inner floor prevents division-by-zero or extreme negative potentials when θ_E → 0. The outer `max(·, ψ_min)` with ψ_min = −10⁵ m clamps ψ_E at the physically plausible floor (the clamping activates when the Brooks-Corey formula would give ψ_raw < −10⁵ m, which occurs when the effective saturation ratio (θ_E/θ_sat) < approximately (|ψ_sat|/|ψ_min|)^(1/B); for typical CLM4 sandy-loam params that threshold is ~0.011).\n\nUnicode parameter and variable names (θ_sat, ψ_sat, ψ_min, θ_E, ψ_E) are preserved verbatim from upstream — sibling files in this directory and the temps/ subdirectory already established unicode identifiers as the rig convention (soil_water_flux.esm, freezing_point_depression.esm, interface_hydraulic_conductivity.esm, water_table_depth.esm).\n\nDefault parameter values: θ_sat = 0.434, ψ_sat = −0.036 m, B = 4.05 are CLM4 Table 5.1 sandy-loam soil-texture values. z_h_upper = 0.05 m, z_h_lower = 0.25 m are typical top-of-column layer interfaces; z_v = 1.0 m is a mid-range water-table depth that places the layer in the well-defined unsaturated domain and yields a non-trivial θ_E. The `ψ_min` constant default matches the upstream `@constants` value exactly.\n\nObservability classification: this component is functionally observed-only — both outputs are given by algebraic equations in terms of the parameters (the `(t)` time index has no role since there are no derivatives). Test scenarios use `time_span: [0, 1]` and sample at t=0 following the convention of sibling observed-only ESM files in this directory.\n\nTolerance rationale (model-level rel=1e-9): observed-only algebraic system (no ODE integration). The longest evaluation chain involves one power (`^`) call per bracketed term (≈1–2 ULP relative error), one subtraction, one multiplication, and one max/min clamp — worst-case roundoff ~10×ULP ≈ 1e-15. 1e-9 leaves six orders of headroom. Saturated-branch assertions (θ_E = θ_sat, ψ_E = ψ_sat by exact arithmetic) use `abs: 1e-15` to gate stuck-at-zero/stuck-at-constant regressions."
  },
  "tolerance": {
    "rel": 1e-09
  },
  "variables": {
    "θ_sat": {
      "type": "parameter",
      "units": "m^3/m^3",
      "default": 0.434,
      "description": "Porosity (volumetric saturation, Clapp-Hornberger Eq. 5.71)"
    },
    "ψ_sat": {
      "type": "parameter",
      "units": "m",
      "default": -0.036,
      "description": "Saturated matric potential (negative by convention, CLM4 Table 5.1 sandy loam, Eq. 5.74)"
    },
    "B": {
      "type": "parameter",
      "units": "1",
      "default": 4.05,
      "description": "Clapp-Hornberger exponent (dimensionless, CLM4 Table 5.1 sandy loam, Eq. 5.72)"
    },
    "z_h_upper": {
      "type": "parameter",
      "units": "m",
      "default": 0.05,
      "description": "Depth of the upper interface of the soil layer (positive downward from surface)"
    },
    "z_h_lower": {
      "type": "parameter",
      "units": "m",
      "default": 0.25,
      "description": "Depth of the lower interface of the soil layer (positive downward from surface)"
    },
    "z_v": {
      "type": "parameter",
      "units": "m",
      "default": 1.0,
      "description": "Water table depth (positive downward from surface, Eq. 5.101 input)"
    },
    "ψ_min": {
      "type": "parameter",
      "units": "m",
      "default": -100000.0,
      "description": "Minimum matric potential floor (converted from −10⁸ mm; upstream @constants, Eq. 5.106 hard lower bound)"
    },
    "θ_E": {
      "type": "unknown",
      "units": "m^3/m^3",
      "description": "Layer-averaged equilibrium volumetric water content (Oleson Eq. 5.101)"
    },
    "ψ_E": {
      "type": "unknown",
      "units": "m",
      "description": "Equilibrium matric potential at the layer node (Oleson Eq. 5.106)"
    }
  },
  "equations": [
    {
      "lhs": "θ_E",
      "rhs": {
        "op": "ifelse",
        "args": [
          {
            "op": "<",
            "args": [
              "z_v",
              "z_h_upper"
            ]
          },
          "θ_sat",
          {
            "op": "max",
            "args": [
              {
                "op": "*",
                "args": [
                  0.0,
                  "θ_sat"
                ]
              },
              {
                "op": "min",
                "args": [
                  "θ_sat",
                  {
                    "op": "*",
                    "args": [
                      {
                        "op": "/",
                        "args": [
                          {
                            "op": "*",
                            "args": [
                              "θ_sat",
                              "ψ_sat"
                            ]
                          },
                          {
                            "op": "*",
                            "args": [
                              {
                                "op": "-",
                                "args": [
                                  "z_h_lower",
                                  "z_h_upper"
                                ]
                              },
                              {
                                "op": "-",
                                "args": [
                                  1.0,
                                  {
                                    "op": "/",
                                    "args": [
                                      1.0,
                                      "B"
                                    ]
                                  }
                                ]
                              }
                            ]
                          }
                        ]
                      },
                      {
                        "op": "-",
                        "args": [
                          {
                            "op": "^",
                            "args": [
                              {
                                "op": "/",
                                "args": [
                                  {
                                    "op": "+",
                                    "args": [
                                      {
                                        "op": "-",
                                        "args": [
                                          "ψ_sat",
                                          "z_v"
                                        ]
                                      },
                                      "z_h_lower"
                                    ]
                                  },
                                  "ψ_sat"
                                ]
                              },
                              {
                                "op": "-",
                                "args": [
                                  1.0,
                                  {
                                    "op": "/",
                                    "args": [
                                      1.0,
                                      "B"
                                    ]
                                  }
                                ]
                              }
                            ]
                          },
                          {
                            "op": "^",
                            "args": [
                              {
                                "op": "/",
                                "args": [
                                  {
                                    "op": "+",
                                    "args": [
                                      {
                                        "op": "-",
                                        "args": [
                                          "ψ_sat",
                                          "z_v"
                                        ]
                                      },
                                      "z_h_upper"
                                    ]
                                  },
                                  "ψ_sat"
                                ]
                              },
                              {
                                "op": "-",
                                "args": [
                                  1.0,
                                  {
                                    "op": "/",
                                    "args": [
                                      1.0,
                                      "B"
                                    ]
                                  }
                                ]
                              }
                            ]
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    },
    {
      "lhs": "ψ_E",
      "rhs": {
        "op": "max",
        "args": [
          {
            "op": "*",
            "args": [
              "ψ_sat",
              {
                "op": "^",
                "args": [
                  {
                    "op": "/",
                    "args": [
                      {
                        "op": "max",
                        "args": [
                          "θ_E",
                          {
                            "op": "*",
                            "args": [
                              0.01,
                              "θ_sat"
                            ]
                          }
                        ]
                      },
                      "θ_sat"
                    ]
                  },
                  {
                    "op": "-",
                    "args": [
                      0.0,
                      "B"
                    ]
                  }
                ]
              }
            ]
          },
          "ψ_min"
        ]
      }
    }
  ],
  "tests": [
    {
      "id": "saturated_above_water_table",
      "description": "Water table well above the layer (z_v = 0.0 m < z_h_upper = 0.05 m): ifelse condition is true so θ_E = θ_sat = 0.434 m³/m³ exactly (Eq. 5.101 saturated branch). ψ_E = max(ψ_sat · 1^(−B), ψ_min) = ψ_sat = −0.036 m (fully saturated, Brooks-Corey at θ/θ_sat = 1). Verifies the saturated branch of both equations.",
      "parameter_overrides": {
        "θ_sat": 0.434,
        "ψ_sat": -0.036,
        "B": 4.05,
        "z_h_upper": 0.05,
        "z_h_lower": 0.25,
        "z_v": 0.0,
        "ψ_min": -100000.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "θ_E",
          "time": 0.0,
          "expected": 0.434,
          "tolerance": {
            "abs": 1e-15
          }
        },
        {
          "variable": "ψ_E",
          "time": 0.0,
          "expected": -0.036,
          "tolerance": {
            "abs": 1e-15
          }
        }
      ]
    },
    {
      "id": "saturated_just_below_upper_interface",
      "description": "Water table just inside the saturated branch (z_v = 0.03 m < z_h_upper = 0.05 m): ifelse condition still true, θ_E = θ_sat = 0.434, ψ_E = −0.036 m. Exercises the saturated branch at a non-zero z_v close to the branch boundary.",
      "parameter_overrides": {
        "θ_sat": 0.434,
        "ψ_sat": -0.036,
        "B": 4.05,
        "z_h_upper": 0.05,
        "z_h_lower": 0.25,
        "z_v": 0.03,
        "ψ_min": -100000.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "θ_E",
          "time": 0.0,
          "expected": 0.434,
          "tolerance": {
            "abs": 1e-15
          }
        },
        {
          "variable": "ψ_E",
          "time": 0.0,
          "expected": -0.036,
          "tolerance": {
            "abs": 1e-15
          }
        }
      ]
    },
    {
      "id": "unsaturated_shallow_water_table",
      "description": "Water table at z_v = 1.0 m, below the layer (z_h_lower = 0.25 m): Eq. 5.101 integral applies. θ_E = 0.19691811127 m³/m³, ψ_E = −0.88364734261 m. Representative mid-range unsaturated state for the standard sandy-loam layer.",
      "parameter_overrides": {
        "θ_sat": 0.434,
        "ψ_sat": -0.036,
        "B": 4.05,
        "z_h_upper": 0.05,
        "z_h_lower": 0.25,
        "z_v": 1.0,
        "ψ_min": -100000.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "θ_E",
          "time": 0.0,
          "expected": 0.19691811127446465
        },
        {
          "variable": "ψ_E",
          "time": 0.0,
          "expected": -0.8836473426142424
        }
      ]
    },
    {
      "id": "unsaturated_deep_water_table",
      "description": "Water table at z_v = 5.0 m: Eq. 5.101 yields θ_E = 0.12909772268638 m³/m³, ψ_E = −4.88557462258 m. Tests the drying curve at a deep water table where the layer is significantly below field capacity.",
      "parameter_overrides": {
        "θ_sat": 0.434,
        "ψ_sat": -0.036,
        "B": 4.05,
        "z_h_upper": 0.05,
        "z_h_lower": 0.25,
        "z_v": 5.0,
        "ψ_min": -100000.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "θ_E",
          "time": 0.0,
          "expected": 0.12909772268637976
        },
        {
          "variable": "ψ_E",
          "time": 0.0,
          "expected": -4.885574622575478
        }
      ]
    },
    {
      "id": "unsaturated_extreme_dry",
      "description": "Water table at z_v = 10.0 m: Eq. 5.101 integral yields θ_E = 0.10847743091340 m³/m³, ψ_E = −9.88578977956 m. Exercises the extreme-dry end of the drying curve, approaching the asymptotic residual moisture limit of Eq. 5.101.",
      "parameter_overrides": {
        "θ_sat": 0.434,
        "ψ_sat": -0.036,
        "B": 4.05,
        "z_h_upper": 0.05,
        "z_h_lower": 0.25,
        "z_v": 10.0,
        "ψ_min": -100000.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "θ_E",
          "time": 0.0,
          "expected": 0.10847743091340305
        },
        {
          "variable": "ψ_E",
          "time": 0.0,
          "expected": -9.885789779555024
        }
      ]
    },
    {
      "id": "thick_layer_saturated",
      "description": "Thicker layer (z_h_upper = 0.1 m, z_h_lower = 0.5 m) with water table above (z_v = 0.05 m < z_h_upper = 0.1 m): saturated branch fires, θ_E = θ_sat = 0.434 m³/m³, ψ_E = ψ_sat = −0.036 m. Confirms the saturation test is independent of layer thickness.",
      "parameter_overrides": {
        "θ_sat": 0.434,
        "ψ_sat": -0.036,
        "B": 4.05,
        "z_h_upper": 0.1,
        "z_h_lower": 0.5,
        "z_v": 0.05,
        "ψ_min": -100000.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "θ_E",
          "time": 0.0,
          "expected": 0.434,
          "tolerance": {
            "abs": 1e-15
          }
        },
        {
          "variable": "ψ_E",
          "time": 0.0,
          "expected": -0.036,
          "tolerance": {
            "abs": 1e-15
          }
        }
      ]
    },
    {
      "id": "thick_layer_below_water_table",
      "description": "Thicker layer (z_h_upper = 0.1 m, z_h_lower = 0.5 m) with water table at z_v = 0.8 m (below the lower interface): Eq. 5.101 integral yields θ_E = 0.22446824733 m³/m³, ψ_E = −0.51994630626 m. A moderately unsaturated state in a thicker layer, testing the integral's (z_h_lower − z_h_upper) denominator scaling.",
      "parameter_overrides": {
        "θ_sat": 0.434,
        "ψ_sat": -0.036,
        "B": 4.05,
        "z_h_upper": 0.1,
        "z_h_lower": 0.5,
        "z_v": 0.8,
        "ψ_min": -100000.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "θ_E",
          "time": 0.0,
          "expected": 0.22446824732627826
        },
        {
          "variable": "ψ_E",
          "time": 0.0,
          "expected": -0.5199463062590982
        }
      ]
    },
    {
      "id": "thick_layer_deep_water_table",
      "description": "Thicker layer (z_h_upper = 0.1 m, z_h_lower = 0.5 m) with water table at z_v = 3.0 m: Eq. 5.101 yields θ_E = 0.14900835112 m³/m³, ψ_E = −2.73295788308 m. Tests the (z_h_lower − z_h_upper) denominator scaling in a thick-layer scenario at a deep water table.",
      "parameter_overrides": {
        "θ_sat": 0.434,
        "ψ_sat": -0.036,
        "B": 4.05,
        "z_h_upper": 0.1,
        "z_h_lower": 0.5,
        "z_v": 3.0,
        "ψ_min": -100000.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "θ_E",
          "time": 0.0,
          "expected": 0.14900835112048255
        },
        {
          "variable": "ψ_E",
          "time": 0.0,
          "expected": -2.732957883081626
        }
      ]
    },
    {
      "id": "sandy_soil_saturated",
      "description": "Sandy soil texture (θ_sat = 0.395, ψ_sat = −0.012 m, B = 3.1 — CLM4 Table 5.1 coarse sand) with water table above layer (z_v = 0.0 m): saturated branch, θ_E = 0.395 m³/m³, ψ_E = −0.012 m. Verifies the saturated branch with different soil parameters.",
      "parameter_overrides": {
        "θ_sat": 0.395,
        "ψ_sat": -0.012,
        "B": 3.1,
        "z_h_upper": 0.05,
        "z_h_lower": 0.25,
        "z_v": 0.0,
        "ψ_min": -100000.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "θ_E",
          "time": 0.0,
          "expected": 0.395,
          "tolerance": {
            "abs": 1e-15
          }
        },
        {
          "variable": "ψ_E",
          "time": 0.0,
          "expected": -0.012,
          "tolerance": {
            "abs": 1e-15
          }
        }
      ]
    },
    {
      "id": "sandy_soil_unsaturated",
      "description": "Sandy soil (θ_sat = 0.395, ψ_sat = −0.012 m, B = 3.1) with water table at z_v = 2.0 m: Eq. 5.101 yields θ_E = 0.07762061602880 m³/m³, ψ_E = −1.86081534393 m. Sandy soils have shallower B exponents, producing a faster drying curve than clay-rich soils.",
      "parameter_overrides": {
        "θ_sat": 0.395,
        "ψ_sat": -0.012,
        "B": 3.1,
        "z_h_upper": 0.05,
        "z_h_lower": 0.25,
        "z_v": 2.0,
        "ψ_min": -100000.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "θ_E",
          "time": 0.0,
          "expected": 0.07762061602879547
        },
        {
          "variable": "ψ_E",
          "time": 0.0,
          "expected": -1.860815343925314
        }
      ]
    },
    {
      "id": "clay_soil_unsaturated",
      "description": "Clay soil (θ_sat = 0.468, ψ_sat = −0.405 m, B = 10.4 — CLM4 Table 5.1 heavy clay) with water table at z_v = 2.0 m: Eq. 5.101 yields θ_E = 0.39678912199 m³/m³, ψ_E = −2.25418947675 m. Clay soils have a high B exponent and deep ψ_sat, retaining much more water at the same water-table depth than sandy soils. Exercises the large-B branch of the power-law integral.",
      "parameter_overrides": {
        "θ_sat": 0.468,
        "ψ_sat": -0.405,
        "B": 10.4,
        "z_h_upper": 0.05,
        "z_h_lower": 0.25,
        "z_v": 2.0,
        "ψ_min": -100000.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "θ_E",
          "time": 0.0,
          "expected": 0.39678912198644495
        },
        {
          "variable": "ψ_E",
          "time": 0.0,
          "expected": -2.254189476753937
        }
      ]
    }
  ],
  "analyses": [
    {
      "id": "theta_E_and_psi_E_vs_z_v",
      "description": "Drying curves: θ_E and ψ_E vs. water table depth z_v for the standard sandy-loam layer (z_h_upper = 0.05 m, z_h_lower = 0.25 m). Sweep covers z_v ∈ [0.3, 10] m, staying in the real-valued domain of Eq. 5.101 (z_v > z_h_lower − |ψ_sat| ≈ 0.214 m). The saturated plateau (θ_E = θ_sat, z_v < z_h_upper) is not shown because the plot renderer uses eager evaluation of the ifelse else-branch which produces complex-valued intermediate results for z_v < z_h_lower − |ψ_sat|.",
      "initial_state": {},
      "parameters": {
        "θ_sat": 0.434,
        "ψ_sat": -0.036,
        "B": 4.05,
        "z_h_upper": 0.05,
        "z_h_lower": 0.25,
        "ψ_min": -100000.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "parameter_sweep": {
        "type": "cartesian",
        "dimensions": [
          {
            "parameter": "z_v",
            "range": {
              "start": 0.3,
              "stop": 10.0,
              "count": 50,
              "scale": "linear"
            }
          }
        ]
      },
      "plots": [
        {
          "id": "theta_E_vs_z_v_line",
          "type": "line",
          "description": "θ_E vs z_v unsaturated drying curve — power-law decay as the water table deepens.",
          "x": {
            "variable": "z_v",
            "label": "z_v (m, water table depth)"
          },
          "y": {
            "variable": "θ_E",
            "label": "θ_E (m³/m³)"
          }
        },
        {
          "id": "psi_E_vs_z_v_line",
          "type": "line",
          "description": "ψ_E vs z_v — equilibrium matric potential deepening as the water table drops.",
          "x": {
            "variable": "z_v",
            "label": "z_v (m, water table depth)"
          },
          "y": {
            "variable": "ψ_E",
            "label": "ψ_E (m)"
          }
        }
      ]
    },
    {
      "id": "theta_E_soil_type_heatmap",
      "description": "θ_E as a joint function of water table depth z_v and Clapp-Hornberger exponent B, sweeping z_v ∈ [0.3, 10.0] m and B ∈ [3.0, 12.0] (sand to clay range). The heatmap surfaces the strong interaction: at any given z_v, clay soils (high B) retain far more water than sandy soils (low B), while at any given B, θ_E decays monotonically with z_v. Sweep starts at z_v = 0.3 m (real-valued domain for Eq. 5.101; z_v < 0.25 − |ψ_sat| is the complex boundary).",
      "initial_state": {},
      "parameters": {
        "θ_sat": 0.434,
        "ψ_sat": -0.036,
        "z_h_upper": 0.05,
        "z_h_lower": 0.25,
        "ψ_min": -100000.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "parameter_sweep": {
        "type": "cartesian",
        "dimensions": [
          {
            "parameter": "z_v",
            "range": {
              "start": 0.3,
              "stop": 10.0,
              "count": 20,
              "scale": "linear"
            }
          },
          {
            "parameter": "B",
            "range": {
              "start": 3.0,
              "stop": 12.0,
              "count": 15,
              "scale": "linear"
            }
          }
        ]
      },
      "plots": [
        {
          "id": "theta_E_heatmap",
          "type": "heatmap",
          "description": "θ_E as a function of (z_v, B) — shows the strong soil-texture control on equilibrium water content.",
          "x": {
            "variable": "z_v",
            "label": "z_v (m, water table depth)"
          },
          "y": {
            "variable": "B",
            "label": "B (Clapp-Hornberger exponent)"
          },
          "value": {
            "variable": "θ_E",
            "reduce": "final"
          }
        }
      ]
    }
  ]
}