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