/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 *
 * 
 *                This source code is part of
 * 
 *                 G   R   O   M   A   C   S
 * 
 *          GROningen MAchine for Chemical Simulations
 * 
 *                        VERSION 3.2.0
 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 * Copyright (c) 2001-2004, The GROMACS development team,
 * check out http://www.gromacs.org for more information.

 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * If you want to redistribute modifications, please consider that
 * scientific software is very special. Version control is crucial -
 * bugs must be traceable. We will be happy to consider code for
 * inclusion in the official distribution, but derived work must not
 * be called official GROMACS. Details are found in the README & COPYING
 * files - if they are missing, get the official version at www.gromacs.org.
 * 
 * To help us fund GROMACS development, we humbly ask that you cite
 * the papers on the package - you can find them in the top README file.
 * 
 * For more info, check our website at http://www.gromacs.org
 * 
 * And Hey:
 * GROningen Mixture of Alchemy and Childrens' Stories
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/* This file is completely threadsafe - keep it that way! */
#ifdef GMX_THREADS
#include <thread_mpi.h>
#endif


#include <ctype.h>
#include "sysstuff.h"
#include "smalloc.h"
#include "string2.h"
#include "gmx_fatal.h"
#include "macros.h"
#include "names.h"
#include "symtab.h"
#include "futil.h"
#include "filenm.h"
#include "gmxfio.h"
#include "topsort.h"
#include "tpxio.h"
#include "txtdump.h"
#include "confio.h"
#include "atomprop.h"
#include "copyrite.h"
#include "vec.h"
#include "mtop_util.h"

/* This number should be increased whenever the file format changes! */
static const int tpx_version = 73;

/* This number should only be increased when you edit the TOPOLOGY section
 * of the tpx format. This way we can maintain forward compatibility too
 * for all analysis tools and/or external programs that only need to
 * know the atom/residue names, charges, and bond connectivity.
 *  
 * It first appeared in tpx version 26, when I also moved the inputrecord
 * to the end of the tpx file, so we can just skip it if we only
 * want the topology.
 */
static const int tpx_generation = 23;

/* This number should be the most recent backwards incompatible version 
 * I.e., if this number is 9, we cannot read tpx version 9 with this code.
 */
static const int tpx_incompatible_version = 9;



/* Struct used to maintain tpx compatibility when function types are added */
typedef struct {
  int fvnr; /* file version number in which the function type first appeared */
  int ftype; /* function type */
} t_ftupd;

/* 
 *The entries should be ordered in:
 * 1. ascending file version number
 * 2. ascending function type number
 */
/*static const t_ftupd ftupd[] = {
  { 20, F_CUBICBONDS        },
  { 20, F_CONNBONDS         },
  { 20, F_HARMONIC          },
  { 20, F_EQM,              },
  { 22, F_DISRESVIOL        },
  { 22, F_ORIRES            },
  { 22, F_ORIRESDEV         },
  { 26, F_FOURDIHS          },
  { 26, F_PIDIHS            },
  { 26, F_DIHRES            },
  { 26, F_DIHRESVIOL        },
  { 30, F_CROSS_BOND_BONDS  },
  { 30, F_CROSS_BOND_ANGLES },
  { 30, F_UREY_BRADLEY      },
  { 30, F_POLARIZATION      }
  };*/
  
/* 
 *The entries should be ordered in:
 * 1. ascending function type number
 * 2. ascending file version number
 */
static const t_ftupd ftupd[] = {
  { 20, F_CUBICBONDS        },
  { 20, F_CONNBONDS         },
  { 20, F_HARMONIC          },
  { 34, F_FENEBONDS         },
  { 43, F_TABBONDS          },
  { 43, F_TABBONDSNC        },
  { 70, F_RESTRBONDS        },
  { 30, F_CROSS_BOND_BONDS  },
  { 30, F_CROSS_BOND_ANGLES },
  { 30, F_UREY_BRADLEY      },
  { 34, F_QUARTIC_ANGLES    },
  { 43, F_TABANGLES         },
  { 26, F_FOURDIHS          },
  { 26, F_PIDIHS            },
  { 43, F_TABDIHS           },
  { 65, F_CMAP              },
  { 60, F_GB12              },
  { 61, F_GB13              },
  { 61, F_GB14              },	
  { 72, F_GBPOL             },
  { 72, F_NPSOLVATION       },
  { 41, F_LJC14_Q           },
  { 41, F_LJC_PAIRS_NB      },
  { 32, F_BHAM_LR           },
  { 32, F_RF_EXCL           },
  { 32, F_COUL_RECIP        },
  { 46, F_DPD               },
  { 30, F_POLARIZATION      },
  { 36, F_THOLE_POL         },
  { 22, F_DISRESVIOL        },
  { 22, F_ORIRES            },
  { 22, F_ORIRESDEV         },
  { 26, F_DIHRES            },
  { 26, F_DIHRESVIOL        },
  { 49, F_VSITE4FDN         },
  { 50, F_VSITEN            },
  { 46, F_COM_PULL          },
  { 20, F_EQM               },
  { 46, F_ECONSERVED        },
  { 69, F_VTEMP             },
  { 66, F_PDISPCORR         },
  { 54, F_DHDL_CON          },
};
#define NFTUPD asize(ftupd)

/* Needed for backward compatibility */
#define MAXNODES 256

static void _do_section(t_fileio *fio,int key,gmx_bool bRead,const char *src,
                        int line)
{
  char buf[STRLEN];
  gmx_bool bDbg;

  if (gmx_fio_getftp(fio) == efTPA) {
    if (!bRead) {
      gmx_fio_write_string(fio,itemstr[key]);
      bDbg       = gmx_fio_getdebug(fio);
      gmx_fio_setdebug(fio,FALSE);
      gmx_fio_write_string(fio,comment_str[key]);
      gmx_fio_setdebug(fio,bDbg);
    }
    else {
      if (gmx_fio_getdebug(fio))
	fprintf(stderr,"Looking for section %s (%s, %d)",
		itemstr[key],src,line);
      
      do {
	gmx_fio_do_string(fio,buf);
      } while ((gmx_strcasecmp(buf,itemstr[key]) != 0));
      
      if (gmx_strcasecmp(buf,itemstr[key]) != 0) 
	gmx_fatal(FARGS,"\nCould not find section heading %s",itemstr[key]);
      else if (gmx_fio_getdebug(fio))
	fprintf(stderr," and found it\n");
    }
  }
}

#define do_section(fio,key,bRead) _do_section(fio,key,bRead,__FILE__,__LINE__)

/**************************************************************
 *
 * Now the higer level routines that do io of the structures and arrays
 *
 **************************************************************/
static void do_pullgrp(t_fileio *fio, t_pullgrp *pgrp, gmx_bool bRead, 
                       int file_version)
{
  gmx_bool bDum=TRUE;
  int  i;

  gmx_fio_do_int(fio,pgrp->nat);
  if (bRead)
    snew(pgrp->ind,pgrp->nat);
  bDum=gmx_fio_ndo_int(fio,pgrp->ind,pgrp->nat);
  gmx_fio_do_int(fio,pgrp->nweight);
  if (bRead)
    snew(pgrp->weight,pgrp->nweight);
  bDum=gmx_fio_ndo_real(fio,pgrp->weight,pgrp->nweight);
  gmx_fio_do_int(fio,pgrp->pbcatom);
  gmx_fio_do_rvec(fio,pgrp->vec);
  gmx_fio_do_rvec(fio,pgrp->init);
  gmx_fio_do_real(fio,pgrp->rate);
  gmx_fio_do_real(fio,pgrp->k);
  if (file_version >= 56) {
    gmx_fio_do_real(fio,pgrp->kB);
  } else {
    pgrp->kB = pgrp->k;
  }
}

static void do_pull(t_fileio *fio, t_pull *pull,gmx_bool bRead, int file_version)
{
  int g;

  gmx_fio_do_int(fio,pull->ngrp);
  gmx_fio_do_int(fio,pull->eGeom);
  gmx_fio_do_ivec(fio,pull->dim);
  gmx_fio_do_real(fio,pull->cyl_r1);
  gmx_fio_do_real(fio,pull->cyl_r0);
  gmx_fio_do_real(fio,pull->constr_tol);
  gmx_fio_do_int(fio,pull->nstxout);
  gmx_fio_do_int(fio,pull->nstfout);
  if (bRead)
    snew(pull->grp,pull->ngrp+1);
  for(g=0; g<pull->ngrp+1; g++)
    do_pullgrp(fio,&pull->grp[g],bRead,file_version);
}

