rtc-tools 2.6.1__tar.gz → 2.7.0a1__tar.gz

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 (58) hide show
  1. {rtc-tools-2.6.1/src/rtc_tools.egg-info → rtc-tools-2.7.0a1}/PKG-INFO +1 -1
  2. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1/src/rtc_tools.egg-info}/PKG-INFO +1 -1
  3. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/_version.py +3 -3
  4. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/modelica_mixin.py +15 -3
  5. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/pi_mixin.py +13 -0
  6. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/simulation/pi_mixin.py +13 -0
  7. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/simulation/simulation_problem.py +91 -15
  8. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/COPYING.LESSER +0 -0
  9. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/MANIFEST.in +0 -0
  10. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/README.md +0 -0
  11. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/pyproject.toml +0 -0
  12. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/setup.cfg +0 -0
  13. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/setup.py +0 -0
  14. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtc_tools.egg-info/SOURCES.txt +0 -0
  15. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtc_tools.egg-info/dependency_links.txt +0 -0
  16. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtc_tools.egg-info/entry_points.txt +0 -0
  17. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtc_tools.egg-info/requires.txt +0 -0
  18. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtc_tools.egg-info/top_level.txt +0 -0
  19. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/__init__.py +0 -0
  20. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/_internal/__init__.py +0 -0
  21. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/_internal/alias_tools.py +0 -0
  22. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/_internal/caching.py +0 -0
  23. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/_internal/casadi_helpers.py +0 -0
  24. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/_internal/debug_check_helpers.py +0 -0
  25. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/data/__init__.py +0 -0
  26. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/data/csv.py +0 -0
  27. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/data/interpolation/__init__.py +0 -0
  28. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/data/interpolation/bspline.py +0 -0
  29. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/data/interpolation/bspline1d.py +0 -0
  30. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/data/interpolation/bspline2d.py +0 -0
  31. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/data/netcdf.py +0 -0
  32. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/data/pi.py +0 -0
  33. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/data/rtc.py +0 -0
  34. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/data/storage.py +0 -0
  35. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/__init__.py +0 -0
  36. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/collocated_integrated_optimization_problem.py +0 -0
  37. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/control_tree_mixin.py +0 -0
  38. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/csv_lookup_table_mixin.py +0 -0
  39. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/csv_mixin.py +0 -0
  40. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/goal_programming_mixin.py +0 -0
  41. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/goal_programming_mixin_base.py +0 -0
  42. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/homotopy_mixin.py +0 -0
  43. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/initial_state_estimation_mixin.py +0 -0
  44. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/io_mixin.py +0 -0
  45. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/linearization_mixin.py +0 -0
  46. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/linearized_order_goal_programming_mixin.py +0 -0
  47. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/min_abs_goal_programming_mixin.py +0 -0
  48. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/netcdf_mixin.py +0 -0
  49. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/optimization_problem.py +0 -0
  50. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/planning_mixin.py +0 -0
  51. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/single_pass_goal_programming_mixin.py +0 -0
  52. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/optimization/timeseries.py +0 -0
  53. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/rtctoolsapp.py +0 -0
  54. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/simulation/__init__.py +0 -0
  55. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/simulation/csv_mixin.py +0 -0
  56. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/simulation/io_mixin.py +0 -0
  57. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/src/rtctools/util.py +0 -0
  58. {rtc-tools-2.6.1 → rtc-tools-2.7.0a1}/versioneer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rtc-tools
3
- Version: 2.6.1
3
+ Version: 2.7.0a1
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: rtc-tools
3
- Version: 2.6.1
3
+ Version: 2.7.0a1
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
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-03-05T12:46:34+0100",
11
+ "date": "2024-04-25T06:42:12+0000",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "0a079ff7858bac8dcf56c5c6fd0b6f3ee2cdbddb",
15
- "version": "2.6.1"
14
+ "full-revisionid": "e3d59506cabe8df7e05acd089ccf574e106a577f",
15
+ "version": "2.7.0a1"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -48,9 +48,21 @@ class ModelicaMixin(OptimizationProblem):
48
48
  else:
49
49
  model_name = self.__class__.__name__
50
50
 
51
- self.__pymoca_model = pymoca.backends.casadi.api.transfer_model(
52
- kwargs["model_folder"], model_name, self.compiler_options()
53
- )
51
+ compiler_options = self.compiler_options()
52
+ logger.info(f"Loading/compiling model {model_name}.")
53
+ try:
54
+ self.__pymoca_model = pymoca.backends.casadi.api.transfer_model(
55
+ kwargs["model_folder"], model_name, compiler_options
56
+ )
57
+ except RuntimeError as error:
58
+ if compiler_options.get("cache", False):
59
+ raise error
60
+ compiler_options["cache"] = False
61
+ logger.warning(f"Loading model {model_name} using a cache file failed: {error}.")
62
+ logger.info(f"Compiling model {model_name}.")
63
+ self.__pymoca_model = pymoca.backends.casadi.api.transfer_model(
64
+ kwargs["model_folder"], model_name, compiler_options
65
+ )
54
66
 
