Overriding Variables#

Aviary allows you to replace certain internally computed quantities with a custom value, which can either be a constant or a value computed by some custom component through a capability called Overriding. The motivation for this capability comes from FLOPS, which allowed the user to override certain variables with pre-computed values from other more accurate sources. Aviary expands this feature to allow direct override of any intermediate calculation except for dynamic mission variables that are controlled by Dymos.

Throughout an Aviary model, all inputs and outputs that begin with “aircraft:” or “mission:” are promoted to the top level. When an output and an input have the same name, they are implicitly connected through this process. When we override an Aviary output, it is no longer promoted as the original variable name, but is instead promoted with the string “AUTO_OVERRIDE:” prepended to the variable name. This eliminates the connection from the output to the input and a visible dead-end promoted name that will be seen in the list_outputs.

Once an output has been overridden, the inputs are free to take on any value specified in the aviary_inputs. If you add an external component to your aviary model that provides this same output, then those inputs will be implicitly connected to that component. If you add multiple components that provide the output, then you will have to override all but one of them.

Replacing Computed Value with a Constant#

Consider a simple case where we want to specify a constant value for the horizontal tail mass instead of using the value calculated in Aviary. We simply set the value in our aviary_inputs.

from aviary.api import Aircraft
import aviary.api as av
from aviary.validation_cases.validation_data.flops_data.FLOPS_Test_Data import FLOPS_Test_Data

aviary_inputs = av.AviaryValues(FLOPS_Test_Data['LargeSingleAisle1FLOPS']['inputs'])

aviary_inputs.set_val(Aircraft.HorizontalTail.MASS, 2200.0, units='lbm')
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/pyoptsparse/pyOpt_MPI.py:68: UserWarning: mpi4py could not be imported. mpi4py is required to use the parallel gradient analysis and parallel objective analysis for non-gradient based optimizers. Continuing using a dummy MPI module from pyOptSparse.
  warnings.warn(warn)

Replacing Computed Value with the Output of Another Component#

Consider a case where we have added one component that computes the mass of the horizontal tail. We want to override the internally computed value of the mass with a new value that comes from an external component. We can do this in the level 2 interface when we define our builder, by simply providing an output that uses Aviary’s name in the variable hierarchy. (i.e., Aircraft.HorizontalTail.MASS for this case) When we do this, aviary will automatically detect that an external subsystem is providing this variable, and will override the internal calculation.

import openmdao.api as om

from aviary.api import SubsystemBuilderBase, Aircraft


class HTailMass(om.ExplicitComponent):
    """
    User-provided subsystem to compute the tail weight with a simple formula.
    """

    def setup(self):

        self.add_input('Area', 1.0, units='ft**2')
        self.add_output('Mass', 1.0, units='lbm')
        
        self.declare_partials('Mass', 'Area', val=20.0)

    def compute(self, inputs, outputs):
        outputs['Mass'] = 20.0 * inputs['Area']
        
        
class HTailWeightBuilder(SubsystemBuilderBase):
    """
    Prototype of a subsystem that overrides an aviary internally computed var.
    """

    def __init__(self, name='wing_weight'):
        super().__init__(name)

    def build_pre_mission(self, aviary_inputs):
        '''
        Build an OpenMDAO system for the pre-mission computations of the subsystem.

        Returns
        -------
        pre_mission_sys : openmdao.core.System
            An OpenMDAO system containing all computations that need to happen in
            the pre-mission part of the Aviary problem. This
            includes sizing, design, and other non-mission parameters.
        '''
        wing_group = om.Group()
        wing_group.add_subsystem("tail_weight", HTailMass(),
                                 promotes_inputs=[('Area', Aircraft.HorizontalTail.AREA)],
                                 promotes_outputs=[('Mass', Aircraft.HorizontalTail.MASS)]
                                )
        return wing_group


from aviary.api import default_height_energy_phase_info as phase_info   
phase_info['pre_mission']['external_subsystems'] = [HTailWeightBuilder(name="tail_external")]

Note that if we add two components that provide the same output, we should be careful to only promote one of them to a specific variable name. If you promote two or more outputs with the same name, an error will be raised indicating that there are multiple outputs with the same name.