Giter VIP home page Giter VIP logo

dymos's Introduction

Dymos: Open Source Optimization of Dynamic Multidisciplinary Systems

Dymos Tests Coverage Status

DOI

Dymos is a framework for the simulation and optimization of dynamical systems within the OpenMDAO Multidisciplinary Analysis and Optimization environment. Dymos leverages implicit and explicit simulation techniques to simulate generic dynamic systems of arbitary complexity.

The software has two primary objectives:

  • Provide a generic ODE integration interface that allows for the analysis of dynamical systems.
  • Allow the user to solve optimal control problems involving dynamical multidisciplinary systems.

Installation

The default installation of the developmental version of Dymos will install the minimum number of prerequisites:

python -m pip install dymos

More advanced installation instructions are available here.

Citation

See our overview paper in the Journal of Open Source Software

If you use Dymos in your work, please cite:

@article{Falck2021,
  doi = {10.21105/joss.02809},
  url = {https://doi.org/10.21105/joss.02809},
  year = {2021},
  publisher = {The Open Journal},
  volume = {6},
  number = {59},
  pages = {2809},
  author = {Robert Falck and Justin S. Gray and Kaushik Ponnapalli and Ted Wright},
  title = {dymos: A Python package for optimal control of multidisciplinary systems},
  journal = {Journal of Open Source Software}
}

Documentation

Documentation for the current development version of Dymos is available at https://openmdao.github.io/dymos/ as well as on the OpenMDAO web site: https://openmdao.org/dymos/docs/latest/. Archived versions for recent releases will also be found here: https://openmdao.org/dymos-documentation/

Defining Ordinary Differential Equations

The first step in simulating or optimizing a dynamical system is to define the ordinary differential equations to be integrated. The user first builds an OpenMDAO model which has outputs that provide the rates of the state variables. This model can be an OpenMDAO model of arbitrary complexity, including nested groups and components, layers of nonlinear solvers, etc.

Dymos solutions are constructed of one or more Phases. When setting up a phase, we add state variables, dynamic controls, and parameters, tell Dymos how the value of each should be connected to the ODE system, and tell Dymos the variable paths in the system that contain the rates of our state variables that are to be integrated.

Integrating Ordinary Differential Equations

Dymos's solver-based pseudspectral transcriptions provide the ability to numerically integrate the ODE system it is given. Used in an optimal control context, these provide a shooting method in which each iteration provides a physically viable trajectory.

Pseudospectral Methods

Dymos currently supports the Radau Pseudospectral Method and high-order Gauss-Lobatto transcriptions. These implicit techniques rely on the optimizer to impose "defect" constraints which enforce the physical accuracy of the resulting trajectories. To verify the physical accuracy of the solutions, Dymos can explicitly integrate them using variable-step methods.

Solving Optimal Control Problems

Dymos uses the concept of Phases to support optimal control of dynamical systems. Users connect one or more Phases to construct trajectories. Each Phase can have its own:

  • Optimal Control Transcription (Gauss-Lobatto or Radau Pseudospectral)
  • Equations of motion
  • Boundary and path constraints

Dymos Phases and Trajectories are ultimately just OpenMDAO Groups that can exist in a problem along with numerous other models, allowing for the simultaneous optimization of systems and dynamics.

import numpy as np
import openmdao.api as om
import dymos as dm
import matplotlib.pyplot as plt

# First define a system which computes the equations of motion
class BrachistochroneEOM(om.ExplicitComponent):
    def initialize(self):
        self.options.declare('num_nodes', types=int)

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

        # Inputs
        self.add_input('v', val=np.zeros(nn), units='m/s', desc='velocity')
        self.add_input('theta', val=np.zeros(nn), units='rad', desc='angle of wire')
        self.add_output('xdot', val=np.zeros(nn), units='m/s', desc='x rate of change')
        self.add_output('ydot', val=np.zeros(nn), units='m/s', desc='y rate of change')
        self.add_output('vdot', val=np.zeros(nn), units='m/s**2', desc='v rate of change')

        # Ask OpenMDAO to compute the partial derivatives using complex-step
        # with a partial coloring algorithm for improved performance
        self.declare_partials(of='*', wrt='*', method='cs')
        self.declare_coloring(wrt='*', method='cs', show_summary=True)

    def compute(self, inputs, outputs):
        v, theta = inputs.values()
        outputs['vdot'] = 9.80665 * np.cos(theta)
        outputs['xdot'] = v * np.sin(theta)
        outputs['ydot'] = -v * np.cos(theta)

p = om.Problem()

# Define a Trajectory object
traj = p.model.add_subsystem('traj', dm.Trajectory())

# Define a Dymos Phase object with GaussLobatto Transcription
tx = dm.GaussLobatto(num_segments=10, order=3)
phase = dm.Phase(ode_class=BrachistochroneEOM, transcription=tx)

traj.add_phase(name='phase0', phase=phase)

# Set the time options
phase.set_time_options(fix_initial=True,
                       duration_bounds=(0.5, 10.0))
# Set the state options
phase.add_state('x', rate_source='xdot',
                fix_initial=True, fix_final=True)
phase.add_state('y', rate_source='ydot',
                fix_initial=True, fix_final=True)
phase.add_state('v', rate_source='vdot',
                fix_initial=True, fix_final=False)
# Define theta as a control.
phase.add_control(name='theta', units='rad',
                  lower=0, upper=np.pi)
# Minimize final time.
phase.add_objective('time', loc='final')

# Set the driver.
p.driver = om.ScipyOptimizeDriver()

# Allow OpenMDAO to automatically determine total
# derivative sparsity pattern.
# This works in conjunction with partial derivative
# coloring to give a large speedup
p.driver.declare_coloring()

# Setup the problem
p.setup()

# Now that the OpenMDAO problem is setup, we can guess the
# values of time, states, and controls.
p.set_val('traj.phase0.t_duration', 2.0)

# States and controls here use a linearly interpolated
# initial guess along the trajectory.
p.set_val('traj.phase0.states:x',
          phase.interp('x', ys=[0, 10]),
          units='m')
p.set_val('traj.phase0.states:y',
          phase.interp('y', ys=[10, 5]),
          units='m')
p.set_val('traj.phase0.states:v',
          phase.interp('v', ys=[0, 5]),
          units='m/s')
# constant initial guess for control
p.set_val('traj.phase0.controls:theta', 90, units='deg')

# Run the driver to solve the problem and generate default plots of
# state and control values vs time
dm.run_problem(p, make_plots=True, simulate=True)

# Load the solution and simulation files.
sol_case = om.CaseReader('dymos_solution.db').get_case('final')
sim_case = om.CaseReader('dymos_simulation.db').get_case('final')

# Plot the results
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(12, 4.5))

