rtc-tools 2.7.0a1__py3-none-any.whl → 2.7.0a3__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rtc-tools
3
- Version: 2.7.0a1
3
+ Version: 2.7.0a3
4
4
  Summary: Toolbox for control and optimization of water systems.
5
5
  Home-page: https://oss.deltares.nl/web/rtc-tools/home
6
6
  Author: Deltares
@@ -30,7 +30,7 @@ Requires-Dist: casadi ==3.6.*,>=3.6.3
30
30
  Requires-Dist: numpy <1.26,>=1.16.0
31
31
  Requires-Dist: scipy <1.11,>=1.0.0
32
32
  Requires-Dist: pymoca ==0.9.*,>=0.9.1
33
- Requires-Dist: rtc-tools-channel-flow >=1.1.0
33
+ Requires-Dist: rtc-tools-channel-flow >=1.2.0
34
34
  Requires-Dist: defusedxml >=0.7.0
35
35
  Provides-Extra: all
36
36
  Requires-Dist: netCDF4 ; extra == 'all'
@@ -1,5 +1,5 @@
1
1
  rtctools/__init__.py,sha256=91hvS2-ryd2Pvw0COpsUzTwJwSnTZ035REiej-1hNI4,107
2
- rtctools/_version.py,sha256=My52FWqz9UB3_PwxQjfVCPUkHjej_S48gNc1rsNpaKc,499
2
+ rtctools/_version.py,sha256=NDNDfYdfbfnXYIbOXMs5aKbrACG7Okc9i2sbnNx2dZU,499
3
3
  rtctools/rtctoolsapp.py,sha256=UnkuiJhv0crEEVs8H6PYvMuc2y_q6V_xLuyKEgXj9GM,4200
4
4
  rtctools/util.py,sha256=PaeKfDUA174ODZbY5fZjCTf-F-TdhW7yEuP189Ro190,9075
5
5
  rtctools/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -10,7 +10,7 @@ rtctools/_internal/debug_check_helpers.py,sha256=UgQTEPw4PyR7MbYLewSSWQqTwQj7xr5
10
10
  rtctools/data/__init__.py,sha256=EllgSmCdrlvQZSd1VilvjPaeYJGhY9ErPiQtedmuFoA,157
11
11
  rtctools/data/csv.py,sha256=iYOEED3AaNxt4ews_aAkHfl9Tq9a-9vjxvYwjVR_lQE,5231
12
12
  rtctools/data/netcdf.py,sha256=xpk4Ggl7gItNG6lO7p3OJPR-elK8_CiCtxUI7cX0gwk,19109
13
- rtctools/data/pi.py,sha256=Ni1hBDdhQdcWYO-NUPhKA1WJdzbSXjZc5w5xauOBcJM,45437
13
+ rtctools/data/pi.py,sha256=VZNLtaYTdyXFVbM6rLM8BKMnyRs-H3NwKz955Mp-KTc,46988
14
14
  rtctools/data/rtc.py,sha256=1yGJZGq2Z36MYLiLuZaHnxupL4mgw-Wuu54PAG05kcM,9077
15
15
  rtctools/data/storage.py,sha256=67J4BRTl0AMEzlKNZ8Xdpy_4cGtwx8Lo_tL2n0G4S9w,13206
16
16
  rtctools/data/interpolation/__init__.py,sha256=GBubCIT5mFoSTV-lOk7cpwvZekNMEe5bvqSQJ9HE34M,73
@@ -41,10 +41,10 @@ rtctools/simulation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
41
41
  rtctools/simulation/csv_mixin.py,sha256=rGDUFPsqGHmF0_dWdXeWzWzMpkPmwCNweTBVrwSh31g,6704
42
42
  rtctools/simulation/io_mixin.py,sha256=SJasNGI--OQ9Y-Z61oeeaGCxSrNddYz4AOVfJYbmf74,6209
43
43
  rtctools/simulation/pi_mixin.py,sha256=vcizArZsOn7tGlSFCsUmkjmRFA1A1kMNqUllLlkVc9Y,9831
