def test_mass_in_msol(): #Test the scaling, should be velocity^2 x position vofid, rofid = 200., 8. assert numpy.fabs(4. * conversion.mass_in_msol(vofid, rofid) / conversion.mass_in_msol(2. * vofid, rofid) - 1.) < 10.**-10., 'mass_in_msol did not work as expected' assert numpy.fabs(2. * conversion.mass_in_msol(vofid, rofid) / conversion.mass_in_msol(vofid, 2 * rofid) - 1.) < 10.**-10., 'mass_in_msol did not work as expected' return None
def test_ChandrasekharDynamicalFrictionForce_constLambda(): # Test that the ChandrasekharDynamicalFrictionForce with constant Lambda # agrees with analytical solutions for circular orbits: # assuming that a mass remains on a circular orbit in an isothermal halo # with velocity dispersion sigma and for constant Lambda: # r_final^2 - r_initial^2 = -0.604 ln(Lambda) GM/sigma t # (e.g., B&T08, p. 648) from galpy.util import conversion from galpy.orbit import Orbit ro, vo = 8., 220. # Parameters GMs = 10.**9. / conversion.mass_in_msol(vo, ro) const_lnLambda = 7. r_init = 2. dt = 2. / conversion.time_in_Gyr(vo, ro) # Compute lp = potential.LogarithmicHaloPotential(normalize=1., q=1.) cdfc= potential.ChandrasekharDynamicalFrictionForce(\ GMs=GMs,const_lnLambda=const_lnLambda, dens=lp) # don't provide sigmar, so it gets computed using galpy.df.jeans o = Orbit([r_init, 0., 1., 0., 0., 0.]) ts = numpy.linspace(0., dt, 1001) o.integrate(ts, [lp, cdfc], method='odeint') r_pred = numpy.sqrt(o.r()**2. - 0.604 * const_lnLambda * GMs * numpy.sqrt(2.) * dt) assert numpy.fabs( r_pred - o.r(ts[-1]) ) < 0.01, 'ChandrasekharDynamicalFrictionForce with constant lnLambda for circular orbits does not agree with analytical prediction' return None
def test_ChandrasekharDynamicalFrictionForce_varLambda(): # Test that dynamical friction with variable Lambda for small r ranges # gives ~ the same result as using a constant Lambda that is the mean of # the variable lambda # Also tests that giving an axisymmetric list of potentials for the # density works from galpy.util import conversion from galpy.orbit import Orbit ro, vo = 8., 220. # Parameters GMs = 10.**9. / conversion.mass_in_msol(vo, ro) r_init = 3. dt = 2. / conversion.time_in_Gyr(vo, ro) # Compute evolution with variable ln Lambda cdf= potential.ChandrasekharDynamicalFrictionForce(\ GMs=GMs,rhm=0.125, dens=potential.MWPotential2014,sigmar=lambda r: 1./numpy.sqrt(2.)) o = Orbit([r_init, 0., 1., 0., 0., 0.]) ts = numpy.linspace(0., dt, 1001) o.integrate(ts, [potential.MWPotential2014, cdf], method='odeint') lnLs = numpy.array([ cdf.lnLambda(r, v) for (r, v) in zip( o.r(ts), numpy.sqrt(o.vx(ts)**2. + o.vy(ts)**2. + o.vz(ts)**2.)) ]) cdfc= potential.ChandrasekharDynamicalFrictionForce(\ GMs=GMs,rhm=0.125,const_lnLambda=numpy.mean(lnLs), dens=potential.MWPotential2014,sigmar=lambda r: 1./numpy.sqrt(2.)) oc = o() oc.integrate(ts, [potential.MWPotential2014, cdfc], method='odeint') assert numpy.fabs( oc.r(ts[-1]) - o.r(ts[-1]) ) < 0.05, 'ChandrasekharDynamicalFrictionForce with variable lnLambda for a short radial range is not close to the calculation using a constant lnLambda' return None
def test_ChandrasekharDynamicalFrictionForce_pickling(): # Test that ChandrasekharDynamicalFrictionForce objects can/cannot be # pickled as expected import pickle from galpy.util import conversion ro, vo = 8., 220. # Parameters GMs = 10.**9. / conversion.mass_in_msol(vo, ro) # sigmar internally computed, should be able to be pickled # Compute evolution with variable ln Lambda cdf= potential.ChandrasekharDynamicalFrictionForce(\ GMs=GMs,rhm=0.125, dens=potential.MWPotential2014, minr=0.5,maxr=2.) pickled = pickle.dumps(cdf) cdfu = pickle.loads(pickled) # Test a few values assert numpy.fabs(cdf.Rforce(1.,0.2,v=[1.,1.,0.])\ -cdfu.Rforce(1.,0.2,v=[1.,1.,0.])) < 1e-10, 'Pickling of ChandrasekharDynamicalFrictionForce object does not work as expected' assert numpy.fabs(cdf.zforce(2.,-0.2,v=[1.,1.,0.])\ -cdfu.zforce(2.,-0.2,v=[1.,1.,0.])) < 1e-10, 'Pickling of ChandrasekharDynamicalFrictionForce object does not work as expected' # Not providing dens = Logarithmic should also work cdf= potential.ChandrasekharDynamicalFrictionForce(\ GMs=GMs,rhm=0.125, minr=0.5,maxr=2.) pickled = pickle.dumps(cdf) cdfu = pickle.loads(pickled) # Test a few values assert numpy.fabs(cdf.Rforce(1.,0.2,v=[1.,1.,0.])\ -cdfu.Rforce(1.,0.2,v=[1.,1.,0.])) < 1e-10, 'Pickling of ChandrasekharDynamicalFrictionForce object does not work as expected' assert numpy.fabs(cdf.zforce(2.,-0.2,v=[1.,1.,0.])\ -cdfu.zforce(2.,-0.2,v=[1.,1.,0.])) < 1e-10, 'Pickling of ChandrasekharDynamicalFrictionForce object does not work as expected' # Providing sigmar as a lambda function gives AttributeError sigmar = lambda r: 1. / r cdf= potential.ChandrasekharDynamicalFrictionForce(\ GMs=GMs,rhm=0.125, dens=potential.MWPotential2014,sigmar=sigmar, minr=0.5,maxr=2.) if PY3: with pytest.raises(AttributeError) as excinfo: pickled = pickle.dumps(cdf) else: with pytest.raises(pickle.PicklingError) as excinfo: pickled = pickle.dumps(cdf) return None
def test_ChandrasekharDynamicalFrictionForce_evaloutsideminrmaxr(): # Test that dynamical friction returns the expected force when evaluating # outside of the [minr,maxr] range over which sigmar is interpolated: # 0 at r < minr # using sigmar(r) for r > maxr from galpy.util import conversion ro, vo = 8., 220. # Parameters GMs = 10.**9. / conversion.mass_in_msol(vo, ro) # Compute evolution with variable ln Lambda sigmar = lambda r: 1. / r cdf= potential.ChandrasekharDynamicalFrictionForce(\ GMs=GMs,rhm=0.125, dens=potential.MWPotential2014,sigmar=sigmar, minr=0.5,maxr=2.) # cdf 2 for checking r > maxr of cdf cdf2= potential.ChandrasekharDynamicalFrictionForce(\ GMs=GMs,rhm=0.125, dens=potential.MWPotential2014,sigmar=sigmar, minr=0.5,maxr=4.) v = [0.1, 0., 0.] # r < minr assert numpy.fabs( cdf.Rforce(0.1, 0., v=v) ) < 1e-16, 'potential.ChandrasekharDynamicalFrictionForce at r < minr not equal to zero' assert numpy.fabs( cdf.zforce(0.1, 0., v=v) ) < 1e-16, 'potential.ChandrasekharDynamicalFrictionForce at r < minr not equal to zero' # r > maxr assert numpy.fabs( cdf.Rforce(3., 0., v=v) - cdf2.Rforce(3., 0., v=v) ) < 1e-10, 'potential.ChandrasekharDynamicalFrictionForce at r > maxr not as expected' assert numpy.fabs( cdf.zforce(3., 0., v=v) - cdf2.zforce(3., 0., v=v) ) < 1e-10, 'potential.ChandrasekharDynamicalFrictionForce at r > maxr not as expected' return None
class Snapshot(): MLU = 8. #kpc MVU = 220. #km/s MTU = time_in_Gyr(MVU, MLU) * 1000. #Myr MMU = mass_in_msol(MVU, MLU) # msol def __init__(self, Filename, Np, hf=0.4, df=0.5, bf=0.1, Nslice=10, dt=0.0078125, frac=True): self.Filename = Filename self.Np = Np if frac: self.Nh = int(self.Np * hf) self.Nd = int(self.Np * df) self.Nb = int(self.Np * bf) else: self.Nh = hf self.Nd = df self.Nb = bf self._step = int((self.Filename.split('_')[3]).split('.')[0]) self._Nslice = Nslice self._dt = dt self.t = self._step * dt self._COM = self.calc_COM() self._rot = self.calc_AngMom() def calc_COM(self): mtot = 0 COMraw = np.zeros(3) for i in range(self._Nslice): offset = int(i * int(np.ceil(self.Np / self._Nslice))) count = int(np.ceil(self.Np / self._Nslice)) if offset + count > self.Np: count = self.Np - offset with open(self.Filename, 'rb') as f: mxv = np.fromfile(f, dtype='f4', offset=offset * 4 * 7, count=count * 7) mxv = np.reshape(mxv, (count, 7)).astype('f8') mtot += np.sum(mxv[:, 0]) COMraw += np.sum(mxv[:, 0][:, None] * (mxv[:, 1:4]), axis=0) del mxv return COMraw / mtot def calc_AngMom(self): Ltot = np.zeros(3) for i in range(self._Nslice): with open(self.Filename, 'rb') as f: mxv = np.fromfile( f, dtype='f4', offset=(self.Nh * 7 + i * 7 * int(self.Nd / self._Nslice)) * 4, count=7 * int(self.Nd / self._Nslice)) mxv = np.reshape(mxv, (int(len(mxv) / 7), 7)) Ltot += np.sum(np.cross((mxv[:, 1:4] - self._COM), mxv[:, 0][:, None] * mxv[:, 4:]), axis=0) Ltot = Ltot / np.sqrt(np.sum(Ltot**2)) rot = self.calc_Rot_matrix(Ltot) del mxv return rot @staticmethod def calc_Rot_matrix(AngMom): hyz = np.sqrt(AngMom[1]**2 + AngMom[2]**2) costh = AngMom[2] / hyz sinth = AngMom[1] / hyz Rx = np.array([[1, 0, 0], [0, costh, -sinth], [0, sinth, costh]]) Lmid = np.matmul(Rx, AngMom) hxz = np.sqrt(Lmid[0]**2 + Lmid[2]**2) cosph = Lmid[2] / hxz sinph = -Lmid[0] / hxz Ry = np.array([[cosph, 0, sinph], [0, 1, 0], [-sinph, 0, cosph]]) return np.matmul(Ry, Rx) def calc_density(self, bins=(90, 90, 90), lim=(30., 30., 10.), adjust=True): Ntot = np.zeros(bins) for i in range(self._Nslice): with open(self.Filename, 'rb') as f: mxv = np.fromfile(f, dtype='f4', offset=i * 7 * int(self.Np / self._Nslice) * 4, count=7 * int(self.Np / self._Nslice)) mxv = np.reshape(mxv, (int(len(mxv) / 7), 7)) if adjust: mxv[:, 1:] = self.adjust(mxv[:, 1:]) N, edges = np.histogramdd(mxv[:, 1:4], bins=bins, range=((-lim[0], lim[0]), (-lim[1], lim[1]), (-lim[2], lim[2])), weights=mxv[:, 0]) Ntot += N del mxv self.N = Ntot self.Nedge = edges return Ntot, edges def adjust(self, particles, vel=True): particles[:, :3] = np.matmul(self._rot, particles[:, :3].T - self._COM[:, None]).T if vel: particles[:, 3:] = np.matmul(self._rot, particles[:, 3:].T).T return particles def sample(self, frac=0.1): ncomp = [self.Nh, self.Nd, self.Nb] sample = np.empty([0, 7]) for i, n in enumerate(ncomp): for j in range(self._Nslice): offset = int(np.sum( ncomp[:i])) + j * int(np.ceil(n / self._Nslice)) count = int(np.ceil(n / self._Nslice)) if offset + count > np.sum(ncomp[:i + 1]): count = np.sum(ncomp[:i + 1]) - offset with open(self.Filename, 'rb') as f: mxv = np.fromfile(f, dtype='f4', offset=offset * 7 * 4, count=7 * count) mxv = np.reshape(mxv, (int(len(mxv) / 7), 7)) indx = np.random.choice(count, int(count * frac), replace=False) sample = np.vstack( [sample, np.reshape(mxv[indx], (len(indx), 7))]) sample[:, 0] /= frac sample[:, 1:] = self.sample(frac=frac) return sample def calc_vrot(self, Rs=np.linspace(0.01, 5., 51), sample=True, frac=0.01): try: return self.vrot except: if sample: sample = self.sample(frac=frac) else: with open(self.Filename, 'rb') as f: sample = np.fromfile(f, dtype='f4', offset=4 * 7 * (self.Nh + self.Nd), count=7 * self.Nb) sample = np.reshape(sample, (self.Nb, 7)) sample = self.adjust(sample) Nh = len(sample[tuple([sample[:, 0] == sample[0, 0]])]) f = pb.new(dm=Nh, star=len(sample[:, 0]) - Nh) f['mass'] = sample[:, 0] / self.MMU f['pos'] = sample[:, 1:4] / self.MLU f['vel'] = sample[:, 4:] / self.MVU f['eps'] = np.ones(len(sample)) * 0.05 / self.MLU sp = galpy.potential.SnapshotRZPotential(f, num_threads=10) vrot = galpy.potential.calcRotcurve(sp, Rs, phi=0.) self.vrot = vrot self._Rs = Rs return vrot def calc_potential(self, sample=True, frac=0.01, a=[27.68, 3.41, 0.536], N=[5, 15, 3], L=[5, 15, 3]): try: print('data/equil_potential_coefficients_N' + str(N[0]) + str(N[1]) + str(N[2]) + '.txt') with open( 'data/equil_potential_coefficients_N' + str(N[0]) + str(N[1]) + str(N[2]) + '.txt', 'rb') as f: Acos, Asin = np.load(f, allow_pickle=True) pot = [ SCFPotential(Acos=Acos[i], Asin=Asin[i], a=a[i] / 8.) for i in range(3) ] except: ncomp = [self.Nh, self.Nd, self.Nb] Acos, Asin = np.empty((2, 3), dtype='object') pot = np.empty(3, dtype='object') fullsample = self.sample(frac=frac) masses = list(set(fullsample[:, 0])) for i, n in enumerate(ncomp): samples = fullsample[tuple([fullsample[:, 0] == masses[i]])] Acos[i], Asin[i] = scf_compute_coeffs_nbody( samples[:, 1:4].T / self.MLU, samples[:, 0] / self.MMU, N[i], L[i], a=a[i] / 8.) pot[i] = SCFPotential(Acos=Acos[i], Asin=Asin[i], a=a[i] / 8.) coeff = np.vstack([Acos, Asin]) with open( 'data/equil_potential_coefficients_N' + str(N[0]) + str(N[1]) + str(N[2]) + '.txt', 'wb') as f: np.save(f, coeff) self.pot = list(pot) return list(pot) def analyze_SN(self, Rcyl, X=8.1, Y=0, correct=True, zrange=[-2, 2], nbins=51): calc = True try: for i, sn in enumerate(self.SN): if (sn.Rcyl == Rcyl) * (sn.X == X) * (sn.Y == Y): # If you want a finer grid, replace the old coarser grid if (nbins > sn.nbins): self.SN[i] = SolarNeighbourhood( self, Rcyl, X, Y, correct, zrange, nbins) calc = False # If it already exists, don't do it again else: calc = False else: continue if calc: self.SN = np.append( self.SN, SolarNeighbourhood(self, Rcyl, X, Y, correct, zrange, nbins)) except: self.SN = np.array( [SolarNeighbourhood(self, Rcyl, X, Y, correct, zrange, nbins)]) def plot_density(self, plot_origin=False, save=False, adjust=True): try: self.N except: self.calc_density(adjust=adjust) fig, [ax1, ax2] = plt.subplots(2, sharex=True, gridspec_kw={'height_ratios': [3, 1]}, figsize=[5, 7]) ax1.imshow(np.log(np.sum(self.N, axis=2).T), cmap='Greys', extent=[-30, 30, -30, 30]) ax2.imshow(np.log(np.sum(self.N, axis=1).T), cmap='Greys', extent=[-30, 30, -10, 10]) if plot_origin: ax1.plot(0., 0., 'or') ax2.plot(0., 0., 'or') ax2.set_xlabel(r'$x \,\mathrm{(kpc)}$') ax1.set_ylabel(r'$y \,\mathrm{(kpc)}$') ax2.set_ylabel(r'$z \,\mathrm{(kpc)}$') fig.subplots_adjust(hspace=0) txt = ax1.annotate(r'$t=%.0f\,\mathrm{Myr}$' % (np.round(self.t, 0)), (0.95, 0.95), xycoords='axes fraction', horizontalalignment='right', verticalalignment='top', size=18.) if save: plt.savefig('plots/Density' + str(np.round(self.t, 0) + '.pdf', bbox_inches='tight'))
def test_amuse_potential_with_physical(): ro, vo=8., 220. amp= 1e8 / conversion.mass_in_msol(ro=ro, vo=vo) a= 0.8 / ro amp_u= 1e8 * apy_u.solMass a_u= 0.8 * apy_u.kpc ro_u, vo_u= 8.*apy_u.kpc, 220.*apy_u.km/apy_u.s x, y, z = 3|units.kpc, 4|units.kpc, 2|units.kpc r = 5|units.kpc # ------------------------------------ # get_potential_at_point pot1= potential.TwoPowerSphericalPotential(amp=amp, a=a, ro=ro, vo=vo) gg1 = pot1(5/ro, 2/ro) gg1 = gg1.to_value(apy_u.km**2 / apy_u.s**2) if hasattr(gg1, 'unit') else gg1 amuse_pot1= to_amuse(pot1) ag1= amuse_pot1.get_potential_at_point(0, x, y, z) assert numpy.abs(gg1 - ag1.value_in(units.kms**2)) < 1e-10 pot2= potential.TwoPowerSphericalPotential(amp=amp_u, a=a_u, ro=ro_u, vo=vo_u) gg2 = pot1(5*apy_u.kpc, 2*apy_u.kpc) gg2 = gg2.to_value(apy_u.km**2 / apy_u.s**2) if hasattr(gg2, 'unit') else gg2 amuse_pot2= to_amuse(pot2) ag2= amuse_pot2.get_potential_at_point(0, x, y, z) assert numpy.abs(gg2 - ag2.value_in(units.kms**2)) < 1e-10 assert numpy.abs(ag1 - ag2) < 1e-10|units.kms**2 # ------------------------------------ # test get_gravity_at_point pot1= potential.TwoPowerSphericalPotential(amp=amp, a=a, ro=ro, vo=vo) amuse_pot1= to_amuse(pot1) ax1, ay1, az1= amuse_pot1.get_gravity_at_point(0, x, y, z) pot2= potential.TwoPowerSphericalPotential(amp=amp_u, a=a_u, ro=ro_u, vo=vo_u) amuse_pot2= to_amuse(pot2) ax2, ay2, az2= amuse_pot2.get_gravity_at_point(0, x, y, z) assert numpy.abs(ax1 - ax2) < 1e-10|units.kms/units.Myr assert numpy.abs(ay1 - ay2) < 1e-10|units.kms/units.Myr assert numpy.abs(az1 - az2) < 1e-10|units.kms/units.Myr # ------------------------------------ # test mass_density pot1= potential.TwoPowerSphericalPotential(amp=amp, a=a, ro=ro, vo=vo) grho1 = pot1.dens(5/ro, 2/ro) grho1 = grho1.to_value(apy_u.solMass/apy_u.pc**3) if hasattr(grho1, 'unit') else grho1 amuse_pot1= to_amuse(pot1) arho1= amuse_pot1.mass_density(x, y, z) assert numpy.abs(grho1 - arho1.value_in(units.MSun/units.parsec**3)) < 1e-10 pot2= potential.TwoPowerSphericalPotential(amp=amp_u, a=a_u, ro=ro_u, vo=vo_u) grho2= pot2.dens(5*apy_u.kpc, 2*apy_u.kpc) grho2= grho2.to_value(apy_u.solMass/apy_u.pc**3) if hasattr(grho2, 'unit') else grho2 amuse_pot2= to_amuse(pot2) arho2= amuse_pot2.mass_density(x, y, z) assert numpy.abs(grho2 - arho2.value_in(units.MSun/units.parsec**3)) < 1e-10 assert numpy.abs(arho1 - arho2) < 1e-10|units.MSun/units.parsec**3 # ------------------------------------ # test circular_velocity pot1= potential.TwoPowerSphericalPotential(amp=amp, a=a, ro=ro, vo=vo) gv1= pot1.vcirc(1*apy_u.kpc) gv1= gv1.to_value(apy_u.km/apy_u.s) if hasattr(gv1, 'unit') else gv1 amuse_pot1= to_amuse(pot1) av1= amuse_pot1.circular_velocity(1|units.kpc) assert numpy.abs(gv1 - av1.value_in(units.kms)) < 1e-10 pot2= potential.TwoPowerSphericalPotential(amp=amp_u, a=a_u, ro=ro_u, vo=vo_u) gv2= pot2.vcirc(1*apy_u.kpc) gv2= gv2.to_value(apy_u.km/apy_u.s) if hasattr(gv2, 'unit') else gv2 amuse_pot2= to_amuse(pot2) av2= amuse_pot2.circular_velocity(1|units.kpc) assert numpy.abs(gv2 - av2.value_in(units.kms)) < 1e-10 assert numpy.abs(av1 - av2) < 1e-10|units.kms # ------------------------------------ # test enclosed_mass pot1= potential.TwoPowerSphericalPotential(amp=amp, a=a, ro=ro, vo=vo) gm1= pot1.mass(1/ro) gm1= gm1.to_value(apy_u.solMass) if hasattr(gm1, 'unit') else gm1 amuse_pot1= to_amuse(pot1) am1= amuse_pot1.enclosed_mass(1|units.kpc) assert numpy.abs(gm1 - am1.value_in(units.MSun)) < 3e-8 pot2= potential.TwoPowerSphericalPotential(amp=amp_u, a=a_u, ro=ro_u, vo=vo_u) gm2= pot2.mass(1*apy_u.kpc) gm2= gm2.to_value(apy_u.solMass) if hasattr(gm2, 'unit') else gm2 amuse_pot2= to_amuse(pot2) am2= amuse_pot2.enclosed_mass(1|units.kpc) assert numpy.abs(gm2 - am2.value_in(units.MSun)) < 3e-8 assert numpy.abs(am1 - am2) < 1e-10|units.MSun return None
def simulate_cdm_subhalos(sdf_pepper, log10Msub_min=5., log10Msub_max=9., timescdm=1., mf_slope=-1.9, c0kpc=2.02 * 10**(-13), r=17., Xrs=5., sigma=120. / 220., rs_fit=True, rs_Nbody_kpc=0.2): ''' Sample amp and slope such that dN/dM = amp*M^slope and simulate subhalo impacts ''' Mbin_edge = np.arange(log10Msub_min, log10Msub_max + 1, 1) Nbins = len(Mbin_edge) - 1 #compute number of subhalos in each mass bin nden_bin = np.empty(Nbins) rate_bin = np.empty(Nbins) for ll in range(Nbins): nden_bin[ll] = nsub_cdm(Mbin_edge[ll], Mbin_edge[ll + 1], r=r, c0kpc=c0kpc, mf_slope=mf_slope) Mmid = 10**(0.5 * (Mbin_edge[ll] + Mbin_edge[ll + 1])) rate_bin[ll] = sdf_pepper.subhalo_encounters( sigma=sigma, nsubhalo=nden_bin[ll], bmax=Xrs * rs(Mmid, plummer=True, rs_fit=rs_fit, rs_Nbody_kpc=rs_Nbody_kpc)) rate = timescdm * np.sum(rate_bin) Nimpact = numpy.random.poisson(rate) if Nbins == 1: #if only one bin then all subhalos are assigned the same mean mass sample_GM = lambda: 10.**(0.5 * (Mbin_edge[0] + Mbin_edge[1]) ) / conversion.mass_in_msol(vo, ro) timpact_sub = numpy.array( sdf_pepper._uniq_timpact)[numpy.random.choice( len(sdf_pepper._uniq_timpact), size=Nimpact, p=sdf_pepper._ptimpact)] # Sample angles from the part of the stream that existed then impact_angle_sub = numpy.array([ sdf_pepper._icdf_stream_len[ti](numpy.random.uniform()) for ti in timpact_sub ]) GM_sub = numpy.array([sample_GM() for a in impact_angle_sub]) rs_sub = numpy.array([ rs(gm * conversion.mass_in_msol(vo, ro), rs_fit=rs_fit, rs_Nbody_kpc=rs_Nbody_kpc) for gm in GM_sub ]) else: norm = 1. / quad(lambda M: M**(mf_slope + 0.5), 10**(Mbin_edge[0]), 10 **(Mbin_edge[Nbins]))[0] def cdf(M): return quad(lambda M: norm * (M)**(mf_slope + 0.5), 10**Mbin_edge[0], M)[0] MM = numpy.linspace(Mbin_edge[0], Mbin_edge[Nbins], 10000) cdfl = [cdf(i) for i in 10**MM] icdf = interpolate.InterpolatedUnivariateSpline(cdfl, 10**MM, k=1) sample_GM = lambda: icdf(numpy.random.uniform() ) / conversion.mass_in_msol(vo, ro) timpact_sub = numpy.array( sdf_pepper._uniq_timpact)[numpy.random.choice( len(sdf_pepper._uniq_timpact), size=Nimpact, p=sdf_pepper._ptimpact)] # Sample angles from the part of the stream that existed then impact_angle_sub = numpy.array([ sdf_pepper._icdf_stream_len[ti](numpy.random.uniform()) for ti in timpact_sub ]) GM_sub = numpy.array([sample_GM() for a in impact_angle_sub]) rs_sub = numpy.array([ rs(gm * conversion.mass_in_msol(vo, ro), rs_fit=rs_fit, rs_Nbody_kpc=rs_Nbody_kpc) for gm in GM_sub ]) # impact b impactb_sub = (2. * numpy.random.uniform(size=len(impact_angle_sub)) - 1.) * Xrs * rs_sub # velocity subhalovel_sub = numpy.empty((len(impact_angle_sub), 3)) for ii in range(len(timpact_sub)): subhalovel_sub[ii] = sdf_pepper._draw_impact_velocities( timpact_sub[ii], sigma, impact_angle_sub[ii], n=1)[0] # Flip angle sign if necessary if not sdf_pepper._gap_leading: impact_angle_sub *= -1. return impact_angle_sub, impactb_sub, subhalovel_sub, timpact_sub, GM_sub, rs_sub