R3BROOT
R3B analysis software
Loading...
Searching...
No Matches
R3BNeulandNeutronReconstructionStatistics.cxx
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) 2019 GSI Helmholtzzentrum für Schwerionenforschung GmbH *
3 * Copyright (C) 2019-2025 Members of R3B Collaboration *
4 * *
5 * This software is distributed under the terms of the *
6 * GNU General Public Licence (GPL) version 3, *
7 * copied verbatim in the file "LICENSE". *
8 * *
9 * In applying this license GSI does not waive the privileges and immunities *
10 * granted to it by virtue of its status as an Intergovernmental Organization *
11 * or submit itself to any jurisdiction. *
12 ******************************************************************************/
13
15#include "R3BNeulandCluster.h"
16#include "R3BNeulandNeutron.h"
17#include <FairRootManager.h>
18#include <FairTask.h>
19#include <RtypesCore.h>
20#include <TFile.h>
21#include <TH1.h>
22#include <fairlogger/Logger.h>
23#include <fmt/core.h>
24#include <limits>
25#include <numeric>
26#include <string_view>
27#include <type_traits>
28#include <vector>
29
30constexpr auto DEFAULT_MULTI = 100;
31
32namespace
33{
34 template <class T>
35 auto almost_equal(T left, T right, int ulp) -> std::enable_if_t<!std::numeric_limits<T>::is_integer, bool>
36 {
37 // the machine epsilon has to be scaled to the magnitude of the values used
38 // and multiplied by the desired precision in ULPs (units in the last place)
39 return std::abs(left - right) <= std::numeric_limits<T>::epsilon() * std::abs(left + right) * ulp
40 // unless the result is subnormal
41 || std::abs(left - right) < std::numeric_limits<T>::min();
42 }
43
44} // namespace
45
47 std::string_view secondary,
48 std::string_view predicted)
49 : primary_clusters_{ primary }
50 , secondary_clusters_(secondary)
51 , predicted_neutrons_(predicted)
52 , predicted_name_(predicted)
53 , multiplicities_{ std::vector<int>(DEFAULT_MULTI) }
54{
55}
56
58{
59 primary_clusters_.init();
62
63 constexpr auto HIST_BIN_SIZE10 = 10;
64 constexpr auto HIST_BIN_SIZE30 = 10;
65 constexpr auto HIST_BIN_SIZE_F1 = 11;
66 constexpr auto HIST_MAX_BIN_F1 = 1.1;
67
68 TH1::AddDirectory(kFALSE);
69 hist_true_positive_ = data_monitor_.add_hist<TH1D>("fhTP", "True Positive", HIST_BIN_SIZE10, 0., HIST_BIN_SIZE10);
70 hist_false_negative_ = data_monitor_.add_hist<TH1D>("fhFN", "False Negative", HIST_BIN_SIZE10, 0., HIST_BIN_SIZE10);
71 hist_false_positive_ = data_monitor_.add_hist<TH1D>("fhFP", "False Positive", HIST_BIN_SIZE10, 0., HIST_BIN_SIZE10);
72 hist_true_negative_ = data_monitor_.add_hist<TH1D>("fhTN", "True Negative", HIST_BIN_SIZE30, 0., HIST_BIN_SIZE30);
73 hist_f1_value_ = data_monitor_.add_hist<TH1D>("fhF1", "F1 Value", HIST_BIN_SIZE_F1, 0., HIST_MAX_BIN_F1);
74
75 return kSUCCESS;
76}
77
79{
80 const auto& actualPositive = primary_clusters_.get();
81 const auto& actualNegative = secondary_clusters_.get();
82 const auto& predictedPositive = predicted_neutrons_.get();
83
84 auto comp = [](const R3BNeulandNeutron& neutron, const R3BNeulandCluster& cluster)
85 {
86 return almost_equal(cluster.GetT(), neutron.GetT(), 2) &&
87 almost_equal(cluster.GetPosition().X(), neutron.GetPosition().X(), 2) &&
88 almost_equal(cluster.GetPosition().Y(), neutron.GetPosition().Y(), 2) &&
89 almost_equal(cluster.GetPosition().Z(), neutron.GetPosition().Z(), 2);
90 };
91
92 multiplicities_.at(predictedPositive.size())++;
93
94 auto truePositives = 0;
95 for (const auto& cluster : actualPositive)
96 {
97 for (const auto& neutron : predictedPositive)
98 {
99 if (comp(neutron, cluster))
100 {
101 truePositives++;
102 break;
103 }
104 }
105 }
106 const auto falseNegatives = static_cast<int>(actualPositive.size()) - truePositives;
107 true_positive_ += truePositives;
108 hist_true_positive_->Fill(truePositives);
109 false_negative_ += falseNegatives;
110 hist_false_negative_->Fill(falseNegatives);
111
112 auto falsePositives = 0;
113 for (const auto& cluster : actualNegative)
114 {
115 for (const auto& neutron : predictedPositive)
116 {
117 if (comp(neutron, cluster))
118 {
119 falsePositives++;
120 break;
121 }
122 }
123 }
124 const auto trueNegatives = static_cast<int>(actualNegative.size()) - falsePositives;
125 false_positive_ += falsePositives;
126 hist_false_positive_->Fill(falsePositives);
127 true_negative_ += trueNegatives;
128 hist_true_negative_->Fill(trueNegatives);
129
130 const auto precision = truePositives / static_cast<double>(truePositives + falsePositives);
131 const auto recall = truePositives / static_cast<double>(truePositives + falseNegatives);
132
133 const auto Fbeta = [&](double beta)
134 {
135 if (precision == 0. && recall == 0.)
136 {
137 return 0.;
138 }
139 return (1. + beta * beta) * (precision * recall) / ((beta * beta * precision) + recall);
140 };
141 hist_f1_value_->Fill(Fbeta(1.));
142}
143
145{
146 const auto dir_name = fmt::format("{}Statistics", predicted_name_);
147 data_monitor_.save_to_sink(dir_name);
148
149 const auto precision = true_positive_ / static_cast<double>(true_positive_ + false_positive_);
150 const auto recall = true_positive_ / static_cast<double>(true_positive_ + false_negative_);
151 auto Fbeta = [&](double beta)
152 { return (1. + beta * beta) * (precision * recall) / ((beta * beta * precision) + recall); };
153 const double accuracy = (true_positive_ + true_negative_) /
155
156 LOGP(info, "PREC DATA");
157 LOGP(info,
158 "{}\tTruePositive \t{}\t{}",
161 true_positive_ / static_cast<double>(true_positive_ + false_negative_));
162 LOGP(info,
163 "{}\tFalsePositive \t{}\t{}",
166 false_positive_ / static_cast<double>(true_negative_ + false_positive_));
167 LOGP(info,
168 "{}\tFalseNegative \t{}\t{}",
171 false_negative_ / static_cast<double>(true_positive_ + false_negative_));
172 LOGP(info,
173 "{}\tTrueNegative \t{}\t{}",
176 true_negative_ / static_cast<double>(true_negative_ + false_positive_));
177
178 LOGP(info, "{}\tAccuracy \t{}", predicted_name_, accuracy);
179 LOGP(info, "{}\tPrecision \t{}", predicted_name_, precision);
180 LOGP(info, "{}\tSensitivity \t{}", predicted_name_, recall);
181 LOGP(info, "{}\tF1 \t{}", predicted_name_, Fbeta(1.));
182 LOGP(info, "MULT DATA");
183
184 const int sum = std::accumulate(multiplicities_.begin(), multiplicities_.end(), 0);
185 for (unsigned int i = 0; i < multiplicities_.size(); i++)
186 {
187 LOGP(info, "{}\t{}\t{}", i, multiplicities_[i], multiplicities_[i] / static_cast<double>(sum));
188 }
189 LOGP(info, "END");
190}
TVector3 GetPosition() const
Double_t GetT() const
R3BNeulandNeutronReconstructionStatistics(std::string_view primary="NeulandNeutronClusters", std::string_view secondary="NeulandPrimaryClusters", std::string_view predicted="NeulandSecondaryClusters")
R3B::InputVectorConnector< R3BNeulandCluster > secondary_clusters_
R3B::InputVectorConnector< R3BNeulandCluster > primary_clusters_
R3B::InputVectorConnector< R3BNeulandNeutron > predicted_neutrons_