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
@@ -3,12 +3,17 @@ import itertools
3
3
  from typing import List
4
4
 
5
5
  import casadi as ca
6
-
7
6
  import numpy as np
8
7
 
9
8
  from .goal_programming_mixin import GoalProgrammingMixin
10
- from .goal_programming_mixin_base import Goal, StateGoal, _GoalProgrammingMixinBase
11
- from .goal_programming_mixin_base import _EmptyEnsembleList, _EmptyEnsembleOrderedDict, _GoalConstraint
9
+ from .goal_programming_mixin_base import (
10
+ Goal,
11
+ StateGoal,
12
+ _EmptyEnsembleList,
13
+ _EmptyEnsembleOrderedDict,
14
+ _GoalConstraint,
15
+ _GoalProgrammingMixinBase,
16
+ )
12
17
  from .single_pass_goal_programming_mixin import SinglePassGoalProgrammingMixin
13
18
  from .timeseries import Timeseries
14
19
 
@@ -20,6 +25,7 @@ class MinAbsGoal(Goal):
20
25
  class, the default order is 1 as absolute minimization is typically
21
26
  desired for fully linear problems.
22
27
  """
28
+
23
29
  order = 1
24
30
 
25
31
 
@@ -28,7 +34,6 @@ class MinAbsStateGoal(StateGoal, MinAbsGoal):
28
34
 
29
35
 
30
36
  class _ConvertedMinAbsGoal(Goal):
31
-
32
37
  order = 1
33
38
 
34
39
  def __init__(self, abs_variable, is_path_goal, orig_goal):
@@ -83,7 +88,7 @@ class MinAbsGoalProgrammingMixin(_GoalProgrammingMixinBase):
83
88
 
84
89
  def bounds(self):
85
90
  bounds = super().bounds()
86
- for abs_var in (self.__problem_vars + self.__problem_path_vars):
91
+ for abs_var in self.__problem_vars + self.__problem_path_vars:
87
92
  bounds[abs_var.name()] = (0.0, np.inf)
88
93
  return bounds
89
94
 
@@ -122,20 +127,29 @@ class MinAbsGoalProgrammingMixin(_GoalProgrammingMixinBase):
122
127
 
123
128
  for goal in goals:
124
129
  if not isinstance(goal, MinAbsGoal):
125
- raise Exception("Absolute goal not an instance of MinAbsGoal for goal {}".format(goal))
130
+ raise Exception(
131
+ "Absolute goal not an instance of MinAbsGoal for goal {}".format(goal)
132
+ )
126
133
 
127
134
  if goal.function_range != (np.nan, np.nan):
128
- raise Exception("Absolute goal function is only allowed for minimization for goal {}".format(goal))
135
+ raise Exception(
136
+ "Absolute goal function is only allowed for minimization for goal {}".format(
137
+ goal
138
+ )
139
+ )
129
140
 
130
141
  if goal.order != 1:
131
- raise Exception("Absolute goal function is only allowed for order = 1 for goal {}".format(goal))
142
+ raise Exception(
143
+ "Absolute goal function is only allowed for order = 1 for goal {}".format(goal)
144
+ )
132
145
 
133
146
  if goal.weight <= 0:
134
- raise Exception("Absolute goal function is only allowed for weight > 0 for goal {}".format(goal))
147
+ raise Exception(
148
+ "Absolute goal function is only allowed for weight > 0 for goal {}".format(goal)
149
+ )
135
150
 
136
151
  @staticmethod
137
152
  def __convert_goals(goals, sym_index, ensemble_size, is_path_goal):
138
-
139
153
  # Replace absolute minimization goals with a new goal, and some
140
154
  # additional hard constraints.
141
155
  constraints = [[] for ensemble_member in range(ensemble_size)]
@@ -160,15 +174,24 @@ class MinAbsGoalProgrammingMixin(_GoalProgrammingMixinBase):
160
174
  # original goal function, such that it corresponds to its absolute
161
175
  # value when minimizing.
162
176
  for ensemble_member in range(ensemble_size):
163
- def _constraint_func(problem, sign, abs_variable=abs_variable,
164
- ensemble_member=ensemble_member, goal=goal,
165
- is_path_goal=is_path_goal):
177
+
178
+ def _constraint_func(
179
+ problem,
180
+ sign,
181
+ abs_variable=abs_variable,
182
+ ensemble_member=ensemble_member,
183
+ goal=goal,
184
+ is_path_goal=is_path_goal,
185
+ ):
166
186
  if is_path_goal:
167
187
  abs_variable = problem.variable(abs_variable.name())
168
188
  else:
169
189
  abs_variable = problem.extra_variable(abs_variable.name(), ensemble_member)
170
190
 
171
- return abs_variable + sign * goal.function(problem, ensemble_member) / goal.function_nominal
191
+ return (
192
+ abs_variable
193
+ + sign * goal.function(problem, ensemble_member) / goal.function_nominal
194
+ )
172
195
 
173
196
  _pos = functools.partial(_constraint_func, sign=1)
174
197
  _neg = functools.partial(_constraint_func, sign=-1)
@@ -188,16 +211,17 @@ class MinAbsGoalProgrammingMixin(_GoalProgrammingMixinBase):
188
211
  seed = [{} for ensemble_member in range(self.ensemble_size)]
189
212
 
190
213
  for goal in goals:
191
-
192
214
  assert isinstance(goal, _ConvertedMinAbsGoal)
193
215
 
194
216
  for ensemble_member in range(self.ensemble_size):
195
217
  if is_path_goal:
196
- expr = self.map_path_expression(goal.orig_goal.function(self, ensemble_member), ensemble_member)
218
+ expr = self.map_path_expression(
219
+ goal.orig_goal.function(self, ensemble_member), ensemble_member
220
+ )
197
221
  else:
198
222
  expr = goal.orig_goal.function(self, ensemble_member)
199
223
 
200
- function = ca.Function('f', [self.solver_input], [expr])
224
+ function = ca.Function("f", [self.solver_input], [expr])
201
225
  value = np.array(function(self.solver_output))
202
226
 
203
227
  assert value.ndim == 2
@@ -244,32 +268,49 @@ class MinAbsGoalProgrammingMixin(_GoalProgrammingMixinBase):
244
268
  # We want to have consistent naming with GPMixin for our auxiliary
245
269
  # variables. We therefore need to loop over all priorities, regardless
246
270
  # of whether there are any MinAbsGoals in it or not.
247
- priorities = {int(goal.priority) for goal in itertools.chain(
248
- goals, path_goals, self.goals(), self.path_goals()) if not goal.is_empty}
271
+ priorities = {
272
+ int(goal.priority)
273
+ for goal in itertools.chain(goals, path_goals, self.goals(), self.path_goals())
274
+ if not goal.is_empty
275
+ }
249
276
 
250
277
  subproblems = []
251
278
  for priority in sorted(priorities):
252
- subproblems.append((
253
- priority,
254
- [goal for goal in goals if int(goal.priority) == priority and not goal.is_empty],
255
- [goal for goal in path_goals if int(goal.priority) == priority and not goal.is_empty]))
279
+ subproblems.append(
280
+ (
281
+ priority,
282
+ [
283
+ goal
284
+ for goal in goals
285
+ if int(goal.priority) == priority and not goal.is_empty
286
+ ],
287
+ [
288
+ goal
289
+ for goal in path_goals
290
+ if int(goal.priority) == priority and not goal.is_empty
291
+ ],
292
+ )
293
+ )
256
294
 
257
295
  # Rewrite absolute minimization goals.
258
296
  self.__converted_goals = []
259
297
  self.__converted_path_goals = []
260
298
 
261
299
  for i, (priority, goals, path_goals) in enumerate(subproblems):
262
-
263
- (goals,
300
+ (
301
+ goals,
264
302
  self.__subproblem_constraints[priority],
265
- self.__subproblem_vars[priority]) = self.__convert_goals(goals, i, self.ensemble_size, False)
303
+ self.__subproblem_vars[priority],
304
+ ) = self.__convert_goals(goals, i, self.ensemble_size, False)
266
305
 
267
306
  self.__converted_goals.extend(goals)
268
307
  self.__subproblem_abs_goals[priority] = goals
269
308
 
270
- (path_goals,
309
+ (
310
+ path_goals,
271
311
  self.__subproblem_path_constraints[priority],
272
- self.__subproblem_path_vars[priority]) = self.__convert_goals(path_goals, i, self.ensemble_size, True)
312
+ self.__subproblem_path_vars[priority],
313
+ ) = self.__convert_goals(path_goals, i, self.ensemble_size, True)
273
314
 
274
315
  self.__converted_path_goals.extend(path_goals)
275
316
  self.__subproblem_path_abs_goals[priority] = path_goals
@@ -307,7 +348,9 @@ class MinAbsGoalProgrammingMixin(_GoalProgrammingMixinBase):
307
348
  # priority.
308
349
  if not self.__first_run and isinstance(self, GoalProgrammingMixin):
309
350
  self.__seeds = self.__calculate_seed(self.__subproblem_abs_goals[priority], False)
310
- self.__path_seeds = self.__calculate_seed(self.__subproblem_path_abs_goals[priority], True)
351
+ self.__path_seeds = self.__calculate_seed(
352
+ self.__subproblem_path_abs_goals[priority], True
353
+ )
311
354
 
312
355
  self.__first_run = False
313
356
 
@@ -3,11 +3,8 @@ import logging
3
3
  from typing import Dict, Union
4
4
 
5
5
  import casadi as ca
6
-
7
6
  import numpy as np
8
-
9
7
  import pkg_resources
10
-
11
8
  import pymoca
12
9
  import pymoca.backends.casadi.api
13
10
 
@@ -28,7 +25,8 @@ class ModelicaMixin(OptimizationProblem):
28
25
  During preprocessing, the Modelica files located inside the ``model`` subfolder are loaded.
29
26
 
30
27
  :cvar modelica_library_folders:
31
- Folders in which any referenced Modelica libraries are to be found. Default is an empty list.
28
+ Folders in which any referenced Modelica libraries are to be found.
29
+ Default is an empty list.
32
30
  """
