Exemplo n.º 1
0
    def modelLikelihood(self, params):
        '''
        Compute the likelihood of model (df+potential specified by scaled params)
        against the data (array of Nx6 position/velocity coordinates of tracer particles).
        This is the function to be maximized; if parameters are outside the allowed range, it returns -infinity
        '''
        prior = self.model.prior(params)
        if prior == -numpy.inf:
            print "Out of range"
            return prior
        try:
            # Compute log-likelihood of DF with given params against an array of actions
            pot = self.model.createPotential(params)
            df = self.model.createDF(params)
            if phase_space_info_mode == 6:  # actions of tracer particles
                if self.particles.shape[
                        0] > 2000:  # create an action finder object for a faster evaluation
                    actions = agama.ActionFinder(pot)(self.particles)
                else:
                    actions = agama.actions(self.particles, pot)
                df_val = df(actions)  # values of DF for these actions
            else:  # have full phase space info for resampled input particles (missing components are filled in)
                af = agama.ActionFinder(pot)
                actions = af(
                    self.samples)  # actions of resampled tracer particles
                # compute values of DF for these actions, multiplied by sample weights
                df_val = df(actions) * self.weights
                # compute the weighted sum of likelihoods of all samples for a single particle,
                # replacing the improbable samples (with NaN as likelihood) with zeroes
                df_val = numpy.sum(numpy.nan_to_num(
                    df_val.reshape(-1, num_subsamples)),
                                   axis=1)

            loglike = numpy.sum(numpy.log(df_val))
            if numpy.isnan(loglike): loglike = -numpy.inf
            loglike += prior
            print "LogL=%.8g" % loglike
            return loglike
        except ValueError as err:
            print "Exception ", err
            return -numpy.inf
Exemplo n.º 2
0
    def modelLikelihood(self, params):
        '''
        Compute the likelihood of model (df+potential specified by scaled params)
        against the data (array of Nx6 position/velocity coordinates of tracer particles).
        This is the function to be maximized; if parameters are outside the allowed range, it returns -infinity
        '''
        prior = self.model.prior(params)
        if prior == -numpy.inf:
            print "Out of range"
            return prior
        try:
            # Compute log-likelihood of DF with given params against an array of actions
            pot     = self.model.createPotential(params)
            df      = self.model.createDF(params)
            if phase_space_info_mode == 6:  # actions of tracer particles
                if self.particles.shape[0] > 2000:  # create an action finder object for a faster evaluation
                    actions = agama.ActionFinder(pot)(self.particles)
                else:
                    actions = agama.actions(self.particles, pot)
                df_val  = df(actions)       # values of DF for these actions
            else:  # have full phase space info for resampled input particles (missing components are filled in)
                af      = agama.ActionFinder(pot)
                actions = af(self.samples)  # actions of resampled tracer particles
                # compute values of DF for these actions, multiplied by sample weights
                df_val  = df(actions) * self.weights
                # compute the weighted sum of likelihoods of all samples for a single particle,
                # replacing the improbable samples (with NaN as likelihood) with zeroes
                df_val  = numpy.sum(numpy.nan_to_num(df_val.reshape(-1, num_subsamples)), axis=1)

            loglike = numpy.sum( numpy.log( df_val ) )
            if numpy.isnan(loglike): loglike = -numpy.inf
            loglike += prior
            print "LogL=%.8g" % loglike
            return loglike
        except ValueError as err:
            print "Exception ", err
            return -numpy.inf
