10 #include <ecl/modules/eclWaveformFit/ECLWaveformFit.h>
13 #include <ecl/digitization/EclConfiguration.h>
14 #include <ecl/digitization/shaperdsp.h>
17 #include <framework/core/Environment.h>
22 #include <TMatrixDSym.h>
23 #include <TDecompChol.h>
43 void ecl_waveform_fit_load_inverse_covariance(
const float* packed_matrix);
48 void ecl_waveform_fit_multiply_inverse_covariance(
double* y,
const double* x);
57 const int c_NFitPoints = 31;
60 const int c_NFitPointsVector = 32;
63 double fitA[c_NFitPoints];
76 void FCN2h(
int&,
double* grad,
double& f,
double* p,
int)
78 double df[c_NFitPointsVector];
79 double da[c_NFitPointsVector];
80 const double Ag = p[1], B = p[0], T = p[2], Ah = p[3];
81 double chi2 = 0, gAg = 0, gB = 0, gT = 0, gAh = 0;
84 double amplitudeGamma[c_NFitPoints], derivativesGamma[c_NFitPoints];
85 double amplitudeHadron[c_NFitPoints], derivativesHadron[c_NFitPoints];
86 g_PhotonSignal->
getShape(T, amplitudeGamma, derivativesGamma);
87 g_HadronSignal->
getShape(T, amplitudeHadron, derivativesHadron);
91 for (
int i = 0; i < c_NFitPoints; ++i)
92 df[i] = fitA[i] - (Ag * amplitudeGamma[i] + Ah * amplitudeHadron[i] + B);
95 ecl_waveform_fit_multiply_inverse_covariance(da, df);
97 #pragma omp simd reduction(+:chi2) reduction(-:gB,gAg,gT,gAh)
98 for (
int i = 0; i < c_NFitPoints; ++i) {
99 chi2 += da[i] * df[i];
101 gAg -= da[i] * amplitudeGamma[i];
102 gT -= da[i] * (derivativesGamma[i] * Ag + derivativesHadron[i] * Ah);
103 gAh -= da[i] * amplitudeHadron[i];
115 void FCN2h2(
int&,
double* grad,
double& f,
double* p,
int)
117 double df[c_NFitPointsVector];
118 double da[c_NFitPointsVector];
119 const double A2 = p[4], T2 = p[5];
120 const double Ag = p[1], B = p[0], T = p[2], Ah = p[3];
121 double chi2 = 0, gA2 = 0, gT2 = 0;
122 double gAg = 0, gB = 0, gT = 0, gAh = 0;
125 double amplitudeGamma[c_NFitPoints], derivativesGamma[c_NFitPoints];
126 double amplitudeGamma2[c_NFitPoints], derivativesGamma2[c_NFitPoints];
127 double amplitudeHadron[c_NFitPoints], derivativesHadron[c_NFitPoints];
128 g_PhotonSignal->
getShape(T, amplitudeGamma, derivativesGamma);
130 g_PhotonSignal->
getShape(T2, amplitudeGamma2, derivativesGamma2);
131 g_HadronSignal->
getShape(T, amplitudeHadron, derivativesHadron);
135 for (
int i = 0; i < c_NFitPoints; ++i) {
136 df[i] = fitA[i] - (Ag * amplitudeGamma[i] + Ah * amplitudeHadron[i]
137 + A2 * amplitudeGamma2[i] + B);
141 ecl_waveform_fit_multiply_inverse_covariance(da, df);
143 #pragma omp simd reduction(+:chi2) reduction(-:gB,gAg,gT,gAh,gA2,gT2)
144 for (
int i = 0; i < c_NFitPoints; ++i) {
145 chi2 += da[i] * df[i];
147 gAg -= da[i] * amplitudeGamma[i];
148 gAh -= da[i] * amplitudeHadron[i];
149 gT -= da[i] * (derivativesGamma[i] * Ag + derivativesHadron[i] * Ah);
151 gA2 -= da[i] * amplitudeGamma2[i];
152 gT2 -= da[i] * derivativesGamma2[i] * A2;
165 void regularize(
double* dst,
const double* src,
const int n,
const double u0 = 13.0,
const double u1 = 0.8)
167 for (
int k = 0; k < n; k++) dst[k] = src[k] / (1 + exp((k - u0) / u1));
171 bool makecovariance(
CovariancePacked& M,
const int nnoise,
const double* acov)
173 TMatrixDSym
E(c_NFitPoints);
174 for (
int i = 0; i < c_NFitPoints; i++)
175 for (
int j = 0; j < i + 1; j++)
176 if (i - j < nnoise)
E(i, j) =
E(j, i) = acov[i - j];
179 const bool status = dc.Invert(
E);
183 for (
int i = 0; i < c_NFitPoints; i++)
184 for (
int j = 0; j < i + 1; j++)
185 M[count++] =
E(i, j);
186 M.
sigma = sqrtf(acov[0]);
197 setDescription(
"Module to fit offline waveforms and measure hadron scintillation component light output.");
198 setPropertyFlags(c_ParallelProcessingCertified);
199 addParam(
"EnergyThreshold", m_EnergyThreshold,
"Energy threshold of online fit result for Fitting Waveforms (GeV).", 0.03);
200 addParam(
"Chi2Threshold25dof", m_chi2Threshold25dof,
"chi2 threshold (25 dof) to classify offline fit as good fit.", 57.1);
201 addParam(
"Chi2Threshold27dof", m_chi2Threshold27dof,
"chi2 threshold (27 dof) to classify offline fit as good fit.", 60.0);
202 addParam(
"CovarianceMatrix", m_CovarianceMatrix,
203 "Option to use crystal dependent covariance matrices (false uses identity matrix).",
true);
215 m_TemplatesLoaded =
true;
217 if (m_IsMCFlag == 0) {
219 std::vector<double> Ptemp(11), Htemp(11), Dtemp(11);
221 for (
int j = 0; j < 11; j++) {
222 Ptemp[j] = (double)m_WaveformParameters->getPhotonParameters(i + 1)[j];
223 Htemp[j] = (double)m_WaveformParameters->getHadronParameters(i + 1)[j];
224 Dtemp[j] = (double)m_WaveformParameters->getDiodeParameters(i + 1)[j];
232 std::vector<double> Ptemp(11), Htemp(11), Dtemp(11);
233 for (
int j = 0; j < 11; j++) {
234 Ptemp[j] = (double)m_WaveformParametersForMC->getPhotonParameters()[j];
235 Htemp[j] = (double)m_WaveformParametersForMC->getHadronParameters()[j];
236 Dtemp[j] = (double)m_WaveformParametersForMC->getDiodeParameters()[j];
248 m_TemplatesLoaded =
false;
251 if (m_CrystalElectronics.isValid()) {
253 m_ADCtoEnergy[i] = m_CrystalElectronics->getCalibVector()[i];
255 if (m_CrystalEnergy.isValid()) {
257 m_ADCtoEnergy[i] *= m_CrystalEnergy->getCalibVector()[i];
261 if (m_CovarianceMatrix) {
263 double buf[c_NFitPoints];
264 double reg[c_NFitPoints];
265 m_AutoCovariance->getAutoCovariance(
id, buf);
266 double x0 = c_NFitPoints;
267 std::memcpy(reg, buf, c_NFitPoints *
sizeof(
double));
268 while (!makecovariance(m_c[
id - 1], c_NFitPoints, reg))
269 regularize(buf, reg, c_NFitPoints, x0 -= 1, 1);
273 double defaultCovariance[c_NFitPoints][c_NFitPoints];
275 const double isigma = 1 / 7.5;
276 for (
int i = 0; i < c_NFitPoints; ++i) {
277 for (
int j = 0; j < c_NFitPoints; ++j) {
278 defaultCovariance[i][j] = (i == j) * isigma * isigma;
282 for (
int i = 0; i < c_NFitPoints; i++) {
283 for (
int j = 0; j < i + 1; j++) {
284 packedDefaultCovariance.
m_covMatPacked[k] = defaultCovariance[i][j];
288 ecl_waveform_fit_load_inverse_covariance(
297 m_eclDSPs.registerInDataStore();
298 m_eclDigits.registerInDataStore();
301 m_Minit2h =
new TMinuit(4);
302 m_Minit2h->SetFCN(FCN2h);
306 m_Minit2h->mnexcm(
"SET PRIntout", arglist, 1, ierflg);
307 m_Minit2h->mnexcm(
"SET NOWarnings", arglist, 0, ierflg);
309 m_Minit2h->mnexcm(
"SET ERR", arglist, 1, ierflg);
311 m_Minit2h->mnexcm(
"SET STRategy", arglist, 1, ierflg);
313 m_Minit2h->mnexcm(
"SET GRAdient", arglist, 1, ierflg);
315 m_Minit2h->mnexcm(
"SET EPSmachine", arglist, 1, ierflg);
318 m_Minit2h2 =
new TMinuit(6);
319 m_Minit2h2->SetFCN(FCN2h2);
321 m_Minit2h2->mnexcm(
"SET PRIntout", arglist, 1, ierflg);
322 m_Minit2h2->mnexcm(
"SET NOWarnings", arglist, 0, ierflg);
324 m_Minit2h2->mnexcm(
"SET ERR", arglist, 1, ierflg);
326 m_Minit2h2->mnexcm(
"SET STRategy", arglist, 1, ierflg);
328 m_Minit2h2->mnexcm(
"SET GRAdient", arglist, 1, ierflg);
330 m_Minit2h2->mnexcm(
"SET EPSmachine", arglist, 1, ierflg);
333 m_TemplatesLoaded =
false;
340 if (!m_TemplatesLoaded) {
342 if (m_eclDSPs.getEntries() > 0) loadTemplateParameterArray();
345 for (
auto& aECLDsp : m_eclDSPs) {
347 aECLDsp.setTwoComponentTotalAmp(-1);
348 aECLDsp.setTwoComponentHadronAmp(-1);
349 aECLDsp.setTwoComponentDiodeAmp(-1);
350 aECLDsp.setTwoComponentChi2(-1);
354 aECLDsp.setTwoComponentTime(-1);
355 aECLDsp.setTwoComponentBaseline(-1);
358 const int id = aECLDsp.getCellId() - 1;
361 for (
int j = 0; j < ec.
m_nsmp; j++)
362 fitA[j] = aECLDsp.getDspA()[j];
366 for (
const auto& aECLDigit : m_eclDigits) {
367 if (aECLDigit.getCellId() - 1 ==
id) {
369 aECLDsp.addRelationTo(&aECLDigit);
373 if (d ==
nullptr)
continue;
376 if (d->getAmp() * m_ADCtoEnergy[
id] < m_EnergyThreshold)
continue;
379 if (m_IsMCFlag == 0) {
381 g_PhotonSignal = &m_si[id][0];
382 g_HadronSignal = &m_si[id][1];
385 g_PhotonSignal = &m_si[0][0];
386 g_HadronSignal = &m_si[0][1];
390 if (m_CovarianceMatrix) {
391 ecl_waveform_fit_load_inverse_covariance(m_c[
id].m_covMatPacked);
392 aNoise = m_c[id].sigma;
396 double p2_b, p2_a, p2_t, p2_a1, p2_chi2, p_extraPhotonEnergy, p_extraPhotonTime;
399 Fit2h(p2_b, p2_a, p2_t, p2_a1, p2_chi2);
403 if (p2_chi2 >= m_chi2Threshold27dof) {
407 Fit2hExtraPhoton(p2_b, p2_a, p2_t, p2_a1, p_extraPhotonEnergy, p_extraPhotonTime, p2_chi2);
411 if (p2_chi2 >= m_chi2Threshold25dof) {
412 g_HadronSignal = &m_si[0][2];
415 Fit2h(p2_b, p2_a, p2_t, p2_a1, p2_chi2);
424 aECLDsp.setTwoComponentTotalAmp(p2_a + p2_a1);
426 aECLDsp.setTwoComponentHadronAmp(0.0);
427 aECLDsp.setTwoComponentDiodeAmp(p2_a1);
429 aECLDsp.setTwoComponentHadronAmp(p2_a1);
430 aECLDsp.setTwoComponentDiodeAmp(0.0);
432 aECLDsp.setTwoComponentChi2(p2_chi2);
433 aECLDsp.setTwoComponentTime(p2_t);
434 aECLDsp.setTwoComponentBaseline(p2_b);
435 aECLDsp.setTwoComponentFitType(fitType);
437 aECLDsp.setbackgroundPhotonEnergy(p_extraPhotonEnergy);
438 aECLDsp.setbackgroundPhotonTime(p_extraPhotonTime);
464 for (
int j = 0; j < c_NFitPoints; j++)
if (amax < fitA[j]) { amax = fitA[j]; jmax = j;}
465 double sumB0 = 0;
int jsum = 0;
466 for (
int j = 0; j < c_NFitPoints; j++)
if (j < jmax - 3 || jmax + 4 < j) { sumB0 += fitA[j]; ++jsum;}
467 double B0 = sumB0 / jsum;
469 if (amax < 0) amax = 10;
470 double T0 = dt * (4.5 - jmax);
474 m_Minit2h->mnparm(0,
"B", B0, 10, B0 / 1.5, B0 * 1.5, ierflg);
475 m_Minit2h->mnparm(1,
"Ag", A0, A0 / 20, 0, 2 * A0, ierflg);
476 m_Minit2h->mnparm(2,
"T", T0, 0.5, T0 - 2.5, T0 + 2.5, ierflg);
477 m_Minit2h->mnparm(3,
"Ah", 0., A0 / 20, -A0, 2 * A0, ierflg);
482 m_Minit2h->mnexcm(
"MIGRAD", arglist, 2, ierflg);
485 int nvpar, nparx, icstat;
486 m_Minit2h->mnstat(amin, edm, errdef, nvpar, nparx, icstat);
490 m_Minit2h->GetParameter(0, B, ep);
491 m_Minit2h->GetParameter(1, Ag, ep);
492 m_Minit2h->GetParameter(2, T, ep);
493 m_Minit2h->GetParameter(3, Ah, ep);
502 double amax = 0;
int jmax = 6;
503 for (
int j = 0; j < c_NFitPoints; j++)
if (amax < fitA[j]) { amax = fitA[j]; jmax = j;}
505 double amax1 = 0;
int jmax1 = 6;
506 for (
int j = 0; j < c_NFitPoints; j++)
507 if (j < jmax - 3 || jmax + 4 < j) {
509 if (amax1 < fitA[j] && fitA[j + 1] < fitA[j]) { amax1 = fitA[j]; jmax1 = j;}
510 }
else if (j == 30) {
511 if (amax1 < fitA[j] && fitA[j - 1] < fitA[j]) { amax1 = fitA[j]; jmax1 = j;}
513 if (amax1 < fitA[j] && fitA[j + 1] < fitA[j] && fitA[j - 1] < fitA[j]) { amax1 = fitA[j]; jmax1 = j;}
517 double sumB0 = 0;
int jsum = 0;
518 for (
int j = 0; j < c_NFitPoints; j++)
if ((j < jmax - 3 || jmax + 4 < j) && (j < jmax1 - 3 || jmax1 + 4 < j)) { sumB0 += fitA[j]; ++jsum;}
519 double B0 = sumB0 / jsum;
520 amax -= B0; amax = std::max(10.0, amax);
521 amax1 -= B0; amax1 = std::max(10.0, amax1);
522 double T0 = dt * (4.5 - jmax);
523 double T01 = dt * (4.5 - jmax1);
525 double A0 = amax, A01 = amax1;
526 m_Minit2h2->mnparm(0,
"B", B0, 10, B0 / 1.5, B0 * 1.5, ierflg);
527 m_Minit2h2->mnparm(1,
"Ag", A0, A0 / 20, 0, 2 * A0, ierflg);
528 m_Minit2h2->mnparm(2,
"T", T0, 0.5, T0 - 2.5, T0 + 2.5, ierflg);
529 m_Minit2h2->mnparm(3,
"Ah", 0., A0 / 20, -A0, 2 * A0, ierflg);
530 m_Minit2h2->mnparm(4,
"A2", A01, A01 / 20, 0, 2 * A01, ierflg);
531 m_Minit2h2->mnparm(5,
"T2", T01, 0.5, T01 - 2.5, T01 + 2.5, ierflg);
536 m_Minit2h2->mnexcm(
"MIGRAD", arglist, 2, ierflg);
539 int nvpar, nparx, icstat;
540 m_Minit2h2->mnstat(amin, edm, errdef, nvpar, nparx, icstat);
542 m_Minit2h2->GetParameter(0, B, ep);
543 m_Minit2h2->GetParameter(1, Ag, ep);
544 m_Minit2h2->GetParameter(2, T, ep);
545 m_Minit2h2->GetParameter(3, Ah, ep);
546 m_Minit2h2->GetParameter(4, A2, ep);
547 m_Minit2h2->GetParameter(5, T2, ep);
556 std::vector<double> p(s.begin() + 1, s.end());
557 p[1] = std::max(0.0029, p[1]);
558 p[4] = std::max(0.0029, p[4]);
563 dd_t t[(c_nt + c_ntail)*c_ndt];
564 dsp.
fillarray(
sizeof(t) /
sizeof(t[0]), t);
566 for (
int i = 0; i < c_nt * c_ndt; i++) {
567 m_FunctionInterpolation[i] = t[i].first;
568 m_DerivativeInterpolation[i] = t[i].second;
570 for (
int i = 0; i < c_ntail; i++) {
571 int j = c_nt * c_ndt + i;
572 int k = c_nt * c_ndt + i * c_ndt;
573 m_FunctionInterpolation[j] = t[k].first;
574 m_DerivativeInterpolation[j] = t[k].second;
576 int i1 = c_nt * c_ndt + c_ntail - 2;
577 int i2 = c_nt * c_ndt + c_ntail - 1;
578 m_r0 = m_FunctionInterpolation[i2] / m_FunctionInterpolation[i1];
579 m_r1 = m_DerivativeInterpolation[i2] / m_DerivativeInterpolation[i1];
583 double t0,
double*
function,
double* derivatives)
const
592 if (k >= c_NFitPoints)
597 double function0[c_NFitPoints], function1[c_NFitPoints];
598 double derivative0[c_NFitPoints], derivative1[c_NFitPoints];
601 double x = t0 * c_idtn;
602 double ix = floor(x);
606 double hw2 = 0.5 * w2;
607 double tw3 = ((1. / 6) * w) * w2;
611 if (iMax > c_NFitPoints)
615 for (
int i = k; i < iMax; ++i) {
616 function0[i] = m_FunctionInterpolation[j];
617 function1[i] = m_FunctionInterpolation[j + 1];
618 derivative0[i] = m_DerivativeInterpolation[j];
619 derivative1[i] = m_DerivativeInterpolation[j + 1];
625 for (
int i = k; i < iMax; ++i) {
627 double dfdt = (function1[i] - function0[i]) * c_idtn;
628 double fp = derivative1[i] + derivative0[i];
630 a[1] = derivative0[i];
631 a[2] = -((fp + derivative0[i]) - 3 * dfdt);
632 a[3] = fp - 2 * dfdt;
633 double b2 = 2 * a[2];
634 double b3 = 6 * a[3];
635 function[i] = a[0] + c_dtn * (a[1] * w + b2 * hw2 + b3 * tw3);
636 derivatives[i] = a[1] + b2 * w + b3 * hw2;
638 t0 = t0 + c_dt * c_nt;
639 if (iMax == c_NFitPoints)
649 tw3 = ((1. / 6) * w) * w2;
652 iMax = k + c_ntail - 1;
653 if (iMax > c_NFitPoints)
658 for (
int i = k; i < iMax; ++i) {
659 j = c_nt * c_ndt + i - k;
665 double f0 = m_FunctionInterpolation[j];
666 double f1 = m_FunctionInterpolation[j + 1];
667 double fp0 = m_DerivativeInterpolation[j];
668 double fp1 = m_DerivativeInterpolation[j + 1];
670 double dfdt = (f1 - f0) * c_idt;
671 double fp = fp1 + fp0;
674 a[2] = -((fp + fp0) - 3 * dfdt);
675 a[3] = fp - 2 * dfdt;
676 double b2 = 2 * a[2];
677 double b3 = 6 * a[3];
678 function[i] = a[0] + c_dt * (a[1] * w + b2 * hw2 + b3 * tw3);
679 derivatives[i] = a[1] + b2 * w + b3 * hw2;
681 if (iMax == c_NFitPoints)
686 while (k < c_NFitPoints) {
687 function[k] =
function[k - 1] * m_r0;
688 derivatives[k] = derivatives[k - 1] * m_r1;
Class to store ECL digitized hits (output of ECLDigi) relation to ECLHit filled in ecl/modules/eclDig...
TwoComponentFitType
Offline two component fit type.
@ photonHadronBackgroundPhoton
photon + hadron template + pile-up photon fit
@ poorChi2
All offline fit attempts were greater than chi2 threshold.
@ photonDiodeCrossing
photon + diode template fit
@ photonHadron
photon + hadron template fit
Singleton class to hold the ECL configuration.
static EclConfiguration & get()
return this instance
static constexpr int m_nsmp
number of ADC measurements for signal fitting
Class include function that calculate electronic response from energy deposit
void settimeseed(double)
set initial time
void settimestride(double)
set grid step for function calculation
void fillarray(int, double *) const
fill array for amplitude and time calculation
bool isMC() const
Do we have generated, not real data?
static Environment & Instance()
Static method to get a reference to the Environment instance.
REG_MODULE(arichBtest)
Register the Module.
const int c_NCrystals
Number of crystals.
Abstract base class for different kinds of events.
Struct to keep upper triangle of the covariance matrix.
float m_covMatPacked[31 *(31+1)/2]
Packed matrix.
Interpolation of signal shape using function values and the first derivative.
SignalInterpolation2()
Default constructor.
void getShape(double t0, double *function, double *derivatives) const
Returns signal shape and derivatives in 31 equidistant time points starting from t0.