Simplify and fix how lambda values are set
authorChristian Blau <cblau.mail@gmail.com>
Thu, 10 Sep 2020 19:04:49 +0000 (19:04 +0000)
committerJoe Jordan <ejjordan12@gmail.com>
Thu, 10 Sep 2020 19:04:49 +0000 (19:04 +0000)
Simplified setCurrentLambdasLocal method to return an array of
current lambda points. Removed the faulty use of lam0 as initial
lambdas.

15 files changed:
src/gromacs/gmxana/gmx_bar.cpp
src/gromacs/gmxpreprocess/readir.cpp
src/gromacs/mdlib/freeenergyparameters.cpp [new file with mode: 0644]
src/gromacs/mdlib/freeenergyparameters.h [new file with mode: 0644]
src/gromacs/mdlib/md_support.cpp
src/gromacs/mdlib/md_support.h
src/gromacs/mdlib/tests/CMakeLists.txt
src/gromacs/mdlib/tests/freeenergyparameters.cpp [new file with mode: 0644]
src/gromacs/mdrun/md.cpp
src/gromacs/mdrun/mimic.cpp
src/gromacs/mdrun/minimize.cpp
src/gromacs/mdrun/rerun.cpp
src/gromacs/mdtypes/state.cpp
src/gromacs/mdtypes/state.h
src/gromacs/modularsimulator/freeenergyperturbationdata.cpp

index 601717df3a5d96b841549bc05f4d8d9db4f77d1a..04c9dda231201530f19d03dc4f78e3730e915000 100644 (file)
@@ -3079,7 +3079,7 @@ static void read_barsim_edr(const char* fn, real* temp, sim_data_t* sd)
                 old_start_lambda = fr->block[i].sub[0].dval[3];
                 delta_lambda     = fr->block[i].sub[0].dval[4];
 
-                if (delta_lambda > 0)
+                if (delta_lambda != 0)
                 {
                     gmx_fatal(FARGS, "Lambda values not constant in %s: can't apply BAR method", fn);
                 }
