394205f590e047719c6e560e88adfbb02b8fffcc
[alexxy/gromacs.git] / src / gromacs / commandline / pargs.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 /* This file is completely threadsafe - keep it that way! */
38 #include "gromacs/commandline/pargs.h"
39
40 #include "config.h"
41
42 #include <cstdlib>
43 #include <cstring>
44
45 #include <algorithm>
46 #include <list>
47
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51
52 #include "thread_mpi/threads.h"
53
54 #include "gromacs/commandline/cmdlinehelpcontext.h"
55 #include "gromacs/commandline/cmdlinehelpwriter.h"
56 #include "gromacs/commandline/cmdlineparser.h"
57 #include "gromacs/fileio/timecontrol.h"
58 #include "gromacs/options/basicoptions.h"
59 #include "gromacs/options/filenameoption.h"
60 #include "gromacs/options/filenameoptionmanager.h"
61 #include "gromacs/options/options.h"
62 #include "gromacs/options/timeunitmanager.h"
63 #include "gromacs/utility/arrayref.h"
64 #include "gromacs/utility/basenetwork.h"
65 #include "gromacs/utility/common.h"
66 #include "gromacs/utility/cstringutil.h"
67 #include "gromacs/utility/exceptions.h"
68 #include "gromacs/utility/fatalerror.h"
69 #include "gromacs/utility/gmxassert.h"
70 #include "gromacs/utility/programcontext.h"
71 #include "gromacs/utility/smalloc.h"
72 #include "gromacs/utility/stringutil.h"
73
74 /* The source code in this file should be thread-safe.
75       Please keep it that way. */
76
77 int nenum(const char *const enumc[])
78 {
79     int i;
80
81     i = 1;
82     /* we *can* compare pointers directly here! */
83     while (enumc[i] && enumc[0] != enumc[i])
84     {
85         i++;
86     }
87
88     return i;
89 }
90
91 int opt2parg_int(const char *option, int nparg, t_pargs pa[])
92 {
93     int i;
94
95     for (i = 0; (i < nparg); i++)
96     {
97         if (strcmp(pa[i].option, option) == 0)
98         {
99             return *pa[i].u.i;
100         }
101     }
102
103     gmx_fatal(FARGS, "No integer option %s in pargs", option);
104
105     return 0;
106 }
107
108 gmx_bool opt2parg_bool(const char *option, int nparg, t_pargs pa[])
109 {
110     int i;
111
112     for (i = 0; (i < nparg); i++)
113     {
114         if (strcmp(pa[i].option, option) == 0)
115         {
116             return *pa[i].u.b;
117         }
118     }
119
120     gmx_fatal(FARGS, "No boolean option %s in pargs", option);
121
122     return FALSE;
123 }
124
125 real opt2parg_real(const char *option, int nparg, t_pargs pa[])
126 {
127     int i;
128
129     for (i = 0; (i < nparg); i++)
130     {
131         if (strcmp(pa[i].option, option) == 0)
132         {
133             return *pa[i].u.r;
134         }
135     }
136
137     gmx_fatal(FARGS, "No real option %s in pargs", option);
138
139     return 0.0;
140 }
141
142 const char *opt2parg_str(const char *option, int nparg, t_pargs pa[])
143 {
144     int i;
145
146     for (i = 0; (i < nparg); i++)
147     {
148         if (strcmp(pa[i].option, option) == 0)
149         {
150             return *(pa[i].u.c);
151         }
152     }
153
154     gmx_fatal(FARGS, "No string option %s in pargs", option);
155
156     return NULL;
157 }
158
159 gmx_bool opt2parg_bSet(const char *option, int nparg, t_pargs pa[])
160 {
161     int i;
162
163     for (i = 0; (i < nparg); i++)
164     {
165         if (strcmp(pa[i].option, option) == 0)
166         {
167             return pa[i].bSet;
168         }
169     }
170
171     gmx_fatal(FARGS, "No such option %s in pargs", option);
172
173     return FALSE; /* Too make some compilers happy */
174 }
175
176 const char *opt2parg_enum(const char *option, int nparg, t_pargs pa[])
177 {
178     int i;
179
180     for (i = 0; (i < nparg); i++)
181     {
182         if (strcmp(pa[i].option, option) == 0)
183         {
184             return pa[i].u.c[0];
185         }
186     }
187
188     gmx_fatal(FARGS, "No such option %s in pargs", option);
189
190     return NULL;
191 }
192
193 /********************************************************************
194  * parse_common_args()
195  */
196
197 namespace gmx
198 {
199
200 namespace
201 {
202
203 /*! \brief
204  * Returns the index of the default xvg format.
205  *
206  * \ingroup module_commandline
207  */
208 int getDefaultXvgFormat(gmx::ConstArrayRef<const char *> xvgFormats)
209 {
210     const char *const select = getenv("GMX_VIEW_XVG");
211     if (select != NULL)
212     {
213         ConstArrayRef<const char *>::const_iterator i =
214             std::find(xvgFormats.begin(), xvgFormats.end(), std::string(select));
215         if (i != xvgFormats.end())
216         {
217             return i - xvgFormats.begin();
218         }
219         else
220         {
221             return exvgNONE - 1;
222         }
223     }
224     /* The default is the first option */
225     return 0;
226 }
227
228 /*! \brief
229  * Conversion helper between t_pargs/t_filenm and Options.
230  *
231  * This class holds the necessary mapping between the old C structures and
232  * the new C++ options to allow copying values back after parsing for cases
233  * where the C++ options do not directly provide the type of value required for
234  * the C structures.
235  *
236  * \ingroup module_commandline
237  */
238 class OptionsAdapter
239 {
240     public:
241         /*! \brief
242          * Initializes the adapter to convert from a specified command line.
243          *
244          * The command line is required, because t_pargs wants to return
245          * strings by reference to the original command line.
246          * OptionsAdapter creates a copy of the `argv` array (but not the
247          * strings) to make this possible, even if the parser removes
248          * options it has recognized.
249          */
250         OptionsAdapter(int argc, const char *const argv[])
251             : argv_(argv, argv + argc)
252         {
253         }
254
255         /*! \brief
256          * Converts a t_filenm option into an Options option.
257          *
258          * \param options Options object to add the new option to.
259          * \param fnm     t_filenm option to convert.
260          */
261         void filenmToOptions(Options *options, t_filenm *fnm);
262         /*! \brief
263          * Converts a t_pargs option into an Options option.
264          *
265          * \param     options Options object to add the new option to.
266          * \param     pa      t_pargs option to convert.
267          */
268         void pargsToOptions(Options *options, t_pargs *pa);
269
270         /*! \brief
271          * Copies values back from options to t_pargs/t_filenm.
272          */
273         void copyValues(bool bReadNode);
274
275     private:
276         struct FileNameData
277         {
278             //! Creates a conversion helper for a given `t_filenm` struct.
279             explicit FileNameData(t_filenm *fnm) : fnm(fnm), optionInfo(NULL)
280             {
281             }
282
283             //! t_filenm structure to receive the final values.
284             t_filenm                 *fnm;
285             //! Option info object for the created FileNameOption.
286             FileNameOptionInfo       *optionInfo;
287             //! Value storage for the created FileNameOption.
288             std::vector<std::string>  values;
289         };
290         struct ProgramArgData
291         {
292             //! Creates a conversion helper for a given `t_pargs` struct.
293             explicit ProgramArgData(t_pargs *pa)
294                 : pa(pa), optionInfo(NULL), enumIndex(0), boolValue(false)
295             {
296             }
297
298             //! t_pargs structure to receive the final values.
299             t_pargs                 *pa;
300             //! Option info object for the created option.
301             OptionInfo              *optionInfo;
302             //! Value storage for a non-enum StringOption (unused for other types).
303             std::string              stringValue;
304             //! Value storage for an enum option (unused for other types).
305             int                      enumIndex;
306             //! Value storage for a BooleanOption (unused for other types).
307             bool                     boolValue;
308         };
309
310         std::vector<const char *>    argv_;
311         // These are lists instead of vectors to avoid relocating existing
312         // objects in case the container is reallocated (the Options object
313         // contains pointes to members of the objects, which would get
314         // invalidated).
315         std::list<FileNameData>      fileNameOptions_;
316         std::list<ProgramArgData>    programArgs_;
317
318         GMX_DISALLOW_COPY_AND_ASSIGN(OptionsAdapter);
319 };
320
321 void OptionsAdapter::filenmToOptions(Options *options, t_filenm *fnm)
322 {
323     if (fnm->opt == NULL)
324     {
325         // Existing code may use opt2fn() instead of ftp2fn() for
326         // options that use the default option name, so we need to
327         // keep the old behavior instead of fixing opt2fn().
328         // TODO: Check that this is not the case, remove this, and make
329         // opt2*() work even if fnm->opt is NULL for some options.
330         fnm->opt = ftp2defopt(fnm->ftp);
331     }
332     const bool        bRead     = ((fnm->flag & ffREAD)  != 0);
333     const bool        bWrite    = ((fnm->flag & ffWRITE) != 0);
334     const bool        bOptional = ((fnm->flag & ffOPT)   != 0);
335     const bool        bLibrary  = ((fnm->flag & ffLIB)   != 0);
336     const bool        bMultiple = ((fnm->flag & ffMULT)  != 0);
337     const char *const name      = &fnm->opt[1];
338     const char *      defName   = fnm->fn;
339     if (defName == NULL)
340     {
341         defName = ftp2defnm(fnm->ftp);
342     }
343     fileNameOptions_.push_back(FileNameData(fnm));
344     FileNameData &data = fileNameOptions_.back();
345     data.optionInfo = options->addOption(
346                 FileNameOption(name).storeVector(&data.values)
347                     .defaultBasename(defName).legacyType(fnm->ftp)
348                     .legacyOptionalBehavior()
349                     .readWriteFlags(bRead, bWrite).required(!bOptional)
350                     .libraryFile(bLibrary).multiValue(bMultiple)
351                     .description(ftp2desc(fnm->ftp)));
352 }
353
354 void OptionsAdapter::pargsToOptions(Options *options, t_pargs *pa)
355 {
356     const bool        bHidden = startsWith(pa->desc, "HIDDEN");
357     const char *const name    = &pa->option[1];
358     const char *const desc    = (bHidden ? &pa->desc[6] : pa->desc);
359     programArgs_.push_back(ProgramArgData(pa));
360     ProgramArgData   &data = programArgs_.back();
361     switch (pa->type)
362     {
363         case etINT:
364             data.optionInfo = options->addOption(
365                         IntegerOption(name).store(pa->u.i)
366                             .description(desc).hidden(bHidden));
367             return;
368         case etINT64:
369             data.optionInfo = options->addOption(
370                         Int64Option(name).store(pa->u.is)
371                             .description(desc).hidden(bHidden));
372             return;
373         case etREAL:
374             data.optionInfo = options->addOption(
375                         RealOption(name).store(pa->u.r)
376                             .description(desc).hidden(bHidden));
377             return;
378         case etTIME:
379             data.optionInfo = options->addOption(
380                         RealOption(name).store(pa->u.r).timeValue()
381                             .description(desc).hidden(bHidden));
382             return;
383         case etSTR:
384         {
385             const char *const defValue = (*pa->u.c != NULL ? *pa->u.c : "");
386             data.optionInfo = options->addOption(
387                         StringOption(name).store(&data.stringValue)
388                             .defaultValue(defValue)
389                             .description(desc).hidden(bHidden));
390             return;
391         }
392         case etBOOL:
393             data.optionInfo = options->addOption(
394                         BooleanOption(name).store(&data.boolValue)
395                             .defaultValue(*pa->u.b)
396                             .description(desc).hidden(bHidden));
397             return;
398         case etRVEC:
399             data.optionInfo = options->addOption(
400                         RealOption(name).store(*pa->u.rv).vector()
401                             .description(desc).hidden(bHidden));
402             return;
403         case etENUM:
404         {
405             const int defaultIndex = (pa->u.c[0] != NULL ? nenum(pa->u.c) - 1 : 0);
406             data.optionInfo = options->addOption(
407                         StringOption(name).storeEnumIndex(&data.enumIndex)
408                             .defaultEnumIndex(defaultIndex)
409                             .enumValueFromNullTerminatedArray(pa->u.c + 1)
410                             .description(desc).hidden(bHidden));
411             return;
412         }
413     }
414     GMX_THROW(NotImplementedError("Argument type not implemented"));
415 }
416
417 void OptionsAdapter::copyValues(bool bReadNode)
418 {
419     std::list<FileNameData>::const_iterator file;
420     for (file = fileNameOptions_.begin(); file != fileNameOptions_.end(); ++file)
421     {
422         if (!bReadNode && (file->fnm->flag & ffREAD))
423         {
424             continue;
425         }
426         if (file->optionInfo->isSet())
427         {
428             file->fnm->flag |= ffSET;
429         }
430         file->fnm->nfiles = file->values.size();
431         snew(file->fnm->fns, file->fnm->nfiles);
432         for (int i = 0; i < file->fnm->nfiles; ++i)
433         {
434             // TODO: Check for out-of-memory.
435             file->fnm->fns[i] = strdup(file->values[i].c_str());
436         }
437     }
438     std::list<ProgramArgData>::const_iterator arg;
439     for (arg = programArgs_.begin(); arg != programArgs_.end(); ++arg)
440     {
441         arg->pa->bSet = arg->optionInfo->isSet();
442         switch (arg->pa->type)
443         {
444             case etSTR:
445             {
446                 if (arg->pa->bSet)
447                 {
448                     std::vector<const char *>::const_iterator pos =
449                         std::find(argv_.begin(), argv_.end(), arg->stringValue);
450                     GMX_RELEASE_ASSERT(pos != argv_.end(),
451                                        "String argument got a value not in argv");
452                     *arg->pa->u.c = *pos;
453                 }
454                 break;
455             }
456             case etBOOL:
457                 *arg->pa->u.b = arg->boolValue;
458                 break;
459             case etENUM:
460                 *arg->pa->u.c = arg->pa->u.c[arg->enumIndex + 1];
461                 break;
462             default:
463                 // For other types, there is nothing type-specific to do.
464                 break;
465         }
466     }
467 }
468
469 } // namespace
470
471 } // namespace gmx
472
473 gmx_bool parse_common_args(int *argc, char *argv[], unsigned long Flags,
474                            int nfile, t_filenm fnm[], int npargs, t_pargs *pa,
475                            int ndesc, const char **desc,
476                            int nbugs, const char **bugs,
477                            output_env_t *oenv)
478 {
479     /* This array should match the order of the enum in oenv.h */
480     const char *const xvg_formats[] = { "xmgrace", "xmgr", "none" };
481
482     // Handle the flags argument, which is a bit field
483     // The FF macro returns whether or not the bit is set
484 #define FF(arg) ((Flags & arg) == arg)
485
486     try
487     {
488         int                        nicelevel = 0;
489         double                     tbegin    = 0.0, tend = 0.0, tdelta = 0.0;
490         bool                       bView     = false;
491         int                        xvgFormat = 0;
492         gmx::TimeUnitManager       timeUnitManager;
493         gmx::OptionsAdapter        adapter(*argc, argv);
494         gmx::Options               options(NULL, NULL);
495         gmx::FileNameOptionManager fileOptManager;
496
497         fileOptManager.disableInputOptionChecking(
498                 FF(PCA_NOT_READ_NODE) || FF(PCA_DISABLE_INPUT_FILE_CHECKING));
499         options.addManager(&fileOptManager);
500         options.setDescription(gmx::ConstArrayRef<const char *>(desc, ndesc));
501
502         options.addOption(
503                 gmx::IntegerOption("nice").store(&nicelevel)
504                     .defaultValue(FF(PCA_BE_NICE) ? 19 : 0)
505                     .description("Set the nicelevel"));
506
507         if (FF(PCA_CAN_SET_DEFFNM))
508         {
509             fileOptManager.addDefaultFileNameOption(&options, "deffnm");
510         }
511         if (FF(PCA_CAN_BEGIN))
512         {
513             options.addOption(
514                     gmx::DoubleOption("b").store(&tbegin).timeValue()
515                         .description("First frame (%t) to read from trajectory"));
516         }
517         if (FF(PCA_CAN_END))
518         {
519             options.addOption(
520                     gmx::DoubleOption("e").store(&tend).timeValue()
521                         .description("Last frame (%t) to read from trajectory"));
522         }
523         if (FF(PCA_CAN_DT))
524         {
525             options.addOption(
526                     gmx::DoubleOption("dt").store(&tdelta).timeValue()
527                         .description("Only use frame when t MOD dt = first time (%t)"));
528         }
529         if (FF(PCA_TIME_UNIT))
530         {
531             timeUnitManager.setTimeUnitFromEnvironment();
532             timeUnitManager.addTimeUnitOption(&options, "tu");
533         }
534         if (FF(PCA_CAN_VIEW))
535         {
536             options.addOption(
537                     gmx::BooleanOption("w").store(&bView)
538                         .description("View output [TT].xvg[tt], [TT].xpm[tt], "
539                                      "[TT].eps[tt] and [TT].pdb[tt] files"));
540         }
541
542         bool bXvgr = false;
543         for (int i = 0; i < nfile; i++)
544         {
545             bXvgr = bXvgr || (fnm[i].ftp == efXVG);
546         }
547         xvgFormat = gmx::getDefaultXvgFormat(xvg_formats);
548         if (bXvgr)
549         {
550             options.addOption(
551                     gmx::StringOption("xvg").enumValue(xvg_formats)
552                         .storeEnumIndex(&xvgFormat)
553                         .description("xvg plot formatting"));
554         }
555
556         /* Now append the program specific arguments */
557         for (int i = 0; i < nfile; i++)
558         {
559             adapter.filenmToOptions(&options, &fnm[i]);
560         }
561         for (int i = 0; i < npargs; i++)
562         {
563             adapter.pargsToOptions(&options, &pa[i]);
564         }
565
566         const gmx::CommandLineHelpContext *context =
567             gmx::GlobalCommandLineHelpContext::get();
568         if (context != NULL)
569         {
570             GMX_RELEASE_ASSERT(gmx_node_rank() == 0,
571                                "Help output should be handled higher up and "
572                                "only get called only on the master rank");
573             gmx::CommandLineHelpWriter(options)
574                 .setShowDescriptions(true)
575                 .setTimeUnitString(timeUnitManager.timeUnitAsString())
576                 .setKnownIssues(gmx::ConstArrayRef<const char *>(bugs, nbugs))
577                 .writeHelp(*context);
578             return FALSE;
579         }
580
581         /* Now parse all the command-line options */
582         gmx::CommandLineParser(&options).skipUnknown(FF(PCA_NOEXIT_ON_ARGS))
583             .parse(argc, argv);
584         options.finish();
585
586         /* set program name, command line, and default values for output options */
587         output_env_init(oenv, gmx::getProgramContext(),
588                         (time_unit_t)(timeUnitManager.timeUnit() + 1), bView,
589                         (xvg_format_t)(xvgFormat + 1), 0);
590
591         /* Set the nice level */
592 #ifdef HAVE_UNISTD_H
593 #ifndef GMX_NO_NICE
594         /* The some system, e.g. the catamount kernel on cray xt3 do not have nice(2). */
595         if (nicelevel != 0)
596         {
597             static gmx_bool            nice_set   = FALSE; /* only set it once */
598             static tMPI_Thread_mutex_t init_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
599             tMPI_Thread_mutex_lock(&init_mutex);
600             if (!nice_set)
601             {
602                 if (nice(nicelevel) == -1)
603                 {
604                     /* Do nothing, but use the return value to avoid warnings. */
605                 }
606                 nice_set = TRUE;
607             }
608             tMPI_Thread_mutex_unlock(&init_mutex);
609         }
610 #endif
611 #endif
612
613         timeUnitManager.scaleTimeOptions(&options);
614
615         /* Extract Time info from arguments */
616         // TODO: Use OptionInfo objects instead of string constants
617         if (FF(PCA_CAN_BEGIN) && options.isSet("b"))
618         {
619             setTimeValue(TBEGIN, tbegin);
620         }
621         if (FF(PCA_CAN_END) && options.isSet("e"))
622         {
623             setTimeValue(TEND, tend);
624         }
625         if (FF(PCA_CAN_DT) && options.isSet("dt"))
626         {
627             setTimeValue(TDELTA, tdelta);
628         }
629
630         adapter.copyValues(!FF(PCA_NOT_READ_NODE));
631
632         return TRUE;
633     }
634     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
635 #undef FF
636 }