Rename format.h to more generic stringutil.h.
[alexxy/gromacs.git] / src / gromacs / selection / selhelp.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2009, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  *
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \internal \file
32  * \brief
33  * Implements functions in selhelp.h.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_selection
37  */
38 #include <string>
39 #include <vector>
40 #include <utility>
41
42 #include <boost/shared_ptr.hpp>
43
44 #include "gromacs/onlinehelp/helptopic.h"
45 #include "gromacs/utility/file.h"
46 #include "gromacs/utility/stringutil.h"
47
48 #include "selhelp.h"
49 #include "selmethod.h"
50 #include "symrec.h"
51
52 namespace
53 {
54
55 struct CommonHelpText
56 {
57     static const char name[];
58     static const char title[];
59     static const char *const text[];
60 };
61
62 const char CommonHelpText::name[] = "selections";
63 const char CommonHelpText::title[] =
64     "Selection syntax and usage";
65 const char *const CommonHelpText::text[] = {
66     "Selections are used to select atoms/molecules/residues for analysis.",
67     "In contrast to traditional index files, selections can be dynamic, i.e.,",
68     "select different atoms for different trajectory frames.[PAR]",
69
70     "Each analysis tool requires a different number of selections and the",
71     "selections are interpreted differently. The general idea is still the",
72     "same: each selection evaluates to a set of positions, where a position",
73     "can be an atom position or center-of-mass or center-of-geometry of",
74     "a set of atoms. The tool then uses these positions for its analysis to",
75     "allow very flexible processing. Some analysis tools may have limitations",
76     "on the types of selections allowed.[PAR]",
77
78     "To get started with selections, run, e.g., [TT][PROGRAM] select[tt]",
79     "without specifying selections on the command-line and use the interactive",
80     "prompt to try out different selections.",
81     "This tool provides output options that allow one to see what is actually",
82     "selected by the given selections, and the interactive prompt reports",
83     "syntax errors immediately, allowing one to try again.",
84     "The subtopics listed below give more details on different aspects of",
85     "selections.",
86 };
87
88 struct ArithmeticHelpText
89 {
90     static const char name[];
91     static const char title[];
92     static const char *const text[];
93 };
94
95 const char ArithmeticHelpText::name[] = "arithmetic";
96 const char ArithmeticHelpText::title[] =
97     "Arithmetic expressions in selections";
98 const char *const ArithmeticHelpText::text[] = {
99     "Basic arithmetic evaluation is supported for numeric expressions.",
100     "Supported operations are addition, subtraction, negation, multiplication,",
101     "division, and exponentiation (using ^).",
102     "Result of a division by zero or other illegal operations is undefined.",
103 };
104
105 struct CmdLineHelpText
106 {
107     static const char name[];
108     static const char title[];
109     static const char *const text[];
110 };
111
112 const char CmdLineHelpText::name[] = "cmdline";
113 const char CmdLineHelpText::title[] =
114     "Specifying selections from command line";
115 const char *const CmdLineHelpText::text[] = {
116     "If no selections are provided on the command line, you are prompted to",
117     "type the selections interactively (a pipe can also be used to provide",
118     "the selections in this case for most tools). While this works well for",
119     "testing, it is easier to provide the selections from the command line",
120     "if they are complex or for scripting.[PAR]",
121
122     "Each tool has different command-line arguments for specifying selections",
123     "(listed by [TT][PROGRAM] help <tool>[tt]).",
124     "You can either pass a single string containing all selections (separated",
125     "by semicolons), or multiple strings, each containing one selection.",
126     "Note that you need to quote the selections to protect them from the",
127     "shell.[PAR]",
128
129     "If you set a selection command-line argument, but do not provide any",
130     "selections, you are prompted to type the selections for that argument",
131     "interactively. This is useful if that selection argument is optional,",
132     "in which case it is not normally prompted for.[PAR]",
133
134     "To provide selections from a file, use [TT]-sf file.dat[tt] in the place",
135     "of the selection for a selection argument (e.g.,",
136     "[TT]-select -sf file.dat[tt]). In general, the [TT]-sf[tt] argument reads",
137     "selections from the provided file and assigns them to selection arguments",
138     "that have been specified up to that point, but for which no selections",
139     "have been provided.",
140     "As a special case, [TT]-sf[tt] provided on its own, without preceding",
141     "selection arguments, assigns the selections to all (yet unset) required",
142     "selections (i.e., those that would be promted interactively if no",
143     "selections are provided on the command line).[PAR]",
144
145     "To use groups from a traditional index file, use argument [TT]-n[tt]",
146     "to provide a file. See the \"syntax\" subtopic for how to use them.",
147     "If this option is not provided, default groups are generated.",
148     "The default groups are generated by reading selections from a file",
149     "[TT]defselection.dat[tt]. If such a file is found in the current",
150     "directory, it is used instead of the one provided by default.[PAR]",
151
152     "Depending on the tool, two additional command-line arguments may be",
153     "available to control the behavior:[BR]",
154     "1. [TT]-seltype[tt] can be used to specify the default type of",
155     "positions to calculate for each selection.[BR]",
156     "2. [TT]-selrpos[tt] can be used to specify the default type of",
157     "positions used in selecting atoms by coordinates.[BR]",
158     "See the \"positions\" subtopic for more information on these options.",
159 };
160
161 struct EvaluationHelpText
162 {
163     static const char name[];
164     static const char title[];
165     static const char *const text[];
166 };
167
168 const char EvaluationHelpText::name[] = "evaluation";
169 const char EvaluationHelpText::title[] =
170     "Selection evaluation and optimization";
171 const char *const EvaluationHelpText::text[] = {
172     "Boolean evaluation proceeds from left to right and is short-circuiting",
173     "i.e., as soon as it is known whether an atom will be selected, the",
174     "remaining expressions are not evaluated at all.",
175     "This can be used to optimize the selections: you should write the",
176     "most restrictive and/or the most inexpensive expressions first in",
177     "boolean expressions.",
178     "The relative ordering between dynamic and static expressions does not",
179     "matter: all static expressions are evaluated only once, before the first",
180     "frame, and the result becomes the leftmost expression.[PAR]",
181
182     "Another point for optimization is in common subexpressions: they are not",
183     "automatically recognized, but can be manually optimized by the use of",
184     "variables. This can have a big impact on the performance of complex",
185     "selections, in particular if you define several index groups like this:",
186     "  [TT]rdist = distance from com of resnr 1 to 5;[tt][BR]",
187     "  [TT]resname RES and rdist < 2;[tt][BR]",
188     "  [TT]resname RES and rdist < 4;[tt][BR]",
189     "  [TT]resname RES and rdist < 6;[tt][BR]",
190     "Without the variable assignment, the distances would be evaluated three",
191     "times, although they are exactly the same within each selection.",
192     "Anything assigned into a variable becomes a common subexpression that",
193     "is evaluated only once during a frame.",
194     "Currently, in some cases the use of variables can actually lead to a small",
195     "performance loss because of the checks necessary to determine for which",
196     "atoms the expression has already been evaluated, but this should not be",
197     "a major problem.",
198 };
199
200 struct ExamplesHelpText
201 {
202     static const char name[];
203     static const char title[];
204     static const char *const text[];
205 };
206
207 const char ExamplesHelpText::name[] = "examples";
208 const char ExamplesHelpText::title[] =
209     "Selection examples";
210 const char *const ExamplesHelpText::text[] = {
211     // TODO: Once there are more tools available, use examples that invoke
212     // tools and explain what the selections do in those tools.
213     "Below, examples of increasingly complex selections are given.[PAR]",
214
215     "Selection of all water oxygens:[BR]",
216     "  resname SOL and name OW",
217     "[PAR]",
218
219     "Centers of mass of residues 1 to 5 and 10:[BR]",
220     "  res_com of resnr 1 to 5 10",
221     "[PAR]",
222
223     "All atoms farther than 1 nm of a fixed position:[BR]",
224     "  not within 1 of (1.2, 3.1, 2.4)",
225     "[PAR]",
226
227     "All atoms of a residue LIG within 0.5 nm of a protein (with a custom name):[BR]",
228     "  \"Close to protein\" resname LIG and within 0.5 of group \"Protein\"",
229     "[PAR]",
230
231     "All protein residues that have at least one atom within 0.5 nm of a residue LIG:[BR]",
232     "  group \"Protein\" and same residue as within 0.5 of resname LIG",
233     "[PAR]",
234
235     "All RES residues whose COM is between 2 and 4 nm from the COM of all of them:[BR]",
236     "  rdist = res_com distance from com of resname RES[BR]",
237     "  resname RES and rdist >= 2 and rdist <= 4",
238     "[PAR]",
239
240     "Selection like C1 C2 C2 C3 C3 C4 ... C8 C9 (e.g., for g_bond):[BR]",
241     "  name \"C[1-8]\" merge name \"C[2-9]\"",
242 };
243
244 struct KeywordsHelpText
245 {
246     static const char name[];
247     static const char title[];
248     static const char *const text[];
249 };
250
251 const char KeywordsHelpText::name[] = "keywords";
252 const char KeywordsHelpText::title[] =
253     "Selection keywords";
254 const char *const KeywordsHelpText::text[] = {
255     "The following selection keywords are currently available.",
256     "For keywords marked with a star, additional help is available through",
257     "a subtopic KEYWORD, where KEYWORD is the name of the keyword.",
258 };
259
260 struct LimitationsHelpText
261 {
262     static const char name[];
263     static const char title[];
264     static const char *const text[];
265 };
266
267 const char LimitationsHelpText::name[] = "limitations";
268 const char LimitationsHelpText::title[] =
269     "Selection limitations";
270 const char *const LimitationsHelpText::text[] = {
271     "Some analysis programs may require a special structure for the input",
272     "selections (e.g., [TT]g_angle[tt] requires the index group to be made",
273     "of groups of three or four atoms).",
274     "For such programs, it is up to the user to provide a proper selection",
275     "expression that always returns such positions.",
276     "[PAR]",
277
278     "Due to technical reasons, having a negative value as the first value in",
279     "expressions like[BR]",
280     "[TT]charge -1 to -0.7[tt][BR]",
281     "result in a syntax error. A workaround is to write[BR]",
282     "[TT]charge {-1 to -0.7}[tt][BR]",
283     "instead.",
284 };
285
286 struct PositionsHelpText
287 {
288     static const char name[];
289     static const char title[];
290     static const char *const text[];
291 };
292
293 const char PositionsHelpText::name[] = "positions";
294 const char PositionsHelpText::title[] =
295     "Specifying positions in selections";
296 const char *const PositionsHelpText::text[] = {
297     "Possible ways of specifying positions in selections are:[PAR]",
298
299     "1. A constant position can be defined as [TT][XX, YY, ZZ][tt], where",
300     "[TT]XX[tt], [TT]YY[tt] and [TT]ZZ[tt] are real numbers.[PAR]",
301
302     "2. [TT]com of ATOM_EXPR [pbc][tt] or [TT]cog of ATOM_EXPR [pbc][tt]",
303     "calculate the center of mass/geometry of [TT]ATOM_EXPR[tt]. If",
304     "[TT]pbc[tt] is specified, the center is calculated iteratively to try",
305     "to deal with cases where [TT]ATOM_EXPR[tt] wraps around periodic",
306     "boundary conditions.[PAR]",
307
308     "3. [TT]POSTYPE of ATOM_EXPR[tt] calculates the specified positions for",
309     "the atoms in [TT]ATOM_EXPR[tt].",
310     "[TT]POSTYPE[tt] can be [TT]atom[tt], [TT]res_com[tt], [TT]res_cog[tt],",
311     "[TT]mol_com[tt] or [TT]mol_cog[tt], with an optional prefix [TT]whole_[tt]",
312     "[TT]part_[tt] or [TT]dyn_[tt].",
313     "[TT]whole_[tt] calculates the centers for the whole residue/molecule,",
314     "even if only part of it is selected.",
315     "[TT]part_[tt] prefix calculates the centers for the selected atoms, but",
316     "uses always the same atoms for the same residue/molecule. The used atoms",
317     "are determined from the the largest group allowed by the selection.",
318     "[TT]dyn_[tt] calculates the centers strictly only for the selected atoms.",
319     "If no prefix is specified, whole selections default to [TT]part_[tt] and",
320     "other places default to [TT]whole_[tt].",
321     "The latter is often desirable to select the same molecules in different",
322     "tools, while the first is a compromise between speed ([TT]dyn_[tt]",
323     "positions can be slower to evaluate than [TT]part_[tt]) and intuitive",
324     "behavior.[PAR]",
325
326     "4. [TT]ATOM_EXPR[tt], when given for whole selections, is handled as 3.",
327     "above, using the position type from the command-line argument",
328     "[TT]-seltype[tt].[PAR]",
329
330     "Selection keywords that select atoms based on their positions, such as",
331     "[TT]dist from[tt], use by default the positions defined by the",
332     "[TT]-selrpos[tt] command-line option.",
333     "This can be overridden by prepending a [TT]POSTYPE[tt] specifier to the",
334     "keyword. For example, [TT]res_com dist from POS[tt] evaluates the",
335     "residue center of mass distances. In the example, all atoms of a residue",
336     "are either selected or not, based on the single distance calculated.",
337 };
338
339 struct SyntaxHelpText
340 {
341     static const char name[];
342     static const char title[];
343     static const char *const text[];
344 };
345
346 const char SyntaxHelpText::name[] = "syntax";
347 const char SyntaxHelpText::title[] =
348     "Selection syntax";
349 const char *const SyntaxHelpText::text[] = {
350     "A set of selections consists of one or more selections, separated by",
351     "semicolons. Each selection defines a set of positions for the analysis.",
352     "Each selection can also be preceded by a string that gives a name for",
353     "the selection for use in, e.g., graph legends.",
354     "If no name is provided, the string used for the selection is used",
355     "automatically as the name.[PAR]",
356
357     "For interactive input, the syntax is slightly altered: line breaks can",
358     "also be used to separate selections. \\ followed by a line break can",
359     "be used to continue a line if necessary.",
360     "Notice that the above only applies to real interactive input,",
361     "not if you provide the selections, e.g., from a pipe.[PAR]",
362
363     "It is possible to use variables to store selection expressions.",
364     "A variable is defined with the following syntax:[BR]",
365     "[TT]VARNAME = EXPR ;[tt][BR]",
366     "where [TT]EXPR[tt] is any valid selection expression.",
367     "After this, [TT]VARNAME[tt] can be used anywhere where [TT]EXPR[tt]",
368     "would be valid.[PAR]",
369
370     "Selections are composed of three main types of expressions, those that",
371     "define atoms ([TT]ATOM_EXPR[tt]s), those that define positions",
372     "([TT]POS_EXPR[tt]s), and those that evaluate to numeric values",
373     "([TT]NUM_EXPR[tt]s). Each selection should be a [TT]POS_EXPR[tt]",
374     "or a [TT]ATOM_EXPR[tt] (the latter is automatically converted to",
375     "positions). The basic rules are as follows:[BR]",
376     "1. An expression like [TT]NUM_EXPR1 < NUM_EXPR2[tt] evaluates to an",
377     "[TT]ATOM_EXPR[tt] that selects all the atoms for which the comparison",
378     "is true.[BR]",
379     "2. Atom expressions can be combined with boolean operations such as",
380     "[TT]not ATOM_EXPR[tt], [TT]ATOM_EXPR and ATOM_EXPR[tt], or",
381     "[TT]ATOM_EXPR or ATOM_EXPR[tt]. Parentheses can be used to alter the",
382     "evaluation order.[BR]",
383     "3. [TT]ATOM_EXPR[tt] expressions can be converted into [TT]POS_EXPR[tt]",
384     "expressions in various ways, see the \"positions\" subtopic for more",
385     "details.[PAR]",
386
387     "Some keywords select atoms based on string values such as the atom name.",
388     "For these keywords, it is possible to use wildcards ([TT]name \"C*\"[tt])",
389     "or regular expressions (e.g., [TT]resname \"R[AB]\"[tt]).",
390     "The match type is automatically guessed from the string: if it contains",
391     "other characters than letters, numbers, '*', or '?', it is interpreted",
392     "as a regular expression.",
393     "Strings that contain non-alphanumeric characters should be enclosed in",
394     "double quotes as in the examples. For other strings, the quotes are",
395     "optional, but if the value conflicts with a reserved keyword, a syntax",
396     "error will occur. If your strings contain uppercase letters, this should",
397     "not happen.[PAR]",
398
399     "Index groups provided with the [TT]-n[tt] command-line option or",
400     "generated by default can be accessed with [TT]group NR[tt] or",
401     "[TT]group NAME[tt], where [TT]NR[tt] is a zero-based index of the group",
402     "and [TT]NAME[tt] is part of the name of the desired group.",
403     "The keyword [TT]group[tt] is optional if the whole selection is",
404     "provided from an index group.",
405     "To see a list of available groups in the interactive mode, press enter",
406     "in the beginning of a line.",
407 };
408
409 } // namespace
410
411 namespace gmx
412 {
413
414 namespace
415 {
416
417 /*! \internal \brief
418  * Help topic implementation for an individual selection method.
419  *
420  * \ingroup module_selection
421  */
422 class KeywordDetailsHelpTopic : public AbstractSimpleHelpTopic
423 {
424     public:
425         //! Initialize help topic for the given selection method.
426         explicit KeywordDetailsHelpTopic(const gmx_ana_selmethod_t &method)
427             : method_(method)
428         {
429         }
430
431         virtual const char *name() const
432         {
433             return method_.name;
434         }
435         virtual const char *title() const
436         {
437             return NULL;
438         }
439
440     protected:
441         virtual std::string helpText() const
442         {
443             return concatenateStrings(method_.help.help, method_.help.nlhelp);
444         }
445
446     private:
447         const gmx_ana_selmethod_t &method_;
448
449         GMX_DISALLOW_COPY_AND_ASSIGN(KeywordDetailsHelpTopic);
450 };
451
452 /*! \internal \brief
453  * Custom help topic for printing a list of selection keywords.
454  *
455  * \ingroup module_selection
456  */
457 class KeywordsHelpTopic : public CompositeHelpTopic<KeywordsHelpText>
458 {
459     public:
460         KeywordsHelpTopic();
461
462         virtual void writeHelp(File *file) const;
463
464     private:
465         /*! \brief
466          * Container for known selection methods.
467          *
468          * The first item in the pair is the name of the selection method, and
469          * the second points to the static data structure that describes the
470          * method.
471          * The name in the first item may differ from the name of the static
472          * data structure if an alias is defined for that method.
473          */
474         typedef std::vector<std::pair<std::string,
475                                       const gmx_ana_selmethod_t *> >
476                 MethodList;
477
478         /*! \brief
479          * Prints a brief list of keywords (selection methods) available.
480          *
481          * \param[in] file  Where to write the list.
482          * \param[in] type  Only methods that return this type are printed.
483          * \param[in] bModifiers  If false, \ref SMETH_MODIFIER methods are
484          *      excluded, otherwise only them are printed.
485          */
486         void printKeywordList(File *file, e_selvalue_t type, bool bModifiers) const;
487
488         MethodList              methods_;
489 };
490
491 KeywordsHelpTopic::KeywordsHelpTopic()
492 {
493     // TODO: This is not a very elegant way of getting the list of selection
494     // methods, but this needs to be rewritten in any case if/when #652 is
495     // implemented.
496     gmx_sel_symtab_t *symtab;
497     _gmx_sel_symtab_create(&symtab);
498     gmx_ana_selmethod_register_defaults(symtab);
499     boost::shared_ptr<gmx_sel_symtab_t> symtabGuard(symtab, &_gmx_sel_symtab_free);
500
501     gmx_sel_symrec_t *symbol = _gmx_sel_first_symbol(symtab, SYMBOL_METHOD);
502     while (symbol)
503     {
504         const char *symname = _gmx_sel_sym_name(symbol);
505         const gmx_ana_selmethod_t *method = _gmx_sel_sym_value_method(symbol);
506         methods_.push_back(std::make_pair(std::string(symname), method));
507         if (method->help.nlhelp > 0 && method->help.help != NULL)
508         {
509             addSubTopic(HelpTopicPointer(new KeywordDetailsHelpTopic(*method)));
510         }
511         symbol = _gmx_sel_next_symbol(symbol, SYMBOL_METHOD);
512     }
513 }
514
515 void KeywordsHelpTopic::writeHelp(File *file) const
516 {
517     writeBasicHelpTopic(file, *this, helpText());
518
519     /* Print the list of keywords */
520     file->writeLine();
521     file->writeLine("Keywords that select atoms by an integer property:");
522     file->writeLine("(use in expressions or like \"atomnr 1 to 5 7 9\")");
523     printKeywordList(file, INT_VALUE, false);
524
525     file->writeLine();
526     file->writeLine("Keywords that select atoms by a numeric property:");
527     file->writeLine("(use in expressions or like \"occupancy 0.5 to 1\")");
528     printKeywordList(file, REAL_VALUE, false);
529
530     file->writeLine();
531     file->writeLine("Keywords that select atoms by a string property:");
532     file->writeLine("(use like \"name PATTERN [PATTERN] ...\")");
533     printKeywordList(file, STR_VALUE, false);
534
535     file->writeLine();
536     file->writeLine("Additional keywords that directly select atoms:");
537     printKeywordList(file, GROUP_VALUE, false);
538
539     file->writeLine();
540     file->writeLine("Keywords that directly evaluate to positions:");
541     file->writeLine("(see also \"positions\" subtopic)");
542     printKeywordList(file, POS_VALUE, false);
543
544     file->writeLine();
545     file->writeLine("Additional keywords:");
546     printKeywordList(file, POS_VALUE, true);
547     printKeywordList(file, NO_VALUE, true);
548 }
549
550 void KeywordsHelpTopic::printKeywordList(File *file, e_selvalue_t type,
551                                          bool bModifiers) const
552 {
553     MethodList::const_iterator iter;
554     for (iter = methods_.begin(); iter != methods_.end(); ++iter)
555     {
556         const gmx_ana_selmethod_t &method = *iter->second;
557         bool bIsModifier = (method.flags & SMETH_MODIFIER) != 0;
558         if (method.type == type && bModifiers == bIsModifier)
559         {
560             bool bHasHelp = (method.help.nlhelp > 0 && method.help.help != NULL);
561             file->writeString(formatString(" %c ", bHasHelp ? '*' : ' '));
562             if (method.help.syntax != NULL)
563             {
564                 file->writeLine(method.help.syntax);
565             }
566             else
567             {
568                 const std::string &symname = iter->first;
569                 file->writeString(symname);
570                 if (symname != method.name)
571                 {
572                     file->writeString(formatString(" (synonym for %s)", method.name));
573                 }
574                 file->writeLine();
575             }
576         }
577     }
578 }
579
580 } // namespace
581
582 /*! \cond internal */
583 HelpTopicPointer createSelectionHelpTopic()
584 {
585     CompositeHelpTopicPointer root(new CompositeHelpTopic<CommonHelpText>);
586     root->registerSubTopic<SimpleHelpTopic<ArithmeticHelpText> >();
587     root->registerSubTopic<SimpleHelpTopic<CmdLineHelpText> >();
588     root->registerSubTopic<SimpleHelpTopic<EvaluationHelpText> >();
589     root->registerSubTopic<SimpleHelpTopic<ExamplesHelpText> >();
590     root->registerSubTopic<KeywordsHelpTopic>();
591     root->registerSubTopic<SimpleHelpTopic<LimitationsHelpText> >();
592     root->registerSubTopic<SimpleHelpTopic<PositionsHelpText> >();
593     root->registerSubTopic<SimpleHelpTopic<SyntaxHelpText> >();
594     return move(root);
595 }
596 //! \cond
597
598 } // namespace gmx