12from ROOT
import Belle2
24import trackfindingcdc.harvest.cdc_peelers
as cdc_peelers
31 return logging.getLogger(__name__)
34CONTACT =
"oliver.frost@desy.de"
38 """Harvester to read, postprocess and inspect MC events for track-segment fit validation"""
43 generator_module =
"low_gun"
45 root_input_file =
"low_gun.root"
52 flight_time_estimation =
"none"
54 flight_time_reestimation =
False
56 use_alpha_in_drift_length =
True
58 flight_time_mass_scale = float(
"nan")
61 fit_positions =
"recoPos"
63 fit_variance =
"proper"
67 """Get the output ROOT filename"""
68 file_name =
"karimaki" if self.
karimaki_fit else "riemann"
80 """Harvest and post-process the MC events"""
83 path.add_module(harvesting_module)
84 return harvesting_module
87 """Convert command-line arguments to basf2 argument list"""
90 argument_parser.add_argument(
93 choices=[
"no",
"medium",
"full"],
96 help=
'Amount of monte carlo information to be used in the segment generation.',
99 argument_parser.add_argument(
104 help=
'Use Karimaki fit instead of Riemann fit'
107 argument_parser.add_argument(
110 choices=[
"recoPos",
"rlDriftCircle",
"wirePos"],
112 dest=
"fit_positions",
113 help=(
"Choose which positional information the segment fit should be used. \n"
114 "* 'wirePos' means only the wire position\n"
115 "* 'recoPos' means only the reconstructed position\n"
116 "* 'rlDriftCircle' means only the drift circle with the right left passage\n")
119 argument_parser.add_argument(
122 choices=[
"unit",
"driftLength",
"pseudo",
"proper"],
125 help=(
"Choose which variance information the segment fit should be used. \n"
126 "* 'unit' means equal variance of 1\n"
127 "* 'driftLength' means inserting the drift length as variance, very improper because dimension mismatch\n"
128 "* 'pseudo' means the squared dirft length + plus the drift length variance "
129 "(correct dimension, proper lower bound)\n"
130 "* 'proper' means only the drift length variance\n")
133 argument_parser.add_argument(
135 "--flight-time-estimation",
136 choices=[
"none",
"outwards",
"downwards"],
138 dest=
"flight_time_estimation",
139 help=(
"Choose which estimation method for the time of flight should be use. \n"
140 "* 'none' no time of flight corrections\n"
141 "* 'outwards' means the minimal time needed to travel to the wire from the interaction point \n"
142 "* 'downwards' means the minimal time needed to travel to the wire from the y = 0 plane downwards \n")
145 argument_parser.add_argument(
147 "--flight-time-reestimation",
149 dest=
"flight_time_reestimation",
150 help=(
"Switch to reestimate drift length before fitting.")
153 argument_parser.add_argument(
155 "--use-alpha-in-drift-length",
157 dest=
"use_alpha_in_drift_length",
158 help=(
"Switch to serve the alpha angle to the drift length translator.")
161 argument_parser.add_argument(
163 "--flight-time-mass-scale",
165 dest=
"flight_time_mass_scale",
166 help=(
"Mass parameter to estimate the velocity in the time of flight estimation")
169 return argument_parser
171 def create_path(self):
173 Sets up a path that plays back pregenerated events or generates events
174 based on the properties
in the base
class.
176 path = super().create_path()
178 path.add_module("TFCDC_WireHitPreparer",
183 path.add_module(
"TFCDC_ClusterPreparer")
189 "TFCDC_SegmentFinderFacetAutomaton",
191 SegmentOrientation=
"none",
196 path.add_module(
"TFCDC_SegmentFinderFacetAutomaton",
198 FacetRelationFilter=
"truth",
199 SegmentOrientation=
"none",
205 path.add_module(
"TFCDC_SegmentCreatorMCTruth",
206 reconstructedDriftLength=
False,
207 reconstructedPositions=
True)
210 raise ValueError(
"Invalid degree of Monte Carlo information")
212 path.add_module(
"TFCDC_SegmentFitter",
213 inputSegments=
"CDCSegment2DVector",
226 """Module to collect information about the generated segments and
227 compose validation plots on terminate."""
231 super().__init__(foreach='CDCSegment2DVector',
232 output_file_name=output_file_name)
238 """Receive signal at the start of event processing"""
247 """Initialize the MC-hit lookup method"""
251 """Select segments with 4 or more hits and a matching primary MC particle"""
253 mc_particle = mc_segment_lookup.getMCParticle(segment)
256 return mc_particle
and is_primary(mc_particle)
and segment.size() > 3
259 """Aggregate the track and MC information for track-segment analysis"""
266 mc_particle = mc_segment_lookup.getMCParticle(segment)
267 mc_particle_crops = peelers.peel_mc_particle(mc_particle)
269 fit3d_truth = mc_segment_lookup.getTrajectory3D(segment)
270 curvature_truth = fit3d_truth.getCurvatureXY()
272 reconstructed_backward = mc_segment_lookup.isForwardOrBackwardToMCTrack(segment)
273 is_fake = mc_segment_lookup.getMCTrackId(segment) < 0
275 if reconstructed_backward != 1:
276 curvature_truth = -curvature_truth
279 tan_lambda_truth=fit3d_truth.getTanLambda(),
280 curvature_truth=curvature_truth,
281 abs_curvature_truth=abs(curvature_truth),
282 curvature_residual=segment_crops[
"curvature_estimate"] - curvature_truth,
283 curvature_pull=(segment_crops[
"curvature_estimate"] - curvature_truth) / segment_crops[
"curvature_variance"],
284 reconstructed_backward=reconstructed_backward,
290 n_total = segment.size()
291 first_i_incorrect = float(
"nan")
292 last_i_incorrect = float(
"nan")
293 first_l_incorrect = 0
298 for i, reco_hit2d
in enumerate(segment):
299 rl_info = reco_hit2d.getRLInfo()
300 hit = reco_hit2d.getWireHit().getHit()
301 true_rl_info = mc_hit_lookup.getRLInfo(hit)
303 if rl_info != last_rl_info:
305 last_rl_info = rl_info
307 if true_rl_info == rl_info:
310 if first_i_incorrect != first_i_incorrect:
311 first_i_incorrect = i
312 first_l_incorrect = reco_hit2d.getRefDriftLength()
314 last_l_incorrect = reco_hit2d.getRefDriftLength()
317 elif rl_info == -1
or rl_info == 65535:
320 alias_score = segment.getAliasScore()
325 n_incorrect=n_total - n_correct,
326 rl_purity=n_correct / n_total,
327 first_incorrect_location=first_i_incorrect / (n_total - 1),
328 first_l_incorrect=first_l_incorrect,
329 last_incorrect_location=last_i_incorrect / (n_total - 1),
330 last_l_incorrect=last_l_incorrect,
331 n_rl_switch=n_rl_switch,
332 n_rl_asymmetry=rl_sum / n_total,
333 n_abs_rl_asymmetry=abs(rl_sum) / n_total,
334 may_alias=alias_score == 0,
335 alias_score=alias_score
338 segment_crops.update(truth_crops)
339 segment_crops.update(mc_particle_crops)
340 segment_crops.update(rl_crops)
345 save_histograms = refiners.save_histograms(outlier_z_score=5.0, allow_discrete=
True)
347 save_tree = refiners.save_tree()
350 save_curvature_pull = refiners.save_pull_analysis(
351 part_name=
"curvature",
354 aux_names=[
"tan_lambda_truth",
"abs_curvature_truth"],
363 save_curvature_pull_rl_pure = refiners.save_pull_analysis(
364 part_name=
"curvature",
367 filter_on=
"rl_purity",
368 filter=
lambda rl_purity: rl_purity > 0.5,
369 aux_names=[
"tan_lambda_truth",
"abs_curvature_truth"],
376 folder_name=
"rl_pure/{groupby_addition}"
380 save_absolute_curvature_pull = refiners.save_pull_analysis(
381 part_name=
"curvature",
384 aux_names=[
"tan_lambda_truth",
"abs_curvature_truth"],
393 save_fit_quality_histograms = refiners.save_histograms(
398 "p_value":
"p-value",
399 "tan_lambda_truth":
"true tan #lambda",
401 groupby=[
None,
"stereo_kind",
"superlayer_id"],
402 title=
"Histogram of {part_name}{stacked_by_indication}{stackby}",
403 description=
"Distribution of {part_name} in the segment fits",
409 run.configure_and_execute_from_commandline()
412if __name__ ==
"__main__":
413 logging.basicConfig(stream=sys.stdout, level=logging.INFO, format=
'%(levelname)s:%(message)s')
def peel_segment2d(segment, key="{part_name}")
static const CDCMCHitLookUp & getInstance()
Getter for the singletone instance.
static const CDCMCSegment2DLookUp & getInstance()
Getter for the singletone instance.
mc_hit_lookup
Method to find matching MC hits.
mc_segment_lookup
by default, there is no method to find matching MC track segments
def __init__(self, output_file_name)
def create_argument_parser(self, **kwds)
float flight_time_mass_scale
assume a very heavy particle for flight time calculation
monte_carlo
Degree of refinement of the segment generation.
bool flight_time_reestimation
do not re-estimate the flight time
bool use_alpha_in_drift_length
use the alpha parameter for drift length
bool karimaki_fit
use the Riemann fit instead of the Karimaki fit
str fit_positions
fit the reconstructed hit position for the segment fit
str fit_variance
use the drift-length variance in the segment fit
def harvesting_module(self, path=None)
str root_input_file
read generated/simulated/reconstructed events from this ROOT file
str flight_time_estimation
do not estimate the flight time
str monte_carlo
do not generate new events
def output_file_name(self)
None output_file_name
Disable the writing of an output ROOT file.
root_input_file
generating events, so there is no ROOT input file
None root_input_file
By default, there is no input ROOT TFile.
None output_file_name
There is no default for the name of the output TFile.