def test_long_only(): """ Far pair with no short-range contribution """ pos = [(0.2, 0.2, 0.2), (0.7, 0.6, 0.5)] mass = [1, 10] print('Ewald summation of forces') acc = _direct_sum(pos, mass, ewald_corr=True) ewald = array([[0.00000000e+00, 5.26024612e+00, 7.09533177e+00], [-6.66133815e-16, -5.26024612e-01, -7.09533177e-01]]) max_acc = sqrt(square(ewald).sum(1).max()) max_err = sqrt(square(ewald - acc).sum(1).max()) print('Acceleration', repr(acc)) assert (max_err < 1e-5) print('Total rate of change of momentum', (acc * reshape(mass, (len(mass), 1))).sum(0)) fs = p3m.get_force_split(r_split=0.15, mode='erf') long_force = p3m.PMAccel(fs) accel = long_force.accel(mass, pos) print('Accel long', accel) accel_err = abs(accel - ewald).max() / max_acc print('Maximum relative error', accel_err) assert (accel_err < 2e-3)
def test_intermediate_grid(): """ test an additional non-periodic grid """ pts = [(0.5, 0.5, 0.5), (0.5, 0.8, 0.5), (0.55, 0.55, 0.55)] wts = [1, 2, 3] print('Ewald summation of forces') acc = _direct_sum(pts, wts, ewald_corr=True) print('Acceleration', repr(acc)) r_coarse = 0.105 print('Long-Short split of forces') fs = p3m.get_force_split(r_split=r_coarse, mode='cubic') #, kernel_pts=200) pairs, accel_short = p3m.pp_accel(fs, wts, pts, r_soft=None) # Cubic-split Newtonian accel_long = p3m.PMAccel(fs).accel(wts, pts) accel = accel_short + accel_long print('Result', repr(accel)) print('Long-Medium-Short split') ifs = p3m.IntermediateGrid(r_coarse=r_coarse, ngrid=96, centre=(0.5, 0.5, 0.5), hw_min=0.01) print('Rlong', ifs.r_coarse, 'Rfine', ifs.r_fine, 'box width', ifs.hw * 2) idx_in, idx_in_nonghost, idx_out, idx_out_nonghost = ifs.split_in_vs_out( pts) print('idx in', idx_in, idx_in_nonghost) pos_in = array(pts)[idx_in] wts_in = array(wts)[idx_in] accel_int_pm = ifs.pm_accel(pos_in, wts_in, idx_in_nonghost) accel_int_pp = ifs.pp_accel(pos_in, wts_in, idx_in_nonghost) accel_int = accel_int_pm + accel_int_pp pos_out = array(pts)[idx_out] wts_out = array(wts)[idx_out] pairs, accel_short = p3m.pp_accel(fs, wts_out, pos_out, r_soft=None) # Cubic-split Newtonian accel_short = accel_short[idx_out_nonghost] # add all the components accel = accel_long acs = zeros_like(accel) acs[idx_out[idx_out_nonghost]] += accel_short aci = zeros_like(accel) aci[idx_in[idx_in_nonghost]] += accel_int print('Accel int pp', accel_int_pp) print('Accel int', aci) print('Accel short', acs) print('ACCel long', accel_long) accel += aci + acs print('Result', repr(accel)) err = abs(accel - acc) print('Error', err) assert (err.max() < 4.0) # about 1.3 % error
def test_short_long(): """ Combination of close (Newtonian) and far (periodic effects) pairs """ from numpy import empty_like, arange print('A close pair and a far point') # worst-case scenario, short-pair near (0.1) splitting scale (0.105) pts = [(0.5, 0.5, 0.5), (0.5, 0.6, 0.5), (0.9, 0.3, 0.95)] wts = [1, 2, 3] print('Ewald summation of forces') acc = _direct_sum(pts, wts, ewald_corr=True) print('Short-long Acceleration', repr(acc)) # Ewald (from direct sum) ewald = array([[2.04840161, 196.8858954, 1.18571601], [1.63087151, -101.75612334, 0.92870922], [-1.77004821, 2.20878376, -1.01437815]]) max_acc = sqrt(square(ewald).sum(1).max()) max_err = sqrt(square(ewald - acc).sum(1).max()) assert (max_err < 1e-3) print('Total rate of change of momentum', (acc * reshape(wts, (len(wts), 1))).sum(0)) print('Hash-grid summation of forces') fs = p3m.get_force_split(r_split=0.105, mode='erf') pairs, accel_short = p3m.pp_accel(fs, wts, pts, r_soft=None) # Erf-split Newtonian assert (pairs == 1) # Only the close pair print('Making long range force via fft') long_force = p3m.PMAccel(fs) accel_long = long_force.accel(wts, pts) accel = accel_short + accel_long print('short+long', accel) accel_err = abs(accel - ewald).max() / max_acc print('short+long maximum relative error', accel_err) assert (accel_err < 1.5e-2)
def test_inter_grid_halo(): from numpy.random import RandomState rs = RandomState(seed=123) npts = 150 phi = 2 * pi * rs.rand(npts) cos_theta = 2 * rs.rand(npts) - 1 sin_theta = sqrt(1 - cos_theta**2) r = 0.5 * sqrt( (arange(npts) + 1.0) / npts ) # 1/r profile, like centre of NFW # mass is uniformly distributed over radii for isothermal halo (rho ~1/r^2) pos = empty((npts, 3)) pos[:, 0] = 0.5 + r * sin_theta * cos(phi) pos[:, 1] = 0.5 + r * sin_theta * sin(phi) pos[:, 2] = 0.5 + r * cos_theta wts = ones(npts) * (1.0 / npts) print('Gravity via short-long') r_coarse = 0.15 r_soft = 0.01 print('Long-Short split of forces') fs = p3m.get_force_split(r_split=r_coarse, mode='cubic') #, kernel_pts=200) pairs, accel_short = p3m.pp_accel(fs, wts, pos, r_soft) accel_long = p3m.PMAccel(fs).accel(wts, pos) accel_LS = accel_short + accel_long print('Total pairs {:,}'.format(pairs)) print('Gravity via short-med-long') ifs = p3m.IntermediateGrid(r_coarse=r_coarse, ngrid=64, centre=(0.5, 0.5, 0.5), hw_min=r_coarse) print('Rlong', ifs.r_coarse, 'Rfine', ifs.r_fine, 'box width', ifs.hw * 2) idx_in, idx_in_nonghost, idx_out, idx_out_nonghost = ifs.split_in_vs_out( pos) print('Number of points in central region', len(idx_in_nonghost)) print('Number of points outside is {:,}, including ghosts is {:,}'.format( len(idx_out_nonghost), len(idx_out))) accel_int_pm = ifs.pm_accel(pos[idx_in], wts[idx_in], idx_in_nonghost) accel_int_pp = ifs.pp_accel(pos[idx_in], wts[idx_in], idx_in_nonghost, r_soft) accel_int = accel_int_pm + accel_int_pp pairs, accel_short = p3m.pp_accel(fs, wts[idx_out], pos[idx_out], r_soft) accel_short = accel_short[idx_out_nonghost] # add all the components accel_LMS = accel_long.copy() acs = zeros_like(accel_LMS) acs[idx_out[idx_out_nonghost]] += accel_short aci_pm = zeros_like(accel_LMS) aci_pm[idx_in[idx_in_nonghost]] += accel_int_pm aci_pp = zeros_like(accel_LMS) aci_pp[idx_in[idx_in_nonghost]] += accel_int_pp accel_LMS += aci_pm + aci_pp + acs print('RMS Long-short', rms(accel_LS)) print('RMS Long-Med-Short', rms(accel_LMS)) ix = arange(npts) #idx_in[idx_in_nonghost] max_acc = sqrt(max(square(accel_LS).sum(1))) err = accel_LMS[ix] - accel_LS[ix] err2 = square(err).sum(1) idx_max = argmax(err2) rad_max = sqrt(square(pos[idx_max] - 0.5).sum()) print('Position of maximum', pos[idx_max], 'radius', rad_max, 'AccelLS', accel_LS[idx_max], 'AccelLMS', accel_LMS[idx_max]) print('M', aci_pm[idx_max], aci_pp[idx_max], 'L', accel_long[idx_max]) print('Index of max', idx_max) max_error = sqrt(err2.max()) print('Max error/max_acc', max_error / max_acc) assert (max_error < 0.01 * max_acc) print('RMS error/RMS acc', rms(err) / rms(accel_LS)) assert (rms(err) < 0.01 * rms(accel_LS))
def test_cosmological_vels_agree_accel(): """ Linearity of cosmological accelerations. The cosmological ICs are made in the linear regime, and thus should have velocities proportional to the gravitational acceleration. By making a realisation and using PPPM when can calculate the gravity and confirm this. """ redshift = 50 # Small box dominated by longest modes boxsize = 10.0 # Mpc/h H0 = 70.0 # km / s / Mpc a = 1.0 / (1.0 + redshift) omegaM = 0.279 omegaL = 0.721 sigma8 = 0.8 pspec = pspec_normalised_by_sigma8_expansion(efstathiou, sigma8=sigma8, a=a, omegaM=omegaM, omegaL=omegaL) ngrid = 30 disp_grid = build_displacement(boxsize=boxsize, ngrid=ngrid, power_func=pspec) disp_sampler = displacement_sampler_factory(disp_grid, boxsize) # fill a box with uniform points xyz = (mgrid[:ngrid, :ngrid, :ngrid] + 0.5) * (boxsize / ngrid) uni_pts = reshape(xyz, (3, ngrid**3)).T uni_sizes = ones(ngrid**3) * (boxsize / ngrid) dm_pos, dm_mass, dm_vel, dm_nums = mass_field(boxsize, uni_pts, uni_sizes, disp_sampler, a, omegaM, omegaL, H0) print('DM vel shape', dm_vel.shape, dm_mass.shape, dm_pos.shape, 'mass', dm_mass) rms_vel = sqrt(square(dm_vel).sum(1).mean(dtype=float64)) print('RMS velocity for DM', rms_vel) # now do gravity rcrit = 2.0 / ngrid log = VerboseTimingLog(insert_timings=True) fs = p3m.get_force_split(rcrit, mode='erf') pos = dm_pos * (1.0 / boxsize) wts = empty(pos.shape[0]) h = H0 / 100.0 wts[:] = dm_mass * G * (msun / h) / (boxsize * mpc / h)**3 print('Pos in', pos.min(axis=0), pos.max(axis=0)) pairs, acc_short = p3m.pp_accel(fs, wts, pos, r_soft=None) # Erf-split Newtonian long_force = p3m.PMAccel(fs) acc_long = long_force.accel(wts, pos) log.close() acc = (acc_short + acc_long) / (a * a * a) # d^2 x / dt^2 hubble = hubble_closure(H0 * (1e5 / mpc), omegaM, omegaL) # In linear regime of matter-dominated universe, vel = 2/3 H * acc eds_vel = acc * (boxsize * mpc / h) * 1e-5 / hubble(a) * a * 2.0 / 3.0 # in km/s physical dm_vel *= sqrt(a) # convert Gadget vels to km/s print('Samples:') print(eds_vel[10], 'km/s velocity (EdS approx from accel)') print(dm_vel[10], 'km/s velocity from linear theory') err = rms(dm_vel - eds_vel) / rms(dm_vel) print('Error in', err.min(), err.max(), 'mean', err.mean()) assert (err.mean() < 0.06)