Source code for shadow4.beamline.optical_elements.mirrors.s4_mirror

"""
The s4 mirror base class (optical element and beamline element).
"""
import numpy

from syned.beamline.shape import Rectangle, Ellipse

from syned.beamline.element_coordinates import ElementCoordinates
from syned.beamline.optical_elements.mirrors.mirror import Mirror

from dabax.dabax_xraylib import DabaxXraylib

from shadow4.physical_models.prerefl.prerefl import PreRefl
from shadow4.beamline.s4_beamline_element import S4BeamlineElement
from shadow4.beam.s4_beam import S4Beam
from shadow4.beamline.s4_beamline_element_movements import S4BeamlineElementMovements

from shadow4.optical_surfaces.s4_conic import S4Conic
from shadow4.optical_surfaces.s4_toroid import S4Toroid
from shadow4.optical_surfaces.s4_mesh import S4Mesh
from shadow4.tools.logger import is_verbose

[docs]class S4Mirror(Mirror): """ Shadow4 Mirror Class This is a base class for mirrors. Use derived classes for plane or other curved mirror surfaces. Constructor. Parameters ---------- name : str, optional The name of the mirror. boundary_shape : instance of BoundaryShape, optional The boundary shape of the mirror. surface_shape : instance of SurfaceShape, optional The surface shape of the mirror. f_reflec : int, optional the reflectivity of surface: - 0=no reflectivity, - 1=full polarization. f_refl : int, optional A flag to indicate the source of reflectivities: * 0=prerefl file, * 1=refraction index, * 2=user defined file (1D angle in mrad, reflectivity), * 3=user defined file (1D energy in eV, reflectivity), * 4=user defined file (2D energy in eV, angle in mrad, reflectivity), * 5=direct calculation using xraylib, * 6=direct calculation using dabax. file_refl : str, optional name of user defined file (for f_refl=0). refraction_index : complex, optional complex scalar with refraction index n (for f_refl=1). material : str, optional string with material formula (for f_refl=5,6) density : float, optional material density in g/cm^3 (for f_refl=5,6) dabax : None or instance of DabaxXraylib, The pointer to the dabax library (used for f_refl=6). Returns ------- instance of S4Mirror. """ def __init__(self, name="Undefined", boundary_shape=None, surface_shape=None, # inputs related to mirror reflectivity f_reflec=0, # reflectivity of surface: 0=no reflectivity, 1=full polarization f_refl=0, # 0=prerefl file # 1=refraction index # 2=user defined file (1D reflectivity vs angle) # 3=user defined file (1D reflectivity vs energy) # 4=user defined file (2D reflectivity vs energy and angle) # 5=direct calculation using xraylib # 6=direct calculation using dabax file_refl="", # preprocessor file fir f_refl=0,2,3,4 refraction_index=1.0, # refraction index (complex) for f_refl=1 coating_material="", # string with coating material formula for f_refl=5,6 coating_density=1.0, # coating material density for f_refl=5,6 coating_roughness=0.0, # coating material roughness in A for f_refl=5,6 dabax=None, ): Mirror.__init__(self, name=name, surface_shape=surface_shape, boundary_shape=boundary_shape, coating=coating_material, coating_thickness=None, #not used ) # reflectivity self._f_reflec = f_reflec self._f_refl = f_refl self._file_refl = file_refl self._refraction_index = refraction_index self._coating_density = coating_density self._coating_roughness = coating_roughness self._dabax = dabax # support text containg name of variable, help text and unit. Will be stored in self._support_dictionary self._add_support_text([ ("f_reflec", "S4: flag for reflectivity", ""), ("f_refl", "S4: refl. source for f_reflec=1: 0=prerefl, 5=xraylib, 6=dabax", ""), ("file_refl", "S4: for f_refl=0: file name", ""), ("refraction_index", "S4: refraction index", ""), ("coating_density", "S4: density of coating material", "g/cm3"), ("coating_roughness", "S4: roughness of coating material", "A"), ] )
[docs] def get_info(self): """ Returns the specific information of the S4 mirror optical element. Returns ------- str """ txt = "\n\n" txt += "MIRROR\n" if self._f_reflec: txt += "Reflectivity calculation: ON\n" if self._f_refl == 0: txt += " Calculated reflectivity from preprocessor (prerefl) file: %s\n" % self._file_refl elif self._f_refl == 1: txt += " Calculated reflectivity from refraction index\n" elif self._f_refl == 2: txt += " Calculated reflectivity from user defined file (1D reflectivity vs angle): %s\n" % self._file_refl elif self._f_refl == 3: txt += " Calculated reflectivity from user defined file (1D reflectivity vs energy): %s\n" % self._file_refl elif self._f_refl == 4: txt += " Calculated reflectivity from user defined file (2D reflectivity vs energy and angle): %s\n" % self._file_refl elif self._f_refl == 5: txt += " Calculated reflectivity using xraylib\n" elif self._f_refl == 6: txt += " Calculated reflectivity using dabax\n" else: txt += "Reflectivity calculation: OFF\n" txt += "\n" ss = self.get_surface_shape() if ss is None: txt += "Surface shape is: Plane (** UNDEFINED?? **)\n" else: txt += "Surface shape is: %s\n" % ss.__class__.__name__ try: sc = self._surface_calculation if sc == 0: txt += "Mirror parameters COMPUTED\n" txt += " Objective focus at p: %f m\n" % ss.get_p_focus() txt += " Image focus at p: %f m\n" % ss.get_q_focus() txt += " Incidence angle (grazing): %f mrad\n" % (1e3 * ss.get_grazing_angle()) else: txt += "Mirror parameters EXTERNAL\n" except: pass txt += "\nParameters:\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 = "" # "\nfrom shadow4.beamline.optical_elements.mirrors.s4_plane_mirror import S4PlaneMirror" 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, 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
def _apply_mirror_reflection(self, beam): sur = self.get_optical_surface_instance() footprint, normal, _, _, _, _, _ = sur.apply_specular_reflection_on_beam(beam) return footprint, normal def _get_dabax_txt(self): if self._f_reflec == 1 and self._f_refl == 6: if isinstance(self._dabax, DabaxXraylib): dabax_txt = 'DabaxXraylib(file_f1f2="%s")' % (self._dabax.get_file_f1f2()) else: dabax_txt = "DabaxXraylib()" else: dabax_txt = "None" return dabax_txt
[docs]class S4MirrorElement(S4BeamlineElement): """ The base class for Shadow4 mirror element. It is made of a S4Mirror and an ElementCoordinates instance. It also includes the input beam. Use derived classes for plane or other curved crystal surfaces. 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. input_beam : instance of S4Beam, optional The S4 incident beam. Returns ------- instance of S4MirrorElement. """ def __init__(self, optical_element : S4Mirror = None, coordinates : ElementCoordinates = None, movements : S4BeamlineElementMovements = None, input_beam : S4Beam = None): super().__init__(optical_element=optical_element if optical_element is not None else S4Mirror(), coordinates=coordinates if coordinates is not None else ElementCoordinates(), movements=movements, input_beam=input_beam)
[docs] def trace_beam(self, **params): """ Runs (ray tracing) the input beam through the element. Parameters ---------- **params : generic parameters can be passed, in particular: flag_lost_value : float, optional value to flag lost rays (default=-1). change_reference_system_in : boolean, optional indicates if the input beam is converted to local o.e. frame (default=True). change_reference_system_out : boolean, optional indicates if the outgoing beam is converted image o.e. frame (default=True). Returns ------- tuple (output_beam, footprint) instances of S4Beam. """ flag_lost_value = params.get("flag_lost_value", -1) change_reference_system_in = params.get("change_reference_system_in", True) change_reference_system_out = params.get("change_reference_system_out", True) if is_verbose(): if not change_reference_system_in: print("change_reference_system_in = False: skipping reference change to o.e.") if not change_reference_system_out: print("change_reference_system_out = False: skipping reference change from o.e. to image") p = self.get_coordinates().p() q = self.get_coordinates().q() theta_grazing1 = numpy.pi / 2 - self.get_coordinates().angle_radial() theta_grazing2 = numpy.pi / 2 - self.get_coordinates().angle_radial_out() alpha1 = self.get_coordinates().angle_azimuthal() # input_beam = self.get_input_beam().duplicate() # # put beam in mirror reference system # if change_reference_system_in: input_beam.rotate(alpha1, axis=2) input_beam.rotate(theta_grazing1, axis=1) input_beam.translation([0.0, -p * numpy.cos(theta_grazing1), p * numpy.sin(theta_grazing1)]) # mirror movement: movements = self.get_movements() if movements is not None: if movements.f_move: input_beam.rot_for(OFFX=movements.offset_x, OFFY=movements.offset_y, OFFZ=movements.offset_z, X_ROT=movements.rotation_x, Y_ROT=movements.rotation_y, Z_ROT=movements.rotation_z) # # reflect beam in the mirror surface # soe = self.get_optical_element() #._optical_element_syned v_in = input_beam.get_columns([4,5,6]) footprint, normal = self.get_optical_element()._apply_mirror_reflection(input_beam) if movements is not None: if movements.f_move: footprint.rot_back(OFFX=movements.offset_x, OFFY=movements.offset_y, OFFZ=movements.offset_z, X_ROT=movements.rotation_x, Y_ROT=movements.rotation_y, Z_ROT=movements.rotation_z) # # apply mirror boundaries # footprint.apply_boundaries_syned(soe.get_boundary_shape(), flag_lost_value=flag_lost_value) # # apply mirror reflectivity # rs = 1.0 rp = 1.0 if soe._f_reflec == 1: # apply reflectivity v_out = input_beam.get_columns([4, 5, 6]) angle_in = numpy.arccos(v_in[0,:] * normal[0,:] + v_in[1,:] * normal[1,:] + v_in[2,:] * normal[2,:]) angle_out = numpy.arccos(v_out[0,:] * normal[0,:] + v_out[1,:] * normal[1,:] + v_out[2,:] * normal[2,:]) grazing_angle_mrad = 1e3 * (numpy.pi / 2 - angle_in) # TODO: it should be checked why s4_conic gives a downwards normal and s4_mesh an upwards normal # This causes negative angles with s4_mesh. Therefore abs() is used grazing_angle_mrad = numpy.abs(grazing_angle_mrad) if soe._f_refl == 0: # prerefl prerefl_file = soe._file_refl pr = PreRefl() pr.read_preprocessor_file(prerefl_file) if is_verbose(): print(pr.info()) rs, rp = pr.reflectivity_amplitudes_fresnel(grazing_angle_mrad=grazing_angle_mrad, photon_energy_ev=input_beam.get_column(-11), roughness_rms_A=soe._coating_roughness, method=0, ) elif soe._f_refl == 1: # refraction index external refraction_index_2 = soe._refraction_index refraction_index_1 = numpy.ones_like(refraction_index_2) rs, rp = PreRefl.reflectivity_amplitudes_fresnel_external( photon_energy_ev=input_beam.get_column(-11), refraction_index_1=refraction_index_1, refraction_index_2=refraction_index_2, grazing_angle_mrad=grazing_angle_mrad, roughness_rms_A=soe._coating_roughness, method=0, ) elif soe._f_refl == 2: # user angle, mrad ref values = numpy.loadtxt(soe._file_refl) mirror_grazing_angles = values[:, 0] mirror_reflectivities = values[:, 1] if mirror_grazing_angles[-1] < mirror_grazing_angles[0]: # XOPPY MLayer gives angles in descendent order mirror_grazing_angles = values[:, 0][::-1] mirror_reflectivities = values[:, 1][::-1] Rs = numpy.interp(grazing_angle_mrad, mirror_grazing_angles, mirror_reflectivities, left=mirror_reflectivities[0], right=mirror_reflectivities[-1]) Rp = Rs rs, rp = numpy.sqrt(Rs), numpy.sqrt(Rp) # the phase is not managed! elif soe._f_refl == 3: # user energy beam_energies = input_beam.get_photon_energy_eV() values = numpy.loadtxt(soe._file_refl) mirror_energies = values[:, 0] mirror_reflectivities = values[:, 1] Rs = numpy.interp(beam_energies, mirror_energies, mirror_reflectivities, left=mirror_reflectivities[0], right=mirror_reflectivities[-1]) Rp = Rs rs, rp = numpy.sqrt(Rs), numpy.sqrt(Rp) # the phase is not managed! elif soe._f_refl == 4: # user 2D values = numpy.loadtxt(soe._file_refl) beam_energies = input_beam.get_photon_energy_eV() mirror_energies = values[:, 0] mirror_grazing_angles = values[:, 1] mirror_energies = numpy.unique(mirror_energies) mirror_grazing_angles = numpy.unique(mirror_grazing_angles) def get_interpolator_weight_2D(mirror_energies, mirror_grazing_angles, mirror_reflectivities): mirror_reflectivities = numpy.reshape(mirror_reflectivities, (mirror_energies.shape[0], mirror_grazing_angles.shape[0])) from scipy.interpolate import RectBivariateSpline interpolator = RectBivariateSpline(mirror_energies, mirror_grazing_angles, mirror_reflectivities, kx=2, ky=2) interpolated_weight = numpy.zeros(beam_energies.shape[0]) for energy, angle, i in zip(beam_energies, grazing_angle_mrad, range(interpolated_weight.shape[0])): interpolated_weight[i] = interpolator(energy, angle) interpolated_weight[numpy.where(numpy.isnan(interpolated_weight))] = 0.0 return interpolated_weight if values.shape[1] == 3: mirror_reflectivities = values[:, 2] Rs = get_interpolator_weight_2D(mirror_energies, mirror_grazing_angles, mirror_reflectivities) Rp = Rs elif values.shape[1] == 4: mirror_reflectivities_s = values[:, 2] mirror_reflectivities_p = values[:, 3] Rs = get_interpolator_weight_2D(mirror_energies, mirror_grazing_angles, mirror_reflectivities_s) Rp = get_interpolator_weight_2D(mirror_energies, mirror_grazing_angles, mirror_reflectivities_p) rs, rp = numpy.sqrt(Rs), numpy.sqrt(Rp) # the phase is not managed! elif soe._f_refl == 5: # xraylib rs, rp = PreRefl.reflectivity_amplitudes_fresnel_external_xraylib( photon_energy_ev=input_beam.get_column(-11), coating_material=soe._coating, coating_density=soe._coating_density, grazing_angle_mrad=grazing_angle_mrad, roughness_rms_A=soe._coating_roughness, method=0, # 0=born & wolf, 1=parratt, 2=shadow3 ) elif soe._f_refl == 6: # dabax rs, rp = PreRefl.reflectivity_amplitudes_fresnel_external_dabax( photon_energy_ev=input_beam.get_column(-11), coating_material=soe._coating, coating_density=soe._coating_density, grazing_angle_mrad=grazing_angle_mrad, roughness_rms_A=soe._coating_roughness, method=0, # 0=born & wolf, 1=parratt, 2=shadow3 dabax=soe._dabax, ) else: raise Exception("Not implemented source of mirror reflectivity") # TODO: # WARNING This application of the rs and rp coefficients does not take into account: # 1) The possible rotation of the sigma and pi directions by the oe orientation angle footprint.apply_reflectivities(numpy.abs(rs), numpy.abs(rp)) footprint.add_phases(numpy.angle(rs), numpy.angle(rp)) # # TODO: write angle.xx for comparison # # # from mirror reference system to image plane # output_beam = footprint.duplicate() if change_reference_system_out: output_beam.change_to_image_reference_system(theta_grazing2, q) return output_beam, footprint
# # i/o utilities #
[docs] def set_grazing_angle(self, theta_grazing, theta_azimuthal=None): """ Sets the grazing angle. Parameters ---------- theta_grazing : float, optional The grazing angle in rad. theta_azimuthal : float, optional The azimuthal angle in rad. """ self.get_coordinates()._angle_radial = numpy.pi / 2 - theta_grazing self.get_coordinates()._angle_radial_out = numpy.pi / 2 - theta_grazing if theta_azimuthal is not None: self.get_coordinates()._angle_azimuthal = theta_azimuthal
if __name__ == "__main__": # # testing mirror movements... # # # # from shadow4.sources.source_geometrical.source_geometrical import SourceGeometrical light_source = SourceGeometrical(name='SourceGeometrical', nrays=10000, seed=5676561) light_source.set_spatial_type_gaussian(sigma_h=5e-06, sigma_v=0.000001) light_source.set_angular_distribution_gaussian(sigdix=0.000001, sigdiz=0.000001) light_source.set_energy_distribution_singleline(1.000000, unit='A') light_source.set_polarization(polarization_degree=1.000000, phase_diff=0.000000, coherent_beam=0) beam = light_source.get_beam() # optical element number XX boundary_shape = None from shadow4.beamline.optical_elements.mirrors.s4_ellipsoid_mirror import S4EllipsoidMirror optical_element = S4EllipsoidMirror(name='Ellipsoid Mirror', boundary_shape=boundary_shape, surface_calculation=0, is_cylinder=0, cylinder_direction=0, convexity=1, min_axis=0.000000, maj_axis=0.000000, p_focus=10.000000, q_focus=6.000000, grazing_angle=0.020944, f_reflec=0, f_refl=1, file_refl='<none>', refraction_index=0.99999 + 0.001j, coating_material='Si', coating_density=2.33, coating_roughness=0) from syned.beamline.element_coordinates import ElementCoordinates coordinates = ElementCoordinates(p=10, q=6, angle_radial=1.54985, angle_azimuthal=0, angle_radial_out=1.54985) from shadow4.beamline.optical_elements.mirrors.s4_ellipsoid_mirror import S4EllipsoidMirrorElement from shadow4.beamline.s4_beamline_element_movements import S4BeamlineElementMovements movements = S4BeamlineElementMovements(f_move=1,rotation_x=numpy.radians(1e-3),rotation_z=numpy.radians(1)) beamline_element = S4EllipsoidMirrorElement(optical_element=optical_element, coordinates=coordinates, movements=movements, input_beam=beam) beam, mirr = beamline_element.trace_beam() # 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(beamline_element.to_python_code()) print(optical_element.info())