rtc-tools 2.5.2rc3__py3-none-any.whl → 2.6.0__py3-none-any.whl
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.5.2rc3.dist-info → rtc_tools-2.6.0.dist-info}/METADATA +7 -7
- rtc_tools-2.6.0.dist-info/RECORD +50 -0
- {rtc_tools-2.5.2rc3.dist-info → rtc_tools-2.6.0.dist-info}/WHEEL +1 -1
- rtctools/__init__.py +2 -1
- rtctools/_internal/alias_tools.py +12 -10
- rtctools/_internal/caching.py +5 -3
- rtctools/_internal/casadi_helpers.py +11 -32
- rtctools/_internal/debug_check_helpers.py +1 -1
- rtctools/_version.py +3 -3
- rtctools/data/__init__.py +2 -2
- rtctools/data/csv.py +54 -33
- rtctools/data/interpolation/bspline.py +3 -3
- rtctools/data/interpolation/bspline1d.py +42 -29
- rtctools/data/interpolation/bspline2d.py +10 -4
- rtctools/data/netcdf.py +137 -93
- rtctools/data/pi.py +304 -210
- rtctools/data/rtc.py +64 -53
- rtctools/data/storage.py +91 -51
- rtctools/optimization/collocated_integrated_optimization_problem.py +1244 -696
- rtctools/optimization/control_tree_mixin.py +68 -66
- rtctools/optimization/csv_lookup_table_mixin.py +107 -74
- rtctools/optimization/csv_mixin.py +83 -52
- rtctools/optimization/goal_programming_mixin.py +239 -148
- rtctools/optimization/goal_programming_mixin_base.py +204 -111
- rtctools/optimization/homotopy_mixin.py +36 -27
- rtctools/optimization/initial_state_estimation_mixin.py +8 -8
- rtctools/optimization/io_mixin.py +48 -43
- rtctools/optimization/linearization_mixin.py +3 -1
- rtctools/optimization/linearized_order_goal_programming_mixin.py +57 -28
- rtctools/optimization/min_abs_goal_programming_mixin.py +72 -29
- rtctools/optimization/modelica_mixin.py +135 -81
- rtctools/optimization/netcdf_mixin.py +32 -18
- rtctools/optimization/optimization_problem.py +181 -127
- rtctools/optimization/pi_mixin.py +68 -36
- rtctools/optimization/planning_mixin.py +19 -0
- rtctools/optimization/single_pass_goal_programming_mixin.py +159 -112
- rtctools/optimization/timeseries.py +4 -6
- rtctools/rtctoolsapp.py +18 -18
- rtctools/simulation/csv_mixin.py +37 -30
- rtctools/simulation/io_mixin.py +9 -5
- rtctools/simulation/pi_mixin.py +62 -32
- rtctools/simulation/simulation_problem.py +471 -180
- rtctools/util.py +84 -56
- rtc_tools-2.5.2rc3.dist-info/RECORD +0 -49
- {rtc_tools-2.5.2rc3.dist-info → rtc_tools-2.6.0.dist-info}/COPYING.LESSER +0 -0
- {rtc_tools-2.5.2rc3.dist-info → rtc_tools-2.6.0.dist-info}/entry_points.txt +0 -0
- {rtc_tools-2.5.2rc3.dist-info → rtc_tools-2.6.0.dist-info}/top_level.txt +0 -0
rtctools/simulation/csv_mixin.py
CHANGED
|
@@ -17,14 +17,15 @@ class CSVMixin(IOMixin):
|
|
|
17
17
|
During preprocessing, files named ``timeseries_import.csv``, ``initial_state.csv``,
|
|
18
18
|
and ``parameters.csv`` are read from the ``input`` subfolder.
|
|
19
19
|
|
|
20
|
-
During postprocessing, a file named ``timeseries_export.csv`` is written to the ``output``
|
|
20
|
+
During postprocessing, a file named ``timeseries_export.csv`` is written to the ``output``
|
|
21
|
+
subfolder.
|
|
21
22
|
|
|
22
23
|
:cvar csv_delimiter: Column delimiter used in CSV files. Default is ``,``.
|
|
23
24
|
:cvar csv_validate_timeseries: Check consistency of timeseries. Default is ``True``.
|
|
24
25
|
"""
|
|
25
26
|
|
|
26
27
|
#: Column delimiter used in CSV files
|
|
27
|
-
csv_delimiter =
|
|
28
|
+
csv_delimiter = ","
|
|
28
29
|
|
|
29
30
|
#: Check consistency of timeseries
|
|
30
31
|
csv_validate_timeseries = True
|
|
@@ -45,30 +46,32 @@ class CSVMixin(IOMixin):
|
|
|
45
46
|
"""
|
|
46
47
|
if initial_state.shape:
|
|
47
48
|
raise Exception(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
"CSVMixin: Initial state file {} contains more than one row of data. "
|
|
50
|
+
"Please remove the data row(s) that do not describe the initial "
|
|
51
|
+
"state.".format(os.path.join(self._input_folder, "initial_state.csv"))
|
|
52
|
+
)
|
|
51
53
|
|
|
52
54
|
# Read CSV files
|
|
53
55
|
_timeseries = csv.load(
|
|
54
|
-
os.path.join(self._input_folder, self.timeseries_import_basename +
|
|
55
|
-
delimiter=self.csv_delimiter,
|
|
56
|
+
os.path.join(self._input_folder, self.timeseries_import_basename + ".csv"),
|
|
57
|
+
delimiter=self.csv_delimiter,
|
|
58
|
+
with_time=True,
|
|
59
|
+
)
|
|
56
60
|
self.__timeseries_times = _timeseries[_timeseries.dtype.names[0]]
|
|
57
61
|
|
|
58
62
|
self.io.reference_datetime = self.__timeseries_times[0]
|
|
59
63
|
|
|
60
64
|
for key in _timeseries.dtype.names[1:]:
|
|
61
65
|
self.io.set_timeseries(
|
|
62
|
-
key,
|
|
63
|
-
|
|
64
|
-
np.asarray(_timeseries[key], dtype=np.float64))
|
|
66
|
+
key, self.__timeseries_times, np.asarray(_timeseries[key], dtype=np.float64)
|
|
67
|
+
)
|
|
65
68
|
|
|
66
69
|
logger.debug("CSVMixin: Read timeseries.")
|
|
67
70
|
|
|
68
71
|
try:
|
|
69
72
|
_parameters = csv.load(
|
|
70
|
-
os.path.join(self._input_folder,
|
|
71
|
-
|
|
73
|
+
os.path.join(self._input_folder, "parameters.csv"), delimiter=self.csv_delimiter
|
|
74
|
+
)
|
|
72
75
|
for key in _parameters.dtype.names:
|
|
73
76
|
self.io.set_parameter(key, float(_parameters[key]))
|
|
74
77
|
logger.debug("CSVMixin: Read parameters.")
|
|
@@ -77,12 +80,13 @@ class CSVMixin(IOMixin):
|
|
|
77
80
|
|
|
78
81
|
try:
|
|
79
82
|
_initial_state = csv.load(
|
|
80
|
-
os.path.join(self._input_folder,
|
|
81
|
-
|
|
83
|
+
os.path.join(self._input_folder, "initial_state.csv"), delimiter=self.csv_delimiter
|
|
84
|
+
)
|
|
82
85
|
logger.debug("CSVMixin: Read initial state.")
|
|
83
86
|
check_initial_state_array(_initial_state)
|
|
84
87
|
self.__initial_state = {
|
|
85
|
-
key: float(_initial_state[key]) for key in _initial_state.dtype.names
|
|
88
|
+
key: float(_initial_state[key]) for key in _initial_state.dtype.names
|
|
89
|
+
}
|
|
86
90
|
except IOError:
|
|
87
91
|
self.__initial_state = {}
|
|
88
92
|
|
|
@@ -92,16 +96,16 @@ class CSVMixin(IOMixin):
|
|
|
92
96
|
continue
|
|
93
97
|
else:
|
|
94
98
|
logger.warning(
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
"CSVMixin: Entry {} in initial_state.csv conflicts with "
|
|
100
|
+
"timeseries_import.csv".format(collision)
|
|
101
|
+
)
|
|
97
102
|
|
|
98
103
|
# Timestamp check
|
|
99
104
|
if self.csv_validate_timeseries:
|
|
100
105
|
times = self.__timeseries_times
|
|
101
106
|
for i in range(len(times) - 1):
|
|
102
107
|
if times[i] >= times[i + 1]:
|
|
103
|
-
raise Exception(
|
|
104
|
-
'CSVMixin: Time stamps must be strictly increasing.')
|
|
108
|
+
raise Exception("CSVMixin: Time stamps must be strictly increasing.")
|
|
105
109
|
|
|
106
110
|
times = self.__timeseries_times
|
|
107
111
|
dt = times[1] - times[0]
|
|
@@ -111,10 +115,10 @@ class CSVMixin(IOMixin):
|
|
|
111
115
|
for i in range(len(times) - 1):
|
|
112
116
|
if times[i + 1] - times[i] != dt:
|
|
113
117
|
raise Exception(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
"CSVMixin: Expecting equidistant timeseries, the time step "
|
|
119
|
+
"towards {} is not the same as the time step(s) before. "
|
|
120
|
+
"Set equidistant=False if this is intended.".format(times[i + 1])
|
|
121
|
+
)
|
|
118
122
|
|
|
119
123
|
def write(self):
|
|
120
124
|
# Call parent class first for default behaviour.
|
|
@@ -123,15 +127,15 @@ class CSVMixin(IOMixin):
|
|
|
123
127
|
times = self._simulation_times
|
|
124
128
|
|
|
125
129
|
# Write output
|
|
126
|
-
names = [
|
|
127
|
-
formats = [
|
|
128
|
-
dtype = {
|
|
130
|
+
names = ["time"] + sorted(set(self._io_output_variables))
|
|
131
|
+
formats = ["O"] + (len(names) - 1) * ["f8"]
|
|
132
|
+
dtype = {"names": names, "formats": formats}
|
|
129
133
|
data = np.zeros(len(times), dtype=dtype)
|
|
130
|
-
data[
|
|
134
|
+
data["time"] = self.io.sec_to_datetime(times, self.io.reference_datetime)
|
|
131
135
|
for variable in self._io_output_variables:
|
|
132
136
|
data[variable] = np.array(self._io_output[variable])
|
|
133
137
|
|
|
134
|
-
fname = os.path.join(self._output_folder, self.timeseries_export_basename +
|
|
138
|
+
fname = os.path.join(self._output_folder, self.timeseries_export_basename + ".csv")
|
|
135
139
|
csv.save(fname, data, delimiter=self.csv_delimiter, with_time=True)
|
|
136
140
|
|
|
137
141
|
@cached
|
|
@@ -149,7 +153,6 @@ class CSVMixin(IOMixin):
|
|
|
149
153
|
|
|
150
154
|
# Load initial states from __initial_state
|
|
151
155
|
for variable, value in self.__initial_state.items():
|
|
152
|
-
|
|
153
156
|
# Get the cannonical vars and signs
|
|
154
157
|
canonical_var, sign = self.alias_relation.canonical_signed(variable)
|
|
155
158
|
|
|
@@ -160,5 +163,9 @@ class CSVMixin(IOMixin):
|
|
|
160
163
|
if logger.getEffectiveLevel() == logging.DEBUG:
|
|
161
164
|
logger.debug("CSVMixin: Read initial state {} = {}".format(variable, value))
|
|
162
165
|
else:
|
|
163
|
-
logger.warning(
|
|
166
|
+
logger.warning(
|
|
167
|
+
"CSVMixin: In initial_state.csv, {} is not an input or state variable.".format(
|
|
168
|
+
variable
|
|
169
|
+
)
|
|
170
|
+
)
|
|
164
171
|
return initial_state
|
rtctools/simulation/io_mixin.py
CHANGED
|
@@ -34,7 +34,8 @@ class IOMixin(SimulationProblem, metaclass=ABCMeta):
|
|
|
34
34
|
@abstractmethod
|
|
35
35
|
def read(self) -> None:
|
|
36
36
|
"""
|
|
37
|
-
Reads input data from files, storing it in the internal data store through the various set
|
|
37
|
+
Reads input data from files, storing it in the internal data store through the various set
|
|
38
|
+
or add methods
|
|
38
39
|
"""
|
|
39
40
|
pass
|
|
40
41
|
|
|
@@ -44,8 +45,9 @@ class IOMixin(SimulationProblem, metaclass=ABCMeta):
|
|
|
44
45
|
|
|
45
46
|
@abstractmethod
|
|
46
47
|
def write(self) -> None:
|
|
47
|
-
"""
|
|
48
|
-
Writes output data to files, getting the data from the data store through the various get
|
|
48
|
+
"""
|
|
49
|
+
Writes output data to files, getting the data from the data store through the various get
|
|
50
|
+
methods
|
|
49
51
|
"""
|
|
50
52
|
pass
|
|
51
53
|
|
|
@@ -102,8 +104,10 @@ class IOMixin(SimulationProblem, metaclass=ABCMeta):
|
|
|
102
104
|
if isfinite(value):
|
|
103
105
|
self.set_var(variable, value)
|
|
104
106
|
else:
|
|
105
|
-
logger.debug(
|
|
106
|
-
|
|
107
|
+
logger.debug(
|
|
108
|
+
"IOMixin: Found bad value {} at index [{}] "
|
|
109
|
+
"in timeseries aliased to input {}".format(value, t_idx, variable)
|
|
110
|
+
)
|
|
107
111
|
|
|
108
112
|
def update(self, dt):
|
|
109
113
|
# Time step
|
rtctools/simulation/pi_mixin.py
CHANGED
|
@@ -16,16 +16,19 @@ class PIMixin(IOMixin):
|
|
|
16
16
|
<https://publicwiki.deltares.nl/display/FEWSDOC/The+Delft-Fews+Published+Interface>`_
|
|
17
17
|
I/O to your simulation problem.
|
|
18
18
|
|
|
19
|
-
During preprocessing, files named ``rtcDataConfig.xml``, ``timeseries_import.xml``,
|
|
20
|
-
are read from the ``input`` subfolder. ``rtcDataConfig.xml``
|
|
21
|
-
tuples of FEWS identifiers, including location and parameter ID, to RTC-Tools time series
|
|
19
|
+
During preprocessing, files named ``rtcDataConfig.xml``, ``timeseries_import.xml``,
|
|
20
|
+
and``rtcParameterConfig.xml`` are read from the ``input`` subfolder. ``rtcDataConfig.xml``
|
|
21
|
+
maps tuples of FEWS identifiers, including location and parameter ID, to RTC-Tools time series
|
|
22
|
+
identifiers.
|
|
22
23
|
|
|
23
|
-
During postprocessing, a file named ``timeseries_export.xml`` is written to the ``output``
|
|
24
|
+
During postprocessing, a file named ``timeseries_export.xml`` is written to the ``output``
|
|
25
|
+
subfolder.
|
|
24
26
|
|
|
25
27
|
:cvar pi_binary_timeseries: Whether to use PI binary timeseries format. Default is ``False``.
|
|
26
28
|
:cvar pi_parameter_config_basenames:
|
|
27
29
|
List of parameter config file basenames to read. Default is [``rtcParameterConfig``].
|
|
28
|
-
:cvar pi_check_for_duplicate_parameters:
|
|
30
|
+
:cvar pi_check_for_duplicate_parameters:
|
|
31
|
+
Check if duplicate parameters are read. Default is ``True``.
|
|
29
32
|
:cvar pi_validate_timeseries: Check consistency of timeseries. Default is ``True``.
|
|
30
33
|
"""
|
|
31
34
|
|
|
@@ -33,7 +36,7 @@ class PIMixin(IOMixin):
|
|
|
33
36
|
pi_binary_timeseries = False
|
|
34
37
|
|
|
35
38
|
#: Location of rtcParameterConfig files
|
|
36
|
-
pi_parameter_config_basenames = [
|
|
39
|
+
pi_parameter_config_basenames = ["rtcParameterConfig"]
|
|
37
40
|
|
|
38
41
|
#: Check consistency of timeseries
|
|
39
42
|
pi_validate_timeseries = True
|
|
@@ -60,11 +63,15 @@ class PIMixin(IOMixin):
|
|
|
60
63
|
self.__parameter_config = []
|
|
61
64
|
try:
|
|
62
65
|
for pi_parameter_config_basename in self.pi_parameter_config_basenames:
|
|
63
|
-
self.__parameter_config.append(
|
|
64
|
-
self._input_folder, pi_parameter_config_basename)
|
|
66
|
+
self.__parameter_config.append(
|
|
67
|
+
pi.ParameterConfig(self._input_folder, pi_parameter_config_basename)
|
|
68
|
+
)
|
|
65
69
|
except FileNotFoundError:
|
|
66
70
|
raise FileNotFoundError(
|
|
67
|
-
"PIMixin: {}.xml not found in {}.".format(
|
|
71
|
+
"PIMixin: {}.xml not found in {}.".format(
|
|
72
|
+
pi_parameter_config_basename, self._input_folder
|
|
73
|
+
)
|
|
74
|
+
)
|
|
68
75
|
|
|
69
76
|
# Make a parameters dict for later access
|
|
70
77
|
for parameter_config in self.__parameter_config:
|
|
@@ -77,15 +84,27 @@ class PIMixin(IOMixin):
|
|
|
77
84
|
|
|
78
85
|
try:
|
|
79
86
|
self.__timeseries_import = pi.Timeseries(
|
|
80
|
-
self.__data_config,
|
|
81
|
-
|
|
87
|
+
self.__data_config,
|
|
88
|
+
self._input_folder,
|
|
89
|
+
self.timeseries_import_basename,
|
|
90
|
+
binary=self.pi_binary_timeseries,
|
|
91
|
+
pi_validate_times=self.pi_validate_timeseries,
|
|
92
|
+
)
|
|
82
93
|
except FileNotFoundError:
|
|
83
|
-
raise FileNotFoundError(
|
|
84
|
-
|
|
94
|
+
raise FileNotFoundError(
|
|
95
|
+
"PIMixin: {}.xml not found in {}".format(
|
|
96
|
+
self.timeseries_import_basename, self._input_folder
|
|
97
|
+
)
|
|
98
|
+
)
|
|
85
99
|
|
|
86
100
|
self.__timeseries_export = pi.Timeseries(
|
|
87
|
-
self.__data_config,
|
|
88
|
-
|
|
101
|
+
self.__data_config,
|
|
102
|
+
self._output_folder,
|
|
103
|
+
self.timeseries_export_basename,
|
|
104
|
+
binary=self.pi_binary_timeseries,
|
|
105
|
+
pi_validate_times=False,
|
|
106
|
+
make_new_file=True,
|
|
107
|
+
)
|
|
89
108
|
|
|
90
109
|
# Convert timeseries timestamps to seconds since t0 for internal use
|
|
91
110
|
timeseries_import_times = self.__timeseries_import.times
|
|
@@ -94,8 +113,7 @@ class PIMixin(IOMixin):
|
|
|
94
113
|
if self.pi_validate_timeseries:
|
|
95
114
|
for i in range(len(timeseries_import_times) - 1):
|
|
96
115
|
if timeseries_import_times[i] >= timeseries_import_times[i + 1]:
|
|
97
|
-
raise ValueError(
|
|
98
|
-
'PIMixin: Time stamps must be strictly increasing.')
|
|
116
|
+
raise ValueError("PIMixin: Time stamps must be strictly increasing.")
|
|
99
117
|
|
|
100
118
|
# Check if the timeseries are equidistant
|
|
101
119
|
dt = timeseries_import_times[1] - timeseries_import_times[0]
|
|
@@ -103,10 +121,12 @@ class PIMixin(IOMixin):
|
|
|
103
121
|
for i in range(len(timeseries_import_times) - 1):
|
|
104
122
|
if timeseries_import_times[i + 1] - timeseries_import_times[i] != dt:
|
|
105
123
|
raise ValueError(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
timeseries_import_times[i + 1]
|
|
124
|
+
"PIMixin: Expecting equidistant timeseries, the time step "
|
|
125
|
+
"towards {} is not the same as the time step(s) before. Set "
|
|
126
|
+
"unit to nonequidistant if this is intended.".format(
|
|
127
|
+
timeseries_import_times[i + 1]
|
|
128
|
+
)
|
|
129
|
+
)
|
|
110
130
|
|
|
111
131
|
# Stick timeseries into an AliasDict
|
|
112
132
|
self.io.reference_datetime = self.__timeseries_import.forecast_datetime
|
|
@@ -115,7 +135,9 @@ class PIMixin(IOMixin):
|
|
|
115
135
|
for variable, values in self.__timeseries_import.items(self.pi_ensemble_member):
|
|
116
136
|
self.io.set_timeseries(variable, timeseries_import_times, values)
|
|
117
137
|
if debug and variable in self.get_variables():
|
|
118
|
-
logger.debug(
|
|
138
|
+
logger.debug(
|
|
139
|
+
"PIMixin: Timeseries {} replaced another aliased timeseries.".format(variable)
|
|
140
|
+
)
|
|
119
141
|
|
|
120
142
|
def write(self):
|
|
121
143
|
# Call parent class first for default behaviour.
|
|
@@ -129,7 +151,9 @@ class PIMixin(IOMixin):
|
|
|
129
151
|
|
|
130
152
|
# Start of write output
|
|
131
153
|
# Write the time range for the export file.
|
|
132
|
-
self.__timeseries_export.times = [
|
|
154
|
+
self.__timeseries_export.times = [
|
|
155
|
+
self.io.reference_datetime + timedelta(seconds=s) for s in times
|
|
156
|
+
]
|
|
133
157
|
|
|
134
158
|
# Write other time settings
|
|
135
159
|
self.__timeseries_export.forecast_datetime = self.io.reference_datetime
|
|
@@ -149,12 +173,15 @@ class PIMixin(IOMixin):
|
|
|
149
173
|
self.__data_config.pi_variable_ids(variable)
|
|
150
174
|
except KeyError:
|
|
151
175
|
logger.debug(
|
|
152
|
-
|
|
153
|
-
|
|
176
|
+
"PIMixin: variable {} has no mapping defined in rtcDataConfig "
|
|
177
|
+
"so cannot be added to the output file.".format(variable)
|
|
178
|
+
)
|
|
154
179
|
continue
|
|
155
180
|
|
|
156
181
|
# Add series to output file
|
|
157
|
-
self.__timeseries_export.set(
|
|
182
|
+
self.__timeseries_export.set(
|
|
183
|
+
variable, values, unit=self.__timeseries_import.get_unit(variable)
|
|
184
|
+
)
|
|
158
185
|
|
|
159
186
|
# Write output file to disk
|
|
160
187
|
self.__timeseries_export.write()
|
|
@@ -186,10 +213,12 @@ class PIMixin(IOMixin):
|
|
|
186
213
|
if check_consistency:
|
|
187
214
|
if len(self.times()) != len(values):
|
|
188
215
|
raise ValueError(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
variable, self.__timeseries_import.dt
|
|
216
|
+
"PIMixin: Trying to set/append values {} with a different "
|
|
217
|
+
"length than the forecast length. Please make sure the "
|
|
218
|
+
"values cover forecastDate through endDate with timestep {}.".format(
|
|
219
|
+
variable, self.__timeseries_import.dt
|
|
220
|
+
)
|
|
221
|
+
)
|
|
193
222
|
|
|
194
223
|
if unit is None:
|
|
195
224
|
unit = self.__timeseries_import.get_unit(variable)
|
|
@@ -199,8 +228,9 @@ class PIMixin(IOMixin):
|
|
|
199
228
|
self.__data_config.pi_variable_ids(variable)
|
|
200
229
|
except KeyError:
|
|
201
230
|
logger.debug(
|
|
202
|
-
|
|
203
|
-
|
|
231
|
+
"PIMixin: variable {} has no mapping defined in rtcDataConfig "
|
|
232
|
+
"so cannot be added to the output file.".format(variable)
|
|
233
|
+
)
|
|
204
234
|
else:
|
|
205
235
|
self.__timeseries_export.set(variable, values, unit=unit)
|
|
206
236
|
|