Make some unit tests use mock file output
[alexxy/gromacs.git] / src / gromacs / selection / selectioncollection.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 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 gmx::SelectionCollection.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_selection
41  */
42 #include "gmxpre.h"
43
44 #include "selectioncollection.h"
45
46 #include <cctype>
47 #include <cstdio>
48
49 #include <string>
50 #include <vector>
51
52 #include <boost/shared_ptr.hpp>
53
54 #include "gromacs/fileio/trx.h"
55 #include "gromacs/legacyheaders/oenv.h"
56 #include "gromacs/onlinehelp/helpmanager.h"
57 #include "gromacs/onlinehelp/helpwritercontext.h"
58 #include "gromacs/options/basicoptions.h"
59 #include "gromacs/options/options.h"
60 #include "gromacs/selection/selection.h"
61 #include "gromacs/selection/selhelp.h"
62 #include "gromacs/topology/topology.h"
63 #include "gromacs/utility/exceptions.h"
64 #include "gromacs/utility/file.h"
65 #include "gromacs/utility/filestream.h"
66 #include "gromacs/utility/gmxassert.h"
67 #include "gromacs/utility/smalloc.h"
68 #include "gromacs/utility/stringutil.h"
69
70 #include "compiler.h"
71 #include "mempool.h"
72 #include "parser.h"
73 #include "poscalc.h"
74 #include "scanner.h"
75 #include "selectioncollection-impl.h"
76 #include "selelem.h"
77 #include "selmethod.h"
78 #include "symrec.h"
79
80 namespace gmx
81 {
82
83 /********************************************************************
84  * SelectionCollection::Impl
85  */
86
87 SelectionCollection::Impl::Impl()
88     : maxAtomIndex_(0), debugLevel_(0), bExternalGroupsSet_(false), grps_(NULL)
89 {
90     sc_.nvars     = 0;
91     sc_.varstrs   = NULL;
92     sc_.top       = NULL;
93     gmx_ana_index_clear(&sc_.gall);
94     sc_.mempool   = NULL;
95     sc_.symtab.reset(new SelectionParserSymbolTable);
96     gmx_ana_selmethod_register_defaults(sc_.symtab.get());
97 }
98
99
100 SelectionCollection::Impl::~Impl()
101 {
102     clearSymbolTable();
103     // The tree must be freed before the SelectionData objects, since the
104     // tree may hold references to the position data in SelectionData.
105     sc_.root.reset();
106     sc_.sel.clear();
107     for (int i = 0; i < sc_.nvars; ++i)
108     {
109         sfree(sc_.varstrs[i]);
110     }
111     sfree(sc_.varstrs);
112     gmx_ana_index_deinit(&sc_.gall);
113     if (sc_.mempool)
114     {
115         _gmx_sel_mempool_destroy(sc_.mempool);
116     }
117 }
118
119
120 void
121 SelectionCollection::Impl::clearSymbolTable()
122 {
123     sc_.symtab.reset();
124 }
125
126
127 namespace
128 {
129
130 /*! \brief
131  * Reads a single selection line from stdin.
132  *
133  * \param[in]  infile        File to read from (typically File::standardInput()).
134  * \param[in]  bInteractive  Whether to print interactive prompts.
135  * \param[out] line          The read line in stored here.
136  * \returns true if something was read, false if at end of input.
137  *
138  * Handles line continuation, reading also the continuing line(s) in one call.
139  */
140 bool promptLine(File *infile, bool bInteractive, std::string *line)
141 {
142     if (bInteractive)
143     {
144         fprintf(stderr, "> ");
145     }
146     if (!infile->readLineWithTrailingSpace(line))
147     {
148         return false;
149     }
150     while (endsWith(*line, "\\\n"))
151     {
152         line->resize(line->length() - 2);
153         if (bInteractive)
154         {
155             fprintf(stderr, "... ");
156         }
157         std::string buffer;
158         // Return value ignored, buffer remains empty and works correctly
159         // if there is nothing to read.
160         infile->readLineWithTrailingSpace(&buffer);
161         line->append(buffer);
162     }
163     if (endsWith(*line, "\n"))
164     {
165         line->resize(line->length() - 1);
166     }
167     else if (bInteractive)
168     {
169         fprintf(stderr, "\n");
170     }
171     return true;
172 }
173
174 /*! \brief
175  * Helper function for tokenizing the input and pushing them to the parser.
176  *
177  * \param     scanner       Tokenizer data structure.
178  * \param     parserState   Parser data structure.
179  * \param[in] bInteractive  Whether to operate in interactive mode.
180  *
181  * Repeatedly reads tokens using \p scanner and pushes them to the parser with
182  * \p parserState until there is no more input, or until enough input is given
183  * (only in interactive mode).
184  */
185 int runParserLoop(yyscan_t scanner, _gmx_sel_yypstate *parserState,
186                   bool bInteractive)
187 {
188     int status    = YYPUSH_MORE;
189     int prevToken = 0;
190     do
191     {
192         YYSTYPE value;
193         YYLTYPE location;
194         int     token = _gmx_sel_yylex(&value, &location, scanner);
195         if (bInteractive)
196         {
197             if (token == 0)
198             {
199                 break;
200             }
201             // Empty commands cause the interactive parser to print out
202             // status information. This avoids producing those unnecessarily,
203             // e.g., from "resname RA;;".
204             if (prevToken == CMD_SEP && token == CMD_SEP)
205             {
206                 continue;
207             }
208             prevToken = token;
209         }
210         status = _gmx_sel_yypush_parse(parserState, token, &value, &location, scanner);
211     }
212     while (status == YYPUSH_MORE);
213     _gmx_sel_lexer_rethrow_exception_if_occurred(scanner);
214     return status;
215 }
216
217 /*! \brief
218  * Print current status in response to empty line in interactive input.
219  *
220  * \param[in] sc             Selection collection data structure.
221  * \param[in] grps           Available index groups.
222  * \param[in] firstSelection Index of first selection from this interactive
223  *     session.
224  * \param[in] maxCount       Maximum number of selections.
225  * \param[in] context        Context to print for what the selections are for.
226  * \param[in] bFirst         Whether this is the header that is printed before
227  *     any user input.
228  *
229  * Prints the available index groups and currently provided selections.
230  */
231 void printCurrentStatus(gmx_ana_selcollection_t *sc, gmx_ana_indexgrps_t *grps,
232                         size_t firstSelection, int maxCount,
233                         const std::string &context, bool bFirst)
234 {
235     if (grps != NULL)
236     {
237         std::fprintf(stderr, "Available static index groups:\n");
238         gmx_ana_indexgrps_print(stderr, grps, 0);
239     }
240     std::fprintf(stderr, "Specify ");
241     if (maxCount < 0)
242     {
243         std::fprintf(stderr, "any number of selections");
244     }
245     else if (maxCount == 1)
246     {
247         std::fprintf(stderr, "a selection");
248     }
249     else
250     {
251         std::fprintf(stderr, "%d selections", maxCount);
252     }
253     std::fprintf(stderr, "%s%s:\n",
254                  context.empty() ? "" : " ", context.c_str());
255     std::fprintf(stderr,
256                  "(one per line, <enter> for status/groups, 'help' for help%s)\n",
257                  maxCount < 0 ? ", Ctrl-D to end" : "");
258     if (!bFirst && (sc->nvars > 0 || sc->sel.size() > firstSelection))
259     {
260         std::fprintf(stderr, "Currently provided selections:\n");
261         for (int i = 0; i < sc->nvars; ++i)
262         {
263             std::fprintf(stderr, "     %s\n", sc->varstrs[i]);
264         }
265         for (size_t i = firstSelection; i < sc->sel.size(); ++i)
266         {
267             std::fprintf(stderr, " %2d. %s\n",
268                          static_cast<int>(i - firstSelection + 1),
269                          sc->sel[i]->selectionText());
270         }
271         if (maxCount > 0)
272         {
273             const int remaining
274                 = maxCount - static_cast<int>(sc->sel.size() - firstSelection);
275             std::fprintf(stderr, "(%d more selection%s required)\n",
276                          remaining, remaining > 1 ? "s" : "");
277         }
278     }
279 }
280
281 /*! \brief
282  * Prints selection help in interactive selection input.
283  *
284  * \param[in] sc    Selection collection data structure.
285  * \param[in] line  Line of user input requesting help (starting with `help`).
286  *
287  * Initializes the selection help if not yet initialized, and finds the help
288  * topic based on words on the input line.
289  */
290 void printHelp(gmx_ana_selcollection_t *sc, const std::string &line)
291 {
292     if (sc->rootHelp.get() == NULL)
293     {
294         sc->rootHelp = createSelectionHelpTopic();
295     }
296     HelpWriterContext context(&TextOutputFile::standardError(),
297                               eHelpOutputFormat_Console);
298     HelpManager       manager(*sc->rootHelp, context);
299     try
300     {
301         std::vector<std::string>                 topic = splitString(line);
302         std::vector<std::string>::const_iterator value;
303         // First item in the list is the 'help' token.
304         for (value = topic.begin() + 1; value != topic.end(); ++value)
305         {
306             manager.enterTopic(*value);
307         }
308     }
309     catch (const InvalidInputError &ex)
310     {
311         fprintf(stderr, "%s\n", ex.what());
312         return;
313     }
314     manager.writeCurrentTopic();
315 }
316
317 /*! \brief
318  * Helper function that runs the parser once the tokenizer has been
319  * initialized.
320  *
321  * \param[in,out] scanner Scanner data structure.
322  * \param[in]     bStdIn  Whether to use a line-based reading
323  *      algorithm designed for interactive input.
324  * \param[in]     maxnr   Maximum number of selections to parse
325  *      (if -1, parse as many as provided by the user).
326  * \param[in]     context Context to print for what the selections are for.
327  * \returns       Vector of parsed selections.
328  * \throws        std::bad_alloc if out of memory.
329  * \throws        InvalidInputError if there is a parsing error.
330  *
331  * Used internally to implement parseFromStdin(), parseFromFile() and
332  * parseFromString().
333  */
334 SelectionList runParser(yyscan_t scanner, bool bStdIn, int maxnr,
335                         const std::string &context)
336 {
337     boost::shared_ptr<void>  scannerGuard(scanner, &_gmx_sel_free_lexer);
338     gmx_ana_selcollection_t *sc   = _gmx_sel_lexer_selcollection(scanner);
339     gmx_ana_indexgrps_t     *grps = _gmx_sel_lexer_indexgrps(scanner);
340
341     size_t                   oldCount = sc->sel.size();
342     {
343         boost::shared_ptr<_gmx_sel_yypstate> parserState(
344                 _gmx_sel_yypstate_new(), &_gmx_sel_yypstate_delete);
345         if (bStdIn)
346         {
347             File       &stdinFile(File::standardInput());
348             const bool  bInteractive = _gmx_sel_is_lexer_interactive(scanner);
349             if (bInteractive)
350             {
351                 printCurrentStatus(sc, grps, oldCount, maxnr, context, true);
352             }
353             std::string line;
354             int         status;
355             while (promptLine(&stdinFile, bInteractive, &line))
356             {
357                 if (bInteractive)
358                 {
359                     line = stripString(line);
360                     if (line.empty())
361                     {
362                         printCurrentStatus(sc, grps, oldCount, maxnr, context, false);
363                         continue;
364                     }
365                     if (startsWith(line, "help")
366                         && (line[4] == 0 || std::isspace(line[4])))
367                     {
368                         printHelp(sc, line);
369                         continue;
370                     }
371                 }
372                 line.append("\n");
373                 _gmx_sel_set_lex_input_str(scanner, line.c_str());
374                 status = runParserLoop(scanner, parserState.get(), true);
375                 if (status != YYPUSH_MORE)
376                 {
377                     // TODO: Check if there is more input, and issue an
378                     // error/warning if some input was ignored.
379                     goto early_termination;
380                 }
381             }
382             {
383                 YYLTYPE location;
384                 status = _gmx_sel_yypush_parse(parserState.get(), 0, NULL,
385                                                &location, scanner);
386             }
387             // TODO: Remove added selections from the collection if parsing failed?
388             _gmx_sel_lexer_rethrow_exception_if_occurred(scanner);
389 early_termination:
390             GMX_RELEASE_ASSERT(status == 0,
391                                "Parser errors should have resulted in an exception");
392         }
393         else
394         {
395             int status = runParserLoop(scanner, parserState.get(), false);
396             GMX_RELEASE_ASSERT(status == 0,
397                                "Parser errors should have resulted in an exception");
398         }
399     }
400     scannerGuard.reset();
401     int nr = sc->sel.size() - oldCount;
402     if (maxnr > 0 && nr != maxnr)
403     {
404         std::string message
405             = formatString("Too few selections provided; got %d, expected %d",
406                            nr, maxnr);
407         GMX_THROW(InvalidInputError(message));
408     }
409
410     SelectionList                     result;
411     SelectionDataList::const_iterator i;
412     result.reserve(nr);
413     for (i = sc->sel.begin() + oldCount; i != sc->sel.end(); ++i)
414     {
415         result.push_back(Selection(i->get()));
416     }
417     return result;
418 }
419
420 /*! \brief
421  * Checks that index groups have valid atom indices.
422  *
423  * \param[in]    root    Root of selection tree to process.
424  * \param[in]    natoms  Maximum number of atoms that the selections are set
425  *     to evaluate.
426  * \param        errors  Object for reporting any error messages.
427  * \throws std::bad_alloc if out of memory.
428  *
429  * Recursively checks the selection tree for index groups.
430  * Each found group is checked that it only contains atom indices that match
431  * the topology/maximum number of atoms set for the selection collection.
432  * Any issues are reported to \p errors.
433  */
434 void checkExternalGroups(const SelectionTreeElementPointer &root,
435                          int                                natoms,
436                          ExceptionInitializer              *errors)
437 {
438     if (root->type == SEL_CONST && root->v.type == GROUP_VALUE)
439     {
440         try
441         {
442             root->checkIndexGroup(natoms);
443         }
444         catch (const UserInputError &)
445         {
446             errors->addCurrentExceptionAsNested();
447         }
448     }
449
450     SelectionTreeElementPointer child = root->child;
451     while (child)
452     {
453         checkExternalGroups(child, natoms, errors);
454         child = child->next;
455     }
456 }
457
458 }   // namespace
459
460
461 void SelectionCollection::Impl::resolveExternalGroups(
462         const SelectionTreeElementPointer &root,
463         ExceptionInitializer              *errors)
464 {
465
466     if (root->type == SEL_GROUPREF)
467     {
468         try
469         {
470             root->resolveIndexGroupReference(grps_, sc_.gall.isize);
471         }
472         catch (const UserInputError &)
473         {
474             errors->addCurrentExceptionAsNested();
475         }
476     }
477
478     SelectionTreeElementPointer child = root->child;
479     while (child)
480     {
481         resolveExternalGroups(child, errors);
482         root->flags |= (child->flags & SEL_UNSORTED);
483         child        = child->next;
484     }
485 }
486
487
488 /********************************************************************
489  * SelectionCollection
490  */
491
492 SelectionCollection::SelectionCollection()
493     : impl_(new Impl)
494 {
495 }
496
497
498 SelectionCollection::~SelectionCollection()
499 {
500 }
501
502
503 void
504 SelectionCollection::initOptions(Options *options)
505 {
506     const char * const debug_levels[]
507         = { "no", "basic", "compile", "eval", "full" };
508
509     bool bAllowNonAtomOutput = false;
510     SelectionDataList::const_iterator iter;
511     for (iter = impl_->sc_.sel.begin(); iter != impl_->sc_.sel.end(); ++iter)
512     {
513         const internal::SelectionData &sel = **iter;
514         if (!sel.hasFlag(efSelection_OnlyAtoms))
515         {
516             bAllowNonAtomOutput = true;
517         }
518     }
519
520     const char *const *postypes = PositionCalculationCollection::typeEnumValues;
521     options->addOption(StringOption("selrpos")
522                            .enumValueFromNullTerminatedArray(postypes)
523                            .store(&impl_->rpost_).defaultValue(postypes[0])
524                            .description("Selection reference positions"));
525     if (bAllowNonAtomOutput)
526     {
527         options->addOption(StringOption("seltype")
528                                .enumValueFromNullTerminatedArray(postypes)
529                                .store(&impl_->spost_).defaultValue(postypes[0])
530                                .description("Default selection output positions"));
531     }
532     else
533     {
534         impl_->spost_ = postypes[0];
535     }
536     GMX_RELEASE_ASSERT(impl_->debugLevel_ >= 0 && impl_->debugLevel_ <= 4,
537                        "Debug level out of range");
538     options->addOption(StringOption("seldebug").hidden(impl_->debugLevel_ == 0)
539                            .enumValue(debug_levels)
540                            .defaultValue(debug_levels[impl_->debugLevel_])
541                            .storeEnumIndex(&impl_->debugLevel_)
542                            .description("Print out selection trees for debugging"));
543 }
544
545
546 void
547 SelectionCollection::setReferencePosType(const char *type)
548 {
549     GMX_RELEASE_ASSERT(type != NULL, "Cannot assign NULL position type");
550     // Check that the type is valid, throw if it is not.
551     e_poscalc_t  dummytype;
552     int          dummyflags;
553     PositionCalculationCollection::typeFromEnum(type, &dummytype, &dummyflags);
554     impl_->rpost_ = type;
555 }
556
557
558 void
559 SelectionCollection::setOutputPosType(const char *type)
560 {
561     GMX_RELEASE_ASSERT(type != NULL, "Cannot assign NULL position type");
562     // Check that the type is valid, throw if it is not.
563     e_poscalc_t  dummytype;
564     int          dummyflags;
565     PositionCalculationCollection::typeFromEnum(type, &dummytype, &dummyflags);
566     impl_->spost_ = type;
567 }
568
569
570 void
571 SelectionCollection::setDebugLevel(int debugLevel)
572 {
573     impl_->debugLevel_ = debugLevel;
574 }
575
576
577 void
578 SelectionCollection::setTopology(t_topology *top, int natoms)
579 {
580     GMX_RELEASE_ASSERT(natoms > 0 || top != NULL,
581                        "The number of atoms must be given if there is no topology");
582     // Get the number of atoms from the topology if it is not given.
583     if (natoms <= 0)
584     {
585         natoms = top->atoms.nr;
586     }
587     if (impl_->bExternalGroupsSet_)
588     {
589         ExceptionInitializer        errors("Invalid index group references encountered");
590         SelectionTreeElementPointer root = impl_->sc_.root;
591         while (root)
592         {
593             checkExternalGroups(root, natoms, &errors);
594             root = root->next;
595         }
596         if (errors.hasNestedExceptions())
597         {
598             GMX_THROW(InconsistentInputError(errors));
599         }
600     }
601     gmx_ana_selcollection_t *sc = &impl_->sc_;
602     // Do this first, as it allocates memory, while the others don't throw.
603     gmx_ana_index_init_simple(&sc->gall, natoms);
604     sc->pcc.setTopology(top);
605     sc->top = top;
606 }
607
608
609 void
610 SelectionCollection::setIndexGroups(gmx_ana_indexgrps_t *grps)
611 {
612     GMX_RELEASE_ASSERT(grps == NULL || !impl_->bExternalGroupsSet_,
613                        "Can only set external groups once or clear them afterwards");
614     impl_->grps_               = grps;
615     impl_->bExternalGroupsSet_ = true;
616
617     ExceptionInitializer        errors("Invalid index group reference(s)");
618     SelectionTreeElementPointer root = impl_->sc_.root;
619     while (root)
620     {
621         impl_->resolveExternalGroups(root, &errors);
622         root->checkUnsortedAtoms(true, &errors);
623         root = root->next;
624     }
625     if (errors.hasNestedExceptions())
626     {
627         GMX_THROW(InconsistentInputError(errors));
628     }
629     for (size_t i = 0; i < impl_->sc_.sel.size(); ++i)
630     {
631         impl_->sc_.sel[i]->refreshName();
632     }
633 }
634
635
636 bool
637 SelectionCollection::requiresTopology() const
638 {
639     e_poscalc_t  type;
640     int          flags;
641
642     if (!impl_->rpost_.empty())
643     {
644         flags = 0;
645         // Should not throw, because has been checked earlier.
646         PositionCalculationCollection::typeFromEnum(impl_->rpost_.c_str(),
647                                                     &type, &flags);
648         if (type != POS_ATOM)
649         {
650             return true;
651         }
652     }
653     if (!impl_->spost_.empty())
654     {
655         flags = 0;
656         // Should not throw, because has been checked earlier.
657         PositionCalculationCollection::typeFromEnum(impl_->spost_.c_str(),
658                                                     &type, &flags);
659         if (type != POS_ATOM)
660         {
661             return true;
662         }
663     }
664
665     SelectionTreeElementPointer sel = impl_->sc_.root;
666     while (sel)
667     {
668         if (_gmx_selelem_requires_top(*sel))
669         {
670             return true;
671         }
672         sel = sel->next;
673     }
674     return false;
675 }
676
677
678 SelectionList
679 SelectionCollection::parseFromStdin(int nr, bool bInteractive,
680                                     const std::string &context)
681 {
682     yyscan_t scanner;
683
684     _gmx_sel_init_lexer(&scanner, &impl_->sc_, bInteractive, nr,
685                         impl_->bExternalGroupsSet_,
686                         impl_->grps_);
687     return runParser(scanner, true, nr, context);
688 }
689
690
691 SelectionList
692 SelectionCollection::parseFromFile(const std::string &filename)
693 {
694
695     try
696     {
697         yyscan_t scanner;
698         File     file(filename, "r");
699         // TODO: Exception-safe way of using the lexer.
700         _gmx_sel_init_lexer(&scanner, &impl_->sc_, false, -1,
701                             impl_->bExternalGroupsSet_,
702                             impl_->grps_);
703         _gmx_sel_set_lex_input_file(scanner, file.handle());
704         return runParser(scanner, false, -1, std::string());
705     }
706     catch (GromacsException &ex)
707     {
708         ex.prependContext(formatString(
709                                   "Error in parsing selections from file '%s'",
710                                   filename.c_str()));
711         throw;
712     }
713 }
714
715
716 SelectionList
717 SelectionCollection::parseFromString(const std::string &str)
718 {
719     yyscan_t scanner;
720
721     _gmx_sel_init_lexer(&scanner, &impl_->sc_, false, -1,
722                         impl_->bExternalGroupsSet_,
723                         impl_->grps_);
724     _gmx_sel_set_lex_input_str(scanner, str.c_str());
725     return runParser(scanner, false, -1, std::string());
726 }
727
728
729 void
730 SelectionCollection::compile()
731 {
732     if (impl_->sc_.top == NULL && requiresTopology())
733     {
734         GMX_THROW(InconsistentInputError("Selection requires topology information, but none provided"));
735     }
736     if (!impl_->bExternalGroupsSet_)
737     {
738         setIndexGroups(NULL);
739     }
740     if (impl_->debugLevel_ >= 1)
741     {
742         printTree(stderr, false);
743     }
744
745     SelectionCompiler compiler;
746     compiler.compile(this);
747
748     if (impl_->debugLevel_ >= 1)
749     {
750         std::fprintf(stderr, "\n");
751         printTree(stderr, false);
752         std::fprintf(stderr, "\n");
753         impl_->sc_.pcc.printTree(stderr);
754         std::fprintf(stderr, "\n");
755     }
756     impl_->sc_.pcc.initEvaluation();
757     if (impl_->debugLevel_ >= 1)
758     {
759         impl_->sc_.pcc.printTree(stderr);
760         std::fprintf(stderr, "\n");
761     }
762
763     // TODO: It would be nicer to associate the name of the selection option
764     // (if available) to the error message.
765     SelectionDataList::const_iterator iter;
766     for (iter = impl_->sc_.sel.begin(); iter != impl_->sc_.sel.end(); ++iter)
767     {
768         const internal::SelectionData &sel = **iter;
769         if (sel.hasFlag(efSelection_OnlyAtoms))
770         {
771             if (!sel.hasOnlyAtoms())
772             {
773                 std::string message = formatString(
774                             "Selection '%s' does not evaluate to individual atoms. "
775                             "This is not allowed in this context.",
776                             sel.selectionText());
777                 GMX_THROW(InvalidInputError(message));
778             }
779         }
780         if (sel.hasFlag(efSelection_DisallowEmpty))
781         {
782             if (sel.posCount() == 0)
783             {
784                 std::string message = formatString(
785                             "Selection '%s' never matches any atoms.",
786                             sel.selectionText());
787                 GMX_THROW(InvalidInputError(message));
788             }
789         }
790     }
791 }
792
793
794 void
795 SelectionCollection::evaluate(t_trxframe *fr, t_pbc *pbc)
796 {
797     if (fr->natoms <= impl_->maxAtomIndex_)
798     {
799         std::string message = formatString(
800                     "Trajectory has less atoms (%d) than what is required for "
801                     "evaluating the provided selections (atoms up to index %d "
802                     "are required).", fr->natoms, impl_->maxAtomIndex_ + 1);
803         GMX_THROW(InconsistentInputError(message));
804     }
805     impl_->sc_.pcc.initFrame();
806
807     SelectionEvaluator evaluator;
808     evaluator.evaluate(this, fr, pbc);
809
810     if (impl_->debugLevel_ >= 3)
811     {
812         std::fprintf(stderr, "\n");
813         printTree(stderr, true);
814     }
815 }
816
817
818 void
819 SelectionCollection::evaluateFinal(int nframes)
820 {
821     SelectionEvaluator evaluator;
822     evaluator.evaluateFinal(this, nframes);
823 }
824
825
826 void
827 SelectionCollection::printTree(FILE *fp, bool bValues) const
828 {
829     SelectionTreeElementPointer sel = impl_->sc_.root;
830     while (sel)
831     {
832         _gmx_selelem_print_tree(fp, *sel, bValues, 0);
833         sel = sel->next;
834     }
835 }
836
837
838 void
839 SelectionCollection::printXvgrInfo(FILE *out, output_env_t oenv) const
840 {
841     if (output_env_get_xvg_format(oenv) != exvgNONE)
842     {
843         const gmx_ana_selcollection_t &sc = impl_->sc_;
844         std::fprintf(out, "# Selections:\n");
845         for (int i = 0; i < sc.nvars; ++i)
846         {
847             std::fprintf(out, "#   %s\n", sc.varstrs[i]);
848         }
849         for (size_t i = 0; i < sc.sel.size(); ++i)
850         {
851             std::fprintf(out, "#   %s\n", sc.sel[i]->selectionText());
852         }
853         std::fprintf(out, "#\n");
854     }
855 }
856
857 } // namespace gmx