f4db1606ced956509df3a728a9e01c559fc0feaa
[alexxy/gromacs.git] / src / gromacs / awh / biasgrid.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 functions in grid.h.
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 "biasgrid.h"
48
49 #include <cassert>
50 #include <cmath>
51 #include <cstring>
52
53 #include <algorithm>
54 #include <optional>
55
56 #include "gromacs/math/functions.h"
57 #include "gromacs/math/utilities.h"
58 #include "gromacs/mdtypes/awh_params.h"
59 #include "gromacs/utility/cstringutil.h"
60 #include "gromacs/utility/exceptions.h"
61 #include "gromacs/utility/gmxassert.h"
62 #include "gromacs/utility/smalloc.h"
63 #include "gromacs/utility/stringutil.h"
64
65 namespace gmx
66 {
67
68 namespace
69 {
70
71 /*! \brief
72  * Modify x so that it is periodic in [-period/2, +period/2).
73  *
74  * x is modified by shifting its value by a +/- a period if
75  * needed. Thus, it is assumed that x is at most one period
76  * away from this interval. For period = 0, x is not modified.
77  *
78  * \param[in,out] x       Pointer to the value to modify.
79  * \param[in]     period  The period, or 0 if not periodic.
80  */
81 void centerPeriodicValueAroundZero(double* x, double period)
82 {
83     GMX_ASSERT(period >= 0, "Periodic should not be negative");
84
85     const double halfPeriod = period * 0.5;
86
87     if (*x >= halfPeriod)
88     {
89         *x -= period;
90     }
91     else if (*x < -halfPeriod)
92     {
93         *x += period;
94     }
95 }
96
97 /*! \brief
98  * If period>0, retrun x so that it is periodic in [0, period), else return x.
99  *
100  * Return x is shifted its value by a +/- a period, if
101  * needed. Thus, it is assumed that x is at most one period
102  * away from this interval. For this domain and period > 0
103  * this is equivalent to x = x % period. For period = 0,
104  * x is not modified.
105  *
106  * \param[in,out] x       Pointer to the value to modify, should be >= 0.
107  * \param[in]     period  The period, or 0 if not periodic.
108  * \returns for period>0: index value witin [0, period), otherwise: \p x.
109  */
110 int indexWithinPeriod(int x, int period)
111 {
112     GMX_ASSERT(period >= 0, "Periodic should not be negative");
113
114     if (period == 0)
115     {
116         return x;
117     }
118
119     GMX_ASSERT(x > -period && x < 2 * period,
120                "x should not be more shifted by more than one period");
121
122     if (x >= period)
123     {
124         return x - period;
125     }
126     else if (x < 0)
127     {
128         return x + period;
129     }
130     else
131     {
132         return x;
133     }
134 }
135
136 /*! \brief
137  * Get the length of the interval (origin, end).
138  *
139  * This returns the distance obtained by connecting the origin point to
140  * the end point in the positive direction. Note that this is generally
141  * not the shortest distance. For period > 0, both origin and
142  * end are expected to take values in the same periodic interval,
143  * ie. |origin - end| < period.
144  *
145  * \param[in] origin    Start value of the interval.
146  * \param[in] end       End value of the interval.
147  * \param[in] period    The period, or 0 if not periodic.
148  * \returns the interval length from origin to end.
149  */
150 double getIntervalLengthPeriodic(double origin, double end, double period)
151 {
152     double length = end - origin;
153     if (length < 0)
154     {
155         /* The interval wraps around the +/- boundary which has a discontinuous jump of -period. */
156         length += period;
157     }
158
159     GMX_RELEASE_ASSERT(length >= 0, "Negative AWH grid axis length.");
160     GMX_RELEASE_ASSERT(period == 0 || length <= period, "Interval length longer than period.");
161
162     return length;
163 }
164
165 /*! \brief
166  * Get the deviation x - x0.
167  *
168  * For period > 0, the deviation with minimum absolute value is returned,
169  * i.e. with a value in the interval [-period/2, +period/2).
170  * Also for period > 0, it is assumed that |x - x0| < period.
171  *
172  * \param[in] x        From value.
173  * \param[in] x0       To value.
174  * \param[in] period   The period, or 0 if not periodic.
175  * \returns the deviation from x to x0.
176  */
177 double getDeviationPeriodic(double x, double x0, double period)
178 {
179     double dev = x - x0;
180
181     if (period > 0)
182     {
183         centerPeriodicValueAroundZero(&dev, period);
184     }
185
186     return dev;
187 }
188
189 } // namespace
190
191 double getDeviationFromPointAlongGridAxis(const BiasGrid& grid, int dimIndex, int pointIndex, double value)
192 {
193     double coordValue = grid.point(pointIndex).coordValue[dimIndex];
194
195     return getDeviationPeriodic(value, coordValue, grid.axis(dimIndex).period());
196 }
197
198 double getDeviationFromPointAlongGridAxis(const BiasGrid& grid, int dimIndex, int pointIndex1, int pointIndex2)
199 {
200     double coordValue1 = grid.point(pointIndex1).coordValue[dimIndex];
201     double coordValue2 = grid.point(pointIndex2).coordValue[dimIndex];
202
203     return getDeviationPeriodic(coordValue1, coordValue2, grid.axis(dimIndex).period());
204 }
205
206 bool pointsAlongLambdaAxis(const BiasGrid& grid, int pointIndex1, int pointIndex2)
207 {
208     if (!grid.hasLambdaAxis())
209     {
210         return false;
211     }
212     if (pointIndex1 == pointIndex2)
213     {
214         return true;
215     }
216     const int numDimensions = grid.numDimensions();
217     for (int d = 0; d < numDimensions; d++)
218     {
219         if (grid.axis(d).isFepLambdaAxis())
220         {
221             if (getDeviationFromPointAlongGridAxis(grid, d, pointIndex1, pointIndex2) == 0)
222             {
223                 return false;
224             }
225         }
226         else
227         {
228             if (getDeviationFromPointAlongGridAxis(grid, d, pointIndex1, pointIndex2) != 0)
229             {
230                 return false;
231             }
232         }
233     }
234     return true;
235 }
236
237 bool pointsHaveDifferentLambda(const BiasGrid& grid, int pointIndex1, int pointIndex2)
238 {
239     if (!grid.hasLambdaAxis())
240     {
241         return false;
242     }
243     if (pointIndex1 == pointIndex2)
244     {
245         return false;
246     }
247     const int numDimensions = grid.numDimensions();
248     for (int d = 0; d < numDimensions; d++)
249     {
250         if (grid.axis(d).isFepLambdaAxis())
251         {
252             if (getDeviationFromPointAlongGridAxis(grid, d, pointIndex1, pointIndex2) != 0)
253             {
254                 return true;
255             }
256         }
257     }
258     return false;
259 }
260
261 void linearArrayIndexToMultiDim(int indexLinear, int numDimensions, const awh_ivec numPointsDim, awh_ivec indexMulti)
262 {
263     for (int d = 0; d < numDimensions; d++)
264     {
265         int stride = 1;
266
267         for (int k = d + 1; k < numDimensions; k++)
268         {
269             stride *= numPointsDim[k];
270         }
271
272         indexMulti[d] = indexLinear / stride;
273         indexLinear -= indexMulti[d] * stride;
274     }
275 }
276
277 void linearGridindexToMultiDim(const BiasGrid& grid, int indexLinear, awh_ivec indexMulti)
278 {
279     awh_ivec  numPointsDim;
280     const int numDimensions = grid.numDimensions();
281     for (int d = 0; d < numDimensions; d++)
282     {
283         numPointsDim[d] = grid.axis(d).numPoints();
284     }
285
286     linearArrayIndexToMultiDim(indexLinear, numDimensions, numPointsDim, indexMulti);
287 }
288
289
290 int multiDimArrayIndexToLinear(const awh_ivec indexMulti, int numDimensions, const awh_ivec numPointsDim)
291 {
292     int stride      = 1;
293     int indexLinear = 0;
294     for (int d = numDimensions - 1; d >= 0; d--)
295     {
296         indexLinear += stride * indexMulti[d];
297         stride *= numPointsDim[d];
298     }
299
300     return indexLinear;
301 }
302
303 namespace
304 {
305
306 /*! \brief Convert a multidimensional grid point index to a linear one.
307  *
308  * \param[in] axis       The grid axes.
309  * \param[in] indexMulti Multidimensional grid point index to convert to a linear one.
310  * \returns the linear index.
311  */
312 int multiDimGridIndexToLinear(const std::vector<GridAxis>& axis, const awh_ivec indexMulti)
313 {
314     awh_ivec numPointsDim = { 0 };
315
316     for (size_t d = 0; d < axis.size(); d++)
317     {
318         numPointsDim[d] = axis[d].numPoints();
319     }
320
321     return multiDimArrayIndexToLinear(indexMulti, axis.size(), numPointsDim);
322 }
323
324 } // namespace
325
326 int multiDimGridIndexToLinear(const BiasGrid& grid, const awh_ivec indexMulti)
327 {
328     return multiDimGridIndexToLinear(grid.axis(), indexMulti);
329 }
330
331 namespace
332 {
333
334 /*! \brief
335  * Take a step in a multidimensional array.
336  *
337  * The multidimensional index gives the starting point to step from. Dimensions are
338  * stepped through in order of decreasing dimensional index such that the index is
339  * incremented in the highest dimension possible. If the starting point is the end
340  * of the array, a step cannot be taken and the index is not modified.
341  *
342  * \param[in] numDim        Number of dimensions of the array.
343  * \param[in] numPoints     Vector with the number of points along each dimension.
344  * \param[in,out] indexDim  Multidimensional index, each with values in [0, numPoints[d] - 1].
345  * \returns true if a step was taken, false if not.
346  */
347 bool stepInMultiDimArray(int numDim, const awh_ivec numPoints, awh_ivec indexDim)
348 {
349     bool haveStepped = false;
350
351     for (int d = numDim - 1; d >= 0 && !haveStepped; d--)
352     {
353         if (indexDim[d] < numPoints[d] - 1)
354         {
355             /* Not at a boundary, just increase by 1. */
356             indexDim[d]++;
357             haveStepped = true;
358         }
359         else
360         {
361             /* At a boundary. If we are not at the end of the array,
362                reset the index and check if we can step in higher dimensions */
363             if (d > 0)
364             {
365                 indexDim[d] = 0;
366             }
367         }
368     }
369
370     return haveStepped;
371 }
372
373 /*! \brief
374  * Transforms a grid point index to to the multidimensional index of a subgrid.
375  *
376  * The subgrid is defined by the location of its origin and the number of points
377  * along each dimension. The index transformation thus consists of a projection
378  * of the linear index onto each dimension, followed by a translation of the origin.
379  * The subgrid may have parts that don't overlap with the grid. E.g. the origin
380  * vector can have negative components meaning the origin lies outside of the grid.
381  * However, the given point needs to be both a grid and subgrid point.
382  *
383  * Periodic boundaries are taken care of by wrapping the subgrid around the grid.
384  * Thus, for periodic dimensions the number of subgrid points need to be less than
385  * the number of points in a period to prevent problems of wrapping around.
386  *
387  * \param[in]     grid            The grid.
388  * \param[in]     subgridOrigin   Vector locating the subgrid origin relative to the grid origin.
389  * \param[in]     subgridNpoints  The number of subgrid points in each dimension.
390  * \param[in]     point           BiasGrid point to get subgrid index for.
391  * \param[in,out] subgridIndex    Subgrid multidimensional index.
392  */
393 void gridToSubgridIndex(const BiasGrid& grid,
394                         const awh_ivec  subgridOrigin,
395                         const awh_ivec  subgridNpoints,
396                         int             point,
397                         awh_ivec        subgridIndex)
398 {
399     /* Get the subgrid index of the given grid point, for each dimension. */
400     for (int d = 0; d < grid.numDimensions(); d++)
401     {
402         /* The multidimensional grid point index relative to the subgrid origin. */
403         subgridIndex[d] = indexWithinPeriod(grid.point(point).index[d] - subgridOrigin[d],
404                                             grid.axis(d).numPointsInPeriod());
405
406         /* The given point should be in the subgrid. */
407         GMX_RELEASE_ASSERT((subgridIndex[d] >= 0) && (subgridIndex[d] < subgridNpoints[d]),
408                            "Attempted to convert an AWH grid point index not in subgrid to out of "
409                            "bounds subgrid index");
410     }
411 }
412
413 /*! \brief
414  * Transform a multidimensional subgrid index to a grid point index.
415  *
416  * If the given subgrid point is not a grid point the transformation will not be successful
417  * and the grid point index will not be set. Periodic boundaries are taken care of by
418  * wrapping the subgrid around the grid.
419  *
420  * \param[in]     grid           The grid.
421  * \param[in]     subgridOrigin  Vector locating the subgrid origin relative to the grid origin.
422  * \param[in]     subgridIndex   Subgrid multidimensional index to get grid point index for.
423  * \param[in,out] gridIndex      BiasGrid point index.
424  * \returns true if the transformation was successful.
425  */
426 bool subgridToGridIndex(const BiasGrid& grid, const awh_ivec subgridOrigin, const awh_ivec subgridIndex, int* gridIndex)
427 {
428     awh_ivec globalIndexDim;
429
430     /* Check and apply boundary conditions for each dimension */
431     for (int d = 0; d < grid.numDimensions(); d++)
432     {
433         /* Transform to global multidimensional indexing by adding the origin */
434         globalIndexDim[d] = subgridOrigin[d] + subgridIndex[d];
435
436         /* The local grid is allowed to stick out on the edges of the global grid. Here the boundary conditions are applied.*/
437         if (globalIndexDim[d] < 0 || globalIndexDim[d] > grid.axis(d).numPoints() - 1)
438         {
439             /* Try to wrap around if periodic. Otherwise, the transformation failed so return. */
440             if (!grid.axis(d).isPeriodic())
441             {
442                 return false;
443             }
444
445             /* The grid might not contain a whole period. Can only wrap around if this gap is not too large. */
446             int gap = grid.axis(d).numPointsInPeriod() - grid.axis(d).numPoints();
447
448             int bridge;
449             int numWrapped;
450             if (globalIndexDim[d] < 0)
451             {
452                 bridge     = -globalIndexDim[d];
453                 numWrapped = bridge - gap;
454                 if (numWrapped > 0)
455                 {
456                     globalIndexDim[d] = grid.axis(d).numPoints() - numWrapped;
457                 }
458             }
459             else
460             {
461                 bridge     = globalIndexDim[d] - (grid.axis(d).numPoints() - 1);
462                 numWrapped = bridge - gap;
463                 if (numWrapped > 0)
464                 {
465                     globalIndexDim[d] = numWrapped - 1;
466                 }
467             }
468
469             if (numWrapped <= 0)
470             {
471                 return false;
472             }
473         }
474     }
475
476     /* Translate from multidimensional to linear indexing and set the return value */
477     (*gridIndex) = multiDimGridIndexToLinear(grid, globalIndexDim);
478
479     return true;
480 }
481
482 } // namespace
483
484 bool advancePointInSubgrid(const BiasGrid& grid,
485                            const awh_ivec  subgridOrigin,
486                            const awh_ivec  subgridNumPoints,
487                            int*            gridPointIndex)
488 {
489     /* Initialize the subgrid index to the subgrid origin. */
490     awh_ivec subgridIndex = { 0 };
491
492     /* Get the subgrid index of the given grid point index. */
493     if (*gridPointIndex >= 0)
494     {
495         gridToSubgridIndex(grid, subgridOrigin, subgridNumPoints, *gridPointIndex, subgridIndex);
496     }
497     else
498     {
499         /* If no grid point is given we start at the subgrid origin (which subgridIndex is initialized to).
500            If this is a valid grid point then we're done, otherwise keep looking below. */
501         /* TODO: separate into a separate function (?) */
502         if (subgridToGridIndex(grid, subgridOrigin, subgridIndex, gridPointIndex))
503         {
504             return true;
505         }
506     }
507
508     /* Traverse the subgrid and look for the first point that is also in the grid. */
509     while (stepInMultiDimArray(grid.numDimensions(), subgridNumPoints, subgridIndex))
510     {
511         /* If this is a valid grid point, the grid point index is updated.*/
512         if (subgridToGridIndex(grid, subgridOrigin, subgridIndex, gridPointIndex))
513         {
514             return true;
515         }
516     }
517
518     return false;
519 }
520
521 /*! \brief
522  * Returns the point distance between from value x to value x0 along the given axis.
523  *
524  * Note that the returned distance may be negative or larger than the
525  * number of points in the axis. For a periodic axis, the distance is chosen
526  * to be in [0, period), i.e. always positive but not the shortest one.
527  *
528  * \param[in]  axis   BiasGrid axis.
529  * \param[in]  x      From value.
530  * \param[in]  x0     To value.
531  * \returns (x - x0) in number of points.
532  */
533 static int pointDistanceAlongAxis(const GridAxis& axis, double x, double x0)
534 {
535     int distance = 0;
536
537     if (axis.spacing() > 0)
538     {
539         /* Get the real-valued distance. For a periodic axis, the shortest one. */
540         double period = axis.period();
541         double dx     = getDeviationPeriodic(x, x0, period);
542
543         /* Transform the distance into a point distance by rounding. */
544         distance = gmx::roundToInt(dx / axis.spacing());
545
546         /* If periodic, shift the point distance to be in [0, period) */
547         distance = indexWithinPeriod(distance, axis.numPointsInPeriod());
548     }
549
550     return distance;
551 }
552
553 /*! \brief
554  * Query if a value is in range of the grid.
555  *
556  * \param[in] value   Value to check.
557  * \param[in] axis    The grid axes.
558  * \returns true if the value is in the grid.
559  */
560 static bool valueIsInGrid(const awh_dvec value, const std::vector<GridAxis>& axis)
561 {
562     /* For each dimension get the one-dimensional index and check if it is in range. */
563     for (size_t d = 0; d < axis.size(); d++)
564     {
565         /* The index is computed as the point distance from the origin. */
566         int index = pointDistanceAlongAxis(axis[d], value[d], axis[d].origin());
567
568         if (!(index >= 0 && index < axis[d].numPoints()))
569         {
570             return false;
571         }
572     }
573
574     return true;
575 }
576
577 bool BiasGrid::covers(const awh_dvec value) const
578 {
579     return valueIsInGrid(value, axis());
580 }
581
582 std::optional<int> BiasGrid::lambdaAxisIndex() const
583 {
584     for (size_t i = 0; i < axis_.size(); i++)
585     {
586         if (axis_[i].isFepLambdaAxis())
587         {
588             return i;
589         }
590     }
591     return {};
592 }
593
594 int BiasGrid::numFepLambdaStates() const
595 {
596     for (size_t i = 0; i < axis_.size(); i++)
597     {
598         if (axis_[i].isFepLambdaAxis())
599         {
600             return axis_[i].numPoints();
601         }
602     }
603     return 0;
604 }
605
606 int GridAxis::nearestIndex(double value) const
607 {
608     /* Get the point distance to the origin. This may by an out of index range for the axis. */
609     int index = pointDistanceAlongAxis(*this, value, origin_);
610
611     if (index < 0 || index >= numPoints_)
612     {
613         if (isPeriodic())
614         {
615             GMX_RELEASE_ASSERT(index >= 0 && index < numPointsInPeriod_,
616                                "Index not in periodic interval 0 for AWH periodic axis");
617             int endDistance    = (index - (numPoints_ - 1));
618             int originDistance = (numPointsInPeriod_ - index);
619             index              = originDistance < endDistance ? 0 : numPoints_ - 1;
620         }
621         else
622         {
623             index = (index < 0) ? 0 : (numPoints_ - 1);
624         }
625     }
626
627     return index;
628 }
629
630 /*! \brief
631  * Map a value to the nearest point in the grid.
632  *
633  * \param[in] value  Value.
634  * \param[in] axis   The grid axes.
635  * \returns the point index nearest to the value.
636  */
637 static int getNearestIndexInGrid(const awh_dvec value, const std::vector<GridAxis>& axis)
638 {
639     awh_ivec indexMulti;
640
641     /* If the index is out of range, modify it so that it is in range by choosing the nearest point on the edge. */
642     for (size_t d = 0; d < axis.size(); d++)
643     {
644         indexMulti[d] = axis[d].nearestIndex(value[d]);
645     }
646
647     return multiDimGridIndexToLinear(axis, indexMulti);
648 }
649
650 int BiasGrid::nearestIndex(const awh_dvec value) const
651 {
652     return getNearestIndexInGrid(value, axis());
653 }
654
655 namespace
656 {
657
658 /*! \brief
659  * Find and set the neighbors of a grid point.
660  *
661  * The search space for neighbors is a subgrid with size set by a scope cutoff.
662  * In general not all point within scope will be valid grid points.
663  *
664  * \param[in]     pointIndex           BiasGrid point index.
665  * \param[in]     grid                 The grid.
666  * \param[in,out] neighborIndexArray   Array to fill with neighbor indices.
667  */
668 void setNeighborsOfGridPoint(int pointIndex, const BiasGrid& grid, std::vector<int>* neighborIndexArray)
669 {
670     const int c_maxNeighborsAlongAxis =
671             1 + 2 * static_cast<int>(BiasGrid::c_numPointsPerSigma * BiasGrid::c_scopeCutoff);
672
673     awh_ivec numCandidates = { 0 };
674     awh_ivec subgridOrigin = { 0 };
675     for (int d = 0; d < grid.numDimensions(); d++)
676     {
677         if (grid.axis(d).isFepLambdaAxis())
678         {
679             /* Use all points along an axis linked to FEP */
680             numCandidates[d] = grid.axis(d).numPoints();
681             subgridOrigin[d] = 0;
682         }
683         else
684         {
685             /* The number of candidate points along this dimension is given by the scope cutoff. */
686             numCandidates[d] = std::min(c_maxNeighborsAlongAxis, grid.axis(d).numPoints());
687
688             /* The origin of the subgrid to search */
689             int centerIndex  = grid.point(pointIndex).index[d];
690             subgridOrigin[d] = centerIndex - numCandidates[d] / 2;
691         }
692     }
693
694     /* Find and set the neighbors */
695     int  neighborIndex = -1;
696     bool aPointExists  = true;
697
698     /* Keep looking for grid points while traversing the subgrid. */
699     while (aPointExists)
700     {
701         /* The point index is updated if a grid point was found. */
702         aPointExists = advancePointInSubgrid(grid, subgridOrigin, numCandidates, &neighborIndex);
703
704         if (aPointExists)
705         {
706             neighborIndexArray->push_back(neighborIndex);
707         }
708     }
709 }
710
711 } // namespace
712
713 void BiasGrid::initPoints()
714 {
715     awh_ivec numPointsDimWork = { 0 };
716     awh_ivec indexWork        = { 0 };
717
718     for (size_t d = 0; d < axis_.size(); d++)
719     {
720         /* Temporarily gather the number of points in each dimension in one array */
721         numPointsDimWork[d] = axis_[d].numPoints();
722     }
723
724     for (auto& point : point_)
725     {
726         for (size_t d = 0; d < axis_.size(); d++)
727         {
728             if (axis_[d].isFepLambdaAxis())
729             {
730                 point.coordValue[d] = indexWork[d];
731             }
732             else
733             {
734                 point.coordValue[d] = axis_[d].origin() + indexWork[d] * axis_[d].spacing();
735             }
736
737             if (axis_[d].period() > 0)
738             {
739                 /* Do we always want the values to be centered around 0 ? */
740                 centerPeriodicValueAroundZero(&point.coordValue[d], axis_[d].period());
741             }
742
743             point.index[d] = indexWork[d];
744         }
745
746         stepInMultiDimArray(axis_.size(), numPointsDimWork, indexWork);
747     }
748 }
749
750 GridAxis::GridAxis(double origin, double end, double period, double pointDensity) :
751     origin_(origin),
752     period_(period),
753     isFepLambdaAxis_(false)
754 {
755     length_ = getIntervalLengthPeriodic(origin_, end, period_);
756
757     /* Automatically determine number of points based on the user given endpoints
758        and the expected fluctuations in the umbrella. */
759     if (length_ == 0)
760     {
761         numPoints_ = 1;
762     }
763     else if (pointDensity == 0)
764     {
765         numPoints_ = 2;
766     }
767     else
768     {
769         /* An extra point is added here to account for the endpoints. The
770            minimum number of points for a non-zero interval is 2. */
771         numPoints_ = 1 + static_cast<int>(std::ceil(length_ * pointDensity));
772     }
773
774     /* Set point spacing based on the number of points */
775     if (isPeriodic())
776     {
777         /* Set the grid spacing so that a period is matched exactly by an integer number of points.
778            The number of points in a period is equal to the number of grid spacings in a period
779            since the endpoints are connected.  */
780         numPointsInPeriod_ =
781                 length_ > 0 ? static_cast<int>(std::ceil(period / length_ * (numPoints_ - 1))) : 1;
782         spacing_ = period_ / numPointsInPeriod_;
783
784         /* Modify the number of grid axis points to be compatible with the period dependent spacing. */
785         numPoints_ = std::min(static_cast<int>(round(length_ / spacing_)) + 1, numPointsInPeriod_);
786     }
787     else
788     {
789         numPointsInPeriod_ = 0;
790         spacing_           = numPoints_ > 1 ? length_ / (numPoints_ - 1) : 0;
791     }
792 }
793
794 GridAxis::GridAxis(double origin, double end, double period, int numPoints, bool isFepLambdaAxis) :
795     origin_(origin),
796     period_(period),
797     numPoints_(numPoints),
798     isFepLambdaAxis_(isFepLambdaAxis)
799 {
800     if (isFepLambdaAxis)
801     {
802         length_            = end - origin_;
803         spacing_           = 1;
804         numPointsInPeriod_ = numPoints;
805     }
806     else
807     {
808         length_            = getIntervalLengthPeriodic(origin_, end, period_);
809         spacing_           = numPoints_ > 1 ? length_ / (numPoints_ - 1) : period_;
810         numPointsInPeriod_ = static_cast<int>(std::round(period_ / spacing_));
811     }
812 }
813
814 BiasGrid::BiasGrid(const std::vector<DimParams>& dimParams, const AwhDimParams* awhDimParams)
815 {
816     /* Define the discretization along each dimension */
817     awh_dvec period;
818     int      numPoints = 1;
819     for (size_t d = 0; d < dimParams.size(); d++)
820     {
821         double origin = dimParams[d].scaleUserInputToInternal(awhDimParams[d].origin);
822         double end    = dimParams[d].scaleUserInputToInternal(awhDimParams[d].end);
823         if (awhDimParams[d].eCoordProvider == eawhcoordproviderPULL)
824         {
825             period[d] = dimParams[d].scaleUserInputToInternal(awhDimParams[d].period);
826             static_assert(
827                     c_numPointsPerSigma >= 1.0,
828                     "The number of points per sigma should be at least 1.0 to get a uniformly "
829                     "covering the reaction using Gaussians");
830             double pointDensity = std::sqrt(dimParams[d].betak) * c_numPointsPerSigma;
831             axis_.emplace_back(origin, end, period[d], pointDensity);
832         }
833         else
834         {
835             axis_.emplace_back(origin, end, 0, dimParams[d].numFepLambdaStates, true);
836         }
837         numPoints *= axis_[d].numPoints();
838     }
839
840     point_.resize(numPoints);
841
842     /* Set their values */
843     initPoints();
844
845     /* Keep a neighbor list for each point.
846      * Note: could also generate neighbor list only when needed
847      * instead of storing them for each point.
848      */
849     for (size_t m = 0; m < point_.size(); m++)
850     {
851         std::vector<int>* neighbor = &point_[m].neighbor;
852
853         setNeighborsOfGridPoint(m, *this, neighbor);
854     }
855 }
856
857 void mapGridToDataGrid(std::vector<int>*    gridpointToDatapoint,
858                        const double* const* data,
859                        int                  numDataPoints,
860                        const std::string&   dataFilename,
861                        const BiasGrid&      grid,
862                        const std::string&   correctFormatMessage)
863 {
864     /* Transform the data into a grid in order to map each grid point to a data point
865        using the grid functions. */
866
867     /* Count the number of points for each dimension. Each dimension
868        has its own stride. */
869     int               stride           = 1;
870     int               numPointsCounted = 0;
871     std::vector<int>  numPoints(grid.numDimensions());
872     std::vector<bool> isFepLambdaAxis(grid.numDimensions());
873     for (int d = grid.numDimensions() - 1; d >= 0; d--)
874     {
875         int    numPointsInDim = 0;
876         int    pointIndex     = 0;
877         double firstValue     = data[d][pointIndex];
878         do
879         {
880             numPointsInDim++;
881             pointIndex += stride;
882         } while (pointIndex < numDataPoints
883                  && !gmx_within_tol(firstValue, data[d][pointIndex], GMX_REAL_EPS));
884
885         /* The stride in dimension dimension d - 1 equals the number of points
886            dimension d. */
887         stride = numPointsInDim;
888
889         numPointsCounted = (numPointsCounted == 0) ? numPointsInDim : numPointsCounted * numPointsInDim;
890
891         numPoints[d]       = numPointsInDim;
892         isFepLambdaAxis[d] = grid.axis(d).isFepLambdaAxis();
893     }
894
895     if (numPointsCounted != numDataPoints)
896     {
897         std::string mesg = gmx::formatString(
898                 "Could not extract data properly from %s. Wrong data format?"
899                 "\n\n%s",
900                 dataFilename.c_str(), correctFormatMessage.c_str());
901         GMX_THROW(InvalidInputError(mesg));
902     }
903
904     std::vector<GridAxis> axis_;
905     axis_.reserve(grid.numDimensions());
906     /* The data grid has the data that was read and the properties of the AWH grid */
907     for (int d = 0; d < grid.numDimensions(); d++)
908     {
909         if (isFepLambdaAxis[d])
910         {
911             axis_.emplace_back(data[d][0], data[d][numDataPoints - 1], 0, numPoints[d], true);
912         }
913         else
914         {
915             axis_.emplace_back(data[d][0], data[d][numDataPoints - 1], grid.axis(d).period(),
916                                numPoints[d], false);
917         }
918     }
919
920     /* Map each grid point to a data point. No interpolation, just pick the nearest one.
921      * It is assumed that the given data is uniformly spaced for each dimension.
922      */
923     for (size_t m = 0; m < grid.numPoints(); m++)
924     {
925         /* We only define what we need for the datagrid since it's not needed here which is a bit ugly */
926
927         if (!valueIsInGrid(grid.point(m).coordValue, axis_))
928         {
929             std::string mesg = gmx::formatString(
930                     "%s does not contain data for all coordinate values. "
931                     "Make sure your input data covers the whole sampling domain "
932                     "and is correctly formatted. \n\n%s",
933                     dataFilename.c_str(), correctFormatMessage.c_str());
934             GMX_THROW(InvalidInputError(mesg));
935         }
936         (*gridpointToDatapoint)[m] = getNearestIndexInGrid(grid.point(m).coordValue, axis_);
937     }
938 }
939
940 } // namespace gmx