gemseo-multi-fidelity 0.0.1__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.
Files changed (76) hide show
  1. gemseo_multi_fidelity/__init__.py +17 -0
  2. gemseo_multi_fidelity/core/MFMapperAdapter_input.json +22 -0
  3. gemseo_multi_fidelity/core/MFMapperAdapter_output.json +22 -0
  4. gemseo_multi_fidelity/core/MFMapperLinker_input.json +22 -0
  5. gemseo_multi_fidelity/core/MFMapperLinker_output.json +22 -0
  6. gemseo_multi_fidelity/core/MFScenarioAdapter_input.json +39 -0
  7. gemseo_multi_fidelity/core/MFScenarioAdapter_output.json +23 -0
  8. gemseo_multi_fidelity/core/__init__.py +16 -0
  9. gemseo_multi_fidelity/core/boxed_domain.py +242 -0
  10. gemseo_multi_fidelity/core/corr_function.py +411 -0
  11. gemseo_multi_fidelity/core/criticality.py +124 -0
  12. gemseo_multi_fidelity/core/ds_mapper.py +307 -0
  13. gemseo_multi_fidelity/core/errors.py +42 -0
  14. gemseo_multi_fidelity/core/eval_mapper.py +188 -0
  15. gemseo_multi_fidelity/core/id_mapper_adapter.py +61 -0
  16. gemseo_multi_fidelity/core/mapper_adapter.py +126 -0
  17. gemseo_multi_fidelity/core/mapper_linker.py +72 -0
  18. gemseo_multi_fidelity/core/mf_formulation.py +635 -0
  19. gemseo_multi_fidelity/core/mf_logger.py +216 -0
  20. gemseo_multi_fidelity/core/mf_opt_problem.py +480 -0
  21. gemseo_multi_fidelity/core/mf_scenario.py +205 -0
  22. gemseo_multi_fidelity/core/noise_criterion.py +94 -0
  23. gemseo_multi_fidelity/core/projpolytope.out +0 -0
  24. gemseo_multi_fidelity/core/scenario_adapter.py +568 -0
  25. gemseo_multi_fidelity/core/stop_criteria.py +201 -0
  26. gemseo_multi_fidelity/core/strict_chain.py +75 -0
  27. gemseo_multi_fidelity/core/utils_model_quality.py +74 -0
  28. gemseo_multi_fidelity/corrections/__init__.py +16 -0
  29. gemseo_multi_fidelity/corrections/add_corr_function.py +80 -0
  30. gemseo_multi_fidelity/corrections/correction_factory.py +65 -0
  31. gemseo_multi_fidelity/corrections/mul_corr_function.py +86 -0
  32. gemseo_multi_fidelity/drivers/__init__.py +16 -0
  33. gemseo_multi_fidelity/drivers/mf_algo_factory.py +38 -0
  34. gemseo_multi_fidelity/drivers/mf_driver_lib.py +462 -0
  35. gemseo_multi_fidelity/drivers/refinement.py +234 -0
  36. gemseo_multi_fidelity/drivers/settings/__init__.py +16 -0
  37. gemseo_multi_fidelity/drivers/settings/base_mf_driver_settings.py +59 -0
  38. gemseo_multi_fidelity/drivers/settings/mf_refine_settings.py +50 -0
  39. gemseo_multi_fidelity/formulations/__init__.py +16 -0
  40. gemseo_multi_fidelity/formulations/refinement.py +144 -0
  41. gemseo_multi_fidelity/mapping/__init__.py +16 -0
  42. gemseo_multi_fidelity/mapping/identity_mapper.py +74 -0
  43. gemseo_multi_fidelity/mapping/interp_mapper.py +422 -0
  44. gemseo_multi_fidelity/mapping/mapper_factory.py +70 -0
  45. gemseo_multi_fidelity/mapping/mapping_errors.py +46 -0
  46. gemseo_multi_fidelity/mapping/subset_mapper.py +122 -0
  47. gemseo_multi_fidelity/mf_rosenbrock/__init__.py +16 -0
  48. gemseo_multi_fidelity/mf_rosenbrock/delayed_disc.py +136 -0
  49. gemseo_multi_fidelity/mf_rosenbrock/refact_rosen_testcase.py +46 -0
  50. gemseo_multi_fidelity/mf_rosenbrock/rosen_mf_case.py +284 -0
  51. gemseo_multi_fidelity/mf_rosenbrock/rosen_mf_funcs.py +350 -0
  52. gemseo_multi_fidelity/models/__init__.py +16 -0
  53. gemseo_multi_fidelity/models/fake_updater.py +112 -0
  54. gemseo_multi_fidelity/models/model_updater.py +91 -0
  55. gemseo_multi_fidelity/models/rbf/__init__.py +16 -0
  56. gemseo_multi_fidelity/models/rbf/kernel_factory.py +66 -0
  57. gemseo_multi_fidelity/models/rbf/kernels/__init__.py +16 -0
  58. gemseo_multi_fidelity/models/rbf/kernels/gaussian.py +93 -0
  59. gemseo_multi_fidelity/models/rbf/kernels/matern_3_2.py +101 -0
  60. gemseo_multi_fidelity/models/rbf/kernels/matern_5_2.py +101 -0
  61. gemseo_multi_fidelity/models/rbf/kernels/rbf_kernel.py +172 -0
  62. gemseo_multi_fidelity/models/rbf/rbf_model.py +422 -0
  63. gemseo_multi_fidelity/models/sparse_rbf_updater.py +96 -0
  64. gemseo_multi_fidelity/models/taylor/__init__.py +16 -0
  65. gemseo_multi_fidelity/models/taylor/taylor.py +212 -0
  66. gemseo_multi_fidelity/models/taylor_updater.py +66 -0
  67. gemseo_multi_fidelity/models/updater_factory.py +62 -0
  68. gemseo_multi_fidelity/settings/__init__.py +16 -0
  69. gemseo_multi_fidelity/settings/drivers.py +22 -0
  70. gemseo_multi_fidelity/settings/formulations.py +16 -0
  71. gemseo_multi_fidelity-0.0.1.dist-info/METADATA +99 -0
  72. gemseo_multi_fidelity-0.0.1.dist-info/RECORD +76 -0
  73. gemseo_multi_fidelity-0.0.1.dist-info/WHEEL +5 -0
  74. gemseo_multi_fidelity-0.0.1.dist-info/entry_points.txt +2 -0
  75. gemseo_multi_fidelity-0.0.1.dist-info/licenses/LICENSE.txt +165 -0
  76. gemseo_multi_fidelity-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,568 @@
