Merge release-2021 into master
[alexxy/gromacs.git] / src / gromacs / selection / evaluate.cpp
index 9b1fb6ed213baca33ef062148cdb4f38b2f14924..079f906cb9701b21d8c94381a1762c42f28a1fb9 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * \author Teemu Murtola <teemu.murtola@gmail.com>
  * \ingroup module_selection
  */
-#include <string.h>
+#include "gmxpre.h"
 
-#include "gromacs/legacyheaders/vec.h"
+#include "evaluate.h"
+
+#include <cstring>
+
+#include <algorithm>
 
 #include "gromacs/math/utilities.h"
+#include "gromacs/math/vec.h"
 #include "gromacs/selection/indexutil.h"
-#include "gromacs/selection/poscalc.h"
 #include "gromacs/selection/selection.h"
-#include "gromacs/selection/selmethod.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/smalloc.h"
 
-#include "evaluate.h"
 #include "mempool.h"
-#include "selectioncollection-impl.h"
+#include "poscalc.h"
+#include "selectioncollection_impl.h"
 #include "selelem.h"
+#include "selmethod.h"
 
 using gmx::SelectionTreeElement;
 using gmx::SelectionTreeElementPointer;
@@ -84,51 +90,50 @@ namespace
  */
 class MempoolSelelemReserver
 {
-    public:
-        //! Constructs a reserver without initial reservation.
-        MempoolSelelemReserver() {}
-        /*! \brief
-         * Constructs a reserver with initial reservation.
-         *
-         * \param[in,out] sel    Selection element for which to reserve.
-         * \param[in]     count  Number of values to reserve.
-         *
-         * \see reserve()
-         */
-        MempoolSelelemReserver(const SelectionTreeElementPointer &sel, int count)
-        {
-            reserve(sel, count);
-        }
-        //! Frees any memory allocated using this reserver.
-        ~MempoolSelelemReserver()
+public:
+    //! Constructs a reserver without initial reservation.
+    MempoolSelelemReserver() {}
+    /*! \brief
+     * Constructs a reserver with initial reservation.
+     *
+     * \param[in,out] sel    Selection element for which to reserve.
+     * \param[in]     count  Number of values to reserve.
+     *
+     * \see reserve()
+     */
+    MempoolSelelemReserver(const SelectionTreeElementPointer& sel, int count)
+    {
+        reserve(sel, count);
+    }
+    //! Frees any memory allocated using this reserver.
+    ~MempoolSelelemReserver()
+    {
+        if (sel_)
         {
-            if (sel_)
-            {
-                sel_->mempoolRelease();
-            }
+            sel_->mempoolRelease();
         }
+    }
 
-        /*! \brief
-         * Reserves memory for selection element values using this reserver.
-         *
-         * \param[in,out] sel    Selection element for which to reserve.
-         * \param[in]     count  Number of values to reserve.
-         *
-         * Allocates space to store \p count output values in \p sel from the
-         * memory pool associated with \p sel, or from the heap if there is no
-         * memory pool.  Type of values to allocate is automatically determined
-         * from \p sel.
-         */
-        void reserve(const SelectionTreeElementPointer &sel, int count)
-        {
-            GMX_RELEASE_ASSERT(!sel_,
-                               "Can only reserve one element with one instance");
-            sel->mempoolReserve(count);
-            sel_ = sel;
-        }
+    /*! \brief
+     * Reserves memory for selection element values using this reserver.
+     *
+     * \param[in,out] sel    Selection element for which to reserve.
+     * \param[in]     count  Number of values to reserve.
+     *
+     * Allocates space to store \p count output values in \p sel from the
+     * memory pool associated with \p sel, or from the heap if there is no
+     * memory pool.  Type of values to allocate is automatically determined
+     * from \p sel.
+     */
+    void reserve(const SelectionTreeElementPointer& sel, int count)
+    {
+        GMX_RELEASE_ASSERT(!sel_, "Can only reserve one element with one instance");
+        sel->mempoolReserve(count);
+        sel_ = sel;
+    }
 
-    private:
-        SelectionTreeElementPointer     sel_;
+private:
+    SelectionTreeElementPointer sel_;
 };
 
 /*! \brief
@@ -141,44 +146,41 @@ class MempoolSelelemReserver
  */
 class MempoolGroupReserver
 {
-    public:
-        /*! \brief
-         * Creates a reserver associated with a given memory pool.
-         *
-         * \param    mp  Memory pool from which to reserve memory.
-         */
-        explicit MempoolGroupReserver(gmx_sel_mempool_t *mp)
-            : mp_(mp), g_(NULL)
-        {
-        }
-        //! Frees any memory allocated using this reserver.
-        ~MempoolGroupReserver()
+public:
+    /*! \brief
+     * Creates a reserver associated with a given memory pool.
+     *
+     * \param    mp  Memory pool from which to reserve memory.
+     */
+    explicit MempoolGroupReserver(gmx_sel_mempool_t* mp) : mp_(mp), g_(nullptr) {}
+    //! Frees any memory allocated using this reserver.
+    ~MempoolGroupReserver()
+    {
+        if (g_ != nullptr)
         {
-            if (g_ != NULL)
-            {
-                _gmx_sel_mempool_free_group(mp_, g_);
-            }
+            _gmx_sel_mempool_free_group(mp_, g_);
         }
+    }
 
-        /*! \brief
-         * Reserves memory for an index group using this reserver.
-         *
-         * \param[in,out] g      Index group to reserve.
-         * \param[in]     count  Number of atoms to reserve space for.
-         *
-         * Allocates memory from the memory pool to store \p count atoms in
-         * \p g.
-         */
-        void reserve(gmx_ana_index_t *g, int count)
-        {
-            GMX_RELEASE_ASSERT(g_ == NULL, "Can only reserve one element with one instance");
-            _gmx_sel_mempool_alloc_group(mp_, g, count);
-            g_ = g;
-        }
+    /*! \brief
+     * Reserves memory for an index group using this reserver.
+     *
+     * \param[in,out] g      Index group to reserve.
+     * \param[in]     count  Number of atoms to reserve space for.
+     *
+     * Allocates memory from the memory pool to store \p count atoms in
+     * \p g.
+     */
+    void reserve(gmx_ana_index_t* g, int count)
+    {
+        GMX_RELEASE_ASSERT(g_ == nullptr, "Can only reserve one element with one instance");
+        _gmx_sel_mempool_alloc_group(mp_, g, count);
+        g_ = g;
+    }
 
-    private:
-        gmx_sel_mempool_t      *mp_;
-        gmx_ana_index_t        *g_;
+private:
+    gmx_sel_mempool_t* mp_;
+    gmx_ana_index_t*   g_;
 };
 
 /*! \brief
@@ -191,71 +193,97 @@ class MempoolGroupReserver
  */
 class SelelemTemporaryValueAssigner
 {
-    public:
-        //! Constructs an assigner without an initial assignment.
-        SelelemTemporaryValueAssigner()
-            : old_ptr_(NULL), old_nalloc_(0)
-        {
-        }
-        /*! \brief
-         * Constructs an assigner with an initial assignment.
-         *
-         * \param[in,out] sel     Selection element for which to assign.
-         * \param[in]     vsource Element to which \p sel values will point to.
-         *
-         * \see assign()
-         */
-        SelelemTemporaryValueAssigner(const SelectionTreeElementPointer &sel,
-                                      const SelectionTreeElement        &vsource)
-        {
-            assign(sel, vsource);
-        }
-        //! Undoes any temporary assignment done using this assigner.
-        ~SelelemTemporaryValueAssigner()
+public:
+    //! Constructs an assigner without an initial assignment.
+    SelelemTemporaryValueAssigner() : old_ptr_(nullptr), old_nalloc_(0) {}
+    /*! \brief
+     * Constructs an assigner with an initial assignment.
+     *
+     * \param[in,out] sel     Selection element for which to assign.
+     * \param[in]     vsource Element to which \p sel values will point to.
+     *
+     * \see assign()
+     */
+    SelelemTemporaryValueAssigner(const SelectionTreeElementPointer& sel, const SelectionTreeElement& vsource)
+    {
+        assign(sel, vsource);
+    }
+    //! Undoes any temporary assignment done using this assigner.
+    ~SelelemTemporaryValueAssigner()
+    {
+        if (sel_)
         {
-            if (sel_)
-            {
-                _gmx_selvalue_setstore_alloc(&sel_->v, old_ptr_, old_nalloc_);
-            }
+            _gmx_selvalue_setstore_alloc(&sel_->v, old_ptr_, old_nalloc_);
         }
+    }
 
-        /*! \brief
-         * Assigns a temporary value pointer.
-         *
-         * \param[in,out] sel     Selection element for which to assign.
-         * \param[in]     vsource Element to which \p sel values will point to.
-         *
-         * Assigns the value pointer in \p sel to point to the values in
-         * \p vsource, i.e., any access/modification to values in \p sel
-         * actually accesses values in \p vsource.
-         */
-        void assign(const SelectionTreeElementPointer &sel,
-                    const SelectionTreeElement        &vsource)
-        {
-            GMX_RELEASE_ASSERT(!sel_,
-                               "Can only assign one element with one instance");
-            GMX_RELEASE_ASSERT(sel->v.type == vsource.v.type,
-                               "Mismatching selection value types");
-            old_ptr_    = sel->v.u.ptr;
-            old_nalloc_ = sel->v.nalloc;
-            _gmx_selvalue_setstore(&sel->v, vsource.v.u.ptr);
-            sel_ = sel;
-        }
+    /*! \brief
+     * Assigns a temporary value pointer.
+     *
+     * \param[in,out] sel     Selection element for which to assign.
+     * \param[in]     vsource Element to which \p sel values will point to.
+     *
+     * Assigns the value pointer in \p sel to point to the values in
+     * \p vsource, i.e., any access/modification to values in \p sel
+     * actually accesses values in \p vsource.
+     */
+    void assign(const SelectionTreeElementPointer& sel, const SelectionTreeElement& vsource)
+    {
+        GMX_RELEASE_ASSERT(!sel_, "Can only assign one element with one instance");
+        GMX_RELEASE_ASSERT(sel->v.type == vsource.v.type, "Mismatching selection value types");
+        _gmx_selvalue_getstore_and_release(&sel->v, &old_ptr_, &old_nalloc_);
+        _gmx_selvalue_setstore(&sel->v, vsource.v.u.ptr);
+        sel_ = sel;
+    }
 
-    private:
-        SelectionTreeElementPointer     sel_;
-        void                           *old_ptr_;
-        int                             old_nalloc_;
+private:
+    SelectionTreeElementPointer sel_;
+    void*                       old_ptr_;
+    int                         old_nalloc_;
 };
 
