rtc-tools 2.5.2rc4__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.2rc4.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.2rc4.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 +237 -146
  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.2rc4.dist-info/RECORD +0 -49
  45. {rtc_tools-2.5.2rc4.dist-info → rtc_tools-2.6.0.dist-info}/COPYING.LESSER +0 -0
  46. {rtc_tools-2.5.2rc4.dist-info → rtc_tools-2.6.0.dist-info}/entry_points.txt +0 -0
  47. {rtc_tools-2.5.2rc4.dist-info → rtc_tools-2.6.0.dist-info}/top_level.txt +0 -0
@@ -5,12 +5,16 @@ from enum import Enum
5
5
  from typing import Dict, Union
6
6
 
7
7
  import casadi as ca
8
-
9
8
  import numpy as np
10
9
 
11
10
  from .goal_programming_mixin import GoalProgrammingMixin
12
- from .goal_programming_mixin_base import Goal, StateGoal, _GoalProgrammingMixinBase # noqa: F401
13
- from .goal_programming_mixin_base import _EmptyEnsembleList, _EmptyEnsembleOrderedDict
11
+ from .goal_programming_mixin_base import ( # noqa: F401
12
+ Goal,
13
+ StateGoal,
14
+ _EmptyEnsembleList,
15
+ _EmptyEnsembleOrderedDict,
16
+ _GoalProgrammingMixinBase,
17
+ )
14
18
  from .timeseries import Timeseries
15
19
 
16
20
  logger = logging.getLogger("rtctools")
@@ -92,7 +96,10 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
92
96
  self.__path_objectives_per_priority = []
93
97
 
94
98
  if isinstance(self, GoalProgrammingMixin):
95
- raise Exception("Cannot be an instance of both GoalProgrammingMixin and SinglePassGoalProgrammingMixin")
99
+ raise Exception(
100
+ "Cannot be an instance of both GoalProgrammingMixin "
101
+ "and SinglePassGoalProgrammingMixin"
102
+ )
96
103
 
97
104
  @property
98
105
  def extra_variables(self):
@@ -104,7 +111,7 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
104
111
 
105
112
  def bounds(self):
106
113
  bounds = super().bounds()
107
- for epsilon in (self.__problem_epsilons + self.__problem_path_epsilons):
114
+ for epsilon in self.__problem_epsilons + self.__problem_path_epsilons:
108
115
  bounds[epsilon.name()] = (0.0, 1.0)
109
116
  return bounds
110
117
 
@@ -115,7 +122,7 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
115
122
 
116
123
  # Append min/max timeseries to the constant inputs. Note that min/max
117
124
  # timeseries are shared between all ensemble members.
118
- for (variable, value) in self.__problem_path_timeseries:
125
+ for variable, value in self.__problem_path_timeseries:
119
126
  if isinstance(value, np.ndarray):
120
127
  value = Timeseries(self.times(), np.broadcast_to(value, (n_times, len(value))))
121
128
  elif not isinstance(value, Timeseries):
@@ -129,7 +136,7 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
129
136
 
130
137
  # Append min/max values to the parameters. Note that min/max values
131
138
  # are shared between all ensemble members.
132
- for (variable, value) in self.__problem_parameters:
139
+ for variable, value in self.__problem_parameters:
133
140
  parameters[variable] = value
134
141
 
135
142
  return parameters
@@ -143,7 +150,8 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
143
150
 
144
151
  additional_constraints = itertools.chain(
145
152
  self.__constraint_store[ensemble_member].values(),
146
- self.__problem_constraints[ensemble_member])
153
+ self.__problem_constraints[ensemble_member],
154
+ )
147
155
 
148
156
  for constraint in additional_constraints:
149
157
  constraints.append((constraint.function(self), constraint.min, constraint.max))
@@ -155,7 +163,8 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
155
163
 