index db9cba464eaed759a9b128825d70a27c5cab93c5..6aa80244ba21b16b5c8c3ed205b537a1329b07a2 100644 (file)
@@ -2487,7 +2487,7 @@ void get_ir(const char*     mdparin,
     /* FREE ENERGY AND EXPANDED ENSEMBLE OPTIONS */
     if (ir->efep != efepNO)
     {
-        if (fep->delta_lambda > 0)
+        if (fep->delta_lambda != 0)
         {
             ir->efep = efepSLOWGROWTH;
         }
diff --git a/src/gromacs/mdlib/freeenergyparameters.cpp b/src/gromacs/mdlib/freeenergyparameters.cpp
new file mode 100644 (file)
index 0000000..ff47b98
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, 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.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Implements routines in freeenergyparameters.h .
+ *
+ * \author Christian Blau <blau@kth.se>
+ */
+
+#include "gmxpre.h"
+
+#include "freeenergyparameters.h"
+
+#include "gromacs/mdtypes/inputrec.h"
+
+namespace gmx
+{
+
+namespace
+{
+
+std::array<real, efptNR> lambdasAtState(const int stateIndex, double** const lambdaArray, const int lambdaArrayExtent)
+{
+    std::array<real, efptNR> lambda;
+    // set lambda from an fep state index from stateIndex, if stateIndex was defined (> -1)
+    if (stateIndex >= 0 && stateIndex < lambdaArrayExtent)
+    {
+        for (int i = 0; i < efptNR; i++)
+        {
+            lambda[i] = lambdaArray[i][stateIndex];
+        }
+    }
+    return lambda;
+}
+
+/*! \brief Evaluates where in the lambda arrays we are at currently.
+ *
+ * \param[in] step the current simulation step
+ * \param[in] deltaLambdaPerStep the change of the overall controlling lambda parameter per step
+ * \param[in] initialFEPStateIndex the FEP state at the start of the simulation. -1 if not set.
+ * \param[in] initialLambda the lambda at the start of the simulation . -1 if not set.
+ * \param[in] lambdaArrayExtent how many lambda values we have
+ * \returns a number that reflects where in the lambda arrays we are at the moment
+ */
+double currentGlobalLambda(const int64_t step,
+                           const double  deltaLambdaPerStep,
+                           const int     initialFEPStateIndex,
+                           const double  initialLambda,
+                           const int     lambdaArrayExtent)
+{
+    const real fracSimulationLambda = step * deltaLambdaPerStep;
+
+    // Set initial lambda value for the simulation either from initialFEPStateIndex or,
+    // if not set, from the initial lambda.
+    double initialGlobalLambda = 0;
+    if (initialFEPStateIndex > -1)
+    {
+        if (lambdaArrayExtent > 1)
+        {
+            initialGlobalLambda = static_cast<double>(initialFEPStateIndex) / (lambdaArrayExtent - 1);
+        }
+    }
+    else
+    {
+        if (initialLambda > -1)
+        {
+            initialGlobalLambda = initialLambda;
+        }
+    }
+
+    return initialGlobalLambda + fracSimulationLambda;
+}
+
+/*! \brief Returns an array of lambda values from linear interpolation of a lambda value matrix.
+ *
+ * \note If there is nothing to interpolate, fills the array with the global current lambda.
+ * \note Returns array boundary values if currentGlobalLambda <0 or >1 .
+ *
+ * \param[in] currentGlobalLambda determines at which position in the lambda array to interpolate
+ * \param[in] lambdaArray array of lambda values
+ * \param[in] lambdaArrayExtent number of lambda values
+ */
+std::array<real, efptNR> interpolatedLambdas(const double   currentGlobalLambda,
+                                             double** const lambdaArray,
+                                             const int      lambdaArrayExtent)
+{
+    std::array<real, efptNR> lambda;
+    // when there is no lambda value array, set all lambdas to steps * deltaLambdaPerStep
+    if (lambdaArrayExtent <= 0)
+    {
+        std::fill(std::begin(lambda), std::end(lambda), currentGlobalLambda);
+        return lambda;
+    }
+
+    // if we run over the boundary of the lambda array, return the boundary array values
+    if (currentGlobalLambda <= 0)
+    {
+        for (int i = 0; i < efptNR; i++)
+        {
+            lambda[i] = lambdaArray[i][0];
+        }
+        return lambda;
+    }
+    if (currentGlobalLambda >= 1)
+    {
+        for (int i = 0; i < efptNR; i++)
+        {
+            lambda[i] = lambdaArray[i][lambdaArrayExtent - 1];
+        }
+        return lambda;
+    }
+
+    // find out between which two value lambda array elements to interpolate
+    const int fepStateLeft = static_cast<int>(std::floor(currentGlobalLambda * (lambdaArrayExtent - 1)));
+    const int fepStateRight = fepStateLeft + 1;
+    // interpolate between this state and the next
+    const double fracBetween = currentGlobalLambda * (lambdaArrayExtent - 1) - fepStateLeft;
+    for (int i = 0; i < efptNR; i++)
+    {
+        lambda[i] = lambdaArray[i][fepStateLeft]
+                    + fracBetween * (lambdaArray[i][fepStateRight] - lambdaArray[i][fepStateLeft]);
+    }
+    return lambda;
+}
+
+} // namespace
+
+std::array<real, efptNR> currentLambdas(const int64_t step, const t_lambda& fepvals, const int currentLambdaState)
+{
+    if (fepvals.delta_lambda == 0)
+    {
+        if (currentLambdaState > -1)
+        {
+            return lambdasAtState(currentLambdaState, fepvals.all_lambda, fepvals.n_lambda);
+        }
+
+        if (fepvals.init_fep_state > -1)
+        {
+            return lambdasAtState(fepvals.init_fep_state, fepvals.all_lambda, fepvals.n_lambda);
+        }
+
+        std::array<real, efptNR> lambdas;
+        std::fill(std::begin(lambdas), std::end(lambdas), fepvals.init_lambda);
+        return lambdas;
+    }
+    const double globalLambda = currentGlobalLambda(step, fepvals.delta_lambda, fepvals.init_fep_state,
+                                                    fepvals.init_lambda, fepvals.n_lambda);
+    return interpolatedLambdas(globalLambda, fepvals.all_lambda, fepvals.n_lambda);
+}
+
+} // namespace gmx
diff --git a/src/gromacs/mdlib/freeenergyparameters.h b/src/gromacs/mdlib/freeenergyparameters.h
new file mode 100644 (file)
index 0000000..0814bc6
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, 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.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Handling of free energy parameters
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_mdlib
+ */
+
+#ifndef GMX_MDLIB_FREEENERGYPARAMETERS_H
+#define GMX_MDLIB_FREEENERGYPARAMETERS_H
+
+#include <array>
+
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/real.h"
+
+struct t_lambda;
+
+namespace gmx
+{
+
+/*! \brief Evaluate the current lambdas
+ *
+ * \param[in] step the current simulation step
+ * \param[in] fepvals describing the lambda setup
+ * \param[in] currentLambdaState the lambda state to use to set the lambdas, -1 if not set
+ * \returns the current lambda-value array
+ */
+std::array<real, efptNR> currentLambdas(int64_t step, const t_lambda& fepvals, int currentLambdaState);
+
+} // namespace gmx
+
+#endif
index 26c19efa166ccea033ae2885c5585bacb070f485..7e301f6bf752816130cc0534d36676beb7315003 100644 (file)
@@ -426,92 +426,6 @@ void compute_globals(gmx_global_stat*               gstat,
     }
 }
 
-void setCurrentLambdasRerun(int64_t           step,
-                            const t_lambda*   fepvals,
-                            const t_trxframe* rerun_fr,
-                            const double*     lam0,
-                            t_state*          globalState)
-{
-    GMX_RELEASE_ASSERT(globalState != nullptr,
-                       "setCurrentLambdasGlobalRerun should be called with a valid state object");
-
-    if (rerun_fr->bLambda)
-    {
-        if (fepvals->delta_lambda == 0)
-        {
-            globalState->lambda[efptFEP] = rerun_fr->lambda;
-        }
-        else
-        {
-            /* find out between which two value of lambda we should be */
-            real frac      = step * fepvals->delta_lambda;
-            int  fep_state = static_cast<int>(std::floor(frac * fepvals->n_lambda));
-            /* interpolate between this state and the next */
-            /* this assumes that the initial lambda corresponds to lambda==0, which is verified in grompp */
-            frac = frac * fepvals->n_lambda - fep_state;
-            for (int i = 0; i < efptNR; i++)
-            {
-                globalState->lambda[i] =
-                        lam0[i] + (fepvals->all_lambda[i][fep_state])
-                        + frac * (fepvals->all_lambda[i][fep_state + 1] - fepvals->all_lambda[i][fep_state]);
-            }
-        }
-    }
-    else if (rerun_fr->bFepState)
-    {
-        globalState->fep_state = rerun_fr->fep_state;
-        for (int i = 0; i < efptNR; i++)
-        {
-            globalState->lambda[i] = fepvals->all_lambda[i][globalState->fep_state];
-        }
-    }
-}
-
-void setCurrentLambdasLocal(const int64_t       step,
-                            const t_lambda*     fepvals,
-                            const double*       lam0,
-                            gmx::ArrayRef<real> lambda,
-                            const int           currentFEPState)
-/* find the current lambdas.  If rerunning, we either read in a state, or a lambda value,
-   requiring different logic. */
-{
-    if (fepvals->delta_lambda != 0)
-    {
-        /* find out between which two value of lambda we should be */
-        real frac = step * fepvals->delta_lambda;
-        if (fepvals->n_lambda > 0)
-        {
-            int fep_state = static_cast<int>(std::floor(frac * fepvals->n_lambda));
-            /* interpolate between this state and the next */
-            /* this assumes that the initial lambda corresponds to lambda==0, which is verified in grompp */
-            frac = frac * fepvals->n_lambda - fep_state;
-            for (int i = 0; i < efptNR; i++)
-            {
-                lambda[i] = lam0[i] + (fepvals->all_lambda[i][fep_state])
-                            + frac * (fepvals->all_lambda[i][fep_state + 1] - fepvals->all_lambda[i][fep_state]);
-            }
-        }
-        else
-        {
-            for (int i = 0; i < efptNR; i++)
-            {
-                lambda[i] = lam0[i] + frac;
-            }
-        }
-    }
-    else
-    {
-        /* if < 0, fep_state was never defined, and we should not set lambda from the state */
-        if (currentFEPState > -1)
-        {
-            for (int i = 0; i < efptNR; i++)
-            {
-                lambda[i] = fepvals->all_lambda[i][currentFEPState];
-            }
-        }
-    }
-}
-
 static void min_zero(int* n, int i)
 {
     if (i > 0 && (*n == 0 || i < *n))
index de69061929b2c4642e4e9b35c73f976675f9816f..3544c6f14f68f1fef79e838f4f3eee72291fcb74 100644 (file)
@@ -39,6 +39,7 @@
 #define GMX_MDLIB_MD_SUPPORT_H
 
 #include "gromacs/mdlib/vcm.h"
+#include "gromacs/mdtypes/md_enums.h"
 #include "gromacs/timing/wallcycle.h"
 
 struct gmx_ekindata_t;
@@ -49,7 +50,6 @@ struct t_extmass;
 struct t_forcerec;
 struct t_grpopts;
 struct t_inputrec;
-struct t_lambda;
 struct t_nrnb;
 class t_state;
 struct t_trxframe;
@@ -101,21 +101,6 @@ void rerun_parallel_comm(t_commrec* cr, t_trxframe* fr, gmx_bool* bLastStep);
 //! \brief Allocate and initialize node-local state entries
 void set_state_entries(t_state* state, const t_inputrec* ir, bool useModularSimulator);
 
-/* Set the lambda values in the global state from a frame read with rerun */
-void setCurrentLambdasRerun(int64_t           step,
-                            const t_lambda*   fepvals,
-                            const t_trxframe* rerun_fr,
-                            const double*     lam0,
-                            t_state*          globalState);
-
-/* Set the lambda values at each step of mdrun when they change */
-void setCurrentLambdasLocal(int64_t             step,
-                            const t_lambda*     fepvals,
-                            const double*       lam0,
-                            gmx::ArrayRef<real> lambda,
-                            int                 currentFEPState);
-
-
 /* Compute global variables during integration
  *
  * Coordinates x are needed for kinetic energy calculation with cosine accelation
index 12cdd137e2a402877e412f478e66214746940a4a..ab92f50785dd2ae6e03a2c38b97deaecb93ab1ab 100644 (file)
@@ -40,6 +40,7 @@ gmx_add_unit_test(MdlibUnitTest mdlib-test
         constrtestrunners.cpp
         ebin.cpp
         energyoutput.cpp
+        freeenergyparameters.cpp
         leapfrog.cpp
         leapfrogtestdata.cpp
         leapfrogtestrunners.cpp
diff --git a/src/gromacs/mdlib/tests/freeenergyparameters.cpp b/src/gromacs/mdlib/tests/freeenergyparameters.cpp
new file mode 100644 (file)
index 0000000..a6ea796
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, 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.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Tests routines in freeenergyparameters.h .
+ *
+ * \author Christian Blau <blau@kth.se>
+ */
+
+#include "gmxpre.h"
+
+#include "gromacs/mdlib/freeenergyparameters.h"
+
+#include <gtest/gtest.h>
+
+#include "gromacs/mdtypes/inputrec.h"
+
+#include "testutils/testasserts.h"
+#include "testutils/testmatchers.h"
+
+
+namespace gmx
+{
+namespace test
+{
+namespace
+{
+
+
+/*! \brief Parameters that will vary from test to test.
+ */
+struct FreeEnergyParameterTestParameters
+{
+    //! current state of lambda in the simulation, -1 if not set
+    int currentLambdaState = -1;
+    //! Fractional value of lambda to start from, -1 if not set
+    double initLambda = -1;
+    //! The initial number of the state, -1 if not set
+    int initFepState = -1;
+    //! Change of lambda per time step (fraction of (0.1)
+    double deltaLambda = 0;
+    //! number of lambda entries
+    int nLambda = 0;
+    //! the current simulation step
+    int64_t step = 0;
+    //! the expected lambda at the current simulation step
+    std::array<real, efptNR> expectedLambdas = { -1, -1, -1, -1, -1, -1, -1 };
+};
+
+
+/*! \brief Sets of parameters on which to run the tests.
+ */
+const FreeEnergyParameterTestParameters freeEnergyParameterSets[] = {
+    // no parameters set at all
+    { -1, -1, -1, 0, 1, 0, { -1, -1, -1, -1, -1, -1, -1 } },
+    // setting current lambda state to 0, no other variables set, using eftpNR * [0.8] as lambda state matrix
+    { 0, -1, -1, 0, 1, 1, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    // setting current lambda state to 1, no other variables set, using eftpNR * [0.2,0.8] as lambda state matrix
+    { 1, -1, -1, 0, 2, 1, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    // test that non-zero deltaLambda trumps current lambda state
+    // setting current lambda state to 1, using deltaLambda 0.1 and setp 0 and eftpNR * [0.2,0.8] as lambda state matrix
+    { 1, -1, -1, 0.1, 2, 0, { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2 } },
+    // test that interpolating lambda values starting
+    // from lambda = 0, deltaLambda = 0.1, step = 10 results in values at end-state
+    { 1, 0, -1, 0.1, 2, 10, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    // interpolating half the way
+    { 1, 0, -1, 0.1, 2, 5, { 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 } },
+    // starting from end-state 1 and move lambda half-way with negative deltaLambda = -0.1
+    { -1, -1, 1, -0.1, 2, 5, { 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 } },
+    // starting from end-state 1 and move one step backwards with negative deltaLambda = -0.1
+    { -1, -1, 1, -0.1, 2, 1, { 0.74, 0.74, 0.74, 0.74, 0.74, 0.74, 0.74 } },
+    // three lambda states, the last two equal ([0.2,0.8,0.8]), move forward with deltaLambda = 0.1
+    { -1, -1, 0, 0.1, 3, 0, { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2 } },
+    { -1, -1, 0, 0.1, 3, 3, { 0.56, 0.56, 0.56, 0.56, 0.56, 0.56, 0.56 } },
+    { -1, -1, 0, 0.1, 3, 7, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    { -1, -1, 0, 0.1, 3, 8, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    { -1, -1, 0, 0.1, 3, 10, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    // three lambda states, the last two equal ([0.2,0.8,0.8]), move backwards
+    { -1, -1, 2, -0.1, 3, 1, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    { -1, -1, 2, -0.1, 3, 2, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    { -1, -1, 2, -0.1, 3, 3, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    { -1, -1, 2, -0.1, 3, 5, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    { -1, -1, 2, -0.1, 3, 7, { 0.56, 0.56, 0.56, 0.56, 0.56, 0.56, 0.56 } },
+    { -1, -1, 2, -0.1, 3, 8, { 0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44 } },
+    { -1, -1, 2, -0.1, 3, 10, { 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2 } },
+    // three lambda states, the last two equal ([0.2,0.8,0.8]), start in middle state, move backwards
+    { -1, -1, 1, -0.1, 3, 0, { 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8 } },
+    { -1, -1, 1, -0.1, 3, 2, { 0.56, 0.56, 0.56, 0.56, 0.56, 0.56, 0.56 } },
+    { -1, -1, 1, -0.1, 3, 3, { 0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44 } },
+};
+
+/*! \brief Test for setting free energy parameters.
+ */
+class FreeEnergyParameterTest : public ::testing::TestWithParam<FreeEnergyParameterTestParameters>
+{
+public:
+    //! Fills in the required FEP values for testing from the test parameters
+    t_lambda getFepVals()
+    {
+        t_lambda fepvals;
+        fepvals.init_fep_state = GetParam().initFepState;
+        fepvals.init_lambda    = GetParam().initLambda;
+        fepvals.delta_lambda   = GetParam().deltaLambda;
+        fepvals.all_lambda     = getLambdaMatrix(GetParam().nLambda);
+        fepvals.n_lambda       = GetParam().nLambda;
+        return fepvals;
+    }
+
+    /*! \brief construct a lambda matrix
+     *
+     * \param[in] nLambda
+     * \returns nLambda * eftpNR matrix with pre-defined values
+     */
+    double** getLambdaMatrix(int nLambda)
+    {
+        for (int i = 0; i < efptNR; ++i)
+        {
+            allLambda_[i] = defaultLambdaArrayForTest_[nLambda].data();
+        }
+        return allLambda_.data();
+    }
+
+private:
+    //! Construction aide for double ** matrix without snew
+    std::array<double*, efptNR> allLambda_;
+    //! a set of default lambda arrays for different lengths
+    std::vector<std::vector<double>> defaultLambdaArrayForTest_ = { {}, { 0.8 }, { 0.2, 0.8 }, { 0.2, 0.8, 0.8 } };
+};
+
+TEST_P(FreeEnergyParameterTest, CorrectLambdas)
+{
+    EXPECT_THAT(GetParam().expectedLambdas,
+                Pointwise(RealEq(defaultRealTolerance()),
+                          currentLambdas(GetParam().step, getFepVals(), GetParam().currentLambdaState)));
+}
+
+INSTANTIATE_TEST_CASE_P(WithParameters, FreeEnergyParameterTest, ::testing::ValuesIn(freeEnergyParameterSets));
+
+} // namespace
+
+} // namespace test
+
+} // namespace gmx
index 03f95e679732b88307517e9dfa42529c4cda34f1..9d89d6541be9601cfcfefa88226138aed396a516 100644 (file)
@@ -86,6 +86,7 @@
 #include "gromacs/mdlib/force.h"
 #include "gromacs/mdlib/force_flags.h"
 #include "gromacs/mdlib/forcerec.h"
+#include "gromacs/mdlib/freeenergyparameters.h"
 #include "gromacs/mdlib/md_support.h"
 #include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdlib/mdoutf.h"
@@ -164,7 +165,7 @@ void gmx::LegacySimulator::do_md()
     // will go away eventually.
     t_inputrec*  ir = inputrec;
     int64_t      step, step_rel;
-    double       t, t0 = ir->init_t, lam0[efptNR];
+    double       t, t0 = ir->init_t;
     gmx_bool     bGStatEveryStep, bGStat, bCalcVir, bCalcEnerStep, bCalcEner;
     gmx_bool     bNS = FALSE, bNStList, bStopCM, bFirstStep, bInitStep, bLastStep = FALSE;
     gmx_bool     bDoDHDL = FALSE, bDoFEP = FALSE, bDoExpanded = FALSE;
@@ -255,7 +256,7 @@ void gmx::LegacySimulator::do_md()
 
     int*                fep_state = MASTER(cr) ? &state_global->fep_state : nullptr;
     gmx::ArrayRef<real> lambda    = MASTER(cr) ? state_global->lambda : gmx::ArrayRef<real>();
-    initialize_lambdas(fplog, *ir, MASTER(cr), fep_state, lambda, lam0);
+    initialize_lambdas(fplog, *ir, MASTER(cr), fep_state, lambda);
     Update     upd(*ir, deform);
     const bool doSimulatedAnnealing = initSimulatedAnnealing(ir, &upd);
     const bool useReplicaExchange   = (replExParams.exchangeInterval > 0);
@@ -760,7 +761,7 @@ void gmx::LegacySimulator::do_md()
         if (ir->efep != efepNO || ir->bSimTemp)
         {
             /* find and set the current lambdas */
-            setCurrentLambdasLocal(step, ir->fepvals, lam0, state->lambda, state->fep_state);
+            state->lambda = currentLambdas(step, *(ir->fepvals), state->fep_state);
 
             bDoDHDL     = do_per_step(step, ir->fepvals->nstdhdl);
             bDoFEP      = ((ir->efep != efepNO) && do_per_step(step, nstfep));
index eab7080e7571d9c5ac3e9ce169d98acaef3de778..d232621817b95097b149d488279b05d81ea9b398 100644 (file)
@@ -81,6 +81,7 @@
 #include "gromacs/mdlib/force.h"
 #include "gromacs/mdlib/force_flags.h"
 #include "gromacs/mdlib/forcerec.h"
+#include "gromacs/mdlib/freeenergyparameters.h"
 #include "gromacs/mdlib/md_support.h"
 #include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdlib/mdoutf.h"
@@ -142,7 +143,7 @@ void gmx::LegacySimulator::do_mimic()
 {
     t_inputrec*       ir = inputrec;
     int64_t           step, step_rel;
-    double            t, lam0[efptNR];
+    double            t;
     bool              isLastStep               = false;
     bool              doFreeEnergyPerturbation = false;
     unsigned int      force_flags;
@@ -223,7 +224,7 @@ void gmx::LegacySimulator::do_mimic()
         nonConstGlobalTopology->intermolecularExclusionGroup = genQmmmIndices(*top_global);
     }
 
-    initialize_lambdas(fplog, *ir, MASTER(cr), &state_global->fep_state, state_global->lambda, lam0);
+    initialize_lambdas(fplog, *ir, MASTER(cr), &state_global->fep_state, state_global->lambda);
 
     const bool        simulationsShareState = false;
     gmx_mdoutf*       outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
@@ -371,7 +372,7 @@ void gmx::LegacySimulator::do_mimic()
 
         if (ir->efep != efepNO)
         {
-            setCurrentLambdasLocal(step, ir->fepvals, lam0, state->lambda, state->fep_state);
+            state->lambda = currentLambdas(step, *(ir->fepvals), state_global->fep_state);
         }
 
         if (MASTER(cr))
index 9c2b75760f55f502de822d36e24ff4eca47fa5e1..f03445b605f941ec857b86e0212d4f04fed4f7c1 100644 (file)
@@ -393,7 +393,7 @@ static void init_em(FILE*                fplog,
     }
     int*                fep_state = MASTER(cr) ? &state_global->fep_state : nullptr;
     gmx::ArrayRef<real> lambda    = MASTER(cr) ? state_global->lambda : gmx::ArrayRef<real>();
-    initialize_lambdas(fplog, *ir, MASTER(cr), fep_state, lambda, nullptr);
+    initialize_lambdas(fplog, *ir, MASTER(cr), fep_state, lambda);
 
     if (ir->eI == eiNM)
     {
index 9b7dcf22265bc528ff045063b09a04e74c9d463a..1f0f7d40cebb1b7dab411b3ed880eb3d2b263393 100644 (file)
@@ -80,6 +80,7 @@
 #include "gromacs/mdlib/force.h"
 #include "gromacs/mdlib/force_flags.h"
 #include "gromacs/mdlib/forcerec.h"
+#include "gromacs/mdlib/freeenergyparameters.h"
 #include "gromacs/mdlib/md_support.h"
 #include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdlib/mdoutf.h"
@@ -174,7 +175,7 @@ void gmx::LegacySimulator::do_rerun()
     // will go away eventually.
     t_inputrec*       ir = inputrec;
     int64_t           step, step_rel;
-    double            t, lam0[efptNR];
+    double            t;
     bool              isLastStep               = false;
     bool              doFreeEnergyPerturbation = false;
     unsigned int      force_flags;
@@ -280,7 +281,7 @@ void gmx::LegacySimulator::do_rerun()
     }
     int*                fep_state = MASTER(cr) ? &state_global->fep_state : nullptr;
     gmx::ArrayRef<real> lambda    = MASTER(cr) ? state_global->lambda : gmx::ArrayRef<real>();
-    initialize_lambdas(fplog, *ir, MASTER(cr), fep_state, lambda, lam0);
+    initialize_lambdas(fplog, *ir, MASTER(cr), fep_state, lambda);
     const bool        simulationsShareState = false;
     gmx_mdoutf*       outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
                                    mdModulesNotifier, ir, top_global, oenv, wcycle,
@@ -477,7 +478,19 @@ void gmx::LegacySimulator::do_rerun()
 
         if (ir->efep != efepNO && MASTER(cr))
         {
-            setCurrentLambdasRerun(step, ir->fepvals, &rerun_fr, lam0, state_global);
+            if (rerun_fr.bLambda)
+            {
+                ir->fepvals->init_lambda = rerun_fr.lambda;
+            }
+            else
+            {
+                if (rerun_fr.bFepState)
+                {
+                    state->fep_state = rerun_fr.fep_state;
+                }
+            }
+
+            state_global->lambda = currentLambdas(step, *(ir->fepvals), state->fep_state);
         }
 
         if (MASTER(cr))
index 6c791cdba20e4fb26a442f12117d20422eb86295..7cf3e29ff7dcee3aeed2d9d2964fb0f54e88194a 100644 (file)
@@ -359,12 +359,7 @@ void printLambdaStateToLog(FILE* fplog, const gmx::ArrayRef<real> lambda, const
     }
 }
 
-void initialize_lambdas(FILE*               fplog,
-                        const t_inputrec&   ir,
-                        bool                isMaster,
-                        int*                fep_state,
-                        gmx::ArrayRef<real> lambda,
-                        double*             lam0)
+void initialize_lambdas(FILE* fplog, const t_inputrec& ir, bool isMaster, int* fep_state, gmx::ArrayRef<real> lambda)
 {
     /* TODO: Clean up initialization of fep_state and lambda in
        t_state.  This function works, but could probably use a logic
@@ -399,10 +394,6 @@ void initialize_lambdas(FILE*               fplog,
         {
             lambda[i] = thisLambda;
         }
-        if (lam0 != nullptr)
-        {
-            lam0[i] = thisLambda;
-        }
     }
     if (ir.bSimTemp)
     {
index 86ec3e5ba87eb9fe5740f4976a7aeb0b746220bd..9b2db3b779d1c17592746a333d58f396dfc13693 100644 (file)
@@ -361,19 +361,12 @@ static inline gmx::ArrayRef<const gmx::RVec> positionsFromStatePointer(const t_s
 void printLambdaStateToLog(FILE* fplog, gmx::ArrayRef<real> lambda, bool isInitialOutput);
 
 
-/*! \brief Fills fep_state, lambda, and lam0 if needed
+/*! \brief Fills fep_state and lambda if needed
  *
- * If FEP or simulated tempering is in use:
- *
- *    fills non-null lam0 with the initial lambda values, and
- *    on master rank fills fep_state and lambda.
+ * If FEP or simulated tempering is in use,  fills fep_state
+ * and lambda on master rank.
  *
  * Reports the initial lambda state to the log file. */
-void initialize_lambdas(FILE*               fplog,
-                        const t_inputrec&   ir,
-                        bool                isMaster,
-                        int*                fep_state,
-                        gmx::ArrayRef<real> lambda,
-                        double*             lam0);
+void initialize_lambdas(FILE* fplog, const t_inputrec& ir, bool isMaster, int* fep_state, gmx::ArrayRef<real> lambda);
 
 #endif
index 20ba5f586cf0ab60ac5ce017d792c3ba074aa828..99f8d3205b2833662a934400bcc9ea8c8e98e41a 100644 (file)
@@ -43,6 +43,7 @@
 
 #include "freeenergyperturbationdata.h"
 
+#include "gromacs/mdlib/freeenergyparameters.h"
 #include "gromacs/mdlib/md_support.h"
 #include "gromacs/mdlib/mdatoms.h"
 #include "gromacs/mdtypes/inputrec.h"
@@ -58,15 +59,13 @@ namespace gmx
 FreeEnergyPerturbationData::FreeEnergyPerturbationData(FILE* fplog, const t_inputrec* inputrec, MDAtoms* mdAtoms) :
     element_(std::make_unique<Element>(this, inputrec->fepvals->delta_lambda)),
     lambda_(),
-    lambda0_(),
     currentFEPState_(0),
     fplog_(fplog),
     inputrec_(inputrec),
     mdAtoms_(mdAtoms)
 {
     lambda_.fill(0);
-    lambda0_.fill(0);
-    initialize_lambdas(fplog_, *inputrec_, true, &currentFEPState_, lambda_, lambda0_.data());
+    initialize_lambdas(fplog_, *inputrec_, true, &currentFEPState_, lambda_);
     update_mdatoms(mdAtoms_->mdatoms(), lambda_[efptMASS]);
 }
 
@@ -83,7 +82,7 @@ void FreeEnergyPerturbationData::Element::scheduleTask(Step step,
 void FreeEnergyPerturbationData::updateLambdas(Step step)
 {
     // at beginning of step (if lambdas change...)
-    setCurrentLambdasLocal(step, inputrec_->fepvals, lambda0_.data(), lambda_, currentFEPState_);
+    lambda_ = currentLambdas(step, *(inputrec_->fepvals), currentFEPState_);
     update_mdatoms(mdAtoms_->mdatoms(), lambda_[efptMASS]);
 }