Belle II Software development
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
19using namespace Belle2;
20
21
22namespace {
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
38void 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
71void 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
106void 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
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.
static DataStore & Instance()
Instance of singleton Store.
Definition: DataStore.cc:54
DependencyMap & getDependencyMap()
Return map of depedencies between modules.
Definition: DataStore.h:524
Collect information about the dependencies between modules.
Definition: DependencyMap.h:29
@ 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
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
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:43
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