67a1284714232eb82e9fb71b12b55a0d1e40ea09
[alexxy/gromacs.git] / src / gromacs / fileio / tpxio.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #include "gmxpre.h"
38
39 /* This file is completely threadsafe - keep it that way! */
40
41 #include "tpxio.h"
42
43 #include <cstdio>
44 #include <cstdlib>
45 #include <cstring>
46
47 #include <algorithm>
48 #include <memory>
49 #include <vector>
50
51 #include "gromacs/fileio/filetypes.h"
52 #include "gromacs/fileio/gmxfio.h"
53 #include "gromacs/fileio/gmxfio_xdr.h"
54 #include "gromacs/math/units.h"
55 #include "gromacs/math/vec.h"
56 #include "gromacs/mdtypes/awh_history.h"
57 #include "gromacs/mdtypes/awh_params.h"
58 #include "gromacs/mdtypes/inputrec.h"
59 #include "gromacs/mdtypes/md_enums.h"
60 #include "gromacs/mdtypes/pull_params.h"
61 #include "gromacs/mdtypes/state.h"
62 #include "gromacs/pbcutil/boxutilities.h"
63 #include "gromacs/pbcutil/pbc.h"
64 #include "gromacs/topology/block.h"
65 #include "gromacs/topology/ifunc.h"
66 #include "gromacs/topology/mtop_util.h"
67 #include "gromacs/topology/symtab.h"
68 #include "gromacs/topology/topology.h"
69 #include "gromacs/utility/arraysize.h"
70 #include "gromacs/utility/baseversion.h"
71 #include "gromacs/utility/cstringutil.h"
72 #include "gromacs/utility/fatalerror.h"
73 #include "gromacs/utility/futil.h"
74 #include "gromacs/utility/gmxassert.h"
75 #include "gromacs/utility/keyvaluetreebuilder.h"
76 #include "gromacs/utility/keyvaluetreeserializer.h"
77 #include "gromacs/utility/smalloc.h"
78 #include "gromacs/utility/snprintf.h"
79 #include "gromacs/utility/txtdump.h"
80
81 #define TPX_TAG_RELEASE  "release"
82
83 /*! \brief Tag string for the file format written to run input files
84  * written by this version of the code.
85  *
86  * Change this if you want to change the run input format in a feature
87  * branch. This ensures that there will not be different run input
88  * formats around which cannot be distinguished, while not causing
89  * problems rebasing the feature branch onto upstream changes. When
90  * merging with mainstream GROMACS, set this tag string back to
91  * TPX_TAG_RELEASE, and instead add an element to tpxv.
92  */
93 static const char *tpx_tag = TPX_TAG_RELEASE;
94
95 /*! \brief Enum of values that describe the contents of a tpr file
96  * whose format matches a version number
97  *
98  * The enum helps the code be more self-documenting and ensure merges
99  * do not silently resolve when two patches make the same bump. When
100  * adding new functionality, add a new element just above tpxv_Count
101  * in this enumeration, and write code below that does the right thing
102  * according to the value of file_version.
103  */
104 enum tpxv
105 {
106     tpxv_ComputationalElectrophysiology = 96,                /**< support for ion/water position swaps (computational electrophysiology) */
107     tpxv_Use64BitRandomSeed,                                 /**< change ld_seed from int to int64_t */
108     tpxv_RestrictedBendingAndCombinedAngleTorsionPotentials, /**< potentials for supporting coarse-grained force fields */
109     tpxv_InteractiveMolecularDynamics,                       /**< interactive molecular dynamics (IMD) */
110     tpxv_RemoveObsoleteParameters1,                          /**< remove optimize_fft, dihre_fc, nstcheckpoint */
111     tpxv_PullCoordTypeGeom,                                  /**< add pull type and geometry per group and flat-bottom */
112     tpxv_PullGeomDirRel,                                     /**< add pull geometry direction-relative */
113     tpxv_IntermolecularBondeds,                              /**< permit inter-molecular bonded interactions in the topology */
114     tpxv_CompElWithSwapLayerOffset,                          /**< added parameters for improved CompEl setups */
115     tpxv_CompElPolyatomicIonsAndMultipleIonTypes,            /**< CompEl now can handle polyatomic ions and more than two types of ions */
116     tpxv_RemoveAdress,                                       /**< removed support for AdResS */
117     tpxv_PullCoordNGroup,                                    /**< add ngroup to pull coord */
118     tpxv_RemoveTwinRange,                                    /**< removed support for twin-range interactions */
119     tpxv_ReplacePullPrintCOM12,                              /**< Replaced print-com-1, 2 with pull-print-com */
120     tpxv_PullExternalPotential,                              /**< Added pull type external potential */
121     tpxv_GenericParamsForElectricField,                      /**< Introduced KeyValueTree and moved electric field parameters */
122     tpxv_AcceleratedWeightHistogram,                         /**< sampling with accelerated weight histogram method (AWH) */
123     tpxv_RemoveImplicitSolvation,                            /**< removed support for implicit solvation */
124     tpxv_PullPrevStepCOMAsReference,                         /**< Enabled using the COM of the pull group of the last frame as reference for PBC */
125     tpxv_MimicQMMM,                                          /**< Introduced support for MiMiC QM/MM interface */
126     tpxv_PullAverage,                                        /**< Added possibility to output average pull force and position */
127     tpxv_GenericInternalParameters,                          /**< Added internal parameters for mdrun modules*/
128     tpxv_VSite2FD,                                           /**< Added 2FD type virtual site */
129     tpxv_Count                                               /**< the total number of tpxv versions */
130 };
131
132 /*! \brief Version number of the file format written to run input
133  * files by this version of the code.
134  *
135  * The tpx_version increases whenever the file format in the main
136  * development branch changes, due to an extension of the tpxv enum above.
137  * Backward compatibility for reading old run input files is maintained
138  * by checking this version number against that of the file and then using
139  * the correct code path.
140  *
141  * When developing a feature branch that needs to change the run input
142  * file format, change tpx_tag instead. */
143 static const int tpx_version = tpxv_Count - 1;
144
145
146 /* This number should only be increased when you edit the TOPOLOGY section
147  * or the HEADER of the tpx format.
148  * This way we can maintain forward compatibility too for all analysis tools
149  * and/or external programs that only need to know the atom/residue names,
150  * charges, and bond connectivity.
151  *
152  * It first appeared in tpx version 26, when I also moved the inputrecord
153  * to the end of the tpx file, so we can just skip it if we only
154  * want the topology.
155  *
156  * In particular, it must be increased when adding new elements to
157  * ftupd, so that old code can read new .tpr files.
158  */
159 static const int tpx_generation = 26;
160
161 /* This number should be the most recent backwards incompatible version
162  * I.e., if this number is 9, we cannot read tpx version 9 with this code.
163  */
164 static const int tpx_incompatible_version = 57; // GMX4.0 has version 58
165
166
167
168 /* Struct used to maintain tpx compatibility when function types are added */
169 typedef struct {
170     int fvnr;  /* file version number in which the function type first appeared */
171     int ftype; /* function type */
172 } t_ftupd;
173
174 /*
175  * TODO The following three lines make little sense, please clarify if
176  * you've had to work out how ftupd works.
177  *
178  * The entries should be ordered in:
179  * 1. ascending function type number
180  * 2. ascending file version number
181  *
182  * Because we support reading of old .tpr file versions (even when
183  * mdrun can no longer run the simulation), we need to be able to read
184  * obsolete t_interaction_function types. Any data read from such
185  * fields is discarded. Their names have _NOLONGERUSED appended to
186  * them to make things clear.
187  */
188 static const t_ftupd ftupd[] = {
189     { 70, F_RESTRBONDS        },
190     { tpxv_RestrictedBendingAndCombinedAngleTorsionPotentials, F_RESTRANGLES },
191     { 76, F_LINEAR_ANGLES     },
192     { tpxv_RestrictedBendingAndCombinedAngleTorsionPotentials, F_RESTRDIHS },
193     { tpxv_RestrictedBendingAndCombinedAngleTorsionPotentials, F_CBTDIHS },
194     { 65, F_CMAP              },
195     { 60, F_GB12_NOLONGERUSED },
196     { 61, F_GB13_NOLONGERUSED },
197     { 61, F_GB14_NOLONGERUSED },
198     { 72, F_GBPOL_NOLONGERUSED },
199     { 72, F_NPSOLVATION_NOLONGERUSED },
200     { 93, F_LJ_RECIP          },
201     { 90, F_FBPOSRES          },
202     { 69, F_VTEMP_NOLONGERUSED},
203     { 66, F_PDISPCORR         },
204     { 76, F_ANHARM_POL        },
205     { 79, F_DVDL_COUL         },
206     { 79, F_DVDL_VDW,         },
207     { 79, F_DVDL_BONDED,      },
208     { 79, F_DVDL_RESTRAINT    },
209     { 79, F_DVDL_TEMPERATURE  },
210     { tpxv_GenericInternalParameters, F_DENSITYFITTING },
211     { tpxv_VSite2FD, F_VSITE2FD },
212 };
213 #define NFTUPD asize(ftupd)
214
215 /* Needed for backward compatibility */
216 #define MAXNODES 256
217
218 /**************************************************************
219  *
220  * Now the higer level routines that do io of the structures and arrays
221  *
222  **************************************************************/
223 static void do_pullgrp_tpx_pre95(gmx::ISerializer         *serializer,
224                                  t_pull_group             *pgrp,
225                                  t_pull_coord             *pcrd)
226 {
227     rvec tmp;
228
229     serializer->doInt(&pgrp->nat);
230     if (serializer->reading())
231     {
232         snew(pgrp->ind, pgrp->nat);
233     }
234     serializer->doIntArray(pgrp->ind, pgrp->nat);
235     serializer->doInt(&pgrp->nweight);
236     if (serializer->reading())
237     {
238         snew(pgrp->weight, pgrp->nweight);
239     }
240     serializer->doRealArray(pgrp->weight, pgrp->nweight);
241     serializer->doInt(&pgrp->pbcatom);
242     serializer->doRvec(&pcrd->vec);
243     clear_rvec(pcrd->origin);
244     serializer->doRvec(&tmp);
245     pcrd->init = tmp[0];
246     serializer->doReal(&pcrd->rate);
247     serializer->doReal(&pcrd->k);
248     serializer->doReal(&pcrd->kB);
249 }
250
251 static void do_pull_group(gmx::ISerializer *serializer, t_pull_group *pgrp)
252 {
253     serializer->doInt(&pgrp->nat);
254     if (serializer->reading())
255     {
256         snew(pgrp->ind, pgrp->nat);
257     }
258     serializer->doIntArray(pgrp->ind, pgrp->nat);
259     serializer->doInt(&pgrp->nweight);
260     if (serializer->reading())
261     {
262         snew(pgrp->weight, pgrp->nweight);
263     }
264     serializer->doRealArray(pgrp->weight, pgrp->nweight);
265     serializer->doInt(&pgrp->pbcatom);
266 }
267
268 static void do_pull_coord(gmx::ISerializer *serializer,
269                           t_pull_coord *pcrd,
270                           int file_version,
271                           int ePullOld, int eGeomOld, ivec dimOld)
272 {
273     if (file_version >= tpxv_PullCoordNGroup)
274     {
275         serializer->doInt(&pcrd->eType);
276         if (file_version >= tpxv_PullExternalPotential)
277         {
278             if (pcrd->eType == epullEXTERNAL)
279             {
280                 std::string buf;
281                 if (serializer->reading())
282                 {
283                     serializer->doString(&buf);
284                     pcrd->externalPotentialProvider = gmx_strdup(buf.c_str());
285                 }
286                 else
287                 {
288                     buf = pcrd->externalPotentialProvider;
289                     serializer->doString(&buf);
290                 }
291             }
292             else
293             {
294                 pcrd->externalPotentialProvider = nullptr;
295             }
296         }
297         else
298         {
299             if (serializer->reading())
300             {
301                 pcrd->externalPotentialProvider = nullptr;
302             }
303         }
304         /* Note that we try to support adding new geometries without
305          * changing the tpx version. This requires checks when printing the
306          * geometry string and a check and fatal_error in init_pull.
307          */
308         serializer->doInt(&pcrd->eGeom);
309         serializer->doInt(&pcrd->ngroup);
310         if (pcrd->ngroup <= c_pullCoordNgroupMax)
311         {
312             serializer->doIntArray(pcrd->group, pcrd->ngroup);
313         }
314         else
315         {
316             /* More groups in file than supported, this must be a new geometry
317              * that is not supported by our current code. Since we will not
318              * use the groups for this coord (checks in the pull and WHAM code
319              * ensure this), we can ignore the groups and set ngroup=0.
320              */
321             int *dum;
322             snew(dum, pcrd->ngroup);
323             serializer->doIntArray(dum, pcrd->ngroup);
324             sfree(dum);
325
326             pcrd->ngroup = 0;
327         }
328         serializer->doIvec(&pcrd->dim);
329     }
330     else
331     {
332         pcrd->ngroup = 2;
333         serializer->doInt(&pcrd->group[0]);
334         serializer->doInt(&pcrd->group[1]);
335         if (file_version >= tpxv_PullCoordTypeGeom)
336         {
337             pcrd->ngroup = (pcrd->eGeom == epullgDIRRELATIVE ? 4 : 2);
338             serializer->doInt(&pcrd->eType);
339             serializer->doInt(&pcrd->eGeom);
340             if (pcrd->ngroup == 4)
341             {
342                 serializer->doInt(&pcrd->group[2]);
343                 serializer->doInt(&pcrd->group[3]);
344             }
345             serializer->doIvec(&pcrd->dim);
346         }
347         else
348         {
349             pcrd->eType = ePullOld;
350             pcrd->eGeom = eGeomOld;
351             copy_ivec(dimOld, pcrd->dim);
352         }
353     }
354     serializer->doRvec(&pcrd->origin);
355     serializer->doRvec(&pcrd->vec);
356     if (file_version >= tpxv_PullCoordTypeGeom)
357     {
358         serializer->doBool(&pcrd->bStart);
359     }
360     else
361     {
362         /* This parameter is only printed, but not actually used by mdrun */
363         pcrd->bStart = FALSE;
364     }
365     serializer->doReal(&pcrd->init);
366     serializer->doReal(&pcrd->rate);
367     serializer->doReal(&pcrd->k);
368     serializer->doReal(&pcrd->kB);
369 }
370
371 static void do_expandedvals(gmx::ISerializer         *serializer,
372                             t_expanded               *expand,
373                             t_lambda                 *fepvals,
374                             int                       file_version)
375 {
376     int      n_lambda = fepvals->n_lambda;
377
378     /* reset the lambda calculation window */
379     fepvals->lambda_start_n = 0;
380     fepvals->lambda_stop_n  = n_lambda;
381     if (file_version >= 79)
382     {
383         if (n_lambda > 0)
384         {
385             if (serializer->reading())
386             {
387                 snew(expand->init_lambda_weights, n_lambda);
388             }
389             serializer->doRealArray(expand->init_lambda_weights, n_lambda);
390             serializer->doBool(&expand->bInit_weights);
391         }
392
393         serializer->doInt(&expand->nstexpanded);
394         serializer->doInt(&expand->elmcmove);
395         serializer->doInt(&expand->elamstats);
396         serializer->doInt(&expand->lmc_repeats);
397         serializer->doInt(&expand->gibbsdeltalam);
398         serializer->doInt(&expand->lmc_forced_nstart);
399         serializer->doInt(&expand->lmc_seed);
400         serializer->doReal(&expand->mc_temp);
401         serializer->doBool(&expand->bSymmetrizedTMatrix);
402         serializer->doInt(&expand->nstTij);
403         serializer->doInt(&expand->minvarmin);
404         serializer->doInt(&expand->c_range);
405         serializer->doReal(&expand->wl_scale);
406         serializer->doReal(&expand->wl_ratio);
407         serializer->doReal(&expand->init_wl_delta);
408         serializer->doBool(&expand->bWLoneovert);
409         serializer->doInt(&expand->elmceq);
410         serializer->doInt(&expand->equil_steps);
411         serializer->doInt(&expand->equil_samples);
412         serializer->doInt(&expand->equil_n_at_lam);
413         serializer->doReal(&expand->equil_wl_delta);
414         serializer->doReal(&expand->equil_ratio);
415     }
416 }
417
418 static void do_simtempvals(gmx::ISerializer         *serializer,
419                            t_simtemp                *simtemp,
420                            int                       n_lambda,
421                            int                       file_version)
422 {
423     if (file_version >= 79)
424     {
425         serializer->doInt(&simtemp->eSimTempScale);
426         serializer->doReal(&simtemp->simtemp_high);
427         serializer->doReal(&simtemp->simtemp_low);
428         if (n_lambda > 0)
429         {
430             if (serializer->reading())
431             {
432                 snew(simtemp->temperatures, n_lambda);
433             }
434             serializer->doRealArray(simtemp->temperatures, n_lambda);
435         }
436     }
437 }
438
439 static void do_imd(gmx::ISerializer *serializer, t_IMD *imd)
440 {
441     serializer->doInt(&imd->nat);
442     if (serializer->reading())
443     {
444         snew(imd->ind, imd->nat);
445     }
446     serializer->doIntArray(imd->ind, imd->nat);
447 }
448
449 static void do_fepvals(gmx::ISerializer         *serializer,
450                        t_lambda                 *fepvals,
451                        int                       file_version)
452 {
453     /* i is defined in the ndo_double macro; use g to iterate. */
454     int      g;
455     real     rdum;
456
457     /* free energy values */
458
459     if (file_version >= 79)
460     {
461         serializer->doInt(&fepvals->init_fep_state);
462         serializer->doDouble(&fepvals->init_lambda);
463         serializer->doDouble(&fepvals->delta_lambda);
464     }
465     else if (file_version >= 59)
466     {
467         serializer->doDouble(&fepvals->init_lambda);
468         serializer->doDouble(&fepvals->delta_lambda);
469     }
470     else
471     {
472         serializer->doReal(&rdum);
473         fepvals->init_lambda = rdum;
474         serializer->doReal(&rdum);
475         fepvals->delta_lambda = rdum;
476     }
477     if (file_version >= 79)
478     {
479         serializer->doInt(&fepvals->n_lambda);
480         if (serializer->reading())
481         {
482             snew(fepvals->all_lambda, efptNR);
483         }
484         for (g = 0; g < efptNR; g++)
485         {
486             if (fepvals->n_lambda > 0)
487             {
488                 if (serializer->reading())
489                 {
490                     snew(fepvals->all_lambda[g], fepvals->n_lambda);
491                 }
492                 serializer->doDoubleArray(fepvals->all_lambda[g], fepvals->n_lambda);
493                 serializer->doBoolArray(fepvals->separate_dvdl, efptNR);
494             }
495             else if (fepvals->init_lambda >= 0)
496             {
497                 fepvals->separate_dvdl[efptFEP] = TRUE;
498             }
499         }
500     }
501     else if (file_version >= 64)
502     {
503         serializer->doInt(&fepvals->n_lambda);
504         if (serializer->reading())
505         {
506             int g;
507
508             snew(fepvals->all_lambda, efptNR);
509             /* still allocate the all_lambda array's contents. */
510             for (g = 0; g < efptNR; g++)
511             {
512                 if (fepvals->n_lambda > 0)
513                 {
514                     snew(fepvals->all_lambda[g], fepvals->n_lambda);
515                 }
516             }
517         }
518         serializer->doDoubleArray(fepvals->all_lambda[efptFEP], fepvals->n_lambda);
519         if (fepvals->init_lambda >= 0)
520         {
521             int g, h;
522
523             fepvals->separate_dvdl[efptFEP] = TRUE;
524
525             if (serializer->reading())
526             {
527                 /* copy the contents of the efptFEP lambda component to all
528                    the other components */
529                 for (g = 0; g < efptNR; g++)
530                 {
531                     for (h = 0; h < fepvals->n_lambda; h++)
532                     {
533                         if (g != efptFEP)
534                         {
535                             fepvals->all_lambda[g][h] =
536                                 fepvals->all_lambda[efptFEP][h];
537                         }
538                     }
539                 }
540             }
541         }
542     }
543     else
544     {
545         fepvals->n_lambda     = 0;
546         fepvals->all_lambda   = nullptr;
547         if (fepvals->init_lambda >= 0)
548         {
549             fepvals->separate_dvdl[efptFEP] = TRUE;
550         }
551     }
552     serializer->doReal(&fepvals->sc_alpha);
553     serializer->doInt(&fepvals->sc_power);
554     if (file_version >= 79)
555     {
556         serializer->doReal(&fepvals->sc_r_power);
557     }
558     else
559     {
560         fepvals->sc_r_power = 6.0;
561     }
562     serializer->doReal(&fepvals->sc_sigma);
563     if (serializer->reading())
564     {
565         if (file_version >= 71)
566         {
567             fepvals->sc_sigma_min = fepvals->sc_sigma;
568         }
569         else
570         {
571             fepvals->sc_sigma_min = 0;
572         }
573     }
574     if (file_version >= 79)
575     {
576         serializer->doBool(&fepvals->bScCoul);
577     }
578     else
579     {
580         fepvals->bScCoul = TRUE;
581     }
582     if (file_version >= 64)
583     {
584         serializer->doInt(&fepvals->nstdhdl);
585     }
586     else
587     {
588         fepvals->nstdhdl = 1;
589     }
590
591     if (file_version >= 73)
592     {
593         serializer->doInt(&fepvals->separate_dhdl_file);
594         serializer->doInt(&fepvals->dhdl_derivatives);
595     }
596     else
597     {
598         fepvals->separate_dhdl_file = esepdhdlfileYES;
599         fepvals->dhdl_derivatives   = edhdlderivativesYES;
600     }
601     if (file_version >= 71)
602     {
603         serializer->doInt(&fepvals->dh_hist_size);
604         serializer->doDouble(&fepvals->dh_hist_spacing);
605     }
606     else
607     {
608         fepvals->dh_hist_size    = 0;
609         fepvals->dh_hist_spacing = 0.1;
610     }
611     if (file_version >= 79)
612     {
613         serializer->doInt(&fepvals->edHdLPrintEnergy);
614     }
615     else
616     {
617         fepvals->edHdLPrintEnergy = edHdLPrintEnergyNO;
618     }
619
620     /* handle lambda_neighbors */
621     if ((file_version >= 83 && file_version < 90) || file_version >= 92)
622     {
623         serializer->doInt(&fepvals->lambda_neighbors);
624         if ( (fepvals->lambda_neighbors >= 0) && (fepvals->init_fep_state >= 0) &&
625              (fepvals->init_lambda < 0) )
626         {
627             fepvals->lambda_start_n = (fepvals->init_fep_state -
628                                        fepvals->lambda_neighbors);
629             fepvals->lambda_stop_n = (fepvals->init_fep_state +
630                                       fepvals->lambda_neighbors + 1);
631             if (fepvals->lambda_start_n < 0)
632             {
633                 fepvals->lambda_start_n = 0;;
634             }
635             if (fepvals->lambda_stop_n >= fepvals->n_lambda)
636             {
637                 fepvals->lambda_stop_n = fepvals->n_lambda;
638             }
639         }
640         else
641         {
642             fepvals->lambda_start_n = 0;
643             fepvals->lambda_stop_n  = fepvals->n_lambda;
644         }
645     }
646     else
647     {
648         fepvals->lambda_start_n = 0;
649         fepvals->lambda_stop_n  = fepvals->n_lambda;
650     }
651 }
652
653 static void do_awhBias(gmx::ISerializer *serializer, gmx::AwhBiasParams *awhBiasParams,
654                        int gmx_unused file_version)
655 {
656     serializer->doInt(&awhBiasParams->eTarget);
657     serializer->doDouble(&awhBiasParams->targetBetaScaling);
658     serializer->doDouble(&awhBiasParams->targetCutoff);
659     serializer->doInt(&awhBiasParams->eGrowth);
660     serializer->doInt(&awhBiasParams->bUserData);
661     serializer->doDouble(&awhBiasParams->errorInitial);
662     serializer->doInt(&awhBiasParams->ndim);
663     serializer->doInt(&awhBiasParams->shareGroup);
664     serializer->doBool(&awhBiasParams->equilibrateHistogram);
665
666     if (serializer->reading())
667     {
668         snew(awhBiasParams->dimParams, awhBiasParams->ndim);
669     }
670
671     for (int d = 0; d < awhBiasParams->ndim; d++)
672     {
673         gmx::AwhDimParams *dimParams = &awhBiasParams->dimParams[d];
674
675         serializer->doInt(&dimParams->eCoordProvider);
676         serializer->doInt(&dimParams->coordIndex);
677         serializer->doDouble(&dimParams->origin);
678         serializer->doDouble(&dimParams->end);
679         serializer->doDouble(&dimParams->period);
680         serializer->doDouble(&dimParams->forceConstant);
681         serializer->doDouble(&dimParams->diffusion);
682         serializer->doDouble(&dimParams->coordValueInit);
683         serializer->doDouble(&dimParams->coverDiameter);
684     }
685 }
686
687 static void do_awh(gmx::ISerializer         *serializer,
688                    gmx::AwhParams           *awhParams,
689                    int gmx_unused            file_version)
690 {
691     serializer->doInt(&awhParams->numBias);
692     serializer->doInt(&awhParams->nstOut);
693     serializer->doInt64(&awhParams->seed);
694     serializer->doInt(&awhParams->nstSampleCoord);
695     serializer->doInt(&awhParams->numSamplesUpdateFreeEnergy);
696     serializer->doInt(&awhParams->ePotential);
697     serializer->doBool(&awhParams->shareBiasMultisim);
698
699     if (awhParams->numBias > 0)
700     {
701         if (serializer->reading())
702         {
703             snew(awhParams->awhBiasParams, awhParams->numBias);
704         }
705
706         for (int k = 0; k < awhParams->numBias; k++)
707         {
708             do_awhBias(serializer, &awhParams->awhBiasParams[k], file_version);
709         }
710     }
711 }
712
713 static void do_pull(gmx::ISerializer         *serializer,
714                     pull_params_t            *pull,
715                     int                       file_version,
716                     int                       ePullOld)
717 {
718     int  eGeomOld = -1;
719     ivec dimOld;
720     int  g;
721
722     if (file_version >= 95)
723     {
724         serializer->doInt(&pull->ngroup);
725     }
726     serializer->doInt(&pull->ncoord);
727     if (file_version < 95)
728     {
729         pull->ngroup = pull->ncoord + 1;
730     }
731     if (file_version < tpxv_PullCoordTypeGeom)
732     {
733         real dum;
734
735         serializer->doInt(&eGeomOld);
736         serializer->doIvec(&dimOld);
737         /* The inner cylinder radius, now removed */
738         serializer->doReal(&dum);
739     }
740     serializer->doReal(&pull->cylinder_r);
741     serializer->doReal(&pull->constr_tol);
742     if (file_version >= 95)
743     {
744         serializer->doBool(&pull->bPrintCOM);
745         /* With file_version < 95 this value is set below */
746     }
747     if (file_version >= tpxv_ReplacePullPrintCOM12)
748     {
749         serializer->doBool(&pull->bPrintRefValue);
750         serializer->doBool(&pull->bPrintComp);
751     }
752     else if (file_version >= tpxv_PullCoordTypeGeom)
753     {
754         int idum;
755         serializer->doInt(&idum); /* used to be bPrintCOM2 */
756         serializer->doBool(&pull->bPrintRefValue);
757         serializer->doBool(&pull->bPrintComp);
758     }
759     else
760     {
761         pull->bPrintRefValue = FALSE;
762         pull->bPrintComp     = TRUE;
763     }
764     serializer->doInt(&pull->nstxout);
765     serializer->doInt(&pull->nstfout);
766     if (file_version >= tpxv_PullPrevStepCOMAsReference)
767     {
768         serializer->doBool(&pull->bSetPbcRefToPrevStepCOM);
769     }
770     else
771     {
772         pull->bSetPbcRefToPrevStepCOM = FALSE;
773     }
774     if (serializer->reading())
775     {
776         snew(pull->group, pull->ngroup);
777         snew(pull->coord, pull->ncoord);
778     }
779     if (file_version < 95)
780     {
781         /* epullgPOS for position pulling, before epullgDIRPBC was removed */
782         if (eGeomOld == epullgDIRPBC)
783         {
784             gmx_fatal(FARGS, "pull-geometry=position is no longer supported");
785         }
786         if (eGeomOld > epullgDIRPBC)
787         {
788             eGeomOld -= 1;
789         }
790
791         for (g = 0; g < pull->ngroup; g++)
792         {
793             /* We read and ignore a pull coordinate for group 0 */
794             do_pullgrp_tpx_pre95(serializer, &pull->group[g], &pull->coord[std::max(g-1, 0)]);
795             if (g > 0)
796             {
797                 pull->coord[g-1].group[0] = 0;
798                 pull->coord[g-1].group[1] = g;
799             }
800         }
801
802         pull->bPrintCOM = (pull->group[0].nat > 0);
803     }
804     else
805     {
806         for (g = 0; g < pull->ngroup; g++)
807         {
808             do_pull_group(serializer, &pull->group[g]);
809         }
810         for (g = 0; g < pull->ncoord; g++)
811         {
812             do_pull_coord(serializer, &pull->coord[g],
813                           file_version, ePullOld, eGeomOld, dimOld);
814         }
815     }
816     if (file_version >= tpxv_PullAverage)
817     {
818         gmx_bool v;
819
820         v                  = pull->bXOutAverage;
821         serializer->doBool(&v);
822         pull->bXOutAverage = v;
823         v                  = pull->bFOutAverage;
824         serializer->doBool(&v);
825         pull->bFOutAverage = v;
826     }
827 }
828
829
830 static void do_rotgrp(gmx::ISerializer         *serializer,
831                       t_rotgrp                 *rotg)
832 {
833     serializer->doInt(&rotg->eType);
834     serializer->doInt(&rotg->bMassW);
835     serializer->doInt(&rotg->nat);
836     if (serializer->reading())
837     {
838         snew(rotg->ind, rotg->nat);
839     }
840     serializer->doIntArray(rotg->ind, rotg->nat);
841     if (serializer->reading())
842     {
843         snew(rotg->x_ref, rotg->nat);
844     }
845     serializer->doRvecArray(rotg->x_ref, rotg->nat);
846     serializer->doRvec(&rotg->inputVec);
847     serializer->doRvec(&rotg->pivot);
848     serializer->doReal(&rotg->rate);
849     serializer->doReal(&rotg->k);
850     serializer->doReal(&rotg->slab_dist);
851     serializer->doReal(&rotg->min_gaussian);
852     serializer->doReal(&rotg->eps);
853     serializer->doInt(&rotg->eFittype);
854     serializer->doInt(&rotg->PotAngle_nstep);
855     serializer->doReal(&rotg->PotAngle_step);
856 }
857
858 static void do_rot(gmx::ISerializer         *serializer,
859                    t_rot                    *rot)
860 {
861     int g;
862
863     serializer->doInt(&rot->ngrp);
864     serializer->doInt(&rot->nstrout);
865     serializer->doInt(&rot->nstsout);
866     if (serializer->reading())
867     {
868         snew(rot->grp, rot->ngrp);
869     }
870     for (g = 0; g < rot->ngrp; g++)
871     {
872         do_rotgrp(serializer, &rot->grp[g]);
873     }
874 }
875
876
877 static void do_swapgroup(gmx::ISerializer         *serializer,
878                          t_swapGroup              *g)
879 {
880
881     /* Name of the group or molecule */
882     std::string buf;
883     if (serializer->reading())
884     {
885         serializer->doString(&buf);
886         g->molname = gmx_strdup(buf.c_str());
887     }
888     else
889     {
890         buf = g->molname;
891         serializer->doString(&buf);
892     }
893
894     /* Number of atoms in the group */
895     serializer->doInt(&g->nat);
896
897     /* The group's atom indices */
898     if (serializer->reading())
899     {
900         snew(g->ind, g->nat);
901     }
902     serializer->doIntArray(g->ind, g->nat);
903
904     /* Requested counts for compartments A and B */
905     serializer->doIntArray(g->nmolReq, eCompNR);
906 }
907
908 static void do_swapcoords_tpx(gmx::ISerializer         *serializer,
909                               t_swapcoords             *swap,
910                               int                       file_version)
911 {
912     /* Enums for better readability of the code */
913     enum {
914         eCompA = 0, eCompB
915     };
916     enum {
917         eChannel0 = 0, eChannel1
918     };
919
920
921     if (file_version >= tpxv_CompElPolyatomicIonsAndMultipleIonTypes)
922     {
923         /* The total number of swap groups is the sum of the fixed groups
924          * (split0, split1, solvent), and the user-defined groups (2+ types of ions)
925          */
926         serializer->doInt(&swap->ngrp);
927         if (serializer->reading())
928         {
929             snew(swap->grp, swap->ngrp);
930         }
931         for (int ig = 0; ig < swap->ngrp; ig++)
932         {
933             do_swapgroup(serializer, &swap->grp[ig]);
934         }
935         serializer->doBool(&swap->massw_split[eChannel0]);
936         serializer->doBool(&swap->massw_split[eChannel1]);
937         serializer->doInt(&swap->nstswap);
938         serializer->doInt(&swap->nAverage);
939         serializer->doReal(&swap->threshold);
940         serializer->doReal(&swap->cyl0r);
941         serializer->doReal(&swap->cyl0u);
942         serializer->doReal(&swap->cyl0l);
943         serializer->doReal(&swap->cyl1r);
944         serializer->doReal(&swap->cyl1u);
945         serializer->doReal(&swap->cyl1l);
946     }
947     else
948     {
949         /*** Support reading older CompEl .tpr files ***/
950
951         /* In the original CompEl .tpr files, we always have 5 groups: */
952         swap->ngrp = 5;
953         snew(swap->grp, swap->ngrp);
954
955         swap->grp[eGrpSplit0 ].molname = gmx_strdup("split0" );  // group 0: split0
956         swap->grp[eGrpSplit1 ].molname = gmx_strdup("split1" );  // group 1: split1
957         swap->grp[eGrpSolvent].molname = gmx_strdup("solvent");  // group 2: solvent
958         swap->grp[3          ].molname = gmx_strdup("anions" );  // group 3: anions
959         swap->grp[4          ].molname = gmx_strdup("cations");  // group 4: cations
960
961         serializer->doInt(&swap->grp[3].nat);
962         serializer->doInt(&swap->grp[eGrpSolvent].nat);
963         serializer->doInt(&swap->grp[eGrpSplit0].nat);
964         serializer->doBool(&swap->massw_split[eChannel0]);
965         serializer->doInt(&swap->grp[eGrpSplit1].nat);
966         serializer->doBool(&swap->massw_split[eChannel1]);
967         serializer->doInt(&swap->nstswap);
968         serializer->doInt(&swap->nAverage);
969         serializer->doReal(&swap->threshold);
970         serializer->doReal(&swap->cyl0r);
971         serializer->doReal(&swap->cyl0u);
972         serializer->doReal(&swap->cyl0l);
973         serializer->doReal(&swap->cyl1r);
974         serializer->doReal(&swap->cyl1u);
975         serializer->doReal(&swap->cyl1l);
976
977         // The order[] array keeps compatibility with older .tpr files
978         // by reading in the groups in the classic order
979         {
980             const int order[4] = {3, eGrpSolvent, eGrpSplit0, eGrpSplit1 };
981
982             for (int ig = 0; ig < 4; ig++)
983             {
984                 int g = order[ig];
985                 snew(swap->grp[g].ind, swap->grp[g].nat);
986                 serializer->doIntArray(swap->grp[g].ind, swap->grp[g].nat);
987             }
988         }
989
990         for (int j = eCompA; j <= eCompB; j++)
991         {
992             serializer->doInt(&swap->grp[3].nmolReq[j]); // group 3 = anions
993             serializer->doInt(&swap->grp[4].nmolReq[j]); // group 4 = cations
994         }
995     }                                                    /* End support reading older CompEl .tpr files */
996
997     if (file_version >= tpxv_CompElWithSwapLayerOffset)
998     {
999         serializer->doReal(&swap->bulkOffset[eCompA]);
1000         serializer->doReal(&swap->bulkOffset[eCompB]);
1001     }
1002
1003 }
1004
1005 static void do_legacy_efield(gmx::ISerializer *serializer, gmx::KeyValueTreeObjectBuilder *root)
1006 {
1007     const char *const dimName[] = { "x", "y", "z" };
1008
1009     auto              appliedForcesObj = root->addObject("applied-forces");
1010     auto              efieldObj        = appliedForcesObj.addObject("electric-field");
1011     // The content of the tpr file for this feature has
1012     // been the same since gromacs 4.0 that was used for
1013     // developing.
1014     for (int j = 0; j < DIM; ++j)
1015     {
1016         int n, nt;
1017         serializer->doInt(&n);
1018         serializer->doInt(&nt);
1019         std::vector<real> aa(n+1), phi(nt+1), at(nt+1), phit(nt+1);
1020         serializer->doRealArray(aa.data(),  n);
1021         serializer->doRealArray(phi.data(), n);
1022         serializer->doRealArray(at.data(),  nt);
1023         serializer->doRealArray(phit.data(), nt);
1024         if (n > 0)
1025         {
1026             if (n > 1 || nt > 1)
1027             {
1028                 gmx_fatal(FARGS, "Can not handle tpr files with more than one electric field term per direction.");
1029             }
1030             auto dimObj = efieldObj.addObject(dimName[j]);
1031             dimObj.addValue<real>("E0", aa[0]);
1032             dimObj.addValue<real>("omega", at[0]);
1033             dimObj.addValue<real>("t0", phi[0]);
1034             dimObj.addValue<real>("sigma", phit[0]);
1035         }
1036     }
1037 }
1038
1039
1040 static void do_inputrec(gmx::ISerializer         *serializer,
1041                         t_inputrec               *ir,
1042                         int                       file_version)
1043 {
1044     int      i, j, k, idum = 0;
1045     real     rdum;
1046     gmx_bool bdum = false;
1047
1048     if (file_version != tpx_version)
1049     {
1050         /* Give a warning about features that are not accessible */
1051         fprintf(stderr, "Note: file tpx version %d, software tpx version %d\n",
1052                 file_version, tpx_version);
1053     }
1054
1055     if (file_version == 0)
1056     {
1057         return;
1058     }
1059
1060     gmx::KeyValueTreeBuilder       paramsBuilder;
1061     gmx::KeyValueTreeObjectBuilder paramsObj = paramsBuilder.rootObject();
1062
1063     /* Basic inputrec stuff */
1064     serializer->doInt(&ir->eI);
1065     if (file_version >= 62)
1066     {
1067         serializer->doInt64(&ir->nsteps);
1068     }
1069     else
1070     {
1071         serializer->doInt(&idum);
1072         ir->nsteps = idum;
1073     }
1074
1075     if (file_version >= 62)
1076     {
1077         serializer->doInt64(&ir->init_step);
1078     }
1079     else
1080     {
1081         serializer->doInt(&idum);
1082         ir->init_step = idum;
1083     }
1084
1085     serializer->doInt(&ir->simulation_part);
1086
1087     if (file_version >= 67)
1088     {
1089         serializer->doInt(&ir->nstcalcenergy);
1090     }
1091     else
1092     {
1093         ir->nstcalcenergy = 1;
1094     }
1095     if (file_version >= 81)
1096     {
1097         serializer->doInt(&ir->cutoff_scheme);
1098         if (file_version < 94)
1099         {
1100             ir->cutoff_scheme = 1 - ir->cutoff_scheme;
1101         }
1102     }
1103     else
1104     {
1105         ir->cutoff_scheme = ecutsGROUP;
1106     }
1107     serializer->doInt(&idum); /* used to be ns_type; not used anymore */
1108     serializer->doInt(&ir->nstlist);
1109     serializer->doInt(&idum); /* used to be ndelta; not used anymore */
1110
1111     serializer->doReal(&ir->rtpi);
1112
1113     serializer->doInt(&ir->nstcomm);
1114     serializer->doInt(&ir->comm_mode);
1115
1116     /* ignore nstcheckpoint */
1117     if (file_version < tpxv_RemoveObsoleteParameters1)
1118     {
1119         serializer->doInt(&idum);
1120     }
1121
1122     serializer->doInt(&ir->nstcgsteep);
1123
1124     serializer->doInt(&ir->nbfgscorr);
1125
1126     serializer->doInt(&ir->nstlog);
1127     serializer->doInt(&ir->nstxout);
1128     serializer->doInt(&ir->nstvout);
1129     serializer->doInt(&ir->nstfout);
1130     serializer->doInt(&ir->nstenergy);
1131     serializer->doInt(&ir->nstxout_compressed);
1132     if (file_version >= 59)
1133     {
1134         serializer->doDouble(&ir->init_t);
1135         serializer->doDouble(&ir->delta_t);
1136     }
1137     else
1138     {
1139         serializer->doReal(&rdum);
1140         ir->init_t = rdum;
1141         serializer->doReal(&rdum);
1142         ir->delta_t = rdum;
1143     }
1144     serializer->doReal(&ir->x_compression_precision);
1145     if (file_version >= 81)
1146     {
1147         serializer->doReal(&ir->verletbuf_tol);
1148     }
1149     else
1150     {
1151         ir->verletbuf_tol = 0;
1152     }
1153     serializer->doReal(&ir->rlist);
1154     if (file_version >= 67 && file_version < tpxv_RemoveTwinRange)
1155     {
1156         if (serializer->reading())
1157         {
1158             // Reading such a file version could invoke the twin-range
1159             // scheme, about which mdrun should give a fatal error.
1160             real dummy_rlistlong = -1;
1161             serializer->doReal(&dummy_rlistlong);
1162
1163             if (ir->rlist > 0 && (dummy_rlistlong == 0 || dummy_rlistlong > ir->rlist))
1164             {
1165                 // Get mdrun to issue an error (regardless of
1166                 // ir->cutoff_scheme).
1167                 ir->useTwinRange = true;
1168             }
1169             else
1170             {
1171                 // grompp used to set rlistlong actively. Users were
1172                 // probably also confused and set rlistlong == rlist.
1173                 // However, in all remaining cases, it is safe to let
1174                 // mdrun proceed normally.
1175                 ir->useTwinRange = false;
1176             }
1177         }
1178     }
1179     else
1180     {
1181         // No need to read or write anything
1182         ir->useTwinRange = false;
1183     }
1184     if (file_version >= 82 && file_version != 90)
1185     {
1186         // Multiple time-stepping is no longer enabled, but the old
1187         // support required the twin-range scheme, for which mdrun
1188         // already emits a fatal error.
1189         int dummy_nstcalclr = -1;
1190         serializer->doInt(&dummy_nstcalclr);
1191     }
1192     serializer->doInt(&ir->coulombtype);
1193     if (file_version >= 81)
1194     {
1195         serializer->doInt(&ir->coulomb_modifier);
1196     }
1197     else
1198     {
1199         ir->coulomb_modifier = (ir->cutoff_scheme == ecutsVERLET ? eintmodPOTSHIFT : eintmodNONE);
1200     }
1201     serializer->doReal(&ir->rcoulomb_switch);
1202     serializer->doReal(&ir->rcoulomb);
1203     serializer->doInt(&ir->vdwtype);
1204     if (file_version >= 81)
1205     {
1206         serializer->doInt(&ir->vdw_modifier);
1207     }
1208     else
1209     {
1210         ir->vdw_modifier = (ir->cutoff_scheme == ecutsVERLET ? eintmodPOTSHIFT : eintmodNONE);
1211     }
1212     serializer->doReal(&ir->rvdw_switch);
1213     serializer->doReal(&ir->rvdw);
1214     serializer->doInt(&ir->eDispCorr);
1215     serializer->doReal(&ir->epsilon_r);
1216     serializer->doReal(&ir->epsilon_rf);
1217     serializer->doReal(&ir->tabext);
1218
1219     // This permits reading a .tpr file that used implicit solvent,
1220     // and later permitting mdrun to refuse to run it.
1221     if (serializer->reading())
1222     {
1223         if (file_version < tpxv_RemoveImplicitSolvation)
1224         {
1225             serializer->doInt(&idum);
1226             serializer->doInt(&idum);
1227             serializer->doReal(&rdum);
1228             serializer->doReal(&rdum);
1229             serializer->doInt(&idum);
1230             ir->implicit_solvent = (idum > 0);
1231         }
1232         else
1233         {
1234             ir->implicit_solvent = false;
1235         }
1236         if (file_version < tpxv_RemoveImplicitSolvation)
1237         {
1238             serializer->doReal(&rdum);
1239             serializer->doReal(&rdum);
1240             serializer->doReal(&rdum);
1241             serializer->doReal(&rdum);
1242             if (file_version >= 60)
1243             {
1244                 serializer->doReal(&rdum);
1245                 serializer->doInt(&idum);
1246             }
1247             serializer->doReal(&rdum);
1248         }
1249     }
1250
1251     if (file_version >= 81)
1252     {
1253         serializer->doReal(&ir->fourier_spacing);
1254     }
1255     else
1256     {
1257         ir->fourier_spacing = 0.0;
1258     }
1259     serializer->doInt(&ir->nkx);
1260     serializer->doInt(&ir->nky);
1261     serializer->doInt(&ir->nkz);
1262     serializer->doInt(&ir->pme_order);
1263     serializer->doReal(&ir->ewald_rtol);
1264
1265     if (file_version >= 93)
1266     {
1267         serializer->doReal(&ir->ewald_rtol_lj);
1268     }
1269     else
1270     {
1271         ir->ewald_rtol_lj = ir->ewald_rtol;
1272     }
1273     serializer->doInt(&ir->ewald_geometry);
1274     serializer->doReal(&ir->epsilon_surface);
1275
1276     /* ignore bOptFFT */
1277     if (file_version < tpxv_RemoveObsoleteParameters1)
1278     {
1279         serializer->doBool(&bdum);
1280     }
1281
1282     if (file_version >= 93)
1283     {
1284         serializer->doInt(&ir->ljpme_combination_rule);
1285     }
1286     serializer->doBool(&ir->bContinuation);
1287     serializer->doInt(&ir->etc);
1288     /* before version 18, ir->etc was a gmx_bool (ir->btc),
1289      * but the values 0 and 1 still mean no and
1290      * berendsen temperature coupling, respectively.
1291      */
1292     if (file_version >= 79)
1293     {
1294         serializer->doBool(&ir->bPrintNHChains);
1295     }
1296     if (file_version >= 71)
1297     {
1298         serializer->doInt(&ir->nsttcouple);
1299     }
1300     else
1301     {
1302         ir->nsttcouple = ir->nstcalcenergy;
1303     }
1304     serializer->doInt(&ir->epc);
1305     serializer->doInt(&ir->epct);
1306     if (file_version >= 71)
1307     {
1308         serializer->doInt(&ir->nstpcouple);
1309     }
1310     else
1311     {
1312         ir->nstpcouple = ir->nstcalcenergy;
1313     }
1314     serializer->doReal(&ir->tau_p);
1315     serializer->doRvec(&ir->ref_p[XX]);
1316     serializer->doRvec(&ir->ref_p[YY]);
1317     serializer->doRvec(&ir->ref_p[ZZ]);
1318     serializer->doRvec(&ir->compress[XX]);
1319     serializer->doRvec(&ir->compress[YY]);
1320     serializer->doRvec(&ir->compress[ZZ]);
1321     serializer->doInt(&ir->refcoord_scaling);
1322     serializer->doRvec(&ir->posres_com);
1323     serializer->doRvec(&ir->posres_comB);
1324
1325     if (file_version < 79)
1326     {
1327         serializer->doInt(&ir->andersen_seed);
1328     }
1329     else
1330     {
1331         ir->andersen_seed = 0;
1332     }
1333
1334     serializer->doReal(&ir->shake_tol);
1335
1336     serializer->doInt(&ir->efep);
1337     do_fepvals(serializer, ir->fepvals, file_version);
1338
1339     if (file_version >= 79)
1340     {
1341         serializer->doBool(&ir->bSimTemp);
1342         if (ir->bSimTemp)
1343         {
1344             ir->bSimTemp = TRUE;
1345         }
1346     }
1347     else
1348     {
1349         ir->bSimTemp = FALSE;
1350     }
1351     if (ir->bSimTemp)
1352     {
1353         do_simtempvals(serializer, ir->simtempvals, ir->fepvals->n_lambda, file_version);
1354     }
1355
1356     if (file_version >= 79)
1357     {
1358         serializer->doBool(&ir->bExpanded);
1359         if (ir->bExpanded)
1360         {
1361             ir->bExpanded = TRUE;
1362         }
1363         else
1364         {
1365             ir->bExpanded = FALSE;
1366         }
1367     }
1368     if (ir->bExpanded)
1369     {
1370         do_expandedvals(serializer, ir->expandedvals, ir->fepvals, file_version);
1371     }
1372
1373     serializer->doInt(&ir->eDisre);
1374     serializer->doInt(&ir->eDisreWeighting);
1375     serializer->doBool(&ir->bDisreMixed);
1376     serializer->doReal(&ir->dr_fc);
1377     serializer->doReal(&ir->dr_tau);
1378     serializer->doInt(&ir->nstdisreout);
1379     serializer->doReal(&ir->orires_fc);
1380     serializer->doReal(&ir->orires_tau);
1381     serializer->doInt(&ir->nstorireout);
1382
1383     /* ignore dihre_fc */
1384     if (file_version < 79)
1385     {
1386         serializer->doReal(&rdum);
1387     }
1388
1389     serializer->doReal(&ir->em_stepsize);
1390     serializer->doReal(&ir->em_tol);
1391     serializer->doBool(&ir->bShakeSOR);
1392     serializer->doInt(&ir->niter);
1393     serializer->doReal(&ir->fc_stepsize);
1394     serializer->doInt(&ir->eConstrAlg);
1395     serializer->doInt(&ir->nProjOrder);
1396     serializer->doReal(&ir->LincsWarnAngle);
1397     serializer->doInt(&ir->nLincsIter);
1398     serializer->doReal(&ir->bd_fric);
1399     if (file_version >= tpxv_Use64BitRandomSeed)
1400     {
1401         serializer->doInt64(&ir->ld_seed);
1402     }
1403     else
1404     {
1405         serializer->doInt(&idum);
1406         ir->ld_seed = idum;
1407     }
1408
1409     for (i = 0; i < DIM; i++)
1410     {
1411         serializer->doRvec(&ir->deform[i]);
1412     }
1413     serializer->doReal(&ir->cos_accel);
1414
1415     serializer->doInt(&ir->userint1);
1416     serializer->doInt(&ir->userint2);
1417     serializer->doInt(&ir->userint3);
1418     serializer->doInt(&ir->userint4);
1419     serializer->doReal(&ir->userreal1);
1420     serializer->doReal(&ir->userreal2);
1421     serializer->doReal(&ir->userreal3);
1422     serializer->doReal(&ir->userreal4);
1423
1424     /* AdResS is removed, but we need to be able to read old files,
1425        and let mdrun refuse to run them */
1426     if (file_version >= 77 && file_version < tpxv_RemoveAdress)
1427     {
1428         serializer->doBool(&ir->bAdress);
1429         if (ir->bAdress)
1430         {
1431             int  idum, numThermoForceGroups, numEnergyGroups;
1432             real rdum;
1433             rvec rvecdum;
1434             serializer->doInt(&idum);
1435             serializer->doReal(&rdum);
1436             serializer->doReal(&rdum);
1437             serializer->doReal(&rdum);
1438             serializer->doInt(&idum);
1439             serializer->doInt(&idum);
1440             serializer->doRvec(&rvecdum);
1441             serializer->doInt(&numThermoForceGroups);
1442             serializer->doReal(&rdum);
1443             serializer->doInt(&numEnergyGroups);
1444             serializer->doInt(&idum);
1445
1446             if (numThermoForceGroups > 0)
1447             {
1448                 std::vector<int> idumn(numThermoForceGroups);
1449                 serializer->doIntArray(idumn.data(), idumn.size());
1450             }
1451             if (numEnergyGroups > 0)
1452             {
1453                 std::vector<int> idumn(numEnergyGroups);
1454                 serializer->doIntArray(idumn.data(), idumn.size());
1455             }
1456         }
1457     }
1458     else
1459     {
1460         ir->bAdress = FALSE;
1461     }
1462
1463     /* pull stuff */
1464     {
1465         int ePullOld = 0;
1466
1467         if (file_version >= tpxv_PullCoordTypeGeom)
1468         {
1469             serializer->doBool(&ir->bPull);
1470         }
1471         else
1472         {
1473             serializer->doInt(&ePullOld);
1474             ir->bPull = (ePullOld > 0);
1475             /* We removed the first ePull=ePullNo for the enum */
1476             ePullOld -= 1;
1477         }
1478         if (ir->bPull)
1479         {
1480             if (serializer->reading())
1481             {
1482                 snew(ir->pull, 1);
1483             }
1484             do_pull(serializer, ir->pull, file_version, ePullOld);
1485         }
1486     }
1487
1488     if (file_version >= tpxv_AcceleratedWeightHistogram)
1489     {
1490         serializer->doBool(&ir->bDoAwh);
1491
1492         if (ir->bDoAwh)
1493         {
1494             if (serializer->reading())
1495             {
1496                 snew(ir->awhParams, 1);
1497             }
1498             do_awh(serializer, ir->awhParams, file_version);
1499         }
1500     }
1501     else
1502     {
1503         ir->bDoAwh = FALSE;
1504     }
1505
1506     /* Enforced rotation */
1507     if (file_version >= 74)
1508     {
1509         serializer->doBool(&ir->bRot);
1510         if (ir->bRot)
1511         {
1512             if (serializer->reading())
1513             {
1514                 snew(ir->rot, 1);
1515             }
1516             do_rot(serializer, ir->rot);
1517         }
1518     }
1519     else
1520     {
1521         ir->bRot = FALSE;
1522     }
1523
1524     /* Interactive molecular dynamics */
1525     if (file_version >= tpxv_InteractiveMolecularDynamics)
1526     {
1527         serializer->doBool(&ir->bIMD);
1528         if (ir->bIMD)
1529         {
1530             if (serializer->reading())
1531             {
1532                 snew(ir->imd, 1);
1533             }
1534             do_imd(serializer, ir->imd);
1535         }
1536     }
1537     else
1538     {
1539         /* We don't support IMD sessions for old .tpr files */
1540         ir->bIMD = FALSE;
1541     }
1542
1543     /* grpopts stuff */
1544     serializer->doInt(&ir->opts.ngtc);
1545     if (file_version >= 69)
1546     {
1547         serializer->doInt(&ir->opts.nhchainlength);
1548     }
1549     else
1550     {
1551         ir->opts.nhchainlength = 1;
1552     }
1553     serializer->doInt(&ir->opts.ngacc);
1554     serializer->doInt(&ir->opts.ngfrz);
1555     serializer->doInt(&ir->opts.ngener);
1556
1557     if (serializer->reading())
1558     {
1559         snew(ir->opts.nrdf,   ir->opts.ngtc);
1560         snew(ir->opts.ref_t,  ir->opts.ngtc);
1561         snew(ir->opts.annealing, ir->opts.ngtc);
1562         snew(ir->opts.anneal_npoints, ir->opts.ngtc);
1563         snew(ir->opts.anneal_time, ir->opts.ngtc);
1564         snew(ir->opts.anneal_temp, ir->opts.ngtc);
1565         snew(ir->opts.tau_t,  ir->opts.ngtc);
1566         snew(ir->opts.nFreeze, ir->opts.ngfrz);
1567         snew(ir->opts.acc,    ir->opts.ngacc);
1568         snew(ir->opts.egp_flags, ir->opts.ngener*ir->opts.ngener);
1569     }
1570     if (ir->opts.ngtc > 0)
1571     {
1572         serializer->doRealArray(ir->opts.nrdf, ir->opts.ngtc);
1573         serializer->doRealArray(ir->opts.ref_t, ir->opts.ngtc);
1574         serializer->doRealArray(ir->opts.tau_t, ir->opts.ngtc);
1575     }
1576     if (ir->opts.ngfrz > 0)
1577     {
1578         serializer->doIvecArray(ir->opts.nFreeze, ir->opts.ngfrz);
1579     }
1580     if (ir->opts.ngacc > 0)
1581     {
1582         serializer->doRvecArray(ir->opts.acc, ir->opts.ngacc);
1583     }
1584     serializer->doIntArray(ir->opts.egp_flags,
1585                            ir->opts.ngener*ir->opts.ngener);
1586
1587     /* First read the lists with annealing and npoints for each group */
1588     serializer->doIntArray(ir->opts.annealing, ir->opts.ngtc);
1589     serializer->doIntArray(ir->opts.anneal_npoints, ir->opts.ngtc);
1590     for (j = 0; j < (ir->opts.ngtc); j++)
1591     {
1592         k = ir->opts.anneal_npoints[j];
1593         if (serializer->reading())
1594         {
1595             snew(ir->opts.anneal_time[j], k);
1596             snew(ir->opts.anneal_temp[j], k);
1597         }
1598         serializer->doRealArray(ir->opts.anneal_time[j], k);
1599         serializer->doRealArray(ir->opts.anneal_temp[j], k);
1600     }
1601     /* Walls */
1602     {
1603         serializer->doInt(&ir->nwall);
1604         serializer->doInt(&ir->wall_type);
1605         serializer->doReal(&ir->wall_r_linpot);
1606         serializer->doInt(&ir->wall_atomtype[0]);
1607         serializer->doInt(&ir->wall_atomtype[1]);
1608         serializer->doReal(&ir->wall_density[0]);
1609         serializer->doReal(&ir->wall_density[1]);
1610         serializer->doReal(&ir->wall_ewald_zfac);
1611     }
1612
1613     /* Cosine stuff for electric fields */
1614     if (file_version < tpxv_GenericParamsForElectricField)
1615     {
1616         do_legacy_efield(serializer, &paramsObj);
1617     }
1618
1619     /* Swap ions */
1620     if (file_version >= tpxv_ComputationalElectrophysiology)
1621     {
1622         serializer->doInt(&ir->eSwapCoords);
1623         if (ir->eSwapCoords != eswapNO)
1624         {
1625             if (serializer->reading())
1626             {
1627                 snew(ir->swap, 1);
1628             }
1629             do_swapcoords_tpx(serializer, ir->swap, file_version);
1630         }
1631     }
1632
1633     /* QMMM stuff */
1634     {
1635         serializer->doBool(&ir->bQMMM);
1636         serializer->doInt(&ir->QMMMscheme);
1637         serializer->doReal(&ir->scalefactor);
1638         serializer->doInt(&ir->opts.ngQM);
1639         if (serializer->reading())
1640         {
1641             snew(ir->opts.QMmethod,    ir->opts.ngQM);
1642             snew(ir->opts.QMbasis,     ir->opts.ngQM);
1643             snew(ir->opts.QMcharge,    ir->opts.ngQM);
1644             snew(ir->opts.QMmult,      ir->opts.ngQM);
1645             snew(ir->opts.bSH,         ir->opts.ngQM);
1646             snew(ir->opts.CASorbitals, ir->opts.ngQM);
1647             snew(ir->opts.CASelectrons, ir->opts.ngQM);
1648             snew(ir->opts.SAon,        ir->opts.ngQM);
1649             snew(ir->opts.SAoff,       ir->opts.ngQM);
1650             snew(ir->opts.SAsteps,     ir->opts.ngQM);
1651         }
1652         if (ir->opts.ngQM > 0 && ir->bQMMM)
1653         {
1654             serializer->doIntArray(ir->opts.QMmethod, ir->opts.ngQM);
1655             serializer->doIntArray(ir->opts.QMbasis, ir->opts.ngQM);
1656             serializer->doIntArray(ir->opts.QMcharge, ir->opts.ngQM);
1657             serializer->doIntArray(ir->opts.QMmult, ir->opts.ngQM);
1658             serializer->doBoolArray(ir->opts.bSH, ir->opts.ngQM);
1659             serializer->doIntArray(ir->opts.CASorbitals, ir->opts.ngQM);
1660             serializer->doIntArray(ir->opts.CASelectrons, ir->opts.ngQM);
1661             serializer->doRealArray(ir->opts.SAon, ir->opts.ngQM);
1662             serializer->doRealArray(ir->opts.SAoff, ir->opts.ngQM);
1663             serializer->doIntArray(ir->opts.SAsteps, ir->opts.ngQM);
1664             /* We leave in dummy i/o for removed parameters to avoid
1665              * changing the tpr format for every QMMM change.
1666              */
1667             std::vector<int> dummy(ir->opts.ngQM, 0);
1668             serializer->doIntArray(dummy.data(), ir->opts.ngQM);
1669             serializer->doIntArray(dummy.data(), ir->opts.ngQM);
1670         }
1671         /* end of QMMM stuff */
1672     }
1673
1674     if (file_version >= tpxv_GenericParamsForElectricField)
1675     {
1676         if (serializer->reading())
1677         {
1678             paramsObj.mergeObject(
1679                     gmx::deserializeKeyValueTree(serializer));
1680         }
1681         else
1682         {
1683             GMX_RELEASE_ASSERT(ir->params != nullptr,
1684                                "Parameters should be present when writing inputrec");
1685             gmx::serializeKeyValueTree(*ir->params, serializer);
1686         }
1687     }
1688     if (serializer->reading())
1689     {
1690         ir->params = new gmx::KeyValueTreeObject(paramsBuilder.build());
1691         // Initialize internal parameters to an empty kvt for all tpr versions
1692         ir->internalParameters = std::make_unique<gmx::KeyValueTreeObject>();
1693     }
1694
1695     if (file_version >= tpxv_GenericInternalParameters)
1696     {
1697         if (serializer->reading())
1698         {
1699             ir->internalParameters = std::make_unique<gmx::KeyValueTreeObject>(gmx::deserializeKeyValueTree(serializer));
1700         }
1701         else
1702         {
1703             GMX_RELEASE_ASSERT(ir->internalParameters != nullptr,
1704                                "Parameters should be present when writing inputrec");
1705             gmx::serializeKeyValueTree(*ir->internalParameters, serializer);
1706         }
1707     }
1708 }
1709
1710
1711 static void do_harm(gmx::ISerializer *serializer, t_iparams *iparams)
1712 {
1713     serializer->doReal(&iparams->harmonic.rA);
1714     serializer->doReal(&iparams->harmonic.krA);
1715     serializer->doReal(&iparams->harmonic.rB);
1716     serializer->doReal(&iparams->harmonic.krB);
1717 }
1718
1719 static void do_iparams(gmx::ISerializer         *serializer,
1720                        t_functype                ftype,
1721                        t_iparams                *iparams,
1722                        int                       file_version)
1723 {
1724     int      idum;
1725     real     rdum;
1726
1727     switch (ftype)
1728     {
1729         case F_ANGLES:
1730         case F_G96ANGLES:
1731         case F_BONDS:
1732         case F_G96BONDS:
1733         case F_HARMONIC:
1734         case F_IDIHS:
1735             do_harm(serializer, iparams);
1736             if ((ftype == F_ANGRES || ftype == F_ANGRESZ) && serializer->reading())
1737             {
1738                 /* Correct incorrect storage of parameters */
1739                 iparams->pdihs.phiB = iparams->pdihs.phiA;
1740                 iparams->pdihs.cpB  = iparams->pdihs.cpA;
1741             }
1742             break;
1743         case F_RESTRANGLES:
1744             serializer->doReal(&iparams->harmonic.rA);
1745             serializer->doReal(&iparams->harmonic.krA);
1746             break;
1747         case F_LINEAR_ANGLES:
1748             serializer->doReal(&iparams->linangle.klinA);
1749             serializer->doReal(&iparams->linangle.aA);
1750             serializer->doReal(&iparams->linangle.klinB);
1751             serializer->doReal(&iparams->linangle.aB);
1752             break;
1753         case F_FENEBONDS:
1754             serializer->doReal(&iparams->fene.bm);
1755             serializer->doReal(&iparams->fene.kb);
1756             break;
1757
1758         case F_RESTRBONDS:
1759             serializer->doReal(&iparams->restraint.lowA);
1760             serializer->doReal(&iparams->restraint.up1A);
1761             serializer->doReal(&iparams->restraint.up2A);
1762             serializer->doReal(&iparams->restraint.kA);
1763             serializer->doReal(&iparams->restraint.lowB);
1764             serializer->doReal(&iparams->restraint.up1B);
1765             serializer->doReal(&iparams->restraint.up2B);
1766             serializer->doReal(&iparams->restraint.kB);
1767             break;
1768         case F_TABBONDS:
1769         case F_TABBONDSNC:
1770         case F_TABANGLES:
1771         case F_TABDIHS:
1772             serializer->doReal(&iparams->tab.kA);
1773             serializer->doInt(&iparams->tab.table);
1774             serializer->doReal(&iparams->tab.kB);
1775             break;
1776         case F_CROSS_BOND_BONDS:
1777             serializer->doReal(&iparams->cross_bb.r1e);
1778             serializer->doReal(&iparams->cross_bb.r2e);
1779             serializer->doReal(&iparams->cross_bb.krr);
1780             break;
1781         case F_CROSS_BOND_ANGLES:
1782             serializer->doReal(&iparams->cross_ba.r1e);
1783             serializer->doReal(&iparams->cross_ba.r2e);
1784             serializer->doReal(&iparams->cross_ba.r3e);
1785             serializer->doReal(&iparams->cross_ba.krt);
1786             break;
1787         case F_UREY_BRADLEY:
1788             serializer->doReal(&iparams->u_b.thetaA);
1789             serializer->doReal(&iparams->u_b.kthetaA);
1790             serializer->doReal(&iparams->u_b.r13A);
1791             serializer->doReal(&iparams->u_b.kUBA);
1792             if (file_version >= 79)
1793             {
1794                 serializer->doReal(&iparams->u_b.thetaB);
1795                 serializer->doReal(&iparams->u_b.kthetaB);
1796                 serializer->doReal(&iparams->u_b.r13B);
1797                 serializer->doReal(&iparams->u_b.kUBB);
1798             }
1799             else
1800             {
1801                 iparams->u_b.thetaB  = iparams->u_b.thetaA;
1802                 iparams->u_b.kthetaB = iparams->u_b.kthetaA;
1803                 iparams->u_b.r13B    = iparams->u_b.r13A;
1804                 iparams->u_b.kUBB    = iparams->u_b.kUBA;
1805             }
1806             break;
1807         case F_QUARTIC_ANGLES:
1808             serializer->doReal(&iparams->qangle.theta);
1809             serializer->doRealArray(iparams->qangle.c, 5);
1810             break;
1811         case F_BHAM:
1812             serializer->doReal(&iparams->bham.a);
1813             serializer->doReal(&iparams->bham.b);
1814             serializer->doReal(&iparams->bham.c);
1815             break;
1816         case F_MORSE:
1817             serializer->doReal(&iparams->morse.b0A);
1818             serializer->doReal(&iparams->morse.cbA);
1819             serializer->doReal(&iparams->morse.betaA);
1820             if (file_version >= 79)
1821             {
1822                 serializer->doReal(&iparams->morse.b0B);
1823                 serializer->doReal(&iparams->morse.cbB);
1824                 serializer->doReal(&iparams->morse.betaB);
1825             }
1826             else
1827             {
1828                 iparams->morse.b0B   = iparams->morse.b0A;
1829                 iparams->morse.cbB   = iparams->morse.cbA;
1830                 iparams->morse.betaB = iparams->morse.betaA;
1831             }
1832             break;
1833         case F_CUBICBONDS:
1834             serializer->doReal(&iparams->cubic.b0);
1835             serializer->doReal(&iparams->cubic.kb);
1836             serializer->doReal(&iparams->cubic.kcub);
1837             break;
1838         case F_CONNBONDS:
1839             break;
1840         case F_POLARIZATION:
1841             serializer->doReal(&iparams->polarize.alpha);
1842             break;
1843         case F_ANHARM_POL:
1844             serializer->doReal(&iparams->anharm_polarize.alpha);
1845             serializer->doReal(&iparams->anharm_polarize.drcut);
1846             serializer->doReal(&iparams->anharm_polarize.khyp);
1847             break;
1848         case F_WATER_POL:
1849             serializer->doReal(&iparams->wpol.al_x);
1850             serializer->doReal(&iparams->wpol.al_y);
1851             serializer->doReal(&iparams->wpol.al_z);
1852             serializer->doReal(&iparams->wpol.rOH);
1853             serializer->doReal(&iparams->wpol.rHH);
1854             serializer->doReal(&iparams->wpol.rOD);
1855             break;
1856         case F_THOLE_POL:
1857             serializer->doReal(&iparams->thole.a);
1858             serializer->doReal(&iparams->thole.alpha1);
1859             serializer->doReal(&iparams->thole.alpha2);
1860             serializer->doReal(&iparams->thole.rfac);
1861             break;
1862         case F_LJ:
1863             serializer->doReal(&iparams->lj.c6);
1864             serializer->doReal(&iparams->lj.c12);
1865             break;
1866         case F_LJ14:
1867             serializer->doReal(&iparams->lj14.c6A);
1868             serializer->doReal(&iparams->lj14.c12A);
1869             serializer->doReal(&iparams->lj14.c6B);
1870             serializer->doReal(&iparams->lj14.c12B);
1871             break;
1872         case F_LJC14_Q:
1873             serializer->doReal(&iparams->ljc14.fqq);
1874             serializer->doReal(&iparams->ljc14.qi);
1875             serializer->doReal(&iparams->ljc14.qj);
1876             serializer->doReal(&iparams->ljc14.c6);
1877             serializer->doReal(&iparams->ljc14.c12);
1878             break;
1879         case F_LJC_PAIRS_NB:
1880             serializer->doReal(&iparams->ljcnb.qi);
1881             serializer->doReal(&iparams->ljcnb.qj);
1882             serializer->doReal(&iparams->ljcnb.c6);
1883             serializer->doReal(&iparams->ljcnb.c12);
1884             break;
1885         case F_PDIHS:
1886         case F_PIDIHS:
1887         case F_ANGRES:
1888         case F_ANGRESZ:
1889             serializer->doReal(&iparams->pdihs.phiA);
1890             serializer->doReal(&iparams->pdihs.cpA);
1891             serializer->doReal(&iparams->pdihs.phiB);
1892             serializer->doReal(&iparams->pdihs.cpB);
1893             serializer->doInt(&iparams->pdihs.mult);
1894             break;
1895         case F_RESTRDIHS:
1896             serializer->doReal(&iparams->pdihs.phiA);
1897             serializer->doReal(&iparams->pdihs.cpA);
1898             break;
1899         case F_DISRES:
1900             serializer->doInt(&iparams->disres.label);
1901             serializer->doInt(&iparams->disres.type);
1902             serializer->doReal(&iparams->disres.low);
1903             serializer->doReal(&iparams->disres.up1);
1904             serializer->doReal(&iparams->disres.up2);
1905             serializer->doReal(&iparams->disres.kfac);
1906             break;
1907         case F_ORIRES:
1908             serializer->doInt(&iparams->orires.ex);
1909             serializer->doInt(&iparams->orires.label);
1910             serializer->doInt(&iparams->orires.power);
1911             serializer->doReal(&iparams->orires.c);
1912             serializer->doReal(&iparams->orires.obs);
1913             serializer->doReal(&iparams->orires.kfac);
1914             break;
1915         case F_DIHRES:
1916             if (file_version < 82)
1917             {
1918                 serializer->doInt(&idum);
1919                 serializer->doInt(&idum);
1920             }
1921             serializer->doReal(&iparams->dihres.phiA);
1922             serializer->doReal(&iparams->dihres.dphiA);
1923             serializer->doReal(&iparams->dihres.kfacA);
1924             if (file_version >= 82)
1925             {
1926                 serializer->doReal(&iparams->dihres.phiB);
1927                 serializer->doReal(&iparams->dihres.dphiB);
1928                 serializer->doReal(&iparams->dihres.kfacB);
1929             }
1930             else
1931             {
1932                 iparams->dihres.phiB  = iparams->dihres.phiA;
1933                 iparams->dihres.dphiB = iparams->dihres.dphiA;
1934                 iparams->dihres.kfacB = iparams->dihres.kfacA;
1935             }
1936             break;
1937         case F_POSRES:
1938             serializer->doRvec(&iparams->posres.pos0A);
1939             serializer->doRvec(&iparams->posres.fcA);
1940             serializer->doRvec(&iparams->posres.pos0B);
1941             serializer->doRvec(&iparams->posres.fcB);
1942             break;
1943         case F_FBPOSRES:
1944             serializer->doInt(&iparams->fbposres.geom);
1945             serializer->doRvec(&iparams->fbposres.pos0);
1946             serializer->doReal(&iparams->fbposres.r);
1947             serializer->doReal(&iparams->fbposres.k);
1948             break;
1949         case F_CBTDIHS:
1950             serializer->doRealArray(iparams->cbtdihs.cbtcA, NR_CBTDIHS);
1951             break;
1952         case F_RBDIHS:
1953             serializer->doRealArray(iparams->rbdihs.rbcA, NR_RBDIHS);
1954             serializer->doRealArray(iparams->rbdihs.rbcB, NR_RBDIHS);
1955             break;
1956         case F_FOURDIHS:
1957             /* Fourier dihedrals are internally represented
1958              * as Ryckaert-Bellemans since those are faster to compute.
1959              */
1960             serializer->doRealArray(iparams->rbdihs.rbcA, NR_RBDIHS);
1961             serializer->doRealArray(iparams->rbdihs.rbcB, NR_RBDIHS);
1962             break;
1963         case F_CONSTR:
1964         case F_CONSTRNC:
1965             serializer->doReal(&iparams->constr.dA);
1966             serializer->doReal(&iparams->constr.dB);
1967             break;
1968         case F_SETTLE:
1969             serializer->doReal(&iparams->settle.doh);
1970             serializer->doReal(&iparams->settle.dhh);
1971             break;
1972         case F_VSITE2:
1973         case F_VSITE2FD:
1974             serializer->doReal(&iparams->vsite.a);
1975             break;
1976         case F_VSITE3:
1977         case F_VSITE3FD:
1978         case F_VSITE3FAD:
1979             serializer->doReal(&iparams->vsite.a);
1980             serializer->doReal(&iparams->vsite.b);
1981             break;
1982         case F_VSITE3OUT:
1983         case F_VSITE4FD:
1984         case F_VSITE4FDN:
1985             serializer->doReal(&iparams->vsite.a);
1986             serializer->doReal(&iparams->vsite.b);
1987             serializer->doReal(&iparams->vsite.c);
1988             break;
1989         case F_VSITEN:
1990             serializer->doInt(&iparams->vsiten.n);
1991             serializer->doReal(&iparams->vsiten.a);
1992             break;
1993         case F_GB12_NOLONGERUSED:
1994         case F_GB13_NOLONGERUSED:
1995         case F_GB14_NOLONGERUSED:
1996             // Implicit solvent parameters can still be read, but never used
1997             if (serializer->reading())
1998             {
1999                 if (file_version < 68)
2000                 {
2001                     serializer->doReal(&rdum);
2002                     serializer->doReal(&rdum);
2003                     serializer->doReal(&rdum);
2004                     serializer->doReal(&rdum);
2005                 }
2006                 if (file_version < tpxv_RemoveImplicitSolvation)
2007                 {
2008                     serializer->doReal(&rdum);
2009                     serializer->doReal(&rdum);
2010                     serializer->doReal(&rdum);
2011                     serializer->doReal(&rdum);
2012                     serializer->doReal(&rdum);
2013                 }
2014             }
2015             break;
2016         case F_CMAP:
2017             serializer->doInt(&iparams->cmap.cmapA);
2018             serializer->doInt(&iparams->cmap.cmapB);
2019             break;
2020         default:
2021             gmx_fatal(FARGS, "unknown function type %d (%s) in %s line %d",
2022                       ftype, interaction_function[ftype].name, __FILE__, __LINE__);
2023     }
2024 }
2025
2026 static void do_ilist(gmx::ISerializer *serializer, InteractionList *ilist)
2027 {
2028     int nr = ilist->size();
2029     serializer->doInt(&nr);
2030     if (serializer->reading())
2031     {
2032         ilist->iatoms.resize(nr);
2033     }
2034     serializer->doIntArray(ilist->iatoms.data(), ilist->size());
2035 }
2036
2037 static void do_ffparams(gmx::ISerializer         *serializer,
2038                         gmx_ffparams_t           *ffparams,
2039                         int                       file_version)
2040 {
2041     serializer->doInt(&ffparams->atnr);
2042     int numTypes = ffparams->numTypes();
2043     serializer->doInt(&numTypes);
2044     if (serializer->reading())
2045     {
2046         ffparams->functype.resize(numTypes);
2047         ffparams->iparams.resize(numTypes);
2048     }
2049     /* Read/write all the function types */
2050     serializer->doIntArray(ffparams->functype.data(), ffparams->functype.size());
2051
2052     if (file_version >= 66)
2053     {
2054         serializer->doDouble(&ffparams->reppow);
2055     }
2056     else
2057     {
2058         ffparams->reppow = 12.0;
2059     }
2060
2061     serializer->doReal(&ffparams->fudgeQQ);
2062
2063     /* Check whether all these function types are supported by the code.
2064      * In practice the code is backwards compatible, which means that the
2065      * numbering may have to be altered from old numbering to new numbering
2066      */
2067     for (int i = 0; i < ffparams->numTypes(); i++)
2068     {
2069         if (serializer->reading())
2070         {
2071             /* Loop over file versions */
2072             for (int k = 0; k < NFTUPD; k++)
2073             {
2074                 /* Compare the read file_version to the update table */
2075                 if ((file_version < ftupd[k].fvnr) &&
2076                     (ffparams->functype[i] >= ftupd[k].ftype))
2077                 {
2078                     ffparams->functype[i] += 1;
2079                 }
2080             }
2081         }
2082
2083         do_iparams(serializer, ffparams->functype[i], &ffparams->iparams[i],
2084                    file_version);
2085     }
2086 }
2087
2088 static void add_settle_atoms(InteractionList *ilist)
2089 {
2090     int i;
2091
2092     /* Settle used to only store the first atom: add the other two */
2093     ilist->iatoms.resize(2*ilist->size());
2094     for (i = ilist->size()/4 - 1; i >= 0; i--)
2095     {
2096         ilist->iatoms[4*i+0] = ilist->iatoms[2*i+0];
2097         ilist->iatoms[4*i+1] = ilist->iatoms[2*i+1];
2098         ilist->iatoms[4*i+2] = ilist->iatoms[2*i+1] + 1;
2099         ilist->iatoms[4*i+3] = ilist->iatoms[2*i+1] + 2;
2100     }
2101 }
2102
2103 static void do_ilists(gmx::ISerializer         *serializer,
2104                       InteractionLists         *ilists,
2105                       int                       file_version)
2106 {
2107     GMX_RELEASE_ASSERT(ilists, "Need a valid ilists object");
2108     GMX_RELEASE_ASSERT(ilists->size() == F_NRE, "The code needs to be in sync with InteractionLists");
2109
2110     for (int j = 0; j < F_NRE; j++)
2111     {
2112         InteractionList &ilist  = (*ilists)[j];
2113         gmx_bool         bClear = FALSE;
2114         if (serializer->reading())
2115         {
2116             for (int k = 0; k < NFTUPD; k++)
2117             {
2118                 if ((file_version < ftupd[k].fvnr) && (j == ftupd[k].ftype))
2119                 {
2120                     bClear = TRUE;
2121                 }
2122             }
2123         }
2124         if (bClear)
2125         {
2126             ilist.iatoms.clear();
2127         }
2128         else
2129         {
2130             do_ilist(serializer, &ilist);
2131             if (file_version < 78 && j == F_SETTLE && ilist.size() > 0)
2132             {
2133                 add_settle_atoms(&ilist);
2134             }
2135         }
2136     }
2137 }
2138
2139 static void do_block(gmx::ISerializer *serializer, t_block *block)
2140 {
2141     serializer->doInt(&block->nr);
2142     if (serializer->reading())
2143     {
2144         if ((block->nalloc_index > 0) && (nullptr != block->index))
2145         {
2146             sfree(block->index);
2147         }
2148         block->nalloc_index = block->nr+1;
2149         snew(block->index, block->nalloc_index);
2150     }
2151     serializer->doIntArray(block->index, block->nr+1);
2152 }
2153
2154 static void do_blocka(gmx::ISerializer *serializer, t_blocka *block)
2155 {
2156     serializer->doInt(&block->nr);
2157     serializer->doInt(&block->nra);
2158     if (serializer->reading())
2159     {
2160         block->nalloc_index = block->nr+1;
2161         snew(block->index, block->nalloc_index);
2162         block->nalloc_a = block->nra;
2163         snew(block->a, block->nalloc_a);
2164     }
2165     serializer->doIntArray(block->index, block->nr+1);
2166     serializer->doIntArray(block->a, block->nra);
2167 }
2168
2169 /* This is a primitive routine to make it possible to translate atomic numbers
2170  * to element names when reading TPR files, without making the Gromacs library
2171  * directory a dependency on mdrun (which is the case if we need elements.dat).
2172  */
2173 static const char *
2174 atomicnumber_to_element(int atomicnumber)
2175 {
2176     const char * p;
2177
2178     /* This does not have to be complete, so we only include elements likely
2179      * to occur in PDB files.
2180      */
2181     switch (atomicnumber)
2182     {
2183         case 1:  p = "H";  break;
2184         case 5:  p = "B";  break;
2185         case 6:  p = "C";  break;
2186         case 7:  p = "N";  break;
2187         case 8:  p = "O";  break;
2188         case 9:  p = "F";  break;
2189         case 11: p = "Na"; break;
2190         case 12: p = "Mg"; break;
2191         case 15: p = "P";  break;
2192         case 16: p = "S";  break;
2193         case 17: p = "Cl"; break;
2194         case 18: p = "Ar"; break;
2195         case 19: p = "K";  break;
2196         case 20: p = "Ca"; break;
2197         case 25: p = "Mn"; break;
2198         case 26: p = "Fe"; break;
2199         case 28: p = "Ni"; break;
2200         case 29: p = "Cu"; break;
2201         case 30: p = "Zn"; break;
2202         case 35: p = "Br"; break;
2203         case 47: p = "Ag"; break;
2204         default: p = "";   break;
2205     }
2206     return p;
2207 }
2208
2209
2210 static void do_atom(gmx::ISerializer *serializer, t_atom *atom)
2211 {
2212     serializer->doReal(&atom->m);
2213     serializer->doReal(&atom->q);
2214     serializer->doReal(&atom->mB);
2215     serializer->doReal(&atom->qB);
2216     serializer->doUShort(&atom->type);
2217     serializer->doUShort(&atom->typeB);
2218     serializer->doInt(&atom->ptype);
2219     serializer->doInt(&atom->resind);
2220     serializer->doInt(&atom->atomnumber);
2221     if (serializer->reading())
2222     {
2223         /* Set element string from atomic number if present.
2224          * This routine returns an empty string if the name is not found.
2225          */
2226         std::strncpy(atom->elem, atomicnumber_to_element(atom->atomnumber), 4);
2227         /* avoid warnings about potentially unterminated string */
2228         atom->elem[3] = '\0';
2229     }
2230 }
2231
2232 static void do_grps(gmx::ISerializer               *serializer,
2233                     gmx::ArrayRef<AtomGroupIndices> grps)
2234 {
2235     for (auto &group : grps)
2236     {
2237         int size = group.size();
2238         serializer->doInt(&size);
2239         if (serializer->reading())
2240         {
2241             group.resize(size);
2242         }
2243         serializer->doIntArray(group.data(), size);
2244     }
2245 }
2246
2247 static void do_symstr(gmx::ISerializer *serializer, char ***nm, t_symtab *symtab)
2248 {
2249     int ls;
2250
2251     if (serializer->reading())
2252     {
2253         serializer->doInt(&ls);
2254         *nm = get_symtab_handle(symtab, ls);
2255     }
2256     else
2257     {
2258         ls = lookup_symtab(symtab, *nm);
2259         serializer->doInt(&ls);
2260     }
2261 }
2262
2263 static void do_strstr(gmx::ISerializer         *serializer,
2264                       int                       nstr,
2265                       char                   ***nm,
2266                       t_symtab                 *symtab)
2267 {
2268     int  j;
2269
2270     for (j = 0; (j < nstr); j++)
2271     {
2272         do_symstr(serializer, &(nm[j]), symtab);
2273     }
2274 }
2275
2276 static void do_resinfo(gmx::ISerializer         *serializer,
2277                        int                       n,
2278                        t_resinfo                *ri,
2279                        t_symtab                 *symtab,
2280                        int                       file_version)
2281 {
2282     int  j;
2283
2284     for (j = 0; (j < n); j++)
2285     {
2286         do_symstr(serializer, &(ri[j].name), symtab);
2287         if (file_version >= 63)
2288         {
2289             serializer->doInt(&ri[j].nr);
2290             serializer->doUChar(&ri[j].ic);
2291         }
2292         else
2293         {
2294             ri[j].nr = j + 1;
2295             ri[j].ic = ' ';
2296         }
2297     }
2298 }
2299
2300 static void do_atoms(gmx::ISerializer         *serializer,
2301                      t_atoms                  *atoms,
2302                      t_symtab                 *symtab,
2303                      int                       file_version)
2304 {
2305     int i;
2306
2307     serializer->doInt(&atoms->nr);
2308     serializer->doInt(&atoms->nres);
2309     if (serializer->reading())
2310     {
2311         /* Since we have always written all t_atom properties in the tpr file
2312          * (at least for all backward compatible versions), we don't store
2313          * but simple set the booleans here.
2314          */
2315         atoms->haveMass    = TRUE;
2316         atoms->haveCharge  = TRUE;
2317         atoms->haveType    = TRUE;
2318         atoms->haveBState  = TRUE;
2319         atoms->havePdbInfo = FALSE;
2320
2321         snew(atoms->atom, atoms->nr);
2322         snew(atoms->atomname, atoms->nr);
2323         snew(atoms->atomtype, atoms->nr);
2324         snew(atoms->atomtypeB, atoms->nr);
2325         snew(atoms->resinfo, atoms->nres);
2326         atoms->pdbinfo = nullptr;
2327     }
2328     else
2329     {
2330         GMX_RELEASE_ASSERT(atoms->haveMass && atoms->haveCharge && atoms->haveType && atoms->haveBState, "Mass, charge, atomtype and B-state parameters should be present in t_atoms when writing a tpr file");
2331     }
2332     for (i = 0; (i < atoms->nr); i++)
2333     {
2334         do_atom(serializer, &atoms->atom[i]);
2335     }
2336     do_strstr(serializer, atoms->nr, atoms->atomname, symtab);
2337     do_strstr(serializer, atoms->nr, atoms->atomtype, symtab);
2338     do_strstr(serializer, atoms->nr, atoms->atomtypeB, symtab);
2339
2340     do_resinfo(serializer, atoms->nres, atoms->resinfo, symtab, file_version);
2341 }
2342
2343 static void do_groups(gmx::ISerializer         *serializer,
2344                       SimulationGroups         *groups,
2345                       t_symtab                 *symtab)
2346 {
2347     do_grps(serializer, groups->groups);
2348     int numberOfGroupNames = groups->groupNames.size();
2349     serializer->doInt(&numberOfGroupNames);
2350     if (serializer->reading())
2351     {
2352         groups->groupNames.resize(numberOfGroupNames);
2353     }
2354     do_strstr(serializer, numberOfGroupNames, groups->groupNames.data(), symtab);
2355     for (auto group : gmx::keysOf(groups->groupNumbers))
2356     {
2357         int numberOfGroupNumbers = groups->numberOfGroupNumbers(group);
2358         serializer->doInt(&numberOfGroupNumbers);
2359         if (numberOfGroupNumbers != 0)
2360         {
2361             if (serializer->reading())
2362             {
2363                 groups->groupNumbers[group].resize(numberOfGroupNumbers);
2364             }
2365             serializer->doUCharArray(groups->groupNumbers[group].data(), numberOfGroupNumbers);
2366         }
2367     }
2368 }
2369
2370 static void do_atomtypes(gmx::ISerializer *serializer, t_atomtypes *atomtypes,
2371                          int file_version)
2372 {
2373     int      j;
2374
2375     serializer->doInt(&atomtypes->nr);
2376     j = atomtypes->nr;
2377     if (serializer->reading())
2378     {
2379         snew(atomtypes->atomnumber, j);
2380     }
2381     if (serializer->reading() && file_version < tpxv_RemoveImplicitSolvation)
2382     {
2383         std::vector<real> dummy(atomtypes->nr, 0);
2384         serializer->doRealArray(dummy.data(), dummy.size());
2385         serializer->doRealArray(dummy.data(), dummy.size());
2386         serializer->doRealArray(dummy.data(), dummy.size());
2387     }
2388     serializer->doIntArray(atomtypes->atomnumber, j);
2389
2390     if (serializer->reading() && file_version >= 60 && file_version < tpxv_RemoveImplicitSolvation)
2391     {
2392         std::vector<real> dummy(atomtypes->nr, 0);
2393         serializer->doRealArray(dummy.data(), dummy.size());
2394         serializer->doRealArray(dummy.data(), dummy.size());
2395     }
2396 }
2397
2398 static void do_symtab(gmx::ISerializer *serializer, t_symtab *symtab)
2399 {
2400     int       i, nr;
2401     t_symbuf *symbuf;
2402
2403     serializer->doInt(&symtab->nr);
2404     nr     = symtab->nr;
2405     if (serializer->reading())
2406     {
2407         snew(symtab->symbuf, 1);
2408         symbuf          = symtab->symbuf;
2409         symbuf->bufsize = nr;
2410         snew(symbuf->buf, nr);
2411         for (i = 0; (i < nr); i++)
2412         {
2413             std::string buf;
2414             serializer->doString(&buf);
2415             symbuf->buf[i] = gmx_strdup(buf.c_str());
2416         }
2417     }
2418     else
2419     {
2420         symbuf = symtab->symbuf;
2421         while (symbuf != nullptr)
2422         {
2423             for (i = 0; (i < symbuf->bufsize) && (i < nr); i++)
2424             {
2425                 std::string buf = symbuf->buf[i];
2426                 serializer->doString(&buf);
2427             }
2428             nr    -= i;
2429             symbuf = symbuf->next;
2430         }
2431         if (nr != 0)
2432         {
2433             gmx_fatal(FARGS, "nr of symtab strings left: %d", nr);
2434         }
2435     }
2436 }
2437
2438 static void do_cmap(gmx::ISerializer *serializer, gmx_cmap_t *cmap_grid)
2439 {
2440
2441     int ngrid = cmap_grid->cmapdata.size();
2442     serializer->doInt(&ngrid);
2443     serializer->doInt(&cmap_grid->grid_spacing);
2444
2445     int gs    = cmap_grid->grid_spacing;
2446     int nelem = gs * gs;
2447
2448     if (serializer->reading())
2449     {
2450         cmap_grid->cmapdata.resize(ngrid);
2451
2452         for (int i = 0; i < ngrid; i++)
2453         {
2454             cmap_grid->cmapdata[i].cmap.resize(4*nelem);
2455         }
2456     }
2457
2458     for (int i = 0; i < ngrid; i++)
2459     {
2460         for (int j = 0; j < nelem; j++)
2461         {
2462             serializer->doReal(&cmap_grid->cmapdata[i].cmap[j*4]);
2463             serializer->doReal(&cmap_grid->cmapdata[i].cmap[j*4+1]);
2464             serializer->doReal(&cmap_grid->cmapdata[i].cmap[j*4+2]);
2465             serializer->doReal(&cmap_grid->cmapdata[i].cmap[j*4+3]);
2466         }
2467     }
2468 }
2469
2470
2471 static void do_moltype(gmx::ISerializer         *serializer,
2472                        gmx_moltype_t            *molt,
2473                        t_symtab                 *symtab,
2474                        int                       file_version)
2475 {
2476     do_symstr(serializer, &(molt->name), symtab);
2477
2478     do_atoms(serializer, &molt->atoms, symtab, file_version);
2479
2480     do_ilists(serializer, &molt->ilist, file_version);
2481
2482     do_block(serializer, &molt->cgs);
2483
2484     /* This used to be in the atoms struct */
2485     do_blocka(serializer, &molt->excls);
2486 }
2487
2488 static void do_molblock(gmx::ISerializer         *serializer,
2489                         gmx_molblock_t           *molb,
2490                         int                       numAtomsPerMolecule)
2491 {
2492     serializer->doInt(&molb->type);
2493     serializer->doInt(&molb->nmol);
2494     /* To maintain forward topology reading compatibility, we store #atoms.
2495      * TODO: Change this to conditional reading of a dummy int when we
2496      *       increase tpx_generation.
2497      */
2498     serializer->doInt(&numAtomsPerMolecule);
2499     /* Position restraint coordinates */
2500     int numPosres_xA = molb->posres_xA.size();
2501     serializer->doInt(&numPosres_xA);
2502     if (numPosres_xA > 0)
2503     {
2504         if (serializer->reading())
2505         {
2506             molb->posres_xA.resize(numPosres_xA);
2507         }
2508         serializer->doRvecArray(as_rvec_array(molb->posres_xA.data()), numPosres_xA);
2509     }
2510     int numPosres_xB = molb->posres_xB.size();
2511     serializer->doInt(&numPosres_xB);
2512     if (numPosres_xB > 0)
2513     {
2514         if (serializer->reading())
2515         {
2516             molb->posres_xB.resize(numPosres_xB);
2517         }
2518         serializer->doRvecArray(as_rvec_array(molb->posres_xB.data()), numPosres_xB);
2519     }
2520
2521 }
2522
2523 static void set_disres_npair(gmx_mtop_t *mtop)
2524 {
2525     gmx_mtop_ilistloop_t     iloop;
2526     int                      nmol;
2527
2528     gmx::ArrayRef<t_iparams> ip = mtop->ffparams.iparams;
2529
2530     iloop     = gmx_mtop_ilistloop_init(mtop);
2531     while (const InteractionLists *ilist = gmx_mtop_ilistloop_next(iloop, &nmol))
2532     {
2533         const InteractionList &il = (*ilist)[F_DISRES];
2534
2535         if (il.size() > 0)
2536         {
2537             gmx::ArrayRef<const int> a     = il.iatoms;
2538             int                      npair = 0;
2539             for (int i = 0; i < il.size(); i += 3)
2540             {
2541                 npair++;
2542                 if (i+3 == il.size() || ip[a[i]].disres.label != ip[a[i+3]].disres.label)
2543                 {
2544                     ip[a[i]].disres.npair = npair;
2545                     npair                 = 0;
2546                 }
2547             }
2548         }
2549     }
2550 }
2551
2552 static void do_mtop(gmx::ISerializer         *serializer,
2553                     gmx_mtop_t               *mtop,
2554                     int                       file_version)
2555 {
2556     do_symtab(serializer, &(mtop->symtab));
2557
2558     do_symstr(serializer, &(mtop->name), &(mtop->symtab));
2559
2560     do_ffparams(serializer, &mtop->ffparams, file_version);
2561
2562     int nmoltype = mtop->moltype.size();
2563     serializer->doInt(&nmoltype);
2564     if (serializer->reading())
2565     {
2566         mtop->moltype.resize(nmoltype);
2567     }
2568     for (gmx_moltype_t &moltype : mtop->moltype)
2569     {
2570         do_moltype(serializer, &moltype, &mtop->symtab, file_version);
2571     }
2572
2573     int nmolblock = mtop->molblock.size();
2574     serializer->doInt(&nmolblock);
2575     if (serializer->reading())
2576     {
2577         mtop->molblock.resize(nmolblock);
2578     }
2579     for (gmx_molblock_t &molblock : mtop->molblock)
2580     {
2581         int numAtomsPerMolecule = (serializer->reading() ? 0 : mtop->moltype[molblock.type].atoms.nr);
2582         do_molblock(serializer, &molblock, numAtomsPerMolecule);
2583     }
2584     serializer->doInt(&mtop->natoms);
2585
2586     if (file_version >= tpxv_IntermolecularBondeds)
2587     {
2588         serializer->doBool(&mtop->bIntermolecularInteractions);
2589         if (mtop->bIntermolecularInteractions)
2590         {
2591             if (serializer->reading())
2592             {
2593                 mtop->intermolecular_ilist = std::make_unique<InteractionLists>();
2594             }
2595             do_ilists(serializer, mtop->intermolecular_ilist.get(), file_version);
2596         }
2597     }
2598     else
2599     {
2600         mtop->bIntermolecularInteractions = FALSE;
2601     }
2602
2603     do_atomtypes (serializer, &(mtop->atomtypes), file_version);
2604
2605     if (file_version >= 65)
2606     {
2607         do_cmap(serializer, &mtop->ffparams.cmap_grid);
2608     }
2609     else
2610     {
2611         mtop->ffparams.cmap_grid.grid_spacing = 0;
2612         mtop->ffparams.cmap_grid.cmapdata.clear();
2613     }
2614
2615     do_groups(serializer, &mtop->groups, &(mtop->symtab));
2616
2617     mtop->haveMoleculeIndices = true;
2618
2619     if (serializer->reading())
2620     {
2621         close_symtab(&(mtop->symtab));
2622     }
2623 }
2624
2625 /*! \brief
2626  * Read the first part of the TPR file to find general system information.
2627  *
2628  * If \p TopOnlyOK is true then we can read even future versions
2629  * of tpx files, provided the \p fileGeneration hasn't changed.
2630  * If it is false, we need the \p ir too, and bail out
2631  * if the file is newer than the program.
2632  *
2633  * The version and generation of the topology (see top of this file)
2634  * are returned in the two last arguments, if those arguments are non-nullptr.
2635  *
2636  * If possible, we will read the \p ir even when \p TopOnlyOK is true.
2637  *
2638  * \param[in,out] serializer The serializer used to handle header processing.
2639  * \param[in,out] tpx File header datastructure.
2640  * \param[in]     filename The name of the file being read/written
2641  * \param[in,out] fio File handle.
2642  * \param[in] TopOnlyOK If not reading \p ir is fine or not.
2643  */
2644 static void do_tpxheader(gmx::FileIOXdrSerializer *serializer,
2645                          TpxFileHeader            *tpx,
2646                          const char               *filename,
2647                          t_fileio                 *fio,
2648                          bool                      TopOnlyOK)
2649 {
2650     gmx_bool  bDouble;
2651     int       precision;
2652     int       idum = 0;
2653     real      rdum = 0;
2654
2655     /* XDR binary topology file */
2656     precision = sizeof(real);
2657     std::string buf;
2658     std::string fileTag;
2659     if (serializer->reading())
2660     {
2661         serializer->doString(&buf);
2662         if (std::strncmp(buf.c_str(), "VERSION", 7) != 0)
2663         {
2664             gmx_fatal(FARGS, "Can not read file %s,\n"
2665                       "             this file is from a GROMACS version which is older than 2.0\n"
2666                       "             Make a new one with grompp or use a gro or pdb file, if possible",
2667                       filename);
2668         }
2669         serializer->doInt(&precision);
2670         bDouble = (precision == sizeof(double));
2671         if ((precision != sizeof(float)) && !bDouble)
2672         {
2673             gmx_fatal(FARGS, "Unknown precision in file %s: real is %d bytes "
2674                       "instead of %zu or %zu",
2675                       filename, precision, sizeof(float), sizeof(double));
2676         }
2677         gmx_fio_setprecision(fio, bDouble);
2678         fprintf(stderr, "Reading file %s, %s (%s precision)\n",
2679                 filename, buf.c_str(), bDouble ? "double" : "single");
2680     }
2681     else
2682     {
2683         buf = gmx::formatString("VERSION %s", gmx_version());
2684         serializer->doString(&buf);
2685         bDouble = (precision == sizeof(double));
2686         gmx_fio_setprecision(fio, bDouble);
2687         serializer->doInt(&precision);
2688         fileTag        = gmx::formatString("%s", tpx_tag);
2689     }
2690
2691     /* Check versions! */
2692     serializer->doInt(&tpx->fileVersion);
2693
2694     /* This is for backward compatibility with development versions 77-79
2695      * where the tag was, mistakenly, placed before the generation,
2696      * which would cause a segv instead of a proper error message
2697      * when reading the topology only from tpx with <77 code.
2698      */
2699     if (tpx->fileVersion >= 77 && tpx->fileVersion <= 79)
2700     {
2701         serializer->doString(&fileTag);
2702     }
2703
2704     serializer->doInt(&tpx->fileGeneration);
2705
2706     if (tpx->fileVersion >= 81)
2707     {
2708         serializer->doString(&fileTag);
2709     }
2710     if (serializer->reading())
2711     {
2712         if (tpx->fileVersion < 77)
2713         {
2714             /* Versions before 77 don't have the tag, set it to release */
2715             fileTag = gmx::formatString("%s", TPX_TAG_RELEASE);
2716         }
2717
2718         if (fileTag != tpx_tag)
2719         {
2720             fprintf(stderr, "Note: file tpx tag '%s', software tpx tag '%s'\n",
2721                     fileTag.c_str(), tpx_tag);
2722
2723             /* We only support reading tpx files with the same tag as the code
2724              * or tpx files with the release tag and with lower version number.
2725              */
2726             if (fileTag != TPX_TAG_RELEASE && tpx->fileVersion < tpx_version)
2727             {
2728                 gmx_fatal(FARGS, "tpx tag/version mismatch: reading tpx file (%s) version %d, tag '%s' with program for tpx version %d, tag '%s'",
2729                           filename, tpx->fileVersion, fileTag.c_str(),
2730                           tpx_version, tpx_tag);
2731             }
2732         }
2733     }
2734
2735     if ((tpx->fileVersion <= tpx_incompatible_version) ||
2736         ((tpx->fileVersion > tpx_version) && !TopOnlyOK) ||
2737         (tpx->fileGeneration > tpx_generation) ||
2738         tpx_version == 80) /*80 was used by both 5.0-dev and 4.6-dev*/
2739     {
2740         gmx_fatal(FARGS, "reading tpx file (%s) version %d with version %d program",
2741                   filename, tpx->fileVersion, tpx_version);
2742     }
2743
2744     serializer->doInt(&tpx->natoms);
2745     serializer->doInt(&tpx->ngtc);
2746
2747     if (tpx->fileVersion < 62)
2748     {
2749         serializer->doInt(&idum);
2750         serializer->doReal(&rdum);
2751     }
2752     if (tpx->fileVersion >= 79)
2753     {
2754         serializer->doInt(&tpx->fep_state);
2755     }
2756     serializer->doReal(&tpx->lambda);
2757     serializer->doBool(&tpx->bIr);
2758     serializer->doBool(&tpx->bTop);
2759     serializer->doBool(&tpx->bX);
2760     serializer->doBool(&tpx->bV);
2761     serializer->doBool(&tpx->bF);
2762     serializer->doBool(&tpx->bBox);
2763
2764     if ((tpx->fileGeneration > tpx_generation))
2765     {
2766         /* This can only happen if TopOnlyOK=TRUE */
2767         tpx->bIr = FALSE;
2768     }
2769 }
2770
2771 static int do_tpx_body(gmx::ISerializer *serializer,
2772                        TpxFileHeader    *tpx,
2773                        t_inputrec       *ir,
2774                        t_state          *state,
2775                        rvec             *x,
2776                        rvec             *v,
2777                        gmx_mtop_t       *mtop)
2778 {
2779     int             ePBC;
2780     gmx_bool        bPeriodicMols;
2781
2782     if (!serializer->reading())
2783     {
2784         GMX_RELEASE_ASSERT(x == nullptr && v == nullptr, "Passing separate x and v pointers to do_tpx() is not supported when writing");
2785     }
2786     else
2787     {
2788         GMX_RELEASE_ASSERT(!(x == nullptr && v != nullptr), "Passing x==NULL and v!=NULL is not supported");
2789     }
2790
2791     if (serializer->reading())
2792     {
2793         state->flags = 0;
2794         init_gtc_state(state, tpx->ngtc, 0, 0);
2795         if (x == nullptr)
2796         {
2797             // v is also nullptr by the above assertion, so we may
2798             // need to make memory in state for storing the contents
2799             // of the tpx file.
2800             if (tpx->bX)
2801             {
2802                 state->flags |= (1 << estX);
2803             }
2804             if (tpx->bV)
2805             {
2806                 state->flags |= (1 << estV);
2807             }
2808             state_change_natoms(state, tpx->natoms);
2809         }
2810     }
2811
2812     if (x == nullptr)
2813     {
2814         x = state->x.rvec_array();
2815         v = state->v.rvec_array();
2816     }
2817
2818 #define do_test(serializer, b, p) if ((serializer)->reading() && ((p) != nullptr) && !(b)) gmx_fatal(FARGS, "No %s in input file",#p)
2819
2820     do_test(serializer, tpx->bBox, state->box);
2821     if (tpx->bBox)
2822     {
2823         serializer->doRvecArray(state->box, DIM);
2824         if (tpx->fileVersion >= 51)
2825         {
2826             serializer->doRvecArray(state->box_rel, DIM);
2827         }
2828         else
2829         {
2830             /* We initialize box_rel after reading the inputrec */
2831             clear_mat(state->box_rel);
2832         }
2833         serializer->doRvecArray(state->boxv, DIM);
2834         if (tpx->fileVersion < 56)
2835         {
2836             matrix mdum;
2837             serializer->doRvecArray(mdum, DIM);
2838         }
2839     }
2840
2841     if (state->ngtc > 0)
2842     {
2843         real *dumv;
2844         snew(dumv, state->ngtc);
2845         if (tpx->fileVersion < 69)
2846         {
2847             serializer->doRealArray(dumv, state->ngtc);
2848         }
2849         /* These used to be the Berendsen tcoupl_lambda's */
2850         serializer->doRealArray(dumv, state->ngtc);
2851         sfree(dumv);
2852     }
2853
2854     do_test(serializer, tpx->bTop, mtop);
2855     if (tpx->bTop)
2856     {
2857         if (mtop)
2858         {
2859             do_mtop(serializer, mtop, tpx->fileVersion);
2860         }
2861         else
2862         {
2863             gmx_mtop_t dum_top;
2864             do_mtop(serializer, &dum_top, tpx->fileVersion);
2865         }
2866     }
2867     do_test(serializer, tpx->bX, x);
2868     if (tpx->bX)
2869     {
2870         if (serializer->reading())
2871         {
2872             state->flags |= (1<<estX);
2873         }
2874         serializer->doRvecArray(x, tpx->natoms);
2875     }
2876
2877     do_test(serializer, tpx->bV, v);
2878     if (tpx->bV)
2879     {
2880         if (serializer->reading())
2881         {
2882             state->flags |= (1<<estV);
2883         }
2884         if (!v)
2885         {
2886             std::vector<gmx::RVec> dummyVelocities(tpx->natoms);
2887             serializer->doRvecArray(as_rvec_array(dummyVelocities.data()), tpx->natoms);
2888         }
2889         else
2890         {
2891             serializer->doRvecArray(v, tpx->natoms);
2892         }
2893     }
2894
2895     // No need to run do_test when the last argument is NULL
2896     if (tpx->bF)
2897     {
2898         std::vector<gmx::RVec> dummyForces(state->natoms);
2899         serializer->doRvecArray(as_rvec_array(dummyForces.data()), tpx->natoms);
2900     }
2901
2902     /* Starting with tpx version 26, we have the inputrec
2903      * at the end of the file, so we can ignore it
2904      * if the file is never than the software (but still the
2905      * same generation - see comments at the top of this file.
2906      *
2907      *
2908      */
2909     ePBC          = -1;
2910     bPeriodicMols = FALSE;
2911
2912     do_test(serializer, tpx->bIr, ir);
2913     if (tpx->bIr)
2914     {
2915         if (tpx->fileVersion >= 53)
2916         {
2917             /* Removed the pbc info from do_inputrec, since we always want it */
2918             if (!serializer->reading())
2919             {
2920                 ePBC          = ir->ePBC;
2921                 bPeriodicMols = ir->bPeriodicMols;
2922             }
2923             serializer->doInt(&ePBC);
2924             serializer->doBool(&bPeriodicMols);
2925         }
2926         if (tpx->fileGeneration <= tpx_generation && ir)
2927         {
2928             do_inputrec(serializer, ir, tpx->fileVersion);
2929             if (tpx->fileVersion < 51)
2930             {
2931                 set_box_rel(ir, state);
2932             }
2933             if (tpx->fileVersion < 53)
2934             {
2935                 ePBC          = ir->ePBC;
2936                 bPeriodicMols = ir->bPeriodicMols;
2937             }
2938         }
2939         if (serializer->reading() && ir && tpx->fileVersion >= 53)
2940         {
2941             /* We need to do this after do_inputrec, since that initializes ir */
2942             ir->ePBC          = ePBC;
2943             ir->bPeriodicMols = bPeriodicMols;
2944         }
2945     }
2946
2947     if (serializer->reading())
2948     {
2949         if (tpx->bIr && ir)
2950         {
2951             if (state->ngtc == 0)
2952             {
2953                 /* Reading old version without tcoupl state data: set it */
2954                 init_gtc_state(state, ir->opts.ngtc, 0, ir->opts.nhchainlength);
2955             }
2956             if (tpx->bTop && mtop)
2957             {
2958                 if (tpx->fileVersion < 57)
2959                 {
2960                     if (mtop->moltype[0].ilist[F_DISRES].size() > 0)
2961                     {
2962                         ir->eDisre = edrSimple;
2963                     }
2964                     else
2965                     {
2966                         ir->eDisre = edrNone;
2967                     }
2968                 }
2969                 set_disres_npair(mtop);
2970             }
2971         }
2972
2973         if (tpx->bTop && mtop)
2974         {
2975             gmx_mtop_finalize(mtop);
2976         }
2977     }
2978
2979     return ePBC;
2980 }
2981
2982 static t_fileio *open_tpx(const char *fn, const char *mode)
2983 {
2984     return gmx_fio_open(fn, mode);
2985 }
2986
2987 static void close_tpx(t_fileio *fio)
2988 {
2989     gmx_fio_close(fio);
2990 }
2991
2992 /*! \brief
2993  * Fill information into the header only from state before writing.
2994  *
2995  * Populating the header needs to be independent from writing the information
2996  * to file to allow things like writing the raw byte stream.
2997  *
2998  * \param[in] state The current simulation state. Can't write without it.
2999  * \param[in] ir Parameter and system information.
3000  * \param[in] mtop Global topology.
3001  * \returns Fully populated header.
3002  */
3003 static TpxFileHeader populateTpxHeader(const t_state    &state,
3004                                        const t_inputrec *ir,
3005                                        const gmx_mtop_t *mtop)
3006 {
3007     TpxFileHeader header;
3008     header.natoms         = state.natoms;
3009     header.ngtc           = state.ngtc;
3010     header.fep_state      = state.fep_state;
3011     header.lambda         = state.lambda[efptFEP];
3012     header.bIr            = ir       != nullptr;
3013     header.bTop           = mtop     != nullptr;
3014     header.bX             = (state.flags & (1 << estX)) != 0;
3015     header.bV             = (state.flags & (1 << estV)) != 0;
3016     header.bF             = false;
3017     header.bBox           = true;
3018     header.fileVersion    = tpx_version;
3019     header.fileGeneration = tpx_generation;
3020
3021     return header;
3022 }
3023
3024 /************************************************************
3025  *
3026  *  The following routines are the exported ones
3027  *
3028  ************************************************************/
3029
3030 TpxFileHeader readTpxHeader(const char *fileName, bool canReadTopologyOnly)
3031 {
3032     t_fileio *fio;
3033
3034     fio = open_tpx(fileName, "r");
3035     gmx::FileIOXdrSerializer serializer(fio);
3036
3037     TpxFileHeader            tpx;
3038     do_tpxheader(&serializer, &tpx, fileName, fio, canReadTopologyOnly);
3039     close_tpx(fio);
3040     return tpx;
3041 }
3042
3043 void write_tpx_state(const char *fn,
3044                      const t_inputrec *ir, const t_state *state, const gmx_mtop_t *mtop)
3045 {
3046     t_fileio *fio;
3047
3048     fio = open_tpx(fn, "w");
3049
3050     TpxFileHeader tpx = populateTpxHeader(*state,
3051                                           ir,
3052                                           mtop);
3053
3054     gmx::FileIOXdrSerializer serializer(fio);
3055     do_tpxheader(&serializer,
3056                  &tpx,
3057                  fn,
3058                  fio,
3059                  ir == nullptr);
3060     do_tpx_body(&serializer,
3061                 &tpx,
3062                 const_cast<t_inputrec *>(ir),
3063                 const_cast<t_state *>(state), nullptr, nullptr,
3064                 const_cast<gmx_mtop_t *>(mtop));
3065     close_tpx(fio);
3066 }
3067
3068 void read_tpx_state(const char *fn,
3069                     t_inputrec *ir, t_state *state, gmx_mtop_t *mtop)
3070 {
3071     t_fileio                *fio;
3072
3073     TpxFileHeader            tpx;
3074     fio = open_tpx(fn, "r");
3075     gmx::FileIOXdrSerializer serializer(fio);
3076     do_tpxheader(&serializer,
3077                  &tpx,
3078                  fn,
3079                  fio,
3080                  ir == nullptr);
3081     do_tpx_body(&serializer,
3082                 &tpx,
3083                 ir,
3084                 state,
3085                 nullptr,
3086                 nullptr,
3087                 mtop);
3088     close_tpx(fio);
3089 }
3090
3091 int read_tpx(const char *fn,
3092              t_inputrec *ir, matrix box, int *natoms,
3093              rvec *x, rvec *v, gmx_mtop_t *mtop)
3094 {
3095     t_fileio                *fio;
3096     t_state                  state;
3097     int                      ePBC;
3098
3099     TpxFileHeader            tpx;
3100     fio     = open_tpx(fn, "r");
3101     gmx::FileIOXdrSerializer serializer(fio);
3102     do_tpxheader(&serializer,
3103                  &tpx,
3104                  fn,
3105                  fio,
3106                  ir == nullptr);
3107     ePBC    = do_tpx_body(&serializer,
3108                           &tpx,
3109                           ir, &state, x, v, mtop);
3110     close_tpx(fio);
3111     if (mtop != nullptr && natoms != nullptr)
3112     {
3113         *natoms = mtop->natoms;
3114     }
3115     if (box)
3116     {
3117         copy_mat(state.box, box);
3118     }
3119
3120     return ePBC;
3121 }
3122
3123 int read_tpx_top(const char *fn,
3124                  t_inputrec *ir, matrix box, int *natoms,
3125                  rvec *x, rvec *v, t_topology *top)
3126 {
3127     gmx_mtop_t  mtop;
3128     int         ePBC;
3129
3130     ePBC = read_tpx(fn, ir, box, natoms, x, v, &mtop);
3131
3132     *top = gmx_mtop_t_to_t_topology(&mtop, true);
3133
3134     return ePBC;
3135 }
3136
3137 gmx_bool fn2bTPX(const char *file)
3138 {
3139     return (efTPR == fn2ftp(file));
3140 }
3141
3142 void pr_tpxheader(FILE *fp, int indent, const char *title, const TpxFileHeader *sh)
3143 {
3144     if (available(fp, sh, indent, title))
3145     {
3146         indent = pr_title(fp, indent, title);
3147         pr_indent(fp, indent);
3148         fprintf(fp, "bIr    = %spresent\n", sh->bIr ? "" : "not ");
3149         pr_indent(fp, indent);
3150         fprintf(fp, "bBox   = %spresent\n", sh->bBox ? "" : "not ");
3151         pr_indent(fp, indent);
3152         fprintf(fp, "bTop   = %spresent\n", sh->bTop ? "" : "not ");
3153         pr_indent(fp, indent);
3154         fprintf(fp, "bX     = %spresent\n", sh->bX ? "" : "not ");
3155         pr_indent(fp, indent);
3156         fprintf(fp, "bV     = %spresent\n", sh->bV ? "" : "not ");
3157         pr_indent(fp, indent);
3158         fprintf(fp, "bF     = %spresent\n", sh->bF ? "" : "not ");
3159
3160         pr_indent(fp, indent);
3161         fprintf(fp, "natoms = %d\n", sh->natoms);
3162         pr_indent(fp, indent);
3163         fprintf(fp, "lambda = %e\n", sh->lambda);
3164     }
3165 }