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 continue; // remove invalid wireID
201 }
202 m_badWireList->setWire(layerNo, round(wireID), 0);
203 continue;
204 }
205 // constructs efficiency of one wire
206 TEfficiency* singleWireEfficiency = new TEfficiency(*singleWirePassed, *singleWireTotal);
207 TGraphAsymmErrors* graphSingleWireEfficiency = singleWireEfficiency->CreateGraph();
208
209 // fits the efficiency with a constant
210 TFitResultPtr singleWireFitResults = graphSingleWireEfficiency->Fit(constantFunction, "SQR");
211 double singleWireEfficiencyFromFit = (singleWireFitResults.Get())->Value(0);
212 double singleWireUpErrorFromFit = (singleWireFitResults.Get())->UpperError(0);
213
214 // applies a chi test on the wire
215 float p_value = chiTest(graphSingleWireEfficiency, graphEfficiencyProjected, minFitRange, maxFitRange);
216 // check three conditions and decide if wire should be marked as bad
217 bool averageCondition = (0.95 * totalAverage) > (singleWireEfficiencyFromFit + 3 * singleWireUpErrorFromFit);
218 bool pvalueCondition = p_value < 0.01;
219 bool generalCondition = singleWireEfficiencyFromFit < 0.4;
220 if (generalCondition || (averageCondition && pvalueCondition)) {
221 double wireID = passed->GetYaxis()->GetBinCenter(i);
222 m_badWireList->setWire(layerNo, round(wireID), singleWireEfficiencyFromFit);
223 }
224 }
225 }
226}
227
228double WireEfficiencyAlgorithm::chiTest(TGraphAsymmErrors* graph1, TGraphAsymmErrors* graph2, double minValue, double maxValue)
229{
230 // Vars to perform the chi test
231 double chi = 0;
232 unsigned short ndof = 0;
233
234 int numOfEntries1 = graph1->GetN();
235 int numOfEntries2 = graph2->GetN();
236
237 // loop over entries in both graphs, index by index.
238 for (int index1 = 0; index1 < numOfEntries1; ++index1) {
239 // TGraph values are usually not listed in increasing order. Need to check that they are within min/max range for comparison.
240 if (graph1->GetX()[index1] < minValue) continue;
241 if (graph1->GetX()[index1] > maxValue) continue;
242 for (int index2 = 0; index2 < numOfEntries2; ++index2) {
243 if (std::abs(graph1->GetX()[index1] - graph2->GetX()[index2]) < 1e-6) {
244 // this is broken up just for readability
245 double chiNumerator = std::pow(graph1->GetY()[index1] - graph2->GetY()[index2], 2);
246 double err1 = 0.5 * (graph1->GetErrorYhigh(index1) + graph1->GetErrorYlow(index1));
247 double err2 = 0.5 * (graph2->GetErrorYhigh(index2) + graph2->GetErrorYlow(index2));
248 double chiDenominator = err1 * err1 + err2 * err2;
249 //double chiDenominator = std::pow(graph1->GetErrorYhigh(index1), 2) + std::pow(graph1->GetErrorYlow(index1), 2);
250 chi += chiNumerator / chiDenominator;
251 ndof++;
252 continue;
253 }
254 }
255 }
256 return TMath::Prob(chi, ndof);
257}
258
260{
262
263 const auto exprun = getRunList()[0];
264 B2INFO("ExpRun used for DB Geometry : " << exprun.first << " " << exprun.second);
265 updateDBObjPtrs(1, exprun.second, exprun.first);
266
268
269 bool enoughData = hasEnoughData();
270 if (not enoughData)
271 return c_NotEnoughData;
272
275 m_badWireList->outputToFile("wireFile.txt");
276 saveCalibration(m_badWireList, "CDCBadWires");
277 return c_OK;
278}
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.