AWH avoid constructors only differing in parameter types
[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].fepDimParams().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].pullDimParams().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].pullDimParams().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()
1010                                * std::sqrt(dimParams[d].pullDimParams().betak * 0.5 * M_1_PI);
1011         }
1012     }
1013
1014     /* Project the sampling weights onto each dimension */
1015     for (size_t m = 0; m < grid.numPoints(); m++)
1016     {
1017         const PointState& pointState = points_[m];
1018
1019         for (int d = 0; d < grid.numDimensions(); d++)
1020         {
1021             int n = grid.point(m).index[d];
1022
1023             /* Is visited if it was already visited or if there is enough weight at the current point */
1024             checkDim[d].visited[n] = checkDim[d].visited[n] || (weightSumCovering_[m] > weightThreshold);
1025
1026             /* Check for covering if there is at least point in this slice that is in the target region and within the cutoff */
1027             checkDim[d].checkCovering[n] =
1028                     checkDim[d].checkCovering[n]
1029                     || (pointState.inTargetRegion() && pointState.freeEnergy() < maxFreeEnergy);
1030         }
1031     }
1032
1033     /* Label each point along each dimension as covered or not. */
1034     for (int d = 0; d < grid.numDimensions(); d++)
1035     {
1036         labelCoveredPoints(checkDim[d].visited, checkDim[d].checkCovering, grid.axis(d).numPoints(),
1037                            grid.axis(d).numPointsInPeriod(), params.coverRadius()[d], checkDim[d].covered);
1038     }
1039
1040     /* Now check for global covering. Each dimension needs to be covered separately.
1041        A dimension is covered if each point is covered.  Multiple simulations collectively
1042        cover the points, i.e. a point is covered if any of the simulations covered it.
1043        However, visited points are not shared, i.e. if a point is covered or not is
1044        determined by the visits of a single simulation. In general the covering criterion is
1045        all points covered => all points are surrounded by visited points up to a radius = coverRadius.
1046        For 1 simulation, all points covered <=> all points visited. For multiple simulations
1047        however, all points visited collectively !=> all points covered, except for coverRadius = 0.
1048        In the limit of large coverRadius, all points covered => all points visited by at least one
1049        simulation (since no point will be covered until all points have been visited by a
1050        single simulation). Basically coverRadius sets how much "connectedness" (or mixing) a point
1051        needs with surrounding points before sharing covering information with other simulations. */
1052
1053     /* Communicate the covered points between sharing simulations if needed. */
1054     if (params.numSharedUpdate > 1)
1055     {
1056         /* For multiple dimensions this may not be the best way to do it. */
1057         for (int d = 0; d < grid.numDimensions(); d++)
1058         {
1059             sumOverSimulations(
1060                     gmx::arrayRefFromArray(checkDim[d].covered.data(), grid.axis(d).numPoints()),
1061                     commRecord, multiSimComm);
1062         }
1063     }
1064
1065     /* Now check if for each dimension all points are covered. Break if not true. */
1066     bool allPointsCovered = true;
1067     for (int d = 0; d < grid.numDimensions() && allPointsCovered; d++)
1068     {
1069         for (int n = 0; n < grid.axis(d).numPoints() && allPointsCovered; n++)
1070         {
1071             allPointsCovered = (checkDim[d].covered[n] != 0);
1072         }
1073     }
1074
1075     return allPointsCovered;
1076 }
1077
1078 /*! \brief
1079  * Normalizes the free energy and PMF sum.
1080  *
1081  * \param[in] pointState  The state of the points.
1082  */
1083 static void normalizeFreeEnergyAndPmfSum(std::vector<PointState>* pointState)
1084 {
1085     double minF = freeEnergyMinimumValue(*pointState);
1086
1087     for (PointState& ps : *pointState)
1088     {
1089         ps.normalizeFreeEnergyAndPmfSum(minF);
1090     }
1091 }
1092
1093 void BiasState::updateFreeEnergyAndAddSamplesToHistogram(const std::vector<DimParams>& dimParams,
1094                                                          const BiasGrid&               grid,
1095                                                          const BiasParams&             params,
1096                                                          const t_commrec*              commRecord,
1097                                                          const gmx_multisim_t*         multiSimComm,
1098                                                          double                        t,
1099                                                          int64_t                       step,
1100                                                          FILE*                         fplog,
1101                                                          std::vector<int>*             updateList)
1102 {
1103     /* Note hat updateList is only used in this scope and is always
1104      * re-initialized. We do not use a local vector, because that would
1105      * cause reallocation every time this funtion is called and the vector
1106      * can be the size of the whole grid.
1107      */
1108
1109     /* Make a list of all local points, i.e. those that could have been touched since
1110        the last update. These are the points needed for summing histograms below
1111        (non-local points only add zeros). For local updates, this will also be the
1112        final update list. */
1113     makeLocalUpdateList(grid, points_, originUpdatelist_, endUpdatelist_, updateList);
1114     if (params.numSharedUpdate > 1)
1115     {
1116         mergeSharedUpdateLists(updateList, points_.size(), commRecord, multiSimComm);
1117     }
1118
1119     /* Reset the range for the next update */
1120     resetLocalUpdateRange(grid);
1121
1122     /* Add samples to histograms for all local points and sync simulations if needed */
1123     sumHistograms(points_, weightSumCovering_, params.numSharedUpdate, commRecord, multiSimComm, *updateList);
1124
1125     sumPmf(points_, params.numSharedUpdate, commRecord, multiSimComm);
1126
1127     /* Renormalize the free energy if values are too large. */
1128     bool needToNormalizeFreeEnergy = false;
1129     for (int& globalIndex : *updateList)
1130     {
1131         /* We want to keep the absolute value of the free energies to be less
1132            c_largePositiveExponent to be able to safely pass these values to exp(). The check below
1133            ensures this as long as the free energy values grow less than 0.5*c_largePositiveExponent
1134            in a return time to this neighborhood. For reasonable update sizes it's unlikely that
1135            this requirement would be broken. */
1136         if (std::abs(points_[globalIndex].freeEnergy()) > 0.5 * detail::c_largePositiveExponent)
1137         {
1138             needToNormalizeFreeEnergy = true;
1139             break;
1140         }
1141     }
1142
1143     /* Update target distribution? */
1144     bool needToUpdateTargetDistribution =
1145             (params.eTarget != eawhtargetCONSTANT && params.isUpdateTargetStep(step));
1146
1147     /* In the initial stage, the histogram grows dynamically as a function of the number of coverings. */
1148     bool detectedCovering = false;
1149     if (inInitialStage())
1150     {
1151         detectedCovering =
1152                 (params.isCheckCoveringStep(step)
1153                  && isSamplingRegionCovered(params, dimParams, grid, commRecord, multiSimComm));
1154     }
1155
1156     /* The weighthistogram size after this update. */
1157     double newHistogramSize = histogramSize_.newHistogramSize(params, t, detectedCovering, points_,
1158                                                               weightSumCovering_, fplog);
1159
1160     /* Make the update list. Usually we try to only update local points,
1161      * but if the update has non-trivial or non-deterministic effects
1162      * on non-local points a global update is needed. This is the case when:
1163      * 1) a covering occurred in the initial stage, leading to non-trivial
1164      *    histogram rescaling factors; or
1165      * 2) the target distribution will be updated, since we don't make any
1166      *    assumption on its form; or
1167      * 3) the AWH parameters are such that we never attempt to skip non-local
1168      *    updates; or
1169      * 4) the free energy values have grown so large that a renormalization
1170      *    is needed.
1171      */
1172     if (needToUpdateTargetDistribution || detectedCovering || !params.skipUpdates() || needToNormalizeFreeEnergy)
1173     {
1174         /* Global update, just add all points. */
1175         updateList->clear();
1176         for (size_t m = 0; m < points_.size(); m++)
1177         {
1178             if (points_[m].inTargetRegion())
1179             {
1180                 updateList->push_back(m);
1181             }
1182         }
1183     }
1184
1185     /* Set histogram scale factors. */
1186     double weightHistScalingSkipped = 0;
1187     double logPmfsumScalingSkipped  = 0;
1188     if (params.skipUpdates())
1189     {
1190         getSkippedUpdateHistogramScaleFactors(params, &weightHistScalingSkipped, &logPmfsumScalingSkipped);
1191     }
1192     double weightHistScalingNew;
1193     double logPmfsumScalingNew;
1194     setHistogramUpdateScaleFactors(params, newHistogramSize, histogramSize_.histogramSize(),
1195                                    &weightHistScalingNew, &logPmfsumScalingNew);
1196
1197     /* Update free energy and reference weight histogram for points in the update list. */
1198     for (int pointIndex : *updateList)
1199     {
1200         PointState* pointStateToUpdate = &points_[pointIndex];
1201
1202         /* Do updates from previous update steps that were skipped because this point was at that time non-local. */
1203         if (params.skipUpdates())
1204         {
1205             pointStateToUpdate->performPreviouslySkippedUpdates(params, histogramSize_.numUpdates(),
1206                                                                 weightHistScalingSkipped,
1207                                                                 logPmfsumScalingSkipped);
1208         }
1209
1210         /* Now do an update with new sampling data. */
1211         pointStateToUpdate->updateWithNewSampling(params, histogramSize_.numUpdates(),
1212                                                   weightHistScalingNew, logPmfsumScalingNew);
1213     }
1214
1215     /* Only update the histogram size after we are done with the local point updates */
1216     histogramSize_.setHistogramSize(newHistogramSize, weightHistScalingNew);
1217
1218     if (needToNormalizeFreeEnergy)
1219     {
1220         normalizeFreeEnergyAndPmfSum(&points_);
1221     }
1222
1223     if (needToUpdateTargetDistribution)
1224     {
1225         /* The target distribution is always updated for all points at once. */
1226         updateTargetDistribution(points_, params);
1227     }
1228
1229     /* Update the bias. The bias is updated separately and last since it simply a function of
1230        the free energy and the target distribution and we want to avoid doing extra work. */
1231     for (int pointIndex : *updateList)
1232     {
1233         points_[pointIndex].updateBias();
1234     }
1235
1236     /* Increase the update counter. */
1237     histogramSize_.incrementNumUpdates();
1238 }
1239
1240 double BiasState::updateProbabilityWeightsAndConvolvedBias(const std::vector<DimParams>& dimParams,
1241                                                            const BiasGrid&               grid,
1242                                                            gmx::ArrayRef<const double> neighborLambdaEnergies,
1243                                                            std::vector<double, AlignedAllocator<double>>* weight) const
1244 {
1245     /* Only neighbors of the current coordinate value will have a non-negligible chance of getting sampled */
1246     const std::vector<int>& neighbors = grid.point(coordState_.gridpointIndex()).neighbor;
1247
1248 #if GMX_SIMD_HAVE_DOUBLE
1249     typedef SimdDouble PackType;
1250     constexpr int      packSize = GMX_SIMD_DOUBLE_WIDTH;
1251 #else
1252     typedef double PackType;
1253     constexpr int  packSize = 1;
1254 #endif
1255     /* Round the size of the weight array up to packSize */
1256     const int weightSize = ((neighbors.size() + packSize - 1) / packSize) * packSize;
1257     weight->resize(weightSize);
1258
1259     double* gmx_restrict weightData = weight->data();
1260     PackType             weightSumPack(0.0);
1261     for (size_t i = 0; i < neighbors.size(); i += packSize)
1262     {
1263         for (size_t n = i; n < i + packSize; n++)
1264         {
1265             if (n < neighbors.size())
1266             {
1267                 const int neighbor = neighbors[n];
1268                 (*weight)[n]       = biasedLogWeightFromPoint(
1269                         dimParams, points_, grid, neighbor, points_[neighbor].bias(),
1270                         coordState_.coordValue(), neighborLambdaEnergies, coordState_.gridpointIndex());
1271             }
1272             else
1273             {
1274                 /* Pad with values that don't affect the result */
1275                 (*weight)[n] = detail::c_largeNegativeExponent;
1276             }
1277         }
1278         PackType weightPack = load<PackType>(weightData + i);
1279         weightPack          = gmx::exp(weightPack);
1280         weightSumPack       = weightSumPack + weightPack;
1281         store(weightData + i, weightPack);
1282     }
1283     /* Sum of probability weights */
1284     double weightSum = reduce(weightSumPack);
1285     GMX_RELEASE_ASSERT(weightSum > 0,
1286                        "zero probability weight when updating AWH probability weights.");
1287
1288     /* Normalize probabilities to sum to 1 */
1289     double invWeightSum = 1 / weightSum;
1290
1291     /* When there is a free energy lambda state axis remove the convolved contributions along that
1292      * axis from the total bias. This must be done after calculating invWeightSum (since weightSum
1293      * will be modified), but before normalizing the weights (below). */
1294     if (grid.hasLambdaAxis())
1295     {
1296         /* If there is only one axis the bias will not be convolved in any dimension. */
1297         if (grid.axis().size() == 1)
1298         {
1299             weightSum = gmx::exp(points_[coordState_.gridpointIndex()].bias());
1300         }
1301         else
1302         {
1303             for (size_t i = 0; i < neighbors.size(); i++)
1304             {
1305                 const int neighbor = neighbors[i];
1306                 if (pointsHaveDifferentLambda(grid, coordState_.gridpointIndex(), neighbor))
1307                 {
1308                     weightSum -= weightData[i];
1309                 }
1310             }
1311         }
1312     }
1313
1314     for (double& w : *weight)
1315     {
1316         w *= invWeightSum;
1317     }
1318
1319     /* Return the convolved bias */
1320     return std::log(weightSum);
1321 }
1322
1323 double BiasState::calcConvolvedBias(const std::vector<DimParams>& dimParams,
1324                                     const BiasGrid&               grid,
1325                                     const awh_dvec&               coordValue) const
1326 {
1327     int              point     = grid.nearestIndex(coordValue);
1328     const GridPoint& gridPoint = grid.point(point);
1329
1330     /* Sum the probability weights from the neighborhood of the given point */
1331     double weightSum = 0;
1332     for (int neighbor : gridPoint.neighbor)
1333     {
1334         /* No convolution is required along the lambda dimension. */
1335         if (pointsHaveDifferentLambda(grid, point, neighbor))
1336         {
1337             continue;
1338         }
1339         double logWeight = biasedLogWeightFromPoint(dimParams, points_, grid, neighbor,
1340                                                     points_[neighbor].bias(), coordValue, {}, point);
1341         weightSum += std::exp(logWeight);
1342     }
1343
1344     /* Returns -GMX_FLOAT_MAX if no neighboring points were in the target region. */
1345     return (weightSum > 0) ? std::log(weightSum) : -GMX_FLOAT_MAX;
1346 }
1347
1348 void BiasState::sampleProbabilityWeights(const BiasGrid& grid, gmx::ArrayRef<const double> probWeightNeighbor)
1349 {
1350     const std::vector<int>& neighbor = grid.point(coordState_.gridpointIndex()).neighbor;
1351
1352     /* Save weights for next update */
1353     for (size_t n = 0; n < neighbor.size(); n++)
1354     {
1355         points_[neighbor[n]].increaseWeightSumIteration(probWeightNeighbor[n]);
1356     }
1357
1358     /* Update the local update range. Two corner points define this rectangular
1359      * domain. We need to choose two new corner points such that the new domain
1360      * contains both the old update range and the current neighborhood.
1361      * In the simplest case when an update is performed every sample,
1362      * the update range would simply equal the current neighborhood.
1363      */
1364     int neighborStart = neighbor[0];
1365     int neighborLast  = neighbor[neighbor.size() - 1];
1366     for (int d = 0; d < grid.numDimensions(); d++)
1367     {
1368         int origin = grid.point(neighborStart).index[d];
1369         int last   = grid.point(neighborLast).index[d];
1370
1371         if (origin > last)
1372         {
1373             /* Unwrap if wrapped around the boundary (only happens for periodic
1374              * boundaries). This has been already for the stored index interval.
1375              */
1376             /* TODO: what we want to do is to find the smallest the update
1377              * interval that contains all points that need to be updated.
1378              * This amounts to combining two intervals, the current
1379              * [origin, end] update interval and the new touched neighborhood
1380              * into a new interval that contains all points from both the old
1381              * intervals.
1382              *
1383              * For periodic boundaries it becomes slightly more complicated
1384              * than for closed boundaries because then it needs not be
1385              * true that origin < end (so one can't simply relate the origin/end
1386              * in the min()/max() below). The strategy here is to choose the
1387              * origin closest to a reference point (index 0) and then unwrap
1388              * the end index if needed and choose the largest end index.
1389              * This ensures that both intervals are in the new interval
1390              * but it's not necessarily the smallest.
1391              * Currently we solve this by going through each possibility
1392              * and checking them.
1393              */
1394             last += grid.axis(d).numPointsInPeriod();
1395         }
1396
1397         originUpdatelist_[d] = std::min(originUpdatelist_[d], origin);
1398         endUpdatelist_[d]    = std::max(endUpdatelist_[d], last);
1399     }
1400 }
1401
1402 void BiasState::sampleCoordAndPmf(const std::vector<DimParams>& dimParams,
1403                                   const BiasGrid&               grid,
1404                                   gmx::ArrayRef<const double>   probWeightNeighbor,
1405                                   double                        convolvedBias)
1406 {
1407     /* Sampling-based deconvolution extracting the PMF.
1408      * Update the PMF histogram with the current coordinate value.
1409      *
1410      * Because of the finite width of the harmonic potential, the free energy
1411      * defined for each coordinate point does not exactly equal that of the
1412      * actual coordinate, the PMF. However, the PMF can be estimated by applying
1413      * the relation exp(-PMF) = exp(-bias_convolved)*P_biased/Z, i.e. by keeping a
1414      * reweighted histogram of the coordinate value. Strictly, this relies on
1415      * the unknown normalization constant Z being either constant or known. Here,
1416      * neither is true except in the long simulation time limit. Empirically however,
1417      * it works (mainly because how the PMF histogram is rescaled).
1418      */
1419
1420     const int                gridPointIndex  = coordState_.gridpointIndex();
1421     const std::optional<int> lambdaAxisIndex = grid.lambdaAxisIndex();
1422
1423     /* Update the PMF of points along a lambda axis with their bias. */
1424     if (lambdaAxisIndex)
1425     {
1426         const std::vector<int>& neighbors = grid.point(gridPointIndex).neighbor;
1427
1428         std::vector<double> lambdaMarginalDistribution =
1429                 calculateFELambdaMarginalDistribution(grid, neighbors, probWeightNeighbor);
1430
1431         awh_dvec coordValueAlongLambda = { coordState_.coordValue()[0], coordState_.coordValue()[1],
1432                                            coordState_.coordValue()[2], coordState_.coordValue()[3] };
1433         for (size_t i = 0; i < neighbors.size(); i++)
1434         {
1435             const int neighbor = neighbors[i];
1436             double    bias;
1437             if (pointsAlongLambdaAxis(grid, gridPointIndex, neighbor))
1438             {
1439                 const double neighborLambda = grid.point(neighbor).coordValue[lambdaAxisIndex.value()];
1440                 if (neighbor == gridPointIndex)
1441                 {
1442                     bias = convolvedBias;
1443                 }
1444                 else
1445                 {
1446                     coordValueAlongLambda[lambdaAxisIndex.value()] = neighborLambda;
1447                     bias = calcConvolvedBias(dimParams, grid, coordValueAlongLambda);
1448                 }
1449
1450                 const double probWeight = lambdaMarginalDistribution[neighborLambda];
1451                 const double weightedBias = bias - std::log(std::max(probWeight, GMX_DOUBLE_MIN)); // avoid log(0)
1452
1453                 if (neighbor == gridPointIndex && grid.covers(coordState_.coordValue()))
1454                 {
1455                     points_[neighbor].samplePmf(weightedBias);
1456                 }
1457                 else
1458                 {
1459                     points_[neighbor].updatePmfUnvisited(weightedBias);
1460                 }
1461             }
1462         }
1463     }
1464     else
1465     {
1466         /* Only save coordinate data that is in range (the given index is always
1467          * in range even if the coordinate value is not).
1468          */
1469         if (grid.covers(coordState_.coordValue()))
1470         {
1471             /* Save PMF sum and keep a histogram of the sampled coordinate values */
1472             points_[gridPointIndex].samplePmf(convolvedBias);
1473         }
1474     }
1475
1476     /* Save probability weights for the update */
1477     sampleProbabilityWeights(grid, probWeightNeighbor);
1478 }
1479
1480 void BiasState::initHistoryFromState(AwhBiasHistory* biasHistory) const
1481 {
1482     biasHistory->pointState.resize(points_.size());
1483 }
1484
1485 void BiasState::updateHistory(AwhBiasHistory* biasHistory, const BiasGrid& grid) const
1486 {
1487     GMX_RELEASE_ASSERT(biasHistory->pointState.size() == points_.size(),
1488                        "The AWH history setup does not match the AWH state.");
1489
1490     AwhBiasStateHistory* stateHistory = &biasHistory->state;
1491     stateHistory->umbrellaGridpoint   = coordState_.umbrellaGridpoint();
1492
1493     for (size_t m = 0; m < biasHistory->pointState.size(); m++)
1494     {
1495         AwhPointStateHistory* psh = &biasHistory->pointState[m];
1496
1497         points_[m].storeState(psh);
1498
1499         psh->weightsum_covering = weightSumCovering_[m];
1500     }
1501
1502     histogramSize_.storeState(stateHistory);
1503
1504     stateHistory->origin_index_updatelist = multiDimGridIndexToLinear(grid, originUpdatelist_);
1505     stateHistory->end_index_updatelist    = multiDimGridIndexToLinear(grid, endUpdatelist_);
1506 }
1507
1508 void BiasState::restoreFromHistory(const AwhBiasHistory& biasHistory, const BiasGrid& grid)
1509 {
1510     const AwhBiasStateHistory& stateHistory = biasHistory.state;
1511
1512     coordState_.restoreFromHistory(stateHistory);
1513
1514     if (biasHistory.pointState.size() != points_.size())
1515     {
1516         GMX_THROW(
1517                 InvalidInputError("Bias grid size in checkpoint and simulation do not match. "
1518                                   "Likely you provided a checkpoint from a different simulation."));
1519     }
1520     for (size_t m = 0; m < points_.size(); m++)
1521     {
1522         points_[m].setFromHistory(biasHistory.pointState[m]);
1523     }
1524
1525     for (size_t m = 0; m < weightSumCovering_.size(); m++)
1526     {
1527         weightSumCovering_[m] = biasHistory.pointState[m].weightsum_covering;
1528     }
1529
1530     histogramSize_.restoreFromHistory(stateHistory);
1531
1532     linearGridindexToMultiDim(grid, stateHistory.origin_index_updatelist, originUpdatelist_);
1533     linearGridindexToMultiDim(grid, stateHistory.end_index_updatelist, endUpdatelist_);
1534 }
1535
1536 void BiasState::broadcast(const t_commrec* commRecord)
1537 {
1538     gmx_bcast(sizeof(coordState_), &coordState_, commRecord->mpi_comm_mygroup);
1539
1540     gmx_bcast(points_.size() * sizeof(PointState), points_.data(), commRecord->mpi_comm_mygroup);
1541
1542     gmx_bcast(weightSumCovering_.size() * sizeof(double), weightSumCovering_.data(),
1543               commRecord->mpi_comm_mygroup);
1544
1545     gmx_bcast(sizeof(histogramSize_), &histogramSize_, commRecord->mpi_comm_mygroup);
1546 }
1547
1548 void BiasState::setFreeEnergyToConvolvedPmf(const std::vector<DimParams>& dimParams, const BiasGrid& grid)
1549 {
1550     std::vector<float> convolvedPmf;
1551
1552     calcConvolvedPmf(dimParams, grid, &convolvedPmf);
1553
1554     for (size_t m = 0; m < points_.size(); m++)
1555     {
1556         points_[m].setFreeEnergy(convolvedPmf[m]);
1557     }
1558 }
1559
1560 /*! \brief
1561  * Count trailing data rows containing only zeros.
1562  *
1563  * \param[in] data        2D data array.
1564  * \param[in] numRows     Number of rows in array.
1565  * \param[in] numColumns  Number of cols in array.
1566  * \returns the number of trailing zero rows.
1567  */
1568 static int countTrailingZeroRows(const double* const* data, int numRows, int numColumns)
1569 {
1570     int numZeroRows = 0;
1571     for (int m = numRows - 1; m >= 0; m--)
1572     {
1573         bool rowIsZero = true;
1574         for (int d = 0; d < numColumns; d++)
1575         {
1576             if (data[d][m] != 0)
1577             {
1578                 rowIsZero = false;
1579                 break;
1580             }
1581         }
1582
1583         if (!rowIsZero)
1584         {
1585             /* At a row with non-zero data */
1586             break;
1587         }
1588         else
1589         {
1590             /* Still at a zero data row, keep checking rows higher up. */
1591             numZeroRows++;
1592         }
1593     }
1594
1595     return numZeroRows;
1596 }
1597
1598 /*! \brief
1599  * Initializes the PMF and target with data read from an input table.
1600  *
1601  * \param[in]     dimParams   The dimension parameters.
1602  * \param[in]     grid        The grid.
1603  * \param[in]     filename    The filename to read PMF and target from.
1604  * \param[in]     numBias     Number of biases.
1605  * \param[in]     biasIndex   The index of the bias.
1606  * \param[in,out] pointState  The state of the points in this bias.
1607  */
1608 static void readUserPmfAndTargetDistribution(const std::vector<DimParams>& dimParams,
1609                                              const BiasGrid&               grid,
1610                                              const std::string&            filename,
1611                                              int                           numBias,
1612                                              int                           biasIndex,
1613                                              std::vector<PointState>*      pointState)
1614 {
1615     /* Read the PMF and target distribution.
1616        From the PMF, the convolved PMF, or the reference value free energy, can be calculated
1617        base on the force constant. The free energy and target together determine the bias.
1618      */
1619     std::string filenameModified(filename);
1620     if (numBias > 1)
1621     {
1622         size_t n = filenameModified.rfind('.');
1623         GMX_RELEASE_ASSERT(n != std::string::npos,
1624                            "The filename should contain an extension starting with .");
1625         filenameModified.insert(n, formatString("%d", biasIndex));
1626     }
1627
1628     std::string correctFormatMessage = formatString(
1629             "%s is expected in the following format. "
1630             "The first ndim column(s) should contain the coordinate values for each point, "
1631             "each column containing values of one dimension (in ascending order). "
1632             "For a multidimensional coordinate, points should be listed "
1633             "in the order obtained by traversing lower dimensions first. "
1634             "E.g. for two-dimensional grid of size nxn: "
1635             "(1, 1), (1, 2),..., (1, n), (2, 1), (2, 2), ..., , (n, n - 1), (n, n). "
1636             "Column ndim +  1 should contain the PMF value for each coordinate value. "
1637             "The target distribution values should be in column ndim + 2  or column ndim + 5. "
1638             "Make sure the input file ends with a new line but has no trailing new lines.",
1639             filename.c_str());
1640     gmx::TextLineWrapper wrapper;
1641     wrapper.settings().setLineLength(c_linewidth);
1642     correctFormatMessage = wrapper.wrapToString(correctFormatMessage);
1643
1644     double** data;
1645     int      numColumns;
1646     int      numRows = read_xvg(filenameModified.c_str(), &data, &numColumns);
1647
1648     /* Check basic data properties here. BiasGrid takes care of more complicated things. */
1649
1650     if (numRows <= 0)
1651     {
1652         std::string mesg = gmx::formatString("%s is empty!.\n\n%s", filename.c_str(),
1653                                              correctFormatMessage.c_str());
1654         GMX_THROW(InvalidInputError(mesg));
1655     }
1656
1657     /* Less than 2 points is not useful for PMF or target. */
1658     if (numRows < 2)
1659     {
1660         std::string mesg = gmx::formatString(
1661                 "%s contains too few data points (%d)."
1662                 "The minimum number of points is 2.",
1663                 filename.c_str(), numRows);
1664         GMX_THROW(InvalidInputError(mesg));
1665     }
1666
1667     /* Make sure there are enough columns of data.
1668
1669        Two formats are allowed. Either with columns  {coords, PMF, target} or
1670        {coords, PMF, x, y, z, target, ...}. The latter format is allowed since that
1671        is how AWH output is written (x, y, z being other AWH variables). For this format,
1672        trailing columns are ignored.
1673      */
1674     int columnIndexTarget;
1675     int numColumnsMin  = dimParams.size() + 2;
1676     int columnIndexPmf = dimParams.size();
1677     if (numColumns == numColumnsMin)
1678     {
1679         columnIndexTarget = columnIndexPmf + 1;
1680     }
1681     else
1682     {
1683         columnIndexTarget = columnIndexPmf + 4;
1684     }
1685
1686     if (numColumns < numColumnsMin)
1687     {
1688         std::string mesg = gmx::formatString(
1689                 "The number of columns in %s should be at least %d."
1690                 "\n\n%s",
1691                 filename.c_str(), numColumnsMin, correctFormatMessage.c_str());
1692         GMX_THROW(InvalidInputError(mesg));
1693     }
1694
1695     /* read_xvg can give trailing zero data rows for trailing new lines in the input. We allow 1 zero row,
1696        since this could be real data. But multiple trailing zero rows cannot correspond to valid data. */
1697     int numZeroRows = countTrailingZeroRows(data, numRows, numColumns);
1698     if (numZeroRows > 1)
1699     {
1700         std::string mesg = gmx::formatString(
1701                 "Found %d trailing zero data rows in %s. Please remove trailing empty lines and "
1702                 "try again.",
1703                 numZeroRows, filename.c_str());
1704         GMX_THROW(InvalidInputError(mesg));
1705     }
1706
1707     /* Convert from user units to internal units before sending the data of to grid. */
1708     for (size_t d = 0; d < dimParams.size(); d++)
1709     {
1710         double scalingFactor = dimParams[d].scaleUserInputToInternal(1);
1711         if (scalingFactor == 1)
1712         {
1713             continue;
1714         }
1715         for (size_t m = 0; m < pointState->size(); m++)
1716         {
1717             data[d][m] *= scalingFactor;
1718         }
1719     }
1720
1721     /* Get a data point for each AWH grid point so that they all get data. */
1722     std::vector<int> gridIndexToDataIndex(grid.numPoints());
1723     mapGridToDataGrid(&gridIndexToDataIndex, data, numRows, filename, grid, correctFormatMessage);
1724
1725     /* Extract the data for each grid point.
1726      * We check if the target distribution is zero for all points.
1727      */
1728     bool targetDistributionIsZero = true;
1729     for (size_t m = 0; m < pointState->size(); m++)
1730     {
1731         (*pointState)[m].setLogPmfSum(-data[columnIndexPmf][gridIndexToDataIndex[m]]);
1732         double target = data[columnIndexTarget][gridIndexToDataIndex[m]];
1733
1734         /* Check if the values are allowed. */
1735         if (target < 0)
1736         {
1737             std::string mesg = gmx::formatString(
1738                     "Target distribution weight at point %zu (%g) in %s is negative.", m, target,
1739                     filename.c_str());
1740             GMX_THROW(InvalidInputError(mesg));
1741         }
1742         if (target > 0)
1743         {
1744             targetDistributionIsZero = false;
1745         }
1746         (*pointState)[m].setTargetConstantWeight(target);
1747     }
1748
1749     if (targetDistributionIsZero)
1750     {
1751         std::string mesg =
1752                 gmx::formatString("The target weights given in column %d in %s are all 0",
1753                                   columnIndexTarget, filename.c_str());
1754         GMX_THROW(InvalidInputError(mesg));
1755     }
1756
1757     /* Free the arrays. */
1758     for (int m = 0; m < numColumns; m++)
1759     {
1760         sfree(data[m]);
1761     }
1762     sfree(data);
1763 }
1764
1765 void BiasState::normalizePmf(int numSharingSims)
1766 {
1767     /* The normalization of the PMF estimate matters because it determines how big effect the next sample has.
1768        Approximately (for large enough force constant) we should have:
1769        sum_x(exp(-pmf(x)) = nsamples*sum_xref(exp(-f(xref)).
1770      */
1771
1772     /* Calculate the normalization factor, i.e. divide by the pmf sum, multiply by the number of samples and the f sum */
1773     double expSumPmf = 0;
1774     double expSumF   = 0;
1775     for (const PointState& pointState : points_)
1776     {
1777         if (pointState.inTargetRegion())
1778         {
1779             expSumPmf += std::exp(pointState.logPmfSum());
1780             expSumF += std::exp(-pointState.freeEnergy());
1781         }
1782     }
1783     double numSamples = histogramSize_.histogramSize() / numSharingSims;
1784
1785     /* Renormalize */
1786     double logRenorm = std::log(numSamples * expSumF / expSumPmf);
1787     for (PointState& pointState : points_)
1788     {
1789         if (pointState.inTargetRegion())
1790         {
1791             pointState.setLogPmfSum(pointState.logPmfSum() + logRenorm);
1792         }
1793     }
1794 }
1795
1796 void BiasState::initGridPointState(const AwhBiasParams&          awhBiasParams,
1797                                    const std::vector<DimParams>& dimParams,
1798                                    const BiasGrid&               grid,
1799                                    const BiasParams&             params,
1800                                    const std::string&            filename,
1801                                    int                           numBias)
1802 {
1803     /* Modify PMF, free energy and the constant target distribution factor
1804      * to user input values if there is data given.
1805      */
1806     if (awhBiasParams.bUserData)
1807     {
1808         readUserPmfAndTargetDistribution(dimParams, grid, filename, numBias, params.biasIndex, &points_);
1809         setFreeEnergyToConvolvedPmf(dimParams, grid);
1810     }
1811
1812     /* The local Boltzmann distribution is special because the target distribution is updated as a function of the reference weighthistogram. */
1813     GMX_RELEASE_ASSERT(params.eTarget != eawhtargetLOCALBOLTZMANN || points_[0].weightSumRef() != 0,
1814                        "AWH reference weight histogram not initialized properly with local "
1815                        "Boltzmann target distribution.");
1816
1817     updateTargetDistribution(points_, params);
1818
1819     for (PointState& pointState : points_)
1820     {
1821         if (pointState.inTargetRegion())
1822         {
1823             pointState.updateBias();
1824         }
1825         else
1826         {
1827             /* Note that for zero target this is a value that represents -infinity but should not be used for biasing. */
1828             pointState.setTargetToZero();
1829         }
1830     }
1831
1832     /* Set the initial reference weighthistogram. */
1833     const double histogramSize = histogramSize_.histogramSize();
1834     for (auto& pointState : points_)
1835     {
1836         pointState.setInitialReferenceWeightHistogram(histogramSize);
1837     }
1838
1839     /* Make sure the pmf is normalized consistently with the histogram size.
1840        Note: the target distribution and free energy need to be set here. */
1841     normalizePmf(params.numSharedUpdate);
1842 }
1843
1844 BiasState::BiasState(const AwhBiasParams&          awhBiasParams,
1845                      double                        histogramSizeInitial,
1846                      const std::vector<DimParams>& dimParams,
1847                      const BiasGrid&               grid) :
1848     coordState_(awhBiasParams, dimParams, grid),
1849     points_(grid.numPoints()),
1850     weightSumCovering_(grid.numPoints()),
1851     histogramSize_(awhBiasParams, histogramSizeInitial)
1852 {
1853     /* The minimum and maximum multidimensional point indices that are affected by the next update */
1854     for (size_t d = 0; d < dimParams.size(); d++)
1855     {
1856         int index            = grid.point(coordState_.gridpointIndex()).index[d];
1857         originUpdatelist_[d] = index;
1858         endUpdatelist_[d]    = index;
1859     }
1860 }
1861
1862 } // namespace gmx