Merge release-4-6 into master
[alexxy/gromacs.git] / src / gromacs / selection / sm_keywords.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 internal selection methods for numeric and string keyword
34  * evaluation.
35  *
36  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
37  * \ingroup module_selection
38  */
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif
42
43 #include <ctype.h>
44 #ifdef HAVE_SYS_TYPES_H
45 #include <sys/types.h>  /*old Mac needs types before regex.h*/
46 #endif
47 #ifdef HAVE_REGEX_H
48 #include <regex.h>
49 #define USE_REGEX
50 #endif
51
52 #include <string>
53
54 #include "gromacs/legacyheaders/macros.h"
55 #include "gromacs/legacyheaders/smalloc.h"
56 #include "gromacs/legacyheaders/string2.h"
57
58 #include "gromacs/selection/selmethod.h"
59 #include "gromacs/utility/exceptions.h"
60 #include "gromacs/utility/messagestringcollector.h"
61 #include "gromacs/utility/stringutil.h"
62
63 #include "keywords.h"
64 #include "parsetree.h"
65 #include "scanner.h"
66 #include "selelem.h"
67
68 /** Allocates data for integer keyword evaluation. */
69 static void *
70 init_data_kwint(int npar, gmx_ana_selparam_t *param);
71 /** Allocates data for real keyword evaluation. */
72 static void *
73 init_data_kwreal(int npar, gmx_ana_selparam_t *param);
74 /** Allocates data for string keyword evaluation. */
75 static void *
76 init_data_kwstr(int npar, gmx_ana_selparam_t *param);
77 /** Initializes data for integer keyword evaluation. */
78 static void
79 init_kwint(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
80 /** Initializes data for real keyword evaluation. */
81 static void
82 init_kwreal(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
83 /** Initializes data for string keyword evaluation. */
84 static void
85 init_kwstr(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
86 /** Frees the memory allocated for string keyword evaluation. */
87 static void
88 free_data_kwstr(void *data);
89 /** Evaluates integer selection keywords. */
90 static void
91 evaluate_keyword_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
92                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
93 /** Evaluates real selection keywords. */
94 static void
95 evaluate_keyword_real(t_topology *top, t_trxframe *fr, t_pbc *pbc,
96                       gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
97 /** Evaluates string selection keywords. */
98 static void
99 evaluate_keyword_str(t_topology *top, t_trxframe *fr, t_pbc *pbc,
100                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
101
102 /*! \internal \brief
103  * Data structure for integer keyword expression evaluation.
104  */
105 typedef struct t_methoddata_kwint
106 {
107     /** Array of values for the keyword. */
108     int               *v;
109     /** Number of ranges in the \p r array. */
110     int                n;
111     /*! \brief
112      * Array of sorted integer ranges to match against.
113      *
114      * Each range is made of two integers, giving the endpoints (inclusive).
115      * This field stores the pointer to the ranges allocated by the
116      * parameter parser; see \ref SPAR_RANGES for more information.
117      */
118     int               *r;
119 } t_methoddata_kwint;
120
121 /*! \internal \brief
122  * Data structure for real keyword expression evaluation.
123  */
124 typedef struct t_methoddata_kwreal
125 {
126     /** Array of values for the keyword. */
127     real              *v;
128     /** Number of ranges in the \p r array. */
129     int                n;
130     /*! \brief
131      * Array of sorted ranges to match against.
132      *
133      * Each range is made of two values, giving the endpoints (inclusive).
134      * This field stores the pointer to the ranges allocated by the
135      * parameter parser; see \ref SPAR_RANGES for more information.
136      */
137     real              *r;
138 } t_methoddata_kwreal;
139
140 /*! \internal \brief
141  * Data structure for string keyword expression evaluation.
142  */
143 typedef struct t_methoddata_kwstr
144 {
145     /** Matching type for the strings. */
146     gmx::SelectionStringMatchType       matchType;
147     /** Array of values for the keyword. */
148     char             **v;
149     /** Number of elements in the \p val array. */
150     int                n;
151     /*! \internal \brief
152      * Array of strings/regular expressions to match against.
153      */
154     struct t_methoddata_kwstr_match {
155         /** true if the expression is a regular expression, false otherwise. */
156         bool           bRegExp;
157         /** The value to match against. */
158         union {
159 #ifdef USE_REGEX
160             /** Compiled regular expression if \p bRegExp is true. */
161             regex_t    r;
162 #endif
163             /** The string if \p bRegExp is false; */
164             const char *s;
165         }              u;
166     }                 *m;
167     /**< Array of strings/regular expressions to match against.*/
168 } t_methoddata_kwstr;
169
170 /** Parameters for integer keyword evaluation. */
171 static gmx_ana_selparam_t smparams_keyword_int[] = {
172     {NULL, {INT_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL},
173     {NULL, {INT_VALUE, -1, {NULL}}, NULL, SPAR_RANGES | SPAR_VARNUM},
174 };
175
176 /** Parameters for real keyword evaluation. */
177 static gmx_ana_selparam_t smparams_keyword_real[] = {
178     {NULL, {REAL_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL | SPAR_DYNAMIC},
179     {NULL, {REAL_VALUE, -1, {NULL}}, NULL, SPAR_RANGES | SPAR_VARNUM},
180 };
181
182 /** Parameters for string keyword evaluation. */
183 static gmx_ana_selparam_t smparams_keyword_str[] = {
184     {NULL, {STR_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL},
185     {NULL, {STR_VALUE, -1, {NULL}}, NULL, SPAR_VARNUM},
186 };
187
188 /** \internal Selection method data for integer keyword evaluation. */
189 gmx_ana_selmethod_t sm_keyword_int = {
190     "kw_int", GROUP_VALUE, SMETH_SINGLEVAL,
191     asize(smparams_keyword_int), smparams_keyword_int,
192     &init_data_kwint,
193      NULL,
194     &init_kwint,
195      NULL,
196      NULL,
197      NULL,
198     &evaluate_keyword_int,
199      NULL,
200     {NULL, 0, NULL},
201 };
202
203 /** \internal Selection method data for real keyword evaluation. */
204 gmx_ana_selmethod_t sm_keyword_real = {
205     "kw_real", GROUP_VALUE, SMETH_SINGLEVAL,
206     asize(smparams_keyword_real), smparams_keyword_real,
207     &init_data_kwreal,
208      NULL,
209     &init_kwreal,
210      NULL,
211      NULL,
212      NULL,
213     &evaluate_keyword_real,
214      NULL,
215     {NULL, 0, NULL},
216 };
217
218 /** \internal Selection method data for string keyword evaluation. */
219 gmx_ana_selmethod_t sm_keyword_str = {
220     "kw_str", GROUP_VALUE, SMETH_SINGLEVAL,
221     asize(smparams_keyword_str), smparams_keyword_str,
222     &init_data_kwstr,
223      NULL,
224     &init_kwstr,
225      NULL,
226     &free_data_kwstr,
227      NULL,
228     &evaluate_keyword_str,
229      NULL,
230     {NULL, 0, NULL},
231 };
232
233 /** Initializes keyword evaluation for an arbitrary group. */
234 static void
235 init_kweval(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
236 /** Initializes output for keyword evaluation in an arbitrary group. */
237 static void
238 init_output_kweval(t_topology *top, gmx_ana_selvalue_t *out, void *data);
239 /** Frees the data allocated for keyword evaluation in an arbitrary group. */
240 static void
241 free_data_kweval(void *data);
242 /** Initializes frame evaluation for keyword evaluation in an arbitrary group. */
243 static void
244 init_frame_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data);
245 /** Evaluates keywords in an arbitrary group. */
246 static void
247 evaluate_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc,
248                 gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
249
250 /*! \internal \brief
251  * Data structure for keyword evaluation in arbitrary groups.
252  */
253 typedef struct
254 {
255     /** Wrapped keyword method for evaluating the values. */
256     gmx_ana_selmethod_t  *kwmethod;
257     /** Method data for \p kwmethod. */
258     void                 *kwmdata;
259     /** Group in which \p kwmethod should be evaluated. */
260     gmx_ana_index_t       g;
261 } t_methoddata_kweval;
262
263 /** Parameters for keyword evaluation in an arbitrary group. */
264 static gmx_ana_selparam_t smparams_kweval[] = {
265     {NULL,   {GROUP_VALUE, 1, {NULL}}, NULL, SPAR_DYNAMIC},
266 };
267
268
269 /********************************************************************
270  * INTEGER KEYWORD EVALUATION
271  ********************************************************************/
272
273 /*!
274  * \param[in] npar  Not used.
275  * \param     param Not used.
276  * \returns   Pointer to the allocated data (\ref t_methoddata_kwint).
277  *
278  * Allocates memory for a \ref t_methoddata_kwint structure.
279  */
280 static void *
281 init_data_kwint(int npar, gmx_ana_selparam_t *param)
282 {
283     t_methoddata_kwint *data;
284
285     snew(data, 1);
286     return data;
287 }
288
289 /*!
290  * \param[in] top   Not used.
291  * \param[in] npar  Not used (should be 2).
292  * \param[in] param Method parameters (should point to \ref smparams_keyword_int).
293  * \param[in] data  Should point to \ref t_methoddata_kwint.
294  */
295 static void
296 init_kwint(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
297 {
298     t_methoddata_kwint *d = (t_methoddata_kwint *)data;
299
300     d->v = param[0].val.u.i;
301     d->n = param[1].val.nr;
302     d->r = param[1].val.u.i;
303 }
304
305 /*!
306  * See sel_updatefunc() for description of the parameters.
307  * \p data should point to a \c t_methoddata_kwint.
308  *
309  * Does a binary search to find which atoms match the ranges in the
310  * \c t_methoddata_kwint structure for this selection.
311  * Matching atoms are stored in \p out->u.g.
312  */
313 static void
314 evaluate_keyword_int(t_topology *top, t_trxframe *fr, t_pbc *pbc,
315                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
316 {
317     t_methoddata_kwint *d = (t_methoddata_kwint *)data;
318     int                 n, i, j, jmin, jmax;
319     int                 val;
320
321     out->u.g->isize = 0;
322     n    = d->n;
323     for (i = 0; i < g->isize; ++i)
324     {
325         val = d->v[i];
326         if (d->r[0] > val || d->r[2*n-1] < val)
327         {
328             continue;
329         }
330         jmin = 0;
331         jmax = n;
332         while (jmax - jmin > 1)
333         {
334             j = jmin + (jmax - jmin) / 2;
335             if (val < d->r[2*j])
336             {
337                 jmax = j;
338             }
339             else
340             {
341                 jmin = j;
342                 if (val <= d->r[2*j+1])
343                 {
344                     break;
345                 }
346                 /* ++jmin;*/
347             }
348         }
349         if (val <= d->r[2*jmin+1])
350         {
351             out->u.g->index[out->u.g->isize++] = g->index[i];
352         }
353     }
354 }
355
356
357 /********************************************************************
358  * REAL KEYWORD EVALUATION
359  ********************************************************************/
360
361 /*!
362  * \param[in] npar  Not used.
363  * \param     param Not used.
364  * \returns   Pointer to the allocated data (\ref t_methoddata_kwreal).
365  *
366  * Allocates memory for a \ref t_methoddata_kwreal structure.
367  */
368 static void *
369 init_data_kwreal(int npar, gmx_ana_selparam_t *param)
370 {
371     t_methoddata_kwreal *data;
372
373     snew(data, 1);
374     return data;
375 }
376
377 /*!
378  * \param[in] top   Not used.
379  * \param[in] npar  Not used (should be 2).
380  * \param[in] param Method parameters (should point to \ref smparams_keyword_real).
381  * \param[in] data  Should point to \ref t_methoddata_kwreal.
382  * \returns   0 (the initialization always succeeds).
383  */
384 static void
385 init_kwreal(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
386 {
387     t_methoddata_kwreal *d = (t_methoddata_kwreal *)data;
388
389     d->v = param[0].val.u.r;
390     d->n = param[1].val.nr;
391     d->r = param[1].val.u.r;
392 }
393
394 /*!
395  * See sel_updatefunc() for description of the parameters.
396  * \p data should point to a \c t_methoddata_kwreal.
397  *
398  * Does a binary search to find which atoms match the ranges in the
399  * \c t_methoddata_kwreal structure for this selection.
400  * Matching atoms are stored in \p out->u.g.
401  */
402 static void
403 evaluate_keyword_real(t_topology *top, t_trxframe *fr, t_pbc *pbc,
404                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
405 {
406     t_methoddata_kwreal *d = (t_methoddata_kwreal *)data;
407     int                  n, i, j, jmin, jmax;
408     real                 val;
409
410     out->u.g->isize = 0;
411     n    = d->n;
412     for (i = 0; i < g->isize; ++i)
413     {
414         val = d->v[i];
415         if (d->r[0] > val || d->r[2*n-1] < val)
416         {
417             continue;
418         }
419         jmin = 0;
420         jmax = n;
421         while (jmax - jmin > 1)
422         {
423             j = jmin + (jmax - jmin) / 2;
424             if (val < d->r[2*j])
425             {
426                 jmax = j;
427             }
428             else
429             {
430                 jmin = j;
431                 if (val <= d->r[2*j+1])
432                 {
433                     break;
434                 }
435                 /* ++jmin;*/
436             }
437         }
438         if (val <= d->r[2*jmin+1])
439         {
440             out->u.g->index[out->u.g->isize++] = g->index[i];
441         }
442     }
443 }
444
445
446 /********************************************************************
447  * STRING KEYWORD EVALUATION
448  ********************************************************************/
449
450 /*!
451  * \param[in] npar  Not used.
452  * \param     param Not used.
453  * \returns Pointer to the allocated data (\ref t_methoddata_kwstr).
454  *
455  * Allocates memory for a \ref t_methoddata_kwstr structure.
456  */
457 static void *
458 init_data_kwstr(int npar, gmx_ana_selparam_t *param)
459 {
460     t_methoddata_kwstr *data;
461
462     snew(data, 1);
463     data->matchType = gmx::eStringMatchType_Auto;
464     return data;
465 }
466
467 /*!
468  * \param[in,out] sel   Selection element to initialize.
469  * \param[in]     matchType  Method to use to match string values.
470  *
471  * Sets the string matching method for string keyword matching.
472  */
473 void
474 _gmx_selelem_set_kwstr_match_type(const gmx::SelectionTreeElementPointer &sel,
475                                   gmx::SelectionStringMatchType matchType)
476 {
477     t_methoddata_kwstr *d = (t_methoddata_kwstr *)sel->u.expr.mdata;
478
479     if (sel->type != SEL_EXPRESSION || !sel->u.expr.method
480         || sel->u.expr.method->name != sm_keyword_str.name)
481     {
482         return;
483     }
484     d->matchType = matchType;
485 }
486
487 /*!
488  * \param[in] top   Not used.
489  * \param[in] npar  Not used (should be 2).
490  * \param[in] param Method parameters (should point to \ref smparams_keyword_str).
491  * \param[in] data  Should point to \ref t_methoddata_kwstr.
492  */
493 static void
494 init_kwstr(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
495 {
496     t_methoddata_kwstr *d = (t_methoddata_kwstr *)data;
497     int                 i;
498
499     d->v   = param[0].val.u.s;
500     d->n   = param[1].val.nr;
501     /* Return if this is not the first time */
502     if (d->m)
503     {
504         return;
505     }
506     snew(d->m, d->n);
507     for (i = 0; i < d->n; ++i)
508     {
509         const char *s = param[1].val.u.s[i];
510         bool bRegExp = (d->matchType == gmx::eStringMatchType_RegularExpression);
511         if (d->matchType == gmx::eStringMatchType_Auto)
512         {
513             for (size_t j = 0; j < strlen(s); ++j)
514             {
515                 if (ispunct(s[j]) && s[j] != '?' && s[j] != '*')
516                 {
517                     bRegExp = true;
518                     break;
519                 }
520             }
521         }
522         if (bRegExp)
523         {
524 #ifdef USE_REGEX
525             std::string buf(gmx::formatString("^%s$", s));
526             if (regcomp(&d->m[i].u.r, buf.c_str(), REG_EXTENDED | REG_NOSUB) != 0)
527             {
528                 GMX_THROW(gmx::InvalidInputError(gmx::formatString(
529                                 "Error in regular expression \"%s\"", s)));
530             }
531 #else
532             GMX_THROW(gmx::InvalidInputError(gmx::formatString(
533                             "No regular expression support, cannot match \"%s\"", s)));
534 #endif
535         }
536         else
537         {
538             d->m[i].u.s = s;
539         }
540         d->m[i].bRegExp = bRegExp;
541     }
542 }
543
544 /*!
545  * \param data Data to free (should point to a \ref t_methoddata_kwstr).
546  *
547  * Frees the memory allocated for t_methoddata_kwstr::val.
548  */
549 static void
550 free_data_kwstr(void *data)
551 {
552     t_methoddata_kwstr *d = (t_methoddata_kwstr *)data;
553     int                 i;
554
555     for (i = 0; i < d->n; ++i)
556     {
557         if (d->m[i].bRegExp)
558         {
559 #ifdef USE_REGEX
560             /* This branch should only be taken if regular expressions
561              * are available, but the ifdef is still needed. */
562             regfree(&d->m[i].u.r);
563 #endif
564         }
565     }
566     sfree(d->m);
567     sfree(d);
568 }
569
570 /*!
571  * See sel_updatefunc() for description of the parameters.
572  * \p data should point to a \c t_methoddata_kwstr.
573  *
574  * Does a linear search to find which atoms match the strings in the
575  * \c t_methoddata_kwstr structure for this selection.
576  * Wildcards are allowed in the strings.
577  * Matching atoms are stored in \p out->u.g.
578  */
579 static void
580 evaluate_keyword_str(t_topology *top, t_trxframe *fr, t_pbc *pbc,
581                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
582 {
583     t_methoddata_kwstr *d = (t_methoddata_kwstr *)data;
584     int                 i, j;
585     bool                bFound;
586
587     out->u.g->isize = 0;
588     for (i = 0; i < g->isize; ++i)
589     {
590         bFound = false;
591         for (j = 0; j < d->n && !bFound; ++j)
592         {
593             if (d->m[j].bRegExp)
594             {
595 #ifdef USE_REGEX
596                 /* This branch should only be taken if regular expressions
597                  * are available, but the ifdef is still needed. */
598                 bFound = (regexec(&d->m[j].u.r, d->v[i], 0, NULL, 0) == 0);
599 #endif
600             }
601             else if (d->matchType == gmx::eStringMatchType_Exact)
602             {
603                 bFound = (strcmp(d->m[j].u.s, d->v[i]) == 0);
604             }
605             else
606             {
607                 bFound = (gmx_wcmatch(d->m[j].u.s, d->v[i]) == 0);
608             }
609         }
610         if (bFound)
611         {
612             out->u.g->index[out->u.g->isize++] = g->index[i];
613         }
614     }
615 }
616
617
618 /********************************************************************
619  * KEYWORD EVALUATION FOR ARBITRARY GROUPS
620  ********************************************************************/
621
622 /*!
623  * \param[in] top   Not used.
624  * \param[in] npar  Not used.
625  * \param[in] param Not used.
626  * \param[in] data  Should point to \ref t_methoddata_kweval.
627  * \returns   0 on success, a non-zero error code on return.
628  *
629  * Calls the initialization method of the wrapped keyword.
630  */
631 static void
632 init_kweval(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data)
633 {
634     t_methoddata_kweval *d = (t_methoddata_kweval *)data;
635
636     d->kwmethod->init(top, 0, NULL, d->kwmdata);
637 }
638
639 /*!
640  * \param[in]     top   Not used.
641  * \param[in,out] out   Pointer to output data structure.
642  * \param[in,out] data  Should point to \c t_methoddata_kweval.
643  * \returns       0 for success.
644  */
645 static void
646 init_output_kweval(t_topology *top, gmx_ana_selvalue_t *out, void *data)
647 {
648     t_methoddata_kweval *d = (t_methoddata_kweval *)data;
649
650     out->nr = d->g.isize;
651 }
652
653 /*!
654  * \param data Data to free (should point to a \c t_methoddata_kweval).
655  *
656  * Frees the memory allocated for all the members of \c t_methoddata_kweval.
657  */
658 static void
659 free_data_kweval(void *data)
660 {
661     t_methoddata_kweval *d = (t_methoddata_kweval *)data;
662
663     _gmx_selelem_free_method(d->kwmethod, d->kwmdata);
664     sfree(d);
665 }
666
667 /*!
668  * \param[in]  top  Topology.
669  * \param[in]  fr   Current frame.
670  * \param[in]  pbc  PBC structure.
671  * \param      data Should point to a \ref t_methoddata_kweval.
672  * \returns    0 on success, a non-zero error code on error.
673  *
674  * Creates a lookup structure that enables fast queries of whether a point
675  * is within the solid angle or not.
676  */
677 static void
678 init_frame_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data)
679 {
680     t_methoddata_kweval *d = (t_methoddata_kweval *)data;
681
682     d->kwmethod->init_frame(top, fr, pbc, d->kwmdata);
683 }
684
685 /*!
686  * See sel_updatefunc() for description of the parameters.
687  * \p data should point to a \c t_methoddata_kweval.
688  *
689  * Calls the evaluation function of the wrapped keyword with the given
690  * parameters, with the exception of using \c t_methoddata_kweval::g for the
691  * evaluation group.
692  */
693 static void
694 evaluate_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc,
695                 gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
696 {
697     t_methoddata_kweval *d = (t_methoddata_kweval *)data;
698
699     d->kwmethod->update(top, fr, pbc, &d->g, out, d->kwmdata);
700 }
701
702 /*!
703  * \param[in]   method  Keyword selection method to evaluate.
704  * \param[in]   params  Parameter that gives the group to evaluate \p method in.
705  * \param[in]   scanner Scanner data structure.
706  * \returns     Pointer to the created selection element (NULL on error).
707  *
708  * Creates a \ref SEL_EXPRESSION selection element that evaluates the keyword
709  * method given by \p method in the group given by \p param.
710  *
711  * The name of \p param should be empty.
712  */
713 gmx::SelectionTreeElementPointer
714 _gmx_sel_init_keyword_evaluator(gmx_ana_selmethod_t *method,
715                                 const gmx::SelectionParserParameterList &params,
716                                 void *scanner)
717 {
718     gmx::MessageStringCollector *errors = _gmx_sel_lexer_error_reporter(scanner);
719     char  buf[1024];
720     sprintf(buf, "In evaluation of '%s'", method->name);
721     gmx::MessageStringContext   context(errors, buf);
722
723     if ((method->flags & (SMETH_SINGLEVAL | SMETH_VARNUMVAL))
724         || method->outinit || method->pupdate)
725     {
726         GMX_THROW(gmx::InternalError(
727                 "Unsupported keyword method for arbitrary group evaluation"));
728     }
729
730     gmx::SelectionTreeElementPointer sel(
731             new gmx::SelectionTreeElement(SEL_EXPRESSION));
732     _gmx_selelem_set_method(sel, method, scanner);
733
734     t_methoddata_kweval  *data;
735     snew(data, 1);
736     data->kwmethod = sel->u.expr.method;
737     data->kwmdata  = sel->u.expr.mdata;
738     gmx_ana_index_clear(&data->g);
739
740     snew(sel->u.expr.method, 1);
741     memcpy(sel->u.expr.method, data->kwmethod, sizeof(gmx_ana_selmethod_t));
742     sel->u.expr.method->flags       |= SMETH_VARNUMVAL;
743     sel->u.expr.method->init_data    = NULL;
744     sel->u.expr.method->set_poscoll  = NULL;
745     sel->u.expr.method->init         = method->init ? &init_kweval : NULL;
746     sel->u.expr.method->outinit      = &init_output_kweval;
747     sel->u.expr.method->free         = &free_data_kweval;
748     sel->u.expr.method->init_frame   = method->init_frame ? &init_frame_kweval : NULL;
749     sel->u.expr.method->update       = &evaluate_kweval;
750     sel->u.expr.method->pupdate      = NULL;
751     sel->u.expr.method->nparams      = asize(smparams_kweval);
752     sel->u.expr.method->param        = smparams_kweval;
753     _gmx_selelem_init_method_params(sel, scanner);
754     sel->u.expr.mdata = data;
755
756     sel->u.expr.method->param[0].val.u.g = &data->g;
757
758     if (!_gmx_sel_parse_params(params, sel->u.expr.method->nparams,
759                                sel->u.expr.method->param, sel, scanner))
760     {
761         return gmx::SelectionTreeElementPointer();
762     }
763     return sel;
764 }