Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-10-17 22:59:01

0001 #include "RecoTracker/MkFitCMS/standalone/buildtestMPlex.h"
0002 #include "RecoTracker/MkFitCore/standalone/ConfigStandalone.h"
0003 #include "RecoTracker/MkFitCore/src/Matrix.h"
0004 #include "RecoTracker/MkFitCore/interface/MkBuilder.h"
0005 #include "RecoTracker/MkFitCMS/interface/MkStdSeqs.h"
0006 #include "RecoTracker/MkFitCMS/standalone/MkStandaloneSeqs.h"
0007 
0008 #include <memory>
0009 
0010 namespace mkfit {
0011 
0012   inline bool sortByHitsChi2(const std::pair<Track, TrackState> &cand1, const std::pair<Track, TrackState> &cand2) {
0013     if (cand1.first.nFoundHits() == cand2.first.nFoundHits())
0014       return cand1.first.chi2() < cand2.first.chi2();
0015 
0016     return cand1.first.nFoundHits() > cand2.first.nFoundHits();
0017   }
0018 
0019   inline bool sortByPhi(const Hit &hit1, const Hit &hit2) {
0020     return std::atan2(hit1.y(), hit1.x()) < std::atan2(hit2.y(), hit2.x());
0021   }
0022 
0023   inline bool sortByEta(const Hit &hit1, const Hit &hit2) { return hit1.eta() < hit2.eta(); }
0024 
0025   inline bool sortTracksByEta(const Track &track1, const Track &track2) { return track1.momEta() < track2.momEta(); }
0026 
0027   inline bool sortTracksByPhi(const Track &track1, const Track &track2) { return track1.momPhi() < track2.momPhi(); }
0028 
0029   struct sortTracksByPhiStruct {
0030     const std::vector<std::vector<Track>> &m_track_candidates;
0031 
0032     sortTracksByPhiStruct(std::vector<std::vector<Track>> *track_candidates) : m_track_candidates(*track_candidates) {}
0033 
0034     bool operator()(const std::pair<int, int> &track1, const std::pair<int, int> &track2) {
0035       return m_track_candidates[track1.first][track1.second].posPhi() <
0036              m_track_candidates[track2.first][track2.second].posPhi();
0037     }
0038   };
0039 
0040   // within a layer with a "reasonable" geometry, ordering by Z is the same as eta
0041   inline bool sortByZ(const Hit &hit1, const Hit &hit2) { return hit1.z() < hit2.z(); }
0042 
0043   //==============================================================================
0044   // NaN and Silly track parameter check
0045   //==============================================================================
0046 
0047   namespace {
0048 
0049     int check_nan_n_silly(TrackVec &tracks, const char *prefix) {
0050       int count = 0;
0051       for (auto &t : tracks) {
0052         if (t.hasSillyValues(Const::nan_n_silly_print_bad_cands_bkfit, false, prefix)) {
0053           ++count;
0054         }
0055       }
0056       return count;
0057     }
0058 
0059     void check_nan_n_silly_candidates(Event &ev) {
0060       // MIMI -- nan_n_silly_per_layer_count is in MkBuilder, could be in MkJob.
0061       // if (Const::nan_n_silly_check_cands_every_layer)
0062       // {
0063       //   int sc = (int) ev.nan_n_silly_per_layer_count_;
0064       //   if (sc > 0)
0065       //     printf("Nan'n'Silly: Number of silly candidates over all layers = %d\n", sc);
0066       // }
0067       if (Const::nan_n_silly_check_cands_pre_bkfit) {
0068         int sc = check_nan_n_silly(ev.candidateTracks_, "Pre-bkfit silly check");
0069         if (sc > 0)
0070           printf("Nan'n'Silly: Number of silly pre-bkfit candidates = %d\n", sc);
0071       }
0072     }
0073 
0074     void check_nan_n_silly_bkfit(Event &ev) {
0075       if (Const::nan_n_silly_check_cands_post_bkfit) {
0076         int sc = check_nan_n_silly(ev.fitTracks_, "Post-bkfit silly check");
0077         if (sc > 0)
0078           printf("Nan'n'Silly: Number of silly post-bkfit candidates = %d\n", sc);
0079       }
0080     }
0081 
0082   }  // namespace
0083 
0084   //==============================================================================
0085   // runBuildTestPlexDumbCMSSW
0086   //==============================================================================
0087 
0088   void runBuildingTestPlexDumbCMSSW(Event &ev, const EventOfHits &eoh, MkBuilder &builder) {
0089     const IterationConfig &itconf = Config::ItrInfo[0];
0090 
0091     MkJob job({Config::TrkInfo, itconf, eoh, eoh.refBeamSpot()});
0092 
0093     builder.begin_event(&job, &ev, __func__);
0094 
0095     if (Config::sim_val_for_cmssw) {
0096       StdSeq::root_val_dumb_cmssw(&ev);
0097     }
0098 
0099     builder.end_event();
0100   }
0101 
0102   //==============================================================================
0103   // runBuildTestPlexBestHit
0104   //==============================================================================
0105 
0106   double runBuildingTestPlexBestHit(Event &ev, const EventOfHits &eoh, MkBuilder &builder) {
0107     const IterationConfig &itconf = Config::ItrInfo[0];
0108 
0109     const bool validation_on = (Config::sim_val || Config::quality_val);
0110 
0111     TrackVec seeds1;
0112     if (validation_on) {
0113       unsigned int algorithms[] = {4};  //only initialStep
0114 
0115       for (auto const &s : ev.seedTracks_) {
0116         //keep seeds form the first iteration for processing
0117         if (std::find(algorithms, algorithms + 1, s.algoint()) != algorithms + 1)
0118           seeds1.push_back(s);
0119       }
0120       ev.seedTracks_.swap(seeds1);  //necessary for the validation - PrepareSeeds
0121       ev.relabel_bad_seedtracks();  //necessary for the validation - PrepareSeeds
0122     }
0123 
0124     IterationMaskIfc mask_ifc;
0125 
0126     // To disable hit-masks, pass nullptr in place of &mask_ifc to MkJob ctor
0127     // and optionally comment out ev.fill_hitmask_bool_vectors() call.
0128 
0129     ev.fill_hitmask_bool_vectors(itconf.m_track_algorithm, mask_ifc.m_mask_vector);
0130 
0131     MkJob job({Config::TrkInfo, itconf, eoh, eoh.refBeamSpot(), &mask_ifc});
0132 
0133     builder.begin_event(&job, &ev, __func__);
0134 
0135     bool seeds_sorted = false;
0136     // CCCC builder.PrepareSeeds();
0137 
0138     // EventOfCandidates event_of_cands;
0139     builder.find_tracks_load_seeds_BH(ev.seedTracks_, seeds_sorted);
0140 
0141 #ifdef USE_VTUNE_PAUSE
0142     __SSC_MARK(0x111);  // use this to resume Intel SDE at the same point
0143     __itt_resume();
0144 #endif
0145 
0146     double time = dtime();
0147 
0148     builder.findTracksBestHit();
0149 
0150     time = dtime() - time;
0151 
0152 #ifdef USE_VTUNE_PAUSE
0153     __itt_pause();
0154     __SSC_MARK(0x222);  // use this to pause Intel SDE at the same point
0155 #endif
0156 
0157     // Hack, get the tracks out.
0158     ev.candidateTracks_ = builder.ref_tracks();
0159 
0160     // For best hit, the candidateTracks_ vector is the direct input to the backward fit so only need to do clean_duplicates once
0161     if (Config::quality_val || Config::sim_val || Config::cmssw_val) {
0162       //Mark tracks as duplicates; if within CMSSW, remove duplicate tracks before backward fit
0163       // CCCC if (Config::removeDuplicates) {
0164       // CCCC   StdSeq::clean_duplicates(ev.candidateTracks_);
0165       // CCCC }
0166     }
0167 
0168     job.switch_to_backward();
0169 
0170     // now do backwards fit... do we want to time this section?
0171     if (Config::backwardFit) {
0172       builder.backwardFitBH();
0173       ev.fitTracks_ = builder.ref_tracks();
0174     }
0175 
0176     if (Config::quality_val) {
0177       StdSeq::Quality qval;
0178       qval.quality_val(&ev);
0179     } else if (Config::sim_val || Config::cmssw_val) {
0180       StdSeq::root_val(&ev);
0181     }
0182 
0183     builder.end_event();
0184 
0185     // ev.print_tracks(ev.candidateTracks_, true);
0186 
0187     if (validation_on) {
0188       ev.seedTracks_.swap(seeds1);
0189     }
0190 
0191     return time;
0192   }
0193 
0194   //==============================================================================
0195   // runBuildTestPlex Combinatorial: Standard
0196   //==============================================================================
0197 
0198   double runBuildingTestPlexStandard(Event &ev, const EventOfHits &eoh, MkBuilder &builder) {
0199     const IterationConfig &itconf = Config::ItrInfo[0];
0200 
0201     const bool validation_on = (Config::sim_val || Config::quality_val);
0202 
0203     TrackVec seeds1;
0204     if (validation_on) {
0205       unsigned int algorithms[] = {4};  //only initialStep
0206 
0207       for (auto const &s : ev.seedTracks_) {
0208         //keep seeds form the first iteration for processing
0209         if (std::find(algorithms, algorithms + 1, s.algoint()) != algorithms + 1)
0210           seeds1.push_back(s);
0211       }
0212       ev.seedTracks_.swap(seeds1);  //necessary for the validation - PrepareSeeds
0213       ev.relabel_bad_seedtracks();  //necessary for the validation - PrepareSeeds
0214     }
0215 
0216     IterationMaskIfc mask_ifc;
0217 
0218     // To disable hit-masks, pass nullptr in place of &mask_ifc to MkJob ctor
0219     // and optionally comment out ev.fill_hitmask_bool_vectors() call.
0220 
0221     ev.fill_hitmask_bool_vectors(itconf.m_track_algorithm, mask_ifc.m_mask_vector);
0222 
0223     MkJob job({Config::TrkInfo, itconf, eoh, eoh.refBeamSpot(), &mask_ifc});
0224 
0225     builder.begin_event(&job, &ev, __func__);
0226 
0227     bool seeds_sorted = false;
0228     // CCCC builder.PrepareSeeds();
0229     ev.setCurrentSeedTracks(ev.seedTracks_);
0230 
0231     builder.find_tracks_load_seeds(ev.seedTracks_, seeds_sorted);
0232 
0233 #ifdef USE_VTUNE_PAUSE
0234     __SSC_MARK(0x111);  // use this to resume Intel SDE at the same point
0235     __itt_resume();
0236 #endif
0237 
0238     double time = dtime();
0239 
0240     builder.findTracksStandard();
0241 
0242     time = dtime() - time;
0243 
0244 #ifdef USE_VTUNE_PAUSE
0245     __itt_pause();
0246     __SSC_MARK(0x222);  // use this to pause Intel SDE at the same point
0247 #endif
0248 
0249     check_nan_n_silly_candidates(ev);
0250 
0251     // first store candidate tracks
0252     ev.candidateTracks_.clear();
0253     builder.export_best_comb_cands(ev.candidateTracks_);
0254 
0255     job.switch_to_backward();
0256 
0257     // now do backwards fit... do we want to time this section?
0258     if (Config::backwardFit) {
0259       // Using the TrackVec version until we home in on THE backward fit etc.
0260       // builder.backwardFit();
0261       builder.select_best_comb_cands();
0262       builder.backwardFitBH();
0263       ev.fitTracks_ = builder.ref_tracks();
0264 
0265       check_nan_n_silly_bkfit(ev);
0266     }
0267 
0268     // CCCC StdSeq::handle_duplicates(&ev);
0269 
0270     if (Config::quality_val) {
0271       StdSeq::Quality qval;
0272       qval.quality_val(&ev);
0273     } else if (Config::sim_val || Config::cmssw_val) {
0274       StdSeq::root_val(&ev);
0275     }
0276 
0277     ev.resetCurrentSeedTracks();
0278 
0279     builder.end_event();
0280 
0281     // ev.print_tracks(ev.candidateTracks_, true);
0282 
0283     if (validation_on) {
0284       ev.seedTracks_.swap(seeds1);
0285     }
0286 
0287     return time;
0288   }
0289 
0290   //==============================================================================
0291   // runBuildTestPlex Combinatorial: CloneEngine
0292   //==============================================================================
0293 
0294   double runBuildingTestPlexCloneEngine(Event &ev, const EventOfHits &eoh, MkBuilder &builder) {
0295     const IterationConfig &itconf = Config::ItrInfo[0];
0296 
0297     const bool validation_on = (Config::sim_val || Config::quality_val);
0298 
0299     TrackVec seeds1;
0300     if (validation_on) {
0301       unsigned int algorithms[] = {4};  //only initialStep
0302 
0303       for (auto const &s : ev.seedTracks_) {
0304         //keep seeds form the first iteration for processing
0305         if (std::find(algorithms, algorithms + 1, s.algoint()) != algorithms + 1)
0306           seeds1.push_back(s);
0307       }
0308       ev.seedTracks_.swap(seeds1);  //necessary for the validation - PrepareSeeds
0309       ev.relabel_bad_seedtracks();  //necessary for the validation - PrepareSeeds
0310     }
0311 
0312     IterationMaskIfc mask_ifc;
0313 
0314     // To disable hit-masks, pass nullptr in place of &mask_ifc to MkJob ctor
0315     // and optionally comment out ev.fill_hitmask_bool_vectors() call.
0316 
0317     ev.fill_hitmask_bool_vectors(itconf.m_track_algorithm, mask_ifc.m_mask_vector);
0318 
0319     MkJob job({Config::TrkInfo, itconf, eoh, eoh.refBeamSpot(), &mask_ifc});
0320 
0321     builder.begin_event(&job, &ev, __func__);
0322 
0323     bool seeds_sorted = false;
0324     // CCCC builder.PrepareSeeds();
0325     ev.setCurrentSeedTracks(ev.seedTracks_);
0326 
0327     builder.find_tracks_load_seeds(ev.seedTracks_, seeds_sorted);
0328 
0329 #ifdef USE_VTUNE_PAUSE
0330     __SSC_MARK(0x111);  // use this to resume Intel SDE at the same point
0331     __itt_resume();
0332 #endif
0333 
0334     double time = dtime();
0335 
0336     builder.findTracksCloneEngine();
0337 
0338     time = dtime() - time;
0339 
0340 #ifdef USE_VTUNE_PAUSE
0341     __itt_pause();
0342     __SSC_MARK(0x222);  // use this to pause Intel SDE at the same point
0343 #endif
0344 
0345     check_nan_n_silly_candidates(ev);
0346 
0347     // first store candidate tracks - needed for BH backward fit and root_validation
0348     ev.candidateTracks_.clear();
0349     builder.export_best_comb_cands(ev.candidateTracks_);
0350 
0351     job.switch_to_backward();
0352 
0353     // now do backwards fit... do we want to time this section?
0354     if (Config::backwardFit) {
0355       // a) TrackVec version:
0356       builder.select_best_comb_cands();
0357       builder.backwardFitBH();
0358       ev.fitTracks_ = builder.ref_tracks();
0359 
0360       // b) Version that runs on CombCand / TrackCand
0361       // builder.backwardFit();
0362       // builder.quality_store_tracks(ev.fitTracks_);
0363 
0364       check_nan_n_silly_bkfit(ev);
0365     }
0366 
0367     // CCCC StdSeq::handle_duplicates(&ev);
0368 
0369     // validation section
0370     if (Config::quality_val) {
0371       StdSeq::Quality qval;
0372       qval.quality_val(&ev);
0373     } else if (Config::sim_val || Config::cmssw_val) {
0374       StdSeq::root_val(&ev);
0375     }
0376 
0377     ev.resetCurrentSeedTracks();
0378 
0379     builder.end_event();
0380 
0381     // ev.print_tracks(ev.candidateTracks_, true);
0382 
0383     if (validation_on) {
0384       ev.seedTracks_.swap(seeds1);
0385     }
0386 
0387     return time;
0388   }
0389 
0390   //==============================================================================
0391   // runBtpCe_MultiIter
0392   //
0393   // Prototype for running multiple iterations, sequentially, using the same builder.
0394   // For cmmsw seeds
0395   //
0396   // There is, in general, a mess in how tracks are processed, marked, or copied out
0397   // in various validation scenarios and export flags.
0398   //
0399   // In particular, MkBuilder::PrepareSeeds does a lot of things to whole / complete
0400   // event,seedTracks_ -- probably this would need to be split into common / and
0401   // per-iteration part.
0402   // - MkBuilder::prep_*** functions also mostly do not belong there (prep_sim is called from
0403   //   PrepareSeeds() for cmssw seeds).
0404   //
0405   // At this point we need to think about what should happen to Event before all the iterations and
0406   // after all the iterations ... from the Validation perspective.
0407   // And if we care about doing too muich work for seeds that will never get processed.
0408   //==============================================================================
0409 
0410   namespace {
0411     constexpr unsigned int algorithms[] = {4, 22, 23, 5, 24, 7, 8, 9, 10, 6};  //9 iterations
0412   }
0413 
0414   std::vector<double> runBtpCe_MultiIter(Event &ev, const EventOfHits &eoh, MkBuilder &builder, int n) {
0415     std::vector<double> timevec;
0416     if (n <= 0)
0417       return timevec;
0418     timevec.resize(n + 1, 0.0);
0419 
0420     const bool validation_on = (Config::sim_val || Config::quality_val);
0421 
0422     TrackVec seeds_used;
0423     TrackVec seeds1;
0424 
0425     if (validation_on) {
0426       for (auto const &s : ev.seedTracks_) {
0427         //keep seeds form the first n iterations for processing
0428         if (std::find(algorithms, algorithms + n, s.algoint()) != algorithms + n)
0429           seeds1.push_back(s);
0430       }
0431       ev.seedTracks_.swap(seeds1);  //necessary for the validation - PrepareSeeds
0432       ev.relabel_bad_seedtracks();  //necessary for the validation - PrepareSeeds
0433     }
0434 
0435     IterationMaskIfc mask_ifc;
0436     TrackVec seeds;
0437     TrackVec tmp_tvec;
0438 
0439     for (int it = 0; it <= n - 1; ++it) {
0440       const IterationConfig &itconf = Config::ItrInfo[it];
0441 
0442       // To disable hit-masks, pass nullptr in place of &mask_ifc to MkJob ctor
0443       // and optionally comment out ev.fill_hitmask_bool_vectors() call.
0444 
0445       ev.fill_hitmask_bool_vectors(itconf.m_track_algorithm, mask_ifc.m_mask_vector);
0446 
0447       MkJob job({Config::TrkInfo, itconf, eoh, eoh.refBeamSpot(), &mask_ifc});
0448 
0449       builder.begin_event(&job, &ev, __func__);
0450 
0451       {  // We could partition seeds once, store beg, end for each iteration in a map or vector.
0452         seeds.clear();
0453         int nc = 0;
0454         for (auto &s : ev.seedTracks_) {
0455           if (s.algoint() == itconf.m_track_algorithm) {
0456             if (itconf.m_requires_seed_hit_sorting) {
0457               s.sortHitsByLayer();
0458             }
0459             seeds.push_back(s);
0460             ++nc;
0461           } else if (nc > 0)
0462             break;
0463         }
0464       }
0465 
0466       bool do_seed_clean = bool(itconf.m_seed_cleaner);
0467 
0468       if (do_seed_clean)
0469         itconf.m_seed_cleaner(seeds, itconf, eoh.refBeamSpot());
0470 
0471       builder.seed_post_cleaning(seeds);
0472 
0473       // Add protection in case no seeds are found for iteration
0474       if (seeds.size() <= 0)
0475         continue;
0476       ev.setCurrentSeedTracks(seeds);
0477 
0478       builder.find_tracks_load_seeds(seeds, do_seed_clean);
0479 
0480       double time = dtime();
0481 
0482       builder.findTracksCloneEngine();
0483 
0484       timevec[it] = dtime() - time;
0485       timevec[n] += timevec[it];
0486 
0487       // Print min and max size of hots vectors of CombCands.
0488       // builder.find_min_max_hots_size();
0489 
0490       if (validation_on)
0491         seeds_used.insert(seeds_used.end(), seeds.begin(), seeds.end());  //cleaned seeds need to be stored somehow
0492 
0493       // Pre backward-fit filtering.
0494       // Note -- slightly different logic than run_OneIteration as we always do nan filters for
0495       // export for validation.
0496       filter_candidates_func pre_filter;
0497       if (itconf.m_pre_bkfit_filter)
0498         pre_filter = [&](const TrackCand &tc, const MkJob &jb) -> bool {
0499           return itconf.m_pre_bkfit_filter(tc, jb) && StdSeq::qfilter_nan_n_silly<TrackCand>(tc, jb);
0500         };
0501       else
0502         pre_filter = StdSeq::qfilter_nan_n_silly<TrackCand>;
0503       // pre_filter is always at least doing nan_n_silly filter.
0504       builder.filter_comb_cands(pre_filter, true);
0505 
0506       builder.select_best_comb_cands();
0507 
0508       {
0509         builder.export_tracks(tmp_tvec);
0510         if (itconf.m_duplicate_cleaner)
0511           itconf.m_duplicate_cleaner(builder.ref_tracks_nc(), itconf);
0512         ev.candidateTracks_.reserve(ev.candidateTracks_.size() + tmp_tvec.size());
0513         for (auto &&t : tmp_tvec)
0514           ev.candidateTracks_.emplace_back(std::move(t));
0515         tmp_tvec.clear();
0516       }
0517 
0518       job.switch_to_backward();
0519 
0520       // now do backwards fit... do we want to time this section?
0521       if (Config::backwardFit) {
0522         // a) TrackVec version:
0523         // builder.backwardFitBH();
0524 
0525         // b) Version that runs on CombCand / TrackCand
0526         const bool do_backward_search = Config::backwardSearch && itconf.m_backward_search;
0527 
0528         // We copy seed-hits into Candidates ... now we have to remove them so backward fit stops
0529         // before reaching seeding region. Ideally, we wouldn't add them in the first place but
0530         // if we want to export full tracks above we need to hold on to them (alternatively, we could
0531         // have a pointer to seed track in CombCandidate and copy them from there).
0532         if (do_backward_search)
0533           builder.compactifyHitStorageForBestCand(itconf.m_backward_drop_seed_hits, itconf.m_backward_fit_min_hits);
0534 
0535         builder.backwardFit();
0536 
0537         if (do_backward_search) {
0538           builder.beginBkwSearch();
0539           builder.findTracksCloneEngine(SteeringParams::IT_BkwSearch);
0540         }
0541 
0542         // Post backward-fit filtering.
0543         // Note -- slightly different logic than run_OneIteration as we export both pre and post
0544         // backward-fit tracks.
0545         filter_candidates_func post_filter;
0546         if (itconf.m_post_bkfit_filter)
0547           post_filter = [&](const TrackCand &tc, const MkJob &jb) -> bool {
0548             return itconf.m_post_bkfit_filter(tc, jb) && StdSeq::qfilter_nan_n_silly<TrackCand>(tc, jb);
0549           };
0550         else
0551           post_filter = StdSeq::qfilter_nan_n_silly<TrackCand>;
0552         // post_filter is always at least doing nan_n_silly filter.
0553         builder.filter_comb_cands(post_filter, true);
0554 
0555         if (do_backward_search)
0556           builder.endBkwSearch();
0557 
0558         builder.select_best_comb_cands(true);  // true -> clear m_tracks as they were already filled once above
0559 
0560         if (itconf.m_duplicate_cleaner)
0561           itconf.m_duplicate_cleaner(builder.ref_tracks_nc(), itconf);
0562 
0563         builder.export_tracks(ev.fitTracks_);
0564       }
0565       ev.resetCurrentSeedTracks();
0566 
0567       builder.end_event();
0568     }
0569 
0570     // MIMI - Fake back event pointer for final processing (that should be done elsewhere)
0571     MkJob job({Config::TrkInfo, Config::ItrInfo[0], eoh, eoh.refBeamSpot()});
0572     builder.begin_event(&job, &ev, __func__);
0573 
0574     if (validation_on) {
0575       StdSeq::prep_simtracks(&ev);
0576       //swap for the cleaned seeds
0577       ev.seedTracks_.swap(seeds_used);
0578     }
0579 
0580     check_nan_n_silly_candidates(ev);
0581 
0582     if (Config::backwardFit)
0583       check_nan_n_silly_bkfit(ev);
0584 
0585     // validation section
0586     if (Config::quality_val) {
0587       StdSeq::Quality qval;
0588       qval.quality_val(&ev);
0589     } else if (Config::sim_val || Config::cmssw_val) {
0590       StdSeq::root_val(&ev);
0591     }
0592 
0593     // ev.print_tracks(ev.candidateTracks_, true);
0594 
0595     // MIMI Unfake.
0596     builder.end_event();
0597 
0598     // In CMSSW runOneIter we now release memory for comb-cands:
0599     builder.release_memory();
0600 
0601     return timevec;
0602   }
0603 
0604 }  // end namespace mkfit