Version: dev

SoilWaterFlux

Soil water flux at the interface between two adjacent soil layers, computed from Darcy's law with the Zeng and Decker (2009) equilibrium-potential correction (Oleson et al. 2010, CLM4 Tech Note Eq. 5.88). q = -k_interface * ((ψ_upper - ψ_lower) + (ψ_E_lower - ψ_E_upper)) / (z_lower - z_upper), positive upward. Migrated from UrbanCanopy.jl@9200edd4 src/hydrology.jl:471 (`SoilWaterFlux` @component). Observed-only: the seven inputs are parameters and q is given by a single algebraic equation.

Domain
urban_canopy
Subdomain
hydro
Type
model
.esm schema
1.0.0
Source
components/urban_canopy/hydro/soil_water_flux.esm
Tags
#urban_canopy #hydro #model #hydrology #soil #darcy #observed-only #clmu

Description

Soil water flux at the interface between two adjacent soil layers, computed from Darcy’s law with the Zeng and Decker (2009) equilibrium-potential correction (Oleson et al. 2010, CLM4 Tech Note Eq. 5.88). q = -k_interface * ((ψ_upper - ψ_lower) + (ψ_E_lower - ψ_E_upper)) / (z_lower - z_upper), positive upward. Migrated from UrbanCanopy.jl@9200edd4 src/hydrology.jl:471 (SoilWaterFlux @component). Observed-only: the seven inputs are parameters and q is given by a single algebraic equation.

Reference

_source_ref: UrbanCanopy.jl@9200edd4f15609f20339d3a7d104de8064544310:src/hydrology.jl:471 Upstream SoilWaterFlux is an MTK @component whose seven inputs (k_interface, ψ_upper, ψ_lower, ψ_E_upper, ψ_E_lower, z_upper, z_lower) are @parameters and whose single output (q) is a time-indexed @variable with one algebraic equation (Eq. 5.88, no derivative). Identical structure preserved here.

Unicode parameter names ψ_upper, ψ_lower, ψ_E_upper, ψ_E_lower are preserved from upstream — sibling files in this directory (soil_water_content_calc.esm, water_table_depth.esm, interface_hydraulic_conductivity.esm) and components/urban_canopy/temps/freezing_point_depression.esm already established that unicode identifiers are the rig convention for new urban_canopy migrations and the canonical Python runner accepts them.

The upstream component has no @constants block and no defaults in @parameters. Defaults here are taken from the upstream Equation Verification test (UrbanCanopy.jl test/hydrology_test.jl:690, equilibrium case): k_interface=1.0e-5 m/s, ψ_upper=-0.5 m, ψ_lower=-1.0 m, ψ_E_upper=-0.3 m, ψ_E_lower=-0.8 m, z_upper=0.1 m, z_lower=0.3 m → q = -1e-5 * ((-0.5-(-1.0)) + (-0.8-(-0.3))) / (0.3-0.1) = -1e-5 * (0.5 + (-0.5)) / 0.2 = 0 m/s. The default state is the canonical equilibrium for which the gravitational-equilibrium correction exactly cancels the matric-potential gradient — a useful default since any non-trivial perturbation surfaces a non-zero flux.

Eq. 5.88 form: q = -k[z_h] * [(ψ_i - ψ_{i+1}) + (ψ_{E,i+1} - ψ_{E,i})] / (z_{i+1} - z_i). The bracketed term is the perturbation of the matric potential from the local hydrostatic equilibrium (Zeng and Decker 2009) — ψ_E is the equilibrium matric potential at each layer node and ψ - ψ_E is the deviation that drives flux. Without the correction, gravitationally-induced equilibrium gradients would produce a spurious flux; with the correction, the flux vanishes when ψ_upper - ψ_lower = ψ_E_upper - ψ_E_lower (the default scenario).

Sign convention: q is positive upward. The leading minus sign is encoded in the AST via {op: '*', args: [-1, k_interface, ...]}, matching the convention established by sibling files (snow_compaction.esm uses -1 as a literal first argument to the * op).

