Example #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
Example #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
Example #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
Example #4
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:
            # integrate orbit
            t,w = potential.integrate_orbit(w0.copy(), dt=0.2, nsteps=50000)

            # radial oscillations
            r = np.sqrt(np.sum(w[:,0,:3]**2, axis=-1))
            T = gd.peak_to_peak_period(t, r)

            # timestep from number of steps per period
            dt = float(T) / float(c['nsteps_per_period'])
            nsteps = int(round(c['nperiods'] * T / dt))

        except RuntimeError:
            logger.warning("Failed to integrate orbit when estimating dt,nsteps")
            result['success'] = False
            result['error_code'] = 1
            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
            return result

        # find apos, peris
        r = np.sqrt(np.sum(ws[:,0,:3]**2, axis=-1))
        pc = r[argrelmin(r)[0]]
        ac = r[argrelmax(r)[0]]
        pc.resize(c['nperiods']+2)
        ac.resize(c['nperiods']+2)

        result['dE_max'] = dEmax
        result['dt'] = float(dt)
        result['nsteps'] = nsteps
        result['success'] = True
        result['error_code'] = 0
        result['pericenters'] = pc
        result['apocenters'] = ac

        return result
Example #5
0
def cyl_orbit_to_events2(t, w, midi_pool_hi, midi_pool_lo):
    """
    Convert an orbit to MIDI events using cylindrical coordinates and rules.

    For cylindrical orbits, crossing the disk midplane (x-y plane) triggers a
    high note with pitch set by the vertical oscillation frequency. Crossing
    the x-z plane triggers a low note with pitch set by the azimuthal frequency.
    The radial oscillations modulate the volume of the note.

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

    """
    ntimes,norbits,_ = w.shape

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

    # normalized R for oscillations
    normed_R = (R - R.min()) / (R.max() - R.min())

    # variable length arrays
    phi_cross = np.array([argrelmin(pphi)[0] for pphi in phi.T])
    z_cross = np.array([argrelmin(zz**2)[0] for zz in z.T])

    # estimate periods
    T_z = np.array([gd.peak_to_peak_period(t, z[:,i]) for i in range(norbits)])
    T_phi = np.array([gd.peak_to_peak_period(t, phi[:,i]) for i in range(norbits)])

    # quantize the periods and map on to notes
    # q_z = quantize(T_z, nbins=len(midi_pool_hi), min=T_z.max(), max=T_z.min())
    # q_phi = quantize(T_phi, nbins=len(midi_pool_lo), min=T_phi.max(), max=T_phi.min())
    q_z = quantize(T_z, nbins=len(midi_pool_hi), min=120., max=30.)  # TOTAL HACk
    q_phi = quantize(T_phi, nbins=len(midi_pool_lo), min=350., max=50.)  # TOTAL HACk

    delays = []
    notes = []
    Rphase = []
    for j in range(w.shape[0]):
        _no = []
        _ph = []
        for i in range(w.shape[1]):
            if j in z_cross[i]:
                _no.append(midi_pool_hi[q_z[i]])
                _ph.append(normed_R[j,i])

            if j in phi_cross[i]:
                _no.append(midi_pool_lo[q_phi[i]])
                _ph.append(1.)

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

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

    return delays, notes, Rphase
Example #6
0
def cyl_orbit_to_events2(t, w, midi_pool_hi, midi_pool_lo):
    """
    Convert an orbit to MIDI events using cylindrical coordinates and rules.

    For cylindrical orbits, crossing the disk midplane (x-y plane) triggers a
    high note with pitch set by the vertical oscillation frequency. Crossing
    the x-z plane triggers a low note with pitch set by the azimuthal frequency.
    The radial oscillations modulate the volume of the note.

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

    """
    ntimes, norbits, _ = w.shape

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

    # normalized R for oscillations
    normed_R = (R - R.min()) / (R.max() - R.min())

    # variable length arrays
    phi_cross = np.array([argrelmin(pphi)[0] for pphi in phi.T])
    z_cross = np.array([argrelmin(zz**2)[0] for zz in z.T])

    # estimate periods
    T_z = np.array(
        [gd.peak_to_peak_period(t, z[:, i]) for i in range(norbits)])
    T_phi = np.array(
        [gd.peak_to_peak_period(t, phi[:, i]) for i in range(norbits)])

    # quantize the periods and map on to notes
    # q_z = quantize(T_z, nbins=len(midi_pool_hi), min=T_z.max(), max=T_z.min())
    # q_phi = quantize(T_phi, nbins=len(midi_pool_lo), min=T_phi.max(), max=T_phi.min())
    q_z = quantize(T_z, nbins=len(midi_pool_hi), min=120.,
                   max=30.)  # TOTAL HACk
    q_phi = quantize(T_phi, nbins=len(midi_pool_lo), min=350.,
                     max=50.)  # TOTAL HACk

    delays = []
    notes = []
    Rphase = []
    for j in range(w.shape[0]):
        _no = []
        _ph = []
        for i in range(w.shape[1]):
            if j in z_cross[i]:
                _no.append(midi_pool_hi[q_z[i]])
                _ph.append(normed_R[j, i])

            if j in phi_cross[i]:
                _no.append(midi_pool_lo[q_phi[i]])
                _ph.append(1.)

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

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

    return delays, notes, Rphase