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