Esempio n. 1
0
    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()
Esempio n. 2
0
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()
Esempio n. 3
0
    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)
Esempio n. 4
0
    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()
Esempio n. 6
0
    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))
Esempio n. 7
0
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()
Esempio n. 8
0
 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))
Esempio n. 9
0
    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))
Esempio n. 10
0
    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)
Esempio n. 11
0
 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)
Esempio n. 12
0
    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)
Esempio n. 13
0
    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)
Esempio n. 14
0
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()
Esempio n. 15
0
    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))
Esempio n. 16
0
    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)
Esempio n. 17
0
    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))
Esempio n. 18
0
    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()
Esempio n. 19
0
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()
Esempio n. 20
0
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)
Esempio n. 21
0
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)
Esempio n. 22
0
    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()
Esempio n. 23
0
    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))
Esempio n. 24
0
    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)
Esempio n. 25
0
 def test_energy_to_wavelength(self):
     self.assertAlmostEqual(
         physics.energy_to_wavelength(self.energy).rescale(self.lam.units),
         self.lam)
Esempio n. 26
0
    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))
Esempio n. 27
0
 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)
Esempio n. 28
0
 def test_energy_to_wavelength(self):
     self.assertAlmostEqual(physics.energy_to_wavelength(self.energy).
                            rescale(self.lam.units), self.lam)
Esempio n. 29
0
 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))
Esempio n. 30
0
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()
Esempio n. 31
0
    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)
Esempio n. 32
0
    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
Esempio n. 33
0
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))
Esempio n. 34
0
    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