Belle II Software development
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
26using namespace std;
27using namespace ROOT::Math;
28
29namespace Belle2 {
35 using namespace TOP;
36
37 //-----------------------------------------------------------------
39 //-----------------------------------------------------------------
40
41 REG_MODULE(OpticalGun);
42
43 //-----------------------------------------------------------------
44 // Implementation
45 //-----------------------------------------------------------------
46
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
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
void setPDG(int pdg)
Set PDG code of the particle.
Definition: MCParticle.h:335
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.
void rotateUz(ROOT::Math::XYZVector &vec, const ROOT::Math::XYZVector &z_Axis)
Replacement for a function TVector3::RotateUz which is not implemented in GenVector classes.
Definition: func.h:114
Abstract base class for different kinds of events.
STL namespace.