axes[0].plot(sol_case.get_val('traj.phase0.timeseries.states:x'),
             sol_case.get_val('traj.phase0.timeseries.states:y'),
             'ro', label='solution')

axes[0].plot(sim_case.get_val('traj.phase0.timeseries.states:x'),
             sim_case.get_val('traj.phase0.timeseries.states:y'),
             'b-', label='simulation')

axes[0].set_xlabel('x (m)')
axes[0].set_ylabel('y (m/s)')
axes[0].legend()
axes[0].grid()

axes[1].plot(sol_case.get_val('traj.phase0.timeseries.time'),
             sol_case.get_val('traj.phase0.timeseries.controls:theta',
                              units='deg'),
             'ro', label='solution')

axes[1].plot(sim_case.get_val('traj.phase0.timeseries.time'),
             sim_case.get_val('traj.phase0.timeseries.controls:theta',
                              units='deg'),
             'b-', label='simulation')

axes[1].set_xlabel('time (s)')
axes[1].set_ylabel(r'$\theta$ (deg)')
axes[1].legend()
axes[1].grid()

plt.show()

Brachistochrone Solution

dymos's People

Contributors

ale5000 avatar andrewellis55 avatar bbahiam avatar dkilkenny avatar dpsanders avatar ehariton avatar hschilling avatar hwangjt avatar joel-martin avatar johnjasa avatar justinsgray avatar kanekosh avatar kaushikponnapalli avatar kenneth-t-moore avatar m-derra avatar mjfwest avatar naylor-b avatar nsteffen avatar pgkirsch avatar robfalck avatar swryan avatar tadkollar avatar wright avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dymos's Issues

