/*! * \param[in] data Data for the current frame. * \param[in] sel Selection element being evaluated. * \param[in] g Group for which \p sel should be evaluated. * \returns 0 on success, a non-zero error code on error. * * Short-circuiting evaluation of logical OR expressions. * * Starts by evaluating the first child element in the group \p g. * For each subsequent child, finds the part of \p g that is not * included the value of any previous child, and evaluates the child * in that group until the last child is evaluated or all of \p g * is included in some child value. * The value of \p sel is set to the union of all the (evaluated) * child values. * * If the first child does not have an evaluation function, its value is * used without evaluation. * This happens if the first child is a constant expression, the selection * has been compiled, and the evaluation group is the same for each frame. * In this case, the compiler has taken care of that the child value is a * subset of \p g, making it unnecessary to evaluate it. * * This function is used as gmx::SelectionTreeElement::evaluate for * \ref SEL_BOOLEAN elements with \ref BOOL_OR. */ void _gmx_sel_evaluate_or(gmx_sel_evaluate_t *data, const gmx::SelectionTreeElementPointer &sel, gmx_ana_index_t *g) { gmx_ana_index_t tmp, tmp2; SelectionTreeElementPointer child = sel->child; if (child->evaluate) { MempoolSelelemReserver reserver(child, g->isize); child->evaluate(data, child, g); gmx_ana_index_partition(sel->v.u.g, &tmp, g, child->v.u.g); } else { gmx_ana_index_partition(sel->v.u.g, &tmp, g, child->v.u.g); } child = child->next; while (child && tmp.isize > 0) { { MempoolSelelemReserver reserver(child, tmp.isize); child->evaluate(data, child, &tmp); gmx_ana_index_partition(&tmp, &tmp2, &tmp, child->v.u.g); } sel->v.u.g->isize += tmp.isize; tmp.isize = tmp2.isize; tmp.index = tmp2.index; child = child->next; } gmx_ana_index_sort(sel->v.u.g); }
/*! * \param[in] data Data for the current frame. * \param[in] sel Selection element being evaluated. * \param[in] g Group for which \p sel should be evaluated. * \returns 0 on success, a non-zero error code on error. * * Short-circuiting evaluation of logical AND expressions. * * Starts by evaluating the first child element in the group \p g. * The each following child element is evaluated in the intersection * of all the previous values until all children have been evaluated * or the intersection becomes empty. * The value of \p sel is set to the intersection of all the (evaluated) * child values. * * If the first child does not have an evaluation function, it is skipped * and the evaluation is started at the second child. * This happens if the first child is a constant expression and during * compilation it was detected that the evaluation group is always a subset * of the constant group * (currently, the compiler never detects this). * * This function is used as gmx::SelectionTreeElement::evaluate for * \ref SEL_BOOLEAN elements with \ref BOOL_AND. */ void _gmx_sel_evaluate_and(gmx_sel_evaluate_t *data, const gmx::SelectionTreeElementPointer &sel, gmx_ana_index_t *g) { SelectionTreeElementPointer child = sel->child; /* Skip the first child if it does not have an evaluation function. */ if (!child->evaluate) { child = child->next; } /* Evaluate the first child */ { MempoolSelelemReserver reserver(child, g->isize); child->evaluate(data, child, g); gmx_ana_index_copy(sel->v.u.g, child->v.u.g, false); } child = child->next; while (child && sel->v.u.g->isize > 0) { MempoolSelelemReserver reserver(child, sel->v.u.g->isize); child->evaluate(data, child, sel->v.u.g); gmx_ana_index_intersection(sel->v.u.g, sel->v.u.g, child->v.u.g); child = child->next; } }
void SelectionCollection::setIndexGroups(gmx_ana_indexgrps_t *grps) { GMX_RELEASE_ASSERT(grps == NULL || !impl_->bExternalGroupsSet_, "Can only set external groups once or clear them afterwards"); impl_->grps_ = grps; impl_->bExternalGroupsSet_ = true; ExceptionInitializer errors("Invalid index group reference(s)"); SelectionTreeElementPointer root = impl_->sc_.root; while (root) { impl_->resolveExternalGroups(root, &errors); root->checkUnsortedAtoms(true, &errors); root = root->next; } if (errors.hasNestedExceptions()) { GMX_THROW(InconsistentInputError(errors)); } for (size_t i = 0; i < impl_->sc_.sel.size(); ++i) { impl_->sc_.sel[i]->refreshName(); } }
/*! * \param[in] name Name of the variable. * \param[in] expr The selection element that evaluates the variable. * \param scanner Scanner data structure. * \returns The created root selection element. * * This function handles the creation of root gmx::SelectionTreeElement objects * for variable assignments. A \ref SEL_ROOT element and a \ref SEL_SUBEXPR * element are both created. */ SelectionTreeElementPointer _gmx_sel_assign_variable(const char *name, const gmx::SelectionTreeElementPointer &expr, yyscan_t scanner) { gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner); const char *pselstr = _gmx_sel_lexer_pselstr(scanner); SelectionTreeElementPointer root; _gmx_selelem_update_flags(expr); /* Check if this is a constant non-group value */ if (expr->type == SEL_CONST && expr->v.type != GROUP_VALUE) { /* If so, just assign the constant value to the variable */ sc->symtab->addVariable(name, expr); } /* Check if we are assigning a variable to another variable */ else if (expr->type == SEL_SUBEXPRREF) { /* If so, make a simple alias */ sc->symtab->addVariable(name, expr->child); } else { /* Create the root element */ root.reset(new SelectionTreeElement(SEL_ROOT)); root->setName(name); /* Create the subexpression element */ root->child.reset(new SelectionTreeElement(SEL_SUBEXPR)); root->child->setName(name); _gmx_selelem_set_vtype(root->child, expr->v.type); root->child->child = expr; /* Update flags */ _gmx_selelem_update_flags(root); gmx::ExceptionInitializer errors("Invalid index group reference(s)"); root->checkUnsortedAtoms(true, &errors); if (errors.hasNestedExceptions()) { GMX_THROW(gmx::InconsistentInputError(errors)); } /* Add the variable to the symbol table */ sc->symtab->addVariable(name, root->child); } srenew(sc->varstrs, sc->nvars + 1); sc->varstrs[sc->nvars] = strdup(pselstr); ++sc->nvars; if (_gmx_sel_is_lexer_interactive(scanner)) { fprintf(stderr, "Variable '%s' parsed\n", pselstr); } return root; }
/*! * \param[in] name Name of the variable. * \param[in] expr The selection element that evaluates the variable. * \param scanner Scanner data structure. * \returns The created root selection element. * * This function handles the creation of root gmx::SelectionTreeElement objects * for variable assignments. A \ref SEL_ROOT element and a \ref SEL_SUBEXPR * element are both created. */ SelectionTreeElementPointer _gmx_sel_assign_variable(const char *name, const SelectionTreeElementPointer &expr, yyscan_t scanner) { gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner); const char *pselstr = _gmx_sel_lexer_pselstr(scanner); SelectionTreeElementPointer root; gmx::MessageStringCollector *errors = _gmx_sel_lexer_error_reporter(scanner); char buf[1024]; sprintf(buf, "In selection '%s'", pselstr); gmx::MessageStringContext context(errors, buf); _gmx_selelem_update_flags(expr, scanner); /* Check if this is a constant non-group value */ if (expr->type == SEL_CONST && expr->v.type != GROUP_VALUE) { /* If so, just assign the constant value to the variable */ sc->symtab->addVariable(name, expr); goto finish; } /* Check if we are assigning a variable to another variable */ if (expr->type == SEL_SUBEXPRREF) { /* If so, make a simple alias */ sc->symtab->addVariable(name, expr->child); goto finish; } /* Create the root element */ root.reset(new SelectionTreeElement(SEL_ROOT)); root->setName(name); /* Create the subexpression element */ root->child.reset(new SelectionTreeElement(SEL_SUBEXPR)); root->child->setName(name); _gmx_selelem_set_vtype(root->child, expr->v.type); root->child->child = expr; /* Update flags */ _gmx_selelem_update_flags(root, scanner); /* Add the variable to the symbol table */ sc->symtab->addVariable(name, root->child); finish: srenew(sc->varstrs, sc->nvars + 1); sc->varstrs[sc->nvars] = strdup(pselstr); ++sc->nvars; if (_gmx_sel_is_lexer_interactive(scanner)) { fprintf(stderr, "Variable '%s' parsed\n", pselstr); } return root; }
/*! \brief * Adds a new subexpression reference to a selection element. * * \param[in,out] root Root element to which the subexpression is added. * \param[in] param Parameter for which this expression is a value. * \param[in] expr Expression to add. * \param[in] scanner Scanner data structure. * \returns The created child element. * * Creates a new \ref SEL_SUBEXPRREF element and adds it into the child * list of \p root. * If \p expr is already a \ref SEL_SUBEXPRREF, it is used as it is. * \ref SEL_ALLOCVAL is cleared for the returned element. */ static SelectionTreeElementPointer add_child(const SelectionTreeElementPointer &root, gmx_ana_selparam_t *param, const SelectionTreeElementPointer &expr, void *scanner) { GMX_RELEASE_ASSERT(root->type == SEL_EXPRESSION || root->type == SEL_MODIFIER, "Unsupported root element for selection parameter parser"); SelectionTreeElementPointer child; /* Create a subexpression reference element if necessary */ if (expr->type == SEL_SUBEXPRREF) { child = expr; } else { // TODO: Initialize such that it includes the parameter. child.reset(new SelectionTreeElement(SEL_SUBEXPRREF, expr->location())); _gmx_selelem_set_vtype(child, expr->v.type); child->child = expr; } /* Setup the child element */ child->flags &= ~SEL_ALLOCVAL; child->u.param = param; if (child->v.type != param->val.type) { // TODO: It would be nice to say what is the expected type. std::string text(_gmx_sel_lexer_get_text(scanner, expr->location())); std::string message = formatString("Expression '%s' is not valid in this context " "(produces the wrong type of values)", text.c_str()); GMX_THROW(InvalidInputError(message)); } _gmx_selelem_update_flags(child); if ((child->flags & SEL_DYNAMIC) && !(param->flags & SPAR_DYNAMIC)) { std::string text(_gmx_sel_lexer_get_text(scanner, expr->location())); std::string message = formatString("Expression '%s' is dynamic, which is not " "valid in this context", text.c_str()); GMX_THROW(InvalidInputError(message)); } if (!(child->flags & SEL_DYNAMIC)) { param->flags &= ~SPAR_DYNAMIC; } /* Put the child element in the correct place */ place_child(root, child, param); return child; }
/*! * \param[in] data Data for the current frame. * \param[in] sel Selection element being evaluated. * \param[in] g Group for which \p sel should be evaluated. * \returns 0 on success, a non-zero error code on error. * * Evaluates each child of \p sel in \p g. */ void _gmx_sel_evaluate_children(gmx_sel_evaluate_t *data, const gmx::SelectionTreeElementPointer &sel, gmx_ana_index_t *g) { SelectionTreeElementPointer child = sel->child; while (child) { if (child->evaluate) { child->evaluate(data, child, g); } child = child->next; } }
/*! \brief * Initializes the storage of an expression value. * * \param[in,out] sel Selection element that evaluates the value. * \param[in] param Parameter to receive the value. * \param[in] i The value of \p sel evaluates the value \p i for * \p param. * \param[in] scanner Scanner data structure. * * Initializes the data pointer of \p sel such that the result is stored * as the value \p i of \p param. * This function is used internally by parse_values_std(). */ static void set_expr_value_store(const SelectionTreeElementPointer &sel, gmx_ana_selparam_t *param, int i, void *scanner) { if (sel->v.type != GROUP_VALUE && !(sel->flags & SEL_SINGLEVAL)) { std::string text(_gmx_sel_lexer_get_text(scanner, sel->location())); std::string message = formatString("Expression '%s' is invalid in this context", text.c_str()); GMX_THROW(InvalidInputError(message)); } switch (sel->v.type) { case INT_VALUE: sel->v.u.i = ¶m->val.u.i[i]; break; case REAL_VALUE: sel->v.u.r = ¶m->val.u.r[i]; break; case STR_VALUE: sel->v.u.s = ¶m->val.u.s[i]; break; case POS_VALUE: sel->v.u.p = ¶m->val.u.p[i]; break; case GROUP_VALUE: sel->v.u.g = ¶m->val.u.g[i]; break; default: /* Error */ GMX_THROW(InternalError("Invalid value type")); } sel->v.nr = 1; sel->v.nalloc = -1; }
void SelectionCollection::Impl::resolveExternalGroups( const SelectionTreeElementPointer &root, ExceptionInitializer *errors) { if (root->type == SEL_GROUPREF) { try { root->resolveIndexGroupReference(grps_, sc_.gall.isize); } catch (const UserInputError &) { errors->addCurrentExceptionAsNested(); } } SelectionTreeElementPointer child = root->child; while (child) { resolveExternalGroups(child, errors); root->flags |= (child->flags & SEL_UNSORTED); child = child->next; } }
/*! * \param[in,out] sel Value of the variable. * \returns The created selection element that references \p sel. * * The reference count of \p sel is updated, but no other modifications are * made. */ SelectionTreeElementPointer _gmx_sel_init_variable_ref(const gmx::SelectionTreeElementPointer &sel) { SelectionTreeElementPointer ref; if (sel->v.type == POS_VALUE && sel->type == SEL_CONST) { ref = sel; } else { ref.reset(new SelectionTreeElement(SEL_SUBEXPRREF)); _gmx_selelem_set_vtype(ref, sel->v.type); ref->setName(sel->name()); ref->child = sel; } return ref; }
/*! * \param[in,out] coll The selection collection to evaluate. * \param[in] fr Frame for which the evaluation should be carried out. * \param[in] pbc PBC data, or NULL if no PBC should be used. * \returns 0 on successful evaluation, a non-zero error code on error. * * This functions sets the global variables for topology, frame and PBC, * clears some information in the selection to initialize the evaluation * for a new frame, and evaluates \p sel and all the selections pointed by * the \p next pointers of \p sel. * * This is the only function that user code should call if they want to * evaluate a selection for a new frame. */ void SelectionEvaluator::evaluate(SelectionCollection *coll, t_trxframe *fr, t_pbc *pbc) { gmx_ana_selcollection_t *sc = &coll->impl_->sc_; gmx_sel_evaluate_t data; _gmx_sel_evaluate_init(&data, sc->mempool, &sc->gall, sc->top, fr, pbc); init_frame_eval(sc->root); SelectionTreeElementPointer sel = sc->root; while (sel) { /* Clear the evaluation group of subexpressions */ if (sel->child && sel->child->type == SEL_SUBEXPR && sel->child->evaluate != NULL) { sel->child->u.cgrp.isize = 0; /* Not strictly necessary, because the value will be overwritten * during first evaluation of the subexpression anyways, but we * clear the group for clarity. Note that this is _not_ done during * compilation because of some additional complexities involved * (see compiler.cpp), so it should not be relied upon in * _gmx_sel_evaluate_subexpr(). */ if (sel->child->v.type == GROUP_VALUE) { sel->child->v.u.g->isize = 0; } } if (sel->evaluate) { sel->evaluate(&data, sel, NULL); } sel = sel->next; } /* Update selection information */ SelectionDataList::const_iterator isel; for (isel = sc->sel.begin(); isel != sc->sel.end(); ++isel) { internal::SelectionData &sel = **isel; sel.refreshMassesAndCharges(sc->top); sel.updateCoveredFractionForFrame(); } }
/*! * \param[in,out] sel Selection element to initialize. * \param[in] method Selection method to set. * \param[in] scanner Scanner data structure. * * Makes a copy of \p method and stores it in \p sel->u.expr.method, * and calls _gmx_selelem_init_method_params(); */ void _gmx_selelem_set_method(const SelectionTreeElementPointer &sel, gmx_ana_selmethod_t *method, yyscan_t scanner) { _gmx_selelem_set_vtype(sel, method->type); sel->setName(method->name); snew(sel->u.expr.method, 1); memcpy(sel->u.expr.method, method, sizeof(gmx_ana_selmethod_t)); _gmx_selelem_init_method_params(sel, scanner); }
/*! * \param[in] data Data for the current frame. * \param[in] sel Selection element being evaluated. * \param[in] g Group for which \p sel should be evaluated. * \returns 0 on success, a non-zero error code on error. * * Evaluates each child of a \ref SEL_EXPRESSION element. * The value of \p sel is not touched. * * This function is not used as gmx::SelectionTreeElement::evaluate, * but is used internally. */ void _gmx_sel_evaluate_method_params(gmx_sel_evaluate_t *data, const gmx::SelectionTreeElementPointer &sel, gmx_ana_index_t *g) { SelectionTreeElementPointer child = sel->child; while (child) { if (child->evaluate && !(child->flags & SEL_EVALFRAME)) { if (child->flags & SEL_ATOMVAL) { child->evaluate(data, child, g); } else { child->flags |= SEL_EVALFRAME; child->evaluate(data, child, NULL); } } child = child->next; } }
void SelectionTreeElement::checkUnsortedAtoms( bool bUnsortedAllowed, ExceptionInitializer *errors) const { const bool bUnsortedSupported = (type == SEL_CONST && v.type == GROUP_VALUE) || type == SEL_ROOT || type == SEL_SUBEXPR || type == SEL_SUBEXPRREF // TODO: Consolidate. || type == SEL_MODIFIER || (type == SEL_EXPRESSION && (u.expr.method->flags & SMETH_ALLOW_UNSORTED)); // TODO: For some complicated selections, this may result in the same // index group reference being flagged as an error multiple times for the // same selection. SelectionTreeElementPointer child = this->child; while (child) { child->checkUnsortedAtoms(bUnsortedAllowed && bUnsortedSupported, errors); child = child->next; } // The logic here is simplified by the fact that only constant groups can // currently be the root cause of SEL_UNSORTED being set, so only those // need to be considered in triggering the error. if (!bUnsortedAllowed && (flags & SEL_UNSORTED) && type == SEL_CONST && v.type == GROUP_VALUE) { std::string message = formatString( "Group '%s' cannot be used in selections except " "as a full value of the selection, " "because atom indices in it are not sorted and/or " "it contains duplicate atoms.", name().c_str()); errors->addNested(InconsistentInputError(message)); } }
/*! * \param sel Selection to append (can be NULL, in which * case nothing is done). * \param last Last selection, or NULL if not present or not known. * \param scanner Scanner data structure. * \returns The last selection after the append. * * Appends \p sel after the last root element, and returns either \p sel * (if it was non-NULL) or the last element (if \p sel was NULL). */ SelectionTreeElementPointer _gmx_sel_append_selection(const SelectionTreeElementPointer &sel, SelectionTreeElementPointer last, yyscan_t scanner) { gmx_ana_selcollection_t *sc = _gmx_sel_lexer_selcollection(scanner); /* Append sel after last, or the last element of sc if last is NULL */ if (last) { last->next = sel; } else { if (sc->root) { last = sc->root; while (last->next) { last = last->next; } last->next = sel; } else { sc->root = sel; } } /* Initialize a selection object if necessary */ if (sel) { last = sel; /* Add the new selection to the collection if it is not a variable. */ if (sel->child->type != SEL_SUBEXPR) { gmx::SelectionDataPointer selPtr( new gmx::internal::SelectionData( sel.get(), _gmx_sel_lexer_pselstr(scanner))); sc->sel.push_back(gmx::move(selPtr)); } } /* Clear the selection string now that we've saved it */ _gmx_sel_lexer_clear_pselstr(scanner); return last; }