5c998412ee7c6778eda248d48a5fe989ff847c0b
[alexxy/gromacs.git] / src / gromacs / selection / selelem.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017,2018, 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 functions in selelem.h.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_selection
41  */
42 #include "gmxpre.h"
43
44 #include "selelem.h"
45
46 #include <cstring>
47
48 #include "gromacs/selection/indexutil.h"
49 #include "gromacs/selection/position.h"
50 #include "gromacs/selection/selectionenums.h"
51 #include "gromacs/utility/exceptions.h"
52 #include "gromacs/utility/gmxassert.h"
53 #include "gromacs/utility/smalloc.h"
54 #include "gromacs/utility/stringutil.h"
55
56 #include "keywords.h"
57 #include "mempool.h"
58 #include "poscalc.h"
59 #include "selmethod.h"
60
61 /*!
62  * \param[in] sel Selection for which the string is requested
63  * \returns   Pointer to a string that corresponds to \p sel->type.
64  *
65  * The return value points to a string constant and should not be \p free'd.
66  *
67  * The function returns NULL if \p sel->type is not one of the valid values.
68  */
69 const char *
70 _gmx_selelem_type_str(const gmx::SelectionTreeElement &sel)
71 {
72     const char *p = nullptr;
73     switch (sel.type)
74     {
75         case SEL_CONST:      p = "CONST";    break;
76         case SEL_EXPRESSION: p = "EXPR";     break;
77         case SEL_BOOLEAN:    p = "BOOL";     break;
78         case SEL_ARITHMETIC: p = "ARITH";    break;
79         case SEL_ROOT:       p = "ROOT";     break;
80         case SEL_SUBEXPR:    p = "SUBEXPR";  break;
81         case SEL_SUBEXPRREF: p = "REF";      break;
82         case SEL_GROUPREF:   p = "GROUPREF"; break;
83         case SEL_MODIFIER:   p = "MODIFIER"; break;
84             // No default clause so we intentionally get compiler errors
85             // if new selection choices are added later.
86     }
87     return p;
88 }
89
90 /*!
91  * \param[in] val Value structore for which the string is requested.
92  * \returns   Pointer to a string that corresponds to \p val->type,
93  *   NULL if the type value is invalid.
94  *
95  * The return value points to a string constant and should not be \p free'd.
96  */
97 const char *
98 _gmx_sel_value_type_str(const gmx_ana_selvalue_t *val)
99 {
100     const char *p = nullptr;
101     switch (val->type)
102     {
103         case NO_VALUE:       p = "NONE";  break;
104         case INT_VALUE:      p = "INT";   break;
105         case REAL_VALUE:     p = "REAL";  break;
106         case STR_VALUE:      p = "STR";   break;
107         case POS_VALUE:      p = "VEC";   break;
108         case GROUP_VALUE:    p = "GROUP"; break;
109             // No default clause so we intentionally get compiler errors
110             // if new selection choices are added later.
111     }
112     return p;
113 }
114
115 /*! \copydoc _gmx_selelem_type_str() */
116 const char *
117 _gmx_selelem_boolean_type_str(const gmx::SelectionTreeElement &sel)
118 {
119     const char *p = nullptr;
120     switch (sel.u.boolt)
121     {
122         case BOOL_NOT:  p = "NOT"; break;
123         case BOOL_AND:  p = "AND"; break;
124         case BOOL_OR:   p = "OR";  break;
125         case BOOL_XOR:  p = "XOR"; break;
126             // No default clause so we intentionally get compiler errors
127             // if new selection choices are added later.
128     }
129     return p;
130 }
131
132
133 namespace gmx
134 {
135
136 SelectionTreeElement::SelectionTreeElement(e_selelem_t              type,
137                                            const SelectionLocation &location)
138     : location_(location)
139 {
140     this->type       = type;
141     this->flags      = (type != SEL_ROOT) ? SEL_ALLOCVAL : 0;
142     if (type == SEL_BOOLEAN)
143     {
144         this->v.type = GROUP_VALUE;
145         this->flags |= SEL_ALLOCDATA;
146     }
147     else
148     {
149         this->v.type = NO_VALUE;
150     }
151     _gmx_selvalue_clear(&this->v);
152     std::memset(&this->u, 0, sizeof(this->u));
153     this->evaluate   = nullptr;
154     this->mempool    = nullptr;
155     this->cdata      = nullptr;
156 }
157
158 SelectionTreeElement::~SelectionTreeElement()
159 {
160     /* Free the children.
161      * Must be done before freeing other data, because the children may hold
162      * references to data in this element. */
163     child.reset();
164
165     freeValues();
166     freeExpressionData();
167     freeCompilerData();
168 }
169
170 void SelectionTreeElement::freeValues()
171 {
172     mempoolRelease();
173     if ((flags & SEL_ALLOCDATA) && v.u.ptr)
174     {
175         /* The number of position/group structures is constant, so the
176          * backup of using sel->v.nr should work for them.
177          * For strings, we report an error if we don't know the allocation
178          * size here. */
179         int n = (v.nalloc > 0) ? v.nalloc : v.nr;
180         switch (v.type)
181         {
182             case STR_VALUE:
183                 GMX_RELEASE_ASSERT(v.nalloc != 0,
184                                    "SEL_ALLOCDATA should only be set for allocated "
185                                    "STR_VALUE values");
186                 for (int i = 0; i < n; ++i)
187                 {
188                     sfree(v.u.s[i]);
189                 }
190                 break;
191             case GROUP_VALUE:
192                 for (int i = 0; i < n; ++i)
193                 {
194                     gmx_ana_index_deinit(&v.u.g[i]);
195                 }
196                 break;
197             default: /* No special handling for other types */
198                 break;
199         }
200     }
201     _gmx_selvalue_free(&v);
202     if (type == SEL_SUBEXPRREF && u.param != nullptr)
203     {
204         // TODO: This is now called from two different locations.
205         // It is likely that one of them is unnecessary, but that requires
206         // extra analysis to clarify.
207         _gmx_selelem_free_param(u.param);
208     }
209 }
210
211 void
212 SelectionTreeElement::freeExpressionData()
213 {
214     if (type == SEL_EXPRESSION || type == SEL_MODIFIER)
215     {
216         _gmx_selelem_free_method(u.expr.method, u.expr.mdata);
217         u.expr.mdata  = nullptr;
218         u.expr.method = nullptr;
219         /* Free position data */
220         delete u.expr.pos;
221         u.expr.pos = nullptr;
222         /* Free position calculation data */
223         if (u.expr.pc)
224         {
225             gmx_ana_poscalc_free(u.expr.pc);
226             u.expr.pc = nullptr;
227         }
228     }
229     if (type == SEL_ARITHMETIC)
230     {
231         sfree(u.arith.opstr);
232         u.arith.opstr = nullptr;
233     }
234     if (type == SEL_SUBEXPR || type == SEL_ROOT
235         || (type == SEL_CONST && v.type == GROUP_VALUE))
236     {
237         gmx_ana_index_deinit(&u.cgrp);
238     }
239     if (type == SEL_GROUPREF)
240     {
241         sfree(u.gref.name);
242     }
243 }
244
245 void SelectionTreeElement::mempoolReserve(int count)
246 {
247     if (!mempool)
248     {
249         return;
250     }
251     switch (v.type)
252     {
253         case INT_VALUE:
254             v.u.i = static_cast<int *>(
255                     _gmx_sel_mempool_alloc(mempool, sizeof(*v.u.i)*count));
256             break;
257
258         case REAL_VALUE:
259             v.u.r = static_cast<real *>(
260                     _gmx_sel_mempool_alloc(mempool, sizeof(*v.u.r)*count));
261             break;
262
263         case GROUP_VALUE:
264             _gmx_sel_mempool_alloc_group(mempool, v.u.g, count);
265             break;
266
267         default:
268             GMX_THROW(gmx::InternalError("Memory pooling not implemented for requested type"));
269     }
270 }
271
272 void SelectionTreeElement::mempoolRelease()
273 {
274     if (!mempool)
275     {
276         return;
277     }
278     switch (v.type)
279     {
280         case INT_VALUE:
281         case REAL_VALUE:
282             _gmx_sel_mempool_free(mempool, v.u.ptr);
283             _gmx_selvalue_setstore(&v, nullptr);
284             break;
285
286         case GROUP_VALUE:
287             if (v.u.g)
288             {
289                 _gmx_sel_mempool_free_group(mempool, v.u.g);
290             }
291             break;
292
293         default:
294             GMX_THROW(gmx::InternalError("Memory pooling not implemented for requested type"));
295     }
296 }
297
298 void SelectionTreeElement::fillNameIfMissing(const char *selectionText)
299 {
300     GMX_RELEASE_ASSERT(type == SEL_ROOT,
301                        "Should not be called for non-root elements");
302     if (name().empty())
303     {
304         // Check whether the actual selection given was from an external group,
305         // and if so, use the name of the external group.
306         SelectionTreeElementPointer child = this->child;
307         if (_gmx_selelem_is_default_kwpos(*child) && child->child
308             && child->child->type == SEL_SUBEXPRREF && child->child->child)
309         {
310             if (child->child->child->type == SEL_CONST
311                 && child->child->child->v.type == GROUP_VALUE)
312             {
313                 setName(child->child->child->name());
314                 return;
315             }
316             // If the group reference is still unresolved, leave the name empty
317             // and fill it later.
318             if (child->child->child->type == SEL_GROUPREF)
319             {
320                 return;
321             }
322         }
323         // If there still is no name, use the selection string.
324         setName(selectionText);
325     }
326 }
327
328 SelectionTopologyProperties
329 SelectionTreeElement::requiredTopologyProperties() const
330 {
331     SelectionTopologyProperties props;
332     if (type == SEL_EXPRESSION || type == SEL_MODIFIER)
333     {
334         bool needsTop    = false;
335         bool needsMasses = false;
336         if (u.expr.method != nullptr)
337         {
338             needsTop    = ((u.expr.method->flags & SMETH_REQTOP) != 0);
339             needsMasses = ((u.expr.method->flags & SMETH_REQMASS) != 0);
340         }
341         if (u.expr.pc != nullptr)
342         {
343             auto requiredTopologyInfo = gmx_ana_poscalc_required_topology_info(u.expr.pc);
344             needsTop    = needsTop
345                 || (requiredTopologyInfo != PositionCalculationCollection::RequiredTopologyInfo::None);
346             needsMasses = needsMasses
347                 || (requiredTopologyInfo == PositionCalculationCollection::RequiredTopologyInfo::TopologyAndMasses);
348         }
349         if (needsTop)
350         {
351             props.merge(SelectionTopologyProperties::topology());
352         }
353         if (needsMasses)
354         {
355             props.merge(SelectionTopologyProperties::masses());
356         }
357     }
358     SelectionTreeElementPointer child = this->child;
359     while (child && !props.hasAll())
360     {
361         props.merge(child->requiredTopologyProperties());
362         child = child->next;
363     }
364     return props;
365 }
366
367 void SelectionTreeElement::checkUnsortedAtoms(
368         bool bUnsortedAllowed, ExceptionInitializer *errors) const
369 {
370     const bool bUnsortedSupported
371         = (type == SEL_CONST && v.type == GROUP_VALUE)
372             || type == SEL_ROOT || type == SEL_SUBEXPR || type == SEL_SUBEXPRREF
373             // TODO: Consolidate.
374             || type == SEL_MODIFIER
375             || (type == SEL_EXPRESSION && ((u.expr.method->flags & SMETH_ALLOW_UNSORTED) != 0));
376
377     // TODO: For some complicated selections, this may result in the same
378     // index group reference being flagged as an error multiple times for the
379     // same selection.
380     SelectionTreeElementPointer child = this->child;
381     while (child)
382     {
383         child->checkUnsortedAtoms(bUnsortedAllowed && bUnsortedSupported,
384                                   errors);
385         child = child->next;
386     }
387
388     // The logic here is simplified by the fact that only constant groups can
389     // currently be the root cause of SEL_UNSORTED being set, so only those
390     // need to be considered in triggering the error.
391     if (!bUnsortedAllowed && (flags & SEL_UNSORTED)
392         && type == SEL_CONST && v.type == GROUP_VALUE)
393     {
394         std::string message = formatString(
395                     "Group '%s' cannot be used in selections except "
396                     "as a full value of the selection, "
397                     "because atom indices in it are not sorted and/or "
398                     "it contains duplicate atoms.",
399                     name().c_str());
400         errors->addNested(InconsistentInputError(message));
401     }
402 }
403
404 bool SelectionTreeElement::requiresIndexGroups() const
405 {
406     if (type == SEL_GROUPREF)
407     {
408         return true;
409     }
410     SelectionTreeElementPointer child = this->child;
411     while (child)
412     {
413         if (child->requiresIndexGroups())
414         {
415             return true;
416         }
417         child = child->next;
418     }
419     return false;
420 }
421
422 void SelectionTreeElement::resolveIndexGroupReference(
423         gmx_ana_indexgrps_t *grps, int natoms)
424 {
425     GMX_RELEASE_ASSERT(type == SEL_GROUPREF,
426                        "Should only be called for index group reference elements");
427     if (grps == nullptr)
428     {
429         std::string message = formatString(
430                     "Cannot match '%s', because index groups are not available.",
431                     name().c_str());
432         GMX_THROW(InconsistentInputError(message));
433     }
434
435     gmx_ana_index_t foundGroup;
436     std::string     foundName;
437     if (u.gref.name != nullptr)
438     {
439         if (!gmx_ana_indexgrps_find(&foundGroup, &foundName, grps, u.gref.name))
440         {
441             std::string message = formatString(
442                         "Cannot match '%s', because no such index group can be found.",
443                         name().c_str());
444             GMX_THROW(InconsistentInputError(message));
445         }
446     }
447     else
448     {
449         if (!gmx_ana_indexgrps_extract(&foundGroup, &foundName, grps, u.gref.id))
450         {
451             std::string message = formatString(
452                         "Cannot match '%s', because no such index group can be found.",
453                         name().c_str());
454             GMX_THROW(InconsistentInputError(message));
455         }
456     }
457
458     if (!gmx_ana_index_check_sorted(&foundGroup))
459     {
460         flags |= SEL_UNSORTED;
461     }
462
463     sfree(u.gref.name);
464     type = SEL_CONST;
465     gmx_ana_index_set(&u.cgrp, foundGroup.isize, foundGroup.index,
466                       foundGroup.nalloc_index);
467     setName(foundName);
468
469     if (natoms > 0)
470     {
471         checkIndexGroup(natoms);
472     }
473 }
474
475 void SelectionTreeElement::checkIndexGroup(int natoms)
476 {
477     GMX_RELEASE_ASSERT(type == SEL_CONST && v.type == GROUP_VALUE,
478                        "Should only be called for index group elements");
479     if (!gmx_ana_index_check_range(&u.cgrp, natoms))
480     {
481         std::string message = formatString(
482                     "Group '%s' cannot be used in selections, because it "
483                     "contains negative atom indices and/or references atoms "
484                     "not present (largest allowed atom index is %d).",
485                     name().c_str(), natoms);
486         GMX_THROW(InconsistentInputError(message));
487     }
488 }
489
490 } // namespace gmx
491
492 /*!
493  * \param[in,out] sel   Selection element to set the type for.
494  * \param[in]     vtype Value type for the selection element.
495  *
496  * If the new type is \ref GROUP_VALUE or \ref POS_VALUE, the
497  * \ref SEL_ALLOCDATA flag is also set.
498  *
499  * This function should only be called at most once for each element,
500  * preferably right after calling _gmx_selelem_create().
501  */
502 void
503 _gmx_selelem_set_vtype(const gmx::SelectionTreeElementPointer &sel,
504                        e_selvalue_t                            vtype)
505 {
506     GMX_RELEASE_ASSERT(sel->type != SEL_BOOLEAN || vtype == GROUP_VALUE,
507                        "Boolean elements must have a group value");
508     GMX_RELEASE_ASSERT(sel->v.type == NO_VALUE || vtype == sel->v.type,
509                        "_gmx_selelem_set_vtype() called more than once");
510     sel->v.type = vtype;
511     if (vtype == GROUP_VALUE || vtype == POS_VALUE)
512     {
513         sel->flags |= SEL_ALLOCDATA;
514     }
515 }
516
517 void
518 _gmx_selelem_free_param(gmx_ana_selparam_t *param)
519 {
520     if (param->val.u.ptr != nullptr)
521     {
522         if (param->val.type == GROUP_VALUE)
523         {
524             for (int i = 0; i < param->val.nr; ++i)
525             {
526                 gmx_ana_index_deinit(&param->val.u.g[i]);
527             }
528         }
529         _gmx_selvalue_free(&param->val);
530     }
531 }
532
533 void
534 _gmx_selelem_free_method(gmx_ana_selmethod_t *method, void *mdata)
535 {
536     sel_freefunc free_func = nullptr;
537
538     /* Save the pointer to the free function. */
539     if (method && method->free)
540     {
541         free_func = method->free;
542     }
543
544     /* Free the method itself.
545      * Has to be done before freeing the method data, because parameter
546      * values are typically stored in the method data, and here we may
547      * access them. */
548     if (method)
549     {
550         /* Free the memory allocated for the parameters that are not managed
551          * by the selection method itself. */
552         for (int i = 0; i < method->nparams; ++i)
553         {
554             _gmx_selelem_free_param(&method->param[i]);
555         }
556         sfree(method->param);
557         sfree(method);
558     }
559     /* Free method data. */
560     if (mdata)
561     {
562         if (free_func)
563         {
564             free_func(mdata);
565         }
566         else
567         {
568             sfree(mdata);
569         }
570     }
571 }
572
573 /*!
574  * \param[in] fp      File handle to receive the output.
575  * \param[in] sel     Root of the selection subtree to print.
576  * \param[in] bValues If true, the evaluated values of selection elements
577  *   are printed as well.
578  * \param[in] level   Indentation level, starting from zero.
579  */
580 void
581 _gmx_selelem_print_tree(FILE *fp, const gmx::SelectionTreeElement &sel,
582                         bool bValues, int level)
583 {
584     int          i;
585
586     fprintf(fp, "%*c %s %s", level*2+1, '*',
587             _gmx_selelem_type_str(sel), _gmx_sel_value_type_str(&sel.v));
588     if (!sel.name().empty())
589     {
590         fprintf(fp, " \"%s\"", sel.name().c_str());
591     }
592     fprintf(fp, " flg=");
593     if (sel.flags & SEL_FLAGSSET)
594     {
595         fprintf(fp, "s");
596     }
597     if (sel.flags & SEL_SINGLEVAL)
598     {
599         fprintf(fp, "S");
600     }
601     if (sel.flags & SEL_ATOMVAL)
602     {
603         fprintf(fp, "A");
604     }
605     if (sel.flags & SEL_VARNUMVAL)
606     {
607         fprintf(fp, "V");
608     }
609     if (sel.flags & SEL_DYNAMIC)
610     {
611         fprintf(fp, "D");
612     }
613     if (!(sel.flags & SEL_VALFLAGMASK))
614     {
615         fprintf(fp, "0");
616     }
617     if (sel.flags & SEL_ALLOCVAL)
618     {
619         fprintf(fp, "Av");
620     }
621     if (sel.flags & SEL_ALLOCDATA)
622     {
623         fprintf(fp, "Ad");
624     }
625     if (sel.mempool)
626     {
627         fprintf(fp, "P");
628     }
629     if (sel.type == SEL_CONST)
630     {
631         if (sel.v.type == INT_VALUE)
632         {
633             fprintf(fp, " %d", sel.v.u.i[0]);
634         }
635         else if (sel.v.type == REAL_VALUE)
636         {
637             fprintf(fp, " %f", sel.v.u.r[0]);
638         }
639         else if (sel.v.type == GROUP_VALUE)
640         {
641             const gmx_ana_index_t *g = sel.v.u.g;
642             if (!g || g->isize == 0)
643             {
644                 g = &sel.u.cgrp;
645             }
646             fprintf(fp, " (%d atoms)", g->isize);
647         }
648     }
649     else if (sel.type == SEL_BOOLEAN)
650     {
651         fprintf(fp, " %s", _gmx_selelem_boolean_type_str(sel));
652     }
653     else if (sel.type == SEL_EXPRESSION
654              && sel.u.expr.method->name == sm_compare.name)
655     {
656         _gmx_selelem_print_compare_info(fp, sel.u.expr.mdata);
657     }
658     if (sel.evaluate)
659     {
660         fprintf(fp, " eval=");
661         _gmx_sel_print_evalfunc_name(fp, sel.evaluate);
662     }
663     if (sel.v.nalloc < 0)
664     {
665         fprintf(fp, " (ext)");
666     }
667     fprintf(fp, "\n");
668
669     if ((sel.type == SEL_CONST && sel.v.type == GROUP_VALUE) || sel.type == SEL_ROOT)
670     {
671         const gmx_ana_index_t *g = sel.v.u.g;
672         if (!g || g->isize == 0 || sel.evaluate != nullptr)
673         {
674             g = &sel.u.cgrp;
675         }
676         if (g->isize < 0)
677         {
678             fprintf(fp, "%*c group: (null)\n", level*2+1, ' ');
679         }
680         else if (g->isize > 0)
681         {
682             fprintf(fp, "%*c group:", level*2+1, ' ');
683             if (g->isize <= 20)
684             {
685                 for (i = 0; i < g->isize; ++i)
686                 {
687                     fprintf(fp, " %d", g->index[i] + 1);
688                 }
689             }
690             else
691             {
692                 fprintf(fp, " %d atoms", g->isize);
693             }
694             fprintf(fp, "\n");
695         }
696     }
697     else if (sel.type == SEL_EXPRESSION)
698     {
699         if (sel.u.expr.pc)
700         {
701             fprintf(fp, "%*c COM", level*2+3, '*');
702             fprintf(fp, "\n");
703         }
704     }
705     else if (sel.type == SEL_SUBEXPRREF && sel.u.param != nullptr)
706     {
707         fprintf(fp, "%*c param", level*2+1, ' ');
708         if (sel.u.param->name != nullptr)
709         {
710             fprintf(fp, " \"%s\"", sel.u.param->name);
711         }
712         if (sel.u.param->val.nalloc < 0)
713         {
714             fprintf(fp, " (ext)");
715         }
716         else
717         {
718             fprintf(fp, " nalloc: %d", sel.u.param->val.nalloc);
719         }
720         fprintf(fp, "\n");
721     }
722
723     if (sel.cdata)
724     {
725         _gmx_selelem_print_compiler_info(fp, sel, level);
726     }
727
728     if (bValues && sel.type != SEL_CONST && sel.type != SEL_ROOT && sel.v.u.ptr)
729     {
730         fprintf(fp, "%*c value: ", level*2+1, ' ');
731         switch (sel.v.type)
732         {
733             case POS_VALUE:
734                 /* In normal use, the pointer should never be NULL, but it's
735                  * useful to have the check for debugging to avoid accidental
736                  * segfaults when printing the selection tree. */
737                 if (sel.v.u.p->x)
738                 {
739                     fprintf(fp, "(%f, %f, %f)",
740                             sel.v.u.p->x[0][XX], sel.v.u.p->x[0][YY],
741                             sel.v.u.p->x[0][ZZ]);
742                 }
743                 else
744                 {
745                     fprintf(fp, "(null)");
746                 }
747                 break;
748             case GROUP_VALUE:
749                 fprintf(fp, "%d atoms", sel.v.u.g->isize);
750                 if (sel.v.u.g->isize < 20)
751                 {
752                     if (sel.v.u.g->isize > 0)
753                     {
754                         fprintf(fp, ":");
755                     }
756                     for (i = 0; i < sel.v.u.g->isize; ++i)
757                     {
758                         fprintf(fp, " %d", sel.v.u.g->index[i] + 1);
759                     }
760                 }
761                 break;
762             default:
763                 fprintf(fp, "???");
764                 break;
765         }
766         fprintf(fp, "\n");
767     }
768
769     /* Print the subexpressions with one more level of indentation */
770     gmx::SelectionTreeElementPointer child = sel.child;
771     while (child)
772     {
773         if (!(sel.type == SEL_SUBEXPRREF && child->type == SEL_SUBEXPR))
774         {
775             _gmx_selelem_print_tree(fp, *child, bValues, level+1);
776         }
777         child = child->next;
778     }
779 }