Add time_phase variable that provides the current phase elapsed time

Sometimes it is necessary for ODE to know the current elapsed time since a phase started (i.e. an event).

  1. Add a new argument for time_phase targets to set_time_options
  2. Add time_phase output to time_comp to be connected to any time_phase targets given
  3. Allow time_phase to be used in the same contexts as time (boundary constraints, objectives, etc).

Replace **kwargs with actual argument list where possible.

This will make it easier for the user to find default values for arguments.
It will change Dymos such that calling set_state_options on the same state will override previous values.

  • set_state_options
  • set_time_options
  • add_path_constraint
  • add_boundary_constraint

timecomp refactor

Use the same time comp in GLM, Gauss-Lobatto, and Radau-PS phases

opt_duration=False causes errors

For instance, in the double integrator example, setting opt_duration=False causes the following.

NameError: Output 't_duration' does not exist for connection in 'phase0' from 't_duration' to 'continuity_comp.t_duration'.

GLM organization refactor

put GLM phase alongside gauss-lobatto and radau-ps phases in the file structure.
get rid of the ozone subfolder

implement set_values for GaussLobatto and RadauPS phases

set_values is the complement to get_values. Given a continuous time-series of data, interpolate as necessary and insert into the appropriate state/control values.

set_values should remove the need for the user directly calling phase.interpolate, and therefore has to support a similar option set (xs, ys, interpolation kind). Signature should be

phase.set_values(name, values, units=None, interp=None, interp_xs=None)

name is the dymos variable name, where internally we check to see if it is a time, state, control, or ODE variable.

values are the values to be assigned.

units are the units in which we are assigning the variable. Internally the appropriate unit conversion will occur.

interp is the type of interpolation to perform on the given values. Default should be None, which will raise an error if the given values aren't of the correct length.

interp_xs are the time/independent variable values to use, equivalent to the xs argument to phase.interpolate. If interp is None or 'linear' then xs are not necessary, otherwise an error should be raised in its absence.

Test

Encompasses all work performed leading up to the 1.0.0 major release.

Documentation Update

SSTO Linear Tangent docs need to be updated - currently don't contain the correct code.
Multi-phase cannonball contains a math display error that needs to be corrected.

Trajectory Objects

Trajectory objects are Groups which serve to organize a set of one or more phases.

Each Trajectory group consists of two primary subsystems:

  • phases
    A ParallelGroup within the Trajectory that can hold each phase, and potentially evaluate them in parallel
  • linkages
    A PhaseLinkageComp that manages all of the linkage constraints between phases

Trajectory objects as currently envisioned have the following primary methods:

  • add_phase(phase_name, phase_object, **kwargs)

    Phases are added to a trajectory with an add_phase method. Functionally, it's very similar to the add_subsystem method of ParallelGroup. Phases are cached in an internal dictionary _phases, and then actually added to the phases subgroup during setup.

  • link_phase(phases, vars=['*'], locs=('++', '--'))
    Trajectory objects provide a link_phases method that provides an ordering of phases in the trajectory. The goal of link_phases is to make common phase linkages (sequential) trivial, and complicated phase linkages (parallel, multiple branches, etc) tractable.

    Argument phases provides a series of two or more phases that follow one another in sequence. Argument vars provides the name of the time, state, controls, or control rates to be linked. Providing the value '*' in the list here will imply that time and all common state variables between the phases are to be linked. Providing a tuple-pair as one of the elements in vars allows variables of different names to be connected across phases.

    Finally locs provides the location of the connection for each pair of phases in the given sequence, using the --, -+, +-, ++ convention for states and controls.

    Ultimately link_phases should probably provide the same functionality as OTIS's global constraints, where any variables can be connected between two phases.

  • simulate(times, parallel=True)
    Simulate each phase in the trajectory, returning the results of each simulation in a dictionary. Use python's multiprocessing module to simulate each phase simultaneously when possible, to improve performance.

  • get_values(var, phases=None, nodes='all', flat=False)
    Return the values of a given variable at the given nodes through the given sequence of phases in the trajectory. Omitting phases returns the variable in all phases. If not present, return nan for each node in the phase. Might have to think about how we treat variables which may have different shapes in different phases, since plotting a 3-vector from various phases, and then just a 1-vector of nans from a phase where the variable is not present. If flat, rather than returning a dictionary, return a continuous array across phases.

