codes

The codes module defines and contains interfaces to (pluggable) dependencies and external codes.

Pluggable Dependencies

Each pluggable dependency should have an API that isolates bluemira users from any changes to that dependency. The main APIs that we have created so far are for the CAD engine and the optimiser library.

For the CAD engine all the functions are wrapped by equivalent functions in the geometry module. The geometry module should be the interface most users access CAD through. The CAD engine is still accessible for advanced users with the hope that any new functionality that is created can be ported to the geometry module. If there is a future CAD engine change we aim to make this invisible to the end user.

The optimiser library is wrapped in a similar way but as optimisers are used in various areas of bluemira the main user interface is through the optimiser module in utilities.

Any additional pluggable dependencies should have APIs created in a similar way.

External Code Interfaces

External codes are defined as programs that are run outside of bluemira. Most often this will be a program that is run on the command line whose outputs can be parsed and incorporated into bluemira.

Codes Interface

A generic interface for all programs that are run externally is defined in the codes.interface module.

Solver

The key class defining the interface to an external program is CodesSolver.

A CodesSolver takes, as input, a MappedParameterFrame, defining the input/output parameters of the solver, and a build_config, defining the solver’s run options. When executed, the solver sequentially calls tasks that set up and run the external program, using a given ‘run mode’ and a shared ‘MappedParameterFrame’.

Below is an example of how one would use a CodesSolver.

Tasks

A task can be thought of as a stage of a solver, a CodesSolver requires implementations of classes for three tasks:

  1. Setup

    This class will generally be responsible for taking parameters from bluemira and generating an input to the external code. This input will most often be a file.

  2. Run

    This class is responsible for running the external code. It will often execute a shell command to do so. The _run_subprocess() method on CodesTask is provided to run shell commands and incorporate standard output with bluemira’s logging system.

  3. Teardown

    This class is responsible for parsing the external program’s outputs, an incorporating them into bluemira. This incorporating usually involves updating the solver’s parameter values with the outputs of the program.

Run Mode

Within each CodesTask, there is the concept of a RunMode. A RunMode enum enumerates the ways in which an external program can be run. It must, at least, contain a RUN option, but will often also have READ and MOCK options. This way, if an external code is not installable (e.g., for licensing reasons), the solver can instead be called in MOCK mode to output some pre-defined result, or in READ mode to parse results from a previous run of the program.

MappedParameterFrames

MappedParameterFrame extends ParameterFrame to allow mapping to external codes’ variables through ParameterMapping. Default values for external codes’ parameters are provided for instances where there are many unmapped variables, which are usually only known by users experienced with the external code. These unmapped parameters can be set using the problem_settings of a CodesSolver instance.

ParameterMapping

ParameterMapping is used to create a connection between bluemira parameters and parameters of any external program. At its most basic level, it is a key-value mapping between two variable names. On top of the mapping, how the parameter value flows between bluemira and the external program is modified by the send and recv attributes.

send

True - set bluemira parameter value as input to external code

False - use default value as input to external code

recv

True - set external code result to the new value of the bluemira parameter

False - keep the original bluemira parameter value ignoring the external value

Example

The simplest interface definition would look something like the below:

from enum import auto

from bluemira.codes.interface import BaseRunMode
from bluemira.codes.interface import CodesSolver, CodesTeardown, CodesSetup, CodesTask


class RunMode(BaseRunMode):
    RUN = auto()


class Setup(CodesSetup):

    def run(self):
        # Write input file using input parameter values
        pass


class Run(CodesTask):

    def run(self, inputs):
        # eg self._run_subprocess(self.binary)
        pass

class Teardown(CodesTeardown):

    def run(self, inputs):
        # read results from the output file
        pass


class Solver(CodesSolver):
    name = "external_program"
    setup_cls = Setup
    run_cls = Run
    teardown_cls = Teardown
    run_mode_cls = RunMode

    def __init__(
        self,
        params,
        build_config,
    ):
        super().__init__(params)

        self.build_config = build_config
        self.binary = build_config.get("binary", None),
        # problem settings are parameters passed directly to the external program
        self.problem_settings = build_config.get("problem_settings", None)

Solver(params=None, build_config={}).execute('run')

APIs

An interface for programs that have an API to a Python library should follow the same pattern as above. For now, we do not have an example integration. The first expected integration will be the PROCESS, once its python interface has been completed.

If you have an existing code that you would like to integrate into bluemira, please contact the maintainers so we can discuss the best way forward.