def _grid_accel_deriv(pos, wts, vel, fft_wts, log): """ Accel and rate of change on the grid using the given FFT weights """ pos = array(pos) fft_size = fft_wts.shape[0] # extra fft_size so that finite difference is grad (dx = 1/fft_size) nwts = array(wts) * fft_size print('Doing {:,}^3 CIC'.format(fft_size), file=log) # momenta in grid coords if isscalar(nwts): mom = vel * nwts * fft_size else: mom = vel * reshape(nwts, (len(nwts), 1)) * fft_size cic = get_cic(pos * fft_size, fft_size, mass=nwts, mom=mom) print('Total mass on grid %3e. Doing FFT of weights.' % cic.sum().real, file=log) modes = fftn(cic) print(MU.OKBLUE + 'Inverse {:,}^3 FFT'.format(fft_size) + MU.ENDC, file=log) pot_times_n = ifftn(modes * fft_wts) # imaginary part contains dpot/dt print('Gradient via finite-differences of accel', file=log) acc = gradient_5pt_3d(pot_times_n.real) # dx = 1/ngrid print('Gradient for da/dt', file=log) da_dt = gradient_5pt_3d(pot_times_n.imag) # dx = 1/ngrid print('RMS and maximum', file=log) rms_acc = (3 * square(acc).mean())**0.5 max_da_dt = square(da_dt).sum(3).max()**0.5 if max_da_dt <= 0: raise Exception( 'Zero acceleration rate => zero vel or floating point problem?') dt_est = rms_acc / max_da_dt return dt_est, acc, da_dt
def test_grad(mode): """ Plot acceleration split into long and short range split """ import sys ngrid = 64 r_fine = 6.0 / ngrid fs = get_force_split(r_fine, mode=mode) # 'cubic', 'quartic' fft_wts = fs.get_fft_wts(sys.stdout) kernel = ifftn(fft_wts).real acc = sqrt(square(gradient_5pt_3d(kernel) * ngrid).sum(3).ravel()) import numpy as np import pylab as pl from lizard.grid import make_k_values r = make_k_values(1.0, ngrid)[1] r *= 1.0 / (ngrid * 2 * pi) # pl.semilogy(r.ravel(),abs(kernel.ravel()), 'k,') pl.semilogy(r.ravel(), acc, 'k,') pl.axvline(r_fine, c='k', ls=':') acc_from_pp = fs.get_kernel( 1e-3, 200) # these are multipliers that you multiply by r r = (arange(len(acc_from_pp)) + 1) * r_fine / len(acc_from_pp) acc_from_pp = 1 / (r * r) + (acc_from_pp * r) #- 1/(r*r) pl.semilogy(r, acc_from_pp, 'b') r = np.linspace(r_fine, 0.75**0.5, 200) acc_cubic_mid = -1.0 / (r * r) # + (4*r*r_coarse - 3*r*r)/(r_coarse**4) pl.semilogy(r, abs(acc_cubic_mid), 'g') pl.show()
def test_inter(): ngrid = 96 r_fine = 6.0 / ngrid r_coarse = 40.0 / ngrid fft_wts = _inter_cubic_fft_wts(ngrid, r_coarse, r_fine) kernel = ifftn(fft_wts).real acc = sqrt(square(gradient_5pt_3d(kernel) * ngrid).sum(3).ravel()) import pylab as pl # pl.imshow(kernel[0]) from lizard.grid import make_k_values r = make_k_values(1.0, ngrid)[1] r *= 1.0 / (ngrid * 2 * pi) # pl.semilogy(r.ravel(),abs(kernel.ravel()), 'k,') pl.semilogy(r.ravel(), acc, 'k,') pl.axvline(r_fine, c='k', ls=':') pl.axvline(r_coarse, c='k', ls=':') acc_from_pp = _newton_soft_cubic_kernel( r_fine, None, 200) # these are multipliers that you multiply by r r = (arange(len(acc_from_pp)) + 1) * r_fine / len(acc_from_pp) acc_from_pp = abs(acc_from_pp * r + 1 / (r * r)) pl.semilogy(r, acc_from_pp) import numpy as np r = np.linspace(r_fine, r_coarse, 200) acc_cubic_mid = -1.0 / (r * r) + (4 * r * r_coarse - 3 * r * r) / (r_coarse **4) pl.semilogy(r, abs(acc_cubic_mid)) pl.show()
def _inter_pm_accel(pos, wts, topleft, width, fft_wts, idx_nonghosts, ncell_ghost, log, ncell_grad=2): """ Force on the grid using the given FFT weights """ fft_size = fft_wts.shape[0] grid_pos = array(pos) - topleft grid_pos = (fft_size / width) * (grid_pos - floor(grid_pos)) print('Isolated PM force on {:,} grid'.format(fft_size), file=log) # Check ghosts are far enough from boundary to avoid periodic effects ghost_range = ncell_grad // 2 if grid_pos.min() < ghost_range or grid_pos.max() > fft_size - ghost_range: print('Grid pos in', grid_pos.min(axis=0), grid_pos.max(axis=0), file=log) raise Exception('Ghosts too close to boundary to be isolated') print('Doing {:,}^3 CIC'.format(fft_size), file=log) cic = get_cic(grid_pos, fft_size, wts) print('Total mass on grid %3f. Doing FFT of weights.' % cic.sum(), file=log) modes = fftn(cic) print('Inverse {:,}^3 FFT'.format(fft_size), file=log) pot = ifftn(modes * fft_wts).real print('Gradient via finite-differences', file=log) clip = int( ncell_ghost ) - ncell_grad # Clip off boundaries that were only used for ghosts & gradients scale = fft_size / ( width * width ) # dx = 1/ngrid, then because we scaled all the positions, we diluted the r^-2 force, need to rescale back pot = pot[clip:-clip, clip:-clip, clip:-clip] * scale accel_grid = gradient_5pt_3d(pot) pos_ng = grid_pos[idx_nonghosts] - clip print('interpolating {:,} points'.format(len(pos_ng)), file=log) if pos_ng.min() < 0.0 or pos_ng.max() > fft_size - 2 * clip: print('Grid pos non-ghost in', pos_ng.min(axis=0), pos_ng.max(axis=0), file=log) raise Exception( 'Real particles outside central region of isolated grid') return interp_vec3(accel_grid, pos_ng)
def _grid_accel(pos, wts, fft_wts, log): """ Acceleration on PM grid using the given FFT weights """ fft_size = fft_wts.shape[0] npos = array(pos) * fft_size print('Doing {:,}^3 CIC'.format(fft_size), file=log) # extra fft_size in wts to make finite difference the grad (dx = 1/fft_size) wts = array(wts) * fft_size cic = get_cic(npos, fft_size, wts) print('Total mass on grid %3f. Doing FFT of weights.' % cic.sum(), file=log) modes = fftn(cic) print(MU.OKBLUE + 'Inverse {:,}^3 FFT'.format(fft_size) + MU.ENDC, file=log) npot = ifftn(modes * fft_wts).real # potential * fft_size print('Gradient via finite-differences', file=log) grad = gradient_5pt_3d(npot) print('interpolating {:,} points'.format(len(pos)), file=log) accel_long = interp_vec3(grad, npos) return accel_long