You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This RFC proposes using two Swedish open-data sources to sharpen FTW's PV modeling:
SMHI STRÅNG — modelled solar irradiance, to (a) score how real panels actually performed against a weather-expected baseline, and (b) calibrate the forward PV predictor.
Lantmäteriet open geodata — building footprints + LiDAR — to derive roof tilt/azimuth automatically and let users draw their panels on a map instead of typing tilt/azimuth by hand.
Everything is additive and optional; nothing changes the control tick, dispatch, or the optimizer contract.
What FTW already has (so we build on it, not around it)
A forecast.Provider interface with met_no, openweather, open_meteo (shortwave), forecast_solar (multi-plane PVGIS); results persist to a forecasts table that already has a solar_wm2 column.
sunpos.POA(t,lat,lon,tilt,az) — plane-of-array physics already in-tree, but currently driven by its own clear-sky model and not wired into the twin.
A self-learning PV twin (RLS + residual σ) and an MPC whose Python optimizer already accepts pv_w per slot plus a robust pv_low_w / pv_high_w band.
config.Weather.PVArrays[] = {name, kWp, tilt_deg, azimuth_deg}, edited today in the Weather settings tab with a Leaflet point-picker + a Three.js 3D preview.
STRÅNG is an analysis/reanalysis product (hourly, ~2.5 km, 1999→present, ~1-day lag, CC BY 4.0, no API key). It has no forward horizon. So its value is not "STRÅNG predicts tomorrow" — it is:
a high-quality historical irradiance truth to score PV performance, and
a calibration / warm-start source for the existing forward predictor.
Forward forecasting stays on open_meteo / forecast_solar / SMHI metfcst.
STRÅNG API specifics & the SMHI forecast-API migration
Point time-series REST: https://opendata-download-metanalys.smhi.se/api/category/strang1g/version/1/geotype/point/lon/{lon}/lat/{lat}/parameter/{p}/data.json
with from / to / interval. Parameters: 117 = Global irradiance (GHI), 122 = Diffuse (verify the others against the live apidocs). Diffuse / direct-horizontal exist only from 2017-04-18.
For the forward horizon, note SMHI's forecast API migrated pmp3g → snow1gv1 (old endpoint retired 2026-03-31); confirm whether it exposes shortwave radiation or only cloud cover before relying on it.
Proposal & phasing (crawl → walk → run)
#
Phase
Value
Effort
1
POA-per-array wiring — project GHI onto each panel plane (uses the existing sunpos physics)
High
Low
2
STRÅNG client + performance scoring — expected-vs-actual performance ratio, shading/soiling/degradation signal, new GET /api/pv/performance, dashed "expected" history overlay
High
Medium
3
STRÅNG-calibrated prediction — train the twin on POA, warm-start its cold prior, feed a ±σ band into the optimizer's existingpv_low_w / pv_high_w (no contract change)
Medium
Medium
4
MapLibre "draw your array" — MapLibre GL JS (BSD-3) + Terra Draw (MIT), lazy-loaded like today's Leaflet; the numeric editor stays as a fallback
Auto-fill + provenance badge — map polygons inherit roof-segment geometry; "Lantmäteriet data from <date>" from the STAC datetime
Medium
Low-Med
Roof geometry: method, tooling, data & licensing
Method — the SPAN paper (Yavuzdoğan, Renewable Energy 2023, DOI 10.1016/j.renene.2023.119022): LiDAR → RANSAC plane fit + DBSCAN clustering → per-segment tilt/azimuth → PVGIS yield (~84 % roof-segment accuracy). Reference implementation is an open-source QGIS plugin.
Optional shading — vostok ("Voxel Octree Solar Toolkit", C++) for shadow-aware per-point irradiance from neighbouring buildings/trees.
Data — Byggnad Nedladdning, vektor (building footprints) + Laserdata Nedladdning, Skog (LiDAR, 1–2 pts/m², STAC API live 2026-06-01), both STAC, EPSG:3006, free CC BY 4.0 but gated behind a Geotorget account + OAuth2 → users bring their own credentials.
Proposed shape: an on-host, opt-in, automatic optional module
The roof module is a separate optional module (sibling to the optimizer) with a versioned roof_model.json contract, independent failure/update semantics, and a safe unavailable state — it only ever pre-fills the user-editablepv_arrays, never touching control.
Runs on the FTW host, core-invoked via POST /api/roofmodel/derive (the existing subprocess pattern), opt-in and default-off, onboarding-time only, off the control tick, in its own time-boxed process.
Automatic: on derive it writes segments straight into pv_arrays and stamps provenance — no manual JSON-shuffling. Derived values remain user-editable afterward.
Credentials (geotorget_username / geotorget_token) are stored redacted, exactly like today's weather.api_key.
Is on-host invocation possible under the GPL? (yes — arm's-length subprocess, no bundling)
The FTW-authored roofmodel/ module uses permissive libraries only (laspy BSD, scikit-learn BSD for RANSAC/DBSCAN, pystac-client, pyproj), and reimplements the SPAN method (an algorithm, not copyrightable code) — so the module itself ships with FTW without GPL obligations.
vostok is GPL-3.0, so it stays an optional, user-installed external tool invoked at arm's length (a separate process, over the CLI + files) only if present on the host and opted in. Under the FSF's mere-aggregation / arm's-length rule, invoking a separate GPL program by subprocess does not make FTW a derivative work — so FTW's own permissive license is unaffected, even when vostok runs on the same host. The single obligation is distribution: FTW must not bundle or redistribute the vostok binary — the user installs it themselves; with vostok absent, shading is simply skipped (safe unavailable). Nothing (vostok, PDAL, laspy) is ever auto-installed on the host.
This is the standard interpretation of the GPL for arm's-length subprocess use, not formal legal advice — worth a maintainer confirmation of the distribution boundary before shipping.
Why on-host is acceptable despite LiDAR weight: derivation is a one-shot onboarding step, not a runtime tick — it runs in a separate, time-boxed, cancellable process and never blocks dispatch or the control loop.
Provenance ("show when the data is from")
Reuse FTW's existing fetched_at_ms epoch-ms convention. The roof model carries captured_at_ms (from the STAC item properties.datetime, or the laser strip datum field) plus derived_at_ms, surfaced via GET /api/roofmodel and rendered as a "Lantmäteriet data from <date>" badge on the map. Lantmäteriet is backfilling STAC properties.datetime through autumn 2026, so the UI degrades gracefully to "capture date unknown" until then.
Open questions for maintainers
Appetite for a third optional module (roofmodel/, on-host, opt-in) alongside the optimizer — vs. keeping roof derivation entirely out-of-repo (import JSON only)?
MapLibre GL JS in the no-build web app: CDN+SRI (like Leaflet) vs vendored (like Three.js)? Basemap preference (OSM raster vs self-hosted PMTiles vs a Lantmäteriet orthophoto overlay)?
Is a GET /api/pv/performance endpoint + a dashed "expected (STRÅNG)" history overlay the right first slice to land (Phases 1–2, no keys, high value)?
Any objection to a STRÅNG backfill path writing to a new irradiance_history table (keeping forecasts forward-only)?
Risks
STRÅNG is historical-only → a calibration/scoring source, not a forward feed.
Geotorget access has lead time → request early even though the roof module ships last.
STAC properties.datetime backfill runs through autumn 2026 → provenance dates may be missing; the UI degrades to "date unknown".
GPL tooling (vostok, SPAN) is kept external (user-installed, invoked at arm's length, never bundled).
The optimizer band's pv_low_w / pv_high_w are site-signed ≤ 0 (generation negative) — the low/high↔pessimistic/optimistic mapping needs a direct unit test.
Attribution: SMHI STRÅNG and Lantmäteriet data are licensed CC BY 4.0. This RFC is a design proposal for discussion — no implementation is attached yet.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Summary
This RFC proposes using two Swedish open-data sources to sharpen FTW's PV modeling:
Everything is additive and optional; nothing changes the control tick, dispatch, or the optimizer contract.
What FTW already has (so we build on it, not around it)
forecast.Providerinterface withmet_no,openweather,open_meteo(shortwave),forecast_solar(multi-plane PVGIS); results persist to aforecaststable that already has asolar_wm2column.sunpos.POA(t,lat,lon,tilt,az)— plane-of-array physics already in-tree, but currently driven by its own clear-sky model and not wired into the twin.pv_wper slot plus a robustpv_low_w/pv_high_wband.config.Weather.PVArrays[]={name, kWp, tilt_deg, azimuth_deg}, edited today in the Weather settings tab with a Leaflet point-picker + a Three.js 3D preview.history_*tables +DailyEnergy()giving measured PV Wh/day.Key finding: STRÅNG is historical, not a forecast
STRÅNG is an analysis/reanalysis product (hourly, ~2.5 km, 1999→present, ~1-day lag, CC BY 4.0, no API key). It has no forward horizon. So its value is not "STRÅNG predicts tomorrow" — it is:
Forward forecasting stays on
open_meteo/forecast_solar/ SMHI metfcst.STRÅNG API specifics & the SMHI forecast-API migration
Point time-series REST:
https://opendata-download-metanalys.smhi.se/api/category/strang1g/version/1/geotype/point/lon/{lon}/lat/{lat}/parameter/{p}/data.jsonwith
from/to/interval. Parameters: 117 = Global irradiance (GHI), 122 = Diffuse (verify the others against the live apidocs). Diffuse / direct-horizontal exist only from 2017-04-18.For the forward horizon, note SMHI's forecast API migrated
pmp3g→snow1gv1(old endpoint retired 2026-03-31); confirm whether it exposes shortwave radiation or only cloud cover before relying on it.Proposal & phasing (crawl → walk → run)
sunposphysics)GET /api/pv/performance, dashed "expected" history overlaypv_low_w/pv_high_w(no contract change)pv_arrays<date>" from the STAC datetimeRoof geometry: method, tooling, data & licensing
Proposed shape: an on-host, opt-in, automatic optional module
The roof module is a separate optional module (sibling to the optimizer) with a versioned
roof_model.jsoncontract, independent failure/update semantics, and a safe unavailable state — it only ever pre-fills the user-editablepv_arrays, never touching control.POST /api/roofmodel/derive(the existing subprocess pattern), opt-in and default-off, onboarding-time only, off the control tick, in its own time-boxed process.pv_arraysand stamps provenance — no manual JSON-shuffling. Derived values remain user-editable afterward.geotorget_username/geotorget_token) are stored redacted, exactly like today'sweather.api_key.Is on-host invocation possible under the GPL? (yes — arm's-length subprocess, no bundling)
The FTW-authored
roofmodel/module uses permissive libraries only (laspy BSD, scikit-learn BSD for RANSAC/DBSCAN, pystac-client, pyproj), and reimplements the SPAN method (an algorithm, not copyrightable code) — so the module itself ships with FTW without GPL obligations.vostok is GPL-3.0, so it stays an optional, user-installed external tool invoked at arm's length (a separate process, over the CLI + files) only if present on the host and opted in. Under the FSF's mere-aggregation / arm's-length rule, invoking a separate GPL program by subprocess does not make FTW a derivative work — so FTW's own permissive license is unaffected, even when vostok runs on the same host. The single obligation is distribution: FTW must not bundle or redistribute the vostok binary — the user installs it themselves; with vostok absent, shading is simply skipped (safe unavailable). Nothing (vostok, PDAL, laspy) is ever auto-installed on the host.
This is the standard interpretation of the GPL for arm's-length subprocess use, not formal legal advice — worth a maintainer confirmation of the distribution boundary before shipping.
Why on-host is acceptable despite LiDAR weight: derivation is a one-shot onboarding step, not a runtime tick — it runs in a separate, time-boxed, cancellable process and never blocks dispatch or the control loop.
Provenance ("show when the data is from")
Reuse FTW's existing
fetched_at_msepoch-ms convention. The roof model carriescaptured_at_ms(from the STAC itemproperties.datetime, or the laser stripdatumfield) plusderived_at_ms, surfaced viaGET /api/roofmodeland rendered as a "Lantmäteriet data from<date>" badge on the map. Lantmäteriet is backfilling STACproperties.datetimethrough autumn 2026, so the UI degrades gracefully to "capture date unknown" until then.Open questions for maintainers
roofmodel/, on-host, opt-in) alongside the optimizer — vs. keeping roof derivation entirely out-of-repo (import JSON only)?GET /api/pv/performanceendpoint + a dashed "expected (STRÅNG)" history overlay the right first slice to land (Phases 1–2, no keys, high value)?irradiance_historytable (keepingforecastsforward-only)?Risks
properties.datetimebackfill runs through autumn 2026 → provenance dates may be missing; the UI degrades to "date unknown".pv_low_w/pv_high_ware site-signed ≤ 0 (generation negative) — the low/high↔pessimistic/optimistic mapping needs a direct unit test.Attribution: SMHI STRÅNG and Lantmäteriet data are licensed CC BY 4.0. This RFC is a design proposal for discussion — no implementation is attached yet.
All reactions