33
31
 
34
32
  # Folders in which the referenced Modelica libraries are found
@@ -36,35 +34,38 @@ class ModelicaMixin(OptimizationProblem):
36
34
 
37
35
  def __init__(self, **kwargs):
38
36
  # Check arguments
39
- assert ('model_folder' in kwargs)
37
+ assert "model_folder" in kwargs
40
38
 
41
39
  # Log pymoca version
42
40
  logger.debug("Using pymoca {}.".format(pymoca.__version__))
43
41
 
44
42
  # Transfer model from the Modelica .mo file to CasADi using pymoca
45
- if 'model_name' in kwargs:
46
- model_name = kwargs['model_name']
43
+ if "model_name" in kwargs:
44
+ model_name = kwargs["model_name"]
47
45
  else:
48
- if hasattr(self, 'model_name'):
46
+ if hasattr(self, "model_name"):
49
47
  model_name = self.model_name
50
48
  else:
51
49
  model_name = self.__class__.__name__
52
50
 
53
51
  self.__pymoca_model = pymoca.backends.casadi.api.transfer_model(
54
- kwargs['model_folder'], model_name, self.compiler_options())
52
+ kwargs["model_folder"], model_name, self.compiler_options()
53
+ )
55
54
 
56
55
  # Extract the CasADi MX variables used in the model