timeseries outputs across a trajectory

Now that the post-processing get_values methods have been replaced by timeseries outputs, a trajectory should have a timeseries output so users can access timeseries across the entire trajectory.

This isn't completely trivial since a state/control variable may not exists in all phases across a trajectory, or more change from being a state in one phase to a control or design parameter in another.

Custom Phase-Wide Control Classes

Add custom control profiles at the phase level.

phase.add_control(..., control_class=X)

The control class will declare it's on design variables, promoted to 'controls:' in the phase.
The control class will provide control values at the control discretization nodes, which will then be connected through the standard control interpolation functions.

simulate updates

phase.simulate should take times as a number or an iterable.

  • If a number, return that many evenly distributed points across the phase (np.linspace(t_initial, t_initial+t_duration, N).
  • If an iterable, require it to be a monotonic increasing or decreasing sequence of times within the phase at which output is requested>
  • It should be a warning to request an extrapolation of time

Fix some issues with RungeKuttaPhase

Move direction out of phase options and put on state options as time_direction.
Add 'solve_subsystems': True to the default continuity solver options.
Add a test case that uses phase (optimizer constrained) linkages.

User-defined timeseries interpolation outputs.

Allow the user to specify phase-tau points at which the timeseries outputs should be provided. The timeseries output will then use interpolation to provide results at the desired points.

Add explicit phases

Adds explicit phases which will initially use fixed-step RK4 integration across each segment of a phase.

add_input_parameter dimension mismatch

When using add_input_parameter to pass static information from outside of Dymos into a phase, there is a shape mismatch error.

from openmdao.api import Group, ExecComp, Problem
from openmdao.api import DirectSolver, pyOptSparseDriver

from dymos import declare_time, declare_state, declare_parameter
from dymos import Phase
from dymos.utils.lgl import lgl
from dymos.models.eom import FlightPathEOM2D
         
import numpy as np
       
@declare_time(units='s')
@declare_state('v', rate_source='eom.v_dot', units='m/s')
@declare_state('h', rate_source='eom.h_dot', units='m')
@declare_parameter('m', targets='sum.m', units='kg', shape=(2, 2))

class TrajectoryODE(Group):
    def initialize(self):
        self.options.declare('num_nodes', types=int)    
    
    def setup(self):
        nn = self.options['num_nodes']
        
        self.add_subsystem('sum', ExecComp('m_tot = sum(m)', 
                                           m={'value':np.zeros(nn)}, 
                                           m_tot={'value':np.zeros(nn)}))
        
        self.add_subsystem('eom', FlightPathEOM2D(num_nodes=nn))
        
        self.connect('sum.m_tot','eom.m')
        
def BallDrop(optimizer='SLSQP', num_segments=1, transcription_order=5):
    
    p = Problem(model=Group())
    
    p.driver = pyOptSparseDriver()
    p.driver.options['optimizer'] = optimizer
    p.driver.options['dynamic_simul_derivs'] = True

    seg_ends, _ = lgl(num_segments + 1)

    phase = Phase(transcription='radau-ps',
                  ode_class=TrajectoryODE,
                  num_segments=num_segments, transcription_order=transcription_order,
                  segment_ends=seg_ends,
                  )    
    
    p.model.add_subsystem('phase0', phase)
    
    phase.set_time_options(initial_bounds=(0.0, 100.0), duration_bounds=(0.,100.))
    
    phase.set_state_options('h', fix_initial=True, fix_final=True, lower=0.0, units='m')
    phase.set_state_options('v', fix_initial=True, fix_final=False, units='m/s')    

    phase.add_input_parameter('m', val=[[1,1],[2,2]], units='kg')
    
    p.model.linear_solver = DirectSolver()
    
    p.setup(check=True, force_alloc_complex=True)

    p['phase0.t_initial'] = 0.0
    p['phase0.t_duration'] = 100.0

    p['phase0.states:h'] = phase.interpolate(ys=[20, 0], nodes='state_input')
    p['phase0.states:v'] = phase.interpolate(ys=[0, -5], nodes='state_input')

    
    p.run_model()

    print('Time:')
    print(p.get_val('phase0.timeseries.time'))
    print('')
    print('Height:')
    print(p.get_val('phase0.timeseries.states:h'))
    print('')
    print('Velocity:')
    print(p.get_val('phase0.timeseries.states:v'))

if __name__ == '__main__':
    p = BallDrop()
Traceback (most recent call last):
  File "mre_target_shape.py", line 84, in <module>
    p = BallDrop()
  File "mre_target_shape.py", line 62, in BallDrop
    p.setup(check=True, force_alloc_complex=True)
  File "/mnt/c/OMDAO/OpenMDAO.git/openmdao/core/problem.py", line 755, in setup
    derivatives)
  File "/mnt/c/OMDAO/OpenMDAO.git/openmdao/core/system.py", line 697, in _setup
    self._setup_connections(recurse=recurse)
  File "/mnt/c/OMDAO/OpenMDAO.git/openmdao/core/group.py", line 913, in _setup_connections
    subsys._setup_connections(recurse)
  File "/mnt/c/OMDAO/OpenMDAO.git/openmdao/core/group.py", line 1084, in _setup_connections
    len(out_shape), source_dimensions))
