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>
40 void ecl_waveform_fit_load_inverse_covariance(
const float* packed_matrix);
45 void ecl_waveform_fit_multiply_inverse_covariance(
double* y,
const double* x);
54 const int c_NFitPoints = 31;
57 const int c_NFitPointsVector = 32;
60 double fitA[c_NFitPoints];
73 void fcnPhotonHadron(
int&,
double* grad,
double& f,
double* p,
int)
75 double df[c_NFitPointsVector];
76 double da[c_NFitPointsVector];
77 const double Ag = p[1], B = p[0], T = p[2], Ah = p[3];
78 double chi2 = 0, gAg = 0, gB = 0, gT = 0, gAh = 0;
81 double amplitudeGamma[c_NFitPoints], derivativesGamma[c_NFitPoints];
82 double amplitudeHadron[c_NFitPoints], derivativesHadron[c_NFitPoints];
83 g_PhotonSignal->
getShape(T, amplitudeGamma, derivativesGamma);
84 g_HadronSignal->
getShape(T, amplitudeHadron, derivativesHadron);
88 for (
int i = 0; i < c_NFitPoints; ++i)
89 df[i] = fitA[i] - (Ag * amplitudeGamma[i] + Ah * amplitudeHadron[i] + B);
92 ecl_waveform_fit_multiply_inverse_covariance(da, df);
94 #pragma omp simd reduction(+:chi2) reduction(-:gB,gAg,gT,gAh)
95 for (
int i = 0; i < c_NFitPoints; ++i) {
96 chi2 += da[i] * df[i];
98 gAg -= da[i] * amplitudeGamma[i];
99 gT -= da[i] * (derivativesGamma[i] * Ag + derivativesHadron[i] * Ah);
100 gAh -= da[i] * amplitudeHadron[i];
112 void fcnPhotonHadronBackgroundPhoton(
int&,
double* grad,
double& f,
double* p,
int)
114 double df[c_NFitPointsVector];
115 double da[c_NFitPointsVector];
116 const double A2 = p[4], T2 = p[5];
117 const double Ag = p[1], B = p[0], T = p[2], Ah = p[3];
118 double chi2 = 0, gA2 = 0, gT2 = 0;
119 double gAg = 0, gB = 0, gT = 0, gAh = 0;
122 double amplitudeGamma[c_NFitPoints], derivativesGamma[c_NFitPoints];
123 double amplitudeGamma2[c_NFitPoints], derivativesGamma2[c_NFitPoints];
124 double amplitudeHadron[c_NFitPoints], derivativesHadron[c_NFitPoints];
125 g_PhotonSignal->
getShape(T, amplitudeGamma, derivativesGamma);
127 g_PhotonSignal->
getShape(T2, amplitudeGamma2, derivativesGamma2);
128 g_HadronSignal->
getShape(T, amplitudeHadron, derivativesHadron);
132 for (
int i = 0; i < c_NFitPoints; ++i) {
133 df[i] = fitA[i] - (Ag * amplitudeGamma[i] + Ah * amplitudeHadron[i]
134 + A2 * amplitudeGamma2[i] + B);
138 ecl_waveform_fit_multiply_inverse_covariance(da, df);
140 #pragma omp simd reduction(+:chi2) reduction(-:gB,gAg,gT,gAh,gA2,gT2)
141 for (
int i = 0; i < c_NFitPoints; ++i) {
142 chi2 += da[i] * df[i];
144 gAg -= da[i] * amplitudeGamma[i];
145 gAh -= da[i] * amplitudeHadron[i];
146 gT -= da[i] * (derivativesGamma[i] * Ag + derivativesHadron[i] * Ah);
148 gA2 -= da[i] * amplitudeGamma2[i];
149 gT2 -= da[i] * derivativesGamma2[i] * A2;
162 void regularize(
double* dst,
const double* src,
const int n,
double u0,
double u1,
double u2)
164 for (
int k = 1; k < n; k++) dst[k] = u2 * src[k] / (1 + exp((k - u0) / u1));
168 bool makecovariance(
CovariancePacked& M,
const int nnoise,
const double* acov)
170 TMatrixDSym
E(c_NFitPoints);
171 for (
int i = 0; i < c_NFitPoints; i++)
172 for (
int j = 0; j < i + 1; j++)
173 if (i - j < nnoise)
E(i, j) =
E(j, i) = acov[i - j];
176 const bool status = dc.Invert(
E);
180 for (
int i = 0; i < c_NFitPoints; i++)
181 for (
int j = 0; j < i + 1; j++)
182 M[count++] =
E(i, j);
183 M.
sigma = sqrtf(acov[0]);
193 setDescription(
"Module to fit offline waveforms and measure hadron scintillation component light output.");
194 setPropertyFlags(c_ParallelProcessingCertified);
195 addParam(
"EnergyThreshold", m_EnergyThreshold,
196 "Energy threshold of online fit result for Fitting Waveforms (GeV).",
198 addParam(
"Chi2Threshold25dof", m_Chi2Threshold25dof,
199 "Chi2 threshold (25 dof) to classify offline fit as good fit.",
201 addParam(
"Chi2Threshold27dof", m_Chi2Threshold27dof,
202 "Chi2 threshold (27 dof) to classify offline fit as good fit.",
204 addParam(
"CovarianceMatrix", m_CovarianceMatrix,
205 "Option to use crystal-dependent covariance matrices (false uses identity matrix).",
207 addParam(
"RegParam1", m_u1,
"u1 parameter for regularization function).", 1.0);
217 m_TemplatesLoaded =
true;
219 if (m_IsMCFlag == 0) {
221 std::vector<double> Ptemp(11), Htemp(11), Dtemp(11);
223 for (
int j = 0; j < 11; j++) {
224 Ptemp[j] = (double)m_WaveformParameters->getPhotonParameters(i + 1)[j];
225 Htemp[j] = (double)m_WaveformParameters->getHadronParameters(i + 1)[j];
226 Dtemp[j] = (double)m_WaveformParameters->getDiodeParameters(i + 1)[j];
234 std::vector<double> Ptemp(11), Htemp(11), Dtemp(11);
235 for (
int j = 0; j < 11; j++) {
236 Ptemp[j] = (double)m_WaveformParametersForMC->getPhotonParameters()[j];
237 Htemp[j] = (double)m_WaveformParametersForMC->getHadronParameters()[j];
238 Dtemp[j] = (double)m_WaveformParametersForMC->getDiodeParameters()[j];
250 m_TemplatesLoaded =
false;
253 if (m_CrystalElectronics.isValid()) {
255 m_ADCtoEnergy[i] = m_CrystalElectronics->getCalibVector()[i];
257 if (m_CrystalEnergy.isValid()) {
259 m_ADCtoEnergy[i] *= m_CrystalEnergy->getCalibVector()[i];
263 if (m_CovarianceMatrix) {
265 double buf[c_NFitPoints];
266 double reg[c_NFitPoints];
267 m_AutoCovariance->getAutoCovariance(
id, buf);
268 double x0 = c_NFitPoints;
269 std::memcpy(reg, buf, c_NFitPoints *
sizeof(
double));
270 bool invertSuccess = makecovariance(m_PackedCovariance[
id - 1], c_NFitPoints, reg);
271 double param_u2 = 1.0;
273 while (invertSuccess ==
false) {
276 regularize(buf, reg, c_NFitPoints, x0 -= 1, m_u1, param_u2);
277 invertSuccess = makecovariance(m_PackedCovariance[
id - 1], c_NFitPoints, reg);
280 if (x0 == 1) param_u2 = 0.0;
284 B2WARNING(
"Could not invert matrix for id " <<
id);
285 B2WARNING(
"Loading neighbour matrix for id " <<
id);
287 m_AutoCovariance->getAutoCovariance(idToLoad = -1, buf);
289 m_AutoCovariance->getAutoCovariance(idToLoad += 1, buf);
291 std::memcpy(reg, buf, c_NFitPoints *
sizeof(
double));
298 double defaultCovariance[c_NFitPoints][c_NFitPoints];
300 const double isigma = 1 / 7.5;
301 for (
int i = 0; i < c_NFitPoints; ++i) {
302 for (
int j = 0; j < c_NFitPoints; ++j) {
303 defaultCovariance[i][j] = (i == j) * isigma * isigma;
307 for (
int i = 0; i < c_NFitPoints; i++) {
308 for (
int j = 0; j < i + 1; j++) {
309 packedDefaultCovariance.
m_covMatPacked[k] = defaultCovariance[i][j];
313 ecl_waveform_fit_load_inverse_covariance(
322 m_eclDSPs.isRequired();
323 m_eclDigits.isRequired();
326 m_eclDSPs.registerRelationTo(m_eclDigits);
329 m_MinuitPhotonHadron =
new TMinuit(4);
330 m_MinuitPhotonHadron->SetFCN(fcnPhotonHadron);
334 m_MinuitPhotonHadron->mnexcm(
"SET PRIntout", arglist, 1, ierflg);
335 m_MinuitPhotonHadron->mnexcm(
"SET NOWarnings", arglist, 0, ierflg);
337 m_MinuitPhotonHadron->mnexcm(
"SET ERR", arglist, 1, ierflg);
339 m_MinuitPhotonHadron->mnexcm(
"SET STRategy", arglist, 1, ierflg);
341 m_MinuitPhotonHadron->mnexcm(
"SET GRAdient", arglist, 1, ierflg);
343 m_MinuitPhotonHadron->mnexcm(
"SET EPSmachine", arglist, 1, ierflg);
346 m_MinuitPhotonHadronBackgroundPhoton =
new TMinuit(6);
347 m_MinuitPhotonHadronBackgroundPhoton->SetFCN(fcnPhotonHadronBackgroundPhoton);
349 m_MinuitPhotonHadronBackgroundPhoton->mnexcm(
"SET PRIntout", arglist, 1, ierflg);
350 m_MinuitPhotonHadronBackgroundPhoton->mnexcm(
"SET NOWarnings", arglist, 0, ierflg);
352 m_MinuitPhotonHadronBackgroundPhoton->mnexcm(
"SET ERR", arglist, 1, ierflg);
354 m_MinuitPhotonHadronBackgroundPhoton->mnexcm(
"SET STRategy", arglist, 1, ierflg);
356 m_MinuitPhotonHadronBackgroundPhoton->mnexcm(
"SET GRAdient", arglist, 1, ierflg);
358 m_MinuitPhotonHadronBackgroundPhoton->mnexcm(
"SET EPSmachine", arglist, 1, ierflg);
361 m_TemplatesLoaded =
false;
368 if (!m_TemplatesLoaded) {
370 if (m_eclDSPs.getEntries() > 0)
371 loadTemplateParameterArray();
374 for (
ECLDsp& aECLDsp : m_eclDSPs) {
376 aECLDsp.setTwoComponentTotalAmp(-1);
377 aECLDsp.setTwoComponentHadronAmp(-1);
378 aECLDsp.setTwoComponentDiodeAmp(-1);
379 aECLDsp.setTwoComponentChi2(-1);
383 aECLDsp.setTwoComponentTime(-1);
384 aECLDsp.setTwoComponentBaseline(-1);
387 const int id = aECLDsp.getCellId() - 1;
390 for (
int j = 0; j < ec.
m_nsmp; j++)
391 fitA[j] = aECLDsp.getDspA()[j];
395 for (
const ECLDigit& aECLDigit : m_eclDigits) {
396 if (aECLDigit.getCellId() - 1 ==
id) {
398 aECLDsp.addRelationTo(&aECLDigit);
406 if (d->getAmp() * m_ADCtoEnergy[
id] < m_EnergyThreshold)
410 if (m_IsMCFlag == 0) {
412 g_PhotonSignal = &m_SignalInterpolation[id][0];
413 g_HadronSignal = &m_SignalInterpolation[id][1];
416 g_PhotonSignal = &m_SignalInterpolation[0][0];
417 g_HadronSignal = &m_SignalInterpolation[0][1];
421 if (m_CovarianceMatrix) {
422 ecl_waveform_fit_load_inverse_covariance(
423 m_PackedCovariance[
id].m_covMatPacked);
424 aNoise = m_PackedCovariance[id].sigma;
428 double pedestal, amplitudePhoton, signalTime, amplitudeHadron,
429 amplitudeBackgroundPhoton, timeBackgroundPhoton, chi2;
432 fitPhotonHadron(pedestal, amplitudePhoton, signalTime, amplitudeHadron,
437 if (chi2 >= m_Chi2Threshold27dof) {
441 fitPhotonHadronBackgroundPhoton(
442 pedestal, amplitudePhoton, signalTime, amplitudeHadron,
443 amplitudeBackgroundPhoton, timeBackgroundPhoton, chi2);
448 if (chi2 >= m_Chi2Threshold25dof) {
450 g_HadronSignal = &m_SignalInterpolation[0][2];
453 fitPhotonHadron(pedestal, amplitudePhoton, signalTime, amplitudeHadron,
458 if (chi2 >= m_Chi2Threshold27dof)
465 aECLDsp.setTwoComponentTotalAmp(amplitudePhoton + amplitudeHadron);
467 aECLDsp.setTwoComponentHadronAmp(0.0);
468 aECLDsp.setTwoComponentDiodeAmp(amplitudeHadron);
470 aECLDsp.setTwoComponentHadronAmp(amplitudeHadron);
471 aECLDsp.setTwoComponentDiodeAmp(0.0);
473 aECLDsp.setTwoComponentChi2(chi2);
474 aECLDsp.setTwoComponentTime(signalTime);
475 aECLDsp.setTwoComponentBaseline(pedestal);
476 aECLDsp.setTwoComponentFitType(fitType);
478 aECLDsp.setbackgroundPhotonEnergy(amplitudeBackgroundPhoton);
479 aECLDsp.setbackgroundPhotonTime(timeBackgroundPhoton);
493 double& pedestal,
double& amplitudePhoton,
double& signalTime,
494 double& amplitudeHadron,
double& chi2)
497 double arglist[10] = {0};
504 for (
int j = 0; j < c_NFitPoints; j++) {
505 if (amax < fitA[j]) {
512 for (
int j = 0; j < c_NFitPoints; j++) {
513 if (j < jmax - 3 || jmax + 4 < j) {
518 double B0 = sumB0 / jsum;
522 double T0 = dt * (4.5 - jmax);
526 m_MinuitPhotonHadron->mnparm(0,
"B", B0, 10, B0 / 1.5, B0 * 1.5, ierflg);
527 m_MinuitPhotonHadron->mnparm(1,
"Ag", A0, A0 / 20, 0, 2 * A0, ierflg);
528 m_MinuitPhotonHadron->mnparm(2,
"T", T0, 0.5, T0 - 2.5, T0 + 2.5, ierflg);
529 m_MinuitPhotonHadron->mnparm(3,
"Ah", 0., A0 / 20, -A0, 2 * A0, ierflg);
534 m_MinuitPhotonHadron->mnexcm(
"MIGRAD", arglist, 2, ierflg);
537 int nvpar, nparx, icstat;
538 m_MinuitPhotonHadron->mnstat(chi2, edm, errdef, nvpar, nparx, icstat);
542 m_MinuitPhotonHadron->GetParameter(0, pedestal, error);
543 m_MinuitPhotonHadron->GetParameter(1, amplitudePhoton, error);
544 m_MinuitPhotonHadron->GetParameter(2, signalTime, error);
545 m_MinuitPhotonHadron->GetParameter(3, amplitudeHadron, error);
549 double& pedestal,
double& amplitudePhoton,
double& signalTime,
550 double& amplitudeHadron,
double& amplitudeBackgroundPhoton,
551 double& timeBackgroundPhoton,
double& chi2)
553 double arglist[10] = {0};
556 double amax = 0;
int jmax = 6;
557 for (
int j = 0; j < c_NFitPoints; j++) {
558 if (amax < fitA[j]) {
564 double amax1 = 0;
int jmax1 = 6;
565 for (
int j = 0; j < c_NFitPoints; j++) {
566 if (j < jmax - 3 || jmax + 4 < j) {
568 if (amax1 < fitA[j] && fitA[j + 1] < fitA[j]) {
572 }
else if (j == 30) {
573 if (amax1 < fitA[j] && fitA[j - 1] < fitA[j]) {
578 if (amax1 < fitA[j] && fitA[j + 1] < fitA[j] && fitA[j - 1] < fitA[j]) {
588 for (
int j = 0; j < c_NFitPoints; j++) {
589 if ((j < jmax - 3 || jmax + 4 < j) && (j < jmax1 - 3 || jmax1 + 4 < j)) {
594 double B0 = sumB0 / jsum;
596 amax = std::max(10.0, amax);
598 amax1 = std::max(10.0, amax1);
599 double T0 = dt * (4.5 - jmax);
600 double T01 = dt * (4.5 - jmax1);
602 double A0 = amax, A01 = amax1;
603 m_MinuitPhotonHadronBackgroundPhoton->mnparm(
604 0,
"B", B0, 10, B0 / 1.5, B0 * 1.5, ierflg);
605 m_MinuitPhotonHadronBackgroundPhoton->mnparm(
606 1,
"Ag", A0, A0 / 20, 0, 2 * A0, ierflg);
607 m_MinuitPhotonHadronBackgroundPhoton->mnparm(
608 2,
"T", T0, 0.5, T0 - 2.5, T0 + 2.5, ierflg);
609 m_MinuitPhotonHadronBackgroundPhoton->mnparm(
610 3,
"Ah", 0., A0 / 20, -A0, 2 * A0, ierflg);
611 m_MinuitPhotonHadronBackgroundPhoton->mnparm(
612 4,
"A2", A01, A01 / 20, 0, 2 * A01, ierflg);
613 m_MinuitPhotonHadronBackgroundPhoton->mnparm(
614 5,
"T2", T01, 0.5, T01 - 2.5, T01 + 2.5, ierflg);
619 m_MinuitPhotonHadronBackgroundPhoton->mnexcm(
"MIGRAD", arglist, 2, ierflg);
622 int nvpar, nparx, icstat;
623 m_MinuitPhotonHadronBackgroundPhoton->mnstat(chi2, edm, errdef, nvpar, nparx, icstat);
625 m_MinuitPhotonHadronBackgroundPhoton->GetParameter(0, pedestal, error);
626 m_MinuitPhotonHadronBackgroundPhoton->GetParameter(1, amplitudePhoton, error);
627 m_MinuitPhotonHadronBackgroundPhoton->GetParameter(2, signalTime, error);
628 m_MinuitPhotonHadronBackgroundPhoton->GetParameter(3, amplitudeHadron, error);
629 m_MinuitPhotonHadronBackgroundPhoton->GetParameter(
630 4, amplitudeBackgroundPhoton, error);
631 m_MinuitPhotonHadronBackgroundPhoton->GetParameter(
632 5, timeBackgroundPhoton, error);
638 std::vector<double> p(s.begin() + 1, s.end());
639 p[1] = std::max(0.0029, p[1]);
640 p[4] = std::max(0.0029, p[4]);
645 dd_t t[(c_nt + c_ntail)*c_ndt];
646 dsp.
fillarray(
sizeof(t) /
sizeof(t[0]), t);
648 for (
int i = 0; i < c_nt * c_ndt; i++) {
649 m_FunctionInterpolation[i] = t[i].first;
650 m_DerivativeInterpolation[i] = t[i].second;
652 for (
int i = 0; i < c_ntail; i++) {
653 int j = c_nt * c_ndt + i;
654 int k = c_nt * c_ndt + i * c_ndt;
655 m_FunctionInterpolation[j] = t[k].first;
656 m_DerivativeInterpolation[j] = t[k].second;
658 int i1 = c_nt * c_ndt + c_ntail - 2;
659 int i2 = c_nt * c_ndt + c_ntail - 1;
660 m_r0 = m_FunctionInterpolation[i2] / m_FunctionInterpolation[i1];
661 m_r1 = m_DerivativeInterpolation[i2] / m_DerivativeInterpolation[i1];
665 double t0,
double*
function,
double* derivatives)
const
674 if (k >= c_NFitPoints)
679 double function0[c_NFitPoints], function1[c_NFitPoints];
680 double derivative0[c_NFitPoints], derivative1[c_NFitPoints];
683 double x = t0 * c_idtn;
684 double ix = floor(x);
688 double hw2 = 0.5 * w2;
689 double tw3 = ((1. / 6) * w) * w2;
693 if (iMax > c_NFitPoints)
697 for (
int i = k; i < iMax; ++i) {
698 function0[i] = m_FunctionInterpolation[j];
699 function1[i] = m_FunctionInterpolation[j + 1];
700 derivative0[i] = m_DerivativeInterpolation[j];
701 derivative1[i] = m_DerivativeInterpolation[j + 1];
707 for (
int i = k; i < iMax; ++i) {
709 double dfdt = (function1[i] - function0[i]) * c_idtn;
710 double fp = derivative1[i] + derivative0[i];
712 a[1] = derivative0[i];
713 a[2] = -((fp + derivative0[i]) - 3 * dfdt);
714 a[3] = fp - 2 * dfdt;
715 double b2 = 2 * a[2];
716 double b3 = 6 * a[3];
717 function[i] = a[0] + c_dtn * (a[1] * w + b2 * hw2 + b3 * tw3);
718 derivatives[i] = a[1] + b2 * w + b3 * hw2;
720 t0 = t0 + c_dt * c_nt;
721 if (iMax == c_NFitPoints)
731 tw3 = ((1. / 6) * w) * w2;
734 iMax = k + c_ntail - 1;
735 if (iMax > c_NFitPoints)
740 for (
int i = k; i < iMax; ++i) {
741 j = c_nt * c_ndt + i - k;
747 double f0 = m_FunctionInterpolation[j];
748 double f1 = m_FunctionInterpolation[j + 1];
749 double fp0 = m_DerivativeInterpolation[j];
750 double fp1 = m_DerivativeInterpolation[j + 1];
752 double dfdt = (f1 - f0) * c_idt;
753 double fp = fp1 + fp0;
756 a[2] = -((fp + fp0) - 3 * dfdt);
757 a[3] = fp - 2 * dfdt;
758 double b2 = 2 * a[2];
759 double b3 = 6 * a[3];
760 function[i] = a[0] + c_dt * (a[1] * w + b2 * hw2 + b3 * tw3);
761 derivatives[i] = a[1] + b2 * w + b3 * hw2;
763 if (iMax == c_NFitPoints)
768 while (k < c_NFitPoints) {
769 function[k] =
function[k - 1] * m_r0;
770 derivatives[k] = derivatives[k - 1] * m_r1;
Class to store ECL digitized hits (output of ECLDigi) relation to ECLHit filled in ecl/modules/eclDig...
Class to store ECL ShaperDSP waveform ADC data.
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.