Use gmx_mtop_t in selections, part 2
[gromacs.git] / src / gromacs / selection / selectioncollection.cpp
blob5ae0f85ca1aa4871c4edd323222543d685f466f1
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010,2011,2012,2013,2014,2015,2016, 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.
35 /*! \internal \file
36 * \brief
37 * Implements gmx::SelectionCollection.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_selection
42 #include "gmxpre.h"
44 #include "selectioncollection.h"
46 #include <cctype>
47 #include <cstdio>
49 #include <memory>
50 #include <string>
51 #include <vector>
53 #include "gromacs/onlinehelp/helpmanager.h"
54 #include "gromacs/onlinehelp/helpwritercontext.h"
55 #include "gromacs/options/basicoptions.h"
56 #include "gromacs/options/ioptionscontainer.h"
57 #include "gromacs/selection/selection.h"
58 #include "gromacs/selection/selhelp.h"
59 #include "gromacs/topology/mtop_util.h"
60 #include "gromacs/topology/topology.h"
61 #include "gromacs/trajectory/trajectoryframe.h"
62 #include "gromacs/utility/exceptions.h"
63 #include "gromacs/utility/filestream.h"
64 #include "gromacs/utility/gmxassert.h"
65 #include "gromacs/utility/smalloc.h"
66 #include "gromacs/utility/stringutil.h"
67 #include "gromacs/utility/textwriter.h"
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"
79 namespace gmx
82 /********************************************************************
83 * SelectionCollection::Impl
86 SelectionCollection::Impl::Impl()
87 : debugLevel_(0), bExternalGroupsSet_(false), grps_(NULL)
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_index_clear(&requiredAtoms_);
96 gmx_ana_selmethod_register_defaults(sc_.symtab.get());
100 SelectionCollection::Impl::~Impl()
102 clearSymbolTable();
103 // The tree must be freed before the SelectionData objects, since the
104 // tree may hold references to the position data in SelectionData.
105 sc_.root.reset();
106 sc_.sel.clear();
107 for (int i = 0; i < sc_.nvars; ++i)
109 sfree(sc_.varstrs[i]);
111 sfree(sc_.varstrs);
112 gmx_ana_index_deinit(&sc_.gall);
113 if (sc_.mempool)
115 _gmx_sel_mempool_destroy(sc_.mempool);
117 gmx_ana_index_deinit(&requiredAtoms_);
121 void
122 SelectionCollection::Impl::clearSymbolTable()
124 sc_.symtab.reset();
128 namespace
131 /*! \brief
132 * Reads a single selection line from stdin.
134 * \param[in] inputStream Stream to read from (typically the StandardInputStream).
135 * \param[in] statusWriter Stream to print prompts to (if NULL, no output is done).
136 * \param[out] line The read line in stored here.
137 * \returns true if something was read, false if at end of input.
139 * Handles line continuation, reading also the continuing line(s) in one call.
141 bool promptLine(TextInputStream *inputStream, TextWriter *statusWriter,
142 std::string *line)
144 if (statusWriter != NULL)
146 statusWriter->writeString("> ");
148 if (!inputStream->readLine(line))
150 return false;
152 while (endsWith(*line, "\\\n"))
154 line->resize(line->length() - 2);
155 if (statusWriter != NULL)
157 statusWriter->writeString("... ");
159 std::string buffer;
160 // Return value ignored, buffer remains empty and works correctly
161 // if there is nothing to read.
162 inputStream->readLine(&buffer);
163 line->append(buffer);
165 if (endsWith(*line, "\n"))
167 line->resize(line->length() - 1);
169 else if (statusWriter != NULL)
171 statusWriter->writeLine();
173 return true;
176 /*! \brief
177 * Helper function for tokenizing the input and pushing them to the parser.
179 * \param scanner Tokenizer data structure.
180 * \param parserState Parser data structure.
181 * \param[in] bInteractive Whether to operate in interactive mode.
183 * Repeatedly reads tokens using \p scanner and pushes them to the parser with
184 * \p parserState until there is no more input, or until enough input is given
185 * (only in interactive mode).
187 int runParserLoop(yyscan_t scanner, _gmx_sel_yypstate *parserState,
188 bool bInteractive)
190 int status = YYPUSH_MORE;
193 YYSTYPE value;
194 YYLTYPE location;
195 int token = _gmx_sel_yylex(&value, &location, scanner);
196 if (bInteractive && token == 0)
198 break;
200 status = _gmx_sel_yypush_parse(parserState, token, &value, &location, scanner);
202 while (status == YYPUSH_MORE);
203 _gmx_sel_lexer_rethrow_exception_if_occurred(scanner);
204 return status;
207 /*! \brief
208 * Print current status in response to empty line in interactive input.
210 * \param[in] writer Writer to use for the output.
211 * \param[in] sc Selection collection data structure.
212 * \param[in] grps Available index groups.
213 * \param[in] firstSelection Index of first selection from this interactive
214 * session.
215 * \param[in] maxCount Maximum number of selections.
216 * \param[in] context Context to print for what the selections are for.
217 * \param[in] bFirst Whether this is the header that is printed before
218 * any user input.
220 * Prints the available index groups and currently provided selections.
222 void printCurrentStatus(TextWriter *writer, gmx_ana_selcollection_t *sc,
223 gmx_ana_indexgrps_t *grps, size_t firstSelection,
224 int maxCount, const std::string &context, bool bFirst)
226 if (grps != NULL)
228 writer->writeLine("Available static index groups:");
229 gmx_ana_indexgrps_print(writer, grps, 0);
231 writer->writeString("Specify ");
232 if (maxCount < 0)
234 writer->writeString("any number of selections");
236 else if (maxCount == 1)
238 writer->writeString("a selection");
240 else
242 writer->writeString(formatString("%d selections", maxCount));
244 writer->writeString(formatString("%s%s:\n",
245 context.empty() ? "" : " ", context.c_str()));
246 writer->writeString(formatString(
247 "(one per line, <enter> for status/groups, 'help' for help%s)\n",
248 maxCount < 0 ? ", Ctrl-D to end" : ""));
249 if (!bFirst && (sc->nvars > 0 || sc->sel.size() > firstSelection))
251 writer->writeLine("Currently provided selections:");
252 for (int i = 0; i < sc->nvars; ++i)
254 writer->writeString(formatString(" %s\n", sc->varstrs[i]));
256 for (size_t i = firstSelection; i < sc->sel.size(); ++i)
258 writer->writeString(formatString(
259 " %2d. %s\n",
260 static_cast<int>(i - firstSelection + 1),
261 sc->sel[i]->selectionText()));
263 if (maxCount > 0)
265 const int remaining
266 = maxCount - static_cast<int>(sc->sel.size() - firstSelection);
267 writer->writeString(formatString(
268 "(%d more selection%s required)\n",
269 remaining, remaining > 1 ? "s" : ""));
274 /*! \brief
275 * Prints selection help in interactive selection input.
277 * \param[in] writer Writer to use for the output.
278 * \param[in] sc Selection collection data structure.
279 * \param[in] line Line of user input requesting help (starting with `help`).
281 * Initializes the selection help if not yet initialized, and finds the help
282 * topic based on words on the input line.
284 void printHelp(TextWriter *writer, gmx_ana_selcollection_t *sc,
285 const std::string &line)
287 if (sc->rootHelp.get() == NULL)
289 sc->rootHelp = createSelectionHelpTopic();
291 HelpWriterContext context(writer, eHelpOutputFormat_Console);
292 HelpManager manager(*sc->rootHelp, context);
295 std::vector<std::string> topic = splitString(line);
296 std::vector<std::string>::const_iterator value;
297 // First item in the list is the 'help' token.
298 for (value = topic.begin() + 1; value != topic.end(); ++value)
300 manager.enterTopic(*value);
303 catch (const InvalidInputError &ex)
305 writer->writeLine(ex.what());
306 return;
308 manager.writeCurrentTopic();
311 /*! \brief
312 * Helper function that runs the parser once the tokenizer has been
313 * initialized.
315 * \param[in,out] scanner Scanner data structure.
316 * \param[in] inputStream Stream to use for input (currently only with
317 * `bInteractive==true`).
318 * \param[in] bInteractive Whether to use a line-based reading
319 * algorithm designed for interactive input.
320 * \param[in] maxnr Maximum number of selections to parse
321 * (if -1, parse as many as provided by the user).
322 * \param[in] context Context to print for what the selections are for.
323 * \returns Vector of parsed selections.
324 * \throws std::bad_alloc if out of memory.
325 * \throws InvalidInputError if there is a parsing error.
327 * Used internally to implement parseInteractive(), parseFromFile() and
328 * parseFromString().
330 SelectionList runParser(yyscan_t scanner, TextInputStream *inputStream,
331 bool bInteractive, int maxnr, const std::string &context)
333 std::shared_ptr<void> scannerGuard(scanner, &_gmx_sel_free_lexer);
334 gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner);
335 gmx_ana_indexgrps_t *grps = _gmx_sel_lexer_indexgrps(scanner);
337 size_t oldCount = sc->sel.size();
339 std::shared_ptr<_gmx_sel_yypstate> parserState(
340 _gmx_sel_yypstate_new(), &_gmx_sel_yypstate_delete);
341 if (bInteractive)
343 TextWriter *statusWriter = _gmx_sel_lexer_get_status_writer(scanner);
344 if (statusWriter != NULL)
346 printCurrentStatus(statusWriter, sc, grps, oldCount, maxnr, context, true);
348 std::string line;
349 int status;
350 while (promptLine(inputStream, statusWriter, &line))
352 if (statusWriter != NULL)
354 line = stripString(line);
355 if (line.empty())
357 printCurrentStatus(statusWriter, sc, grps, oldCount, maxnr, context, false);
358 continue;
360 if (startsWith(line, "help")
361 && (line[4] == 0 || std::isspace(line[4])))
363 printHelp(statusWriter, sc, line);
364 continue;
367 line.append("\n");
368 _gmx_sel_set_lex_input_str(scanner, line.c_str());
369 status = runParserLoop(scanner, parserState.get(), true);
370 if (status != YYPUSH_MORE)
372 // TODO: Check if there is more input, and issue an
373 // error/warning if some input was ignored.
374 goto early_termination;
378 YYLTYPE location;
379 status = _gmx_sel_yypush_parse(parserState.get(), 0, NULL,
380 &location, scanner);
382 // TODO: Remove added selections from the collection if parsing failed?
383 _gmx_sel_lexer_rethrow_exception_if_occurred(scanner);
384 early_termination:
385 GMX_RELEASE_ASSERT(status == 0,
386 "Parser errors should have resulted in an exception");
388 else
390 int status = runParserLoop(scanner, parserState.get(), false);
391 GMX_RELEASE_ASSERT(status == 0,
392 "Parser errors should have resulted in an exception");
395 scannerGuard.reset();
396 int nr = sc->sel.size() - oldCount;
397 if (maxnr > 0 && nr != maxnr)
399 std::string message
400 = formatString("Too few selections provided; got %d, expected %d",
401 nr, maxnr);
402 GMX_THROW(InvalidInputError(message));
405 SelectionList result;
406 SelectionDataList::const_iterator i;
407 result.reserve(nr);
408 for (i = sc->sel.begin() + oldCount; i != sc->sel.end(); ++i)
410 result.emplace_back(i->get());
412 return result;
415 /*! \brief
416 * Checks that index groups have valid atom indices.
418 * \param[in] root Root of selection tree to process.
419 * \param[in] natoms Maximum number of atoms that the selections are set
420 * to evaluate.
421 * \param errors Object for reporting any error messages.
422 * \throws std::bad_alloc if out of memory.
424 * Recursively checks the selection tree for index groups.
425 * Each found group is checked that it only contains atom indices that match
426 * the topology/maximum number of atoms set for the selection collection.
427 * Any issues are reported to \p errors.
429 void checkExternalGroups(const SelectionTreeElementPointer &root,
430 int natoms,
431 ExceptionInitializer *errors)
433 if (root->type == SEL_CONST && root->v.type == GROUP_VALUE)
437 root->checkIndexGroup(natoms);
439 catch (const UserInputError &)
441 errors->addCurrentExceptionAsNested();
445 SelectionTreeElementPointer child = root->child;
446 while (child)
448 checkExternalGroups(child, natoms, errors);
449 child = child->next;
453 //! Checks whether the given topology properties are available.
454 void checkTopologyProperties(const t_topology *top,
455 const SelectionTopologyProperties &props)
457 if (top == NULL)
459 if (props.hasAny())
461 GMX_THROW(InconsistentInputError("Selection requires topology information, but none provided"));
463 return;
465 if (props.needsMasses && !top->atoms.haveMass)
467 GMX_THROW(InconsistentInputError("Selection requires mass information, but it is not available in the topology"));
471 } // namespace
474 void SelectionCollection::Impl::resolveExternalGroups(
475 const SelectionTreeElementPointer &root,
476 ExceptionInitializer *errors)
479 if (root->type == SEL_GROUPREF)
483 root->resolveIndexGroupReference(grps_, sc_.gall.isize);
485 catch (const UserInputError &)
487 errors->addCurrentExceptionAsNested();
491 SelectionTreeElementPointer child = root->child;
492 while (child)
494 resolveExternalGroups(child, errors);
495 root->flags |= (child->flags & SEL_UNSORTED);
496 child = child->next;
501 bool SelectionCollection::Impl::areForcesRequested() const
503 for (const auto &sel : sc_.sel)
505 if (sel->hasFlag(gmx::efSelection_EvaluateForces))
507 return true;
510 return false;
514 SelectionTopologyProperties
515 SelectionCollection::Impl::requiredTopologyPropertiesForPositionType(
516 const std::string &post, bool forces) const
518 SelectionTopologyProperties props;
519 if (!post.empty())
521 switch (PositionCalculationCollection::requiredTopologyInfoForType(post.c_str(), forces))
523 case PositionCalculationCollection::RequiredTopologyInfo::None:
524 break;
525 case PositionCalculationCollection::RequiredTopologyInfo::Topology:
526 props.merge(SelectionTopologyProperties::topology());
527 break;
528 case PositionCalculationCollection::RequiredTopologyInfo::TopologyAndMasses:
529 props.merge(SelectionTopologyProperties::masses());
530 break;
533 return props;
537 /********************************************************************
538 * SelectionCollection
541 SelectionCollection::SelectionCollection()
542 : impl_(new Impl)
547 SelectionCollection::~SelectionCollection()
552 void
553 SelectionCollection::initOptions(IOptionsContainer *options,
554 SelectionTypeOption selectionTypeOption)
556 const char * const debug_levels[]
557 = { "no", "basic", "compile", "eval", "full" };
559 const char *const *postypes = PositionCalculationCollection::typeEnumValues;
560 options->addOption(StringOption("selrpos")
561 .enumValueFromNullTerminatedArray(postypes)
562 .store(&impl_->rpost_).defaultValue(postypes[0])
563 .description("Selection reference positions"));
564 if (selectionTypeOption == IncludeSelectionTypeOption)
566 options->addOption(StringOption("seltype")
567 .enumValueFromNullTerminatedArray(postypes)
568 .store(&impl_->spost_).defaultValue(postypes[0])
569 .description("Default selection output positions"));
571 else
573 impl_->spost_ = postypes[0];
575 GMX_RELEASE_ASSERT(impl_->debugLevel_ >= 0 && impl_->debugLevel_ <= 4,
576 "Debug level out of range");
577 options->addOption(EnumIntOption("seldebug").hidden(impl_->debugLevel_ == 0)
578 .enumValue(debug_levels).store(&impl_->debugLevel_)
579 .description("Print out selection trees for debugging"));
583 void
584 SelectionCollection::setReferencePosType(const char *type)
586 GMX_RELEASE_ASSERT(type != NULL, "Cannot assign NULL position type");
587 // Check that the type is valid, throw if it is not.
588 e_poscalc_t dummytype;
589 int dummyflags;
590 PositionCalculationCollection::typeFromEnum(type, &dummytype, &dummyflags);
591 impl_->rpost_ = type;
595 void
596 SelectionCollection::setOutputPosType(const char *type)
598 GMX_RELEASE_ASSERT(type != NULL, "Cannot assign NULL position type");
599 // Check that the type is valid, throw if it is not.
600 e_poscalc_t dummytype;
601 int dummyflags;
602 PositionCalculationCollection::typeFromEnum(type, &dummytype, &dummyflags);
603 impl_->spost_ = type;
607 void
608 SelectionCollection::setDebugLevel(int debugLevel)
610 impl_->debugLevel_ = debugLevel;
614 void
615 SelectionCollection::setTopology(gmx_mtop_t *top, int natoms)
617 GMX_RELEASE_ASSERT(natoms > 0 || top != NULL,
618 "The number of atoms must be given if there is no topology");
619 // Get the number of atoms from the topology if it is not given.
620 if (natoms <= 0)
622 natoms = top->natoms;
624 if (impl_->bExternalGroupsSet_)
626 ExceptionInitializer errors("Invalid index group references encountered");
627 SelectionTreeElementPointer root = impl_->sc_.root;
628 while (root)
630 checkExternalGroups(root, natoms, &errors);
631 root = root->next;
633 if (errors.hasNestedExceptions())
635 GMX_THROW(InconsistentInputError(errors));
638 gmx_ana_selcollection_t *sc = &impl_->sc_;
639 // Do this first, as it allocates memory, while the others don't throw.
640 gmx_ana_index_init_simple(&sc->gall, natoms);
641 // TODO: Adapt to use mtop throughout.
642 if (top != nullptr)
644 snew(sc->top, 1);
645 *sc->top = gmx_mtop_t_to_t_topology(top, false);
646 checkTopologyProperties(sc->top, requiredTopologyProperties());
648 else
650 checkTopologyProperties(nullptr, requiredTopologyProperties());
652 sc->pcc.setTopology(top);
656 void
657 SelectionCollection::setIndexGroups(gmx_ana_indexgrps_t *grps)
659 GMX_RELEASE_ASSERT(grps == NULL || !impl_->bExternalGroupsSet_,
660 "Can only set external groups once or clear them afterwards");
661 impl_->grps_ = grps;
662 impl_->bExternalGroupsSet_ = true;
664 ExceptionInitializer errors("Invalid index group reference(s)");
665 SelectionTreeElementPointer root = impl_->sc_.root;
666 while (root)
668 impl_->resolveExternalGroups(root, &errors);
669 root->checkUnsortedAtoms(true, &errors);
670 root = root->next;
672 if (errors.hasNestedExceptions())
674 GMX_THROW(InconsistentInputError(errors));
676 for (size_t i = 0; i < impl_->sc_.sel.size(); ++i)
678 impl_->sc_.sel[i]->refreshName();
682 SelectionTopologyProperties
683 SelectionCollection::requiredTopologyProperties() const
685 SelectionTopologyProperties props;
687 // These should not throw, because has been checked earlier.
688 props.merge(impl_->requiredTopologyPropertiesForPositionType(impl_->rpost_, false));
689 const bool forcesRequested = impl_->areForcesRequested();
690 props.merge(impl_->requiredTopologyPropertiesForPositionType(impl_->spost_,
691 forcesRequested));
693 SelectionTreeElementPointer sel = impl_->sc_.root;
694 while (sel && !props.hasAll())
696 props.merge(sel->requiredTopologyProperties());
697 sel = sel->next;
699 return props;
703 bool
704 SelectionCollection::requiresIndexGroups() const
706 SelectionTreeElementPointer sel = impl_->sc_.root;
707 while (sel)
709 if (sel->requiresIndexGroups())
711 return true;
713 sel = sel->next;
715 return false;
719 SelectionList
720 SelectionCollection::parseFromStdin(int count, bool bInteractive,
721 const std::string &context)
723 return parseInteractive(count, &StandardInputStream::instance(),
724 bInteractive ? &TextOutputFile::standardError() : NULL,
725 context);
728 namespace
731 //! Helper function to initialize status writer for interactive selection parsing.
732 std::unique_ptr<TextWriter> initStatusWriter(TextOutputStream *statusStream)
734 std::unique_ptr<TextWriter> statusWriter;
735 if (statusStream != NULL)
737 statusWriter.reset(new TextWriter(statusStream));
738 statusWriter->wrapperSettings().setLineLength(78);
740 return statusWriter;
743 } // namespace
745 SelectionList
746 SelectionCollection::parseInteractive(int count,
747 TextInputStream *inputStream,
748 TextOutputStream *statusStream,
749 const std::string &context)
751 yyscan_t scanner;
753 const std::unique_ptr<TextWriter> statusWriter(
754 initStatusWriter(statusStream));
755 _gmx_sel_init_lexer(&scanner, &impl_->sc_, statusWriter.get(),
756 count, impl_->bExternalGroupsSet_, impl_->grps_);
757 return runParser(scanner, inputStream, true, count, context);
761 SelectionList
762 SelectionCollection::parseFromFile(const std::string &filename)
767 yyscan_t scanner;
768 TextInputFile file(filename);
769 // TODO: Exception-safe way of using the lexer.
770 _gmx_sel_init_lexer(&scanner, &impl_->sc_, NULL, -1,
771 impl_->bExternalGroupsSet_,
772 impl_->grps_);
773 _gmx_sel_set_lex_input_file(scanner, file.handle());
774 return runParser(scanner, NULL, false, -1, std::string());
776 catch (GromacsException &ex)
778 ex.prependContext(formatString(
779 "Error in parsing selections from file '%s'",
780 filename.c_str()));
781 throw;
786 SelectionList
787 SelectionCollection::parseFromString(const std::string &str)
789 yyscan_t scanner;
791 _gmx_sel_init_lexer(&scanner, &impl_->sc_, NULL, -1,
792 impl_->bExternalGroupsSet_,
793 impl_->grps_);
794 _gmx_sel_set_lex_input_str(scanner, str.c_str());
795 return runParser(scanner, NULL, false, -1, std::string());
799 void
800 SelectionCollection::compile()
802 checkTopologyProperties(impl_->sc_.top, requiredTopologyProperties());
803 if (!impl_->bExternalGroupsSet_)
805 setIndexGroups(NULL);
807 if (impl_->debugLevel_ >= 1)
809 printTree(stderr, false);
812 SelectionCompiler compiler;
813 compiler.compile(this);
815 if (impl_->debugLevel_ >= 1)
817 std::fprintf(stderr, "\n");
818 printTree(stderr, false);
819 std::fprintf(stderr, "\n");
820 impl_->sc_.pcc.printTree(stderr);
821 std::fprintf(stderr, "\n");
823 impl_->sc_.pcc.initEvaluation();
824 if (impl_->debugLevel_ >= 1)
826 impl_->sc_.pcc.printTree(stderr);
827 std::fprintf(stderr, "\n");
830 // TODO: It would be nicer to associate the name of the selection option
831 // (if available) to the error message.
832 SelectionDataList::const_iterator iter;
833 for (iter = impl_->sc_.sel.begin(); iter != impl_->sc_.sel.end(); ++iter)
835 const internal::SelectionData &sel = **iter;
836 if (sel.hasFlag(efSelection_OnlyAtoms))
838 if (!sel.hasOnlyAtoms())
840 std::string message = formatString(
841 "Selection '%s' does not evaluate to individual atoms. "
842 "This is not allowed in this context.",
843 sel.selectionText());
844 GMX_THROW(InvalidInputError(message));
846 if (sel.hasFlag(efSelection_OnlySorted))
848 if (!sel.hasSortedAtomIndices())
850 const std::string message = formatString(
851 "Selection '%s' does not evaluate to atoms in an "
852 "ascending (sorted) order. "
853 "This is not allowed in this context.",
854 sel.selectionText());
855 GMX_THROW(InvalidInputError(message));
859 if (sel.hasFlag(efSelection_DisallowEmpty))
861 if (sel.posCount() == 0)
863 std::string message = formatString(
864 "Selection '%s' never matches any atoms.",
865 sel.selectionText());
866 GMX_THROW(InvalidInputError(message));
870 impl_->rpost_.clear();
871 impl_->spost_.clear();
875 void
876 SelectionCollection::evaluate(t_trxframe *fr, t_pbc *pbc)
878 checkTopologyProperties(impl_->sc_.top, requiredTopologyProperties());
879 if (fr->bIndex)
881 gmx_ana_index_t g;
882 gmx_ana_index_set(&g, fr->natoms, fr->index, 0);
883 GMX_RELEASE_ASSERT(gmx_ana_index_check_sorted(&g),
884 "Only trajectories with atoms in ascending order "
885 "are currently supported");
886 if (!gmx_ana_index_contains(&g, &impl_->requiredAtoms_))
888 const std::string message = formatString(
889 "Trajectory does not contain all atoms required for "
890 "evaluating the provided selections.");
891 GMX_THROW(InconsistentInputError(message));
894 else
896 const int maxAtomIndex = gmx_ana_index_get_max_index(&impl_->requiredAtoms_);
897 if (fr->natoms <= maxAtomIndex)
899 const std::string message = formatString(
900 "Trajectory has less atoms (%d) than what is required for "
901 "evaluating the provided selections (atoms up to index %d "
902 "are required).", fr->natoms, maxAtomIndex + 1);
903 GMX_THROW(InconsistentInputError(message));
906 impl_->sc_.pcc.initFrame(fr);
908 SelectionEvaluator evaluator;
909 evaluator.evaluate(this, fr, pbc);
911 if (impl_->debugLevel_ >= 3)
913 std::fprintf(stderr, "\n");
914 printTree(stderr, true);
919 void
920 SelectionCollection::evaluateFinal(int nframes)
922 SelectionEvaluator evaluator;
923 evaluator.evaluateFinal(this, nframes);
927 void
928 SelectionCollection::printTree(FILE *fp, bool bValues) const
930 SelectionTreeElementPointer sel = impl_->sc_.root;
931 while (sel)
933 _gmx_selelem_print_tree(fp, *sel, bValues, 0);
934 sel = sel->next;
939 void
940 SelectionCollection::printXvgrInfo(FILE *out) const
942 const gmx_ana_selcollection_t &sc = impl_->sc_;
943 std::fprintf(out, "# Selections:\n");
944 for (int i = 0; i < sc.nvars; ++i)
946 std::fprintf(out, "# %s\n", sc.varstrs[i]);
948 for (size_t i = 0; i < sc.sel.size(); ++i)
950 std::fprintf(out, "# %s\n", sc.sel[i]->selectionText());
952 std::fprintf(out, "#\n");
955 } // namespace gmx