name: crop-water-use
version: "0.1"
status: draft                          # layer graph authored; not yet run through
                                       # the engine. See PROVENANCE.
category: time-series-trends
themes: [agriculture, water-flood]
family: crop-phenology-monitoring
cites_papers:
  - melton-2022                        # OpenET ensemble validation framework
  - senay-2013                         # SSEBop operational ET model (alternate path)

description: >
  Per-field crop water use (actual evapotranspiration) from an OpenET ensemble
  raster time series for an AOI in the San Joaquin Valley. For each field in the
  Fields of the World boundary layer, aggregate the gridded ET signal temporally
  to a seasonal total and spatially to a per-field mean and sum, yielding a table
  that ranks fields by irrigation demand and seasonal consumption. The per-field
  ET table is the crop-water-use product; the raw ET raster stack and seasonal
  accumulation raster are the scrubable "show your work" layers.

  The primary compute path ingests OpenET ensemble ET (@openet) directly as a
  cloud-native raster time series and skips the radiometric preprocessing stack
  that a raw-satellite path requires. An alternate SSEBop-from-Landsat path
  (documented in comments below) derives ET from Landsat C2-L2 land-surface
  temperature via the operational SSEBop model (Senay 2013), providing a
  cross-check that is useful when OpenET ensemble coverage is sparse or lagged.

# ─────────────────────────────────────────────────────────────────────────────
# PROVENANCE (why this recipe exists)
#   Authored 2026-07-14 from the crop white-space suite that identified field-
#   scale evapotranspiration as an unimplemented recipe in Folia's agricultural
#   catalog. The method reproduced here follows the OpenET ensemble ET framework
#   validated in Melton et al. 2022 (melton-2022) — multi-model ensemble mean
#   over a CONUS agricultural domain. The SSEBop alternate path is grounded in
#   Senay et al. 2013 (senay-2013), the operational USGS ET model derived from
#   Landsat land-surface temperature.
#
#   Field-level aggregation is anchored to the Fields of the World (FTW) global
#   field-boundary dataset (@ftw/global-field-boundaries), which carries per-
#   field confidence attributes for quality-filtering before zonal aggregation.
#
# STATUS: draft. Op composition follows each op's documented signature but the
#   recipe has NOT been run through the engine. Validate with `folia run` on the
#   San Joaquin example AOI; reconcile any param/shape mismatches; add a
#   validations/ block; then drop status:draft.
# ─────────────────────────────────────────────────────────────────────────────

# ─────────────────────────────────────────────────────────────────────────────
# PARAMETERIZATION
#   settings.season:       the accumulation window for seasonal ET total. Aligns
#                          with the California water year (Oct-Sep) by default.
#   settings.field_stats:  which statistics to compute per field. mean ET density
#                          (mm/day or mm/month) + sum ET volume proxy.
#   settings.ftw_min_confidence: filters FTW boundaries by the per-field
#                          confidence attribute before using them as zones.
#   inputs.fields:         the FTW field-boundary vector — the zones ET is
#                          aggregated into. Any polygon set works; FTW is the
#                          default because it covers the global crop domain.
#   inputs.aoi / date_range:  the bound spatial-temporal instance.
# ─────────────────────────────────────────────────────────────────────────────

settings:
  # Seasonal accumulation window (aligns to California water year)
  season:
    start_month: 1                     # January — use calendar year for simplicity
    end_month:   12

  field_stats: [mean, sum, min, max]   # per-field ET aggregation statistics

  # FTW boundary quality gate — filter out low-confidence field delineations
  # before using them as aggregation zones (FTW carries a per-field confidence
  # attribute; 0.5 is a conservative threshold for crop-water-use attribution)
  ftw_min_confidence: 0.5

