Skip to content

Commit

Permalink
Backport PR #832: fix: infeasible problem due to float precision error (
Browse files Browse the repository at this point in the history
#832)

* limit equal_v to be in the [min_v, max_v] range

Signed-off-by: Victor Garcia Reolid <victor@seita.nl>

* fix: equal_v out of bound

Signed-off-by: Victor Garcia Reolid <victor@seita.nl>

* remove wrong changes

Signed-off-by: Victor Garcia Reolid <victor@seita.nl>

* add test

Signed-off-by: Victor Garcia Reolid <victor@seita.nl>

* revert (wrong) merge change

Signed-off-by: Victor Garcia Reolid <victor@seita.nl>

* test for CBC and HiGHS

Signed-off-by: Victor Garcia Reolid <victor@seita.nl>

* test: floating point case for minimum constraint

Signed-off-by: F.N. Claessen <felix@seita.nl>

* docs: changelog entry

Signed-off-by: F.N. Claessen <felix@seita.nl>

* docs: set patch release date

Signed-off-by: F.N. Claessen <felix@seita.nl>

---------

Signed-off-by: Victor Garcia Reolid <victor@seita.nl>
Signed-off-by: Victor <victor@seita.nl>
Signed-off-by: F.N. Claessen <felix@seita.nl>
Co-authored-by: F.N. Claessen <felix@seita.nl>
Signed-off-by: F.N. Claessen <felix@seita.nl>
  • Loading branch information
victorgarcia98 and Flix6x committed Aug 28, 2023
1 parent a305ef8 commit 65f9896
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 2 deletions.
3 changes: 2 additions & 1 deletion documentation/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
FlexMeasures Changelog
**********************

v0.15.1 | August XX, 2023
v0.15.1 | August 28, 2023
============================

Bugfixes
-----------

* Fix infeasible problem due to floating point error in :abbr:`SoC (state of charge)` targets [see `PR #832 <https://github.com/FlexMeasures/flexmeasures/pull/832>`_]
* Use the `source` to filter beliefs in the `AggregatorReporter` and fix the case of having multiple sources [see `PR #819 <https://github.com/FlexMeasures/flexmeasures/pull/819>`_]
* Disable HiGHS logs on the standard output when `LOGGING_LEVEL=INFO` [see `PR #824 <https://github.com/FlexMeasures/flexmeasures/pull/824>`_ and `PR #826 <https://github.com/FlexMeasures/flexmeasures/pull/826>`_]
* Fix showing sensor data on the asset page of public assets, and searching for annotations on public assets [see `PR #830 <https://github.com/FlexMeasures/flexmeasures/pull/830>`_]
Expand Down
10 changes: 10 additions & 0 deletions flexmeasures/data/models/planning/linear_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,19 +140,29 @@ def commitment_quantity_select(m, c, j):
return commitment_quantities[c].iloc[j]

def device_max_select(m, d, j):
min_v = device_constraints[d]["min"].iloc[j]
max_v = device_constraints[d]["max"].iloc[j]
equal_v = device_constraints[d]["equals"].iloc[j]
if np.isnan(max_v) and np.isnan(equal_v):
return infinity
else:
if not np.isnan(equal_v):
# make min_v < equal_v
equal_v = np.nanmax([equal_v, min_v])

return np.nanmin([max_v, equal_v])

def device_min_select(m, d, j):
min_v = device_constraints[d]["min"].iloc[j]
max_v = device_constraints[d]["max"].iloc[j]
equal_v = device_constraints[d]["equals"].iloc[j]
if np.isnan(min_v) and np.isnan(equal_v):
return -infinity
else:
if not np.isnan(equal_v):
# make equal_v <= max_v
equal_v = np.nanmin([equal_v, max_v])

return np.nanmax([min_v, equal_v])

def device_derivative_max_select(m, d, j):
Expand Down
74 changes: 74 additions & 0 deletions flexmeasures/data/models/planning/tests/test_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -990,3 +990,77 @@ def get_sensors_from_db(battery_assets):
assert battery.get_attribute("market_id") == epex_da.id

return epex_da, battery


@pytest.mark.parametrize("solver", ["appsi_highs", "cbc"])
def test_numerical_errors(app, setup_planning_test_data, solver):
"""Test that a soc-target = soc-max can exceed this value due to numerical errors in the operations
to compute the device constraint DataFrame.
In the case of HiGHS, the tiny difference creates an infeasible constraint.
"""

epex_da = Sensor.query.filter(Sensor.name == "epex_da").one_or_none()
charging_station = setup_planning_test_data[
"Test charging station (bidirectional)"
].sensors[0]
assert charging_station.get_attribute("capacity_in_mw") == 2
assert charging_station.get_attribute("market_id") == epex_da.id

tz = pytz.timezone("Europe/Amsterdam")
start = tz.localize(datetime(2015, 1, 2))
end = tz.localize(datetime(2015, 1, 3))
resolution = timedelta(minutes=5)

duration_until_next_target = timedelta(hours=1)
target_soc_datetime_1 = pd.Timestamp(start + duration_until_next_target).isoformat()
target_soc_datetime_2 = pd.Timestamp(
start + 2 * duration_until_next_target
).isoformat()

# select which solver to use
app.config["FLEXMEASURES_LP_SOLVER"] = solver

scheduler = StorageScheduler(
charging_station,
start,
end,
resolution,
flex_model={
"soc-at-start": 0.01456,
"soc-min": 0.01295,
"soc-max": 0.056,
"roundtrip-efficiency": 0.85,
"storage-efficiency": 1,
"soc-targets": [
{"value": 0.01295, "datetime": target_soc_datetime_1},
{"value": 0.056, "datetime": target_soc_datetime_2},
],
"soc-unit": "MWh",
},
)

(
sensor,
start,
end,
resolution,
soc_at_start,
device_constraints,
ems_constraints,
commitment_quantities,
commitment_downwards_deviation_price,
commitment_upwards_deviation_price,
) = scheduler._prepare(skip_validation=True)

_, _, results, model = device_scheduler(
device_constraints,
ems_constraints,
commitment_quantities,
commitment_downwards_deviation_price,
commitment_upwards_deviation_price,
initial_stock=soc_at_start * (timedelta(hours=1) / resolution),
)

assert device_constraints[0]["equals"].max() > device_constraints[0]["max"].max()
assert device_constraints[0]["equals"].min() < device_constraints[0]["min"].min()
assert results.solver.status == "ok"
1 change: 0 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ ignore = E501, W503, E203
addopts = --strict-markers
markers =
skip_github: skip test in GitHub Actions. Useful in case the test passes, but breaks the test suite on GH Actions.

0 comments on commit 65f9896

Please sign in to comment.