2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
5 * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
38 * Tests the position mapping engine.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_selection
45 #include "gromacs/selection/poscalc.h"
50 #include <gtest/gtest.h>
52 #include "gromacs/math/vec.h"
53 #include "gromacs/selection/indexutil.h"
54 #include "gromacs/selection/position.h"
55 #include "gromacs/topology/topology.h"
56 #include "gromacs/trajectory/trajectoryframe.h"
57 #include "gromacs/utility/arrayref.h"
58 #include "gromacs/utility/smalloc.h"
60 #include "testutils/refdata.h"
67 /********************************************************************
68 * PositionCalculationTest
71 class PositionCalculationTest
: public ::testing::Test
74 PositionCalculationTest();
75 ~PositionCalculationTest() override
;
77 void generateCoordinates();
79 gmx_ana_poscalc_t
* createCalculation(e_poscalc_t type
, int flags
);
80 void setMaximumGroup(gmx_ana_poscalc_t
* pc
, const gmx::ArrayRef
<const int>& atoms
);
81 gmx_ana_pos_t
* initPositions(gmx_ana_poscalc_t
* pc
, const char* name
);
83 void checkInitialized();
84 void updateAndCheck(gmx_ana_poscalc_t
* pc
,
86 const gmx::ArrayRef
<const int>& atoms
,
87 gmx::test::TestReferenceChecker
* checker
,
90 void testSingleStatic(e_poscalc_t type
,
93 const gmx::ArrayRef
<const int>& atoms
,
94 const gmx::ArrayRef
<const int>& index
= {});
95 void testSingleDynamic(e_poscalc_t type
,
98 const gmx::ArrayRef
<const int>& initAtoms
,
99 const gmx::ArrayRef
<const int>& evalAtoms
,
100 const gmx::ArrayRef
<const int>& index
= {});
102 gmx::test::TestReferenceData data_
;
103 gmx::test::TestReferenceChecker checker_
;
104 gmx::test::TopologyManager topManager_
;
105 gmx::PositionCalculationCollection pcc_
;
108 typedef std::unique_ptr
<gmx_ana_pos_t
> PositionPointer
;
112 PositionTest(PositionPointer pos
, gmx_ana_poscalc_t
* pc
, const char* name
) :
120 gmx_ana_poscalc_t
* pc
;
124 typedef std::vector
<PositionTest
> PositionTestList
;
126 void setTopologyIfRequired();
127 void checkPositions(gmx::test::TestReferenceChecker
* checker
, const char* name
, gmx_ana_pos_t
* p
, bool bCoordinates
);
129 std::vector
<gmx_ana_poscalc_t
*> pcList_
;
130 PositionTestList posList_
;
134 PositionCalculationTest::PositionCalculationTest() : checker_(data_
.rootChecker()), bTopSet_(false)
136 topManager_
.requestFrame();
139 PositionCalculationTest::~PositionCalculationTest()
141 std::vector
<gmx_ana_poscalc_t
*>::reverse_iterator pci
;
142 for (pci
= pcList_
.rbegin(); pci
!= pcList_
.rend(); ++pci
)
144 gmx_ana_poscalc_free(*pci
);
148 void PositionCalculationTest::generateCoordinates()
150 t_atoms
& atoms
= topManager_
.atoms();
151 t_trxframe
* frame
= topManager_
.frame();
152 for (int i
= 0; i
< atoms
.nr
; ++i
)
155 frame
->x
[i
][YY
] = atoms
.atom
[i
].resind
;
156 frame
->x
[i
][ZZ
] = 0.0;
159 copy_rvec(frame
->x
[i
], frame
->v
[i
]);
160 frame
->v
[i
][ZZ
] = 1.0;
164 copy_rvec(frame
->x
[i
], frame
->f
[i
]);
165 frame
->f
[i
][ZZ
] = -1.0;
170 gmx_ana_poscalc_t
* PositionCalculationTest::createCalculation(e_poscalc_t type
, int flags
)
172 pcList_
.reserve(pcList_
.size() + 1);
173 pcList_
.push_back(pcc_
.createCalculation(type
, flags
));
174 return pcList_
.back();
177 void PositionCalculationTest::setMaximumGroup(gmx_ana_poscalc_t
* pc
, const gmx::ArrayRef
<const int>& atoms
)
179 setTopologyIfRequired();
181 g
.isize
= atoms
.size();
182 g
.index
= const_cast<int*>(atoms
.data());
183 gmx_ana_poscalc_set_maxindex(pc
, &g
);
186 gmx_ana_pos_t
* PositionCalculationTest::initPositions(gmx_ana_poscalc_t
* pc
, const char* name
)
188 posList_
.reserve(posList_
.size() + 1);
189 PositionPointer
p(new gmx_ana_pos_t());
190 gmx_ana_pos_t
* result
= p
.get();
191 posList_
.emplace_back(std::move(p
), pc
, name
);
192 gmx_ana_poscalc_init_pos(pc
, result
);
196 void PositionCalculationTest::checkInitialized()
198 gmx::test::TestReferenceChecker
compound(checker_
.checkCompound("InitializedPositions", nullptr));
199 PositionTestList::const_iterator pi
;
200 for (pi
= posList_
.begin(); pi
!= posList_
.end(); ++pi
)
202 checkPositions(&compound
, pi
->name
, pi
->pos
.get(), false);
206 void PositionCalculationTest::updateAndCheck(gmx_ana_poscalc_t
* pc
,
208 const gmx::ArrayRef
<const int>& atoms
,
209 gmx::test::TestReferenceChecker
* checker
,
213 g
.isize
= atoms
.size();
214 g
.index
= const_cast<int*>(atoms
.data());
215 gmx_ana_poscalc_update(pc
, p
, &g
, topManager_
.frame(), nullptr);
216 checkPositions(checker
, name
, p
, true);
219 void PositionCalculationTest::testSingleStatic(e_poscalc_t type
,
222 const gmx::ArrayRef
<const int>& atoms
,
223 const gmx::ArrayRef
<const int>& index
)
225 t_trxframe
* frame
= topManager_
.frame();
228 flags
|= POS_VELOCITIES
;
234 gmx_ana_poscalc_t
* pc
= createCalculation(type
, flags
);
235 const bool requiresTopology
= gmx_ana_poscalc_required_topology_info(pc
)
236 != gmx::PositionCalculationCollection::RequiredTopologyInfo::None
;
237 EXPECT_EQ(bExpectTop
, requiresTopology
);
238 setMaximumGroup(pc
, atoms
);
239 gmx_ana_pos_t
* p
= initPositions(pc
, nullptr);
242 generateCoordinates();
245 topManager_
.initFrameIndices(index
);
247 pcc_
.initEvaluation();
248 pcc_
.initFrame(frame
);
249 gmx::test::TestReferenceChecker
frameCompound(
250 checker_
.checkCompound("EvaluatedPositions", "Frame0"));
251 updateAndCheck(pc
, p
, atoms
, &frameCompound
, nullptr);
255 void PositionCalculationTest::testSingleDynamic(e_poscalc_t type
,
258 const gmx::ArrayRef
<const int>& initAtoms
,
259 const gmx::ArrayRef
<const int>& evalAtoms
,
260 const gmx::ArrayRef
<const int>& index
)
262 gmx_ana_poscalc_t
* pc
= createCalculation(type
, flags
| POS_DYNAMIC
);
263 const bool requiresTopology
= gmx_ana_poscalc_required_topology_info(pc
)
264 != gmx::PositionCalculationCollection::RequiredTopologyInfo::None
;
265 EXPECT_EQ(bExpectTop
, requiresTopology
);
266 setMaximumGroup(pc
, initAtoms
);
267 gmx_ana_pos_t
* p
= initPositions(pc
, nullptr);
270 generateCoordinates();
273 topManager_
.initFrameIndices(index
);
275 pcc_
.initEvaluation();
276 pcc_
.initFrame(topManager_
.frame());
277 gmx::test::TestReferenceChecker
frameCompound(
278 checker_
.checkCompound("EvaluatedPositions", "Frame0"));
279 updateAndCheck(pc
, p
, evalAtoms
, &frameCompound
, nullptr);
283 void PositionCalculationTest::setTopologyIfRequired()
289 std::vector
<gmx_ana_poscalc_t
*>::const_iterator pci
;
290 for (pci
= pcList_
.begin(); pci
!= pcList_
.end(); ++pci
)
292 const bool requiresTopology
= gmx_ana_poscalc_required_topology_info(*pci
)
293 != gmx::PositionCalculationCollection::RequiredTopologyInfo::None
;
294 if (requiresTopology
)
297 pcc_
.setTopology(topManager_
.topology());
303 void PositionCalculationTest::checkPositions(gmx::test::TestReferenceChecker
* checker
,
308 gmx::test::TestReferenceChecker
compound(checker
->checkCompound("Positions", name
));
309 compound
.checkInteger(p
->count(), "Count");
310 const char* type
= "???";
313 case INDEX_UNKNOWN
: type
= "unknown"; break;
314 case INDEX_ATOM
: type
= "atoms"; break;
315 case INDEX_RES
: type
= "residues"; break;
316 case INDEX_MOL
: type
= "molecules"; break;
317 case INDEX_ALL
: type
= "single"; break;
319 compound
.checkString(type
, "Type");
320 compound
.checkSequenceArray(p
->count() + 1, p
->m
.mapb
.index
, "Block");
321 for (int i
= 0; i
< p
->count(); ++i
)
323 gmx::test::TestReferenceChecker
posCompound(compound
.checkCompound("Position", nullptr));
324 posCompound
.checkSequence(&p
->m
.mapb
.a
[p
->m
.mapb
.index
[i
]],
325 &p
->m
.mapb
.a
[p
->m
.mapb
.index
[i
+ 1]], "Atoms");
326 posCompound
.checkInteger(p
->m
.refid
[i
], "RefId");
329 posCompound
.checkVector(p
->x
[i
], "Coordinates");
331 if (bCoordinates
&& p
->v
!= nullptr)
333 posCompound
.checkVector(p
->v
[i
], "Velocity");
335 if (bCoordinates
&& p
->f
!= nullptr)
337 posCompound
.checkVector(p
->f
[i
], "Force");
339 int originalIdIndex
= (p
->m
.refid
[i
] != -1 ? p
->m
.refid
[i
] : i
);
340 EXPECT_EQ(p
->m
.orgid
[originalIdIndex
], p
->m
.mapid
[i
]);
344 /********************************************************************
348 TEST_F(PositionCalculationTest
, ComputesAtomPositions
)
350 const int group
[] = { 0, 1, 2, 3 };
351 topManager_
.requestVelocities();
352 topManager_
.requestForces();
353 topManager_
.initAtoms(4);
354 testSingleStatic(POS_ATOM
, 0, false, group
);
357 TEST_F(PositionCalculationTest
, ComputesResidueCOGPositions
)
359 const int group
[] = { 0, 1, 2, 3, 4, 8 };
360 topManager_
.requestVelocities();
361 topManager_
.requestForces();
362 topManager_
.initAtoms(9);
363 topManager_
.initUniformResidues(3);
364 testSingleStatic(POS_RES
, 0, true, group
);
367 TEST_F(PositionCalculationTest
, ComputesResidueCOMPositions
)
369 const int group
[] = { 0, 1, 2, 3, 4, 8 };
370 topManager_
.requestVelocities();
371 topManager_
.requestForces();
372 topManager_
.initAtoms(9);
373 topManager_
.initUniformResidues(3);
374 testSingleStatic(POS_RES
, POS_MASS
, true, group
);
377 TEST_F(PositionCalculationTest
, ComputesGroupCOGPositions
)
379 const int group
[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
380 topManager_
.requestVelocities();
381 topManager_
.requestForces();
382 topManager_
.initAtoms(9);
383 // Topology (masses) is requires for computing the force
384 testSingleStatic(POS_ALL
, 0, true, group
);
387 TEST_F(PositionCalculationTest
, ComputesGroupCOMPositions
)
389 const int group
[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
390 topManager_
.requestVelocities();
391 topManager_
.requestForces();
392 topManager_
.initAtoms(9);
393 testSingleStatic(POS_ALL
, POS_MASS
, true, group
);
396 TEST_F(PositionCalculationTest
, ComputesPositionsWithCompleteWhole
)
398 const int group
[] = { 0, 1, 2, 3, 4, 8 };
399 topManager_
.initAtoms(9);
400 topManager_
.initUniformResidues(3);
401 testSingleStatic(POS_RES
, POS_COMPLWHOLE
, true, group
);
404 TEST_F(PositionCalculationTest
, ComputesPositionsWithCompleteMax
)
406 const int maxGroup
[] = { 0, 1, 4, 5, 6, 8 };
407 const int evalGroup
[] = { 0, 1, 5, 6 };
408 topManager_
.initAtoms(9);
409 topManager_
.initUniformResidues(3);
410 testSingleDynamic(POS_RES
, POS_COMPLMAX
, true, maxGroup
, evalGroup
);
413 TEST_F(PositionCalculationTest
, ComputesPositionMask
)
415 const int maxGroup
[] = { 0, 1, 2, 3, 4, 5 };
416 const int evalGroup
[] = { 1, 2, 4 };
417 topManager_
.initAtoms(6);
418 testSingleDynamic(POS_ATOM
, POS_MASKONLY
, false, maxGroup
, evalGroup
);
421 // TODO: Check for POS_ALL_PBC
423 TEST_F(PositionCalculationTest
, HandlesFramesWithLessAtoms
)
425 const int group
[] = { 2, 3, 5, 6 };
426 topManager_
.initAtoms(10);
427 testSingleStatic(POS_ATOM
, 0, false, group
, group
);
430 TEST_F(PositionCalculationTest
, HandlesFramesWithLessAtoms2
)
432 const int group
[] = { 2, 3, 6, 7 };
433 const int index
[] = { 1, 2, 3, 4, 6, 7 };
434 topManager_
.initAtoms(10);
435 testSingleStatic(POS_ATOM
, 0, false, group
, index
);
438 TEST_F(PositionCalculationTest
, HandlesIdenticalStaticCalculations
)
440 const int group
[] = { 0, 1, 4, 5, 6, 7 };
441 topManager_
.initAtoms(9);
442 topManager_
.initUniformResidues(3);
444 gmx_ana_poscalc_t
* pc1
= createCalculation(POS_RES
, 0);
445 gmx_ana_poscalc_t
* pc2
= createCalculation(POS_RES
, 0);
446 gmx_ana_poscalc_t
* pc3
= createCalculation(POS_RES
, 0);
447 setMaximumGroup(pc1
, group
);
448 setMaximumGroup(pc2
, group
);
449 setMaximumGroup(pc3
, group
);
450 gmx_ana_pos_t
* p1
= initPositions(pc1
, "Positions");
451 gmx_ana_pos_t
* p2
= initPositions(pc2
, "Positions");
452 gmx_ana_pos_t
* p3
= initPositions(pc3
, "Positions");
455 generateCoordinates();
456 pcc_
.initEvaluation();
457 pcc_
.initFrame(topManager_
.frame());
458 gmx::test::TestReferenceChecker
frameCompound(
459 checker_
.checkCompound("EvaluatedPositions", "Frame0"));
460 updateAndCheck(pc1
, p1
, group
, &frameCompound
, "Positions");
461 updateAndCheck(pc2
, p2
, group
, &frameCompound
, "Positions");
462 updateAndCheck(pc3
, p3
, group
, &frameCompound
, "Positions");
466 TEST_F(PositionCalculationTest
, HandlesOverlappingStaticCalculations
)
468 const int group1
[] = { 0, 1, 4, 5 };
469 const int group2
[] = { 4, 5, 7, 8 };
470 topManager_
.initAtoms(9);
471 topManager_
.initUniformResidues(3);
473 gmx_ana_poscalc_t
* pc1
= createCalculation(POS_RES
, 0);
474 gmx_ana_poscalc_t
* pc2
= createCalculation(POS_RES
, 0);
475 setMaximumGroup(pc1
, group1
);
476 setMaximumGroup(pc2
, group2
);
477 gmx_ana_pos_t
* p1
= initPositions(pc1
, "P1");
478 gmx_ana_pos_t
* p2
= initPositions(pc2
, "P2");
481 generateCoordinates();
482 pcc_
.initEvaluation();
483 pcc_
.initFrame(topManager_
.frame());
484 gmx::test::TestReferenceChecker
frameCompound(
485 checker_
.checkCompound("EvaluatedPositions", "Frame0"));
486 updateAndCheck(pc1
, p1
, group1
, &frameCompound
, "P1");
487 updateAndCheck(pc2
, p2
, group2
, &frameCompound
, "P2");
491 // TODO: Check for handling of more multiple calculation cases