inputs:
  aoi:
    name: San Joaquin Valley cropland — Fresno / Kings Counties
    bbox: [-120.05, 36.55, -119.85, 36.72]
    crs_target: EPSG:32611
    mgrs_tile: "11SKB"

  fields:
    # Fields of the World global field-boundary dataset. Each polygon carries a
    # per-field confidence attribute (0-1) used to gate noisy delineations before
    # zonal aggregation. Required: without field zones this recipe is just a
    # gridded ET raster stack with no per-field product.
    name: Fields of the World — San Joaquin Valley subset
    uri: "@ftw/global-field-boundaries"
    confidence_attribute: confidence   # attribute name to filter on
    description: >
      Global field-boundary polygons (FTW). Filter by confidence >=
      settings.ftw_min_confidence before using as zonal_stats zones.
      License: CC-BY-4.0.

  date_range:
    start: "2023-01-01"
    end:   "2026-06-12"

  # PRIMARY data source: OpenET ensemble ET raster time series
  et_collection:
    uri: "@openet"
    asset: ensemble_mean               # monthly or seasonal ensemble-mean ET (mm)
    description: >
      OpenET ensemble evapotranspiration. Multi-model ensemble mean (mm/month)
      for irrigated and rainfed croplands at 30 m resolution. Cloud-native raster
      time series accessed via the @openet data source binding.

  # ALTERNATE source (SSEBop path — see comment block in layers below):
  # landsat_collection:
  #   uri: "@usgs/landsat/c2-l2"
  #   bands: [T1]                      # canonical thermal-1 band (the ST_B10 producer key)
  #   description: >
  #     Landsat Collection 2 Level-2 surface temperature (canonical band T1,
  #     the ST_B10 producer key; Kelvin scaled).
  #     Used by the SSEBop model (Senay 2013) to derive ET via a cold/hot
  #     end-member fraction approach. Provides temporal coverage independent of
  #     OpenET's model-run latency.

