Exemplo n.º 1
0
def estimate_dt_nsteps(w0, potential, nperiods, nsteps_per_period, dE_threshold=1E-9, return_periods=False):
    """
    Estimate the timestep and number of steps to integrate for given a potential
    and set of initial conditions.

    Parameters
    ----------
    w0 : array_like
    potential : :class:`~gary.potential.Potential`
    nperiods : int
        Number of (max) periods to integrate.
    nsteps_per_period : int
        Number of steps to take per (max) orbital period.
    dE_threshold : numeric (optional)
        Maximum fractional energy difference -- used to determine initial timestep.
        Set to ``None`` to ignore this.
    return_periods : bool (optional)
        Also return the estimated periods for the orbit.

    """

    # integrate orbit
    dt = _autodetermine_initial_dt(w0, potential, dE_threshold=dE_threshold)
    nsteps = int(round(10000 / dt))
    t,w = potential.integrate_orbit(w0, dt=dt, nsteps=nsteps)

    # if loop, align circulation with Z and take R period
    loop = gd.classify_orbit(w[:,0])
    if np.any(loop):
        w = gd.align_circulation_with_z(w[:,0], loop)

        # convert to cylindrical coordinates
        R = np.sqrt(w[:,0]**2 + w[:,1]**2)
        phi = np.arctan2(w[:,1], w[:,0])
        z = w[:,2]

        T = np.array([gd.peak_to_peak_period(t, f) for f in [R, phi, z]])

    else:
        T = np.array([gd.peak_to_peak_period(t, f) for f in w.T[:3,0]])

    # timestep from number of steps per period
    Tmax = T.max()
    if np.isnan(Tmax):
        T = T[np.isfinite(T)]
        Tmax = T.max()

    if np.isnan(Tmax):
        raise RuntimeError("Failed to find period.")

    dt = float(Tmax) / float(nsteps_per_period)
    nsteps = int(round(nperiods * Tmax / dt))

    if dt == 0.:
        raise ValueError("Timestep is zero!")

    if return_periods:
        return dt, nsteps, T
    else:
        return dt, nsteps
Exemplo n.º 2
0
def elliptical_orbit_to_events(t, w):
    """
    Convert an orbit to MIDI events using Cartesian coordinates and rules.

    Parameters
    ----------
    t : array_like
    w : array_like
    midi_pool : array_like

    """

    loop = gd.classify_orbit(w)

    # apocenters
    x,y,z = w.T[:3]
    r = np.sqrt(x**2 + y**2 + z**2)
    apo = np.array([argrelmax(rr)[0] for rr in r])

    # get periods
    periods = []
    for i in range(w.shape[1]):
        if np.any(loop[i] == 1):
            w2 = gd.align_circulation_with_z(w[:,i], loop[i])

            R = np.sqrt(w2[:,0]**2 + w2[:,1]**2)
            phi = np.arctan2(w2[:,1], w2[:,0]) % (2*np.pi)
            z = w2[:,2]

            # loop
            T1 = gd.peak_to_peak_period(t, R)
            T2 = gd.peak_to_peak_period(t, phi)
            T3 = gd.peak_to_peak_period(t, z)

        else:
            # box
            T1 = gd.peak_to_peak_period(t, w[:,i,0])
            T2 = gd.peak_to_peak_period(t, w[:,i,1])
            T3 = gd.peak_to_peak_period(t, w[:,i,2])

        periods.append([T1,T2,T3])

    freqs = (2*np.pi / np.array(periods)) * 10000.

    delays = []
    notes = []
    for j in range(w.shape[0]):
        _no = []
        for i in range(w.shape[1]):
            if j in apo[i]:
                _no.append(freqs[i].tolist())

        if len(_no) > 0:
            delays.append(t[j])
            notes.append(np.unique(_no).tolist())

    delays = np.array(delays)
    notes = np.array(notes)

    return delays, notes
