Belle II Software  release-05-02-19
OpticalGunModule.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2010 - Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Marko Staric *
7  * Stefano Lacaprara *
8  * *
9  * This software is provided "as is" without any warranty. *
10  **************************************************************************/
11 
12 // Own include
13 #include <top/modules/OpticalGun/OpticalGunModule.h>
14 #include <top/geometry/TOPGeometryPar.h>
15 
16 
17 // framework - DataStore
18 #include <framework/datastore/StoreArray.h>
19 
20 // framework aux
21 #include <framework/gearbox/Unit.h>
22 #include <framework/logging/Logger.h>
23 
24 // ROOT
25 #include <TRandom.h>
26 #include <TVector3.h>
27 #include <TRotation.h>
28 #include <TFormula.h>
29 #include <TF1.h>
30 
31 using namespace std;
32 
33 namespace Belle2 {
39  using namespace TOP;
40 
41  //-----------------------------------------------------------------
42  // Register module
43  //-----------------------------------------------------------------
44 
45  REG_MODULE(OpticalGun)
46 
47  //-----------------------------------------------------------------
48  // Implementation
49  //-----------------------------------------------------------------
50 
52  {
53  // set module description
54  setDescription("Source of optical photons");
55  setPropertyFlags(c_ParallelProcessingCertified);
56 
57  // Add parameters
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.",
67  string("Gaussian"));
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",
76  0.0);
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",
79  0);
80  addParam("slitDX", m_slitDX, "slit size in x [cm], if positive, otherwise full open",
81  0.0);
82  addParam("slitDY", m_slitDY, "slit size in y [cm], if positive, otherwise full open",
83  0.0);
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",
87  0.0);
88  }
89 
90  OpticalGunModule::~OpticalGunModule()
91  {
92  if (m_customDistribution) delete m_customDistribution;
93  }
94 
95  void OpticalGunModule::initialize()
96  {
97  // data store objects registration
98  m_MCParticles.registerInDataStore();
99  m_simCalPulses.isOptional();
100 
101  // parameters check
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.");
106  if (m_minAlpha < 0)
107  B2FATAL("Minimum emission angle must be positive");
108  if (m_maxAlpha < 0)
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");
112 
113 
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);
118  else {
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();
122  if (result != 0) {
123  B2FATAL(m_angularDistribution << " is not a valid angular distribution keyword, or it's a TFormula that does not compile.");
124  }
125  double testPoint = m_minAlpha; // let's test if the function is postive defined everywhere
126  while (testPoint < m_maxAlpha) {
127  double value = testFormula.Eval(testPoint * Unit::deg);
128  if (value < 0) {
129  B2FATAL("The formula " << m_angularDistribution << " is not positively defined in the test point " << testPoint << " deg (value = "
130  <<
131  value << ")");
132  }
133  testPoint += (m_maxAlpha - m_minAlpha) / 100.;
134  }
135  m_customDistribution = new TF1("m_customDistribution", m_angularDistribution.c_str(), m_minAlpha * Unit::deg,
136  m_maxAlpha * Unit::deg);
137  }
138 
139 
140 
141  // set other private variables
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;
145 
146  double barY0 = 0;
147  double barZ0 = 0;
148  double barPhi = 0;
149  if (m_slotID != 0) {
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");
155  m_slotID = 0;
156  } else {
157  barY0 = geo->getModule(m_slotID).getRadius();
158  barZ0 = geo->getModule(m_slotID).getZc();
159  barPhi = geo->getModule(m_slotID).getPhi() - M_PI / 2;
160  }
161  }
162  m_translate.SetXYZ(m_x, m_y + barY0, m_z + barZ0);
163  m_rotate.RotateXEulerAngles(m_phi * Unit::deg,
164  m_theta * Unit::deg,
165  m_psi * Unit::deg);
166  m_rotateBar.RotateZ(barPhi);
167 
168  }
169 
170  void OpticalGunModule::beginRun()
171  {
172  }
173 
174  void OpticalGunModule::event()
175  {
176 
177  // generate number of photons
178  int numPhotons = 1;
179  if (m_numPhotons > 0) numPhotons = gRandom->Poisson(m_numPhotons);
180 
181  // start time
182  double startTime = m_startTime;
183  if (m_simCalPulses.getEntries() > 0) { // TOPCalPulseGenerator in the path
184  startTime += m_simCalPulses[0]->getTime(); // set start time w.r.t cal pulse
185  }
186 
187  // generate photons and store them to MCParticles
188  for (int i = 0; i < numPhotons; i++) {
189 
190  // generate emission point
191  double x, y;
192  do {
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);
197 
198  // generate direction
199  TVector3 direction;
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();
206  } else { // we already have tested the formula and initialized the TF1 in the initialize() method
207  direction = getDirectionCustom();
208  }
209  TVector3 momentum = m_energy * direction;
210 
211  // check if photon flies through the slit
212  if (!isInsideSlit(point, direction)) continue;
213 
214  // generate polarization vector (unpolarized light source assumed)
215  double alphaPol = 2.0 * M_PI * gRandom->Rndm();
216  TVector3 polarization(cos(alphaPol), sin(alphaPol), 0);
217  polarization.RotateUz(direction);
218 
219  // generate emission time
220  double emissionTime = gRandom->Gaus(startTime, m_pulseWidth);
221 
222  // transform to Belle II frame
223  momentum.Transform(m_rotate);
224  polarization.Transform(m_rotate);
225  point.Transform(m_rotate);
226  point += m_translate;
227  if (m_slotID > 0) {
228  momentum.Transform(m_rotateBar);
229  polarization.Transform(m_rotateBar);
230  point.Transform(m_rotateBar);
231  }
232 
233  // store generated photon
234  auto* part = m_MCParticles.appendNew();
235  part->setPDG(0); // optical photon
236  part->setMass(0);
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); // use this location temporary
244  }
245 
246  }
247 
248 
249  void OpticalGunModule::endRun()
250  {
251  }
252 
253  void OpticalGunModule::terminate()
254  {
255  }
256 
257  void OpticalGunModule::printModuleParams() const
258  {
259  }
260 
261  bool OpticalGunModule::isInsideSlit(const TVector3& point,
262  const TVector3& direction) const
263  {
264  if (m_slitZ < 0.01) return true; // no screen with a slit is put infront of a source
265  if (direction.Z() < 1.0e-6) return false; // must fly toward the slit
266 
267  double pathLength = (m_slitZ - point.Z()) / direction.Z();
268  if (m_slitDX > 0) {
269  double x = point.X() + pathLength * direction.X();
270  if (abs(x - m_slitX0) > m_slitDX / 2.0) return false;
271  }
272  if (m_slitDY > 0) {
273  double y = point.Y() + pathLength * direction.Y();
274  if (abs(y - m_slitY0) > m_slitDY / 2.0) return false;
275  }
276 
277  return true;
278  }
279 
280  TVector3 OpticalGunModule::getDirectionGaussian() const
281  {
282  // NA is defined as the aperture where the amplitude is 5% of that of the
283  // peak, which translates into 2.45 sigma for a gaussian distribution
284  double x = 0;
285  double y = 0;
286  double z = 0;
287  do {
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;
291  } while (z < 0);
292  return TVector3(x, y, sqrt(z));
293  }
294 
295  TVector3 OpticalGunModule::getDirectionUniform() const
296  {
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);
301  }
302 
303  TVector3 OpticalGunModule::getDirectionLambertian() const
304  {
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);
310  }
311 
312  TVector3 OpticalGunModule::getDirectionCustom() const
313  {
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));
317  }
318 
319 
321 } // end Belle2 namespace
322 
REG_MODULE
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:652
Belle2::Module
Base class for Modules.
Definition: Module.h:74
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::OpticalGunModule
Source of optical photons for the simulation of the TOP laser system.
Definition: OpticalGunModule.h:57