3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
9 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11 * Copyright (c) 2001-2009, The GROMACS development team,
12 * check out http://www.gromacs.org for more information.
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * If you want to redistribute modifications, please consider that
20 * scientific software is very special. Version control is crucial -
21 * bugs must be traceable. We will be happy to consider code for
22 * inclusion in the official distribution, but derived work must not
23 * be called official GROMACS. Details are found in the README & COPYING
24 * files - if they are missing, get the official version at www.gromacs.org.
26 * To help us fund GROMACS development, we humbly ask that you cite
27 * the papers on the package - you can find them in the top README file.
29 * For more info, check our website at http://www.gromacs.org
33 * Implements gmx::SelectionCollection.
35 * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36 * \ingroup module_selection
44 #include <boost/shared_ptr.hpp>
46 #include "gromacs/legacyheaders/oenv.h"
47 #include "gromacs/legacyheaders/smalloc.h"
48 #include "gromacs/legacyheaders/xvgr.h"
50 #include "gromacs/options/basicoptions.h"
51 #include "gromacs/options/options.h"
52 #include "gromacs/selection/selection.h"
53 #include "gromacs/selection/selectioncollection.h"
54 #include "gromacs/utility/exceptions.h"
55 #include "gromacs/utility/file.h"
56 #include "gromacs/utility/gmxassert.h"
57 #include "gromacs/utility/messagestringcollector.h"
58 #include "gromacs/utility/stringutil.h"
65 #include "selectioncollection-impl.h"
68 #include "selmethod.h"
74 /********************************************************************
75 * SelectionCollection::Impl
78 SelectionCollection::Impl::Impl()
79 : debugLevel_(0), bExternalGroupsSet_(false), grps_(NULL)
84 gmx_ana_index_clear(&sc_.gall);
86 sc_.symtab.reset(new SelectionParserSymbolTable);
87 gmx_ana_selmethod_register_defaults(sc_.symtab.get());
91 SelectionCollection::Impl::~Impl()
94 // The tree must be freed before the SelectionData objects, since the
95 // tree may hold references to the position data in SelectionData.
98 for (int i = 0; i < sc_.nvars; ++i)
100 sfree(sc_.varstrs[i]);
103 gmx_ana_index_deinit(&sc_.gall);
106 _gmx_sel_mempool_destroy(sc_.mempool);
112 SelectionCollection::Impl::clearSymbolTable()
122 * Reads a single selection line from stdin.
124 * \param[in] infile File to read from (typically File::standardInput()).
125 * \param[in] bInteractive Whether to print interactive prompts.
126 * \param[out] line The read line in stored here.
127 * \returns true if something was read, false if at end of input.
129 * Handles line continuation, reading also the continuing line(s) in one call.
131 bool promptLine(File *infile, bool bInteractive, std::string *line)
135 fprintf(stderr, "> ");
137 if (!infile->readLine(line))
141 while(endsWith(*line, "\\\n"))
143 line->resize(line->length() - 2);
146 fprintf(stderr, "... ");
149 // Return value ignored, buffer remains empty and works correctly
150 // if there is nothing to read.
151 infile->readLine(&buffer);
152 line->append(buffer);
154 if (endsWith(*line, "\n"))
156 line->resize(line->length() - 1);
158 else if (bInteractive)
160 fprintf(stderr, "\n");
166 * Helper function for tokenizing the input and pushing them to the parser.
168 * \param scanner Tokenizer data structure.
169 * \param parserState Parser data structure.
170 * \param[in] bInteractive Whether to operate in interactive mode.
172 * Repeatedly reads tokens using \p scanner and pushes them to the parser with
173 * \p parserState until there is no more input, or until enough input is given
174 * (only in interactive mode).
176 int runParserLoop(yyscan_t scanner, _gmx_sel_yypstate *parserState,
179 int status = YYPUSH_MORE;
184 int token = _gmx_sel_yylex(&value, scanner);
191 // Empty commands cause the interactive parser to print out
192 // status information. This avoids producing those unnecessarily,
193 // e.g., from "resname RA;;".
194 if (prevToken == CMD_SEP && token == CMD_SEP)
200 status = _gmx_sel_yypush_parse(parserState, token, &value, scanner);
202 while (status == YYPUSH_MORE);
203 _gmx_sel_lexer_rethrow_exception_if_occurred(scanner);
208 * Helper function that runs the parser once the tokenizer has been
211 * \param[in,out] scanner Scanner data structure.
212 * \param[in] bStdIn Whether to use a line-based reading
213 * algorithm designed for interactive input.
214 * \param[in] maxnr Maximum number of selections to parse
215 * (if -1, parse as many as provided by the user).
216 * \returns Vector of parsed selections.
217 * \throws std::bad_alloc if out of memory.
218 * \throws InvalidInputError if there is a parsing error.
220 * Used internally to implement parseFromStdin(), parseFromFile() and
223 SelectionList runParser(yyscan_t scanner, bool bStdIn, int maxnr)
225 boost::shared_ptr<void> scannerGuard(scanner, &_gmx_sel_free_lexer);
226 gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
228 MessageStringCollector errors;
229 _gmx_sel_set_lexer_error_reporter(scanner, &errors);
231 int oldCount = sc->sel.size();
234 boost::shared_ptr<_gmx_sel_yypstate> parserState(
235 _gmx_sel_yypstate_new(), &_gmx_sel_yypstate_delete);
238 File &stdinFile(File::standardInput());
239 bool bInteractive = _gmx_sel_is_lexer_interactive(scanner);
242 while (promptLine(&stdinFile, bInteractive, &line))
245 _gmx_sel_set_lex_input_str(scanner, line.c_str());
246 status = runParserLoop(scanner, parserState.get(), true);
247 if (status != YYPUSH_MORE)
249 // TODO: Check if there is more input, and issue an
250 // error/warning if some input was ignored.
251 goto early_termination;
253 if (!errors.isEmpty() && bInteractive)
255 fprintf(stderr, "%s", errors.toString().c_str());
259 status = _gmx_sel_yypush_parse(parserState.get(), 0, NULL,
261 _gmx_sel_lexer_rethrow_exception_if_occurred(scanner);
267 int status = runParserLoop(scanner, parserState.get(), false);
271 scannerGuard.reset();
272 int nr = sc->sel.size() - oldCount;
273 if (maxnr > 0 && nr != maxnr)
276 errors.append("Too few selections provided");
279 // TODO: Remove added selections from the collection if parsing failed?
280 if (!bOk || !errors.isEmpty())
282 GMX_ASSERT(!bOk && !errors.isEmpty(), "Inconsistent error reporting");
283 GMX_THROW(InvalidInputError(errors.toString()));
286 SelectionList result;
287 SelectionDataList::const_iterator i;
289 for (i = sc->sel.begin() + oldCount; i != sc->sel.end(); ++i)
291 result.push_back(Selection(i->get()));
299 void SelectionCollection::Impl::resolveExternalGroups(
300 const SelectionTreeElementPointer &root,
301 MessageStringCollector *errors)
304 if (root->type == SEL_GROUPREF)
309 // TODO: Improve error messages
310 errors->append("Unknown group referenced in a selection");
313 else if (root->u.gref.name != NULL)
315 char *name = root->u.gref.name;
316 if (!gmx_ana_indexgrps_find(&root->u.cgrp, grps_, name))
318 // TODO: Improve error messages
319 errors->append("Unknown group referenced in a selection");
329 if (!gmx_ana_indexgrps_extract(&root->u.cgrp, grps_,
332 // TODO: Improve error messages
333 errors->append("Unknown group referenced in a selection");
339 root->type = SEL_CONST;
340 root->setName(root->u.cgrp.name);
344 SelectionTreeElementPointer child = root->child;
347 resolveExternalGroups(child, errors);
353 /********************************************************************
354 * SelectionCollection
357 SelectionCollection::SelectionCollection()
363 SelectionCollection::~SelectionCollection()
369 SelectionCollection::initOptions(Options *options)
371 static const char * const debug_levels[]
372 = {"no", "basic", "compile", "eval", "full", NULL};
374 static const char * const desc[] = {
375 "This program supports selections in addition to traditional",
376 "index files. Use [TT]-select help[tt] for additional information,",
377 "or type 'help' in the selection prompt.",
380 options.setDescription(desc);
383 const char *const *postypes = PositionCalculationCollection::typeEnumValues;
384 options->addOption(StringOption("selrpos").enumValue(postypes)
385 .store(&impl_->rpost_).defaultValue(postypes[0])
386 .description("Selection reference positions"));
387 options->addOption(StringOption("seltype").enumValue(postypes)
388 .store(&impl_->spost_).defaultValue(postypes[0])
389 .description("Default selection output positions"));
390 GMX_RELEASE_ASSERT(impl_->debugLevel_ >= 0 && impl_->debugLevel_ <= 4,
391 "Debug level out of range");
392 options->addOption(StringOption("seldebug").hidden(impl_->debugLevel_ == 0)
393 .enumValue(debug_levels)
394 .defaultValue(debug_levels[impl_->debugLevel_])
395 .storeEnumIndex(&impl_->debugLevel_)
396 .description("Print out selection trees for debugging"));
401 SelectionCollection::setReferencePosType(const char *type)
403 GMX_RELEASE_ASSERT(type != NULL, "Cannot assign NULL position type");
404 // Check that the type is valid, throw if it is not.
405 e_poscalc_t dummytype;
407 PositionCalculationCollection::typeFromEnum(type, &dummytype, &dummyflags);
408 impl_->rpost_ = type;
413 SelectionCollection::setOutputPosType(const char *type)
415 GMX_RELEASE_ASSERT(type != NULL, "Cannot assign NULL position type");
416 // Check that the type is valid, throw if it is not.
417 e_poscalc_t dummytype;
419 PositionCalculationCollection::typeFromEnum(type, &dummytype, &dummyflags);
420 impl_->spost_ = type;
425 SelectionCollection::setDebugLevel(int debugLevel)
427 impl_->debugLevel_ = debugLevel;
432 SelectionCollection::setTopology(t_topology *top, int natoms)
434 GMX_RELEASE_ASSERT(natoms > 0 || top != NULL,
435 "The number of atoms must be given if there is no topology");
436 // Get the number of atoms from the topology if it is not given.
439 natoms = top->atoms.nr;
441 gmx_ana_selcollection_t *sc = &impl_->sc_;
442 // Do this first, as it allocates memory, while the others don't throw.
443 gmx_ana_index_init_simple(&sc->gall, natoms, NULL);
444 sc->pcc.setTopology(top);
450 SelectionCollection::setIndexGroups(gmx_ana_indexgrps_t *grps)
452 GMX_RELEASE_ASSERT(grps == NULL || !impl_->bExternalGroupsSet_,
453 "Can only set external groups once or clear them afterwards");
455 impl_->bExternalGroupsSet_ = true;
457 MessageStringCollector errors;
458 SelectionTreeElementPointer root = impl_->sc_.root;
461 impl_->resolveExternalGroups(root, &errors);
464 if (!errors.isEmpty())
466 GMX_THROW(InvalidInputError(errors.toString()));
472 SelectionCollection::requiresTopology() const
477 if (!impl_->rpost_.empty())
480 // Should not throw, because has been checked earlier.
481 PositionCalculationCollection::typeFromEnum(impl_->rpost_.c_str(),
483 if (type != POS_ATOM)
488 if (!impl_->spost_.empty())
491 // Should not throw, because has been checked earlier.
492 PositionCalculationCollection::typeFromEnum(impl_->spost_.c_str(),
494 if (type != POS_ATOM)
500 SelectionTreeElementPointer sel = impl_->sc_.root;
503 if (_gmx_selelem_requires_top(*sel))
514 SelectionCollection::parseFromStdin(int nr, bool bInteractive)
518 _gmx_sel_init_lexer(&scanner, &impl_->sc_, bInteractive, nr,
519 impl_->bExternalGroupsSet_,
521 return runParser(scanner, true, nr);
526 SelectionCollection::parseFromFile(const std::string &filename)
532 File file(filename, "r");
533 // TODO: Exception-safe way of using the lexer.
534 _gmx_sel_init_lexer(&scanner, &impl_->sc_, false, -1,
535 impl_->bExternalGroupsSet_,
537 _gmx_sel_set_lex_input_file(scanner, file.handle());
538 return runParser(scanner, false, -1);
540 catch (GromacsException &ex)
542 ex.prependContext(formatString(
543 "Error in parsing selections from file '%s'",
551 SelectionCollection::parseFromString(const std::string &str)
555 _gmx_sel_init_lexer(&scanner, &impl_->sc_, false, -1,
556 impl_->bExternalGroupsSet_,
558 _gmx_sel_set_lex_input_str(scanner, str.c_str());
559 return runParser(scanner, false, -1);
564 SelectionCollection::compile()
566 if (impl_->sc_.top == NULL && requiresTopology())
568 GMX_THROW(InconsistentInputError("Selection requires topology information, but none provided"));
570 if (!impl_->bExternalGroupsSet_)
572 setIndexGroups(NULL);
574 if (impl_->debugLevel_ >= 1)
576 printTree(stderr, false);
579 SelectionCompiler compiler;
580 compiler.compile(this);
582 if (impl_->debugLevel_ >= 1)
584 std::fprintf(stderr, "\n");
585 printTree(stderr, false);
586 std::fprintf(stderr, "\n");
587 impl_->sc_.pcc.printTree(stderr);
588 std::fprintf(stderr, "\n");
590 impl_->sc_.pcc.initEvaluation();
591 if (impl_->debugLevel_ >= 1)
593 impl_->sc_.pcc.printTree(stderr);
594 std::fprintf(stderr, "\n");
600 SelectionCollection::evaluate(t_trxframe *fr, t_pbc *pbc)
602 impl_->sc_.pcc.initFrame();
604 SelectionEvaluator evaluator;
605 evaluator.evaluate(this, fr, pbc);
607 if (impl_->debugLevel_ >= 3)
609 std::fprintf(stderr, "\n");
610 printTree(stderr, true);
616 SelectionCollection::evaluateFinal(int nframes)
618 SelectionEvaluator evaluator;
619 evaluator.evaluateFinal(this, nframes);
624 SelectionCollection::printTree(FILE *fp, bool bValues) const
626 SelectionTreeElementPointer sel = impl_->sc_.root;
629 _gmx_selelem_print_tree(fp, *sel, bValues, 0);
636 SelectionCollection::printXvgrInfo(FILE *out, output_env_t oenv) const
638 if (output_env_get_xvg_format(oenv) != exvgNONE)
640 const gmx_ana_selcollection_t &sc = impl_->sc_;
641 std::fprintf(out, "# Selections:\n");
642 for (int i = 0; i < sc.nvars; ++i)
644 std::fprintf(out, "# %s\n", sc.varstrs[i]);
646 for (size_t i = 0; i < sc.sel.size(); ++i)
648 std::fprintf(out, "# %s\n", sc.sel[i]->selectionText());
650 std::fprintf(out, "#\n");
656 SelectionCollection::createDefaultHelpTopic()
658 return createSelectionHelpTopic();