Belle II Software development
WireEfficiencyAlgorithm.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#include <cdc/calibration/WireEfficiencyAlgorithm.h>
10#include <calibration/CalibrationAlgorithm.h>
11
12#include <cdc/topology/CDCWireTopology.h>
13#include <cdc/topology/CDCWireLayer.h>
14
15#include <cdc/geometry/CDCGeometryPar.h>
16#include <cdc/dbobjects/CDCBadWires.h>
17#include <framework/logging/Logger.h>
18#include <TH2F.h>
19#include <TFitResult.h>
20#include <TF1.h>
21#include <TGraphAsymmErrors.h>
22#include <TMath.h>
23
24using namespace Belle2;
25using namespace CDC;
27{
29 " -------------------------- Wire Efficiency Estimation Algorithm -------------------------\n"
30 );
31}
32
34{
35 unsigned short wireID;
36 unsigned short layerID;
37 float z;
38 bool isFound;
39
40 auto efftree = getObjectPtr<TTree>("efftree");
41 efftree->SetBranchAddress("wireID", &wireID);
42 efftree->SetBranchAddress("layerID", &layerID);
43 efftree->SetBranchAddress("z", &z);
44 efftree->SetBranchAddress("isFound", &isFound);
45
46 static const CDCWireTopology& wireTopology = CDCWireTopology::getInstance();
47 static const CDCGeometryPar& cdcgeo = CDCGeometryPar::Instance();
48
49 std::vector<long long> wireCounts;
50
51 for (const CDCWireLayer& wireLayer : wireTopology.getWireLayers()) {
52 unsigned short layerNo = wireLayer.getICLayer();
53 unsigned short nwidbins = cdcgeo.nWiresInLayer(layerNo);
54 wireCounts.resize(wireCounts.size() + nwidbins, 0);
55 }
56
57 const Long64_t nEntries = efftree->GetEntries();
58
59 for (Long64_t i = 0; i < nEntries; i++) {
60 efftree->GetEntry(i);
61 wireCounts[wireID]++;
62 }
63
64 unsigned int totalCountsForActiveWires = 0;
65 unsigned int activeWireCount = 0;
66
67 for (auto count : wireCounts) {
68 if (count > 0) {
69 totalCountsForActiveWires += count;
70 activeWireCount++;
71 }
72 }
73
74 double averageOccupancy =
75 (activeWireCount > 0) ? (1.0 * totalCountsForActiveWires / activeWireCount) : 0.0;
76
77 bool enoughData = (averageOccupancy > m_averageOccupancyThreshold);
78 if (enoughData)
79 B2INFO("Will run the calibration for this run with enough hits per wire: " << averageOccupancy);
80 else
81 B2WARNING("Not enough hits per wire in this run: " << averageOccupancy);
82 return enoughData;
83}
84
86{
87 unsigned short wireID;
88 unsigned short layerID;
89 float z;
90 bool isFound;
91
92 auto efftree = getObjectPtr<TTree>("efftree");
93 efftree->SetBranchAddress("wireID", &wireID);
94 efftree->SetBranchAddress("layerID", &layerID);
95 efftree->SetBranchAddress("z", &z);
96 efftree->SetBranchAddress("isFound", &isFound);
97
98 static const CDCWireTopology& wireTopology = CDCWireTopology::getInstance();
99 static const CDCGeometryPar& cdcgeo = CDCGeometryPar::Instance();
100
101 for (const CDCWireLayer& wireLayer : wireTopology.getWireLayers()) {
102 unsigned short layerNo = wireLayer.getICLayer();
103
104 unsigned short nzbins = 30 - layerNo / 7;
105 unsigned short nwidbins = cdcgeo.nWiresInLayer(layerNo);
106
107 double widbins[2] = { -0.5, (double)cdcgeo.nWiresInLayer(layerNo) - 0.5};
108 double zbins[2] = {wireLayer.getBackwardZ(), wireLayer.getForwardZ()};
109
110 TEfficiency* effInLayer = new TEfficiency(
111 Form("effLayer_%d", layerNo),
112 Form("Hit efficiency of L%d ; z (cm) ; IWire ", layerNo),
113 nzbins, zbins[0], zbins[1], nwidbins, widbins[0], widbins[1]);
114
115 m_efficiencyList->Add(effInLayer);
116 }
117
118 const Long64_t nEntries = efftree->GetEntries();
119
120 for (Long64_t i = 0; i < nEntries; i++) {
121 efftree->GetEntry(i);
122
123 if (layerID < m_efficiencyList->GetEntries()) {
124 TEfficiency* efficiencyInLayer =
125 (TEfficiency*)m_efficiencyList->At(layerID);
126
127 efficiencyInLayer->Fill(isFound, z, wireID);
128 }
129 }
130
131 TFile* outputCollection = new TFile("LayerEfficiencies.root", "RECREATE");
132 m_efficiencyList->Write();
133 outputCollection->Close();
134 delete outputCollection;
135}
136
138{
139 static const CDCWireTopology& wireTopology = CDCWireTopology::getInstance();
140
141 for (const CDCWireLayer& wireLayer : wireTopology.getWireLayers()) {
142 unsigned short layerNo = wireLayer.getICLayer();
143
144 // need to use casting here because GetPassedHistogram assumes it returns TH1
145 auto efficiencyInLayer = (TEfficiency*)m_efficiencyList->At(layerNo);
146 auto passed = (TH2F*)efficiencyInLayer->GetPassedHistogram();
147 auto total = (TH2F*)efficiencyInLayer->GetTotalHistogram();
148
149 // Ignoring layers that have no hits at all
150 if (!total->GetEntries()) continue;
151
152 // prepare fitting functions
153 double minFitRange = wireLayer.getBackwardZ() + 30;
154 double maxFitRange = wireLayer.getForwardZ() - 30;
155 TF1* constantFunction = new TF1("constantFunction", "pol0", minFitRange, maxFitRange);
156
157 // Estimate average efficiency of the layer as function of z
158 auto passedProjectedX = passed->ProjectionX("projectionx1");
159 auto totalProjectedX = total->ProjectionX("projectionx2");
160 TEfficiency* efficiencyProjectedX = new TEfficiency(*passedProjectedX, *totalProjectedX);
161
162 unsigned short minFitBin = passedProjectedX->FindBin(minFitRange) + 1;
163 unsigned short maxFitBin = passedProjectedX->FindBin(maxFitRange) - 1;
164
165 // Estimate average efficiency of each wire in the layer
166 auto passedProjectedY = passed->ProjectionY("projectiony1", minFitBin, maxFitBin);
167 auto totalProjectedY = total->ProjectionY("projectiony2", minFitBin, maxFitBin);
168 TEfficiency* efficiencyProjectedY = new TEfficiency(*passedProjectedY, *totalProjectedY);
169
170 // Estimate average efficiency of the whole layer by summing up averages of individual wires
171 float totalAverage = 0;
172 int nonZeroWires = 0;
173 for (int i = 1; i <= passedProjectedY->GetNbinsX(); ++i) {
174 float efficiencyAtBin = efficiencyProjectedY->GetEfficiency(i);
175 if (efficiencyAtBin > 0.2) {
176 totalAverage += efficiencyAtBin;
177 nonZeroWires++;
178 }
179 }
180 // safeguard in case all wires are 0
181 if (nonZeroWires > 0) totalAverage /= nonZeroWires;
182 else totalAverage = 0;
183
184 TGraphAsymmErrors* graphEfficiencyProjected = efficiencyProjectedX->CreateGraph();
185
186 // Due to the way histograms are binned, every bin center should correspond a wire ID.
187 for (int i = 1; i <= passed->GetNbinsY(); ++i) {
188
189 // project out 1 wire
190 auto singleWirePassed = passed->ProjectionX("single wire projection passed", i, i);
191 auto singleWireTotal = total->ProjectionX("single wire projection total", i, i);
192
193 // if not a single hit within the central area of wire then mark it as bad
194 if (!singleWirePassed->Integral(minFitBin, maxFitBin)) {
195 double wireID = passed->GetYaxis()->GetBinCenter(i);
196 unsigned short maxWires = CDCGeometryPar::Instance().nWiresInLayer(layerNo);
197 if (wireID < 0 || wireID >= maxWires) {
198 B2ERROR("Invalid wireID: " << wireID << " for LayerID: " << layerNo
199 << ". Max wires: " << maxWires);
200 delete singleWirePassed;
201 delete singleWireTotal;
202 continue;
203 }
204 m_badWireList->setWire(layerNo, round(wireID), 0);
205 delete singleWirePassed;
206 delete singleWireTotal;
207 continue;
208 }
209 // constructs efficiency of one wire
210 TEfficiency* singleWireEfficiency = new TEfficiency(*singleWirePassed, *singleWireTotal);
211 TGraphAsymmErrors* graphSingleWireEfficiency = singleWireEfficiency->CreateGraph();
212
213 // fits the efficiency with a constant
214 TFitResultPtr singleWireFitResults = graphSingleWireEfficiency->Fit(constantFunction, "SQR");
215 double singleWireEfficiencyFromFit = (singleWireFitResults.Get())->Value(0);
216 double singleWireUpErrorFromFit = (singleWireFitResults.Get())->UpperError(0);
217
218 // applies a chi test on the wire
219 float p_value = chiTest(graphSingleWireEfficiency, graphEfficiencyProjected, minFitRange, maxFitRange);
220 // check three conditions and decide if wire should be marked as bad
221 bool averageCondition = (0.95 * totalAverage) > (singleWireEfficiencyFromFit + 3 * singleWireUpErrorFromFit);
222 bool pvalueCondition = p_value < 0.01;
223 bool generalCondition = singleWireEfficiencyFromFit < 0.4;
224 if (generalCondition || (averageCondition && pvalueCondition)) {
225 double wireID = passed->GetYaxis()->GetBinCenter(i);
226 m_badWireList->setWire(layerNo, round(wireID), singleWireEfficiencyFromFit);
227 }
228
229 delete singleWireEfficiency;
230 delete graphSingleWireEfficiency;
231 delete singleWirePassed;
232 delete singleWireTotal;
233 }
234
235 delete constantFunction;
236 delete passedProjectedX;
237 delete totalProjectedX;
238 delete efficiencyProjectedX;
239 delete passedProjectedY;
240 delete totalProjectedY;
241 delete efficiencyProjectedY;
242 delete graphEfficiencyProjected;
243 }
244}
245
246double WireEfficiencyAlgorithm::chiTest(TGraphAsymmErrors* graph1, TGraphAsymmErrors* graph2, double minValue, double maxValue)
247{
248 // Vars to perform the chi test
249 double chi = 0;
250 unsigned short ndof = 0;
251
252 int numOfEntries1 = graph1->GetN();
253 int numOfEntries2 = graph2->GetN();
254
255 // loop over entries in both graphs, index by index.
256 for (int index1 = 0; index1 < numOfEntries1; ++index1) {
257 // TGraph values are usually not listed in increasing order. Need to check that they are within min/max range for comparison.
258 if (graph1->GetX()[index1] < minValue) continue;
259 if (graph1->GetX()[index1] > maxValue) continue;
260 for (int index2 = 0; index2 < numOfEntries2; ++index2) {
261 if (std::abs(graph1->GetX()[index1] - graph2->GetX()[index2]) < 1e-6) {
262 // this is broken up just for readability
263 double chiNumerator = std::pow(graph1->GetY()[index1] - graph2->GetY()[index2], 2);
264 double err1 = 0.5 * (graph1->GetErrorYhigh(index1) + graph1->GetErrorYlow(index1));
265 double err2 = 0.5 * (graph2->GetErrorYhigh(index2) + graph2->GetErrorYlow(index2));
266 double chiDenominator = err1 * err1 + err2 * err2;
267 //double chiDenominator = std::pow(graph1->GetErrorYhigh(index1), 2) + std::pow(graph1->GetErrorYlow(index1), 2);
268 chi += chiNumerator / chiDenominator;
269 ndof++;
270 continue;
271 }
272 }
273 }
274 return TMath::Prob(chi, ndof);
275}
276
278{
280
281 const auto exprun = getRunList()[0];
282 B2INFO("ExpRun used for DB Geometry : " << exprun.first << " " << exprun.second);
283 updateDBObjPtrs(1, exprun.second, exprun.first);
284
286
287 bool enoughData = hasEnoughData();
288 if (not enoughData)
289 return c_NotEnoughData;
290
293 m_badWireList->outputToFile("wireFile.txt");
294 saveCalibration(m_badWireList, "CDCBadWires");
295 return c_OK;
296}
Database object for bad wires.
Definition CDCBadWires.h:27
The Class for CDC Geometry Parameters.
unsigned nWiresInLayer(int layerId) const
Returns wire numbers in a layer.
static CDCGeometryPar & Instance(const CDCGeometry *=nullptr)
Static method to get a reference to the CDCGeometryPar instance.
Class representing a sense wire layer in the central drift chamber.
Class representing the sense wire arrangement in the whole of the central drift chamber.
const std::vector< CDCWireLayer > & getWireLayers() const
Getter for the underlying storing layer vector.
static CDCWireTopology & getInstance()
Getter for the singleton instance of the wire topology.
TList * m_efficiencyList
TList of efficiencies.
double chiTest(TGraphAsymmErrors *graph1, TGraphAsymmErrors *graph2, double minVale, double maxValue)
chitest
CDCBadWires * m_badWireList
BadWireList that willbe built.
DBObjPtr< CDCGeometry > m_cdcGeo
Geometry of CDC.
EResult calibrate() override
Run algo on data.
void buildEfficiencies()
create 2D TEfficiency for each wire
bool hasEnoughData()
check if there is enough data to run the calibration
float m_averageOccupancyThreshold
Threshold for the average layer occupancy to run the calibration.
void saveCalibration(TClonesArray *data, const std::string &name)
Store DBArray payload with given name with default IOV.
static void updateDBObjPtrs(const unsigned int event, const int run, const int experiment)
Updates any DBObjPtrs by calling update(event) for DBStore.
void setDescription(const std::string &description)
Set algorithm description (in constructor)
const std::vector< Calibration::ExpRun > & getRunList() const
Get the list of runs for which calibration is called.
EResult
The result of calibration.
@ c_OK
Finished successfully =0 in Python.
@ c_NotEnoughData
Needs more data =2 in Python.
CalibrationAlgorithm(const std::string &collectorModuleName)
Constructor - sets the prefix for collected objects (won't be accesses until execute(....
std::shared_ptr< T > getObjectPtr(const std::string &name, const std::vector< Calibration::ExpRun > &requestedRuns)
Get calibration data object by name and list of runs, the Merge function will be called to generate t...
Abstract base class for different kinds of events.