def test_transmission_sampling(self): def compute_transmission_function(n, ps, energy, material): wedge = np.tile(np.arange(n), [n, 1]) * ps wedge = StaticBody(wedge, ps, material=material) return wedge.transfer((n, n), ps, energy, exponent=True) def compute_distance(n, ps, lam, ps_per_lam): ca = (lam / (ps_per_lam * ps)).simplified.magnitude alpha = np.arccos(ca) theta = np.pi / 2 - alpha return (n * ps / (2 * np.tan(theta))).simplified n = 32 ps = 1 * q.um energies = np.arange(5, 30) * q.keV energy = 10 * q.keV lam = physics.energy_to_wavelength(energy) # Delta causes phase shift between two adjacent pixels by 2 Pi delta = (lam / ps).simplified.magnitude ri = np.ones_like(energies.magnitude, dtype=np.complex) * delta + 0j material = Material('dummy', ri, energies) # Single object u = compute_transmission_function(n, ps, energy, material) self.assertFalse(physics.is_wavefield_sampling_ok(u)) # 4x supersampling => phase shift Pi/2 u = compute_transmission_function(4 * n, ps / 4, energy, material) self.assertTrue(physics.is_wavefield_sampling_ok(u)) # 4x supersampling with 2 objects => phase shift Pi n *= 4 ps /= 4 wedge = np.tile(np.arange(n), [n, 1]) * ps wedge = StaticBody(wedge, ps, material=material) u = physics.transfer_many([wedge, wedge], (n, n), ps, energy, exponent=True) self.assertFalse(physics.is_wavefield_sampling_ok(u)) # X-ray source with a parabolic phase profile n = 128 ps = 1 * q.um trajectory = Trajectory([(n / 2, n / 2, 0)] * ps) # 1 pixel per wavelength => insufficient sampling d = compute_distance(n, ps, lam, 1) source = BendingMagnet(2.5 * q.GeV, 150 * q.mA, 1.5 * q.T, d, 1, np.array([0.2, 0.8]) * q.mm, ps, trajectory, phase_profile='parabola') u = source.transfer((n, n), ps, energy, exponent=True) self.assertFalse(physics.is_wavefield_sampling_ok(u)) # 4 pixel per wavelength => good sampling d = compute_distance(n, ps, lam, 4) source = BendingMagnet(2.5 * q.GeV, 150 * q.mA, 1.5 * q.T, d, 1, np.array([0.2, 0.8]) * q.mm, ps, trajectory, phase_profile='parabola') u = source.transfer((n, n), ps, energy, exponent=True) self.assertTrue(physics.is_wavefield_sampling_ok(u))
def _transfer( self, shape, pixel_size, energy, offset, exponent=False, t=None, queue=None, out=None, check=True, block=False, ): """Compute the flat field wavefield. Returned *out* array is different from the input one. """ if queue is None: queue = cfg.OPENCL.queue if out is None: out = cl_array.Array(queue, shape, dtype=cfg.PRECISION.np_cplx) ps = make_tuple(pixel_size) if t is None: x, y, z = self.trajectory.control_points.simplified.magnitude[0] else: x, y, z = self.trajectory.get_point(t).simplified.magnitude x += offset[1].simplified.magnitude y += offset[0].simplified.magnitude center = (x, y, z) phase = self.phase_profile != "plane" parabola = self.phase_profile == "parabola" compute_exponent = exponent or check and phase self._transfer_real(shape, center, ps, energy, compute_exponent, phase, parabola, out, queue, block) if compute_exponent: if check and phase and not is_wavefield_sampling_ok(out, queue=queue): LOG.error("Insufficient beam phase sampling") if not exponent: out = clmath.exp(out, queue=queue) return out
def _transfer(self, shape, pixel_size, energy, offset, exponent=False, t=None, queue=None, out=None, check=True, block=False): """Compute the flat field wavefield. Returned *out* array is different from the input one.""" if queue is None: queue = cfg.OPENCL.queue ps = make_tuple(pixel_size) if t is None: x, y, z = self.trajectory.control_points.simplified.magnitude[0] else: x, y, z = self.trajectory.get_point(t).simplified.magnitude x += offset[1].simplified.magnitude y += offset[0].simplified.magnitude center = (x, y, z) cl_center = gutil.make_vfloat3(*center) cl_ps = gutil.make_vfloat2(*pixel_size.simplified.magnitude[::-1]) fov = np.arange(0, shape[0]) * ps[0] - y * q.m angles = np.arctan((fov / self.sample_distance).simplified) profile = self._create_vertical_profile(energy, angles, ps[0]).rescale( 1 / q.s).magnitude profile = cl_array.to_device(queue, profile.astype(cfg.PRECISION.np_float)) if out is None: out = cl_array.Array(queue, shape, dtype=cfg.PRECISION.np_cplx) z_sample = self.sample_distance.simplified.magnitude lam = energy_to_wavelength(energy).simplified.magnitude phase = self.phase_profile != 'plane' parabola = self.phase_profile == 'parabola' if exponent or check and phase: ev = cfg.OPENCL.programs['physics'].make_flat( queue, shape[::-1], None, out.data, profile.data, cl_center, cl_ps, cfg.PRECISION.np_float(z_sample), cfg.PRECISION.np_float(lam), np.int32(True), np.int32(phase), np.int32(parabola)) if check and phase and not is_wavefield_sampling_ok(out, queue=queue): LOG.error('Insufficient beam phase sampling') if not exponent: out = clmath.exp(out, queue=queue) else: ev = cfg.OPENCL.programs['physics'].make_flat( queue, shape[::-1], None, out.data, profile.data, cl_center, cl_ps, cfg.PRECISION.np_float(z_sample), cfg.PRECISION.np_float(lam), np.int32(exponent), np.int32(phase), np.int32(parabola)) if block: ev.wait() return out