156
164
  additional_path_constraints = itertools.chain(
157
165
  self.__path_constraint_store[ensemble_member].values(),
158
- self.__problem_path_constraints[ensemble_member])
166
+ self.__problem_path_constraints[ensemble_member],
167
+ )
159
168
 
160
169
  for constraint in additional_path_constraints:
161
170
  path_constraints.append((constraint.function(self), constraint.min, constraint.max))
@@ -163,32 +172,30 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
163
172
  return path_constraints
164
173
 
165
174
  def solver_options(self):
166
-
167
175
  # TODO: Split off into private
168
176
 
169
177
  # Call parent
170
178
  options = super().solver_options()
171
179
 
172
- solver = options['solver']
173
- assert solver in ['bonmin', 'ipopt']
180
+ solver = options["solver"]
181
+ assert solver in ["bonmin", "ipopt"]
174
182
 
175
183
  # Make sure constant states, such as min/max timeseries for violation variables,
176
184
  # are turned into parameters for the final optimization problem.
177
185
  ipopt_options = options[solver]
178
- ipopt_options['fixed_variable_treatment'] = 'make_parameter'
186
+ ipopt_options["fixed_variable_treatment"] = "make_parameter"
179
187
 
180
188
  # Define temporary variable to avoid infinite loop between
181
189
  # solver_options and goal_programming_options.
182
190
  self._loop_breaker_solver_options = True
183
191
 
184
- if not hasattr(self, '_loop_breaker_goal_programming_options'):
185
- if not self.goal_programming_options()['mu_reinit']:
186
- ipopt_options['mu_strategy'] = 'monotone'
192
+ if not hasattr(self, "_loop_breaker_goal_programming_options"):
193
+ if not self.goal_programming_options()["mu_reinit"]:
194
+ ipopt_options["mu_strategy"] = "monotone"
187
195
  if not self._gp_first_run:
188
- ipopt_options['mu_init'] = self.solver_stats['iterations'][
189
- 'mu'][-1]
196
+ ipopt_options["mu_init"] = self.solver_stats["iterations"]["mu"][-1]
190
197
 
191
- delattr(self, '_loop_breaker_solver_options')
198
+ delattr(self, "_loop_breaker_solver_options")
192
199
 
193
200
  return options
194
201
 
@@ -224,51 +231,53 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
224
231
  If ``fix_minimized_values`` is set to ``True``, goal functions will be set to equal their
225
232
  optimized values in optimization problems generated during subsequent priorities. Otherwise,
226
233
  only an upper bound will be set. Use of this option is normally not required.
227
- Note that the use of this option may add non-convex constraints to the optimization problem.
228
- The default value for this parameter is ``True`` for the default solvers IPOPT/BONMIN. If any
229
- other solver is used, the default value is ``False``.
234
+ Note that the use of this option may add non-convex constraints to the optimization
235
+ problem. The default value for this parameter is ``True`` for the default solvers
236
+ IPOPT/BONMIN. If any other solver is used, the default value is ``False``.
230
237
 
231
- If ``check_monotonicity`` is set to ``True``, then it will be checked whether goals with the same
232
- function key form a monotonically decreasing sequence with regards to the target interval.
238
+ If ``check_monotonicity`` is set to ``True``, then it will be checked whether goals with
239
+ the same function key form a monotonically decreasing sequence with regards to the target
240
+ interval.
233
241
 
234
- The option ``equality_threshold`` controls when a two-sided inequality constraint is folded into
235
- an equality constraint.
242
+ The option ``equality_threshold`` controls when a two-sided inequality constraint is folded
243
+ into an equality constraint.
236
244
 
237
- If ``scale_by_problem_size`` is set to ``True``, the objective (i.e. the sum of the violation variables)
238
- will be divided by the number of goals, and the path objective will be divided by the number
239
- of path goals and the number of active time steps (per goal). This will make sure the objectives are always in
240
- the range [0, 1], at the cost of solving each goal/time step less accurately.
245
+ If ``scale_by_problem_size`` is set to ``True``, the objective (i.e. the sum of the
246
+ violation variables) will be divided by the number of goals, and the path objective will
247
+ be divided by the number of path goals and the number of active time steps (per goal).
248
+ This will make sure the objectives are always in the range [0, 1], at the cost of solving
249
+ each goal/time step less accurately.
241
250
 