57
56
  self.__mx = {}
58
- self.__mx['time'] = [self.__pymoca_model.time]
59
- self.__mx['states'] = [v.symbol for v in self.__pymoca_model.states]
60
- self.__mx['derivatives'] = [v.symbol for v in self.__pymoca_model.der_states]
61
- self.__mx['algebraics'] = [v.symbol for v in self.__pymoca_model.alg_states]
62
- self.__mx['parameters'] = [v.symbol for v in self.__pymoca_model.parameters]
63
- self.__mx['string_parameters'] = [v.name for v in (*self.__pymoca_model.string_parameters,
64
- *self.__pymoca_model.string_constants)]
65
- self.__mx['control_inputs'] = []
66
- self.__mx['constant_inputs'] = []
67
- self.__mx['lookup_tables'] = []
57
+ self.__mx["time"] = [self.__pymoca_model.time]
58
+ self.__mx["states"] = [v.symbol for v in self.__pymoca_model.states]
59
+ self.__mx["derivatives"] = [v.symbol for v in self.__pymoca_model.der_states]
60
+ self.__mx["algebraics"] = [v.symbol for v in self.__pymoca_model.alg_states]
61
+ self.__mx["parameters"] = [v.symbol for v in self.__pymoca_model.parameters]
62
+ self.__mx["string_parameters"] = [
63
+ v.name
64
+ for v in (*self.__pymoca_model.string_parameters, *self.__pymoca_model.string_constants)
65
+ ]
66
+ self.__mx["control_inputs"] = []
67
+ self.__mx["constant_inputs"] = []
68
+ self.__mx["lookup_tables"] = []
68
69
 