def compare(ic, inttime, numsteps):
    g_orb_obj = galpy.orbit.Orbit([ic[0],ic[3],ic[5],ic[1],ic[4],ic[2]])
    times = numpy.linspace(0, inttime, numsteps)
    dt = time.time()
    g_orb_obj.integrate(times, g_pot)
    g_orb = g_orb_obj.getOrbit()
    print 'Time to integrate orbit in galpy: %s s' % (time.time()-dt)

    dt = time.time()
    c_orb_car = agama.orbit(ic=[ic[0],0,ic[1],ic[3],ic[5],ic[4]], pot=w_pot._pot, time=inttime, step=inttime/numsteps)
    print 'Time to integrate orbit in Agama: %s s' % (time.time()-dt)
    times_c = numpy.linspace(0,inttime,len(c_orb_car[:,0]))
    ### make it compatible with galpy's convention (native output is in cartesian coordinates)
    c_orb = c_orb_car*1.0
    c_orb[:,0] = (c_orb_car[:,0]**2+c_orb_car[:,1]**2)**0.5
    c_orb[:,3] = c_orb_car[:,2]

    ### in galpy, this is the only tool that can estimate interfocal distance,
    ### but it requires the orbit to be computed first
    delta = estimateDeltaStaeckel(g_orb[:,0], g_orb[:,3], pot=g_pot)
    print "interfocal distance Delta=",delta

    ### plot the orbit(s) in R,z plane, along with the prolate spheroidal coordinate grid
    plt.axes([0.05, 0.55, 0.45, 0.45])
    plotCoords(delta, 1.5)
    plt.plot(g_orb[:,0],g_orb[:,3], 'b', label='galpy')  # R,z
    plt.plot(c_orb[:,0],c_orb[:,3], 'g', label='Agama')  # R,z
    plt.xlabel("R/8kpc")
    plt.ylabel("z/8kpc")
    plt.xlim(0, 1.2)
    plt.ylim(-1,1)
    plt.legend()

    ### plot R(t), z(t)
    plt.axes([0.55, 0.55, 0.45, 0.45])
    plt.plot(times, g_orb[:,0], label='R')
    plt.plot(times, g_orb[:,3], label='z')
    plt.xlabel("t")
    plt.ylabel("R,z")
    plt.legend()
    plt.xlim(0,50)

    ### create galpy action/angle finder for the given value of Delta
    ### note: using c=False in the routine below is much slower but apparently more accurate,
    ### comparable to the Agama for the same value of delta
    g_actfinder = galpy.actionAngle.actionAngleStaeckel(pot=g_pot, delta=delta, c=True)

    ### find the actions for each point of the orbit
    dt = time.time()
    g_act = g_actfinder(g_orb[:,0],g_orb[:,1],g_orb[:,2],g_orb[:,3],g_orb[:,4],fixed_quad=True)
    print 'Time to compute actions in galpy: %s s' % (time.time()-dt)

    ### use the Agama action routine for the same value of Delta as in galpy
    dt = time.time()
    c_act = agama.actions(point=c_orb_car, pot=w_pot._pot, ifd=delta)   # explicitly specify interfocal distance
    print 'Time to compute actions in Agama: %s s' % (time.time()-dt)

    ### use the Agama action finder (initialized at the beginning) that automatically determines the best value of Delta
    dt = time.time()
    a_act = c_actfinder(c_orb_car)   # use the interfocal distance estimated by action finder
    print 'Time to compute actions in Agama: %s s' % (time.time()-dt)

    ### plot Jr vs Jz
    plt.axes([0.05, 0.05, 0.45, 0.45])
    plt.plot(g_act[0],g_act[2], label='galpy')
    plt.plot(c_act[:,0],c_act[:,1], label=r'Agama,$\Delta='+str(delta)+'$')
    plt.plot(a_act[:,0],a_act[:,1], label=r'Agama,$\Delta=$auto')
    plt.xlabel("$J_r$")
    plt.ylabel("$J_z$")
    plt.legend()

    ### plot Jr(t) and Jz(t)
    plt.axes([0.55, 0.05, 0.45, 0.45])
    plt.plot(times, g_act[0], label='galpy', c='b')
    plt.plot(times, g_act[2], c='b')
    plt.plot(times_c, c_act[:,0], label='Agama,$\Delta='+str(delta)+'$', c='g')
    plt.plot(times_c, c_act[:,1], c='g')
    plt.plot(times_c, a_act[:,0], label='Agama,$\Delta=$auto', c='r')
    plt.plot(times_c, a_act[:,1], c='r')
    plt.text(0, c_act[0,0], '$J_r$', fontsize=16)
    plt.text(0, c_act[0,1], '$J_z$', fontsize=16)
    plt.xlabel("t")
    plt.ylabel("$J_r, J_z$")
    plt.legend(loc='center right')
    plt.xlim(0,50)
    plt.show()
