Belle II Software  release-08-01-10
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 header.
10 #include <top/modules/OpticalGun/OpticalGunModule.h>
11 
12 // TOP headers.
13 #include <top/geometry/TOPGeometryPar.h>
14 
15 // framework aux
16 #include <framework/gearbox/Unit.h>
17 #include <framework/logging/Logger.h>
18 
19 // ROOT
20 #include <TRandom.h>
21 #include <Math/EulerAngles.h>
22 #include <TFormula.h>
23 #include <TF1.h>
24 #include <top/reconstruction_cpp/func.h>
25 
26 using namespace std;
27 using namespace ROOT::Math;
28 
29 namespace Belle2 {
35  using namespace TOP;
36 
37  //-----------------------------------------------------------------
39  //-----------------------------------------------------------------
40 
41  REG_MODULE(OpticalGun);
42 
43  //-----------------------------------------------------------------
44  // Implementation
45  //-----------------------------------------------------------------
46 
47  OpticalGunModule::OpticalGunModule() : Module()
48  {
49  // set module description
50  setDescription("Source of optical photons");
52 
53  // Add parameters
54  addParam("x", m_x, "position in x [cm]", 0.0);
55  addParam("y", m_y, "position in y [cm]", 0.0);
56  addParam("z", m_z, "position in z [cm]", 0.0);
57  addParam("diameter", m_diameter, "source diameter [cm]", 0.0);
58  addParam("minAlpha", m_minAlpha, "source minimum emission angle [deg]. ", 0.0);
59  addParam("maxAlpha", m_maxAlpha, "source maximum emission angle [deg]. ", 30.);
60  addParam("na", m_na, "source numerical aperture. It is used only by the Gaussian distribution", 0.50);
61  addParam("angularDistribution", m_angularDistribution,
62  "source angular distribution: uniform, Lambertian, an arbitrary TFormula, or Gaussian "
63  "(numerical aperture instead of minAlpha and maxAlpha). If you are writing a TFormula, "
64  "assume the angles are measured in degrees. The conversion to radians is done internally.",
65  string("Gaussian"));
66  addParam("wavelength", m_wavelength, "wavelength of photons [nm]", 405.0);
67  addParam("phi", m_phi, "first rotation angle (around z) [deg]", 0.0);
68  addParam("theta", m_theta, "second rotation angle (around x) [deg]", 0.0);
69  addParam("psi", m_psi, "third rotation angle (around z) [deg]", 0.0);
70  addParam("startTime", m_startTime,
71  "start time [ns]. If TOPCalPulseGenerator is in path this is relative to the first cal pulse", 0.0);
72  addParam("pulseWidth", m_pulseWidth, "pulse duration (Gaussian sigma) [ns]", 0.0);
73  addParam("numPhotons", m_numPhotons,
74  "average number of photons per pulse, if positive, otherwise exactly one", 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, "
77  "otherwise Belle II frame is assumed", 0);
78  addParam("slitDX", m_slitDX, "slit size in x [cm], if positive, otherwise full open", 0.0);
79  addParam("slitDY", m_slitDY, "slit size in y [cm], if positive, otherwise full open", 0.0);
80  addParam("slitX0", m_slitX0, "slit x-offset in respect to source [cm] ", 0.0);
81  addParam("slitY0", m_slitY0, "slit y-offset in respect to source [cm] ", 0.0);
82  addParam("slitZ", m_slitZ, "slit distance to source [cm], if > 0.01, otherwise slit full open", 0.0);
83  }
84 
86  {
88  }
89 
91  {
92  // data store objects registration
94  m_simCalPulses.isOptional();
95 
96  // parameters check
97  if (m_wavelength < 150 or m_wavelength > 1000)
98  B2FATAL("Wavelength does not correspond to optical photons.");
99  if (m_na < 0 or m_na > 1)
100  B2FATAL("Numerical aperture must be between 0 and 1.");
101  if (m_minAlpha < 0)
102  B2FATAL("Minimum emission angle must be positive");
103  if (m_maxAlpha < 0)
104  B2FATAL("Maximum emission angle must be positive");
105  if (m_minAlpha >= m_maxAlpha)
106  B2FATAL("Minimum emission angle must me smaller than the maximum emission angle");
107 
108  if (m_angularDistribution == string("uniform") or
109  m_angularDistribution == string("Lambertian") or
110  m_angularDistribution == string("Gaussian"))
111  B2INFO("Using the pre-defined angular distribution " << m_angularDistribution);
112  else {
113  B2INFO(m_angularDistribution << " is not a pre-defined distribution. "
114  << "Checking if it's a valid, positively-defined TFormula.");
115  TFormula testFormula("testFormula", m_angularDistribution.c_str());
116  int result = testFormula.Compile();
117  if (result != 0) {
118  B2FATAL(m_angularDistribution << " TFormula does not compile.");
119  }
120  double testPoint = m_minAlpha; // let's test if the function is postive defined everywhere
121  while (testPoint < m_maxAlpha) {
122  double value = testFormula.Eval(testPoint * Unit::deg);
123  if (value < 0) {
124  B2FATAL("The formula " << m_angularDistribution << " is not positively defined at the test point "
125  << testPoint << " deg (value = " << value << ")");
126  }
127  testPoint += (m_maxAlpha - m_minAlpha) / 100.;
128  }
129  m_customDistribution = new TF1("m_customDistribution", m_angularDistribution.c_str(), m_minAlpha * Unit::deg,
131  }
132 
133  // set other private variables
137 
138  EulerAngles ea(-m_phi * Unit::deg, -m_theta * Unit::deg, -m_psi * Unit::deg); // rotation of an object as in TRotation
139  m_transform = Transform3D(Rotation3D(ea), Translation3D(m_x, m_y, m_z)); // source positioning and elevation
140  if (m_slotID != 0) {
141  const auto* geo = TOPGeometryPar::Instance()->getGeometry();
142  if (not geo->isModuleIDValid(m_slotID)) B2FATAL("Slot ID is not valid");
143  const auto& T = geo->getModule(m_slotID).getTransformation(); // slot to BelleII
144  m_transform = T * m_transform;
145  }
146 
147  }
148 
149 
151  {
152  // generate number of photons
153  int numPhotons = 1;
154  if (m_numPhotons > 0) numPhotons = gRandom->Poisson(m_numPhotons);
155 
156  // start time
157  double startTime = m_startTime;
158  if (m_simCalPulses.getEntries() > 0) { // TOPCalPulseGenerator in the path
159  startTime += m_simCalPulses[0]->getTime(); // set start time w.r.t cal pulse
160  }
161 
162  // generate photons and store them to MCParticles
163  for (int i = 0; i < numPhotons; i++) {
164 
165  // generate emission point
166  double x, y;
167  do {
168  x = m_diameter * (gRandom->Rndm() - 0.5);
169  y = m_diameter * (gRandom->Rndm() - 0.5);
170  } while (x * x + y * y > m_diameter * m_diameter / 4.0);
171  XYZPoint point(x, y, 0);
172 
173  // generate direction
174  XYZVector direction;
175  if (m_angularDistribution == string("uniform")) {
176  direction = getDirectionUniform();
177  } else if (m_angularDistribution == string("Lambertian")) {
178  direction = getDirectionLambertian();
179  } else if (m_angularDistribution == string("Gaussian")) {
180  direction = getDirectionGaussian();
181  } else { // we already have tested the formula and initialized the TF1 in the initialize() method
182  direction = getDirectionCustom();
183  }
184  XYZVector momentum = m_energy * direction;
185 
186  // check if photon passes the slit
187  if (not isInsideSlit(point, direction)) continue;
188 
189  // generate polarization vector (unpolarized light source assumed)
190  double alphaPol = 2.0 * M_PI * gRandom->Rndm();
191  XYZVector polarization(cos(alphaPol), sin(alphaPol), 0);
192  func::rotateUz(polarization, direction);
193 
194  // generate emission time
195  double emissionTime = gRandom->Gaus(startTime, m_pulseWidth);
196 
197  // transform to Belle II frame
198  point = m_transform * point;
199  momentum = m_transform * momentum;
200  polarization = m_transform * polarization;
201 
202  // store generated photon
203  auto* part = m_MCParticles.appendNew();
204  part->setPDG(0); // optical photon
205  part->setMass(0);
206  part->setStatus(MCParticle::c_PrimaryParticle);
207  part->addStatus(MCParticle::c_StableInGenerator);
208  part->setProductionVertex(static_cast<XYZVector>(point));
209  part->setProductionTime(emissionTime);
210  part->setMomentum(momentum);
211  part->setEnergy(m_energy);
212  part->setDecayVertex(polarization); // use this location temporary to pass photon polarization to FullSim
213  }
214 
215  }
216 
217 
218  bool OpticalGunModule::isInsideSlit(const XYZPoint& point,
219  const XYZVector& direction) const
220  {
221  if (m_slitZ < 0.01) return true; // no screen with a slit is put infront of a source
222  if (direction.Z() < 1.0e-6) return false; // must fly toward the slit
223 
224  double pathLength = (m_slitZ - point.Z()) / direction.Z();
225  if (m_slitDX > 0) {
226  double x = point.X() + pathLength * direction.X();
227  if (abs(x - m_slitX0) > m_slitDX / 2.0) return false;
228  }
229  if (m_slitDY > 0) {
230  double y = point.Y() + pathLength * direction.Y();
231  if (abs(y - m_slitY0) > m_slitDY / 2.0) return false;
232  }
233 
234  return true;
235  }
236 
238  {
239  // NA is defined as the aperture where the amplitude is 5% of that of the
240  // peak, which translates into 2.45 sigma for a gaussian distribution
241  double x = 0;
242  double y = 0;
243  double z = 0;
244  do {
245  x = gRandom->Gaus(0., asin(m_na) / 2.45);
246  y = gRandom->Gaus(0., asin(m_na) / 2.45);
247  z = 1. - x * x - y * y;
248  } while (z < 0);
249  return XYZVector(x, y, sqrt(z));
250  }
251 
253  {
254  double cosTheta = (m_cosMinAlpha - m_cosMaxAlpha) * gRandom->Rndm() + m_cosMaxAlpha;
255  double sinTheta = sqrt(1.0 - cosTheta * cosTheta);
256  double phi = 2.0 * M_PI * gRandom->Rndm();
257  return XYZVector(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
258  }
259 
261  {
262  double cosTheta = sqrt((m_cosMinAlpha * m_cosMinAlpha - m_cosMaxAlpha * m_cosMaxAlpha) * gRandom->Rndm() +
264  double sinTheta = sqrt(1.0 - cosTheta * cosTheta);
265  double phi = 2.0 * M_PI * gRandom->Rndm();
266  return XYZVector(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
267  }
268 
270  {
271  double alpha = m_customDistribution->GetRandom();
272  double phi = 2.0 * M_PI * gRandom->Rndm();
273  return XYZVector(cos(phi) * sin(alpha), sin(phi) * sin(alpha), cos(alpha));
274  }
275 
276 
278 } // end Belle2 namespace
279 
@ c_PrimaryParticle
bit 0: Particle is primary particle.
Definition: MCParticle.h:47
@ c_StableInGenerator
bit 1: Particle is stable, i.e., not decaying in the generator.
Definition: MCParticle.h:49
Base class for Modules.
Definition: Module.h:72
void setDescription(const std::string &description)
Sets the description of the module.
Definition: Module.cc:214
void setPropertyFlags(unsigned int propertyFlags)
Sets the flags for the module properties.
Definition: Module.cc:208
@ c_ParallelProcessingCertified
This module can be run in parallel processing mode safely (All I/O must be done through the data stor...
Definition: Module.h:80
std::string m_angularDistribution
source angular distribution
double m_minAlpha
minimum emission angle
TF1 * m_customDistribution
Custom angular distribution, that uses m_angularDistribution as formula.
double m_slitZ
slit distance from source
double m_cosMinAlpha
cos of m_minAlpha
StoreArray< TOPSimCalPulse > m_simCalPulses
simulated cal pulse collection
double m_slitDX
slit size in x
double m_wavelength
source wavelenght [nm]
double m_cosMaxAlpha
cos of m_maxAlpha
double m_slitDY
slit size in y
double m_slitX0
slit x-offset in respect to source
double m_na
source numerical aperture.
double m_slitY0
slit y-offset in respect to source
double m_phi
first rotation angle (around z) [deg]
double m_x
source position in x
double m_theta
second rotation angle (around x) [deg]
double m_startTime
start time
double m_maxAlpha
maximum emission angle
double m_energy
photon energy (from wavelength)
double m_diameter
source diameter
double m_psi
third rotation angle (around z) [deg]
double m_z
source position in z
double m_y
source position in y
double m_pulseWidth
pulse duration (Gaussian sigma)
double m_numPhotons
average number of photons in a pulse
StoreArray< MCParticle > m_MCParticles
MC particles collection.
ROOT::Math::Transform3D m_transform
transformation to BelleII frame
bool registerInDataStore(DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut)
Register the object/array in the DataStore.
T * appendNew()
Construct a new T object at the end of the array.
Definition: StoreArray.h:246
const TOPGeometry * getGeometry() const
Returns pointer to geometry object using basf2 units.
static TOPGeometryPar * Instance()
Static method to obtain the pointer to its instance.
static const double c_hc
Planck constant times speed of light in [eV*nm].
static const double deg
degree to radians
Definition: Unit.h:109
static const double eV
[electronvolt]
Definition: Unit.h:112
void addParam(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
Definition: Module.h:560
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
double sqrt(double a)
sqrt for double
Definition: beamHelpers.h:28
ROOT::Math::XYZVector getDirectionGaussian() const
Return photon direction according to a projected 2D gaussian distribution based on numerical aperture...
virtual ~OpticalGunModule()
Destructor.
ROOT::Math::XYZVector getDirectionUniform() const
Return photon direction according to a projected uniform distribution with opening angle alpha.
ROOT::Math::XYZVector getDirectionCustom() const
Return photon direction according to a custom angular distribution given by TFormula.
virtual void initialize() override
Initialize the Module.
virtual void event() override
Event processor.
bool isInsideSlit(const ROOT::Math::XYZPoint &point, const ROOT::Math::XYZVector &direction) const
Checks if photon passes the slit.
ROOT::Math::XYZVector getDirectionLambertian() const
Return photon direction according to a lambertian distribution with opening angle alpha.
Abstract base class for different kinds of events.