242
251
  :returns: A dictionary of goal programming options.
243
252
  """
244
253
 
245
254
  options = {}
246
255
 
247
- options['mu_reinit'] = True
248
- options['constraint_relaxation'] = 0.0 # Disable by default
249
- options['fix_minimized_values'] = False
250
- options['check_monotonicity'] = True
251
- options['equality_threshold'] = 1e-8
252
- options['scale_by_problem_size'] = False
256
+ options["mu_reinit"] = True
257
+ options["constraint_relaxation"] = 0.0 # Disable by default
258
+ options["fix_minimized_values"] = False
259
+ options["check_monotonicity"] = True
260
+ options["equality_threshold"] = 1e-8
261
+ options["scale_by_problem_size"] = False
253
262
 
254
263
  # Forced options to be able to re-use GoalProgrammingMixin's
255
264
  # GoalProgrammingMixin._gp_* functions. These are not relevant for
256
265
  # SinglePassGoalProgrammingMixin, or should be set to a certain value
257
266
  # for it to make sense.
258
- options['violation_relaxation'] = 0.0 # Disable by default
259
- options['violation_tolerance'] = np.inf # Disable by default
260
- options['interior_distance'] = 1e-6
261
- options['keep_soft_constraints'] = True
267
+ options["violation_relaxation"] = 0.0 # Disable by default
268
+ options["violation_tolerance"] = np.inf # Disable by default
269
+ options["interior_distance"] = 1e-6
270
+ options["keep_soft_constraints"] = True
262
271
 
263
272
  # Define temporary variable to avoid infinite loop between
264
273
  # solver_options and goal_programming_options.
265
274
  self._loop_breaker_goal_programming_options = True
266
275
 
267
- if not hasattr(self, '_loop_breaker_solver_options'):
268
- if self.solver_options()['solver'] in {'ipopt', 'bonmin'}:
269
- options['fix_minimized_values'] = True
276
+ if not hasattr(self, "_loop_breaker_solver_options"):
277
+ if self.solver_options()["solver"] in {"ipopt", "bonmin"}:
278
+ options["fix_minimized_values"] = True
270
279
 
271
- delattr(self, '_loop_breaker_goal_programming_options')
280
+ delattr(self, "_loop_breaker_goal_programming_options")
272
281
 
273
282
  return options
274
283
 
@@ -288,13 +297,26 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
288
297
  self._gp_validate_goals(goals, is_path_goal=False)
289
298
  self._gp_validate_goals(path_goals, is_path_goal=True)
290
299
 
291
- priorities = sorted({int(goal.priority) for goal in itertools.chain(goals, path_goals) if not goal.is_empty})
300
+ priorities = sorted(
301
+ {int(goal.priority) for goal in itertools.chain(goals, path_goals) if not goal.is_empty}
302
+ )
292
303
 
293
304
  for priority in priorities:
294
- subproblems.append((
295
- priority,
296
- [goal for goal in goals if int(goal.priority) == priority and not goal.is_empty],
297
- [goal for goal in path_goals if int(goal.priority) == priority and not goal.is_empty]))
305
+ subproblems.append(
306
+ (
307
+ priority,
308
+ [
309
+ goal
310
+ for goal in goals
311
+ if int(goal.priority) == priority and not goal.is_empty
312
+ ],
313
+ [
314
+ goal
315
+ for goal in path_goals
316
+ if int(goal.priority) == priority and not goal.is_empty
317
+ ],
318
+ )
319
+ )
298
320
 
299
321
  # Solve the subproblems one by one
300
322
  logger.info("Starting goal programming")
@@ -302,7 +324,9 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
302
324
  success = False
303
325
 
304
326
  self.__constraint_store = [OrderedDict() for ensemble_member in range(self.ensemble_size)]
305
- self.__path_constraint_store = [OrderedDict() for ensemble_member in range(self.ensemble_size)]
327
+ self.__path_constraint_store = [
328
+ OrderedDict() for ensemble_member in range(self.ensemble_size)
329
+ ]
306
330
 
307
331
  self.__problem_constraints = [[] for ensemble_member in range(self.ensemble_size)]
308
332
  self.__problem_path_constraints = [[] for ensemble_member in range(self.ensemble_size)]
@@ -325,15 +349,21 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
325
349
  self.__objectives = []
326
350
 
327
351
  for i, (_, goals, path_goals) in enumerate(subproblems):
328
- (subproblem_epsilons, subproblem_objectives,
329
- subproblem_soft_constraints, hard_constraints,
330
- subproblem_parameters) = \
331
- self._gp_goal_constraints(goals, i, options, is_path_goal=False)
332
-
333
- (subproblem_path_epsilons, subproblem_path_objectives,
334
- subproblem_path_soft_constraints, path_hard_constraints,
335
- subproblem_path_timeseries) = \
336
- self._gp_goal_constraints(path_goals, i, options, is_path_goal=True)
352
+ (
353
+ subproblem_epsilons,
354
+ subproblem_objectives,
355
+ subproblem_soft_constraints,
356
+ hard_constraints,
357
+ subproblem_parameters,
358
+ ) = self._gp_goal_constraints(goals, i, options, is_path_goal=False)
359
+
360
+ (
361
+ subproblem_path_epsilons,
362
+ subproblem_path_objectives,
363
+ subproblem_path_soft_constraints,
364
+ path_hard_constraints,
365
+ subproblem_path_timeseries,
366
+ ) = self._gp_goal_constraints(path_goals, i, options, is_path_goal=True)
337
367
 
338
368
  # Put hard constraints in the constraint stores
339
369
  self._gp_update_constraint_store(self.__constraint_store, hard_constraints)
@@ -349,9 +379,11 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
349
379
 
350
380
  for ensemble_member in range(self.ensemble_size):
351
381
  self.__problem_constraints[ensemble_member].extend(
352
- subproblem_soft_constraints[ensemble_member])
382
+ subproblem_soft_constraints[ensemble_member]
383
+ )
353
384
  self.__problem_path_constraints[ensemble_member].extend(
354
- subproblem_path_soft_constraints[ensemble_member])
385
+ subproblem_path_soft_constraints[ensemble_member]
386
+ )
355
387
 
356
388
  self.__objectives_per_priority.append(subproblem_objectives)
357
389
  self.__path_objectives_per_priority.append(subproblem_path_objectives)
@@ -364,7 +396,10 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
364
396
 
365
397
  # Solve subproblem
366
398
  success = super().optimize(
367
- preprocessing=False, postprocessing=False, log_solver_failure_as_error=log_solver_failure_as_error)
399
+ preprocessing=False,
400
+ postprocessing=False,
401
+ log_solver_failure_as_error=log_solver_failure_as_error,
402
+ )
368
403
  if not success:
369
404
  break
370
405
 
@@ -376,15 +411,18 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
376
411
  # two relevant options here for later use.
377
412
  options = self.goal_programming_options()
378
413
  self.__objective_constraint_options = {
379
- k: v for k, v in options.items()
380
- if k in {'fix_minimized_values', 'constraint_relaxation'}
414
+ k: v
415
+ for k, v in options.items()
416
+ if k in {"fix_minimized_values", "constraint_relaxation"}
381
417
  }
382
418
 
383
419
  # Store results. Do this here, to make sure we have results even
384
420
  # if a subsequent priority fails.
385
421
  self.__results_are_current = False
386
- self.__results = [self.extract_results(
387
- ensemble_member) for ensemble_member in range(self.ensemble_size)]
422
+ self.__results = [
423
+ self.extract_results(ensemble_member)
424
+ for ensemble_member in range(self.ensemble_size)
425
+ ]
388
426
  self.__results_are_current = True
389
427
 
390
428
  # Call the post priority hook, so that intermediate results can be
@@ -403,16 +441,21 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
403
441
  return success
404
442
 
405
443
  def transcribe(self):
406
-
407
444
  def _objective_func(subproblem_objectives, subproblem_path_objectives):
408
445
  val = 0.0
409
446
  for ensemble_member in range(self.ensemble_size):
410
447
  n_objectives = self._gp_n_objectives(
411
- subproblem_objectives, subproblem_path_objectives, ensemble_member)
448
+ subproblem_objectives, subproblem_path_objectives, ensemble_member
449
+ )
412
450
  expr = self._gp_objective(subproblem_objectives, n_objectives, ensemble_member)
413
- expr += ca.sum1(self.map_path_expression(
414
- self._gp_path_objective(subproblem_path_objectives, n_objectives, ensemble_member),
415
- ensemble_member))
451
+ expr += ca.sum1(
452
+ self.map_path_expression(
453
+ self._gp_path_objective(
454
+ subproblem_path_objectives, n_objectives, ensemble_member
455
+ ),
456
+ ensemble_member,
457
+ )
458
+ )
416
459
  val += self.ensemble_member_probability(ensemble_member) * expr
417
460
 
418
461
  return val
@@ -426,8 +469,11 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
426
469
 
427
470
  # Objectives
428
471
  for subproblem_objectives, subproblem_path_objectives in zip(
429
- self.__objectives_per_priority, self.__path_objectives_per_priority):
430
- self.__objectives.append(_objective_func(subproblem_objectives, subproblem_path_objectives))
472
+ self.__objectives_per_priority, self.__path_objectives_per_priority
473
+ ):
474
+ self.__objectives.append(
475
+ _objective_func(subproblem_objectives, subproblem_path_objectives)
476
+ )
431
477
 
432
478
  if self.single_pass_method == SinglePassMethod.UPDATE_OBJECTIVE_CONSTRAINT_BOUNDS:
433
479
  # The objectives are also directly added as constraints
@@ -439,18 +485,20 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
439
485
  options = self.__objective_constraint_options
440
486
 
441
487
  previous_objective = self.__objectives[self.__current_priority - 1]
442
- f = ca.Function('tmp', [self.solver_input], [previous_objective])
488
+ f = ca.Function("tmp", [self.solver_input], [previous_objective])
443
489
  obj_val = float(f(self.solver_output))
444
490
 
445
- if options['fix_minimized_values']:
491
+ if options["fix_minimized_values"]:
446
492
  lb, ub = obj_val, obj_val
447
493
  self.linear_collocation = False # Disable solver option jac_c_constant for IPOPT
448
494
  else:
449
- obj_val += options['constraint_relaxation']
495
+ obj_val += options["constraint_relaxation"]
450
496
  lb, ub = -np.inf, obj_val
451
497
 
452
498
  if self.single_pass_method == SinglePassMethod.APPEND_CONSTRAINTS_OBJECTIVE:
453
- self.__additional_constraints.append((self.__objectives[self.__current_priority - 1], lb, ub))
499
+ self.__additional_constraints.append(
500
+ (self.__objectives[self.__current_priority - 1], lb, ub)
501
+ )
454
502
  elif self.single_pass_method == SinglePassMethod.UPDATE_OBJECTIVE_CONSTRAINT_BOUNDS:
455
503
  ind = self.__current_priority - 1
456
504
  constraint = self.__additional_constraints[ind]
@@ -463,13 +511,13 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
463
511
  if self.__additional_constraints:
464
512
  g_extra, lbg_extra, ubg_extra = zip(*self.__additional_constraints)
465
513
 
466
- g = ca.vertcat(nlp['g'], *g_extra)
514
+ g = ca.vertcat(nlp["g"], *g_extra)
467
515
  lbg = [*lbg.copy(), *lbg_extra]
468
516
  ubg = [*ubg.copy(), *ubg_extra]
469
517
 
470
- nlp['g'] = g
518
+ nlp["g"] = g
471
519
 
472
- nlp['f'] = self.__objectives[self.__current_priority]
520
+ nlp["f"] = self.__objectives[self.__current_priority]
473
521
 
474
522
  if not self._gp_first_run:
475
523
  x0 = self.solver_output.copy()
@@ -508,24 +556,25 @@ class CachingQPSol:
508
556
  self._tlcache = {}
509
557
 
510
558
  def __call__(self, name, solver_name, nlp, options):
511
-
512
559
  class Solver:
513
- def __init__(self, nlp=nlp, solver_name=solver_name, options=options, cache=self._tlcache):
514
- x = nlp['x']
515
- f = nlp['f']
516
- g = nlp['g']
560
+ def __init__(
561
+ self, nlp=nlp, solver_name=solver_name, options=options, cache=self._tlcache
562
+ ):
563
+ x = nlp["x"]
564
+ f = nlp["f"]
565
+ g = nlp["g"]
517
566
 
518
567
  if isinstance(x, ca.MX):
519
568
  # Can only convert SX to DM
520
- x = ca.SX.sym('X', *x.shape)
521
- x_mx = nlp['x']
569
+ x = ca.SX.sym("X", *x.shape)
570
+ x_mx = nlp["x"]
522
571
  expand = True
523
572
  else:
524
573
  x_mx = None
525
574
  expand = False
526
575
 
527
576
  if expand:
528
- expand_f = ca.Function('f', [x_mx], [f]).expand()
577
+ expand_f = ca.Function("f", [x_mx], [f]).expand()
529
578
  f = expand_f(x)
530
579
 
531
580
  # Gradient of the objective: gf == Hx + g
@@ -535,37 +584,40 @@ class CachingQPSol:
535
584
  c = ca.substitute(gf, x, ca.DM.zeros(x.sparsity()))
536
585
 
537
586
  # Identify the quadratic term in the objective
538
- H = 0.5*ca.jacobian(gf, x, {"symmetric": True})
587
+ H = 0.5 * ca.jacobian(gf, x, {"symmetric": True})
539
588
 
540
589
  if cache:
541
- if not x.size1() == cache['A'].size2():
590
+ if not x.size1() == cache["A"].size2():
542
591
  raise Exception(
543
- "Number of variables {} does not match cached constraint matrix dimensions {}".format(
544
- x.size1(), cache['A'].shape))
592
+ "Number of variables {} does not match "
593
+ "cached constraint matrix dimensions {}".format(
594
+ x.size1(), cache["A"].shape
595
+ )
596
+ )
545
597
 
546
- n_g_cache = cache['A'].size1()
598
+ n_g_cache = cache["A"].size1()
547
599
  n_g = g.size1()
548
600
 
549
601
  if n_g_cache == n_g:
550
- b = cache['b']
551
- A = cache['A']
602
+ b = cache["b"]
603
+ A = cache["A"]
552
604
  else:
553
605
  g_new = g[n_g_cache:]
554
606
 
555
607
  if expand:
556
- expand_g_new = ca.Function('f', [x_mx], [g_new]).expand()
608
+ expand_g_new = ca.Function("f", [x_mx], [g_new]).expand()
557
609
  g_new = expand_g_new(x)
558
610
 
559
611
  # Identify the constant term in the constraints
560
- b = ca.vertcat(cache['b'],
561
- ca.substitute(g_new, x, ca.DM.zeros(x.sparsity())))
612
+ b = ca.vertcat(
613
+ cache["b"], ca.substitute(g_new, x, ca.DM.zeros(x.sparsity()))
614
+ )
562
615
 
563
616
  # Identify the linear term in the constraints
564
- A = ca.vertcat(cache['A'],
565
- ca.jacobian(g_new, x))
617
+ A = ca.vertcat(cache["A"], ca.jacobian(g_new, x))
566
618
  else:
567
619
  if expand:
568
- expand_g = ca.Function('f', [x_mx], [g]).expand()
620
+ expand_g = ca.Function("f", [x_mx], [g]).expand()
569
621
  g = expand_g(x)
570
622
 
571
623
  # Identify the constant term in the constraints
@@ -574,16 +626,12 @@ class CachingQPSol:
574
626
  # Identify the linear term in the constraints
575
627
  A = ca.jacobian(g, x)
576
628
 
577
- cache['A'] = A
578
- cache['b'] = b
629
+ cache["A"] = A
630
+ cache["b"] = b
579
631
 
580
- self._solver = ca.conic("mysolver",
581
- solver_name,
582
- {
583
- 'h': H.sparsity(),
584
- 'a': A.sparsity()
585
- },
586
- options)
632
+ self._solver = ca.conic(
633
+ "mysolver", solver_name, {"h": H.sparsity(), "a": A.sparsity()}, options
634
+ )
587
635
  self._solver_in = {}
588
636
  self._solver_in["h"] = ca.DM(H)
589
637
  self._solver_in["g"] = ca.DM(c)
@@ -591,7 +639,6 @@ class CachingQPSol:
591
639
  self._b = ca.DM(b)
592
640
 
593
641
  def __call__(self, x0, lbx, ubx, lbg, ubg):
594
-
595
642
  self._solver_in["x0"] = x0
596
643
  self._solver_in["lbx"] = lbx
597
644
  self._solver_in["ubx"] = ubx
@@ -600,7 +647,7 @@ class CachingQPSol:
600
647
 
601
648
  solver_out = self._solver(**self._solver_in)
602
649
 
603
- solver_out['f'] = solver_out['cost']
650
+ solver_out["f"] = solver_out["cost"]
604
651
 
605
652
  return solver_out
606
653
 
@@ -1,7 +1,6 @@
1
1
  from typing import Union
2
2
 
3
3
  import casadi as ca
4
-
5
4
  import numpy as np
6
5
 
7
6
 
@@ -28,7 +27,7 @@ class Timeseries:
28
27
  elif isinstance(values, (np.ndarray, list)) and len(values) == 1:
29
28
  values = values[0]
30
29
 
31
- if hasattr(values, '__iter__'):
30
+ if hasattr(values, "__iter__"):
32
31
  self.__values = np.array(values, dtype=np.float64, copy=True)
33
32
  else:
34
33
  self.__values = np.full_like(times, values, dtype=np.float64)
@@ -47,12 +46,11 @@ class Timeseries:
47
46
  """
