12from ROOT
import Belle2
24import trackfindingcdc.harvest.cdc_peelers
as cdc_peelers
31 return logging.getLogger(__name__)
35 """Harvester to read, postprocess and inspect MC events for track-segment fit validation"""
40 generator_module =
"low_gun"
42 root_input_file =
"low_gun.root"
49 flight_time_estimation =
"none"
51 flight_time_reestimation =
False
53 use_alpha_in_drift_length =
True
55 flight_time_mass_scale = float(
"nan")
58 fit_positions =
"recoPos"
60 fit_variance =
"proper"
64 """Get the output ROOT filename"""
65 file_name =
"karimaki" if self.
karimaki_fit else "riemann"
77 """Harvest and post-process the MC events"""
80 path.add_module(harvesting_module)
81 return harvesting_module
84 """Convert command-line arguments to basf2 argument list"""
87 argument_parser.add_argument(
90 choices=[
"no",
"medium",
"full"],
93 help=
'Amount of monte carlo information to be used in the segment generation.',
96 argument_parser.add_argument(
101 help=
'Use Karimaki fit instead of Riemann fit'
104 argument_parser.add_argument(
107 choices=[
"recoPos",
"rlDriftCircle",
"wirePos"],
109 dest=
"fit_positions",
110 help=(
"Choose which positional information the segment fit should be used. \n"
111 "* 'wirePos' means only the wire position\n"
112 "* 'recoPos' means only the reconstructed position\n"
113 "* 'rlDriftCircle' means only the drift circle with the right left passage\n")
116 argument_parser.add_argument(
119 choices=[
"unit",
"driftLength",
"pseudo",
"proper"],
122 help=(
"Choose which variance information the segment fit should be used. \n"
123 "* 'unit' means equal variance of 1\n"
124 "* 'driftLength' means inserting the drift length as variance, very improper because dimension mismatch\n"
125 "* 'pseudo' means the squared dirft length + plus the drift length variance "
126 "(correct dimension, proper lower bound)\n"
127 "* 'proper' means only the drift length variance\n")
130 argument_parser.add_argument(
132 "--flight-time-estimation",
133 choices=[
"none",
"outwards",
"downwards"],
135 dest=
"flight_time_estimation",
136 help=(
"Choose which estimation method for the time of flight should be use. \n"
137 "* 'none' no time of flight corrections\n"
138 "* 'outwards' means the minimal time needed to travel to the wire from the interaction point \n"
139 "* 'downwards' means the minimal time needed to travel to the wire from the y = 0 plane downwards \n")
142 argument_parser.add_argument(
144 "--flight-time-reestimation",
146 dest=
"flight_time_reestimation",
147 help=(
"Switch to reestimate drift length before fitting.")
150 argument_parser.add_argument(
152 "--use-alpha-in-drift-length",
154 dest=
"use_alpha_in_drift_length",
155 help=(
"Switch to serve the alpha angle to the drift length translator.")
158 argument_parser.add_argument(
160 "--flight-time-mass-scale",
162 dest=
"flight_time_mass_scale",
163 help=(
"Mass parameter to estimate the velocity in the time of flight estimation")
166 return argument_parser
168 def create_path(self):
170 Sets up a path that plays back pregenerated events or generates events
171 based on the properties
in the base
class.
173 path = super().create_path()
175 path.add_module("TFCDC_WireHitPreparer",
180 path.add_module(
"TFCDC_ClusterPreparer")
186 "TFCDC_SegmentFinderFacetAutomaton",
188 SegmentOrientation=
"none",
193 path.add_module(
"TFCDC_SegmentFinderFacetAutomaton",
195 FacetRelationFilter=
"truth",
196 SegmentOrientation=
"none",
202 path.add_module(
"TFCDC_SegmentCreatorMCTruth",
203 reconstructedDriftLength=
False,
204 reconstructedPositions=
True)
207 raise ValueError(
"Invalid degree of Monte Carlo information")
209 path.add_module(
"TFCDC_SegmentFitter",
210 inputSegments=
"CDCSegment2DVector",
223 """Module to collect information about the generated segments and
224 compose validation plots on terminate."""
228 super().__init__(foreach='CDCSegment2DVector',
229 output_file_name=output_file_name)
235 """Receive signal at the start of event processing"""
244 """Initialize the MC-hit lookup method"""
248 """Select segments with 4 or more hits and a matching primary MC particle"""
250 mc_particle = mc_segment_lookup.getMCParticle(segment)
253 return mc_particle
and is_primary(mc_particle)
and segment.size() > 3
256 """Aggregate the track and MC information for track-segment analysis"""
263 mc_particle = mc_segment_lookup.getMCParticle(segment)
264 mc_particle_crops = peelers.peel_mc_particle(mc_particle)
266 fit3d_truth = mc_segment_lookup.getTrajectory3D(segment)
267 curvature_truth = fit3d_truth.getCurvatureXY()
269 reconstructed_backward = mc_segment_lookup.isForwardOrBackwardToMCTrack(segment)
270 is_fake = mc_segment_lookup.getMCTrackId(segment) < 0
272 if reconstructed_backward != 1:
273 curvature_truth = -curvature_truth
276 tan_lambda_truth=fit3d_truth.getTanLambda(),
277 curvature_truth=curvature_truth,
278 abs_curvature_truth=abs(curvature_truth),
279 curvature_residual=segment_crops[
"curvature_estimate"] - curvature_truth,
280 curvature_pull=(segment_crops[
"curvature_estimate"] - curvature_truth) / segment_crops[
"curvature_variance"],
281 reconstructed_backward=reconstructed_backward,
287 n_total = segment.size()
288 first_i_incorrect = float(
"nan")
289 last_i_incorrect = float(
"nan")
290 first_l_incorrect = 0
295 for i, reco_hit2d
in enumerate(segment):
296 rl_info = reco_hit2d.getRLInfo()
297 hit = reco_hit2d.getWireHit().getHit()
298 true_rl_info = mc_hit_lookup.getRLInfo(hit)
300 if rl_info != last_rl_info:
302 last_rl_info = rl_info
304 if true_rl_info == rl_info:
307 if first_i_incorrect != first_i_incorrect:
308 first_i_incorrect = i
309 first_l_incorrect = reco_hit2d.getRefDriftLength()
311 last_l_incorrect = reco_hit2d.getRefDriftLength()
314 elif rl_info == -1
or rl_info == 65535:
317 alias_score = segment.getAliasScore()
322 n_incorrect=n_total - n_correct,
323 rl_purity=n_correct / n_total,
324 first_incorrect_location=first_i_incorrect / (n_total - 1),
325 first_l_incorrect=first_l_incorrect,
326 last_incorrect_location=last_i_incorrect / (n_total - 1),
327 last_l_incorrect=last_l_incorrect,
328 n_rl_switch=n_rl_switch,
329 n_rl_asymmetry=rl_sum / n_total,
330 n_abs_rl_asymmetry=abs(rl_sum) / n_total,
331 may_alias=alias_score == 0,
332 alias_score=alias_score
335 segment_crops.update(truth_crops)
336 segment_crops.update(mc_particle_crops)
337 segment_crops.update(rl_crops)
342 save_histograms = refiners.save_histograms(outlier_z_score=5.0, allow_discrete=
True)
344 save_tree = refiners.save_tree()
347 save_curvature_pull = refiners.save_pull_analysis(
348 part_name=
"curvature",
351 aux_names=[
"tan_lambda_truth",
"abs_curvature_truth"],
360 save_curvature_pull_rl_pure = refiners.save_pull_analysis(
361 part_name=
"curvature",
364 filter_on=
"rl_purity",
365 filter=
lambda rl_purity: rl_purity > 0.5,
366 aux_names=[
"tan_lambda_truth",
"abs_curvature_truth"],
373 folder_name=
"rl_pure/{groupby_addition}"
377 save_absolute_curvature_pull = refiners.save_pull_analysis(
378 part_name=
"curvature",
381 aux_names=[
"tan_lambda_truth",
"abs_curvature_truth"],
390 save_fit_quality_histograms = refiners.save_histograms(
395 "p_value":
"p-value",
396 "tan_lambda_truth":
"true tan #lambda",
398 groupby=[
None,
"stereo_kind",
"superlayer_id"],
399 title=
"Histogram of {part_name}{stacked_by_indication}{stackby}",
400 description=
"Distribution of {part_name} in the segment fits",
406 run.configure_and_execute_from_commandline()
409if __name__ ==
"__main__":
410 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.