"""
@authors: Alberto FOSSA' Giuliana Elena MICELI
"""
import numpy as np
from latom.nlp.nlp import SinglePhaseNLP, MultiPhaseNLP
from latom.odes.odes_2d import ODE2dVarThrust, ODE2dConstThrust, ODE2dVertical
from latom.odes.odes_2d_group import ODE2dVToff
from latom.guess.guess_2d import TwoDimAscGuess, TwoDimDescGuess
[docs]class TwoDimNLP(SinglePhaseNLP):
"""TwoDimNLP class transcribes a two-dimensional, continuous-time optimal control problem in trajectory
optimization into a Non Linear Programming Problem (NLP) using the OpenMDAO and dymos libraries.
The two-dimensional trajectory is described in polar coordinates centered at the center of the attracting body.
Parameters
----------
body : Primary
Instance of `Primary` class representing the central attracting body
sc : Spacecraft
Instance of `Spacecraft` class representing the spacecraft
alt : float
Orbit altitude [m]
alpha_bounds : iterable
Lower and upper bounds on thrust vector direction [rad]
method : str
Transcription method used to discretize the continuous time trajectory into a finite set of nodes,
allowed ``gauss-lobatto``, ``radau-ps`` and ``runge-kutta``
nb_seg : int
Number of segments in which each phase is discretized
order : int
Transcription order within each phase, must be odd
solver : str
NLP solver, must be supported by OpenMDAO
ode_class : ExplicitComponent
Instance of OpenMDAO `ExplicitComponent` describing the Ordinary Differential Equations (ODEs) that drive the
system dynamics
ode_kwargs : dict
Keywords arguments to be passed to `ode_class`
ph_name : str
Name of the phase within OpenMDAO
snopt_opts : dict or None, optional
SNOPT optional settings expressed as key-value pairs. Refer to the SNOPT User Guide for more details.
Default is None
rec_file : str or None, optional
Name of the file in which the computed solution is recorded or None. Default is None
Attributes
----------
alt : float
Orbit altitude [m]
alpha_bounds : ndarray
Lower and upper bounds on thrust vector direction [rad]
r_circ : float
Orbit radius [m]
v_circ : float
Orbital velocity [m/s]
guess : TwoDimGuess
Initial guess to be provided before solving the NLP
"""
def __init__(self, body, sc, alt, alpha_bounds, method, nb_seg, order, solver, ode_class,
ode_kwargs, ph_name, snopt_opts=None, rec_file=None):
"""Initializes TwoDimNLP class. """
# set central body, spacecraft and transcription
SinglePhaseNLP.__init__(self, body, sc, method, nb_seg, order, solver, ode_class, ode_kwargs, ph_name,
snopt_opts=snopt_opts, rec_file=rec_file)
self.alt = alt
self.alpha_bounds = np.asarray(alpha_bounds)
self.r_circ = body.R + self.alt
self.v_circ = (body.GM/self.r_circ)**0.5
self.guess = None
[docs] def set_states_options(self, theta, u_bound=None):
"""Set options on the state variables of the NLP.
Parameters
----------
theta : float
Reference value for spawn angle [rad]
u_bound : str or None, optional
Bounds on spacecraft radial velocity between ``lower`` and ``upper`` or None. Default is None
"""
# radius
self.phase.set_state_options('r', fix_initial=True, fix_final=True, lower=1.0, ref0=1.0,
ref=self.r_circ/self.body.R)
# angle (all cases except descent with variable thrust and constraint on minimum safe altitude)
if theta > 0.0:
self.phase.set_state_options('theta', fix_initial=True, fix_final=False, lower=0.0, ref=theta)
# angle (only descent with variable thrust and constraint on minimum safe altitude)
else:
self.phase.set_state_options('theta', fix_initial=False, fix_final=True, upper=0.0, adder=-theta,
scaler=-1.0/theta)
# positive radial velocity (ascent)
if u_bound == 'lower':
self.phase.set_state_options('u', fix_initial=True, fix_final=True, lower=0.0, ref=self.v_circ/self.body.vc)
# negative radial velocity (descent)
elif u_bound == 'upper':
self.phase.set_state_options('u', fix_initial=True, fix_final=True, upper=0.0,
adder=self.v_circ/self.body.vc, scaler=self.body.vc/self.v_circ)
# no path constraints on radial velocity
elif u_bound is None:
self.phase.set_state_options('u', fix_initial=True, fix_final=True, ref=self.v_circ/self.body.vc)
else:
raise ValueError('u_bound must be either lower, upper or None')
# tangential velocity
self.phase.set_state_options('v', fix_initial=True, fix_final=True, lower=0.0, ref=self.v_circ/self.body.vc)
# spacecraft mass
self.phase.set_state_options('m', fix_initial=True, fix_final=False, lower=self.sc.m_dry, upper=self.sc.m0,
ref0=self.sc.m_dry, ref=self.sc.m0)
[docs] def set_controls_options(self, throttle=True):
"""Set options on control variables.
Parameters
----------
throttle : bool, optional
``True`` for variable thrust magnitude, ``False`` otherwise. Default is `'True``
"""
if throttle:
twr_min = self.sc.T_min / self.sc.m0 / self.body.g
self.phase.add_control('thrust', fix_initial=False, fix_final=False, continuity=False,
rate_continuity=False, rate2_continuity=False, lower=twr_min, upper=self.sc.twr,
ref0=twr_min, ref=self.sc.twr)
else:
self.phase.add_design_parameter('thrust', opt=False, val=self.sc.twr)
self.phase.add_control('alpha', fix_initial=False, fix_final=False, continuity=True, rate_continuity=True,
rate2_continuity=False, lower=self.alpha_bounds[0], upper=self.alpha_bounds[1],
ref=self.alpha_bounds[1])
self.phase.add_design_parameter('w', opt=False, val=self.sc.w/self.body.vc)
[docs] def set_initial_guess(self, check_partials=False, fix_final=False, throttle=True):
"""Set the initial guess for the iterative solution of the NLP.
Parameters
----------
check_partials : bool, optional
Check the partial derivatives computed analytically against complex step method. Default is ``False``
fix_final : bool, optional
``True`` if the final time is fixed, ``False`` otherwise. Default is ``False``
throttle : bool, optional
``True`` for variable thrust magnitude, ``False`` otherwise. Default is `'True``
"""
self.set_initial_guess_interpolation(bcs=np.ones((7, 2)), check_partials=False, throttle=throttle)
self.guess.compute_trajectory(fix_final=fix_final, t_eval=self.t_all*self.body.tc, throttle=throttle)
self.p[self.phase_name + '.states:r'] = np.take(self.guess.states[:, 0]/self.body.R, self.state_nodes)
self.p[self.phase_name + '.states:theta'] = np.take(self.guess.states[:, 1], self.state_nodes)
self.p[self.phase_name + '.states:u'] = np.take(self.guess.states[:, 2]/self.body.vc, self.state_nodes)
self.p[self.phase_name + '.states:v'] = np.take(self.guess.states[:, 3]/self.body.vc, self.state_nodes)
self.p[self.phase_name + '.states:m'] = np.take(self.guess.states[:, 4], self.state_nodes)
self.p[self.phase_name + '.controls:alpha'] = np.take(self.guess.controls[:, 1], self.control_nodes)
if throttle: # variable thrust
self.p[self.phase_name + '.controls:thrust'] = np.take(self.guess.controls[:, 0]/self.sc.m0/self.body.g,
self.control_nodes)
self.p.run_model()
if check_partials:
self.p.check_partials(method='cs', compact_print=True, show_only_incorrect=True)
[docs] def set_initial_guess_interpolation(self, bcs=np.ones((7, 2)), check_partials=False, throttle=True):
"""Set the initial guess for the solution of the NLP interpolating the Boundary Conditions (BCs) imposed on the
state and control variables.
Parameters
----------
bcs : ndarray, optional
Boundary Conditions on state and control variables. Default is all ones
check_partials : bool, optional
Check the partial derivatives computed analytically against complex step method. Default is ``False``
throttle : bool, optional
``True`` for variable thrust magnitude, ``False`` otherwise. Default is `'True``
"""
self.set_time_guess(self.tof)
self.p[self.phase_name + '.states:r'] = self.phase.interpolate(ys=bcs[0], nodes='state_input')
self.p[self.phase_name + '.states:theta'] = self.phase.interpolate(ys=bcs[1], nodes='state_input')
self.p[self.phase_name + '.states:u'] = self.phase.interpolate(ys=bcs[2], nodes='state_input')
self.p[self.phase_name + '.states:v'] = self.phase.interpolate(ys=bcs[3], nodes='state_input')
self.p[self.phase_name + '.states:m'] = self.phase.interpolate(ys=bcs[4], nodes='state_input')
self.p[self.phase_name + '.controls:alpha'] = self.phase.interpolate(ys=bcs[5], nodes='control_input')
if throttle:
self.p[self.phase_name + '.controls:alpha'] = self.phase.interpolate(ys=bcs[6], nodes='control_input')
self.p.run_model()
if check_partials:
self.p.check_partials(method='cs', compact_print=True, show_only_incorrect=True)
[docs]class TwoDimConstNLP(TwoDimNLP):
"""TwoDimConstNLP class transcribes a two-dimensional, continuous-time optimal control problem in trajectory
optimization into a Non Linear Programming Problem (NLP) using the OpenMDAO and dymos libraries.
The two-dimensional trajectory is described in polar coordinates centered at the center of the attracting body.
The thrust delivered by the spacecraft engines is supposed to have a constant magnitude throughout the whole phase.
Parameters
----------
body : Primary
Instance of `Primary` class representing the central attracting body
sc : Spacecraft
Instance of `Spacecraft` class representing the spacecraft
alt : float
Orbit altitude [m]
theta : float
Guessed spawn angle [rad]
alpha_bounds : tuple
Lower and upper bounds on thrust vector direction [rad]
tof : float
Guessed time of flight [s]
t_bounds : tuple
Time of flight bounds expressed as fraction of TOF [-]
method : str
Transcription method used to discretize the continuous time trajectory into a finite set of nodes,
allowed ``gauss-lobatto``, ``radau-ps`` and ``runge-kutta``
nb_seg : int
Number of segments in which each phase is discretized
order : int
Transcription order within each phase, must be odd
solver : str
NLP solver, must be supported by OpenMDAO
ph_name : str
Name of the phase within OpenMDAO
snopt_opts : dict or None, optional
SNOPT optional settings expressed as key-value pairs. Refer to the SNOPT User Guide for more details.
Default is None
rec_file : str or None, optional
Name of the file in which the computed solution is recorded or None. Default is None
u_bound : str or None, optional
Bounds on spacecraft radial velocity between ``lower`` and ``upper`` or None. Default is None
"""
def __init__(self, body, sc, alt, theta, alpha_bounds, tof, t_bounds, method, nb_seg, order, solver, ph_name,
snopt_opts=None, rec_file=None, u_bound=None):
"""Initializes TwoDimConstNLP class. """
ode_kwargs = {'GM': 1.0, 'T': sc.twr, 'w': sc.w / body.vc}
TwoDimNLP.__init__(self, body, sc, alt, alpha_bounds, method, nb_seg, order, solver, ODE2dConstThrust,
ode_kwargs, ph_name, snopt_opts=snopt_opts, rec_file=rec_file)
self.set_options(theta, tof, t_bounds, u_bound=u_bound)
self.setup()
[docs] def set_options(self, theta, tof, t_bounds, u_bound=None):
"""Set options on state and control variables, time and objective function.
Parameters
----------
theta : float
Guessed spawn angle [rad]
tof : float
Guessed time of flight [s]
t_bounds : tuple
Time of flight bounds expressed as fraction of TOF [-]
u_bound : str or None, optional
Bounds on spacecraft radial velocity between ``lower`` and ``upper`` or None. Default is None
"""
self.set_states_options(theta, u_bound=u_bound)
self.set_controls_options(throttle=False)
self.set_time_options(tof, t_bounds)
self.set_objective()
[docs]class TwoDimAscConstNLP(TwoDimConstNLP):
"""TwoDimAscConstNLP class transcribes a two-dimensional, continuous-time optimal control problem in trajectory
optimization into a Non Linear Programming Problem (NLP) using the OpenMDAO and dymos libraries.
The two-dimensional ascent trajectory is described in polar coordinates centered at the center of the
attracting body. The thrust delivered by the spacecraft engines is supposed to have a constant magnitude throughout
the whole phase.
Parameters
----------
body : Primary
Instance of `Primary` class representing the central attracting body
sc : Spacecraft
Instance of `Spacecraft` class representing the spacecraft
alt : float
Orbit altitude [m]
theta : float
Guessed spawn angle [rad]
alpha_bounds : tuple
Lower and upper bounds on thrust vector direction [rad]
tof : float
Guessed time of flight [s]
t_bounds : tuple
Time of flight bounds expressed as fraction of `tof` [-]
method : str
Transcription method used to discretize the continuous time trajectory into a finite set of nodes,
allowed ``gauss-lobatto``, ``radau-ps`` and ``runge-kutta``
nb_seg : int
Number of segments in which each phase is discretized
order : int
Transcription order within each phase, must be odd
solver : str
NLP solver, must be supported by OpenMDAO
ph_name : str
Name of the phase within OpenMDAO
snopt_opts : dict or None, optional
SNOPT optional settings expressed as key-value pairs. Refer to the SNOPT User Guide for more details.
Default is None
rec_file : str or None, optional
Name of the file in which the computed solution is recorded or None. Default is None
check_partials : bool, optional
Check the partial derivatives computed analytically against complex step method. Default is ``False``
u_bound : str or None, optional
Bounds on spacecraft radial velocity between ``lower`` and ``upper`` or None. Default is ``lower``
"""
def __init__(self, body, sc, alt, theta, alpha_bounds, tof, t_bounds, method, nb_seg, order, solver,
ph_name, snopt_opts=None, rec_file=None, check_partials=False, u_bound='lower'):
"""Initializes TwoDimAscConstNLP class. """
TwoDimConstNLP.__init__(self, body, sc, alt, theta, alpha_bounds, tof, t_bounds, method, nb_seg, order, solver,
ph_name, snopt_opts=snopt_opts, rec_file=rec_file, u_bound=u_bound)
bcs = np.array([[1.0, self.r_circ/self.body.R], [0.0, theta], [0.0, 0.0], [0.0, self.v_circ/self.body.vc],
[self.sc.m0, self.sc.m_dry], [0.0, 0.0]])
self.set_initial_guess_interpolation(bcs, check_partials=check_partials, throttle=False)
[docs]class TwoDimDescConstNLP(TwoDimConstNLP):
"""TwoDimDescConstNLP class transcribes a two-dimensional, continuous-time optimal control problem in trajectory
optimization into a Non Linear Programming Problem (NLP) using the OpenMDAO and dymos libraries.
The two-dimensional descent trajectory is described in polar coordinates centered at the center of the
attracting body. The thrust delivered by the spacecraft engines is supposed to have a constant magnitude throughout
the whole phase.
Parameters
----------
body : Primary
Instance of `Primary` class representing the central attracting body
sc : Spacecraft
Instance of `Spacecraft` class representing the spacecraft
alt : float
Orbit altitude [m]
theta : float
Guessed spawn angle [rad]
alpha_bounds : tuple
Lower and upper bounds on thrust vector direction [rad]
tof : float
Guessed time of flight [s]
t_bounds : tuple
Time of flight bounds expressed as fraction of `tof` [-]
method : str
Transcription method used to discretize the continuous time trajectory into a finite set of nodes,
allowed ``gauss-lobatto``, ``radau-ps`` and ``runge-kutta``
nb_seg : int
Number of segments in which each phase is discretized
order : int
Transcription order within each phase, must be odd
solver : str
NLP solver, must be supported by OpenMDAO
ph_name : str
Name of the phase within OpenMDAO
snopt_opts : dict or None, optional
SNOPT optional settings expressed as key-value pairs. Refer to the SNOPT User Guide for more details.
Default is None
rec_file : str or None, optional
Name of the file in which the computed solution is recorded or None. Default is None
check_partials : bool, optional
Check the partial derivatives computed analytically against complex step method. Default is ``False``
u_bound : str or None, optional
Bounds on spacecraft radial velocity between ``lower`` and ``upper`` or None. Default is ``upper``
Attributes
----------
vp : float
Velocity at the periapsis of the Hohmann transfer where the final powered descent is initiated [m/s]
"""
def __init__(self, body, sc, alt, vp, theta, alpha_bounds, tof, t_bounds, method, nb_seg, order, solver,
ph_name, snopt_opts=None, rec_file=None, check_partials=False, u_bound='upper'):
"""Initializes TwoDimDescConstNLP class. """
TwoDimConstNLP.__init__(self, body, sc, alt, theta, alpha_bounds, tof, t_bounds, method, nb_seg, order, solver,
ph_name, snopt_opts=snopt_opts, rec_file=rec_file, u_bound=u_bound)
self.vp = vp
bcs = np.array([[self.r_circ/self.body.R, 1.0], [0.0, theta], [0.0, 0.0], [self.vp/self.body.vc, 0.0],
[self.sc.m0, self.sc.m_dry], [1.5*np.pi, np.pi/2]])
self.set_initial_guess_interpolation(bcs, check_partials=check_partials, throttle=False)
[docs]class TwoDimVarNLP(TwoDimNLP):
"""TwoDimVarNLP class transcribes a two-dimensional, continuous-time optimal control problem in trajectory
optimization into a Non Linear Programming Problem (NLP) using the OpenMDAO and dymos libraries.
The two-dimensional trajectory is described in polar coordinates centered at the center of the attracting body.
The thrust delivered by the spacecraft engines varies in magnitude during the phase.
Parameters
----------
body : Primary
Instance of `Primary` class representing the central attracting body
sc : Spacecraft
Instance of `Spacecraft` class representing the spacecraft
alt : float
Orbit altitude [m]
alpha_bounds : tuple
Lower and upper bounds on thrust vector direction [rad]
t_bounds : tuple
Time of flight bounds expressed as fraction of `tof` [-]
method : str
Transcription method used to discretize the continuous time trajectory into a finite set of nodes,
allowed ``gauss-lobatto``, ``radau-ps`` and ``runge-kutta``
nb_seg : int
Number of segments in which each phase is discretized
order : int
Transcription order within each phase, must be odd
solver : str
NLP solver, must be supported by OpenMDAO
ph_name : str
Name of the phase within OpenMDAO
guess : TwoDimGuess
Initial guess for the NLP solution
snopt_opts : dict or None, optional
SNOPT optional settings expressed as key-value pairs. Refer to the SNOPT User Guide for more details.
Default is None
rec_file : str or None, optional
Name of the file in which the computed solution is recorded or None. Default is None
check_partials : bool, optional
Check the partial derivatives computed analytically against complex step method. Default is ``False``
u_bound : str or None, optional
Bounds on spacecraft radial velocity between ``lower`` and ``upper`` or None. Default is None
fix_final : bool, optional
``True`` if the final time is fixed, ``False`` otherwise. Default is ``False``
Attributes
----------
guess : TwoDimGuess
Initial guess for the NLP solution
"""
def __init__(self, body, sc, alt, alpha_bounds, t_bounds, method, nb_seg, order, solver, ph_name, guess,
snopt_opts=None, rec_file=None, check_partials=False, u_bound=None, fix_final=False):
"""Initializes TwoDimVarNLP class. """
ode_kwargs = {'GM': 1.0, 'w': sc.w / body.vc}
TwoDimNLP.__init__(self, body, sc, alt, alpha_bounds, method, nb_seg, order, solver, ODE2dVarThrust, ode_kwargs,
ph_name, snopt_opts=snopt_opts, rec_file=rec_file)
self.guess = guess
self.set_options(np.pi, t_bounds, u_bound=u_bound) # was pi/2
self.setup()
self.set_initial_guess(check_partials=check_partials, fix_final=fix_final)
[docs] def set_options(self, theta, t_bounds, u_bound=None):
"""Set options on state and control variables, time and objective function.
Parameters
----------
theta : float
Guessed spawn angle [rad]
t_bounds : tuple
Time of flight bounds expressed as fraction of `tof` [-]
u_bound : str or None, optional
Bounds on spacecraft radial velocity between ``lower`` and ``upper`` or None. Default is None
"""
self.set_states_options(theta, u_bound=u_bound)
self.set_controls_options(throttle=True)
self.set_time_options(self.guess.tf, t_bounds)
self.set_objective()
[docs]class TwoDimAscVarNLP(TwoDimVarNLP):
"""TwoDimAscVarNLP class transcribes a two-dimensional, continuous-time optimal control problem in trajectory
optimization into a Non Linear Programming Problem (NLP) using the OpenMDAO and dymos libraries.
The two-dimensional ascent trajectory is described in polar coordinates centered at the center of the attracting
body. The thrust delivered by the spacecraft engines varies in magnitude during the phase.
Parameters
----------
body : Primary
Instance of `Primary` class representing the central attracting body
sc : Spacecraft
Instance of `Spacecraft` class representing the spacecraft
alt : float
Orbit altitude [-]
alpha_bounds : tuple
Lower and upper bounds on thrust vector direction [rad]
t_bounds : tuple
Time of flight bounds expressed as fraction of `tof` [-]
method : str
Transcription method used to discretize the continuous time trajectory into a finite set of nodes,
allowed ``gauss-lobatto``, ``radau-ps`` and ``runge-kutta``
nb_seg : int
Number of segments in which each phase is discretized
order : int
Transcription order within each phase, must be odd
solver : str
NLP solver, must be supported by OpenMDAO
ph_name : str
Name of the phase within OpenMDAO
snopt_opts : dict or None, optional
SNOPT optional settings expressed as key-value pairs. Refer to the SNOPT User Guide for more details.
Default is None
rec_file : str or None, optional
Name of the file in which the computed solution is recorded or None. Default is None
check_partials : bool, optional
Check the partial derivatives computed analytically against complex step method. Default is ``False``
u_bound : str or None, optional
Bounds on spacecraft radial velocity between ``lower`` and ``upper`` or None. Default is ``lower``
fix_final : bool, optional
``True`` if the final time is fixed, ``False`` otherwise. Default is ``False``
"""
def __init__(self, body, sc, alt, alpha_bounds, t_bounds, method, nb_seg, order, solver, ph_name, snopt_opts=None,
rec_file=None, check_partials=False, u_bound='lower', fix_final=False):
"""Initializes TwoDimAscVarNLP class. """
TwoDimVarNLP.__init__(self, body, sc, alt, alpha_bounds, t_bounds, method, nb_seg, order, solver, ph_name,
TwoDimAscGuess(body.GM, body.R, alt, sc), snopt_opts=snopt_opts, rec_file=rec_file,
check_partials=check_partials, u_bound=u_bound, fix_final=fix_final)
[docs]class TwoDimDescVarNLP(TwoDimVarNLP):
"""TwoDimDescVarNLP class transcribes a two-dimensional, continuous-time optimal control problem in trajectory
optimization into a Non Linear Programming Problem (NLP) using the OpenMDAO and dymos libraries.
The two-dimensional descent trajectory is described in polar coordinates centered at the center of the attracting
body. The thrust delivered by the spacecraft engines varies in magnitude during the phase.
Parameters
----------
body : Primary
Instance of `Primary` class representing the central attracting body
sc : Spacecraft
Instance of `Spacecraft` class representing the spacecraft
alt : float
Orbit altitude [m]
alpha_bounds : tuple
Lower and upper bounds on thrust vector direction [rad]
t_bounds : tuple
Time of flight bounds expressed as fraction of `tof` [-]
method : str
Transcription method used to discretize the continuous time trajectory into a finite set of nodes,
allowed ``gauss-lobatto``, ``radau-ps`` and ``runge-kutta``
nb_seg : int
Number of segments in which each phase is discretized
order : int
Transcription order within each phase, must be odd
solver : str
NLP solver, must be supported by OpenMDAO
ph_name : str
Name of the phase within OpenMDAO
snopt_opts : dict or None, optional
SNOPT optional settings expressed as key-value pairs. Refer to the SNOPT User Guide for more details.
Default is None
rec_file : str or None, optional
Name of the file in which the computed solution is recorded or None. Default is None
check_partials : bool, optional
Check the partial derivatives computed analytically against complex step method. Default is ``False``
u_bound : str or None, optional
Bounds on spacecraft radial velocity between ``lower`` and ``upper`` or None. Default is ``upper``
fix_final : bool, optional
``True`` if the final time is fixed, ``False`` otherwise. Default is ``False``
"""
def __init__(self, body, sc, alt, alpha_bounds, t_bounds, method, nb_seg, order, solver, ph_name, snopt_opts=None,
rec_file=None, check_partials=False, u_bound='upper', fix_final=False):
"""Initializes TwoDimDescVarNLP class. """
TwoDimVarNLP.__init__(self, body, sc, alt, alpha_bounds, t_bounds, method, nb_seg, order, solver, ph_name,
TwoDimDescGuess(body.GM, body.R, alt, sc), snopt_opts=snopt_opts, rec_file=rec_file,
check_partials=check_partials, u_bound=u_bound, fix_final=fix_final)
[docs]class TwoDimVToffNLP(TwoDimVarNLP):
"""TwoDimVToffNLP class transcribes a two-dimensional, continuous-time optimal control problem in trajectory
optimization into a Non Linear Programming Problem (NLP) using the OpenMDAO and dymos libraries.
The two-dimensional ascent/descent trajectory is described in polar coordinates centered at the center of the
attracting body. The thrust delivered by the spacecraft engines varies in magnitude during the phase.
An appropriate path constraint is imposed on the spacecraft state to guarantee a vertical take-off or landing.
Parameters
----------
body : Primary
Instance of `Primary` class representing the central attracting body
sc : Spacecraft
Instance of `Spacecraft` class representing the spacecraft
alt : float
Orbit altitude [m]
alt_safe : float
Minimum altitude above the Moon surface to be maintained by the spacecraft far from the launch site [m]
slope : float
Slope of the path constraint on the spacecraft radius and angle close to the launch site.
Higher the value, steeper the ascent/descent [-]
alpha_bounds : tuple
Lower and upper bounds on thrust vector direction [rad]
t_bounds : tuple
Time of flight bounds expressed as fraction of `tof` [-]
method : str
Transcription method used to discretize the continuous time trajectory into a finite set of nodes,
allowed ``gauss-lobatto``, ``radau-ps`` and ``runge-kutta``
nb_seg : int
Number of segments in which each phase is discretized
order : int
Transcription order within each phase, must be odd
solver : str
NLP solver, must be supported by OpenMDAO
ph_name : str
Name of the phase within OpenMDAO
guess : TwoDimGuess
Initial guess for the NLP solution
snopt_opts : dict or None, optional
SNOPT optional settings expressed as key-value pairs. Refer to the SNOPT User Guide for more details.
Default is None
rec_file : str or None, optional
Name of the file in which the computed solution is recorded or None. Default is None
check_partials : bool, optional
Check the partial derivatives computed analytically against complex step method. Default is ``False``
u_bound : str or None, optional
Bounds on spacecraft radial velocity between ``lower`` and ``upper`` or None. Default is None
fix_final : bool, optional
``True`` if the final time is fixed, ``False`` otherwise. Default is ``False``
Attributes
----------
alt_safe : float
Minimum altitude above the Moon surface to be maintained by the spacecraft far from the launch site [m]
slope : float
Slope of the path constraint on the spacecraft radius and angle close to the launch site.
Higher the value, steeper the ascent [-]
guess : TwoDimGuess
Initial guess for the NLP solution
"""
def __init__(self, body, sc, alt, alt_safe, slope, alpha_bounds, t_bounds, method, nb_seg, order, solver, ph_name,
guess, snopt_opts=None, rec_file=None, check_partials=False, u_bound=None, fix_final=False):
"""Initializes TwoDimVToffNLP class. """
ode_kwargs = {'GM': 1.0, 'w': sc.w/body.vc, 'R': 1.0, 'alt_safe': alt_safe/body.R, 'slope': slope}
TwoDimNLP.__init__(self, body, sc, alt, alpha_bounds, method, nb_seg, order, solver, ODE2dVToff, ode_kwargs,
ph_name, snopt_opts=snopt_opts, rec_file=rec_file)
self.alt_safe = alt_safe
self.slope = slope
self.guess = guess
self.set_options(np.sign(slope)*np.pi, t_bounds, u_bound=u_bound)
self.setup()
self.set_initial_guess(check_partials=check_partials, fix_final=fix_final)
[docs] def set_options(self, theta, t_bounds, u_bound=None):
"""Set the states and controls options and the path constraint.
Parameters
----------
theta : float
Guessed spawn angle [rad]
t_bounds : tuple
Time of flight bounds expressed as fraction of `tof` [-]
u_bound : str or None, optional
Bounds on spacecraft radial velocity between ``lower`` and ``upper`` or None. Default is None
"""
self.phase.add_path_constraint('dist_safe', lower=0.0, ref=self.alt_safe/self.body.R)
self.phase.add_timeseries_output('r_safe')
TwoDimVarNLP.set_options(self, theta, t_bounds, u_bound=u_bound)
[docs]class TwoDimAscVToffNLP(TwoDimVToffNLP):
"""TwoDimAscVToffNLP class transcribes a two-dimensional, continuous-time optimal control problem in trajectory
optimization into a Non Linear Programming Problem (NLP) using the OpenMDAO and dymos libraries.
The two-dimensional ascent trajectory is described in polar coordinates centered at the center of the attracting
body. The thrust delivered by the spacecraft engines varies in magnitude during the phase.
An appropriate path constraint is imposed on the spacecraft state to guarantee a vertical take-off.
Parameters
----------
body : Primary
Instance of `Primary` class representing the central attracting body
sc : Spacecraft
Instance of `Spacecraft` class representing the spacecraft
alt : float
Orbit altitude [m]
alt_safe : float
Minimum altitude above the Moon surface to be maintained by the spacecraft far from the launch site [m]
slope : float
Slope of the path constraint on the spacecraft radius and angle close to the launch site.
Higher the value, steeper the ascent [-]
alpha_bounds : tuple
Lower and upper bounds on thrust vector direction [rad]
t_bounds : tuple
Time of flight bounds expressed as fraction of `tof` [-]
method : str
Transcription method used to discretize the continuous time trajectory into a finite set of nodes,
allowed ``gauss-lobatto``, ``radau-ps`` and ``runge-kutta``
nb_seg : int
Number of segments in which each phase is discretized
order : int
Transcription order within each phase, must be odd
solver : str
NLP solver, must be supported by OpenMDAO
ph_name : str
Name of the phase within OpenMDAO
snopt_opts : dict or None, optional
SNOPT optional settings expressed as key-value pairs. Refer to the SNOPT User Guide for more details.
Default is None
rec_file : str or None, optional
Name of the file in which the computed solution is recorded or None. Default is None
check_partials : bool, optional
Check the partial derivatives computed analytically against complex step method. Default is ``False``
u_bound : str or None, optional
Bounds on spacecraft radial velocity between ``lower`` and ``upper`` or None. Default is ``lower``
fix_final : bool, optional
``True`` if the final time is fixed, ``False`` otherwise. Default is ``False``
"""
def __init__(self, body, sc, alt, alt_safe, slope, alpha_bounds, t_bounds, method, nb_seg, order, solver, ph_name,
snopt_opts=None, rec_file=None, check_partials=False, u_bound='lower', fix_final=False):
"""Initializes TwoDimAscVToffNLP class. """
TwoDimVToffNLP.__init__(self, body, sc, alt, alt_safe, slope, alpha_bounds, t_bounds, method, nb_seg, order,
solver, ph_name, TwoDimAscGuess(body.GM, body.R, alt, sc), snopt_opts=snopt_opts,
rec_file=rec_file, check_partials=check_partials, u_bound=u_bound, fix_final=fix_final)
[docs]class TwoDimDescVLandNLP(TwoDimVToffNLP):
"""TwoDimAscVToffNLP class transcribes a two-dimensional, continuous-time optimal control problem in trajectory
optimization into a Non Linear Programming Problem (NLP) using the OpenMDAO and dymos libraries.
The two-dimensional descent trajectory is described in polar coordinates centered at the center of the attracting
body. The thrust delivered by the spacecraft engines varies in magnitude during the phase.
An appropriate path constraint is imposed on the spacecraft state to guarantee a vertical landing.
Parameters
----------
body : Primary
Instance of `Primary` class representing the central attracting body
sc : Spacecraft
Instance of `Spacecraft` class representing the spacecraft
alt : float
Orbit altitude [m]
alt_safe : float
Minimum altitude above the Moon surface to be maintained by the spacecraft far from the launch site [m]
slope : float
Slope of the path constraint on the spacecraft radius and angle close to the launch site.
Higher the value, steeper the descent [-]
alpha_bounds : tuple
Lower and upper bounds on thrust vector direction [rad]
t_bounds : tuple
Time of flight bounds expressed as fraction of `tof` [-]
method : str
Transcription method used to discretize the continuous time trajectory into a finite set of nodes,
allowed ``gauss-lobatto``, ``radau-ps`` and ``runge-kutta``
nb_seg : int
Number of segments in which each phase is discretized
order : int
Transcription order within each phase, must be odd
solver : str
NLP solver, must be supported by OpenMDAO
ph_name : str
Name of the phase within OpenMDAO
snopt_opts : dict or None, optional
SNOPT optional settings expressed as key-value pairs. Refer to the SNOPT User Guide for more details.
Default is None
rec_file : str or None, optional
Name of the file in which the computed solution is recorded or None. Default is None
check_partials : bool, optional
Check the partial derivatives computed analytically against complex step method. Default is ``False``
u_bound : str or None, optional
Bounds on spacecraft radial velocity between ``lower`` and ``upper`` or None. Default is ``upper``
fix_final : bool, optional
``True`` if the final time is fixed, ``False`` otherwise. Default is ``False``
"""
def __init__(self, body, sc, alt, alt_safe, slope, alpha_bounds, t_bounds, method, nb_seg, order, solver, ph_name,
snopt_opts=None, rec_file=None, check_partials=False, u_bound='upper', fix_final=True):
"""Initializes TwoDimDescVLandNLP class. """
TwoDimVToffNLP.__init__(self, body, sc, alt, alt_safe, slope, alpha_bounds, t_bounds, method, nb_seg, order,
solver, ph_name, TwoDimDescGuess(body.GM, body.R, alt, sc), snopt_opts=snopt_opts,
rec_file=rec_file, check_partials=check_partials, u_bound=u_bound, fix_final=fix_final)
[docs]class TwoDimDescTwoPhasesNLP(MultiPhaseNLP):
"""TwoDimDescTwoPhasesNLP transcribes a continuous-time optimal control problem for a two-dimensional descent
trajectory into a Non Linear Programming Problem (NLP) using the OpenMDAO and dymos libraries.
The two-phases transfer is constituted by an initial deorbit burn to lower the periapsis of the departure orbit,
an Hohmann transfer, a first powered phase from its periapsis to a predetermined altitude or time to go and a final
vertical descent at full thrust.
Parameters
----------
body : Primary
Instance of `Primary` class representing the central attracting body
sc : Spacecraft
Instance of `Spacecraft` class representing the spacecraft
alt : float
Periselene altitude at which the powered descent is initiated [m]
alt_switch : float
Altitude at which the vertical descent is triggered [m]
vp :float
Periselene velocity at which the powered descent is initiated [m/s]
theta : float
Guessed spawn angle [rad]
alpha_bounds : iterable
Lower and upper bounds on thrust vector direction [rad]
tof : iterable
Guessed time of flight for the two phases [s]
t_bounds : tuple
Time of flight bounds expressed as fraction of `tof` [-]
method : str
Transcription method used to discretize the continuous time trajectory into a finite set of nodes,
allowed ``gauss-lobatto``, ``radau-ps`` and ``runge-kutta``
nb_seg : int or tuple
Number of segments in which each phase is discretized
order : int or tuple
Transcription order within each phase, must be odd
solver : str
NLP solver, must be supported by OpenMDAO
ph_name : tuple
Name of the phase within OpenMDAO
snopt_opts : dict or None, optional
SNOPT optional settings expressed as key-value pairs. Refer to the SNOPT User Guide for more details.
Default is None
rec_file : str or None, optional
Name of the file in which the computed solution is recorded or None. Default is None
check_partials : bool, optional
Check the partial derivatives computed analytically against complex step method. Default is ``False``
fix : str, optional
``alt`` to trigger the vertical phase at fixed altitude equal to `alt_switch`, ``time`` to trigger the vertical
phase at fixed time to go equal to the second component of `tof`. Default is ``alt``
Attributes
----------
alt : float
Periselene altitude at which the powered descent is initiated [m]
alt_switch : float
Altitude at which the vertical descent is triggered [m]
rp : float
Periselene radius at which the powered descent is initiated [m]
r_switch : float
Radius at which the vertical descent is triggered [m]
vp :float
Periselene velocity at which the powered descent is initiated [m/s]
alpha_bounds : iterable
Lower and upper bounds on thrust vector direction [rad]
tof : iterable
Guessed time of flight for the two phases [s]
fix : str, optional
``alt`` to trigger the vertical phase at fixed altitude equal to `alt_switch`, ``time`` to trigger the vertical
phase at fixed time to go equal to the second component of `tof`. Default is ``alt``
"""
def __init__(self, body, sc, alt, alt_switch, vp, theta, alpha_bounds, tof, t_bounds, method, nb_seg, order, solver,
ph_name, snopt_opts=None, rec_file=None, check_partials=False, fix='alt'):
"""Initializes TwoDimDescTwoPhasesNLP class. """
ode_kwargs = {'GM': 1.0, 'T': sc.twr, 'w': sc.w/body.vc}
MultiPhaseNLP.__init__(self, body, sc, method, nb_seg, order, solver, (ODE2dConstThrust, ODE2dVertical),
(ode_kwargs, ode_kwargs), ph_name, snopt_opts=snopt_opts, rec_file=rec_file)
self.alt = alt
self.alt_switch = alt_switch
self.rp = alt + body.R
self.r_switch = alt_switch + body.R
self.vp = vp
self.alpha_bounds = np.asarray(alpha_bounds)
self.tof = np.asarray(tof)
if fix in ['alt', 'time']:
self.fix = fix
else:
raise ValueError('fix must be either alt or time')
self.set_options(theta, t_bounds)
self.trajectory.link_phases(ph_name, vars=['time', 'r', 'u', 'm'])
self.setup()
self.set_initial_guess(theta, check_partials=check_partials)
[docs] def set_options(self, theta, t_bounds):
"""Set the time, states and control options for both phases and add the NLP objective.
Parameters
----------
theta : float
Guessed spawn angle [rad]
t_bounds : iterable
Time of flight bounds expressed as fraction of `tof` [-]
"""
# time
if t_bounds is not None:
t_bounds = np.asarray(t_bounds)*np.reshape(self.tof, (2, 1))
self.phase[0].set_time_options(fix_initial=True, duration_ref=self.tof[0]/self.body.tc,
duration_bounds=t_bounds[0]/self.body.tc)
if self.fix == 'time':
self.phase[1].set_time_options(fix_initial=False, fix_duration=True,
initial_ref=t_bounds[0, 1]/self.body.tc,
initial_bounds=t_bounds[0]/self.body.tc,
duration_ref=self.tof[1]/self.body.tc,
duration_bounds=t_bounds[1]/self.body.tc)
else:
self.phase[1].set_time_options(fix_initial=False, fix_duration=False,
initial_ref=t_bounds[0, 1]/self.body.tc,
initial_bounds=t_bounds[0]/self.body.tc,
duration_ref=self.tof[1]/self.body.tc,
duration_bounds=t_bounds[1]/self.body.tc)
else:
self.phase[0].set_time_options(fix_initial=True, duration_ref=self.tof[0]/self.body.tc)
if self.fix == 'time':
self.phase[1].set_time_options(fix_initial=False, fix_duration=True,
initial_ref=self.tof[0]/self.body.tc,
duration_ref=self.tof[1]/self.body.tc)
else:
self.phase[1].set_time_options(fix_initial=False, fix_duration=False,
initial_ref=self.tof[0]/self.body.tc,
duration_ref=self.tof[1]/self.body.tc)
# first phase
if self.fix == 'alt':
self.phase[0].set_state_options('r', fix_initial=True, fix_final=True, lower=1.0, ref0=1.0,
ref=self.rp/self.body.R)
else:
self.phase[0].set_state_options('r', fix_initial=True, fix_final=False, lower=1.0, ref0=1.0,
ref=self.rp/self.body.R)
self.phase[0].set_state_options('theta', fix_initial=True, fix_final=False, lower=0.0, ref=theta)
self.phase[0].set_state_options('u', fix_initial=True, fix_final=False, ref=self.vp/self.body.vc)
self.phase[0].set_state_options('v', fix_initial=True, fix_final=True, lower=0.0, ref=self.vp/self.body.vc)
self.phase[0].set_state_options('m', fix_initial=True, fix_final=False, lower=self.sc.m_dry, upper=self.sc.m0,
ref0=self.sc.m_dry, ref=self.sc.m0)
self.phase[0].add_control('alpha', fix_initial=False, fix_final=False, continuity=True, rate_continuity=True,
rate2_continuity=False, lower=self.alpha_bounds[0], upper=self.alpha_bounds[1],
ref=self.alpha_bounds[1])
self.phase[0].add_design_parameter('w', opt=False, val=self.sc.w/self.body.vc)
self.phase[0].add_design_parameter('thrust', opt=False, val=self.sc.twr)
# second phase
if self.fix == 'alt':
self.phase[1].set_state_options('r', fix_initial=True, fix_final=True, lower=1.0, ref0=1.0,
ref=self.rp/self.body.R)
else:
self.phase[1].set_state_options('r', fix_initial=False, fix_final=True, lower=1.0, ref0=1.0,
ref=self.rp/self.body.R)
self.phase[1].set_state_options('u', fix_initial=False, fix_final=True, ref=self.vp/self.body.vc)
self.phase[1].set_state_options('m', fix_initial=False, fix_final=False, lower=self.sc.m_dry, upper=self.sc.m0,
ref0=self.sc.m_dry, ref=self.sc.m0)
self.phase[1].add_design_parameter('w', opt=False, val=self.sc.w/self.body.vc)
self.phase[1].add_design_parameter('thrust', opt=False, val=self.sc.twr)
# objective
self.phase[1].add_objective('m', loc='final', scaler=-1.0)
[docs] def set_initial_guess(self, theta, check_partials=False):
"""Set the initial guess for the NLP solution as simple linear interpolation of the Boundary Conditions.
Parameters
----------
theta : float
Guessed spawn angle [rad]
check_partials : bool, optional
Check the partial derivatives computed analytically against complex step method. Default is ``False``
"""
# time
self.p[self.phase_name[0] + '.t_initial'] = 0.0
self.p[self.phase_name[0] + '.t_duration'] = self.tof[0]/self.body.tc
self.p[self.phase_name[1] + '.t_initial'] = self.tof[0]/self.body.tc
self.p[self.phase_name[1] + '.t_duration'] = self.tof[1]/self.body.tc
self.p[self.phase_name[0] + '.states:r'] =\
self.phase[0].interpolate(ys=(self.rp/self.body.R, self.r_switch/self.body.R), nodes='state_input')
self.p[self.phase_name[0] + '.states:theta'] = self.phase[0].interpolate(ys=(0.0, theta), nodes='state_input')
self.p[self.phase_name[0] + '.states:u'] = self.phase[0].interpolate(ys=(0.0, -100/self.body.vc),
nodes='state_input')
self.p[self.phase_name[0] + '.states:v'] = self.phase[0].interpolate(ys=(self.vp/self.body.vc, 0.0),
nodes='state_input')
self.p[self.phase_name[0] + '.states:m'] = self.phase[0].interpolate(ys=(self.sc.m0, self.sc.m0/2),
nodes='state_input')
self.p[self.phase_name[0] + '.controls:alpha'] = self.phase[0].interpolate(ys=(np.pi, np.pi/2),
nodes='control_input')
self.p[self.phase_name[1] + '.states:r'] = self.phase[1].interpolate(ys=(self.r_switch/self.body.R, 1.0),
nodes='state_input')
self.p[self.phase_name[1] + '.states:u'] = self.phase[1].interpolate(ys=(-100/self.body.vc, 0.0),
nodes='state_input')
self.p[self.phase_name[1] + '.states:m'] = self.phase[1].interpolate(ys=(self.sc.m0/2, self.sc.m_dry),
nodes='state_input')
self.p.run_model()
if check_partials:
self.p.check_partials(method='cs', compact_print=True, show_only_incorrect=True)