def get_potential(log_M_h, log_r_s, log_M_n, log_a): mw_potential = gp.CCompositePotential() mw_potential['bulge'] = gp.HernquistPotential(m=5E9, c=1., units=galactic) mw_potential['disk'] = gp.MiyamotoNagaiPotential(m=6.8E10 * u.Msun, a=3 * u.kpc, b=280 * u.pc, units=galactic) mw_potential['nucl'] = gp.HernquistPotential(m=np.exp(log_M_n), c=np.exp(log_a) * u.pc, units=galactic) mw_potential['halo'] = gp.NFWPotential(m=np.exp(log_M_h), r_s=np.exp(log_r_s), units=galactic) return mw_potential
def test_hernquist(): nmax = 6 lmax = 2 M = 1E10 r_s = 3.5 cos_coeff = np.zeros((nmax+1,lmax+1,lmax+1)) sin_coeff = np.zeros((nmax+1,lmax+1,lmax+1)) cos_coeff[0,0,0] = 1. scf_potential = _bfe_class.SCFPotential(m=M, r_s=r_s, Snlm=cos_coeff, Tnlm=sin_coeff, units=galactic) # scf_potential = HackPotential(m=10., units=galactic) nbins = 128 rr = np.linspace(0.1,10.,nbins) xyz = np.zeros((3,nbins)) xyz[0] = rr * np.cos(np.pi/4.) * np.sin(np.pi/4.) xyz[1] = rr * np.sin(np.pi/4.) * np.sin(np.pi/4.) xyz[2] = rr * np.cos(np.pi/4.) hernquist = gp.HernquistPotential(m=M, c=r_s, units=galactic) bfe_pot = scf_potential.energy(xyz).value true_pot = hernquist.energy(xyz).value np.testing.assert_allclose(bfe_pot, true_pot) bfe_grad = scf_potential.gradient(xyz).value true_grad = hernquist.gradient(xyz).value np.testing.assert_allclose(bfe_grad, true_grad)
def get_potential(total_mass = 130.0075e10*u.Msun,\ r_scale = 18.927757889861788 * u.kpc,\ mw_conc = 9.39): a_term = math.sqrt(2 * (math.log(1 + mw_conc) - mw_conc / (1 + mw_conc))) mw_a = r_scale * a_term return gp.HernquistPotential(m=total_mass, c=mw_a, units=galactic)
def test_staeckel_fudge_delta(): import galpy.potential as galpy_pot from galpy.actionAngle import estimateDeltaStaeckel ro = 8.1 * u.kpc vo = 229 * u.km / u.s paired_potentials = [] # Miyamoto-Nagai potential = gp.MiyamotoNagaiPotential(m=6e10 * u.Msun, a=3 * u.kpc, b=0.3 * u.kpc, units=galactic) amp = (G * potential.parameters['m']).to_value(vo**2 * ro) a = potential.parameters['a'].to_value(ro) b = potential.parameters['b'].to_value(ro) galpy_potential = galpy_pot.MiyamotoNagaiPotential(amp=amp, a=a, b=b, ro=ro, vo=vo) paired_potentials.append((potential, galpy_potential)) # Hernquist potential = gp.HernquistPotential(m=6e10 * u.Msun, c=0.3 * u.kpc, units=galactic) amp = (G * potential.parameters['m']).to_value(vo**2 * ro) a = potential.parameters['c'].to_value(ro) galpy_potential = galpy_pot.HernquistPotential(amp=amp, a=a, ro=ro, vo=vo) paired_potentials.append((potential, galpy_potential)) # NFW potential = gp.NFWPotential(m=6e11 * u.Msun, r_s=15.6 * u.kpc, units=galactic) amp = (G * potential.parameters['m']).to_value(vo**2 * ro) a = potential.parameters['r_s'].to_value(ro) galpy_potential = galpy_pot.NFWPotential(amp=amp, a=a, ro=ro, vo=vo) paired_potentials.append((potential, galpy_potential)) # TEST: N = 1024 rnd = np.random.default_rng(42) w = PhaseSpacePosition(pos=rnd.uniform(-10, 10, size=(3, N)) * u.kpc, vel=rnd.uniform(-100, 100, size=(3, N)) * u.km / u.s) R = w.cylindrical.rho.to_value(ro) z = w.z.to_value(ro) for p, galpy_p in paired_potentials: galpy_deltas = estimateDeltaStaeckel(galpy_p, R, z, no_median=True) gala_deltas = get_staeckel_fudge_delta(p, w).value print(p, np.allclose(gala_deltas, galpy_deltas)) assert np.allclose(gala_deltas, galpy_deltas, atol=1e-6)
def integrate(w0,\ pot=gp.HernquistPotential(m=130.0075e10*u.Msun,c = 32.089, units=galactic),\ timestep = -10.67,\ ntimesteps = 250,\ verbose=False): # TODO orbit = None # define the orbit object in this scope if verbose: print('integrating ... ') start_time = time.time() orbit = gp.Hamiltonian(pot).integrate_orbit(w0, dt=timestep, n_steps=ntimesteps) orbit_time = time.time() - start_time print('done integrating') print('Length of integration: %1.3f' % orbit_time) else: orbit = gp.Hamiltonian(pot).integrate_orbit(w0, dt=timestep, n_steps=ntimesteps) return orbit, pot
a=1.9746 * 5 * u.kpc, b=1 * u.kpc, units=galactic) pot['disk2'] = gp.MiyamotoNagaiPotential(m=10E10 * -1.2756 * u.Msun, a=4.2333 * 5 * u.kpc, b=1 * u.kpc, units=galactic) pot['disk3'] = gp.MiyamotoNagaiPotential(m=10E10 * 0.2153 * u.Msun, a=0.6354 * 5 * u.kpc, b=1 * u.kpc, units=galactic) pot['bulge'] = gp.HernquistPotential(m=3e10 * u.Msun, c=0.5 * u.kpc, units=galactic) ## Make grid (0.1 kpc resolution) for mock image mock_image = np.zeros((61, 201)) ## Calculate densities along line of sight in each x-z bin temp_index = np.indices((1, 201, 1), dtype=float) temp_index[1] = temp_index[1] / 2. - 50 # Not sure how to get properly shaped array of output from pot.density, hence for-loops # Only run this once!! for xind in range(0, 201): for zind in range(0, 61): xtemp = xind / 2. - 50
def hernquist_gradient(xyz, M, r_s): import gala.potential as gp p = gp.HernquistPotential(m=M, c=r_s, units=[u.kpc, u.Myr, u.Msun, u.radian]) return p.gradient(xyz).value
Omega = ConfigItem(40., "Bar pattern speed [km/s/kpc]") bar_mass = ConfigItem(1E10, "Bar mass [Msun]") # TODO: add other tunable parameters once I decide on the potential... # ============================================================================== # Define the potential model up to the bar component potential_no_bar = gp.CCompositePotential() potential_no_bar['disk'] = gp.MiyamotoNagaiPotential(m=5E10 * u.Msun, a=3 * u.kpc, b=280 * u.pc, units=galactic) potential_no_bar['spheroid'] = gp.HernquistPotential(m=4E9 * u.Msun, c=0.6 * u.kpc, units=galactic) potential_no_bar['halo'] = gp.NFWPotential(m=6E11, r_s=18 * u.kpc, units=galactic) def get_hamiltonian(config_file=None): c = Config() c.load(config_file) pot = potential_no_bar.copy() pot['bar'] = get_bar_potential(config_file) Om = [0., 0., c.Omega] * u.km / u.s / u.kpc frame = gp.ConstantRotatingFrame(Omega=Om, units=galactic)
def integrate_dyn_fric(w0,\ sat_mass = 1e8,\ pot=gp.HernquistPotential(m=130.0075e10*u.Msun,c = 32.089, units=galactic),\ scale_radius = 18.927757889861788,\ timestep = -10.67,\ ntimesteps = 250,\ verbose=False): def F_dyn_fric(t, w, msat, rs): G_gal = 4.49850215e-12 # gravitational constant in galactic units (kpc^3 Msun^-1 Myr^-2) accdf = 0.0 q = w[0:3] p = w[3:6] verf = np.vectorize(math.erf) absr = np.linalg.norm(q, axis=0) absv = np.linalg.norm(p, axis=0) loglambda = np.maximum(0, np.log(absr / 3 / 1.6)) rhoh = pot.density(q).value normalized_r = absr / rs # 0.2199 kpc / Myr = 215 km/s sigma = 0.2199 * (1.4393 * normalized_r**0.354 / (1 + 1.1756 * normalized_r**0.725)) chand_x = absv / math.sqrt(2) / sigma # k is the part of the equation with the erf and exp components k = verf(chand_x) - 2 * chand_x * np.exp( -np.square(chand_x)) / math.sqrt(math.pi) accdf = (4 * math.pi * G_gal * G_gal * msat * loglambda * rhoh / absv**3) * k * p q_dot = p p_dot = -pot.gradient(q).value + accdf toreturn = np.concatenate((q_dot, p_dot)) return toreturn integrator = gi.LeapfrogIntegrator(F_dyn_fric, func_args=(sat_mass, scale_radius), func_units=galactic) orbit = None if verbose: print('integrating ... ') start_time = time.time() orbit = integrator.run(w0, dt=timestep, n_steps=ntimesteps) orbit_time = time.time() - start_time print('done integrating') print('Length of integration: %1.3f' % orbit_time) else: orbit = integrator.run(w0, dt=timestep, n_steps=ntimesteps) return orbit, pot
def get_potential(self, p): """pars is the unpacked pars dict""" # HACK: TODO: allow other potentials return gp.HernquistPotential(m=p['pot_m'], c=np.exp(p['pot_log_rs']), units=galactic)
# clockwise 1, widdershins -1, angular thing c1 = 1.0 c2 = -1.0 w1 = 1.0 w2 = 1.0 # Milky Way and Andromeda M_MW = 1.0E11 * u.Msun M_AND = 1.23E12 * u.Msun SIZE_MW = 36.8 # kpc SIZE_AND = 33.7 # kpc MW_AND_DIST = 778.0 / sqrt(3) MW_AND_VEL = -110.0 / sqrt(3) POT_MW = gp.MilkyWayPotential() POT_AND = gp.HernquistPotential(M_AND.value, 0, units=galactic) ICS_MW = gd.PhaseSpacePosition(pos=[0, 0, 0] * u.kpc, vel=[0, 0, 0] * u.km / u.s) ICS_AND = gd.PhaseSpacePosition( pos=[MW_AND_DIST, MW_AND_DIST, MW_AND_DIST] * u.kpc, vel=[MW_AND_VEL, MW_AND_VEL, MW_AND_VEL] * (u.km / u.s)) # Lörg and Smøl Magellanic Clouds M_LORG = 1.0E10 * u.M_sun SIZE_LORG = 4.3 # u.kpc MW_LORG_DIST = 49.97 # wrt MW, in kpc MW_LORG_VEL = 321.0 # wrt MW, in km/s POT_LORG = gp.HernquistPotential(M_LORG.value, 0, units=galactic) ICS_LORG = gd.PhaseSpacePosition( pos=[MW_LORG_DIST, MW_LORG_DIST, MW_LORG_DIST] * u.kpc,
def run_gala_orbit(pos_sat, vel_sat, pos_tp, vel_tp): """ Specify units in ICs """ ## GALA set up # Design gala potential using Nbody and a test particle # Present-day position/velocity in inertial frame moving with instantaneous # Milky Way velocity: w0_mw = gd.PhaseSpacePosition( pos=[0, 0, 0] * u.kpc, vel=[0, 0, 0] * u.km / u.s, ) pot_mw = gp.HernquistPotential(m=Mhost * u.Msun, c=1 * u.kpc, units=galactic) # Values from Vasiliev et al. 2020 w0_lmc = gd.PhaseSpacePosition( pos=pos_sat, vel=vel_sat, ) w0_test = gd.PhaseSpacePosition( pos=pos_tp, vel=vel_tp, ) pot_lmc = gp.HernquistPotential(m=Msat * u.Msun, c=1 * u.kpc, units=galactic) w0 = gd.combine((w0_mw, w0_lmc, w0_test)) nbody = gd.DirectNBody(w0, [pot_mw, pot_lmc, None]) w1 = gd.combine((w0_lmc, w0_test)) isolated = gd.DirectNBody(w1, [pot_lmc, None]) # Full : Host - Satellite - test particle orbits = nbody.integrate_orbit(dt=dt * u.Gyr, t1=tmin * u.Gyr, t2=tmax * u.Gyr) # Orbit quantities full orbit pos_halo = np.array([orbits.xyz[:, :, 0].to(u.kpc).value])[0] pos_sat = np.array([orbits.xyz[:, :, 1].to(u.kpc).value])[0] pos_tp = np.array([orbits.xyz[:, :, 2].to(u.kpc).value])[0] vel_halo = np.array([orbits.v_xyz[:, :, 0].to(u.km / u.s).value])[0] vel_sat = np.array([orbits.v_xyz[:, :, 1].to(u.km / u.s).value])[0] vel_tp = np.array([orbits.v_xyz[:, :, 2].to(u.km / u.s).value])[0] r_halo = (np.sum(orbits.xyz[:, :, 0]**2, axis=0))**0.5 r_sat = (np.sum(orbits.xyz[:, :, 1]**2, axis=0))**0.5 r_tp = (np.sum(orbits.xyz[:, :, 2]**2, axis=0))**0.5 v_halo = (np.sum(orbits.v_xyz[:, :, 0].to(u.km / u.s).value**2, axis=0))**0.5 v_sat = (np.sum(orbits.v_xyz[:, :, 1].to(u.km / u.s).value**2, axis=0))**0.5 v_tp = (np.sum(orbits.v_xyz[:, :, 2].to(u.km / u.s).value**2, axis=0))**0.5 rtp_r2_sat = np.sum((np.array(orbits.xyz[:, :, 2].to(u.kpc).value - orbits.xyz[:, :, 1].to(u.kpc).value))**2, axis=0)**0.5 vtp_r2_sat = np.sum( (np.array(orbits.v_xyz[:, :, 2].to(u.km / u.s).value - orbits.v_xyz[:, :, 1].to(u.km / u.s).value))**2, axis=0)**0.5 return [pos_halo, vel_halo], [pos_sat, vel_sat], [pos_tp, vel_tp]
# coordinates (x, y, z) and returns the (scalar) value of the density at that # location: def density_func(x, y, z): r = np.sqrt(x**2 + y**2 + z**2) return 1 / (r**1.8 * (1 + r)**2.7) # Let's visualize this density function. For comparison, let's also over-plot # the Hernquist density distribution. The SCF expansion uses the Hernquist # density for radial basis functions, so the similarity of the density we want # to represent and the Hernquist function gives us a sense of how many radial # terms we will need in the expansion: hern = gp.HernquistPotential(m=1, c=1) # + x = np.logspace(-1, 1, 128) plt.plot(x, density_func(x, 0, 0), marker='', label='custom density') # need a 3D grid for the potentials in Gala xyz = np.zeros((3, len(x))) xyz[0] = x plt.plot(x, hern.density(xyz), marker='', label='Hernquist') plt.xscale('log') plt.yscale('log') plt.xlabel('$r$') plt.ylabel(r'$\rho(r)$')