13from ROOT
import Belle2
24import trackfindingcdc.harvest.cdc_peelers
as cdc_peelers
30 return logging.getLogger(__name__)
33CONTACT =
"oliver.frost@desy.de"
37 """Harvester to generate, postprocess and inspect MC events for track-segment-pair fit validation"""
41 generator_module =
"simple_gun"
46 segment_orientation =
"outwards"
49 fit_method_name =
"fuse-sz"
53 """Get the output ROOT filename"""
65 """Harvest and post-process the MC events"""
68 path.add_module(harvesting_module)
69 return harvesting_module
72 """Convert command-line arguments to basf2 argument list"""
75 argument_parser.add_argument(
78 choices=[
"no",
"medium",
"full"],
81 help=
'Amount of monte carlo information to be used in the segment generation.',
84 argument_parser.add_argument(
86 choices=[
"zreco",
"fuse-pre",
"fuse-sz",
"fuse-sz-re"],
88 dest=
"fit_method_name",
89 help=(
"Choose which fit positional information of the segment should be used. \n"
90 "* 'zreco' means the z coordinate is reconstructed and a linear sz fit is made. "
91 "No covariance between the circle and the linear sz part can be made.\n"
92 "* 'fuse-sz' means the Kalmanesk fuse of the two trajectory fits.\n"
93 "* 'fuse-sz-re' means the Kalmanesk fuse of the two trajectory fits but reestimate the drift length."
97 return argument_parser
100 """Determine which track-segment-pair fitter to use"""
103 if fit_method_name ==
'zreco':
106 def z_reconstruction_fit(pair):
107 return sz_fitter.update(pair)
108 return z_reconstruction_fit
110 elif fit_method_name.startswith(
'fuse-pre'):
113 fusionFit = CDCAxialStereoFusion()
115 def sz_segment_pair_preliminary_fit(pair):
116 fusionFit.fusePreliminary(pair)
117 return sz_segment_pair_preliminary_fit
119 elif fit_method_name.startswith(
'fuse-sz'):
122 reestimateDriftLength = fit_method_name.endswith(
"re")
123 fusionFit = CDCAxialStereoFusion(reestimateDriftLength)
125 def sz_segment_pair_fusion_fit(pair):
126 fusionFit.reconstructFuseTrajectories(pair)
129 trajectory3D = pair.getTrajectory3D()
130 revFromSegment = pair.getFromSegment().reversed()
131 revToSegment = pair.getToSegment().reversed()
132 revPair = CDCSegmentPair(revToSegment, revFromSegment)
134 CDCAxialStereoFusion.reconstructFuseTrajectories(revPair)
135 revTrajectory3D = revPair.getTrajectory3D().reversed()
145 print(
"One parameters", [trajectory3D.getLocalHelix().parameters()[i]
for i
in range(5)])
146 print(
"Rev parameters", [revTrajectory3D.getLocalHelix().parameters()[i]
for i
in range(5)])
148 print(
"One covariance")
150 print([trajectory3D.getLocalHelix().helixCovariance()(i, j)
for i
in range(5)])
152 print(
"Rev covariance")
154 print([revTrajectory3D.getLocalHelix().helixCovariance()(i, j)
for i
in range(5)])
159 return sz_segment_pair_fusion_fit
162 raise ValueError(f
"Unexpected fit_positions {fit_method_name}")
164 def create_path(self):
166 Sets up a path that plays back pregenerated events or generates events
167 based on the properties
in the base
class.
169 path = super().create_path()
171 path.add_module("TFCDC_WireHitPreparer",
172 flightTimeEstimation=
"outwards",
176 path.add_module(
"TFCDC_ClusterPreparer")
181 path.add_module(
"TFCDC_SegmentFinderFacetAutomaton",
182 SegmentOrientation=
"outwards"
185 path.add_module(
"TFCDC_SegmentFitter",
186 inputSegments=
"CDCSegment2DVector",
187 updateDriftLength=
True,
188 useAlphaInDriftLength=
True,
193 path.add_module(
"TFCDC_SegmentFinderFacetAutomaton",
195 FacetRelationFilter=
"truth",
196 SegmentOrientation=
"outwards"
199 path.add_module(
"TFCDC_SegmentFitter",
200 inputSegments=
"CDCSegment2DVector",
201 updateDriftLength=
True,
202 useAlphaInDriftLength=
True,
208 path.add_module(
"TFCDC_SegmentCreatorMCTruth",
209 reconstructedDriftLength=
False,
210 reconstructedPositions=
True,
214 path.add_module(
"TFCDC_SegmentFitter",
215 inputSegments=
"CDCSegment2DVector",
216 updateDriftLength=
False,
221 raise ValueError(
"Invalid degree of Monte Carlo information")
223 path.add_module(
"TFCDC_SegmentOrienter",
224 SegmentOrientation=
"outwards",
226 inputSegments=
"CDCSegment2DVector",
227 segments=
"CDCSegment2DVectorOriented"
230 path.add_module(
"TFCDC_TrackFinderSegmentPairAutomaton",
231 inputSegments=
"CDCSegment2DVectorOriented",
232 WriteSegmentPairs=
True,
233 SegmentPairFilter=
"truth",
234 SegmentPairFilterParameters={
"allowReverse":
True},
235 SegmentPairRelationFilter=
"none"
244 """Module to collect information about the generated segment pairs and
245 compose validation plots on terminate."""
250 output_file_name=output_file_name,
251 foreach="CDCSegmentPairVector"
258 """Receive signal at the start of event processing"""
263 """Initialize the MC-hit lookup method"""
267 """Select segment pairs with 4 or more hits per segment and a matching primary MC particle"""
269 from_segment = segment_pair.getFromSegment()
270 to_segment = segment_pair.getToSegment()
271 mc_particle = mc_segment_lookup.getMCParticle(from_segment)
272 return (mc_particle
and
273 is_primary(mc_particle)
and
274 from_segment.size() > 3
and
275 to_segment.size() > 3)
278 """Aggregate the track and MC information for track-segment analysis"""
282 to_segment = segment_pair.getToSegment()
287 fit3d_truth = mc_segment_lookup.getTrajectory3D(to_segment)
289 fb_info = 1
if segment_pair.getAutomatonCell().getCellWeight() > 0
else -1
291 curvature_truth=fb_info * fit3d_truth.getCurvatureXY(),
292 tan_lambda_truth=fb_info * fit3d_truth.getTanLambda(),
296 segment_pair_crops.update(truth_crops)
297 return segment_pair_crops
301 save_histograms = refiners.save_histograms(outlier_z_score=5.0, allow_discrete=
True)
303 save_tree = refiners.save_tree()
306 save_curvature_pull_aux = refiners.save_pull_analysis(
307 part_name=
"curvature",
311 aux_names=[
"superlayer_id_pair",
"tan_lambda_truth"],
316 save_curvature_pull = refiners.save_pull_analysis(
317 part_name=
"curvature",
320 groupby=[
None,
"superlayer_id_pair"],
324 save_absolute_curvature_pull = refiners.save_pull_analysis(
325 part_name=
"curvature",
328 groupby=[
None,
"superlayer_id_pair"],
332 save_tan_lambda_pull = refiners.save_pull_analysis(
333 part_name=
"tan_lambda",
334 quantity_name=
"tan #lambda",
336 groupby=[
None,
"superlayer_id_pair"],
340 save_fit_quality_histograms = refiners.save_histograms(
342 select={
"ndf":
"ndf",
344 "p_value":
"p-value",
346 groupby=[
None,
"superlayer_id_pair"],
347 description=
"Distribution of {part_name} in the segment fits",
351 save_fit_quality_by_tan_lambda_profiles = refiners.save_profiles(
353 "p_value":
"fit p-value",
354 "tan_lambda_truth":
"true tan #lambda",
356 groupby=[
None,
"superlayer_id_pair"],
357 x=
"true tan #lambda",
359 check=(
"The {y_part_name} should be essentially the same over"
360 "the whole range of the tan lambda spectrum"),
361 description=(
"Investigating the reconstruction quality for different "
362 "tan lambda regions of the CDC. Most notably is the superlayer dependency."
363 "For stereo superlayers the curve is not flat but has distinct slope."),
369 """Fit axial and stereo track segment pairs"""
373 """Default method to fit the generated segment pairs."""
376 CDCAxialStereoFusion.reconstructFuseTrajectories(segmentPair,
393 """Called by basf2 for each event"""
397 """Fits all pairs in the StoreArray with designated fit method."""
402 wrapped_segment_pair_relations = stored_segment_pair_relations.obj()
403 segment_pair_relations = wrapped_segment_pair_relations.get()
405 for segment_pair_relation
in segment_pair_relations:
411 run.configure_and_execute_from_commandline()
414if __name__ ==
"__main__":
415 logging.basicConfig(stream=sys.stdout, level=logging.INFO, format=
'%(levelname)s:%(message)s')
def peel_segment_pair(segment_pair, key="{part_name}")
a (simplified) python wrapper for StoreObjPtr.
Utility class implementing the Kalmanesk combination of to two dimensional trajectories to one three ...
static const CDCMCHitLookUp & getInstance()
Getter for the singletone instance.
static const CDCMCSegment2DLookUp & getInstance()
Getter for the singletone instance.
static const CDCSZFitter & getFitter()
Getter for a standard sz line fitter instance.
Class representing a pair of one reconstructed axial segment and one stereo segment in adjacent super...
def default_fit_method(segmentPair)
def __init__(self, fit_method=None)
fit_method
fit_method : function A function called on each stored segment pair as its only argument to update it...
mc_segment_lookup
by default, there is no method to find matching MC track segments
def peel(self, segment_pair)
def pick(self, segment_pair)
def __init__(self, output_file_name)
def create_argument_parser(self, **kwds)
monte_carlo
Degree of refinement of the segment generation.
str fit_method_name
use the Kalmanesk fuse of the two trajectory fits
def harvesting_module(self, path=None)
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.