Exemplo n.º 3
0
def elliptical_orbit_to_events(t, w):
    """
    Convert an orbit to MIDI events using Cartesian coordinates and rules.

    Parameters
    ----------
    t : array_like
    w : array_like
    midi_pool : array_like

    """

    loop = gd.classify_orbit(w)

    # apocenters
    x, y, z = w.T[:3]
    r = np.sqrt(x**2 + y**2 + z**2)
    apo = np.array([argrelmax(rr)[0] for rr in r])

    # get periods
    periods = []
    for i in range(w.shape[1]):
        if np.any(loop[i] == 1):
            w2 = gd.align_circulation_with_z(w[:, i], loop[i])

            R = np.sqrt(w2[:, 0]**2 + w2[:, 1]**2)
            phi = np.arctan2(w2[:, 1], w2[:, 0]) % (2 * np.pi)
            z = w2[:, 2]

            # loop
            T1 = gd.peak_to_peak_period(t, R)
            T2 = gd.peak_to_peak_period(t, phi)
            T3 = gd.peak_to_peak_period(t, z)

        else:
            # box
            T1 = gd.peak_to_peak_period(t, w[:, i, 0])
            T2 = gd.peak_to_peak_period(t, w[:, i, 1])
            T3 = gd.peak_to_peak_period(t, w[:, i, 2])

        periods.append([T1, T2, T3])

    freqs = (2 * np.pi / np.array(periods)) * 10000.

    delays = []
    notes = []
    for j in range(w.shape[0]):
        _no = []
        for i in range(w.shape[1]):
            if j in apo[i]:
                _no.append(freqs[i].tolist())

        if len(_no) > 0:
            delays.append(t[j])
            notes.append(np.unique(_no).tolist())

    delays = np.array(delays)
    notes = np.array(notes)

    return delays, notes
