Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2023-12-19 04:05:13

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