+/*! \brief
+ * Expands a value array from one-per-position to one-per-atom.
+ *
+ * \param[in,out] value  Array to expand.
+ * \param[in,out] nr     Number of values in \p value.
+ * \param[in]     pos    Position data.
+ * \tparam        T      Value type of the array to expand.
+ *
+ * On input, \p value contains one value for each position in \p pos (and `*nr`
+ * must match).  On output, \p value will contain a value for each atom used to
+ * evaluate `pos`: each input value is replicated to all atoms that make up the
+ * corresponding position.
+ * The operation is done in-place.
+ *
+ * Does not throw.
+ */
+template<typename T>
+void expandValueForPositions(T value[], int* nr, gmx_ana_pos_t* pos)
+{
+    GMX_RELEASE_ASSERT(*nr == pos->count(),
+                       "Position update method did not return the correct number of values");
+    *nr = pos->m.mapb.nra;
+    // Loop over the positions in reverse order so that the expansion can be
+    // done in-place (assumes that each position has at least one atom, which
+    // should always be the case).
+    int outputIndex = pos->m.mapb.nra;
+    for (int i = pos->count() - 1; i >= 0; --i)
+    {
+        const int atomCount = pos->m.mapb.index[i + 1] - pos->m.mapb.index[i];
+        outputIndex -= atomCount;
+        GMX_ASSERT(outputIndex >= i, "In-place algorithm would overwrite data not yet used");
+        std::fill(&value[outputIndex], &value[outputIndex + atomCount], value[i]);
+    }
+}
+
 } // namespace
 
 /*!
  * \param[in] fp       File handle to receive the output.
  * \param[in] evalfunc Function pointer to print.
  */
