rtc-tools 2.7.1__tar.gz → 2.7.3__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.
- {rtc_tools-2.7.1/src/rtc_tools.egg-info → rtc_tools-2.7.3}/PKG-INFO +2 -2
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/README.md +12 -6
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/pyproject.toml +1 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/setup.py +1 -1
- {rtc_tools-2.7.1 → rtc_tools-2.7.3/src/rtc_tools.egg-info}/PKG-INFO +2 -2
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtc_tools.egg-info/requires.txt +1 -1
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/_internal/casadi_helpers.py +39 -3
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/_version.py +3 -3
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/data/pi.py +37 -24
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/collocated_integrated_optimization_problem.py +185 -131
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/modelica_mixin.py +12 -5
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/optimization_problem.py +3 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/single_pass_goal_programming_mixin.py +20 -1
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/rtctoolsapp.py +1 -1
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/COPYING.LESSER +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/MANIFEST.in +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/setup.cfg +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtc_tools.egg-info/SOURCES.txt +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtc_tools.egg-info/dependency_links.txt +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtc_tools.egg-info/entry_points.txt +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtc_tools.egg-info/top_level.txt +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/__init__.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/_internal/__init__.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/_internal/alias_tools.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/_internal/caching.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/_internal/debug_check_helpers.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/data/__init__.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/data/csv.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/data/interpolation/__init__.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/data/interpolation/bspline.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/data/interpolation/bspline1d.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/data/interpolation/bspline2d.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/data/netcdf.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/data/rtc.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/data/storage.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/__init__.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/control_tree_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/csv_lookup_table_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/csv_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/goal_programming_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/goal_programming_mixin_base.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/homotopy_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/initial_state_estimation_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/io_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/linearization_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/linearized_order_goal_programming_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/min_abs_goal_programming_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/netcdf_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/pi_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/planning_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/timeseries.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/simulation/__init__.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/simulation/csv_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/simulation/io_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/simulation/pi_mixin.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/simulation/simulation_problem.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/util.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/tests/test_case.py +0 -0
- {rtc_tools-2.7.1 → rtc_tools-2.7.3}/versioneer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rtc-tools
|
|
3
|
-
Version: 2.7.
|
|
3
|
+
Version: 2.7.3
|
|
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
|
Download-URL: http://github.com/deltares/rtc-tools/
|
|
@@ -25,7 +25,7 @@ Classifier: Operating System :: Unix
|
|
|
25
25
|
Classifier: Operating System :: MacOS
|
|
26
26
|
Requires-Python: >=3.9
|
|
27
27
|
License-File: COPYING.LESSER
|
|
28
|
-
Requires-Dist: casadi!=3.6.6
|
|
28
|
+
Requires-Dist: casadi!=3.6.6,<3.8,>=3.6.3
|
|
29
29
|
Requires-Dist: numpy>=1.16.0
|
|
30
30
|
Requires-Dist: scipy>=1.0.0
|
|
31
31
|
Requires-Dist: pymoca==0.9.*,>=0.9.1
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Deltares RTC-Tools
|
|
2
2
|
|
|
3
|
-
[](
|
|
4
|
+
https://github.com/rtc-tools/rtc-tools/actions/workflows/ci.yml
|
|
5
5
|
)
|
|
6
6
|
[.
|
|
52
52
|
|
|
53
|
+
## Contributing
|
|
54
|
+
|
|
55
|
+
We welcome contributions to RTC-Tools! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details on how to get started, coding standards, and our development process.
|
|
56
|
+
|
|
57
|
+
The project is governed according to our [Technical Charter](CHARTER.md) and [Governance Model](GOVERNANCE.md).
|
|
58
|
+
|
|
53
59
|
|
|
54
60
|
## Migration from GitLab
|
|
55
61
|
|
|
56
|
-
The rtc-tools repository has been migrated from gitlab (https://gitlab.com/
|
|
62
|
+
The rtc-tools repository has been migrated from gitlab (https://gitlab.com/rtc-tools/rtc-tools)
|
|
57
63
|
to here.
|
|
58
64
|
To change the git remote url, run
|
|
59
65
|
|
|
60
|
-
`git remote set-url origin https://github.com/
|
|
66
|
+
`git remote set-url origin https://github.com/rtc-tools/rtc-tools.git`
|
|
61
67
|
|
|
62
68
|
and
|
|
63
69
|
|
|
64
|
-
`git remote set-url --push origin https://github.com/
|
|
70
|
+
`git remote set-url --push origin https://github.com/rtc-tools/rtc-tools.git`.
|
|
65
71
|
|
|
66
72
|
|
|
67
73
|
## License
|
|
@@ -73,4 +79,4 @@ and can be used free of charge. Deltares offers support packages for users who r
|
|
|
73
79
|
If you use RTC-Tools in your work, please acknowledge it in any resulting publications.
|
|
74
80
|
You can do this by citing RTC-Tools and providing a link to our
|
|
75
81
|
[website](https://oss.deltares.nl/web/rtc-tools/home) or
|
|
76
|
-
[GitHub repository](https://github.com/
|
|
82
|
+
[GitHub repository](https://github.com/rtc-tools/rtc-tools).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rtc-tools
|
|
3
|
-
Version: 2.7.
|
|
3
|
+
Version: 2.7.3
|
|
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
|
Download-URL: http://github.com/deltares/rtc-tools/
|
|
@@ -25,7 +25,7 @@ Classifier: Operating System :: Unix
|
|
|
25
25
|
Classifier: Operating System :: MacOS
|
|
26
26
|
Requires-Python: >=3.9
|
|
27
27
|
License-File: COPYING.LESSER
|
|
28
|
-
Requires-Dist: casadi!=3.6.6
|
|
28
|
+
Requires-Dist: casadi!=3.6.6,<3.8,>=3.6.3
|
|
29
29
|
Requires-Dist: numpy>=1.16.0
|
|
30
30
|
Requires-Dist: scipy>=1.0.0
|
|
31
31
|
Requires-Dist: pymoca==0.9.*,>=0.9.1
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import logging
|
|
2
4
|
|
|
3
5
|
import casadi as ca
|
|
4
6
|
|
|
5
7
|
logger = logging.getLogger("rtctools")
|
|
6
8
|
|
|
9
|
+
# Limit the number of times we try to substitute in external functions, e.g. in
|
|
10
|
+
# case of infinite recursion. Generally unlikely that we will hit this limit for
|
|
11
|
+
# any reasonable use case.
|
|
12
|
+
MAX_SUBSTITUTE_DEPTH = 10
|
|
13
|
+
|
|
7
14
|
|
|
8
15
|
def is_affine(expr, symbols):
|
|
9
16
|
try:
|
|
@@ -37,12 +44,41 @@ def reduce_matvec(e, v):
|
|
|
37
44
|
return ca.reshape(ca.mtimes(A, v), e.shape)
|
|
38
45
|
|
|
39
46
|
|
|
40
|
-
def substitute_in_external(
|
|
41
|
-
|
|
47
|
+
def substitute_in_external(
|
|
48
|
+
expr: list[ca.MX],
|
|
49
|
+
symbols: list[ca.MX],
|
|
50
|
+
values: list[ca.MX | ca.DM | float],
|
|
51
|
+
/,
|
|
52
|
+
resolve_numerically=False,
|
|
53
|
+
):
|
|
54
|
+
# We expect expr to be a list of (at most) length 1
|
|
55
|
+
assert len(expr) <= 1
|
|
56
|
+
|
|
57
|
+
if not expr or len(symbols) == 0 or all(isinstance(x, ca.DM) for x in expr):
|
|
42
58
|
return expr
|
|
43
|
-
|
|
59
|
+
elif not resolve_numerically:
|
|
44
60
|
f = ca.Function("f", symbols, expr)
|
|
45
61
|
return f.call(values, True, False)
|
|
62
|
+
else:
|
|
63
|
+
# CasADi < 3.7 workaround: f.call() with MX values returns wrapped
|
|
64
|
+
# results like f(...){0}. Resolve symbolics with ca.substitute(), and
|
|
65
|
+
# convert resulting MX constants to floats first. Remove when dropping
|
|
66
|
+
# support for CasADi 3.6.x.
|
|
67
|
+
resolved_values = list(values)
|
|
68
|
+
for _ in range(MAX_SUBSTITUTE_DEPTH):
|
|
69
|
+
for i, v in enumerate(resolved_values):
|
|
70
|
+
if isinstance(v, ca.MX) and not v.is_constant():
|
|
71
|
+
resolved_values[i] = ca.substitute([v], symbols, resolved_values)[0]
|
|
72
|
+
elif isinstance(v, ca.MX):
|
|
73
|
+
resolved_values[i] = float(v)
|
|
74
|
+
|
|
75
|
+
# Substitute in expression using f.call() for external function support
|
|
76
|
+
f = ca.Function("f", symbols, expr).expand()
|
|
77
|
+
expr = f.call(resolved_values, True, False)
|
|
78
|
+
if expr[0].is_constant():
|
|
79
|
+
break
|
|
80
|
+
|
|
81
|
+
return expr
|
|
46
82
|
|
|
47
83
|
|
|
48
84
|
def interpolate(ts, xs, t, equidistant, mode=0):
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "
|
|
11
|
+
"date": "2026-01-20T14:58:22+0100",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "2.7.
|
|
14
|
+
"full-revisionid": "20585c7d4d1b615468ea7f7dab44a240aeb1f05c",
|
|
15
|
+
"version": "2.7.3"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -411,6 +411,7 @@ class Timeseries:
|
|
|
411
411
|
self.__forecast_index = None
|
|
412
412
|
self.__contains_ensemble = False
|
|
413
413
|
self.__ensemble_size = 1
|
|
414
|
+
ensemble_indexes = set()
|
|
414
415
|
for series in self.__xml_root.findall("pi:series", ns):
|
|
415
416
|
header = series.find("pi:header", ns)
|
|
416
417
|
|
|
@@ -471,14 +472,23 @@ class Timeseries:
|
|
|
471
472
|
|
|
472
473
|
el = header.find("pi:ensembleMemberIndex", ns)
|
|
473
474
|
if el is not None:
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
475
|
+
ensemble_indexes.add(int(el.text))
|
|
476
|
+
|
|
477
|
+
# We assume the ensemble ids are zero-based and increasing by 1
|
|
478
|
+
if len(ensemble_indexes) > 1:
|
|
479
|
+
# check if ids are zero-based and increasing by 1
|
|
480
|
+
sorted_ensemble_indexes = sorted(ensemble_indexes)
|
|
481
|
+
if sorted_ensemble_indexes != list(range(len(sorted_ensemble_indexes))):
|
|
482
|
+
raise ValueError(
|
|
483
|
+
"PI: Ensemble ids must be zero-based and increasing by 1 when more than one"
|
|
484
|
+
" ensemble member is present."
|
|
485
|
+
)
|
|
486
|
+
self.__contains_ensemble = True
|
|
487
|
+
self.__ensemble_size = len(ensemble_indexes)
|
|
488
|
+
else:
|
|
489
|
+
# There are no ensemble members, or a single ensemble member of arbitrary id
|
|
490
|
+
self.__contains_ensemble = False
|
|
491
|
+
self.__ensemble_size = 1
|
|
482
492
|
|
|
483
493
|
# Define the times, and floor the global forecast_datetime to the
|
|
484
494
|
# global time step to get its index
|
|
@@ -538,24 +548,27 @@ class Timeseries:
|
|
|
538
548
|
end_datetime = self.__parse_date_time(header.find("pi:endDate", ns))
|
|
539
549
|
|
|
540
550
|
make_virtual_ensemble = False
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
self.__values
|
|
546
|
-
|
|
547
|
-
self.__units
|
|
551
|
+
if self.__contains_ensemble:
|
|
552
|
+
el = header.find("pi:ensembleMemberIndex", ns)
|
|
553
|
+
if el is not None:
|
|
554
|
+
ensemble_member = int(el.text)
|
|
555
|
+
while ensemble_member >= len(self.__values):
|
|
556
|
+
self.__values.append({})
|
|
557
|
+
while ensemble_member >= len(self.__units):
|
|
558
|
+
self.__units.append({})
|
|
559
|
+
else:
|
|
560
|
+
ensemble_member = 0
|
|
561
|
+
if el is None:
|
|
562
|
+
# Expand values dict to accommodate referencing of (virtual)
|
|
563
|
+
# ensemble series to the input values. This is e.g. needed
|
|
564
|
+
# for initial states that have a single historical values.
|
|
565
|
+
while self.ensemble_size > len(self.__values):
|
|
566
|
+
self.__values.append({})
|
|
567
|
+
while self.ensemble_size > len(self.__units):
|
|
568
|
+
self.__units.append({})
|
|
569
|
+
make_virtual_ensemble = True
|
|
548
570
|
else:
|
|
549
571
|
ensemble_member = 0
|
|
550
|
-
if el is None and self.contains_ensemble is True:
|
|
551
|
-
# Expand values dict to accommodate referencing of (virtual)
|
|
552
|
-
# ensemble series to the input values. This is e.g. needed
|
|
553
|
-
# for initial states that have a single historical values.
|
|
554
|
-
while self.ensemble_size > len(self.__values):
|
|
555
|
-
self.__values.append({})
|
|
556
|
-
while self.ensemble_size > len(self.__units):
|
|
557
|
-
self.__units.append({})
|
|
558
|
-
make_virtual_ensemble = True
|
|
559
572
|
|
|
560
573
|
if self.__dt:
|
|
561
574
|
n_values = int(
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import itertools
|
|
2
4
|
import logging
|
|
3
5
|
import warnings
|
|
4
6
|
from abc import ABCMeta, abstractmethod
|
|
5
|
-
from typing import Dict, Union
|
|
6
7
|
|
|
7
8
|
import casadi as ca
|
|
8
9
|
import numpy as np
|
|
10
|
+
from numpy.typing import NDArray
|
|
9
11
|
|
|
10
12
|
from rtctools._internal.alias_tools import AliasDict
|
|
11
13
|
from rtctools._internal.casadi_helpers import (
|
|
@@ -17,7 +19,7 @@ from rtctools._internal.casadi_helpers import (
|
|
|
17
19
|
)
|
|
18
20
|
from rtctools._internal.debug_check_helpers import DebugLevel, debug_check
|
|
19
21
|
|
|
20
|
-
from .optimization_problem import OptimizationProblem
|
|
22
|
+
from .optimization_problem import BT, OptimizationProblem
|
|
21
23
|
from .timeseries import Timeseries
|
|
22
24
|
|
|
23
25
|
logger = logging.getLogger("rtctools")
|
|
@@ -44,11 +46,17 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
44
46
|
|
|
45
47
|
:cvar check_collocation_linearity:
|
|
46
48
|
If ``True``, check whether collocation constraints are linear. Default is ``True``.
|
|
49
|
+
:cvar inline_delay_expressions:
|
|
50
|
+
If ``True``, delay expressions are inlined into constraints and objectives instead of
|
|
51
|
+
being added as separate equality constraints. Default is ``False``.
|
|
47
52
|
"""
|
|
48
53
|
|
|
49
54
|
#: Check whether the collocation constraints are linear
|
|
50
55
|
check_collocation_linearity = True
|
|
51
56
|
|
|
57
|
+
#: Inline delay expressions instead of adding them as separate equality constraints
|
|
58
|
+
inline_delay_expressions = False
|
|
59
|
+
|
|
52
60
|
#: Whether or not the collocation constraints are linear (affine)
|
|
53
61
|
linear_collocation = None
|
|
54
62
|
|
|
@@ -172,7 +180,7 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
172
180
|
# algebraic parts. Theta then only applies to the ODE part.
|
|
173
181
|
return 1.0
|
|
174
182
|
|
|
175
|
-
def map_options(self) ->
|
|
183
|
+
def map_options(self) -> dict[str, str | int]:
|
|
176
184
|
"""
|
|
177
185
|
Returns a dictionary of CasADi ``map()`` options.
|
|
178
186
|
|
|
@@ -312,7 +320,7 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
312
320
|
for variable in self.__integrated_states:
|
|
313
321
|
if self.__variable_sizes.get(variable, 1) > 1:
|
|
314
322
|
raise NotImplementedError(
|
|
315
|
-
"Vector symbol not supported for integrated state '{}'"
|
|
323
|
+
f"Vector symbol not supported for integrated state '{variable}'"
|
|
316
324
|
)
|
|
317
325
|
else:
|
|
318
326
|
self.__integrated_states = []
|
|
@@ -321,7 +329,7 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
321
329
|
for variable in self.controls:
|
|
322
330
|
if self.__variable_sizes.get(variable, 1) > 1:
|
|
323
331
|
raise NotImplementedError(
|
|
324
|
-
"Vector symbol not supported for control state '{}'"
|
|
332
|
+
f"Vector symbol not supported for control state '{variable}'"
|
|
325
333
|
)
|
|
326
334
|
|
|
327
335
|
# Collocation times
|
|
@@ -342,7 +350,7 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
342
350
|
try:
|
|
343
351
|
constant_input = raw_constant_inputs[variable]
|
|
344
352
|
except KeyError:
|
|
345
|
-
raise Exception("No values found for constant input {}"
|
|
353
|
+
raise Exception(f"No values found for constant input {variable}")
|
|
346
354
|
else:
|
|
347
355
|
values = constant_input.values
|
|
348
356
|
interpolation_method = self.interpolation_method(variable)
|
|
@@ -371,7 +379,7 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
371
379
|
try:
|
|
372
380
|
parameter_values[i] = parameters[variable]
|
|
373
381
|
except KeyError:
|
|
374
|
-
raise Exception("No value specified for parameter {}"
|
|
382
|
+
raise Exception(f"No value specified for parameter {variable}")
|
|
375
383
|
|
|
376
384
|
if len(dynamic_parameters) > 0:
|
|
377
385
|
jac_1 = ca.jacobian(symbolic_parameters, ca.vertcat(*dynamic_parameters))
|
|
@@ -385,7 +393,10 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
385
393
|
):
|
|
386
394
|
parameter_values = nullvertcat(*parameter_values)
|
|
387
395
|
[parameter_values] = substitute_in_external(
|
|
388
|
-
[parameter_values],
|
|
396
|
+
[parameter_values],
|
|
397
|
+
self.dae_variables["parameters"],
|
|
398
|
+
ca.vertsplit(parameter_values),
|
|
399
|
+
resolve_numerically=False,
|
|
389
400
|
)
|
|
390
401
|
else:
|
|
391
402
|
parameter_values = nullvertcat(*parameter_values)
|
|
@@ -410,7 +421,10 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
410
421
|
if v.ndim == 1:
|
|
411
422
|
ensemble_data["extra_constant_inputs"][k] = v[:, None]
|
|
412
423
|
|
|
413
|
-
|
|
424
|
+
if self.ensemble_specific_bounds:
|
|
425
|
+
bounds = [self.bounds(ensemble_member=i) for i in range(self.ensemble_size)]
|
|
426
|
+
else:
|
|
427
|
+
bounds = self.bounds()
|
|
414
428
|
|
|
415
429
|
# Initialize control discretization
|
|
416
430
|
(
|
|
@@ -494,8 +508,8 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
494
508
|
collocated_variables += self.dae_variables["control_inputs"]
|
|
495
509
|
|
|
496
510
|
if logger.getEffectiveLevel() == logging.DEBUG:
|
|
497
|
-
logger.debug("Integrating variables {
|
|
498
|
-
logger.debug("Collocating variables {
|
|
511
|
+
logger.debug(f"Integrating variables {repr(integrated_variables)}")
|
|
512
|
+
logger.debug(f"Collocating variables {repr(collocated_variables)}")
|
|
499
513
|
|
|
500
514
|
integrated_variable_names = [v.name() for v in integrated_variables]
|
|
501
515
|
integrated_variable_nominals = np.array(
|
|
@@ -517,14 +531,14 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
517
531
|
collocated_derivatives = self.dae_variables["derivatives"][:]
|
|
518
532
|
self.__algebraic_and_control_derivatives = []
|
|
519
533
|
for var in self.dae_variables["algebraics"]:
|
|
520
|
-
sym = ca.MX.sym("der({
|
|
534
|
+
sym = ca.MX.sym(f"der({var.name()})")
|
|
521
535
|
self.__algebraic_and_control_derivatives.append(sym)
|
|
522
536
|
if self.integrate_states:
|
|
523
537
|
integrated_derivatives.append(sym)
|
|
524
538
|
else:
|
|
525
539
|
collocated_derivatives.append(sym)
|
|
526
540
|
for var in self.dae_variables["control_inputs"]:
|
|
527
|
-
sym = ca.MX.sym("der({
|
|
541
|
+
sym = ca.MX.sym(f"der({var.name()})")
|
|
528
542
|
self.__algebraic_and_control_derivatives.append(sym)
|
|
529
543
|
collocated_derivatives.append(sym)
|
|
530
544
|
|
|
@@ -758,7 +772,7 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
758
772
|
try:
|
|
759
773
|
lookup_table = lookup_tables[sym_name]
|
|
760
774
|
except KeyError:
|
|
761
|
-
raise Exception("Unable to find lookup table function for {}"
|
|
775
|
+
raise Exception(f"Unable to find lookup table function for {sym_name}")
|
|
762
776
|
else:
|
|
763
777
|
input_syms = [
|
|
764
778
|
self.variable(input_sym.name()) for input_sym in lookup_table.inputs
|
|
@@ -823,9 +837,9 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
823
837
|
if self.integrate_states:
|
|
824
838
|
I = ca.MX.sym("I", len(integrated_variables)) # noqa: E741
|
|
825
839
|
I0 = ca.MX.sym("I0", len(integrated_variables))
|
|
826
|
-
C0 = [ca.MX.sym("C0[{}]"
|
|
840
|
+
C0 = [ca.MX.sym(f"C0[{i}]") for i in range(len(collocated_variables))]
|
|
827
841
|
CI0 = [
|
|
828
|
-
ca.MX.sym("CI0[{}]"
|
|
842
|
+
ca.MX.sym(f"CI0[{i}]")
|
|
829
843
|
for i in range(len(self.dae_variables["constant_inputs"]))
|
|
830
844
|
]
|
|
831
845
|
dt_sym = ca.MX.sym("dt")
|
|
@@ -1243,6 +1257,12 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
1243
1257
|
# Integrators are saved for result extraction later on
|
|
1244
1258
|
self.__integrators = []
|
|
1245
1259
|
|
|
1260
|
+
# If inlining delay expressions, prepare for the single call to ca.substitute()
|
|
1261
|
+
# at the end
|
|
1262
|
+
if self.inline_delay_expressions:
|
|
1263
|
+
delayed_feedback_variable_replacement = ca.MX.zeros(X.numel())
|
|
1264
|
+
delayed_feedback_variable_replacement[:] = X
|
|
1265
|
+
|
|
1246
1266
|
# Process the objectives and constraints for each ensemble member separately.
|
|
1247
1267
|
# Note that we don't use map here for the moment, so as to allow each ensemble member to
|
|
1248
1268
|
# define its own constraints and objectives. Path constraints are applied for all ensemble
|
|
@@ -1250,9 +1270,7 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
1250
1270
|
# ensemble member to specify its own path constraints as well, once CasADi has some kind
|
|
1251
1271
|
# of loop detection.
|
|
1252
1272
|
for ensemble_member in range(self.ensemble_size):
|
|
1253
|
-
logger.info(
|
|
1254
|
-
"Transcribing ensemble member {}/{}".format(ensemble_member + 1, self.ensemble_size)
|
|
1255
|
-
)
|
|
1273
|
+
logger.info(f"Transcribing ensemble member {ensemble_member + 1}/{self.ensemble_size}")
|
|
1256
1274
|
|
|
1257
1275
|
initial_state = ensemble_aggregate["initial_state"][:, ensemble_member]
|
|
1258
1276
|
initial_derivatives = ensemble_aggregate["initial_derivatives"][:, ensemble_member]
|
|
@@ -1299,9 +1317,7 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
1299
1317
|
|
|
1300
1318
|
if val < lbx[idx] or val > ubx[idx]:
|
|
1301
1319
|
logger.warning(
|
|
1302
|
-
"Initial value {} for variable '{}' outside bounds."
|
|
1303
|
-
val, variable
|
|
1304
|
-
)
|
|
1320
|
+
f"Initial value {val} for variable '{variable}' outside bounds."
|
|
1305
1321
|
)
|
|
1306
1322
|
|
|
1307
1323
|
lbx[idx] = ubx[idx] = val
|
|
@@ -1627,13 +1643,13 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
1627
1643
|
states_and_algebraics_and_controls,
|
|
1628
1644
|
states_and_algebraics_and_controls_derivatives,
|
|
1629
1645
|
*[
|
|
1630
|
-
ca.
|
|
1646
|
+
ca.MX(constant_inputs[variable][1:]).T
|
|
1631
1647
|
for variable in dae_constant_inputs_names
|
|
1632
1648
|
],
|
|
1633
|
-
ca.
|
|
1649
|
+
ca.MX(collocation_times[1:]).T,
|
|
1634
1650
|
path_variables.T if path_variables.numel() > 0 else ca.MX(),
|
|
1635
1651
|
*[
|
|
1636
|
-
ca.
|
|
1652
|
+
ca.MX(extra_constant_inputs[variable][1:]).T
|
|
1637
1653
|
for (variable, _) in extra_constant_inputs_name_and_size
|
|
1638
1654
|
],
|
|
1639
1655
|
),
|
|
@@ -1826,8 +1842,8 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
1826
1842
|
np.isnan(delayed_feedback_history[hist_start_ind:, i])
|
|
1827
1843
|
):
|
|
1828
1844
|
logger.warning(
|
|
1829
|
-
"Incomplete history for delayed expression {}. "
|
|
1830
|
-
"Extrapolating t0 value backwards in time."
|
|
1845
|
+
f"Incomplete history for delayed expression {expression}. "
|
|
1846
|
+
"Extrapolating t0 value backwards in time."
|
|
1831
1847
|
)
|
|
1832
1848
|
out_times = out_times[len(history_times) :]
|
|
1833
1849
|
out_values = out_values[len(history_times) :]
|
|
@@ -1851,10 +1867,32 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
1851
1867
|
|
|
1852
1868
|
nominal = nominal_delayed_feedback[i]
|
|
1853
1869
|
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1870
|
+
if self.inline_delay_expressions:
|
|
1871
|
+
# Get the indices for the delayed feedback variable in the optimization
|
|
1872
|
+
# vector
|
|
1873
|
+
indices = self.__indices[ensemble_member][in_canonical]
|
|
1874
|
+
|
|
1875
|
+
# Insert replacement values into appropriate slot
|
|
1876
|
+
delayed_feedback_variable_replacement[indices] = (
|
|
1877
|
+
x_out_delayed / in_nominal / in_sign
|
|
1878
|
+
)
|
|
1879
|
+
|
|
1880
|
+
# Equate delayed feedback variable to zero so that the numerical solver can
|
|
1881
|
+
# remove it from the problem.
|
|
1882
|
+
#
|
|
1883
|
+
# Note: The alternative approach would be to shrink self.solver_input=X,
|
|
1884
|
+
# self.__indices, and any other variables that directly or indirectly
|
|
1885
|
+
# depend on the size and order of 'X'. This would be a complex operation,
|
|
1886
|
+
# which would however be redundant for solvers that (like Ipopt) already
|
|
1887
|
+
# automatically detect and remove variables where lbx==ubx.
|
|
1888
|
+
lbx[indices] = 0.0
|
|
1889
|
+
ubx[indices] = 0.0
|
|
1890
|
+
else:
|
|
1891
|
+
# Default behavior: add delay expressions as equality constraints
|
|
1892
|
+
g.append((x_in - x_out_delayed) / nominal)
|
|
1893
|
+
zeros = np.zeros(n_collocation_times)
|
|
1894
|
+
lbg.extend(zeros)
|
|
1895
|
+
ubg.extend(zeros)
|
|
1858
1896
|
|
|
1859
1897
|
# Objective
|
|
1860
1898
|
f_member = self.objective(ensemble_member)
|
|
@@ -1868,7 +1906,7 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
1868
1906
|
f.append(self.ensemble_member_probability(ensemble_member) * f_member)
|
|
1869
1907
|
|
|
1870
1908
|
if logger.getEffectiveLevel() == logging.DEBUG:
|
|
1871
|
-
logger.debug("Adding objective {}"
|
|
1909
|
+
logger.debug(f"Adding objective {f_member}")
|
|
1872
1910
|
|
|
1873
1911
|
# Constraints
|
|
1874
1912
|
constraints = self.constraints(ensemble_member)
|
|
@@ -1898,9 +1936,7 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
1898
1936
|
elif lbg_i.shape[0] != g_i.shape[0]:
|
|
1899
1937
|
raise Exception(
|
|
1900
1938
|
"Shape mismatch between constraint "
|
|
1901
|
-
"#{} ({},) and its lower bound ({},)"
|
|
1902
|
-
i, g_i.shape[0], lbg_i.shape[0]
|
|
1903
|
-
)
|
|
1939
|
+
f"#{i} ({g_i.shape[0]},) and its lower bound ({lbg_i.shape[0]},)"
|
|
1904
1940
|
)
|
|
1905
1941
|
|
|
1906
1942
|
if not isinstance(ubg_i, np.ndarray) or ubg_i.shape[0] == 1:
|
|
@@ -1908,9 +1944,7 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
1908
1944
|
elif ubg_i.shape[0] != g_i.shape[0]:
|
|
1909
1945
|
raise Exception(
|
|
1910
1946
|
"Shape mismatch between constraint "
|
|
1911
|
-
"#{} ({},) and its upper bound ({},)"
|
|
1912
|
-
i, g_i.shape[0], ubg_i.shape[0]
|
|
1913
|
-
)
|
|
1947
|
+
f"#{i} ({g_i.shape[0]},) and its upper bound ({ubg_i.shape[0]},)"
|
|
1914
1948
|
)
|
|
1915
1949
|
|
|
1916
1950
|
g.extend(g_constraint)
|
|
@@ -1978,6 +2012,14 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
1978
2012
|
lbg.extend(lbg_path_constraints.transpose().ravel())
|
|
1979
2013
|
ubg.extend(ubg_path_constraints.transpose().ravel())
|
|
1980
2014
|
|
|
2015
|
+
# If inlining delay expressions, carry out the single call to ca.substitute()
|
|
2016
|
+
# A single call is more efficient as it constructs a single ca.Function internally,
|
|
2017
|
+
# rather than one for each element of the list. Also note that we do this _outside_
|
|
2018
|
+
# the ensemble member loop for speed reasons, as each substitute() roughly takes the
|
|
2019
|
+
# same amount of time regardless how many expressions we are replacing.
|
|
2020
|
+
if self.inline_delay_expressions:
|
|
2021
|
+
g = [ca.substitute(ca.vertcat(*g), X, delayed_feedback_variable_replacement)]
|
|
2022
|
+
|
|
1981
2023
|
# NLP function
|
|
1982
2024
|
logger.info("Creating NLP dictionary")
|
|
1983
2025
|
|
|
@@ -2021,7 +2063,7 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2021
2063
|
return self.__solver_input
|
|
2022
2064
|
|
|
2023
2065
|
def solver_options(self):
|
|
2024
|
-
options = super(
|
|
2066
|
+
options = super().solver_options()
|
|
2025
2067
|
|
|
2026
2068
|
solver = options["solver"]
|
|
2027
2069
|
assert solver in ["bonmin", "ipopt"]
|
|
@@ -2043,7 +2085,12 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2043
2085
|
def controls(self):
|
|
2044
2086
|
return self.__controls
|
|
2045
2087
|
|
|
2046
|
-
def _collint_get_lbx_ubx(
|
|
2088
|
+
def _collint_get_lbx_ubx(
|
|
2089
|
+
self,
|
|
2090
|
+
bounds: dict[str, BT] | list[dict[str, BT]],
|
|
2091
|
+
count: int,
|
|
2092
|
+
indices: list[dict[str, slice | int]],
|
|
2093
|
+
) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
2047
2094
|
lbx = np.full(count, -np.inf, dtype=np.float64)
|
|
2048
2095
|
ubx = np.full(count, np.inf, dtype=np.float64)
|
|
2049
2096
|
|
|
@@ -2054,6 +2101,11 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2054
2101
|
|
|
2055
2102
|
# Bounds, defaulting to +/- inf, if not set
|
|
2056
2103
|
for ensemble_member in range(self.ensemble_size):
|
|
2104
|
+
if self.ensemble_specific_bounds:
|
|
2105
|
+
bounds_member = bounds[ensemble_member]
|
|
2106
|
+
else:
|
|
2107
|
+
bounds_member = bounds
|
|
2108
|
+
|
|
2057
2109
|
for variable, inds in indices[ensemble_member].items():
|
|
2058
2110
|
variable_size = variable_sizes[variable]
|
|
2059
2111
|
|
|
@@ -2065,7 +2117,7 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2065
2117
|
n_times = len(times)
|
|
2066
2118
|
|
|
2067
2119
|
try:
|
|
2068
|
-
bound =
|
|
2120
|
+
bound = bounds_member[variable]
|
|
2069
2121
|
except KeyError:
|
|
2070
2122
|
pass
|
|
2071
2123
|
else:
|
|
@@ -2094,7 +2146,7 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2094
2146
|
)
|
|
2095
2147
|
else:
|
|
2096
2148
|
lower_bound = bound[0]
|
|
2097
|
-
lbx[inds] = lower_bound / nominal
|
|
2149
|
+
lbx[inds] = np.maximum(lbx[inds], lower_bound / nominal)
|
|
2098
2150
|
|
|
2099
2151
|
if bound[1] is not None:
|
|
2100
2152
|
if isinstance(bound[1], Timeseries):
|
|
@@ -2114,13 +2166,23 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2114
2166
|
)
|
|
2115
2167
|
else:
|
|
2116
2168
|
upper_bound = bound[1]
|
|
2117
|
-
ubx[inds] = upper_bound / nominal
|
|
2169
|
+
ubx[inds] = np.minimum(ubx[inds], upper_bound / nominal)
|
|
2118
2170
|
|
|
2119
2171
|
# Warn for NaNs
|
|
2120
2172
|
if np.any(np.isnan(lbx[inds])):
|
|
2121
|
-
logger.error("Lower bound on variable {} contains NaN"
|
|
2173
|
+
logger.error(f"Lower bound on variable {variable} contains NaN")
|
|
2122
2174
|
if np.any(np.isnan(ubx[inds])):
|
|
2123
|
-
logger.error("Upper bound on variable {} contains NaN"
|
|
2175
|
+
logger.error(f"Upper bound on variable {variable} contains NaN")
|
|
2176
|
+
|
|
2177
|
+
# Check that the lower bounds are not higher than the upper
|
|
2178
|
+
# bounds. To avoid spam, we just log the first offending one per
|
|
2179
|
+
# variable, not _all_ time steps.
|
|
2180
|
+
if np.any(lbx[inds] > ubx[inds]):
|
|
2181
|
+
error_inds = np.where(lbx[inds] > ubx[inds])[0].tolist()
|
|
2182
|
+
logger.error(
|
|
2183
|
+
f"Lower bound {lbx[inds][error_inds[0]] * nominal} is higher than "
|
|
2184
|
+
f"upper bound {ubx[inds][error_inds[0]] * nominal} for variable {variable}"
|
|
2185
|
+
)
|
|
2124
2186
|
|
|
2125
2187
|
return lbx, ubx
|
|
2126
2188
|
|
|
@@ -2154,15 +2216,16 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2154
2216
|
)
|
|
2155
2217
|
|
|
2156
2218
|
if isinstance(seed_k, Timeseries):
|
|
2157
|
-
|
|
2219
|
+
seed_k = (
|
|
2158
2220
|
self.interpolate(
|
|
2159
2221
|
times, seed_k.times, seed_k.values, 0, 0, interpolation_method
|
|
2160
2222
|
)
|
|
2161
2223
|
.transpose()
|
|
2162
2224
|
.ravel()
|
|
2163
2225
|
)
|
|
2164
|
-
|
|
2165
|
-
|
|
2226
|
+
if isinstance(inds, int) and isinstance(seed_k, np.ndarray):
|
|
2227
|
+
seed_k = seed_k.item()
|
|
2228
|
+
x0[inds] = seed_k
|
|
2166
2229
|
|
|
2167
2230
|
x0[inds] /= nominal
|
|
2168
2231
|
except KeyError:
|
|
@@ -2492,10 +2555,8 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2492
2555
|
else:
|
|
2493
2556
|
if not extrapolate and (t < times[0] or t > times[-1]):
|
|
2494
2557
|
raise Exception(
|
|
2495
|
-
"Cannot interpolate for {}: "
|
|
2496
|
-
"Point {} outside of range [{}, {}]"
|
|
2497
|
-
canonical, t, times[0], times[-1]
|
|
2498
|
-
)
|
|
2558
|
+
f"Cannot interpolate for {canonical}: "
|
|
2559
|
+
f"Point {t} outside of range [{times[0]}, {times[-1]}]"
|
|
2499
2560
|
)
|
|
2500
2561
|
|
|
2501
2562
|
interpolation_method = self.interpolation_method(canonical)
|
|
@@ -2583,9 +2644,8 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2583
2644
|
history_timeseries = history[canonical]
|
|
2584
2645
|
except KeyError:
|
|
2585
2646
|
raise Exception(
|
|
2586
|
-
"No history found for variable {},
|
|
2587
|
-
|
|
2588
|
-
)
|
|
2647
|
+
f"No history found for variable {variable}, "
|
|
2648
|
+
"but a historical value was requested"
|
|
2589
2649
|
)
|
|
2590
2650
|
else:
|
|
2591
2651
|
history_times = history_timeseries.times[:-1]
|
|
@@ -2612,10 +2672,21 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2612
2672
|
|
|
2613
2673
|
return x, t
|
|
2614
2674
|
|
|
2615
|
-
def states_in(
|
|
2616
|
-
|
|
2675
|
+
def states_in(
|
|
2676
|
+
self,
|
|
2677
|
+
variable: str,
|
|
2678
|
+
t0: float | None = None,
|
|
2679
|
+
tf: float | None = None,
|
|
2680
|
+
ensemble_member: int = 0,
|
|
2681
|
+
*,
|
|
2682
|
+
return_times: bool = False,
|
|
2683
|
+
) -> ca.MX | tuple[ca.DM, ca.MX]:
|
|
2684
|
+
x, t = self.__states_times_in(variable, t0, tf, ensemble_member)
|
|
2617
2685
|
|
|
2618
|
-
|
|
2686
|
+
if return_times:
|
|
2687
|
+
return x, t
|
|
2688
|
+
else:
|
|
2689
|
+
return x
|
|
2619
2690
|
|
|
2620
2691
|
def integral(self, variable, t0=None, tf=None, ensemble_member=0):
|
|
2621
2692
|
x, t = self.__states_times_in(variable, t0, tf, ensemble_member)
|
|
@@ -2754,11 +2825,9 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2754
2825
|
if len(ensemble_members) == 1:
|
|
2755
2826
|
ensemble_members = ensemble_members[0]
|
|
2756
2827
|
else:
|
|
2757
|
-
ensemble_members = "[{}]".format(
|
|
2758
|
-
",".join((str(x) for x in sorted(ensemble_members)))
|
|
2759
|
-
)
|
|
2828
|
+
ensemble_members = "[{}]".format(",".join(str(x) for x in sorted(ensemble_members)))
|
|
2760
2829
|
|
|
2761
|
-
var_names.append("{}__e{}__t{}"
|
|
2830
|
+
var_names.append(f"{var_name}__e{ensemble_members}__t{t_i}")
|
|
2762
2831
|
|
|
2763
2832
|
# Create named versions of the constraints
|
|
2764
2833
|
named_x = ca.vertcat(*(ca.SX.sym(v) for v in var_names))
|
|
@@ -2805,62 +2874,62 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2805
2874
|
lb, ub = lbg[i], ubg[i]
|
|
2806
2875
|
|
|
2807
2876
|
if np.isfinite(lb) and np.isfinite(ub) and lb == ub:
|
|
2808
|
-
c_str = "{} = {}"
|
|
2877
|
+
c_str = f"{c_str} = {lb}"
|
|
2809
2878
|
elif np.isfinite(lb) and np.isfinite(ub):
|
|
2810
|
-
c_str = "{} <= {} <= {}"
|
|
2879
|
+
c_str = f"{lb} <= {c_str} <= {ub}"
|
|
2811
2880
|
elif np.isfinite(lb):
|
|
2812
|
-
c_str = "{} >= {}"
|
|
2881
|
+
c_str = f"{c_str} >= {lb}"
|
|
2813
2882
|
elif np.isfinite(ub):
|
|
2814
|
-
c_str = "{} <= {}"
|
|
2883
|
+
c_str = f"{c_str} <= {ub}"
|
|
2815
2884
|
|
|
2816
2885
|
return c_str
|
|
2817
2886
|
|
|
2818
2887
|
# Checking for right hand side of constraints
|
|
2819
|
-
logger.info("Sanity check of lbg and ubg, checking for small values (<{})"
|
|
2888
|
+
logger.info(f"Sanity check of lbg and ubg, checking for small values (<{tol_zero})")
|
|
2820
2889
|
|
|
2821
2890
|
lbg_abs_no_zero = np.abs(lbg.copy())
|
|
2822
2891
|
lbg_abs_no_zero[lbg_abs_no_zero == 0.0] = +np.inf
|
|
2823
2892
|
ind = np.argmin(lbg_abs_no_zero)
|
|
2824
2893
|
if np.any(np.isfinite(lbg_abs_no_zero)):
|
|
2825
|
-
logger.info("Smallest (absolute) lbg coefficient {
|
|
2826
|
-
logger.info("E.g., {
|
|
2894
|
+
logger.info(f"Smallest (absolute) lbg coefficient {lbg_abs_no_zero[ind]}")
|
|
2895
|
+
logger.info(f"E.g., {constr_to_str(ind)}")
|
|
2827
2896
|
lbg_inds = lbg_abs_no_zero < tol_zero
|
|
2828
2897
|
if np.any(lbg_inds):
|
|
2829
|
-
logger.info("Too small of a (absolute) lbg found: {
|
|
2898
|
+
logger.info(f"Too small of a (absolute) lbg found: {min(lbg[lbg_inds])}")
|
|
2830
2899
|
|
|
2831
2900
|
ubg_abs_no_zero = np.abs(ubg.copy())
|
|
2832
2901
|
ubg_abs_no_zero[ubg_abs_no_zero == 0.0] = +np.inf
|
|
2833
2902
|
ind = np.argmin(ubg_abs_no_zero)
|
|
2834
2903
|
if np.any(np.isfinite(ubg_abs_no_zero)):
|
|
2835
|
-
logger.info("Smallest (absolute) ubg coefficient {
|
|
2836
|
-
logger.info("E.g., {
|
|
2904
|
+
logger.info(f"Smallest (absolute) ubg coefficient {ubg_abs_no_zero[ind]}")
|
|
2905
|
+
logger.info(f"E.g., {constr_to_str(ind)}")
|
|
2837
2906
|
ubg_inds = ubg_abs_no_zero < tol_zero
|
|
2838
2907
|
if np.any(ubg_inds):
|
|
2839
|
-
logger.info("Too small of a (absolute) ubg found: {
|
|
2908
|
+
logger.info(f"Too small of a (absolute) ubg found: {min(ubg[ubg_inds])}")
|
|
2840
2909
|
|
|
2841
|
-
logger.info("Sanity check of lbg and ubg, checking for large values (>{})"
|
|
2910
|
+
logger.info(f"Sanity check of lbg and ubg, checking for large values (>{tol_rhs})")
|
|
2842
2911
|
|
|
2843
2912
|
lbg_abs_no_inf = np.abs(lbg.copy())
|
|
2844
2913
|
lbg_abs_no_inf[~np.isfinite(lbg_abs_no_inf)] = -np.inf
|
|
2845
2914
|
ind = np.argmax(lbg_abs_no_inf)
|
|
2846
2915
|
if np.any(np.isfinite(lbg_abs_no_inf)):
|
|
2847
|
-
logger.info("Largest (absolute) lbg coefficient {
|
|
2848
|
-
logger.info("E.g., {
|
|
2916
|
+
logger.info(f"Largest (absolute) lbg coefficient {lbg_abs_no_inf[ind]}")
|
|
2917
|
+
logger.info(f"E.g., {constr_to_str(ind)}")
|
|
2849
2918
|
|
|
2850
2919
|
lbg_inds = lbg_abs_no_inf > tol_rhs
|
|
2851
2920
|
if np.any(lbg_inds):
|
|
2852
|
-
raise Exception("Too large of a (absolute) lbg found: {
|
|
2921
|
+
raise Exception(f"Too large of a (absolute) lbg found: {max(lbg[lbg_inds])}")
|
|
2853
2922
|
|
|
2854
2923
|
ubg_abs_no_inf = np.abs(ubg.copy())
|
|
2855
2924
|
ubg_abs_no_inf[~np.isfinite(ubg)] = -np.inf
|
|
2856
2925
|
ind = np.argmax(ubg_abs_no_inf)
|
|
2857
2926
|
if np.any(np.isfinite(ubg_abs_no_inf)):
|
|
2858
|
-
logger.info("Largest (absolute) ubg coefficient {
|
|
2859
|
-
logger.info("E.g., {
|
|
2927
|
+
logger.info(f"Largest (absolute) ubg coefficient {ubg_abs_no_inf[ind]}")
|
|
2928
|
+
logger.info(f"E.g., {constr_to_str(ind)}")
|
|
2860
2929
|
|
|
2861
2930
|
ubg_inds = ubg_abs_no_inf > tol_rhs
|
|
2862
2931
|
if np.any(ubg_inds):
|
|
2863
|
-
raise Exception("Too large of a (absolute) ubg found: {
|
|
2932
|
+
raise Exception(f"Too large of a (absolute) ubg found: {max(ubg[ubg_inds])}")
|
|
2864
2933
|
|
|
2865
2934
|
eval_point = x0 if evaluate_at_x0 else 1.0
|
|
2866
2935
|
eval_point_str = "x0" if evaluate_at_x0 else "1.0"
|
|
@@ -2887,25 +2956,20 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2887
2956
|
# Objective
|
|
2888
2957
|
A_obj, b_obj = out[0]
|
|
2889
2958
|
logger.info(
|
|
2890
|
-
"Statistics of objective: max & min of abs(jac(f, {})))
|
|
2891
|
-
|
|
2892
|
-
)
|
|
2959
|
+
f"Statistics of objective: max & min of abs(jac(f, {eval_point_str}))) "
|
|
2960
|
+
f"f({eval_point_str}), constants"
|
|
2893
2961
|
)
|
|
2894
2962
|
max_obj_A = max(np.abs(A_obj.data), default=None)
|
|
2895
2963
|
min_obj_A = min(np.abs(A_obj.data[A_obj.data != 0.0]), default=None)
|
|
2896
2964
|
obj_x0 = np.array(ca.Function("tmp", [in_var], [nlp["f"]])(eval_point)).ravel()[0]
|
|
2897
2965
|
obj_b = b_obj.data[0] if len(b_obj.data) > 0 else 0.0
|
|
2898
2966
|
|
|
2899
|
-
logger.info("{} & {}, {}, {}"
|
|
2967
|
+
logger.info(f"{max_obj_A} & {min_obj_A}, {obj_x0}, {obj_b}")
|
|
2900
2968
|
|
|
2901
2969
|
if abs(obj_b) > tol_up:
|
|
2902
|
-
logger.info(
|
|
2903
|
-
"Constant '{}' in objective exceeds upper tolerance of '{}'".format(obj_b, tol_up)
|
|
2904
|
-
)
|
|
2970
|
+
logger.info(f"Constant '{obj_b}' in objective exceeds upper tolerance of '{tol_up}'")
|
|
2905
2971
|
if abs(obj_b) > tol_up:
|
|
2906
|
-
logger.info(
|
|
2907
|
-
"Objective value at x0 '{}' exceeds upper tolerance of '{}'".format(obj_x0, tol_up)
|
|
2908
|
-
)
|
|
2972
|
+
logger.info(f"Objective value at x0 '{obj_x0}' exceeds upper tolerance of '{tol_up}'")
|
|
2909
2973
|
|
|
2910
2974
|
# Constraints
|
|
2911
2975
|
A_constr, b_constr = out[1]
|
|
@@ -2916,12 +2980,21 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2916
2980
|
min_constr_A = min(np.abs(A_constr.data[A_constr.data != 0.0]), default=None)
|
|
2917
2981
|
max_constr_b = max(np.abs(b_constr.data), default=None)
|
|
2918
2982
|
min_constr_b = min(np.abs(b_constr.data[b_constr.data != 0.0]), default=None)
|
|
2919
|
-
logger.info(
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2983
|
+
logger.info(f"{max_constr_A} & {min_constr_A}, {max_constr_b} & {min_constr_b}")
|
|
2984
|
+
|
|
2985
|
+
# Filter out exactly zero, as those entries do not show up in the
|
|
2986
|
+
# matrix. Shut up SonarCloud warning about this exact-to-zero
|
|
2987
|
+
# comparison.
|
|
2988
|
+
maxs = [
|
|
2989
|
+
x
|
|
2990
|
+
for x in [max_constr_A, max_constr_b, max_obj_A, obj_b]
|
|
2991
|
+
if x is not None and x != 0.0 # NOSONAR
|
|
2992
|
+
]
|
|
2993
|
+
mins = [
|
|
2994
|
+
x
|
|
2995
|
+
for x in [min_constr_A, min_constr_b, min_obj_A, obj_b]
|
|
2996
|
+
if x is not None and x != 0.0 # NOSONAR
|
|
2997
|
+
]
|
|
2925
2998
|
if (maxs and max(maxs) > tol_up) or (mins and min(mins) < tol_down):
|
|
2926
2999
|
logger.info("Jacobian matrix /constants coefficients values outside typical range!")
|
|
2927
3000
|
|
|
@@ -2950,25 +3023,17 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
2950
3023
|
if exceedences:
|
|
2951
3024
|
logger.info(
|
|
2952
3025
|
"Exceedence in jacobian of constraints evaluated at x0"
|
|
2953
|
-
" (max > {:g}, min < {:g}, or max / min > {:g}):"
|
|
2954
|
-
tol_up, tol_down, tol_range
|
|
2955
|
-
)
|
|
3026
|
+
f" (max > {tol_up:g}, min < {tol_down:g}, or max / min > {tol_range:g}):"
|
|
2956
3027
|
)
|
|
2957
3028
|
|
|
2958
3029
|
exceedences = sorted(exceedences, key=lambda x: x[1] / x[2], reverse=True)
|
|
2959
3030
|
|
|
2960
3031
|
for i, (r, max_r, min_r, c) in enumerate(exceedences):
|
|
2961
|
-
logger.info(
|
|
2962
|
-
"row {} (max: {}, min: {}, range: {}): {}".format(
|
|
2963
|
-
r, max_r, min_r, max_r / min_r, c
|
|
2964
|
-
)
|
|
2965
|
-
)
|
|
3032
|
+
logger.info(f"row {r} (max: {max_r}, min: {min_r}, range: {max_r / min_r}): {c}")
|
|
2966
3033
|
|
|
2967
3034
|
if i >= 9:
|
|
2968
3035
|
logger.info(
|
|
2969
|
-
"Too many warnings of same type ({} others remain)."
|
|
2970
|
-
len(exceedences) - 10
|
|
2971
|
-
)
|
|
3036
|
+
f"Too many warnings of same type ({len(exceedences) - 10} others remain)."
|
|
2972
3037
|
)
|
|
2973
3038
|
break
|
|
2974
3039
|
|
|
@@ -3021,24 +3086,18 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
3021
3086
|
|
|
3022
3087
|
exceedences = sorted(exceedences, key=lambda x: x[1], reverse=True)
|
|
3023
3088
|
|
|
3024
|
-
logger.info("Max range found: {}"
|
|
3089
|
+
logger.info(f"Max range found: {max_range_found}")
|
|
3025
3090
|
if exceedences:
|
|
3026
|
-
logger.info("Exceedence in range per column (max / min > {:g}):"
|
|
3091
|
+
logger.info(f"Exceedence in range per column (max / min > {tol_range:g}):")
|
|
3027
3092
|
|
|
3028
3093
|
for i, (c, exc, min_, max_, c_min_str, c_max_str) in enumerate(exceedences):
|
|
3029
|
-
logger.info(
|
|
3030
|
-
"col {} ({}): range {}, min {}, max {}".format(
|
|
3031
|
-
c, var_names[c], exc, min_, max_
|
|
3032
|
-
)
|
|
3033
|
-
)
|
|
3094
|
+
logger.info(f"col {c} ({var_names[c]}): range {exc}, min {min_}, max {max_}")
|
|
3034
3095
|
logger.info(c_min_str)
|
|
3035
3096
|
logger.info(c_max_str)
|
|
3036
3097
|
|
|
3037
3098
|
if i >= 9:
|
|
3038
3099
|
logger.info(
|
|
3039
|
-
"Too many warnings of same type ({} others remain)."
|
|
3040
|
-
len(exceedences) - 10
|
|
3041
|
-
)
|
|
3100
|
+
f"Too many warnings of same type ({len(exceedences) - 10} others remain)."
|
|
3042
3101
|
)
|
|
3043
3102
|
break
|
|
3044
3103
|
|
|
@@ -3064,24 +3123,19 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
3064
3123
|
if max_range > tol_range:
|
|
3065
3124
|
exceedences.append((c, max_range, obj_coeff, min_r, max_r))
|
|
3066
3125
|
|
|
3067
|
-
logger.info("Max range found: {}"
|
|
3126
|
+
logger.info(f"Max range found: {max_range_found}")
|
|
3068
3127
|
if exceedences:
|
|
3069
|
-
logger.info(
|
|
3070
|
-
"Exceedence in range of objective variable (range > {:g}):".format(tol_range)
|
|
3071
|
-
)
|
|
3128
|
+
logger.info(f"Exceedence in range of objective variable (range > {tol_range:g}):")
|
|
3072
3129
|
|
|
3073
3130
|
for i, (c, max_range, obj_coeff, min_r, max_r) in enumerate(exceedences):
|
|
3074
3131
|
logger.info(
|
|
3075
|
-
"col {} ({}): range: {}, obj: {},
|
|
3076
|
-
|
|
3077
|
-
)
|
|
3132
|
+
f"col {c} ({var_names[c]}): range: {max_range}, obj: {obj_coeff}, "
|
|
3133
|
+
f"min constr: {min_r}, max constr {max_r}"
|
|
3078
3134
|
)
|
|
3079
3135
|
|
|
3080
3136
|
if i >= 9:
|
|
3081
3137
|
logger.info(
|
|
3082
|
-
"Too many warnings of same type ({} others remain)."
|
|
3083
|
-
len(exceedences) - 10
|
|
3084
|
-
)
|
|
3138
|
+
f"Too many warnings of same type ({len(exceedences) - 10} others remain)."
|
|
3085
3139
|
)
|
|
3086
3140
|
break
|
|
3087
3141
|
|
|
@@ -3125,11 +3179,11 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
3125
3179
|
if exceedences:
|
|
3126
3180
|
logger.info(
|
|
3127
3181
|
"Variables with at least one (absolute) state vector entry/entries "
|
|
3128
|
-
"larger than {}"
|
|
3182
|
+
f"larger than {tol_up}"
|
|
3129
3183
|
)
|
|
3130
3184
|
|
|
3131
3185
|
for k, v in exceedences:
|
|
3132
|
-
logger.info("{}: abs max = {}"
|
|
3186
|
+
logger.info(f"{k}: abs max = {v}")
|
|
3133
3187
|
|
|
3134
3188
|
if len(inds_down) > 0:
|
|
3135
3189
|
exceedences = []
|
|
@@ -3144,11 +3198,11 @@ class CollocatedIntegratedOptimizationProblem(OptimizationProblem, metaclass=ABC
|
|
|
3144
3198
|
ignore_all_zero_string = " (but not all zero)" if ignore_all_zero else ""
|
|
3145
3199
|
logger.info(
|
|
3146
3200
|
"Variables with all (absolute) state vector entry/entries "
|
|
3147
|
-
"smaller than {}{}"
|
|
3201
|
+
f"smaller than {tol_down}{ignore_all_zero_string}"
|
|
3148
3202
|
)
|
|
3149
3203
|
|
|
3150
3204
|
for k, v in exceedences:
|
|
3151
3205
|
if ignore_all_zero and v == 0.0:
|
|
3152
3206
|
continue
|
|
3153
3207
|
|
|
3154
|
-
logger.info("{}: abs max = {}"
|
|
3208
|
+
logger.info(f"{k}: abs max = {v}")
|
|
@@ -290,7 +290,10 @@ class ModelicaMixin(OptimizationProblem):
|
|
|
290
290
|
# If start contains symbolics, try substituting parameter values
|
|
291
291
|
if isinstance(start, ca.MX) and not start.is_constant():
|
|
292
292
|
[start] = substitute_in_external(
|
|
293
|
-
[start],
|
|
293
|
+
[start],
|
|
294
|
+
self.__mx["parameters"],
|
|
295
|
+
parameter_values,
|
|
296
|
+
resolve_numerically=True,
|
|
294
297
|
)
|
|
295
298
|
if not start.is_constant() or np.isnan(float(start)):
|
|
296
299
|
raise Exception(
|
|
@@ -341,7 +344,9 @@ class ModelicaMixin(OptimizationProblem):
|
|
|
341
344
|
|
|
342
345
|
m_ = v.min
|
|
343
346
|
if isinstance(m_, ca.MX) and not m_.is_constant():
|
|
344
|
-
[m_] = substitute_in_external(
|
|
347
|
+
[m_] = substitute_in_external(
|
|
348
|
+
[m_], self.__mx["parameters"], parameter_values, resolve_numerically=True
|
|
349
|
+
)
|
|
345
350
|
if not m_.is_constant() or np.isnan(float(m_)):
|
|
346
351
|
raise Exception(
|
|
347
352
|
"Could not resolve lower bound for variable {}".format(sym_name)
|
|
@@ -350,7 +355,9 @@ class ModelicaMixin(OptimizationProblem):
|
|
|
350
355
|
|
|
351
356
|
M_ = v.max
|
|
352
357
|
if isinstance(M_, ca.MX) and not M_.is_constant():
|
|
353
|
-
[M_] = substitute_in_external(
|
|
358
|
+
[M_] = substitute_in_external(
|
|
359
|
+
[M_], self.__mx["parameters"], parameter_values, resolve_numerically=True
|
|
360
|
+
)
|
|
354
361
|
if not M_.is_constant() or np.isnan(float(M_)):
|
|
355
362
|
raise Exception(
|
|
356
363
|
"Could not resolve upper bound for variable {}".format(sym_name)
|
|
@@ -390,7 +397,7 @@ class ModelicaMixin(OptimizationProblem):
|
|
|
390
397
|
# If start contains symbolics, try substituting parameter values
|
|
391
398
|
if isinstance(start, ca.MX) and not start.is_constant():
|
|
392
399
|
[start] = substitute_in_external(
|
|
393
|
-
[start], self.__mx["parameters"], parameter_values
|
|
400
|
+
[start], self.__mx["parameters"], parameter_values, resolve_numerically=True
|
|
394
401
|
)
|
|
395
402
|
if not start.is_constant() or np.isnan(float(start)):
|
|
396
403
|
logger.error(
|
|
@@ -437,7 +444,7 @@ class ModelicaMixin(OptimizationProblem):
|
|
|
437
444
|
# If nominal contains parameter symbols, substitute them
|
|
438
445
|
if isinstance(nominal, ca.MX) and not nominal.is_constant():
|
|
439
446
|
[nominal] = substitute_in_external(
|
|
440
|
-
[nominal], self.__mx["parameters"], parameter_values
|
|
447
|
+
[nominal], self.__mx["parameters"], parameter_values, resolve_numerically=True
|
|
441
448
|
)
|
|
442
449
|
if not nominal.is_constant() or np.isnan(float(nominal)):
|
|
443
450
|
logger.error(
|
|
@@ -46,6 +46,9 @@ class OptimizationProblem(DataStoreAccessor, metaclass=ABCMeta):
|
|
|
46
46
|
_debug_check_level = DebugLevel.MEDIUM
|
|
47
47
|
_debug_check_options = {}
|
|
48
48
|
|
|
49
|
+
#: Enable ensemble-specific bounds functionality
|
|
50
|
+
ensemble_specific_bounds = False
|
|
51
|
+
|
|
49
52
|
def __init__(self, **kwargs):
|
|
50
53
|
# Call parent class first for default behaviour.
|
|
51
54
|
super().__init__(**kwargs)
|
{rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/single_pass_goal_programming_mixin.py
RENAMED
|
@@ -143,7 +143,26 @@ class SinglePassGoalProgrammingMixin(_GoalProgrammingMixinBase):
|
|
|
143
143
|
|
|
144
144
|
def seed(self, ensemble_member):
|
|
145
145
|
assert self._gp_first_run
|
|
146
|
-
|
|
146
|
+
|
|
147
|
+
seed = super().seed(ensemble_member)
|
|
148
|
+
|
|
149
|
+
# Seed epsilons of current priority
|
|
150
|
+
for epsilon in self.__problem_epsilons:
|
|
151
|
+
eps_size = epsilon.size1()
|
|
152
|
+
if eps_size > 1:
|
|
153
|
+
seed[epsilon.name()] = np.ones(eps_size)
|
|
154
|
+
else:
|
|
155
|
+
seed[epsilon.name()] = 1.0
|
|
156
|
+
|
|
157
|
+
times = self.times()
|
|
158
|
+
for epsilon in self.__problem_path_epsilons:
|
|
159
|
+
eps_size = epsilon.size1()
|
|
160
|
+
if eps_size > 1:
|
|
161
|
+
seed[epsilon.name()] = Timeseries(times, np.ones((eps_size, len(times))))
|
|
162
|
+
else:
|
|
163
|
+
seed[epsilon.name()] = Timeseries(times, np.ones(len(times)))
|
|
164
|
+
|
|
165
|
+
return seed
|
|
147
166
|
|
|
148
167
|
def constraints(self, ensemble_member):
|
|
149
168
|
constraints = super().constraints(ensemble_member)
|
|
@@ -105,7 +105,7 @@ def download_examples(*args):
|
|
|
105
105
|
|
|
106
106
|
version = rtctools.__version__
|
|
107
107
|
try:
|
|
108
|
-
url = "https://github.com/
|
|
108
|
+
url = "https://github.com/rtc-tools/rtc-tools/zipball/{}".format(version)
|
|
109
109
|
|
|
110
110
|
opener = urllib.request.build_opener()
|
|
111
111
|
urllib.request.install_opener(opener)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/goal_programming_mixin_base.py
RENAMED
|
File without changes
|
|
File without changes
|
{rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/initial_state_estimation_mixin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rtc_tools-2.7.1 → rtc_tools-2.7.3}/src/rtctools/optimization/min_abs_goal_programming_mixin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|