rtc-tools 2.7.3__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.
- rtc_tools-2.7.3.dist-info/METADATA +53 -0
- rtc_tools-2.7.3.dist-info/RECORD +50 -0
- rtc_tools-2.7.3.dist-info/WHEEL +5 -0
- rtc_tools-2.7.3.dist-info/entry_points.txt +3 -0
- rtc_tools-2.7.3.dist-info/licenses/COPYING.LESSER +165 -0
- rtc_tools-2.7.3.dist-info/top_level.txt +1 -0
- rtctools/__init__.py +5 -0
- rtctools/_internal/__init__.py +0 -0
- rtctools/_internal/alias_tools.py +188 -0
- rtctools/_internal/caching.py +25 -0
- rtctools/_internal/casadi_helpers.py +99 -0
- rtctools/_internal/debug_check_helpers.py +41 -0
- rtctools/_version.py +21 -0
- rtctools/data/__init__.py +4 -0
- rtctools/data/csv.py +150 -0
- rtctools/data/interpolation/__init__.py +3 -0
- rtctools/data/interpolation/bspline.py +31 -0
- rtctools/data/interpolation/bspline1d.py +169 -0
- rtctools/data/interpolation/bspline2d.py +54 -0
- rtctools/data/netcdf.py +467 -0
- rtctools/data/pi.py +1236 -0
- rtctools/data/rtc.py +228 -0
- rtctools/data/storage.py +343 -0
- rtctools/optimization/__init__.py +0 -0
- rtctools/optimization/collocated_integrated_optimization_problem.py +3208 -0
- rtctools/optimization/control_tree_mixin.py +221 -0
- rtctools/optimization/csv_lookup_table_mixin.py +462 -0
- rtctools/optimization/csv_mixin.py +300 -0
- rtctools/optimization/goal_programming_mixin.py +769 -0
- rtctools/optimization/goal_programming_mixin_base.py +1094 -0
- rtctools/optimization/homotopy_mixin.py +165 -0
- rtctools/optimization/initial_state_estimation_mixin.py +89 -0
- rtctools/optimization/io_mixin.py +320 -0
- rtctools/optimization/linearization_mixin.py +33 -0
- rtctools/optimization/linearized_order_goal_programming_mixin.py +235 -0
- rtctools/optimization/min_abs_goal_programming_mixin.py +385 -0
- rtctools/optimization/modelica_mixin.py +482 -0
- rtctools/optimization/netcdf_mixin.py +177 -0
- rtctools/optimization/optimization_problem.py +1302 -0
- rtctools/optimization/pi_mixin.py +292 -0
- rtctools/optimization/planning_mixin.py +19 -0
- rtctools/optimization/single_pass_goal_programming_mixin.py +676 -0
- rtctools/optimization/timeseries.py +56 -0
- rtctools/rtctoolsapp.py +131 -0
- rtctools/simulation/__init__.py +0 -0
- rtctools/simulation/csv_mixin.py +171 -0
- rtctools/simulation/io_mixin.py +195 -0
- rtctools/simulation/pi_mixin.py +255 -0
- rtctools/simulation/simulation_problem.py +1293 -0
- rtctools/util.py +241 -0
rtctools/util.py
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import cProfile
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import pstats
|
|
5
|
+
import re
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
import casadi
|
|
9
|
+
|
|
10
|
+
from . import __version__
|
|
11
|
+
from ._internal.alias_tools import OrderedSet
|
|
12
|
+
from .data import pi
|
|
13
|
+
from .optimization.csv_mixin import CSVMixin as OptimizationCSVMixin
|
|
14
|
+
from .optimization.io_mixin import IOMixin as OptimizationIOMixin
|
|
15
|
+
from .optimization.pi_mixin import PIMixin as OptimizationPIMixin
|
|
16
|
+
from .simulation.csv_mixin import CSVMixin as SimulationCSVMixin
|
|
17
|
+
from .simulation.io_mixin import IOMixin as SimulationIOMixin
|
|
18
|
+
from .simulation.pi_mixin import PIMixin as SimulationPIMixin
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _resolve_folder(kwargs, base_folder, subfolder_kw, default):
|
|
22
|
+
subfolder = kwargs.pop(subfolder_kw, default)
|
|
23
|
+
if os.path.isabs(subfolder):
|
|
24
|
+
return subfolder
|
|
25
|
+
else:
|
|
26
|
+
return os.path.join(base_folder, subfolder)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def run_optimization_problem(
|
|
30
|
+
optimization_problem_class, base_folder="..", log_level=logging.INFO, profile=False, **kwargs
|
|
31
|
+
):
|
|
32
|
+
"""
|
|
33
|
+
Sets up and solves an optimization problem.
|
|
34
|
+
|
|
35
|
+
This function makes the following assumptions:
|
|
36
|
+
|
|
37
|
+
1. That the ``base_folder`` contains subfolders ``input``, ``output``, and ``model``,
|
|
38
|
+
containing input data, output data, and the model, respectively.
|
|
39
|
+
2. When using :class:`.CSVLookupTableMixin`, that the base folder contains
|
|
40
|
+
a subfolder ``lookup_tables``.
|
|
41
|
+
3. When using :class:`.ModelicaMixin`, that the base folder contains
|
|
42
|
+
a subfolder ``model``.
|
|
43
|
+
4. When using :class:`.ModelicaMixin`, that the toplevel Modelica model name
|
|
44
|
+
equals the class name.
|
|
45
|
+
|
|
46
|
+
:param optimization_problem_class: Optimization problem class to solve.
|
|
47
|
+
:param base_folder: Base folder.
|
|
48
|
+
:param log_level: The log level to use.
|
|
49
|
+
:param profile: Whether or not to enable profiling.
|
|
50
|
+
|
|
51
|
+
:returns: :class:`.OptimizationProblem` instance.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
# Do some checks on the problem class
|
|
55
|
+
if issubclass(optimization_problem_class, SimulationCSVMixin):
|
|
56
|
+
raise ValueError(
|
|
57
|
+
"Optimization problem class cannot be derived from simulation.csv_mixin.CSVMixin."
|
|
58
|
+
)
|
|
59
|
+
if issubclass(optimization_problem_class, SimulationIOMixin):
|
|
60
|
+
raise ValueError(
|
|
61
|
+
"Optimization problem class cannot be derived from simulation.csv_mixin.IOMixin."
|
|
62
|
+
)
|
|
63
|
+
if issubclass(optimization_problem_class, SimulationPIMixin):
|
|
64
|
+
raise ValueError(
|
|
65
|
+
"Optimization problem class cannot be derived from simulation.csv_mixin.PIMixin."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Set input/output folders
|
|
69
|
+
if not os.path.isabs(base_folder):
|
|
70
|
+
# Resolve base folder relative to script folder
|
|
71
|
+
base_folder = os.path.join(sys.path[0], base_folder)
|
|
72
|
+
|
|
73
|
+
model_folder = _resolve_folder(kwargs, base_folder, "model_folder", "model")
|
|
74
|
+
input_folder = _resolve_folder(kwargs, base_folder, "input_folder", "input")
|
|
75
|
+
output_folder = _resolve_folder(kwargs, base_folder, "output_folder", "output")
|
|
76
|
+
|
|
77
|
+
# Set up logging
|
|
78
|
+
logger = logging.getLogger("rtctools")
|
|
79
|
+
|
|
80
|
+
# Add stream handler if it does not already exist.
|
|
81
|
+
if not logger.hasHandlers() and not any(
|
|
82
|
+
(isinstance(h, logging.StreamHandler) for h in logger.handlers)
|
|
83
|
+
):
|
|
84
|
+
handler = logging.StreamHandler()
|
|
85
|
+
formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
|
|
86
|
+
handler.setFormatter(formatter)
|
|
87
|
+
logger.addHandler(handler)
|
|
88
|
+
|
|
89
|
+
# Add pi.DiagHandler, if using PIMixin. Only add it if it does not already exist.
|
|
90
|
+
if issubclass(optimization_problem_class, OptimizationPIMixin) and not any(
|
|
91
|
+
(isinstance(h, pi.DiagHandler) for h in logger.handlers)
|
|
92
|
+
):
|
|
93
|
+
handler = pi.DiagHandler(output_folder)
|
|
94
|
+
logger.addHandler(handler)
|
|
95
|
+
|
|
96
|
+
# Set log level
|
|
97
|
+
logger.setLevel(log_level)
|
|
98
|
+
|
|
99
|
+
# Log version info
|
|
100
|
+
logger.info("Using RTC-Tools {}.".format(__version__))
|
|
101
|
+
logger.debug("Using CasADi {}.".format(casadi.__version__))
|
|
102
|
+
|
|
103
|
+
# Check for some common mistakes in inheritance order
|
|
104
|
+
suggested_order = OrderedSet(
|
|
105
|
+
[
|
|
106
|
+
"HomotopyMixin",
|
|
107
|
+
"MinAbsGoalProgrammingMixin",
|
|
108
|
+
"LinearizedOrderGoalProgrammingMixin",
|
|
109
|
+
"SinglePassGoalProgrammingMixin",
|
|
110
|
+
"GoalProgrammingMixin",
|
|
111
|
+
"PIMixin",
|
|
112
|
+
"CSVMixin",
|
|
113
|
+
"IOMixin",
|
|
114
|
+
"ModelicaMixin",
|
|
115
|
+
"PlanningMixin",
|
|
116
|
+
"ControlTreeMixin",
|
|
117
|
+
"CollocatedIntegratedOptimizationProblem",
|
|
118
|
+
"OptimizationProblem",
|
|
119
|
+
]
|
|
120
|
+
)
|
|
121
|
+
base_names = OrderedSet([b.__name__ for b in optimization_problem_class.__bases__])
|
|
122
|
+
if suggested_order & base_names != base_names & suggested_order:
|
|
123
|
+
msg = "Please inherit from base classes in the following order: {}".format(
|
|
124
|
+
list(base_names & suggested_order)
|
|
125
|
+
)
|
|
126
|
+
logger.error(msg)
|
|
127
|
+
raise Exception(msg)
|
|
128
|
+
|
|
129
|
+
# Run
|
|
130
|
+
try:
|
|
131
|
+
prob = optimization_problem_class(
|
|
132
|
+
model_folder=model_folder,
|
|
133
|
+
input_folder=input_folder,
|
|
134
|
+
output_folder=output_folder,
|
|
135
|
+
**kwargs,
|
|
136
|
+
)
|
|
137
|
+
if profile:
|
|
138
|
+
filename = os.path.join(base_folder, "profile.prof")
|
|
139
|
+
|
|
140
|
+
cProfile.runctx("prob.optimize()", globals(), locals(), filename)
|
|
141
|
+
|
|
142
|
+
s = pstats.Stats(filename)
|
|
143
|
+
s.strip_dirs().sort_stats("time").print_stats()
|
|
144
|
+
else:
|
|
145
|
+
prob.optimize()
|
|
146
|
+
return prob
|
|
147
|
+
except Exception as e:
|
|
148
|
+
logger.error(str(e))
|
|
149
|
+
if isinstance(e, TypeError):
|
|
150
|
+
exc_info = sys.exc_info()
|
|
151
|
+
value = exc_info[1]
|
|
152
|
+
try:
|
|
153
|
+
failed_class = re.search(
|
|
154
|
+
"Can't instantiate (.*) with abstract methods", str(value)
|
|
155
|
+
).group(1)
|
|
156
|
+
abstract_method = re.search(" with abstract methods (.*)", str(value)).group(1)
|
|
157
|
+
logger.error(
|
|
158
|
+
"The {} is missing a mixin. Please add a mixin that instantiates "
|
|
159
|
+
"abstract method {}, so that the optimizer can run.".format(
|
|
160
|
+
failed_class, abstract_method
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
except Exception:
|
|
164
|
+
pass
|
|
165
|
+
for handler in logger.handlers:
|
|
166
|
+
handler.flush()
|
|
167
|
+
raise
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def run_simulation_problem(
|
|
171
|
+
simulation_problem_class, base_folder="..", log_level=logging.INFO, **kwargs
|
|
172
|
+
):
|
|
173
|
+
"""
|
|
174
|
+
Sets up and runs a simulation problem.
|
|
175
|
+
|
|
176
|
+
:param simulation_problem_class: Simulation problem class to solve.
|
|
177
|
+
:param base_folder: Folder within which subfolders "input", "output", and "model"
|
|
178
|
+
exist, containing input and output data, and the model, respectively.
|
|
179
|
+
:param log_level: The log level to use.
|
|
180
|
+
|
|
181
|
+
:returns: :class:`SimulationProblem` instance.
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
# Do some checks on the problem class
|
|
185
|
+
if issubclass(simulation_problem_class, OptimizationCSVMixin):
|
|
186
|
+
raise ValueError(
|
|
187
|
+
"Simulation problem class cannot be derived from optimization.csv_mixin.CSVMixin."
|
|
188
|
+
)
|
|
189
|
+
if issubclass(simulation_problem_class, OptimizationIOMixin):
|
|
190
|
+
raise ValueError(
|
|
191
|
+
"Simulation problem class cannot be derived from optimization.csv_mixin.IOMixin."
|
|
192
|
+
)
|
|
193
|
+
if issubclass(simulation_problem_class, OptimizationPIMixin):
|
|
194
|
+
raise ValueError(
|
|
195
|
+
"Simulation problem class cannot be derived from optimization.csv_mixin.PIMixin."
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Set input/output folders
|
|
199
|
+
if base_folder is None:
|
|
200
|
+
# Check command line arguments
|
|
201
|
+
if len(sys.argv) != 2:
|
|
202
|
+
raise Exception("Usage: {} BASE_FOLDER".format(sys.argv[0]))
|
|
203
|
+
|
|
204
|
+
base_folder = sys.argv[1]
|
|
205
|
+
else:
|
|
206
|
+
if not os.path.isabs(base_folder):
|
|
207
|
+
# Resolve base folder relative to script folder
|
|
208
|
+
base_folder = os.path.join(sys.path[0], base_folder)
|
|
209
|
+
|
|
210
|
+
model_folder = _resolve_folder(kwargs, base_folder, "model_folder", "model")
|
|
211
|
+
input_folder = _resolve_folder(kwargs, base_folder, "input_folder", "input")
|
|
212
|
+
output_folder = _resolve_folder(kwargs, base_folder, "output_folder", "output")
|
|
213
|
+
|
|
214
|
+
# Set up logging
|
|
215
|
+
logger = logging.getLogger("rtctools")
|
|
216
|
+
if not logger.hasHandlers() and not any(
|
|
217
|
+
(isinstance(h, logging.StreamHandler) for h in logger.handlers)
|
|
218
|
+
):
|
|
219
|
+
handler = logging.StreamHandler()
|
|
220
|
+
formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
|
|
221
|
+
handler.setFormatter(formatter)
|
|
222
|
+
logger.addHandler(handler)
|
|
223
|
+
|
|
224
|
+
# Add pi.DiagHandler, if using PIMixin. Only add it if it does not already exist.
|
|
225
|
+
if issubclass(simulation_problem_class, SimulationPIMixin) and not any(
|
|
226
|
+
(isinstance(h, pi.DiagHandler) for h in logger.handlers)
|
|
227
|
+
):
|
|
228
|
+
handler = pi.DiagHandler(output_folder)
|
|
229
|
+
logger.addHandler(handler)
|
|
230
|
+
|
|
231
|
+
logger.setLevel(log_level)
|
|
232
|
+
|
|
233
|
+
logger.info("Using RTC-Tools {}".format(__version__))
|
|
234
|
+
logger.debug("Using CasADi {}.".format(casadi.__version__))
|
|
235
|
+
|
|
236
|
+
# Run
|
|
237
|
+
prob = simulation_problem_class(
|
|
238
|
+
model_folder=model_folder, input_folder=input_folder, output_folder=output_folder, **kwargs
|
|
239
|
+
)
|
|
240
|
+
prob.simulate()
|
|
241
|
+
return prob
|