rtc-tools 2.6.0b2__tar.gz → 2.7.0a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rtc-tools might be problematic. Click here for more details.
- {rtc-tools-2.6.0b2/src/rtc_tools.egg-info → rtc-tools-2.7.0a1}/PKG-INFO +1 -1
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/setup.py +2 -1
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1/src/rtc_tools.egg-info}/PKG-INFO +1 -1
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtc_tools.egg-info/requires.txt +2 -1
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/_version.py +3 -3
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/data/pi.py +13 -16
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/data/rtc.py +3 -2
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/csv_lookup_table_mixin.py +10 -12
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/modelica_mixin.py +15 -3
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/pi_mixin.py +13 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/rtctoolsapp.py +2 -1
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/simulation/pi_mixin.py +13 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/simulation/simulation_problem.py +91 -15
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/COPYING.LESSER +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/MANIFEST.in +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/README.md +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/pyproject.toml +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/setup.cfg +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtc_tools.egg-info/SOURCES.txt +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtc_tools.egg-info/dependency_links.txt +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtc_tools.egg-info/entry_points.txt +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtc_tools.egg-info/top_level.txt +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/__init__.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/_internal/__init__.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/_internal/alias_tools.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/_internal/caching.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/_internal/casadi_helpers.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/_internal/debug_check_helpers.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/data/__init__.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/data/csv.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/data/interpolation/__init__.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/data/interpolation/bspline.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/data/interpolation/bspline1d.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/data/interpolation/bspline2d.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/data/netcdf.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/data/storage.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/__init__.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/collocated_integrated_optimization_problem.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/control_tree_mixin.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/csv_mixin.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/goal_programming_mixin.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/goal_programming_mixin_base.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/homotopy_mixin.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/initial_state_estimation_mixin.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/io_mixin.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/linearization_mixin.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/linearized_order_goal_programming_mixin.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/min_abs_goal_programming_mixin.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/netcdf_mixin.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/optimization_problem.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/planning_mixin.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/single_pass_goal_programming_mixin.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/timeseries.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/simulation/__init__.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/simulation/csv_mixin.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/simulation/io_mixin.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/util.py +0 -0
- {rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/versioneer.py +0 -0
|
@@ -47,8 +47,9 @@ setup(
|
|
|
47
47
|
"casadi >= 3.6.3, == 3.6.*",
|
|
48
48
|
"numpy >= 1.16.0, <1.26",
|
|
49
49
|
"scipy >= 1.0.0, <1.11",
|
|
50
|
-
"pymoca
|
|
50
|
+
"pymoca >= 0.9.1, == 0.9.*",
|
|
51
51
|
"rtc-tools-channel-flow >= 1.1.0",
|
|
52
|
+
"defusedxml >= 0.7.0",
|
|
52
53
|
],
|
|
53
54
|
tests_require=["pytest", "pytest-runner", "netCDF4"],
|
|
54
55
|
extras_require={
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2024-
|
|
11
|
+
"date": "2024-04-25T06:42:12+0000",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "2.
|
|
14
|
+
"full-revisionid": "e3d59506cabe8df7e05acd089ccf574e106a577f",
|
|
15
|
+
"version": "2.7.0a1"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -5,6 +5,7 @@ import logging
|
|
|
5
5
|
import os
|
|
6
6
|
import xml.etree.ElementTree as ET
|
|
7
7
|
|
|
8
|
+
import defusedxml.ElementTree as DefusedElementTree
|
|
8
9
|
import numpy as np
|
|
9
10
|
|
|
10
11
|
ns = {"fews": "http://www.wldelft.nl/fews", "pi": "http://www.wldelft.nl/fews/PI"}
|
|
@@ -30,7 +31,7 @@ class Diag:
|
|
|
30
31
|
"""
|
|
31
32
|
self.__path_xml = os.path.join(folder, basename + ".xml")
|
|
32
33
|
|
|
33
|
-
self.__tree =
|
|
34
|
+
self.__tree = DefusedElementTree.parse(self.__path_xml)
|
|
34
35
|
self.__xml_root = self.__tree.getroot()
|
|
35
36
|
|
|
36
37
|
def get(self, level=ERROR_FATAL):
|
|
@@ -87,7 +88,7 @@ class DiagHandler(logging.Handler):
|
|
|
87
88
|
self.__path_xml = os.path.join(folder, basename + ".xml")
|
|
88
89
|
|
|
89
90
|
try:
|
|
90
|
-
self.__tree =
|
|
91
|
+
self.__tree = DefusedElementTree.parse(self.__path_xml)
|
|
91
92
|
self.__xml_root = self.__tree.getroot()
|
|
92
93
|
except Exception:
|
|
93
94
|
self.__xml_root = ET.Element("{%s}Diag" % (ns["pi"],))
|
|
@@ -135,7 +136,7 @@ class ParameterConfig:
|
|
|
135
136
|
basename = basename + ".xml"
|
|
136
137
|
self.__path_xml = os.path.join(folder, basename)
|
|
137
138
|
|
|
138
|
-
self.__tree =
|
|
139
|
+
self.__tree = DefusedElementTree.parse(self.__path_xml)
|
|
139
140
|
self.__xml_root = self.__tree.getroot()
|
|
140
141
|
|
|
141
142
|
def get(self, group_id, parameter_id, location_id=None, model=None):
|
|
@@ -300,9 +301,8 @@ class ParameterConfig:
|
|
|
300
301
|
# get table contenstart_datetime
|
|
301
302
|
el_row = child.findall("pi:row", ns)
|
|
302
303
|
table = {
|
|
303
|
-
columnId[key]: np.empty(len(el_row), columnType[key])
|
|
304
|
-
|
|
305
|
-
}
|
|
304
|
+
columnId[key]: np.empty(len(el_row), columnType[key]) for key in columnId
|
|
305
|
+
} # initialize table
|
|
306
306
|
|
|
307
307
|
i_row = 0
|
|
308
308
|
for row in el_row:
|
|
@@ -378,7 +378,7 @@ class Timeseries:
|
|
|
378
378
|
if self.make_new_file:
|
|
379
379
|
self.__reset_xml_tree()
|
|
380
380
|
else:
|
|
381
|
-
self.__tree =
|
|
381
|
+
self.__tree = DefusedElementTree.parse(self.__path_xml)
|
|
382
382
|
self.__xml_root = self.__tree.getroot()
|
|
383
383
|
|
|
384
384
|
self.__values = [{}]
|
|
@@ -855,7 +855,7 @@ class Timeseries:
|
|
|
855
855
|
|
|
856
856
|
variable = self.__data_config.variable(header)
|
|
857
857
|
|
|
858
|
-
miss_val =
|
|
858
|
+
miss_val = header.find("pi:missVal", ns).text
|
|
859
859
|
values = self.__values[ensemble_member][variable]
|
|
860
860
|
|
|
861
861
|
# Update the header, which may have changed
|
|
@@ -868,11 +868,8 @@ class Timeseries:
|
|
|
868
868
|
self.__xml_root.remove(series)
|
|
869
869
|
continue
|
|
870
870
|
|
|
871
|
-
# Replace NaN with missing value
|
|
872
|
-
nans = np.isnan(values)
|
|
873
|
-
values[nans] = miss_val
|
|
874
|
-
|
|
875
871
|
# Write output
|
|
872
|
+
nans = np.isnan(values)
|
|
876
873
|
if self.__binary:
|
|
877
874
|
f.write(values.astype(self.__pi_dtype).tobytes())
|
|
878
875
|
else:
|
|
@@ -897,7 +894,10 @@ class Timeseries:
|
|
|
897
894
|
event = ET.Element("pi:event")
|
|
898
895
|
event.set("date", t.strftime("%Y-%m-%d"))
|
|
899
896
|
event.set("time", t.strftime("%H:%M:%S"))
|
|
900
|
-
|
|
897
|
+
if nans[i]:
|
|
898
|
+
event.set("value", miss_val)
|
|
899
|
+
else:
|
|
900
|
+
event.set("value", str(values[i]))
|
|
901
901
|
series.append(event)
|
|
902
902
|
if self.dt:
|
|
903
903
|
t += self.dt
|
|
@@ -907,9 +907,6 @@ class Timeseries:
|
|
|
907
907
|
for i in range(len(values), len(events)):
|
|
908
908
|
series.remove(events[i])
|
|
909
909
|
|
|
910
|
-
# Restore NaN
|
|
911
|
-
values[nans] = np.nan
|
|
912
|
-
|
|
913
910
|
if self.__binary:
|
|
914
911
|
f.close()
|
|
915
912
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
|
-
import xml.etree.ElementTree as ET
|
|
4
3
|
from collections import namedtuple
|
|
5
4
|
|
|
5
|
+
import defusedxml.ElementTree as DefusedElementTree
|
|
6
|
+
|
|
6
7
|
ts_ids = namedtuple("ids", "location_id parameter_id qualifier_id")
|
|
7
8
|
p_ids = namedtuple("ids", "model_id location_id parameter_id")
|
|
8
9
|
|
|
@@ -31,7 +32,7 @@ class DataConfig:
|
|
|
31
32
|
|
|
32
33
|
path = os.path.join(folder, "rtcDataConfig.xml")
|
|
33
34
|
try:
|
|
34
|
-
tree =
|
|
35
|
+
tree = DefusedElementTree.parse(path)
|
|
35
36
|
root = tree.getroot()
|
|
36
37
|
|
|
37
38
|
timeseriess1 = root.findall("./*/fews:timeSeries", ns)
|
|
@@ -2,7 +2,6 @@ import configparser
|
|
|
2
2
|
import glob
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
|
-
import pickle
|
|
6
5
|
from typing import Iterable, List, Tuple, Union
|
|
7
6
|
|
|
8
7
|
import casadi as ca
|
|
@@ -323,7 +322,7 @@ class CSVLookupTableMixin(OptimizationProblem):
|
|
|
323
322
|
|
|
324
323
|
# If tck file is newer than the csv file, first try to load the cached values from
|
|
325
324
|
# the tck file
|
|
326
|
-
tck_filename = filename.replace(".csv", ".
|
|
325
|
+
tck_filename = filename.replace(".csv", ".npz")
|
|
327
326
|
valid_cache = False
|
|
328
327
|
if os.path.exists(tck_filename):
|
|
329
328
|
if no_curvefit_options:
|
|
@@ -338,11 +337,13 @@ class CSVLookupTableMixin(OptimizationProblem):
|
|
|
338
337
|
output
|
|
339
338
|
)
|
|
340
339
|
)
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
tck
|
|
344
|
-
|
|
345
|
-
|
|
340
|
+
try:
|
|
341
|
+
with np.load(filename.replace(".csv", ".npz")) as data:
|
|
342
|
+
tck = (data["arr_0"], data["arr_1"], int(data["arr_2"]))
|
|
343
|
+
function = ca.Function.load(filename.replace(".csv", ".ca"))
|
|
344
|
+
except Exception:
|
|
345
|
+
valid_cache = False
|
|
346
|
+
|
|
346
347
|
if not valid_cache:
|
|
347
348
|
logger.info("CSVLookupTableMixin: Recalculating tck values for {}".format(output))
|
|
348
349
|
|
|
@@ -446,11 +447,8 @@ class CSVLookupTableMixin(OptimizationProblem):
|
|
|
446
447
|
)
|
|
447
448
|
|
|
448
449
|
if not valid_cache:
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
open(filename.replace(".csv", ".tck"), "wb"),
|
|
452
|
-
protocol=pickle.HIGHEST_PROTOCOL,
|
|
453
|
-
)
|
|
450
|
+
np.savez(filename.replace(".csv", ".npz"), *tck)
|
|
451
|
+
function.save(filename.replace(".csv", ".ca"))
|
|
454
452
|
|
|
455
453
|
def lookup_tables(self, ensemble_member):
|
|
456
454
|
# Call parent class first for default values.
|
|
@@ -48,9 +48,21 @@ class ModelicaMixin(OptimizationProblem):
|
|
|
48
48
|
else:
|
|
49
49
|
model_name = self.__class__.__name__
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
compiler_options = self.compiler_options()
|
|
52
|
+
logger.info(f"Loading/compiling model {model_name}.")
|
|
53
|
+
try:
|
|
54
|
+
self.__pymoca_model = pymoca.backends.casadi.api.transfer_model(
|
|
55
|
+
kwargs["model_folder"], model_name, compiler_options
|
|
56
|
+
)
|
|
57
|
+
except RuntimeError as error:
|
|
58
|
+
if compiler_options.get("cache", False):
|
|
59
|
+
raise error
|
|
60
|
+
compiler_options["cache"] = False
|
|
61
|
+
logger.warning(f"Loading model {model_name} using a cache file failed: {error}.")
|
|
62
|
+
logger.info(f"Compiling model {model_name}.")
|
|
63
|
+
self.__pymoca_model = pymoca.backends.casadi.api.transfer_model(
|
|
64
|
+
kwargs["model_folder"], model_name, compiler_options
|
|
65
|
+
)
|
|
54
66
|
|
|
55
67
|
# Extract the CasADi MX variables used in the model
|
|
56
68
|
self.__mx = {}
|
|
@@ -277,3 +277,16 @@ class PIMixin(IOMixin):
|
|
|
277
277
|
:class:`pi.Timeseries` object for holding the output data.
|
|
278
278
|
"""
|
|
279
279
|
return self.__timeseries_export
|
|
280
|
+
|
|
281
|
+
def set_unit(self, variable: str, unit: str):
|
|
282
|
+
"""
|
|
283
|
+
Set the unit of a time series.
|
|
284
|
+
|
|
285
|
+
:param variable: Time series ID.
|
|
286
|
+
:param unit: Unit.
|
|
287
|
+
"""
|
|
288
|
+
assert hasattr(
|
|
289
|
+
self, "_PIMixin__timeseries_import"
|
|
290
|
+
), "set_unit can only be called after read() in pre() has finished."
|
|
291
|
+
self.__timeseries_import.set_unit(variable, unit, 0)
|
|
292
|
+
self.__timeseries_export.set_unit(variable, unit, 0)
|
|
@@ -108,7 +108,8 @@ def download_examples(*args):
|
|
|
108
108
|
|
|
109
109
|
opener = urllib.request.build_opener()
|
|
110
110
|
urllib.request.install_opener(opener)
|
|
111
|
-
|
|
111
|
+
# The security warning can be dismissed as the url variable is hardcoded to a remote.
|
|
112
|
+
local_filename, _ = urllib.request.urlretrieve(url) # nosec
|
|
112
113
|
except HTTPError:
|
|
113
114
|
sys.exit("Could not found examples for RTC-Tools version {}.".format(version))
|
|
114
115
|
|
|
@@ -240,3 +240,16 @@ class PIMixin(IOMixin):
|
|
|
240
240
|
def get_timeseries(self, variable):
|
|
241
241
|
_, values = self.io.get_timeseries(variable)
|
|
242
242
|
return values
|
|
243
|
+
|
|
244
|
+
def set_unit(self, variable: str, unit: str):
|
|
245
|
+
"""
|
|
246
|
+
Set the unit of a time series.
|
|
247
|
+
|
|
248
|
+
:param variable: Time series ID.
|
|
249
|
+
:param unit: Unit.
|
|
250
|
+
"""
|
|
251
|
+
assert hasattr(
|
|
252
|
+
self, "_PIMixin__timeseries_import"
|
|
253
|
+
), "set_unit can only be called after read() in pre() has finished."
|
|
254
|
+
self.__timeseries_import.set_unit(variable, unit, 0)
|
|
255
|
+
self.__timeseries_export.set_unit(variable, unit, 0)
|
|
@@ -82,9 +82,21 @@ class SimulationProblem(DataStoreAccessor):
|
|
|
82
82
|
model_name = self.__class__.__name__
|
|
83
83
|
|
|
84
84
|
# Load model from pymoca backend
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
compiler_options = self.compiler_options()
|
|
86
|
+
logger.info(f"Loading/compiling model {model_name}.")
|
|
87
|
+
try:
|
|
88
|
+
self.__pymoca_model = pymoca.backends.casadi.api.transfer_model(
|
|
89
|
+
kwargs["model_folder"], model_name, compiler_options
|
|
90
|
+
)
|
|
91
|
+
except RuntimeError as error:
|
|
92
|
+
if compiler_options.get("cache", False):
|
|
93
|
+
raise error
|
|
94
|
+
compiler_options["cache"] = False
|
|
95
|
+
logger.warning(f"Loading model {model_name} using a cache file failed: {error}.")
|
|
96
|
+
logger.info(f"Compiling model {model_name}.")
|
|
97
|
+
self.__pymoca_model = pymoca.backends.casadi.api.transfer_model(
|
|
98
|
+
kwargs["model_folder"], model_name, compiler_options
|
|
99
|
+
)
|
|
88
100
|
|
|
89
101
|
# Extract the CasADi MX variables used in the model
|
|
90
102
|
self.__mx = {}
|
|
@@ -319,6 +331,17 @@ class SimulationProblem(DataStoreAccessor):
|
|
|
319
331
|
"""
|
|
320
332
|
Initialize state vector with default values
|
|
321
333
|
|
|
334
|
+
Initial values are first read from the given Modelica files.
|
|
335
|
+
If an initial value equals zero or is not provided by a Modelica file,
|
|
336
|
+
and the variable is not marked as fixed,
|
|
337
|
+
then the initial value is tried to be set with the initial_state method.
|
|
338
|
+
When using CSVMixin, this method by default looks for initial values
|
|
339
|
+
in an initial_state.csv file.
|
|
340
|
+
Furthermore, if a variable is not marked as fixed
|
|
341
|
+
and no initial value is given by the initial_state method,
|
|
342
|
+
the initial value can be overwritten using the seed method.
|
|
343
|
+
When a variable is marked as fixed, the initial value is only read from the Modelica file.
|
|
344
|
+
|
|
322
345
|
:param config_file: Path to an initialization file.
|
|
323
346
|
"""
|
|
324
347
|
if config_file:
|
|
@@ -393,29 +416,35 @@ class SimulationProblem(DataStoreAccessor):
|
|
|
393
416
|
for var in itertools.chain(self.__pymoca_model.states, self.__pymoca_model.alg_states):
|
|
394
417
|
var_name = var.symbol.name()
|
|
395
418
|
var_nominal = self.get_variable_nominal(var_name)
|
|
419
|
+
start_values = {}
|
|
396
420
|
|
|
397
421
|
# Attempt to cast var.start to python type
|
|
422
|
+
start_value_is_symbolic = False
|
|
398
423
|
mx_start = ca.MX(var.start)
|
|
399
424
|
if mx_start.is_constant():
|
|
400
425
|
# cast var.start to python type
|
|
401
|
-
|
|
426
|
+
start_value_pymoca = var.python_type(mx_start.to_DM())
|
|
427
|
+
if start_value_pymoca is not None and start_value_pymoca != 0:
|
|
428
|
+
start_values["modelica"] = start_value_pymoca
|
|
402
429
|
else:
|
|
403
|
-
|
|
404
|
-
|
|
430
|
+
start_value_is_symbolic = True
|
|
431
|
+
start_values["modelica"] = mx_start
|
|
405
432
|
|
|
406
|
-
if
|
|
433
|
+
if not var.fixed:
|
|
407
434
|
# To make initialization easier, we allow setting initial states by providing
|
|
408
435
|
# timeseries with names that match a symbol in the model. We only check for this
|
|
409
436
|
# matching if the start and fixed attributes were left as default
|
|
410
437
|
try:
|
|
411
|
-
|
|
438
|
+
start_values["initial_state"] = self.initial_state()[var_name]
|
|
412
439
|
except KeyError:
|
|
413
440
|
pass
|
|
414
441
|
else:
|
|
415
442
|
# An initial state was found- add it to the constrained residuals
|
|
416
443
|
logger.debug(
|
|
417
444
|
"Initialize: Added {} = {} to initial equations "
|
|
418
|
-
"(found matching timeseries).".format(
|
|
445
|
+
"(found matching timeseries).".format(
|
|
446
|
+
var_name, start_values["initial_state"]
|
|
447
|
+
)
|
|
419
448
|
)
|
|
420
449
|
# Set var to be fixed
|
|
421
450
|
var.fixed = True
|
|
@@ -425,36 +454,75 @@ class SimulationProblem(DataStoreAccessor):
|
|
|
425
454
|
# timeseries with names that match a symbol in the model. We only check for this
|
|
426
455
|
# matching if the start and fixed attributes were left as default
|
|
427
456
|
try:
|
|
428
|
-
|
|
457
|
+
start_values["seed"] = self.seed()[var_name]
|
|
429
458
|
except KeyError:
|
|
430
459
|
pass
|
|
431
460
|
else:
|
|
432
461
|
# An initial state was found- add it to the constrained residuals
|
|
433
462
|
logger.debug(
|
|
434
463
|
"Initialize: Added {} = {} as initial guess "
|
|
435
|
-
"(found matching timeseries).".format(var_name,
|
|
464
|
+
"(found matching timeseries).".format(var_name, start_values["seed"])
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
# Set the start value based on the different inputs.
|
|
468
|
+
if "seed" in start_values:
|
|
469
|
+
input_source = "seed"
|
|
470
|
+
source_description = "seed method"
|
|
471
|
+
elif "modelica" in start_values:
|
|
472
|
+
input_source = "modelica"
|
|
473
|
+
source_description = "modelica file"
|
|
474
|
+
elif "initial_state" in start_values:
|
|
475
|
+
input_source = "initial_state"
|
|
476
|
+
source_description = "initial_state method (typically reads initial_state.csv)"
|
|
477
|
+
else:
|
|
478
|
+
start_values["modelica"] = start_value_pymoca
|
|
479
|
+
input_source = "modelica"
|
|
480
|
+
source_description = "modelica file or default value"
|
|
481
|
+
start_val = start_values.get(input_source, None)
|
|
482
|
+
is_numeric = start_val is not None and not start_value_is_symbolic
|
|
483
|
+
numeric_start_val = start_val if is_numeric else 0.0
|
|
484
|
+
if len(start_values) > 1:
|
|
485
|
+
logger.warning(
|
|
486
|
+
"Initialize: Multiple initial values for {} are provided: {}.".format(
|
|
487
|
+
var_name, start_values
|
|
436
488
|
)
|
|
489
|
+
+ " Value from {} will be used to continue.".format(source_description)
|
|
490
|
+
)
|
|
437
491
|
|
|
438
492
|
# Attempt to set start_val in the state vector. Default to zero if unknown.
|
|
439
493
|
try:
|
|
440
|
-
self.set_var(var_name,
|
|
494
|
+
self.set_var(var_name, numeric_start_val)
|
|
441
495
|
except KeyError:
|
|
442
496
|
logger.warning(
|
|
443
497
|
"Initialize: {} not found in state vector. "
|
|
444
|
-
"Initial value of {} not set.".format(var_name,
|
|
498
|
+
"Initial value of {} not set.".format(var_name, numeric_start_val)
|
|
445
499
|
)
|
|
446
500
|
|
|
447
501
|
# Add a residual for the difference between the state and its starting expression
|
|
448
|
-
start_expr = start_val
|
|
502
|
+
start_expr = start_val
|
|
449
503
|
if var.fixed:
|
|
450
504
|
# Set bounds to be equal to each other, such that IPOPT can
|
|
451
505
|
# turn the decision variable into a parameter.
|
|
506
|
+
if var.min != -np.inf or var.max != np.inf:
|
|
507
|
+
logger.info(
|
|
508
|
+
"Initialize: bounds of {} will be overwritten".format(var_name)
|
|
509
|
+
+ " by the start value given by {}.".format(source_description)
|
|
510
|
+
)
|
|
452
511
|
var.min = start_expr
|
|
453
512
|
var.max = start_expr
|
|
454
513
|
else:
|
|
455
514
|
# minimize residual
|
|
456
515
|
minimized_residuals.append((var.symbol - start_expr) / var_nominal)
|
|
457
516
|
|
|
517
|
+
# Check that the start_value is in between the variable bounds.
|
|
518
|
+
if start_val is not None and is_numeric:
|
|
519
|
+
if not (var.min <= start_val and start_val <= var.max):
|
|
520
|
+
logger.warning(
|
|
521
|
+
"Initialize: start value {} = {}".format(var_name, start_val)
|
|
522
|
+
+ " is not in between bounds {} and {}".format(var.min, var.max)
|
|
523
|
+
+ " and will be adjusted."
|
|
524
|
+
)
|
|
525
|
+
|
|
458
526
|
# Default start var for ders is zero
|
|
459
527
|
for der_var in self.__mx["derivatives"]:
|
|
460
528
|
self.set_var(der_var.name(), 0.0)
|
|
@@ -610,7 +678,15 @@ class SimulationProblem(DataStoreAccessor):
|
|
|
610
678
|
# If unsuccessful, stop.
|
|
611
679
|
return_status = solver.stats()["return_status"]
|
|
612
680
|
if return_status not in {"Solve_Succeeded", "Solved_To_Acceptable_Level"}:
|
|
613
|
-
|
|
681
|
+
if return_status == "Infeasible_Problem_Detected":
|
|
682
|
+
message = (
|
|
683
|
+
"Initialization Failed with return status: {}. ".format(return_status)
|
|
684
|
+
+ "This means no initial state could be found "
|
|
685
|
+
+ "that satisfies all equations and constraints."
|
|
686
|
+
)
|
|
687
|
+
else:
|
|
688
|
+
message = "Initialization Failed with return status: {}. ".format(return_status)
|
|
689
|
+
raise Exception(message)
|
|
614
690
|
|
|
615
691
|
# Update state vector with initial conditions
|
|
616
692
|
self.__state_vector[: self.__n_states] = initial_state["x"][: self.__n_states].T
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/goal_programming_mixin_base.py
RENAMED
|
File without changes
|
|
File without changes
|
{rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/initial_state_estimation_mixin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rtc-tools-2.6.0b2 → rtc-tools-2.7.0a1}/src/rtctools/optimization/min_abs_goal_programming_mixin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|