Customized Optimization
Although this framework was designed to perform simple forms of aerodynamic optimization, it still covers a broad range of applications and use-cases. It seems therefore unrealistic to see this framework as a universal tool. Yet, a specific care was taken when designing and assembling the building parts of it in order to make it as customizable as possible.
Hence any optimization part of the framework can be customized with the following steps:
1) create a <custom-script>.py
file in the working directory (e.g. inspyred_debug.py
or pymoo_debug.py
),
2) inherit all classes to be customized respecting the class naming format i.e respectively CustomMesh
, CustomSimulator
, CustomOptimizer
and CustomEvolution
if overriding a Mesh
, Simulator
, Optimizer
or Evolution
based class,
3) add a custom_file
entry specifying the path to the custom script in the "study"
sub-dictionary of the configuration file.
Note
As of now, the FFD_2D
class is the only static element of the framework. However, extending Optimizer
to enable its customization would be pretty straightforward.
Illustration
The scripts in the debug
example illustrate how to customize the Simulator
, Optimizer
and Evolution
classes both with pymoo
(or inspyred
).
First, the Simulator
class is customized:
class CustomSimulator(DebugSimulator):
def __init__(self, config: dict):
super().__init__(config)
logger.info("INIT CUSTOM SIMULATOR")
Then, the Optimizer
class is customized:
class CustomOptimizer(PymooDebugOptimizer):
def __init__(self, config: dict):
super().__init__(config)
logger.info("INIT CUSTOM OPTIMIZER")
Finally, the Evolution
class is customized:
class CustomEvolution(PymooEvolution):
def set_ea(self):
logger.info("SET CUSTOM EA")
self.ea = PSO(
pop_size=self.optimizer.doe_size,
sampling=self.optimizer.generator._pymoo_generator(),
**self.optimizer.ea_kwargs
)
def evolve(self):
logger.info("EXECUTE CUSTOM EVOLVE")
res = minimize(problem=self.optimizer,
algorithm=self.ea,
termination=get_termination("n_gen", self.optimizer.max_generations),
seed=self.optimizer.seed,
verbose=True)
self.optimizer.final_observe()
# output results
best = res.F
index, opt_J = min(enumerate(self.optimizer.J), key=lambda x: abs(best - x[1]))
gid, cid = (index // self.optimizer.doe_size, index % self.optimizer.doe_size)
logger.info(f"optimal J: {opt_J} (J_pymoo: {best}),\n"
f"D: {' '.join([str(d) for d in self.optimizer.inputs[gid][cid]])}\n"
f"D_pymoo: {' '.join([str(d) for d in res.X])}\n"
f"[g{gid}, c{cid}]")
Of course, this is a purely illustrative example and the customization simply consists in overriding these upper level classes with already existing ones. It is still representative of how it can be done.
Quick Experiments
Running the following commands will execute a simple optimization with pymoo
:
# from aero-optim to debug
cd examples/debug
optim -c debug.json
pymoo_debug.py
script is given in debug.json
and CustomEvolution
inherits from PymooEvolution
, in this case, there is no need to specify the optimization library which is automatically selected.
The custom script can also be superseded by passing it to optim
with the --file
option. Hence, simply running the command below will execute an optimization with inspyred
:
# from aero-optim to debug
cd examples/debug
optim -c debug.json -f inspyred_debug.py