Belle II Software  release-06-00-14
OpticalGunModule.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 // Own include
10 #include <top/modules/OpticalGun/OpticalGunModule.h>
11 #include <top/geometry/TOPGeometryPar.h>
12 
13 
14 // framework - DataStore
15 #include <framework/datastore/StoreArray.h>
16 
17 // framework aux
18 #include <framework/gearbox/Unit.h>
19 #include <framework/logging/Logger.h>
20 
21 // ROOT
22 #include <TRandom.h>
23 #include <TVector3.h>
24 #include <TRotation.h>
25 #include <TFormula.h>
26 #include <TF1.h>
27 
28 using namespace std;
29 
30 namespace Belle2 {
36  using namespace TOP;
37 
38  //-----------------------------------------------------------------
39  // Register module
40  //-----------------------------------------------------------------
41 
42  REG_MODULE(OpticalGun)
43 
44  //-----------------------------------------------------------------
45  // Implementation
46  //-----------------------------------------------------------------
47 
49  {
50  // set module description
51  setDescription("Source of optical photons");
52  setPropertyFlags(c_ParallelProcessingCertified);
53 
54  // Add parameters
55  addParam("x", m_x, "position in x [cm]", 0.0);
56  addParam("y", m_y, "position in y [cm]", 0.0);
57  addParam("z", m_z, "position in z [cm]", 0.0);
58  addParam("diameter", m_diameter, "source diameter [cm]", 0.0);
59  addParam("minAlpha", m_minAlpha, "source minimum emission angle [deg]. ", 0.0);
60  addParam("maxAlpha", m_maxAlpha, "source maximum emission angle [deg]. ", 30.);
61  addParam("na", m_na, "source numerical aperture. It is used only by the Gaussian distribution", 0.50);
62  addParam("angularDistribution", m_angularDistribution,
63  "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.",
64  string("Gaussian"));
65  addParam("wavelength", m_wavelength, "wavelength of photons [nm]", 405.0);
66  addParam("phi", m_phi, "first rotation angle (around z) [deg]", 0.0);
67  addParam("theta", m_theta, "second rotation angle (around x) [deg]", 0.0);
68  addParam("psi", m_psi, "third rotation angle (around z) [deg]", 0.0);
69  addParam("startTime", m_startTime,
70  "start time [ns]. If TOPCalPulseGenerator is in path this is relative to the first cal pulse", 0.0);
71  addParam("pulseWidth", m_pulseWidth, "pulse duration (Gaussian sigma) [ns]", 0.0);
72  addParam("numPhotons", m_numPhotons,
73  "average number of photons per pulse, if positive, otherwise exactly one",
74  0.0);
75  addParam("slotID", m_slotID,
76  "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",
77  0);
78  addParam("slitDX", m_slitDX, "slit size in x [cm], if positive, otherwise full open",
79  0.0);
80  addParam("slitDY", m_slitDY, "slit size in y [cm], if positive, otherwise full open",
81  0.0);
82  addParam("slitX0", m_slitX0, "slit x-offset in respect to source [cm] ", 0.0);
83  addParam("slitY0", m_slitY0, "slit y-offset in respect to source [cm] ", 0.0);
84  addParam("slitZ", m_slitZ, "slit distance to source [cm], if > 0.01, otherwise none",
85  0.0);
86  }
87 
88  OpticalGunModule::~OpticalGunModule()
89  {
90  if (m_customDistribution) delete m_customDistribution;
91  }
92 
93  void OpticalGunModule::initialize()
94  {
95  // data store objects registration
96  m_MCParticles.registerInDataStore();
97  m_simCalPulses.isOptional();
98 
99  // parameters check
100  if (m_wavelength < 150 or m_wavelength > 1000)
101  B2FATAL("Wavelength does not correspond to optical photons.");
102  if (m_na < 0 or m_na > 1)
103  B2FATAL("Numerical aperture must be between 0 and 1.");
104  if (m_minAlpha < 0)
105  B2FATAL("Minimum emission angle must be positive");
106  if (m_maxAlpha < 0)
107  B2FATAL("Maximum emission angle must be positive");
108  if (m_minAlpha >= m_maxAlpha)
109  B2FATAL("Minimum emission angle msut me smaller than the maximum emission angle");
110 
111 
112  if (m_angularDistribution == string("uniform") or
113  m_angularDistribution == string("Lambertian") or
114  m_angularDistribution == string("Gaussian"))
115  B2INFO("Using the pre-defined angular distribution " << m_angularDistribution);
116  else {
117  B2INFO(m_angularDistribution << " is not a pre-defined distribution. Checking if it's a valid, positively-defined TFormula.");
118  TFormula testFormula("testFormula", m_angularDistribution.c_str());
119  int result = testFormula.Compile();
120  if (result != 0) {
121  B2FATAL(m_angularDistribution << " is not a valid angular distribution keyword, or it's a TFormula that does not compile.");
122  }
123  double testPoint = m_minAlpha; // let's test if the function is postive defined everywhere
124  while (testPoint < m_maxAlpha) {
125  double value = testFormula.Eval(testPoint * Unit::deg);
126  if (value < 0) {
127  B2FATAL("The formula " << m_angularDistribution << " is not positively defined in the test point " << testPoint << " deg (value = "
128  <<
129  value << ")");
130  }
131  testPoint += (m_maxAlpha - m_minAlpha) / 100.;
132  }
133  m_customDistribution = new TF1("m_customDistribution", m_angularDistribution.c_str(), m_minAlpha * Unit::deg,
134  m_maxAlpha * Unit::deg);
135  }
136 
137 
138 
139  // set other private variables
140  m_cosMinAlpha = cos(m_minAlpha * Unit::deg);
141  m_cosMaxAlpha = cos(m_maxAlpha * Unit::deg);
142  m_energy = TOPGeometryPar::c_hc / m_wavelength * Unit::eV;
143 
144  double barY0 = 0;
145  double barZ0 = 0;
146  double barPhi = 0;
147  if (m_slotID != 0) {
148  const auto* geo = TOPGeometryPar::Instance()->getGeometry();
149  int Nbars = geo->getNumModules();
150  if (Nbars == 0) B2ERROR("TOP bars are not defined");
151  if (m_slotID < 0 or m_slotID > Nbars) {
152  B2ERROR("barID = " << m_slotID << " : not a valid ID");
153  m_slotID = 0;
154  } else {
155  barY0 = geo->getModule(m_slotID).getRadius();
156  barZ0 = geo->getModule(m_slotID).getZc();
157  barPhi = geo->getModule(m_slotID).getPhi() - M_PI / 2;
158  }
159  }
160  m_translate.SetXYZ(m_x, m_y + barY0, m_z + barZ0);
161  m_rotate.RotateXEulerAngles(m_phi * Unit::deg,
162  m_theta * Unit::deg,
163  m_psi * Unit::deg);
164  m_rotateBar.RotateZ(barPhi);
165 
166  }
167 
168  void OpticalGunModule::beginRun()
169  {
170  }
171 
172  void OpticalGunModule::event()
173  {
174 
175  // generate number of photons
176  int numPhotons = 1;
177  if (m_numPhotons > 0) numPhotons = gRandom->Poisson(m_numPhotons);
178 
179  // start time
180  double startTime = m_startTime;
181  if (m_simCalPulses.getEntries() > 0) { // TOPCalPulseGenerator in the path
182  startTime += m_simCalPulses[0]->getTime(); // set start time w.r.t cal pulse
183  }
184 
185  // generate photons and store them to MCParticles
186  for (int i = 0; i < numPhotons; i++) {
187 
188  // generate emission point
189  double x, y;
190  do {
191  x = m_diameter * (gRandom->Rndm() - 0.5);
192  y = m_diameter * (gRandom->Rndm() - 0.5);
193  } while (x * x + y * y > m_diameter * m_diameter / 4.0);
194  TVector3 point(x, y, 0);
195 
196  // generate direction
197  TVector3 direction;
198  if (m_angularDistribution == string("uniform")) {
199  direction = getDirectionUniform();
200  } else if (m_angularDistribution == string("Lambertian")) {
201  direction = getDirectionLambertian();
202  } else if (m_angularDistribution == string("Gaussian")) {
203  direction = getDirectionGaussian();
204  } else { // we already have tested the formula and initialized the TF1 in the initialize() method
205  direction = getDirectionCustom();
206  }
207  TVector3 momentum = m_energy * direction;
208 
209  // check if photon flies through the slit
210  if (!isInsideSlit(point, direction)) continue;
211 
212  // generate polarization vector (unpolarized light source assumed)
213  double alphaPol = 2.0 * M_PI * gRandom->Rndm();
214  TVector3 polarization(cos(alphaPol), sin(alphaPol), 0);
215  polarization.RotateUz(direction);
216 
217  // generate emission time
218  double emissionTime = gRandom->Gaus(startTime, m_pulseWidth);
219 
220  // transform to Belle II frame
221  momentum.Transform(m_rotate);
222  polarization.Transform(m_rotate);
223  point.Transform(m_rotate);
224  point += m_translate;
225  if (m_slotID > 0) {
226  momentum.Transform(m_rotateBar);
227  polarization.Transform(m_rotateBar);
228  point.Transform(m_rotateBar);
229  }
230 
231  // store generated photon
232  auto* part = m_MCParticles.appendNew();
233  part->setPDG(0); // optical photon
234  part->setMass(0);
235  part->setStatus(MCParticle::c_PrimaryParticle);
236  part->addStatus(MCParticle::c_StableInGenerator);
237  part->setProductionVertex(point);
238  part->setProductionTime(emissionTime);
239  part->setMomentum(momentum);
240  part->setEnergy(m_energy);
241  part->setDecayVertex(polarization); // use this location temporary
242  }
243 
244  }
245 
246 
247  void OpticalGunModule::endRun()
248  {
249  }
250 
251  void OpticalGunModule::terminate()
252  {
253  }
254 
255  void OpticalGunModule::printModuleParams() const
256  {
257  }
258 
259  bool OpticalGunModule::isInsideSlit(const TVector3& point,
260  const TVector3& direction) const
261  {
262  if (m_slitZ < 0.01) return true; // no screen with a slit is put infront of a source
263  if (direction.Z() < 1.0e-6) return false; // must fly toward the slit
264 
265  double pathLength = (m_slitZ - point.Z()) / direction.Z();
266  if (m_slitDX > 0) {
267  double x = point.X() + pathLength * direction.X();
268  if (abs(x - m_slitX0) > m_slitDX / 2.0) return false;
269  }
270  if (m_slitDY > 0) {
271  double y = point.Y() + pathLength * direction.Y();
272  if (abs(y - m_slitY0) > m_slitDY / 2.0) return false;
273  }
274 
275  return true;
276  }
277 
278  TVector3 OpticalGunModule::getDirectionGaussian() const
279  {
280  // NA is defined as the aperture where the amplitude is 5% of that of the
281  // peak, which translates into 2.45 sigma for a gaussian distribution
282  double x = 0;
283  double y = 0;
284  double z = 0;
285  do {
286  x = gRandom->Gaus(0., asin(m_na) / 2.45);
287  y = gRandom->Gaus(0., asin(m_na) / 2.45);
288  z = 1. - x * x - y * y;
289  } while (z < 0);
290  return TVector3(x, y, sqrt(z));
291  }
292 
293  TVector3 OpticalGunModule::getDirectionUniform() const
294  {
295  double cosTheta = (m_cosMinAlpha - m_cosMaxAlpha) * gRandom->Rndm() + m_cosMaxAlpha;
296  double sinTheta = sqrt(1.0 - cosTheta * cosTheta);
297  double phi = 2.0 * M_PI * gRandom->Rndm();
298  return TVector3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
299  }
300 
301  TVector3 OpticalGunModule::getDirectionLambertian() const
302  {
303  double cosTheta = sqrt((m_cosMinAlpha * m_cosMinAlpha - m_cosMaxAlpha * m_cosMaxAlpha) * gRandom->Rndm() +
304  m_cosMaxAlpha * m_cosMaxAlpha);
305  double sinTheta = sqrt(1.0 - cosTheta * cosTheta);
306  double phi = 2.0 * M_PI * gRandom->Rndm();
307  return TVector3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
308  }
309 
310  TVector3 OpticalGunModule::getDirectionCustom() const
311  {
312  double alpha = m_customDistribution->GetRandom();
313  double phi = 2.0 * M_PI * gRandom->Rndm();
314  return TVector3(cos(phi) * sin(alpha), sin(phi) * sin(alpha), cos(alpha));
315  }
316 
317 
319 } // end Belle2 namespace
320 
Base class for Modules.
Definition: Module.h:72
Source of optical photons for the simulation of the TOP laser system.
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
Abstract base class for different kinds of events.