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.
Files changed (50) hide show
  1. rtc_tools-2.7.3.dist-info/METADATA +53 -0
  2. rtc_tools-2.7.3.dist-info/RECORD +50 -0
  3. rtc_tools-2.7.3.dist-info/WHEEL +5 -0
  4. rtc_tools-2.7.3.dist-info/entry_points.txt +3 -0
  5. rtc_tools-2.7.3.dist-info/licenses/COPYING.LESSER +165 -0
  6. rtc_tools-2.7.3.dist-info/top_level.txt +1 -0
  7. rtctools/__init__.py +5 -0
  8. rtctools/_internal/__init__.py +0 -0
  9. rtctools/_internal/alias_tools.py +188 -0
  10. rtctools/_internal/caching.py +25 -0
  11. rtctools/_internal/casadi_helpers.py +99 -0
  12. rtctools/_internal/debug_check_helpers.py +41 -0
  13. rtctools/_version.py +21 -0
  14. rtctools/data/__init__.py +4 -0
  15. rtctools/data/csv.py +150 -0
  16. rtctools/data/interpolation/__init__.py +3 -0
  17. rtctools/data/interpolation/bspline.py +31 -0
  18. rtctools/data/interpolation/bspline1d.py +169 -0
  19. rtctools/data/interpolation/bspline2d.py +54 -0
  20. rtctools/data/netcdf.py +467 -0
  21. rtctools/data/pi.py +1236 -0
  22. rtctools/data/rtc.py +228 -0
  23. rtctools/data/storage.py +343 -0
  24. rtctools/optimization/__init__.py +0 -0
  25. rtctools/optimization/collocated_integrated_optimization_problem.py +3208 -0
  26. rtctools/optimization/control_tree_mixin.py +221 -0
  27. rtctools/optimization/csv_lookup_table_mixin.py +462 -0
  28. rtctools/optimization/csv_mixin.py +300 -0
  29. rtctools/optimization/goal_programming_mixin.py +769 -0
  30. rtctools/optimization/goal_programming_mixin_base.py +1094 -0
  31. rtctools/optimization/homotopy_mixin.py +165 -0
  32. rtctools/optimization/initial_state_estimation_mixin.py +89 -0
  33. rtctools/optimization/io_mixin.py +320 -0
  34. rtctools/optimization/linearization_mixin.py +33 -0
  35. rtctools/optimization/linearized_order_goal_programming_mixin.py +235 -0
  36. rtctools/optimization/min_abs_goal_programming_mixin.py +385 -0
  37. rtctools/optimization/modelica_mixin.py +482 -0
  38. rtctools/optimization/netcdf_mixin.py +177 -0
  39. rtctools/optimization/optimization_problem.py +1302 -0
  40. rtctools/optimization/pi_mixin.py +292 -0
  41. rtctools/optimization/planning_mixin.py +19 -0
  42. rtctools/optimization/single_pass_goal_programming_mixin.py +676 -0
  43. rtctools/optimization/timeseries.py +56 -0
  44. rtctools/rtctoolsapp.py +131 -0
  45. rtctools/simulation/__init__.py +0 -0
  46. rtctools/simulation/csv_mixin.py +171 -0
  47. rtctools/simulation/io_mixin.py +195 -0
  48. rtctools/simulation/pi_mixin.py +255 -0
  49. rtctools/simulation/simulation_problem.py +1293 -0
  50. 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)