id: cal_val_match
name: Cal/Val Match-up
description: >
  Spatial-temporal match-up of in-situ point observations against a
  raster timeseries (e.g. SMAP, ASCAT, Sentinel-1 surface SM). For each
  in-situ observation, finds the satellite pixel(s) covering the station
  location and the satellite acquisition(s) within a configurable
  temporal window, returning a row per matched pair.

  The cal/val match-up table is the canonical input to scorecard
  computation (ubRMSE, bias, R, anomR) and the substrate primitive for
  satellite product validation.

version: 0.1.0
category: evaluation
type: timeseries+raster-to-table
default_implementation: native

inputs:
  - name: in_situ
    type: timeseries
    description: >
      Point timeseries with station geometry. Schema:
      (station_id, time, value, qc_flag, provider_class, geom, ...).
    required: true
  - name: raster
    type: raster_timeseries
    description: >
      Satellite or model raster timeseries (e.g. SMAP L3 daily Zarr cube).
      Must carry CRS + temporal coordinate.
    required: true

requires: {}

outputs:
  - name: matchups
    type: table
    description: >
      One row per matched (station, raster-time) pair:
      (station_id, in_situ_time, raster_time, dt_minutes, in_situ_value,
       raster_value, distance_m, provider_class, geom).

params:
  spatial:
    type: select
    options: [nearest_pixel, bilinear, mean_3x3, mean_5x5]
    default: nearest_pixel
    description: >
      Spatial sampling method for matching the in-situ point to raster pixels.
  temporal_window_minutes:
    type: number
    default: 180
    description: >
      Maximum time difference (minutes) between in-situ observation and
      satellite acquisition for a valid match. SMAP CVS convention is 180 min
      (3 h) around the 06:00 local solar time descending overpass.
  keep_partial:
    type: boolean
    default: false
    description: >
      If true, keep matched pairs where the raster pixel is masked (NaN);
      otherwise drop them.

uncertainty:
  error_model: bounded
  factors:
    - name: spatial_representativeness
      description: >
        9 km pixel vs point station — the representativeness gap is the
        single largest source of apparent ubRMSE in any cal/val. ISMN-CCI
        upscaling literature (Gruber et al. 2020) is the canonical reference.
      severity: high
      mitigations:
        - "Use triple-collocation (TC) for fairer comparison when 3+ products available."
        - "Report station-density metric per pixel."
    - name: temporal_window_choice
      description: >
        Wider windows accept more matches at the cost of diurnal cycle aliasing.
      severity: medium
  limitations:
    - "v0 implements nearest_pixel only; bilinear / mean_NxN are stubs."
    - "No triple-collocation in v0; planned follow-up."
  methodology_reference: "products/insitu-harmonization/candidates/soil-moisture.md"

# backends: audited 2026-08-14 (defect 50). NOTHING dispatches this op: no arm in folia-engine
# `dispatch_op`, no `registerOp`/OP_TABLE entry in packages/compute, no `_BUILTIN_OP_MAP`
# key in folia/compute.py, no backend manifest. Declared EMPTY on purpose — an absent
# block would be indistinguishable from one nobody ever wrote.
# the declared `folia.domains.geo.transforms.evaluation.cal_val_match` does not exist and `_BUILTIN_OP_MAP` has no key for it.
backends: {}