layers:

  # ── SOURCE ──────────────────────────────────────────────────────────────────

  source/et:
    type: raster_timeseries
    uri: "@openet"
    description: >
      OpenET ensemble-mean ET raster time series over the AOI: one raster per
      time step (monthly), each a spatially continuous ET map (mm/month) at 30 m.
      This is the primary data source for the crop-water-use recipe — it replaces
      the full Landsat radiometric preprocessing chain (LST -> cold/hot end-member
      -> ET fraction -> actual ET) with a calibrated multi-model ensemble product
      validated against eddy-covariance towers (Melton et al. 2022).
    params:
      aoi: { bbox: "${inputs.aoi.bbox}" }
      datetime: "${inputs.date_range.start}/${inputs.date_range.end}"
      asset: "${inputs.et_collection.asset}"
      crs_target: "${inputs.aoi.crs_target}"

  # ── ALTERNATE SOURCE PATH (SSEBop from Landsat C2-L2 ST) ────────────────────
  #
  # When OpenET coverage is sparse, lagged, or unavailable for the AOI, derive ET
  # from Landsat surface temperature via the SSEBop operational model (Senay 2013).
  #
  # source/landsat_st:
  #   type: raster_timeseries
  #   uri: "@usgs/landsat/c2-l2"
  #   description: >
  #     Landsat C2-L2 land-surface temperature (canonical band T1) time series over the AOI.
  #     Input to the SSEBop ET model. One scene per clear Landsat pass (~16-day).
  #   params:
  #     aoi: { bbox: "${inputs.aoi.bbox}" }
  #     datetime: "${inputs.date_range.start}/${inputs.date_range.end}"
  #     bands: [T1]
  #
  # compute/ssebop_et:
  #   type: raster_timeseries
  #   description: >
  #     SSEBop ET fraction from Landsat LST. The SSEBop model (Senay 2013) derives
  #     an ET fraction (ETf) from the ratio of the cold end-member temperature
  #     difference to the hot end-member temperature difference, then scales by
  #     reference ET to produce actual ET. No op in the current registry covers
  #     the full SSEBop end-member model — implement as a raster_calc expression
  #     once cold/hot end-member rasters are pre-staged, or add ssebop_et as a
  #     domain op wrapping the USGS operational SSEBop implementation.
  #   compute:
  #     op: raster_calc                # placeholder — full SSEBop needs end-member
  #     inputs:                        # rasters (Tc, Th) staged externally
  #       data: { layer: source/landsat_st }
  #     params:
  #       expression: "(Tc - b1) / (Tc - Th)"       # ETf, conceptual (b1 = T1)
  #       output_band: et_fraction
  #
  # ── END ALTERNATE PATH ────────────────────────────────────────────────────────

  # ── COMPUTE ──────────────────────────────────────────────────────────────────

  compute/seasonal_et:
    type: raster
    description: >
      Seasonal ET accumulation raster: sum the monthly ET stack (source/et) over
      the full date range to produce a single raster of total ET (mm/season) per
      pixel. temporal_reduce with op=sum collapses the (t, y, x) stack to (y, x),
      yielding the pixel-level cumulative water use for the growing season. This
      is the "show your work" raster — field aggregation in the next layer reads
      directly from this spatial total.
    compute:
      op: temporal_reduce
      inputs:
        collection: { layer: source/et }
      params:
        reducer: sum                   # accumulate ET over time (mm/season)

  compute/field_et:
    type: table
    description: >
      The crop-water-use product: one row per field with mean and total seasonal
      ET aggregated from compute/seasonal_et over each @ftw field-boundary polygon.
      zonal_stats computes mean ET density (mm/season, comparable across field
      sizes) and sum ET as a proxy for total volumetric water consumption (scale
      by pixel area to get m³). Filter zones by FTW confidence >= ftw_min_confidence
      before passing to zonal_stats so low-quality delineations do not corrupt the
      per-field signal. Sort this table to rank fields by irrigation demand, flag
      outliers exceeding a threshold, or join to crop-type or parcel records.
    compute:
      op: zonal_stats
      inputs:
        raster: { layer: compute/seasonal_et }
        zones:
          feature: "${inputs.fields.uri}"
          filter:
            attribute: "${inputs.fields.confidence_attribute}"
            min_value: "${settings.ftw_min_confidence}"
      params:
        stats: "${settings.field_stats}"

outputs:
  primary:
    - compute/field_et                 # table (geojson) — per-field seasonal ET (THE product)
  show_your_work:
    - source/et                        # zarr (t,y,x) — raw OpenET monthly raster stack
    - compute/seasonal_et              # cog (y,x)   — pixel-level seasonal ET accumulation
  provenance:
    - source_scene_ids                 # OpenET tile/scene ids per time step

# ── DASHBOARD ───────────────────────────────────────────────────────────────
dashboard:
  patterns:
    - slug: time-scrub-availability   # scrub the monthly ET raster stack over the season
      slot: temporal
      layer: source/et
    - slug: zonal-rank                # rank fields by total/mean ET; highlight outliers
      slot: aggregation
      layer: compute/field_et
    - slug: legend-as-histogram       # distribution of per-field ET across the AOI
      slot: recipe-manipulation
      layer: compute/field_et

license:
  recipe: Apache-2.0
  output_dataset: CC-BY-4.0
  upstream_data:
    - { source: sentinel-2-l2a, license: usgs-public-domain }
    - { source: ftw-global-field-boundaries, license: CC-BY-4.0 }
    - { source: openet-ensemble-et, license: CC-BY-4.0 }   # verify per OpenET TOS

# Related: crop-monitoring (the NDVI-phenology sibling that shares this family
#   and the same FTW field-zone pattern); index-timeseries (the source->index
#   skeleton). The SSEBop alternate path connects to any recipe that ingests
#   @usgs/landsat/c2-l2 ST (e.g. urban-heat-island if one is added).
# White-space provenance: crop white-space suite, 2026-07-14.
