Add index group option to density fitting
[alexxy/gromacs.git] / src / gromacs / applied_forces / tests / densityfittingoptions.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2019, 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 /*! \internal \file
36  * \brief
37  * Tests for density fitting module options.
38  *
39  * \author Christian Blau <blau@kth.se>
40  * \ingroup module_applied_forces
41  */
42 #include "gmxpre.h"
43
44 #include "gromacs/applied_forces/densityfittingoptions.h"
45
46 #include <string>
47 #include <vector>
48
49 #include <gtest/gtest.h>
50
51 #include "gromacs/options/options.h"
52 #include "gromacs/options/treesupport.h"
53 #include "gromacs/selection/indexutil.h"
54 #include "gromacs/utility/keyvaluetreebuilder.h"
55 #include "gromacs/utility/keyvaluetreemdpwriter.h"
56 #include "gromacs/utility/keyvaluetreetransform.h"
57 #include "gromacs/utility/smalloc.h"
58 #include "gromacs/utility/stringcompare.h"
59 #include "gromacs/utility/stringstream.h"
60 #include "gromacs/utility/textwriter.h"
61
62 #include "testutils/testasserts.h"
63 #include "testutils/testmatchers.h"
64
65 namespace gmx
66 {
67
68 namespace
69 {
70
71 class DensityFittingOptionsTest : public ::testing::Test
72 {
73     public:
74         DensityFittingOptionsTest()
75         {
76             init_blocka(&defaultGroups_);
77         }
78         ~DensityFittingOptionsTest() override
79         {
80             done_blocka(&defaultGroups_);
81         }
82
83         void setFromMdpValues(const KeyValueTreeObject &densityFittingMdpValues)
84         {
85             // set up options
86             Options densityFittingModuleOptions;
87             densityFittingOptions_.initMdpOptions(&densityFittingModuleOptions);
88
89             // Add rules to transform mdp inputs to densityFittingModule data
90             KeyValueTreeTransformer transform;
91             transform.rules()->addRule().keyMatchType("/", StringCompareType::CaseAndDashInsensitive);
92
93             densityFittingOptions_.initMdpTransform(transform.rules());
94
95             // Execute the transform on the mdpValues
96             auto transformedMdpValues = transform.transform(densityFittingMdpValues, nullptr);
97             assignOptionsFromKeyValueTree(&densityFittingModuleOptions, transformedMdpValues.object(), nullptr);
98         }
99
100         KeyValueTreeObject densityFittingSetActiveAsMdpValues()
101         {
102             // Prepare MDP inputs
103             KeyValueTreeBuilder mdpValueBuilder;
104             mdpValueBuilder.rootObject().addValue("density-guided-simulation-active",
105                                                   std::string("yes"));
106             return mdpValueBuilder.build();
107         }
108
109         IndexGroupsAndNames genericIndexGroupsAndNames()
110         {
111             done_blocka(&defaultGroups_);
112             stupid_fill_blocka(&defaultGroups_, 3);
113             std::vector<std::string> groupNames = { "A", "protein", "C" };
114             const char *const        namesAsConstChar[3]
115                 = { groupNames[0].c_str(), groupNames[1].c_str(), groupNames[2].c_str() };
116             return {defaultGroups_, namesAsConstChar};
117         }
118
119         IndexGroupsAndNames differingIndexGroupsAndNames()
120         {
121             done_blocka(&defaultGroups_);
122             stupid_fill_blocka(&defaultGroups_, 3);
123             std::vector<std::string> groupNames = { "protein", "C", "A"};
124             const char *const        namesAsConstChar[3]
125                 = { groupNames[0].c_str(), groupNames[1].c_str(), groupNames[2].c_str() };
126             return { defaultGroups_, namesAsConstChar };
127         }
128
129         void mangleInternalParameters()
130         {
131             densityFittingOptions_.setFitGroupIndices(differingIndexGroupsAndNames());
132         }
133     protected:
134         t_blocka              defaultGroups_;
135         DensityFittingOptions densityFittingOptions_;
136 };
137
138 TEST_F(DensityFittingOptionsTest, DefaultParameters)
139 {
140     EXPECT_FALSE(densityFittingOptions_.buildParameters().active_);
141 }
142
143 TEST_F(DensityFittingOptionsTest, OptionSetsActive)
144 {
145     EXPECT_FALSE(densityFittingOptions_.buildParameters().active_);
146     setFromMdpValues(densityFittingSetActiveAsMdpValues());
147     EXPECT_TRUE(densityFittingOptions_.buildParameters().active_);
148 }
149
150 TEST_F(DensityFittingOptionsTest, OutputDefaultValues)
151 {
152     // Transform module data into a flat key-value tree for output.
153
154     StringOutputStream        stream;
155     KeyValueTreeBuilder       builder;
156     KeyValueTreeObjectBuilder builderObject = builder.rootObject();
157
158     densityFittingOptions_.buildMdpOutput(&builderObject);
159     {
160         TextWriter writer(&stream);
161         writeKeyValueTreeAsMdp(&writer, builder.build());
162     }
163     stream.close();
164
165     EXPECT_EQ(stream.toString(), std::string("density-guided-simulation-active = false\n"));
166 }
167
168 TEST_F(DensityFittingOptionsTest, CanConvertGroupStringToIndexGroup)
169 {
170     setFromMdpValues(densityFittingSetActiveAsMdpValues());
171
172     const auto indexGroupAndNames = genericIndexGroupsAndNames();
173     densityFittingOptions_.setFitGroupIndices(indexGroupAndNames);
174
175     EXPECT_EQ(1, densityFittingOptions_.buildParameters().indices_.size());
176     EXPECT_EQ(1, densityFittingOptions_.buildParameters().indices_[0]);
177 }
178
179 TEST_F(DensityFittingOptionsTest, InternalsToKvt)
180 {
181     // stores the default internal options
182     DensityFittingOptions densityFittingOptions;
183     KeyValueTreeBuilder   builder;
184     densityFittingOptions.writeInternalParametersToKvt(builder.rootObject());
185     const auto            kvtTree = builder.build();
186     EXPECT_TRUE(kvtTree.keyExists("density-guided-simulation-group"));
187     EXPECT_TRUE(kvtTree["density-guided-simulation-group"].isArray());
188     auto storedIndex =  kvtTree["density-guided-simulation-group"].asArray().values();
189
190     EXPECT_EQ(0, storedIndex.size());
191 }
192
193 TEST_F(DensityFittingOptionsTest, KvtToInternal)
194 {
195     setFromMdpValues(densityFittingSetActiveAsMdpValues());
196
197     KeyValueTreeBuilder builder;
198     auto                addedArray = builder.rootObject().addUniformArray<index>("density-guided-simulation-group");
199     addedArray.addValue(1);
200     addedArray.addValue(15);
201     const auto tree = builder.build();
202
203     densityFittingOptions_.readInternalParametersFromKvt(tree);
204
205     EXPECT_EQ(2, densityFittingOptions_.buildParameters().indices_.size());
206     EXPECT_EQ(1, densityFittingOptions_.buildParameters().indices_[0]);
207     EXPECT_EQ(15, densityFittingOptions_.buildParameters().indices_[1]);
208 }
209
210 TEST_F(DensityFittingOptionsTest, RoundTripForInternalsIsIdempotent)
211 {
212     setFromMdpValues(densityFittingSetActiveAsMdpValues());
213     {
214         const IndexGroupsAndNames indexGroupAndNames = genericIndexGroupsAndNames();
215         densityFittingOptions_.setFitGroupIndices(indexGroupAndNames);
216     }
217
218     DensityFittingParameters parametersBefore = densityFittingOptions_.buildParameters();
219
220     KeyValueTreeBuilder      builder;
221     densityFittingOptions_.writeInternalParametersToKvt(builder.rootObject());
222     const auto               inputTree = builder.build();
223
224     mangleInternalParameters();
225
226     DensityFittingParameters parametersAfter = densityFittingOptions_.buildParameters();
227     EXPECT_NE(parametersBefore, parametersAfter);
228
229     densityFittingOptions_.readInternalParametersFromKvt(inputTree);
230
231     parametersAfter = densityFittingOptions_.buildParameters();
232     EXPECT_EQ(parametersBefore, parametersAfter);
233 }
234
235 } // namespace
236
237 } // namespace gmx