48
47
  return self.__values
49
48
 
50
- def __neg__(self) -> 'Timeseries':
49
+ def __neg__(self) -> "Timeseries":
51
50
  return self.__class__(self.times, -self.values)
52
51
 
53
52
  def __repr__(self) -> str:
54
- return 'Timeseries({}, {})'.format(self.__times, self.__values)
53
+ return "Timeseries({}, {})".format(self.__times, self.__values)
55
54
 
56
55
  def __eq__(self, other: "Timeseries") -> bool:
57
- return (np.array_equal(self.times, other.times) and
58
- np.array_equal(self.values, other.values))
56
+ return np.array_equal(self.times, other.times) and np.array_equal(self.values, other.values)
rtctools/rtctoolsapp.py CHANGED
@@ -6,13 +6,12 @@ from pathlib import Path
6
6
 
7
7
  import rtctools
8
8
 
9
- logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s')
9
+ logging.basicConfig(format="%(asctime)s %(levelname)s %(message)s")
10
10
  logger = logging.getLogger("rtctools")
11
11
  logger.setLevel(logging.INFO)
12
12
 
13
13
 
14
14
  def copy_libraries(*args):
15
-
16
15
  if not args:
17
16
  args = sys.argv[1:]
18
17
 
@@ -38,25 +37,30 @@ def copy_libraries(*args):
38
37
  else:
