"""
Grid source defined in polar coordinates.
"""
import numpy
from shadow4.beam.s4_beam import S4Beam
from shadow4.sources.s4_light_source_base import S4LightSourceBase
from shadow4.tools.arrayofvectors import vector_default_efields
[docs]class SourceGridPolar(S4LightSourceBase):
def __init__(self,
real_space_width=[1e-6,0,1e-6],
direction_space_width=[1e-6,1e-6],
real_space_points=[100,36],
direction_space_points=[1,1],
real_space_center=[0,0,0],
direction_space_center=[0,1,0],
name="Undefined",
nrays=0, # not used
seed=0, # not used
wavelength=1e-10,
polarization_degree=1.0,
polarization_phase_deg=0.0,
coherent_beam=1,
):
"""
Defines a grid source, so points starting in a ellipsoid-like volume in real space and angularly gridded in X,Z.
Parameters
----------
real_space_width : list, optional
The widths of the real_space_width volume [2a,2b,2c] of the ellipsoid.
direction_space_width : list, optional
The "angular" aperture [Dx',Dz'].
real_space_points : list, optional
Number of points [Nradial,Nangular].
direction_space_points : list, optional
Number of points [Nradial',Nangular'].
real_space_center : list, optional
Center cartesian coordinates in real space [Xc,Yc,Zc].
direction_space_center : list, optional
Center coordinates in divergence space [X'_c,Z'_c]. Note that (X')^2+(Z')^2 < 1.
name : str, optional
A name.
nrays : int, optional
Number of rays generated using SourceGaussian.get_beam()
seed : int, optional
Seed for the Monte Carlo generator.
wavelength : float, optional
The photon wavelength in m.
polarization_degree : float
The polarization degree (cos_s / (cos_s + cos_p).
polarization_phase_deg : float, optional
The polarization phase in degrees (0=linear).
coherent_beam : int, optional
(0) random (incoherent), or (1) constant (coherent) s-phases.
"""
super().__init__(name=name, nrays=nrays, seed=seed)
if real_space_width[1] != 0:
raise Exception("Finite source depth not yet implemented!") # TODO: implement it!
self._real_space_width = real_space_width
self._direction_space_width = direction_space_width
self._real_space_points = real_space_points
self._direction_space_points = direction_space_points
self._real_space_center = real_space_center
self._direction_space_center = direction_space_center
self._wavelength = wavelength
self._polarization_degree = polarization_degree
self._polarization_phase_deg = polarization_phase_deg
self._coherent_beam = coherent_beam
[docs] @classmethod
def initialize_point_source(cls,
real_space_center=[0.0, 0.0, 0.0],
direction_space_width=[1e-6,1e-6],
direction_space_points=[5,36],
direction_space_center=[0.0,0.0],
):
"""
Initializes a point source.
Parameters
----------
direction_space_width : list, optional
The "angular" aperture [Dx',Dz'].
direction_space_points : list, optional
Number of points [Nradial', Nangular'].
real_space_center : list, optional
Center cartesian coordinates in real space [Xc,Yc,Zc].
direction_space_center : list, optional
Center coordinates in divergence space [X'_c,Z'_c]. Note that (X')^2+(Z')^2 < 1.
Returns
-------
instance of SourceGridPolar.
"""
return SourceGridPolar(real_space_width=[0,0,0],
direction_space_width=direction_space_width,
real_space_points=[1,1,1],
direction_space_points=direction_space_points,
real_space_center=real_space_center,
direction_space_center=direction_space_center,
)
[docs] @classmethod
def initialize_collimated_source(cls,
real_space_width=[1e-6,0.0,1e-6],
real_space_points=[100,36],
real_space_center=[0.0,0.0,0.0],
direction_space_center=[0.0,0.0],
):
"""
Parameters
----------
real_space_width : list, optional
The widths of the real_space_width volume [2a, 2b, 2c] of the ellipsoid.
real_space_points : list, optional
Number of points [Nradial, Nangular].
real_space_center : list, optional
Center cartesian coordinates in real space [Xc, Yc, Zc].
direction_space_center : list, optional
Center coordinates in divergence space [X'_c,Z'_c]. Note that (X')^2+(Z')^2 < 1.
Returns
-------
instance of SourceGridPolar.
"""
return SourceGridPolar(real_space_width=real_space_width,
direction_space_width=[0.0,0.0],
real_space_points=real_space_points,
direction_space_points=[1,1],
real_space_center=real_space_center,
direction_space_center=direction_space_center,)
#
# getters
#
[docs] def get_number_of_points_real_space(self):
"""
Returns the number of points in real space.
Returns
-------
int
"""
return self._real_space_points[0] * self._real_space_points[1]
[docs] def get_number_of_points_direction_space(self):
"""
Returns the number of points in direction space.
Returns
-------
int
"""
return self._direction_space_points[0] * self._direction_space_points[1]
[docs] def get_number_of_points(self):
"""
Returns the total number of points.
Returns
-------
int
"""
return self.get_number_of_points_real_space() * self.get_number_of_points_direction_space()
def _get_arrays_real_space(self):
# Returns three arrays with the spatial coordinates 1D arrays
radial_ratio = numpy.linspace(0, 1, self._real_space_points[0] )
angular = numpy.arange(self._real_space_points[1]) / self._real_space_points[1]
angular *= 2 * numpy.pi
npoints = self.get_number_of_points_real_space()
x = numpy.zeros(npoints)
z = numpy.zeros(npoints)
i = -1
for radius_ratio in radial_ratio:
for angle in angular:
i += 1
x[i] = self._real_space_width[0]/2 * radius_ratio * numpy.cos(angle)
z[i] = self._real_space_width[2]/2 * radius_ratio * numpy.sin(angle)
y = numpy.zeros_like(x)
return x,y,z
def _get_arrays_direction_space(self):
# Returns two arrays with the direction angles (in fact components of the direction vector)
radial_ratio = numpy.linspace(0, 1, self._direction_space_points[0] )
angular = numpy.arange(self._direction_space_points[1]) / self._direction_space_points[1]
angular *= 2 * numpy.pi
npoints = self.get_number_of_points_direction_space() # self._real_space_points[0] * self._real_space_points[1]
vx = numpy.zeros(npoints)
vz = numpy.zeros(npoints)
i = -1
for radius_ratio in radial_ratio:
for angle in angular:
i += 1
vx[i] = self._direction_space_width[0]/2 * radius_ratio * numpy.cos(angle)
vz[i] = self._direction_space_width[0]/2 * radius_ratio * numpy.sin(angle)
return vx,vz
return numpy.array([0]), numpy.array([0])
def _get_volume(self):
# Returns an array (6,npoints) with x,y,z,xp,yp,zp (first index 0,1,2,3,4,5 respectively) with the
# spatial and direction coordinates
X, Y, Z = self._get_arrays_real_space()
VX, VZ = self._get_arrays_direction_space()
npoint = self.get_number_of_points()
V1x = numpy.zeros(npoint)
V1y = numpy.zeros(npoint)
V1z = numpy.zeros(npoint)
V2x = numpy.zeros(npoint)
V2z = numpy.zeros(npoint)
V2y = numpy.zeros(npoint)
ij = -1
for i in range(self.get_number_of_points_real_space()):
for j in range(self.get_number_of_points_direction_space()):
ij += 1
V1x[ij] = X[i]
V1y[ij] = Y[i]
V1z[ij] = Z[i]
V2x[ij] = VX[j]
V2z[ij] = VZ[j]
V1x += self._real_space_center[0]
V1y += self._real_space_center[1]
V1z += self._real_space_center[2]
V2x += self._direction_space_center[0]
V2z += self._direction_space_center[1]
try:
V2y = numpy.sqrt(1 - V2x ** 2 - V2z ** 2)
except:
raise Exception('Failed to normalize directions. Try smalled angular width and/or angular center.')
return numpy.vstack((V1x,V1y,V1z,V2x,V2y,V2z))
#
# info
#
[docs] def get_info(self):
"""
Returns an array of strings with info.
Returns
-------
str
"""
txt = ""
txt += "Number of points: Nreal_space: %d, Ndirection_space: %d, Total: %d \n"%(
self.get_number_of_points_real_space(),
self.get_number_of_points_direction_space(),
self.get_number_of_points())
txt += "Gridding in real space: %d, %d\n"%(self._real_space_points[0],
self._real_space_points[1])
txt += "Gridding in direction space: %d, %d \n"%(self._direction_space_points[0],
self._direction_space_points[1])
txt += "\n"
txt += "real_space widths"+repr(self._real_space_width) + "\n"
txt += "direction_space widths "+repr(self._direction_space_width) + "\n"
txt += "real_space_points "+repr(self._real_space_points) + "\n"
txt += "direction_space_points "+repr(self._direction_space_points) + "\n"
txt += "real_space_center "+repr(self._real_space_center) + "\n"
txt += "direction_space_center "+repr(self._direction_space_center) + "\n"
txt += "\nWavelength = %g m" % self._wavelength
txt += "Degree of polarization is %f. Angular difference in phase is %f\n" % \
(self._polarization_degree, self._polarization_phase_deg)
return txt
[docs] def get_beam(self):
"""
Returns an instance of S4Beam with the sampled rays.
Returns
-------
instance of S4Beam
"""
N = self.get_number_of_points()
rays = numpy.zeros((N, 18))
volume = self._get_volume()
rays[:, 0] = volume[0, :]
rays[:, 1] = volume[1, :]
rays[:, 2] = volume[2, :]
rays[:, 3] = volume[3, :]
rays[:, 4] = volume[4, :]
rays[:, 5] = volume[5, :]
rays[:,9] = 1 # flag
rays[:,10] = 2 * numpy.pi / (self._wavelength * 1e2) # wavenumber in cm**-1
rays[:,11] = numpy.arange(self.get_number_of_points(),dtype=float) # index
if not self._coherent_beam:
rays[:, 13] = numpy.random.random(N) * 2 * numpy.pi # Phase s
rays[:, 14] = rays[:, 13] + numpy.radians(self._polarization_phase_deg) # Phase p
DIREC = rays[:, 3:6]
A_VEC, AP_VEC = vector_default_efields(DIREC, pol_deg=self._polarization_degree)
rays[:, 6:9] = A_VEC
rays[:, 15:18] = AP_VEC
return S4Beam.initialize_from_array(rays)
[docs] def to_python_code(self):
"""
Returns the python code to recreate the grid source.
Returns
-------
str
The python code.
"""
txt = ""
txt += "\n#\n#\n#"
txt += "\nfrom shadow4.sources.source_geometrical.source_grid_polar import SourceGridPolar"
txt += "\nlight_source = SourceGridPolar(name='%s', " % (self.get_name())
txt += "\n real_space_width = [%f, %f, %f]," % (tuple(self._real_space_width))
txt += "\n real_space_center = [%f, %f, %f]," % (tuple(self._real_space_center))
txt += "\n real_space_points = [%d, %d]," % (tuple(self._real_space_points))
txt += "\n direction_space_width = [%f, %f]," % (tuple(self._direction_space_width))
txt += "\n direction_space_center = [%f, %f]," % (tuple(self._direction_space_center))
txt += "\n direction_space_points = [%d, %d]," % (tuple(self._direction_space_points))
txt += "\n wavelength = %g," % self._wavelength
txt += "\n polarization_degree = %g," % self._polarization_degree
txt += "\n polarization_phase_deg = %g," % self._polarization_phase_deg
txt += "\n coherent_beam = %d)" % self._coherent_beam
txt += "\nbeam = light_source.get_beam()"
return txt
if __name__ == "__main__":
from srxraylib.plot.gol import plot_scatter
if True:
a = SourceGridPolar.initialize_point_source(
direction_space_width = [2e-3,2e-3],
direction_space_points = [20, 5],
direction_space_center = [0.0, 0.0] )
print(a.get_info())
beam = a.get_beam()
plot_scatter(beam.get_column(4) * 1e6, beam.get_column(6) * 1e6, title="Polar grid in direction space")
print("check orthogonality", beam.efields_orthogonal())
#
#
if False:
a = SourceGridPolar.initialize_collimated_source(
real_space_width=[2e-6, 0.0, 1e-6],
real_space_points=[10, 4],
real_space_center=[0.0, 0.0, 0.0]
)
print(a.get_info())
beam = a.get_beam()
plot_scatter(beam.get_column(1)*1e6, beam.get_column(3)*1e6, xrange=[-1,1], yrange=[-1,1],
title="Polar grid in real space")
if False:
a = SourceGridPolar(
real_space_width=[1e-6, 0.0, 1e-6],
real_space_points=[2, 4],
real_space_center=[0.0, 0.0, 0.0],
direction_space_width=[2e-6, 2e-6],
direction_space_points=[5, 5],
direction_space_center=[0.0, 0.0])
print(a.get_info())
beam = a.get_beam()
plot_scatter(beam.get_column(1)*1e6, beam.get_column(3)*1e6, xrange=[-1,1], yrange=[-1,1],
title="Real space")
plot_scatter(beam.get_column(4)*1e6, beam.get_column(6)*1e6, xrange=[-1,1], yrange=[-1,1],
title="Directions space")
plot_scatter(beam.get_column(1)*1e6, beam.get_column(4)*1e6, xrange=[-1,1], yrange=[-1,1],
title="Phase space X")
plot_scatter(beam.get_column(3)*1e6, beam.get_column(6)*1e6, xrange=[-1,1], yrange=[-1,1],
title="Phase space Z")
if False:
a = SourceGridPolar(
real_space_width=[2e-3, 0.0, 2e-3],
real_space_points=[2, 8],
real_space_center=[0.0, 0.0, 0.0],
direction_space_width=[20e-3, 20e-3],
direction_space_points=[3, 359],
direction_space_center=[0.0, 0.0])
print(a.get_info())
beam = a.get_beam()
plot_scatter(beam.get_column(1)*1e6, beam.get_column(3)*1e6, xrange=[-1.1e3,1.1e3], yrange=[-1.1e3,1.1e3], title="Real space")
plot_scatter(beam.get_column(4)*1e6, beam.get_column(6)*1e6, xrange=[-11e3,11e3], yrange=[-11e3,11e3], title="Directions space")
plot_scatter(beam.get_column(1)*1e6, beam.get_column(4)*1e6, xrange=[-1.1e3,1.1e3], yrange=[-11e3,11e3], title="Phase space X")
plot_scatter(beam.get_column(3)*1e6, beam.get_column(6)*1e6, xrange=[-1.1e3,1.1e3], yrange=[-11e3,11e3], title="Phase space Z")
if False:
a = SourceGridPolar(
real_space_width=[2e-3, 0.0, 2e-3],
real_space_points=[2, 8],
real_space_center=[0.0, 0.0, 0.0],
direction_space_width=[20e-3, 20e-3],
direction_space_points=[3, 359],
direction_space_center=[0.0, 0.0])
print(a.to_python_code())