ValueError: The source indices [[[0 1]  [2 3]] [[0 1]  [2 3]] [[0 1]  [2 3]] [[0 1]  [2 3]] [[0 1]  [2 3]] [[0 1]  [2 3]]] do not specify a valid shape for the connection 'phase0.input_params.input_parameters:m_out' to 'phase0.rhs_all.sum.m'. The source has 3 dimensions but the indices expect 2.

default pointer-heritage phases to compressed=True

Compressed transcription seems to be giving a substantial performance advantage over uncompressed. It might even be worth looking into testing if pyoptsparse is ordering linear constraints in the SNOPT-recommended way.

Additional state options needed for RungeKuttaPhase and solved pseudospectral phases

Options fixed_initial and fixed_final are not sufficient to determine the direction of integration of each variable. Change the current state option direction (used only by RungeKuttaPhase at the moment) to time_direction.

Options connected_initial and connected_final specify that either the initial state value or final state value is to be connected to another output.

If options fixed_initial or fixed_final are True, then the optimizer owns those values and the initial/final values are not design variables. If they are fixed then they cannot be connected. fixed_final cannot be used in forward shooting (time_direction = 1) and option fixed_initial cannot be used with backward shooting time_direction = -1

If options fixed_initial or fixed_final are False, and the respective connect_initial or connect_final is False, then the initial/final value is governed by the optimizer as a design variable.

There are various combinations of these options that are invalid that need to be checked.

Connection issues when connecting static, shaped input parameters

import numpy as np

from openmdao.api import ExplicitComponent, Group, Problem
from dymos import declare_time, declare_state, declare_parameter, Phase, ODEOptions


n_traj = 4


class MyComp(ExplicitComponent):

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

    def setup(self):
        nn = self.options['num_nodes']
        n_traj = self.options['n_traj']
        self.add_input('time', val=np.zeros(nn))
        self.add_input('alpha', shape=np.zeros((n_traj, 2)).shape)

        self.add_output('y', val=np.zeros(nn))

    def compute(self, inputs, outputs):
        pass

    def compute_partials(self, inputs, partials):
        pass

class MyODE(Group):
    ode_options = ODEOptions()
    ode_options.declare_time(units='s', targets = ['comp.time'])
    ode_options.declare_state(name='F', rate_source='comp.y')
    ode_options.declare_parameter(name='alpha', shape=(4,2), targets = 'comp.alpha')

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

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

        self.add_subsystem(name='comp',
                           subsys=MyComp(num_nodes=nn,
                                         n_traj=n_traj))

p = Problem(model=Group())

phase = Phase(transcription='gauss-lobatto',
              ode_class=MyODE,
              num_segments=25,
              transcription_order=3,
              compressed=True)

p.model.add_subsystem('phase0', phase)

phase.add_input_parameter('alpha', val=np.ones((n_traj, 2)), units='m')

p.setup()