69
70
  # Merge with user-specified delayed feedback
70
71
  for v in self.__pymoca_model.inputs:
@@ -72,29 +73,31 @@ class ModelicaMixin(OptimizationProblem):
72
73
  # Delayed feedback variables are local to each ensemble, and
73
74
  # therefore belong to the collection of algebraic variables,
74
75
  # rather than to the control inputs.
75
- self.__mx['algebraics'].append(v.symbol)
76
+ self.__mx["algebraics"].append(v.symbol)
76
77
  else:
77
- if v.symbol.name() in kwargs.get('lookup_tables', []):
78
- self.__mx['lookup_tables'].append(v.symbol)
78
+ if v.symbol.name() in kwargs.get("lookup_tables", []):
79
+ self.__mx["lookup_tables"].append(v.symbol)
79
80
  elif v.fixed:
80
- self.__mx['constant_inputs'].append(v.symbol)
81
+ self.__mx["constant_inputs"].append(v.symbol)
81
82
  else:
82
- self.__mx['control_inputs'].append(v.symbol)
83
+ self.__mx["control_inputs"].append(v.symbol)
83
84
 
84
85
  # Initialize nominals and types
85
86
  # These are not in @cached dictionary properties for backwards compatibility.
86
87
  self.__python_types = AliasDict(self.alias_relation)
87
88
  for v in itertools.chain(
88
- self.__pymoca_model.states, self.__pymoca_model.alg_states, self.__pymoca_model.inputs):
89
+ self.__pymoca_model.states, self.__pymoca_model.alg_states, self.__pymoca_model.inputs
90
+ ):
89
91
  self.__python_types[v.symbol.name()] = v.python_type
90
92
 
91
93
  # Initialize dae, initial residuals, as well as delay arguments
92
94
  # These are not in @cached dictionary properties so that we need to create the list
93
95
  # of function arguments only once.
94
- variable_lists = ['states', 'der_states', 'alg_states', 'inputs', 'constants', 'parameters']
96
+ variable_lists = ["states", "der_states", "alg_states", "inputs", "constants", "parameters"]
95
97
  function_arguments = [self.__pymoca_model.time] + [
96
98
  ca.veccat(*[v.symbol for v in getattr(self.__pymoca_model, variable_list)])
97
- for variable_list in variable_lists]
99
+ for variable_list in variable_lists
100
+ ]
98
101
 
