LCOV - code coverage report
Current view: top level - cltools - GenExample.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 17 220 7.7 %
Date: 2024-10-11 08:09:47 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             : {
      64             : private:
      65             :   int multi;
      66             :   std::string status, version;
      67             :   Communicator intracomm;
      68             :   Communicator intercomm;
      69             : public:
      70             :   static void registerKeywords( Keywords& keys );
      71             :   explicit GenExample(const CLToolOptions& co );
      72             :   int main(FILE* in, FILE*out,Communicator& pc) override;
      73           4 :   std::string description()const override {
      74           4 :     return "construct an example for the manual that users can interact with";
      75             :   }
      76             :   void printExampleInput( const std::vector<std::vector<std::string> >& input, const std::string& egname, const std::string& divname, std::ofstream& ofile );
      77             :   std::vector<std::vector<std::string> > createLongInput( const std::vector<std::vector<std::string> >& input );
      78             : };
      79             : 
      80       10423 : PLUMED_REGISTER_CLTOOL(GenExample,"gen_example")
      81             : 
      82        3473 : void GenExample::registerKeywords( Keywords& keys ) {
      83        3473 :   CLTool::registerKeywords( keys );
      84        6946 :   keys.add("compulsory","--plumed","plumed.dat","convert the input in this file to the html manual");
      85        6946 :   keys.add("compulsory","--out","example.html","the file on which to output the example in html");
      86        6946 :   keys.add("compulsory","--name","ppp","the name to use for this particular input");
      87        6946 :   keys.add("compulsory","--status","nobadge","whether or not the input file works");
      88        6946 :   keys.add("compulsory","--multi","0","set number of replicas for multi environment (needs MPI)");
      89        3473 : }
      90             : 
      91           4 : GenExample::GenExample(const CLToolOptions& co ):
      92             :   CLTool(co),
      93           4 :   multi(0),
      94           8 :   status("nobadge"),
      95           4 :   version("master")
      96             : {
      97           4 :   inputdata=commandline;
      98           4 : }
      99             : 
     100           0 : int GenExample::main(FILE* in, FILE*out,Communicator& pc) {
     101             : 
     102             : // set up for multi replica driver:
     103           0 :   parse("--multi",multi);
     104           0 :   if(multi) {
     105           0 :     int ntot=pc.Get_size(); int nintra=ntot/multi;
     106           0 :     if(multi*nintra!=ntot) error("invalid number of processes for multi environment");
     107           0 :     pc.Split(pc.Get_rank()/nintra,pc.Get_rank(),intracomm);
     108           0 :     pc.Split(pc.Get_rank()%nintra,pc.Get_rank(),intercomm);
     109             :   } else {
     110           0 :     intracomm.Set_comm(pc.Get_comm());
     111             :   }
     112             : 
     113           0 :   if( config::getVersionLong().find("dev")==std::string::npos ) version="v"+config::getVersion();
     114           0 :   std::string fname, egname, outfile; parse("--plumed",fname);
     115           0 :   parse("--name",egname); parse("--out",outfile); parse("--status",status);
     116             : 
     117           0 :   int r=0;
     118           0 :   if(intracomm.Get_rank()==0) r=intercomm.Get_rank();
     119           0 :   intracomm.Bcast(r,0);
     120           0 :   if(r>0) outfile="/dev/null";
     121             : 
     122           0 :   IFile ifile; ifile.open(fname); ifile.allowNoEOL(); std::ofstream ofile; ofile.open(outfile); std::vector<bool> shortcuts;
     123             :   bool hasshortcuts=false, endplumed=false; std::vector<std::vector<std::string> > input; std::vector<std::string> words;
     124           0 :   while( Tools::getParsedLine(ifile, words, false) ) {
     125           0 :     input.push_back( words ); shortcuts.push_back( false );
     126           0 :     if( words.empty() || words[0].find("#")!=std::string::npos || endplumed ) continue;
     127           0 :     std::vector<std::string> interpreted( words ); Tools::interpretLabel(interpreted);
     128           0 :     if( interpreted[0]=="ENDPLUMED" ) { endplumed=true; continue; }
     129           0 :     Keywords keys; actionRegister().getKeywords( interpreted[0], keys );
     130           0 :     if( status=="working" && keys.exists("IS_SHORTCUT") ) hasshortcuts=shortcuts[shortcuts.size()-1]=true;
     131           0 :   }
     132           0 :   ifile.close();
     133           0 :   if( hasshortcuts ) {
     134           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";
     135           0 :     ofile<<"<div style=\"width: 10%; float:left\"><button type=\"button\" id=\""<<egname<<"_button\" onclick=\'swapInput(\""<<egname<<"\")\'>contract shortcuts</button></div>";
     136             :   } else {
     137           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";
     138             :   }
     139           0 :   ofile<<"<div style=\"width: 10%; float:left\">";
     140           0 :   ofile<<"<img src=\"https://img.shields.io/badge/";
     141           0 :   if(status=="working") ofile<<version<<"-passing-green";
     142           0 :   else if(status=="broken") ofile<<version<<"-failed-red";
     143           0 :   else if(status=="loads") ofile<<"with-LOAD-yellow";
     144           0 :   else if(status=="incomplete") ofile<<version<<"-incomplete-yellow";
     145           0 :   else error("unknown status");
     146           0 :   ofile<<".svg\" alt=\"tested on "<<version<<"\" /></div>";
     147           0 :   ofile.flush();
     148           0 :   if( hasshortcuts ) {
     149             :     // Write out the short version of the input
     150           0 :     ofile<<"<div style=\"width: 100%; float:left\" id=\"input_"<<egname<<"\"></div>"<<std::endl;
     151             :     // Write an extra pre to make sure the html after the example is put in the right place on the page
     152           0 :     ofile<<"<pre style=\"width: 97%;\" class=\"fragment\"></pre>"<<std::endl;
     153           0 :     ofile<<"<script type=\"text/javascript\">"<<std::endl;
     154           0 :     ofile<<"if (window.addEventListener) { // Mozilla, Netscape, Firefox"<<std::endl;
     155           0 :     ofile<<"    window.addEventListener('load', "<<egname<<"Load, false);"<<std::endl;
     156           0 :     ofile<<"} else if (window.attachEvent) { // IE"<<std::endl;
     157           0 :     ofile<<"    window.attachEvent('onload', "<<egname<<"Load);"<<std::endl;
     158           0 :     ofile<<"}"<<std::endl;
     159           0 :     ofile<<"function "<<egname<<"Load(event) {"<<std::endl;
     160           0 :     ofile<<"       swapInput(\""<<egname<<"\");"<<std::endl;
     161           0 :     ofile<<"}"<<std::endl;
     162           0 :     ofile<<"</script>"<<std::endl;
     163           0 :     ofile<<"<div style=\"display:none;\" id=\""<<egname<<"short\">"<<std::endl;
     164           0 :     printExampleInput( input, egname + "short", egname, ofile );
     165           0 :     ofile<<"</div>"<<std::endl;
     166             :     // Write out long version of the input
     167           0 :     ofile<<"<div style=\"display:none;\" id=\""<<egname<<"long\">";
     168           0 :     std::vector<std::vector<std::string> > long_input = createLongInput( input );
     169           0 :     printExampleInput( long_input, egname + "long", egname, ofile );
     170           0 :     ofile<<"</div>"<<std::endl;
     171           0 :   } else printExampleInput( input, egname, egname, ofile );
     172           0 :   ofile.close(); return 0;
     173           0 : }
     174             : 
     175           0 : std::vector<std::vector<std::string> > GenExample::createLongInput( const std::vector<std::vector<std::string> >& input ) {
     176           0 :   std::vector<std::vector<std::string> > long_input; PlumedMain myplumed; int rr=sizeof(double), natoms=10000000; double kt=2.49;
     177           0 :   myplumed.cmd("setRealPrecision",&rr);
     178           0 :   if(Communicator::initialized()) {
     179           0 :     if(multi) {
     180           0 :       if(intracomm.Get_rank()==0) myplumed.cmd("GREX setMPIIntercomm",&intercomm.Get_comm());
     181           0 :       myplumed.cmd("GREX setMPIIntracomm",&intracomm.Get_comm()); myplumed.cmd("GREX init");
     182             :     }
     183           0 :     myplumed.cmd("setMPIComm",&intracomm.Get_comm());
     184             :   }
     185           0 :   bool endplumed=false; myplumed.cmd("setNatoms",&natoms); myplumed.cmd("setKbT",&kt); myplumed.cmd("init");
     186           0 :   for(unsigned ll=0; ll<input.size(); ++ll) {
     187           0 :     if( input[ll].empty() || endplumed ) { long_input.push_back( input[ll] ); continue; }
     188           0 :     if( input[ll][0].find("#")!=std::string::npos ) { long_input.push_back( input[ll] ); continue; }
     189           0 :     std::vector<std::string> interpreted( input[ll] ); Tools::interpretLabel(interpreted);
     190           0 :     if( interpreted[0]=="ENDPLUMED" ) { endplumed=true; long_input.push_back( input[ll] ); continue; }
     191           0 :     Keywords keys; plumed_assert( actionRegister().check( interpreted[0] ) );
     192           0 :     actionRegister().getKeywords( interpreted[0], keys ); std::string lab, myinputline;
     193           0 :     if( Tools::parse(interpreted, "LABEL", lab ) ) myinputline = lab + ": ";
     194           0 :     myinputline += interpreted[0] + " "; bool trailingcomment=false;
     195           0 :     for(unsigned i=1; i<interpreted.size(); ++i) {
     196           0 :       if( trailingcomment && interpreted[i]=="@newline") { trailingcomment=false; continue; }
     197           0 :       if( interpreted[i].find("#")!=std::string::npos ) { trailingcomment=true; continue; }
     198           0 :       if( interpreted[i]=="@newline" || interpreted[i]=="..." ) continue;
     199           0 :       std::size_t pos = 0;  while ((pos = interpreted[i].find("@newline",pos)) != std::string::npos) { interpreted[i].replace(pos, 8, "\n"); pos++; }
     200           0 :       myinputline += interpreted[i] + " ";
     201             :     }
     202           0 :     if( status=="working" && keys.exists("IS_SHORTCUT") ) {
     203           0 :       myplumed.readInputLine( myinputline );
     204           0 :       ActionShortcut* as=dynamic_cast<ActionShortcut*>( myplumed.getActionSet()[myplumed.getActionSet().size()-1].get() );
     205           0 :       plumed_assert( as ); std::vector<std::string> shortcut_commands = as->getSavedInputLines();
     206           0 :       for(unsigned i=0; i<shortcut_commands.size(); ++i) {
     207           0 :         std::vector<std::string> words = Tools::getWords( shortcut_commands[i] ); long_input.push_back( words );
     208           0 :       }
     209           0 :     } else { long_input.push_back( input[ll] ); myplumed.readInputLine( myinputline ); }
     210           0 :   }
     211           0 :   return long_input;
     212           0 : }
     213             : 
     214           0 : void GenExample::printExampleInput( const std::vector<std::vector<std::string> >& input, const std::string& egname, const std::string& divname, std::ofstream& ofile ) {
     215           0 :   PlumedMain myplumed; int rr=sizeof(double), natoms=10000000; 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) myplumed.cmd("GREX setMPIIntercomm",&intercomm.Get_comm());
     220           0 :       myplumed.cmd("GREX setMPIIntracomm",&intracomm.Get_comm()); myplumed.cmd("GREX init");
     221             :     }
     222           0 :     myplumed.cmd("setMPIComm",&intracomm.Get_comm());
     223             :   }
     224           0 :   myplumed.cmd("setNatoms",&natoms); myplumed.cmd("setKbT",&kt); myplumed.cmd("init");
     225             :   std::vector<std::string> labellist; bool endplumed=false;
     226           0 :   ofile<<"<pre style=\"width: 97%;\" class=\"fragment\">"<<std::endl;
     227           0 :   for(unsigned ll=0; ll<input.size(); ++ll) {
     228           0 :     if( input[ll].empty() ) { ofile<<std::endl; continue; }
     229           0 :     if( input[ll][0].find("#")!=std::string::npos || endplumed ) {
     230           0 :       ofile<<"<span style=\"color:blue\">"<<input[ll][0];
     231           0 :       for(unsigned i=1; i<input[ll].size(); ++i) ofile<<" "<<input[ll][i];
     232           0 :       ofile<<"</span>"<<std::endl;;
     233             :     } else {
     234             :       // Interpret the label if this needs to be done
     235           0 :       std::vector<std::string> interpreted( input[ll] ); Tools::interpretLabel(interpreted); std::string lab, myinputline;
     236             :       // Now read in the label
     237           0 :       if( Tools::parse(interpreted,"LABEL",lab) ) {
     238           0 :         ofile<<"<b name=\""<<egname<<lab<<"\" onclick=\'showPath(\""<<divname<<"\",\""<<egname<<lab<<"\")\'>"<<lab<<": </b>";
     239           0 :         labellist.push_back(lab); myinputline = lab + ": ";
     240             :       }
     241             :       // Print the keyword in use in the action
     242           0 :       std::string action = interpreted[0]; myinputline += interpreted[0] + " ";
     243           0 :       if( action=="ENDPLUMED" ) endplumed=true;
     244           0 :       Keywords keys; actionRegister().getKeywords( interpreted[0], keys );
     245             :       // Handle conversion of action names to links
     246           0 :       std::transform(action.begin(), action.end(), action.begin(), [](unsigned char c) { return std::tolower(c); });
     247           0 :       ofile<<"<a href=\"https://www.plumed.org/doc-"<<version<<"/user-doc/html/";
     248             :       for(unsigned n=0;; ++n) {
     249           0 :         std::size_t und=action.find_first_of("_");
     250           0 :         if( und==std::string::npos ) break;
     251           0 :         std::string first=action.substr(0,und);
     252           0 :         for(auto c : first ) { if( isdigit(c) ) ofile<<c; else ofile<<"_"<<c; }
     253           0 :         ofile<<"_"; action=action.substr(und+1);
     254           0 :       }
     255           0 :       for(auto c : action ) { if( isdigit(c) ) ofile<<c; else ofile<<"_"<<c; }
     256           0 :       ofile<<".html\" style=\"color:green\">"<<interpreted[0]<<"</a> ";
     257             :       // And write out everything else in the input line
     258             :       bool trailingcomment=false;
     259           0 :       for(unsigned i=1; i<interpreted.size(); ++i) {
     260           0 :         if( interpreted[i]=="@newline" && i==1 ) { ofile<<"..."<<std::endl<<"   "; continue; }
     261           0 :         else if( interpreted[i]=="@newline" ) {
     262           0 :           if( trailingcomment ) { ofile<<"</span>"; trailingcomment=false; }
     263           0 :           if( interpreted[i+1]=="..." ) ofile<<std::endl;
     264           0 :           else ofile<<std::endl<<"   ";
     265           0 :           continue;
     266           0 :         } else if( interpreted[i]=="__FILL__" ) {
     267           0 :           if( status!="incomplete" ) error("found __FILL__ statement but status is " + status);
     268           0 :           ofile<<"<span style=\"background-color:yellow\">__FILL__</span>";
     269           0 :           continue;
     270           0 :         } else if( interpreted[i]==action ) continue;
     271           0 :         if( interpreted[i].find("#")!=std::string::npos ) { trailingcomment=true; ofile<<"<span style=\"color:blue\">"; }
     272             : 
     273           0 :         if( !trailingcomment ) {
     274           0 :           std::size_t eq=interpreted[i].find_first_of("=");
     275           0 :           if( eq!=std::string::npos ) {
     276           0 :             std::string keyword=interpreted[i].substr(0,eq), rest=interpreted[i].substr(eq+1);
     277           0 :             ofile<<"<div class=\"tooltip\">"<<keyword<<"<div class=\"right\">"<<keys.getTooltip(keyword)<<"<i></i></div></div>";
     278           0 :             if( rest=="__FILL__" ) {
     279           0 :               if( status!="incomplete" ) error("found __FILL__ statement but status is " + status);
     280           0 :               ofile<<"=<span style=\"background-color:yellow\">__FILL__</span>";
     281           0 :             } else if( rest.find_first_of("{")!=std::string::npos ) {
     282           0 :               std::size_t pos = 0;  while ((pos = rest.find("@newline",pos)) != std::string::npos) { rest.replace(pos, 8, "\n"); pos++; }
     283           0 :               ofile<<"="<<rest<<" "; myinputline += keyword + "=" + rest + " ";
     284             :             } else {
     285           0 :               std::vector<std::string> args=Tools::getWords(rest,"\t\n ,"); ofile<<"=";
     286           0 :               for(unsigned i=0; i<args.size(); ++i) {
     287             :                 bool islabel=false; std::string thislab;
     288           0 :                 for(unsigned j=0; j<labellist.size(); ++j) {
     289           0 :                   std::size_t dot=args[i].find_first_of("."); std::string lll=args[i].substr(0,dot);
     290           0 :                   if( lll==labellist[j] ) { islabel=true; thislab=labellist[j]; break; }
     291             :                 }
     292           0 :                 if( islabel ) ofile<<"<b name=\""<<egname<<thislab<<"\">"<<args[i]<<"</b>";
     293             :                 else ofile<<args[i];
     294           0 :                 if( i!=args.size()-1 ) ofile<<",";
     295             :               }
     296           0 :               myinputline += interpreted[i] + " ";
     297           0 :             }
     298           0 :             ofile<<" ";
     299           0 :           } else if( interpreted[i]!="@newline" && interpreted[i]!="..." ) {
     300           0 :             myinputline += interpreted[i] + " ";
     301           0 :             ofile<<"<div class=\"tooltip\">"<<interpreted[i]<<"<div class=\"right\">"<<keys.getTooltip(interpreted[i])<<"<i></i></div></div> ";
     302           0 :           } else if( interpreted[i]=="..." ) ofile<<"...";
     303           0 :         } else ofile<<interpreted[i]<<" ";
     304             :       }
     305           0 :       if( trailingcomment ) ofile<<"</span>";
     306             :       // This builds the hidden content that tells the user about what is calculated
     307           0 :       if( status=="working" ) {
     308           0 :         ofile<<"<span style=\"display:none;\" id=\""<<egname<<lab<<"\">";
     309           0 :         ofile<<"The "<<interpreted[0]<<" action with label <b>"<<lab<<"</b>";
     310           0 :         myplumed.readInputLine( myinputline );
     311           0 :         ActionWithValue* av=dynamic_cast<ActionWithValue*>( myplumed.getActionSet().selectWithLabel<Action*>(lab) );
     312           0 :         if( av ) {
     313           0 :           if( av->getNumberOfComponents()==1 ) { ofile<<" calculates a single scalar value"; }
     314           0 :           else if( av->getNumberOfComponents()>0 ) {
     315           0 :             ofile<<" calculates the following quantities:"<<std::endl;
     316           0 :             ofile<<"<table  align=\"center\" frame=\"void\" width=\"95%%\" cellpadding=\"5%%\">"<<std::endl;
     317           0 :             ofile<<"<tr><td width=\"5%%\"><b> Quantity </b>  </td><td><b> Description </b> </td></tr>"<<std::endl;
     318           0 :             unsigned ncomp = av->getNumberOfComponents();
     319           0 :             for(unsigned k=0; k<ncomp; ++k ) {
     320           0 :               std::string myname = av->copyOutput(k)->getName(); std::size_t dot=myname.find_first_of(".");
     321           0 :               std::string tname=myname.substr(dot+1); std::size_t und=tname.find_last_of("_"); std::size_t hyph=tname.find_first_of("-");
     322           0 :               if( und!=std::string::npos && hyph!=std::string::npos ) plumed_merror("cannot use underscore and hyphen in name");
     323           0 :               ofile<<"<tr><td width=\"5%%\">"<<myname<<"</td><td>";
     324           0 :               if( und!=std::string::npos ) {
     325           0 :                 ofile<<keys.getOutputComponentDescription(tname.substr(und))<<" This particular component measures this quantity for the input CV named ";
     326           0 :                 ofile<<tname.substr(0,und);
     327           0 :               } else if( hyph!=std::string::npos ) {
     328           0 :                 ofile<<keys.getOutputComponentDescription(tname.substr(0,hyph))<<"  This is the "<<tname.substr(hyph+1)<<"th of these quantities";
     329           0 :               } else ofile<<keys.getOutputComponentDescription(tname);
     330           0 :               ofile<<"</td></tr>"<<std::endl;
     331             :             }
     332           0 :             ofile<<"</table>"<<std::endl;
     333             :           }
     334             :         } else {
     335           0 :           ActionWithVirtualAtom* avv=dynamic_cast<ActionWithVirtualAtom*>( myplumed.getActionSet().selectWithLabel<Action*>(lab) );
     336           0 :           if( avv ) ofile<<" calculates the position of a virtual atom";
     337           0 :           else if( interpreted[0]=="GROUP" ) ofile<<" defines a group of atoms so that they can be referred to later in the input";
     338             :         }
     339           0 :         ofile<<"</span>"<<std::endl;
     340             :       } else {
     341           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;
     342             :       }
     343           0 :     }
     344           0 :     ofile.flush();
     345             :   }
     346           0 :   ofile<<"</pre>"<<std::endl;
     347           0 : }
     348             : 
     349             : } // End of namespace
     350             : }

Generated by: LCOV version 1.15