Create fileio module
[alexxy/gromacs.git] / src / gromacs / gmxlib / statutil.cpp
1 /* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
2  *
3  *
4  *                This source code is part of
5  *
6  *                 G   R   O   M   A   C   S
7  *
8  *          GROningen MAchine for Chemical Simulations
9  *
10  *                        VERSION 3.2.0
11  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
12  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
13  * Copyright (c) 2001-2004, The GROMACS development team,
14  * check out http://www.gromacs.org for more information.
15
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License
18  * as published by the Free Software Foundation; either version 2
19  * of the License, or (at your option) any later version.
20  *
21  * If you want to redistribute modifications, please consider that
22  * scientific software is very special. Version control is crucial -
23  * bugs must be traceable. We will be happy to consider code for
24  * inclusion in the official distribution, but derived work must not
25  * be called official GROMACS. Details are found in the README & COPYING
26  * files - if they are missing, get the official version at www.gromacs.org.
27  *
28  * To help us fund GROMACS development, we humbly ask that you cite
29  * the papers on the package - you can find them in the top README file.
30  *
31  * For more info, check our website at http://www.gromacs.org
32  *
33  * And Hey:
34  * GROningen Mixture of Alchemy and Childrens' Stories
35  */
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif
39
40 #include <cctype>
41 #include <cmath>
42 #include <cstdlib>
43
44 #include "sysstuff.h"
45 #include "macros.h"
46 #include "string2.h"
47 #include "smalloc.h"
48 #include "statutil.h"
49 #include "gromacs/fileio/tpxio.h"
50 #include "gmx_fatal.h"
51 #include "network.h"
52 #include "gromacs/fileio/gmxfio.h"
53 #include "gromacs/fileio/trxio.h"
54
55 #include "gromacs/onlinehelp/wman.h"
56 #include "gromacs/utility/exceptions.h"
57 #include "gromacs/utility/gmxassert.h"
58 #include "gromacs/utility/programinfo.h"
59
60 #include "thread_mpi/threads.h"
61
62 #ifdef HAVE_UNISTD_H
63 #include <unistd.h>
64 #endif
65
66 /* used for npri */
67 #ifdef __sgi
68 #include <sys/schedctl.h>
69 #include <sys/sysmp.h>
70 #endif
71
72 /* The source code in this file should be thread-safe.
73       Please keep it that way. */
74
75 /******************************************************************
76  *
77  *             T R A J E C T O R Y   S T U F F
78  *
79  ******************************************************************/
80
81 /****************************************************************
82  *
83  *            E X P O R T E D   F U N C T I O N S
84  *
85  ****************************************************************/
86
87
88 /* progam names, etc. */
89
90 const char *ShortProgram(void)
91 {
92     try
93     {
94         // TODO: Use the display name once it doesn't break anything.
95         return gmx::ProgramInfo::getInstance().programName().c_str();
96     }
97     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
98 }
99
100 const char *Program(void)
101 {
102     try
103     {
104         return gmx::ProgramInfo::getInstance().programNameWithPath().c_str();
105     }
106     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
107 }
108
109 /* utility functions */
110
111 gmx_bool bRmod_fd(double a, double b, double c, gmx_bool bDouble)
112 {
113     int    iq;
114     double tol;
115
116     tol = 2*(bDouble ? GMX_DOUBLE_EPS : GMX_FLOAT_EPS);
117
118     iq = static_cast<int>((a - b + tol*a)/c);
119
120     if (std::fabs(a - b - c*iq) <= tol*std::fabs(a))
121     {
122         return TRUE;
123     }
124     else
125     {
126         return FALSE;
127     }
128 }
129
130
131
132 static void set_default_time_unit(const char *time_list[], gmx_bool bCanTime)
133 {
134     int         i      = 0;
135     const char *select = NULL;
136
137     if (bCanTime)
138     {
139         select = getenv("GMXTIMEUNIT");
140         if (select != NULL)
141         {
142             i = 1;
143             while (time_list[i] && strcmp(time_list[i], select) != 0)
144             {
145                 i++;
146             }
147         }
148     }
149     if (!bCanTime || select == NULL ||
150         time_list[i] == NULL || strcmp(time_list[i], select) != 0)
151     {
152         /* Set it to the default: ps */
153         i = 1;
154         while (time_list[i] && strcmp(time_list[i], "ps") != 0)
155         {
156             i++;
157         }
158
159     }
160     time_list[0] = time_list[i];
161 }
162
163
164 static void set_default_xvg_format(const char *xvg_list[])
165 {
166     int         i;
167     const char *select;
168
169     select = getenv("GMX_VIEW_XVG");
170     if (select == NULL)
171     {
172         /* The default is the first option */
173         xvg_list[0] = xvg_list[1];
174     }
175     else
176     {
177         i = 1;
178         while (xvg_list[i] && strcmp(xvg_list[i], select) != 0)
179         {
180             i++;
181         }
182         if (xvg_list[i] != NULL)
183         {
184             xvg_list[0] = xvg_list[i];
185         }
186         else
187         {
188             xvg_list[0] = xvg_list[exvgNONE];
189         }
190     }
191 }
192
193
194 /***** T O P O L O G Y   S T U F F ******/
195
196 t_topology *read_top(const char *fn, int *ePBC)
197 {
198     int         epbc, natoms;
199     t_topology *top;
200
201     snew(top, 1);
202     epbc = read_tpx_top(fn, NULL, NULL, &natoms, NULL, NULL, NULL, top);
203     if (ePBC)
204     {
205         *ePBC = epbc;
206     }
207
208     return top;
209 }
210
211 /*************************************************************
212  *
213  *           P A R S I N G   S T U F F
214  *
215  *************************************************************/
216
217 static void usage(const char *type, const char *arg)
218 {
219     GMX_ASSERT(arg != NULL, "NULL command-line argument should not occur");
220     gmx_fatal(FARGS, "Expected %s argument for option %s\n", type, arg);
221 }
222
223 int iscan(int argc, char *argv[], int *i)
224 {
225     const char *const arg = argv[*i];
226     if (argc <= (*i)+1)
227     {
228         usage("an integer", arg);
229     }
230     const char *const value = argv[++(*i)];
231     char             *endptr;
232     int               var = std::strtol(value, &endptr, 10);
233     if (*value == '\0' || *endptr != '\0')
234     {
235         usage("an integer", arg);
236     }
237     return var;
238 }
239
240 gmx_large_int_t istepscan(int argc, char *argv[], int *i)
241 {
242     const char *const arg = argv[*i];
243     if (argc <= (*i)+1)
244     {
245         usage("an integer", arg);
246     }
247     const char *const value = argv[++(*i)];
248     char             *endptr;
249     gmx_large_int_t   var = str_to_large_int_t(value, &endptr);
250     if (*value == '\0' || *endptr != '\0')
251     {
252         usage("an integer", arg);
253     }
254     return var;
255 }
256
257 double dscan(int argc, char *argv[], int *i)
258 {
259     const char *const arg = argv[*i];
260     if (argc <= (*i)+1)
261     {
262         usage("a real", arg);
263     }
264     const char *const value = argv[++(*i)];
265     char             *endptr;
266     double            var = std::strtod(value, &endptr);
267     if (*value == '\0' || *endptr != '\0')
268     {
269         usage("a real", arg);
270     }
271     return var;
272 }
273
274 char *sscan(int argc, char *argv[], int *i)
275 {
276     if (argc > (*i)+1)
277     {
278         if ( (argv[(*i)+1][0] == '-') && (argc > (*i)+2) &&
279              (argv[(*i)+2][0] != '-') )
280         {
281             fprintf(stderr, "Possible missing string argument for option %s\n\n",
282                     argv[*i]);
283         }
284     }
285     else
286     {
287         usage("a string", argv[*i]);
288     }
289
290     return argv[++(*i)];
291 }
292
293 int nenum(const char *const enumc[])
294 {
295     int i;
296
297     i = 1;
298     /* we *can* compare pointers directly here! */
299     while (enumc[i] && enumc[0] != enumc[i])
300     {
301         i++;
302     }
303
304     return i;
305 }
306
307 static void pdesc(char *desc)
308 {
309     char *ptr, *nptr;
310
311     ptr = desc;
312     if ((int)strlen(ptr) < 70)
313     {
314         fprintf(stderr, "\t%s\n", ptr);
315     }
316     else
317     {
318         for (nptr = ptr+70; (nptr != ptr) && (!std::isspace(*nptr)); nptr--)
319         {
320             ;
321         }
322         if (nptr == ptr)
323         {
324             fprintf(stderr, "\t%s\n", ptr);
325         }
326         else
327         {
328             *nptr = '\0';
329             nptr++;
330             fprintf(stderr, "\t%s\n", ptr);
331             pdesc(nptr);
332         }
333     }
334 }
335
336 static int add_parg(int npargs, t_pargs *pa, t_pargs *pa_add)
337 {
338     memcpy(&(pa[npargs]), pa_add, sizeof(*pa_add));
339
340     return npargs+1;
341 }
342
343 static char *mk_desc(t_pargs *pa, const char *time_unit_str)
344 {
345     char      *newdesc = NULL, *ndesc = NULL, *nptr = NULL;
346     const char*ptr     = NULL;
347     int        len, k;
348
349     /* First compute length for description */
350     len = strlen(pa->desc)+1;
351     if ((ptr = strstr(pa->desc, "HIDDEN")) != NULL)
352     {
353         len += 4;
354     }
355     if (pa->type == etENUM)
356     {
357         len += 10;
358         for (k = 1; (pa->u.c[k] != NULL); k++)
359         {
360             len += strlen(pa->u.c[k])+12;
361         }
362     }
363     snew(newdesc, len);
364
365     /* add label for hidden options */
366     if (is_hidden(pa))
367     {
368         sprintf(newdesc, "[hidden] %s", ptr+6);
369     }
370     else
371     {
372         strcpy(newdesc, pa->desc);
373     }
374
375     /* change '%t' into time_unit */
376 #define TUNITLABEL "%t"
377 #define NTUNIT strlen(TUNITLABEL)
378     if (pa->type == etTIME)
379     {
380         while ( (nptr = strstr(newdesc, TUNITLABEL)) != NULL)
381         {
382             nptr[0] = '\0';
383             nptr   += NTUNIT;
384             len    += strlen(time_unit_str)-NTUNIT;
385             snew(ndesc, len);
386             strcpy(ndesc, newdesc);
387             strcat(ndesc, time_unit_str);
388             strcat(ndesc, nptr);
389             sfree(newdesc);
390             newdesc = ndesc;
391             ndesc   = NULL;
392         }
393     }
394 #undef TUNITLABEL
395 #undef NTUNIT
396
397     /* Add extra comment for enumerateds */
398     if (pa->type == etENUM)
399     {
400         strcat(newdesc, ": ");
401         for (k = 1; (pa->u.c[k] != NULL); k++)
402         {
403             strcat(newdesc, "[TT]");
404             strcat(newdesc, pa->u.c[k]);
405             strcat(newdesc, "[tt]");
406             /* Print a comma everywhere but at the last one */
407             if (pa->u.c[k+1] != NULL)
408             {
409                 if (pa->u.c[k+2] == NULL)
410                 {
411                     strcat(newdesc, " or ");
412                 }
413                 else
414                 {
415                     strcat(newdesc, ", ");
416                 }
417             }
418         }
419     }
420     return newdesc;
421 }
422
423
424 gmx_bool parse_common_args(int *argc, char *argv[], unsigned long Flags,
425                            int nfile, t_filenm fnm[], int npargs, t_pargs *pa,
426                            int ndesc, const char **desc,
427                            int nbugs, const char **bugs,
428                            output_env_t *oenv)
429 {
430     const char *manstr[] = {
431         NULL, "no", "help", "html", "nroff", "completion", NULL
432     };
433     /* This array should match the order of the enum in oenv.h */
434     const char *xvg_format[] = { NULL, "xmgrace", "xmgr", "none", NULL };
435     /* This array should match the order of the enum in oenv.h */
436     const char *time_units[] = {
437         NULL, "fs", "ps", "ns", "us", "ms", "s",
438         NULL
439     };
440     int         nicelevel = 0, debug_level = 0;
441     char       *deffnm    = NULL;
442     real        tbegin    = 0, tend = 0, tdelta = 0;
443     gmx_bool    bView     = FALSE;
444
445     t_pargs    *all_pa = NULL;
446
447 #ifdef __sgi
448     int     npri      = 0;
449     t_pargs npri_pa   = {
450         "-npri", FALSE, etINT,   {&npri},
451         "HIDDEN Set non blocking priority (try 128)"
452     };
453 #endif
454     t_pargs nice_pa   = {
455         "-nice", FALSE, etINT,   {&nicelevel},
456         "Set the nicelevel"
457     };
458     t_pargs deffnm_pa = {
459         "-deffnm", FALSE, etSTR, {&deffnm},
460         "Set the default filename for all file options"
461     };
462     t_pargs begin_pa  = {
463         "-b",    FALSE, etTIME,  {&tbegin},
464         "First frame (%t) to read from trajectory"
465     };
466     t_pargs end_pa    = {
467         "-e",    FALSE, etTIME,  {&tend},
468         "Last frame (%t) to read from trajectory"
469     };
470     t_pargs dt_pa     = {
471         "-dt",   FALSE, etTIME,  {&tdelta},
472         "Only use frame when t MOD dt = first time (%t)"
473     };
474     t_pargs view_pa   = {
475         "-w",    FALSE, etBOOL,  {&bView},
476         "View output [TT].xvg[tt], [TT].xpm[tt], [TT].eps[tt] and [TT].pdb[tt] files"
477     };
478     t_pargs xvg_pa    = {
479         "-xvg",  FALSE, etENUM,  {xvg_format},
480         "xvg plot formatting"
481     };
482     t_pargs time_pa   = {
483         "-tu",   FALSE, etENUM,  {time_units},
484         "Time unit"
485     };
486     /* Maximum number of extra arguments */
487 #define EXTRA_PA 16
488
489     t_pargs  pca_pa[] = {
490         { "-man",  FALSE, etENUM,  {manstr},
491           "HIDDENWrite manual and quit" },
492         { "-debug", FALSE, etINT, {&debug_level},
493           "HIDDENWrite file with debug information, 1: short, 2: also x and f" },
494     };
495 #define NPCA_PA asize(pca_pa)
496     gmx_bool bExit, bXvgr;
497     int      i, j, k, npall, max_pa;
498
499     // Handle the flags argument, which is a bit field
500     // The FF macro returns whether or not the bit is set
501 #define FF(arg) ((Flags & arg) == arg)
502
503     /* Check for double arguments */
504     for (i = 1; (i < *argc); i++)
505     {
506         if (argv[i] && (strlen(argv[i]) > 1) && (!std::isdigit(argv[i][1])))
507         {
508             for (j = i+1; (j < *argc); j++)
509             {
510                 if ( (argv[i][0] == '-') && (argv[j][0] == '-') &&
511                      (strcmp(argv[i], argv[j]) == 0) )
512                 {
513                     if (FF(PCA_NOEXIT_ON_ARGS))
514                     {
515                         fprintf(stderr, "Double command line argument %s\n",
516                                 argv[i]);
517                     }
518                     else
519                     {
520                         gmx_fatal(FARGS, "Double command line argument %s\n",
521                                   argv[i]);
522                     }
523                 }
524             }
525         }
526     }
527     debug_gmx();
528
529     /* Check ALL the flags ... */
530     max_pa = NPCA_PA + EXTRA_PA + npargs+1;
531     snew(all_pa, max_pa);
532
533     for (i = npall = 0; (i < static_cast<int>(NPCA_PA)); i++)
534     {
535         npall = add_parg(npall, all_pa, &(pca_pa[i]));
536     }
537
538 #ifdef __sgi
539     const char *envstr = getenv("GMXNPRIALL");
540     if (envstr)
541     {
542         npri = strtol(envstr, NULL, 10);
543     }
544     if (FF(PCA_BE_NICE))
545     {
546         envstr = getenv("GMXNPRI");
547         if (envstr)
548         {
549             npri = strtol(envstr, NULL, 10);
550         }
551     }
552     npall = add_parg(npall, all_pa, &npri_pa);
553 #endif
554
555     if (FF(PCA_BE_NICE))
556     {
557         nicelevel = 19;
558     }
559     npall = add_parg(npall, all_pa, &nice_pa);
560
561     if (FF(PCA_CAN_SET_DEFFNM))
562     {
563         npall = add_parg(npall, all_pa, &deffnm_pa);
564     }
565     if (FF(PCA_CAN_BEGIN))
566     {
567         npall = add_parg(npall, all_pa, &begin_pa);
568     }
569     if (FF(PCA_CAN_END))
570     {
571         npall = add_parg(npall, all_pa, &end_pa);
572     }
573     if (FF(PCA_CAN_DT))
574     {
575         npall = add_parg(npall, all_pa, &dt_pa);
576     }
577     if (FF(PCA_TIME_UNIT))
578     {
579         npall = add_parg(npall, all_pa, &time_pa);
580     }
581     if (FF(PCA_CAN_VIEW))
582     {
583         npall = add_parg(npall, all_pa, &view_pa);
584     }
585
586     bXvgr = FALSE;
587     for (i = 0; (i < nfile); i++)
588     {
589         bXvgr = bXvgr ||  (fnm[i].ftp == efXVG);
590     }
591     if (bXvgr)
592     {
593         npall = add_parg(npall, all_pa, &xvg_pa);
594     }
595
596     /* Now append the program specific arguments */
597     for (i = 0; (i < npargs); i++)
598     {
599         npall = add_parg(npall, all_pa, &(pa[i]));
600     }
601
602     /* set etENUM options to default */
603     for (i = 0; (i < npall); i++)
604     {
605         if (all_pa[i].type == etENUM)
606         {
607             all_pa[i].u.c[0] = all_pa[i].u.c[1];
608         }
609     }
610     set_default_time_unit(time_units, FF(PCA_TIME_UNIT));
611     set_default_xvg_format(xvg_format);
612
613     /* Now parse all the command-line options */
614     get_pargs(argc, argv, npall, all_pa, FF(PCA_KEEP_ARGS));
615
616     /* set program name, command line, and default values for output options */
617     output_env_init(oenv, *argc, argv, (time_unit_t)nenum(time_units), bView,
618                     (xvg_format_t)nenum(xvg_format), 0, debug_level);
619
620     if (FF(PCA_CAN_SET_DEFFNM) && (deffnm != NULL))
621     {
622         set_default_file_name(deffnm);
623     }
624
625     /* Parse the file args */
626     parse_file_args(argc, argv, nfile, fnm, FF(PCA_KEEP_ARGS), !FF(PCA_NOT_READ_NODE));
627
628     /* Open the debug file */
629     if (debug_level > 0)
630     {
631         char buf[256];
632
633         if (gmx_mpi_initialized())
634         {
635             sprintf(buf, "%s%d.debug", output_env_get_short_program_name(*oenv),
636                     gmx_node_rank());
637         }
638         else
639         {
640             sprintf(buf, "%s.debug", output_env_get_short_program_name(*oenv));
641         }
642
643         init_debug(debug_level, buf);
644         fprintf(stderr, "Opening debug file %s (src code file %s, line %d)\n",
645                 buf, __FILE__, __LINE__);
646     }
647
648     /* Now copy the results back... */
649     for (i = 0, k = npall-npargs; (i < npargs); i++, k++)
650     {
651         memcpy(&(pa[i]), &(all_pa[k]), (size_t)sizeof(pa[i]));
652     }
653
654
655     for (i = 0; (i < npall); i++)
656     {
657         all_pa[i].desc = mk_desc(&(all_pa[i]), output_env_get_time_unit(*oenv));
658     }
659
660     // To satisfy clang.
661     GMX_ASSERT(manstr[0] != NULL,
662                "Enum option default assignment should have changed this");
663     bExit = (strcmp(manstr[0], "no") != 0);
664
665 #if (defined __sgi && USE_SGI_FPE)
666     doexceptions();
667 #endif
668
669     /* Set the nice level */
670 #ifdef __sgi
671     if (npri != 0 && !bExit)
672     {
673         schedctl(MPTS_RTPRI, 0, npri);
674     }
675 #endif
676
677 #ifdef HAVE_UNISTD_H
678 #ifndef GMX_NO_NICE
679     /* The some system, e.g. the catamount kernel on cray xt3 do not have nice(2). */
680     if (nicelevel != 0 && !bExit)
681     {
682         static gmx_bool            nice_set   = FALSE; /* only set it once */
683         static tMPI_Thread_mutex_t init_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
684         tMPI_Thread_mutex_lock(&init_mutex);
685         if (!nice_set)
686         {
687             if (nice(nicelevel) == -1)
688             {
689                 /* Do nothing, but use the return value to avoid warnings. */
690             }
691             nice_set = TRUE;
692         }
693         tMPI_Thread_mutex_unlock(&init_mutex);
694     }
695 #endif
696 #endif
697
698     if (strcmp(manstr[0], "no") != 0 && !(FF(PCA_QUIET)))
699     {
700         if (!strcmp(manstr[0], "completion"))
701         {
702             /* one file each for csh, bash and zsh if we do completions */
703             write_man("completion-zsh", output_env_get_short_program_name(*oenv),
704                       ndesc, desc, nfile, fnm, npall, all_pa, nbugs, bugs);
705             write_man("completion-bash", output_env_get_short_program_name(*oenv),
706                       ndesc, desc, nfile, fnm, npall, all_pa, nbugs, bugs);
707             write_man("completion-csh", output_env_get_short_program_name(*oenv),
708                       ndesc, desc, nfile, fnm, npall, all_pa, nbugs, bugs);
709         }
710         else
711         {
712             write_man(manstr[0], output_env_get_short_program_name(*oenv),
713                       ndesc, desc, nfile, fnm, npall, all_pa, nbugs, bugs);
714         }
715     }
716
717     /* convert time options, must be done after printing! */
718
719     for (i = 0; i < npall; i++)
720     {
721         if ((all_pa[i].type == etTIME) && (*all_pa[i].u.r >= 0))
722         {
723             *all_pa[i].u.r *= output_env_get_time_invfactor(*oenv);
724         }
725     }
726
727     /* Extract Time info from arguments */
728     if (FF(PCA_CAN_BEGIN) && opt2parg_bSet("-b", npall, all_pa))
729     {
730         setTimeValue(TBEGIN, opt2parg_real("-b", npall, all_pa));
731     }
732
733     if (FF(PCA_CAN_END) && opt2parg_bSet("-e", npall, all_pa))
734     {
735         setTimeValue(TEND, opt2parg_real("-e", npall, all_pa));
736     }
737
738     if (FF(PCA_CAN_DT) && opt2parg_bSet("-dt", npall, all_pa))
739     {
740         setTimeValue(TDELTA, opt2parg_real("-dt", npall, all_pa));
741     }
742
743     /* clear memory */
744     for (i = 0; i < npall; ++i)
745     {
746         sfree((void *)all_pa[i].desc);
747     }
748     sfree(all_pa);
749
750     if (!FF(PCA_NOEXIT_ON_ARGS))
751     {
752         if (*argc > 1)
753         {
754             gmx_cmd(argv[1]);
755         }
756     }
757     return !bExit;
758 #undef FF
759 }