Exemplo n.º 4
0
def compute_all_freqs(t, ws, hamming_p=1, nintvec=10, force_cartesian=False):
    """
    Compute the fundamental frequencies and amplitudes for all
    specified orbits.

    This assumes that all orbits have the same geometry as the first
    orbit in the orbit array. That is, (if ``force_cartesian`` is
    ``False``) if the first orbit is a tube orbit, it assumes all orbits
    are tubes.

    Parameters
    ----------
    t : array_like
    ws : array_like
    hamming_p : int (optional)
    nintvec : int (optional)
    force_cartesian : bool (optional)

    Returns
    -------
    freqs : :class:`numpy.ndarray`
    amps : :class:`numpy.ndarray`
    """

    # classify parent orbit
    circ = gd.classify_orbit(ws[:,0])
    is_tube = np.any(circ)

    allfreqs = []
    allamps = []
    for i in range(ws.shape[1]):
        ww = ws[:,i]
        if is_tube and not force_cartesian:
            # need to flip coordinates until circulation is around z axis
            new_ws = gd.align_circulation_with_z(ww, circ)
            new_ws = gc.cartesian_to_poincare_polar(new_ws)
        else:
            new_ws = ww

        fs = [(new_ws[:,j] + 1j*new_ws[:,j+ws.shape[-1]//2]) for j in range(ws.shape[-1]//2)]
        sf = SuperFreq(t, p=hamming_p)

        try:
            freqs,d,ixs = sf.find_fundamental_frequencies(fs, nintvec=nintvec)
        except:
            allfreqs.append([np.nan,np.nan,np.nan])
            allamps.append([np.nan,np.nan,np.nan])
            continue

        allfreqs.append(freqs.tolist())
        allamps.append(d['|A|'][ixs].tolist())

    allfreqs = np.array(allfreqs)
    allamps = np.array(allamps)

    return allfreqs, allamps
Exemplo n.º 5
0
def make_orbit_files(potential,
                     w0,
                     N_max=6,
                     suffix="",
                     overwrite=False,
                     force_harmonic_oscillator=False):

    orbit_filename = os.path.join(plot_path, "orbits{}.npy".format(suffix))
    units = potential.units
    nsteps = 100000
    dt = 5.

    #NT = 9*N_max**3/2
    #every = nsteps // NT

    if overwrite and os.path.exists(orbit_filename):
        os.remove(orbit_filename)

    toy_potential = None
    if not os.path.exists(orbit_filename):
        # integrate an orbit in a axisymmetric potential
        logger.debug("Integrating orbit for {} steps...".format(nsteps))
        acc = lambda t, w: np.hstack(
            (w[..., 3:], potential.acceleration(w[..., :3])))
        integrator = si.DOPRI853Integrator(acc)
        # acc = lambda t,x: potential.acceleration(x)
        # integrator = si.LeapfrogIntegrator(acc)
        t, w = integrator.run(w0, dt=dt, nsteps=nsteps)
        logger.debug("...done!")

        loop = sd.classify_orbit(w)
        if np.any(loop == 1) and not force_harmonic_oscillator:  # loop orbit
            logger.debug("Orbit classified as LOOP")
            m, b = sd.fit_isochrone(w, units=units)
            toy_potential = sp.IsochronePotential(m=m, b=b, units=units)
        else:
            logger.debug("Orbit classified as BOX")
            omegas = sd.fit_harmonic_oscillator(w, units=units)
            toy_potential = sp.HarmonicOscillatorPotential(omega=omegas,
                                                           units=units)

        # also integrate the orbit in the best-fitting toy potential
        toy_steps = w.shape[0] // 10
        logger.debug("Integrating toy orbit for {} steps...".format(toy_steps))
        acc = lambda ts, ws: np.hstack(
            (ws[..., 3:], toy_potential.acceleration(ws[..., :3])))
        integrator = si.DOPRI853Integrator(acc)
        # acc = lambda t,x: toy_potential.acceleration(x)
        # integrator = si.LeapfrogIntegrator(acc)
        toy_t, toy_w = integrator.run(w0, dt=dt, nsteps=toy_steps)
        logger.debug("...done!")

        # cache the orbits
        np.save(orbit_filename, (t, w, toy_t, toy_w))

        logger.debug(
            "Orbit computed and saved to file: {}".format(orbit_filename))
    else:
        t, w, toy_t, toy_w = np.load(orbit_filename)
        logger.debug("Orbit read from file: {}".format(orbit_filename))

    # for i in range(100):
    #     omega0 = np.random.uniform(0.,100.,size=3)
    #     omegas = sd.fit_harmonic_oscillator(w, units=units, omega=omega0)
    #     print(omegas)
    #     toy_potential = sp.HarmonicOscillatorPotential(omega=omegas, units=units)
    # sys.exit(0)

    if toy_potential is None:
        loop = sd.classify_orbit(w)
        if np.any(loop == 1) and not force_harmonic_oscillator:  # loop orbit
            m, b = sd.fit_isochrone(w, units=units)
            toy_potential = sp.IsochronePotential(m=m, b=b, units=units)
        else:
            omegas = sd.fit_harmonic_oscillator(w, units=units)
            toy_potential = sp.HarmonicOscillatorPotential(omega=omegas,
                                                           units=units)

    logger.debug("Toy potential: {}".format(toy_potential))

    # plot a smaller section of the orbit in projections of XYZ
    plot_w = w[:w.shape[0] // 10]
    fig = sd.plot_orbits(toy_w,
                         marker=None,
                         linestyle='-',
                         alpha=0.5,
                         triangle=True,
                         c='r')
    fig = sd.plot_orbits(plot_w,
                         axes=fig.axes,
                         marker=None,
                         linestyle='-',
                         alpha=0.8,
                         triangle=True,
                         c='k')
    fig.savefig(os.path.join(plot_path, "orbit_xyz{}.png".format(suffix)))

    # plot a smaller section of the orbit in the meridional plane
    fig, ax = plt.subplots(1, 1, figsize=(6, 6))
    R = np.sqrt(plot_w[:, 0, 0]**2 + plot_w[:, 0, 1]**2)
    toy_R = np.sqrt(toy_w[:, 0, 0]**2 + toy_w[:, 0, 1]**2)
    ax.plot(toy_R,
            toy_w[:, 0, 2],
            marker=None,
            linestyle='-',
            alpha=0.5,
            c='r')
    ax.plot(R, plot_w[:, 0, 2], marker=None, linestyle='-', alpha=0.8, c='k')
    ax.set_xlabel("$R$")
    ax.set_ylabel("$Z$", rotation='horizontal')
    fig.savefig(os.path.join(plot_path, "orbit_Rz{}.png".format(suffix)))

    return t, w, toy_potential
Exemplo n.º 6
0
def make_orbit_files(potential, w0, N_max=6, suffix="", overwrite=False,
                     force_harmonic_oscillator=False):

    orbit_filename = os.path.join(plot_path, "orbits{}.npy".format(suffix))
    units = potential.units
    nsteps = 100000
    dt = 5.

    #NT = 9*N_max**3/2
    #every = nsteps // NT

    if overwrite and os.path.exists(orbit_filename):
        os.remove(orbit_filename)

    toy_potential = None
    if not os.path.exists(orbit_filename):
        # integrate an orbit in a axisymmetric potential
        logger.debug("Integrating orbit for {} steps...".format(nsteps))
        acc = lambda t,w: np.hstack((w[...,3:],potential.acceleration(w[...,:3])))
        integrator = si.DOPRI853Integrator(acc)
        # acc = lambda t,x: potential.acceleration(x)
        # integrator = si.LeapfrogIntegrator(acc)
        t,w = integrator.run(w0, dt=dt, nsteps=nsteps)
        logger.debug("...done!")

        loop = sd.classify_orbit(w)
        if np.any(loop == 1) and not force_harmonic_oscillator: # loop orbit
            logger.debug("Orbit classified as LOOP")
            m,b = sd.fit_isochrone(w, units=units)
            toy_potential = sp.IsochronePotential(m=m, b=b, units=units)
        else:
            logger.debug("Orbit classified as BOX")
            omegas = sd.fit_harmonic_oscillator(w, units=units)
            toy_potential = sp.HarmonicOscillatorPotential(omega=omegas, units=units)

        # also integrate the orbit in the best-fitting toy potential
        toy_steps = w.shape[0]//10
        logger.debug("Integrating toy orbit for {} steps...".format(toy_steps))
        acc = lambda ts,ws: np.hstack((ws[...,3:],toy_potential.acceleration(ws[...,:3])))
        integrator = si.DOPRI853Integrator(acc)
        # acc = lambda t,x: toy_potential.acceleration(x)
        # integrator = si.LeapfrogIntegrator(acc)
        toy_t,toy_w = integrator.run(w0, dt=dt, nsteps=toy_steps)
        logger.debug("...done!")

        # cache the orbits
        np.save(orbit_filename, (t,w,toy_t,toy_w))

        logger.debug("Orbit computed and saved to file: {}".format(orbit_filename))
    else:
        t,w,toy_t,toy_w = np.load(orbit_filename)
        logger.debug("Orbit read from file: {}".format(orbit_filename))

    # for i in range(100):
    #     omega0 = np.random.uniform(0.,100.,size=3)
    #     omegas = sd.fit_harmonic_oscillator(w, units=units, omega=omega0)
    #     print(omegas)
    #     toy_potential = sp.HarmonicOscillatorPotential(omega=omegas, units=units)
    # sys.exit(0)

    if toy_potential is None:
        loop = sd.classify_orbit(w)
        if np.any(loop == 1) and not force_harmonic_oscillator: # loop orbit
            m,b = sd.fit_isochrone(w, units=units)
            toy_potential = sp.IsochronePotential(m=m, b=b, units=units)
        else:
            omegas = sd.fit_harmonic_oscillator(w, units=units)
            toy_potential = sp.HarmonicOscillatorPotential(omega=omegas, units=units)

    logger.debug("Toy potential: {}".format(toy_potential))

    # plot a smaller section of the orbit in projections of XYZ
    plot_w = w[:w.shape[0]//10]
    fig = sd.plot_orbits(toy_w, marker=None, linestyle='-',
                         alpha=0.5, triangle=True, c='r')
    fig = sd.plot_orbits(plot_w, axes=fig.axes, marker=None, linestyle='-',
                         alpha=0.8, triangle=True, c='k')
    fig.savefig(os.path.join(plot_path, "orbit_xyz{}.png".format(suffix)))

    # plot a smaller section of the orbit in the meridional plane
    fig,ax = plt.subplots(1,1,figsize=(6,6))
    R = np.sqrt(plot_w[:,0,0]**2 + plot_w[:,0,1]**2)
    toy_R = np.sqrt(toy_w[:,0,0]**2 + toy_w[:,0,1]**2)
    ax.plot(toy_R, toy_w[:,0,2], marker=None, linestyle='-', alpha=0.5, c='r')
    ax.plot(R, plot_w[:,0,2], marker=None, linestyle='-', alpha=0.8, c='k')
    ax.set_xlabel("$R$")
    ax.set_ylabel("$Z$", rotation='horizontal')
    fig.savefig(os.path.join(plot_path, "orbit_Rz{}.png".format(suffix)))

    return t,w,toy_potential
Exemplo n.º 7
0
    def run(cls, w0, potential, **kwargs):
        c = dict()
        for k in cls.config_defaults.keys():
            if k not in kwargs:
                c[k] = cls.config_defaults[k]
            else:
                c[k] = kwargs[k]

        # return dict
        result = dict()

        # get timestep and nsteps for integration
        try:
            dt, nsteps = estimate_dt_nsteps(w0.copy(), potential,
                                            c['nperiods'],
                                            c['nsteps_per_period'])
        except RuntimeError:
            logger.warning("Failed to integrate orbit when estimating dt,nsteps")
            result['freqs'] = np.ones((2,3))*np.nan
            result['success'] = False
            result['error_code'] = 1
            return result
        except:
            logger.warning("Unexpected failure!")
            result['freqs'] = np.ones((2,3))*np.nan
            result['success'] = False
            result['error_code'] = 4
            return result

        # integrate orbit
        logger.debug("Integrating orbit with dt={0}, nsteps={1}".format(dt, nsteps))
        try:
            t,ws = potential.integrate_orbit(w0.copy(), dt=dt, nsteps=nsteps,
                                             Integrator=gi.DOPRI853Integrator,
                                             Integrator_kwargs=dict(atol=1E-11))
        except RuntimeError: # ODE integration failed
            logger.warning("Orbit integration failed.")
            dEmax = 1E10
        else:
            logger.debug('Orbit integrated successfully, checking energy conservation...')

            # check energy conservation for the orbit
            E = potential.total_energy(ws[:,0,:3].copy(), ws[:,0,3:].copy())
            dE = np.abs(E[1:] - E[0])
            dEmax = dE.max() / np.abs(E[0])
            logger.debug('max(∆E) = {0:.2e}'.format(dEmax))

        if dEmax > c['energy_tolerance']:
            logger.warning("Failed due to energy conservation check.")
            result['freqs'] = np.ones((2,3))*np.nan
            result['success'] = False
            result['error_code'] = 2
            result['dE_max'] = dEmax
            return result

        # start finding the frequencies -- do first half then second half
        sf1 = SuperFreq(t[:nsteps//2+1], p=c['hamming_p'])
        sf2 = SuperFreq(t[nsteps//2:], p=c['hamming_p'])

        # classify orbit full orbit
        circ = gd.classify_orbit(ws)
        is_tube = np.any(circ)

        # define slices for first and second parts
        sl1 = slice(None,nsteps//2+1)
        sl2 = slice(nsteps//2,None)

        if is_tube and not c['force_cartesian']:
            # first need to flip coordinates so that circulation is around z axis
            new_ws = gd.align_circulation_with_z(ws, circ)
            new_ws = gc.cartesian_to_poincare_polar(new_ws)
            fs1 = [(new_ws[sl1,j] + 1j*new_ws[sl1,j+3]) for j in range(3)]
            fs2 = [(new_ws[sl2,j] + 1j*new_ws[sl2,j+3]) for j in range(3)]

        else:  # box
            fs1 = [(ws[sl1,0,j] + 1j*ws[sl1,0,j+3]) for j in range(3)]
            fs2 = [(ws[sl2,0,j] + 1j*ws[sl2,0,j+3]) for j in range(3)]

        logger.debug("Running SuperFreq on the orbits")
        try:
            freqs1,d1,ixs1 = sf1.find_fundamental_frequencies(fs1, nintvec=c['nintvec'])
            freqs2,d2,ixs2 = sf2.find_fundamental_frequencies(fs2, nintvec=c['nintvec'])
        except:
            result['freqs'] = np.ones((2,3))*np.nan
            result['success'] = False
            result['error_code'] = 3
            return result

        result['freqs'] = np.vstack((freqs1, freqs2))
        result['dE_max'] = dEmax
        result['is_tube'] = float(is_tube)
        result['dt'] = float(dt)
        result['nsteps'] = nsteps
        result['amps'] = np.vstack((d1['|A|'][ixs1], d2['|A|'][ixs2]))
        result['success'] = True
        result['error_code'] = 0
        return result
Exemplo n.º 8
0
    def run(cls, w0, potential, **kwargs):
        c = dict()
        for k in cls.config_defaults.keys():
            if k not in kwargs:
                c[k] = cls.config_defaults[k]
            else:
                c[k] = kwargs[k]

        # container for return
        result = dict()

        # automatically estimate dt, nsteps
        try:
            dt, nsteps = estimate_dt_nsteps(w0.copy(), potential,
                                            c['total_nperiods'], c['nsteps_per_period'])
        except RuntimeError:
            logger.warning("Failed to integrate orbit when estimating dt,nsteps")
            result['freqs'] = np.nan
            result['success'] = False
            result['error_code'] = 1
            return result

        logger.debug("Integrating orbit with dt={0}, nsteps={1}".format(dt, nsteps))
        try:
            t,ws = potential.integrate_orbit(w0.copy(), dt=dt, nsteps=nsteps,
                                             Integrator=gi.DOPRI853Integrator,
                                             Integrator_kwargs=dict(atol=1E-11))
        except RuntimeError:  # ODE integration failed
            logger.warning("Orbit integration failed.")
            dEmax = 1E10
        else:
            logger.debug('Orbit integrated successfully, checking energy conservation...')

            # check energy conservation for the orbit
            E = potential.total_energy(ws[:,0,:3].copy(), ws[:,0,3:].copy())
            dE = np.abs(E[1:] - E[0])
            dEmax = dE.max() / np.abs(E[0])
            logger.debug('max(∆E) = {0:.2e}'.format(dEmax))

        if dEmax > c['energy_tolerance']:
            logger.warning("Failed due to energy conservation check.")
            result['freqs'] = np.nan
            result['success'] = False
            result['error_code'] = 2
            return result

        # windowing properties - convert from period to steps
        window_width = int(c['window_width'] * c['nsteps_per_period'])
        window_stride = int(c['window_stride'] * c['nsteps_per_period'])

        # classify orbit full orbit
        circ = gd.classify_orbit(ws[:,0])
        is_tube = np.any(circ)

        logger.debug("Running SuperFreq on each window:")

        allfreqs = []
        allamps = []
        for (i1,i2),ww in rolling_window(ws[:,0], window_size=window_width, stride=window_stride, return_idx=True):
            if i2 >= nsteps:
                break

            logger.debug("Window: {0}:{1}".format(i1,i2))
            if is_tube and not c['force_cartesian']:
                # need to flip coordinates until circulation is around z axis
                new_ws = gd.align_circulation_with_z(ww, circ)
                new_ws = gc.cartesian_to_poincare_polar(new_ws)
            else:
                new_ws = ww

            fs = [(new_ws[:,j] + 1j*new_ws[:,j+3]) for j in range(3)]
            naff = SuperFreq(t[i1:i2], p=c['hamming_p'])

            try:
                freqs,d,ixs = naff.find_fundamental_frequencies(fs, nintvec=c['nintvec'])
            except:
                result['freqs'] = np.nan
                result['success'] = False
                result['error_code'] = 3
                return result

            allfreqs.append(freqs.tolist())
            allamps.append(d['|A|'][ixs].tolist())
        allfreqs = np.array(allfreqs)
        allamps = np.array(allamps)

        result['freqs'] = allfreqs
        result['amps'] = allamps
        result['dE_max'] = dEmax
        result['dt'] = float(dt)
        result['nsteps'] = nsteps
        result['is_tube'] = is_tube
        result['success'] = True
        result['error_code'] = 0
        return result