Source code for shadow4.beamline.optical_elements.refractors.s4_crl

"""
The s4 compound refractive lens, CRL (optical element and beamline element).
"""
import numpy

from syned.beamline.element_coordinates import ElementCoordinates
from syned.beamline.optical_elements.refractors.crl import CRL
from syned.beamline.shape import Rectangle, Ellipse, Circle

from dabax.dabax_xraylib import DabaxXraylib

from shadow4.beam.s4_beam import S4Beam
from shadow4.beamline.s4_beamline_element import S4BeamlineElement
from shadow4.beamline.s4_beamline_element_movements import S4BeamlineElementMovements
from shadow4.beamline.optical_elements.refractors.s4_conic_interface import S4ConicInterfaceElement
from shadow4.beamline.s4_optical_element_decorators import S4RefractiveLensOpticalElementDecorator
from shadow4.beamline.optical_elements.refractors.s4_lens import _get_lens_interfaces
from shadow4.beamline.optical_elements.absorbers.s4_screen import S4Screen, S4ScreenElement

[docs]class S4CRL(CRL, S4RefractiveLensOpticalElementDecorator): """ Constructor. Parameters ---------- name : str, optional The name of the mirror. boundary_shape : instance of BoundaryShape, optional The boundary shape of the mirror. n_lens : int, optional The number of (identical) lenses. Note: it is possible to set n_lens=0, in this case empty screens are used in the place of the interfaces of the lens. Lens thickness is considered. piling_thickness : float, optional The distance from one lens to the next one in m. material : str, optional A string with the material element symbol or compound formula. density : float, optional The density of the material in the lens in g/cm^3. thickness : float, optional The thickness of a single lens in m. (Distance between the two interfaces at the center of the lenses.) surface_shape : int, optional A flag to indicate the shape of the optical surfaces: 0=plane, 1=sphere, 2=parabola, 3=conic coefficients. convex_to_the_beam : int, optional A flag to indicate the convexity of the first optical surface. Used for surface_shape > 0. The first interface exposed to the beam is convex: 0=No, 1=Yes. The second interface has opposite convexity. cylinder_angle : int, optional A flag to indicate is the CRL is 2D0fucusing, aor 1D focusing and in which direction: Used for surface_shape > 0. Values are: 0=CRL is focusing in 2D (not cylindrical), 1=CRL is focusing in 1D (meridional focusing), 2=CRL is focusing in 2D (sagittal focusing). ri_calculation_mode : int, optional A flag to indicate the source of the refraction index. Values are: * 0=User, * 1=prerefl file, * 2=direct calculation using xraylib, * 3=direct calculation using dabax. prerefl_file : str, optional For ri_calculation_mode=1, the prerefl preprocessor file name. refraction_index : float, optional For ri_calculation_mode=0, the real part of the refraction index. attenuation_coefficient : float, optional For ri_calculation_mode=0, the attenuation coefficient in m^-1 !!!. dabax : None or instance of DabaxXraylib, The pointer to the dabax library (used for ri_calculation_mode=3). radius : float, optional For surface_shape=(1,2), the lens radius in m. (For parabolic lenses, it is the radius at the tip for paraboloid.) conic_coefficients1 : None or list, optional For surface_shape=3, A list with the 10 conic coefficients of interface 1. None is considered as Plane. conic_coefficients2 : None or list, optional For surface_shape=3, A list with the 10 conic coefficients of interface 2. None is considered as Plane. Returns ------- instance of S4CRL. """ def __init__(self, name="Undefined", n_lens=1, piling_thickness=0.0, # syned stuff, boundary_shape=None, # syned stuff, replaces "diameter" in the shadow3 append_lens material="", density=1.0, thickness=0.0, # syned stuff, lens thickness [m] (distance between the two interfaces at the center of the lenses) surface_shape=1, # now: 0=plane, 1=sphere, 2=parabola, 3=conic coefficients # (in shadow3: 1=sphere 4=paraboloid, 5=plane) convex_to_the_beam=1, # for surface_shape: convexity of the first interface exposed to the beam 0=No, 1=Yes # the second interface has opposite convexity cylinder_angle=0, # for surface_shape: 0=not cylindricaL, 1=meridional 2=sagittal ri_calculation_mode=0, # source of refraction indices and absorption coefficients # 0=User # 1=prerefl file # 2=direct calculation using xraylib # 3=direct calculation using dabax prerefl_file=None, # for ri_calculation_mode=0: file name (from prerefl) to get the refraction index. refraction_index=1.0, # for ri_calculation_mode=1: n (real) attenuation_coefficient=0.0, # for ri_calculation_mode=1: mu in m^-1 (real) dabax=None, radius=500e-6, # for surface_shape=(1,2): lens radius [m] (for spherical, or radius at the tip for paraboloid) conic_coefficients1=None, # for surface_shape = 3: the conic coefficients of the first interface conic_coefficients2=None, # for surface_shape = 3: the conic coefficients of the second interface ): """ Parameters ---------- """ S4RefractiveLensOpticalElementDecorator.__init__(self, surface_shape, convex_to_the_beam, cylinder_angle, ri_calculation_mode, prerefl_file, refraction_index, attenuation_coefficient, density, dabax, radius, conic_coefficients1, conic_coefficients2) CRL.__init__(self, name=name, n_lens=n_lens, surface_shape1=self.get_surface_shape_instance()[0], surface_shape2=self.get_surface_shape_instance()[1], boundary_shape=boundary_shape, material=material, thickness=thickness, piling_thickness=piling_thickness) if ri_calculation_mode == 3: if isinstance(dabax, DabaxXraylib): dabax_txt = 'DabaxXraylib(file_f1f2="%s", file_CrossSec="%s")' % \ (dabax.get_file_f1f2(), dabax.get_file_CrossSec()) else: dabax_txt = 'DabaxXraylib()' else: dabax_txt = "None" self.__inputs = { "name": name, "n_lens" : n_lens, "boundary_shape": boundary_shape, "material": material, "thickness": thickness, "piling_thickness" : piling_thickness, "surface_shape": surface_shape, "convex_to_the_beam": convex_to_the_beam, "cylinder_angle": cylinder_angle, "ri_calculation_mode": ri_calculation_mode, "prerefl_file": prerefl_file, "refraction_index": refraction_index, "attenuation_coefficient": attenuation_coefficient, "density": density, "dabax": dabax_txt, "radius": radius, "conic_coefficients1": repr(conic_coefficients1), "conic_coefficients2": repr(conic_coefficients2), }
[docs] def interthickness(self): """ Returns the interthickness of the beamline element, which is the distance covered by the element along the optical axis. Elements with a single optical surface (mirrors, crystals, etc.) have interthickness zero. Elements like lenses, CRL, transfocators, etc. have interthickness > 0. It is redefined in this method. Note that the interthickness is the projection along the (image) optical axis. Returns ------- float """ return self._piling_thickness * self._n_lens
[docs] def get_info(self): """ Returns the specific information of the S4 CRL optical element. Returns ------- str """ txt = "\n\n" txt += "CRL (COMPOUND REFRACTIVE LENS)\n" txt += "\n" txt += "Source of material refraction index for all lenses:\n" if self._ri_calculation_mode == 0: txt += " constant value\n" txt += " index of refraction (real part): %f \n" % self._refraction_index txt += " attenuation coeff: %f \n" % self._attenuation_coefficient elif self._ri_calculation_mode == 1: txt += " file generated by prerefl preprocessor: %s \n" % self._prerefl_file elif self._ri_calculation_mode == 2: txt += " calculated using xraylib\n" txt += " material: %s\n" % self.__inputs["material"] txt += " density: %f g/cm3\n" % self.__inputs["density"] elif self._ri_calculation_mode == 3: txt += " calculated using dabax\n" txt += " material: %s\n" % self.__inputs["material"] txt += " density: %f g/cm3\n" % self.__inputs["density"] txt += "Number of lenses: %d \n" % self._n_lens txt += "Radius: %f m\n" % self.__inputs["radius"] txt += "Piling thickness: %f m\n" % self._piling_thickness txt += "thickness: %f m\n" % self._thickness txt += "Total thickness [interthickness]: %f m\n" % self.interthickness() txt += "convex_to_the_beam (0:No, 1=Yes): %d \n" % self.__inputs["convex_to_the_beam"] if self.__inputs["cylinder_angle"] == 0: txt += "2D focusing lens\n" elif self.__inputs["cylinder_angle"] == 1: txt += "1D focusing lens (meridional)\n" elif self.__inputs["cylinder_angle"] == 2: txt += "1D focusing lens (sagittal)\n" if self._surface_shapes == 0: txt = "Lenses are PLANE" elif self._surface_shapes == 1: txt = "Lenses are SPHERE" elif self._surface_shapes == 2: txt = "Lenses are PARABOLA" elif self._surface_shapes == 3: txt = "Lenses are CONIC COEFFICIENTS" # (in shadow3: 1=sphere 4=paraboloid, 5=plane txt += "\n" ss = self.get_surface_shape_instance()[0] if ss is None: txt += "Surface shape 1 is: Plane (** UNDEFINED?? **)\n" else: txt += "Surface shape 1 is: %s\n" % ss.__class__.__name__ txt += "Parameters:\n %s\n" % ss.info() ss = self.get_surface_shape_instance()[1] if ss is None: txt += "Surface shape 2 is: Plane (** UNDEFINED?? **)\n" else: txt += "Surface shape 2 is: %s\n" % ss.__class__.__name__ txt += "Parameters:\n %s\n" % ss.info() # # txt += self.get_optical_surface_instance().info() + "\n" # boundary = self.get_boundary_shape() if boundary is None: txt += "Surface boundaries not considered (infinite)" else: txt += "Surface boundaries are: %s\n" % boundary.__class__.__name__ txt += " Limits: " + repr( boundary.get_boundaries()) + "\n" txt += boundary.info() return txt
[docs] def to_python_code_boundary_shape(self): """ Creates a code block with information of boundary shape. Returns ------- str The text with the code. """ txt = "" bs = self._boundary_shape if bs is None: txt += "\nboundary_shape = None" elif isinstance(bs, Rectangle): txt += "\nfrom syned.beamline.shape import Rectangle" txt += "\nboundary_shape = Rectangle(x_left=%g, x_right=%g, y_bottom=%g, y_top=%g)" % bs.get_boundaries() elif isinstance(bs, Circle): txt += "\nfrom syned.beamline.shape import Circle" txt += "\nboundary_shape = Circle(radius=%g, x_center=%g,y_center=%g)" % bs.get_boundaries() elif isinstance(bs, Ellipse): txt += "\nfrom syned.beamline.shape import Ellipse" txt += "\nboundary_shape = Ellipse(a_axis_min=%g, a_axis_max=%g, b_axis_min=%g, b_axis_max=%g)" % bs.get_boundaries() return txt
[docs] def to_python_code(self, **kwargs): """ Creates the python code for defining the element. Parameters ---------- **kwargs Returns ------- str Python code. """ txt = self.to_python_code_boundary_shape() txt_pre = """ from shadow4.beamline.optical_elements.refractors.s4_crl import S4CRL optical_element = S4CRL(name='{name:s}', n_lens={n_lens:d}, piling_thickness={piling_thickness:g}, # syned stuff boundary_shape=boundary_shape, # syned stuff, replaces "diameter" in the shadow3 append_lens material='{material:s}', # the material for ri_calculation_mode > 1 density={density:g}, # the density for ri_calculation_mode > 1 thickness={thickness}, # syned stuff, lens thickness [m] (distance between the two interfaces at the center of the lenses) surface_shape={surface_shape}, # 0=plane, 1=sphere, 2=parabola, 3=conic coefficients convex_to_the_beam={convex_to_the_beam}, # for surface_shape: convexity of the first interface exposed to the beam 0=No, 1=Yes cylinder_angle={cylinder_angle}, # for surface_shape: 0=not cylindricaL, 1=meridional 2=sagittal ri_calculation_mode={ri_calculation_mode}, # source of refr indices and absorp coeff 0=User, 1=prerefl file, 2=xraylib, 3=dabax prerefl_file='{prerefl_file:s}', # for ri_calculation_mode=0: file name (from prerefl) to get the refraction index. refraction_index={refraction_index:.10g}, # for ri_calculation_mode=1: n (real) attenuation_coefficient={attenuation_coefficient:g}, # for ri_calculation_mode=1: mu in m^-1 (real) dabax={dabax:s}, # if using dabax (ri_calculation_mode=3), instance of DabaxXraylib() (use None for default) radius={radius:g}, # for surface_shape=(1,2): lens radius [m] (for spherical, or radius at the tip for paraboloid) conic_coefficients1={conic_coefficients1}, # for surface_shape = 3: the conic coefficients of the single lens interface 1 conic_coefficients2={conic_coefficients2}, # for surface_shape = 3: the conic coefficients of the single lens interface 2 ) """ txt += txt_pre.format(**self.__inputs) return txt
[docs] def get_lens_interfaces(self): single_lens_optical_surfaces = self.get_optical_surface_instance() lens_interfaces = numpy.full((self._n_lens, 2), None) boundary_shape = self.get_boundary_shape() for lens_index in range(self._n_lens): lens_interfaces[lens_index, 0], \ lens_interfaces[lens_index, 1] = \ _get_lens_interfaces(lens_optical_surfaces=single_lens_optical_surfaces, boundary_shape=boundary_shape, ri_calculation_mode=self._ri_calculation_mode, refraction_index=self._refraction_index, attenuation_coefficient=self._attenuation_coefficient, prerefl_file=self._prerefl_file, material=self.get_material(), density=self._density, dabax=self._dabax, ) return lens_interfaces
[docs]class S4CRLElement(S4BeamlineElement): """ Constructor. Parameters ---------- optical_element : instance of OpticalElement, optional The syned optical element. coordinates : instance of ElementCoordinates, optional The syned element coordinates. movements : instance of S4BeamlineElementMovements, optional The S4 element movements. (The same movements are applied to the two interfaces. Therefore, each rotation is applied around the local axes of each interface, which are different.) input_beam : instance of S4Beam, optional The S4 incident beam. Returns ------- instance of S4CRLElement. """ def __init__(self, optical_element: S4CRL = None, coordinates: ElementCoordinates = None, movements: S4BeamlineElementMovements = None, input_beam: S4Beam = None): super().__init__(optical_element=optical_element if optical_element is not None else S4CRL(), coordinates=coordinates if coordinates is not None else ElementCoordinates(), movements=movements, input_beam=input_beam)
[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 += "\nimport numpy" txt += self.to_python_code_coordinates() txt += self.to_python_code_movements() txt += "\nfrom shadow4.beamline.optical_elements.refractors.s4_crl import S4CRLElement" txt += "\nbeamline_element = S4CRLElement(optical_element=optical_element, coordinates=coordinates, movements=movements, input_beam=beam)" txt += "\n\nbeam, footprint = beamline_element.trace_beam()" return txt
[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() movements = self.get_movements() oe = self.get_optical_element() optical_surfaces = oe.get_lens_interfaces() p, q, angle_radial, angle_radial_out, angle_azimuthal = self.get_coordinates().get_positions() n_lens = oe.get_n_lens() if n_lens == 0: # use screens in the position of the interfaces coordinates_1 = ElementCoordinates(p=p, q=oe.get_thickness()*0.5, angle_radial=angle_radial, angle_radial_out=numpy.pi, angle_azimuthal=angle_azimuthal) coordinates_2 = ElementCoordinates(p=oe.get_thickness()*0.5, q=q, angle_radial=0.0, angle_radial_out=angle_radial_out, angle_azimuthal=0.0) beamline_element = S4ScreenElement(optical_element=S4Screen(), coordinates=coordinates_1, input_beam=input_beam) beam1, footprint1 = beamline_element.trace_beam() beamline_element = S4ScreenElement(optical_element=S4Screen(), coordinates=coordinates_2, input_beam=beam1) beam2, footprint2 = beamline_element.trace_beam() else: for lens_index in range(n_lens): if lens_index==0: source_plane = p else: source_plane = oe.get_piling_thickness() if lens_index==n_lens-1: image_plane = q else: image_plane = 0.0 coordinates_1 = ElementCoordinates(p=source_plane, q=oe.get_thickness()*0.5, angle_radial=angle_radial, angle_radial_out=numpy.pi, angle_azimuthal=angle_azimuthal) coordinates_2 = ElementCoordinates(p=oe.get_thickness()*0.5, q=image_plane, angle_radial=0.0, angle_radial_out=angle_radial_out, angle_azimuthal=0.0) beamline_element_1 = S4ConicInterfaceElement(optical_element=optical_surfaces[lens_index, 0], coordinates=coordinates_1, movements=movements, input_beam=input_beam) if lens_index==0: beam1, footprint1 = beamline_element_1.trace_beam() n1, mu1, n2, mu2 = beamline_element_1.get_stored_optical_constants() else: beam1, _ = beamline_element_1.trace_beam(reused_stored_optical_constants=(n1, mu1, n2, mu2)) beamline_element_2 = S4ConicInterfaceElement(optical_element=optical_surfaces[lens_index, 1], coordinates=coordinates_2, movements=movements, input_beam=beam1) if lens_index==n_lens-1: beam2, footprint2 = beamline_element_2.trace_beam(reused_stored_optical_constants=(n2, mu2, n1, mu1)) else: beam2, _ = beamline_element_2.trace_beam(reused_stored_optical_constants=(n2, mu2, n1, mu1)) if lens_index < n_lens-1: input_beam = beam2.duplicate() return beam2, [footprint1, footprint2]
if __name__ == "__main__": if False: from shadow4.physical_models.prerefl.prerefl import PreRefl # # collimated source # from shadow4.sources.source_geometrical.source_geometrical import SourceGeometrical src = SourceGeometrical() src.set_energy_distribution_singleline(value=5000, unit='eV') src.set_spatial_type_rectangle(width=1e-3, height=1e-3) src.set_angular_distribution_uniform(0, 0, 0, 0) beam = src.get_beam() filename = "Be.dat" PreRefl.prerefl(interactive=False, SYMBOL="Be", FILE=filename, DENSITY=1.848, E_MIN=4500, E_MAX=5500, E_STEP=1) # # lens # lens = S4CRL(n_lens=3, piling_thickness=2.5e-3, prerefl_file=filename) e = S4CRLElement(optical_element=lens, coordinates=ElementCoordinates(p=10, q=20, angle_radial=0, angle_azimuthal=0, angle_radial_out=numpy.pi), input_beam=beam) e.trace_beam() print(e.to_python_code()) if True: from shadow4.tools.logger import set_verbose set_verbose() 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_gaussian(sigma_h=6.38000015e-05, sigma_v=0.000064) light_source.set_depth_distribution_off() light_source.set_angular_distribution_cone(cone_max=0.000010, cone_min=0.000000) light_source.set_energy_distribution_singleline(14000.000000, unit='eV') 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.refractors.s4_crl import S4CRL optical_element = S4CRL(name='Compound Refractive Lens', n_lens=30, piling_thickness=0.000625, # syned stuff boundary_shape=boundary_shape, # syned stuff, replaces "diameter" in the shadow3 append_lens material='Al', # the material for ri_calculation_mode > 1 density=2.6989, # the density for ri_calculation_mode > 1 thickness=2.4999999999999998e-05, # syned stuff, lens thickness [m] (distance between the two interfaces at the center of the lenses) surface_shape=1, # now: 0=plane, 1=sphere, 2=parabola, 3=conic coefficients # (in shadow3: 1=sphere 4=paraboloid, 5=plane) convex_to_the_beam=0, # for surface_shape: convexity of the first interface exposed to the beam 0=No, 1=Yes cylinder_angle=1, # for surface_shape: 0=not cylindricaL, 1=meridional 2=sagittal ri_calculation_mode=3, # source of refraction indices and absorption coefficients # 0=User, 1=prerefl file, 2=xraylib, 3=dabax prerefl_file='Al5_55.dat', # for ri_calculation_mode=0: file name (from prerefl) to get the refraction index. refraction_index=1, # for ri_calculation_mode=1: n (real) attenuation_coefficient=0, # for ri_calculation_mode=1: mu in m^-1 (real) dabax=None, # the pointer to dabax library radius=0.0003, # for surface_shape=(1,2): lens radius [m] (for spherical, or radius at the tip for paraboloid) conic_coefficients1=None, # for surface_shape = 3: the conic coefficients of the single lens interface 1 conic_coefficients2=None, # for surface_shape = 3: the conic coefficients of the single lens interface 2 ) import numpy from syned.beamline.element_coordinates import ElementCoordinates coordinates = ElementCoordinates(p=30, q=1.8987, angle_radial=0, angle_azimuthal=0, angle_radial_out=3.141592654) movements = None from shadow4.beamline.optical_elements.refractors.s4_crl import S4CRLElement beamline_element = S4CRLElement(optical_element=optical_element, coordinates=coordinates, movements=movements, input_beam=beam) beam, mirr = 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') print(beam.intensity()) print(optical_element.get_info()) print(optical_element.to_python_code())