LCOV - code coverage report
Current view: top level - cltools - ShowGraph.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 217 218 99.5 %
Date: 2025-04-08 21:11:17 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-2023 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    plumed is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : #include "CLTool.h"
      23             : #include "core/CLToolRegister.h"
      24             : #include "tools/Tools.h"
      25             : #include "config/Config.h"
      26             : #include "core/PlumedMain.h"
      27             : #include "core/ActionSet.h"
      28             : #include "core/ActionRegister.h"
      29             : #include "core/ActionShortcut.h"
      30             : #include "core/ActionToPutData.h"
      31             : #include "core/ActionWithVirtualAtom.h"
      32             : #include "core/ActionWithVector.h"
      33             : #include <cstdio>
      34             : #include <string>
      35             : #include <iostream>
      36             : 
      37             : namespace PLMD {
      38             : namespace cltools {
      39             : 
      40             : //+PLUMEDOC TOOLS show_graph
      41             : /*
      42             : show_graph is a tool that takes a plumed input and generates a flowchart showing how
      43             : data flows through the action set involved.
      44             : 
      45             : For example, if we have the following plumed input:
      46             : 
      47             : ```plumed
      48             : d1: DISTANCE ATOMS=1,2
      49             : a1: ANGLE ATOMS=1,2,3
      50             : t1: TORSION ATOMS=1,2,3,4
      51             : r: RESTRAINT ARG=d1,a1 AT=1.0,pi/2 KAPPA=100,100
      52             : PRINT ARG=d1,a1,t1,r.* FILE=colvar
      53             : ```
      54             : 
      55             : We can use the command:
      56             : 
      57             : ```plumed
      58             : plumed show_graph --plumed plumed.dat
      59             : ```
      60             : 
      61             : To generate the following flowchart showing how data passes through these actions during the PLUMED calculation.
      62             : 
      63             : ```plumed
      64             : #MERMAID=value
      65             : d1: DISTANCE ATOMS=1,2
      66             : a1: ANGLE ATOMS=1,2,3
      67             : t1: TORSION ATOMS=1,2,3,4
      68             : r: RESTRAINT ARG=d1,a1 AT=1.0,pi/2 KAPPA=100,100
      69             : PRINT ARG=d1,a1,t1,r.* FILE=colvar
      70             : ```
      71             : 
      72             : Furthermore, if we want to understand how forces on the atoms are calculated from these actions by using the chain rule
      73             : we can use the following command:
      74             : 
      75             : ```plumed
      76             : plumed show_graph --plumed plumed.dat --force
      77             : ```
      78             : 
      79             : To generate the following flowchart:
      80             : 
      81             : ```plumed
      82             : #MERMAID=force
      83             : d1: DISTANCE ATOMS=1,2
      84             : a1: ANGLE ATOMS=1,2,3
      85             : t1: TORSION ATOMS=1,2,3,4
      86             : r: RESTRAINT ARG=d1,a1 AT=1.0,pi/2 KAPPA=100,100
      87             : PRINT ARG=d1,a1,t1,r.* FILE=colvar
      88             : ```
      89             : 
      90             : These flowcharts are output in a file called `graph.md` unless you use the `--out` option as shown below:
      91             : 
      92             : ```plumed
      93             : plumed show_graph --plumed plumed.dat --out mygraph.md
      94             : ```
      95             : 
      96             : In this case the flowchart is output to a file called `mygraph.md`.  This file contains the instructions for constructing the
      97             : flowchart in [mermaid flowchart syntax](https://mermaid.js.org/syntax/flowchart.html).  To construct images similar to those
      98             : above you can copy and paste the contents of the output `graph.md` file into [this online tool for
      99             : rendering mermaid diagrams](https://mermaid.live).
     100             : 
     101             : If you are writing documentation for PLUMED or tutorials for the [plumed tutorials](www.plumed-tutorials.org) site you can add
     102             : these diagrams by adding the instruction `#MERMAID=value` or `#MERMAID=force` into example inputs.  When these options are given
     103             : inputs are displayed as mermaid diagrams in the final output html.
     104             : 
     105             : */
     106             : //+ENDPLUMEDOC
     107             : 
     108             : class ShowGraph :
     109             :   public CLTool {
     110             : public:
     111             :   static void registerKeywords( Keywords& keys );
     112             :   explicit ShowGraph(const CLToolOptions& co );
     113             :   int main(FILE* in, FILE*out,Communicator& pc);
     114           5 :   std::string description()const {
     115           5 :     return "generate a graph showing how data flows through a PLUMED action set";
     116             :   }
     117             :   std::string getLabel(const Action* a, const bool& amp=false);
     118             :   std::string getLabel(const std::string& s, const bool& amp=false );
     119             :   void printStyle( const unsigned& linkcount, const Value* v, OFile& ofile );
     120             :   void printArgumentConnections( const ActionWithArguments* a, unsigned& linkcount, const bool& force, OFile& ofile );
     121             :   void printAtomConnections( const ActionAtomistic* a, unsigned& linkcount, const bool& force, OFile& ofile );
     122             :   void drawActionWithVectorNode( OFile& ofile, PlumedMain& p, Action* ag, const std::vector<std::string>& mychain, std::vector<bool>& printed );
     123             : };
     124             : 
     125       16267 : PLUMED_REGISTER_CLTOOL(ShowGraph,"show_graph")
     126             : 
     127        5418 : void ShowGraph::registerKeywords( Keywords& keys ) {
     128        5418 :   CLTool::registerKeywords( keys );
     129        5418 :   keys.add("compulsory","--plumed","plumed.dat","the plumed input that we are generating the graph for");
     130        5418 :   keys.add("compulsory","--out","graph.md","the dot file containing the graph that has been generated");
     131        5418 :   keys.addFlag("--force",false,"print a graph that shows how forces are passed through the actions");
     132        5418 : }
     133             : 
     134          13 : ShowGraph::ShowGraph(const CLToolOptions& co ):
     135          13 :   CLTool(co) {
     136          13 :   inputdata=commandline;
     137          13 : }
     138             : 
     139         377 : std::string ShowGraph::getLabel(const Action* a, const bool& amp) {
     140         377 :   return getLabel( a->getLabel(), amp );
     141             : }
     142             : 
     143         453 : std::string ShowGraph::getLabel( const std::string& s, const bool& amp ) {
     144         453 :   if( s.find("@")==std::string::npos ) {
     145         405 :     return s;
     146             :   }
     147          48 :   std::size_t p=s.find_first_of("@");
     148          48 :   if( amp ) {
     149          30 :     return "#64;" + s.substr(p+1);
     150             :   }
     151          33 :   return s.substr(p+1);
     152             : }
     153             : 
     154          85 : void ShowGraph::printStyle( const unsigned& linkcount, const Value* v, OFile& ofile ) {
     155          85 :   if( v->getRank()>0 && v->hasDerivatives() ) {
     156           0 :     ofile.printf("linkStyle %d stroke:green,color:green;\n", linkcount);
     157          85 :   } else if( v->getRank()==1 ) {
     158          33 :     ofile.printf("linkStyle %d stroke:blue,color:blue;\n", linkcount);
     159          52 :   } else if ( v->getRank()==2 ) {
     160          30 :     ofile.printf("linkStyle %d stroke:red,color:red;\n", linkcount);
     161             :   }
     162          85 : }
     163             : 
     164          63 : void ShowGraph::printArgumentConnections( const ActionWithArguments* a, unsigned& linkcount, const bool& force, OFile& ofile ) {
     165          63 :   if( !a ) {
     166             :     return;
     167             :   }
     168         101 :   for(const auto & v : a->getArguments() ) {
     169          55 :     if( force && v->forcesWereAdded() ) {
     170          28 :       ofile.printf("%s -- %s --> %s\n", getLabel(a).c_str(), v->getName().c_str(), getLabel(v->getPntrToAction()).c_str() );
     171          14 :       printStyle( linkcount, v, ofile );
     172          14 :       linkcount++;
     173          41 :     } else if( !force ) {
     174          66 :       ofile.printf("%s -- %s --> %s\n", getLabel(v->getPntrToAction()).c_str(),v->getName().c_str(),getLabel(a).c_str() );
     175          33 :       printStyle( linkcount, v, ofile );
     176          33 :       linkcount++;
     177             :     }
     178             :   }
     179             : }
     180             : 
     181          55 : void ShowGraph::printAtomConnections( const ActionAtomistic* a, unsigned& linkcount, const bool& force, OFile& ofile ) {
     182          55 :   if( !a ) {
     183             :     return;
     184             :   }
     185         179 :   for(const auto & d : a->getDependencies() ) {
     186         138 :     ActionToPutData* dp=dynamic_cast<ActionToPutData*>(d);
     187         138 :     if( dp && dp->getLabel()=="posx" ) {
     188          18 :       if( force && (dp->copyOutput(0))->forcesWereAdded() ) {
     189           8 :         ofile.printf("%s --> MD\n", getLabel(a).c_str() );
     190           8 :         ofile.printf("linkStyle %d stroke:violet,color:violet;\n", linkcount);
     191           8 :         linkcount++;
     192             :       } else {
     193          10 :         ofile.printf("MD --> %s\n", getLabel(a).c_str() );
     194          10 :         ofile.printf("linkStyle %d stroke:violet,color:violet;\n", linkcount);
     195          10 :         linkcount++;
     196             :       }
     197         120 :     } else if( dp && dp->getLabel()!="posy" && dp->getLabel()!="posz" && dp->getLabel()!="Masses" && dp->getLabel()!="Charges" ) {
     198          21 :       if( force && (dp->copyOutput(0))->forcesWereAdded() ) {
     199          18 :         ofile.printf("%s -- %s --> %s\n",getLabel(a).c_str(), getLabel(d).c_str(), getLabel(d).c_str() );
     200           9 :         printStyle( linkcount, dp->copyOutput(0), ofile );
     201           9 :         linkcount++;
     202             :       } else {
     203          24 :         ofile.printf("%s -- %s --> %s\n", getLabel(d).c_str(),getLabel(d).c_str(),getLabel(a).c_str() );
     204          12 :         printStyle( linkcount, dp->copyOutput(0), ofile );
     205          12 :         linkcount++;
     206             :       }
     207          21 :       continue;
     208             :     }
     209         117 :     ActionWithVirtualAtom* dv=dynamic_cast<ActionWithVirtualAtom*>(d);
     210         117 :     if( dv ) {
     211           4 :       if( force && (dv->copyOutput(0))->forcesWereAdded() ) {
     212           2 :         ofile.printf("%s -- %s --> %s\n", getLabel(a).c_str(),getLabel(d).c_str(),getLabel(d).c_str() );
     213           1 :         ofile.printf("linkStyle %d stroke:violet,color:violet;\n", linkcount);
     214           1 :         linkcount++;
     215             :       } else {
     216           6 :         ofile.printf("%s -- %s --> %s\n", getLabel(d).c_str(),getLabel(d).c_str(),getLabel(a).c_str() );
     217           3 :         ofile.printf("linkStyle %d stroke:violet,color:violet;\n", linkcount);
     218           3 :         linkcount++;
     219             :       }
     220             :     }
     221             :   }
     222             : }
     223             : 
     224          30 : void ShowGraph::drawActionWithVectorNode( OFile& ofile, PlumedMain& p, Action* ag, const std::vector<std::string>& mychain, std::vector<bool>& printed ) {
     225          30 :   ActionWithVector* agg=dynamic_cast<ActionWithVector*>(ag);
     226             :   std::vector<std::string> matchain;
     227          30 :   agg->getAllActionLabelsInMatrixChain( matchain );
     228          30 :   if( matchain.size()>0 ) {
     229          16 :     ofile.printf("subgraph sub%s_mat [%s]\n",getLabel(agg).c_str(), getLabel(agg).c_str());
     230          24 :     for(unsigned j=0; j<matchain.size(); ++j ) {
     231          16 :       Action* agm=p.getActionSet().selectWithLabel<Action*>(matchain[j]);
     232          60 :       for(unsigned k=0; k<mychain.size(); ++k ) {
     233          60 :         if( mychain[k]==matchain[j] ) {
     234             :           printed[k]=true;
     235          16 :           break;
     236             :         }
     237             :       }
     238          32 :       ofile.printf("%s([\"label=%s \n %s \n\"])\n", getLabel(matchain[j]).c_str(), getLabel(matchain[j],true).c_str(), agm->writeInGraph().c_str() );
     239             :     }
     240           8 :     ofile.printf("end\n");
     241          16 :     ofile.printf("style sub%s_mat fill:lightblue\n",getLabel(ag).c_str());
     242             :   } else {
     243          44 :     ofile.printf("%s([\"label=%s \n %s \n\"])\n", getLabel(ag->getLabel()).c_str(), getLabel(ag->getLabel(),true).c_str(), ag->writeInGraph().c_str() );
     244             :   }
     245          30 : }
     246             : 
     247           8 : int ShowGraph::main(FILE* in, FILE*out,Communicator& pc) {
     248             : 
     249             :   std::string inpt;
     250          16 :   parse("--plumed",inpt);
     251             :   std::string outp;
     252           8 :   parse("--out",outp);
     253             :   bool forces;
     254           8 :   parseFlag("--force",forces);
     255             : 
     256             :   // Create a plumed main object and initilize
     257           8 :   PlumedMain p;
     258           8 :   int rr=sizeof(double);
     259           8 :   p.cmd("setRealPrecision",&rr);
     260           8 :   double lunit=1.0;
     261           8 :   p.cmd("setMDLengthUnits",&lunit);
     262           8 :   double cunit=1.0;
     263           8 :   p.cmd("setMDChargeUnits",&cunit);
     264           8 :   double munit=1.0;
     265           8 :   p.cmd("setMDMassUnits",&munit);
     266           8 :   p.cmd("setPlumedDat",inpt.c_str());
     267           8 :   p.cmd("setLog",out);
     268           8 :   int natoms=1000000;
     269           8 :   p.cmd("setNatoms",&natoms);
     270           8 :   p.cmd("init");
     271             : 
     272           8 :   unsigned linkcount=0;
     273           8 :   OFile ofile;
     274           8 :   ofile.open(outp);
     275           8 :   if( forces ) {
     276             :     unsigned step=1;
     277           4 :     p.cmd("setStep",step);
     278           4 :     p.cmd("prepareCalc");
     279           4 :     ofile.printf("flowchart BT \n");
     280             :     std::vector<std::string> drawn_nodes;
     281             :     std::set<std::string> atom_force_set;
     282         103 :     for(auto pp=p.getActionSet().rbegin(); pp!=p.getActionSet().rend(); ++pp) {
     283             :       const auto & a(pp->get());
     284         534 :       if( a->getName()=="DOMAIN_DECOMPOSITION" || a->getLabel()=="posx" || a->getLabel()=="posy" || a->getLabel()=="posz" || a->getLabel()=="Masses" || a->getLabel()=="Charges" ) {
     285          24 :         continue;
     286             :       }
     287             : 
     288          75 :       if(a->isActive()) {
     289          44 :         ActionToPutData* ap=dynamic_cast<ActionToPutData*>(a);
     290          44 :         if( ap ) {
     291           8 :           ofile.printf("%s(\"label=%s \n %s \n\")\n", getLabel(a).c_str(), getLabel(a,true).c_str(), a->writeInGraph().c_str() );
     292           4 :           continue;
     293             :         }
     294          40 :         ActionWithValue* av=dynamic_cast<ActionWithValue*>(a);
     295          40 :         if( !av ) {
     296           5 :           continue ;
     297             :         }
     298             :         // Now apply the force if there is one
     299          35 :         a->apply();
     300             :         bool hasforce=false;
     301          67 :         for(int i=0; i<av->getNumberOfComponents(); ++i) {
     302          42 :           if( (av->copyOutput(i))->forcesWereAdded() ) {
     303             :             hasforce=true;
     304             :             break;
     305             :           }
     306             :         }
     307             :         //Check if there are forces here
     308          35 :         ActionWithArguments* aaa=dynamic_cast<ActionWithArguments*>(a);
     309          35 :         if( aaa ) {
     310          46 :           for(const auto & v : aaa->getArguments() ) {
     311          30 :             if( v->forcesWereAdded() ) {
     312             :               hasforce=true;
     313             :               break;
     314             :             }
     315             :           }
     316             :         }
     317          35 :         if( !hasforce ) {
     318          14 :           continue;
     319             :         }
     320          21 :         ActionWithVector* avec=dynamic_cast<ActionWithVector*>(a);
     321          21 :         if( avec ) {
     322           8 :           ActionWithVector* head=avec->getFirstActionInChain();
     323             :           std::vector<std::string> mychain;
     324           8 :           head->getAllActionLabelsInChain( mychain );
     325           8 :           std::vector<bool> printed(mychain.size(),false);
     326          16 :           ofile.printf("subgraph sub%s [%s]\n",getLabel(head).c_str(),getLabel(head).c_str());
     327          70 :           for(unsigned i=0; i<mychain.size(); ++i) {
     328             :             bool drawn=false;
     329         314 :             for(unsigned j=0; j<drawn_nodes.size(); ++j ) {
     330         294 :               if( drawn_nodes[j]==mychain[i] ) {
     331             :                 drawn=true;
     332             :                 break;
     333             :               }
     334             :             }
     335          62 :             if( drawn ) {
     336          42 :               continue;
     337             :             }
     338          20 :             ActionWithVector* ag=p.getActionSet().selectWithLabel<ActionWithVector*>(mychain[i]);
     339          20 :             plumed_assert( ag );
     340          20 :             drawn_nodes.push_back( mychain[i] );
     341          20 :             if( !printed[i] ) {
     342          16 :               drawActionWithVectorNode( ofile, p, ag, mychain, printed );
     343             :               printed[i]=true;
     344             :             }
     345          41 :             for(const auto & v : ag->getArguments() ) {
     346             :               bool chain_conn=false;
     347         109 :               for(unsigned j=0; j<mychain.size(); ++j) {
     348         105 :                 if( (v->getPntrToAction())->getLabel()==mychain[j] ) {
     349             :                   chain_conn=true;
     350             :                   break;
     351             :                 }
     352             :               }
     353          21 :               if( !chain_conn ) {
     354           4 :                 continue;
     355             :               }
     356          34 :               ofile.printf("%s -. %s .-> %s\n", getLabel(v->getPntrToAction()).c_str(),v->getName().c_str(),getLabel(ag).c_str() );
     357          17 :               printStyle( linkcount, v, ofile );
     358          17 :               linkcount++;
     359             :             }
     360             :           }
     361           8 :           ofile.printf("end\n");
     362           8 :           if( avec!=head ) {
     363          70 :             for(unsigned i=0; i<mychain.size(); ++i) {
     364          62 :               ActionWithVector* c = p.getActionSet().selectWithLabel<ActionWithVector*>( mychain[i] );
     365          62 :               plumed_assert(c);
     366          62 :               if( c->getNumberOfAtoms()>0 || c->hasStoredArguments() ) {
     367          60 :                 for(unsigned j=0; j<avec->getNumberOfComponents(); ++j ) {
     368          30 :                   if( avec->copyOutput(j)->getRank()>0 ) {
     369          20 :                     continue;
     370             :                   }
     371          20 :                   ofile.printf("%s == %s ==> %s\n", getLabel(avec).c_str(), avec->copyOutput(j)->getName().c_str(), getLabel(c).c_str() );
     372          10 :                   linkcount++;
     373             :                 }
     374          30 :                 if( c->getNumberOfAtoms()>0 ) {
     375          16 :                   atom_force_set.insert( c->getLabel() );
     376             :                 }
     377             :               }
     378             :             }
     379             :           }
     380           8 :         } else {
     381             :           // Print out the node if we have force on it
     382          26 :           ofile.printf("%s([\"label=%s \n %s \n\"])\n", getLabel(a).c_str(), getLabel(a,true).c_str(), a->writeInGraph().c_str() );
     383             :         }
     384             :         // Check where this force is being added
     385          21 :         printArgumentConnections( aaa, linkcount, true, ofile );
     386             :       }
     387             :     }
     388             :     // Now draw connections from action atomistic to relevant actions
     389           4 :     std::vector<ActionAtomistic*> all_atoms = p.getActionSet().select<ActionAtomistic*>();
     390          33 :     for(const auto & at : all_atoms ) {
     391          29 :       ActionWithValue* av=dynamic_cast<ActionWithValue*>(at);
     392             :       bool hasforce=false;
     393          29 :       if( av ) {
     394          44 :         for(unsigned i=0; i<av->getNumberOfComponents(); ++i ) {
     395          26 :           if( av->copyOutput(i)->forcesWereAdded() ) {
     396           8 :             printAtomConnections( at, linkcount, true, ofile );
     397           8 :             atom_force_set.erase( av->getLabel() );
     398             :             break;
     399             :           }
     400             :         }
     401             :       }
     402             :     }
     403           9 :     for(const auto & l : atom_force_set ) {
     404           5 :       ActionAtomistic* at = p.getActionSet().selectWithLabel<ActionAtomistic*>(l);
     405           5 :       plumed_assert(at);
     406           5 :       printAtomConnections( at, linkcount, true, ofile );
     407             :     }
     408           4 :     ofile.printf("MD(positions from MD)\n");
     409             :     return 0;
     410           4 :   }
     411             : 
     412           4 :   ofile.printf("flowchart TB \n");
     413           4 :   ofile.printf("MD(positions from MD)\n");
     414          98 :   for(const auto & aa : p.getActionSet() ) {
     415             :     Action* a(aa.get());
     416         504 :     if( a->getName()=="DOMAIN_DECOMPOSITION" || a->getLabel()=="posx" || a->getLabel()=="posy" || a->getLabel()=="posz" || a->getLabel()=="Masses" || a->getLabel()=="Charges" ) {
     417          24 :       continue;
     418             :     }
     419          70 :     ActionToPutData* ap=dynamic_cast<ActionToPutData*>(a);
     420          70 :     if( ap ) {
     421           8 :       ofile.printf("%s(\"label=%s \n %s \n\")\n", getLabel(a).c_str(), getLabel(a,true).c_str(), a->writeInGraph().c_str() );
     422           4 :       continue;
     423             :     }
     424          66 :     ActionShortcut* as=dynamic_cast<ActionShortcut*>(a);
     425          66 :     if( as ) {
     426          24 :       continue ;
     427             :     }
     428          42 :     ActionWithValue* av=dynamic_cast<ActionWithValue*>(a);
     429          42 :     ActionWithArguments* aaa=dynamic_cast<ActionWithArguments*>(a);
     430          42 :     ActionAtomistic* at=dynamic_cast<ActionAtomistic*>(a);
     431          42 :     ActionWithVector* avec=dynamic_cast<ActionWithVector*>(a);
     432             :     // Print out the connections between nodes
     433          42 :     printAtomConnections( at, linkcount, false, ofile );
     434          42 :     printArgumentConnections( aaa, linkcount, false, ofile );
     435             :     // Print out the nodes
     436          42 :     if( avec && !avec->actionInChain() ) {
     437           6 :       ofile.printf("subgraph sub%s [%s]\n",getLabel(a).c_str(),getLabel(a).c_str());
     438             :       std::vector<std::string> mychain;
     439           3 :       avec->getAllActionLabelsInChain( mychain );
     440           3 :       std::vector<bool> printed(mychain.size(),false);
     441          21 :       for(unsigned i=0; i<mychain.size(); ++i) {
     442          18 :         Action* ag=p.getActionSet().selectWithLabel<Action*>(mychain[i]);
     443          18 :         if( !printed[i] ) {
     444          14 :           drawActionWithVectorNode( ofile, p, ag, mychain, printed );
     445             :           printed[i]=true;
     446             :         }
     447             :       }
     448           3 :       ofile.printf("end\n");
     449          42 :     } else if( !av ) {
     450          22 :       ofile.printf("%s(\"label=%s \n %s \n\")\n", getLabel(a).c_str(), getLabel(a,true).c_str(), a->writeInGraph().c_str() );
     451          28 :     } else if( !avec ) {
     452          26 :       ofile.printf("%s([\"label=%s \n %s \n\"])\n", getLabel(a).c_str(), getLabel(a,true).c_str(), a->writeInGraph().c_str() );
     453             :     }
     454             :   }
     455           4 :   ofile.close();
     456             : 
     457             :   return 0;
     458           8 : }
     459             : 
     460             : } // End of namespace
     461             : }

Generated by: LCOV version 1.16