39
38
  if not os.path.exists(d):
40
39
  shutil.copy2(s, d)
41
- elif Path(s).name.lower() == 'package.mo':
40
+ elif Path(s).name.lower() == "package.mo":
42
41
  # Pick the largest one, assuming that all plugin packages
43
42
  # to not provide a meaningful package.mo
44
43
  if os.stat(s).st_size > os.stat(d).st_size:
45
- logger.warning("Overwriting '{}' with '{}' as the latter is larger.".format(d, s))
44
+ logger.warning(
45
+ "Overwriting '{}' with '{}' as the latter is larger.".format(d, s)
46
+ )
46
47
  os.remove(d)
47
48
  shutil.copy2(s, d)
48
49
  else:
49
- logger.warning("Not copying '{}' to '{}' as the latter is larger.".format(s, d))
50
+ logger.warning(
51
+ "Not copying '{}' to '{}' as the latter is larger.".format(s, d)
52
+ )
50
53
  else:
51
54
  raise OSError("Could not combine two folders")
52
55
 
53
56
  dst = Path(path)
54
57
 
55
58
  library_folders = []
56
- for ep in pkg_resources.iter_entry_points(group='rtctools.libraries.modelica'):
59
+ for ep in pkg_resources.iter_entry_points(group="rtctools.libraries.modelica"):
57
60
  if ep.name == "library_folder":