static void do_inputrec(t_fileio *fio, t_inputrec *ir,gmx_bool bRead, 
                        int file_version, real *fudgeQQ)
{
    int  i,j,k,*tmp,idum=0; 
    gmx_bool bDum=TRUE;
    real rdum,bd_temp;
    rvec vdum;
    gmx_bool bSimAnn;
    real zerotemptime,finish_t,init_temp,finish_temp;
    
    if (file_version != tpx_version)
    {
        /* Give a warning about features that are not accessible */
        fprintf(stderr,"Note: tpx file_version %d, software version %d\n",
                file_version,tpx_version);
    }

    if (bRead)
    {
        init_inputrec(ir);
    }

    if (file_version == 0)
    {
        return;
    }

    /* Basic inputrec stuff */  
    gmx_fio_do_int(fio,ir->eI); 
    if (file_version >= 62) {
      gmx_fio_do_gmx_large_int(fio, ir->nsteps);
    } else {
      gmx_fio_do_int(fio,idum);
      ir->nsteps = idum;
    }
    if(file_version > 25) {
      if (file_version >= 62) {
	gmx_fio_do_gmx_large_int(fio, ir->init_step);
      } else {
	gmx_fio_do_int(fio,idum);
	ir->init_step = idum;
      }
    }  else {
      ir->init_step=0;
    }

	if(file_version >= 58)
	  gmx_fio_do_int(fio,ir->simulation_part);
	else
	  ir->simulation_part=1;
	  
    if (file_version >= 67) {
      gmx_fio_do_int(fio,ir->nstcalcenergy);
    } else {
      ir->nstcalcenergy = 1;
    }
    if (file_version < 53) {
      /* The pbc info has been moved out of do_inputrec,
       * since we always want it, also without reading the inputrec.
       */
      gmx_fio_do_int(fio,ir->ePBC);
      if ((file_version <= 15) && (ir->ePBC == 2))
	ir->ePBC = epbcNONE;
      if (file_version >= 45) {
	gmx_fio_do_int(fio,ir->bPeriodicMols);
      } else {
	if (ir->ePBC == 2) {
	  ir->ePBC = epbcXYZ;
	  ir->bPeriodicMols = TRUE;
	} else {
	ir->bPeriodicMols = FALSE;
	}
      }
    }
    gmx_fio_do_int(fio,ir->ns_type);
    gmx_fio_do_int(fio,ir->nstlist);
    gmx_fio_do_int(fio,ir->ndelta);
    if (file_version < 41) {
      gmx_fio_do_int(fio,idum);
      gmx_fio_do_int(fio,idum);
    }
    if (file_version >= 45)
      gmx_fio_do_real(fio,ir->rtpi);
    else
      ir->rtpi = 0.05;
    gmx_fio_do_int(fio,ir->nstcomm); 
    if (file_version > 34)
      gmx_fio_do_int(fio,ir->comm_mode);
    else if (ir->nstcomm < 0) 
      ir->comm_mode = ecmANGULAR;
    else
      ir->comm_mode = ecmLINEAR;
    ir->nstcomm = abs(ir->nstcomm);
    
    if(file_version > 25)
      gmx_fio_do_int(fio,ir->nstcheckpoint);
    else
      ir->nstcheckpoint=0;
    
    gmx_fio_do_int(fio,ir->nstcgsteep); 

    if(file_version>=30)
      gmx_fio_do_int(fio,ir->nbfgscorr); 
    else if (bRead)
      ir->nbfgscorr = 10;

    gmx_fio_do_int(fio,ir->nstlog); 
    gmx_fio_do_int(fio,ir->nstxout); 
    gmx_fio_do_int(fio,ir->nstvout); 
    gmx_fio_do_int(fio,ir->nstfout); 
    gmx_fio_do_int(fio,ir->nstenergy); 
    gmx_fio_do_int(fio,ir->nstxtcout); 
    if (file_version >= 59) {
      gmx_fio_do_double(fio,ir->init_t);
      gmx_fio_do_double(fio,ir->delta_t);
    } else {
      gmx_fio_do_real(fio,rdum);
      ir->init_t = rdum;
      gmx_fio_do_real(fio,rdum);
      ir->delta_t = rdum;
    }
    gmx_fio_do_real(fio,ir->xtcprec); 
    if (file_version < 19) {
      gmx_fio_do_int(fio,idum); 
      gmx_fio_do_int(fio,idum);
    }
    if(file_version < 18)
      gmx_fio_do_int(fio,idum); 
    gmx_fio_do_real(fio,ir->rlist); 
    if (file_version >= 67) {
      gmx_fio_do_real(fio,ir->rlistlong);
    }
    gmx_fio_do_int(fio,ir->coulombtype); 
    if (file_version < 32 && ir->coulombtype == eelRF)
      ir->coulombtype = eelRF_NEC;      
    gmx_fio_do_real(fio,ir->rcoulomb_switch); 
    gmx_fio_do_real(fio,ir->rcoulomb); 
    gmx_fio_do_int(fio,ir->vdwtype);
    gmx_fio_do_real(fio,ir->rvdw_switch); 
    gmx_fio_do_real(fio,ir->rvdw); 
    if (file_version < 67) {
      ir->rlistlong = max_cutoff(ir->rlist,max_cutoff(ir->rvdw,ir->rcoulomb));
    }
    gmx_fio_do_int(fio,ir->eDispCorr); 
    gmx_fio_do_real(fio,ir->epsilon_r);
    if (file_version >= 37) {
      gmx_fio_do_real(fio,ir->epsilon_rf);
    } else {
      if (EEL_RF(ir->coulombtype)) {
	ir->epsilon_rf = ir->epsilon_r;
	ir->epsilon_r  = 1.0;
      } else {
	ir->epsilon_rf = 1.0;
      }
    }
    if (file_version >= 29)
      gmx_fio_do_real(fio,ir->tabext);
    else
      ir->tabext=1.0;
 
    if(file_version > 25) {
      gmx_fio_do_int(fio,ir->gb_algorithm);
      gmx_fio_do_int(fio,ir->nstgbradii);
      gmx_fio_do_real(fio,ir->rgbradii);
      gmx_fio_do_real(fio,ir->gb_saltconc);
      gmx_fio_do_int(fio,ir->implicit_solvent);
    } else {
      ir->gb_algorithm=egbSTILL;
      ir->nstgbradii=1;
      ir->rgbradii=1.0;
      ir->gb_saltconc=0;
      ir->implicit_solvent=eisNO;
    }
	if(file_version>=55)
	{
		gmx_fio_do_real(fio,ir->gb_epsilon_solvent);
		gmx_fio_do_real(fio,ir->gb_obc_alpha);
		gmx_fio_do_real(fio,ir->gb_obc_beta);
		gmx_fio_do_real(fio,ir->gb_obc_gamma);
		if(file_version>=60)
		{
			gmx_fio_do_real(fio,ir->gb_dielectric_offset);
			gmx_fio_do_int(fio,ir->sa_algorithm);
		}
		else
		{
			ir->gb_dielectric_offset = 0.009;
			ir->sa_algorithm = esaAPPROX;
		}
		gmx_fio_do_real(fio,ir->sa_surface_tension);

    /* Override sa_surface_tension if it is not changed in the mpd-file */
    if(ir->sa_surface_tension<0)
    {
      if(ir->gb_algorithm==egbSTILL)
      {
        ir->sa_surface_tension = 0.0049 * 100 * CAL2JOULE;
      }
      else if(ir->gb_algorithm==egbHCT || ir->gb_algorithm==egbOBC)
      {
        ir->sa_surface_tension = 0.0054 * 100 * CAL2JOULE;
      }
    }
    
	}
	else
	{
		/* Better use sensible values than insane (0.0) ones... */
		ir->gb_epsilon_solvent = 80;
		ir->gb_obc_alpha       = 1.0;
		ir->gb_obc_beta        = 0.8;
		ir->gb_obc_gamma       = 4.85;
		ir->sa_surface_tension = 2.092;
	}

	  
    gmx_fio_do_int(fio,ir->nkx); 
    gmx_fio_do_int(fio,ir->nky); 
    gmx_fio_do_int(fio,ir->nkz);
    gmx_fio_do_int(fio,ir->pme_order);
    gmx_fio_do_real(fio,ir->ewald_rtol);

    if (file_version >=24) 
      gmx_fio_do_int(fio,ir->ewald_geometry);
    else
      ir->ewald_geometry=eewg3D;

    if (file_version <=17) {
      ir->epsilon_surface=0;
      if (file_version==17)
	gmx_fio_do_int(fio,idum);
    } 
    else
      gmx_fio_do_real(fio,ir->epsilon_surface);
    
    gmx_fio_do_gmx_bool(fio,ir->bOptFFT);

    gmx_fio_do_gmx_bool(fio,ir->bContinuation); 
    gmx_fio_do_int(fio,ir->etc);
    /* before version 18, ir->etc was a gmx_bool (ir->btc),
     * but the values 0 and 1 still mean no and
     * berendsen temperature coupling, respectively.
     */
    if (file_version >= 71)
    {
        gmx_fio_do_int(fio,ir->nsttcouple);
    }
    else
    {
        ir->nsttcouple = ir->nstcalcenergy;
    }
    if (file_version <= 15)
    {
        gmx_fio_do_int(fio,idum);
    }
    if (file_version <=17)
    {
        gmx_fio_do_int(fio,ir->epct); 
        if (file_version <= 15)
        {
            if (ir->epct == 5)
            {
                ir->epct = epctSURFACETENSION;
            }
            gmx_fio_do_int(fio,idum);
        }
        ir->epct -= 1;
        /* we have removed the NO alternative at the beginning */
        if(ir->epct==-1)
        {
            ir->epc=epcNO;
            ir->epct=epctISOTROPIC;
        } 
        else
        {
            ir->epc=epcBERENDSEN;
        }
    } 
    else
    {
        gmx_fio_do_int(fio,ir->epc);
        gmx_fio_do_int(fio,ir->epct);
    }
    if (file_version >= 71)
    {
        gmx_fio_do_int(fio,ir->nstpcouple);
    }
    else
    {
        ir->nstpcouple = ir->nstcalcenergy;
    }
    gmx_fio_do_real(fio,ir->tau_p); 
    if (file_version <= 15) {
      gmx_fio_do_rvec(fio,vdum);
      clear_mat(ir->ref_p);
      for(i=0; i<DIM; i++)
	ir->ref_p[i][i] = vdum[i];
    } else {
      gmx_fio_do_rvec(fio,ir->ref_p[XX]);
      gmx_fio_do_rvec(fio,ir->ref_p[YY]);
      gmx_fio_do_rvec(fio,ir->ref_p[ZZ]);
    }
    if (file_version <= 15) {
      gmx_fio_do_rvec(fio,vdum);
      clear_mat(ir->compress);
      for(i=0; i<DIM; i++)
	ir->compress[i][i] = vdum[i];
    } 
    else {
      gmx_fio_do_rvec(fio,ir->compress[XX]);
      gmx_fio_do_rvec(fio,ir->compress[YY]);
      gmx_fio_do_rvec(fio,ir->compress[ZZ]);
    }
    if (file_version >= 47) {
      gmx_fio_do_int(fio,ir->refcoord_scaling);
      gmx_fio_do_rvec(fio,ir->posres_com);
      gmx_fio_do_rvec(fio,ir->posres_comB);
    } else {
      ir->refcoord_scaling = erscNO;
      clear_rvec(ir->posres_com);
      clear_rvec(ir->posres_comB);
    }
    if(file_version > 25)
      gmx_fio_do_int(fio,ir->andersen_seed);
    else
      ir->andersen_seed=0;
    
    if(file_version < 26) {
      gmx_fio_do_gmx_bool(fio,bSimAnn); 
      gmx_fio_do_real(fio,zerotemptime);
    }
    
    if (file_version < 37)
      gmx_fio_do_real(fio,rdum); 

    gmx_fio_do_real(fio,ir->shake_tol);
    if (file_version < 54)
      gmx_fio_do_real(fio,*fudgeQQ);
    gmx_fio_do_int(fio,ir->efep);
    if (file_version <= 14 && ir->efep > efepNO)
      ir->efep = efepYES;
    if (file_version >= 59) {
      gmx_fio_do_double(fio, ir->init_lambda); 
      gmx_fio_do_double(fio, ir->delta_lambda);
    } else {
      gmx_fio_do_real(fio,rdum);
      ir->init_lambda = rdum;
      gmx_fio_do_real(fio,rdum);
      ir->delta_lambda = rdum;
    }
    if (file_version >= 64) {
      gmx_fio_do_int(fio,ir->n_flambda);
      if (bRead) {
	snew(ir->flambda,ir->n_flambda);
      }
      bDum=gmx_fio_ndo_double(fio,ir->flambda,ir->n_flambda);
    } else {
      ir->n_flambda = 0;
      ir->flambda   = NULL;
    }
    if (file_version >= 13)
      gmx_fio_do_real(fio,ir->sc_alpha);
    else
      ir->sc_alpha = 0;
    if (file_version >= 38)
      gmx_fio_do_int(fio,ir->sc_power);
    else
      ir->sc_power = 2;
    if (file_version >= 15)
      gmx_fio_do_real(fio,ir->sc_sigma);
    else
      ir->sc_sigma = 0.3;
    if (bRead)
    {
        if (file_version >= 71)
        {
            ir->sc_sigma_min = ir->sc_sigma;
        }
        else
        {
            ir->sc_sigma_min = 0;
        }
    }
    if (file_version >= 64) {
      gmx_fio_do_int(fio,ir->nstdhdl);
    } else {
      ir->nstdhdl = 1;
    }

    if (file_version >= 73)
    {
        gmx_fio_do_int(fio, ir->separate_dhdl_file);
        gmx_fio_do_int(fio, ir->dhdl_derivatives);
    }
    else
    {
        ir->separate_dhdl_file = sepdhdlfileYES;
        ir->dhdl_derivatives = dhdlderivativesYES;
    }

    if (file_version >= 71)
    {
        gmx_fio_do_int(fio,ir->dh_hist_size);
        gmx_fio_do_double(fio,ir->dh_hist_spacing);
    }
    else
    {
        ir->dh_hist_size    = 0;
        ir->dh_hist_spacing = 0.1;
    }
    if (file_version >= 57) {
      gmx_fio_do_int(fio,ir->eDisre); 
    }
    gmx_fio_do_int(fio,ir->eDisreWeighting); 
    if (file_version < 22) {
      if (ir->eDisreWeighting == 0)
	ir->eDisreWeighting = edrwEqual;
      else
	ir->eDisreWeighting = edrwConservative;
    }
    gmx_fio_do_gmx_bool(fio,ir->bDisreMixed); 
    gmx_fio_do_real(fio,ir->dr_fc); 
    gmx_fio_do_real(fio,ir->dr_tau); 
    gmx_fio_do_int(fio,ir->nstdisreout);
    if (file_version >= 22) {
      gmx_fio_do_real(fio,ir->orires_fc);
      gmx_fio_do_real(fio,ir->orires_tau);
      gmx_fio_do_int(fio,ir->nstorireout);
    } else {
      ir->orires_fc = 0;
      ir->orires_tau = 0;
      ir->nstorireout = 0;
    }
    if(file_version >= 26) {
      gmx_fio_do_real(fio,ir->dihre_fc);
      if (file_version < 56) {
	gmx_fio_do_real(fio,rdum);
	gmx_fio_do_int(fio,idum);
      }
    } else {
      ir->dihre_fc=0;
    }

    gmx_fio_do_real(fio,ir->em_stepsize); 
    gmx_fio_do_real(fio,ir->em_tol); 
    if (file_version >= 22) 
      gmx_fio_do_gmx_bool(fio,ir->bShakeSOR);
    else if (bRead)
      ir->bShakeSOR = TRUE;
    if (file_version >= 11)
      gmx_fio_do_int(fio,ir->niter);
    else if (bRead) {
      ir->niter = 25;
      fprintf(stderr,"Note: niter not in run input file, setting it to %d\n",
	      ir->niter);
    }
    if (file_version >= 21)
      gmx_fio_do_real(fio,ir->fc_stepsize);
    else
      ir->fc_stepsize = 0;
    gmx_fio_do_int(fio,ir->eConstrAlg);
    gmx_fio_do_int(fio,ir->nProjOrder);
    gmx_fio_do_real(fio,ir->LincsWarnAngle);
    if (file_version <= 14)
      gmx_fio_do_int(fio,idum);
    if (file_version >=26)
      gmx_fio_do_int(fio,ir->nLincsIter);
    else if (bRead) {
      ir->nLincsIter = 1;
      fprintf(stderr,"Note: nLincsIter not in run input file, setting it to %d\n",
	      ir->nLincsIter);
    }
    if (file_version < 33)
      gmx_fio_do_real(fio,bd_temp);
    gmx_fio_do_real(fio,ir->bd_fric);
    gmx_fio_do_int(fio,ir->ld_seed);
    if (file_version >= 33) {
      for(i=0; i<DIM; i++)
	gmx_fio_do_rvec(fio,ir->deform[i]);
    } else {
      for(i=0; i<DIM; i++)
	clear_rvec(ir->deform[i]);
    }
    if (file_version >= 14)
      gmx_fio_do_real(fio,ir->cos_accel);
    else if (bRead)
      ir->cos_accel = 0;
    gmx_fio_do_int(fio,ir->userint1); 
    gmx_fio_do_int(fio,ir->userint2); 
    gmx_fio_do_int(fio,ir->userint3); 
    gmx_fio_do_int(fio,ir->userint4); 
    gmx_fio_do_real(fio,ir->userreal1); 
    gmx_fio_do_real(fio,ir->userreal2); 
    gmx_fio_do_real(fio,ir->userreal3); 
    gmx_fio_do_real(fio,ir->userreal4); 
    
    /* pull stuff */
    if (file_version >= 48) {
      gmx_fio_do_int(fio,ir->ePull);
      if (ir->ePull != epullNO) {
	if (bRead)
	  snew(ir->pull,1);
	do_pull(fio, ir->pull,bRead,file_version);
      }
    } else {
      ir->ePull = epullNO;
    }
    
    /* grpopts stuff */
    gmx_fio_do_int(fio,ir->opts.ngtc); 
    if (file_version >= 69) {
      gmx_fio_do_int(fio,ir->opts.nhchainlength);
    } else {
      ir->opts.nhchainlength = 1;
    }
    gmx_fio_do_int(fio,ir->opts.ngacc); 
    gmx_fio_do_int(fio,ir->opts.ngfrz); 
    gmx_fio_do_int(fio,ir->opts.ngener);
    
    if (bRead) {
      snew(ir->opts.nrdf,   ir->opts.ngtc); 
      snew(ir->opts.ref_t,  ir->opts.ngtc); 
      snew(ir->opts.annealing, ir->opts.ngtc); 
      snew(ir->opts.anneal_npoints, ir->opts.ngtc); 
      snew(ir->opts.anneal_time, ir->opts.ngtc); 
      snew(ir->opts.anneal_temp, ir->opts.ngtc); 
      snew(ir->opts.tau_t,  ir->opts.ngtc); 
      snew(ir->opts.nFreeze,ir->opts.ngfrz); 
      snew(ir->opts.acc,    ir->opts.ngacc); 
      snew(ir->opts.egp_flags,ir->opts.ngener*ir->opts.ngener);
    } 
    if (ir->opts.ngtc > 0) {
      if (bRead && file_version<13) {
	snew(tmp,ir->opts.ngtc);
	bDum=gmx_fio_ndo_int(fio,tmp, ir->opts.ngtc);
	for(i=0; i<ir->opts.ngtc; i++)
	  ir->opts.nrdf[i] = tmp[i];
	sfree(tmp);
      } else {
	bDum=gmx_fio_ndo_real(fio,ir->opts.nrdf, ir->opts.ngtc);
      }
      bDum=gmx_fio_ndo_real(fio,ir->opts.ref_t,ir->opts.ngtc); 
      bDum=gmx_fio_ndo_real(fio,ir->opts.tau_t,ir->opts.ngtc); 
      if (file_version<33 && ir->eI==eiBD) {
	for(i=0; i<ir->opts.ngtc; i++)
	  ir->opts.tau_t[i] = bd_temp;
      }
    }
    if (ir->opts.ngfrz > 0) 
      bDum=gmx_fio_ndo_ivec(fio,ir->opts.nFreeze,ir->opts.ngfrz);
    if (ir->opts.ngacc > 0) 
      gmx_fio_ndo_rvec(fio,ir->opts.acc,ir->opts.ngacc); 
    if (file_version >= 12)
      bDum=gmx_fio_ndo_int(fio,ir->opts.egp_flags,
                           ir->opts.ngener*ir->opts.ngener);

    if(bRead && file_version < 26) {
      for(i=0;i<ir->opts.ngtc;i++) {
	if(bSimAnn) {
	  ir->opts.annealing[i] = eannSINGLE;
	  ir->opts.anneal_npoints[i] = 2;
	  snew(ir->opts.anneal_time[i],2);
	  snew(ir->opts.anneal_temp[i],2);
	  /* calculate the starting/ending temperatures from reft, zerotemptime, and nsteps */
	  finish_t = ir->init_t + ir->nsteps * ir->delta_t;
	  init_temp = ir->opts.ref_t[i]*(1-ir->init_t/zerotemptime);
	  finish_temp = ir->opts.ref_t[i]*(1-finish_t/zerotemptime);
	  ir->opts.anneal_time[i][0] = ir->init_t;
	  ir->opts.anneal_time[i][1] = finish_t;
	  ir->opts.anneal_temp[i][0] = init_temp;
	  ir->opts.anneal_temp[i][1] = finish_temp;
	} else {
	  ir->opts.annealing[i] = eannNO;
	  ir->opts.anneal_npoints[i] = 0;
	}
      }
    } else {
      /* file version 26 or later */
      /* First read the lists with annealing and npoints for each group */
      bDum=gmx_fio_ndo_int(fio,ir->opts.annealing,ir->opts.ngtc);
      bDum=gmx_fio_ndo_int(fio,ir->opts.anneal_npoints,ir->opts.ngtc);
      for(j=0;j<(ir->opts.ngtc);j++) {
	k=ir->opts.anneal_npoints[j];
	if(bRead) {
	  snew(ir->opts.anneal_time[j],k);
	  snew(ir->opts.anneal_temp[j],k);
	}
	bDum=gmx_fio_ndo_real(fio,ir->opts.anneal_time[j],k);
	bDum=gmx_fio_ndo_real(fio,ir->opts.anneal_temp[j],k);
      }
    }
    /* Walls */
    if (file_version >= 45) {
      gmx_fio_do_int(fio,ir->nwall);
      gmx_fio_do_int(fio,ir->wall_type);
      if (file_version >= 50)
	gmx_fio_do_real(fio,ir->wall_r_linpot);
      else
	ir->wall_r_linpot = -1;
      gmx_fio_do_int(fio,ir->wall_atomtype[0]);
      gmx_fio_do_int(fio,ir->wall_atomtype[1]);
      gmx_fio_do_real(fio,ir->wall_density[0]);
      gmx_fio_do_real(fio,ir->wall_density[1]);
      gmx_fio_do_real(fio,ir->wall_ewald_zfac);
    } else {
      ir->nwall = 0;
      ir->wall_type = 0;
      ir->wall_atomtype[0] = -1;
      ir->wall_atomtype[1] = -1;
      ir->wall_density[0] = 0;
      ir->wall_density[1] = 0;
      ir->wall_ewald_zfac = 3;
    }
    /* Cosine stuff for electric fields */
    for(j=0; (j<DIM); j++) {
      gmx_fio_do_int(fio,ir->ex[j].n);
      gmx_fio_do_int(fio,ir->et[j].n);
      if (bRead) {
	snew(ir->ex[j].a,  ir->ex[j].n);
	snew(ir->ex[j].phi,ir->ex[j].n);
	snew(ir->et[j].a,  ir->et[j].n);
	snew(ir->et[j].phi,ir->et[j].n);
      }
      bDum=gmx_fio_ndo_real(fio,ir->ex[j].a,  ir->ex[j].n);
      bDum=gmx_fio_ndo_real(fio,ir->ex[j].phi,ir->ex[j].n);
      bDum=gmx_fio_ndo_real(fio,ir->et[j].a,  ir->et[j].n);
      bDum=gmx_fio_ndo_real(fio,ir->et[j].phi,ir->et[j].n);
    }
    
    /* QMMM stuff */
    if(file_version>=39){
      gmx_fio_do_gmx_bool(fio,ir->bQMMM);
      gmx_fio_do_int(fio,ir->QMMMscheme);
      gmx_fio_do_real(fio,ir->scalefactor);
      gmx_fio_do_int(fio,ir->opts.ngQM);
      if (bRead) {
        snew(ir->opts.QMmethod,    ir->opts.ngQM);
        snew(ir->opts.QMbasis,     ir->opts.ngQM);
        snew(ir->opts.QMcharge,    ir->opts.ngQM);
        snew(ir->opts.QMmult,      ir->opts.ngQM);
        snew(ir->opts.bSH,         ir->opts.ngQM);
        snew(ir->opts.CASorbitals, ir->opts.ngQM);
        snew(ir->opts.CASelectrons,ir->opts.ngQM);
        snew(ir->opts.SAon,        ir->opts.ngQM);
        snew(ir->opts.SAoff,       ir->opts.ngQM);
        snew(ir->opts.SAsteps,     ir->opts.ngQM);
        snew(ir->opts.bOPT,        ir->opts.ngQM);
        snew(ir->opts.bTS,         ir->opts.ngQM);
      }
      if (ir->opts.ngQM > 0) {
        bDum=gmx_fio_ndo_int(fio,ir->opts.QMmethod,ir->opts.ngQM);
        bDum=gmx_fio_ndo_int(fio,ir->opts.QMbasis,ir->opts.ngQM);
        bDum=gmx_fio_ndo_int(fio,ir->opts.QMcharge,ir->opts.ngQM);
        bDum=gmx_fio_ndo_int(fio,ir->opts.QMmult,ir->opts.ngQM);
        bDum=gmx_fio_ndo_gmx_bool(fio,ir->opts.bSH,ir->opts.ngQM);
        bDum=gmx_fio_ndo_int(fio,ir->opts.CASorbitals,ir->opts.ngQM);
        bDum=gmx_fio_ndo_int(fio,ir->opts.CASelectrons,ir->opts.ngQM);
        bDum=gmx_fio_ndo_real(fio,ir->opts.SAon,ir->opts.ngQM);
        bDum=gmx_fio_ndo_real(fio,ir->opts.SAoff,ir->opts.ngQM);
        bDum=gmx_fio_ndo_int(fio,ir->opts.SAsteps,ir->opts.ngQM);
        bDum=gmx_fio_ndo_gmx_bool(fio,ir->opts.bOPT,ir->opts.ngQM);
        bDum=gmx_fio_ndo_gmx_bool(fio,ir->opts.bTS,ir->opts.ngQM);
      }
      /* end of QMMM stuff */
    }    
}