99
102
  self.__dae_residual = self.__pymoca_model.dae_residual_function(*function_arguments)
100
103
  if self.__dae_residual is None:
@@ -106,18 +109,36 @@ class ModelicaMixin(OptimizationProblem):
106
109
 
107
110
  # Log variables in debug mode
108
111
  if logger.getEffectiveLevel() == logging.DEBUG:
109
- logger.debug("ModelicaMixin: Found states {}".format(
110
- ', '.join([var.name() for var in self.__mx['states']])))
111
- logger.debug("ModelicaMixin: Found derivatives {}".format(
112
- ', '.join([var.name() for var in self.__mx['derivatives']])))
113
- logger.debug("ModelicaMixin: Found algebraics {}".format(
114
- ', '.join([var.name() for var in self.__mx['algebraics']])))
115
- logger.debug("ModelicaMixin: Found control inputs {}".format(
116
- ', '.join([var.name() for var in self.__mx['control_inputs']])))
117
- logger.debug("ModelicaMixin: Found constant inputs {}".format(
118
- ', '.join([var.name() for var in self.__mx['constant_inputs']])))
119
- logger.debug("ModelicaMixin: Found parameters {}".format(
120
- ', '.join([var.name() for var in self.__mx['parameters']])))
112
+ logger.debug(
113
+ "ModelicaMixin: Found states {}".format(
114
+ ", ".join([var.name() for var in self.__mx["states"]])
115
+ )
116
+ )
117
+ logger.debug(
118
+ "ModelicaMixin: Found derivatives {}".format(
119
+ ", ".join([var.name() for var in self.__mx["derivatives"]])
120
+ )
121
+ )
122
+ logger.debug(
123
+ "ModelicaMixin: Found algebraics {}".format(
124
+ ", ".join([var.name() for var in self.__mx["algebraics"]])
125
+ )
126
+ )
127
+ logger.debug(
128
+ "ModelicaMixin: Found control inputs {}".format(
129
+ ", ".join([var.name() for var in self.__mx["control_inputs"]])
130
+ )
131
+ )
132
+ logger.debug(
133
+ "ModelicaMixin: Found constant inputs {}".format(
134
+ ", ".join([var.name() for var in self.__mx["constant_inputs"]])
135
+ )
136
+ )
137
+ logger.debug(
138
+ "ModelicaMixin: Found parameters {}".format(
139
+ ", ".join([var.name() for var in self.__mx["parameters"]])
140
+ )
141
+ )
121
142
 
122
143
  # Call parent class first for default behaviour.
123
144
  super().__init__(**kwargs)
@@ -125,55 +146,56 @@ class ModelicaMixin(OptimizationProblem):
125
146
  @cached
126
147
  def compiler_options(self) -> Dict[str, Union[str, bool]]:
127
148
  """
128
- Subclasses can configure the `pymoca <http://github.com/pymoca/pymoca>`_ compiler options here.
149
+ Subclasses can configure the `pymoca <http://github.com/pymoca/pymoca>`_ compiler options
150
+ here.
129
151
 
130
- :returns: A dictionary of pymoca compiler options. See the pymoca documentation for details.
152
+ :returns:
153
+ A dictionary of pymoca compiler options. See the pymoca documentation for details.
131
154
  """
132
155
 
133
156
  # Default options
134
157
  compiler_options = {}
135
158
 
136
159
  # Expand vector states to multiple scalar component states.
137
- compiler_options['expand_vectors'] = True
160
+ compiler_options["expand_vectors"] = True
138
161
 
139
162
  # Where imported model libraries are located.
140
163
  library_folders = self.modelica_library_folders.copy()
141
164
 
142
- for ep in pkg_resources.iter_entry_points(group='rtctools.libraries.modelica'):
165
+ for ep in pkg_resources.iter_entry_points(group="rtctools.libraries.modelica"):
143
166
  if ep.name == "library_folder":
