OpenMDAO / dymos

Open Source Optimization of Dynamic Multidisciplinary Systems
Apache License 2.0
208 stars 66 forks source link

Error check for phase linkages to make sure at least one side of it is free to be varied #478

Closed JustinSGray closed 3 years ago

JustinSGray commented 3 years ago

Summary of Issue

It is a common mistake for users to link a state/control across two phases, but then set both sides of that linkage to fixed. This seems like something we could check for in setup and provide a clear error for.

Issue Type

Description

Give an error saying

"Linkage between the final side of phase0 and the initial side of phase1 for variable foo is not configured correctly. Both sides of the connection are fixed, which makes the problem ill-posed. Change at least one side non-fixed"

Example

This toy example has two cases:

1) bad time linkage. 2) bad state linkage.

Both these cases are common and easy mistakes to make, and we should error on setup with them

import openmdao.api as om

import dymos as dm

class SimpleODE(om.ExplicitComponent):

    def initialize(self):
        self.options.declare('num_nodes', types=int)

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

        self.add_input('v', shape=nn, units='km/s') # just so there is an input... 

        self.add_output('v_dot', shape=nn, units='km/s**2')

    def compute(self, inputs, outputs): 

        outputs['v_dot'] = .00981 #km/s

p = om.Problem(model=om.Group())
p.driver = om.pyOptSparseDriver()
p.driver.declare_coloring()

tx = dm.Radau(num_segments=1, order=3, compressed=False)

traj = p.model.add_subsystem('traj', dm.Trajectory())

phase0 = traj.add_phase('phase0', dm.Phase(ode_class=SimpleODE, transcription=tx))

phase0.set_time_options(fix_initial=True, fix_duration=True, units='s')

phase0.add_state('v', fix_initial=True, fix_final=False, rate_source='v_dot', units='km/s')

phase1 = traj.add_phase('phase1', dm.Phase(ode_class=SimpleODE, transcription=tx))
# phase1.set_time_options(fix_initial=True, fix_duration=True, units='s') # ERROR! you can't have fix initial on and fix_duration on both sides of this link
phase1.set_time_options(fix_initial=False, fix_duration=True, units='s') 

# phase1.add_state('v', fix_initial=True, fix_final=False, rate_source='v_dot', units='km/s') # error you can't fix both sides of this link
phase1.add_state('v', fix_initial=False, fix_final=False, rate_source='v_dot', units='km/s')

# Maximize distance travelled in one second.
phase1.add_objective('v', loc='final', scaler=-1)

traj.link_phases(phases=['phase0', 'phase1'], vars=['v', 'time'])

p.model.linear_solver = om.DirectSolver()

p.setup(check=True)

p['traj.phase0.t_initial'] = 0.0
p['traj.phase0.t_duration'] = 1.0
p['traj.phase1.t_duration'] = 1.0

p['traj.phase0.states:v'] = phase0.interpolate(ys=[0, 10], nodes='state_input')
p['traj.phase1.states:v'] = phase0.interpolate(ys=[0, 0], nodes='state_input')

p.run_driver()
robfalck commented 3 years ago

If a parameter or control is specified as opt=False, then it exists as a potential input which some other output can connect to. There's no simple way to tell if these inputs are ultimately tied to a design variable or not.

Therefore, if implemented, this capability will be limited to states and time. Given this limitation, are we giving the user a false sense of security that everything is OK when in reality they have some incompatibility that we can't check? I suspect that it's probably preferable to implement this for states and time, since those are by far the most common variable types to be linked between phases.

JustinSGray commented 3 years ago

I think the non states/time that you are referring to don't have the option for fix_initial and fix_final do they?

So we can apply the error check in cases where we allow things to be explicitly fixed as boundary conditions. The error can even include the term bounary condition. I don't think its unreasonable to catch an error we can, just because there are some other less common errors that we can't.

robfalck commented 3 years ago

Right, we can check fix_initial/fix_final. The option opt=True|False is commonly used to fix the value of design parameters or controls, but since it makes those inputs we cannot be certain as to whether or not they're fixed.