static void do_harm(t_fileio *fio, t_iparams *iparams,gmx_bool bRead)
{
  gmx_fio_do_real(fio,iparams->harmonic.rA);
  gmx_fio_do_real(fio,iparams->harmonic.krA);
  gmx_fio_do_real(fio,iparams->harmonic.rB);
  gmx_fio_do_real(fio,iparams->harmonic.krB);
}

void do_iparams(t_fileio *fio, t_functype ftype,t_iparams *iparams,
                gmx_bool bRead, int file_version)
{
  int i;
  gmx_bool bDum;
  real rdum;
  
  if (!bRead)
    gmx_fio_set_comment(fio, interaction_function[ftype].name);
  switch (ftype) {
  case F_ANGLES:
  case F_G96ANGLES:
  case F_BONDS:
  case F_G96BONDS:
  case F_HARMONIC:
  case F_IDIHS:
    do_harm(fio, iparams,bRead);
    if ((ftype == F_ANGRES || ftype == F_ANGRESZ) && bRead) {
      /* Correct incorrect storage of parameters */
      iparams->pdihs.phiB = iparams->pdihs.phiA;
      iparams->pdihs.cpB  = iparams->pdihs.cpA;
    }
    break;
  case F_FENEBONDS:
    gmx_fio_do_real(fio,iparams->fene.bm);
    gmx_fio_do_real(fio,iparams->fene.kb);
    break;
  case F_RESTRBONDS:
    gmx_fio_do_real(fio,iparams->restraint.lowA);
    gmx_fio_do_real(fio,iparams->restraint.up1A);
    gmx_fio_do_real(fio,iparams->restraint.up2A);
    gmx_fio_do_real(fio,iparams->restraint.kA);
    gmx_fio_do_real(fio,iparams->restraint.lowB);
    gmx_fio_do_real(fio,iparams->restraint.up1B);
    gmx_fio_do_real(fio,iparams->restraint.up2B);
    gmx_fio_do_real(fio,iparams->restraint.kB);
    break;
  case F_TABBONDS:
  case F_TABBONDSNC:
  case F_TABANGLES:
  case F_TABDIHS:
    gmx_fio_do_real(fio,iparams->tab.kA);
    gmx_fio_do_int(fio,iparams->tab.table);
    gmx_fio_do_real(fio,iparams->tab.kB);
    break;
  case F_CROSS_BOND_BONDS:
    gmx_fio_do_real(fio,iparams->cross_bb.r1e);
    gmx_fio_do_real(fio,iparams->cross_bb.r2e);
    gmx_fio_do_real(fio,iparams->cross_bb.krr);
    break;
  case F_CROSS_BOND_ANGLES:
    gmx_fio_do_real(fio,iparams->cross_ba.r1e);
    gmx_fio_do_real(fio,iparams->cross_ba.r2e);
    gmx_fio_do_real(fio,iparams->cross_ba.r3e);
    gmx_fio_do_real(fio,iparams->cross_ba.krt);
    break;
  case F_UREY_BRADLEY:
    gmx_fio_do_real(fio,iparams->u_b.theta);
    gmx_fio_do_real(fio,iparams->u_b.ktheta);
    gmx_fio_do_real(fio,iparams->u_b.r13);
    gmx_fio_do_real(fio,iparams->u_b.kUB);
    break;
  case F_QUARTIC_ANGLES:
    gmx_fio_do_real(fio,iparams->qangle.theta);
    bDum=gmx_fio_ndo_real(fio,iparams->qangle.c,5);
    break;
  case F_BHAM:
    gmx_fio_do_real(fio,iparams->bham.a);
    gmx_fio_do_real(fio,iparams->bham.b);
    gmx_fio_do_real(fio,iparams->bham.c);
    break;
  case F_MORSE:
    gmx_fio_do_real(fio,iparams->morse.b0);
    gmx_fio_do_real(fio,iparams->morse.cb);
    gmx_fio_do_real(fio,iparams->morse.beta);
    break;
  case F_CUBICBONDS:
    gmx_fio_do_real(fio,iparams->cubic.b0);
    gmx_fio_do_real(fio,iparams->cubic.kb);
    gmx_fio_do_real(fio,iparams->cubic.kcub);
    break;
  case F_CONNBONDS:
    break;
  case F_POLARIZATION:
    gmx_fio_do_real(fio,iparams->polarize.alpha);
    break;
  case F_WATER_POL:
    if (file_version < 31) 
      gmx_fatal(FARGS,"Old tpr files with water_polarization not supported. Make a new.");
    gmx_fio_do_real(fio,iparams->wpol.al_x);
    gmx_fio_do_real(fio,iparams->wpol.al_y);
    gmx_fio_do_real(fio,iparams->wpol.al_z);
    gmx_fio_do_real(fio,iparams->wpol.rOH);
    gmx_fio_do_real(fio,iparams->wpol.rHH);
    gmx_fio_do_real(fio,iparams->wpol.rOD);
    break;
  case F_THOLE_POL:
    gmx_fio_do_real(fio,iparams->thole.a);
    gmx_fio_do_real(fio,iparams->thole.alpha1);
    gmx_fio_do_real(fio,iparams->thole.alpha2);
    gmx_fio_do_real(fio,iparams->thole.rfac);
    break;
  case F_LJ:
    gmx_fio_do_real(fio,iparams->lj.c6);
    gmx_fio_do_real(fio,iparams->lj.c12);
    break;
  case F_LJ14:
    gmx_fio_do_real(fio,iparams->lj14.c6A);
    gmx_fio_do_real(fio,iparams->lj14.c12A);
    gmx_fio_do_real(fio,iparams->lj14.c6B);
    gmx_fio_do_real(fio,iparams->lj14.c12B);
    break;
  case F_LJC14_Q:
    gmx_fio_do_real(fio,iparams->ljc14.fqq);
    gmx_fio_do_real(fio,iparams->ljc14.qi);
    gmx_fio_do_real(fio,iparams->ljc14.qj);
    gmx_fio_do_real(fio,iparams->ljc14.c6);
    gmx_fio_do_real(fio,iparams->ljc14.c12);
    break;
  case F_LJC_PAIRS_NB:
    gmx_fio_do_real(fio,iparams->ljcnb.qi);
    gmx_fio_do_real(fio,iparams->ljcnb.qj);
    gmx_fio_do_real(fio,iparams->ljcnb.c6);
    gmx_fio_do_real(fio,iparams->ljcnb.c12);
    break;
  case F_PDIHS:
  case F_PIDIHS:
  case F_ANGRES:
  case F_ANGRESZ:
    gmx_fio_do_real(fio,iparams->pdihs.phiA);
    gmx_fio_do_real(fio,iparams->pdihs.cpA);
    if ((ftype == F_ANGRES || ftype == F_ANGRESZ) && file_version < 42) {
      /* Read the incorrectly stored multiplicity */
      gmx_fio_do_real(fio,iparams->harmonic.rB);
      gmx_fio_do_real(fio,iparams->harmonic.krB);
      iparams->pdihs.phiB = iparams->pdihs.phiA;
      iparams->pdihs.cpB  = iparams->pdihs.cpA;
    } else {
      gmx_fio_do_real(fio,iparams->pdihs.phiB);
      gmx_fio_do_real(fio,iparams->pdihs.cpB);
      gmx_fio_do_int(fio,iparams->pdihs.mult);
    }
    break;
  case F_DISRES:
    gmx_fio_do_int(fio,iparams->disres.label);
    gmx_fio_do_int(fio,iparams->disres.type);
    gmx_fio_do_real(fio,iparams->disres.low);
    gmx_fio_do_real(fio,iparams->disres.up1);
    gmx_fio_do_real(fio,iparams->disres.up2);
    gmx_fio_do_real(fio,iparams->disres.kfac);
    break;
  case F_ORIRES:
    gmx_fio_do_int(fio,iparams->orires.ex);
    gmx_fio_do_int(fio,iparams->orires.label);
    gmx_fio_do_int(fio,iparams->orires.power);
    gmx_fio_do_real(fio,iparams->orires.c);
    gmx_fio_do_real(fio,iparams->orires.obs);
    gmx_fio_do_real(fio,iparams->orires.kfac);
    break;
  case F_DIHRES:
    gmx_fio_do_int(fio,iparams->dihres.power);
    gmx_fio_do_int(fio,iparams->dihres.label);
    gmx_fio_do_real(fio,iparams->dihres.phi);
    gmx_fio_do_real(fio,iparams->dihres.dphi);
    gmx_fio_do_real(fio,iparams->dihres.kfac);
    break;
  case F_POSRES:
    gmx_fio_do_rvec(fio,iparams->posres.pos0A);
    gmx_fio_do_rvec(fio,iparams->posres.fcA);
    if (bRead && file_version < 27) {
      copy_rvec(iparams->posres.pos0A,iparams->posres.pos0B);
      copy_rvec(iparams->posres.fcA,iparams->posres.fcB);
    } else {
      gmx_fio_do_rvec(fio,iparams->posres.pos0B);
      gmx_fio_do_rvec(fio,iparams->posres.fcB);
    }
    break;
  case F_RBDIHS:
    bDum=gmx_fio_ndo_real(fio,iparams->rbdihs.rbcA,NR_RBDIHS);
    if(file_version>=25) 
      bDum=gmx_fio_ndo_real(fio,iparams->rbdihs.rbcB,NR_RBDIHS);
    break;
  case F_FOURDIHS:
    /* Fourier dihedrals are internally represented
     * as Ryckaert-Bellemans since those are faster to compute.
     */
     bDum=gmx_fio_ndo_real(fio,iparams->rbdihs.rbcA, NR_RBDIHS);
     bDum=gmx_fio_ndo_real(fio,iparams->rbdihs.rbcB, NR_RBDIHS);
    break;
  case F_CONSTR:
  case F_CONSTRNC:
    gmx_fio_do_real(fio,iparams->constr.dA);
    gmx_fio_do_real(fio,iparams->constr.dB);
    break;
  case F_SETTLE:
    gmx_fio_do_real(fio,iparams->settle.doh);
    gmx_fio_do_real(fio,iparams->settle.dhh);
    break;
  case F_VSITE2:
    gmx_fio_do_real(fio,iparams->vsite.a);
    break;
  case F_VSITE3:
  case F_VSITE3FD:
  case F_VSITE3FAD:
    gmx_fio_do_real(fio,iparams->vsite.a);
    gmx_fio_do_real(fio,iparams->vsite.b);
    break;
  case F_VSITE3OUT:
  case F_VSITE4FD: 
  case F_VSITE4FDN: 
    gmx_fio_do_real(fio,iparams->vsite.a);
    gmx_fio_do_real(fio,iparams->vsite.b);
    gmx_fio_do_real(fio,iparams->vsite.c);
    break;
  case F_VSITEN:
    gmx_fio_do_int(fio,iparams->vsiten.n);
    gmx_fio_do_real(fio,iparams->vsiten.a);
    break;
  case F_GB12:
  case F_GB13:
  case F_GB14:
    /* We got rid of some parameters in version 68 */
    if(bRead && file_version<68)
    {
        gmx_fio_do_real(fio,rdum);	
        gmx_fio_do_real(fio,rdum);	
        gmx_fio_do_real(fio,rdum);	
        gmx_fio_do_real(fio,rdum);	
    }
	gmx_fio_do_real(fio,iparams->gb.sar);	
	gmx_fio_do_real(fio,iparams->gb.st);
	gmx_fio_do_real(fio,iparams->gb.pi);
	gmx_fio_do_real(fio,iparams->gb.gbr);
	gmx_fio_do_real(fio,iparams->gb.bmlt);
	break;
  case F_CMAP:
	gmx_fio_do_int(fio,iparams->cmap.cmapA);
	gmx_fio_do_int(fio,iparams->cmap.cmapB);
    break;
  default:
    gmx_fatal(FARGS,"unknown function type %d (%s) in %s line %d",
		
    		ftype,interaction_function[ftype].name,__FILE__,__LINE__);
  }
  if (!bRead)
    gmx_fio_unset_comment(fio);
}

