356cb1ac2bf79de88d42c46ed3157e77e480a1e3
[alexxy/gromacs.git] / src / gromacs / applied_forces / awh / biasstate.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35
36 /*! \internal \file
37  * \brief
38  * Implements the BiasState class.
39  *
40  * \author Viveca Lindahl
41  * \author Berk Hess <hess@kth.se>
42  * \ingroup module_awh
43  */
44
45 #include "gmxpre.h"
46
47 #include "biasstate.h"
48
49 #include <cassert>
50 #include <cmath>
51 #include <cstdio>
52 #include <cstdlib>
53 #include <cstring>
54
55 #include <algorithm>
56 #include <optional>
57
58 #include "gromacs/fileio/gmxfio.h"
59 #include "gromacs/fileio/xvgr.h"
60 #include "gromacs/gmxlib/network.h"
61 #include "gromacs/math/utilities.h"
62 #include "gromacs/mdrunutility/multisim.h"
63 #include "gromacs/mdtypes/awh_history.h"
64 #include "gromacs/mdtypes/awh_params.h"
65 #include "gromacs/mdtypes/commrec.h"
66 #include "gromacs/simd/simd.h"
67 #include "gromacs/simd/simd_math.h"
68 #include "gromacs/utility/arrayref.h"
69 #include "gromacs/utility/exceptions.h"
70 #include "gromacs/utility/gmxassert.h"
71 #include "gromacs/utility/smalloc.h"
72 #include "gromacs/utility/stringutil.h"
73
74 #include "biasgrid.h"
75 #include "pointstate.h"
76
77 namespace gmx
78 {
79
80 void BiasState::getPmf(gmx::ArrayRef<float> pmf) const
81 {
82     GMX_ASSERT(pmf.size() == points_.size(), "pmf should have the size of the bias grid");
83
84     /* The PMF is just the negative of the log of the sampled PMF histogram.
85      * Points with zero target weight are ignored, they will mostly contain noise.
86      */
87     for (size_t i = 0; i < points_.size(); i++)
88     {
89         pmf[i] = points_[i].inTargetRegion() ? -points_[i].logPmfSum() : GMX_FLOAT_MAX;
90     }
91 }
92
93 namespace
94 {
95
96 /*! \brief
97  * Sum an array over all simulations on the master rank of each simulation.
98  *
99  * \param[in,out] arrayRef      The data to sum.
100  * \param[in]     multiSimComm  Struct for multi-simulation communication.
101  */
102 void sumOverSimulations(gmx::ArrayRef<int> arrayRef, const gmx_multisim_t* multiSimComm)
103 {
104     gmx_sumi_sim(arrayRef.size(), arrayRef.data(), multiSimComm);
105 }
106
107 /*! \brief
108  * Sum an array over all simulations on the master rank of each simulation.
109  *
110  * \param[in,out] arrayRef      The data to sum.
111  * \param[in]     multiSimComm  Struct for multi-simulation communication.
112  */
113 void sumOverSimulations(gmx::ArrayRef<double> arrayRef, const gmx_multisim_t* multiSimComm)
114 {
115     gmx_sumd_sim(arrayRef.size(), arrayRef.data(), multiSimComm);
116 }
117
118 /*! \brief
119  * Sum an array over all simulations on all ranks of each simulation.
120  *
121  * This assumes the data is identical on all ranks within each simulation.
122  *
123  * \param[in,out] arrayRef      The data to sum.
124  * \param[in]     commRecord    Struct for intra-simulation communication.
125  * \param[in]     multiSimComm  Struct for multi-simulation communication.
126  */
127 template<typename T>
128 void sumOverSimulations(gmx::ArrayRef<T> arrayRef, const t_commrec* commRecord, const gmx_multisim_t* multiSimComm)
129 {
130     if (MASTER(commRecord))
131     {
132         sumOverSimulations(arrayRef, multiSimComm);
133     }
134     if (commRecord->nnodes > 1)
135     {
136         gmx_bcast(arrayRef.size() * sizeof(T), arrayRef.data(), commRecord->mpi_comm_mygroup);
137     }
138 }
139
140 /*! \brief
141  * Sum PMF over multiple simulations, when requested.
142  *
143  * \param[in,out] pointState         The state of the points in the bias.
144  * \param[in]     numSharedUpdate    The number of biases sharing the histogram.
145  * \param[in]     commRecord         Struct for intra-simulation communication.
146  * \param[in]     multiSimComm       Struct for multi-simulation communication.
147  */
148 void sumPmf(gmx::ArrayRef<PointState> pointState,
149             int                       numSharedUpdate,
150             const t_commrec*          commRecord,
151             const gmx_multisim_t*     multiSimComm)
152 {
153     if (numSharedUpdate == 1)
154     {
155         return;
156     }
157     GMX_ASSERT(multiSimComm != nullptr && numSharedUpdate % multiSimComm->numSimulations_ == 0,
158                "numSharedUpdate should be a multiple of multiSimComm->numSimulations_");
159     GMX_ASSERT(numSharedUpdate == multiSimComm->numSimulations_,
160                "Sharing within a simulation is not implemented (yet)");
161
162     std::vector<double> buffer(pointState.size());
163
164     /* Need to temporarily exponentiate the log weights to sum over simulations */
165     for (size_t i = 0; i < buffer.size(); i++)
166     {
167         buffer[i] = pointState[i].inTargetRegion() ? std::exp(-pointState[i].logPmfSum()) : 0;
168     }
169
170     sumOverSimulations(gmx::ArrayRef<double>(buffer), commRecord, multiSimComm);
171
172     /* Take log again to get (non-normalized) PMF */
173     double normFac = 1.0 / numSharedUpdate;
174     for (gmx::index i = 0; i < pointState.ssize(); i++)
175     {
176         if (pointState[i].inTargetRegion())
177         {
178             pointState[i].setLogPmfSum(-std::log(buffer[i] * normFac));
179         }
180     }
181 }
182
183 /*! \brief
184  * Find the minimum free energy value.
185  *
186  * \param[in] pointState  The state of the points.
187  * \returns the minimum free energy value.
188  */
189 double freeEnergyMinimumValue(gmx::ArrayRef<const PointState> pointState)
190 {
191     double fMin = GMX_FLOAT_MAX;
192
193     for (auto const& ps : pointState)
194     {
195         if (ps.inTargetRegion() && ps.freeEnergy() < fMin)
196         {
197             fMin = ps.freeEnergy();
198         }
199     }
200
201     return fMin;
202 }
203
204 /*! \brief
205  * Find and return the log of the probability weight of a point given a coordinate value.
206  *
207  * The unnormalized weight is given by
208  * w(point|value) = exp(bias(point) - U(value,point)),
209  * where U is a harmonic umbrella potential.
210  *
211  * \param[in] dimParams              The bias dimensions parameters
212  * \param[in] points                 The point state.
213  * \param[in] grid                   The grid.
214  * \param[in] pointIndex             Point to evaluate probability weight for.
215  * \param[in] pointBias              Bias for the point (as a log weight).
216  * \param[in] value                  Coordinate value.
217  * \param[in] neighborLambdaEnergies The energy of the system in neighboring lambdas states. Can be
218  * empty when there are no free energy lambda state dimensions.
219  * \param[in] gridpointIndex         The index of the current grid point.
220  * \returns the log of the biased probability weight.
221  */
222 double biasedLogWeightFromPoint(const std::vector<DimParams>&  dimParams,
223                                 const std::vector<PointState>& points,
224                                 const BiasGrid&                grid,
225                                 int                            pointIndex,
226                                 double                         pointBias,
227                                 const awh_dvec                 value,
228                                 gmx::ArrayRef<const double>    neighborLambdaEnergies,
229                                 int                            gridpointIndex)
230 {
231     double logWeight = detail::c_largeNegativeExponent;
232
233     /* Only points in the target region have non-zero weight */
234     if (points[pointIndex].inTargetRegion())
235     {
236         logWeight = pointBias;
237
238         /* Add potential for all parameter dimensions */
239         for (size_t d = 0; d < dimParams.size(); d++)
240         {
241             if (dimParams[d].isFepLambdaDimension())
242             {
243                 /* If this is not a sampling step or if this function is called from
244                  * calcConvolvedBias(), when writing energy subblocks, neighborLambdaEnergies will
245                  * be empty. No convolution is required along the lambda dimension. */
246                 if (!neighborLambdaEnergies.empty())
247                 {
248                     const int pointLambdaIndex     = grid.point(pointIndex).coordValue[d];
249                     const int gridpointLambdaIndex = grid.point(gridpointIndex).coordValue[d];
250                     logWeight -= dimParams[d].beta
251                                  * (neighborLambdaEnergies[pointLambdaIndex]
252                                     - neighborLambdaEnergies[gridpointLambdaIndex]);
253                 }
254             }
255             else
256             {
257                 double dev = getDeviationFromPointAlongGridAxis(grid, d, pointIndex, value[d]);
258                 logWeight -= 0.5 * dimParams[d].betak * dev * dev;
259             }
260         }
261     }
262     return logWeight;
263 }
264
265 /*! \brief
266  * Calculates the marginal distribution (marginal probability) for each value along
267  * a free energy lambda axis.
268  * The marginal distribution of one coordinate dimension value is the sum of the probability
269  * distribution of all values (herein all neighbor values) with the same value in the dimension
270  * of interest.
271  * \param[in] grid               The bias grid.
272  * \param[in] neighbors          The points to use for the calculation of the marginal distribution.
273  * \param[in] probWeightNeighbor Probability weights of the neighbors.
274  * \returns The calculated marginal distribution in a 1D array with
275  * as many elements as there are points along the axis of interest.
276  */
277 std::vector<double> calculateFELambdaMarginalDistribution(const BiasGrid&          grid,
278                                                           gmx::ArrayRef<const int> neighbors,
279                                                           gmx::ArrayRef<const double> probWeightNeighbor)
280 {
281     const std::optional<int> lambdaAxisIndex = grid.lambdaAxisIndex();
282     GMX_RELEASE_ASSERT(lambdaAxisIndex,
283                        "There must be a free energy lambda axis in order to calculate the free "
284                        "energy lambda marginal distribution.");
285     const int           numFepLambdaStates = grid.numFepLambdaStates();
286     std::vector<double> lambdaMarginalDistribution(numFepLambdaStates, 0);
287
288     for (size_t i = 0; i < neighbors.size(); i++)
289     {
290         const int neighbor    = neighbors[i];
291         const int lambdaState = grid.point(neighbor).coordValue[lambdaAxisIndex.value()];
292         lambdaMarginalDistribution[lambdaState] += probWeightNeighbor[i];
293     }
294     return lambdaMarginalDistribution;
295 }
296
297 } // namespace
298
299 void BiasState::calcConvolvedPmf(const std::vector<DimParams>& dimParams,
300                                  const BiasGrid&               grid,
301                                  std::vector<float>*           convolvedPmf) const
302 {
303     size_t numPoints = grid.numPoints();
304
305     convolvedPmf->resize(numPoints);
306
307     /* Get the PMF to convolve. */
308     std::vector<float> pmf(numPoints);
309     getPmf(pmf);
310
311     for (size_t m = 0; m < numPoints; m++)
312     {
313         double           freeEnergyWeights = 0;
314         const GridPoint& point             = grid.point(m);
315         for (auto& neighbor : point.neighbor)
316         {
317             /* The negative PMF is a positive bias. */
318             double biasNeighbor = -pmf[neighbor];
319
320             /* Add the convolved PMF weights for the neighbors of this point.
321                Note that this function only adds point within the target > 0 region.
322                Sum weights, take the logarithm last to get the free energy. */
323             double logWeight = biasedLogWeightFromPoint(dimParams, points_, grid, neighbor,
324                                                         biasNeighbor, point.coordValue, {}, m);
325             freeEnergyWeights += std::exp(logWeight);
326         }
327
328         GMX_RELEASE_ASSERT(freeEnergyWeights > 0,
329                            "Attempting to do log(<= 0) in AWH convolved PMF calculation.");
330         (*convolvedPmf)[m] = -std::log(static_cast<float>(freeEnergyWeights));
331     }
332 }
333
334 namespace
335 {
336
337 /*! \brief
338  * Updates the target distribution for all points.
339  *
340  * The target distribution is always updated for all points
341  * at the same time.
342  *
343  * \param[in,out] pointState  The state of all points.
344  * \param[in]     params      The bias parameters.
345  */
346 void updateTargetDistribution(gmx::ArrayRef<PointState> pointState, const BiasParams& params)
347 {
348     double freeEnergyCutoff = 0;
349     if (params.eTarget == eawhtargetCUTOFF)
350     {
351         freeEnergyCutoff = freeEnergyMinimumValue(pointState) + params.freeEnergyCutoffInKT;
352     }
353
354     double sumTarget = 0;
355     for (PointState& ps : pointState)
356     {
357         sumTarget += ps.updateTargetWeight(params, freeEnergyCutoff);
358     }
359     GMX_RELEASE_ASSERT(sumTarget > 0, "We should have a non-zero distribution");
360
361     /* Normalize to 1 */
362     double invSum = 1.0 / sumTarget;
363     for (PointState& ps : pointState)
364     {
365         ps.scaleTarget(invSum);
366     }
367 }
368
369 /*! \brief
370  * Puts together a string describing a grid point.
371  *
372  * \param[in] grid         The grid.
373  * \param[in] point        BiasGrid point index.
374  * \returns a string for the point.
375  */
376 std::string gridPointValueString(const BiasGrid& grid, int point)
377 {
378     std::string pointString;
379
380     pointString += "(";
381
382     for (int d = 0; d < grid.numDimensions(); d++)
383     {
384         pointString += gmx::formatString("%g", grid.point(point).coordValue[d]);
385         if (d < grid.numDimensions() - 1)
386         {
387             pointString += ",";
388         }
389         else
390         {
391             pointString += ")";
392         }
393     }
394
395     return pointString;
396 }
397
398 } // namespace
399
400 int BiasState::warnForHistogramAnomalies(const BiasGrid& grid, int biasIndex, double t, FILE* fplog, int maxNumWarnings) const
401 {
402     GMX_ASSERT(fplog != nullptr, "Warnings can only be issued if there is log file.");
403     const double maxHistogramRatio = 0.5; /* Tolerance for printing a warning about the histogram ratios */
404
405     /* Sum up the histograms and get their normalization */
406     double sumVisits  = 0;
407     double sumWeights = 0;
408     for (auto& pointState : points_)
409     {
410         if (pointState.inTargetRegion())
411         {
412             sumVisits += pointState.numVisitsTot();
413             sumWeights += pointState.weightSumTot();
414         }
415     }
416     GMX_RELEASE_ASSERT(sumVisits > 0, "We should have visits");
417     GMX_RELEASE_ASSERT(sumWeights > 0, "We should have weight");
418     double invNormVisits = 1.0 / sumVisits;
419     double invNormWeight = 1.0 / sumWeights;
420
421     /* Check all points for warnings */
422     int    numWarnings = 0;
423     size_t numPoints   = grid.numPoints();
424     for (size_t m = 0; m < numPoints; m++)
425     {
426         /* Skip points close to boundary or non-target region */
427         const GridPoint& gridPoint = grid.point(m);
428         bool             skipPoint = false;
429         for (size_t n = 0; (n < gridPoint.neighbor.size()) && !skipPoint; n++)
430         {
431             int neighbor = gridPoint.neighbor[n];
432             skipPoint    = !points_[neighbor].inTargetRegion();
433             for (int d = 0; (d < grid.numDimensions()) && !skipPoint; d++)
434             {
435                 const GridPoint& neighborPoint = grid.point(neighbor);
436                 skipPoint                      = neighborPoint.index[d] == 0
437                             || neighborPoint.index[d] == grid.axis(d).numPoints() - 1;
438             }
439         }
440
441         /* Warn if the coordinate distribution is less than the target distribution with a certain fraction somewhere */
442         const double relativeWeight = points_[m].weightSumTot() * invNormWeight;
443         const double relativeVisits = points_[m].numVisitsTot() * invNormVisits;
444         if (!skipPoint && relativeVisits < relativeWeight * maxHistogramRatio)
445         {
446             std::string pointValueString = gridPointValueString(grid, m);
447             std::string warningMessage   = gmx::formatString(
448                     "\nawh%d warning: "
449                     "at t = %g ps the obtained coordinate distribution at coordinate value %s "
450                     "is less than a fraction %g of the reference distribution at that point. "
451                     "If you are not certain about your settings you might want to increase your "
452                     "pull force constant or "
453                     "modify your sampling region.\n",
454                     biasIndex + 1, t, pointValueString.c_str(), maxHistogramRatio);
455             gmx::TextLineWrapper wrapper;
456             wrapper.settings().setLineLength(c_linewidth);
457             fprintf(fplog, "%s", wrapper.wrapToString(warningMessage).c_str());
458
459             numWarnings++;
460         }
461         if (numWarnings >= maxNumWarnings)
462         {
463             break;
464         }
465     }
466
467     return numWarnings;
468 }
469
470 double BiasState::calcUmbrellaForceAndPotential(const std::vector<DimParams>& dimParams,
471                                                 const BiasGrid&               grid,
472                                                 int                           point,
473                                                 ArrayRef<const double>        neighborLambdaDhdl,
474                                                 gmx::ArrayRef<double>         force) const
475 {
476     double potential = 0;
477     for (size_t d = 0; d < dimParams.size(); d++)
478     {
479         if (dimParams[d].isFepLambdaDimension())
480         {
481             if (!neighborLambdaDhdl.empty())
482             {
483                 const int coordpointLambdaIndex = grid.point(point).coordValue[d];
484                 force[d]                        = neighborLambdaDhdl[coordpointLambdaIndex];
485                 /* The potential should not be affected by the lambda dimension. */
486             }
487         }
488         else
489         {
490             double deviation =
491                     getDeviationFromPointAlongGridAxis(grid, d, point, coordState_.coordValue()[d]);
492             double k = dimParams[d].k;
493
494             /* Force from harmonic potential 0.5*k*dev^2 */
495             force[d] = -k * deviation;
496             potential += 0.5 * k * deviation * deviation;
497         }
498     }
499
500     return potential;
501 }
502
503 void BiasState::calcConvolvedForce(const std::vector<DimParams>& dimParams,
504                                    const BiasGrid&               grid,
505                                    gmx::ArrayRef<const double>   probWeightNeighbor,
506                                    ArrayRef<const double>        neighborLambdaDhdl,
507                                    gmx::ArrayRef<double>         forceWorkBuffer,
508                                    gmx::ArrayRef<double>         force) const
509 {
510     for (size_t d = 0; d < dimParams.size(); d++)
511     {
512         force[d] = 0;
513     }
514
515     /* Only neighboring points have non-negligible contribution. */
516     const std::vector<int>& neighbor          = grid.point(coordState_.gridpointIndex()).neighbor;
517     gmx::ArrayRef<double>   forceFromNeighbor = forceWorkBuffer;
518     for (size_t n = 0; n < neighbor.size(); n++)
519     {
520         double weightNeighbor = probWeightNeighbor[n];
521         int    indexNeighbor  = neighbor[n];
522
523         /* Get the umbrella force from this point. The returned potential is ignored here. */
524         calcUmbrellaForceAndPotential(dimParams, grid, indexNeighbor, neighborLambdaDhdl, forceFromNeighbor);
525
526         /* Add the weighted umbrella force to the convolved force. */
527         for (size_t d = 0; d < dimParams.size(); d++)
528         {
529             force[d] += forceFromNeighbor[d] * weightNeighbor;
530         }
531     }
532 }
533
534 double BiasState::moveUmbrella(const std::vector<DimParams>& dimParams,
535                                const BiasGrid&               grid,
536                                gmx::ArrayRef<const double>   probWeightNeighbor,
537                                ArrayRef<const double>        neighborLambdaDhdl,
538                                gmx::ArrayRef<double>         biasForce,
539                                int64_t                       step,
540                                int64_t                       seed,
541                                int                           indexSeed,
542                                bool                          onlySampleUmbrellaGridpoint)
543 {
544     /* Generate and set a new coordinate reference value */
545     coordState_.sampleUmbrellaGridpoint(grid, coordState_.gridpointIndex(), probWeightNeighbor,
546                                         step, seed, indexSeed);
547
548     if (onlySampleUmbrellaGridpoint)
549     {
550         return 0;
551     }
552
553     std::vector<double> newForce(dimParams.size());
554     double              newPotential = calcUmbrellaForceAndPotential(
555             dimParams, grid, coordState_.umbrellaGridpoint(), neighborLambdaDhdl, newForce);
556
557     /*  A modification of the reference value at time t will lead to a different
558         force over t-dt/2 to t and over t to t+dt/2. For high switching rates
559         this means the force and velocity will change signs roughly as often.
560         To avoid any issues we take the average of the previous and new force
561         at steps when the reference value has been moved. E.g. if the ref. value
562         is set every step to (coord dvalue +/- delta) would give zero force.
563      */
564     for (gmx::index d = 0; d < biasForce.ssize(); d++)
565     {
566         /* Average of the current and new force */
567         biasForce[d] = 0.5 * (biasForce[d] + newForce[d]);
568     }
569
570     return newPotential;
571 }
572
573 namespace
574 {
575
576 /*! \brief
577  * Sets the histogram rescaling factors needed to control the histogram size.
578  *
579  * For sake of robustness, the reference weight histogram can grow at a rate
580  * different from the actual sampling rate. Typically this happens for a limited
581  * initial time, alternatively growth is scaled down by a constant factor for all
582  * times. Since the size of the reference histogram sets the size of the free
583  * energy update this should be reflected also in the PMF. Thus the PMF histogram
584  * needs to be rescaled too.
585  *
586  * This function should only be called by the bias update function or wrapped by a function that
587  * knows what scale factors should be applied when, e.g,
588  * getSkippedUpdateHistogramScaleFactors().
589  *
590  * \param[in]  params             The bias parameters.
591  * \param[in]  newHistogramSize   New reference weight histogram size.
592  * \param[in]  oldHistogramSize   Previous reference weight histogram size (before adding new samples).
593  * \param[out] weightHistScaling  Scaling factor for the reference weight histogram.
594  * \param[out] logPmfSumScaling   Log of the scaling factor for the PMF histogram.
595  */
596 void setHistogramUpdateScaleFactors(const BiasParams& params,
597                                     double            newHistogramSize,
598                                     double            oldHistogramSize,
599                                     double*           weightHistScaling,
600                                     double*           logPmfSumScaling)
601 {
602
603     /* The two scaling factors below are slightly different (ignoring the log factor) because the
604        reference and the PMF histogram apply weight scaling differently. The weight histogram
605        applies is  locally, i.e. each sample is scaled down meaning all samples get equal weight.
606        It is done this way because that is what target type local Boltzmann (for which
607        target = weight histogram) needs. In contrast, the PMF histogram is rescaled globally
608        by repeatedly scaling down the whole histogram. The reasons for doing it this way are:
609        1) empirically this is necessary for converging the PMF; 2) since the extraction of
610        the PMF is theoretically only valid for a constant bias, new samples should get more
611        weight than old ones for which the bias is fluctuating more. */
612     *weightHistScaling =
613             newHistogramSize / (oldHistogramSize + params.updateWeight * params.localWeightScaling);
614     *logPmfSumScaling = std::log(newHistogramSize / (oldHistogramSize + params.updateWeight));
615 }
616
617 } // namespace
618
619 void BiasState::getSkippedUpdateHistogramScaleFactors(const BiasParams& params,
620                                                       double*           weightHistScaling,
621                                                       double*           logPmfSumScaling) const
622 {
623     GMX_ASSERT(params.skipUpdates(),
624                "Calling function for skipped updates when skipping updates is not allowed");
625
626     if (inInitialStage())
627     {
628         /* In between global updates the reference histogram size is kept constant so we trivially
629            know what the histogram size was at the time of the skipped update. */
630         double histogramSize = histogramSize_.histogramSize();
631         setHistogramUpdateScaleFactors(params, histogramSize, histogramSize, weightHistScaling,
632                                        logPmfSumScaling);
633     }
634     else
635     {
636         /* In the final stage, the reference histogram grows at the sampling rate which gives trivial scale factors. */
637         *weightHistScaling = 1;
638         *logPmfSumScaling  = 0;
639     }
640 }
641
642 void BiasState::doSkippedUpdatesForAllPoints(const BiasParams& params)
643 {
644     double weightHistScaling;
645     double logPmfsumScaling;
646
647     getSkippedUpdateHistogramScaleFactors(params, &weightHistScaling, &logPmfsumScaling);
648
649     for (auto& pointState : points_)
650     {
651         bool didUpdate = pointState.performPreviouslySkippedUpdates(
652                 params, histogramSize_.numUpdates(), weightHistScaling, logPmfsumScaling);
653
654         /* Update the bias for this point only if there were skipped updates in the past to avoid calculating the log unneccessarily */
655         if (didUpdate)
656         {
657             pointState.updateBias();
658         }
659     }
660 }
661
662 void BiasState::doSkippedUpdatesInNeighborhood(const BiasParams& params, const BiasGrid& grid)
663 {
664     double weightHistScaling;
665     double logPmfsumScaling;
666
667     getSkippedUpdateHistogramScaleFactors(params, &weightHistScaling, &logPmfsumScaling);
668
669     /* For each neighbor point of the center point, refresh its state by adding the results of all past, skipped updates. */
670     const std::vector<int>& neighbors = grid.point(coordState_.gridpointIndex()).neighbor;
671     for (auto& neighbor : neighbors)
672     {
673         bool didUpdate = points_[neighbor].performPreviouslySkippedUpdates(
674                 params, histogramSize_.numUpdates(), weightHistScaling, logPmfsumScaling);
675
676         if (didUpdate)
677         {
678             points_[neighbor].updateBias();
679         }
680     }
681 }
682
683 namespace
684 {
685
686 /*! \brief
687  * Merge update lists from multiple sharing simulations.
688  *
689  * \param[in,out] updateList    Update list for this simulation (assumed >= npoints long).
690  * \param[in]     numPoints     Total number of points.
691  * \param[in]     commRecord    Struct for intra-simulation communication.
692  * \param[in]     multiSimComm  Struct for multi-simulation communication.
693  */
694 void mergeSharedUpdateLists(std::vector<int>*     updateList,
695                             int                   numPoints,
696                             const t_commrec*      commRecord,
697                             const gmx_multisim_t* multiSimComm)
698 {
699     std::vector<int> numUpdatesOfPoint;
700
701     /* Flag the update points of this sim.
702        TODO: we can probably avoid allocating this array and just use the input array. */
703     numUpdatesOfPoint.resize(numPoints, 0);
704     for (auto& pointIndex : *updateList)
705     {
706         numUpdatesOfPoint[pointIndex] = 1;
707     }
708
709     /* Sum over the sims to get all the flagged points */
710     sumOverSimulations(arrayRefFromArray(numUpdatesOfPoint.data(), numPoints), commRecord, multiSimComm);
711
712     /* Collect the indices of the flagged points in place. The resulting array will be the merged update list.*/
713     updateList->clear();
714     for (int m = 0; m < numPoints; m++)
715     {
716         if (numUpdatesOfPoint[m] > 0)
717         {
718             updateList->push_back(m);
719         }
720     }
721 }
722
723 /*! \brief
724  * Generate an update list of points sampled since the last update.
725  *
726  * \param[in] grid              The AWH bias.
727  * \param[in] points            The point state.
728  * \param[in] originUpdatelist  The origin of the rectangular region that has been sampled since
729  * last update. \param[in] endUpdatelist     The end of the rectangular that has been sampled since
730  * last update. \param[in,out] updateList    Local update list to set (assumed >= npoints long).
731  */
732 void makeLocalUpdateList(const BiasGrid&                grid,
733                          const std::vector<PointState>& points,
734                          const awh_ivec                 originUpdatelist,
735                          const awh_ivec                 endUpdatelist,
736                          std::vector<int>*              updateList)
737 {
738     awh_ivec origin;
739     awh_ivec numPoints;
740
741     /* Define the update search grid */
742     for (int d = 0; d < grid.numDimensions(); d++)
743     {
744         origin[d]    = originUpdatelist[d];
745         numPoints[d] = endUpdatelist[d] - originUpdatelist[d] + 1;
746
747         /* Because the end_updatelist is unwrapped it can be > (npoints - 1) so that numPoints can be > npoints in grid.
748            This helps for calculating the distance/number of points but should be removed and fixed when the way of
749            updating origin/end updatelist is changed (see sampleProbabilityWeights). */
750         numPoints[d] = std::min(grid.axis(d).numPoints(), numPoints[d]);
751     }
752
753     /* Make the update list */
754     updateList->clear();
755     int  pointIndex  = -1;
756     bool pointExists = true;
757     while (pointExists)
758     {
759         pointExists = advancePointInSubgrid(grid, origin, numPoints, &pointIndex);
760
761         if (pointExists && points[pointIndex].inTargetRegion())
762         {
763             updateList->push_back(pointIndex);
764         }
765     }
766 }
767
768 } // namespace
769
770 void BiasState::resetLocalUpdateRange(const BiasGrid& grid)
771 {
772     const int gridpointIndex = coordState_.gridpointIndex();
773     for (int d = 0; d < grid.numDimensions(); d++)
774     {
775         /* This gives the  minimum range consisting only of the current closest point. */
776         originUpdatelist_[d] = grid.point(gridpointIndex).index[d];
777         endUpdatelist_[d]    = grid.point(gridpointIndex).index[d];
778     }
779 }
780
781 namespace
782 {
783
784 /*! \brief
785  * Add partial histograms (accumulating between updates) to accumulating histograms.
786  *
787  * \param[in,out] pointState         The state of the points in the bias.
788  * \param[in,out] weightSumCovering  The weights for checking covering.
789  * \param[in]     numSharedUpdate    The number of biases sharing the histrogram.
790  * \param[in]     commRecord         Struct for intra-simulation communication.
791  * \param[in]     multiSimComm       Struct for multi-simulation communication.
792  * \param[in]     localUpdateList    List of points with data.
793  */
794 void sumHistograms(gmx::ArrayRef<PointState> pointState,
795                    gmx::ArrayRef<double>     weightSumCovering,
796                    int                       numSharedUpdate,
797                    const t_commrec*          commRecord,
798                    const gmx_multisim_t*     multiSimComm,
799                    const std::vector<int>&   localUpdateList)
800 {
801     /* The covering checking histograms are added before summing over simulations, so that the
802        weights from different simulations are kept distinguishable. */
803     for (int globalIndex : localUpdateList)
804     {
805         weightSumCovering[globalIndex] += pointState[globalIndex].weightSumIteration();
806     }
807
808     /* Sum histograms over multiple simulations if needed. */
809     if (numSharedUpdate > 1)
810     {
811         GMX_ASSERT(numSharedUpdate == multiSimComm->numSimulations_,
812                    "Sharing within a simulation is not implemented (yet)");
813
814         /* Collect the weights and counts in linear arrays to be able to use gmx_sumd_sim. */
815         std::vector<double> weightSum;
816         std::vector<double> coordVisits;
817
818         weightSum.resize(localUpdateList.size());
819         coordVisits.resize(localUpdateList.size());
820
821         for (size_t localIndex = 0; localIndex < localUpdateList.size(); localIndex++)
822         {
823             const PointState& ps = pointState[localUpdateList[localIndex]];
824
825             weightSum[localIndex]   = ps.weightSumIteration();
826             coordVisits[localIndex] = ps.numVisitsIteration();
827         }
828
829         sumOverSimulations(gmx::ArrayRef<double>(weightSum), commRecord, multiSimComm);
830         sumOverSimulations(gmx::ArrayRef<double>(coordVisits), commRecord, multiSimComm);
831
832         /* Transfer back the result */
833         for (size_t localIndex = 0; localIndex < localUpdateList.size(); localIndex++)
834         {
835             PointState& ps = pointState[localUpdateList[localIndex]];
836
837             ps.setPartialWeightAndCount(weightSum[localIndex], coordVisits[localIndex]);
838         }
839     }
840
841     /* Now add the partial counts and weights to the accumulating histograms.
842        Note: we still need to use the weights for the update so we wait
843        with resetting them until the end of the update. */
844     for (int globalIndex : localUpdateList)
845     {
846         pointState[globalIndex].addPartialWeightAndCount();
847     }
848 }
849
850 /*! \brief
851  * Label points along an axis as covered or not.
852  *
853  * A point is covered if it is surrounded by visited points up to a radius = coverRadius.
854  *
855  * \param[in]     visited        Visited? For each point.
856  * \param[in]     checkCovering  Check for covering? For each point.
857  * \param[in]     numPoints      The number of grid points along this dimension.
858  * \param[in]     period         Period in number of points.
859  * \param[in]     coverRadius    Cover radius, in points, needed for defining a point as covered.
860  * \param[in,out] covered        In this array elements are 1 for covered points and 0 for
861  * non-covered points, this routine assumes that \p covered has at least size \p numPoints.
862  */
863 void labelCoveredPoints(const std::vector<bool>& visited,
864                         const std::vector<bool>& checkCovering,
865                         int                      numPoints,
866                         int                      period,
867                         int                      coverRadius,
868                         gmx::ArrayRef<int>       covered)
869 {
870     GMX_ASSERT(covered.ssize() >= numPoints, "covered should be at least as large as the grid");
871
872     bool haveFirstNotVisited = false;
873     int  firstNotVisited     = -1;
874     int  notVisitedLow       = -1;
875     int  notVisitedHigh      = -1;
876
877     for (int n = 0; n < numPoints; n++)
878     {
879         if (checkCovering[n] && !visited[n])
880         {
881             if (!haveFirstNotVisited)
882             {
883                 notVisitedLow       = n;
884                 firstNotVisited     = n;
885                 haveFirstNotVisited = true;
886             }
887             else
888             {
889                 notVisitedHigh = n;
890
891                 /* Have now an interval I = [notVisitedLow,notVisitedHigh] of visited points bounded
892                    by unvisited points. The unvisted end points affect the coveredness of the
893                    visited with a reach equal to the cover radius. */
894                 int notCoveredLow  = notVisitedLow + coverRadius;
895                 int notCoveredHigh = notVisitedHigh - coverRadius;
896                 for (int i = notVisitedLow; i <= notVisitedHigh; i++)
897                 {
898                     covered[i] = static_cast<int>((i > notCoveredLow) && (i < notCoveredHigh));
899                 }
900
901                 /* Find a new interval to set covering for. Make the notVisitedHigh of this interval
902                    the notVisitedLow of the next. */
903                 notVisitedLow = notVisitedHigh;
904             }
905         }
906     }
907
908     /* Have labelled all the internal points. Now take care of the boundary regions. */
909     if (!haveFirstNotVisited)
910     {
911         /* No non-visited points <=> all points visited => all points covered. */
912
913         for (int n = 0; n < numPoints; n++)
914         {
915             covered[n] = 1;
916         }
917     }
918     else
919     {
920         int lastNotVisited = notVisitedLow;
921
922         /* For periodic boundaries, non-visited points can influence points
923            on the other side of the boundary so we need to wrap around. */
924
925         /* Lower end. For periodic boundaries the last upper end not visited point becomes the low-end not visited point.
926            For non-periodic boundaries there is no lower end point so a dummy value is used. */
927         int notVisitedHigh = firstNotVisited;
928         int notVisitedLow  = period > 0 ? (lastNotVisited - period) : -(coverRadius + 1);
929
930         int notCoveredLow  = notVisitedLow + coverRadius;
931         int notCoveredHigh = notVisitedHigh - coverRadius;
932
933         for (int i = 0; i <= notVisitedHigh; i++)
934         {
935             /* For non-periodic boundaries notCoveredLow = -1 will impose no restriction. */
936             covered[i] = static_cast<int>((i > notCoveredLow) && (i < notCoveredHigh));
937         }
938
939         /* Upper end. Same as for lower end but in the other direction. */
940         notVisitedHigh = period > 0 ? (firstNotVisited + period) : (numPoints + coverRadius);
941         notVisitedLow  = lastNotVisited;
942
943         notCoveredLow  = notVisitedLow + coverRadius;
944         notCoveredHigh = notVisitedHigh - coverRadius;
945
946         for (int i = notVisitedLow; i <= numPoints - 1; i++)
947         {
948             /* For non-periodic boundaries notCoveredHigh = numPoints will impose no restriction. */
949             covered[i] = static_cast<int>((i > notCoveredLow) && (i < notCoveredHigh));
950         }
951     }
952 }
953
954 } // namespace
955
956 bool BiasState::isSamplingRegionCovered(const BiasParams&             params,
957                                         const std::vector<DimParams>& dimParams,
958                                         const BiasGrid&               grid,
959                                         const t_commrec*              commRecord,
960                                         const gmx_multisim_t*         multiSimComm) const
961 {
962     /* Allocate and initialize arrays: one for checking visits along each dimension,
963        one for keeping track of which points to check and one for the covered points.
964        Possibly these could be kept as AWH variables to avoid these allocations. */
965     struct CheckDim
966     {
967         std::vector<bool> visited;
968         std::vector<bool> checkCovering;
969         // We use int for the covering array since we might use gmx_sumi_sim.
970         std::vector<int> covered;
971     };
972
973     std::vector<CheckDim> checkDim;
974     checkDim.resize(grid.numDimensions());
975
976     for (int d = 0; d < grid.numDimensions(); d++)
977     {
978         const size_t numPoints = grid.axis(d).numPoints();
979         checkDim[d].visited.resize(numPoints, false);
980         checkDim[d].checkCovering.resize(numPoints, false);
981         checkDim[d].covered.resize(numPoints, 0);
982     }
983
984     /* Set visited points along each dimension and which points should be checked for covering.
985        Specifically, points above the free energy cutoff (if there is one) or points outside
986        of the target region are ignored. */
987
988     /* Set the free energy cutoff */
989     double maxFreeEnergy = GMX_FLOAT_MAX;
990
991     if (params.eTarget == eawhtargetCUTOFF)
992     {
993         maxFreeEnergy = freeEnergyMinimumValue(points_) + params.freeEnergyCutoffInKT;
994     }
995
996     /* Set the threshold weight for a point to be considered visited. */
997     double weightThreshold = 1;
998     for (int d = 0; d < grid.numDimensions(); d++)
999     {
1000         if (grid.axis(d).isFepLambdaAxis())
1001         {
1002             /* TODO: Verify that a threshold of 1.0 is OK. With a very high sample weight 1.0 can be
1003              * reached quickly even in regions with low probability. Should the sample weight be
1004              * taken into account here? */
1005             weightThreshold *= 1.0;
1006         }
1007         else
1008         {
1009             weightThreshold *= grid.axis(d).spacing() * std::sqrt(dimParams[d].betak * 0.5 * M_1_PI);
1010         }
1011     }
1012
1013     /* Project the sampling weights onto each dimension */
1014     for (size_t m = 0; m < grid.numPoints(); m++)
1015     {
1016         const PointState& pointState = points_[m];
1017
1018         for (int d = 0; d < grid.numDimensions(); d++)
1019         {
1020             int n = grid.point(m).index[d];
1021
1022             /* Is visited if it was already visited or if there is enough weight at the current point */
1023             checkDim[d].visited[n] = checkDim[d].visited[n] || (weightSumCovering_[m] > weightThreshold);
1024
1025             /* Check for covering if there is at least point in this slice that is in the target region and within the cutoff */
1026             checkDim[d].checkCovering[n] =
1027                     checkDim[d].checkCovering[n]
1028                     || (pointState.inTargetRegion() && pointState.freeEnergy() < maxFreeEnergy);
1029         }
1030     }
1031
1032     /* Label each point along each dimension as covered or not. */
1033     for (int d = 0; d < grid.numDimensions(); d++)
1034     {
1035         labelCoveredPoints(checkDim[d].visited, checkDim[d].checkCovering, grid.axis(d).numPoints(),
1036                            grid.axis(d).numPointsInPeriod(), params.coverRadius()[d], checkDim[d].covered);
1037     }
1038
1039     /* Now check for global covering. Each dimension needs to be covered separately.
1040        A dimension is covered if each point is covered.  Multiple simulations collectively
1041        cover the points, i.e. a point is covered if any of the simulations covered it.
1042        However, visited points are not shared, i.e. if a point is covered or not is
1043        determined by the visits of a single simulation. In general the covering criterion is
1044        all points covered => all points are surrounded by visited points up to a radius = coverRadius.
1045        For 1 simulation, all points covered <=> all points visited. For multiple simulations
1046        however, all points visited collectively !=> all points covered, except for coverRadius = 0.
1047        In the limit of large coverRadius, all points covered => all points visited by at least one
1048        simulation (since no point will be covered until all points have been visited by a
1049        single simulation). Basically coverRadius sets how much "connectedness" (or mixing) a point
1050        needs with surrounding points before sharing covering information with other simulations. */
1051
1052     /* Communicate the covered points between sharing simulations if needed. */
1053     if (params.numSharedUpdate > 1)
1054     {
1055         /* For multiple dimensions this may not be the best way to do it. */
1056         for (int d = 0; d < grid.numDimensions(); d++)
1057         {
1058             sumOverSimulations(
1059                     gmx::arrayRefFromArray(checkDim[d].covered.data(), grid.axis(d).numPoints()),
1060                     commRecord, multiSimComm);
1061         }
1062     }
1063
1064     /* Now check if for each dimension all points are covered. Break if not true. */
1065     bool allPointsCovered = true;
1066     for (int d = 0; d < grid.numDimensions() && allPointsCovered; d++)
1067     {
1068         for (int n = 0; n < grid.axis(d).numPoints() && allPointsCovered; n++)
1069         {
1070             allPointsCovered = (checkDim[d].covered[n] != 0);
1071         }
1072     }
1073
1074     return allPointsCovered;
1075 }
1076
1077 /*! \brief
1078  * Normalizes the free energy and PMF sum.
1079  *
1080  * \param[in] pointState  The state of the points.
1081  */
1082 static void normalizeFreeEnergyAndPmfSum(std::vector<PointState>* pointState)
1083 {
1084     double minF = freeEnergyMinimumValue(*pointState);
1085
1086     for (PointState& ps : *pointState)
1087     {
1088         ps.normalizeFreeEnergyAndPmfSum(minF);
1089     }
1090 }
1091
1092 void BiasState::updateFreeEnergyAndAddSamplesToHistogram(const std::vector<DimParams>& dimParams,
1093                                                          const BiasGrid&               grid,
1094                                                          const BiasParams&             params,
1095                                                          const t_commrec*              commRecord,
1096                                                          const gmx_multisim_t*         multiSimComm,
1097                                                          double                        t,
1098                                                          int64_t                       step,
1099                                                          FILE*                         fplog,
1100                                                          std::vector<int>*             updateList)
1101 {
1102     /* Note hat updateList is only used in this scope and is always
1103      * re-initialized. We do not use a local vector, because that would
1104      * cause reallocation every time this funtion is called and the vector
1105      * can be the size of the whole grid.
1106      */
1107
1108     /* Make a list of all local points, i.e. those that could have been touched since
1109        the last update. These are the points needed for summing histograms below
1110        (non-local points only add zeros). For local updates, this will also be the
1111        final update list. */
1112     makeLocalUpdateList(grid, points_, originUpdatelist_, endUpdatelist_, updateList);
1113     if (params.numSharedUpdate > 1)
1114     {
1115         mergeSharedUpdateLists(updateList, points_.size(), commRecord, multiSimComm);
1116     }
1117
1118     /* Reset the range for the next update */
1119     resetLocalUpdateRange(grid);
1120
1121     /* Add samples to histograms for all local points and sync simulations if needed */
1122     sumHistograms(points_, weightSumCovering_, params.numSharedUpdate, commRecord, multiSimComm, *updateList);
1123
1124     sumPmf(points_, params.numSharedUpdate, commRecord, multiSimComm);
1125
1126     /* Renormalize the free energy if values are too large. */
1127     bool needToNormalizeFreeEnergy = false;
1128     for (int& globalIndex : *updateList)
1129     {
1130         /* We want to keep the absolute value of the free energies to be less
1131            c_largePositiveExponent to be able to safely pass these values to exp(). The check below
1132            ensures this as long as the free energy values grow less than 0.5*c_largePositiveExponent
1133            in a return time to this neighborhood. For reasonable update sizes it's unlikely that
1134            this requirement would be broken. */
1135         if (std::abs(points_[globalIndex].freeEnergy()) > 0.5 * detail::c_largePositiveExponent)
1136         {
1137             needToNormalizeFreeEnergy = true;
1138             break;
1139         }
1140     }
1141
1142     /* Update target distribution? */
1143     bool needToUpdateTargetDistribution =
1144             (params.eTarget != eawhtargetCONSTANT && params.isUpdateTargetStep(step));
1145
1146     /* In the initial stage, the histogram grows dynamically as a function of the number of coverings. */
1147     bool detectedCovering = false;
1148     if (inInitialStage())
1149     {
1150         detectedCovering =
1151                 (params.isCheckCoveringStep(step)
1152                  && isSamplingRegionCovered(params, dimParams, grid, commRecord, multiSimComm));
1153     }
1154
1155     /* The weighthistogram size after this update. */
1156     double newHistogramSize = histogramSize_.newHistogramSize(params, t, detectedCovering, points_,
1157                                                               weightSumCovering_, fplog);
1158
1159     /* Make the update list. Usually we try to only update local points,
1160      * but if the update has non-trivial or non-deterministic effects
1161      * on non-local points a global update is needed. This is the case when:
1162      * 1) a covering occurred in the initial stage, leading to non-trivial
1163      *    histogram rescaling factors; or
1164      * 2) the target distribution will be updated, since we don't make any
1165      *    assumption on its form; or
1166      * 3) the AWH parameters are such that we never attempt to skip non-local
1167      *    updates; or
1168      * 4) the free energy values have grown so large that a renormalization
1169      *    is needed.
1170      */
1171     if (needToUpdateTargetDistribution || detectedCovering || !params.skipUpdates() || needToNormalizeFreeEnergy)
1172     {
1173         /* Global update, just add all points. */
1174         updateList->clear();
1175         for (size_t m = 0; m < points_.size(); m++)
1176         {
1177             if (points_[m].inTargetRegion())
1178             {
1179                 updateList->push_back(m);
1180             }
1181         }
1182     }
1183
1184     /* Set histogram scale factors. */
1185     double weightHistScalingSkipped = 0;
1186     double logPmfsumScalingSkipped  = 0;
1187     if (params.skipUpdates())
1188     {
1189         getSkippedUpdateHistogramScaleFactors(params, &weightHistScalingSkipped, &logPmfsumScalingSkipped);
1190     }
1191     double weightHistScalingNew;
1192     double logPmfsumScalingNew;
1193     setHistogramUpdateScaleFactors(params, newHistogramSize, histogramSize_.histogramSize(),
1194                                    &weightHistScalingNew, &logPmfsumScalingNew);
1195
1196     /* Update free energy and reference weight histogram for points in the update list. */
1197     for (int pointIndex : *updateList)
1198     {
1199         PointState* pointStateToUpdate = &points_[pointIndex];
1200
1201         /* Do updates from previous update steps that were skipped because this point was at that time non-local. */
1202         if (params.skipUpdates())
1203         {
1204             pointStateToUpdate->performPreviouslySkippedUpdates(params, histogramSize_.numUpdates(),
1205                                                                 weightHistScalingSkipped,
1206                                                                 logPmfsumScalingSkipped);
1207         }
1208
1209         /* Now do an update with new sampling data. */
1210         pointStateToUpdate->updateWithNewSampling(params, histogramSize_.numUpdates(),
1211                                                   weightHistScalingNew, logPmfsumScalingNew);
1212     }
1213
1214     /* Only update the histogram size after we are done with the local point updates */
1215     histogramSize_.setHistogramSize(newHistogramSize, weightHistScalingNew);
1216
1217     if (needToNormalizeFreeEnergy)
1218     {
1219         normalizeFreeEnergyAndPmfSum(&points_);
1220     }
1221
1222     if (needToUpdateTargetDistribution)
1223     {
1224         /* The target distribution is always updated for all points at once. */
1225         updateTargetDistribution(points_, params);
1226     }
1227
1228     /* Update the bias. The bias is updated separately and last since it simply a function of
1229        the free energy and the target distribution and we want to avoid doing extra work. */
1230     for (int pointIndex : *updateList)
1231     {
1232         points_[pointIndex].updateBias();
1233     }
1234
1235     /* Increase the update counter. */
1236     histogramSize_.incrementNumUpdates();
1237 }
1238
1239 double BiasState::updateProbabilityWeightsAndConvolvedBias(const std::vector<DimParams>& dimParams,
1240                                                            const BiasGrid&               grid,
1241                                                            gmx::ArrayRef<const double> neighborLambdaEnergies,
1242                                                            std::vector<double, AlignedAllocator<double>>* weight) const
1243 {
1244     /* Only neighbors of the current coordinate value will have a non-negligible chance of getting sampled */
1245     const std::vector<int>& neighbors = grid.point(coordState_.gridpointIndex()).neighbor;
1246
1247 #if GMX_SIMD_HAVE_DOUBLE
1248     typedef SimdDouble PackType;
1249     constexpr int      packSize = GMX_SIMD_DOUBLE_WIDTH;
1250 #else
1251     typedef double PackType;
1252     constexpr int  packSize = 1;
1253 #endif
1254     /* Round the size of the weight array up to packSize */
1255     const int weightSize = ((neighbors.size() + packSize - 1) / packSize) * packSize;
1256     weight->resize(weightSize);
1257
1258     double* gmx_restrict weightData = weight->data();
1259     PackType             weightSumPack(0.0);
1260     for (size_t i = 0; i < neighbors.size(); i += packSize)
1261     {
1262         for (size_t n = i; n < i + packSize; n++)
1263         {
1264             if (n < neighbors.size())
1265             {
1266                 const int neighbor = neighbors[n];
1267                 (*weight)[n]       = biasedLogWeightFromPoint(
1268                         dimParams, points_, grid, neighbor, points_[neighbor].bias(),
1269                         coordState_.coordValue(), neighborLambdaEnergies, coordState_.gridpointIndex());
1270             }
1271             else
1272             {
1273                 /* Pad with values that don't affect the result */
1274                 (*weight)[n] = detail::c_largeNegativeExponent;
1275             }
1276         }
1277         PackType weightPack = load<PackType>(weightData + i);
1278         weightPack          = gmx::exp(weightPack);
1279         weightSumPack       = weightSumPack + weightPack;
1280         store(weightData + i, weightPack);
1281     }
1282     /* Sum of probability weights */
1283     double weightSum = reduce(weightSumPack);
1284     GMX_RELEASE_ASSERT(weightSum > 0,
1285                        "zero probability weight when updating AWH probability weights.");
1286
1287     /* Normalize probabilities to sum to 1 */
1288     double invWeightSum = 1 / weightSum;
1289
1290     /* When there is a free energy lambda state axis remove the convolved contributions along that
1291      * axis from the total bias. This must be done after calculating invWeightSum (since weightSum
1292      * will be modified), but before normalizing the weights (below). */
1293     if (grid.hasLambdaAxis())
1294     {
1295         /* If there is only one axis the bias will not be convolved in any dimension. */
1296         if (grid.axis().size() == 1)
1297         {
1298             weightSum = gmx::exp(points_[coordState_.gridpointIndex()].bias());
1299         }
1300         else
1301         {
1302             for (size_t i = 0; i < neighbors.size(); i++)
1303             {
1304                 const int neighbor = neighbors[i];
1305                 if (pointsHaveDifferentLambda(grid, coordState_.gridpointIndex(), neighbor))
1306                 {
1307                     weightSum -= weightData[i];
1308                 }
1309             }
1310         }
1311     }
1312
1313     for (double& w : *weight)
1314     {
1315         w *= invWeightSum;
1316     }
1317
1318     /* Return the convolved bias */
1319     return std::log(weightSum);
1320 }
1321
1322 double BiasState::calcConvolvedBias(const std::vector<DimParams>& dimParams,
1323                                     const BiasGrid&               grid,
1324                                     const awh_dvec&               coordValue) const
1325 {
1326     int              point     = grid.nearestIndex(coordValue);
1327     const GridPoint& gridPoint = grid.point(point);
1328
1329     /* Sum the probability weights from the neighborhood of the given point */
1330     double weightSum = 0;
1331     for (int neighbor : gridPoint.neighbor)
1332     {
1333         /* No convolution is required along the lambda dimension. */
1334         if (pointsHaveDifferentLambda(grid, point, neighbor))
1335         {
1336             continue;
1337         }
1338         double logWeight = biasedLogWeightFromPoint(dimParams, points_, grid, neighbor,
1339                                                     points_[neighbor].bias(), coordValue, {}, point);
1340         weightSum += std::exp(logWeight);
1341     }
1342
1343     /* Returns -GMX_FLOAT_MAX if no neighboring points were in the target region. */
1344     return (weightSum > 0) ? std::log(weightSum) : -GMX_FLOAT_MAX;
1345 }
1346
1347 void BiasState::sampleProbabilityWeights(const BiasGrid& grid, gmx::ArrayRef<const double> probWeightNeighbor)
1348 {
1349     const std::vector<int>& neighbor = grid.point(coordState_.gridpointIndex()).neighbor;
1350
1351     /* Save weights for next update */
1352     for (size_t n = 0; n < neighbor.size(); n++)
1353     {
1354         points_[neighbor[n]].increaseWeightSumIteration(probWeightNeighbor[n]);
1355     }
1356
1357     /* Update the local update range. Two corner points define this rectangular
1358      * domain. We need to choose two new corner points such that the new domain
1359      * contains both the old update range and the current neighborhood.
1360      * In the simplest case when an update is performed every sample,
1361      * the update range would simply equal the current neighborhood.
1362      */
1363     int neighborStart = neighbor[0];
1364     int neighborLast  = neighbor[neighbor.size() - 1];
1365     for (int d = 0; d < grid.numDimensions(); d++)
1366     {
1367         int origin = grid.point(neighborStart).index[d];
1368         int last   = grid.point(neighborLast).index[d];
1369
1370         if (origin > last)
1371         {
1372             /* Unwrap if wrapped around the boundary (only happens for periodic
1373              * boundaries). This has been already for the stored index interval.
1374              */
1375             /* TODO: what we want to do is to find the smallest the update
1376              * interval that contains all points that need to be updated.
1377              * This amounts to combining two intervals, the current
1378              * [origin, end] update interval and the new touched neighborhood
1379              * into a new interval that contains all points from both the old
1380              * intervals.
1381              *
1382              * For periodic boundaries it becomes slightly more complicated
1383              * than for closed boundaries because then it needs not be
1384              * true that origin < end (so one can't simply relate the origin/end
1385              * in the min()/max() below). The strategy here is to choose the
1386              * origin closest to a reference point (index 0) and then unwrap
1387              * the end index if needed and choose the largest end index.
1388              * This ensures that both intervals are in the new interval
1389              * but it's not necessarily the smallest.
1390              * Currently we solve this by going through each possibility
1391              * and checking them.
1392              */
1393             last += grid.axis(d).numPointsInPeriod();
1394         }
1395
1396         originUpdatelist_[d] = std::min(originUpdatelist_[d], origin);
1397         endUpdatelist_[d]    = std::max(endUpdatelist_[d], last);
1398     }
1399 }
1400
1401 void BiasState::sampleCoordAndPmf(const std::vector<DimParams>& dimParams,
1402                                   const BiasGrid&               grid,
1403                                   gmx::ArrayRef<const double>   probWeightNeighbor,
1404                                   double                        convolvedBias)
1405 {
1406     /* Sampling-based deconvolution extracting the PMF.
1407      * Update the PMF histogram with the current coordinate value.
1408      *
1409      * Because of the finite width of the harmonic potential, the free energy
1410      * defined for each coordinate point does not exactly equal that of the
1411      * actual coordinate, the PMF. However, the PMF can be estimated by applying
1412      * the relation exp(-PMF) = exp(-bias_convolved)*P_biased/Z, i.e. by keeping a
1413      * reweighted histogram of the coordinate value. Strictly, this relies on
1414      * the unknown normalization constant Z being either constant or known. Here,
1415      * neither is true except in the long simulation time limit. Empirically however,
1416      * it works (mainly because how the PMF histogram is rescaled).
1417      */
1418
1419     const int                gridPointIndex  = coordState_.gridpointIndex();
1420     const std::optional<int> lambdaAxisIndex = grid.lambdaAxisIndex();
1421
1422     /* Update the PMF of points along a lambda axis with their bias. */
1423     if (lambdaAxisIndex)
1424     {
1425         const std::vector<int>& neighbors = grid.point(gridPointIndex).neighbor;
1426
1427         std::vector<double> lambdaMarginalDistribution =
1428                 calculateFELambdaMarginalDistribution(grid, neighbors, probWeightNeighbor);
1429
1430         awh_dvec coordValueAlongLambda = { coordState_.coordValue()[0], coordState_.coordValue()[1],
1431                                            coordState_.coordValue()[2], coordState_.coordValue()[3] };
1432         for (size_t i = 0; i < neighbors.size(); i++)
1433         {
1434             const int neighbor = neighbors[i];
1435             double    bias;
1436             if (pointsAlongLambdaAxis(grid, gridPointIndex, neighbor))
1437             {
1438                 const double neighborLambda = grid.point(neighbor).coordValue[lambdaAxisIndex.value()];
1439                 if (neighbor == gridPointIndex)
1440                 {
1441                     bias = convolvedBias;
1442                 }
1443                 else
1444                 {
1445                     coordValueAlongLambda[lambdaAxisIndex.value()] = neighborLambda;
1446                     bias = calcConvolvedBias(dimParams, grid, coordValueAlongLambda);
1447                 }
1448
1449                 const double probWeight = lambdaMarginalDistribution[neighborLambda];
1450                 const double weightedBias = bias - std::log(std::max(probWeight, GMX_DOUBLE_MIN)); // avoid log(0)
1451
1452                 if (neighbor == gridPointIndex && grid.covers(coordState_.coordValue()))
1453                 {
1454                     points_[neighbor].samplePmf(weightedBias);
1455                 }
1456                 else
1457                 {
1458                     points_[neighbor].updatePmfUnvisited(weightedBias);
1459                 }
1460             }
1461         }
1462     }
1463     else
1464     {
1465         /* Only save coordinate data that is in range (the given index is always
1466          * in range even if the coordinate value is not).
1467          */
1468         if (grid.covers(coordState_.coordValue()))
1469         {
1470             /* Save PMF sum and keep a histogram of the sampled coordinate values */
1471             points_[gridPointIndex].samplePmf(convolvedBias);
1472         }
1473     }
1474
1475     /* Save probability weights for the update */
1476     sampleProbabilityWeights(grid, probWeightNeighbor);
1477 }
1478
1479 void BiasState::initHistoryFromState(AwhBiasHistory* biasHistory) const
1480 {
1481     biasHistory->pointState.resize(points_.size());
1482 }
1483
1484 void BiasState::updateHistory(AwhBiasHistory* biasHistory, const BiasGrid& grid) const
1485 {
1486     GMX_RELEASE_ASSERT(biasHistory->pointState.size() == points_.size(),
1487                        "The AWH history setup does not match the AWH state.");
1488
1489     AwhBiasStateHistory* stateHistory = &biasHistory->state;
1490     stateHistory->umbrellaGridpoint   = coordState_.umbrellaGridpoint();
1491
1492     for (size_t m = 0; m < biasHistory->pointState.size(); m++)
1493     {
1494         AwhPointStateHistory* psh = &biasHistory->pointState[m];
1495
1496         points_[m].storeState(psh);
1497
1498         psh->weightsum_covering = weightSumCovering_[m];
1499     }
1500
1501     histogramSize_.storeState(stateHistory);
1502
1503     stateHistory->origin_index_updatelist = multiDimGridIndexToLinear(grid, originUpdatelist_);
1504     stateHistory->end_index_updatelist    = multiDimGridIndexToLinear(grid, endUpdatelist_);
1505 }
1506
1507 void BiasState::restoreFromHistory(const AwhBiasHistory& biasHistory, const BiasGrid& grid)
1508 {
1509     const AwhBiasStateHistory& stateHistory = biasHistory.state;
1510
1511     coordState_.restoreFromHistory(stateHistory);
1512
1513     if (biasHistory.pointState.size() != points_.size())
1514     {
1515         GMX_THROW(
1516                 InvalidInputError("Bias grid size in checkpoint and simulation do not match. "
1517                                   "Likely you provided a checkpoint from a different simulation."));
1518     }
1519     for (size_t m = 0; m < points_.size(); m++)
1520     {
1521         points_[m].setFromHistory(biasHistory.pointState[m]);
1522     }
1523
1524     for (size_t m = 0; m < weightSumCovering_.size(); m++)
1525     {
1526         weightSumCovering_[m] = biasHistory.pointState[m].weightsum_covering;
1527     }
1528
1529     histogramSize_.restoreFromHistory(stateHistory);
1530
1531     linearGridindexToMultiDim(grid, stateHistory.origin_index_updatelist, originUpdatelist_);
1532     linearGridindexToMultiDim(grid, stateHistory.end_index_updatelist, endUpdatelist_);
1533 }
1534
1535 void BiasState::broadcast(const t_commrec* commRecord)
1536 {
1537     gmx_bcast(sizeof(coordState_), &coordState_, commRecord->mpi_comm_mygroup);
1538
1539     gmx_bcast(points_.size() * sizeof(PointState), points_.data(), commRecord->mpi_comm_mygroup);
1540
1541     gmx_bcast(weightSumCovering_.size() * sizeof(double), weightSumCovering_.data(),
1542               commRecord->mpi_comm_mygroup);
1543
1544     gmx_bcast(sizeof(histogramSize_), &histogramSize_, commRecord->mpi_comm_mygroup);
1545 }
1546
1547 void BiasState::setFreeEnergyToConvolvedPmf(const std::vector<DimParams>& dimParams, const BiasGrid& grid)
1548 {
1549     std::vector<float> convolvedPmf;
1550
1551     calcConvolvedPmf(dimParams, grid, &convolvedPmf);
1552
1553     for (size_t m = 0; m < points_.size(); m++)
1554     {
1555         points_[m].setFreeEnergy(convolvedPmf[m]);
1556     }
1557 }
1558
1559 /*! \brief
1560  * Count trailing data rows containing only zeros.
1561  *
1562  * \param[in] data        2D data array.
1563  * \param[in] numRows     Number of rows in array.
1564  * \param[in] numColumns  Number of cols in array.
1565  * \returns the number of trailing zero rows.
1566  */
1567 static int countTrailingZeroRows(const double* const* data, int numRows, int numColumns)
1568 {
1569     int numZeroRows = 0;
1570     for (int m = numRows - 1; m >= 0; m--)
1571     {
1572         bool rowIsZero = true;
1573         for (int d = 0; d < numColumns; d++)
1574         {
1575             if (data[d][m] != 0)
1576             {
1577                 rowIsZero = false;
1578                 break;
1579             }
1580         }
1581
1582         if (!rowIsZero)
1583         {
1584             /* At a row with non-zero data */
1585             break;
1586         }
1587         else
1588         {
1589             /* Still at a zero data row, keep checking rows higher up. */
1590             numZeroRows++;
1591         }
1592     }
1593
1594     return numZeroRows;
1595 }
1596
1597 /*! \brief
1598  * Initializes the PMF and target with data read from an input table.
1599  *
1600  * \param[in]     dimParams   The dimension parameters.
1601  * \param[in]     grid        The grid.
1602  * \param[in]     filename    The filename to read PMF and target from.
1603  * \param[in]     numBias     Number of biases.
1604  * \param[in]     biasIndex   The index of the bias.
1605  * \param[in,out] pointState  The state of the points in this bias.
1606  */
1607 static void readUserPmfAndTargetDistribution(const std::vector<DimParams>& dimParams,
1608                                              const BiasGrid&               grid,
1609                                              const std::string&            filename,
1610                                              int                           numBias,
1611                                              int                           biasIndex,
1612                                              std::vector<PointState>*      pointState)
1613 {
1614     /* Read the PMF and target distribution.
1615        From the PMF, the convolved PMF, or the reference value free energy, can be calculated
1616        base on the force constant. The free energy and target together determine the bias.
1617      */
1618     std::string filenameModified(filename);
1619     if (numBias > 1)
1620     {
1621         size_t n = filenameModified.rfind('.');
1622         GMX_RELEASE_ASSERT(n != std::string::npos,
1623                            "The filename should contain an extension starting with .");
1624         filenameModified.insert(n, formatString("%d", biasIndex));
1625     }
1626
1627     std::string correctFormatMessage = formatString(
1628             "%s is expected in the following format. "
1629             "The first ndim column(s) should contain the coordinate values for each point, "
1630             "each column containing values of one dimension (in ascending order). "
1631             "For a multidimensional coordinate, points should be listed "
1632             "in the order obtained by traversing lower dimensions first. "
1633             "E.g. for two-dimensional grid of size nxn: "
1634             "(1, 1), (1, 2),..., (1, n), (2, 1), (2, 2), ..., , (n, n - 1), (n, n). "
1635             "Column ndim +  1 should contain the PMF value for each coordinate value. "
1636             "The target distribution values should be in column ndim + 2  or column ndim + 5. "
1637             "Make sure the input file ends with a new line but has no trailing new lines.",
1638             filename.c_str());
1639     gmx::TextLineWrapper wrapper;
1640     wrapper.settings().setLineLength(c_linewidth);
1641     correctFormatMessage = wrapper.wrapToString(correctFormatMessage);
1642
1643     double** data;
1644     int      numColumns;
1645     int      numRows = read_xvg(filenameModified.c_str(), &data, &numColumns);
1646
1647     /* Check basic data properties here. BiasGrid takes care of more complicated things. */
1648
1649     if (numRows <= 0)
1650     {
1651         std::string mesg = gmx::formatString("%s is empty!.\n\n%s", filename.c_str(),
1652                                              correctFormatMessage.c_str());
1653         GMX_THROW(InvalidInputError(mesg));
1654     }
1655
1656     /* Less than 2 points is not useful for PMF or target. */
1657     if (numRows < 2)
1658     {
1659         std::string mesg = gmx::formatString(
1660                 "%s contains too few data points (%d)."
1661                 "The minimum number of points is 2.",
1662                 filename.c_str(), numRows);
1663         GMX_THROW(InvalidInputError(mesg));
1664     }
1665
1666     /* Make sure there are enough columns of data.
1667
1668        Two formats are allowed. Either with columns  {coords, PMF, target} or
1669        {coords, PMF, x, y, z, target, ...}. The latter format is allowed since that
1670        is how AWH output is written (x, y, z being other AWH variables). For this format,
1671        trailing columns are ignored.
1672      */
1673     int columnIndexTarget;
1674     int numColumnsMin  = dimParams.size() + 2;
1675     int columnIndexPmf = dimParams.size();
1676     if (numColumns == numColumnsMin)
1677     {
1678         columnIndexTarget = columnIndexPmf + 1;
1679     }
1680     else
1681     {
1682         columnIndexTarget = columnIndexPmf + 4;
1683     }
1684
1685     if (numColumns < numColumnsMin)
1686     {
1687         std::string mesg = gmx::formatString(
1688                 "The number of columns in %s should be at least %d."
1689                 "\n\n%s",
1690                 filename.c_str(), numColumnsMin, correctFormatMessage.c_str());
1691         GMX_THROW(InvalidInputError(mesg));
1692     }
1693
1694     /* read_xvg can give trailing zero data rows for trailing new lines in the input. We allow 1 zero row,
1695        since this could be real data. But multiple trailing zero rows cannot correspond to valid data. */
1696     int numZeroRows = countTrailingZeroRows(data, numRows, numColumns);
1697     if (numZeroRows > 1)
1698     {
1699         std::string mesg = gmx::formatString(
1700                 "Found %d trailing zero data rows in %s. Please remove trailing empty lines and "
1701                 "try again.",
1702                 numZeroRows, filename.c_str());
1703         GMX_THROW(InvalidInputError(mesg));
1704     }
1705
1706     /* Convert from user units to internal units before sending the data of to grid. */
1707     for (size_t d = 0; d < dimParams.size(); d++)
1708     {
1709         double scalingFactor = dimParams[d].scaleUserInputToInternal(1);
1710         if (scalingFactor == 1)
1711         {
1712             continue;
1713         }
1714         for (size_t m = 0; m < pointState->size(); m++)
1715         {
1716             data[d][m] *= scalingFactor;
1717         }
1718     }
1719
1720     /* Get a data point for each AWH grid point so that they all get data. */
1721     std::vector<int> gridIndexToDataIndex(grid.numPoints());
1722     mapGridToDataGrid(&gridIndexToDataIndex, data, numRows, filename, grid, correctFormatMessage);
1723
1724     /* Extract the data for each grid point.
1725      * We check if the target distribution is zero for all points.
1726      */
1727     bool targetDistributionIsZero = true;
1728     for (size_t m = 0; m < pointState->size(); m++)
1729     {
1730         (*pointState)[m].setLogPmfSum(-data[columnIndexPmf][gridIndexToDataIndex[m]]);
1731         double target = data[columnIndexTarget][gridIndexToDataIndex[m]];
1732
1733         /* Check if the values are allowed. */
1734         if (target < 0)
1735         {
1736             std::string mesg = gmx::formatString(
1737                     "Target distribution weight at point %zu (%g) in %s is negative.", m, target,
1738                     filename.c_str());
1739             GMX_THROW(InvalidInputError(mesg));
1740         }
1741         if (target > 0)
1742         {
1743             targetDistributionIsZero = false;
1744         }
1745         (*pointState)[m].setTargetConstantWeight(target);
1746     }
1747
1748     if (targetDistributionIsZero)
1749     {
1750         std::string mesg =
1751                 gmx::formatString("The target weights given in column %d in %s are all 0",
1752                                   columnIndexTarget, filename.c_str());
1753         GMX_THROW(InvalidInputError(mesg));
1754     }
1755
1756     /* Free the arrays. */
1757     for (int m = 0; m < numColumns; m++)
1758     {
1759         sfree(data[m]);
1760     }
1761     sfree(data);
1762 }
1763
1764 void BiasState::normalizePmf(int numSharingSims)
1765 {
1766     /* The normalization of the PMF estimate matters because it determines how big effect the next sample has.
1767        Approximately (for large enough force constant) we should have:
1768        sum_x(exp(-pmf(x)) = nsamples*sum_xref(exp(-f(xref)).
1769      */
1770
1771     /* Calculate the normalization factor, i.e. divide by the pmf sum, multiply by the number of samples and the f sum */
1772     double expSumPmf = 0;
1773     double expSumF   = 0;
1774     for (const PointState& pointState : points_)
1775     {
1776         if (pointState.inTargetRegion())
1777         {
1778             expSumPmf += std::exp(pointState.logPmfSum());
1779             expSumF += std::exp(-pointState.freeEnergy());
1780         }
1781     }
1782     double numSamples = histogramSize_.histogramSize() / numSharingSims;
1783
1784     /* Renormalize */
1785     double logRenorm = std::log(numSamples * expSumF / expSumPmf);
1786     for (PointState& pointState : points_)
1787     {
1788         if (pointState.inTargetRegion())
1789         {
1790             pointState.setLogPmfSum(pointState.logPmfSum() + logRenorm);
1791         }
1792     }
1793 }
1794
1795 void BiasState::initGridPointState(const AwhBiasParams&          awhBiasParams,
1796                                    const std::vector<DimParams>& dimParams,
1797                                    const BiasGrid&               grid,
1798                                    const BiasParams&             params,
1799                                    const std::string&            filename,
1800                                    int                           numBias)
1801 {
1802     /* Modify PMF, free energy and the constant target distribution factor
1803      * to user input values if there is data given.
1804      */
1805     if (awhBiasParams.bUserData)
1806     {
1807         readUserPmfAndTargetDistribution(dimParams, grid, filename, numBias, params.biasIndex, &points_);
1808         setFreeEnergyToConvolvedPmf(dimParams, grid);
1809     }
1810
1811     /* The local Boltzmann distribution is special because the target distribution is updated as a function of the reference weighthistogram. */
1812     GMX_RELEASE_ASSERT(params.eTarget != eawhtargetLOCALBOLTZMANN || points_[0].weightSumRef() != 0,
1813                        "AWH reference weight histogram not initialized properly with local "
1814                        "Boltzmann target distribution.");
1815
1816     updateTargetDistribution(points_, params);
1817
1818     for (PointState& pointState : points_)
1819     {
1820         if (pointState.inTargetRegion())
1821         {
1822             pointState.updateBias();
1823         }
1824         else
1825         {
1826             /* Note that for zero target this is a value that represents -infinity but should not be used for biasing. */
1827             pointState.setTargetToZero();
1828         }
1829     }
1830
1831     /* Set the initial reference weighthistogram. */
1832     const double histogramSize = histogramSize_.histogramSize();
1833     for (auto& pointState : points_)
1834     {
1835         pointState.setInitialReferenceWeightHistogram(histogramSize);
1836     }
1837
1838     /* Make sure the pmf is normalized consistently with the histogram size.
1839        Note: the target distribution and free energy need to be set here. */
1840     normalizePmf(params.numSharedUpdate);
1841 }
1842
1843 BiasState::BiasState(const AwhBiasParams&          awhBiasParams,
1844                      double                        histogramSizeInitial,
1845                      const std::vector<DimParams>& dimParams,
1846                      const BiasGrid&               grid) :
1847     coordState_(awhBiasParams, dimParams, grid),
1848     points_(grid.numPoints()),
1849     weightSumCovering_(grid.numPoints()),
1850     histogramSize_(awhBiasParams, histogramSizeInitial)
1851 {
1852     /* The minimum and maximum multidimensional point indices that are affected by the next update */
1853     for (size_t d = 0; d < dimParams.size(); d++)
1854     {
1855         int index            = grid.point(coordState_.gridpointIndex()).index[d];
1856         originUpdatelist_[d] = index;
1857         endUpdatelist_[d]    = index;
1858     }
1859 }
1860
1861 } // namespace gmx