-
Notifications
You must be signed in to change notification settings - Fork 4
Description
In shadow you can define geometrical sources with angular distribution flat or unform.
In the old shadow manual https://github.com/srio/shadow3-docs/blob/master/doc/shadow-source.pdf you can read:
Flat Angle Distribution: The source angle distribution dictates how the rays in momentum space will be generated. If GRID is chosen as the model in momentum space the user must supply the number of points in the X and Z directions. In Flat angle distribution, the rays have an equal probability of being assigned any angle within the user specified interval of horizontal and vertical half-divergences (in radians). This source will not illuminate uniformly a sphere centered on it, but rather a plane.
Uniform Distribution: With a uniform distribution, the intensity of the source incident on a plane varies as the cosine of the angle of the ray with respect to the optical axis, following Lambert’s law. This means a uniform brightness source, where the flux flowing into a solid angle dW from an area dA is independent of the observation angle. In general, dN / dq = cos q. For the case of an isotropic source, see the “Conical Source” case.
In shadow3 there is no difference in the output produced for these two cases: (https://github.com/oasys-kit/shadow3/blob/master/src/fortran/shadow_kernel.f90) and the Flat is simulated as Uniform (I am not sure about shadow2, to be checked!)
! C
! C Uniform distribution ( Isotrope emitter ) and cosine source
! C
! C Distinction not ready yet. Not important for small apertures
! C
In Shadow4 we make the difference:
In Flat, we sample with uniform distribution in the input values (presented as angles, but in reality are vx, vz director cosines)
return numpy.random.random(N) * (self._x_max-self._x_min) + self._x_min
For uniform we use
# ! C Uniform distribution ( Isotrope emitter )
XMAX1 = numpy.tan(self._h_min)
XMAX2 = numpy.tan(self._h_max)
ZMAX1 = numpy.tan(self._v_min)
ZMAX2 = numpy.tan(self._v_max)
XRAND = numpy.random.random(N) * (XMAX1 - XMAX2) + XMAX2
ZRAND = numpy.random.random(N) * (ZMAX1 - ZMAX2) + ZMAX2
THETAR = numpy.arctan(numpy.sqrt(XRAND**2 + ZRAND**2))
PHIR = numpy.arctan2(ZRAND, XRAND)
DIREC1 = numpy.cos(PHIR) * numpy.sin(THETAR)
DIREC3 = numpy.sin(PHIR) * numpy.sin(THETAR)
therefore here the limits are changed!
For small angles, both Flat and Uniform are very similar. For not-so-small angles, prefer Uniform. For an isotropic emitter, use Cone.
Therefore, Shadow3 and Shadow4 are equivalent for Uniform sources, but not for Flat.
You can use this code to compare the Uniform:
#
#
#
from shadow4.sources.source_geometrical.source_geometrical import SourceGeometrical
light_source = SourceGeometrical(name='SourceGeometrical', nrays=5000, seed=5676561)
light_source.set_spatial_type_point()
light_source.set_depth_distribution_off()
light_source.set_angular_distribution_uniform(hdiv1=0.200000,hdiv2=0.200000,vdiv1=0.100000,vdiv2=0.100000)
light_source.set_energy_distribution_singleline(1000.000000, unit='eV')
light_source.set_polarization(polarization_degree=1.000000, phase_diff=0.000000, coherent_beam=0)
beam = light_source.get_beam()
# test plot
from srxraylib.plot.gol import plot_scatter
rays = beam.get_rays()
# plot_scatter(1e6 * rays[:, 0], 1e6 * rays[:, 2], title='(X,Z) in microns')
xpS4 = beam.get_column(4)
zpS4 = beam.get_column(6)
print(xpS4, zpS4)
print(xpS4.min(),xpS4.max(), xpS4)
print(zpS4.min(),zpS4.max(), zpS4)
###########################################
#
# Python script to run shadow3. Created automatically with ShadowTools.make_python_script_from_list().
#
import Shadow
import numpy
# write (1) or not (0) SHADOW files start.xx end.xx star.xx
iwrite = 0
#
# initialize shadow3 source (oe0) and beam
#
beam = Shadow.Beam()
oe0 = Shadow.Source()
#
# Define variables. See meaning of variables in:
# https://raw.githubusercontent.com/srio/shadow3/master/docs/source.nml
# https://raw.githubusercontent.com/srio/shadow3/master/docs/oe.nml
#
oe0.FDISTR = 2 # flat=1, uniform=2
oe0.FSOUR = 0
oe0.F_PHOT = 0
oe0.HDIV1 = 0.2
oe0.HDIV2 = -0.2
oe0.IDO_VX = 0
oe0.IDO_VZ = 0
oe0.IDO_X_S = 0
oe0.IDO_Y_S = 0
oe0.IDO_Z_S = 0
oe0.ISTAR1 = 5676561
oe0.PH1 = 1000.0
oe0.VDIV1 = 0.1
oe0.VDIV2 = -0.1
# Run SHADOW to create the source
if iwrite:
oe0.write("start.00")
beam.genSource(oe0)
if iwrite:
oe0.write("end.00")
beam.write("begin.dat")
# Shadow.ShadowTools.plotxy(beam, 1, 3, nbins=101, nolost=1, title="Real space")
# Shadow.ShadowTools.plotxy(beam,1,4,nbins=101,nolost=1,title="Phase space X")
# Shadow.ShadowTools.plotxy(beam,3,6,nbins=101,nolost=1,title="Phase space Z")
###################################
xp = beam.getshonecol(4)
zp = beam.getshonecol(6)
print("S3: ", xp, zp)
print("S3: ", xp.min(),xp.max(), xp)
print("S3: ", zp.min(),zp.max(), zp)
print("S4: ",xpS4, zp)
print("S4: ",xpS4.min(),xpS4.max(), xpS4)
print("S4: ",zpS4.min(),zpS4.max(), zpS4)
#S3: [0.1977157 0.1977157 0.1977157 ... 0.1977157 0.1977157 0.1977157] [0.09786265 0.09786265 0.09786265 ... 0.09786265 0.09786265 0.09786265]
#S3: 0.19771570429323465 0.19771570429323465 [0.1977157 0.1977157 0.1977157 ... 0.1977157 0.1977157 0.1977157]
#S3: 0.0978626455598292 0.0978626455598292 [0.09786265 0.09786265 0.09786265 ... 0.09786265 0.09786265 0.09786265]
#S4: [0.1977157 0.1977157 0.1977157 ... 0.1977157 0.1977157 0.1977157] [0.09786265 0.09786265 0.09786265 ... 0.09786265 0.09786265 0.09786265]
#S4: 0.19771570429323465 0.19771570429323465 [0.1977157 0.1977157 0.1977157 ... 0.1977157 0.1977157 0.1977157]
#S4: 0.09786264555982918 0.09786264555982918 [0.09786265 0.09786265 0.09786265 ... 0.09786265 0.09786265 0.09786265]