LCOV - code coverage report
Current view: top level - cltools - GenExample.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 17 306 5.6 %
Date: 2026-03-30 13:16:06 Functions: 6 9 66.7 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2019-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 "CLToolRegister.h"
      24             : #include "tools/Tools.h"
      25             : #include "config/Config.h"
      26             : #include "core/ActionRegister.h"
      27             : #include "core/ActionWithValue.h"
      28             : #include "core/ActionWithVirtualAtom.h"
      29             : #include "core/ActionShortcut.h"
      30             : #include "core/ActionSet.h"
      31             : #include "core/PlumedMain.h"
      32             : #include "tools/IFile.h"
      33             : #include <cstdio>
      34             : #include <string>
      35             : #include <vector>
      36             : #include <iostream>
      37             : #include <fstream>
      38             : 
      39             : namespace PLMD {
      40             : namespace cltools {
      41             : 
      42             : //+PLUMEDOC TOOLS gen_example
      43             : /*
      44             : gen_example is a tool that you can use to construct an example for the manual that users can interact with to understand
      45             : 
      46             : The example constructed by this action is in html. In all probability you will never need to use this
      47             : tool. However, it is used within the scripts that generate the html manual for PLUMED.  If you need to use this
      48             : tool outside those scripts the input is specified using the following command line arguments.
      49             : 
      50             : \par Examples
      51             : 
      52             : The following generates an example based on the contents of the plumed file plumed.dat
      53             : \verbatim
      54             : plumed gen_example --plumed plumed.dat --status working
      55             : \endverbatim
      56             : 
      57             : 
      58             : */
      59             : //+ENDPLUMEDOC
      60             : 
      61             : class GenExample:
      62             :   public CLTool {
      63             : private:
      64             :   int multi;
      65             :   std::string status, version;
      66             :   Communicator intracomm;
      67             :   Communicator intercomm;
      68             : public:
      69             :   static void registerKeywords( Keywords& keys );
      70             :   explicit GenExample(const CLToolOptions& co );
      71             :   int main(FILE* in, FILE*out,Communicator& pc) override;
      72           4 :   std::string description()const override {
      73           4 :     return "construct an example for the manual that users can interact with";
      74             :   }
      75             :   void printExampleInput( const std::vector<std::vector<std::string> >& input, const std::string& egname, const std::string& divname, std::ofstream& ofile );
      76             :   std::vector<std::vector<std::string> > createLongInput( const std::vector<std::vector<std::string> >& input );
      77             : };
      78             : 
      79       13789 : PLUMED_REGISTER_CLTOOL(GenExample,"gen_example")
      80             : 
      81        4595 : void GenExample::registerKeywords( Keywords& keys ) {
      82        4595 :   CLTool::registerKeywords( keys );
      83        9190 :   keys.add("compulsory","--plumed","plumed.dat","convert the input in this file to the html manual");
      84        9190 :   keys.add("compulsory","--out","example.html","the file on which to output the example in html");
      85        9190 :   keys.add("compulsory","--name","ppp","the name to use for this particular input");
      86        9190 :   keys.add("compulsory","--status","nobadge","whether or not the input file works");
      87        9190 :   keys.add("compulsory","--multi","0","set number of replicas for multi environment (needs MPI)");
      88        4595 : }
      89             : 
      90           4 : GenExample::GenExample(const CLToolOptions& co ):
      91             :   CLTool(co),
      92           4 :   multi(0),
      93           8 :   status("nobadge"),
      94           4 :   version("master") {
      95           4 :   inputdata=commandline;
      96           4 : }
      97             : 
      98           0 : int GenExample::main(FILE* in, FILE*out,Communicator& pc) {
      99             : 
     100             : // set up for multi replica driver:
     101           0 :   parse("--multi",multi);
     102           0 :   if(multi) {
     103           0 :     int ntot=pc.Get_size();
     104           0 :     int nintra=ntot/multi;
     105           0 :     if(multi*nintra!=ntot) {
     106           0 :       error("invalid number of processes for multi environment");
     107             :     }
     108           0 :     pc.Split(pc.Get_rank()/nintra,pc.Get_rank(),intracomm);
     109           0 :     pc.Split(pc.Get_rank()%nintra,pc.Get_rank(),intercomm);
     110             :   } else {
     111           0 :     intracomm.Set_comm(pc.Get_comm());
     112             :   }
     113             : 
     114           0 :   if( config::getVersionLong().find("dev")==std::string::npos ) {
     115           0 :     version="v"+config::getVersion();
     116             :   }
     117             :   std::string fname, egname, outfile;
     118           0 :   parse("--plumed",fname);
     119           0 :   parse("--name",egname);
     120           0 :   parse("--out",outfile);
     121           0 :   parse("--status",status);
     122             : 
     123           0 :   int r=0;
     124           0 :   if(intracomm.Get_rank()==0) {
     125           0 :     r=intercomm.Get_rank();
     126             :   }
     127           0 :   intracomm.Bcast(r,0);
     128           0 :   if(r>0) {
     129             :     outfile="/dev/null";
     130             :   }
     131             : 
     132           0 :   IFile ifile;
     133           0 :   ifile.open(fname);
     134           0 :   ifile.allowNoEOL();
     135           0 :   std::ofstream ofile;
     136           0 :   ofile.open(outfile);
     137             :   std::vector<bool> shortcuts;
     138             :   bool hasshortcuts=false, endplumed=false;
     139             :   std::vector<std::vector<std::string> > input;
     140             :   std::vector<std::string> words;
     141           0 :   while( Tools::getParsedLine(ifile, words, false) ) {
     142           0 :     input.push_back( words );
     143           0 :     shortcuts.push_back( false );
     144           0 :     if( words.empty() || words[0].find("#")!=std::string::npos || endplumed ) {
     145           0 :       continue;
     146             :     }
     147           0 :     std::vector<std::string> interpreted( words );
     148           0 :     Tools::interpretLabel(interpreted);
     149           0 :     if( interpreted[0]=="ENDPLUMED" ) {
     150             :       endplumed=true;
     151             :       continue;
     152             :     }
     153           0 :     Keywords keys;
     154           0 :     actionRegister().getKeywords( interpreted[0], keys );
     155           0 :     if( status=="working" && keys.exists("IS_SHORTCUT") ) {
     156           0 :       hasshortcuts=shortcuts[shortcuts.size()-1]=true;
     157             :     }
     158           0 :   }
     159           0 :   ifile.close();
     160           0 :   if( hasshortcuts ) {
     161           0 :     ofile<<"<div style=\"width: 80%; float:left\" id=\"value_details_"<<egname<<"\"> Click on the labels of the actions for more information on what each action computes </div>\n";
     162           0 :     ofile<<"<div style=\"width: 10%; float:left\"><button type=\"button\" id=\""<<egname<<"_button\" onclick=\'swapInput(\""<<egname<<"\")\'>contract shortcuts</button></div>";
     163             :   } else {
     164           0 :     ofile<<"<div style=\"width: 90%; float:left\" id=\"value_details_"<<egname<<"\"> Click on the labels of the actions for more information on what each action computes </div>\n";
     165             :   }
     166           0 :   ofile<<"<div style=\"width: 10%; float:left\">";
     167           0 :   ofile<<"<img src=\"https://img.shields.io/badge/";
     168           0 :   if(status=="working") {
     169           0 :     ofile<<version<<"-passing-green";
     170           0 :   } else if(status=="broken") {
     171           0 :     ofile<<version<<"-failed-red";
     172           0 :   } else if(status=="loads") {
     173           0 :     ofile<<"with-LOAD-yellow";
     174           0 :   } else if(status=="incomplete") {
     175           0 :     ofile<<version<<"-incomplete-yellow";
     176             :   } else {
     177           0 :     error("unknown status");
     178             :   }
     179           0 :   ofile<<".svg\" alt=\"tested on "<<version<<"\" /></div>";
     180           0 :   ofile.flush();
     181           0 :   if( hasshortcuts ) {
     182             :     // Write out the short version of the input
     183           0 :     ofile<<"<div style=\"width: 100%; float:left\" id=\"input_"<<egname<<"\"></div>"<<std::endl;
     184             :     // Write an extra pre to make sure the html after the example is put in the right place on the page
     185           0 :     ofile<<"<pre style=\"width: 97%;\" class=\"fragment\"></pre>"<<std::endl;
     186           0 :     ofile<<"<script type=\"text/javascript\">"<<std::endl;
     187           0 :     ofile<<"if (window.addEventListener) { // Mozilla, Netscape, Firefox"<<std::endl;
     188           0 :     ofile<<"    window.addEventListener('load', "<<egname<<"Load, false);"<<std::endl;
     189           0 :     ofile<<"} else if (window.attachEvent) { // IE"<<std::endl;
     190           0 :     ofile<<"    window.attachEvent('onload', "<<egname<<"Load);"<<std::endl;
     191           0 :     ofile<<"}"<<std::endl;
     192           0 :     ofile<<"function "<<egname<<"Load(event) {"<<std::endl;
     193           0 :     ofile<<"       swapInput(\""<<egname<<"\");"<<std::endl;
     194           0 :     ofile<<"}"<<std::endl;
     195           0 :     ofile<<"</script>"<<std::endl;
     196           0 :     ofile<<"<div style=\"display:none;\" id=\""<<egname<<"short\">"<<std::endl;
     197           0 :     printExampleInput( input, egname + "short", egname, ofile );
     198           0 :     ofile<<"</div>"<<std::endl;
     199             :     // Write out long version of the input
     200           0 :     ofile<<"<div style=\"display:none;\" id=\""<<egname<<"long\">";
     201           0 :     std::vector<std::vector<std::string> > long_input = createLongInput( input );
     202           0 :     printExampleInput( long_input, egname + "long", egname, ofile );
     203           0 :     ofile<<"</div>"<<std::endl;
     204           0 :   } else {
     205           0 :     printExampleInput( input, egname, egname, ofile );
     206             :   }
     207           0 :   ofile.close();
     208           0 :   return 0;
     209           0 : }
     210             : 
     211           0 : std::vector<std::vector<std::string> > GenExample::createLongInput( const std::vector<std::vector<std::string> >& input ) {
     212             :   std::vector<std::vector<std::string> > long_input;
     213           0 :   PlumedMain myplumed;
     214           0 :   int rr=sizeof(double), natoms=10000000;
     215           0 :   double kt=2.49;
     216           0 :   myplumed.cmd("setRealPrecision",&rr);
     217           0 :   if(Communicator::initialized()) {
     218           0 :     if(multi) {
     219           0 :       if(intracomm.Get_rank()==0) {
     220           0 :         myplumed.cmd("GREX setMPIIntercomm",&intercomm.Get_comm());
     221             :       }
     222           0 :       myplumed.cmd("GREX setMPIIntracomm",&intracomm.Get_comm());
     223           0 :       myplumed.cmd("GREX init");
     224             :     }
     225           0 :     myplumed.cmd("setMPIComm",&intracomm.Get_comm());
     226             :   }
     227             :   bool endplumed=false;
     228           0 :   myplumed.cmd("setNatoms",&natoms);
     229           0 :   myplumed.cmd("setKbT",&kt);
     230           0 :   myplumed.cmd("init");
     231           0 :   for(unsigned ll=0; ll<input.size(); ++ll) {
     232           0 :     if( input[ll].empty() || endplumed ) {
     233           0 :       long_input.push_back( input[ll] );
     234           0 :       continue;
     235             :     }
     236           0 :     if( input[ll][0].find("#")!=std::string::npos ) {
     237           0 :       long_input.push_back( input[ll] );
     238           0 :       continue;
     239             :     }
     240           0 :     std::vector<std::string> interpreted( input[ll] );
     241           0 :     Tools::interpretLabel(interpreted);
     242           0 :     if( interpreted[0]=="ENDPLUMED" ) {
     243             :       endplumed=true;
     244           0 :       long_input.push_back( input[ll] );
     245             :       continue;
     246             :     }
     247           0 :     Keywords keys;
     248           0 :     plumed_assert( actionRegister().check( interpreted[0] ) );
     249           0 :     actionRegister().getKeywords( interpreted[0], keys );
     250             :     std::string lab, myinputline;
     251           0 :     if( Tools::parse(interpreted, "LABEL", lab ) ) {
     252           0 :       myinputline = lab + ": ";
     253             :     }
     254           0 :     myinputline += interpreted[0] + " ";
     255             :     bool trailingcomment=false;
     256           0 :     for(unsigned i=1; i<interpreted.size(); ++i) {
     257           0 :       if( trailingcomment && interpreted[i]=="@newline") {
     258             :         trailingcomment=false;
     259           0 :         continue;
     260             :       }
     261           0 :       if( interpreted[i].find("#")!=std::string::npos ) {
     262             :         trailingcomment=true;
     263           0 :         continue;
     264             :       }
     265           0 :       if( interpreted[i]=="@newline" || interpreted[i]=="..." ) {
     266           0 :         continue;
     267             :       }
     268             :       std::size_t pos = 0;
     269           0 :       while ((pos = interpreted[i].find("@newline",pos)) != std::string::npos) {
     270           0 :         interpreted[i].replace(pos, 8, "\n");
     271           0 :         pos++;
     272             :       }
     273           0 :       myinputline += interpreted[i] + " ";
     274             :     }
     275           0 :     if( status=="working" && keys.exists("IS_SHORTCUT") ) {
     276           0 :       myplumed.readInputLine( myinputline );
     277           0 :       ActionShortcut* as=dynamic_cast<ActionShortcut*>( myplumed.getActionSet()[myplumed.getActionSet().size()-1].get() );
     278           0 :       plumed_assert( as );
     279           0 :       std::vector<std::string> shortcut_commands = as->getSavedInputLines();
     280           0 :       for(unsigned i=0; i<shortcut_commands.size(); ++i) {
     281           0 :         std::vector<std::string> words = Tools::getWords( shortcut_commands[i] );
     282           0 :         long_input.push_back( words );
     283           0 :       }
     284           0 :     } else {
     285           0 :       long_input.push_back( input[ll] );
     286           0 :       myplumed.readInputLine( myinputline );
     287             :     }
     288           0 :   }
     289           0 :   return long_input;
     290           0 : }
     291             : 
     292           0 : void GenExample::printExampleInput( const std::vector<std::vector<std::string> >& input, const std::string& egname, const std::string& divname, std::ofstream& ofile ) {
     293           0 :   PlumedMain myplumed;
     294           0 :   int rr=sizeof(double), natoms=10000000;
     295           0 :   double kt=2.49;
     296           0 :   myplumed.cmd("setRealPrecision",&rr);
     297           0 :   if(Communicator::initialized()) {
     298           0 :     if(multi) {
     299           0 :       if(intracomm.Get_rank()==0) {
     300           0 :         myplumed.cmd("GREX setMPIIntercomm",&intercomm.Get_comm());
     301             :       }
     302           0 :       myplumed.cmd("GREX setMPIIntracomm",&intracomm.Get_comm());
     303           0 :       myplumed.cmd("GREX init");
     304             :     }
     305           0 :     myplumed.cmd("setMPIComm",&intracomm.Get_comm());
     306             :   }
     307           0 :   myplumed.cmd("setNatoms",&natoms);
     308           0 :   myplumed.cmd("setKbT",&kt);
     309           0 :   myplumed.cmd("init");
     310             :   std::vector<std::string> labellist;
     311             :   bool endplumed=false;
     312           0 :   ofile<<"<pre style=\"width: 97%;\" class=\"fragment\">"<<std::endl;
     313           0 :   for(unsigned ll=0; ll<input.size(); ++ll) {
     314           0 :     if( input[ll].empty() ) {
     315             :       ofile<<std::endl;
     316           0 :       continue;
     317             :     }
     318           0 :     if( input[ll][0].find("#")!=std::string::npos || endplumed ) {
     319           0 :       ofile<<"<span style=\"color:blue\">"<<input[ll][0];
     320           0 :       for(unsigned i=1; i<input[ll].size(); ++i) {
     321           0 :         ofile<<" "<<input[ll][i];
     322             :       }
     323           0 :       ofile<<"</span>"<<std::endl;;
     324             :     } else {
     325             :       // Interpret the label if this needs to be done
     326           0 :       std::vector<std::string> interpreted( input[ll] );
     327           0 :       Tools::interpretLabel(interpreted);
     328             :       std::string lab, myinputline;
     329             :       // Now read in the label
     330           0 :       if( Tools::parse(interpreted,"LABEL",lab) ) {
     331           0 :         ofile<<"<b name=\""<<egname<<lab<<"\" onclick=\'showPath(\""<<divname<<"\",\""<<egname<<lab<<"\")\'>"<<lab<<": </b>";
     332           0 :         labellist.push_back(lab);
     333           0 :         myinputline = lab + ": ";
     334             :       }
     335             :       // Print the keyword in use in the action
     336           0 :       std::string action = interpreted[0];
     337           0 :       myinputline += interpreted[0] + " ";
     338           0 :       if( action=="ENDPLUMED" ) {
     339             :         endplumed=true;
     340             :       }
     341           0 :       Keywords keys;
     342           0 :       actionRegister().getKeywords( interpreted[0], keys );
     343             :       // Handle conversion of action names to links
     344           0 :       std::transform(action.begin(), action.end(), action.begin(), [](unsigned char c) {
     345           0 :         return std::tolower(c);
     346             :       });
     347           0 :       ofile<<"<a href=\"https://www.plumed.org/doc-"<<version<<"/user-doc/html/";
     348             :       for(unsigned n=0;; ++n) {
     349           0 :         std::size_t und=action.find_first_of("_");
     350           0 :         if( und==std::string::npos ) {
     351             :           break;
     352             :         }
     353           0 :         std::string first=action.substr(0,und);
     354           0 :         for(auto c : first ) {
     355           0 :           if( isdigit(c) ) {
     356           0 :             ofile<<c;
     357             :           } else {
     358           0 :             ofile<<"_"<<c;
     359             :           }
     360             :         }
     361           0 :         ofile<<"_";
     362           0 :         action=action.substr(und+1);
     363           0 :       }
     364           0 :       for(auto c : action ) {
     365           0 :         if( isdigit(c) ) {
     366           0 :           ofile<<c;
     367             :         } else {
     368           0 :           ofile<<"_"<<c;
     369             :         }
     370             :       }
     371           0 :       ofile<<".html\" style=\"color:green\">"<<interpreted[0]<<"</a> ";
     372             :       // And write out everything else in the input line
     373             :       bool trailingcomment=false;
     374           0 :       for(unsigned i=1; i<interpreted.size(); ++i) {
     375           0 :         if( interpreted[i]=="@newline" && i==1 ) {
     376           0 :           ofile<<"..."<<std::endl<<"   ";
     377           0 :           continue;
     378           0 :         } else if( interpreted[i]=="@newline" ) {
     379           0 :           if( trailingcomment ) {
     380           0 :             ofile<<"</span>";
     381             :             trailingcomment=false;
     382             :           }
     383           0 :           if( interpreted[i+1]=="..." ) {
     384             :             ofile<<std::endl;
     385             :           } else {
     386           0 :             ofile<<std::endl<<"   ";
     387             :           }
     388           0 :           continue;
     389           0 :         } else if( interpreted[i]=="__FILL__" ) {
     390           0 :           if( status!="incomplete" ) {
     391           0 :             error("found __FILL__ statement but status is " + status);
     392             :           }
     393           0 :           ofile<<"<span style=\"background-color:yellow\">__FILL__</span>";
     394           0 :           continue;
     395           0 :         } else if( interpreted[i]==action ) {
     396           0 :           continue;
     397             :         }
     398           0 :         if( interpreted[i].find("#")!=std::string::npos ) {
     399             :           trailingcomment=true;
     400           0 :           ofile<<"<span style=\"color:blue\">";
     401             :         }
     402             : 
     403           0 :         if( !trailingcomment ) {
     404           0 :           std::size_t eq=interpreted[i].find_first_of("=");
     405           0 :           if( eq!=std::string::npos ) {
     406           0 :             std::string keyword=interpreted[i].substr(0,eq), rest=interpreted[i].substr(eq+1);
     407           0 :             ofile<<"<span class=\"tooltip\">"<<keyword<<"<span class=\"right\">"<<keys.getTooltip(keyword)<<"<i></i></span></span>";
     408           0 :             if( rest=="__FILL__" ) {
     409           0 :               if( status!="incomplete" ) {
     410           0 :                 error("found __FILL__ statement but status is " + status);
     411             :               }
     412           0 :               ofile<<"=<span style=\"background-color:yellow\">__FILL__</span>";
     413           0 :             } else if( rest.find_first_of("{")!=std::string::npos ) {
     414             :               std::size_t pos = 0;
     415           0 :               while ((pos = rest.find("@newline",pos)) != std::string::npos) {
     416           0 :                 rest.replace(pos, 8, "\n");
     417           0 :                 pos++;
     418             :               }
     419           0 :               ofile<<"="<<rest<<" ";
     420           0 :               myinputline += keyword + "=" + rest + " ";
     421             :             } else {
     422           0 :               std::vector<std::string> args=Tools::getWords(rest,"\t\n ,");
     423           0 :               ofile<<"=";
     424           0 :               for(unsigned i=0; i<args.size(); ++i) {
     425             :                 bool islabel=false;
     426             :                 std::string thislab;
     427           0 :                 for(unsigned j=0; j<labellist.size(); ++j) {
     428           0 :                   std::size_t dot=args[i].find_first_of(".");
     429           0 :                   std::string lll=args[i].substr(0,dot);
     430           0 :                   if( lll==labellist[j] ) {
     431             :                     islabel=true;
     432             :                     thislab=labellist[j];
     433             :                     break;
     434             :                   }
     435             :                 }
     436             :                 if( islabel ) {
     437           0 :                   ofile<<"<b name=\""<<egname<<thislab<<"\">"<<args[i]<<"</b>";
     438             :                 } else {
     439             :                   ofile<<args[i];
     440             :                 }
     441           0 :                 if( i!=args.size()-1 ) {
     442           0 :                   ofile<<",";
     443             :                 }
     444             :               }
     445           0 :               myinputline += interpreted[i] + " ";
     446           0 :             }
     447           0 :             ofile<<" ";
     448           0 :           } else if( interpreted[i]!="@newline" && interpreted[i]!="..." ) {
     449           0 :             myinputline += interpreted[i] + " ";
     450           0 :             ofile<<"<span class=\"tooltip\">"<<interpreted[i]<<"<span class=\"right\">"<<keys.getTooltip(interpreted[i])<<"<i></i></span></span> ";
     451           0 :           } else if( interpreted[i]=="..." ) {
     452           0 :             ofile<<"...";
     453             :           }
     454             :         } else {
     455           0 :           ofile<<interpreted[i]<<" ";
     456             :         }
     457             :       }
     458           0 :       if( trailingcomment ) {
     459           0 :         ofile<<"</span>";
     460             :       }
     461             :       // This builds the hidden content that tells the user about what is calculated
     462           0 :       if( status=="working" ) {
     463           0 :         ofile<<"<span style=\"display:none;\" id=\""<<egname<<lab<<"\">";
     464           0 :         ofile<<"The "<<interpreted[0]<<" action with label <b>"<<lab<<"</b>";
     465           0 :         myplumed.readInputLine( myinputline );
     466           0 :         ActionWithValue* av=dynamic_cast<ActionWithValue*>( myplumed.getActionSet().selectWithLabel<Action*>(lab) );
     467           0 :         if( av ) {
     468           0 :           if( av->getNumberOfComponents()==1 ) {
     469           0 :             ofile<<" calculates a single scalar value";
     470           0 :           } else if( av->getNumberOfComponents()>0 ) {
     471           0 :             ofile<<" calculates the following quantities:"<<std::endl;
     472           0 :             ofile<<"<table  align=\"center\" frame=\"void\" width=\"95%%\" cellpadding=\"5%%\">"<<std::endl;
     473           0 :             ofile<<"<tr><td width=\"5%%\"><b> Quantity </b>  </td><td><b> Description </b> </td></tr>"<<std::endl;
     474           0 :             unsigned ncomp = av->getNumberOfComponents();
     475           0 :             for(unsigned k=0; k<ncomp; ++k ) {
     476           0 :               std::string myname = av->copyOutput(k)->getName();
     477           0 :               std::size_t dot=myname.find_first_of(".");
     478           0 :               std::string tname=myname.substr(dot+1);
     479           0 :               std::size_t und=tname.find_last_of("_");
     480           0 :               std::size_t hyph=tname.find_first_of("-");
     481           0 :               if( und!=std::string::npos && hyph!=std::string::npos ) {
     482           0 :                 plumed_merror("cannot use underscore and hyphen in name");
     483             :               }
     484           0 :               ofile<<"<tr><td width=\"5%%\">"<<myname<<"</td><td>";
     485           0 :               if( und!=std::string::npos ) {
     486           0 :                 ofile<<keys.getOutputComponentDescription(tname.substr(und))<<" This particular component measures this quantity for the input CV named ";
     487           0 :                 ofile<<tname.substr(0,und);
     488           0 :               } else if( hyph!=std::string::npos ) {
     489           0 :                 ofile<<keys.getOutputComponentDescription(tname.substr(0,hyph))<<"  This is the "<<tname.substr(hyph+1)<<"th of these quantities";
     490             :               } else {
     491           0 :                 ofile<<keys.getOutputComponentDescription(tname);
     492             :               }
     493           0 :               ofile<<"</td></tr>"<<std::endl;
     494             :             }
     495           0 :             ofile<<"</table>"<<std::endl;
     496             :           }
     497             :         } else {
     498           0 :           ActionWithVirtualAtom* avv=dynamic_cast<ActionWithVirtualAtom*>( myplumed.getActionSet().selectWithLabel<Action*>(lab) );
     499           0 :           if( avv ) {
     500           0 :             ofile<<" calculates the position of a virtual atom";
     501           0 :           } else if( interpreted[0]=="GROUP" ) {
     502           0 :             ofile<<" defines a group of atoms so that they can be referred to later in the input";
     503             :           }
     504             :         }
     505           0 :         ofile<<"</span>"<<std::endl;
     506             :       } else {
     507           0 :         ofile<<"<span style=\"display:none;\" id=\""<<egname<<lab<<"\"> You cannot view the components that are calculated by each action for this input file. Sorry </span>"<<std::endl;
     508             :       }
     509           0 :     }
     510           0 :     ofile.flush();
     511             :   }
     512           0 :   ofile<<"</pre>"<<std::endl;
     513           0 : }
     514             : 
     515             : } // End of namespace
     516             : }

Generated by: LCOV version 1.16