Skip to content
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
33e5296
introduce self.time as casadi symbolic
sarahleidolf Feb 5, 2025
cfa6269
add at Example OneRoom_SimpleMPC
sarahleidolf Feb 13, 2025
c511e39
adjust parsing and data handling for ml mpc
sarahleidolf Feb 17, 2025
5294934
add example
sarahleidolf Feb 17, 2025
468ff65
Merge branch 'main' into 42-add-datadriven-mpc
sarahleidolf Apr 14, 2025
8240253
change requirements
sarahleidolf May 22, 2025
43a6538
change self.time
sarahleidolf May 22, 2025
b07d8a3
first approach for new objective handling
sarahleidolf May 23, 2025
be5fcf2
Merge branch
sarahleidolf May 27, 2025
1097e96
Revert "first approach for new objective handling"
sarahleidolf Jul 2, 2025
7fd7811
Revert "change self.time"
sarahleidolf Jul 2, 2025
cc3004e
Revert "Revert "change self.time""
sarahleidolf Jul 2, 2025
ca461e1
add set_actuation
sarahleidolf Jul 14, 2025
1766125
update requirements
sarahleidolf Jan 21, 2026
87f0d25
main into #42
sarahleidolf Jan 21, 2026
69b9be8
update globals
sarahleidolf Jan 21, 2026
e9c4d97
minor updates after merge from main branch
sarahleidolf Jan 21, 2026
da6e785
allow multiple shooting
sarahleidolf Feb 10, 2026
6d4a1b4
minor import changes
sarahleidolf Feb 24, 2026
39b7fb5
Merge branch 'main' into 42-add-datadriven-mpc
sarahleidolf Feb 24, 2026
16220f6
new numpy version
sarahleidolf Feb 24, 2026
3f8c377
move example
sarahleidolf Feb 24, 2026
099df9f
move example
sarahleidolf Feb 24, 2026
6ea896c
adjust snapshots of ci tests for new time grid formulation
sarahleidolf Feb 25, 2026
6f8f0dc
adjust ci workflow
sarahleidolf Feb 25, 2026
9d92f57
change r_del_u handling
sarahleidolf Feb 25, 2026
758861b
include review
sarahleidolf Mar 26, 2026
c259f92
Merge branch 'main' into 42-add-datadriven-mpc_
sarahleidolf Mar 27, 2026
647f780
update examples and tests
sarahleidolf Mar 27, 2026
733c708
Update snapshots after assumption "Set the first value of power_flex…
sarahleidolf Apr 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ jobs:
CREATE_PAGES_ON_FAILURE: true
EXECUTE_TESTS: true
EXECUTE_COVERAGE_TEST: true
EXTRA_REQUIREMENTS: '["ml"]'
85 changes: 57 additions & 28 deletions agentlib_flexquant/data_structures/flex_kpis.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,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.

Expand All @@ -215,7 +215,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


"""
Expand All @@ -227,10 +228,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
Expand All @@ -245,7 +246,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()

Expand Down Expand Up @@ -303,27 +304,42 @@ 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
"""
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"
)
power_flex_offer = self.power_flex_offer.value.iloc[:-1]

# Only drop collocation points if using collocation method
if time_grid_info and time_grid_info.get("type") == "collocation":
power_flex_offer = power_flex_offer.drop(
time_grid_info["grid"], errors="ignore"
)

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"
)

# Only drop collocation points if using collocation method
if time_grid_info and time_grid_info.get("type") == "collocation":
power_flex_offer_integration.value = power_flex_offer_integration.value.drop(
time_grid_info["grid"], errors="ignore"
)

Comment thread
sarahleidolf marked this conversation as resolved.
Outdated
# Calculate the average and stores the original value
power_flex_offer_avg = power_flex_offer_integration.avg()

Expand Down Expand Up @@ -352,9 +368,14 @@ 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.")

Expand All @@ -363,9 +384,13 @@ def _calculate_energy_flex(self, mpc_time_grid, collocation_time_grid: list = No
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"
)

# Only drop collocation points if using collocation method
if time_grid_info and time_grid_info.get("type") == "collocation":
power_flex_offer_integration.value = power_flex_offer_integration.value.drop(
time_grid_info["grid"], errors="ignore"
)

# Calculate the energy flex and stores the original value
energy_flex = power_flex_offer_integration.integrate(time_unit="hours")

Expand All @@ -378,7 +403,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.
Expand All @@ -388,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


"""
Expand All @@ -405,9 +430,12 @@ 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"
)

# Only drop collocation points if using collocation method
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"
)

# Calculate series
self.electricity_costs_series.value = (
Expand Down Expand Up @@ -605,15 +633,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(
Expand All @@ -627,7 +656,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,
Expand All @@ -640,7 +669,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
Expand All @@ -657,4 +686,4 @@ def reset_time_grid(self):
Reset the common time grid.
This should be called between different flexibility calculations.
"""
self._common_time_grid = None
self._common_time_grid = None
6 changes: 3 additions & 3 deletions agentlib_flexquant/data_structures/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
CONSTANT = 'constant'
COLLOCATION = 'collocation'
INTEGRATION_METHOD = Literal[LINEAR, CONSTANT]

FlexibilityDirections = Literal["positive", "negative"]

POWER_ALIAS_BASE = "_P_el_base"
POWER_ALIAS_NEG = "_P_el_neg"
POWER_ALIAS_POS = "_P_el_pos"
Expand All @@ -25,7 +27,7 @@
full_trajectory_suffix: str = "_full"
base_vars_to_communicate_suffix: str = "_base"
shadow_suffix: str = "_shadow"
COLLOCATION_TIME_GRID = 'collocation_time_grid'
TIME_GRID_INFO = 'time_grid_info'
PROVISION_VAR_NAME = "in_provision"
ACCEPTED_POWER_VAR_NAME = "_P_external"
RELATIVE_EVENT_START_TIME_VAR_NAME = "rel_start"
Expand All @@ -43,11 +45,9 @@

def return_baseline_cost_function(power_variable: str, comfort_variable: str) -> str:
"""Return baseline cost function

Args:
power_variable: name of the power variable
comfort_variable: name of the comfort variable

Returns:
Cost function in the baseline mpc, obj_std is to be evaluated according to
user definition
Expand Down
Loading