pythonoptimizationopenmdao

'Output states' in Dymos?


I'm building a model in Dymos/OpenMDAO. There are a few states which are required for calculating the dynamics, and then some which I'd just like to derive and output. For example, say I had a very simple model with dynamic states of displacement x and velocity v, and a force input u. Then the dynamics would be x_dot = v, v_dot = u/m (where m is mass), and the OpenMDAO model would be:

class MyModel(om.ExplicitComponent):
    def initialize(self):
        self.options.declare('num_nodes', types=int)

    def setup(self):
        nn = self.options['num_nodes']

        self.add_input('x', shape=(nn,), desc='displacement', units='m')
        self.add_input('v', shape=(nn,), desc='velocity', units='m/s')
        self.add_input('u', shape=(nn,), desc='force', units='N')
        self.add_input('m', shape=(nn,), desc='mass', units='kg')

        # Not rates, just states
        self.add_output('y', val=np.zeros(nn), desc='derived quantity', units='...')
        # State rates
        self.add_output('v_dot', val=np.zeros(nn), desc='rate of change of velocity', units='m/s**2')

        self.declare_partials(of='*', wrt='*', method='cs')

    def compute(self, inputs, outputs):
        x, v, u, m = inputs.values()

        outputs['v_dot'] = u / m

        outputs['y'] = # Some expression

There's an additional state y which I'd just like to compute and output, so that I can use it in path constraints etc. However, I can't just do

phase.add_state('y', rate_source='<what to put here?>', targets=['y'], units='...')

when defining the Dymos phase because the rate_source (of which there is none) can't be left unspecified. I thought about setting it as a parameter instead of a state but that requires y to be an input to the OpenMDAO model.

What would be the best way to accomplish this? Maybe I need to create an additional OpenMDAO component for the derived states and link the two together? I don't have much experience of this, so haven't explored this as an option as of yet.


Solution

  • States are integrated based on some governing differential equation, but in the process of computing those we might be interested in computing these "auxiliary" calculations, such as y in this case.

    To consistently have outputs in a single place, Dymos has the notion of a timeseries, where, all time, state, and control variables are placed. You can add other outputs of the ODE, such as y to the timeseries outputs.

    phase.add_timeseries_output('y')

    In this case, Dymos will recognize that y is not one of the known states, controls, or a time variable and assume that it is in the ODE. If your ODE has nested groups, you would ask for y using the ODE-relative path.

    Then you can access that output in the standard OpenMDAO way:

    y = prob.get_val('<path_to_phase>.timeseries.y')

    Dymos will also allow you to provide ODE outputs as boundary constraints, path constraints, or objectives. If you do this, they are automatically added to the timeseries.

    phase.add_boundary_constraint('y', loc='final', lower=..., upper=...)

    phase.add_path_constraint('y', lower=..., upper=...)

    phase.add_objective('y', loc='final', ref=...)

    In the Minimum Time Climb Example, Mach is a computed output in the aero subsystem. You can see it's added as a path constraint:

    phase.add_path_constraint(name='aero.mach', lower=0.1, upper=1.8)