def pretty(self, x): """ prob.pretty(x) - x: encoded trajectory Prints human readable information on the trajectory represented by the decision vector x """ n_seg = self.__n_seg m_i = self.__sc.mass t0 = x[0] T = x[1] m_f = x[2] thrusts = [np.linalg.norm(x[3 + 3 * i : 6 + 3 * i]) for i in range(n_seg)] tf = t0 + T mP = m_i - m_f deltaV = self.__sc.isp * G0 * np.log(m_i / m_f) dt = np.append(self.__fwd_dt, self.__bwd_dt) * T / DAY2SEC time_thrusts_on = sum(dt[i] for i in range(len(thrusts)) if thrusts[i] > 0.1) print("Departure:", epoch(t0), "(", t0, "mjd2000 )") print("Time of flight:", T, "days") print("Arrival:", epoch(tf), "(", tf, "mjd2000 )") print("Delta-v:", deltaV, "m/s") print("Propellant consumption:", mP, "kg") print("Thrust-on time:", time_thrusts_on, "days")
def double_segments(self,x): """ new_prob, new_x = prob.double_segments(self,x) - x: encoded trajectory Returns the decision vector encoding a low trust trajectory having double the number of segments with respect to x and a 'similar' throttle history. In case high fidelity is True, and x is a feasible trajectory, the returned decision vector also encodes a feasible trajectory that can be further optimized Returns the problem and the decision vector encoding a low-thrust trajectory having double the number of segments with respect to x and the same throttle history. If x is a feasible trajectory, the new chromosome is "almost" feasible, due to the new refined Earth gravity that is now different in the 2 halves of each segment). """ new_x = list(x[:3]) for i in range(self.__n_seg): new_x.extend(x[3 + 3 * i : 6 + 3 * i] * 2) new_prob = earth_gravity( target = self.target, n_seg = 2 * self.__n_seg, grid_type = self.__grid_type, t0 = [epoch(self.lb[0]), epoch(self.ub[0])], tof = [self.lb[1], self.ub[1]], m0 = self.__sc.mass, Tmax = self.__sc.thrust, Isp = self.__sc.isp, earth_gravity = self.__earth_gravity, start = self.__start ) return new_prob, new_x
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 __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 (years) - 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 dim = 7 + (self.__n_legs - 1) * 4 obj_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_1dsm, self).__init__(dim, 0, obj_dim, 0, 0, 0) # 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 # And we compute the bounds lb = [t0[0].mjd2000, tof[0] * 365.25] + [ 0.0, 0.0, vinf[0] * 1000, 1e-5, 1e-5 ] + [-2 * pi, 1.1, 1e-5, 1e-5] * (self.__n_legs - 1) ub = [t0[1].mjd2000, tof[1] * 365.25] + [ 1.0, 1.0, vinf[1] * 1000, 1.0 - 1e-5, 1.0 - 1e-5 ] + [2 * pi, 30.0, 1.0 - 1e-5, 1.0 - 1e-5] * (self.__n_legs - 1) # Accounting that each planet has a different safe radius...... for i, pl in enumerate(seq[1:-1]): lb[8 + 4 * i] = pl.safe_radius / pl.radius # And we set them self.set_bounds(lb, ub)
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], mga_sol=None, add_vinf_dep=False, add_vinf_arr=True, multi_objective=False, avoid=[]): """ 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 (years) - 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) """ # 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) dim = 7 + (len(seq) - 2) * 4 obj_dim = multi_objective + 1 super(mga_1dsm_transx, self).__init__(seq, dim, obj_dim, avoid) self.__add_vinf_dep = add_vinf_dep self.__add_vinf_arr = add_vinf_arr # And we compute the bounds lb = [t0[0].mjd2000, tof[0] * 365.25] + [ 0.0, 0.0, vinf[0] * 1000, 1e-5, 1e-5 ] + [-2 * pi, 1.1, 1e-5, 1e-5] * (self.n_legs - 1) ub = [t0[1].mjd2000, tof[1] * 365.25] + [ 1.0, 1.0, vinf[1] * 1000, 1.0 - 1e-5, 1.0 - 1e-5 ] + [2 * pi, 30.0, 1.0 - 1e-5, 1.0 - 1e-5] * (self.n_legs - 1) self.__mga_sol = mga_sol # Accounting that each planet has a different safe radius...... for i, pl in enumerate(seq[1:-1]): lb[8 + 4 * i] = pl.safe_radius / pl.radius # And we set them self.set_bounds(lb, ub)
def _objfun_impl(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 = sum(x[5::4]) if self.f_dimension == 1: return (DV1 + DV2 + DV_others,) else: return (DV1 + DV2 + DV_others, x[1])
def _objfun_impl(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 = sum(x[5::4]) if self.f_dimension == 1: return (DV1 + DV2 + DV_others, ) else: return (DV1 + DV2 + DV_others, x[1])
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], mga_sol = None, add_vinf_dep=False, add_vinf_arr=True, multi_objective=False, avoid = []): """ 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 (years) - 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) """ # 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) dim = 7 + (len(seq) - 2) * 4 obj_dim = multi_objective + 1 super(mga_1dsm_transx, self).__init__(seq, dim, obj_dim, avoid) self.__add_vinf_dep = add_vinf_dep self.__add_vinf_arr = add_vinf_arr # And we compute the bounds lb = [t0[0].mjd2000, tof[0] * 365.25] + [0.0, 0.0, vinf[0] * 1000, 1e-5, 1e-5] + [-2 * pi, 1.1, 1e-5, 1e-5] * (self.n_legs - 1) ub = [t0[1].mjd2000, tof[1] * 365.25] + [1.0, 1.0, vinf[1] * 1000, 1.0 - 1e-5, 1.0 - 1e-5] + [2 * pi, 30.0, 1.0 - 1e-5, 1.0 - 1e-5] * (self.n_legs - 1) self.__mga_sol = mga_sol # Accounting that each planet has a different safe radius...... for i, pl in enumerate(seq[1:-1]): lb[8 + 4 * i] = pl.safe_radius / pl.radius # And we set them self.set_bounds(lb, ub)
def _objfun_impl(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.f_dimension == 1: return (sum(DV), ) else: return (sum(DV), sum(T))
def _objfun_impl(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.f_dimension == 1: return (sum(DV),) else: return (sum(DV), sum(T))
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 (years) - 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 dim = 7 + (self.__n_legs - 1) * 4 obj_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_1dsm, self).__init__(dim, 0, obj_dim, 0, 0, 0) # 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 # And we compute the bounds lb = [t0[0].mjd2000, tof[0] * 365.25] + [0.0, 0.0, vinf[0] * 1000, 1e-5, 1e-5] + [-2 * pi, 1.1, 1e-5, 1e-5] * (self.__n_legs - 1) ub = [t0[1].mjd2000, tof[1] * 365.25] + [1.0, 1.0, vinf[1] * 1000, 1.0 - 1e-5, 1.0 - 1e-5] + [2 * pi, 30.0, 1.0 - 1e-5, 1.0 - 1e-5] * (self.__n_legs - 1) # Accounting that each planet has a different safe radius...... for i, pl in enumerate(seq[1:-1]): lb[8 + 4 * i] = pl.safe_radius / pl.radius # And we set them self.set_bounds(lb, ub)
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_trajectory(self, x, units=AU, plot_segments=True, ax=None): """ ax = prob.plot_trajectory(self, x, units=AU, plot_segments=True, ax=None) - x: encoded trajectory - units: the length unit to be used in the plot - plot_segments: when true plots also the segments boundaries - [out] ax: matplotlib axis where to plot Plots the trajectory """ import matplotlib as mpl import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from PyKEP.orbit_plots import plot_planet, plot_taylor # 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 n_seg = self.__n_seg fwd_seg = self.__fwd_seg bwd_seg = self.__bwd_seg t0 = x[0] T = x[1] isp = self.__sc.isp veff = isp * G0 fwd_grid = t0 + T * self.__fwd_grid # days bwd_grid = t0 + T * self.__bwd_grid # days throttles = [x[3 + 3 * i : 6 + 3 * i] for i in range(n_seg)] alphas = [min(1., np.linalg.norm(t)) for t in throttles] times = np.concatenate((fwd_grid, bwd_grid)) rfwd, rbwd, vfwd, vbwd, mfwd, mbwd, ufwd, ubwd, fwd_dt, bwd_dt = self._propagate(x) # Plotting the Sun, the Earth and the target axis.scatter([0], [0], [0], color='y') plot_planet(self.__earth, epoch(t0), units=units, legend=True, color=(0.7, 0.7, 1), ax=axis) plot_planet(self.target, epoch(t0 + T), units=units, legend=True, color=(0.7, 0.7, 1), ax=axis) # Forward propagation xfwd = [0.0] * (fwd_seg + 1) yfwd = [0.0] * (fwd_seg + 1) zfwd = [0.0] * (fwd_seg + 1) xfwd[0] = rfwd[0][0] / units yfwd[0] = rfwd[0][1] / units zfwd[0] = rfwd[0][2] / units for i in range(fwd_seg): plot_taylor(rfwd[i], vfwd[i], mfwd[i], ufwd[i], fwd_dt[i], MU_SUN, veff, N=10, units=units, color=(alphas[i], 0, 1-alphas[i]), ax=axis) xfwd[i+1] = rfwd[i+1][0] / units yfwd[i+1] = rfwd[i+1][1] / units zfwd[i+1] = rfwd[i+1][2] / units if plot_segments: axis.scatter(xfwd[:-1], yfwd[:-1], zfwd[:-1], label='nodes', marker='o', s=1) # Backward propagation xbwd = [0.0] * (bwd_seg + 1) ybwd = [0.0] * (bwd_seg + 1) zbwd = [0.0] * (bwd_seg + 1) xbwd[-1] = rbwd[-1][0] / units ybwd[-1] = rbwd[-1][1] / units zbwd[-1] = rbwd[-1][2] / units for i in range(bwd_seg): plot_taylor(rbwd[-i-1], vbwd[-i-1], mbwd[-i-1], ubwd[-i-1], -bwd_dt[-i-1], MU_SUN, veff, N=10, units=units, color=(alphas[-i-1], 0, 1-alphas[-i-1]), ax=axis) xbwd[-i-2] = rbwd[-i-2][0] / units ybwd[-i-2] = rbwd[-i-2][1] / units zbwd[-i-2] = rbwd[-i-2][2] / units if plot_segments: axis.scatter(xbwd[1:], ybwd[1:], zbwd[1:], marker='o', s=1) if ax is None: # show only if axis is not set plt.show() return axis
def calc_objective(self, x, should_print=False): # 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) Vinf = [Vinfx, Vinfy, Vinfz] # 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]) if should_print: self.print_time_info(self.seq, t_P) if self.__add_vinf_dep: DV[0] += self.burn_cost(self.seq[0], Vinf) if should_print: self.print_escape(self.seq[0], v_P[0], r_P[0], Vinf, t_P[0].mjd) # 3 - We start with the first leg v0 = [a + b for a, b in zip(v_P[0], Vinf)] 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_beg_l = l.get_v1()[0] v_end_l = l.get_v2()[0] # First DSM occuring at time nu1*T1 deltaV = [a - b for a, b in zip(v_beg_l, v)] DV[0] += norm(deltaV) if should_print: self.print_dsm(v, r, deltaV, v_beg_l, t_P[0].mjd + dt / DAY2SEC) # 4 - And we proceed with each successive leg for i in range(1, self.n_legs): # Fly-by radius = x[8 + (i - 1) * 4] * self.seq[i].radius beta = x[7 + (i - 1) * 4] v_out = fb_prop(v_end_l, v_P[i], radius, beta, self.seq[i].mu_self) if should_print: v_rel_in = [a - b for a, b in zip(v_end_l, v_P[i])] v_rel_out = [a - b for a, b in zip(v_out, v_P[i])] self.print_flyby(self.seq[i], v_P[i], r_P[i], v_rel_in, v_rel_out, t_P[i].mjd) # 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 deltaV = [a - b for a, b in zip(v_beg_l, v)] DV[i] = norm(deltaV) if should_print: self.print_dsm(v, r, deltaV, v_beg_l, t_P[i].mjd + dt / DAY2SEC) # Last Delta-v if self.__add_vinf_arr: Vexc_arr = [a - b for a, b in zip(v_end_l, v_P[-1])] DV[-1] = self.burn_cost(self.seq[-1], Vexc_arr) if should_print: self.print_arrival(self.seq[-1], Vexc_arr, t_P[-1].mjd) fuelCost = sum(DV) if should_print: print("Total fuel cost: %10.3f m/s" % round(fuelCost, 3)) if self.f_dimension == 1: return (fuelCost, ) else: return (fuelCost, sum(T))
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 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 _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 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 __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, 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 point(self, x, filename): """ prob.point(decision_vector, filename) This method will save all the relevant simulation data for the trajectory described by decision_vector in json form in the file 'filename.json'. The data generated can then be used by the postprocessing toolbox (from this fork of PyKEP : PyKEP/postProcessingToolbox/), without having to re-create a prob abject. Indeed, if the problem definition is changed, the decision_vector will not mean anything: it only encode a trajectory for a very specific problem. When tens or hundreds of trajectories are generated, and then compared, keeping only the decision vector is not a good idea, if the parameters of the problems have been tweaked to realize those tens or hundreds of different simulations. The data stored in the file can be imported using import.json from the json package. The description of the stored data has still to be described in detail // TODO : description of the content of the .json file Those files are to be given as parameters for the postprocessing toolbox. """ from PyKEP import epoch, AU from PyKEP.sims_flanagan import sc_state from PyKEP.orbit_plots import plot_planet, plot_sf_leg from PyKEP.orbit_plots import point_sf_leg, point_kepler, point_taylor, point_planet from scipy.linalg import norm from math import sqrt import csv import pprint from PyKEP.trajopt.motor import getObjectMotor from PyKEP.trajopt.spacecraft import getObjectSpacecraft XYZ = [] xx = [] yy = [] zz = [] tt = [] mm = [] x_bounds = [] y_bounds = [] z_bounds = [] seq1 = self.seq1 seq2 = self.seq2 data = {} # 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[3 + i * 8] # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.__n_legs + 2)) r_P = list([None] * (self.__n_legs + 2)) v_P = list([None] * (self.__n_legs + 2)) for i, planet in enumerate(self.seq1): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = self.seq1[i].eph(t_P[i]) for i, planet in enumerate(self.seq2): t_P[i + len(seq1)] = epoch(x[0] + sum(T[0:i + len(seq1) - 1]) + x[1]) r_P[i + len(seq1)], v_P[i + len(seq1)] = self.seq2[i].eph( t_P[i + len(seq1)]) # 3 - We iterate through legs to compute mismatches and throttles constraints ceq = list() cineq = list() m0 = x[2] data['legs'] = [] massesAlongTheWay = [m0] self.__leg.set_spacecraft(self.__spacecrafts[0]) sc = self.__spacecrafts[0] for i in range(len(self.seq1) - 1): # First Leg v = [a + b for a, b in zip(v_P[i], x[(5 + i * 8):(8 + i * 8)])] x0 = sc_state(r_P[i], v, m0) v = [ a + b for a, b in zip(v_P[i + 1], x[(8 + i * 8):(11 + i * 8)]) ] xe = sc_state(r_P[i + 1], v, x[4 + i * 8]) throttles = x[(3 + 8 * self.__n_legs + 3 * sum(self.__n_seg[:i])):( 3 + 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! minit = m0 m0 = x[4 + 8 * i] massesAlongTheWay = massesAlongTheWay + [m0] xi, yi, zi, mi, ti, Txi, Tyi, Tzi, Tmaxi, x_boundsi, y_boundsi, z_boundsi = point_sf_leg( self.__leg, units=AU, N=10) xx = xx + xi yy = yy + yi zz = zz + zi ti = [e + t_P[i].jd for e in ti] #for k in range(len(Txi)): # print norm([Tyi[k],Txi[k],Tzi[k]]) data['legs'] = data['legs'] + [{ 'x': [e for e in xi], 'y': [e for e in yi], 'z': [e for e in zi], 'm': [e for e in mi], 't': [e for e in ti], 'P_tot': [sc.get_totalPower(norm(e)) for e in zip(xi, yi, zi)], 'P_engine': [ sc.DutyCycle * sc.get_powerThrust(norm(e[0:3]) * e[3] / sc.DutyCycle) for e in zip(Txi, Tyi, Tzi, Tmaxi) ], 'Tx': [e for e in Txi], 'Ty': [e for e in Tyi], 'Tz': [e for e in Tzi], 'Tmax': [e for e in Tmaxi], 'mi': minit, 'mf': m0, 'planet1': { 'name': seq1[i].name, 'date': t_P[i].jd }, 'planet2': { 'name': seq1[i + 1].name, 'date': t_P[i + 1].jd } }] XYZ = XYZ + [xi] + [yi] + [zi] x_bounds = x_bounds + x_boundsi y_bounds = y_bounds + y_boundsi z_bounds = z_bounds + z_boundsi #outbound m0 = m0 - self.__dm # mass lost around ceres massesAlongTheWay = massesAlongTheWay + [m0] self.__leg.set_spacecraft(self.__spacecrafts[1]) sc = self.__spacecrafts[1] for i in range( len(self.seq1) - 1, len(self.seq1) + len(self.seq2) - 2): # First Leg v = [a + b for a, b in zip(v_P[i + 1], x[(5 + i * 8):(8 + i * 8)])] x0 = sc_state(r_P[i + 1], v, m0) v = [ a + b for a, b in zip(v_P[i + 2], x[(8 + i * 8):(11 + i * 8)]) ] xe = sc_state(r_P[i + 2], v, x[4 + i * 8]) throttles = x[(3 + 8 * self.__n_legs + 3 * sum(self.__n_seg[:i])):( 3 + 8 * self.__n_legs + 3 * sum(self.__n_seg[:i]) + 3 * self.__n_seg[i])] self.__leg.set(t_P[i + 1], x0, throttles, t_P[i + 2], xe) # update mass! minit = m0 m0 = x[4 + 8 * i] massesAlongTheWay = massesAlongTheWay + [m0] xi, yi, zi, mi, ti, Txi, Tyi, Tzi, Tmaxi, x_boundsi, y_boundsi, z_boundsi = point_sf_leg( self.__leg, units=AU, N=10) xx = xx + xi yy = yy + yi zz = zz + zi ti = [e + t_P[i + 1].jd for e in ti] #for k in range(len(Txi)): # print norm([Tyi[k],Txi[k],Tzi[k]]) data['legs'] = data['legs'] + [{ 'x': [e for e in xi], 'y': [e for e in yi], 'z': [e for e in zi], 'm': [e for e in mi], 't': [e for e in ti], 'P_tot': [sc.get_totalPower(norm(e)) for e in zip(xi, yi, zi)], 'P_engine': [ sc.DutyCycle * sc.get_powerThrust(norm(e[0:3]) * e[3] / sc.DutyCycle) for e in zip(Txi, Tyi, Tzi, Tmaxi) ], 'Tx': [e for e in Txi], 'Ty': [e for e in Tyi], 'Tz': [e for e in Tzi], 'Tmax': [e for e in Tmaxi], 'mi': minit, 'mf': m0, 'planet1': { 'name': seq2[i - len(seq1) - 2].name, 'date': t_P[i + 1].jd }, 'planet2': { 'name': seq2[i + 1 - len(seq1) - 2].name, 'date': t_P[i + 2].jd } }] XYZ = XYZ + [xi] + [yi] + [zi] x_bounds = x_bounds + x_boundsi y_bounds = y_bounds + y_boundsi z_bounds = z_bounds + z_boundsi XYZplanet = [] data['planetOrbits'] = [] data['planetPosition'] = [] listAlreadySaved = [] for i, planet in enumerate(self.seq1): xp, yp, zp = point_planet(planet, t_P[i], units=AU) #Save orbit if it has not been done already if (seq1[i].name not in listAlreadySaved): listAlreadySaved.append(seq1[i].name) data['planetOrbits'].append({ 'name': seq1[i].name, 'x': [e for e in xp], 'y': [e for e in yp], 'z': [e for e in zp] }) data['planetPosition'].append({ 'name': seq1[i].name, 'x': xp[0], 'y': yp[0], 'z': zp[0], 'dateS': pprint.pformat(t_P[i])[0:20], 'date': t_P[i].jd, 'scmass': massesAlongTheWay[i] }) for i, planet in enumerate(self.seq2): xp, yp, zp = point_planet(planet, t_P[i + len(seq1)], units=AU) #Save orbit if it has not been done already if (seq2[i].name not in listAlreadySaved): listAlreadySaved.append(seq2[i].name) data['planetOrbits'].append({ 'name': seq2[i].name, 'x': [e for e in xp], 'y': [e for e in yp], 'z': [e for e in zp] }) data['planetPosition'].append({ 'name': seq2[i].name, 'x': xp[0], 'y': yp[0], 'z': zp[0], 'dateS': pprint.pformat(t_P[i + len(seq1)])[0:20], 'date': t_P[i + len(seq1)].jd, 'scmass': massesAlongTheWay[i + 3] }) #Calculate vinf velocities v_dep_1 = sqrt(x[5]**2 + x[6]**2 + x[7]**2) v_arr_1 = sqrt(x[8 + (len(seq1) - 2) * 8]**2 + x[9 + (len(seq1) - 2) * 8]**2 + x[10 + (len(seq1) - 2) * 8]**2) v_dep_2 = sqrt(x[5 + (len(seq1) - 1) * 8]**2 + x[6 + (len(seq1) - 1) * 8]**2 + x[7 + (len(seq1) - 1) * 8]**2) v_arr_2 = sqrt(x[8 + (len(seq1) - 1 + len(seq2) - 2) * 8]**2 + x[9 + (len(seq1) - 1 + len(seq2) - 2)]**2 + x[10 * (len(seq1) - 1 + len(seq2) - 2)]**2) #Logging all trajectory parameters data['traj'] = {} data['traj']['high_fidelity'] = self.__leg.high_fidelity data['traj']['solar_powered'] = self.__leg.solar_powered data['traj']['outbound'] = [[seq1[i].name, t_P[i].jd] for i in range(len(seq1))] data['traj']['inbound'] = [[seq2[i].name, t_P[i + len(seq1)].jd] for i in range(len(seq2))] data['traj']['vinf'] = { 'v_dep1': v_dep_1, 'v_dep2': v_dep_2, 'v_arr1': v_arr_1, 'v_arr2': v_arr_2 } data['traj']['spacecrafts'] = [ getObjectSpacecraft(self.__spacecrafts[0]), getObjectSpacecraft(self.__spacecrafts[1]) ] data['traj']['duty_cycle'] = [ self.__spacecrafts[0].DutyCycle, self.__spacecrafts[1].DutyCycle, ] data['traj']['dm'] = self.__dm data['traj']['mi'] = x[2] data['traj']['mf'] = self.__mf data['traj']['decision_vector'] = x data['traj']['dt'] = x[1] data['traj']['t_launch'] = t_P[0].jd data['traj']['t_launchS'] = pprint.pformat(t_P[0])[0:20] data['traj']['t_return'] = t_P[-1].jd data['traj']['t_returnS'] = pprint.pformat(t_P[-1])[0:20] data['traj']['duration'] = t_P[-1].jd - t_P[0].jd import json with open(filename, 'w') as outfile: json.dump(data, outfile) # File structure: # Number of legs, length points, number of planets to plot, length points # Legs trajectories # Planets trajectories # Planets names #with open('test1.csv', 'w') as csvfile: # writer = csv.writer(csvfile) # #writer.writerow([self.__n_legs, len(XYZ[0]), len(list_planet),len(XYZplanet[0])]) # for i in range(len(XYZ)): # writer.writerow(XYZ[i]) # for i in range(len(XYZplanet)): # writer.writerow(XYZplanet[i]) # #writer.writerow(list_planet) return xx, yy, zz, x_bounds, y_bounds, z_bounds, data
def plot_dists_thrust(self, x, ax=None): """ ax = prob.plot_dists_thrust(self, x, ax=None) - x: encoded trajectory - [out] ax: matplotlib axis where to plot Plots the distance of the spacecraft from the Earth/Sun and the thrust profile """ import matplotlib as mpl import matplotlib.pyplot as plt # Creating the axis if necessary if ax is None: mpl.rcParams['legend.fontsize'] = 10 fig = plt.figure() axis = fig.add_subplot(111) else: axis = ax n_seg = self.__n_seg fwd_seg = self.__fwd_seg bwd_seg = self.__bwd_seg t0 = x[0] T = x[1] fwd_grid = t0 + T * self.__fwd_grid # days bwd_grid = t0 + T * self.__bwd_grid # days throttles = [x[3 + 3 * i : 6 + 3 * i] for i in range(n_seg)] thrusts = [min(1., np.linalg.norm(t)) for t in throttles] thrusts.append(thrusts[-1]) # duplicate the last for plotting dist_earth = [0.0] * (n_seg + 2) # distances spacecraft - Earth dist_sun = [0.0] * (n_seg + 2) # distances spacecraft - Sun times = np.concatenate((fwd_grid, bwd_grid)) rfwd, rbwd, vfwd, vbwd, mfwd, mbwd, ufwd, ubwd, fwd_dt, bwd_dt = self._propagate(x) # Forward propagation xfwd = [0.0] * (fwd_seg + 1) yfwd = [0.0] * (fwd_seg + 1) zfwd = [0.0] * (fwd_seg + 1) xfwd[0] = rfwd[0][0] / AU yfwd[0] = rfwd[0][1] / AU zfwd[0] = rfwd[0][2] / AU r_E = [ri/AU for ri in self.__earth.eph(epoch(fwd_grid[0]))[0]] dist_earth[0] = np.linalg.norm([r_E[0]-xfwd[0], r_E[1]-yfwd[0], r_E[2]-zfwd[0]]) dist_sun[0] = np.linalg.norm([xfwd[0], yfwd[0], zfwd[0]]) for i in range(fwd_seg): xfwd[i+1] = rfwd[i+1][0] / AU yfwd[i+1] = rfwd[i+1][1] / AU zfwd[i+1] = rfwd[i+1][2] / AU r_E = [ri/AU for ri in self.__earth.eph(epoch(fwd_grid[i+1]))[0]] dist_earth[i+1] = np.linalg.norm([r_E[0]-xfwd[i+1], r_E[1]-yfwd[i+1], r_E[2]-zfwd[i+1]]) dist_sun[i+1] = np.linalg.norm([xfwd[i+1], yfwd[i+1], zfwd[i+1]]) # Backward propagation xbwd = [0.0] * (bwd_seg + 1) ybwd = [0.0] * (bwd_seg + 1) zbwd = [0.0] * (bwd_seg + 1) xbwd[-1] = rbwd[-1][0] / AU ybwd[-1] = rbwd[-1][1] / AU zbwd[-1] = rbwd[-1][2] / AU r_E = [ri/AU for ri in self.__earth.eph(epoch(bwd_grid[-1]))[0]] dist_earth[-1] = np.linalg.norm([r_E[0]-xbwd[-1], r_E[1]-ybwd[-1], r_E[2]-zbwd[-1]]) dist_sun[-1] = np.linalg.norm([xbwd[-1], ybwd[-1], zbwd[-1]]) for i in range(bwd_seg): xbwd[-i-2] = rbwd[-i-2][0] / AU ybwd[-i-2] = rbwd[-i-2][1] / AU zbwd[-i-2] = rbwd[-i-2][2] / AU r_E = [ri/AU for ri in self.__earth.eph(epoch(bwd_grid[-i-2]))[0]] dist_earth[-i-2] = np.linalg.norm([r_E[0]-xbwd[-i-2], r_E[1]-ybwd[-i-2], r_E[2]-zbwd[-i-2]]) dist_sun[-i-2] = np.linalg.norm([xbwd[-i-2], ybwd[-i-2], zbwd[-i-2]]) axis.set_xlabel("t [mjd2000]") # Plot Earth distance axis.plot(times,dist_earth, c = 'b', label = "sc-Earth") # Plot Sun distance axis.plot(times,dist_sun, c = 'y', label = "sc-Sun") axis.set_ylabel("distance [AU]", color = 'k') axis.set_ylim(bottom = 0.) axis.tick_params('y', colors = 'k') axis.legend(loc=2) # draw threshold where Earth gravity equals 0.1*Tmax axis.axhline(y = np.sqrt(MU_EARTH * self.__sc.mass / (self.__sc.thrust * 0.1)) / AU, c = 'g', ls = ":", lw = 1) # Plot thrust profile axis = axis.twinx() axis.step(np.concatenate((fwd_grid, bwd_grid[1:])), thrusts, where = "post", c = 'r', linestyle = '--') axis.set_ylabel("T/Tmax",color='r') axis.tick_params('y',colors='r') axis.set_xlim([times[0],times[-1]]) axis.set_ylim([0,1.2]) if ax is None: # show only if axis is not set plt.show() return axis
def __init__(self, target = jpl_lp('mars'), n_seg = 20, grid_type = "uniform", t0 = [epoch(0), epoch(1000)], tof = [200, 500], m0 = 50.0, Tmax = 0.001, Isp = 2000.0, earth_gravity = False, start = "earth"): """ prob = lt_margo(target = jpl_lp('mars'), n_seg = 20, grid_type = "uniform", t0 = [epoch(0), epoch(1000)], tof = [200, 500], m0 = 50.0, Tmax = 0.001, Isp = 2000.0, earth_gravity = False, start = "earth") - target: target PyKEP.planet - n_seg: number of segments - grid_type: "uniform" for uniform segments, "nonuniform" for a denser grid in the first part of the trajectory - t0: list of two epochs defining the launch window - tof: list of two floats definind the minimum and maximum time of flight (days) - m0: initial mass of the spacecraft - Tmax: maximum thrust - Isp: engine specific impulse - earth_gravity: boolean specifying whether to take Earth's gravity into account - start: starting point ("earth", "l1", or "l2"). .. note:: L1 and L2 are approximated as the points on the line connecting the Sun and the Earth at a distance of, respectively, 0.99 and 1.01 AU from the Sun. .. note:: If the Earth's gravity is enabled, the starting point cannot be the Earth """ # Various checks if start not in ["earth", "l1", "l2"]: raise ValueError("start must be either 'earth', 'l1' or 'l2'") if grid_type not in ["uniform", "nonuniform"]: raise ValueError("grid_type must be either 'uniform' or 'nonuniform'") if earth_gravity and start == "earth": raise ValueError("If Earth gravity is enabled the starting point cannot be the Earth") # 1a) The decision vector length ([t0, tof, mf, [Tx,Ty,Tz] * n_seg]) dim = 3 + n_seg * 3 # 1b) The total number of constraints (mismatch + throttles) c_dim = 7 + n_seg # 1c) The number of inequality constraints (throttles) c_ineq_dim = n_seg # First we call the constructor for the base PyGMO problem super(lt_margo, self).__init__(dim, 0, 1, c_dim, c_ineq_dim, 1e-4) # 2) We then define some class data members # public: self.target = target # private: self.__n_seg = n_seg self.__grid_type = grid_type # gridding function (must be 0 in 0, 1 in 1, and strictly increasing) self.__sc = spacecraft(m0, Tmax, Isp) self.__earth = jpl_lp('earth') self.__earth_gravity = earth_gravity self.__start = start # grid construction if grid_type == "uniform": grid = np.array([i/n_seg for i in range(n_seg + 1)]) elif grid_type == "nonuniform": grid_f = lambda x : x**2 if x<0.5 else 0.25+1.5*(x-0.5) # quadratic in [0,0.5], linear in [0.5,1] grid = np.array([grid_f(i/n_seg) for i in range(n_seg + 1)]) fwd_seg = np.searchsorted(grid, 0.5, side='right') # index corresponding to the middle of the transfer bwd_seg = n_seg - fwd_seg fwd_grid = grid[:fwd_seg + 1] bwd_grid = grid[fwd_seg:] self.__fwd_seg = fwd_seg self.__fwd_grid = fwd_grid self.__fwd_dt = np.array([(fwd_grid[i+1] - fwd_grid[i]) for i in range(fwd_seg)]) * DAY2SEC self.__bwd_seg = bwd_seg self.__bwd_grid = bwd_grid self.__bwd_dt = np.array([(bwd_grid[i+1] - bwd_grid[i]) for i in range(bwd_seg)]) * DAY2SEC # 3) We compute the bounds lb = [t0[0].mjd2000] + [tof[0]] + [0] + [-1, -1, -1] * n_seg ub = [t0[1].mjd2000] + [tof[1]] + [m0] + [1, 1, 1] * n_seg # 4) And we set the bounds self.set_bounds(lb, ub)
def _compute_constraints_impl(self, x): seq1 = self.seq1 seq2 = self.seq2 # 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[3 + i * 8] # 2 - We compute the epochs and ephemerides of the planetary encounters t_P = list([None] * (self.__n_legs + 2)) r_P = list([None] * (self.__n_legs + 2)) v_P = list([None] * (self.__n_legs + 2)) for i, planet in enumerate(self.seq1): t_P[i] = epoch(x[0] + sum(T[0:i])) r_P[i], v_P[i] = self.seq1[i].eph(t_P[i]) for i, planet in enumerate(self.seq2): t_P[i + len(seq1)] = epoch(x[0] + sum(T[0:i + len(seq1) - 1]) + x[1]) r_P[i + len(seq1)], v_P[i + len(seq1)] = self.seq2[i].eph( t_P[i + len(seq1)]) # 3 - We iterate through legs to compute mismatches and throttles constraints ceq = list() cineq = list() m0 = x[2] #inbound self.__leg.set_spacecraft(self.__spacecrafts[0]) for i in range(len(self.seq1) - 1): # First Leg v = [a + b for a, b in zip(v_P[i], x[(5 + i * 8):(8 + i * 8)])] x0 = sc_state(r_P[i], v, m0) v = [ a + b for a, b in zip(v_P[i + 1], x[(8 + i * 8):(11 + i * 8)]) ] xe = sc_state(r_P[i + 1], v, x[4 + i * 8]) throttles = x[(3 + 8 * self.__n_legs + 3 * sum(self.__n_seg[:i])):( 3 + 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[4 + 8 * i] ceq.extend(self.__leg.mismatch_constraints()) cineq.extend(self.__leg.throttles_constraints()) #outbound self.__leg.set_spacecraft(self.__spacecrafts[1]) m0 = m0 - self.__dm # mass lost around ceres for i in range( len(self.seq1) - 1, len(self.seq1) + len(self.seq2) - 2): # First Leg v = [a + b for a, b in zip(v_P[i + 1], x[(5 + i * 8):(8 + i * 8)])] x0 = sc_state(r_P[i + 1], v, m0) v = [ a + b for a, b in zip(v_P[i + 2], x[(8 + i * 8):(11 + i * 8)]) ] xe = sc_state(r_P[i + 2], v, x[4 + i * 8]) throttles = x[(3 + 8 * self.__n_legs + 3 * sum(self.__n_seg[:i])):( 3 + 8 * self.__n_legs + 3 * sum(self.__n_seg[:i]) + 3 * self.__n_seg[i])] self.__leg.set(t_P[i + 1], x0, throttles, t_P[i + 2], xe) # update mass! m0 = x[4 + 8 * i] ceq.extend(self.__leg.mismatch_constraints()) cineq.extend(self.__leg.throttles_constraints()) # Adding the boundary constraints # departure1 v_dep_con1 = (x[5]**2 + x[6]**2 + x[7]**2 - self.__vinf_dep1**2) / (EARTH_VELOCITY**2) # arrival1 v_arr_con1 = (x[8 + (len(seq1) - 2) * 8]**2 + x[9 + (len(seq1) - 2) * 8]**2 + x[10 + (len(seq1) - 2) * 8]**2 - self.__vinf_arr1**2) / (EARTH_VELOCITY**2) # departure2 v_dep_con2 = (x[5 + (len(seq1) - 1) * 8]**2 + x[6 + (len(seq1) - 1) * 8]**2 + x[7 + (len(seq1) - 1) * 8]**2 - self.__vinf_dep2**2) / (EARTH_VELOCITY**2) # arrival2 v_arr_con2 = (x[8 + (len(seq1) - 1 + len(seq2) - 2) * 8]**2 + x[9 + (len(seq1) - 1 + len(seq2) - 2)]**2 + x[10 * (len(seq1) - 1 + len(seq2) - 2)]**2 - self.__vinf_arr2**2) / (EARTH_VELOCITY**2) cineq.append(v_dep_con1 * 100) cineq.append(v_arr_con1 * 100) cineq.append(v_dep_con2 * 100) cineq.append(v_arr_con2 * 100) # We add the fly-by constraints for i in range(len(seq1) - 2): DV_eq, alpha_ineq = fb_con(x[8 + i * 8:11 + i * 8], x[13 + i * 8:16 + i * 8], self.seq1[i + 1]) ceq.append(DV_eq / (EARTH_VELOCITY**2)) cineq.append(alpha_ineq) for i in range(len(seq2) - 2): DV_eq, alpha_ineq = fb_con( x[8 + (i + len(seq1) - 1) * 8:11 + (i + len(seq1) - 1) * 8], x[13 + (i + len(seq1) - 1) * 8:16 + (i + len(seq1) - 1) * 8], self.seq2[i + 1]) ceq.append(DV_eq / (EARTH_VELOCITY**2)) cineq.append(alpha_ineq) #Adding the mass constraint mass_con = self.__mf - m0 ceq.append(mass_con / self.__mf) # 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.__mf # We assemble the constraint vector retval = list() retval.extend(ceq) retval.extend(cineq) return retval
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) - start: a PyKEP planet defining the starting orbit - target: a PyKEP planet defining the target orbit - N_max: maximum number of impulses - tof: a list containing the box bounds [lower,upper] for the time of flight (days) - vinf: a list containing the box bounds [lower,upper] for each DV magnitude (km/sec) - phase_free: when True, no randezvous condition is enforced and the final orbit will be reached at an optimal true anomaly - multi_objective: when True, a multi-objective problem is constructed with DV and time of flight as objectives - t0: launch window defined as a list of two epochs [epoch,epoch] """ # 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') # We compute the PyGMO problem dimensions dim = 2 + 4 * (N_max - 2) + 1 + phase_free obj_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(pl2pl_N_impulses, self).__init__(dim, 0, obj_dim, 0, 0, 0) # 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.__common_mu = start.mu_central_body # And we compute the bounds if phase_free: lb = [start.ref_epoch.mjd2000, tof[0] ] + [0.0, 0.0, 0.0, vinf[0] * 1000] * (N_max - 2) + [0.0] + [ target.ref_epoch.mjd2000 ] ub = [ start.ref_epoch.mjd2000 + 2 * start.period * SEC2DAY, tof[1] ] + [1.0, 1.0, 1.0, vinf[1] * 1000] * (N_max - 2) + [1.0] + [ target.ref_epoch.mjd2000 + 2 * target.period * SEC2DAY ] else: lb = [t0[0].mjd2000, tof[0] ] + [0.0, 0.0, 0.0, vinf[0] * 1000] * (N_max - 2) + [0.0] ub = [t0[1].mjd2000, tof[1] ] + [1.0, 1.0, 1.0, vinf[1] * 1000] * (N_max - 2) + [1.0] # And we set them 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 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=axis) plot_planet(self.target, t0=epoch(x[0] + x[1]), color=(0.8, 0.6, 0.8), legend=True, units=AU, ax=axis) # 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])] plot_kepler(rsc, vsc, T[i] * DAY2SEC, self.__common_mu, N=200, color='b', legend=False, units=AU, ax=axis) 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='r', legend=False, units=AU, ax=axis, N=200) plt.show() return axis
def _propagate(self, x): # 1 - We decode the chromosome t0 = x[0] T = x[1] m_f = x[2] # We extract the number of segments for forward and backward propagation n_seg = self.__n_seg fwd_seg = self.__fwd_seg bwd_seg = self.__bwd_seg # We extract information on the spacecraft m_i = self.__sc.mass max_thrust = self.__sc.thrust isp = self.__sc.isp veff = isp * G0 # And on the leg throttles = [x[3 + 3 * i : 6 + 3 * i] for i in range(n_seg)] # Return lists n_points_fwd = fwd_seg + 1 n_points_bwd = bwd_seg + 1 rfwd = [[0.0] * 3] * (n_points_fwd) vfwd = [[0.0] * 3] * (n_points_fwd) mfwd = [0.0] * (n_points_fwd) ufwd = [[0.0] * 3] * (fwd_seg) rbwd = [[0.0] * 3] * (n_points_bwd) vbwd = [[0.0] * 3] * (n_points_bwd) mbwd = [0.0] * (n_points_bwd) ubwd = [[0.0] * 3] * (bwd_seg) # 2 - We compute the initial and final epochs and ephemerides t_i = epoch(t0) r_i, v_i = self.__earth.eph(t_i) if self.__start == 'l1': r_i = [r * 0.99 for r in r_i] v_i = [v * 0.99 for v in v_i] elif self.__start == 'l2': r_i = [r * 1.01 for r in r_i] v_i = [v * 1.01 for v in v_i] t_f = epoch(t0 + T) r_f, v_f = self.target.eph(t_f) # 3 - Forward propagation fwd_grid = t0 + T * self.__fwd_grid # days fwd_dt = T * self.__fwd_dt # seconds # Initial conditions rfwd[0] = r_i vfwd[0] = v_i mfwd[0] = m_i # Propagate for i, t in enumerate(throttles[:fwd_seg]): ufwd[i] = [max_thrust * thr for thr in t] if self.__earth_gravity: r_E, v_E = self.__earth.eph(epoch(fwd_grid[i])) r_delta = [a - b for a,b in zip(r_E, rfwd[i])] r3 = sum([r**2 for r in r_delta])**(3/2) ufwd[i] = [ui + mfwd[i] * MU_EARTH / r3 * ri for ui,ri in zip(ufwd[i], r_delta)] rfwd[i+1], vfwd[i+1], mfwd[i+1] = propagate_taylor(rfwd[i],vfwd[i],mfwd[i],ufwd[i],fwd_dt[i],MU_SUN,veff,-10,-10) # 4 - Backward propagation bwd_grid = t0 + T * self.__bwd_grid # days bwd_dt = T * self.__bwd_dt # seconds # Final conditions rbwd[-1] = r_f vbwd[-1] = v_f mbwd[-1] = m_f # Propagate for i, t in enumerate(throttles[-1:-bwd_seg - 1:-1]): ubwd[-i-1] = [max_thrust * thr for thr in t] if self.__earth_gravity: r_E, v_E = self.__earth.eph(epoch(bwd_grid[-i-1])) r_delta = [a - b for a,b in zip(r_E, rbwd[-i-1])] r3 = sum([r**2 for r in r_delta])**(3/2) ubwd[-i-1] = [ui + mbwd[-i-1] * MU_EARTH / r3 * ri for ui,ri in zip(ubwd[-i-1], r_delta)] rbwd[-i-2], vbwd[-i-2], mbwd[-i-2] = propagate_taylor(rbwd[-i-1],vbwd[-i-1],mbwd[-i-1],ubwd[-i-1],-bwd_dt[-i-1],MU_SUN,veff,-10,-10) return rfwd, rbwd, vfwd, vbwd, mfwd, mbwd, ufwd, ubwd, fwd_dt, bwd_dt
def __init__( self, seq1=['earth', 'mars', 'ceres'], seq2=['ceres', 'mars', 'earth'], n_seg=[15] * 4, t0=[epoch(11000), epoch(13000)], tos=[200, 1000], # time of stay on planet tof=[[40, 900]] * 4, vinf_dep1=0.001, vinf_arr1=0.001, vinf_dep2=0.001, vinf_arr2=5, dm=400.0, mass_end=700.0, Tmax=0.4, Isp=3800.0, spacecrafts=None, fb_rel_vel=6, multi_objective=False, high_fidelity=True, solar_powered=True): """ 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. """ # We instanciate the sequences if seq1[-1] != seq2[0]: print 'Error : last planet of inbound and first planet of outbound must be the same' else: seq1 = self.make_sequence(seq1) seq2 = self.make_sequence(seq2) # 1) We compute the problem dimensions .... and call the base problem constructor self.__n_legs = len(seq1) + len(seq2) - 2 n_fb = self.__n_legs - 2 # number of fylbys # 1a) The decision vector length dim = 1 + 1 + 1 + self.__n_legs * 8 + sum(n_seg) * 3 # 1b) The total number of constraints (mass + mismatch + fly-by + boundary + throttles c_dim = 1 + self.__n_legs * 7 + n_fb * 2 + 4 + sum(n_seg) # 1c) The number of inequality constraints (boundary + fly-by angle + throttles) c_ineq_dim = 4 + 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_return_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.seq1 = seq1 self.seq2 = seq2 # private: self.__n_seg = n_seg self.__vinf_dep1 = vinf_dep1 * 1000 self.__vinf_arr1 = vinf_arr1 * 1000 self.__vinf_dep2 = vinf_dep2 * 1000 self.__vinf_arr2 = vinf_arr2 * 1000 self.__mf = mass_end self.__dm = dm #Spacecaft(s) if spacecrafts == None: sc = spacecraft(mass_end, Tmax, Isp) print "!!Spacecraft not defined (motors)!!" #sc = spacecraft(mass_end, Tmax, Isp, 0.0000331,-0.0077, 9000, 2500, 29400, 300) #T6 x 2 #sc = spacecraft(mass_end, Tmax, Isp, 0.0000331,-0.0077, 9000, 2500, 25000, 300) #T6 x 2 self.__spacecrafts = [sc, sc] elif len(spacecrafts) == 1: print "Copying spacecraft twice" self.__spacecrafts = [spacecrafts, spacecrafts] elif len(spacecrafts) == 2: self.__spacecrafts = spacecrafts else: print "!!Spacecraft definition does ot have the right size!!" #sc = spacecraft(mass_end, Tmax, Isp, 0.0000331,-0.0077, 9000, 2500, 29400, 300) #T6 x 2 #sc = spacecraft(mass_end, Tmax, Isp, 0.0000331,-0.0077, 9000, 2500, 25000, 300) #T6 x 2 # T6 Qinetic x 2 #self.__sc = spacecraft(mass_end, Tmax, Isp, 0.0000331,-0.0077, 9000, 2500, 25000, 300) #self.__sc = spacecraft(mass_end, Tmax, Isp, 0.00003333,-0.0072, 10000, 2000, 29400, 300) #self.__sc = spacecraft(mass_end, Tmax, Isp,0.0000,0.2, 9000, 2500, 10000, 300) self.__leg = leg() self.__leg.set_mu(MU_SUN) self.__leg.set_spacecraft(self.__spacecrafts[0]) self.__leg.high_fidelity = high_fidelity self.__leg.solar_powered = solar_powered fb_rel_vel *= 1000 # 3) We compute the bounds lb = [t0[0].mjd2000] + [tos[0]] + [1000] + [ 0, mass_end, -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] + [tos[1]] + [5000] + [ 1, 5000, 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...... # 3a1 for departure 1 lb[5:8] = [-self.__vinf_dep1] * 3 ub[5:8] = [self.__vinf_dep1] * 3 # 3a2 for arrival 1 lb[5 + (len(seq1) - 2) * 8:8 + (len(seq1) - 2) * 8] = [-self.__vinf_arr1] * 3 ub[5 + (len(seq1) - 2) * 8:8 + (len(seq1) - 2) * 8] = [self.__vinf_arr1] * 3 # 3a3 for departure 2 lb[5 + (len(seq1) - 2 + 1) * 8:8 + (len(seq1) - 2 + 1) * 8] = [-self.__vinf_dep2] * 3 ub[5 + (len(seq1) - 2 + 1) * 8:8 + (len(seq1) - 2 + 1) * 8] = [self.__vinf_dep2] * 3 # 3a4 for arrival 2 lb[-sum(self.__n_seg) * 3 - 3:-sum(self.__n_seg) * 3] = [-self.__vinf_arr2] * 3 ub[-sum(self.__n_seg) * 3 - 3:-sum(self.__n_seg) * 3] = [self.__vinf_arr2] * 3 # 3b... and for the time of flight lb[3:1 + 8 * self.__n_legs:8] = [el[0] for el in tof] ub[3:1 + 8 * self.__n_legs:8] = [el[1] for el in tof] # 4) And we set the bounds self.set_bounds(lb, ub)