78a4d923673887da5b465cc91a8fc603b93bb030
[alexxy/gromacs.git] / src / gromacs / mdlib / tests / energyoutput.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 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 /*! \internal \file
36  * \brief
37  * Tests for energy output to log and .edr files.
38  *
39  * \todo Position and orientation restraints tests.
40  * \todo Average and sum in edr file test.
41  * \todo AWH output tests.
42  * \todo The log and edr outputs are currently saved to the file on the disk and then read
43  *       to compare with the reference data. This will be more elegant (and run faster) when we
44  *       refactor the output routines to write to a stream interface, which can already be handled
45  *       in-memory when running tests.
46  *
47  * \author Mark Abraham <mark.j.abraham@gmail.com>
48  * \author Artem Zhmurov <zhmurov@gmail.com>
49  *
50  * \ingroup module_mdlib
51  */
52 #include "gmxpre.h"
53
54 #include "gromacs/mdlib/energyoutput.h"
55
56 #include <cstdio>
57
58 #include <gtest/gtest.h>
59
60 #include "gromacs/mdlib/ebin.h"
61 #include "gromacs/mdlib/makeconstraints.h"
62 #include "gromacs/mdrunutility/handlerestart.h"
63 #include "gromacs/mdtypes/commrec.h"
64 #include "gromacs/mdtypes/fcdata.h"
65 #include "gromacs/mdtypes/group.h"
66 #include "gromacs/mdtypes/inputrec.h"
67 #include "gromacs/mdtypes/mdatom.h"
68 #include "gromacs/mdtypes/state.h"
69 #include "gromacs/topology/topology.h"
70 #include "gromacs/utility/cstringutil.h"
71 #include "gromacs/utility/mdmodulenotification.h"
72 #include "gromacs/utility/stringutil.h"
73 #include "gromacs/utility/textreader.h"
74 #include "gromacs/utility/unique_cptr.h"
75
76 #include "testutils/refdata.h"
77 #include "testutils/setenv.h"
78 #include "testutils/testasserts.h"
79 #include "testutils/testfilemanager.h"
80
81 namespace gmx
82 {
83 namespace test
84 {
85 namespace
86 {
87
88 //! Wraps fclose to discard the return value to use it as a deleter with gmx::unique_cptr.
89 void fcloseWrapper(FILE* fp)
90 {
91     fclose(fp);
92 }
93
94 /*! \brief Test parameters space.
95  *
96  * The test will run on a set of combinations of this steucture parameters.
97  */
98 struct EnergyOutputTestParameters
99 {
100     //! Thermostat (enum)
101     TemperatureCoupling temperatureCouplingScheme;
102     //! Barostat (enum)
103     PressureCoupling pressureCouplingScheme;
104     //! Integrator
105     IntegrationAlgorithm integrator;
106     //! Number of saved energy frames (to test averages output).
107     int numFrames;
108     //! If output should be initialized as a rerun.
109     bool isRerun;
110     //! Is box triclinic (off-diagonal elements will be printed).
111     bool isBoxTriclinic;
112 };
113
114 /*! \brief Sets of parameters on which to run the tests.
115  *
116  * Only several combinations of the parameters are used. Using all possible combinations will
117  * require ~10 MB of test data and ~2 sec to run the tests.
118  */
119 const EnergyOutputTestParameters parametersSets[] = {
120     { TemperatureCoupling::No, PressureCoupling::No, IntegrationAlgorithm::MD, 1, false, false },
121     { TemperatureCoupling::No, PressureCoupling::No, IntegrationAlgorithm::MD, 1, true, false },
122     { TemperatureCoupling::No, PressureCoupling::No, IntegrationAlgorithm::MD, 1, false, true },
123     { TemperatureCoupling::No, PressureCoupling::No, IntegrationAlgorithm::MD, 0, false, false },
124     { TemperatureCoupling::No, PressureCoupling::No, IntegrationAlgorithm::MD, 10, false, false },
125     { TemperatureCoupling::VRescale, PressureCoupling::No, IntegrationAlgorithm::MD, 1, false, false },
126     { TemperatureCoupling::NoseHoover, PressureCoupling::No, IntegrationAlgorithm::MD, 1, false, false },
127     { TemperatureCoupling::No, PressureCoupling::ParrinelloRahman, IntegrationAlgorithm::MD, 1, false, false },
128     { TemperatureCoupling::No, PressureCoupling::Mttk, IntegrationAlgorithm::MD, 1, false, false },
129     { TemperatureCoupling::No, PressureCoupling::No, IntegrationAlgorithm::VV, 1, false, false },
130     { TemperatureCoupling::No, PressureCoupling::Mttk, IntegrationAlgorithm::VV, 1, false, false }
131 };
132
133 /*! \brief Test fixture to test energy output.
134  *
135  * The class is initialized to maximize amount of energy terms printed.
136  * The test is run for different combinations of temperature and pressure control
137  * schemes. Different number of printed steps is also tested.
138  */
139 class EnergyOutputTest : public ::testing::TestWithParam<EnergyOutputTestParameters>
140 {
141     int  numTempCouplingGroups_ = 3;
142     real cosAccel_              = 1.0;
143
144 public:
145     //! File manager
146     TestFileManager fileManager_;
147     //! Energy (.edr) file
148     ener_file_t energyFile_;
149     //! Input data
150     t_inputrec inputrec_;
151     //! Topology
152     gmx_mtop_t mtop_;
153     //! Simulation time
154     double time_;
155     //! Total mass
156     real tmass_;
157     //! Potential energy data
158     std::unique_ptr<gmx_enerdata_t> enerdata_;
159     //! Kinetic energy data (for temperatures output)
160     gmx_ekindata_t ekindata_;
161     //! System state
162     t_state state_;
163     //! PBC box
164     matrix box_;
165     //! Virial from constraints
166     tensor constraintsVirial_;
167     //! Virial from force computation
168     tensor forceVirial_;
169     //! Total virial
170     tensor totalVirial_;
171     //! Pressure
172     tensor pressure_;
173     //! Names for the groups.
174     std::vector<std::string> groupNameStrings_ = { "Protein", "Water", "Lipid" };
175     //! Names for the groups as C strings.
176     std::vector<std::vector<char>> groupNameCStrings_;
177     //! Handles to the names as C strings in the way needed for SimulationGroups.
178     std::vector<char*> groupNameHandles_;
179     //! Total dipole momentum
180     rvec muTotal_;
181     //! Communication record
182     t_commrec cr_;
183     //! Constraints object (for constraints RMSD output in case of LINCS)
184     std::unique_ptr<Constraints> constraints_;
185     //! Temporary output filename
186     std::string logFilename_;
187     //! Temporary energy output filename
188     std::string edrFilename_;
189     //! Pointer to a temporary output file
190     FILE* log_;
191     //! Log file wrapper
192     unique_cptr<FILE, fcloseWrapper> logFileGuard_;
193     //! Reference data
194     TestReferenceData refData_;
195     //! Checker for reference data
196     TestReferenceChecker checker_;
197
198     EnergyOutputTest() :
199         ekindata_(numTempCouplingGroups_, cosAccel_, 1),
200         logFilename_(fileManager_.getTemporaryFilePath(".log")),
201         edrFilename_(fileManager_.getTemporaryFilePath(".edr")),
202         log_(std::fopen(logFilename_.c_str(), "w")),
203         logFileGuard_(log_),
204         checker_(refData_.rootChecker())
205     {
206         // Input record
207         inputrec_.delta_t = 0.001;
208
209         // F_EQM
210         inputrec_.bQMMM = true;
211         // F_RF_EXCL will not be tested - group scheme is not supported any more
212         inputrec_.cutoff_scheme = CutoffScheme::Verlet;
213         // F_COUL_RECIP
214         inputrec_.coulombtype = CoulombInteractionType::Pme;
215         // F_LJ_RECIP
216         inputrec_.vdwtype = VanDerWaalsType::Pme;
217
218         // F_DVDL_COUL, F_DVDL_VDW, F_DVDL_BONDED, F_DVDL_RESTRAINT, F_DKDL and F_DVDL
219         inputrec_.efep = FreeEnergyPerturbationType::Yes;
220         inputrec_.fepvals->separate_dvdl[FreeEnergyPerturbationCouplingType::Coul]      = true;
221         inputrec_.fepvals->separate_dvdl[FreeEnergyPerturbationCouplingType::Vdw]       = true;
222         inputrec_.fepvals->separate_dvdl[FreeEnergyPerturbationCouplingType::Bonded]    = true;
223         inputrec_.fepvals->separate_dvdl[FreeEnergyPerturbationCouplingType::Restraint] = true;
224         inputrec_.fepvals->separate_dvdl[FreeEnergyPerturbationCouplingType::Mass]      = true;
225         inputrec_.fepvals->separate_dvdl[FreeEnergyPerturbationCouplingType::Coul]      = true;
226         inputrec_.fepvals->separate_dvdl[FreeEnergyPerturbationCouplingType::Fep]       = true;
227
228         // F_DISPCORR and F_PDISPCORR
229         inputrec_.eDispCorr = DispersionCorrectionType::Ener;
230         inputrec_.bRot      = true;
231
232         // F_ECONSERVED
233         inputrec_.ref_p[YY][XX] = 0.0;
234         inputrec_.ref_p[ZZ][XX] = 0.0;
235         inputrec_.ref_p[ZZ][YY] = 0.0;
236
237         // Dipole (mu)
238         inputrec_.ewald_geometry = EwaldGeometry::ThreeDC;
239
240         // GMX_CONSTRAINTVIR environment variable should also be
241         // set to print constraints and force virials separately.
242         gmxSetenv("GMX_CONSTRAINTVIR", "true", 1);
243         // To print constrain RMSD, constraints algorithm should be set to LINCS.
244         inputrec_.eConstrAlg = ConstraintAlgorithm::Lincs;
245
246         mtop_.bIntermolecularInteractions = false;
247
248         // Constructing molecular topology
249         gmx_moltype_t molType;
250
251         molType.atoms.nr = 2;
252
253         // F_CONSTR
254         // This must be initialized so that Constraints object can be created below.
255         InteractionList interactionListConstr;
256         interactionListConstr.iatoms.resize(NRAL(F_CONSTR) + 1);
257         interactionListConstr.iatoms[0] = 0;
258         interactionListConstr.iatoms[1] = 0;
259         interactionListConstr.iatoms[2] = 1;
260         molType.ilist.at(F_CONSTR)      = interactionListConstr;
261
262         InteractionList interactionListEmpty;
263         interactionListEmpty.iatoms.resize(0);
264         molType.ilist.at(F_CONSTRNC) = interactionListEmpty;
265         molType.ilist.at(F_SETTLE)   = interactionListEmpty;
266
267         // F_LJ14 and F_COUL14
268         InteractionList interactionListLJ14;
269         interactionListLJ14.iatoms.resize(NRAL(F_LJ14) + 1);
270         molType.ilist.at(F_LJ14) = interactionListLJ14;
271
272         // F_LJC14_Q
273         InteractionList interactionListLJC14Q;
274         interactionListLJC14Q.iatoms.resize(NRAL(F_LJC14_Q) + 1);
275         molType.ilist.at(F_LJC14_Q) = interactionListLJC14Q;
276
277         // TODO Do proper initialization for distance and orientation
278         //      restraints and remove comments to enable their output
279         // F_DISRES
280         // InteractionList interactionListDISRES;
281         // interactionListDISRES.iatoms.resize(NRAL(F_DISRES) + 1);
282         // molType.ilist.at(F_DISRES)   = interactionListDISRES;
283         //
284         // F_ORIRES
285         // InteractionList interactionListORIRES;
286         // interactionListORIRES.iatoms.resize(NRAL(F_ORIRES) + 1);
287         // molType.ilist.at(F_ORIRES)   = interactionListORIRES;
288
289         mtop_.moltype.push_back(molType);
290
291         gmx_molblock_t molBlock;
292         molBlock.type = 0;
293         molBlock.nmol = 1;
294         mtop_.molblock.push_back(molBlock);
295
296         // This is to keep constraints initialization happy
297         mtop_.natoms = 2;
298         mtop_.ffparams.iparams.resize(F_NRE);
299         mtop_.ffparams.functype.resize(F_NRE);
300         mtop_.ffparams.iparams.at(F_CONSTR).constr.dA   = 1.0;
301         mtop_.ffparams.iparams.at(F_CONSTR).constr.dB   = 1.0;
302         mtop_.ffparams.iparams.at(F_CONSTRNC).constr.dA = 1.0;
303         mtop_.ffparams.iparams.at(F_CONSTRNC).constr.dB = 1.0;
304
305         // Groups for energy output, temperature coupling and acceleration
306         for (const auto& string : groupNameStrings_)
307         {
308             std::vector<char> cString(string.begin(), string.end());
309             // Need to add null termination
310             cString.push_back('\0');
311             groupNameCStrings_.emplace_back(cString);
312             groupNameHandles_.emplace_back(groupNameCStrings_.back().data());
313         }
314         for (auto& handle : groupNameHandles_)
315         {
316             mtop_.groups.groupNames.emplace_back(&handle);
317         }
318
319         mtop_.groups.groups[SimulationAtomGroupType::EnergyOutput].resize(numTempCouplingGroups_);
320         mtop_.groups.groups[SimulationAtomGroupType::EnergyOutput][0] = 0;
321         mtop_.groups.groups[SimulationAtomGroupType::EnergyOutput][1] = 1;
322         mtop_.groups.groups[SimulationAtomGroupType::EnergyOutput][2] = 2;
323
324         mtop_.groups.groups[SimulationAtomGroupType::TemperatureCoupling].resize(numTempCouplingGroups_);
325         mtop_.groups.groups[SimulationAtomGroupType::TemperatureCoupling][0] = 0;
326         mtop_.groups.groups[SimulationAtomGroupType::TemperatureCoupling][1] = 1;
327         mtop_.groups.groups[SimulationAtomGroupType::TemperatureCoupling][2] = 2;
328
329         // Nose-Hoover chains
330         inputrec_.bPrintNHChains     = true;
331         inputrec_.opts.nhchainlength = 2;
332         state_.nosehoover_xi.resize(
333                 mtop_.groups.groups[SimulationAtomGroupType::TemperatureCoupling].size()
334                 * inputrec_.opts.nhchainlength);
335         state_.nosehoover_vxi.resize(
336                 mtop_.groups.groups[SimulationAtomGroupType::TemperatureCoupling].size()
337                 * inputrec_.opts.nhchainlength);
338
339         // This will be needed only with MTTK barostat
340         state_.nhpres_xi.resize(1 * inputrec_.opts.nhchainlength);
341         state_.nhpres_vxi.resize(1 * inputrec_.opts.nhchainlength);
342
343         // Group pairs
344         enerdata_ = std::make_unique<gmx_enerdata_t>(
345                 mtop_.groups.groups[SimulationAtomGroupType::EnergyOutput].size(), 0);
346
347         // Kinetic energy and related data
348         ekindata_.tcstat.resize(mtop_.groups.groups[SimulationAtomGroupType::TemperatureCoupling].size());
349
350         // This is needed so that the ebin space will be allocated
351         inputrec_.cos_accel = cosAccel_;
352
353         // Group options for annealing output
354         inputrec_.opts.ngtc = numTempCouplingGroups_;
355         snew(inputrec_.opts.ref_t, inputrec_.opts.ngtc);
356         snew(inputrec_.opts.annealing, inputrec_.opts.ngtc);
357         inputrec_.opts.annealing[0] = SimulatedAnnealing::No;
358         inputrec_.opts.annealing[1] = SimulatedAnnealing::Single;
359         inputrec_.opts.annealing[2] = SimulatedAnnealing::Periodic;
360
361         // This is to keep done_inputrec happy (otherwise sfree() segfaults)
362         snew(inputrec_.opts.anneal_time, inputrec_.opts.ngtc);
363         snew(inputrec_.opts.anneal_temp, inputrec_.opts.ngtc);
364
365         // Communication record (for Constraints constructor)
366         cr_.nnodes = 1;
367         cr_.dd     = nullptr;
368
369         // Constraints object (to get constraints RMSD from)
370         // TODO EnergyOutput should not take Constraints object
371         // TODO This object will always return zero as RMSD value.
372         //      It is more relevant to have non-zero value for testing.
373         constraints_ = makeConstraints(
374                 mtop_, inputrec_, nullptr, false, nullptr, &cr_, nullptr, nullptr, nullptr, false);
375     }
376
377     /*! \brief Helper function to generate synthetic data to output
378      *
379      * \param[in,out] testValue    Base value fr energy data.
380      */
381     void setStepData(double* testValue)
382     {
383
384         time_  = (*testValue += 0.1);
385         tmass_ = (*testValue += 0.1);
386
387         enerdata_->term[F_LJ]      = (*testValue += 0.1);
388         enerdata_->term[F_COUL_SR] = (*testValue += 0.1);
389         enerdata_->term[F_EPOT]    = (*testValue += 0.1);
390         enerdata_->term[F_EKIN]    = (*testValue += 0.1);
391         enerdata_->term[F_ETOT]    = (*testValue += 0.1);
392         enerdata_->term[F_TEMP]    = (*testValue += 0.1);
393         enerdata_->term[F_PRES]    = (*testValue += 0.1);
394
395         enerdata_->term[F_BHAM]         = (*testValue += 0.1);
396         enerdata_->term[F_EQM]          = (*testValue += 0.1);
397         enerdata_->term[F_RF_EXCL]      = (*testValue += 0.1);
398         enerdata_->term[F_COUL_RECIP]   = (*testValue += 0.1);
399         enerdata_->term[F_LJ_RECIP]     = (*testValue += 0.1);
400         enerdata_->term[F_LJ14]         = (*testValue += 0.1);
401         enerdata_->term[F_COUL14]       = (*testValue += 0.1);
402         enerdata_->term[F_LJC14_Q]      = (*testValue += 0.1);
403         enerdata_->term[F_LJC_PAIRS_NB] = (*testValue += 0.1);
404
405         enerdata_->term[F_DVDL_COUL]      = (*testValue += 0.1);
406         enerdata_->term[F_DVDL_VDW]       = (*testValue += 0.1);
407         enerdata_->term[F_DVDL_BONDED]    = (*testValue += 0.1);
408         enerdata_->term[F_DVDL_RESTRAINT] = (*testValue += 0.1);
409         enerdata_->term[F_DKDL]           = (*testValue += 0.1);
410         enerdata_->term[F_DVDL]           = (*testValue += 0.1);
411
412         enerdata_->term[F_DISPCORR]   = (*testValue += 0.1);
413         enerdata_->term[F_PDISPCORR]  = (*testValue += 0.1);
414         enerdata_->term[F_DISRESVIOL] = (*testValue += 0.1);
415         enerdata_->term[F_ORIRESDEV]  = (*testValue += 0.1);
416         enerdata_->term[F_COM_PULL]   = (*testValue += 0.1);
417         enerdata_->term[F_ECONSERVED] = (*testValue += 0.1);
418
419         // Group pairs
420         for (int i = 0; i < enerdata_->grpp.nener; i++)
421         {
422             for (int k = 0; k < static_cast<int>(NonBondedEnergyTerms::Count); k++)
423             {
424                 enerdata_->grpp.energyGroupPairTerms[k][i] = (*testValue += 0.1);
425             }
426         }
427
428         // Kinetic energy and related data
429         for (auto& tcstat : ekindata_.tcstat)
430         {
431             tcstat.T      = (*testValue += 0.1);
432             tcstat.lambda = (*testValue += 0.1);
433         }
434         // Removing constant acceleration removed a total increment of 0.6
435         // To avoid unnecessary changes in reference data, we keep the increment
436         (*testValue += 0.6);
437
438         // This conditional is to check whether the ebin was allocated.
439         // Otherwise it will print cosacc data into the first bin.
440         if (inputrec_.cos_accel != 0)
441         {
442             ekindata_.cosacc.cos_accel = (*testValue += 0.1);
443             ekindata_.cosacc.vcos      = (*testValue += 0.1);
444         }
445
446         state_.box[XX][XX] = (*testValue += 0.1);
447         state_.box[XX][YY] = (*testValue += 0.1);
448         state_.box[XX][ZZ] = (*testValue += 0.1);
449         state_.box[YY][XX] = (*testValue += 0.1);
450         state_.box[YY][YY] = (*testValue += 0.1);
451         state_.box[YY][ZZ] = (*testValue += 0.1);
452         state_.box[ZZ][XX] = (*testValue += 0.1);
453         state_.box[ZZ][YY] = (*testValue += 0.1);
454         state_.box[ZZ][ZZ] = (*testValue += 0.1);
455
456         box_[XX][XX] = (*testValue += 0.1);
457         box_[XX][YY] = (*testValue += 0.1);
458         box_[XX][ZZ] = (*testValue += 0.1);
459         box_[YY][XX] = (*testValue += 0.1);
460         box_[YY][YY] = (*testValue += 0.1);
461         box_[YY][ZZ] = (*testValue += 0.1);
462         box_[ZZ][XX] = (*testValue += 0.1);
463         box_[ZZ][YY] = (*testValue += 0.1);
464         box_[ZZ][ZZ] = (*testValue += 0.1);
465
466         constraintsVirial_[XX][XX] = (*testValue += 0.1);
467         constraintsVirial_[XX][YY] = (*testValue += 0.1);
468         constraintsVirial_[XX][ZZ] = (*testValue += 0.1);
469         constraintsVirial_[YY][XX] = (*testValue += 0.1);
470         constraintsVirial_[YY][YY] = (*testValue += 0.1);
471         constraintsVirial_[YY][ZZ] = (*testValue += 0.1);
472         constraintsVirial_[ZZ][XX] = (*testValue += 0.1);
473         constraintsVirial_[ZZ][YY] = (*testValue += 0.1);
474         constraintsVirial_[ZZ][ZZ] = (*testValue += 0.1);
475
476         forceVirial_[XX][XX] = (*testValue += 0.1);
477         forceVirial_[XX][YY] = (*testValue += 0.1);
478         forceVirial_[XX][ZZ] = (*testValue += 0.1);
479         forceVirial_[YY][XX] = (*testValue += 0.1);
480         forceVirial_[YY][YY] = (*testValue += 0.1);
481         forceVirial_[YY][ZZ] = (*testValue += 0.1);
482         forceVirial_[ZZ][XX] = (*testValue += 0.1);
483         forceVirial_[ZZ][YY] = (*testValue += 0.1);
484         forceVirial_[ZZ][ZZ] = (*testValue += 0.1);
485
486         totalVirial_[XX][XX] = (*testValue += 0.1);
487         totalVirial_[XX][YY] = (*testValue += 0.1);
488         totalVirial_[XX][ZZ] = (*testValue += 0.1);
489         totalVirial_[YY][XX] = (*testValue += 0.1);
490         totalVirial_[YY][YY] = (*testValue += 0.1);
491         totalVirial_[YY][ZZ] = (*testValue += 0.1);
492         totalVirial_[ZZ][XX] = (*testValue += 0.1);
493         totalVirial_[ZZ][YY] = (*testValue += 0.1);
494         totalVirial_[ZZ][ZZ] = (*testValue += 0.1);
495
496         pressure_[XX][XX] = (*testValue += 0.1);
497         pressure_[XX][YY] = (*testValue += 0.1);
498         pressure_[XX][ZZ] = (*testValue += 0.1);
499         pressure_[YY][XX] = (*testValue += 0.1);
500         pressure_[YY][YY] = (*testValue += 0.1);
501         pressure_[YY][ZZ] = (*testValue += 0.1);
502         pressure_[ZZ][XX] = (*testValue += 0.1);
503         pressure_[ZZ][YY] = (*testValue += 0.1);
504         pressure_[ZZ][ZZ] = (*testValue += 0.1);
505
506         muTotal_[XX] = (*testValue += 0.1);
507         muTotal_[YY] = (*testValue += 0.1);
508         muTotal_[ZZ] = (*testValue += 0.1);
509
510         state_.boxv[XX][XX] = (*testValue += 0.1);
511         state_.boxv[XX][YY] = (*testValue += 0.1);
512         state_.boxv[XX][ZZ] = (*testValue += 0.1);
513         state_.boxv[YY][XX] = (*testValue += 0.1);
514         state_.boxv[YY][YY] = (*testValue += 0.1);
515         state_.boxv[YY][ZZ] = (*testValue += 0.1);
516         state_.boxv[ZZ][XX] = (*testValue += 0.1);
517         state_.boxv[ZZ][YY] = (*testValue += 0.1);
518         state_.boxv[ZZ][ZZ] = (*testValue += 0.1);
519
520         for (int i = 0; i < inputrec_.opts.ngtc; i++)
521         {
522             inputrec_.opts.ref_t[i] = (*testValue += 0.1);
523         }
524
525         for (index k = 0; k < ssize(mtop_.groups.groups[SimulationAtomGroupType::TemperatureCoupling])
526                                       * inputrec_.opts.nhchainlength;
527              k++)
528         {
529             state_.nosehoover_xi[k]  = (*testValue += 0.1);
530             state_.nosehoover_vxi[k] = (*testValue += 0.1);
531         }
532         for (int k = 0; k < inputrec_.opts.nhchainlength; k++)
533         {
534             state_.nhpres_xi[k]  = (*testValue += 0.1);
535             state_.nhpres_vxi[k] = (*testValue += 0.1);
536         }
537     }
538
539     /*! \brief Check if the contents of the .edr file correspond to the reference data.
540      *
541      * The code below is based on the 'gmx dump' tool.
542      *
543      * \param[in] fileName    Name of the file to check.
544      * \param[in] frameCount  Number of frames to check.
545      */
546     void checkEdrFile(const char* fileName, int frameCount)
547     {
548         ener_file_t  edrFile;
549         gmx_enxnm_t* energyTermsEdr = nullptr;
550         int          numEnergyTermsEdr;
551
552         edrFile = open_enx(fileName, "r");
553         do_enxnms(edrFile, &numEnergyTermsEdr, &energyTermsEdr);
554         assert(energyTermsEdr);
555
556         // Check header
557         TestReferenceChecker edrFileRef(checker_.checkCompound("File", "EnergyFile"));
558         TestReferenceChecker energyTermsRef(
559                 edrFileRef.checkSequenceCompound("EnergyTerms", numEnergyTermsEdr));
560         for (int i = 0; i < numEnergyTermsEdr; i++)
561         {
562             TestReferenceChecker energyTermRef(energyTermsRef.checkCompound("EnergyTerm", nullptr));
563             energyTermRef.checkString(energyTermsEdr[i].name, "Name");
564             energyTermRef.checkString(energyTermsEdr[i].unit, "Units");
565         }
566
567         // Check frames
568         TestReferenceChecker framesRef(edrFileRef.checkSequenceCompound("Frames", frameCount));
569         t_enxframe*          frameEdr;
570         snew(frameEdr, 1);
571         char buffer[22];
572         for (int frameId = 0; frameId < frameCount; frameId++)
573         {
574             bool bCont = do_enx(edrFile, frameEdr);
575             EXPECT_TRUE(bCont) << gmx::formatString("Cant read frame %d from .edr file.", frameId);
576
577             TestReferenceChecker frameRef(framesRef.checkCompound("Frame", nullptr));
578             frameRef.checkReal(frameEdr->t, "Time");
579             frameRef.checkReal(frameEdr->dt, "Timestep");
580             frameRef.checkString(gmx_step_str(frameEdr->step, buffer), "Step");
581             frameRef.checkString(gmx_step_str(frameEdr->nsum, buffer), "NumSteps");
582
583             EXPECT_EQ(frameEdr->nre, numEnergyTermsEdr)
584                     << gmx::formatString("Wrong number of energy terms in frame %d.", frameId);
585             TestReferenceChecker energyValuesRef(
586                     frameRef.checkSequenceCompound("EnergyTerms", numEnergyTermsEdr));
587             for (int i = 0; i < numEnergyTermsEdr; i++)
588             {
589                 TestReferenceChecker energyValueRef(energyValuesRef.checkCompound("EnergyTerm", nullptr));
590                 energyValueRef.checkString(energyTermsEdr[i].name, "Name");
591                 energyValueRef.checkReal(frameEdr->ener[i].e, "Value");
592             }
593         }
594
595         free_enxnms(numEnergyTermsEdr, energyTermsEdr);
596         done_ener_file(edrFile);
597
598         free_enxframe(frameEdr);
599         sfree(frameEdr);
600     }
601 };
602
603 TEST_P(EnergyOutputTest, CheckOutput)
604 {
605     ASSERT_NE(log_, nullptr);
606     // Binary output will be written to the temporary location
607     energyFile_ = open_enx(edrFilename_.c_str(), "w");
608     ASSERT_NE(energyFile_, nullptr);
609
610     EnergyOutputTestParameters parameters = GetParam();
611     inputrec_.etc                         = parameters.temperatureCouplingScheme;
612     inputrec_.epc                         = parameters.pressureCouplingScheme;
613     inputrec_.eI                          = parameters.integrator;
614
615     if (parameters.isBoxTriclinic)
616     {
617         inputrec_.ref_p[YY][XX] = 1.0;
618     }
619
620     MdModulesNotifier             mdModulesNotifier;
621     std::unique_ptr<EnergyOutput> energyOutput =
622             std::make_unique<EnergyOutput>(energyFile_,
623                                            mtop_,
624                                            inputrec_,
625                                            nullptr,
626                                            nullptr,
627                                            parameters.isRerun,
628                                            StartingBehavior::NewSimulation,
629                                            false,
630                                            mdModulesNotifier);
631
632     // Add synthetic data for a single step
633     double testValue = 10.0;
634     for (int frame = 0; frame < parameters.numFrames; frame++)
635     {
636         setStepData(&testValue);
637         energyOutput->addDataAtEnergyStep(false,
638                                           true,
639                                           time_,
640                                           tmass_,
641                                           enerdata_.get(),
642                                           nullptr,
643                                           nullptr,
644                                           box_,
645                                           PTCouplingArrays({ state_.boxv,
646                                                              state_.nosehoover_xi,
647                                                              state_.nosehoover_vxi,
648                                                              state_.nhpres_xi,
649                                                              state_.nhpres_vxi }),
650                                           state_.fep_state,
651                                           constraintsVirial_,
652                                           forceVirial_,
653                                           totalVirial_,
654                                           pressure_,
655                                           &ekindata_,
656                                           muTotal_,
657                                           constraints_.get());
658
659         energyOutput->printAnnealingTemperatures(log_, &mtop_.groups, &inputrec_.opts);
660         energyOutput->printStepToEnergyFile(
661                 energyFile_, true, false, false, log_, 100 * frame, time_, nullptr, nullptr);
662         time_ += 1.0;
663     }
664
665     energyOutput->printAnnealingTemperatures(log_, &mtop_.groups, &inputrec_.opts);
666     energyOutput->printAverages(log_, &mtop_.groups);
667
668     // We need to close the file before the contents are available.
669     logFileGuard_.reset(nullptr);
670
671     done_ener_file(energyFile_);
672
673     // Set tolerance
674     checker_.setDefaultTolerance(relativeToleranceAsFloatingPoint(testValue, 1.0e-5));
675
676     if (parameters.numFrames > 0)
677     {
678         // Test binary output
679         checkEdrFile(edrFilename_.c_str(), parameters.numFrames);
680     }
681
682     // Test printed values
683     checker_.checkInteger(energyOutput->numEnergyTerms(), "Number of Energy Terms");
684     checker_.checkString(TextReader::readFileToString(logFilename_), "log");
685 }
686
687 INSTANTIATE_TEST_CASE_P(WithParameters, EnergyOutputTest, ::testing::ValuesIn(parametersSets));
688
689 } // namespace
690 } // namespace test
691 } // namespace gmx