check_data_dependencies.cc
Go to the documentation of this file.
9 #include "boost/graph/graph_utility.hpp"
11 #include "fhiclcpp/ParameterSet.h"
16 #include "fhiclcpp/types/Table.h"
17 #include "fhiclcpp/types/TupleAs.h"
18 
19 #include <cassert>
20 #include <fstream>
21 #include <iostream>
22 #include <regex>
23 
24 using namespace art::detail;
25 using namespace fhicl;
26 using namespace std::string_literals;
27 using std::string;
28 
29 namespace {
30  names_t const all_tables{"physics.producers",
31  "physics.filters",
32  "physics.analyzers",
33  "outputs"};
34  names_t const tables_with_modifiers{all_tables[0], all_tables[1]};
35  names_t const tables_with_observers{all_tables[2], all_tables[3]};
36 
38  module_type_for_table(string const& table)
39  {
40  if (table == "physics.producers")
42  if (table == "physics.filters")
44  if (table == "physics.analyzers")
46  if (table == "outputs")
49  }
50 
51  auto
52  get_module_configs(ParameterSet const& pset)
53  {
54  std::map<std::string, ModuleConfigInfo> result;
55  for (auto const& name : all_tables) {
56  if (!pset.has_key(name))
57  continue;
58 
59  if (!pset.is_key_to_table(name))
60  continue;
61 
62  auto const table_pset = pset.get<fhicl::ParameterSet>(name);
63  auto const modules_in_table = table_pset.get_pset_names();
64  auto const module_type = module_type_for_table(name);
65  for (auto const& module_name : modules_in_table) {
67  {},
68  module_name, // Use module-name for libspec
69  module_name,
73  md, table_pset.get<fhicl::ParameterSet>(module_name), module_type};
74  result.emplace(module_name, std::move(info));
75  }
76  }
77  return result;
78  }
79 
81  get_paths_to_modules(
82  ParameterSet const& physics,
83  std::map<std::string, ModuleConfigInfo> const& module_configs)
84  {
86  for (auto const& name : physics.get_names()) {
87  if (!physics.is_key_to_sequence(name))
88  continue;
89  auto const tmp = physics.get<std::vector<string>>(name);
90  configs_t configs;
92  tmp, back_inserter(configs), [&module_configs](auto const& label) {
93  auto const& info = module_configs.at(label);
96  });
97  result.emplace_back(art::PathSpec{name, art::PathID::invalid()},
98  std::move(configs));
99  }
100  return result;
101  }
102 
103  inline bool
104  module_found_in_table(string const& module_name,
105  ParameterSet const& pset,
106  string const& table_name)
107  {
108  if (!pset.has_key(table_name)) {
109  return false;
110  }
111  auto const& table = pset.get<ParameterSet>(table_name);
112  return table.has_key(module_name);
113  }
114 
115  inline art::ModuleType
116  module_found_with_type(string const& module_name, ParameterSet const& pset)
117  {
118  if (module_found_in_table(module_name, pset, "physics.producers"))
120  if (module_found_in_table(module_name, pset, "physics.filters"))
122  if (module_found_in_table(module_name, pset, "physics.analyzers"))
124  if (module_found_in_table(module_name, pset, "outputs"))
127  }
128 
129  inline string
130  table_for_module_type(art::ModuleType const module_type)
131  {
132  if (module_type == art::ModuleType::producer)
133  return "physics.producers";
134  if (module_type == art::ModuleType::filter)
135  return "physics.filters";
136  if (module_type == art::ModuleType::analyzer)
137  return "physics.analyzers";
138  if (module_type == art::ModuleType::output_module)
139  return "outputs";
140  return {};
141  }
142 
143  bool
144  module_found_in_tables(string const& module_name,
145  ParameterSet const& pset,
146  names_t const& table_names)
147  {
148  for (auto const& table_name : table_names) {
149  if (module_found_in_table(module_name, pset, table_name)) {
150  return true;
151  }
152  }
153  return false;
154  }
155 
157  select_paths(ParameterSet const& pset,
158  names_t const& tables,
159  paths_to_modules_t& paths_to_modules)
160  {
162  std::vector<string> paths_to_erase;
163  for (auto const& pr : paths_to_modules) {
164  auto const& path_name = pr.first.name;
165  auto const& modules = pr.second;
166  bool first_module{true};
167  bool present{true};
168  for (auto const& module : modules) {
169  auto const& module_label =
170  module.moduleConfigInfo->modDescription.moduleLabel();
171  if (first_module) {
172  first_module = false;
173  present = module_found_in_tables(module_label, pset, tables);
174  } else if (present !=
175  module_found_in_tables(module_label, pset, tables)) {
176  // The presence of the first module determines what the
177  // remaining modules should be.
179  << "There is an inconsistency in path " << path_name << ".\n"
180  << "Module " << module_label
181  << " is a modifier/observer whereas the other modules\n"
182  << "on the path are the opposite.";
183  }
184  }
185  if (present) {
186  result.emplace_back(pr);
187  paths_to_erase.push_back(path_name);
188  }
189  }
190  for (auto const& path : paths_to_erase) {
191  auto const path_it =
192  std::find_if(paths_to_modules.cbegin(),
193  paths_to_modules.cend(),
194  [&path](auto const& pr) { return pr.first.name == path; });
195  assert(path_it != paths_to_modules.cend());
196  paths_to_modules.erase(path_it);
197  }
198  return result;
199  }
200 
201  configs_t
202  merge_end_paths(paths_to_modules_t const& paths_to_modules)
203  {
205  for (auto const& pr : paths_to_modules) {
206  result.insert(cend(result), cbegin(pr.second), cend(pr.second));
207  }
208  return result;
209  }
210 
211  name_set_t
212  path_names(paths_to_modules_t const& paths_to_modules)
213  {
215  for (auto const& pr : paths_to_modules) {
216  result.insert(pr.first.name);
217  }
218  return result;
219  }
220 
221  std::set<art::ProductInfo>
222  sorted_produced_products(
223  std::vector<art::test::TypeAndInstance> const& productsToProduce,
224  string const& module_name,
225  string const& current_process_name)
226  {
227  std::set<art::ProductInfo> result;
228  for (auto const& prod : productsToProduce) {
229  result.emplace(
231  prod.friendlyClassName,
232  module_name,
233  prod.productInstanceName,
234  art::ProcessTag{current_process_name, current_process_name});
235  }
236  return result;
237  }
238 
239  template <typename T>
240  auto
241  consumables_for_module(Table<T> const& module,
242  string const& current_process_name)
243  {
244  std::vector<art::ProductInfo> sorted_deps;
245  std::vector<art::test::TypeAndTag> deps;
246  if (module().consumes(deps)) {
247  for (auto const& dep : deps) {
248  art::ProcessTag const processTag{dep.inputTag.process(),
249  current_process_name};
250  sorted_deps.emplace_back(art::ProductInfo::ConsumableType::Product,
251  dep.friendlyClassName,
252  dep.inputTag.label(),
253  dep.inputTag.instance(),
254  processTag);
255  }
256  }
257  std::vector<std::string> many;
258  if (module().consumesMany(many)) {
259  for (auto const& class_name : many) {
260  sorted_deps.emplace_back(art::ProductInfo::ConsumableType::Many,
261  class_name);
262  }
263  }
264  cet::sort_all(sorted_deps);
265  art::ConsumesInfo::consumables_t::mapped_type result{{}};
266  result[art::InEvent] = std::move(sorted_deps);
267  return result;
268  }
269 
270  void
271  fillProducesInfo(
272  ParameterSet const& pset,
273  string const& process_name,
274  string const& path_name,
275  configs_t const& module_configs,
276  std::map<std::string, std::set<art::ProductInfo>>& produced_products,
277  collection_map_t& modules)
278  {
279  auto const begin = cbegin(module_configs);
280  for (auto it = begin, end = cend(module_configs); it != end; ++it) {
281  auto const& module_name =
282  it->moduleConfigInfo->modDescription.moduleLabel();
283  auto& info = modules[module_name];
284  info.paths.insert(path_name);
285  info.module_type = module_found_with_type(module_name, pset);
286  auto const table_name = table_for_module_type(info.module_type);
287  auto const& table =
288  pset.get<ParameterSet>(table_name + "." + module_name);
289  if (!is_modifier(info.module_type))
290  continue;
291 
293  std::vector<art::test::TypeAndInstance> prods;
294  if (mod().produces(prods)) {
295  info.produced_products =
296  sorted_produced_products(prods, module_name, process_name);
297  produced_products[module_name] = info.produced_products;
298  }
299  }
300  }
301 
302  void
303  fillModifierInfo(
304  ParameterSet const& pset,
305  string const& process_name,
306  string const& path_name,
307  configs_t const& module_configs,
308  std::map<std::string, std::set<art::ProductInfo>> const& produced_products,
309  collection_map_t& modules)
310  {
311  auto const begin = cbegin(module_configs);
312  for (auto it = begin, end = cend(module_configs); it != end; ++it) {
313  auto const& module_name =
314  it->moduleConfigInfo->modDescription.moduleLabel();
315  auto& info = modules[module_name];
316  info.paths.insert(path_name);
317  info.module_type = module_found_with_type(module_name, pset);
318  auto const table_name = table_for_module_type(info.module_type);
319  auto const& table =
320  pset.get<ParameterSet>(table_name + "." + module_name);
321  if (!is_modifier(info.module_type))
322  continue;
323 
325  auto const consumables = consumables_for_module(mod, process_name);
326  info.consumed_products = consumed_products_for_module(
327  process_name, consumables, produced_products, {}, begin, it);
328  }
329  }
330 
331  void
332  fillObserverInfo(
333  ParameterSet const& pset,
334  string const& process_name,
335  string const& path_name,
336  configs_t const& module_configs,
337  std::map<std::string, std::set<art::ProductInfo>> const& produced_products,
338  collection_map_t& modules)
339  {
340  auto const begin = cbegin(module_configs);
341  for (auto it = begin, end = cend(module_configs); it != end; ++it) {
342  auto const& module_name =
343  it->moduleConfigInfo->modDescription.moduleLabel();
344  auto& info = modules[module_name];
345  info.paths.insert(path_name);
346  info.module_type = module_found_with_type(module_name, pset);
347  auto const table_name = table_for_module_type(info.module_type);
348  auto const& table =
349  pset.get<ParameterSet>(table_name + "." + module_name);
350  if (!is_observer(info.module_type))
351  continue;
352 
354  auto const consumables = consumables_for_module(mod, process_name);
355  info.consumed_products = consumed_products_for_module(
356  process_name, consumables, produced_products, {}, begin, it);
357  std::vector<string> sel;
358  if (mod().select_events(sel)) {
359  info.select_events = std::set<string>(cbegin(sel), cend(sel));
360  }
361  }
362  }
363 }
364 
365 int
366 main(int argc, char** argv) try {
367  if (argc != 2)
368  return 1;
369 
370  string const filename{argv[1]};
372  auto const pset = fhicl::ParameterSet::make(filename, maker);
374  auto const& process_name = table().process_name();
375  auto const& test_properties = table().test_properties();
376 
377  ParameterSet physics;
378  if (!table().physics.get_if_present(physics)) {
379  return 0;
380  }
381 
382  // Form the paths
383  auto module_configs = get_module_configs(pset);
384  auto paths_to_modules = get_paths_to_modules(physics, module_configs);
385  auto const trigger_paths =
386  select_paths(pset, tables_with_modifiers, paths_to_modules);
387  auto end_paths = paths_to_modules;
388 
389  auto const end_path = merge_end_paths(end_paths);
390 
391  // Get modules
393 
394  auto& source_info = modules["input_source"];
395  if (!trigger_paths.empty()) {
396  source_info.paths = path_names(trigger_paths);
397  } else if (!end_path.empty()) {
398  source_info.paths = {"end_path"};
399  }
400 
401  // Assemble all the information for products to be produced.
402  std::map<std::string, std::set<art::ProductInfo>> produced_products{};
403  for (auto const& path : trigger_paths) {
404  fillProducesInfo(pset,
405  process_name,
406  path.first.name,
407  path.second,
408  produced_products,
409  modules);
410  }
411 
412  // Now go through and assemble the rest of the graph info objects,
413  // based on the consumes clauses. The reason this is separate from
414  // the filling of the produces information is that we want to allow
415  // users to specify consumes dependencies at this stage, checking
416  // for correct types, etc. *before* checking if the workflow is
417  // well-formed (i.e. no interpath dependencies, or intrapath
418  // circularities). This pattern mimics what is done in
419  // art::PathManager, where all produces information is filled first,
420  // and then the graph is assembled afterward.
421  std::string err_msg;
422  bool graph_failure{false};
423  try {
424  for (auto const& path : trigger_paths) {
425  fillModifierInfo(pset,
426  process_name,
427  path.first.name,
428  path.second,
429  produced_products,
430  modules);
431  }
432 
433  if (!trigger_paths.empty()) {
434  modules["TriggerResults"] = ModuleGraphInfo{art::ModuleType::producer};
435  }
436 
437  fillObserverInfo(
438  pset, process_name, "end_path", end_path, produced_products, modules);
439  }
440  catch (cet::exception const& e) {
441  err_msg += e.what();
442  graph_failure = true;
443  }
444 
445  // Build the graph only if there was no error in constructing the
446  // information it needs.
447  if (err_msg.empty()) {
448  ModuleGraphInfoMap const modInfos{modules};
449  auto const module_graph =
450  art::detail::make_module_graph(modInfos, trigger_paths, end_path);
451  auto const& err = module_graph.second;
452  if (!err.empty()) {
453  err_msg += err;
454  graph_failure = true;
455  }
456 
457  auto const pos = filename.find(".fcl");
458  string const basename =
459  (pos != string::npos) ? filename.substr(0, pos) : filename;
460  std::ofstream ofs{basename + ".dot"};
461  print_module_graph(ofs, modInfos, module_graph.first);
462  }
463 
464  // Check if test properties have been satisfied
465  int rc{};
466  bool const graph_failure_expected = test_properties.graph_failure_expected();
467  if (graph_failure && !graph_failure_expected) {
468  std::cerr << "Unexpected graph-construction failure.\n"
469  << "Error message:\n"
470  << err_msg << '\n';
471  rc = 1;
472  } else if (!graph_failure && graph_failure_expected) {
473  std::cerr << "Unexpected graph-construction success.\n";
474  rc = 1;
475  }
476  string expected_msg;
477  if (test_properties.error_message(expected_msg)) {
478  std::regex const re{expected_msg};
479  if (!std::regex_search(err_msg, re)) {
480  std::cerr << " The error message does not match what was expected:\n"
481  << " Actual: [" << err_msg << "]\n"
482  << " Expected: [" << expected_msg << "]\n";
483  rc = 3;
484  }
485  }
486  return rc;
487 }
488 catch (detail::validationException const& v) {
489  std::cerr << v.what();
490  return 1;
491 }
492 catch (std::exception const& e) {
493  std::cerr << e.what() << '\n';
494  return 1;
495 }
496 catch (...) {
497  std::cerr << "Job failed.\n";
498  return 1;
499 }
static QCString name
Definition: declinfo.cpp:673
end
while True: pbar.update(maxval-len(onlies[E][S])) #print iS, "/", len(onlies[E][S]) found = False for...
decltype(auto) constexpr cend(T &&obj)
ADL-aware version of std::cend.
Definition: StdUtils.h:87
static QCString result
std::string string
Definition: nybbler.cc:12
static ParameterSet make(intermediate_table const &tbl)
Definition: ParameterSet.cc:68
std::map< module_name_t, ModuleGraphInfo > collection_map_t
ModuleType module_type(std::string const &full_key)
string filename
Definition: train.py:213
int main(int argc, char **argv)
void sort_all(RandCont &)
bool is_key_to_sequence(std::string const &key) const
Definition: ParameterSet.h:171
std::vector< std::string > get_pset_names() const
bool is_key_to_table(std::string const &key) const
Definition: ParameterSet.h:165
const double e
std::set< std::string > name_set_t
std::vector< WorkerInPath::ConfigInfo > configs_t
def move(depos, offset)
Definition: depos.py:107
T get(std::string const &key) const
Definition: ParameterSet.h:271
constexpr exempt_ptr< E > make_exempt_ptr(E *) noexcept
string tmp
Definition: languages.py:63
void print_module_graph(std::ostream &os, ModuleGraphInfoMap const &modInfos, ModuleGraph const &graph)
bool has_key(std::string const &key) const
std::vector< std::pair< PathSpec, configs_t >> paths_to_modules_t
QCString & insert(uint index, const char *s)
Definition: qcstring.cpp:355
auto transform_all(Container &, OutputIt, UnaryOp)
void err(const char *fmt,...)
Definition: message.cpp:226
bool is_modifier(ModuleType const mt)
Definition: ModuleType.h:22
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
Definition: Exception.h:66
constexpr static auto invalid() noexcept
Definition: PathSpec.h:20
std::optional< T > get_if_present(std::string const &key) const
Definition: ParameterSet.h:224
std::vector< std::string > get_names() const
std::set< ProductInfo > consumed_products_for_module(std::string const &current_process, ConsumesInfo::consumables_t::mapped_type const &consumables, std::map< std::string, std::set< ProductInfo >> const &produced_products, std::map< std::string, std::set< std::string >> const &viewable_products, config_const_iterator const config_begin, config_const_iterator const config_it)
decltype(auto) constexpr cbegin(T &&obj)
ADL-aware version of std::cbegin.
Definition: StdUtils.h:82
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
Definition: StdUtils.h:72
std::vector< std::string > names_t
bool is_observer(ModuleType const mt)
Definition: ModuleType.h:28
def maker(G, ac, typename)
Definition: apa.py:280
constexpr ProductStatus present() noexcept
Definition: ProductStatus.h:10
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
ModuleType
Definition: ModuleType.h:11
std::pair< ModuleGraph, std::string > make_module_graph(ModuleGraphInfoMap const &modInfos, paths_to_modules_t const &trigger_paths, configs_t const &end_path)