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