mdlib -> imd
mdlib -> ewald
mdlib -> pulling
+mdlib -> awh
simd -> hardware
gpu_utils -> hardware
topology -> listed-forces
--- /dev/null
+The accelerated weight histogram method (AWH) {#page_awh}
+=============================================
+
+Accelerating sampling with AWH
+==============================
+
+AWH calculates the free energy along an order parameter of the system.
+Free energy barriers are overcome by adaptively tuning a bias potential along
+the order parameter such that the biased distribution along the parameter
+converges toward a chosen target distribution.
+The fundamental equation governing the tuning is: log(target) = bias - free energy, where
+the bias and free energy are initially unknown. Typically the target distribution is simply
+chosen uniform, such that the bias completely flattens the free energy landscape.
+
+
+Design of the AWH module
+========================
+
+The module implements AWH for the case when the order parameter corresponds to a reaction coordinate,
+here referred to as coordinate for short, i.e. a function of the system configuration.
+The bias is coupled to the system by a bias potential: either in the form of an harmonic ("umbrella") potential
+Monte-Carlo (MC) "jumping" around the current coordinate value, or as a smooth convolution of the umbrellas.
+
+The AWH module is organizes as follows:
+The Awh class is the interface between the outside and inside of the module.
+The Awh class contains one or more BiasCoupledToSystem objects.
+The BiasCoupledToSystem class takes care of the reaction coordinate input
+and force output for the single Bias object it containts.
+The Bias class is a container and wrapper for a object BiasState + helpers.
+All computation takes place in the BiasState object and its sub-classes.
+The Bias class also contains a BiasWriter object that takes care of i/o.
+
+Use of AWH in mdrun
+===================
+
+The basic use of Awh in mdrun consists of 2 method calls:
+Call the constructor Awh() after the pull module has been initialized.
+Call applyBiasForcesAndUpdateBias() at every MD step after the pull
+potential calculation function has been called.
+
+In grompp the pull potential provider should be registered using
+registerAwhWithPull() so grompp can check for unregistered potentials.
+
+The main tasks of AWH are:
+- calculate and set the bias force given the current coordinate value.
+- after accumulating a number of coordinate samples, update the free energy estimate and the bias.
+
+AWH currently relies on the pull code for the first task. Pull provides AWH with updated coordinate values
+and distributes the bias force that AWH calculates to the atoms making up the coordinate. This
+also means that there are some order dependencies where pull functions need to be called before AWH
+functions (see below).
+
+The implementation is quite general. There can be multiple independent AWH biases coupled to the system
+simultaneously. This makes sense if the system is made up of several fairly independent parts,
+like monomers in a protein. Each bias acts on exactly one, possibly multidimensional, coordinate.
+Each coordinate dimension maps to exactly one pull coordinate. Thus, an n-dimensional
+biased coordinate is defined by a set of n pull coordinates. Periodicity is taken care of for coordinate
+dimensions that require it (dihedral angles). For increased parallelism, there is the option of
+having multiple communicating simulations sharing all samples. All simulations would then share a single
+bias and free energy estimate. Alternatively, one may partition the sampling domain into smaller
+subdomains with some overlap and have multiple independent simulations sample each subdomain.
+
+Note that internally the AWH module keep tracks of free energies in units
+of the thermal energy kT. This is because we mostly deal with free energies
+in the form of -log(probability) and using any other unit would be bug prone.
+All energy type variables are explicitly documented to be in units of kT.
+Also the checkpoint and energy file data is in units of kT. The analysis
+tool will by default convert energies to kJ/mol, but there is also
+a kT option.
+
- \subpage page_simd <br/>
Documentation about the new SIMD module that makes it possible to write
highly accelerated CPU code that is still portable.
+ - \subpage page_awh <br/>
+ Documentation about the accelerated weight histogram (AWH) method,
+ which is used for accelerating sampling along reaction coordinates.
- \subpage page_mdmodules <br/>
Documentation for work-in-progress modularization of parts of mdrun, that
should make it easier to implement additional features as add-ons on top of
:mdp:`free-energy` is turned on. The force constant is then (1 -
lambda) * :mdp:`pull-coord1-k` + lambda * :mdp:`pull-coord1-kB`.
+AWH adaptive biasing
+^^^^^^^^^^^^^^^^^^^^
+
+.. mdp:: awh
+
+ .. mdp-value:: no
+
+ No biasing.
+
+ .. mdp-value:: yes
+
+ Adaptively bias a reaction coordinate using the AWH method and estimate
+ the corresponding PMF. The PMF and other AWH data are written to energy
+ file at an interval set by :mdp:`awh-nstout` and can be extracted with
+ the ``gmx awh`` tool. The AWH coordinate can be
+ multidimensional and is defined by mapping each dimension to a pull coordinate index.
+ This is only allowed if :mdp-value:`pull-coord1-type=external-potential` and
+ :mdp:`pull-coord1-potential-provider` = ``awh`` for the concerned pull coordinate
+ indices.
+
+.. mdp:: awh-potential
+
+ .. mdp-value:: convolved
+
+ The applied biasing potential is the convolution of the bias function and a
+ set of harmonic umbrella potentials (see :mdp-value:`awh-potential=umbrella` below). This results
+ in a smooth potential function and force. The resolution of the potential is set
+ by the force constant of each umbrella, see :mdp:`awh1-dim1-force-constant`.
+
+ .. mdp-value:: umbrella
+
+ The potential bias is applied by controlling the position of an harmonic potential
+ using Monte-Carlo sampling. The force constant is set with
+ :mdp:`awh1-dim1-force-constant`. The umbrella location
+ is sampled using Monte-Carlo every :mdp:`awh-nstsample` steps.
+ This option can be useful for cases when calculating the convolved force for each step becomes
+ computationally expensive.
+
+.. mdp:: awh-share-multisim
+
+ .. mdp-value:: no
+
+ AWH will not share biases across simulations started with
+ :ref:`gmx mdrun` option ``-multidir``. The biases will be independent.
+
+ .. mdp-value:: yes
+
+ With :ref:`gmx mdrun` and option ``-multidir`` the bias and PMF estimates
+ for biases with :mdp:`awh1-share-group` >0 will be shared across simulations
+ with the biases with the same :mdp:`awh1-share-group` value.
+ The simulations should have the same AWH settings for sharing to make sense.
+ :ref:`gmx mdrun` will check whether the simulations are technically
+ compatible for sharing, but the user should check that bias sharing
+ physically makes sense.
+
+.. mdp:: awh-seed
+
+ (-1) Random seed for Monte-Carlo sampling the umbrella position,
+ where -1 indicates to generate a seed. Only used with
+ :mdp-value:`awh-potential=umbrella`.
+
+.. mdp:: awh-nstout
+
+ (100000)
+ Number of steps between printing AWH data to the energy file, should be
+ a multiple of :mdp:`nstenergy`.
+
+.. mdp:: awh-nstsample
+
+ (10)
+ Number of steps between sampling of the coordinate value. This sampling
+ is the basis for updating the bias and estimating the PMF and other AWH observables.
+
+.. mdp:: awh-nsamples-update
+
+ (10)
+ The number of coordinate samples used for each AWH update.
+ The update interval in steps is :mdp:`awh-nstsample` times this value.
+
+.. mdp:: awh-nbias
+
+ (1)
+ The number of biases, each acting on its own coordinate.
+ The following options should be specified
+ for each bias although below only the options for bias number 1 is shown. Options for
+ other bias indices are obtained by replacing '1' by the bias index.
+
+.. mdp:: awh1-error-init
+
+ (10.0) \[kJ mol-1\]
+ Estimated initial average error of the PMF for this bias. This value together with the
+ given diffusion constant(s) :mdp:`awh1-dim1-diffusion` determine the initial biasing rate.
+ The error is obviously not known *a priori*. Only a rough estimate of :mdp:`awh1-error-init`
+ is needed however.
+ As a general guideline, leave :mdp:`awh1-error-init` to its default value when starting a new
+ simulation. On the other hand, when there is *a priori* knowledge of the PMF (e.g. when
+ an initial PMF estimate is provided, see the :mdp:`awh1-user-data` option)
+ then :mdp:`awh1-error-init` should reflect that knowledge.
+
+.. mdp:: awh1-growth
+
+ .. mdp-value:: exp-linear
+
+ Each bias keeps a reference weight histogram for the coordinate samples.
+ Its size sets the magnitude of the bias function and free energy estimate updates
+ (few samples corresponds to large updates and vice versa).
+ Thus, its growth rate sets the maximum convergence rate.
+ By default, there is an initial stage in which the histogram grows close to exponentially (but slower than the sampling rate).
+ In the final stage that follows, the growth rate is linear and equal to the sampling rate (set by :mdp:`awh-nstsample`).
+ The initial stage is typically necessary for efficient convergence when starting a new simulation where
+ high free energy barriers have not yet been flattened by the bias.
+
+ .. mdp-value:: linear
+
+ As :mdp-value:`awh1-growth=exp-linear` but skip the initial stage. This may be useful if there is *a priori*
+ knowledge (see :mdp:`awh1-error-init`) which eliminates the need for an initial stage. This is also
+ the setting compatible with :mdp-value:`awh1-target=local-boltzmann`.
+
+.. mdp:: awh1-equilibrate-histogram
+
+ .. mdp-value:: no
+
+ Do not equilibrate histogram.
+
+ .. mdp-value:: yes
+
+ Before entering the initial stage (see :mdp-value:`awh1-growth=exp-linear`), make sure the
+ histogram of sampled weights is following the target distribution closely enough (specifically,
+ at least 80% of the target region needs to have a local relative error of less than 20%). This
+ option would typically only be used when :mdp:`awh1-share-group` > 0
+ and the initial configurations poorly represent the target
+ distribution.
+
+.. mdp:: awh1-target
+
+ .. mdp-value:: constant
+
+ The bias is tuned towards a constant (uniform) coordinate distribution
+ in the defined sampling interval (defined by \[:mdp:`awh1-dim1-start`, :mdp:`awh1-dim1-end`\]).
+
+ .. mdp-value:: cutoff
+
+ Similar to :mdp-value:`awh1-target=constant`, but the target
+ distribution is proportional to 1/(1 + exp(F - :mdp-value:`awh1-target=cutoff`)),
+ where F is the free energy relative to the estimated global minimum.
+ This provides a smooth switch of a flat target distribution in
+ regions with free energy lower than the cut-off to a Boltzmann
+ distribution in regions with free energy higher than the cut-off.
+
+ .. mdp-value:: boltzmann
+
+ The target distribution is a Boltzmann distribtution with a scaled beta (inverse temperature)
+ factor given by :mdp:`awh1-target-beta-scaling`. *E.g.*, a value of 0.1
+ would give the same coordinate distribution as sampling with a simulation temperature
+ scaled by 10.
+
+ .. mdp-value:: local-boltzmann
+
+ Same target distribution and use of :mdp:`awh1-target-beta-scaling`
+ but the convergence towards the target distribution is inherently local *i.e.*, the rate of
+ change of the bias only depends on the local sampling. This local convergence property is
+ only compatible with :mdp-value:`awh1-growth=linear`, since for
+ :mdp-value:`awh1-growth=exp-linear` histograms are globally rescaled in the initial stage.
+
+.. mdp:: awh1-target-beta-scaling
+
+ [0] \[\]
+ For :mdp-value:`awh1-target=boltzmann` and :mdp-value:`awh1-target=local-boltzmann`
+ it is the unitless beta scaling factor taking values in (0,1).
+
+.. mdp:: awh1-target-cutoff
+
+ [0] \[kJ mol-1\]
+ For :mdp-value:`awh1-target=cutoff` this is the cutoff, should be > 0.
+
+.. mdp:: awh1-user-data
+
+ .. mdp-value:: no
+
+ Initialize the PMF and target distribution with default values.
+
+ .. mdp-value:: yes
+
+ Initialize the PMF and target distribution with user provided data. For :mdp:`awh-nbias` = 1,
+ :ref:`gmx mdrun` will expect a file ``awh-init.xvg`` to be present in the run directory.
+ For multiple biases, :ref:`gmx mdrun` expects files ``awh-init1.xvg``, ``awh-init2.xvg``, etc.
+ The file name can be changed with the ``-awh`` option.
+ The first :mdp:`awh1-ndim` columns of
+ each input file should contain the coordinate values, such that each row defines a point in
+ coordinate space. Column :mdp:`awh1-ndim` + 1 should contain the PMF value for each point.
+ The target distribution column can either follow the PMF (column :mdp:`awh1-ndim` + 2) or
+ be in the same column as written by ``gmx awh``.
+
+.. mdp:: awh1-share-group
+
+ .. mdp-value:: 0
+
+ Do not share the bias.
+
+ .. mdp-value:: positive
+
+ Share the bias and PMF estimates within and/or between simulations.
+ Within a simulation, the bias will be shared between biases that have the
+ same :mdp:`awh1-share-group` index (note that the current code does not support this).
+ With :mdp-value:`awh-share-multisim=yes` and
+ :ref:`gmx mdrun` option ``-multidir`` the bias will also be shared across simulations.
+ Sharing may increase convergence initially, although the starting configurations
+ can be critical, especially when sharing between many biases.
+ Currently, positive group values should start at 1 and increase
+ by 1 for each subsequent bias that is shared.
+
+.. mdp:: awh1-ndim
+
+ (1) \[integer\]
+ Number of dimensions of the coordinate, each dimension maps to 1 pull coordinate.
+ The following options should be specified for each such dimension. Below only
+ the options for dimension number 1 is shown. Options for other dimension indices are
+ obtained by replacing '1' by the dimension index.
+
+.. mdp:: awh1-dim1-coord-provider
+
+ .. mdp-value:: pull
+
+ The module providing the reaction coordinate for this dimension.
+ Currently AWH can only act on pull coordinates.
+
+.. mdp:: awh1-dim1-coord-index
+
+ (1)
+ Index of the pull coordinate defining this coordinate dimension.
+
+.. mdp:: awh1-dim1-force-constant
+
+ (0) \[kJ/mol/nm^2\] or \[kJ/mol/rad^2\]
+ Force constant for the (convolved) umbrella potential(s) along this
+ coordinate dimension.
+
+.. mdp:: awh1-dim1-start
+
+ (0.0) \[nm\]/\[rad\]
+ Start value of the sampling interval along this dimension. The range of allowed
+ values depends on the relevant pull geometry (see :mdp:`pull-coord1-geometry`).
+ For periodic geometries :mdp:`awh1-dim1-start` greater than :mdp:`awh1-dim1-end`
+ is allowed. The interval will then wrap around from +period/2 to -period/2.
+
+.. mdp:: awh1-dim1-end
+
+ (0.0) \[nm\]/\[rad\]
+ End value defining the sampling interval together with :mdp:`awh1-dim1-start`.
+
+.. mdp:: awh1-dim1-period
+
+ (0.0) \[nm\]/\[rad\]
+ The period of this reaction coordinate, use 0 when the coordinate is not periodic.
+
+.. mdp:: awh1-dim1-diffusion
+
+ (1e-5) \[nm^2/ps\]/\[rad^2/ps\]
+ Estimated diffusion constant for this coordinate dimension determining the initial
+ biasing rate. This needs only be a rough estimate and should not critically
+ affect the results unless it is set to something very low, leading to slow convergence,
+ or very high, forcing the system far from equilibrium. Not setting this value
+ explicitly generates a warning.
+
+.. mdp:: awh1-dim1-cover-diameter
+
+ (0.0)) \[nm\]/\[rad\]
+ Diameter that needs to be sampled by a single simulation around a coordinate value
+ before the point is considered covered in the initial stage (see :mdp-value:`awh1-growth=exp-linear`).
+ A value > 0 ensures that for each covering there is a continuous transition of this diameter
+ across each coordinate value.
+ This is trivially true for independent simulations but not for for multiple bias-sharing simulations
+ (:mdp:`awh1-share-group`>0).
+ For a diameter = 0, covering occurs as soon as the simulations have sampled the whole interval, which
+ for many sharing simulations does not guarantee transitions across free energy barriers.
+ On the other hand, when the diameter >= the sampling interval length, covering occurs when a single simulation
+ has independently sampled the whole interval.
Enforced rotation
^^^^^^^^^^^^^^^^^
add_subdirectory(swap)
add_subdirectory(essentialdynamics)
add_subdirectory(pulling)
+add_subdirectory(awh)
add_subdirectory(simd)
add_subdirectory(imd)
if (NOT GMX_BUILD_MDRUN_ONLY)
--- /dev/null
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2014,2015,2016,2017, 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.
+
+file(GLOB AWH_SOURCES *.cpp)
+set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${AWH_SOURCES} PARENT_SCOPE)
+
+if (BUILD_TESTING)
+ add_subdirectory(tests)
+endif()
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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 the Awh class.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#include "gmxpre.h"
+
+#include "awh.h"
+
+#include <assert.h>
+
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include <algorithm>
+
+#include "gromacs/gmxlib/network.h"
+#include "gromacs/math/units.h"
+#include "gromacs/mdtypes/awh-history.h"
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/forceoutput.h"
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/pull-params.h"
+#include "gromacs/mdtypes/state.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/pulling/pull.h"
+#include "gromacs/timing/wallcycle.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/pleasecite.h"
+
+#include "bias.h"
+#include "biassharing.h"
+#include "pointstate.h"
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief A bias and its coupling to the system.
+ *
+ * This struct is used to separate the bias machinery in the Bias class,
+ * which should be independent from the reaction coordinate, from the
+ * obtaining of the reaction coordinate values and passing the computed forces.
+ * Currently the AWH method couples to the system by mapping each
+ * AWH bias to a pull coordinate. This can easily be generalized here.
+ */
+struct BiasCoupledToSystem
+{
+ /*! \brief Constructor, couple a bias to a set of pull coordinates.
+ *
+ * \param[in] bias The bias.
+ * \param[in] pullCoordIndex The pull coordinate indices.
+ */
+ BiasCoupledToSystem(Bias bias,
+ const std::vector<int> &pullCoordIndex);
+
+ Bias bias; /**< The bias. */
+ const std::vector<int> pullCoordIndex; /**< The pull coordinates this bias acts on. */
+
+ /* Here AWH can be extended to work on other coordinates than pull. */
+};
+
+BiasCoupledToSystem::BiasCoupledToSystem(Bias bias,
+ const std::vector<int> &pullCoordIndex) :
+ bias(bias),
+ pullCoordIndex(pullCoordIndex)
+{
+ /* We already checked for this in grompp, but check again here. */
+ GMX_RELEASE_ASSERT(static_cast<size_t>(bias.ndim()) == pullCoordIndex.size(), "The bias dimensionality should match the number of pull coordinates.");
+}
+
+Awh::Awh(FILE *fplog,
+ const t_inputrec &inputRecord,
+ const t_commrec *commRecord,
+ const AwhParams &awhParams,
+ const std::string &biasInitFilename,
+ pull_t *pull_work) :
+ seed_(awhParams.seed),
+ commRecord_(commRecord),
+ pull_(pull_work),
+ potentialOffset_(0)
+{
+ /* We already checked for this in grompp, but check again here. */
+ GMX_RELEASE_ASSERT(inputRecord.pull != nullptr, "With AWH we should have pull parameters");
+ GMX_RELEASE_ASSERT(pull_work != nullptr, "With AWH pull should be initialized before initializing AWH");
+
+ if (fplog != nullptr)
+ {
+ please_cite(fplog, "Lindahl2014");
+ }
+
+ if (haveBiasSharingWithinSimulation(awhParams))
+ {
+ /* This has likely been checked by grompp, but throw anyhow. */
+ GMX_THROW(InvalidInputError("Biases within a simulation are shared, currently sharing of biases is only supported between simulations"));
+ }
+
+ int numSharingSimulations = 1;
+ if (awhParams.shareBiasMultisim && MULTISIM(commRecord_))
+ {
+ numSharingSimulations = commRecord_->ms->nsim;
+ }
+
+ /* Initialize all the biases */
+ const double beta = 1/(BOLTZ*inputRecord.opts.ref_t[0]);
+ for (int k = 0; k < awhParams.numBias; k++)
+ {
+ const AwhBiasParams &awhBiasParams = awhParams.awhBiasParams[k];
+
+ std::vector<int> pullCoordIndex;
+ std::vector<DimParams> dimParams;
+ for (int d = 0; d < awhBiasParams.ndim; d++)
+ {
+ const AwhDimParams &awhDimParams = awhBiasParams.dimParams[d];
+ GMX_RELEASE_ASSERT(awhDimParams.eCoordProvider == eawhcoordproviderPULL, "Currently only the pull code is supported as coordinate provider");
+ const t_pull_coord &pullCoord = inputRecord.pull->coord[awhDimParams.coordIndex];
+ double conversionFactor = pull_coordinate_is_angletype(&pullCoord) ? DEG2RAD : 1;
+ dimParams.push_back(DimParams(conversionFactor, awhDimParams.forceConstant, beta));
+
+ pullCoordIndex.push_back(awhDimParams.coordIndex);
+ }
+
+ /* Construct the bias and couple it to the system. */
+ Bias::ThisRankWillDoIO thisRankWillDoIO = (MASTER(commRecord_) ? Bias::ThisRankWillDoIO::Yes : Bias::ThisRankWillDoIO::No);
+ biasCoupledToSystem_.emplace_back(Bias(k, awhParams, awhParams.awhBiasParams[k], dimParams, beta, inputRecord.delta_t, numSharingSimulations, biasInitFilename, thisRankWillDoIO),
+ pullCoordIndex);
+ }
+
+ /* Need to register the AWH coordinates to be allowed to apply forces to the pull coordinates. */
+ registerAwhWithPull(awhParams, pull_);
+
+ if (numSharingSimulations > 1 && MASTER(commRecord_))
+ {
+ std::vector<size_t> pointSize;
+ for (auto const &biasCts : biasCoupledToSystem_)
+ {
+ pointSize.push_back(biasCts.bias.state().points().size());
+ }
+ /* Ensure that the shared biased are compatible between simulations */
+ biasesAreCompatibleForSharingBetweenSimulations(awhParams, pointSize, commRecord_->ms);
+ }
+}
+
+Awh::~Awh() = default;
+
+real Awh::applyBiasForcesAndUpdateBias(int ePBC,
+ const t_mdatoms &mdatoms,
+ const matrix box,
+ gmx::ForceWithVirial *forceWithVirial,
+ double t,
+ gmx_int64_t step,
+ gmx_wallcycle *wallcycle,
+ FILE *fplog)
+{
+ GMX_ASSERT(forceWithVirial, "Need a valid ForceWithVirial object");
+
+ wallcycle_start(wallcycle, ewcAWH);
+
+ t_pbc pbc;
+ set_pbc(&pbc, ePBC, box);
+
+ /* During the AWH update the potential can instantaneously jump due to either
+ an bias update or moving the umbrella. The jumps are kept track of and
+ subtracted from the potential in order to get a useful conserved energy quantity. */
+ double awhPotential = potentialOffset_;
+
+ for (auto &biasCts : biasCoupledToSystem_)
+ {
+ /* Update the AWH coordinate values with those of the corresponding
+ * pull coordinates.
+ */
+ awh_dvec coordValue = { 0, 0, 0, 0 };
+ for (int d = 0; d < biasCts.bias.ndim(); d++)
+ {
+ coordValue[d] = get_pull_coord_value(pull_, biasCts.pullCoordIndex[d], &pbc);
+ }
+
+ /* Perform an AWH biasing step: this means, at regular intervals,
+ * sampling observables based on the input pull coordinate value,
+ * setting the bias force and/or updating the AWH bias state.
+ */
+ awh_dvec biasForce;
+ double biasPotential;
+ double biasPotentialJump;
+ /* Note: In the near future this call will be split in calls
+ * to supports bias sharing within a single simulation.
+ */
+ biasCts.bias.calcForceAndUpdateBias(coordValue, biasForce,
+ &biasPotential, &biasPotentialJump,
+ commRecord_->ms,
+ t, step, seed_, fplog);
+
+ awhPotential += biasPotential;
+
+ /* Keep track of the total potential shift needed to remove the potential jumps. */
+ potentialOffset_ -= biasPotentialJump;
+
+ /* Communicate the bias force to the pull struct.
+ * The bias potential is returned at the end of this function,
+ * so that it can be added externally to the correct energy data block.
+ */
+ for (int d = 0; d < biasCts.bias.ndim(); d++)
+ {
+ apply_external_pull_coord_force(pull_, biasCts.pullCoordIndex[d],
+ biasForce[d], &mdatoms,
+ forceWithVirial);
+ }
+ }
+
+ wallcycle_stop(wallcycle, ewcAWH);
+
+ return MASTER(commRecord_) ? static_cast<real>(awhPotential) : 0;
+}
+
+std::shared_ptr<AwhHistory> Awh::initHistoryFromState() const
+{
+ if (MASTER(commRecord_))
+ {
+ std::shared_ptr<AwhHistory> awhHistory(new AwhHistory);
+ awhHistory->bias.clear();
+ awhHistory->bias.resize(biasCoupledToSystem_.size());
+
+ for (size_t k = 0; k < awhHistory->bias.size(); k++)
+ {
+ biasCoupledToSystem_[k].bias.initHistoryFromState(&awhHistory->bias[k]);
+ }
+
+ return awhHistory;
+ }
+ else
+ {
+ /* Return an empty pointer */
+ return std::shared_ptr<AwhHistory>();
+ }
+}
+
+void Awh::restoreStateFromHistory(const AwhHistory *awhHistory)
+{
+ /* Restore the history to the current state */
+ if (MASTER(commRecord_))
+ {
+ GMX_RELEASE_ASSERT(awhHistory != nullptr, "The master rank should have a valid awhHistory when restoring the state from history.");
+
+ if (awhHistory->bias.size() != biasCoupledToSystem_.size())
+ {
+ GMX_THROW(InvalidInputError("AWH state and history contain different numbers of biases. Likely you provided a checkpoint from a different simulation."));
+ }
+
+ potentialOffset_ = awhHistory->potentialOffset;
+ }
+ if (PAR(commRecord_))
+ {
+ gmx_bcast(sizeof(potentialOffset_), &potentialOffset_, commRecord_);
+ }
+
+ for (size_t k = 0; k < biasCoupledToSystem_.size(); k++)
+ {
+ biasCoupledToSystem_[k].bias.restoreStateFromHistory(awhHistory ? &awhHistory->bias[k] : nullptr, commRecord_);
+ }
+}
+
+void Awh::updateHistory(AwhHistory *awhHistory) const
+{
+ if (!MASTER(commRecord_))
+ {
+ return;
+ }
+
+ /* This assert will also catch a non-master rank calling this function. */
+ GMX_RELEASE_ASSERT(awhHistory->bias.size() == biasCoupledToSystem_.size(), "AWH state and history bias count should match");
+
+ awhHistory->potentialOffset = potentialOffset_;
+
+ for (size_t k = 0; k < awhHistory->bias.size(); k++)
+ {
+ biasCoupledToSystem_[k].bias.updateHistory(&awhHistory->bias[k]);
+ }
+}
+
+const char * Awh::externalPotentialString()
+{
+ return "AWH";
+}
+
+void Awh::registerAwhWithPull(const AwhParams &awhParams,
+ pull_t *pull_work)
+{
+ GMX_RELEASE_ASSERT(pull_work, "Need a valid pull object");
+
+ for (int k = 0; k < awhParams.numBias; k++)
+ {
+ const AwhBiasParams &biasParams = awhParams.awhBiasParams[k];
+
+ for (int d = 0; d < biasParams.ndim; d++)
+ {
+ register_external_pull_potential(pull_work, biasParams.dimParams[d].coordIndex, Awh::externalPotentialString());
+ }
+ }
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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.
+ */
+
+/*! \libinternal
+ * \defgroup module_awh Accelerated weight histogram (AWH) method
+ * \ingroup group_mdrun
+ * \brief
+ * Implements the "accelerated weight histogram" sampling method.
+ *
+ * This class provides the interface between the AWH module and
+ * other modules using it. Currently AWH can only act on COM pull
+ * reaction coordinates, but this can easily be extended to other
+ * types of reaction coordinates.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * Declares the Awh class.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_H
+#define GMX_AWH_H
+
+#include <cstdio>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_multisim_t;
+struct gmx_wallcycle;
+struct pull_work_t;
+struct pull_t;
+class t_state;
+struct t_commrec;
+struct t_inputrec;
+struct t_mdatoms;
+
+namespace gmx
+{
+
+struct AwhHistory;
+struct AwhParams;
+class Bias;
+struct BiasCoupledToSystem;
+class ForceWithVirial;
+
+/*! \libinternal
+ * \brief Coupling of the accelerated weight histogram method (AWH) with the system.
+ *
+ * AWH calculates the free energy along order parameters of the system.
+ * Free energy barriers are overcome by adaptively tuning a bias potential along
+ * the order parameter such that the biased distribution along the parameter
+ * converges toward a chosen target distribution.
+ *
+ * The Awh class takes care of the coupling between the system and the AWH
+ * bias(es). The Awh class contains one or more BiasCoupledToSystem objects.
+ * The BiasCoupledToSystem class takes care of the reaction coordinate input
+ * and force output for the single Bias object it containts.
+ *
+ * \todo Update parameter reading and checkpointing, when general C++ framework is ready.
+ */
+class Awh
+{
+ public:
+ /*! \brief Construct an AWH at the start of a simulation.
+ *
+ * AWH will here also register itself with the pull struct as the
+ * potential provider for the pull coordinates given as AWH coordinates
+ * in the user input. This allows AWH to later apply the bias force to
+ * these coordinate in \ref Awh::applyBiasForcesAndUpdateBias.
+ *
+ * \param[in,out] fplog General output file, normally md.log, can be nullptr.
+ * \param[in] inputRecord General input parameters (as set up by grompp).
+ * \param[in] commRecord Struct for communication, can be nullptr.
+ * \param[in] awhParams AWH input parameters, consistent with the relevant parts of \p inputRecord (as set up by grompp).
+ * \param[in] biasInitFilename Name of file to read PMF and target from.
+ * \param[in,out] pull_work Pointer to a pull struct which AWH will couple to, has to be initialized, is assumed not to change during the lifetime of the Awh object.
+ */
+ Awh(FILE *fplog,
+ const t_inputrec &inputRecord,
+ const t_commrec *commRecord,
+ const AwhParams &awhParams,
+ const std::string &biasInitFilename,
+ pull_t *pull_work);
+
+ /*! \brief Destructor. */
+ ~Awh();
+
+ /*! \brief Peform an AWH update, to be called every MD step.
+ *
+ * An update has two tasks: apply the bias force and improve
+ * the bias and the free energy estimate that AWH keeps internally.
+ *
+ * For the first task, AWH retrieves the pull coordinate values from the pull struct.
+ * With these, the bias potential and forces are calculated.
+ * The bias force together with the atom forces and virial
+ * are passed on to pull which applies the bias force to the atoms.
+ * This is done at every step.
+ *
+ * Secondly, coordinate values are regularly sampled and kept by AWH.
+ * Convergence of the bias and free energy estimate is achieved by
+ * updating the AWH bias state after a certain number of samples has been collected.
+ *
+ * \note Requires that pull_potential from pull.h has been called first
+ * since AWH needs the current coordinate values (the pull code checks
+ * for this).
+ *
+ * \param[in] mdatoms Atom properties.
+ * \param[in] ePBC Type of periodic boundary conditions.
+ * \param[in] box Box vectors.
+ * \param[in,out] forceWithVirial Force and virial buffers, should cover at least the local atoms.
+ * \param[in] t Time.
+ * \param[in] step Time step.
+ * \param[in,out] wallcycle Wallcycle counter, can be nullptr.
+ * \param[in,out] fplog General output file, normally md.log, can be nullptr.
+ * \returns the potential energy for the bias.
+ */
+ real applyBiasForcesAndUpdateBias(int ePBC,
+ const t_mdatoms &mdatoms,
+ const matrix box,
+ gmx::ForceWithVirial *forceWithVirial,
+ double t,
+ gmx_int64_t step,
+ gmx_wallcycle *wallcycle,
+ FILE *fplog);
+
+ /*! \brief
+ * Update the AWH history in preparation for writing to checkpoint file.
+ *
+ * Should be called at least on the master rank at checkpoint steps.
+ *
+ * Should be called with a valid \p awhHistory (is checked).
+ *
+ * \param[in,out] awhHistory AWH history to set.
+ */
+ void updateHistory(AwhHistory *awhHistory) const;
+
+ /*! \brief
+ * Allocate and initialize an AWH history with the given AWH state.
+ *
+ * This function should be called at the start of a new simulation
+ * at least on the master rank.
+ * Note that only constant data will be initialized here.
+ * History data is set by \ref Awh::updateHistory.
+ *
+ * \returns a shared pointer to the AWH history on the rank that does I/O, nullptr otherwise.
+ */
+ std::shared_ptr<AwhHistory> initHistoryFromState() const;
+
+ /*! \brief Restore the AWH state from the given history.
+ *
+ * Should be called on all ranks (for internal MPI broadcast).
+ * Should pass a point to an AwhHistory on the master rank that
+ * is compatible with the AWH setup in this simulation. Will throw
+ * an exception if it is not compatible.
+ *
+ * \param[in] awhHistory AWH history to restore from.
+ */
+ void restoreStateFromHistory(const AwhHistory *awhHistory);
+
+ /*! \brief Returns string "AWH" for registering AWH as an external potential provider with the pull module.
+ */
+ static const char *externalPotentialString();
+
+ /*! \brief Register the AWH biased coordinates with pull.
+ *
+ * This function is public because it needs to be called by grompp
+ * (and is otherwise only called by Awh()).
+ * Pull requires all external potentials to register themselves
+ * before the end of pre-processing and before the first MD step.
+ * If this has not happened, pull with throw an error.
+ *
+ * \param[in] awhParams The AWH parameters.
+ * \param[in,out] pull_work Pull struct which AWH will register the bias into.
+ */
+ static void registerAwhWithPull(const AwhParams &awhParams,
+ pull_t *pull_work);
+
+ private:
+ std::vector<BiasCoupledToSystem> biasCoupledToSystem_; /**< AWH biases and definitions of their coupling to the system. */
+ const gmx_int64_t seed_; /**< Random seed for MC jumping with umbrella type bias potential. */
+ const t_commrec *commRecord_; /**< Pointer to the communication record. */
+ pull_t *pull_; /**< Pointer to the pull working data. */
+ double potentialOffset_; /**< The offset of the bias potential which changes due to bias updates. */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_H */
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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 the Bias class.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#include "gmxpre.h"
+
+#include "bias.h"
+
+#include <assert.h>
+
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include <algorithm>
+
+#include "gromacs/fileio/gmxfio.h"
+#include "gromacs/gmxlib/network.h"
+#include "gromacs/math/utilities.h"
+#include "gromacs/mdtypes/awh-history.h"
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/mdtypes/commrec.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "pointstate.h"
+
+namespace gmx
+{
+
+void Bias::warnForHistogramAnomalies(double t, gmx_int64_t step, FILE *fplog)
+{
+ const int maxNumWarningsInCheck = 1; /* The maximum number of warnings to print per check */
+ const int maxNumWarningsInRun = 10; /* The maximum number of warnings to print in a run */
+
+ if (fplog == nullptr || numWarningsIssued_ >= maxNumWarningsInRun || state_.inInitialStage() ||
+ !params_.isCheckStep(state_.points().size(), step))
+ {
+ return;
+ }
+
+ numWarningsIssued_ +=
+ state_.warnForHistogramAnomalies(grid_, biasIndex(), t, fplog,
+ maxNumWarningsInCheck);
+
+ if (numWarningsIssued_ >= maxNumWarningsInRun)
+ {
+ fprintf(fplog, "\nawh%d: suppressing future AWH warnings.\n", biasIndex() + 1);
+ }
+}
+
+void Bias::doSkippedUpdatesForAllPoints()
+{
+ state_.doSkippedUpdatesForAllPoints(params_);
+}
+
+void Bias::calcForceAndUpdateBias(const awh_dvec coordValue,
+ awh_dvec biasForce,
+ double *awhPotential,
+ double *potentialJump,
+ const gmx_multisim_t *ms,
+ double t,
+ gmx_int64_t step,
+ gmx_int64_t seed,
+ FILE *fplog)
+{
+ if (step < 0)
+ {
+ GMX_THROW(InvalidInputError("The step number is negative which is not supported by the AWH code."));
+ }
+
+ state_.setCoordValue(grid_, coordValue);
+
+ std::vector<double> *probWeightNeighbor = &tempWorkSpace_;
+
+ /* If the convolved force is needed or this is a sampling step,
+ * the bias in the current neighborhood needs to be up-to-date
+ * and the probablity weights need to be calculated.
+ */
+ const bool sampleCoord = params_.isSampleCoordStep(step);
+ const bool moveUmbrella = (sampleCoord || step == 0);
+ double convolvedBias = 0;
+ if (params_.convolveForce || moveUmbrella || sampleCoord)
+ {
+ if (params_.skipUpdates())
+ {
+ state_.doSkippedUpdatesInNeighborhood(params_, grid_);
+ }
+
+ convolvedBias = state_.updateProbabilityWeightsAndConvolvedBias(dimParams_, grid_, probWeightNeighbor);
+
+ if (sampleCoord)
+ {
+ state_.sampleCoordAndPmf(grid_, *probWeightNeighbor, convolvedBias);
+ }
+ }
+
+ const CoordState &coordState = state_.coordState();
+
+ /* Set the bias force and get the potential contribution from this bias.
+ * The potential jump occurs at different times depending on how
+ * the force is applied (and how the potential is normalized).
+ * For the convolved force it happens when the bias is updated,
+ * for the umbrella when the umbrella is moved.
+ */
+ *potentialJump = 0;
+ double potential;
+ if (params_.convolveForce)
+ {
+ state_.calcConvolvedForce(dimParams_, grid_, *probWeightNeighbor,
+ biasForce);
+
+ potential = -convolvedBias*params_.invBeta;
+ }
+ else
+ {
+ /* Umbrella force */
+ GMX_RELEASE_ASSERT(state_.points()[coordState.umbrellaGridpoint()].inTargetRegion(),
+ "AWH bias grid point for the umbrella reference value is outside of the target region.");
+ potential =
+ state_.calcUmbrellaForceAndPotential(dimParams_, grid_, coordState.umbrellaGridpoint(), biasForce);
+
+ /* Moving the umbrella results in a force correction and
+ * a new potential. The umbrella center is sampled as often as
+ * the coordinate so we know the probability weights needed
+ * for moving the umbrella are up-to-date.
+ */
+ if (moveUmbrella)
+ {
+ double newPotential = state_.moveUmbrella(dimParams_, grid_, *probWeightNeighbor, biasForce, step, seed, params_.biasIndex);
+ *potentialJump = newPotential - potential;
+ }
+ }
+
+ /* Update the free energy estimates and bias and other history dependent method parameters */
+ if (params_.isUpdateFreeEnergyStep(step))
+ {
+ state_.updateFreeEnergyAndAddSamplesToHistogram(dimParams_, grid_,
+ params_,
+ ms, t, step, fplog,
+ &updateList_);
+
+ if (params_.convolveForce)
+ {
+ /* The update results in a potential jump, so we need the new convolved potential. */
+ double newPotential = -calcConvolvedBias(coordState.coordValue())*params_.invBeta;
+ *potentialJump = newPotential - potential;
+ }
+ }
+
+ /* Return the potential. */
+ *awhPotential = potential;
+
+ /* Check the sampled histograms and potentially warn user if something is suspicious */
+ warnForHistogramAnomalies(t, step, fplog);
+}
+
+void Bias::restoreStateFromHistory(const AwhBiasHistory *biasHistory,
+ const t_commrec *cr)
+{
+ GMX_RELEASE_ASSERT(thisRankDoesIO_ == MASTER(cr), "The master rank should do I/O, the other ranks should not");
+
+ if (MASTER(cr))
+ {
+ GMX_RELEASE_ASSERT(biasHistory != nullptr, "On the master rank we need a valid history object to restore from");
+ state_.restoreFromHistory(*biasHistory, grid_);
+ }
+
+ if (PAR(cr))
+ {
+ state_.broadcast(cr);
+ }
+}
+
+void Bias::initHistoryFromState(AwhBiasHistory *biasHistory) const
+{
+ GMX_RELEASE_ASSERT(biasHistory != nullptr, "Need a valid biasHistory");
+
+ state_.initHistoryFromState(biasHistory);
+}
+
+void Bias::updateHistory(AwhBiasHistory *biasHistory) const
+{
+ GMX_RELEASE_ASSERT(biasHistory != nullptr, "Need a valid biasHistory");
+
+ state_.updateHistory(biasHistory, grid_);
+}
+
+Bias::Bias(int biasIndexInCollection,
+ const AwhParams &awhParams,
+ const AwhBiasParams &awhBiasParams,
+ const std::vector<DimParams> &dimParamsInit,
+ double beta,
+ double mdTimeStep,
+ int numSharingSimulations,
+ const std::string &biasInitFilename,
+ ThisRankWillDoIO thisRankWillDoIO,
+ BiasParams::DisableUpdateSkips disableUpdateSkips) :
+ dimParams_(dimParamsInit),
+ grid_(dimParamsInit, awhBiasParams.dimParams),
+ params_(awhParams, awhBiasParams, dimParams_, beta, mdTimeStep, disableUpdateSkips, numSharingSimulations, grid_.axis(), biasIndexInCollection),
+ state_(awhBiasParams, params_.initialHistogramSize, dimParams_, grid_),
+ thisRankDoesIO_(thisRankWillDoIO == ThisRankWillDoIO::Yes),
+ tempWorkSpace_(),
+ numWarningsIssued_(0)
+{
+ /* For a global update updateList covers all points, so reserve that */
+ updateList_.reserve(grid_.numPoints());
+
+ state_.initGridPointState(awhBiasParams, dimParams_, grid_, params_, biasInitFilename, awhParams.numBias);
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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
+ * Declares the Bias class.
+ *
+ * This class is essentially a wrapper around the BiasState class.
+ * In addition to BiasState, it holds all data that BiasState needs
+ * to update the bias. Interaction of the outside world, such as updating
+ * BiasState or extracting bias data all happen through Bias.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_BIAS_H
+#define GMX_AWH_BIAS_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "biasparams.h"
+#include "biasstate.h"
+#include "dimparams.h"
+#include "grid.h"
+
+struct gmx_multisim_t;
+struct t_commrec;
+
+namespace gmx
+{
+
+struct AwhBiasHistory;
+struct AwhBiasParams;
+struct AwhHistory;
+struct AwhParams;
+struct AwhPointStateHistory;
+class Grid;
+class GridAxis;
+class PointState;
+
+/*! \internal
+ * \brief A bias acting on a multidimensional coordinate.
+ *
+ * At each step AWH should provide its biases with updated
+ * values of their coordinates. Each bias provides AWH with an updated
+ * bias forces and the corresponding potential.
+ *
+ * See the user manual for details on the algorithm and equations.
+ *
+ * The bias is responsible for keeping and updating a free energy estimate
+ * along the coordinate. The bias potential is basically a function of the
+ * free energy estimate and so also changes by the update.
+ * The free energy update is based on information from coordinate samples
+ * collected at a constant bias potential, between updates.
+ *
+ * The bias keeps a grid with coordinate points that organizes spatial
+ * information about the coordinate. The grid has the the same geometry
+ * as the coordinate, i.e. they have the same dimensionality and periodicity
+ * (if any). The number of points in the grid sets the resolution of
+ * the collected data and its extent defines the sampling region of interest.
+ *
+ * Each coordinate point has further statistical properties and function values
+ * which a grid point does not know about. E.g., for the bias each coordinate point
+ * is associated with values of the bias, free energy and target distribution,
+ * accumulated sampling weight, etc. For this the bias attaches to each grid
+ * point a state. The grid + vector of point states are the bias coordinate points.
+ *
+ * The bias has a fairly complex global state keeping track of where
+ * the system (coordinate) currently is (CoordState), where it has
+ * sampled since the last update (BiasState) and controlling the free energy
+ * convergence rate (HistogramSize).
+ *
+ * Partly, the complexity comes from the bias having two convergence stages:
+ * an initial stage which in an heuristic, non-deterministic way restricts
+ * the early convergence rate for sake of robustness; and a final stage
+ * where the convergence rate is constant. The length of the initial stage
+ * depends on the sampling and is unknown beforehand.
+ *
+ * Another complexity comes from the fact that coordinate points,
+ * for sake of efficiency in the case of many grid points, are typically
+ * only accessed in recently sampled regions even though the free energy
+ * update is inherently global and affects all points.
+ * The bias allows points thay are non-local at the time the update
+ * was issued to postpone ("skip", as it is called in the code) the update.
+ * A non-local point is defined as a point which has not been sampled since
+ * the last update. Local points are points that have been sampled since
+ * the last update. The (current) set of local points are kept track of by
+ * the bias state and reset after every update. An update is called local
+ * if it only updates local points. Non-local points will temporarily "skip"
+ * the update until next time they are local (or when a global update
+ * is issued). For this to work, the bias keeps a global "clock"
+ * (in HistogramSize) of the number of issued updates. Each PointState
+ * also has its own local "clock" with the counting the number of updates
+ * it has pulled through. When a point updates its state it asserts
+ * that its local clock is synchronized with the global clock.
+ */
+class Bias
+{
+ public:
+ //! Enum for requesting Bias set up with(out) I/O on this rank.
+ enum class ThisRankWillDoIO
+ {
+ No, //!< This rank will not do I/O.
+ Yes //!< This rank will do I/O.
+ };
+
+ /*! \brief
+ * Constructor.
+ *
+ * \param[in] biasIndexInCollection Index of the bias in collection.
+ * \param[in] awhParams AWH parameters.
+ * \param[in] awhBiasParams Bias parameters.
+ * \param[in] dimParams Bias dimension parameters.
+ * \param[in] beta 1/(k_B T).
+ * \param[in] mdTimeStep The MD time step.
+ * \param[in] numSharingSimulations The number of simulations to share the bias across.
+ * \param[in] biasInitFilename Name of file to read PMF and target from.
+ * \param[in] thisRankWillDoIO Tells whether this MPI rank will do I/O (checkpointing, AWH output), normally (only) the master rank does I/O.
+ * \param[in] disableUpdateSkips If to disable update skips, useful for testing.
+ */
+ Bias(int biasIndexInCollection,
+ const AwhParams &awhParams,
+ const AwhBiasParams &awhBiasParams,
+ const std::vector<DimParams> &dimParams,
+ double beta,
+ double mdTimeStep,
+ int numSharingSimulations,
+ const std::string &biasInitFilename,
+ ThisRankWillDoIO thisRankWillDoIO,
+ BiasParams::DisableUpdateSkips disableUpdateSkips = BiasParams::DisableUpdateSkips::no);
+
+ /*! \brief
+ * Evolves the bias at every step.
+ *
+ * At each step the bias step needs to:
+ * - set the bias force and potential;
+ * - update the free energy and bias if needed;
+ * - reweight samples to extract the PMF.
+ *
+ * \param[in] coordValue The current coordinate value(s).
+ * \param[out] biasForce The bias force.
+ * \param[out] awhPotential Bias potential.
+ * \param[out] potentialJump Change in bias potential for this bias.
+ * \param[in] ms Struct for multi-simulation communication.
+ * \param[in] t Time.
+ * \param[in] step Time step.
+ * \param[in] seed Random seed.
+ * \param[in,out] fplog Log file.
+ */
+ void calcForceAndUpdateBias(const awh_dvec coordValue,
+ awh_dvec biasForce,
+ double *awhPotential,
+ double *potentialJump,
+ const gmx_multisim_t *ms,
+ double t,
+ gmx_int64_t step,
+ gmx_int64_t seed,
+ FILE *fplog);
+
+ /*! \brief
+ * Calculates the convolved bias for a given coordinate value.
+ *
+ * The convolved bias is the effective bias acting on the coordinate.
+ * Since the bias here has arbitrary normalization, this only makes
+ * sense as a relative, to other coordinate values, measure of the bias.
+ *
+ * \param[in] coordValue The coordinate value.
+ * \returns the convolved bias >= -GMX_DOUBLE_MAX.
+ */
+ double calcConvolvedBias(const awh_dvec &coordValue) const
+ {
+ return state_.calcConvolvedBias(dimParams_, grid_, coordValue);
+ }
+
+ /*! \brief
+ * Restore the bias state from history on the master rank and broadcast it.
+ *
+ * \param[in] biasHistory Bias history struct, only allowed to be nullptr on non-master ranks.
+ * \param[in] cr The communication record.
+ */
+ void restoreStateFromHistory(const AwhBiasHistory *biasHistory,
+ const t_commrec *cr);
+
+ /*! \brief
+ * Allocate and initialize a bias history with the given bias state.
+ *
+ * This function will be called at the start of a new simulation.
+ * Note that only constant data will be initialized here.
+ * History data is set by \ref updateHistory.
+ *
+ * \param[in,out] biasHistory AWH history to initialize.
+ */
+ void initHistoryFromState(AwhBiasHistory *biasHistory) const;
+
+ /*! \brief
+ * Update the bias history with the current state.
+ *
+ * \param[out] biasHistory Bias history struct.
+ */
+ void updateHistory(AwhBiasHistory *biasHistory) const;
+
+ /*! \brief
+ * Do all previously skipped updates.
+ * Public for use by tests.
+ */
+ void doSkippedUpdatesForAllPoints();
+
+ //! Returns the dimensionality of the bias.
+ inline int ndim() const
+ {
+ return dimParams_.size();
+ }
+
+ /*! \brief Returns the dimension parameters.
+ */
+ inline const std::vector<DimParams> &dimParams() const
+ {
+ return dimParams_;
+ }
+
+ //! Returns the bias parameters
+ inline const BiasParams ¶ms() const
+ {
+ return params_;
+ }
+
+ //! Returns the global state of the bias.
+ inline const BiasState &state() const
+ {
+ return state_;
+ }
+
+ //! Returns the index of the bias.
+ inline int biasIndex() const
+ {
+ return params_.biasIndex;
+ }
+
+ private:
+ /*! \brief
+ * Performs statistical checks on the collected histograms and warns if issues are detected.
+ *
+ * \param[in] t Time.
+ * \param[in] step Time step.
+ * \param[in,out] fplog Output file for warnings.
+ */
+ void warnForHistogramAnomalies(double t,
+ gmx_int64_t step,
+ FILE *fplog);
+
+ /* Data members. */
+ private:
+ const std::vector<DimParams> dimParams_; /**< Parameters for each dimension. */
+ const Grid grid_; /**< The multidimensional grid organizing the coordinate point locations. */
+
+ const BiasParams params_; /**< Constant parameters for the method. */
+
+ BiasState state_; /**< The state, both global and of the grid points */
+ std::vector<int> updateList_; /**< List of points for update for temporary use (could be made another tempWorkSpace) */
+
+ const bool thisRankDoesIO_; /**< Tells whether this MPI rank will do I/O (checkpointing, AWH output) */
+
+ /* Temporary working vector used during the update.
+ * This only here to avoid allocation at every MD step.
+ */
+ std::vector<double> tempWorkSpace_; /**< Working vector of doubles. */
+
+ int numWarningsIssued_; /**< The number of warning issued in the current run. */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_BIAS_H */
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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 the initialization of the BiasParams class.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#include "gmxpre.h"
+
+#include "biasparams.h"
+
+#include <cmath>
+
+#include <algorithm>
+
+#include "gromacs/math/functions.h"
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "grid.h"
+
+namespace gmx
+{
+
+bool BiasParams::isCheckStep(std::size_t numPointsInHistogram,
+ gmx_int64_t step) const
+{
+ int numStepsUpdateFreeEnergy = numSamplesUpdateFreeEnergy_*numStepsSampleCoord_;
+ int numStepsCheck = (1 + numPointsInHistogram/numSamplesUpdateFreeEnergy_)*numStepsUpdateFreeEnergy;
+
+ if (step > 0 && step % numStepsCheck == 0)
+ {
+ GMX_ASSERT(isUpdateFreeEnergyStep(step), "We should only check at free-energy update steps");
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+namespace
+{
+
+/*! \brief Determines the interval for updating the target distribution.
+ *
+ * The interval value is based on the target distrbution type
+ * (this could be made a user-option but there is most likely
+ * no big need for tweaking this for most users).
+ *
+ * \param[in] awhParams AWH parameters.
+ * \param[in] awhBiasParams Bias parameters.
+ * \returns the target update interval in steps.
+ */
+gmx_int64_t calcTargetUpdateInterval(const AwhParams &awhParams,
+ const AwhBiasParams &awhBiasParams)
+{
+ gmx_int64_t numStepsUpdateTarget = 0;
+ /* Set the target update frequency based on the target distrbution type
+ * (this could be made a user-option but there is most likely no big need
+ * for tweaking this for most users).
+ */
+ switch (awhBiasParams.eTarget)
+ {
+ case eawhtargetCONSTANT:
+ numStepsUpdateTarget = 0;
+ break;
+ case eawhtargetCUTOFF:
+ case eawhtargetBOLTZMANN:
+ /* Updating the target generally requires updating the whole grid so to keep the cost down
+ we generally update the target less often than the free energy (unless the free energy
+ update step is set to > 100 samples). */
+ numStepsUpdateTarget = std::max(100 % awhParams.numSamplesUpdateFreeEnergy,
+ awhParams.numSamplesUpdateFreeEnergy)*awhParams.nstSampleCoord;
+ break;
+ case eawhtargetLOCALBOLTZMANN:
+ /* The target distribution is set equal to the reference histogram which is updated every free energy update.
+ So the target has to be updated at the same time. This leads to a global update each time because it is
+ assumed that a target distribution update can take any form. This is a bit unfortunate for a "local"
+ target distribution. One could avoid the global update by making a local target update function (and
+ postponing target updates for non-local points as for the free energy update). We avoid such additions
+ for now and accept that this target type always does global updates. */
+ numStepsUpdateTarget = awhParams.numSamplesUpdateFreeEnergy*awhParams.nstSampleCoord;
+ break;
+ default:
+ GMX_RELEASE_ASSERT(false, "Unknown AWH target type");
+ break;
+ }
+
+ return numStepsUpdateTarget;
+}
+
+/*! \brief
+ * Returns an approximation of the geometry factor used for initializing the AWH update size.
+ *
+ * The geometry factor is defined as the following sum of Gaussians:
+ * sum_{k!=0} exp(-0.5*(k*pi*x)^2)/(pi*k)^2,
+ * where k is a xArray.size()-dimensional integer vector with k_i in {0,1,..}.
+ *
+ * \param[in] xArray Array to evaluate.
+ * \returns the geometry factor.
+ */
+double gaussianGeometryFactor(gmx::ArrayRef<const double> xArray)
+{
+ /* For convenience we give the geometry factor function a name: zeta(x) */
+ constexpr size_t tableSize = 5;
+ std::array<const double, tableSize> xTabulated =
+ { {1e-5, 1e-4, 1e-3, 1e-2, 1e-1} };
+ std::array<const double, tableSize> zetaTable1d =
+ { { 0.166536811948, 0.16653116886, 0.166250075882, 0.162701098306, 0.129272430287 } };
+ std::array<const double, tableSize> zetaTable2d =
+ { { 2.31985974274, 1.86307292523, 1.38159772648, 0.897554759158, 0.405578211115 } };
+
+ gmx::ArrayRef<const double> zetaTable;
+
+ if (xArray.size() == 1)
+ {
+ zetaTable = zetaTable1d;
+ }
+ else if (xArray.size() == 2)
+ {
+ zetaTable = zetaTable2d;
+ }
+ else
+ {
+ /* TODO... but this is anyway a rough estimate and > 2 dimensions is not so popular. */
+ zetaTable = zetaTable2d;
+ }
+
+ /* TODO. Really zeta is a function of an ndim-dimensional vector x and we shoudl have a ndim-dimensional lookup-table.
+ Here we take the geometric average of the components of x which is ok if the x-components are not very different. */
+ double xScalar = 1;
+ for (const double &x : xArray)
+ {
+ xScalar *= x;
+ }
+
+ GMX_ASSERT(xArray.size() > 0, "We should have a non-empty input array");
+ xScalar = std::pow(xScalar, 1.0/xArray.size());
+
+ /* Look up zeta(x) */
+ size_t xIndex = 0;
+ while ((xIndex < xTabulated.size()) && (xScalar > xTabulated[xIndex]))
+ {
+ xIndex++;
+ }
+
+ double zEstimate;
+ if (xIndex == xTabulated.size())
+ {
+ /* Take last value */
+ zEstimate = zetaTable[xTabulated.size() - 1];
+ }
+ else if (xIndex == 0)
+ {
+ zEstimate = zetaTable[xIndex];
+ }
+ else
+ {
+ /* Interpolate */
+ double x0 = xTabulated[xIndex - 1];
+ double x1 = xTabulated[xIndex];
+ double w = (xScalar - x0)/(x1 - x0);
+ zEstimate = w*zetaTable[xIndex - 1] + (1 - w)*zetaTable[xIndex];
+ }
+
+ return zEstimate;
+}
+
+/*! \brief
+ * Estimate a reasonable initial reference weight histogram size.
+ *
+ * \param[in] dimParams Parameters for the dimensions of the coordinate.
+ * \param[in] awhBiasParams Bias parameters.
+ * \param[in] gridAxis The Grid axes.
+ * \param[in] beta 1/(k_B T).
+ * \param[in] samplingTimestep Sampling frequency of probability weights.
+ * \returns estimate of initial histogram size.
+ */
+double getInitialHistogramSizeEstimate(const std::vector<DimParams> &dimParams,
+ const AwhBiasParams &awhBiasParams,
+ const std::vector<GridAxis> &gridAxis,
+ double beta,
+ double samplingTimestep)
+{
+ /* Get diffusion factor */
+ double crossingTime = 0.;
+ std::vector<double> x;
+ for (size_t d = 0; d < gridAxis.size(); d++)
+ {
+ double axisLength = gridAxis[d].length();
+ if (axisLength > 0)
+ {
+ crossingTime += awhBiasParams.dimParams[d].diffusion/(axisLength*axisLength);
+ /* The sigma of the Gaussian distribution in the umbrella */
+ double sigma = 1./std::sqrt(dimParams[d].betak);
+ x.push_back(sigma/axisLength);
+ }
+ }
+ GMX_RELEASE_ASSERT(crossingTime > 0, "We need at least one dimension with non-zero length");
+ double errorInitialInKT = beta*awhBiasParams.errorInitial;
+ double histogramSize = gaussianGeometryFactor(x)/(crossingTime*gmx::square(errorInitialInKT)*samplingTimestep);
+
+ return histogramSize;
+}
+
+/*! \brief Returns the number of simulations sharing bias updates.
+ *
+ * \param[in] awhBiasParams Bias parameters.
+ * \param[in] numSharingSimulations The number of simulations to share the bias across.
+ * \returns the number of shared updates.
+ */
+int getNumSharedUpdate(const AwhBiasParams &awhBiasParams,
+ int numSharingSimulations)
+{
+ GMX_RELEASE_ASSERT(numSharingSimulations >= 1, "We should ''share'' at least with ourselves");
+
+ int numShared = 1;
+
+ if (awhBiasParams.shareGroup > 0)
+ {
+ /* We do not yet support sharing within a simulation */
+ int numSharedWithinThisSimulation = 1;
+ numShared = numSharingSimulations*numSharedWithinThisSimulation;
+ }
+
+ return numShared;
+}
+
+} // namespace
+
+BiasParams::BiasParams(const AwhParams &awhParams,
+ const AwhBiasParams &awhBiasParams,
+ const std::vector<DimParams> &dimParams,
+ double beta,
+ double mdTimeStep,
+ DisableUpdateSkips disableUpdateSkips,
+ int numSharingSimulations,
+ const std::vector<GridAxis> &gridAxis,
+ int biasIndex) :
+ invBeta(beta > 0 ? 1/beta : 0),
+ numStepsSampleCoord_(awhParams.nstSampleCoord),
+ numSamplesUpdateFreeEnergy_(awhParams.numSamplesUpdateFreeEnergy),
+ numStepsUpdateTarget_(calcTargetUpdateInterval(awhParams, awhBiasParams)),
+ eTarget(awhBiasParams.eTarget),
+ freeEnergyCutoffInKT(beta*awhBiasParams.targetCutoff),
+ temperatureScaleFactor(awhBiasParams.targetBetaScaling),
+ idealWeighthistUpdate(eTarget != eawhtargetLOCALBOLTZMANN),
+ numSharedUpdate(getNumSharedUpdate(awhBiasParams, numSharingSimulations)),
+ updateWeight(numSamplesUpdateFreeEnergy_*numSharedUpdate),
+ localWeightScaling(eTarget == eawhtargetLOCALBOLTZMANN ? temperatureScaleFactor : 1),
+ initialHistogramSize(getInitialHistogramSizeEstimate(dimParams, awhBiasParams, gridAxis, beta, numStepsSampleCoord_*mdTimeStep)),
+ convolveForce(awhParams.ePotential == eawhpotentialCONVOLVED),
+ biasIndex(biasIndex),
+ disableUpdateSkips_(disableUpdateSkips == DisableUpdateSkips::yes)
+{
+ if (beta <= 0)
+ {
+ GMX_THROW(InvalidInputError("To use AWH, the beta=1/(k_B T) should be > 0"));
+ }
+
+ for (int d = 0; d < awhBiasParams.ndim; d++)
+ {
+ double coverRadiusInNm = 0.5*awhBiasParams.dimParams[d].coverDiameter;
+ double spacing = gridAxis[d].spacing();
+ coverRadius_[d] = spacing > 0 ? static_cast<int>(std::round(coverRadiusInNm/spacing)) : 0;
+ }
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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
+ * Declares the BiasParams class.
+ *
+ * This class holds the parameters for the bias. Most are direct copies
+ * of the input that the user provided. Some are a combination of user
+ * input and properties of the simulated system.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_BIASPARAMS_H
+#define GMX_AWH_BIASPARAMS_H
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+#include "dimparams.h"
+
+namespace gmx
+{
+
+struct AwhBiasParams;
+struct AwhParams;
+struct DimParams;
+class GridAxis;
+
+/*! \internal \brief Constant parameters for the bias.
+ */
+class BiasParams
+{
+ public:
+ /*! \brief Switch to turn off update skips, useful for testing.
+ */
+ enum class DisableUpdateSkips
+ {
+ no, /**< Allow update skips (when supported by the method) */
+ yes /**< Disable update skips */
+ };
+
+ /*! \brief
+ * Check if the parameters permit skipping updates.
+ *
+ * Generally, we can skip updates of points that are non-local
+ * at the time of the update if we for later times, when the points
+ * with skipped updates have become local, know exactly how to apply
+ * the previous updates. The free energy updates only depend
+ * on local sampling, but the histogram rescaling factors
+ * generally depend on the histogram size (all samples).
+ * If the histogram size is kept constant or the scaling factors
+ * are trivial, this is not a problem. However, if the histogram growth
+ * is scaled down by some factor the size at the time of the update
+ * needs to be known. It would be fairly simple to, for a deterministically
+ * growing histogram, backtrack and calculate this value, but currently
+ * we just disallow this case. This is not a restriction because it
+ * only affects the local Boltzmann target type for which every update
+ * is currently anyway global because the target is always updated globally.
+ *
+ * \returns true when we can skip updates.
+ */
+ inline bool skipUpdates() const
+ {
+ return (!disableUpdateSkips_ && localWeightScaling == 1);
+ }
+
+ /*! \brief
+ * Returns the the radius that needs to be sampled around a point before it is considered covered.
+ */
+ inline const awh_ivec &coverRadius() const
+ {
+ return coverRadius_;
+ }
+
+ /*! \brief
+ * Returns whether we should sample the coordinate.
+ *
+ * \param[in] step The MD step number.
+ */
+ inline bool isSampleCoordStep(gmx_int64_t step) const
+ {
+ return (step > 0 && step % numStepsSampleCoord_ == 0);
+ }
+
+ /*! \brief
+ * Returns whether we should update the free energy.
+ *
+ * \param[in] step The MD step number.
+ */
+ inline bool isUpdateFreeEnergyStep(gmx_int64_t step) const
+ {
+ int stepIntervalUpdateFreeEnergy = numSamplesUpdateFreeEnergy_*numStepsSampleCoord_;
+ return (step > 0 && step % stepIntervalUpdateFreeEnergy == 0);
+ }
+
+ /*! \brief
+ * Returns whether we should update the target distribution.
+ *
+ * \param[in] step The MD step number.
+ */
+ inline bool isUpdateTargetStep(gmx_int64_t step) const
+ {
+ return step % numStepsUpdateTarget_ == 0;
+ }
+
+ /*! \brief
+ * Returns if to do checks, only returns true at free-energy update steps.
+ *
+ * To avoid overhead due to expensive checks, we only do checks when we
+ * have taken at least as many samples as we have points.
+ *
+ * \param[in] numPointsInHistogram The total number of points in the bias histogram.
+ * \param[in] step Time step.
+ * \returns true at steps where checks should be performed.
+ */
+ bool isCheckStep(std::size_t numPointsInHistogram,
+ gmx_int64_t step) const;
+
+ /*! \brief Constructor.
+ *
+ * The local Boltzmann target distibution is defined by
+ * 1) Adding the sampled weights instead of the target weights to the reference weight histogram.
+ * 2) Scaling the weights of these samples by the beta scaling factor.
+ * 3) Setting the target distribution equal the reference weight histogram.
+ * This requires the following special update settings:
+ * localWeightScaling = targetParam
+ * idealWeighthistUpdate = false
+ * Note: these variables could in principle be set to something else also for other target distribution types.
+ * However, localWeightScaling < 1 is in general expected to give lower efficiency and, except for local Boltzmann,
+ * idealWeightHistUpdate = false gives (in my experience) unstable, non-converging results.
+ *
+ * \param[in] awhParams AWH parameters.
+ * \param[in] awhBiasParams Bias parameters.
+ * \param[in] dimParams Bias dimension parameters.
+ * \param[in] beta 1/(k_B T) in units of 1/(kJ/mol), should be > 0.
+ * \param[in] mdTimeStep The MD time step.
+ * \param[in] numSharingSimulations The number of simulations to share the bias across.
+ * \param[in] gridAxis The grid axes.
+ * \param[in] disableUpdateSkips If to disable update skips, useful for testing.
+ * \param[in] biasIndex Index of the bias.
+ */
+ BiasParams(const AwhParams &awhParams,
+ const AwhBiasParams &awhBiasParams,
+ const std::vector<DimParams> &dimParams,
+ double beta,
+ double mdTimeStep,
+ DisableUpdateSkips disableUpdateSkips,
+ int numSharingSimulations,
+ const std::vector<GridAxis> &gridAxis,
+ int biasIndex);
+
+ /* Data members */
+ const double invBeta; /**< 1/beta = kT in kJ/mol */
+ private:
+ const gmx_int64_t numStepsSampleCoord_; /**< Number of steps per coordinate value sample. */
+ const int numSamplesUpdateFreeEnergy_; /**< Number of samples per free energy update. */
+ const gmx_int64_t numStepsUpdateTarget_; /**< Number of steps per updating the target distribution. */
+ public:
+ const int eTarget; /**< Type of target distribution. */
+ const double freeEnergyCutoffInKT; /**< Free energy cut-off in kT for cut-off target distribution. */
+ const double temperatureScaleFactor; /**< Temperature scaling factor for temperature scaled targed distributions. */
+ const bool idealWeighthistUpdate; /**< Update reference weighthistogram using the target distribution? Otherwise use the realized distribution. */
+ const int numSharedUpdate; /**< The number of (multi-)simulations sharing the bias update */
+ const double updateWeight; /**< The probability weight accumulated for each update. */
+ const double localWeightScaling; /**< Scaling factor applied to a sample before adding it to the reference weight histogram (= 1, usually). */
+ const double initialHistogramSize; /**< Initial reference weight histogram size. */
+ private:
+ awh_ivec coverRadius_; /**< The radius (in points) that needs to be sampled around a point before it is considered covered. */
+ public:
+ const bool convolveForce; /**< True if we convolve the force, false means use MC between umbrellas. */
+ const int biasIndex; /**< Index of the bias, used as a second random seed and for priting. */
+ private:
+ const bool disableUpdateSkips_; /**< If true, we disallow update skips, even when the method supports it. */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_BIASPARAMS_H */
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017, 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 bias sharing checking functionality.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#include "gmxpre.h"
+
+#include "biassharing.h"
+
+#include <vector>
+
+#include "gromacs/gmxlib/network.h"
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/mdtypes/commrec.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace gmx
+{
+
+bool haveBiasSharingWithinSimulation(const AwhParams &awhParams)
+{
+ bool haveSharing = false;
+
+ for (int k = 0; k < awhParams.numBias; k++)
+ {
+ int shareGroup = awhParams.awhBiasParams[k].shareGroup;
+ if (shareGroup > 0)
+ {
+ for (int i = k + 1; i < awhParams.numBias; i++)
+ {
+ if (awhParams.awhBiasParams[i].shareGroup == shareGroup)
+ {
+ haveSharing = true;
+ }
+ }
+ }
+ }
+
+ return haveSharing;
+}
+
+void biasesAreCompatibleForSharingBetweenSimulations(const AwhParams &awhParams,
+ const std::vector<size_t> &pointSize,
+ const gmx_multisim_t *multiSimComm)
+{
+ const int numSim = multiSimComm->nsim;
+
+ /* We currently enforce subsequent shared biases to have consecutive
+ * share-group values starting at 1. This means we can reduce shared
+ * biases in order over the ranks and it does not restrict possibilities.
+ */
+ int numShare = 0;
+ for (int b = 0; b < awhParams.numBias; b++)
+ {
+ int group = awhParams.awhBiasParams[b].shareGroup;
+ if (group > 0)
+ {
+ numShare++;
+ if (group != numShare)
+ {
+ GMX_THROW(InvalidInputError("AWH biases that are shared should use consequetive share-group values starting at 1"));
+ }
+ }
+ }
+ std::vector<int> numShareAll(numSim);
+ numShareAll[multiSimComm->sim] = numShare;
+ gmx_sumi_sim(numShareAll.size(), numShareAll.data(), multiSimComm);
+ for (int sim = 1; sim < numSim; sim++)
+ {
+ if (numShareAll[sim] != numShareAll[0])
+ {
+ GMX_THROW(InvalidInputError("Different simulations attempt to share different number of biases"));
+ }
+ }
+
+ std::vector<int> intervals(numSim*2);
+ intervals[numSim*0 + multiSimComm->sim] = awhParams.nstSampleCoord;
+ intervals[numSim*1 + multiSimComm->sim] = awhParams.numSamplesUpdateFreeEnergy;
+ gmx_sumi_sim(intervals.size(), intervals.data(), multiSimComm);
+ for (int sim = 1; sim < numSim; sim++)
+ {
+ if (intervals[sim] != intervals[0])
+ {
+ GMX_THROW(InvalidInputError("All simulations should have the same AWH sample interval"));
+ }
+ if (intervals[numSim + sim] != intervals[numSim])
+ {
+ GMX_THROW(InvalidInputError("All simulations should have the same AWH free-energy update interval"));
+ }
+ }
+
+ /* Check the point sizes. This is a sufficient condition for running
+ * as shared multi-sim run. No physics checks are performed here.
+ */
+ for (int b = 0; b < awhParams.numBias; b++)
+ {
+ if (awhParams.awhBiasParams[b].shareGroup > 0)
+ {
+ std::vector<gmx_int64_t> pointSizes(numSim);
+ pointSizes[multiSimComm->sim] = pointSize[b];
+ gmx_sumli_sim(pointSizes.size(), pointSizes.data(), multiSimComm);
+ for (int sim = 1; sim < numSim; sim++)
+ {
+ if (pointSizes[sim] != pointSizes[0])
+ {
+ GMX_THROW(InvalidInputError(gmx::formatString("Shared AWH bias %d has different grid sizes in different simulations\n", b + 1)));
+ }
+ }
+ }
+ }
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017, 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
+ * Declares functions to check bias sharing properties.
+ *
+ * This actual sharing of biases is currently implemeted in BiasState.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_BIASSHARING_H
+#define GMX_AWH_BIASSHARING_H
+
+#include <cstddef>
+
+#include <vector>
+
+struct gmx_multisim_t;
+
+namespace gmx
+{
+
+struct AwhParams;
+
+/*! \brief Returns if any bias is sharing within a simulation.
+ *
+ * \param[in] awhParams The AWH parameters.
+ */
+bool haveBiasSharingWithinSimulation(const AwhParams &awhParams);
+
+/*! \brief Checks if biases are compatible for sharing between simulations, throws if not.
+ *
+ * Should be called simultaneously on the master rank of every simulation.
+ * Note that this only checks for technical compatibility. It is up to
+ * the user to check that the sharing physically makes sense.
+ * Throws an exception when shared biases are not compatible.
+ *
+ * \param[in] awhParams The AWH parameters.
+ * \param[in] pointSize Vector of grid-point sizes for each bias.
+ * \param[in] multiSimComm Struct for multi-simulation communication.
+ */
+void biasesAreCompatibleForSharingBetweenSimulations(const AwhParams &awhParams,
+ const std::vector<size_t> &pointSize,
+ const gmx_multisim_t *multiSimComm);
+
+} // namespace gmx
+
+#endif /* GMX_AWH_BIASSHARING_H */
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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 the BiasState class.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#include "gmxpre.h"
+
+#include "biasstate.h"
+
+#include <assert.h>
+
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include <algorithm>
+
+#include "gromacs/fileio/gmxfio.h"
+#include "gromacs/fileio/xvgr.h"
+#include "gromacs/gmxlib/network.h"
+#include "gromacs/math/utilities.h"
+#include "gromacs/mdtypes/awh-history.h"
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/mdtypes/commrec.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/smalloc.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "grid.h"
+#include "pointstate.h"
+
+namespace gmx
+{
+
+void BiasState::getPmf(std::vector<float> *pmf) const
+{
+ /* The PMF is just the negative of the log of the sampled PMF histogram.
+ * Points with zero target weight are ignored, they will mostly contain noise.
+ */
+
+ pmf->resize(points_.size());
+
+ for (size_t i = 0; i < points_.size(); i++)
+ {
+ (*pmf)[i] = points_[i].inTargetRegion() ? -points_[i].logPmfSum() : GMX_FLOAT_MAX;
+ }
+}
+
+namespace
+{
+
+/*! \brief
+ * Sum PMF over multiple simulations, when requested.
+ *
+ * \param[in,out] pointState The state of the points in the bias.
+ * \param[in] numSharedUpdate The number of biases sharing the histogram.
+ * \param[in] multiSimComm Struct for multi-simulation communication.
+ */
+void sumPmf(gmx::ArrayRef<PointState> pointState,
+ int numSharedUpdate,
+ const gmx_multisim_t *multiSimComm)
+{
+ if (numSharedUpdate == 1)
+ {
+ return;
+ }
+ GMX_ASSERT(multiSimComm != nullptr && numSharedUpdate % multiSimComm->nsim == 0, "numSharedUpdate should be a multiple of multiSimComm->nsim");
+ GMX_ASSERT(numSharedUpdate == multiSimComm->nsim, "Sharing within a simulation is not implemented (yet)");
+
+ std::vector<double> buffer(pointState.size());
+
+ /* Need to temporarily exponentiate the log weights to sum over simulations */
+ for (size_t i = 0; i < buffer.size(); i++)
+ {
+ buffer[i] = pointState[i].inTargetRegion() ? std::exp(static_cast<float>(pointState[i].logPmfSum())) : 0;
+ }
+
+ gmx_sumd_sim(buffer.size(), buffer.data(), multiSimComm);
+
+ /* Take log again to get (non-normalized) PMF */
+ double normFac = 1.0/numSharedUpdate;
+ for (size_t i = 0; i < pointState.size(); i++)
+ {
+ if (pointState[i].inTargetRegion())
+ {
+ pointState[i].setLogPmfSum(-std::log(buffer[i]*normFac));
+ }
+ }
+}
+
+/*! \brief
+ * Find the minimum free energy value.
+ *
+ * \param[in] pointState The state of the points.
+ * \returns the minimum free energy value.
+ */
+double freeEnergyMinimumValue(gmx::ArrayRef<const PointState> pointState)
+{
+ double fMin = GMX_DOUBLE_MAX;
+
+ for (auto const &ps : pointState)
+ {
+ if (ps.inTargetRegion() && ps.freeEnergy() < fMin)
+ {
+ fMin = ps.freeEnergy();
+ }
+ }
+
+ return fMin;
+}
+
+/*! \brief
+ * Find the probability weight of a point given a coordinate value.
+ *
+ * The unnormalized weight is given by
+ * w(point|value) = exp(bias(point) - U(value,point)),
+ * where U is a harmonic umbrella potential.
+ *
+ * \param[in] dimParams The bias dimensions parameters
+ * \param[in] points The point state.
+ * \param[in] grid The grid.
+ * \param[in] pointIndex Point to evaluate probability weight for.
+ * \param[in] pointBias Bias for the point (as a log weight).
+ * \param[in] value Coordinate value.
+ * \returns the biased probability weight.
+ */
+double biasedWeightFromPoint(const std::vector<DimParams> &dimParams,
+ const std::vector<PointState> &points,
+ const Grid &grid,
+ int pointIndex,
+ double pointBias,
+ const awh_dvec value)
+{
+ double weight = 0;
+
+ /* Only points in the target reigon have non-zero weight */
+ if (points[pointIndex].inTargetRegion())
+ {
+ double logWeight = pointBias;
+
+ /* Add potential for all parameter dimensions */
+ for (size_t d = 0; d < dimParams.size(); d++)
+ {
+ double dev = getDeviationFromPointAlongGridAxis(grid, d, pointIndex, value[d]);
+ logWeight -= 0.5*dimParams[d].betak*dev*dev;
+ }
+
+ weight = std::exp(logWeight);
+ }
+
+ return weight;
+}
+
+} // namespace
+
+void BiasState::calcConvolvedPmf(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ std::vector<float> *convolvedPmf) const
+{
+ size_t numPoints = grid.numPoints();
+
+ convolvedPmf->resize(numPoints);
+
+ /* Get the PMF to convolve. */
+ std::vector<float> pmf;
+ getPmf(&pmf);
+
+ for (size_t m = 0; m < numPoints; m++)
+ {
+ double freeEnergyWeights = 0;
+ const GridPoint &point = grid.point(m);
+ for (auto &neighbor : point.neighbor)
+ {
+ /* The negative PMF is a positive bias. */
+ double biasNeighbor = -pmf[neighbor];
+
+ /* Add the convolved PMF weights for the neighbors of this point.
+ Note that this function only adds point within the target > 0 region.
+ Sum weights, take the logarithm last to get the free energy. */
+ freeEnergyWeights += biasedWeightFromPoint(dimParams, points_, grid,
+ neighbor, biasNeighbor,
+ point.coordValue);
+ }
+
+ GMX_RELEASE_ASSERT(freeEnergyWeights > 0, "Attempting to do log(<= 0) in AWH convolved PMF calculation.");
+ (*convolvedPmf)[m] = -std::log(static_cast<float>(freeEnergyWeights));
+ }
+}
+
+namespace
+{
+
+/*! \brief
+ * Updates the target distribution for all points.
+ *
+ * The target distribution is always updated for all points
+ * at the same time.
+ *
+ * \param[in,out] pointState The state of all points.
+ * \param[in] params The bias parameters.
+ */
+void updateTargetDistribution(gmx::ArrayRef<PointState> pointState,
+ const BiasParams ¶ms)
+{
+ double freeEnergyCutoff = 0;
+ if (params.eTarget == eawhtargetCUTOFF)
+ {
+ freeEnergyCutoff = freeEnergyMinimumValue(pointState) + params.freeEnergyCutoffInKT;
+ }
+
+ double sumTarget = 0;
+ for (PointState &ps : pointState)
+ {
+ sumTarget += ps.updateTargetWeight(params, freeEnergyCutoff);
+ }
+ GMX_RELEASE_ASSERT(sumTarget > 0, "We should have a non-zero distribution");
+
+ /* Normalize to 1 */
+ double invSum = 1.0/sumTarget;
+ for (PointState &ps : pointState)
+ {
+ ps.scaleTarget(invSum);
+ }
+}
+
+/*! \brief
+ * Puts together a string describing a grid point.
+ *
+ * \param[in] grid The grid.
+ * \param[in] point Grid point index.
+ * \returns a string for the point.
+ */
+std::string gridPointValueString(const Grid &grid, int point)
+{
+ std::string pointString;
+
+ pointString += "(";
+
+ for (int d = 0; d < grid.numDimensions(); d++)
+ {
+ pointString += gmx::formatString("%g", grid.point(point).coordValue[d]);
+ if (d < grid.numDimensions() - 1)
+ {
+ pointString += ",";
+ }
+ else
+ {
+ pointString += ")";
+ }
+ }
+
+ return pointString;
+}
+
+} // namespace
+
+int BiasState::warnForHistogramAnomalies(const Grid &grid,
+ int biasIndex,
+ double t,
+ FILE *fplog,
+ int maxNumWarnings) const
+{
+ const double maxHistogramRatio = 0.5; /* Tolerance for printing a warning about the histogram ratios */
+
+ /* Sum up the histograms and get their normalization */
+ double sumVisits = 0;
+ double sumWeights = 0;
+ for (auto &pointState : points_)
+ {
+ if (pointState.inTargetRegion())
+ {
+ sumVisits += pointState.numVisitsTot();
+ sumWeights += pointState.weightSumTot();
+ }
+ }
+ GMX_RELEASE_ASSERT(sumVisits > 0, "We should have visits");
+ GMX_RELEASE_ASSERT(sumWeights > 0, "We should have weight");
+ double invNormVisits = 1.0/sumVisits;
+ double invNormWeight = 1.0/sumWeights;
+
+ /* Check all points for warnings */
+ int numWarnings = 0;
+ size_t numPoints = grid.numPoints();
+ for (size_t m = 0; m < numPoints; m++)
+ {
+ /* Skip points close to boundary or non-target region */
+ const GridPoint &gridPoint = grid.point(m);
+ bool skipPoint = false;
+ for (size_t n = 0; (n < gridPoint.neighbor.size()) && !skipPoint; n++)
+ {
+ int neighbor = gridPoint.neighbor[n];
+ skipPoint = !points_[neighbor].inTargetRegion();
+ for (int d = 0; (d < grid.numDimensions()) && !skipPoint; d++)
+ {
+ const GridPoint &neighborPoint = grid.point(neighbor);
+ skipPoint =
+ neighborPoint.index[d] == 0 ||
+ neighborPoint.index[d] == grid.axis(d).numPoints() - 1;
+ }
+ }
+
+ /* Warn if the coordinate distribution is less than the target distribution with a certain fraction somewhere */
+ const double relativeWeight = points_[m].weightSumTot()*invNormWeight;
+ const double relativeVisits = points_[m].numVisitsTot()*invNormVisits;
+ if (!skipPoint &&
+ relativeVisits < relativeWeight*maxHistogramRatio)
+ {
+ std::string pointValueString = gridPointValueString(grid, m);
+ std::string warningMessage =
+ gmx::formatString("\nawh%d warning: "
+ "at t = %g ps the obtained coordinate distribution at coordinate value %s "
+ "is less than a fraction %g of the reference distribution at that point. "
+ "If you are not certain about your settings you might want to increase your pull force constant or "
+ "modify your sampling region.\n",
+ biasIndex + 1, t, pointValueString.c_str(), maxHistogramRatio);
+ gmx::TextLineWrapper wrapper;
+ wrapper.settings().setLineLength(c_linewidth);
+ fprintf(fplog, "%s", wrapper.wrapToString(warningMessage).c_str());
+
+ numWarnings++;
+ }
+ if (numWarnings >= maxNumWarnings)
+ {
+ break;
+ }
+ }
+
+ return numWarnings;
+}
+
+double BiasState::calcUmbrellaForceAndPotential(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ int point,
+ awh_dvec force) const
+{
+ double potential = 0;
+ for (size_t d = 0; d < dimParams.size(); d++)
+ {
+ double deviation = getDeviationFromPointAlongGridAxis(grid, d, point, coordState_.coordValue()[d]);
+
+ double k = dimParams[d].k;
+
+ /* Force from harmonic potential 0.5*k*dev^2 */
+ force[d] = -k*deviation;
+ potential += 0.5*k*deviation*deviation;
+ }
+
+ return potential;
+}
+
+void BiasState::calcConvolvedForce(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ const std::vector<double> &probWeightNeighbor,
+ awh_dvec force) const
+{
+ for (size_t d = 0; d < dimParams.size(); d++)
+ {
+ force[d] = 0;
+ }
+
+ /* Only neighboring points have non-negligible contribution. */
+ const std::vector<int> &neighbor = grid.point(coordState_.gridpointIndex()).neighbor;
+ for (size_t n = 0; n < neighbor.size(); n++)
+ {
+ double weightNeighbor = probWeightNeighbor[n];
+ int indexNeighbor = neighbor[n];
+
+ /* Get the umbrella force from this point. The returned potential is ignored here. */
+ awh_dvec forceFromNeighbor;
+ calcUmbrellaForceAndPotential(dimParams, grid, indexNeighbor,
+ forceFromNeighbor);
+
+ /* Add the weighted umbrella force to the convolved force. */
+ for (size_t d = 0; d < dimParams.size(); d++)
+ {
+ force[d] += forceFromNeighbor[d]*weightNeighbor;
+ }
+ }
+}
+
+double BiasState::moveUmbrella(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ const std::vector<double> &probWeightNeighbor,
+ awh_dvec biasForce,
+ gmx_int64_t step,
+ gmx_int64_t seed,
+ int indexSeed)
+{
+ /* Generate and set a new coordinate reference value */
+ coordState_.sampleUmbrellaGridpoint(grid, coordState_.gridpointIndex(), probWeightNeighbor, step, seed, indexSeed);
+
+ awh_dvec newForce;
+ double newPotential =
+ calcUmbrellaForceAndPotential(dimParams, grid, coordState_.umbrellaGridpoint(), newForce);
+
+ /* A modification of the reference value at time t will lead to a different
+ force over t-dt/2 to t and over t to t+dt/2. For high switching rates
+ this means the force and velocity will change signs roughly as often.
+ To avoid any issues we take the average of the previous and new force
+ at steps when the reference value has been moved. E.g. if the ref. value
+ is set every step to (coord dvalue +/- delta) would give zero force.
+ */
+ for (size_t d = 0; d < dimParams.size(); d++)
+ {
+ /* clang thinks newForce[d] can be garbage */
+#ifndef __clang_analyzer__
+ /* Average of the current and new force */
+ biasForce[d] = 0.5*(biasForce[d] + newForce[d]);
+#endif
+ }
+
+ return newPotential;
+}
+
+namespace
+{
+
+/*! \brief
+ * Sets the histogram rescaling factors needed to control the histogram size.
+ *
+ * For sake of robustness, the reference weight histogram can grow at a rate
+ * different from the actual sampling rate. Typically this happens for a limited
+ * initial time, alternatively growth is scaled down by a constant factor for all
+ * times. Since the size of the reference histogram sets the size of the free
+ * energy update this should be reflected also in the PMF. Thus the PMF histogram
+ * needs to be rescaled too.
+ *
+ * This function should only be called by the bias update function or wrapped by a function that
+ * knows what scale factors should be applied when, e.g,
+ * getSkippedUpdateHistogramScaleFactors().
+ *
+ * \param[in] params The bias parameters.
+ * \param[in] newHistogramSize New reference weight histogram size.
+ * \param[in] oldHistogramSize Previous reference weight histogram size (before adding new samples).
+ * \param[out] weightHistScaling Scaling factor for the reference weight histogram.
+ * \param[out] logPmfSumScaling Log of the scaling factor for the PMF histogram.
+ */
+void setHistogramUpdateScaleFactors(const BiasParams ¶ms,
+ double newHistogramSize,
+ double oldHistogramSize,
+ double *weightHistScaling,
+ double *logPmfSumScaling)
+{
+
+ /* The two scaling factors below are slightly different (ignoring the log factor) because the
+ reference and the PMF histogram apply weight scaling differently. The weight histogram
+ applies is locally, i.e. each sample is scaled down meaning all samples get equal weight.
+ It is done this way because that is what target type local Boltzmann (for which
+ target = weight histogram) needs. In contrast, the PMF histogram is rescaled globally
+ by repeatedly scaling down the whole histogram. The reasons for doing it this way are:
+ 1) empirically this is necessary for converging the PMF; 2) since the extraction of
+ the PMF is theoretically only valid for a constant bias, new samples should get more
+ weight than old ones for which the bias is fluctuating more. */
+ *weightHistScaling = newHistogramSize/(oldHistogramSize + params.updateWeight*params.localWeightScaling);
+ *logPmfSumScaling = std::log(newHistogramSize/(oldHistogramSize + params.updateWeight));
+}
+
+} // namespace
+
+void BiasState::getSkippedUpdateHistogramScaleFactors(const BiasParams ¶ms,
+ double *weightHistScaling,
+ double *logPmfSumScaling) const
+{
+ GMX_ASSERT(params.skipUpdates(), "Calling function for skipped updates when skipping updates is not allowed");
+
+ if (inInitialStage())
+ {
+ /* In between global updates the reference histogram size is kept constant so we trivially know what the
+ histogram size was at the time of the skipped update. */
+ double histogramSize = histogramSize_.histogramSize();
+ setHistogramUpdateScaleFactors(params, histogramSize, histogramSize,
+ weightHistScaling, logPmfSumScaling);
+ }
+ else
+ {
+ /* In the final stage, the reference histogram grows at the sampling rate which gives trivial scale factors. */
+ *weightHistScaling = 1;
+ *logPmfSumScaling = 0;
+ }
+}
+
+void BiasState::doSkippedUpdatesForAllPoints(const BiasParams ¶ms)
+{
+ double weightHistScaling;
+ double logPmfsumScaling;
+
+ getSkippedUpdateHistogramScaleFactors(params, &weightHistScaling, &logPmfsumScaling);
+
+ for (auto &pointState : points_)
+ {
+ bool didUpdate = pointState.performPreviouslySkippedUpdates(params, histogramSize_.numUpdates(), weightHistScaling, logPmfsumScaling);
+
+ /* Update the bias for this point only if there were skipped updates in the past to avoid calculating the log unneccessarily */
+ if (didUpdate)
+ {
+ pointState.updateBias();
+ }
+ }
+}
+
+void BiasState::doSkippedUpdatesInNeighborhood(const BiasParams ¶ms,
+ const Grid &grid)
+{
+ double weightHistScaling;
+ double logPmfsumScaling;
+
+ getSkippedUpdateHistogramScaleFactors(params, &weightHistScaling, &logPmfsumScaling);
+
+ /* For each neighbor point of the center point, refresh its state by adding the results of all past, skipped updates. */
+ const std::vector<int> &neighbors = grid.point(coordState_.gridpointIndex()).neighbor;
+ for (auto &neighbor : neighbors)
+ {
+ bool didUpdate = points_[neighbor].performPreviouslySkippedUpdates(params, histogramSize_.numUpdates(), weightHistScaling, logPmfsumScaling);
+
+ if (didUpdate)
+ {
+ points_[neighbor].updateBias();
+ }
+ }
+}
+
+namespace
+{
+
+/*! \brief
+ * Merge update lists from multiple sharing simulations.
+ *
+ * \param[in,out] updateList Update list for this simulation (assumed >= npoints long).
+ * \param[in] numPoints Total number of points.
+ * \param[in] multiSimComm Struct for multi-simulation communication.
+ */
+void mergeSharedUpdateLists(std::vector<int> *updateList,
+ int numPoints,
+ const gmx_multisim_t *multiSimComm)
+{
+ std::vector<int> numUpdatesOfPoint;
+
+ /* Flag the update points of this sim.
+ TODO: we can probably avoid allocating this array and just use the input array. */
+ numUpdatesOfPoint.resize(numPoints, 0);
+ for (auto &pointIndex : *updateList)
+ {
+ numUpdatesOfPoint[pointIndex] = 1;
+ }
+
+ /* Sum over the sims to get all the flagged points */
+ gmx_sumi_sim(numPoints, numUpdatesOfPoint.data(), multiSimComm);
+
+ /* Collect the indices of the flagged points in place. The resulting array will be the merged update list.*/
+ updateList->clear();
+ for (int m = 0; m < numPoints; m++)
+ {
+ if (numUpdatesOfPoint[m] > 0)
+ {
+ updateList->push_back(m);
+ }
+ }
+}
+
+/*! \brief
+ * Generate an update list of points sampled since the last update.
+ *
+ * \param[in] grid The AWH bias.
+ * \param[in] points The point state.
+ * \param[in] originUpdatelist The origin of the rectangular region that has been sampled since last update.
+ * \param[in] endUpdatelist The end of the rectangular that has been sampled since last update.
+ * \param[in,out] updateList Local update list to set (assumed >= npoints long).
+ */
+void makeLocalUpdateList(const Grid &grid,
+ const std::vector<PointState> &points,
+ const awh_ivec originUpdatelist,
+ const awh_ivec endUpdatelist,
+ std::vector<int> *updateList)
+{
+ awh_ivec origin;
+ awh_ivec numPoints;
+
+ /* Define the update search grid */
+ for (int d = 0; d < grid.numDimensions(); d++)
+ {
+ origin[d] = originUpdatelist[d];
+ numPoints[d] = endUpdatelist[d] - originUpdatelist[d] + 1;
+
+ /* Because the end_updatelist is unwrapped it can be > (npoints - 1) so that numPoints can be > npoints in grid.
+ This helps for calculating the distance/number of points but should be removed and fixed when the way of
+ updating origin/end updatelist is changed (see sampleProbabilityWeights). */
+ numPoints[d] = std::min(grid.axis(d).numPoints(), numPoints[d]);
+ }
+
+ /* Make the update list */
+ updateList->clear();
+ int pointIndex = -1;
+ bool pointExists = true;
+ while (pointExists)
+ {
+ pointExists = advancePointInSubgrid(grid, origin, numPoints, &pointIndex);
+
+ if (pointExists && points[pointIndex].inTargetRegion())
+ {
+ updateList->push_back(pointIndex);
+ }
+ }
+}
+
+} // namespace
+
+void BiasState::resetLocalUpdateRange(const Grid &grid)
+{
+ const int gridpointIndex = coordState_.gridpointIndex();
+ for (int d = 0; d < grid.numDimensions(); d++)
+ {
+ /* This gives the minimum range consisting only of the current closest point. */
+ originUpdatelist_[d] = grid.point(gridpointIndex).index[d];
+ endUpdatelist_[d] = grid.point(gridpointIndex).index[d];
+ }
+}
+
+namespace
+{
+
+/*! \brief
+ * Add partial histograms (accumulating between updates) to accumulating histograms.
+ *
+ * \param[in,out] pointState The state of the points in the bias.
+ * \param[in,out] weightSumCovering The weights for checking covering.
+ * \param[in] numSharedUpdate The number of biases sharing the histrogram.
+ * \param[in] multiSimComm Struct for multi-simulation communication.
+ * \param[in] localUpdateList List of points with data.
+ */
+void sumHistograms(gmx::ArrayRef<PointState> pointState,
+ gmx::ArrayRef<double> weightSumCovering,
+ int numSharedUpdate,
+ const gmx_multisim_t *multiSimComm,
+ const std::vector<int> &localUpdateList)
+{
+ /* The covering checking histograms are added before summing over simulations, so that the weights from different
+ simulations are kept distinguishable. */
+ for (int globalIndex : localUpdateList)
+ {
+ weightSumCovering[globalIndex] +=
+ pointState[globalIndex].weightSumIteration();
+ }
+
+ /* Sum histograms over multiple simulations if needed. */
+ if (numSharedUpdate > 1)
+ {
+ GMX_ASSERT(numSharedUpdate == multiSimComm->nsim, "Sharing within a simulation is not implemented (yet)");
+
+ /* Collect the weights and counts in linear arrays to be able to use gmx_sumd_sim. */
+ std::vector<double> weightSum;
+ std::vector<double> coordVisits;
+
+ weightSum.resize(localUpdateList.size());
+ coordVisits.resize(localUpdateList.size());
+
+ for (size_t localIndex = 0; localIndex < localUpdateList.size(); localIndex++)
+ {
+ const PointState &ps = pointState[localUpdateList[localIndex]];
+
+ weightSum[localIndex] = ps.weightSumIteration();
+ coordVisits[localIndex] = ps.numVisitsIteration();
+ }
+
+ gmx_sumd_sim(weightSum.size(), weightSum.data(), multiSimComm);
+ gmx_sumd_sim(coordVisits.size(), coordVisits.data(), multiSimComm);
+
+ /* Transfer back the result */
+ for (size_t localIndex = 0; localIndex < localUpdateList.size(); localIndex++)
+ {
+ PointState &ps = pointState[localUpdateList[localIndex]];
+
+ ps.setPartialWeightAndCount(weightSum[localIndex],
+ coordVisits[localIndex]);
+ }
+ }
+
+ /* Now add the partial counts and weights to the accumulating histograms.
+ Note: we still need to use the weights for the update so we wait
+ with resetting them until the end of the update. */
+ for (int globalIndex : localUpdateList)
+ {
+ pointState[globalIndex].addPartialWeightAndCount();
+ }
+}
+
+/*! \brief
+ * Label points along an axis as covered or not.
+ *
+ * A point is covered if it is surrounded by visited points up to a radius = coverRadius.
+ *
+ * \param[in] visited Visited? For each point.
+ * \param[in] checkCovering Check for covering? For each point.
+ * \param[in] numPoints The number of grid points along this dimension.
+ * \param[in] period Period in number of points.
+ * \param[in] coverRadius Cover radius, in points, needed for defining a point as covered.
+ * \param[in,out] covered In this array elements are 1 for covered points and 0 for non-covered points, this routine assumes that \p covered has at least size \p numPoints.
+ */
+void labelCoveredPoints(const std::vector<bool> &visited,
+ const std::vector<bool> &checkCovering,
+ int numPoints,
+ int period,
+ int coverRadius,
+ gmx::ArrayRef<int> covered)
+{
+ GMX_ASSERT(covered.size() >= static_cast<size_t>(numPoints), "covered should be at least as large as the grid");
+
+ bool haveFirstNotVisited = false;
+ int firstNotVisited = -1;
+ int notVisitedLow = -1;
+ int notVisitedHigh = -1;
+
+ for (int n = 0; n < numPoints; n++)
+ {
+ if (checkCovering[n] && !visited[n])
+ {
+ if (!haveFirstNotVisited)
+ {
+ notVisitedLow = n;
+ firstNotVisited = n;
+ haveFirstNotVisited = true;
+ }
+ else
+ {
+ notVisitedHigh = n;
+
+ /* Have now an interval I = [notVisitedLow,notVisitedHigh] of visited points bounded
+ by unvisited points. The unvisted end points affect the coveredness of the visited
+ with a reach equal to the cover radius. */
+ int notCoveredLow = notVisitedLow + coverRadius;
+ int notCoveredHigh = notVisitedHigh - coverRadius;
+ for (int i = notVisitedLow; i <= notVisitedHigh; i++)
+ {
+ covered[i] = (i > notCoveredLow) && (i < notCoveredHigh);
+ }
+
+ /* Find a new interval to set covering for. Make the notVisitedHigh of this interval the
+ notVisitedLow of the next. */
+ notVisitedLow = notVisitedHigh;
+ }
+ }
+ }
+
+ /* Have labelled all the internal points. Now take care of the boundary regions. */
+ if (!haveFirstNotVisited)
+ {
+ /* No non-visited points <=> all points visited => all points covered. */
+
+ for (int n = 0; n < numPoints; n++)
+ {
+ covered[n] = 1;
+ }
+ }
+ else
+ {
+ int lastNotVisited = notVisitedLow;
+
+ /* For periodic boundaries, non-visited points can influence points
+ on the other side of the boundary so we need to wrap around. */
+
+ /* Lower end. For periodic boundaries the last upper end not visited point becomes the low-end not visited point.
+ For non-periodic boundaries there is no lower end point so a dummy value is used. */
+ int notVisitedHigh = firstNotVisited;
+ int notVisitedLow = period > 0 ? (lastNotVisited - period) : -(coverRadius + 1);
+
+ int notCoveredLow = notVisitedLow + coverRadius;
+ int notCoveredHigh = notVisitedHigh - coverRadius;
+
+ for (int i = 0; i <= notVisitedHigh; i++)
+ {
+ /* For non-periodic boundaries notCoveredLow = -1 will impose no restriction. */
+ covered[i] = (i > notCoveredLow) && (i < notCoveredHigh);
+ }
+
+ /* Upper end. Same as for lower end but in the other direction. */
+ notVisitedHigh = period > 0 ? (firstNotVisited + period) : (numPoints + coverRadius);
+ notVisitedLow = lastNotVisited;
+
+ notCoveredLow = notVisitedLow + coverRadius;
+ notCoveredHigh = notVisitedHigh - coverRadius;
+
+ for (int i = notVisitedLow; i <= numPoints - 1; i++)
+ {
+ /* For non-periodic boundaries notCoveredHigh = numPoints will impose no restriction. */
+ covered[i] = (i > notCoveredLow) && (i < notCoveredHigh);
+ }
+ }
+}
+
+} // namespace
+
+bool BiasState::isSamplingRegionCovered(const BiasParams ¶ms,
+ const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ const gmx_multisim_t *multiSimComm) const
+{
+ /* Allocate and initialize arrays: one for checking visits along each dimension,
+ one for keeping track of which points to check and one for the covered points.
+ Possibly these could be kept as AWH variables to avoid these allocations. */
+ struct CheckDim
+ {
+ std::vector<bool> visited;
+ std::vector<bool> checkCovering;
+ // We use int for the covering array since we might use gmx_sumi_sim.
+ std::vector<int> covered;
+ };
+
+ std::vector<CheckDim> checkDim;
+ checkDim.resize(grid.numDimensions());
+
+ for (int d = 0; d < grid.numDimensions(); d++)
+ {
+ const size_t numPoints = grid.axis(d).numPoints();
+ checkDim[d].visited.resize(numPoints, false);
+ checkDim[d].checkCovering.resize(numPoints, false);
+ checkDim[d].covered.resize(numPoints, 0);
+ }
+
+ /* Set visited points along each dimension and which points should be checked for covering.
+ Specifically, points above the free energy cutoff (if there is one) or points outside
+ of the target region are ignored. */
+
+ /* Set the free energy cutoff */
+ double maxFreeEnergy = GMX_DOUBLE_MAX;
+
+ if (params.eTarget == eawhtargetCUTOFF)
+ {
+ maxFreeEnergy = freeEnergyMinimumValue(points_) + params.freeEnergyCutoffInKT;
+ }
+
+ /* Set the threshold weight for a point to be considered visited. */
+ double weightThreshold = 1;
+ for (int d = 0; d < grid.numDimensions(); d++)
+ {
+ weightThreshold *= grid.axis(d).spacing()*std::sqrt(dimParams[d].betak*0.5*M_1_PI);
+ }
+
+ /* Project the sampling weights onto each dimension */
+ for (size_t m = 0; m < grid.numPoints(); m++)
+ {
+ const PointState &pointState = points_[m];
+
+ for (int d = 0; d < grid.numDimensions(); d++)
+ {
+ int n = grid.point(m).index[d];
+
+ /* Is visited if it was already visited or if there is enough weight at the current point */
+ checkDim[d].visited[n] = checkDim[d].visited[n] || (weightSumCovering_[m] > weightThreshold);
+
+ /* Check for covering if there is at least point in this slice that is in the target region and within the cutoff */
+ checkDim[d].checkCovering[n] = checkDim[d].checkCovering[n] || (pointState.inTargetRegion() && pointState.freeEnergy() < maxFreeEnergy);
+ }
+ }
+
+ /* Label each point along each dimension as covered or not. */
+ for (int d = 0; d < grid.numDimensions(); d++)
+ {
+ labelCoveredPoints(checkDim[d].visited, checkDim[d].checkCovering, grid.axis(d).numPoints(), grid.axis(d).numPointsInPeriod(),
+ params.coverRadius()[d], checkDim[d].covered);
+ }
+
+ /* Now check for global covering. Each dimension needs to be covered separately.
+ A dimension is covered if each point is covered. Multiple simulations collectively
+ cover the points, i.e. a point is covered if any of the simulations covered it.
+ However, visited points are not shared, i.e. if a point is covered or not is
+ determined by the visits of a single simulation. In general the covering criterion is
+ all points covered => all points are surrounded by visited points up to a radius = coverRadius.
+ For 1 simulation, all points covered <=> all points visited. For multiple simulations
+ however, all points visited collectively !=> all points covered, except for coverRadius = 0.
+ In the limit of large coverRadius, all points covered => all points visited by at least one
+ simulation (since no point will be covered until all points have been visited by a
+ single simulation). Basically coverRadius sets how much "connectedness" (or mixing) a point
+ needs with surrounding points before sharing covering information with other simulations. */
+
+ /* Communicate the covered points between sharing simulations if needed. */
+ if (params.numSharedUpdate > 1)
+ {
+ /* For multiple dimensions this may not be the best way to do it. */
+ for (int d = 0; d < grid.numDimensions(); d++)
+ {
+ gmx_sumi_sim(grid.axis(d).numPoints(), checkDim[d].covered.data(), multiSimComm);
+ }
+ }
+
+ /* Now check if for each dimension all points are covered. Break if not true. */
+ bool allPointsCovered = true;
+ for (int d = 0; d < grid.numDimensions() && allPointsCovered; d++)
+ {
+ for (int n = 0; n < grid.axis(d).numPoints() && allPointsCovered; n++)
+ {
+ allPointsCovered = checkDim[d].covered[n];
+ }
+ }
+
+ return allPointsCovered;
+}
+
+/*! \brief
+ * Normalizes the free energy and PMF sum.
+ *
+ * \param[in] pointState The state of the points.
+ */
+static void normalizeFreeEnergyAndPmfSum(std::vector<PointState> *pointState)
+{
+ double minF = freeEnergyMinimumValue(*pointState);
+
+ for (PointState &ps : *pointState)
+ {
+ ps.normalizeFreeEnergyAndPmfSum(minF);
+ }
+}
+
+void BiasState::updateFreeEnergyAndAddSamplesToHistogram(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ const BiasParams ¶ms,
+ const gmx_multisim_t *multiSimComm,
+ double t,
+ gmx_int64_t step,
+ FILE *fplog,
+ std::vector<int> *updateList)
+{
+ /* Note hat updateList is only used in this scope and is always
+ * re-initialized. We do not use a local vector, because that would
+ * cause reallocation every time this funtion is called and the vector
+ * can be the size of the whole grid.
+ */
+
+ /* Make a list of all local points, i.e. those that could have been touched since
+ the last update. These are the points needed for summing histograms below
+ (non-local points only add zeros). For local updates, this will also be the
+ final update list. */
+ makeLocalUpdateList(grid, points_, originUpdatelist_, endUpdatelist_,
+ updateList);
+ if (params.numSharedUpdate > 1)
+ {
+ mergeSharedUpdateLists(updateList, points_.size(), multiSimComm);
+ }
+
+ /* Reset the range for the next update */
+ resetLocalUpdateRange(grid);
+
+ /* Add samples to histograms for all local points and sync simulations if needed */
+ sumHistograms(points_, weightSumCovering_,
+ params.numSharedUpdate, multiSimComm, *updateList);
+
+ sumPmf(points_, params.numSharedUpdate, multiSimComm);
+
+ /* Renormalize the free energy if values are too large. */
+ bool needToNormalizeFreeEnergy = false;
+ for (int &globalIndex : *updateList)
+ {
+ /* We want to keep the absolute value of the free energies to be less c_largePositiveExponent
+ to be able to safely pass these values to exp(). The check below ensures this as long as
+ the free energy values grow less than 0.5*c_largePositiveExponent in a return time to this
+ neighborhood. For reasonable update sizes it's unlikely that this requirement would be
+ broken. */
+ if (std::abs(points_[globalIndex].freeEnergy()) > 0.5*c_largePositiveExponent)
+ {
+ needToNormalizeFreeEnergy = true;
+ break;
+ }
+ }
+
+ /* Update target distribution? */
+ bool needToUpdateTargetDistribution =
+ (params.eTarget != eawhtargetCONSTANT &&
+ params.isUpdateTargetStep(step));
+
+ /* In the initial stage, the histogram grows dynamically as a function of the number of coverings. */
+ bool detectedCovering = false;
+ if (inInitialStage())
+ {
+ detectedCovering = (params.isCheckStep(points_.size(), step) &&
+ isSamplingRegionCovered(params, dimParams, grid, multiSimComm));
+ }
+
+ /* The weighthistogram size after this update. */
+ double newHistogramSize = histogramSize_.newHistogramSize(params, t, detectedCovering, points_, weightSumCovering_, fplog);
+
+ /* Make the update list. Usually we try to only update local points,
+ * but if the update has non-trivial or non-deterministic effects
+ * on non-local points a global update is needed. This is the case when:
+ * 1) a covering occurred in the initial stage, leading to non-trivial
+ * histogram rescaling factors; or
+ * 2) the target distribution will be updated, since we don't make any
+ * assumption on its form; or
+ * 3) the AWH parameters are such that we never attempt to skip non-local
+ * updates; or
+ * 4) the free energy values have grown so large that a renormalization
+ * is needed.
+ */
+ if (needToUpdateTargetDistribution || detectedCovering || !params.skipUpdates() || needToNormalizeFreeEnergy)
+ {
+ /* Global update, just add all points. */
+ updateList->clear();
+ for (size_t m = 0; m < points_.size(); m++)
+ {
+ if (points_[m].inTargetRegion())
+ {
+ updateList->push_back(m);
+ }
+ }
+ }
+
+ /* Set histogram scale factors. */
+ double weightHistScalingSkipped = 0;
+ double logPmfsumScalingSkipped = 0;
+ if (params.skipUpdates())
+ {
+ getSkippedUpdateHistogramScaleFactors(params, &weightHistScalingSkipped, &logPmfsumScalingSkipped);
+ }
+ double weightHistScalingNew;
+ double logPmfsumScalingNew;
+ setHistogramUpdateScaleFactors(params, newHistogramSize, histogramSize_.histogramSize(),
+ &weightHistScalingNew, &logPmfsumScalingNew);
+
+ /* Update free energy and reference weight histogram for points in the update list. */
+ for (int pointIndex : *updateList)
+ {
+ PointState *pointStateToUpdate = &points_[pointIndex];
+
+ /* Do updates from previous update steps that were skipped because this point was at that time non-local. */
+ if (params.skipUpdates())
+ {
+ pointStateToUpdate->performPreviouslySkippedUpdates(params, histogramSize_.numUpdates(), weightHistScalingSkipped, logPmfsumScalingSkipped);
+ }
+
+ /* Now do an update with new sampling data. */
+ pointStateToUpdate->updateWithNewSampling(params, histogramSize_.numUpdates(), weightHistScalingNew, logPmfsumScalingNew);
+ }
+
+ /* Only update the histogram size after we are done with the local point updates */
+ histogramSize_.setHistogramSize(newHistogramSize, weightHistScalingNew);
+
+ if (needToNormalizeFreeEnergy)
+ {
+ normalizeFreeEnergyAndPmfSum(&points_);
+ }
+
+ if (needToUpdateTargetDistribution)
+ {
+ /* The target distribution is always updated for all points at once. */
+ updateTargetDistribution(points_, params);
+ }
+
+ /* Update the bias. The bias is updated separately and last since it simply a function of
+ the free energy and the target distribution and we want to avoid doing extra work. */
+ for (int pointIndex : *updateList)
+ {
+ points_[pointIndex].updateBias();
+ }
+
+ /* Increase the update counter. */
+ histogramSize_.incrementNumUpdates();
+}
+
+double BiasState::updateProbabilityWeightsAndConvolvedBias(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ std::vector<double> *weight) const
+{
+ /* Only neighbors of the current coordinate value will have a non-negligible chance of getting sampled */
+ const std::vector<int> &neighbors = grid.point(coordState_.gridpointIndex()).neighbor;
+
+ weight->resize(neighbors.size());
+
+ double weightSum = 0;
+ for (size_t n = 0; n < neighbors.size(); n++)
+ {
+ int neighbor = neighbors[n];
+ (*weight)[n] = biasedWeightFromPoint(dimParams, points_, grid,
+ neighbor, points_[neighbor].bias(),
+ coordState_.coordValue());
+ weightSum += (*weight)[n];
+ }
+ GMX_RELEASE_ASSERT(weightSum > 0, "zero probability weight when updating AWH probability weights.");
+
+ /* Normalize probabilities to sum to 1 */
+ double invWeightSum = 1/weightSum;
+ for (double &w : *weight)
+ {
+ w *= invWeightSum;
+ }
+
+ /* Return the convolved bias */
+ return std::log(weightSum);
+}
+
+double BiasState::calcConvolvedBias(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ const awh_dvec &coordValue) const
+{
+ int point = grid.nearestIndex(coordValue);
+ const GridPoint &gridPoint = grid.point(point);
+
+ /* Sum the probability weights from the neighborhood of the given point */
+ double weightSum = 0;
+ for (int neighbor : gridPoint.neighbor)
+ {
+ weightSum += biasedWeightFromPoint(dimParams, points_, grid,
+ neighbor, points_[neighbor].bias(),
+ coordValue);
+ }
+
+ /* Returns -GMX_DOUBLE_MAX if no neighboring points were in the target region. */
+ return (weightSum > 0) ? std::log(weightSum) : -GMX_DOUBLE_MAX;
+}
+
+void BiasState::sampleProbabilityWeights(const Grid &grid,
+ const std::vector<double> &probWeightNeighbor)
+{
+ const std::vector<int> &neighbor = grid.point(coordState_.gridpointIndex()).neighbor;
+
+ /* Save weights for next update */
+ for (size_t n = 0; n < neighbor.size(); n++)
+ {
+ points_[neighbor[n]].increaseWeightSumIteration(probWeightNeighbor[n]);
+ }
+
+ /* Update the local update range. Two corner points define this rectangular
+ * domain. We need to choose two new corner points such that the new domain
+ * contains both the old update range and the current neighborhood.
+ * In the simplest case when an update is performed every sample,
+ * the update range would simply equal the current neighborhood.
+ */
+ int neighborStart = neighbor[0];
+ int neighborLast = neighbor[neighbor.size() - 1];
+ for (int d = 0; d < grid.numDimensions(); d++)
+ {
+ int origin = grid.point(neighborStart).index[d];
+ int last = grid.point(neighborLast).index[d];
+
+ if (origin > last)
+ {
+ /* Unwrap if wrapped around the boundary (only happens for periodic
+ * boundaries). This has been already for the stored index interval.
+ */
+ /* TODO: what we want to do is to find the smallest the update
+ * interval that contains all points that need to be updated.
+ * This amounts to combining two intervals, the current
+ * [origin, end] update interval and the new touched neighborhood
+ * into a new interval that contains all points from both the old
+ * intervals.
+ *
+ * For periodic boundaries it becomes slightly more complicated
+ * than for closed boundaries because then it needs not be
+ * true that origin < end (so one can't simply relate the origin/end
+ * in the min()/max() below). The strategy here is to choose the
+ * origin closest to a reference point (index 0) and then unwrap
+ * the end index if needed and choose the largest end index.
+ * This ensures that both intervals are in the new interval
+ * but it's not necessarily the smallest.
+ * Currently we solve this by going through each possibility
+ * and checking them.
+ */
+ last += grid.axis(d).numPointsInPeriod();
+ }
+
+ originUpdatelist_[d] = std::min(originUpdatelist_[d], origin);
+ endUpdatelist_[d] = std::max(endUpdatelist_[d], last);
+ }
+}
+
+void BiasState::sampleCoordAndPmf(const Grid &grid,
+ const std::vector<double> &probWeightNeighbor,
+ double convolvedBias)
+{
+ /* Sampling-based deconvolution extracting the PMF.
+ * Update the PMF histogram with the current coordinate value.
+ *
+ * Because of the finite width of the harmonic potential, the free energy
+ * defined for each coordinate point does not exactly equal that of the
+ * actual coordinate, the PMF. However, the PMF can be estimated by applying
+ * the relation exp(-PMF) = exp(-bias_convolved)*P_biased/Z, i.e. by keeping a
+ * reweighted histogram of the coordinate value. Strictly, this relies on
+ * the unknown normalization constant Z being either constant or known. Here,
+ * neither is true except in the long simulation time limit. Empirically however,
+ * it works (mainly because how the PMF histogram is rescaled).
+ */
+
+ /* Only save coordinate data that is in range (the given index is always
+ * in range even if the coordinate value is not).
+ */
+ if (grid.covers(coordState_.coordValue()))
+ {
+ /* Save PMF sum and keep a histogram of the sampled coordinate values */
+ points_[coordState_.gridpointIndex()].samplePmf(convolvedBias);
+ }
+
+ /* Save probability weights for the update */
+ sampleProbabilityWeights(grid, probWeightNeighbor);
+}
+
+void BiasState::initHistoryFromState(AwhBiasHistory *biasHistory) const
+{
+ biasHistory->pointState.resize(points_.size());
+}
+
+void BiasState::updateHistory(AwhBiasHistory *biasHistory,
+ const Grid &grid) const
+{
+ GMX_RELEASE_ASSERT(biasHistory->pointState.size() == points_.size(), "The AWH history setup does not match the AWH state.");
+
+ AwhBiasStateHistory *stateHistory = &biasHistory->state;
+ stateHistory->umbrellaGridpoint = coordState_.umbrellaGridpoint();
+
+ for (size_t m = 0; m < biasHistory->pointState.size(); m++)
+ {
+ AwhPointStateHistory *psh = &biasHistory->pointState[m];
+
+ points_[m].storeState(psh);
+
+ psh->weightsum_covering = weightSumCovering_[m];
+ }
+
+ histogramSize_.storeState(stateHistory);
+
+ stateHistory->origin_index_updatelist = multiDimGridIndexToLinear(grid,
+ originUpdatelist_);
+ stateHistory->end_index_updatelist = multiDimGridIndexToLinear(grid,
+ endUpdatelist_);
+}
+
+void BiasState::restoreFromHistory(const AwhBiasHistory &biasHistory,
+ const Grid &grid)
+{
+ const AwhBiasStateHistory &stateHistory = biasHistory.state;
+
+ coordState_.restoreFromHistory(stateHistory);
+
+ if (biasHistory.pointState.size() != points_.size())
+ {
+ GMX_THROW(InvalidInputError("Bias grid size in checkpoint and simulation do not match. Likely you provided a checkpoint from a different simulation."));
+ }
+ for (size_t m = 0; m < points_.size(); m++)
+ {
+ points_[m].setFromHistory(biasHistory.pointState[m]);
+ }
+
+ for (size_t m = 0; m < weightSumCovering_.size(); m++)
+ {
+ weightSumCovering_[m] = biasHistory.pointState[m].weightsum_covering;
+ }
+
+ histogramSize_.restoreFromHistory(stateHistory);
+
+ linearGridindexToMultiDim(grid, stateHistory.origin_index_updatelist, originUpdatelist_);
+ linearGridindexToMultiDim(grid, stateHistory.end_index_updatelist, endUpdatelist_);
+}
+
+void BiasState::broadcast(const t_commrec *commRecord)
+{
+ gmx_bcast(sizeof(coordState_), &coordState_, commRecord);
+
+ gmx_bcast(points_.size()*sizeof(PointState), points_.data(), commRecord);
+
+ gmx_bcast(weightSumCovering_.size()*sizeof(double), weightSumCovering_.data(), commRecord);
+
+ gmx_bcast(sizeof(histogramSize_), &histogramSize_, commRecord);
+}
+
+void BiasState::setFreeEnergyToConvolvedPmf(const std::vector<DimParams> &dimParams,
+ const Grid &grid)
+{
+ std::vector<float> convolvedPmf;
+
+ calcConvolvedPmf(dimParams, grid, &convolvedPmf);
+
+ for (size_t m = 0; m < points_.size(); m++)
+ {
+ points_[m].setFreeEnergy(convolvedPmf[m]);
+ }
+}
+
+/*! \brief
+ * Count trailing data rows containing only zeros.
+ *
+ * \param[in] data 2D data array.
+ * \param[in] numRows Number of rows in array.
+ * \param[in] numColumns Number of cols in array.
+ * \returns the number of trailing zero rows.
+ */
+static int countTrailingZeroRows(const double* const *data,
+ int numRows,
+ int numColumns)
+{
+ int numZeroRows = 0;
+ for (int m = numRows - 1; m >= 0; m--)
+ {
+ bool rowIsZero = true;
+ for (int d = 0; d < numColumns; d++)
+ {
+ if (data[d][m] != 0)
+ {
+ rowIsZero = false;
+ break;
+ }
+ }
+
+ if (!rowIsZero)
+ {
+ /* At a row with non-zero data */
+ break;
+ }
+ else
+ {
+ /* Still at a zero data row, keep checking rows higher up. */
+ numZeroRows++;
+ }
+ }
+
+ return numZeroRows;
+}
+
+/*! \brief
+ * Initializes the PMF and target with data read from an input table.
+ *
+ * \param[in] dimParams The dimension parameters.
+ * \param[in] grid The grid.
+ * \param[in] filename The filename to read PMF and target from.
+ * \param[in] numBias Number of biases.
+ * \param[in] biasIndex The index of the bias.
+ * \param[in,out] pointState The state of the points in this bias.
+ */
+static void readUserPmfAndTargetDistribution(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ const std::string &filename,
+ int numBias,
+ int biasIndex,
+ std::vector<PointState> *pointState)
+{
+ /* Read the PMF and target distribution.
+ From the PMF, the convolved PMF, or the reference value free energy, can be calculated
+ base on the force constant. The free energy and target together determine the bias.
+ */
+ std::string filenameModified(filename);
+ if (numBias > 1)
+ {
+ size_t n = filenameModified.rfind(".");
+ GMX_RELEASE_ASSERT(n != std::string::npos, "The filename should contain an extension starting with .");
+ filenameModified.insert(n, formatString("%d", biasIndex));
+ }
+
+ std::string correctFormatMessage =
+ formatString("%s is expected in the following format. "
+ "The first ndim column(s) should contain the coordinate values for each point, "
+ "each column containing values of one dimension (in ascending order). "
+ "For a multidimensional coordinate, points should be listed "
+ "in the order obtained by traversing lower dimensions first. "
+ "E.g. for two-dimensional grid of size nxn: "
+ "(1, 1), (1, 2),..., (1, n), (2, 1), (2, 2), ..., , (n, n - 1), (n, n). "
+ "Column ndim + 1 should contain the PMF value for each coordinate value. "
+ "The target distribution values should be in column ndim + 2 or column ndim + 5. "
+ "Make sure the input file ends with a new line but has no trailing new lines.",
+ filename.c_str());
+ gmx::TextLineWrapper wrapper;
+ wrapper.settings().setLineLength(c_linewidth);
+ correctFormatMessage = wrapper.wrapToString(correctFormatMessage).c_str();
+
+ double **data;
+ int numColumns;
+ int numRows = read_xvg(filenameModified.c_str(), &data, &numColumns);
+
+ /* Check basic data properties here. Grid takes care of more complicated things. */
+
+ if (numRows <= 0)
+ {
+ std::string mesg = gmx::formatString("%s is empty!.\n\n%s", filename.c_str(), correctFormatMessage.c_str());
+ GMX_THROW(InvalidInputError(mesg));
+ }
+
+ /* Less than 2 points is not useful for PMF or target. */
+ if (numRows < 2)
+ {
+ std::string mesg =
+ gmx::formatString("%s contains too few data points (%d)."
+ "The minimum number of points is 2.",
+ filename.c_str(), numRows);
+ GMX_THROW(InvalidInputError(mesg));
+ }
+
+ /* Make sure there are enough columns of data.
+
+ Two formats are allowed. Either with columns {coords, PMF, target} or
+ {coords, PMF, x, y, z, target, ...}. The latter format is allowed since that
+ is how AWH output is written (x, y, z being other AWH variables). For this format,
+ trailing columns are ignored.
+ */
+ int columnIndexTarget;
+ int numColumnsMin = dimParams.size() + 2;
+ int columnIndexPmf = dimParams.size();
+ if (numColumns == numColumnsMin)
+ {
+ columnIndexTarget = columnIndexPmf + 1;
+ }
+ else
+ {
+ columnIndexTarget = columnIndexPmf + 4;
+ }
+
+ if (numColumns < numColumnsMin)
+ {
+ std::string mesg =
+ gmx::formatString("The number of columns in %s (%d) should be at least %d."
+ "\n\n%s",
+ filename.c_str(), correctFormatMessage.c_str());
+ GMX_THROW(InvalidInputError(mesg));
+ }
+
+ /* read_xvg can give trailing zero data rows for trailing new lines in the input. We allow 1 zero row,
+ since this could be real data. But multiple trailing zero rows cannot correspond to valid data. */
+ int numZeroRows = countTrailingZeroRows(data, numRows, numColumns);
+ if (numZeroRows > 1)
+ {
+ std::string mesg = gmx::formatString("Found %d trailing zero data rows in %s. Please remove trailing empty lines and try again.",
+ numZeroRows, filename.c_str());
+ GMX_THROW(InvalidInputError(mesg));
+ }
+
+ /* Convert from user units to internal units before sending the data of to grid. */
+ for (size_t d = 0; d < dimParams.size(); d++)
+ {
+ double scalingFactor = dimParams[d].scaleUserInputToInternal(1);
+ if (scalingFactor == 1)
+ {
+ continue;
+ }
+ for (size_t m = 0; m < pointState->size(); m++)
+ {
+ data[d][m] *= scalingFactor;
+ }
+ }
+
+ /* Get a data point for each AWH grid point so that they all get data. */
+ std::vector<int> gridIndexToDataIndex(grid.numPoints());
+ mapGridToDataGrid(&gridIndexToDataIndex, data, numRows, filename, grid, correctFormatMessage);
+
+ /* Extract the data for each grid point.
+ * We check if the target distribution is zero for all points.
+ */
+ bool targetDistributionIsZero = true;
+ for (size_t m = 0; m < pointState->size(); m++)
+ {
+ (*pointState)[m].setLogPmfSum(-data[columnIndexPmf][gridIndexToDataIndex[m]]);
+ double target = data[columnIndexTarget][gridIndexToDataIndex[m]];
+
+ /* Check if the values are allowed. */
+ if (target < 0)
+ {
+ std::string mesg = gmx::formatString("Target distribution weight at point %d (%g) in %s is negative.",
+ m, target, filename.c_str());
+ GMX_THROW(InvalidInputError(mesg));
+ }
+ if (target > 0)
+ {
+ targetDistributionIsZero = false;
+ }
+ (*pointState)[m].setTargetConstantWeight(target);
+ }
+
+ if (targetDistributionIsZero)
+ {
+ std::string mesg = gmx::formatString("The target weights given in column %d in %s are all 0",
+ filename.c_str(), columnIndexTarget);
+ GMX_THROW(InvalidInputError(mesg));
+ }
+
+ /* Free the arrays. */
+ for (int m = 0; m < numColumns; m++)
+ {
+ sfree(data[m]);
+ }
+ sfree(data);
+}
+
+void BiasState::normalizePmf(int numSharingSims)
+{
+ /* The normalization of the PMF estimate matters because it determines how big effect the next sample has.
+ Approximately (for large enough force constant) we should have:
+ sum_x(exp(-pmf(x)) = nsamples*sum_xref(exp(-f(xref)).
+ */
+
+ /* Calculate the normalization factor, i.e. divide by the pmf sum, multiply by the number of samples and the f sum */
+ double expSumPmf = 0;
+ double expSumF = 0;
+ for (const PointState &pointState : points_)
+ {
+ if (pointState.inTargetRegion())
+ {
+ expSumPmf += std::exp( pointState.logPmfSum());
+ expSumF += std::exp(-pointState.freeEnergy());
+ }
+ }
+ double numSamples = histogramSize_.histogramSize()/numSharingSims;
+
+ /* Renormalize */
+ double logRenorm = std::log(numSamples*expSumF/expSumPmf);
+ for (PointState &pointState : points_)
+ {
+ if (pointState.inTargetRegion())
+ {
+ pointState.setLogPmfSum(pointState.logPmfSum() + logRenorm);
+ }
+ }
+}
+
+void BiasState::initGridPointState(const AwhBiasParams &awhBiasParams,
+ const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ const BiasParams ¶ms,
+ const std::string &filename,
+ int numBias)
+{
+ /* Modify PMF, free energy and the constant target distribution factor
+ * to user input values if there is data given.
+ */
+ if (awhBiasParams.bUserData)
+ {
+ readUserPmfAndTargetDistribution(dimParams, grid, filename, numBias, params.biasIndex, &points_);
+ setFreeEnergyToConvolvedPmf(dimParams, grid);
+ }
+
+ /* The local Boltzmann distribution is special because the target distribution is updated as a function of the reference weighthistogram. */
+ GMX_RELEASE_ASSERT(params.eTarget != eawhtargetLOCALBOLTZMANN ||
+ points_[0].weightSumRef() != 0,
+ "AWH reference weight histogram not initialized properly with local Boltzmann target distribution.");
+
+ updateTargetDistribution(points_, params);
+
+ for (PointState &pointState : points_)
+ {
+ if (pointState.inTargetRegion())
+ {
+ pointState.updateBias();
+ }
+ else
+ {
+ /* Note that for zero target this is a value that represents -infinity but should not be used for biasing. */
+ pointState.setTargetToZero();
+ }
+ }
+
+ /* Set the initial reference weighthistogram. */
+ const double histogramSize = histogramSize_.histogramSize();
+ for (auto &pointState : points_)
+ {
+ pointState.setInitialReferenceWeightHistogram(histogramSize);
+ }
+
+ /* Make sure the pmf is normalized consistently with the histogram size.
+ Note: the target distribution and free energy need to be set here. */
+ normalizePmf(params.numSharedUpdate);
+}
+
+BiasState::BiasState(const AwhBiasParams &awhBiasParams,
+ double histogramSizeInitial,
+ const std::vector<DimParams> &dimParams,
+ const Grid &grid) :
+ coordState_(awhBiasParams, dimParams, grid),
+ points_(grid.numPoints()),
+ weightSumCovering_(grid.numPoints()),
+ histogramSize_(awhBiasParams, histogramSizeInitial)
+{
+ /* The minimum and maximum multidimensional point indices that are affected by the next update */
+ for (size_t d = 0; d < dimParams.size(); d++)
+ {
+ int index = grid.point(coordState_.gridpointIndex()).index[d];
+ originUpdatelist_[d] = index;
+ endUpdatelist_[d] = index;
+ }
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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
+ * Declares the BiasState class.
+ *
+ * The data members of this class are the state variables of the bias.
+ * All interaction from the outside happens through the Bias class, which
+ * holds important helper classes such as DimParams and Grid.
+ * This class holds many methods, but more are const methods that compute
+ * properties of the state.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_BIASSTATE_H
+#define GMX_AWH_BIASSTATE_H
+
+#include <cstdio>
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "coordstate.h"
+#include "dimparams.h"
+#include "histogramsize.h"
+
+struct gmx_multisim_t;
+struct t_commrec;
+
+namespace gmx
+{
+
+struct AwhBiasHistory;
+struct AwhBiasParams;
+class BiasParams;
+class Grid;
+class GridAxis;
+class PointState;
+
+/*! \internal
+ * \brief The state of a bias.
+ *
+ * The bias state has the current coordinate state: its value and the grid point
+ * it maps to (the grid point of the umbrella potential if needed). It contains
+ * a vector with the state for each point on the grid. It also
+ * counts the number of updates issued and tracks which points have been sampled
+ * since last update. Finally, the convergence state is a global property set
+ * ultimately by the histogram size histogramSize in the sub-class HistogramSize,
+ * since the update sizes are ~ 1/histogramSize.
+ */
+class BiasState
+{
+ public:
+ /*! \brief Constructor.
+ *
+ * Constructs the global state and the point states on a provided
+ * geometric grid passed in \p grid.
+ *
+ * \param[in] awhBiasParams The Bias parameters from inputrec.
+ * \param[in] histogramSizeInitial The estimated initial histogram size.
+ * This is floating-point, since histograms use weighted
+ * entries and grow by a floating-point scaling factor.
+ * \param[in] dimParams The dimension parameters.
+ * \param[in] grid The bias grid.
+ */
+ BiasState(const AwhBiasParams &awhBiasParams,
+ double histogramSizeInitial,
+ const std::vector<DimParams> &dimParams,
+ const Grid &grid);
+
+ /*! \brief
+ * Restore the bias state from history.
+ *
+ * \param[in] biasHistory Bias history struct.
+ * \param[in] grid The bias grid.
+ */
+ void restoreFromHistory(const AwhBiasHistory &biasHistory,
+ const Grid &grid);
+
+ /*! \brief
+ * Broadcast the bias state over the MPI ranks in this simulation.
+ *
+ * \param[in] commRecord Struct for communication.
+ */
+ void broadcast(const t_commrec *commRecord);
+
+ /*! \brief
+ * Allocate and initialize a bias history with the given bias state.
+ *
+ * This function will be called at the start of a new simulation.
+ * Note that this only sets the correct size and does produces
+ * a history object with all variables zero.
+ * History data is set by \ref updateHistory.
+ *
+ * \param[in,out] biasHistory AWH history to initialize.
+ */
+ void initHistoryFromState(AwhBiasHistory *biasHistory) const;
+
+ /*! \brief
+ * Update the bias history with a new state.
+ *
+ * \param[out] biasHistory Bias history struct.
+ * \param[in] grid The bias grid.
+ */
+ void updateHistory(AwhBiasHistory *biasHistory,
+ const Grid &grid) const;
+
+ private:
+ /*! \brief Convolves the given PMF using the given AWH bias.
+ *
+ * \note: The PMF is in single precision, because it is a statistical
+ * quantity and therefore never reaches full float precision.
+ *
+ * \param[in] dimParams The bias dimensions parameters
+ * \param[in] grid The grid.
+ * \param[in,out] convolvedPmf Array returned will be of the same length as the AWH grid to store the convolved PMF in.
+ */
+ void calcConvolvedPmf(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ std::vector<float> *convolvedPmf) const;
+
+ /*! \brief
+ * Convolves the PMF and sets the initial free energy to its convolution.
+ *
+ * \param[in] dimParams The bias dimensions parameters
+ * \param[in] grid The bias grid.
+ */
+ void setFreeEnergyToConvolvedPmf(const std::vector<DimParams> &dimParams,
+ const Grid &grid);
+
+ /*! \brief
+ * Normalize the PMF histogram.
+ *
+ * \param[in] numSharingSims The number of simulations sharing the bias.
+ */
+ void normalizePmf(int numSharingSims);
+
+ public:
+ /*! \brief
+ * Initialize the state of grid coordinate points.
+ *
+ * \param[in] awhBiasParams Bias parameters from inputrec.
+ * \param[in] dimParams The dimension parameters.
+ * \param[in] grid The grid.
+ * \param[in] params The bias parameters.
+ * \param[in] filename Name of file to read PMF and target from.
+ * \param[in] numBias The number of biases.
+ */
+ void initGridPointState(const AwhBiasParams &awhBiasParams,
+ const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ const BiasParams ¶ms,
+ const std::string &filename,
+ int numBias);
+
+ /*! \brief
+ * Performs statistical checks on the collected histograms and warns if issues are detected.
+ *
+ * \param[in] grid The grid.
+ * \param[in] biasIndex The index of the bias we are checking for.
+ * \param[in] t Time.
+ * \param[in,out] fplog Output file for warnings.
+ * \param[in] maxNumWarnings Don't issue more than this number of warnings.
+ * \returns the number of warnings issued.
+ */
+ int warnForHistogramAnomalies(const Grid &grid,
+ int biasIndex,
+ double t,
+ FILE *fplog,
+ int maxNumWarnings) const;
+
+ /*! \brief
+ * Calculates and sets the force the coordinate experiences from an umbrella centered at the given point.
+ *
+ * The umbrella potential is an harmonic potential given by 0.5k(coord value - point value)^2. This
+ * value is also returned.
+ *
+ * \param[in] dimParams The bias dimensions parameters.
+ * \param[in] grid The grid.
+ * \param[in] point Point for umbrella center.
+ * \param[in,out] force Force vector to set.
+ * Returns the umbrella potential.
+ */
+ double calcUmbrellaForceAndPotential(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ int point,
+ awh_dvec force) const;
+
+ /*! \brief
+ * Calculates and sets the convolved force acting on the coordinate.
+ *
+ * The convolved force is the weighted sum of forces from umbrellas
+ * located at each point in the grid.
+ *
+ * \param[in] dimParams The bias dimensions parameters.
+ * \param[in] grid The grid.
+ * \param[in] probWeightNeighbor Probability weights of the neighbors.
+ * \param[in,out] force Bias force vector to set.
+ */
+ void calcConvolvedForce(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ const std::vector<double> &probWeightNeighbor,
+ awh_dvec force) const;
+
+ /*! \brief
+ * Move the center point of the umbrella potential.
+ *
+ * A new umbrella center is sampled from the biased distibution. Also, the bias
+ * force is updated and the new potential is return.
+ *
+ * This function should only be called when the bias force is not being convolved.
+ * It is assumed that the probability distribution has been updated.
+ *
+ * \param[in] dimParams Bias dimension parameters.
+ * \param[in] grid The grid.
+ * \param[in] probWeightNeighbor Probability weights of the neighbors.
+ * \param[in,out] biasForce The AWH bias force.
+ * \param[in] step Step number, needed for the random number generator.
+ * \param[in] seed Random seed.
+ * \param[in] indexSeed Second random seed, should be the bias Index.
+ * \returns the new potential value.
+ */
+ double moveUmbrella(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ const std::vector<double> &probWeightNeighbor,
+ awh_dvec biasForce,
+ gmx_int64_t step,
+ gmx_int64_t seed,
+ int indexSeed);
+
+ private:
+ /*! \brief
+ * Gets the histogram rescaling factors needed for skipped updates.
+ *
+ * \param[in] params The bias parameters.
+ * \param[out] weighthistScaling Scaling factor for the reference weight histogram.
+ * \param[out] logPmfsumScaling Log of the scaling factor for the PMF histogram.
+ */
+ void getSkippedUpdateHistogramScaleFactors(const BiasParams ¶ms,
+ double *weighthistScaling,
+ double *logPmfsumScaling) const;
+
+ public:
+ /*! \brief
+ * Do all previously skipped updates.
+ * Public for use by tests.
+ *
+ * \param[in] params The bias parameters.
+ */
+ void doSkippedUpdatesForAllPoints(const BiasParams ¶ms);
+
+ /*! \brief
+ * Do previously skipped updates in this neighborhood.
+ *
+ * \param[in] params The bias parameters.
+ * \param[in] grid The grid.
+ */
+ void doSkippedUpdatesInNeighborhood(const BiasParams ¶ms,
+ const Grid &grid);
+
+ private:
+ /*! \brief
+ * Reset the range used to make the local update list.
+ *
+ * \param[in] grid The grid.
+ */
+ void resetLocalUpdateRange(const Grid &grid);
+
+ /*! \brief
+ * Returns the new size of the reference weight histogram in the initial stage.
+ *
+ * This function also takes care resetting the histogram used for covering checks
+ * and for exiting the initial stage.
+ *
+ * \param[in] params The bias parameters.
+ * \param[in] t Time.
+ * \param[in] detectedCovering True if we detected that the sampling interval has been sufficiently covered.
+ * \param[in,out] fplog Log file.
+ * \returns the new histogram size.
+ */
+ double newHistogramSizeInitialStage(const BiasParams ¶ms,
+ double t,
+ bool detectedCovering,
+ FILE *fplog);
+
+ /*! \brief
+ * Check if the sampling region has been covered "enough" or not.
+ *
+ * A one-dimensional interval is defined as covered if each point has
+ * accumulated the same weight as is in the peak of a discretized normal
+ * distribution. For multiple dimensions, the weights are simply projected
+ * onto each dimension and the multidimensional space is covered if each
+ * dimension is.
+ *
+ * \note The covering criterion for multiple dimensions could improved, e.g.
+ * by using a path finding algorithm.
+ *
+ * \param[in] params The bias parameters.
+ * \param[in] dimParams Bias dimension parameters.
+ * \param[in] grid The grid.
+ * \param[in] multiSimComm Struct for multi-simulation communication.
+ * \returns true if covered.
+ */
+ bool isSamplingRegionCovered(const BiasParams ¶ms,
+ const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ const gmx_multisim_t *multiSimComm) const;
+
+ /*! \brief
+ * Return the new reference weight histogram size for the current update.
+ *
+ * This function also takes care of checking for covering in the initial stage.
+ *
+ * \param[in] params The bias parameters.
+ * \param[in] t Time.
+ * \param[in] covered True if the sampling interval has been covered enough.
+ * \param[in,out] fplog Log file.
+ * \returns the new histogram size.
+ */
+ double newHistogramSize(const BiasParams ¶ms,
+ double t,
+ bool covered,
+ FILE *fplog);
+
+ public:
+ /*! \brief
+ * Update the reaction coordinate value.
+ *
+ * \param[in] grid The bias grid.
+ * \param[in] coordValue The current reaction coordinate value (there are no limits on allowed values).
+ */
+ void setCoordValue(const Grid &grid,
+ const awh_dvec coordValue)
+ {
+ coordState_.setCoordValue(grid, coordValue);
+ };
+
+ /*! \brief
+ * Performs an update of the bias.
+ *
+ * The objective of the update is to use collected samples (probability weights)
+ * to improve the free energy estimate. For sake of efficiency, the update is
+ * local whenever possible, meaning that only points that have actually been sampled
+ * are accessed and updated here. For certain AWH settings or at certain steps
+ * however, global need to be performed. Besides the actual free energy update, this
+ * function takes care of ensuring future convergence of the free energy. Convergence
+ * is obtained by increasing the size of the reference weight histogram in a controlled
+ * (sometimes dynamic) manner. Also, there are AWH variables that are direct functions
+ * of the free energy or sampling history that need to be updated here, namely the target
+ * distribution and the bias function.
+ *
+ * \param[in] dimParams The dimension parameters.
+ * \param[in] grid The grid.
+ * \param[in] params The bias parameters.
+ * \param[in] ms Struct for multi-simulation communication.
+ * \param[in] t Time.
+ * \param[in] step Time step.
+ * \param[in,out] fplog Log file.
+ * \param[in,out] updateList Work space to store a temporary list.
+ */
+ void updateFreeEnergyAndAddSamplesToHistogram(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ const BiasParams ¶ms,
+ const gmx_multisim_t *ms,
+ double t,
+ gmx_int64_t step,
+ FILE *fplog,
+ std::vector<int> *updateList);
+
+ /*! \brief
+ * Update the probability weights and the convolved bias.
+ *
+ * Given a coordinate value, each grid point is assigned a probability
+ * weight, w(point|value), that depends on the current bias function. The sum
+ * of these weights is needed for normalizing the probability sum to 1 but
+ * also equals the effective, or convolved, biasing weight for this coordinate
+ * value. The convolved bias is needed e.g. for extracting the PMF, so we save
+ * it here since this saves us from doing extra exponential function evaluations
+ * later on.
+ *
+ * \param[in] dimParams The bias dimensions parameters
+ * \param[in] grid The grid.
+ * \param[out] weight Probability weights of the neighbors.
+ * \returns the convolved bias.
+ */
+
+ double updateProbabilityWeightsAndConvolvedBias(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ std::vector<double> *weight) const;
+
+ /*! \brief
+ * Take samples of the current probability weights for future updates and analysis.
+ *
+ * Points in the current neighborhood will now have data meaning they
+ * need to be included in the local update list of the next update.
+ * Therefore, the local update range is also update here.
+ *
+ * \param[in] grid The grid.
+ * \param[in] probWeightNeighbor Probability weights of the neighbors.
+ */
+ void sampleProbabilityWeights(const Grid &grid,
+ const std::vector<double> &probWeightNeighbor);
+
+ /*! \brief
+ * Sample the reaction coordinate and PMF for future updates or analysis.
+ *
+ * These samples do not affect the (future) sampling and are thus
+ * pure observables. Statisics of these are stored in the energy file.
+ *
+ * \param[in] grid The grid.
+ * \param[in] probWeightNeighbor Probability weights of the neighbors.
+ * \param[in] convolvedBias The convolved bias.
+ */
+ void sampleCoordAndPmf(const Grid &grid,
+ const std::vector<double> &probWeightNeighbor,
+ double convolvedBias);
+ /*! \brief
+ * Calculates the convolved bias for a given coordinate value.
+ *
+ * The convolved bias is the effective bias acting on the coordinate.
+ * Since the bias here has arbitrary normalization, this only makes
+ * sense as a relative, to other coordinate values, measure of the bias.
+ *
+ * \note If it turns out to be costly to calculate this pointwise
+ * the convolved bias for the whole grid could be returned instead.
+ *
+ * \param[in] dimParams The bias dimensions parameters
+ * \param[in] grid The grid.
+ * \param[in] coordValue Coordinate value.
+ * \returns the convolved bias >= -GMX_DOUBLE_MAX.
+ */
+ double calcConvolvedBias(const std::vector<DimParams> &dimParams,
+ const Grid &grid,
+ const awh_dvec &coordValue) const;
+
+ /*! \brief
+ * Fills the given array with PMF values, resizes if necessary.
+ *
+ * Points outside of the biasing target region will get PMF = GMX_FLOAT_MAX.
+ * \note: The PMF is in single precision, because it is a statistical
+ * quantity and therefore never reaches full float precision.
+ *
+ * \param[in,out] pmf Array returned will be of the same length as the AWH grid to store the PMF in.
+ */
+ void getPmf(std::vector<float> *pmf) const;
+
+ /*! \brief Returns the current coordinate state.
+ */
+ const CoordState &coordState() const
+ {
+ return coordState_;
+ };
+
+ /*! \brief Returns a const reference to the point state.
+ */
+ const std::vector<PointState> &points() const
+ {
+ return points_;
+ };
+
+ /*! \brief Returns true if we are in the initial stage.
+ */
+ bool inInitialStage() const
+ {
+ return histogramSize_.inInitialStage();
+ };
+
+ /* Data members */
+ private:
+ CoordState coordState_; /**< The Current coordinate state */
+
+ /* The grid point state */
+ std::vector<PointState> points_; /**< Vector of state of the grid points */
+
+ /* Covering values for each point on the grid */
+ std::vector<double> weightSumCovering_; /**< Accumulated weights for covering checks */
+
+ HistogramSize histogramSize_; /**< Global histogram size related values. */
+
+ /* Track the part of the grid sampled since the last update. */
+ awh_ivec originUpdatelist_; /**< The origin of the rectangular region that has been sampled since last update. */
+ awh_ivec endUpdatelist_; /**< The end of the rectangular region that has been sampled since last update. */
+};
+
+//! Linewidth used for warning output
+static const int c_linewidth = 80 - 2;
+
+//! Indent used for warning output
+static const int c_indent = 0;
+
+} // namespace gmx
+
+#endif /* GMX_AWH_BIASSTATE_H */
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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 the CoordState class.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#include "gmxpre.h"
+
+#include "coordstate.h"
+
+#include <algorithm>
+
+#include "gromacs/math/utilities.h"
+#include "gromacs/mdtypes/awh-history.h"
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/random/threefry.h"
+#include "gromacs/random/uniformrealdistribution.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "grid.h"
+
+namespace gmx
+{
+
+CoordState::CoordState(const AwhBiasParams &awhBiasParams,
+ const std::vector<DimParams> &dimParams,
+ const Grid &grid)
+{
+ for (size_t d = 0; d < dimParams.size(); d++)
+ {
+ coordValue_[d] = dimParams[d].scaleUserInputToInternal(awhBiasParams.dimParams[d].coordValueInit);
+ }
+
+ /* The grid-point index is always the nearest point to the coordinate.
+ * We initialize the umbrella location to the same nearest point.
+ * More correctly one would sample from the biased distribution,
+ * but it doesn't really matter, as this will happen after a few steps.
+ */
+ gridpointIndex_ = grid.nearestIndex(coordValue_);
+ umbrellaGridpoint_ = gridpointIndex_;
+}
+
+namespace
+{
+
+/*! \brief Generate a sample from a discrete probability distribution defined on [0, distr.size() - 1].
+ *
+ * The pair (indexSeed0,indexSeed1) should be different for every invocation.
+ *
+ * \param[in] distr Normalized probability distribution to generate a sample from.
+ * \param[in] seed Random seed for initializing the random number generator.
+ * \param[in] indexSeed0 Random seed needed by the random number generator.
+ * \param[in] indexSeed1 Random seed needed by the random number generator.
+ * \returns a sample index in [0, distr.size() - 1]
+ */
+int getSampleFromDistribution(ArrayRef<const double> distr,
+ gmx_int64_t seed,
+ gmx_int64_t indexSeed0,
+ gmx_int64_t indexSeed1)
+{
+ gmx::ThreeFry2x64<0> rng(seed, gmx::RandomDomain::AwhBiasing);
+ gmx::UniformRealDistribution<real> uniformRealDistr;
+
+ GMX_RELEASE_ASSERT(distr.size() > 0, "We need a non-zero length distribution to sample from");
+
+ /* Generate the cumulative probability distribution function */
+ std::vector<double> cumulativeDistribution(distr.size());
+
+ cumulativeDistribution[0] = distr[0];
+
+ for (size_t i = 1; i < distr.size(); i++)
+ {
+ cumulativeDistribution[i] = cumulativeDistribution[i - 1] + distr[i];
+ }
+
+ GMX_RELEASE_ASSERT(gmx_within_tol(cumulativeDistribution.back(), 1.0, 0.01), "Attempt to get sample from non-normalized/zero distribution");
+
+ /* Use binary search to convert the real value to an integer in [0, ndistr - 1] distributed according to distr. */
+ rng.restart(indexSeed0, indexSeed1);
+
+ double value = uniformRealDistr(rng);
+ int sample = std::upper_bound(cumulativeDistribution.begin(), cumulativeDistribution.end() - 1, value) - cumulativeDistribution.begin();
+
+ return sample;
+}
+
+} // namespace
+
+void
+CoordState::sampleUmbrellaGridpoint(const Grid &grid,
+ int gridpointIndex,
+ const std::vector<double> &probWeightNeighbor,
+ gmx_int64_t step,
+ gmx_int64_t seed,
+ int indexSeed)
+{
+ /* Sample new umbrella reference value from the probability distribution
+ * which is defined for the neighboring points of the current coordinate.
+ */
+ const std::vector<int> &neighbor = grid.point(gridpointIndex).neighbor;
+
+ /* In order to use the same seed for all AWH biases and get independent
+ samples we use the index of the bias. */
+ int localIndex = getSampleFromDistribution(probWeightNeighbor,
+ seed, step, indexSeed);
+
+ umbrellaGridpoint_ = neighbor[localIndex];
+}
+
+void CoordState::setCoordValue(const Grid &grid,
+ const awh_dvec coordValue)
+{
+ for (int dim = 0; dim < grid.numDimensions(); dim++)
+ {
+ coordValue_[dim] = coordValue[dim];
+ }
+
+ /* The grid point closest to the coordinate value defines the current
+ * neighborhood of points. Besides at steps when global updates and/or
+ * checks are performed, only the neighborhood will be touched.
+ */
+ gridpointIndex_ = grid.nearestIndex(coordValue_);
+}
+
+void
+CoordState::restoreFromHistory(const AwhBiasStateHistory &stateHistory)
+{
+ umbrellaGridpoint_ = stateHistory.umbrellaGridpoint;
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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
+ * Declares the CoordState class.
+ *
+ * It sets and holds the current coordinate value and corresponding closest
+ * grid point index. These are (re)set at every step.
+ * With umbrella potential type, this class also holds and updates the umbrella
+ * potential reference location, which is a state variable that presists over
+ * the duration of an AWH sampling interval.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_COORDSTATE_H
+#define GMX_AWH_COORDSTATE_H
+
+#include <vector>
+
+#include "dimparams.h"
+
+namespace gmx
+{
+
+struct AwhBiasParams;
+struct AwhBiasStateHistory;
+class BiasParams;
+class Grid;
+
+/*! \internal \brief Keeps track of the current coordinate value, grid index and umbrella location.
+ */
+class CoordState
+{
+ public:
+ /*! \brief Constructor.
+ *
+ * \param[in] awhBiasParams The Bias parameters from inputrec.
+ * \param[in] dimParams The dimension Parameters.
+ * \param[in] grid The grid.
+ */
+ CoordState(const AwhBiasParams &awhBiasParams,
+ const std::vector<DimParams> &dimParams,
+ const Grid &grid);
+
+ /*! \brief
+ * Sample a new umbrella reference point given the current coordinate value.
+ *
+ * It is assumed that the probability distribution has been updated.
+ *
+ * \param[in] grid The grid.
+ * \param[in] gridpointIndex The grid point, sets the neighborhood.
+ * \param[in] probWeightNeighbor Probability weights of the neighbors.
+ * \param[in] step Step number, needed for the random number generator.
+ * \param[in] seed Random seed.
+ * \param[in] indexSeed Second random seed, should be the bias Index.
+ * \returns the index of the sampled point.
+ */
+ void sampleUmbrellaGridpoint(const Grid &grid,
+ int gridpointIndex,
+ const std::vector<double> &probWeightNeighbor,
+ gmx_int64_t step,
+ gmx_int64_t seed,
+ int indexSeed);
+
+ /*! \brief Update the coordinate value with coordValue.
+ *
+ * \param[in] grid The grid.
+ * \param[in] coordValue The new coordinate value.
+ */
+ void setCoordValue(const Grid &grid,
+ const awh_dvec coordValue);
+
+ /*! \brief Restores the coordinate state from history.
+ *
+ * \param[in] stateHistory The AWH bias state history.
+ */
+ void restoreFromHistory(const AwhBiasStateHistory &stateHistory);
+
+ /*! \brief Returns the current coordinate value.
+ */
+ const awh_dvec &coordValue() const
+ {
+ return coordValue_;
+ };
+
+ /*! \brief Returns the grid point index for the current coordinate value.
+ */
+ int gridpointIndex() const
+ {
+ return gridpointIndex_;
+ }
+
+ /*! \brief Returns the index for the current reference grid point.
+ */
+ int umbrellaGridpoint() const
+ {
+ return umbrellaGridpoint_;
+ };
+
+ private:
+ awh_dvec coordValue_; /**< Current coordinate value in (nm or rad) */
+ int gridpointIndex_; /**< The grid point index for the current coordinate value */
+ int umbrellaGridpoint_; /**< Index for the current reference grid point for the umbrella, only used with umbrella potential type */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_COORDSTATE_H */
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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
+ * Declares the DimParams struct and AWH vector types.
+ *
+ * This class holds the physical information for a dimension
+ * of the bias reaction-coordinate grid.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_DIMPARAMS_H
+#define GMX_AWH_DIMPARAMS_H
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+namespace gmx
+{
+
+//! The maximum dimensionality of the AWH coordinate.
+static const int c_biasMaxNumDim = 4;
+
+//! A real vector in AWH coordinate space.
+typedef double awh_dvec[c_biasMaxNumDim];
+
+//! An integer vector in AWH coordinate space.
+typedef int awh_ivec[c_biasMaxNumDim];
+
+/*! \internal \brief Constant parameters for each dimension of the coordinate.
+ */
+struct DimParams
+{
+ /*! \brief
+ * Constructor.
+ *
+ * \param[in] conversionFactor Conversion factor from user coordinate units to bias internal units (=DEG2RAD for angles).
+ * \param[in] forceConstant The harmonic force constant.
+ * \param[in] beta 1/(k_B T).
+ */
+ DimParams(double conversionFactor,
+ double forceConstant,
+ double beta) :
+ k(forceConstant),
+ betak(beta*forceConstant),
+ userCoordUnitsToInternal(conversionFactor)
+ {
+ };
+
+ /*! \brief Convert internal coordinate units to external, user coordinate units.
+ *
+ * \param[in] value Value to convert.
+ * \returns the converted value.
+ */
+ double scaleInternalToUserInput(double value) const
+ {
+ return value/userCoordUnitsToInternal;
+ }
+
+ /*! \brief Convert external, user coordinate units to internal coordinate units.
+ *
+ * \param[in] value Value to convert.
+ * \returns the converted value.
+ */
+ double scaleUserInputToInternal(double value) const
+ {
+ return value*userCoordUnitsToInternal;
+ }
+
+ const double k; /**< Force constant (kJ/mol/nm^2) for each coordinate dimension. */
+ const double betak; /**< Inverse variance (1/nm^2) for each coordinate dimension. */
+ const double userCoordUnitsToInternal; /**< Conversion factor coordinate units. */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_DIMPARAMS_H */
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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 functions in grid.h.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#include "gmxpre.h"
+
+#include "grid.h"
+
+#include <assert.h>
+
+#include <cmath>
+#include <cstring>
+
+#include <algorithm>
+
+#include "gromacs/math/utilities.h"
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/utility/cstringutil.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/smalloc.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace gmx
+{
+
+namespace
+{
+
+/*! \brief
+ * Modify x so that it is periodic in [-period/2, +period/2).
+ *
+ * x is modified by shifting its value by a +/- a period if
+ * needed. Thus, it is assumed that x is at most one period
+ * away from this interval. For period = 0, x is not modified.
+ *
+ * \param[in,out] x Pointer to the value to modify.
+ * \param[in] period The period, or 0 if not periodic.
+ */
+void centerPeriodicValueAroundZero(double *x,
+ double period)
+{
+ GMX_ASSERT(period >= 0, "Periodic should not be negative");
+
+ const double halfPeriod = period*0.5;
+
+ if (*x >= halfPeriod)
+ {
+ *x -= period;
+ }
+ else if (*x < -halfPeriod)
+ {
+ *x += period;
+ }
+}
+
+/*! \brief
+ * If period>0, retrun x so that it is periodic in [0, period), else return x.
+ *
+ * Return x is shifted its value by a +/- a period, if
+ * needed. Thus, it is assumed that x is at most one period
+ * away from this interval. For this domain and period > 0
+ * this is equivalent to x = x % period. For period = 0,
+ * x is not modified.
+ *
+ * \param[in,out] x Pointer to the value to modify, should be >= 0.
+ * \param[in] period The period, or 0 if not periodic.
+ * \returns for period>0: index value witin [0, period), otherwise: \p x.
+ */
+int indexWithinPeriod(int x,
+ int period)
+{
+ GMX_ASSERT(period >= 0, "Periodic should not be negative");
+
+ if (period == 0)
+ {
+ return x;
+ }
+
+ GMX_ASSERT(x > -period && x < 2*period, "x should not be more shifted by more than one period");
+
+ if (x >= period)
+ {
+ return x - period;
+ }
+ else if (x < 0)
+ {
+ return x + period;
+ }
+ else
+ {
+ return x;
+ }
+}
+
+/*! \brief
+ * Get the length of the interval (origin, end).
+ *
+ * This returns the distance obtained by connecting the origin point to
+ * the end point in the positive direction. Note that this is generally
+ * not the shortest distance. For period > 0, both origin and
+ * end are expected to take values in the same periodic interval,
+ * ie. |origin - end| < period.
+ *
+ * \param[in] origin Start value of the interval.
+ * \param[in] end End value of the interval.
+ * \param[in] period The period, or 0 if not periodic.
+ * \returns the interval length from origin to end.
+ */
+double getIntervalLengthPeriodic(double origin,
+ double end,
+ double period)
+{
+ double length = end - origin;
+ if (length < 0)
+ {
+ /* The interval wraps around the +/- boundary which has a discontinuous jump of -period. */
+ length += period;
+ }
+
+ GMX_RELEASE_ASSERT(length >= 0, "Negative AWH grid axis length.");
+ GMX_RELEASE_ASSERT(period == 0 || length <= period, "Interval length longer than period.");
+
+ return length;
+}
+
+/*! \brief
+ * Get the deviation x - x0.
+ *
+ * For period > 0, the deviation with minimum absolute value is returned,
+ * i.e. with a value in the interval [-period/2, +period/2).
+ * Also for period > 0, it is assumed that |x - x0| < period.
+ *
+ * \param[in] x From value.
+ * \param[in] x0 To value.
+ * \param[in] period The period, or 0 if not periodic.
+ * \returns the deviation from x to x0.
+ */
+double getDeviationPeriodic(double x,
+ double x0,
+ double period)
+{
+ double dev = x - x0;
+
+ if (period > 0)
+ {
+ centerPeriodicValueAroundZero(&dev, period);
+ }
+
+ return dev;
+}
+
+} // namespace
+
+double getDeviationFromPointAlongGridAxis(const Grid &grid,
+ int dimIndex,
+ int pointIndex,
+ double value)
+{
+ double coordValue = grid.point(pointIndex).coordValue[dimIndex];
+
+ return getDeviationPeriodic(value, coordValue, grid.axis(dimIndex).period());
+}
+
+void linearArrayIndexToMultiDim(int indexLinear, int numDimensions, const awh_ivec numPointsDim, awh_ivec indexMulti)
+{
+ for (int d = 0; d < numDimensions; d++)
+ {
+ int stride = 1;
+
+ /* Workaround for bug in clang */
+#ifndef __clang_analyzer__
+ for (int k = d + 1; k < numDimensions; k++)
+ {
+ stride *= numPointsDim[k];
+ }
+#endif
+
+ indexMulti[d] = indexLinear/stride;
+ indexLinear -= indexMulti[d]*stride;
+ }
+}
+
+void linearGridindexToMultiDim(const Grid &grid,
+ int indexLinear,
+ awh_ivec indexMulti)
+{
+ awh_ivec numPointsDim;
+
+ for (int d = 0; d < grid.numDimensions(); d++)
+ {
+ numPointsDim[d] = grid.axis(d).numPoints();
+ }
+
+ linearArrayIndexToMultiDim(indexLinear, grid.numDimensions(), numPointsDim, indexMulti);
+}
+
+
+int multiDimArrayIndexToLinear(const awh_ivec indexMulti,
+ int numDimensions,
+ const awh_ivec numPointsDim)
+{
+ int stride = 1;
+ int indexLinear = 0;
+ for (int d = numDimensions - 1; d >= 0; d--)
+ {
+ indexLinear += stride*indexMulti[d];
+ stride *= numPointsDim[d];
+ }
+
+ return indexLinear;
+}
+
+namespace
+{
+
+/*! \brief Convert a multidimensional grid point index to a linear one.
+ *
+ * \param[in] axis The grid axes.
+ * \param[in] indexMulti Multidimensional grid point index to convert to a linear one.
+ * \returns the linear index.
+ */
+int multiDimGridIndexToLinear(const std::vector<GridAxis> &axis,
+ const awh_ivec indexMulti)
+{
+ awh_ivec numPointsDim = { 0 };
+
+ for (size_t d = 0; d < axis.size(); d++)
+ {
+ numPointsDim[d] = axis[d].numPoints();
+ }
+
+ return multiDimArrayIndexToLinear(indexMulti, axis.size(), numPointsDim);
+}
+
+} // namespace
+
+int multiDimGridIndexToLinear(const Grid &grid,
+ const awh_ivec indexMulti)
+{
+ return multiDimGridIndexToLinear(grid.axis(), indexMulti);
+}
+
+namespace
+{
+
+/*! \brief
+ * Take a step in a multidimensional array.
+ *
+ * The multidimensional index gives the starting point to step from. Dimensions are
+ * stepped through in order of decreasing dimensional index such that the index is
+ * incremented in the highest dimension possible. If the starting point is the end
+ * of the array, a step cannot be taken and the index is not modified.
+ *
+ * \param[in] numDim Number of dimensions of the array.
+ * \param[in] numPoints Vector with the number of points along each dimension.
+ * \param[in,out] indexDim Multidimensional index, each with values in [0, numPoints[d] - 1].
+ * \returns true if a step was taken, false if not.
+ */
+bool stepInMultiDimArray(int numDim,
+ const awh_ivec numPoints,
+ awh_ivec indexDim)
+{
+ bool haveStepped = false;
+
+ for (int d = numDim - 1; d >= 0 && !haveStepped; d--)
+ {
+ if (indexDim[d] < numPoints[d] - 1)
+ {
+ /* Not at a boundary, just increase by 1. */
+ indexDim[d]++;
+ haveStepped = true;
+ }
+ else
+ {
+ /* At a boundary. If we are not at the end of the array,
+ reset the index and check if we can step in higher dimensions */
+ if (d > 0)
+ {
+ indexDim[d] = 0;
+ }
+ }
+ }
+
+ return haveStepped;
+}
+
+/*! \brief
+ * Transforms a grid point index to to the multidimensional index of a subgrid.
+ *
+ * The subgrid is defined by the location of its origin and the number of points
+ * along each dimension. The index transformation thus consists of a projection
+ * of the linear index onto each dimension, followed by a translation of the origin.
+ * The subgrid may have parts that don't overlap with the grid. E.g. the origin
+ * vector can have negative components meaning the origin lies outside of the grid.
+ * However, the given point needs to be both a grid and subgrid point.
+ *
+ * Periodic boundaries are taken care of by wrapping the subgrid around the grid.
+ * Thus, for periodic dimensions the number of subgrid points need to be less than
+ * the number of points in a period to prevent problems of wrapping around.
+ *
+ * \param[in] grid The grid.
+ * \param[in] subgridOrigin Vector locating the subgrid origin relative to the grid origin.
+ * \param[in] subgridNpoints The number of subgrid points in each dimension.
+ * \param[in] point Grid point to get subgrid index for.
+ * \param[in,out] subgridIndex Subgrid multidimensional index.
+ */
+void gridToSubgridIndex(const Grid &grid,
+ const awh_ivec subgridOrigin,
+ const awh_ivec subgridNpoints,
+ int point,
+ awh_ivec subgridIndex)
+{
+ /* Get the subgrid index of the given grid point, for each dimension. */
+ for (int d = 0; d < grid.numDimensions(); d++)
+ {
+ /* The multidimensional grid point index relative to the subgrid origin. */
+ subgridIndex[d] =
+ indexWithinPeriod(grid.point(point).index[d] - subgridOrigin[d],
+ grid.axis(d).numPointsInPeriod());
+
+ /* The given point should be in the subgrid. */
+ GMX_RELEASE_ASSERT((subgridIndex[d] >= 0) && (subgridIndex[d] < subgridNpoints[d]),
+ "Attempted to convert an AWH grid point index not in subgrid to out of bounds subgrid index");
+ }
+}
+
+/*! \brief
+ * Transform a multidimensional subgrid index to a grid point index.
+ *
+ * If the given subgrid point is not a grid point the transformation will not be successful
+ * and the grid point index will not be set. Periodic boundaries are taken care of by
+ * wrapping the subgrid around the grid.
+ *
+ * \param[in] grid The grid.
+ * \param[in] subgridOrigin Vector locating the subgrid origin relative to the grid origin.
+ * \param[in] subgridIndex Subgrid multidimensional index to get grid point index for.
+ * \param[in,out] gridIndex Grid point index.
+ * \returns true if the transformation was successful.
+ */
+bool subgridToGridIndex(const Grid &grid,
+ const awh_ivec subgridOrigin,
+ const awh_ivec subgridIndex,
+ int *gridIndex)
+{
+ awh_ivec globalIndexDim;
+
+ /* Check and apply boundary conditions for each dimension */
+ for (int d = 0; d < grid.numDimensions(); d++)
+ {
+ /* Transform to global multidimensional indexing by adding the origin */
+ globalIndexDim[d] = subgridOrigin[d] + subgridIndex[d];
+
+ /* The local grid is allowed to stick out on the edges of the global grid. Here the boundary conditions are applied.*/
+ if (globalIndexDim[d] < 0 || globalIndexDim[d] > grid.axis(d).numPoints() - 1)
+ {
+ /* Try to wrap around if periodic. Otherwise, the transformation failed so return. */
+ if (!grid.axis(d).isPeriodic())
+ {
+ return false;
+ }
+
+ /* The grid might not contain a whole period. Can only wrap around if this gap is not too large. */
+ int gap = grid.axis(d).numPointsInPeriod() - grid.axis(d).numPoints();
+
+ int bridge;
+ int numWrapped;
+ if (globalIndexDim[d] < 0)
+ {
+ bridge = -globalIndexDim[d];
+ numWrapped = bridge - gap;
+ if (numWrapped > 0)
+ {
+ globalIndexDim[d] = grid.axis(d).numPoints() - numWrapped;
+ }
+ }
+ else
+ {
+ bridge = globalIndexDim[d] - (grid.axis(d).numPoints() - 1);
+ numWrapped = bridge - gap;
+ if (numWrapped > 0)
+ {
+ globalIndexDim[d] = numWrapped - 1;
+ }
+ }
+
+ if (numWrapped <= 0)
+ {
+ return false;
+ }
+ }
+ }
+
+ /* Translate from multidimensional to linear indexing and set the return value */
+ (*gridIndex) = multiDimGridIndexToLinear(grid, globalIndexDim);
+
+ return true;
+}
+
+} // namespace
+
+bool advancePointInSubgrid(const Grid &grid,
+ const awh_ivec subgridOrigin,
+ const awh_ivec subgridNumPoints,
+ int *gridPointIndex)
+{
+ /* Initialize the subgrid index to the subgrid origin. */
+ awh_ivec subgridIndex = { 0 };
+
+ /* Get the subgrid index of the given grid point index. */
+ if (*gridPointIndex >= 0)
+ {
+ gridToSubgridIndex(grid, subgridOrigin, subgridNumPoints, *gridPointIndex, subgridIndex);
+ }
+ else
+ {
+ /* If no grid point is given we start at the subgrid origin (which subgridIndex is initialized to).
+ If this is a valid grid point then we're done, otherwise keep looking below. */
+ /* TODO: separate into a separate function (?) */
+ if (subgridToGridIndex(grid, subgridOrigin, subgridIndex, gridPointIndex))
+ {
+ return true;
+ }
+ }
+
+ /* Traverse the subgrid and look for the first point that is also in the grid. */
+ while (stepInMultiDimArray(grid.numDimensions(), subgridNumPoints, subgridIndex))
+ {
+ /* If this is a valid grid point, the grid point index is updated.*/
+ if (subgridToGridIndex(grid, subgridOrigin, subgridIndex, gridPointIndex))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*! \brief
+ * Returns the point distance between from value x to value x0 along the given axis.
+ *
+ * Note that the returned distance may be negative or larger than the
+ * number of points in the axis. For a periodic axis, the distance is chosen
+ * to be in [0, period), i.e. always positive but not the shortest one.
+ *
+ * \param[in] axis Grid axis.
+ * \param[in] x From value.
+ * \param[in] x0 To value.
+ * \returns (x - x0) in number of points.
+ */
+static int pointDistanceAlongAxis(const GridAxis &axis, double x, double x0)
+{
+ int distance = 0;
+
+ if (axis.spacing() > 0)
+ {
+ /* Get the real-valued distance. For a periodic axis, the shortest one. */
+ double period = axis.period();
+ double dx = getDeviationPeriodic(x, x0, period);
+
+ /* Transform the distance into a point distance.
+ Shift by +0.5 so we can use floor or integer casting below to get the integer index */
+ distance = static_cast<int>(floor(dx/axis.spacing() + 0.5));
+
+ /* If periodic, shift the point distance to be in [0, period) */
+ distance = indexWithinPeriod(distance, axis.numPointsInPeriod());
+ }
+
+ return distance;
+}
+
+/*! \brief
+ * Query if a value is in range of the grid.
+ *
+ * \param[in] value Value to check.
+ * \param[in] axis The grid axes.
+ * \returns true if the value is in the grid.
+ */
+static bool valueIsInGrid(const awh_dvec value,
+ const std::vector<GridAxis> &axis)
+{
+ /* For each dimension get the one-dimensional index and check if it is in range. */
+ for (size_t d = 0; d < axis.size(); d++)
+ {
+ /* The index is computed as the point distance from the origin. */
+ int index = pointDistanceAlongAxis(axis[d], value[d], axis[d].origin());
+
+ if (!(index >= 0 && index < axis[d].numPoints()))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Grid::covers(const awh_dvec value) const
+{
+ return valueIsInGrid(value, axis());
+}
+
+int GridAxis::nearestIndex(double value) const
+{
+ /* Get the point distance to the origin. This may by an out of index range for the axis. */
+ int index = pointDistanceAlongAxis(*this, value, origin_);
+
+ if (index < 0 || index >= numPoints_)
+ {
+ if (isPeriodic())
+ {
+ GMX_RELEASE_ASSERT(index >= 0 && index < numPointsInPeriod_,
+ "Index not in periodic interval 0 for AWH periodic axis");
+ int endDistance = (index - (numPoints_ - 1));
+ int originDistance = (numPointsInPeriod_ - index);
+ index = originDistance < endDistance ? 0 : numPoints_ - 1;
+ }
+ else
+ {
+ index = (index < 0) ? 0 : (numPoints_ - 1);
+ }
+ }
+
+ return index;
+}
+
+/*! \brief
+ * Map a value to the nearest point in the grid.
+ *
+ * \param[in] value Value.
+ * \param[in] axis The grid axes.
+ * \returns the point index nearest to the value.
+ */
+static int getNearestIndexInGrid(const awh_dvec value,
+ const std::vector<GridAxis> &axis)
+{
+ awh_ivec indexMulti;
+
+ /* If the index is out of range, modify it so that it is in range by choosing the nearest point on the edge. */
+ for (size_t d = 0; d < axis.size(); d++)
+ {
+ indexMulti[d] = axis[d].nearestIndex(value[d]);
+ }
+
+ return multiDimGridIndexToLinear(axis, indexMulti);
+}
+
+int Grid::nearestIndex(const awh_dvec value) const
+{
+ return getNearestIndexInGrid(value, axis());
+}
+
+namespace
+{
+
+/*! \brief
+ * Find and set the neighbors of a grid point.
+ *
+ * The search space for neighbors is a subgrid with size set by a scope cutoff.
+ * In general not all point within scope will be valid grid points.
+ *
+ * \param[in] pointIndex Grid point index.
+ * \param[in] grid The grid.
+ * \param[in,out] neighborIndexArray Array to fill with neighbor indices.
+ */
+void setNeighborsOfGridPoint(int pointIndex,
+ const Grid &grid,
+ std::vector<int> *neighborIndexArray)
+{
+ const int c_maxNeighborsAlongAxis = 1 + 2*static_cast<int>(Grid::c_numPointsPerSigma*Grid::c_scopeCutoff);
+
+ awh_ivec numCandidates = {0};
+ awh_ivec subgridOrigin = {0};
+ for (int d = 0; d < grid.numDimensions(); d++)
+ {
+ /* The number of candidate points along this dimension is given by the scope cutoff. */
+ numCandidates[d] = std::min(c_maxNeighborsAlongAxis,
+ grid.axis(d).numPoints());
+
+ /* The origin of the subgrid to search */
+ int centerIndex = grid.point(pointIndex).index[d];
+ subgridOrigin[d] = centerIndex - numCandidates[d]/2;
+ }
+
+ /* Find and set the neighbors */
+ int neighborIndex = -1;
+ bool aPointExists = true;
+
+ /* Keep looking for grid points while traversing the subgrid. */
+ while (aPointExists)
+ {
+ /* The point index is updated if a grid point was found. */
+ aPointExists = advancePointInSubgrid(grid, subgridOrigin, numCandidates, &neighborIndex);
+
+ if (aPointExists)
+ {
+ neighborIndexArray->push_back(neighborIndex);
+ }
+ }
+}
+
+} // namespace
+
+void Grid::initPoints()
+{
+ awh_ivec numPointsDimWork = { 0 };
+ awh_ivec indexWork = { 0 };
+
+ for (size_t d = 0; d < axis_.size(); d++)
+ {
+ /* Temporarily gather the number of points in each dimension in one array */
+ numPointsDimWork[d] = axis_[d].numPoints();
+ }
+
+ for (auto &point : point_)
+ {
+ for (size_t d = 0; d < axis_.size(); d++)
+ {
+ point.coordValue[d] = axis_[d].origin() + indexWork[d]*axis_[d].spacing();
+
+ if (axis_[d].period() > 0)
+ {
+ /* Do we always want the values to be centered around 0 ? */
+ centerPeriodicValueAroundZero(&point.coordValue[d], axis_[d].period());
+ }
+
+ point.index[d] = indexWork[d];
+ }
+
+ stepInMultiDimArray(axis_.size(), numPointsDimWork, indexWork);
+ }
+}
+
+GridAxis::GridAxis(double origin, double end,
+ double period, double pointDensity) :
+ origin_(origin),
+ period_(period)
+{
+ length_ = getIntervalLengthPeriodic(origin_, end, period_);
+
+ /* Automatically determine number of points based on the user given endpoints
+ and the expected fluctuations in the umbrella. */
+ if (length_ == 0)
+ {
+ numPoints_ = 1;
+ }
+ else if (pointDensity == 0)
+ {
+ numPoints_ = 2;
+ }
+ else
+ {
+ double lengthInPoints = length_*pointDensity;
+
+ numPoints_ = 1 + static_cast<int>(std::ceil(lengthInPoints));
+ }
+
+ /* Set point spacing based on the number of points */
+ if (isPeriodic())
+ {
+ /* Set the grid spacing so that a period is matched exactly by an integer number of points.
+ The number of points in a period is equal to the number of grid spacings in a period
+ since the endpoints are connected. */
+ numPointsInPeriod_ = length_ > 0 ? static_cast<int>(std::ceil(period/length_*(numPoints_ - 1))) : 1;
+ spacing_ = period_/numPointsInPeriod_;
+
+ /* Modify the number of grid axis points to be compatible with the period dependent spacing. */
+ numPoints_ = std::min(static_cast<int>(round(length_/spacing_)) + 1,
+ numPointsInPeriod_);
+ }
+ else
+ {
+ numPointsInPeriod_ = 0;
+ spacing_ = numPoints_ > 1 ? length_/(numPoints_ - 1) : 0;
+ }
+}
+
+GridAxis::GridAxis(double origin, double end,
+ double period, int numPoints) :
+ origin_(origin),
+ period_(period),
+ numPoints_(numPoints)
+{
+ length_ = getIntervalLengthPeriodic(origin_, end, period_);
+ spacing_ = numPoints_ > 1 ? length_/(numPoints_ - 1) : period_;
+ numPointsInPeriod_ = static_cast<int>(std::round(period_/spacing_));
+}
+
+Grid::Grid(const std::vector<DimParams> &dimParams,
+ const AwhDimParams *awhDimParams)
+{
+ /* Define the discretization along each dimension */
+ awh_dvec period;
+ int numPoints = 1;
+ for (size_t d = 0; d < dimParams.size(); d++)
+ {
+ double origin = dimParams[d].scaleUserInputToInternal(awhDimParams[d].origin);
+ double end = dimParams[d].scaleUserInputToInternal(awhDimParams[d].end);
+ period[d] = dimParams[d].scaleUserInputToInternal(awhDimParams[d].period);
+ static_assert(c_numPointsPerSigma >= 1.0, "The number of points per sigma should be at least 1.0 to get a uniformly covering the reaction using Gaussians");
+ double pointDensity = std::sqrt(dimParams[d].betak)*c_numPointsPerSigma;
+ axis_.push_back(GridAxis(origin, end, period[d], pointDensity));
+ numPoints *= axis_[d].numPoints();
+ }
+
+ point_.resize(numPoints);
+
+ /* Set their values */
+ initPoints();
+
+ /* Keep a neighbor list for each point.
+ * Note: could also generate neighbor list only when needed
+ * instead of storing them for each point.
+ */
+ for (size_t m = 0; m < point_.size(); m++)
+ {
+ std::vector<int> *neighbor = &point_[m].neighbor;
+
+ setNeighborsOfGridPoint(m, *this, neighbor);
+ }
+}
+
+void mapGridToDataGrid(std::vector<int> *gridpointToDatapoint,
+ const double* const *data,
+ int numDataPoints,
+ const std::string &dataFilename,
+ const Grid &grid,
+ const std::string &correctFormatMessage)
+{
+ /* Transform the data into a grid in order to map each grid point to a data point
+ using the grid functions. */
+ std::vector<GridAxis> axis_;
+
+ /* Count the number of points for each dimension. Each dimension
+ has its own stride. */
+ int stride = 1;
+ int numPointsCounted = 0;
+ std::vector<int> numPoints(grid.numDimensions());
+ for (int d = grid.numDimensions() - 1; d >= 0; d--)
+ {
+ int numPointsInDim = 0;
+ int pointIndex = 0;
+ double firstValue = data[d][pointIndex];
+ do
+ {
+ numPointsInDim++;
+ pointIndex += stride;
+ }
+ while (pointIndex < numDataPoints &&
+ !gmx_within_tol(firstValue, data[d][pointIndex], GMX_REAL_EPS));
+
+ /* The stride in dimension dimension d - 1 equals the number of points
+ dimension d. */
+ stride = numPointsInDim;
+
+ numPointsCounted = (numPointsCounted == 0) ? numPointsInDim : numPointsCounted*numPointsInDim;
+
+ numPoints[d] = numPointsInDim;
+ }
+
+ if (numPointsCounted != numDataPoints)
+ {
+ std::string mesg = gmx::formatString("Could not extract data properly from %s. Wrong data format?"
+ "\n\n%s",
+ dataFilename.c_str(), correctFormatMessage.c_str());
+ GMX_THROW(InvalidInputError(mesg));
+ }
+
+ /* The data grid has the data that was read and the properties of the AWH grid */
+ for (int d = 0; d < grid.numDimensions(); d++)
+ {
+ axis_.push_back(GridAxis(data[d][0], data[d][numDataPoints - 1],
+ grid.axis(d).period(), numPoints[d]));
+ }
+
+ /* Map each grid point to a data point. No interpolation, just pick the nearest one.
+ * It is assumed that the given data is uniformly spaced for each dimension.
+ */
+ for (size_t m = 0; m < grid.numPoints(); m++)
+ {
+ /* We only define what we need for the datagrid since it's not needed here which is a bit ugly */
+
+ if (!valueIsInGrid(grid.point(m).coordValue, axis_))
+ {
+ std::string mesg =
+ gmx::formatString("%s does not contain data for all coordinate values. "
+ "Make sure your input data covers the whole sampling domain "
+ "and is correctly formatted. \n\n%s",
+ dataFilename.c_str(), correctFormatMessage.c_str());
+ GMX_THROW(InvalidInputError(mesg));
+ }
+ (*gridpointToDatapoint)[m] = getNearestIndexInGrid(grid.point(m).coordValue, axis_);
+ }
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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
+ * This file contains datatypes and function declarations necessary
+ * for AWH to interface with the grid code.
+ *
+ * The grid organizes spatial properties of the AWH coordinate points.
+ * This includes traversing points in a specific order, locating
+ * neighboring points and calculating distances. Multiple dimensions
+ * as well as periodic dimensions are supported.
+ *
+ * \todo: Replace this by a more generic grid class once that is available.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_GRID_H
+#define GMX_AWH_GRID_H
+
+#include <memory>
+#include <string>
+
+#include "dimparams.h" /* This is needed for awh_dvec */
+
+namespace gmx
+{
+
+struct AwhDimParams;
+
+/*! \internal
+ * \brief An axis, i.e. dimension, of the grid.
+ */
+class GridAxis
+{
+ public:
+ /*! \brief Constructor.
+ *
+ * The spacing and number of points are set such that we have
+ * at least the requested point density.
+ * Requesting 0 point density results in the minimum number
+ * of points (2).
+ *
+ * \param[in] origin Starting value.
+ * \param[in] end End value.
+ * \param[in] period Period, pass 0 if not periodic.
+ * \param[in] pointDensity Requested number of point per unit of axis length.
+ */
+ GridAxis(double origin, double end,
+ double period, double pointDensity);
+
+ /*! \brief Constructor.
+ *
+ * \param[in] origin Starting value.
+ * \param[in] end End value.
+ * \param[in] period Period, pass 0 if not periodic.
+ * \param[in] numPoints The number of points.
+ */
+ GridAxis(double origin, double end,
+ double period, int numPoints);
+
+ /*! \brief Returns if the axis has periodic boundaries.
+ */
+ bool isPeriodic() const
+ {
+ return period_ > 0;
+ }
+
+ /*! \brief Returns the period of the grid along the axis.
+ */
+ double period() const
+ {
+ return period_;
+ }
+
+ /*! \brief Returns the grid origin along the axis.
+ */
+ double origin() const
+ {
+ return origin_;
+ }
+
+ /*! \brief Returns the grid point spacing along the axis.
+ */
+ double spacing() const
+ {
+ return spacing_;
+ }
+
+ /*! \brief Returns the number of grid points along the axis.
+ */
+ int numPoints() const
+ {
+ return numPoints_;
+ }
+
+ /*! \brief Returns the period of the grid in points along the axis.
+ *
+ * Returns 0 if the axis is not periodic.
+ */
+ int numPointsInPeriod() const
+ {
+ return numPointsInPeriod_;
+ }
+
+ /*! \brief Returns the length of the interval.
+ *
+ * This returns the distance obtained by connecting the origin point to
+ * the end point in the positive direction. Note that this is generally
+ * not the shortest distance. For period > 0, both origin and
+ * end are expected to take values in the same periodic interval.
+ */
+ double length() const
+ {
+ return length_;
+ }
+
+ /*! \brief Map a value to the nearest point index along an axis.
+ *
+ * \param[in] value Value along the axis.
+ * \returns the index nearest to the value.
+ */
+ int nearestIndex(double value) const;
+
+ private:
+ double origin_; /**< Interval start value */
+ double length_; /**< Interval length */
+ double period_; /**< The period, 0 if not periodic */
+ double spacing_; /**< Point spacing */
+ int numPoints_; /**< Number of points in the interval */
+ int numPointsInPeriod_; /**< Number of points in a period (0 if no periodicity) */
+};
+
+/*! \internal
+ * \brief A point in the grid.
+ *
+ * A grid point has a coordinate value and a coordinate index of the same dimensionality as the grid.
+ * It knows the the linear indices of its neighboring point (which are useful only when handed up to
+ * the grid).
+ */
+struct GridPoint
+{
+ awh_dvec coordValue; /**< Multidimensional coordinate value of this point */
+ awh_ivec index; /**< Multidimensional point indices */
+ std::vector<int> neighbor; /**< Linear point indices of the neighboring points */
+};
+
+/*! \internal
+ * \brief The grid, generally multidimensional and periodic.
+ *
+ * The grid discretizes a multidimensional space with some given resolution.
+ * Each dimension is represented by an axis which sets the spatial extent,
+ * point spacing and periodicity of the grid in that direction.
+ */
+class Grid
+{
+ private:
+ /*! \brief Initializes the grid points.
+ */
+ void initPoints();
+
+ public:
+ /*! \brief
+ * The point density per sigma of the Gaussian distribution in an umbrella.
+ *
+ * This value should be at least 1 to uniformly cover the reaction coordinate
+ * range with density and having it larger than 1 does not add information.
+ */
+ static constexpr double c_numPointsPerSigma = 1.0;
+
+ //! Cut-off in sigma for considering points, neglects 4e-8 of the density.
+ static constexpr double c_scopeCutoff = 5.5;
+
+ /*! \brief Construct a grid using AWH input parameters.
+ *
+ * \param[in] dimParams Dimension parameters including the expected inverse variance of the coordinate living on the grid (determines the grid spacing).
+ * \param[in] awhDimParams Dimension params from inputrec.
+ */
+ Grid(const std::vector<DimParams> &dimParams,
+ const AwhDimParams *awhDimParams);
+
+ /*! \brief Returns the number of points in the grid.
+ *
+ * \returns the number of points in the grid.
+ */
+ size_t numPoints() const
+ {
+ return point_.size();
+ }
+
+ /*! \brief Returns a reference to a point on the grid.
+ *
+ * \returns a constant reference to a point on the grid.
+ */
+ const GridPoint &point(size_t pointIndex) const
+ {
+ return point_[pointIndex];
+ }
+
+ /*! \brief Returns the dimensionality of the grid.
+ *
+ * \returns the dimensionality of the grid.
+ */
+ int numDimensions() const
+ {
+ return axis_.size();
+ }
+
+ /*! \brief Returns the grid axes.
+ *
+ * \returns a constant reference to the grid axes.
+ */
+ const std::vector<GridAxis> &axis() const
+ {
+ return axis_;
+ }
+
+ /*! \brief Returns a grid axis.
+ *
+ * param[in] dim Dimension to return the grid axis for.
+ * \returns a constant reference to the grid axis.
+ */
+ const GridAxis &axis(int dim) const
+ {
+ return axis_[dim];
+ }
+
+ /*! \brief Find the grid point with value nearest to the given value.
+ *
+ * \param[in] value Value vector.
+ * \returns the grid point index.
+ */
+ int nearestIndex(const awh_dvec value) const;
+
+ /*! \brief Query if the value is in the grid.
+ *
+ * \param[in] value Value vector.
+ * \returns true if the value is in the grid.
+ * \note It is assumed that any periodicity of value has already been taken care of.
+ */
+ bool covers(const awh_dvec value) const;
+
+ private:
+ std::vector<GridPoint> point_; /**< Points on the grid */
+ std::vector<GridAxis> axis_; /**< Axes, one for each dimension. */
+};
+
+/*! \endcond */
+
+/*! \brief Convert a multidimensional grid point index to a linear one.
+ *
+ * \param[in] grid The grid.
+ * \param[in] indexMulti Multidimensional grid point index to convert to a linear one.
+ * \returns the linear index.
+ */
+int multiDimGridIndexToLinear(const Grid &grid, const awh_ivec indexMulti);
+
+/*! \brief Convert multidimensional array index to a linear one.
+ *
+ * \param[in] indexMulti Multidimensional index to convert to a linear one.
+ * \param[in] numDim Number of dimensions of the array.
+ * \param[in] numPointsDim Number of points of the array.
+ * \returns the linear index.
+ * \note This function can be used without having an initialized grid.
+ */
+int multiDimArrayIndexToLinear(const awh_ivec indexMulti,
+ int numDim,
+ const awh_ivec numPointsDim);
+
+/*! \brief Convert a linear grid point index to a multidimensional one.
+ *
+ * \param[in] grid The grid.
+ * \param[in] indexLinear Linear grid point index to convert to a multidimensional one.
+ * \param[out] indexMulti The multidimensional index.
+ */
+void linearGridindexToMultiDim(const Grid &grid,
+ int indexLinear,
+ awh_ivec indexMulti);
+
+/*! \brief Convert a linear array index to a multidimensional one.
+ *
+ * \param[in] indexLinear Linear array index
+ * \param[in] ndim Number of dimensions of the array.
+ * \param[in] numPointsDim Number of points for each dimension.
+ * \param[out] indexMulti The multidimensional index.
+ */
+void linearArrayIndexToMultiDim(int indexLinear,
+ int ndim,
+ const awh_ivec numPointsDim,
+ awh_ivec indexMulti);
+
+/*! \brief
+ * Find the next grid point in the sub-part of the grid given a starting point.
+ *
+ * The given grid point index is updated to the next valid grid point index
+ * by traversing the sub-part of the grid, here termed the subgrid.
+ * Since the subgrid range might extend beyond the actual size of the grid,
+ * the subgrid is traversed until a point both in the subgrid and grid is
+ * found. If no point is found, the function returns false and the index is
+ * not modified. The starting point needs to be inside of the subgrid. However,
+ * if this index is not given, meaning < 0, then the search is initialized at
+ * the subgrid origin, i.e. in this case the "next" grid point index is
+ * defined to be the first common grid/subgrid point.
+ *
+ * \param[in] grid The grid.
+ * \param[in] subgridOrigin Vector locating the subgrid origin relative to the grid origin.
+ * \param[in] subgridNpoints Number of points along each subgrid dimension.
+ * \param[in,out] gridPointIndex Pointer to the starting/next grid point index.
+ * \returns true if the grid point was updated.
+ */
+bool advancePointInSubgrid(const Grid &grid,
+ const awh_ivec subgridOrigin,
+ const awh_ivec subgridNpoints,
+ int *gridPointIndex);
+
+/*! \brief Maps each point in the grid to a point in the data grid.
+ *
+ * This functions maps an AWH bias grid to a user provided input data grid.
+ * The value of data grid point i along dimension d is given by data[d][i].
+ * The number of dimensions of the data should equal that of the grid.
+ * A fatal error is thrown if extracting the data fails or the data does not cover the whole grid.
+ *
+ * \param[out] gridpointToDatapoint Array mapping each grid point to a data point index.
+ * \param[in] data 2D array in format ndim x ndatapoints with data grid point values.
+ * \param[in] numDataPoints Number of data points.
+ * \param[in] dataFilename The data filename.
+ * \param[in] grid The grid.
+ * \param[in] correctFormatMessage String to include in error message if extracting the data fails.
+ */
+void mapGridToDataGrid(std::vector<int> *gridpointToDatapoint,
+ const double* const *data,
+ int numDataPoints,
+ const std::string &dataFilename,
+ const Grid &grid,
+ const std::string &correctFormatMessage);
+
+/*! \brief
+ * Get the deviation along one dimension from the given value to a point in the grid.
+ *
+ * \param[in] grid The grid.
+ * \param[in] dimIndex Dimensional index in [0, ndim -1].
+ * \param[in] pointIndex Grid point index.
+ * \param[in] value Value along the given dimension.
+ * \returns the deviation of the given value to the given point.
+ */
+double getDeviationFromPointAlongGridAxis(const Grid &grid,
+ int dimIndex,
+ int pointIndex,
+ double value);
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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 the HistogramSize class.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#include "gmxpre.h"
+
+#include "histogramsize.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include <algorithm>
+
+#include "gromacs/mdtypes/awh-history.h"
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "biasparams.h"
+#include "pointstate.h"
+
+namespace gmx
+{
+
+HistogramSize::HistogramSize(const AwhBiasParams &awhBiasParams,
+ double histogramSizeInitial) :
+ numUpdates_(0),
+ histogramSize_(histogramSizeInitial),
+ inInitialStage_(awhBiasParams.eGrowth == eawhgrowthEXP_LINEAR),
+ equilibrateHistogram_(awhBiasParams.equilibrateHistogram),
+ logScaledSampleWeight_(0),
+ maxLogScaledSampleWeight_(0),
+ havePrintedAboutCovering_(false)
+{
+}
+
+double HistogramSize::newHistogramSizeInitialStage(const BiasParams ¶ms,
+ double t,
+ bool detectedCovering,
+ ArrayRef<double> weightsumCovering,
+ FILE *fplog)
+{
+ /* The histogram size is kept constant until the sampling region has been covered
+ and the the current sample weight is large enough and the histogram is ready. */
+ if (!detectedCovering ||
+ (logScaledSampleWeight_ < maxLogScaledSampleWeight_) ||
+ equilibrateHistogram_)
+ {
+ return histogramSize_;
+ }
+
+ /* Reset the covering weight histogram. If we got this far we are either entering a
+ new covering stage with a new covering histogram or exiting the initial stage
+ altogether. */
+ std::fill(weightsumCovering.begin(), weightsumCovering.end(), 0);
+
+ /* The current sample weigth is now the maximum. */
+ double prevMaxLogScaledSampleWeight = maxLogScaledSampleWeight_;
+ maxLogScaledSampleWeight_ = logScaledSampleWeight_;
+
+ /* Increase the histogram size by a constant scale factor if we can, i.e. if the sample weight
+ resulting from such a scaling is still larger than the previous maximum sample weight
+ (ensuring that the sample weights at the end of each covering stage are monotonically
+ increasing). If we cannot, exit the initial stage without changing the histogram size. */
+
+ /* The scale factor. The value is not very critical but should obviously be > 1 (or the exit
+ will happen very late) and probably < 5 or so (or there will be no initial stage). */
+ static const double growthFactor = 3;
+
+ /* The scale factor is in most cases very close to the histogram growth factor. */
+ double scaleFactor = growthFactor/(1. + params.updateWeight*params.localWeightScaling/histogramSize_);
+
+ bool exitInitialStage = (logScaledSampleWeight_ - std::log(scaleFactor) <= prevMaxLogScaledSampleWeight);
+ double newHistogramSize = exitInitialStage ? histogramSize_ : histogramSize_*growthFactor;
+
+ /* Update the AWH bias about the exit. */
+ inInitialStage_ = !exitInitialStage;
+
+ /* Print information about coverings and if there was an exit. */
+ if (fplog != nullptr)
+ {
+ std::string prefix = gmx::formatString("\nawh%d:", params.biasIndex + 1);
+ fprintf(fplog, "%s covering at t = %g ps. Decreased the update size.\n", prefix.c_str(), t);
+
+ if (exitInitialStage)
+ {
+ fprintf(fplog, "%s out of the initial stage at t = %g.\n", prefix.c_str(), t);
+ /* It would be nice to have a way of estimating a minimum time until exit but it
+ is difficult because the exit time is determined by how long it takes to cover
+ relative to the time it takes to "regaining" enough sample weight. The latter
+ is easy to calculate, but how the former depends on the histogram size
+ is not known. */
+ }
+ fflush(fplog);
+ }
+ return newHistogramSize;
+}
+
+namespace
+{
+
+/*! \brief
+ * Checks if the histogram has equilibrated to the target distribution.
+ *
+ * The histogram is considered equilibrated if, for a minimum fraction of
+ * the target region, the relative error of the sampled weight relative
+ * to the target is less than a tolerance value.
+ *
+ * \param[in] pointStates The state of the bias points.
+ * \returns true if the histogram is equilibrated.
+ */
+bool histogramIsEquilibrated(const std::vector<PointState> &pointStates)
+{
+ /* Get the total weight of the total weight histogram; needed for normalization. */
+ double totalWeight = 0;
+ int numTargetPoints = 0;
+ for (auto &pointState : pointStates)
+ {
+ if (!pointState.inTargetRegion())
+ {
+ continue;
+ }
+ totalWeight += pointState.weightSumTot();
+ numTargetPoints++;
+ }
+ GMX_RELEASE_ASSERT(totalWeight > 0, "No samples when normalizing AWH histogram.");
+ double inverseTotalWeight = 1./totalWeight;
+
+ /* Points with target weight below a certain cutoff are ignored. */
+ static const double minTargetCutoff = 0.05;
+ double minTargetWeight = 1./numTargetPoints*minTargetCutoff;
+
+ /* Points with error less than this tolerance pass the check.*/
+ static const double errorTolerance = 0.2;
+
+ /* Sum up weight of points that do or don't pass the check. */
+ double equilibratedWeight = 0;
+ double notEquilibratedWeight = 0;
+ for (auto &pointState : pointStates)
+ {
+ double targetWeight = pointState.target();
+ double sampledWeight = pointState.weightSumTot()*inverseTotalWeight;
+
+ /* Ignore these points. */
+ if (!pointState.inTargetRegion() || targetWeight < minTargetWeight)
+ {
+ continue;
+ }
+
+ if (std::abs(sampledWeight/targetWeight - 1) > errorTolerance)
+ {
+ notEquilibratedWeight += targetWeight;
+ }
+ else
+ {
+ equilibratedWeight += targetWeight;
+ }
+ }
+
+ /* It is enough if sampling in at least a fraction of the target region follows the target
+ distribution. Boundaries will in general fail and this should be ignored (to some extent). */
+ static const double minFraction = 0.8;
+
+ return equilibratedWeight/(equilibratedWeight + notEquilibratedWeight) > minFraction;;
+}
+
+} // namespace
+
+double HistogramSize::newHistogramSize(const BiasParams ¶ms,
+ double t,
+ bool covered,
+ const std::vector<PointState> &pointStates,
+ ArrayRef<double> weightsumCovering,
+ FILE *fplog)
+{
+ double newHistogramSize;
+ if (inInitialStage_)
+ {
+ /* Only bother with checking equilibration if we have covered already. */
+ if (equilibrateHistogram_ && covered)
+ {
+ /* The histogram is equilibrated at most once. */
+ equilibrateHistogram_ = !histogramIsEquilibrated(pointStates);
+
+ std::string prefix = gmx::formatString("\nawh%d:", params.biasIndex + 1);
+ if (!equilibrateHistogram_)
+ {
+ fprintf(fplog, "%s equilibrated histogram at t = %g ps.\n", prefix.c_str(), t);
+ }
+ else if (!havePrintedAboutCovering_)
+ {
+ fprintf(fplog, "%s covered but histogram not equilibrated at t = %g ps.\n", prefix.c_str(), t);
+ havePrintedAboutCovering_ = true; /* Just print once. */
+ }
+ }
+
+ /* In the initial stage, the histogram grows dynamically as a function of the number of coverings. */
+ newHistogramSize = newHistogramSizeInitialStage(params, t, covered, weightsumCovering, fplog);
+ }
+ else
+ {
+ /* If not in the initial stage, the histogram grows at a linear, possibly scaled down, rate. */
+ newHistogramSize = histogramSize_ + params.updateWeight*params.localWeightScaling;
+ }
+
+ return newHistogramSize;
+}
+
+void HistogramSize::setHistogramSize(double histogramSize,
+ double weightHistogramScalingFactor)
+{
+ GMX_ASSERT(histogramSize > 0, "The histogram should not be empty");
+ GMX_ASSERT(weightHistogramScalingFactor > 0, "The histogram scaling factor should be positive");
+
+ histogramSize_ = histogramSize;
+
+ /* The weight of new samples relative to previous ones change
+ * when the histogram is rescaled. We keep the log since this number
+ * can become very large.
+ */
+ logScaledSampleWeight_ -= std::log(weightHistogramScalingFactor);
+};
+
+void HistogramSize::restoreFromHistory(const AwhBiasStateHistory &stateHistory)
+{
+ numUpdates_ = stateHistory.numUpdates;
+ histogramSize_ = stateHistory.histSize;
+ inInitialStage_ = stateHistory.in_initial;
+ equilibrateHistogram_ = stateHistory.equilibrateHistogram;
+ logScaledSampleWeight_ = stateHistory.logScaledSampleWeight;
+ maxLogScaledSampleWeight_ = stateHistory.maxLogScaledSampleWeight;
+ havePrintedAboutCovering_ = false;
+}
+
+void HistogramSize::storeState(AwhBiasStateHistory *stateHistory) const
+{
+ stateHistory->numUpdates = numUpdates_;
+ stateHistory->histSize = histogramSize_;
+ stateHistory->in_initial = inInitialStage_;
+ stateHistory->equilibrateHistogram = equilibrateHistogram_;
+ stateHistory->logScaledSampleWeight = logScaledSampleWeight_;
+ stateHistory->maxLogScaledSampleWeight = maxLogScaledSampleWeight_;
+ /* We'll print again about covering when restoring the state */
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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
+ * Declares the HistogramSize class.
+ *
+ * The data members of this class keep track of global size and update related
+ * properties of the bias histogram and the evolution of the histogram size.
+ * Initially histogramSize_ (and thus the convergence rate) is controlled
+ * heuristically to get good initial estimates, i.e. increase the robustness
+ * of the method.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_HISTOGRAMSIZE_H
+#define GMX_AWH_HISTOGRAMSIZE_H
+
+#include <cstdio>
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+
+namespace gmx
+{
+
+struct AwhBiasStateHistory;
+struct AwhBiasParams;
+class BiasParams;
+class PointState;
+
+/*! \internal
+ * \brief Tracks global size related properties of the bias histogram.
+ *
+ * Tracks the number of updates and the histogram size.
+ * Also keep track of the stage (initial/final of the AWH method
+ * and printing warnings about covering.
+ *
+ * \note Histogram sizes are floating-point values, since the histogram uses weighted
+ * entries and we can assign a floating-point scaling factor when changing it.
+ */
+class HistogramSize
+{
+ public:
+ /*! \brief Constructor.
+ *
+ * \param[in] awhBiasParams The Bias parameters from inputrec.
+ * \param[in] histogramSizeInitial The initial histogram size.
+ */
+ HistogramSize(const AwhBiasParams &awhBiasParams,
+ double histogramSizeInitial);
+
+ private:
+ /*! \brief
+ * Returns the new size of the reference weight histogram in the initial stage.
+ *
+ * This function also takes care resetting the histogram used for covering checks
+ * and for exiting the initial stage.
+ *
+ * \param[in] params The bias parameters.
+ * \param[in] t Time.
+ * \param[in] detectedCovering True if we detected that the sampling interval has been sufficiently covered.
+ * \param[in,out] weightsumCovering The weight sum for checking covering.
+ * \param[in,out] fplog Log file.
+ * \returns the new histogram size.
+ */
+ double newHistogramSizeInitialStage(const BiasParams ¶ms,
+ double t,
+ bool detectedCovering,
+ ArrayRef<double> weightsumCovering,
+ FILE *fplog);
+
+ public:
+ /*! \brief
+ * Return the new reference weight histogram size for the current update.
+ *
+ * This function also takes care of checking for covering in the initial stage.
+ *
+ * \param[in] params The bias parameters.
+ * \param[in] t Time.
+ * \param[in] covered True if the sampling interval has been covered enough.
+ * \param[in] pointStates The state of the grid points.
+ * \param[in,out] weightsumCovering The weight sum for checking covering.
+ * \param[in,out] fplog Log file.
+ * \returns the new histogram size.
+ */
+ double newHistogramSize(const BiasParams ¶ms,
+ double t,
+ bool covered,
+ const std::vector<PointState> &pointStates,
+ ArrayRef<double> weightsumCovering,
+ FILE *fplog);
+
+ /*! \brief Restores the histogram size from history.
+ *
+ * \param[in] stateHistory The AWH bias state history.
+ */
+ void restoreFromHistory(const AwhBiasStateHistory &stateHistory);
+
+ /*! \brief Store the histogram size state in a history struct.
+ *
+ * \param[in,out] stateHistory The AWH bias state history.
+ */
+ void storeState(AwhBiasStateHistory *stateHistory) const;
+
+ /*! \brief Returns the number of updates since the start of the simulation.
+ */
+ int numUpdates() const
+ {
+ return numUpdates_;
+ };
+
+ /*! \brief Increments the number of updates by 1.
+ */
+ void incrementNumUpdates()
+ {
+ numUpdates_ += 1;
+ }
+
+ /*! \brief Returns the histogram size.
+ */
+ double histogramSize() const
+ {
+ return histogramSize_;
+ };
+
+ /*! \brief Sets the histogram size.
+ *
+ * \param[in] histogramSize The new histogram size.
+ * \param[in] weightHistogramScalingFactor The factor to scale the weight by.
+ */
+ void setHistogramSize(double histogramSize,
+ double weightHistogramScalingFactor);
+
+ /*! \brief Returns true if we are in the initial stage of the AWH method.
+ */
+ inline bool inInitialStage() const
+ {
+ return inInitialStage_;
+ };
+
+ private:
+ gmx_int64_t numUpdates_; /**< The number of updates performed since the start of the simulation. */
+
+ /* The histogram size sets the update size and so controls the convergence rate of the free energy and bias. */
+ double histogramSize_; /**< Size of reference weight histogram. */
+
+ /* Values that control the evolution of the histogram size. */
+ bool inInitialStage_; /**< True if in the intial stage. */
+ bool equilibrateHistogram_; /**< True if samples are kept from accumulating until the sampled distribution is close enough to the target. */
+ double logScaledSampleWeight_; /**< The log of the current sample weight, scaled because of the histogram rescaling. */
+ double maxLogScaledSampleWeight_; /**< Maximum sample weight obtained for previous (smaller) histogram sizes. */
+
+ /* Bool to avoid printing multiple, not so useful, messages to log */
+ bool havePrintedAboutCovering_; /**< True if we have printed about covering to the log while equilibrateHistogram==true */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_HISTOGRAMSIZE_H */
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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 the one method of the PointState class called only for one point per step.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#include "gmxpre.h"
+
+#include "pointstate.h"
+
+namespace gmx
+{
+
+namespace
+{
+
+/*! \brief Returns the exponent c where exp(c) = exp(a) + exp(b).
+ *
+ * \param[in] a First exponent.
+ * \param[in] b Second exponent.
+ * \returns c.
+ */
+double expSum(double a,
+ double b)
+{
+ return (a > b ? a : b) + std::log1p(std::exp(-std::fabs(a - b)));
+}
+
+} // namespace
+
+void PointState::samplePmf(double convolvedBias)
+{
+ if (inTargetRegion())
+ {
+ logPmfSum_ = expSum(logPmfSum_, -convolvedBias);
+ numVisitsIteration_ += 1;
+ }
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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
+ * Declares and defines the PointState class.
+ *
+ * Since nearly all operations on PointState objects occur in loops over
+ * (parts of) the grid of an AWH bias, all these methods should be inlined.
+ * Only samplePmf() is called only once per step and is thus not inlined.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_POINTSTATE_H
+#define GMX_AWH_POINTSTATE_H
+
+#include <cmath>
+
+#include "gromacs/mdtypes/awh-history.h"
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "biasparams.h"
+
+namespace gmx
+{
+
+namespace
+{
+
+//! A value that can be passed to exp() with result 0, also with SIMD
+static constexpr double c_largeNegativeExponent = -10000.0;
+
+//! The largest acceptable positive exponent for variables that are passed to exp().
+static constexpr double c_largePositiveExponent = 700.0;
+
+} // namepace
+
+/*! \internal
+ * \brief The state of a coordinate point.
+ *
+ * This class contains all the state variables of a coordinate point
+ * (on the bias grid) and methods to update the state of a point.
+ */
+class PointState
+{
+ public:
+ /*! \brief Constructs a point state with default values. */
+ PointState() : bias_(0),
+ freeEnergy_(0),
+ target_(1),
+ targetConstantWeight_(1),
+ weightSumIteration_(0),
+ weightSumTot_(0),
+ weightSumRef_(1),
+ lastUpdateIndex_(0),
+ logPmfSum_(0),
+ numVisitsIteration_(0),
+ numVisitsTot_(0)
+ {
+ };
+
+ /*! \brief
+ * Set all values in the state to those from a history.
+ *
+ * \param[in] psh Coordinate point history to copy from.
+ */
+ void setFromHistory(const AwhPointStateHistory &psh)
+ {
+ target_ = psh.target;
+ freeEnergy_ = psh.free_energy;
+ bias_ = psh.bias;
+ weightSumIteration_ = psh.weightsum_iteration;
+ weightSumTot_ = psh.weightsum_tot;
+ weightSumRef_ = psh.weightsum_ref;
+ lastUpdateIndex_ = psh.last_update_index;
+ logPmfSum_ = psh.log_pmfsum;
+ numVisitsIteration_ = psh.visits_iteration;
+ numVisitsTot_ = psh.visits_tot;
+ }
+
+ /*! \brief
+ * Store the state of a point in a history struct.
+ *
+ * \param[in,out] psh Coordinate point history to copy to.
+ */
+ void storeState(AwhPointStateHistory *psh) const
+ {
+ psh->target = target_;
+ psh->free_energy = freeEnergy_;
+ psh->bias = bias_;
+ psh->weightsum_iteration = weightSumIteration_;
+ psh->weightsum_tot = weightSumTot_;
+ psh->weightsum_ref = weightSumRef_;
+ psh->last_update_index = lastUpdateIndex_;
+ psh->log_pmfsum = logPmfSum_;
+ psh->visits_iteration = numVisitsIteration_;
+ psh->visits_tot = numVisitsTot_;
+ }
+
+ /*! \brief
+ * Query if the point is in the target region.
+ *
+ * \returns true if the point is in the target region.
+ */
+ bool inTargetRegion() const
+ {
+ return target_ > 0;
+ }
+
+ /*! \brief Return the bias function estimate. */
+ double bias() const
+ {
+ return bias_;
+ }
+
+ /*! \brief Set the target to zero and the bias to minus infinity. */
+ void setTargetToZero()
+ {
+ target_ = 0;
+ /* the bias = log(target) + const = -infty */
+ bias_ = c_largeNegativeExponent;
+ }
+
+ /*! \brief Return the free energy. */
+ double freeEnergy() const
+ {
+ return freeEnergy_;
+ }
+
+ /*! \brief Set the free energy, only to be used at initialization.
+ *
+ * \param[in] freeEnergy The free energy.
+ */
+ void setFreeEnergy(double freeEnergy)
+ {
+ freeEnergy_ = freeEnergy;
+ }
+
+ /*! \brief Return the target distribution value. */
+ double target() const
+ {
+ return target_;
+ }
+
+ /*! \brief Return the weight accumulated since the last update. */
+ double weightSumIteration() const
+ {
+ return weightSumIteration_;
+ }
+
+ /*! \brief Increases the weight accumulated since the last update.
+ *
+ * \param[in] weight The amount to add to the weight
+ */
+ void increaseWeightSumIteration(double weight)
+ {
+ weightSumIteration_ += weight;
+ }
+
+ /*! \brief Returns the accumulated weight */
+ double weightSumTot() const
+ {
+ return weightSumTot_;
+ }
+
+ /*! \brief Return the reference weight histogram. */
+ double weightSumRef() const
+ {
+ return weightSumRef_;
+ }
+
+ /*! \brief Return log(PmfSum). */
+ double logPmfSum() const
+ {
+ return logPmfSum_;
+ }
+
+ /*! \brief Set log(PmfSum).
+ *
+ * TODO: Replace this setter function with a more elegant solution.
+ *
+ * \param[in] logPmfSum The log(PmfSum).
+ */
+ void setLogPmfSum(double logPmfSum)
+ {
+ logPmfSum_ = logPmfSum;
+ }
+
+ /*! \brief Return the number of visits since the last update */
+ double numVisitsIteration() const
+ {
+ return numVisitsIteration_;
+ }
+
+ /*! \brief Return the total number of visits */
+ double numVisitsTot() const
+ {
+ return numVisitsTot_;
+ }
+
+ /*! \brief Set the constant target weight factor.
+ *
+ * \param[in] targetConstantWeight The target weight factor.
+ */
+ void setTargetConstantWeight(double targetConstantWeight)
+ {
+ targetConstantWeight_ = targetConstantWeight;
+ }
+
+ /*! \brief Updates the bias of a point. */
+ void updateBias()
+ {
+ GMX_ASSERT(target_ > 0, "AWH target distribution must be > 0 to calculate the point bias.");
+
+ bias_ = freeEnergy() + std::log(target_);
+ }
+
+ /*! \brief Set the initial reference weighthistogram.
+ *
+ * \param[in] histogramSize The weight histogram size.
+ */
+ void setInitialReferenceWeightHistogram(double histogramSize)
+ {
+ weightSumRef_ = histogramSize*target_;
+ }
+
+ /*! \brief Correct free energy and PMF sum for the change in minimum.
+ *
+ * \param[in] minimumFreeEnergy The free energy at the minimum;
+ */
+ void normalizeFreeEnergyAndPmfSum(double minimumFreeEnergy)
+ {
+ if (inTargetRegion())
+ {
+ /* The sign of the free energy and PMF constants are opposite
+ * because the PMF samples are reweighted with the negative
+ * bias e^(-bias) ~ e^(-free energy).
+ */
+ freeEnergy_ -= minimumFreeEnergy;
+ logPmfSum_ += minimumFreeEnergy;
+ }
+ }
+
+ /*! \brief Apply previous updates that were skipped.
+ *
+ * An update can only be skipped if the parameters needed for the update are constant or
+ * deterministic so that the same update can be performed at a later time.
+ * Here, the necessary parameters are the sampled weight and scaling factors for the
+ * histograms. The scaling factors are provided as arguments only to avoid recalculating
+ * them for each point
+ *
+ * The last update index is also updated here.
+ *
+ * \param[in] params The AWH bias parameters.
+ * \param[in] numUpdates The global number of updates.
+ * \param[in] weighthistScaling Scale factor for the reference weight histogram.
+ * \param[in] logPmfSumScaling Scale factor for the reference PMF histogram.
+ * \returns true if at least one update was applied.
+ */
+ bool performPreviouslySkippedUpdates(const BiasParams ¶ms,
+ gmx_int64_t numUpdates,
+ double weighthistScaling,
+ double logPmfSumScaling)
+ {
+ GMX_ASSERT(params.skipUpdates(), "Calling function for skipped updates when skipping updates is not allowed");
+
+ if (!inTargetRegion())
+ {
+ return false;
+ }
+
+ /* The most current past update */
+ gmx_int64_t lastUpdateIndex = numUpdates;
+ gmx_int64_t numUpdatesSkipped = lastUpdateIndex - lastUpdateIndex_;
+
+ if (numUpdatesSkipped == 0)
+ {
+ /* Was not updated */
+ return false;
+ }
+
+ for (int i = 0; i < numUpdatesSkipped; i++)
+ {
+ /* This point was non-local at the time of the update meaning no weight */
+ updateFreeEnergyAndWeight(params, 0, weighthistScaling, logPmfSumScaling);
+ }
+
+ /* Only past updates are applied here. */
+ lastUpdateIndex_ = lastUpdateIndex;
+
+ return true;
+ }
+
+ /*! \brief Apply a point update with new sampling.
+ *
+ * \note The last update index is also updated here.
+ * \note The new sampling containers are cleared here.
+ *
+ * \param[in] params The AWH bias parameters.
+ * \param[in] numUpdates The global number of updates.
+ * \param[in] weighthistScaling Scaling factor for the reference weight histogram.
+ * \param[in] logPmfSumScaling Log of the scaling factor for the PMF histogram.
+ */
+ void updateWithNewSampling(const BiasParams ¶ms,
+ gmx_int64_t numUpdates,
+ double weighthistScaling,
+ double logPmfSumScaling)
+ {
+ GMX_RELEASE_ASSERT(lastUpdateIndex_ == numUpdates, "When doing a normal update, the point update index should match the global index, otherwise we lost (skipped?) updates.");
+
+ updateFreeEnergyAndWeight(params, weightSumIteration_, weighthistScaling, logPmfSumScaling);
+ lastUpdateIndex_ += 1;
+
+ /* Clear the iteration collection data */
+ weightSumIteration_ = 0;
+ numVisitsIteration_ = 0;
+ }
+
+
+ /*! \brief Update the PMF histogram with the current coordinate value.
+ *
+ * \param[in] convolvedBias The convolved bias.
+ */
+ void samplePmf(double convolvedBias);
+
+ private:
+ /*! \brief Update the free energy estimate of a point.
+ *
+ * The free energy update here is inherently local, i.e. it just depends on local sampling and on constant
+ * AWH parameters. This assumes that the variables used here are kept constant, at least in between
+ * global updates.
+ *
+ * \param[in] params The AWH bias parameters.
+ * \param[in] weightAtPoint Sampled probability weight at this point.
+ */
+ void updateFreeEnergy(const BiasParams ¶ms,
+ double weightAtPoint)
+ {
+ double weighthistSampled = weightSumRef() + weightAtPoint;
+ double weighthistTarget = weightSumRef() + params.updateWeight*target_;
+
+ double df = -std::log(weighthistSampled/weighthistTarget);
+ freeEnergy_ += df;
+
+ GMX_RELEASE_ASSERT(std::abs(freeEnergy_) < c_largePositiveExponent,
+ "Very large free energy differences or badly normalized free energy in AWH update.");
+ }
+
+ /*! \brief Update the reference weight histogram of a point.
+ *
+ * \param[in] params The AWH bias parameters.
+ * \param[in] weightAtPoint Sampled probability weight at this point.
+ * \param[in] scaleFactor Factor to rescale the histogram with.
+ */
+ void updateWeightHistogram(const BiasParams ¶ms,
+ double weightAtPoint,
+ double scaleFactor)
+ {
+ if (params.idealWeighthistUpdate)
+ {
+ /* Grow histogram using the target distribution. */
+ weightSumRef_ += target_*params.updateWeight*params.localWeightScaling;
+ }
+ else
+ {
+ /* Grow using the actual samples (which are distributed ~ as target). */
+ weightSumRef_ += weightAtPoint*params.localWeightScaling;
+ }
+
+ weightSumRef_ *= scaleFactor;
+ }
+
+ /*! \brief Apply a point update.
+ *
+ * This updates local properties that can be updated without
+ * accessing or affecting all points.
+ * This excludes updating the size of reference weight histogram and
+ * the target distribution. The bias update is excluded only because
+ * if updates have been skipped this function will be called multiple
+ * times, while the bias only needs to be updated once (last).
+ *
+ * Since this function only performs the update with the given
+ * arguments and does not know anything about the time of the update,
+ * the last update index is not updated here. The caller should take
+ * care of updating the update index.
+ *
+ * \param[in] params The AWH bias parameters.
+ * \param[in] weightAtPoint Sampled probability weight at this point.
+ * \param[in] weighthistScaling Scaling factor for the reference weight histogram.
+ * \param[in] logPmfSumScaling Log of the scaling factor for the PMF histogram.
+ */
+ void updateFreeEnergyAndWeight(const BiasParams ¶ms,
+ double weightAtPoint,
+ double weighthistScaling,
+ double logPmfSumScaling)
+ {
+ updateFreeEnergy(params, weightAtPoint);
+ updateWeightHistogram(params, weightAtPoint, weighthistScaling);
+ logPmfSum_ += logPmfSumScaling;
+ }
+
+
+ public:
+ /*! \brief Update the target weight of a point.
+ *
+ * Note that renormalization over all points is needed after the update.
+ *
+ * \param[in] params The AWH bias parameters.
+ * \param[in] freeEnergyCutoff The cut-off for the free energy for target type "cutoff".
+ * \returns the updated value of the target.
+ */
+ double updateTargetWeight(const BiasParams ¶ms,
+ double freeEnergyCutoff)
+ {
+ switch (params.eTarget)
+ {
+ case eawhtargetCONSTANT:
+ target_ = 1;
+ break;
+ case eawhtargetCUTOFF:
+ {
+ double df = freeEnergy_ - freeEnergyCutoff;
+ target_ = 1/(1 + std::exp(df));
+ break;
+ }
+ case eawhtargetBOLTZMANN:
+ target_ = std::exp(-params.temperatureScaleFactor*freeEnergy_);
+ break;
+ case eawhtargetLOCALBOLTZMANN:
+ target_ = weightSumRef_;
+ break;
+ }
+
+ /* All target types can be modulated by a constant factor. */
+ target_ *= targetConstantWeight_;
+
+ return target_;
+ }
+
+ /*! \brief Set the weight and count accumulated since the last update.
+ *
+ * \param[in] weightSum The weight-sum value
+ * \param[in] numVisits The number of visits
+ */
+ void setPartialWeightAndCount(double weightSum,
+ double numVisits)
+ {
+ weightSumIteration_ = weightSum;
+ numVisitsIteration_ = numVisits;
+ }
+
+ /*! \brief Add the weights and counts accumulated between updates. */
+ void addPartialWeightAndCount()
+ {
+ weightSumTot_ += weightSumIteration_;
+ numVisitsTot_ += numVisitsIteration_;
+ }
+
+ /*! \brief Scale the target weight of the point.
+ *
+ * \param[in] scaleFactor Factor to scale with.
+ */
+ void scaleTarget(double scaleFactor)
+ {
+ target_ *= scaleFactor;
+ }
+
+ private:
+ double bias_; /**< Current biasing function estimate */
+ double freeEnergy_; /**< Current estimate of the convolved free energy/PMF. */
+ double target_; /**< Current target distribution, normalized to 1 */
+ double targetConstantWeight_; /**< Constant target weight, from user data. */
+ double weightSumIteration_; /**< Accumulated weight this iteration; note: only contains data for this Bias, even when sharing biases. */
+ double weightSumTot_; /**< Accumulated weights, never reset */
+ double weightSumRef_; /**< The reference weight histogram determining the free energy updates */
+ gmx_int64_t lastUpdateIndex_; /**< The last update that was performed at this point, in units of number of updates. */
+ double logPmfSum_; /**< Logarithm of the PMF histogram */
+ double numVisitsIteration_; /**< Visits to this bin this iteration; note: only contains data for this Bias, even when sharing biases. */
+ double numVisitsTot_; /**< Accumulated visits to this bin */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_POINTSTATE_H */
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017, 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.
+ */
+#include "gmxpre.h"
+
+#include "read-params.h"
+
+#include "gromacs/awh/awh.h"
+#include "gromacs/fileio/readinp.h"
+#include "gromacs/fileio/warninp.h"
+#include "gromacs/math/units.h"
+#include "gromacs/math/utilities.h"
+#include "gromacs/math/vec.h"
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/pull-params.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/pulling/pull.h"
+#include "gromacs/random/seed.h"
+#include "gromacs/utility/cstringutil.h"
+#include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/smalloc.h"
+
+#include "biasparams.h"
+#include "biassharing.h"
+
+namespace gmx
+{
+
+const char *eawhtarget_names[eawhtargetNR+1] = {
+ "constant", "cutoff", "boltzmann", "local-boltzmann", nullptr
+};
+
+const char *eawhgrowth_names[eawhgrowthNR+1] = {
+ "exp-linear", "linear", nullptr
+};
+
+const char *eawhpotential_names[eawhpotentialNR+1] = {
+ "convolved", "umbrella", nullptr
+};
+
+const char *eawhcoordprovider_names[eawhcoordproviderNR+1] = {
+ "pull", nullptr
+};
+
+/*! \brief
+ * Read parameters of an AWH bias dimension.
+ *
+ * \param[in,out] ninp_p Number of read input file entries.
+ * \param[in,out] inp_p Input file entries.
+ * \param[in] prefix Prefix for dimension parameters.
+ * \param[in,out] dimParams AWH dimensional parameters.
+ * \param[in] pull_params Pull parameters.
+ * \param[in,out] wi Struct for bookeeping warnings.
+ * \param[in] bComment True if comments should be printed.
+ */
+static void readDimParams(int *ninp_p, t_inpfile **inp_p, const char *prefix,
+ AwhDimParams *dimParams, const pull_params_t *pull_params,
+ warninp_t wi, bool bComment)
+{
+ char warningmsg[STRLEN];
+
+ int ninp = *ninp_p;
+ t_inpfile *inp = *inp_p;
+
+ if (bComment)
+ {
+ CTYPE("The provider of the reaction coordinate, currently only pull is supported");
+ }
+ char opt[STRLEN];
+ sprintf(opt, "%s-coord-provider", prefix);
+ EETYPE(opt, dimParams->eCoordProvider, eawhcoordprovider_names);
+
+ if (bComment)
+ {
+ CTYPE("The coordinate index for this dimension");
+ }
+ sprintf(opt, "%s-coord-index", prefix);
+ int coordIndexInput;
+ ITYPE(opt, coordIndexInput, 1);
+ if (coordIndexInput < 1)
+ {
+ gmx_fatal(FARGS, "Failed to read a valid coordinate index for %s. "
+ "Note that the pull coordinate indexing starts at 1.", opt);
+ }
+
+ /* The pull coordinate indices start at 1 in the input file, at 0 internally */
+ dimParams->coordIndex = coordIndexInput - 1;
+
+ /* The pull settings need to be consistent with the AWH settings */
+ if (!(pull_params->coord[dimParams->coordIndex].eType == epullEXTERNAL) )
+ {
+ gmx_fatal(FARGS, "AWH biasing can only be applied to pull type %s",
+ EPULLTYPE(epullEXTERNAL));
+ }
+
+ if (dimParams->coordIndex >= pull_params->ncoord)
+ {
+ gmx_fatal(FARGS, "The given AWH coordinate index (%d) is larger than the number of pull coordinates (%d)",
+ coordIndexInput, pull_params->ncoord);
+ }
+ if (pull_params->coord[dimParams->coordIndex].rate != 0)
+ {
+ sprintf(warningmsg, "Setting pull-coord%d-rate (%g) is incompatible with AWH biasing this coordinate", coordIndexInput, pull_params->coord[dimParams->coordIndex].rate);
+ warning_error(wi, warningmsg);
+ }
+
+ /* Grid params for each axis */
+ int eGeom = pull_params->coord[dimParams->coordIndex].eGeom;
+
+ if (bComment)
+ {
+ CTYPE("Start and end values for each coordinate dimension");
+ }
+
+ sprintf(opt, "%s-start", prefix);
+ RTYPE(opt, dimParams->origin, 0.);
+
+ sprintf(opt, "%s-end", prefix);
+ RTYPE(opt, dimParams->end, 0.);
+
+ if (gmx_within_tol(dimParams->end - dimParams->origin, 0, GMX_REAL_EPS))
+ {
+ sprintf(warningmsg, "The given interval length given by %s-start (%g) and %s-end (%g) is zero. "
+ "This will result in only one point along this axis in the coordinate value grid.",
+ prefix, dimParams->origin, prefix, dimParams->end);
+ warning(wi, warningmsg);
+ }
+ /* Check that the requested interval is in allowed range */
+ if (eGeom == epullgDIST)
+ {
+ if (dimParams->origin < 0 || dimParams->end < 0)
+ {
+ gmx_fatal(FARGS, "%s-start (%g) or %s-end (%g) set to a negative value. With pull geometry distance coordinate values are non-negative. "
+ "Perhaps you want to use geometry %s instead?",
+ prefix, dimParams->origin, prefix, dimParams->end, EPULLGEOM(epullgDIR));
+ }
+ }
+ else if (eGeom == epullgANGLE || eGeom == epullgANGLEAXIS)
+ {
+ if (dimParams->origin < 0 || dimParams->end > 180)
+ {
+ gmx_fatal(FARGS, "%s-start (%g) and %s-end (%g) are outside of the allowed range 0 to 180 deg for pull geometries %s and %s ",
+ prefix, dimParams->origin, prefix, dimParams->end, EPULLGEOM(epullgANGLE), EPULLGEOM(epullgANGLEAXIS));
+ }
+ }
+ else if (eGeom == epullgDIHEDRAL)
+ {
+ if (dimParams->origin < -180 || dimParams->end > 180)
+ {
+ gmx_fatal(FARGS, "%s-start (%g) and %s-end (%g) are outside of the allowed range -180 to 180 deg for pull geometry %s. ",
+ prefix, dimParams->origin, prefix, dimParams->end, EPULLGEOM(epullgDIHEDRAL));
+ }
+ }
+
+ if (bComment)
+ {
+ CTYPE("The force constant for this coordinate (kJ/mol/nm^2 or kJ/mol/rad^2)");
+ }
+ sprintf(opt, "%s-force-constant", prefix);
+ RTYPE(opt, dimParams->forceConstant, 0);
+ if (dimParams->forceConstant <= 0)
+ {
+ warning_error(wi, "The force AWH bias force constant should be > 0");
+ }
+
+ if (bComment)
+ {
+ CTYPE("Estimated diffusion constant (nm^2/ps or rad^2/ps)");
+ }
+ sprintf(opt, "%s-diffusion", prefix);
+ RTYPE(opt, dimParams->diffusion, 0);
+
+ if (dimParams->diffusion <= 0)
+ {
+ const double diffusion_default = 1e-5;
+ sprintf(warningmsg, "%s not explicitly set by user."
+ " You can choose to use a default value (%g nm^2/ps or rad^2/ps) but this may very well be non-optimal for your system!",
+ opt, diffusion_default);
+ warning(wi, warningmsg);
+ dimParams->diffusion = diffusion_default;
+ }
+
+ if (bComment)
+ {
+ CTYPE("Diameter that needs to be sampled around a point before it is considered covered.");
+ }
+ sprintf(opt, "%s-cover-diameter", prefix);
+ RTYPE(opt, dimParams->coverDiameter, 0);
+
+ if (dimParams->coverDiameter < 0)
+ {
+ gmx_fatal(FARGS, "%s (%g) cannot be negative.",
+ opt, dimParams->coverDiameter);
+ }
+
+ *ninp_p = ninp;
+ *inp_p = inp;
+}
+
+/*! \brief
+ * Check consistency of input at the AWH bias level.
+ *
+ * \param[in] awhBiasParams AWH bias parameters.
+ * \param[in,out] wi Struct for bookkeeping warnings.
+ */
+static void checkInputConsistencyAwhBias(const AwhBiasParams &awhBiasParams,
+ warninp_t wi)
+{
+ /* Covering diameter and sharing warning. */
+ for (int d = 0; d < awhBiasParams.ndim; d++)
+ {
+ double coverDiameter = awhBiasParams.dimParams[d].coverDiameter;
+ if (awhBiasParams.shareGroup <= 0 && coverDiameter > 0)
+ {
+ warning(wi, "The covering diameter is only relevant to set for bias sharing simulations.");
+ }
+ }
+}
+
+/*! \brief
+ * Read parameters of an AWH bias.
+ *
+ * \param[in,out] ninp_p Number of read input file entries.
+ * \param[in,out] inp_p Input file entries.
+ * \param[in,out] awhBiasParams AWH dimensional parameters.
+ * \param[in] prefix Prefix for bias parameters.
+ * \param[in] ir Input parameter struct.
+ * \param[in,out] wi Struct for bookeeping warnings.
+ * \param[in] bComment True if comments should be printed.
+ */
+static void read_bias_params(int *ninp_p, t_inpfile **inp_p, AwhBiasParams *awhBiasParams, const char *prefix,
+ const t_inputrec *ir, warninp_t wi, bool bComment)
+{
+ int ninp;
+ t_inpfile *inp;
+ char opt[STRLEN], prefixdim[STRLEN];
+ char warningmsg[STRLEN];
+
+ /* These are assumed to be declared by the gromacs reading functions */
+ ninp = *ninp_p;
+ inp = *inp_p;
+
+ if (bComment)
+ {
+ CTYPE("Estimated initial PMF error (kJ/mol)");
+ }
+ sprintf(opt, "%s-error-init", prefix);
+
+ /* We allow using a default value here without warning (but warn the user if the diffusion constant is not set). */
+ RTYPE(opt, awhBiasParams->errorInitial, 10);
+ if (awhBiasParams->errorInitial <= 0)
+ {
+ gmx_fatal(FARGS, "%s (%d) needs to be > 0.", opt);
+ }
+
+ if (bComment)
+ {
+ CTYPE("Growth rate of the reference histogram determining the bias update size: exp-linear or linear");
+ }
+ sprintf(opt, "%s-growth", prefix);
+ EETYPE(opt, awhBiasParams->eGrowth, eawhgrowth_names);
+
+ if (bComment)
+ {
+ CTYPE("Start the simulation by equilibrating histogram towards the target distribution: no or yes");
+ }
+ sprintf(opt, "%s-equilibrate-histogram", prefix);
+ EETYPE(opt, awhBiasParams->equilibrateHistogram, yesno_names);
+ if (awhBiasParams->equilibrateHistogram && awhBiasParams->eGrowth != eawhgrowthEXP_LINEAR)
+ {
+ sprintf(warningmsg, "Option %s will only have an effect for histogram growth type '%s'.",
+ opt, EAWHGROWTH(eawhgrowthEXP_LINEAR));
+ warning(wi, warningmsg);
+ }
+
+ if (bComment)
+ {
+ CTYPE("Target distribution type: constant, cutoff, boltzmann or local-boltzmann");
+ }
+ sprintf(opt, "%s-target", prefix);
+ EETYPE(opt, awhBiasParams->eTarget, eawhtarget_names);
+
+ if ((awhBiasParams->eTarget == eawhtargetLOCALBOLTZMANN) &&
+ (awhBiasParams->eGrowth == eawhgrowthEXP_LINEAR))
+ {
+ sprintf(warningmsg, "Target type '%s' combined with histogram growth type '%s' is not "
+ "expected to give stable bias updates. You probably want to use growth type "
+ "'%s' instead.",
+ EAWHTARGET(eawhtargetLOCALBOLTZMANN), EAWHGROWTH(eawhgrowthEXP_LINEAR),
+ EAWHGROWTH(eawhgrowthLINEAR));
+ warning(wi, warningmsg);
+ }
+
+ if (bComment)
+ {
+ CTYPE("Boltzmann beta scaling factor for target distribution types 'boltzmann' and 'boltzmann-local'");
+ }
+ sprintf(opt, "%s-target-beta-scaling", prefix);
+ RTYPE(opt, awhBiasParams->targetBetaScaling, 0);
+
+ switch (awhBiasParams->eTarget)
+ {
+ case eawhtargetBOLTZMANN:
+ case eawhtargetLOCALBOLTZMANN:
+ if (awhBiasParams->targetBetaScaling < 0 || awhBiasParams->targetBetaScaling > 1)
+ {
+ gmx_fatal(FARGS, "%s = %g is not useful for target type %s.",
+ opt, awhBiasParams->targetBetaScaling, EAWHTARGET(awhBiasParams->eTarget));
+ }
+ break;
+ default:
+ if (awhBiasParams->targetBetaScaling != 0)
+ {
+ gmx_fatal(FARGS, "Value for %s (%g) set explicitly but will not be used for target type %s.",
+ opt, awhBiasParams->targetBetaScaling, EAWHTARGET(awhBiasParams->eTarget));
+ }
+ break;
+ }
+
+ if (bComment)
+ {
+ CTYPE("Free energy cutoff value for target distribution type 'cutoff'");
+ }
+ sprintf(opt, "%s-target-cutoff", prefix);
+ RTYPE(opt, awhBiasParams->targetCutoff, 0);
+
+ switch (awhBiasParams->eTarget)
+ {
+ case eawhtargetCUTOFF:
+ if (awhBiasParams->targetCutoff <= 0)
+ {
+ gmx_fatal(FARGS, "%s = %g is not useful for target type %s.",
+ opt, awhBiasParams->targetCutoff, EAWHTARGET(awhBiasParams->eTarget));
+ }
+ break;
+ default:
+ if (awhBiasParams->targetCutoff != 0)
+ {
+ gmx_fatal(FARGS, "Value for %s (%g) set explicitly but will not be used for target type %s.",
+ opt, awhBiasParams->targetCutoff, EAWHTARGET(awhBiasParams->eTarget));
+ }
+ break;
+ }
+
+ if (bComment)
+ {
+ CTYPE("Initialize PMF and target with user data: no or yes");
+ }
+ sprintf(opt, "%s-user-data", prefix);
+ EETYPE(opt, awhBiasParams->bUserData, yesno_names);
+
+ if (bComment)
+ {
+ CTYPE("Group index to share the bias with, 0 means not shared");
+ }
+ sprintf(opt, "%s-share-group", prefix);
+ ITYPE(opt, awhBiasParams->shareGroup, 0);
+ if (awhBiasParams->shareGroup < 0)
+ {
+ warning_error(wi, "AWH bias share-group should be >= 0");
+ }
+
+ if (bComment)
+ {
+ CTYPE("Dimensionality of the coordinate");
+ }
+ sprintf(opt, "%s-ndim", prefix);
+ ITYPE(opt, awhBiasParams->ndim, 0);
+
+ if (awhBiasParams->ndim <= 0 ||
+ awhBiasParams->ndim > c_biasMaxNumDim)
+ {
+ gmx_fatal(FARGS, "%s-ndim (%d) needs to be > 0 and at most %d\n", prefix, awhBiasParams->ndim, c_biasMaxNumDim);
+ }
+ if (awhBiasParams->ndim > 2)
+ {
+ warning_note(wi, "For awh-dim > 2 the estimate based on the diffusion and the initial error is currently only a rough guideline."
+ " You should verify its usefulness for your system before production runs!");
+ }
+ snew(awhBiasParams->dimParams, awhBiasParams->ndim);
+ for (int d = 0; d < awhBiasParams->ndim; d++)
+ {
+ bComment = bComment && d == 0;
+ sprintf(prefixdim, "%s-dim%d", prefix, d + 1);
+ readDimParams(&ninp, &inp, prefixdim, &awhBiasParams->dimParams[d], ir->pull, wi, bComment);
+ }
+
+ /* Check consistencies here that cannot be checked at read time at a lower level. */
+ checkInputConsistencyAwhBias(*awhBiasParams, wi);
+
+ *ninp_p = ninp;
+ *inp_p = inp;
+}
+
+/*! \brief
+ * Check consistency of input at the AWH level.
+ *
+ * \param[in] awhParams AWH parameters.
+ * \param[in,out] wi Struct for bookkeeping warnings.
+ */
+static void checkInputConsistencyAwh(const AwhParams &awhParams,
+ warninp_t wi)
+{
+ /* Each pull coord can map to at most 1 AWH coord.
+ * Check that we have a shared bias when requesting multisim sharing.
+ */
+ bool haveSharedBias = false;
+ for (int k1 = 0; k1 < awhParams.numBias; k1++)
+ {
+ const AwhBiasParams &awhBiasParams1 = awhParams.awhBiasParams[k1];
+
+ if (awhBiasParams1.shareGroup > 0)
+ {
+ haveSharedBias = true;
+ }
+
+ /* k1 is the reference AWH, k2 is the AWH we compare with (can be equal to k1) */
+ for (int k2 = k1; k2 < awhParams.numBias; k2++)
+ {
+ for (int d1 = 0; d1 < awhBiasParams1.ndim; d1++)
+ {
+ const AwhBiasParams &awhBiasParams2 = awhParams.awhBiasParams[k2];
+
+ /* d1 is the reference dimension of the reference AWH. d2 is the dim index of the AWH to compare with. */
+ for (int d2 = 0; d2 < awhBiasParams2.ndim; d2++)
+ {
+ /* Give an error if (d1, k1) is different from (d2, k2) but the pull coordinate is the same */
+ if ( (d1 != d2 || k1 != k2) && (awhBiasParams1.dimParams[d1].coordIndex == awhBiasParams2.dimParams[d2].coordIndex) )
+ {
+ char errormsg[STRLEN];
+ sprintf(errormsg, "One pull coordinate (%d) cannot be mapped to two separate AWH dimensions (awh%d-dim%d and awh%d-dim%d). "
+ "If this is really what you want to do you will have to duplicate this pull coordinate.",
+ awhBiasParams1.dimParams[d1].coordIndex + 1, k1 + 1, d1 + 1, k2 + 1, d2 + 1);
+ gmx_fatal(FARGS, errormsg);
+ }
+ }
+ }
+ }
+ }
+
+ if (awhParams.shareBiasMultisim && !haveSharedBias)
+ {
+ warning(wi, "Sharing of biases over multiple simulations is requested, but no bias is marked as shared (share-group > 0)");
+ }
+
+ /* mdrun does not support this (yet), but will check again */
+ if (haveBiasSharingWithinSimulation(awhParams))
+ {
+ warning(wi, "You have shared biases within a single simulation, but mdrun does not support this (yet)");
+ }
+}
+
+AwhParams *readAndCheckAwhParams(int *ninp_p, t_inpfile **inp_p, const t_inputrec *ir, warninp_t wi)
+{
+ char opt[STRLEN], prefix[STRLEN], prefixawh[STRLEN];
+
+ AwhParams *awhParams;
+ snew(awhParams, 1);
+
+ int ninp = *ninp_p;
+ t_inpfile *inp = *inp_p;
+
+ sprintf(prefix, "%s", "awh");
+
+ /* Parameters common for all biases */
+
+ CTYPE("The way to apply the biasing potential: convolved or umbrella");
+ sprintf(opt, "%s-potential", prefix);
+ EETYPE(opt, awhParams->ePotential, eawhpotential_names);
+
+ CTYPE("The random seed used for sampling the umbrella center in the case of umbrella type potential");
+ sprintf(opt, "%s-seed", prefix);
+ ITYPE(opt, awhParams->seed, -1);
+ if (awhParams->seed == -1)
+ {
+ awhParams->seed = static_cast<int>(gmx::makeRandomSeed());
+ fprintf(stderr, "Setting the AWH bias MC random seed to %" GMX_PRId64 "\n", awhParams->seed);
+ }
+
+ CTYPE("Data output interval in number of steps");
+ sprintf(opt, "%s-nstout", prefix);
+ ITYPE(opt, awhParams->nstOut, 100000);
+ if (awhParams->nstOut <= 0)
+ {
+ char buf[STRLEN];
+ sprintf(buf, "Not writing AWH output with AWH (%s = %d) does not make sense",
+ opt, awhParams->nstOut);
+ warning_error(wi, buf);
+ }
+
+ CTYPE("Coordinate sampling interval in number of steps");
+ sprintf(opt, "%s-nstsample", prefix);
+ ITYPE(opt, awhParams->nstSampleCoord, 10);
+
+ CTYPE("Free energy and bias update interval in number of samples");
+ sprintf(opt, "%s-nsamples-update", prefix);
+ ITYPE(opt, awhParams->numSamplesUpdateFreeEnergy, 10);
+
+ CTYPE("When true, biases with share-group>0 are shared between multiple simulations");
+ sprintf(opt, "%s-share-multisim", prefix);
+ EETYPE(opt, awhParams->shareBiasMultisim, yesno_names);
+
+ CTYPE("The number of independent AWH biases");
+ sprintf(opt, "%s-nbias", prefix);
+ ITYPE(opt, awhParams->numBias, 1);
+ if (awhParams->numBias <= 0)
+ {
+ gmx_fatal(FARGS, "%s needs to be an integer > 0", opt);
+ }
+
+ /* Read the parameters specific to each AWH bias */
+ snew(awhParams->awhBiasParams, awhParams->numBias);
+
+ for (int k = 0; k < awhParams->numBias; k++)
+ {
+ bool bComment = (k == 0);
+ sprintf(prefixawh, "%s%d", prefix, k + 1);
+ read_bias_params(&ninp, &inp, &awhParams->awhBiasParams[k], prefixawh, ir, wi, bComment);
+ }
+
+ /* Do a final consistency check before returning */
+ checkInputConsistencyAwh(*awhParams, wi);
+
+ if (ir->init_step != 0)
+ {
+ warning_error(wi, "With AWH init-step should be 0");
+ }
+
+ *ninp_p = ninp;
+ *inp_p = inp;
+
+ return awhParams;
+}
+
+/*! \brief
+ * Gets the period of a pull coordinate.
+ *
+ * \param[in] pull_params Pull parameters.
+ * \param[in] coord_ind Pull coordinate index.
+ * \param[in] box Box vectors.
+ * \returns the period (or 0 if not periodic).
+ */
+static double get_pull_coord_period(const pull_params_t *pull_params,
+ int coord_ind,
+ const matrix box)
+{
+ double period;
+ t_pull_coord *pcrd_params = &pull_params->coord[coord_ind];
+
+ if (pcrd_params->eGeom == epullgDIRPBC)
+ {
+ /* For direction periodic, we need the pull vector to be one of the box vectors
+ (or more generally I guess it could be an integer combination of boxvectors).
+ This boxvector should to be orthogonal to the (periodic) plane spanned by the other two box vectors.
+ Here we assume that the pull vector is either x, y or z.
+ * E.g. for pull vec = (1, 0, 0) the box vector tensor should look like:
+ * | x 0 0 |
+ * | 0 a c |
+ * | 0 b d |
+ *
+ The period is then given by the box length x.
+
+ Note: we make these checks here for AWH and not in pull because we allow pull to be more general.
+ */
+ int m_pullvec, count_nonzeros = 0;
+
+ /* Check that pull vec has only one component and which component it is. This component gives the relevant box vector */
+ for (int m = 0; m < DIM; m++)
+ {
+ if (pcrd_params->vec[m] != 0)
+ {
+ m_pullvec = m;
+ count_nonzeros++;
+ }
+ }
+ if (count_nonzeros != 1)
+ {
+ gmx_fatal(FARGS, "For AWH biasing pull coordinate %d with pull geometry %s, the pull vector needs to be parallel to "
+ "a box vector that is parallel to either the x, y or z axis and is orthogonal to the other box vectors.",
+ coord_ind + 1, EPULLGEOM(epullgDIRPBC));
+ }
+
+ /* Check that there is a box vec parallel to pull vec and that this boxvec is orthogonal to the other box vectors */
+ for (int m = 0; m < DIM; m++)
+ {
+ for (int n = 0; n < DIM; n++)
+ {
+ if ((n != m) && (n == m_pullvec || m == m_pullvec) && box[m][n] > 0)
+ {
+ gmx_fatal(FARGS, "For AWH biasing pull coordinate %d with pull geometry %s, there needs to be a box vector parallel to the pull vector that is "
+ "orthogonal to the other box vectors.",
+ coord_ind + 1, EPULLGEOM(epullgDIRPBC));
+ }
+ }
+ }
+
+ /* If this box vector only has one component as we assumed the norm should be equal to the absolute value of that component */
+ period = static_cast<double>(norm(box[m_pullvec]));
+ }
+ else if (pcrd_params->eGeom == epullgDIHEDRAL)
+ {
+ /* The dihedral angle is periodic in -180 to 180 deg */
+ period = 360;
+ }
+ else
+ {
+ period = 0;
+ }
+
+ return period;
+}
+
+/*! \brief
+ * Checks if the given interval is defined in the correct periodic interval.
+ *
+ * \param[in] origin Start value of interval.
+ * \param[in] end End value of interval.
+ * \param[in] period Period (or 0 if not periodic).
+ * \returns true if the end point values are in the correct periodic interval.
+ */
+static bool intervalIsInPeriodicInterval(double origin, double end, double period)
+{
+ return (period == 0) || (std::fabs(origin) <= 0.5*period && std::fabs(end) <= 0.5*period);
+}
+
+/*! \brief
+ * Checks if a value is within an interval.
+ *
+ * \param[in] origin Start value of interval.
+ * \param[in] end End value of interval.
+ * \param[in] period Period (or 0 if not periodic).
+ * \param[in] value Value to check.
+ * \returns true if the value is within the interval.
+ */
+static bool valueIsInInterval(double origin, double end, double period, double value)
+{
+ bool bIn_interval;
+
+ if (period > 0)
+ {
+ if (origin < end)
+ {
+ /* The interval closes within the periodic interval */
+ bIn_interval = (value >= origin) && (value <= end);
+ }
+ else
+ {
+ /* The interval wraps around the periodic boundary */
+ bIn_interval = ((value >= origin) && (value <= 0.5*period)) || ((value >= -0.5*period) && (value <= end));
+ }
+ }
+ else
+ {
+ bIn_interval = (value >= origin) && (value <= end);
+ }
+
+ return bIn_interval;
+}
+
+/*! \brief
+ * Check if the starting configuration is consistent with the given interval.
+ *
+ * \param[in] awhParams AWH parameters.
+ * \param[in,out] wi Struct for bookeeping warnings.
+ */
+static void checkInputConsistencyInterval(const AwhParams *awhParams, warninp_t wi)
+{
+ for (int k = 0; k < awhParams->numBias; k++)
+ {
+ AwhBiasParams *awhBiasParams = &awhParams->awhBiasParams[k];
+ for (int d = 0; d < awhBiasParams->ndim; d++)
+ {
+ AwhDimParams *dimParams = &awhBiasParams->dimParams[d];
+ int coordIndex = dimParams->coordIndex;
+ double origin = dimParams->origin, end = dimParams->end, period = dimParams->period;
+ double coordValueInit = dimParams->coordValueInit;
+
+ if ((period == 0) && (origin > end))
+ {
+ gmx_fatal(FARGS, "For the non-periodic pull coordinates awh%d-dim%d-start cannot be larger than awh%d-dim%d-end",
+ k + 1, d + 1, origin, k + 1, d + 1, end);
+ }
+
+ /* Currently we assume symmetric periodic intervals, meaning we use [-period/2, period/2] as the reference interval.
+ Make sure the AWH interval is within this reference interval.
+
+ Note: we could fairly simply allow using a more general interval (e.g. [x, x + period]) but it complicates
+ things slightly and I don't see that there is a great need for it. It would also mean that the interval would
+ depend on AWH input. Also, for dihedral angles you would always want the reference interval to be -180, +180,
+ independent of AWH parameters.
+ */
+ if (!intervalIsInPeriodicInterval(origin, end, period))
+ {
+ gmx_fatal(FARGS, "When using AWH with periodic pull coordinate geometries awh%d-dim%d-start (%.8g) and "
+ "awh%d-dim%d-end (%.8g) should cover at most one period (%.8g) and take values in between "
+ "minus half a period and plus half a period, i.e. in the interval [%.8g, %.8g].",
+ k + 1, d + 1, origin, k + 1, d + 1, end,
+ period, -0.5*period, 0.5*period);
+
+ }
+
+ /* Warn if the pull initial coordinate value is not in the grid */
+ if (!valueIsInInterval(origin, end, period, coordValueInit))
+ {
+ char warningmsg[STRLEN];
+ sprintf(warningmsg, "The initial coordinate value (%.8g) for pull coordinate index %d falls outside "
+ "of the sampling nterval awh%d-dim%d-start (%.8g) to awh%d-dim%d-end (%.8g). "
+ "This can lead to large initial forces pulling the coordinate towards the sampling interval.",
+ coordValueInit, coordIndex + 1,
+ k + 1, d + 1, origin, k + 1, d + 1, end);
+ warning(wi, warningmsg);
+ }
+ }
+ }
+}
+
+void setStateDependentAwhParams(AwhParams *awhParams,
+ const pull_params_t *pull_params, pull_t *pull_work,
+ const matrix box, int ePBC,
+ const t_grpopts *inputrecGroupOptions, warninp_t wi)
+{
+ /* The temperature is not really state depenendent but is not known
+ * when read_awhParams is called (in get ir).
+ * It is known first after do_index has been called in grompp.cpp.
+ */
+ if (inputrecGroupOptions->ref_t == NULL ||
+ inputrecGroupOptions->ref_t[0] <= 0)
+ {
+ gmx_fatal(FARGS, "AWH biasing is only supported for temperatures > 0");
+ }
+ for (int i = 1; i < inputrecGroupOptions->ngtc; i++)
+ {
+ if (inputrecGroupOptions->ref_t[i] != inputrecGroupOptions->ref_t[0])
+ {
+ gmx_fatal(FARGS, "AWH biasing is currently only supported for identical temperatures for all temperature coupling groups");
+ }
+ }
+
+ t_pbc pbc;
+ set_pbc(&pbc, ePBC, box);
+
+ for (int k = 0; k < awhParams->numBias; k++)
+ {
+ AwhBiasParams *awhBiasParams = &awhParams->awhBiasParams[k];
+ for (int d = 0; d < awhBiasParams->ndim; d++)
+ {
+ AwhDimParams *dimParams = &awhBiasParams->dimParams[d];
+
+ /* The periodiciy of the AWH grid in certain cases depends on the simulation box */
+ dimParams->period = get_pull_coord_period(pull_params, dimParams->coordIndex, box);
+
+ /* The initial coordinate value, converted to external user units. */
+ dimParams->coordValueInit =
+ get_pull_coord_value(pull_work, dimParams->coordIndex, &pbc);
+
+ t_pull_coord *pullCoord = &pull_params->coord[dimParams->coordIndex];
+ dimParams->coordValueInit *= pull_conversion_factor_internal2userinput(pullCoord);
+ }
+ }
+ checkInputConsistencyInterval(awhParams, wi);
+
+ /* Register AWH as external potential with pull to check consistency. */
+ Awh::registerAwhWithPull(*awhParams, pull_work);
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2016,2017, 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.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * Declares functions needed for reading, initializing and setting the AWH parameter data types.
+ *
+ * \author Viveca Lindahl
+ * \inlibraryapi
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_READPARAMS_H
+#define GMX_AWH_READPARAMS_H
+
+#include "gromacs/fileio/readinp.h"
+#include "gromacs/math/vectypes.h"
+
+struct t_grpopts;
+struct t_inputrec;
+struct pull_params_t;
+struct pull_t;
+
+namespace gmx
+{
+struct AwhParams;
+
+/*! \brief Allocate, initialize and check the AWH parameters with values from the input file.
+ *
+ * \param[in,out] ninp_p Number of read input file entries.
+ * \param[in,out] inp_p Input file entries.
+ * \param[in] inputrec Input parameter struct.
+ * \param[in,out] wi Struct for bookeeping warnings.
+ * \returns AWH parameters.
+ */
+AwhParams *readAndCheckAwhParams(int *ninp_p,
+ t_inpfile **inp_p,
+ const t_inputrec *inputrec,
+ warninp_t wi);
+
+
+/*! \brief
+ * Sets AWH parameters that need state parameters such as the box vectors.
+ *
+ * \param[in,out] awhParams AWH parameters.
+ * \param[in] pull_params Pull parameters.
+ * \param[in,out] pull_work Pull working struct to register AWH bias in.
+ * \param[in] box Box vectors.
+ * \param[in] ePBC Periodic boundary conditions enum.
+ * \param[in] inputrecGroupOptions Parameters for atom groups.
+ * \param[in,out] wi Struct for bookeeping warnings.
+ *
+ * \note This function currently relies on the function set_pull_init to have been called.
+ */
+void setStateDependentAwhParams(AwhParams *awhParams,
+ const pull_params_t *pull_params,
+ pull_t *pull_work,
+ const matrix box,
+ int ePBC,
+ const t_grpopts *inputrecGroupOptions,
+ warninp_t wi);
+
+} // namespace gmx
+
+#endif /* GMX_AWH_READPARAMS_H */
--- /dev/null
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2017, 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.
+
+gmx_add_unit_test(AwhTest awh-test
+ bias.cpp biasstate.cpp grid.cpp)
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017, 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.
+ */
+#include "gmxpre.h"
+
+#include "gromacs/awh/bias.h"
+
+#include <cmath>
+
+#include <memory>
+#include <tuple>
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "gromacs/awh/pointstate.h"
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "testutils/refdata.h"
+#include "testutils/testasserts.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+/*! \internal \brief
+ * Struct that gathers all input for setting up and using a Bias
+ */
+struct AwhTestParameters
+{
+ double beta; //!< 1/(kB*T)
+
+ AwhDimParams awhDimParams; //!< Dimension parameters pointed to by \p awhBiasParams
+ AwhBiasParams awhBiasParams; //!< Bias parameters pointed to by \[ awhParams
+ AwhParams awhParams; //!< AWH parameters, this is the struct to actually use
+
+ std::vector<DimParams> dimParams; //!< Dimension parameters for setting up Bias
+};
+
+//! Helper function to set up the C-style AWH parameters for the test
+static AwhTestParameters getAwhTestParameters(int eawhgrowth,
+ int eawhpotential)
+{
+ AwhTestParameters params;
+
+ params.beta = 0.4;
+
+ AwhDimParams &awhDimParams = params.awhDimParams;
+
+ awhDimParams.period = 0;
+ awhDimParams.diffusion = 0.1;
+ awhDimParams.origin = 0.5;
+ awhDimParams.end = 1.5;
+ awhDimParams.coordValueInit = awhDimParams.origin;
+ awhDimParams.coverDiameter = 0;
+
+ AwhBiasParams &awhBiasParams = params.awhBiasParams;
+
+ awhBiasParams.ndim = 1;
+ awhBiasParams.dimParams = &awhDimParams;
+ awhBiasParams.eTarget = eawhtargetCONSTANT;
+ awhBiasParams.targetBetaScaling = 0;
+ awhBiasParams.targetCutoff = 0;
+ awhBiasParams.eGrowth = eawhgrowth;
+ awhBiasParams.bUserData = FALSE;
+ awhBiasParams.errorInitial = 0.5/params.beta;
+ awhBiasParams.shareGroup = 0;
+ awhBiasParams.equilibrateHistogram = FALSE;
+
+ double convFactor = 1;
+ double k = 1000;
+ gmx_int64_t seed = 93471803;
+
+ params.dimParams.push_back(DimParams(convFactor, k, params.beta));
+
+ AwhParams &awhParams = params.awhParams;
+
+ awhParams.numBias = 1;
+ awhParams.awhBiasParams = &awhBiasParams;
+ awhParams.seed = seed;
+ awhParams.nstOut = 0;
+ awhParams.nstSampleCoord = 1;
+ awhParams.numSamplesUpdateFreeEnergy = 10;
+ awhParams.ePotential = eawhpotential;
+ awhParams.shareBiasMultisim = FALSE;
+
+ return params;
+}
+
+//! Database of 21 test coordinates that represent a trajectory */
+const double g_coords[] = {
+ 0.62,
+ 0.70,
+ 0.68,
+ 0.80,
+ 0.93,
+ 0.87,
+ 1.16,
+ 1.14,
+ 0.95,
+ 0.89,
+ 0.91,
+ 0.86,
+ 0.88,
+ 0.79,
+ 0.75,
+ 0.82,
+ 0.74,
+ 0.70,
+ 0.68,
+ 0.71,
+ 0.73
+};
+
+//! Convenience typedef: growth type enum, potential type enum, disable update skips
+typedef std::tuple<int, int, BiasParams::DisableUpdateSkips> BiasTestParameters;
+
+/*! \brief Test fixture for testing Bias updates
+ */
+class BiasTest : public ::testing::TestWithParam<BiasTestParameters>
+{
+ public:
+ //! Random seed for AWH MC sampling
+ gmx_int64_t seed_;
+
+ //! Coordinates representing a trajectory in time
+ std::vector<double> coordinates_;
+ //! The awh Bias
+ std::unique_ptr<Bias> bias_;
+
+ //! Constructor
+ BiasTest() :
+ coordinates_(std::begin(g_coords), std::end(g_coords))
+ {
+ /* We test all combinations of:
+ * eawhgrowth:
+ * eawhgrowthLINEAR: final, normal update phase
+ * ewahgrowthEXP_LINEAR: intial phase, updated size is constant
+ * eawhpotential (should only affect the force output):
+ * eawhpotentialUMBRELLA: MC on lambda (umbrella potential location)
+ * eawhpotentialCONVOLVED: MD on a convolved potential landscape
+ * disableUpdateSkips (should not affect the results):
+ * BiasParams::DisableUpdateSkips::yes: update the point state for every sample
+ * BiasParams::DisableUpdateSkips::no: update the point state at an interval > 1 sample
+ *
+ * Note: It would be nice to explicitly check that eawhpotential
+ * and disableUpdateSkips do not affect the point state.
+ * But the reference data will also ensure this.
+ */
+ int eawhgrowth;
+ int eawhpotential;
+ BiasParams::DisableUpdateSkips disableUpdateSkips;
+ std::tie(eawhgrowth, eawhpotential, disableUpdateSkips) = GetParam();
+
+ /* Set up a basic AWH setup with a single, 1D bias with parameters
+ * such that we can measure the effects of different parameters.
+ * The idea is to, among other things, have part of the interval
+ * not covered by samples.
+ */
+ const AwhTestParameters params = getAwhTestParameters(eawhgrowth, eawhpotential);
+
+ seed_ = params.awhParams.seed;
+
+ double mdTimeStep = 0.1;
+
+ int numSamples = coordinates_.size() - 1; // No sample taken at step 0
+ GMX_RELEASE_ASSERT(numSamples % params.awhParams.numSamplesUpdateFreeEnergy == 0, "This test is intended to reproduce the situation when the might need to write output during a normal AWH run, therefore the number of samples should be a multiple of the free-energy update interval (but the test should also runs fine without this condition).");
+
+ bias_ = std::unique_ptr<Bias>(new Bias(-1, params.awhParams, params.awhBiasParams, params.dimParams, params.beta, mdTimeStep, 1, "", Bias::ThisRankWillDoIO::No, disableUpdateSkips));
+ }
+};
+
+TEST_P(BiasTest, ForcesBiasPmf)
+{
+ gmx::test::TestReferenceData data;
+ gmx::test::TestReferenceChecker checker(data.rootChecker());
+
+ Bias &bias = *bias_.get();
+
+ /* Make strings with the properties we expect to be different in the tests.
+ * These also helps to interpret the reference data.
+ */
+ std::vector<std::string> props;
+ props.push_back(formatString("stage: %s", bias.state().inInitialStage() ? "initial" : "final"));
+ props.push_back(formatString("convolve forces: %s", bias.params().convolveForce ? "yes" : "no"));
+ props.push_back(formatString("skip updates: %s", bias.params().skipUpdates() ? "yes" : "no"));
+
+ SCOPED_TRACE(gmx::formatString("%s, %s, %s", props[0].c_str(), props[1].c_str(), props[2].c_str()));
+
+ std::vector<double> force, pot, potJump;
+
+ double coordMaxValue = 0;
+ double potentialJump = 0;
+ gmx_int64_t step = 0;
+ for (auto &coord : coordinates_)
+ {
+ coordMaxValue = std::max(coordMaxValue, std::abs(coord));
+
+ awh_dvec coordValue = { coord, 0, 0, 0 };
+ awh_dvec biasForce;
+ double potential = 0;
+ bias.calcForceAndUpdateBias(coordValue,
+ biasForce, &potential, &potentialJump,
+ nullptr, step, step, seed_, nullptr);
+
+ force.push_back(biasForce[0]);
+ pot.push_back(potential);
+ potJump.push_back(potentialJump);
+
+ step++;
+ }
+
+ /* When skipping updates, ensure all skipped updates are performed here.
+ * This should result in the same bias state as at output in a normal run.
+ */
+ if (bias.params().skipUpdates())
+ {
+ bias.doSkippedUpdatesForAllPoints();
+ }
+
+ std::vector<double> pointBias, logPmfsum;
+ for (auto &point : bias.state().points())
+ {
+ pointBias.push_back(point.bias());
+ logPmfsum.push_back(point.logPmfSum());
+ }
+
+ /* The umbrella force is computed from the coordinate deviation.
+ * In taking this deviation we lose a lot of precision, so we should
+ * compare against k*max(coord) instead of the instantaneous force.
+ */
+ const double kCoordMax = bias.dimParams()[0].k*coordMaxValue;
+
+ const double ulpTol = 10;
+
+ checker.checkSequence(props.begin(), props.end(), "Properties");
+ checker.setDefaultTolerance(absoluteTolerance(kCoordMax*GMX_DOUBLE_EPS*ulpTol));
+ checker.checkSequence(force.begin(), force.end(), "Force");
+ checker.checkSequence(pot.begin(), pot.end(), "Potential");
+ checker.checkSequence(potJump.begin(), potJump.end(), "PotentialJump");
+ checker.setDefaultTolerance(relativeToleranceAsUlp(1.0, ulpTol));
+ checker.checkSequence(pointBias.begin(), pointBias.end(), "PointBias");
+ checker.checkSequence(logPmfsum.begin(), logPmfsum.end(), "PointLogPmfsum");
+}
+
+/* Scan initial/final phase, MC/convolved force and update skip (not) allowed
+ * Both the convolving and skipping should not affect the bias and PMF.
+ * It would be nice if the test would explicitly check for this.
+ * Currently this is tested through identical reference data.
+ */
+INSTANTIATE_TEST_CASE_P(WithParameters, BiasTest,
+ ::testing::Combine(
+ ::testing::Values(eawhgrowthLINEAR, eawhgrowthEXP_LINEAR),
+ ::testing::Values(eawhpotentialUMBRELLA, eawhpotentialCONVOLVED),
+ ::testing::Values(BiasParams::DisableUpdateSkips::yes, BiasParams::DisableUpdateSkips::no)));
+
+// Test that we detect coverings and exit the initial stage at the correct step
+TEST(BiasTest, DetectsCovering)
+{
+ const AwhTestParameters params = getAwhTestParameters(eawhgrowthEXP_LINEAR, eawhpotentialCONVOLVED);
+ const AwhDimParams &awhDimParams = params.awhParams.awhBiasParams[0].dimParams[0];
+
+ const double mdTimeStep = 0.1;
+
+ Bias bias(-1, params.awhParams, params.awhBiasParams, params.dimParams, params.beta, mdTimeStep, 1, "", Bias::ThisRankWillDoIO::No);
+
+ /* We use a trajectory of the sum of two sines to cover the reaction
+ * coordinate range in a semi-realistic way. The period is 4*pi=12.57.
+ * We get out of the initial stage after 4 coverings at step 300.
+ */
+ const gmx_int64_t exitStepRef = 300;
+ const double midPoint = 0.5*(awhDimParams.end + awhDimParams.origin);
+ const double halfWidth = 0.5*(awhDimParams.end - awhDimParams.origin);
+
+ bool inInitialStage = bias.state().inInitialStage();
+ /* Normally this loop exits at exitStepRef, but we extend with failure */
+ gmx_int64_t step;
+ for (step = 0; step <= 2*exitStepRef; step++)
+ {
+ double t = step*mdTimeStep;
+ double coord = midPoint + halfWidth*(0.5*std::sin(t) + 0.55*std::sin(1.5*t));
+
+ awh_dvec coordValue = { coord, 0, 0, 0 };
+ awh_dvec biasForce;
+ double potential = 0;
+ double potentialJump = 0;
+ bias.calcForceAndUpdateBias(coordValue,
+ biasForce, &potential, &potentialJump,
+ nullptr, step, step, params.awhParams.seed, nullptr);
+
+ inInitialStage = bias.state().inInitialStage();
+ if (!inInitialStage)
+ {
+ break;
+ }
+ }
+
+ EXPECT_EQ(false, inInitialStage);
+ if (!inInitialStage)
+ {
+ EXPECT_EQ(exitStepRef, step);
+ }
+}
+
+} // namespace
+} // namespace
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017, 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.
+ */
+#include "gmxpre.h"
+
+#include "gromacs/awh/biasstate.h"
+
+#include <cmath>
+
+#include <memory>
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "gromacs/awh/grid.h"
+#include "gromacs/awh/pointstate.h"
+#include "gromacs/math/functions.h"
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/utility/smalloc.h"
+
+#include "testutils/testasserts.h"
+#include "testutils/testfilemanager.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+/*! \internal \brief
+ * Struct that gathers all input for setting up and using a Bias
+ */
+struct AwhTestParameters
+{
+ double beta; //!< 1/(kB*T)
+
+ AwhDimParams awhDimParams[2]; //!< Dimension parameters pointed to by \p awhBiasParams
+ AwhBiasParams awhBiasParams; //!< Bias parameters pointed to by \[ awhParams
+ AwhParams awhParams; //!< AWH parameters, this is the struct to actually use
+};
+
+//! Helper function to set up the C-style AWH parameters for the test
+static AwhTestParameters getAwhTestParameters()
+{
+ AwhTestParameters params;
+
+ params.beta = 1.0;
+
+ AwhParams &awhParams = params.awhParams;
+ snew(params.awhParams.awhBiasParams, 1);
+ AwhBiasParams &awhBiasParams = params.awhParams.awhBiasParams[0];
+ snew(awhBiasParams.dimParams, 2);
+
+ AwhDimParams &awhDimParams0 = awhBiasParams.dimParams[0];
+
+ awhDimParams0.period = 0;
+ awhDimParams0.diffusion = 0.1;
+ awhDimParams0.origin = 0.5;
+ awhDimParams0.end = 1.5;
+ awhDimParams0.coordValueInit = awhDimParams0.origin;
+ awhDimParams0.coverDiameter = 0;
+
+ AwhDimParams &awhDimParams1 = awhBiasParams.dimParams[1];
+
+ awhDimParams1.period = 0;
+ awhDimParams1.diffusion = 0.1;
+ awhDimParams1.origin = 0.8;
+ awhDimParams1.end = 1.3;
+ awhDimParams1.coordValueInit = awhDimParams1.origin;
+ awhDimParams1.coverDiameter = 0;
+
+ awhBiasParams.ndim = 2;
+ awhBiasParams.eTarget = eawhtargetCONSTANT;
+ awhBiasParams.targetBetaScaling = 0;
+ awhBiasParams.targetCutoff = 0;
+ awhBiasParams.eGrowth = eawhgrowthLINEAR;
+ awhBiasParams.bUserData = TRUE;
+ awhBiasParams.errorInitial = 0.5;
+ awhBiasParams.shareGroup = 0;
+ awhBiasParams.equilibrateHistogram = FALSE;
+
+ awhParams.numBias = 1;
+ awhParams.seed = 93471803;
+ awhParams.nstOut = 0;
+ awhParams.nstSampleCoord = 1;
+ awhParams.numSamplesUpdateFreeEnergy = 10;
+ awhParams.ePotential = eawhpotentialCONVOLVED;
+ awhParams.shareBiasMultisim = FALSE;
+
+ return params;
+}
+
+/*! \brief Test fixture for testing Bias updates
+ */
+class BiasStateTest : public ::testing::TestWithParam<const char *>
+{
+ public:
+ std::unique_ptr<BiasState> biasState_; //!< The bias state
+
+ //! Constructor
+ BiasStateTest()
+ {
+ AwhTestParameters params = getAwhTestParameters();
+ const AwhParams &awhParams = params.awhParams;
+ const AwhBiasParams &awhBiasParams = awhParams.awhBiasParams[0];
+ std::vector<DimParams> dimParams;
+ dimParams.push_back(DimParams(1.0, 15.0, params.beta));
+ dimParams.push_back(DimParams(1.0, 15.0, params.beta));
+ Grid grid(dimParams, awhBiasParams.dimParams);
+ BiasParams biasParams(awhParams, awhBiasParams, dimParams, 1.0, 1.0, BiasParams::DisableUpdateSkips::no, 1, grid.axis(), 0);
+ biasState_ = std::unique_ptr<BiasState>(new BiasState(awhBiasParams, 1.0, dimParams, grid));
+
+ // Here we initialize the grid point state using the input file
+ std::string filename = gmx::test::TestFileManager::getInputFilePath(GetParam());
+ biasState_->initGridPointState(awhBiasParams, dimParams, grid, biasParams, filename, params.awhParams.numBias);
+
+ sfree(params.awhParams.awhBiasParams[0].dimParams);
+ sfree(params.awhParams.awhBiasParams);
+ }
+};
+
+TEST_P(BiasStateTest, InitializesFromFile)
+{
+ gmx::ArrayRef<const PointState> points = biasState_->points();
+
+ /* Compute the mean square deviation from the expected values in the file.
+ * The PMF values are spaced by 0.5 per points and logPmfsum has opposite sign.
+ * The target is (index + 1)/120.
+ */
+ double msdPmf = 0;
+ double msdTarget = 0;
+ for (size_t i = 0; i < points.size(); i++)
+ {
+ msdPmf += gmx::square(points[i].logPmfSum() - points[0].logPmfSum() + 0.5*i);
+ msdTarget += gmx::square(points[i].target() - (i + 1)/120.0);
+ }
+
+ msdPmf /= points.size();
+ msdTarget /= points.size();
+
+ EXPECT_EQ(0.0, msdPmf);
+ EXPECT_EQ(0.0, msdTarget);
+}
+
+// Test that Bias initialization open and reads the correct initialization
+// files and the the correct PMF and target distribution is set.
+INSTANTIATE_TEST_CASE_P(WithParameters, BiasStateTest,
+ ::testing::Values("pmf_target_format0.xvg",
+ "pmf_target_format1.xvg"));
+
+} // namespace
+} // namespace
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017, 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.
+ */
+#include "gmxpre.h"
+
+#include "gromacs/awh/grid.h"
+
+#include <cmath>
+
+#include <memory>
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "gromacs/mdtypes/awh-params.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "testutils/testasserts.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+TEST(gridTest, neighborhood)
+{
+ constexpr double pointsPerScope = Grid::c_scopeCutoff*Grid::c_numPointsPerSigma;
+ GMX_RELEASE_ASSERT(std::abs(pointsPerScope - std::round(pointsPerScope)) > 1e-4, "If the scope is close to an integer number of points, this test can be unstable due to rounding issues");
+
+ const int scopeInPoints = static_cast<int>(pointsPerScope);
+
+ /* We test a 2-dimensional grid with dim0 periodic, dim1 not periodic.
+ * This test should cover most relevant cases: multi-dimension grids
+ * and non-periodic and periodic dimensions.
+ * The only thing not tested here is a periodic dimension with a gap.
+ */
+
+ const int numDim = 2;
+ std::vector<AwhDimParams> awhDimParams(numDim);
+
+ awhDimParams[0].origin = -5;
+ awhDimParams[0].end = 5;
+ awhDimParams[0].period = 10;
+
+ awhDimParams[1].origin = 0.5;
+ awhDimParams[1].end = 2.0;
+ awhDimParams[1].period = 0;
+
+ const real conversionFactor = 1;
+ const real beta = 3.0;
+
+ /* Set up dimParams to get about 15 points along each dimension */
+ std::vector<DimParams> dimParams;
+ dimParams.push_back(DimParams(conversionFactor, 1/(beta*0.7*0.7), beta));
+ dimParams.push_back(DimParams(conversionFactor, 1/(beta*0.1*0.1), beta));
+
+ Grid grid(dimParams, awhDimParams.data());
+
+ const int numPoints = grid.numPoints();
+
+ int numPointsDim[numDim];
+ for (int d = 0; d < numDim; d++)
+ {
+ numPointsDim[d] = grid.axis(d).numPoints();
+
+ GMX_RELEASE_ASSERT(numPointsDim[d] >= 2*scopeInPoints + 1, "The test code currently assume that the grid is larger of equal to the scope in each dimension");
+ }
+ int halfNumPoints0 = numPointsDim[0]/2;
+
+ bool haveOutOfGridNeighbors = false;
+ bool haveDuplicateNeighbors = false;
+ bool haveIncorrectNeighbors = false;
+ bool haveCorrectNumNeighbors = true;
+
+ /* Set up a grid for checking for duplicate neighbors */
+ std::vector<bool> isInNeighborhood(grid.numPoints(), false);
+
+ /* Checking for all points is overkill, we check every 7th */
+ for (size_t i = 0; i < grid.numPoints(); i += 7)
+ {
+ const GridPoint &point = grid.point(i);
+
+ /* NOTE: This code relies on major-minor index ordering in Grid */
+ int pointIndex0 = i/numPointsDim[1];
+ int pointIndex1 = i - pointIndex0*numPointsDim[1];
+
+ /* To check if we have the correct neighbors, we check the expected
+ * number of neighbors, if all neighbors are within the grid bounds
+ * and if they are within scope.
+ */
+ int distanceFromEdge1 = std::min(pointIndex1, numPointsDim[1] - 1 - pointIndex1);
+ size_t numNeighbors = (2*scopeInPoints + 1)*(scopeInPoints + std::min(scopeInPoints, distanceFromEdge1) + 1);
+ if (point.neighbor.size() != numNeighbors)
+ {
+ haveCorrectNumNeighbors = false;
+ }
+
+ for (auto &j : point.neighbor)
+ {
+ if (j >= 0 && j < numPoints)
+ {
+ if (isInNeighborhood[j])
+ {
+ haveDuplicateNeighbors = true;
+ }
+ isInNeighborhood[j] = true;
+
+ int neighborIndex0 = j/numPointsDim[1];
+ int neighborIndex1 = j - neighborIndex0*numPointsDim[1];
+
+ int distance0 = neighborIndex0 - pointIndex0;
+ int distance1 = neighborIndex1 - pointIndex1;
+ /* Adjust distance for periodicity of dimension 0 */
+ if (distance0 < -halfNumPoints0)
+ {
+ distance0 += numPointsDim[0];
+ }
+ else if (distance0 > halfNumPoints0)
+ {
+ distance0 -= numPointsDim[0];
+ }
+ /* Check if the distance is within scope */
+ if (distance0 < -scopeInPoints || distance0 > scopeInPoints ||
+ distance1 < -scopeInPoints || distance1 > scopeInPoints)
+ {
+ haveIncorrectNeighbors = true;
+ }
+ }
+ else
+ {
+ haveOutOfGridNeighbors = true;
+ }
+ }
+
+ /* Clear the marked points in the checking grid */
+ for (auto &neighbor : point.neighbor)
+ {
+ if (neighbor >= 0 && neighbor < numPoints)
+ {
+ isInNeighborhood[neighbor] = false;
+ }
+ }
+ }
+
+ EXPECT_FALSE(haveOutOfGridNeighbors);
+ EXPECT_FALSE(haveDuplicateNeighbors);
+ EXPECT_FALSE(haveIncorrectNeighbors);
+ EXPECT_TRUE(haveCorrectNumNeighbors);
+}
+
+} // namespace
+} // namespace
--- /dev/null
+0.5 0.8 0.0 1
+0.5 1.05 0.5 2
+0.5 1.3 1.0 3
+0.75 0.8 1.5 4
+0.75 1.05 2.0 5
+0.75 1.3 2.5 6
+1.0 0.8 3.0 7
+1.0 1.05 3.5 8
+1.0 1.3 4.0 9
+1.25 0.8 4.5 10
+1.25 1.05 5.0 11
+1.25 1.3 5.5 12
+1.5 0.8 6.0 13
+1.5 1.05 6.5 14
+1.5 1.3 7.0 15
--- /dev/null
+0.5 0.8 0.0 0 0 0 1
+0.5 1.05 0.5 0 0 0 2
+0.5 1.3 1.0 0 0 0 3
+0.75 0.8 1.5 0 0 0 4
+0.75 1.05 2.0 0 0 0 5
+0.75 1.3 2.5 0 0 0 6
+1.0 0.8 3.0 0 0 0 7
+1.0 1.05 3.5 0 0 0 8
+1.0 1.3 4.0 0 0 0 9
+1.25 0.8 4.5 0 0 0 10
+1.25 1.05 5.0 0 0 0 11
+1.25 1.3 5.5 0 0 0 12
+1.5 0.8 6.0 0 0 0 13
+1.5 1.05 6.5 0 0 0 14
+1.5 1.3 7.0 0 0 0 15
--- /dev/null
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+ <Sequence Name="Properties">
+ <Int Name="Length">3</Int>
+ <String>stage: final</String>
+ <String>convolve forces: no</String>
+ <String>skip updates: no</String>
+ </Sequence>
+ <Sequence Name="Force">
+ <Int Name="Length">21</Int>
+ <Real>-70.000000000000014</Real>
+ <Real>-49.999999999999986</Real>
+ <Real>-5.0000000000000604</Real>
+ <Real>-49.999999999999993</Real>
+ <Real>-5</Real>
+ <Real>80.000000000000014</Real>
+ <Real>-184.99999999999989</Real>
+ <Real>-64.999999999999829</Real>
+ <Real>75.000000000000071</Real>
+ <Real>10.000000000000011</Real>
+ <Real>-59.999999999999943</Real>
+ <Real>-9.999999999999897</Real>
+ <Real>-4.9999999999999485</Real>
+ <Real>60</Real>
+ <Real>0</Real>
+ <Real>-44.999999999999929</Real>
+ <Real>60.000000000000057</Real>
+ <Real>5.3290705182007514e-14</Real>
+ <Real>-30.000000000000028</Real>
+ <Real>-59.999999999999943</Real>
+ <Real>-29.999999999999972</Real>
+ </Sequence>
+ <Sequence Name="Potential">
+ <Int Name="Length">21</Int>
+ <Real>7.1999999999999993</Real>
+ <Real>4.9999999999999973</Real>
+ <Real>0.19999999999999815</Real>
+ <Real>11.250000000000004</Real>
+ <Real>3.1999999999999966</Real>
+ <Real>8.4500000000000011</Real>
+ <Real>33.799999999999969</Real>
+ <Real>4.0499999999999874</Real>
+ <Real>11.250000000000021</Real>
+ <Real>1.7999999999999965</Real>
+ <Real>1.7999999999999965</Real>
+ <Real>0.049999999999998976</Real>
+ <Real>0.44999999999999746</Real>
+ <Real>6.049999999999998</Real>
+ <Real>1.2500000000000022</Real>
+ <Real>7.1999999999999993</Real>
+ <Real>6.0500000000000105</Real>
+ <Real>1.2500000000000022</Real>
+ <Real>0.45000000000000084</Real>
+ <Real>1.7999999999999965</Real>
+ <Real>3.1999999999999966</Real>
+ </Sequence>
+ <Sequence Name="PotentialJump">
+ <Int Name="Length">21</Int>
+ <Real>-6.9999999999999991</Real>
+ <Real>-4.9999999999999973</Real>
+ <Real>0.25000000000000266</Real>
+ <Real>-10.000000000000002</Real>
+ <Real>-0.75</Real>
+ <Real>-8</Real>
+ <Real>-27.749999999999982</Real>
+ <Real>-3.2499999999999947</Real>
+ <Real>-11.250000000000021</Real>
+ <Real>-0.99999999999999956</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>-0.24999999999999711</Real>
+ <Real>-5.9999999999999982</Real>
+ <Real>0</Real>
+ <Real>-6.7499999999999956</Real>
+ <Real>-6.0000000000000107</Real>
+ <Real>-5.5511151231257827e-15</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>-2.9999999999999964</Real>
+ </Sequence>
+ <Sequence Name="PointBias">
+ <Int Name="Length">21</Int>
+ <Real>-2.7472944451046044</Real>
+ <Real>-2.761382468216012</Real>
+ <Real>-2.8841487700294044</Real>
+ <Real>-3.2209538871703414</Real>
+ <Real>-3.5026939887681463</Real>
+ <Real>-3.5195714843782056</Real>
+ <Real>-3.4502423109495881</Real>
+ <Real>-3.469814270443778</Real>
+ <Real>-3.4405375988254123</Real>
+ <Real>-3.2226649751609364</Real>
+ <Real>-2.9351106219650442</Real>
+ <Real>-2.8182713948302314</Real>
+ <Real>-2.9109204815675662</Real>
+ <Real>-2.9974201933988711</Real>
+ <Real>-2.909045620263937</Real>
+ <Real>-2.7875440262964757</Real>
+ <Real>-2.7504540277720304</Real>
+ <Real>-2.7468371894369819</Real>
+ <Real>-2.7467110222070819</Real>
+ <Real>-2.7467093840049168</Real>
+ <Real>-2.7467093840049168</Real>
+ </Sequence>
+ <Sequence Name="PointLogPmfsum">
+ <Int Name="Length">21</Int>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.6151742683824608</Real>
+ <Real>4.4405502576634461</Real>
+ <Real>4.4441023756206013</Real>
+ <Real>4.3362531480424424</Real>
+ <Real>4.4431072875080853</Real>
+ <Real>4.3095557059880116</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.3095557021915543</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ </Sequence>
+</ReferenceData>
--- /dev/null
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+ <Sequence Name="Properties">
+ <Int Name="Length">3</Int>
+ <String>stage: final</String>
+ <String>convolve forces: no</String>
+ <String>skip updates: yes</String>
+ </Sequence>
+ <Sequence Name="Force">
+ <Int Name="Length">21</Int>
+ <Real>-70.000000000000014</Real>
+ <Real>-49.999999999999986</Real>
+ <Real>-5.0000000000000604</Real>
+ <Real>-49.999999999999993</Real>
+ <Real>-5</Real>
+ <Real>80.000000000000014</Real>
+ <Real>-184.99999999999989</Real>
+ <Real>-64.999999999999829</Real>
+ <Real>75.000000000000071</Real>
+ <Real>10.000000000000011</Real>
+ <Real>-59.999999999999943</Real>
+ <Real>-9.999999999999897</Real>
+ <Real>-4.9999999999999485</Real>
+ <Real>60</Real>
+ <Real>0</Real>
+ <Real>-44.999999999999929</Real>
+ <Real>60.000000000000057</Real>
+ <Real>5.3290705182007514e-14</Real>
+ <Real>-30.000000000000028</Real>
+ <Real>-59.999999999999943</Real>
+ <Real>-29.999999999999972</Real>
+ </Sequence>
+ <Sequence Name="Potential">
+ <Int Name="Length">21</Int>
+ <Real>7.1999999999999993</Real>
+ <Real>4.9999999999999973</Real>
+ <Real>0.19999999999999815</Real>
+ <Real>11.250000000000004</Real>
+ <Real>3.1999999999999966</Real>
+ <Real>8.4500000000000011</Real>
+ <Real>33.799999999999969</Real>
+ <Real>4.0499999999999874</Real>
+ <Real>11.250000000000021</Real>
+ <Real>1.7999999999999965</Real>
+ <Real>1.7999999999999965</Real>
+ <Real>0.049999999999998976</Real>
+ <Real>0.44999999999999746</Real>
+ <Real>6.049999999999998</Real>
+ <Real>1.2500000000000022</Real>
+ <Real>7.1999999999999993</Real>
+ <Real>6.0500000000000105</Real>
+ <Real>1.2500000000000022</Real>
+ <Real>0.45000000000000084</Real>
+ <Real>1.7999999999999965</Real>
+ <Real>3.1999999999999966</Real>
+ </Sequence>
+ <Sequence Name="PotentialJump">
+ <Int Name="Length">21</Int>
+ <Real>-6.9999999999999991</Real>
+ <Real>-4.9999999999999973</Real>
+ <Real>0.25000000000000266</Real>
+ <Real>-10.000000000000002</Real>
+ <Real>-0.75</Real>
+ <Real>-8</Real>
+ <Real>-27.749999999999982</Real>
+ <Real>-3.2499999999999947</Real>
+ <Real>-11.250000000000021</Real>
+ <Real>-0.99999999999999956</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>-0.24999999999999711</Real>
+ <Real>-5.9999999999999982</Real>
+ <Real>0</Real>
+ <Real>-6.7499999999999956</Real>
+ <Real>-6.0000000000000107</Real>
+ <Real>-5.5511151231257827e-15</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>-2.9999999999999964</Real>
+ </Sequence>
+ <Sequence Name="PointBias">
+ <Int Name="Length">21</Int>
+ <Real>-2.7472944451046044</Real>
+ <Real>-2.761382468216012</Real>
+ <Real>-2.8841487700294044</Real>
+ <Real>-3.2209538871703414</Real>
+ <Real>-3.5026939887681463</Real>
+ <Real>-3.5195714843782056</Real>
+ <Real>-3.4502423109495881</Real>
+ <Real>-3.469814270443778</Real>
+ <Real>-3.4405375988254123</Real>
+ <Real>-3.2226649751609364</Real>
+ <Real>-2.9351106219650442</Real>
+ <Real>-2.8182713948302314</Real>
+ <Real>-2.9109204815675662</Real>
+ <Real>-2.9974201933988711</Real>
+ <Real>-2.909045620263937</Real>
+ <Real>-2.7875440262964757</Real>
+ <Real>-2.7504540277720304</Real>
+ <Real>-2.7468371894369819</Real>
+ <Real>-2.7467110222070819</Real>
+ <Real>-2.7467093840049168</Real>
+ <Real>-2.7467093840049168</Real>
+ </Sequence>
+ <Sequence Name="PointLogPmfsum">
+ <Int Name="Length">21</Int>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.6151742683824608</Real>
+ <Real>4.4405502576634461</Real>
+ <Real>4.4441023756206013</Real>
+ <Real>4.3362531480424424</Real>
+ <Real>4.4431072875080853</Real>
+ <Real>4.3095557059880116</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.3095557021915543</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ </Sequence>
+</ReferenceData>
--- /dev/null
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+ <Sequence Name="Properties">
+ <Int Name="Length">3</Int>
+ <String>stage: final</String>
+ <String>convolve forces: yes</String>
+ <String>skip updates: no</String>
+ </Sequence>
+ <Sequence Name="Force">
+ <Int Name="Length">21</Int>
+ <Real>0.21526724902654029</Real>
+ <Real>0.00037168043378189554</Real>
+ <Real>0.0023506021278633634</Real>
+ <Real>1.8322908294787776e-14</Real>
+ <Real>1.8182159860257569e-05</Real>
+ <Real>-1.8182159861071263e-05</Real>
+ <Real>-6.7883759693111072e-06</Real>
+ <Real>6.7883758926473613e-06</Real>
+ <Real>-4.5547116425681544e-14</Real>
+ <Real>6.7883759521357456e-06</Real>
+ <Real>-6.7883759527878661e-06</Real>
+ <Real>-3.5538537690811558</Real>
+ <Real>-2.0147107048387825</Real>
+ <Real>-2.7052521678814725</Real>
+ <Real>-1.0406813794780787</Real>
+ <Real>-3.967066260592369</Real>
+ <Real>-0.87907598448922164</Real>
+ <Real>-1.6612940249811217</Real>
+ <Real>-2.6184399315060456</Real>
+ <Real>-1.2743003165852991</Real>
+ <Real>-0.86472575028406273</Real>
+ </Sequence>
+ <Sequence Name="Potential">
+ <Int Name="Length">21</Int>
+ <Real>5.3171052747656216</Real>
+ <Real>5.3139634951471235</Real>
+ <Real>5.3139852805648378</Real>
+ <Real>5.3139597783455503</Real>
+ <Real>5.313959928304433</Real>
+ <Real>5.313959928304433</Real>
+ <Real>5.3139598111771384</Real>
+ <Real>5.3139598111771384</Real>
+ <Real>5.3139597783455486</Real>
+ <Real>5.3139598111771358</Real>
+ <Real>5.3139598111771358</Real>
+ <Real>5.8523574407739236</Real>
+ <Real>5.909517708141518</Real>
+ <Real>5.5891055083437386</Real>
+ <Real>5.5179043538034485</Real>
+ <Real>5.6909400832286146</Real>
+ <Real>5.5084236091309347</Real>
+ <Real>5.4647638463620405</Real>
+ <Real>5.4221743541770948</Real>
+ <Real>5.4793615522962513</Real>
+ <Real>5.4998305387193387</Real>
+ </Sequence>
+ <Sequence Name="PotentialJump">
+ <Int Name="Length">21</Int>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0.60502446654887354</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0.83485389150670564</Real>
+ </Sequence>
+ <Sequence Name="PointBias">
+ <Int Name="Length">21</Int>
+ <Real>-2.7472944451046044</Real>
+ <Real>-2.761382468216012</Real>
+ <Real>-2.8841487700294044</Real>
+ <Real>-3.2209538871703414</Real>
+ <Real>-3.5026939887681463</Real>
+ <Real>-3.5195714843782056</Real>
+ <Real>-3.4502423109495881</Real>
+ <Real>-3.469814270443778</Real>
+ <Real>-3.4405375988254123</Real>
+ <Real>-3.2226649751609364</Real>
+ <Real>-2.9351106219650442</Real>
+ <Real>-2.8182713948302314</Real>
+ <Real>-2.9109204815675662</Real>
+ <Real>-2.9974201933988711</Real>
+ <Real>-2.909045620263937</Real>
+ <Real>-2.7875440262964757</Real>
+ <Real>-2.7504540277720304</Real>
+ <Real>-2.7468371894369819</Real>
+ <Real>-2.7467110222070819</Real>
+ <Real>-2.7467093840049168</Real>
+ <Real>-2.7467093840049168</Real>
+ </Sequence>
+ <Sequence Name="PointLogPmfsum">
+ <Int Name="Length">21</Int>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.6151742683824608</Real>
+ <Real>4.4405502576634461</Real>
+ <Real>4.4441023756206013</Real>
+ <Real>4.3362531480424424</Real>
+ <Real>4.4431072875080853</Real>
+ <Real>4.3095557059880116</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.3095557021915543</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ </Sequence>
+</ReferenceData>
--- /dev/null
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+ <Sequence Name="Properties">
+ <Int Name="Length">3</Int>
+ <String>stage: final</String>
+ <String>convolve forces: yes</String>
+ <String>skip updates: yes</String>
+ </Sequence>
+ <Sequence Name="Force">
+ <Int Name="Length">21</Int>
+ <Real>0.21526724902654029</Real>
+ <Real>0.00037168043378189554</Real>
+ <Real>0.0023506021278633634</Real>
+ <Real>1.8322908294787776e-14</Real>
+ <Real>1.8182159860257569e-05</Real>
+ <Real>-1.8182159861071263e-05</Real>
+ <Real>-6.7883759693111072e-06</Real>
+ <Real>6.7883758926473613e-06</Real>
+ <Real>-4.5547116425681544e-14</Real>
+ <Real>6.7883759521357456e-06</Real>
+ <Real>-6.7883759527878661e-06</Real>
+ <Real>-3.5538537690811558</Real>
+ <Real>-2.0147107048387825</Real>
+ <Real>-2.7052521678814725</Real>
+ <Real>-1.0406813794780787</Real>
+ <Real>-3.967066260592369</Real>
+ <Real>-0.87907598448922164</Real>
+ <Real>-1.6612940249811217</Real>
+ <Real>-2.6184399315060456</Real>
+ <Real>-1.2743003165852991</Real>
+ <Real>-0.86472575028406273</Real>
+ </Sequence>
+ <Sequence Name="Potential">
+ <Int Name="Length">21</Int>
+ <Real>5.3171052747656216</Real>
+ <Real>5.3139634951471235</Real>
+ <Real>5.3139852805648378</Real>
+ <Real>5.3139597783455503</Real>
+ <Real>5.313959928304433</Real>
+ <Real>5.313959928304433</Real>
+ <Real>5.3139598111771384</Real>
+ <Real>5.3139598111771384</Real>
+ <Real>5.3139597783455486</Real>
+ <Real>5.3139598111771358</Real>
+ <Real>5.3139598111771358</Real>
+ <Real>5.8523574407739236</Real>
+ <Real>5.909517708141518</Real>
+ <Real>5.5891055083437386</Real>
+ <Real>5.5179043538034485</Real>
+ <Real>5.6909400832286146</Real>
+ <Real>5.5084236091309347</Real>
+ <Real>5.4647638463620405</Real>
+ <Real>5.4221743541770948</Real>
+ <Real>5.4793615522962513</Real>
+ <Real>5.4998305387193387</Real>
+ </Sequence>
+ <Sequence Name="PotentialJump">
+ <Int Name="Length">21</Int>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0.60502446654887354</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0.83485389150670564</Real>
+ </Sequence>
+ <Sequence Name="PointBias">
+ <Int Name="Length">21</Int>
+ <Real>-2.7472944451046044</Real>
+ <Real>-2.761382468216012</Real>
+ <Real>-2.8841487700294044</Real>
+ <Real>-3.2209538871703414</Real>
+ <Real>-3.5026939887681463</Real>
+ <Real>-3.5195714843782056</Real>
+ <Real>-3.4502423109495881</Real>
+ <Real>-3.469814270443778</Real>
+ <Real>-3.4405375988254123</Real>
+ <Real>-3.2226649751609364</Real>
+ <Real>-2.9351106219650442</Real>
+ <Real>-2.8182713948302314</Real>
+ <Real>-2.9109204815675662</Real>
+ <Real>-2.9974201933988711</Real>
+ <Real>-2.909045620263937</Real>
+ <Real>-2.7875440262964757</Real>
+ <Real>-2.7504540277720304</Real>
+ <Real>-2.7468371894369819</Real>
+ <Real>-2.7467110222070819</Real>
+ <Real>-2.7467093840049168</Real>
+ <Real>-2.7467093840049168</Real>
+ </Sequence>
+ <Sequence Name="PointLogPmfsum">
+ <Int Name="Length">21</Int>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.6151742683824608</Real>
+ <Real>4.4405502576634461</Real>
+ <Real>4.4441023756206013</Real>
+ <Real>4.3362531480424424</Real>
+ <Real>4.4431072875080853</Real>
+ <Real>4.3095557059880116</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.3095557021915543</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ <Real>4.0544222744639065</Real>
+ </Sequence>
+</ReferenceData>
--- /dev/null
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+ <Sequence Name="Properties">
+ <Int Name="Length">3</Int>
+ <String>stage: initial</String>
+ <String>convolve forces: no</String>
+ <String>skip updates: no</String>
+ </Sequence>
+ <Sequence Name="Force">
+ <Int Name="Length">21</Int>
+ <Real>-70.000000000000014</Real>
+ <Real>-49.999999999999986</Real>
+ <Real>-5.0000000000000604</Real>
+ <Real>-49.999999999999993</Real>
+ <Real>-5</Real>
+ <Real>80.000000000000014</Real>
+ <Real>-184.99999999999989</Real>
+ <Real>-64.999999999999829</Real>
+ <Real>75.000000000000071</Real>
+ <Real>10.000000000000011</Real>
+ <Real>-59.999999999999943</Real>
+ <Real>-9.999999999999897</Real>
+ <Real>-4.9999999999999485</Real>
+ <Real>60</Real>
+ <Real>0</Real>
+ <Real>-44.999999999999929</Real>
+ <Real>60.000000000000057</Real>
+ <Real>5.3290705182007514e-14</Real>
+ <Real>-30.000000000000028</Real>
+ <Real>-59.999999999999943</Real>
+ <Real>-29.999999999999972</Real>
+ </Sequence>
+ <Sequence Name="Potential">
+ <Int Name="Length">21</Int>
+ <Real>7.1999999999999993</Real>
+ <Real>4.9999999999999973</Real>
+ <Real>0.19999999999999815</Real>
+ <Real>11.250000000000004</Real>
+ <Real>3.1999999999999966</Real>
+ <Real>8.4500000000000011</Real>
+ <Real>33.799999999999969</Real>
+ <Real>4.0499999999999874</Real>
+ <Real>11.250000000000021</Real>
+ <Real>1.7999999999999965</Real>
+ <Real>1.7999999999999965</Real>
+ <Real>0.049999999999998976</Real>
+ <Real>0.44999999999999746</Real>
+ <Real>6.049999999999998</Real>
+ <Real>1.2500000000000022</Real>
+ <Real>7.1999999999999993</Real>
+ <Real>6.0500000000000105</Real>
+ <Real>1.2500000000000022</Real>
+ <Real>0.45000000000000084</Real>
+ <Real>1.7999999999999965</Real>
+ <Real>3.1999999999999966</Real>
+ </Sequence>
+ <Sequence Name="PotentialJump">
+ <Int Name="Length">21</Int>
+ <Real>-6.9999999999999991</Real>
+ <Real>-4.9999999999999973</Real>
+ <Real>0.25000000000000266</Real>
+ <Real>-10.000000000000002</Real>
+ <Real>-0.75</Real>
+ <Real>-8</Real>
+ <Real>-27.749999999999982</Real>
+ <Real>-3.2499999999999947</Real>
+ <Real>-11.250000000000021</Real>
+ <Real>-0.99999999999999956</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>-0.24999999999999711</Real>
+ <Real>-5.9999999999999982</Real>
+ <Real>0</Real>
+ <Real>-6.7499999999999956</Real>
+ <Real>-6.0000000000000107</Real>
+ <Real>-5.5511151231257827e-15</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>-2.9999999999999964</Real>
+ </Sequence>
+ <Sequence Name="PointBias">
+ <Int Name="Length">21</Int>
+ <Real>-2.7252571125147735</Real>
+ <Real>-2.7406945936944584</Real>
+ <Real>-2.8751558537650319</Real>
+ <Real>-3.2406864635799923</Real>
+ <Real>-3.5460149559446359</Real>
+ <Real>-3.5687884755542472</Real>
+ <Real>-3.4901303714495153</Real>
+ <Real>-3.4970015262603624</Real>
+ <Real>-3.4496621504416853</Real>
+ <Real>-3.2125376886451624</Real>
+ <Real>-2.91505863813991</Real>
+ <Real>-2.7962936950351538</Real>
+ <Real>-2.8888307484050659</Real>
+ <Real>-2.9753285114361296</Real>
+ <Real>-2.8869539267116591</Real>
+ <Real>-2.7654523327441973</Real>
+ <Real>-2.7283623342197525</Real>
+ <Real>-2.7247454958847039</Real>
+ <Real>-2.7246193286548039</Real>
+ <Real>-2.7246176904526385</Real>
+ <Real>-2.7246176904526385</Real>
+ </Sequence>
+ <Sequence Name="PointLogPmfsum">
+ <Int Name="Length">21</Int>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>4.3399332371624695</Real>
+ <Real>4.1747174038020365</Real>
+ <Real>4.1623670231442622</Real>
+ <Real>4.0396595028897746</Real>
+ <Real>4.1446557041188061</Real>
+ <Real>3.9896509587172271</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.9896509549207697</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ </Sequence>
+</ReferenceData>
--- /dev/null
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+ <Sequence Name="Properties">
+ <Int Name="Length">3</Int>
+ <String>stage: initial</String>
+ <String>convolve forces: no</String>
+ <String>skip updates: yes</String>
+ </Sequence>
+ <Sequence Name="Force">
+ <Int Name="Length">21</Int>
+ <Real>-70.000000000000014</Real>
+ <Real>-49.999999999999986</Real>
+ <Real>-5.0000000000000604</Real>
+ <Real>-49.999999999999993</Real>
+ <Real>-5</Real>
+ <Real>80.000000000000014</Real>
+ <Real>-184.99999999999989</Real>
+ <Real>-64.999999999999829</Real>
+ <Real>75.000000000000071</Real>
+ <Real>10.000000000000011</Real>
+ <Real>-59.999999999999943</Real>
+ <Real>-9.999999999999897</Real>
+ <Real>-4.9999999999999485</Real>
+ <Real>60</Real>
+ <Real>0</Real>
+ <Real>-44.999999999999929</Real>
+ <Real>60.000000000000057</Real>
+ <Real>5.3290705182007514e-14</Real>
+ <Real>-30.000000000000028</Real>
+ <Real>-59.999999999999943</Real>
+ <Real>-29.999999999999972</Real>
+ </Sequence>
+ <Sequence Name="Potential">
+ <Int Name="Length">21</Int>
+ <Real>7.1999999999999993</Real>
+ <Real>4.9999999999999973</Real>
+ <Real>0.19999999999999815</Real>
+ <Real>11.250000000000004</Real>
+ <Real>3.1999999999999966</Real>
+ <Real>8.4500000000000011</Real>
+ <Real>33.799999999999969</Real>
+ <Real>4.0499999999999874</Real>
+ <Real>11.250000000000021</Real>
+ <Real>1.7999999999999965</Real>
+ <Real>1.7999999999999965</Real>
+ <Real>0.049999999999998976</Real>
+ <Real>0.44999999999999746</Real>
+ <Real>6.049999999999998</Real>
+ <Real>1.2500000000000022</Real>
+ <Real>7.1999999999999993</Real>
+ <Real>6.0500000000000105</Real>
+ <Real>1.2500000000000022</Real>
+ <Real>0.45000000000000084</Real>
+ <Real>1.7999999999999965</Real>
+ <Real>3.1999999999999966</Real>
+ </Sequence>
+ <Sequence Name="PotentialJump">
+ <Int Name="Length">21</Int>
+ <Real>-6.9999999999999991</Real>
+ <Real>-4.9999999999999973</Real>
+ <Real>0.25000000000000266</Real>
+ <Real>-10.000000000000002</Real>
+ <Real>-0.75</Real>
+ <Real>-8</Real>
+ <Real>-27.749999999999982</Real>
+ <Real>-3.2499999999999947</Real>
+ <Real>-11.250000000000021</Real>
+ <Real>-0.99999999999999956</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>-0.24999999999999711</Real>
+ <Real>-5.9999999999999982</Real>
+ <Real>0</Real>
+ <Real>-6.7499999999999956</Real>
+ <Real>-6.0000000000000107</Real>
+ <Real>-5.5511151231257827e-15</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>-2.9999999999999964</Real>
+ </Sequence>
+ <Sequence Name="PointBias">
+ <Int Name="Length">21</Int>
+ <Real>-2.7252571125147735</Real>
+ <Real>-2.7406945936944584</Real>
+ <Real>-2.8751558537650319</Real>
+ <Real>-3.2406864635799923</Real>
+ <Real>-3.5460149559446359</Real>
+ <Real>-3.5687884755542472</Real>
+ <Real>-3.4901303714495153</Real>
+ <Real>-3.4970015262603624</Real>
+ <Real>-3.4496621504416853</Real>
+ <Real>-3.2125376886451624</Real>
+ <Real>-2.91505863813991</Real>
+ <Real>-2.7962936950351538</Real>
+ <Real>-2.8888307484050659</Real>
+ <Real>-2.9753285114361296</Real>
+ <Real>-2.8869539267116591</Real>
+ <Real>-2.7654523327441973</Real>
+ <Real>-2.7283623342197525</Real>
+ <Real>-2.7247454958847039</Real>
+ <Real>-2.7246193286548039</Real>
+ <Real>-2.7246176904526385</Real>
+ <Real>-2.7246176904526385</Real>
+ </Sequence>
+ <Sequence Name="PointLogPmfsum">
+ <Int Name="Length">21</Int>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>4.3399332371624695</Real>
+ <Real>4.1747174038020365</Real>
+ <Real>4.1623670231442622</Real>
+ <Real>4.0396595028897746</Real>
+ <Real>4.1446557041188061</Real>
+ <Real>3.9896509587172271</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.9896509549207697</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ </Sequence>
+</ReferenceData>
--- /dev/null
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+ <Sequence Name="Properties">
+ <Int Name="Length">3</Int>
+ <String>stage: initial</String>
+ <String>convolve forces: yes</String>
+ <String>skip updates: no</String>
+ </Sequence>
+ <Sequence Name="Force">
+ <Int Name="Length">21</Int>
+ <Real>0.21526724902654029</Real>
+ <Real>0.00037168043378189554</Real>
+ <Real>0.0023506021278633634</Real>
+ <Real>1.8322908294787776e-14</Real>
+ <Real>1.8182159860257569e-05</Real>
+ <Real>-1.8182159861071263e-05</Real>
+ <Real>-6.7883759693111072e-06</Real>
+ <Real>6.7883758926473613e-06</Real>
+ <Real>-4.5547116425681544e-14</Real>
+ <Real>6.7883759521357456e-06</Real>
+ <Real>-6.7883759527878661e-06</Real>
+ <Real>-3.5538537690811558</Real>
+ <Real>-2.0147107048387825</Real>
+ <Real>-2.7052521678814725</Real>
+ <Real>-1.0406813794780787</Real>
+ <Real>-3.967066260592369</Real>
+ <Real>-0.87907598448922164</Real>
+ <Real>-1.6612940249811217</Real>
+ <Real>-2.6184399315060456</Real>
+ <Real>-1.2743003165852991</Real>
+ <Real>-0.86472575028406273</Real>
+ </Sequence>
+ <Sequence Name="Potential">
+ <Int Name="Length">21</Int>
+ <Real>5.3171052747656216</Real>
+ <Real>5.3139634951471235</Real>
+ <Real>5.3139852805648378</Real>
+ <Real>5.3139597783455503</Real>
+ <Real>5.313959928304433</Real>
+ <Real>5.313959928304433</Real>
+ <Real>5.3139598111771384</Real>
+ <Real>5.3139598111771384</Real>
+ <Real>5.3139597783455486</Real>
+ <Real>5.3139598111771358</Real>
+ <Real>5.3139598111771358</Real>
+ <Real>5.8523574407739236</Real>
+ <Real>5.909517708141518</Real>
+ <Real>5.5891055083437386</Real>
+ <Real>5.5179043538034485</Real>
+ <Real>5.6909400832286146</Real>
+ <Real>5.5084236091309347</Real>
+ <Real>5.4647638463620405</Real>
+ <Real>5.4221743541770948</Real>
+ <Real>5.4793615522962513</Real>
+ <Real>5.4998305387193387</Real>
+ </Sequence>
+ <Sequence Name="PotentialJump">
+ <Int Name="Length">21</Int>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0.60502446654887354</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0.93416481023846032</Real>
+ </Sequence>
+ <Sequence Name="PointBias">
+ <Int Name="Length">21</Int>
+ <Real>-2.7252571125147735</Real>
+ <Real>-2.7406945936944584</Real>
+ <Real>-2.8751558537650319</Real>
+ <Real>-3.2406864635799923</Real>
+ <Real>-3.5460149559446359</Real>
+ <Real>-3.5687884755542472</Real>
+ <Real>-3.4901303714495153</Real>
+ <Real>-3.4970015262603624</Real>
+ <Real>-3.4496621504416853</Real>
+ <Real>-3.2125376886451624</Real>
+ <Real>-2.91505863813991</Real>
+ <Real>-2.7962936950351538</Real>
+ <Real>-2.8888307484050659</Real>
+ <Real>-2.9753285114361296</Real>
+ <Real>-2.8869539267116591</Real>
+ <Real>-2.7654523327441973</Real>
+ <Real>-2.7283623342197525</Real>
+ <Real>-2.7247454958847039</Real>
+ <Real>-2.7246193286548039</Real>
+ <Real>-2.7246176904526385</Real>
+ <Real>-2.7246176904526385</Real>
+ </Sequence>
+ <Sequence Name="PointLogPmfsum">
+ <Int Name="Length">21</Int>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>4.3399332371624695</Real>
+ <Real>4.1747174038020365</Real>
+ <Real>4.1623670231442622</Real>
+ <Real>4.0396595028897746</Real>
+ <Real>4.1446557041188061</Real>
+ <Real>3.9896509587172271</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.9896509549207697</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ </Sequence>
+</ReferenceData>
--- /dev/null
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+ <Sequence Name="Properties">
+ <Int Name="Length">3</Int>
+ <String>stage: initial</String>
+ <String>convolve forces: yes</String>
+ <String>skip updates: yes</String>
+ </Sequence>
+ <Sequence Name="Force">
+ <Int Name="Length">21</Int>
+ <Real>0.21526724902654029</Real>
+ <Real>0.00037168043378189554</Real>
+ <Real>0.0023506021278633634</Real>
+ <Real>1.8322908294787776e-14</Real>
+ <Real>1.8182159860257569e-05</Real>
+ <Real>-1.8182159861071263e-05</Real>
+ <Real>-6.7883759693111072e-06</Real>
+ <Real>6.7883758926473613e-06</Real>
+ <Real>-4.5547116425681544e-14</Real>
+ <Real>6.7883759521357456e-06</Real>
+ <Real>-6.7883759527878661e-06</Real>
+ <Real>-3.5538537690811558</Real>
+ <Real>-2.0147107048387825</Real>
+ <Real>-2.7052521678814725</Real>
+ <Real>-1.0406813794780787</Real>
+ <Real>-3.967066260592369</Real>
+ <Real>-0.87907598448922164</Real>
+ <Real>-1.6612940249811217</Real>
+ <Real>-2.6184399315060456</Real>
+ <Real>-1.2743003165852991</Real>
+ <Real>-0.86472575028406273</Real>
+ </Sequence>
+ <Sequence Name="Potential">
+ <Int Name="Length">21</Int>
+ <Real>5.3171052747656216</Real>
+ <Real>5.3139634951471235</Real>
+ <Real>5.3139852805648378</Real>
+ <Real>5.3139597783455503</Real>
+ <Real>5.313959928304433</Real>
+ <Real>5.313959928304433</Real>
+ <Real>5.3139598111771384</Real>
+ <Real>5.3139598111771384</Real>
+ <Real>5.3139597783455486</Real>
+ <Real>5.3139598111771358</Real>
+ <Real>5.3139598111771358</Real>
+ <Real>5.8523574407739236</Real>
+ <Real>5.909517708141518</Real>
+ <Real>5.5891055083437386</Real>
+ <Real>5.5179043538034485</Real>
+ <Real>5.6909400832286146</Real>
+ <Real>5.5084236091309347</Real>
+ <Real>5.4647638463620405</Real>
+ <Real>5.4221743541770948</Real>
+ <Real>5.4793615522962513</Real>
+ <Real>5.4998305387193387</Real>
+ </Sequence>
+ <Sequence Name="PotentialJump">
+ <Int Name="Length">21</Int>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0.60502446654887354</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0</Real>
+ <Real>0.93416481023846032</Real>
+ </Sequence>
+ <Sequence Name="PointBias">
+ <Int Name="Length">21</Int>
+ <Real>-2.7252571125147735</Real>
+ <Real>-2.7406945936944584</Real>
+ <Real>-2.8751558537650319</Real>
+ <Real>-3.2406864635799923</Real>
+ <Real>-3.5460149559446359</Real>
+ <Real>-3.5687884755542472</Real>
+ <Real>-3.4901303714495153</Real>
+ <Real>-3.4970015262603624</Real>
+ <Real>-3.4496621504416853</Real>
+ <Real>-3.2125376886451624</Real>
+ <Real>-2.91505863813991</Real>
+ <Real>-2.7962936950351538</Real>
+ <Real>-2.8888307484050659</Real>
+ <Real>-2.9753285114361296</Real>
+ <Real>-2.8869539267116591</Real>
+ <Real>-2.7654523327441973</Real>
+ <Real>-2.7283623342197525</Real>
+ <Real>-2.7247454958847039</Real>
+ <Real>-2.7246193286548039</Real>
+ <Real>-2.7246176904526385</Real>
+ <Real>-2.7246176904526385</Real>
+ </Sequence>
+ <Sequence Name="PointLogPmfsum">
+ <Int Name="Length">21</Int>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>4.3399332371624695</Real>
+ <Real>4.1747174038020365</Real>
+ <Real>4.1623670231442622</Real>
+ <Real>4.0396595028897746</Real>
+ <Real>4.1446557041188061</Real>
+ <Real>3.9896509587172271</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.9896509549207697</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ <Real>3.7345175271931224</Real>
+ </Sequence>
+</ReferenceData>
#include "gromacs/math/vec.h"
#include "gromacs/math/vecdump.h"
#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/awh-history.h"
#include "gromacs/mdtypes/commrec.h"
#include "gromacs/mdtypes/df_history.h"
#include "gromacs/mdtypes/edsamhistory.h"
* But old code can not read a new entry that is present in the file
* (but can read a new format when new entries are not present).
*/
-static const int cpt_version = 16;
+static const int cpt_version = 17;
const char *est_names[estNR] =
"accumulated_plus", "accumulated_minus", "accumulated_plus_2", "accumulated_minus_2", "Tij", "Tij_empirical"
};
+/* AWH biasing history variables */
+enum {
+ eawhhIN_INITIAL,
+ eawhhEQUILIBRATEHISTOGRAM,
+ eawhhHISTSIZE,
+ eawhhNPOINTS,
+ eawhhCOORDPOINT, eawhhUMBRELLAGRIDPOINT,
+ eawhhUPDATELIST,
+ eawhhLOGSCALEDSAMPLEWEIGHT,
+ eawhhNUMUPDATES,
+ eawhhNR
+};
+
+const char *eawhh_names[eawhhNR] =
+{
+ "awh_in_initial",
+ "awh_equilibrateHistogram",
+ "awh_histsize",
+ "awh_npoints",
+ "awh_coordpoint", "awh_umbrellaGridpoint",
+ "awh_updatelist",
+ "awh_logScaledSampleWeight",
+ "awh_numupdates"
+};
+
//! Higher level vector element type, only used for formatting checkpoint dumps
enum class CptElementType
{
//! \brief Parts of the checkpoint state, only used for reporting
enum class StatePart
{
- microState, //!< The microstate of the simulated system
- kineticEnergy, //!< Kinetic energy, needed for T/P-coupling state
- energyHistory, //!< Energy observable statistics
- freeEnergyHistory //!< Free-energy state and observable statistics
+ microState, //!< The microstate of the simulated system
+ kineticEnergy, //!< Kinetic energy, needed for T/P-coupling state
+ energyHistory, //!< Energy observable statistics
+ freeEnergyHistory, //!< Free-energy state and observable statistics
+ accWeightHistogram //!< Accelerated weight histogram method state
};
//! \brief Return the name of a checkpoint entry based on part and part entry
{
switch (part)
{
- case StatePart::microState: return est_names [ecpt];
- case StatePart::kineticEnergy: return eeks_names[ecpt];
- case StatePart::energyHistory: return eenh_names[ecpt];
- case StatePart::freeEnergyHistory: return edfh_names[ecpt];
+ case StatePart::microState: return est_names [ecpt];
+ case StatePart::kineticEnergy: return eeks_names[ecpt];
+ case StatePart::energyHistory: return eenh_names[ecpt];
+ case StatePart::freeEnergyHistory: return edfh_names[ecpt];
+ case StatePart::accWeightHistogram: return eawhh_names[ecpt];
}
return nullptr;
int *nnodes, int *dd_nc, int *npme,
int *natoms, int *ngtc, int *nnhpres, int *nhchainlength,
int *nlambda, int *flags_state,
- int *flags_eks, int *flags_enh, int *flags_dfh,
+ int *flags_eks, int *flags_enh, int *flags_dfh, int *flags_awhh,
int *nED, int *eSwapCoords,
FILE *list)
{
{
*nED = 0;
}
+
if (*file_version >= 16)
{
do_cpt_int_err(xd, "swap", eSwapCoords, list);
{
*eSwapCoords = eswapNO;
}
+
+ if (*file_version >= 17)
+ {
+ do_cpt_int_err(xd, "AWH history flags", flags_awhh, list);
+ }
+ else
+ {
+ *flags_awhh = 0;
+ }
}
static int do_cpt_footer(XDR *xd, int file_version)
return 0;
}
+static int do_cpt_awh_bias(XDR *xd, gmx_bool bRead,
+ int fflags, gmx::AwhBiasHistory *biasHistory,
+ FILE *list)
+{
+ int ret = 0;
+
+ gmx::AwhBiasStateHistory *state = &biasHistory->state;
+ for (int i = 0; (i < eawhhNR && ret == 0); i++)
+ {
+ if (fflags & (1<<i))
+ {
+ switch (i)
+ {
+ case eawhhIN_INITIAL:
+ do_cpt_int_err(xd, eawhh_names[i], &state->in_initial, list); break;
+ case eawhhEQUILIBRATEHISTOGRAM:
+ do_cpt_int_err(xd, eawhh_names[i], &state->equilibrateHistogram, list); break;
+ case eawhhHISTSIZE:
+ do_cpt_double_err(xd, eawhh_names[i], &state->histSize, list); break;
+ case eawhhNPOINTS:
+ {
+ int numPoints;
+ if (!bRead)
+ {
+ numPoints = biasHistory->pointState.size();
+ }
+ do_cpt_int_err(xd, eawhh_names[i], &numPoints, list);
+ if (bRead)
+ {
+ biasHistory->pointState.resize(numPoints);
+ }
+ }
+ break;
+ case eawhhCOORDPOINT:
+ for (auto &psh : biasHistory->pointState)
+ {
+ do_cpt_double_err(xd, eawhh_names[i], &psh.target, list);
+ do_cpt_double_err(xd, eawhh_names[i], &psh.free_energy, list);
+ do_cpt_double_err(xd, eawhh_names[i], &psh.bias, list);
+ do_cpt_double_err(xd, eawhh_names[i], &psh.weightsum_iteration, list);
+ do_cpt_double_err(xd, eawhh_names[i], &psh.weightsum_covering, list);
+ do_cpt_double_err(xd, eawhh_names[i], &psh.weightsum_tot, list);
+ do_cpt_double_err(xd, eawhh_names[i], &psh.weightsum_ref, list);
+ do_cpt_step_err(xd, eawhh_names[i], &psh.last_update_index, list);
+ do_cpt_double_err(xd, eawhh_names[i], &psh.log_pmfsum, list);
+ do_cpt_double_err(xd, eawhh_names[i], &psh.visits_iteration, list);
+ do_cpt_double_err(xd, eawhh_names[i], &psh.visits_tot, list);
+ }
+ break;
+ case eawhhUMBRELLAGRIDPOINT:
+ do_cpt_int_err(xd, eawhh_names[i], &(state->umbrellaGridpoint), list); break;
+ case eawhhUPDATELIST:
+ do_cpt_int_err(xd, eawhh_names[i], &(state->origin_index_updatelist), list);
+ do_cpt_int_err(xd, eawhh_names[i], &(state->end_index_updatelist), list);
+ break;
+ case eawhhLOGSCALEDSAMPLEWEIGHT:
+ do_cpt_double_err(xd, eawhh_names[i], &(state->logScaledSampleWeight), list);
+ do_cpt_double_err(xd, eawhh_names[i], &(state->maxLogScaledSampleWeight), list);
+ break;
+ case eawhhNUMUPDATES:
+ do_cpt_step_err(xd, eawhh_names[i], &(state->numUpdates), list);
+ break;
+ default:
+ gmx_fatal(FARGS, "Unknown awh history entry %d\n", i);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int do_cpt_awh(XDR *xd, gmx_bool bRead,
+ int fflags, gmx::AwhHistory *awhHistory,
+ FILE *list)
+{
+ int ret = 0;
+
+ if (fflags != 0)
+ {
+ std::shared_ptr<gmx::AwhHistory> awhHistoryLocal;
+
+ if (awhHistory == nullptr)
+ {
+ GMX_RELEASE_ASSERT(bRead, "do_cpt_awh should not be called for writing without an AwhHistory");
+
+ awhHistoryLocal = std::shared_ptr<gmx::AwhHistory>(new gmx::AwhHistory());
+ awhHistory = awhHistoryLocal.get();
+ }
+
+ /* To be able to read and write the AWH data several parameters determining the layout of the AWH structs need to be known
+ (nbias, npoints, etc.). The best thing (?) would be to have these passed to this function. When writing to a checkpoint
+ these parameters are available in awh_history (after calling init_awh_history). When reading from a checkpoint though, there
+ is no initialized awh_history (it is initialized and set in this function). The AWH parameters have not always been read
+ at the time when this function is called for reading so I don't know how to pass them as input. Here, this is solved by
+ when writing a checkpoint, also storing parameters needed for future reading of the checkpoint.
+
+ Another issue is that some variables that AWH checkpoints don't have a registered enum and string (e.g. nbias below).
+ One difficulty is the multilevel structure of the data which would need to be represented somehow. */
+
+ int numBias;
+ if (!bRead)
+ {
+ numBias = awhHistory->bias.size();
+ }
+ do_cpt_int_err(xd, "awh_nbias", &numBias, list);
+
+ if (bRead)
+ {
+ awhHistory->bias.resize(numBias);
+ }
+ for (auto &bias : awhHistory->bias)
+ {
+ ret = do_cpt_awh_bias(xd, bRead, fflags, &bias, list);
+ if (ret)
+ {
+ return ret;
+ }
+ }
+ do_cpt_double_err(xd, "awh_potential_offset", &awhHistory->potentialOffset, list);
+ }
+ return ret;
+}
static int do_cpt_files(XDR *xd, gmx_bool bRead,
gmx_file_position_t **p_outputfiles, int *nfiles,
flags_dfh = 0;
}
+ int flags_awhh = 0;
+ if (state->awhHistory != nullptr && state->awhHistory->bias.size() > 0)
+ {
+ flags_awhh |= ( (1 << eawhhIN_INITIAL) |
+ (1 << eawhhEQUILIBRATEHISTOGRAM) |
+ (1 << eawhhHISTSIZE) |
+ (1 << eawhhNPOINTS) |
+ (1 << eawhhCOORDPOINT) |
+ (1 << eawhhUMBRELLAGRIDPOINT) |
+ (1 << eawhhUPDATELIST) |
+ (1 << eawhhLOGSCALEDSAMPLEWEIGHT) |
+ (1 << eawhhNUMUPDATES));
+ }
+
/* We can check many more things now (CPU, acceleration, etc), but
* it is highly unlikely to have two separate builds with exactly
* the same version, user, time, and build host!
&eIntegrator, &simulation_part, &step, &t, &nppnodes,
DOMAINDECOMP(cr) ? domdecCells : nullptr, &npmenodes,
&state->natoms, &state->ngtc, &state->nnhpres,
- &state->nhchainlength, &nlambda, &state->flags, &flags_eks, &flags_enh, &flags_dfh,
+ &state->nhchainlength, &nlambda, &state->flags, &flags_eks, &flags_enh, &flags_dfh, &flags_awhh,
&nED, &eSwapCoords,
nullptr);
(do_cpt_enerhist(gmx_fio_getxdr(fp), FALSE, flags_enh, enerhist, nullptr) < 0) ||
(do_cpt_df_hist(gmx_fio_getxdr(fp), flags_dfh, nlambda, &state->dfhist, nullptr) < 0) ||
(do_cpt_EDstate(gmx_fio_getxdr(fp), FALSE, nED, edsamhist, nullptr) < 0) ||
+ (do_cpt_awh(gmx_fio_getxdr(fp), FALSE, flags_awhh, state->awhHistory.get(), NULL) < 0) ||
(do_cpt_swapstate(gmx_fio_getxdr(fp), FALSE, eSwapCoords, swaphist, nullptr) < 0) ||
(do_cpt_files(gmx_fio_getxdr(fp), FALSE, &outputfiles, &noutputfiles, nullptr,
file_version) < 0))
char buf[STEPSTRSIZE];
int eIntegrator_f, nppnodes_f, npmenodes_f;
ivec dd_nc_f;
- int natoms, ngtc, nnhpres, nhchainlength, nlambda, fflags, flags_eks, flags_enh, flags_dfh;
+ int natoms, ngtc, nnhpres, nhchainlength, nlambda, fflags, flags_eks, flags_enh, flags_dfh, flags_awhh;
int nED, eSwapCoords;
int ret;
gmx_file_position_t *outputfiles;
&eIntegrator_f, simulation_part, step, t,
&nppnodes_f, dd_nc_f, &npmenodes_f,
&natoms, &ngtc, &nnhpres, &nhchainlength, &nlambda,
- &fflags, &flags_eks, &flags_enh, &flags_dfh,
+ &fflags, &flags_eks, &flags_enh, &flags_dfh, &flags_awhh,
&nED, &eSwapCoords, nullptr);
if (bAppendOutputFiles &&
cp_error();
}
+ if (flags_awhh != 0 && state->awhHistory == nullptr)
+ {
+ state->awhHistory = std::shared_ptr<gmx::AwhHistory>(new gmx::AwhHistory());
+ }
+ ret = do_cpt_awh(gmx_fio_getxdr(fp), TRUE,
+ flags_awhh, state->awhHistory.get(), NULL);
+ if (ret)
+ {
+ cp_error();
+ }
+
if (eSwapCoords != eswapNO && observablesHistory->swapHistory == nullptr)
{
observablesHistory->swapHistory = std::unique_ptr<swaphistory_t>(new swaphistory_t {});
int nppnodes, npme;
ivec dd_nc;
int nlambda;
- int flags_eks, flags_enh, flags_dfh;
+ int flags_eks, flags_enh, flags_dfh, flags_awhh;
double t;
t_state state;
int nED, eSwapCoords;
&version, &btime, &buser, &bhost, &double_prec, &fprog, &ftime,
&eIntegrator, simulation_part, step, &t, &nppnodes, dd_nc, &npme,
&state.natoms, &state.ngtc, &state.nnhpres, &state.nhchainlength,
- &nlambda, &state.flags, &flags_eks, &flags_enh, &flags_dfh,
+ &nlambda, &state.flags, &flags_eks, &flags_enh, &flags_dfh, &flags_awhh,
&nED, &eSwapCoords, nullptr);
gmx_fio_close(fp);
int nppnodes, npme;
ivec dd_nc;
int nlambda;
- int flags_eks, flags_enh, flags_dfh;
+ int flags_eks, flags_enh, flags_dfh, flags_awhh;
int nED, eSwapCoords;
int nfiles_loc;
gmx_file_position_t *files_loc = nullptr;
&version, &btime, &buser, &bhost, &double_prec, &fprog, &ftime,
&eIntegrator, simulation_part, step, t, &nppnodes, dd_nc, &npme,
&state->natoms, &state->ngtc, &state->nnhpres, &state->nhchainlength,
- &nlambda, &state->flags, &flags_eks, &flags_enh, &flags_dfh,
+ &nlambda, &state->flags, &flags_eks, &flags_enh, &flags_dfh, &flags_awhh,
&nED, &eSwapCoords, nullptr);
ret =
do_cpt_state(gmx_fio_getxdr(fp), state->flags, state, nullptr);
cp_error();
}
+ ret = do_cpt_awh(gmx_fio_getxdr(fp), TRUE,
+ flags_awhh, state->awhHistory.get(), NULL);
+ if (ret)
+ {
+ cp_error();
+ }
+
swaphistory_t swaphist = {};
ret = do_cpt_swapstate(gmx_fio_getxdr(fp), TRUE, eSwapCoords, &swaphist, nullptr);
if (ret)
double t;
ivec dd_nc;
int nlambda;
- int flags_eks, flags_enh, flags_dfh;
+ int flags_eks, flags_enh, flags_dfh, flags_awhh;;
int nED, eSwapCoords;
int ret;
gmx_file_position_t *outputfiles;
&eIntegrator, &simulation_part, &step, &t, &nppnodes, dd_nc, &npme,
&state.natoms, &state.ngtc, &state.nnhpres, &state.nhchainlength,
&nlambda, &state.flags,
- &flags_eks, &flags_enh, &flags_dfh, &nED, &eSwapCoords,
+ &flags_eks, &flags_enh, &flags_dfh, &flags_awhh, &nED, &eSwapCoords,
out);
ret = do_cpt_state(gmx_fio_getxdr(fp), state.flags, &state, out);
if (ret)
ret = do_cpt_EDstate(gmx_fio_getxdr(fp), TRUE, nED, &edsamhist, out);
}
+ if (ret == 0)
+ {
+ ret = do_cpt_awh(gmx_fio_getxdr(fp), TRUE,
+ flags_awhh, state.awhHistory.get(), out);
+ }
+
if (ret == 0)
{
swaphistory_t swaphist = {};
#include "gromacs/fileio/gmxfio-xdr.h"
#include "gromacs/math/units.h"
#include "gromacs/math/vec.h"
+#include "gromacs/mdtypes/awh-history.h"
+#include "gromacs/mdtypes/awh-params.h"
#include "gromacs/mdtypes/inputrec.h"
#include "gromacs/mdtypes/md_enums.h"
#include "gromacs/mdtypes/pull-params.h"
tpxv_ReplacePullPrintCOM12, /**< Replaced print-com-1, 2 with pull-print-com */
tpxv_PullExternalPotential, /**< Added pull type external potential */
tpxv_GenericParamsForElectricField, /**< Introduced KeyValueTree and moved electric field parameters */
+ tpxv_AcceleratedWeightHistogram, /**< sampling with accelerated weight histogram method (AWH) */
tpxv_Count /**< the total number of tpxv versions */
};
}
}
+static void do_awhBias(t_fileio *fio, gmx::AwhBiasParams *awhBiasParams, gmx_bool bRead,
+ int gmx_unused file_version)
+{
+ gmx_fio_do_int(fio, awhBiasParams->eTarget);
+ gmx_fio_do_double(fio, awhBiasParams->targetBetaScaling);
+ gmx_fio_do_double(fio, awhBiasParams->targetCutoff);
+ gmx_fio_do_int(fio, awhBiasParams->eGrowth);
+ gmx_fio_do_int(fio, awhBiasParams->bUserData);
+ gmx_fio_do_double(fio, awhBiasParams->errorInitial);
+ gmx_fio_do_int(fio, awhBiasParams->ndim);
+ gmx_fio_do_int(fio, awhBiasParams->shareGroup);
+ gmx_fio_do_gmx_bool(fio, awhBiasParams->equilibrateHistogram);
+
+ if (bRead)
+ {
+ snew(awhBiasParams->dimParams, awhBiasParams->ndim);
+ }
+
+ for (int d = 0; d < awhBiasParams->ndim; d++)
+ {
+ gmx::AwhDimParams *dimParams = &awhBiasParams->dimParams[d];
+
+ gmx_fio_do_int(fio, dimParams->eCoordProvider);
+ gmx_fio_do_int(fio, dimParams->coordIndex);
+ gmx_fio_do_double(fio, dimParams->origin);
+ gmx_fio_do_double(fio, dimParams->end);
+ gmx_fio_do_double(fio, dimParams->period);
+ gmx_fio_do_double(fio, dimParams->forceConstant);
+ gmx_fio_do_double(fio, dimParams->diffusion);
+ gmx_fio_do_double(fio, dimParams->coordValueInit);
+ gmx_fio_do_double(fio, dimParams->coverDiameter);
+ }
+}
+
+static void do_awh(t_fileio *fio, gmx::AwhParams *awhParams, gmx_bool bRead,
+ int gmx_unused file_version)
+{
+ gmx_fio_do_int(fio, awhParams->numBias);
+ gmx_fio_do_int(fio, awhParams->nstOut);
+ gmx_fio_do_int64(fio, awhParams->seed);
+ gmx_fio_do_int(fio, awhParams->nstSampleCoord);
+ gmx_fio_do_int(fio, awhParams->numSamplesUpdateFreeEnergy);
+ gmx_fio_do_int(fio, awhParams->ePotential);
+ gmx_fio_do_gmx_bool(fio, awhParams->shareBiasMultisim);
+
+ if (awhParams->numBias > 0)
+ {
+ if (bRead)
+ {
+ snew(awhParams->awhBiasParams, awhParams->numBias);
+ }
+
+ for (int k = 0; k < awhParams->numBias; k++)
+ {
+ do_awhBias(fio, &awhParams->awhBiasParams[k], bRead, file_version);
+ }
+ }
+}
+
static void do_pull(t_fileio *fio, pull_params_t *pull, gmx_bool bRead,
int file_version, int ePullOld)
{
ir->bPull = FALSE;
}
+ if (file_version >= tpxv_AcceleratedWeightHistogram)
+ {
+ gmx_fio_do_gmx_bool(fio, ir->bDoAwh);
+
+ if (ir->bDoAwh)
+ {
+ if (bRead)
+ {
+ snew(ir->awhParams, 1);
+ }
+ do_awh(fio, ir->awhParams, bRead, file_version);
+ }
+ }
+ else
+ {
+ ir->bDoAwh = FALSE;
+ }
+
/* Enforced rotation */
if (file_version >= 74)
{
#include <sys/types.h>
+#include "gromacs/awh/read-params.h"
#include "gromacs/commandline/pargs.h"
#include "gromacs/ewald/ewald-utils.h"
#include "gromacs/ewald/pme.h"
* that providers have been registerd for all external potentials.
*/
+ if (ir->bDoAwh)
+ {
+ setStateDependentAwhParams(ir->awhParams, ir->pull, pull,
+ state.box, ir->ePBC, &ir->opts, wi);
+ }
+
if (ir->bPull)
{
finish_pull(pull);
#include <algorithm>
#include <string>
+#include "gromacs/awh/read-params.h"
#include "gromacs/fileio/readinp.h"
#include "gromacs/fileio/warninp.h"
#include "gromacs/gmxlib/chargegroup.h"
is->pull_grp = read_pullparams(&ninp, &inp, ir->pull, wi);
}
+ /* AWH biasing
+ NOTE: needs COM pulling input */
+ CCTYPE("AWH biasing");
+ EETYPE("awh", ir->bDoAwh, yesno_names);
+ if (ir->bDoAwh)
+ {
+ if (ir->bPull)
+ {
+ ir->awhParams = gmx::readAndCheckAwhParams(&ninp, &inp, ir, wi);
+ }
+ else
+ {
+ gmx_fatal(FARGS, "AWH biasing is only compatible with COM pulling turned on");
+ }
+ }
+
/* Enforced rotation */
CCTYPE("ENFORCED ROTATION");
CTYPE("Enforced rotation: No or Yes");
pcrd->init = 0;
}
- get_pull_coord_value(pull_work, c, &pbc, &value);
+ value = get_pull_coord_value(pull_work, c, &pbc);
value *= pull_conversion_factor_internal2userinput(pcrd);
fprintf(stderr, " %10.3f %s", value, pull_coordinate_units(pcrd));
; COM PULLING
pull = no
+; AWH biasing
+awh = no
+
; ENFORCED ROTATION
; Enforced rotation: No or Yes
rotation = no
; COM PULLING
pull = no
+; AWH biasing
+awh = no
+
; ENFORCED ROTATION
; Enforced rotation: No or Yes
rotation = no
; COM PULLING
pull = no
+; AWH biasing
+awh = no
+
; ENFORCED ROTATION
; Enforced rotation: No or Yes
rotation = no
; COM PULLING
pull = no
+; AWH biasing
+awh = no
+
; ENFORCED ROTATION
; Enforced rotation: No or Yes
rotation = no
; COM PULLING
pull = no
+; AWH biasing
+awh = no
+
; ENFORCED ROTATION
; Enforced rotation: No or Yes
rotation = no
; COM PULLING
pull = no
+; AWH biasing
+awh = no
+
; ENFORCED ROTATION
; Enforced rotation: No or Yes
rotation = no
; COM PULLING
pull = no
+; AWH biasing
+awh = no
+
; ENFORCED ROTATION
; Enforced rotation: No or Yes
rotation = no
#include "gromacs/math/vec.h"
#include "gromacs/mdlib/mdrun.h"
#include "gromacs/mdlib/tgroup.h"
+#include "gromacs/mdtypes/awh-params.h"
#include "gromacs/mdtypes/commrec.h"
#include "gromacs/mdtypes/inputrec.h"
#include "gromacs/mdtypes/md_enums.h"
}
}
+static void bc_awhBias(const t_commrec *cr, gmx::AwhBiasParams *awhBiasParams)
+{
+ block_bc(cr, *awhBiasParams);
+
+ snew_bc(cr, awhBiasParams->dimParams, awhBiasParams->ndim);
+ nblock_bc(cr, awhBiasParams->ndim, awhBiasParams->dimParams);
+}
+
+static void bc_awh(const t_commrec *cr, gmx::AwhParams *awhParams)
+{
+ int k;
+
+ block_bc(cr, *awhParams);
+ snew_bc(cr, awhParams->awhBiasParams, awhParams->numBias);
+ for (k = 0; k < awhParams->numBias; k++)
+ {
+ bc_awhBias(cr, &awhParams->awhBiasParams[k]);
+ }
+}
+
static void bc_pull_group(const t_commrec *cr, t_pull_group *pgrp)
{
block_bc(cr, *pgrp);
snew_bc(cr, inputrec->pull, 1);
bc_pull(cr, inputrec->pull);
}
+ if (inputrec->bDoAwh)
+ {
+ snew_bc(cr, inputrec->awhParams, 1);
+ bc_awh(cr, inputrec->awhParams);
+ }
+
if (inputrec->bRot)
{
snew_bc(cr, inputrec->rot, 1);
#include <array>
+#include "gromacs/awh/awh.h"
#include "gromacs/domdec/dlbtiming.h"
#include "gromacs/domdec/domdec.h"
#include "gromacs/domdec/domdec_struct.h"
* global communication at the end, so global barriers within the MD loop
* are as close together as possible.
*
+ * \param[in] fplog The log file
* \param[in] cr The communication record
* \param[in] inputrec The input record
* \param[in] step The current MD step
* \todo Convert all other algorithms called here to ForceProviders.
*/
static void
-computeSpecialForces(t_commrec *cr,
+computeSpecialForces(FILE *fplog,
+ t_commrec *cr,
t_inputrec *inputrec,
gmx_int64_t step,
double t,
forceWithVirial,
mdatoms, enerd, lambda, t,
wcycle);
+
+ if (inputrec->bDoAwh)
+ {
+ Awh &awh = *inputrec->awh;
+ enerd->term[F_COM_PULL] +=
+ awh.applyBiasForcesAndUpdateBias(inputrec->ePBC, *mdatoms, box,
+ forceWithVirial,
+ t, step, wcycle, fplog);
+ }
}
rvec *f = as_rvec_array(forceWithVirial->force_.data());
wallcycle_stop(wcycle, ewcFORCE);
- computeSpecialForces(cr, inputrec, step, t, wcycle,
+ computeSpecialForces(fplog, cr, inputrec, step, t, wcycle,
fr->forceProviders, box, x, mdatoms, lambda,
flags, &forceWithVirial, enerd,
ed, bNS);
}
}
- computeSpecialForces(cr, inputrec, step, t, wcycle,
+ computeSpecialForces(fplog, cr, inputrec, step, t, wcycle,
fr->forceProviders, box, x, mdatoms, lambda,
flags, &forceWithVirial, enerd,
ed, bNS);
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017, 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.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * Contains datatypes and function declarations needed by AWH to
+ * have its data checkpointed.
+ *
+ * \author Viveca Lindahl
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MDTYPES_AWHHISTORY_H
+#define GMX_MDTYPES_AWHHISTORY_H
+
+#include <vector>
+
+#include "gromacs/utility/basedefinitions.h"
+
+namespace gmx
+{
+
+/*! \cond INTERNAL */
+
+//! Grid point state history data.
+struct AwhPointStateHistory
+{
+ double bias; /**< Current biasing function estimate */
+ double free_energy; /**< Current estimate of the convolved free energy/PMF. */
+ double target; /**< Current target distribution, normalized to 1 */
+ double weightsum_iteration; /**< Accumulated weight this iteration (1 replica) */
+ double weightsum_covering; /**< Accumulated weights for covering checks */
+ double weightsum_tot; /**< Accumulated weights, never reset */
+ double weightsum_ref; /**< The reference weight histogram determining the f updates */
+ gmx_int64_t last_update_index; /**< The last update that was performed at this point. */
+ double log_pmfsum; /**< Logarithm of the PMF histogram (for 1 replica) */
+ double visits_iteration; /**< Visits to this bin this iteration (1 replica) */
+ double visits_tot; /**< Accumulated visits to this bin */
+};
+
+//! The global AWH bias history state, contains most data of the corresponding struct in awh.h.
+struct AwhBiasStateHistory
+{
+ int umbrellaGridpoint; /**< Index for the current umbrella reference coordinate point (for umbrella potential type) */
+ int origin_index_updatelist; /**< Point index of the origin of the subgrid that has been touched since last update. */
+ int end_index_updatelist; /**< Point index of the end of the subgrid that has been touched since last update. */
+ int in_initial; /**< True if in the intial stage. */
+ int equilibrateHistogram; /**< True if histogram needs equilibration. */
+ double histSize; /**< Size of reference weight histogram. */
+ double logScaledSampleWeight; /**< The log of the current sample weight, scaled because of the histogram rescaling. */
+ double maxLogScaledSampleWeight; /**< Maximum sample weight obtained for previous (smaller) histogram sizes. */
+ gmx_int64_t numUpdates; /**< The number of updates. */
+
+ /*! \brief Constructor. */
+ AwhBiasStateHistory() : umbrellaGridpoint(0),
+ origin_index_updatelist(0),
+ end_index_updatelist(0),
+ in_initial(0),
+ equilibrateHistogram(0),
+ histSize(0),
+ logScaledSampleWeight(0),
+ maxLogScaledSampleWeight(0),
+ numUpdates(0)
+ {
+ }
+};
+
+//! AWH bias history data. Note that this is a copy of an AWH internal struct.
+struct AwhBiasHistory
+{
+ std::vector<AwhPointStateHistory> pointState; /**< History for grid coordinate points. */
+
+ AwhBiasStateHistory state; /**< The global state of the AWH bias. */
+
+ /*! \brief Constructor. */
+ AwhBiasHistory() : pointState(),
+ state()
+ {
+ }
+};
+
+//! A collection of AWH bias history data. */
+struct AwhHistory
+{
+ std::vector<AwhBiasHistory> bias; /**< History for each bias. */
+ double potentialOffset; /**< The offset of the bias potential due to bias updates. */
+
+ /*! \brief Constructor. */
+ AwhHistory() : bias(),
+ potentialOffset(0)
+ {
+ }
+};
+
+/*! \endcond */
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2016,2017, 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.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * Declares AWH parameter data types.
+ *
+ * Besides internal use by the AWH module, the AWH parameters are needed
+ * for reading the user input (mdp) file and for reading and writing the
+ * parameters to the mdrun input (tpr) file.
+ *
+ * \author Viveca Lindahl
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MDTYPES_AWH_PARAMS_H
+#define GMX_MDTYPES_AWH_PARAMS_H
+
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/basedefinitions.h"
+
+namespace gmx
+{
+
+//! Target distribution enum.
+enum {
+ eawhtargetCONSTANT, eawhtargetCUTOFF, eawhtargetBOLTZMANN, eawhtargetLOCALBOLTZMANN, eawhtargetNR
+};
+//! String for target distribution.
+extern const char *eawhtarget_names[eawhtargetNR+1];
+//! Macro for target distribution string.
+#define EAWHTARGET(e) enum_name(e, gmx::eawhtargetNR, gmx::eawhtarget_names)
+
+//! Weight histogram growth enum.
+enum {
+ eawhgrowthEXP_LINEAR, eawhgrowthLINEAR, eawhgrowthNR
+};
+//! String for weight histogram growth
+extern const char *eawhgrowth_names[eawhgrowthNR+1];
+//! Macro for weight histogram growth string.
+#define EAWHGROWTH(e) enum_name(e, gmx::eawhgrowthNR, gmx::eawhgrowth_names)
+
+//! AWH potential type enum.
+enum {
+ eawhpotentialCONVOLVED, eawhpotentialUMBRELLA, eawhpotentialNR
+};
+//! String for AWH potential type
+extern const char *eawhpotential_names[eawhpotentialNR+1];
+//! Macro for AWH potential type string.
+#define EAWHPOTENTIAL(e) enum_name(e, gmx::eawhpotentialNR, gmx::eawhpotential_names)
+
+//! AWH bias reaction coordinate provider
+enum {
+ eawhcoordproviderPULL, eawhcoordproviderNR
+};
+//! String for AWH bias reaction coordinate provider.
+extern const char *eawhcoordprovider_names[eawhcoordproviderNR+1];
+//! Macro for AWH bias reaction coordinate provider.
+#define EAWHCOORDPROVIDER(e) enum_name(e, gmx::eawhcoordproviderNR, gmx::eawhcoordprovider_names)
+
+/*! \cond INTERNAL */
+
+//! Parameters for an AWH coordinate dimension.
+struct AwhDimParams
+{
+ int eCoordProvider; /**< The module providing the reaction coordinate. */
+ int coordIndex; /**< Index of reaction coordinate in the provider. */
+ double origin; /**< Start value of the interval. */
+ double end; /**< End value of the interval. */
+ double period; /**< The period of this dimension (= 0 if not periodic). */
+ double forceConstant; /**< The force constant in kJ/mol/nm^2, kJ/mol/rad^2 */
+ double diffusion; /**< Estimated diffusion constant in units of nm^2/ps or rad^2/ps. */
+ double coordValueInit; /**< The initial coordinate value. */
+ double coverDiameter; /**< The diameter that needs to be sampled around a point before it is considered covered. */
+};
+
+//! Parameters for an AWH bias.
+struct AwhBiasParams
+{
+ // TODO: Turn dimParams into a std::vector when moved into AWH module
+ int ndim; /**< Dimension of the coordinate space. */
+ AwhDimParams *dimParams; /**< AWH parameters per dimension. */
+ int eTarget; /**< Type of target distribution. */
+ double targetBetaScaling; /**< Beta scaling value for Boltzmann type target distributions. */
+ double targetCutoff; /**< Free energy cutoff value for cutoff type target distribution in kJ/mol.*/
+ int eGrowth; /**< How the biasing histogram grows. */
+ int bUserData; /**< Is there a user-defined initial PMF estimate and target estimate? */
+ double errorInitial; /**< Estimated initial free energy error in kJ/mol. */
+ int shareGroup; /**< When >0, the bias is shared with biases of the same group and across multiple simulations when shareBiasMultisim=true */
+ gmx_bool equilibrateHistogram; /**< True if the simulation starts out by equilibrating the histogram. */
+};
+
+//! Parameters for AWH.
+struct AwhParams
+{
+ // TODO: Turn awhBiasParams into a std::vector when moved into AWH module
+ int numBias; /**< The number of AWH biases.*/
+ AwhBiasParams *awhBiasParams; /**< AWH bias parameters.*/
+ gmx_int64_t seed; /**< Random seed.*/
+ int nstOut; /**< Output step interval.*/
+ int nstSampleCoord; /**< Number of samples per coordinate sample (also used for PMF) */
+ int numSamplesUpdateFreeEnergy; /**< Number of samples per free energy update. */
+ int ePotential; /**< Type of potential. */
+ gmx_bool shareBiasMultisim; /**< When true, share biases with shareGroup>0 between multi-simulations */
+};
+
+/*! \endcond */
+
+} // namespace gmx
+
+#endif /* GMX_MDTYPES_AWH_PARAMS_H */
force_(force),
computeVirial_(computeVirial)
{
- clear_mat(virial_);
+ for (int dim1 = 0; dim1 < DIM; dim1++)
+ {
+ for (int dim2 = 0; dim2 < DIM; dim2++)
+ {
+ virial_[dim1][dim2] = 0;
+ }
+ }
}
/*! \brief Adds a virial contribution
#include "gromacs/math/veccompare.h"
#include "gromacs/math/vecdump.h"
+#include "gromacs/mdtypes/awh-params.h"
#include "gromacs/mdtypes/md_enums.h"
#include "gromacs/mdtypes/pull-params.h"
#include "gromacs/pbcutil/pbc.h"
}
}
+static void pr_awh_bias_dim(FILE *fp, int indent, gmx::AwhDimParams *awhDimParams, char *prefix)
+{
+ pr_indent(fp, indent);
+ indent++;
+ fprintf(fp, "%s:\n", prefix);
+ PS("coord-provider", EAWHCOORDPROVIDER(awhDimParams->eCoordProvider));
+ PI("coord-index", awhDimParams->coordIndex + 1);
+ PR("start", awhDimParams->origin);
+ PR("end", awhDimParams->end);
+ PR("period", awhDimParams->period);
+ PR("force-constant", awhDimParams->forceConstant);
+ PR("diffusion", awhDimParams->diffusion);
+ PR("start", awhDimParams->origin);
+ PR("end", awhDimParams->end);
+ PR("cover-diameter", awhDimParams->coverDiameter);
+}
+
+static void pr_awh_bias(FILE *fp, int indent, gmx::AwhBiasParams *awhBiasParams, char *prefix)
+{
+ char opt[STRLEN];
+
+ sprintf(opt, "%s-error-init", prefix);
+ PR(opt, awhBiasParams->errorInitial);
+ sprintf(opt, "%s-growth", prefix);
+ PS(opt, EAWHGROWTH(awhBiasParams->eGrowth));
+ sprintf(opt, "%s-target", prefix);
+ PS(opt, EAWHTARGET(awhBiasParams->eTarget));
+ sprintf(opt, "%s-target-beta-scalng", prefix);
+ PR(opt, awhBiasParams->targetBetaScaling);
+ sprintf(opt, "%s-target-cutoff", prefix);
+ PR(opt, awhBiasParams->targetCutoff);
+ sprintf(opt, "%s-user-data", prefix);
+ PS(opt, EBOOL(awhBiasParams->bUserData));
+ sprintf(opt, "%s-share-group", prefix);
+ PI(opt, awhBiasParams->shareGroup);
+ sprintf(opt, "%s-equilibrate-histogram", prefix);
+ PS(opt, EBOOL(awhBiasParams->equilibrateHistogram));
+ sprintf(opt, "%s-ndim", prefix);
+ PI(opt, awhBiasParams->ndim);
+
+ for (int d = 0; d < awhBiasParams->ndim; d++)
+ {
+ char prefixdim[STRLEN];
+ sprintf(prefixdim, "%s-dim%d", prefix, d + 1);
+ pr_awh_bias_dim(fp, indent, &awhBiasParams->dimParams[d], prefixdim);
+ }
+}
+
+static void pr_awh(FILE *fp, int indent, gmx::AwhParams *awhParams)
+{
+ int k;
+ char opt[STRLEN], prefix[STRLEN];
+
+ sprintf(prefix, "%s", "awh");
+
+ sprintf(opt, "%s-potential", prefix);
+ PS(opt, EAWHPOTENTIAL(awhParams->ePotential));
+ sprintf(opt, "%s-seed", prefix);
+ PI(opt, awhParams->seed);
+ sprintf(opt, "%s-nstout", prefix);
+ PI(opt, awhParams->nstOut);
+ sprintf(opt, "%s-nstsample", prefix);
+ PI(opt, awhParams->nstSampleCoord);
+ sprintf(opt, "%s-nsamples-update", prefix);
+ PI(opt, awhParams->numSamplesUpdateFreeEnergy);
+ sprintf(opt, "%s-share-bias-multisim", prefix);
+ PS(opt, EBOOL(awhParams->shareBiasMultisim));
+ sprintf(opt, "%s-nbias", prefix);
+ PI(opt, awhParams->numBias);
+
+ for (k = 0; k < awhParams->numBias; k++)
+ {
+ sprintf(prefix, "awh%d", k + 1);
+ pr_awh_bias(fp, indent, &awhParams->awhBiasParams[k], prefix);
+ }
+}
+
static void pr_rotgrp(FILE *fp, int indent, int g, const t_rotgrp *rotg)
{
pr_indent(fp, indent);
pr_pull(fp, indent, ir->pull);
}
+ /* AWH BIASING */
+ PS("awh", EBOOL(ir->bDoAwh));
+ if (ir->bDoAwh)
+ {
+ pr_awh(fp, indent, ir->awhParams);
+ }
+
/* ENFORCED ROTATION */
PS("rotation", EBOOL(ir->bRot));
if (ir->bRot)
fprintf(fp, "WARNING: Both files use COM pulling, but comparing of the pull struct is not implemented (yet). The pull parameters could be the same or different.\n");
}
+static void cmp_awhDimParams(FILE *fp, const gmx::AwhDimParams *dimp1, const gmx::AwhDimParams *dimp2, int dimIndex, real ftol, real abstol)
+{
+ /* Note that we have double index here, but the compare functions only
+ * support one index, so here we only print the dim index and not the bias.
+ */
+ cmp_int(fp, "inputrec.awhParams->bias?->dim->coord_index", dimIndex, dimp1->coordIndex, dimp2->coordIndex);
+ cmp_double(fp, "inputrec->awhParams->bias?->dim->period", dimIndex, dimp1->period, dimp2->period, ftol, abstol);
+ cmp_double(fp, "inputrec->awhParams->bias?->dim->diffusion", dimIndex, dimp1->diffusion, dimp2->diffusion, ftol, abstol);
+ cmp_double(fp, "inputrec->awhParams->bias?->dim->origin", dimIndex, dimp1->origin, dimp2->origin, ftol, abstol);
+ cmp_double(fp, "inputrec->awhParams->bias?->dim->end", dimIndex, dimp1->end, dimp2->end, ftol, abstol);
+ cmp_double(fp, "inputrec->awhParams->bias?->dim->coord_value_init", dimIndex, dimp1->coordValueInit, dimp2->coordValueInit, ftol, abstol);
+ cmp_double(fp, "inputrec->awhParams->bias?->dim->coverDiameter", dimIndex, dimp1->coverDiameter, dimp2->coverDiameter, ftol, abstol);
+}
+
+static void cmp_awhBiasParams(FILE *fp, const gmx::AwhBiasParams *bias1, const gmx::AwhBiasParams *bias2, int biasIndex, real ftol, real abstol)
+{
+ cmp_int(fp, "inputrec->awhParams->ndim", biasIndex, bias1->ndim, bias2->ndim);
+ cmp_int(fp, "inputrec->awhParams->biaseTarget", biasIndex, bias1->eTarget, bias2->eTarget);
+ cmp_double(fp, "inputrec->awhParams->biastargetBetaScaling", biasIndex, bias1->targetBetaScaling, bias2->targetBetaScaling, ftol, abstol);
+ cmp_double(fp, "inputrec->awhParams->biastargetCutoff", biasIndex, bias1->targetCutoff, bias2->targetCutoff, ftol, abstol);
+ cmp_int(fp, "inputrec->awhParams->biaseGrowth", biasIndex, bias1->eGrowth, bias2->eGrowth);
+ cmp_bool(fp, "inputrec->awhParams->biasbUserData", biasIndex, bias1->bUserData, bias2->bUserData);
+ cmp_double(fp, "inputrec->awhParams->biaserror_initial", biasIndex, bias1->errorInitial, bias2->errorInitial, ftol, abstol);
+ cmp_int(fp, "inputrec->awhParams->biasShareGroup", biasIndex, bias1->shareGroup, bias2->shareGroup);
+
+ for (int dim = 0; dim < std::min(bias1->ndim, bias2->ndim); dim++)
+ {
+ cmp_awhDimParams(fp, &bias1->dimParams[dim], &bias2->dimParams[dim], dim, ftol, abstol);
+ }
+}
+
+static void cmp_awhParams(FILE *fp, const gmx::AwhParams *awh1, const gmx::AwhParams *awh2, real ftol, real abstol)
+{
+ cmp_int(fp, "inputrec->awhParams->nbias", -1, awh1->numBias, awh2->numBias);
+ cmp_int64(fp, "inputrec->awhParams->seed", awh1->seed, awh2->seed);
+ cmp_int(fp, "inputrec->awhParams->nstout", -1, awh1->nstOut, awh2->nstOut);
+ cmp_int(fp, "inputrec->awhParams->nstsample_coord", -1, awh1->nstSampleCoord, awh2->nstSampleCoord);
+ cmp_int(fp, "inputrec->awhParams->nsamples_update_free_energy", -1, awh1->numSamplesUpdateFreeEnergy, awh2->numSamplesUpdateFreeEnergy);
+ cmp_int(fp, "inputrec->awhParams->ePotential", -1, awh1->ePotential, awh2->ePotential);
+ cmp_bool(fp, "inputrec->awhParams->shareBiasMultisim", -1, awh1->shareBiasMultisim, awh2->shareBiasMultisim);
+
+ if (awh1->numBias == awh2->numBias)
+ {
+ for (int bias = 0; bias < awh1->numBias; bias++)
+ {
+ cmp_awhBiasParams(fp, &awh1->awhBiasParams[bias], &awh2->awhBiasParams[bias], bias, ftol, abstol);
+ }
+ }
+}
+
static void cmp_simtempvals(FILE *fp, const t_simtemp *simtemp1, const t_simtemp *simtemp2, int n_lambda, real ftol, real abstol)
{
int i;
cmp_pull(fp);
}
+ cmp_bool(fp, "inputrec->bDoAwh", -1, ir1->bDoAwh, ir2->bDoAwh);
+ if (ir1->bDoAwh && ir2->bDoAwh)
+ {
+ cmp_awhParams(fp, ir1->awhParams, ir2->awhParams, ftol, abstol);
+ }
+
cmp_int(fp, "inputrec->eDisre", -1, ir1->eDisre, ir2->eDisre);
cmp_real(fp, "inputrec->dr_fc", -1, ir1->dr_fc, ir2->dr_fc, ftol, abstol);
cmp_int(fp, "inputrec->eDisreWeighting", -1, ir1->eDisreWeighting, ir2->eDisreWeighting);
#ifndef GMX_MDTYPES_INPUTREC_H
#define GMX_MDTYPES_INPUTREC_H
-#include <stdio.h>
+#include <cstdio>
+
+#include <memory>
#include "gromacs/math/vectypes.h"
#include "gromacs/mdtypes/md_enums.h"
namespace gmx
{
+class Awh;
+struct AwhParams;
class KeyValueTreeObject;
}
/* COM pulling data */
gmx_bool bPull; /* Do we do COM pulling? */
struct pull_params_t *pull; /* The data for center of mass pulling */
- struct pull_t *pull_work; /* The COM pull force calculation data structure; TODO this pointer should live somewhere else */
+ // TODO: Remove this by converting pull into a ForceProvider
+ struct pull_t *pull_work; /* The COM pull force calculation data structure */
+
+ /* AWH bias data */
+ gmx_bool bDoAwh; /* Use awh biasing for PMF calculations? */
+ gmx::AwhParams *awhParams; /* AWH biasing parameters */
+ // TODO: Remove this by converting AWH into a ForceProvider
+ gmx::Awh *awh; /* AWH work object */
/* Enforced rotation data */
gmx_bool bRot; /* Calculate enforced rotation potential(s)? */
#include "gromacs/math/paddedvector.h"
#include "gromacs/math/vec.h"
#include "gromacs/math/veccompare.h"
+#include "gromacs/mdtypes/awh-history.h"
#include "gromacs/mdtypes/df_history.h"
#include "gromacs/mdtypes/inputrec.h"
#include "gromacs/mdtypes/md_enums.h"
ekinstate(),
hist(),
dfhist(nullptr),
+ awhHistory(nullptr),
ddp_count(0),
ddp_count_cg_gl(0),
cg_gl()
#define GMX_MDTYPES_STATE_H
#include <array>
+#include <memory>
#include <vector>
#include "gromacs/math/paddedvector.h"
struct t_inputrec;
+namespace gmx
+{
+struct AwhHistory;
+}
+
/*
* The t_state struct should contain all the (possibly) non-static
* information required to define the state of the system.
ekinstate_t ekinstate; //!< The state of the kinetic energy
/* History for special algorithms, should be moved to a history struct */
- history_t hist; //!< Time history for restraints
- df_history_t *dfhist; //!< Free-energy history for free energy analysis
+ history_t hist; //!< Time history for restraints
+ df_history_t *dfhist; //!< Free-energy history for free energy analysis
+ std::shared_ptr<gmx::AwhHistory> awhHistory; //!< Accelerated weight histogram history
- int ddp_count; //!< The DD partitioning count for this state
- int ddp_count_cg_gl; //!< The DD partitioning count for index_gl
- std::vector<int> cg_gl; //!< The global cg number of the local cgs
+ int ddp_count; //!< The DD partitioning count for this state
+ int ddp_count_cg_gl; //!< The DD partitioning count for index_gl
+ std::vector<int> cg_gl; //!< The global cg number of the local cgs
};
#ifndef DOXYGEN
#include "gromacs/utility/real.h"
#include "gromacs/utility/smalloc.h"
-static bool pull_coordinate_is_angletype(const t_pull_coord *pcrd)
+bool pull_coordinate_is_angletype(const t_pull_coord *pcrd)
{
return (pcrd->eGeom == epullgANGLE ||
pcrd->eGeom == epullgDIHEDRAL ||
return dev;
}
-void get_pull_coord_value(struct pull_t *pull,
- int coord_ind,
- const t_pbc *pbc,
- double *value)
+double get_pull_coord_value(struct pull_t *pull,
+ int coord_ind,
+ const t_pbc *pbc)
{
get_pull_coord_distance(pull, coord_ind, pbc);
- *value = pull->coord[coord_ind].value;
+ return pull->coord[coord_ind].value;
}
static void clear_pull_forces_coord(pull_coord_work_t *pcrd)
* potential energy is added either to the pull term or to a term
* specific to the external module.
*/
-void apply_external_pull_coord_force(struct pull_t *pull,
- int coord_index,
- double coord_force,
- const t_mdatoms *mdatoms,
- rvec *force, tensor virial)
+void apply_external_pull_coord_force(struct pull_t *pull,
+ int coord_index,
+ double coord_force,
+ const t_mdatoms *mdatoms,
+ gmx::ForceWithVirial *forceWithVirial)
{
pull_coord_work_t *pcrd;
calc_pull_coord_vector_force(pcrd);
/* Add the forces for this coordinate to the total virial and force */
- add_virial_coord(virial, pcrd);
+ if (forceWithVirial->computeVirial_)
+ {
+ matrix virial = { { 0 } };
+ add_virial_coord(virial, pcrd);
+ forceWithVirial->addVirialContribution(virial);
+ }
- apply_forces_coord(pull, coord_index, mdatoms, force);
+ apply_forces_coord(pull, coord_index, mdatoms,
+ as_rvec_array(forceWithVirial->force_.data()));
}
pull->numExternalPotentialsStillToBeAppliedThisStep--;
class ForceWithVirial;
}
+/*! \brief Returns if the pull coordinate is an angle
+ *
+ * \param[in] pcrd The pull coordinate to query the type for.
+ * \returns a boolean telling if the coordinate is of angle type.
+ */
+bool pull_coordinate_is_angletype(const t_pull_coord *pcrd);
+
/*! \brief Returns the units of the pull coordinate.
*
* \param[in] pcrd The pull coordinate to query the units for.
* \param[in,out] pull The pull struct.
* \param[in] coord_ind Number of the pull coordinate.
* \param[in] pbc Information structure about periodicity.
- * \param[out] value The value of the pull coordinate.
+ * \returns the value of the pull coordinate.
*/
-void get_pull_coord_value(struct pull_t *pull,
- int coord_ind,
- const struct t_pbc *pbc,
- double *value);
-
+double get_pull_coord_value(struct pull_t *pull,
+ int coord_ind,
+ const struct t_pbc *pbc);
/*! \brief Registers the provider of an external potential for a coordinate.
*
* This function should be called after pull_potential has been called and,
* obviously, before the coordinates are updated uses the forces.
*
- * \param[in,out] pull The pull struct.
- * \param[in] coord_index The pull coordinate index to set the force for.
- * \param[in] coord_force The scalar force for the pull coordinate.
- * \param[in] mdatoms Atom properties, only masses are used.
- * \param[in,out] force The force buffer.
- * \param[in,out] virial The virial, can be NULL.
+ * \param[in,out] pull The pull struct.
+ * \param[in] coord_index The pull coordinate index to set the force for.
+ * \param[in] coord_force The scalar force for the pull coordinate.
+ * \param[in] mdatoms Atom properties, only masses are used.
+ * \param[in,out] forceWithVirial Force and virial buffers.
*/
-void apply_external_pull_coord_force(struct pull_t *pull,
- int coord_index,
- double coord_force,
- const t_mdatoms *mdatoms,
- rvec *force,
- tensor virial);
+void apply_external_pull_coord_force(struct pull_t *pull,
+ int coord_index,
+ double coord_force,
+ const t_mdatoms *mdatoms,
+ gmx::ForceWithVirial *forceWithVirial);
/*! \brief Set the all the pull forces to zero.
/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2015,2016, by the GROMACS development team, led by
+ * Copyright (c) 2015,2016,2017, 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.
Thermostat = 0x00005000, //!< Stochastic temperature coupling
Barostat = 0x00006000, //!< Stochastic pressure coupling
ReplicaExchange = 0x00007000, //!< Replica exchange metropolis moves
- ExpandedEnsemble = 0x00008000 //!< Expanded ensemble lambda moves
+ ExpandedEnsemble = 0x00008000, //!< Expanded ensemble lambda moves
+ AwhBiasing = 0x00009000 //!< AWH biasing reference value moves
};
} // namespace gmx
"PME wait for PP", "Wait + Recv. PME F",
"Wait PME GPU spread", "Wait PME GPU gather", "Reduce GPU PME F",
"Wait GPU NB nonloc.", "Wait GPU NB local", "NB X/F buffer ops.",
- "Vsite spread", "COM pull force",
+ "Vsite spread", "COM pull force", "AWH",
"Write traj.", "Update", "Constraints", "Comm. energies",
"Enforced rotation", "Add rot. forces", "Position swapping", "IMD", "Test"
};
ewcPMEWAITCOMM, ewcPP_PMEWAITRECVF,
ewcWAIT_GPU_PME_SPREAD, ewcWAIT_GPU_PME_GATHER, ewcPME_GPU_F_REDUCTION,
ewcWAIT_GPU_NB_NL, ewcWAIT_GPU_NB_L, ewcNB_XF_BUF_OPS,
- ewcVSITESPREAD, ewcPULLPOT,
+ ewcVSITESPREAD, ewcPULLPOT, ewcAWH,
ewcTRAJ, ewcUPDATE, ewcCONSTR, ewcMoveE, ewcROT, ewcROTadd, ewcSWAP, ewcIMD,
ewcTEST, ewcNR
};
"Quantifying Artifacts in Ewald Simulations of Inhomogeneous Systems with a Net Charge",
"J. Chem. Theory Comput.",
10, 2014, "381-393" },
+ { "Lindahl2014",
+ "V. Lindahl, J. Lidmar, B. Hess",
+ "Accelerated weight histogram method for exploring free energy landscapes",
+ "J. Chem. Phys.",
+ 141, 2014, "044110" },
};
#define NSTR (int)asize(citedb)
#include "thread_mpi/threads.h"
+#include "gromacs/awh/awh.h"
#include "gromacs/commandline/filenm.h"
#include "gromacs/domdec/domdec.h"
#include "gromacs/domdec/domdec_network.h"
#include "gromacs/mdlib/update.h"
#include "gromacs/mdlib/vcm.h"
#include "gromacs/mdlib/vsite.h"
+#include "gromacs/mdtypes/awh-history.h"
#include "gromacs/mdtypes/commrec.h"
#include "gromacs/mdtypes/df_history.h"
#include "gromacs/mdtypes/energyhistory.h"
{
gmx_fatal(FARGS, "Shell particles are not implemented with domain decomposition, use a single rank");
}
+ if (shellfc && ir->bDoAwh)
+ {
+ gmx_fatal(FARGS, "AWH biasing does not support shell particles.");
+ }
if (inputrecDeform(ir))
{
set_constraints(constr, top, ir, mdatoms, cr);
}
+ /* Initialize AWH and restore state from history in checkpoint if needed. */
+ if (ir->bDoAwh)
+ {
+ ir->awh = new gmx::Awh(fplog, *ir, cr, *ir->awhParams, opt2fn("-awh", nfile, fnm), ir->pull_work);
+
+ if (startingFromCheckpoint)
+ {
+ /* Restore the AWH history read from checkpoint */
+ ir->awh->restoreStateFromHistory(MASTER(cr) ? state_global->awhHistory.get() : nullptr);
+ }
+ else if (MASTER(cr))
+ {
+ /* Initialize the AWH history here */
+ state_global->awhHistory = ir->awh->initHistoryFromState();
+ }
+ }
+
const bool useReplicaExchange = (replExParams.exchangeInterval > 0);
if (useReplicaExchange && MASTER(cr))
{
/* To minimize communication, compute_globals computes the COM velocity
* and the kinetic energy for the velocities without COM motion removed.
* Thus to get the kinetic energy without the COM contribution, we need
- * to calls compute_globals twice.
+ * to call compute_globals twice.
*/
for (int cgloIteration = 0; cgloIteration < (bStopCM ? 2 : 1); cgloIteration++)
{
}
else
{
+ /* The AWH history need to be saved _before_ doing force calculations where the AWH bias is updated
+ (or the AWH update will be performed twice for one step when continuing). It would be best to
+ call this update function from do_md_trajectory_writing but that would occur after do_force.
+ One would have to divide the update_awh function into one function applying the AWH force
+ and one doing the AWH bias update. The update AWH bias function could then be called after
+ do_md_trajectory_writing (then containing update_awh_history).
+ The checkpointing will in the future probably moved to the start of the md loop which will
+ rid of this issue. */
+ if (ir->bDoAwh && bCPT && MASTER(cr))
+ {
+ ir->awh->updateHistory(state_global->awhHistory.get());
+ }
+
/* The coordinates (x) are shifted (to get whole molecules)
* in do_force.
* This is parallellized as well, and does communication too.
print_replica_exchange_statistics(fplog, repl_ex);
}
+ if (ir->bDoAwh)
+ {
+ delete ir->awh;
+ }
+
// Clean up swapcoords
if (ir->eSwapCoords != eswapNO)
{
{ efLOG, "-rt", "rottorque", ffOPTWR },
{ efMTX, "-mtx", "nm", ffOPTWR },
{ efRND, "-multidir", nullptr, ffOPTRDMULT},
+ { efXVG, "-awh", "awhinit", ffOPTRD },
{ efDAT, "-membed", "membed", ffOPTRD },
{ efTOP, "-mp", "membed", ffOPTRD },
{ efNDX, "-mn", "membed", ffOPTRD },