2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2018,2019,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.
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.
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.
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.
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.
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.
37 * Tests for the update groups functionality.
39 * \author berk Hess <hess@kth.se>
40 * \ingroup module_mdlib
44 #include "gromacs/mdlib/updategroups.h"
46 #include <gtest/gtest.h>
48 #include "gromacs/topology/topology.h"
50 #include "testutils/loggertest.h"
51 #include "testutils/testasserts.h"
59 /* TODO: Actually initialize moltype.atoms.atom when this is converted to C++ */
61 /*! \brief Returns a flexible ethane united-atom molecule */
62 gmx_moltype_t flexibleEthaneUA()
64 gmx_moltype_t moltype = {};
67 moltype.ilist[F_BONDS].iatoms = { 0, 0, 1 };
72 /*! \brief Returns an ethane united-atom molecule */
73 gmx_moltype_t ethaneUA()
75 gmx_moltype_t moltype = {};
78 moltype.ilist[F_CONSTR].iatoms = { 0, 0, 1 };
83 /*! \brief Returns a methane molecule */
84 gmx_moltype_t methane()
86 gmx_moltype_t moltype = {};
89 moltype.ilist[F_CONSTR].iatoms = { 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 4 };
94 /*! \brief Returns an ethane molecule */
95 gmx_moltype_t ethane()
97 gmx_moltype_t moltype = {};
100 moltype.ilist[F_CONSTR].iatoms = { 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 4, 5, 0, 4, 6, 0, 4, 7 };
101 moltype.ilist[F_ANGLES].iatoms = { 1, 1, 0, 2, 1, 1, 0, 3, 1, 2, 0, 3,
102 1, 5, 4, 6, 1, 5, 4, 7, 1, 6, 4, 7 };
107 /*! \brief Returns a butane fully-constrained united-atom molecule */
108 gmx_moltype_t butaneUA()
110 gmx_moltype_t moltype = {};
112 moltype.atoms.nr = 4;
113 moltype.ilist[F_CONSTR].iatoms = { 0, 0, 1, 0, 1, 2, 0, 2, 3 };
118 /*! \brief Returns a three-site water molecule */
119 gmx_moltype_t waterThreeSite()
121 gmx_moltype_t moltype = {};
123 moltype.atoms.nr = 3;
124 moltype.ilist[F_SETTLE].iatoms = { 0, 0, 1, 2 };
129 /*! \brief Returns a four-site water molecule with virtual site */
130 gmx_moltype_t waterFourSite()
132 gmx_moltype_t moltype = {};
134 moltype.atoms.nr = 4;
135 moltype.ilist[F_SETTLE].iatoms = { 0, 1, 2, 3 };
136 moltype.ilist[F_VSITE3].iatoms = { 1, 0, 1, 2, 3 };
141 /*! \brief Returns a water molecule with flexible angle */
142 gmx_moltype_t waterFlexAngle()
144 gmx_moltype_t moltype = {};
146 moltype.atoms.nr = 3;
147 moltype.ilist[F_CONSTR].iatoms = {
150 moltype.ilist[F_ANGLES].iatoms = {
160 //! Test fixture class
161 class UpdateGroupsTest : public ::testing::Test
164 //! Global toplogy to use in tests
166 //! Default temperature for tests
167 real temperature_ = 298;
168 //! Logger to use in tests
169 test::LoggerTestHelper logHelper_;
172 TEST_F(UpdateGroupsTest, WithEthaneUA)
174 mtop_.moltype.emplace_back(ethaneUA());
177 iparams.constr = { 0.3, 0.3 };
178 mtop_.ffparams.iparams.push_back(iparams);
181 auto updateGroupingsPerMoleculeType = gmx::makeUpdateGroupingsPerMoleculeType(mtop_);
183 ASSERT_EQ(updateGroupingsPerMoleculeType.size(), 1);
184 EXPECT_EQ(updateGroupingsPerMoleculeType[0].numBlocks(), 1);
186 real maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
187 EXPECT_FLOAT_EQ(maxRadius, 0.3 / 2);
189 logHelper_.expectNoEntries(MDLogger::LogLevel::Info);
190 UpdateGroups updateGroups = makeUpdateGroups(
191 logHelper_.logger(), std::move(updateGroupingsPerMoleculeType), maxRadius, true, true, 1e6_real);
194 TEST_F(UpdateGroupsTest, WithMethane)
196 mtop_.moltype.emplace_back(methane());
199 iparams.constr = { 0.1, 0.1 };
200 mtop_.ffparams.iparams.push_back(iparams);
203 auto updateGroupingsPerMoleculeType = gmx::makeUpdateGroupingsPerMoleculeType(mtop_);
205 ASSERT_EQ(updateGroupingsPerMoleculeType.size(), 1);
206 EXPECT_EQ(updateGroupingsPerMoleculeType[0].numBlocks(), 1);
208 real maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
209 EXPECT_FLOAT_EQ(maxRadius, 0.14);
211 logHelper_.expectNoEntries(MDLogger::LogLevel::Info);
212 UpdateGroups updateGroups = makeUpdateGroups(
213 logHelper_.logger(), std::move(updateGroupingsPerMoleculeType), maxRadius, true, true, 1e6_real);
216 TEST_F(UpdateGroupsTest, WithEthane)
218 mtop_.moltype.emplace_back(ethane());
221 iparams.constr = { 0.1, 0.1 };
222 mtop_.ffparams.iparams.push_back(iparams);
223 iparams.harmonic = { 107.800, 276.144, 107.800, 276.144 };
224 mtop_.ffparams.iparams.push_back(iparams);
227 auto updateGroupingsPerMoleculeType = gmx::makeUpdateGroupingsPerMoleculeType(mtop_);
229 ASSERT_EQ(updateGroupingsPerMoleculeType.size(), 1);
230 EXPECT_EQ(updateGroupingsPerMoleculeType[0].numBlocks(), 2);
232 real maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
233 EXPECT_FLOAT_EQ(maxRadius, 0.094746813);
235 logHelper_.expectNoEntries(MDLogger::LogLevel::Info);
236 UpdateGroups updateGroups = makeUpdateGroups(
237 logHelper_.logger(), std::move(updateGroupingsPerMoleculeType), maxRadius, true, true, 1e6_real);
240 TEST_F(UpdateGroupsTest, CheckRadiusCalculationAtDifferentTemperaturesWithEthane)
242 mtop_.moltype.emplace_back(ethane());
245 iparams.constr = { 0.1, 0.1 };
246 mtop_.ffparams.iparams.push_back(iparams);
247 iparams.harmonic = { 107.800, 276.144, 107.800, 276.144 };
248 mtop_.ffparams.iparams.push_back(iparams);
251 auto updateGroupingsPerMoleculeType = gmx::makeUpdateGroupingsPerMoleculeType(mtop_);
253 ASSERT_EQ(updateGroupingsPerMoleculeType.size(), 1);
254 EXPECT_EQ(updateGroupingsPerMoleculeType[0].numBlocks(), 2);
256 real maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
257 EXPECT_FLOAT_EQ(maxRadius, 0.094746813);
259 // Observe that the temperature affects the radius only when valid
261 maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
262 EXPECT_FLOAT_EQ(maxRadius, 0.10310466);
265 maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
266 EXPECT_FLOAT_EQ(maxRadius, 0.125);
269 TEST_F(UpdateGroupsTest, WithButaneUALogsThatUnsuitableForUpdateGroups)
271 mtop_.moltype.emplace_back(butaneUA());
274 iparams.constr = { 0.3, 0.3 };
275 mtop_.ffparams.iparams.push_back(iparams);
278 auto updateGroupingsPerMoleculeType = gmx::makeUpdateGroupingsPerMoleculeType(mtop_);
280 EXPECT_EQ(updateGroupingsPerMoleculeType.size(), 0);
282 real maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
283 EXPECT_FLOAT_EQ(maxRadius, 0.0);
285 logHelper_.expectEntryMatchingRegex(
286 MDLogger::LogLevel::Info,
287 "At least one moleculetype does not conform to the requirements");
288 UpdateGroups updateGroups = makeUpdateGroups(
289 logHelper_.logger(), std::move(updateGroupingsPerMoleculeType), maxRadius, true, true, 1e6_real);
292 TEST_F(UpdateGroupsTest, WithWaterThreeSite)
294 mtop_.moltype.emplace_back(waterThreeSite());
297 iparams.settle = { 0.1, 0.1633 };
298 mtop_.ffparams.iparams.push_back(iparams);
301 auto updateGroupingsPerMoleculeType = gmx::makeUpdateGroupingsPerMoleculeType(mtop_);
303 ASSERT_EQ(updateGroupingsPerMoleculeType.size(), 1);
304 EXPECT_EQ(updateGroupingsPerMoleculeType[0].numBlocks(), 1);
306 real maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
307 EXPECT_FLOAT_EQ(maxRadius, 0.083887339);
309 logHelper_.expectNoEntries(MDLogger::LogLevel::Info);
310 UpdateGroups updateGroups = makeUpdateGroups(
311 logHelper_.logger(), std::move(updateGroupingsPerMoleculeType), maxRadius, true, true, 1e6_real);
314 // Tests update group with virtual site
315 TEST_F(UpdateGroupsTest, WithWaterFourSite)
317 mtop_.moltype.emplace_back(waterFourSite());
319 t_iparams iparams[2];
320 iparams[0].settle = { 0.1, 0.1633 };
321 iparams[1].vsite = { 0.128, 0.128 };
322 mtop_.ffparams.iparams.push_back(iparams[0]);
323 mtop_.ffparams.iparams.push_back(iparams[1]);
326 auto updateGroupingsPerMoleculeType = gmx::makeUpdateGroupingsPerMoleculeType(mtop_);
328 ASSERT_EQ(updateGroupingsPerMoleculeType.size(), 1);
329 EXPECT_EQ(updateGroupingsPerMoleculeType[0].numBlocks(), 1);
331 real maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
332 EXPECT_FLOAT_EQ(maxRadius, 0.083887339);
334 logHelper_.expectNoEntries(MDLogger::LogLevel::Info);
335 UpdateGroups updateGroups = makeUpdateGroups(
336 logHelper_.logger(), std::move(updateGroupingsPerMoleculeType), maxRadius, true, true, 1e6_real);
339 TEST_F(UpdateGroupsTest, WithFourAtomsWithSettle)
341 mtop_.moltype.emplace_back(waterThreeSite());
342 mtop_.moltype.back().atoms.nr = 4;
344 t_iparams iparams[2];
345 iparams[0].settle = { 0.1, 0.1633 };
346 iparams[1].vsite = { 0.128, 0.128 };
347 mtop_.ffparams.iparams.push_back(iparams[0]);
348 mtop_.ffparams.iparams.push_back(iparams[1]);
351 auto updateGroupingsPerMoleculeType = gmx::makeUpdateGroupingsPerMoleculeType(mtop_);
353 ASSERT_EQ(updateGroupingsPerMoleculeType.size(), 1);
354 EXPECT_EQ(updateGroupingsPerMoleculeType[0].numBlocks(), 2);
356 real maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
357 EXPECT_FLOAT_EQ(maxRadius, 0.083887339);
359 logHelper_.expectNoEntries(MDLogger::LogLevel::Info);
360 UpdateGroups updateGroups = makeUpdateGroups(
361 logHelper_.logger(), std::move(updateGroupingsPerMoleculeType), maxRadius, true, true, 1e6_real);
364 // Tests groups with two constraints and an angle potential
365 TEST_F(UpdateGroupsTest, WithWaterFlexAngle)
367 mtop_.moltype.emplace_back(waterFlexAngle());
370 iparams.constr = { 0.1, 0.1 };
371 mtop_.ffparams.iparams.push_back(iparams);
372 iparams.harmonic = { 109.47, 383.0, 109.47, 383.0 };
373 mtop_.ffparams.iparams.push_back(iparams);
376 auto updateGroupingsPerMoleculeType = gmx::makeUpdateGroupingsPerMoleculeType(mtop_);
378 ASSERT_EQ(updateGroupingsPerMoleculeType.size(), 1);
379 EXPECT_EQ(updateGroupingsPerMoleculeType[0].numBlocks(), 1);
381 real maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
382 EXPECT_FLOAT_EQ(maxRadius, 0.090824135);
384 logHelper_.expectNoEntries(MDLogger::LogLevel::Info);
385 UpdateGroups updateGroups = makeUpdateGroups(
386 logHelper_.logger(), std::move(updateGroupingsPerMoleculeType), maxRadius, true, true, 1e6_real);
389 TEST_F(UpdateGroupsTest, CheckRadiusCalculationAtDifferentTemperaturesWithWaterFlexAngle)
391 mtop_.moltype.emplace_back(waterFlexAngle());
394 iparams.constr = { 0.1, 0.1 };
395 mtop_.ffparams.iparams.push_back(iparams);
396 iparams.harmonic = { 109.47, 383.0, 109.47, 383.0 };
397 mtop_.ffparams.iparams.push_back(iparams);
400 auto updateGroupingsPerMoleculeType = gmx::makeUpdateGroupingsPerMoleculeType(mtop_);
402 ASSERT_EQ(updateGroupingsPerMoleculeType.size(), 1);
403 EXPECT_EQ(updateGroupingsPerMoleculeType[0].numBlocks(), 1);
405 real maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
406 EXPECT_FLOAT_EQ(maxRadius, 0.090824135);
408 // Observe that the temperature affects the radius only when valid
410 maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
411 EXPECT_FLOAT_EQ(maxRadius, 0.1);
414 maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
415 EXPECT_FLOAT_EQ(maxRadius, 0.1);
418 TEST_F(UpdateGroupsTest, WithTwoMoltypes)
420 mtop_.moltype.emplace_back(methane());
423 iparams.constr = { 0.1, 0.1 };
424 mtop_.ffparams.iparams.push_back(iparams);
427 mtop_.moltype.emplace_back(waterThreeSite());
428 // Note: iparams not accessed for SETTLE when not computing radius
430 auto updateGroupingsPerMoleculeType = gmx::makeUpdateGroupingsPerMoleculeType(mtop_);
432 ASSERT_EQ(updateGroupingsPerMoleculeType.size(), 2);
433 EXPECT_EQ(updateGroupingsPerMoleculeType[0].numBlocks(), 1);
434 EXPECT_EQ(updateGroupingsPerMoleculeType[1].numBlocks(), 1);
436 real maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
437 EXPECT_FLOAT_EQ(maxRadius, 0.14);
439 logHelper_.expectNoEntries(MDLogger::LogLevel::Info);
440 UpdateGroups updateGroups = makeUpdateGroups(
441 logHelper_.logger(), std::move(updateGroupingsPerMoleculeType), maxRadius, true, true, 1e6_real);
444 TEST_F(UpdateGroupsTest, LogsWhenSizesAreInvalid)
446 mtop_.moltype.emplace_back(methane());
449 iparams.constr = { 0.1, 0.1 };
450 mtop_.ffparams.iparams.push_back(iparams);
453 auto updateGroupingsPerMoleculeType = gmx::makeUpdateGroupingsPerMoleculeType(mtop_);
454 logHelper_.expectEntryMatchingRegex(MDLogger::LogLevel::Info,
455 "The combination of rlist and box size prohibits");
456 UpdateGroups updateGroups = makeUpdateGroups(
457 logHelper_.logger(), std::move(updateGroupingsPerMoleculeType), 1e9_real, true, true, 1e6_real);
460 TEST_F(UpdateGroupsTest, LogsWhenUpdateGroupsAreNotUseful)
462 mtop_.moltype.emplace_back(flexibleEthaneUA());
465 iparams.harmonic = { 0.1, 10.0, 0.1, 10.0 };
466 mtop_.ffparams.iparams.push_back(iparams);
469 auto updateGroupingsPerMoleculeType = gmx::makeUpdateGroupingsPerMoleculeType(mtop_);
471 ASSERT_EQ(updateGroupingsPerMoleculeType.size(), 1);
472 EXPECT_EQ(updateGroupingsPerMoleculeType[0].numBlocks(), 2);
474 real maxRadius = computeMaxUpdateGroupRadius(mtop_, updateGroupingsPerMoleculeType, temperature_);
475 EXPECT_FLOAT_EQ(maxRadius, 0);
477 logHelper_.expectEntryMatchingRegex(MDLogger::LogLevel::Info,
478 "No constraints or virtual sites are in use");
479 UpdateGroups updateGroups = makeUpdateGroups(
480 logHelper_.logger(), std::move(updateGroupingsPerMoleculeType), maxRadius, true, false, 1e6_real);