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

Generated by: LCOV version 1.16