2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 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 * \brief End-to-end tests checking sanity of results of simulations
38 * containing freeze groups
40 * \author Pascal Merz <pascal.merz@me.com>
41 * \ingroup module_mdrun_integration_tests
47 #include "gromacs/topology/ifunc.h"
48 #include "gromacs/utility/stringutil.h"
50 #include "testutils/mpitest.h"
51 #include "testutils/simulationdatabase.h"
52 #include "testutils/testmatchers.h"
54 #include "moduletest.h"
55 #include "simulatorcomparison.h"
56 #include "trajectoryreader.h"
62 /*! \brief Test fixture checking sanity of freeze group results
64 * This tests the sanity of simulation results containing fully and partially
65 * frozen atoms. For fully frozen atoms, it checks that their reported position
66 * is identical for all steps, and that their velocity is zero. For partially
67 * frozen atoms (for simplicity only in z-direction), it checks that their
68 * position is identical in the frozen dimension for all steps, and that their
69 * velocity is zero in the frozen dimension.
71 using FreezeGroupTestParams = std::tuple<std::string, std::string, std::string>;
72 class FreezeGroupTest : public MdrunTestFixture, public ::testing::WithParamInterface<FreezeGroupTestParams>
75 //! Check that the frozen positions don't change and velocities are zero
76 static void checkFreezeGroups(const std::string& trajectoryName,
77 ArrayRef<const unsigned int> fullyFrozenAtoms,
78 ArrayRef<const unsigned int> partiallyFrozenAtomsDimZ,
79 const TrajectoryTolerances& tolerances)
81 auto [fullyFrozenPositions, fullyFrozenVelocities] =
82 getFrozenPositionsAndVelocities(trajectoryName, fullyFrozenAtoms);
83 auto [partiallyFrozenPositions, partiallyFrozenVelocities] =
84 getFrozenPositionsAndVelocities(trajectoryName, partiallyFrozenAtomsDimZ);
85 GMX_RELEASE_ASSERT(fullyFrozenPositions.size() == fullyFrozenVelocities.size(),
86 "Position and velocity trajectory don't have the same length.");
87 GMX_RELEASE_ASSERT(partiallyFrozenPositions.size() == partiallyFrozenVelocities.size(),
88 "Position and velocity trajectory don't have the same length.");
89 GMX_RELEASE_ASSERT(fullyFrozenPositions.size() == partiallyFrozenPositions.size(),
90 "Fully and partially frozen trajectory don't have the same length.");
91 const auto trajectorySize = fullyFrozenPositions.size();
93 for (auto frameIdx = decltype(trajectorySize){ 0 }; frameIdx < trajectorySize; frameIdx++)
95 SCOPED_TRACE(formatString("Checking frame %lu", frameIdx + 1));
98 checkFullyFrozenPositions(
99 fullyFrozenPositions[frameIdx], fullyFrozenPositions[frameIdx - 1], tolerances);
100 checkZDimFrozenPositions(partiallyFrozenPositions[frameIdx],
101 partiallyFrozenPositions[frameIdx - 1],
104 checkFullyFrozenVelocities(fullyFrozenVelocities[frameIdx], tolerances);
105 checkZDimFrozenVelocities(partiallyFrozenVelocities[frameIdx], tolerances);
109 //! Check that fully frozen frame velocities are zero
110 static void checkFullyFrozenVelocities(ArrayRef<const RVec> velocities,
111 const TrajectoryTolerances& tolerances)
113 SCOPED_TRACE("Checking fully frozen velocity frame");
114 std::vector<RVec> zeroVelocities(velocities.size(), RVec{ 0, 0, 0 });
115 EXPECT_THAT(zeroVelocities, Pointwise(RVecEq(tolerances.velocities), velocities));
117 //! Check that z-dimension frozen frame velocities are zero
118 static void checkZDimFrozenVelocities(ArrayRef<const RVec> velocities,
119 const TrajectoryTolerances& tolerances)
121 SCOPED_TRACE("Checking z-dimension frozen velocity frame");
122 std::vector<real> zVelocities;
123 for (const auto& v : velocities)
125 zVelocities.emplace_back(v[ZZ]);
127 std::vector<real> zeroVelocities(zVelocities.size(), 0);
128 EXPECT_THAT(zeroVelocities, Pointwise(RealEq(tolerances.velocities), zVelocities));
130 //! Check that fully frozen frame positions are static
131 static void checkFullyFrozenPositions(ArrayRef<const RVec> positions,
132 ArrayRef<const RVec> previousPositions,
133 const TrajectoryTolerances& tolerances)
135 SCOPED_TRACE("Checking fully frozen position frame");
136 EXPECT_THAT(previousPositions, Pointwise(RVecEq(tolerances.coordinates), positions));
138 //! Check that z-dimension frozen frame positions are zero
139 static void checkZDimFrozenPositions(ArrayRef<const RVec> positions,
140 ArrayRef<const RVec> previousPositions,
141 const TrajectoryTolerances& tolerances)
143 SCOPED_TRACE("Checking z-dimension frozen position frame");
144 std::vector<real> zPositions;
145 for (const auto& p : positions)
147 zPositions.emplace_back(p[ZZ]);
149 std::vector<real> zPrevPositions;
150 for (const auto& p : previousPositions)
152 zPrevPositions.emplace_back(p[ZZ]);
154 EXPECT_THAT(zPrevPositions, Pointwise(RealEq(tolerances.coordinates), zPositions));
157 static std::tuple<std::vector<std::vector<RVec>>, std::vector<std::vector<RVec>>>
158 getFrozenPositionsAndVelocities(const std::string& trajectoryName, ArrayRef<const unsigned int> frozenAtoms)
160 std::vector<std::vector<RVec>> positions;
161 std::vector<std::vector<RVec>> velocities;
163 TrajectoryFrameReader trajectoryFrameReader(trajectoryName);
164 while (trajectoryFrameReader.readNextFrame())
166 const auto frame = trajectoryFrameReader.frame();
167 positions.emplace_back();
168 velocities.emplace_back();
169 for (const auto& index : frozenAtoms)
171 positions.back().emplace_back(frame.x().at(index));
172 velocities.back().emplace_back(frame.v().at(index));
176 return { std::move(positions), std::move(velocities) };
180 TEST_P(FreezeGroupTest, WithinTolerances)
182 const auto& params = GetParam();
183 const auto& integrator = std::get<0>(params);
184 const auto& tcoupling = std::get<1>(params);
185 const auto& pcoupling = std::get<2>(params);
186 const auto& simulationName = "alanine_vacuo";
188 constexpr std::array<unsigned int, 5> backbone = { 4, 6, 8, 14, 16 };
189 constexpr std::array<unsigned int, 13> sideChainH = { 0, 1, 2, 3, 9, 10, 11,
190 12, 13, 18, 19, 20, 21 };
192 if (integrator == "md-vv" && pcoupling == "parrinello-rahman")
194 // Parrinello-Rahman is not implemented in md-vv
199 auto mdpFieldValues = prepareMdpFieldValues(simulationName, integrator, tcoupling, pcoupling);
200 mdpFieldValues["nsteps"] = "8";
201 mdpFieldValues["nstxout"] = "4";
202 mdpFieldValues["nstvout"] = "4";
203 mdpFieldValues["freezegrps"] = "Backbone SideChain";
204 mdpFieldValues["freezedim"] = "Y Y Y N N Y";
205 mdpFieldValues["constraints"] = "all-bonds";
208 runner_.useTopGroAndNdxFromDatabase(simulationName);
209 runner_.useStringAsMdpFile(prepareMdpFileContents(mdpFieldValues));
210 // Allow one warning for COMM removal + partially frozen atoms
211 runGrompp(&runner_, { SimulationOptionTuple("-maxwarn", "1") });
215 // Check frozen atoms
216 checkFreezeGroups(runner_.fullPrecisionTrajectoryFileName_,
219 TrajectoryComparison::s_defaultTrajectoryTolerances);
222 INSTANTIATE_TEST_SUITE_P(FreezeWorks,
224 ::testing::Combine(::testing::Values("md", "md-vv", "sd", "bd"),
225 ::testing::Values("no"),
226 ::testing::Values("no")));
228 } // namespace gmx::test