Skip to content

Commit 9165569

Browse files
committed
chore: Apply lint fixes after rebase
1 parent a97059a commit 9165569

5 files changed

Lines changed: 1005 additions & 4 deletions

File tree

quartz_solar_forecast/forecast.py

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from quartz_solar_forecast.data import get_nwp, make_pv_data
77
from quartz_solar_forecast.forecasts import (
8+
LightGBMSolarPredictor,
89
TryolabsSolarPowerPredictor,
910
forecast_v1_tilt_orientation,
1011
)
@@ -121,6 +122,64 @@ def predict_tryolabs(site: PVSite, ts: datetime | str = None):
121122
return predictions
122123

123124

125+
def predict_lightgbm(site: PVSite, ts: datetime | str = None):
126+
"""
127+
Run the forecast with the LightGBM model.
128+
129+
This model uses enhanced feature engineering including solar position,
130+
cyclical time features, and derived weather features.
131+
132+
:param site: the PV site
133+
:param ts: the timestamp of the site. If None, defaults to the current
134+
timestamp rounded down to 15 minutes.
135+
:return: The PV forecast of the site for time (ts) for 48 hours
136+
"""
137+
138+
# instantiate class to make predictions
139+
solar_power_predictor = LightGBMSolarPredictor()
140+
141+
# set start and end time, if no time is given use current time
142+
if ts is None:
143+
start_date = pd.Timestamp.now().strftime("%Y-%m-%d")
144+
start_time = pd.Timestamp.now().round(freq="h")
145+
else:
146+
start_date = pd.Timestamp(ts).strftime("%Y-%m-%d")
147+
start_time = pd.Timestamp(ts).round(freq="h")
148+
149+
end_time = start_time + pd.Timedelta(hours=48)
150+
start_date_datetime = datetime.strptime(start_date, "%Y-%m-%d")
151+
152+
# Check if the start date is more than 3 months ago
153+
three_months_ago = datetime.today() - timedelta(days=3 * 30)
154+
155+
if start_date_datetime < three_months_ago:
156+
print(
157+
f"Start date ({start_date}) is more than 3 months ago, no",
158+
"forecast data available.",
159+
)
160+
return None
161+
else:
162+
# load model (will use physics-based fallback if not trained yet)
163+
solar_power_predictor.load_model()
164+
# make predictions
165+
predictions = solar_power_predictor.predict_power_output(
166+
latitude=site.latitude,
167+
longitude=site.longitude,
168+
start_date=start_date,
169+
kwp=site.capacity_kwp,
170+
orientation=site.orientation,
171+
tilt=site.tilt,
172+
)
173+
174+
# postprocessing of the dataframe
175+
predictions = predictions[
176+
(predictions["date"] >= start_time) & (predictions["date"] < end_time)
177+
]
178+
predictions = predictions.reset_index(drop=True)
179+
predictions.set_index("date", inplace=True)
180+
print("Predictions finished.")
181+
return predictions
182+
124183
def run_forecast(
125184
site: PVSite,
126185
model: str = "gb",
@@ -132,8 +191,10 @@ def run_forecast(
132191
Predict solar power output for a given site using a specified model.
133192
134193
:param site: the PV site
135-
:param model: the model to use for prediction, choose between "ocf" and "tryolabs",
136-
by default "ocf" is used
194+
:param model: the model to use for prediction. Options:
195+
- "gb": Gradient Boosting (default, OCF model)
196+
- "xgb": XGBoost (Tryolabs model)
197+
- "lgbm": LightGBM with enhanced features (experimental)
137198
:param ts: the timestamp of the site. If None, defaults to the current
138199
timestamp rounded down to 15 minutes.
139200
:param nwp_source: the nwp data source. Either "gfs", "icon" or "ukmo". Defaults to "icon"
@@ -160,5 +221,11 @@ def run_forecast(
160221
"Ignoring live_generation input.")
161222
return predict_tryolabs(site, ts)
162223

224+
elif model == "lgbm":
225+
if live_generation is not None:
226+
log.warning("Live generation data is currently not supported with the lgbm model. " \
227+
"Ignoring live_generation input.")
228+
return predict_lightgbm(site, ts)
229+
163230
else:
164-
raise ValueError(f"Unsupported model: {model}. Choose between 'xgb' and 'gb'")
231+
raise ValueError(f"Unsupported model: {model}. Choose between 'gb', 'xgb', or 'lgbm'")

quartz_solar_forecast/forecasts/__init__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,20 @@
55
Different models are put in different files like:
66
- v1.py contains the v1 model
77
- v2.py contains the v2 model which was developed by Tryolabs
8+
- v3_lightgbm.py contains the v3 LightGBM model with enhanced features
89
"""
910

1011
from .v1 import forecast_v1
1112
from .v1_tilt_orientation import forecast_v1_tilt_orientation
1213
from .v2 import TryolabsSolarPowerPredictor
14+
from .v3_lightgbm import LightGBMSolarPredictor
15+
from .feature_engineering import FeatureEngineer
16+
17+
__all__ = [
18+
"forecast_v1",
19+
"forecast_v1_tilt_orientation",
20+
"TryolabsSolarPowerPredictor",
21+
"LightGBMSolarPredictor",
22+
"FeatureEngineer",
23+
]
1324

14-
__all__ = ["forecast_v1", "forecast_v1_tilt_orientation", "TryolabsSolarPowerPredictor"]

0 commit comments

Comments
 (0)