Skip to content

Commit

Permalink
optimization: add MultiSeedMixin
Browse files Browse the repository at this point in the history
Add a mixin class to enable a workflow
for solving an optimization problem
by trying multiple seeds.
  • Loading branch information
SGeeversAtVortech committed Dec 6, 2024
1 parent a3b60de commit efb2452
Show file tree
Hide file tree
Showing 12 changed files with 399 additions and 0 deletions.
1 change: 1 addition & 0 deletions doc/optimization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Contents:
optimization/modelica_models
optimization/csv_io
optimization/fews_io
optimization/multi_seeding
optimization/linearization
optimization/lookup_tables
optimization/homotopy
Expand Down
10 changes: 10 additions & 0 deletions doc/optimization/multi_seeding.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Using multiple seeds
====================

A seed can be defined by implementing the :py:meth:`OptimizaionProblem.seed` method.
To implement a workflow for solving an optimization problem by trying multiple seeds,
one can use the :py:class:`MultiSeedMixin` class.

.. autoclass:: rtctools.optimization.multi_seed_mixin.MultiSeedMixin
:members: use_seed_id, selected_seed_id, seed_ids, seed_from_id
:show-inheritance:
79 changes: 79 additions & 0 deletions src/rtctools/optimization/multi_seed_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from typing import Any, Dict, Iterable

from rtctools.optimization.optimization_problem import OptimizationProblem
from rtctools.optimization.timeseries import Timeseries


class MultiSeedMixin(OptimizationProblem):
"""
Enables a workflow to solve an optimization problem by trying multiple seeds.
"""

def __init__(self, **kwargs):
self.__selected_seed_id = None
super().__init__(**kwargs)

def use_seed_id(self) -> bool:
"""Return ``True`` if the selected seed id is used.
Return ``True`` if the seed corresponding to the selected seed identifier is used
for the current run.
By default, this is ``True``.
Overwrite this method when in some cases the seed is not based on the seed id,
e.g. in case of goal programming, return False after the first priority,
or in case of homotopy, return ``False`` after the homotopy parameter is increased.
"""
return True

def selected_seed_id(self) -> Any:
"""Get the selected seed identifier.
If the selected seed id is None, use the default seed.
"""
return self.__selected_seed_id

def seed_ids(self) -> Iterable:
"""Return a list or iterator of seed identifiers.
When the id is None, the default seed will be used.
This method should be implemented by the user.
"""
raise NotImplementedError("This method should be implemented by the user.")

def seed_from_id(self, seed_id) -> Dict[str, Timeseries]:
"""
Get the seed timeseries from the selected identifier.
This method should be implemented by the user.
"""
del seed_id
raise NotImplementedError("This method should be implemented by the user.")

def seed(self, ensemble_member):
if self.selected_seed_id() is None or not self.use_seed_id():
return super().seed(ensemble_member)
seed: dict = super().seed(ensemble_member).copy() # Copy to prevent updating cached seeds.
seed_from_id = self.seed_from_id(self.selected_seed_id())
seed.update(seed_from_id)
return seed