static void do_ilist(t_fileio *fio, t_ilist *ilist,gmx_bool bRead,int file_version,
		     int ftype)
{
  int  i,k,idum;
  gmx_bool bDum=TRUE;
  
  if (!bRead) {
    gmx_fio_set_comment(fio, interaction_function[ftype].name);
  }
  if (file_version < 44) {
    for(i=0; i<MAXNODES; i++)
      gmx_fio_do_int(fio,idum);
  }
  gmx_fio_do_int(fio,ilist->nr);
  if (bRead)
    snew(ilist->iatoms,ilist->nr);
  bDum=gmx_fio_ndo_int(fio,ilist->iatoms,ilist->nr);
  if (!bRead)
    gmx_fio_unset_comment(fio);
}

static void do_ffparams(t_fileio *fio, gmx_ffparams_t *ffparams,
			gmx_bool bRead, int file_version)
{
  int  idum,i,j;
  gmx_bool bDum=TRUE;
  unsigned int k;

  gmx_fio_do_int(fio,ffparams->atnr);
  if (file_version < 57) {
    gmx_fio_do_int(fio,idum);
  }
  gmx_fio_do_int(fio,ffparams->ntypes);
  if (bRead && debug)
    fprintf(debug,"ffparams->atnr = %d, ntypes = %d\n",
	    ffparams->atnr,ffparams->ntypes);
  if (bRead) {
    snew(ffparams->functype,ffparams->ntypes);
    snew(ffparams->iparams,ffparams->ntypes);
  }
  /* Read/write all the function types */
  bDum=gmx_fio_ndo_int(fio,ffparams->functype,ffparams->ntypes);
  if (bRead && debug)
    pr_ivec(debug,0,"functype",ffparams->functype,ffparams->ntypes,TRUE);

  if (file_version >= 66) {
    gmx_fio_do_double(fio,ffparams->reppow);
  } else {
    ffparams->reppow = 12.0;
  }

  if (file_version >= 57) {
    gmx_fio_do_real(fio,ffparams->fudgeQQ);
  }

  /* Check whether all these function types are supported by the code.
   * In practice the code is backwards compatible, which means that the
   * numbering may have to be altered from old numbering to new numbering
   */
  for (i=0; (i<ffparams->ntypes); i++) {
    if (bRead)
      /* Loop over file versions */
      for (k=0; (k<NFTUPD); k++)
	/* Compare the read file_version to the update table */
	if ((file_version < ftupd[k].fvnr) && 
	    (ffparams->functype[i] >= ftupd[k].ftype)) {
	  ffparams->functype[i] += 1;
	  if (debug) {
	    fprintf(debug,"Incrementing function type %d to %d (due to %s)\n",
		    i,ffparams->functype[i],
		    interaction_function[ftupd[k].ftype].longname);
	    fflush(debug);
	  }
	}
    
    do_iparams(fio, ffparams->functype[i],&ffparams->iparams[i],bRead,
               file_version);
    if (bRead && debug)
      pr_iparams(debug,ffparams->functype[i],&ffparams->iparams[i]);
  }
}

