forked from AliceO2Group/AliceO2
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGEMAmplification.cxx
More file actions
209 lines (193 loc) · 8.61 KB
/
GEMAmplification.cxx
File metadata and controls
209 lines (193 loc) · 8.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.
/// \file GEMAmplification.cxx
/// \brief Implementation of the GEM amplification
/// \author Andi Mathis, TU München, andreas.mathis@ph.tum.de
#include "TPCSimulation/GEMAmplification.h"
#include <TStopwatch.h>
#include "MathUtils/CachingTF1.h"
#include <TFile.h>
#include "TPCBase/CDBInterface.h"
#include <fstream>
#include "Framework/Logger.h"
#include <filesystem>
using namespace o2::tpc;
using namespace o2::math_utils;
using boost::format;
GEMAmplification::GEMAmplification()
: mRandomGaus(),
mRandomFlat(RandomRing<>::RandomType::Flat),
mGain()
{
updateParameters();
TStopwatch watch;
watch.Start();
const float sigmaOverMu = mGasParam->SigmaOverMu;
const float kappa = 1 / (sigmaOverMu * sigmaOverMu);
boost::format polya("1/(TMath::Gamma(%1%)*%2%) * TMath::Power(x/%3%, %4%) * TMath::Exp(-x/%5%)");
const char* polyaFileName = "tpc_polya.root";
TFile* outfile;
auto cacheexists = std::filesystem::exists(polyaFileName);
if (cacheexists) {
LOG(info) << "TPC: GEM setup from existing cache";
outfile = TFile::Open(polyaFileName);
} else {
LOG(info) << "TPC: GEM setup - initialization from scratch";
outfile = TFile::Open(polyaFileName, "CREATE");
}
/// Set the polya distribution for the individual GEMs
for (int i = 0; i < 4; ++i) {
float s = mGEMParam->AbsoluteGain[i] / kappa;
polya % kappa % s % s % (kappa - 1) % s;
std::string name = polya.str();
o2::math_utils::CachingTF1* polyaDistribution = nullptr;
if (!cacheexists) {
polyaDistribution = new o2::math_utils::CachingTF1("polya", name.c_str(), 0, 10.f * mGEMParam->AbsoluteGain[i]);
/// this dramatically alters the speed with which the filling is executed...
/// without this, the distribution makes discrete steps at every int
polyaDistribution->SetNpx(100000);
} else {
polyaDistribution = (o2::math_utils::CachingTF1*)outfile->Get(TString::Format("func%d", i).Data());
// FIXME: verify that distribution corresponds to the parameters used here
}
mGain[i].initialize(*polyaDistribution);
if (!cacheexists) {
outfile->WriteTObject(polyaDistribution, TString::Format("func%d", i).Data());
}
delete polyaDistribution;
}
/// Set the polya distribution for the full stack per region (IROC, OROC1, OROC2, OROC3)
const float gainStack = mGEMParam->TotalGainStack;
const float kappaStack = mGEMParam->KappaStack;
const float effStack = mGEMParam->EfficiencyStack;
// Correct for electron losses occurring when changing kappa and the efficiency
for (int i = 0; i < 4; ++i) {
const float gainStackPerRegion = mGEMParam->RelativeGainStack[i] * gainStack;
LOG(info) << "TPC: Region: " << i << " Gain: " << gainStackPerRegion;
const float sStack = gainStackPerRegion / (kappaStack * effStack);
polya % kappaStack % sStack % sStack % (kappaStack - 1) % sStack;
std::string name = polya.str();
o2::math_utils::CachingTF1* polyaDistribution = nullptr;
if (!cacheexists) {
polyaDistribution = new o2::math_utils::CachingTF1("polya", name.c_str(), 0, 25.f * gainStackPerRegion);
polyaDistribution->SetNpx(50000);
} else {
polyaDistribution = (o2::math_utils::CachingTF1*)outfile->Get(TString::Format("polyaStack%d", i).Data());
}
mGainFullStack[i].initialize(*polyaDistribution);
if (!cacheexists) {
outfile->WriteTObject(polyaDistribution, TString::Format("polyaStack%d", i).Data());
}
delete polyaDistribution;
}
if (outfile) {
outfile->Close();
}
watch.Stop();
LOG(info) << "TPC: GEM setup (polya) took " << watch.CpuTime();
}
void GEMAmplification::updateParameters()
{
auto& cdb = CDBInterface::instance();
mGEMParam = &(ParameterGEM::Instance());
mGasParam = &(ParameterGas::Instance());
mGainMap = &(cdb.getGainMap());
}
int GEMAmplification::getStackAmplification(int nElectrons)
{
/// We start with an arbitrary number of electrons given to the first amplification stage
/// The amplification in the GEM stack is handled for each electron individually and the resulting amplified
/// electrons are passed to the next amplification stage.
const int nElectronsGEM1 = getSingleGEMAmplification(nElectrons, 0);
const int nElectronsGEM2 = getSingleGEMAmplification(nElectronsGEM1, 1);
const int nElectronsGEM3 = getSingleGEMAmplification(nElectronsGEM2, 2);
const int nElectronsGEM4 = getSingleGEMAmplification(nElectronsGEM3, 3);
return nElectronsGEM4;
}
int GEMAmplification::getEffectiveStackAmplification(int region, int nElectrons)
{
/// We start with an arbitrary number of electrons given to the first amplification stage
/// The amplification in the GEM stack is handled for each electron individually and the amplification
/// in the stack is handled in an effective manner for different regions (IROC, OROC1, OROC2, OROC3)
int nElectronsGEM = 0;
for (int i = 0; i < nElectrons; ++i) {
if (mRandomFlat.getNextValue() > mGEMParam->EfficiencyStack) {
continue;
}
nElectronsGEM += mGainFullStack[region].getNextValue();
}
return nElectronsGEM;
}
int GEMAmplification::getSingleGEMAmplification(int nElectrons, int GEM)
{
/// The effective gain of the GEM foil is given by three components
/// -# Collection of the electrons, which is related to the collection efficiency ε_coll
/// -# Amplification of the charges in the GEM holes, which is related to the GEM absolute gain G_abs
/// -# Extraction of the electrons, which is related to the extraction efficiency ε_extr
/// The effective gain, and thus the overall amplification of the GEM is then given by
/// G_eff = ε_coll * G_abs * ε_extr
/// Each of the three processes is handled by a sub-routine
int collectionGEM = getElectronLosses(nElectrons, mGEMParam->CollectionEfficiency[GEM]);
int amplificationGEM = getGEMMultiplication(collectionGEM, GEM);
int extractionGEM = getElectronLosses(amplificationGEM, mGEMParam->ExtractionEfficiency[GEM]);
return extractionGEM;
}
int GEMAmplification::getGEMMultiplication(int nElectrons, int GEM)
{
/// Total charge multiplication in the GEM
/// We take into account fluctuations of the avalanche process
if (nElectrons < 1) {
/// All electrons are lost in case none are given to the GEM
return 0;
} else if (nElectrons > 500) {
/// For this condition the central limit theorem holds and we can approximate the amplification fluctuations by
/// a Gaussian for all electrons
/// The mean is given by nElectrons * G_abs and the width by sqrt(nElectrons) * Sigma/Mu (Polya) * G_abs
return ((mRandomGaus.getNextValue() * std::sqrt(static_cast<float>(nElectrons)) *
mGasParam->SigmaOverMu) +
nElectrons) *
mGEMParam->AbsoluteGain[GEM];
} else {
/// Otherwise we compute the gain fluctuations as the convolution of many single electron amplification
/// fluctuations
int electronsOut = 0;
for (int i = 0; i < nElectrons; ++i) {
electronsOut += mGain[GEM].getNextValue();
}
return electronsOut;
}
}
int GEMAmplification::getElectronLosses(int nElectrons, float probability)
{
/// Electrons losses due to collection or extraction processes
float electronsFloat = static_cast<float>(nElectrons);
if (nElectrons < 1 || probability < 0.00001) {
/// All electrons are lost in case none are given to the GEM, or the probability is negligible
return 0;
} else if (probability > 0.99999) {
/// For sufficiently large probabilities all electrons are passed further on
return nElectrons;
} else if (electronsFloat * probability >= 5.f && electronsFloat * (1.f - probability) >= 5.f) {
/// Condition whether the binomial distribution can be approximated by a Gaussian with mean n*p+0.5 and
/// width sqrt(n*p*(1-p))
return (mRandomGaus.getNextValue() * std::sqrt(electronsFloat * probability * (1 - probability))) +
electronsFloat * probability + 0.5;
} else {
/// Explicit handling of the probability for each individual electron
int nElectronsOut = 0;
for (int i = 0; i < nElectrons; ++i) {
if (mRandomFlat.getNextValue() < probability) {
++nElectronsOut;
}
}
return nElectronsOut;
}
}