GribStream

Solar Panel Generation Forecast

This dashboard estimates hourly solar panel generation from weather-model shortwave radiation and air temperature. It starts with a normal residential-style default so nobody has to fill in a full PV design form: Denver, a 6 kW DC array, a fixed-tilt array-geometry proxy, 14 percent total PV losses, 96 percent inverter efficiency, and a 1.2 DC/AC ratio.

The forecast uses GFS, HRRR, or NBM data through the standard GribStream /api/v2/[dataset]/timeseries endpoint. The core weather input is DSWRF at the surface, NOAA's downward shortwave radiation flux field, combined with 2 m air temperature for a simple panel-temperature derate. The dashboard keeps the lead window to 24 or 36 hours so the returned rows stay hourly across the available model choices.

What the controls do

How to read the panels

The math is intentionally lightweight. It is useful for comparing weather-driven production patterns, screening a proposed install, and explaining how GribStream expressions can turn forecast fields into application metrics. It is not a bankable PV design model and it does not replace PVWatts, SAM, site shade modeling, measured soiling, or an engineering-grade plane-of-array irradiance model.

The array-geometry control is deliberately conservative. A real tilt and azimuth calculation needs solar position, direct normal irradiance, diffuse horizontal irradiance, albedo, module orientation, row geometry, and shading. This dashboard only requests DSWRF and 2 m temperature from GribStream, so it exposes the geometry assumption as a named multiplier instead of hiding a fake precision model behind a tilt field.

Math and constants

The dashboard computes every derived field in GribStream expressions. With the default controls, the equations are:

  1. temp_c = temp_k - 273.15
  2. poa_wm2 = ghi_wm2 * 1.0800
  3. cell_temp_c = temp_c + (poa_wm2 / 800.0) * 25.0000
  4. temperature_factor = max(0, 1 + (-0.3500 / 100) * (cell_temp_c - 25.0))
  5. loss_factor = max(0, 1 - 14.0000 / 100)
  6. dc_power_kw = 6.0000 * poa_wm2 / 1000.0 * temperature_factor * loss_factor
  7. ac_limit_kw = 6.0000 / 1.2000
  8. ac_power_kw = min(dc_power_kw * 96.0000 / 100, ac_limit_kw)
  9. hourly_energy_kwh = ac_power_kw, because the dashboard limits the query to hourly rows.

The most important constants are:

These constants make the forecast explainable and easy to vary, but they are not a replacement for a module datasheet, measured albedo, measured soiling, shade geometry, or a full PVWatts/SAM model run.

Representative GribStream request

This example uses the default 6 kW fixed-tilt proxy at Denver with GFS. The dashboard fills the numeric defaults from Grafana variables and computes the PV estimates in one standard GribStream /timeseries call.

curl -X POST 'https://gribstream.com/api/v2/gfs/timeseries' \
  -H 'Content-Type: application/json' \
  -H 'Accept: text/csv' \
  -H 'Authorization: Bearer [API_TOKEN]' \
  -d '{
    "fromTime": "2026-05-03T12:00:00Z",
    "untilTime": "2026-05-05T00:00:00Z",
    "minLeadTime": "0h",
    "maxLeadTime": "36h",
    "coordinates": [
      { "lat": 39.7392, "lon": -104.9903, "name": "Denver, CO" }
    ],
    "variables": [
      { "name": "DSWRF", "level": "surface", "alias": "ghi_wm2" },
      { "name": "TMP", "level": "2 m above ground", "alias": "temp_k" }
    ],
    "expressions": [
      { "expression": "temp_k - 273.15", "alias": "temp_c" },
      { "expression": "ghi_wm2 * 1.0800", "alias": "poa_wm2" },
      { "expression": "temp_c + (poa_wm2 / 800.0) * 25.0000", "alias": "cell_temp_c" },
      { "expression": "func.Max(0.0, 1 + (-0.3500 / 100.0) * (cell_temp_c - 25.0))", "alias": "temperature_factor" },
      { "expression": "func.Max(0.0, 1 - 14.0000 / 100.0)", "alias": "loss_factor" },
      { "expression": "6.0000 * poa_wm2 / 1000.0 * temperature_factor * loss_factor", "alias": "dc_power_kw" },
      { "expression": "6.0000 / 1.2000", "alias": "ac_limit_kw" },
      { "expression": "func.Min(dc_power_kw * 96.0000 / 100.0, ac_limit_kw)", "alias": "ac_power_kw" },
      { "expression": "ac_power_kw", "alias": "hourly_energy_kwh" },
      { "expression": "func.Max(0.0, (6.0000 - ac_power_kw) / 6.0000 * 100.0)", "alias": "unused_capacity_pct" }
    ]
  }'

Why the defaults are reasonable

Background and references: GribStream expressions · GFS model inventory · HRRR model inventory · NBM model inventory · NOAA GRIB2 shortwave radiation table · PVWatts V8 API · SAM irradiance definitions · Grafana variables.