Exemplo n.º 4
0
def compare(ic, inttime, numsteps):
    g_orb_obj = galpy.orbit.Orbit([ic[0],ic[3],ic[5],ic[1],ic[4],ic[2]])
    times = numpy.linspace(0, inttime, numsteps)
    dt = time.time()
    g_orb_obj.integrate(times, g_pot)
    g_orb = g_orb_obj.getOrbit()
    print 'Time to integrate orbit in galpy: %s s' % (time.time()-dt)
    dt = time.time()
    times_c, c_orb_car = agama.orbit(ic=[ic[0],0,ic[1],ic[3],ic[5],ic[4]], potential=w_pot._pot, time=inttime, trajsize=numsteps)
    print 'Time to integrate orbit in Agama: %s s' % (time.time()-dt)
    ### make it compatible with galpy's convention (native output is in cartesian coordinates)
    c_orb = c_orb_car*1.0
    c_orb[:,0] = (c_orb_car[:,0]**2+c_orb_car[:,1]**2)**0.5
    c_orb[:,3] = c_orb_car[:,2]

    ### in galpy, this is the only tool that can estimate focal distance,
    ### but it requires the orbit to be computed first
    delta = estimateDeltaStaeckel(g_pot, g_orb[:,0], g_orb[:,3])
    print "interfocal distance Delta=",delta

    ### plot the orbit(s) in R,z plane, along with the prolate spheroidal coordinate grid
    plt.axes([0.05, 0.55, 0.45, 0.45])
    plotCoords(delta, 1.5)
    plt.plot(g_orb[:,0],g_orb[:,3], 'b', label='galpy')  # R,z
    plt.plot(c_orb[:,0],c_orb[:,3], 'g', label='Agama')  # R,z
    plt.xlabel("R/8kpc")
    plt.ylabel("z/8kpc")
    plt.xlim(0, 1.2)
    plt.ylim(-1,1)
    plt.legend()

    ### plot R(t), z(t)
    plt.axes([0.55, 0.55, 0.45, 0.45])
    plt.plot(times, g_orb[:,0], label='R')
    plt.plot(times, g_orb[:,3], label='z')
    plt.xlabel("t")
    plt.ylabel("R,z")
    plt.legend()
    plt.xlim(0,50)

    ### create galpy action/angle finder for the given value of Delta
    ### note: using c=False in the routine below is much slower but apparently more accurate,
    ### comparable to the Agama for the same value of delta
    g_actfinder = galpy.actionAngle.actionAngleStaeckel(pot=g_pot, delta=delta, c=True)

    ### find the actions for each point of the orbit
    dt = time.time()
    g_act = g_actfinder(g_orb[:,0],g_orb[:,1],g_orb[:,2],g_orb[:,3],g_orb[:,4],fixed_quad=True)
    print 'Time to compute actions in galpy: %s s' % (time.time()-dt)

    ### use the Agama action routine for the same value of Delta as in galpy
    dt = time.time()
    c_act = agama.actions(point=c_orb_car, potential=w_pot._pot, fd=delta)   # explicitly specify focal distance
    print 'Time to compute actions in Agama using Galpy-estimated focal distance: %s s' % (time.time()-dt)

    ### use the Agama action finder (initialized at the beginning) that automatically determines the best value of Delta
    dt = time.time()
    a_act = c_actfinder(c_orb_car)   # use the focal distance estimated by action finder
    print 'Time to compute actions in Agama using pre-initialized focal distance: %s s' % (time.time()-dt)

    ### use the interpolated Agama action finder (initialized at the beginning) - less accurate but faster
    dt = time.time()
    i_act = i_actfinder(c_orb_car)
    print 'Time to compute actions in Agama with interpolated action finder: %s s' % (time.time()-dt)

    ### plot Jr vs Jz
    plt.axes([0.05, 0.05, 0.45, 0.45])
    plt.plot(g_act[0],g_act[2], label='galpy')
    plt.plot(c_act[:,0],c_act[:,1], label=r'Agama,$\Delta='+str(delta)+'$')
    plt.plot(a_act[:,0],a_act[:,1], label=r'Agama,$\Delta=$auto')
    plt.plot(i_act[:,0],i_act[:,1], label=r'Agama,interpolated')
    plt.xlabel("$J_r$")
    plt.ylabel("$J_z$")
    plt.legend(loc='lower left')

    ### plot Jr(t) and Jz(t)
    plt.axes([0.55, 0.05, 0.45, 0.45])
    plt.plot(times, g_act[0], label='galpy', c='b')
    plt.plot(times, g_act[2], c='b')
    plt.plot(times_c, c_act[:,0], label='Agama,$\Delta='+str(delta)+'$', c='g')
    plt.plot(times_c, c_act[:,1], c='g')
    plt.plot(times_c, a_act[:,0], label='Agama,$\Delta=$auto', c='r')
    plt.plot(times_c, a_act[:,1], c='r')
    plt.plot(times_c, i_act[:,0], label='Agama,interpolated', c='c')
    plt.plot(times_c, i_act[:,1], c='c')
    plt.text(0, c_act[0,0], '$J_r$', fontsize=16)
    plt.text(0, c_act[0,1], '$J_z$', fontsize=16)
    plt.xlabel("t")
    plt.ylabel("$J_r, J_z$")
    plt.legend(loc='center right')
    plt.ylim(0.14,0.25)
    plt.xlim(0,50)
    plt.show()