1
+ # Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com
2
+ #
3
+ # This program is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License version 3 as published by the Free Software Foundation.
6
+ #
7
+ # This program is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
+ # Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public License
13
+ # along with this program; if not, write to the Free Software Foundation,
14
+ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15
+
16
+ # Copyright (c) 2019 AIRBUS OPERATIONS
17
+
18
+ #
19
+ # Contributors:
20
+ # INITIAL AUTHORS - API and implementation and/or documentation
21
+ # :author: Romain Olivanti
22
+ # OTHER AUTHORS - MACROSCOPIC CHANGES
23
+ # Francois Gallard: portage to GEMSEO 6
24
+ """Multi-fidelity scenario adapter."""
25
+
26
+ from __future__ import annotations
27
+
28
+ import contextlib
29
+ import logging
30
+ from collections.abc import Iterable
31
+ from typing import TYPE_CHECKING
32
+ from typing import Any
33
+
34
+ from gemseo.algos.database import Database
35
+ from gemseo.core.discipline.discipline import Discipline
36
+ from gemseo.scenarios.mdo_scenario import MDOScenario
37
+
38
+ from gemseo_multi_fidelity.core.corr_function import MDOCorrectedFunction
39
+
40
+ if TYPE_CHECKING:
41
+ from collections.abc import Callable
42
+ from collections.abc import Mapping
43
+
44
+ from gemseo.core._process_flow.base_process_flow import BaseProcessFlow
45
+ from gemseo.core.mdo_functions.mdo_function import MDOFunction
46
+ from gemseo.typing import StrKeyMapping
47
+ from numpy.typing import NDArray
48
+
49
+ LOGGER = logging.getLogger(__name__)
50
+
51
+
52
+ class CallbackReturnError(Exception):
53
+ """Error to raise if a return is requested at runtime."""
54
+
55
+ def __init__(self, msg: str | None = None) -> None:
56
+ """Constructor.
57
+
58
+ Args:
59
+ msg: explanation for the error.
60
+ """
61
+ super().__init__()
62
+ self.msg = msg
63
+
64
+ def __str__(self) -> str:
65
+ """Custom string representation."""
66
+ return repr(self.msg)
67
+
68
+
69
+ class MFScenarioAdapter(Discipline):
70
+ """An adapter class to run a scenario in a multi-fidelity context."""
71
+
72
+ X_START = "x_start"
73
+ X_BEST = "x_best"
74
+
75
+ INPUT_OPTIONS = "input_options"
76
+ OUTPUT_OPTIONS = "output_options"
77
+
78
+ X_REF = "x_reference"
79
+ OUTPUT_REF = "output_reference"
80
+
81
+ ON_ENTRY_CALLBACKS = "on_entry_callbacks"
82
+ RETURN_CALLBACK = "return_callback"
83
+ ON_EXIT_CALLBACKS = "on_exit_callbacks"
84
+
85
+ OUTPUT_STATUS = "output_status"
86
+ NOT_STARTED = "not started"
87
+ EARLY_RETURN = "asked to return"
88
+ RUN_COMPLETED = "completed"
89
+ N_ITER_RUN = "n_iters"
90
+
91
+ MAX_CORR_RAD = "max_corr_rad"
92
+ HOTSTART_OPTIONS = "hotstart_options"
93
+ HESS_PAIRS = "input_hess_pairs"
94
+ PRECOND = "input_precond"
95
+
96
+ DEF_HOTSTART_OPTIONS_GETTER = "get_hotstart_options"
97
+
98
+ scenario: MDOScenario
99
+ """The wrapped scenario"""
100
+
101
+ auto_detect_grammar_files = True
102
+
103
+ def __init__(self, scenario: MDOScenario) -> None:
104
+ """Constructor.
105
+
106
+ Args:
107
+ scenario: The scenario to run.
108
+ """
109
+ if not isinstance(scenario, MDOScenario):
110
+ msg = "input scenario must be an MDOScenario"
111
+ raise TypeError(msg)
112
+
113
+ super().__init__(name=scenario.name)
114
+
115
+ self.scenario = scenario
116
+
117
+ self._opt_status = self.NOT_STARTED
118
+ self._return_callback: callable | None = None
119
+ self._hotstart_options_set: bool = False
120
+ self._init_return_callback()
121
+
122
+ def _init_return_callback(self):
123
+ """Initialize return callback.
124
+
125
+ Initializes a return callback linked to the database of the optimization
126
+ problem. By default, the callback has no effect.
127
+ """
128
+
129
+ def must_return(x_vect=None):
130
+ return False
131
+
132
+ self._return_callback = must_return
133
+ self.scenario.formulation.optimization_problem.database.add_new_iter_listener(
134
+ self._return_callback
135
+ )
136
+
137
+ @staticmethod
138
+ def check_callback(callback: Callable) -> None:
139
+ """Check if the callback provided is valid.
140
+
141
+ Args:
142
+ callback: The callback to check.
143
+ """
144
+ if not callable(callback):
145
+ msg = f"{callback} must be callable"
146
+ raise TypeError(msg)
147
+
148
+ @staticmethod
149
+ def _check_return_callback(callback: Callable) -> None:
150
+ """Check return callback.
151
+
152
+ Checks that the callback provided is a valid return callback, i.e. that it only
153
+ returns a bool as output and that it should not already order to return at check
154
+ time.
155
+
156
+ Args:
157
+ callback: The callback to check.
158
+ """
159
+ MFScenarioAdapter.check_callback(callback)
160
+
161
+ # Specific checks
162
+ return_flag = callback()
163
+ if not isinstance(return_flag, bool):
164
+ msg = "return_callback must return a bool"
165
+ raise TypeError(msg)
166
+
167
+ if return_flag:
168
+ LOGGER.warning(
169
+ "return_callback is asking to return before the "
170
+ "beginning of the optimization"
171
+ )
172
+
173
+ def _link_return_callback(self, return_callback: Callable) -> None:
174
+ """Link return callback.
175
+
176
+ Implements the return callback using a listener linked to the optimization
177
+ database of the scenario. ``return_callback`` must return a bool and take the
178
+ design vector as argument.
179
+ At least two stores are allowed to be performed irrespectively to the return
180
+ callback in order to avoid deadlocks during optimization (overrides the return
181
+ flag).
182
+
183
+ Args:
184
+ return_callback: The callback allowing an early return.
185
+ """
186
+ self._check_return_callback(return_callback)
187
+
188
+ # Delete the current listener
189
+ opt_prob = self.scenario.formulation.optimization_problem
190
+ # TODO access should be cleaner than this, add a method in GEMSEO
191
+ new_it_listeners = opt_prob.database._Database__new_iter_listeners
192
+ with contextlib.suppress(ValueError):
193
+ new_it_listeners.remove(self._return_callback)
194
+ # Make sure at least one real optimization iteration can be performed by the
195
+ # MDOScenario, build a specific callback.
196
+ it_init = len(opt_prob.database)
197
+ it_return_allowed = it_init + 1
198
+
199
+ def must_return(x_vect=None) -> None:
200
+ """Callback making sure that at least on real optimization is performed."""
201
+ asked_to_return = return_callback()
202
+ curr_it = len(opt_prob.database)
203
+ if asked_to_return and (curr_it > it_return_allowed):
204
+ msg = "Ordered to return by the callback"
205
+ raise CallbackReturnError(msg)
206
+
207
+ # Add the new callback
208
+ self._return_callback = must_return
209
+ opt_prob.database.add_new_iter_listener(self._return_callback)
210
+
211
+ def run_callbacks_from_local_data(self, callback_type: str) -> list:
212
+ """Run the specified callback type from the local data.
213
+
214
+ Also checks that the callbacks defined in the default_inputs are also called if
215
+ different from the ones defined in the local_data.
216
+ This is explained by the fact that the behavior expected here is not to override
217
+ but to append.
218
+
219
+ Args:
220
+ callback_type: The key in the local data.
221
+
222
+ Returns:
223
+ The list ??? # TODO
224
+ """
225
+ callbacks = self.io.data.get(callback_type)
226
+ out = []
227
+
228
+ if callbacks is not None:
229
+ # Make sure that the callbacks defined in the default_inputs are
230
+ # not simply overridden
231
+ if callable(callbacks):
232
+ # Make sure it is a list to ease the check
233
+ callbacks = [callbacks]
234
+ # Add them if there are not there yet
235
+ callbacks_in_default = self.default_input_data.get(callback_type, [])
236
+ if callable(callbacks_in_default):
237
+ callbacks_in_default = [callbacks_in_default]
238
+ for callback in callbacks_in_default:
239
+ if callback not in callbacks:
240
+ callbacks.append(callback)
241
+ out = self.run_callbacks(callbacks, self.io.data)
242
+ return out
243
+
244
+ @staticmethod
245
+ def run_callbacks(
246
+ callbacks: Iterable[Callable] | Callable,
247
+ *args: list[Any],
248
+ **kwargs: Mapping[str, Any],
249
+ ) -> list[Callable]:
250
+ """Run the specified callbacks with the same args sequentially.
251
+
252
+ Args:
253
+ callbacks: The callbacks to run.
254
+ args: The args.
255
+ kwargs: The keyword arguments.
256
+
257
+ Returns:
258
+ The list of outputs.
259
+ """
260
+ if isinstance(callbacks, Iterable):
261
+ return [callback(*args, **kwargs) for callback in callbacks]
262
+
263
+ return [callbacks(*args, **kwargs)]
264
+
265
+ def get_all_nonproc_funcs(self) -> list[MDOFunction]:
266
+ """Get all non-processed functions.
267
+
268
+ Convenience method to get all the non-processed functions even when the
269
+ functions have not been preprocessed yet.
270
+
271
+ Returns:
272
+ The list of the non-processed functions.
273
+ """
274
+ opt_prob = self.scenario.formulation.optimization_problem
275
+
276
+ if opt_prob._functions_are_preprocessed:
277
+ return opt_prob.functions
278
+ return opt_prob.original_functions
279
+
280
+ def get_algo_lib(self) -> str:
281
+ """Convenience method to get the algo lib specified to run the scenario.
282
+
283
+ Returns:
284
+ The lib.
285
+ """
286
+ # The algo name is either set by the input data or already defined in the
287
+ # default inputs of the scenario
288
+ tag_algo = "algo_name"
289
+ default_algo = None
290
+ if self.scenario._settings is not None:
291
+ default_algo = self.scenario._settings.algo_name
292
+ algo_name = self.io.data.get(tag_algo, default_algo)
293
+ if algo_name is None:
294
+ msg = "No algorithm specified to execute the scenario"
295
+ raise RuntimeError(msg)
296
+
297
+ return self.scenario._algo_factory.create(algo_name)
298
+
299
+ def _set_func_reference(
300
+ self, func: MDOCorrectedFunction, x_ref: NDArray, output_ref: dict
301
+ ) -> None:
302
+ """Set function reference.
303
+
304
+ Sets the reference data for the specified function.
305
+ Nothing is done if no data are provided for the specified function.
306
+
307
+ Args:
308
+ func: The MDOCorrectedFunction to update.
309
+ x_ref: The reference point.
310
+ output_ref: The reference data to set.
311
+ """
312
+ max_norm_corr = self.io.data.get(self.MAX_CORR_RAD)
313
+ func_name = func.name
314
+
315
+ val_ref = output_ref.get(func_name)
316
+ if val_ref is not None:
317
+ grad_ref = output_ref.get(Database.GRAD_TAG + func_name)
318
+ if func.original is not None and isinstance(
319
+ func.original, MDOCorrectedFunction
320
+ ):
321
+ func = func.original
322
+ func.set_reference(
323
+ x_ref, val_ref, grad_ref=grad_ref, max_norm_thr=max_norm_corr
324
+ )
325
+
326
+ def _set_reference(self) -> None:
327
+ """Set the reference data provided."""
328
+ x_ref = self.io.data.get(self.X_REF)
329
+ output_ref = self.io.data.get(self.OUTPUT_REF)
330
+
331
+ if x_ref is None or output_ref is None:
332
+ # Nothing to do
333
+ return
334
+
335
+ opt_prob = self.scenario.formulation.optimization_problem
336
+
337
+ # Correction will be set, therefore, clear the current optimization data as it
338
+ # may no longer be consistent
339
+ opt_prob.database.clear()
340
+
341
+ to_array = self.scenario.design_space.convert_dict_to_array
342
+ # Convert to arrays
343
+ if isinstance(x_ref, dict):
344
+ x_ref = to_array(x_ref)
345
+ for func, jac in output_ref.items():
346
+ if isinstance(jac, dict):
347
+ output_ref[func] = to_array(x_ref)
348
+
349
+ # Apply the reference data
350
+ for func in self.get_all_nonproc_funcs():
351
+ self._set_func_reference(func, x_ref, output_ref)
352
+
353
+ def _filter_subprocess_inputs(self) -> dict:
354
+ """Filter subprocess inputs.
355
+
356
+ Filters self.local_data to keep only the data required to run the MDOScenario.
357
+
358
+ Returns:
359
+ The filtered inputs.
360
+ """
361
+ LOGGER.warning(
362
+ "ScenarioAdapter._filter_subprocess_inputs: Deactivated subprocess inputs!"
363
+ )
364
+ return {}
365
+ input_data = {}
366
+
367
+ key_algo = "algo_name"
368
+ key_algo_opt = "algo_options"
369
+ key_max_iter = "max_iter"
370
+
371
+ # Check if the algo specified in self.local_data is the same as the one defined
372
+ # in the default inputs
373
+ def_algo = (
374
+ self.scenario._settings.algo_name
375
+ if self.scenario._settings is not None
376
+ else None
377
+ )
378
+ algo = self.io.data.get(key_algo)
379
+ #
380
+ new_algo_options = self.io.data.get(key_algo_opt, {})
381
+ #
382
+ if def_algo is not None and (algo == def_algo or algo is None):
383
+ # Same algo Try to load the default inputs in the input data as only a
384
+ # limited number of options may have been updated
385
+ algo_options = self.scenario._settings.model_dump()
386
+ algo_options.update(new_algo_options)
387
+ else:
388
+ # The new algo differs from the default one
389
+ # Use only new options
390
+ algo_options = new_algo_options
391
+
392
+ max_iter = self.io.data.get(key_max_iter)
393
+
394
+ # Handle preconditioner and hess approx pairs inputs
395
+ other_algo_options = self._proccess_lib_options()
396
+ algo_options.update(other_algo_options)
397
+
398
+ input_data = {
399
+ key: data
400
+ for key, data in zip(
401
+ [key_algo, key_max_iter], [algo, max_iter], strict=False
402
+ )
403
+ if data is not None
404
+ }
405
+
406
+ input_data.update(algo_options)
407
+
408
+ return input_data
409
+
410
+ def _proccess_lib_options(self) -> dict:
411
+ """Process lib-specific options.
412
+
413
+ Returns:
414
+ The processed options.
415
+ """
416
+ lib_options = {}
417
+ # Preconditioner
418
+ precond = self.io.data.get(self.PRECOND)
419
+ algo_lib = self.get_algo_lib()
420
+ if precond is not None:
421
+ try:
422
+ precond_lib_name = algo_lib.PRECOND
423
+ lib_options[precond_lib_name] = precond
424
+ except AttributeError:
425
+ pass
426
+ # Hessian approx pairs
427
+ pairs = self.io.data.get(self.HESS_PAIRS)
428
+ if pairs is not None:
429
+ try:
430
+ pairs_lib_name = algo_lib.HESS_PAIRS
431
+ lib_options[pairs_lib_name] = pairs
432
+ except AttributeError:
433
+ pass
434
+ return lib_options
435
+
436
+ def _set_inputs(self) -> dict:
437
+ """Tasks to perform before executing self.scenario.
438
+
439
+ Returns:
440
+ The input data to run the MDOScenario.
441
+ """
442
+ self._opt_status = self.NOT_STARTED
443
+ x_start = self.io.data[self.X_START]
444
+ self.scenario.design_space.set_current_value(x_start)
445
+
446
+ # Unpack forwarded options
447
+ input_options = self.io.data.get(self.INPUT_OPTIONS)
448
+ if input_options is not None:
449
+ for option, value in input_options.items():
450
+ self.io.data[option] = value
451
+
452
+ # Set the reference data
453
+ self._set_reference()
454
+
455
+ # Link the update callback
456
+ return_callback = self.io.data.get(self.RETURN_CALLBACK)
457
+ if return_callback is not None:
458
+ self._link_return_callback(return_callback)
459
+
460
+ # Filter scenario inputs
461
+ return self._filter_subprocess_inputs()
462
+
463
+ def _set_outputs(self) -> None:
464
+ """Store the outputs from the optimization."""
465
+ # Output the best data expressed in the upper design space
466
+ opt_prob = self.scenario.formulation.optimization_problem
467
+
468
+ # Get the best point
469
+ x_best = opt_prob.optimum[1]
470
+ x_best = opt_prob.design_space.convert_array_to_dict(x_best)
471
+
472
+ # Update the output data
473
+ self.io.data[self.X_BEST] = x_best
474
+
475
+ # Update the output status
476
+ self.io.data[self.OUTPUT_STATUS] = self._opt_status
477
+
478
+ # Set the potential hotstart options in the event of a restart
479
+ # At this level
480
+ self._set_hotstart_options()
481
+
482
+ def _set_hotstart_options(self) -> None:
483
+ """Set hotstart options.
484
+
485
+ Either tries to recover hotstart options from the lib itself, using a specific
486
+ getter if a string is provided in self.local_data[self.HOTSTART_OPTIONS] or uses
487
+ the options provided if a dict is provided.
488
+ If nothing is provided, a default getter is called.
489
+ If the latter is not recognized by the lib, nothing is done.
490
+ """
491
+ hotstart_options = self.io.data.get(
492
+ self.HOTSTART_OPTIONS, self.DEF_HOTSTART_OPTIONS_GETTER
493
+ )
494
+ options = None
495
+ self._hotstart_options_set = False
496
+ if isinstance(hotstart_options, dict):
497
+ # Simply use the options provided
498
+ options = hotstart_options
499
+ elif isinstance(hotstart_options, str):
500
+ # Try to recover the options from the specified getter
501
+ algo_lib = self.get_algo_lib()
502
+ try:
503
+ hotstart_options_getter = getattr(algo_lib, hotstart_options)
504
+ opt_prob = self.scenario.formulation.optimization_problem
505
+ options = hotstart_options_getter(
506
+ opt_prob.database, opt_prob.objective.name
507
+ )
508
+ except: # noqa: E722
509
+ # Nothing can be done
510
+ pass
511
+
512
+ if options is not None:
513
+ # Set the options
514
+ try:
515
+ algo_options = self.io.data["algo_options"]
516
+ algo_options.update(options)
517
+ except KeyError:
518
+ # No algo options have defined yet
519
+ self.io.data["algo_options"] = options
520
+ self._hotstart_options_set = True
521
+
522
+ def _run_subprocess(self, input_data: StrKeyMapping) -> None:
523
+ """Run the subprocess, here the optimization of the scenario.
524
+
525
+ Args:
526
+ input_data: The input data.
527
+ """
528
+ self.scenario.execute(**input_data)
529
+
530
+ def _run(self, input_data: StrKeyMapping) -> None:
531
+ """Run.
532
+
533
+ Processes the input data potentially:
534
+ - setting the reference data
535
+ - setting the return callback
536
+ then runs:
537
+ - the on entry callbacks
538
+ - the scenario
539
+ - the on exit callbacks.
540
+
541
+ Args:
542
+ input_data: The input data.
543
+ """
544
+ # Run the on entry callback(s)
545
+ self.run_callbacks_from_local_data(self.ON_ENTRY_CALLBACKS)
546
+
547
+ inputs_subprocess = self._set_inputs()
548
+ # Run the subprocess
549
+ try:
550
+ self._run_subprocess(inputs_subprocess)
551
+ self._opt_status = self.RUN_COMPLETED
552
+ except CallbackReturnError:
553
+ # Asked to return by the callback
554
+ self._opt_status = self.EARLY_RETURN
555
+
556
+ # Set the output data
557
+ self._set_outputs()
558
+
559
+ # Run the on exit callback(s)
560
+ self.run_callbacks_from_local_data(self.ON_EXIT_CALLBACKS)
561
+
562
+ def get_process_flow(self) -> BaseProcessFlow:
563
+ """Get the process flow.
564
+
565
+ Returns:
566
+ The process flow.
567
+ """
568
+ return self.scenario.get_process_flow()