Skip to content

Commit

Permalink
Remove uncertainty from get_forecast and load params within forecaster
Browse files Browse the repository at this point in the history
  • Loading branch information
dhblum committed Dec 11, 2024
1 parent 01ab685 commit 59113fa
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 153 deletions.
89 changes: 60 additions & 29 deletions forecast/forecaster.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
This module contains the Forecaster class with methods to obtain
forecast data for the test case. It relies on the data_manager object
of the test case to provide a deterministic forecast, and the
of the test case to provide a deterministic forecast, and the
error_emulator module to generate errors for uncertain forecasts.
'''
from .error_emulator import predict_temperature_error_AR1, predict_solar_error_AR1, mean_filter
import numpy as np
import json


class Forecaster(object):
Expand All @@ -33,6 +34,8 @@ def __init__(self, testcase):

# Point to the test case object
self.case = testcase
# Load forecast uncertainty parameters
self.uncertainty_params = self.load_uncertainty_params()

def get_forecast(self, point_names, horizon=24 * 3600, interval=3600,
wea_tem_dry_bul=None, wea_sol_glo_hor=None, seed=None):
Expand All @@ -47,16 +50,18 @@ def get_forecast(self, point_names, horizon=24 * 3600, interval=3600,
Forecast horizon in seconds (default is 86400 seconds, i.e., one day).
interval : int, optional
Time interval between forecast points in seconds (default is 3600 seconds, i.e., one hour).
wea_tem_dry_bul : dict, optional
Parameters for the AR1 model to simulate forecast error in dry bulb temperature:
- F0, K0, F, K, mu : parameters used in the AR1 model.
If None, defaults to a dictionary with all parameters set to zero, simulating no forecast error.
wea_tem_dry_bul : str, optional
Uncertainty level for outside air dry bulb temperature. 'low', 'medium', or 'high'
If None, defaults to no forecast error.
Default is None.
wea_sol_glo_hor : dict, optional
Parameters for the AR1 model to simulate forecast error in global horizontal solar irradiation:
- ag0, bg0, phi, ag, bg : parameters used in the AR1 model.
If None, defaults to a dictionary with all parameters set to zero, simulating no forecast error.
Uncertainty level for outside solar radiation. 'low', 'medium', or 'high'
If None, defaults to no forecast error.
Default is None.
seed : int, optional
Seed for the random number generator to ensure reproducibility of the stochastic forecast error.
If None, no seed is used.
Default is None.
Returns
-------
Expand All @@ -66,40 +71,45 @@ def get_forecast(self, point_names, horizon=24 * 3600, interval=3600,
'''

# Set uncertainty parameters to 0 if no forecast uncertainty
if wea_tem_dry_bul is None:
wea_tem_dry_bul = {
"F0": 0, "K0": 0, "F": 0, "K": 0, "mu": 0
}

if wea_sol_glo_hor is None:
wea_sol_glo_hor = {
"ag0": 0, "bg0": 0, "phi": 0, "ag": 0, "bg": 0
}
temperature_params = {
"F0": 0, "K0": 0, "F": 0, "K": 0, "mu": 0
}

solar_params = {
"ag0": 0, "bg0": 0, "phi": 0, "ag": 0, "bg": 0
}

if wea_tem_dry_bul is not None:
temperature_params.update(self.uncertainty_params['temperature'][wea_tem_dry_bul])

if wea_sol_glo_hor is not None:
solar_params.update(self.uncertainty_params['solar'][wea_sol_glo_hor])

# Get the forecast
forecast = self.case.data_manager.get_data(variables=point_names,
horizon=horizon,
interval=interval)

# Add any outside dry bulb temperature error
if 'TDryBul' in point_names and any(wea_tem_dry_bul.values()):
if 'TDryBul' in point_names and any(temperature_params.values()):
if seed is not None:
np.random.seed(seed)
# error in the forecast
error_forecast_temp = predict_temperature_error_AR1(
hp=int(horizon / interval + 1),
F0=wea_tem_dry_bul["F0"],
K0=wea_tem_dry_bul["K0"],
F=wea_tem_dry_bul["F"],
K=wea_tem_dry_bul["K"],
mu=wea_tem_dry_bul["mu"]
F0=temperature_params["F0"],
K0=temperature_params["K0"],
F=temperature_params["F"],
K=temperature_params["K"],
mu=temperature_params["mu"]
)

# forecast error just added to dry bulb temperature
forecast['TDryBul'] = forecast['TDryBul'] - error_forecast_temp
forecast['TDryBul'] = forecast['TDryBul'].tolist()

# Add any outside global horizontal irradiation error
if 'HGloHor' in point_names and any(wea_sol_glo_hor.values()):
if 'HGloHor' in point_names and any(solar_params.values()):

original_HGloHor = np.array(forecast['HGloHor']).copy()
lower_bound = 0.2 * original_HGloHor
Expand All @@ -111,11 +121,11 @@ def get_forecast(self, point_names, horizon=24 * 3600, interval=3600,
np.random.seed(seed+i*i)
error_forecast_solar = predict_solar_error_AR1(
int(horizon / interval + 1),
wea_sol_glo_hor["ag0"],
wea_sol_glo_hor["bg0"],
wea_sol_glo_hor["phi"],
wea_sol_glo_hor["ag"],
wea_sol_glo_hor["bg"]
solar_params["ag0"],
solar_params["bg0"],
solar_params["phi"],
solar_params["ag"],
solar_params["bg"]
)

forecast['HGloHor'] = original_HGloHor - error_forecast_solar
Expand All @@ -131,3 +141,24 @@ def get_forecast(self, point_names, horizon=24 * 3600, interval=3600,
break

return forecast

def load_uncertainty_params(self, filepath='forecast/forecast_uncertainty_params.json'):
'''Load the uncertainty parameters from a JSON file.
Parameters
----------
filepath : str, optional
Path to the JSON file containing the uncertainty parameters.
Default is 'forecast_uncertainty_params.json'.
Returns
-------
dict
Uncertainty parameters loaded from the JSON file.
'''

with open(filepath, 'r') as f:
uncertainty_params = json.load(f)

return uncertainty_params
10 changes: 1 addition & 9 deletions restapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,6 @@ def handle_validation_error(self, error, bundle_errors):
# Add required parameters
parser_forecast_points.add_argument('horizon', required=True)
parser_forecast_points.add_argument('interval', required=True)
# Add optional uncertainty parameters
parser_forecast_points.add_argument('temperature_uncertainty', required=False)
parser_forecast_points.add_argument('solar_uncertainty', required=False)
# ``results`` interface
results_var = reqparse.RequestParser(argument_class=CustomArgument)
results_var.add_argument('point_names', type=list, action='append', required=True)
Expand Down Expand Up @@ -206,14 +203,10 @@ def put(self):
args = parser_forecast_points.parse_args()
horizon = args['horizon']
interval = args['interval']
temperature_uncertainty = args.get('temperature_uncertainty', None)
solar_uncertainty = args.get('solar_uncertainty', None)
point_names = []
for point_name in args['point_names']:
point_names.append(''.join(point_name))
status, message, payload = case.get_forecast(point_names, horizon, interval,
temperature_uncertainty,
solar_uncertainty)
status, message, payload = case.get_forecast(point_names, horizon, interval)

return construct(status, message, payload)

Expand Down Expand Up @@ -287,4 +280,3 @@ def post(self):

if __name__ == '__main__':
app.run(host='0.0.0.0')

Loading

0 comments on commit 59113fa

Please sign in to comment.