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 B2INFO("Creating efficiencies for every layer");
36
37 unsigned short wireID;
38 unsigned short layerID;
39 float z;
40 bool isFound;
41
42 auto efftree = getObjectPtr<TTree>("efftree");
43 efftree->SetBranchAddress("wireID", &wireID);
44 efftree->SetBranchAddress("layerID", &layerID);
45 efftree->SetBranchAddress("z", &z);
46 efftree->SetBranchAddress("isFound", &isFound);
47
48 // Set ranges and names for the TEfficiency objects
49 B2INFO("Building empty efficiency objects");
50 static const CDCWireTopology& wireTopology = CDCWireTopology::getInstance();
51 B2INFO("Wire Topology found");
52 static const CDCGeometryPar& cdcgeo = CDCGeometryPar::Instance();
53 B2INFO("CDC Geometry found");
54
55 for (const CDCWireLayer& wireLayer : wireTopology.getWireLayers()) {
56 unsigned short layerNo = wireLayer.getICLayer();
57 //B2INFO("Got layer " << layerNo);
58 //std::string m_nameOfLayer = std::string("effLayer_").append(std::to_string(layerNo));
59 //std::string m_titleOfLayer = std::string("Efficiency of wires in layer ").append(std::to_string(layerNo));
60 //B2INFO("Built names for " << layerNo);
61
62 unsigned short nzbins = 30 - layerNo / 7;
63 unsigned short nwidbins = cdcgeo.nWiresInLayer(layerNo);
64 double widbins[2] = { -0.5, cdcgeo.nWiresInLayer(layerNo) - 0.5};
65 double zbins[2] = {wireLayer.getBackwardZ(), wireLayer.getForwardZ()};
66
67 TEfficiency* effInLayer = new TEfficiency(Form("effLayer_%d", layerNo), Form("Hit efficiency pf L%d ; z (cm) ; IWire ", layerNo),
68 nzbins, zbins[0], zbins[1], nwidbins, widbins[0], widbins[1]);
69 m_efficiencyList->Add(effInLayer);
70 B2INFO("Teff for layer " << layerNo << " was successfully listed.");
71 }
72 TFile* outputCollection = new TFile("LayerEfficiencies.root", "RECREATE");
73
74 // loop over entries in the tree and build the TEfficiencies
75 B2INFO("Filling the efficiencies");
76 const Long64_t nEntries = efftree->GetEntries();
77 B2INFO("Number of entries in tree: " << nEntries);
78 for (Long64_t i = 0; i < nEntries; i++) {
79 efftree->GetEntry(i);
80 TEfficiency* efficiencyInLayer = (TEfficiency*)m_efficiencyList->At(layerID);
81 efficiencyInLayer->Fill(isFound, z, wireID);
82 }
83 m_efficiencyList->Write();
84 B2INFO("TEfficiencies successfully filled.");
85 outputCollection->Close();
86 B2INFO("TEfficiencies successfully saved");
87 // setting 1000 entries as the minimum value to consider "enough data", but this might need to be tailored in the future.
88 if (nEntries > 1000) return true;
89 else return false;
90}
91
93{
94 B2INFO("Beginning detection of bad wires");
95 static const CDCWireTopology& wireTopology = CDCWireTopology::getInstance();
96 B2INFO("Wire Topology found");
97
98 for (const CDCWireLayer& wireLayer : wireTopology.getWireLayers()) {
99 unsigned short layerNo = wireLayer.getICLayer();
100 B2INFO("Checking layer " << layerNo);
101
102 // need to use casting here because GetPassedHistogram assumes it returns TH1
103 auto efficiencyInLayer = (TEfficiency*)m_efficiencyList->At(layerNo);
104 auto passed = (TH2F*)efficiencyInLayer->GetPassedHistogram();
105 auto total = (TH2F*)efficiencyInLayer->GetTotalHistogram();
106
107 // Ignoring layers that have no hits at all
108 if (!total->GetEntries()) continue;
109
110 // prepare fitting functions
111 double minFitRange = wireLayer.getBackwardZ() + 30;
112 double maxFitRange = wireLayer.getForwardZ() - 30;
113 TF1* constantFunction = new TF1("constantFunction", "pol0", minFitRange, maxFitRange);
114
115 // Estimate average efficiency of the layer as function of z
116 auto passedProjectedX = passed->ProjectionX("projectionx1");
117 auto totalProjectedX = total->ProjectionX("projectionx2");
118 TEfficiency* efficiencyProjectedX = new TEfficiency(*passedProjectedX, *totalProjectedX);
119
120 unsigned short minFitBin = passedProjectedX->FindBin(minFitRange) + 1;
121 unsigned short maxFitBin = passedProjectedX->FindBin(maxFitRange) - 1;
122
123 // Estimate average efficiency of each wire in the layer
124 auto passedProjectedY = passed->ProjectionY("projectiony1", minFitRange, maxFitRange);
125 auto totalProjectedY = total->ProjectionY("projectiony2", minFitRange, maxFitRange);
126 TEfficiency* efficiencyProjectedY = new TEfficiency(*passedProjectedY, *totalProjectedY);
127
128 // Estimate average efficiency of the whole layer by summing up averages of individual wires
129 float totalAverage = 0;
130 int nonZeroWires = 0;
131 for (int i = 0; i <= passedProjectedY->GetNbinsX(); ++i) {
132 float efficiencyAtBin = efficiencyProjectedY->GetEfficiency(i);
133 if (efficiencyAtBin > 0.2) {
134 totalAverage += efficiencyAtBin;
135 nonZeroWires++;
136 }
137 }
138 // safeguard in case all wires are 0
139 nonZeroWires > 0 ? totalAverage /= nonZeroWires : totalAverage == 0;
140
141 TGraphAsymmErrors* graphEfficiencyProjected = efficiencyProjectedX->CreateGraph();
142
143 B2INFO("Successfully determined average properties of layer " << layerNo);
144 B2INFO("Looping over wires");
145
146 // Due to the way histograms are binned, every bin center should correspond a wire ID.
147 for (int i = 0; i <= passed->GetNbinsY(); ++i) {
148
149 // project out 1 wire
150 auto singleWirePassed = passed->ProjectionX("single wire projection passed", i, i);
151 auto singleWireTotal = total->ProjectionX("single wire projection total", i, i);
152
153 // if not a single hit within the central area of wire then mark it as bad
154 if (!singleWirePassed->Integral(minFitBin, maxFitBin)) {
155 double wireID = passed->GetYaxis()->GetBinCenter(i);
156 unsigned short maxWires = CDCGeometryPar::Instance().nWiresInLayer(layerNo);
157 if (wireID < 0 || wireID >= maxWires) {
158 B2ERROR("Invalid wireID: " << wireID << " for LayerID: " << layerNo
159 << ". Max wires: " << maxWires);
160 continue; // remove invalid wireID
161 }
162 m_badWireList->setWire(layerNo, round(wireID), 0);
163 continue;
164 }
165 // constructs efficiency of one wire
166 TEfficiency* singleWireEfficiency = new TEfficiency(*singleWirePassed, *singleWireTotal);
167 TGraphAsymmErrors* graphSingleWireEfficiency = singleWireEfficiency->CreateGraph();
168
169 // fits the efficiency with a constant
170 TFitResultPtr singleWireFitResults = graphSingleWireEfficiency->Fit(constantFunction, "SQR");
171 double singleWireEfficiencyFromFit = (singleWireFitResults.Get())->Value(0);
172 double singleWireUpErrorFromFit = (singleWireFitResults.Get())->UpperError(0);
173
174 // applies a chi test on the wire
175 float p_value = chiTest(graphSingleWireEfficiency, graphEfficiencyProjected, minFitRange, maxFitRange);
176 // check three conditions and decide if wire should be marked as bad
177 bool averageCondition = (0.95 * totalAverage) > (singleWireEfficiencyFromFit + 3 * singleWireUpErrorFromFit);
178 bool pvalueCondition = p_value < 0.01;
179 bool generalCondition = singleWireEfficiencyFromFit < 0.4;
180 if (generalCondition || (averageCondition && pvalueCondition)) {
181 double wireID = passed->GetYaxis()->GetBinCenter(i);
182 m_badWireList->setWire(layerNo, round(wireID), singleWireEfficiencyFromFit);
183 }
184 }
185 B2INFO("Bad wires for " << layerNo << " recorded");
186 }
187 m_badWireList->outputToFile("wireFile.txt");
188 saveCalibration(m_badWireList, "CDCBadWires");
189 B2INFO("Bad wire list successfully saved.");
190}
191
192double WireEfficiencyAlgorithm::chiTest(TGraphAsymmErrors* graph1, TGraphAsymmErrors* graph2, double minValue, double maxValue)
193{
194
195 // Vars to perform the chi test
196 double chi = 0;
197 unsigned short ndof = 0;
198
199
200 int numOfEntries1 = graph1->GetN();
201 int numOfEntries2 = graph2->GetN();
202
203 // loop over entries in both graphs, index by index.
204
205 for (int index1 = 0; index1 < numOfEntries1; ++index1) {
206 // TGraph values are usually not listed in increasing order. Need to check that they are within min/max range for comparison.
207 if (graph1->GetX()[index1] < minValue) continue;
208 if (graph1->GetX()[index1] > maxValue) continue;
209 for (int index2 = 0; index2 < numOfEntries2; ++index2) {
210 if (graph1->GetX()[index1] == graph2->GetX()[index2]) {
211 // this is broken up just for readability
212 double chiNumerator = pow(graph1->GetY()[index1] - graph2->GetY()[index2], 2);
213 double chiDenominator = pow(graph1->GetErrorYhigh(index1), 2) + pow(graph1->GetErrorYlow(index1), 2);
214 chi += chiNumerator / chiDenominator;
215 ndof++;
216 continue;
217 }
218 }
219 }
220 return TMath::Prob(chi, ndof);
221}
222
224{
225 const auto exprun = getRunList()[0];
226 B2INFO("ExpRun used for DB Geometry : " << exprun.first << " " << exprun.second);
227 updateDBObjPtrs(1, exprun.second, exprun.first);
228
229 B2INFO("Creating CDCGeometryPar object");
231
232 bool enoughEventsInInput;
233 enoughEventsInInput = buildEfficiencies();
234
236
237 if (enoughEventsInInput) return c_OK;
238 else return c_NotEnoughData;
239}
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.
bool buildEfficiencies()
create 2D TEfficiency for each wire and return True if more than 1000 entries
void saveCalibration(TClonesArray *data, const std::string &name)
Store DBArray payload with given name with default IOV.
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.