static void do_ilists(t_fileio *fio, t_ilist *ilist,gmx_bool bRead, 
                      int file_version)
{
  int i,j,renum[F_NRE];
  gmx_bool bDum=TRUE,bClear;
  unsigned int k;
  
  for(j=0; (j<F_NRE); j++) {
    bClear = FALSE;
    if (bRead)
      for (k=0; k<NFTUPD; k++)
	if ((file_version < ftupd[k].fvnr) && (j == ftupd[k].ftype))
	  bClear = TRUE;
    if (bClear) {
      ilist[j].nr = 0;
      ilist[j].iatoms = NULL;
    } else {
      do_ilist(fio, &ilist[j],bRead,file_version,j);
    }
    /*
    if (bRead && gmx_debug_at)
      pr_ilist(debug,0,interaction_function[j].longname,
	       functype,&ilist[j],TRUE);
    */
  }
}

static void do_idef(t_fileio *fio, gmx_ffparams_t *ffparams,gmx_moltype_t *molt,
		    gmx_bool bRead, int file_version)
{
  do_ffparams(fio, ffparams,bRead,file_version);
    
  if (file_version >= 54) {
    gmx_fio_do_real(fio,ffparams->fudgeQQ);
  }

  do_ilists(fio, molt->ilist,bRead,file_version);
}

static void do_block(t_fileio *fio, t_block *block,gmx_bool bRead,int file_version)
{
  int  i,idum,dum_nra,*dum_a;
  gmx_bool bDum=TRUE;

  if (file_version < 44)
    for(i=0; i<MAXNODES; i++)
      gmx_fio_do_int(fio,idum);
  gmx_fio_do_int(fio,block->nr);
  if (file_version < 51)
    gmx_fio_do_int(fio,dum_nra);
  if (bRead) {
    block->nalloc_index = block->nr+1;
    snew(block->index,block->nalloc_index);
  }
  bDum=gmx_fio_ndo_int(fio,block->index,block->nr+1);

  if (file_version < 51 && dum_nra > 0) {
    snew(dum_a,dum_nra);
    bDum=gmx_fio_ndo_int(fio,dum_a,dum_nra);
    sfree(dum_a);
  }
}

static void do_blocka(t_fileio *fio, t_blocka *block,gmx_bool bRead,
                      int file_version)
{
  int  i,idum;
  gmx_bool bDum=TRUE;

  if (file_version < 44)
    for(i=0; i<MAXNODES; i++)
      gmx_fio_do_int(fio,idum);
  gmx_fio_do_int(fio,block->nr);
  gmx_fio_do_int(fio,block->nra);
  if (bRead) {
    block->nalloc_index = block->nr+1;
    snew(block->index,block->nalloc_index);
    block->nalloc_a = block->nra;
    snew(block->a,block->nalloc_a);
  }
  bDum=gmx_fio_ndo_int(fio,block->index,block->nr+1);
  bDum=gmx_fio_ndo_int(fio,block->a,block->nra);
}

static void do_atom(t_fileio *fio, t_atom *atom,int ngrp,gmx_bool bRead, 
                    int file_version, gmx_groups_t *groups,int atnr)
{ 
  int i,myngrp;
  
  gmx_fio_do_real(fio,atom->m);
  gmx_fio_do_real(fio,atom->q);
  gmx_fio_do_real(fio,atom->mB);
  gmx_fio_do_real(fio,atom->qB);
  gmx_fio_do_ushort(fio, atom->type);
  gmx_fio_do_ushort(fio, atom->typeB);
  gmx_fio_do_int(fio,atom->ptype);
  gmx_fio_do_int(fio,atom->resind);
  if (file_version >= 52)
    gmx_fio_do_int(fio,atom->atomnumber);
  else if (bRead)
    atom->atomnumber = NOTSET;
  if (file_version < 23) 
    myngrp = 8;
  else if (file_version < 39) 
    myngrp = 9;
  else
    myngrp = ngrp;

  if (file_version < 57) {
    unsigned char uchar[egcNR];
    gmx_fio_ndo_uchar(fio,uchar,myngrp);
    for(i=myngrp; (i<ngrp); i++) {
      uchar[i] = 0;
    }
    /* Copy the old data format to the groups struct */
    for(i=0; i<ngrp; i++) {
      groups->grpnr[i][atnr] = uchar[i];
    }
  }
}

static void do_grps(t_fileio *fio, int ngrp,t_grps grps[],gmx_bool bRead, 
                    int file_version)
{
  int i,j,myngrp;
  gmx_bool bDum=TRUE;
  
  if (file_version < 23) 
    myngrp = 8;
  else if (file_version < 39) 
    myngrp = 9;
  else
    myngrp = ngrp;

  for(j=0; (j<ngrp); j++) {
    if (j<myngrp) {
      gmx_fio_do_int(fio,grps[j].nr);
      if (bRead)
	snew(grps[j].nm_ind,grps[j].nr);
      bDum=gmx_fio_ndo_int(fio,grps[j].nm_ind,grps[j].nr);
    }
    else {
      grps[j].nr = 1;
      snew(grps[j].nm_ind,grps[j].nr);
    }
  }
}

static void do_symstr(t_fileio *fio, char ***nm,gmx_bool bRead,t_symtab *symtab)
{
  int ls;
  
  if (bRead) {
    gmx_fio_do_int(fio,ls);
    *nm = get_symtab_handle(symtab,ls);
  }
  else {
    ls = lookup_symtab(symtab,*nm);
    gmx_fio_do_int(fio,ls);
  }
}

static void do_strstr(t_fileio *fio, int nstr,char ***nm,gmx_bool bRead,
                      t_symtab *symtab)
{
  int  j;
  
  for (j=0; (j<nstr); j++) 
    do_symstr(fio, &(nm[j]),bRead,symtab);
}

static void do_resinfo(t_fileio *fio, int n,t_resinfo *ri,gmx_bool bRead,
                       t_symtab *symtab, int file_version)
{
  int  j;
  
  for (j=0; (j<n); j++) {
    do_symstr(fio, &(ri[j].name),bRead,symtab);
    if (file_version >= 63) {
      gmx_fio_do_int(fio,ri[j].nr);
      gmx_fio_do_uchar(fio, ri[j].ic);
    } else {
      ri[j].nr = j + 1;
      ri[j].ic = ' ';
    }
  }
}