def optimize(
self,
preprocessing: bool = True,
postprocessing: bool = True,
log_solver_failure_as_error: bool = True,
) -> bool:
if preprocessing:
self.pre()
for seed_id in self.seed_ids():
self.__selected_seed_id = seed_id
success = super().optimize(
preprocessing=False,
postprocessing=False,
log_solver_failure_as_error=log_solver_failure_as_error,
)
if success:
break
if postprocessing:
self.post()
return success
1 change: 1 addition & 0 deletions tests/optimization/data/reservoir/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
timeseries_export.*
2 changes: 2 additions & 0 deletions tests/optimization/data/reservoir/initial_state.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
volume
15.0
9 changes: 9 additions & 0 deletions tests/optimization/data/reservoir/reservoir.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
model Reservoir
// Basic model for in/outlow of a reservoir
parameter Real theta;
input Real q_in(fixed=true);
input Real q_out(fixed=false, min=0.0, max=5.0);
output Real volume;
equation
der(volume) = q_in - (2 - theta) * q_out;
end Reservoir;
15 changes: 15 additions & 0 deletions tests/optimization/data/reservoir/rtcDataConfig.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<rtcDataConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rtc="http://www.wldelft.nl/fews" xmlns="http://www.wldelft.nl/fews" xsi:schemaLocation="http://www.wldelft.nl/fews ../../../xsd/rtcDataConfig.xsd">
<timeSeries id="q_out">
<PITimeSeries>
<locationId>Seeds</locationId>
<parameterId>Q</parameterId>
</PITimeSeries>
</timeSeries>
<timeSeries id="q_in">
<PITimeSeries>
<locationId>Inputs</locationId>
<parameterId>Q</parameterId>
</PITimeSeries>
</timeSeries>
</rtcDataConfig>
6 changes: 6 additions & 0 deletions tests/optimization/data/reservoir/seed.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Time,q_out
2020-01-01 00:00:00,
2020-01-01 00:00:01,1.0
2020-01-01 00:00:02,
2020-01-01 00:00:03,3.0
2020-01-01 00:00:04,
21 changes: 21 additions & 0 deletions tests/optimization/data/reservoir/seed.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<ns0:TimeSeries xmlns:ns0="http://www.wldelft.nl/fews/PI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="http://www.wldelft.nl/fews/PI ../../xsd/pi_timeseries.xsd">
<ns0:timeZone>0.0</ns0:timeZone>
<ns0:series>
<ns0:header>
<ns0:type>instantaneous</ns0:type>
<ns0:locationId>Seeds</ns0:locationId>
<ns0:parameterId>Q</ns0:parameterId>
<ns0:timeStep multiplier="1" unit="second" />
<ns0:startDate date="2020-01-01" time="00:00:00" />
<ns0:forecastDate date="2020-01-01" time="00:00:00" />
<ns0:endDate date="2020-01-01" time="00:00:04" />
<ns0:missVal>-999.0</ns0:missVal>
<ns0:units>m3/s</ns0:units>
</ns0:header>
<ns0:event date="2020-01-01" flag="0" time="00:00:00" value="-999.0" />
<ns0:event date="2020-01-01" flag="0" time="00:00:01" value="1.0" />
<ns0:event date="2020-01-01" flag="0" time="00:00:02" value="-999.0" />
<ns0:event date="2020-01-01" flag="0" time="00:00:03" value="3.0" />
<ns0:event date="2020-01-01" flag="0" time="00:00:04" value="-999.0" />
</ns0:series>
</ns0:TimeSeries>
6 changes: 6 additions & 0 deletions tests/optimization/data/reservoir/timeseries_import.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Time,q_in
2020-01-01 00:00:00,0
2020-01-01 00:00:01,1.0
2020-01-01 00:00:02,2.0
2020-01-01 00:00:03,3.0
2020-01-01 00:00:04,4.0
21 changes: 21 additions & 0 deletions tests/optimization/data/reservoir/timeseries_import.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<ns0:TimeSeries xmlns:ns0="http://www.wldelft.nl/fews/PI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="http://www.wldelft.nl/fews/PI ../../xsd/pi_timeseries.xsd">
<ns0:timeZone>0.0</ns0:timeZone>
<ns0:series>
<ns0:header>
<ns0:type>instantaneous</ns0:type>
<ns0:locationId>Inputs</ns0:locationId>
<ns0:parameterId>Q</ns0:parameterId>
<ns0:timeStep multiplier="1" unit="second" />
<ns0:startDate date="2020-01-01" time="00:00:00" />
<ns0:forecastDate date="2020-01-01" time="00:00:00" />
<ns0:endDate date="2020-01-01" time="00:00:04" />
<ns0:missVal>-999.0</ns0:missVal>
<ns0:units>m3/s</ns0:units>
</ns0:header>
<ns0:event date="2020-01-01" flag="0" time="00:00:00" value="0.0" />
<ns0:event date="2020-01-01" flag="0" time="00:00:01" value="1.0" />
<ns0:event date="2020-01-01" flag="0" time="00:00:02" value="2.0" />
<ns0:event date="2020-01-01" flag="0" time="00:00:03" value="3.0" />
<ns0:event date="2020-01-01" flag="0" time="00:00:04" value="4.0" />
</ns0:series>
</ns0:TimeSeries>
Loading

0 comments on commit efb2452

Please sign in to comment.