Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-25 02:14:12

0001 #include "RecoTracker/MkFitCMS/standalone/Shell.h"
0002 
0003 #include "RecoTracker/MkFitCore/src/Debug.h"
0004 
0005 // #include "RecoTracker/MkFitCore/src/Matriplex/MatriplexCommon.h"
0006 
0007 #include "RecoTracker/MkFitCMS/interface/runFunctions.h"
0008 
0009 #include "RecoTracker/MkFitCore/interface/HitStructures.h"
0010 #include "RecoTracker/MkFitCore/interface/MkBuilder.h"
0011 #include "RecoTracker/MkFitCore/src/MkFitter.h"
0012 #include "RecoTracker/MkFitCMS/interface/MkStdSeqs.h"
0013 #include "RecoTracker/MkFitCMS/standalone/MkStandaloneSeqs.h"
0014 
0015 #include "RecoTracker/MkFitCore/interface/Config.h"
0016 #include "RecoTracker/MkFitCore/standalone/ConfigStandalone.h"
0017 
0018 #include "RecoTracker/MkFitCore/standalone/Event.h"
0019 
0020 #ifndef NO_ROOT
0021 #include "TROOT.h"
0022 #include "TRint.h"
0023 #endif
0024 
0025 #include "oneapi/tbb/task_arena.h"
0026 
0027 #include <vector>
0028 
0029 // clang-format off
0030 
0031 namespace {
0032   constexpr int algos[] = {4, 22, 23, 5, 24, 7, 8, 9, 10, 6};  // 10 iterations
0033   constexpr int n_algos = sizeof(algos) / sizeof(int);
0034 
0035   const char* b2a(bool b) { return b ? "true" : "false"; }
0036 }
0037 
0038 namespace mkfit {
0039 
0040   Shell::Shell(std::vector<DeadVec> &dv, const std::string &in_file, int start_ev)
0041     : m_deadvectors(dv)
0042   {
0043     m_eoh = new EventOfHits(Config::TrkInfo);
0044     m_builder = new MkBuilder(Config::silent);
0045 
0046     m_backward_fit = Config::backwardFit;
0047 
0048     m_data_file = new DataFile;
0049     m_evs_in_file = m_data_file->openRead(in_file, Config::TrkInfo.n_layers());
0050 
0051     m_event = new Event(0, Config::TrkInfo.n_layers());
0052     GoToEvent(start_ev);
0053   }
0054 
0055   void Shell::Run() {
0056 #ifndef NO_ROOT
0057     std::vector<const char *> argv = { "mkFit", "-l" };
0058     int argc = argv.size();
0059     TRint rint("mkFit-shell", &argc, const_cast<char**>(argv.data()));
0060 
0061     char buf[256];
0062     sprintf(buf, "mkfit::Shell &s = * (mkfit::Shell*) %p;", this);
0063     gROOT->ProcessLine(buf);
0064     printf("Shell &s variable is set\n");
0065 
0066     rint.Run(true);
0067     printf("Shell::Run finished\n");
0068 #else
0069     printf("Shell::Run() no root, we rot -- erroring out. Recompile with WITH_ROOT=1\n");
0070     exit(1);
0071 #endif
0072   }
0073 
0074   void Shell::Status() {
0075     printf("On event %d, selected iteration index %d, algo %d - %s\n"
0076           "  debug = %s, use_dead_modules = %s\n"
0077            "  clean_seeds = %s, backward_fit = %s, remove_duplicates = %s\n",
0078            m_event->evtID(), m_it_index, algos[m_it_index], TrackBase::algoint_to_cstr(algos[m_it_index]),
0079            b2a(g_debug), b2a(Config::useDeadModules),
0080            b2a(m_clean_seeds), b2a(m_backward_fit), b2a(m_remove_duplicates));
0081   }
0082 
0083   //===========================================================================
0084   // Event navigation / processing
0085   //===========================================================================
0086 
0087   void Shell::GoToEvent(int eid) {
0088     if (eid < 1) {
0089       fprintf(stderr, "Requested event %d is less than 1 -- 1 is the first event, %d is total number of events in file\n",
0090              eid, m_evs_in_file);
0091       throw std::runtime_error("event out of range");
0092     }
0093     if (eid > m_evs_in_file) {
0094       fprintf(stderr, "Requested event %d is grater than total number of events in file %d\n",
0095              eid, m_evs_in_file);
0096       throw std::runtime_error("event out of range");
0097     }
0098 
0099     int pos = m_event->evtID();
0100     if (eid > pos) {
0101       m_data_file->skipNEvents(eid - pos - 1);
0102     } else {
0103       m_data_file->rewind();
0104       m_data_file->skipNEvents(eid - 1);
0105     }
0106     m_event->resetCurrentSeedTracks(); // left after ProcessEvent() for debugging etc
0107     m_event->reset(eid);
0108     m_event->read_in(*m_data_file);
0109     StdSeq::loadHitsAndBeamSpot(*m_event, *m_eoh);
0110     if (Config::useDeadModules) {
0111       StdSeq::loadDeads(*m_eoh, m_deadvectors);
0112     }
0113 
0114     printf("At event %d\n", eid);
0115   }
0116 
0117   void Shell::NextEvent(int skip) {
0118     GoToEvent(m_event->evtID() + skip);
0119   }
0120 
0121   void Shell::ProcessEvent(SeedSelect_e seed_select, int selected_seed, int count) {
0122     // count is only used for SS_IndexPreCleaning and SS_IndexPostCleaning.
0123     //       There are no checks for upper bounds, ie, if requested seeds beyond the first one exist.
0124 
0125     const IterationConfig &itconf = Config::ItrInfo[m_it_index];
0126     IterationMaskIfc mask_ifc;
0127     m_event->fill_hitmask_bool_vectors(itconf.m_track_algorithm, mask_ifc.m_mask_vector);
0128 
0129     m_seeds.clear();
0130     m_tracks.clear();
0131 
0132     {
0133       int n_algo = 0; // seeds are grouped by algo
0134       for (auto &s : m_event->seedTracks_) {
0135         if (s.algoint() == itconf.m_track_algorithm) {
0136           if (seed_select == SS_UseAll || seed_select == SS_IndexPostCleaning) {
0137             m_seeds.push_back(s);
0138           } else if (seed_select == SS_Label && s.label() == selected_seed) {
0139             m_seeds.push_back(s);
0140             break;
0141           } else if (seed_select == SS_IndexPreCleaning && n_algo >= selected_seed) {
0142             m_seeds.push_back(s);
0143             if (--count <= 0)
0144               break;
0145           }
0146           ++n_algo;
0147         } else if (n_algo > 0)
0148           break;
0149       }
0150     }
0151 
0152     printf("Shell::ProcessEvent running over %d seeds\n", (int) m_seeds.size());
0153 
0154     // Equivalent to run_OneIteration(...) without MkBuilder::release_memory().
0155     // If seed_select == SS_IndexPostCleaning the given seed is picked after cleaning.
0156     {
0157       const TrackerInfo &trackerInfo = Config::TrkInfo;
0158       const EventOfHits &eoh = *m_eoh;
0159       const IterationMaskIfcBase &it_mask_ifc = mask_ifc;
0160       MkBuilder &builder = *m_builder;
0161       TrackVec &seeds = m_seeds;
0162       TrackVec &out_tracks = m_tracks;
0163       bool do_seed_clean = m_clean_seeds;
0164       bool do_backward_fit = m_backward_fit;
0165       bool do_remove_duplicates = m_remove_duplicates;
0166 
0167       MkJob job({trackerInfo, itconf, eoh, eoh.refBeamSpot(), &it_mask_ifc});
0168 
0169       builder.begin_event(&job, m_event, __func__);
0170 
0171       // Seed cleaning not done on all iterations.
0172       do_seed_clean = m_clean_seeds && itconf.m_seed_cleaner;
0173 
0174       if (do_seed_clean) {
0175         itconf.m_seed_cleaner(seeds, itconf, eoh.refBeamSpot());
0176         printf("Shell::ProcessEvent post seed-cleaning: %d seeds\n", (int) m_seeds.size());
0177       } else {
0178         printf("Shell::ProcessEvent no seed-cleaning\n");
0179       }
0180 
0181       // Check nans in seeds -- this should not be needed when Slava fixes
0182       // the track parameter coordinate transformation.
0183       builder.seed_post_cleaning(seeds);
0184 
0185       if (seed_select == SS_IndexPostCleaning) {
0186         int seed_size = (int) seeds.size();
0187         if (selected_seed >= 0 && selected_seed < seed_size) {
0188           if (selected_seed + count >= seed_size) {
0189             count = seed_size - selected_seed;
0190             printf("  -- selected seed_index + count > seed vector size after cleaning -- trimming count to %d\n",
0191                    count);
0192           }
0193           for (int i = 0; i < count; ++i)
0194             seeds[i] = seeds[selected_seed + i];
0195           seeds.resize(count);
0196         } else {
0197           seeds.clear();
0198         }
0199       }
0200 
0201       if (seeds.empty()) {
0202         if (seed_select != SS_UseAll)
0203           printf("Shell::ProcessEvent requested seed not found among seeds of the selected iteration.\n");
0204         else
0205           printf("Shell::ProcessEvent no seeds found.\n");
0206         return;
0207       }
0208 
0209       if (itconf.m_requires_seed_hit_sorting) {
0210         for (auto &s : seeds)
0211           s.sortHitsByLayer();  // sort seed hits for the matched hits (I hope it works here)
0212       }
0213 
0214       m_event->setCurrentSeedTracks(seeds);
0215 
0216       builder.find_tracks_load_seeds(seeds, do_seed_clean);
0217 
0218       builder.findTracksCloneEngine();
0219 
0220       printf("Shell::ProcessEvent post fwd search: %d comb-cands\n", builder.ref_eocc().size());
0221 
0222       // Pre backward-fit filtering.
0223       filter_candidates_func pre_filter;
0224       if (do_backward_fit && itconf.m_pre_bkfit_filter)
0225         pre_filter = [&](const TrackCand &tc, const MkJob &jb) -> bool {
0226           return itconf.m_pre_bkfit_filter(tc, jb) && StdSeq::qfilter_nan_n_silly<TrackCand>(tc, jb);
0227         };
0228       else if (itconf.m_pre_bkfit_filter)
0229         pre_filter = itconf.m_pre_bkfit_filter;
0230       else if (do_backward_fit)
0231         pre_filter = StdSeq::qfilter_nan_n_silly<TrackCand>;
0232       // pre_filter can be null if we are not doing backward fit as nan_n_silly will be run below.
0233       if (pre_filter)
0234         builder.filter_comb_cands(pre_filter, true);
0235 
0236       printf("Shell::ProcessEvent post pre-bkf-filter (%s) and nan-filter (%s) filter: %d comb-cands\n",
0237              b2a(bool(itconf.m_pre_bkfit_filter)), b2a(do_backward_fit), builder.ref_eocc().size());
0238 
0239       job.switch_to_backward();
0240 
0241       if (do_backward_fit) {
0242         if (itconf.m_backward_search) {
0243           builder.compactifyHitStorageForBestCand(itconf.m_backward_drop_seed_hits, itconf.m_backward_fit_min_hits);
0244         }
0245 
0246         builder.backwardFit();
0247 
0248         if (itconf.m_backward_search) {
0249           builder.beginBkwSearch();
0250           builder.findTracksCloneEngine(SteeringParams::IT_BkwSearch);
0251         }
0252 
0253         printf("Shell::ProcessEvent post backward fit / search: %d comb-cands\n", builder.ref_eocc().size());
0254       }
0255 
0256       // Post backward-fit filtering.
0257       filter_candidates_func post_filter;
0258       if (do_backward_fit && itconf.m_post_bkfit_filter)
0259         post_filter = [&](const TrackCand &tc, const MkJob &jb) -> bool {
0260           return itconf.m_post_bkfit_filter(tc, jb) && StdSeq::qfilter_nan_n_silly<TrackCand>(tc, jb);
0261         };
0262       else
0263         post_filter = StdSeq::qfilter_nan_n_silly<TrackCand>;
0264       // post_filter is always at least doing nan_n_silly filter.
0265       builder.filter_comb_cands(post_filter, true);
0266 
0267       printf("Shell::ProcessEvent post post-bkf-filter (%s) and nan-filter (true): %d comb-cands\n",
0268              b2a(do_backward_fit && itconf.m_post_bkfit_filter), builder.ref_eocc().size());
0269 
0270       if (do_backward_fit && itconf.m_backward_search)
0271         builder.endBkwSearch();
0272 
0273       builder.export_best_comb_cands(out_tracks, true);
0274 
0275       if (do_remove_duplicates && itconf.m_duplicate_cleaner) {
0276         itconf.m_duplicate_cleaner(out_tracks, itconf);
0277       }
0278 
0279       printf("Shell::ProcessEvent post remove-duplicates: %d comb-cands\n", (int) out_tracks.size());
0280 
0281       // Do not clear ... useful for debugging / printouts!
0282       // m_event->resetCurrentSeedTracks();
0283 
0284       builder.end_event();
0285     }
0286 
0287     printf("Shell::ProcessEvent found %d tracks, number of seeds at end %d\n",
0288            (int) m_tracks.size(), (int) m_seeds.size());
0289   }
0290 
0291   //===========================================================================
0292   // Iteration selection
0293   //===========================================================================
0294 
0295   void Shell::SelectIterationIndex(int itidx) {
0296     if (itidx < 0 || itidx >= n_algos) {
0297       fprintf(stderr, "Requested iteration index out of range [%d, %d)", 0, n_algos);
0298       throw std::runtime_error("iteration index out of range");
0299     }
0300     m_it_index = itidx;
0301   }
0302 
0303   void Shell::SelectIterationAlgo(int algo) {
0304     for (int i = 0; i < n_algos; ++i) {
0305       if (algos[i] == algo) {
0306         m_it_index = i;
0307         return;
0308       }
0309     }
0310     fprintf(stderr, "Requested algo %d not found", algo);
0311     throw std::runtime_error("algo not found");
0312   }
0313 
0314   void Shell::PrintIterations() {
0315     printf("Shell::PrintIterations selected index = %d, %d iterations hardcoded as\n",
0316             m_it_index, n_algos);
0317     for (int i = 0; i < n_algos; ++i)
0318       printf("%d %2d %s\n", i, algos[i], TrackBase::algoint_to_cstr(algos[i]));
0319   }
0320 
0321   //===========================================================================
0322   // Flags / status setters
0323   //===========================================================================
0324 
0325   void Shell::SetDebug(bool b) { g_debug = b; }
0326   void Shell::SetCleanSeeds(bool b) { m_clean_seeds = b; }
0327   void Shell::SetBackwardFit(bool b) { m_backward_fit = b; }
0328   void Shell::SetRemoveDuplicates(bool b) { m_remove_duplicates = b; }
0329   void Shell::SetUseDeadModules(bool b) { Config::useDeadModules = b; }
0330 
0331   //===========================================================================
0332   // Analysis helpers
0333   //===========================================================================
0334 
0335   /*
0336     sim tracks are written to .bin files with a label equal to its own index.
0337     reco tracks labels are seed indices.
0338     seed labels are sim track indices
0339     --
0340     mkfit labels are seed indices in given iteration after cleaning (at seed load-time).
0341           This is no longer true -- was done like that in branch where this code originated from.
0342           It seems the label is the same as seed label.
0343   */
0344 
0345   int Shell::LabelFromHits(Track &t, bool replace, float good_frac) {
0346     std::map<int, int> lab_cnt;
0347     for (int hi = 0; hi < t.nTotalHits(); ++hi) {
0348       auto hot = t.getHitOnTrack(hi);
0349       if (hot.index < 0)
0350         continue;
0351       const Hit &h = m_event->layerHits_[hot.layer][hot.index];
0352       int hl = m_event->simHitsInfo_[h.mcHitID()].mcTrackID_;
0353       if (hl >= 0)
0354         ++lab_cnt[hl];
0355     }
0356     int max_c = -1, max_l = -1;
0357     for (auto& x : lab_cnt) {
0358       if (x.second > max_c) {
0359         max_l = x.first;
0360         max_c = x.second;
0361       }
0362     }
0363     bool success = max_c >= good_frac * t.nFoundHits();
0364     int relabel = success ? max_l : -1;
0365     // printf("found_hits=%d, best_lab %d (%d hits), existing label=%d (replace flag=%s)\n",
0366     //        t.nFoundHits(), max_l, max_c, t.label(), b2a(replace));
0367     if (replace)
0368         t.setLabel(relabel);
0369     return relabel;
0370   }
0371 
0372   void Shell::FillByLabelMaps_CkfBase() {
0373     Event &ev = *m_event;
0374     const int track_algo = Config::ItrInfo[m_it_index].m_track_algorithm;
0375 
0376     m_ckf_map.clear();
0377     m_sim_map.clear();
0378     m_seed_map.clear();
0379     m_mkf_map.clear();
0380 
0381     // Pick ckf tracks with right algo and a good label.
0382     int rec_algo_match = 0;
0383     for (auto &t : ev.cmsswTracks_) {
0384       if (t.algoint() != track_algo)
0385         continue;
0386       ++rec_algo_match;
0387       int label = LabelFromHits(t, false, 0.5);
0388       if (label >= 0) {
0389         m_ckf_map.insert(std::make_pair(label, &t));
0390       }
0391     }
0392 
0393     // Pick sim tracks with labels found by ckf.
0394     for (auto &t : ev.simTracks_) {
0395       if (t.label() >= 0 && m_ckf_map.find(t.label()) != m_ckf_map.end()) {
0396         m_sim_map.insert(std::make_pair(t.label(), &t));
0397       }
0398     }
0399 
0400     // Pick seeds with right algo and a label found by ckf.
0401     for (auto &t : ev.seedTracks_) {
0402       if (t.algoint() == track_algo && t.label() >= 0 && m_ckf_map.find(t.label()) != m_ckf_map.end()) {
0403         m_seed_map.insert(std::make_pair(t.label(), &t));
0404       }
0405     }
0406     // Some seeds seem to be labeled -1, try fixing when not otherwise found.
0407     for (auto &t : ev.seedTracks_) {
0408       if (t.algoint() == track_algo && t.label() == -1) {
0409         int lab = LabelFromHits(t, true, 0.5);
0410         if (lab >= 0 && m_seed_map.find(lab) == m_seed_map.end()) {
0411           if (m_ckf_map.find(lab) != m_ckf_map.end()) {
0412             m_seed_map.insert(std::make_pair(t.label(), &t));
0413             printf("Saved seed with label -1 -> %d\n", lab);
0414           }
0415         }
0416       }
0417     }
0418 
0419     // Pick mkfit tracks, label by 
0420     for (auto &t : m_tracks) {
0421       int label = LabelFromHits(t, false, 0.5);
0422       if (label >= 0) {
0423         m_mkf_map.insert(std::make_pair(label, &t));
0424       }
0425     }
0426 
0427     printf("Shell::FillByLabelMaps reporting tracks with label >= 0, algo=%d (%s): "
0428            "ckf: %d of %d (same algo=%d)), sim: %d of %d, seed: %d of %d, mkfit: %d w/label of %d\n",
0429            track_algo, TrackBase::algoint_to_cstr(track_algo),
0430            (int) m_ckf_map.size(), (int) ev.cmsswTracks_.size(), rec_algo_match,
0431            (int) m_sim_map.size(), (int) ev.simTracks_.size(),
0432            (int) m_seed_map.size(), (int) m_seeds.size(),
0433            (int) m_mkf_map.size(), (int) m_tracks.size()
0434     );
0435   }
0436 
0437   bool Shell::CheckMkFitLayerPlanVsReferenceHits(const Track &mkft, const Track &reft, const std::string &name) {
0438     // Check if all hit-layers of a reference track reft are in the mkfit layer plan.
0439     // Returns true if all layers are in the plan.
0440     // String name is printed in front of label, expected to be SIMK or CKF.
0441     const IterationConfig &itconf = Config::ItrInfo[m_it_index];
0442     auto lp = itconf.m_steering_params[ mkft.getEtaRegion() ].m_layer_plan;
0443     bool ret = true;
0444     for (int hi = 0; hi < reft.nTotalHits(); ++hi) {
0445       auto hot = reft.getHitOnTrack(hi);
0446       if (std::find_if(lp.begin(), lp.end(), [=](auto &x){ return x.m_layer == hot.layer; }) == lp.end())
0447       {
0448         printf("CheckMkfLayerPlanVsCkfHits: layer %d not in layer plan for region %d, %s label=%d\n",
0449                 hot.layer, mkft.getEtaRegion(), name.c_str(), reft.label());
0450         ret = false;
0451       }
0452     }
0453     return ret;
0454   }
0455 
0456   //===========================================================================
0457   // Analysis drivers / main functions / Comparators
0458   //===========================================================================
0459 
0460   void Shell::Compare() {
0461     Event &ev = *m_event;
0462     const IterationConfig &itconf = Config::ItrInfo[m_it_index];
0463 
0464     FillByLabelMaps_CkfBase();
0465 
0466     printf("------------------------------------------------------\n");
0467 
0468     const bool print_all_def = false;
0469     int mkf_cnt=0, less_hits=0, more_hits=0;
0470 
0471     // TOBTEC: look for rec-seeds with hits in tob1 and 2 only.
0472     int tot_cnt = 0, no_mkf_cnt = 0;
0473 
0474     for (auto& [l, simt_ptr]: m_sim_map)
0475     {
0476       auto &simt = * simt_ptr;
0477       auto &ckft = * m_ckf_map[l];
0478       auto mi = m_mkf_map.find(l);
0479 
0480       bool print_all = print_all_def;
0481 
0482       // TOBTEC: look for rec-seeds with hits in tob1 and 2 only.
0483       bool select = true;
0484       {
0485         auto &ckf_seed = ev.seedTracks_[ckft.label()];
0486         for (int hi = 0; hi < ckf_seed.nTotalHits(); ++hi) {
0487           const HitOnTrack hot = ckf_seed.getHitOnTrack(hi);
0488           if (hot.index >= 0 && (hot.layer < 10 || hot.layer > 13)) {
0489             select = false;
0490             break;
0491           }
0492         }
0493       }
0494       if ( ! select) continue;
0495 
0496       ++tot_cnt;
0497       //print_all = true;
0498 
0499       if (mi != m_mkf_map.end())
0500       {
0501         auto &mkft = * mi->second;
0502         mkf_cnt++;
0503         if (mkft.nFoundHits() < ckft.nFoundHits()) ++less_hits;
0504         if (mkft.nFoundHits() > ckft.nFoundHits()) ++more_hits;
0505 
0506         CheckMkFitLayerPlanVsReferenceHits(mkft, ckft, "CKF");
0507         // CheckMkFitLayerPlanVsReferenceHits(mkft, simt, "SIM");
0508 
0509         (void) print_all;
0510         if (/* itconf.m_track_algorithm == 10 ||*/ print_all) {
0511           // ckf label is wrong when validation is on (even quality val) for mixedTriplet, pixelless and tobtec
0512           // as seed tracks get removed for non-mkfit iterations and indices from rec-tracks are no longer valid.
0513           auto &ckf_seed = ev.seedTracks_[ckft.label()];
0514           auto &mkf_seed = m_seeds[mkft.label()];
0515           print("ckf  ", 0, ckft, ev);
0516           print("mkfit", 0, mkft, ev);
0517           print("sim  ", 0, simt, ev);
0518           print("ckf seed", 0, ckf_seed, ev);
0519           print("mkf seed", 0, mkf_seed, ev);
0520           printf("------------------------------------------------------\n");
0521 
0522           TrackVec ssss;
0523           ssss.push_back(mkf_seed);
0524 
0525           IterationSeedPartition pppp(1);
0526           IterationConfig::get_seed_partitioner("phase1:1:debug")(Config::TrkInfo, ssss, *m_eoh, pppp);
0527 
0528           printf("------------------------------------------------------\n");
0529           printf("\n");
0530         }
0531       }
0532       else
0533       {
0534         printf("\n!!!!! No mkfit track with this label.\n\n");
0535         ++no_mkf_cnt;
0536 
0537         auto &ckf_seed = ev.seedTracks_[ckft.label()];
0538         print("ckf ", 0, ckft, ev);
0539         print("sim ", 0, simt, ev);
0540         print("ckf seed", 0, ckf_seed, ev);
0541         auto smi = m_seed_map.find(l);
0542         if (smi != m_seed_map.end())
0543           print("seed with matching label", 0, *smi->second, ev);
0544         printf("------------------------------------------------------\n");
0545       }
0546     }
0547 
0548     printf("mkFit found %d, matching_label=%d, less_hits=%d, more_hits=%d  [algo=%d (%s)]\n",
0549            (int) ev.fitTracks_.size(), mkf_cnt, less_hits, more_hits,
0550            itconf.m_track_algorithm, TrackBase::algoint_to_cstr(itconf.m_track_algorithm));
0551 
0552     if (tot_cnt > 0) {
0553       printf("\ntobtec tob1/2 tot=%d no_mkf=%d (%f%%)\n",
0554             tot_cnt, no_mkf_cnt, 100.0 * no_mkf_cnt / tot_cnt);
0555     } else {
0556       printf("\nNo CKF tracks with seed hits in TOB1/2 found (need iteration idx 8, TobTec?)\n");
0557     }
0558 
0559     printf("-------------------------------------------------------------------------------------------\n");
0560     printf("-------------------------------------------------------------------------------------------\n");
0561     printf("\n");
0562   }
0563 
0564 }