Exemplo n.º 5
0
def compare(ic, inttime, numsteps):
    times = numpy.linspace(0, inttime, numsteps)

    ### integrate the orbit in galpy using MWPotential2014 from galpy
    g_orb_obj = galpy.orbit.Orbit([ic[0], ic[3], ic[5], ic[1], ic[4], ic[2]])
    dt = time.time()
    g_orb_obj.integrate(times, g_pot)
    g_orb = g_orb_obj.getOrbit()
    print('Time to integrate orbit in galpy: %.4g s' % (time.time() - dt))

    ### integrate the orbit with the galpy routine, but using Agama potential instead
    ### (much slower because of repeated transfer of control between C++ and Python
    dt = time.time()
    g_orb_obj.integrate(times[:numsteps // 10], a_pot)
    a_orb = g_orb_obj.getOrbit()
    print(
        'Time to integrate 1/10th of the orbit in galpy using Agama potential: %.4g s'
        % (time.time() - dt))

    ### integrate the same orbit (note different calling conventions - cartesian coordinates as input)
    ### using both the orbit integration routine and the potential from Agama - much faster
    dt = time.time()
    times_c, c_orb_car = agama.orbit(ic=[ic[0],0,ic[1],ic[3],ic[5],ic[4]], \
        potential=a_pot, time=inttime, trajsize=numsteps)
    print('Time to integrate orbit in Agama: %.4g s' % (time.time() - dt))

    ### make it compatible with galpy's convention (native output is in cartesian coordinates)
    c_orb = c_orb_car * 1.0
    c_orb[:, 0] = (c_orb_car[:, 0]**2 + c_orb_car[:, 1]**2)**0.5
    c_orb[:, 3] = c_orb_car[:, 2]

    ### in galpy, this is the only tool that can estimate focal distance,
    ### but it requires the orbit to be computed first
    delta = float(
        galpy.actionAngle.estimateDeltaStaeckel(g_pot, g_orb[:, 0], g_orb[:,
                                                                          3]))
    print('Focal distance estimated from the entire trajectory: Delta=%.4g' %
          delta)

    ### plot the orbit(s) in R,z plane, along with the prolate spheroidal coordinate grid
    plt.figure(figsize=(12, 8))
    plt.axes([0.04, 0.54, 0.45, 0.45])
    plotCoords(delta, 1.5)
    plt.plot(g_orb[:, 0], g_orb[:, 3], 'b', label='galpy')  # R,z
    plt.plot(c_orb[:, 0], c_orb[:, 3], 'g', label='Agama')
    plt.plot(a_orb[:, 0],
             a_orb[:, 3],
             'r',
             label='galpy using Agama potential')
    plt.xlabel("R/8kpc")
    plt.ylabel("z/8kpc")
    plt.xlim(0, 1.2)
    plt.ylim(-1, 1)
    plt.legend(loc='lower left', frameon=False)

    ### create galpy action/angle finder for the given value of Delta
    ### note: using c=False in the routine below is much slower but apparently more accurate,
    ### comparable to the Agama for the same value of delta
    g_actfinder = galpy.actionAngle.actionAngleStaeckel(pot=g_pot,
                                                        delta=delta,
                                                        c=True)

    ### find the actions for each point of the orbit using galpy action finder
    dt = time.time()
    g_act = g_actfinder(g_orb[:, 0],
                        g_orb[:, 1],
                        g_orb[:, 2],
                        g_orb[:, 3],
                        g_orb[:, 4],
                        fixed_quad=True)
    print('Time to compute actions in galpy: %.4g s' % (time.time() - dt))
    print('Jr = %.6g +- %.4g, Jz = %.6g +- %.4g' % \
        (numpy.mean(g_act[0]), numpy.std(g_act[0]), numpy.mean(g_act[2]), numpy.std(g_act[2])))

    ### use the Agama action routine for the same value of Delta as in galpy -
    ### the result is almost identical but computed much faster
    dt = time.time()
    c_act = agama.actions(point=c_orb_car, potential=a_pot,
                          fd=delta)  # explicitly specify focal distance
    print(
        'Time to compute actions in Agama using Galpy-estimated focal distance: %.4g s'
        % (time.time() - dt))
    print('Jr = %.6g +- %.4g, Jz = %.6g +- %.4g' % \
        (numpy.mean(c_act[:,0]), numpy.std(c_act[:,0]), numpy.mean(c_act[:,1]), numpy.std(c_act[:,1])))

    ### use the Agama action finder (initialized at the beginning) that automatically determines
    ### the best value of Delta (same computational cost as the previous one)
    dt = time.time()
    a_act = a_actfinder(
        c_orb_car)  # use the focal distance estimated by action finder
    print(
        'Time to compute actions in Agama using pre-initialized focal distance: %.4g s'
        % (time.time() - dt))
    print('Jr = %.6g +- %.4g, Jz = %.6g +- %.4g' % \
        (numpy.mean(a_act[:,0]), numpy.std(a_act[:,0]), numpy.mean(a_act[:,1]), numpy.std(a_act[:,1])))

    ### use the interpolated Agama action finder (initialized at the beginning) - less accurate but faster
    dt = time.time()
    i_act = i_actfinder(c_orb_car)
    print(
        'Time to compute actions in Agama with interpolated action finder: %.4g s'
        % (time.time() - dt))
    print('Jr = %.6g +- %.4g, Jz = %.6g +- %.4g' % \
        (numpy.mean(i_act[:,0]), numpy.std(i_act[:,0]), numpy.mean(i_act[:,1]), numpy.std(i_act[:,1])))

    ### plot Jr vs Jz
    plt.axes([0.54, 0.54, 0.45, 0.45])
    plt.plot(g_act[0], g_act[2], c='b', label='galpy')
    plt.plot(c_act[:, 0],
             c_act[:, 1],
             c='g',
             label=r'Agama,$\Delta=%.4f$' % delta)
    plt.plot(a_act[:, 0], a_act[:, 1], c='r', label=r'Agama,$\Delta=$auto')
    plt.plot(i_act[:, 0], i_act[:, 1], c='c', label=r'Agama,interpolated')
    plt.xlabel("$J_r$")
    plt.ylabel("$J_z$")
    plt.legend(loc='lower left', frameon=False)

    ### plot Jr(t) and Jz(t)
    plt.axes([0.04, 0.04, 0.95, 0.45])
    plt.plot(times, g_act[0], c='b', label='galpy')
    plt.plot(times, g_act[2], c='b')
    plt.plot(times_c, c_act[:, 0], c='g', label='Agama,$\Delta=%.4f$' % delta)
    plt.plot(times_c, c_act[:, 1], c='g')
    plt.plot(times_c, a_act[:, 0], c='r', label='Agama,$\Delta=$auto')
    plt.plot(times_c, a_act[:, 1], c='r')
    plt.plot(times_c, i_act[:, 0], c='c', label='Agama,interpolated')
    plt.plot(times_c, i_act[:, 1], c='c')
    plt.text(0, c_act[0, 0], '$J_r$', fontsize=16)
    plt.text(0, c_act[0, 1], '$J_z$', fontsize=16)
    plt.xlabel("t")
    plt.ylabel("$J_r, J_z$")
    plt.legend(loc='center right', ncol=2, frameon=False)
    #plt.ylim(0.14,0.25)
    plt.xlim(0, 50)
    plt.show()
def compare(ic, inttime, numsteps):
    times = numpy.linspace(0, inttime, numsteps)
    times1 = times[:numsteps //
                   10]  # compute only 1/10th of the orbit to save time in galpy

    ### integrate the orbit in galpy using the native MWPotential2014 from galpy
    g_orb_obj = galpy.orbit.Orbit(ic)
    dt = time.time()
    g_orb_obj.integrate(times, g_pot_native)
    g_orb_g_native = g_orb_obj.getOrbit()
    print(
        'Time to integrate the orbit in galpy, using galpy-native potential [1]: %.4g s'
        % (time.time() - dt))

    ### integrate the orbit again with the galpy routine, but now using the chimera potential
    ### representing a galpy-native underlying potential.
    ### since galpy has no idea that this potential has a C implementation, it switches to a slower
    ### pure-Python integration algorithm, so to save time, we compute only 1/10th of the orbit
    dt = time.time()
    g_orb_obj.integrate(times1, g_pot_hybrid)
    g_orb_g_hybrid = g_orb_obj.getOrbit()
    print(
        'Time to integrate 1/10th of the orbit in galpy, using galpy-hybrid potential [2]: %.4g s'
        % (time.time() - dt))

    ### integrate the orbit with the galpy routine, but using the chimera potential
    ### representing an agama-native underlying potential instead
    ### (also much slower because of repeated transfer of control between C++ and Python)
    dt = time.time()
    g_orb_obj.integrate(times1, a_pot_hybrid)
    g_orb_a_hybrid = g_orb_obj.getOrbit()
    print(
        'Time to integrate 1/10th of the orbit in galpy, using agama-hybrid potential [3]: %.4g s'
        % (time.time() - dt))

    ### integrate the orbit with the agama routine, using the galpy-native potential through the chimera
    ### (note different calling conventions and the use of cartesian coordinates)
    dt = time.time()
    a_orb_g_hybrid = agama.orbit(potential=g_pot_hybrid,
                                 time=inttime,
                                 trajsize=numsteps,
                                 ic=toCar(ic))[1]
    print(
        'Time to integrate the orbit in agama, using galpy-hybrid potential [4]: %.4g s'
        % (time.time() - dt))

    ### integrate the orbit in a native agama potential approximation constructed from the galpy potential
    dt = time.time()
    a_orb_g_approx = agama.orbit(potential=g_pot_approx,
                                 time=inttime,
                                 trajsize=numsteps,
                                 ic=toCar(ic))[1]
    print(
        'Time to integrate the orbit in agama, using galpy-approx potential [5]: %.4g s'
        % (time.time() - dt))

    ### using both the orbit integration routine and the native potential from agama - much faster
    dt = time.time()
    a_orb_a_native = agama.orbit(potential=a_pot_native,
                                 time=inttime,
                                 trajsize=numsteps,
                                 ic=toCar(ic))[1]
    print(
        'Time to integrate the orbit in agama, using agama-native potential [6]: %.4g s'
        % (time.time() - dt))

    ### the same but with the chimera potential representing an agama-native potential
    ### (should be identical to the above, since in both cases the same underlying C++ object is used)
    dt = time.time()
    a_orb_a_hybrid = agama.orbit(potential=a_pot_hybrid,
                                 time=inttime,
                                 trajsize=numsteps,
                                 ic=toCar(ic))[1]
    print(
        'Time to integrate the orbit in agama, using agama-hybrid potential [7]: %.4g s'
        % (time.time() - dt))

    ### compare the differences between the orbits computed in different ways
    ### (both the potentials and integration routines are not exactly equivalent);
    ### use only common initial segment (1/10th of the orbit) for comparison
    print('Differences between orbits: ' + '[1]-[2]=%g, ' % numpy.max(
        numpy.abs(toCar(g_orb_g_native[:len(times1)]) - toCar(g_orb_g_hybrid))
    ) + '[1]-[3]=%g, ' % numpy.max(
        numpy.abs(toCar(g_orb_g_native[:len(times1)]) - toCar(g_orb_a_hybrid)))
          + '[1]-[4]=%g, ' % numpy.max(
              numpy.abs(toCar(g_orb_g_native) - a_orb_g_hybrid)[:len(times1)])
          + '[1]-[5]=%g, ' % numpy.max(
              numpy.abs(toCar(g_orb_g_native) - a_orb_g_approx)[:len(times1)])
          + '[1]-[6]=%g, ' % numpy.max(
              numpy.abs(toCar(g_orb_g_native) - a_orb_a_native)[:len(times1)])
          + '[6]-[4]=%g, ' %
          numpy.max(numpy.abs(a_orb_a_native - a_orb_g_hybrid)[:len(times1)]) +
          '[6]-[7]=%g' %
          numpy.max(numpy.abs(a_orb_a_native - a_orb_a_hybrid)[:len(times1)])
          )  # should be zero

    ### convert the orbits to the same galpy coordinate convention
    gg_orb = g_orb_g_native  # it's already in this convention
    ga_orb = g_orb_a_hybrid
    ag_orb = toCyl(a_orb_g_hybrid)
    aa_orb = toCyl(a_orb_a_native)
    ### in galpy, this is the only tool that can estimate focal distance,
    ### but it requires the orbit to be computed first
    delta = float(
        galpy.actionAngle.estimateDeltaStaeckel(g_pot_hybrid, gg_orb[:, 0],
                                                gg_orb[:, 3]))
    print('Focal distance estimated from the entire trajectory: Delta=%.4g' %
          delta)

    ### plot the orbit(s) in R,z plane, along with the prolate spheroidal coordinate grid
    plt.figure(figsize=(12, 8))
    plt.axes([0.06, 0.56, 0.43, 0.43])
    plotCoords(delta, 1.5)
    plt.plot(gg_orb[:, 0], gg_orb[:, 3], 'b', label='galpy native')  # R,z
    plt.plot(aa_orb[:, 0],
             aa_orb[:, 3],
             'g',
             label='agama native',
             dashes=[4, 2])
    plt.plot(ag_orb[:, 0],
             ag_orb[:, 3],
             'y',
             label='agama using galpy potential',
             dashes=[1, 1])
    plt.plot(ga_orb[:, 0],
             ga_orb[:, 3],
             'r',
             label='galpy using agama potential')
    plt.xlabel("R/8kpc")
    plt.ylabel("z/8kpc")
    plt.xlim(0, 1.2)
    plt.ylim(-1, 1)
    plt.legend(loc='lower left', ncol=2)

    ### create galpy action/angle finder for the given value of Delta
    ### note: using c=False in the routine below is much slower but apparently more accurate,
    ### comparable to the agama for the same value of delta
    g_actfinder = galpy.actionAngle.actionAngleStaeckel(pot=g_pot_native,
                                                        delta=delta,
                                                        c=True)

    ### find the actions for each point of the orbit using galpy action finder
    dt = time.time()
    g_act = g_actfinder(gg_orb[:, 0],
                        gg_orb[:, 1],
                        gg_orb[:, 2],
                        gg_orb[:, 3],
                        gg_orb[:, 4],
                        fixed_quad=True)
    print('Time to compute actions in galpy: %.4g s' % (time.time() - dt))
    print('Jr = %.6g +- %.4g, Jz = %.6g +- %.4g' % \
        (numpy.mean(g_act[0]), numpy.std(g_act[0]), numpy.mean(g_act[2]), numpy.std(g_act[2])))

    ### use the agama action routine for the same value of Delta as in galpy (explicity specify focal distance):
    ### the result is almost identical but computed much faster
    dt = time.time()
    c_act = agama.actions(point=a_orb_a_hybrid,
                          potential=a_pot_hybrid,
                          fd=delta)
    print(
        'Time to compute actions in agama using galpy-estimated focal distance: %.4g s'
        % (time.time() - dt))
    print('Jr = %.6g +- %.4g, Jz = %.6g +- %.4g' % \
        (numpy.mean(c_act[:,0]), numpy.std(c_act[:,0]), numpy.mean(c_act[:,1]), numpy.std(c_act[:,1])))

    ### use the agama action finder (initialized at the beginning) that automatically determines
    ### the best value of Delta (same computational cost as the previous one)
    dt = time.time()
    a_act = a_actfinder(
        a_orb_a_hybrid)  # use the focal distance estimated by action finder
    print(
        'Time to compute actions in agama using pre-initialized focal distance: %.4g s'
        % (time.time() - dt))
    print('Jr = %.6g +- %.4g, Jz = %.6g +- %.4g' % \
        (numpy.mean(a_act[:,0]), numpy.std(a_act[:,0]), numpy.mean(a_act[:,1]), numpy.std(a_act[:,1])))

    ### use the interpolated agama action finder (initialized at the beginning) - less accurate but faster
    dt = time.time()
    i_act = i_actfinder(a_orb_a_hybrid)
    print(
        'Time to compute actions in agama with interpolated action finder: %.4g s'
        % (time.time() - dt))
    print('Jr = %.6g +- %.4g, Jz = %.6g +- %.4g' % \
        (numpy.mean(i_act[:,0]), numpy.std(i_act[:,0]), numpy.mean(i_act[:,1]), numpy.std(i_act[:,1])))

    ### plot Jr vs Jz
    plt.axes([0.55, 0.56, 0.43, 0.43])
    plt.plot(g_act[0], g_act[2], c='b', label='galpy')
    plt.plot(c_act[:, 0],
             c_act[:, 1],
             c='g',
             label=r'agama, $\Delta=%.4f$' % delta,
             dashes=[4, 2])
    plt.plot(a_act[:, 0], a_act[:, 1], c='r', label=r'agama, $\Delta=$auto')
    plt.plot(i_act[:, 0], i_act[:, 1], c='c', label=r'agama, interpolated')
    plt.xlabel("$J_r$")
    plt.ylabel("$J_z$")
    plt.legend(loc='lower left', frameon=False)

    ### plot Jr(t) and Jz(t)
    plt.axes([0.06, 0.06, 0.92, 0.43])
    plt.plot(times, g_act[0], c='b', label='galpy')
    plt.plot(times, g_act[2], c='b')
    plt.plot(times,
             c_act[:, 0],
             c='g',
             label='agama, $\Delta=%.4f$' % delta,
             dashes=[4, 2])
    plt.plot(times, c_act[:, 1], c='g', dashes=[4, 2])
    plt.plot(times, a_act[:, 0], c='r', label='agama, $\Delta=$auto')
    plt.plot(times, a_act[:, 1], c='r')
    plt.plot(times, i_act[:, 0], c='c', label='agama, interpolated')
    plt.plot(times, i_act[:, 1], c='c')
    plt.text(0, c_act[0, 0], '$J_r$', fontsize=16)
    plt.text(0, c_act[0, 1], '$J_z$', fontsize=16)
    plt.xlabel("t")
    plt.ylabel("$J_r, J_z$")
    plt.legend(loc='center right', ncol=2, frameon=False)
    #plt.ylim(0.14,0.25)
    plt.xlim(0, 50)
    plt.show()