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