Use smart pointers to manage the selection tree.
[alexxy/gromacs.git] / src / gromacs / selection / selelem.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 selelem.h.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_selection
37  */
38 #include <cstring>
39
40 #include "gromacs/legacyheaders/smalloc.h"
41
42 #include "gromacs/selection/indexutil.h"
43 #include "gromacs/selection/poscalc.h"
44 #include "gromacs/selection/position.h"
45 #include "gromacs/selection/selmethod.h"
46 #include "gromacs/utility/exceptions.h"
47 #include "gromacs/utility/gmxassert.h"
48
49 #include "keywords.h"
50 #include "mempool.h"
51 #include "selelem.h"
52
53 /*!
54  * \param[in] sel Selection for which the string is requested
55  * \returns   Pointer to a string that corresponds to \p sel->type.
56  *
57  * The return value points to a string constant and should not be \p free'd.
58  * 
59  * The function returns NULL if \p sel->type is not one of the valid values.
60  */
61 const char *
62 _gmx_selelem_type_str(const gmx::SelectionTreeElement &sel)
63 {
64     switch (sel.type)
65     {
66         case SEL_CONST:      return "CONST";
67         case SEL_EXPRESSION: return "EXPR";
68         case SEL_BOOLEAN:    return "BOOL";
69         case SEL_ARITHMETIC: return "ARITH";
70         case SEL_ROOT:       return "ROOT";
71         case SEL_SUBEXPR:    return "SUBEXPR";
72         case SEL_SUBEXPRREF: return "REF";
73         case SEL_GROUPREF:   return "GROUPREF";
74         case SEL_MODIFIER:   return "MODIFIER";
75     }
76     return NULL;
77 }
78
79 /*!
80  * \param[in] val Value structore for which the string is requested.
81  * \returns   Pointer to a string that corresponds to \p val->type,
82  *   NULL if the type value is invalid.
83  *
84  * The return value points to a string constant and should not be \p free'd.
85  */
86 const char *
87 _gmx_sel_value_type_str(const gmx_ana_selvalue_t *val)
88 {
89     switch (val->type)
90     {
91         case NO_VALUE:       return "NONE";
92         case INT_VALUE:      return "INT";
93         case REAL_VALUE:     return "REAL";
94         case STR_VALUE:      return "STR";
95         case POS_VALUE:      return "VEC";
96         case GROUP_VALUE:    return "GROUP";
97     }
98     return NULL;
99 }
100
101 /*! \copydoc _gmx_selelem_type_str() */
102 const char *
103 _gmx_selelem_boolean_type_str(const gmx::SelectionTreeElement &sel)
104 {
105     switch (sel.u.boolt)
106     {
107         case BOOL_NOT:  return "NOT"; break;
108         case BOOL_AND:  return "AND"; break;
109         case BOOL_OR:   return "OR";  break;
110         case BOOL_XOR:  return "XOR"; break;
111     }
112     return NULL;
113 }
114
115
116 namespace gmx
117 {
118
119 SelectionTreeElement::SelectionTreeElement(e_selelem_t type)
120 {
121     this->name       = NULL;
122     this->type       = type;
123     this->flags      = (type != SEL_ROOT) ? SEL_ALLOCVAL : 0;
124     if (type == SEL_BOOLEAN)
125     {
126         this->v.type = GROUP_VALUE;
127         this->flags |= SEL_ALLOCDATA;
128     }
129     else
130     {
131         this->v.type = NO_VALUE;
132     }
133     _gmx_selvalue_clear(&this->v);
134     std::memset(&this->u, 0, sizeof(this->u));
135     this->evaluate   = NULL;
136     this->mempool    = NULL;
137     this->cdata      = NULL;
138 }
139
140 SelectionTreeElement::~SelectionTreeElement()
141 {
142     /* Free the children.
143      * Must be done before freeing other data, because the children may hold
144      * references to data in this element. */
145     child.reset();
146
147     freeValues();
148     freeExpressionData();
149     freeCompilerData();
150 }
151
152 void SelectionTreeElement::freeValues()
153 {
154     mempoolRelease();
155     if ((flags & SEL_ALLOCDATA) && v.u.ptr)
156     {
157         /* The number of position/group structures is constant, so the
158          * backup of using sel->v.nr should work for them.
159          * For strings, we report an error if we don't know the allocation
160          * size here. */
161         int n = (v.nalloc > 0) ? v.nalloc : v.nr;
162         switch (v.type)
163         {
164             case STR_VALUE:
165                 GMX_RELEASE_ASSERT(v.nalloc != 0,
166                         "SEL_ALLOCDATA should only be set for allocated "
167                         "STR_VALUE values");
168                 for (int i = 0; i < n; ++i)
169                 {
170                     sfree(v.u.s[i]);
171                 }
172                 break;
173             case POS_VALUE:
174                 for (int i = 0; i < n; ++i)
175                 {
176                     gmx_ana_pos_deinit(&v.u.p[i]);
177                 }
178                 break;
179             case GROUP_VALUE:
180                 for (int i = 0; i < n; ++i)
181                 {
182                     gmx_ana_index_deinit(&v.u.g[i]);
183                 }
184                 break;
185             default: /* No special handling for other types */
186                 break;
187         }
188     }
189     if (flags & SEL_ALLOCVAL)
190     {
191         sfree(v.u.ptr);
192     }
193     _gmx_selvalue_setstore(&v, NULL);
194     if (type == SEL_SUBEXPRREF && u.param)
195     {
196         u.param->val.u.ptr = NULL;
197     }
198 }
199
200 void
201 SelectionTreeElement::freeExpressionData()
202 {
203     if (type == SEL_EXPRESSION || type == SEL_MODIFIER)
204     {
205         _gmx_selelem_free_method(u.expr.method, u.expr.mdata);
206         u.expr.mdata = NULL;
207         u.expr.method = NULL;
208         /* Free position data */
209         if (u.expr.pos)
210         {
211             gmx_ana_pos_free(u.expr.pos);
212             u.expr.pos = NULL;
213         }
214         /* Free position calculation data */
215         if (u.expr.pc)
216         {
217             gmx_ana_poscalc_free(u.expr.pc);
218             u.expr.pc = NULL;
219         }
220     }
221     if (type == SEL_ARITHMETIC)
222     {
223         sfree(u.arith.opstr);
224         u.arith.opstr = NULL;
225     }
226     if (type == SEL_SUBEXPR || type == SEL_ROOT
227         || (type == SEL_CONST && v.type == GROUP_VALUE))
228     {
229         gmx_ana_index_deinit(&u.cgrp);
230     }
231     if (type == SEL_GROUPREF)
232     {
233         sfree(u.gref.name);
234     }
235 }
236
237 void SelectionTreeElement::mempoolReserve(int count)
238 {
239     if (!mempool)
240     {
241         return;
242     }
243     switch (v.type)
244     {
245         case INT_VALUE:
246             v.u.i = static_cast<int *>(
247                     _gmx_sel_mempool_alloc(mempool, sizeof(*v.u.i)*count));
248             break;
249
250         case REAL_VALUE:
251             v.u.r = static_cast<real *>(
252                     _gmx_sel_mempool_alloc(mempool, sizeof(*v.u.r)*count));
253             break;
254
255         case GROUP_VALUE:
256             _gmx_sel_mempool_alloc_group(mempool, v.u.g, count);
257             break;
258
259         default:
260             GMX_THROW(gmx::InternalError("Memory pooling not implemented for requested type"));
261     }
262 }
263
264 void SelectionTreeElement::mempoolRelease()
265 {
266     if (!mempool)
267     {
268         return;
269     }
270     switch (v.type)
271     {
272         case INT_VALUE:
273         case REAL_VALUE:
274             _gmx_sel_mempool_free(mempool, v.u.ptr);
275             _gmx_selvalue_setstore(&v, NULL);
276             break;
277
278         case GROUP_VALUE:
279             if (v.u.g)
280             {
281                 _gmx_sel_mempool_free_group(mempool, v.u.g);
282             }
283             break;
284
285         default:
286             GMX_THROW(gmx::InternalError("Memory pooling not implemented for requested type"));
287     }
288 }
289
290 } // namespace gmx
291
292 /*!
293  * \param[in,out] sel   Selection element to set the type for.
294  * \param[in]     vtype Value type for the selection element.
295  *
296  * If the new type is \ref GROUP_VALUE or \ref POS_VALUE, the
297  * \ref SEL_ALLOCDATA flag is also set.
298  *
299  * This function should only be called at most once for each element,
300  * preferably right after calling _gmx_selelem_create().
301  */
302 void
303 _gmx_selelem_set_vtype(const gmx::SelectionTreeElementPointer &sel,
304                        e_selvalue_t vtype)
305 {
306     GMX_RELEASE_ASSERT(sel->type != SEL_BOOLEAN || vtype == GROUP_VALUE,
307                        "Boolean elements must have a group value");
308     GMX_RELEASE_ASSERT(sel->v.type == NO_VALUE || vtype == sel->v.type,
309                        "_gmx_selelem_set_vtype() called more than once");
310     sel->v.type = vtype;
311     if (vtype == GROUP_VALUE || vtype == POS_VALUE)
312     {
313         sel->flags |= SEL_ALLOCDATA;
314     }
315 }
316
317 /*!
318  * \param[in] method Method to free.
319  * \param[in] mdata  Method data to free.
320  */
321 void
322 _gmx_selelem_free_method(gmx_ana_selmethod_t *method, void *mdata)
323 {
324     sel_freefunc free_func = NULL;
325
326     /* Save the pointer to the free function. */
327     if (method && method->free)
328     {
329         free_func = method->free;
330     }
331
332     /* Free the method itself.
333      * Has to be done before freeing the method data, because parameter
334      * values are typically stored in the method data, and here we may
335      * access them. */
336     if (method)
337     {
338         int  i, j;
339
340         /* Free the memory allocated for the parameters that are not managed
341          * by the selection method itself. */
342         for (i = 0; i < method->nparams; ++i)
343         {
344             gmx_ana_selparam_t *param = &method->param[i];
345
346             if (param->val.u.ptr)
347             {
348                 if (param->val.type == GROUP_VALUE)
349                 {
350                     for (j = 0; j < param->val.nr; ++j)
351                     {
352                         gmx_ana_index_deinit(&param->val.u.g[j]);
353                     }
354                 }
355                 else if (param->val.type == POS_VALUE)
356                 {
357                     for (j = 0; j < param->val.nr; ++j)
358                     {
359                         gmx_ana_pos_deinit(&param->val.u.p[j]);
360                     }
361                 }
362
363                 if (param->val.nalloc > 0)
364                 {
365                     sfree(param->val.u.ptr);
366                 }
367             }
368         }
369         sfree(method->param);
370         sfree(method);
371     }
372     /* Free method data. */
373     if (mdata)
374     {
375         if (free_func)
376         {
377             free_func(mdata);
378         }
379         sfree(mdata);
380     }
381 }
382
383 /*!
384  * \param[in] fp      File handle to receive the output.
385  * \param[in] sel     Root of the selection subtree to print.
386  * \param[in] bValues If true, the evaluated values of selection elements
387  *   are printed as well.
388  * \param[in] level   Indentation level, starting from zero.
389  */
390 void
391 _gmx_selelem_print_tree(FILE *fp, const gmx::SelectionTreeElement &sel,
392                         bool bValues, int level)
393 {
394     int          i;
395
396     fprintf(fp, "%*c %s %s", level*2+1, '*',
397             _gmx_selelem_type_str(sel), _gmx_sel_value_type_str(&sel.v));
398     if (sel.name)
399     {
400         fprintf(fp, " \"%s\"", sel.name);
401     }
402     fprintf(fp, " flg=");
403     if (sel.flags & SEL_FLAGSSET)
404     {
405         fprintf(fp, "s");
406     }
407     if (sel.flags & SEL_SINGLEVAL)
408     {
409         fprintf(fp, "S");
410     }
411     if (sel.flags & SEL_ATOMVAL)
412     {
413         fprintf(fp, "A");
414     }
415     if (sel.flags & SEL_VARNUMVAL)
416     {
417         fprintf(fp, "V");
418     }
419     if (sel.flags & SEL_DYNAMIC)
420     {
421         fprintf(fp, "D");
422     }
423     if (!(sel.flags & SEL_VALFLAGMASK))
424     {
425         fprintf(fp, "0");
426     }
427     if (sel.mempool)
428     {
429         fprintf(fp, "P");
430     }
431     if (sel.type == SEL_CONST)
432     {
433         if (sel.v.type == INT_VALUE)
434         {
435             fprintf(fp, " %d", sel.v.u.i[0]);
436         }
437         else if (sel.v.type == REAL_VALUE)
438         {
439             fprintf(fp, " %f", sel.v.u.r[0]);
440         }
441         else if (sel.v.type == GROUP_VALUE)
442         {
443             const gmx_ana_index_t *g = sel.v.u.g;
444             if (!g || g->isize == 0)
445                 g = &sel.u.cgrp;
446             fprintf(fp, " (%d atoms)", g->isize);
447         }
448     }
449     else if (sel.type == SEL_BOOLEAN)
450     {
451         fprintf(fp, " %s", _gmx_selelem_boolean_type_str(sel));
452     }
453     else if (sel.type == SEL_EXPRESSION
454              && sel.u.expr.method->name == sm_compare.name)
455     {
456         _gmx_selelem_print_compare_info(fp, sel.u.expr.mdata);
457     }
458     if (sel.evaluate)
459     {
460         fprintf(fp, " eval=");
461         _gmx_sel_print_evalfunc_name(fp, sel.evaluate);
462     }
463     if (!(sel.flags & SEL_ALLOCVAL))
464     {
465         fprintf(fp, " (ext. output)");
466     }
467     fprintf(fp, "\n");
468
469     if ((sel.type == SEL_CONST && sel.v.type == GROUP_VALUE) || sel.type == SEL_ROOT)
470     {
471         const gmx_ana_index_t *g = sel.v.u.g;
472         if (!g || g->isize == 0 || sel.evaluate != NULL)
473         {
474             g = &sel.u.cgrp;
475         }
476         if (g->isize < 0)
477         {
478             fprintf(fp, "%*c group: (null)\n", level*2+1, ' ');
479         }
480         else if (g->isize > 0)
481         {
482             fprintf(fp, "%*c group:", level*2+1, ' ');
483             if (g->isize <= 20)
484             {
485                 for (i = 0; i < g->isize; ++i)
486                 {
487                     fprintf(fp, " %d", g->index[i] + 1);
488                 }
489             }
490             else
491             {
492                 fprintf(fp, " %d atoms", g->isize);
493             }
494             fprintf(fp, "\n");
495         }
496     }
497     else if (sel.type == SEL_EXPRESSION)
498     {
499         if (sel.u.expr.pc)
500         {
501             fprintf(fp, "%*c COM", level*2+3, '*');
502             fprintf(fp, "\n");
503         }
504     }
505
506     if (sel.cdata)
507     {
508         _gmx_selelem_print_compiler_info(fp, sel, level);
509     }
510
511     if (bValues && sel.type != SEL_CONST && sel.type != SEL_ROOT && sel.v.u.ptr)
512     {
513         fprintf(fp, "%*c value: ", level*2+1, ' ');
514         switch (sel.v.type)
515         {
516             case POS_VALUE:
517                 /* In normal use, the pointer should never be NULL, but it's
518                  * useful to have the check for debugging to avoid accidental
519                  * segfaults when printing the selection tree. */
520                 if (sel.v.u.p->x)
521                 {
522                     fprintf(fp, "(%f, %f, %f)",
523                             sel.v.u.p->x[0][XX], sel.v.u.p->x[0][YY],
524                             sel.v.u.p->x[0][ZZ]);
525                 }
526                 else
527                 {
528                     fprintf(fp, "(null)");
529                 }
530                 break;
531             case GROUP_VALUE:
532                 fprintf(fp, "%d atoms", sel.v.u.g->isize);
533                 if (sel.v.u.g->isize < 20)
534                 {
535                     if (sel.v.u.g->isize > 0)
536                     {
537                         fprintf(fp, ":");
538                     }
539                     for (i = 0; i < sel.v.u.g->isize; ++i)
540                     {
541                         fprintf(fp, " %d", sel.v.u.g->index[i] + 1);
542                     }
543                 }
544                 break;
545             default:
546                 fprintf(fp, "???");
547                 break;
548         }
549         fprintf(fp, "\n");
550     }
551
552     /* Print the subexpressions with one more level of indentation */
553     gmx::SelectionTreeElementPointer child = sel.child;
554     while (child)
555     {
556         if (!(sel.type == SEL_SUBEXPRREF && child->type == SEL_SUBEXPR))
557         {
558             _gmx_selelem_print_tree(fp, *child, bValues, level+1);
559         }
560         child = child->next;
561     }
562 }
563
564 /*!
565  * \param[in] root Root of the subtree to query.
566  * \returns true if \p root or any any of its elements require topology
567  *   information, false otherwise.
568  */
569 bool
570 _gmx_selelem_requires_top(const gmx::SelectionTreeElement &root)
571 {
572     if (root.type == SEL_EXPRESSION || root.type == SEL_MODIFIER)
573     {
574         if (root.u.expr.method && (root.u.expr.method->flags & SMETH_REQTOP))
575         {
576             return true;
577         }
578         if (root.u.expr.pc && gmx_ana_poscalc_requires_top(root.u.expr.pc))
579         {
580             return true;
581         }
582     }
583     gmx::SelectionTreeElementPointer child = root.child;
584     while (child)
585     {
586         if (_gmx_selelem_requires_top(*child))
587         {
588             return true;
589         }
590         child = child->next;
591     }
592     return false;
593 }