144
- library_folders.append(
145
- pkg_resources.resource_filename(ep.module_name, ep.attrs[0]))
167
+ library_folders.append(pkg_resources.resource_filename(ep.module_name, ep.attrs[0]))
146
168
 
147
- compiler_options['library_folders'] = library_folders
169
+ compiler_options["library_folders"] = library_folders
148
170
 
149
171
  # Eliminate equations of the type 'var = const'.
150
- compiler_options['eliminate_constant_assignments'] = True
172
+ compiler_options["eliminate_constant_assignments"] = True
151
173
 
152
174
  # Eliminate constant symbols from model, replacing them with the values
153
175
  # specified in the model.
154
- compiler_options['replace_constant_values'] = True
176
+ compiler_options["replace_constant_values"] = True
155
177
 
156
178
  # Replace any constant expressions into the model.
157
- compiler_options['replace_constant_expressions'] = True
179
+ compiler_options["replace_constant_expressions"] = True
158
180
 
159
181
  # Replace any parameter expressions into the model.
160
- compiler_options['replace_parameter_expressions'] = True
182
+ compiler_options["replace_parameter_expressions"] = True
161
183
 
162
184
  # Eliminate variables starting with underscores.
163
- compiler_options['eliminable_variable_expression'] = r'(.*[.]|^)_\w+(\[[\d,]+\])?\Z'
185
+ compiler_options["eliminable_variable_expression"] = r"(.*[.]|^)_\w+(\[[\d,]+\])?\Z"
164
186
 
165
187
  # Pymoca currently requires `expand_mx` to be set for
166
188
  # `eliminable_variable_expression` to work.
167
- compiler_options['expand_mx'] = True
189
+ compiler_options["expand_mx"] = True
168
190
 
169
191
  # Automatically detect and eliminate alias variables.
170
- compiler_options['detect_aliases'] = True
192
+ compiler_options["detect_aliases"] = True
171
193
 
172
194
  # Disallow aliasing to derivative states
173
- compiler_options['allow_derivative_aliases'] = False
195
+ compiler_options["allow_derivative_aliases"] = False
174
196
 
175
197
  # Cache the model on disk
176
- compiler_options['cache'] = True
198
+ compiler_options["cache"] = True
177
199
 
178
200
  # Done
179
201
  return compiler_options
@@ -182,9 +204,10 @@ class ModelicaMixin(OptimizationProblem):
182
204
  delayed_feedback = super().delayed_feedback()
183
205
 
184
206
  # Create delayed feedback
185
- for delay_state, delay_argument in zip(self.__pymoca_model.delay_states, self.__pymoca_model.delay_arguments):
186
- delayed_feedback.append(
187
- (delay_argument.expr, delay_state, delay_argument.duration))
207
+ for delay_state, delay_argument in zip(
208
+ self.__pymoca_model.delay_states, self.__pymoca_model.delay_arguments
209
+ ):
210
+ delayed_feedback.append((delay_argument.expr, delay_state, delay_argument.duration))
188
211
  return delayed_feedback
189
212
 
190
213
  @property
@@ -198,9 +221,8 @@ class ModelicaMixin(OptimizationProblem):
198
221
  @property
199
222
  @cached
200
223
  def output_variables(self):
201
- output_variables = [ca.MX.sym(variable)
202
- for variable in self.__pymoca_model.outputs]
203
- output_variables.extend(self.__mx['control_inputs'])
224
+ output_variables = [ca.MX.sym(variable) for variable in self.__pymoca_model.outputs]
225
+ output_variables.extend(self.__mx["control_inputs"])
204
226
  return output_variables
205
227
 
206
228
  @cached
@@ -234,7 +256,9 @@ class ModelicaMixin(OptimizationProblem):
234
256
 
235
257
  # Parameter values
236
258
  parameters = self.parameters(ensemble_member)
