def RelaunchRays1(self, xp, eikonal, vg, a, L, material): '''Use wave data to create a new ray distribution. At present azimuthal phase variation is ignored. :param numpy.ndarray xp: phase space data with shape (Nb,7,8) :param numpy.ndarray eik: eikonal data with shape (Nb,4) :param numpy.ndarray vg: group velocity with shape (Nb,7,4) :param numpy.ndarray a: vector potential with shape (Nw,Nx,Ny) :param float L: length of the wave zone :param class material: dispersion object rays are emerging from''' # Assume vacuum for now wn, xn, yn, ext = self.GetGridInfo() a, xn = grid_tools.AddGhostCells(a, xn, 1) # Work out the eikonal wavevectors indirectly # This is equivalent to k = grad(phase), but does not require unwrapping phase eps = np.max(np.abs(a)) * 1e-6 if xn.shape[0] > 1: dx = xn[1] - xn[0] adxastar = a * np.gradient(np.conj(a), dx, axis=1) kx = np.real(0.5j * (adxastar - np.conj(adxastar))) / (eps + np.abs(a))**2 else: dx = 1.0 kx = np.zeros(a.shape) phase = scipy.integrate.cumtrapz(kx, dx=dx, axis=1, initial=0.0) phase -= 0.25 * kx[:, (0, ), :] * dx # Rays keep their original frequency and transverse positions. # Frequency shifts are still accounted for because amplitude may change. rho, phi = self.GetCylCoords(xp) impact = np.where( np.logical_and(rho[:, 0] < xn[-1], xp[:, 0, 4] < wn[-1]))[0] w = xp[impact, :, 4] x = rho[impact, :] y = phi[impact, :] try: kr = grid_tools.DataFromGrid(w, x, y, wn, xn, yn, kx) except: kr = np.zeros(y.shape) kr[np.where(kr**2 >= w**2)] = 0.0 xp[impact, :, 5] = kr * np.cos(y) xp[impact, :, 6] = kr * np.sin(y) xp[impact, :, 7] = np.sqrt(w**2 - xp[impact, :, 5]**2 - xp[impact, :, 6]**2) vg[impact, ...] = xp[impact, ..., 4:8] / xp[impact, ..., 4:5] speed = np.sqrt(vg[impact, :, 1]**2 + vg[impact, :, 2]**2 + vg[impact, :, 3]**2) xp[impact, :, 0] += L / speed xp[impact, :, 3] += L try: eikonal[impact, 0] = grid_tools.DataFromGrid(w[:, 0], x[:, 0], y[:, 0], wn, xn, yn, phase) eikonal[impact, 1] = grid_tools.DataFromGrid(w[:, 0], x[:, 0], y[:, 0], wn, xn, yn, np.abs(a)) except: eikonal[impact, 0] *= 1.0 eikonal[impact, 1] *= 1.0 eikonal[impact, 2] = 0.0 eikonal[impact, 3] = 0.0
def RelaunchRays(self, xp, eikonal, vg, a, L, material): '''Use wave data to create a new ray distribution. :param numpy.ndarray xp: phase space data with shape (Nb,7,8) :param numpy.ndarray eik: eikonal data with shape (Nb,4) :param numpy.ndarray vg: group velocity with shape (Nb,7,4) :param numpy.ndarray a: vector potential with shape (Nw,Nx,Ny) :param float L: length of the wave zone :param class material: dispersion object rays are emerging from''' warnings.warn( 'Using simplified ray relaunch; only valid for close couplings.') # Assume vacuum for now wn, xn, yn, ext = self.GetGridInfo() # Rays keep their original frequency and transverse positions. # Frequency shifts are still accounted for because amplitude may change. rho, phi = self.GetCylCoords(xp) wtest = np.logical_and(xp[:, 0, 4] >= wn[0], xp[:, 0, 4] <= wn[-1]) impact = np.where(np.logical_and(rho[:, 0] <= xn[-1], wtest))[0] w = xp[impact, :, 4] x = rho[impact, :] y = phi[impact, :] xp[impact, :, 5] = 0.0 xp[impact, :, 6] = 0.0 xp[impact, :, 7] = w vg[impact, ...] = xp[impact, ..., 4:8] / xp[impact, ..., 4:5] xp[impact, :, 0] += L / material.GroupVelocityMagnitude(w) xp[impact, :, 3] += L eikonal[impact, 0] = w[:, 0] * xp[impact, 0, 0] + grid_tools.DataFromGrid( w[:, 0], x[:, 0], y[:, 0], wn, xn, yn, np.angle(a)) eikonal[impact, 1] = grid_tools.DataFromGrid(w[:, 0], x[:, 0], y[:, 0], wn, xn, yn, np.abs(a)) eikonal[impact, 2] = 0.0 eikonal[impact, 3] = 0.0
def test_full_slab_edge(self): a = np.ones((4, 4, 4)) a[2, 2, 3] = 2.0 wn = grid_tools.cyclic_nodes(0.0, 4.0, 4) xn = grid_tools.cell_centers(0.0, 4.0, 4) yn = grid_tools.cell_centers(0.0, 4.0, 4) w = np.array([2.0, 2.0, 2.0]) x = np.array([2.5, 2.5, 2.5]) y = np.array([4.0, 3.5, 3.0]) data = grid_tools.DataFromGrid(w, x, y, wn, xn, yn, a) assert np.allclose(data, [2.5, 2.0, 1.5])
def RelaunchRays(self, xp, eikonal, vg, a, L): '''Use wave data to create a new ray distribution. :param numpy.ndarray xp: phase space data with shape (Nb,7,8) :param numpy.ndarray eik: eikonal data with shape (Nb,4) :param numpy.ndarray vg: group velocity with shape (Nb,7,4) :param numpy.ndarray a: vector potential with shape (Nw,Nx,Ny) :param float L: length of the wave zone''' # Assume vacuum for now wn, xn, yn, ext = self.GetGridInfo() # Work out the eikonal wavevectors indirectly # This is equivalent to k = grad(phase), but does not require unwrapping phase eps = np.max(np.abs(a)) * 1e-6 if xn.shape[0] > 1: dx = xn[1] - xn[0] adxastar = a * np.gradient(np.conj(a), dx, axis=1) kx = np.real(0.5j * (adxastar - np.conj(adxastar))) / (eps + np.abs(a))**2 else: dx = 1.0 kx = np.zeros(a.shape) if yn.shape[0] > 1: dy = yn[1] - yn[0] adyastar = a * np.gradient(np.conj(a), dy, axis=2) ky = np.real(0.5j * (adyastar - np.conj(adyastar))) / (eps + np.abs(a))**2 else: dy = 1.0 ky = np.zeros(a.shape) # Work out the phase using del^2(phase) = div(k) # Solve in Fourier space: -K2*phase = iKx*kx + iKy*ky iKx = 1j * np.outer(self.T.k_diff(1), np.ones(kx.shape[2])) iKy = 1j * np.outer(np.ones(ky.shape[1]), self.T.k_diff(2)) source = iKx * np.fft.fft(np.fft.fft( kx, axis=1), axis=2) + iKy * np.fft.fft(np.fft.fft(ky, axis=1), axis=2) K2m = iKx**2 + iKy**2 source[:, 0, 0] = 0.0 K2m[0, 0] = 1.0 phase = np.real(np.fft.ifft(np.fft.ifft(source / K2m, axis=2), axis=1)) # Rays keep their original frequency and transverse positions. # Frequency shifts are still accounted for because amplitude may change. impact = self.ClipRaysToGrid(xp) w = xp[impact, :, 4] x = xp[impact, :, 1] y = xp[impact, :, 2] k1 = grid_tools.DataFromGrid(w, x, y, wn, xn, yn, kx) k2 = grid_tools.DataFromGrid(w, x, y, wn, xn, yn, ky) sel = np.where(k1**2 + k2**2 >= w**2) k1[sel] = 0.0 k2[sel] = 0.0 xp[impact, :, 0] += L xp[impact, :, 3] += L xp[impact, :, 5] = k1 xp[impact, :, 6] = k2 xp[impact, :, 7] = np.sqrt(w**2 - k1**2 - k2**2) eikonal[impact, 0] = grid_tools.DataFromGrid(w[:, 0], x[:, 0], y[:, 0], wn, xn, yn, phase) eikonal[impact, 1] = grid_tools.DataFromGrid(w[:, 0], x[:, 0], y[:, 0], wn, xn, yn, np.abs(a)) eikonal[impact, 2] = 0.0 eikonal[impact, 3] = 0.0 vg[impact, ...] = xp[impact, ..., 4:8] / xp[impact, ..., 4:5]