Make AWH parameters proper C++
[alexxy/gromacs.git] / src / gromacs / applied_forces / awh / tests / biasgrid.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2017,2018,2019,2020,2021, 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 #include "gmxpre.h"
36
37 #include "gromacs/applied_forces/awh/biasgrid.h"
38
39 #include <cmath>
40
41 #include <memory>
42 #include <vector>
43
44 #include <gmock/gmock.h>
45 #include <gtest/gtest.h>
46
47 #include "gromacs/mdtypes/awh_params.h"
48 #include "gromacs/utility/gmxassert.h"
49 #include "gromacs/utility/inmemoryserializer.h"
50
51 #include "gromacs/applied_forces/awh/tests/awh_setup.h"
52 #include "testutils/testasserts.h"
53
54 namespace gmx
55 {
56
57 namespace test
58 {
59
60 TEST(biasGridTest, neighborhood)
61 {
62     constexpr double pointsPerScope = BiasGrid::c_scopeCutoff * BiasGrid::c_numPointsPerSigma;
63     GMX_RELEASE_ASSERT(std::abs(pointsPerScope - std::round(pointsPerScope)) > 1e-4,
64                        "If the scope is close to an integer number of points, this test can be "
65                        "unstable due to rounding issues");
66
67     const int scopeInPoints = static_cast<int>(pointsPerScope);
68
69     /* We test a 2-dimensional grid with dim0 periodic, dim1 not periodic.
70      * This test should cover most relevant cases: multi-dimension grids
71      * and non-periodic and periodic dimensions.
72      * The only thing not tested here is a periodic dimension with a gap.
73      */
74
75     const int                 numDim = 2;
76     std::vector<AwhDimParams> awhDimParams;
77     AwhCoordinateProviderType coordinateProvider = AwhCoordinateProviderType::Pull;
78     double                    diffusion          = 0.1;
79     {
80         int    coordIndex = 0;
81         double origin     = -5;
82         double end        = 5;
83         double period     = 10;
84         auto   awhDimBuffer =
85                 awhDimParamSerialized(coordinateProvider, coordIndex, origin, end, period, diffusion);
86         gmx::InMemoryDeserializer serializer(awhDimBuffer, false);
87         awhDimParams.emplace_back(AwhDimParams(&serializer));
88     }
89     {
90         int    coordIndex = 1;
91         double origin     = 0.5;
92         double end        = 2;
93         double period     = 0;
94         auto   awhDimBuffer =
95                 awhDimParamSerialized(coordinateProvider, coordIndex, origin, end, period, diffusion);
96         gmx::InMemoryDeserializer serializer(awhDimBuffer, false);
97         awhDimParams.emplace_back(AwhDimParams(&serializer));
98     }
99
100     const real conversionFactor = 1;
101     const real beta             = 3.0;
102
103     /* Set up dimParams to get about 15 points along each dimension */
104     std::vector<DimParams> dimParams;
105     dimParams.push_back(DimParams::pullDimParams(conversionFactor, 1 / (beta * 0.7 * 0.7), beta));
106     dimParams.push_back(DimParams::pullDimParams(conversionFactor, 1 / (beta * 0.1 * 0.1), beta));
107
108     BiasGrid grid(dimParams, awhDimParams);
109
110     const int numPoints = grid.numPoints();
111
112     int numPointsDim[numDim];
113     for (int d = 0; d < numDim; d++)
114     {
115         numPointsDim[d] = grid.axis(d).numPoints();
116
117         GMX_RELEASE_ASSERT(numPointsDim[d] >= 2 * scopeInPoints + 1,
118                            "The test code currently assume that the grid is larger of equal to the "
119                            "scope in each dimension");
120     }
121     int halfNumPoints0 = numPointsDim[0] / 2;
122
123     bool haveOutOfGridNeighbors  = false;
124     bool haveDuplicateNeighbors  = false;
125     bool haveIncorrectNeighbors  = false;
126     bool haveCorrectNumNeighbors = true;
127
128     /* Set up a grid for checking for duplicate neighbors */
129     std::vector<bool> isInNeighborhood(grid.numPoints(), false);
130
131     /* Checking for all points is overkill, we check every 7th */
132     for (size_t i = 0; i < grid.numPoints(); i += 7)
133     {
134         const GridPoint& point = grid.point(i);
135
136         /* NOTE: This code relies on major-minor index ordering in Grid */
137         int pointIndex0 = i / numPointsDim[1];
138         int pointIndex1 = i - pointIndex0 * numPointsDim[1];
139
140         /* To check if we have the correct neighbors, we check the expected
141          * number of neighbors, if all neighbors are within the grid bounds
142          * and if they are within scope.
143          */
144         int    distanceFromEdge1 = std::min(pointIndex1, numPointsDim[1] - 1 - pointIndex1);
145         size_t numNeighbors      = (2 * scopeInPoints + 1)
146                               * (scopeInPoints + std::min(scopeInPoints, distanceFromEdge1) + 1);
147         if (point.neighbor.size() != numNeighbors)
148         {
149             haveCorrectNumNeighbors = false;
150         }
151
152         for (auto& j : point.neighbor)
153         {
154             if (j >= 0 && j < numPoints)
155             {
156                 if (isInNeighborhood[j])
157                 {
158                     haveDuplicateNeighbors = true;
159                 }
160                 isInNeighborhood[j] = true;
161
162                 int neighborIndex0 = j / numPointsDim[1];
163                 int neighborIndex1 = j - neighborIndex0 * numPointsDim[1];
164
165                 int distance0 = neighborIndex0 - pointIndex0;
166                 int distance1 = neighborIndex1 - pointIndex1;
167                 /* Adjust distance for periodicity of dimension 0 */
168                 if (distance0 < -halfNumPoints0)
169                 {
170                     distance0 += numPointsDim[0];
171                 }
172                 else if (distance0 > halfNumPoints0)
173                 {
174                     distance0 -= numPointsDim[0];
175                 }
176                 /* Check if the distance is within scope */
177                 if (distance0 < -scopeInPoints || distance0 > scopeInPoints
178                     || distance1 < -scopeInPoints || distance1 > scopeInPoints)
179                 {
180                     haveIncorrectNeighbors = true;
181                 }
182             }
183             else
184             {
185                 haveOutOfGridNeighbors = true;
186             }
187         }
188
189         /* Clear the marked points in the checking grid */
190         for (auto& neighbor : point.neighbor)
191         {
192             if (neighbor >= 0 && neighbor < numPoints)
193             {
194                 isInNeighborhood[neighbor] = false;
195             }
196         }
197     }
198
199     EXPECT_FALSE(haveOutOfGridNeighbors);
200     EXPECT_FALSE(haveDuplicateNeighbors);
201     EXPECT_FALSE(haveIncorrectNeighbors);
202     EXPECT_TRUE(haveCorrectNumNeighbors);
203 }
204
205 } // namespace test
206 } // namespace gmx