def __init__(self, sc=spacecraft(1000, 0.3, 2500), mu=MU_SUN, alpha=1, bound=True): # check spacecraft if isinstance(sc, spacecraft): self.spacecraft = sc else: raise TypeError( "Spacecraft should be instance of pontryagin.spacecraft class.") # check gravitational parametre if not (isinstance(mu, float) or isinstance(mu, int)): raise TypeError( "Gravitational parametre, mu, must be supplied as either int or float.") elif not mu > 0: raise TypeError( "Gravitational parametre, mu, must be a positive number.") else: self.mu = float(mu) # check homotopy if not (isinstance(alpha, float) or isinstance(alpha, int)): raise TypeError( "Homotopy parametre, alpha, must be supplied as float or int.") elif not (alpha >= 0 and alpha <= 1): raise ValueError( "Homotopy parametre, alpha, must be between 0 and 1.") else: self.alpha = float(alpha) # check bound if not isinstance(bound, bool): raise TypeError( "Control bounding parametre, bound, supplied as boolean.") else: self.bound = bool(bound) # check control validity if (self.alpha == 1 and self.bound == False): raise ValueError( "Control can only be unbounded with quadratic control, i.e. bound == True if alpha == 1.") else: pass # spacecraft parametres self.c1 = self.spacecraft.thrust self.c2 = self.spacecraft.thrust / (self.spacecraft.isp * G0) # define nondimenional units self.L = AU self.V = EARTH_VELOCITY self.M = self.spacecraft.mass self.A = (self.V * self.V) / self.L self.F = self.M * self.A self.T = self.L / self.V self.Q = self.F / self.V # nondimensionalise parametres self.c1 /= self.F self.c2 /= self.Q self.mu /= MU_SUN
def __init__(self, sc=spacecraft(1000, 0.3, 2500), mu=MU_SUN, alpha=1, bound=True): # check spacecraft if isinstance(sc, spacecraft): self.spacecraft = sc else: raise TypeError( "Spacecraft should be instance of pontryagin.spacecraft class.") # check gravitational parameter if not (isinstance(mu, float) or isinstance(mu, int)): raise TypeError( "Gravitational parameter, mu, must be supplied as either int or float.") elif not mu > 0: raise TypeError( "Gravitational parameter, mu, must be a positive number.") else: self.mu = float(mu) # check homotopy if not (isinstance(alpha, float) or isinstance(alpha, int)): raise TypeError( "Homotopy parameter, alpha, must be supplied as float or int.") elif not (alpha >= 0 and alpha <= 1): raise ValueError( "Homotopy parameter, alpha, must be between 0 and 1.") else: self.alpha = float(alpha) # check bound if not isinstance(bound, bool): raise TypeError( "Control bounding parameter, bound, supplied as boolean.") else: self.bound = bool(bound) # check control validity if (self.alpha == 1 and self.bound == False): raise ValueError( "Control can only be unbounded with quadratic control, i.e. bound == True if alpha == 1.") else: pass # spacecraft parameter self.c1 = self.spacecraft.thrust self.c2 = self.spacecraft.thrust / (self.spacecraft.isp * G0) # define nondimensional units self.L = AU self.V = EARTH_VELOCITY self.M = self.spacecraft.mass self.A = (self.V * self.V) / self.L self.F = self.M * self.A self.T = self.L / self.V self.Q = self.F / self.V # nondimensionalise parameters self.c1 /= self.F self.c2 /= self.Q self.mu /= MU_SUN
def __init__(self, seq=[jpl_lp('earth'), jpl_lp('venus'), jpl_lp('mercury')], n_seg=[5, 20], t0=[3000, 4000], tof=[[100, 1000], [200, 2000]], vinf_dep=3, vinf_arr=2, mass=[1200., 2000.0], Tmax=0.5, Isp=3500.0, fb_rel_vel=6, multi_objective=False, high_fidelity=False): """ Args: - seq (```list of pykep.planet```): defines the encounter sequence for the trajectory (including the initial planet). - n_seg (```list``` of ```int```): the number of segments to be used for each leg. - t0 (```list``` of ```floats```): the launch window (in mjd2000). - tof (```list``` of ```2D-list```): minimum and maximum time of each leg (days). - vinf_dep (```float```): maximum launch hyperbolic velocity allowed (in km/sec). - vinf_arr (```float```): maximum arrival hyperbolic velocity allowed (in km/sec). - mass (```float```): spacecraft starting mass. (in kg). - Tmax (```float```): maximum thrust (in N). - Isp (```float```):: engine specific impulse (in s). . - fb_rel_vel (```float```): determines the bounds on the maximum allowed relative velocity at all fly-bys (in km/sec). - multi-objective (```bool```): when True defines the problem as a multi-objective problem, returning total DV and time of flight. - high_fidelity (```bool```):makes the trajectory computations slower, but actually dynamically feasible. """ # We define some data members (we use the double underscore to # indicate they are private) self._seq = seq self._n_seg = n_seg self._t0 = t0 self._tof = tof self._vinf_dep = vinf_dep * 1000 self._vinf_arr = vinf_arr * 1000 self._mass = mass self._Tmax = Tmax self._fb_rel_vel = fb_rel_vel self._multiobjective = multi_objective self._high_fidelity = high_fidelity self._n_legs = len(seq) - 1 self._sc = spacecraft(mass[1], Tmax, Isp) self._leg = leg() self._leg.set_mu(MU_SUN) self._leg.set_spacecraft(self._sc)
def __init__(self, t0=None, x0=None, l0=None, tf=None, xf=None, sc=spacecraft(1000, 0.3, 2500), mu=MU_SUN, freemass=True, freetime=True, alpha=1, bound=True): """Initialises an indirect optimal control transcription trajectory leg. Args: - t0 (``pykep.epoch``, ``None``): Departure time [mjd2000]. - x0 (``pykep.sims_flanagan.sc_state``, ``None``): Cartesian departure state [m, m, m, m/s, m/s, m/s, kg]. - l0 (``numpy.ndarray``, ``list``, ``tuple``, ``None``): Costate variables [ND, ND, ND, ND, ND, ND, ND]. - tf (``pykep.epoch``, ``None``): Arrival time [mjd2000]. - xf (``pykep.sims_flanagan.sc_state``, ``None``): Cartesian arrival state [m, m, m, m/s, m/s, m/s, kg]. - sc (``pykep.sims_flanagan.spacecraft``): Generic spacecraft with propulsive properties. - mu (``float``, ``int``): Gravitational parametre of primary body [m^3/s^2]. - freemass (``bool``): Activates final mass transversality condition. - freetime (``bool``): Activates final time transversality condition. Allows final time to vary. - alpha (``float``, ``int``): Homotopy parametre, governing the degree to which the theoretical control law is intended to reduce propellant expenditure or energy. Setting the parametre to 1 enforces a mass-optimal control law, with a characteristic bang-bang control profile (either full throttle or off). Setting the parametre to 0 enforces a pure quadratic control law. - bound (``bool``): Activates bounded control, in which the control throttle is bounded between 0 and 1, otherwise the control throttle is allowed to unbounded. Raises: - TypeError: If ``sc`` is not supplied as an instance of `pykep.sims_flanagan.spacecraft`. - TypeError: If ``mu`` is neither supplied as an instance of ``float`` nor ``int``. - ValueError: If ``mu`` is not supplied as a positive number. - TypeError: If either ``freemass``, ``freetime``, or ``bound`` is not supplied as an instance of ``bool``. - AttributeError: If equality constraint dimension cannot be determined form ``freemass`` and ``freetime``. - TypeError: If ``alpha`` is neither supplied as an instance of ``float`` nor ``int``. - ValueError: If ``alpha`` is not in between 0 and 1. - ValueError: If ``alpha == 1`` and ``bound == False``. Control cannot be unbounded with mass-optimal control. - TypeError: If either ``t0`` or ``tf`` is not supplied as an instance of ``pykep.epoch``. - ValueError: If departure time, ``t0.mjd2000`` does not occur before arrival time, `t`f.mjd2000``. - TypeError: If either ``x0`` or ``xf`` is not supplied as an instance of ``pykep.sims_flanagan.sc_state``. - TypeError: If costate, ``l0``, is neither supplied as an instance of ``numpy.ndarray``, ``list``, nor ``tuple``. - TypeError: If costate, ``l0``, is does not have 7 elements. Each element of ``l0`` corresponds to the respective elements of ``x0``. Examples: >>> l = pykep.pontryagin.leg() >>> l = pykep.pontryagin.leg(t0, x0, l0, tf, xf) .. note:: Typically spacecraft trajectories are optimised to reduce final mass, which theoretically results in a bang-bang control profile. However, a bang-bang control profile (either 0 or 1) is problematic to an optimiser due to its discontinous nature. The trajectory optimisation problem becomes easier when one first optimises with unbounded quadratic control (``alpha == 0 and bound == False``), stores the solution, then uses the solution to optimise with bounded quadratic control (``alpha == 0 and bound == True``). Then using the solution from bounded quadratic control, one can incrementally optimise for increasing homotopy parametres (e.g. ``alphas = numpy.linspace(0, 1, 200)``) until a mass-optimal control solution converges. .. note:: Boundary conditions ``t0``, ``x0``, ``l0``, ``tf``, and ``xf`` are optional in the constructor. If some, but not all, boundary conditions are supplied, they are not set and disregarded. If the boundary conditions are not set upon construction (``__init__``), they must be set with ``set(t0, x0, l0, tf, xf)``. .. note:: ``mismatch_constraints`` will not work unless the boundary conditions have been set either through ``__init__`` or ``set``. """ # check spacecraft if isinstance(sc, spacecraft): self.spacecraft = sc else: raise TypeError( "Spacecraft, sc, must be of pontryagin.spacecraft type.") # check gravitational parametre if not (isinstance(mu, float) or not isinstance(mu, int)): raise TypeError( "Gravitational parametre, mu, must be supplied as float or int." ) elif not mu > 0: raise ValueError("Gravitational parametre, mu, must be positive.") else: self.mu = float(mu) # dynamics self._dynamics = _dynamics(sc=self.spacecraft, mu=mu, alpha=alpha, bound=bound) # check freetime, freemass, and bound if not all( [isinstance(param, bool) for param in [freemass, freetime, bound]]): raise TypeError( "Freemass, freetime, bound parametres must supplied as booleans." ) else: self.freemass = bool(freemass) self.freetime = bool(freetime) self.bound = bool(bound) # equality constraint dimensionality if self.freemass and self.freetime: self.nec = 8 elif self.freemass and not self.freetime: self.nec = 7 elif not self.freemass and self.freetime: self.nec = 8 elif not self.freemass and not self.freetime: self.nec = 7 else: raise AttributeError( "Could not determine equality constraint dimensionality.") # check alpha if not (isinstance(alpha, float) or isinstance(alpha, int)): raise TypeError( "Homotopy parametre, alpha, must be supplied as float or int.") elif not (alpha >= 0 and alpha <= 1): raise ValueError( "Homotopy parametre, alpha, must be between 0 and 1.") elif (alpha == 1 and self.bound == False): raise ValueError( "If homotopy parametre, alpha, is 1, control must be bounded.") else: self.alpha = float(alpha) # if any of the necessary boundaries are not supplied if any(elem is None for elem in [t0, x0, l0, tf, xf]): pass # check validity if they're all supplied else: # check departure and arrival times if not all([isinstance(t, epoch) for t in [t0, tf]]): raise TypeError( "Departure and arrival times, t0 & tf, must be supplied as pykep.epoch." ) elif not t0.mjd2000 < tf.mjd2000: raise ValueError( "Departure time must occur before arrival time.") else: self.t0 = float(t0.mjd2000) self.tf = float(tf.mjd2000) # check departure and arrival states states = [x0, xf] if not all([isinstance(state, sc_state) for state in states]): raise TypeError( "x0 and xf must be instances of pykep.sims_flanagan.sc_state." ) else: self.x0 = np.asarray(x0.get(), np.float64) self.xf = np.asarray(xf.get(), np.float64) # check departure costate if not (isinstance(l0, list) or isinstance(l0, np.ndarray) or isinstance(l0, tuple)): raise TypeError( "Departure costate, l0, must be supplied as either a list, numpy.ndarray, or tuple." ) elif len(l0) != 7: raise TypeError("Costate vector, l0, must be 7-dimensional.") else: self.l0 = np.asarray(l0, np.float64) # integrator self._integrator = ode( lambda t, fs: self._dynamics._eom_fullstate(fs), #lambda t, fs: self._dynamics._eom_fullstate_jac(fs) )
def __init__(self, t0=None, x0=None, l0=None, tf=None, xf=None, sc=spacecraft(1000, 0.3, 2500), mu=MU_SUN, freemass=True, freetime=True, alpha=1, bound=True): """Initialises an indirect optimal control transcription trajectory leg. Args: - t0 (``pykep.epoch``, ``None``): Departure time [mjd2000]. - x0 (``pykep.sims_flanagan.sc_state``, ``None``): Cartesian departure state [m, m, m, m/s, m/s, m/s, kg]. - l0 (``numpy.ndarray``, ``list``, ``tuple``, ``None``): Costate variables [ND, ND, ND, ND, ND, ND, ND]. - tf (``pykep.epoch``, ``None``): Arrival time [mjd2000]. - xf (``pykep.sims_flanagan.sc_state``, ``None``): Cartesian arrival state [m, m, m, m/s, m/s, m/s, kg]. - sc (``pykep.sims_flanagan.spacecraft``): Generic spacecraft with propulsive properties. - mu (``float``, ``int``): Gravitational parametre of primary body [m^3/s^2]. - freemass (``bool``): Activates final mass transversality condition. - freetime (``bool``): Activates final time transversality condition. Allows final time to vary. - alpha (``float``, ``int``): Homotopy parametre, governing the degree to which the theoretical control law is intended to reduce propellant expenditure or energy. Setting the parametre to 1 enforces a mass-optimal control law, with a characteristic bang-bang control profile (either full throttle or off). Setting the parametre to 0 enforces a pure quadratic control law. - bound (``bool``): Activates bounded control, in which the control throttle is bounded between 0 and 1, otherwise the control throttle is allowed to unbounded. Raises: - TypeError: If ``sc`` is not supplied as an instance of `pykep.sims_flanagan.spacecraft`. - TypeError: If ``mu`` is neither supplied as an instance of ``float`` nor ``int``. - ValueError: If ``mu`` is not supplied as a positive number. - TypeError: If either ``freemass``, ``freetime``, or ``bound`` is not supplied as an instance of ``bool``. - AttributeError: If equality constraint dimension cannot be determined form ``freemass`` and ``freetime``. - TypeError: If ``alpha`` is neither supplied as an instance of ``float`` nor ``int``. - ValueError: If ``alpha`` is not in between 0 and 1. - ValueError: If ``alpha == 1`` and ``bound == False``. Control cannot be unbounded with mass-optimal control. - TypeError: If either ``t0`` or ``tf`` is not supplied as an instance of ``pykep.epoch``. - ValueError: If departure time, ``t0.mjd2000`` does not occur before arrival time, `t`f.mjd2000``. - TypeError: If either ``x0`` or ``xf`` is not supplied as an instance of ``pykep.sims_flanagan.sc_state``. - TypeError: If costate, ``l0``, is neither supplied as an instance of ``numpy.ndarray``, ``list``, nor ``tuple``. - TypeError: If costate, ``l0``, is does not have 7 elements. Each element of ``l0`` corresponds to the respective elements of ``x0``. Examples: >>> l = pykep.pontryagin.leg() >>> l = pykep.pontryagin.leg(t0, x0, l0, tf, xf) .. note:: Typically spacecraft trajectories are optimised to reduce final mass, which theoretically results in a bang-bang control profile. However, a bang-bang control profile (either 0 or 1) is problematic to an optimiser due to its discontinous nature. The trajectory optimisation problem becomes easier when one first optimises with unbounded quadratic control (``alpha == 0 and bound == False``), stores the solution, then uses the solution to optimise with bounded quadratic control (``alpha == 0 and bound == True``). Then using the solution from bounded quadratic control, one can incrementally optimise for increasing homotopy parametres (e.g. ``alphas = numpy.linspace(0, 1, 200)``) until a mass-optimal control solution converges. .. note:: Boundary conditions ``t0``, ``x0``, ``l0``, ``tf``, and ``xf`` are optional in the constructor. If some, but not all, boundary conditions are supplied, they are not set and disregarded. If the boundary conditions are not set upon construction (``__init__``), they must be set with ``set(t0, x0, l0, tf, xf)``. .. note:: ``mismatch_constraints`` will not work unless the boundary conditions have been set either through ``__init__`` or ``set``. """ # check spacecraft if isinstance(sc, spacecraft): self.spacecraft = sc else: raise TypeError( "Spacecraft, sc, must be of pontryagin.spacecraft type.") # check gravitational parametre if not (isinstance(mu, float) or not isinstance(mu, int)): raise TypeError( "Gravitational parametre, mu, must be supplied as float or int.") elif not mu > 0: raise ValueError("Gravitational parametre, mu, must be positive.") else: self.mu = float(mu) # dynamics self._dynamics = _dynamics( sc=self.spacecraft, mu=mu, alpha=alpha, bound=bound) # check freetime, freemass, and bound if not all([isinstance(param, bool) for param in [freemass, freetime, bound]]): raise TypeError( "Freemass, freetime, bound parametres must supplied as booleans.") else: self.freemass = bool(freemass) self.freetime = bool(freetime) self.bound = bool(bound) # equality constraint dimensionality if self.freemass and self.freetime: self.nec = 8 elif self.freemass and not self.freetime: self.nec = 7 elif not self.freemass and self.freetime: self.nec = 8 elif not self.freemass and not self.freetime: self.nec = 7 else: raise AttributeError( "Could not determine equality constraint dimensionality.") # check alpha if not (isinstance(alpha, float) or isinstance(alpha, int)): raise TypeError( "Homotopy parametre, alpha, must be supplied as float or int.") elif not (alpha >= 0 and alpha <= 1): raise ValueError( "Homotopy parametre, alpha, must be between 0 and 1.") elif (alpha == 1 and self.bound == False): raise ValueError( "If homotopy parametre, alpha, is 1, control must be bounded.") else: self.alpha = float(alpha) # if any of the necessary boundaries are not supplied if any(elem is None for elem in [t0, x0, l0, tf, xf]): pass # check validity if they're all supplied else: # check departure and arrival times if not all([isinstance(t, epoch) for t in [t0, tf]]): raise TypeError( "Departure and arrival times, t0 & tf, must be supplied as pykep.epoch.") elif not t0.mjd2000 < tf.mjd2000: raise ValueError( "Departure time must occur before arrival time.") else: self.t0 = float(t0.mjd2000) self.tf = float(tf.mjd2000) # check departure and arrival states states = [x0, xf] if not all([isinstance(state, sc_state) for state in states]): raise TypeError( "x0 and xf must be instances of pykep.sims_flanagan.sc_state.") else: self.x0 = np.asarray(x0.get(), np.float64) self.xf = np.asarray(xf.get(), np.float64) # check departure costate if not (isinstance(l0, list) or isinstance(l0, np.ndarray) or isinstance(l0, tuple)): raise TypeError( "Departure costate, l0, must be supplied as either a list, numpy.ndarray, or tuple.") elif len(l0) != 7: raise TypeError("Costate vector, l0, must be 7-dimensional.") else: self.l0 = np.asarray(l0, np.float64) # integrator self._integrator = ode( lambda t, fs: self._dynamics._eom_fullstate(fs), #lambda t, fs: self._dynamics._eom_fullstate_jac(fs) )