Source code for dymos.phase.analytic_phase

from copy import deepcopy

import openmdao.api as om

from .phase import Phase
from ..transcriptions import Analytic
from .options import StateOptionsDictionary

from ..utils.misc import _unspecified


class AnalyticPhase(Phase):
    """
    The AnalyticPhase object in Dymos.

    The AnalyticPhase object in dymos inherits from PhaseBase but is used to override some base methods with ones
    that will warn about certain options or methods being invalid for the AnalyticPhase.

    Parameters
    ----------
    from_phase : <Phase> or None
        A phase instance from which the initialized phase should copy its data.
    **kwargs : dict
        Dictionary of optional phase arguments.
    """

    def __init__(self, from_phase=None, **kwargs):
        super().__init__(from_phase=from_phase, **kwargs)
        self.simulate_options = None

    def initialize(self):
        """
        Declare instantiation options for the phase.
        """
        super().initialize()
        self.options.declare('num_nodes', types=int, default=2,
                             desc='Number of points in time at which to evaluate the solution to the ODE.')

[docs] def add_state(self, name, state_name=None, units=_unspecified, shape=_unspecified): """ Add a state variable to be integrated by the phase. Parameters ---------- name : str Path to use as the source of the state variable. state_name : str Name of the state variable, if the last element of the source path is ambiguous. units : str or None Units in which the state variable is defined. If units is not specified here then the unit will be determined from the source. shape : tuple of int The shape of the state variable. For instance, a 3D cartesian position vector would have a shape of (3,). """ _state_name = name.split('.')[-1] if state_name is None else state_name if name not in self.state_options: self.state_options[_state_name] = StateOptionsDictionary() self.state_options[_state_name]['name'] = _state_name self.set_state_options(name=name, state_name=state_name, units=units, shape=shape)
def set_state_options(self, name, state_name=None, units=_unspecified, shape=_unspecified): """ Set options that apply the EOM state variable of the given name. Parameters ---------- name : str Path to use as the source of the state variable. state_name : str Name of the state variable, if the last element of the source path is ambiguous. units : str or None Units in which the state variable is defined. If units is not specified here then the unit will be determined from the source. shape : tuple of int The shape of the state variable. For instance, a 3D cartesian position vector would have a shape of (3,). """ source = name _state_name = name.split('.')[-1] if state_name is None else state_name if name not in self.state_options: self.state_options[_state_name] = StateOptionsDictionary() self.state_options[_state_name]['name'] = _state_name if units is not _unspecified: self.state_options[_state_name]['units'] = units if shape is not _unspecified: self.state_options[_state_name]['shape'] = shape if source is not _unspecified: self.state_options[_state_name]['source'] = source def add_control(self, name, units=_unspecified, desc=_unspecified, opt=_unspecified, fix_initial=_unspecified, fix_final=_unspecified, targets=_unspecified, rate_targets=_unspecified, rate2_targets=_unspecified, val=_unspecified, shape=_unspecified, lower=_unspecified, upper=_unspecified, scaler=_unspecified, adder=_unspecified, ref0=_unspecified, ref=_unspecified, continuity=_unspecified, continuity_scaler=_unspecified, rate_continuity=_unspecified, rate_continuity_scaler=_unspecified, rate2_continuity=_unspecified, rate2_continuity_scaler=_unspecified): """ Adds a dynamic control variable to be tied to a parameter in the ODE. Parameters ---------- name : str The name assigned to the control variable. If the ODE has been decorated with parameters, this should be the name of a control in the system. units : str or None The units with which the control parameter in this phase will be defined. It must be compatible with the units of the targets to which the control is connected. desc : str A description of the control variable. opt : bool If True, the control value will be a design variable for the optimization problem. If False, allow the control to be connected externally. fix_initial : bool If True, the initial value of this control is fixed and not a design variable. This option is invalid if opt=False. fix_final : bool If True, the final value of this control is fixed and not a design variable. This option is invalid if opt=False. targets : Sequence of str or None Targets in the ODE to which this control is connected. In the future, if left _unspecified (the default), the phase control will try to connect to an ODE input of the same name. Set targets to None to prevent this. rate_targets : Sequence of str or None The targets in the ODE to which the control rate is connected. rate2_targets : Sequence of str or None The parameter in the ODE to which the control 2nd derivative is connected. val : float The default value of the control variable at the control input nodes. shape : Sequence of int The shape of the control variable at each point in time. Only needed for controls that don't have a target in the ode. lower : Sequence of Number or None The lower bound of the control variable at the nodes. This option is invalid if opt=False. upper : Sequence or Number or None The upper bound of the control variable at the nodes. This option is invalid if opt=False. scaler : float or None The scaler of the control variable at the nodes. This option is invalid if opt=False. adder : float or None The adder of the control variable at the nodes. This option is invalid if opt=False. ref0 : float or None The zero-reference value of the control variable at the nodes. This option is invalid if opt=False. ref : float or None The unit-reference value of the control variable at the nodes. This option is invalid if opt=False. continuity : bool Enforce continuity of control values at segment boundaries. This option is invalid if opt=False. continuity_scaler : bool Scaler of the continuity constraint. This option is invalid if opt=False. This option is only relevant in the Radau pseudospectral transcription where the continuity constraint is nonlinear. For Gauss-Lobatto the continuity constraint is linear. rate_continuity : bool Enforce continuity of control first derivatives (in dimensionless time) at segment boundaries. This option is invalid if opt=False. rate_continuity_scaler : float Scaler of the rate continuity constraint at segment boundaries. This option is invalid if opt=False. rate2_continuity : bool Enforce continuity of control second derivatives at segment boundaries. This option is invalid if opt=False. rate2_continuity_scaler : float Scaler of the dimensionless rate continuity constraint at segment boundaries. This option is invalid if opt=False. Notes ----- rate and rate2 continuity are not enforced for input controls. """ raise NotImplementedError('AnalyticPhase does not support controls.') def set_control_options(self, name, units=_unspecified, desc=_unspecified, opt=_unspecified, fix_initial=_unspecified, fix_final=_unspecified, targets=_unspecified, rate_targets=_unspecified, rate2_targets=_unspecified, val=_unspecified, shape=_unspecified, lower=_unspecified, upper=_unspecified, scaler=_unspecified, adder=_unspecified, ref0=_unspecified, ref=_unspecified, continuity=_unspecified, continuity_scaler=_unspecified, rate_continuity=_unspecified, rate_continuity_scaler=_unspecified, rate2_continuity=_unspecified, rate2_continuity_scaler=_unspecified): """ Set options on an existing dynamic control variable in the phase. Parameters ---------- name : str The name assigned to the control variable. If the ODE has been decorated with parameters, this should be the name of a control in the system. units : str or None The units with which the control parameter in this phase will be defined. It must be compatible with the units of the targets to which the control is connected. desc : str A description of the control variable. opt : bool If True, the control value will be a design variable for the optimization problem. If False, allow the control to be connected externally. fix_initial : bool If True, the initial value of this control is fixed and not a design variable. This option is invalid if opt=False. fix_final : bool If True, the final value of this control is fixed and not a design variable. This option is invalid if opt=False. targets : Sequence of str or None Targets in the ODE to which this control is connected. In the future, if left _unspecified (the default), the phase control will try to connect to an ODE input of the same name. Set targets to None to prevent this. rate_targets : Sequence of str or None The targets in the ODE to which the control rate is connected. rate2_targets : Sequence of str or None The parameter in the ODE to which the control 2nd derivative is connected. val : float The default value of the control variable at the control input nodes. shape : Sequence of int The shape of the control variable at each point in time. Only needed for controls that don't have a target in the ode. lower : Sequence of Number or None The lower bound of the control variable at the nodes. This option is invalid if opt=False. upper : Sequence or Number or None The upper bound of the control variable at the nodes. This option is invalid if opt=False. scaler : float or None The scaler of the control variable at the nodes. This option is invalid if opt=False. adder : float or None The adder of the control variable at the nodes. This option is invalid if opt=False. ref0 : float or None The zero-reference value of the control variable at the nodes. This option is invalid if opt=False. ref : float or None The unit-reference value of the control variable at the nodes. This option is invalid if opt=False. continuity : bool Enforce continuity of control values at segment boundaries. This option is invalid if opt=False. continuity_scaler : bool Scaler of the continuity constraint. This option is invalid if opt=False. This option is only relevant in the Radau pseudospectral transcription where the continuity constraint is nonlinear. For Gauss-Lobatto the continuity constraint is linear. rate_continuity : bool Enforce continuity of control first derivatives (in dimensionless time) at segment boundaries. This option is invalid if opt=False. rate_continuity_scaler : float Scaler of the rate continuity constraint at segment boundaries. This option is invalid if opt=False. rate2_continuity : bool Enforce continuity of control second derivatives at segment boundaries. This option is invalid if opt=False. rate2_continuity_scaler : float Scaler of the dimensionless rate continuity constraint at segment boundaries. This option is invalid if opt=False. Notes ----- rate and rate2 continuity are not enforced for input controls. """ raise NotImplementedError('AnalyticPhase does not support controls.') def add_polynomial_control(self, name, order, desc=_unspecified, val=_unspecified, units=_unspecified, opt=_unspecified, fix_initial=_unspecified, fix_final=_unspecified, lower=_unspecified, upper=_unspecified, scaler=_unspecified, adder=_unspecified, ref0=_unspecified, ref=_unspecified, targets=_unspecified, rate_targets=_unspecified, rate2_targets=_unspecified, shape=_unspecified): """ Adds an polynomial control variable to be tied to a parameter in the ODE. Polynomial controls are defined by values at the Legendre-Gauss-Lobatto nodes of a single polynomial, defined on [-1, 1] in phase tau space. For a polynomial control of a given order, the number of nodes used to define the polynomial is (order + 1). Parameters ---------- name : str Name of the controllable parameter in the ODE. order : int The order of the interpolating polynomial used to represent the control value in phase tau space. desc : str A description of the polynomial control. val : float or ndarray Default value of the control at all nodes. If val scalar and the control is dynamic it will be broadcast. units : str or None or 0 Units in which the control variable is defined. If 0, use the units declared for the parameter in the ODE. opt : bool If True (default) the value(s) of this control will be design variables in the optimization problem, in the path 'phase_name.indep_controls.controls:control_name'. If False, the values of this control will exist as input controls:{name}. fix_initial : bool If True, the given initial value of the polynomial control is not a design variable and will not be changed during the optimization. fix_final : bool If True, the given final value of the polynomial control is not a design variable and will not be changed during the optimization. lower : float or ndarray The lower bound of the control at the nodes of the phase. upper : float or ndarray The upper bound of the control at the nodes of the phase. scaler : float or ndarray The scaler of the control value at the nodes of the phase. adder : float or ndarray The adder of the control value at the nodes of the phase. ref0 : float or ndarray The zero-reference value of the control at the nodes of the phase. ref : float or ndarray The unit-reference value of the control at the nodes of the phase. targets : Sequence of str or None Targets in the ODE to which this polynomial control is connected. rate_targets : None or str The name of the parameter in the ODE to which the first time-derivative of the control value is connected. rate2_targets : None or str The name of the parameter in the ODE to which the second time-derivative of the control value is connected. shape : Sequence of int The shape of the control variable at each point in time. """ raise NotImplementedError('AnalyticPhase does not support polynomial controls.') def setup(self): """ Build the model hierarchy for a Dymos AnalyticPhase. """ # Finalize the variables if it hasn't happened already. # If this phase exists within a Trajectory, the trajectory will finalize them during setup. transcription = self.options['transcription'] = Analytic(order=self.options['num_nodes']) transcription.setup_time(self) if self.control_options: transcription.setup_controls(self) if self.parameter_options: transcription.setup_parameters(self) # Never allow state rate outputs for analytic phases self.timeseries_options['include_state_rates'] = False self.timeseries_options._dict['include_state_rates']['values'] = [False] transcription.setup_states(self) self._check_ode() transcription.setup_ode(self) transcription.setup_timeseries_outputs(self) transcription.setup_defects(self) transcription.setup_solvers(self) def simulate(self, times_per_seg=10, method=_unspecified, atol=_unspecified, rtol=_unspecified, first_step=_unspecified, max_step=_unspecified, record_file=None): """ Stub to make sure users are informed that simulate cannot be done on AnalyticPhase. Parameters ---------- times_per_seg : int or None Number of equally spaced times per segment at which output is requested. If None, output will be provided at all Nodes. method : str The scipy.integrate.solve_ivp integration method. atol : float Absolute convergence tolerance for scipy.integrate.solve_ivp. rtol : float Relative convergence tolerance for scipy.integrate.solve_ivp. first_step : float Initial step size for the integration. max_step : float Maximum step size for the integration. record_file : str or None 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 ------- problem An OpenMDAO Problem in which the simulation is implemented. This Problem interface can be interrogated to obtain timeseries outputs in the same manner as other Phases to obtain results at the requested times. """ raise NotImplementedError('Method `simulate` is not available for AnalyticPhase.') def set_simulate_options(self, *args, **kwargs): """ Stub to make sure users are informed that simulate cannot be done on AnalyticPhase. Parameters ---------- *args Position arguments. **kwargs : float Keyword arguments. Raises ------ NotImplementedError Simulation cannot be performed on AnalyticPhase. """ raise NotImplementedError('Method set_simulate_options is not available for AnalyticPhase.') def duplicate(self, num_nodes=None, boundary_constraints=False, path_constraints=False, objectives=False, fix_initial_time=False): """ Create a copy of this phase where most options and attributes are deep copies of those in the original. By default, a deepcopy of the transcription in the original phase is used. Boundary constraints, path constraints, and objectives are _NOT_ copied by default, but the user may opt to do so. By default, initial time is not fixed, nor are the initial or final state values. These also can be overridden with the appropriate arguments. Parameters ---------- num_nodes : int or None The number of nodes to use in the new phase, or None if it should use the same number as the phase being duplicated. boundary_constraints : bool If True, retain all boundary constraints from the phase to be copied. path_constraints : bool If True, retain all path constraints from the phase to be copied. objectives : bool If True, retain all objectives from the phase to be copied. fix_initial_time : bool If True, fix the initial time of the returned phase. Returns ------- AnalyticPhase The new phase created by duplicating this one. """ nn = num_nodes if num_nodes is not None else self.options['num_nodes'] ode_class = self.options['ode_class'] ode_init_kwargs = self.options['ode_init_kwargs'] auto_solvers = self.options['auto_solvers'] p = AnalyticPhase(num_nodes=nn, ode_class=ode_class, ode_init_kwargs=ode_init_kwargs, auto_solvers=auto_solvers) p.time_options.update(deepcopy(self.time_options)) p.time_options['fix_initial'] = fix_initial_time for state_name, state_options in self.state_options.items(): p.state_options[state_name] = deepcopy(state_options) for param_name, param_options in self.parameter_options.items(): p.parameter_options[param_name] = deepcopy(param_options) p._timeseries = deepcopy(self._timeseries) p.refine_options = deepcopy(self.refine_options) p.simulate_options = deepcopy(self.simulate_options) p.timeseries_options = deepcopy(self.timeseries_options) if boundary_constraints: p._initial_boundary_constraints = deepcopy(self._initial_boundary_constraints) p._final_boundary_constraints = deepcopy(self._final_boundary_constraints) if path_constraints: p._path_constraints = deepcopy(self._path_constraints) if objectives: p._objectives = deepcopy(self._objectives) return p