def get_gauss_2d(shape, sigma, pixel_size=1, fourier=False, queue=None, block=False): """Get 2D Gaussian of *shape* with standard deviation *sigma* and *pixel_size*. If *fourier* is True the fourier transform of it is returned so it is faster for usage by convolution. Use command *queue* if specified. If *block* is True, wait for the kernel to finish. """ shape = make_tuple(shape) pixel_size = get_magnitude(make_tuple(pixel_size)) sigma = get_magnitude(make_tuple(sigma)) LOG.debug('get_gauss_2d, shape: %s, sigma: %s, pixel size: %s, fourier: %s', shape, sigma, pixel_size, fourier) if queue is None: queue = cfg.OPENCL.queue out = cl.array.Array(queue, shape, dtype=cfg.PRECISION.np_float) if fourier: ev = cfg.OPENCL.programs['improc'].gauss_2d_f(queue, shape[::-1], None, out.data, g_util.make_vfloat2(sigma[1], sigma[0]), g_util.make_vfloat2(pixel_size[1], pixel_size[0])) else: ev = cfg.OPENCL.programs['improc'].gauss_2d(queue, shape[::-1], None, out.data, g_util.make_vfloat2(sigma[1], sigma[0]), g_util.make_vfloat2(pixel_size[1], pixel_size[0])) if block: ev.wait() return out
def transfer(self, shape, pixel_size, energy, exponent=False, offset=None, t=None, queue=None, out=None, check=True, block=False): """Transfer function of the element on an image plane of size *shape*, use *pixel_size*, *energy*, *offset* is the physical spatial offset of the element as (y, x), transfer at time *t*. If *exponent* is true, compute the exponent of the transfer function without applying the wavenumber. Use *queue* for OpenCL computations and *out* pyopencl array. If *block* is True, wait for the kernel to finish. If *check* is True, the function is checked for aliasing artefacts. """ shape = make_tuple(shape, num_dims=2) pixel_size = make_tuple(pixel_size, num_dims=2) if offset is None: offset = (0, 0) * q.m if queue is None: queue = cfg.OPENCL.queue return self._transfer(shape, pixel_size, energy, offset, exponent=exponent, t=t, queue=queue, out=out, check=check, block=block)
def project(self, shape, pixel_size, offset=None, t=None, queue=None, out=None, block=False): """Project thickness at time *t* to the image plane of size *shape* which is either 1D and is extended to (n, n) or is 2D as HxW. *pixel_size* is the point size, also either 1D or 2D. *offset* is the physical spatial body offset as (y, x). *queue* is an OpenCL command queue, *out* is the pyopencl array used for result. If *block* is True, wait for the kernel to finish. """ shape = make_tuple(shape, num_dims=2) pixel_size = make_tuple(pixel_size, num_dims=2) if offset is None: offset = (0, 0) * q.m if queue is None: queue = cfg.OPENCL.queue return self._project(shape, pixel_size, offset, t=t, queue=queue, out=None, block=block)
def transfer_fourier(self, shape, pixel_size, energy, t=None, queue=None, out=None, block=False): """Transfer function of the element in Fourier space of size *shape*, use *pixel_size*, *energy* and comput the function at time *t*. Use *queue* for OpenCL computations and *out* pyopencl array. If *block* is True, wait for the kernel to finish. """ shape = make_tuple(shape, num_dims=2) pixel_size = make_tuple(pixel_size, num_dims=2) if queue is None: queue = cfg.OPENCL.queue return self._transfer_fourier(shape, pixel_size, energy, t=t, queue=queue, out=out, block=block)
def compute_slices(self, shape, pixel_size, queue=None, out=None, offset=None): """Compute slices with *shape* as (z, y, x), *pixel_size*. Use *queue* and *out* for outuput. Offset is the starting point offset as (x, y, z). """ if queue is None: queue = cfg.OPENCL.queue if out is None: out = cl_array.zeros(queue, shape, dtype=np.uint8) pixel_size = make_tuple(pixel_size, num_dims=2) v_1, v_2, v_3 = self._make_inputs(queue, pixel_size) psm = pixel_size.simplified.magnitude max_dx = self.max_triangle_x_diff.simplified.magnitude / psm[1] if offset is None: offset = gutil.make_vfloat3(0, 0, 0) else: offset = offset.simplified.magnitude offset = gutil.make_vfloat3(offset[0] / psm[1], offset[1] / psm[0], offset[2] / psm[1]) cfg.OPENCL.programs['mesh'].compute_slices(queue, (shape[2], shape[0]), None, v_1.data, v_2.data, v_3.data, out.data, np.int32(shape[1]), np.int32(self.num_triangles), offset, cfg.PRECISION.np_float(max_dx)) return out
def rescale(image, shape, sampler=None, queue=None, out=None, block=False): """Rescale *image* to *shape* and use *sampler* which is a :class:`pyopencl.Sampler` instance. Use OpenCL *queue* and *out* pyopencl Array. If *block* is True, wait for the copy to finish. """ if cfg.PRECISION.cl_float == 8: raise TypeError('Double precision mode not supported') shape = make_tuple(shape) # OpenCL order factor = float(shape[1]) / image.shape[1], float(shape[0]) / image.shape[0] LOG.debug('rescale, shape: %s, final_shape: %s, factor: %s', image.shape, shape, factor) if queue is None: queue = cfg.OPENCL.queue if out is None: out = cl.array.Array(queue, shape, dtype=cfg.PRECISION.np_float) if not sampler: sampler = cl.Sampler(cfg.OPENCL.ctx, False, cl.addressing_mode.CLAMP_TO_EDGE, cl.filter_mode.LINEAR) image = g_util.get_image(image) ev = cfg.OPENCL.programs['improc'].rescale(queue, shape[::-1], None, image, out.data, sampler, g_util.make_vfloat2(*factor)) if block: ev.wait() return out
def decimate(image, shape, sigma=None, average=False, queue=None, block=False): """Decimate *image* so that its dimensions match the final *shape*, which has to be a divisor of the original shape. Remove low frequencies by a Gaussian filter with *sigma* pixels. If *sigma* is None, use the FWHM of one low resolution pixel. Use command *queue*, if *block* is True, wait for the copy to finish. """ if queue is None: queue = cfg.OPENCL.queue image = g_util.get_array(image, queue=queue) shape = make_tuple(shape) pow_shape = tuple([next_power_of_two(n) for n in image.shape]) orig_shape = image.shape if image.shape != pow_shape: image = pad(image, region=(0, 0) + pow_shape, queue=queue) if sigma is None: sigma = tuple([fwnm_to_sigma(float(image.shape[i]) / shape[i], n=2) for i in range(2)]) LOG.debug( "decimate, shape: %s, final_shape: %s, sigma: %s, average: %s", image.shape, shape, sigma, average, ) fltr = get_gauss_2d(image.shape, sigma, fourier=True, queue=queue, block=block) image = image.astype(cfg.PRECISION.np_cplx) fft_2(image, queue=queue, block=block) image *= fltr ifft_2(image, queue=queue, block=block) image = crop(image.real, (0, 0) + orig_shape, queue=queue, block=block) return bin_image(image, shape, average=average, queue=queue, block=block)
def compute_slices(self, shape, pixel_size, queue=None, out=None, offset=None): """Compute slices with *shape* as (z, y, x), *pixel_size*. Use *queue* and *out* for outuput. Offset is the starting point offset as (x, y, z). """ if queue is None: queue = cfg.OPENCL.queue if out is None: out = cl_array.zeros(queue, shape, dtype=np.uint8) pixel_size = make_tuple(pixel_size, num_dims=2) v_1, v_2, v_3 = self._make_inputs(queue, pixel_size) psm = pixel_size.simplified.magnitude max_dx = self.max_triangle_x_diff.simplified.magnitude / psm[1] if offset is None: offset = gutil.make_vfloat3(0, 0, 0) else: offset = offset.simplified.magnitude offset = gutil.make_vfloat3(offset[0] / psm[1], offset[1] / psm[0], offset[2] / psm[1]) cfg.OPENCL.programs['mesh'].compute_slices( queue, (shape[2], shape[0]), None, v_1.data, v_2.data, v_3.data, out.data, np.int32(shape[1]), np.int32(self.num_triangles), offset, cfg.PRECISION.np_float(max_dx)) return out
def __init__( self, energies, flux, sample_distance, size, trajectory, pixel_size=None, phase_profile="plane", ): """Source with a pre-computed *flux* at given *energies*. *flux* can be a 1D array, in which case there is no spatial distribution of intensities, or it can be a 3D array, in which case every 2D image *flux[i]* represents spatial intensity distribution for *energies[i]*. """ super(FixedSpectrumSource, self).__init__(sample_distance, size, trajectory, phase_profile=phase_profile) if len(flux) != len(energies): raise XRaySourceError("Flux must have the same length as energies") if flux.ndim == 3 and pixel_size is None: raise XRaySourceError( "pixel_size must be specified for 3D flux input") self._pixel_size = make_tuple(pixel_size, num_dims=2) self._energies = energies self._flux = flux
def rescale(image, shape, sampler=None, queue=None, out=None, block=False): """Rescale *image* to *shape* and use *sampler* which is a :class:`pyopencl.Sampler` instance. Use OpenCL *queue* and *out* pyopencl Array. If *block* is True, wait for the copy to finish. """ if cfg.PRECISION.cl_float == 8: raise TypeError("Double precision mode not supported") shape = make_tuple(shape) # OpenCL order factor = float(shape[1]) / image.shape[1], float(shape[0]) / image.shape[0] LOG.debug("rescale, shape: %s, final_shape: %s, factor: %s", image.shape, shape, factor) if queue is None: queue = cfg.OPENCL.queue if out is None: out = cl.array.Array(queue, shape, dtype=cfg.PRECISION.np_float) if not sampler: sampler = cl.Sampler( cfg.OPENCL.ctx, False, cl.addressing_mode.CLAMP_TO_EDGE, cl.filter_mode.LINEAR ) image = g_util.get_image(image) ev = cfg.OPENCL.programs["improc"].rescale( queue, shape[::-1], None, image, out.data, sampler, g_util.make_vfloat2(*factor) ) if block: ev.wait() return out
def decimate(image, shape, sigma=None, average=False, queue=None, block=False): """Decimate *image* so that its dimensions match the final *shape*, which has to be a divisor of the original shape. Remove low frequencies by a Gaussian filter with *sigma* pixels. If *sigma* is None, use the FWHM of one low resolution pixel. Use command *queue*, if *block* is True, wait for the copy to finish. """ if queue is None: queue = cfg.OPENCL.queue image = g_util.get_array(image, queue=queue) shape = make_tuple(shape) pow_shape = tuple([next_power_of_two(n) for n in image.shape]) orig_shape = image.shape if image.shape != pow_shape: image = pad(image, region=(0, 0) + pow_shape, queue=queue) if sigma is None: sigma = tuple([fwnm_to_sigma(float(image.shape[i]) / shape[i], n=2) for i in range(2)]) LOG.debug('decimate, shape: %s, final_shape: %s, sigma: %s, average: %s', image.shape, shape, sigma, average) fltr = get_gauss_2d(image.shape, sigma, fourier=True, queue=queue, block=block) image = image.astype(cfg.PRECISION.np_cplx) fft_2(image, queue=queue, block=block) image *= fltr ifft_2(image, queue=queue, block=block) image = crop(image.real, (0, 0) + orig_shape, queue=queue, block=block) return bin_image(image, shape, average=average, queue=queue, block=block)
def project(self, shape, pixel_size, offset=None, t=None, queue=None, out=None, block=False): """Project thickness at time *t* (if it is None no transformation is applied) to the image plane of size *shape* which is either 1D and is extended to (n, n) or is 2D as HxW. *pixel_size* is the point size, also either 1D or 2D. *offset* is the physical spatial body offset as (y, x). *queue* is an OpenCL command queue, *out* is the pyopencl array used for result. If *block* is True, wait for the kernel to finish. """ pixel_size = make_tuple(pixel_size, 2) if offset is None: offset = (0, 0) * q.m if t is not None: self.move(t) if self.cache_projection: if (self._p_cache['time'] is None or np.any(self._p_cache['ps'] != pixel_size) or self._p_cache['shape'] != shape or np.any(self._p_cache['offset'] != offset)): moved = True self.update_projection_cache(t=t, shape=shape, pixel_size=pixel_size, offset=offset) else: # 0.99 to make sure we recompute when next_time from cached time is current t moved = self.moved(min(self._p_cache['time'], t), max(self._p_cache['time'], t), 0.99 * min(pixel_size), bind=False) if moved: LOG.debug('{} computing projection at {}'.format(self, t)) self._p_cache['time'] = t self._p_cache['projection'] = super(MovableBody, self).project( shape, pixel_size, offset=offset, t=t, queue=queue, out=None, block=block) projection = self._p_cache['projection'] else: projection = super(MovableBody, self).project(shape, pixel_size, offset=offset, t=t, queue=queue, out=None, block=block) return projection
def get_extrema(sgn): func = np.max if sgn > 0 else np.min x_ps = util.make_tuple(pixel_size)[1] res = [(ball.position[2] + sgn * (2 * ball.radius + x_ps)).simplified.magnitude for ball in metaballs] return func(res)
def project_metaballs_naive(metaballs, shape, pixel_size, offset=None, z_step=None, queue=None, out=None, block=False): """Project a list of :class:`.MetaBall` on an image plane with *shape*, *pixel_size*. *z_step* is the physical step in the z-dimension, if not specified it is the same as *pixel_size*. *offset* is the physical spatial body offset as (y, x). Use OpenCL *queue* and *out* pyopencl Array instance for returning the result. If *block* is True, wait for the kernel to finish. """ def get_extrema(sgn): func = np.max if sgn > 0 else np.min x_ps = util.make_tuple(pixel_size)[1] res = [(ball.position[2] + sgn * (2 * ball.radius + x_ps)).simplified.magnitude for ball in metaballs] return func(res) if offset is None: offset = (0, 0) * q.m if not queue: queue = cfg.OPENCL.queue if out is None: out = cl_array.Array(queue, shape, cfg.PRECISION.np_float) string = b"".join([body.pack() for body in metaballs]) data = np.fromstring(string, dtype=np.float32) data = cl_array.to_device(queue, data) n, m = shape ps = util.make_tuple(pixel_size.simplified.magnitude) z_step = ps[1] if z_step is None else z_step.simplified.magnitude z_range = get_extrema(-1), get_extrema(1) offset = g_util.make_vfloat2(*offset.simplified.magnitude[::-1]) ev = cfg.OPENCL.programs["geometry"].naive_metaballs( cfg.OPENCL.queue, (m, n), None, out.data, data.data, np.int32(len(metaballs)), offset, g_util.make_vfloat2(*z_range), cfg.PRECISION.np_float(z_step), g_util.make_vfloat2(*ps[::-1]), np.int32(True), ) if block: ev.wait() return out
def get_gauss_2d(shape, sigma, pixel_size=None, fourier=False): shape = make_tuple(shape) sigma = get_magnitude(make_tuple(sigma)) if pixel_size is None: pixel_size = (1, 1) else: pixel_size = get_magnitude(make_tuple(pixel_size)) if fourier: i = np.fft.fftfreq(shape[1]) / pixel_size[1] j = np.fft.fftfreq(shape[0]) / pixel_size[0] i, j = np.meshgrid(i, j) return np.exp(-2 * np.pi ** 2 * ((i * sigma[1]) ** 2 + (j * sigma[0]) ** 2)) else: x = (np.arange(shape[1]) - shape[1] / 2) * pixel_size[1] y = (np.arange(shape[0]) - shape[0] / 2) * pixel_size[0] x, y = np.meshgrid(x, y) gauss = np.exp(- x ** 2 / (2. * sigma[1] ** 2) - y ** 2 / (2. * sigma[0] ** 2)) return np.fft.ifftshift(gauss)
def get_gauss_2d(shape, sigma, pixel_size=None, fourier=False): shape = make_tuple(shape) sigma = get_magnitude(make_tuple(sigma)) if pixel_size is None: pixel_size = (1, 1) else: pixel_size = get_magnitude(make_tuple(pixel_size)) if fourier: i = np.fft.fftfreq(shape[1]) / pixel_size[1] j = np.fft.fftfreq(shape[0]) / pixel_size[0] i, j = np.meshgrid(i, j) return np.exp(-2 * np.pi ** 2 * ((i * sigma[1]) ** 2 + (j * sigma[0]) ** 2)) else: x = (np.arange(shape[1]) - shape[1] // 2) * pixel_size[1] y = (np.arange(shape[0]) - shape[0] // 2) * pixel_size[0] x, y = np.meshgrid(x, y) gauss = np.exp(-(x ** 2) / (2.0 * sigma[1] ** 2) - y ** 2 / (2.0 * sigma[0] ** 2)) return np.fft.ifftshift(gauss)
def main(): args = parse_args() syris.init(device_index=0) shape = (args.n, args.n) pixel_size = 1 * q.um if args.method == "random": # Random metaballs creation metaballs, objects_all = create_metaballs_random( args.n, pixel_size, args.num, args.min_radius, args.max_radius, distance_from_center=args.distance_from_center, ) elif args.method == "file": # 1e6 because packing converts to meters values = np.fromfile(args.input, dtype=np.float32) * 1e6 metaballs, objects_all = create_metaballs( values.reshape(len(values) // 4, 4), pixel_size) else: distance = args.distance or args.n / 4 positions = [ (args.n / 2 - distance, args.n / 2, 0, args.n / 6), (args.n / 2 + distance, args.n / 2, 0, args.n / 6), ] metaballs, objects_all = create_metaballs(positions, pixel_size) if args.output: with open(args.output, mode="wb") as out_file: out_file.write(objects_all) z_min, z_max = get_z_range(metaballs) print("z min, max:", z_min.rescale(q.um), z_max.rescale(q.um), args.n * pixel_size + z_min) if args.algorithm == "fast": traj = Trajectory([(0, 0, 0)] * q.m) comp = MetaBalls(traj, metaballs) thickness = comp.project(shape, pixel_size).get() else: print("Z steps:", int(((z_max - z_min) / pixel_size).simplified.magnitude + 0.5)) thickness = project_metaballs_naive(metaballs, shape, make_tuple(pixel_size)).get() if args.output_thickness: imageio.imwrite(args.output_thickness, thickness) show(thickness) plt.show()
def make_sphere(n, radius, pixel_size=1 * q.m, material=None, queue=None): """Make a sphere with image shape (*n*, *n*), *radius* and *pixel_size*. Sphere center is in n / 2 + 0.5, which means between two adjacent pixels. *pixel_size*, *material* and *queue*, which is an OpenCL command queue, are used to create :class:`.StaticBody`. """ pixel_size = make_tuple(pixel_size, num_dims=2) image = np.zeros((n, n), dtype=cfg.PRECISION.np_float) y, x = np.mgrid[-n // 2 : n // 2, -n // 2 : n // 2] x = (x + 0.5) * pixel_size[1].simplified.magnitude y = (y + 0.5) * pixel_size[0].simplified.magnitude radius = radius.simplified.magnitude valid = np.where(x ** 2 + y ** 2 < radius ** 2) image[valid] = 2 * np.sqrt(radius ** 2 - x[valid] ** 2 - y[valid] ** 2) return StaticBody(image * q.m, pixel_size, material=material, queue=queue)
def test_make_tuple(): assert make_tuple(1) == (1, 1) assert make_tuple(1, num_dims=3) == (1, 1, 1) assert make_tuple((1, 2)) == (1, 2) assert_raises(ValueError, make_tuple, (1, 1), num_dims=3) assert tuple(make_tuple(1 * q.m).simplified.magnitude) == (1, 1) assert tuple(make_tuple(1 * q.m, num_dims=3).simplified.magnitude) == (1, 1, 1) assert tuple(make_tuple((1, 2) * q.m).simplified.magnitude) == (1, 2) assert_raises(ValueError, make_tuple, (1, 1) * q.mm, num_dims=3)
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 __init__(self, filename, sample_distance, dE, size, pixel_size, trajectory, phase_profile='sphere', fluctuation=None): super(SpectraSource, self).__init__() self.sample_distance = sample_distance.simplified self.dE = dE self.pixel_size = make_tuple(pixel_size, num_dims=2) self.size = size.simplified self.trajectory = trajectory self._phase_profile = None self.phase_profile = phase_profile self.fluctuation = fluctuation self._spline2d = self.import_spectra_file(filename)
def main(): args = parse_args() syris.init(device_index=0) shape = (args.n, args.n) pixel_size = 1 * q.um if args.method == 'random': # Random metaballs creation metaballs, objects_all = create_metaballs_random(args.n, pixel_size, args.num, args.min_radius, args.max_radius) elif args.method == 'file': # 1e6 because packing converts to meters values = np.fromfile(args.input, dtype=np.float32) * 1e6 metaballs, objects_all = create_metaballs(values.reshape(len(values) / 4, 4)) else: distance = args.distance or args.n / 4 positions = [(args.n / 2 - distance, args.n / 2, 0, args.n / 6), (args.n / 2 + distance, args.n / 2, 0, args.n / 6)] metaballs, objects_all = create_metaballs(positions) if args.output: with open(args.output, mode='wb') as out_file: out_file.write(objects_all) z_min, z_max = get_z_range(metaballs) print 'z min, max:', z_min.rescale(q.um), z_max.rescale(q.um), args.n * pixel_size + z_min if args.algorithm == 'fast': traj = Trajectory([(0, 0, 0)] * q.m) comp = MetaBalls(traj, metaballs) thickness = comp.project(shape, pixel_size).get() else: print 'Z steps:', int(((z_max - z_min) / pixel_size).simplified.magnitude + 0.5) thickness = project_metaballs_naive(metaballs, shape, make_tuple(pixel_size)).get() if args.output_thickness: save_image(args.output_thickness, thickness) show(thickness) plt.show()
def __init__(self, electron_energy, el_current, magnetic_field, sample_distance, dE, size, pixel_size, trajectory, profile_approx=True, phase_profile='plane'): """The parameters are *electron_energy*, electric *el_current*, *magnetic_field*, place it into *sample_distance* (distance between the source and a sample), take into account energy spacing *dE* which sets the amount of photons obtained for an energy to be: .. math:: \Phi = \int_{E - dE / 2}^{E + dE / 2} \Phi(E) dE Set its *size* (y, x) specified as FWHM and approximate it by a Gaussian. *pixel_size* is the effective pixel size. *trajectory* is the trajectory defining the source position in space and time. If *profile_approx* is True, the profile at a given vertical observation angle will not be integrated over the relevant energies but will be calculated for the mean energy and multiplied by *dE*. *phase_profile* can be one of 'plane', 'parabola' and 'sphere', where plane denotes constant phase profile (plane wave approximation) and parabola is the parabolic approximation of the real spherical profile. """ super(BendingMagnet, self).__init__() self.electron_energy = electron_energy.simplified self.el_current = el_current.simplified self.magnetic_field = magnetic_field self.sample_distance = sample_distance.simplified self.dE = dE self.size = size.simplified self.pixel_size = make_tuple(pixel_size, num_dims=2) self.trajectory = trajectory self.profile_approx = profile_approx self._phase_profile = None self.phase_profile = phase_profile
def project(self, shape, pixel_size, offset=None, t=None, queue=None, out=None, block=False): """Project thickness at time *t* (if it is None no transformation is applied) to the image plane of size *shape* which is either 1D and is extended to (n, n) or is 2D as HxW. *pixel_size* is the point size, also either 1D or 2D. *offset* is the physical spatial body offset as (y, x). *queue* is an OpenCL command queue, *out* is the pyopencl array used for result. If *block* is True, wait for the kernel to finish. """ pixel_size = make_tuple(pixel_size, 2) if offset is None: offset = (0, 0) * q.m if t is not None: self.move(t) if self.cache_projection: if (self._p_cache['time'] is None or np.any(self._p_cache['ps'] != pixel_size) or self._p_cache['shape'] != shape or np.any(self._p_cache['offset'] != offset)): moved = True self.update_projection_cache(t=t, shape=shape, pixel_size=pixel_size, offset=offset) else: # 0.99 to make sure we recompute when next_time from cached time is current t moved = self.moved(min(self._p_cache['time'], t), max(self._p_cache['time'], t), 0.99 * min(pixel_size), bind=False) if moved: LOG.debug('{} computing projection at {}'.format(self, t)) self._p_cache['time'] = t self._p_cache['projection'] = super(MovableBody, self).project(shape, pixel_size, offset=offset, t=t, queue=queue, out=None, block=block) projection = self._p_cache['projection'] else: projection = super(MovableBody, self).project(shape, pixel_size, offset=offset, t=t, queue=queue, out=None, block=block) return projection
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
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.warning( "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.warning( "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
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 __init__(self, thickness, pixel_size, material=None, queue=None): super(StaticBody, self).__init__(material) self.thickness = g_util.get_array(thickness.simplified.magnitude, queue=queue) self.pixel_size = make_tuple(pixel_size, num_dims=2)