Include selection keyword details in user guide
[alexxy/gromacs.git] / src / gromacs / selection / sm_keywords.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2009,2010,2011,2012,2013,2014,2015, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements internal selection methods for numeric and string keyword
38  * evaluation.
39  *
40  * \author Teemu Murtola <teemu.murtola@gmail.com>
41  * \ingroup module_selection
42  */
43 #include "gmxpre.h"
44
45 #include <cctype>
46 #include <cstring>
47
48 #include <string>
49
50 #include <boost/shared_ptr.hpp>
51
52 #include "gromacs/legacyheaders/macros.h"
53 #include "gromacs/selection/position.h"
54 #include "gromacs/utility/cstringutil.h"
55 #include "gromacs/utility/exceptions.h"
56 #include "gromacs/utility/gmxassert.h"
57 #include "gromacs/utility/gmxregex.h"
58 #include "gromacs/utility/smalloc.h"
59 #include "gromacs/utility/stringutil.h"
60
61 #include "keywords.h"
62 #include "parsetree.h"
63 #include "scanner.h"
64 #include "selelem.h"
65 #include "selmethod.h"
66
67 /*! \brief
68  * Allocates data for integer keyword evaluation.
69  *
70  * \param[in] npar  Not used.
71  * \param     param Not used.
72  * \returns   Pointer to the allocated data (\ref t_methoddata_kwint).
73  *
74  * Allocates memory for a \ref t_methoddata_kwint structure.
75  */
76 static void *
77 init_data_kwint(int  npar, gmx_ana_selparam_t * param);
78 /*! \brief
79  * Allocates data for real keyword evaluation.
80  *
81  * \param[in] npar  Not used.
82  * \param     param Not used.
83  * \returns   Pointer to the allocated data (\ref t_methoddata_kwreal).
84  *
85  * Allocates memory for a \ref t_methoddata_kwreal structure.
86  */
87 static void *
88 init_data_kwreal(int npar, gmx_ana_selparam_t * param);
89 /*! \brief
90  * Allocates data for string keyword evaluation.
91  *
92  * \param[in] npar  Not used.
93  * \param     param Not used.
94  * \returns Pointer to the allocated data (t_methoddata_kwstr).
95  *
96  * Allocates memory for a t_methoddata_kwstr structure.
97  */
98 static void *
99 init_data_kwstr(int npar, gmx_ana_selparam_t * param);
100 /** /brief Initializes data for integer keyword evaluation.
101  *
102  * \param[in] top   Not used.
103  * \param[in] npar  Not used (should be 2).
104  * \param[in] param Method parameters (should point to \ref smparams_keyword_int).
105  * \param[in] data  Should point to \ref t_methoddata_kwint.
106  */
107 static void
108 init_kwint(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
109 /*! \brief
110  * Initializes data for real keyword evaluation.
111  *
112  * \param[in] top   Not used.
113  * \param[in] npar  Not used (should be 2).
114  * \param[in] param Method parameters (should point to \ref smparams_keyword_real).
115  * \param[in] data  Should point to \ref t_methoddata_kwreal.
116  * \returns   0 (the initialization always succeeds).
117  */
118 static void
119 init_kwreal(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
120 /*! \brief
121  * Initializes data for string keyword evaluation.
122  *
123  * \param[in] top   Not used.
124  * \param[in] npar  Not used (should be 2).
125  * \param[in] param Method parameters (should point to \ref smparams_keyword_str).
126  * \param[in] data  Should point to t_methoddata_kwstr.
127  */
128 static void
129 init_kwstr(t_topology *top, int npar, gmx_ana_selparam_t *param, void *data);
130 /** Frees the memory allocated for string keyword evaluation. */
131 static void
132 free_data_kwstr(void *data);
133 /** Evaluates integer selection keywords. */
134 static void
135 evaluate_keyword_int(t_topology * /* top */, t_trxframe * /* fr */, t_pbc * /* pbc */,
136                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
137 /** Evaluates real selection keywords. */
138 static void
139 evaluate_keyword_real(t_topology * /* top */, t_trxframe * /* fr */, t_pbc * /* pbc */,
140                       gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
141 /** Evaluates string selection keywords. */
142 static void
143 evaluate_keyword_str(t_topology * /* top */, t_trxframe * /* fr */, t_pbc * /* pbc */,
144                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data);
145
146 /*! \internal \brief
147  * Data structure for integer keyword expression evaluation.
148  */
149 typedef struct t_methoddata_kwint
150 {
151     /** Array of values for the keyword. */
152     int               *v;
153     /** Number of ranges in the \p r array. */
154     int                n;
155     /*! \brief
156      * Array of sorted integer ranges to match against.
157      *
158      * Each range is made of two integers, giving the endpoints (inclusive).
159      * This field stores the pointer to the ranges allocated by the
160      * parameter parser; see \ref SPAR_RANGES for more information.
161      */
162     int               *r;
163 } t_methoddata_kwint;
164
165 /*! \internal \brief
166  * Data structure for real keyword expression evaluation.
167  */
168 typedef struct t_methoddata_kwreal
169 {
170     /** Array of values for the keyword. */
171     real              *v;
172     /** Number of ranges in the \p r array. */
173     int                n;
174     /*! \brief
175      * Array of sorted ranges to match against.
176      *
177      * Each range is made of two values, giving the endpoints (inclusive).
178      * This field stores the pointer to the ranges allocated by the
179      * parameter parser; see \ref SPAR_RANGES for more information.
180      */
181     real              *r;
182 } t_methoddata_kwreal;
183
184 namespace
185 {
186
187 /*! \internal \brief
188  * Single item in the list of strings/regular expressions to match.
189  *
190  * \ingroup module_selection
191  */
192 class StringKeywordMatchItem
193 {
194     public:
195         /*! \brief
196          * Constructs a matcher from a string.
197          *
198          * \param[in] matchType String matching type.
199          * \param[in] str       String to use for matching.
200          */
201         StringKeywordMatchItem(gmx::SelectionStringMatchType matchType,
202                                const char                   *str)
203             : str_(str)
204         {
205             bool bRegExp = (matchType == gmx::eStringMatchType_RegularExpression);
206             if (matchType == gmx::eStringMatchType_Auto)
207             {
208                 for (size_t j = 0; j < std::strlen(str); ++j)
209                 {
210                     if (std::ispunct(str[j]) && str[j] != '?' && str[j] != '*')
211                     {
212                         bRegExp = true;
213                         break;
214                     }
215                 }
216             }
217             if (bRegExp)
218             {
219                 if (!gmx::Regex::isSupported())
220                 {
221                     std::string message
222                         = gmx::formatString("No regular expression support, "
223                                             "cannot match \"%s\"", str);
224                     GMX_THROW(gmx::InvalidInputError(message));
225                 }
226                 regex_.reset(new gmx::Regex(str));
227             }
228         }
229
230         /*! \brief
231          * Checks whether this item matches a string.
232          *
233          * \param[in] matchType String matching type.
234          * \param[in] value     String to match.
235          * \returns   true if this item matches \p value.
236          */
237         bool match(gmx::SelectionStringMatchType matchType,
238                    const char                   *value) const
239         {
240             if (matchType == gmx::eStringMatchType_Exact)
241             {
242                 return str_ == value;
243             }
244             else if (regex_)
245             {
246                 return gmx::regexMatch(value, *regex_);
247             }
248             else
249             {
250                 return gmx_wcmatch(str_.c_str(), value) == 0;
251             }
252         }
253
254     private:
255         //! The raw string passed for the matcher.
256         std::string                     str_;
257         //! Regular expression compiled from \p str_, if applicable.
258         boost::shared_ptr<gmx::Regex>   regex_;
259 };
260
261 /*! \internal \brief
262  * Data structure for string keyword expression evaluation.
263  */
264 struct t_methoddata_kwstr
265 {
266     /** Matching type for the strings. */
267     gmx::SelectionStringMatchType       matchType;
268     /** Array of values for the keyword. */
269     char                              **v;
270     /** Array of strings/regular expressions to match against.*/
271     std::vector<StringKeywordMatchItem> matches;
272 };
273
274 } // namespace
275
276 /** Parameters for integer keyword evaluation. */
277 static gmx_ana_selparam_t smparams_keyword_int[] = {
278     {NULL, {INT_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL},
279     {NULL, {INT_VALUE, -1, {NULL}}, NULL, SPAR_RANGES | SPAR_VARNUM},
280 };
281
282 /** Parameters for real keyword evaluation. */
283 static gmx_ana_selparam_t smparams_keyword_real[] = {
284     {NULL, {REAL_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL | SPAR_DYNAMIC},
285     {NULL, {REAL_VALUE, -1, {NULL}}, NULL, SPAR_RANGES | SPAR_VARNUM},
286 };
287
288 /** Parameters for string keyword evaluation. */
289 static gmx_ana_selparam_t smparams_keyword_str[] = {
290     {NULL, {STR_VALUE, -1, {NULL}}, NULL, SPAR_ATOMVAL},
291     {NULL, {STR_VALUE, -1, {NULL}}, NULL, SPAR_VARNUM},
292 };
293
294 /** Selection method data for integer keyword evaluation. */
295 gmx_ana_selmethod_t sm_keyword_int = {
296     "kw_int", GROUP_VALUE, SMETH_SINGLEVAL,
297     asize(smparams_keyword_int), smparams_keyword_int,
298     &init_data_kwint,
299     NULL,
300     &init_kwint,
301     NULL,
302     NULL,
303     NULL,
304     &evaluate_keyword_int,
305     NULL,
306     {NULL, NULL, 0, NULL},
307 };
308
309 /** Selection method data for real keyword evaluation. */
310 gmx_ana_selmethod_t sm_keyword_real = {
311     "kw_real", GROUP_VALUE, SMETH_SINGLEVAL,
312     asize(smparams_keyword_real), smparams_keyword_real,
313     &init_data_kwreal,
314     NULL,
315     &init_kwreal,
316     NULL,
317     NULL,
318     NULL,
319     &evaluate_keyword_real,
320     NULL,
321     {NULL, NULL, 0, NULL},
322 };
323
324 /** Selection method data for string keyword evaluation. */
325 gmx_ana_selmethod_t sm_keyword_str = {
326     "kw_str", GROUP_VALUE, SMETH_SINGLEVAL,
327     asize(smparams_keyword_str), smparams_keyword_str,
328     &init_data_kwstr,
329     NULL,
330     &init_kwstr,
331     NULL,
332     &free_data_kwstr,
333     NULL,
334     &evaluate_keyword_str,
335     NULL,
336     {NULL, NULL, 0, NULL},
337 };
338
339 /*! \brief
340  * Initializes keyword evaluation for an arbitrary group.
341  *
342  * \param[in] top   Not used.
343  * \param[in] npar  Not used.
344  * \param[in] param Not used.
345  * \param[in] data  Should point to \ref t_methoddata_kweval.
346  * \returns   0 on success, a non-zero error code on return.
347  *
348  * Calls the initialization method of the wrapped keyword.
349  */
350 static void
351 init_kweval(t_topology *top, int npar, gmx_ana_selparam_t * param, void *data);
352 /*! \brief
353  * Initializes output for keyword evaluation in an arbitrary group.
354  *
355  * \param[in]     top   Not used.
356  * \param[in,out] out   Pointer to output data structure.
357  * \param[in,out] data  Should point to \c t_methoddata_kweval.
358  * \returns       0 for success.
359  */
360 static void
361 init_output_kweval(t_topology *top, gmx_ana_selvalue_t *out, void *data);
362 /** Frees the data allocated for keyword evaluation in an arbitrary group. */
363 static void
364 free_data_kweval(void *data);
365 /** Initializes frame evaluation for keyword evaluation in an arbitrary group. */
366 static void
367 init_frame_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data);
368 /*! \brief
369  * Evaluates keywords in an arbitrary group.
370  *
371  * See sel_updatefunc() for description of the parameters.
372  * \p data should point to a \c t_methoddata_kweval.
373  *
374  * Calls the evaluation function of the wrapped keyword with the given
375  * parameters, with the exception of using \c t_methoddata_kweval::g for the
376  * evaluation group.
377  */
378 static void
379 evaluate_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc, gmx_ana_index_t *g,
380                 gmx_ana_selvalue_t *out, void *data);
381 /*! \brief
382  * Evaluates keywords in an arbitrary set of positions.
383  *
384  * See sel_updatefunc() for description of the parameters.
385  * \p data should point to a \c t_methoddata_kweval.
386  *
387  * Calls the evaluation function of the wrapped keyword with the given
388  * parameters, with the exception of using \c t_methoddata_kweval::p for the
389  * evaluation positions.
390  */
391 static void
392 evaluate_kweval_pos(t_topology *top, t_trxframe *fr, t_pbc *pbc, gmx_ana_index_t *g,
393                     gmx_ana_selvalue_t *out, void *data);
394
395 /*! \internal \brief
396  * Data structure for keyword evaluation in arbitrary groups.
397  */
398 struct t_methoddata_kweval
399 {
400     //! Initialize new keyword evaluator for the given keyword.
401     t_methoddata_kweval(gmx_ana_selmethod_t *method, void *data)
402         : kwmethod(method), kwmdata(data)
403     {
404         gmx_ana_index_clear(&g);
405     }
406     ~t_methoddata_kweval()
407     {
408         _gmx_selelem_free_method(kwmethod, kwmdata);
409     }
410
411     //! Wrapped keyword method for evaluating the values.
412     gmx_ana_selmethod_t  *kwmethod;
413     //! Method data for \p kwmethod.
414     void                 *kwmdata;
415     //! Group in which \p kwmethod should be evaluated.
416     gmx_ana_index_t       g;
417     //! Positions for which \p kwmethod should be evaluated.
418     gmx_ana_pos_t         p;
419 };
420
421 /** Parameters for keyword evaluation in an arbitrary group. */
422 static gmx_ana_selparam_t smparams_kweval_group[] = {
423     {NULL,   {GROUP_VALUE, 1, {NULL}}, NULL, SPAR_DYNAMIC},
424 };
425 /** Parameters for keyword evaluation from positions. */
426 static gmx_ana_selparam_t smparams_kweval_pos[] = {
427     {NULL,   {POS_VALUE, 1, {NULL}}, NULL, SPAR_DYNAMIC},
428 };
429
430
431 /********************************************************************
432  * INTEGER KEYWORD EVALUATION
433  ********************************************************************/
434
435 static void *
436 init_data_kwint(int  /* npar */, gmx_ana_selparam_t * /* param */)
437 {
438     t_methoddata_kwint *data;
439
440     snew(data, 1);
441     return data;
442 }
443
444 static void
445 init_kwint(t_topology * /* top */, int /* npar */, gmx_ana_selparam_t *param, void *data)
446 {
447     t_methoddata_kwint *d = (t_methoddata_kwint *)data;
448
449     d->v = param[0].val.u.i;
450     d->n = param[1].val.nr;
451     d->r = param[1].val.u.i;
452 }
453
454 /*!
455  * See sel_updatefunc() for description of the parameters.
456  * \p data should point to a \c t_methoddata_kwint.
457  *
458  * Does a binary search to find which atoms match the ranges in the
459  * \c t_methoddata_kwint structure for this selection.
460  * Matching atoms are stored in \p out->u.g.
461  */
462 static void
463 evaluate_keyword_int(t_topology * /* top */, t_trxframe * /* fr */, t_pbc * /* pbc */,
464                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
465 {
466     t_methoddata_kwint *d = (t_methoddata_kwint *)data;
467     int                 n, i, j, jmin, jmax;
468     int                 val;
469
470     out->u.g->isize = 0;
471     n               = d->n;
472     for (i = 0; i < g->isize; ++i)
473     {
474         val = d->v[i];
475         if (d->r[0] > val || d->r[2*n-1] < val)
476         {
477             continue;
478         }
479         jmin = 0;
480         jmax = n;
481         while (jmax - jmin > 1)
482         {
483             j = jmin + (jmax - jmin) / 2;
484             if (val < d->r[2*j])
485             {
486                 jmax = j;
487             }
488             else
489             {
490                 jmin = j;
491                 if (val <= d->r[2*j+1])
492                 {
493                     break;
494                 }
495                 /* ++jmin;*/
496             }
497         }
498         if (val <= d->r[2*jmin+1])
499         {
500             out->u.g->index[out->u.g->isize++] = g->index[i];
501         }
502     }
503 }
504
505
506 /********************************************************************
507  * REAL KEYWORD EVALUATION
508  ********************************************************************/
509
510 static void *
511 init_data_kwreal(int /* npar */, gmx_ana_selparam_t * /* param */)
512 {
513     t_methoddata_kwreal *data;
514
515     snew(data, 1);
516     return data;
517 }
518
519 static void
520 init_kwreal(t_topology * /* top */, int /* npar */, gmx_ana_selparam_t *param, void *data)
521 {
522     t_methoddata_kwreal *d = (t_methoddata_kwreal *)data;
523
524     d->v = param[0].val.u.r;
525     d->n = param[1].val.nr;
526     d->r = param[1].val.u.r;
527 }
528
529 /*!
530  * See sel_updatefunc() for description of the parameters.
531  * \p data should point to a \c t_methoddata_kwreal.
532  *
533  * Does a binary search to find which atoms match the ranges in the
534  * \c t_methoddata_kwreal structure for this selection.
535  * Matching atoms are stored in \p out->u.g.
536  */
537 static void
538 evaluate_keyword_real(t_topology * /* top */, t_trxframe * /* fr */, t_pbc * /* pbc */,
539                       gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
540 {
541     t_methoddata_kwreal *d = (t_methoddata_kwreal *)data;
542     int                  n, i, j, jmin, jmax;
543     real                 val;
544
545     out->u.g->isize = 0;
546     n               = d->n;
547     for (i = 0; i < g->isize; ++i)
548     {
549         val = d->v[i];
550         if (d->r[0] > val || d->r[2*n-1] < val)
551         {
552             continue;
553         }
554         jmin = 0;
555         jmax = n;
556         while (jmax - jmin > 1)
557         {
558             j = jmin + (jmax - jmin) / 2;
559             if (val < d->r[2*j])
560             {
561                 jmax = j;
562             }
563             else
564             {
565                 jmin = j;
566                 if (val <= d->r[2*j+1])
567                 {
568                     break;
569                 }
570                 /* ++jmin;*/
571             }
572         }
573         if (val <= d->r[2*jmin+1])
574         {
575             out->u.g->index[out->u.g->isize++] = g->index[i];
576         }
577     }
578 }
579
580
581 /********************************************************************
582  * STRING KEYWORD EVALUATION
583  ********************************************************************/
584
585 static void *
586 init_data_kwstr(int /* npar */, gmx_ana_selparam_t * /* param */)
587 {
588     t_methoddata_kwstr *data = new t_methoddata_kwstr();
589     data->matchType = gmx::eStringMatchType_Auto;
590     return data;
591 }
592
593 /*!
594  * \param[in,out] sel   Selection element to initialize.
595  * \param[in]     matchType  Method to use to match string values.
596  *
597  * Sets the string matching method for string keyword matching.
598  */
599 void
600 _gmx_selelem_set_kwstr_match_type(const gmx::SelectionTreeElementPointer &sel,
601                                   gmx::SelectionStringMatchType           matchType)
602 {
603     t_methoddata_kwstr *d = static_cast<t_methoddata_kwstr *>(sel->u.expr.mdata);
604
605     if (sel->type != SEL_EXPRESSION || !sel->u.expr.method
606         || sel->u.expr.method->name != sm_keyword_str.name)
607     {
608         return;
609     }
610     d->matchType = matchType;
611 }
612
613 static void
614 init_kwstr(t_topology * /* top */, int /* npar */, gmx_ana_selparam_t *param, void *data)
615 {
616     t_methoddata_kwstr *d = static_cast<t_methoddata_kwstr *>(data);
617
618     d->v   = param[0].val.u.s;
619     /* Return if this is not the first time */
620     if (!d->matches.empty())
621     {
622         return;
623     }
624     int n = param[1].val.nr;
625     d->matches.reserve(n);
626     for (int i = 0; i < n; ++i)
627     {
628         const char *s = param[1].val.u.s[i];
629         d->matches.push_back(StringKeywordMatchItem(d->matchType, s));
630     }
631 }
632
633 /*!
634  * \param data Data to free (should point to a t_methoddata_kwstr).
635  */
636 static void
637 free_data_kwstr(void *data)
638 {
639     t_methoddata_kwstr *d = static_cast<t_methoddata_kwstr *>(data);
640     delete d;
641 }
642
643 /*!
644  * See sel_updatefunc() for description of the parameters.
645  * \p data should point to a \c t_methoddata_kwstr.
646  *
647  * Does a linear search to find which atoms match the strings in the
648  * \c t_methoddata_kwstr structure for this selection.
649  * Wildcards are allowed in the strings.
650  * Matching atoms are stored in \p out->u.g.
651  */
652 static void
653 evaluate_keyword_str(t_topology * /* top */, t_trxframe * /* fr */, t_pbc * /* pbc */,
654                      gmx_ana_index_t *g, gmx_ana_selvalue_t *out, void *data)
655 {
656     t_methoddata_kwstr *d = static_cast<t_methoddata_kwstr *>(data);
657
658     out->u.g->isize = 0;
659     for (int i = 0; i < g->isize; ++i)
660     {
661         for (size_t j = 0; j < d->matches.size(); ++j)
662         {
663             if (d->matches[j].match(d->matchType, d->v[i]))
664             {
665                 out->u.g->index[out->u.g->isize++] = g->index[i];
666                 break;
667             }
668         }
669     }
670 }
671
672
673 /********************************************************************
674  * KEYWORD EVALUATION FOR ARBITRARY GROUPS
675  ********************************************************************/
676
677 static void
678 init_kweval(t_topology *top, int /* npar */, gmx_ana_selparam_t * /* param */, void *data)
679 {
680     t_methoddata_kweval *d = static_cast<t_methoddata_kweval *>(data);
681
682     d->kwmethod->init(top, 0, NULL, d->kwmdata);
683 }
684
685 static void
686 init_output_kweval(t_topology * /* top */, gmx_ana_selvalue_t *out, void *data)
687 {
688     t_methoddata_kweval *d = static_cast<t_methoddata_kweval *>(data);
689
690     out->nr = d->g.isize;
691     _gmx_selvalue_reserve(out, out->nr);
692 }
693
694 /*!
695  * \param data Data to free (should point to a \c t_methoddata_kweval).
696  *
697  * Frees the memory allocated for all the members of \c t_methoddata_kweval.
698  */
699 static void
700 free_data_kweval(void *data)
701 {
702     t_methoddata_kweval *d = static_cast<t_methoddata_kweval *>(data);
703
704     delete d;
705 }
706
707 /*!
708  * \param[in]  top  Topology.
709  * \param[in]  fr   Current frame.
710  * \param[in]  pbc  PBC structure.
711  * \param      data Should point to a \ref t_methoddata_kweval.
712  * \returns    0 on success, a non-zero error code on error.
713  *
714  * Creates a lookup structure that enables fast queries of whether a point
715  * is within the solid angle or not.
716  */
717 static void
718 init_frame_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc, void *data)
719 {
720     t_methoddata_kweval *d = static_cast<t_methoddata_kweval *>(data);
721
722     d->kwmethod->init_frame(top, fr, pbc, d->kwmdata);
723 }
724
725 static void
726 evaluate_kweval(t_topology *top, t_trxframe *fr, t_pbc *pbc,
727                 gmx_ana_index_t * /* g */, gmx_ana_selvalue_t *out, void *data)
728 {
729     t_methoddata_kweval *d = static_cast<t_methoddata_kweval *>(data);
730
731     d->kwmethod->update(top, fr, pbc, &d->g, out, d->kwmdata);
732 }
733
734 static void
735 evaluate_kweval_pos(t_topology *top, t_trxframe *fr, t_pbc *pbc,
736                     gmx_ana_index_t * /* g */, gmx_ana_selvalue_t *out, void *data)
737 {
738     t_methoddata_kweval *d = static_cast<t_methoddata_kweval *>(data);
739
740     d->kwmethod->pupdate(top, fr, pbc, &d->p, out, d->kwmdata);
741 }
742
743 /*! \brief
744  * Initializes a selection element for evaluating a keyword in a given group.
745  *
746  * \param[in]   method  Keyword selection method to evaluate.
747  * \param[in]   params  Parameters to pass to initialization (the child group).
748  * \param[in]   scanner Scanner data structure.
749  * \returns     Pointer to the created selection element.
750  *
751  * Implements _gmx_sel_init_keyword_evaluator() for \ref GROUP_VALUE input
752  * selections.
753  */
754 static gmx::SelectionTreeElementPointer
755 init_evaluator_group(gmx_ana_selmethod_t                     *method,
756                      const gmx::SelectionParserParameterList &params,
757                      void                                    *scanner)
758 {
759     if ((method->flags & (SMETH_SINGLEVAL | SMETH_VARNUMVAL))
760         || method->outinit || method->pupdate)
761     {
762         std::string message
763             = gmx::formatString("Keyword '%s' cannot be evaluated in this context",
764                                 method->name);
765         GMX_THROW(gmx::InvalidInputError(message));
766     }
767
768     // TODO: For same ... as ..., some other location could be more intuitive.
769     gmx::SelectionTreeElementPointer sel(
770             new gmx::SelectionTreeElement(
771                     SEL_EXPRESSION, _gmx_sel_lexer_get_current_location(scanner)));
772     _gmx_selelem_set_method(sel, method, scanner);
773
774     t_methoddata_kweval *data
775         = new t_methoddata_kweval(sel->u.expr.method, sel->u.expr.mdata);
776
777     snew(sel->u.expr.method, 1);
778     sel->u.expr.method->name         = data->kwmethod->name;
779     sel->u.expr.method->type         = data->kwmethod->type;
780     sel->u.expr.method->flags        = data->kwmethod->flags | SMETH_VARNUMVAL;
781     sel->u.expr.method->init_data    = NULL;
782     sel->u.expr.method->set_poscoll  = NULL;
783     sel->u.expr.method->init         = method->init ? &init_kweval : NULL;
784     sel->u.expr.method->outinit      = &init_output_kweval;
785     sel->u.expr.method->free         = &free_data_kweval;
786     sel->u.expr.method->init_frame   = method->init_frame ? &init_frame_kweval : NULL;
787     sel->u.expr.method->update       = &evaluate_kweval;
788     sel->u.expr.method->pupdate      = NULL;
789     sel->u.expr.method->nparams      = asize(smparams_kweval_group);
790     sel->u.expr.method->param        = smparams_kweval_group;
791     _gmx_selelem_init_method_params(sel, scanner);
792     sel->u.expr.mdata = data;
793
794     sel->u.expr.method->param[0].val.u.g = &data->g;
795
796     _gmx_sel_parse_params(params, sel->u.expr.method->nparams,
797                           sel->u.expr.method->param, sel, scanner);
798     return sel;
799 }
800
801 /*! \brief
802  * Initializes a selection element for evaluating a keyword in given positions.
803  *
804  * \param[in]   method  Keyword selection method to evaluate.
805  * \param[in]   params  Parameters to pass to initialization (the child positions).
806  * \param[in]   scanner Scanner data structure.
807  * \returns     Pointer to the created selection element.
808  *
809  * Implements _gmx_sel_init_keyword_evaluator() for \ref POS_VALUE input
810  * selections.
811  */
812 static gmx::SelectionTreeElementPointer
813 init_evaluator_pos(gmx_ana_selmethod_t                     *method,
814                    const gmx::SelectionParserParameterList &params,
815                    void                                    *scanner)
816 {
817     if ((method->flags & (SMETH_SINGLEVAL | SMETH_VARNUMVAL))
818         || method->outinit || method->pupdate == NULL)
819     {
820         std::string message
821             = gmx::formatString("Keyword '%s' cannot be evaluated in this context",
822                                 method->name);
823         GMX_THROW(gmx::InvalidInputError(message));
824     }
825
826     gmx::SelectionTreeElementPointer sel(
827             new gmx::SelectionTreeElement(
828                     SEL_EXPRESSION, _gmx_sel_lexer_get_current_location(scanner)));
829     _gmx_selelem_set_method(sel, method, scanner);
830
831     t_methoddata_kweval *data
832         = new t_methoddata_kweval(sel->u.expr.method, sel->u.expr.mdata);
833
834     snew(sel->u.expr.method, 1);
835     sel->u.expr.method->name         = data->kwmethod->name;
836     sel->u.expr.method->type         = data->kwmethod->type;
837     sel->u.expr.method->flags        = data->kwmethod->flags | SMETH_SINGLEVAL;
838     sel->u.expr.method->init_data    = NULL;
839     sel->u.expr.method->set_poscoll  = NULL;
840     sel->u.expr.method->init         = method->init ? &init_kweval : NULL;
841     sel->u.expr.method->outinit      = NULL;
842     sel->u.expr.method->free         = &free_data_kweval;
843     sel->u.expr.method->init_frame   = method->init_frame ? &init_frame_kweval : NULL;
844     sel->u.expr.method->update       = &evaluate_kweval_pos;
845     sel->u.expr.method->pupdate      = NULL;
846     sel->u.expr.method->nparams      = asize(smparams_kweval_pos);
847     sel->u.expr.method->param        = smparams_kweval_pos;
848     _gmx_selelem_init_method_params(sel, scanner);
849     sel->u.expr.mdata = data;
850
851     sel->u.expr.method->param[0].val.u.p = &data->p;
852
853     _gmx_sel_parse_params(params, sel->u.expr.method->nparams,
854                           sel->u.expr.method->param, sel, scanner);
855     return sel;
856 }
857
858 gmx::SelectionTreeElementPointer
859 _gmx_sel_init_keyword_evaluator(gmx_ana_selmethod_t                    *method,
860                                 const gmx::SelectionTreeElementPointer &child,
861                                 void                                   *scanner)
862 {
863     gmx::SelectionParserParameterList    params;
864     params.push_back(
865             gmx::SelectionParserParameter::createFromExpression(NULL, child));
866     if (child->v.type == GROUP_VALUE)
867     {
868         return init_evaluator_group(method, params, scanner);
869     }
870     else if (child->v.type == POS_VALUE)
871     {
872         return init_evaluator_pos(method, params, scanner);
873     }
874     else
875     {
876         std::string text(_gmx_sel_lexer_get_text(scanner, child->location()));
877         std::string message
878             = gmx::formatString("Expression '%s' cannot be used to evaluate keywords",
879                                 text.c_str());
880         GMX_THROW(gmx::InvalidInputError(message));
881     }
882 }