Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interim continuous-adjoint shooting implementation #885

Merged
merged 29 commits into from
Jan 7, 2023

Conversation

robfalck
Copy link
Contributor

@robfalck robfalck commented Dec 27, 2022

Summary

This is an interim implementation of a continuous-adjoint direct shooting implementation for Dymos.
Ultimately, this implementation will replace the current ExplicitShooting transcription but that will come in a following PR since this one is already massive.

  • Introduces DirectShootingPhase, a temporary name that will ultimately be ExplicitShootingPhase.
  • Like AnalyticPhase, DirectShootingPhase automatically assumes a particular transcription (DirectShooting, which will be renamed ExplicitShooting in a future PR).

DirectShooting has a few key differences with the existing ExplicitShooting implementation.

  1. It uses an off-the-shelf integrator from scipy rather than its own fixed-step implementation.
  2. It only propagates the derivatives of the states at the output nodes wrt the integration parameters (time bounds, initial states, parameters, controls, etc). These are fed into a follow-up vectorized ODE that computes the auxiliary ODE outputs for the timeseries and constraints with the appropriate derivatives.
  3. The integration is modular so while it currently uses scipy.solve_ivp, we can bring back the discrete-adjoint integration from the current ExplicitShooting phase if desired, or implement something along the lines of the neural ODE approach.
  4. This new phase will ultimately replace the SolveIVP implementation in a future PR, making for easier maintenance.

The Concept of Grids

Dymos currently conflates the notion of Grids and Transcriptions a bit.
With this DirectShootingPhase, the points at which output are desired do not necessarily line up with the grid on which the control variables are defined.
Allowing the user to specify a whole transcription object at which outputs are desired seems overkill, so this PR introduces GaussLobattoGrid, RadauGrid, and UniformGrid. These define node placement without all of the extra setup bloat.
In a future PR, these will be used to specify output points for simulate and timeseries output points.

Some rework was done on ControlGroup and PolynomialControlGroup to allow control outputs to be provided on different grids, with the requirement that the segment spacing in the grids remains the same (otherwise control rate interpolation would be nonsense).

Other things handled along the way.

  • Fixes for numpy 1.24
  • Replaced pep8 dependency with pycodestyle. Note there was a significant amount of reformatting to satisfy pycodestyle.
  • Bumped oldest tested version of pyoptsparse to 2.4.0.

Related Issues

Backwards incompatibilities

None

New Dependencies

Replaced pep8 testing dependency with pycodestyle.

@robfalck robfalck marked this pull request as draft December 27, 2022 20:44
@robfalck robfalck changed the title An implementation of DirectShooting to replace ExplicitShooting in dymos. Interim continuous-adjoint shooting implementation Jan 5, 2023
@robfalck robfalck marked this pull request as ready for review January 5, 2023 17:33
Copy link
Member

@johnjasa johnjasa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great changes here, Rob. I have minor suggestions, mostly docstring related. I'm excited to use this for the Aviary work later on!

self.options.declare('propagate_derivs', types=bool, default=True,
desc='If True, propagate the state and derivatives of the state and time with respect to '
'the integration parameters. If False, only propagate the primal states. If only '
'using this transcription to propagate an ODE and derivatives are needed, setting '
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it should be "and derivatives are not needed"? Might be missing a word or I'm misunderstanding

If a string, the file to which the result of the simulation will be saved.
If None, no record of the simulation will be saved.

Returns
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor detail: does this return problem if an error is just raised? I'm not sure if this docstring should be different

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood, the NotImplementedError should be overridden ultimately.


Returns
-------
problem
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same minor detail comment about the return docstring as above

"""
Provides a dict of node info and locations for a uniformly distributed set of n nodes on the range [-1, 1].

This distribution is not to be used to define polynomials, since equally space nodes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space -> spaced

self.options.declare('propagate_derivs', types=bool, default=True,
desc='If True, propagate the state and derivatives of the state and time with respect to '
'the integration parameters. If False, only propagate the primal states. If only '
'using this transcription to propagate an ODE and derivatives are needed, setting '
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"are" -> "are not"?

elif var_type == 'control_rate':
control_name = var[:-5]
path = f'control_rates:{control_name}_rate'
control_name = var[:-5]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

second control_name definition unnecessary?


def setup(self):
"""
Add the necessary I/O and storage for the RKIntegrationComp.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RKIntegrationComp -> ODEIntegrationComp


return f, f_x, f_t, f_theta

def _f_augmented(self, t, y, theta, dtheta_dz):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You know, this is just really neat stuff!

A component which interpolates control values in 1D using Vandermonde interpolation.

Takes training values for control variables at given _input_ nodes,
broadcaasts them to _discretization_ nodes, and then interpolates the discretization values
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

broadcaasts -> broadcasts


def compute_partials(self, inputs, partials, discrete_inputs=None):
"""
Compute derivatives interpolated control values and rates wrt the inputs.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"derivatives" -> "derivatives of"?

@review-notebook-app
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@robfalck robfalck merged commit 07a1e91 into OpenMDAO:master Jan 7, 2023
@robfalck robfalck deleted the direct_shooting branch February 1, 2024 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Replace pep8 with pycodestyle
3 participants