44
- rtctools/simulation/simulation_problem.py,sha256=wGRIOm4AHAy75R7cbqeA13-Qy1ED3RY3Yc5Hvtmv6PY,48541
45
- rtc_tools-2.7.0a1.dist-info/COPYING.LESSER,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
46
- rtc_tools-2.7.0a1.dist-info/METADATA,sha256=p33wEk1F8t8jCHyV1bUe5R4JcedANOtp-9NsTH1mWBc,1494
47
- rtc_tools-2.7.0a1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
48
- rtc_tools-2.7.0a1.dist-info/entry_points.txt,sha256=-x622IB_l1duw2M6t6syfQ4yzOiQTp0IZxKGcYRgWgk,151
49
- rtc_tools-2.7.0a1.dist-info/top_level.txt,sha256=pnBrb58PFPd1kp1dqa-JHU7R55h3alDNJIJnF3Jf9Dw,9
50
- rtc_tools-2.7.0a1.dist-info/RECORD,,
44
+ rtctools/simulation/simulation_problem.py,sha256=8k3MjznzvFIut04NAiu7UAhfndAvNTkQFsO9vbmNd-0,49567
45
+ rtc_tools-2.7.0a3.dist-info/COPYING.LESSER,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
46
+ rtc_tools-2.7.0a3.dist-info/METADATA,sha256=o9lVOLg5xdv9pe2GRSjdPr0_6ubcI7SvqPc6jOU9gRc,1494
47
+ rtc_tools-2.7.0a3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
48
+ rtc_tools-2.7.0a3.dist-info/entry_points.txt,sha256=-x622IB_l1duw2M6t6syfQ4yzOiQTp0IZxKGcYRgWgk,151
49
+ rtc_tools-2.7.0a3.dist-info/top_level.txt,sha256=pnBrb58PFPd1kp1dqa-JHU7R55h3alDNJIJnF3Jf9Dw,9
50
+ rtc_tools-2.7.0a3.dist-info/RECORD,,
rtctools/_version.py CHANGED
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-04-25T06:42:12+0000",
11
+ "date": "2024-07-23T15:26:53+0200",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "e3d59506cabe8df7e05acd089ccf574e106a577f",
15
- "version": "2.7.0a1"
14
+ "full-revisionid": "4657ff3b5634df60af117b022fca4e968b88feb7",
15
+ "version": "2.7.0a3"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
rtctools/data/pi.py CHANGED
@@ -369,8 +369,6 @@ class Timeseries:
369
369
  self.__folder = folder
370
370
  self.__basename = basename
371
371
 
372
- self.__path_xml = os.path.join(self.__folder, basename + ".xml")
373
-
374
372
  self.__internal_dtype = np.float64
375
373
  self.__pi_dtype = np.float32
376
374
 
@@ -378,7 +376,7 @@ class Timeseries:
378
376
  if self.make_new_file:
379
377
  self.__reset_xml_tree()
380
378
  else:
381
- self.__tree = DefusedElementTree.parse(self.__path_xml)
379
+ self.__tree = DefusedElementTree.parse(self.path)
382
380
  self.__xml_root = self.__tree.getroot()
383
381
 
384
382
  self.__values = [{}]
@@ -801,13 +799,20 @@ class Timeseries:
801
799
  # Add series to xml
802
800
  self.__xml_root.append(series)
803
801
 
804
- def write(self):
802
+ def write(self, output_folder=None, output_filename=None) -> None:
805
803
  """
806
804
  Writes the time series data to disk.
805
+
806
+ :param output_folder: The folder in which the output file is located.
807
+ If None, the original folder is used.
808
+ :param output_filename: The name of the output file without extension.
809
+ If None, the original filename is used.
807
810
  """
811
+ xml_path = self.output_path(output_folder, output_filename)
812
+ binary_path = self.output_binary_path(output_folder, output_filename)
808
813
 
809
814
  if self.__binary:
810
- f = io.open(self.binary_path, "wb")
815
+ f = io.open(binary_path, "wb")
811
816
 
812
817
  if self.make_new_file:
813
818
  # Force reinitialization in case write() is called more than once
@@ -911,7 +916,7 @@ class Timeseries:
911
916
  f.close()
912
917
 
913
918
  self.format_xml_data()
914
- self.__tree.write(self.__path_xml)
919
+ self.__tree.write(xml_path)
915
920
 
916
921
  def format_xml_data(self):
917
922
  """
@@ -1170,16 +1175,45 @@ class Timeseries:
1170
1175
  self.__end_datetime = end_datetime
1171
1176
 
1172
1177
  @property
1173
- def path(self):
1174
- return self.__path_xml
1178
+ def path(self) -> str:
1179
+ """
1180
+ The path to the original xml file.
1181
+ """
1182
+ return os.path.join(self.__folder, self.__basename + ".xml")
1175
1183
 
1176
1184
  @property
1177
- def binary_path(self):
1185
+ def binary_path(self) -> str:
1178
1186
  """
1179
- The path for the binary data .bin file.
1187
+ The path to the original binary data .bin file.
1180
1188
  """
1181
1189
  return os.path.join(self.__folder, self.__basename + ".bin")
1182
1190
 
1191
+ def _output_path_without_extension(self, output_folder=None, output_filename=None) -> str:
1192
+ """
1193
+ Get the output path without file extension.
1194
+ """
1195
+ if output_folder is None:
1196
+ output_folder = self.__folder
1197
+ if output_filename is None:
1198
+ output_filename = self.__basename
1199
+ return os.path.join(output_folder, output_filename)
1200
+
1201
+ def output_path(self, output_folder=None, output_filename=None) -> str:
1202
+ """
1203
+ Get the path to the output xml file.
1204
+
1205
+ The optional arguments are the same as in :py:method:`write`.
1206
+ """
1207
+ return self._output_path_without_extension(output_folder, output_filename) + ".xml"
1208
+
1209
+ def output_binary_path(self, output_folder=None, output_filename=None) -> str:
1210
+ """
1211
+ Get the path to the output binary file.
1212
+
1213
+ The optional arguments are the same as in :py:method:`write`.
1214
+ """
1215
+ return self._output_path_without_extension(output_folder, output_filename) + ".bin"
1216
+
1183
1217
  def items(self, ensemble_member=0):
1184
1218
  """