55
67
  # Extract the CasADi MX variables used in the model
56
68
  self.__mx = {}
@@ -277,3 +277,16 @@ class PIMixin(IOMixin):
277
277
  :class:`pi.Timeseries` object for holding the output data.
278
278
  """
279
279
  return self.__timeseries_export
280
+
281
+ def set_unit(self, variable: str, unit: str):
282
+ """
283
+ Set the unit of a time series.
284
+
285
+ :param variable: Time series ID.
286
+ :param unit: Unit.
287
+ """
288
+ assert hasattr(
289
+ self, "_PIMixin__timeseries_import"
290
+ ), "set_unit can only be called after read() in pre() has finished."
291
+ self.__timeseries_import.set_unit(variable, unit, 0)
292
+ self.__timeseries_export.set_unit(variable, unit, 0)
@@ -240,3 +240,16 @@ class PIMixin(IOMixin):
240
240
  def get_timeseries(self, variable):
241
241
  _, values = self.io.get_timeseries(variable)
242
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(
252
+ self, "_PIMixin__timeseries_import"
253
+ ), "set_unit can only be called after read() in pre() has finished."
254
+ self.__timeseries_import.set_unit(variable, unit, 0)
255
+ self.__timeseries_export.set_unit(variable, unit, 0)
@@ -82,9 +82,21 @@ class SimulationProblem(DataStoreAccessor):
82
82
  model_name = self.__class__.__name__
83
83
 
84
84
  # Load model from pymoca backend
85
- self.__pymoca_model = pymoca.backends.casadi.api.transfer_model(
86
- kwargs["model_folder"], model_name, self.compiler_options()
87
- )
85
+ compiler_options = self.compiler_options()
86
+ logger.info(f"Loading/compiling model {model_name}.")
87
+ try:
88
+ self.__pymoca_model = pymoca.backends.casadi.api.transfer_model(
89
+ kwargs["model_folder"], model_name, compiler_options
90
+ )
91
+ except RuntimeError as error:
92
+ if compiler_options.get("cache", False):
93
+ raise error
94
+ compiler_options["cache"] = False
95
+ logger.warning(f"Loading model {model_name} using a cache file failed: {error}.")
96
+ logger.info(f"Compiling model {model_name}.")
97
+ self.__pymoca_model = pymoca.backends.casadi.api.transfer_model(
98
+ kwargs["model_folder"], model_name, compiler_options
99
+ )
88
100
 
89
101
  # Extract the CasADi MX variables used in the model
90
102
  self.__mx = {}
@@ -319,6 +331,17 @@ class SimulationProblem(DataStoreAccessor):
319
331
  """
320
332
  Initialize state vector with default values
321
333
 
334
+ Initial values are first read from the given Modelica files.
335
+ If an initial value equals zero or is not provided by a Modelica file,
336
+ and the variable is not marked as fixed,
337
+ then the initial value is tried to be set with the initial_state method.
338
+ When using CSVMixin, this method by default looks for initial values
339
+ in an initial_state.csv file.
340
+ Furthermore, if a variable is not marked as fixed
341
+ and no initial value is given by the initial_state method,
342
+ the initial value can be overwritten using the seed method.
343
+ When a variable is marked as fixed, the initial value is only read from the Modelica file.
344
+
322
345
  :param config_file: Path to an initialization file.