58
61
  library_folders.append(
59
- Path(pkg_resources.resource_filename(ep.module_name, ep.attrs[0])))
62
+ Path(pkg_resources.resource_filename(ep.module_name, ep.attrs[0]))
63
+ )
60
64
 
61
65
  tlds = {}
62
66
  for lf in library_folders:
@@ -78,7 +82,6 @@ def copy_libraries(*args):
78
82
 
79
83
 
80
84
  def download_examples(*args):
81
-
82
85
  if not args:
83
86
  args = sys.argv[1:]
84
87
 
@@ -96,25 +99,22 @@ def download_examples(*args):
96
99
  from urllib.error import HTTPError
97
100
  from zipfile import ZipFile
98
101
 
99
- # GitLab is blocking requests unless we specify a user agent
100
- user_agent = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0'
101
-
102
102
  version = rtctools.__version__
103
- rtc_full_name = 'rtc-tools-{}'.format(version)
103
+ rtc_full_name = "rtc-tools-{}".format(version)
104
104
  try:
105
- url = 'https://gitlab.com/deltares/rtc-tools/-/archive/' \
106
- '{}/{}.zip'.format(version, rtc_full_name)
105
+ url = "https://gitlab.com/deltares/rtc-tools/-/archive/{}/{}.zip".format(
106
+ version, rtc_full_name
107
+ )
107
108
 
108
109
  opener = urllib.request.build_opener()
109
- opener.addheaders = [('User-agent', user_agent)]
110
110
  urllib.request.install_opener(opener)
111
111
  local_filename, _ = urllib.request.urlretrieve(url)
112
112
  except HTTPError:
113
113
  sys.exit("Could not found examples for RTC-Tools version {}.".format(version))
114
114
 
115
- with ZipFile(local_filename, 'r') as z:
116
- target = path / 'rtc-tools-examples'
117
- prefix = '{}/examples/'.format(rtc_full_name)
115
+ with ZipFile(local_filename, "r") as z:
116
+ target = path / "rtc-tools-examples"
117
+ prefix = "{}/examples/".format(rtc_full_name)
118
118
  members = [x for x in z.namelist() if x.startswith(prefix)]
119
119
  z.extractall(members=members)
120
120
  shutil.move(prefix, target)