Ejemplo n.º 1
0
    def compute_solution(self, nb=200):
        """Retrieves the optimization results and computes the spacecraft states along the ballistic arc as well as the
        final insertion burn.

        Parameters
        ----------
        nb : int, optional
            Number of points in which the spacecraft states along the ballistic arc are computed. Default is ``200``

        """

        # states and COEs at the end of the departure burn
        states_end = self.states[-1]
        a, e, h, ta = TwoDimOrb.polar2coe(self.gm_res, states_end[0],
                                          states_end[2], states_end[3])

        # coasting orbit in dimensional units
        if np.isclose(self.gm_res, 1.0):
            self.transfer = TwoDimOrb(self.body.GM, a=a * self.body.R, e=e)
        else:
            self.transfer = TwoDimOrb(self.body.GM, a=a, e=e)

        # finite dV at departure [m/s]
        self.dv_dep = ImpulsiveBurn.tsiolkovsky_dv(self.sc.m0, states_end[-1],
                                                   self.sc.Isp)

        # impulsive dV at arrival [m/s]
        sc = deepcopy(self.sc)
        sc.m0 = states_end[-1]
        self.insertion_burn = ImpulsiveBurn(
            sc, self.guess.ht.arrOrb.va - self.transfer.va)

        # time and states along transfer orbit
        t, states = TwoDimOrb.propagate(self.gm_res, a, e, ta, np.pi, nb)

        # adjust time
        t_pow_end = self.time[-1, -1]
        t_coast_start = t[0, 0]
        t_coast_end = t[-1, 0]
        tof_coast = t_coast_end - t_coast_start

        self.tof = [self.tof, tof_coast]
        self.time = [self.time, (t - t_coast_start) + t_pow_end]

        # add mass
        m = np.vstack((states_end[-1] * np.ones(
            (len(t) - 1, 1)), [self.insertion_burn.mf]))
        states = np.hstack((states, m))

        # stack states and controls
        self.states = [self.states, states]
        self.controls = [self.controls, np.zeros((len(t), 2))]
        self.controls[-1][-1, 0] = self.controls[0][0, 0]

        # adjust theta
        dtheta = ta - self.states[0][-1, 1]
        self.states[0][:, 1] = self.states[0][:, 1] + dtheta
        if self.states_exp is not None:
            self.states_exp[:, 1] = self.states_exp[:, 1] + dtheta
Ejemplo n.º 2
0
    def compute_energy_mprop(self, r, u, v, m_coast):
        """Computes the specific energy of the spacecraft on the ballistic arc and the total propellant mass including
        the final insertion burn.

        Parameters
        ----------
        r : float
            Radius at the end of the first powered phase [m]
        u : float
            Radial velocity at the end of the first powered phase [m/s]
        v : float
            Tangential velocity at the end of the first powered phase [m/s]
        m_coast : float
            Spacecraft mass on the ballistic arc [kg]

        Returns
        -------
        en : float
            Specific energy of the spacecraft on the ballistic arc [m/s]
        m_prop : float
            Total propellant mass including the final insertion burn [kg]

        """

        en = TwoDimOrb.polar2energy(
            self.body.GM, r, u,
            v)  # specific energy on ballistic arc [m^2/s^2]
        va = (2 * (en + self.body.GM / self.nlp.guess.ht.arrOrb.ra)
              )**0.5  # ballistic arc apoapsis velocity [m/s]
        dva = self.guess.ht.arrOrb.va - va  # apoapsis dv [m/s]
        m_prop = self.sc.m0 - ImpulsiveBurn.tsiolkovsky_mf(
            m_coast, dva, self.sc.Isp)  # total propellant mass [kg]

        return en, m_prop
