45 m_fitparams{{{ -0.6, 4, 52, -0.145075, 0.885072, 0.386489, -1.361947, 0.662144, 7.134312, 0.449013, 27.397423},
46 { -0.5, 4, 51, -0.082732, 0.795969, 0.674888, -1.281492, 0.232786, 6.784821, 0.452656, 26.333126},
47 { -0.4, 5, 50, -0.071727, 0.828312, 0.657696, -1.228757, 0.297860, 7.371104, 0.514466, 24.651866},
48 { -0.3, 5, 49, -0.071360, 0.833236, 0.628764, -1.167383, 0.418553, 7.939829, 0.421581, 23.109511},
49 { -0.2, 6, 49, -0.075286, 0.850655, 0.631255, -1.187544, 0.526128, 8.469763, 0.345813, 21.632224},
50 { -0.1, 6, 48, -0.084704, 0.888931, 0.598693, -1.350362, 0.595812, 9.081677, 0.433728, 20.394818},
51 { 0.0, 7, 48, -0.091469, 0.859441, 0.587823, -1.244311, 0.641963, 9.767411, 0.364095, 18.956789},
52 { 0.1, 7, 48, -0.100383, 0.941895, 0.548498, -1.434151, 0.806756, 10.400005, 0.382099, 17.762602},
53 { 0.2, 9, 47, -0.108262, 0.917920, 0.531122, -1.417409, 0.875909, 11.205481, 0.355874, 16.347529},
54 { 0.3, 9, 48, -0.125444, 1.024868, 0.493756, -1.684685, 1.010871, 11.895900, 0.330729, 15.227423},
55 { 0.4, 10, 48, -0.140058, 1.022069, 0.458708, -1.517339, 1.246522, 13.112702, 0.366439, 13.597788},
56 { 0.5, 12, 47, -0.144628, 1.201504, 0.450720, -2.059850, 1.280475, 13.949971, 0.278829, 12.030733},
57 { 0.6, 13, 48, -0.156869, 1.095534, 0.451224, -1.872655, 1.412490, 15.791843, 0.292740, 9.729090},
58 { 0.7, 15, 50, -0.167605, 2.117748, 0.352186, -1.999952, 3.009412, 17.997337, 0.332440, 6.500688},
59 { 0.8, 19, 51, -0.232060, 0.726019, 0.581841, -0.142827, 1.205148, 24.876122, 0.457538, 1.328250}}}
63 setDescription(R
"DOC(A module to save timing of neutral cluster events derived from nearby TOP interactions)DOC");
65 addParam("saveFits", m_saveFits,
66 "Set to true to save RooPlot of fit to TOP signal from which the timing is extracted, for debug.",
68 addParam(
"minClusterE", m_minClusterE,
69 "Minimum (incl.) cluster energy to be considered in adding relation to TOP time extraction [GeV]",
71 addParam(
"minNphotons", m_minNphotons,
72 "Minimum (incl.) no. of Cherenkov photons measured in TOP slot for which time extraction will take place",
74 addParam(
"minClusterNHits", m_minClusterNHits,
75 "Minimum (incl.) no. of crystals in cluster required for adding relation to TOP time extraction",
77 addParam(
"includeSlotsWithTracks", m_includeSlotsWithTracks,
78 "Flag to allow fitting of neutral cluster TOP signal even if there are charged tracks matched to the same slot",
80 addParam(
"saveMoreFitParams", m_saveMoreFitParams,
81 "Flag to save additonal parameters from TOP timing fit (e.g. RooFit errors) to output, for debug.",
87 RooMsgService::instance().setGlobalKillBelow(RooFit::WARNING);
93 std::string xname =
"x_" + std::to_string(row[0]);
94 RooRealVar x(
"x", xname.c_str(), -10, 80);
95 RooRealVar mu(
"mu",
"mu", row[8], -9, 80);
96 RooRealVar gamma(
"gamma",
"gamma", row[6], row[6], row[6]);
97 RooRealVar lambda(
"lambda",
"lambda", row[7], row[7], row[7]);
98 RooRealVar delta(
"delta",
"delta", row[4], row[4], row[4]);
101 RooJohnson john(
"john",
"john", x, mu, lambda, gamma, delta, 0);
103 RooRealVar shift_minus_mu(
"shift_minus_mu",
"shift_minus_mu", row[10], 0., 30.);
104 RooRealVar coeff(
"coeff",
"coeff", row[3], row[3], row[3]);
105 RooRealVar w(
"w",
"w", row[9], row[9], row[9]);
106 RooRealVar frac(
"frac",
"frac", row[5], 0.1, 1.);
109 gamma.setConstant(
true);
110 lambda.setConstant(
true);
111 delta.setConstant(
true);
112 shift_minus_mu.setConstant(
true);
113 coeff.setConstant(
true);
116 RooFormulaVar shift(
"shift",
"@0+@1", RooArgList(mu, shift_minus_mu));
117 RooFormulaVar xshifted(
"xshifted",
"@0-@1", RooArgList(x, shift));
118 RooExponential exp(
"exp",
"exp", xshifted, coeff);
119 RooGenericPdf smooth_step(
"smooth_step",
"1.0/(1.0 + exp((-@0)/@1))", RooArgList(xshifted, w));
122 RooProdPdf smoothedexp(
"smoothedexp",
"exp * smooth_step", RooArgList(exp, smooth_step));
125 RooAddPdf sgnModel(
"sgnModel",
"signal model", john, smoothedexp, frac);
127 RooRealVar slope(
"slope",
"slope", 0);
128 slope.setConstant(
true);
129 RooPolynomial bkgModel(
"bkgModel",
"bkgModel", x, RooArgList(slope));
131 RooRealVar sigPhotons(
"sigPhotons",
"sigPhotons", 50, 0, 500);
132 RooRealVar bkgPhotons(
"bkgPhotons",
"bkgPhotons", 50, 0, 500);
135 std::string modelname =
"model_" + std::to_string(row[0]);
136 RooAddPdf model(
"model", modelname.c_str(), RooArgList(sgnModel, bkgModel), RooArgList(sigPhotons, bkgPhotons));
138 std::string wsname =
"ws_" + std::to_string(row[0]);
139 RooWorkspace ws(
"ws", wsname.c_str());
157 RooRealVar* x, RooDataSet data, RooFitResult* res,
int nTracksPerSlot)
160 int cosThetaClusterIndex = int(std::round(cosTheta * 10));
161 double nearestClusterCosTheta = cosThetaClusterIndex / 10.0;
162 cosThetaClusterIndex += 6;
166 TCanvas* c =
new TCanvas(
"c",
"Johnson SU fit", 900, 900);
168 std::string rooPlotTitle =
"evtNo: " + std::to_string(eventMetaData->getEvent()) +
" runNo.: " + std::to_string(
169 eventMetaData->getRun()) +
" clusterE: " + std::to_string(
170 clusterE) +
" slot: " + std::to_string(moduleID) +
" fitted cosTheta: " + std::to_string(nearestClusterCosTheta) +
171 " no. of tracks in slot: " + std::to_string(nTracksPerSlot);
173 RooPlot* frame = x->frame(RooFit::Title(rooPlotTitle.c_str()));
174 data.plotOn(frame, RooFit::Binning(binning));
175 model->plotOn(frame);
178 RooFit::MoveToBack(),
179 RooFit::Components(
"john"),
180 RooFit::LineColor(kCyan));
183 RooFit::MoveToBack(),
184 RooFit::Components(
"smoothedexp"),
185 RooFit::LineColor(kSpring));
188 double redchisq = frame->chiSquare(res->floatParsFinal().getSize());
194 TPaveText* box =
new TPaveText(0.7, 0.65, 0.98, 0.92,
"NDC");
195 box->SetFillColor(0);
196 box->SetBorderSize(1);
199 int nPhotons = data.numEntries();
200 box->AddText(Form(
"nPhotons=%d", nPhotons));
204 for (
int i = 0; i < res->floatParsFinal().getSize(); ++i) {
205 RooRealVar* p = (RooRealVar*)res->floatParsFinal().at(i);
206 box->AddText(Form(
"%s = %.3f ± %.3f",
210 if ((std::string)p->GetName() ==
"mu") {
211 xpeak1 = p->getVal();
215 TLine* xpeak1line =
new TLine(xpeak1, 0, xpeak1, 1e6);
216 xpeak1line->SetLineStyle(2);
217 xpeak1line->SetLineColor(kRed);
218 xpeak1line->SetLineWidth(5);
222 box->AddText(
"Results");
223 box->AddText(Form(
"forward peak: %.2f ns", xpeak1));
224 box->AddText(Form(
"redchisq = %.4f", redchisq));
226 box->SetTextSizePixels(50);
230 std::string roofitname =
"TOPBackSplashFit_evtNo_" + std::to_string(eventMetaData->getEvent()) +
"_runNo_" + std::to_string(
231 eventMetaData->getRun()) +
"_clusterE_" + std::to_string(
232 clusterE) +
"_slot_" + std::to_string(moduleID) +
"_cosTheta_" + std::to_string(
233 nearestClusterCosTheta) +
".png";
234 c->SaveAs(roofitname.c_str());
238 std::vector<const TOPDigit*>& digitsPerSlot,
double clusterE,
double clusterCosTheta,
int nTracksPerSlot)
244 initLogCapture.
start();
247 int cosThetaIndex = int(std::round(clusterCosTheta * 10));
252 if (cosThetaIndex >=
int(
m_wss.size())) {
256 RooRealVar* x =
m_wss[cosThetaIndex].var(
"x");
258 RooDataSet data(
"data",
"unbinned", RooArgSet(*x));
259 for (
auto digit : digitsPerSlot) {
260 double v = digit->getTime();
262 data.add(RooArgSet(*x));
269 double clusterSinTheta = TMath::Sqrt(1 - clusterCosTheta * clusterCosTheta);
270 double zeta = 119 * (clusterCosTheta / clusterSinTheta) + 83.;
275 double minTime = 120 / (30 * clusterSinTheta) + zeta / 20.;
276 double maxTime = 120 / (3 * clusterSinTheta) + 1.4 * zeta / 20.;
277 double shift = 2 * (270 - zeta) / 20;
279 m_wss[cosThetaIndex].var(
"sigPhotons")->setVal(data.numEntries() * 0.8);
280 m_wss[cosThetaIndex].var(
"sigPhotons")->setMin(data.numEntries() * 0.2);
281 m_wss[cosThetaIndex].var(
"sigPhotons")->setMax(data.numEntries() * 1.1);
283 m_wss[cosThetaIndex].var(
"bkgPhotons")->setVal(data.numEntries() * 0.2);
284 m_wss[cosThetaIndex].var(
"bkgPhotons")->setMin(data.numEntries() * 0.);
285 m_wss[cosThetaIndex].var(
"bkgPhotons")->setMax(data.numEntries() * 1.1);
287 m_wss[cosThetaIndex].var(
"shift_minus_mu")->setVal(shift);
288 m_wss[cosThetaIndex].var(
"shift_minus_mu")->setMin(0.);
289 m_wss[cosThetaIndex].var(
"shift_minus_mu")->setMax(shift * 1.3);
290 m_wss[cosThetaIndex].var(
"shift_minus_mu")->setConstant(
false);
291 m_wss[cosThetaIndex].var(
"mu")->setVal(0.5 * (minTime + maxTime));
292 m_wss[cosThetaIndex].var(
"mu")->setMin(minTime);
293 m_wss[cosThetaIndex].var(
"mu")->setMax(maxTime);
295 RooAbsPdf* model =
m_wss[cosThetaIndex].pdf(
"model");
297 RooFitResult* res = model->fitTo(data, RooFit::Save(), RooFit::PrintLevel(-1), RooFit::Strategy(0), RooFit::Extended());
299 if (res->status() > 0) {
303 double res_time =
m_wss[cosThetaIndex].var(
"mu")->getValV();
304 double res_timeErr =
m_wss[cosThetaIndex].var(
"mu")->getError();
305 double res_deltaT =
m_wss[cosThetaIndex].var(
"shift_minus_mu")->getValV();
306 double res_deltaTErr =
m_wss[cosThetaIndex].var(
"shift_minus_mu")->getError();
307 double res_frac =
m_wss[cosThetaIndex].var(
"frac")->getValV();
308 double res_fracErr =
m_wss[cosThetaIndex].var(
"frac")->getError();
309 double res_signalPhotons =
m_wss[cosThetaIndex].var(
"sigPhotons")->getValV();
310 double res_signalPhotonsErr =
m_wss[cosThetaIndex].var(
"sigPhotons")->getError();
315 RooPlot* frame = x->frame();
316 data.plotOn(frame, RooFit::Binning(binning));
317 model->plotOn(frame);
318 int nfloatParsFinal = res->floatParsFinal().getSize();
319 double redchisq = frame->chiSquare(nfloatParsFinal);
322 makePlot(clusterCosTheta, clusterE, moduleID, model, x, data, res, nTracksPerSlot);
330 res_signalPhotons, res_signalPhotonsErr, redchisq, data.sumEntries(), nTracksPerSlot);
357 int nTracksPerSlots[16] = {0};
358 for (
const auto& track :
m_tracks) {
360 if (not tl)
continue;
362 if (not te)
continue;
363 int slotID = te->getCopyID();
365 nTracksPerSlots[slotID - 1]++;
369 std::array<std::vector<const TOPDigit*>, 16> digitsPerSlots;
371 if (digi.getHitQuality() != TOPDigit::c_Good) {
374 if (digi.getTime() > 0 && digi.getTime() < 80) {
375 digitsPerSlots[ int(digi.getModuleID()) - 1].push_back(&digi);
385 if (cluster.getTheta() > 31.0 * M_PI / 180.0 && cluster.getTheta() < 128.0 * M_PI / 180.0 &&
386 cluster.getDetectorRegion() == 2 &&
388 cluster.isTrack() ==
false &&
393 double phi = cluster.getPhi();
399 if (
int(digitsPerSlots[moduleID - 1].size()) <
m_minNphotons) {
407 auto* fitresult =
fitTimingDigits(moduleID, digitsPerSlots[moduleID - 1],
408 clusterE, std::cos(cluster.getTheta()), nTracksPerSlots[moduleID - 1]);
411 fitresult->addRelationTo(&cluster);