-
Notifications
You must be signed in to change notification settings - Fork 3
42 add datadriven mpc #71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 30 commits
33e5296
cfa6269
c511e39
5294934
468ff65
8240253
43a6538
b07d8a3
be5fcf2
1097e96
7fd7811
cc3004e
ca461e1
1766125
87f0d25
69b9be8
e9c4d97
da6e785
6d4a1b4
39b7fb5
16220f6
3f8c377
099df9f
6ea896c
6f8f0dc
9d92f57
758861b
c259f92
647f780
733c708
bbacf73
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -134,6 +134,10 @@ class FlexibilityKPIs(pydantic.BaseModel): | |
| default=KPISeries(name="power_flex_offer", unit="kW", integration_method=LINEAR), | ||
| description="Power flexibility", | ||
| ) | ||
| power_flex_offer_prepared: KPISeries = pydantic.Field( | ||
| default=KPISeries(name="power_flex_offer_prepared", unit="kW", integration_method=LINEAR), | ||
| description="Power flexibility Series prepared for integration", | ||
| ) | ||
| power_flex_offer_max: KPI = pydantic.Field( | ||
| default=KPI(name="power_flex_offer_max", unit="kW"), | ||
| description="Maximum power flexibility", | ||
|
|
@@ -201,7 +205,7 @@ def calculate( | |
| enable_energy_costs_correction: bool, | ||
| calculate_flex_cost: bool, | ||
| integration_method: INTEGRATION_METHOD, | ||
| collocation_time_grid: list = None, | ||
| time_grid_info: dict = None, | ||
| ): | ||
| """Calculate the KPIs based on the power and electricity price input profiles. | ||
|
|
||
|
|
@@ -217,7 +221,8 @@ def calculate( | |
| enable_energy_costs_correction: whether the energy costs should be corrected | ||
| calculate_flex_cost: whether the cost of the flexibility should be calculated | ||
| integration_method: method used for integration of KPISeries e.g. linear, constant | ||
| collocation_time_grid: Time grid of the mpc output with collocation discretization | ||
| time_grid_info: Dictionary with 'type' ('collocation', 'multiple_shooting', 'none') | ||
| and 'grid' (list of time points) keys | ||
|
|
||
|
|
||
| """ | ||
|
|
@@ -229,10 +234,10 @@ def calculate( | |
| integration_method=integration_method, | ||
| ) | ||
| self._calculate_power_flex_stats( | ||
| mpc_time_grid=mpc_time_grid, collocation_time_grid=collocation_time_grid | ||
| mpc_time_grid=mpc_time_grid, time_grid_info=time_grid_info | ||
| ) | ||
| self._calculate_energy_flex( | ||
| mpc_time_grid=mpc_time_grid, collocation_time_grid=collocation_time_grid | ||
| mpc_time_grid=mpc_time_grid, time_grid_info=time_grid_info | ||
| ) | ||
|
|
||
| # Costs KPIs | ||
|
|
@@ -250,7 +255,7 @@ def calculate( | |
| stored_energy_diff=stored_energy_diff, | ||
| integration_method=integration_method, | ||
| mpc_time_grid=mpc_time_grid, | ||
| collocation_time_grid=collocation_time_grid, | ||
| time_grid_info=time_grid_info, | ||
| ) | ||
| self._calculate_costs_rel() | ||
|
|
||
|
|
@@ -292,10 +297,6 @@ def _calculate_power_flex( | |
| # Set values to zero if the difference is small | ||
| relative_difference = (power_flex / power_profile_base).abs() | ||
| power_flex.loc[relative_difference < relative_error_acceptance] = 0 | ||
| # Set the first value of power_flex to zero, since it comes from the measurement/simulator | ||
| # and is the same for baseline and shadow mpcs. | ||
| # For quantification of flexibility, only power difference is of interest. | ||
| power_flex.iloc[0] = 0 | ||
|
|
||
| # Set values | ||
| self.power_flex_full.value = power_flex | ||
|
|
@@ -308,28 +309,38 @@ def _calculate_power_flex( | |
| self.power_flex_offer.integration_method = integration_method | ||
|
|
||
| def _calculate_power_flex_stats( | ||
| self, mpc_time_grid: np.array, collocation_time_grid: list = None | ||
| self, mpc_time_grid: np.array, time_grid_info: dict = None | ||
| ): | ||
| """Calculate the characteristic values of the power flexibility for the offer.""" | ||
| """Calculate the characteristic values of the power flexibility for the offer. | ||
|
|
||
| Args: | ||
| mpc_time_grid: the MPC time grid over the horizon | ||
| time_grid_info: Dictionary with 'type' and 'grid' keys for discretization info | ||
|
Comment on lines
+325
to
+326
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the difference between the mpc_time_grid and timegrid_info["grid"]? Maybe add a short description for that |
||
| """ | ||
| if self.power_flex_offer.value is None: | ||
| raise ValueError("Power flexibility value is empty.") | ||
|
|
||
| # Calculate characteristic values | ||
| # max and min of power flex offer | ||
| power_flex_offer = self.power_flex_offer.value.iloc[:-1].drop( | ||
| collocation_time_grid, errors="ignore" | ||
| ) | ||
|
|
||
| self.power_flex_offer_prepared = self.power_flex_offer.__deepcopy__() | ||
|
|
||
| # Only drop collocation points if using collocation method | ||
| if time_grid_info and time_grid_info.get("type") == "collocation": | ||
| self.power_flex_offer_prepared.value = self.power_flex_offer_prepared.value.drop( | ||
| time_grid_info["grid"], errors="ignore" | ||
| ) | ||
| power_flex_offer = self.power_flex_offer_prepared.value.iloc[:-1] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is due to AgentLib-MPCs cost function, right? Can you add a short comment for that? |
||
|
|
||
| power_flex_offer_max = power_flex_offer.max() | ||
| power_flex_offer_min = power_flex_offer.min() | ||
|
|
||
| # Average of the power flex offer | ||
| # Get the series for integration before calculating average | ||
| power_flex_offer_integration = self._get_series_for_integration( | ||
| series=self.power_flex_offer, mpc_time_grid=mpc_time_grid | ||
| ) | ||
| power_flex_offer_integration.value = power_flex_offer_integration.value.drop( | ||
| collocation_time_grid, errors="ignore" | ||
| series=self.power_flex_offer_prepared, mpc_time_grid=mpc_time_grid | ||
| ) | ||
| # Calculate the average and stores the original value | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why remove that? |
||
|
|
||
| power_flex_offer_avg = power_flex_offer_integration.avg() | ||
|
|
||
| # Set values | ||
|
|
@@ -338,7 +349,7 @@ def _calculate_power_flex_stats( | |
| self.power_flex_offer_avg.value = power_flex_offer_avg | ||
|
|
||
| def _get_series_for_integration( | ||
| self, series: KPISeries, mpc_time_grid: np.ndarray | ||
| self, series: Union[pd.Series, KPISeries], mpc_time_grid: np.ndarray | ||
| ) -> KPISeries: | ||
| """Return the KPISeries value sampled on the MPC time grid when the integration | ||
| method is constant. | ||
|
|
@@ -357,20 +368,23 @@ def _get_series_for_integration( | |
| else: | ||
| return series.__deepcopy__() | ||
|
|
||
| def _calculate_energy_flex(self, mpc_time_grid, collocation_time_grid: list = None): | ||
| def _calculate_energy_flex(self, mpc_time_grid, time_grid_info: dict = None): | ||
| """Calculate the energy flexibility by integrating the power flexibility | ||
| of the offer window.""" | ||
| of the offer window. | ||
|
|
||
| Args: | ||
| mpc_time_grid: the MPC time grid over the horizon | ||
| time_grid_info: Dictionary with 'type' and 'grid' keys for discretization info | ||
| """ | ||
| if self.power_flex_offer.value is None: | ||
| raise ValueError("Power flexibility value of the offer is empty.") | ||
|
|
||
| # Calculate flexibility | ||
| # Get the series for integration before calculating average | ||
| power_flex_offer_integration = self._get_series_for_integration( | ||
| series=self.power_flex_offer, mpc_time_grid=mpc_time_grid | ||
| ) | ||
| power_flex_offer_integration.value = power_flex_offer_integration.value.drop( | ||
| collocation_time_grid, errors="ignore" | ||
| series=self.power_flex_offer_prepared, mpc_time_grid=mpc_time_grid | ||
| ) | ||
|
|
||
| # Calculate the energy flex and stores the original value | ||
| energy_flex = power_flex_offer_integration.integrate(time_unit="hours") | ||
|
|
||
|
|
@@ -386,7 +400,7 @@ def _calculate_costs( | |
| stored_energy_diff: float, | ||
| integration_method: INTEGRATION_METHOD, | ||
| mpc_time_grid: np.ndarray, | ||
| collocation_time_grid: list = None, | ||
| time_grid_info: dict = None, | ||
| ): | ||
| """Calculate the costs of the flexibility event based on the electricity costs profile, | ||
| the power flexibility profile and difference of stored energy. | ||
|
|
@@ -399,7 +413,7 @@ def _calculate_costs( | |
| stored_energy_diff: the difference of the stored energy between baseline and shadow mpc | ||
| integration_method: the integration method used to integrate KPISeries | ||
| mpc_time_grid: the MPC time grid over the horizon | ||
| collocation_time_grid: Time grid of the mpc output with collocation discretization | ||
| time_grid_info: Dictionary with 'type' and 'grid' keys for discretization info | ||
|
|
||
|
|
||
| """ | ||
|
|
@@ -435,9 +449,10 @@ def _calculate_costs( | |
| power_flex_full_integration = self._get_series_for_integration( | ||
| series=self.power_flex_full, mpc_time_grid=mpc_time_grid | ||
| ) | ||
| power_flex_full_integration.value = power_flex_full_integration.value.drop( | ||
| collocation_time_grid, errors="ignore" | ||
| ) | ||
| if time_grid_info and time_grid_info.get("type") == "collocation": | ||
| power_flex_full_integration.value = power_flex_full_integration.value.drop( | ||
| time_grid_info["grid"], errors="ignore" | ||
| ) | ||
|
|
||
| # Difference in costs between shadow and baseline mpc | ||
| delta_cost = cost_profile_shadow - cost_profile_base | ||
|
|
@@ -447,7 +462,6 @@ def _calculate_costs( | |
|
|
||
| # Calculate the costs and stores the original value | ||
| costs = self.electricity_costs_series.integrate(time_unit="hours") | ||
|
|
||
| # correct the costs | ||
| corrected_costs = costs - stored_energy_diff * np.mean(electricity_price_signal) | ||
|
|
||
|
|
@@ -640,15 +654,16 @@ def calculate( | |
| enable_energy_costs_correction: bool, | ||
| calculate_flex_cost: bool, | ||
| integration_method: INTEGRATION_METHOD, | ||
| collocation_time_grid: list = None, | ||
| time_grid_info: dict = None, | ||
| ): | ||
| """Calculate the KPIs for the positive and negative flexibility. | ||
|
|
||
| Args: | ||
| enable_energy_costs_correction: whether the energy costs should be corrected | ||
| calculate_flex_cost: whether the cost of the flexibility should be calculated | ||
| integration_method: method used for integration of KPISeries e.g. linear, constant | ||
| collocation_time_grid: Time grid of the mpc output with collocation discretization | ||
| time_grid_info: Dictionary with 'type' ('collocation', 'multiple_shooting', 'none') | ||
| and 'grid' (list of time points) keys | ||
|
|
||
| """ | ||
| self.kpis_pos.calculate( | ||
|
|
@@ -663,7 +678,7 @@ def calculate( | |
| enable_energy_costs_correction=enable_energy_costs_correction, | ||
| calculate_flex_cost=calculate_flex_cost, | ||
| integration_method=integration_method, | ||
| collocation_time_grid=collocation_time_grid, | ||
| time_grid_info=time_grid_info, | ||
| ) | ||
| self.kpis_neg.calculate( | ||
| power_profile_base=self.power_profile_base, | ||
|
|
@@ -677,7 +692,7 @@ def calculate( | |
| enable_energy_costs_correction=enable_energy_costs_correction, | ||
| calculate_flex_cost=calculate_flex_cost, | ||
| integration_method=integration_method, | ||
| collocation_time_grid=collocation_time_grid, | ||
| time_grid_info=time_grid_info, | ||
| ) | ||
| self.reset_time_grid() | ||
| return self.kpis_pos, self.kpis_neg | ||
|
|
@@ -701,5 +716,3 @@ def update_profile(self, name: str, value: pd.Series, mpc:bool) -> None: | |
| if value is not None: | ||
| value = self.unify_inputs(series=value, mpc= mpc) | ||
| setattr(self, name, value) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe
power_flex_offer_prep_int?