|
2 | 2 |
|
3 | 3 | #include <TF1.h> |
4 | 4 | #include <TFile.h> |
| 5 | +#include <TH1F.h> |
5 | 6 | #include <TProfile.h> |
6 | 7 | #include <TSpline.h> |
7 | 8 |
|
|
21 | 22 | #include <limits> |
22 | 23 | #include <string> |
23 | 24 |
|
24 | | -static ROOT::TThreadExecutor *t = new ROOT::TThreadExecutor(1);// NOLINT(misc-use-anonymous-namespace) |
| 25 | +static ROOT::TThreadExecutor *t = new ROOT::TThreadExecutor(1); // NOLINT(misc-use-anonymous-namespace) |
25 | 26 | double CaloWaveformFitting::template_function(double *x, double *par) |
26 | 27 | { |
27 | 28 | Double_t v1 = (par[0] * h_template->Interpolate(x[0] - par[1])) + par[2]; |
@@ -619,3 +620,253 @@ float CaloWaveformFitting::psinc(float time, std::vector<float> &vec_signal_samp |
619 | 620 |
|
620 | 621 | return sum; |
621 | 622 | } |
| 623 | + |
| 624 | +double CaloWaveformFitting::SignalShape_PowerLawExp(double *x, double *par) |
| 625 | +{ |
| 626 | + // par[0]: Amplitude |
| 627 | + // par[1]: Sample Start (t0) |
| 628 | + // par[2]: Power |
| 629 | + // par[3]: Decay |
| 630 | + // par[4]: Pedestal |
| 631 | + double pedestal = par[4]; |
| 632 | + if (x[0] < par[1]) |
| 633 | + { |
| 634 | + return pedestal; |
| 635 | + } |
| 636 | + double signal = par[0] * pow((x[0] - par[1]), par[2]) * exp(-(x[0] - par[1]) * par[3]); |
| 637 | + return pedestal + signal; |
| 638 | +} |
| 639 | + |
| 640 | +double CaloWaveformFitting::SignalShape_PowerLawDoubleExp(double *x, double *par) |
| 641 | +{ |
| 642 | + // par[0]: Amplitude |
| 643 | + // par[1]: Sample Start (t0) |
| 644 | + // par[2]: Power |
| 645 | + // par[3]: Peak Time 1 |
| 646 | + // par[4]: Pedestal |
| 647 | + // par[5]: Amplitude ratio |
| 648 | + // par[6]: Peak Time 2 |
| 649 | + double pedestal = par[4]; |
| 650 | + if (x[0] < par[1]) |
| 651 | + { |
| 652 | + return pedestal; |
| 653 | + } |
| 654 | + double signal = par[0] * pow((x[0] - par[1]), par[2]) * |
| 655 | + (((1.0 - par[5]) / pow(par[3], par[2]) * exp(par[2])) * |
| 656 | + exp(-(x[0] - par[1]) * (par[2] / par[3])) + |
| 657 | + (par[5] / pow(par[6], par[2]) * exp(par[2])) * |
| 658 | + exp(-(x[0] - par[1]) * (par[2] / par[6]))); |
| 659 | + return pedestal + signal; |
| 660 | +} |
| 661 | + |
| 662 | +std::vector<std::vector<float>> CaloWaveformFitting::calo_processing_funcfit(const std::vector<std::vector<float>> &chnlvector) |
| 663 | +{ |
| 664 | + std::vector<std::vector<float>> fit_values; |
| 665 | + int nchnls = chnlvector.size(); |
| 666 | + |
| 667 | + for (int m = 0; m < nchnls; m++) |
| 668 | + { |
| 669 | + const std::vector<float> &v = chnlvector.at(m); |
| 670 | + int nsamples = v.size(); |
| 671 | + |
| 672 | + float amp = 0; |
| 673 | + float time = 0; |
| 674 | + float ped = 0; |
| 675 | + float chi2 = std::numeric_limits<float>::quiet_NaN(); |
| 676 | + |
| 677 | + // Handle zero-suppressed samples (2-sample case) |
| 678 | + if (nsamples == _nzerosuppresssamples) |
| 679 | + { |
| 680 | + amp = v.at(1) - v.at(0); |
| 681 | + time = std::numeric_limits<float>::quiet_NaN(); |
| 682 | + ped = v.at(0); |
| 683 | + if (v.at(0) != 0 && v.at(1) == 0) |
| 684 | + { |
| 685 | + chi2 = 1000000; |
| 686 | + } |
| 687 | + fit_values.push_back({amp, time, ped, chi2, 0}); |
| 688 | + continue; |
| 689 | + } |
| 690 | + |
| 691 | + // Find peak position and estimate pedestal |
| 692 | + float maxheight = 0; |
| 693 | + int maxbin = 0; |
| 694 | + for (int i = 0; i < nsamples; i++) |
| 695 | + { |
| 696 | + if (v.at(i) > maxheight) |
| 697 | + { |
| 698 | + maxheight = v.at(i); |
| 699 | + maxbin = i; |
| 700 | + } |
| 701 | + } |
| 702 | + |
| 703 | + float pedestal = 1500; |
| 704 | + if (maxbin > 4) |
| 705 | + { |
| 706 | + pedestal = 0.5 * (v.at(maxbin - 4) + v.at(maxbin - 5)); |
| 707 | + } |
| 708 | + else if (maxbin > 3) |
| 709 | + { |
| 710 | + pedestal = v.at(maxbin - 4); |
| 711 | + } |
| 712 | + else |
| 713 | + { |
| 714 | + pedestal = 0.5 * (v.at(nsamples - 3) + v.at(nsamples - 2)); |
| 715 | + } |
| 716 | + |
| 717 | + // Software zero suppression check |
| 718 | + if ((_bdosoftwarezerosuppression && v.at(6) - v.at(0) < _nsoftwarezerosuppression) || |
| 719 | + (_maxsoftwarezerosuppression && maxheight - pedestal < _nsoftwarezerosuppression)) |
| 720 | + { |
| 721 | + amp = v.at(6) - v.at(0); |
| 722 | + time = std::numeric_limits<float>::quiet_NaN(); |
| 723 | + ped = v.at(0); |
| 724 | + if (v.at(0) != 0 && v.at(1) == 0) |
| 725 | + { |
| 726 | + chi2 = 1000000; |
| 727 | + } |
| 728 | + fit_values.push_back({amp, time, ped, chi2, 0}); |
| 729 | + continue; |
| 730 | + } |
| 731 | + |
| 732 | + // Create histogram for fitting |
| 733 | + TH1F h("h_funcfit", "", nsamples, -0.5, nsamples - 0.5); |
| 734 | + int ndata = 0; |
| 735 | + for (int i = 0; i < nsamples; ++i) |
| 736 | + { |
| 737 | + if ((v.at(i) == 16383) && _handleSaturation) |
| 738 | + { |
| 739 | + continue; |
| 740 | + } |
| 741 | + h.SetBinContent(i + 1, v.at(i)); |
| 742 | + h.SetBinError(i + 1, 1); |
| 743 | + ndata++; |
| 744 | + } |
| 745 | + |
| 746 | + // If too many saturated, use all data |
| 747 | + if (ndata < (nsamples - 4)) |
| 748 | + { |
| 749 | + ndata = nsamples; |
| 750 | + for (int i = 0; i < nsamples; ++i) |
| 751 | + { |
| 752 | + h.SetBinContent(i + 1, v.at(i)); |
| 753 | + h.SetBinError(i + 1, 1); |
| 754 | + } |
| 755 | + } |
| 756 | + |
| 757 | + double fit_amp = 0; |
| 758 | + double fit_time = 0; |
| 759 | + double fit_ped = 0; |
| 760 | + double chi2val = 0; |
| 761 | + int npar = 0; |
| 762 | + |
| 763 | + if (m_funcfit_type == POWERLAWEXP) |
| 764 | + { |
| 765 | + // Create fit function with 5 parameters |
| 766 | + TF1 f("f_powerlaw", SignalShape_PowerLawExp, 0, nsamples, 5); |
| 767 | + npar = 5; |
| 768 | + |
| 769 | + // Set initial parameters |
| 770 | + double risetime = m_powerlaw_power / m_powerlaw_decay; |
| 771 | + double par[5]; |
| 772 | + par[0] = maxheight - pedestal; // Amplitude |
| 773 | + par[1] = maxbin - risetime; // t0 |
| 774 | + par[1] = std::max<double>(par[1], 0); |
| 775 | + par[2] = m_powerlaw_power; // Power |
| 776 | + par[3] = m_powerlaw_decay; // Decay |
| 777 | + par[4] = pedestal; // Pedestal |
| 778 | + |
| 779 | + f.SetParameters(par); |
| 780 | + f.SetParLimits(0, (maxheight - pedestal) * 0.5, (maxheight - pedestal) * 10); |
| 781 | + f.SetParLimits(1, 0, nsamples); |
| 782 | + f.SetParLimits(2, 0, 10.0); |
| 783 | + f.SetParLimits(3, 0, 10.0); |
| 784 | + f.SetParLimits(4, pedestal - std::abs(maxheight - pedestal), pedestal + std::abs(maxheight - pedestal)); |
| 785 | + |
| 786 | + // Perform fit |
| 787 | + h.Fit(&f, "QRN0W", "", 0, nsamples); |
| 788 | + |
| 789 | + // Calculate peak amplitude and time from fit parameters |
| 790 | + // Peak height is (p0 * Power(p2/p3, p2)) / exp(p2) |
| 791 | + fit_amp = (f.GetParameter(0) * pow(f.GetParameter(2) / f.GetParameter(3), f.GetParameter(2))) / exp(f.GetParameter(2)); |
| 792 | + // Peak time is t0 + power/decay |
| 793 | + fit_time = f.GetParameter(1) + f.GetParameter(2) / f.GetParameter(3); |
| 794 | + fit_ped = f.GetParameter(4); |
| 795 | + |
| 796 | + // Calculate chi2 |
| 797 | + for (int i = 0; i < nsamples; i++) |
| 798 | + { |
| 799 | + if (h.GetBinContent(i + 1) > 0) |
| 800 | + { |
| 801 | + double diff = h.GetBinContent(i + 1) - f.Eval(i); |
| 802 | + chi2val += diff * diff; |
| 803 | + } |
| 804 | + } |
| 805 | + } |
| 806 | + else // POWERLAWDOUBLEEXP |
| 807 | + { |
| 808 | + // Create fit function with 7 parameters |
| 809 | + TF1 f("f_doubleexp", SignalShape_PowerLawDoubleExp, 0, nsamples, 7); |
| 810 | + npar = 7; |
| 811 | + |
| 812 | + // Set initial parameters |
| 813 | + double risetime = 2.0; |
| 814 | + double par[7]; |
| 815 | + par[0] = (maxheight - pedestal) * 0.7; // Amplitude |
| 816 | + par[1] = maxbin - risetime; // t0 |
| 817 | + par[1] = std::max<double>(par[1], 0); |
| 818 | + par[2] = m_doubleexp_power; // Power |
| 819 | + par[3] = m_doubleexp_peaktime1; // Peak Time 1 |
| 820 | + par[4] = pedestal; // Pedestal |
| 821 | + par[5] = m_doubleexp_ratio; // Amplitude ratio |
| 822 | + par[6] = m_doubleexp_peaktime2; // Peak Time 2 |
| 823 | + |
| 824 | + f.SetParameters(par); |
| 825 | + f.SetParLimits(0, (maxheight - pedestal) * -1.5, (maxheight - pedestal) * 1.5); |
| 826 | + f.SetParLimits(1, maxbin - 3 * risetime, maxbin + risetime); |
| 827 | + f.SetParLimits(2, 1, 5.0); |
| 828 | + f.SetParLimits(3, risetime * 0.5, risetime * 4); |
| 829 | + f.SetParLimits(4, pedestal - std::abs(maxheight - pedestal), pedestal + std::abs(maxheight - pedestal)); |
| 830 | + f.SetParLimits(5, 0, 1); |
| 831 | + f.SetParLimits(6, risetime * 0.5, risetime * 4); |
| 832 | + |
| 833 | + // Perform fit |
| 834 | + h.Fit(&f, "QRN0W", "", 0, nsamples); |
| 835 | + |
| 836 | + // Find peak by evaluating the function |
| 837 | + double peakpos1 = f.GetParameter(3); |
| 838 | + double peakpos2 = f.GetParameter(6); |
| 839 | + double max_peakpos = f.GetParameter(1) + (peakpos1 > peakpos2 ? peakpos1 : peakpos2); |
| 840 | + max_peakpos = std::min<double>(max_peakpos, nsamples - 1); |
| 841 | + |
| 842 | + fit_time = f.GetMaximumX(f.GetParameter(1), max_peakpos); |
| 843 | + fit_amp = f.Eval(fit_time) - f.GetParameter(4); |
| 844 | + fit_ped = f.GetParameter(4); |
| 845 | + |
| 846 | + // Calculate chi2 |
| 847 | + for (int i = 0; i < nsamples; i++) |
| 848 | + { |
| 849 | + if (h.GetBinContent(i + 1) > 0) |
| 850 | + { |
| 851 | + double diff = h.GetBinContent(i + 1) - f.Eval(i); |
| 852 | + chi2val += diff * diff; |
| 853 | + } |
| 854 | + } |
| 855 | + } |
| 856 | + |
| 857 | + int ndf = ndata - npar; |
| 858 | + if (ndf > 0) |
| 859 | + { |
| 860 | + chi2val /= ndf; |
| 861 | + } |
| 862 | + else |
| 863 | + { |
| 864 | + chi2val = std::numeric_limits<double>::quiet_NaN(); |
| 865 | + } |
| 866 | + |
| 867 | + fit_values.push_back({static_cast<float>(fit_amp), static_cast<float>(fit_time), |
| 868 | + static_cast<float>(fit_ped), static_cast<float>(chi2val), 0}); |
| 869 | + } |
| 870 | + |
| 871 | + return fit_values; |
| 872 | +} |
0 commit comments