Belle II Software  release-08-01-10
DataFlowVisualization.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 //first to avoid _XOPEN_SOURCE warnings
10 #include <framework/core/Module.h>
11 #include <framework/core/Path.h>
12 #include <framework/core/ModuleManager.h>
13 
14 #include <framework/core/DataFlowVisualization.h>
15 #include <framework/datastore/DataStore.h>
16 
17 #include <fstream>
18 
19 using namespace Belle2;
20 
21 
22 namespace {
23  const std::string unknownfillcolor = "gray82";
24 }
25 
27  m_map(dependencyMap)
28 {
29  m_fillcolor[DependencyMap::c_Input] = "cornflowerblue";
32 
33  m_arrowcolor[DependencyMap::c_Input] = "cornflowerblue";
36 }
37 
38 void DataFlowVisualization::visualizePath(const std::string& filename, const Path& path)
39 {
40  std::ofstream file(filename.c_str());
41  file << "digraph allModules {\n";
42  file << " rankdir=LR;\n"; //left -> right
43  file << " compound=true;\n"; //allow edges to subgraphs
44 
45  //for steering file data flow graph, we may get multiple definitions of each node
46  //graphviz merges these into the last one, so we'll go through module list in reverse (all boxes should be coloured as outputs)
47  const bool steeringFileFlow = true;
48  for (const ModulePtr& mod : path.buildModulePathList())
49  generateModulePlot(file, *mod, steeringFileFlow);
50 
51  plotPath(file, path);
52 
53  //add nodes
54  for (const std::string& name : m_allOutputs) {
55  file << " \"" << name << "\" [shape=box,style=filled,fillcolor=" << m_fillcolor[DependencyMap::c_Output] << "];\n";
56  }
57  for (const std::string& name : m_allInputs) {
58  if (m_allOutputs.count(name) == 0)
59  file << " \"" << name << "\" [shape=box,style=filled,fillcolor=" << m_fillcolor[DependencyMap::c_Input] << "];\n";
60  }
61  for (const std::string& name : m_unknownArrays) {
62  if (m_allOutputs.count(name) == 0 && m_allInputs.count(name) == 0)
63  file << " \"" << name << "\" [shape=box,style=filled,fillcolor=" << unknownfillcolor << "];\n";
64  }
65 
66  file << "}\n\n";
67 
68  B2INFO("Data flow diagram created. You can use 'dot dataflow.dot -Tps -o dataflow.ps' to create a PostScript file from it.");
69 }
70 
71 void DataFlowVisualization::plotPath(std::ofstream& file, const Path& path, const std::string& pathName)
72 {
73  //graph name must begin with cluster for fancy graphics!
74  const std::string graphname = pathName.empty() ? "clusterMain" : ("cluster" + pathName);
75  file << " subgraph \"" << graphname << "\" {\n";
76  if (pathName.empty()) {
77  file << " rank=min;\n";
78  } else {
79  file << " rank=same;\n";
80  }
81  file << " style=solid;\n";
82  file << " color=grey;\n";
83  file << " \"" << graphname << "_inv\" [shape=point,style=invis];\n";
84  std::string lastModule("");
85  //connect modules in right order...
86  for (const ModulePtr& mod : path.getModules()) {
87  const std::string& module = DependencyMap::getModuleID(*mod);
88  file << " \"" << module << "\";\n";
89  if (!lastModule.empty()) {
90  file << " \"" << lastModule << "\" -> \"" << module << "\" [color=black];\n";
91  }
92  if (mod->hasCondition()) {
93  for (const auto& condition : mod->getAllConditions()) {
94  const std::string& conditionName = condition.getString();
95  plotPath(file, *condition.getPath(), conditionName);
96  file << " \"" << module << "\" -> \"cluster" << conditionName << "_inv\" " <<
97  "[color=grey,lhead=\"cluster" << conditionName << "\",label=\"" << conditionName << "\",fontcolor=grey];\n";
98  }
99  }
100 
101  lastModule = module;
102  }
103  file << " }\n";
104 }
105 
106 void DataFlowVisualization::generateModulePlot(std::ofstream& file, const Module& mod, bool steeringFileFlow)
107 {
108  const std::string& name = DependencyMap::getModuleID(mod);
109  const std::string& label = mod.getName();
110  if (!steeringFileFlow)
111  file << "digraph \"" << name << "\" {\n";
112  file << " " << name << " [label=\"" << label << "\"];\n";
113 
114  const auto foundInfoIter = m_map->getModuleInfoMap().find(name);
115  if (foundInfoIter != m_map->getModuleInfoMap().end()) {
116  const DependencyMap::ModuleInfo& moduleInfo = foundInfoIter->second;
117  for (int i = 0; i < DependencyMap::c_NEntryTypes; i++) {
118  const std::set<std::string>& entries = moduleInfo.entries[i];
119  const std::set<std::string>& relations = moduleInfo.relations[i];
120  const std::string fillcolor = m_fillcolor[i];
121  const std::string arrowcolor = m_arrowcolor[i];
122 
123  for (const std::string& dsentry : entries) {
124  if (!steeringFileFlow)
125  file << " \"" << dsentry << "\" [shape=box,style=filled,fillcolor=" << fillcolor << "];\n";
126  if (i == DependencyMap::c_Output) {
127  m_allOutputs.insert(dsentry);
128  file << " \"" << name << "\" -> \"" << dsentry << "\" [color=" << arrowcolor << "];\n";
129  } else {
130  m_allInputs.insert(dsentry);
131  file << " \"" << dsentry << "\" -> \"" << name << "\" [color=" << arrowcolor << "];\n";
132  }
133  }
134 
135  for (const std::string& relname : relations) {
136  size_t pos = relname.rfind("To");
137  if (pos == std::string::npos or pos != relname.find("To")) {
138  B2WARNING("generateModulePlot(): couldn't split relation name!");
139  //Searching for parts in input/output lists might be helpful...
140  continue;
141  }
142 
143  const std::string from = relname.substr(0, pos);
144  const std::string to = relname.substr(pos + 2);
145 
146  //any connected arrays that are neither input nor output?
147  if (checkArrayUnknown(from, moduleInfo)) {
148  if (!steeringFileFlow)
149  file << " \"" << from << "\" [shape=box,style=filled,fillcolor=" << unknownfillcolor << "];\n";
150  }
151  if (checkArrayUnknown(to, moduleInfo)) {
152  if (!steeringFileFlow)
153  file << " \"" << to << "\" [shape=box,style=filled,fillcolor=" << unknownfillcolor << "];\n";
154  }
155 
156  file << " \"" << from << "\" -> \"" << to << "\" [color=" << arrowcolor << ",style=dashed];\n";
157  }
158  }
159  }
160  if (!steeringFileFlow)
161  file << "}\n\n";
162 }
163 
164 
166 {
167  // construct given module and gearbox
168  std::shared_ptr<Module> modulePtr = ModuleManager::Instance().registerModule(module);
169  std::shared_ptr<Module> gearboxPtr = ModuleManager::Instance().registerModule("Gearbox");
170 
171  // call initialize() method
172  //may throw some ERRORs, but that's OK.
173  // TODO:(ignore missing inputs)
174  gearboxPtr->initialize();
176  modulePtr->initialize();
177 
178  // create plot
179  const std::string filename = module + ".dot";
180  DataFlowVisualization v(&DataStore::Instance().getDependencyMap());
181  std::ofstream file(filename.c_str());
182  v.generateModulePlot(file, *modulePtr, false);
183 
184  //clean up to avoid problems with ~TROOT
185  modulePtr->terminate();
186  gearboxPtr->terminate();
187 }
188 
189 bool DataFlowVisualization::checkArrayUnknown(const std::string& name, const DependencyMap::ModuleInfo& info)
190 {
191  for (const auto& entry : info.entries) {
192  if (entry.count(name) != 0)
193  return false; //found
194  }
195 
196  //not found
197  m_unknownArrays.insert(name);
198  return true;
199 }
class to visualize data flow between modules.
DataFlowVisualization(const DependencyMap *dependencyMap)
Constructor.
std::set< std::string > m_allInputs
set of all inputs (including optionals), for steering file visualisation.
static void executeModuleAndCreateIOPlot(const std::string &module)
Create independent I/O graph for a single module (without requiring a steering file).
bool checkArrayUnknown(const std::string &name, const DependencyMap::ModuleInfo &info)
If the given array name isn't found in any of info's fields, it is added to m_unknownArrays (and true...
std::set< std::string > m_allOutputs
set of all outputs, for steering file visualisation.
static void plotPath(std::ofstream &file, const Path &path, const std::string &pathName="")
Create a subgraph for the given Path (including conditional paths).
void generateModulePlot(std::ofstream &file, const Module &mod, bool steeringFileFlow=false)
Create I/O graph for a single module (written to file).
std::string m_arrowcolor[DependencyMap::c_NEntryTypes]
arrow colors.
void visualizePath(const std::string &filename, const Path &path)
Create graphs with datastore inputs/outputs of each module in path.
std::string m_fillcolor[DependencyMap::c_NEntryTypes]
fill colors.
const DependencyMap * m_map
Stores information on inputs/outputs of each module, as obtained by require()/createEntry();.
std::set< std::string > m_unknownArrays
set of array only being used in relations, for steering file visualisation.
DependencyMap & getDependencyMap()
Return map of depedencies between modules.
Definition: DataStore.h:524
static DataStore & Instance()
Instance of singleton Store.
Definition: DataStore.cc:54
Collect information about the dependencies between modules.
Definition: DependencyMap.h:29
const std::map< std::string, ModuleInfo > & getModuleInfoMap() const
return information on inputs/outputs of each module, as obtained by requireInput()/optionalInput()/re...
Definition: DependencyMap.h:66
@ c_Input
required input.
Definition: DependencyMap.h:33
@ c_NEntryTypes
size of this enum.
Definition: DependencyMap.h:37
@ c_Output
registered output.
Definition: DependencyMap.h:35
@ c_OptionalInput
optional input.
Definition: DependencyMap.h:34
void setModule(const Module &mod)
Set the current module (for getCurrentModuleInfo())
Definition: DependencyMap.h:60
static std::string getModuleID(const Module &mod)
Return unique ID for given module.
std::shared_ptr< Module > registerModule(const std::string &moduleName, std::string sharedLibPath="") noexcept(false)
Creates an instance of a module and registers it to the ModuleManager.
static ModuleManager & Instance()
Exception is thrown if the requested module could not be created by the ModuleManager.
Base class for Modules.
Definition: Module.h:72
Implements a path consisting of Module and/or Path objects.
Definition: Path.h:38
std::shared_ptr< Module > ModulePtr
Defines a pointer to a module object as a boost shared pointer.
Definition: Module.h:40
Abstract base class for different kinds of events.
Stores information on inputs/outputs of a module, as obtained by requireInput()/optionalInput()/regis...
Definition: DependencyMap.h:41
std::set< std::string > entries[c_NEntryTypes]
objects/arrays.
Definition: DependencyMap.h:42
std::set< std::string > relations[c_NEntryTypes]
relations between them.
Definition: DependencyMap.h:43