237
- parameter_values = [parameters.get(param.name(), param) for param in self.__mx['parameters']]
259
+ parameter_values = [
260
+ parameters.get(param.name(), param) for param in self.__mx["parameters"]
261
+ ]
238
262
 
239
263
  # Initial conditions obtained from start attributes.
240
264
  for v in self.__pymoca_model.states:
@@ -245,16 +269,24 @@ class ModelicaMixin(OptimizationProblem):
245
269
  if isinstance(start, ca.MX):
246
270
  # If start contains symbolics, try substituting parameter values
247
271
  if isinstance(start, ca.MX) and not start.is_constant():
248
- [start] = substitute_in_external([start], self.__mx['parameters'], parameter_values)
272
+ [start] = substitute_in_external(
273
+ [start], self.__mx["parameters"], parameter_values
274
+ )
249
275
  if not start.is_constant() or np.isnan(float(start)):
250
- raise Exception('ModelicaMixin: Could not resolve initial value for {}'.format(sym_name))
276
+ raise Exception(
277
+ "ModelicaMixin: Could not resolve initial value for {}".format(
278
+ sym_name
279
+ )
280
+ )
251
281
 
252
282
  start = v.python_type(start)
253
283
 
254
284
  history[sym_name] = Timeseries(initial_time, start)
255
285
 
256
286
  if logger.getEffectiveLevel() == logging.DEBUG:
257
- logger.debug("ModelicaMixin: Initial state variable {} = {}".format(sym_name, start))
287
+ logger.debug(
288
+ "ModelicaMixin: Initial state variable {} = {}".format(sym_name, start)
289
+ )
258
290
 
259
291
  return history
260
292
 
@@ -269,11 +301,14 @@ class ModelicaMixin(OptimizationProblem):
269
301
 
270
302
  # Parameter values
271
303
  parameters = self.parameters(0)
272
- parameter_values = [parameters.get(param.name(), param) for param in self.__mx['parameters']]
304
+ parameter_values = [
305
+ parameters.get(param.name(), param) for param in self.__mx["parameters"]
306
+ ]
273
307
 
274
308
  # Load additional bounds from model
275
309
  for v in itertools.chain(
276
- self.__pymoca_model.states, self.__pymoca_model.alg_states, self.__pymoca_model.inputs):
310
+ self.__pymoca_model.states, self.__pymoca_model.alg_states, self.__pymoca_model.inputs
311
+ ):
277
312
  sym_name = v.symbol.name()
278
313
 
279
314
  try:
@@ -286,16 +321,20 @@ class ModelicaMixin(OptimizationProblem):
286
321
 
287
322
  m_ = v.min
288
323
  if isinstance(m_, ca.MX) and not m_.is_constant():
289
- [m_] = substitute_in_external([m_], self.__mx['parameters'], parameter_values)
324
+ [m_] = substitute_in_external([m_], self.__mx["parameters"], parameter_values)
290
325
  if not m_.is_constant() or np.isnan(float(m_)):
291
- raise Exception('Could not resolve lower bound for variable {}'.format(sym_name))
326
+ raise Exception(
327
+ "Could not resolve lower bound for variable {}".format(sym_name)
328
+ )
292
329
  m_ = float(m_)
293
330
 
294
331
  M_ = v.max
295
332
  if isinstance(M_, ca.MX) and not M_.is_constant():
296
- [M_] = substitute_in_external([M_], self.__mx['parameters'], parameter_values)
333
+ [M_] = substitute_in_external([M_], self.__mx["parameters"], parameter_values)
297
334
  if not M_.is_constant() or np.isnan(float(M_)):
298
- raise Exception('Could not resolve upper bound for variable {}'.format(sym_name))
335
+ raise Exception(
336
+ "Could not resolve upper bound for variable {}".format(sym_name)
337
+ )
299
338
  M_ = float(M_)
300
339
 
