"""
The s4 numerical mesh crystal (optical element and beamline element).
"""
from syned.beamline.shape import NumericalMesh
from shadow4.beam.s4_beam import S4Beam
from shadow4.beamline.optical_elements.crystals.s4_crystal import S4CrystalElement, S4Crystal, ElementCoordinates
from shadow4.beamline.s4_optical_element_decorators import S4NumericalMeshOpticalElementDecorator
from shadow4.beamline.s4_beamline_element_movements import S4BeamlineElementMovements
[docs]class S4NumericalMeshCrystal(S4Crystal, S4NumericalMeshOpticalElementDecorator):
"""
Shadow4 Mesh Crystal Class
This is a curved perfect crystal in reflection geometry (Bragg), using the diffracted beam.
The surface shape is defined as a numerical mesh, either in an hdf5 file or in arrays.
Constructor.
Parameters
----------
name : str, optional
A name for the crystal
boundary_shape : instance of BoundaryShape, optional
The information on the crystal boundaries.
xx : numpy array, optional
The array with the X (width) coordinates.
yy : numpy array, optional
The array with the Y (length) coordinates.
zz : numpy array, optional
The array with the Z(X,Y) coordinates.
surface_data_file : str, optional
The h5 file name with the mesh following the Oasys convention.
material : str, optional
The crystal material name (a name accepted by crystalpy).
miller_index_h : int, optional
The Miller index H.
miller_index_k : int, optional
The Miller index K.
miller_index_l : int, optional
The Miller index L.
f_bragg_a : int, optional
Asymmetric crystal 0:No, 1:Yes.
asymmetry_angle : float, optional
For f_bragg_a=1, the asymmetry angle (angle between crystal planes and surface) in rads.
is_thick : int, optional
Use thick crystal approximation.
thickness : float, optional
For is_thick=0, the crystal thickness in m.
f_central : int, optional
Flag for autosetting the crystal to the corrected Bragg angle.
f_phot_cent : int, optional
0: setting photon energy in eV, 1:setting photon wavelength in m.
phot_cent : float, optional
for f_central=1, the value of the photon energy (f_phot_cent=0) or photon wavelength (f_phot_cent=1).
f_ext : inf, optional
Flag for autosetting the crystal surface parameters.
0: internal/calculated parameters, 1:external/user defined parameters. TODO: delete?
material_constants_library_flag : int, optional
Flag for indicating the origin of the crystal data:
0: xraylib, 1: dabax, 2: preprocessor file v1, 3: preprocessor file v2.
file_refl : str, optional
for material_constants_library_flag=2,3, the name of the file containing the crystal parameters.
dabax : None or instance of DabaxXraylib,
The pointer to the dabax library (used for material_constants_library_flag=1).
Returns
-------
instance of S4NumericalMeshCrystal.
"""
def __init__(self,
name="Numerical Mesh Crystal",
boundary_shape=None,
xx=None,
yy=None,
zz=None,
surface_data_file="",
# inputs related to crystal
material=None,
miller_index_h=1,
miller_index_k=1,
miller_index_l=1,
f_bragg_a=False,
asymmetry_angle=0.0,
is_thick=0, # 1=Use thick crystal approximation
thickness=0.010,
f_central=False,
f_phot_cent=0,
phot_cent=8000.0,
f_ext=0,
material_constants_library_flag=0, # 0=xraylib, 1=dabax
# 2=shadow preprocessor file v1
# 3=shadow preprocessor file v1
file_refl="",
dabax=None,
):
S4NumericalMeshOpticalElementDecorator.__init__(self, xx, yy, zz, surface_data_file)
S4Crystal.__init__(self,
name=name,
boundary_shape=boundary_shape,
surface_shape=self.get_surface_shape_instance(),
material=material,
# diffraction_geometry=diffraction_geometry, # ?? not supposed to be in syned...
miller_index_h=miller_index_h,
miller_index_k=miller_index_k,
miller_index_l=miller_index_l,
asymmetry_angle=asymmetry_angle,
is_thick=is_thick,
thickness=thickness,
f_central=f_central,
f_phot_cent=f_phot_cent,
phot_cent=phot_cent,
file_refl=file_refl,
f_bragg_a=f_bragg_a,
f_ext=f_ext,
material_constants_library_flag=material_constants_library_flag,
dabax=dabax,
)
self.__inputs = {
"name": name,
"boundary_shape": boundary_shape,
"xx": xx,
"yy": yy,
"zz": zz,
"surface_data_file": surface_data_file,
"material": material,
"miller_index_h": miller_index_h,
"miller_index_k": miller_index_k,
"miller_index_l": miller_index_l,
"asymmetry_angle": asymmetry_angle,
"is_thick": is_thick,
"thickness": thickness,
"f_central": f_central,
"f_phot_cent": f_phot_cent,
"phot_cent": phot_cent,
"file_refl": file_refl,
"f_bragg_a": f_bragg_a,
"f_ext": f_ext,
"material_constants_library_flag": material_constants_library_flag,
"dabax": self._get_dabax_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.crystals.s4_numerical_mesh_crystal import S4NumericalMeshCrystal
optical_element = S4NumericalMeshCrystal(name='{name:s}',boundary_shape=boundary_shape,
xx=None,yy=None,zz=None,surface_data_file='{surface_data_file:s}',
material='{material}',
miller_index_h={miller_index_h}, miller_index_k={miller_index_k}, miller_index_l={miller_index_l},
f_bragg_a={f_bragg_a}, asymmetry_angle={asymmetry_angle},
is_thick={is_thick}, thickness={thickness},
f_central={f_central}, f_phot_cent={f_phot_cent}, phot_cent={phot_cent},
file_refl='{file_refl}',
f_ext={f_ext},
material_constants_library_flag={material_constants_library_flag}, # 0=xraylib,1=dabax,2=preprocessor v1,3=preprocessor v2
dabax={dabax}, # used when material_constants_library_flag=1
)"""
txt += txt_pre.format(**self.__inputs)
return txt
[docs]class S4NumericalMeshCrystalElement(S4CrystalElement):
"""
The Shadow4 mesh crystal element.
It is made of a S4meshCrystal and an ElementCoordinates instance. It also includes the input beam.
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.
"""
def __init__(self,
optical_element: S4NumericalMeshCrystal = None,
coordinates: ElementCoordinates = None,
movements: S4BeamlineElementMovements = None,
input_beam: S4Beam = None):
super().__init__(optical_element=optical_element if optical_element is not None else S4NumericalMeshCrystal(),
coordinates=coordinates if coordinates is not None else ElementCoordinates(),
movements=movements,
input_beam=input_beam)
if not isinstance(self.get_optical_element().get_surface_shape(), NumericalMesh):
raise ValueError("Wrong Optical Element: only Surface Data shape is accepted")
[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 += self.to_python_code_movements()
txt += "\nfrom shadow4.beamline.optical_elements.crystals.s4_numerical_mesh_crystal import S4NumericalMeshCrystalElement"
txt += "\nbeamline_element = S4NumericalMeshCrystalElement(optical_element=optical_element, coordinates=coordinates, movements=movements, input_beam=beam)"
txt += "\n\nbeam, footprint = beamline_element.trace_beam()"
return txt
if __name__ == "__main__":
a = S4NumericalMeshCrystal(name="")
b = S4NumericalMeshCrystalElement(optical_element=a)
print(b.info())
print(b.to_python_code())
#
#
#
from shadow4.sources.source_geometrical.source_geometrical import SourceGeometrical
light_source = SourceGeometrical(name='SourceGeometrical', nrays=25000, seed=5676561)
light_source.set_spatial_type_gaussian(sigma_h=5.70000011e-07, sigma_v=0.000007)
light_source.set_depth_distribution_off()
light_source.set_angular_distribution_gaussian(sigdix=0.000088, sigdiz=0.000007)
light_source.set_energy_distribution_uniform(value_min=9990.000000, value_max=10010.000000, unit='eV')
light_source.set_polarization(polarization_degree=1.000000, phase_diff=0.000000, coherent_beam=0)
beam = light_source.get_beam()
# optical element number XX
#write file
import numpy
# calculate a Gaussian bump
# create an array of 2 cm length
npoints = 51
length = 2.0e-2
x = numpy.linspace(-0.5 * length, 0.5 * length, npoints)
y = numpy.linspace(-0.5 * length * 2, 0.5 * length * 2, npoints * 2)
z = numpy.zeros((x.size, y.size))
# write file for h5
from oasys.util.oasys_util import write_surface_file
write_surface_file(z.T, x, y, 'bump.h5', overwrite=True)
# run
from shadow4.beamline.optical_elements.crystals.s4_plane_crystal import S4PlaneCrystal
optical_element = S4NumericalMeshCrystal(name='Plane Crystal',
boundary_shape=None, material='Si',
miller_index_h=1, miller_index_k=1, miller_index_l=1,
f_bragg_a=False, asymmetry_angle=0.0,
is_thick=1, thickness=0.001,
f_central=1, f_phot_cent=0, phot_cent=10000.0,
file_refl='/users/srio/Oasys/Si5_55.111',
f_ext=0,
material_constants_library_flag=2,
surface_data_file="bump.h5",
)
from syned.beamline.element_coordinates import ElementCoordinates
coordinates = ElementCoordinates(p=30, q=10, angle_radial=1.37174, angle_azimuthal=0, angle_radial_out=1.37174)
from shadow4.beamline.optical_elements.crystals.s4_plane_crystal import S4PlaneCrystalElement
beamline_element = S4NumericalMeshCrystalElement(optical_element=optical_element, coordinates=coordinates, input_beam=beam)
beam, footprint = 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')