13 #include <top/modules/OpticalGun/OpticalGunModule.h>
14 #include <top/geometry/TOPGeometryPar.h>
18 #include <framework/datastore/StoreArray.h>
21 #include <framework/gearbox/Unit.h>
22 #include <framework/logging/Logger.h>
27 #include <TRotation.h>
54 setDescription(
"Source of optical photons");
55 setPropertyFlags(c_ParallelProcessingCertified);
58 addParam(
"x", m_x,
"position in x [cm]", 0.0);
59 addParam(
"y", m_y,
"position in y [cm]", 0.0);
60 addParam(
"z", m_z,
"position in z [cm]", 0.0);
61 addParam(
"diameter", m_diameter,
"source diameter [cm]", 0.0);
62 addParam(
"minAlpha", m_minAlpha,
"source minimum emission angle [deg]. ", 0.0);
63 addParam(
"maxAlpha", m_maxAlpha,
"source maximum emission angle [deg]. ", 30.);
64 addParam(
"na", m_na,
"source numerical aperture. It is used only by the Gaussian distribution", 0.50);
65 addParam(
"angularDistribution", m_angularDistribution,
66 "source angular distribution: uniform, Lambertian, an arbitrary TFormula, or Gaussian (only one to use na and ignore m_minAlpha dn m_maxAlpha). If you are writing a TFormula, assume the angles are measured in degrees. The conversion to rad is done internally.",
68 addParam(
"wavelength", m_wavelength,
"wavelength of photons [nm]", 405.0);
69 addParam(
"phi", m_phi,
"first rotation angle (around z) [deg]", 0.0);
70 addParam(
"theta", m_theta,
"second rotation angle (around x) [deg]", 0.0);
71 addParam(
"psi", m_psi,
"third rotation angle (around z) [deg]", 0.0);
72 addParam(
"startTime", m_startTime,
"start time [ns]", 0.0);
73 addParam(
"pulseWidth", m_pulseWidth,
"pulse duration (Gaussian sigma) [ns]", 0.0);
74 addParam(
"numPhotons", m_numPhotons,
75 "average number of photons per pulse, if positive, otherwise exactly one",
77 addParam(
"slotID", m_slotID,
78 "TOP slot ID (1-16): if valid, source position and rotation angles assumed to be given in a local bar frame, otherwise Belle II frame assumed",
80 addParam(
"slitDX", m_slitDX,
"slit size in x [cm], if positive, otherwise full open",
82 addParam(
"slitDY", m_slitDY,
"slit size in y [cm], if positive, otherwise full open",
84 addParam(
"slitX0", m_slitX0,
"slit x-offset in respect to source [cm] ", 0.0);
85 addParam(
"slitY0", m_slitY0,
"slit y-offset in respect to source [cm] ", 0.0);
86 addParam(
"slitZ", m_slitZ,
"slit distance to source [cm], if > 0.01, otherwise none",
90 OpticalGunModule::~OpticalGunModule()
92 if (m_customDistribution)
delete m_customDistribution;
95 void OpticalGunModule::initialize()
98 m_MCParticles.registerInDataStore();
99 m_simCalPulses.isOptional();
102 if (m_wavelength < 150 or m_wavelength > 1000)
103 B2FATAL(
"Wavelength does not correspond to optical photons.");
104 if (m_na < 0 or m_na > 1)
105 B2FATAL(
"Numerical aperture must be between 0 and 1.");
107 B2FATAL(
"Minimum emission angle must be positive");
109 B2FATAL(
"Maximum emission angle must be positive");
110 if (m_minAlpha >= m_maxAlpha)
111 B2FATAL(
"Minimum emission angle msut me smaller than the maximum emission angle");
114 if (m_angularDistribution ==
string(
"uniform") or
115 m_angularDistribution ==
string(
"Lambertian") or
116 m_angularDistribution ==
string(
"Gaussian"))
117 B2INFO(
"Using the pre-defined angular distribution " << m_angularDistribution);
119 B2INFO(m_angularDistribution <<
" is not a pre-defined distribution. Checking if it's a valid, positively-defined TFormula.");
120 TFormula testFormula(
"testFormula", m_angularDistribution.c_str());
121 int result = testFormula.Compile();
123 B2FATAL(m_angularDistribution <<
" is not a valid angular distribution keyword, or it's a TFormula that does not compile.");
125 double testPoint = m_minAlpha;
126 while (testPoint < m_maxAlpha) {
127 double value = testFormula.Eval(testPoint * Unit::deg);
129 B2FATAL(
"The formula " << m_angularDistribution <<
" is not positively defined in the test point " << testPoint <<
" deg (value = "
133 testPoint += (m_maxAlpha - m_minAlpha) / 100.;
135 m_customDistribution =
new TF1(
"m_customDistribution", m_angularDistribution.c_str(), m_minAlpha * Unit::deg,
136 m_maxAlpha * Unit::deg);
142 m_cosMinAlpha = cos(m_minAlpha * Unit::deg);
143 m_cosMaxAlpha = cos(m_maxAlpha * Unit::deg);
144 m_energy = TOPGeometryPar::c_hc / m_wavelength * Unit::eV;
150 const auto* geo = TOPGeometryPar::Instance()->getGeometry();
151 int Nbars = geo->getNumModules();
152 if (Nbars == 0) B2ERROR(
"TOP bars are not defined");
153 if (m_slotID < 0 or m_slotID > Nbars) {
154 B2ERROR(
"barID = " << m_slotID <<
" : not a valid ID");
157 barY0 = geo->getModule(m_slotID).getRadius();
158 barZ0 = geo->getModule(m_slotID).getZc();
159 barPhi = geo->getModule(m_slotID).getPhi() - M_PI / 2;
162 m_translate.SetXYZ(m_x, m_y + barY0, m_z + barZ0);
163 m_rotate.RotateXEulerAngles(m_phi * Unit::deg,
166 m_rotateBar.RotateZ(barPhi);
170 void OpticalGunModule::beginRun()
174 void OpticalGunModule::event()
179 if (m_numPhotons > 0) numPhotons = gRandom->Poisson(m_numPhotons);
182 double startTime = m_startTime;
183 if (m_simCalPulses.getEntries() > 0) {
184 startTime += m_simCalPulses[0]->getTime();
188 for (
int i = 0; i < numPhotons; i++) {
193 x = m_diameter * (gRandom->Rndm() - 0.5);
194 y = m_diameter * (gRandom->Rndm() - 0.5);
195 }
while (x * x + y * y > m_diameter * m_diameter / 4.0);
196 TVector3 point(x, y, 0);
200 if (m_angularDistribution ==
string(
"uniform")) {
201 direction = getDirectionUniform();
202 }
else if (m_angularDistribution ==
string(
"Lambertian")) {
203 direction = getDirectionLambertian();
204 }
else if (m_angularDistribution ==
string(
"Gaussian")) {
205 direction = getDirectionGaussian();
207 direction = getDirectionCustom();
209 TVector3 momentum = m_energy * direction;
212 if (!isInsideSlit(point, direction))
continue;
215 double alphaPol = 2.0 * M_PI * gRandom->Rndm();
216 TVector3 polarization(cos(alphaPol), sin(alphaPol), 0);
217 polarization.RotateUz(direction);
220 double emissionTime = gRandom->Gaus(startTime, m_pulseWidth);
223 momentum.Transform(m_rotate);
224 polarization.Transform(m_rotate);
225 point.Transform(m_rotate);
226 point += m_translate;
228 momentum.Transform(m_rotateBar);
229 polarization.Transform(m_rotateBar);
230 point.Transform(m_rotateBar);
234 auto* part = m_MCParticles.appendNew();
237 part->setStatus(MCParticle::c_PrimaryParticle);
238 part->addStatus(MCParticle::c_StableInGenerator);
239 part->setProductionVertex(point);
240 part->setProductionTime(emissionTime);
241 part->setMomentum(momentum);
242 part->setEnergy(m_energy);
243 part->setDecayVertex(polarization);
249 void OpticalGunModule::endRun()
253 void OpticalGunModule::terminate()
257 void OpticalGunModule::printModuleParams()
const
261 bool OpticalGunModule::isInsideSlit(
const TVector3& point,
262 const TVector3& direction)
const
264 if (m_slitZ < 0.01)
return true;
265 if (direction.Z() < 1.0e-6)
return false;
267 double pathLength = (m_slitZ - point.Z()) / direction.Z();
269 double x = point.X() + pathLength * direction.X();
270 if (abs(x - m_slitX0) > m_slitDX / 2.0)
return false;
273 double y = point.Y() + pathLength * direction.Y();
274 if (abs(y - m_slitY0) > m_slitDY / 2.0)
return false;
280 TVector3 OpticalGunModule::getDirectionGaussian()
const
288 x = gRandom->Gaus(0., asin(m_na) / 2.45);
289 y = gRandom->Gaus(0., asin(m_na) / 2.45);
290 z = 1. - x * x - y * y;
292 return TVector3(x, y, sqrt(z));
295 TVector3 OpticalGunModule::getDirectionUniform()
const
297 double cosTheta = (m_cosMinAlpha - m_cosMaxAlpha) * gRandom->Rndm() + m_cosMaxAlpha;
298 double sinTheta = sqrt(1.0 - cosTheta * cosTheta);
299 double phi = 2.0 * M_PI * gRandom->Rndm();
300 return TVector3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
303 TVector3 OpticalGunModule::getDirectionLambertian()
const
305 double cosTheta = sqrt((m_cosMinAlpha * m_cosMinAlpha - m_cosMaxAlpha * m_cosMaxAlpha) * gRandom->Rndm() +
306 m_cosMaxAlpha * m_cosMaxAlpha);
307 double sinTheta = sqrt(1.0 - cosTheta * cosTheta);
308 double phi = 2.0 * M_PI * gRandom->Rndm();
309 return TVector3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
312 TVector3 OpticalGunModule::getDirectionCustom()
const
314 double alpha = m_customDistribution->GetRandom();
315 double phi = 2.0 * M_PI * gRandom->Rndm();
316 return TVector3(cos(phi) * sin(alpha), sin(phi) * sin(alpha), cos(alpha));