rtc-tools 2.7.3__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.
- rtc_tools-2.7.3.dist-info/METADATA +53 -0
- rtc_tools-2.7.3.dist-info/RECORD +50 -0
- rtc_tools-2.7.3.dist-info/WHEEL +5 -0
- rtc_tools-2.7.3.dist-info/entry_points.txt +3 -0
- rtc_tools-2.7.3.dist-info/licenses/COPYING.LESSER +165 -0
- rtc_tools-2.7.3.dist-info/top_level.txt +1 -0
- rtctools/__init__.py +5 -0
- rtctools/_internal/__init__.py +0 -0
- rtctools/_internal/alias_tools.py +188 -0
- rtctools/_internal/caching.py +25 -0
- rtctools/_internal/casadi_helpers.py +99 -0
- rtctools/_internal/debug_check_helpers.py +41 -0
- rtctools/_version.py +21 -0
- rtctools/data/__init__.py +4 -0
- rtctools/data/csv.py +150 -0
- rtctools/data/interpolation/__init__.py +3 -0
- rtctools/data/interpolation/bspline.py +31 -0
- rtctools/data/interpolation/bspline1d.py +169 -0
- rtctools/data/interpolation/bspline2d.py +54 -0
- rtctools/data/netcdf.py +467 -0
- rtctools/data/pi.py +1236 -0
- rtctools/data/rtc.py +228 -0
- rtctools/data/storage.py +343 -0
- rtctools/optimization/__init__.py +0 -0
- rtctools/optimization/collocated_integrated_optimization_problem.py +3208 -0
- rtctools/optimization/control_tree_mixin.py +221 -0
- rtctools/optimization/csv_lookup_table_mixin.py +462 -0
- rtctools/optimization/csv_mixin.py +300 -0
- rtctools/optimization/goal_programming_mixin.py +769 -0
- rtctools/optimization/goal_programming_mixin_base.py +1094 -0
- rtctools/optimization/homotopy_mixin.py +165 -0
- rtctools/optimization/initial_state_estimation_mixin.py +89 -0
- rtctools/optimization/io_mixin.py +320 -0
- rtctools/optimization/linearization_mixin.py +33 -0
- rtctools/optimization/linearized_order_goal_programming_mixin.py +235 -0
- rtctools/optimization/min_abs_goal_programming_mixin.py +385 -0
- rtctools/optimization/modelica_mixin.py +482 -0
- rtctools/optimization/netcdf_mixin.py +177 -0
- rtctools/optimization/optimization_problem.py +1302 -0
- rtctools/optimization/pi_mixin.py +292 -0
- rtctools/optimization/planning_mixin.py +19 -0
- rtctools/optimization/single_pass_goal_programming_mixin.py +676 -0
- rtctools/optimization/timeseries.py +56 -0
- rtctools/rtctoolsapp.py +131 -0
- rtctools/simulation/__init__.py +0 -0
- rtctools/simulation/csv_mixin.py +171 -0
- rtctools/simulation/io_mixin.py +195 -0
- rtctools/simulation/pi_mixin.py +255 -0
- rtctools/simulation/simulation_problem.py +1293 -0
- rtctools/util.py +241 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from datetime import timedelta
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
import rtctools.data.pi as pi
|
|
7
|
+
import rtctools.data.rtc as rtc
|
|
8
|
+
from rtctools.simulation.io_mixin import IOMixin
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger("rtctools")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PIMixin(IOMixin):
|
|
14
|
+
"""
|
|
15
|
+
Adds `Delft-FEWS Published Interface
|
|
16
|
+
<https://publicwiki.deltares.nl/display/FEWSDOC/The+Delft-Fews+Published+Interface>`_
|
|
17
|
+
I/O to your simulation problem.
|
|
18
|
+
|
|
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.
|
|
23
|
+
|
|
24
|
+
During postprocessing, a file named ``timeseries_export.xml`` is written to the ``output``
|
|
25
|
+
subfolder.
|
|
26
|
+
|
|
27
|
+
:cvar pi_binary_timeseries: Whether to use PI binary timeseries format. Default is ``False``.
|
|
28
|
+
:cvar pi_parameter_config_basenames:
|
|
29
|
+
List of parameter config file basenames to read. Default is [``rtcParameterConfig``].
|
|
30
|
+
:cvar pi_check_for_duplicate_parameters:
|
|
31
|
+
Check if duplicate parameters are read. Default is ``True``.
|
|
32
|
+
:cvar pi_validate_timeseries: Check consistency of timeseries. Default is ``True``.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
#: Whether to use PI binary timeseries format
|
|
36
|
+
pi_binary_timeseries = False
|
|
37
|
+
|
|
38
|
+
#: Location of rtcParameterConfig files
|
|
39
|
+
pi_parameter_config_basenames = ["rtcParameterConfig"]
|
|
40
|
+
|
|
41
|
+
#: Check consistency of timeseries
|
|
42
|
+
pi_validate_timeseries = True
|
|
43
|
+
|
|
44
|
+
#: Check for duplicate parameters
|
|
45
|
+
pi_check_for_duplicate_parameters = True
|
|
46
|
+
|
|
47
|
+
#: Ensemble member to read from input
|
|
48
|
+
pi_ensemble_member = 0
|
|
49
|
+
|
|
50
|
+
def __init__(self, **kwargs):
|
|
51
|
+
# Call parent class first for default behaviour.
|
|
52
|
+
super().__init__(**kwargs)
|
|
53
|
+
|
|
54
|
+
# Load rtcDataConfig.xml. We assume this file does not change over the
|
|
55
|
+
# life time of this object.
|
|
56
|
+
self.__data_config = rtc.DataConfig(self._input_folder)
|
|
57
|
+
|
|
58
|
+
def read(self):
|
|
59
|
+
# Call parent class first for default behaviour.
|
|
60
|
+
super().read()
|
|
61
|
+
|
|
62
|
+
# rtcParameterConfig
|
|
63
|
+
self.__parameter_config = []
|
|
64
|
+
try:
|
|
65
|
+
for pi_parameter_config_basename in self.pi_parameter_config_basenames:
|
|
66
|
+
self.__parameter_config.append(
|
|
67
|
+
pi.ParameterConfig(self._input_folder, pi_parameter_config_basename)
|
|
68
|
+
)
|
|
69
|
+
except FileNotFoundError:
|
|
70
|
+
raise FileNotFoundError(
|
|
71
|
+
"PIMixin: {}.xml not found in {}.".format(
|
|
72
|
+
pi_parameter_config_basename, self._input_folder
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Make a parameters dict for later access
|
|
77
|
+
for parameter_config in self.__parameter_config:
|
|
78
|
+
for location_id, model_id, parameter_id, value in parameter_config:
|
|
79
|
+
try:
|
|
80
|
+
parameter = self.__data_config.parameter(parameter_id, location_id, model_id)
|
|
81
|
+
except KeyError:
|
|
82
|
+
parameter = parameter_id
|
|
83
|
+
self.io.set_parameter(parameter, value)
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
self.__timeseries_import = pi.Timeseries(
|
|
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
|
+
)
|
|
93
|
+
except FileNotFoundError:
|
|
94
|
+
raise FileNotFoundError(
|
|
95
|
+
"PIMixin: {}.xml not found in {}".format(
|
|
96
|
+
self.timeseries_import_basename, self._input_folder
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
self.__timeseries_export = pi.Timeseries(
|
|
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
|
+
)
|
|
108
|
+
|
|
109
|
+
# Convert timeseries timestamps to seconds since t0 for internal use
|
|
110
|
+
timeseries_import_times = self.__timeseries_import.times
|
|
111
|
+
|
|
112
|
+
# Timestamp check
|
|
113
|
+
if self.pi_validate_timeseries:
|
|
114
|
+
for i in range(len(timeseries_import_times) - 1):
|
|
115
|
+
if timeseries_import_times[i] >= timeseries_import_times[i + 1]:
|
|
116
|
+
raise ValueError("PIMixin: Time stamps must be strictly increasing.")
|
|
117
|
+
|
|
118
|
+
# Check if the timeseries are equidistant
|
|
119
|
+
dt = timeseries_import_times[1] - timeseries_import_times[0]
|
|
120
|
+
if self.pi_validate_timeseries:
|
|
121
|
+
for i in range(len(timeseries_import_times) - 1):
|
|
122
|
+
if timeseries_import_times[i + 1] - timeseries_import_times[i] != dt:
|
|
123
|
+
raise ValueError(
|
|
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
|
+
)
|
|
130
|
+
|
|
131
|
+
# Stick timeseries into an AliasDict
|
|
132
|
+
self.io.reference_datetime = self.__timeseries_import.forecast_datetime
|
|
133
|
+
|
|
134
|
+
debug = logger.getEffectiveLevel() == logging.DEBUG
|
|
135
|
+
for variable, values in self.__timeseries_import.items(self.pi_ensemble_member):
|
|
136
|
+
self.io.set_timeseries(variable, timeseries_import_times, values)
|
|
137
|
+
if debug and variable in self.get_variables():
|
|
138
|
+
logger.debug(
|
|
139
|
+
"PIMixin: Timeseries {} replaced another aliased timeseries.".format(variable)
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def write(self):
|
|
143
|
+
# Call parent class first for default behaviour.
|
|
144
|
+
super().write()
|
|
145
|
+
|
|
146
|
+
times = self._simulation_times
|
|
147
|
+
if len(set(np.diff(times))) == 1:
|
|
148
|
+
dt = timedelta(seconds=times[1] - times[0])
|
|
149
|
+
else:
|
|
150
|
+
dt = None
|
|
151
|
+
|
|
152
|
+
# Start of write output
|
|
153
|
+
# Write the time range for the export file.
|
|
154
|
+
self.__timeseries_export.times = [
|
|
155
|
+
self.io.reference_datetime + timedelta(seconds=s) for s in times
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
# Write other time settings
|
|
159
|
+
self.__timeseries_export.forecast_datetime = self.io.reference_datetime
|
|
160
|
+
self.__timeseries_export.dt = dt
|
|
161
|
+
self.__timeseries_export.timezone = self.__timeseries_import.timezone
|
|
162
|
+
|
|
163
|
+
# Write the ensemble properties for the export file.
|
|
164
|
+
self.__timeseries_export.ensemble_size = 1
|
|
165
|
+
self.__timeseries_export.contains_ensemble = self.__timeseries_import.contains_ensemble
|
|
166
|
+
|
|
167
|
+
# For all variables that are output variables the values are
|
|
168
|
+
# extracted from the results.
|
|
169
|
+
for variable in self._io_output_variables:
|
|
170
|
+
values = np.array(self._io_output[variable])
|
|
171
|
+
# Check if ID mapping is present
|
|
172
|
+
try:
|
|
173
|
+
self.__data_config.pi_variable_ids(variable)
|
|
174
|
+
except KeyError:
|
|
175
|
+
logger.debug(
|
|
176
|
+
"PIMixin: variable {} has no mapping defined in rtcDataConfig "
|
|
177
|
+
"so cannot be added to the output file.".format(variable)
|
|
178
|
+
)
|
|
179
|
+
continue
|
|
180
|
+
|
|
181
|
+
# Add series to output file
|
|
182
|
+
self.__timeseries_export.set(
|
|
183
|
+
variable, values, unit=self.__timeseries_import.get_unit(variable)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# Write output file to disk
|
|
187
|
+
self.__timeseries_export.write()
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def timeseries_import(self):
|
|
191
|
+
"""
|
|
192
|
+
:class:`pi.Timeseries` object containing the input data.
|
|
193
|
+
"""
|
|
194
|
+
return self.__timeseries_import
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def timeseries_import_times(self):
|
|
198
|
+
"""
|
|
199
|
+
List of time stamps for which input data is specified.
|
|
200
|
+
|
|
201
|
+
The time stamps are in seconds since t0, and may be negative.
|
|
202
|
+
"""
|
|
203
|
+
return self.io.times_sec
|
|
204
|
+
|
|
205
|
+
@property
|
|
206
|
+
def timeseries_export(self):
|
|
207
|
+
"""
|
|
208
|
+
:class:`pi.Timeseries` object for holding the output data.
|
|
209
|
+
"""
|
|
210
|
+
return self.__timeseries_export
|
|
211
|
+
|
|
212
|
+
def set_timeseries(self, variable, values, output=True, check_consistency=True, unit=None):
|
|
213
|
+
if check_consistency:
|
|
214
|
+
if len(self.times()) != len(values):
|
|
215
|
+
raise ValueError(
|
|
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
|
+
)
|
|
222
|
+
|
|
223
|
+
if unit is None:
|
|
224
|
+
unit = self.__timeseries_import.get_unit(variable)
|
|
225
|
+
|
|
226
|
+
if output:
|
|
227
|
+
try:
|
|
228
|
+
self.__data_config.pi_variable_ids(variable)
|
|
229
|
+
except KeyError:
|
|
230
|
+
logger.debug(
|
|
231
|
+
"PIMixin: variable {} has no mapping defined in rtcDataConfig "
|
|
232
|
+
"so cannot be added to the output file.".format(variable)
|
|
233
|
+
)
|
|
234
|
+
else:
|
|
235
|
+
self.__timeseries_export.set(variable, values, unit=unit)
|
|
236
|
+
|
|
237
|
+
self.__timeseries_import.set(variable, values, unit=unit)
|
|
238
|
+
self.io.set_timeseries(variable, self.io.datetimes, values)
|
|
239
|
+
|
|
240
|
+
def get_timeseries(self, variable):
|
|
241
|
+
_, values = self.io.get_timeseries(variable)
|
|
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(self, "_PIMixin__timeseries_import"), (
|
|
252
|
+
"set_unit can only be called after read() in pre() has finished."
|
|
253
|
+
)
|
|
254
|
+
self.__timeseries_import.set_unit(variable, unit, 0)
|
|
255
|
+
self.__timeseries_export.set_unit(variable, unit, 0)
|