def plot_orbits(self, pop, ax=None): import matplotlib.pylab as plt from mpl_toolkits.mplot3d import Axes3D A1, A2 = self._ast1, self._ast2 if ax is None: fig = plt.figure() axis = fig.add_subplot(111, projection='3d') else: axis = ax plot_planet(A1, axes=axis, s=10, t0=epoch(self.lb[0])) plot_planet(A2, axes=axis, s=10, t0=epoch(self.ub[0])) for ind in pop: if ind.cur_f[0] == self._UNFEASIBLE: continue dep, arr = ind.cur_x rdep, vdep = A1.eph(epoch(dep)) rarr, varr = A2.eph(epoch(arr)) l = lambert_problem(rdep, rarr, (arr - dep) * DAY2SEC, A1.mu_central_body, False, 1) axis = plot_lambert(l, axes=axis, alpha=0.8, color='k') if ax is None: plt.show() return axis
def plot_orbits(self, pop, ax=None): import matplotlib.pylab as plt from mpl_toolkits.mplot3d import Axes3D A1, A2 = self._ast1, self._ast2 if ax is None: fig = plt.figure() axis = fig.add_subplot(111, projection='3d') else: axis = ax plot_planet(A1, ax=axis, s=10, t0=epoch(self.lb[0])) plot_planet(A2, ax=axis, s=10, t0=epoch(self.ub[0])) for ind in pop: if ind.cur_f[0] == self._UNFEASIBLE: continue dep, arr = ind.cur_x rdep, vdep = A1.eph(epoch(dep)) rarr, varr = A2.eph(epoch(arr)) l = lambert_problem(rdep, rarr, (arr - dep) * DAY2SEC, A1.mu_central_body, False, 1) axis = plot_lambert(l, ax=axis, alpha=0.8, color='k') if ax is None: plt.show() return axis
def pretty(self, x): # 1 - we 'decode' the chromosome recording the various deep space # manouvres timing (days) in the list T T = list([0] * (self.N_max - 1)) for i in range(len(T)): T[i] = log(x[2 + 4 * i]) total = sum(T) T = [x[1] * time / total for time in T] # 2 - We compute the starting and ending position r_start, v_start = self.start.eph(epoch(x[0])) if self.phase_free: r_target, v_target = self.target.eph(epoch(x[-1])) else: r_target, v_target = self.target.eph(epoch(x[0] + x[1])) # 3 - We loop across inner impulses rsc = r_start vsc = v_start for i, time in enumerate(T[:-1]): theta = 2 * pi * x[3 + 4 * i] phi = acos(2 * x[4 + 4 * i] - 1) - pi / 2 Vinfx = x[5 + 4 * i] * cos(phi) * cos(theta) Vinfy = x[5 + 4 * i] * cos(phi) * sin(theta) Vinfz = x[5 + 4 * i] * sin(phi) # We apply the (i+1)-th impulse vsc = [a + b for a, b in zip(vsc, [Vinfx, Vinfy, Vinfz])] rsc, vsc = propagate_lagrangian( rsc, vsc, T[i] * DAY2SEC, self.__common_mu) cw = (ic2par(rsc, vsc, self.start.mu_central_body)[2] > pi / 2) # We now compute the remaining two final impulses # Lambert arc to reach seq[1] dt = T[-1] * DAY2SEC l = lambert_problem(rsc, r_target, dt, self.__common_mu, cw, False) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] DV1 = norm([a - b for a, b in zip(v_beg_l, vsc)]) DV2 = norm([a - b for a, b in zip(v_end_l, v_target)]) DV_others = list(x[5::4]) DV_others.extend([DV1, DV2]) print("Total DV (m/s): ", sum(DV_others)) print("Dvs (m/s): ", DV_others) print("Tofs (days): ", T)
def fitness(self, x): # 1 - we 'decode' the chromosome into the various deep space # manouvres times (days) in the list T T = list([0] * (self.N_max - 1)) for i in range(len(T)): T[i] = log(x[2 + 4 * i]) total = sum(T) T = [x[1] * time / total for time in T] # 2 - We compute the starting and ending position r_start, v_start = self.start.eph(epoch(x[0])) if self.phase_free: r_target, v_target = self.target.eph(epoch(x[-1])) else: r_target, v_target = self.target.eph(epoch(x[0] + x[1])) # 3 - We loop across inner impulses rsc = r_start vsc = v_start for i, time in enumerate(T[:-1]): theta = 2 * pi * x[3 + 4 * i] phi = acos(2 * x[4 + 4 * i] - 1) - pi / 2 Vinfx = x[5 + 4 * i] * cos(phi) * cos(theta) Vinfy = x[5 + 4 * i] * cos(phi) * sin(theta) Vinfz = x[5 + 4 * i] * sin(phi) # We apply the (i+1)-th impulse vsc = [a + b for a, b in zip(vsc, [Vinfx, Vinfy, Vinfz])] rsc, vsc = propagate_lagrangian( rsc, vsc, T[i] * DAY2SEC, self.__common_mu) cw = (ic2par(rsc, vsc, self.start.mu_central_body)[2] > pi / 2) # We now compute the remaining two final impulses # Lambert arc to reach seq[1] dt = T[-1] * DAY2SEC l = lambert_problem(rsc, r_target, dt, self.__common_mu, cw, False) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] DV1 = norm([a - b for a, b in zip(v_beg_l, vsc)]) DV2 = norm([a - b for a, b in zip(v_end_l, v_target)]) DV_others = sum(x[5::4]) if self.obj_dim == 1: return (DV1 + DV2 + DV_others,) else: return (DV1 + DV2 + DV_others, x[1])
def plot(self, x, axes=None, units=AU, N=60): """plot(self, x, axes=None, units=pk.AU, N=60) Plots the spacecraft trajectory. Args: - x (``tuple``, ``list``, ``numpy.ndarray``): Decision chromosome. - axes (``matplotlib.axes._subplots.Axes3DSubplot``): 3D axes to use for the plot - units (``float``, ``int``): Length unit by which to normalise data. - N (``float``): Number of points to plot per leg """ import matplotlib as mpl import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from pykep.orbit_plots import plot_planet, plot_lambert # Creating the axes if necessary if axes is None: mpl.rcParams['legend.fontsize'] = 10 fig = plt.figure() axes = fig.gca(projection='3d') T = self._decode_tofs(x) ep = np.insert(T, 0, x[0]) # [t0, T1, T2 ...] ep = np.cumsum(ep) # [t0, t1, t2, ...] _, _, _, l, _ = self._compute_dvs(x) for pl, e in zip(self.seq, ep): plot_planet(pl, epoch(e), units=units, legend=True, color=(0.7, 0.7, 1), axes=axes) for lamb in l: plot_lambert(lamb, N=N, sol=0, units=units, color='k', legend=False, axes=axes, alpha=0.8) return axes
def fitness(self, x): # 1 - we 'decode' the chromosome recording the various times of flight # (days) in the list T and the cartesian components of vinf T, Vinfx, Vinfy, Vinfz = self._decode_times_and_vinf(x) # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.__n_legs + 1)) r_P = list([None] * (self.__n_legs + 1)) v_P = list([None] * (self.__n_legs + 1)) DV = list([0.0] * (self.__n_legs + 1)) for i, planet in enumerate(self.seq): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = self.seq[i].eph(t_P[i]) # 3 - We start with the first leg v0 = [a + b for a, b in zip(v_P[0], [Vinfx, Vinfy, Vinfz])] r, v = propagate_lagrangian(r_P[0], v0, x[5] * T[0] * DAY2SEC, self.common_mu) # Lambert arc to reach seq[1] dt = (1 - x[5]) * T[0] * DAY2SEC l = lambert_problem(r, r_P[1], dt, self.common_mu, False, False) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # First DSM occuring at time nu1*T1 DV[0] = norm([a - b for a, b in zip(v_beg_l, v)]) # 4 - And we proceed with each successive leg for i in range(1, self.__n_legs): # Fly-by v_out = fb_prop(v_end_l, v_P[i], x[8 + (i - 1) * 4] * self.seq[i].radius, x[7 + (i - 1) * 4], self.seq[i].mu_self) # s/c propagation before the DSM r, v = propagate_lagrangian(r_P[i], v_out, x[9 + (i - 1) * 4] * T[i] * DAY2SEC, self.common_mu) # Lambert arc to reach Earth during (1-nu2)*T2 (second segment) dt = (1 - x[9 + (i - 1) * 4]) * T[i] * DAY2SEC l = lambert_problem(r, r_P[i + 1], dt, self.common_mu, False, False) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # DSM occuring at time nu2*T2 DV[i] = norm([a - b for a, b in zip(v_beg_l, v)]) # Last Delta-v if self.__add_vinf_arr: DV[-1] = norm([a - b for a, b in zip(v_end_l, v_P[-1])]) if self.__add_vinf_dep: DV[0] += x[3] if self._obj_dim == 1: return (sum(DV), ) else: return (sum(DV), sum(T))
def fitness(self, x): # 1 - we 'decode' the chromosome recording the various times of flight # (days) in the list T and the cartesian components of vinf T, Vinfx, Vinfy, Vinfz = self._decode_times_and_vinf(x) # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.__n_legs + 1)) r_P = list([None] * (self.__n_legs + 1)) v_P = list([None] * (self.__n_legs + 1)) DV = list([0.0] * (self.__n_legs + 1)) for i, planet in enumerate(self.seq): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = self.seq[i].eph(t_P[i]) # 3 - We start with the first leg v0 = [a + b for a, b in zip(v_P[0], [Vinfx, Vinfy, Vinfz])] r, v = propagate_lagrangian( r_P[0], v0, x[5] * T[0] * DAY2SEC, self.common_mu) # Lambert arc to reach seq[1] dt = (1 - x[5]) * T[0] * DAY2SEC l = lambert_problem(r, r_P[1], dt, self.common_mu, False, False) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # First DSM occuring at time nu1*T1 DV[0] = norm([a - b for a, b in zip(v_beg_l, v)]) # 4 - And we proceed with each successive leg for i in range(1, self.__n_legs): # Fly-by v_out = fb_prop(v_end_l, v_P[i], x[ 8 + (i - 1) * 4] * self.seq[i].radius, x[7 + (i - 1) * 4], self.seq[i].mu_self) # s/c propagation before the DSM r, v = propagate_lagrangian( r_P[i], v_out, x[9 + (i - 1) * 4] * T[i] * DAY2SEC, self.common_mu) # Lambert arc to reach Earth during (1-nu2)*T2 (second segment) dt = (1 - x[9 + (i - 1) * 4]) * T[i] * DAY2SEC l = lambert_problem(r, r_P[i + 1], dt, self.common_mu, False, False) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # DSM occuring at time nu2*T2 DV[i] = norm([a - b for a, b in zip(v_beg_l, v)]) # Last Delta-v if self.__add_vinf_arr: DV[-1] = norm([a - b for a, b in zip(v_end_l, v_P[-1])]) if self.__add_vinf_dep: DV[0] += x[3] if self._obj_dim == 1: return (sum(DV),) else: return (sum(DV), sum(T))
def get_orbit(planet, day): planet = pk.planet.jpl_lp(planet) epoch_ = epoch(day) T = planet.compute_period(epoch_) * SEC2DAY N = int(round(T)) when = np.linspace(0, T, N) x = np.array([0.0] * N) y = np.array([0.0] * N) z = np.array([0.0] * N) for i, day in enumerate(when): r, v = planet.eph(epoch(epoch_.mjd2000 + day)) x[i] = r[0] / AU y[i] = r[1] / AU z[i] = r[2] / AU return x, y, z
def __init__(self, seq=[jpl_lp('earth'), jpl_lp('venus'), jpl_lp('earth')], t0=[epoch(0), epoch(1000)], tof=[1.0, 5.0], vinf=[0.5, 2.5], add_vinf_dep=False, add_vinf_arr=True, multi_objective=False): """ pykep.trajopt.mga_1dsm(seq = [jpl_lp('earth'), jpl_lp('venus'), jpl_lp('earth')], t0 = [epoch(0),epoch(1000)], tof = [1.0,5.0], vinf = [0.5, 2.5], multi_objective = False, add_vinf_dep = False, add_vinf_arr=True) - seq: list of pykep planets defining the encounter sequence (including the starting launch) - t0: list of two epochs defining the launch window - tof: list of two floats defining the minimum and maximum allowed mission lenght (days) - vinf: list of two floats defining the minimum and maximum allowed initial hyperbolic velocity (at launch), in km/sec - multi_objective: when True constructs a multiobjective problem (dv, T) - add_vinf_dep: when True the computed Dv includes the initial hyperbolic velocity (at launch) - add_vinf_arr: when True the computed Dv includes the final hyperbolic velocity (at the last planet) """ # Sanity checks ...... all planets need to have the same # mu_central_body if ([r.mu_central_body for r in seq].count(seq[0].mu_central_body) != len(seq)): raise ValueError( 'All planets in the sequence need to have exactly the same mu_central_body' ) self.__add_vinf_dep = add_vinf_dep self.__add_vinf_arr = add_vinf_arr self.__n_legs = len(seq) - 1 self._t0 = t0 self._tof = tof self._vinf = vinf self._obj_dim = multi_objective + 1 # We then define all planets in the sequence and the common central # body gravity as data members self.seq = seq self.common_mu = seq[0].mu_central_body
def __init__(self, seq=[jpl_lp('earth'), jpl_lp('venus'), jpl_lp('earth')], t0=[epoch(0), epoch(1000)], tof=[1.0, 5.0], vinf=[0.5, 2.5], add_vinf_dep=False, add_vinf_arr=True, multi_objective=False): """ pykep.trajopt.mga_1dsm(seq = [jpl_lp('earth'), jpl_lp('venus'), jpl_lp('earth')], t0 = [epoch(0),epoch(1000)], tof = [1.0,5.0], vinf = [0.5, 2.5], multi_objective = False, add_vinf_dep = False, add_vinf_arr=True) - seq: list of pykep planets defining the encounter sequence (including the starting launch) - t0: list of two epochs defining the launch window - tof: list of two floats defining the minimum and maximum allowed mission lenght (days) - vinf: list of two floats defining the minimum and maximum allowed initial hyperbolic velocity (at launch), in km/sec - multi_objective: when True constructs a multiobjective problem (dv, T) - add_vinf_dep: when True the computed Dv includes the initial hyperbolic velocity (at launch) - add_vinf_arr: when True the computed Dv includes the final hyperbolic velocity (at the last planet) """ # Sanity checks ...... all planets need to have the same # mu_central_body if ([r.mu_central_body for r in seq].count(seq[0].mu_central_body) != len(seq)): raise ValueError( 'All planets in the sequence need to have exactly the same mu_central_body') self.__add_vinf_dep = add_vinf_dep self.__add_vinf_arr = add_vinf_arr self.__n_legs = len(seq) - 1 self._t0 = t0 self._tof = tof self._vinf = vinf self._obj_dim = multi_objective + 1 # We then define all planets in the sequence and the common central # body gravity as data members self.seq = seq self.common_mu = seq[0].mu_central_body
def __init__(self, planet, rvt_in, rvt_pl, resonances=[[1, 1], [5, 4], [4, 3], [3, 2], [5, 3]]): """ Args: - planet (``planet``): resonance planet. - rvt_in: (``rvt``): incoming orbit. - rvt_pl: (``rvt``): planet orbit. - resonances (``list`` of ``int``): resonance options. """ assert rvt_in._t == rvt_pl._t # timing must be consistent self._planet = planet self._rvt_in = rvt_in self._rvt_pl = rvt_pl self._time = rvt_in._t self._resonances = resonances self._period = planet.compute_period(epoch(self._time * SEC2DAY)) self._mu = planet.mu_self self._timing_error = -1 self._rvt_out = None self._resonance = None
def __init__(self, start=jpl_lp('earth'), target=jpl_lp('venus'), N_max=3, tof=[20., 400.], vinf=[0., 4.], phase_free=True, multi_objective=False, t0=None ): """ prob = pykep.trajopt.pl2pl_N_impulses(start=jpl_lp('earth'), target=jpl_lp('venus'), N_max=3, tof=[20., 400.], vinf=[0., 4.], phase_free=True, multi_objective=False, t0=None) Args: - start (``pykep.planet``): the starting planet - target (``pykep.planet``): the target planet - N_max (``int``): maximum number of impulses - tof (``list``): the box bounds [lower,upper] for the time of flight (days) - vinf (``list``): the box bounds [lower,upper] for each DV magnitude (km/sec) - phase_free (``bool``): when True, no randezvous condition are enforced and start and arrival anomalies will be free - multi_objective (``bool``): when True, a multi-objective problem is constructed with DV and time of flight as objectives - t0 (``list``): the box bounds on the launch window containing two pykep.epoch. This is not needed if phase_free is True. """ # Sanity checks # 1) all planets need to have the same mu_central_body if (start.mu_central_body != target.mu_central_body): raise ValueError( 'Starting and ending pykep.planet must have the same mu_central_body') # 2) Number of impulses must be at least 2 if N_max < 2: raise ValueError('Number of impulses N is less than 2') # 3) If phase_free is True, t0 does not make sense if (t0 is None and not phase_free): t0 = [epoch(0), epoch(1000)] if (t0 is not None and phase_free): raise ValueError('When phase_free is True no t0 can be specified') if (type(t0[0]) != type(epoch(0))): t0[0] = epoch(t0[0]) if (type(t0[1]) != type(epoch(0))): t0[1] = epoch(t0[1]) self.obj_dim = multi_objective + 1 # We then define all class data members self.start = start self.target = target self.N_max = N_max self.phase_free = phase_free self.multi_objective = multi_objective self.vinf = [s * 1000 for s in vinf] self.__common_mu = start.mu_central_body # And we compute the bounds if phase_free: self._lb = [0, tof[0]] + [1e-3, 0.0, 0.0, vinf[0] * 1000] * (N_max - 2) + [1e-3] + [0] self._ub = [2 * start.compute_period(epoch(0)) * SEC2DAY, tof[1]] + [1.0-1e-3, 1.0, 1.0, vinf[ 1] * 1000] * (N_max - 2) + [1.0-1e-3] + [2 * target.compute_period(epoch(0)) * SEC2DAY] else: self._lb = [t0[0].mjd2000, tof[0]] + \ [1e-3, 0.0, 0.0, vinf[0] * 1000] * (N_max - 2) + [1e-3] self._ub = [t0[1].mjd2000, tof[1]] + \ [1.0-1e-3, 1.0, 1.0, vinf[1] * 1000] * (N_max - 2) + [1.0-1e-3]
def ic_from_mga_1dsm(self, x): """ x_lt = prob.ic_from_mga_1dsm(x_mga) - x_mga: compatible trajectory as encoded by an mga_1dsm problem Returns an initial guess for the low-thrust trajectory, converting the mga_1dsm solution x_dsm. The user is responsible that x_mga makes sense (i.e. it is a viable mga_1dsm representation). The conversion is done by importing in the low-thrust encoding a) the launch date b) all the legs durations, c) the in and out relative velocities at each planet. All throttles are put to zero. Example:: x_lt= prob.ic_from_mga_1dsm(x_mga) """ from math import pi, cos, sin, acos from scipy.linalg import norm from pykep import propagate_lagrangian, lambert_problem, DAY2SEC, fb_prop retval = list([0.0] * self.dimension) # 1 - we 'decode' the chromosome recording the various times of flight # (days) in the list T T = list([0] * (self.__n_legs)) for i in range(len(T)): T[i] = log(x[2 + 4 * i]) total = sum(T) T = [x[1] * time / total for time in T] retval[0] = x[0] for i in range(self.__n_legs): retval[1 + 8 * i] = T[i] retval[2 + 8 * i] = self.__sc.mass # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.__n_legs + 1)) r_P = list([None] * (self.__n_legs + 1)) v_P = list([None] * (self.__n_legs + 1)) DV = list([None] * (self.__n_legs + 1)) for i, planet in enumerate(self.seq): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = self.seq[i].eph(t_P[i]) # 3 - We start with the first leg theta = 2 * pi * x[1] phi = acos(2 * x[2] - 1) - pi / 2 Vinfx = x[3] * cos(phi) * cos(theta) Vinfy = x[3] * cos(phi) * sin(theta) Vinfz = x[3] * sin(phi) retval[3:6] = [Vinfx, Vinfy, Vinfz] v0 = [a + b for a, b in zip(v_P[0], [Vinfx, Vinfy, Vinfz])] r, v = propagate_lagrangian(r_P[0], v0, x[4] * T[0] * DAY2SEC, MU_SUN) # Lambert arc to reach seq[1] dt = (1 - x[4]) * T[0] * DAY2SEC l = lambert_problem(r, r_P[1], dt, MU_SUN) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] retval[6:9] = [a - b for a, b in zip(v_end_l, v_P[1])] # 4 - And we proceed with each successive leg for i in range(1, self.__n_legs): # Fly-by v_out = fb_prop(v_end_l, v_P[i], x[ 7 + (i - 1) * 4] * self.seq[i].radius, x[6 + (i - 1) * 4], self.seq[i].mu_self) retval[3 + i * 8:6 + i * 8] = [a - b for a, b in zip(v_out, v_P[i])] # s/c propagation before the DSM r, v = propagate_lagrangian( r_P[i], v_out, x[8 + (i - 1) * 4] * T[i] * DAY2SEC, MU_SUN) # Lambert arc to reach Earth during (1-nu2)*T2 (second segment) dt = (1 - x[8 + (i - 1) * 4]) * T[i] * DAY2SEC l = lambert_problem(r, r_P[i + 1], dt, MU_SUN) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # DSM occuring at time nu2*T2 DV[i] = norm([a - b for a, b in zip(v_beg_l, v)]) retval[6 + i * 8:9 + i * 8] = [a - b for a, b in zip(v_end_l, v_P[i + 1])] return retval
def plot(self, x, axes=None): """ ax = prob.plot(x, ax=None) - x: encoded trajectory - ax: matplotlib axis where to plot. If None figure and axis will be created - [out] ax: matplotlib axis where to plot Plots the trajectory represented by a decision vector x on the 3d axis ax Example:: ax = prob.plot(x) """ import matplotlib as mpl from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from pykep.orbit_plots import plot_sf_leg, plot_planet # Creating the axis if necessary if axes is None: mpl.rcParams['legend.fontsize'] = 10 fig = plt.figure() ax = fig.gca(projection='3d') else: ax = axes # Plotting the Sun ........ ax.scatter([0], [0], [0], color=['y']) # We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self._n_legs + 1)) r_P = list([None] * (self._n_legs + 1)) v_P = list([None] * (self._n_legs + 1)) for i in range(len(self._seq)): t_P[i] = epoch(x[0] + sum(x[1:i * 8:8])) r_P[i], v_P[i] = self._seq[i].eph(t_P[i]) plot_planet(self._seq[i], t0=t_P[i], units=AU, legend=False, color=(0.7, 0.7, 0.7), s=30, axes=ax) # We assemble the constraints. # 1 - Mismatch Constraints for i in range(self._n_legs): # Departure velocity of the spacecraft in the heliocentric frame v0 = [a + b for a, b in zip(v_P[i], x[3 + 8 * i:6 + 8 * i])] if i == 0: m0 = self._mass[1] else: m0 = x[2 + 8 * (i - 1)] x0 = sc_state(r_P[i], v0, m0) vf = [a + b for a, b in zip(v_P[i + 1], x[6 + 8 * i:9 + 8 * i])] xf = sc_state(r_P[i + 1], vf, x[2 + 8 * i]) idx_start = 1 + 8 * self._n_legs + sum(self._n_seg[:i]) * 3 idx_end = 1 + 8 * self._n_legs + sum(self._n_seg[:i + 1]) * 3 self._leg.set(t_P[i], x0, x[idx_start:idx_end], t_P[i + 1], xf) plot_sf_leg(self._leg, units=AU, N=10, axes=ax, legend=False) return axes
def plot(self, x, axes=None): """ ax = prob.plot_trajectory(x, axes=None) - x: encoded trajectory - axes: matplotlib axis where to plot. If None figure and axis will be created - [out] ax: matplotlib axis where to plot Plots the trajectory represented by a decision vector x on the 3d axis ax Example:: ax = prob.plot(x) """ import matplotlib as mpl from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from pykep.orbit_plots import plot_planet, plot_lambert, plot_kepler if axes is None: mpl.rcParams['legend.fontsize'] = 10 fig = plt.figure() axes = fig.gca(projection='3d') axes.scatter(0, 0, 0, color='y') # 1 - we 'decode' the chromosome recording the various deep space # manouvres timing (days) in the list T T = list([0] * (self.N_max - 1)) for i in range(len(T)): T[i] = log(x[2 + 4 * i]) total = sum(T) T = [x[1] * time / total for time in T] # 2 - We compute the starting and ending position r_start, v_start = self.start.eph(epoch(x[0])) if self.phase_free: r_target, v_target = self.target.eph(epoch(x[-1])) else: r_target, v_target = self.target.eph(epoch(x[0] + x[1])) plot_planet(self.start, t0=epoch(x[0]), color=( 0.8, 0.6, 0.8), legend=True, units=AU, ax=axes, s=0) plot_planet(self.target, t0=epoch( x[0] + x[1]), color=(0.8, 0.6, 0.8), legend=True, units=AU, ax=axes, s=0) DV_list = x[5::4] maxDV = max(DV_list) DV_list = [s / maxDV * 30 for s in DV_list] colors = ['b', 'r'] * (len(DV_list) + 1) # 3 - We loop across inner impulses rsc = r_start vsc = v_start for i, time in enumerate(T[:-1]): theta = 2 * pi * x[3 + 4 * i] phi = acos(2 * x[4 + 4 * i] - 1) - pi / 2 Vinfx = x[5 + 4 * i] * cos(phi) * cos(theta) Vinfy = x[5 + 4 * i] * cos(phi) * sin(theta) Vinfz = x[5 + 4 * i] * sin(phi) # We apply the (i+1)-th impulse vsc = [a + b for a, b in zip(vsc, [Vinfx, Vinfy, Vinfz])] axes.scatter(rsc[0] / AU, rsc[1] / AU, rsc[2] / AU, color='k', s=DV_list[i]) plot_kepler(rsc, vsc, T[i] * DAY2SEC, self.__common_mu, N=200, color=colors[i], legend=False, units=AU, ax=axes) rsc, vsc = propagate_lagrangian( rsc, vsc, T[i] * DAY2SEC, self.__common_mu) cw = (ic2par(rsc, vsc, self.start.mu_central_body)[2] > pi / 2) # We now compute the remaining two final impulses # Lambert arc to reach seq[1] dt = T[-1] * DAY2SEC l = lambert_problem(rsc, r_target, dt, self.__common_mu, cw, False) plot_lambert(l, sol=0, color=colors[ i + 1], legend=False, units=AU, ax=axes, N=200) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] DV1 = norm([a - b for a, b in zip(v_beg_l, vsc)]) DV2 = norm([a - b for a, b in zip(v_end_l, v_target)]) axes.scatter(rsc[0] / AU, rsc[1] / AU, rsc[2] / AU, color='k', s=min(DV1 / maxDV * 30, 40)) axes.scatter(r_target[0] / AU, r_target[1] / AU, r_target[2] / AU, color='k', s=min(DV2 / maxDV * 30, 40)) return axes
def _fitness_impl(self, decoded_x, logging=False, plotting=False, ax=None): """ Computation of the objective function. """ saturn_distance_violated = 0 # decode x t0, u, v, dep_vinf, etas, T, betas, rps = decoded_x # convert incoming velocity vector theta, phi = 2.0 * pi * u, acos(2.0 * v - 1.0) - pi / 2.0 Vinfx = dep_vinf * cos(phi) * cos(theta) Vinfy = dep_vinf * cos(phi) * sin(theta) Vinfz = dep_vinf * sin(phi) # epochs and ephemerides of the planetary encounters t_P = list([None] * (self._n_legs + 1)) r_P = list([None] * (self._n_legs + 1)) v_P = list([None] * (self._n_legs + 1)) lamberts = list([None] * (self._n_legs)) v_outs = list([None] * (self._n_legs)) DV = list([0.0] * (self._n_legs + 1)) for i, planet in enumerate(self.seq): t_P[i] = epoch(t0 + sum(T[0:i])) r_P[i], v_P[i] = self.seq[i].eph(t_P[i]) # first leg v_outs[0] = [Vinfx, Vinfy, Vinfz] # bug fixed # check first leg up to DSM saturn_distance_violated += self.check_distance( r_P[0], v_outs[0], t0, etas[0] * T[0]) r, v = propagate_lagrangian(r_P[0], v_outs[0], etas[0] * T[0] * DAY2SEC, self.common_mu) # Lambert arc to reach seq[1] dt = (1.0 - etas[0]) * T[0] * DAY2SEC lamberts[0] = lambert_problem(r, r_P[1], dt, self.common_mu, self.cw, 0) v_end_l = lamberts[0].get_v2()[0] v_beg_l = lamberts[0].get_v1()[0] # First DSM occuring at time eta0*T0 DV[0] = norm([a - b for a, b in zip(v_beg_l, v)]) # checking first leg after DSM saturn_distance_violated += self.check_distance( r, v_beg_l, etas[0] * T[0], T[0]) # successive legs for i in range(1, self._n_legs): # Fly-by v_outs[i] = fb_prop(v_end_l, v_P[i], rps[i - 1] * self.seq[i].radius, betas[i - 1], self.seq[i].mu_self) # checking next leg up to DSM saturn_distance_violated += self.check_distance( r_P[i], v_outs[i], T[i - 1], etas[i] * T[i]) # s/c propagation before the DSM r, v = propagate_lagrangian(r_P[i], v_outs[i], etas[i] * T[i] * DAY2SEC, self.common_mu) # Lambert arc to reach next body dt = (1 - etas[i]) * T[i] * DAY2SEC lamberts[i] = lambert_problem(r, r_P[i + 1], dt, self.common_mu, self.cw, 0) v_end_l = lamberts[i].get_v2()[0] v_beg_l = lamberts[i].get_v1()[0] # DSM occuring at time eta_i*T_i DV[i] = norm([a - b for a, b in zip(v_beg_l, v)]) # checking next leg after DSM saturn_distance_violated += self.check_distance( r, v_beg_l, etas[i] * T[i], T[i]) # single dv penalty for now if saturn_distance_violated > 0: DV[-1] += DV_PENALTY arr_vinf = norm([a - b for a, b in zip(v_end_l, v_P[-1])]) # last Delta-v if self._add_vinf_arr: DV[-1] = arr_vinf if self._add_vinf_dep: DV[0] += dep_vinf # pretty printing if logging: print("First leg: {} to {}".format(self.seq[0].name, self.seq[1].name)) print("Departure: {0} ({1:0.6f} mjd2000)".format( t_P[0], t_P[0].mjd2000)) print("Duration: {0:0.6f}d".format(T[0])) print("VINF: {0:0.3f}m/s".format(dep_vinf)) print("DSM after {0:0.6f}d".format(etas[0] * T[0])) print("DSM magnitude: {0:0.6f}m/s".format(DV[0])) for i in range(1, self._n_legs): print("\nleg {}: {} to {}".format(i + 1, self.seq[i].name, self.seq[i + 1].name)) print("Duration: {0:0.6f}d".format(T[i])) print("Fly-by epoch: {0} ({1:0.6f} mjd2000)".format( t_P[i], t_P[i].mjd2000)) print("Fly-by radius: {0:0.6f} planetary radii".format(rps[i - 1])) print("DSM after {0:0.6f}d".format(etas[i] * T[i])) print("DSM magnitude: {0:0.6f}m/s".format(DV[i])) print("\nArrival at {}".format(self.seq[-1].name)) print("Arrival epoch: {0} ({1:0.6f} mjd2000)".format( t_P[-1], t_P[-1].mjd2000)) print("Arrival Vinf: {0:0.3f}m/s".format(arr_vinf)) print("Total mission time: {0:0.6f}d ({1:0.3f} years)".format( sum(T), sum(T) / 365.25)) # plotting if plotting: ax.scatter(0, 0, 0, color='chocolate') for i, planet in enumerate(self.seq): plot_planet(planet, t0=t_P[i], color=pl2c[planet.name], legend=True, units=AU, ax=ax) for i in range(0, self._n_legs): plot_kepler(r_P[i], v_outs[i], etas[i] * T[i] * DAY2SEC, self.common_mu, N=100, color='b', legend=False, units=AU, ax=ax) for l in lamberts: plot_lambert(l, sol=0, color='r', legend=False, units=AU, N=1000, ax=ax) # returning building blocks for objectives return (DV, T, arr_vinf, lamberts)
# specific impulse ISP = 308 # constants related to mass M_WET = 5000 # kg M_DRY = 3000 # kg DV_BUDGET = ISP * G0 * log(M_WET / M_DRY) # additive constant for violation of constraints DV_PENALTY = 200000 # constants related to time T_START, T_END = 20000, 24000 # MJD2000 # 2020-01-January, 00:00:00 REF_EPOCH = epoch(2458849.500000000, julian_date_type='jd') # make the bodies enceladus = keplerian(REF_EPOCH, ENCELADUS_ELEM, MU_SATURN, MU_ENCELADUS, RADIUS_ENCELADUS, RADIUS_ENCELADUS * 1.1, 'enceladus') tethys = keplerian(REF_EPOCH, TETHYS_ELEM, MU_SATURN, MU_TETHYS, RADIUS_TETHYS, RADIUS_TETHYS * 1.1, 'tethys') dione = keplerian(REF_EPOCH, DIONE_ELEM, MU_SATURN, MU_DIONE, RADIUS_DIONE, RADIUS_DIONE * 1.1, 'dione') rhea = keplerian(REF_EPOCH, RHEA_ELEM, MU_SATURN, MU_RHEA, RADIUS_RHEA, RADIUS_RHEA * 1.1, 'rhea') titan = keplerian(REF_EPOCH, TITAN_ELEM, MU_SATURN, MU_TITAN, RADIUS_TITAN, RADIUS_TITAN * 1.1, 'titan') # different colors for plotting pl2c = {
def plot(self, x, ax=None): """ ax = prob.plot(x, ax=None) - x: encoded trajectory - ax: matplotlib axis where to plot. If None figure and axis will be created - [out] ax: matplotlib axis where to plot Plots the trajectory represented by a decision vector x on the 3d axis ax Example:: ax = prob.plot(x) """ import matplotlib as mpl from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from pykep import epoch, AU from pykep.sims_flanagan import sc_state from pykep.orbit_plots import plot_planet, plot_sf_leg # Creating the axis if necessary if ax is None: mpl.rcParams['legend.fontsize'] = 10 fig = plt.figure() axis = fig.gca(projection='3d') else: axis = ax # Plotting the Sun ........ axis.scatter([0], [0], [0], color='y') # Plotting the legs ....... # 1 - We decode the chromosome extracting the time of flights T = list([0] * (self.__n_legs)) for i in range(self.__n_legs): T[i] = x[1 + i * 8] # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.__n_legs + 1)) r_P = list([None] * (self.__n_legs + 1)) v_P = list([None] * (self.__n_legs + 1)) for i, planet in enumerate(self.seq): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = self.seq[i].eph(t_P[i]) # 3 - We iterate through legs to compute mismatches and throttles # constraints ceq = list() cineq = list() m0 = self.__sc.mass for i in range(self.__n_legs): # First Leg v = [a + b for a, b in zip(v_P[i], x[(3 + i * 8):(6 + i * 8)])] x0 = sc_state(r_P[i], v, m0) v = [ a + b for a, b in zip(v_P[i + 1], x[(6 + i * 8):(11 + i * 8)]) ] xe = sc_state(r_P[i + 1], v, x[2 + i * 8]) throttles = x[(1 + 8 * self.__n_legs + 3 * sum(self.__n_seg[:i])):( 1 + 8 * self.__n_legs + 3 * sum(self.__n_seg[:i]) + 3 * self.__n_seg[i])] self.__leg.set(t_P[i], x0, throttles, t_P[i + 1], xe) # update mass! m0 = x[2 + 8 * i] plot_sf_leg(self.__leg, units=AU, N=10, ax=axis) # Plotting planets for i, planet in enumerate(self.seq): plot_planet(planet, t_P[i], units=AU, legend=True, color=(0.7, 0.7, 1), ax=axis) plt.show() return axis
def get_patching_conditions(self, x): """ returns the final epoch of the trajectory, the last ending point and the outgoing velocity """ # DV, T, arr_vinf, lamberts = self._fitness_impl(x) DV, T, arr_vinf, lamberts = self.decode_state_eval_fitness(x) v = lamberts[-1].get_v2()[0] return (epoch(x[0] + sum(T)), self.seq[-1], v)
def fitness(self, x): # The final mass is the fitness objfun = [-x[2 + 8 * (self._n_legs - 1)]] eq_c = [] ineq_c = [] if self._multiobjective: objfun = objfun + [sum(x[1:8 * self._n_legs:8])] # We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self._n_legs + 1)) r_P = list([None] * (self._n_legs + 1)) v_P = list([None] * (self._n_legs + 1)) for i in range(len(self._seq)): t_P[i] = epoch(x[0] + sum(x[1:i * 8:8])) r_P[i], v_P[i] = self._seq[i].eph(t_P[i]) # We assemble the constraints. # 1 - Mismatch Constraints for i in range(self._n_legs): # Departure velocity of the spacecraft in the heliocentric frame v0 = [a + b for a, b in zip(v_P[i], x[3 + 8 * i:6 + 8 * i])] if i == 0: m0 = self._mass[1] else: m0 = x[2 + 8 * (i - 1)] x0 = sc_state(r_P[i], v0, m0) vf = [a + b for a, b in zip(v_P[i + 1], x[6 + 8 * i:9 + 8 * i])] xf = sc_state(r_P[i + 1], vf, x[2 + 8 * i]) idx_start = 1 + 8 * self._n_legs + sum(self._n_seg[:i]) * 3 idx_end = 1 + 8 * self._n_legs + sum(self._n_seg[:i + 1]) * 3 self._leg.set(t_P[i], x0, x[idx_start:idx_end], t_P[i + 1], xf) mismatch = list(self._leg.mismatch_constraints()) # Making the mismatch non dimensional (assumes an heliocentric interplanetary trajectory) mismatch[0] /= AU mismatch[1] /= AU mismatch[2] /= AU mismatch[3] /= EARTH_VELOCITY mismatch[4] /= EARTH_VELOCITY mismatch[5] /= EARTH_VELOCITY mismatch[6] /= self._mass[1] eq_c = eq_c + mismatch ineq_c = ineq_c + list(self._leg.throttles_constraints()) # 2 - Fly-by constraints for i in range(self._n_legs - 1): DV_eq, alpha_ineq = fb_con(x[6 + i * 8:9 + i * 8], x[11 + i * 8:14 + i * 8], self._seq[i + 1]) eq_c = eq_c + [DV_eq / (EARTH_VELOCITY * EARTH_VELOCITY)] ineq_c = ineq_c + [alpha_ineq] # 3 - Departure and arrival Vinf # departure v_dep_con = (x[3] * x[3] + x[4] * x[4] + x[5] * x[5] - self._vinf_dep * self._vinf_dep) / (EARTH_VELOCITY * EARTH_VELOCITY) # arrival n_fb = self._n_legs - 1 v_arr_con = (x[6 + n_fb * 8] * x[6 + n_fb * 8] + x[7 + n_fb * 8] * x[7 + n_fb * 8] + x[8 + n_fb * 8] * x[8 + n_fb * 8] - self._vinf_arr * self._vinf_arr) / (EARTH_VELOCITY * EARTH_VELOCITY) ineq_c = ineq_c + [v_dep_con, v_arr_con] return objfun + eq_c + ineq_c
def plot(self, x, axes=None): """ ax = prob.plot_trajectory(x, axes=None) - x: encoded trajectory - axes: matplotlib axis where to plot. If None figure and axis will be created - [out] ax: matplotlib axis where to plot Plots the trajectory represented by a decision vector x on the 3d axis ax Example:: ax = prob.plot(x) """ import matplotlib as mpl from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from pykep.orbit_plots import plot_planet, plot_lambert, plot_kepler if axes is None: mpl.rcParams['legend.fontsize'] = 10 fig = plt.figure() axes = fig.gca(projection='3d') axes.scatter(0, 0, 0, color='y') # 1 - we 'decode' the chromosome recording the various deep space # manouvres timing (days) in the list T T = list([0] * (self.N_max - 1)) for i in range(len(T)): T[i] = log(x[2 + 4 * i]) total = sum(T) T = [x[1] * time / total for time in T] # 2 - We compute the starting and ending position r_start, v_start = self.start.eph(epoch(x[0])) if self.phase_free: r_target, v_target = self.target.eph(epoch(x[-1])) else: r_target, v_target = self.target.eph(epoch(x[0] + x[1])) plot_planet(self.start, t0=epoch(x[0]), color=(0.8, 0.6, 0.8), legend=True, units=AU, ax=axes, s=0) plot_planet(self.target, t0=epoch(x[0] + x[1]), color=(0.8, 0.6, 0.8), legend=True, units=AU, ax=axes, s=0) DV_list = x[5::4] maxDV = max(DV_list) DV_list = [s / maxDV * 30 for s in DV_list] colors = ['b', 'r'] * (len(DV_list) + 1) # 3 - We loop across inner impulses rsc = r_start vsc = v_start for i, time in enumerate(T[:-1]): theta = 2 * pi * x[3 + 4 * i] phi = acos(2 * x[4 + 4 * i] - 1) - pi / 2 Vinfx = x[5 + 4 * i] * cos(phi) * cos(theta) Vinfy = x[5 + 4 * i] * cos(phi) * sin(theta) Vinfz = x[5 + 4 * i] * sin(phi) # We apply the (i+1)-th impulse vsc = [a + b for a, b in zip(vsc, [Vinfx, Vinfy, Vinfz])] axes.scatter(rsc[0] / AU, rsc[1] / AU, rsc[2] / AU, color='k', s=DV_list[i]) plot_kepler(rsc, vsc, T[i] * DAY2SEC, self.__common_mu, N=200, color=colors[i], legend=False, units=AU, ax=axes) rsc, vsc = propagate_lagrangian(rsc, vsc, T[i] * DAY2SEC, self.__common_mu) cw = (ic2par(rsc, vsc, self.start.mu_central_body)[2] > pi / 2) # We now compute the remaining two final impulses # Lambert arc to reach seq[1] dt = T[-1] * DAY2SEC l = lambert_problem(rsc, r_target, dt, self.__common_mu, cw, False) plot_lambert(l, sol=0, color=colors[i + 1], legend=False, units=AU, ax=axes, N=200) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] DV1 = norm([a - b for a, b in zip(v_beg_l, vsc)]) DV2 = norm([a - b for a, b in zip(v_end_l, v_target)]) axes.scatter(rsc[0] / AU, rsc[1] / AU, rsc[2] / AU, color='k', s=min(DV1 / maxDV * 30, 40)) axes.scatter(r_target[0] / AU, r_target[1] / AU, r_target[2] / AU, color='k', s=min(DV2 / maxDV * 30, 40)) return axes
def fitness(self, x): # 1 - we 'decode' the chromosome recording the various times of flight # (days) in the list T and the cartesian components of vinf T, Vinfx, Vinfy, Vinfz = self._decode_times_and_vinf(x) # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.n_legs + 1)) r_P = list([None] * (self.n_legs + 1)) v_P = list([None] * (self.n_legs + 1)) DV = list([0.0] * (self.n_legs + 1)) for i in range(len(self._seq)): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = self._seq[i].eph(t_P[i]) # 3 - We start with the first leg v0 = [a + b for a, b in zip(v_P[0], [Vinfx, Vinfy, Vinfz])] r, v = propagate_lagrangian(r_P[0], v0, x[4] * T[0] * DAY2SEC, self.common_mu) # Lambert arc to reach seq[1] dt = (1 - x[4]) * T[0] * DAY2SEC l = lambert_problem(r, r_P[1], dt, self.common_mu, cw=False, max_revs=0) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # First DSM occuring at time nu1*T1 DV[0] = norm([a - b for a, b in zip(v_beg_l, v)]) # 4 - And we proceed with each successive leg for i in range(1, self.n_legs): # Fly-by v_out = fb_prop(v_end_l, v_P[i], x[7 + (i - 1) * 4] * self._seq[i].radius, x[6 + (i - 1) * 4], self._seq[i].mu_self) # s/c propagation before the DSM r, v = propagate_lagrangian(r_P[i], v_out, x[8 + (i - 1) * 4] * T[i] * DAY2SEC, self.common_mu) # Lambert arc to reach Earth during (1-nu2)*T2 (second segment) dt = (1 - x[8 + (i - 1) * 4]) * T[i] * DAY2SEC l = lambert_problem(r, r_P[i + 1], dt, self.common_mu, cw=False, max_revs=0) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # DSM occuring at time nu2*T2 DV[i] = norm([a - b for a, b in zip(v_beg_l, v)]) # Last Delta-v if self._add_vinf_arr: DV[-1] = norm([a - b for a, b in zip(v_end_l, v_P[-1])]) if self._orbit_insertion: # In this case we compute the insertion DV as a single pericenter # burn DVper = np.sqrt(DV[-1] * DV[-1] + 2 * self._seq[-1].mu_self / self._rp_target) DVper2 = np.sqrt(2 * self._seq[-1].mu_self / self._rp_target - self._seq[-1].mu_self / self._rp_target * (1. - self._e_target)) DV[-1] = np.abs(DVper - DVper2) if self._add_vinf_dep: DV[0] += x[3] if not self._multi_objective: return (sum(DV), ) else: return (sum(DV), sum(T))
def _compute_dvs(self, x: List[float]) -> Tuple[ List[float], # DVs List[Any], # Lambert legs List[float], # T List[Tuple[List[float], List[float]]], # ballistic legs List[float], # epochs of ballistic legs ]: # 1 - we 'decode' the chromosome recording the various times of flight # (days) in the list T and the cartesian components of vinf T, Vinfx, Vinfy, Vinfz = self._decode_times_and_vinf(x) # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.n_legs + 1)) r_P = list([None] * (self.n_legs + 1)) v_P = list([None] * (self.n_legs + 1)) DV = list([0.0] * (self.n_legs + 1)) for i in range(len(self._seq)): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = self._seq[i].eph(t_P[i]) ballistic_legs: List[Tuple[List[float], List[float]]] = [] ballistic_ep: List[float] = [] lamberts = [] # 3 - We start with the first leg v0 = [a + b for a, b in zip(v_P[0], [Vinfx, Vinfy, Vinfz])] ballistic_legs.append((r_P[0], v0)) ballistic_ep.append(t_P[0].mjd2000) r, v = propagate_lagrangian(r_P[0], v0, x[4] * T[0] * DAY2SEC, self.common_mu) # Lambert arc to reach seq[1] dt = (1 - x[4]) * T[0] * DAY2SEC l = lambert_problem_multirev( v, lambert_problem(r, r_P[1], dt, self.common_mu, cw=False, max_revs=self.max_revs)) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] lamberts.append(l) ballistic_legs.append((r, v_beg_l)) ballistic_ep.append(t_P[0].mjd2000 + x[4] * T[0]) # First DSM occuring at time nu1*T1 DV[0] = norm([a - b for a, b in zip(v_beg_l, v)]) # 4 - And we proceed with each successive leg for i in range(1, self.n_legs): # Fly-by v_out = fb_prop(v_end_l, v_P[i], x[7 + (i - 1) * 4] * self._seq[i].radius, x[6 + (i - 1) * 4], self._seq[i].mu_self) ballistic_legs.append((r_P[i], v_out)) ballistic_ep.append(t_P[i].mjd2000) # s/c propagation before the DSM r, v = propagate_lagrangian(r_P[i], v_out, x[8 + (i - 1) * 4] * T[i] * DAY2SEC, self.common_mu) # Lambert arc to reach Earth during (1-nu2)*T2 (second segment) dt = (1 - x[8 + (i - 1) * 4]) * T[i] * DAY2SEC l = lambert_problem_multirev( v, lambert_problem(r, r_P[i + 1], dt, self.common_mu, cw=False, max_revs=self.max_revs)) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] lamberts.append(l) # DSM occuring at time nu2*T2 DV[i] = norm([a - b for a, b in zip(v_beg_l, v)]) ballistic_legs.append((r, v_beg_l)) ballistic_ep.append(t_P[i].mjd2000 + x[8 + (i - 1) * 4] * T[i]) # Last Delta-v if self._add_vinf_arr: DV[-1] = norm([a - b for a, b in zip(v_end_l, v_P[-1])]) if self._orbit_insertion: # In this case we compute the insertion DV as a single pericenter # burn DVper = np.sqrt(DV[-1] * DV[-1] + 2 * self._seq[-1].mu_self / self._rp_target) DVper2 = np.sqrt(2 * self._seq[-1].mu_self / self._rp_target - self._seq[-1].mu_self / self._rp_target * (1. - self._e_target)) DV[-1] = np.abs(DVper - DVper2) if self._add_vinf_dep: DV[0] += x[3] return (DV, lamberts, T, ballistic_legs, ballistic_ep)
def __init__(self, seq=[jpl_lp('earth'), jpl_lp('venus'), jpl_lp('earth')], n_seg=[10] * 2, t0=[epoch(0), epoch(1000)], tof=[[200, 500], [200, 500]], vinf_dep=2.5, vinf_arr=2.0, mass=4000.0, Tmax=1.0, Isp=2000.0, fb_rel_vel=6, multi_objective=False, high_fidelity=False): """ prob = mga_lt_nep(seq = [jpl_lp('earth'),jpl_lp('venus'),jpl_lp('earth')], n_seg = [10]*2, t0 = [epoch(0),epoch(1000)], tof = [[200,500],[200,500]], vinf_dep=2.5, vinf_arr=2.0, mass=4000.0, Tmax=1.0, Isp=2000.0, multi_objective = False, fb_rel_vel = 6, high_fidelity=False) - seq: list of pykep.planet defining the encounter sequence for the trajectoty (including the initial planet) - n_seg: list of integers containing the number of segments to be used for each leg (len(n_seg) = len(seq)-1) - t0: list of pykep epochs defining the launch window - tof: minimum and maximum time of each leg (days) - vinf_dep: maximum launch hyperbolic velocity allowed (in km/sec) - vinf_arr: maximum arrival hyperbolic velocity allowed (in km/sec) - mass: spacecraft starting mass - Tmax: maximum thrust - Isp: engine specific impulse - fb_rel_vel = determines the bounds on the maximum allowed relative velocity at all fly-bys (in km/sec) - multi-objective: when True defines the problem as a multi-objective problem, returning total DV and time of flight - high_fidelity = makes the trajectory computations slower, but actually dynamically feasible. """ # 1) We compute the problem dimensions .... and call the base problem # constructor self.__n_legs = len(seq) - 1 n_fb = self.__n_legs - 1 # 1a) The decision vector length dim = 1 + self.__n_legs * 8 + sum(n_seg) * 3 # 1b) The total number of constraints (mismatch + fly-by + boundary + # throttles c_dim = self.__n_legs * 7 + n_fb * 2 + 2 + sum(n_seg) # 1c) The number of inequality constraints (boundary + fly-by angle + # throttles) c_ineq_dim = 2 + n_fb + sum(n_seg) # 1d) the number of objectives f_dim = multi_objective + 1 # First we call the constructor for the base pygmo problem # As our problem is n dimensional, box-bounded (may be multi-objective), we write # (dim, integer dim, number of obj, number of con, number of inequality con, tolerance on con violation) super(mga_lt_nep, self).__init__( dim, 0, f_dim, c_dim, c_ineq_dim, 1e-4) # 2) We then define some class data members # public: self.seq = seq # private: self.__n_seg = n_seg self.__vinf_dep = vinf_dep * 1000 self.__vinf_arr = vinf_arr * 1000 self.__sc = spacecraft(mass, Tmax, Isp) self.__leg = leg() self.__leg.set_mu(MU_SUN) self.__leg.set_spacecraft(self.__sc) self.__leg.high_fidelity = high_fidelity fb_rel_vel *= 1000 # 3) We compute the bounds lb = [t0[0].mjd2000] + [0, mass / 2, -fb_rel_vel, -fb_rel_vel, -fb_rel_vel, - fb_rel_vel, -fb_rel_vel, -fb_rel_vel] * self.__n_legs + [-1, -1, -1] * sum(self.__n_seg) ub = [t0[1].mjd2000] + [1, mass, fb_rel_vel, fb_rel_vel, fb_rel_vel, fb_rel_vel, fb_rel_vel, fb_rel_vel] * self.__n_legs + [1, 1, 1] * sum(self.__n_seg) # 3a ... and account for the bounds on the vinfs...... lb[3:6] = [-self.__vinf_dep] * 3 ub[3:6] = [self.__vinf_dep] * 3 lb[-sum(self.__n_seg) * 3 - 3:-sum(self.__n_seg) * 3] = [-self.__vinf_arr] * 3 ub[-sum(self.__n_seg) * 3 - 3:-sum(self.__n_seg) * 3] = [self.__vinf_arr] * 3 # 3b... and for the time of flight lb[1:1 + 8 * self.__n_legs:8] = [el[0] for el in tof] ub[1:1 + 8 * self.__n_legs:8] = [el[1] for el in tof] # 4) And we set the bounds self.set_bounds(lb, ub)
def plot(self, x, ax=None): """ ax = prob.plot(x, ax=None) - x: encoded trajectory - ax: matplotlib axis where to plot. If None figure and axis will be created - [out] ax: matplotlib axis where to plot Plots the trajectory represented by a decision vector x on the 3d axis ax Example:: ax = prob.plot(x) """ import matplotlib as mpl from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from pykep import epoch, AU from pykep.sims_flanagan import sc_state from pykep.orbit_plots import plot_planet, plot_sf_leg # Creating the axis if necessary if ax is None: mpl.rcParams['legend.fontsize'] = 10 fig = plt.figure() axis = fig.gca(projection='3d') else: axis = ax # Plotting the Sun ........ axis.scatter([0], [0], [0], color='y') # Plotting the legs ....... # 1 - We decode the chromosome extracting the time of flights T = list([0] * (self.__n_legs)) for i in range(self.__n_legs): T[i] = x[1 + i * 8] # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.__n_legs + 1)) r_P = list([None] * (self.__n_legs + 1)) v_P = list([None] * (self.__n_legs + 1)) for i, planet in enumerate(self.seq): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = self.seq[i].eph(t_P[i]) # 3 - We iterate through legs to compute mismatches and throttles # constraints ceq = list() cineq = list() m0 = self.__sc.mass for i in range(self.__n_legs): # First Leg v = [a + b for a, b in zip(v_P[i], x[(3 + i * 8):(6 + i * 8)])] x0 = sc_state(r_P[i], v, m0) v = [a + b for a, b in zip(v_P[i + 1], x[(6 + i * 8):(11 + i * 8)])] xe = sc_state(r_P[i + 1], v, x[2 + i * 8]) throttles = x[(1 + 8 * self.__n_legs + 3 * sum(self.__n_seg[:i])):( 1 + 8 * self.__n_legs + 3 * sum(self.__n_seg[:i]) + 3 * self.__n_seg[i])] self.__leg.set(t_P[i], x0, throttles, t_P[i + 1], xe) # update mass! m0 = x[2 + 8 * i] plot_sf_leg(self.__leg, units=AU, N=10, ax=axis) # Plotting planets for i, planet in enumerate(self.seq): plot_planet(planet, t_P[i], units=AU, legend=True, color=(0.7, 0.7, 1), ax=axis) plt.show() return axis
def _compute_constraints_impl(self, x): # 1 - We decode the chromosome extracting the time of flights T = list([0] * (self.__n_legs)) for i in range(self.__n_legs): T[i] = x[1 + i * 8] # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.__n_legs + 1)) r_P = list([None] * (self.__n_legs + 1)) v_P = list([None] * (self.__n_legs + 1)) for i, planet in enumerate(self.seq): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = self.seq[i].eph(t_P[i]) # 3 - We iterate through legs to compute mismatches and throttles # constraints ceq = list() cineq = list() m0 = self.__sc.mass for i in range(self.__n_legs): # First Leg v = [a + b for a, b in zip(v_P[i], x[(3 + i * 8):(6 + i * 8)])] x0 = sc_state(r_P[i], v, m0) v = [a + b for a, b in zip(v_P[i + 1], x[(6 + i * 8):(9 + i * 8)])] xe = sc_state(r_P[i + 1], v, x[2 + i * 8]) throttles = x[(1 + 8 * self.__n_legs + 3 * sum(self.__n_seg[:i])):( 1 + 8 * self.__n_legs + 3 * sum(self.__n_seg[:i]) + 3 * self.__n_seg[i])] self.__leg.set(t_P[i], x0, throttles, t_P[i + 1], xe) # update mass! m0 = x[2 + 8 * i] ceq.extend(self.__leg.mismatch_constraints()) cineq.extend(self.__leg.throttles_constraints()) # Adding the boundary constraints # departure v_dep_con = (x[3] ** 2 + x[4] ** 2 + x[5] ** 2 - self.__vinf_dep ** 2) / (EARTH_VELOCITY ** 2) # arrival v_arr_con = (x[6 + (self.__n_legs - 1) * 8] ** 2 + x[7 + (self.__n_legs - 1) * 8] ** 2 + x[8 + (self.__n_legs - 1) * 8] ** 2 - self.__vinf_arr ** 2) / (EARTH_VELOCITY ** 2) cineq.append(v_dep_con * 100) cineq.append(v_arr_con * 100) # We add the fly-by constraints for i in range(self.__n_legs - 1): DV_eq, alpha_ineq = fb_con( x[6 + i * 8:9 + i * 8], x[11 + i * 8:14 + i * 8], self.seq[i + 1]) ceq.append(DV_eq / (EARTH_VELOCITY ** 2)) cineq.append(alpha_ineq) # Making the mismatches non dimensional for i in range(self.__n_legs): ceq[0 + i * 7] /= AU ceq[1 + i * 7] /= AU ceq[2 + i * 7] /= AU ceq[3 + i * 7] /= EARTH_VELOCITY ceq[4 + i * 7] /= EARTH_VELOCITY ceq[5 + i * 7] /= EARTH_VELOCITY ceq[6 + i * 7] /= self.__sc.mass # We assemble the constraint vector retval = list() retval.extend(ceq) retval.extend(cineq) return retval
def __init__(self, seq=[jpl_lp('earth'), jpl_lp('venus'), jpl_lp('earth')], n_seg=[10] * 2, t0=[epoch(0), epoch(1000)], tof=[[200, 500], [200, 500]], vinf_dep=2.5, vinf_arr=2.0, mass=4000.0, Tmax=1.0, Isp=2000.0, fb_rel_vel=6, multi_objective=False, high_fidelity=False): """ prob = mga_lt_nep(seq = [jpl_lp('earth'),jpl_lp('venus'),jpl_lp('earth')], n_seg = [10]*2, t0 = [epoch(0),epoch(1000)], tof = [[200,500],[200,500]], vinf_dep=2.5, vinf_arr=2.0, mass=4000.0, Tmax=1.0, Isp=2000.0, multi_objective = False, fb_rel_vel = 6, high_fidelity=False) - seq: list of pykep.planet defining the encounter sequence for the trajectoty (including the initial planet) - n_seg: list of integers containing the number of segments to be used for each leg (len(n_seg) = len(seq)-1) - t0: list of pykep epochs defining the launch window - tof: minimum and maximum time of each leg (days) - vinf_dep: maximum launch hyperbolic velocity allowed (in km/sec) - vinf_arr: maximum arrival hyperbolic velocity allowed (in km/sec) - mass: spacecraft starting mass - Tmax: maximum thrust - Isp: engine specific impulse - fb_rel_vel = determines the bounds on the maximum allowed relative velocity at all fly-bys (in km/sec) - multi-objective: when True defines the problem as a multi-objective problem, returning total DV and time of flight - high_fidelity = makes the trajectory computations slower, but actually dynamically feasible. """ # 1) We compute the problem dimensions .... and call the base problem # constructor self.__n_legs = len(seq) - 1 n_fb = self.__n_legs - 1 # 1a) The decision vector length dim = 1 + self.__n_legs * 8 + sum(n_seg) * 3 # 1b) The total number of constraints (mismatch + fly-by + boundary + # throttles c_dim = self.__n_legs * 7 + n_fb * 2 + 2 + sum(n_seg) # 1c) The number of inequality constraints (boundary + fly-by angle + # throttles) c_ineq_dim = 2 + n_fb + sum(n_seg) # 1d) the number of objectives f_dim = multi_objective + 1 # First we call the constructor for the base pygmo problem # As our problem is n dimensional, box-bounded (may be multi-objective), we write # (dim, integer dim, number of obj, number of con, number of inequality con, tolerance on con violation) super(mga_lt_nep, self).__init__(dim, 0, f_dim, c_dim, c_ineq_dim, 1e-4) # 2) We then define some class data members # public: self.seq = seq # private: self.__n_seg = n_seg self.__vinf_dep = vinf_dep * 1000 self.__vinf_arr = vinf_arr * 1000 self.__sc = spacecraft(mass, Tmax, Isp) self.__leg = leg() self.__leg.set_mu(MU_SUN) self.__leg.set_spacecraft(self.__sc) self.__leg.high_fidelity = high_fidelity fb_rel_vel *= 1000 # 3) We compute the bounds lb = [t0[0].mjd2000] + [ 0, mass / 2, -fb_rel_vel, -fb_rel_vel, -fb_rel_vel, -fb_rel_vel, -fb_rel_vel, -fb_rel_vel ] * self.__n_legs + [-1, -1, -1] * sum(self.__n_seg) ub = [t0[1].mjd2000] + [ 1, mass, fb_rel_vel, fb_rel_vel, fb_rel_vel, fb_rel_vel, fb_rel_vel, fb_rel_vel ] * self.__n_legs + [1, 1, 1] * sum(self.__n_seg) # 3a ... and account for the bounds on the vinfs...... lb[3:6] = [-self.__vinf_dep] * 3 ub[3:6] = [self.__vinf_dep] * 3 lb[-sum(self.__n_seg) * 3 - 3:-sum(self.__n_seg) * 3] = [-self.__vinf_arr] * 3 ub[-sum(self.__n_seg) * 3 - 3:-sum(self.__n_seg) * 3] = [self.__vinf_arr] * 3 # 3b... and for the time of flight lb[1:1 + 8 * self.__n_legs:8] = [el[0] for el in tof] ub[1:1 + 8 * self.__n_legs:8] = [el[1] for el in tof] # 4) And we set the bounds self.set_bounds(lb, ub)
def plot(self, x, ax=None): """ ax = prob.plot(x, ax=None) - x: encoded trajectory - ax: matplotlib axis where to plot. If None figure and axis will be created - [out] ax: matplotlib axis where to plot Plots the trajectory represented by a decision vector x on the 3d axis ax Example:: ax = prob.plot(x) """ import matplotlib as mpl from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from pykep.orbit_plots import plot_planet, plot_lambert, plot_kepler if ax is None: mpl.rcParams['legend.fontsize'] = 10 fig = plt.figure() axis = fig.gca(projection='3d') else: axis = ax axis.scatter(0, 0, 0, color='y') # 1 - we 'decode' the chromosome recording the various times of flight # (days) in the list T and the cartesian components of vinf T, Vinfx, Vinfy, Vinfz = self._decode_times_and_vinf(x) # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.n_legs + 1)) r_P = list([None] * (self.n_legs + 1)) v_P = list([None] * (self.n_legs + 1)) DV = list([None] * (self.n_legs + 1)) for i, planet in enumerate(self._seq): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = planet.eph(t_P[i]) plot_planet(planet, t0=t_P[i], color=(0.8, 0.6, 0.8), legend=True, units=AU, axes=axis, N=150) # 3 - We start with the first leg v0 = [a + b for a, b in zip(v_P[0], [Vinfx, Vinfy, Vinfz])] r, v = propagate_lagrangian(r_P[0], v0, x[4] * T[0] * DAY2SEC, self.common_mu) plot_kepler(r_P[0], v0, x[4] * T[0] * DAY2SEC, self.common_mu, N=100, color='b', legend=False, units=AU, axes=axis) # Lambert arc to reach seq[1] dt = (1 - x[4]) * T[0] * DAY2SEC l = lambert_problem(r, r_P[1], dt, self.common_mu, False, False) plot_lambert(l, sol=0, color='r', legend=False, units=AU, axes=axis) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # First DSM occuring at time nu1*T1 DV[0] = norm([a - b for a, b in zip(v_beg_l, v)]) # 4 - And we proceed with each successive leg for i in range(1, self.n_legs): # Fly-by v_out = fb_prop(v_end_l, v_P[i], x[7 + (i - 1) * 4] * self._seq[i].radius, x[6 + (i - 1) * 4], self._seq[i].mu_self) # s/c propagation before the DSM r, v = propagate_lagrangian(r_P[i], v_out, x[8 + (i - 1) * 4] * T[i] * DAY2SEC, self.common_mu) plot_kepler(r_P[i], v_out, x[8 + (i - 1) * 4] * T[i] * DAY2SEC, self.common_mu, N=100, color='b', legend=False, units=AU, axes=axis) # Lambert arc to reach Earth during (1-nu2)*T2 (second segment) dt = (1 - x[8 + (i - 1) * 4]) * T[i] * DAY2SEC l = lambert_problem(r, r_P[i + 1], dt, self.common_mu, False, False) plot_lambert(l, sol=0, color='r', legend=False, units=AU, N=1000, axes=axis) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # DSM occuring at time nu2*T2 DV[i] = norm([a - b for a, b in zip(v_beg_l, v)]) plt.show() return axis
def pretty(self, x): """ prob.plot(x) - x: encoded trajectory Prints human readable information on the trajectory represented by the decision vector x Example:: print(prob.pretty(x)) """ # 1 - we 'decode' the chromosome recording the various times of flight # (days) in the list T and the cartesian components of vinf T, Vinfx, Vinfy, Vinfz = self._decode_times_and_vinf(x) # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.n_legs + 1)) r_P = list([None] * (self.n_legs + 1)) v_P = list([None] * (self.n_legs + 1)) DV = list([0.0] * (self.n_legs + 1)) for i in range(len(self._seq)): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = self._seq[i].eph(t_P[i]) # 3 - We start with the first leg print("First Leg: " + self._seq[0].name + " to " + self._seq[1].name) print("Departure: " + str(t_P[0]) + " (" + str(t_P[0].mjd2000) + " mjd2000) ") print("Duration: " + str(T[0]) + "days") print("VINF: " + str(x[3] / 1000) + " km/sec") v0 = [a + b for a, b in zip(v_P[0], [Vinfx, Vinfy, Vinfz])] r, v = propagate_lagrangian(r_P[0], v0, x[4] * T[0] * DAY2SEC, self.common_mu) print("DSM after " + str(x[4] * T[0]) + " days") # Lambert arc to reach seq[1] dt = (1 - x[4]) * T[0] * DAY2SEC l = lambert_problem(r, r_P[1], dt, self.common_mu, cw=False, max_revs=0) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # First DSM occuring at time nu1*T1 DV[0] = norm([a - b for a, b in zip(v_beg_l, v)]) print("DSM magnitude: " + str(DV[0]) + "m/s") # 4 - And we proceed with each successive leg for i in range(1, self.n_legs): print("\nleg no. " + str(i + 1) + ": " + self._seq[i].name + " to " + self._seq[i + 1].name) print("Duration: " + str(T[i]) + "days") # Fly-by v_out = fb_prop(v_end_l, v_P[i], x[7 + (i - 1) * 4] * self._seq[i].radius, x[6 + (i - 1) * 4], self._seq[i].mu_self) print("Fly-by epoch: " + str(t_P[i]) + " (" + str(t_P[i].mjd2000) + " mjd2000) ") print("Fly-by radius: " + str(x[7 + (i - 1) * 4]) + " planetary radii") # s/c propagation before the DSM r, v = propagate_lagrangian(r_P[i], v_out, x[8 + (i - 1) * 4] * T[i] * DAY2SEC, self.common_mu) print("DSM after " + str(x[8 + (i - 1) * 4] * T[i]) + " days") # Lambert arc to reach Earth during (1-nu2)*T2 (second segment) dt = (1 - x[8 + (i - 1) * 4]) * T[i] * DAY2SEC l = lambert_problem(r, r_P[i + 1], dt, self.common_mu, cw=False, max_revs=0) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # DSM occuring at time nu2*T2 DV[i] = norm([a - b for a, b in zip(v_beg_l, v)]) print("DSM magnitude: " + str(DV[i]) + "m/s") # Last Delta-v print("\nArrival at " + self._seq[-1].name) DV[-1] = norm([a - b for a, b in zip(v_end_l, v_P[-1])]) print("Arrival epoch: " + str(t_P[-1]) + " (" + str(t_P[-1].mjd2000) + " mjd2000) ") print("Arrival Vinf: " + str(DV[-1]) + "m/s") if self._orbit_insertion: # In this case we compute the insertion DV as a single pericenter # burn DVper = np.sqrt(DV[-1] * DV[-1] + 2 * self._seq[-1].mu_self / self._rp_target) DVper2 = np.sqrt(2 * self._seq[-1].mu_self / self._rp_target - self._seq[-1].mu_self / self._rp_target * (1. - self._e_target)) DVinsertion = np.abs(DVper - DVper2) print("Insertion DV: " + str(DVinsertion) + "m/s") print("Total mission time: " + str(sum(T) / 365.25) + " years (" + str(sum(T)) + " days)")
def ic_from_mga_1dsm(self, x): """ x_lt = prob.ic_from_mga_1dsm(x_mga) - x_mga: compatible trajectory as encoded by an mga_1dsm problem Returns an initial guess for the low-thrust trajectory, converting the mga_1dsm solution x_dsm. The user is responsible that x_mga makes sense (i.e. it is a viable mga_1dsm representation). The conversion is done by importing in the low-thrust encoding a) the launch date b) all the legs durations, c) the in and out relative velocities at each planet. All throttles are put to zero. Example:: x_lt= prob.ic_from_mga_1dsm(x_mga) """ from math import pi, cos, sin, acos from scipy.linalg import norm from pykep import propagate_lagrangian, lambert_problem, DAY2SEC, fb_prop retval = list([0.0] * self.dimension) # 1 - we 'decode' the chromosome recording the various times of flight # (days) in the list T T = list([0] * (self.__n_legs)) for i in range(len(T)): T[i] = log(x[2 + 4 * i]) total = sum(T) T = [x[1] * time / total for time in T] retval[0] = x[0] for i in range(self.__n_legs): retval[1 + 8 * i] = T[i] retval[2 + 8 * i] = self.__sc.mass # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.__n_legs + 1)) r_P = list([None] * (self.__n_legs + 1)) v_P = list([None] * (self.__n_legs + 1)) DV = list([None] * (self.__n_legs + 1)) for i, planet in enumerate(self.seq): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = self.seq[i].eph(t_P[i]) # 3 - We start with the first leg theta = 2 * pi * x[1] phi = acos(2 * x[2] - 1) - pi / 2 Vinfx = x[3] * cos(phi) * cos(theta) Vinfy = x[3] * cos(phi) * sin(theta) Vinfz = x[3] * sin(phi) retval[3:6] = [Vinfx, Vinfy, Vinfz] v0 = [a + b for a, b in zip(v_P[0], [Vinfx, Vinfy, Vinfz])] r, v = propagate_lagrangian(r_P[0], v0, x[4] * T[0] * DAY2SEC, MU_SUN) # Lambert arc to reach seq[1] dt = (1 - x[4]) * T[0] * DAY2SEC l = lambert_problem(r, r_P[1], dt, MU_SUN) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] retval[6:9] = [a - b for a, b in zip(v_end_l, v_P[1])] # 4 - And we proceed with each successive leg for i in range(1, self.__n_legs): # Fly-by v_out = fb_prop(v_end_l, v_P[i], x[7 + (i - 1) * 4] * self.seq[i].radius, x[6 + (i - 1) * 4], self.seq[i].mu_self) retval[3 + i * 8:6 + i * 8] = [a - b for a, b in zip(v_out, v_P[i])] # s/c propagation before the DSM r, v = propagate_lagrangian(r_P[i], v_out, x[8 + (i - 1) * 4] * T[i] * DAY2SEC, MU_SUN) # Lambert arc to reach Earth during (1-nu2)*T2 (second segment) dt = (1 - x[8 + (i - 1) * 4]) * T[i] * DAY2SEC l = lambert_problem(r, r_P[i + 1], dt, MU_SUN) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # DSM occuring at time nu2*T2 DV[i] = norm([a - b for a, b in zip(v_beg_l, v)]) retval[6 + i * 8:9 + i * 8] = [a - b for a, b in zip(v_end_l, v_P[i + 1])] return retval
def __init__(self, seq=[jpl_lp('earth'), jpl_lp('venus'), jpl_lp('earth')], t0=[epoch(0), epoch(1000)], tof=[[10, 300], [10, 300]], vinf=[0.5, 2.5], add_vinf_dep=False, add_vinf_arr=True, tof_encoding='direct', multi_objective=False, orbit_insertion=False, e_target=None, rp_target=None, eta_lb=0.1, eta_ub=0.9, rp_ub=30): """ pykep.trajopt.mga_1dsm(seq = [jpl_lp('earth'), jpl_lp('venus'), jpl_lp('earth')], t0 = [epoch(0),epoch(1000)], tof = [1.0,5.0], vinf = [0.5, 2.5], multi_objective = False, add_vinf_dep = False, add_vinf_arr=True) - seq (``list`` of ``pykep.planet``): the encounter sequence (including the starting launch) - t0 (``list`` of ``pykep.epoch`` or ``floats``): the launch window (in mjd2000 if floats) - tof (``list`` or ``float``): bounds on the time of flight (days). If *tof_encoding* is 'direct', this contains a list of 2D lists defining the upper and lower bounds on each leg. If *tof_encoding* is 'alpha', this contains a list of two floats containing the lower and upper bounds on the time-of-flight. If *tof_encoding* is 'eta' tof is a float defining the upper bound on the time-of-flight - vinf (``list``): the minimum and maximum allowed initial hyperbolic velocity (at launch), in km/sec - add_vinf_dep (``bool``): when True the computed Dv includes the initial hyperbolic velocity (at launch) - add_vinf_arr (``bool``): when True the computed Dv includes the final hyperbolic velocity (at the last planet) - tof_encoding (``str``): one of 'direct', 'alpha' or 'eta'. Selects the encoding for the time of flights - multi_objective (``bool``): when True constructs a multiobjective problem (dv, T) - orbit_insertion (``bool``): when True the arrival dv is computed as that required to acquire a target orbit defined by e_target and rp_target - e_target (``float``): if orbit_insertion is True this defines the target orbit eccentricity around the final planet - rp_target (``float``): if orbit_insertion is True this defines the target orbit pericenter around the final planet (in m) """ # Sanity checks # 1 - Planets need to have the same mu_central_body if ([r.mu_central_body for r in seq].count(seq[0].mu_central_body) != len(seq)): raise ValueError( 'All planets in the sequence need to have identical mu_central_body' ) # 2 - tof encoding needs to be one of 'alpha', 'eta', 'direct' if tof_encoding not in ['alpha', 'eta', 'direct']: raise TypeError( 'tof encoding must be one of \'alpha\', \'eta\', \'direct\'') # 3 - tof is expected to have different content depending on the tof_encoding if tof_encoding == 'direct': if np.shape(np.array(tof)) != (len(seq) - 1, 2): raise TypeError( 'tof_encoding is ' + tof_encoding + ' and tof must be a list of two dimensional lists and with length equal to the number of legs' ) if tof_encoding == 'alpha': if np.shape(np.array(tof)) != (2, ): raise TypeError('tof_encoding is ' + tof_encoding + ' and tof must be a list of two floats') if tof_encoding == 'eta': if np.shape(np.array(tof)) != (): raise TypeError('tof_encoding is ' + tof_encoding + ' and tof must be a float') # 4 - Check launch window t0. If defined in terms of floats transform into epochs if len(t0) != 2: raise TypeError('t0 is ' + t0 + ' while should be a list of two floats or epochs') if type(t0[0]) is not epoch: t0[0] = epoch(t0[0]) if type(t0[1]) is not epoch: t0[1] = epoch(t0[1]) # 5 - Check that if orbit insertion is selected e_target and r_p are # defined if orbit_insertion: if rp_target is None: raise ValueError( 'The rp_target needs to be specified when orbit insertion is selected' ) if e_target is None: raise ValueError( 'The e_target needs to be specified when orbit insertion is selected' ) if add_vinf_arr is False: raise ValueError( 'When orbit insertion is selected, the add_vinf_arr must be True' ) self._seq = seq self._t0 = t0 self._tof = tof self._vinf = vinf self._add_vinf_dep = add_vinf_dep self._add_vinf_arr = add_vinf_arr self._tof_encoding = tof_encoding self._multi_objective = multi_objective self._orbit_insertion = orbit_insertion self._e_target = e_target self._rp_target = rp_target self._eta_lb = eta_lb self._eta_ub = eta_ub self._rp_ub = rp_ub self.n_legs = len(seq) - 1 self.common_mu = seq[0].mu_central_body
def __init__(self, seq=[jpl_lp('earth'), jpl_lp('venus'), jpl_lp('earth')], t0=[0, 1000], tof=[[30, 200], [200, 300]], vinf=2.5, multi_objective=False, tof_encoding='direct', orbit_insertion=False, e_target=None, rp_target=None, max_revs=0): """mga(seq=[pk.planet.jpl_lp('earth'), pk.planet.jpl_lp('venus'), pk.planet.jpl_lp('earth')], t0=[0, 1000], tof=[100, 500], vinf=2.5, multi_objective=False, alpha_encoding=False, orbit_insertion=False, e_target=None, rp_target=None) Args: - seq (``list of pk.planet``): sequence of body encounters including the starting object - t0 (``list of pk.epoch``): the launch window - tof (``list`` or ``float``): bounds on the time of flight. If *tof_encoding* is 'direct', this contains a list of 2D lists defining the upper and lower bounds on each leg. If *tof_encoding* is 'alpha', this contains a 2D list with the lower and upper bounds on the time-of-flight. If *tof_encoding* is 'eta' tof is a float defining the upper bound on the time-of-flight - vinf (``float``): the vinf provided at launch for free - multi_objective (``bool``): when True constructs a multiobjective problem (dv, T). In this case, 'alpha' or `eta` encodings are recommended - tof_encoding (``str``): one of 'direct', 'alpha' or 'eta'. Selects the encoding for the time of flights - orbit_insertion (``bool``): when True the arrival dv is computed as that required to acquire a target orbit defined by e_target and rp_target - e_target (``float``): if orbit_insertion is True this defines the target orbit eccentricity around the final planet - rp_target (``float``): if orbit_insertion is True this defines the target orbit pericenter around the final planet (in m) - max_revs (``int``): maximal number of revolutions for lambert transfer Raises: - ValueError: if *planets* do not share the same central body (checked on the mu_central_body attribute) - ValueError: if *t0* does not contain objects able to construct a epoch (e.g. pk. epoch or floats) - ValueError: if *tof* is badly defined - ValueError: it the target orbit is not defined and *orbit_insertion* is True """ # Sanity checks # 1 - All planets need to have the same mu_central_body if ([r.mu_central_body for r in seq].count(seq[0].mu_central_body) != len(seq)): raise ValueError( 'All planets in the sequence need to have exactly the same mu_central_body' ) # 2 - We try to build epochs out of the t0 list (mjd2000 by default) for i in range(len(t0)): if (type(t0[i]) != type(epoch(0))): t0[i] = epoch(t0[i]) # 3 - Check the tof bounds if tof_encoding == 'alpha': if len(tof) != 2: raise ValueError( r'When the tof_encoding is \'alpha\', tof is expected to be something like [lb, ub]' ) elif tof_encoding == 'direct': if len(tof) != (len(seq) - 1): raise ValueError( 'When tof_encoding is direct, the tof must be a float (upper bound on the time of flight)' + str(len(seq) - 1)) elif tof_encoding == 'eta': try: float(tof) except TypeError: raise ValueError('The tof needs to be have len equal to ' + str(len(seq) - 1)) if not tof_encoding in ['alpha', 'eta', 'direct']: raise ValueError( "tof_encoding must be one of 'alpha', 'eta', 'direct'") # 4 - Check that if orbit insertion is selected e_target and r_p are # defined if orbit_insertion: if rp_target is None: raise ValueError( 'The rp_target needs to be specified when orbit insertion is selected' ) if e_target is None: raise ValueError( 'The e_target needs to be specified when orbit insertion is selected' ) # Public data members self.seq = seq self.t0 = t0 self.tof = tof self.vinf = vinf * 1000 self.multi_objective = multi_objective self.tof_encoding = tof_encoding self.orbit_insertion = orbit_insertion self.e_target = e_target self.rp_target = rp_target # Private data members self._n_legs = len(seq) - 1 self._common_mu = seq[0].mu_central_body self.max_revs = max_revs
def pretty(self, x): """ prob.plot(x) - x: encoded trajectory Prints human readable information on the trajectory represented by the decision vector x Example:: print(prob.pretty(x)) """ # 1 - we 'decode' the chromosome recording the various times of flight # (days) in the list T and the cartesian components of vinf T, Vinfx, Vinfy, Vinfz = self._decode_times_and_vinf(x) # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.__n_legs + 1)) r_P = list([None] * (self.__n_legs + 1)) v_P = list([None] * (self.__n_legs + 1)) DV = list([None] * (self.__n_legs + 1)) for i, planet in enumerate(self.seq): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = self.seq[i].eph(t_P[i]) # 3 - We start with the first leg print("First Leg: " + self.seq[0].name + " to " + self.seq[1].name) print("Departure: " + str(t_P[0]) + " (" + str(t_P[0].mjd2000) + " mjd2000) ") print("Duration: " + str(T[0]) + "days") print("VINF: " + str(x[4] / 1000) + " km/sec") v0 = [a + b for a, b in zip(v_P[0], [Vinfx, Vinfy, Vinfz])] r, v = propagate_lagrangian( r_P[0], v0, x[5] * T[0] * DAY2SEC, self.common_mu) print("DSM after " + str(x[5] * T[0]) + " days") # Lambert arc to reach seq[1] dt = (1 - x[5]) * T[0] * DAY2SEC l = lambert_problem(r, r_P[1], dt, self.common_mu, False, False) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # First DSM occuring at time nu1*T1 DV[0] = norm([a - b for a, b in zip(v_beg_l, v)]) print("DSM magnitude: " + str(DV[0]) + "m/s") # 4 - And we proceed with each successive leg for i in range(1, self.__n_legs): print("\nleg no. " + str(i + 1) + ": " + self.seq[i].name + " to " + self.seq[i + 1].name) print("Duration: " + str(T[i]) + "days") # Fly-by v_out = fb_prop(v_end_l, v_P[i], x[ 8 + (i - 1) * 4] * self.seq[i].radius, x[7 + (i - 1) * 4], self.seq[i].mu_self) print( "Fly-by epoch: " + str(t_P[i]) + " (" + str(t_P[i].mjd2000) + " mjd2000) ") print( "Fly-by radius: " + str(x[8 + (i - 1) * 4]) + " planetary radii") # s/c propagation before the DSM r, v = propagate_lagrangian( r_P[i], v_out, x[9 + (i - 1) * 4] * T[i] * DAY2SEC, self.common_mu) print("DSM after " + str(x[9 + (i - 1) * 4] * T[i]) + " days") # Lambert arc to reach Earth during (1-nu2)*T2 (second segment) dt = (1 - x[9 + (i - 1) * 4]) * T[i] * DAY2SEC l = lambert_problem(r, r_P[i + 1], dt, self.common_mu, False, False) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # DSM occuring at time nu2*T2 DV[i] = norm([a - b for a, b in zip(v_beg_l, v)]) print("DSM magnitude: " + str(DV[i]) + "m/s") # Last Delta-v print("\nArrival at " + self.seq[-1].name) DV[-1] = norm([a - b for a, b in zip(v_end_l, v_P[-1])]) print( "Arrival epoch: " + str(t_P[-1]) + " (" + str(t_P[-1].mjd2000) + " mjd2000) ") print("Arrival Vinf: " + str(DV[-1]) + "m/s") print("Total mission time: " + str(sum(T) / 365.25) + " years")
def plot(self, x, ax=None): """ ax = prob.plot(x, ax=None) - x: encoded trajectory - ax: matplotlib axis where to plot. If None figure and axis will be created - [out] ax: matplotlib axis where to plot Plots the trajectory represented by a decision vector x on the 3d axis ax Example:: ax = prob.plot(x) """ import matplotlib as mpl from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from pykep.orbit_plots import plot_planet, plot_lambert, plot_kepler if ax is None: mpl.rcParams['legend.fontsize'] = 10 fig = plt.figure() axis = fig.gca(projection='3d') else: axis = ax axis.scatter(0, 0, 0, color='y') # 1 - we 'decode' the chromosome recording the various times of flight # (days) in the list T and the cartesian components of vinf T, Vinfx, Vinfy, Vinfz = self._decode_times_and_vinf(x) # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.__n_legs + 1)) r_P = list([None] * (self.__n_legs + 1)) v_P = list([None] * (self.__n_legs + 1)) DV = list([None] * (self.__n_legs + 1)) for i, planet in enumerate(self.seq): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = planet.eph(t_P[i]) plot_planet(planet, t0=t_P[i], color=( 0.8, 0.6, 0.8), legend=True, units=AU, ax=axis) # 3 - We start with the first leg v0 = [a + b for a, b in zip(v_P[0], [Vinfx, Vinfy, Vinfz])] r, v = propagate_lagrangian( r_P[0], v0, x[5] * T[0] * DAY2SEC, self.common_mu) plot_kepler(r_P[0], v0, x[5] * T[0] * DAY2SEC, self.common_mu, N=100, color='b', legend=False, units=AU, ax=axis) # Lambert arc to reach seq[1] dt = (1 - x[5]) * T[0] * DAY2SEC l = lambert_problem(r, r_P[1], dt, self.common_mu, False, False) plot_lambert(l, sol=0, color='r', legend=False, units=AU, ax=axis) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # First DSM occuring at time nu1*T1 DV[0] = norm([a - b for a, b in zip(v_beg_l, v)]) # 4 - And we proceed with each successive leg for i in range(1, self.__n_legs): # Fly-by v_out = fb_prop(v_end_l, v_P[i], x[ 8 + (i - 1) * 4] * self.seq[i].radius, x[7 + (i - 1) * 4], self.seq[i].mu_self) # s/c propagation before the DSM r, v = propagate_lagrangian( r_P[i], v_out, x[9 + (i - 1) * 4] * T[i] * DAY2SEC, self.common_mu) plot_kepler(r_P[i], v_out, x[9 + (i - 1) * 4] * T[i] * DAY2SEC, self.common_mu, N=100, color='b', legend=False, units=AU, ax=axis) # Lambert arc to reach Earth during (1-nu2)*T2 (second segment) dt = (1 - x[9 + (i - 1) * 4]) * T[i] * DAY2SEC l = lambert_problem(r, r_P[i + 1], dt, self.common_mu, False, False) plot_lambert(l, sol=0, color='r', legend=False, units=AU, N=1000, ax=axis) v_end_l = l.get_v2()[0] v_beg_l = l.get_v1()[0] # DSM occuring at time nu2*T2 DV[i] = norm([a - b for a, b in zip(v_beg_l, v)]) plt.show() return axis
def __init__(self, start=jpl_lp('earth'), target=jpl_lp('venus'), N_max=3, tof=[20., 400.], vinf=[0., 4.], phase_free=True, multi_objective=False, t0=None): """ prob = pykep.trajopt.pl2pl_N_impulses(start=jpl_lp('earth'), target=jpl_lp('venus'), N_max=3, tof=[20., 400.], vinf=[0., 4.], phase_free=True, multi_objective=False, t0=None) Args: - start (``pykep.planet``): the starting planet - target (``pykep.planet``): the target planet - N_max (``int``): maximum number of impulses - tof (``list``): the box bounds [lower,upper] for the time of flight (days) - vinf (``list``): the box bounds [lower,upper] for each DV magnitude (km/sec) - phase_free (``bool``): when True, no randezvous condition are enforced and start and arrival anomalies will be free - multi_objective (``bool``): when True, a multi-objective problem is constructed with DV and time of flight as objectives - t0 (``list``): the box bounds on the launch window containing two pykep.epoch. This is not needed if phase_free is True. """ # Sanity checks # 1) all planets need to have the same mu_central_body if (start.mu_central_body != target.mu_central_body): raise ValueError( 'Starting and ending pykep.planet must have the same mu_central_body' ) # 2) Number of impulses must be at least 2 if N_max < 2: raise ValueError('Number of impulses N is less than 2') # 3) If phase_free is True, t0 does not make sense if (t0 is None and not phase_free): t0 = [epoch(0), epoch(1000)] if (t0 is not None and phase_free): raise ValueError('When phase_free is True no t0 can be specified') if (type(t0[0]) != type(epoch(0))): t0[0] = epoch(t0[0]) if (type(t0[1]) != type(epoch(0))): t0[1] = epoch(t0[1]) self.obj_dim = multi_objective + 1 # We then define all class data members self.start = start self.target = target self.N_max = N_max self.phase_free = phase_free self.multi_objective = multi_objective self.vinf = [s * 1000 for s in vinf] self.__common_mu = start.mu_central_body # And we compute the bounds if phase_free: self._lb = [ 0, tof[0] ] + [1e-3, 0.0, 0.0, vinf[0] * 1000] * (N_max - 2) + [1e-3] + [0] self._ub = [ 2 * start.compute_period(epoch(0)) * SEC2DAY, tof[1] ] + [1.0 - 1e-3, 1.0, 1.0, vinf[1] * 1000] * (N_max - 2) + [ 1.0 - 1e-3 ] + [2 * target.compute_period(epoch(0)) * SEC2DAY] else: self._lb = [t0[0].mjd2000, tof[0]] + \ [1e-3, 0.0, 0.0, vinf[0] * 1000] * (N_max - 2) + [1e-3] self._ub = [t0[1].mjd2000, tof[1]] + \ [1.0-1e-3, 1.0, 1.0, vinf[1] * 1000] * (N_max - 2) + [1.0-1e-3]
def _compute_constraints_impl(self, x): # 1 - We decode the chromosome extracting the time of flights T = list([0] * (self.__n_legs)) for i in range(self.__n_legs): T[i] = x[1 + i * 8] # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.__n_legs + 1)) r_P = list([None] * (self.__n_legs + 1)) v_P = list([None] * (self.__n_legs + 1)) for i, planet in enumerate(self.seq): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = self.seq[i].eph(t_P[i]) # 3 - We iterate through legs to compute mismatches and throttles # constraints ceq = list() cineq = list() m0 = self.__sc.mass for i in range(self.__n_legs): # First Leg v = [a + b for a, b in zip(v_P[i], x[(3 + i * 8):(6 + i * 8)])] x0 = sc_state(r_P[i], v, m0) v = [a + b for a, b in zip(v_P[i + 1], x[(6 + i * 8):(9 + i * 8)])] xe = sc_state(r_P[i + 1], v, x[2 + i * 8]) throttles = x[(1 + 8 * self.__n_legs + 3 * sum(self.__n_seg[:i])):( 1 + 8 * self.__n_legs + 3 * sum(self.__n_seg[:i]) + 3 * self.__n_seg[i])] self.__leg.set(t_P[i], x0, throttles, t_P[i + 1], xe) # update mass! m0 = x[2 + 8 * i] ceq.extend(self.__leg.mismatch_constraints()) cineq.extend(self.__leg.throttles_constraints()) # Adding the boundary constraints # departure v_dep_con = (x[3]**2 + x[4]**2 + x[5]**2 - self.__vinf_dep**2) / (EARTH_VELOCITY**2) # arrival v_arr_con = (x[6 + (self.__n_legs - 1) * 8]**2 + x[7 + (self.__n_legs - 1) * 8] **2 + x[8 + (self.__n_legs - 1) * 8]**2 - self.__vinf_arr**2) / (EARTH_VELOCITY**2) cineq.append(v_dep_con * 100) cineq.append(v_arr_con * 100) # We add the fly-by constraints for i in range(self.__n_legs - 1): DV_eq, alpha_ineq = fb_con(x[6 + i * 8:9 + i * 8], x[11 + i * 8:14 + i * 8], self.seq[i + 1]) ceq.append(DV_eq / (EARTH_VELOCITY**2)) cineq.append(alpha_ineq) # Making the mismatches non dimensional for i in range(self.__n_legs): ceq[0 + i * 7] /= AU ceq[1 + i * 7] /= AU ceq[2 + i * 7] /= AU ceq[3 + i * 7] /= EARTH_VELOCITY ceq[4 + i * 7] /= EARTH_VELOCITY ceq[5 + i * 7] /= EARTH_VELOCITY ceq[6 + i * 7] /= self.__sc.mass # We assemble the constraint vector retval = list() retval.extend(ceq) retval.extend(cineq) return retval