name: point-clear-sky-hourly
version: "0.1"
category: features-objects
themes: [energy, terrain]
family: terrain-corrected-solar-shade
license: CC-BY-4.0
description: >
  Hourly clear-sky solar irradiance on a horizontal plane at a single point,
  for one date. NOAA closed-form ephemeris for solar az/el, Kasten-Young air
  mass + Meinel transmission for DNI, flat 10% diffuse for DHI. Horizontal
  global irradiance GHI = DNI * sin(elevation) + DHI. Optionally scales to
  expected PV output: nameplate_w * (GHI / 1000) * derate. Intended for
  quick "does my panel reading make sense?" checks — a point companion to
  the raster horizon-shade recipe (same transform module, no horizon walk;
  assumes open sky, SVF = 1).

settings:
  samples_per_hour: 4          # 15-min sub-hour samples averaged per hour bin;
                               # smooths the sunrise/sunset partial hours.
  refraction_deg: 0.55         # standard near-horizon refraction correction.
  derate: 0.85                 # system losses: cell temperature (~-0.35%/degC
                               # above 25C), wiring, soiling. Override per rig.

inputs:
  point:
    lat: 49.26247933154005     # Kitsilano, Vancouver BC — van rooftop, flat
    lon: -123.1616100945195    # panels, ~3 m above ground (no irradiance
                               # effect; implies clean local horizon).
    utc_offset_hours: -7       # PDT
  date: "2026-08-10"
  panel:
    nameplate_w: null          # set to rated W to get predicted output;
                               # null = report W/m2 only.
    tilt_deg: 0                # 0 = horizontal (flat on the roof). Only 0 is
                               # implemented in v0.1.

layers:

  compute/solar-position:
    type: table
    description: >
      Solar azimuth/elevation at `samples_per_hour` sub-hour steps across the
      local-time day, NOAA closed-form (accurate to arcminutes).
    compute:
      op: solar_position_timeseries      # reference impl: folia/domains/geo/
      params:                            # transforms/insolation.py::solar_position
        lat: "${inputs.point.lat}"
        lon: "${inputs.point.lon}"
        date: "${inputs.date}"
        utc_offset_hours: "${inputs.point.utc_offset_hours}"
        samples_per_hour: "${settings.samples_per_hour}"

  result/hourly-irradiance:
    type: table
    description: >
      Per-local-hour mean clear-sky DNI, DHI, horizontal GHI (W/m2), and —
      when `panel.nameplate_w` is set — predicted panel watts
      nameplate * (GHI/1000) * derate. Max row = best hour of the day.
    compute:
      op: clear_sky_horizontal_hourly    # reference impl: insolation.py::
      inputs:                            # clear_sky_dni_dhi + hourly binning
        sun: { layer: compute/solar-position }
      params:
        refraction_deg: "${settings.refraction_deg}"
        nameplate_w: "${inputs.panel.nameplate_w}"
        derate: "${settings.derate}"

outputs:
  primary:
    - result/hourly-irradiance
  show_your_work:
    - compute/solar-position

# ─────────────────────────────────────────────────────────────────────────────
# Ops `solar_position_timeseries` / `clear_sky_horizontal_hourly` are not yet
# in the engine (new ops land in products/sdk/folia-engine per ADR-0029; the
# closed-form math is ~40 lines of Rust). Until then this recipe runs via the
# reference implementation, same as horizon-shade:
#   registry/recipes/point-clear-sky-hourly/reference_run.py
# ─────────────────────────────────────────────────────────────────────────────