static void do_atoms(t_fileio *fio, t_atoms *atoms,gmx_bool bRead,t_symtab *symtab,
		     int file_version,
		     gmx_groups_t *groups)
{
  int i;
  
  gmx_fio_do_int(fio,atoms->nr);
  gmx_fio_do_int(fio,atoms->nres);
  if (file_version < 57) {
    gmx_fio_do_int(fio,groups->ngrpname);
    for(i=0; i<egcNR; i++) {
      groups->ngrpnr[i] = atoms->nr;
      snew(groups->grpnr[i],groups->ngrpnr[i]);
    }
  }
  if (bRead) {
    snew(atoms->atom,atoms->nr);
    snew(atoms->atomname,atoms->nr);
    snew(atoms->atomtype,atoms->nr);
    snew(atoms->atomtypeB,atoms->nr);
    snew(atoms->resinfo,atoms->nres);
    if (file_version < 57) {
      snew(groups->grpname,groups->ngrpname);
    }
    atoms->pdbinfo = NULL;
  }
  for(i=0; (i<atoms->nr); i++) {
    do_atom(fio, &atoms->atom[i],egcNR,bRead, file_version,groups,i);
  }
  do_strstr(fio, atoms->nr,atoms->atomname,bRead,symtab);
  if (bRead && (file_version <= 20)) {
    for(i=0; i<atoms->nr; i++) {
      atoms->atomtype[i]  = put_symtab(symtab,"?");
      atoms->atomtypeB[i] = put_symtab(symtab,"?");
    }
  } else {
    do_strstr(fio, atoms->nr,atoms->atomtype,bRead,symtab);
    do_strstr(fio, atoms->nr,atoms->atomtypeB,bRead,symtab);
  }
  do_resinfo(fio, atoms->nres,atoms->resinfo,bRead,symtab,file_version);

  if (file_version < 57) {
    do_strstr(fio, groups->ngrpname,groups->grpname,bRead,symtab);
  
    do_grps(fio, egcNR,groups->grps,bRead,file_version);
  }
}

static void do_groups(t_fileio *fio, gmx_groups_t *groups,
		      gmx_bool bRead,t_symtab *symtab,
		      int file_version)
{
  int  g,n,i;
  gmx_bool bDum=TRUE;

  do_grps(fio, egcNR,groups->grps,bRead,file_version);
  gmx_fio_do_int(fio,groups->ngrpname);
  if (bRead) {
    snew(groups->grpname,groups->ngrpname);
  }
  do_strstr(fio, groups->ngrpname,groups->grpname,bRead,symtab);
  for(g=0; g<egcNR; g++) {
    gmx_fio_do_int(fio,groups->ngrpnr[g]);
    if (groups->ngrpnr[g] == 0) {
      if (bRead) {
	groups->grpnr[g] = NULL;
      }
    } else {
      if (bRead) {
	snew(groups->grpnr[g],groups->ngrpnr[g]);
      }
      bDum=gmx_fio_ndo_uchar(fio, groups->grpnr[g],groups->ngrpnr[g]);
    }
  }
}

static void do_atomtypes(t_fileio *fio, t_atomtypes *atomtypes,gmx_bool bRead,
			 t_symtab *symtab,int file_version)
{
  int i,j;
  gmx_bool bDum = TRUE;
  
  if (file_version > 25) {
    gmx_fio_do_int(fio,atomtypes->nr);
    j=atomtypes->nr;
    if (bRead) {
      snew(atomtypes->radius,j);
      snew(atomtypes->vol,j);
      snew(atomtypes->surftens,j);
      snew(atomtypes->atomnumber,j);
      snew(atomtypes->gb_radius,j);
      snew(atomtypes->S_hct,j);
    }
    bDum=gmx_fio_ndo_real(fio,atomtypes->radius,j);
    bDum=gmx_fio_ndo_real(fio,atomtypes->vol,j);
    bDum=gmx_fio_ndo_real(fio,atomtypes->surftens,j);
    if(file_version >= 40)
    {
        bDum=gmx_fio_ndo_int(fio,atomtypes->atomnumber,j);
    }
	if(file_version >= 60)
	{
		bDum=gmx_fio_ndo_real(fio,atomtypes->gb_radius,j);
		bDum=gmx_fio_ndo_real(fio,atomtypes->S_hct,j);
	}
  } else {
    /* File versions prior to 26 cannot do GBSA, 
     * so they dont use this structure 
     */
    atomtypes->nr = 0;
    atomtypes->radius = NULL;
    atomtypes->vol = NULL;
    atomtypes->surftens = NULL;
    atomtypes->atomnumber = NULL;
    atomtypes->gb_radius = NULL;
    atomtypes->S_hct = NULL;
  }  
}

static void do_symtab(t_fileio *fio, t_symtab *symtab,gmx_bool bRead)
{
  int i,nr;
  t_symbuf *symbuf;
  char buf[STRLEN];
  
  gmx_fio_do_int(fio,symtab->nr);
  nr     = symtab->nr;
  if (bRead) {
    snew(symtab->symbuf,1);
    symbuf = symtab->symbuf;
    symbuf->bufsize = nr;
    snew(symbuf->buf,nr);
    for (i=0; (i<nr); i++) {
      gmx_fio_do_string(fio,buf);
      symbuf->buf[i]=strdup(buf);
    }
  }
  else {
    symbuf = symtab->symbuf;
    while (symbuf!=NULL) {
      for (i=0; (i<symbuf->bufsize) && (i<nr); i++) 
	gmx_fio_do_string(fio,symbuf->buf[i]);
      nr-=i;
      symbuf=symbuf->next;
    }
    if (nr != 0)
      gmx_fatal(FARGS,"nr of symtab strings left: %d",nr);
  }
}

static void do_cmap(t_fileio *fio, gmx_cmap_t *cmap_grid, gmx_bool bRead)
{
	int i,j,ngrid,gs,nelem;
	
	gmx_fio_do_int(fio,cmap_grid->ngrid);
	gmx_fio_do_int(fio,cmap_grid->grid_spacing);
	
	ngrid = cmap_grid->ngrid;
	gs    = cmap_grid->grid_spacing;
	nelem = gs * gs;
	
	if(bRead)
	{
		snew(cmap_grid->cmapdata,ngrid);
		
		for(i=0;i<cmap_grid->ngrid;i++)
		{
			snew(cmap_grid->cmapdata[i].cmap,4*nelem);
		}
	}
	
	for(i=0;i<cmap_grid->ngrid;i++)
	{
		for(j=0;j<nelem;j++)
		{
			gmx_fio_do_real(fio,cmap_grid->cmapdata[i].cmap[j*4]);
			gmx_fio_do_real(fio,cmap_grid->cmapdata[i].cmap[j*4+1]);
			gmx_fio_do_real(fio,cmap_grid->cmapdata[i].cmap[j*4+2]);
			gmx_fio_do_real(fio,cmap_grid->cmapdata[i].cmap[j*4+3]);
		}
	}	
}


void tpx_make_chain_identifiers(t_atoms *atoms,t_block *mols)
{
    int m,a,a0,a1,r;
    char c,chainid;
    int  chainnum;
    
    /* We always assign a new chain number, but save the chain id characters 
     * for larger molecules.
     */
#define CHAIN_MIN_ATOMS 15
    
    chainnum=0;
    chainid='A';
    for(m=0; m<mols->nr; m++) 
    {
        a0=mols->index[m];
        a1=mols->index[m+1];
        if ((a1-a0 >= CHAIN_MIN_ATOMS) && (chainid <= 'Z')) 
        {
            c=chainid;
            chainid++;
        } 
        else
        {
            c=' ';
        }
        for(a=a0; a<a1; a++) 
        {
            atoms->resinfo[atoms->atom[a].resind].chainnum = chainnum;
            atoms->resinfo[atoms->atom[a].resind].chainid  = c;
        }
        chainnum++;
    }
    
    /* Blank out the chain id if there was only one chain */
    if (chainid == 'B') 
    {
        for(r=0; r<atoms->nres; r++) 
        {
            atoms->resinfo[r].chainid = ' ';
        }
    }
}
  
static void do_moltype(t_fileio *fio, gmx_moltype_t *molt,gmx_bool bRead,
                       t_symtab *symtab, int file_version,
		       gmx_groups_t *groups)
{
  int i;

  if (file_version >= 57) {
    do_symstr(fio, &(molt->name),bRead,symtab);
  }

  do_atoms(fio, &molt->atoms, bRead, symtab, file_version, groups);

  if (bRead && gmx_debug_at) {
    pr_atoms(debug,0,"atoms",&molt->atoms,TRUE);
  }
  
  if (file_version >= 57) {
    do_ilists(fio, molt->ilist,bRead,file_version);

    do_block(fio, &molt->cgs,bRead,file_version);
    if (bRead && gmx_debug_at) {
      pr_block(debug,0,"cgs",&molt->cgs,TRUE);
    }
  }

  /* This used to be in the atoms struct */
  do_blocka(fio, &molt->excls, bRead, file_version);
}

static void do_molblock(t_fileio *fio, gmx_molblock_t *molb,gmx_bool bRead,
                        int file_version)
{
  int i;

  gmx_fio_do_int(fio,molb->type);
  gmx_fio_do_int(fio,molb->nmol);
  gmx_fio_do_int(fio,molb->natoms_mol);
  /* Position restraint coordinates */
  gmx_fio_do_int(fio,molb->nposres_xA);
  if (molb->nposres_xA > 0) {
    if (bRead) {
      snew(molb->posres_xA,molb->nposres_xA);
    }
    gmx_fio_ndo_rvec(fio,molb->posres_xA,molb->nposres_xA);
  }
  gmx_fio_do_int(fio,molb->nposres_xB);
  if (molb->nposres_xB > 0) {
    if (bRead) {
      snew(molb->posres_xB,molb->nposres_xB);
    }
    gmx_fio_ndo_rvec(fio,molb->posres_xB,molb->nposres_xB);
  }

}

static t_block mtop_mols(gmx_mtop_t *mtop)
{
  int mb,m,a,mol;
  t_block mols;

  mols.nr = 0;
  for(mb=0; mb<mtop->nmolblock; mb++) {
    mols.nr += mtop->molblock[mb].nmol;
  }
  mols.nalloc_index = mols.nr + 1;
  snew(mols.index,mols.nalloc_index);

  a = 0;
  m = 0;
  mols.index[m] = a;
  for(mb=0; mb<mtop->nmolblock; mb++) {
    for(mol=0; mol<mtop->molblock[mb].nmol; mol++) {
      a += mtop->molblock[mb].natoms_mol;
      m++;
      mols.index[m] = a;
    }
  }
  
  return mols;
}

