"""
The s4 ideal Fresnel zone plate (optical element and beamline element).
"""
import numpy
from syned.beamline.element_coordinates import ElementCoordinates
from syned.beamline.optical_elements.ideal_elements.ideal_fzp import IdealFZP
from shadow4.beam.s4_beam import S4Beam
from shadow4.beamline.s4_optical_element_decorators import S4OpticalElementDecorator
from shadow4.beamline.s4_beamline_element import S4BeamlineElement
[docs]class S4IdealFZP(IdealFZP, S4OpticalElementDecorator):
"""
Defines an ideal Fresnel Zone Plate.
Constructor.
Parameters
----------
name : str, optional
The name of the optical element.
focusing_direction : int
0=None, 1=x (sagittal), 2=z (meridional), 3=2D focusing.
focal : float
The focal length in meters.
nominal_wavelength : float
The nominal wavelength in m for where the focal length is defined.
diameter : float
The FZP diameter in m.
"""
def __init__(self,
name="Ideal FZP",
focusing_direction=3, # 0=None, 1=x (sagittal), 2=z (meridional), 3=2D focusing.
focal=1.0, # focal distance (m)
nominal_wavelength=1e-10, # nominal wavelength in m
diameter=0.001, # FZP diameter in m
):
super().__init__(name=name,
focusing_direction=focusing_direction,
focal=focal,
nominal_wavelength=nominal_wavelength,
# r0=r0,
diameter=diameter,
)
[docs] def to_python_code(self, **kwargs):
"""
Creates the python code for defining the element.
Parameters
----------
**kwargs
Returns
-------
str
Python code.
"""
txt_pre = """
from shadow4.beamline.optical_elements.ideal_elements.s4_ideal_fzp import S4IdealFZP
optical_element = S4IdealFZP(name='{name:s}',
focusing_direction={focusing_direction:d}, # 0=None, 1=x (sagittal), 2=z (meridional), 3=2D focusing.
focal={focal:g}, # focal distance (m)
nominal_wavelength={nominal_wavelength:g}, # nominal wavelength in m
diameter={diameter:g}, # FZP diameter in m
)
"""
txt = txt_pre.format(**{ 'name': self.get_name(),
'focusing_direction': self.focusing_direction(),
'focal': self.focal(),
'nominal_wavelength': self.nominal_wavelength(),
'diameter': self.diameter(),
})
return txt
[docs]class S4IdealFZPElement(S4BeamlineElement):
"""
Constructor.
Parameters
----------
optical_element : instance of OpticalElement, optional
The syned optical element.
coordinates : instance of ElementCoordinates, optional
The syned element coordinates.
input_beam : instance of S4Beam, optional
The S4 incident beam.
Returns
-------
instance of S4IdealFZPElement.
"""
def __init__(self,
optical_element : S4IdealFZP = None,
coordinates : ElementCoordinates = None,
input_beam : S4Beam = None):
super().__init__(optical_element=optical_element if optical_element is not None else S4IdealFZP(),
coordinates=coordinates if coordinates is not None else ElementCoordinates(),
input_beam=input_beam)
[docs] def trace_beam(self, **params):
"""
Runs (ray tracing) the input beam through the element.
Parameters
----------
**params
Returns
-------
tuple
(output_beam, footprint) instances of S4Beam.
"""
input_beam = self.get_input_beam().duplicate()
p, q = self.get_coordinates().get_p_and_q()
if p != 0.0: input_beam.retrace(p, resetY=True)
#
##############################################################################
#
output_beam = input_beam.duplicate()
focusing_direction = self.get_optical_element().focusing_direction()
if focusing_direction > 0:
#
lambda1 = input_beam.get_column(19) # lambda in Angstroms
lambda1m = lambda1 * 1e-10
x = input_beam.get_column(1)
z = input_beam.get_column(3)
xpin = input_beam.get_column(4)
zpin = input_beam.get_column(6)
if focusing_direction == 1:
z *= 0
elif focusing_direction == 2:
x *= 0
#
Kmod = 2 * numpy.pi / lambda1m # wavevector modulus in m-1
r = numpy.sqrt(x ** 2. + z ** 2.) # distance to center
Kxin = Kmod * xpin
Kzin = Kmod * zpin
nrays = input_beam.N
n = numpy.zeros(nrays)
d = numpy.zeros(nrays)
#
# calculate n (index of n-th zone) and d (radius if the nth zone minus
# radius of the n-a zone)
#
# Rays that arrive onto the inner zone
# IN are the indices of rays that arrive inside the inner zone
R0 = self.get_optical_element().r0_exact()
focal = self.get_optical_element().focal()
nomlambda = self.get_optical_element().nominal_wavelength()
DDm = self.get_optical_element().diameter()
IN = numpy.where(r <= R0)
IN = numpy.array(IN)
if IN.size > 0:
n[IN] = 0.0
d[IN] = 0.0
# Rays that arrive outside the inner zone
# (see formulas in A.G. Michette, "X-ray science and technology"
# Institute of Physics Publishing (1993))
OUT = numpy.where(r >= R0)
OUT = numpy.array(OUT)
if OUT.size > 0:
n[OUT] = (r[OUT] ** 2 - R0 ** 2) / (nomlambda * focal) # eq 8.56
# d-spacing: we suppose the ray is in the middle of two r's with fractional n
d[OUT] = numpy.sqrt((n[OUT] + .5) * nomlambda * focal + R0 ** 2) - \
numpy.sqrt((n[OUT] - .5) * nomlambda * focal + R0 ** 2)
# computing G (the "grating" wavevector)
Gx = -numpy.pi / d * (x / r)
Gz = -numpy.pi / d * (z / r)
# capture infinities
Gx[d == 0] = 0.0
Gz[d == 0] = 0.0
# computing kout
Kxout = Kxin + Gx
Kzout = Kzin + Gz
xpout = Kxout / Kmod
zpout = Kzout / Kmod
# Handle rays that arrive outside the FZP
# flag for lost rays
LOST = numpy.where(r > DDm / 2)
LOST = numpy.array(LOST)
if LOST.size > 0:
output_beam.rays[LOST, 9] = -100.0
output_beam.rays[:, 3] = xpout
output_beam.rays[:, 4] = numpy.sqrt(1 - xpout ** 2 - zpout ** 2)
output_beam.rays[:, 5] = zpout
footprint = output_beam.duplicate()
footprint.rotate(numpy.pi / 2, axis=1)
return output_beam, footprint
[docs] def to_python_code(self, **kwargs):
"""
Creates the python code for defining the element.
Parameters
----------
**kwargs
Returns
-------
str
Python code.
"""
txt = "\n\n# optical element number XX"
txt += self.get_optical_element().to_python_code()
txt += self.to_python_code_coordinates()
txt += "\nfrom shadow4.beamline.optical_elements.ideal_elements.s4_ideal_fzp import S4IdealFZPElement"
txt += "\nbeamline_element = S4IdealFZPElement(optical_element=optical_element, coordinates=coordinates, input_beam=beam)"
txt += "\n\nbeam, footprint = beamline_element.trace_beam()"
return txt
if __name__ == "__main__":
if 0:
# a = S4IdealFZP()
# print(a.to_python_code())
# a = S4SuperIdealFZP()
# print(a.to_python_code())
b = S4IdealFZPElement()
print(b.to_python_code())
# b = S4SuperIdealFZPElement()
# print(b.to_python_code())
#==============================
if 1:
from shadow4.beamline.s4_beamline import S4Beamline
beamline = S4Beamline()
#
#
#
from shadow4.sources.source_geometrical.source_geometrical import SourceGeometrical
light_source = SourceGeometrical(name='SourceGeometrical', nrays=5000, seed=5676561)
light_source.set_spatial_type_rectangle(width=0.000800,height=0.000800)
light_source.set_depth_distribution_off()
light_source.set_angular_distribution_flat(hdiv1=-0.000000,hdiv2=0.000000,vdiv1=-0.000000,vdiv2=0.000000)
light_source.set_energy_distribution_singleline(1.540000, unit='A')
light_source.set_polarization(polarization_degree=1.000000, phase_diff=0.000000, coherent_beam=0)
beam = light_source.get_beam()
beamline.set_light_source(light_source)
# optical element number XX
boundary_shape = None
from shadow4.beamline.optical_elements.absorbers.s4_screen import S4Screen
optical_element = S4Screen(name='Generic Beam Screen/Slit/Stopper/Attenuator', boundary_shape=boundary_shape,
i_abs=0, # 0=No, 1=prerefl file_abs, 2=xraylib, 3=dabax
i_stop=0, thick=0, file_abs='<specify file name>', material='Au', density=19.3)
from syned.beamline.element_coordinates import ElementCoordinates
coordinates = ElementCoordinates(p=1, q=0, angle_radial=0, angle_azimuthal=0, angle_radial_out=3.141592654)
from shadow4.beamline.optical_elements.absorbers.s4_screen import S4ScreenElement
beamline_element = S4ScreenElement(optical_element=optical_element, coordinates=coordinates, input_beam=beam)
beam, footprint = beamline_element.trace_beam()
beamline.append_beamline_element(beamline_element)
# optical element number XX
from shadow4.beamline.optical_elements.ideal_elements.s4_ideal_fzp import S4IdealFZP
optical_element = S4IdealFZP(name='Ideal FZP',
focusing_direction=3, # 0=None, 1=x (sagittal), 2=z (meridional), 3=2D focusing.
focal=1, # focal distance (m)
nominal_wavelength=1.54e-10, # nominal wavelength in m
diameter=0.000618, # FZP diameter in m
)
from syned.beamline.element_coordinates import ElementCoordinates
coordinates = ElementCoordinates(p=0, q=0, angle_radial=0, angle_azimuthal=0, angle_radial_out=3.141592654)
from shadow4.beamline.optical_elements.ideal_elements.s4_ideal_fzp import S4IdealFZPElement
beamline_element = S4IdealFZPElement(optical_element=optical_element, coordinates=coordinates, input_beam=beam)
beam, footprint = beamline_element.trace_beam()
beamline.append_beamline_element(beamline_element)
# optical element number XX
boundary_shape = None
from shadow4.beamline.optical_elements.absorbers.s4_screen import S4Screen
optical_element = S4Screen(name='Generic Beam Screen/Slit/Stopper/Attenuator', boundary_shape=boundary_shape,
i_abs=0, # 0=No, 1=prerefl file_abs, 2=xraylib, 3=dabax
i_stop=0, thick=0, file_abs='<specify file name>', material='Au', density=19.3)
from syned.beamline.element_coordinates import ElementCoordinates
coordinates = ElementCoordinates(p=1, q=0, angle_radial=0, angle_azimuthal=0, angle_radial_out=3.141592654)
from shadow4.beamline.optical_elements.absorbers.s4_screen import S4ScreenElement
beamline_element = S4ScreenElement(optical_element=optical_element, coordinates=coordinates, input_beam=beam)
beam, footprint = beamline_element.trace_beam()
beamline.append_beamline_element(beamline_element)
# test plot
if True:
from srxraylib.plot.gol import plot_scatter
# plot_scatter(beam.get_photon_energy_eV(nolost=1), beam.get_column(23, nolost=1), title='(Intensity,Photon Energy)', plot_histograms=0)
plot_scatter(1e6 * beam.get_column(1, nolost=1), 1e6 * beam.get_column(3, nolost=1), title='(X,Z) in microns')