2 * This file is part of the GROMACS molecular simulation package.
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.
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.
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.
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.
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.
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.
37 * Implements gmx::SelectionCollection.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_selection
42 #include "selectioncollection.h"
50 #include <boost/shared_ptr.hpp>
52 #include "gromacs/legacyheaders/oenv.h"
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/topology/topology.h"
60 #include "gromacs/utility/exceptions.h"
61 #include "gromacs/utility/file.h"
62 #include "gromacs/utility/gmxassert.h"
63 #include "gromacs/utility/messagestringcollector.h"
64 #include "gromacs/utility/smalloc.h"
65 #include "gromacs/utility/stringutil.h"
72 #include "selection.h"
73 #include "selectioncollection-impl.h"
76 #include "selmethod.h"
82 /********************************************************************
83 * SelectionCollection::Impl
86 SelectionCollection::Impl::Impl()
87 : debugLevel_(0), bExternalGroupsSet_(false), grps_(NULL)
92 gmx_ana_index_clear(&sc_.gall);
94 sc_.symtab.reset(new SelectionParserSymbolTable);
95 gmx_ana_selmethod_register_defaults(sc_.symtab.get());
99 SelectionCollection::Impl::~Impl()
102 // The tree must be freed before the SelectionData objects, since the
103 // tree may hold references to the position data in SelectionData.
106 for (int i = 0; i < sc_.nvars; ++i)
108 sfree(sc_.varstrs[i]);
111 gmx_ana_index_deinit(&sc_.gall);
114 _gmx_sel_mempool_destroy(sc_.mempool);
120 SelectionCollection::Impl::clearSymbolTable()
130 * Reads a single selection line from stdin.
132 * \param[in] infile File to read from (typically File::standardInput()).
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.
137 * Handles line continuation, reading also the continuing line(s) in one call.
139 bool promptLine(File *infile, bool bInteractive, std::string *line)
143 fprintf(stderr, "> ");
145 if (!infile->readLineWithTrailingSpace(line))
149 while (endsWith(*line, "\\\n"))
151 line->resize(line->length() - 2);
154 fprintf(stderr, "... ");
157 // Return value ignored, buffer remains empty and works correctly
158 // if there is nothing to read.
159 infile->readLineWithTrailingSpace(&buffer);
160 line->append(buffer);
162 if (endsWith(*line, "\n"))
164 line->resize(line->length() - 1);
166 else if (bInteractive)
168 fprintf(stderr, "\n");
174 * Helper function for tokenizing the input and pushing them to the parser.
176 * \param scanner Tokenizer data structure.
177 * \param parserState Parser data structure.
178 * \param[in] bInteractive Whether to operate in interactive mode.
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).
184 int runParserLoop(yyscan_t scanner, _gmx_sel_yypstate *parserState,
187 int status = YYPUSH_MORE;
192 int token = _gmx_sel_yylex(&value, scanner);
199 // Empty commands cause the interactive parser to print out
200 // status information. This avoids producing those unnecessarily,
201 // e.g., from "resname RA;;".
202 if (prevToken == CMD_SEP && token == CMD_SEP)
208 status = _gmx_sel_yypush_parse(parserState, token, &value, scanner);
210 while (status == YYPUSH_MORE);
211 _gmx_sel_lexer_rethrow_exception_if_occurred(scanner);
216 * Print current status in response to empty line in interactive input.
218 * \param[in] sc Selection collection data structure.
219 * \param[in] grps Available index groups.
220 * \param[in] firstSelection Index of first selection from this interactive
222 * \param[in] maxCount Maximum number of selections.
223 * \param[in] context Context to print for what the selections are for.
224 * \param[in] bFirst Whether this is the header that is printed before
227 * Prints the available index groups and currently provided selections.
229 void printCurrentStatus(gmx_ana_selcollection_t *sc, gmx_ana_indexgrps_t *grps,
230 size_t firstSelection, int maxCount,
231 const std::string &context, bool bFirst)
235 std::fprintf(stderr, "Available static index groups:\n");
236 gmx_ana_indexgrps_print(stderr, grps, 0);
238 std::fprintf(stderr, "Specify ");
241 std::fprintf(stderr, "any number of selections");
243 else if (maxCount == 1)
245 std::fprintf(stderr, "a selection");
249 std::fprintf(stderr, "%d selections", maxCount);
251 std::fprintf(stderr, "%s%s:\n",
252 context.empty() ? "" : " ", context.c_str());
254 "(one per line, <enter> for status/groups, 'help' for help%s)\n",
255 maxCount < 0 ? ", Ctrl-D to end" : "");
256 if (!bFirst && (sc->nvars > 0 || sc->sel.size() > firstSelection))
258 std::fprintf(stderr, "Currently provided selections:\n");
259 for (int i = 0; i < sc->nvars; ++i)
261 std::fprintf(stderr, " %s\n", sc->varstrs[i]);
263 for (size_t i = firstSelection; i < sc->sel.size(); ++i)
265 std::fprintf(stderr, " %2d. %s\n",
266 static_cast<int>(i - firstSelection + 1),
267 sc->sel[i]->selectionText());
272 = maxCount - static_cast<int>(sc->sel.size() - firstSelection);
273 std::fprintf(stderr, "(%d more selection%s required)\n",
274 remaining, remaining > 1 ? "s" : "");
280 * Prints selection help in interactive selection input.
282 * \param[in] sc Selection collection data structure.
283 * \param[in] line Line of user input requesting help (starting with `help`).
285 * Initializes the selection help if not yet initialized, and finds the help
286 * topic based on words on the input line.
288 void printHelp(gmx_ana_selcollection_t *sc, const std::string &line)
290 if (sc->rootHelp.get() == NULL)
292 sc->rootHelp = createSelectionHelpTopic();
294 HelpWriterContext context(&File::standardError(),
295 eHelpOutputFormat_Console);
296 HelpManager manager(*sc->rootHelp, context);
299 std::vector<std::string> topic = splitString(line);
300 std::vector<std::string>::const_iterator value;
301 // First item in the list is the 'help' token.
302 for (value = topic.begin() + 1; value != topic.end(); ++value)
304 manager.enterTopic(*value);
307 catch (const InvalidInputError &ex)
309 fprintf(stderr, "%s\n", ex.what());
312 manager.writeCurrentTopic();
316 * Helper function that runs the parser once the tokenizer has been
319 * \param[in,out] scanner Scanner data structure.
320 * \param[in] bStdIn Whether to use a line-based reading
321 * algorithm designed for interactive input.
322 * \param[in] maxnr Maximum number of selections to parse
323 * (if -1, parse as many as provided by the user).
324 * \param[in] context Context to print for what the selections are for.
325 * \returns Vector of parsed selections.
326 * \throws std::bad_alloc if out of memory.
327 * \throws InvalidInputError if there is a parsing error.
329 * Used internally to implement parseFromStdin(), parseFromFile() and
332 SelectionList runParser(yyscan_t scanner, bool bStdIn, int maxnr,
333 const std::string &context)
335 boost::shared_ptr<void> scannerGuard(scanner, &_gmx_sel_free_lexer);
336 gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
337 gmx_ana_indexgrps_t *grps = _gmx_sel_lexer_indexgrps(scanner);
339 MessageStringCollector errors;
340 _gmx_sel_set_lexer_error_reporter(scanner, &errors);
342 size_t oldCount = sc->sel.size();
345 boost::shared_ptr<_gmx_sel_yypstate> parserState(
346 _gmx_sel_yypstate_new(), &_gmx_sel_yypstate_delete);
349 File &stdinFile(File::standardInput());
350 const bool bInteractive = _gmx_sel_is_lexer_interactive(scanner);
353 printCurrentStatus(sc, grps, oldCount, maxnr, context, true);
357 while (promptLine(&stdinFile, bInteractive, &line))
361 line = stripString(line);
364 printCurrentStatus(sc, grps, oldCount, maxnr, context, false);
367 if (startsWith(line, "help")
368 && (line[4] == 0 || std::isspace(line[4])))
375 _gmx_sel_set_lex_input_str(scanner, line.c_str());
376 status = runParserLoop(scanner, parserState.get(), true);
377 if (status != YYPUSH_MORE)
379 // TODO: Check if there is more input, and issue an
380 // error/warning if some input was ignored.
381 goto early_termination;
383 if (!errors.isEmpty() && bInteractive)
385 fprintf(stderr, "%s", errors.toString().c_str());
389 status = _gmx_sel_yypush_parse(parserState.get(), 0, NULL,
391 _gmx_sel_lexer_rethrow_exception_if_occurred(scanner);
397 int status = runParserLoop(scanner, parserState.get(), false);
401 scannerGuard.reset();
402 int nr = sc->sel.size() - oldCount;
403 if (maxnr > 0 && nr != maxnr)
406 errors.append("Too few selections provided");
409 // TODO: Remove added selections from the collection if parsing failed?
410 if (!bOk || !errors.isEmpty())
412 GMX_ASSERT(!bOk && !errors.isEmpty(), "Inconsistent error reporting");
413 GMX_THROW(InvalidInputError(errors.toString()));
416 SelectionList result;
417 SelectionDataList::const_iterator i;
419 for (i = sc->sel.begin() + oldCount; i != sc->sel.end(); ++i)
421 result.push_back(Selection(i->get()));
427 * Checks that index groups have valid atom indices.
429 * \param[in] root Root of selection tree to process.
430 * \param[in] natoms Maximum number of atoms that the selections are set
432 * \param errors Object for reporting any error messages.
433 * \throws std::bad_alloc if out of memory.
435 * Recursively checks the selection tree for index groups.
436 * Each found group is checked that it only contains atom indices that match
437 * the topology/maximum number of atoms set for the selection collection.
438 * Any issues are reported to \p errors.
440 void checkExternalGroups(const SelectionTreeElementPointer &root,
442 ExceptionInitializer *errors)
444 if (root->type == SEL_CONST && root->v.type == GROUP_VALUE)
448 root->checkIndexGroup(natoms);
450 catch (const UserInputError &)
452 errors->addCurrentExceptionAsNested();
456 SelectionTreeElementPointer child = root->child;
459 checkExternalGroups(child, natoms, errors);
467 void SelectionCollection::Impl::resolveExternalGroups(
468 const SelectionTreeElementPointer &root,
469 ExceptionInitializer *errors)
472 if (root->type == SEL_GROUPREF)
476 root->resolveIndexGroupReference(grps_, sc_.gall.isize);
478 catch (const UserInputError &)
480 errors->addCurrentExceptionAsNested();
484 SelectionTreeElementPointer child = root->child;
487 resolveExternalGroups(child, errors);
488 root->flags |= (child->flags & SEL_UNSORTED);
494 /********************************************************************
495 * SelectionCollection
498 SelectionCollection::SelectionCollection()
504 SelectionCollection::~SelectionCollection()
510 SelectionCollection::initOptions(Options *options)
512 const char * const debug_levels[]
513 = { "no", "basic", "compile", "eval", "full" };
515 bool bAllowNonAtomOutput = false;
516 SelectionDataList::const_iterator iter;
517 for (iter = impl_->sc_.sel.begin(); iter != impl_->sc_.sel.end(); ++iter)
519 const internal::SelectionData &sel = **iter;
520 if (!sel.hasFlag(efSelection_OnlyAtoms))
522 bAllowNonAtomOutput = true;
526 const char *const *postypes = PositionCalculationCollection::typeEnumValues;
527 options->addOption(StringOption("selrpos")
528 .enumValueFromNullTerminatedArray(postypes)
529 .store(&impl_->rpost_).defaultValue(postypes[0])
530 .description("Selection reference positions"));
531 if (bAllowNonAtomOutput)
533 options->addOption(StringOption("seltype")
534 .enumValueFromNullTerminatedArray(postypes)
535 .store(&impl_->spost_).defaultValue(postypes[0])
536 .description("Default selection output positions"));
540 impl_->spost_ = postypes[0];
542 GMX_RELEASE_ASSERT(impl_->debugLevel_ >= 0 && impl_->debugLevel_ <= 4,
543 "Debug level out of range");
544 options->addOption(StringOption("seldebug").hidden(impl_->debugLevel_ == 0)
545 .enumValue(debug_levels)
546 .defaultValue(debug_levels[impl_->debugLevel_])
547 .storeEnumIndex(&impl_->debugLevel_)
548 .description("Print out selection trees for debugging"));
553 SelectionCollection::setReferencePosType(const char *type)
555 GMX_RELEASE_ASSERT(type != NULL, "Cannot assign NULL position type");
556 // Check that the type is valid, throw if it is not.
557 e_poscalc_t dummytype;
559 PositionCalculationCollection::typeFromEnum(type, &dummytype, &dummyflags);
560 impl_->rpost_ = type;
565 SelectionCollection::setOutputPosType(const char *type)
567 GMX_RELEASE_ASSERT(type != NULL, "Cannot assign NULL position type");
568 // Check that the type is valid, throw if it is not.
569 e_poscalc_t dummytype;
571 PositionCalculationCollection::typeFromEnum(type, &dummytype, &dummyflags);
572 impl_->spost_ = type;
577 SelectionCollection::setDebugLevel(int debugLevel)
579 impl_->debugLevel_ = debugLevel;
584 SelectionCollection::setTopology(t_topology *top, int natoms)
586 GMX_RELEASE_ASSERT(natoms > 0 || top != NULL,
587 "The number of atoms must be given if there is no topology");
588 // Get the number of atoms from the topology if it is not given.
591 natoms = top->atoms.nr;
593 if (impl_->bExternalGroupsSet_)
595 ExceptionInitializer errors("Invalid index group references encountered");
596 SelectionTreeElementPointer root = impl_->sc_.root;
599 checkExternalGroups(root, natoms, &errors);
602 if (errors.hasNestedExceptions())
604 GMX_THROW(InconsistentInputError(errors));
607 gmx_ana_selcollection_t *sc = &impl_->sc_;
608 // Do this first, as it allocates memory, while the others don't throw.
609 gmx_ana_index_init_simple(&sc->gall, natoms);
610 sc->pcc.setTopology(top);
616 SelectionCollection::setIndexGroups(gmx_ana_indexgrps_t *grps)
618 GMX_RELEASE_ASSERT(grps == NULL || !impl_->bExternalGroupsSet_,
619 "Can only set external groups once or clear them afterwards");
621 impl_->bExternalGroupsSet_ = true;
623 ExceptionInitializer errors("Invalid index group reference(s)");
624 SelectionTreeElementPointer root = impl_->sc_.root;
627 impl_->resolveExternalGroups(root, &errors);
628 root->checkUnsortedAtoms(true, &errors);
631 if (errors.hasNestedExceptions())
633 GMX_THROW(InconsistentInputError(errors));
635 for (size_t i = 0; i < impl_->sc_.sel.size(); ++i)
637 impl_->sc_.sel[i]->refreshName();
643 SelectionCollection::requiresTopology() const
648 if (!impl_->rpost_.empty())
651 // Should not throw, because has been checked earlier.
652 PositionCalculationCollection::typeFromEnum(impl_->rpost_.c_str(),
654 if (type != POS_ATOM)
659 if (!impl_->spost_.empty())
662 // Should not throw, because has been checked earlier.
663 PositionCalculationCollection::typeFromEnum(impl_->spost_.c_str(),
665 if (type != POS_ATOM)
671 SelectionTreeElementPointer sel = impl_->sc_.root;
674 if (_gmx_selelem_requires_top(*sel))
685 SelectionCollection::parseFromStdin(int nr, bool bInteractive,
686 const std::string &context)
690 _gmx_sel_init_lexer(&scanner, &impl_->sc_, bInteractive, nr,
691 impl_->bExternalGroupsSet_,
693 return runParser(scanner, true, nr, context);
698 SelectionCollection::parseFromFile(const std::string &filename)
704 File file(filename, "r");
705 // TODO: Exception-safe way of using the lexer.
706 _gmx_sel_init_lexer(&scanner, &impl_->sc_, false, -1,
707 impl_->bExternalGroupsSet_,
709 _gmx_sel_set_lex_input_file(scanner, file.handle());
710 return runParser(scanner, false, -1, std::string());
712 catch (GromacsException &ex)
714 ex.prependContext(formatString(
715 "Error in parsing selections from file '%s'",
723 SelectionCollection::parseFromString(const std::string &str)
727 _gmx_sel_init_lexer(&scanner, &impl_->sc_, false, -1,
728 impl_->bExternalGroupsSet_,
730 _gmx_sel_set_lex_input_str(scanner, str.c_str());
731 return runParser(scanner, false, -1, std::string());
736 SelectionCollection::compile()
738 if (impl_->sc_.top == NULL && requiresTopology())
740 GMX_THROW(InconsistentInputError("Selection requires topology information, but none provided"));
742 if (!impl_->bExternalGroupsSet_)
744 setIndexGroups(NULL);
746 if (impl_->debugLevel_ >= 1)
748 printTree(stderr, false);
751 SelectionCompiler compiler;
752 compiler.compile(this);
754 if (impl_->debugLevel_ >= 1)
756 std::fprintf(stderr, "\n");
757 printTree(stderr, false);
758 std::fprintf(stderr, "\n");
759 impl_->sc_.pcc.printTree(stderr);
760 std::fprintf(stderr, "\n");
762 impl_->sc_.pcc.initEvaluation();
763 if (impl_->debugLevel_ >= 1)
765 impl_->sc_.pcc.printTree(stderr);
766 std::fprintf(stderr, "\n");
769 // TODO: It would be nicer to associate the name of the selection option
770 // (if available) to the error message.
771 SelectionDataList::const_iterator iter;
772 for (iter = impl_->sc_.sel.begin(); iter != impl_->sc_.sel.end(); ++iter)
774 const internal::SelectionData &sel = **iter;
775 if (sel.hasFlag(efSelection_OnlyAtoms))
777 if (sel.type() != INDEX_ATOM)
779 std::string message = formatString(
780 "Selection '%s' does not evaluate to individual atoms. "
781 "This is not allowed in this context.",
782 sel.selectionText());
783 GMX_THROW(InvalidInputError(message));
786 if (sel.hasFlag(efSelection_DisallowEmpty))
788 if (sel.posCount() == 0)
790 std::string message = formatString(
791 "Selection '%s' never matches any atoms.",
792 sel.selectionText());
793 GMX_THROW(InvalidInputError(message));
801 SelectionCollection::evaluate(t_trxframe *fr, t_pbc *pbc)
803 impl_->sc_.pcc.initFrame();
805 SelectionEvaluator evaluator;
806 evaluator.evaluate(this, fr, pbc);
808 if (impl_->debugLevel_ >= 3)
810 std::fprintf(stderr, "\n");
811 printTree(stderr, true);
817 SelectionCollection::evaluateFinal(int nframes)
819 SelectionEvaluator evaluator;
820 evaluator.evaluateFinal(this, nframes);
825 SelectionCollection::printTree(FILE *fp, bool bValues) const
827 SelectionTreeElementPointer sel = impl_->sc_.root;
830 _gmx_selelem_print_tree(fp, *sel, bValues, 0);
837 SelectionCollection::printXvgrInfo(FILE *out, output_env_t oenv) const
839 if (output_env_get_xvg_format(oenv) != exvgNONE)
841 const gmx_ana_selcollection_t &sc = impl_->sc_;
842 std::fprintf(out, "# Selections:\n");
843 for (int i = 0; i < sc.nvars; ++i)
845 std::fprintf(out, "# %s\n", sc.varstrs[i]);
847 for (size_t i = 0; i < sc.sel.size(); ++i)
849 std::fprintf(out, "# %s\n", sc.sel[i]->selectionText());
851 std::fprintf(out, "#\n");