13 from ROOT
import Belle2
25 import trackfindingcdc.harvest.cdc_peelers
as cdc_peelers
32 return logging.getLogger(__name__)
35 CONTACT =
"oliver.frost@desy.de"
39 """Harvester to read, postprocess and inspect MC events for track-segment fit validation"""
44 generator_module =
"low_gun"
46 root_input_file =
"low_gun.root"
53 flight_time_estimation =
"none"
55 flight_time_reestimation =
False
57 use_alpha_in_drift_length =
True
59 flight_time_mass_scale = float(
"nan")
62 fit_positions =
"recoPos"
64 fit_variance =
"proper"
68 """Get the output ROOT filename"""
69 file_name =
"karimaki" if self.
karimaki_fitkarimaki_fit
else "riemann"
81 """Harvest and post-process the MC events"""
84 path.add_module(harvesting_module)
85 return harvesting_module
88 """Convert command-line arguments to basf2 argument list"""
91 argument_parser.add_argument(
94 choices=[
"no",
"medium",
"full"],
97 help=
'Amount of monte carlo information to be used in the segment generation.',
100 argument_parser.add_argument(
105 help=
'Use Karimaki fit instead of Riemann fit'
108 argument_parser.add_argument(
111 choices=[
"recoPos",
"rlDriftCircle",
"wirePos"],
113 dest=
"fit_positions",
114 help=(
"Choose which positional information the segment fit should be used. \n"
115 "* 'wirePos' means only the wire position\n"
116 "* 'recoPos' means only the reconstructed position\n"
117 "* 'rlDriftCircle' means only the drift circle with the right left passage\n")
120 argument_parser.add_argument(
123 choices=[
"unit",
"driftLength",
"pseudo",
"proper"],
126 help=(
"Choose which variance information the segment fit should be used. \n"
127 "* 'unit' means equal variance of 1\n"
128 "* 'driftLength' means inserting the drift length as variance, very improper because dimension mismatch\n"
129 "* 'pseudo' means the squared dirft length + plus the drift length variance "
130 "(correct dimension, proper lower bound)\n"
131 "* 'proper' means only the drift length variance\n")
134 argument_parser.add_argument(
136 "--flight-time-estimation",
137 choices=[
"none",
"outwards",
"downwards"],
139 dest=
"flight_time_estimation",
140 help=(
"Choose which estimation method for the time of flight should be use. \n"
141 "* 'none' no time of flight corrections\n"
142 "* 'outwards' means the minimal time needed to travel to the wire from the interaction point \n"
143 "* 'downwards' means the minimal time needed to travel to the wire from the y = 0 plane downwards \n")
146 argument_parser.add_argument(
148 "--flight-time-reestimation",
150 dest=
"flight_time_reestimation",
151 help=(
"Switch to reestimate drift length before fitting.")
154 argument_parser.add_argument(
156 "--use-alpha-in-drift-length",
158 dest=
"use_alpha_in_drift_length",
159 help=(
"Switch to serve the alpha angle to the drift length translator.")
162 argument_parser.add_argument(
164 "--flight-time-mass-scale",
166 dest=
"flight_time_mass_scale",
167 help=(
"Mass parameter to estimate the velocity in the time of flight estimation")
170 return argument_parser
174 Sets up a path that plays back pregenerated events or generates events
175 based on the properties in the base class.
179 path.add_module(
"TFCDC_WireHitPreparer",
184 path.add_module(
"TFCDC_ClusterPreparer")
190 "TFCDC_SegmentFinderFacetAutomaton",
192 SegmentOrientation=
"none",
197 path.add_module(
"TFCDC_SegmentFinderFacetAutomaton",
199 FacetRelationFilter=
"truth",
200 SegmentOrientation=
"none",
206 path.add_module(
"TFCDC_SegmentCreatorMCTruth",
207 reconstructedDriftLength=
False,
208 reconstructedPositions=
True)
211 raise ValueError(
"Invalid degree of Monte Carlo information")
213 path.add_module(
"TFCDC_SegmentFitter",
214 inputSegments=
"CDCSegment2DVector",
227 """Module to collect information about the generated segments and
228 compose validation plots on terminate."""
232 super().
__init__(foreach=
'CDCSegment2DVector',
233 output_file_name=output_file_name)
239 """Receive signal at the start of event processing"""
248 """Initialize the MC-hit lookup method"""
252 """Select segments with 4 or more hits and a matching primary MC particle"""
254 mc_particle = mc_segment_lookup.getMCParticle(segment)
257 return mc_particle
and is_primary(mc_particle)
and segment.size() > 3
260 """Aggregate the track and MC information for track-segment analysis"""
267 mc_particle = mc_segment_lookup.getMCParticle(segment)
268 mc_particle_crops = peelers.peel_mc_particle(mc_particle)
270 fit3d_truth = mc_segment_lookup.getTrajectory3D(segment)
271 curvature_truth = fit3d_truth.getCurvatureXY()
273 reconstructed_backward = mc_segment_lookup.isForwardOrBackwardToMCTrack(segment)
274 is_fake = mc_segment_lookup.getMCTrackId(segment) < 0
276 if reconstructed_backward != 1:
277 curvature_truth = -curvature_truth
280 tan_lambda_truth=fit3d_truth.getTanLambda(),
281 curvature_truth=curvature_truth,
282 abs_curvature_truth=abs(curvature_truth),
283 curvature_residual=segment_crops[
"curvature_estimate"] - curvature_truth,
284 curvature_pull=(segment_crops[
"curvature_estimate"] - curvature_truth) / segment_crops[
"curvature_variance"],
285 reconstructed_backward=reconstructed_backward,
291 n_total = segment.size()
292 first_i_incorrect = float(
"nan")
293 last_i_incorrect = float(
"nan")
294 first_l_incorrect = 0
299 for i, reco_hit2d
in enumerate(segment):
300 rl_info = reco_hit2d.getRLInfo()
301 hit = reco_hit2d.getWireHit().getHit()
302 true_rl_info = mc_hit_lookup.getRLInfo(hit)
304 if rl_info != last_rl_info:
306 last_rl_info = rl_info
308 if true_rl_info == rl_info:
311 if first_i_incorrect != first_i_incorrect:
312 first_i_incorrect = i
313 first_l_incorrect = reco_hit2d.getRefDriftLength()
315 last_l_incorrect = reco_hit2d.getRefDriftLength()
318 elif rl_info == -1
or rl_info == 65535:
321 alias_score = segment.getAliasScore()
326 n_incorrect=n_total - n_correct,
327 rl_purity=n_correct / n_total,
328 first_incorrect_location=first_i_incorrect / (n_total - 1),
329 first_l_incorrect=first_l_incorrect,
330 last_incorrect_location=last_i_incorrect / (n_total - 1),
331 last_l_incorrect=last_l_incorrect,
332 n_rl_switch=n_rl_switch,
333 n_rl_asymmetry=rl_sum / n_total,
334 n_abs_rl_asymmetry=abs(rl_sum) / n_total,
335 may_alias=alias_score == 0,
336 alias_score=alias_score
339 segment_crops.update(truth_crops)
340 segment_crops.update(mc_particle_crops)
341 segment_crops.update(rl_crops)
346 save_histograms = refiners.save_histograms(outlier_z_score=5.0, allow_discrete=
True)
348 save_tree = refiners.save_tree()
351 save_curvature_pull = refiners.save_pull_analysis(
352 part_name=
"curvature",
355 aux_names=[
"tan_lambda_truth",
"abs_curvature_truth"],
364 save_curvature_pull_rl_pure = refiners.save_pull_analysis(
365 part_name=
"curvature",
368 filter_on=
"rl_purity",
369 filter=
lambda rl_purity: rl_purity > 0.5,
370 aux_names=[
"tan_lambda_truth",
"abs_curvature_truth"],
377 folder_name=
"rl_pure/{groupby_addition}"
381 save_absolute_curvature_pull = refiners.save_pull_analysis(
382 part_name=
"curvature",
385 aux_names=[
"tan_lambda_truth",
"abs_curvature_truth"],
394 save_fit_quality_histograms = refiners.save_histograms(
399 "p_value":
"p-value",
400 "tan_lambda_truth":
"true tan #lambda",
402 groupby=[
None,
"stereo_kind",
"superlayer_id"],
403 title=
"Histogram of {part_name}{stacked_by_indication}{stackby}",
404 description=
"Distribution of {part_name} in the segment fits",
410 run.configure_and_execute_from_commandline()
413 if __name__ ==
"__main__":
414 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)
string root_input_file
read generated/simulated/reconstructed events from this ROOT file
def create_argument_parser(self, **kwds)
string flight_time_estimation
do not estimate the flight time
monte_carlo
Degree of refinement of the segment generation.
bool flight_time_reestimation
do not re-estimate the flight time
string fit_variance
use the drift-length variance in the segment fit
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
string fit_positions
fit the reconstructed hit position for the segment fit
string monte_carlo
do not generate new events
def harvesting_module(self, path=None)
flight_time_mass_scale
assume a very heavy particle for flight time calculation
def output_file_name(self)
output_file_name
Disable the writing of an output ROOT file.
root_input_file
generating events, so there is no ROOT input file
root_input_file
By default, there is no input ROOT TFile.
output_file_name
There is no default for the name of the output TFile.
int main(int argc, char **argv)
Run all tests.