R3BROOT
R3B analysis software
Loading...
Searching...
No Matches
R3BNeulandCalToHitTask.cxx
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) 2019 GSI Helmholtzzentrum für Schwerionenforschung GmbH *
3 * Copyright (C) 2019-2023 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 "R3BDataMonitor.h"
16#include "R3BException.h"
17#include "R3BNeulandCalData2.h"
20#include "R3BNeulandCommon.h"
21#include "R3BNeulandHit2.h"
23#include "R3BValueError.h"
24#include <FairRootManager.h>
25#include <FairRuntimeDb.h>
26#include <Math/Vector3Dfwd.h>
28#include <R3BShared.h>
29#include <TH1.h>
30#include <cmath>
31#include <fairlogger/Logger.h>
32#include <fmt/core.h>
33#include <string_view>
34#include <vector>
35
36namespace R3B::Neuland
37{
38 namespace
39 {
40 using R3B::ValueErrorD;
41 using R3B::Neuland::CalibratedSignal;
42 using R3B::Neuland::HitModulePar;
43 using ROOT::Math::XYZVector;
44
45 template <typename T>
46 inline auto sqrt_value_error(const ValueError<T>& val_err)
47 {
48 // NOTE: Error evaluation of the sqrt function only uses the first order of Taylor expansion
49 const auto value = std::sqrt(val_err.value);
50 const auto error = 1. / 2 / std::sqrt(val_err.value) * val_err.error;
51 return ValueErrorD{ value, error };
52 }
53
54 inline auto get_hit_energy(const ValueErrorD& first_e, const ValueErrorD& second_e, const HitModulePar& par)
55 {
56 return par.light_attenuation_factor * sqrt_value_error(first_e) * sqrt_value_error(second_e);
57 }
58
59 inline auto get_hit_position(const ValueErrorD& tdc_left, const ValueErrorD& tdc_right, const HitModulePar& par)
60 {
61 const auto plane_id = ModuleID2PlaneID(static_cast<int>(par.module_num - 1));
62 LOGP(debug2,
63 "Calculating position with left tdc: {}, right tdc {}, effective speed: {}, tdc_diff: {}",
64 tdc_left,
65 tdc_right,
66 par.effective_speed,
67 tdc_right - tdc_left);
68 const auto pos_along_bar = -par.effective_speed * (tdc_right - tdc_left) / 2.;
69 const auto pos_perp_bar = ValueErrorD{ GetBarVerticalDisplacement(par.module_num), BarSize_XY / SQRT_12 };
70 const auto pos_z = ValueErrorD{ (plane_id + 0.5) * BarSize_Z, BarSize_Z / SQRT_12 };
71 LOGP(debug2,
72 "pos along the bar: {} cm, pos perp to bar {} cm, z: {} cm",
73 pos_along_bar,
74 pos_perp_bar,
75 pos_z);
76
77 auto is_horizontal = IsPlaneIDHorizontal(plane_id);
78 return is_horizontal ? XYZVectorValueErrorD{ pos_along_bar, pos_perp_bar, pos_z }
79 : XYZVectorValueErrorD{ pos_perp_bar, pos_along_bar, pos_z };
80 }
81
82 inline auto get_hit_pixel(const XYZVectorValueErrorD& position)
83 {
84 const auto pixel_x =
85 std::floor(position.X().value / ::R3B::Neuland::BarSize_XY) + (::R3B::Neuland::BarsPerPlane / 2.);
86 const auto pixel_y =
87 std::floor(position.Y().value / ::R3B::Neuland::BarSize_XY) + (::R3B::Neuland::BarsPerPlane / 2.);
88 const auto pixel_z = std::floor(position.Z().value / ::R3B::Neuland::BarSize_Z);
89
90 return XYZVector{ pixel_x, pixel_y, pixel_z };
91 }
92 } // namespace
93
94 Cal2HitTask::Cal2HitTask(std::string_view input_cal_data_name,
95 std::string_view input_cal_2_hit_par_name,
96 std::string_view output_hit_data_name)
97 : CalibrationTask("R3BNeulandCal2Hit", 1)
98 , cal_data_{ input_cal_data_name }
99 , hit_data_{ output_hit_data_name }
100 , cal_to_hit_par_{ input_cal_2_hit_par_name }
101 {
102 }
103
105
106 void Cal2HitTask::ExtraInit(FairRootManager* /*rootMan*/)
107 {
108 cal_data_.init();
109 hit_data_.init(not IsHistDisabled());
110 if (cal_to_hit_par_->GetModulePars().empty())
111 {
112 throw R3B::logic_error("Cal2HitPar has no modules!");
113 }
114 }
115
116 void Cal2HitTask::SetExtraPar(FairRuntimeDb* /*rtdb*/) { cal_to_hit_par_.init(this); }
117
119
121 {
122 for (const auto& calBar : cal_data_)
123 {
124 temp_left_signals_.clear();
125 temp_right_signals_.clear();
126
127 LOGP(debug1, "Input calBar: {}", calBar);
128 if (calBar.module_num == 0)
129 {
130 throw R3B::runtime_error("cal-level bar signal has invalid module number 0!");
131 }
132
135
136 const auto& module_par = cal_to_hit_par_->GetModuleParAt(calBar.module_num);
138 }
139 }
140
142 /* inout */ std::vector<CalibratedSignal>& signals,
143 Side side)
144 {
145 const auto calBar_signals = (side == Side::left) ? calBar.left : calBar.right;
146
147 const auto& module_par = cal_to_hit_par_->GetModuleParAt(calBar.module_num);
148
149 for (const auto& cal_signal : calBar_signals)
150 {
151 signals.push_back(to_calibrated_signal(cal_signal, module_par, side));
152 }
153 }
154
155 auto Cal2HitTask::construct_hit(const LRPair<CalibratedSignal>& signalPair, const HitModulePar& par) const -> Hit
156 {
157 auto hit = Hit{};
158
159 LOGP(debug2,
160 "Input left calibrated signal: {} and right calibrated signal: {}",
161 signalPair.left(),
162 signalPair.right());
163 // hit module id is 0-based
164 hit.module_id = static_cast<int>(par.module_num - 1);
165 hit.tdc_left = signalPair.left().time;
166 hit.tdc_right = signalPair.right().time;
167 hit.time = get_hit_time(hit.tdc_left, hit.tdc_right);
168 hit.qdc_left = signalPair.left().energy;
169 hit.qdc_right = signalPair.right().energy;
170 hit.energy = get_hit_energy(hit.qdc_left, hit.qdc_right, par);
171 hit.position = get_hit_position(hit.tdc_left, hit.tdc_right, par);
172 hit.pixel = get_hit_pixel(hit.position);
173 LOGP(debug, "Adding a new NeulandHit: {}\n", hit);
174 return hit;
175 }
176
177 void Cal2HitTask::construct_hits(const std::vector<CalibratedSignal>& left_signals,
178 const std::vector<CalibratedSignal>& right_signals,
179 const HitModulePar& par,
180 /* inout */ std::vector<Hit>& hits)
181 {
182 // TODO: Multi-hits needs to be implemented here
183 if (left_signals.size() == 1 and right_signals.size() == 1)
184 {
185 const auto& left_signal = left_signals.front();
186 const auto& right_signal = right_signals.front();
187 // signal_match_checking(left_signal, right_signal, par);
188 const auto new_hit = construct_hit(R3B::LRPair<CalibratedSignal>{ left_signal, right_signal }, par);
189 LOGP(debug, "Adding a new hit with: {}", new_hit);
190 hits.push_back(new_hit);
191 }
192 }
193
195 const CalibratedSignal& second_signal,
196 const HitModulePar& par) -> bool
197 {
198 const auto first_input =
199 SignalMatcher::Input{ .time = first_signal.time.value, .energy = first_signal.energy.value };
200 const auto second_input =
201 SignalMatcher::Input{ .time = second_signal.time.value, .energy = second_signal.energy.value };
202
203 const auto match_par = SignalMatcher::Par{ .attenuation = BarLength / par.light_attenuation_length.value,
204 .c_medium = par.effective_speed.value };
205 const auto match_goodness = SignalMatcher::GetGoodnessOfMatch(first_input, second_input, match_par);
206 const auto match_result = std::log10(match_goodness);
207 // FIXME: BAD comparison values. They should be 0. Why? Need fixing
208 GetHistMonitor().get("match_values")->Fill(match_result);
209 return true;
210 }
211
213
214 auto Cal2HitTask::CheckConditions([[maybe_unused]] TH1L* hist_condition) const -> bool
215 {
217 {
218 const auto t_start = GetEventHeader()->GetTStart();
219 const auto is_beam_on = not std::isnan(t_start);
220 return is_beam_on;
221 }
222 return true;
223 }
224
226 {
227 auto larger_t = (first_t > second_t) ? first_t : second_t;
228 auto smaller_t = (first_t < second_t) ? first_t : second_t;
229
230 // check if the smaller value is overflowed
231 // TODO: overflow should be checked in cal level for better time calibration. But the cal level calibration
232 // doesn't have signal pairing. Solution: Do it in cal level only for one hit bar signal and calibration should
233 // only use those with one hit.
234 if (larger_t - smaller_t > 0.5 * R3B::Neuland::MaxCalTime)
235 {
236 larger_t -= R3B::Neuland::MaxCalTime;
237 }
238 const auto t_start = [this]() -> double
239 {
240 const auto los_time = GetEventHeader()->GetTStart();
241 if (std::isnan(los_time))
242 {
243 return 0.;
244 }
245 return los_time;
246 }();
247 auto time_val = ((larger_t + smaller_t) / 2.) - global_time_offset_ - t_start;
248 time_val.value = std::remainder(time_val.value, R3B::Neuland::MaxCalTime);
249 return time_val;
250 }
251
253 -> ValueErrorD
254 {
255 const auto tot_no_offset = calSignal.time_over_threshold - par.pedestal.get(side);
256
257 const auto denominator = par.energy_gain.get(side) - par.pmt_saturation.get(side) * tot_no_offset;
258
259 if (denominator.value == 0.)
260 {
261 return ValueErrorD{};
262 }
263
264 // apply minimum 1 ns:
265 return (tot_no_offset.value < 1) ? ValueErrorD{} : tot_no_offset / denominator;
266 }
267
269 -> ValueErrorD
270 {
271 // TODO: why positive for left?
272 const auto time_offset =
273 (side == R3B::Side::left) ? (par.t_sync - par.t_diff / 2) : (par.t_sync + par.t_diff / 2);
274 return calSignal.leading_time - calSignal.trigger_time - time_offset;
275 }
276
279 {
280 const auto energy = get_calibrated_energy(calSignal, par, side);
281 const auto time = get_calibrated_time(calSignal, par, side);
282 return CalibratedSignal{ .energy = energy, .time = time };
283 }
284} // namespace R3B::Neuland
static auto get_calibrated_energy(const CalDataSignal &calSignal, const HitModulePar &par, R3B::Side side) -> ValueErrorD
void SetExtraPar(FairRuntimeDb *rtdb) override
void construct_hits(const std::vector< CalibratedSignal > &left_signals, const std::vector< CalibratedSignal > &right_signals, const HitModulePar &par, std::vector< Hit > &hits)
void ExtraInit(FairRootManager *rootMan) override
InputParView< Cal2HitPar > cal_to_hit_par_
auto construct_hit(const LRPair< CalibratedSignal > &signalPair, const HitModulePar &par) const -> Hit
Cal2HitTask(std::string_view input_cal_data_name="NeulandCalData", std::string_view input_cal_2_hit_par_name="NeulandHitPar", std::string_view output_hit_data_name="NeulandHits")
static auto get_calibrated_time(const CalDataSignal &calSignal, const HitModulePar &par, R3B::Side side) -> ValueErrorD
auto get_hit_time(ValueErrorD first_t, ValueErrorD second_t) const -> ValueErrorD
auto CheckConditions(TH1L *hist_condition) const -> bool override
static auto to_calibrated_signal(const CalDataSignal &calSignal, const HitModulePar &par, R3B::Side side) -> CalibratedSignal
std::vector< CalibratedSignal > temp_left_signals_
void calculate_calibrated_signals(const BarCalData &calBar, std::vector< CalibratedSignal > &signals, Side side)
auto signal_match_checking(const CalibratedSignal &first_signal, const CalibratedSignal &second_signal, const HitModulePar &par) -> bool
void HistogramInit(DataMonitor &histograms) override
InputVectorConnector< BarCalData > cal_data_
std::vector< CalibratedSignal > temp_right_signals_
OutputVectorConnector< Hit > hit_data_
auto GetTrigger() const -> CalTrigger
Simulation of NeuLAND Bar/Paddle.
constexpr auto BarsPerPlane
constexpr auto ModuleID2PlaneID(int moduleID) -> int
constexpr auto GetBarVerticalDisplacement(int module_num) -> double
constexpr auto SQRT_12
constexpr auto BarLength
constexpr auto BarSize_Z
constexpr auto BarSize_XY
ROOT::Math::DisplacementVector3D< ROOT::Math::Cartesian3D< ValueErrorD >, ROOT::Math::DefaultCoordinateSystemTag > XYZVectorValueErrorD
constexpr auto IsPlaneIDHorizontal(int plane_id) -> bool
ValueError< double > ValueErrorD
constexpr auto MaxCalTime
ValueError< double > ValueErrorD
std::vector< CalDataSignal > left
std::vector< CalDataSignal > right
static auto GetGoodnessOfMatch(const Input &firstSignal, const Input &secondSignal, const Par &par) -> float