1185
1219
  Returns an iterator over all timeseries IDs and value arrays for the given
@@ -419,7 +419,6 @@ class SimulationProblem(DataStoreAccessor):
419
419
  start_values = {}
420
420
 
421
421
  # Attempt to cast var.start to python type
422
- start_value_is_symbolic = False
423
422
  mx_start = ca.MX(var.start)
424
423
  if mx_start.is_constant():
425
424
  # cast var.start to python type
@@ -427,7 +426,6 @@ class SimulationProblem(DataStoreAccessor):
427
426
  if start_value_pymoca is not None and start_value_pymoca != 0:
428
427
  start_values["modelica"] = start_value_pymoca
429
428
  else:
430
- start_value_is_symbolic = True
431
429
  start_values["modelica"] = mx_start
432
430
 
433
431
  if not var.fixed:
@@ -479,8 +477,8 @@ class SimulationProblem(DataStoreAccessor):
479
477
  input_source = "modelica"
480
478
  source_description = "modelica file or default value"
481
479
  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
480
+ start_is_numeric = start_val is not None and not isinstance(start_val, ca.MX)
481
+ numeric_start_val = start_val if start_is_numeric else 0.0
484
482
  if len(start_values) > 1:
485
483
  logger.warning(
486
484
  "Initialize: Multiple initial values for {} are provided: {}.".format(
@@ -500,10 +498,12 @@ class SimulationProblem(DataStoreAccessor):
500
498
 
501
499
  # Add a residual for the difference between the state and its starting expression
502
500
  start_expr = start_val
501
+ min_is_symbolic = isinstance(var.min, ca.MX)
502
+ max_is_symbolic = isinstance(var.max, ca.MX)
503
503
  if var.fixed:
504
504
  # Set bounds to be equal to each other, such that IPOPT can
505
505
  # turn the decision variable into a parameter.
506
- if var.min != -np.inf or var.max != np.inf:
506
+ if min_is_symbolic or max_is_symbolic or var.min != -np.inf or var.max != np.inf:
507
507
  logger.info(
508
508
  "Initialize: bounds of {} will be overwritten".format(var_name)
509
509
  + " by the start value given by {}.".format(source_description)
@@ -515,7 +515,7 @@ class SimulationProblem(DataStoreAccessor):
515
515
  minimized_residuals.append((var.symbol - start_expr) / var_nominal)
516
516
 
517
517
  # Check that the start_value is in between the variable bounds.
518
- if start_val is not None and is_numeric:
518
+ if start_is_numeric and not min_is_symbolic and not max_is_symbolic:
519
519
  if not (var.min <= start_val and start_val <= var.max):
520
520
  logger.warning(
521
521
  "Initialize: start value {} = {}".format(var_name, start_val)
@@ -838,6 +838,8 @@ class SimulationProblem(DataStoreAccessor):
838
838
  self.set_var("time", self.get_current_time() + dt)
839
839
 
840
840
  # take a step
841
+ if np.isnan(self.__state_vector).any():
842
+ logger.error("Found a nan in the state vector (before making the step)")
841
843
  guess = self.__state_vector[: self.__n_states]
842
844
  if len(self.__mx["parameters"]) > 0:
843
845
  next_state = self.__do_step(
@@ -845,6 +847,23 @@ class SimulationProblem(DataStoreAccessor):
845
847
  )
846
848
  else:
847
849
  next_state = self.__do_step(guess, dt, self.__state_vector)
850
+
851
+ try:
852
+ if np.isnan(next_state).any():
853
+ index_to_name = {index[0]: name for name, index in self.__indices.items()}
854
+ named_next_state = {
855
+ index_to_name[i]: float(next_state[i]) for i in range(0, next_state.shape[0])
856
+ }
857
+ variables_with_nan = [
858
+ name for name, value in named_next_state.items() if np.isnan(value)
859
+ ]
860
+ if variables_with_nan:
861
+ logger.error(
862
+ f"Found nan(s) in the next_state vector for:\n\t {variables_with_nan}"
863
+ )
864
+ except (KeyError, IndexError, TypeError):
865
+ logger.warning("Something went wrong while checking for nans in the next_state vector")
866
+
848
867
  # Check convergence of rootfinder
849
868
  rootfinder_stats = self.__do_step.stats()
850
869