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.

Files changed (47) hide show
  1. {rtc_tools-2.5.2rc3.dist-info → rtc_tools-2.6.0.dist-info}/METADATA +7 -7
  2. rtc_tools-2.6.0.dist-info/RECORD +50 -0
  3. {rtc_tools-2.5.2rc3.dist-info → rtc_tools-2.6.0.dist-info}/WHEEL +1 -1
  4. rtctools/__init__.py +2 -1
  5. rtctools/_internal/alias_tools.py +12 -10
  6. rtctools/_internal/caching.py +5 -3
  7. rtctools/_internal/casadi_helpers.py +11 -32
  8. rtctools/_internal/debug_check_helpers.py +1 -1
  9. rtctools/_version.py +3 -3
  10. rtctools/data/__init__.py +2 -2
  11. rtctools/data/csv.py +54 -33
  12. rtctools/data/interpolation/bspline.py +3 -3
  13. rtctools/data/interpolation/bspline1d.py +42 -29
  14. rtctools/data/interpolation/bspline2d.py +10 -4
  15. rtctools/data/netcdf.py +137 -93
  16. rtctools/data/pi.py +304 -210
  17. rtctools/data/rtc.py +64 -53
  18. rtctools/data/storage.py +91 -51
  19. rtctools/optimization/collocated_integrated_optimization_problem.py +1244 -696
  20. rtctools/optimization/control_tree_mixin.py +68 -66
  21. rtctools/optimization/csv_lookup_table_mixin.py +107 -74
  22. rtctools/optimization/csv_mixin.py +83 -52
  23. rtctools/optimization/goal_programming_mixin.py +239 -148
  24. rtctools/optimization/goal_programming_mixin_base.py +204 -111
  25. rtctools/optimization/homotopy_mixin.py +36 -27
  26. rtctools/optimization/initial_state_estimation_mixin.py +8 -8
  27. rtctools/optimization/io_mixin.py +48 -43
  28. rtctools/optimization/linearization_mixin.py +3 -1
  29. rtctools/optimization/linearized_order_goal_programming_mixin.py +57 -28
  30. rtctools/optimization/min_abs_goal_programming_mixin.py +72 -29
  31. rtctools/optimization/modelica_mixin.py +135 -81
  32. rtctools/optimization/netcdf_mixin.py +32 -18
  33. rtctools/optimization/optimization_problem.py +181 -127
  34. rtctools/optimization/pi_mixin.py +68 -36
  35. rtctools/optimization/planning_mixin.py +19 -0
  36. rtctools/optimization/single_pass_goal_programming_mixin.py +159 -112
  37. rtctools/optimization/timeseries.py +4 -6
  38. rtctools/rtctoolsapp.py +18 -18
  39. rtctools/simulation/csv_mixin.py +37 -30
  40. rtctools/simulation/io_mixin.py +9 -5
  41. rtctools/simulation/pi_mixin.py +62 -32
  42. rtctools/simulation/simulation_problem.py +471 -180
  43. rtctools/util.py +84 -56
  44. rtc_tools-2.5.2rc3.dist-info/RECORD +0 -49
  45. {rtc_tools-2.5.2rc3.dist-info → rtc_tools-2.6.0.dist-info}/COPYING.LESSER +0 -0
  46. {rtc_tools-2.5.2rc3.dist-info → rtc_tools-2.6.0.dist-info}/entry_points.txt +0 -0
  47. {rtc_tools-2.5.2rc3.dist-info → rtc_tools-2.6.0.dist-info}/top_level.txt +0 -0
@@ -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`` subfolder.
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
- 'CSVMixin: Initial state file {} contains more than one row of data. '
49
- 'Please remove the data row(s) that do not describe the initial '
50
- 'state.'.format(os.path.join(self._input_folder, 'initial_state.csv')))
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 + '.csv'),
55
- delimiter=self.csv_delimiter, with_time=True)
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
- self.__timeseries_times,
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, 'parameters.csv'),
71
- delimiter=self.csv_delimiter)
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, 'initial_state.csv'),
81
- delimiter=self.csv_delimiter)
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
- 'CSVMixin: Entry {} in initial_state.csv conflicts with '
96
- 'timeseries_import.csv'.format(collision))
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
- 'CSVMixin: Expecting equidistant timeseries, the time step '
115
- 'towards {} is not the same as the time step(s) before. '
116
- 'Set equidistant=False if this is intended.'.format(
117
- times[i + 1]))
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 = ['time'] + sorted(set(self._io_output_variables))
127
- formats = ['O'] + (len(names) - 1) * ['f8']
128
- dtype = {'names': names, 'formats': formats}
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['time'] = self.io.sec_to_datetime(times, self.io.reference_datetime)
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 + '.csv')
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("CSVMixin: In initial_state.csv, {} is not an input or state variable.".format(variable))
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
@@ -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 or add methods
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 methods
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("IOMixin: Found bad value {} at index [{}] in timeseries aliased to input {}"
106
- .format(value, t_idx, variable))
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
@@ -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``, and``rtcParameterConfig.xml``
20
- are read from the ``input`` subfolder. ``rtcDataConfig.xml`` maps
21
- tuples of FEWS identifiers, including location and parameter ID, to RTC-Tools time series identifiers.
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`` subfolder.
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: Check if duplicate parameters are read. Default is ``True``.
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 = ['rtcParameterConfig']
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(pi.ParameterConfig(
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(pi_parameter_config_basename, self._input_folder))
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, self._input_folder, self.timeseries_import_basename,
81
- binary=self.pi_binary_timeseries, pi_validate_times=self.pi_validate_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
+ )
82
93
  except FileNotFoundError:
83
- raise FileNotFoundError('PIMixin: {}.xml not found in {}'.format(
84
- self.timeseries_import_basename, self._input_folder))
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, self._output_folder, self.timeseries_export_basename,
88
- binary=self.pi_binary_timeseries, pi_validate_times=False, make_new_file=True)
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
- 'PIMixin: Expecting equidistant timeseries, the time step '
107
- 'towards {} is not the same as the time step(s) before. Set '
108
- 'unit to nonequidistant if this is intended.'.format(
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('PIMixin: Timeseries {} replaced another aliased timeseries.'.format(variable))
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 = [self.io.reference_datetime + timedelta(seconds=s) for s in 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
- 'PIMixin: variable {} has no mapping defined in rtcDataConfig '
153
- 'so cannot be added to the output file.'.format(variable))
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(variable, values, unit=self.__timeseries_import.get_unit(variable))
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
- 'PIMixin: Trying to set/append values {} with a different '
190
- 'length than the forecast length. Please make sure the '
191
- 'values cover forecastDate through endDate with timestep {}.'.format(
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
- 'PIMixin: variable {} has no mapping defined in rtcDataConfig '
203
- 'so cannot be added to the output file.'.format(variable))
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