Update instructions in containers.rst
[gromacs.git] / src / gromacs / selection / selectioncollection.cpp
bloba9c6e6825aea95cfa26d8577b2915cc9d9c11983
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010-2018, The GROMACS development team.
5 * Copyright (c) 2019,2020, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
36 /*! \internal \file
37 * \brief
38 * Implements gmx::SelectionCollection.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_selection
43 #include "gmxpre.h"
45 #include "selectioncollection.h"
47 #include <cctype>
48 #include <cstdio>
50 #include <memory>
51 #include <string>
52 #include <vector>
54 #include "gromacs/onlinehelp/helpmanager.h"
55 #include "gromacs/onlinehelp/helpwritercontext.h"
56 #include "gromacs/options/basicoptions.h"
57 #include "gromacs/options/ioptionscontainer.h"
58 #include "gromacs/selection/selection.h"
59 #include "gromacs/selection/selhelp.h"
60 #include "gromacs/topology/mtop_util.h"
61 #include "gromacs/topology/topology.h"
62 #include "gromacs/trajectory/trajectoryframe.h"
63 #include "gromacs/utility/exceptions.h"
64 #include "gromacs/utility/filestream.h"
65 #include "gromacs/utility/gmxassert.h"
66 #include "gromacs/utility/smalloc.h"
67 #include "gromacs/utility/stringutil.h"
68 #include "gromacs/utility/textwriter.h"
70 #include "compiler.h"
71 #include "mempool.h"
72 #include "parser.h"
73 #include "poscalc.h"
74 #include "scanner.h"
75 #include "selectioncollection_impl.h"
76 #include "selelem.h"
77 #include "selmethod.h"
78 #include "symrec.h"
80 namespace gmx
83 /********************************************************************
84 * SelectionCollection::Impl
87 SelectionCollection::Impl::Impl() :
88 debugLevel_(DebugLevel::None),
89 bExternalGroupsSet_(false),
90 grps_(nullptr)
92 sc_.nvars = 0;
93 sc_.varstrs = nullptr;
94 sc_.top = nullptr;
95 gmx_ana_index_clear(&sc_.gall);
96 sc_.mempool = nullptr;
97 sc_.symtab = std::make_unique<SelectionParserSymbolTable>();
98 gmx_ana_index_clear(&requiredAtoms_);
99 gmx_ana_selmethod_register_defaults(sc_.symtab.get());
103 SelectionCollection::Impl::~Impl()
105 clearSymbolTable();
106 // The tree must be freed before the SelectionData objects, since the
107 // tree may hold references to the position data in SelectionData.
108 sc_.root.reset();
109 sc_.sel.clear();
110 for (int i = 0; i < sc_.nvars; ++i)
112 sfree(sc_.varstrs[i]);
114 sfree(sc_.varstrs);
115 gmx_ana_index_deinit(&sc_.gall);
116 if (sc_.mempool)
118 _gmx_sel_mempool_destroy(sc_.mempool);
120 gmx_ana_index_deinit(&requiredAtoms_);
124 void SelectionCollection::Impl::clearSymbolTable()
126 sc_.symtab.reset();
130 namespace
133 /*! \brief
134 * Reads a single selection line from stdin.
136 * \param[in] inputStream Stream to read from (typically the StandardInputStream).
137 * \param[in] statusWriter Stream to print prompts to (if NULL, no output is done).
138 * \param[out] line The read line in stored here.
139 * \returns true if something was read, false if at end of input.
141 * Handles line continuation, reading also the continuing line(s) in one call.
143 bool promptLine(TextInputStream* inputStream, TextWriter* statusWriter, std::string* line)
145 if (statusWriter != nullptr)
147 statusWriter->writeString("> ");
149 if (!inputStream->readLine(line))
151 return false;
153 while (endsWith(*line, "\\\n"))
155 line->resize(line->length() - 2);
156 if (statusWriter != nullptr)
158 statusWriter->writeString("... ");
160 std::string buffer;
161 // Return value ignored, buffer remains empty and works correctly
162 // if there is nothing to read.
163 inputStream->readLine(&buffer);
164 line->append(buffer);
166 if (endsWith(*line, "\n"))
168 line->resize(line->length() - 1);
170 else if (statusWriter != nullptr)
172 statusWriter->writeLine();
174 return true;
177 /*! \brief
178 * Helper function for tokenizing the input and pushing them to the parser.
180 * \param scanner Tokenizer data structure.
181 * \param parserState Parser data structure.
182 * \param[in] bInteractive Whether to operate in interactive mode.
184 * Repeatedly reads tokens using \p scanner and pushes them to the parser with
185 * \p parserState until there is no more input, or until enough input is given
186 * (only in interactive mode).
188 int runParserLoop(yyscan_t scanner, _gmx_sel_yypstate* parserState, 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);
201 } while (status == YYPUSH_MORE);
202 _gmx_sel_lexer_rethrow_exception_if_occurred(scanner);
203 return status;
206 /*! \brief
207 * Print current status in response to empty line in interactive input.
209 * \param[in] writer Writer to use for the output.
210 * \param[in] sc Selection collection data structure.
211 * \param[in] grps Available index groups.
212 * \param[in] firstSelection Index of first selection from this interactive
213 * session.
214 * \param[in] maxCount Maximum number of selections.
215 * \param[in] context Context to print for what the selections are for.
216 * \param[in] bFirst Whether this is the header that is printed before
217 * any user input.
219 * Prints the available index groups and currently provided selections.
221 void printCurrentStatus(TextWriter* writer,
222 gmx_ana_selcollection_t* sc,
223 gmx_ana_indexgrps_t* grps,
224 size_t firstSelection,
225 int maxCount,
226 const std::string& context,
227 bool bFirst)
229 if (grps != nullptr)
231 writer->writeLine("Available static index groups:");
232 gmx_ana_indexgrps_print(writer, grps, 0);
234 writer->writeString("Specify ");
235 if (maxCount < 0)
237 writer->writeString("any number of selections");
239 else if (maxCount == 1)
241 writer->writeString("a selection");
243 else
245 writer->writeString(formatString("%d selections", maxCount));
247 writer->writeString(formatString("%s%s:\n", context.empty() ? "" : " ", context.c_str()));
248 writer->writeString(
249 formatString("(one per line, <enter> for status/groups, 'help' for help%s)\n",
250 maxCount < 0 ? ", Ctrl-D to end" : ""));
251 if (!bFirst && (sc->nvars > 0 || sc->sel.size() > firstSelection))
253 writer->writeLine("Currently provided selections:");
254 for (int i = 0; i < sc->nvars; ++i)
256 writer->writeString(formatString(" %s\n", sc->varstrs[i]));
258 for (size_t i = firstSelection; i < sc->sel.size(); ++i)
260 writer->writeString(formatString(" %2d. %s\n", static_cast<int>(i - firstSelection + 1),
261 sc->sel[i]->selectionText()));
263 if (maxCount > 0)
265 const int remaining = maxCount - static_cast<int>(sc->sel.size() - firstSelection);
266 writer->writeString(formatString("(%d more selection%s required)\n", remaining,
267 remaining > 1 ? "s" : ""));
272 /*! \brief
273 * Prints selection help in interactive selection input.
275 * \param[in] writer Writer to use for the output.
276 * \param[in] sc Selection collection data structure.
277 * \param[in] line Line of user input requesting help (starting with `help`).
279 * Initializes the selection help if not yet initialized, and finds the help
280 * topic based on words on the input line.
282 void printHelp(TextWriter* writer, gmx_ana_selcollection_t* sc, const std::string& line)
284 if (sc->rootHelp.get() == nullptr)
286 sc->rootHelp = createSelectionHelpTopic();
288 HelpWriterContext context(writer, eHelpOutputFormat_Console);
289 HelpManager manager(*sc->rootHelp, context);
292 std::vector<std::string> topic = splitString(line);
293 std::vector<std::string>::const_iterator value;
294 // First item in the list is the 'help' token.
295 for (value = topic.begin() + 1; value != topic.end(); ++value)
297 manager.enterTopic(*value);
300 catch (const InvalidInputError& ex)
302 writer->writeLine(ex.what());
303 return;
305 manager.writeCurrentTopic();
308 /*! \brief
309 * Helper function that runs the parser once the tokenizer has been
310 * initialized.
312 * \param[in,out] scanner Scanner data structure.
313 * \param[in] inputStream Stream to use for input (currently only with
314 * `bInteractive==true`).
315 * \param[in] bInteractive Whether to use a line-based reading
316 * algorithm designed for interactive input.
317 * \param[in] maxnr Maximum number of selections to parse
318 * (if -1, parse as many as provided by the user).
319 * \param[in] context Context to print for what the selections are for.
320 * \returns Vector of parsed selections.
321 * \throws std::bad_alloc if out of memory.
322 * \throws InvalidInputError if there is a parsing error.
324 * Used internally to implement parseInteractive(), parseFromFile() and
325 * parseFromString().
327 SelectionList runParser(yyscan_t scanner,
328 TextInputStream* inputStream,
329 bool bInteractive,
330 int maxnr,
331 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(_gmx_sel_yypstate_new(), &_gmx_sel_yypstate_delete);
340 if (bInteractive)
342 TextWriter* statusWriter = _gmx_sel_lexer_get_status_writer(scanner);
343 if (statusWriter != nullptr)
345 printCurrentStatus(statusWriter, sc, grps, oldCount, maxnr, context, true);
347 std::string line;
348 int status;
349 while (promptLine(inputStream, statusWriter, &line))
351 if (statusWriter != nullptr)
353 line = stripString(line);
354 if (line.empty())
356 printCurrentStatus(statusWriter, sc, grps, oldCount, maxnr, context, false);
357 continue;
359 if (startsWith(line, "help") && (line[4] == 0 || (std::isspace(line[4]) != 0)))
361 printHelp(statusWriter, sc, line);
362 continue;
365 line.append("\n");
366 _gmx_sel_set_lex_input_str(scanner, line.c_str());
367 status = runParserLoop(scanner, parserState.get(), true);
368 if (status != YYPUSH_MORE)
370 // TODO: Check if there is more input, and issue an
371 // error/warning if some input was ignored.
372 goto early_termination;
376 YYLTYPE location;
377 status = _gmx_sel_yypush_parse(parserState.get(), 0, nullptr, &location, scanner);
379 // TODO: Remove added selections from the collection if parsing failed?
380 _gmx_sel_lexer_rethrow_exception_if_occurred(scanner);
381 early_termination:
382 GMX_RELEASE_ASSERT(status == 0, "Parser errors should have resulted in an exception");
384 else
386 int status = runParserLoop(scanner, parserState.get(), false);
387 GMX_RELEASE_ASSERT(status == 0, "Parser errors should have resulted in an exception");
390 scannerGuard.reset();
391 int nr = sc->sel.size() - oldCount;
392 if (maxnr > 0 && nr != maxnr)
394 std::string message = formatString("Too few selections provided; got %d, expected %d", nr, maxnr);
395 GMX_THROW(InvalidInputError(message));
398 SelectionList result;
399 SelectionDataList::const_iterator i;
400 result.reserve(nr);
401 for (i = sc->sel.begin() + oldCount; i != sc->sel.end(); ++i)
403 result.emplace_back(i->get());
405 return result;
408 /*! \brief
409 * Checks that index groups have valid atom indices.
411 * \param[in] root Root of selection tree to process.
412 * \param[in] natoms Maximum number of atoms that the selections are set
413 * to evaluate.
414 * \param errors Object for reporting any error messages.
415 * \throws std::bad_alloc if out of memory.
417 * Recursively checks the selection tree for index groups.
418 * Each found group is checked that it only contains atom indices that match
419 * the topology/maximum number of atoms set for the selection collection.
420 * Any issues are reported to \p errors.
422 void checkExternalGroups(const SelectionTreeElementPointer& root, int natoms, ExceptionInitializer* errors)
424 if (root->type == SEL_CONST && root->v.type == GROUP_VALUE)
428 root->checkIndexGroup(natoms);
430 catch (const UserInputError&)
432 errors->addCurrentExceptionAsNested();
436 SelectionTreeElementPointer child = root->child;
437 while (child)
439 checkExternalGroups(child, natoms, errors);
440 child = child->next;
444 //! Checks whether the given topology properties are available.
445 void checkTopologyProperties(const gmx_mtop_t* top, const SelectionTopologyProperties& props)
447 if (top == nullptr)
449 if (props.hasAny())
451 GMX_THROW(InconsistentInputError(
452 "Selection requires topology information, but none provided"));
454 return;
456 if (props.needsMasses && !gmx_mtop_has_masses(top))
458 GMX_THROW(InconsistentInputError(
459 "Selection requires mass information, but it is not available in the topology"));
463 } // namespace
466 void SelectionCollection::Impl::resolveExternalGroups(const SelectionTreeElementPointer& root,
467 ExceptionInitializer* errors)
470 if (root->type == SEL_GROUPREF)
474 root->resolveIndexGroupReference(grps_, sc_.gall.isize);
476 catch (const UserInputError&)
478 errors->addCurrentExceptionAsNested();
482 SelectionTreeElementPointer child = root->child;
483 while (child)
485 resolveExternalGroups(child, errors);
486 root->flags |= (child->flags & SEL_UNSORTED);
487 child = child->next;
492 bool SelectionCollection::Impl::areForcesRequested() const
494 for (const auto& sel : sc_.sel)
496 if (sel->hasFlag(gmx::efSelection_EvaluateForces))
498 return true;
501 return false;
505 SelectionTopologyProperties
506 SelectionCollection::Impl::requiredTopologyPropertiesForPositionType(const std::string& post, bool forces)
508 SelectionTopologyProperties props;
509 if (!post.empty())
511 switch (PositionCalculationCollection::requiredTopologyInfoForType(post.c_str(), forces))
513 case PositionCalculationCollection::RequiredTopologyInfo::None: break;
514 case PositionCalculationCollection::RequiredTopologyInfo::Topology:
515 props.merge(SelectionTopologyProperties::topology());
516 break;
517 case PositionCalculationCollection::RequiredTopologyInfo::TopologyAndMasses:
518 props.merge(SelectionTopologyProperties::masses());
519 break;
522 return props;
526 /********************************************************************
527 * SelectionCollection
530 SelectionCollection::SelectionCollection() : impl_(new Impl) {}
533 SelectionCollection::~SelectionCollection() {}
536 void SelectionCollection::initOptions(IOptionsContainer* options, SelectionTypeOption selectionTypeOption)
538 static const EnumerationArray<Impl::DebugLevel, const char*> s_debugLevelNames = {
539 { "no", "basic", "compile", "eval", "full" }
542 const char* const* postypes = PositionCalculationCollection::typeEnumValues;
543 options->addOption(StringOption("selrpos")
544 .enumValueFromNullTerminatedArray(postypes)
545 .store(&impl_->rpost_)
546 .defaultValue(postypes[0])
547 .description("Selection reference positions"));
548 if (selectionTypeOption == IncludeSelectionTypeOption)
550 options->addOption(StringOption("seltype")
551 .enumValueFromNullTerminatedArray(postypes)
552 .store(&impl_->spost_)
553 .defaultValue(postypes[0])
554 .description("Default selection output positions"));
556 else
558 impl_->spost_ = postypes[0];
560 GMX_RELEASE_ASSERT(impl_->debugLevel_ != Impl::DebugLevel::Count, "Debug level out of range");
561 options->addOption(EnumOption<Impl::DebugLevel>("seldebug")
562 .hidden(impl_->debugLevel_ == Impl::DebugLevel::None)
563 .enumValue(s_debugLevelNames)
564 .store(&impl_->debugLevel_)
565 .description("Print out selection trees for debugging"));
569 void SelectionCollection::setReferencePosType(const char* type)
571 GMX_RELEASE_ASSERT(type != nullptr, "Cannot assign NULL position type");
572 // Check that the type is valid, throw if it is not.
573 e_poscalc_t dummytype;
574 int dummyflags;
575 PositionCalculationCollection::typeFromEnum(type, &dummytype, &dummyflags);
576 impl_->rpost_ = type;
580 void SelectionCollection::setOutputPosType(const char* type)
582 GMX_RELEASE_ASSERT(type != nullptr, "Cannot assign NULL position type");
583 // Check that the type is valid, throw if it is not.
584 e_poscalc_t dummytype;
585 int dummyflags;
586 PositionCalculationCollection::typeFromEnum(type, &dummytype, &dummyflags);
587 impl_->spost_ = type;
591 void SelectionCollection::setDebugLevel(int debugLevel)
593 impl_->debugLevel_ = Impl::DebugLevel(debugLevel);
597 void SelectionCollection::setTopology(gmx_mtop_t* top, int natoms)
599 GMX_RELEASE_ASSERT(natoms > 0 || top != nullptr,
600 "The number of atoms must be given if there is no topology");
601 checkTopologyProperties(top, requiredTopologyProperties());
602 // Get the number of atoms from the topology if it is not given.
603 if (natoms <= 0)
605 natoms = top->natoms;
607 if (impl_->bExternalGroupsSet_)
609 ExceptionInitializer errors("Invalid index group references encountered");
610 SelectionTreeElementPointer root = impl_->sc_.root;
611 while (root)
613 checkExternalGroups(root, natoms, &errors);
614 root = root->next;
616 if (errors.hasNestedExceptions())
618 GMX_THROW(InconsistentInputError(errors));
621 gmx_ana_selcollection_t* sc = &impl_->sc_;
622 // Do this first, as it allocates memory, while the others don't throw.
623 gmx_ana_index_init_simple(&sc->gall, natoms);
624 sc->top = top;
625 sc->pcc.setTopology(top);
629 void SelectionCollection::setIndexGroups(gmx_ana_indexgrps_t* grps)
631 GMX_RELEASE_ASSERT(grps == nullptr || !impl_->bExternalGroupsSet_,
632 "Can only set external groups once or clear them afterwards");
633 impl_->grps_ = grps;
634 impl_->bExternalGroupsSet_ = true;
636 ExceptionInitializer errors("Invalid index group reference(s)");
637 SelectionTreeElementPointer root = impl_->sc_.root;
638 while (root)
640 impl_->resolveExternalGroups(root, &errors);
641 root->checkUnsortedAtoms(true, &errors);
642 root = root->next;
644 if (errors.hasNestedExceptions())
646 GMX_THROW(InconsistentInputError(errors));
648 for (size_t i = 0; i < impl_->sc_.sel.size(); ++i)
650 impl_->sc_.sel[i]->refreshName();
654 SelectionTopologyProperties SelectionCollection::requiredTopologyProperties() const
656 SelectionTopologyProperties props;
658 // These should not throw, because has been checked earlier.
659 props.merge(impl_->requiredTopologyPropertiesForPositionType(impl_->rpost_, false));
660 const bool forcesRequested = impl_->areForcesRequested();
661 props.merge(impl_->requiredTopologyPropertiesForPositionType(impl_->spost_, forcesRequested));
663 SelectionTreeElementPointer sel = impl_->sc_.root;
664 while (sel && !props.hasAll())
666 props.merge(sel->requiredTopologyProperties());
667 sel = sel->next;
669 return props;
673 bool SelectionCollection::requiresIndexGroups() const
675 SelectionTreeElementPointer sel = impl_->sc_.root;
676 while (sel)
678 if (sel->requiresIndexGroups())
680 return true;
682 sel = sel->next;
684 return false;
688 SelectionList SelectionCollection::parseFromStdin(int count, bool bInteractive, const std::string& context)
690 StandardInputStream inputStream;
691 return parseInteractive(count, &inputStream,
692 bInteractive ? &TextOutputFile::standardError() : nullptr, context);
695 namespace
698 //! Helper function to initialize status writer for interactive selection parsing.
699 std::unique_ptr<TextWriter> initStatusWriter(TextOutputStream* statusStream)
701 std::unique_ptr<TextWriter> statusWriter;
702 if (statusStream != nullptr)
704 statusWriter = std::make_unique<TextWriter>(statusStream);
705 statusWriter->wrapperSettings().setLineLength(78);
707 return statusWriter;
710 } // namespace
712 SelectionList SelectionCollection::parseInteractive(int count,
713 TextInputStream* inputStream,
714 TextOutputStream* statusStream,
715 const std::string& context)
717 yyscan_t scanner;
719 const std::unique_ptr<TextWriter> statusWriter(initStatusWriter(statusStream));
720 _gmx_sel_init_lexer(&scanner, &impl_->sc_, statusWriter.get(), count,
721 impl_->bExternalGroupsSet_, impl_->grps_);
722 return runParser(scanner, inputStream, true, count, context);
726 SelectionList SelectionCollection::parseFromFile(const std::string& filename)
731 yyscan_t scanner;
732 TextInputFile file(filename);
733 // TODO: Exception-safe way of using the lexer.
734 _gmx_sel_init_lexer(&scanner, &impl_->sc_, nullptr, -1, impl_->bExternalGroupsSet_, impl_->grps_);
735 _gmx_sel_set_lex_input_file(scanner, file.handle());
736 return runParser(scanner, nullptr, false, -1, std::string());
738 catch (GromacsException& ex)
740 ex.prependContext(formatString("Error in parsing selections from file '%s'", filename.c_str()));
741 throw;
746 SelectionList SelectionCollection::parseFromString(const std::string& str)
748 yyscan_t scanner;
750 _gmx_sel_init_lexer(&scanner, &impl_->sc_, nullptr, -1, impl_->bExternalGroupsSet_, impl_->grps_);
751 _gmx_sel_set_lex_input_str(scanner, str.c_str());
752 return runParser(scanner, nullptr, false, -1, std::string());
756 void SelectionCollection::compile()
758 checkTopologyProperties(impl_->sc_.top, requiredTopologyProperties());
759 if (!impl_->bExternalGroupsSet_)
761 setIndexGroups(nullptr);
763 if (impl_->debugLevel_ != Impl::DebugLevel::None)
765 printTree(stderr, false);
768 compileSelection(this);
770 if (impl_->debugLevel_ != Impl::DebugLevel::None)
772 std::fprintf(stderr, "\n");
773 printTree(stderr, false);
774 std::fprintf(stderr, "\n");
775 impl_->sc_.pcc.printTree(stderr);
776 std::fprintf(stderr, "\n");
778 impl_->sc_.pcc.initEvaluation();
779 if (impl_->debugLevel_ != Impl::DebugLevel::None)
781 impl_->sc_.pcc.printTree(stderr);
782 std::fprintf(stderr, "\n");
785 // TODO: It would be nicer to associate the name of the selection option
786 // (if available) to the error message.
787 SelectionDataList::const_iterator iter;
788 for (iter = impl_->sc_.sel.begin(); iter != impl_->sc_.sel.end(); ++iter)
790 const internal::SelectionData& sel = **iter;
791 if (sel.hasFlag(efSelection_OnlyAtoms))
793 if (!sel.hasOnlyAtoms())
795 std::string message = formatString(
796 "Selection '%s' does not evaluate to individual atoms. "
797 "This is not allowed in this context.",
798 sel.selectionText());
799 GMX_THROW(InvalidInputError(message));
801 if (sel.hasFlag(efSelection_OnlySorted))
803 if (!sel.hasSortedAtomIndices())
805 const std::string message = formatString(
806 "Selection '%s' does not evaluate to atoms in an "
807 "ascending (sorted) order. "
808 "This is not allowed in this context.",
809 sel.selectionText());
810 GMX_THROW(InvalidInputError(message));
814 if (sel.hasFlag(efSelection_DisallowEmpty))
816 if (sel.posCount() == 0)
818 std::string message =
819 formatString("Selection '%s' never matches any atoms.", sel.selectionText());
820 GMX_THROW(InvalidInputError(message));
824 impl_->rpost_.clear();
825 impl_->spost_.clear();
829 void SelectionCollection::evaluate(t_trxframe* fr, t_pbc* pbc)
831 checkTopologyProperties(impl_->sc_.top, requiredTopologyProperties());
832 if (fr->bIndex)
834 gmx_ana_index_t g;
835 gmx_ana_index_set(&g, fr->natoms, fr->index, 0);
836 GMX_RELEASE_ASSERT(gmx_ana_index_check_sorted(&g),
837 "Only trajectories with atoms in ascending order "
838 "are currently supported");
839 if (!gmx_ana_index_contains(&g, &impl_->requiredAtoms_))
841 const std::string message = formatString(
842 "Trajectory does not contain all atoms required for "
843 "evaluating the provided selections.");
844 GMX_THROW(InconsistentInputError(message));
847 else
849 const int maxAtomIndex = gmx_ana_index_get_max_index(&impl_->requiredAtoms_);
850 if (fr->natoms <= maxAtomIndex)
852 const std::string message = formatString(
853 "Trajectory has less atoms (%d) than what is required for "
854 "evaluating the provided selections (atoms up to index %d "
855 "are required).",
856 fr->natoms, maxAtomIndex + 1);
857 GMX_THROW(InconsistentInputError(message));
860 impl_->sc_.pcc.initFrame(fr);
862 SelectionEvaluator evaluator;
863 evaluator.evaluate(this, fr, pbc);
865 if (impl_->debugLevel_ == Impl::DebugLevel::Evaluated || impl_->debugLevel_ == Impl::DebugLevel::Full)
867 std::fprintf(stderr, "\n");
868 printTree(stderr, true);
873 void SelectionCollection::evaluateFinal(int nframes)
875 SelectionEvaluator evaluator;
876 evaluator.evaluateFinal(this, nframes);
880 void SelectionCollection::printTree(FILE* fp, bool bValues) const
882 SelectionTreeElementPointer sel = impl_->sc_.root;
883 while (sel)
885 _gmx_selelem_print_tree(fp, *sel, bValues, 0);
886 sel = sel->next;
891 void SelectionCollection::printXvgrInfo(FILE* out) const
893 const gmx_ana_selcollection_t& sc = impl_->sc_;
894 std::fprintf(out, "# Selections:\n");
895 for (int i = 0; i < sc.nvars; ++i)
897 std::fprintf(out, "# %s\n", sc.varstrs[i]);
899 for (size_t i = 0; i < sc.sel.size(); ++i)
901 std::fprintf(out, "# %s\n", sc.sel[i]->selectionText());
903 std::fprintf(out, "#\n");
906 } // namespace gmx