#!/usr/bin/env python
# -*- coding: utf-8 -*-
# #########################################################################
# Copyright (c) 2015-2019, UChicago Argonne, LLC. All rights reserved. #
# #
# Copyright 2015-2019. UChicago Argonne, LLC. This software was produced #
# under U.S. Government contract DE-AC02-06CH11357 for Argonne National #
# Laboratory (ANL), which is operated by UChicago Argonne, LLC for the #
# U.S. Department of Energy. The U.S. Government has rights to use, #
# reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR #
# UChicago Argonne, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR #
# ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is #
# modified to produce derivative works, such modified software should #
# be clearly marked, so as not to confuse it with the version available #
# from ANL. #
# #
# Additionally, redistribution and use in source and binary forms, with #
# or without modification, are permitted provided that the following #
# conditions are met: #
# #
# * Redistributions of source code must retain the above copyright #
# notice, this list of conditions and the following disclaimer. #
# #
# * Redistributions in binary form must reproduce the above copyright #
# notice, this list of conditions and the following disclaimer in #
# the documentation and/or other materials provided with the #
# distribution. #
# #
# * Neither the name of UChicago Argonne, LLC, Argonne National #
# Laboratory, ANL, the U.S. Government, nor the names of its #
# contributors may be used to endorse or promote products derived #
# from this software without specific prior written permission. #
# #
# THIS SOFTWARE IS PROVIDED BY UChicago Argonne, LLC AND CONTRIBUTORS #
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT #
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS #
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UChicago #
# Argonne, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, #
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, #
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; #
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT #
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN #
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE #
# POSSIBILITY OF SUCH DAMAGE. #
# #########################################################################
"""
Module for simulation of x-rays.
"""
import numpy as np
import logging
logger = logging.getLogger(__name__)
__author__ = "Doga Gursoy"
__copyright__ = "Copyright (c) 2015, UChicago Argonne, LLC."
__docformat__ = 'restructuredtext en'
__all__ = ['calc_intensity',
'propagate_tie',
'probe_gauss']
[docs]def propagate_tie(mu, delta, pixel_size, dist):
"""
Propagate emitting x-ray wave based on Transport of Intensity.
Parameters
----------
mu : ndarray, optional
3D tomographic data for attenuation.
delta : ndarray
3D tomographic data for refractive index.
pixel_size : float
Detector pixel size in cm.
dist : float
Propagation distance of the wavefront in cm.
Returns
-------
ndarray
3D propagated tomographic intensity.
"""
i1 = np.exp(-mu)
i2 = np.zeros(delta.shape)
for m in range(delta.shape[0]):
dx, dy = np.gradient(delta[m], pixel_size)
d2x, _ = np.gradient(i1[m] * dx, pixel_size)
_, d2y = np.gradient(i1[m] * dy, pixel_size)
i2[m] = i1[m] + dist * (d2x + d2y)
return i2
[docs]def probe_gauss(nx, ny, fwhm=None, center=None, max_int=1):
"""
Simulate incident x-ray beam (probe) as a square Gaussian kernel.
Parameters
----------
nx, ny : int
Grid size along x and y axes.
fwhm : float, optional
Effective radius of the source.
center : array_like, optional
x and y coordinates of the center of the gaussian function.
max_int : int
Maximum x-ray intensity.
Returns
-------
ndarray
2D source intensity distribution.
"""
if fwhm is None:
fwhm = max(nx, ny) // 2
if center is None:
x0, y0 = nx // 2, ny // 2
else:
x0, y0 = np.array(center)
x, y = np.mgrid[0:nx, 0:ny]
return max_int * np.exp(-4 * np.log(2) * (
(x - x0 + 0.5) ** 2 +
(y - y0 + 0.5) ** 2) / fwhm ** 2)
def _rect_scan_coords(probe_grid, proj_grid, shift_x, shift_y):
"""
Calculate upper-left scan coordinates of a rectangular kernel given
a projection image.
Parameters
----------
proj_grid : (int, int)
Grid size of the projection image along x and y axes.
shift_x, shift_y : int
Relative shift distace of the source along x and y axes.
Returns
-------
array
x coordinates of upper-left scan coordinates
array
y coordinates of upper-left scan coordinates
"""
x = np.arange(0, proj_grid[0], shift_x)
y = np.arange(0, proj_grid[1], shift_y)
while x.size * shift_x > proj_grid[0] - probe_grid[0] + shift_x:
x = x[:-1]
while y.size * shift_y > proj_grid[1] - probe_grid[1] + shift_y:
y = y[:-1]
return x, y
def _rect_scan_probe(probe, proj, shift_x=None, shift_y=None):
"""
Calculate individual raster scanned images for a given rectangular
x-ray probe and an object plane intensity.
Parameters
----------
probe : ndarray
Rectangular x-ray source kernel.
proj : ndarray
Object plane intensity image.
shift_x, shift_y : int, optional
Shift amount of probe along x and y axes.
Returns
-------
ndarray
Individual raster scanned images as 3D array.
"""
sx, sy = probe.shape
px, py = proj.shape
# Assume half overlap.
if shift_x is None:
shift_x = sx // 2
if shift_y is None:
shift_y = sy // 2
# Calculate upper-left scan coordinates along x and y axes.
x, y = _rect_scan_coords(probe.shape, proj.shape, shift_x, shift_y)
# Convert to image stack.
arr = [probe * proj[i:i + sx, j:j + sy] for i in x for j in y]
return np.array(arr)
[docs]def calc_intensity(probe, proj, shift_x=None, shift_y=None, mode='near'):
"""
Calculate far field intensity.
Parameters
----------
probe : ndarray
Rectangular x-ray source kernel.
proj : ndarray
Object plane intensity image.
shift_x, shift_y : int, optional
Shift amount of probe along x and y axes.
mode : str, optional
Specify the regime. 'near' or 'far'
Returns
-------
ndarray
Individual raster scanned far field images as 3D array.
"""
psi = _rect_scan_probe(probe, proj, shift_x, shift_y)
if mode == 'near':
intensity = abs(psi) ** 2
elif mode == 'far':
intensity = abs(np.fft.fftshift(np.fft.fft2(psi))) ** 2
return intensity