static void add_posres_molblock(gmx_mtop_t *mtop)
{
  t_ilist *il;
  int am,i,mol,a;
  gmx_bool bFE;
  gmx_molblock_t *molb;
  t_iparams *ip;

  il = &mtop->moltype[0].ilist[F_POSRES];
  if (il->nr == 0) {
    return;
  }
  am = 0;
  bFE = FALSE;
  for(i=0; i<il->nr; i+=2) {
    ip = &mtop->ffparams.iparams[il->iatoms[i]];
    am = max(am,il->iatoms[i+1]);
    if (ip->posres.pos0B[XX] != ip->posres.pos0A[XX] ||
	ip->posres.pos0B[YY] != ip->posres.pos0A[YY] ||
	ip->posres.pos0B[ZZ] != ip->posres.pos0A[ZZ]) {
      bFE = TRUE;
    }
  }
  /* Make the posres coordinate block end at a molecule end */
  mol = 0;
  while(am >= mtop->mols.index[mol+1]) {
    mol++;
  }
  molb = &mtop->molblock[0];
  molb->nposres_xA = mtop->mols.index[mol+1];
  snew(molb->posres_xA,molb->nposres_xA);
  if (bFE) {
    molb->nposres_xB = molb->nposres_xA;
    snew(molb->posres_xB,molb->nposres_xB);
  } else {
    molb->nposres_xB = 0;
  }
  for(i=0; i<il->nr; i+=2) {
    ip = &mtop->ffparams.iparams[il->iatoms[i]];
    a  = il->iatoms[i+1];
    molb->posres_xA[a][XX] = ip->posres.pos0A[XX];
    molb->posres_xA[a][YY] = ip->posres.pos0A[YY];
    molb->posres_xA[a][ZZ] = ip->posres.pos0A[ZZ];
    if (bFE) {
      molb->posres_xB[a][XX] = ip->posres.pos0B[XX];
      molb->posres_xB[a][YY] = ip->posres.pos0B[YY];
      molb->posres_xB[a][ZZ] = ip->posres.pos0B[ZZ];
    }
  }
}

static void set_disres_npair(gmx_mtop_t *mtop)
{
  int mt,i,npair;
  t_iparams *ip;
  t_ilist *il;
  t_iatom *a;

  ip = mtop->ffparams.iparams;

  for(mt=0; mt<mtop->nmoltype; mt++) {
    il = &mtop->moltype[mt].ilist[F_DISRES];
    if (il->nr > 0) {
      a = il->iatoms;
      npair = 0;
      for(i=0; i<il->nr; i+=3) {
	npair++;
	if (i+3 == il->nr || ip[a[i]].disres.label != ip[a[i+3]].disres.label) {
	  ip[a[i]].disres.npair = npair;
	  npair = 0;
	}
      }
    }
  }
}

static void do_mtop(t_fileio *fio, gmx_mtop_t *mtop,gmx_bool bRead, 
                    int file_version)
{
  int  mt,mb,i;
  t_blocka dumb;

  if (bRead)
    init_mtop(mtop);
  do_symtab(fio, &(mtop->symtab),bRead);
  if (bRead && debug) 
    pr_symtab(debug,0,"symtab",&mtop->symtab);
  
  do_symstr(fio, &(mtop->name),bRead,&(mtop->symtab));
  
  if (file_version >= 57) {
    do_ffparams(fio, &mtop->ffparams,bRead,file_version);

    gmx_fio_do_int(fio,mtop->nmoltype);
  } else {
    mtop->nmoltype = 1;
  }
  if (bRead) {
    snew(mtop->moltype,mtop->nmoltype);
    if (file_version < 57) {
      mtop->moltype[0].name = mtop->name;
    }
  }
  for(mt=0; mt<mtop->nmoltype; mt++) {
    do_moltype(fio, &mtop->moltype[mt],bRead,&mtop->symtab,file_version,
	       &mtop->groups);
  }

  if (file_version >= 57) {
    gmx_fio_do_int(fio,mtop->nmolblock);
  } else {
    mtop->nmolblock = 1;
  }
  if (bRead) {
    snew(mtop->molblock,mtop->nmolblock);
  }
  if (file_version >= 57) {
    for(mb=0; mb<mtop->nmolblock; mb++) {
      do_molblock(fio, &mtop->molblock[mb],bRead,file_version);
    }
    gmx_fio_do_int(fio,mtop->natoms);
  } else {
    mtop->molblock[0].type = 0;
    mtop->molblock[0].nmol = 1;
    mtop->molblock[0].natoms_mol = mtop->moltype[0].atoms.nr;
    mtop->molblock[0].nposres_xA = 0;
    mtop->molblock[0].nposres_xB = 0;
  }

  do_atomtypes (fio, &(mtop->atomtypes),bRead,&(mtop->symtab), file_version);
  if (bRead && debug) 
    pr_atomtypes(debug,0,"atomtypes",&mtop->atomtypes,TRUE);

  if (file_version < 57) {
    /* Debug statements are inside do_idef */    
    do_idef (fio, &mtop->ffparams,&mtop->moltype[0],bRead,file_version);
    mtop->natoms = mtop->moltype[0].atoms.nr;
  }
	
  if(file_version >= 65)
  {
      do_cmap(fio, &mtop->ffparams.cmap_grid,bRead);
  }
  else
  {
      mtop->ffparams.cmap_grid.ngrid        = 0;
      mtop->ffparams.cmap_grid.grid_spacing = 0.1;
      mtop->ffparams.cmap_grid.cmapdata     = NULL;
  }
	  
  if (file_version >= 57) {
    do_groups(fio, &mtop->groups,bRead,&(mtop->symtab),file_version);
  }

  if (file_version < 57) {
    do_block(fio, &mtop->moltype[0].cgs,bRead,file_version);
    if (bRead && gmx_debug_at) {
      pr_block(debug,0,"cgs",&mtop->moltype[0].cgs,TRUE);
    }
    do_block(fio, &mtop->mols,bRead,file_version);
    /* Add the posres coordinates to the molblock */
    add_posres_molblock(mtop);
  }
  if (bRead) {
    if (file_version >= 57) {
      mtop->mols = mtop_mols(mtop);
    }
    if (gmx_debug_at) { 
      pr_block(debug,0,"mols",&mtop->mols,TRUE);
    }
  }

  if (file_version < 51) {
    /* Here used to be the shake blocks */
    do_blocka(fio, &dumb,bRead,file_version);
    if (dumb.nr > 0)
      sfree(dumb.index);
    if (dumb.nra > 0)
      sfree(dumb.a);
  }

  if (bRead) {
    close_symtab(&(mtop->symtab));
  }
}

/* If TopOnlyOK is TRUE then we can read even future versions
 * of tpx files, provided the file_generation hasn't changed.
 * If it is FALSE, we need the inputrecord too, and bail out
 * if the file is newer than the program.
 * 
 * The version and generation if the topology (see top of this file)
 * are returned in the two last arguments.
 * 
 * If possible, we will read the inputrec even when TopOnlyOK is TRUE.
 */
static void do_tpxheader(t_fileio *fio,gmx_bool bRead,t_tpxheader *tpx, 
                         gmx_bool TopOnlyOK, int *file_version, 
                         int *file_generation)
{
  char  buf[STRLEN];
  gmx_bool  bDouble;
  int   precision;
  int   fver,fgen;
  int   idum=0;
  real  rdum=0;

  gmx_fio_checktype(fio);
  gmx_fio_setdebug(fio,bDebugMode());
  
  /* NEW! XDR tpb file */
  precision = sizeof(real);
  if (bRead) {
    gmx_fio_do_string(fio,buf);
    if (strncmp(buf,"VERSION",7))
      gmx_fatal(FARGS,"Can not read file %s,\n"
		  "             this file is from a Gromacs version which is older than 2.0\n"
		  "             Make a new one with grompp or use a gro or pdb file, if possible",
		  gmx_fio_getname(fio));
    gmx_fio_do_int(fio,precision);
    bDouble = (precision == sizeof(double));
    if ((precision != sizeof(float)) && !bDouble)
      gmx_fatal(FARGS,"Unknown precision in file %s: real is %d bytes "
		  "instead of %d or %d",
		  gmx_fio_getname(fio),precision,sizeof(float),sizeof(double));
    gmx_fio_setprecision(fio,bDouble);
    fprintf(stderr,"Reading file %s, %s (%s precision)\n",
	    gmx_fio_getname(fio),buf,bDouble ? "double" : "single");
  }
  else {
    gmx_fio_write_string(fio,GromacsVersion());
    bDouble = (precision == sizeof(double));
    gmx_fio_setprecision(fio,bDouble);
    gmx_fio_do_int(fio,precision);
    fver = tpx_version;
    fgen = tpx_generation;
  }
  
  /* Check versions! */
  gmx_fio_do_int(fio,fver);
  
  if(fver>=26)
    gmx_fio_do_int(fio,fgen);
  else
    fgen=0;
 
  if(file_version!=NULL)
    *file_version = fver;
  if(file_generation!=NULL)
    *file_generation = fgen;
   
  
  if ((fver <= tpx_incompatible_version) ||
      ((fver > tpx_version) && !TopOnlyOK) ||
      (fgen > tpx_generation))
    gmx_fatal(FARGS,"reading tpx file (%s) version %d with version %d program",
		gmx_fio_getname(fio),fver,tpx_version);
  
  do_section(fio,eitemHEADER,bRead);
  gmx_fio_do_int(fio,tpx->natoms);
  if (fver >= 28)
    gmx_fio_do_int(fio,tpx->ngtc);
  else
    tpx->ngtc = 0;
  if (fver < 62) {
    gmx_fio_do_int(fio,idum);
    gmx_fio_do_real(fio,rdum);
  }
  gmx_fio_do_real(fio,tpx->lambda);
  gmx_fio_do_int(fio,tpx->bIr);
  gmx_fio_do_int(fio,tpx->bTop);
  gmx_fio_do_int(fio,tpx->bX);
  gmx_fio_do_int(fio,tpx->bV);
  gmx_fio_do_int(fio,tpx->bF);
  gmx_fio_do_int(fio,tpx->bBox);

  if((fgen > tpx_generation)) {
    /* This can only happen if TopOnlyOK=TRUE */
    tpx->bIr=FALSE;
  }
}

