def _transfer_real(self, shape, center, pixel_size, energy, exponent, compute_phase, is_parabola, out, queue, block, flux=1): """Compte the actual wavefield. *center*, *pixel_size*, *sample_distance* and *wavelength* are all unitless values which can be passed directly to OpenCL kernels. """ cl_center = gutil.make_vfloat3(*center) cl_ps = gutil.make_vfloat2(*pixel_size.simplified.magnitude[::-1]) z_sample = self.sample_distance.simplified.magnitude lam = energy_to_wavelength(energy).simplified.magnitude kernel = cfg.OPENCL.programs['physics'].make_flat_from_scalar ev = kernel(queue, shape[::-1], None, out.data, cfg.PRECISION.np_float(flux), cl_center, cl_ps, cfg.PRECISION.np_float(z_sample), cfg.PRECISION.np_float(lam), np.int32(exponent), np.int32(compute_phase), np.int32(is_parabola)) if block: ev.wait()
def main(): """main""" syris.init(double_precision=True) n = 512 ps = 0.5 * q.um energy = 10 * q.keV lam = energy_to_wavelength(energy) # Compute the sampling limit for given n, ps and lam ca = (lam / 2 / ps).simplified.magnitude tga = np.tan(np.arccos(ca)) distance = (tga * n * ps / 2).simplified print 'Propagation distance:', distance propagator = compute_propagator(n, distance, lam, ps, apply_phase_factor=True, mollified=False).get() full_propagator = compute_propagator(n, distance, lam, ps, fresnel=False, mollified=False).get() np_propagator = compute_fourier_propagator(n, lam, distance, ps) np_full_propagator = compute_fourier_propagator(n, lam, distance, ps, fresnel=False) diff = propagator - np_propagator full_diff = full_propagator - np_full_propagator show(np.fft.fftshift(propagator.real), 'Syris Fresnel Propagator (Real Part)') show(np.fft.fftshift(np_propagator.real), 'Numpy Fresnel propagator (Real Part)') show(np.fft.fftshift(diff.real), 'Fresnel Syris - Fresnel Numpy (Real Part)') show(np.fft.fftshift(full_propagator.real), 'Syris Full Propagator (Real Part)') show(np.fft.fftshift(np_full_propagator.real), 'Numpy Full propagator (Real Part)') show(np.fft.fftshift(full_diff.real), 'Full Syris - Full Numpy (Real Part)') plt.show()
def _transfer( self, shape, pixel_size, energy, offset, exponent=False, t=None, queue=None, out=None, check=True, block=False, ): """Transfer function implementation based on a refractive index.""" ri = self.material.get_refractive_index(energy) lam = energy_to_wavelength(energy) proj = self.project(shape, pixel_size, offset=offset, t=t, queue=queue, out=out, block=block) return transfer(proj, ri, lam, exponent=exponent, queue=queue, out=out, check=check, block=block)
def _transfer( self, shape, pixel_size, energy, offset, exponent=False, t=None, queue=None, out=None, check=True, block=False, ): """Transfer function implementation. Only *energy* is relevant because a filter has the same thickness everywhere. """ lam = energy_to_wavelength(energy).simplified.magnitude thickness = self.thickness.simplified.magnitude ri = self.material.get_refractive_index(energy) result = -2 * np.pi / lam * thickness * (ri.imag + ri.real * 1j) if not exponent: result = np.exp(result) return result.astype(cfg.PRECISION.np_cplx)
def main(): args = parse_args() syris.init() n = 32 ps = 1 * q.um energies = np.arange(5, 30) * q.keV energy = 10 * q.keV lam = 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) fmt = 'Computing with n: {:>4}, pixel size: {}' wedge = np.tile(np.arange(n), [n, 1]) * ps # Non-supersampled object shape causes phase shifts 0, 2Pi, 4Pi, ..., thus the phase is constant # as an extreme result of aliasing print fmt.format(n, ps) u = compute_transmission_function(n, ps, 1, energy, material) # Supersampling helps resolve the transmission function print fmt.format(n * args.supersampling, ps / args.supersampling) u_s = compute_transmission_function(n, ps, args.supersampling, energy, material) show(wedge.magnitude, title='Projected Object [um]') show(u.real, title='Re[T(x, y)]') show(u_s.real, title='Re[T(x, y)] Supersampled') plt.show()
def test_transfer_many(self): n = 32 shape = (n, n) 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 Pi / 16 delta = (lam / (32 * ps)).simplified.magnitude ri = np.ones_like(energies.magnitude, dtype=np.complex) * delta + 0j material = Material('dummy', ri, energies) wedge = np.tile(np.arange(n), [n, 1]) * ps wedge = StaticBody(wedge, ps, material=material) # Test more objects u_many = physics.transfer_many([wedge, wedge], shape, ps, energy).get() # 2 objects u = wedge.transfer(shape, ps, energy).get()**2 np.testing.assert_almost_equal(u, u_many) # Test exponent u = physics.transfer_many([wedge], shape, ps, energy, exponent=False).get() u_exp = physics.transfer_many([wedge], shape, ps, energy, exponent=True).get() np.testing.assert_almost_equal(u, np.exp(u_exp))
def main(): args = parse_args() syris.init() n = 32 ps = 1 * q.um energies = np.arange(5, 30) * q.keV energy = 10 * q.keV lam = 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) fmt = "Computing with n: {:>4}, pixel size: {}" wedge = np.tile(np.arange(n), [n, 1]) * ps # Non-supersampled object shape causes phase shifts 0, 2Pi, 4Pi, ..., thus the phase is constant # as an extreme result of aliasing print(fmt.format(n, ps)) u = compute_transmission_function(n, ps, 1, energy, material) # Supersampling helps resolve the transmission function print(fmt.format(n * args.supersampling, ps / args.supersampling)) u_s = compute_transmission_function(n, ps, args.supersampling, energy, material) show(wedge.magnitude, title="Projected Object [um]") show(u.real, title="Re[T(x, y)]") show(u_s.real, title="Re[T(x, y)] Supersampled") plt.show()
def test_attenuation(self): ref_index = 1e-7 + 1e-10j energy = 20 * q.keV lam = physics.energy_to_wavelength(energy) gt = 4 * np.pi * ref_index.imag / lam.simplified self.assertAlmostEqual( gt, physics.ref_index_to_attenuation_coeff(ref_index, lam))
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 test_transfer(self): thickness = self.thickness.simplified.magnitude lam = energy_to_wavelength(self.energy).simplified.magnitude coeff = -2 * np.pi * thickness / lam ri = self.material.get_refractive_index(self.energy) gt = np.exp(coeff * (ri.imag + ri.real * 1j)) fltr = self.fltr.transfer(None, None, self.energy) self.assertAlmostEqual(gt, fltr)
def test_transfer(self): go = StaticBody(np.arange(4 ** 2).reshape(4, 4) * q.um, 1 * q.um, material=self.material) transferred = go.transfer((4, 4), 1 * q.um, self.energy).get() gt = transfer( go.thickness, self.material.get_refractive_index(self.energy), energy_to_wavelength(self.energy), ).get() np.testing.assert_almost_equal(gt, transferred)
def _transfer(self, shape, pixel_size, energy, offset, exponent=False, t=None, queue=None, out=None, check=True, block=False): """Transfer function implementation based on a refractive index.""" ri = self.material.get_refractive_index(energy) lam = energy_to_wavelength(energy) proj = self.project(shape, pixel_size, offset=offset, t=t, queue=queue, out=out, block=block) return transfer(proj, ri, lam, exponent=exponent, queue=queue, out=out, check=check, block=block)
def main(): args = parse_args() syris.init() # Propagate to 20 cm d = 5 * q.cm # Compute grid n_camera = 256 n = n_camera * args.supersampling shape = (n, n) material = get_material("pmma_5_30_kev.mat") energy = 15 * q.keV ps = 1 * q.um ps_hd = ps / args.supersampling radius = n / 4.0 * ps_hd fmt = " Wavelength: {}" print(fmt.format(energy_to_wavelength(energy))) fmt = "Pixel size used for propagation: {}" print(fmt.format(ps_hd.rescale(q.um))) print(" Field of view: {}".format(n * ps_hd.rescale(q.um))) fmt = " Sphere diameter: {}" print(fmt.format(2 * radius)) sample = make_sphere(n, radius, pixel_size=ps_hd, material=material) projection = sample.project((n, n), ps_hd).get() * 1e6 projection = decimate(projection, (n_camera, n_camera), average=True).get() # Propagation with a monochromatic plane incident wave hd = propagate([sample], shape, [energy], d, ps_hd).get() ld = decimate(hd, (n_camera, n_camera), average=True).get() kernel = compute_tie_kernel(n_camera, ps, d, material, energy) mju = material.get_attenuation_coefficient(energy).rescale(1 / q.m).magnitude f_ld = fft_2(ld) f_ld *= get_array(kernel.astype(cfg.PRECISION.np_float)) retrieved = ifft_2(f_ld).get().real retrieved = -1 / mju * np.log(retrieved) * 1e6 if args.output_thickness: imageio.imwrite(args.output_thickness, projection) if args.output_projection: imageio.imwrite(args.output_projection, ld) if args.output_retrieved: imageio.imwrite(args.output_retrieved, retrieved) show(hd, title="High resolution") show(ld, title="Low resolution (detector)") show(retrieved, title="Retrieved [um]") show(projection, title="Projection [um]") show(projection - retrieved, title="Projection - retrieved") plt.show()
def test_transfer(self): thickness = np.linspace(0, 0.01, 16).reshape(4, 4) * q.mm energy = 10 * q.keV wavelength = physics.energy_to_wavelength(energy) refractive_index = 1e-6 + 1e-9j wavefield = physics.transfer(thickness, refractive_index, wavelength).get() exponent = -2 * np.pi * thickness.simplified / wavelength.simplified truth = np.exp(exponent * np.complex(refractive_index.imag, refractive_index.real)) np.testing.assert_almost_equal(truth, wavefield) # Exponent wavefield = physics.transfer(thickness, refractive_index, wavelength, exponent=True).get() np.testing.assert_almost_equal(truth, np.exp(wavefield))
def _transfer(self, shape, pixel_size, energy, offset, exponent=False, t=None, queue=None, out=None, check=True, block=False): """Transfer function implementation. Only *energy* is relevant because a filter has the same thickness everywhere. """ lam = energy_to_wavelength(energy).simplified.magnitude thickness = self.thickness.simplified.magnitude ri = self.material.get_refractive_index(energy) result = -2 * np.pi / lam * thickness * (ri.imag + ri.real * 1j) if not exponent: result = np.exp(result) return result.astype(cfg.PRECISION.np_cplx)
def test_transfer(self): thickness = np.linspace(0, 0.01, 16).reshape(4, 4) * q.mm energy = 10 * q.keV wavelength = physics.energy_to_wavelength(energy) refractive_index = 1e-6 + 1e-9j wavefield = physics.transfer(thickness, refractive_index, wavelength).get() exponent = - 2 * np.pi * thickness.simplified / wavelength.simplified truth = np.exp(exponent * np.complex(refractive_index.imag, refractive_index.real)) np.testing.assert_almost_equal(truth, wavefield) # Exponent wavefield = physics.transfer(thickness, refractive_index, wavelength, exponent=True).get() np.testing.assert_almost_equal(truth, np.exp(wavefield))
def _transfer_real( self, shape, center, pixel_size, energy, exponent, compute_phase, is_parabola, out, queue, block, flux=1, ): flux = (self.get_flux(energy, None, pixel_size).rescale( 1 / q.s).magnitude.astype(cfg.PRECISION.np_float)) cl_image = gutil.get_image(flux, queue=queue) sampler = cl.Sampler(cfg.OPENCL.ctx, False, cl.addressing_mode.CLAMP, cl.filter_mode.LINEAR) cl_center = gutil.make_vfloat3(*center) cl_ps = gutil.make_vfloat2(*pixel_size.simplified.magnitude[::-1]) cl_input_ps = gutil.make_vfloat2( *self._pixel_size.simplified.magnitude[::-1]) z_sample = self.sample_distance.simplified.magnitude lam = energy_to_wavelength(energy).simplified.magnitude kernel = cfg.OPENCL.programs["physics"].make_flat_from_2D_profile ev = kernel( queue, shape[::-1], None, out.data, cl_image, sampler, cl_center, cl_ps, cl_input_ps, cfg.PRECISION.np_float(z_sample), cfg.PRECISION.np_float(lam), np.int32(exponent), np.int32(compute_phase), np.int32(is_parabola), ) if block: ev.wait()
def make_phase(n, ps, d, energy, phase_profile): y, x = np.mgrid[-n // 2:n // 2, -n // 2:n // 2] * ps x = x.simplified.magnitude y = y.simplified.magnitude d = d.simplified.magnitude lam = energy_to_wavelength(energy).simplified.magnitude k = 2 * np.pi / lam if phase_profile == "parabola": r = (x**2 + y**2) / (2 * d) elif phase_profile == "sphere": r = np.sqrt(x**2 + y**2 + d**2) else: raise ValueError("Unknown phase profile '{}'".format(phase_profile)) return np.exp(k * r * 1j)
def make_phase(n, ps, d, energy, phase_profile): y, x = np.mgrid[-n / 2:n / 2, -n / 2:n / 2] * ps x = x.simplified.magnitude y = y.simplified.magnitude d = d.simplified.magnitude lam = energy_to_wavelength(energy).simplified.magnitude k = 2 * np.pi / lam if phase_profile == 'parabola': r = (x ** 2 + y ** 2) / (2 * d) elif phase_profile == 'sphere': r = np.sqrt(x ** 2 + y ** 2 + d ** 2) else: raise ValueError("Unknown phase profile '{}'".format(phase_profile)) return np.exp(k * r * 1j)
def _transfer_real( self, shape, center, pixel_size, energy, exponent, compute_phase, is_parabola, out, queue, block, ): """Compute the flat field wavefield. Returned *out* array is different from the input one. """ cl_center = gutil.make_vfloat3(*center) cl_ps = gutil.make_vfloat2(*pixel_size.simplified.magnitude[::-1]) fov = np.arange(0, shape[0]) * pixel_size[0] - center[1] * q.m angles = np.arctan((fov / self.sample_distance).simplified) profile = (self._create_vertical_profile( energy, angles, pixel_size[0]).rescale(1 / q.s).magnitude) profile = cl_array.to_device(queue, profile.astype(cfg.PRECISION.np_float)) z_sample = self.sample_distance.simplified.magnitude lam = energy_to_wavelength(energy).simplified.magnitude kernel = cfg.OPENCL.programs["physics"].make_flat_from_vertical_profile ev = kernel( 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(compute_phase), np.int32(is_parabola), ) if block: ev.wait()
def test_transfer_many(self): n = 32 shape = (n, n) 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 Pi / 16 delta = (lam / (32 * ps)).simplified.magnitude ri = np.ones_like(energies.magnitude, dtype=np.complex) * delta + 0j material = Material('dummy', ri, energies) wedge = np.tile(np.arange(n), [n, 1]) * ps wedge = StaticBody(wedge, ps, material=material) # Test more objects u_many = physics.transfer_many([wedge, wedge], shape, ps, energy).get() # 2 objects u = wedge.transfer(shape, ps, energy).get() ** 2 np.testing.assert_almost_equal(u, u_many) # Test exponent u = physics.transfer_many([wedge], shape, ps, energy, exponent=False).get() u_exp = physics.transfer_many([wedge], shape, ps, energy, exponent=True).get() np.testing.assert_almost_equal(u, np.exp(u_exp))
def get_attenuation_coefficient(self, energy): """Get the linear attenuation coefficient at *energy*.""" ref_index = self.get_refractive_index(energy) lam = physics.energy_to_wavelength(energy) return physics.ref_index_to_attenuation_coeff(ref_index, lam)
def test_energy_to_wavelength(self): self.assertAlmostEqual( physics.energy_to_wavelength(self.energy).rescale(self.lam.units), self.lam)
def test_transfer(self): go = StaticBody(np.arange(4 ** 2).reshape(4, 4) * q.um, 1 * q.um, material=self.material) transferred = go.transfer((4, 4), 1 * q.um, self.energy).get() gt = transfer(go.thickness, self.material.get_refractive_index(self.energy), energy_to_wavelength(self.energy)).get() np.testing.assert_almost_equal(gt, transferred)
def test_energy_to_wavelength(self): self.assertAlmostEqual(physics.energy_to_wavelength(self.energy). rescale(self.lam.units), self.lam)
def test_attenuation(self): ref_index = 1e-7 + 1e-10j energy = 20 * q.keV lam = physics.energy_to_wavelength(energy) gt = 4 * np.pi * ref_index.imag / lam.simplified self.assertAlmostEqual(gt, physics.ref_index_to_attenuation_coeff(ref_index, lam))
def main(): """Main function.""" args = parse_args() syris.init(loglevel=logging.INFO, device_index=-1) lam = energy_to_wavelength(args.energy * q.keV).simplified w = args.aperture / 2 * q.um # Width of the aperture is 2w, make the aperture half the image size fov = 4 * w ss = args.supersampling d = (w ** 2 / args.fn / lam).simplified ns, ps = compute_propagation_sampling(lam, d, fov, fresnel=True) # Convolution outlier ns *= 2 ps /= ss fmt = 'n sampling: {}, ps: {}, FOV: {}, propagation distance: {}' LOG.info(fmt.format(ns, np.round(ps.rescale(q.nm), 2), fov, np.round(d.rescale(q.cm), 2))) res_a = propagate_analytically(ns * ss, w, ps, d, lam) # Power of two for FFT n = int(2 ** np.ceil(np.log2(ns))) # Supersampling of the pixel size requires supersampling^2 more data points because we enlarge # the FOV by changing the diffraction angle via changing the pixel size and this enlarged FOV is # then sampled by supersampling-smaller pixel size n_proper = n * ss ** 2 numerical_results = {} for divisor in args.numerical_divisors: if np.modf(np.log2(divisor))[0] != 0: raise ValueError('All divisors must be power of two') n_current = n_proper / divisor if n_current < n * ss: raise ValueError('divisor too large, maximum is {}'.format(ss)) numerical_results[n_current] = propagate_numerically(n_current, w, ps, d, lam) x_data = np.linspace(-2 * w.magnitude, 2 * w.magnitude, res_a.shape[0]) aperture = np.zeros(res_a.shape[1]) aperture[res_a.shape[1] / 4:3 * res_a.shape[1] / 4] = res_a[n / 4 / ss].max() if args.txt_output: txt_data = [x_data, aperture, res_a[n / 4 / ss]] txt_header = 'x\taperture\tanalytical' plt.figure() plt.plot(x_data, aperture, label='Aperture') for n_pixels, num_result in numerical_results.items(): fraction = n_pixels / float(n_proper) if args.txt_output: txt_header += '\t{}'.format(fraction) txt_data.append(num_result[n / 4 / ss]) plt.plot(x_data, num_result[n / 4 / ss], '--', label='Numerical {}'.format(fraction)) LOG.info('MSE: {}'.format(np.mean((num_result - res_a) ** 2))) plt.plot(x_data, res_a[n / 4 / ss], '-.', label='Analytical') plt.title('Analytical vs. Numerical Diffraction Pattern') plt.xlabel(r'$\mu m$') plt.ylabel(r'$I$') plt.legend(loc='best') plt.grid() if args.txt_output: txt_data = np.array(txt_data).T np.savetxt(args.txt_output, txt_data, fmt='%g', delimiter='\t', header=txt_header) plt.show()
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
def get_propagator_psf(n, d, ps, energy): lam = energy_to_wavelength(energy) propagator = compute_propagator(n, d, lam, ps).get() return np.fft.fftshift(np.fft.ifft2(propagator))
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