Results in error:

ValueError: The source indices [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] do not specify a valid shape for the connection 'phase0.input_params.input_parameters:alpha_out' to 'phase0.rhs_disc.comp.alpha'. The target shape is (2, 2) but indices are (50,).

Replace get_values functionality with a component that provides timeseries outputs

Part 1:

Phases get a new component timeseries_output_comp which aggregates all output variables.

By default, the following timeseries outputs are provided:

  • time
  • time_phase
  • states
  • controls
  • design parameters
  • input parameters
  • constraints
  • objective

User may add additional timeseries outputs using
phase.add_timeseries_output(ode_rel_path)

Outputs are accessible via path_to_phase.outputs:output_name

Part 2:

Remove the complexity of PhaseSimulationResults and TrajSimulationResults by having simulate only record the specified timeseries outputs.

Allow the rate source for a state to be connected to a dynamic control or another state.

Sometimes it's convenient to define the rate of a variable as a control, and then integrate it to keep track of the control value. Currently, doing so requires a 'pass-through' component in the ODE that just echoes the control value as an output.

This change would allow the user to declare_state with a rate_source of None in the ODE, which could then be overridden with a rate_source option to set_state_options.

For some problems, like the double integrator, the problem could potentially be solved without an ODE at all.

AnalyticPhase

Define an AnalyticPhase for which the solution to the ODE is known analytically. Rather than taking an ODE, take a system that provides a solution for any time.

This would be useful to have on some of the project's we're currently working, so I'm bumping the priority up.

For the initial cut, we need a new Phase called AnalyticPhase that inherits fromPhase and overrides some of its methods.

Handling states

In an analytic phase, states are really just outputs of the form f(x0, t, p).
That is analytic phase will, at least initially, fundamentally just solve the IVP analytically, providing states just as any other output.
We'll maintain an official list of states for the sake of keeping things consistent with other phases in dymos when it comes to linking them together and stuff like that.

We will override the set_state_options method so that it doesn't have a rate_src, but instead has val_src - the output in the ODE that provides the value of the state at a given time.

Handling controls

Initially there will be no support for controls in AnalyticPhase.
Attempting to add a control or polynomial control will raise an exception.
I'm not certain that there's any example where an ODE with dynamic controls has an analytic solution.

TImeseries outputs

Timeseries outputs will still be provided in the form phasename.timeseries.* in order to be consistent with other phases, to support constraints and linkages.

Transcriptions

I don't think it makes sense that AnalyticPhases have transcriptions. In this case we're not really converting an infinite-dimensional optimal control problem to a discrete optimization problem.

Still, the mechanisms in dymos dictate that the transcription helps to setup/configure the phase, so we'll have an AnalyticTranscription that will just automatically be associated with the AnalyticPhase.

The next question is, where do the nodes go?
For this initial cut I think it makes sense that AnalyticPhase has a num_nodes option and provides output nodes by using the formula np.linspace(t_initial, t_initial + t_duration, num_nodes).

Constraints

We should still support path and boundary constraints as we do with the other Phase/transcription types.
From a control viewpoint, this phase is similar to ExplicitShooting, in that it fundamentally is governed by values of the initial states, parameters, initial time and duration.

Possible example cases

  1. Propagating a 2-body orbit using f and g series. Example here

  2. Propagate some simple ODE, such as the one here

Phase design parameters support external connection via the `input=True` argument

This change makes phase design parameters consistent with times in that they support external connections via the input=True argument to add_design_parameter.

As with time, once a design parameter is set to be input from an external source, the user is playing by big kid rules. Options on the design parameter dealing with optimization are invalid.

Trajectories support design parameters

This enhancement allows trajectories to own design parameters. The design parameters can then be connected to each phase within the trajectory.

Syntax:
trajectory.add_design_parameter(name, val, units=None, targets=None, opt=True, input=False, lower, upper, adder, scaler, ref0, ref)

All options are the same for the phase add_design_parameter method except for targets. If given, targets is a dictionary that maps each phase name to the ODE parameter name to which it is being connected. This allows a single design parameter at the trajectory level to be connected to variables which may have different names in different phases. If targets is None, assume that the given name is also the name of the target parameter in each phase.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.