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.
38 * Implements gmx::SelectionCollection.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_selection
45 #include "selectioncollection.h"
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"
75 #include "selectioncollection_impl.h"
77 #include "selmethod.h"
83 /********************************************************************
84 * SelectionCollection::Impl
87 SelectionCollection::Impl::Impl() :
88 debugLevel_(DebugLevel::None
),
89 bExternalGroupsSet_(false),
93 sc_
.varstrs
= 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()
106 // The tree must be freed before the SelectionData objects, since the
107 // tree may hold references to the position data in SelectionData.
110 for (int i
= 0; i
< sc_
.nvars
; ++i
)
112 sfree(sc_
.varstrs
[i
]);
115 gmx_ana_index_deinit(&sc_
.gall
);
118 _gmx_sel_mempool_destroy(sc_
.mempool
);
120 gmx_ana_index_deinit(&requiredAtoms_
);
124 void SelectionCollection::Impl::clearSymbolTable()
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
))
153 while (endsWith(*line
, "\\\n"))
155 line
->resize(line
->length() - 2);
156 if (statusWriter
!= nullptr)
158 statusWriter
->writeString("... ");
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();
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
;
195 int token
= _gmx_sel_yylex(&value
, &location
, scanner
);
196 if (bInteractive
&& token
== 0)
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
);
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
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
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
,
226 const std::string
& context
,
231 writer
->writeLine("Available static index groups:");
232 gmx_ana_indexgrps_print(writer
, grps
, 0);
234 writer
->writeString("Specify ");
237 writer
->writeString("any number of selections");
239 else if (maxCount
== 1)
241 writer
->writeString("a selection");
245 writer
->writeString(formatString("%d selections", maxCount
));
247 writer
->writeString(formatString("%s%s:\n", context
.empty() ? "" : " ", context
.c_str()));
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()));
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" : ""));
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());
305 manager
.writeCurrentTopic();
309 * Helper function that runs the parser once the tokenizer has been
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
327 SelectionList
runParser(yyscan_t scanner
,
328 TextInputStream
* inputStream
,
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
);
342 TextWriter
* statusWriter
= _gmx_sel_lexer_get_status_writer(scanner
);
343 if (statusWriter
!= nullptr)
345 printCurrentStatus(statusWriter
, sc
, grps
, oldCount
, maxnr
, context
, true);
349 while (promptLine(inputStream
, statusWriter
, &line
))
351 if (statusWriter
!= nullptr)
353 line
= stripString(line
);
356 printCurrentStatus(statusWriter
, sc
, grps
, oldCount
, maxnr
, context
, false);
359 if (startsWith(line
, "help") && (line
[4] == 0 || (std::isspace(line
[4]) != 0)))
361 printHelp(statusWriter
, sc
, line
);
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
;
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
);
382 GMX_RELEASE_ASSERT(status
== 0, "Parser errors should have resulted in an exception");
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
;
401 for (i
= sc
->sel
.begin() + oldCount
; i
!= sc
->sel
.end(); ++i
)
403 result
.emplace_back(i
->get());
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
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
;
439 checkExternalGroups(child
, natoms
, errors
);
444 //! Checks whether the given topology properties are available.
445 void checkTopologyProperties(const gmx_mtop_t
* top
, const SelectionTopologyProperties
& props
)
451 GMX_THROW(InconsistentInputError(
452 "Selection requires topology information, but none provided"));
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"));
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
;
485 resolveExternalGroups(child
, errors
);
486 root
->flags
|= (child
->flags
& SEL_UNSORTED
);
492 bool SelectionCollection::Impl::areForcesRequested() const
494 for (const auto& sel
: sc_
.sel
)
496 if (sel
->hasFlag(gmx::efSelection_EvaluateForces
))
505 SelectionTopologyProperties
506 SelectionCollection::Impl::requiredTopologyPropertiesForPositionType(const std::string
& post
, bool forces
)
508 SelectionTopologyProperties props
;
511 switch (PositionCalculationCollection::requiredTopologyInfoForType(post
.c_str(), forces
))
513 case PositionCalculationCollection::RequiredTopologyInfo::None
: break;
514 case PositionCalculationCollection::RequiredTopologyInfo::Topology
:
515 props
.merge(SelectionTopologyProperties::topology());
517 case PositionCalculationCollection::RequiredTopologyInfo::TopologyAndMasses
:
518 props
.merge(SelectionTopologyProperties::masses());
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"));
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
;
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
;
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.
605 natoms
= top
->natoms
;
607 if (impl_
->bExternalGroupsSet_
)
609 ExceptionInitializer
errors("Invalid index group references encountered");
610 SelectionTreeElementPointer root
= impl_
->sc_
.root
;
613 checkExternalGroups(root
, natoms
, &errors
);
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
);
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");
634 impl_
->bExternalGroupsSet_
= true;
636 ExceptionInitializer
errors("Invalid index group reference(s)");
637 SelectionTreeElementPointer root
= impl_
->sc_
.root
;
640 impl_
->resolveExternalGroups(root
, &errors
);
641 root
->checkUnsortedAtoms(true, &errors
);
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());
673 bool SelectionCollection::requiresIndexGroups() const
675 SelectionTreeElementPointer sel
= impl_
->sc_
.root
;
678 if (sel
->requiresIndexGroups())
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
);
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);
712 SelectionList
SelectionCollection::parseInteractive(int count
,
713 TextInputStream
* inputStream
,
714 TextOutputStream
* statusStream
,
715 const std::string
& context
)
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
)
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()));
746 SelectionList
SelectionCollection::parseFromString(const std::string
& str
)
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());
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
));
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 "
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
;
885 _gmx_selelem_print_tree(fp
, *sel
, bValues
, 0);
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");