301
340
  # We take the intersection of all provided bounds
@@ -313,7 +352,9 @@ class ModelicaMixin(OptimizationProblem):
313
352
 
314
353
  # Parameter values
315
354
  parameters = self.parameters(ensemble_member)
316
- parameter_values = [parameters.get(param.name(), param) for param in self.__mx['parameters']]
355
+ parameter_values = [
356
+ parameters.get(param.name(), param) for param in self.__mx["parameters"]
357
+ ]
317
358
 
318
359
  # Load seeds
319
360
  for var in itertools.chain(self.__pymoca_model.states, self.__pymoca_model.alg_states):
@@ -328,9 +369,13 @@ class ModelicaMixin(OptimizationProblem):
328
369
 
329
370
  # If start contains symbolics, try substituting parameter values
330
371
  if isinstance(start, ca.MX) and not start.is_constant():
331
- [start] = substitute_in_external([start], self.__mx['parameters'], parameter_values)
372
+ [start] = substitute_in_external(
373
+ [start], self.__mx["parameters"], parameter_values
374
+ )
332
375
  if not start.is_constant() or np.isnan(float(start)):
333
- logger.error('ModelicaMixin: Could not resolve seed value for {}'.format(sym_name))
376
+ logger.error(
377
+ "ModelicaMixin: Could not resolve seed value for {}".format(sym_name)
378
+ )
334
379
  continue
335
380
 
336
381
  times = self.times(sym_name)
@@ -353,25 +398,31 @@ class ModelicaMixin(OptimizationProblem):
353
398
  @property
354
399
  @cached
355
400
  def __nominals(self):
356
-
357
401
  # Make the dict
358
402
  nominal_dict = AliasDict(self.alias_relation)
359
403
 
360
404
  # Grab parameters and their values
361
405
  parameters = self.parameters(0)
362
- parameter_values = [parameters.get(param.name(), param) for param in self.__mx['parameters']]
406
+ parameter_values = [
407
+ parameters.get(param.name(), param) for param in self.__mx["parameters"]
408
+ ]
363
409
 
364
410
  # Iterate over nominalizable states
365
411
  for v in itertools.chain(
366
- self.__pymoca_model.states, self.__pymoca_model.alg_states, self.__pymoca_model.inputs):
412
+ self.__pymoca_model.states, self.__pymoca_model.alg_states, self.__pymoca_model.inputs
413
+ ):
367
414
  sym_name = v.symbol.name()
368
415
  nominal = v.nominal
369
416
 
370
417
  # If nominal contains parameter symbols, substitute them
371
418
  if isinstance(nominal, ca.MX) and not nominal.is_constant():
372
- [nominal] = substitute_in_external([nominal], self.__mx['parameters'], parameter_values)
419
+ [nominal] = substitute_in_external(
420
+ [nominal], self.__mx["parameters"], parameter_values
421
+ )
373
422
  if not nominal.is_constant() or np.isnan(float(nominal)):
374
- logger.error('ModelicaMixin: Could not resolve nominal value for {}'.format(sym_name))
423
+ logger.error(
424
+ "ModelicaMixin: Could not resolve nominal value for {}".format(sym_name)
425
+ )
375
426
  continue
376
427
 
377
428
  nominal = float(nominal)
@@ -387,8 +438,11 @@ class ModelicaMixin(OptimizationProblem):
387
438
  nominal_dict[sym_name] = nominal
388
439
 
389
440
  if logger.getEffectiveLevel() == logging.DEBUG:
390
- logger.debug("ModelicaMixin: Set nominal value for variable {} to {}".format(
391
- sym_name, nominal))
441
+ logger.debug(
442
+ "ModelicaMixin: Set nominal value for variable {} to {}".format(
443
+ sym_name, nominal
444
+ )
445
+ )
392
446
  else:
393
447
  logger.warning("ModelicaMixin: Could not set nominal value for {}".format(sym_name))
394
448