Ejemplo n.º 3
0
    def compute_trajectory(self, fix_final=True, throttle=True, **kwargs):
        """Computes the states and controls timeseries on the provided time grid.

        Parameters
        ----------
        fix_final : bool, optional
            ``True`` if the final angle is fixed, ``False`` otherwise. Default is ``True``
        throttle : bool, optional
            If ``True`` replace the spacecraft states on the last node with the ones from `ImpulsiveBurn`

        Other Parameters
        ----------------
        t_eval : ndarray
            Time vector in which states and controls are computed [s]
        nb_nodes : int
            Number of equally space nodes in time in which states and controls are computed

        """

        TwoDimHEOGuess.compute_trajectory(self, **kwargs)

        t_pow = self.t[self.t <= self.pow.tf]
        t_ht = self.t[self.t > self.pow.tf]

        self.pow.compute_trajectory(t_pow)

        if np.size(t_ht) > 0:
            self.ht.compute_trajectory(t_ht,
                                       self.pow.tf,
                                       theta0=self.pow.thetaf,
                                       m=self.pow.mf)

            self.states = np.vstack((self.pow.states, self.ht.states))
            self.controls = np.vstack((self.pow.controls, self.ht.controls))
        else:
            self.states = self.pow.states
            self.controls = self.pow.controls

        if fix_final:
            self.states[:,
                        1] = self.states[:,
                                         1] - self.pow.thetaf  # final true anomaly equal to pi

        # insertion burn at the NRHO aposelene
        sc = deepcopy(self.sc)
        sc.m0 = self.pow.mf

        self.insertion_burn = ImpulsiveBurn(sc, self.ht.dva)

        if throttle:
            self.states[-1, 3] = self.ht.arrOrb.va
            self.states[-1, 4] = self.insertion_burn.mf
            self.controls[-1, 0] = self.sc.T_max
Ejemplo n.º 4
0
    def __init__(self, gm, r, alt, rp, t, sc):
        """Initializes `TwoDimHEO2LLOGuess` class. """

        arr = TwoDimOrb(gm, a=(r + alt), e=0)
        dep = TwoDimOrb(gm, T=t, rp=rp)

        TwoDimGuess.__init__(self, gm, r, dep, arr, sc)

        self.deorbit_burn = ImpulsiveBurn(sc, self.ht.dva)
        self.pow = PowConstRadius(gm,
                                  arr.a,
                                  self.ht.transfer.vp,
                                  arr.vp,
                                  self.deorbit_burn.mf,
                                  sc.T_max,
                                  sc.Isp,
                                  t0=self.ht.tof)
        self.pow.compute_final_time_states()

        self.tf = self.pow.tf
Ejemplo n.º 5
0
    def sampling(self,
                 body,
                 isp_lim,
                 twr_lim,
                 alt,
                 alt_p,
                 alt_switch,
                 theta,
                 tof,
                 t_bounds,
                 method,
                 nb_seg,
                 order,
                 solver,
                 nb_samp,
                 samp_method='lhs',
                 criterion='m',
                 snopt_opts=None):
        """Compute a new set of training data starting from a given sampling grid.

        Parameters
        ----------
        body : Primary
            Central attracting body
        isp_lim : iterable
            Specific impulse lower and upper limits [s]
        twr_lim : iterable
            Thrust/weight ratio lower and upper limits [-]
        alt : float
            Orbit altitude [m]
        alt_p : float
            Periapsis altitude where the final powered descent is initiated [m]
        alt_switch : float
            Altitude at which the final vertical descent is triggered [m]
        theta : float
            Guessed spawn angle [rad]
        tof : iterable
            Guessed time of flight for the two phases [s]
        t_bounds : iterable
            Time of flight lower and upper bounds expressed as fraction of `tof`
        method : str
            Transcription method used to discretize the continuous time trajectory into a finite set of nodes,
            allowed ``gauss-lobatto``, ``radau-ps`` and ``runge-kutta``
        nb_seg : int or iterable
            Number of segments in which each phase is discretized
        order : int or iterable
            Transcription order within each phase, must be odd
        solver : str
            NLP solver, must be supported by OpenMDAO
        nb_samp : iterable
            Total number of sampling points
        samp_method : str, optional
            Sampling scheme, ``lhs`` for Latin Hypercube Sampling or ``full`` for Full-Factorial Sampling.
            Default is ``lhs``
        criterion : str, optional
            Criterion used to construct the LHS design among ``center``, ``maximin``, ``centermaximin``,
            ``correlation``, ``c``, ``m``, ``cm``, ``corr``, ``ese``. ``c``, ``m``, ``cm`` and ``corr`` are
            abbreviations of ``center``, ``maximin``, ``centermaximin`` and ``correlation``, ``respectively``.
            Default is ``m``
        snopt_opts : dict or None, optional
            SNOPT optional settings expressed as key-value pairs. Default is None

        """

        self.compute_grid(isp_lim,
                          twr_lim,
                          nb_samp,
                          samp_method=samp_method,
                          criterion=criterion)

        ht = HohmannTransfer(body.GM,
                             TwoDimOrb(body.GM, a=(body.R + alt), e=0.0),
                             TwoDimOrb(body.GM, a=(body.R + alt_p), e=0.0))

        for i in range(nb_samp):

            sc = Spacecraft(self.x_samp[i, 0], self.x_samp[i, 1], g=body.g)
            deorbit_burn = ImpulsiveBurn(sc, ht.dva)
            nlp = TwoDimDescTwoPhasesNLP(body,
                                         deorbit_burn.sc,
                                         alt,
                                         alt_switch,
                                         ht.transfer.vp,
                                         theta, (0.0, np.pi),
                                         tof,
                                         t_bounds,
                                         method,
                                         nb_seg,
                                         order,
                                         solver, ('free', 'vertical'),
                                         snopt_opts=snopt_opts)

            self.m_prop[i, 0], self.failures[i, 0] = self.solve(nlp, i)
