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