Observability classification: this component is functionally observed-only — q is given by a single algebraic equation in the parameters (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 the other observed-only ESM files in this directory (soil_water_content_calc.esm, water_table_depth.esm, interface_hydraulic_conductivity.esm).

Tolerance rationale (model-level rel=1e-9): observed-only algebraic system (no ODE integration); the equation is a chain of three subtractions, one addition, one multiplication (by -1 and k_interface), and one division — eight IEEE-754 float64 operations, worst-case roundoff ~8*ULP ≈ 1e-15. 1e-9 leaves comfortable headroom and is well below any plausible physical-discrimination scale on soil water fluxes (m/s, O(1e-6) for the saturated end and O(1e-12) for the dry end of CLMU soils; gradients across realistic layer separations of O(0.1 m) span ~6 orders of magnitude). Equilibrium / zero-flux tests assert abs: 1e-15 since the upstream Julia test pins exactly that bound (Eq. 5.88 evaluates to a closed-form zero when the matric-potential perturbation vanishes)._

Parameters

NameUnitsDefaultDescription
k_interfacem/s1e-05Hydraulic conductivity at the interface between adjacent layers (Oleson Eq. 5.88)
ψ_upperm-0.5Soil matric potential of the upper layer (negative by convention, SI m)
ψ_lowerm-1.0Soil matric potential of the lower layer (negative by convention, SI m)
ψ_E_upperm-0.3Equilibrium matric potential of the upper layer (Zeng and Decker 2009 correction, SI m)
ψ_E_lowerm-0.8Equilibrium matric potential of the lower layer (Zeng and Decker 2009 correction, SI m)
z_upperm0.1Node depth of the upper layer (positive downward from surface)
z_lowerm0.3Node depth of the lower layer (positive downward from surface)

Observed

NameUnitsDefaultDescription
qm/sSoil water flux at the interface (Oleson Eq. 5.88, positive upward)

Observed expressions

$$ q = \frac{\left(-1\right) \cdot k_{interface} \cdot \left(ψ_{upper} - ψ_{lower} + ψ_{E\,lower} - ψ_{E\,upper}\right)}{z_{lower} - z_{upper}} $$

Soil water flux at the interface (Oleson Eq. 5.88, positive upward)

Analyses

q_vs_psi_upper

Soil water flux vs. upper-layer matric potential at fixed lower-layer state and uniform equilibrium correction. Sweeping ψ_upper from -2.0 m (dry) to 0 m (saturated) at fixed ψ_lower = -0.5 m traces the linear Darcy response of Eq. 5.88: q is positive (upward) when ψ_upper < ψ_lower (capillary rise from wetter lower layer) and negative (downward) when ψ_upper > ψ_lower.

Time span: 0.0 → 1.0

Swept over: ψ_upper

q vs ψ_upper — linear Darcy response through the gradient zero-crossing at ψ_upper = ψ_lower.

q_vs_psi_upper_and_k

Joint sweep over ψ_upper and k_interface — surfaces the iso-q contours of Eq. 5.88. Higher k_interface amplifies the same matric-potential gradient into proportionally larger fluxes; the iso-q lines are hyperbolae in (ψ_upper, k_interface) space at fixed denominator.

Time span: 0.0 → 1.0

Swept over: ψ_upper, k_interface

q as a function of upper-layer matric potential and hydraulic conductivity (log k axis).

Raw .esm

Raw .esm JSON (this component)
{
  "reference": {
    "notes": "source_ref: UrbanCanopy.jl@9200edd4f15609f20339d3a7d104de8064544310:src/hydrology.jl:471\nUpstream `SoilWaterFlux` is an MTK `@component` whose seven inputs (k_interface, ψ_upper, ψ_lower, ψ_E_upper, ψ_E_lower, z_upper, z_lower) are `@parameters` and whose single output (q) is a time-indexed `@variable` with one algebraic equation (Eq. 5.88, no derivative). Identical structure preserved here.\n\nUnicode parameter names ψ_upper, ψ_lower, ψ_E_upper, ψ_E_lower are preserved from upstream — sibling files in this directory (soil_water_content_calc.esm, water_table_depth.esm, interface_hydraulic_conductivity.esm) and components/urban_canopy/temps/freezing_point_depression.esm already established that unicode identifiers are the rig convention for new urban_canopy migrations and the canonical Python runner accepts them.\n\nThe upstream component has no `@constants` block and no defaults in `@parameters`. Defaults here are taken from the upstream Equation Verification test (UrbanCanopy.jl test/hydrology_test.jl:690, equilibrium case): k_interface=1.0e-5 m/s, ψ_upper=-0.5 m, ψ_lower=-1.0 m, ψ_E_upper=-0.3 m, ψ_E_lower=-0.8 m, z_upper=0.1 m, z_lower=0.3 m → q = -1e-5 * ((-0.5-(-1.0)) + (-0.8-(-0.3))) / (0.3-0.1) = -1e-5 * (0.5 + (-0.5)) / 0.2 = 0 m/s. The default state is the canonical equilibrium for which the gravitational-equilibrium correction exactly cancels the matric-potential gradient — a useful default since any non-trivial perturbation surfaces a non-zero flux.\n\nEq. 5.88 form: q = -k[z_h] * [(ψ_i - ψ_{i+1}) + (ψ_{E,i+1} - ψ_{E,i})] / (z_{i+1} - z_i). The bracketed term is the *perturbation* of the matric potential from the local hydrostatic equilibrium (Zeng and Decker 2009) — ψ_E is the equilibrium matric potential at each layer node and ψ - ψ_E is the deviation that drives flux. Without the correction, gravitationally-induced equilibrium gradients would produce a spurious flux; with the correction, the flux vanishes when ψ_upper - ψ_lower = ψ_E_upper - ψ_E_lower (the default scenario).\n\nSign convention: q is positive *upward*. The leading minus sign is encoded in the AST via `{op: '*', args: [-1, k_interface, ...]}`, matching the convention established by sibling files (snow_compaction.esm uses `-1` as a literal first argument to the `*` op).\n\nObservability classification: this component is functionally observed-only — q is given by a single algebraic equation in the parameters (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 the other observed-only ESM files in this directory (soil_water_content_calc.esm, water_table_depth.esm, interface_hydraulic_conductivity.esm).\n\nTolerance rationale (model-level rel=1e-9): observed-only algebraic system (no ODE integration); the equation is a chain of three subtractions, one addition, one multiplication (by -1 and k_interface), and one division — eight IEEE-754 float64 operations, worst-case roundoff ~8*ULP ≈ 1e-15. 1e-9 leaves comfortable headroom and is well below any plausible physical-discrimination scale on soil water fluxes (m/s, O(1e-6) for the saturated end and O(1e-12) for the dry end of CLMU soils; gradients across realistic layer separations of O(0.1 m) span ~6 orders of magnitude). Equilibrium / zero-flux tests assert `abs: 1e-15` since the upstream Julia test pins exactly that bound (Eq. 5.88 evaluates to a closed-form zero when the matric-potential perturbation vanishes)."
  },
  "tolerance": {
    "rel": 1e-09
  },
  "variables": {
    "k_interface": {
      "type": "parameter",
      "units": "m/s",
      "default": 1e-05,
      "description": "Hydraulic conductivity at the interface between adjacent layers (Oleson Eq. 5.88)"
    },
    "ψ_upper": {
      "type": "parameter",
      "units": "m",
      "default": -0.5,
      "description": "Soil matric potential of the upper layer (negative by convention, SI m)"
    },
    "ψ_lower": {
      "type": "parameter",
      "units": "m",
      "default": -1.0,
      "description": "Soil matric potential of the lower layer (negative by convention, SI m)"
    },
    "ψ_E_upper": {
      "type": "parameter",
      "units": "m",
      "default": -0.3,
      "description": "Equilibrium matric potential of the upper layer (Zeng and Decker 2009 correction, SI m)"
    },
    "ψ_E_lower": {
      "type": "parameter",
      "units": "m",
      "default": -0.8,
      "description": "Equilibrium matric potential of the lower layer (Zeng and Decker 2009 correction, SI m)"
    },
    "z_upper": {
      "type": "parameter",
      "units": "m",
      "default": 0.1,
      "description": "Node depth of the upper layer (positive downward from surface)"
    },
    "z_lower": {
      "type": "parameter",
      "units": "m",
      "default": 0.3,
      "description": "Node depth of the lower layer (positive downward from surface)"
    },
    "q": {
      "type": "unknown",
      "units": "m/s",
      "description": "Soil water flux at the interface (Oleson Eq. 5.88, positive upward)"
    }
  },
  "equations": [
    {
      "lhs": "q",
      "rhs": {
        "op": "/",
        "args": [
          {
            "op": "*",
            "args": [
              -1,
              "k_interface",
              {
                "op": "+",
                "args": [
                  {
                    "op": "-",
                    "args": [
                      "ψ_upper",
                      "ψ_lower"
                    ]
                  },
                  {
                    "op": "-",
                    "args": [
                      "ψ_E_lower",
                      "ψ_E_upper"
                    ]
                  }
                ]
              }
            ]
          },
          {
            "op": "-",
            "args": [
              "z_lower",
              "z_upper"
            ]
          }
        ]
      }
    }
  ],
  "tests": [
    {
      "id": "equilibrium_zero_flux",
      "description": "Canonical equilibrium case from upstream Equation Verification (UrbanCanopy.jl test/hydrology_test.jl:699): k_interface = 1e-5 m/s, ψ_upper = -0.5 m, ψ_lower = -1.0 m, ψ_E_upper = -0.3 m, ψ_E_lower = -0.8 m, z_upper = 0.1 m, z_lower = 0.3 m. The matric-potential gradient (ψ_upper - ψ_lower = 0.5 m) exactly cancels the equilibrium-potential gradient (ψ_E_lower - ψ_E_upper = -0.5 m), so the Zeng and Decker (2009) perturbation vanishes and q = 0 m/s. Verifies the equilibrium branch of Eq. 5.88 — the primary purpose of the equilibrium-potential correction.",
      "parameter_overrides": {
        "k_interface": 1e-05,
        "ψ_upper": -0.5,
        "ψ_lower": -1.0,
        "ψ_E_upper": -0.3,
        "ψ_E_lower": -0.8,
        "z_upper": 0.1,
        "z_lower": 0.3
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "q",
          "time": 0.0,
          "expected": 0.0,
          "tolerance": {
            "abs": 1e-15
          }
        }
      ]
    },
    {
      "id": "non_zero_downward_flux",
      "description": "Non-zero downward flux from upstream Equation Verification (UrbanCanopy.jl test/hydrology_test.jl:715): k_interface = 1e-5 m/s, ψ_upper = -0.2 m, ψ_lower = -0.5 m, ψ_E_upper = ψ_E_lower = -0.1 m (uniform equilibrium → no correction), z_upper = 0.1 m, z_lower = 0.3 m. The matric-potential perturbation drives flux: q = -1e-5 * ((-0.2 - (-0.5)) + 0) / 0.2 = -1e-5 * 0.3 / 0.2 = -1.5e-5 m/s (negative → downward, consistent with the upper layer being wetter / less negative ψ). Verifies the non-equilibrium branch of Eq. 5.88.",
      "parameter_overrides": {
        "k_interface": 1e-05,
        "ψ_upper": -0.2,
        "ψ_lower": -0.5,
        "ψ_E_upper": -0.1,
        "ψ_E_lower": -0.1,
        "z_upper": 0.1,
        "z_lower": 0.3
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "q",
          "time": 0.0,
          "expected": -1.5e-05
        }
      ]
    },
    {
      "id": "upward_flux_dry_upper",
      "description": "Upward flux: the lower layer is wetter (less negative ψ) than the upper layer, so soil water moves upward to relieve the matric-potential gradient (capillary rise). k_interface = 2e-6 m/s, ψ_upper = -1.5 m, ψ_lower = -0.5 m, uniform equilibrium ψ_E_* = -0.1 m, z_upper = 0.1 m, z_lower = 0.3 m. q = -2e-6 * ((-1.5 - (-0.5)) + 0) / 0.2 = -2e-6 * (-1.0) / 0.2 = +1.0e-5 m/s (positive → upward). Verifies the sign convention (positive upward) and an upward-driven flux.",
      "parameter_overrides": {
        "k_interface": 2e-06,
        "ψ_upper": -1.5,
        "ψ_lower": -0.5,
        "ψ_E_upper": -0.1,
        "ψ_E_lower": -0.1,
        "z_upper": 0.1,
        "z_lower": 0.3
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "q",
          "time": 0.0,
          "expected": 1e-05
        }
      ]
    },
    {
      "id": "pure_equilibrium_perturbation",
      "description": "Pure equilibrium-perturbation flux: the matric potentials are uniform (ψ_upper = ψ_lower) so the actual gradient is zero, but the equilibrium potentials differ. q is driven entirely by the (ψ_E_lower - ψ_E_upper) term. k_interface = 1e-5 m/s, ψ_upper = ψ_lower = -0.5 m, ψ_E_upper = -0.4 m, ψ_E_lower = -0.6 m, z_upper = 0.1 m, z_lower = 0.3 m. q = -1e-5 * (0 + (-0.6 - (-0.4))) / 0.2 = -1e-5 * (-0.2) / 0.2 = +1.0e-5 m/s. Isolates the Zeng-Decker correction term and confirms its sign convention.",
      "parameter_overrides": {
        "k_interface": 1e-05,
        "ψ_upper": -0.5,
        "ψ_lower": -0.5,
        "ψ_E_upper": -0.4,
        "ψ_E_lower": -0.6,
        "z_upper": 0.1,
        "z_lower": 0.3
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "q",
          "time": 0.0,
          "expected": 1e-05
        }
      ]
    },
    {
      "id": "wider_layer_separation",
      "description": "Wider layer separation reduces the gradient and so reduces |q| at fixed potentials. Same matric and equilibrium potentials as `non_zero_downward_flux`, but z_lower = 1.1 m instead of 0.3 m so (z_lower - z_upper) = 1.0 m (5× larger). q = -1e-5 * 0.3 / 1.0 = -3.0e-6 m/s, i.e. 5× smaller magnitude than the canonical 0.3 m separation case. Exercises the 1/(z_lower - z_upper) dependence in Eq. 5.88.",
      "parameter_overrides": {
        "k_interface": 1e-05,
        "ψ_upper": -0.2,
        "ψ_lower": -0.5,
        "ψ_E_upper": -0.1,
        "ψ_E_lower": -0.1,
        "z_upper": 0.1,
        "z_lower": 1.1
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "q",
          "time": 0.0,
          "expected": -3e-06
        }
      ]
    },
    {
      "id": "high_conductivity_proportional",
      "description": "Flux is linearly proportional to k_interface. With the same `non_zero_downward_flux` potentials and geometry but k_interface = 1e-4 m/s (10× larger), q = -1e-4 * 0.3 / 0.2 = -1.5e-4 m/s (10× larger magnitude). Exercises the linear k_interface scaling in Eq. 5.88 — important since k_interface itself varies over many orders of magnitude across the soil saturation range (~1e-12 to ~1e-5 m/s).",
      "parameter_overrides": {
        "k_interface": 0.0001,
        "ψ_upper": -0.2,
        "ψ_lower": -0.5,
        "ψ_E_upper": -0.1,
        "ψ_E_lower": -0.1,
        "z_upper": 0.1,
        "z_lower": 0.3
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "q",
          "time": 0.0,
          "expected": -0.00015
        }
      ]
    },
    {
      "id": "zero_conductivity_zero_flux",
      "description": "Zero hydraulic conductivity (e.g. fully frozen layer, k_interface → 0) blocks all flux regardless of the matric-potential gradient. k_interface = 0.0, all other parameters from the `non_zero_downward_flux` scenario. q = 0 * (anything) / (anything > 0) = 0 m/s. Verifies the k_interface = 0 limit of Eq. 5.88 — physically meaningful for frozen or impervious interfaces.",
      "parameter_overrides": {
        "k_interface": 0.0,
        "ψ_upper": -0.2,
        "ψ_lower": -0.5,
        "ψ_E_upper": -0.1,
        "ψ_E_lower": -0.1,
        "z_upper": 0.1,
        "z_lower": 0.3
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "q",
          "time": 0.0,
          "expected": 0.0,
          "tolerance": {
            "abs": 1e-15
          }
        }
      ]
    },
    {
      "id": "deep_soil_low_conductivity",
      "description": "Deep soil column with low conductivity and a small matric-potential gradient — representative of a near-saturation interior layer pair. k_interface = 5e-8 m/s, ψ_upper = -2.0 m, ψ_lower = -2.1 m (small gradient), uniform equilibrium ψ_E_* = -1.0 m, z_upper = 1.5 m, z_lower = 2.0 m (deep interface, 0.5 m separation). q = -5e-8 * ((-2.0 - (-2.1)) + 0) / 0.5 = -5e-8 * 0.1 / 0.5 = -1.0e-8 m/s. Tests Eq. 5.88 at a small-magnitude operating point characteristic of the deep mineral-soil column.",
      "parameter_overrides": {
        "k_interface": 5e-08,
        "ψ_upper": -2.0,
        "ψ_lower": -2.1,
        "ψ_E_upper": -1.0,
        "ψ_E_lower": -1.0,
        "z_upper": 1.5,
        "z_lower": 2.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "q",
          "time": 0.0,
          "expected": -1e-08
        }
      ]
    }
  ],
  "analyses": [
    {
      "id": "q_vs_psi_upper",
      "description": "Soil water flux vs. upper-layer matric potential at fixed lower-layer state and uniform equilibrium correction. Sweeping ψ_upper from -2.0 m (dry) to 0 m (saturated) at fixed ψ_lower = -0.5 m traces the linear Darcy response of Eq. 5.88: q is positive (upward) when ψ_upper < ψ_lower (capillary rise from wetter lower layer) and negative (downward) when ψ_upper > ψ_lower.",
      "initial_state": {},
      "parameters": {
        "k_interface": 1e-05,
        "ψ_lower": -0.5,
        "ψ_E_upper": -0.1,
        "ψ_E_lower": -0.1,
        "z_upper": 0.1,
        "z_lower": 0.3
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "parameter_sweep": {
        "type": "cartesian",
        "dimensions": [
          {
            "parameter": "ψ_upper",
            "range": {
              "start": -2.0,
              "stop": 0.0,
              "count": 21,
              "scale": "linear"
            }
          }
        ]
      },
      "plots": [
        {
          "id": "q_vs_psi_upper_line",
          "type": "line",
          "description": "q vs ψ_upper — linear Darcy response through the gradient zero-crossing at ψ_upper = ψ_lower.",
          "x": {
            "variable": "ψ_upper",
            "label": "ψ_upper (m)"
          },
          "y": {
            "variable": "q",
            "label": "q (m/s, +upward)"
          }
        }
      ]
    },
    {
      "id": "q_vs_psi_upper_and_k",
      "description": "Joint sweep over ψ_upper and k_interface — surfaces the iso-q contours of Eq. 5.88. Higher k_interface amplifies the same matric-potential gradient into proportionally larger fluxes; the iso-q lines are hyperbolae in (ψ_upper, k_interface) space at fixed denominator.",
      "initial_state": {},
      "parameters": {
        "ψ_lower": -0.5,
        "ψ_E_upper": -0.1,
        "ψ_E_lower": -0.1,
        "z_upper": 0.1,
        "z_lower": 0.3
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "parameter_sweep": {
        "type": "cartesian",
        "dimensions": [
          {
            "parameter": "ψ_upper",
            "range": {
              "start": -2.0,
              "stop": 0.0,
              "count": 13,
              "scale": "linear"
            }
          },
          {
            "parameter": "k_interface",
            "range": {
              "start": 1e-07,
              "stop": 0.0001,
              "count": 10,
              "scale": "log"
            }
          }
        ]
      },
      "plots": [
        {
          "id": "q_heatmap",
          "type": "heatmap",
          "description": "q as a function of upper-layer matric potential and hydraulic conductivity (log k axis).",
          "x": {
            "variable": "ψ_upper",
            "label": "ψ_upper (m)"
          },
          "y": {
            "variable": "k_interface",
            "label": "k_interface (m/s, log scale)"
          },
          "value": {
            "variable": "q",
            "reduce": "final"
          }
        }
      ]
    }
  ]
}