5 #include "boost/algorithm/string.hpp" 7 #include "cetlib/container_algorithms.h" 9 #include "range/v3/view.hpp" 11 #include <initializer_list> 16 using namespace fhicl;
22 using modules_t = std::map<std::string, std::string>;
26 std::string
const at_nil{
"@nil"};
27 std::string
const trigger_paths_str{
"trigger_paths"};
28 std::string
const end_paths_str{
"end_paths"};
30 matches(std::string
const& path_spec_str, std::string
const& path_name)
32 std::regex
const path_spec_re{R
"((\d+:)?)" + path_name}; 33 return std::regex_match(path_spec_str, path_spec_re);
36 auto module_tables = {
"physics.producers",
40 auto modifier_tables = {
"physics.producers",
"physics.filters"};
41 auto observer_tables = {
"physics.analyzers",
"outputs"};
43 auto allowed_physics_tables = {
"producers",
"filters",
"analyzers"};
48 config_exception(std::string
const& context)
54 path_exception(std::string
const& selection_override,
56 std::string
const& suffix)
58 std::string msg{
"The following error occurred while processing " +
60 msg +=
" (i.e. '" + suffix +
"'):";
61 return config_exception(msg);
68 case ModuleCategory::modifier:
71 case ModuleCategory::observer:
74 case ModuleCategory::unset:
83 auto result = ModuleCategory::unset;
85 case ModuleCategory::modifier:
86 result = ModuleCategory::observer;
88 case ModuleCategory::observer:
89 result = ModuleCategory::modifier;
91 case ModuleCategory::unset: {
93 <<
"The " << cat <<
" category has no opposite.\n";
100 module_category(std::string
const& full_module_key)
105 full_module_key.substr(0, full_module_key.find_last_of(
'.'));
106 if (cet::search_all(modifier_tables, table)) {
107 return ModuleCategory::modifier;
109 if (cet::search_all(observer_tables, table)) {
110 return ModuleCategory::observer;
112 return ModuleCategory::unset;
116 is_path_selection_override(std::string
const& path_name)
118 return path_name == trigger_paths_str or path_name == end_paths_str;
125 case ModuleCategory::modifier:
126 return trigger_paths_str;
127 case ModuleCategory::observer:
128 return end_paths_str;
138 std::initializer_list<char const*> tables)
141 for (
auto const tbl : tables) {
145 for (
auto const& [modname,
value] : table) {
152 auto const key =
fhicl_key(tbl, modname);
153 auto const [it, success] = result.try_emplace(modname,
fhicl_key(key));
154 if (!success && it->second != key) {
155 auto const& cached_key = it->second;
157 cached_key.substr(0, cached_key.rfind(modname) - 1);
158 throw config_exception(
"An error occurred while processing " 159 "module configurations.")
160 <<
"Module label '" << modname <<
"' has been used in '" << tbl
161 <<
"' and '" << parent <<
"'.\n" 162 <<
"Module labels must be unique across an art process.\n";
172 std::vector<ModuleSpec>
175 std::vector<ModuleSpec> result;
176 for (
auto const& ev : seq) {
177 if (allow_nil_entries and ev.is_a(
NIL)) {
184 auto mod_spec = ev.to_string();
185 if (
empty(mod_spec)) {
188 boost::replace_all(mod_spec,
"\"",
"");
190 if (mod_spec[0] ==
'!') {
191 action = FilterAction::Veto;
192 mod_spec = mod_spec.substr(1);
193 }
else if (mod_spec[0] ==
'-') {
194 action = FilterAction::Ignore;
195 mod_spec = mod_spec.substr(1);
199 if (mod_spec.find_first_of(
"!-") != std::string::npos) {
200 throw config_exception(
"There was an error parsing the entry "s +
201 ev.to_string() +
"in a FHiCL sequence.")
202 <<
"The '!' or '-' character may appear as only the first character " 203 "in the path entry.\n";
205 result.push_back({mod_spec, action});
208 if (result.size() != seq.size()) {
209 throw config_exception(
"There was an error parsing the specified entries " 210 "in a FHiCL sequence.")
211 <<
"One of the presented elements is either an empty string or not a " 217 std::vector<art::PathSpec>
218 path_specs(std::vector<ModuleSpec>
const& selection_override_entries,
219 std::string
const& path_selection_override)
221 auto guidance = [](std::string
const& name,
222 std::string
const& path_selection_override,
223 std::string
const& id_str) {
224 std::ostringstream oss;
225 oss <<
"If you would like to repeat the path specification, all " 226 "path specifications\n" 228 << name <<
"' must be prepended with the same path ID (e.g.):\n\n" 229 <<
" " << path_selection_override <<
": ['" << id_str <<
':' << name
230 <<
"', '" << id_str <<
':' << name <<
"', ...]\n\n";
234 std::map<PathID, std::string> id_to_name;
235 std::map<std::string, PathID> name_to_id;
236 std::vector<art::PathSpec> result;
239 for (
auto it =
cbegin(selection_override_entries),
240 e =
cend(selection_override_entries);
243 auto const& path = *it;
245 if (spec.name == at_nil) {
250 auto const emplacement_result =
251 name_to_id.try_emplace(spec.name, spec.path_id);
252 bool const name_already_present = not emplacement_result.second;
253 auto const emplaced_path_id = emplacement_result.first->second;
255 if (name_already_present) {
257 throw path_exception(path_selection_override, i, path.name)
258 <<
"The path name '" << spec.name
259 <<
"' has already been specified in the " << path_selection_override
261 << guidance(spec.name,
262 path_selection_override,
265 if (spec.path_id != emplaced_path_id) {
266 throw path_exception(path_selection_override, i, path.name)
267 <<
"The path name '" << spec.name
268 <<
"' has already been specified (perhaps implicitly) with a\n" 270 <<
to_string(emplaced_path_id) <<
" (not " 271 <<
to_string(spec.path_id) <<
") in the " << path_selection_override
273 << guidance(spec.name,
274 path_selection_override,
285 emplacement_result.first->second = spec.path_id;
289 if (
auto const [it, inserted] =
290 id_to_name.try_emplace(spec.path_id, spec.name);
292 throw path_exception(path_selection_override, i, path.name)
294 <<
" cannot be assigned to path name '" << spec.name
295 <<
"' as it has already been assigned to path name '" << it->second
299 result.push_back(std::move(spec));
307 verify_supported_names(
table_t const& physics_table)
309 std::string bad_names{};
310 for (
auto const& [name,
value] : physics_table) {
316 if (is_table and cet::search_all(allowed_physics_tables, name)) {
319 std::string
const type = is_table ?
"table" :
"atom";
320 bad_names +=
" \"physics." + name +
"\" (" + type +
")\n";
323 if (
empty(bad_names)) {
327 throw config_exception(
328 "\nYou have specified the following unsupported parameters in the\n" 329 "\"physics\" block of your configuration:\n")
331 <<
"\nSupported parameters include the following tables:\n" 332 " \"physics.producers\"\n" 333 " \"physics.filters\"\n" 334 " \"physics.analyzers\"\n" 335 "and sequences. Atomic configuration parameters are not " 341 std::set<std::string>
const& empty_paths,
342 std::string
const& path_selection_override)
348 for (
auto const& name : empty_paths) {
352 [&name](
auto const& ex_val) {
353 if (not ex_val.is_a(
STRING)) {
356 std::string path_spec_str;
358 return matches(path_spec_str, name);
368 std::string
const physics{
"physics"};
372 std::set<std::string> empty_paths;
373 std::map<std::string, std::vector<ModuleSpec>> paths;
375 verify_supported_names(table);
376 for (
auto const& [path_name, module_names] : table) {
381 if (
empty(entries)) {
382 empty_paths.insert(path_name);
385 module_names, is_path_selection_override(path_name));
390 config, empty_paths,
fhicl_key(physics, trigger_paths_str));
391 replace_empty_paths(config, empty_paths,
fhicl_key(physics, end_paths_str));
410 for (
auto const& [path_name, entries] : all_paths) {
412 if (is_path_selection_override(path_name)) {
415 std::vector<ModuleSpec> right_modules;
416 std::vector<std::string> wrong_modules;
417 for (
auto const& mod_spec : entries) {
418 auto const& name = mod_spec.name;
419 auto full_module_key_it = modules.find(name);
420 if (full_module_key_it ==
cend(modules)) {
421 throw config_exception(
"The following error occurred while " 422 "processing a path configuration:")
423 <<
"Entry with name " << name <<
" in path " << path_name
424 <<
" does not have a module configuration.\n";
426 auto const& full_module_key = full_module_key_it->second;
427 auto const module_cat = module_category(full_module_key);
428 assert(module_cat != ModuleCategory::unset);
429 if (module_cat == category) {
430 right_modules.push_back(mod_spec);
432 wrong_modules.push_back(name);
436 if (right_modules.empty()) {
443 if (right_modules.size() == entries.size()) {
444 sorted_result.try_emplace(path_name, std::move(right_modules));
448 auto e = config_exception(
"An error occurred while " 449 "processing a path configuration.");
450 e <<
"The following modules specified in path " << path_name <<
" are " 451 << opposite(category)
454 << category <<
"s:\n";
455 for (
auto const& modname : wrong_modules) {
456 e <<
" '" << modname <<
"'\n";
465 for (
auto const& [path_name, modules] : sorted_result) {
473 std::vector<ModuleSpec>
const& override_entries,
475 std::string
const& path_selection_override)
482 std::ostringstream os;
483 for (
auto& spec : specs) {
484 auto res = modules_for_path.find(spec.name);
485 if (res ==
cend(modules_for_path)) {
486 os <<
"Unknown path " << spec.name <<
" has been specified in '" 487 << path_selection_override <<
"'.\n";
492 if (
empty(res->second)) {
497 for (
auto const& entry : res->second) {
498 auto const& name = entry.name;
499 auto full_module_key_it = modules.find(name);
500 if (full_module_key_it ==
cend(modules)) {
501 throw config_exception(
"The following error occurred while " 502 "processing a path configuration:")
503 <<
"Entry with name " << name <<
" in path " << spec.name
504 <<
" does not have a module configuration.\n";
507 result.emplace_back(std::move(spec), res->second);
510 auto const err = os.str();
512 throw config_exception(
513 "The following error occurred while processing path configurations:")
521 get_enabled_modules(
modules_t const& modules,
526 for (
auto const& [
path_spec, entries] : enabled_paths) {
527 for (
auto const& [module_name, action] : entries) {
528 auto const& module_key = modules.at(module_name);
529 auto const actual_category = module_category(module_key);
531 if (actual_category != category) {
532 throw config_exception(
"The following error occurred while " 533 "processing a path configuration:")
534 <<
"The '" << path_selection_override(category)
535 <<
"' override parameter contains the path " <<
path_spec.
name 537 << (actual_category == ModuleCategory::observer ?
" an\n" :
" a\n")
538 <<
to_string(
type) <<
" with the name " << module_name <<
".\n\n" 540 <<
" should instead be included as part of the '" 541 << path_selection_override(opposite(category)) <<
"' parameter.\n" 542 <<
"Contact artists@fnal.gov for guidance.\n";
546 throw config_exception(
"The following error occurred while " 547 "processing a path configuration:")
548 <<
"Entry with name " << module_name <<
" in path " 550 << (category == ModuleCategory::observer ?
" an " :
" a ")
551 <<
to_string(
type) <<
" and cannot have a '!' or '-' prefix.\n";
559 std::pair<module_entries_for_ordered_path_t, bool>
564 auto const selection_override = path_selection_override(category);
565 auto const it = paths.find(selection_override);
566 if (it ==
cend(paths)) {
567 return {paths_for_category(paths, modules, category),
false};
571 explicitly_declared_paths(paths, it->second, modules, selection_override),
578 bool const report_enabled,
581 auto const modules = declared_modules(config, module_tables);
583 auto paths = all_paths(config);
585 auto [trigger_paths, trigger_paths_override] =
586 enabled_paths(paths, modules, ModuleCategory::modifier);
587 auto [end_paths, end_paths_override] =
588 enabled_paths(paths, modules, ModuleCategory::observer);
590 auto enabled_modules =
591 get_enabled_modules(modules, trigger_paths, ModuleCategory::modifier);
595 auto end_path_enabled_modules =
596 get_enabled_modules(modules, end_paths, ModuleCategory::observer);
597 enabled_modules.insert(
begin(end_path_enabled_modules),
598 end(end_path_enabled_modules));
601 for (
auto const& pr : modules) {
602 if (enabled_modules.find(pr.first) ==
cend(enabled_modules)) {
603 unused_modules.insert(pr);
609 paths.erase(
"trigger_paths");
610 paths.erase(
"end_paths");
611 for (
auto const& spec : trigger_paths | views::keys) {
612 paths.erase(spec.name);
614 for (
auto const& spec : end_paths | views::keys) {
615 paths.erase(spec.name);
619 if (report_enabled && !
empty(paths)) {
620 std::cerr <<
"The following paths have not been enabled for execution and " 621 "will be ignored:\n";
622 for (
auto const& path_name : paths | views::keys) {
623 std::cerr <<
" " << path_name <<
'\n';
627 if (report_enabled && !
empty(unused_modules)) {
628 std::ostringstream os;
629 os <<
"The following module label" 630 << ((unused_modules.size() == 1) ?
" is" :
"s are")
631 <<
" either not assigned to any path,\n" 632 <<
"or " << ((unused_modules.size() == 1ull) ?
"it has" :
"they have")
633 <<
" been assigned to ignored path(s):\n";
634 for (
auto const& label : unused_modules | views::keys) {
635 os <<
" " << label <<
'\n';
637 std::cerr << os.str();
641 auto to_full_path_name = [](
auto const& path_name) {
644 for (
auto const& key :
645 paths | views::keys | views::transform(to_full_path_name)) {
653 auto if_outside_prolog = [&config](
auto const& table_name) {
656 for (
auto const& table_name :
657 module_tables | views::filter(if_outside_prolog)) {
659 config.
erase(table_name);
666 config.
erase(
"physics");
672 if (not
empty(end_paths) and not end_paths_override) {
674 auto end_paths_entries =
675 end_paths | views::keys |
679 config.
put(
"physics.end_paths", std::move(end_paths_entries));
683 if (not
empty(trigger_paths)) {
684 auto trigger_paths_entries = trigger_paths | views::keys |
685 views::transform([](
auto const&
path_spec) {
689 if (not trigger_paths_override) {
690 config.
put(
"physics.trigger_paths", trigger_paths_entries);
692 config.
put(
"trigger_paths.trigger_paths", std::move(trigger_paths_entries));
696 std::move(trigger_paths),
697 std::move(end_paths),
698 trigger_paths_override,
decltype(auto) constexpr cend(T &&obj)
ADL-aware version of std::cend.
bool exists_outside_prolog(fhicl::intermediate_table const &config, std::string const &key)
std::vector< ModuleSpec > sequence_to_entries(sequence_t const &seq, bool const allow_nil_entries)
std::map< std::string, ModuleKeyAndType > keytype_for_name_t
ModuleType module_type(std::string const &full_key)
std::map< std::string, std::vector< ModuleSpec >> module_entries_for_path_t
std::string to_string(Protection p)
void decode(std::any const &, std::string &)
std::map< std::string, std::string > modules_t
decltype(auto) constexpr end(T &&obj)
ADL-aware version of std::end.
EnabledModules prune_config_if_enabled(bool prune_config, bool report_enabled, fhicl::intermediate_table &config)
decltype(auto) constexpr to_string(T &&obj)
ADL-aware version of std::to_string.
decltype(auto) values(Coll &&coll)
Range-for loop helper iterating across the values of the specified collection.
std::enable_if_t< std::is_convertible_v< T, std::string >, std::string > fhicl_key(T const &name)
std::vector< extended_value > sequence_t
shims::map< std::string, extended_value > table_t
fhicl::extended_value::sequence_t sequence_t
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
constexpr static auto invalid() noexcept
decltype(auto) constexpr cbegin(T &&obj)
ADL-aware version of std::cbegin.
bool is_table(par_type const pt)
PathSpec path_spec(std::string const &path_spec)
std::vector< art::PathSpec > path_specs(std::vector< ModuleSpec > const &selection_override_entries, std::string const &path_selection_override)
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
std::ostream & operator<<(std::ostream &, ParameterSetID const &)
decltype(auto) constexpr empty(T &&obj)
ADL-aware version of std::empty.
std::vector< std::pair< PathSpec, std::vector< ModuleSpec >>> module_entries_for_ordered_path_t