2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012, by the GROMACS development team, led by
5 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6 * others, as listed in the AUTHORS file in the top-level source
7 * 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"
46 #include <boost/shared_ptr.hpp>
48 #include "gromacs/legacyheaders/oenv.h"
49 #include "gromacs/legacyheaders/smalloc.h"
50 #include "gromacs/legacyheaders/xvgr.h"
52 #include "gromacs/options/basicoptions.h"
53 #include "gromacs/options/options.h"
54 #include "gromacs/selection/selection.h"
55 #include "gromacs/utility/exceptions.h"
56 #include "gromacs/utility/file.h"
57 #include "gromacs/utility/gmxassert.h"
58 #include "gromacs/utility/messagestringcollector.h"
59 #include "gromacs/utility/stringutil.h"
66 #include "selectioncollection-impl.h"
69 #include "selmethod.h"
75 /********************************************************************
76 * SelectionCollection::Impl
79 SelectionCollection::Impl::Impl()
80 : debugLevel_(0), bExternalGroupsSet_(false), grps_(NULL)
85 gmx_ana_index_clear(&sc_.gall);
87 sc_.symtab.reset(new SelectionParserSymbolTable);
88 gmx_ana_selmethod_register_defaults(sc_.symtab.get());
92 SelectionCollection::Impl::~Impl()
95 // The tree must be freed before the SelectionData objects, since the
96 // tree may hold references to the position data in SelectionData.
99 for (int i = 0; i < sc_.nvars; ++i)
101 sfree(sc_.varstrs[i]);
104 gmx_ana_index_deinit(&sc_.gall);
107 _gmx_sel_mempool_destroy(sc_.mempool);
113 SelectionCollection::Impl::clearSymbolTable()
123 * Reads a single selection line from stdin.
125 * \param[in] infile File to read from (typically File::standardInput()).
126 * \param[in] bInteractive Whether to print interactive prompts.
127 * \param[out] line The read line in stored here.
128 * \returns true if something was read, false if at end of input.
130 * Handles line continuation, reading also the continuing line(s) in one call.
132 bool promptLine(File *infile, bool bInteractive, std::string *line)
136 fprintf(stderr, "> ");
138 if (!infile->readLine(line))
142 while (endsWith(*line, "\\\n"))
144 line->resize(line->length() - 2);
147 fprintf(stderr, "... ");
150 // Return value ignored, buffer remains empty and works correctly
151 // if there is nothing to read.
152 infile->readLine(&buffer);
153 line->append(buffer);
155 if (endsWith(*line, "\n"))
157 line->resize(line->length() - 1);
159 else if (bInteractive)
161 fprintf(stderr, "\n");
167 * Helper function for tokenizing the input and pushing them to the parser.
169 * \param scanner Tokenizer data structure.
170 * \param parserState Parser data structure.
171 * \param[in] bInteractive Whether to operate in interactive mode.
173 * Repeatedly reads tokens using \p scanner and pushes them to the parser with
174 * \p parserState until there is no more input, or until enough input is given
175 * (only in interactive mode).
177 int runParserLoop(yyscan_t scanner, _gmx_sel_yypstate *parserState,
180 int status = YYPUSH_MORE;
185 int token = _gmx_sel_yylex(&value, scanner);
192 // Empty commands cause the interactive parser to print out
193 // status information. This avoids producing those unnecessarily,
194 // e.g., from "resname RA;;".
195 if (prevToken == CMD_SEP && token == CMD_SEP)
201 status = _gmx_sel_yypush_parse(parserState, token, &value, scanner);
203 while (status == YYPUSH_MORE);
204 _gmx_sel_lexer_rethrow_exception_if_occurred(scanner);
209 * Helper function that runs the parser once the tokenizer has been
212 * \param[in,out] scanner Scanner data structure.
213 * \param[in] bStdIn Whether to use a line-based reading
214 * algorithm designed for interactive input.
215 * \param[in] maxnr Maximum number of selections to parse
216 * (if -1, parse as many as provided by the user).
217 * \returns Vector of parsed selections.
218 * \throws std::bad_alloc if out of memory.
219 * \throws InvalidInputError if there is a parsing error.
221 * Used internally to implement parseFromStdin(), parseFromFile() and
224 SelectionList runParser(yyscan_t scanner, bool bStdIn, int maxnr)
226 boost::shared_ptr<void> scannerGuard(scanner, &_gmx_sel_free_lexer);
227 gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
229 MessageStringCollector errors;
230 _gmx_sel_set_lexer_error_reporter(scanner, &errors);
232 int oldCount = sc->sel.size();
235 boost::shared_ptr<_gmx_sel_yypstate> parserState(
236 _gmx_sel_yypstate_new(), &_gmx_sel_yypstate_delete);
239 File &stdinFile(File::standardInput());
240 bool bInteractive = _gmx_sel_is_lexer_interactive(scanner);
243 while (promptLine(&stdinFile, bInteractive, &line))
246 _gmx_sel_set_lex_input_str(scanner, line.c_str());
247 status = runParserLoop(scanner, parserState.get(), true);
248 if (status != YYPUSH_MORE)
250 // TODO: Check if there is more input, and issue an
251 // error/warning if some input was ignored.
252 goto early_termination;
254 if (!errors.isEmpty() && bInteractive)
256 fprintf(stderr, "%s", errors.toString().c_str());
260 status = _gmx_sel_yypush_parse(parserState.get(), 0, NULL,
262 _gmx_sel_lexer_rethrow_exception_if_occurred(scanner);
268 int status = runParserLoop(scanner, parserState.get(), false);
272 scannerGuard.reset();
273 int nr = sc->sel.size() - oldCount;
274 if (maxnr > 0 && nr != maxnr)
277 errors.append("Too few selections provided");
280 // TODO: Remove added selections from the collection if parsing failed?
281 if (!bOk || !errors.isEmpty())
283 GMX_ASSERT(!bOk && !errors.isEmpty(), "Inconsistent error reporting");
284 GMX_THROW(InvalidInputError(errors.toString()));
287 SelectionList result;
288 SelectionDataList::const_iterator i;
290 for (i = sc->sel.begin() + oldCount; i != sc->sel.end(); ++i)
292 result.push_back(Selection(i->get()));
300 void SelectionCollection::Impl::resolveExternalGroups(
301 const SelectionTreeElementPointer &root,
302 MessageStringCollector *errors)
305 if (root->type == SEL_GROUPREF)
310 // TODO: Improve error messages
311 errors->append("Unknown group referenced in a selection");
314 else if (root->u.gref.name != NULL)
316 char *name = root->u.gref.name;
317 if (!gmx_ana_indexgrps_find(&root->u.cgrp, grps_, name))
319 // TODO: Improve error messages
320 errors->append("Unknown group referenced in a selection");
330 if (!gmx_ana_indexgrps_extract(&root->u.cgrp, grps_,
333 // TODO: Improve error messages
334 errors->append("Unknown group referenced in a selection");
340 root->type = SEL_CONST;
341 root->setName(root->u.cgrp.name);
345 SelectionTreeElementPointer child = root->child;
348 resolveExternalGroups(child, errors);
354 /********************************************************************
355 * SelectionCollection
358 SelectionCollection::SelectionCollection()
364 SelectionCollection::~SelectionCollection()
370 SelectionCollection::initOptions(Options *options)
372 static const char * const debug_levels[]
373 = {"no", "basic", "compile", "eval", "full", NULL};
375 static const char * const desc[] = {
376 "This program supports selections in addition to traditional",
377 "index files. Use [TT]-select help[tt] for additional information,",
378 "or type 'help' in the selection prompt.",
381 options.setDescription(desc);
384 const char *const *postypes = PositionCalculationCollection::typeEnumValues;
385 options->addOption(StringOption("selrpos").enumValue(postypes)
386 .store(&impl_->rpost_).defaultValue(postypes[0])
387 .description("Selection reference positions"));
388 options->addOption(StringOption("seltype").enumValue(postypes)
389 .store(&impl_->spost_).defaultValue(postypes[0])
390 .description("Default selection output positions"));
391 GMX_RELEASE_ASSERT(impl_->debugLevel_ >= 0 && impl_->debugLevel_ <= 4,
392 "Debug level out of range");
393 options->addOption(StringOption("seldebug").hidden(impl_->debugLevel_ == 0)
394 .enumValue(debug_levels)
395 .defaultValue(debug_levels[impl_->debugLevel_])
396 .storeEnumIndex(&impl_->debugLevel_)
397 .description("Print out selection trees for debugging"));
402 SelectionCollection::setReferencePosType(const char *type)
404 GMX_RELEASE_ASSERT(type != NULL, "Cannot assign NULL position type");
405 // Check that the type is valid, throw if it is not.
406 e_poscalc_t dummytype;
408 PositionCalculationCollection::typeFromEnum(type, &dummytype, &dummyflags);
409 impl_->rpost_ = type;
414 SelectionCollection::setOutputPosType(const char *type)
416 GMX_RELEASE_ASSERT(type != NULL, "Cannot assign NULL position type");
417 // Check that the type is valid, throw if it is not.
418 e_poscalc_t dummytype;
420 PositionCalculationCollection::typeFromEnum(type, &dummytype, &dummyflags);
421 impl_->spost_ = type;
426 SelectionCollection::setDebugLevel(int debugLevel)
428 impl_->debugLevel_ = debugLevel;
433 SelectionCollection::setTopology(t_topology *top, int natoms)
435 GMX_RELEASE_ASSERT(natoms > 0 || top != NULL,
436 "The number of atoms must be given if there is no topology");
437 // Get the number of atoms from the topology if it is not given.
440 natoms = top->atoms.nr;
442 gmx_ana_selcollection_t *sc = &impl_->sc_;
443 // Do this first, as it allocates memory, while the others don't throw.
444 gmx_ana_index_init_simple(&sc->gall, natoms, NULL);
445 sc->pcc.setTopology(top);
451 SelectionCollection::setIndexGroups(gmx_ana_indexgrps_t *grps)
453 GMX_RELEASE_ASSERT(grps == NULL || !impl_->bExternalGroupsSet_,
454 "Can only set external groups once or clear them afterwards");
456 impl_->bExternalGroupsSet_ = true;
458 MessageStringCollector errors;
459 SelectionTreeElementPointer root = impl_->sc_.root;
462 impl_->resolveExternalGroups(root, &errors);
465 if (!errors.isEmpty())
467 GMX_THROW(InvalidInputError(errors.toString()));
473 SelectionCollection::requiresTopology() const
478 if (!impl_->rpost_.empty())
481 // Should not throw, because has been checked earlier.
482 PositionCalculationCollection::typeFromEnum(impl_->rpost_.c_str(),
484 if (type != POS_ATOM)
489 if (!impl_->spost_.empty())
492 // Should not throw, because has been checked earlier.
493 PositionCalculationCollection::typeFromEnum(impl_->spost_.c_str(),
495 if (type != POS_ATOM)
501 SelectionTreeElementPointer sel = impl_->sc_.root;
504 if (_gmx_selelem_requires_top(*sel))
515 SelectionCollection::parseFromStdin(int nr, bool bInteractive)
519 _gmx_sel_init_lexer(&scanner, &impl_->sc_, bInteractive, nr,
520 impl_->bExternalGroupsSet_,
522 return runParser(scanner, true, nr);
527 SelectionCollection::parseFromFile(const std::string &filename)
533 File file(filename, "r");
534 // TODO: Exception-safe way of using the lexer.
535 _gmx_sel_init_lexer(&scanner, &impl_->sc_, false, -1,
536 impl_->bExternalGroupsSet_,
538 _gmx_sel_set_lex_input_file(scanner, file.handle());
539 return runParser(scanner, false, -1);
541 catch (GromacsException &ex)
543 ex.prependContext(formatString(
544 "Error in parsing selections from file '%s'",
552 SelectionCollection::parseFromString(const std::string &str)
556 _gmx_sel_init_lexer(&scanner, &impl_->sc_, false, -1,
557 impl_->bExternalGroupsSet_,
559 _gmx_sel_set_lex_input_str(scanner, str.c_str());
560 return runParser(scanner, false, -1);
565 SelectionCollection::compile()
567 if (impl_->sc_.top == NULL && requiresTopology())
569 GMX_THROW(InconsistentInputError("Selection requires topology information, but none provided"));
571 if (!impl_->bExternalGroupsSet_)
573 setIndexGroups(NULL);
575 if (impl_->debugLevel_ >= 1)
577 printTree(stderr, false);
580 SelectionCompiler compiler;
581 compiler.compile(this);
583 if (impl_->debugLevel_ >= 1)
585 std::fprintf(stderr, "\n");
586 printTree(stderr, false);
587 std::fprintf(stderr, "\n");
588 impl_->sc_.pcc.printTree(stderr);
589 std::fprintf(stderr, "\n");
591 impl_->sc_.pcc.initEvaluation();
592 if (impl_->debugLevel_ >= 1)
594 impl_->sc_.pcc.printTree(stderr);
595 std::fprintf(stderr, "\n");
601 SelectionCollection::evaluate(t_trxframe *fr, t_pbc *pbc)
603 impl_->sc_.pcc.initFrame();
605 SelectionEvaluator evaluator;
606 evaluator.evaluate(this, fr, pbc);
608 if (impl_->debugLevel_ >= 3)
610 std::fprintf(stderr, "\n");
611 printTree(stderr, true);
617 SelectionCollection::evaluateFinal(int nframes)
619 SelectionEvaluator evaluator;
620 evaluator.evaluateFinal(this, nframes);
625 SelectionCollection::printTree(FILE *fp, bool bValues) const
627 SelectionTreeElementPointer sel = impl_->sc_.root;
630 _gmx_selelem_print_tree(fp, *sel, bValues, 0);
637 SelectionCollection::printXvgrInfo(FILE *out, output_env_t oenv) const
639 if (output_env_get_xvg_format(oenv) != exvgNONE)
641 const gmx_ana_selcollection_t &sc = impl_->sc_;
642 std::fprintf(out, "# Selections:\n");
643 for (int i = 0; i < sc.nvars; ++i)
645 std::fprintf(out, "# %s\n", sc.varstrs[i]);
647 for (size_t i = 0; i < sc.sel.size(); ++i)
649 std::fprintf(out, "# %s\n", sc.sel[i]->selectionText());
651 std::fprintf(out, "#\n");
657 SelectionCollection::createDefaultHelpTopic()
659 return createSelectionHelpTopic();