Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2023-02-07 02:58:07

0001 #include "RecoTracker/MkFitCore/interface/cms_common_macros.h"
0002 #include "RecoTracker/MkFitCore/interface/IterationConfig.h"
0003 #include "RecoTracker/MkFitCore/interface/Config.h"
0004 #include "RecoTracker/MkFitCore/interface/Track.h"
0005 
0006 //#define DEBUG
0007 #include "Debug.h"
0008 
0009 #include "nlohmann/json.hpp"
0010 
0011 #include <fstream>
0012 #include <mutex>
0013 #include <regex>
0014 #include <iostream>
0015 #include <iomanip>
0016 
0017 // Redefine to also support ordered_json ... we want to keep variable order in JSON save files.
0018 #define ITCONF_DEFINE_TYPE_NON_INTRUSIVE(Type, ...)                                             \
0019   inline void to_json(nlohmann::json &nlohmann_json_j, const Type &nlohmann_json_t) {           \
0020     NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__))                    \
0021   }                                                                                             \
0022   inline void from_json(const nlohmann::json &nlohmann_json_j, Type &nlohmann_json_t) {         \
0023     NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__))                  \
0024   }                                                                                             \
0025   inline void to_json(nlohmann::ordered_json &nlohmann_json_j, const Type &nlohmann_json_t) {   \
0026     NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__))                    \
0027   }                                                                                             \
0028   inline void from_json(const nlohmann::ordered_json &nlohmann_json_j, Type &nlohmann_json_t) { \
0029     NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__))                  \
0030   }
0031 
0032 namespace mkfit {
0033 
0034   // Begin AUTO code, some members commented out.
0035 
0036   ITCONF_DEFINE_TYPE_NON_INTRUSIVE(mkfit::LayerControl,
0037                                    /* int   */ m_layer)
0038 
0039   ITCONF_DEFINE_TYPE_NON_INTRUSIVE(mkfit::SteeringParams,
0040                                    /* std::vector<LayerControl> */ m_layer_plan,
0041                                    /* std::string */ m_track_scorer_name,
0042                                    /* int */ m_region,
0043                                    /* int */ m_fwd_search_pickup,
0044                                    /* int */ m_bkw_fit_last,
0045                                    /* int */ m_bkw_search_pickup)
0046 
0047   ITCONF_DEFINE_TYPE_NON_INTRUSIVE(mkfit::IterationLayerConfig,
0048                                    /* int */ m_layer,
0049                                    /* float */ m_select_min_dphi,
0050                                    /* float */ m_select_max_dphi,
0051                                    /* float */ m_select_min_dq,
0052                                    /* float */ m_select_max_dq,
0053                                    /* std::vector<float> */ m_winpars_fwd,
0054                                    /* std::vector<float> */ m_winpars_bkw)
0055 
0056   ITCONF_DEFINE_TYPE_NON_INTRUSIVE(mkfit::IterationParams,
0057                                    /* int */ nlayers_per_seed,
0058                                    /* int */ maxCandsPerSeed,
0059                                    /* int */ maxHolesPerCand,
0060                                    /* int */ maxConsecHoles,
0061                                    /* float */ chi2Cut_min,
0062                                    /* float */ chi2CutOverlap,
0063                                    /* float */ pTCutOverlap,
0064                                    /* int */ minHitsQF,
0065                                    /* float */ minPtCut,
0066                                    /* unsigned int */ maxClusterSize)
0067 
0068   ITCONF_DEFINE_TYPE_NON_INTRUSIVE(mkfit::IterationConfig,
0069                                    /* int */ m_iteration_index,
0070                                    /* int */ m_track_algorithm,
0071                                    /* std::string */ m_seed_cleaner_name,
0072                                    /* std::string */ m_seed_partitioner_name,
0073                                    /* std::string */ m_pre_bkfit_filter_name,
0074                                    /* std::string */ m_post_bkfit_filter_name,
0075                                    /* std::string */ m_duplicate_cleaner_name,
0076                                    /* std::string */ m_default_track_scorer_name,
0077                                    /* bool */ m_requires_seed_hit_sorting,
0078                                    /* bool */ m_backward_search,
0079                                    /* bool */ m_backward_drop_seed_hits,
0080                                    /* int */ m_backward_fit_min_hits,
0081                                    /* float */ sc_ptthr_hpt,
0082                                    /* float */ sc_drmax_bh,
0083                                    /* float */ sc_dzmax_bh,
0084                                    /* float */ sc_drmax_eh,
0085                                    /* float */ sc_dzmax_eh,
0086                                    /* float */ sc_drmax_bl,
0087                                    /* float */ sc_dzmax_bl,
0088                                    /* float */ sc_drmax_el,
0089                                    /* float */ sc_dzmax_el,
0090                                    /* float */ dc_fracSharedHits,
0091                                    /* float */ dc_drth_central,
0092                                    /* float */ dc_drth_obarrel,
0093                                    /* float */ dc_drth_forward,
0094                                    /* mkfit::IterationParams */ m_params,
0095                                    /* mkfit::IterationParams */ m_backward_params,
0096                                    /* int */ m_n_regions,
0097                                    /* vector<int> */ m_region_order,
0098                                    /* vector<mkfit::SteeringParams> */ m_steering_params,
0099                                    /* vector<mkfit::IterationLayerConfig> */ m_layer_configs)
0100 
0101   ITCONF_DEFINE_TYPE_NON_INTRUSIVE(mkfit::IterationsInfo,
0102                                    /* vector<mkfit::IterationConfig> */ m_iterations)
0103 
0104   // End AUTO code.
0105 
0106   // Begin IterationConfig function catalogs
0107 
0108   namespace {
0109     struct FuncCatalog {
0110       std::map<std::string, clean_seeds_func> seed_cleaners;
0111       std::map<std::string, partition_seeds_func> seed_partitioners;
0112       std::map<std::string, filter_candidates_func> candidate_filters;
0113       std::map<std::string, clean_duplicates_func> duplicate_cleaners;
0114       std::map<std::string, track_score_func> track_scorers;
0115 
0116       std::mutex catalog_mutex;
0117     };
0118 
0119     FuncCatalog &get_catalog() {
0120       CMS_SA_ALLOW static FuncCatalog func_catalog;
0121       return func_catalog;
0122     }
0123   }  // namespace
0124 
0125 #define GET_FC              \
0126   auto &fc = get_catalog(); \
0127   const std::lock_guard<std::mutex> lock(fc.catalog_mutex)
0128 
0129   void IterationConfig::register_seed_cleaner(const std::string &name, clean_seeds_func func) {
0130     GET_FC;
0131     fc.seed_cleaners.insert({name, func});
0132   }
0133   void IterationConfig::register_seed_partitioner(const std::string &name, partition_seeds_func func) {
0134     GET_FC;
0135     fc.seed_partitioners.insert({name, func});
0136   }
0137   void IterationConfig::register_candidate_filter(const std::string &name, filter_candidates_func func) {
0138     GET_FC;
0139     fc.candidate_filters.insert({name, func});
0140   }
0141   void IterationConfig::register_duplicate_cleaner(const std::string &name, clean_duplicates_func func) {
0142     GET_FC;
0143     fc.duplicate_cleaners.insert({name, func});
0144   }
0145   void IterationConfig::register_track_scorer(const std::string &name, track_score_func func) {
0146     GET_FC;
0147     fc.track_scorers.insert({name, func});
0148   }
0149 
0150   namespace {
0151     template <class T>
0152     typename T::mapped_type resolve_func_name(const T &cont, const std::string &name, const char *func) {
0153       if (name.empty()) {
0154         return nullptr;
0155       }
0156       auto ii = cont.find(name);
0157       if (ii == cont.end()) {
0158         std::string es(func);
0159         es += " '" + name + "' not found in function registry.";
0160         throw std::runtime_error(es);
0161       }
0162       return ii->second;
0163     }
0164   }  // namespace
0165 
0166   clean_seeds_func IterationConfig::get_seed_cleaner(const std::string &name) {
0167     GET_FC;
0168     return resolve_func_name(fc.seed_cleaners, name, __func__);
0169   }
0170   partition_seeds_func IterationConfig::get_seed_partitioner(const std::string &name) {
0171     GET_FC;
0172     return resolve_func_name(fc.seed_partitioners, name, __func__);
0173   }
0174   filter_candidates_func IterationConfig::get_candidate_filter(const std::string &name) {
0175     GET_FC;
0176     return resolve_func_name(fc.candidate_filters, name, __func__);
0177   }
0178   clean_duplicates_func IterationConfig::get_duplicate_cleaner(const std::string &name) {
0179     GET_FC;
0180     return resolve_func_name(fc.duplicate_cleaners, name, __func__);
0181   }
0182   track_score_func IterationConfig::get_track_scorer(const std::string &name) {
0183     GET_FC;
0184     return resolve_func_name(fc.track_scorers, name, __func__);
0185   }
0186 
0187 #undef GET_FC
0188 
0189   // End IterationConfig function catalogs
0190 
0191   void IterationConfig::setupStandardFunctionsFromNames() {
0192     m_seed_cleaner = get_seed_cleaner(m_seed_cleaner_name);
0193     dprintf(" Set seed_cleaner for '%s' %s\n", m_seed_cleaner_name.c_str(), m_seed_cleaner ? "SET" : "NOT SET");
0194 
0195     m_seed_partitioner = get_seed_partitioner(m_seed_partitioner_name);
0196     dprintf(
0197         " Set seed_partitioner for '%s' %s\n", m_seed_partitioner_name.c_str(), m_seed_partitioner ? "SET" : "NOT SET");
0198 
0199     m_pre_bkfit_filter = get_candidate_filter(m_pre_bkfit_filter_name);
0200     dprintf(
0201         " Set pre_bkfit_filter for '%s' %s\n", m_pre_bkfit_filter_name.c_str(), m_pre_bkfit_filter ? "SET" : "NOT SET");
0202 
0203     m_post_bkfit_filter = get_candidate_filter(m_post_bkfit_filter_name);
0204     dprintf(" Set post_bkfit_filter for '%s' %s\n",
0205             m_post_bkfit_filter_name.c_str(),
0206             m_post_bkfit_filter ? "SET" : "NOT SET");
0207 
0208     m_duplicate_cleaner = get_duplicate_cleaner(m_duplicate_cleaner_name);
0209     dprintf(" Set duplicate_cleaner for '%s' %s\n",
0210             m_duplicate_cleaner_name.c_str(),
0211             m_duplicate_cleaner ? "SET" : "NOT SET");
0212 
0213     m_default_track_scorer = get_track_scorer(m_default_track_scorer_name);
0214     for (auto &sp : m_steering_params) {
0215       sp.m_track_scorer =
0216           sp.m_track_scorer_name.empty() ? m_default_track_scorer : get_track_scorer(sp.m_track_scorer_name);
0217     }
0218   }
0219 
0220   // ============================================================================
0221   // ConfigJsonPatcher
0222   // ============================================================================
0223 
0224   ConfigJsonPatcher::ConfigJsonPatcher(bool verbose) : m_verbose(verbose) {}
0225 
0226   ConfigJsonPatcher::~ConfigJsonPatcher() = default;
0227 
0228   std::string ConfigJsonPatcher::get_abs_path() const {
0229     std::string s;
0230     s.reserve(64);
0231     for (auto &p : m_path_stack)
0232       s += p;
0233     return s;
0234   }
0235 
0236   std::string ConfigJsonPatcher::exc_hdr(const char *func) const {
0237     std::string s;
0238     s.reserve(128);
0239     s = "ConfigJsonPatcher";
0240     if (func) {
0241       s += "::";
0242       s += func;
0243     }
0244     s += " '";
0245     s += get_abs_path();
0246     s += "' ";
0247     return s;
0248   }
0249 
0250   template <class T>
0251   void ConfigJsonPatcher::load(const T &o) {
0252     m_json = std::make_unique<nlohmann::json>();
0253     *m_json = o;
0254     cd_top();
0255   }
0256   template void ConfigJsonPatcher::load<IterationsInfo>(const IterationsInfo &o);
0257   template void ConfigJsonPatcher::load<IterationConfig>(const IterationConfig &o);
0258 
0259   template <class T>
0260   void ConfigJsonPatcher::save(T &o) {
0261     from_json(*m_json, o);
0262   }
0263   template void ConfigJsonPatcher::save<IterationConfig>(IterationConfig &o);
0264 
0265   // Must not bork the IterationConfig elements of IterationsInfo ... default
0266   // deserializator apparently reinitializes the vectors with defaults c-tors.
0267   template <>
0268   void ConfigJsonPatcher::save<IterationsInfo>(IterationsInfo &o) {
0269     auto &itc_arr = m_json->at("m_iterations");
0270     for (int i = 0; i < o.size(); ++i) {
0271       from_json(itc_arr[i], o[i]);
0272     }
0273   }
0274 
0275   void ConfigJsonPatcher::cd(const std::string &path) {
0276     nlohmann::json::json_pointer jp(path);
0277     m_json_stack.push_back(m_current);
0278     m_path_stack.push_back(path);
0279     m_current = &m_current->at(jp);
0280   }
0281 
0282   void ConfigJsonPatcher::cd_up(const std::string &path) {
0283     if (m_json_stack.empty())
0284       throw std::runtime_error("JSON stack empty on cd_up");
0285 
0286     m_current = m_json_stack.back();
0287     m_json_stack.pop_back();
0288     m_path_stack.pop_back();
0289     if (!path.empty())
0290       cd(path);
0291   }
0292 
0293   void ConfigJsonPatcher::cd_top(const std::string &path) {
0294     m_current = m_json.get();
0295     m_json_stack.clear();
0296     m_path_stack.clear();
0297     if (!path.empty())
0298       cd(path);
0299   }
0300 
0301   template <typename T>
0302   void ConfigJsonPatcher::replace(const std::string &path, T val) {
0303     nlohmann::json::json_pointer jp(path);
0304     m_current->at(jp) = val;
0305   }
0306   template void ConfigJsonPatcher::replace<int>(const std::string &path, int val);
0307   template void ConfigJsonPatcher::replace<float>(const std::string &path, float val);
0308   template void ConfigJsonPatcher::replace<double>(const std::string &path, double val);
0309 
0310   template <typename T>
0311   void ConfigJsonPatcher::replace(int first, int last, const std::string &path, T val) {
0312     nlohmann::json::json_pointer jp(path);
0313     for (int i = first; i <= last; ++i) {
0314       m_current->at(i).at(jp) = val;
0315     }
0316   }
0317   template void ConfigJsonPatcher::replace<int>(int first, int last, const std::string &path, int val);
0318   template void ConfigJsonPatcher::replace<float>(int first, int last, const std::string &path, float val);
0319   template void ConfigJsonPatcher::replace<double>(int first, int last, const std::string &path, double val);
0320 
0321   nlohmann::json &ConfigJsonPatcher::get(const std::string &path) {
0322     nlohmann::json::json_pointer jp(path);
0323     return m_current->at(jp);
0324   }
0325 
0326   int ConfigJsonPatcher::replace(const nlohmann::json &j) {
0327     if (j.is_null())
0328       throw std::runtime_error(exc_hdr(__func__) + "null not expected");
0329 
0330     if (j.is_boolean() || j.is_number() || j.is_string()) {
0331       throw std::runtime_error(exc_hdr(__func__) + "value not expected on this parsing level");
0332     }
0333 
0334     int n_replaced = 0;
0335 
0336     if (j.is_object()) {
0337       static const std::regex index_range_re("^\\[(\\d+)..(\\d+)\\]$", std::regex::optimize);
0338 
0339       for (auto &[key, value] : j.items()) {
0340         std::smatch m;
0341         std::regex_search(key, m, index_range_re);
0342 
0343         if (m.size() == 3) {
0344           if (!m_current->is_array())
0345             throw std::runtime_error(exc_hdr(__func__) + "array range encountered when current json is not an array");
0346           int first = std::stoi(m.str(1));
0347           int last = std::stoi(m.str(2));
0348           for (int i = first; i <= last; ++i) {
0349             std::string s("/");
0350             s += std::to_string(i);
0351             cd(s);
0352             if (value.is_array()) {
0353               for (auto &el : value)
0354                 n_replaced += replace(el);
0355             } else {
0356               n_replaced += replace(value);
0357             }
0358             cd_up();
0359           }
0360         } else if (value.is_array() || value.is_object()) {
0361           std::string s("/");
0362           s += key;
0363           cd(s);
0364           n_replaced += replace(value);
0365           cd_up();
0366         } else if (value.is_number() || value.is_boolean() || value.is_string()) {
0367           std::string s("/");
0368           s += key;
0369           nlohmann::json::json_pointer jp(s);
0370           if (m_current->at(jp) != value) {
0371             if (m_verbose)
0372               std::cout << "  " << get_abs_path() << s << ": " << m_current->at(jp) << " -> " << value << "\n";
0373 
0374             m_current->at(jp) = value;
0375             ++n_replaced;
0376           }
0377         } else {
0378           throw std::runtime_error(exc_hdr(__func__) + "unexpected value type");
0379         }
0380       }
0381     } else if (j.is_array() && j.empty()) {
0382     } else if (j.is_array()) {
0383       // Arrays are somewhat tricky.
0384       // At the moment all elements are expected to be objects.
0385       //    This means arrays of basic types are not supported (like layer index arrays).
0386       //    Should not be too hard to add support for this.
0387       // Now, the objects in the array can be of two kinds:
0388       // a) Their keys can be json_pointer strings starting with numbers or ranges [i_low..i_high].
0389       // b) They can be actual elements of the array. In this case we require the length of
0390       //    the array to be equal to existing length in the configuration.
0391       // It is not allowed for these two kinds to mix.
0392 
0393       // Determine the kind of array: json_ptr or object
0394 
0395       static const std::regex index_re("^(?:\\[\\d+..\\d+\\]|\\d+(?:/.*)?)$", std::regex::optimize);
0396 
0397       bool has_index = false, has_plain = false;
0398       for (int i = 0; i < (int)j.size(); ++i) {
0399         const nlohmann::json &el = j[i];
0400 
0401         if (!el.is_object())
0402           throw std::runtime_error(exc_hdr(__func__) + "array elements expected to be objects");
0403 
0404         for (nlohmann::json::const_iterator it = el.begin(); it != el.end(); ++it) {
0405           if (std::regex_search(it.key(), index_re)) {
0406             has_index = true;
0407             if (has_plain)
0408               throw std::runtime_error(exc_hdr(__func__) + "indexed array entry following plain one");
0409           } else {
0410             has_plain = true;
0411             if (has_index)
0412               throw std::runtime_error(exc_hdr(__func__) + "plain array entry following indexed one");
0413           }
0414         }
0415       }
0416       if (has_index) {
0417         for (auto &element : j) {
0418           n_replaced += replace(element);
0419         }
0420       } else {
0421         if (m_current && !m_current->is_array())
0422           throw std::runtime_error(exc_hdr(__func__) + "plain array detected when current is not an array");
0423         if (m_current->size() != j.size())
0424           throw std::runtime_error(exc_hdr(__func__) + "plain array of different size than at current pos");
0425 
0426         std::string s;
0427         for (int i = 0; i < (int)j.size(); ++i) {
0428           s = "/";
0429           s += std::to_string(i);
0430           cd(s);
0431           n_replaced += replace(j[i]);
0432           cd_up();
0433         }
0434       }
0435     } else {
0436       throw std::runtime_error(exc_hdr(__func__) + "unexpected json type");
0437     }
0438 
0439     return n_replaced;
0440   }
0441 
0442   std::string ConfigJsonPatcher::dump(int indent) { return m_json->dump(indent); }
0443 
0444   // ============================================================================
0445   // patch_File steering function
0446   // ============================================================================
0447   /*
0448     See example JSON patcher input: "mkFit/config-parse/test.json"
0449 
0450     The file can contain several valid JSON dumps in sequence.
0451 
0452     '/' character can be used to descend more than one level at a time.
0453 
0454     A number can be used to specify an array index. This can be combined with
0455     the '/' syntax.
0456 
0457     "[first,last]" key (as string) can be used to denote a range of array
0458     elements. Such a key must not be combined with a '/' syntax.
0459 */
0460 
0461   namespace {
0462     // Open file for writing, throw exception on failure.
0463     void open_ofstream(std::ofstream &ofs, const std::string &fname, const char *pfx = nullptr) {
0464       ofs.open(fname, std::ofstream::trunc);
0465       if (!ofs) {
0466         char m[2048];
0467         snprintf(m, 2048, "%s%sError opening %s for write: %m", pfx ? pfx : "", pfx ? " " : "", fname.c_str());
0468         throw std::runtime_error(m);
0469       }
0470     }
0471 
0472     // Open file for reading, throw exception on failure.
0473     void open_ifstream(std::ifstream &ifs, const std::string &fname, const char *pfx = nullptr) {
0474       ifs.open(fname);
0475       if (!ifs) {
0476         char m[2048];
0477         snprintf(m, 2048, "%s%sError opening %s for read: %m", pfx ? pfx : "", pfx ? " " : "", fname.c_str());
0478         throw std::runtime_error(m);
0479       }
0480     }
0481 
0482     // Skip white-space, return true if more characters are available, false if eof.
0483     bool skipws_ifstream(std::ifstream &ifs) {
0484       while (std::isspace(ifs.peek()))
0485         ifs.get();
0486       return !ifs.eof();
0487     }
0488   }  // namespace
0489 
0490   void ConfigJson::patch_Files(IterationsInfo &its_info,
0491                                const std::vector<std::string> &fnames,
0492                                ConfigJsonPatcher::PatchReport *report) {
0493     ConfigJsonPatcher cjp(m_verbose);
0494     cjp.load(its_info);
0495 
0496     ConfigJsonPatcher::PatchReport rep;
0497 
0498     for (auto &fname : fnames) {
0499       std::ifstream ifs;
0500       open_ifstream(ifs, fname, __func__);
0501 
0502       if (m_verbose) {
0503         printf("%s begin reading from file %s.\n", __func__, fname.c_str());
0504       }
0505 
0506       int n_read = 0, n_tot_replaced = 0;
0507       while (skipws_ifstream(ifs)) {
0508         nlohmann::json j;
0509         ifs >> j;
0510         ++n_read;
0511 
0512         if (m_verbose) {
0513           std::cout << " Read JSON entity " << n_read << " -- applying patch:\n";
0514           // std::cout << j.dump(3) << "\n";
0515         }
0516 
0517         int n_replaced = cjp.replace(j);
0518 
0519         if (m_verbose) {
0520           std::cout << " Replaced " << n_replaced << " entries.\n";
0521         }
0522         cjp.cd_top();
0523         n_tot_replaced += n_replaced;
0524       }
0525 
0526       if (m_verbose) {
0527         printf("%s read %d JSON entities from file %s, replaced %d parameters.\n",
0528                __func__,
0529                n_read,
0530                fname.c_str(),
0531                n_tot_replaced);
0532       }
0533 
0534       ifs.close();
0535 
0536       rep.inc_counts(1, n_read, n_tot_replaced);
0537     }
0538 
0539     if (rep.n_replacements > 0) {
0540       cjp.save(its_info);
0541     }
0542 
0543     if (report)
0544       report->inc_counts(rep);
0545   }
0546 
0547   std::unique_ptr<IterationConfig> ConfigJson::patchLoad_File(const IterationsInfo &its_info,
0548                                                               const std::string &fname,
0549                                                               ConfigJsonPatcher::PatchReport *report) {
0550     ConfigJsonPatcher::PatchReport rep;
0551 
0552     std::ifstream ifs;
0553     open_ifstream(ifs, fname, __func__);
0554 
0555     if (m_verbose) {
0556       printf("%s begin reading from file %s.\n", __func__, fname.c_str());
0557     }
0558 
0559     if (!skipws_ifstream(ifs))
0560       throw std::runtime_error("empty file");
0561 
0562     nlohmann::json j;
0563     ifs >> j;
0564     int track_algo = j["m_track_algorithm"];
0565 
0566     int iii = -1;
0567     for (int i = 0; i < its_info.size(); ++i) {
0568       if (its_info[i].m_track_algorithm == track_algo) {
0569         iii = i;
0570         break;
0571       }
0572     }
0573     if (iii == -1)
0574       throw std::runtime_error("matching IterationConfig not found");
0575 
0576     if (m_verbose) {
0577       std::cout << " Read JSON entity, Iteration index is " << iii << " -- cloning and applying JSON patch:\n";
0578     }
0579 
0580     IterationConfig *icp = new IterationConfig(its_info[iii]);
0581     IterationConfig &ic = *icp;
0582 
0583     ConfigJsonPatcher cjp(m_verbose);
0584     cjp.load(ic);
0585 
0586     int n_replaced = cjp.replace(j);
0587 
0588     cjp.cd_top();
0589 
0590     if (m_verbose) {
0591       printf("%s read 1 JSON entity from file %s, replaced %d parameters.\n", __func__, fname.c_str(), n_replaced);
0592     }
0593 
0594     ifs.close();
0595 
0596     rep.inc_counts(1, 1, n_replaced);
0597 
0598     if (rep.n_replacements > 0) {
0599       cjp.save(ic);
0600     }
0601 
0602     if (report)
0603       report->inc_counts(rep);
0604 
0605     return std::unique_ptr<IterationConfig>(icp);
0606   }
0607 
0608   std::unique_ptr<IterationConfig> ConfigJson::load_File(const std::string &fname) {
0609     std::ifstream ifs;
0610     open_ifstream(ifs, fname, __func__);
0611 
0612     if (m_verbose) {
0613       printf("%s begin reading from file %s.\n", __func__, fname.c_str());
0614     }
0615 
0616     if (!skipws_ifstream(ifs))
0617       throw std::runtime_error("empty file");
0618 
0619     nlohmann::json j;
0620     ifs >> j;
0621 
0622     if (m_verbose) {
0623       std::cout << " Read JSON entity, iteration index is " << j["m_iteration_index"] << ", track algorithm is "
0624                 << j["m_track_algorithm"] << ". Instantiating IterationConfig object and over-laying it with JSON.\n";
0625     }
0626 
0627     IterationConfig *icp = new IterationConfig();
0628 
0629     from_json(j, *icp);
0630 
0631     return std::unique_ptr<IterationConfig>(icp);
0632   }
0633 
0634   // ============================================================================
0635   // Save each IterationConfig into a separate json file
0636   // ============================================================================
0637 
0638   void ConfigJson::save_Iterations(IterationsInfo &its_info,
0639                                    const std::string &fname_fmt,
0640                                    bool include_iter_info_preamble) {
0641     bool has_pct_d = fname_fmt.find("%d") != std::string::npos;
0642     bool has_pct_s = fname_fmt.find("%s") != std::string::npos;
0643 
0644     assert((has_pct_d || has_pct_s) && "JSON save filename-format must include a %d or %s substring");
0645     assert(!(has_pct_d && has_pct_s) && "JSON save filename-format must include only one of %d or %s substrings");
0646 
0647     for (int ii = 0; ii < its_info.size(); ++ii) {
0648       const IterationConfig &itconf = its_info[ii];
0649 
0650       char fname[1024];
0651       if (has_pct_d)
0652         snprintf(fname, 1024, fname_fmt.c_str(), ii);
0653       else
0654         snprintf(fname, 1024, fname_fmt.c_str(), TrackBase::algoint_to_cstr(itconf.m_track_algorithm));
0655 
0656       std::ofstream ofs;
0657       open_ofstream(ofs, fname, __func__);
0658 
0659       if (include_iter_info_preamble) {
0660         ofs << "{ \"m_iterations/" << ii << "\": ";
0661       }
0662 
0663       nlohmann::ordered_json j;
0664       to_json(j, itconf);
0665 
0666       ofs << std::setw(1);
0667       ofs << j;
0668 
0669       if (include_iter_info_preamble) {
0670         ofs << " }";
0671       }
0672 
0673       ofs << "\n";
0674       ofs.close();
0675     }
0676   }
0677 
0678   void ConfigJson::dump(IterationsInfo &its_info) {
0679     nlohmann::ordered_json j = its_info;
0680     std::cout << j.dump(3) << "\n";
0681   }
0682 
0683   // ============================================================================
0684   // Tests for ConfigJson stuff
0685   // ============================================================================
0686 
0687   void ConfigJson::test_Direct(IterationConfig &it_cfg) {
0688     using nlohmann::json;
0689 
0690     std::string lojz("/m_select_max_dphi");
0691 
0692     json j = it_cfg;
0693     std::cout << j.dump(1) << "\n";
0694 
0695     std::cout << "Layer 43, m_select_max_dphi = " << j["/m_layer_configs/43/m_select_max_dphi"_json_pointer] << "\n";
0696     std::cout << "Patching it to pi ...\n";
0697     json p = R"([
0698         { "op": "replace", "path": "/m_layer_configs/43/m_select_max_dphi", "value": 3.141 }
0699     ])"_json;
0700     j = j.patch(p);
0701     std::cout << "Layer 43, m_select_max_dphi = " << j["/m_layer_configs/43/m_select_max_dphi"_json_pointer] << "\n";
0702 
0703     auto &jx = j["/m_layer_configs/60"_json_pointer];
0704     // jx["m_select_max_dphi"] = 99.876;
0705     json::json_pointer jp(lojz);
0706     jx[jp] = 99.876;
0707 
0708     // try loading it back, see what happens to vector m_layer_configs.
0709 
0710     from_json(j, it_cfg);
0711     printf("Layer 43 : m_select_max_dphi = %f, size_of_layer_vec=%d, m_n_regions=%d, size_of_steering_params=%d\n",
0712            it_cfg.m_layer_configs[43].m_select_max_dphi,
0713            (int)it_cfg.m_layer_configs.size(),
0714            it_cfg.m_n_regions,
0715            (int)it_cfg.m_steering_params.size());
0716 
0717     printf("Layer 60 : m_select_max_dphi = %f, size_of_layer_vec=%d, m_n_regions=%d, size_of_steering_params=%d\n",
0718            it_cfg.m_layer_configs[60].m_select_max_dphi,
0719            (int)it_cfg.m_layer_configs.size(),
0720            it_cfg.m_n_regions,
0721            (int)it_cfg.m_steering_params.size());
0722 
0723     // try accessing something that does not exist
0724 
0725     // std::cout << "Non-existent path " << j["/m_layer_configs/143/m_select_max_dphi"_json_pointer] << "\n";
0726 
0727     auto &x = j["/m_layer_configs"_json_pointer];
0728     std::cout << "Typename /m_layer_configs " << x.type_name() << "\n";
0729     auto &y = j["/m_layer_configs/143"_json_pointer];
0730     std::cout << "Typename /m_layer_configs/143 " << y.type_name() << ", is_null=" << y.is_null() << "\n";
0731   }
0732 
0733   void ConfigJson::test_Patcher(IterationConfig &it_cfg) {
0734     ConfigJsonPatcher cjp;
0735     cjp.load(it_cfg);
0736 
0737     std::cout << cjp.dump(1) << "\n";
0738 
0739     {
0740       cjp.cd("/m_layer_configs/43/m_select_max_dphi");
0741       std::cout << "Layer 43, m_select_max_dphi = " << cjp.get("") << "\n";
0742       std::cout << "Setting it to pi ...\n";
0743       cjp.replace("", 3.141);
0744       cjp.cd_top();
0745       std::cout << "Layer 43, m_select_max_dphi = " << cjp.get("/m_layer_configs/43/m_select_max_dphi") << "\n";
0746     }
0747     {
0748       std::cout << "Replacing layer 60 m_select_max_dphi with full path\n";
0749       cjp.replace("/m_layer_configs/60/m_select_max_dphi", 99.876);
0750     }
0751     try {
0752       std::cout << "Trying to replace an non-existent array entry\n";
0753       cjp.replace("/m_layer_configs/1460/m_select_max_dphi", 666.666);
0754     } catch (std::exception &exc) {
0755       std::cout << "Caugth exception: " << exc.what() << "\n";
0756     }
0757     try {
0758       std::cout << "Trying to replace an non-existent object entry\n";
0759       cjp.replace("/m_layer_configs/1/moo_select_max_dphi", 666.666);
0760     } catch (std::exception &exc) {
0761       std::cout << "Caugth exception: " << exc.what() << "\n";
0762     }
0763     {
0764       std::cout << "Replacing m_select_max_dphi on layers 1 to 3 to 7.7\n";
0765       cjp.cd("/m_layer_configs");
0766       cjp.replace(1, 3, "/m_select_max_dphi", 7.7);
0767       cjp.cd_top();
0768     }
0769 
0770     // try getting it back into c++, see what happens to vector m_layer_configs.
0771 
0772     cjp.save(it_cfg);
0773 
0774     printf("Layer 43: m_select_max_dphi = %f, size_of_layer_vec=%d, m_n_regions=%d, size_of_steering_params=%d\n",
0775            it_cfg.m_layer_configs[43].m_select_max_dphi,
0776            (int)it_cfg.m_layer_configs.size(),
0777            it_cfg.m_n_regions,
0778            (int)it_cfg.m_steering_params.size());
0779 
0780     printf("Layer 60: m_select_max_dphi = %f\n", it_cfg.m_layer_configs[60].m_select_max_dphi);
0781     for (int i = 0; i < 5; ++i)
0782       printf("Layer %2d: m_select_max_dphi = %f\n", i, it_cfg.m_layer_configs[i].m_select_max_dphi);
0783 
0784     // try accessing something that does not exist
0785 
0786     // std::cout << "Non-existent path " << j["/m_layer_configs/143/m_select_max_dphi"_json_pointer] << "\n";
0787 
0788     auto &j = cjp.get("");
0789 
0790     auto &x = j["/m_layer_configs"_json_pointer];
0791     std::cout << "Typename /m_layer_configs " << x.type_name() << "\n";
0792     auto &y = j["/m_layer_configs/143"_json_pointer];
0793     std::cout << "Typename /m_layer_configs/143 " << y.type_name() << ", is_null=" << y.is_null() << "\n";
0794   }
0795 
0796 }  // namespace mkfit