static int do_tpx(t_fileio *fio, gmx_bool bRead,
		  t_inputrec *ir,t_state *state,rvec *f,gmx_mtop_t *mtop,
		  gmx_bool bXVallocated)
{
  t_tpxheader tpx;
  t_inputrec  dum_ir;
  gmx_mtop_t  dum_top;
  gmx_bool        TopOnlyOK,bDum=TRUE;
  int         file_version,file_generation;
  int         i;
  rvec        *xptr,*vptr;
  int         ePBC;
  gmx_bool        bPeriodicMols;

  if (!bRead) {
    tpx.natoms = state->natoms;
    tpx.ngtc   = state->ngtc;
    tpx.lambda = state->lambda;
    tpx.bIr  = (ir       != NULL);
    tpx.bTop = (mtop     != NULL);
    tpx.bX   = (state->x != NULL);
    tpx.bV   = (state->v != NULL);
    tpx.bF   = (f        != NULL);
    tpx.bBox = TRUE;
  }
  
  TopOnlyOK = (ir==NULL);
  
  do_tpxheader(fio,bRead,&tpx,TopOnlyOK,&file_version,&file_generation);

  if (bRead) {
    state->flags  = 0;
    state->lambda = tpx.lambda;
    /* The init_state calls initialize the Nose-Hoover xi integrals to zero */
    if (bXVallocated) {
      xptr = state->x;
      vptr = state->v;
      init_state(state,0,tpx.ngtc,0,0);  /* nose-hoover chains */ /* eventually, need to add nnhpres here? */
      state->natoms = tpx.natoms; 
      state->nalloc = tpx.natoms; 
      state->x = xptr;
      state->v = vptr;
    } else {
      init_state(state,tpx.natoms,tpx.ngtc,0,0);  /* nose-hoover chains */
    }
  }

#define do_test(fio,b,p) if (bRead && (p!=NULL) && !b) gmx_fatal(FARGS,"No %s in %s",#p,gmx_fio_getname(fio)) 

  do_test(fio,tpx.bBox,state->box);
  do_section(fio,eitemBOX,bRead);
  if (tpx.bBox) {
    gmx_fio_ndo_rvec(fio,state->box,DIM);
    if (file_version >= 51) {
      gmx_fio_ndo_rvec(fio,state->box_rel,DIM);
    } else {
      /* We initialize box_rel after reading the inputrec */
      clear_mat(state->box_rel);
    }
    if (file_version >= 28) {
      gmx_fio_ndo_rvec(fio,state->boxv,DIM);
      if (file_version < 56) {
	matrix mdum;
	gmx_fio_ndo_rvec(fio,mdum,DIM);
      }
    }
  }
  
  if (state->ngtc > 0 && file_version >= 28) {
    real *dumv;
    /*ndo_double(state->nosehoover_xi,state->ngtc,bDum);*/
    /*ndo_double(state->nosehoover_vxi,state->ngtc,bDum);*/
    /*ndo_double(state->therm_integral,state->ngtc,bDum);*/
    snew(dumv,state->ngtc);
    if (file_version < 69) {
      bDum=gmx_fio_ndo_real(fio,dumv,state->ngtc);
    }
    /* These used to be the Berendsen tcoupl_lambda's */
    bDum=gmx_fio_ndo_real(fio,dumv,state->ngtc);
    sfree(dumv);
  }

  /* Prior to tpx version 26, the inputrec was here.
   * I moved it to enable partial forward-compatibility
   * for analysis/viewer programs.
   */
  if(file_version<26) {
    do_test(fio,tpx.bIr,ir);
    do_section(fio,eitemIR,bRead);
    if (tpx.bIr) {
      if (ir) {
	do_inputrec(fio, ir,bRead,file_version,
                    mtop ? &mtop->ffparams.fudgeQQ : NULL);
	if (bRead && debug) 
	  pr_inputrec(debug,0,"inputrec",ir,FALSE);
      }
      else {
	do_inputrec(fio, &dum_ir,bRead,file_version,
                    mtop ? &mtop->ffparams.fudgeQQ :NULL);
	if (bRead && debug) 
	  pr_inputrec(debug,0,"inputrec",&dum_ir,FALSE);
	done_inputrec(&dum_ir);
      }
      
    }
  }
  
  do_test(fio,tpx.bTop,mtop);
  do_section(fio,eitemTOP,bRead);
  if (tpx.bTop) {
    if (mtop) {
      do_mtop(fio,mtop,bRead, file_version);
    } else {
      do_mtop(fio,&dum_top,bRead,file_version);
      done_mtop(&dum_top,TRUE);
    }
  }
  do_test(fio,tpx.bX,state->x);  
  do_section(fio,eitemX,bRead);
  if (tpx.bX) {
    if (bRead) {
      state->flags |= (1<<estX);
    }
    gmx_fio_ndo_rvec(fio,state->x,state->natoms);
  }
  
  do_test(fio,tpx.bV,state->v);
  do_section(fio,eitemV,bRead);
  if (tpx.bV) {
    if (bRead) {
      state->flags |= (1<<estV);
    }
    gmx_fio_ndo_rvec(fio,state->v,state->natoms);
  }

  do_test(fio,tpx.bF,f);
  do_section(fio,eitemF,bRead);
  if (tpx.bF) gmx_fio_ndo_rvec(fio,f,state->natoms);

  /* Starting with tpx version 26, we have the inputrec
   * at the end of the file, so we can ignore it 
   * if the file is never than the software (but still the
   * same generation - see comments at the top of this file.
   *
   * 
   */
  ePBC = -1;
  bPeriodicMols = FALSE;
  if (file_version >= 26) {
    do_test(fio,tpx.bIr,ir);
    do_section(fio,eitemIR,bRead);
    if (tpx.bIr) {
      if (file_version >= 53) {
	/* Removed the pbc info from do_inputrec, since we always want it */
	if (!bRead) {
	  ePBC          = ir->ePBC;
	  bPeriodicMols = ir->bPeriodicMols;
	}
	gmx_fio_do_int(fio,ePBC);
	gmx_fio_do_gmx_bool(fio,bPeriodicMols);
      }
      if (file_generation <= tpx_generation && ir) {
	do_inputrec(fio, ir,bRead,file_version,mtop ? &mtop->ffparams.fudgeQQ : NULL);
	if (bRead && debug) 
	  pr_inputrec(debug,0,"inputrec",ir,FALSE);
	if (file_version < 51)
	  set_box_rel(ir,state);
	if (file_version < 53) {
	  ePBC          = ir->ePBC;
	  bPeriodicMols = ir->bPeriodicMols;
	}
      }
      if (bRead && ir && file_version >= 53) {
	/* We need to do this after do_inputrec, since that initializes ir */
	ir->ePBC          = ePBC;
	ir->bPeriodicMols = bPeriodicMols;
      }
    }
  }

    if (bRead)
    {
        if (tpx.bIr && ir)
        {
            if (state->ngtc == 0)
            {
                /* Reading old version without tcoupl state data: set it */
                init_gtc_state(state,ir->opts.ngtc,0,ir->opts.nhchainlength);
            }
            if (tpx.bTop && mtop)
            {
                if (file_version < 57)
                {
                    if (mtop->moltype[0].ilist[F_DISRES].nr > 0)
                    {
                        ir->eDisre = edrSimple;
                    }
                    else
                    {
                        ir->eDisre = edrNone;
                    }
                }
                set_disres_npair(mtop);
            }
        }

        if (tpx.bTop && mtop)
        {
            gmx_mtop_finalize(mtop);
        }

        if (file_version >= 57)
        {
            char *env;
            int  ienv;
            env = getenv("GMX_NOCHARGEGROUPS");
            if (env != NULL)
            {
                sscanf(env,"%d",&ienv);
                fprintf(stderr,"\nFound env.var. GMX_NOCHARGEGROUPS = %d\n",
                        ienv);
                if (ienv > 0)
                {
                    fprintf(stderr,
                            "Will make single atomic charge groups in non-solvent%s\n",
                            ienv > 1 ? " and solvent" : "");
                    gmx_mtop_make_atomic_charge_groups(mtop,ienv==1);
                }
                fprintf(stderr,"\n");
            }
        }
    }

    return ePBC;
}

/************************************************************
 *
 *  The following routines are the exported ones
 *
 ************************************************************/

t_fileio *open_tpx(const char *fn,const char *mode)
{
  return gmx_fio_open(fn,mode);
}    
 
void close_tpx(t_fileio *fio)
{
  gmx_fio_close(fio);
}

void read_tpxheader(const char *fn, t_tpxheader *tpx, gmx_bool TopOnlyOK,
                    int *file_version, int *file_generation)
{
  t_fileio *fio;

  fio = open_tpx(fn,"r");
  do_tpxheader(fio,TRUE,tpx,TopOnlyOK,file_version,file_generation);
  close_tpx(fio);
}

void write_tpx_state(const char *fn,
		     t_inputrec *ir,t_state *state,gmx_mtop_t *mtop)
{
  t_fileio *fio;

  fio = open_tpx(fn,"w");
  do_tpx(fio,FALSE,ir,state,NULL,mtop,FALSE);
  close_tpx(fio);
}

void read_tpx_state(const char *fn,
		    t_inputrec *ir,t_state *state,rvec *f,gmx_mtop_t *mtop)
{
  t_fileio *fio;
	
  fio = open_tpx(fn,"r");
  do_tpx(fio,TRUE,ir,state,f,mtop,FALSE);
  close_tpx(fio);
}

int read_tpx(const char *fn,
	     t_inputrec *ir, matrix box,int *natoms,
	     rvec *x,rvec *v,rvec *f,gmx_mtop_t *mtop)
{
  t_fileio *fio;
  t_state state;
  int ePBC;

  state.x = x;
  state.v = v;
  fio = open_tpx(fn,"r");
  ePBC = do_tpx(fio,TRUE,ir,&state,f,mtop,TRUE);
  close_tpx(fio);
  *natoms = state.natoms;
  if (box) 
    copy_mat(state.box,box);
  state.x = NULL;
  state.v = NULL;
  done_state(&state);

  return ePBC;
}

int read_tpx_top(const char *fn,
		 t_inputrec *ir, matrix box,int *natoms,
		 rvec *x,rvec *v,rvec *f,t_topology *top)
{
  gmx_mtop_t mtop;
  t_topology *ltop;
  int ePBC;

  ePBC = read_tpx(fn,ir,box,natoms,x,v,f,&mtop);
  
  *top = gmx_mtop_t_to_t_topology(&mtop);

  return ePBC;
}

gmx_bool fn2bTPX(const char *file)
{
  switch (fn2ftp(file)) {
  case efTPR:
  case efTPB:
  case efTPA:
    return TRUE;
  default:
    return FALSE;
  }
}

gmx_bool read_tps_conf(const char *infile,char *title,t_topology *top,int *ePBC,
		   rvec **x,rvec **v,matrix box,gmx_bool bMass)
{
  t_tpxheader  header;
  int          natoms,i,version,generation;
  gmx_bool         bTop,bXNULL;
  gmx_mtop_t   *mtop;
  t_topology   *topconv;
  gmx_atomprop_t aps;
  
  bTop = fn2bTPX(infile);
  *ePBC = -1;
  if (bTop) {
    read_tpxheader(infile,&header,TRUE,&version,&generation);
    if (x)
      snew(*x,header.natoms);
    if (v)
      snew(*v,header.natoms);
    snew(mtop,1);
    *ePBC = read_tpx(infile,NULL,box,&natoms,
		     (x==NULL) ? NULL : *x,(v==NULL) ? NULL : *v,NULL,mtop);
    *top = gmx_mtop_t_to_t_topology(mtop);
    sfree(mtop);
    strcpy(title,*top->name);
    tpx_make_chain_identifiers(&top->atoms,&top->mols);
  }
  else {
    get_stx_coordnum(infile,&natoms);
    init_t_atoms(&top->atoms,natoms,FALSE);
    bXNULL = (x == NULL);
    snew(*x,natoms);
    if (v)
      snew(*v,natoms);
    read_stx_conf(infile,title,&top->atoms,*x,(v==NULL) ? NULL : *v,ePBC,box);
    if (bXNULL) {
      sfree(*x);
      x = NULL;
    }
    if (bMass) {
      aps = gmx_atomprop_init();
      for(i=0; (i<natoms); i++)
	if (!gmx_atomprop_query(aps,epropMass,
				*top->atoms.resinfo[top->atoms.atom[i].resind].name,
				*top->atoms.atomname[i],
				&(top->atoms.atom[i].m))) {
	  if (debug) 
	    fprintf(debug,"Can not find mass for atom %s %d %s, setting to 1\n",
		    *top->atoms.resinfo[top->atoms.atom[i].resind].name,
		    top->atoms.resinfo[top->atoms.atom[i].resind].nr,
		    *top->atoms.atomname[i]);
	}
      gmx_atomprop_destroy(aps);
    }
    top->idef.ntypes=-1;
  }

  return bTop;
}
