def compute_propagator(size, distance, lam, pixel_size, fresnel=True, region=None, apply_phase_factor=False, mollified=True, queue=None, block=False): """Create a propagator with (*size*, *size*) dimensions for propagation *distance*, wavelength *lam* and *pixel_size*. If *fresnel* is True, use the Fresnel approximation, if it is False, use the full propagator (don't approximate the square root). *region* is the diameter of the the wavefront area which is capable of interference. If *apply_phase_factor* is True, apply the phase factor defined by Fresnel approximation. If *mollified* is True the aliased frequencies are suppressed. If command *queue* is specified, execute the kernel on it. If *block* is True, wait for the kernel to finish. """ if size % 2: raise ValueError('Only even sizes are supported') if queue is None: queue = cfg.OPENCL.queue # Check the sampling r_cutoff = compute_aliasing_limit(size, lam, pixel_size, distance, fov=region, fourier=False) min_n = 4 if r_cutoff < min_n: LOG.error('Propagator too narrow, propagation distance too small or pixel size too large') f_cutoff = compute_aliasing_limit(size, lam, pixel_size, distance, fov=region, fourier=True) if f_cutoff < min_n: LOG.error('Propagator too wide, propagation distance too large or pixel size too small') out = cl_array.Array(queue, (size, size), cfg.PRECISION.np_cplx) if apply_phase_factor: phase_factor = np.exp(2 * np.pi * distance.simplified / lam.simplified * 1j) else: phase_factor = 0 + 0j ev = cfg.OPENCL.programs['physics'].propagator(queue, (size / 2 + 1, size / 2 + 1), None, out.data, cfg.PRECISION.np_float(distance.simplified), cfg.PRECISION.np_float(lam.simplified), cfg.PRECISION.np_float(pixel_size.simplified), g_util.make_vcomplex(phase_factor), np.int32(fresnel)) if block: ev.wait() if mollified: fwtm = compute_aliasing_limit(size, lam, pixel_size, distance, fov=size * pixel_size, fourier=True) if region is not None: fwtm_region = compute_aliasing_limit(size, lam, pixel_size, distance, region, fourier=True) fwtm = min(fwtm_region, fwtm) sigma = fwnm_to_sigma(fwtm, n=10) mollifier = get_gauss_2d(size, sigma, fourier=False, queue=queue, block=block) out = out * mollifier return out
def make_source_blur(self, shape, pixel_size, queue=None, block=False): """Make geometrical source blurring kernel with *shape* (y, x) size and *pixel_size*. Use OpenCL command *queue* and *block* if True. """ l = self.source.sample_distance size = self.source.size width = (self.propagation_distance * size[1] / l).simplified.magnitude height = (self.propagation_distance * size[0] / l).simplified.magnitude sigma = (smath.fwnm_to_sigma(height, n=2), smath.fwnm_to_sigma(width, n=2)) * q.m return ip.get_gauss_2d(shape, sigma, pixel_size=pixel_size, fourier=True, queue=queue, block=block)
def _test_gauss(self, shape, fourier): """Test if the gauss in Fourier space calculated on a GPU is the same as Fourier transform of a gauss in real space. """ sigma = (shape[0] * self.pixel_size.magnitude, shape[1] / 2 * self.pixel_size.magnitude) * self.pixel_size.units if fourier: # Make the profile broad sigma = (1. / sigma[0].magnitude, 1. / sigma[1].magnitude) * sigma.units gauss = ip.get_gauss_2d(shape, sigma, self.pixel_size, fourier=fourier).get() gt = get_gauss_2d(shape, sigma, self.pixel_size, fourier=fourier) np.testing.assert_almost_equal(gauss, gt)
def apply_blur(self, intensity, distance, pixel_size, queue=None, block=False): """Apply source blur based on van Cittert-Zernike theorem at *distance*.""" fwhm = (distance * self.size / self.sample_distance).simplified sigma = smath.fwnm_to_sigma(fwhm, n=2) psf = ip.get_gauss_2d(intensity.shape, sigma, pixel_size=pixel_size, fourier=True, queue=queue, block=block) return ip.ifft_2(ip.fft_2(intensity) * psf).real
def make_source_blur(self, shape, pixel_size, queue=None, block=False): """Make geometrical source blurring kernel with *shape* (y, x) size and *pixel_size*. Use OpenCL command *queue* and *block* if True. """ l = self.source.sample_distance size = self.source.size width = (self.propagation_distance * size[1] / l).simplified.magnitude height = (self.propagation_distance * size[0] / l).simplified.magnitude sigma = (smath.fwnm_to_sigma( height, n=2), smath.fwnm_to_sigma(width, n=2)) * q.m return ip.get_gauss_2d(shape, sigma, pixel_size=pixel_size, fourier=True, queue=queue, block=block)
def compute_intensity(self, t_0, t_1, shape, pixel_size, queue=None, block=False, flat=False): """Compute intensity between times *t_0* and *t_1*.""" exp_time = (t_1 - t_0).simplified.magnitude if queue is None: queue = cfg.OPENCL.queue u = cl_array.Array(queue, shape, dtype=cfg.PRECISION.np_cplx) u_sample = cl_array.zeros(queue, shape, cfg.PRECISION.np_cplx) intensity = cl_array.zeros(queue, shape, cfg.PRECISION.np_float) for energy in self.energies: u.fill(1) for oeid, oe in enumerate(self.oe): if flat and oe == self.sample: continue u *= oe.transfer(shape, pixel_size, energy, t=t_0, queue=queue, out=u_sample, check=False, block=block) # Propagate and blur optical element when not source if self.distances[oeid] != 0 * q.m and oe != self.source: lam = energy_to_wavelength(energy) propagator = compute_propagator(u.shape[0], self.distances[oeid], lam, pixel_size, queue=queue, block=block, mollified=True) ip.fft_2(u, queue=queue, block=block) sdistance = np.sum(self.distances[:oeid + 1]) fwhm = (self.distances[oeid] * self.source.size / sdistance).simplified sigma = smath.fwnm_to_sigma(fwhm, n=2) psf = ip.get_gauss_2d(shape, sigma, pixel_size=pixel_size, fourier=True, queue=queue, block=block) u *= psf u *= propagator ip.ifft_2(u, queue=queue, block=block) intensity += self.detector.convert(abs(u)**2, energy) return intensity * exp_time
def compute_propagator(size, distance, lam, pixel_size, fresnel=True, region=None, apply_phase_factor=False, mollified=True, queue=None, block=False): """Create a propagator with (*size*, *size*) dimensions for propagation *distance*, wavelength *lam* and *pixel_size*. If *fresnel* is True, use the Fresnel approximation, if it is False, use the full propagator (don't approximate the square root). *region* is the diameter of the the wavefront area which is capable of interference. If *apply_phase_factor* is True, apply the phase factor defined by Fresnel approximation. If *mollified* is True the aliased frequencies are suppressed. If command *queue* is specified, execute the kernel on it. If *block* is True, wait for the kernel to finish. """ if size % 2: raise ValueError('Only even sizes are supported') if queue is None: queue = cfg.OPENCL.queue pixel_size = make_tuple(pixel_size) def check_cutoff(ps): # Check the sampling r_cutoff = compute_aliasing_limit(size, lam, ps, distance, fov=region, fourier=False) min_n = 4 if r_cutoff < min_n: LOG.error('Propagator too narrow, propagation distance too small or pixel size too large') f_cutoff = compute_aliasing_limit(size, lam, ps, distance, fov=region, fourier=True) if f_cutoff < min_n: LOG.error('Propagator too wide, propagation distance too large or pixel size too small') check_cutoff(pixel_size[1]) check_cutoff(pixel_size[0]) out = cl_array.Array(queue, (size, size), cfg.PRECISION.np_cplx) if apply_phase_factor: phase_factor = np.exp(2 * np.pi * distance.simplified / lam.simplified * 1j) else: phase_factor = 0 + 0j ev = cfg.OPENCL.programs['physics'].propagator(queue, (size / 2 + 1, size / 2 + 1), None, out.data, cfg.PRECISION.np_float(distance.simplified), cfg.PRECISION.np_float(lam.simplified), g_util.make_vfloat2(*pixel_size[::-1].simplified), g_util.make_vcomplex(phase_factor), np.int32(fresnel)) if block: ev.wait() if mollified: def compute_sigma_component(ps): fwtm = compute_aliasing_limit(size, lam, ps, distance, fov=size * ps, fourier=True) if region is not None: fwtm_region = compute_aliasing_limit(size, lam, ps, distance, region, fourier=True) fwtm = min(fwtm_region, fwtm) sigma = fwnm_to_sigma(fwtm, n=10) return sigma sigma = (compute_sigma_component(pixel_size[0]), compute_sigma_component(pixel_size[1])) mollifier = get_gauss_2d(size, sigma, fourier=False, queue=queue, block=block) out = out * mollifier return out