-void
-_gmx_sel_print_evalfunc_name(FILE *fp, gmx::sel_evalfunc evalfunc)
+void _gmx_sel_print_evalfunc_name(FILE* fp, gmx::sel_evalfunc evalfunc)
 {
     if (!evalfunc)
     {
@@ -315,7 +343,7 @@ _gmx_sel_print_evalfunc_name(FILE *fp, gmx::sel_evalfunc evalfunc)
     }
     else
     {
-        fprintf(fp, "%p", (void*)(evalfunc));
+        fprintf(fp, "%p", reinterpret_cast<void*>(evalfunc));
     }
 }
 
@@ -327,10 +355,12 @@ _gmx_sel_print_evalfunc_name(FILE *fp, gmx::sel_evalfunc evalfunc)
  * \param[in]  fr   New frame for evaluation.
  * \param[in]  pbc  New PBC information for evaluation.
  */
-void
-_gmx_sel_evaluate_init(gmx_sel_evaluate_t *data,
-                       gmx_sel_mempool_t *mp, gmx_ana_index_t *gall,
-                       t_topology *top, t_trxframe *fr, t_pbc *pbc)
+void _gmx_sel_evaluate_init(gmx_sel_evaluate_t* data,
+                            gmx_sel_mempool_t*  mp,
+                            gmx_ana_index_t*    gall,
+                            const gmx_mtop_t*   top,
+                            t_trxframe*         fr,
+                            t_pbc*              pbc)
 {
     data->mp   = mp;
     data->gall = gall;
@@ -350,8 +380,7 @@ _gmx_sel_evaluate_init(gmx_sel_evaluate_t *data,
  *
  * The \ref SEL_EVALFRAME flag is cleared for all elements.
  */
-static void
-init_frame_eval(SelectionTreeElementPointer sel)
+static void init_frame_eval(SelectionTreeElementPointer sel)
 {
     while (sel)
     {
@@ -374,9 +403,7 @@ init_frame_eval(SelectionTreeElementPointer sel)
 namespace gmx
 {
 
-SelectionEvaluator::SelectionEvaluator()
-{
-}
+SelectionEvaluator::SelectionEvaluator() {}
 
 /*!
  * \param[in,out] coll  The selection collection to evaluate.
@@ -392,11 +419,10 @@ SelectionEvaluator::SelectionEvaluator()
  * 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)
+// NOLINTNEXTLINE readability-convert-member-functions-to-static
+void SelectionEvaluator::evaluate(SelectionCollection* coll, t_trxframe* fr, t_pbc* pbc)
 {
-    gmx_ana_selcollection_t *sc = &coll->impl_->sc_;
+    gmx_ana_selcollection_tsc = &coll->impl_->sc_;
     gmx_sel_evaluate_t       data;
 
     _gmx_sel_evaluate_init(&data, sc->mempool, &sc->gall, sc->top, fr, pbc);
@@ -405,8 +431,7 @@ SelectionEvaluator::evaluate(SelectionCollection *coll,
     while (sel)
     {
         /* Clear the evaluation group of subexpressions */
-        if (sel->child && sel->child->type == SEL_SUBEXPR
-            && sel->child->evaluate != NULL)
+        if (sel->child && sel->child->type == SEL_SUBEXPR && sel->child->evaluate != nullptr)
         {
             sel->child->u.cgrp.isize = 0;
             /* Not strictly necessary, because the value will be overwritten
@@ -422,7 +447,7 @@ SelectionEvaluator::evaluate(SelectionCollection *coll,
         }
         if (sel->evaluate)
         {
-            sel->evaluate(&data, sel, NULL);
+            sel->evaluate(&data, sel, nullptr);
         }
         sel = sel->next;
     }
@@ -430,7 +455,7 @@ SelectionEvaluator::evaluate(SelectionCollection *coll,
     SelectionDataList::const_iterator isel;
     for (isel = sc->sel.begin(); isel != sc->sel.end(); ++isel)
     {
-        internal::SelectionData &sel = **isel;
+        internal::SelectionDatasel = **isel;
         sel.refreshMassesAndCharges(sc->top);
         sel.updateCoveredFractionForFrame();
     }
@@ -440,15 +465,15 @@ SelectionEvaluator::evaluate(SelectionCollection *coll,
  * \param[in,out] coll  The selection collection to evaluate.
  * \param[in]     nframes Total number of frames.
  */
-void
-SelectionEvaluator::evaluateFinal(SelectionCollection *coll, int nframes)
+// NOLINTNEXTLINE readability-convert-member-functions-to-static
+void SelectionEvaluator::evaluateFinal(SelectionCollection* coll, int nframes)
 {
-    gmx_ana_selcollection_t          *sc = &coll->impl_->sc_;
+    gmx_ana_selcollection_tsc = &coll->impl_->sc_;
 
     SelectionDataList::const_iterator isel;
     for (isel = sc->sel.begin(); isel != sc->sel.end(); ++isel)
     {
-        internal::SelectionData &sel = **isel;
+        internal::SelectionDatasel = **isel;
         sel.restoreOriginalPositions(sc->top);
         sel.computeAverageCoveredFraction(nframes);
     }
@@ -464,10 +489,9 @@ SelectionEvaluator::evaluateFinal(SelectionCollection *coll, int nframes)
  *
  * 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)
+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)
@@ -480,27 +504,25 @@ _gmx_sel_evaluate_children(gmx_sel_evaluate_t                     *data,
     }
 }
 
-void
-_gmx_sel_evaluate_root(gmx_sel_evaluate_t                     *data,
-                       const gmx::SelectionTreeElementPointer &sel,
-                       gmx_ana_index_t                         * /* g */)
+void _gmx_sel_evaluate_root(gmx_sel_evaluate_t*                     data,
+                            const gmx::SelectionTreeElementPointer& sel,
+                            gmx_ana_index_t* /* g */)
 {
     if (sel->u.cgrp.isize == 0 || !sel->child->evaluate)
     {
         return;
     }
 
-    sel->child->evaluate(data, sel->child,
-                         sel->u.cgrp.isize < 0 ? NULL : &sel->u.cgrp);
+    sel->child->evaluate(data, sel->child, sel->u.cgrp.isize < 0 ? nullptr : &sel->u.cgrp);
 }
 
-void
-_gmx_sel_evaluate_static(gmx_sel_evaluate_t                      * /* data */,
-                         const gmx::SelectionTreeElementPointer &sel,
-                         gmx_ana_index_t                        *g)
+void _gmx_sel_evaluate_static(gmx_sel_evaluate_t* /* data */,
+                              const gmx::SelectionTreeElementPointer& sel,
+                              gmx_ana_index_t*                        g)
 {
     if (sel->flags & SEL_UNSORTED)
     {
+        gmx_ana_index_reserve(sel->v.u.g, sel->u.cgrp.isize);
         // This only works if g contains all the atoms, but that is currently
         // the only supported case.
         gmx_ana_index_copy(sel->v.u.g, &sel->u.cgrp, false);
@@ -530,10 +552,9 @@ _gmx_sel_evaluate_static(gmx_sel_evaluate_t                      * /* data */,
  * \ref SEL_SUBEXPR elements that are used only once, and hence do not need
  * full subexpression handling.
  */
-void
-_gmx_sel_evaluate_subexpr_simple(gmx_sel_evaluate_t                     *data,
-                                 const gmx::SelectionTreeElementPointer &sel,
-                                 gmx_ana_index_t                        *g)
+void _gmx_sel_evaluate_subexpr_simple(gmx_sel_evaluate_t*                     data,
+                                      const gmx::SelectionTreeElementPointer& sel,
+                                      gmx_ana_index_t*                        g)
 {
     if (sel->child->evaluate)
     {
@@ -558,10 +579,9 @@ _gmx_sel_evaluate_subexpr_simple(gmx_sel_evaluate_t                     *data,
  * \ref SEL_SUBEXPR elements that have a static evaluation group, and hence do
  * not need full subexpression handling.
  */
-void
-_gmx_sel_evaluate_subexpr_staticeval(gmx_sel_evaluate_t                     *data,
-                                     const gmx::SelectionTreeElementPointer &sel,
-                                     gmx_ana_index_t                        *g)
+void _gmx_sel_evaluate_subexpr_staticeval(gmx_sel_evaluate_t*                     data,
+                                          const gmx::SelectionTreeElementPointer& sel,
+                                          gmx_ana_index_t*                        g)
 {
     if (sel->u.cgrp.isize == 0)
     {
@@ -598,12 +618,11 @@ _gmx_sel_evaluate_subexpr_staticeval(gmx_sel_evaluate_t                     *dat
  * _gmx_sel_evaluate_subexpr_staticeval() can be used, so this should not be a
  * major problem.
  */
-void
-_gmx_sel_evaluate_subexpr(gmx_sel_evaluate_t                     *data,
-                          const gmx::SelectionTreeElementPointer &sel,
-                          gmx_ana_index_t                        *g)
+void _gmx_sel_evaluate_subexpr(gmx_sel_evaluate_t*                     data,
+                               const gmx::SelectionTreeElementPointer& sel,
+                               gmx_ana_index_t*                        g)
 {
-    gmx_ana_index_t      gmiss;
+    gmx_ana_index_t gmiss;
 
     MempoolGroupReserver gmissreserver(data->mp);
     if (sel->u.cgrp.isize == 0)
@@ -613,7 +632,7 @@ _gmx_sel_evaluate_subexpr(gmx_sel_evaluate_t                     *data,
             sel->child->evaluate(data, sel->child, g);
         }
         gmx_ana_index_copy(&sel->u.cgrp, g, false);
-        gmiss.isize      = 0;
+        gmiss.isize = 0;
     }
     else
     {
@@ -632,7 +651,7 @@ _gmx_sel_evaluate_subexpr(gmx_sel_evaluate_t                     *data,
         }
         else
         {
-            int  i, j, k;
+            int i, j, k;
 
             i = sel->u.cgrp.isize - 1;
             j = gmiss.isize - 1;
@@ -686,11 +705,11 @@ _gmx_sel_evaluate_subexpr(gmx_sel_evaluate_t                     *data,
 
                 case POS_VALUE:
                     /* TODO: Implement this */
-                    GMX_THROW(gmx::NotImplementedError("position subexpressions not implemented properly"));
+                    GMX_THROW(gmx::NotImplementedError(
+                            "position subexpressions not implemented properly"));
 
                 case NO_VALUE:
-                case GROUP_VALUE:
-                    GMX_THROW(gmx::InternalError("Invalid subexpression type"));
+                case GROUP_VALUE: GMX_THROW(gmx::InternalError("Invalid subexpression type"));
             }
         }
         gmx_ana_index_merge(&sel->u.cgrp, &sel->u.cgrp, &gmiss);
@@ -711,16 +730,14 @@ _gmx_sel_evaluate_subexpr(gmx_sel_evaluate_t                     *data,
  * \ref SEL_SUBEXPRREF elements for which the \ref SEL_SUBEXPR does not have
  * other references.
  */
-void
-_gmx_sel_evaluate_subexprref_simple(gmx_sel_evaluate_t                     *data,
-                                    const gmx::SelectionTreeElementPointer &sel,
-                                    gmx_ana_index_t                        *g)
+void _gmx_sel_evaluate_subexprref_simple(gmx_sel_evaluate_t*                     data,
+                                         const gmx::SelectionTreeElementPointer& sel,
+                                         gmx_ana_index_t*                        g)
 {
     if (g)
     {
         _gmx_selvalue_setstore(&sel->child->v, sel->v.u.ptr);
-        _gmx_selvalue_setstore_alloc(&sel->child->child->v, sel->v.u.ptr,
-                                     sel->child->child->v.nalloc);
+        _gmx_selvalue_setstore_alloc(&sel->child->child->v, sel->v.u.ptr, sel->child->child->v.nalloc);
         sel->child->evaluate(data, sel->child, g);
     }
     sel->v.nr = sel->child->v.nr;
@@ -750,25 +767,24 @@ _gmx_sel_evaluate_subexprref_simple(gmx_sel_evaluate_t                     *data
  * This function is used as gmx::SelectionTreeElement::evaluate for
  * \ref SEL_SUBEXPRREF elements.
  */
-void
-_gmx_sel_evaluate_subexprref(gmx_sel_evaluate_t                     *data,
-                             const gmx::SelectionTreeElementPointer &sel,
-                             gmx_ana_index_t                        *g)
+void _gmx_sel_evaluate_subexprref(gmx_sel_evaluate_t*                     data,
+                                  const gmx::SelectionTreeElementPointer& sel,
+                                  gmx_ana_index_t*                        g)
 {
-    int        i, j;
+    int i, j;
 
-    if (g != NULL && sel->child->evaluate != NULL)
+    if (g != nullptr && sel->child->evaluate != nullptr)
     {
         sel->child->evaluate(data, sel->child, g);
     }
-    const SelectionTreeElementPointer &expr = sel->child;
+    const SelectionTreeElementPointerexpr = sel->child;
     switch (sel->v.type)
     {
         case INT_VALUE:
             if (!g)
             {
                 sel->v.nr = expr->v.nr;
-                memcpy(sel->v.u.i, expr->v.u.i, sel->v.nr*sizeof(*sel->v.u.i));
+                memcpy(sel->v.u.i, expr->v.u.i, sel->v.nr * sizeof(*sel->v.u.i));
             }
             else
             {
@@ -789,7 +805,7 @@ _gmx_sel_evaluate_subexprref(gmx_sel_evaluate_t                     *data,
             if (!g)
             {
                 sel->v.nr = expr->v.nr;
-                memcpy(sel->v.u.r, expr->v.u.r, sel->v.nr*sizeof(*sel->v.u.r));
+                memcpy(sel->v.u.r, expr->v.u.r, sel->v.nr * sizeof(*sel->v.u.r));
             }
             else
             {
@@ -810,7 +826,7 @@ _gmx_sel_evaluate_subexprref(gmx_sel_evaluate_t                     *data,
             if (!g)
             {
                 sel->v.nr = expr->v.nr;
-                memcpy(sel->v.u.s, expr->v.u.s, sel->v.nr*sizeof(*sel->v.u.s));
+                memcpy(sel->v.u.s, expr->v.u.s, sel->v.nr * sizeof(*sel->v.u.s));
             }
             else
             {
@@ -875,10 +891,9 @@ _gmx_sel_evaluate_subexprref(gmx_sel_evaluate_t                     *data,
  * 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)
+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)
@@ -892,7 +907,7 @@ _gmx_sel_evaluate_method_params(gmx_sel_evaluate_t                     *data,
             else
             {
                 child->flags |= SEL_EVALFRAME;
-                child->evaluate(data, child, NULL);
+                child->evaluate(data, child, nullptr);
             }
         }
         child = child->next;
@@ -916,30 +931,36 @@ _gmx_sel_evaluate_method_params(gmx_sel_evaluate_t                     *data,
  * This function is used as gmx::SelectionTreeElement::evaluate for
  * \ref SEL_EXPRESSION elements.
  */
-void
-_gmx_sel_evaluate_method(gmx_sel_evaluate_t                     *data,
-                         const gmx::SelectionTreeElementPointer &sel,
-                         gmx_ana_index_t                        *g)
+void _gmx_sel_evaluate_method(gmx_sel_evaluate_t*                     data,
+                              const gmx::SelectionTreeElementPointer& sel,
+                              gmx_ana_index_t*                        g)
 {
     _gmx_sel_evaluate_method_params(data, sel, g);
+    gmx::SelMethodEvalContext context(data->top, data->fr, data->pbc);
     if (sel->flags & SEL_INITFRAME)
     {
         sel->flags &= ~SEL_INITFRAME;
-        sel->u.expr.method->init_frame(data->top, data->fr, data->pbc,
-                                       sel->u.expr.mdata);
+        sel->u.expr.method->init_frame(context, sel->u.expr.mdata);
     }
     if (sel->u.expr.pc)
     {
-        gmx_ana_poscalc_update(sel->u.expr.pc, sel->u.expr.pos, g,
-                               data->fr, data->pbc);
-        sel->u.expr.method->pupdate(data->top, data->fr, data->pbc,
-                                    sel->u.expr.pos, &sel->v,
-                                    sel->u.expr.mdata);
+        gmx_ana_poscalc_update(sel->u.expr.pc, sel->u.expr.pos, g, data->fr, data->pbc);
+        sel->u.expr.method->pupdate(context, sel->u.expr.pos, &sel->v, sel->u.expr.mdata);
+        if ((sel->flags & SEL_ATOMVAL) && sel->v.nr < g->isize)
+        {
+            switch (sel->v.type)
+            {
+                case REAL_VALUE:
+                    expandValueForPositions(sel->v.u.r, &sel->v.nr, sel->u.expr.pos);
+                    break;
+                default:
+                    GMX_RELEASE_ASSERT(false, "Unimplemented value type for position update method");
+            }
+        }
     }
     else
     {
-        sel->u.expr.method->update(data->top, data->fr, data->pbc, g,
-                                   &sel->v, sel->u.expr.mdata);
+        sel->u.expr.method->update(context, g, &sel->v, sel->u.expr.mdata);
     }
 }
 
@@ -958,27 +979,22 @@ _gmx_sel_evaluate_method(gmx_sel_evaluate_t                     *data,
  * This function is used as gmx::SelectionTreeElement::evaluate for
  * \ref SEL_MODIFIER elements.
  */
-void
-_gmx_sel_evaluate_modifier(gmx_sel_evaluate_t                     *data,
-                           const gmx::SelectionTreeElementPointer &sel,
-                           gmx_ana_index_t                        *g)
+void _gmx_sel_evaluate_modifier(gmx_sel_evaluate_t*                     data,
+                                const gmx::SelectionTreeElementPointer& sel,
+                                gmx_ana_index_t*                        g)
 {
     _gmx_sel_evaluate_method_params(data, sel, g);
+    gmx::SelMethodEvalContext context(data->top, data->fr, data->pbc);
     if (sel->flags & SEL_INITFRAME)
     {
         sel->flags &= ~SEL_INITFRAME;
-        sel->u.expr.method->init_frame(data->top, data->fr, data->pbc,
-                                       sel->u.expr.mdata);
+        sel->u.expr.method->init_frame(context, sel->u.expr.mdata);
     }
-    GMX_RELEASE_ASSERT(sel->child != NULL,
-                       "Modifier element with a value must have a child");
-    if (sel->child->v.type != POS_VALUE)
+    if (sel->child && sel->child->v.type != POS_VALUE)
     {
         GMX_THROW(gmx::NotImplementedError("Non-position valued modifiers not implemented"));
     }
-    sel->u.expr.method->pupdate(data->top, data->fr, data->pbc,
-                                sel->child->v.u.p,
-                                &sel->v, sel->u.expr.mdata);
+    sel->u.expr.method->pupdate(context, nullptr, &sel->v, sel->u.expr.mdata);
 }
 
 
@@ -999,10 +1015,9 @@ _gmx_sel_evaluate_modifier(gmx_sel_evaluate_t                     *data,
  * This function is used as gmx::SelectionTreeElement::evaluate for
  * \ref SEL_BOOLEAN elements with \ref BOOL_NOT.
  */
-void
-_gmx_sel_evaluate_not(gmx_sel_evaluate_t                     *data,
-                      const gmx::SelectionTreeElementPointer &sel,
-                      gmx_ana_index_t                        *g)
+void _gmx_sel_evaluate_not(gmx_sel_evaluate_t*                     data,
+                           const gmx::SelectionTreeElementPointer& sel,
+                           gmx_ana_index_t*                        g)
 {
     MempoolSelelemReserver reserver(sel->child, g->isize);
     sel->child->evaluate(data, sel->child, g);
@@ -1034,10 +1049,9 @@ _gmx_sel_evaluate_not(gmx_sel_evaluate_t                     *data,
  * 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)
+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. */
@@ -1087,12 +1101,11 @@ _gmx_sel_evaluate_and(gmx_sel_evaluate_t                     *data,
  * 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)
+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;
+    gmx_ana_index_t tmp, tmp2;
 
     SelectionTreeElementPointer child = sel->child;
     if (child->evaluate)
@@ -1114,9 +1127,9 @@ _gmx_sel_evaluate_or(gmx_sel_evaluate_t                     *data,
             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;
+        tmp.isize = tmp2.isize;
+        tmp.index = tmp2.index;
+        child     = child->next;
     }
     gmx_ana_index_sort(sel->v.u.g);
 }
@@ -1132,19 +1145,18 @@ _gmx_sel_evaluate_or(gmx_sel_evaluate_t                     *data,
  * \param[in] g    Group for which \p sel should be evaluated.
  * \returns   0 on success, a non-zero error code on error.
  */
-void
-_gmx_sel_evaluate_arithmetic(gmx_sel_evaluate_t                     *data,
-                             const gmx::SelectionTreeElementPointer &sel,
-                             gmx_ana_index_t                        *g)
+void _gmx_sel_evaluate_arithmetic(gmx_sel_evaluate_t*                     data,
+                                  const gmx::SelectionTreeElementPointer& sel,
+                                  gmx_ana_index_t*                        g)
 {
-    int         n, i, i1, i2;
-    real        lval, rval = 0., val = 0.;
+    int  n, i, i1, i2;
+    real lval, rval = 0., val = 0.;
 
-    const SelectionTreeElementPointer &left  = sel->child;
-    const SelectionTreeElementPointer &right = left->next;
+    const SelectionTreeElementPointerleft  = sel->child;
+    const SelectionTreeElementPointerright = left->next;
 
-    SelelemTemporaryValueAssigner      assigner;
-    MempoolSelelemReserver             reserver;
+    SelelemTemporaryValueAssigner assigner;
+    MempoolSelelemReserver        reserver;
     if (left->mempool)
     {
         assigner.assign(left, *sel);
@@ -1163,8 +1175,7 @@ _gmx_sel_evaluate_arithmetic(gmx_sel_evaluate_t                     *data,
     sel->v.nr = n;
 
     bool bArithNeg = (sel->u.arith.type == ARITH_NEG);
-    GMX_ASSERT(right || bArithNeg,
-               "Right operand cannot be null except for negations");
+    GMX_ASSERT(right || bArithNeg, "Right operand cannot be null except for negations");
     for (i = i1 = i2 = 0; i < n; ++i)
     {
         lval = left->v.u.r[i1];
@@ -1174,12 +1185,12 @@ _gmx_sel_evaluate_arithmetic(gmx_sel_evaluate_t                     *data,
         }
         switch (sel->u.arith.type)
         {
-            case ARITH_PLUS:    val = lval + rval;     break;
-            case ARITH_MINUS:   val = lval - rval;     break;
-            case ARITH_NEG:     val = -lval;           break;
-            case ARITH_MULT:    val = lval * rval;     break;
-            case ARITH_DIV:     val = lval / rval;     break;
-            case ARITH_EXP:     val = pow(lval, rval); break;
+            case ARITH_PLUS: val = lval + rval; break;
+            case ARITH_MINUS: val = lval - rval; break;
+            case ARITH_NEG: val = -lval; break;
+            case ARITH_MULT: val = lval * rval; break;
+            case ARITH_DIV: val = lval / rval; break;
+            case ARITH_EXP: val = pow(lval, rval); break;
         }
         sel->v.u.r[i] = val;
         if (!(left->flags & SEL_SINGLEVAL))