"""
Grid source defined in cartesian 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 SourceGridCartesian(S4LightSourceBase):
"""
Defines a grid source, so points starting in a cube-like volume in real space and directions gridded in X,Z
Parameters
----------
real_space_width : list, optional
the widths of the real_space volume (parallellepipedal) [Dx,Dy,Dz].
direction_space_width : list, optional
The "angular" aperture [Dx',Dz'].
real_space_points : list, optional
Number of points [Nx,Ny,Nz].
direction_space_points : list, optional
Number of points [Nx',Nz']
real_space_center : list, optional
Center coordinates in real space [Cx,Cy,Cz].
direction_space_center : list, optional
Center coordinates in divergence space [Cx',Cz']. Note that (Cx')^2+(Cz')^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.
"""
def __init__(self,
real_space_width=[1e-3,1e-3,1e-3],
direction_space_width=[0,0],
real_space_points=[10,10,10],
direction_space_points=[1,1],
real_space_center=[0,0,0],
direction_space_center=[0,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,
):
super().__init__(name=name, nrays=nrays, seed=seed)
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
# support text containg name of variable, help text and unit. Will be stored in self._support_dictionary
self._set_support_text([
("real_space_width", "the widths of the real_space volume (parallellepipedal) [Dx,Dy,Dz].", ""),
("direction_space_width", "The angular aperture [Dx',Dz'].", ""),
("real_space_points", "Number of points [Nx,Ny,Nz].", ""),
("direction_space_points", "Number of points [Nx',Nz']", ""),
("real_space_center", "Center coordinates in real space [Cx,Cy,Cz].", ""),
("direction_space_center",
"Center coordinates in divergence space [Cx',Cz']. Note that (Cx')^2+(Cz')^2 < 1.", ""),
("name", "the name", ""),
("nrays", "Number of rays generated using SourceGaussian.get_beam()", ""),
("seed", "Seed for the Monte Carlo generator.", ""),
("wavelength", "The photon wavelength in m.", ""),
("polarization_degree", "The polarization degree (cos_s / (cos_s + cos_p).", ""),
("polarization_phase_deg", "The polarization phase in degrees (0=linear).", ""),
("coherent_beam", "Random(incoherent) (0) or constant (coherent) s-phases.", ""),
] )
[docs] @classmethod
def initialize_point_source(cls,
direction_space_width=[1e-6,1e-6],
direction_space_points=[100,100],
direction_space_center=[0.0,0.0] ):
"""
Initializes a point source (zero size).
Parameters
----------
direction_space_width : list, optional
Interval for the direction space [Xwidth, Zwidth].
direction_space_points : list, optional
Number of points for the direction space [Nx, Nz].
direction_space_center : list, optional
Center for the direction space [Xcenter, Zcenter].
Returns
-------
instance of SourceGridCartesian.
"""
return SourceGridCartesian(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=[0.0,0.0,0.0],
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,1,100],
real_space_center=[0.0,0.0,0.0] ):
"""
Initializes a collimated source (zero divergence).
Parameters
----------
real_space_width : list, optional
real_space_width [Xwidth, Ywidth, Zwidth].
real_space_points : list, optional
real_space_points [Nx, Ny, Nz].
real_space_center : list, optional
real_space_points [Xcenter, Ycenter, Zcenter].
Returns
-------
"""
return SourceGridCartesian(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=[0.0,0.0],)
#
# getters
#
[docs] def get_number_of_points(self):
"""
Returns the total number of points or rays.
Returns
-------
int
"""
return self._real_space_points[0] * self._real_space_points[1] * self._real_space_points[2]* \
self._direction_space_points[0] * self._direction_space_points[1]
[docs] def get_arrays_real_space(self):
"""
Returns three arrays with the sampled spatial coordinates.
Returns
-------
tuple
(x, y, z).
"""
if self._real_space_points[0] <= 1:
x = numpy.array([self._real_space_center[0]])
else:
x = numpy.linspace(-0.5*self._real_space_width[0],
0.5*self._real_space_width[0],
self._real_space_points[0]) + self._real_space_center[0]
if self._real_space_points[1] <= 1:
y = numpy.array([self._real_space_center[1]])
else:
y = numpy.linspace(-0.5*self._real_space[1],
0.5*self._real_space[1],
self._real_space_points[1]) + self._real_space_center[1]
if self._real_space_points[2] <= 1:
z = numpy.array([self._real_space_center[2]])
else:
z = numpy.linspace(-0.5*self._real_space_width[2],
0.5*self._real_space_width[2],
self._real_space_points[2]) + self._real_space_center[2]
return x,y,z
[docs] def get_arrays_direction_space(self):
"""
Returns two arrays with the sampled angles (in fact, the components of the direction vector).
Returns
-------
tuple
(x', z')
"""
if self._direction_space_points[0] <= 1:
x = numpy.array([self._direction_space_center[0]])
else:
hdiv1 = 0.5*self._direction_space_width[0]
hdiv2 = -0.5*self._direction_space_width[0]
xmax1 = numpy.tan(hdiv1)
xmax2 = numpy.tan(hdiv2)
x = numpy.linspace(0,1,self._direction_space_points[0])
x = x * (xmax1 - xmax2) + xmax2 + self._direction_space_center[0]
if self._direction_space_points[1] <= 1:
y = numpy.array([self._direction_space_center[1]])
else:
vdiv1 = 0.5*self._direction_space_width[1]
vdiv2 = -0.5*self._direction_space_width[1]
ymax1 = numpy.tan(vdiv1)
ymax2 = numpy.tan(vdiv2)
y = numpy.linspace(0,1,self._direction_space_points[1])
y = y * (ymax1 - ymax2) + ymax2 + self._direction_space_center[1]
return x,y
[docs] def get_mesh_divergences(self):
"""
Returns two mesh arrays (Nx, Nz) with the Xp and Zp values.
Returns
-------
tuple
(X',Z')
"""
xp,zp = self.get_arrays_direction_space()
XP = numpy.array(numpy.outer(xp,numpy.ones_like(zp)))
YP = numpy.array(numpy.outer(numpy.ones_like(xp),zp))
thetar = numpy.arctan(numpy.sqrt(XP*XP + YP*YP))
phir = numpy.arctan2(YP,XP)
return numpy.cos(phir) * numpy.sin(thetar), numpy.sin(phir) * numpy.sin(thetar)
[docs] def get_mesh_real_space(self):
"""
Returns two mesh arrays with the spatial cross section coordinates X,Z.
Returns
-------
tuple
(x,z)
"""
x,y,z = self.get_arrays_real_space()
return numpy.array(numpy.outer(x,numpy.ones_like(z))), \
numpy.array(numpy.outer(numpy.ones_like(x),z))
[docs] def get_volume_divergences(self):
"""
Returns an array (3,npoints) with xp,yp,zp (first index 0,1,2, respectively) with the direction vectors.
Returns
-------
numpy array
"""
XP,ZP = self.get_mesh_divergences()
YP = numpy.sqrt(numpy.ones_like(XP) -XP**2 -ZP**2 )
tmp = numpy.vstack((XP.flatten(),YP.flatten(),ZP.flatten()))
return tmp
[docs] def get_volume_real_space(self):
"""
Returns an array (3,npoints) with x,y,z (first index 0,1,2, respectively) with the spatial coordinates.
Returns
-------
numpy array
"""
x,y,z = self.get_arrays_real_space()
x.flatten()
y.flatten()
z.flatten()
X = numpy.outer(x,numpy.ones_like(y))
Y = numpy.outer(numpy.ones_like(x),y)
X.flatten()
Y.flatten()
XX = numpy.outer(X,numpy.ones_like(z))
YY = numpy.outer(Y,numpy.ones_like(z))
ZZ = numpy.outer(numpy.ones_like(X),z)
return numpy.vstack((XX.flatten(),YY.flatten(),ZZ.flatten()))
[docs] 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.
Returns
-------
numpy array
"""
v1 = self.get_volume_real_space()
v2 = self.get_volume_divergences()
v1x = v1[0,:].copy().flatten()
v1y = v1[1,:].copy().flatten()
v1z = v1[2,:].copy().flatten()
v2x = v2[0,:].copy().flatten()
v2y = v2[1,:].copy().flatten()
v2z = v2[2,:].copy().flatten()
V1x = numpy.outer(v1x,numpy.ones_like(v2x)).flatten()
V1y = numpy.outer(v1y,numpy.ones_like(v2x)).flatten()
V1z = numpy.outer(v1z,numpy.ones_like(v2x)).flatten()
V2x = numpy.outer(numpy.ones_like(v1x),v2x).flatten()
V2y = numpy.outer(numpy.ones_like(v1x),v2y).flatten()
V2z = numpy.outer(numpy.ones_like(v1x),v2z).flatten()
return numpy.vstack((V1x,V1y,V1z,V2x,V2y,V2z))
# get_info
[docs] def get_info(self):
"""
Returns an array of strings with info.
Returns
-------
str
"""
txt = ""
txt += "Gridding in real space: %d, %d, %d \n"%(self._real_space_points[0],
self._real_space_points[1],
self._real_space_points[2])
txt += "Gridding in direction space: %d, %d \n"%(self._direction_space_points[0],
self._direction_space_points[1])
txt += "\n"
txt += "real_space_width "+repr(self._real_space_width) + "\n"
txt += "direction_space_width "+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))
rays[:, 0] = self.get_volume()[0, :]
rays[:, 1] = self.get_volume()[1, :]
rays[:, 2] = self.get_volume()[2, :]
rays[:, 3] = self.get_volume()[3, :]
rays[:, 4] = self.get_volume()[4, :]
rays[:, 5] = self.get_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_cartesian import SourceGridCartesian"
txt += "\nlight_source = SourceGridCartesian(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, %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
a = SourceGridCartesian.initialize_point_source(
direction_space_width = [2e-3,2e-3],
direction_space_points = [20, 20],
direction_space_center = [0.0, 0.0] )
print(a.get_info())
x,y,z = a.get_arrays_real_space()
print("x:",x)
print("y:",y)
print("z:",z)
xp,zp = a.get_arrays_direction_space()
XP,ZP = a.get_mesh_divergences()
print("XP ZP.shape",XP.shape, ZP.shape)
VP = a.get_volume_divergences()
print("VP",VP.shape,VP.size)
Vx = a.get_volume_real_space()
print("Vx: ",Vx.shape)
V = a.get_volume()
print("V: ",V.shape)
beam = a.get_beam()
plot_scatter(beam.get_column(4), beam.get_column(6), plot_histograms=0, title="Point source. Cols 4,6")
print("check orthogonality", beam.efields_orthogonal())
#
#
#
a = SourceGridCartesian.initialize_collimated_source(real_space_width=[10.,0.0,10.0],real_space_points=[100,1,100])
print(a.info()) # syned
print(a.get_info()) # local
beam = a.get_beam()
plot_scatter(beam.get_column(1), beam.get_column(3), plot_histograms=0, title="Collimated source. Cols 1,3")
print(a.to_python_code())