Ejemplo n.º 6
0
    def solve(body, sc, alt, t_bounds, method, nb_seg, order, solver, snopt_opts=None, u_bound=None, **kwargs):
        """Solve the NLP for the i-th `twr` and k-th `Isp` values.

        Parameters
        ----------
        body : Primary
            Central attracting body
        sc : Spacecraft
            Spacecraft object characterized by the i-th `twr` and k-th `Isp` values
        alt : float
            Orbit altitude [m]
        t_bounds : iterable
            Time of flight lower and upper bounds expressed as fraction of `tof`
        method : str
            Transcription method used to discretize the continuous time trajectory into a finite set of nodes,
            allowed ``gauss-lobatto``, ``radau-ps`` and ``runge-kutta``
        nb_seg : int
            Number of segments in which the phase is discretized
        order : int
            Transcription order within the phase, must be odd
        solver : str
            NLP solver, must be supported by OpenMDAO
        snopt_opts : dict or None, optional
            SNOPT optional settings expressed as key-value pairs. Default is None
        u_bound : str, optional
            Specify the bounds on the radial velocity along the transfer as ``lower``, ``upper`` or ``None``.
            Default is ``None``

        Other Parameters
        ----------------
        alt_p : float
            Periapsis altitude where the final powered descent is initiated [m]
        theta : float
            Guessed spawn angle [rad]
        tof : float
            Guessed time of flight [s]

        Returns
        -------
        m_prop : float
            Propellant fraction [-]
        f : bool
            Failure status

        """

        if 'alt_p' in kwargs:
            alt_p = kwargs['alt_p']
        else:
            alt_p = alt

        dep = TwoDimOrb(body.GM, a=(body.R + alt), e=0.0)
        arr = TwoDimOrb(body.GM, a=(body.R + alt_p), e=0.0)

        ht = HohmannTransfer(body.GM, dep, arr)
        deorbit_burn = ImpulsiveBurn(sc, ht.dva)

        nlp = TwoDimDescConstNLP(body, deorbit_burn.sc, alt_p, ht.transfer.vp, kwargs['theta'], (0.0, 1.5 * np.pi),
                                 kwargs['tof'], t_bounds, method, nb_seg, order, solver, 'powered',
                                 snopt_opts=snopt_opts, u_bound=u_bound)

        f = nlp.p.run_driver()
        nlp.cleanup()
        m_prop = 1. - nlp.p.get_val(nlp.phase_name + '.timeseries.states:m')[-1, -1]

        return m_prop, f