323
346
  """
324
347
  if config_file:
@@ -393,29 +416,35 @@ class SimulationProblem(DataStoreAccessor):
393
416
  for var in itertools.chain(self.__pymoca_model.states, self.__pymoca_model.alg_states):
394
417
  var_name = var.symbol.name()
395
418
  var_nominal = self.get_variable_nominal(var_name)
419
+ start_values = {}
396
420
 
397
421
  # Attempt to cast var.start to python type
422
+ start_value_is_symbolic = False
398
423
  mx_start = ca.MX(var.start)
399
424
  if mx_start.is_constant():
400
425
  # cast var.start to python type
401
- start_val = var.python_type(mx_start.to_DM())
426
+ start_value_pymoca = var.python_type(mx_start.to_DM())
427
+ if start_value_pymoca is not None and start_value_pymoca != 0:
428
+ start_values["modelica"] = start_value_pymoca
402
429
  else:
403
- # var.start is a symbolic expression with unknown value
404
- start_val = None
430
+ start_value_is_symbolic = True
431
+ start_values["modelica"] = mx_start
405
432
 
406
- if start_val == 0.0 and not var.fixed:
433
+ if not var.fixed:
407
434
  # To make initialization easier, we allow setting initial states by providing
408
435
  # timeseries with names that match a symbol in the model. We only check for this
409
436
  # matching if the start and fixed attributes were left as default
410
437
  try:
411
- start_val = self.initial_state()[var_name]
438
+ start_values["initial_state"] = self.initial_state()[var_name]
412
439
  except KeyError:
413
440
  pass
414
441
  else:
415
442
  # An initial state was found- add it to the constrained residuals
416
443
  logger.debug(
417
444
  "Initialize: Added {} = {} to initial equations "
418
- "(found matching timeseries).".format(var_name, start_val)
445
+ "(found matching timeseries).".format(
446
+ var_name, start_values["initial_state"]
447
+ )
419
448
  )
420
449
  # Set var to be fixed
421
450
  var.fixed = True
@@ -425,36 +454,75 @@ class SimulationProblem(DataStoreAccessor):
425
454
  # timeseries with names that match a symbol in the model. We only check for this
426
455
  # matching if the start and fixed attributes were left as default
427
456
  try:
428
- start_val = self.seed()[var_name]
457
+ start_values["seed"] = self.seed()[var_name]
429
458
  except KeyError:
430
459
  pass
431
460
  else:
432
461
  # An initial state was found- add it to the constrained residuals
433
462
  logger.debug(
434
463
  "Initialize: Added {} = {} as initial guess "
435
- "(found matching timeseries).".format(var_name, start_val)
464
+ "(found matching timeseries).".format(var_name, start_values["seed"])
465
+ )
466
+
467
+ # Set the start value based on the different inputs.
468
+ if "seed" in start_values:
469
+ input_source = "seed"
470
+ source_description = "seed method"
471
+ elif "modelica" in start_values:
472
+ input_source = "modelica"
473
+ source_description = "modelica file"
474
+ elif "initial_state" in start_values:
475
+ input_source = "initial_state"
476
+ source_description = "initial_state method (typically reads initial_state.csv)"
477
+ else:
478
+ start_values["modelica"] = start_value_pymoca
479
+ input_source = "modelica"
480
+ source_description = "modelica file or default value"
481
+ 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
484
+ if len(start_values) > 1:
485
+ logger.warning(
486
+ "Initialize: Multiple initial values for {} are provided: {}.".format(
487
+ var_name, start_values
436
488
  )
489
+ + " Value from {} will be used to continue.".format(source_description)
490
+ )
437
491
 
438
492
  # Attempt to set start_val in the state vector. Default to zero if unknown.
439
493
  try:
440
- self.set_var(var_name, start_val if start_val is not None else 0.0)
494
+ self.set_var(var_name, numeric_start_val)
441
495
  except KeyError:
442
496
  logger.warning(
443
497
  "Initialize: {} not found in state vector. "
444
- "Initial value of {} not set.".format(var_name, start_val)
498
+ "Initial value of {} not set.".format(var_name, numeric_start_val)
445
499
  )
446
500
 
447
501
  # Add a residual for the difference between the state and its starting expression
448
- start_expr = start_val if start_val is not None else var.start
502
+ start_expr = start_val
449
503
  if var.fixed:
450
504
  # Set bounds to be equal to each other, such that IPOPT can
451
505
  # turn the decision variable into a parameter.
506
+ if var.min != -np.inf or var.max != np.inf:
507
+ logger.info(
508
+ "Initialize: bounds of {} will be overwritten".format(var_name)
509
+ + " by the start value given by {}.".format(source_description)
510
+ )
452
511
  var.min = start_expr
453
512
  var.max = start_expr
454
513
  else:
455
514
  # minimize residual
456
515
  minimized_residuals.append((var.symbol - start_expr) / var_nominal)
457
516
 
517
+ # Check that the start_value is in between the variable bounds.
518
+ if start_val is not None and is_numeric:
519
+ if not (var.min <= start_val and start_val <= var.max):
520
+ logger.warning(
521
+ "Initialize: start value {} = {}".format(var_name, start_val)
522
+ + " is not in between bounds {} and {}".format(var.min, var.max)
523
+ + " and will be adjusted."
524
+ )
525
+
458
526
  # Default start var for ders is zero
459
527
  for der_var in self.__mx["derivatives"]:
460
528
  self.set_var(der_var.name(), 0.0)
@@ -610,7 +678,15 @@ class SimulationProblem(DataStoreAccessor):
610
678
  # If unsuccessful, stop.
611
679
  return_status = solver.stats()["return_status"]
612
680
  if return_status not in {"Solve_Succeeded", "Solved_To_Acceptable_Level"}:
613
- raise Exception('Initialization Failed with return status "{}"'.format(return_status))
681
+ if return_status == "Infeasible_Problem_Detected":
682
+ message = (
683
+ "Initialization Failed with return status: {}. ".format(return_status)
684
+ + "This means no initial state could be found "
685
+ + "that satisfies all equations and constraints."
686
+ )
687
+ else:
688
+ message = "Initialization Failed with return status: {}. ".format(return_status)
689
+ raise Exception(message)
614
690
 
615
691
  # Update state vector with initial conditions
616
692
  self.__state_vector[: self.__n_states] = initial_state["x"][: self.__n_states].T
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes