def my_moments(potential, df, point): # create an action finder to transform from position+velocity to actions af = agama.ActionFinder(potential) # function to be integrated over [scaled] velocity def integrand(scaledv): # input is a Nx3 array of velocity values in polar coordinates (|v|, theta, phi) sintheta = numpy.sin(scaledv[:, 1]) posvel = numpy.column_stack(( \ numpy.tile(point, len(scaledv)).reshape(-1,3), \ scaledv[:,0] * sintheta * numpy.cos(scaledv[:,2]), \ scaledv[:,0] * sintheta * numpy.sin(scaledv[:,2]), \ scaledv[:,0] * numpy.cos(scaledv[:,1]) )) jacobian = scaledv[:, 0]**2 * sintheta # jacobian of the above transformation actions = af(posvel) # compute actions at the given points return df( actions ) * jacobian # and return the values of DF times the jacobian # integration region: |v| from 0 to escape velocity, theta and phi are angles of spherical coords v_esc = (-2 * potential.potential(point))**0.5 result, error, neval = agama.integrateNdim(integrand, [0, 0, 0], [v_esc, numpy.pi, 2 * numpy.pi], toler=1e-5) return result
def main(): pot = agama.Potential(type="Dehnen", mass=1, scaleRadius=1.) actf = agama.ActionFinder(pot) particles, masses = createHernquistModel(100000) actions = actf(particles) # do a parameter search to find best-fit distribution function describing these particles initparams = numpy.array([2.0, 4.0, 1.0, 1.0, 0.0]) result = minimize(model_search_fnc, initparams, args=(actions, ), method='Nelder-Mead', options=dict(maxiter=1000, maxfev=1000, disp=True)) # explore the parameter space around the best-fit values using the MCMC chain try: import matplotlib.pyplot as plt, emcee, corner except ImportError as ex: print ex, "\nYou need to install 'emcee' and 'corner' packages" print 'Starting MCMC' ndim = len(initparams) nwalkers = 16 # number of parallel walkers in the chain nsteps = 300 # number of steps in MCMC chain nburnin = 100 # number of initial steps to discard # initial coverage of parameter space - around the best-fit solution with a small dispersion initwalkers = [ result.x + 0.01 * numpy.random.randn(ndim) for i in range(nwalkers) ] sampler = emcee.EnsembleSampler(nwalkers, ndim, model_search_emcee, args=(actions, )) sampler.run_mcmc(initwalkers, nsteps) # show the time evolution of parameters carried by the ensemble of walkers (time=number of MC steps) fig, axes = plt.subplots(ndim + 1, 1, sharex=True) for i in range(ndim): axes[i].plot(sampler.chain[:, :, i].T, color='k', alpha=0.5) axes[i].set_ylabel(labels[i]) # last panel shows the evolution of log-likelihood for the ensemble of walkers axes[-1].plot(sampler.lnprobability.T, color='k', alpha=0.5) axes[-1].set_ylabel('log(L)') maxloglike = numpy.max(sampler.lnprobability) axes[-1].set_ylim(maxloglike - 3 * ndim, maxloglike) fig.tight_layout(h_pad=0.) plt.show() # show the posterior distribution of parameters samples = sampler.chain[:, nburnin:, :].reshape((-1, ndim)) trueval = (1.56, 1.55, 5.29, 1.22, 1.56) # believed to be best-fit values corner.corner(samples, \ labels=labels, quantiles=[0.16, 0.5, 0.84], truths=trueval) plt.show() print "Acceptance fraction: ", numpy.mean( sampler.acceptance_fraction) # should be in the range 0.2-0.5 print "Autocorrelation time: ", sampler.acor # should be considerably shorter than the total number of steps
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 iterate(self): if len(self.components) == 0: raise TypeError("'components' is an empty list") # prepare ground: make sure the potential and the corresponding action finder are defined if self.potential is None: self.updatePotential() elif self.af is None: self.af = agama.ActionFinder(self.potential) # compute the density of all components for ic, component in enumerate(self.components): print("Computing density for component %i..." % ic) component.update(self.potential, self.af) # update the total potential and the corresponding action finder self.updatePotential()
def updatePotential(self): print("Updating potential...") # sort out density and potential components into several groups densitySph = [] densityCyl = [] potentials = [] for component in self.components: dens = component.getDensity() if dens is not None: if component.disklike: densityCyl.append(dens) else: densitySph.append(dens) else: potentials.append(component.getPotential()) # create a single Multipole potential for all non-disk-like density components if len(densitySph) > 0: potentials.append( agama.Potential(type='Multipole', density=agama.Density(*densitySph), gridsizer=self.sizeradialsph, rmin=self.rminsph, rmax=self.rmaxsph, lmax=self.lmaxangularsph, mmax=0, symmetry='a')) # create a single CylSpline potential representing all disk-like density components if len(densityCyl) > 0: potentials.append( agama.Potential(type='CylSpline', density=agama.Density(*densityCyl), gridsizer=self.sizeradialcyl, rmin=self.rmincyl, rmax=self.rmaxcyl, gridsizez=self.sizeverticalcyl, zmin=self.zmincyl, zmax=self.zmaxcyl, mmax=0, symmetry='a')) # combine all potential components and reinitialize the action finder self.potential = agama.Potential(*potentials) print("Updating action finder...") self.af = agama.ActionFinder(self.potential)
diskPot = agama.Potential( \ type="CylSpline", particles=(diskParticles[:,0:3], diskParticles[:,6]), \ gridsizer=20, gridsizez=20, mmax=0, Rmin=0.2, Rmax=100, Zmin=0.05, Zmax=50) print("%f s to init %s potential for the disk; value at origin=%f (km/s)^2" % \ ((time.clock()-tbegin), diskPot.name(), diskPot.potential(0,0,0))) # save the potentials into text files; on the next call may load them instead of re-computing diskPot.export("model_stars_final.pot") haloPot.export("model_dm_final.pot") #3c. combine the two potentials into a single composite one totalPot = agama.Potential(diskPot, haloPot) #4. compute actions for disk particles tbegin = time.clock() actFinder = agama.ActionFinder(totalPot) print("%f s to init action finder" % (time.clock() - tbegin)) tbegin = time.clock() actions = actFinder(diskParticles[:, 0:6]) print("%f s to compute actions for %i particles" % (time.clock() - tbegin, diskParticles.shape[0])) #5. write out data Rz = numpy.vstack((numpy.sqrt(diskParticles[:, 0]**2 + diskParticles[:, 1]**2), diskParticles[:, 2])).T energy = (totalPot.potential(diskParticles[:,0:3]) + \ 0.5 * numpy.sum(diskParticles[:,3:6]**2, axis=1) ).reshape(-1,1) numpy.savetxt( "disk_actions.txt", numpy.hstack((Rz, actions, energy)), \ header="R[Kpc]\tz[Kpc]\tJ_r[Kpc*km/s]\tJ_z[Kpc*km/s]\tJ_phi[Kpc*km/s]\tE[(km/s)^2]", \ fmt="%.6g", delimiter="\t" )
R_z = np.array([[np.cos(theta[2]), -np.sin(theta[2]), 0], [np.sin(theta[2]), np.cos(theta[2]), 0], [0, 0, 1]]) R = np.dot(R_x, np.dot(R_y, R_z)) # this is the real one # R = np.dot(R_z, np.dot( R_y, R_x )) return R def euler_rotate(theta, vec): mat = euler_matrix(theta) return np.transpose(np.tensordot(mat, np.transpose(vec), axes=1)) potential = agama.Potential(file='fiducial_pot') af = agama.ActionFinder(potential, interp=False) # read in simulated positions and velocities posfile = 'pos_' + sys.argv[1] + '.npy' velfile = 'vel_' + sys.argv[1] + '.npy' pos_full = np.load(posfile) vel_full = np.load(velfile) global nframes, npart, ndim, pos, vel def init_minimizer(cadence): pos = pos_full[::cadence] vel = vel_full[::cadence] nframes, npart, ndim = np.shape(pos)
def main(args: Optional[list] = None, opts: Optional[argparse.Namespace] = None): """Script Function. Parameters ---------- args : list, optional an optional single argument that holds the sys.argv list, except for the script name (e.g., argv[1:]) opts : Namespace, optional pre-constructed results of parsed args if not None, used ONLY if args is None """ if opts is not None and args is None: pass else: if opts is not None: warnings.warn("Not using `opts` because `args` are given") parser = make_parser() opts = parser.parse_args(args) foutname = DATA + "result_orbits.txt" if not os.path.isfile(foutname): # STEP 1: create Monte Carlo realizations of position and velocity of each cluster, # sampling from their measured uncertainties. # this file should have been produced by run_fit.py tab = np.loadtxt(DATA + "summary.txt", dtype=str) names = tab[:, 0] # 0th column is the cluster name (string) tab = tab[:, 1:].astype(float) # remaining columns are numbers ra0 = tab[:, 0] # coordinates of cluster centers [deg] dec0 = tab[:, 1] dist0 = tab[:, 2] # distance [kpc] vlos0 = tab[:, 3] # line-of-sight velocity [km/s] vlose = tab[:, 4] # its error estimate pmra0 = tab[:, 7] # mean proper motion [mas/yr] pmdec0 = tab[:, 8] pmrae = tab[:, 9] # its uncertainty pmdece = tab[:, 10] pmcorr = tab[:, 11] # correlation coefficient for errors in two PM components vlose = np.maximum( vlose, 2.0) # assumed error of at least 2 km/s on line-of-sight velocity diste = (dist0 * 0.46 * 0.1 ) # assumed error of 0.1 mag in distance modulus # create bootstrap samples np.random.seed(42) # ensure repeatability of random samples nboot = 100 # number of bootstrap samples for each cluster nclust = len(tab) ra = np.repeat(ra0, nboot) dec = np.repeat(dec0, nboot) pmra = np.repeat(pmra0, nboot) pmdec = np.repeat(pmdec0, nboot) for i in range(nclust): # draw PM realizations from a correlated 2d gaussian for each cluster A = np.random.normal(size=nboot) B = (np.random.normal(size=nboot) * (1 - pmcorr[i]**2)**0.5 + A * pmcorr[i]) pmra[i * nboot:(i + 1) * nboot] += pmrae[i] * A pmdec[i * nboot:(i + 1) * nboot] += pmdece[i] * B vlos = np.repeat(vlos0, nboot) + np.hstack( [np.random.normal(scale=e, size=nboot) for e in vlose]) dist = np.repeat(dist0, nboot) + np.hstack( [np.random.normal(scale=e, size=nboot) for e in diste]) # convert coordinates from heliocentric (ra,dec,dist,PM,vlos) to Galactocentric (kpc and km/s) u.kms = u.km / u.s c_sky = coord.ICRS( ra=ra * u.degree, dec=dec * u.degree, pm_ra_cosdec=pmra * u.mas / u.yr, pm_dec=pmdec * u.mas / u.yr, distance=dist * u.kpc, radial_velocity=vlos * u.kms, ) c_gal = c_sky.transform_to( coord.Galactocentric( galcen_distance=8.2 * u.kpc, galcen_v_sun=coord.CartesianDifferential([10.0, 248.0, 7.0] * u.kms), )) pos = np.column_stack( (c_gal.x / u.kpc, c_gal.y / u.kpc, c_gal.z / u.kpc)) vel = np.column_stack( (c_gal.v_x / u.kms, c_gal.v_y / u.kms, c_gal.v_z / u.kms)) # add uncertainties from the solar position and velocity pos[:, 0] += np.random.normal( scale=0.1, size=nboot * nclust) # uncertainty in solar distance from Galactic center vel[:, 0] += np.random.normal(scale=1.0, size=nboot * nclust) # uncertainty in solar velocity vel[:, 1] += np.random.normal(scale=3.0, size=nboot * nclust) vel[:, 2] += np.random.normal(scale=1.0, size=nboot * nclust) pos[:, 0] *= ( -1 ) # revert back to normal orientation of coordinate system (solar position at x=+8.2) vel[:, 0] *= -1 # same for velocity posvel = np.column_stack((pos, vel)).value np.savetxt(DATA + "posvel.txt", posvel, fmt="%.6g") # STEP 2: compute the orbits, min/max galactocentric radii, and actions, for all Monte Carlo samples print(agama.setUnits( length=1, velocity=1, mass=1)) # units: kpc, km/s, Msun; time unit ~ 1 Gyr potential = agama.Potential( DATA + "McMillan17.ini") # MW potential from McMillan(2017) # compute orbits for each realization of initial conditions, # integrated for 100 dynamical times or 20 Gyr (whichever is lower) print( "Computing orbits for %d realizations of cluster initial conditions" % len(posvel)) inttime = np.minimum(20.0, potential.Tcirc(posvel) * 100) orbits = agama.orbit(ic=posvel, potential=potential, time=inttime, trajsize=1000)[:, 1] rmin = np.zeros(len(orbits)) rmax = np.zeros(len(orbits)) for i, o in enumerate(orbits): r = np.sum(o[:, 0:3]**2, axis=1)**0.5 rmin[i] = np.min(r) if len(r) > 0 else np.nan rmax[i] = np.max(r) if len(r) > 0 else np.nan # replace nboot samples rmin/rmax with their median and 68% confidence intervals for each cluster rmin = np.nanpercentile(rmin.reshape(nclust, nboot), [16, 50, 84], axis=1) rmax = np.nanpercentile(rmax.reshape(nclust, nboot), [16, 50, 84], axis=1) # compute actions for the same initial conditions actfinder = agama.ActionFinder(potential) actions = actfinder(posvel) # again compute the median and 68% confidence intervals for each cluster actions = np.nanpercentile(actions.reshape(nclust, nboot, 3), [16, 50, 84], axis=1) # compute the same confidence intervals for the total energy energy = potential.potential( posvel[:, 0:3]) + 0.5 * np.sum(posvel[:, 3:6]**2, axis=1) energy = np.percentile(energy.reshape(nclust, nboot), [16, 50, 84], axis=1) # write the orbit parameters, actions and energy - one line per cluster, with the median and uncertainties fileout = open(foutname, "w") fileout.write( "# Name \t pericenter[kpc] \t apocenter[kpc] \t" + " Jr[kpc*km/s] \t Jz[kpc*km/s] \t Jphi[kpc*km/s] \t Energy[km^2/s^2] \n" ) for i in range(nclust): fileout.write(("%-15s" + "\t%7.2f" * 6 + "\t%7.0f" * 12 + "\n") % ( names[i], rmin[0, i], rmin[1, i], rmin[2, i], rmax[0, i], rmax[1, i], rmax[2, i], actions[0, i, 0], actions[1, i, 0], actions[2, i, 0], actions[0, i, 1], actions[1, i, 1], actions[2, i, 1], actions[0, i, 2], actions[1, i, 2], actions[2, i, 2], energy[0, i], energy[1, i], energy[2, i], )) fileout.close()
agama.setUnits(mass=1, length=1, velocity=1) # import the MW potential from gala bulge = agama.Potential(type='Dehnen', gamma=1, mass=5E9, scaleRadius=1.0) nucleus = agama.Potential(type='Dehnen', gamma=1, mass=1.71E09, scaleRadius=0.07) disk = agama.Potential(type='MiyamotoNagai', mass=6.80e+10, scaleRadius=3.0, scaleHeight=0.28) halo = agama.Potential(type='NFW', mass=5.4E11, scaleRadius=15.62) mwpot = agama.Potential(bulge, nucleus, disk, halo) af = agama.ActionFinder(mwpot, interp=False) pos_vel = np.array([8., 0., 0., 75., 150., 50.]) agama_act = af(pos_vel) # now do it for gala pot = gp.MilkyWayPotential() w0 = gd.PhaseSpacePosition(pos=[8, 0, 0.] * u.kpc, vel=[75, 150, 50.] * u.km / u.s) w = gp.Hamiltonian(pot).integrate_orbit(w0, dt=0.5, n_steps=10000) gala_act = gd.find_actions(w, N_max=8)
def isTuple(obj, length): return isinstance(obj, tuple) and len(obj) == length def isArray(obj, shape): return isinstance(obj, numpy.ndarray) and obj.shape == shape # set up some non-trivial dimensional units agama.setUnits(length=2, velocity=3, mass=4e6) dens = agama.Density(type='plummer') pots = agama.Potential(type='dehnen', gamma=0) # spherical potential potf = agama.Potential(type='plummer', q=0.75) # flattened potential pott = agama.Potential(type='plummer', p=0.75, q=0.5) # triaxial potential actf = agama.ActionFinder(potf) actm = agama.ActionMapper(potf, [1, 1, 1]) df0 = agama.DistributionFunction(type='quasispherical', density=pots, potential=pots, r_a=2.0) df1 = agama.DistributionFunction(type='quasispherical', density=dens, potential=pots, beta0=-0.2) df2 = agama.DistributionFunction(df1, df0) # composite DF with two components gms1 = agama.GalaxyModel(pots, df1) # simple DF, spherical potential gms2 = agama.GalaxyModel(pots, df2) # composite DF (2 components), spherical gmf1 = agama.GalaxyModel(potf, df1) # simple, flattened Phi0 = pots.potential(0, 0, 0) # value of the potential at origin
###2. set up equivalent potential from the Agama library agama.setUnits( mass=1., length=8., velocity=345.67) p_bulge = {"type":"SpheroidDensity", "densityNorm":6.669e9, "gamma":1.8, "beta":1.8, "scaleRadius":1, "outerCutoffRadius":1.9/8}; p_disk = {"type":"MiyamotoNagai", "mass":1.678e11, "scaleradius":3./8, "scaleheight":0.28/8}; p_halo = {"type":"SpheroidDensity", "densityNorm":1.072e10, "gamma":1.0, "beta":3.0, "scaleRadius":2.}; ### one can create the genuine instance of Agama potential as follows: #c_pot = agama.Potential("../data/MWPotential2014.ini") # read parameters from ini file #c_pot = agama.Potential(p_bulge, p_disk, p_halo) # or create potential from a list of parameters ### or one can instead create a galpy-compatible potential as follows: w_pot = galpy_agama.CPotential(p_bulge, p_disk, p_halo) # same as above, two variants ### ...and then use _pot member variable to access the instance of raw Agama potential dt = time.time() ### initialization of the action finder needs to be done once for the given potential c_actfinder = agama.ActionFinder(w_pot._pot, interp=False) print 'Time to set up action finder: %s s' % (time.time()-dt) ### we have a faster but less accurate "interpolated action finder", which takes a bit longer to initialize i_actfinder = agama.ActionFinder(w_pot._pot, interp=True) print 'Time to set up interpolated action finder: %s s' % (time.time()-dt) ### conversion from prolate spheroidal to cylindrical coords def ProlSphToCyl(la, nu, ifd): return ( ((la - ifd*ifd) * (1 - abs(nu)/ifd**2))**0.5, (la*abs(nu))**0.5 / ifd * numpy.sign(nu) ) ### show coordinate grid in prolate spheroidal coords def plotCoords(ifd, maxR): la = numpy.linspace(0, maxR, 32)**2 + ifd**2 ls = numpy.linspace(0, 1, 21) nu = ls*ls*(3-2*ls)*ifd**2 for i in range(len(la)):
### instead we use a Multipole expansion (which never needs the value of the original potential at 0); ### it is less accurate in this case, but still acceptable #g_pot_approx = agama.Potential(type='CylSpline', potential=g_pot_hybrid, symmetry='axi', rmin=0.01, rmax=10, zmin=0.01, zmax=10) g_pot_approx = agama.Potential(type='Multipole', potential=g_pot_hybrid, symmetry='axi', rmin=0.01, rmax=10, lmax=20) #g_pot_approx.export('example_galpy.ini') # may save the potential coefs for later use print('Time to set up a CylSpline approximation to galpy potential: %.4g s' % (time.time() - dt)) ### initialization of the action finder needs to be done once for the given potential dt = time.time() a_actfinder = agama.ActionFinder(a_pot_hybrid, interp=False) print('Time to set up agama action finder: %.4g s' % (time.time() - dt)) ### we have a faster but less accurate "interpolated action finder", which takes a bit longer to initialize dt = time.time() i_actfinder = agama.ActionFinder(a_pot_hybrid, interp=True) print('Time to set up agama interpolated action finder: %.4g s' % (time.time() - dt)) ### conversion from prolate spheroidal to cylindrical coords def ProlSphToCyl(la, nu, fd): return (((la - fd * fd) * (1 - abs(nu) / fd**2))**0.5, (la * abs(nu))**0.5 / fd * numpy.sign(nu)) ### show coordinate grid in prolate spheroidal coords
#!/usr/bin/python # illustrate the use of Torus Machine for transformation from action/angle to position/velocity import agama, numpy, matplotlib.pyplot as plt ax = plt.subplots(2, 2, figsize=(15, 10))[1] pot = agama.Potential(type='Plummer', mass=10, axisratioz=.6) act = [1.0, 2.0, 3.0] # Jr, Jz, Jphi - some fiducial values am = agama.ActionMapper(pot, act) # J,theta -> x,v (Torus), with the given actions af = agama.ActionFinder(pot) # x,v -> J,theta (Staeckel fudge) t = numpy.linspace(0, 100, 1001) # construct the orbit using Torus xv_torus = am( numpy.column_stack((am.Omegar * t, am.Omegaz * t, am.Omegaphi * t))) ax[0, 0].plot((xv_torus[:, 0]**2 + xv_torus[:, 1]**2)**0.5, xv_torus[:, 2], label='torus') # construct the orbit by numerically integrating the equations of motion _, xv_orbit = agama.orbit(ic=xv_torus[0], potential=pot, time=t[-1], trajsize=len(t)) ax[0, 0].plot((xv_orbit[:, 0]**2 + xv_orbit[:, 1]**2)**0.5, xv_orbit[:, 2], label='orbit') # compute actions for the torus orbit using Staeckel approximation J, theta, Omega = af(xv_torus, angles=True) ax[0, 1].plot(t, J, label='J,torus') ax[1, 1].plot(t, Omega, label='Omega,torus') ax[1, 0].plot(t, theta, label='theta,torus') # same for the actual orbit J, theta, Omega = af(xv_orbit, angles=True)
def update_t(self, t): bar_pot = agama.Potential(file=self.ai_bar(t)) dark_pot = agama.Potential(file=self.ai_dark(t)) self.potential = agama.Potential(bar_pot, dark_pot) self.af = agama.ActionFinder(self.potential, interp=False)
orbits = agama.orbit(ic=posvel, potential=potential, time=inttime, trajsize=1000)[:, 1] rmin = numpy.zeros(len(orbits)) rmax = numpy.zeros(len(orbits)) for i, o in enumerate(orbits): r = numpy.sum(o[:, 0:3]**2, axis=1)**0.5 rmin[i] = numpy.min(r) if len(r) > 0 else numpy.nan rmax[i] = numpy.max(r) if len(r) > 0 else numpy.nan # replace nboot samples rmin/rmax with their median and 68% confidence intervals for each cluster rmin = numpy.nanpercentile(rmin.reshape(nclust, nboot), [16, 50, 84], axis=1) rmax = numpy.nanpercentile(rmax.reshape(nclust, nboot), [16, 50, 84], axis=1) # compute actions for the same initial conditions actfinder = agama.ActionFinder(potential) actions = actfinder(posvel) # again compute the median and 68% confidence intervals for each cluster actions = numpy.nanpercentile(actions.reshape(nclust, nboot, 3), [16, 50, 84], axis=1) # compute the same confidence intervals for the total energy energy = potential.potential( posvel[:, 0:3]) + 0.5 * numpy.sum(posvel[:, 3:6]**2, axis=1) energy = numpy.percentile(energy.reshape(nclust, nboot), [16, 50, 84], axis=1) # write the orbit parameters, actions and energy - one line per cluster, with the median and uncertainties fileout = open("result_orbits.txt", "w") fileout.write( "# Name \t pericenter[kpc] \t apocenter[kpc] \t" + " Jr[kpc*km/s] \t Jz[kpc*km/s] \t Jphi[kpc*km/s] \t Energy[km^2/s^2] \n"
def update_index(self, index, ss_id=None, snap=None): agama.setUnits(mass=1, length=1, velocity=1) self.current_index = index self.ss_id = ss_id if snap is not None: self.snap = snap else: self.snap = \ gizmo.io.Read.read_snapshots(['star', 'gas', 'dark'], 'index', index, properties=['id', 'position', 'velocity', 'mass', 'form.scalefactor'], simulation_directory= self.simulation_directory, assign_principal_axes=True) if self.sim_name is None: head = gizmo.io.Read.read_header(snapshot_value=self.startnum, simulation_directory= self.simulation_directory) self.sim_name = head['simulation.name'].replace(" ", "_") potential_cache_file = self.cache_directory + '/potential_id'+str(index) potential_cache_file += '_' + self.sim_name + '_pot' try: self.potential = agama.Potential(file=potential_cache_file) except: star_position = self.snap['star'].prop('host.distance.principal') gas_position = self.snap['gas'].prop('host.distance.principal') dark_position = self.snap['dark'].prop('host.distance.principal') star_mass = self.snap['star']['mass'] gas_mass = self.snap['gas']['mass'] dark_mass = self.snap['dark']['mass'] position = np.concatenate((star_position, gas_position)) mass = np.concatenate((star_mass, gas_mass)) #TODO make these user-controllable self.pdark = agama.Potential(type="Multipole", particles=(dark_position, dark_mass), symmetry='a', gridsizeR=20, lmax=2) self.pbar = agama.Potential(type="CylSpline", particles=(position, mass), gridsizer=20, gridsizez=20, mmax=0, Rmin=0.2, symmetry='a', Rmax=50, Zmin=0.02, Zmax=10) self.potential = agama.Potential(self.pdark, self.pbar) self.potential.export(potential_cache_file) if ss_id is not None: self.ss_init = True ss_key = np.where(self.snap['star']['id'] == self.ss_id)[0] self.chosen_position = self.snap['star'].prop( 'host.distance.principal')[ss_key] self.chosen_velocity = self.snap['star'].prop( 'host.velocity.principal')[ss_key] else: self.ss_init = False self.af = agama.ActionFinder(self.potential, interp=False)
p_halo = { "type": "SpheroidDensity", "densityNorm": 1.072e10, "gamma": 1.0, "beta": 3.0, "scaleRadius": 2. } ### one can create the genuine instance of Agama potential as follows: #c_pot = agama.Potential("../data/MWPotential2014.ini") # read parameters from ini file #c_pot = agama.Potential(p_bulge, p_disk, p_halo) # or create potential from a list of parameters ### or one can instead create a galpy-compatible potential as follows: w_pot = galpy_agama.CPotential(p_bulge, p_disk, p_halo) # same as above, two variants ### ...and then use _pot member variable to access the instance of raw Agama potential dt = time.time() c_actfinder = agama.ActionFinder(w_pot._pot) print 'Time to set up action finder: %s s' % (time.time() - dt) ### this needs to be done once for the given potential, ### and initializes the interfocal distance estimator for all values of E and L ### conversion from prolate spheroidal to cylindrical coords def ProlSphToCyl(la, nu, ifd): return (((la - ifd * ifd) * (1 - abs(nu) / ifd**2))**0.5, (la * abs(nu))**0.5 / ifd * numpy.sign(nu)) ### show coordinate grid in prolate spheroidal coords def plotCoords(ifd, maxR): la = numpy.linspace(0, maxR, 32)**2 + ifd**2