Merge remote-tracking branch 'gerrit/release-4-6'
authorTeemu Murtola <teemu.murtola@gmail.com>
Tue, 24 Jul 2012 04:37:37 +0000 (07:37 +0300)
committerTeemu Murtola <teemu.murtola@gmail.com>
Tue, 24 Jul 2012 04:38:17 +0000 (07:38 +0300)
Conflicts:
    src/gromacs/selection/compiler.cpp (resolved in favor of master)

Change-Id: I4d6696dbac8c2cc4dd9fdd1f3b9601bd1856dff0

19 files changed:
1  2 
CMakeLists.txt
src/gromacs/gmxlib/gmxfio.c
src/gromacs/gmxlib/libxdrf.c
src/gromacs/gmxlib/maths.c
src/gromacs/gmxlib/sfactor.c
src/gromacs/gmxlib/trxio.c
src/gromacs/gmxpreprocess/pdb2top.c
src/gromacs/legacyheaders/gmxfio.h
src/gromacs/legacyheaders/xdrf.h
src/gromacs/mdlib/domdec_setup.c
src/gromacs/mdlib/forcerec.c
src/gromacs/mdlib/mdebin.c
src/gromacs/mdlib/minimize.c
src/gromacs/mdlib/pull_rotation.c
src/gromacs/mdlib/shakef.c
src/gromacs/mdlib/shellfc.c
src/programs/mdrun/runner.c
src/tools/CMakeLists.txt
src/tools/gmx_hbond.c

diff --cc CMakeLists.txt
index da8d2c9bb3f0020ec1d5888cf903655c0f54846d,16830abb7b19655bb5f76ae800fb3590c8737e0c..8cac7ae235d926b7e3f121e10e7b7312fdcd59f8
@@@ -49,8 -45,10 +49,11 @@@ if(NOT CMAKE_BUILD_TYPE
  endif(NOT CMAKE_BUILD_TYPE)
  
  enable_language(C)
 +enable_language(CXX)
  
+ set(GMX_USE_RELATIVE_INSTALL_PATH OFF CACHE STRING "Use relative paths not absolute paths for cmake install. Has only an effect on cpack.")
+ mark_as_advanced(GMX_USE_RELATIVE_INSTALL_PATH)
  set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
  set(CPACK_PACKAGE_VENDOR "gromacs.org")
  set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Gromacs - a toolkit for high-performance molecular simulation")
index 972c1f909e28e1c43f181810443e6bdb6c0964ab,0000000000000000000000000000000000000000..f6fd787248ffa20d7d028193c3263dc9b11f5e76
mode 100644,000000..100644
--- /dev/null
@@@ -1,1165 -1,0 +1,1165 @@@
- int xtc_seek_time(t_fileio *fio, real time, int natoms)
 +/* -*- 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
 +
 +#include <ctype.h>
 +#include <stdio.h>
 +#include <errno.h>
 +#ifdef HAVE_IO_H
 +#include <io.h>
 +#endif
 +#ifdef HAVE_UNISTD_H
 +#include <unistd.h>
 +#endif
 +
 +#include "gmx_fatal.h"
 +#include "macros.h"
 +#include "smalloc.h"
 +#include "futil.h"
 +#include "filenm.h"
 +#include "string2.h"
 +#include "gmxfio.h"
 +#include "md5.h"
 +
 +#ifdef GMX_THREAD_MPI
 +#include "thread_mpi.h"
 +#endif
 +
 +#include "gmxfio_int.h"
 +
 +/* This is the new improved and thread safe version of gmxfio. */
 +
 +
 +
 +/* the list of open files is a linked list, with a dummy element at its head; 
 +       it is initialized when the first file is opened. */
 +static t_fileio *open_files = NULL;
 +
 +
 +#ifdef GMX_THREAD_MPI
 +/* this mutex locks the open_files structure so that no two threads can 
 +   modify it.        
 +
 +   For now, we use this as a coarse grained lock on all file 
 +   insertion/deletion operations because it makes avoiding deadlocks 
 +   easier, and adds almost no overhead: the only overhead is during
 +   opening and closing of files, or during global operations like
 +   iterating along all open files. All these cases should be rare
 +   during the simulation. */
 +static tMPI_Thread_mutex_t open_file_mutex=TMPI_THREAD_MUTEX_INITIALIZER;
 +#endif
 +
 +
 +/* These simple lists define the I/O type for these files */
 +static const int ftpXDR[] =
 +    { efTPR, efTRR, efEDR, efXTC, efMTX, efCPT };
 +static const int ftpASC[] =
 +    { efTPA, efGRO, efPDB };
 +static const int ftpBIN[] =
 +    { efTPB, efTRJ };
 +#ifdef HAVE_XML
 +static const int ftpXML[] =
 +    {   efXML};
 +#endif
 +
 +const char *itemstr[eitemNR] =
 +    { "[header]", "[inputrec]", "[box]", "[topology]", "[coordinates]",
 +                "[velocities]", "[forces]" };
 +
 +const char *eioNames[eioNR] =
 +    { "REAL", "INT", "GMX_STE_T", "UCHAR", "NUCHAR", "USHORT", "RVEC", "NRVEC",
 +                "IVEC", "STRING" };
 +
 +
 +
 +/* Comment strings for TPA only */
 +const char *comment_str[eitemNR] = {
 +"; The header holds information on the number of atoms etc. and on whether\n"
 +"; certain items are present in the file or not.\n"
 +"; \n"
 +";                             WARNING\n"
 +";                   DO NOT EDIT THIS FILE BY HAND\n"
 +"; The GROMACS preprocessor performs a lot of checks on your input that\n"
 +"; you ignore when editing this. Your simulation may crash because of this\n",
 +"; The inputrec holds the parameters for MD such as the number of steps,\n"
 +"; the timestep and the cut-offs.\n",
 +"; The simulation box in nm.\n",
 +"; The topology section describes the topology of the molecules\n"
 +"; i.e. bonds, angles and dihedrals etc. and also holds the force field\n"
 +"; parameters.\n", 
 +"; The atomic coordinates in nm\n",
 +"; The atomic velocities in nm/ps\n",
 +"; The forces on the atoms in nm/ps^2\n" };
 +
 +
 +
 +
 +/******************************************************************
 + *
 + * Internal functions: 
 + *
 + ******************************************************************/
 +
 +static int gmx_fio_int_flush(t_fileio* fio)
 +{
 +    int rc = 0;
 +
 +    if (fio->fp)
 +    {
 +        rc = fflush(fio->fp);
 +    }
 +    else if (fio->xdr)
 +    {
 +        rc = fflush((FILE *) fio->xdr->x_private);
 +    }
 +
 +    return rc;
 +}
 +
 +/* returns TRUE if the file type ftp is in the set set */
 +static gmx_bool in_ftpset(int ftp, int nset, const int set[])
 +{
 +    int i;
 +    gmx_bool bResult;
 +
 +    bResult = FALSE;
 +    for (i = 0; (i < nset); i++)
 +        if (ftp == set[i])
 +            bResult = TRUE;
 +
 +    return bResult;
 +}
 +
 +
 +
 +extern void gmx_fio_set_comment(t_fileio *fio, const char *comment)
 +{
 +    fio->comment=comment;
 +}
 +
 +extern void gmx_fio_unset_comment(t_fileio *fio)
 +{
 +    fio->comment=NULL;
 +}
 +
 +
 +const char *gmx_fio_dbgstr(t_fileio *fio, const char *desc, char *buf)
 +{
 +    if (!fio->bDebug)
 +    {
 +        /* set to empty string */
 +        buf[0]=0;
 +    }
 +    else
 +    {
 +        snprintf(buf, GMX_FIO_BUFLEN, "  ; %s %s", fio->comment ? fio->comment : "", desc);
 +    }
 +    return buf;
 +}
 +
 +
 +/* check the number of items given against the type */
 +void gmx_fio_check_nitem(t_fileio *fio, int eio, int nitem, const char *file, 
 +                         int line)
 +{
 +    if ((nitem != 1) && !((eio == eioNRVEC) || (eio == eioNUCHAR)))
 +        gmx_fatal(FARGS, 
 +                  "nitem (%d) may differ from 1 only for %s or %s, not   for %s"
 +                  "(%s, %d)",nitem,eioNames[eioNUCHAR],eioNames[eioNRVEC],
 +                  eioNames[eio],file,line);
 +}
 +
 +
 +/* output a data type error. */
 +void gmx_fio_fe(t_fileio *fio, int eio, const char *desc, 
 +                const char *srcfile, int line)
 +{
 +
 +    gmx_fatal(FARGS, "Trying to %s %s type %d (%s), src %s, line %d",
 +              fio->bRead ? "read" : "write",desc,eio,
 +              ((eio >= 0) && (eio < eioNR)) ? eioNames[eio] : "unknown",
 +              srcfile,line);
 +}
 +
 +
 +/* set the reader/writer functions based on the file type */
 +static void gmx_fio_set_iotype(t_fileio *fio)
 +{
 +    if (in_ftpset(fio->iFTP, asize(ftpXDR), ftpXDR))
 +    {
 +#ifdef USE_XDR    
 +        fio->iotp=&xdr_iotype;
 +#else
 +        gmx_fatal(FARGS,"Sorry, no XDR");
 +#endif
 +    }
 +    else if (in_ftpset(fio->iFTP, asize(ftpASC), ftpASC))
 +    {
 +        fio->iotp=&asc_iotype;
 +    }
 +    else if (in_ftpset(fio->iFTP, asize(ftpBIN), ftpBIN))
 +    {
 +        fio->iotp=&bin_iotype;
 +    }
 +#ifdef HAVE_XMl
 +    else if (in_ftpset(fio->iFTP,asize(ftpXML),ftpXML))
 +    {
 +        fio->iotp=&dummy_iotype;
 +    }
 +#endif
 +    else
 +        fio->iotp=&dummy_iotype;
 +}
 +
 +
 +/* lock the mutex associated with this fio. This needs to be done for every
 +   type of access to the fio's elements. */
 +void gmx_fio_lock(t_fileio *fio)
 +{
 +#ifdef GMX_THREAD_MPI
 +    tMPI_Lock_lock(&(fio->mtx));
 +#endif
 +}
 +/* unlock the mutex associated with this fio.  */
 +void gmx_fio_unlock(t_fileio *fio)
 +{
 +#ifdef GMX_THREAD_MPI
 +    tMPI_Lock_unlock(&(fio->mtx));
 +#endif
 +}
 +
 +/* make a dummy head element, assuming we locked everything. */
 +static void gmx_fio_make_dummy(void)
 +{
 +    if (!open_files)
 +    {
 +        snew(open_files,1);
 +        open_files->fp=NULL;
 +        open_files->fn=NULL;
 +        open_files->next=open_files;
 +        open_files->prev=open_files;
 +#ifdef GMX_THREAD_MPI
 +        tMPI_Lock_init(&(open_files->mtx));
 +#endif
 +    }
 +}
 +
 +
 +
 +
 +
 +
 +
 +/***********************************************************************
 + *
 + * FILE LIST OPERATIONS
 + *
 +***********************************************************************/
 +
 +
 +/* insert a new t_fileio into the list */
 +static void gmx_fio_insert(t_fileio *fio)
 +{
 +    t_fileio *prev;
 +#ifdef GMX_THREAD_MPI
 +    /* first lock the big open_files mutex. */
 +    tMPI_Thread_mutex_lock(&open_file_mutex);
 +#endif
 +    /* now check whether the dummy element has been allocated, 
 +       and allocate it if it hasn't */
 +    gmx_fio_make_dummy();
 +
 +    /* and lock the fio we got and the list's head **/
 +    gmx_fio_lock(fio);
 +    gmx_fio_lock(open_files);
 +    prev=open_files->prev;
 +    /* lock the element after the current one */
 +    if (prev != open_files)
 +    {
 +        gmx_fio_lock(prev);
 +    }
 +
 +    /* now do the actual insertion: */
 +    fio->next=open_files;
 +    open_files->prev=fio;
 +    prev->next=fio;
 +    fio->prev=prev;
 +
 +    /* now unlock all our locks */
 +    if (prev != open_files)
 +    {
 +        gmx_fio_unlock(prev);
 +    }
 +    gmx_fio_unlock(open_files);
 +    gmx_fio_unlock(fio);
 +
 +#ifdef GMX_THREAD_MPI
 +    /* now unlock the big open_files mutex.  */
 +    tMPI_Thread_mutex_unlock(&open_file_mutex);
 +#endif
 +}
 +
 +/* remove a t_fileio into the list. We assume the fio is locked, and we leave 
 +   it locked. 
 +   NOTE: We also assume that the open_file_mutex has been locked */
 +static void gmx_fio_remove(t_fileio *fio)
 +{    
 +    t_fileio *prev;
 +
 +    /* lock prev, because we're changing it */ 
 +    gmx_fio_lock(fio->prev);
 +
 +    /* now set the prev's pointer */
 +    fio->prev->next=fio->next;
 +    gmx_fio_unlock(fio->prev);
 +
 +    /* with the next ptr, we can simply lock while the original was locked */
 +    gmx_fio_lock(fio->next);
 +    fio->next->prev=fio->prev;
 +    gmx_fio_unlock(fio->next);
 +
 +    /* and make sure we point nowhere in particular */
 +    fio->next=fio->prev=fio;
 +}
 +
 +
 +/* get the first open file, or NULL if there is none. 
 +   Returns a locked fio. */
 +static t_fileio *gmx_fio_get_first(void)
 +{
 +    t_fileio *ret;
 +    /* first lock the big open_files mutex and the dummy's mutex */
 +
 +#ifdef GMX_THREAD_MPI
 +    /* first lock the big open_files mutex. */
 +    tMPI_Thread_mutex_lock(&open_file_mutex);
 +#endif
 +    gmx_fio_make_dummy();
 +
 +    gmx_fio_lock(open_files);
 +    ret=open_files->next;
 +
 +
 +    /* check whether there were any to begin with */
 +    if (ret==open_files)
 +    {
 +        /* after this, the open_file pointer should never change */
 +        ret=NULL;
 +    }
 +    else
 +    {
 +        gmx_fio_lock(open_files->next);
 +    }
 +    gmx_fio_unlock(open_files);
 +
 +
 +    return ret;
 +}
 +
 +/* get the next open file, or NULL if there is none. 
 +   Unlocks the previous fio and locks the next one. */
 +static t_fileio *gmx_fio_get_next(t_fileio *fio)
 +{
 +    t_fileio *ret;
 +
 +    ret=fio->next;
 +    /* check if that was the last one */
 +    if (fio->next==open_files)
 +    {
 +        ret=NULL;
 +#ifdef GMX_THREAD_MPI
 +        tMPI_Thread_mutex_unlock(&open_file_mutex);
 +#endif
 +    }
 +    else
 +    {
 +        gmx_fio_lock(ret);
 +    }
 +    gmx_fio_unlock(fio);
 +
 +    return ret;
 +}
 +
 +/* Stop looping through the open_files.  Unlocks the global lock. */
 +static void gmx_fio_stop_getting_next(t_fileio *fio)
 +{
 +    gmx_fio_unlock(fio);
 +#ifdef GMX_THREAD_MPI
 +    tMPI_Thread_mutex_unlock(&open_file_mutex);
 +#endif
 +}
 +
 +
 +
 +
 +/*****************************************************************
 + *
 + *                     EXPORTED SECTION
 + *
 + *****************************************************************/
 +t_fileio *gmx_fio_open(const char *fn, const char *mode)
 +{
 +    t_fileio *fio = NULL;
 +    int i;
 +    char newmode[5];
 +    gmx_bool bRead, bReadWrite;
 +    int xdrid;
 +
 +    if (fn2ftp(fn) == efTPA)
 +    {
 +        strcpy(newmode, mode);
 +    }
 +    else
 +    {
 +        /* sanitize the mode string */
 +        if (strncmp(mode, "r+", 2) == 0)
 +        {
 +            strcpy(newmode, "r+");
 +        }
 +        else if (mode[0] == 'r')
 +        {
 +            strcpy(newmode, "r");
 +        }
 +        else if (strncmp(mode, "w+", 2) == 0)
 +        {
 +            strcpy(newmode, "w+");
 +        }
 +        else if (mode[0] == 'w')
 +        {
 +            strcpy(newmode, "w");
 +        }
 +        else if (strncmp(mode, "a+", 2) == 0)
 +        {
 +            strcpy(newmode, "a+");
 +        }
 +        else if (mode[0] == 'a')
 +        {
 +            strcpy(newmode, "a");
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS, "DEATH HORROR in gmx_fio_open, mode is '%s'",mode);
 +        }
 +    }
 +
 +    /* Check if it should be opened as a binary file */
 +    if (strncmp(ftp2ftype(fn2ftp(fn)),"ASCII",5))
 +    {
 +        /* Not ascii, add b to file mode */
 +        if ((strchr(newmode,'b')==NULL) && (strchr(newmode,'B')==NULL))
 +        {
 +            strcat(newmode,"b");
 +        }
 +    }
 +
 +    snew(fio, 1);
 +#ifdef GMX_THREAD_MPI
 +    tMPI_Lock_init(&(fio->mtx));
 +#endif
 +    bRead = (newmode[0]=='r' && newmode[1]!='+');
 +    bReadWrite = (newmode[1]=='+');
 +    fio->fp = NULL;
 +    fio->xdr = NULL;
 +    if (fn)
 +    {
 +        fio->iFTP = fn2ftp(fn);
 +        fio->fn = strdup(fn);
 +        fio->bStdio = FALSE;
 +
 +        /* If this file type is in the list of XDR files, open it like that */
 +        if (in_ftpset(fio->iFTP,asize(ftpXDR),ftpXDR))
 +        {
 +            /* First check whether we have to make a backup,
 +             * only for writing, not for read or append.
 +             */
 +            if (newmode[0]=='w')
 +            {
 +#ifndef GMX_FAHCORE
 +                /* only make backups for normal gromacs */
 +                make_backup(fn);
 +#endif
 +            }
 +            else 
 +            {
 +                /* Check whether file exists */
 +                if (!gmx_fexist(fn))
 +                {
 +                    gmx_open(fn);
 +                }
 +            }
 +            /* Open the file */
 +            fio->fp = ffopen(fn,newmode);
 +
 +            /* determine the XDR direction */
 +            if (newmode[0] == 'w' || newmode[0]=='a')
 +            {
 +                fio->xdrmode=XDR_ENCODE;
 +            }
 +            else
 +            {
 +                fio->xdrmode=XDR_DECODE;
 +            }
 +
 +            snew(fio->xdr,1);
 +            xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
 +        }
 +        else
 +        {
 +            /* If it is not, open it as a regular file */
 +            fio->fp = ffopen(fn,newmode);
 +        }
 +
 +        /* for appending seek to end of file to make sure ftell gives correct position
 +         * important for checkpointing */
 +        if (newmode[0]=='a')
 +        {
 +            gmx_fseek(fio->fp, 0, SEEK_END);
 +        }
 +    }
 +    else
 +    {
 +        /* Use stdin/stdout for I/O */
 +        fio->iFTP   = efTPA;
 +        fio->fp     = bRead ? stdin : stdout;
 +        fio->fn     = strdup("STDIO");
 +        fio->bStdio = TRUE;
 +    }
 +    fio->bRead  = bRead;
 +    fio->bReadWrite = bReadWrite;
 +    fio->bDouble= (sizeof(real) == sizeof(double));
 +    fio->bDebug = FALSE;
 +    fio->bOpen  = TRUE;
 +    fio->bLargerThan_off_t = FALSE;
 +
 +    /* set the reader/writer functions */
 +    gmx_fio_set_iotype(fio);
 +
 +    /* and now insert this file into the list of open files. */
 +    gmx_fio_insert(fio);
 +    return fio;
 +}
 +
 +static int gmx_fio_close_locked(t_fileio *fio)
 +{
 +    int rc = 0;
 +
 +    if (!fio->bOpen)
 +    {
 +        gmx_fatal(FARGS,"File %s closed twice!\n", fio->fn);
 +    }
 +
 +    if (in_ftpset(fio->iFTP, asize(ftpXDR), ftpXDR))
 +    {
 +        xdr_destroy(fio->xdr);
 +        sfree(fio->xdr);
 +    }
 +
 +    /* Don't close stdin and stdout! */
 +    if (!fio->bStdio && fio->fp!=NULL)
 +        rc = ffclose(fio->fp); /* fclose returns 0 if happy */
 +
 +    fio->bOpen = FALSE;
 +
 +    return rc;
 +}
 +
 +int gmx_fio_close(t_fileio *fio)
 +{
 +    int rc = 0;
 +
 +#ifdef GMX_THREAD_MPI
 +    /* first lock the big open_files mutex. */
 +    /* We don't want two processes operating on the list at the same time */
 +    tMPI_Thread_mutex_lock(&open_file_mutex);
 +#endif
 +
 +    gmx_fio_lock(fio);
 +    /* first remove it from the list */
 +    gmx_fio_remove(fio);
 +    rc=gmx_fio_close_locked(fio);
 +    gmx_fio_unlock(fio);
 +
 +    sfree(fio->fn);
 +    sfree(fio);
 +
 +#ifdef GMX_THREAD_MPI
 +    tMPI_Thread_mutex_unlock(&open_file_mutex);
 +#endif
 +
 +    return rc;
 +}
 +
 +/* close only fp but keep FIO entry. */
 +int gmx_fio_fp_close(t_fileio *fio)
 +{
 +    int rc=0;
 +    gmx_fio_lock(fio);
 +    if (!in_ftpset(fio->iFTP,asize(ftpXDR),ftpXDR) && !fio->bStdio)
 +    {
 +        rc = ffclose(fio->fp); /* fclose returns 0 if happy */
 +        fio->fp = NULL; 
 +    }
 +    gmx_fio_unlock(fio);
 +
 +    return rc;
 +}
 +
 +FILE * gmx_fio_fopen(const char *fn, const char *mode)
 +{
 +    FILE *fp, *ret;
 +    t_fileio *fio;
 +
 +    fio = gmx_fio_open(fn, mode);
 +    gmx_fio_lock(fio);
 +    ret = fio->fp;
 +    gmx_fio_unlock(fio);
 +
 +    return ret;
 +}
 +
 +int gmx_fio_fclose(FILE *fp)
 +{
 +    t_fileio *cur;
 +    t_fileio *found=NULL;
 +    int rc=-1;
 +
 +    cur=gmx_fio_get_first();
 +    while(cur)
 +    {
 +        if (cur->fp == fp)
 +        {
 +            rc=gmx_fio_close_locked(cur);
 +            gmx_fio_remove(cur);
 +            gmx_fio_stop_getting_next(cur);
 +            sfree(cur->fn);
 +            sfree(cur);
 +            break;
 +        }
 +        cur=gmx_fio_get_next(cur);
 +    }
 +
 +    return rc;
 +}
 +
 +/* internal variant of get_file_md5 that operates on a locked file */
 +static int gmx_fio_int_get_file_md5(t_fileio *fio, gmx_off_t offset, 
 +                                    unsigned char digest[])
 +{
 +    /*1MB: large size important to catch almost identical files */
 +#define CPT_CHK_LEN  1048576 
 +    md5_state_t state;
 +    unsigned char buf[CPT_CHK_LEN];
 +    gmx_off_t read_len;
 +    gmx_off_t seek_offset;
 +    int ret = -1;
 +
 +    seek_offset = offset - CPT_CHK_LEN;
 +    if (seek_offset < 0)
 +    {
 +        seek_offset = 0;
 +    }
 +    read_len = offset - seek_offset;
 +
 +
 +    if (fio->fp && fio->bReadWrite)
 +    {
 +        ret=gmx_fseek(fio->fp, seek_offset, SEEK_SET);
 +        if (ret)
 +        {
 +            gmx_fseek(fio->fp, 0, SEEK_END);
 +        }
 +    }
 +    if (ret) /*either no fp, not readwrite, or fseek not successful */
 +    {
 +        return -1;
 +    }
 +
 +    /* the read puts the file position back to offset */
 +    if ((gmx_off_t)fread(buf, 1, read_len, fio->fp) != read_len)
 +    {
 +        /* not fatal: md5sum check to prevent overwriting files
 +         * works (less safe) without
 +         * */
 +        if (ferror(fio->fp))
 +        {
 +            fprintf(stderr, "\nTrying to get md5sum: %s: %s\n", fio->fn,
 +                    strerror(errno));
 +        }
 +        else if (feof(fio->fp))
 +        {
 +            /*
 +             * For long runs that checkpoint frequently but write e.g. logs
 +             * infrequently we don't want to issue lots of warnings before we
 +             * have written anything to the log.
 +             */
 +            if(0)
 +            {
 +                fprintf(stderr, "\nTrying to get md5sum: EOF: %s\n", fio->fn);
 +            }
 +        }
 +        else
 +        {
 +            fprintf(
 +                stderr,
 +                "\nTrying to get md5sum: Unknown reason for short read: %s\n",
 +                fio->fn);
 +        }
 +
 +        gmx_fseek(fio->fp, 0, SEEK_END);
 +
 +        ret = -1;
 +    }
 +    gmx_fseek(fio->fp, 0, SEEK_END); /*is already at end, but under windows 
 +                                       it gives problems otherwise*/
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "chksum %s readlen %ld\n", fio->fn, (long int)read_len);
 +    }
 +
 +    if (!ret)
 +    {
 +        md5_init(&state);
 +        md5_append(&state, buf, read_len);
 +        md5_finish(&state, digest);
 +        return read_len;
 +    }
 +    else
 +    {
 +        return ret;
 +    }
 +}
 +
 +
 +/*
 + * fio: file to compute md5 for
 + * offset: starting pointer of region to use for md5
 + * digest: return array of md5 sum 
 + */
 +int gmx_fio_get_file_md5(t_fileio *fio, gmx_off_t offset, 
 +                         unsigned char digest[])
 +{
 +    int ret;
 +
 +    gmx_fio_lock(fio);
 +    ret=gmx_fio_int_get_file_md5(fio, offset, digest);
 +    gmx_fio_unlock(fio);
 +
 +    return ret;
 +}
 +
 +/* The fio_mutex should ALWAYS be locked when this function is called */
 +static int gmx_fio_int_get_file_position(t_fileio *fio, gmx_off_t *offset)
 +{
 +    char buf[STRLEN];
 +
 +    /* Flush the file, so we are sure it is written */
 +    if (gmx_fio_int_flush(fio))
 +    {
 +        char buf[STRLEN];
 +        sprintf(
 +            buf,
 +            "Cannot write file '%s'; maybe you are out of disk space?",
 +            fio->fn);
 +        gmx_file(buf);
 +    }
 +
 +    /* We cannot count on XDR being able to write 64-bit integers, 
 +     so separate into high/low 32-bit values.
 +     In case the filesystem has 128-bit offsets we only care 
 +     about the first 64 bits - we'll have to fix
 +     this when exabyte-size output files are common...
 +     */
 +    *offset=gmx_ftell(fio->fp);
 +
 +    return 0;
 +}
 +
 +int gmx_fio_check_file_position(t_fileio *fio)
 +{
 +    /* If gmx_off_t is 4 bytes we can not store file offset > 2 GB.
 +     * If we do not have ftello, we will play it safe.
 +     */
 +#if (SIZEOF_GMX_OFF_T == 4 || !defined HAVE_FSEEKO)
 +    gmx_off_t offset;
 +
 +    gmx_fio_lock(fio);
 +    gmx_fio_int_get_file_position(fio,&offset);
 +    /* We have a 4 byte offset,
 +     * make sure that we will detect out of range for all possible cases.
 +     */
 +    if (offset < 0 || offset > 2147483647)
 +    {
 +        fio->bLargerThan_off_t = TRUE;
 +    }
 +    gmx_fio_unlock(fio);
 +#endif
 +
 +    return 0;
 +}
 +
 +int gmx_fio_get_output_file_positions(gmx_file_position_t **p_outputfiles,
 +                                      int *p_nfiles)
 +{
 +    int i, nfiles, rc, nalloc;
 +    int pos_hi, pos_lo;
 +    long pos;
 +    gmx_file_position_t * outputfiles;
 +    char buf[STRLEN];
 +    t_fileio *cur;
 +
 +    nfiles = 0;
 +
 +    /* pre-allocate 100 files */
 +    nalloc = 100;
 +    snew(outputfiles,nalloc);
 +
 +    cur=gmx_fio_get_first();
 +    while(cur)
 +    {
 +        /* Skip the checkpoint files themselves, since they could be open when 
 +           we call this routine... */
 +        /* also skip debug files (shoud be the only iFTP==efNR) */
 +        if (cur->bOpen && 
 +            !cur->bRead && 
 +            !cur->bStdio && 
 +            cur->iFTP != efCPT && 
 +            cur->iFTP != efNR)
 +        {
 +            int ret;
 +            /* This is an output file currently open for writing, add it */
 +            if (nfiles == nalloc)
 +            {
 +                nalloc += 100;
 +                srenew(outputfiles,nalloc);
 +            }
 +
 +            strncpy(outputfiles[nfiles].filename, cur->fn, STRLEN - 1);
 +
 +            /* Get the file position */
 +            if (cur->bLargerThan_off_t)
 +            {
 +                /* -1 signals out of range */
 +                outputfiles[nfiles].offset = -1;
 +                outputfiles[nfiles].chksum_size = -1;
 +            }
 +            else
 +            {
 +                gmx_fio_int_get_file_position(cur, &outputfiles[nfiles].offset);
 +#ifndef GMX_FAHCORE
 +                outputfiles[nfiles].chksum_size
 +                    = gmx_fio_int_get_file_md5(cur, 
 +                                               outputfiles[nfiles].offset,
 +                                               outputfiles[nfiles].chksum);
 +#endif
 +            }
 +
 +            nfiles++;
 +        }
 +
 +        cur=gmx_fio_get_next(cur);
 +    }
 +    *p_nfiles = nfiles;
 +    *p_outputfiles = outputfiles;
 +
 +    return 0;
 +}
 +
 +
 +void gmx_fio_checktype(t_fileio *fio)
 +{
 +    if (in_ftpset(fio->iFTP, asize(ftpXDR), ftpXDR))
 +    {
 +        return;
 +    }
 +    else if (in_ftpset(fio->iFTP, asize(ftpASC), ftpASC))
 +    {
 +        return;
 +    }
 +    else if (in_ftpset(fio->iFTP, asize(ftpBIN), ftpBIN))
 +    {
 +        return;
 +    }
 +#ifdef HAVE_XMl
 +    else if (in_ftpset(fio->iFTP,asize(ftpXML),ftpXML))
 +    {
 +        return;
 +    }
 +#endif
 +    else
 +        gmx_fatal(FARGS, "Can not read/write topologies to file type %s",
 +                  ftp2ext(fio->iFTP));
 +
 +}
 +
 +
 +void gmx_fio_setprecision(t_fileio *fio, gmx_bool bDouble)
 +{
 +    gmx_fio_lock(fio);
 +    fio->bDouble = bDouble;
 +    gmx_fio_unlock(fio);
 +}
 +
 +gmx_bool gmx_fio_getdebug(t_fileio *fio)
 +{
 +    gmx_bool ret;
 +
 +    gmx_fio_lock(fio);
 +    ret = fio->bDebug;
 +    gmx_fio_unlock(fio);
 +
 +    return ret;
 +}
 +
 +void gmx_fio_setdebug(t_fileio *fio, gmx_bool bDebug)
 +{
 +    gmx_fio_lock(fio);
 +    fio->bDebug = bDebug;
 +    gmx_fio_unlock(fio);
 +}
 +
 +char *gmx_fio_getname(t_fileio *fio)
 +{
 +    char *ret;
 +    gmx_fio_lock(fio);
 +    ret = fio->fn;
 +    gmx_fio_unlock(fio);
 +
 +    return ret;
 +}
 +
 +int gmx_fio_getftp(t_fileio* fio)
 +{
 +    int ret;
 +
 +    gmx_fio_lock(fio);
 +    ret = fio->iFTP;
 +    gmx_fio_unlock(fio);
 +
 +    return ret;
 +}
 +
 +void gmx_fio_rewind(t_fileio* fio)
 +{
 +    gmx_fio_lock(fio);
 +
 +    if (fio->xdr)
 +    {
 +        xdr_destroy(fio->xdr);
 +        frewind(fio->fp);
 +        xdrstdio_create(fio->xdr, fio->fp, fio->xdrmode);
 +    }
 +    else
 +    {
 +        frewind(fio->fp);
 +    }
 +    gmx_fio_unlock(fio);
 +}
 +
 +
 +int gmx_fio_flush(t_fileio* fio)
 +{
 +    int ret;
 +
 +    gmx_fio_lock(fio);
 +    ret=gmx_fio_int_flush(fio);
 +    gmx_fio_unlock(fio);
 +
 +    return ret;
 +}
 +
 +
 +
 +static int gmx_fio_int_fsync(t_fileio *fio)
 +{
 +    int rc = 0;
 +    int filen=-1;
 +
 +
 +    if (fio->fp)
 +    {
 +        rc=gmx_fsync(fio->fp);
 +    }
 +    else if (fio->xdr) /* this should normally not happen */
 +    {
 +        rc=gmx_fsync((FILE*) fio->xdr->x_private);
 +                                    /* ^ is this actually OK? */
 +    }
 +
 +    return rc;
 +}
 +
 +
 +int gmx_fio_fsync(t_fileio *fio)
 +{
 +    int rc;
 +
 +    gmx_fio_lock(fio);
 +    rc=gmx_fio_int_fsync(fio);
 +    gmx_fio_unlock(fio);
 +
 +    return rc;
 +}
 +
 +
 +
 +t_fileio *gmx_fio_all_output_fsync(void)
 +{
 +    t_fileio *ret=NULL;
 +    t_fileio *cur;
 +
 +    cur=gmx_fio_get_first();
 +    while(cur)
 +    {
 +        /* skip debug files (shoud be the only iFTP==efNR) */
 +        if (cur->bOpen && 
 +            !cur->bRead && 
 +            !cur->bStdio && 
 +            cur->iFTP != efNR)
 +        {
 +            /* if any of them fails, return failure code */
 +            int rc=gmx_fio_int_fsync(cur);
 +            if (rc != 0 && !ret) 
 +            {
 +                ret=cur;
 +            }
 +        }
 +        cur=gmx_fio_get_next(cur);
 +    }
 +
 +    /* in addition, we force these to be written out too, if they're being
 +       redirected. We don't check for errors because errors most likely mean
 +       that they're not redirected. */
 +    fflush(stdout);
 +    fflush(stderr);
 +#if (defined(HAVE_FSYNC))
 +    /* again, fahcore defines HAVE_FSYNC and fsync() */
 +    fsync(STDOUT_FILENO);
 +    fsync(STDERR_FILENO);
 +#endif
 +
 +    return ret;
 +}
 +
 +
 +gmx_off_t gmx_fio_ftell(t_fileio* fio)
 +{
 +    gmx_off_t ret = 0;
 +
 +    gmx_fio_lock(fio);
 +    if (fio->fp)
 +        ret = gmx_ftell(fio->fp);
 +    gmx_fio_unlock(fio);
 +    return ret;
 +}
 +
 +int gmx_fio_seek(t_fileio* fio, gmx_off_t fpos)
 +{
 +    int rc;
 +
 +    gmx_fio_lock(fio);
 +    if (fio->fp)
 +    {
 +        rc = gmx_fseek(fio->fp, fpos, SEEK_SET);
 +    }
 +    else
 +    {
 +        gmx_file(fio->fn);
 +        rc = -1;
 +    }
 +    gmx_fio_unlock(fio);
 +    return rc;
 +}
 +
 +FILE *gmx_fio_getfp(t_fileio *fio)
 +{
 +    FILE *ret = NULL;
 +
 +    gmx_fio_lock(fio);
 +    if (fio->fp)
 +        ret = fio->fp;
 +    gmx_fio_unlock(fio);
 +    return ret;
 +}
 +
 +XDR *gmx_fio_getxdr(t_fileio* fio)
 +{
 +    XDR *ret = NULL;
 +
 +    gmx_fio_lock(fio);
 +    if (fio->xdr)
 +        ret = fio->xdr;
 +    gmx_fio_unlock(fio);
 +
 +    return ret;
 +}
 +
 +gmx_bool gmx_fio_getread(t_fileio* fio)
 +{
 +    gmx_bool ret;
 +
 +    gmx_fio_lock(fio);
 +    ret = fio->bRead;
 +    gmx_fio_unlock(fio);
 +
 +    return ret;
 +}
 +
 +int xtc_seek_frame(t_fileio *fio, int frame, int natoms)
 +{
 +    int ret;
 +
 +    gmx_fio_lock(fio);
 +    ret=xdr_xtc_seek_frame(frame, fio->fp, fio->xdr, natoms);
 +    gmx_fio_unlock(fio);
 +
 +    return ret;
 +}
 +
-     ret=xdr_xtc_seek_time(time, fio->fp, fio->xdr, natoms);
++int xtc_seek_time(t_fileio *fio, real time, int natoms,gmx_bool bSeekForwardOnly)
 +{
 +    int ret;
 +
 +    gmx_fio_lock(fio);
++    ret=xdr_xtc_seek_time(time, fio->fp, fio->xdr, natoms, bSeekForwardOnly);
 +    gmx_fio_unlock(fio);
 +
 +    return ret;
 +}
 +
 +
Simple merge
Simple merge
Simple merge
Simple merge
index 3e2a2a5c4c7933983cbc07a4bc167ab89e404ce6,0000000000000000000000000000000000000000..ed59e5e7328920c86f8a608311301790c9d4922c
mode 100644,000000..100644
--- /dev/null
@@@ -1,1579 -1,0 +1,1579 @@@
-         gmx_fatal(FARGS,"There is a dangling bond at at least one of the terminal ends and the force field does not provide terminal entries or files. Edit a .n.tdb and/or .c.tdb file.");
 +/* -*- 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:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <stdio.h>
 +#include <math.h>
 +#include <ctype.h>
 +
 +#include "vec.h"
 +#include "copyrite.h"
 +#include "smalloc.h"
 +#include "macros.h"
 +#include "symtab.h"
 +#include "futil.h"
 +#include "statutil.h"
 +#include "gmx_fatal.h"
 +#include "pdb2top.h"
 +#include "gpp_nextnb.h"
 +#include "topdirs.h"
 +#include "toputil.h"
 +#include "h_db.h"
 +#include "pgutil.h"
 +#include "resall.h"
 +#include "topio.h"
 +#include "string2.h"
 +#include "physics.h"
 +#include "pdbio.h"
 +#include "gen_ad.h"
 +#include "filenm.h"
 +#include "index.h"
 +#include "gen_vsite.h"
 +#include "add_par.h"
 +#include "toputil.h"
 +#include "fflibutil.h"
 +#include "strdb.h"
 +
 +/* this must correspond to enum in pdb2top.h */
 +const char *hh[ehisNR]   = { "HISD", "HISE", "HISH", "HIS1" };
 +
 +static int missing_atoms(t_restp *rp, int resind,t_atoms *at, int i0, int i)
 +{
 +    int  j,k,nmiss;
 +    char *name;
 +    gmx_bool bFound, bRet;
 +    
 +    nmiss = 0;
 +    for (j=0; j<rp->natom; j++)
 +    {
 +        name=*(rp->atomname[j]);
 +        bFound=FALSE;
 +        for (k=i0; k<i; k++) 
 +        {
 +            bFound = (bFound || !gmx_strcasecmp(*(at->atomname[k]),name));
 +        }
 +        if (!bFound)
 +        {
 +            nmiss++;
 +            fprintf(stderr,"\nWARNING: "
 +                    "atom %s is missing in residue %s %d in the pdb file\n",
 +                    name,*(at->resinfo[resind].name),at->resinfo[resind].nr);
 +            if (name[0]=='H' || name[0]=='h')
 +            {
 +                fprintf(stderr,"         You might need to add atom %s to the hydrogen database of building block %s\n"
 +                        "         in the file %s.hdb (see the manual)\n",
 +                        name,*(at->resinfo[resind].rtp),rp->filebase);
 +            }
 +            fprintf(stderr,"\n");
 +        }
 +    }
 +  
 +    return nmiss;
 +}
 +
 +gmx_bool is_int(double x)
 +{
 +  const double tol = 1e-4;
 +  int   ix;
 +  
 +  if (x < 0)
 +    x=-x;
 +  ix=gmx_nint(x);
 +  
 +  return (fabs(x-ix) < tol);
 +}
 +
 +static void swap_strings(char **s,int i,int j)
 +{
 +    char *tmp;
 +
 +    tmp  = s[i];
 +    s[i] = s[j];
 +    s[j] = tmp;
 +}
 +
 +void
 +choose_ff(const char *ffsel,
 +          char *forcefield, int ff_maxlen,
 +          char *ffdir, int ffdir_maxlen)
 +{
 +    int  nff;
 +    char **ffdirs,**ffs,**ffs_dir,*ptr;
 +    int  i,j,sel,cwdsel,nfound;
 +    char buf[STRLEN],**desc;
 +    FILE *fp;
 +    char *pret;
 +    
 +    nff = fflib_search_file_in_dirend(fflib_forcefield_itp(),
 +                                      fflib_forcefield_dir_ext(),
 +                                      &ffdirs);
 +
 +    if (nff == 0)
 +    {
 +        gmx_fatal(FARGS,"No force fields found (files with name '%s' in subdirectories ending on '%s')",
 +                  fflib_forcefield_itp(),fflib_forcefield_dir_ext());
 +    }
 +
 +    /* Replace with unix path separators */
 +    if(DIR_SEPARATOR!='/')
 +    {
 +        for(i=0;i<nff;i++)
 +        {
 +            while( (ptr=strchr(ffdirs[i],DIR_SEPARATOR))!=NULL )
 +            {
 +                *ptr='/';
 +            }
 +        }
 +    }
 +    
 +    /* Store the force field names in ffs */
 +    snew(ffs,nff);
 +    snew(ffs_dir,nff);
 +    for(i=0; i<nff; i++)
 +    {
 +        /* Remove the path from the ffdir name - use our unix standard here! */
 +        ptr = strrchr(ffdirs[i],'/');
 +        if (ptr == NULL)
 +        {
 +            ffs[i] = strdup(ffdirs[i]);
 +            ffs_dir[i] = low_gmxlibfn(ffdirs[i],FALSE,FALSE);
 +            if (ffs_dir[i] == NULL)
 +            {
 +                gmx_fatal(FARGS,"Can no longer find file '%s'",ffdirs[i]);
 +            }
 +        }
 +        else
 +        {
 +            ffs[i] = strdup(ptr+1);
 +            ffs_dir[i] = strdup(ffdirs[i]);
 +        }
 +        ffs_dir[i][strlen(ffs_dir[i])-strlen(ffs[i])-1] = '\0';
 +        /* Remove the extension from the ffdir name */
 +        ffs[i][strlen(ffs[i])-strlen(fflib_forcefield_dir_ext())] = '\0';
 +    }
 +
 +    if (ffsel != NULL)
 +    {
 +        sel     = -1;
 +        cwdsel  = -1;
 +        nfound  = 0;
 +        for(i=0; i<nff; i++)
 +        {
 +            if ( strcmp(ffs[i],ffsel)==0 )
 +            {
 +                /* Matching ff name */
 +                sel = i;
 +                nfound++;
 +                
 +                if( strncmp(ffs_dir[i],".",1)==0 )
 +                {
 +                    cwdsel = i;
 +                }
 +            }
 +        }
 +        
 +        if(cwdsel != -1)
 +        {
 +            sel = cwdsel;
 +        }
 +        
 +        if(nfound>1)
 +        {
 +            if(cwdsel!=-1)
 +            {
 +                fprintf(stderr,
 +                        "Force field '%s' occurs in %d places. pdb2gmx is using the one in the\n"
 +                        "current directory. Use interactive selection (not the -ff option) if\n"
 +                        "you would prefer a different one.\n",ffsel,nfound);
 +            }
 +            else
 +            {
 +                gmx_fatal(FARGS,
 +                          "Force field '%s' occurs in %d places, but not in the current directory.\n"
 +                          "Run without the -ff switch and select the force field interactively.",ffsel,nfound);
 +            }
 +        }
 +        else if (nfound==0)
 +        {
 +            gmx_fatal(FARGS,"Could not find force field '%s' in current directory, install tree or GMXDATA path.",ffsel);
 +        }
 +    }
 +    else if (nff > 1)
 +    {
 +        snew(desc,nff);
 +        for(i=0; (i<nff); i++)
 +        {
 +            sprintf(buf,"%s%c%s%s%c%s",
 +                    ffs_dir[i],DIR_SEPARATOR,
 +                    ffs[i],fflib_forcefield_dir_ext(),DIR_SEPARATOR,
 +                    fflib_forcefield_doc());
 +            if (gmx_fexist(buf))
 +            {
 +                /* We don't use fflib_open, because we don't want printf's */
 +                fp = ffopen(buf,"r");
 +                snew(desc[i],STRLEN);
 +                get_a_line(fp,desc[i],STRLEN);
 +                ffclose(fp);
 +            }
 +            else
 +            {
 +                desc[i] = strdup(ffs[i]);
 +            }
 +        }
 +        /* Order force fields from the same dir alphabetically
 +         * and put deprecated force fields at the end.
 +         */
 +        for(i=0; (i<nff); i++)
 +        {
 +            for(j=i+1; (j<nff); j++)
 +            {
 +                if (strcmp(ffs_dir[i],ffs_dir[j]) == 0 &&
 +                    ((desc[i][0] == '[' && desc[j][0] != '[') ||
 +                     ((desc[i][0] == '[' || desc[j][0] != '[') &&
 +                      gmx_strcasecmp(desc[i],desc[j]) > 0)))
 +                {
 +                    swap_strings(ffdirs,i,j);
 +                    swap_strings(ffs   ,i,j);
 +                    swap_strings(desc  ,i,j);
 +                }
 +            }
 +        }
 +
 +        printf("\nSelect the Force Field:\n");
 +        for(i=0; (i<nff); i++)
 +        {
 +            if (i == 0 || strcmp(ffs_dir[i-1],ffs_dir[i]) != 0)
 +            {
 +                if( strcmp(ffs_dir[i],".")==0 )
 +                {
 +                    printf("From current directory:\n");
 +                }
 +                else
 +                {
 +                    printf("From '%s':\n",ffs_dir[i]);
 +                }
 +            }
 +            printf("%2d: %s\n",i+1,desc[i]);
 +            sfree(desc[i]);
 +        }
 +        sfree(desc);
 +
 +        do
 +        {
 +            pret = fgets(buf,STRLEN,stdin);
 +            
 +            if (pret != NULL)
 +            {
 +                sscanf(buf,"%d",&sel);
 +                sel--;
 +            }
 +        }
 +        while ( pret==NULL || (sel < 0) || (sel >= nff));
 +
 +        /* Check for a current limitation of the fflib code.
 +         * It will always read from the first ff directory in the list.
 +         * This check assumes that the order of ffs matches the order
 +         * in which fflib_open searches ff library files.
 +         */
 +        for(i=0; i<sel; i++)
 +        {
 +            if (strcmp(ffs[i],ffs[sel]) == 0)
 +            {
 +                gmx_fatal(FARGS,"Can only select the first of multiple force field entries with directory name '%s%s' in the list. If you want to use the next entry, run pdb2gmx in a different directory or rename or move the force field directory present in the current working directory.",
 +                          ffs[sel],fflib_forcefield_dir_ext());
 +            }
 +        }
 +    }
 +    else
 +    {
 +        sel = 0;
 +    }
 +
 +    if (strlen(ffs[sel]) >= (size_t)ff_maxlen)
 +    {
 +        gmx_fatal(FARGS,"Length of force field name (%d) >= maxlen (%d)",
 +                  strlen(ffs[sel]),ff_maxlen);
 +    }
 +    strcpy(forcefield,ffs[sel]);
 +
 +    if (strlen(ffdirs[sel]) >= (size_t)ffdir_maxlen)
 +    {
 +        gmx_fatal(FARGS,"Length of force field dir (%d) >= maxlen (%d)",
 +                  strlen(ffdirs[sel]),ffdir_maxlen);
 +    }
 +    strcpy(ffdir,ffdirs[sel]);
 +
 +    for(i=0; (i<nff); i++)
 +    {
 +        sfree(ffdirs[i]);
 +        sfree(ffs[i]);
 +        sfree(ffs_dir[i]);
 +    }
 +    sfree(ffdirs);
 +    sfree(ffs);
 +    sfree(ffs_dir);
 +}
 +
 +void choose_watermodel(const char *wmsel,const char *ffdir,
 +                       char **watermodel)
 +{
 +    const char *fn_watermodels="watermodels.dat";
 +    char fn_list[STRLEN];
 +    FILE *fp;
 +    char buf[STRLEN];
 +    int  nwm,sel,i;
 +    char **model;
 +    char *pret;
 +
 +    if (strcmp(wmsel,"none") == 0)
 +    {
 +        *watermodel = NULL;
 +        
 +        return;
 +    }
 +    else if (strcmp(wmsel,"select") != 0)
 +    {
 +        *watermodel = strdup(wmsel);
 +
 +        return;
 +    }
 +
 +    sprintf(fn_list,"%s%c%s",ffdir,DIR_SEPARATOR,fn_watermodels);
 +    
 +    if (!fflib_fexist(fn_list))
 +    {
 +        fprintf(stderr,"No file '%s' found, will not include a water model\n",
 +                fn_watermodels);
 +        *watermodel = NULL;
 +        
 +        return;
 +    }
 +
 +    fp = fflib_open(fn_list);
 +    printf("\nSelect the Water Model:\n");
 +    nwm = 0;
 +    model = NULL;
 +    while (get_a_line(fp,buf,STRLEN))
 +    {
 +        srenew(model,nwm+1);
 +        snew(model[nwm],STRLEN);
 +        sscanf(buf,"%s%n",model[nwm],&i);
 +        if (i > 0)
 +        {
 +            ltrim(buf+i);
 +            fprintf(stderr,"%2d: %s\n",nwm+1,buf+i);
 +            nwm++;
 +        }
 +        else
 +        {
 +            sfree(model[nwm]);
 +        }
 +    }
 +    ffclose(fp);
 +    fprintf(stderr,"%2d: %s\n",nwm+1,"None");
 +
 +    do
 +    {
 +        pret = fgets(buf,STRLEN,stdin);
 +        
 +        if (pret != NULL)
 +        {
 +            sscanf(buf,"%d",&sel);
 +            sel--;
 +        }
 +    }
 +    while (pret == NULL || sel < 0 || sel > nwm);
 +
 +    if (sel == nwm)
 +    {
 +        *watermodel = NULL;
 +    }
 +    else
 +    {
 +        *watermodel = strdup(model[sel]);
 +    }
 +
 +    for(i=0; i<nwm; i++)
 +    {
 +        sfree(model[i]);
 +    }
 +    sfree(model);
 +}
 +
 +static int name2type(t_atoms *at, int **cgnr, gpp_atomtype_t atype, 
 +                     t_restp restp[], gmx_residuetype_t rt)
 +{
 +  int     i,j,prevresind,resind,i0,prevcg,cg,curcg;
 +  char    *name;
 +  gmx_bool    bProt, bNterm;
 +  double  qt;
 +  int     nmissat;
 +    
 +  nmissat = 0;
 +
 +  resind=-1;
 +  bProt=FALSE;
 +  bNterm=FALSE;
 +  i0=0;
 +  snew(*cgnr,at->nr);
 +  qt=0;
 +  prevcg=NOTSET;
 +  curcg=0;
 +  cg=-1;
 +  j=NOTSET;
 +  
 +  for(i=0; (i<at->nr); i++) {
 +    prevresind=resind;
 +    if (at->atom[i].resind != resind) {
 +      resind = at->atom[i].resind;
 +      bProt = gmx_residuetype_is_protein(rt,*(at->resinfo[resind].name));
 +      bNterm=bProt && (resind == 0);
 +      if (resind > 0) {
 +          nmissat += missing_atoms(&restp[prevresind],prevresind,at,i0,i);
 +      }
 +      i0=i;
 +    }
 +    if (at->atom[i].m == 0) {
 +      if (debug)
 +      fprintf(debug,"atom %d%s: curcg=%d, prevcg=%d, cg=%d\n",
 +              i+1,*(at->atomname[i]),curcg,prevcg,
 +              j==NOTSET ? NOTSET : restp[resind].cgnr[j]);
 +      qt=0;
 +      prevcg=cg;
 +      name=*(at->atomname[i]);
 +      j=search_jtype(&restp[resind],name,bNterm);
 +      at->atom[i].type = restp[resind].atom[j].type;
 +      at->atom[i].q    = restp[resind].atom[j].q;
 +      at->atom[i].m    = get_atomtype_massA(restp[resind].atom[j].type,
 +                                          atype);
 +      cg = restp[resind].cgnr[j];
 +      /* A charge group number -1 signals a separate charge group
 +       * for this atom.
 +       */
 +      if ( (cg == -1) || (cg != prevcg) || (resind != prevresind) ) {
 +          curcg++;
 +      }
 +    } else {
 +      if (debug)
 +      fprintf(debug,"atom %d%s: curcg=%d, qt=%g, is_int=%d\n",
 +              i+1,*(at->atomname[i]),curcg,qt,is_int(qt));
 +      cg=-1;
 +      if (is_int(qt)) {
 +      qt=0;
 +      curcg++;
 +      }
 +      qt+=at->atom[i].q;
 +    }
 +    (*cgnr)[i]=curcg;
 +    at->atom[i].typeB = at->atom[i].type;
 +    at->atom[i].qB    = at->atom[i].q;
 +    at->atom[i].mB    = at->atom[i].m;
 +  }
 +  nmissat += missing_atoms(&restp[resind],resind,at,i0,i);
 +
 +  return nmissat;
 +}
 +
 +static void print_top_heavy_H(FILE *out, real mHmult)
 +{
 +  if (mHmult == 2.0) 
 +    fprintf(out,"; Using deuterium instead of hydrogen\n\n");
 +  else if (mHmult == 4.0)
 +    fprintf(out,"#define HEAVY_H\n\n");
 +  else if (mHmult != 1.0)
 +    fprintf(stderr,"WARNING: unsupported proton mass multiplier (%g) "
 +          "in pdb2top\n",mHmult);
 +}
 +
 +void print_top_comment(FILE *out,
 +                       const char *filename,
 +                       const char *generator,
 +                       const char *ffdir,
 +                       gmx_bool bITP)
 +{
 +  char tmp[256]; 
 +  char ffdir_parent[STRLEN];
 +  char *p;
 +        
 +  nice_header(out,filename);
 +  fprintf(out,";\tThis is a %s topology file\n;\n",bITP ? "include" : "standalone");
 +  fprintf(out,";\tIt was generated using program:\n;\t%s\n;\n",
 +          (NULL == generator) ? "unknown" : generator);
 +  fprintf(out,";\tCommand line was:\n;\t%s\n;\n",command_line());
 +
 +  if(strchr(ffdir,'/')==NULL)
 +  {
 +      fprintf(out,";\tForce field was read from the standard Gromacs share directory.\n;\n\n");
 +  }
 +  else if(ffdir[0]=='.')
 +  {
 +      fprintf(out,";\tForce field was read from current directory or a relative path - path added.\n;\n\n");
 +  }
 +  else
 +  {
 +      strncpy(ffdir_parent,ffdir,STRLEN-1);
 +      ffdir_parent[STRLEN-1]='\0'; /*make sure it is 0-terminated even for long string*/
 +      p=strrchr(ffdir_parent,'/');
 +
 +      *p='\0';
 +      
 +      fprintf(out,
 +              ";\tForce field data was read from:\n"
 +              ";\t%s\n"
 +              ";\n"
 +              ";\tNote:\n"
 +              ";\tThis might be a non-standard force field location. When you use this topology, the\n"
 +              ";\tforce field must either be present in the current directory, or the location\n"
 +              ";\tspecified in the GMXLIB path variable or with the 'include' mdp file option.\n;\n\n",
 +              ffdir_parent);
 +  }
 +}
 +
 +void print_top_header(FILE *out,const char *filename, 
 +                      const char *title,gmx_bool bITP,const char *ffdir,real mHmult)
 +{
 +    const char *p;
 +    
 +    print_top_comment(out,filename,title,ffdir,bITP);
 +    
 +    print_top_heavy_H(out, mHmult);
 +    fprintf(out,"; Include forcefield parameters\n");
 +
 +    p=strrchr(ffdir,'/');        
 +    p = (ffdir[0]=='.' || p==NULL) ? ffdir : p+1;
 +
 +    fprintf(out,"#include \"%s/%s\"\n\n",p,fflib_forcefield_itp());
 +}
 +
 +static void print_top_posre(FILE *out,const char *pr)
 +{
 +  fprintf(out,"; Include Position restraint file\n");
 +  fprintf(out,"#ifdef POSRES\n");
 +  fprintf(out,"#include \"%s\"\n",pr);
 +  fprintf(out,"#endif\n\n");
 +}
 +  
 +static void print_top_water(FILE *out,const char *ffdir,const char *water)
 +{
 +  const char *p;
 +  char  buf[STRLEN];
 +    
 +  fprintf(out,"; Include water topology\n");
 +
 +  p=strrchr(ffdir,'/');        
 +  p = (ffdir[0]=='.' || p==NULL) ? ffdir : p+1;
 +  fprintf(out,"#include \"%s/%s.itp\"\n",p,water);
 +  
 +  fprintf(out,"\n");
 +  fprintf(out,"#ifdef POSRES_WATER\n");
 +  fprintf(out,"; Position restraint for each water oxygen\n");
 +  fprintf(out,"[ position_restraints ]\n");
 +  fprintf(out,";%3s %5s %9s %10s %10s\n","i","funct","fcx","fcy","fcz");
 +  fprintf(out,"%4d %4d %10g %10g %10g\n",1,1,1000.0,1000.0,1000.0);
 +  fprintf(out,"#endif\n");
 +  fprintf(out,"\n");
 +
 +  sprintf(buf,"%s/ions.itp",p);
 +
 +  if (fflib_fexist(buf))
 +  {
 +    fprintf(out,"; Include topology for ions\n");
 +    fprintf(out,"#include \"%s\"\n",buf);
 +    fprintf(out,"\n");
 +  }
 +}
 +
 +static void print_top_system(FILE *out, const char *title)
 +{
 +  fprintf(out,"[ %s ]\n",dir2str(d_system));
 +  fprintf(out,"; Name\n");
 +  fprintf(out,"%s\n\n",title[0]?title:"Protein");
 +}
 +
 +void print_top_mols(FILE *out,
 +                    const char *title, const char *ffdir, const char *water,
 +                    int nincl, char **incls, int nmol, t_mols *mols)
 +{
 +  int  i;
 +  char *incl;
 +
 +  if (nincl>0) {
 +    fprintf(out,"; Include chain topologies\n");
 +    for (i=0; (i<nincl); i++) {
 +        incl = strrchr(incls[i],DIR_SEPARATOR);
 +        if (incl == NULL) {
 +            incl = incls[i];
 +        } else {
 +            /* Remove the path from the include name */
 +            incl = incl + 1;
 +        }
 +      fprintf(out,"#include \"%s\"\n",incl);
 +    }
 +    fprintf(out,"\n");
 +  }
 +
 +    if (water)
 +    {
 +      print_top_water(out,ffdir,water);
 +    }
 +    print_top_system(out, title);
 +  
 +  if (nmol) {
 +    fprintf(out,"[ %s ]\n",dir2str(d_molecules));
 +    fprintf(out,"; %-15s %5s\n","Compound","#mols");
 +    for (i=0; (i<nmol); i++)
 +      fprintf(out,"%-15s %5d\n",mols[i].name,mols[i].nr);
 +  }
 +}
 +
 +void write_top(FILE *out, char *pr,char *molname,
 +               t_atoms *at,gmx_bool bRTPresname,
 +               int bts[],t_params plist[],t_excls excls[],
 +               gpp_atomtype_t atype,int *cgnr, int nrexcl)
 +     /* NOTE: nrexcl is not the size of *excl! */
 +{
 +  if (at && atype && cgnr) {
 +    fprintf(out,"[ %s ]\n",dir2str(d_moleculetype));
 +    fprintf(out,"; %-15s %5s\n","Name","nrexcl");
 +    fprintf(out,"%-15s %5d\n\n",molname?molname:"Protein",nrexcl);
 +    
 +    print_atoms(out, atype, at, cgnr, bRTPresname);
 +    print_bondeds(out,at->nr,d_bonds,      F_BONDS,    bts[ebtsBONDS], plist);
 +    print_bondeds(out,at->nr,d_constraints,F_CONSTR,   0,              plist);
 +    print_bondeds(out,at->nr,d_constraints,F_CONSTRNC, 0,              plist);
 +    print_bondeds(out,at->nr,d_pairs,      F_LJ14,     0,              plist);
 +    print_excl(out,at->nr,excls);
 +    print_bondeds(out,at->nr,d_angles,     F_ANGLES,   bts[ebtsANGLES],plist);
 +    print_bondeds(out,at->nr,d_dihedrals,  F_PDIHS,    bts[ebtsPDIHS], plist);
 +    print_bondeds(out,at->nr,d_dihedrals,  F_IDIHS,    bts[ebtsIDIHS], plist);
 +    print_bondeds(out,at->nr,d_cmap,       F_CMAP,     bts[ebtsCMAP],  plist);
 +    print_bondeds(out,at->nr,d_polarization,F_POLARIZATION,   0,       plist);
 +    print_bondeds(out,at->nr,d_thole_polarization,F_THOLE_POL,0,       plist);
 +    print_bondeds(out,at->nr,d_vsites2,    F_VSITE2,   0,              plist);
 +    print_bondeds(out,at->nr,d_vsites3,    F_VSITE3,   0,              plist);
 +    print_bondeds(out,at->nr,d_vsites3,    F_VSITE3FD, 0,              plist);
 +    print_bondeds(out,at->nr,d_vsites3,    F_VSITE3FAD,0,              plist);
 +    print_bondeds(out,at->nr,d_vsites3,    F_VSITE3OUT,0,              plist);
 +    print_bondeds(out,at->nr,d_vsites4,    F_VSITE4FD, 0,              plist);
 +    print_bondeds(out,at->nr,d_vsites4,    F_VSITE4FDN, 0,             plist);
 +    
 +    if (pr)
 +      print_top_posre(out,pr);
 +  }
 +}
 +
 +static atom_id search_res_atom(const char *type,int resind,
 +                   t_atoms *atoms,
 +                             const char *bondtype,gmx_bool bAllowMissing)
 +{
 +  int i;
 +
 +  for(i=0; (i<atoms->nr); i++)
 +  {
 +    if (atoms->atom[i].resind == resind)
 +    {
 +      return search_atom(type,i,atoms,bondtype,bAllowMissing);
 +    }
 +  }
 +  
 +  return NO_ATID;
 +}
 +
 +static void do_ssbonds(t_params *ps,t_atoms *atoms,
 +                     int nssbonds,t_ssbond *ssbonds,gmx_bool bAllowMissing)
 +{
 +  int     i,ri,rj;
 +  atom_id ai,aj;
 +  
 +  for(i=0; (i<nssbonds); i++) {
 +    ri = ssbonds[i].res1;
 +    rj = ssbonds[i].res2;
 +    ai = search_res_atom(ssbonds[i].a1,ri,atoms,
 +                       "special bond",bAllowMissing);
 +    aj = search_res_atom(ssbonds[i].a2,rj,atoms,
 +                       "special bond",bAllowMissing);
 +    if ((ai == NO_ATID) || (aj == NO_ATID))
 +      gmx_fatal(FARGS,"Trying to make impossible special bond (%s-%s)!",
 +                ssbonds[i].a1,ssbonds[i].a2);
 +    add_param(ps,ai,aj,NULL,NULL);
 +  }
 +}
 +
 +static void at2bonds(t_params *psb, t_hackblock *hb,
 +                     t_atoms *atoms,
 +                     rvec x[],
 +                     real long_bond_dist, real short_bond_dist,
 +                     gmx_bool bAllowMissing)
 +{
 +  int     resind,i,j,k;
 +  atom_id ai,aj;
 +  real    dist2, long_bond_dist2, short_bond_dist2;
 +  const char *ptr;
 +
 +  long_bond_dist2  = sqr(long_bond_dist);
 +  short_bond_dist2 = sqr(short_bond_dist);
 +
 +  if (debug)
 +    ptr = "bond";
 +  else
 +    ptr = "check";
 +
 +  fprintf(stderr,"Making bonds...\n");
 +  i=0;
 +  for(resind=0; (resind < atoms->nres) && (i<atoms->nr); resind++) {
 +    /* add bonds from list of bonded interactions */
 +    for(j=0; j < hb[resind].rb[ebtsBONDS].nb; j++) {
 +      /* Unfortunately we can not issue errors or warnings
 +       * for missing atoms in bonds, as the hydrogens and terminal atoms
 +       * have not been added yet.
 +       */
 +      ai=search_atom(hb[resind].rb[ebtsBONDS].b[j].AI,i,atoms,
 +                   ptr,TRUE);
 +      aj=search_atom(hb[resind].rb[ebtsBONDS].b[j].AJ,i,atoms,
 +                   ptr,TRUE);
 +      if (ai != NO_ATID && aj != NO_ATID) {
 +          dist2 = distance2(x[ai],x[aj]);
 +          if (dist2 > long_bond_dist2 )
 +          {
 +              fprintf(stderr,"Warning: Long Bond (%d-%d = %g nm)\n",
 +                      ai+1,aj+1,sqrt(dist2));
 +          }
 +          else if (dist2 < short_bond_dist2 )
 +          {
 +              fprintf(stderr,"Warning: Short Bond (%d-%d = %g nm)\n",
 +                      ai+1,aj+1,sqrt(dist2));
 +          }
 +          add_param(psb,ai,aj,NULL,hb[resind].rb[ebtsBONDS].b[j].s);
 +      }
 +    }
 +    /* add bonds from list of hacks (each added atom gets a bond) */
 +    while( (i<atoms->nr) && (atoms->atom[i].resind == resind) ) {
 +      for(j=0; j < hb[resind].nhack; j++)
 +      if ( ( hb[resind].hack[j].tp > 0 ||
 +             hb[resind].hack[j].oname==NULL ) &&
 +           strcmp(hb[resind].hack[j].AI,*(atoms->atomname[i])) == 0 ) {
 +        switch(hb[resind].hack[j].tp) {
 +        case 9:          /* COOH terminus */
 +          add_param(psb,i,i+1,NULL,NULL);     /* C-O  */
 +          add_param(psb,i,i+2,NULL,NULL);     /* C-OA */
 +          add_param(psb,i+2,i+3,NULL,NULL);   /* OA-H */
 +          break;
 +        default:
 +          for(k=0; (k<hb[resind].hack[j].nr); k++)
 +            add_param(psb,i,i+k+1,NULL,NULL);
 +        }
 +      }
 +      i++;
 +    }
 +    /* we're now at the start of the next residue */
 +  }
 +}
 +
 +static int pcompar(const void *a, const void *b)
 +{
 +  t_param *pa,*pb;
 +  int     d;
 +  pa=(t_param *)a;
 +  pb=(t_param *)b;
 +  
 +  d = pa->AI - pb->AI;
 +  if (d == 0) 
 +    d = pa->AJ - pb->AJ;
 +  if (d == 0)
 +    return strlen(pb->s) - strlen(pa->s);
 +  else
 +    return d;
 +}
 +
 +static void clean_bonds(t_params *ps)
 +{
 +  int     i,j;
 +  atom_id a;
 +  
 +  if (ps->nr > 0) {
 +    /* swap atomnumbers in bond if first larger than second: */
 +    for(i=0; (i<ps->nr); i++)
 +      if ( ps->param[i].AJ < ps->param[i].AI ) {
 +      a = ps->param[i].AI;
 +      ps->param[i].AI = ps->param[i].AJ;
 +      ps->param[i].AJ = a;
 +      }
 +    
 +    /* Sort bonds */
 +    qsort(ps->param,ps->nr,(size_t)sizeof(ps->param[0]),pcompar);
 +    
 +    /* remove doubles, keep the first one always. */
 +    j = 1;
 +    for(i=1; (i<ps->nr); i++) {
 +      if ((ps->param[i].AI != ps->param[j-1].AI) ||
 +        (ps->param[i].AJ != ps->param[j-1].AJ) ) {
 +        if (j != i) {
 +          cp_param(&(ps->param[j]),&(ps->param[i]));
 +        }
 +      j++;
 +      } 
 +    }
 +    fprintf(stderr,"Number of bonds was %d, now %d\n",ps->nr,j);
 +    ps->nr=j;
 +  }
 +  else
 +    fprintf(stderr,"No bonds\n");
 +}
 +
 +void print_sums(t_atoms *atoms, gmx_bool bSystem)
 +{
 +  double m,qtot;
 +  int    i;
 +  const char *where;
 +  
 +  if (bSystem)
 +    where=" in system";
 +  else
 +    where="";
 +  
 +  m=0;
 +  qtot=0;
 +  for(i=0; (i<atoms->nr); i++) {
 +    m+=atoms->atom[i].m;
 +    qtot+=atoms->atom[i].q;
 +  }
 +  
 +  fprintf(stderr,"Total mass%s %.3f a.m.u.\n",where,m);
 +  fprintf(stderr,"Total charge%s %.3f e\n",where,qtot);
 +}
 +
 +static void check_restp_type(const char *name,int t1,int t2)
 +{
 +    if (t1 != t2)
 +    {
 +        gmx_fatal(FARGS,"Residues in one molecule have a different '%s' type: %d and %d",name,t1,t2);
 +    }
 +}
 +
 +static void check_restp_types(t_restp *r0,t_restp *r1)
 +{
 +    int i;
 +
 +    check_restp_type("all dihedrals",r0->bAlldih,r1->bAlldih);
 +    check_restp_type("nrexcl",r0->nrexcl,r1->nrexcl);
 +    check_restp_type("HH14",r0->HH14,r1->HH14);
 +    check_restp_type("remove dihedrals",r0->bRemoveDih,r1->bRemoveDih);
 +
 +    for(i=0; i<ebtsNR; i++)
 +    {
 +        check_restp_type(btsNames[i],r0->rb[i].type,r1->rb[i].type);
 +    }
 +}
 +
 +void add_atom_to_restp(t_restp *restp,int resnr,int at_start,const t_hack *hack)
 +{
 +    char buf[STRLEN];
 +    int  k;
 +    const char *Hnum="123456";
 +
 +    /*if (debug)
 +    {
 +        fprintf(debug,"adding atom(s) %s to atom %s in res %d%s in rtp\n",
 +                hack->nname,
 +                *restp->atomname[at_start], resnr, restp->resname);
 +                }*/
 +    strcpy(buf, hack->nname);
 +    buf[strlen(buf)+1]='\0';
 +    if ( hack->nr > 1 )
 +    {
 +        buf[strlen(buf)]='-';
 +    }
 +    /* make space */
 +    restp->natom += hack->nr;
 +    srenew(restp->atom,     restp->natom);
 +    srenew(restp->atomname, restp->natom);
 +    srenew(restp->cgnr,     restp->natom);
 +    /* shift the rest */
 +    for(k=restp->natom-1; k > at_start+hack->nr; k--)
 +    {
 +        restp->atom[k] =
 +            restp->atom    [k - hack->nr];
 +        restp->atomname[k] =
 +            restp->atomname[k - hack->nr];
 +        restp->cgnr[k] =
 +            restp->cgnr    [k - hack->nr];
 +    }
 +    /* now add them */
 +    for(k=0; k < hack->nr; k++)
 +    {
 +        /* set counter in atomname */
 +        if ( hack->nr > 1 )
 +        {
 +            buf[strlen(buf)-1] = Hnum[k];
 +        }
 +        snew( restp->atomname[at_start+1+k], 1);
 +        restp->atom     [at_start+1+k] = *hack->atom;
 +        *restp->atomname[at_start+1+k] = strdup(buf);
 +        if ( hack->cgnr != NOTSET )
 +        {
 +            restp->cgnr   [at_start+1+k] = hack->cgnr;
 +        }
 +        else
 +        {
 +            restp->cgnr   [at_start+1+k] = restp->cgnr[at_start];
 +        }
 +    }
 +}
 +
 +void get_hackblocks_rtp(t_hackblock **hb, t_restp **restp, 
 +                             int nrtp, t_restp rtp[],
 +                             int nres, t_resinfo *resinfo, 
 +                             int nterpairs,
 +                             t_hackblock **ntdb, t_hackblock **ctdb,
 +                             int *rn, int *rc)
 +{
 +  int i, j, k, l;
 +  char *key;
 +  t_restp *res;
 +  char buf[STRLEN];
 +  const char *Hnum="123456";
 +  int tern,terc;
 +  gmx_bool bN,bC,bRM;
 +
 +  snew(*hb,nres);
 +  snew(*restp,nres);
 +  /* first the termini */
 +  for(i=0; i<nterpairs; i++) {
 +      if (rn[i] >= 0 && ntdb[i] != NULL) {
 +          copy_t_hackblock(ntdb[i], &(*hb)[rn[i]]);
 +      }
 +      if (rc[i] >= 0 && ctdb[i] != NULL) {
 +          merge_t_hackblock(ctdb[i], &(*hb)[rc[i]]);
 +      }
 +  }  
 +
 +  /* then the whole rtp */
 +  for(i=0; i < nres; i++) {
 +    /* Here we allow a mismatch of one character when looking for the rtp entry.
 +     * For such a mismatch there should be only one mismatching name.
 +     * This is mainly useful for small molecules such as ions.
 +     * Note that this will usually not work for protein, DNA and RNA,
 +     * since there the residue names should be listed in residuetypes.dat
 +     * and an error will have been generated earlier in the process.
 +     */
 +    key = *resinfo[i].rtp;
 +    snew(resinfo[i].rtp,1);
 +    *resinfo[i].rtp = search_rtp(key,nrtp,rtp);
 +    res = get_restp(*resinfo[i].rtp,nrtp,rtp);
 +    copy_t_restp(res, &(*restp)[i]);
 +
 +    /* Check that we do not have different bonded types in one molecule */
 +    check_restp_types(&(*restp)[0],&(*restp)[i]);
 +
 +    tern = -1;
 +    for(j=0; j<nterpairs && tern==-1; j++) {
 +        if (i == rn[j]) {
 +            tern = j;
 +        }
 +    }
 +    terc = -1;
 +    for(j=0; j<nterpairs && terc == -1; j++) {
 +        if (i == rc[j]) {
 +            terc = j;
 +        }
 +    }
 +    bRM = merge_t_bondeds(res->rb, (*hb)[i].rb,tern>=0,terc>=0);
 +
 +    if (bRM && ((tern >= 0 && ntdb[tern] == NULL) ||
 +                (terc >= 0 && ctdb[terc] == NULL))) {
-         gmx_fatal(FARGS,"There is a dangling bond at at least one of the terminal ends. Select a proper terminal entry.");
++        gmx_fatal(FARGS,"There is a dangling bond at at least one of the terminal ends and the force field does not provide terminal entries or files. Fix your terminal residues so that they match the residue database (.rtp) entries, or provide terminal database entries (.tdb).");
 +    }
 +    if (bRM && ((tern >= 0 && ntdb[tern]->nhack == 0) ||
 +                (terc >= 0 && ctdb[terc]->nhack == 0))) {
++        gmx_fatal(FARGS,"There is a dangling bond at at least one of the terminal ends. Fix your coordinate file, add a new terminal database entry (.tdb), or select the proper existing terminal entry.");
 +    }
 +  }
 +  
 +  /* now perform t_hack's on t_restp's,
 +     i.e. add's and deletes from termini database will be 
 +     added to/removed from residue topology 
 +     we'll do this on one big dirty loop, so it won't make easy reading! */
 +    for(i=0; i < nres; i++)
 +    {
 +        for(j=0; j < (*hb)[i].nhack; j++)
 +        {
 +            if ( (*hb)[i].hack[j].nr )
 +            {
 +                /* find atom in restp */
 +                for(l=0; l < (*restp)[i].natom; l++)
 +                    if ( ( (*hb)[i].hack[j].oname==NULL && 
 +                           strcmp((*hb)[i].hack[j].AI, *(*restp)[i].atomname[l])==0 ) ||
 +                         ( (*hb)[i].hack[j].oname!=NULL &&
 +                           strcmp((*hb)[i].hack[j].oname,*(*restp)[i].atomname[l])==0 ) )
 +                        break;
 +                if (l == (*restp)[i].natom)
 +                {
 +                    /* If we are doing an atom rename only, we don't need
 +                     * to generate a fatal error if the old name is not found
 +                     * in the rtp.
 +                     */
 +                    /* Deleting can happen also only on the input atoms,
 +                     * not necessarily always on the rtp entry.
 +                     */
 +                    if (!((*hb)[i].hack[j].oname != NULL &&
 +                          (*hb)[i].hack[j].nname != NULL) &&
 +                        !((*hb)[i].hack[j].oname != NULL &&
 +                          (*hb)[i].hack[j].nname == NULL))
 +                    {
 +                        gmx_fatal(FARGS,
 +                                  "atom %s not found in buiding block %d%s "
 +                                  "while combining tdb and rtp",
 +                                  (*hb)[i].hack[j].oname!=NULL ? 
 +                                  (*hb)[i].hack[j].oname : (*hb)[i].hack[j].AI, 
 +                                  i+1,*resinfo[i].rtp);
 +                    }
 +                }
 +                else
 +                {
 +                    if ( (*hb)[i].hack[j].oname==NULL ) {
 +                        /* we're adding: */
 +                        add_atom_to_restp(&(*restp)[i],resinfo[i].nr,l,
 +                                          &(*hb)[i].hack[j]);
 +                    }
 +                    else
 +                    {
 +                        /* oname != NULL */
 +                        if ( (*hb)[i].hack[j].nname==NULL ) {
 +                            /* we're deleting */
 +                            if (debug) 
 +                                fprintf(debug, "deleting atom %s from res %d%s in rtp\n",
 +                                        *(*restp)[i].atomname[l], 
 +                                        i+1,(*restp)[i].resname);
 +                            /* shift the rest */
 +                            (*restp)[i].natom--;
 +                            for(k=l; k < (*restp)[i].natom; k++) {
 +                                (*restp)[i].atom    [k] = (*restp)[i].atom    [k+1];
 +                                (*restp)[i].atomname[k] = (*restp)[i].atomname[k+1];
 +                                (*restp)[i].cgnr    [k] = (*restp)[i].cgnr    [k+1];
 +                            }
 +                            /* give back space */
 +                            srenew((*restp)[i].atom,     (*restp)[i].natom);
 +                            srenew((*restp)[i].atomname, (*restp)[i].natom);
 +                            srenew((*restp)[i].cgnr,     (*restp)[i].natom);
 +                        } else { /* nname != NULL */
 +                            /* we're replacing */
 +                            if (debug) 
 +                                fprintf(debug, "replacing atom %s by %s in res %d%s in rtp\n",
 +                                        *(*restp)[i].atomname[l], (*hb)[i].hack[j].nname, 
 +                                        i+1,(*restp)[i].resname);
 +                            snew( (*restp)[i].atomname[l], 1);
 +                            (*restp)[i].atom[l]      =       *(*hb)[i].hack[j].atom;
 +                            *(*restp)[i].atomname[l] = strdup((*hb)[i].hack[j].nname);
 +                            if ( (*hb)[i].hack[j].cgnr != NOTSET )
 +                                (*restp)[i].cgnr   [l] = (*hb)[i].hack[j].cgnr;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static gmx_bool atomname_cmp_nr(const char *anm,t_hack *hack,int *nr)
 +{
 +
 +    if (hack->nr == 1)
 +    {
 +        *nr = 0;
 +        
 +        return (gmx_strcasecmp(anm,hack->nname) == 0);
 +    }
 +    else
 +    {
 +        if (isdigit(anm[strlen(anm)-1]))
 +        {
 +            *nr = anm[strlen(anm)-1] - '0';
 +        }
 +        else
 +        {
 +            *nr = 0;
 +        }
 +        if (*nr <= 0 || *nr > hack->nr)
 +        {
 +            return FALSE;
 +        }
 +        else
 +        {
 +            return (strlen(anm) == strlen(hack->nname) + 1 &&
 +                    gmx_strncasecmp(anm,hack->nname,strlen(hack->nname)) == 0);
 +        }
 +    }
 +}
 +
 +static gmx_bool match_atomnames_with_rtp_atom(t_atoms *pdba,rvec *x,int atind,
 +                                          t_restp *rptr,t_hackblock *hbr,
 +                                          gmx_bool bVerbose)
 +{
 +    int  resnr;
 +    int  i,j,k;
 +    char *oldnm,*newnm;
 +    int  anmnr;
 +    char *start_at,buf[STRLEN];
 +    int  start_nr;
 +    gmx_bool bReplaceReplace,bFoundInAdd;
 +    gmx_bool bDeleted;
 +
 +    oldnm = *pdba->atomname[atind];
 +    resnr = pdba->resinfo[pdba->atom[atind].resind].nr;
 +
 +    bDeleted = FALSE;
 +    for(j=0; j<hbr->nhack; j++)
 +    {
 +        if (hbr->hack[j].oname != NULL && hbr->hack[j].nname != NULL &&
 +            gmx_strcasecmp(oldnm,hbr->hack[j].oname) == 0)
 +        {
 +            /* This is a replace entry. */
 +            /* Check if we are not replacing a replaced atom. */
 +            bReplaceReplace = FALSE;
 +            for(k=0; k<hbr->nhack; k++) {
 +                if (k != j &&
 +                    hbr->hack[k].oname != NULL && hbr->hack[k].nname != NULL &&
 +                    gmx_strcasecmp(hbr->hack[k].nname,hbr->hack[j].oname) == 0)
 +                {
 +                    /* The replace in hack[j] replaces an atom that
 +                     * was already replaced in hack[k], we do not want
 +                     * second or higher level replaces at this stage.
 +                     */
 +                    bReplaceReplace = TRUE;
 +                }
 +            }
 +            if (bReplaceReplace)
 +            {
 +                /* Skip this replace. */
 +                continue;
 +            }
 +
 +            /* This atom still has the old name, rename it */
 +            newnm = hbr->hack[j].nname;
 +            for(k=0; k<rptr->natom; k++)
 +            {
 +                if (gmx_strcasecmp(newnm,*rptr->atomname[k]) == 0)
 +                {
 +                    break;
 +                }
 +            }
 +            if (k == rptr->natom)
 +            {
 +                /* The new name is not present in the rtp.
 +                 * We need to apply the replace also to the rtp entry.
 +                 */
 +                
 +                /* We need to find the add hack that can add this atom
 +                 * to find out after which atom it should be added.
 +                 */
 +                bFoundInAdd = FALSE;
 +                for(k=0; k<hbr->nhack; k++)
 +                {
 +                    if (hbr->hack[k].oname == NULL &&
 +                        hbr->hack[k].nname != NULL &&
 +                        atomname_cmp_nr(newnm,&hbr->hack[k],&anmnr))
 +                    {
 +                        if (anmnr <= 1)
 +                        {
 +                            start_at = hbr->hack[k].a[0];
 +                        }
 +                        else
 +                        {
 +                            sprintf(buf,"%s%d",hbr->hack[k].nname,anmnr-1);
 +                            start_at = buf;
 +                        }
 +                        for(start_nr=0; start_nr<rptr->natom; start_nr++)
 +                        {
 +                            if (gmx_strcasecmp(start_at,(*rptr->atomname[start_nr])) == 0)
 +                            {
 +                                break;
 +                            }
 +                        }
 +                        if (start_nr == rptr->natom)
 +                        {
 +                            gmx_fatal(FARGS,"Could not find atom '%s' in residue building block '%s' to add atom '%s' to",
 +                                      start_at,rptr->resname,newnm);
 +                        }
 +                        /* We can add the atom after atom start_nr */
 +                        add_atom_to_restp(rptr,resnr,start_nr,
 +                                          &hbr->hack[j]);
 +                        
 +                        bFoundInAdd = TRUE;
 +                    }
 +                }
 +
 +                if (!bFoundInAdd)
 +                {
 +                    gmx_fatal(FARGS,"Could not find an 'add' entry for atom named '%s' corresponding to the 'replace' entry from atom name '%s' to '%s' for tdb or hdb database of residue type '%s'",
 +                              newnm,
 +                              hbr->hack[j].oname,hbr->hack[j].nname,
 +                              rptr->resname);
 +                }
 +            }
 +                
 +            if (bVerbose)
 +            {
 +                printf("Renaming atom '%s' in residue '%s' %d to '%s'\n",
 +                       oldnm,rptr->resname,resnr,newnm);
 +            }
 +            /* Rename the atom in pdba */
 +            snew(pdba->atomname[atind],1);
 +            *pdba->atomname[atind] = strdup(newnm);
 +        }
 +        else if (hbr->hack[j].oname != NULL && hbr->hack[j].nname == NULL &&
 +                 gmx_strcasecmp(oldnm,hbr->hack[j].oname) == 0)
 +        {
 +            /* This is a delete entry, check if this atom is present
 +             * in the rtp entry of this residue.
 +             */
 +            for(k=0; k<rptr->natom; k++)
 +            {
 +                if (gmx_strcasecmp(oldnm,*rptr->atomname[k]) == 0)
 +                {
 +                    break;
 +                }
 +            }
 +            if (k == rptr->natom)
 +            {
 +                /* This atom is not present in the rtp entry,
 +                 * delete is now from the input pdba.
 +                 */
 +                if (bVerbose)
 +                {
 +                    printf("Deleting atom '%s' in residue '%s' %d\n",
 +                           oldnm,rptr->resname,resnr);
 +                }
 +                /* We should free the atom name,
 +                 * but it might be used multiple times in the symtab.
 +                 * sfree(pdba->atomname[atind]);
 +                 */
 +                for(k=atind+1; k<pdba->nr; k++)
 +                {
 +                    pdba->atom[k-1]     = pdba->atom[k];
 +                    pdba->atomname[k-1] = pdba->atomname[k];
 +                    copy_rvec(x[k],x[k-1]);
 +                }
 +                pdba->nr--;
 +                bDeleted = TRUE;
 +            }
 +        }
 +    }
 +
 +    return bDeleted;
 +}
 +    
 +void match_atomnames_with_rtp(t_restp restp[],t_hackblock hb[],
 +                              t_atoms *pdba,rvec *x,
 +                              gmx_bool bVerbose)
 +{
 +    int  i,j,k;
 +    char *oldnm,*newnm;
 +    int  resnr;
 +    t_restp *rptr;
 +    t_hackblock *hbr;
 +    int  anmnr;
 +    char *start_at,buf[STRLEN];
 +    int  start_nr;
 +    gmx_bool bFoundInAdd;
 +    
 +    for(i=0; i<pdba->nr; i++)
 +    {
 +        oldnm = *pdba->atomname[i];
 +        resnr = pdba->resinfo[pdba->atom[i].resind].nr;
 +        rptr  = &restp[pdba->atom[i].resind];
 +        for(j=0; (j<rptr->natom); j++)
 +        {
 +            if (gmx_strcasecmp(oldnm,*(rptr->atomname[j])) == 0)
 +            {
 +                break;
 +            }
 +        }
 +        if (j == rptr->natom)
 +        {
 +            /* Not found yet, check if we have to rename this atom */
 +            if (match_atomnames_with_rtp_atom(pdba,x,i,
 +                                              rptr,&(hb[pdba->atom[i].resind]),
 +                                              bVerbose))
 +            {
 +                /* We deleted this atom, decrease the atom counter by 1. */
 +                i--;
 +            }
 +        }
 +    }
 +}
 +
 +#define NUM_CMAP_ATOMS 5
 +static void gen_cmap(t_params *psb, t_restp *restp, t_atoms *atoms, gmx_residuetype_t rt)
 +{
 +    int residx,i,j,k;
 +    const char *ptr;
 +    t_resinfo *resinfo = atoms->resinfo;
 +    int nres = atoms->nres;
 +    gmx_bool bAddCMAP;
 +    atom_id cmap_atomid[NUM_CMAP_ATOMS];
 +    int cmap_chainnum=-1, this_residue_index;
 +
 +      if (debug)
 +              ptr = "cmap";
 +      else
 +              ptr = "check";
 +      
 +      fprintf(stderr,"Making cmap torsions...");
 +      i=0;
 +      /* End loop at nres-1, since the very last residue does not have a +N atom, and
 +       * therefore we get a valgrind invalid 4 byte read error with atom am */
 +      for(residx=0; residx<nres-1; residx++)
 +      {
 +              /* Add CMAP terms from the list of CMAP interactions */
 +              for(j=0;j<restp[residx].rb[ebtsCMAP].nb; j++)
 +              {
 +            bAddCMAP = TRUE;
 +            /* Loop over atoms in a candidate CMAP interaction and
 +             * check that they exist, are from the same chain and are
 +             * from residues labelled as protein. */
 +            for(k = 0; k < NUM_CMAP_ATOMS && bAddCMAP; k++)
 +            {
 +                cmap_atomid[k] = search_atom(restp[residx].rb[ebtsCMAP].b[j].a[k],
 +                                             i,atoms,ptr,TRUE);
 +                bAddCMAP = bAddCMAP && (cmap_atomid[k] != NO_ATID);
 +                if (!bAddCMAP)
 +                {
 +                    /* This break is necessary, because cmap_atomid[k]
 +                     * == NO_ATID cannot be safely used as an index
 +                     * into the atom array. */
 +                    break;
 +                }
 +                this_residue_index = atoms->atom[cmap_atomid[k]].resind;
 +                if (0 == k)
 +                {
 +                    cmap_chainnum = resinfo[this_residue_index].chainnum;
 +                }
 +                else
 +                {
 +                    /* Does the residue for this atom have the same
 +                     * chain number as the residues for previous
 +                     * atoms? */
 +                    bAddCMAP = bAddCMAP &&
 +                        cmap_chainnum == resinfo[this_residue_index].chainnum;
 +                }
 +                bAddCMAP = bAddCMAP && gmx_residuetype_is_protein(rt,*(resinfo[this_residue_index].name));
 +            }
 +
 +            if(bAddCMAP)
 +            {
 +                add_cmap_param(psb,cmap_atomid[0],cmap_atomid[1],cmap_atomid[2],cmap_atomid[3],cmap_atomid[4],restp[residx].rb[ebtsCMAP].b[j].s);
 +                      }
 +              }
 +              
 +              if(residx<nres-1)
 +              {
 +                      while(atoms->atom[i].resind<residx+1)
 +                      {
 +                              i++;
 +                      }
 +              }
 +      }
 +      
 +      /* Start the next residue */
 +}
 +
 +static void 
 +scrub_charge_groups(int *cgnr, int natoms)
 +{
 +      int i;
 +      
 +      for(i=0;i<natoms;i++)
 +      {
 +              cgnr[i]=i+1;
 +      }
 +}
 +
 +
 +void pdb2top(FILE *top_file, char *posre_fn, char *molname,
 +             t_atoms *atoms, rvec **x, gpp_atomtype_t atype, t_symtab *tab,
 +             int nrtp, t_restp rtp[],
 +             t_restp *restp, t_hackblock *hb,
 +             int nterpairs,t_hackblock **ntdb, t_hackblock **ctdb,
 +             gmx_bool bAllowMissing,
 +             gmx_bool bVsites, gmx_bool bVsiteAromatics,
 +             const char *ff, const char *ffdir,
 +             real mHmult,
 +             int nssbonds, t_ssbond *ssbonds,
 +             real long_bond_dist, real short_bond_dist,
 +             gmx_bool bDeuterate, gmx_bool bChargeGroups, gmx_bool bCmap,
 +             gmx_bool bRenumRes,gmx_bool bRTPresname)
 +{
 +    /*
 +  t_hackblock *hb;
 +  t_restp  *restp;
 +    */
 +  t_params plist[F_NRE];
 +  t_excls  *excls;
 +  t_nextnb nnb;
 +  int      *cgnr;
 +  int      *vsite_type;
 +  int      i,nmissat;
 +  int      bts[ebtsNR];
 +  gmx_residuetype_t rt;
 +  
 +  init_plist(plist);
 +  gmx_residuetype_init(&rt);
 +
 +  if (debug) {
 +    print_resall(debug, atoms->nres, restp, atype);
 +    dump_hb(debug, atoms->nres, hb);
 +  }
 +  
 +  /* Make bonds */
 +  at2bonds(&(plist[F_BONDS]), hb, 
 +           atoms, *x,
 +           long_bond_dist, short_bond_dist, bAllowMissing);
 +  
 +  /* specbonds: disulphide bonds & heme-his */
 +  do_ssbonds(&(plist[F_BONDS]),
 +           atoms, nssbonds, ssbonds,
 +           bAllowMissing);
 +  
 +  nmissat = name2type(atoms, &cgnr, atype, restp, rt);
 +  if (nmissat) {
 +    if (bAllowMissing)
 +      fprintf(stderr,"There were %d missing atoms in molecule %s\n",
 +            nmissat,molname);
 +    else
 +      gmx_fatal(FARGS,"There were %d missing atoms in molecule %s, if you want to use this incomplete topology anyhow, use the option -missing",
 +                nmissat,molname);
 +  }
 +  
 +  /* Cleanup bonds (sort and rm doubles) */ 
 +  clean_bonds(&(plist[F_BONDS]));
 +  
 +  snew(vsite_type,atoms->nr);
 +  for(i=0; i<atoms->nr; i++)
 +    vsite_type[i]=NOTSET;
 +  if (bVsites) {
 +    /* determine which atoms will be vsites and add dummy masses 
 +       also renumber atom numbers in plist[0..F_NRE]! */
 +    do_vsites(nrtp, rtp, atype, atoms, tab, x, plist, 
 +              &vsite_type, &cgnr, mHmult, bVsiteAromatics, ffdir);
 +  }
 +  
 +  /* Make Angles and Dihedrals */
 +  fprintf(stderr,"Generating angles, dihedrals and pairs...\n");
 +  snew(excls,atoms->nr);
 +  init_nnb(&nnb,atoms->nr,4);
 +  gen_nnb(&nnb,plist);
 +  print_nnb(&nnb,"NNB");
 +  gen_pad(&nnb,atoms,restp[0].nrexcl,restp[0].HH14,
 +          plist,excls,hb,restp[0].bAlldih,restp[0].bRemoveDih,
 +          bAllowMissing);
 +  done_nnb(&nnb);
 +  
 +    /* Make CMAP */
 +    if (TRUE == bCmap)
 +    {
 +        gen_cmap(&(plist[F_CMAP]), restp, atoms, rt);
 +        if (plist[F_CMAP].nr > 0)
 +        {
 +            fprintf(stderr, "There are %4d cmap torsion pairs\n",
 +                    plist[F_CMAP].nr);
 +        }
 +    }
 +
 +  /* set mass of all remaining hydrogen atoms */
 +  if (mHmult != 1.0)
 +    do_h_mass(&(plist[F_BONDS]),vsite_type,atoms,mHmult,bDeuterate);
 +  sfree(vsite_type);
 +  
 +  /* Cleanup bonds (sort and rm doubles) */ 
 +  /* clean_bonds(&(plist[F_BONDS]));*/
 +   
 +  fprintf(stderr,
 +        "There are %4d dihedrals, %4d impropers, %4d angles\n"
 +        "          %4d pairs,     %4d bonds and  %4d virtual sites\n",
 +        plist[F_PDIHS].nr, plist[F_IDIHS].nr, plist[F_ANGLES].nr,
 +        plist[F_LJ14].nr, plist[F_BONDS].nr,
 +        plist[F_VSITE2].nr +
 +        plist[F_VSITE3].nr +
 +        plist[F_VSITE3FD].nr +
 +        plist[F_VSITE3FAD].nr +
 +        plist[F_VSITE3OUT].nr +
 +      plist[F_VSITE4FD].nr +
 +      plist[F_VSITE4FDN].nr );
 +  
 +  print_sums(atoms, FALSE);
 +  
 +  if (FALSE == bChargeGroups)
 +  {
 +        scrub_charge_groups(cgnr, atoms->nr);
 +  }
 +
 +    if (bRenumRes)
 +    {
 +        for(i=0; i<atoms->nres; i++) 
 +        {
 +            atoms->resinfo[i].nr = i + 1;
 +            atoms->resinfo[i].ic = ' ';
 +        }
 +    }
 +      
 +  if (top_file) {
 +    fprintf(stderr,"Writing topology\n");
 +    /* We can copy the bonded types from the first restp,
 +     * since the types have to be identical for all residues in one molecule.
 +     */
 +    for(i=0; i<ebtsNR; i++) {
 +        bts[i] = restp[0].rb[i].type;
 +    }
 +    write_top(top_file, posre_fn, molname,
 +              atoms, bRTPresname, 
 +              bts, plist, excls, atype, cgnr, restp[0].nrexcl);
 +  }
 +  
 +  /* cleaning up */
 +  free_t_hackblock(atoms->nres, &hb);
 +  free_t_restp(atoms->nres, &restp);
 +  gmx_residuetype_destroy(rt);
 +    
 +  /* we should clean up hb and restp here, but that is a *L*O*T* of work! */
 +  sfree(cgnr);
 +  for (i=0; i<F_NRE; i++)
 +    sfree(plist[i].param);
 +  for (i=0; i<atoms->nr; i++)
 +    sfree(excls[i].e);
 +  sfree(excls);
 +}
index e0db1ad32a636701c1219ab386bca9d9f7979c21,0000000000000000000000000000000000000000..a1e9c12addce9a8a77678651c1ade1b3522ba3af
mode 100644,000000..100644
--- /dev/null
@@@ -1,459 -1,0 +1,459 @@@
- int xtc_seek_time(t_fileio *fio, real time, int natoms);
 +/*
 + * 
 + *                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:
 + * Gromacs Runs On Most of All Computer Systems
 + */
 +
 +#ifndef _gmxfio_h
 +#define _gmxfio_h
 +
 +#include <stdio.h>
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "xdrf.h"
 +#include "futil.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +/* types */
 +
 +
 +/* Enumerated for different items in files */
 +enum { eitemHEADER, eitemIR, eitemBOX, 
 +       eitemTOP, eitemX, eitemV, eitemF, eitemNR };
 +       
 +/* Enumerated for data types in files */
 +enum { eioREAL, eioFLOAT, eioDOUBLE, eioINT, eioGMX_LARGE_INT,
 +       eioUCHAR, eioNUCHAR, eioUSHORT,
 +       eioRVEC, eioNRVEC, eioIVEC, eioSTRING, eioNR };
 +
 +typedef struct t_fileio t_fileio;
 +
 +extern const char *itemstr[eitemNR];
 +extern const char *comment_str[eitemNR];
 +
 +/* NOTE ABOUT THREAD SAFETY:
 +
 +   The functions are all thread-safe, provided that two threads don't 
 +   do something silly like closing the same file, or one thread 
 +   accesses a file that has been closed by another.
 +   */
 +
 +/********************************************************
 + * Open and Close 
 + ********************************************************/
 +
 +t_fileio *gmx_fio_open(const char *fn,const char *mode);
 +/* Open a new file for reading or writing.
 + * The file type will be deduced from the file name.
 + * If fn is NULL, stdin / stdout will be used for Ascii I/O (TPA type)
 + * mode may be "r", "w", or "a". You should append a "b" to the mode
 + * if you are writing a binary file, but the routine will also 
 + * doublecheck it and try to do it if you forgot. This has no effect on
 + * unix, but is important on windows.
 + */
 + 
 +int gmx_fio_close(t_fileio *fp);
 +/* Close the file corresponding to fp (if not stdio)
 + * The routine will exit when an invalid fio is handled.
 + * Returns 0 on success.
 + */
 +
 +int gmx_fio_fp_close(t_fileio *fp);
 +/* Close the file corresponding to fp without closing the FIO entry
 + * Needed e.g. for trxio because the FIO entries are used to store 
 + * additional data.
 + * NOTE that the fp still needs to be properly closed with gmx_fio_close().
 + * The routine will exit when an invalid fio is handled.
 + * Returns 0 on success.
 + */
 +
 +
 +/* Open a file, return a stream, record the entry in internal FIO object */
 +FILE* gmx_fio_fopen(const char *fn,const char *mode);
 +
 +/* Close a file previously opened with gmx_fio_fopen. 
 + * Do not mix these calls with standard fopen/fclose ones!
 + * Returns 0 on success.  */
 +int gmx_fio_fclose(FILE *fp);
 +
 +
 +
 +/********************************************************
 + * Change properties of the open file
 + ********************************************************/
 +
 +void gmx_fio_setprecision(t_fileio *fio,gmx_bool bDouble);
 +/* Select the floating point precision for reading and writing files */
 +
 +char *gmx_fio_getname(t_fileio *fio);
 +/* Return the filename corresponding to the fio index */
 +
 +int gmx_fio_getftp(t_fileio *fio);
 +/* Return the filetype corresponding to the fio index. 
 +    There is as of now no corresponding setftp function because the file
 +    was opened as a specific file type and changing that midway is most 
 +    likely an evil hack. */
 +
 +void gmx_fio_setdebug(t_fileio *fio,gmx_bool bDebug);
 +/* Set the debug mode */
 +
 +gmx_bool gmx_fio_getdebug(t_fileio *fio);
 +/* Return  whether debug mode is on in fio  */
 +
 +gmx_bool gmx_fio_getread(t_fileio *fio);
 +/* Return  whether read mode is on in fio  */
 +
 +
 +void gmx_fio_checktype(t_fileio *fio);
 +/* Check whether the fio is of a sane type */
 +
 +/***************************************************
 + * FILE Operations
 + ***************************************************/
 +
 +void gmx_fio_rewind(t_fileio *fio);
 +/* Rewind the tpa file in fio */
 +
 +int gmx_fio_flush(t_fileio *fio);
 +/* Flush the fio, returns 0 on success */
 +
 +int gmx_fio_fsync(t_fileio *fio);
 +/* fsync the fio, returns 0 on success. 
 +   NOTE: don't use fsync function unless you're absolutely sure you need it
 +   because it deliberately interferes with the OS's caching mechanisms and
 +   can cause dramatically slowed down IO performance. Some OSes (Linux, 
 +   for example), may implement fsync as a full sync() point. */
 +
 +gmx_off_t gmx_fio_ftell(t_fileio *fio);
 +/* Return file position if possible */
 +
 +int gmx_fio_seek(t_fileio *fio,gmx_off_t fpos);
 +/* Set file position if possible, quit otherwise */
 +
 +FILE *gmx_fio_getfp(t_fileio *fio);
 +/* Return the file pointer itself */
 +
 +XDR *gmx_fio_getxdr(t_fileio *fio);
 +/* Return the file pointer itself */
 +
 +
 +
 +
 +
 +/* Element with information about position in a currently open file.
 + * gmx_off_t should be defined by autoconf if your system does not have it.
 + * If you do not have it on some other platform you do not have largefile 
 + * support at all, and you can define it to int (or better, find out how to 
 + * enable large files).  */
 +typedef struct
 +{
 +      char      filename[STRLEN];
 +      gmx_off_t offset; 
 +      unsigned char chksum[16];
 +      int       chksum_size;
 +} 
 +gmx_file_position_t;
 +
 +
 +int gmx_fio_check_file_position(t_fileio *fio);
 +/* Check if the file position is out of the range of off_t.
 + * The result is stored along with the other file data of fio.
 + */
 +
 +int gmx_fio_get_output_file_positions(gmx_file_position_t ** outputfiles,
 +                                      int *nfiles );
 +/* Return the name and file pointer positions for all currently open
 + * output files. This is used for saving in the checkpoint files, so we
 + * can truncate output files upon restart-with-appending.
 + *
 + * For the first argument you should use a pointer, which will be set to
 + * point to a list of open files.
 + */
 +
 +t_fileio *gmx_fio_all_output_fsync(void);
 +/* fsync all open output files. This is used for checkpointing, where
 +   we need to ensure that all output is actually written out to 
 +   disk. 
 +   This is most important in the case of some networked file systems, 
 +   where data is not synced with the file server until close() or 
 +   fsync(), so data could remain in cache for days.
 +   Note the caveats reported with gmx_fio_fsync(). 
 +   
 +    returns: NULL if no error occurred, or a pointer to the first file that
 +             failed if an error occurred 
 +*/
 +
 +
 +int gmx_fio_get_file_md5(t_fileio *fio, gmx_off_t offset,  
 +                         unsigned char digest[]);
 +
 +
 +int xtc_seek_frame(t_fileio *fio, int frame, int natoms);
 +
++int xtc_seek_time(t_fileio *fio, real time, int natoms,gmx_bool bSeekForwardOnly);
 +
 +      
 +/* Add this to the comment string for debugging */
 +void gmx_fio_set_comment(t_fileio *fio, const char *comment);
 +
 +/* Remove previously set comment */
 +void gmx_fio_unset_comment(t_fileio *fio);
 +
 +
 +
 +
 +/********************************************************
 + * Read and write
 + ********************************************************/
 +
 +
 +/* basic reading & writing */
 +gmx_bool gmx_fio_reade_real(t_fileio *fio, real *item, 
 +                        const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_reade_float(t_fileio *fio, float *item, 
 +                          const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_reade_double(t_fileio *fio, double *item, 
 +                          const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_reade_int(t_fileio *fio, int *item, 
 +                       const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_reade_gmx_large_int(t_fileio *fio, gmx_large_int_t *item, 
 +                                 const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_reade_uchar(t_fileio *fio, unsigned char *item, 
 +                         const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_reade_ushort(t_fileio *fio, unsigned short *item, 
 +                         const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_reade_rvec(t_fileio *fio, rvec *item, 
 +                        const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_reade_ivec(t_fileio *fio, ivec *item, 
 +                        const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_reade_string(t_fileio *fio, char *item, 
 +                          const char *desc, const char *srcfile, int line);
 +
 +gmx_bool gmx_fio_writee_real(t_fileio *fio, real item, 
 +                         const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_writee_float(t_fileio *fio, float item, 
 +                           const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_writee_double(t_fileio *fio, double item, 
 +                           const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_writee_int(t_fileio *fio, int item, 
 +                        const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_writee_gmx_large_int(t_fileio *fio, gmx_large_int_t item, 
 +                                  const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_writee_uchar(t_fileio *fio, unsigned char item, 
 +                          const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_writee_ushort(t_fileio *fio, unsigned short item, 
 +                          const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_writee_rvec(t_fileio *fio, rvec *item, 
 +                         const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_writee_ivec(t_fileio *fio, ivec *item, 
 +                         const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_writee_string(t_fileio *fio, const char *item, 
 +                           const char *desc, const char *srcfile, int line);
 +
 +/* reading or writing, depending on the file's opening mode string */
 +gmx_bool gmx_fio_doe_real(t_fileio *fio, real *item, 
 +                      const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_doe_float(t_fileio *fio, float *item, 
 +                       const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_doe_double(t_fileio *fio, double *item, 
 +                        const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_doe_gmx_bool(t_fileio *fio, gmx_bool *item, 
 +                     const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_doe_int(t_fileio *fio, int *item, 
 +                     const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_doe_gmx_large_int(t_fileio *fio, gmx_large_int_t *item, 
 +                               const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_doe_uchar(t_fileio *fio, unsigned char *item, 
 +                       const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_doe_ushort(t_fileio *fio, unsigned short *item, 
 +                       const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_doe_rvec(t_fileio *fio, rvec *item, 
 +                      const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_doe_ivec(t_fileio *fio, ivec *item, 
 +                      const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_doe_string(t_fileio *fio, char *item, 
 +                        const char *desc, const char *srcfile, int line);
 +
 +
 +
 +
 +/* array reading & writing */
 +gmx_bool gmx_fio_nreade_real(t_fileio *fio, real *item, int n, 
 +                         const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nreade_float(t_fileio *fio, float *item, int n, 
 +                          const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nreade_double(t_fileio *fio, double *item, int n, 
 +                           const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nreade_int(t_fileio *fio, int *item, int n, 
 +                        const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nreade_gmx_large_int(t_fileio *fio, gmx_large_int_t *item, int n, 
 +                                  const char *desc, const char *srcfile, 
 +                                  int line);
 +gmx_bool gmx_fio_nreade_uchar(t_fileio *fio, unsigned char *item, int n, 
 +                          const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nreade_ushort(t_fileio *fio, unsigned short *item, int n, 
 +                          const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nreade_rvec(t_fileio *fio, rvec *item, int n, 
 +                         const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nreade_ivec(t_fileio *fio, ivec *item, int n, 
 +                         const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nreade_string(t_fileio *fio, char *item[], int n, 
 +                           const char *desc, const char *srcfile, int line);
 +
 +gmx_bool gmx_fio_nwritee_real(t_fileio *fio, const real *item, int n, 
 +                          const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nwritee_float(t_fileio *fio, const float *item, int n, 
 +                           const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nwritee_double(t_fileio *fio, const double *item, int n, 
 +                            const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nwritee_int(t_fileio *fio, const int *item, int n, 
 +                         const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nwritee_gmx_large_int(t_fileio *fio, 
 +                                   const gmx_large_int_t *item, int n,
 +                                   const char *desc, const char *srcfile, 
 +                                   int line);
 +gmx_bool gmx_fio_nwritee_uchar(t_fileio *fio, const unsigned char *item, int n, 
 +                           const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nwritee_ushort(t_fileio *fio, const unsigned short *item, int n, 
 +                           const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nwritee_rvec(t_fileio *fio, const rvec *item, int n, 
 +                          const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nwritee_ivec(t_fileio *fio, const ivec *item, int n, 
 +                          const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_nwritee_string(t_fileio *fio, const char *item[], int n, 
 +                            const char *desc, const char *srcfile, int line);
 +
 +gmx_bool gmx_fio_ndoe_real(t_fileio *fio, real *item, int n, 
 +                       const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_ndoe_float(t_fileio *fio, float *item, int n, 
 +                         const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_ndoe_double(t_fileio *fio, double *item, int n, 
 +                         const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_ndoe_gmx_bool(t_fileio *fio, gmx_bool *item, int n, 
 +                      const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_ndoe_int(t_fileio *fio, int *item, int n, 
 +                      const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_ndoe_gmx_large_int(t_fileio *fio, gmx_large_int_t *item, int n, 
 +                                const char *desc, const char *srcfile, 
 +                                int line);
 +gmx_bool gmx_fio_ndoe_uchar(t_fileio *fio, unsigned char *item, int n, 
 +                        const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_ndoe_ushort(t_fileio *fio, unsigned short *item, int n, 
 +                        const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_ndoe_rvec(t_fileio *fio, rvec *item, int n, 
 +                       const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_ndoe_ivec(t_fileio *fio, ivec *item, int n, 
 +                       const char *desc, const char *srcfile, int line);
 +gmx_bool gmx_fio_ndoe_string(t_fileio *fio, char *item[], int n, 
 +                         const char *desc, const char *srcfile, int line);
 +
 +
 +
 +/* convenience macros */
 +#define gmx_fio_read_real(fio, item)           gmx_fio_reade_real(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_read_float(fio, item)          gmx_fio_reade_float(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_read_double(fio, item)         gmx_fio_reade_double(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_read_int(fio, item)            gmx_fio_reade_int(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_read_gmx_large_int(fio, item)  gmx_fio_reade_gmx_large_int(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_read_uchar(fio, item)          gmx_fio_reade_uchar(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_read_ushort(fio, item)         gmx_fio_reade_ushort(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_read_rvec(fio, item)           gmx_fio_reade_rvec(fio, item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_read_ivec(fio, item)           gmx_fio_reade_ivec(fio, item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_read_string(fio, item)         gmx_fio_reade_string(fio, item, (#item), __FILE__, __LINE__)
 +
 +#define gmx_fio_write_real(fio, item)           gmx_fio_writee_real(fio, item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_write_float(fio, item)          gmx_fio_writee_float(fio, item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_write_double(fio, item)         gmx_fio_writee_double(fio, item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_write_int(fio, item)            gmx_fio_writee_int(fio, item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_write_gmx_large_int(fio, item)  gmx_fio_writee_gmx_large_int(fio, item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_write_uchar(fio, item)          gmx_fio_writee_uchar(fio, item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_write_ushort(fio, item)         gmx_fio_writee_ushort(fio, item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_write_rvec(fio, item)           gmx_fio_writee_rvec(fio, item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_write_ivec(fio, item)           gmx_fio_writee_ivec(fio, item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_write_string(fio, item)         gmx_fio_writee_string(fio, item, (#item), __FILE__, __LINE__)
 +
 +#define gmx_fio_do_real(fio, item)              gmx_fio_doe_real(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_do_float(fio, item)             gmx_fio_doe_float(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_do_double(fio, item)            gmx_fio_doe_double(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_do_gmx_bool(fio, item)              gmx_fio_doe_gmx_bool(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_do_int(fio, item)               gmx_fio_doe_int(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_do_gmx_large_int(fio, item)     gmx_fio_doe_gmx_large_int(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_do_uchar(fio, item)             gmx_fio_doe_uchar(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_do_ushort(fio, item)            gmx_fio_doe_ushort(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_do_rvec(fio, item)              gmx_fio_doe_rvec(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_do_ivec(fio, item)              gmx_fio_doe_ivec(fio, &item, (#item), __FILE__, __LINE__)
 +#define gmx_fio_do_string(fio, item)            gmx_fio_doe_string(fio, item, (#item), __FILE__, __LINE__)
 +
 +
 +
 +
 +#define gmx_fio_nread_real(fio, item, n)            gmx_fio_nreade_real(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nread_float(fio, item, n)           gmx_fio_nreade_float(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nread_double(fio, item, n)          gmx_fio_nreade_double(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nread_int(fio, item, n)             gmx_fio_nreade_int(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nread_gmx_large_int(fio, item, n)   gmx_fio_nreade_gmx_large_int(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nread_uchar(fio, item, n)           gmx_fio_nreade_uchar(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nread_ushort(fio, item, n)          gmx_fio_nreade_ushort(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nread_rvec(fio, item, n)            gmx_fio_nreade_rvec(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nread_ivec(fio, item, n)            gmx_fio_nreade_ivec(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nread_string(fio, item, n)          gmx_fio_nreade_string(fio, item, n, (#item), __FILE__, __LINE__)
 +
 +#define gmx_fio_nwrite_real(fio, item, n)           gmx_fio_nwritee_real(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nwrite_float(fio, item, n)          gmx_fio_nwritee_float(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nwrite_double(fio, item, n)         gmx_fio_nwritee_double(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nwrite_int(fio, item, n)            gmx_fio_nwritee_int(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nwrite_gmx_large_int(fio, item, n)  gmx_fio_nwritee_gmx_large_int(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nwrite_uchar(fio, item, n)          gmx_fio_nwritee_uchar(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nwrite_ushort(fio, item, n)         gmx_fio_nwritee_ushort(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nwrite_rvec(fio, item, n)           gmx_fio_nwritee_rvec(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nwrite_ivec(fio, item, n)           gmx_fio_nwritee_ivec(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_nwrite_string(fio, item, n)         gmx_fio_nwritee_string(fio, item, n, (#item), __FILE__, __LINE__)
 +
 +#define gmx_fio_ndo_real(fio, item, n)              gmx_fio_ndoe_real(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_ndo_float(fio, item, n)             gmx_fio_ndoe_float(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_ndo_double(fio, item, n)            gmx_fio_ndoe_double(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_ndo_gmx_bool(fio, item, n)              gmx_fio_ndoe_gmx_bool(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_ndo_int(fio, item, n)               gmx_fio_ndoe_int(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_ndo_gmx_large_int(fio, item, n)     gmx_fio_ndoe_gmx_large_int(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_ndo_uchar(fio, item, n)             gmx_fio_ndoe_uchar(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_ndo_ushort(fio, item, n)            gmx_fio_ndoe_ushort(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_ndo_rvec(fio, item, n)              gmx_fio_ndoe_rvec(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_ndo_ivec(fio, item, n)              gmx_fio_ndoe_ivec(fio, item, n, (#item), __FILE__, __LINE__)
 +#define gmx_fio_ndo_string(fio, item, n)            gmx_fio_ndoe_string(fio, item, n, (#item), __FILE__, __LINE__)
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif
index fa874a634c93313c25f4f58d59ec5349b0df4b44,0000000000000000000000000000000000000000..894a463b1bb9d87468b28344cb28c07309ca9dbb
mode 100644,000000..100644
--- /dev/null
@@@ -1,123 -1,0 +1,123 @@@
- int xdr_xtc_seek_time(real time, FILE *fp, XDR *xdrs, int natoms);
 +/*
 + * 
 + *                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:
 + * Gromacs Runs On Most of All Computer Systems
 + */
 +
 +#ifndef _xdrf_h
 +#define _xdrf_h
 +
 +
 +#include <stdio.h>
 +#include "typedefs.h"
 +
 +#ifdef __PGI    /*Portland group compiler*/
 +#define int64_t long long
 +#endif
 +
 +#include "../utility/gmx_header_config.h"
 +#if (defined GMX_NATIVE_WINDOWS || defined GMX_CYGWIN || defined GMX_INTERNAL_XDR)
 +#include "gmx_system_xdr.h"
 +#else
 +#include <rpc/rpc.h>
 +#include <rpc/xdr.h>
 +#endif
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +
 +/* THESE 3 FUNCTIONS (xdropen, xdrclose and xdr_get_fp) ARE NOW OBSOLETE 
 +   AND ONLY PROVIDED FOR BACKWARD COMPATIBILITY OF 3D PARTY TOOLS. 
 +   THEY SHOULD NOT BE USED ANYWHERE IN GROMACS ITSELF. 
 +int xdropen(XDR *xdrs, const char *filename, const char *type);
 +int xdrclose(XDR *xdrs);
 +*/
 +
 +/* the xdr data types; note that there is no data type 'real' because
 +   here we deal with the types as they are actually written to disk.  */
 +typedef enum
 +{
 +    xdr_datatype_int,
 +    xdr_datatype_float,
 +    xdr_datatype_double,
 +    xdr_datatype_large_int,
 +    xdr_datatype_char,
 +    xdr_datatype_string
 +} xdr_datatype;
 +
 +/* names corresponding to the xdr_datatype enum */
 +extern const char *xdr_datatype_names[];
 +
 +/* Read or write reduced precision *float* coordinates */
 +int xdr3dfcoord(XDR *xdrs, float *fp, int *size, float *precision);
 +
 +
 +/* Read or write a *real* value (stored as float) */
 +int xdr_real(XDR *xdrs,real *r); 
 +
 +
 +/* Read or write reduced precision *real* coordinates */
 +int xdr3drcoord(XDR *xdrs,real *fp,int *size,real *precision);
 +
 +
 +int xdr_gmx_large_int(XDR *xdrs,gmx_large_int_t *i,const char *warn);
 +/* Read or write a gmx_large_int_t value.
 + * 32bit code reading a 64bit gmx_large_int_t value from xdrs could
 + * lead to values out of int range.
 + * When warn!=NULL a warning will be written to stderr
 + * when a value does not fit,
 + * the first line is:
 + * "WARNING during %s:", where warn is printed in %s.
 + */
 +
 +float xdr_xtc_estimate_dt(FILE *fp, XDR *xdrs, int natoms, gmx_bool * bOK);
 +
++  int xdr_xtc_seek_time(real time, FILE *fp, XDR *xdrs, int natoms,gmx_bool bSeekForwardOnly);
 +
 +
 +int xdr_xtc_seek_frame(int frame, FILE *fp, XDR *xdrs, int natoms);
 +
 +
 +float xdr_xtc_get_last_frame_time(FILE *fp, XDR *xdrs, int natoms, gmx_bool * bOK);
 +
 +
 +int xdr_xtc_get_last_frame_number(FILE *fp, XDR *xdrs, int natoms, gmx_bool * bOK);
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif
 +
 +
Simple merge
index 9a7222fc78fa181990502372b225d94362a4572d,0000000000000000000000000000000000000000..5d45b7b141e144e58166ed04d23909428e396bbb
mode 100644,000000..100644
--- /dev/null
@@@ -1,1829 -1,0 +1,1829 @@@
-     if( (getenv("GMX_DISABLE_ACCELERATION") != NULL) || (getenv("GMX_NOOPTIMIZEDKERNELS") != NULL) )
 +/* -*- 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:
 + * GROwing Monsters And Cloning Shrimps
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <string.h>
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "macros.h"
 +#include "smalloc.h"
 +#include "macros.h"
 +#include "physics.h"
 +#include "force.h"
 +#include "nonbonded.h"
 +#include "invblock.h"
 +#include "names.h"
 +#include "network.h"
 +#include "pbc.h"
 +#include "ns.h"
 +#include "mshift.h"
 +#include "txtdump.h"
 +#include "coulomb.h"
 +#include "mdrun.h"
 +#include "domdec.h"
 +#include "partdec.h"
 +#include "qmmm.h"
 +#include "copyrite.h"
 +#include "mtop_util.h"
 +#include "gmx_detectcpu.h"
 +
 +#ifdef _MSC_VER
 +/* MSVC definition for __cpuid() */
 +#include <intrin.h>
 +#endif
 +
 +
 +
 +t_forcerec *mk_forcerec(void)
 +{
 +  t_forcerec *fr;
 +  
 +  snew(fr,1);
 +  
 +  return fr;
 +}
 +
 +#ifdef DEBUG
 +static void pr_nbfp(FILE *fp,real *nbfp,gmx_bool bBHAM,int atnr)
 +{
 +  int i,j;
 +  
 +  for(i=0; (i<atnr); i++) {
 +    for(j=0; (j<atnr); j++) {
 +      fprintf(fp,"%2d - %2d",i,j);
 +      if (bBHAM)
 +      fprintf(fp,"  a=%10g, b=%10g, c=%10g\n",BHAMA(nbfp,atnr,i,j),
 +              BHAMB(nbfp,atnr,i,j),BHAMC(nbfp,atnr,i,j));
 +      else
 +      fprintf(fp,"  c6=%10g, c12=%10g\n",C6(nbfp,atnr,i,j),
 +              C12(nbfp,atnr,i,j));
 +    }
 +  }
 +}
 +#endif
 +
 +static real *mk_nbfp(const gmx_ffparams_t *idef,gmx_bool bBHAM)
 +{
 +  real *nbfp;
 +  int  i,j,k,atnr;
 +  
 +  atnr=idef->atnr;
 +  if (bBHAM) {
 +    snew(nbfp,3*atnr*atnr);
 +    for(i=k=0; (i<atnr); i++) {
 +      for(j=0; (j<atnr); j++,k++) {
 +      BHAMA(nbfp,atnr,i,j) = idef->iparams[k].bham.a;
 +      BHAMB(nbfp,atnr,i,j) = idef->iparams[k].bham.b;
 +      BHAMC(nbfp,atnr,i,j) = idef->iparams[k].bham.c;
 +      }
 +    }
 +  }
 +  else {
 +    snew(nbfp,2*atnr*atnr);
 +    for(i=k=0; (i<atnr); i++) {
 +      for(j=0; (j<atnr); j++,k++) {
 +      C6(nbfp,atnr,i,j)   = idef->iparams[k].lj.c6;
 +      C12(nbfp,atnr,i,j)  = idef->iparams[k].lj.c12;
 +      }
 +    }
 +  }
 +  return nbfp;
 +}
 +
 +/* This routine sets fr->solvent_opt to the most common solvent in the 
 + * system, e.g. esolSPC or esolTIP4P. It will also mark each charge group in 
 + * the fr->solvent_type array with the correct type (or esolNO).
 + *
 + * Charge groups that fulfill the conditions but are not identical to the
 + * most common one will be marked as esolNO in the solvent_type array. 
 + *
 + * TIP3p is identical to SPC for these purposes, so we call it
 + * SPC in the arrays (Apologies to Bill Jorgensen ;-)
 + * 
 + * NOTE: QM particle should not
 + * become an optimized solvent. Not even if there is only one charge
 + * group in the Qm 
 + */
 +
 +typedef struct 
 +{
 +    int    model;          
 +    int    count;
 +    int    vdwtype[4];
 +    real   charge[4];
 +} solvent_parameters_t;
 +
 +static void
 +check_solvent_cg(const gmx_moltype_t   *molt,
 +                 int                   cg0,
 +                 int                   nmol,
 +                 const unsigned char   *qm_grpnr,
 +                 const t_grps          *qm_grps,
 +                 t_forcerec *          fr,
 +                 int                   *n_solvent_parameters,
 +                 solvent_parameters_t  **solvent_parameters_p,
 +                 int                   cginfo,
 +                 int                   *cg_sp)
 +{
 +    const t_blocka *  excl;
 +    t_atom            *atom;
 +    int               j,k;
 +    int               j0,j1,nj;
 +    gmx_bool              perturbed;
 +    gmx_bool              has_vdw[4];
 +    gmx_bool              match;
 +    real              tmp_charge[4];
 +    int               tmp_vdwtype[4];
 +    int               tjA;
 +    gmx_bool              qm;
 +    solvent_parameters_t *solvent_parameters;
 +
 +    /* We use a list with parameters for each solvent type. 
 +     * Every time we discover a new molecule that fulfills the basic 
 +     * conditions for a solvent we compare with the previous entries
 +     * in these lists. If the parameters are the same we just increment
 +     * the counter for that type, and otherwise we create a new type
 +     * based on the current molecule.
 +     *
 +     * Once we've finished going through all molecules we check which
 +     * solvent is most common, and mark all those molecules while we
 +     * clear the flag on all others.
 +     */   
 +
 +    solvent_parameters = *solvent_parameters_p;
 +
 +    /* Mark the cg first as non optimized */
 +    *cg_sp = -1;
 +    
 +    /* Check if this cg has no exclusions with atoms in other charge groups
 +     * and all atoms inside the charge group excluded.
 +     * We only have 3 or 4 atom solvent loops.
 +     */
 +    if (GET_CGINFO_EXCL_INTER(cginfo) ||
 +        !GET_CGINFO_EXCL_INTRA(cginfo))
 +    {
 +        return;
 +    }
 +
 +    /* Get the indices of the first atom in this charge group */
 +    j0     = molt->cgs.index[cg0];
 +    j1     = molt->cgs.index[cg0+1];
 +    
 +    /* Number of atoms in our molecule */
 +    nj     = j1 - j0;
 +
 +    if (debug) {
 +        fprintf(debug,
 +                "Moltype '%s': there are %d atoms in this charge group\n",
 +                *molt->name,nj);
 +    }
 +    
 +    /* Check if it could be an SPC (3 atoms) or TIP4p (4) water,
 +     * otherwise skip it.
 +     */
 +    if (nj<3 || nj>4)
 +    {
 +        return;
 +    }
 +    
 +    /* Check if we are doing QM on this group */
 +    qm = FALSE; 
 +    if (qm_grpnr != NULL)
 +    {
 +        for(j=j0 ; j<j1 && !qm; j++)
 +        {
 +            qm = (qm_grpnr[j] < qm_grps->nr - 1);
 +        }
 +    }
 +    /* Cannot use solvent optimization with QM */
 +    if (qm)
 +    {
 +        return;
 +    }
 +    
 +    atom = molt->atoms.atom;
 +
 +    /* Still looks like a solvent, time to check parameters */
 +    
 +    /* If it is perturbed (free energy) we can't use the solvent loops,
 +     * so then we just skip to the next molecule.
 +     */   
 +    perturbed = FALSE; 
 +    
 +    for(j=j0; j<j1 && !perturbed; j++)
 +    {
 +        perturbed = PERTURBED(atom[j]);
 +    }
 +    
 +    if (perturbed)
 +    {
 +        return;
 +    }
 +    
 +    /* Now it's only a question if the VdW and charge parameters 
 +     * are OK. Before doing the check we compare and see if they are 
 +     * identical to a possible previous solvent type.
 +     * First we assign the current types and charges.    
 +     */
 +    for(j=0; j<nj; j++)
 +    {
 +        tmp_vdwtype[j] = atom[j0+j].type;
 +        tmp_charge[j]  = atom[j0+j].q;
 +    } 
 +    
 +    /* Does it match any previous solvent type? */
 +    for(k=0 ; k<*n_solvent_parameters; k++)
 +    {
 +        match = TRUE;
 +        
 +        
 +        /* We can only match SPC with 3 atoms and TIP4p with 4 atoms */
 +        if( (solvent_parameters[k].model==esolSPC   && nj!=3)  ||
 +            (solvent_parameters[k].model==esolTIP4P && nj!=4) )
 +            match = FALSE;
 +        
 +        /* Check that types & charges match for all atoms in molecule */
 +        for(j=0 ; j<nj && match==TRUE; j++)
 +        {                     
 +            if (tmp_vdwtype[j] != solvent_parameters[k].vdwtype[j])
 +            {
 +                match = FALSE;
 +            }
 +            if(tmp_charge[j] != solvent_parameters[k].charge[j])
 +            {
 +                match = FALSE;
 +            }
 +        }
 +        if (match == TRUE)
 +        {
 +            /* Congratulations! We have a matched solvent.
 +             * Flag it with this type for later processing.
 +             */
 +            *cg_sp = k;
 +            solvent_parameters[k].count += nmol;
 +
 +            /* We are done with this charge group */
 +            return;
 +        }
 +    }
 +    
 +    /* If we get here, we have a tentative new solvent type.
 +     * Before we add it we must check that it fulfills the requirements
 +     * of the solvent optimized loops. First determine which atoms have
 +     * VdW interactions.   
 +     */
 +    for(j=0; j<nj; j++) 
 +    {
 +        has_vdw[j] = FALSE;
 +        tjA        = tmp_vdwtype[j];
 +        
 +        /* Go through all other tpes and see if any have non-zero
 +         * VdW parameters when combined with this one.
 +         */   
 +        for(k=0; k<fr->ntype && (has_vdw[j]==FALSE); k++)
 +        {
 +            /* We already checked that the atoms weren't perturbed,
 +             * so we only need to check state A now.
 +             */ 
 +            if (fr->bBHAM) 
 +            {
 +                has_vdw[j] = (has_vdw[j] || 
 +                              (BHAMA(fr->nbfp,fr->ntype,tjA,k) != 0.0) ||
 +                              (BHAMB(fr->nbfp,fr->ntype,tjA,k) != 0.0) ||
 +                              (BHAMC(fr->nbfp,fr->ntype,tjA,k) != 0.0));
 +            }
 +            else
 +            {
 +                /* Standard LJ */
 +                has_vdw[j] = (has_vdw[j] || 
 +                              (C6(fr->nbfp,fr->ntype,tjA,k)  != 0.0) ||
 +                              (C12(fr->nbfp,fr->ntype,tjA,k) != 0.0));
 +            }
 +        }
 +    }
 +    
 +    /* Now we know all we need to make the final check and assignment. */
 +    if (nj == 3)
 +    {
 +        /* So, is it an SPC?
 +         * For this we require thatn all atoms have charge, 
 +         * the charges on atom 2 & 3 should be the same, and only
 +         * atom 1 should have VdW.
 +         */
 +        if (has_vdw[0] == TRUE && 
 +            has_vdw[1] == FALSE &&
 +            has_vdw[2] == FALSE &&
 +            tmp_charge[0]  != 0 &&
 +            tmp_charge[1]  != 0 &&
 +            tmp_charge[2]  == tmp_charge[1])
 +        {
 +            srenew(solvent_parameters,*n_solvent_parameters+1);
 +            solvent_parameters[*n_solvent_parameters].model = esolSPC;
 +            solvent_parameters[*n_solvent_parameters].count = nmol;
 +            for(k=0;k<3;k++)
 +            {
 +                solvent_parameters[*n_solvent_parameters].vdwtype[k] = tmp_vdwtype[k];
 +                solvent_parameters[*n_solvent_parameters].charge[k]  = tmp_charge[k];
 +            }
 +
 +            *cg_sp = *n_solvent_parameters;
 +            (*n_solvent_parameters)++;
 +        }
 +    }
 +    else if (nj==4)
 +    {
 +        /* Or could it be a TIP4P?
 +         * For this we require thatn atoms 2,3,4 have charge, but not atom 1. 
 +         * Only atom 1 should have VdW.
 +         */
 +        if(has_vdw[0] == TRUE && 
 +           has_vdw[1] == FALSE &&
 +           has_vdw[2] == FALSE &&
 +           has_vdw[3] == FALSE &&
 +           tmp_charge[0]  == 0 &&
 +           tmp_charge[1]  != 0 &&
 +           tmp_charge[2]  == tmp_charge[1] &&
 +           tmp_charge[3]  != 0)
 +        {
 +            srenew(solvent_parameters,*n_solvent_parameters+1);
 +            solvent_parameters[*n_solvent_parameters].model = esolTIP4P;
 +            solvent_parameters[*n_solvent_parameters].count = nmol;
 +            for(k=0;k<4;k++)
 +            {
 +                solvent_parameters[*n_solvent_parameters].vdwtype[k] = tmp_vdwtype[k];
 +                solvent_parameters[*n_solvent_parameters].charge[k]  = tmp_charge[k];
 +            }
 +            
 +            *cg_sp = *n_solvent_parameters;
 +            (*n_solvent_parameters)++;
 +        }
 +    }
 +
 +    *solvent_parameters_p = solvent_parameters;
 +}
 +
 +static void
 +check_solvent(FILE *                fp,
 +              const gmx_mtop_t *    mtop,
 +              t_forcerec *          fr,
 +              cginfo_mb_t           *cginfo_mb)
 +{
 +    const t_block *   cgs;
 +    const t_block *   mols;
 +    const gmx_moltype_t *molt;
 +    int               mb,mol,cg_mol,at_offset,cg_offset,am,cgm,i,nmol_ch,nmol;
 +    int               n_solvent_parameters;
 +    solvent_parameters_t *solvent_parameters;
 +    int               **cg_sp;
 +    int               bestsp,bestsol;
 +
 +    if (debug)
 +    {
 +        fprintf(debug,"Going to determine what solvent types we have.\n");
 +    }
 +
 +    mols = &mtop->mols;
 +
 +    n_solvent_parameters = 0;
 +    solvent_parameters = NULL;
 +    /* Allocate temporary array for solvent type */
 +    snew(cg_sp,mtop->nmolblock);
 +
 +    cg_offset = 0;
 +    at_offset = 0;
 +    for(mb=0; mb<mtop->nmolblock; mb++)
 +    {
 +        molt = &mtop->moltype[mtop->molblock[mb].type];
 +        cgs  = &molt->cgs;
 +        /* Here we have to loop over all individual molecules
 +         * because we need to check for QMMM particles.
 +         */
 +        snew(cg_sp[mb],cginfo_mb[mb].cg_mod);
 +        nmol_ch = cginfo_mb[mb].cg_mod/cgs->nr;
 +        nmol    = mtop->molblock[mb].nmol/nmol_ch;
 +        for(mol=0; mol<nmol_ch; mol++)
 +        {
 +            cgm = mol*cgs->nr;
 +            am  = mol*cgs->index[cgs->nr];
 +            for(cg_mol=0; cg_mol<cgs->nr; cg_mol++)
 +            {
 +                check_solvent_cg(molt,cg_mol,nmol,
 +                                 mtop->groups.grpnr[egcQMMM] ?
 +                                 mtop->groups.grpnr[egcQMMM]+at_offset+am : 0,
 +                                 &mtop->groups.grps[egcQMMM],
 +                                 fr,
 +                                 &n_solvent_parameters,&solvent_parameters,
 +                                 cginfo_mb[mb].cginfo[cgm+cg_mol],
 +                                 &cg_sp[mb][cgm+cg_mol]);
 +            }
 +        }
 +        cg_offset += cgs->nr;
 +        at_offset += cgs->index[cgs->nr];
 +    }
 +
 +    /* Puh! We finished going through all charge groups.
 +     * Now find the most common solvent model.
 +     */   
 +    
 +    /* Most common solvent this far */
 +    bestsp = -2;
 +    for(i=0;i<n_solvent_parameters;i++)
 +    {
 +        if (bestsp == -2 ||
 +            solvent_parameters[i].count > solvent_parameters[bestsp].count)
 +        {
 +            bestsp = i;
 +        }
 +    }
 +    
 +    if (bestsp >= 0)
 +    {
 +        bestsol = solvent_parameters[bestsp].model;
 +    }
 +    else
 +    {
 +        bestsol = esolNO;
 +    }
 +    
 +#ifdef DISABLE_WATER_NLIST
 +      bestsol = esolNO;
 +#endif
 +
 +    fr->nWatMol = 0;
 +    for(mb=0; mb<mtop->nmolblock; mb++)
 +    {
 +        cgs = &mtop->moltype[mtop->molblock[mb].type].cgs;
 +        nmol = (mtop->molblock[mb].nmol*cgs->nr)/cginfo_mb[mb].cg_mod;
 +        for(i=0; i<cginfo_mb[mb].cg_mod; i++)
 +        {
 +            if (cg_sp[mb][i] == bestsp)
 +            {
 +                SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[i],bestsol);
 +                fr->nWatMol += nmol;
 +            }
 +            else
 +            {
 +                SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[i],esolNO);
 +            }
 +        }
 +        sfree(cg_sp[mb]);
 +    }
 +    sfree(cg_sp);
 +    
 +    if (bestsol != esolNO && fp!=NULL)
 +    {
 +        fprintf(fp,"\nEnabling %s-like water optimization for %d molecules.\n\n",
 +                esol_names[bestsol],
 +                solvent_parameters[bestsp].count);
 +    }
 +
 +    sfree(solvent_parameters);
 +    fr->solvent_opt = bestsol;
 +}
 +
 +static cginfo_mb_t *init_cginfo_mb(FILE *fplog,const gmx_mtop_t *mtop,
 +                                   t_forcerec *fr,gmx_bool bNoSolvOpt)
 +{
 +    const t_block *cgs;
 +    const t_blocka *excl;
 +    const gmx_moltype_t *molt;
 +    const gmx_molblock_t *molb;
 +    cginfo_mb_t *cginfo_mb;
 +    int  *cginfo;
 +    int  cg_offset,a_offset,cgm,am;
 +    int  mb,m,ncg_tot,cg,a0,a1,gid,ai,j,aj,excl_nalloc;
 +    gmx_bool bId,*bExcl,bExclIntraAll,bExclInter;
 +
 +    ncg_tot = ncg_mtop(mtop);
 +    snew(cginfo_mb,mtop->nmolblock);
 +
 +    excl_nalloc = 10;
 +    snew(bExcl,excl_nalloc);
 +    cg_offset = 0;
 +    a_offset  = 0;
 +    for(mb=0; mb<mtop->nmolblock; mb++)
 +    {
 +        molb = &mtop->molblock[mb];
 +        molt = &mtop->moltype[molb->type];
 +        cgs  = &molt->cgs;
 +        excl = &molt->excls;
 +
 +        /* Check if the cginfo is identical for all molecules in this block.
 +         * If so, we only need an array of the size of one molecule.
 +         * Otherwise we make an array of #mol times #cgs per molecule.
 +         */
 +        bId = TRUE;
 +        am = 0;
 +        for(m=0; m<molb->nmol; m++)
 +        {
 +            am = m*cgs->index[cgs->nr];
 +            for(cg=0; cg<cgs->nr; cg++)
 +            {
 +                a0 = cgs->index[cg];
 +                a1 = cgs->index[cg+1];
 +                if (ggrpnr(&mtop->groups,egcENER,a_offset+am+a0) !=
 +                    ggrpnr(&mtop->groups,egcENER,a_offset   +a0))
 +                {
 +                    bId = FALSE;
 +                }
 +                if (mtop->groups.grpnr[egcQMMM] != NULL)
 +                {
 +                    for(ai=a0; ai<a1; ai++)
 +                    {
 +                        if (mtop->groups.grpnr[egcQMMM][a_offset+am+ai] !=
 +                            mtop->groups.grpnr[egcQMMM][a_offset   +ai])
 +                        {
 +                            bId = FALSE;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        cginfo_mb[mb].cg_start = cg_offset;
 +        cginfo_mb[mb].cg_end   = cg_offset + molb->nmol*cgs->nr;
 +        cginfo_mb[mb].cg_mod   = (bId ? 1 : molb->nmol)*cgs->nr;
 +        snew(cginfo_mb[mb].cginfo,cginfo_mb[mb].cg_mod);
 +        cginfo = cginfo_mb[mb].cginfo;
 +
 +        for(m=0; m<(bId ? 1 : molb->nmol); m++)
 +        {
 +            cgm = m*cgs->nr;
 +            am  = m*cgs->index[cgs->nr];
 +            for(cg=0; cg<cgs->nr; cg++)
 +            {
 +                a0 = cgs->index[cg];
 +                a1 = cgs->index[cg+1];
 +
 +                /* Store the energy group in cginfo */
 +                gid = ggrpnr(&mtop->groups,egcENER,a_offset+am+a0);
 +                SET_CGINFO_GID(cginfo[cgm+cg],gid);
 +                
 +                /* Check the intra/inter charge group exclusions */
 +                if (a1-a0 > excl_nalloc) {
 +                    excl_nalloc = a1 - a0;
 +                    srenew(bExcl,excl_nalloc);
 +                }
 +                /* bExclIntraAll: all intra cg interactions excluded
 +                 * bExclInter:    any inter cg interactions excluded
 +                 */
 +                bExclIntraAll = TRUE;
 +                bExclInter    = FALSE;
 +                for(ai=a0; ai<a1; ai++) {
 +                    /* Clear the exclusion list for atom ai */
 +                    for(aj=a0; aj<a1; aj++) {
 +                        bExcl[aj-a0] = FALSE;
 +                    }
 +                    /* Loop over all the exclusions of atom ai */
 +                    for(j=excl->index[ai]; j<excl->index[ai+1]; j++)
 +                    {
 +                        aj = excl->a[j];
 +                        if (aj < a0 || aj >= a1)
 +                        {
 +                            bExclInter = TRUE;
 +                        }
 +                        else
 +                        {
 +                            bExcl[aj-a0] = TRUE;
 +                        }
 +                    }
 +                    /* Check if ai excludes a0 to a1 */
 +                    for(aj=a0; aj<a1; aj++)
 +                    {
 +                        if (!bExcl[aj-a0])
 +                        {
 +                            bExclIntraAll = FALSE;
 +                        }
 +                    }
 +                }
 +                if (bExclIntraAll)
 +                {
 +                    SET_CGINFO_EXCL_INTRA(cginfo[cgm+cg]);
 +                }
 +                if (bExclInter)
 +                {
 +                    SET_CGINFO_EXCL_INTER(cginfo[cgm+cg]);
 +                }
 +                if (a1 - a0 > MAX_CHARGEGROUP_SIZE)
 +                {
 +                    /* The size in cginfo is currently only read with DD */
 +                    gmx_fatal(FARGS,"A charge group has size %d which is larger than the limit of %d atoms",a1-a0,MAX_CHARGEGROUP_SIZE);
 +                }
 +                SET_CGINFO_NATOMS(cginfo[cgm+cg],a1-a0);
 +            }
 +        }
 +        cg_offset += molb->nmol*cgs->nr;
 +        a_offset  += molb->nmol*cgs->index[cgs->nr];
 +    }
 +    sfree(bExcl);
 +    
 +    /* the solvent optimizer is called after the QM is initialized,
 +     * because we don't want to have the QM subsystemto become an
 +     * optimized solvent
 +     */
 +
 +    check_solvent(fplog,mtop,fr,cginfo_mb);
 +    
 +    if (getenv("GMX_NO_SOLV_OPT"))
 +    {
 +        if (fplog)
 +        {
 +            fprintf(fplog,"Found environment variable GMX_NO_SOLV_OPT.\n"
 +                    "Disabling all solvent optimization\n");
 +        }
 +        fr->solvent_opt = esolNO;
 +    }
 +    if (bNoSolvOpt)
 +    {
 +        fr->solvent_opt = esolNO;
 +    }
 +    if (!fr->solvent_opt)
 +    {
 +        for(mb=0; mb<mtop->nmolblock; mb++)
 +        {
 +            for(cg=0; cg<cginfo_mb[mb].cg_mod; cg++)
 +            {
 +                SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[cg],esolNO);
 +            }
 +        }
 +    }
 +    
 +    return cginfo_mb;
 +}
 +
 +static int *cginfo_expand(int nmb,cginfo_mb_t *cgi_mb)
 +{
 +    int ncg,mb,cg;
 +    int *cginfo;
 +
 +    ncg = cgi_mb[nmb-1].cg_end;
 +    snew(cginfo,ncg);
 +    mb = 0;
 +    for(cg=0; cg<ncg; cg++)
 +    {
 +        while (cg >= cgi_mb[mb].cg_end)
 +        {
 +            mb++;
 +        }
 +        cginfo[cg] =
 +            cgi_mb[mb].cginfo[(cg - cgi_mb[mb].cg_start) % cgi_mb[mb].cg_mod];
 +    }
 +
 +    return cginfo;
 +}
 +
 +static void set_chargesum(FILE *log,t_forcerec *fr,const gmx_mtop_t *mtop)
 +{
 +    double qsum;
 +    int    mb,nmol,i;
 +    const t_atoms *atoms;
 +    
 +    qsum = 0;
 +    for(mb=0; mb<mtop->nmolblock; mb++)
 +    {
 +        nmol  = mtop->molblock[mb].nmol;
 +        atoms = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +        for(i=0; i<atoms->nr; i++)
 +        {
 +            qsum += nmol*atoms->atom[i].q;
 +        }
 +    }
 +    fr->qsum[0] = qsum;
 +    if (fr->efep != efepNO)
 +    {
 +        qsum = 0;
 +        for(mb=0; mb<mtop->nmolblock; mb++)
 +        {
 +            nmol  = mtop->molblock[mb].nmol;
 +            atoms = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +            for(i=0; i<atoms->nr; i++)
 +            {
 +                qsum += nmol*atoms->atom[i].qB;
 +            }
 +            fr->qsum[1] = qsum;
 +        }
 +    }
 +    else
 +    {
 +        fr->qsum[1] = fr->qsum[0];
 +    }
 +    if (log) {
 +        if (fr->efep == efepNO)
 +            fprintf(log,"System total charge: %.3f\n",fr->qsum[0]);
 +        else
 +            fprintf(log,"System total charge, top. A: %.3f top. B: %.3f\n",
 +                    fr->qsum[0],fr->qsum[1]);
 +    }
 +}
 +
 +void update_forcerec(FILE *log,t_forcerec *fr,matrix box)
 +{
 +    if (fr->eeltype == eelGRF)
 +    {
 +        calc_rffac(NULL,fr->eeltype,fr->epsilon_r,fr->epsilon_rf,
 +                   fr->rcoulomb,fr->temp,fr->zsquare,box,
 +                   &fr->kappa,&fr->k_rf,&fr->c_rf);
 +    }
 +}
 +
 +void set_avcsixtwelve(FILE *fplog,t_forcerec *fr,const gmx_mtop_t *mtop)
 +{
 +    const t_atoms *atoms,*atoms_tpi;
 +    const t_blocka *excl;
 +    int    mb,nmol,nmolc,i,j,tpi,tpj,j1,j2,k,n,nexcl,q;
 +#if (defined SIZEOF_LONG_LONG_INT) && (SIZEOF_LONG_LONG_INT >= 8)    
 +    long long int  npair,npair_ij,tmpi,tmpj;
 +#else
 +    double npair, npair_ij,tmpi,tmpj;
 +#endif
 +    double csix,ctwelve;
 +    int    ntp,*typecount;
 +    gmx_bool   bBHAM;
 +    real   *nbfp;
 +
 +    ntp = fr->ntype;
 +    bBHAM = fr->bBHAM;
 +    nbfp = fr->nbfp;
 +    
 +    for(q=0; q<(fr->efep==efepNO ? 1 : 2); q++) {
 +        csix = 0;
 +        ctwelve = 0;
 +        npair = 0;
 +        nexcl = 0;
 +        if (!fr->n_tpi) {
 +            /* Count the types so we avoid natoms^2 operations */
 +            snew(typecount,ntp);
 +            for(mb=0; mb<mtop->nmolblock; mb++) {
 +                nmol  = mtop->molblock[mb].nmol;
 +                atoms = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +                for(i=0; i<atoms->nr; i++) {
 +                    if (q == 0)
 +                    {
 +                        tpi = atoms->atom[i].type;
 +                    }
 +                    else
 +                    {
 +                        tpi = atoms->atom[i].typeB;
 +                    }
 +                    typecount[tpi] += nmol;
 +                }
 +            }
 +            for(tpi=0; tpi<ntp; tpi++) {
 +                for(tpj=tpi; tpj<ntp; tpj++) {
 +                    tmpi = typecount[tpi];
 +                    tmpj = typecount[tpj];
 +                    if (tpi != tpj)
 +                    {
 +                        npair_ij = tmpi*tmpj;
 +                    }
 +                    else
 +                    {
 +                        npair_ij = tmpi*(tmpi - 1)/2;
 +                    }
 +                    if (bBHAM) {
 +                        csix    += npair_ij*BHAMC(nbfp,ntp,tpi,tpj);
 +                    } else {
 +                        csix    += npair_ij*   C6(nbfp,ntp,tpi,tpj);
 +                        ctwelve += npair_ij*  C12(nbfp,ntp,tpi,tpj);
 +                    }
 +                    npair += npair_ij;
 +                }
 +            }
 +            sfree(typecount);
 +            /* Subtract the excluded pairs.
 +             * The main reason for substracting exclusions is that in some cases
 +             * some combinations might never occur and the parameters could have
 +             * any value. These unused values should not influence the dispersion
 +             * correction.
 +             */
 +            for(mb=0; mb<mtop->nmolblock; mb++) {
 +                nmol  = mtop->molblock[mb].nmol;
 +                atoms = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +                excl  = &mtop->moltype[mtop->molblock[mb].type].excls;
 +                for(i=0; (i<atoms->nr); i++) {
 +                    if (q == 0)
 +                    {
 +                        tpi = atoms->atom[i].type;
 +                    }
 +                    else
 +                    {
 +                        tpi = atoms->atom[i].typeB;
 +                    }
 +                    j1  = excl->index[i];
 +                    j2  = excl->index[i+1];
 +                    for(j=j1; j<j2; j++) {
 +                        k = excl->a[j];
 +                        if (k > i)
 +                        {
 +                            if (q == 0)
 +                            {
 +                                tpj = atoms->atom[k].type;
 +                            }
 +                            else
 +                            {
 +                                tpj = atoms->atom[k].typeB;
 +                            }
 +                            if (bBHAM) {
 +                               csix -= nmol*BHAMC(nbfp,ntp,tpi,tpj);
 +                            } else {
 +                                csix    -= nmol*C6 (nbfp,ntp,tpi,tpj);
 +                                ctwelve -= nmol*C12(nbfp,ntp,tpi,tpj);
 +                            }
 +                            nexcl += nmol;
 +                        }
 +                    }
 +                }
 +            }
 +        } else {
 +            /* Only correct for the interaction of the test particle
 +             * with the rest of the system.
 +             */
 +            atoms_tpi =
 +                &mtop->moltype[mtop->molblock[mtop->nmolblock-1].type].atoms;
 +
 +            npair = 0;
 +            for(mb=0; mb<mtop->nmolblock; mb++) {
 +                nmol  = mtop->molblock[mb].nmol;
 +                atoms = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +                for(j=0; j<atoms->nr; j++) {
 +                    nmolc = nmol;
 +                    /* Remove the interaction of the test charge group
 +                     * with itself.
 +                     */
 +                    if (mb == mtop->nmolblock-1)
 +                    {
 +                        nmolc--;
 +                        
 +                        if (mb == 0 && nmol == 1)
 +                        {
 +                            gmx_fatal(FARGS,"Old format tpr with TPI, please generate a new tpr file");
 +                        }
 +                    }
 +                    if (q == 0)
 +                    {
 +                        tpj = atoms->atom[j].type;
 +                    }
 +                    else
 +                    {
 +                        tpj = atoms->atom[j].typeB;
 +                    }
 +                    for(i=0; i<fr->n_tpi; i++)
 +                    {
 +                        if (q == 0)
 +                        {
 +                            tpi = atoms_tpi->atom[i].type;
 +                        }
 +                        else
 +                        {
 +                            tpi = atoms_tpi->atom[i].typeB;
 +                        }
 +                        if (bBHAM)
 +                        {
 +                            csix    += nmolc*BHAMC(nbfp,ntp,tpi,tpj);
 +                        }
 +                        else
 +                        {
 +                            csix    += nmolc*C6 (nbfp,ntp,tpi,tpj);
 +                            ctwelve += nmolc*C12(nbfp,ntp,tpi,tpj);
 +                        }
 +                        npair += nmolc;
 +                    }
 +                }
 +            }
 +        }
 +        if (npair - nexcl <= 0 && fplog) {
 +            fprintf(fplog,"\nWARNING: There are no atom pairs for dispersion correction\n\n");
 +            csix     = 0;
 +            ctwelve  = 0;
 +        } else {
 +            csix    /= npair - nexcl;
 +            ctwelve /= npair - nexcl;
 +        }
 +        if (debug) {
 +            fprintf(debug,"Counted %d exclusions\n",nexcl);
 +            fprintf(debug,"Average C6 parameter is: %10g\n",(double)csix);
 +            fprintf(debug,"Average C12 parameter is: %10g\n",(double)ctwelve);
 +        }
 +        fr->avcsix[q]    = csix;
 +        fr->avctwelve[q] = ctwelve;
 +    }
 +    if (fplog != NULL)
 +    {
 +        if (fr->eDispCorr == edispcAllEner ||
 +            fr->eDispCorr == edispcAllEnerPres)
 +        {
 +            fprintf(fplog,"Long Range LJ corr.: <C6> %10.4e, <C12> %10.4e\n",
 +                    fr->avcsix[0],fr->avctwelve[0]);
 +        }
 +        else
 +        {
 +            fprintf(fplog,"Long Range LJ corr.: <C6> %10.4e\n",fr->avcsix[0]);
 +        }
 +    }
 +}
 +
 +
 +static void set_bham_b_max(FILE *fplog,t_forcerec *fr,
 +                           const gmx_mtop_t *mtop)
 +{
 +    const t_atoms *at1,*at2;
 +    int  mt1,mt2,i,j,tpi,tpj,ntypes;
 +    real b,bmin;
 +    real *nbfp;
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog,"Determining largest Buckingham b parameter for table\n");
 +    }
 +    nbfp   = fr->nbfp;
 +    ntypes = fr->ntype;
 +    
 +    bmin           = -1;
 +    fr->bham_b_max = 0;
 +    for(mt1=0; mt1<mtop->nmoltype; mt1++)
 +    {
 +        at1 = &mtop->moltype[mt1].atoms;
 +        for(i=0; (i<at1->nr); i++)
 +        {
 +            tpi = at1->atom[i].type;
 +            if (tpi >= ntypes)
 +                gmx_fatal(FARGS,"Atomtype[%d] = %d, maximum = %d",i,tpi,ntypes);
 +            
 +            for(mt2=mt1; mt2<mtop->nmoltype; mt2++)
 +            {
 +                at2 = &mtop->moltype[mt2].atoms;
 +                for(j=0; (j<at2->nr); j++) {
 +                    tpj = at2->atom[j].type;
 +                    if (tpj >= ntypes)
 +                    {
 +                        gmx_fatal(FARGS,"Atomtype[%d] = %d, maximum = %d",j,tpj,ntypes);
 +                    }
 +                    b = BHAMB(nbfp,ntypes,tpi,tpj);
 +                    if (b > fr->bham_b_max)
 +                    {
 +                        fr->bham_b_max = b;
 +                    }
 +                    if ((b < bmin) || (bmin==-1))
 +                    {
 +                        bmin = b;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    if (fplog)
 +    {
 +        fprintf(fplog,"Buckingham b parameters, min: %g, max: %g\n",
 +                bmin,fr->bham_b_max);
 +    }
 +}
 +
 +static void make_nbf_tables(FILE *fp,const output_env_t oenv,
 +                            t_forcerec *fr,real rtab,
 +                          const t_commrec *cr,
 +                          const char *tabfn,char *eg1,char *eg2,
 +                          t_nblists *nbl)
 +{
 +  char buf[STRLEN];
 +  int i,j;
 +
 +  if (tabfn == NULL) {
 +    if (debug)
 +      fprintf(debug,"No table file name passed, can not read table, can not do non-bonded interactions\n");
 +    return;
 +  }
 +    
 +  sprintf(buf,"%s",tabfn);
 +  if (eg1 && eg2)
 +    /* Append the two energy group names */
 +    sprintf(buf + strlen(tabfn) - strlen(ftp2ext(efXVG)) - 1,"_%s_%s.%s",
 +          eg1,eg2,ftp2ext(efXVG));
 +  nbl->tab = make_tables(fp,oenv,fr,MASTER(cr),buf,rtab,0);
 +  /* Copy the contents of the table to separate coulomb and LJ tables too,
 +   * to improve cache performance.
 +   */
 +
 +  /* For performance reasons we want
 +   * the table data to be aligned to 16-byte. The pointer could be freed
 +   * but currently isn't.
 +   */
 +  snew_aligned(nbl->vdwtab,8*(nbl->tab.n+1),16);
 +  snew_aligned(nbl->coultab,4*(nbl->tab.n+1),16);
 +  
 +  for(i=0; i<=nbl->tab.n; i++) {
 +    for(j=0; j<4; j++)
 +      nbl->coultab[4*i+j] = nbl->tab.tab[12*i+j];
 +    for(j=0; j<8; j++)
 +      nbl->vdwtab [8*i+j] = nbl->tab.tab[12*i+4+j];
 +  }
 +}
 +
 +static void count_tables(int ftype1,int ftype2,const gmx_mtop_t *mtop,
 +                         int *ncount,int **count)
 +{
 +    const gmx_moltype_t *molt;
 +    const t_ilist *il;
 +    int mt,ftype,stride,i,j,tabnr;
 +    
 +    for(mt=0; mt<mtop->nmoltype; mt++)
 +    {
 +        molt = &mtop->moltype[mt];
 +        for(ftype=0; ftype<F_NRE; ftype++)
 +        {
 +            if (ftype == ftype1 || ftype == ftype2) {
 +                il = &molt->ilist[ftype];
 +                stride = 1 + NRAL(ftype);
 +                for(i=0; i<il->nr; i+=stride) {
 +                    tabnr = mtop->ffparams.iparams[il->iatoms[i]].tab.table;
 +                    if (tabnr < 0)
 +                        gmx_fatal(FARGS,"A bonded table number is smaller than 0: %d\n",tabnr);
 +                    if (tabnr >= *ncount) {
 +                        srenew(*count,tabnr+1);
 +                        for(j=*ncount; j<tabnr+1; j++)
 +                            (*count)[j] = 0;
 +                        *ncount = tabnr+1;
 +                    }
 +                    (*count)[tabnr]++;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static bondedtable_t *make_bonded_tables(FILE *fplog,
 +                                         int ftype1,int ftype2,
 +                                         const gmx_mtop_t *mtop,
 +                                         const char *basefn,const char *tabext)
 +{
 +    int  i,ncount,*count;
 +    char tabfn[STRLEN];
 +    bondedtable_t *tab;
 +    
 +    tab = NULL;
 +    
 +    ncount = 0;
 +    count = NULL;
 +    count_tables(ftype1,ftype2,mtop,&ncount,&count);
 +    
 +    if (ncount > 0) {
 +        snew(tab,ncount);
 +        for(i=0; i<ncount; i++) {
 +            if (count[i] > 0) {
 +                sprintf(tabfn,"%s",basefn);
 +                sprintf(tabfn + strlen(basefn) - strlen(ftp2ext(efXVG)) - 1,"_%s%d.%s",
 +                        tabext,i,ftp2ext(efXVG));
 +                tab[i] = make_bonded_table(fplog,tabfn,NRAL(ftype1)-2);
 +            }
 +        }
 +        sfree(count);
 +    }
 +  
 +    return tab;
 +}
 +
 +void forcerec_set_ranges(t_forcerec *fr,
 +                         int ncg_home,int ncg_force,
 +                         int natoms_force,
 +                         int natoms_force_constr,int natoms_f_novirsum)
 +{
 +    fr->cg0 = 0;
 +    fr->hcg = ncg_home;
 +
 +    /* fr->ncg_force is unused in the standard code,
 +     * but it can be useful for modified code dealing with charge groups.
 +     */
 +    fr->ncg_force           = ncg_force;
 +    fr->natoms_force        = natoms_force;
 +    fr->natoms_force_constr = natoms_force_constr;
 +
 +    if (fr->natoms_force_constr > fr->nalloc_force)
 +    {
 +        fr->nalloc_force = over_alloc_dd(fr->natoms_force_constr);
 +
 +        if (fr->bTwinRange)
 +        {
 +            srenew(fr->f_twin,fr->nalloc_force);
 +        }
 +    }
 +
 +    if (fr->bF_NoVirSum)
 +    {
 +        fr->f_novirsum_n = natoms_f_novirsum;
 +        if (fr->f_novirsum_n > fr->f_novirsum_nalloc)
 +        {
 +            fr->f_novirsum_nalloc = over_alloc_dd(fr->f_novirsum_n);
 +            srenew(fr->f_novirsum_alloc,fr->f_novirsum_nalloc);
 +        }
 +    }
 +    else
 +    {
 +        fr->f_novirsum_n = 0;
 +    }
 +}
 +
 +static real cutoff_inf(real cutoff)
 +{
 +    if (cutoff == 0)
 +    {
 +        cutoff = GMX_CUTOFF_INF;
 +    }
 +
 +    return cutoff;
 +}
 +
 +static void make_adress_tf_tables(FILE *fp,const output_env_t oenv,
 +                            t_forcerec *fr,const t_inputrec *ir,
 +                          const char *tabfn, const gmx_mtop_t *mtop,
 +                            matrix     box)
 +{
 +  char buf[STRLEN];
 +  int i,j;
 +
 +  if (tabfn == NULL) {
 +        gmx_fatal(FARGS,"No thermoforce table file given. Use -tabletf to specify a file\n");
 +    return;
 +  }
 +
 +  snew(fr->atf_tabs, ir->adress->n_tf_grps);
 +
 +  for (i=0; i<ir->adress->n_tf_grps; i++){
 +    j = ir->adress->tf_table_index[i]; /* get energy group index */
 +    sprintf(buf + strlen(tabfn) - strlen(ftp2ext(efXVG)) - 1,"tf_%s.%s",
 +        *(mtop->groups.grpname[mtop->groups.grps[egcENER].nm_ind[j]]) ,ftp2ext(efXVG));
 +    printf("loading tf table for energygrp index %d from %s\n", ir->adress->tf_table_index[j], buf);
 +    fr->atf_tabs[i] = make_atf_table(fp,oenv,fr,buf, box);
 +  }
 +
 +}
 +
 +gmx_bool can_use_allvsall(const t_inputrec *ir, const gmx_mtop_t *mtop,
 +                      gmx_bool bPrintNote,t_commrec *cr,FILE *fp)
 +{
 +    gmx_bool bAllvsAll;
 +
 +    bAllvsAll =
 +        (
 +         ir->rlist==0            &&
 +         ir->rcoulomb==0         &&
 +         ir->rvdw==0             &&
 +         ir->ePBC==epbcNONE      &&
 +         ir->vdwtype==evdwCUT    &&
 +         ir->coulombtype==eelCUT &&
 +         ir->efep==efepNO        &&
 +         (ir->implicit_solvent == eisNO || 
 +          (ir->implicit_solvent==eisGBSA && (ir->gb_algorithm==egbSTILL || 
 +                                             ir->gb_algorithm==egbHCT   || 
 +                                             ir->gb_algorithm==egbOBC))) &&
 +         getenv("GMX_NO_ALLVSALL") == NULL
 +            );
 +    
 +    if (bAllvsAll && ir->opts.ngener > 1)
 +    {
 +        const char *note="NOTE: Can not use all-vs-all force loops, because there are multiple energy monitor groups; you might get significantly higher performance when using only a single energy monitor group.\n";
 +
 +        if (bPrintNote)
 +        {
 +            if (MASTER(cr))
 +            {
 +                fprintf(stderr,"\n%s\n",note);
 +            }
 +            if (fp != NULL)
 +            {
 +                fprintf(fp,"\n%s\n",note);
 +            }
 +        }
 +        bAllvsAll = FALSE;
 +    }
 +
 +    if(bAllvsAll && fp && MASTER(cr))
 +    {
 +        fprintf(fp,"\nUsing accelerated all-vs-all kernels.\n\n");
 +    }
 +    
 +    return bAllvsAll;
 +}
 +
 +
 +
 +void init_forcerec(FILE *fp,
 +                   const output_env_t oenv,
 +                   t_forcerec *fr,
 +                   t_fcdata   *fcd,
 +                   const t_inputrec *ir,
 +                   const gmx_mtop_t *mtop,
 +                   const t_commrec  *cr,
 +                   matrix     box,
 +                   gmx_bool       bMolEpot,
 +                   const char *tabfn,
 +                   const char *tabafn,
 +                   const char *tabpfn,
 +                   const char *tabbfn,
 +                   gmx_bool       bNoSolvOpt,
 +                   real       print_force)
 +{
 +    int     i,j,m,natoms,ngrp,negp_pp,negptable,egi,egj;
 +    real    rtab;
 +    char    *env;
 +    double  dbl;
 +    rvec    box_size;
 +    const t_block *cgs;
 +    gmx_bool    bGenericKernelOnly;
 +    gmx_bool    bTab,bSep14tab,bNormalnblists;
 +    t_nblists *nbl;
 +    int     *nm_ind,egp_flags;
 +    
 +    gmx_detectcpu(&fr->cpu_information);
 +    if(MASTER(cr))
 +    {
 +        /* Only print warnings from master */
 +        gmx_detectcpu_check_acceleration(fr->cpu_information,fp);
 +    }
 +
 +    /* By default we turn acceleration on, but it might be turned off further down... */
 +    fr->use_acceleration = TRUE;
 +
 +    fr->bDomDec = DOMAINDECOMP(cr);
 +
 +    natoms = mtop->natoms;
 +
 +    if (check_box(ir->ePBC,box))
 +    {
 +        gmx_fatal(FARGS,check_box(ir->ePBC,box));
 +    }
 +    
 +    /* Test particle insertion ? */
 +    if (EI_TPI(ir->eI)) {
 +        /* Set to the size of the molecule to be inserted (the last one) */
 +        /* Because of old style topologies, we have to use the last cg
 +         * instead of the last molecule type.
 +         */
 +        cgs = &mtop->moltype[mtop->molblock[mtop->nmolblock-1].type].cgs;
 +        fr->n_tpi = cgs->index[cgs->nr] - cgs->index[cgs->nr-1];
 +        if (fr->n_tpi != mtop->mols.index[mtop->mols.nr] - mtop->mols.index[mtop->mols.nr-1]) {
 +            gmx_fatal(FARGS,"The molecule to insert can not consist of multiple charge groups.\nMake it a single charge group.");
 +        }
 +    } else {
 +        fr->n_tpi = 0;
 +    }
 +    
 +    /* Copy AdResS parameters */
 +    if (ir->bAdress) {
 +      fr->adress_type     = ir->adress->type;
 +      fr->adress_const_wf = ir->adress->const_wf;
 +      fr->adress_ex_width = ir->adress->ex_width;
 +      fr->adress_hy_width = ir->adress->hy_width;
 +      fr->adress_icor     = ir->adress->icor;
 +      fr->adress_site     = ir->adress->site;
 +      fr->adress_ex_forcecap = ir->adress->ex_forcecap;
 +      fr->adress_do_hybridpairs = ir->adress->do_hybridpairs;
 +
 +
 +      snew(fr->adress_group_explicit , ir->adress->n_energy_grps);
 +      for (i=0; i< ir->adress->n_energy_grps; i++){
 +          fr->adress_group_explicit[i]= ir->adress->group_explicit[i];
 +      }
 +
 +      fr->n_adress_tf_grps = ir->adress->n_tf_grps;
 +      snew(fr->adress_tf_table_index, fr->n_adress_tf_grps);
 +      for (i=0; i< fr->n_adress_tf_grps; i++){
 +          fr->adress_tf_table_index[i]= ir->adress->tf_table_index[i];
 +      }
 +      copy_rvec(ir->adress->refs,fr->adress_refs);
 +    } else {
 +      fr->adress_type = eAdressOff;
 +      fr->adress_do_hybridpairs = FALSE;
 +    }
 +    
 +    /* Copy the user determined parameters */
 +    fr->userint1 = ir->userint1;
 +    fr->userint2 = ir->userint2;
 +    fr->userint3 = ir->userint3;
 +    fr->userint4 = ir->userint4;
 +    fr->userreal1 = ir->userreal1;
 +    fr->userreal2 = ir->userreal2;
 +    fr->userreal3 = ir->userreal3;
 +    fr->userreal4 = ir->userreal4;
 +    
 +    /* Shell stuff */
 +    fr->fc_stepsize = ir->fc_stepsize;
 +    
 +    /* Free energy */
 +    fr->efep       = ir->efep;
 +    fr->sc_alphavdw = ir->fepvals->sc_alpha;
 +    if (ir->fepvals->bScCoul)
 +    {
 +        fr->sc_alphacoul = ir->fepvals->sc_alpha;
 +        fr->sc_sigma6_min = pow(ir->fepvals->sc_sigma_min,6);
 +    }
 +    else
 +    {
 +        fr->sc_alphacoul = 0;
 +        fr->sc_sigma6_min = 0; /* only needed when bScCoul is on */
 +    }
 +    fr->sc_power   = ir->fepvals->sc_power;
 +    fr->sc_r_power   = ir->fepvals->sc_r_power;
 +    fr->sc_sigma6_def = pow(ir->fepvals->sc_sigma,6);
 +
 +    env = getenv("GMX_SCSIGMA_MIN");
 +    if (env != NULL)
 +    {
 +        dbl = 0;
 +        sscanf(env,"%lf",&dbl);
 +        fr->sc_sigma6_min = pow(dbl,6);
 +        if (fp)
 +        {
 +            fprintf(fp,"Setting the minimum soft core sigma to %g nm\n",dbl);
 +        }
 +    }
 +
 +    bGenericKernelOnly = FALSE;
 +    if (getenv("GMX_NB_GENERIC") != NULL)
 +    {
 +        if (fp != NULL)
 +        {
 +            fprintf(fp,
 +                    "Found environment variable GMX_NB_GENERIC.\n"
 +                    "Disabling interaction-specific nonbonded kernels.\n\n");
 +        }
 +        bGenericKernelOnly = TRUE;
 +        bNoSolvOpt         = TRUE;
 +    }
 +    
-     if (getenv("GMX_FORCE_TABLES"))
++    if (getenv("GMX_DISABLE_ACCELERATION") != NULL)
 +    {
 +        fr->use_acceleration = FALSE;
 +        if (fp != NULL)
 +        {
 +            fprintf(fp,
 +                    "\nFound environment variable GMX_DISABLE_ACCELERATION.\n"
 +                    "Disabling all architecture-specific (e.g. SSE2/SSE4/AVX) routines.\n\n");
 +        }
 +    }
 +
 +    /* Check if we can/should do all-vs-all kernels */
 +    fr->bAllvsAll       = can_use_allvsall(ir,mtop,FALSE,NULL,NULL);
 +    fr->AllvsAll_work   = NULL;
 +    fr->AllvsAll_workgb = NULL;
 +
 +    
 +    
 +    /* Neighbour searching stuff */
 +    fr->bGrid      = (ir->ns_type == ensGRID);
 +    fr->ePBC       = ir->ePBC;
 +    fr->bMolPBC    = ir->bPeriodicMols;
 +    fr->rc_scaling = ir->refcoord_scaling;
 +    copy_rvec(ir->posres_com,fr->posres_com);
 +    copy_rvec(ir->posres_comB,fr->posres_comB);
 +    fr->rlist      = cutoff_inf(ir->rlist);
 +    fr->rlistlong  = cutoff_inf(ir->rlistlong);
 +    fr->eeltype    = ir->coulombtype;
 +    fr->vdwtype    = ir->vdwtype;
 +    
 +    fr->bTwinRange = fr->rlistlong > fr->rlist;
 +    fr->bEwald     = (EEL_PME(fr->eeltype) || fr->eeltype==eelEWALD);
 +    
 +    fr->reppow     = mtop->ffparams.reppow;
 +    fr->bvdwtab    = (fr->vdwtype != evdwCUT ||
 +                      !gmx_within_tol(fr->reppow,12.0,10*GMX_DOUBLE_EPS));
 +    fr->bcoultab   = (!(fr->eeltype == eelCUT || EEL_RF(fr->eeltype)) ||
 +                      fr->eeltype == eelRF_ZERO);
 +    
++    if (getenv("GMX_REQUIRE_TABLES"))
 +    {
 +        fr->bvdwtab  = TRUE;
 +        fr->bcoultab = TRUE;
 +    }
 +    
 +    if (fp) {
 +        fprintf(fp,"Table routines are used for coulomb: %s\n",bool_names[fr->bcoultab]);
 +        fprintf(fp,"Table routines are used for vdw:     %s\n",bool_names[fr->bvdwtab ]);
 +    }
 +    
 +    /* Tables are used for direct ewald sum */
 +    if(fr->bEwald)
 +    {
 +        if (EEL_PME(ir->coulombtype))
 +        {
 +            if (fp)
 +                fprintf(fp,"Will do PME sum in reciprocal space.\n");
 +            if (ir->coulombtype == eelP3M_AD)
 +            {
 +                please_cite(fp,"Hockney1988");
 +                please_cite(fp,"Ballenegger2012");
 +            }
 +            else
 +            {
 +                please_cite(fp,"Essmann95a");
 +            }
 +            
 +            if (ir->ewald_geometry == eewg3DC)
 +            {
 +                if (fp)
 +                {
 +                    fprintf(fp,"Using the Ewald3DC correction for systems with a slab geometry.\n");
 +                }
 +                please_cite(fp,"In-Chul99a");
 +            }
 +        }
 +        fr->ewaldcoeff=calc_ewaldcoeff(ir->rcoulomb, ir->ewald_rtol);
 +        init_ewald_tab(&(fr->ewald_table), cr, ir, fp);
 +        if (fp)
 +        {
 +            fprintf(fp,"Using a Gaussian width (1/beta) of %g nm for Ewald\n",
 +                    1/fr->ewaldcoeff);
 +        }
 +    }
 +    
 +    /* Electrostatics */
 +    fr->epsilon_r  = ir->epsilon_r;
 +    fr->epsilon_rf = ir->epsilon_rf;
 +    fr->fudgeQQ    = mtop->ffparams.fudgeQQ;
 +    fr->rcoulomb_switch = ir->rcoulomb_switch;
 +    fr->rcoulomb        = cutoff_inf(ir->rcoulomb);
 +    
 +    /* Parameters for generalized RF */
 +    fr->zsquare = 0.0;
 +    fr->temp    = 0.0;
 +    
 +    if (fr->eeltype == eelGRF)
 +    {
 +        init_generalized_rf(fp,mtop,ir,fr);
 +    }
 +    else if (fr->eeltype == eelSHIFT)
 +    {
 +        for(m=0; (m<DIM); m++)
 +            box_size[m]=box[m][m];
 +        
 +        if ((fr->eeltype == eelSHIFT && fr->rcoulomb > fr->rcoulomb_switch))
 +            set_shift_consts(fp,fr->rcoulomb_switch,fr->rcoulomb,box_size,fr);
 +    }
 +    
 +    fr->bF_NoVirSum = (EEL_FULL(fr->eeltype) ||
 +                       gmx_mtop_ftype_count(mtop,F_POSRES) > 0 ||
 +                       gmx_mtop_ftype_count(mtop,F_FBPOSRES) > 0 ||
 +                       IR_ELEC_FIELD(*ir) ||
 +                       (fr->adress_icor != eAdressICOff)
 +                      );
 +    
 +    /* Mask that says whether or not this NBF list should be computed */
 +    /*  if (fr->bMask == NULL) {
 +        ngrp = ir->opts.ngener*ir->opts.ngener;
 +        snew(fr->bMask,ngrp);*/
 +    /* Defaults to always */
 +    /*    for(i=0; (i<ngrp); i++)
 +          fr->bMask[i] = TRUE;
 +          }*/
 +    
 +    if (ncg_mtop(mtop) > fr->cg_nalloc && !DOMAINDECOMP(cr)) {
 +        /* Count the total number of charge groups */
 +        fr->cg_nalloc = ncg_mtop(mtop);
 +        srenew(fr->cg_cm,fr->cg_nalloc);
 +    }
 +    if (fr->shift_vec == NULL)
 +        snew(fr->shift_vec,SHIFTS);
 +    
 +    if (fr->fshift == NULL)
 +        snew(fr->fshift,SHIFTS);
 +    
 +    if (fr->nbfp == NULL) {
 +        fr->ntype = mtop->ffparams.atnr;
 +        fr->bBHAM = (mtop->ffparams.functype[0] == F_BHAM);
 +        fr->nbfp  = mk_nbfp(&mtop->ffparams,fr->bBHAM);
 +    }
 +    
 +    /* Copy the energy group exclusions */
 +    fr->egp_flags = ir->opts.egp_flags;
 +    
 +    /* Van der Waals stuff */
 +    fr->rvdw        = cutoff_inf(ir->rvdw);
 +    fr->rvdw_switch = ir->rvdw_switch;
 +    if ((fr->vdwtype != evdwCUT) && (fr->vdwtype != evdwUSER) && !fr->bBHAM) {
 +        if (fr->rvdw_switch >= fr->rvdw)
 +            gmx_fatal(FARGS,"rvdw_switch (%f) must be < rvdw (%f)",
 +                      fr->rvdw_switch,fr->rvdw);
 +        if (fp)
 +            fprintf(fp,"Using %s Lennard-Jones, switch between %g and %g nm\n",
 +                    (fr->eeltype==eelSWITCH) ? "switched":"shifted",
 +                    fr->rvdw_switch,fr->rvdw);
 +    } 
 +    
 +    if (fr->bBHAM && (fr->vdwtype == evdwSHIFT || fr->vdwtype == evdwSWITCH))
 +        gmx_fatal(FARGS,"Switch/shift interaction not supported with Buckingham");
 +    
 +    if (fp)
 +        fprintf(fp,"Cut-off's:   NS: %g   Coulomb: %g   %s: %g\n",
 +                fr->rlist,fr->rcoulomb,fr->bBHAM ? "BHAM":"LJ",fr->rvdw);
 +    
 +    fr->eDispCorr = ir->eDispCorr;
 +    if (ir->eDispCorr != edispcNO)
 +    {
 +        set_avcsixtwelve(fp,fr,mtop);
 +    }
 +    
 +    if (fr->bBHAM)
 +    {
 +        set_bham_b_max(fp,fr,mtop);
 +    }
 +
 +    fr->bGB = (ir->implicit_solvent == eisGBSA);
 +      fr->gb_epsilon_solvent = ir->gb_epsilon_solvent;
 +
 +    /* Copy the GBSA data (radius, volume and surftens for each
 +     * atomtype) from the topology atomtype section to forcerec.
 +     */
 +    snew(fr->atype_radius,fr->ntype);
 +    snew(fr->atype_vol,fr->ntype);
 +    snew(fr->atype_surftens,fr->ntype);
 +    snew(fr->atype_gb_radius,fr->ntype);
 +    snew(fr->atype_S_hct,fr->ntype);
 +
 +    if (mtop->atomtypes.nr > 0)
 +    {
 +        for(i=0;i<fr->ntype;i++)
 +            fr->atype_radius[i] =mtop->atomtypes.radius[i];
 +        for(i=0;i<fr->ntype;i++)
 +            fr->atype_vol[i] = mtop->atomtypes.vol[i];
 +        for(i=0;i<fr->ntype;i++)
 +            fr->atype_surftens[i] = mtop->atomtypes.surftens[i];
 +        for(i=0;i<fr->ntype;i++)
 +            fr->atype_gb_radius[i] = mtop->atomtypes.gb_radius[i];
 +        for(i=0;i<fr->ntype;i++)
 +            fr->atype_S_hct[i] = mtop->atomtypes.S_hct[i];
 +    }  
 +      
 +      /* Generate the GB table if needed */
 +      if(fr->bGB)
 +      {
 +#ifdef GMX_DOUBLE
 +              fr->gbtabscale=2000;
 +#else
 +              fr->gbtabscale=500;
 +#endif
 +              
 +              fr->gbtabr=100;
 +              fr->gbtab=make_gb_table(fp,oenv,fr,tabpfn,fr->gbtabscale);
 +
 +        init_gb(&fr->born,cr,fr,ir,mtop,ir->rgbradii,ir->gb_algorithm);
 +
 +        /* Copy local gb data (for dd, this is done in dd_partition_system) */
 +        if (!DOMAINDECOMP(cr))
 +        {
 +            make_local_gb(cr,fr->born,ir->gb_algorithm);
 +        }
 +    }
 +
 +    /* Set the charge scaling */
 +    if (fr->epsilon_r != 0)
 +        fr->epsfac = ONE_4PI_EPS0/fr->epsilon_r;
 +    else
 +        /* eps = 0 is infinite dieletric: no coulomb interactions */
 +        fr->epsfac = 0;
 +    
 +    /* Reaction field constants */
 +    if (EEL_RF(fr->eeltype))
 +        calc_rffac(fp,fr->eeltype,fr->epsilon_r,fr->epsilon_rf,
 +                   fr->rcoulomb,fr->temp,fr->zsquare,box,
 +                   &fr->kappa,&fr->k_rf,&fr->c_rf);
 +    
 +    set_chargesum(fp,fr,mtop);
 +    
 +    /* if we are using LR electrostatics, and they are tabulated,
 +     * the tables will contain modified coulomb interactions.
 +     * Since we want to use the non-shifted ones for 1-4
 +     * coulombic interactions, we must have an extra set of tables.
 +     */
 +    
 +    /* Construct tables.
 +     * A little unnecessary to make both vdw and coul tables sometimes,
 +     * but what the heck... */
 +    
 +    bTab = fr->bcoultab || fr->bvdwtab;
 +
 +    bSep14tab = ((!bTab || fr->eeltype!=eelCUT || fr->vdwtype!=evdwCUT ||
 +                  fr->bBHAM) &&
 +                 (gmx_mtop_ftype_count(mtop,F_LJ14) > 0 ||
 +                  gmx_mtop_ftype_count(mtop,F_LJC14_Q) > 0 ||
 +                  gmx_mtop_ftype_count(mtop,F_LJC_PAIRS_NB) > 0));
 +
 +    negp_pp = ir->opts.ngener - ir->nwall;
 +    negptable = 0;
 +    if (!bTab) {
 +        bNormalnblists = TRUE;
 +        fr->nnblists = 1;
 +    } else {
 +        bNormalnblists = (ir->eDispCorr != edispcNO);
 +        for(egi=0; egi<negp_pp; egi++) {
 +            for(egj=egi;  egj<negp_pp; egj++) {
 +                egp_flags = ir->opts.egp_flags[GID(egi,egj,ir->opts.ngener)];
 +                if (!(egp_flags & EGP_EXCL)) {
 +                    if (egp_flags & EGP_TABLE) {
 +                        negptable++;
 +                    } else {
 +                        bNormalnblists = TRUE;
 +                    }
 +                }
 +            }
 +        }
 +        if (bNormalnblists) {
 +            fr->nnblists = negptable + 1;
 +        } else {
 +            fr->nnblists = negptable;
 +        }
 +        if (fr->nnblists > 1)
 +            snew(fr->gid2nblists,ir->opts.ngener*ir->opts.ngener);
 +    }
 +    snew(fr->nblists,fr->nnblists);
 +    
 +    /* This code automatically gives table length tabext without cut-off's,
 +     * in that case grompp should already have checked that we do not need
 +     * normal tables and we only generate tables for 1-4 interactions.
 +     */
 +    rtab = ir->rlistlong + ir->tabext;
 +
 +    if (bTab) {
 +        /* make tables for ordinary interactions */
 +        if (bNormalnblists) {
 +            make_nbf_tables(fp,oenv,fr,rtab,cr,tabfn,NULL,NULL,&fr->nblists[0]);
 +            if (!bSep14tab)
 +                fr->tab14 = fr->nblists[0].tab;
 +            m = 1;
 +        } else {
 +            m = 0;
 +        }
 +        if (negptable > 0) {
 +            /* Read the special tables for certain energy group pairs */
 +            nm_ind = mtop->groups.grps[egcENER].nm_ind;
 +            for(egi=0; egi<negp_pp; egi++) {
 +                for(egj=egi;  egj<negp_pp; egj++) {
 +                    egp_flags = ir->opts.egp_flags[GID(egi,egj,ir->opts.ngener)];
 +                    if ((egp_flags & EGP_TABLE) && !(egp_flags & EGP_EXCL)) {
 +                        nbl = &(fr->nblists[m]);
 +                        if (fr->nnblists > 1) {
 +                            fr->gid2nblists[GID(egi,egj,ir->opts.ngener)] = m;
 +                        }
 +                        /* Read the table file with the two energy groups names appended */
 +                        make_nbf_tables(fp,oenv,fr,rtab,cr,tabfn,
 +                                        *mtop->groups.grpname[nm_ind[egi]],
 +                                        *mtop->groups.grpname[nm_ind[egj]],
 +                                        &fr->nblists[m]);
 +                        m++;
 +                    } else if (fr->nnblists > 1) {
 +                        fr->gid2nblists[GID(egi,egj,ir->opts.ngener)] = 0;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    if (bSep14tab)
 +    {
 +        /* generate extra tables with plain Coulomb for 1-4 interactions only */
 +        fr->tab14 = make_tables(fp,oenv,fr,MASTER(cr),tabpfn,rtab,
 +                                GMX_MAKETABLES_14ONLY);
 +    }
 +
 +    /* Read AdResS Thermo Force table if needed */
 +    if(fr->adress_icor == eAdressICThermoForce)
 +    {
 +        /* old todo replace */ 
 +        
 +        if (ir->adress->n_tf_grps > 0){
 +            make_adress_tf_tables(fp,oenv,fr,ir,tabfn, mtop, box);
 +
 +        }else{
 +            /* load the default table */
 +            snew(fr->atf_tabs, 1);
 +            fr->atf_tabs[DEFAULT_TF_TABLE] = make_atf_table(fp,oenv,fr,tabafn, box);
 +        }
 +    }
 +    
 +    /* Wall stuff */
 +    fr->nwall = ir->nwall;
 +    if (ir->nwall && ir->wall_type==ewtTABLE)
 +    {
 +        make_wall_tables(fp,oenv,ir,tabfn,&mtop->groups,fr);
 +    }
 +    
 +    if (fcd && tabbfn) {
 +        fcd->bondtab  = make_bonded_tables(fp,
 +                                           F_TABBONDS,F_TABBONDSNC,
 +                                           mtop,tabbfn,"b");
 +        fcd->angletab = make_bonded_tables(fp,
 +                                           F_TABANGLES,-1,
 +                                           mtop,tabbfn,"a");
 +        fcd->dihtab   = make_bonded_tables(fp,
 +                                           F_TABDIHS,-1,
 +                                           mtop,tabbfn,"d");
 +    } else {
 +        if (debug)
 +            fprintf(debug,"No fcdata or table file name passed, can not read table, can not do bonded interactions\n");
 +    }
 +    
 +    if (ir->eDispCorr != edispcNO)
 +    {
 +        calc_enervirdiff(fp,ir->eDispCorr,fr);
 +    }
 +
 +    /* QM/MM initialization if requested
 +     */
 +    if (ir->bQMMM)
 +    {
 +        fprintf(stderr,"QM/MM calculation requested.\n");
 +    }
 +    
 +    fr->bQMMM      = ir->bQMMM;   
 +    fr->qr         = mk_QMMMrec();
 +    
 +    /* Set all the static charge group info */
 +    fr->cginfo_mb = init_cginfo_mb(fp,mtop,fr,bNoSolvOpt);
 +
 +    if (DOMAINDECOMP(cr)) {
 +        fr->cginfo = NULL;
 +    } else {
 +        fr->cginfo = cginfo_expand(mtop->nmolblock,fr->cginfo_mb);
 +    }
 +    
 +    if (!DOMAINDECOMP(cr))
 +    {
 +        /* When using particle decomposition, the effect of the second argument,
 +         * which sets fr->hcg, is corrected later in do_md and init_em.
 +         */
 +        forcerec_set_ranges(fr,ncg_mtop(mtop),ncg_mtop(mtop),
 +                            mtop->natoms,mtop->natoms,mtop->natoms);
 +    }
 +    
 +    fr->print_force = print_force;
 +
 +
 +    /* coarse load balancing vars */
 +    fr->t_fnbf=0.;
 +    fr->t_wait=0.;
 +    fr->timesteps=0;
 +    
 +    /* Initialize neighbor search */
 +    init_ns(fp,cr,&fr->ns,fr,mtop,box);
 +    
 +    if (cr->duty & DUTY_PP){
 +        gmx_setup_kernels(fp,fr,bGenericKernelOnly);
 +        if (ir->bAdress)
 +            gmx_setup_adress_kernels(fp,bGenericKernelOnly);
 +    }
 +}
 +
 +#define pr_real(fp,r) fprintf(fp,"%s: %e\n",#r,r)
 +#define pr_int(fp,i)  fprintf((fp),"%s: %d\n",#i,i)
 +#define pr_bool(fp,b) fprintf((fp),"%s: %s\n",#b,bool_names[b])
 +
 +void pr_forcerec(FILE *fp,t_forcerec *fr,t_commrec *cr)
 +{
 +  int i;
 +
 +  pr_real(fp,fr->rlist);
 +  pr_real(fp,fr->rcoulomb);
 +  pr_real(fp,fr->fudgeQQ);
 +  pr_bool(fp,fr->bGrid);
 +  pr_bool(fp,fr->bTwinRange);
 +  /*pr_int(fp,fr->cg0);
 +    pr_int(fp,fr->hcg);*/
 +  for(i=0; i<fr->nnblists; i++)
 +    pr_int(fp,fr->nblists[i].tab.n);
 +  pr_real(fp,fr->rcoulomb_switch);
 +  pr_real(fp,fr->rcoulomb);
 +  
 +  fflush(fp);
 +}
index 63dfc8b9e3ce0e74e6d28a8f1a9885ba56e7e9bf,0000000000000000000000000000000000000000..d48ff48c241de96278c2dbfd82c3059e547d9258
mode 100644,000000..100644
--- /dev/null
@@@ -1,1442 -1,0 +1,1446 @@@
-         snew(md->dhc, 1);
 +/* -*- 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:
 + * GROwing Monsters And Cloning Shrimps
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <string.h>
 +#include <float.h>
 +#include "typedefs.h"
 +#include "string2.h"
 +#include "mdebin.h"
 +#include "smalloc.h"
 +#include "physics.h"
 +#include "enxio.h"
 +#include "vec.h"
 +#include "disre.h"
 +#include "main.h"
 +#include "network.h"
 +#include "names.h"
 +#include "orires.h"
 +#include "constr.h"
 +#include "mtop_util.h"
 +#include "xvgr.h"
 +#include "gmxfio.h"
 +#include "macros.h"
 +#include "mdrun.h"
 +#include "mdebin_bar.h"
 +
 +
 +static const char *conrmsd_nm[] = { "Constr. rmsd", "Constr.2 rmsd" };
 +
 +static const char *boxs_nm[] = { "Box-X", "Box-Y", "Box-Z" };
 +
 +static const char *tricl_boxs_nm[] = {
 +    "Box-XX", "Box-YY", "Box-ZZ",
 +    "Box-YX", "Box-ZX", "Box-ZY"
 +};
 +
 +static const char *vol_nm[] = { "Volume" };
 +
 +static const char *dens_nm[] = {"Density" };
 +
 +static const char *pv_nm[] = {"pV" };
 +
 +static const char *enthalpy_nm[] = {"Enthalpy" };
 +
 +static const char *boxvel_nm[] = {
 +    "Box-Vel-XX", "Box-Vel-YY", "Box-Vel-ZZ",
 +    "Box-Vel-YX", "Box-Vel-ZX", "Box-Vel-ZY"
 +};
 +
 +#define NBOXS asize(boxs_nm)
 +#define NTRICLBOXS asize(tricl_boxs_nm)
 +
 +t_mdebin *init_mdebin(ener_file_t fp_ene,
 +                      const gmx_mtop_t *mtop,
 +                      const t_inputrec *ir,
 +                      FILE *fp_dhdl)
 +{
 +    const char *ener_nm[F_NRE];
 +    static const char *vir_nm[] = {
 +        "Vir-XX", "Vir-XY", "Vir-XZ",
 +        "Vir-YX", "Vir-YY", "Vir-YZ",
 +        "Vir-ZX", "Vir-ZY", "Vir-ZZ"
 +    };
 +    static const char *sv_nm[] = {
 +        "ShakeVir-XX", "ShakeVir-XY", "ShakeVir-XZ",
 +        "ShakeVir-YX", "ShakeVir-YY", "ShakeVir-YZ",
 +        "ShakeVir-ZX", "ShakeVir-ZY", "ShakeVir-ZZ"
 +    };
 +    static const char *fv_nm[] = {
 +        "ForceVir-XX", "ForceVir-XY", "ForceVir-XZ",
 +        "ForceVir-YX", "ForceVir-YY", "ForceVir-YZ",
 +        "ForceVir-ZX", "ForceVir-ZY", "ForceVir-ZZ"
 +    };
 +    static const char *pres_nm[] = {
 +        "Pres-XX","Pres-XY","Pres-XZ",
 +        "Pres-YX","Pres-YY","Pres-YZ",
 +        "Pres-ZX","Pres-ZY","Pres-ZZ"
 +    };
 +    static const char *surft_nm[] = {
 +        "#Surf*SurfTen"
 +    };
 +    static const char *mu_nm[] = {
 +        "Mu-X", "Mu-Y", "Mu-Z"
 +    };
 +    static const char *vcos_nm[] = {
 +        "2CosZ*Vel-X"
 +    };
 +    static const char *visc_nm[] = {
 +        "1/Viscosity"
 +    };
 +    static const char *baro_nm[] = {
 +        "Barostat"
 +    };
 +
 +    char     **grpnms;
 +    const gmx_groups_t *groups;
 +    char     **gnm;
 +    char     buf[256];
 +    const char     *bufi;
 +    t_mdebin *md;
 +    int      i,j,ni,nj,n,nh,k,kk,ncon,nset;
 +    gmx_bool     bBHAM,bNoseHoover,b14;
 +
 +    snew(md,1);
 +
 +    md->bVir=TRUE;
 +    md->bPress=TRUE;
 +    md->bSurft=TRUE;
 +    md->bMu=TRUE;
 +
 +    if (EI_DYNAMICS(ir->eI))
 +    {
 +        md->delta_t = ir->delta_t;
 +    }
 +    else
 +    {
 +        md->delta_t = 0;
 +    }
 +
 +    groups = &mtop->groups;
 +
 +    bBHAM = (mtop->ffparams.functype[0] == F_BHAM);
 +    b14   = (gmx_mtop_ftype_count(mtop,F_LJ14) > 0 ||
 +             gmx_mtop_ftype_count(mtop,F_LJC14_Q) > 0);
 +
 +    ncon = gmx_mtop_ftype_count(mtop,F_CONSTR);
 +    nset = gmx_mtop_ftype_count(mtop,F_SETTLE);
 +    md->bConstr    = (ncon > 0 || nset > 0);
 +    md->bConstrVir = FALSE;
 +    if (md->bConstr) {
 +        if (ncon > 0 && ir->eConstrAlg == econtLINCS) {
 +            if (ir->eI == eiSD2)
 +                md->nCrmsd = 2;
 +            else
 +                md->nCrmsd = 1;
 +        }
 +        md->bConstrVir = (getenv("GMX_CONSTRAINTVIR") != NULL);
 +    } else {
 +        md->nCrmsd = 0;
 +    }
 +
 +    /* Energy monitoring */
 +    for(i=0;i<egNR;i++)
 +    {
 +        md->bEInd[i]=FALSE;
 +    }
 +
 +#ifndef GMX_OPENMM
 +    for(i=0; i<F_NRE; i++)
 +    {
 +        md->bEner[i] = FALSE;
 +        if (i == F_LJ)
 +            md->bEner[i] = !bBHAM;
 +        else if (i == F_BHAM)
 +            md->bEner[i] = bBHAM;
 +        else if (i == F_EQM)
 +            md->bEner[i] = ir->bQMMM;
 +        else if (i == F_COUL_LR)
 +            md->bEner[i] = (ir->rcoulomb > ir->rlist);
 +        else if (i == F_LJ_LR)
 +            md->bEner[i] = (!bBHAM && ir->rvdw > ir->rlist);
 +        else if (i == F_BHAM_LR)
 +            md->bEner[i] = (bBHAM && ir->rvdw > ir->rlist);
 +        else if (i == F_RF_EXCL)
 +            md->bEner[i] = (EEL_RF(ir->coulombtype) && ir->coulombtype != eelRF_NEC);
 +        else if (i == F_COUL_RECIP)
 +            md->bEner[i] = EEL_FULL(ir->coulombtype);
 +        else if (i == F_LJ14)
 +            md->bEner[i] = b14;
 +        else if (i == F_COUL14)
 +            md->bEner[i] = b14;
 +        else if (i == F_LJC14_Q || i == F_LJC_PAIRS_NB)
 +            md->bEner[i] = FALSE;
 +        else if ((i == F_DVDL_COUL && ir->fepvals->separate_dvdl[efptCOUL]) ||
 +                 (i == F_DVDL_VDW  && ir->fepvals->separate_dvdl[efptVDW]) ||
 +                 (i == F_DVDL_BONDED && ir->fepvals->separate_dvdl[efptBONDED]) ||
 +                 (i == F_DVDL_RESTRAINT && ir->fepvals->separate_dvdl[efptRESTRAINT]) ||
 +                 (i == F_DKDL && ir->fepvals->separate_dvdl[efptMASS]) ||
 +                 (i == F_DVDL && ir->fepvals->separate_dvdl[efptFEP]))
 +            md->bEner[i] = (ir->efep != efepNO);
 +        else if ((interaction_function[i].flags & IF_VSITE) ||
 +                 (i == F_CONSTR) || (i == F_CONSTRNC) || (i == F_SETTLE))
 +            md->bEner[i] = FALSE;
 +        else if ((i == F_COUL_SR) || (i == F_EPOT) || (i == F_PRES)  || (i==F_EQM))
 +            md->bEner[i] = TRUE;
 +        else if ((i == F_GBPOL) && ir->implicit_solvent==eisGBSA)
 +            md->bEner[i] = TRUE;
 +        else if ((i == F_NPSOLVATION) && ir->implicit_solvent==eisGBSA && (ir->sa_algorithm != esaNO))
 +            md->bEner[i] = TRUE;
 +        else if ((i == F_GB12) || (i == F_GB13) || (i == F_GB14))
 +            md->bEner[i] = FALSE;
 +        else if ((i == F_ETOT) || (i == F_EKIN) || (i == F_TEMP))
 +            md->bEner[i] = EI_DYNAMICS(ir->eI);
 +        else if (i==F_VTEMP)
 +            md->bEner[i] =  (EI_DYNAMICS(ir->eI) && getenv("GMX_VIRIAL_TEMPERATURE"));
 +        else if (i == F_DISPCORR || i == F_PDISPCORR)
 +            md->bEner[i] = (ir->eDispCorr != edispcNO);
 +        else if (i == F_DISRESVIOL)
 +            md->bEner[i] = (gmx_mtop_ftype_count(mtop,F_DISRES) > 0);
 +        else if (i == F_ORIRESDEV)
 +            md->bEner[i] = (gmx_mtop_ftype_count(mtop,F_ORIRES) > 0);
 +        else if (i == F_CONNBONDS)
 +            md->bEner[i] = FALSE;
 +        else if (i == F_COM_PULL)
 +            md->bEner[i] = (ir->ePull == epullUMBRELLA || ir->ePull == epullCONST_F || ir->bRot);
 +        else if (i == F_ECONSERVED)
 +            md->bEner[i] = ((ir->etc == etcNOSEHOOVER || ir->etc == etcVRESCALE) &&
 +                            (ir->epc == epcNO || ir->epc==epcMTTK));
 +        else
 +            md->bEner[i] = (gmx_mtop_ftype_count(mtop,i) > 0);
 +    }
 +#else
 +    /* OpenMM always produces only the following 4 energy terms */
 +    md->bEner[F_EPOT] = TRUE;
 +    md->bEner[F_EKIN] = TRUE;
 +    md->bEner[F_ETOT] = TRUE;
 +    md->bEner[F_TEMP] = TRUE;
 +#endif
 +
 +    /* for adress simulations, most energy terms are not meaningfull, and thus disabled*/
 +    if (ir->bAdress && !debug) {
 +        for (i = 0; i < F_NRE; i++) {
 +            md->bEner[i] = FALSE;
 +            if(i == F_EKIN){ md->bEner[i] = TRUE;}
 +            if(i == F_TEMP){ md->bEner[i] = TRUE;}
 +        }
 +        md->bVir=FALSE;
 +        md->bPress=FALSE;
 +        md->bSurft=FALSE;
 +        md->bMu=FALSE;
 +    }
 +
 +    md->f_nre=0;
 +    for(i=0; i<F_NRE; i++)
 +    {
 +        if (md->bEner[i])
 +        {
 +            /* FIXME: The constness should not be cast away */
 +            /*ener_nm[f_nre]=(char *)interaction_function[i].longname;*/
 +            ener_nm[md->f_nre]=interaction_function[i].longname;
 +            md->f_nre++;
 +        }
 +    }
 +
 +    md->epc = ir->epc;
 +    md->bDiagPres = !TRICLINIC(ir->ref_p);
 +    md->ref_p = (ir->ref_p[XX][XX]+ir->ref_p[YY][YY]+ir->ref_p[ZZ][ZZ])/DIM;
 +    md->bTricl = TRICLINIC(ir->compress) || TRICLINIC(ir->deform);
 +    md->bDynBox = DYNAMIC_BOX(*ir);
 +    md->etc = ir->etc;
 +    md->bNHC_trotter = IR_NVT_TROTTER(ir);
 +    md->bPrintNHChains = ir-> bPrintNHChains;
 +    md->bMTTK = (IR_NPT_TROTTER(ir) || IR_NPH_TROTTER(ir));
 +
 +    md->ebin  = mk_ebin();
 +    /* Pass NULL for unit to let get_ebin_space determine the units
 +     * for interaction_function[i].longname
 +     */
 +    md->ie    = get_ebin_space(md->ebin,md->f_nre,ener_nm,NULL);
 +    if (md->nCrmsd)
 +    {
 +        /* This should be called directly after the call for md->ie,
 +         * such that md->iconrmsd follows directly in the list.
 +         */
 +        md->iconrmsd = get_ebin_space(md->ebin,md->nCrmsd,conrmsd_nm,"");
 +    }
 +    if (md->bDynBox)
 +    {
 +        md->ib    = get_ebin_space(md->ebin,
 +                                   md->bTricl ? NTRICLBOXS : NBOXS,
 +                                   md->bTricl ? tricl_boxs_nm : boxs_nm,
 +                                   unit_length);
 +        md->ivol  = get_ebin_space(md->ebin, 1, vol_nm,  unit_volume);
 +        md->idens = get_ebin_space(md->ebin, 1, dens_nm, unit_density_SI);
 +        if (md->bDiagPres)
 +        {
 +            md->ipv   = get_ebin_space(md->ebin, 1, pv_nm,   unit_energy);
 +            md->ienthalpy = get_ebin_space(md->ebin, 1, enthalpy_nm,   unit_energy);
 +        }
 +    }
 +    if (md->bConstrVir)
 +    {
 +        md->isvir = get_ebin_space(md->ebin,asize(sv_nm),sv_nm,unit_energy);
 +        md->ifvir = get_ebin_space(md->ebin,asize(fv_nm),fv_nm,unit_energy);
 +    }
 +    if (md->bVir)
 +        md->ivir   = get_ebin_space(md->ebin,asize(vir_nm),vir_nm,unit_energy);
 +    if (md->bPress)
 +        md->ipres  = get_ebin_space(md->ebin,asize(pres_nm),pres_nm,unit_pres_bar);
 +    if (md->bSurft)
 +        md->isurft = get_ebin_space(md->ebin,asize(surft_nm),surft_nm,
 +                                unit_surft_bar);
 +    if (md->epc == epcPARRINELLORAHMAN || md->epc == epcMTTK)
 +    {
 +        md->ipc = get_ebin_space(md->ebin,md->bTricl ? 6 : 3,
 +                                 boxvel_nm,unit_vel);
 +    }
 +    if (md->bMu)
 +        md->imu    = get_ebin_space(md->ebin,asize(mu_nm),mu_nm,unit_dipole_D);
 +    if (ir->cos_accel != 0)
 +    {
 +        md->ivcos = get_ebin_space(md->ebin,asize(vcos_nm),vcos_nm,unit_vel);
 +        md->ivisc = get_ebin_space(md->ebin,asize(visc_nm),visc_nm,
 +                                   unit_invvisc_SI);
 +    }
 +
 +    /* Energy monitoring */
 +    for(i=0;i<egNR;i++)
 +    {
 +        md->bEInd[i] = FALSE;
 +    }
 +    md->bEInd[egCOULSR] = TRUE;
 +    md->bEInd[egLJSR  ] = TRUE;
 +
 +    if (ir->rcoulomb > ir->rlist)
 +    {
 +        md->bEInd[egCOULLR] = TRUE;
 +    }
 +    if (!bBHAM)
 +    {
 +        if (ir->rvdw > ir->rlist)
 +        {
 +            md->bEInd[egLJLR]   = TRUE;
 +        }
 +    }
 +    else
 +    {
 +        md->bEInd[egLJSR]   = FALSE;
 +        md->bEInd[egBHAMSR] = TRUE;
 +        if (ir->rvdw > ir->rlist)
 +        {
 +            md->bEInd[egBHAMLR]   = TRUE;
 +        }
 +    }
 +    if (b14)
 +    {
 +        md->bEInd[egLJ14] = TRUE;
 +        md->bEInd[egCOUL14] = TRUE;
 +    }
 +    md->nEc=0;
 +    for(i=0; (i<egNR); i++)
 +    {
 +        if (md->bEInd[i])
 +        {
 +            md->nEc++;
 +        }
 +    }
 +
 +    n=groups->grps[egcENER].nr;
 +    /* for adress simulations, most energy terms are not meaningfull, and thus disabled*/
 +    if (!ir->bAdress){
 +        /*standard simulation*/
 +        md->nEg=n;
 +        md->nE=(n*(n+1))/2;
 +    }
 +    else if (!debug) {
 +        /*AdResS simulation*/
 +       md->nU=0;
 +       md->nEg=0;
 +       md->nE=0;
 +       md->nEc=0;
 +       md->isvir=FALSE;
 +    }
 +    snew(md->igrp,md->nE);
 +    if (md->nE > 1)
 +    {
 +        n=0;
 +        snew(gnm,md->nEc);
 +        for(k=0; (k<md->nEc); k++)
 +        {
 +            snew(gnm[k],STRLEN);
 +        }
 +        for(i=0; (i<groups->grps[egcENER].nr); i++)
 +        {
 +            ni=groups->grps[egcENER].nm_ind[i];
 +            for(j=i; (j<groups->grps[egcENER].nr); j++)
 +            {
 +                nj=groups->grps[egcENER].nm_ind[j];
 +                for(k=kk=0; (k<egNR); k++)
 +                {
 +                    if (md->bEInd[k])
 +                    {
 +                        sprintf(gnm[kk],"%s:%s-%s",egrp_nm[k],
 +                                *(groups->grpname[ni]),*(groups->grpname[nj]));
 +                        kk++;
 +                    }
 +                }
 +                md->igrp[n]=get_ebin_space(md->ebin,md->nEc,
 +                                           (const char **)gnm,unit_energy);
 +                n++;
 +            }
 +        }
 +        for(k=0; (k<md->nEc); k++)
 +        {
 +            sfree(gnm[k]);
 +        }
 +        sfree(gnm);
 +
 +        if (n != md->nE)
 +        {
 +            gmx_incons("Number of energy terms wrong");
 +        }
 +    }
 +
 +    md->nTC=groups->grps[egcTC].nr;
 +    md->nNHC = ir->opts.nhchainlength; /* shorthand for number of NH chains */
 +    if (md->bMTTK)
 +    {
 +        md->nTCP = 1;  /* assume only one possible coupling system for barostat
 +                          for now */
 +    }
 +    else
 +    {
 +        md->nTCP = 0;
 +    }
 +    if (md->etc == etcNOSEHOOVER)
 +    {
 +        if (md->bNHC_trotter)
 +        {
 +            md->mde_n = 2*md->nNHC*md->nTC;
 +        }
 +        else
 +        {
 +            md->mde_n = 2*md->nTC;
 +        }
 +        if (md->epc == epcMTTK)
 +        {
 +            md->mdeb_n = 2*md->nNHC*md->nTCP;
 +        }
 +    } else {
 +        md->mde_n = md->nTC;
 +        md->mdeb_n = 0;
 +    }
 +
 +    snew(md->tmp_r,md->mde_n);
 +    snew(md->tmp_v,md->mde_n);
 +    snew(md->grpnms,md->mde_n);
 +    grpnms = md->grpnms;
 +
 +    for(i=0; (i<md->nTC); i++)
 +    {
 +        ni=groups->grps[egcTC].nm_ind[i];
 +        sprintf(buf,"T-%s",*(groups->grpname[ni]));
 +        grpnms[i]=strdup(buf);
 +    }
 +    md->itemp=get_ebin_space(md->ebin,md->nTC,(const char **)grpnms,
 +                             unit_temp_K);
 +
 +    if (md->etc == etcNOSEHOOVER)
 +    {
 +        if (md->bPrintNHChains)
 +        {
 +            if (md->bNHC_trotter)
 +            {
 +                for(i=0; (i<md->nTC); i++)
 +                {
 +                    ni=groups->grps[egcTC].nm_ind[i];
 +                    bufi = *(groups->grpname[ni]);
 +                    for(j=0; (j<md->nNHC); j++)
 +                    {
 +                        sprintf(buf,"Xi-%d-%s",j,bufi);
 +                        grpnms[2*(i*md->nNHC+j)]=strdup(buf);
 +                        sprintf(buf,"vXi-%d-%s",j,bufi);
 +                        grpnms[2*(i*md->nNHC+j)+1]=strdup(buf);
 +                    }
 +                }
 +                md->itc=get_ebin_space(md->ebin,md->mde_n,
 +                                       (const char **)grpnms,unit_invtime);
 +                if (md->bMTTK)
 +                {
 +                    for(i=0; (i<md->nTCP); i++)
 +                    {
 +                        bufi = baro_nm[0];  /* All barostat DOF's together for now. */
 +                        for(j=0; (j<md->nNHC); j++)
 +                        {
 +                            sprintf(buf,"Xi-%d-%s",j,bufi);
 +                            grpnms[2*(i*md->nNHC+j)]=strdup(buf);
 +                            sprintf(buf,"vXi-%d-%s",j,bufi);
 +                            grpnms[2*(i*md->nNHC+j)+1]=strdup(buf);
 +                        }
 +                    }
 +                    md->itcb=get_ebin_space(md->ebin,md->mdeb_n,
 +                                            (const char **)grpnms,unit_invtime);
 +                }
 +            }
 +            else
 +            {
 +                for(i=0; (i<md->nTC); i++)
 +                {
 +                    ni=groups->grps[egcTC].nm_ind[i];
 +                    bufi = *(groups->grpname[ni]);
 +                    sprintf(buf,"Xi-%s",bufi);
 +                    grpnms[2*i]=strdup(buf);
 +                    sprintf(buf,"vXi-%s",bufi);
 +                    grpnms[2*i+1]=strdup(buf);
 +                }
 +                md->itc=get_ebin_space(md->ebin,md->mde_n,
 +                                       (const char **)grpnms,unit_invtime);
 +            }
 +        }
 +    }
 +    else if (md->etc == etcBERENDSEN || md->etc == etcYES ||
 +             md->etc == etcVRESCALE)
 +    {
 +        for(i=0; (i<md->nTC); i++)
 +        {
 +            ni=groups->grps[egcTC].nm_ind[i];
 +            sprintf(buf,"Lamb-%s",*(groups->grpname[ni]));
 +            grpnms[i]=strdup(buf);
 +        }
 +        md->itc=get_ebin_space(md->ebin,md->mde_n,(const char **)grpnms,"");
 +    }
 +
 +    sfree(grpnms);
 +
 +
 +    md->nU=groups->grps[egcACC].nr;
 +    if (md->nU > 1)
 +    {
 +        snew(grpnms,3*md->nU);
 +        for(i=0; (i<md->nU); i++)
 +        {
 +            ni=groups->grps[egcACC].nm_ind[i];
 +            sprintf(buf,"Ux-%s",*(groups->grpname[ni]));
 +            grpnms[3*i+XX]=strdup(buf);
 +            sprintf(buf,"Uy-%s",*(groups->grpname[ni]));
 +            grpnms[3*i+YY]=strdup(buf);
 +            sprintf(buf,"Uz-%s",*(groups->grpname[ni]));
 +            grpnms[3*i+ZZ]=strdup(buf);
 +        }
 +        md->iu=get_ebin_space(md->ebin,3*md->nU,(const char **)grpnms,unit_vel);
 +        sfree(grpnms);
 +    }
 +
 +    if ( fp_ene )
 +    {
 +        do_enxnms(fp_ene,&md->ebin->nener,&md->ebin->enm);
 +    }
 +
 +    md->print_grpnms=NULL;
 +
 +    /* check whether we're going to write dh histograms */
 +    md->dhc=NULL;
 +    if (ir->fepvals->separate_dhdl_file == esepdhdlfileNO )
 +    {
-         mde_delta_h_coll_init(md->dhc, ir);
++        /* Currently dh histograms are only written with dynamics */
++        if (EI_DYNAMICS(ir->eI))
++        {
++            snew(md->dhc, 1);
 +
++            mde_delta_h_coll_init(md->dhc, ir);
++        }
 +        md->fp_dhdl = NULL;
 +    }
 +    else
 +    {
 +        md->fp_dhdl = fp_dhdl;
 +    }
 +    if (ir->bSimTemp) {
 +        int i;
 +        snew(md->temperatures,ir->fepvals->n_lambda);
 +        for (i=0;i<ir->fepvals->n_lambda;i++)
 +        {
 +            md->temperatures[i] = ir->simtempvals->temperatures[i];
 +        }
 +    }
 +    return md;
 +}
 +
 +extern FILE *open_dhdl(const char *filename,const t_inputrec *ir,
 +                       const output_env_t oenv)
 +{
 +    FILE *fp;
 +    const char *dhdl="dH/d\\lambda",*deltag="\\DeltaH",*lambda="\\lambda",
 +        *lambdastate="\\lambda state",*remain="remaining";
 +    char title[STRLEN],label_x[STRLEN],label_y[STRLEN];
 +    int  i,np,nps,nsets,nsets_de,nsetsbegin;
 +    t_lambda *fep;
 +    char **setname;
 +    char buf[STRLEN];
 +    int bufplace=0;
 +
 +    int nsets_dhdl = 0;
 +    int s = 0;
 +    int nsetsextend;
 +
 +    /* for simplicity */
 +    fep = ir->fepvals;
 +
 +    if (fep->n_lambda == 0)
 +    {
 +        sprintf(title,"%s",dhdl);
 +        sprintf(label_x,"Time (ps)");
 +        sprintf(label_y,"%s (%s %s)",
 +                dhdl,unit_energy,"[\\lambda]\\S-1\\N");
 +    }
 +    else
 +    {
 +        sprintf(title,"%s and %s",dhdl,deltag);
 +        sprintf(label_x,"Time (ps)");
 +        sprintf(label_y,"%s and %s (%s %s)",
 +                dhdl,deltag,unit_energy,"[\\8l\\4]\\S-1\\N");
 +    }
 +    fp = gmx_fio_fopen(filename,"w+");
 +    xvgr_header(fp,title,label_x,label_y,exvggtXNY,oenv);
 +
 +    if (!(ir->bSimTemp))
 +    {
 +        bufplace = sprintf(buf,"T = %g (K) ",
 +                ir->opts.ref_t[0]);
 +    }
 +    if (ir->efep != efepSLOWGROWTH)
 +    {
 +        if (fep->n_lambda == 0)
 +        {
 +            sprintf(&(buf[bufplace]),"%s = %g",
 +                    lambda,fep->init_lambda);
 +        }
 +        else
 +        {
 +            sprintf(&(buf[bufplace]),"%s = %d",
 +                    lambdastate,fep->init_fep_state);
 +        }
 +    }
 +    xvgr_subtitle(fp,buf,oenv);
 +
 +    for (i=0;i<efptNR;i++)
 +    {
 +        if (fep->separate_dvdl[i]) {nsets_dhdl++;}
 +    }
 +
 +    /* count the number of delta_g states */
 +    nsets_de = fep->n_lambda;
 +
 +    nsets = nsets_dhdl + nsets_de; /* dhdl + fep differences */
 +
 +    if (fep->n_lambda>0 && ir->bExpanded)
 +    {
 +        nsets += 1;   /*add fep state for expanded ensemble */
 +    }
 +
 +    if (fep->bPrintEnergy)
 +    {
 +        nsets += 1;  /* add energy to the dhdl as well */
 +    }
 +
 +    nsetsextend = nsets;
 +    if ((ir->epc!=epcNO) && (fep->n_lambda>0))
 +    {
 +        nsetsextend += 1; /* for PV term, other terms possible if required for the reduced potential (only needed with foreign lambda) */
 +    }
 +    snew(setname,nsetsextend);
 +
 +    if (ir->bExpanded)
 +    {
 +        /* state for the fep_vals, if we have alchemical sampling */
 +        sprintf(buf,"%s","Thermodynamic state");
 +        setname[s] = strdup(buf);
 +        s+=1;
 +    }
 +
 +    if (fep->bPrintEnergy)
 +    {
 +        sprintf(buf,"%s (%s)","Energy",unit_energy);
 +        setname[s] = strdup(buf);
 +        s+=1;
 +    }
 +
 +    for (i=0;i<efptNR;i++)
 +    {
 +        if (fep->separate_dvdl[i]) {
 +            sprintf(buf,"%s (%s)",dhdl,efpt_names[i]);
 +            setname[s] = strdup(buf);
 +            s+=1;
 +        }
 +    }
 +
 +    if (fep->n_lambda > 0)
 +    {
 +        /* g_bar has to determine the lambda values used in this simulation
 +         * from this xvg legend.
 +         */
 +
 +        if (ir->bExpanded) {
 +            nsetsbegin = 1;  /* for including the expanded ensemble */
 +        } else {
 +            nsetsbegin = 0;
 +        }
 +
 +        if (fep->bPrintEnergy)
 +        {
 +            nsetsbegin += 1;
 +        }
 +        nsetsbegin += nsets_dhdl;
 +
 +        for(s=nsetsbegin; s<nsets; s++)
 +        {
 +            nps = sprintf(buf,"%s %s (",deltag,lambda);
 +            for (i=0;i<efptNR;i++)
 +            {
 +                if (fep->separate_dvdl[i])
 +                {
 +                    np = sprintf(&buf[nps],"%g,",fep->all_lambda[i][s-(nsetsbegin)]);
 +                    nps += np;
 +                }
 +            }
 +            if (ir->bSimTemp)
 +            {
 +                /* print the temperature for this state if doing simulated annealing */
 +                sprintf(&buf[nps],"T = %g (%s))",ir->simtempvals->temperatures[s-(nsetsbegin)],unit_temp_K);
 +            }
 +            else
 +            {
 +                sprintf(&buf[nps-1],")");  /* -1 to overwrite the last comma */
 +            }
 +            setname[s] = strdup(buf);
 +        }
 +        if (ir->epc!=epcNO) {
 +            np = sprintf(buf,"pV (%s)",unit_energy);
 +            setname[nsetsextend-1] = strdup(buf);  /* the first entry after nsets */
 +        }
 +
 +        xvgr_legend(fp,nsetsextend,(const char **)setname,oenv);
 +
 +        for(s=0; s<nsetsextend; s++)
 +        {
 +            sfree(setname[s]);
 +        }
 +        sfree(setname);
 +    }
 +
 +    return fp;
 +}
 +
 +static void copy_energy(t_mdebin *md, real e[],real ecpy[])
 +{
 +    int i,j;
 +
 +    for(i=j=0; (i<F_NRE); i++)
 +        if (md->bEner[i])
 +            ecpy[j++] = e[i];
 +    if (j != md->f_nre)
 +        gmx_incons("Number of energy terms wrong");
 +}
 +
 +void upd_mdebin(t_mdebin *md,
 +                gmx_bool bDoDHDL,
 +                gmx_bool bSum,
 +                double time,
 +                real tmass,
 +                gmx_enerdata_t *enerd,
 +                t_state *state,
 +                t_lambda *fep,
 +                t_expanded *expand,
 +                matrix  box,
 +                tensor svir,
 +                tensor fvir,
 +                tensor vir,
 +                tensor pres,
 +                gmx_ekindata_t *ekind,
 +                rvec mu_tot,
 +                gmx_constr_t constr)
 +{
 +    int    i,j,k,kk,m,n,gid;
 +    real   crmsd[2],tmp6[6];
 +    real   bs[NTRICLBOXS],vol,dens,pv,enthalpy;
 +    real   eee[egNR];
 +    real   ecopy[F_NRE];
 +    double store_dhdl[efptNR];
 +    double *dE=NULL;
 +    real   store_energy=0;
 +    real   tmp;
 +
 +    /* Do NOT use the box in the state variable, but the separate box provided
 +     * as an argument. This is because we sometimes need to write the box from
 +     * the last timestep to match the trajectory frames.
 +     */
 +    copy_energy(md, enerd->term,ecopy);
 +    add_ebin(md->ebin,md->ie,md->f_nre,ecopy,bSum);
 +    if (md->nCrmsd)
 +    {
 +        crmsd[0] = constr_rmsd(constr,FALSE);
 +        if (md->nCrmsd > 1)
 +        {
 +            crmsd[1] = constr_rmsd(constr,TRUE);
 +        }
 +        add_ebin(md->ebin,md->iconrmsd,md->nCrmsd,crmsd,FALSE);
 +    }
 +    if (md->bDynBox)
 +    {
 +        int nboxs;
 +        if(md->bTricl)
 +        {
 +            bs[0] = box[XX][XX];
 +            bs[1] = box[YY][YY];
 +            bs[2] = box[ZZ][ZZ];
 +            bs[3] = box[YY][XX];
 +            bs[4] = box[ZZ][XX];
 +            bs[5] = box[ZZ][YY];
 +            nboxs=NTRICLBOXS;
 +        }
 +        else
 +        {
 +            bs[0] = box[XX][XX];
 +            bs[1] = box[YY][YY];
 +            bs[2] = box[ZZ][ZZ];
 +            nboxs=NBOXS;
 +        }
 +        vol  = box[XX][XX]*box[YY][YY]*box[ZZ][ZZ];
 +        dens = (tmass*AMU)/(vol*NANO*NANO*NANO);
 +        add_ebin(md->ebin,md->ib   ,nboxs,bs   ,bSum);
 +        add_ebin(md->ebin,md->ivol ,1    ,&vol ,bSum);
 +        add_ebin(md->ebin,md->idens,1    ,&dens,bSum);
 +
 +        if (md->bDiagPres)
 +        {
 +            /* This is pV (in kJ/mol).  The pressure is the reference pressure,
 +               not the instantaneous pressure */
 +            pv = vol*md->ref_p/PRESFAC;
 +
 +            add_ebin(md->ebin,md->ipv  ,1    ,&pv  ,bSum);
 +            enthalpy = pv + enerd->term[F_ETOT];
 +            add_ebin(md->ebin,md->ienthalpy  ,1    ,&enthalpy  ,bSum);
 +        }
 +    }
 +    if (md->bConstrVir)
 +    {
 +        add_ebin(md->ebin,md->isvir,9,svir[0],bSum);
 +        add_ebin(md->ebin,md->ifvir,9,fvir[0],bSum);
 +    }
 +    if (md->bVir)
 +        add_ebin(md->ebin,md->ivir,9,vir[0],bSum);
 +    if (md->bPress)
 +        add_ebin(md->ebin,md->ipres,9,pres[0],bSum);
 +    if (md->bSurft){
 +        tmp = (pres[ZZ][ZZ]-(pres[XX][XX]+pres[YY][YY])*0.5)*box[ZZ][ZZ];
 +        add_ebin(md->ebin,md->isurft,1,&tmp,bSum);
 +    }
 +    if (md->epc == epcPARRINELLORAHMAN || md->epc == epcMTTK)
 +    {
 +        tmp6[0] = state->boxv[XX][XX];
 +        tmp6[1] = state->boxv[YY][YY];
 +        tmp6[2] = state->boxv[ZZ][ZZ];
 +        tmp6[3] = state->boxv[YY][XX];
 +        tmp6[4] = state->boxv[ZZ][XX];
 +        tmp6[5] = state->boxv[ZZ][YY];
 +        add_ebin(md->ebin,md->ipc,md->bTricl ? 6 : 3,tmp6,bSum);
 +    }
 +    if(md->bMu)
 +        add_ebin(md->ebin,md->imu,3,mu_tot,bSum);
 +    if (ekind && ekind->cosacc.cos_accel != 0)
 +    {
 +        vol  = box[XX][XX]*box[YY][YY]*box[ZZ][ZZ];
 +        dens = (tmass*AMU)/(vol*NANO*NANO*NANO);
 +        add_ebin(md->ebin,md->ivcos,1,&(ekind->cosacc.vcos),bSum);
 +        /* 1/viscosity, unit 1/(kg m^-1 s^-1) */
 +        tmp = 1/(ekind->cosacc.cos_accel/(ekind->cosacc.vcos*PICO)
 +                 *dens*vol*sqr(box[ZZ][ZZ]*NANO/(2*M_PI)));
 +        add_ebin(md->ebin,md->ivisc,1,&tmp,bSum);
 +    }
 +    if (md->nE > 1)
 +    {
 +        n=0;
 +        for(i=0; (i<md->nEg); i++)
 +        {
 +            for(j=i; (j<md->nEg); j++)
 +            {
 +                gid=GID(i,j,md->nEg);
 +                for(k=kk=0; (k<egNR); k++)
 +                {
 +                    if (md->bEInd[k])
 +                    {
 +                        eee[kk++] = enerd->grpp.ener[k][gid];
 +                    }
 +                }
 +                add_ebin(md->ebin,md->igrp[n],md->nEc,eee,bSum);
 +                n++;
 +            }
 +        }
 +    }
 +
 +    if (ekind)
 +    {
 +        for(i=0; (i<md->nTC); i++)
 +        {
 +            md->tmp_r[i] = ekind->tcstat[i].T;
 +        }
 +        add_ebin(md->ebin,md->itemp,md->nTC,md->tmp_r,bSum);
 +
 +        if (md->etc == etcNOSEHOOVER)
 +        {
 +            /* whether to print Nose-Hoover chains: */
 +            if (md->bPrintNHChains)
 +            {
 +                if (md->bNHC_trotter)
 +                {
 +                    for(i=0; (i<md->nTC); i++)
 +                    {
 +                        for (j=0;j<md->nNHC;j++)
 +                        {
 +                            k = i*md->nNHC+j;
 +                            md->tmp_r[2*k] = state->nosehoover_xi[k];
 +                            md->tmp_r[2*k+1] = state->nosehoover_vxi[k];
 +                        }
 +                    }
 +                    add_ebin(md->ebin,md->itc,md->mde_n,md->tmp_r,bSum);
 +
 +                    if (md->bMTTK) {
 +                        for(i=0; (i<md->nTCP); i++)
 +                        {
 +                            for (j=0;j<md->nNHC;j++)
 +                            {
 +                                k = i*md->nNHC+j;
 +                                md->tmp_r[2*k] = state->nhpres_xi[k];
 +                                md->tmp_r[2*k+1] = state->nhpres_vxi[k];
 +                            }
 +                        }
 +                        add_ebin(md->ebin,md->itcb,md->mdeb_n,md->tmp_r,bSum);
 +                    }
 +                }
 +                else
 +                {
 +                    for(i=0; (i<md->nTC); i++)
 +                    {
 +                        md->tmp_r[2*i] = state->nosehoover_xi[i];
 +                        md->tmp_r[2*i+1] = state->nosehoover_vxi[i];
 +                    }
 +                    add_ebin(md->ebin,md->itc,md->mde_n,md->tmp_r,bSum);
 +                }
 +            }
 +        }
 +        else if (md->etc == etcBERENDSEN || md->etc == etcYES ||
 +                 md->etc == etcVRESCALE)
 +        {
 +            for(i=0; (i<md->nTC); i++)
 +            {
 +                md->tmp_r[i] = ekind->tcstat[i].lambda;
 +            }
 +            add_ebin(md->ebin,md->itc,md->nTC,md->tmp_r,bSum);
 +        }
 +    }
 +
 +    if (ekind && md->nU > 1)
 +    {
 +        for(i=0; (i<md->nU); i++)
 +        {
 +            copy_rvec(ekind->grpstat[i].u,md->tmp_v[i]);
 +        }
 +        add_ebin(md->ebin,md->iu,3*md->nU,md->tmp_v[0],bSum);
 +    }
 +
 +    ebin_increase_count(md->ebin,bSum);
 +
 +    /* BAR + thermodynamic integration values */
 +    if ((md->fp_dhdl || md->dhc) && bDoDHDL && (enerd->n_lambda > 0))
 +    {
 +        snew(dE,enerd->n_lambda-1);
 +        for(i=0; i<enerd->n_lambda-1; i++) {
 +            dE[i] = enerd->enerpart_lambda[i+1]-enerd->enerpart_lambda[0];  /* zero for simulated tempering */
 +            if (md->temperatures!=NULL)
 +            {
 +                /* MRS: is this right, given the way we have defined the exchange probabilities? */
 +                /* is this even useful to have at all? */
 +                dE[i] += (md->temperatures[i]/md->temperatures[state->fep_state]-1.0)*enerd->term[F_EKIN];
 +            }
 +        }
 +    }
 +
 +    if (md->fp_dhdl && bDoDHDL)
 +    {
 +        fprintf(md->fp_dhdl,"%.4f",time);
 +        /* the current free energy state */
 +
 +        /* print the current state if we are doing expanded ensemble */
 +        if (expand->elmcmove > elmcmoveNO) {
 +            fprintf(md->fp_dhdl," %4d",state->fep_state);
 +        }
 +        /* total energy (for if the temperature changes */
 +        if (fep->bPrintEnergy)
 +        {
 +            store_energy = enerd->term[F_ETOT];
 +            fprintf(md->fp_dhdl," %#.8g",store_energy);
 +        }
 +
 +        for (i=0;i<efptNR;i++)
 +        {
 +            if (fep->separate_dvdl[i])
 +            {
 +                fprintf(md->fp_dhdl," %#.8g",enerd->term[F_DVDL+i]); /* assumes F_DVDL is first */
 +            }
 +        }
 +        for(i=1; i<enerd->n_lambda; i++)
 +        {
 +            fprintf(md->fp_dhdl," %#.8g",dE[i-1]);
 +
 +        }
 +        if ((md->epc!=epcNO)  && (enerd->n_lambda > 0))
 +        {
 +            fprintf(md->fp_dhdl," %#.8g",pv);   /* PV term only needed when there are alternate state lambda */
 +        }
 +        fprintf(md->fp_dhdl,"\n");
 +        /* and the binary free energy output */
 +    }
 +    if (md->dhc && bDoDHDL)
 +    {
 +        int idhdl = 0;
 +        for (i=0;i<efptNR;i++)
 +        {
 +            if (fep->separate_dvdl[i])
 +            {
 +                store_dhdl[idhdl] = enerd->term[F_DVDL+i]; /* assumes F_DVDL is first */
 +                idhdl+=1;
 +            }
 +        }
 +        /* store_dh is dE */
 +        mde_delta_h_coll_add_dh(md->dhc,
 +                                (double)state->fep_state,
 +                                store_energy,
 +                                pv,
 +                                (expand->elamstats>elamstatsNO),
 +                                (fep->bPrintEnergy),
 +                                (md->epc!=epcNO),
 +                                idhdl,
 +                                fep->n_lambda,
 +                                store_dhdl,
 +                                dE,
 +                                time);
 +    }
 +    if ((md->fp_dhdl || md->dhc) && bDoDHDL && (enerd->n_lambda >0))
 +    {
 +        sfree(dE);
 +    }
 +}
 +
 +
 +void upd_mdebin_step(t_mdebin *md)
 +{
 +    ebin_increase_count(md->ebin,FALSE);
 +}
 +
 +static void npr(FILE *log,int n,char c)
 +{
 +    for(; (n>0); n--) fprintf(log,"%c",c);
 +}
 +
 +static void pprint(FILE *log,const char *s,t_mdebin *md)
 +{
 +    char CHAR='#';
 +    int  slen;
 +    char buf1[22],buf2[22];
 +
 +    slen = strlen(s);
 +    fprintf(log,"\t<======  ");
 +    npr(log,slen,CHAR);
 +    fprintf(log,"  ==>\n");
 +    fprintf(log,"\t<====  %s  ====>\n",s);
 +    fprintf(log,"\t<==  ");
 +    npr(log,slen,CHAR);
 +    fprintf(log,"  ======>\n\n");
 +
 +    fprintf(log,"\tStatistics over %s steps using %s frames\n",
 +            gmx_step_str(md->ebin->nsteps_sim,buf1),
 +            gmx_step_str(md->ebin->nsum_sim,buf2));
 +    fprintf(log,"\n");
 +}
 +
 +void print_ebin_header(FILE *log,gmx_large_int_t steps,double time,real lambda)
 +{
 +    char buf[22];
 +
 +    fprintf(log,"   %12s   %12s   %12s\n"
 +            "   %12s   %12.5f   %12.5f\n\n",
 +            "Step","Time","Lambda",gmx_step_str(steps,buf),time,lambda);
 +}
 +
 +void print_ebin(ener_file_t fp_ene,gmx_bool bEne,gmx_bool bDR,gmx_bool bOR,
 +                FILE *log,
 +                gmx_large_int_t step,double time,
 +                int mode,gmx_bool bCompact,
 +                t_mdebin *md,t_fcdata *fcd,
 +                gmx_groups_t *groups,t_grpopts *opts)
 +{
 +    /*static char **grpnms=NULL;*/
 +    char        buf[246];
 +    int         i,j,n,ni,nj,ndr,nor,b;
 +    int         ndisre=0;
 +    real        *disre_rm3tav, *disre_rt;
 +
 +    /* these are for the old-style blocks (1 subblock, only reals), because
 +       there can be only one per ID for these */
 +    int         nr[enxNR];
 +    int         id[enxNR];
 +    real        *block[enxNR];
 +
 +    /* temporary arrays for the lambda values to write out */
 +    double      enxlambda_data[2];
 +
 +    t_enxframe  fr;
 +
 +    switch (mode)
 +    {
 +        case eprNORMAL:
 +            init_enxframe(&fr);
 +            fr.t            = time;
 +            fr.step         = step;
 +            fr.nsteps       = md->ebin->nsteps;
 +            fr.dt           = md->delta_t;
 +            fr.nsum         = md->ebin->nsum;
 +            fr.nre          = (bEne) ? md->ebin->nener : 0;
 +            fr.ener         = md->ebin->e;
 +            ndisre          = bDR ? fcd->disres.npair : 0;
 +            disre_rm3tav    = fcd->disres.rm3tav;
 +            disre_rt        = fcd->disres.rt;
 +            /* Optional additional old-style (real-only) blocks. */
 +            for(i=0; i<enxNR; i++)
 +            {
 +                nr[i] = 0;
 +            }
 +            if (fcd->orires.nr > 0 && bOR)
 +            {
 +                diagonalize_orires_tensors(&(fcd->orires));
 +                nr[enxOR]     = fcd->orires.nr;
 +                block[enxOR]  = fcd->orires.otav;
 +                id[enxOR]     = enxOR;
 +                nr[enxORI]    = (fcd->orires.oinsl != fcd->orires.otav) ?
 +                          fcd->orires.nr : 0;
 +                block[enxORI] = fcd->orires.oinsl;
 +                id[enxORI]    = enxORI;
 +                nr[enxORT]    = fcd->orires.nex*12;
 +                block[enxORT] = fcd->orires.eig;
 +                id[enxORT]    = enxORT;
 +            }
 +
 +            /* whether we are going to wrte anything out: */
 +            if (fr.nre || ndisre || nr[enxOR] || nr[enxORI])
 +            {
 +
 +                /* the old-style blocks go first */
 +                fr.nblock = 0;
 +                for(i=0; i<enxNR; i++)
 +                {
 +                    if (nr[i] > 0)
 +                    {
 +                        fr.nblock = i + 1;
 +                    }
 +                }
 +                add_blocks_enxframe(&fr, fr.nblock);
 +                for(b=0;b<fr.nblock;b++)
 +                {
 +                    add_subblocks_enxblock(&(fr.block[b]), 1);
 +                    fr.block[b].id=id[b];
 +                    fr.block[b].sub[0].nr = nr[b];
 +#ifndef GMX_DOUBLE
 +                    fr.block[b].sub[0].type = xdr_datatype_float;
 +                    fr.block[b].sub[0].fval = block[b];
 +#else
 +                    fr.block[b].sub[0].type = xdr_datatype_double;
 +                    fr.block[b].sub[0].dval = block[b];
 +#endif
 +                }
 +
 +                /* check for disre block & fill it. */
 +                if (ndisre>0)
 +                {
 +                    int db = fr.nblock;
 +                    fr.nblock+=1;
 +                    add_blocks_enxframe(&fr, fr.nblock);
 +
 +                    add_subblocks_enxblock(&(fr.block[db]), 2);
 +                    fr.block[db].id=enxDISRE;
 +                    fr.block[db].sub[0].nr=ndisre;
 +                    fr.block[db].sub[1].nr=ndisre;
 +#ifndef GMX_DOUBLE
 +                    fr.block[db].sub[0].type=xdr_datatype_float;
 +                    fr.block[db].sub[1].type=xdr_datatype_float;
 +                    fr.block[db].sub[0].fval=disre_rt;
 +                    fr.block[db].sub[1].fval=disre_rm3tav;
 +#else
 +                    fr.block[db].sub[0].type=xdr_datatype_double;
 +                    fr.block[db].sub[1].type=xdr_datatype_double;
 +                    fr.block[db].sub[0].dval=disre_rt;
 +                    fr.block[db].sub[1].dval=disre_rm3tav;
 +#endif
 +                }
 +                /* here we can put new-style blocks */
 +
 +                /* Free energy perturbation blocks */
 +                if (md->dhc)
 +                {
 +                    mde_delta_h_coll_handle_block(md->dhc, &fr, fr.nblock);
 +                }
 +
 +                /* we can now free & reset the data in the blocks */
 +                if (md->dhc)
 +                {
 +                    mde_delta_h_coll_reset(md->dhc);
 +                }
 +
 +                /* do the actual I/O */
 +                do_enx(fp_ene,&fr);
 +                gmx_fio_check_file_position(enx_file_pointer(fp_ene));
 +                if (fr.nre)
 +                {
 +                    /* We have stored the sums, so reset the sum history */
 +                    reset_ebin_sums(md->ebin);
 +                }
 +            }
 +            free_enxframe(&fr);
 +            break;
 +        case eprAVER:
 +            if (log)
 +            {
 +                pprint(log,"A V E R A G E S",md);
 +            }
 +            break;
 +        case eprRMS:
 +            if (log)
 +            {
 +                pprint(log,"R M S - F L U C T U A T I O N S",md);
 +            }
 +            break;
 +        default:
 +            gmx_fatal(FARGS,"Invalid print mode (%d)",mode);
 +    }
 +
 +    if (log)
 +    {
 +        for(i=0;i<opts->ngtc;i++)
 +        {
 +            if(opts->annealing[i]!=eannNO)
 +            {
 +                fprintf(log,"Current ref_t for group %s: %8.1f\n",
 +                        *(groups->grpname[groups->grps[egcTC].nm_ind[i]]),
 +                        opts->ref_t[i]);
 +            }
 +        }
 +        if (mode==eprNORMAL && fcd->orires.nr>0)
 +        {
 +            print_orires_log(log,&(fcd->orires));
 +        }
 +        fprintf(log,"   Energies (%s)\n",unit_energy);
 +        pr_ebin(log,md->ebin,md->ie,md->f_nre+md->nCrmsd,5,mode,TRUE);
 +        fprintf(log,"\n");
 +
 +        if (!bCompact)
 +        {
 +            if (md->bDynBox)
 +            {
 +                pr_ebin(log,md->ebin,md->ib, md->bTricl ? NTRICLBOXS : NBOXS,5,
 +                        mode,TRUE);
 +                fprintf(log,"\n");
 +            }
 +            if (md->bConstrVir)
 +            {
 +                fprintf(log,"   Constraint Virial (%s)\n",unit_energy);
 +                pr_ebin(log,md->ebin,md->isvir,9,3,mode,FALSE);
 +                fprintf(log,"\n");
 +                fprintf(log,"   Force Virial (%s)\n",unit_energy);
 +                pr_ebin(log,md->ebin,md->ifvir,9,3,mode,FALSE);
 +                fprintf(log,"\n");
 +            }
 +            if (md->bVir)
 +            {
 +                fprintf(log,"   Total Virial (%s)\n",unit_energy);
 +                pr_ebin(log,md->ebin,md->ivir,9,3,mode,FALSE);
 +                fprintf(log,"\n");
 +            }
 +            if (md->bPress)
 +            {
 +                fprintf(log,"   Pressure (%s)\n",unit_pres_bar);
 +                pr_ebin(log,md->ebin,md->ipres,9,3,mode,FALSE);
 +                fprintf(log,"\n");
 +            }
 +            fprintf(log,"   Total Dipole (%s)\n",unit_dipole_D);
 +            pr_ebin(log,md->ebin,md->imu,3,3,mode,FALSE);
 +            fprintf(log,"\n");
 +
 +            if (md->nE > 1)
 +            {
 +                if (md->print_grpnms==NULL)
 +                {
 +                    snew(md->print_grpnms,md->nE);
 +                    n=0;
 +                    for(i=0; (i<md->nEg); i++)
 +                    {
 +                        ni=groups->grps[egcENER].nm_ind[i];
 +                        for(j=i; (j<md->nEg); j++)
 +                        {
 +                            nj=groups->grps[egcENER].nm_ind[j];
 +                            sprintf(buf,"%s-%s",*(groups->grpname[ni]),
 +                                    *(groups->grpname[nj]));
 +                            md->print_grpnms[n++]=strdup(buf);
 +                        }
 +                    }
 +                }
 +                sprintf(buf,"Epot (%s)",unit_energy);
 +                fprintf(log,"%15s   ",buf);
 +                for(i=0; (i<egNR); i++)
 +                {
 +                    if (md->bEInd[i])
 +                    {
 +                        fprintf(log,"%12s   ",egrp_nm[i]);
 +                    }
 +                }
 +                fprintf(log,"\n");
 +                for(i=0; (i<md->nE); i++)
 +                {
 +                    fprintf(log,"%15s",md->print_grpnms[i]);
 +                    pr_ebin(log,md->ebin,md->igrp[i],md->nEc,md->nEc,mode,
 +                            FALSE);
 +                }
 +                fprintf(log,"\n");
 +            }
 +            if (md->nTC > 1)
 +            {
 +                pr_ebin(log,md->ebin,md->itemp,md->nTC,4,mode,TRUE);
 +                fprintf(log,"\n");
 +            }
 +            if (md->nU > 1)
 +            {
 +                fprintf(log,"%15s   %12s   %12s   %12s\n",
 +                        "Group","Ux","Uy","Uz");
 +                for(i=0; (i<md->nU); i++)
 +                {
 +                    ni=groups->grps[egcACC].nm_ind[i];
 +                    fprintf(log,"%15s",*groups->grpname[ni]);
 +                    pr_ebin(log,md->ebin,md->iu+3*i,3,3,mode,FALSE);
 +                }
 +                fprintf(log,"\n");
 +            }
 +        }
 +    }
 +
 +}
 +
 +void update_energyhistory(energyhistory_t * enerhist,t_mdebin * mdebin)
 +{
 +    int i;
 +
 +    enerhist->nsteps     = mdebin->ebin->nsteps;
 +    enerhist->nsum       = mdebin->ebin->nsum;
 +    enerhist->nsteps_sim = mdebin->ebin->nsteps_sim;
 +    enerhist->nsum_sim   = mdebin->ebin->nsum_sim;
 +    enerhist->nener      = mdebin->ebin->nener;
 +
 +    if (mdebin->ebin->nsum > 0)
 +    {
 +        /* Check if we need to allocate first */
 +        if(enerhist->ener_ave == NULL)
 +        {
 +            snew(enerhist->ener_ave,enerhist->nener);
 +            snew(enerhist->ener_sum,enerhist->nener);
 +        }
 +
 +        for(i=0;i<enerhist->nener;i++)
 +        {
 +            enerhist->ener_ave[i] = mdebin->ebin->e[i].eav;
 +            enerhist->ener_sum[i] = mdebin->ebin->e[i].esum;
 +        }
 +    }
 +
 +    if (mdebin->ebin->nsum_sim > 0)
 +    {
 +        /* Check if we need to allocate first */
 +        if(enerhist->ener_sum_sim == NULL)
 +        {
 +            snew(enerhist->ener_sum_sim,enerhist->nener);
 +        }
 +
 +        for(i=0;i<enerhist->nener;i++)
 +        {
 +            enerhist->ener_sum_sim[i] = mdebin->ebin->e_sim[i].esum;
 +        }
 +    }
 +    if (mdebin->dhc)
 +    {
 +        mde_delta_h_coll_update_energyhistory(mdebin->dhc, enerhist);
 +    }
 +}
 +
 +void restore_energyhistory_from_state(t_mdebin * mdebin,
 +                                      energyhistory_t * enerhist)
 +{
 +    int i;
 +
 +    if ((enerhist->nsum > 0 || enerhist->nsum_sim > 0) &&
 +        mdebin->ebin->nener != enerhist->nener)
 +    {
 +        gmx_fatal(FARGS,"Mismatch between number of energies in run input (%d) and checkpoint file (%d).",
 +                  mdebin->ebin->nener,enerhist->nener);
 +    }
 +
 +    mdebin->ebin->nsteps     = enerhist->nsteps;
 +    mdebin->ebin->nsum       = enerhist->nsum;
 +    mdebin->ebin->nsteps_sim = enerhist->nsteps_sim;
 +    mdebin->ebin->nsum_sim   = enerhist->nsum_sim;
 +
 +    for(i=0; i<mdebin->ebin->nener; i++)
 +    {
 +        mdebin->ebin->e[i].eav  =
 +                  (enerhist->nsum > 0 ? enerhist->ener_ave[i] : 0);
 +        mdebin->ebin->e[i].esum =
 +                  (enerhist->nsum > 0 ? enerhist->ener_sum[i] : 0);
 +        mdebin->ebin->e_sim[i].esum =
 +                  (enerhist->nsum_sim > 0 ? enerhist->ener_sum_sim[i] : 0);
 +    }
 +    if (mdebin->dhc)
 +    {
 +        mde_delta_h_coll_restore_energyhistory(mdebin->dhc, enerhist);
 +    }
 +}
index 33d154272f924832391aec14875b8b33de736a2e,0000000000000000000000000000000000000000..781629b4f9ed5fc9dcabc257e026277e3431546b
mode 100644,000000..100644
--- /dev/null
@@@ -1,2474 -1,0 +1,2486 @@@
-     write_traj(fplog,cr,outf,MDOF_X | MDOF_F,
 +/* -*- 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:
 + * GROwing Monsters And Cloning Shrimps
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <string.h>
 +#include <time.h>
 +#include <math.h>
 +#include "sysstuff.h"
 +#include "string2.h"
 +#include "network.h"
 +#include "confio.h"
 +#include "copyrite.h"
 +#include "smalloc.h"
 +#include "nrnb.h"
 +#include "main.h"
 +#include "force.h"
 +#include "macros.h"
 +#include "random.h"
 +#include "names.h"
 +#include "gmx_fatal.h"
 +#include "txtdump.h"
 +#include "typedefs.h"
 +#include "update.h"
 +#include "constr.h"
 +#include "vec.h"
 +#include "statutil.h"
 +#include "tgroup.h"
 +#include "mdebin.h"
 +#include "vsite.h"
 +#include "force.h"
 +#include "mdrun.h"
 +#include "domdec.h"
 +#include "partdec.h"
 +#include "trnio.h"
 +#include "mdatoms.h"
 +#include "ns.h"
 +#include "gmx_wallcycle.h"
 +#include "mtop_util.h"
 +#include "gmxfio.h"
 +#include "pme.h"
 +
 +#include "gromacs/linearalgebra/mtxio.h"
 +#include "gromacs/linearalgebra/sparsematrix.h"
 +
 +typedef struct {
 +  t_state s;
 +  rvec    *f;
 +  real    epot;
 +  real    fnorm;
 +  real    fmax;
 +  int     a_fmax;
 +} em_state_t;
 +
 +static em_state_t *init_em_state()
 +{
 +  em_state_t *ems;
 +
 +  snew(ems,1);
 +
 +  /* does this need to be here?  Should the array be declared differently (staticaly)in the state definition? */
 +  snew(ems->s.lambda,efptNR);
 +
 +  return ems;
 +}
 +
 +static void print_em_start(FILE *fplog,t_commrec *cr,gmx_runtime_t *runtime,
 +                           gmx_wallcycle_t wcycle,
 +                           const char *name)
 +{
 +    char buf[STRLEN];
 +
 +    runtime_start(runtime);
 +
 +    sprintf(buf,"Started %s",name);
 +    print_date_and_time(fplog,cr->nodeid,buf,NULL);
 +
 +    wallcycle_start(wcycle,ewcRUN);
 +}
 +static void em_time_end(FILE *fplog,t_commrec *cr,gmx_runtime_t *runtime,
 +                        gmx_wallcycle_t wcycle)
 +{
 +    wallcycle_stop(wcycle,ewcRUN);
 +
 +    runtime_end(runtime);
 +}
 +
 +static void sp_header(FILE *out,const char *minimizer,real ftol,int nsteps)
 +{
 +    fprintf(out,"\n");
 +    fprintf(out,"%s:\n",minimizer);
 +    fprintf(out,"   Tolerance (Fmax)   = %12.5e\n",ftol);
 +    fprintf(out,"   Number of steps    = %12d\n",nsteps);
 +}
 +
 +static void warn_step(FILE *fp,real ftol,gmx_bool bLastStep,gmx_bool bConstrain)
 +{
 +    if (bLastStep)
 +    {
 +        fprintf(fp,"\nReached the maximum number of steps before reaching Fmax < %g\n",ftol);
 +    }
 +    else
 +    {
 +        fprintf(fp,"\nStepsize too small, or no change in energy.\n"
 +                "Converged to machine precision,\n"
 +                "but not to the requested precision Fmax < %g\n",
 +                ftol);
 +        if (sizeof(real)<sizeof(double))
 +        {
 +            fprintf(fp,"\nDouble precision normally gives you higher accuracy.\n");
 +        }
 +        if (bConstrain)
 +        {
 +            fprintf(fp,"You might need to increase your constraint accuracy, or turn\n"
 +                    "off constraints alltogether (set constraints = none in mdp file)\n");
 +        }
 +    }
 +}
 +
 +
 +
 +static void print_converged(FILE *fp,const char *alg,real ftol,
 +                          gmx_large_int_t count,gmx_bool bDone,gmx_large_int_t nsteps,
 +                          real epot,real fmax, int nfmax, real fnorm)
 +{
 +  char buf[STEPSTRSIZE];
 +
 +  if (bDone)
 +    fprintf(fp,"\n%s converged to Fmax < %g in %s steps\n",
 +          alg,ftol,gmx_step_str(count,buf));
 +  else if(count<nsteps)
 +    fprintf(fp,"\n%s converged to machine precision in %s steps,\n"
 +               "but did not reach the requested Fmax < %g.\n",
 +          alg,gmx_step_str(count,buf),ftol);
 +  else
 +    fprintf(fp,"\n%s did not converge to Fmax < %g in %s steps.\n",
 +          alg,ftol,gmx_step_str(count,buf));
 +
 +#ifdef GMX_DOUBLE
 +  fprintf(fp,"Potential Energy  = %21.14e\n",epot);
 +  fprintf(fp,"Maximum force     = %21.14e on atom %d\n",fmax,nfmax+1);
 +  fprintf(fp,"Norm of force     = %21.14e\n",fnorm);
 +#else
 +  fprintf(fp,"Potential Energy  = %14.7e\n",epot);
 +  fprintf(fp,"Maximum force     = %14.7e on atom %d\n",fmax,nfmax+1);
 +  fprintf(fp,"Norm of force     = %14.7e\n",fnorm);
 +#endif
 +}
 +
 +static void get_f_norm_max(t_commrec *cr,
 +                         t_grpopts *opts,t_mdatoms *mdatoms,rvec *f,
 +                         real *fnorm,real *fmax,int *a_fmax)
 +{
 +  double fnorm2,*sum;
 +  real fmax2,fmax2_0,fam;
 +  int  la_max,a_max,start,end,i,m,gf;
 +
 +  /* This routine finds the largest force and returns it.
 +   * On parallel machines the global max is taken.
 +   */
 +  fnorm2 = 0;
 +  fmax2 = 0;
 +  la_max = -1;
 +  gf = 0;
 +  start = mdatoms->start;
 +  end   = mdatoms->homenr + start;
 +  if (mdatoms->cFREEZE) {
 +    for(i=start; i<end; i++) {
 +      gf = mdatoms->cFREEZE[i];
 +      fam = 0;
 +      for(m=0; m<DIM; m++)
 +      if (!opts->nFreeze[gf][m])
 +        fam += sqr(f[i][m]);
 +      fnorm2 += fam;
 +      if (fam > fmax2) {
 +      fmax2  = fam;
 +      la_max = i;
 +      }
 +    }
 +  } else {
 +    for(i=start; i<end; i++) {
 +      fam = norm2(f[i]);
 +      fnorm2 += fam;
 +      if (fam > fmax2) {
 +      fmax2  = fam;
 +      la_max = i;
 +      }
 +    }
 +  }
 +
 +  if (la_max >= 0 && DOMAINDECOMP(cr)) {
 +    a_max = cr->dd->gatindex[la_max];
 +  } else {
 +    a_max = la_max;
 +  }
 +  if (PAR(cr)) {
 +    snew(sum,2*cr->nnodes+1);
 +    sum[2*cr->nodeid]   = fmax2;
 +    sum[2*cr->nodeid+1] = a_max;
 +    sum[2*cr->nnodes]   = fnorm2;
 +    gmx_sumd(2*cr->nnodes+1,sum,cr);
 +    fnorm2 = sum[2*cr->nnodes];
 +    /* Determine the global maximum */
 +    for(i=0; i<cr->nnodes; i++) {
 +      if (sum[2*i] > fmax2) {
 +      fmax2 = sum[2*i];
 +      a_max = (int)(sum[2*i+1] + 0.5);
 +      }
 +    }
 +    sfree(sum);
 +  }
 +
 +  if (fnorm)
 +    *fnorm = sqrt(fnorm2);
 +  if (fmax)
 +    *fmax  = sqrt(fmax2);
 +  if (a_fmax)
 +    *a_fmax = a_max;
 +}
 +
 +static void get_state_f_norm_max(t_commrec *cr,
 +                         t_grpopts *opts,t_mdatoms *mdatoms,
 +                         em_state_t *ems)
 +{
 +  get_f_norm_max(cr,opts,mdatoms,ems->f,&ems->fnorm,&ems->fmax,&ems->a_fmax);
 +}
 +
 +void init_em(FILE *fplog,const char *title,
 +             t_commrec *cr,t_inputrec *ir,
 +             t_state *state_global,gmx_mtop_t *top_global,
 +             em_state_t *ems,gmx_localtop_t **top,
 +             rvec **f,rvec **f_global,
 +             t_nrnb *nrnb,rvec mu_tot,
 +             t_forcerec *fr,gmx_enerdata_t **enerd,
 +             t_graph **graph,t_mdatoms *mdatoms,gmx_global_stat_t *gstat,
 +             gmx_vsite_t *vsite,gmx_constr_t constr,
 +             int nfile,const t_filenm fnm[],
 +             gmx_mdoutf_t **outf,t_mdebin **mdebin)
 +{
 +    int  start,homenr,i;
 +    real dvdlambda;
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog,"Initiating %s\n",title);
 +    }
 +
 +    state_global->ngtc = 0;
 +
 +    /* Initialize lambda variables */
 +    initialize_lambdas(fplog,ir,&(state_global->fep_state),state_global->lambda,NULL);
 +
 +    init_nrnb(nrnb);
 +
 +    if (DOMAINDECOMP(cr))
 +    {
 +        *top = dd_init_local_top(top_global);
 +
 +        dd_init_local_state(cr->dd,state_global,&ems->s);
 +
 +        *f = NULL;
 +
 +        /* Distribute the charge groups over the nodes from the master node */
 +        dd_partition_system(fplog,ir->init_step,cr,TRUE,1,
 +                            state_global,top_global,ir,
 +                            &ems->s,&ems->f,mdatoms,*top,
 +                            fr,vsite,NULL,constr,
 +                            nrnb,NULL,FALSE);
 +        dd_store_state(cr->dd,&ems->s);
 +
 +        if (ir->nstfout)
 +        {
 +            snew(*f_global,top_global->natoms);
 +        }
 +        else
 +        {
 +            *f_global = NULL;
 +        }
 +        *graph = NULL;
 +    }
 +    else
 +    {
 +        snew(*f,top_global->natoms);
 +
 +        /* Just copy the state */
 +        ems->s = *state_global;
 +        snew(ems->s.x,ems->s.nalloc);
 +        snew(ems->f,ems->s.nalloc);
 +        for(i=0; i<state_global->natoms; i++)
 +        {
 +            copy_rvec(state_global->x[i],ems->s.x[i]);
 +        }
 +        copy_mat(state_global->box,ems->s.box);
 +
 +        if (PAR(cr) && ir->eI != eiNM)
 +        {
 +            /* Initialize the particle decomposition and split the topology */
 +            *top = split_system(fplog,top_global,ir,cr);
 +
 +            pd_cg_range(cr,&fr->cg0,&fr->hcg);
 +        }
 +        else
 +        {
 +            *top = gmx_mtop_generate_local_top(top_global,ir);
 +        }
 +        *f_global = *f;
 +
 +        if (ir->ePBC != epbcNONE && !ir->bPeriodicMols)
 +        {
 +            *graph = mk_graph(fplog,&((*top)->idef),0,top_global->natoms,FALSE,FALSE);
 +        }
 +        else
 +        {
 +            *graph = NULL;
 +        }
 +
 +        if (PARTDECOMP(cr))
 +        {
 +            pd_at_range(cr,&start,&homenr);
 +            homenr -= start;
 +        }
 +        else
 +        {
 +            start  = 0;
 +            homenr = top_global->natoms;
 +        }
 +        atoms2md(top_global,ir,0,NULL,start,homenr,mdatoms);
 +        update_mdatoms(mdatoms,state_global->lambda[efptFEP]);
 +
 +        if (vsite)
 +        {
 +            set_vsite_top(vsite,*top,mdatoms,cr);
 +        }
 +    }
 +
 +    if (constr)
 +    {
 +        if (ir->eConstrAlg == econtSHAKE &&
 +            gmx_mtop_ftype_count(top_global,F_CONSTR) > 0)
 +        {
 +            gmx_fatal(FARGS,"Can not do energy minimization with %s, use %s\n",
 +                      econstr_names[econtSHAKE],econstr_names[econtLINCS]);
 +        }
 +
 +        if (!DOMAINDECOMP(cr))
 +        {
 +            set_constraints(constr,*top,ir,mdatoms,cr);
 +        }
 +
 +        if (!ir->bContinuation)
 +        {
 +            /* Constrain the starting coordinates */
 +            dvdlambda=0;
 +            constrain(PAR(cr) ? NULL : fplog,TRUE,TRUE,constr,&(*top)->idef,
 +                      ir,NULL,cr,-1,0,mdatoms,
 +                      ems->s.x,ems->s.x,NULL,ems->s.box,
 +                      ems->s.lambda[efptFEP],&dvdlambda,
 +                      NULL,NULL,nrnb,econqCoord,FALSE,0,0);
 +        }
 +    }
 +
 +    if (PAR(cr))
 +    {
 +        *gstat = global_stat_init(ir);
 +    }
 +
 +    *outf = init_mdoutf(nfile,fnm,0,cr,ir,NULL);
 +
 +    snew(*enerd,1);
 +    init_enerdata(top_global->groups.grps[egcENER].nr,ir->fepvals->n_lambda,
 +                  *enerd);
 +
 +    if (mdebin != NULL)
 +    {
 +        /* Init bin for energy stuff */
 +        *mdebin = init_mdebin((*outf)->fp_ene,top_global,ir,NULL);
 +    }
 +
 +    clear_rvec(mu_tot);
 +    calc_shifts(ems->s.box,fr->shift_vec);
 +}
 +
 +static void finish_em(FILE *fplog,t_commrec *cr,gmx_mdoutf_t *outf,
 +                      gmx_runtime_t *runtime,gmx_wallcycle_t wcycle)
 +{
 +  if (!(cr->duty & DUTY_PME)) {
 +    /* Tell the PME only node to finish */
 +    gmx_pme_finish(cr);
 +  }
 +
 +  done_mdoutf(outf);
 +
 +  em_time_end(fplog,cr,runtime,wcycle);
 +}
 +
 +static void swap_em_state(em_state_t *ems1,em_state_t *ems2)
 +{
 +  em_state_t tmp;
 +
 +  tmp   = *ems1;
 +  *ems1 = *ems2;
 +  *ems2 = tmp;
 +}
 +
 +static void copy_em_coords(em_state_t *ems,t_state *state)
 +{
 +    int i;
 +
 +    for(i=0; (i<state->natoms); i++)
 +    {
 +        copy_rvec(ems->s.x[i],state->x[i]);
 +    }
 +}
 +
 +static void write_em_traj(FILE *fplog,t_commrec *cr,
 +                          gmx_mdoutf_t *outf,
 +                          gmx_bool bX,gmx_bool bF,const char *confout,
 +                          gmx_mtop_t *top_global,
 +                          t_inputrec *ir,gmx_large_int_t step,
 +                          em_state_t *state,
 +                          t_state *state_global,rvec *f_global)
 +{
 +    int mdof_flags;
 +
 +    if ((bX || bF || confout != NULL) && !DOMAINDECOMP(cr))
 +    {
 +        copy_em_coords(state,state_global);
 +        f_global = state->f;
 +    }
 +
 +    mdof_flags = 0;
 +    if (bX) { mdof_flags |= MDOF_X; }
 +    if (bF) { mdof_flags |= MDOF_F; }
 +    write_traj(fplog,cr,outf,mdof_flags,
 +               top_global,step,(double)step,
 +               &state->s,state_global,state->f,f_global,NULL,NULL);
 +
 +    if (confout != NULL && MASTER(cr))
 +    {
 +        if (ir->ePBC != epbcNONE && !ir->bPeriodicMols && DOMAINDECOMP(cr))
 +        {
 +            /* Make molecules whole only for confout writing */
 +            do_pbc_mtop(fplog,ir->ePBC,state_global->box,top_global,
 +                        state_global->x);
 +        }
 +
 +        write_sto_conf_mtop(confout,
 +                            *top_global->name,top_global,
 +                            state_global->x,NULL,ir->ePBC,state_global->box);
 +    }
 +}
 +
 +static void do_em_step(t_commrec *cr,t_inputrec *ir,t_mdatoms *md,
 +                     em_state_t *ems1,real a,rvec *f,em_state_t *ems2,
 +                     gmx_constr_t constr,gmx_localtop_t *top,
 +                     t_nrnb *nrnb,gmx_wallcycle_t wcycle,
 +                     gmx_large_int_t count)
 +
 +{
 +  t_state *s1,*s2;
 +  int  start,end,gf,i,m;
 +  rvec *x1,*x2;
 +  real dvdlambda;
 +
 +  s1 = &ems1->s;
 +  s2 = &ems2->s;
 +
 +  if (DOMAINDECOMP(cr) && s1->ddp_count != cr->dd->ddp_count)
 +    gmx_incons("state mismatch in do_em_step");
 +
 +  s2->flags = s1->flags;
 +
 +  if (s2->nalloc != s1->nalloc) {
 +    s2->nalloc = s1->nalloc;
 +    srenew(s2->x,s1->nalloc);
 +    srenew(ems2->f,  s1->nalloc);
 +    if (s2->flags & (1<<estCGP))
 +      srenew(s2->cg_p,  s1->nalloc);
 +  }
 +
 +  s2->natoms = s1->natoms;
 +  /* Copy free energy state -> is this necessary? */
 +  for (i=0;i<efptNR;i++)
 +  {
 +      s2->lambda[i] = s1->lambda[i];
 +  }
 +  copy_mat(s1->box,s2->box);
 +
 +  start = md->start;
 +  end   = md->start + md->homenr;
 +
 +  x1 = s1->x;
 +  x2 = s2->x;
 +  gf = 0;
 +  for(i=start; i<end; i++) {
 +    if (md->cFREEZE)
 +      gf = md->cFREEZE[i];
 +    for(m=0; m<DIM; m++) {
 +      if (ir->opts.nFreeze[gf][m])
 +      x2[i][m] = x1[i][m];
 +      else
 +      x2[i][m] = x1[i][m] + a*f[i][m];
 +    }
 +  }
 +
 +  if (s2->flags & (1<<estCGP)) {
 +    /* Copy the CG p vector */
 +    x1 = s1->cg_p;
 +    x2 = s2->cg_p;
 +    for(i=start; i<end; i++)
 +      copy_rvec(x1[i],x2[i]);
 +  }
 +
 +  if (DOMAINDECOMP(cr)) {
 +    s2->ddp_count = s1->ddp_count;
 +    if (s2->cg_gl_nalloc < s1->cg_gl_nalloc) {
 +      s2->cg_gl_nalloc = s1->cg_gl_nalloc;
 +      srenew(s2->cg_gl,s2->cg_gl_nalloc);
 +    }
 +    s2->ncg_gl = s1->ncg_gl;
 +    for(i=0; i<s2->ncg_gl; i++)
 +      s2->cg_gl[i] = s1->cg_gl[i];
 +    s2->ddp_count_cg_gl = s1->ddp_count_cg_gl;
 +  }
 +
 +  if (constr) {
 +    wallcycle_start(wcycle,ewcCONSTR);
 +    dvdlambda = 0;
 +    constrain(NULL,TRUE,TRUE,constr,&top->idef,
 +              ir,NULL,cr,count,0,md,
 +              s1->x,s2->x,NULL,s2->box,s2->lambda[efptBONDED],
 +              &dvdlambda,NULL,NULL,nrnb,econqCoord,FALSE,0,0);
 +    wallcycle_stop(wcycle,ewcCONSTR);
 +  }
 +}
 +
 +static void em_dd_partition_system(FILE *fplog,int step,t_commrec *cr,
 +                                   gmx_mtop_t *top_global,t_inputrec *ir,
 +                                   em_state_t *ems,gmx_localtop_t *top,
 +                                   t_mdatoms *mdatoms,t_forcerec *fr,
 +                                   gmx_vsite_t *vsite,gmx_constr_t constr,
 +                                   t_nrnb *nrnb,gmx_wallcycle_t wcycle)
 +{
 +    /* Repartition the domain decomposition */
 +    wallcycle_start(wcycle,ewcDOMDEC);
 +    dd_partition_system(fplog,step,cr,FALSE,1,
 +                        NULL,top_global,ir,
 +                        &ems->s,&ems->f,
 +                        mdatoms,top,fr,vsite,NULL,constr,
 +                        nrnb,wcycle,FALSE);
 +    dd_store_state(cr->dd,&ems->s);
 +    wallcycle_stop(wcycle,ewcDOMDEC);
 +}
 +
 +static void evaluate_energy(FILE *fplog,gmx_bool bVerbose,t_commrec *cr,
 +                            t_state *state_global,gmx_mtop_t *top_global,
 +                            em_state_t *ems,gmx_localtop_t *top,
 +                            t_inputrec *inputrec,
 +                            t_nrnb *nrnb,gmx_wallcycle_t wcycle,
 +                            gmx_global_stat_t gstat,
 +                            gmx_vsite_t *vsite,gmx_constr_t constr,
 +                            t_fcdata *fcd,
 +                            t_graph *graph,t_mdatoms *mdatoms,
 +                            t_forcerec *fr,rvec mu_tot,
 +                            gmx_enerdata_t *enerd,tensor vir,tensor pres,
 +                            gmx_large_int_t count,gmx_bool bFirst)
 +{
 +  real t;
 +  gmx_bool bNS;
 +  int  nabnsb;
 +  tensor force_vir,shake_vir,ekin;
 +  real dvdlambda,prescorr,enercorr,dvdlcorr;
 +  real terminate=0;
 +
 +  /* Set the time to the initial time, the time does not change during EM */
 +  t = inputrec->init_t;
 +
 +  if (bFirst ||
 +      (DOMAINDECOMP(cr) && ems->s.ddp_count < cr->dd->ddp_count)) {
 +    /* This the first state or an old state used before the last ns */
 +    bNS = TRUE;
 +  } else {
 +    bNS = FALSE;
 +    if (inputrec->nstlist > 0) {
 +      bNS = TRUE;
 +    } else if (inputrec->nstlist == -1) {
 +      nabnsb = natoms_beyond_ns_buffer(inputrec,fr,&top->cgs,NULL,ems->s.x);
 +      if (PAR(cr))
 +      gmx_sumi(1,&nabnsb,cr);
 +      bNS = (nabnsb > 0);
 +    }
 +  }
 +
 +  if (vsite)
 +    construct_vsites(fplog,vsite,ems->s.x,nrnb,1,NULL,
 +                   top->idef.iparams,top->idef.il,
 +                   fr->ePBC,fr->bMolPBC,graph,cr,ems->s.box);
 +
 +  if (DOMAINDECOMP(cr)) {
 +    if (bNS) {
 +      /* Repartition the domain decomposition */
 +      em_dd_partition_system(fplog,count,cr,top_global,inputrec,
 +                           ems,top,mdatoms,fr,vsite,constr,
 +                           nrnb,wcycle);
 +    }
 +  }
 +
 +    /* Calc force & energy on new trial position  */
 +    /* do_force always puts the charge groups in the box and shifts again
 +     * We do not unshift, so molecules are always whole in congrad.c
 +     */
 +    do_force(fplog,cr,inputrec,
 +             count,nrnb,wcycle,top,top_global,&top_global->groups,
 +             ems->s.box,ems->s.x,&ems->s.hist,
 +             ems->f,force_vir,mdatoms,enerd,fcd,
 +             ems->s.lambda,graph,fr,vsite,mu_tot,t,NULL,NULL,TRUE,
 +             GMX_FORCE_STATECHANGED | GMX_FORCE_ALLFORCES | GMX_FORCE_VIRIAL |
 +             (bNS ? GMX_FORCE_NS | GMX_FORCE_DOLR : 0));
 +
 +    /* Clear the unused shake virial and pressure */
 +    clear_mat(shake_vir);
 +    clear_mat(pres);
 +
 +    /* Communicate stuff when parallel */
 +    if (PAR(cr) && inputrec->eI != eiNM)
 +    {
 +        wallcycle_start(wcycle,ewcMoveE);
 +
 +        global_stat(fplog,gstat,cr,enerd,force_vir,shake_vir,mu_tot,
 +                    inputrec,NULL,NULL,NULL,1,&terminate,
 +                    top_global,&ems->s,FALSE,
 +                    CGLO_ENERGY |
 +                    CGLO_PRESSURE |
 +                    CGLO_CONSTRAINT |
 +                    CGLO_FIRSTITERATE);
 +
 +        wallcycle_stop(wcycle,ewcMoveE);
 +    }
 +
 +    /* Calculate long range corrections to pressure and energy */
 +    calc_dispcorr(fplog,inputrec,fr,count,top_global->natoms,ems->s.box,ems->s.lambda[efptVDW],
 +                  pres,force_vir,&prescorr,&enercorr,&dvdlcorr);
 +    enerd->term[F_DISPCORR] = enercorr;
 +    enerd->term[F_EPOT] += enercorr;
 +    enerd->term[F_PRES] += prescorr;
 +    enerd->term[F_DVDL] += dvdlcorr;
 +
 +  ems->epot = enerd->term[F_EPOT];
 +
 +  if (constr) {
 +    /* Project out the constraint components of the force */
 +    wallcycle_start(wcycle,ewcCONSTR);
 +    dvdlambda = 0;
 +    constrain(NULL,FALSE,FALSE,constr,&top->idef,
 +              inputrec,NULL,cr,count,0,mdatoms,
 +              ems->s.x,ems->f,ems->f,ems->s.box,ems->s.lambda[efptBONDED],&dvdlambda,
 +              NULL,&shake_vir,nrnb,econqForceDispl,FALSE,0,0);
 +    if (fr->bSepDVDL && fplog)
 +      fprintf(fplog,sepdvdlformat,"Constraints",t,dvdlambda);
 +    enerd->term[F_DVDL_BONDED] += dvdlambda;
 +    m_add(force_vir,shake_vir,vir);
 +    wallcycle_stop(wcycle,ewcCONSTR);
 +  } else {
 +    copy_mat(force_vir,vir);
 +  }
 +
 +  clear_mat(ekin);
 +  enerd->term[F_PRES] =
 +    calc_pres(fr->ePBC,inputrec->nwall,ems->s.box,ekin,vir,pres);
 +
 +  sum_dhdl(enerd,ems->s.lambda,inputrec->fepvals);
 +
 +    if (EI_ENERGY_MINIMIZATION(inputrec->eI))
 +    {
 +        get_state_f_norm_max(cr,&(inputrec->opts),mdatoms,ems);
 +    }
 +}
 +
 +static double reorder_partsum(t_commrec *cr,t_grpopts *opts,t_mdatoms *mdatoms,
 +                            gmx_mtop_t *mtop,
 +                            em_state_t *s_min,em_state_t *s_b)
 +{
 +  rvec *fm,*fb,*fmg;
 +  t_block *cgs_gl;
 +  int ncg,*cg_gl,*index,c,cg,i,a0,a1,a,gf,m;
 +  double partsum;
 +  unsigned char *grpnrFREEZE;
 +
 +  if (debug)
 +    fprintf(debug,"Doing reorder_partsum\n");
 +
 +  fm = s_min->f;
 +  fb = s_b->f;
 +
 +  cgs_gl = dd_charge_groups_global(cr->dd);
 +  index = cgs_gl->index;
 +
 +  /* Collect fm in a global vector fmg.
 +   * This conflicts with the spirit of domain decomposition,
 +   * but to fully optimize this a much more complicated algorithm is required.
 +   */
 +  snew(fmg,mtop->natoms);
 +
 +  ncg   = s_min->s.ncg_gl;
 +  cg_gl = s_min->s.cg_gl;
 +  i = 0;
 +  for(c=0; c<ncg; c++) {
 +    cg = cg_gl[c];
 +    a0 = index[cg];
 +    a1 = index[cg+1];
 +    for(a=a0; a<a1; a++) {
 +      copy_rvec(fm[i],fmg[a]);
 +      i++;
 +    }
 +  }
 +  gmx_sum(mtop->natoms*3,fmg[0],cr);
 +
 +  /* Now we will determine the part of the sum for the cgs in state s_b */
 +  ncg   = s_b->s.ncg_gl;
 +  cg_gl = s_b->s.cg_gl;
 +  partsum = 0;
 +  i = 0;
 +  gf = 0;
 +  grpnrFREEZE = mtop->groups.grpnr[egcFREEZE];
 +  for(c=0; c<ncg; c++) {
 +    cg = cg_gl[c];
 +    a0 = index[cg];
 +    a1 = index[cg+1];
 +    for(a=a0; a<a1; a++) {
 +      if (mdatoms->cFREEZE && grpnrFREEZE) {
 +      gf = grpnrFREEZE[i];
 +      }
 +      for(m=0; m<DIM; m++) {
 +      if (!opts->nFreeze[gf][m]) {
 +        partsum += (fb[i][m] - fmg[a][m])*fb[i][m];
 +      }
 +      }
 +      i++;
 +    }
 +  }
 +
 +  sfree(fmg);
 +
 +  return partsum;
 +}
 +
 +static real pr_beta(t_commrec *cr,t_grpopts *opts,t_mdatoms *mdatoms,
 +                  gmx_mtop_t *mtop,
 +                  em_state_t *s_min,em_state_t *s_b)
 +{
 +  rvec *fm,*fb;
 +  double sum;
 +  int  gf,i,m;
 +
 +  /* This is just the classical Polak-Ribiere calculation of beta;
 +   * it looks a bit complicated since we take freeze groups into account,
 +   * and might have to sum it in parallel runs.
 +   */
 +
 +  if (!DOMAINDECOMP(cr) ||
 +      (s_min->s.ddp_count == cr->dd->ddp_count &&
 +       s_b->s.ddp_count   == cr->dd->ddp_count)) {
 +    fm = s_min->f;
 +    fb = s_b->f;
 +    sum = 0;
 +    gf = 0;
 +    /* This part of code can be incorrect with DD,
 +     * since the atom ordering in s_b and s_min might differ.
 +     */
 +    for(i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
 +      if (mdatoms->cFREEZE)
 +      gf = mdatoms->cFREEZE[i];
 +      for(m=0; m<DIM; m++)
 +      if (!opts->nFreeze[gf][m]) {
 +        sum += (fb[i][m] - fm[i][m])*fb[i][m];
 +      }
 +    }
 +  } else {
 +    /* We need to reorder cgs while summing */
 +    sum = reorder_partsum(cr,opts,mdatoms,mtop,s_min,s_b);
 +  }
 +  if (PAR(cr))
 +    gmx_sumd(1,&sum,cr);
 +
 +  return sum/sqr(s_min->fnorm);
 +}
 +
 +double do_cg(FILE *fplog,t_commrec *cr,
 +             int nfile,const t_filenm fnm[],
 +             const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
 +             int nstglobalcomm,
 +             gmx_vsite_t *vsite,gmx_constr_t constr,
 +             int stepout,
 +             t_inputrec *inputrec,
 +             gmx_mtop_t *top_global,t_fcdata *fcd,
 +             t_state *state_global,
 +             t_mdatoms *mdatoms,
 +             t_nrnb *nrnb,gmx_wallcycle_t wcycle,
 +             gmx_edsam_t ed,
 +             t_forcerec *fr,
 +             int repl_ex_nst, int repl_ex_nex, int repl_ex_seed,
 +             gmx_membed_t membed,
 +             real cpt_period,real max_hours,
 +             const char *deviceOptions,
 +             unsigned long Flags,
 +             gmx_runtime_t *runtime)
 +{
 +  const char *CG="Polak-Ribiere Conjugate Gradients";
 +
 +  em_state_t *s_min,*s_a,*s_b,*s_c;
 +  gmx_localtop_t *top;
 +  gmx_enerdata_t *enerd;
 +  rvec   *f;
 +  gmx_global_stat_t gstat;
 +  t_graph    *graph;
 +  rvec   *f_global,*p,*sf,*sfm;
 +  double gpa,gpb,gpc,tmp,sum[2],minstep;
 +  real   fnormn;
 +  real   stepsize;
 +  real   a,b,c,beta=0.0;
 +  real   epot_repl=0;
 +  real   pnorm;
 +  t_mdebin   *mdebin;
 +  gmx_bool   converged,foundlower;
 +  rvec   mu_tot;
 +  gmx_bool   do_log=FALSE,do_ene=FALSE,do_x,do_f;
 +  tensor vir,pres;
 +  int    number_steps,neval=0,nstcg=inputrec->nstcgsteep;
 +  gmx_mdoutf_t *outf;
 +  int    i,m,gf,step,nminstep;
 +  real   terminate=0;
 +
 +  step=0;
 +
 +  s_min = init_em_state();
 +  s_a   = init_em_state();
 +  s_b   = init_em_state();
 +  s_c   = init_em_state();
 +
 +  /* Init em and store the local state in s_min */
 +  init_em(fplog,CG,cr,inputrec,
 +          state_global,top_global,s_min,&top,&f,&f_global,
 +          nrnb,mu_tot,fr,&enerd,&graph,mdatoms,&gstat,vsite,constr,
 +          nfile,fnm,&outf,&mdebin);
 +
 +  /* Print to log file */
 +  print_em_start(fplog,cr,runtime,wcycle,CG);
 +
 +  /* Max number of steps */
 +  number_steps=inputrec->nsteps;
 +
 +  if (MASTER(cr))
 +    sp_header(stderr,CG,inputrec->em_tol,number_steps);
 +  if (fplog)
 +    sp_header(fplog,CG,inputrec->em_tol,number_steps);
 +
 +  /* Call the force routine and some auxiliary (neighboursearching etc.) */
 +  /* do_force always puts the charge groups in the box and shifts again
 +   * We do not unshift, so molecules are always whole in congrad.c
 +   */
 +  evaluate_energy(fplog,bVerbose,cr,
 +                state_global,top_global,s_min,top,
 +                inputrec,nrnb,wcycle,gstat,
 +                vsite,constr,fcd,graph,mdatoms,fr,
 +                mu_tot,enerd,vir,pres,-1,TRUE);
 +  where();
 +
 +  if (MASTER(cr)) {
 +    /* Copy stuff to the energy bin for easy printing etc. */
 +    upd_mdebin(mdebin,FALSE,FALSE,(double)step,
 +               mdatoms->tmass,enerd,&s_min->s,inputrec->fepvals,inputrec->expandedvals,s_min->s.box,
 +               NULL,NULL,vir,pres,NULL,mu_tot,constr);
 +
 +    print_ebin_header(fplog,step,step,s_min->s.lambda[efptFEP]);
 +    print_ebin(outf->fp_ene,TRUE,FALSE,FALSE,fplog,step,step,eprNORMAL,
 +               TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
 +  }
 +  where();
 +
 +  /* Estimate/guess the initial stepsize */
 +  stepsize = inputrec->em_stepsize/s_min->fnorm;
 +
 +  if (MASTER(cr)) {
 +    fprintf(stderr,"   F-max             = %12.5e on atom %d\n",
 +          s_min->fmax,s_min->a_fmax+1);
 +    fprintf(stderr,"   F-Norm            = %12.5e\n",
 +          s_min->fnorm/sqrt(state_global->natoms));
 +    fprintf(stderr,"\n");
 +    /* and copy to the log file too... */
 +    fprintf(fplog,"   F-max             = %12.5e on atom %d\n",
 +          s_min->fmax,s_min->a_fmax+1);
 +    fprintf(fplog,"   F-Norm            = %12.5e\n",
 +          s_min->fnorm/sqrt(state_global->natoms));
 +    fprintf(fplog,"\n");
 +  }
 +  /* Start the loop over CG steps.
 +   * Each successful step is counted, and we continue until
 +   * we either converge or reach the max number of steps.
 +   */
 +  converged = FALSE;
 +  for(step=0; (number_steps<0 || (number_steps>=0 && step<=number_steps)) && !converged;step++) {
 +
 +    /* start taking steps in a new direction
 +     * First time we enter the routine, beta=0, and the direction is
 +     * simply the negative gradient.
 +     */
 +
 +    /* Calculate the new direction in p, and the gradient in this direction, gpa */
 +    p  = s_min->s.cg_p;
 +    sf = s_min->f;
 +    gpa = 0;
 +    gf = 0;
 +    for(i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
 +      if (mdatoms->cFREEZE)
 +      gf = mdatoms->cFREEZE[i];
 +      for(m=0; m<DIM; m++) {
 +      if (!inputrec->opts.nFreeze[gf][m]) {
 +        p[i][m] = sf[i][m] + beta*p[i][m];
 +        gpa -= p[i][m]*sf[i][m];
 +        /* f is negative gradient, thus the sign */
 +      } else {
 +          p[i][m] = 0;
 +      }
 +      }
 +    }
 +
 +    /* Sum the gradient along the line across CPUs */
 +    if (PAR(cr))
 +      gmx_sumd(1,&gpa,cr);
 +
 +    /* Calculate the norm of the search vector */
 +    get_f_norm_max(cr,&(inputrec->opts),mdatoms,p,&pnorm,NULL,NULL);
 +
 +    /* Just in case stepsize reaches zero due to numerical precision... */
 +    if(stepsize<=0)
 +      stepsize = inputrec->em_stepsize/pnorm;
 +
 +    /*
 +     * Double check the value of the derivative in the search direction.
 +     * If it is positive it must be due to the old information in the
 +     * CG formula, so just remove that and start over with beta=0.
 +     * This corresponds to a steepest descent step.
 +     */
 +    if(gpa>0) {
 +      beta = 0;
 +      step--; /* Don't count this step since we are restarting */
 +      continue; /* Go back to the beginning of the big for-loop */
 +    }
 +
 +    /* Calculate minimum allowed stepsize, before the average (norm)
 +     * relative change in coordinate is smaller than precision
 +     */
 +    minstep=0;
 +    for (i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
 +      for(m=0; m<DIM; m++) {
 +      tmp = fabs(s_min->s.x[i][m]);
 +      if(tmp < 1.0)
 +        tmp = 1.0;
 +      tmp = p[i][m]/tmp;
 +      minstep += tmp*tmp;
 +      }
 +    }
 +    /* Add up from all CPUs */
 +    if(PAR(cr))
 +      gmx_sumd(1,&minstep,cr);
 +
 +    minstep = GMX_REAL_EPS/sqrt(minstep/(3*state_global->natoms));
 +
 +    if(stepsize<minstep) {
 +      converged=TRUE;
 +      break;
 +    }
 +
 +    /* Write coordinates if necessary */
 +    do_x = do_per_step(step,inputrec->nstxout);
 +    do_f = do_per_step(step,inputrec->nstfout);
 +
 +    write_em_traj(fplog,cr,outf,do_x,do_f,NULL,
 +                  top_global,inputrec,step,
 +                  s_min,state_global,f_global);
 +
 +    /* Take a step downhill.
 +     * In theory, we should minimize the function along this direction.
 +     * That is quite possible, but it turns out to take 5-10 function evaluations
 +     * for each line. However, we dont really need to find the exact minimum -
 +     * it is much better to start a new CG step in a modified direction as soon
 +     * as we are close to it. This will save a lot of energy evaluations.
 +     *
 +     * In practice, we just try to take a single step.
 +     * If it worked (i.e. lowered the energy), we increase the stepsize but
 +     * the continue straight to the next CG step without trying to find any minimum.
 +     * If it didn't work (higher energy), there must be a minimum somewhere between
 +     * the old position and the new one.
 +     *
 +     * Due to the finite numerical accuracy, it turns out that it is a good idea
 +     * to even accept a SMALL increase in energy, if the derivative is still downhill.
 +     * This leads to lower final energies in the tests I've done. / Erik
 +     */
 +    s_a->epot = s_min->epot;
 +    a = 0.0;
 +    c = a + stepsize; /* reference position along line is zero */
 +
 +    if (DOMAINDECOMP(cr) && s_min->s.ddp_count < cr->dd->ddp_count) {
 +      em_dd_partition_system(fplog,step,cr,top_global,inputrec,
 +                           s_min,top,mdatoms,fr,vsite,constr,
 +                           nrnb,wcycle);
 +    }
 +
 +    /* Take a trial step (new coords in s_c) */
 +    do_em_step(cr,inputrec,mdatoms,s_min,c,s_min->s.cg_p,s_c,
 +             constr,top,nrnb,wcycle,-1);
 +
 +    neval++;
 +    /* Calculate energy for the trial step */
 +    evaluate_energy(fplog,bVerbose,cr,
 +                  state_global,top_global,s_c,top,
 +                  inputrec,nrnb,wcycle,gstat,
 +                  vsite,constr,fcd,graph,mdatoms,fr,
 +                  mu_tot,enerd,vir,pres,-1,FALSE);
 +
 +    /* Calc derivative along line */
 +    p  = s_c->s.cg_p;
 +    sf = s_c->f;
 +    gpc=0;
 +    for(i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
 +      for(m=0; m<DIM; m++)
 +        gpc -= p[i][m]*sf[i][m];  /* f is negative gradient, thus the sign */
 +    }
 +    /* Sum the gradient along the line across CPUs */
 +    if (PAR(cr))
 +      gmx_sumd(1,&gpc,cr);
 +
 +    /* This is the max amount of increase in energy we tolerate */
 +    tmp=sqrt(GMX_REAL_EPS)*fabs(s_a->epot);
 +
 +    /* Accept the step if the energy is lower, or if it is not significantly higher
 +     * and the line derivative is still negative.
 +     */
 +    if (s_c->epot < s_a->epot || (gpc < 0 && s_c->epot < (s_a->epot + tmp))) {
 +      foundlower = TRUE;
 +      /* Great, we found a better energy. Increase step for next iteration
 +       * if we are still going down, decrease it otherwise
 +       */
 +      if(gpc<0)
 +      stepsize *= 1.618034;  /* The golden section */
 +      else
 +      stepsize *= 0.618034;  /* 1/golden section */
 +    } else {
 +      /* New energy is the same or higher. We will have to do some work
 +       * to find a smaller value in the interval. Take smaller step next time!
 +       */
 +      foundlower = FALSE;
 +      stepsize *= 0.618034;
 +    }
 +
 +
 +
 +
 +    /* OK, if we didn't find a lower value we will have to locate one now - there must
 +     * be one in the interval [a=0,c].
 +     * The same thing is valid here, though: Don't spend dozens of iterations to find
 +     * the line minimum. We try to interpolate based on the derivative at the endpoints,
 +     * and only continue until we find a lower value. In most cases this means 1-2 iterations.
 +     *
 +     * I also have a safeguard for potentially really patological functions so we never
 +     * take more than 20 steps before we give up ...
 +     *
 +     * If we already found a lower value we just skip this step and continue to the update.
 +     */
 +    if (!foundlower) {
 +      nminstep=0;
 +
 +      do {
 +      /* Select a new trial point.
 +       * If the derivatives at points a & c have different sign we interpolate to zero,
 +       * otherwise just do a bisection.
 +       */
 +      if(gpa<0 && gpc>0)
 +        b = a + gpa*(a-c)/(gpc-gpa);
 +      else
 +        b = 0.5*(a+c);
 +
 +      /* safeguard if interpolation close to machine accuracy causes errors:
 +       * never go outside the interval
 +       */
 +      if(b<=a || b>=c)
 +        b = 0.5*(a+c);
 +
 +      if (DOMAINDECOMP(cr) && s_min->s.ddp_count != cr->dd->ddp_count) {
 +        /* Reload the old state */
 +        em_dd_partition_system(fplog,-1,cr,top_global,inputrec,
 +                               s_min,top,mdatoms,fr,vsite,constr,
 +                               nrnb,wcycle);
 +      }
 +
 +      /* Take a trial step to this new point - new coords in s_b */
 +      do_em_step(cr,inputrec,mdatoms,s_min,b,s_min->s.cg_p,s_b,
 +                 constr,top,nrnb,wcycle,-1);
 +
 +      neval++;
 +      /* Calculate energy for the trial step */
 +      evaluate_energy(fplog,bVerbose,cr,
 +                      state_global,top_global,s_b,top,
 +                      inputrec,nrnb,wcycle,gstat,
 +                      vsite,constr,fcd,graph,mdatoms,fr,
 +                      mu_tot,enerd,vir,pres,-1,FALSE);
 +
 +      /* p does not change within a step, but since the domain decomposition
 +       * might change, we have to use cg_p of s_b here.
 +       */
 +      p  = s_b->s.cg_p;
 +      sf = s_b->f;
 +      gpb=0;
 +      for(i=mdatoms->start; i<mdatoms->start+mdatoms->homenr; i++) {
 +        for(m=0; m<DIM; m++)
 +            gpb -= p[i][m]*sf[i][m];   /* f is negative gradient, thus the sign */
 +      }
 +      /* Sum the gradient along the line across CPUs */
 +      if (PAR(cr))
 +        gmx_sumd(1,&gpb,cr);
 +
 +      if (debug)
 +        fprintf(debug,"CGE: EpotA %f EpotB %f EpotC %f gpb %f\n",
 +                s_a->epot,s_b->epot,s_c->epot,gpb);
 +
 +      epot_repl = s_b->epot;
 +
 +      /* Keep one of the intervals based on the value of the derivative at the new point */
 +      if (gpb > 0) {
 +        /* Replace c endpoint with b */
 +        swap_em_state(s_b,s_c);
 +        c = b;
 +        gpc = gpb;
 +      } else {
 +        /* Replace a endpoint with b */
 +        swap_em_state(s_b,s_a);
 +        a = b;
 +        gpa = gpb;
 +      }
 +
 +      /*
 +       * Stop search as soon as we find a value smaller than the endpoints.
 +       * Never run more than 20 steps, no matter what.
 +       */
 +      nminstep++;
 +      } while ((epot_repl > s_a->epot || epot_repl > s_c->epot) &&
 +             (nminstep < 20));
 +
 +      if (fabs(epot_repl - s_min->epot) < fabs(s_min->epot)*GMX_REAL_EPS ||
 +        nminstep >= 20) {
 +      /* OK. We couldn't find a significantly lower energy.
 +       * If beta==0 this was steepest descent, and then we give up.
 +       * If not, set beta=0 and restart with steepest descent before quitting.
 +         */
 +      if (beta == 0.0) {
 +        /* Converged */
 +        converged = TRUE;
 +        break;
 +      } else {
 +        /* Reset memory before giving up */
 +        beta = 0.0;
 +        continue;
 +      }
 +      }
 +
 +      /* Select min energy state of A & C, put the best in B.
 +       */
 +      if (s_c->epot < s_a->epot) {
 +      if (debug)
 +        fprintf(debug,"CGE: C (%f) is lower than A (%f), moving C to B\n",
 +                s_c->epot,s_a->epot);
 +      swap_em_state(s_b,s_c);
 +      gpb = gpc;
 +      b = c;
 +      } else {
 +      if (debug)
 +        fprintf(debug,"CGE: A (%f) is lower than C (%f), moving A to B\n",
 +                s_a->epot,s_c->epot);
 +      swap_em_state(s_b,s_a);
 +      gpb = gpa;
 +      b = a;
 +      }
 +
 +    } else {
 +      if (debug)
 +      fprintf(debug,"CGE: Found a lower energy %f, moving C to B\n",
 +              s_c->epot);
 +      swap_em_state(s_b,s_c);
 +      gpb = gpc;
 +      b = c;
 +    }
 +
 +    /* new search direction */
 +    /* beta = 0 means forget all memory and restart with steepest descents. */
 +    if (nstcg && ((step % nstcg)==0))
 +      beta = 0.0;
 +    else {
 +      /* s_min->fnorm cannot be zero, because then we would have converged
 +       * and broken out.
 +       */
 +
 +      /* Polak-Ribiere update.
 +       * Change to fnorm2/fnorm2_old for Fletcher-Reeves
 +       */
 +      beta = pr_beta(cr,&inputrec->opts,mdatoms,top_global,s_min,s_b);
 +    }
 +    /* Limit beta to prevent oscillations */
 +    if (fabs(beta) > 5.0)
 +      beta = 0.0;
 +
 +
 +    /* update positions */
 +    swap_em_state(s_min,s_b);
 +    gpa = gpb;
 +
 +    /* Print it if necessary */
 +    if (MASTER(cr)) {
 +      if(bVerbose)
 +      fprintf(stderr,"\rStep %d, Epot=%12.6e, Fnorm=%9.3e, Fmax=%9.3e (atom %d)\n",
 +              step,s_min->epot,s_min->fnorm/sqrt(state_global->natoms),
 +              s_min->fmax,s_min->a_fmax+1);
 +      /* Store the new (lower) energies */
 +      upd_mdebin(mdebin,FALSE,FALSE,(double)step,
 +                 mdatoms->tmass,enerd,&s_min->s,inputrec->fepvals,inputrec->expandedvals,s_min->s.box,
 +                 NULL,NULL,vir,pres,NULL,mu_tot,constr);
 +
 +      do_log = do_per_step(step,inputrec->nstlog);
 +      do_ene = do_per_step(step,inputrec->nstenergy);
 +      if(do_log)
 +          print_ebin_header(fplog,step,step,s_min->s.lambda[efptFEP]);
 +      print_ebin(outf->fp_ene,do_ene,FALSE,FALSE,
 +               do_log ? fplog : NULL,step,step,eprNORMAL,
 +               TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
 +    }
 +
 +    /* Stop when the maximum force lies below tolerance.
 +     * If we have reached machine precision, converged is already set to true.
 +     */
 +    converged = converged || (s_min->fmax < inputrec->em_tol);
 +
 +  } /* End of the loop */
 +
 +  if (converged)
 +    step--; /* we never took that last step in this case */
 +
 +    if (s_min->fmax > inputrec->em_tol)
 +    {
 +        if (MASTER(cr))
 +        {
 +            warn_step(stderr,inputrec->em_tol,step-1==number_steps,FALSE);
 +            warn_step(fplog ,inputrec->em_tol,step-1==number_steps,FALSE);
 +        }
 +        converged = FALSE;
 +    }
 +
 +  if (MASTER(cr)) {
 +    /* If we printed energy and/or logfile last step (which was the last step)
 +     * we don't have to do it again, but otherwise print the final values.
 +     */
 +    if(!do_log) {
 +      /* Write final value to log since we didn't do anything the last step */
 +      print_ebin_header(fplog,step,step,s_min->s.lambda[efptFEP]);
 +    }
 +    if (!do_ene || !do_log) {
 +      /* Write final energy file entries */
 +      print_ebin(outf->fp_ene,!do_ene,FALSE,FALSE,
 +               !do_log ? fplog : NULL,step,step,eprNORMAL,
 +               TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
 +    }
 +  }
 +
 +  /* Print some stuff... */
 +  if (MASTER(cr))
 +    fprintf(stderr,"\nwriting lowest energy coordinates.\n");
 +
 +  /* IMPORTANT!
 +   * For accurate normal mode calculation it is imperative that we
 +   * store the last conformation into the full precision binary trajectory.
 +   *
 +   * However, we should only do it if we did NOT already write this step
 +   * above (which we did if do_x or do_f was true).
 +   */
 +  do_x = !do_per_step(step,inputrec->nstxout);
 +  do_f = (inputrec->nstfout > 0 && !do_per_step(step,inputrec->nstfout));
 +
 +  write_em_traj(fplog,cr,outf,do_x,do_f,ftp2fn(efSTO,nfile,fnm),
 +                top_global,inputrec,step,
 +                s_min,state_global,f_global);
 +
 +  fnormn = s_min->fnorm/sqrt(state_global->natoms);
 +
 +  if (MASTER(cr)) {
 +    print_converged(stderr,CG,inputrec->em_tol,step,converged,number_steps,
 +                  s_min->epot,s_min->fmax,s_min->a_fmax,fnormn);
 +    print_converged(fplog,CG,inputrec->em_tol,step,converged,number_steps,
 +                  s_min->epot,s_min->fmax,s_min->a_fmax,fnormn);
 +
 +    fprintf(fplog,"\nPerformed %d energy evaluations in total.\n",neval);
 +  }
 +
 +  finish_em(fplog,cr,outf,runtime,wcycle);
 +
 +  /* To print the actual number of steps we needed somewhere */
 +  runtime->nsteps_done = step;
 +
 +  return 0;
 +} /* That's all folks */
 +
 +
 +double do_lbfgs(FILE *fplog,t_commrec *cr,
 +                int nfile,const t_filenm fnm[],
 +                const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
 +                int nstglobalcomm,
 +                gmx_vsite_t *vsite,gmx_constr_t constr,
 +                int stepout,
 +                t_inputrec *inputrec,
 +                gmx_mtop_t *top_global,t_fcdata *fcd,
 +                t_state *state,
 +                t_mdatoms *mdatoms,
 +                t_nrnb *nrnb,gmx_wallcycle_t wcycle,
 +                gmx_edsam_t ed,
 +                t_forcerec *fr,
 +                int repl_ex_nst, int repl_ex_nex, int repl_ex_seed,
 +                gmx_membed_t membed,
 +                real cpt_period,real max_hours,
 +                const char *deviceOptions,
 +                unsigned long Flags,
 +                gmx_runtime_t *runtime)
 +{
 +  static const char *LBFGS="Low-Memory BFGS Minimizer";
 +  em_state_t ems;
 +  gmx_localtop_t *top;
 +  gmx_enerdata_t *enerd;
 +  rvec   *f;
 +  gmx_global_stat_t gstat;
 +  t_graph    *graph;
 +  rvec   *f_global;
 +  int    ncorr,nmaxcorr,point,cp,neval,nminstep;
 +  double stepsize,gpa,gpb,gpc,tmp,minstep;
 +  real   *rho,*alpha,*ff,*xx,*p,*s,*lastx,*lastf,**dx,**dg;
 +  real   *xa,*xb,*xc,*fa,*fb,*fc,*xtmp,*ftmp;
 +  real   a,b,c,maxdelta,delta;
 +  real   diag,Epot0,Epot,EpotA,EpotB,EpotC;
 +  real   dgdx,dgdg,sq,yr,beta;
 +  t_mdebin   *mdebin;
 +  gmx_bool   converged,first;
 +  rvec   mu_tot;
 +  real   fnorm,fmax;
 +  gmx_bool   do_log,do_ene,do_x,do_f,foundlower,*frozen;
 +  tensor vir,pres;
 +  int    start,end,number_steps;
 +  gmx_mdoutf_t *outf;
 +  int    i,k,m,n,nfmax,gf,step;
++  int    mdof_flags;
 +  /* not used */
 +  real   terminate;
 +
 +  if (PAR(cr))
 +    gmx_fatal(FARGS,"Cannot do parallel L-BFGS Minimization - yet.\n");
 +
 +  n = 3*state->natoms;
 +  nmaxcorr = inputrec->nbfgscorr;
 +
 +  /* Allocate memory */
 +  /* Use pointers to real so we dont have to loop over both atoms and
 +   * dimensions all the time...
 +   * x/f are allocated as rvec *, so make new x0/f0 pointers-to-real
 +   * that point to the same memory.
 +   */
 +  snew(xa,n);
 +  snew(xb,n);
 +  snew(xc,n);
 +  snew(fa,n);
 +  snew(fb,n);
 +  snew(fc,n);
 +  snew(frozen,n);
 +
 +  snew(p,n);
 +  snew(lastx,n);
 +  snew(lastf,n);
 +  snew(rho,nmaxcorr);
 +  snew(alpha,nmaxcorr);
 +
 +  snew(dx,nmaxcorr);
 +  for(i=0;i<nmaxcorr;i++)
 +    snew(dx[i],n);
 +
 +  snew(dg,nmaxcorr);
 +  for(i=0;i<nmaxcorr;i++)
 +    snew(dg[i],n);
 +
 +  step = 0;
 +  neval = 0;
 +
 +  /* Init em */
 +  init_em(fplog,LBFGS,cr,inputrec,
 +          state,top_global,&ems,&top,&f,&f_global,
 +          nrnb,mu_tot,fr,&enerd,&graph,mdatoms,&gstat,vsite,constr,
 +          nfile,fnm,&outf,&mdebin);
 +  /* Do_lbfgs is not completely updated like do_steep and do_cg,
 +   * so we free some memory again.
 +   */
 +  sfree(ems.s.x);
 +  sfree(ems.f);
 +
 +  xx = (real *)state->x;
 +  ff = (real *)f;
 +
 +  start = mdatoms->start;
 +  end   = mdatoms->homenr + start;
 +
 +  /* Print to log file */
 +  print_em_start(fplog,cr,runtime,wcycle,LBFGS);
 +
 +  do_log = do_ene = do_x = do_f = TRUE;
 +
 +  /* Max number of steps */
 +  number_steps=inputrec->nsteps;
 +
 +  /* Create a 3*natoms index to tell whether each degree of freedom is frozen */
 +  gf = 0;
 +  for(i=start; i<end; i++) {
 +    if (mdatoms->cFREEZE)
 +      gf = mdatoms->cFREEZE[i];
 +     for(m=0; m<DIM; m++)
 +       frozen[3*i+m]=inputrec->opts.nFreeze[gf][m];
 +  }
 +  if (MASTER(cr))
 +    sp_header(stderr,LBFGS,inputrec->em_tol,number_steps);
 +  if (fplog)
 +    sp_header(fplog,LBFGS,inputrec->em_tol,number_steps);
 +
 +  if (vsite)
 +    construct_vsites(fplog,vsite,state->x,nrnb,1,NULL,
 +                   top->idef.iparams,top->idef.il,
 +                   fr->ePBC,fr->bMolPBC,graph,cr,state->box);
 +
 +  /* Call the force routine and some auxiliary (neighboursearching etc.) */
 +  /* do_force always puts the charge groups in the box and shifts again
 +   * We do not unshift, so molecules are always whole
 +   */
 +  neval++;
 +  ems.s.x = state->x;
 +  ems.f = f;
 +  evaluate_energy(fplog,bVerbose,cr,
 +                state,top_global,&ems,top,
 +                inputrec,nrnb,wcycle,gstat,
 +                vsite,constr,fcd,graph,mdatoms,fr,
 +                mu_tot,enerd,vir,pres,-1,TRUE);
 +  where();
 +
 +  if (MASTER(cr)) {
 +    /* Copy stuff to the energy bin for easy printing etc. */
 +    upd_mdebin(mdebin,FALSE,FALSE,(double)step,
 +               mdatoms->tmass,enerd,state,inputrec->fepvals,inputrec->expandedvals,state->box,
 +               NULL,NULL,vir,pres,NULL,mu_tot,constr);
 +
 +    print_ebin_header(fplog,step,step,state->lambda[efptFEP]);
 +    print_ebin(outf->fp_ene,TRUE,FALSE,FALSE,fplog,step,step,eprNORMAL,
 +               TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
 +  }
 +  where();
 +
 +  /* This is the starting energy */
 +  Epot = enerd->term[F_EPOT];
 +
 +  fnorm = ems.fnorm;
 +  fmax  = ems.fmax;
 +  nfmax = ems.a_fmax;
 +
 +  /* Set the initial step.
 +   * since it will be multiplied by the non-normalized search direction
 +   * vector (force vector the first time), we scale it by the
 +   * norm of the force.
 +   */
 +
 +  if (MASTER(cr)) {
 +    fprintf(stderr,"Using %d BFGS correction steps.\n\n",nmaxcorr);
 +    fprintf(stderr,"   F-max             = %12.5e on atom %d\n",fmax,nfmax+1);
 +    fprintf(stderr,"   F-Norm            = %12.5e\n",fnorm/sqrt(state->natoms));
 +    fprintf(stderr,"\n");
 +    /* and copy to the log file too... */
 +    fprintf(fplog,"Using %d BFGS correction steps.\n\n",nmaxcorr);
 +    fprintf(fplog,"   F-max             = %12.5e on atom %d\n",fmax,nfmax+1);
 +    fprintf(fplog,"   F-Norm            = %12.5e\n",fnorm/sqrt(state->natoms));
 +    fprintf(fplog,"\n");
 +  }
 +
 +  point=0;
 +  for(i=0;i<n;i++)
 +    if(!frozen[i])
 +      dx[point][i] = ff[i];  /* Initial search direction */
 +    else
 +      dx[point][i] = 0;
 +
 +  stepsize = 1.0/fnorm;
 +  converged = FALSE;
 +
 +  /* Start the loop over BFGS steps.
 +   * Each successful step is counted, and we continue until
 +   * we either converge or reach the max number of steps.
 +   */
 +
 +  ncorr=0;
 +
 +  /* Set the gradient from the force */
 +  converged = FALSE;
 +  for(step=0; (number_steps<0 || (number_steps>=0 && step<=number_steps)) && !converged; step++) {
 +
 +    /* Write coordinates if necessary */
 +    do_x = do_per_step(step,inputrec->nstxout);
 +    do_f = do_per_step(step,inputrec->nstfout);
 +
++    mdof_flags = 0;
++    if (do_x)
++    {
++        mdof_flags |= MDOF_X;
++    }
++
++    if (do_f)
++    {
++        mdof_flags |= MDOF_F;
++    }
++
++    write_traj(fplog,cr,outf,mdof_flags,
 +               top_global,step,(real)step,state,state,f,f,NULL,NULL);
 +
 +    /* Do the linesearching in the direction dx[point][0..(n-1)] */
 +
 +    /* pointer to current direction - point=0 first time here */
 +    s=dx[point];
 +
 +    /* calculate line gradient */
 +    for(gpa=0,i=0;i<n;i++)
 +      gpa-=s[i]*ff[i];
 +
 +    /* Calculate minimum allowed stepsize, before the average (norm)
 +     * relative change in coordinate is smaller than precision
 +     */
 +    for(minstep=0,i=0;i<n;i++) {
 +      tmp=fabs(xx[i]);
 +      if(tmp<1.0)
 +      tmp=1.0;
 +      tmp = s[i]/tmp;
 +      minstep += tmp*tmp;
 +    }
 +    minstep = GMX_REAL_EPS/sqrt(minstep/n);
 +
 +    if(stepsize<minstep) {
 +      converged=TRUE;
 +      break;
 +    }
 +
 +    /* Store old forces and coordinates */
 +    for(i=0;i<n;i++) {
 +      lastx[i]=xx[i];
 +      lastf[i]=ff[i];
 +    }
 +    Epot0=Epot;
 +
 +    first=TRUE;
 +
 +    for(i=0;i<n;i++)
 +      xa[i]=xx[i];
 +
 +    /* Take a step downhill.
 +     * In theory, we should minimize the function along this direction.
 +     * That is quite possible, but it turns out to take 5-10 function evaluations
 +     * for each line. However, we dont really need to find the exact minimum -
 +     * it is much better to start a new BFGS step in a modified direction as soon
 +     * as we are close to it. This will save a lot of energy evaluations.
 +     *
 +     * In practice, we just try to take a single step.
 +     * If it worked (i.e. lowered the energy), we increase the stepsize but
 +     * the continue straight to the next BFGS step without trying to find any minimum.
 +     * If it didn't work (higher energy), there must be a minimum somewhere between
 +     * the old position and the new one.
 +     *
 +     * Due to the finite numerical accuracy, it turns out that it is a good idea
 +     * to even accept a SMALL increase in energy, if the derivative is still downhill.
 +     * This leads to lower final energies in the tests I've done. / Erik
 +     */
 +    foundlower=FALSE;
 +    EpotA = Epot0;
 +    a = 0.0;
 +    c = a + stepsize; /* reference position along line is zero */
 +
 +    /* Check stepsize first. We do not allow displacements
 +     * larger than emstep.
 +     */
 +    do {
 +      c = a + stepsize;
 +      maxdelta=0;
 +      for(i=0;i<n;i++) {
 +      delta=c*s[i];
 +      if(delta>maxdelta)
 +        maxdelta=delta;
 +      }
 +      if(maxdelta>inputrec->em_stepsize)
 +      stepsize*=0.1;
 +    } while(maxdelta>inputrec->em_stepsize);
 +
 +    /* Take a trial step */
 +    for (i=0; i<n; i++)
 +      xc[i] = lastx[i] + c*s[i];
 +
 +    neval++;
 +    /* Calculate energy for the trial step */
 +    ems.s.x = (rvec *)xc;
 +    ems.f   = (rvec *)fc;
 +    evaluate_energy(fplog,bVerbose,cr,
 +                  state,top_global,&ems,top,
 +                  inputrec,nrnb,wcycle,gstat,
 +                  vsite,constr,fcd,graph,mdatoms,fr,
 +                  mu_tot,enerd,vir,pres,step,FALSE);
 +    EpotC = ems.epot;
 +
 +    /* Calc derivative along line */
 +    for(gpc=0,i=0; i<n; i++) {
 +      gpc -= s[i]*fc[i];   /* f is negative gradient, thus the sign */
 +    }
 +    /* Sum the gradient along the line across CPUs */
 +    if (PAR(cr))
 +      gmx_sumd(1,&gpc,cr);
 +
 +     /* This is the max amount of increase in energy we tolerate */
 +   tmp=sqrt(GMX_REAL_EPS)*fabs(EpotA);
 +
 +    /* Accept the step if the energy is lower, or if it is not significantly higher
 +     * and the line derivative is still negative.
 +     */
 +    if(EpotC<EpotA || (gpc<0 && EpotC<(EpotA+tmp))) {
 +      foundlower = TRUE;
 +      /* Great, we found a better energy. Increase step for next iteration
 +       * if we are still going down, decrease it otherwise
 +       */
 +      if(gpc<0)
 +      stepsize *= 1.618034;  /* The golden section */
 +      else
 +      stepsize *= 0.618034;  /* 1/golden section */
 +    } else {
 +      /* New energy is the same or higher. We will have to do some work
 +       * to find a smaller value in the interval. Take smaller step next time!
 +       */
 +      foundlower = FALSE;
 +      stepsize *= 0.618034;
 +    }
 +
 +    /* OK, if we didn't find a lower value we will have to locate one now - there must
 +     * be one in the interval [a=0,c].
 +     * The same thing is valid here, though: Don't spend dozens of iterations to find
 +     * the line minimum. We try to interpolate based on the derivative at the endpoints,
 +     * and only continue until we find a lower value. In most cases this means 1-2 iterations.
 +     *
 +     * I also have a safeguard for potentially really patological functions so we never
 +     * take more than 20 steps before we give up ...
 +     *
 +     * If we already found a lower value we just skip this step and continue to the update.
 +     */
 +
 +    if(!foundlower) {
 +
 +      nminstep=0;
 +      do {
 +      /* Select a new trial point.
 +       * If the derivatives at points a & c have different sign we interpolate to zero,
 +       * otherwise just do a bisection.
 +       */
 +
 +      if(gpa<0 && gpc>0)
 +        b = a + gpa*(a-c)/(gpc-gpa);
 +      else
 +        b = 0.5*(a+c);
 +
 +      /* safeguard if interpolation close to machine accuracy causes errors:
 +       * never go outside the interval
 +       */
 +      if(b<=a || b>=c)
 +        b = 0.5*(a+c);
 +
 +      /* Take a trial step */
 +      for (i=0; i<n; i++)
 +        xb[i] = lastx[i] + b*s[i];
 +
 +      neval++;
 +      /* Calculate energy for the trial step */
 +      ems.s.x = (rvec *)xb;
 +      ems.f   = (rvec *)fb;
 +      evaluate_energy(fplog,bVerbose,cr,
 +                      state,top_global,&ems,top,
 +                      inputrec,nrnb,wcycle,gstat,
 +                      vsite,constr,fcd,graph,mdatoms,fr,
 +                      mu_tot,enerd,vir,pres,step,FALSE);
 +      EpotB = ems.epot;
 +
 +      fnorm = ems.fnorm;
 +
 +      for(gpb=0,i=0; i<n; i++)
 +        gpb -= s[i]*fb[i];   /* f is negative gradient, thus the sign */
 +
 +      /* Sum the gradient along the line across CPUs */
 +      if (PAR(cr))
 +        gmx_sumd(1,&gpb,cr);
 +
 +      /* Keep one of the intervals based on the value of the derivative at the new point */
 +      if(gpb>0) {
 +        /* Replace c endpoint with b */
 +        EpotC = EpotB;
 +        c = b;
 +        gpc = gpb;
 +        /* swap coord pointers b/c */
 +        xtmp = xb;
 +        ftmp = fb;
 +        xb = xc;
 +        fb = fc;
 +        xc = xtmp;
 +        fc = ftmp;
 +      } else {
 +        /* Replace a endpoint with b */
 +        EpotA = EpotB;
 +        a = b;
 +        gpa = gpb;
 +        /* swap coord pointers a/b */
 +        xtmp = xb;
 +        ftmp = fb;
 +        xb = xa;
 +        fb = fa;
 +        xa = xtmp;
 +        fa = ftmp;
 +      }
 +
 +      /*
 +       * Stop search as soon as we find a value smaller than the endpoints,
 +       * or if the tolerance is below machine precision.
 +       * Never run more than 20 steps, no matter what.
 +       */
 +      nminstep++;
 +      } while((EpotB>EpotA || EpotB>EpotC) && (nminstep<20));
 +
 +      if(fabs(EpotB-Epot0)<GMX_REAL_EPS || nminstep>=20) {
 +      /* OK. We couldn't find a significantly lower energy.
 +       * If ncorr==0 this was steepest descent, and then we give up.
 +       * If not, reset memory to restart as steepest descent before quitting.
 +         */
 +      if(ncorr==0) {
 +      /* Converged */
 +        converged=TRUE;
 +        break;
 +      } else {
 +        /* Reset memory */
 +        ncorr=0;
 +        /* Search in gradient direction */
 +        for(i=0;i<n;i++)
 +          dx[point][i]=ff[i];
 +        /* Reset stepsize */
 +        stepsize = 1.0/fnorm;
 +        continue;
 +      }
 +      }
 +
 +      /* Select min energy state of A & C, put the best in xx/ff/Epot
 +       */
 +      if(EpotC<EpotA) {
 +      Epot = EpotC;
 +      /* Use state C */
 +      for(i=0;i<n;i++) {
 +        xx[i]=xc[i];
 +        ff[i]=fc[i];
 +      }
 +      stepsize=c;
 +      } else {
 +      Epot = EpotA;
 +      /* Use state A */
 +      for(i=0;i<n;i++) {
 +        xx[i]=xa[i];
 +        ff[i]=fa[i];
 +      }
 +      stepsize=a;
 +      }
 +
 +    } else {
 +      /* found lower */
 +      Epot = EpotC;
 +      /* Use state C */
 +      for(i=0;i<n;i++) {
 +      xx[i]=xc[i];
 +      ff[i]=fc[i];
 +      }
 +      stepsize=c;
 +    }
 +
 +    /* Update the memory information, and calculate a new
 +     * approximation of the inverse hessian
 +     */
 +
 +    /* Have new data in Epot, xx, ff */
 +    if(ncorr<nmaxcorr)
 +      ncorr++;
 +
 +    for(i=0;i<n;i++) {
 +      dg[point][i]=lastf[i]-ff[i];
 +      dx[point][i]*=stepsize;
 +    }
 +
 +    dgdg=0;
 +    dgdx=0;
 +    for(i=0;i<n;i++) {
 +      dgdg+=dg[point][i]*dg[point][i];
 +      dgdx+=dg[point][i]*dx[point][i];
 +    }
 +
 +    diag=dgdx/dgdg;
 +
 +    rho[point]=1.0/dgdx;
 +    point++;
 +
 +    if(point>=nmaxcorr)
 +      point=0;
 +
 +    /* Update */
 +    for(i=0;i<n;i++)
 +      p[i]=ff[i];
 +
 +    cp=point;
 +
 +    /* Recursive update. First go back over the memory points */
 +    for(k=0;k<ncorr;k++) {
 +      cp--;
 +      if(cp<0)
 +      cp=ncorr-1;
 +
 +      sq=0;
 +      for(i=0;i<n;i++)
 +      sq+=dx[cp][i]*p[i];
 +
 +      alpha[cp]=rho[cp]*sq;
 +
 +      for(i=0;i<n;i++)
 +      p[i] -= alpha[cp]*dg[cp][i];
 +    }
 +
 +    for(i=0;i<n;i++)
 +      p[i] *= diag;
 +
 +    /* And then go forward again */
 +    for(k=0;k<ncorr;k++) {
 +      yr = 0;
 +      for(i=0;i<n;i++)
 +      yr += p[i]*dg[cp][i];
 +
 +      beta = rho[cp]*yr;
 +      beta = alpha[cp]-beta;
 +
 +      for(i=0;i<n;i++)
 +      p[i] += beta*dx[cp][i];
 +
 +      cp++;
 +      if(cp>=ncorr)
 +      cp=0;
 +    }
 +
 +    for(i=0;i<n;i++)
 +      if(!frozen[i])
 +      dx[point][i] = p[i];
 +      else
 +      dx[point][i] = 0;
 +
 +    stepsize=1.0;
 +
 +    /* Test whether the convergence criterion is met */
 +    get_f_norm_max(cr,&(inputrec->opts),mdatoms,f,&fnorm,&fmax,&nfmax);
 +
 +    /* Print it if necessary */
 +    if (MASTER(cr)) {
 +      if(bVerbose)
 +      fprintf(stderr,"\rStep %d, Epot=%12.6e, Fnorm=%9.3e, Fmax=%9.3e (atom %d)\n",
 +              step,Epot,fnorm/sqrt(state->natoms),fmax,nfmax+1);
 +      /* Store the new (lower) energies */
 +      upd_mdebin(mdebin,FALSE,FALSE,(double)step,
 +                 mdatoms->tmass,enerd,state,inputrec->fepvals,inputrec->expandedvals,state->box,
 +                 NULL,NULL,vir,pres,NULL,mu_tot,constr);
 +      do_log = do_per_step(step,inputrec->nstlog);
 +      do_ene = do_per_step(step,inputrec->nstenergy);
 +      if(do_log)
 +          print_ebin_header(fplog,step,step,state->lambda[efptFEP]);
 +      print_ebin(outf->fp_ene,do_ene,FALSE,FALSE,
 +               do_log ? fplog : NULL,step,step,eprNORMAL,
 +               TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
 +    }
 +
 +    /* Stop when the maximum force lies below tolerance.
 +     * If we have reached machine precision, converged is already set to true.
 +     */
 +
 +    converged = converged || (fmax < inputrec->em_tol);
 +
 +  } /* End of the loop */
 +
 +  if(converged)
 +    step--; /* we never took that last step in this case */
 +
 +    if(fmax>inputrec->em_tol)
 +    {
 +        if (MASTER(cr))
 +        {
 +            warn_step(stderr,inputrec->em_tol,step-1==number_steps,FALSE);
 +            warn_step(fplog ,inputrec->em_tol,step-1==number_steps,FALSE);
 +        }
 +        converged = FALSE;
 +    }
 +
 +  /* If we printed energy and/or logfile last step (which was the last step)
 +   * we don't have to do it again, but otherwise print the final values.
 +   */
 +  if(!do_log) /* Write final value to log since we didn't do anythin last step */
 +    print_ebin_header(fplog,step,step,state->lambda[efptFEP]);
 +  if(!do_ene || !do_log) /* Write final energy file entries */
 +    print_ebin(outf->fp_ene,!do_ene,FALSE,FALSE,
 +             !do_log ? fplog : NULL,step,step,eprNORMAL,
 +             TRUE,mdebin,fcd,&(top_global->groups),&(inputrec->opts));
 +
 +  /* Print some stuff... */
 +  if (MASTER(cr))
 +    fprintf(stderr,"\nwriting lowest energy coordinates.\n");
 +
 +  /* IMPORTANT!
 +   * For accurate normal mode calculation it is imperative that we
 +   * store the last conformation into the full precision binary trajectory.
 +   *
 +   * However, we should only do it if we did NOT already write this step
 +   * above (which we did if do_x or do_f was true).
 +   */
 +  do_x = !do_per_step(step,inputrec->nstxout);
 +  do_f = !do_per_step(step,inputrec->nstfout);
 +  write_em_traj(fplog,cr,outf,do_x,do_f,ftp2fn(efSTO,nfile,fnm),
 +                top_global,inputrec,step,
 +                &ems,state,f);
 +
 +  if (MASTER(cr)) {
 +    print_converged(stderr,LBFGS,inputrec->em_tol,step,converged,
 +                  number_steps,Epot,fmax,nfmax,fnorm/sqrt(state->natoms));
 +    print_converged(fplog,LBFGS,inputrec->em_tol,step,converged,
 +                  number_steps,Epot,fmax,nfmax,fnorm/sqrt(state->natoms));
 +
 +    fprintf(fplog,"\nPerformed %d energy evaluations in total.\n",neval);
 +  }
 +
 +  finish_em(fplog,cr,outf,runtime,wcycle);
 +
 +  /* To print the actual number of steps we needed somewhere */
 +  runtime->nsteps_done = step;
 +
 +  return 0;
 +} /* That's all folks */
 +
 +
 +double do_steep(FILE *fplog,t_commrec *cr,
 +                int nfile, const t_filenm fnm[],
 +                const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
 +                int nstglobalcomm,
 +                gmx_vsite_t *vsite,gmx_constr_t constr,
 +                int stepout,
 +                t_inputrec *inputrec,
 +                gmx_mtop_t *top_global,t_fcdata *fcd,
 +                t_state *state_global,
 +                t_mdatoms *mdatoms,
 +                t_nrnb *nrnb,gmx_wallcycle_t wcycle,
 +                gmx_edsam_t ed,
 +                t_forcerec *fr,
 +                int repl_ex_nst, int repl_ex_nex, int repl_ex_seed,
 +                gmx_membed_t membed,
 +                real cpt_period,real max_hours,
 +                const char *deviceOptions,
 +                unsigned long Flags,
 +                gmx_runtime_t *runtime)
 +{
 +  const char *SD="Steepest Descents";
 +  em_state_t *s_min,*s_try;
 +  rvec       *f_global;
 +  gmx_localtop_t *top;
 +  gmx_enerdata_t *enerd;
 +  rvec   *f;
 +  gmx_global_stat_t gstat;
 +  t_graph    *graph;
 +  real   stepsize,constepsize;
 +  real   ustep,dvdlambda,fnormn;
 +  gmx_mdoutf_t *outf;
 +  t_mdebin   *mdebin;
 +  gmx_bool   bDone,bAbort,do_x,do_f;
 +  tensor vir,pres;
 +  rvec   mu_tot;
 +  int    nsteps;
 +  int    count=0;
 +  int    steps_accepted=0;
 +  /* not used */
 +  real   terminate=0;
 +
 +  s_min = init_em_state();
 +  s_try = init_em_state();
 +
 +  /* Init em and store the local state in s_try */
 +  init_em(fplog,SD,cr,inputrec,
 +          state_global,top_global,s_try,&top,&f,&f_global,
 +          nrnb,mu_tot,fr,&enerd,&graph,mdatoms,&gstat,vsite,constr,
 +          nfile,fnm,&outf,&mdebin);
 +
 +  /* Print to log file  */
 +  print_em_start(fplog,cr,runtime,wcycle,SD);
 +
 +  /* Set variables for stepsize (in nm). This is the largest
 +   * step that we are going to make in any direction.
 +   */
 +  ustep = inputrec->em_stepsize;
 +  stepsize = 0;
 +
 +  /* Max number of steps  */
 +  nsteps = inputrec->nsteps;
 +
 +  if (MASTER(cr))
 +    /* Print to the screen  */
 +    sp_header(stderr,SD,inputrec->em_tol,nsteps);
 +  if (fplog)
 +    sp_header(fplog,SD,inputrec->em_tol,nsteps);
 +
 +  /**** HERE STARTS THE LOOP ****
 +   * count is the counter for the number of steps
 +   * bDone will be TRUE when the minimization has converged
 +   * bAbort will be TRUE when nsteps steps have been performed or when
 +   * the stepsize becomes smaller than is reasonable for machine precision
 +   */
 +  count  = 0;
 +  bDone  = FALSE;
 +  bAbort = FALSE;
 +  while( !bDone && !bAbort ) {
 +    bAbort = (nsteps >= 0) && (count == nsteps);
 +
 +    /* set new coordinates, except for first step */
 +    if (count > 0) {
 +      do_em_step(cr,inputrec,mdatoms,s_min,stepsize,s_min->f,s_try,
 +               constr,top,nrnb,wcycle,count);
 +    }
 +
 +    evaluate_energy(fplog,bVerbose,cr,
 +                  state_global,top_global,s_try,top,
 +                  inputrec,nrnb,wcycle,gstat,
 +                  vsite,constr,fcd,graph,mdatoms,fr,
 +                  mu_tot,enerd,vir,pres,count,count==0);
 +
 +    if (MASTER(cr))
 +      print_ebin_header(fplog,count,count,s_try->s.lambda[efptFEP]);
 +
 +    if (count == 0)
 +      s_min->epot = s_try->epot + 1;
 +
 +    /* Print it if necessary  */
 +    if (MASTER(cr)) {
 +      if (bVerbose) {
 +      fprintf(stderr,"Step=%5d, Dmax= %6.1e nm, Epot= %12.5e Fmax= %11.5e, atom= %d%c",
 +              count,ustep,s_try->epot,s_try->fmax,s_try->a_fmax+1,
 +              (s_try->epot < s_min->epot) ? '\n' : '\r');
 +      }
 +
 +      if (s_try->epot < s_min->epot) {
 +      /* Store the new (lower) energies  */
 +      upd_mdebin(mdebin,FALSE,FALSE,(double)count,
 +                 mdatoms->tmass,enerd,&s_try->s,inputrec->fepvals,inputrec->expandedvals,
 +                   s_try->s.box, NULL,NULL,vir,pres,NULL,mu_tot,constr);
 +      print_ebin(outf->fp_ene,TRUE,
 +                 do_per_step(steps_accepted,inputrec->nstdisreout),
 +                 do_per_step(steps_accepted,inputrec->nstorireout),
 +                 fplog,count,count,eprNORMAL,TRUE,
 +                 mdebin,fcd,&(top_global->groups),&(inputrec->opts));
 +      fflush(fplog);
 +      }
 +    }
 +
 +    /* Now if the new energy is smaller than the previous...
 +     * or if this is the first step!
 +     * or if we did random steps!
 +     */
 +
 +    if ( (count==0) || (s_try->epot < s_min->epot) ) {
 +      steps_accepted++;
 +
 +      /* Test whether the convergence criterion is met...  */
 +      bDone = (s_try->fmax < inputrec->em_tol);
 +
 +      /* Copy the arrays for force, positions and energy  */
 +      /* The 'Min' array always holds the coords and forces of the minimal
 +       sampled energy  */
 +      swap_em_state(s_min,s_try);
 +      if (count > 0)
 +      ustep *= 1.2;
 +
 +      /* Write to trn, if necessary */
 +      do_x = do_per_step(steps_accepted,inputrec->nstxout);
 +      do_f = do_per_step(steps_accepted,inputrec->nstfout);
 +      write_em_traj(fplog,cr,outf,do_x,do_f,NULL,
 +                    top_global,inputrec,count,
 +                    s_min,state_global,f_global);
 +    }
 +    else {
 +      /* If energy is not smaller make the step smaller...  */
 +      ustep *= 0.5;
 +
 +      if (DOMAINDECOMP(cr) && s_min->s.ddp_count != cr->dd->ddp_count) {
 +      /* Reload the old state */
 +      em_dd_partition_system(fplog,count,cr,top_global,inputrec,
 +                             s_min,top,mdatoms,fr,vsite,constr,
 +                             nrnb,wcycle);
 +      }
 +    }
 +
 +    /* Determine new step  */
 +    stepsize = ustep/s_min->fmax;
 +
 +    /* Check if stepsize is too small, with 1 nm as a characteristic length */
 +#ifdef GMX_DOUBLE
 +        if (count == nsteps || ustep < 1e-12)
 +#else
 +        if (count == nsteps || ustep < 1e-6)
 +#endif
 +        {
 +            if (MASTER(cr))
 +            {
 +                warn_step(stderr,inputrec->em_tol,count==nsteps,constr!=NULL);
 +                warn_step(fplog ,inputrec->em_tol,count==nsteps,constr!=NULL);
 +            }
 +            bAbort=TRUE;
 +        }
 +
 +    count++;
 +  } /* End of the loop  */
 +
 +    /* Print some shit...  */
 +  if (MASTER(cr))
 +    fprintf(stderr,"\nwriting lowest energy coordinates.\n");
 +  write_em_traj(fplog,cr,outf,TRUE,inputrec->nstfout,ftp2fn(efSTO,nfile,fnm),
 +              top_global,inputrec,count,
 +              s_min,state_global,f_global);
 +
 +  fnormn = s_min->fnorm/sqrt(state_global->natoms);
 +
 +  if (MASTER(cr)) {
 +    print_converged(stderr,SD,inputrec->em_tol,count,bDone,nsteps,
 +                  s_min->epot,s_min->fmax,s_min->a_fmax,fnormn);
 +    print_converged(fplog,SD,inputrec->em_tol,count,bDone,nsteps,
 +                  s_min->epot,s_min->fmax,s_min->a_fmax,fnormn);
 +  }
 +
 +  finish_em(fplog,cr,outf,runtime,wcycle);
 +
 +  /* To print the actual number of steps we needed somewhere */
 +  inputrec->nsteps=count;
 +
 +  runtime->nsteps_done = count;
 +
 +  return 0;
 +} /* That's all folks */
 +
 +
 +double do_nm(FILE *fplog,t_commrec *cr,
 +             int nfile,const t_filenm fnm[],
 +             const output_env_t oenv, gmx_bool bVerbose,gmx_bool bCompact,
 +             int nstglobalcomm,
 +             gmx_vsite_t *vsite,gmx_constr_t constr,
 +             int stepout,
 +             t_inputrec *inputrec,
 +             gmx_mtop_t *top_global,t_fcdata *fcd,
 +             t_state *state_global,
 +             t_mdatoms *mdatoms,
 +             t_nrnb *nrnb,gmx_wallcycle_t wcycle,
 +             gmx_edsam_t ed,
 +             t_forcerec *fr,
 +             int repl_ex_nst, int repl_ex_nex, int repl_ex_seed,
 +             gmx_membed_t membed,
 +             real cpt_period,real max_hours,
 +             const char *deviceOptions,
 +             unsigned long Flags,
 +             gmx_runtime_t *runtime)
 +{
 +    const char *NM = "Normal Mode Analysis";
 +    gmx_mdoutf_t *outf;
 +    int        natoms,atom,d;
 +    int        nnodes,node;
 +    rvec       *f_global;
 +    gmx_localtop_t *top;
 +    gmx_enerdata_t *enerd;
 +    rvec       *f;
 +    gmx_global_stat_t gstat;
 +    t_graph    *graph;
 +    real       t,t0,lambda,lam0;
 +    gmx_bool       bNS;
 +    tensor     vir,pres;
 +    rvec       mu_tot;
 +    rvec       *fneg,*dfdx;
 +    gmx_bool       bSparse; /* use sparse matrix storage format */
 +    size_t     sz;
 +    gmx_sparsematrix_t * sparse_matrix = NULL;
 +    real *     full_matrix             = NULL;
 +    em_state_t *   state_work;
 +
 +    /* added with respect to mdrun */
 +    int        i,j,k,row,col;
 +    real       der_range=10.0*sqrt(GMX_REAL_EPS);
 +    real       x_min;
 +    real       fnorm,fmax;
 +
 +    if (constr != NULL)
 +    {
 +        gmx_fatal(FARGS,"Constraints present with Normal Mode Analysis, this combination is not supported");
 +    }
 +
 +    state_work = init_em_state();
 +
 +    /* Init em and store the local state in state_minimum */
 +    init_em(fplog,NM,cr,inputrec,
 +            state_global,top_global,state_work,&top,
 +            &f,&f_global,
 +            nrnb,mu_tot,fr,&enerd,&graph,mdatoms,&gstat,vsite,constr,
 +            nfile,fnm,&outf,NULL);
 +
 +    natoms = top_global->natoms;
 +    snew(fneg,natoms);
 +    snew(dfdx,natoms);
 +
 +#ifndef GMX_DOUBLE
 +    if (MASTER(cr))
 +    {
 +        fprintf(stderr,
 +                "NOTE: This version of Gromacs has been compiled in single precision,\n"
 +                "      which MIGHT not be accurate enough for normal mode analysis.\n"
 +                "      Gromacs now uses sparse matrix storage, so the memory requirements\n"
 +                "      are fairly modest even if you recompile in double precision.\n\n");
 +    }
 +#endif
 +
 +    /* Check if we can/should use sparse storage format.
 +     *
 +     * Sparse format is only useful when the Hessian itself is sparse, which it
 +      * will be when we use a cutoff.
 +      * For small systems (n<1000) it is easier to always use full matrix format, though.
 +      */
 +    if(EEL_FULL(fr->eeltype) || fr->rlist==0.0)
 +    {
 +        fprintf(stderr,"Non-cutoff electrostatics used, forcing full Hessian format.\n");
 +        bSparse = FALSE;
 +    }
 +    else if(top_global->natoms < 1000)
 +    {
 +        fprintf(stderr,"Small system size (N=%d), using full Hessian format.\n",top_global->natoms);
 +        bSparse = FALSE;
 +    }
 +    else
 +    {
 +        fprintf(stderr,"Using compressed symmetric sparse Hessian format.\n");
 +        bSparse = TRUE;
 +    }
 +
 +    sz = DIM*top_global->natoms;
 +
 +    fprintf(stderr,"Allocating Hessian memory...\n\n");
 +
 +    if(bSparse)
 +    {
 +        sparse_matrix=gmx_sparsematrix_init(sz);
 +        sparse_matrix->compressed_symmetric = TRUE;
 +    }
 +    else
 +    {
 +        snew(full_matrix,sz*sz);
 +    }
 +
 +    /* Initial values */
 +    t0           = inputrec->init_t;
 +    lam0         = inputrec->fepvals->init_lambda;
 +    t            = t0;
 +    lambda       = lam0;
 +
 +    init_nrnb(nrnb);
 +
 +    where();
 +
 +    /* Write start time and temperature */
 +    print_em_start(fplog,cr,runtime,wcycle,NM);
 +
 +    /* fudge nr of steps to nr of atoms */
 +    inputrec->nsteps = natoms*2;
 +
 +    if (MASTER(cr))
 +    {
 +        fprintf(stderr,"starting normal mode calculation '%s'\n%d steps.\n\n",
 +                *(top_global->name),(int)inputrec->nsteps);
 +    }
 +
 +    nnodes = cr->nnodes;
 +
 +    /* Make evaluate_energy do a single node force calculation */
 +    cr->nnodes = 1;
 +    evaluate_energy(fplog,bVerbose,cr,
 +                    state_global,top_global,state_work,top,
 +                    inputrec,nrnb,wcycle,gstat,
 +                    vsite,constr,fcd,graph,mdatoms,fr,
 +                    mu_tot,enerd,vir,pres,-1,TRUE);
 +    cr->nnodes = nnodes;
 +
 +    /* if forces are not small, warn user */
 +    get_state_f_norm_max(cr,&(inputrec->opts),mdatoms,state_work);
 +
 +    if (MASTER(cr))
 +    {
 +        fprintf(stderr,"Maximum force:%12.5e\n",state_work->fmax);
 +        if (state_work->fmax > 1.0e-3)
 +        {
 +            fprintf(stderr,"Maximum force probably not small enough to");
 +            fprintf(stderr," ensure that you are in an \nenergy well. ");
 +            fprintf(stderr,"Be aware that negative eigenvalues may occur");
 +            fprintf(stderr," when the\nresulting matrix is diagonalized.\n");
 +        }
 +    }
 +
 +    /***********************************************************
 +     *
 +     *      Loop over all pairs in matrix
 +     *
 +     *      do_force called twice. Once with positive and
 +     *      once with negative displacement
 +     *
 +     ************************************************************/
 +
 +    /* Steps are divided one by one over the nodes */
 +    for(atom=cr->nodeid; atom<natoms; atom+=nnodes)
 +    {
 +
 +        for (d=0; d<DIM; d++)
 +        {
 +            x_min = state_work->s.x[atom][d];
 +
 +            state_work->s.x[atom][d] = x_min - der_range;
 +
 +            /* Make evaluate_energy do a single node force calculation */
 +            cr->nnodes = 1;
 +            evaluate_energy(fplog,bVerbose,cr,
 +                            state_global,top_global,state_work,top,
 +                            inputrec,nrnb,wcycle,gstat,
 +                            vsite,constr,fcd,graph,mdatoms,fr,
 +                            mu_tot,enerd,vir,pres,atom*2,FALSE);
 +
 +            for(i=0; i<natoms; i++)
 +            {
 +                copy_rvec(state_work->f[i], fneg[i]);
 +            }
 +
 +            state_work->s.x[atom][d] = x_min + der_range;
 +
 +            evaluate_energy(fplog,bVerbose,cr,
 +                            state_global,top_global,state_work,top,
 +                            inputrec,nrnb,wcycle,gstat,
 +                            vsite,constr,fcd,graph,mdatoms,fr,
 +                            mu_tot,enerd,vir,pres,atom*2+1,FALSE);
 +            cr->nnodes = nnodes;
 +
 +            /* x is restored to original */
 +            state_work->s.x[atom][d] = x_min;
 +
 +            for(j=0; j<natoms; j++)
 +            {
 +                for (k=0; (k<DIM); k++)
 +                {
 +                    dfdx[j][k] =
 +                        -(state_work->f[j][k] - fneg[j][k])/(2*der_range);
 +                }
 +            }
 +
 +            if (!MASTER(cr))
 +            {
 +#ifdef GMX_MPI
 +#ifdef GMX_DOUBLE
 +#define mpi_type MPI_DOUBLE
 +#else
 +#define mpi_type MPI_FLOAT
 +#endif
 +                MPI_Send(dfdx[0],natoms*DIM,mpi_type,MASTERNODE(cr),cr->nodeid,
 +                         cr->mpi_comm_mygroup);
 +#endif
 +            }
 +            else
 +            {
 +                for(node=0; (node<nnodes && atom+node<natoms); node++)
 +                {
 +                    if (node > 0)
 +                    {
 +#ifdef GMX_MPI
 +                        MPI_Status stat;
 +                        MPI_Recv(dfdx[0],natoms*DIM,mpi_type,node,node,
 +                                 cr->mpi_comm_mygroup,&stat);
 +#undef mpi_type
 +#endif
 +                    }
 +
 +                    row = (atom + node)*DIM + d;
 +
 +                    for(j=0; j<natoms; j++)
 +                    {
 +                        for(k=0; k<DIM; k++)
 +                        {
 +                            col = j*DIM + k;
 +
 +                            if (bSparse)
 +                            {
 +                                if (col >= row && dfdx[j][k] != 0.0)
 +                                {
 +                                    gmx_sparsematrix_increment_value(sparse_matrix,
 +                                                                     row,col,dfdx[j][k]);
 +                                }
 +                            }
 +                            else
 +                            {
 +                                full_matrix[row*sz+col] = dfdx[j][k];
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +
 +            if (bVerbose && fplog)
 +            {
 +                fflush(fplog);
 +            }
 +        }
 +        /* write progress */
 +        if (MASTER(cr) && bVerbose)
 +        {
 +            fprintf(stderr,"\rFinished step %d out of %d",
 +                    min(atom+nnodes,natoms),natoms);
 +            fflush(stderr);
 +        }
 +    }
 +
 +    if (MASTER(cr))
 +    {
 +        fprintf(stderr,"\n\nWriting Hessian...\n");
 +        gmx_mtxio_write(ftp2fn(efMTX,nfile,fnm),sz,sz,full_matrix,sparse_matrix);
 +    }
 +
 +    finish_em(fplog,cr,outf,runtime,wcycle);
 +
 +    runtime->nsteps_done = natoms*2;
 +
 +    return 0;
 +}
 +
index 13c236fc14aca61faa35a6743876208e06d1fe91,0000000000000000000000000000000000000000..5a21f1dc9ed7fe1b036fe21275c46c66c0112859
mode 100644,000000..100644
--- /dev/null
@@@ -1,3859 -1,0 +1,3876 @@@
-         rvec *x,gmx_mtop_t *mtop,gmx_bool bVerbose,FILE *out_slabs, gmx_bool bOutputCenters)
 +/*
 + * 
 + *                This source code is part of
 + * 
 + *                 G   R   O   M   A   C   S
 + * 
 + *          GROningen MAchine for Chemical Simulations
 + * 
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2008, 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:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +#include "domdec.h"
 +#include "gmx_wallcycle.h"
 +#include "gmx_cyclecounter.h"
 +#include "trnio.h"
 +#include "smalloc.h"
 +#include "network.h"
 +#include "pbc.h"
 +#include "futil.h"
 +#include "mdrun.h"
 +#include "txtdump.h"
 +#include "names.h"
 +#include "mtop_util.h"
 +#include "names.h"
 +#include "nrjac.h"
 +#include "vec.h"
 +#include "gmx_ga2la.h"
 +#include "xvgr.h"
 +#include "gmxfio.h"
 +#include "groupcoord.h"
 +#include "pull_rotation.h"
 +#include "gmx_sort.h"
 +#include "copyrite.h"
 +#include "macros.h"
 +
 +
 +static char *RotStr = {"Enforced rotation:"};
 +
 +
 +/* Set the minimum weight for the determination of the slab centers */
 +#define WEIGHT_MIN (10*GMX_FLOAT_MIN)
 +
 +/* Helper structure for sorting positions along rotation vector             */
 +typedef struct {
 +    real xcproj;            /* Projection of xc on the rotation vector        */
 +    int ind;                /* Index of xc                                    */
 +    real m;                 /* Mass                                           */
 +    rvec x;                 /* Position                                       */
 +    rvec x_ref;             /* Reference position                             */
 +} sort_along_vec_t;
 +
 +
 +/* Enforced rotation / flexible: determine the angle of each slab             */
 +typedef struct gmx_slabdata
 +{
 +    int  nat;               /* Number of atoms belonging to this slab         */
 +    rvec *x;                /* The positions belonging to this slab. In 
 +                               general, this should be all positions of the 
 +                               whole rotation group, but we leave those away 
 +                               that have a small enough weight                */
 +    rvec *ref;              /* Same for reference                             */
 +    real *weight;           /* The weight for each atom                       */
 +} t_gmx_slabdata;
 +
 +
 +/* Helper structure for potential fitting */
 +typedef struct gmx_potfit
 +{
 +    real   *degangle;       /* Set of angles for which the potential is
 +                               calculated. The optimum fit is determined as
 +                               the angle for with the potential is minimal    */
 +    real   *V;              /* Potential for the different angles             */
 +    matrix *rotmat;         /* Rotation matrix corresponding to the angles    */
 +} t_gmx_potfit;
 +
 +
 +/* Enforced rotation data for all groups                                      */
 +typedef struct gmx_enfrot
 +{
 +    FILE  *out_rot;         /* Output file for rotation data                  */
 +    FILE  *out_torque;      /* Output file for torque data                    */
 +    FILE  *out_angles;      /* Output file for slab angles for flexible type  */
 +    FILE  *out_slabs;       /* Output file for slab centers                   */
 +    int   bufsize;          /* Allocation size of buf                         */
 +    rvec  *xbuf;            /* Coordinate buffer variable for sorting         */
 +    real  *mbuf;            /* Masses buffer variable for sorting             */
 +    sort_along_vec_t *data; /* Buffer variable needed for position sorting    */
 +    real  *mpi_inbuf;       /* MPI buffer                                     */
 +    real  *mpi_outbuf;      /* MPI buffer                                     */
 +    int   mpi_bufsize;      /* Allocation size of in & outbuf                 */
 +    unsigned long Flags;    /* mdrun flags                                    */
 +    gmx_bool bOut;          /* Used to skip first output when appending to 
 +                             * avoid duplicate entries in rotation outfiles   */
 +} t_gmx_enfrot;
 +
 +
 +/* Global enforced rotation data for a single rotation group                  */
 +typedef struct gmx_enfrotgrp
 +{
 +    real    degangle;       /* Rotation angle in degrees                      */
 +    matrix  rotmat;         /* Rotation matrix                                */
 +    atom_id *ind_loc;       /* Local rotation indices                         */
 +    int     nat_loc;        /* Number of local group atoms                    */
 +    int     nalloc_loc;     /* Allocation size for ind_loc and weight_loc     */
 +
 +    real  V;                /* Rotation potential for this rotation group     */
 +    rvec  *f_rot_loc;       /* Array to store the forces on the local atoms
 +                               resulting from enforced rotation potential     */
 +
 +    /* Collective coordinates for the whole rotation group */
 +    real  *xc_ref_length;   /* Length of each x_rotref vector after x_rotref 
 +                               has been put into origin                       */
 +    int   *xc_ref_ind;      /* Position of each local atom in the collective
 +                               array                                          */
 +    rvec  xc_center;        /* Center of the rotation group positions, may
 +                               be mass weighted                               */
 +    rvec  xc_ref_center;    /* dito, for the reference positions              */
 +    rvec  *xc;              /* Current (collective) positions                 */
 +    ivec  *xc_shifts;       /* Current (collective) shifts                    */
 +    ivec  *xc_eshifts;      /* Extra shifts since last DD step                */
 +    rvec  *xc_old;          /* Old (collective) positions                     */
 +    rvec  *xc_norm;         /* Normalized form of the current positions       */
 +    rvec  *xc_ref_sorted;   /* Reference positions (sorted in the same order 
 +                               as xc when sorted)                             */
 +    int   *xc_sortind;      /* Where is a position found after sorting?       */
 +    real  *mc;              /* Collective masses                              */
 +    real  *mc_sorted;
 +    real  invmass;          /* one over the total mass of the rotation group  */
 +
 +    real  torque_v;         /* Torque in the direction of rotation vector     */
 +    real  angle_v;          /* Actual angle of the whole rotation group       */
 +    /* Fixed rotation only */
 +    real  weight_v;         /* Weights for angle determination                */
 +    rvec  *xr_loc;          /* Local reference coords, correctly rotated      */
 +    rvec  *x_loc_pbc;       /* Local current coords, correct PBC image        */
 +    real  *m_loc;           /* Masses of the current local atoms              */
 +
 +    /* Flexible rotation only */
 +    int   nslabs_alloc;     /* For this many slabs memory is allocated        */
 +    int   slab_first;       /* Lowermost slab for that the calculation needs 
 +                               to be performed at a given time step           */
 +    int   slab_last;        /* Uppermost slab ...                             */
 +    int   slab_first_ref;   /* First slab for which ref. center is stored     */
 +    int   slab_last_ref;    /* Last ...                                       */
 +    int   slab_buffer;      /* Slab buffer region around reference slabs      */
 +    int   *firstatom;       /* First relevant atom for a slab                 */
 +    int   *lastatom;        /* Last relevant atom for a slab                  */
 +    rvec  *slab_center;     /* Gaussian-weighted slab center                  */
 +    rvec  *slab_center_ref; /* Gaussian-weighted slab center for the
 +                               reference positions                            */
 +    real  *slab_weights;    /* Sum of gaussian weights in a slab              */
 +    real  *slab_torque_v;   /* Torque T = r x f for each slab.                */
 +                            /* torque_v = m.v = angular momentum in the 
 +                               direction of v                                 */
 +    real  max_beta;         /* min_gaussian from inputrec->rotgrp is the
 +                               minimum value the gaussian must have so that 
 +                               the force is actually evaluated max_beta is 
 +                               just another way to put it                     */
 +    real  *gn_atom;         /* Precalculated gaussians for a single atom      */
 +    int   *gn_slabind;      /* Tells to which slab each precalculated gaussian 
 +                               belongs                                        */
 +    rvec  *slab_innersumvec;/* Inner sum of the flexible2 potential per slab;
 +                               this is precalculated for optimization reasons */
 +    t_gmx_slabdata *slab_data; /* Holds atom positions and gaussian weights 
 +                               of atoms belonging to a slab                   */
 +
 +    /* For potential fits with varying angle: */
 +    t_gmx_potfit *PotAngleFit;  /* Used for fit type 'potential'              */
 +} t_gmx_enfrotgrp;
 +
 +
 +/* Activate output of forces for correctness checks */
 +/* #define PRINT_FORCES */
 +#ifdef PRINT_FORCES
 +#define PRINT_FORCE_J  fprintf(stderr,"f%d = %15.8f %15.8f %15.8f\n",erg->xc_ref_ind[j],erg->f_rot_loc[j][XX], erg->f_rot_loc[j][YY], erg->f_rot_loc[j][ZZ]);
 +#define PRINT_POT_TAU  if (MASTER(cr)) { \
 +                           fprintf(stderr,"potential = %15.8f\n" "torque    = %15.8f\n", erg->V, erg->torque_v); \
 +                       }
 +#else
 +#define PRINT_FORCE_J
 +#define PRINT_POT_TAU
 +#endif
 +
 +/* Shortcuts for often used queries */
 +#define ISFLEX(rg) ( (rg->eType==erotgFLEX) || (rg->eType==erotgFLEXT) || (rg->eType==erotgFLEX2) || (rg->eType==erotgFLEX2T) )
 +#define ISCOLL(rg) ( (rg->eType==erotgFLEX) || (rg->eType==erotgFLEXT) || (rg->eType==erotgFLEX2) || (rg->eType==erotgFLEX2T) || (rg->eType==erotgRMPF) || (rg->eType==erotgRM2PF) )
 +
 +
 +/* Does any of the rotation groups use slab decomposition? */
 +static gmx_bool HaveFlexibleGroups(t_rot *rot)
 +{
 +    int g;
 +    t_rotgrp *rotg;
 +
 +
 +    for (g=0; g<rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        if (ISFLEX(rotg))
 +            return TRUE;
 +    }
 +
 +    return FALSE;
 +}
 +
 +
 +/* Is for any group the fit angle determined by finding the minimum of the
 + * rotation potential? */
 +static gmx_bool HavePotFitGroups(t_rot *rot)
 +{
 +    int g;
 +    t_rotgrp *rotg;
 +
 +
 +    for (g=0; g<rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        if (erotgFitPOT == rotg->eFittype)
 +            return TRUE;
 +    }
 +
 +    return FALSE;
 +}
 +
 +
 +static double** allocate_square_matrix(int dim)
 +{
 +    int i;
 +    double** mat = NULL; 
 +    
 +    
 +    snew(mat, dim);
 +    for(i=0; i<dim; i++)
 +        snew(mat[i], dim);
 +
 +    return mat;
 +}
 +
 +
 +static void free_square_matrix(double** mat, int dim)
 +{
 +    int i;
 +    
 +    
 +    for (i=0; i<dim; i++)
 +        sfree(mat[i]);
 +    sfree(mat);
 +}
 +
 +
 +/* Return the angle for which the potential is minimal */
 +static real get_fitangle(t_rotgrp *rotg, gmx_enfrotgrp_t erg)
 +{
 +    int i;
 +    real fitangle = -999.9;
 +    real pot_min = GMX_FLOAT_MAX;
 +    t_gmx_potfit *fit;
 +
 +
 +    fit = erg->PotAngleFit;
 +
 +    for (i = 0; i < rotg->PotAngle_nstep; i++)
 +    {
 +        if (fit->V[i] < pot_min)
 +        {
 +            pot_min = fit->V[i];
 +            fitangle = fit->degangle[i];
 +        }
 +    }
 +
 +    return fitangle;
 +}
 +
 +
 +/* Reduce potential angle fit data for this group at this time step? */
 +static gmx_inline gmx_bool bPotAngle(t_rot *rot, t_rotgrp *rotg, gmx_large_int_t step)
 +{
 +    return ( (erotgFitPOT==rotg->eFittype) && (do_per_step(step, rot->nstsout) || do_per_step(step, rot->nstrout)) );
 +}
 +
 +/* Reduce slab torqe data for this group at this time step? */
 +static gmx_inline gmx_bool bSlabTau(t_rot *rot, t_rotgrp *rotg, gmx_large_int_t step)
 +{
 +    return ( (ISFLEX(rotg)) && do_per_step(step, rot->nstsout) );
 +}
 +
 +/* Output rotation energy, torques, etc. for each rotation group */
 +static void reduce_output(t_commrec *cr, t_rot *rot, real t, gmx_large_int_t step)
 +{
 +    int      g,i,islab,nslabs=0;
 +    int      count;      /* MPI element counter                               */
 +    t_rotgrp *rotg;
 +    gmx_enfrot_t er;     /* Pointer to the enforced rotation buffer variables */
 +    gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data           */
 +    real     fitangle;
 +    gmx_bool bFlex;
 +
 +    
 +    er=rot->enfrot;
 +    
 +    /* Fill the MPI buffer with stuff to reduce. If items are added for reduction
 +     * here, the MPI buffer size has to be enlarged also in calc_mpi_bufsize() */
 +    if (PAR(cr))
 +    {
 +        count=0;
 +        for (g=0; g < rot->ngrp; g++)
 +        {
 +            rotg = &rot->grp[g];
 +            erg = rotg->enfrotgrp;
 +            nslabs = erg->slab_last - erg->slab_first + 1;
 +            er->mpi_inbuf[count++] = erg->V;
 +            er->mpi_inbuf[count++] = erg->torque_v;
 +            er->mpi_inbuf[count++] = erg->angle_v;
 +            er->mpi_inbuf[count++] = erg->weight_v; /* weights are not needed for flex types, but this is just a single value */
 +
 +            if (bPotAngle(rot, rotg, step))
 +            {
 +                for (i = 0; i < rotg->PotAngle_nstep; i++)
 +                    er->mpi_inbuf[count++] = erg->PotAngleFit->V[i];
 +            }
 +            if (bSlabTau(rot, rotg, step))
 +            {
 +                for (i=0; i<nslabs; i++)
 +                    er->mpi_inbuf[count++] = erg->slab_torque_v[i];
 +            }
 +        }
 +        if (count > er->mpi_bufsize)
 +            gmx_fatal(FARGS, "%s MPI buffer overflow, please report this error.", RotStr);
 +
 +#ifdef GMX_MPI
 +        MPI_Reduce(er->mpi_inbuf, er->mpi_outbuf, count, GMX_MPI_REAL, MPI_SUM, MASTERRANK(cr), cr->mpi_comm_mygroup);
 +#endif
 +
 +        /* Copy back the reduced data from the buffer on the master */
 +        if (MASTER(cr))
 +        {
 +            count=0;
 +            for (g=0; g < rot->ngrp; g++)
 +            {
 +                rotg = &rot->grp[g];
 +                erg = rotg->enfrotgrp;
 +                nslabs = erg->slab_last - erg->slab_first + 1;
 +                erg->V        = er->mpi_outbuf[count++];
 +                erg->torque_v = er->mpi_outbuf[count++];
 +                erg->angle_v  = er->mpi_outbuf[count++];
 +                erg->weight_v = er->mpi_outbuf[count++];
 +
 +                if (bPotAngle(rot, rotg, step))
 +                {
 +                    for (i = 0; i < rotg->PotAngle_nstep; i++)
 +                        erg->PotAngleFit->V[i] = er->mpi_outbuf[count++];
 +                }
 +                if (bSlabTau(rot, rotg, step))
 +                {
 +                    for (i=0; i<nslabs; i++)
 +                        erg->slab_torque_v[i] = er->mpi_outbuf[count++];
 +                }
 +            }
 +        }
 +    }
 +    
 +    /* Output */
 +    if (MASTER(cr))
 +    {
 +        /* Angle and torque for each rotation group */
 +        for (g=0; g < rot->ngrp; g++)
 +        {
 +            rotg=&rot->grp[g];
 +            bFlex = ISFLEX(rotg);
 +
 +            erg=rotg->enfrotgrp;
 +            
 +            /* Output to main rotation output file: */
 +            if ( do_per_step(step, rot->nstrout) )
 +            {
 +                if (erotgFitPOT == rotg->eFittype)
 +                {
 +                    fitangle = get_fitangle(rotg, erg);
 +                }
 +                else
 +                {
 +                    if (bFlex)
 +                        fitangle = erg->angle_v; /* RMSD fit angle */
 +                    else
 +                        fitangle = (erg->angle_v/erg->weight_v)*180.0*M_1_PI;
 +                }
 +                fprintf(er->out_rot, "%12.4f", fitangle);
 +                fprintf(er->out_rot, "%12.3e", erg->torque_v);
 +                fprintf(er->out_rot, "%12.3e", erg->V);
 +            }
 +
 +            if ( do_per_step(step, rot->nstsout) )
 +            {
 +                /* Output to torque log file: */
 +                if (bFlex)
 +                {
 +                    fprintf(er->out_torque, "%12.3e%6d", t, g);
 +                    for (i=erg->slab_first; i<=erg->slab_last; i++)
 +                    {
 +                        islab = i - erg->slab_first;  /* slab index */
 +                        /* Only output if enough weight is in slab */
 +                        if (erg->slab_weights[islab] > rotg->min_gaussian)
 +                            fprintf(er->out_torque, "%6d%12.3e", i, erg->slab_torque_v[islab]);
 +                    }
 +                    fprintf(er->out_torque , "\n");
 +                }
 +
 +                /* Output to angles log file: */
 +                if (erotgFitPOT == rotg->eFittype)
 +                {
 +                    fprintf(er->out_angles, "%12.3e%6d%12.4f", t, g, erg->degangle);
 +                    /* Output energies at a set of angles around the reference angle */
 +                    for (i = 0; i < rotg->PotAngle_nstep; i++)
 +                        fprintf(er->out_angles, "%12.3e", erg->PotAngleFit->V[i]);
 +                    fprintf(er->out_angles, "\n");
 +                }
 +            }
 +        }
 +        if ( do_per_step(step, rot->nstrout) )
 +            fprintf(er->out_rot, "\n");
 +    }
 +}
 +
 +
 +/* Add the forces from enforced rotation potential to the local forces.
 + * Should be called after the SR forces have been evaluated */
 +extern real add_rot_forces(t_rot *rot, rvec f[], t_commrec *cr, gmx_large_int_t step, real t)
 +{
 +    int g,l,ii;
 +    t_rotgrp *rotg;
 +    gmx_enfrot_t er;     /* Pointer to the enforced rotation buffer variables */
 +    gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data           */
 +    real Vrot = 0.0;     /* If more than one rotation group is present, Vrot
 +                            assembles the local parts from all groups         */
 +
 +    
 +    er=rot->enfrot;
 +    
 +    /* Loop over enforced rotation groups (usually 1, though)
 +     * Apply the forces from rotation potentials */
 +    for (g=0; g<rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        erg=rotg->enfrotgrp;
 +        Vrot += erg->V;  /* add the local parts from the nodes */
 +        for (l=0; l<erg->nat_loc; l++)
 +        {
 +            /* Get the right index of the local force */
 +            ii = erg->ind_loc[l];
 +            /* Add */
 +            rvec_inc(f[ii],erg->f_rot_loc[l]);
 +        }
 +    }
 +
 +    /* Reduce energy,torque, angles etc. to get the sum values (per rotation group)
 +     * on the master and output these values to file. */
 +    if ( (do_per_step(step, rot->nstrout) || do_per_step(step, rot->nstsout)) && er->bOut)
 +        reduce_output(cr, rot, t, step);
 +
 +    /* When appending, er->bOut is FALSE the first time to avoid duplicate entries */
 +    er->bOut = TRUE;
 +    
 +    PRINT_POT_TAU
 +
 +    return Vrot;
 +}
 +
 +
 +/* The Gaussian norm is chosen such that the sum of the gaussian functions
 + * over the slabs is approximately 1.0 everywhere */
 +#define GAUSS_NORM   0.569917543430618
 +
 +
 +/* Calculate the maximum beta that leads to a gaussian larger min_gaussian,
 + * also does some checks
 + */
 +static double calc_beta_max(real min_gaussian, real slab_dist)
 +{
 +    double sigma;
 +    double arg;
 +    
 +    
 +    /* Actually the next two checks are already made in grompp */
 +    if (slab_dist <= 0)
 +        gmx_fatal(FARGS, "Slab distance of flexible rotation groups must be >=0 !");
 +    if (min_gaussian <= 0)
 +        gmx_fatal(FARGS, "Cutoff value for Gaussian must be > 0. (You requested %f)");
 +
 +    /* Define the sigma value */
 +    sigma = 0.7*slab_dist;
 +
 +    /* Calculate the argument for the logarithm and check that the log() result is negative or 0 */
 +    arg = min_gaussian/GAUSS_NORM;
 +    if (arg > 1.0)
 +        gmx_fatal(FARGS, "min_gaussian of flexible rotation groups must be <%g", GAUSS_NORM);
 +    
 +    return sqrt(-2.0*sigma*sigma*log(min_gaussian/GAUSS_NORM));
 +}
 +
 +
 +static gmx_inline real calc_beta(rvec curr_x, t_rotgrp *rotg, int n)
 +{
 +    return iprod(curr_x, rotg->vec) - rotg->slab_dist * n;
 +}
 +
 +
 +static gmx_inline real gaussian_weight(rvec curr_x, t_rotgrp *rotg, int n)
 +{
 +    const real norm = GAUSS_NORM;
 +    real       sigma;
 +
 +    
 +    /* Define the sigma value */
 +    sigma = 0.7*rotg->slab_dist;
 +    /* Calculate the Gaussian value of slab n for position curr_x */
 +    return norm * exp( -0.5 * sqr( calc_beta(curr_x, rotg, n)/sigma ) );
 +}
 +
 +
 +/* Returns the weight in a single slab, also calculates the Gaussian- and mass-
 + * weighted sum of positions for that slab */
 +static real get_slab_weight(int j, t_rotgrp *rotg, rvec xc[], real mc[], rvec *x_weighted_sum)
 +{
 +    rvec curr_x;              /* The position of an atom                      */
 +    rvec curr_x_weighted;     /* The gaussian-weighted position               */
 +    real gaussian;            /* A single gaussian weight                     */
 +    real wgauss;              /* gaussian times current mass                  */
 +    real slabweight = 0.0;    /* The sum of weights in the slab               */
 +    int i,islab;
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data      */
 +
 +    
 +    erg=rotg->enfrotgrp;
 +    clear_rvec(*x_weighted_sum);
 +    
 +    /* Slab index */
 +    islab = j - erg->slab_first;
 +    
 +    /* Loop over all atoms in the rotation group */
 +     for (i=0; i<rotg->nat; i++)
 +     {
 +         copy_rvec(xc[i], curr_x);
 +         gaussian = gaussian_weight(curr_x, rotg, j);
 +         wgauss = gaussian * mc[i];
 +         svmul(wgauss, curr_x, curr_x_weighted);
 +         rvec_add(*x_weighted_sum, curr_x_weighted, *x_weighted_sum);
 +         slabweight += wgauss;
 +     } /* END of loop over rotation group atoms */
 +
 +     return slabweight;
 +}
 +
 +
 +static void get_slab_centers(
 +        t_rotgrp *rotg,       /* The rotation group information               */
 +        rvec      *xc,        /* The rotation group positions; will 
 +                                 typically be enfrotgrp->xc, but at first call 
 +                                 it is enfrotgrp->xc_ref                      */
 +        real      *mc,        /* The masses of the rotation group atoms       */
 +        int       g,          /* The number of the rotation group             */
 +        real      time,       /* Used for output only                         */
 +        FILE      *out_slabs, /* For outputting center per slab information   */
 +        gmx_bool  bOutStep,   /* Is this an output step?                      */
 +        gmx_bool  bReference) /* If this routine is called from
 +                                 init_rot_group we need to store
 +                                 the reference slab centers                   */
 +{
 +    int j,islab;
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
 +    
 +    
 +    erg=rotg->enfrotgrp;
 +
 +    /* Loop over slabs */
 +    for (j = erg->slab_first; j <= erg->slab_last; j++)
 +    {
 +        islab = j - erg->slab_first;
 +        erg->slab_weights[islab] = get_slab_weight(j, rotg, xc, mc, &erg->slab_center[islab]);
 +        
 +        /* We can do the calculations ONLY if there is weight in the slab! */
 +        if (erg->slab_weights[islab] > WEIGHT_MIN)
 +        {
 +            svmul(1.0/erg->slab_weights[islab], erg->slab_center[islab], erg->slab_center[islab]);
 +        }
 +        else
 +        {
 +            /* We need to check this here, since we divide through slab_weights
 +             * in the flexible low-level routines! */
 +            gmx_fatal(FARGS, "Not enough weight in slab %d. Slab center cannot be determined!", j);
 +        }
 +        
 +        /* At first time step: save the centers of the reference structure */
 +        if (bReference)
 +            copy_rvec(erg->slab_center[islab], erg->slab_center_ref[islab]);
 +    } /* END of loop over slabs */
 +    
 +    /* Output on the master */
 +    if ( (NULL != out_slabs) && bOutStep)
 +    {
 +        fprintf(out_slabs, "%12.3e%6d", time, g);
 +        for (j = erg->slab_first; j <= erg->slab_last; j++)
 +        {
 +            islab = j - erg->slab_first;
 +            fprintf(out_slabs, "%6d%12.3e%12.3e%12.3e",
 +                    j,erg->slab_center[islab][XX],erg->slab_center[islab][YY],erg->slab_center[islab][ZZ]);
 +        }
 +        fprintf(out_slabs, "\n");
 +    }
 +}
 +
 +
 +static void calc_rotmat(
 +        rvec vec,
 +        real degangle,  /* Angle alpha of rotation at time t in degrees       */
 +        matrix rotmat)  /* Rotation matrix                                    */
 +{
 +    real radangle;            /* Rotation angle in radians */
 +    real cosa;                /* cosine alpha              */
 +    real sina;                /* sine alpha                */
 +    real OMcosa;              /* 1 - cos(alpha)            */
 +    real dumxy, dumxz, dumyz; /* save computations         */
 +    rvec rot_vec;             /* Rotate around rot_vec ... */
 +
 +
 +    radangle = degangle * M_PI/180.0;
 +    copy_rvec(vec , rot_vec );
 +
 +    /* Precompute some variables: */
 +    cosa   = cos(radangle);
 +    sina   = sin(radangle);
 +    OMcosa = 1.0 - cosa;
 +    dumxy  = rot_vec[XX]*rot_vec[YY]*OMcosa;
 +    dumxz  = rot_vec[XX]*rot_vec[ZZ]*OMcosa;
 +    dumyz  = rot_vec[YY]*rot_vec[ZZ]*OMcosa;
 +
 +    /* Construct the rotation matrix for this rotation group: */
 +    /* 1st column: */
 +    rotmat[XX][XX] = cosa  + rot_vec[XX]*rot_vec[XX]*OMcosa;
 +    rotmat[YY][XX] = dumxy + rot_vec[ZZ]*sina;
 +    rotmat[ZZ][XX] = dumxz - rot_vec[YY]*sina;
 +    /* 2nd column: */
 +    rotmat[XX][YY] = dumxy - rot_vec[ZZ]*sina;
 +    rotmat[YY][YY] = cosa  + rot_vec[YY]*rot_vec[YY]*OMcosa;
 +    rotmat[ZZ][YY] = dumyz + rot_vec[XX]*sina;
 +    /* 3rd column: */
 +    rotmat[XX][ZZ] = dumxz + rot_vec[YY]*sina;
 +    rotmat[YY][ZZ] = dumyz - rot_vec[XX]*sina;
 +    rotmat[ZZ][ZZ] = cosa  + rot_vec[ZZ]*rot_vec[ZZ]*OMcosa;
 +
 +#ifdef PRINTMATRIX
 +    int iii,jjj;
 +
 +    for (iii=0; iii<3; iii++) {
 +        for (jjj=0; jjj<3; jjj++)
 +            fprintf(stderr, " %10.8f ",  rotmat[iii][jjj]);
 +        fprintf(stderr, "\n");
 +    }
 +#endif
 +}
 +
 +
 +/* Calculates torque on the rotation axis tau = position x force */
 +static gmx_inline real torque(
 +        rvec rotvec,  /* rotation vector; MUST be normalized!                 */
 +        rvec force,   /* force                                                */
 +        rvec x,       /* position of atom on which the force acts             */
 +        rvec pivot)   /* pivot point of rotation axis                         */
 +{
 +    rvec vectmp, tau;
 +
 +    
 +    /* Subtract offset */
 +    rvec_sub(x,pivot,vectmp);
 +    
 +    /* position x force */
 +    cprod(vectmp, force, tau);
 +    
 +    /* Return the part of the torque which is parallel to the rotation vector */
 +    return iprod(tau, rotvec);
 +}
 +
 +
 +/* Right-aligned output of value with standard width */
 +static void print_aligned(FILE *fp, char *str)
 +{
 +    fprintf(fp, "%12s", str);
 +}
 +
 +
 +/* Right-aligned output of value with standard short width */
 +static void print_aligned_short(FILE *fp, char *str)
 +{
 +    fprintf(fp, "%6s", str);
 +}
 +
 +
 +static FILE *open_output_file(const char *fn, int steps, const char what[])
 +{
 +    FILE *fp;
 +    
 +    
 +    fp = ffopen(fn, "w");
 +
 +    fprintf(fp, "# Output of %s is written in intervals of %d time step%s.\n#\n",
 +            what,steps, steps>1 ? "s":"");
 +    
 +    return fp;
 +}
 +
 +
 +/* Open output file for slab center data. Call on master only */
 +static FILE *open_slab_out(const char *fn, t_rot *rot, const output_env_t oenv)
 +{
 +    FILE      *fp;
 +    int       g,i;
 +    t_rotgrp  *rotg;
 +
 +
 +    if (rot->enfrot->Flags & MD_APPENDFILES)
 +    {
 +        fp = gmx_fio_fopen(fn,"a");
 +    }
 +    else
 +    {
 +        fp = open_output_file(fn, rot->nstsout, "gaussian weighted slab centers");
 +
 +        for (g=0; g<rot->ngrp; g++)
 +        {
 +            rotg = &rot->grp[g];
 +            if (ISFLEX(rotg))
 +            {
 +                fprintf(fp, "# Rotation group %d (%s), slab distance %f nm, %s.\n",
 +                        g, erotg_names[rotg->eType], rotg->slab_dist,
 +                        rotg->bMassW? "centers of mass":"geometrical centers");
 +            }
 +        }
 +
 +        fprintf(fp, "# Reference centers are listed first (t=-1).\n");
 +        fprintf(fp, "# The following columns have the syntax:\n");
 +        fprintf(fp, "#     ");
 +        print_aligned_short(fp, "t");
 +        print_aligned_short(fp, "grp");
 +        /* Print legend for the first two entries only ... */
 +        for (i=0; i<2; i++)
 +        {
 +            print_aligned_short(fp, "slab");
 +            print_aligned(fp, "X center");
 +            print_aligned(fp, "Y center");
 +            print_aligned(fp, "Z center");
 +        }
 +        fprintf(fp, " ...\n");
 +        fflush(fp);
 +    }
 +
 +    return fp;
 +}
 +
 +
 +/* Adds 'buf' to 'str' */
 +static void add_to_string(char **str, char *buf)
 +{
 +    int len;
 +
 +
 +    len = strlen(*str) + strlen(buf) + 1;
 +    srenew(*str, len);
 +    strcat(*str, buf);
 +}
 +
 +
 +static void add_to_string_aligned(char **str, char *buf)
 +{
 +    char buf_aligned[STRLEN];
 +
 +    sprintf(buf_aligned, "%12s", buf);
 +    add_to_string(str, buf_aligned);
 +}
 +
 +
 +/* Open output file and print some general information about the rotation groups.
 + * Call on master only */
 +static FILE *open_rot_out(const char *fn, t_rot *rot, const output_env_t oenv)
 +{
 +    FILE       *fp;
 +    int        g,nsets;
 +    t_rotgrp   *rotg;
 +    const char **setname;
 +    char       buf[50], buf2[75];
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +    gmx_bool   bFlex;
 +    char       *LegendStr=NULL;
 +
 +
 +    if (rot->enfrot->Flags & MD_APPENDFILES)
 +    {
 +        fp = gmx_fio_fopen(fn,"a");
 +    }
 +    else
 +    {
 +        fp = xvgropen(fn, "Rotation angles and energy", "Time (ps)", "angles (degrees) and energies (kJ/mol)", oenv);
 +        fprintf(fp, "# Output of enforced rotation data is written in intervals of %d time step%s.\n#\n", rot->nstrout, rot->nstrout > 1 ? "s":"");
 +        fprintf(fp, "# The scalar tau is the torque (kJ/mol) in the direction of the rotation vector v.\n");
 +        fprintf(fp, "# To obtain the vectorial torque, multiply tau with the group's rot_vec.\n");
 +        fprintf(fp, "# For flexible groups, tau(t,n) from all slabs n have been summed in a single value tau(t) here.\n");
 +        fprintf(fp, "# The torques tau(t,n) are found in the rottorque.log (-rt) output file\n");
 +        
 +        for (g=0; g<rot->ngrp; g++)
 +        {
 +            rotg = &rot->grp[g];
 +            erg=rotg->enfrotgrp;
 +            bFlex = ISFLEX(rotg);
 +
 +            fprintf(fp, "#\n");
 +            fprintf(fp, "# ROTATION GROUP %d, potential type '%s':\n"      , g, erotg_names[rotg->eType]);
 +            fprintf(fp, "# rot_massw%d          %s\n"                      , g, yesno_names[rotg->bMassW]);
 +            fprintf(fp, "# rot_vec%d            %12.5e %12.5e %12.5e\n"    , g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
 +            fprintf(fp, "# rot_rate%d           %12.5e degrees/ps\n"       , g, rotg->rate);
 +            fprintf(fp, "# rot_k%d              %12.5e kJ/(mol*nm^2)\n"    , g, rotg->k);
 +            if ( rotg->eType==erotgISO || rotg->eType==erotgPM || rotg->eType==erotgRM || rotg->eType==erotgRM2)
 +                fprintf(fp, "# rot_pivot%d          %12.5e %12.5e %12.5e  nm\n", g, rotg->pivot[XX], rotg->pivot[YY], rotg->pivot[ZZ]);
 +
 +            if (bFlex)
 +            {
 +                fprintf(fp, "# rot_slab_distance%d   %f nm\n", g, rotg->slab_dist);
 +                fprintf(fp, "# rot_min_gaussian%d   %12.5e\n", g, rotg->min_gaussian);
 +            }
 +
 +            /* Output the centers of the rotation groups for the pivot-free potentials */
 +            if ((rotg->eType==erotgISOPF) || (rotg->eType==erotgPMPF) || (rotg->eType==erotgRMPF) || (rotg->eType==erotgRM2PF
 +                || (rotg->eType==erotgFLEXT) || (rotg->eType==erotgFLEX2T)) )
 +            {
 +                fprintf(fp, "# ref. grp. %d center  %12.5e %12.5e %12.5e\n", g,
 +                            erg->xc_ref_center[XX], erg->xc_ref_center[YY], erg->xc_ref_center[ZZ]);
 +
 +                fprintf(fp, "# grp. %d init.center  %12.5e %12.5e %12.5e\n", g,
 +                            erg->xc_center[XX], erg->xc_center[YY], erg->xc_center[ZZ]);
 +            }
 +
 +            if ( (rotg->eType == erotgRM2) || (rotg->eType==erotgFLEX2) || (rotg->eType==erotgFLEX2T) )
 +            {
 +                fprintf(fp, "# rot_eps%d            %12.5e nm^2\n", g, rotg->eps);
 +            }
 +            if (erotgFitPOT == rotg->eFittype)
 +            {
 +                fprintf(fp, "#\n");
 +                fprintf(fp, "# theta_fit%d is determined by first evaluating the potential for %d angles around theta_ref%d.\n",
 +                            g, rotg->PotAngle_nstep, g);
 +                fprintf(fp, "# The fit angle is the one with the smallest potential. It is given as the deviation\n");
 +                fprintf(fp, "# from the reference angle, i.e. if theta_ref=X and theta_fit=Y, then the angle with\n");
 +                fprintf(fp, "# minimal value of the potential is X+Y. Angular resolution is %g degrees.\n", rotg->PotAngle_step);
 +            }
 +        }
 +        
 +        /* Print a nice legend */
 +        snew(LegendStr, 1);
 +        LegendStr[0] = '\0';
 +        sprintf(buf, "#     %6s", "time");
 +        add_to_string_aligned(&LegendStr, buf);
 +
 +        nsets = 0;
 +        snew(setname, 4*rot->ngrp);
 +        
 +        for (g=0; g<rot->ngrp; g++)
 +        {
 +            rotg = &rot->grp[g];
 +            sprintf(buf, "theta_ref%d", g);
 +            add_to_string_aligned(&LegendStr, buf);
 +
 +            sprintf(buf2, "%s (degrees)", buf);
 +            setname[nsets] = strdup(buf2);
 +            nsets++;
 +        }
 +        for (g=0; g<rot->ngrp; g++)
 +        {
 +            rotg = &rot->grp[g];
 +            bFlex = ISFLEX(rotg);
 +
 +            /* For flexible axis rotation we use RMSD fitting to determine the
 +             * actual angle of the rotation group */
 +            if (bFlex || erotgFitPOT == rotg->eFittype)
 +                sprintf(buf, "theta_fit%d", g);
 +            else
 +                sprintf(buf, "theta_av%d", g);
 +            add_to_string_aligned(&LegendStr, buf);
 +            sprintf(buf2, "%s (degrees)", buf);
 +            setname[nsets] = strdup(buf2);
 +            nsets++;
 +
 +            sprintf(buf, "tau%d", g);
 +            add_to_string_aligned(&LegendStr, buf);
 +            sprintf(buf2, "%s (kJ/mol)", buf);
 +            setname[nsets] = strdup(buf2);
 +            nsets++;
 +
 +            sprintf(buf, "energy%d", g);
 +            add_to_string_aligned(&LegendStr, buf);
 +            sprintf(buf2, "%s (kJ/mol)", buf);
 +            setname[nsets] = strdup(buf2);
 +            nsets++;
 +        }
 +        fprintf(fp, "#\n");
 +        
 +        if (nsets > 1)
 +            xvgr_legend(fp, nsets, setname, oenv);
 +        sfree(setname);
 +
 +        fprintf(fp, "#\n# Legend for the following data columns:\n");
 +        fprintf(fp, "%s\n", LegendStr);
 +        sfree(LegendStr);
 +        
 +        fflush(fp);
 +    }
 +    
 +    return fp;
 +}
 +
 +
 +/* Call on master only */
 +static FILE *open_angles_out(const char *fn, t_rot *rot, const output_env_t oenv)
 +{
 +    int      g,i;
 +    FILE     *fp;
 +    t_rotgrp *rotg;
 +    gmx_enfrotgrp_t erg;        /* Pointer to enforced rotation group data */
 +    char     buf[100];
 +
 +
 +    if (rot->enfrot->Flags & MD_APPENDFILES)
 +    {
 +        fp = gmx_fio_fopen(fn,"a");
 +    }
 +    else
 +    {
 +        /* Open output file and write some information about it's structure: */
 +        fp = open_output_file(fn, rot->nstsout, "rotation group angles");
 +        fprintf(fp, "# All angles given in degrees, time in ps.\n");
 +        for (g=0; g<rot->ngrp; g++)
 +        {
 +            rotg = &rot->grp[g];
 +            erg=rotg->enfrotgrp;
 +
 +            /* Output for this group happens only if potential type is flexible or
 +             * if fit type is potential! */
 +            if ( ISFLEX(rotg) || (erotgFitPOT == rotg->eFittype) )
 +            {
 +                if (ISFLEX(rotg))
 +                    sprintf(buf, " slab distance %f nm, ", rotg->slab_dist);
 +                else
 +                    buf[0] = '\0';
 +
 +                fprintf(fp, "#\n# ROTATION GROUP %d '%s',%s fit type '%s'.\n",
 +                        g, erotg_names[rotg->eType], buf, erotg_fitnames[rotg->eFittype]);
 +
 +                /* Special type of fitting using the potential minimum. This is
 +                 * done for the whole group only, not for the individual slabs. */
 +                if (erotgFitPOT == rotg->eFittype)
 +                {
 +                    fprintf(fp, "#    To obtain theta_fit%d, the potential is evaluated for %d angles around theta_ref%d\n", g, rotg->PotAngle_nstep, g);
 +                    fprintf(fp, "#    The fit angle in the rotation standard outfile is the one with minimal energy E(theta_fit) [kJ/mol].\n");
 +                    fprintf(fp, "#\n");
 +                }
 +
 +                fprintf(fp, "# Legend for the group %d data columns:\n", g);
 +                fprintf(fp, "#     ");
 +                print_aligned_short(fp, "time");
 +                print_aligned_short(fp, "grp");
 +                print_aligned(fp, "theta_ref");
 +
 +                if (erotgFitPOT == rotg->eFittype)
 +                {
 +                    /* Output the set of angles around the reference angle */
 +                    for (i = 0; i < rotg->PotAngle_nstep; i++)
 +                    {
 +                        sprintf(buf, "E(%g)", erg->PotAngleFit->degangle[i]);
 +                        print_aligned(fp, buf);
 +                    }
 +                }
 +                else
 +                {
 +                    /* Output fit angle for each slab */
 +                    print_aligned_short(fp, "slab");
 +                    print_aligned_short(fp, "atoms");
 +                    print_aligned(fp, "theta_fit");
 +                    print_aligned_short(fp, "slab");
 +                    print_aligned_short(fp, "atoms");
 +                    print_aligned(fp, "theta_fit");
 +                    fprintf(fp, " ...");
 +                }
 +                fprintf(fp, "\n");
 +            }
 +        }
 +        fflush(fp);
 +    }
 +
 +    return fp;
 +}
 +
 +
 +/* Open torque output file and write some information about it's structure.
 + * Call on master only */
 +static FILE *open_torque_out(const char *fn, t_rot *rot, const output_env_t oenv)
 +{
 +    FILE      *fp;
 +    int       g;
 +    t_rotgrp  *rotg;
 +
 +
 +    if (rot->enfrot->Flags & MD_APPENDFILES)
 +    {
 +        fp = gmx_fio_fopen(fn,"a");
 +    }
 +    else
 +    {
 +        fp = open_output_file(fn, rot->nstsout,"torques");
 +
 +        for (g=0; g<rot->ngrp; g++)
 +        {
 +            rotg = &rot->grp[g];
 +            if (ISFLEX(rotg))
 +            {
 +                fprintf(fp, "# Rotation group %d (%s), slab distance %f nm.\n", g, erotg_names[rotg->eType], rotg->slab_dist);
 +                fprintf(fp, "# The scalar tau is the torque (kJ/mol) in the direction of the rotation vector.\n");
 +                fprintf(fp, "# To obtain the vectorial torque, multiply tau with\n");
 +                fprintf(fp, "# rot_vec%d            %10.3e %10.3e %10.3e\n", g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
 +                fprintf(fp, "#\n");
 +            }
 +        }
 +        fprintf(fp, "# Legend for the following data columns: (tau=torque for that slab):\n");
 +        fprintf(fp, "#     ");
 +        print_aligned_short(fp, "t");
 +        print_aligned_short(fp, "grp");
 +        print_aligned_short(fp, "slab");
 +        print_aligned(fp, "tau");
 +        print_aligned_short(fp, "slab");
 +        print_aligned(fp, "tau");
 +        fprintf(fp, " ...\n");
 +        fflush(fp);
 +    }
 +
 +    return fp;
 +}
 +
 +
 +static void swap_val(double* vec, int i, int j)
 +{
 +    double tmp = vec[j];
 +    
 +    
 +    vec[j]=vec[i];
 +    vec[i]=tmp;
 +}
 +
 +
 +static void swap_col(double **mat, int i, int j)
 +{
 +    double tmp[3] = {mat[0][j], mat[1][j], mat[2][j]};
 +    
 +    
 +    mat[0][j]=mat[0][i];
 +    mat[1][j]=mat[1][i];
 +    mat[2][j]=mat[2][i];
 +    
 +    mat[0][i]=tmp[0];
 +    mat[1][i]=tmp[1];
 +    mat[2][i]=tmp[2];
 +} 
 +
 +
 +/* Eigenvectors are stored in columns of eigen_vec */
 +static void diagonalize_symmetric(
 +        double **matrix,
 +        double **eigen_vec,
 +        double eigenval[3])
 +{
 +    int n_rot;
 +    
 +    
 +    jacobi(matrix,3,eigenval,eigen_vec,&n_rot);
 +    
 +    /* sort in ascending order */
 +    if (eigenval[0] > eigenval[1])
 +    {
 +        swap_val(eigenval, 0, 1);
 +        swap_col(eigen_vec, 0, 1);
 +    } 
 +    if (eigenval[1] > eigenval[2])
 +    {
 +        swap_val(eigenval, 1, 2);
 +        swap_col(eigen_vec, 1, 2);
 +    }
 +    if (eigenval[0] > eigenval[1])
 +    {
 +        swap_val(eigenval, 0, 1);
 +        swap_col(eigen_vec, 0, 1);
 +    }
 +}
 +
 +
 +static void align_with_z(
 +        rvec* s,           /* Structure to align */
 +        int natoms,
 +        rvec axis)
 +{
 +    int    i, j, k;
 +    rvec   zet = {0.0, 0.0, 1.0};
 +    rvec   rot_axis={0.0, 0.0, 0.0};
 +    rvec   *rotated_str=NULL;
 +    real   ooanorm;
 +    real   angle;
 +    matrix rotmat;
 +    
 +    
 +    snew(rotated_str, natoms);
 +
 +    /* Normalize the axis */
 +    ooanorm = 1.0/norm(axis);
 +    svmul(ooanorm, axis, axis);
 +    
 +    /* Calculate the angle for the fitting procedure */
 +    cprod(axis, zet, rot_axis);
 +    angle = acos(axis[2]);
 +    if (angle < 0.0)
 +        angle += M_PI;
 +    
 +    /* Calculate the rotation matrix */
 +    calc_rotmat(rot_axis, angle*180.0/M_PI, rotmat);
 +    
 +    /* Apply the rotation matrix to s */
 +    for (i=0; i<natoms; i++)
 +    {    
 +        for(j=0; j<3; j++)
 +        {
 +            for(k=0; k<3; k++)
 +            {
 +                rotated_str[i][j] += rotmat[j][k]*s[i][k];
 +            }
 +        }
 +    }
 +    
 +    /* Rewrite the rotated structure to s */
 +    for(i=0; i<natoms; i++)
 +    {
 +        for(j=0; j<3; j++)
 +        {
 +            s[i][j]=rotated_str[i][j];
 +        }
 +    }
 +    
 +    sfree(rotated_str);
 +} 
 +
 +
 +static void calc_correl_matrix(rvec* Xstr, rvec* Ystr, double** Rmat, int natoms)
 +{    
 +    int i, j, k;
 + 
 +    
 +    for (i=0; i<3; i++)
 +        for (j=0; j<3; j++)
 +            Rmat[i][j] = 0.0;
 +    
 +    for (i=0; i<3; i++) 
 +        for (j=0; j<3; j++) 
 +            for (k=0; k<natoms; k++) 
 +                Rmat[i][j] += Ystr[k][i] * Xstr[k][j];
 +}
 +
 +
 +static void weigh_coords(rvec* str, real* weight, int natoms)
 +{
 +    int i, j;
 +    
 +    
 +    for(i=0; i<natoms; i++)
 +    {
 +        for(j=0; j<3; j++)
 +            str[i][j] *= sqrt(weight[i]);
 +    }  
 +}
 +
 +
 +static real opt_angle_analytic(
 +        rvec* ref_s,
 +        rvec* act_s,
 +        real* weight, 
 +        int natoms,
 +        rvec ref_com,
 +        rvec act_com,
 +        rvec axis)
 +{    
 +    int    i, j, k;
 +    rvec   *ref_s_1=NULL;
 +    rvec   *act_s_1=NULL;
 +    rvec   shift;
 +    double **Rmat, **RtR, **eigvec;
 +    double eigval[3];
 +    double V[3][3], WS[3][3];
 +    double rot_matrix[3][3];
 +    double opt_angle;
 +    
 +    
 +    /* Do not change the original coordinates */ 
 +    snew(ref_s_1, natoms);
 +    snew(act_s_1, natoms);
 +    for(i=0; i<natoms; i++)
 +    {
 +        copy_rvec(ref_s[i], ref_s_1[i]);
 +        copy_rvec(act_s[i], act_s_1[i]);
 +    }
 +    
 +    /* Translate the structures to the origin */
 +    shift[XX] = -ref_com[XX];
 +    shift[YY] = -ref_com[YY];
 +    shift[ZZ] = -ref_com[ZZ];
 +    translate_x(ref_s_1, natoms, shift);
 +    
 +    shift[XX] = -act_com[XX];
 +    shift[YY] = -act_com[YY];
 +    shift[ZZ] = -act_com[ZZ];
 +    translate_x(act_s_1, natoms, shift);
 +    
 +    /* Align rotation axis with z */
 +    align_with_z(ref_s_1, natoms, axis);
 +    align_with_z(act_s_1, natoms, axis);
 +    
 +    /* Correlation matrix */
 +    Rmat = allocate_square_matrix(3);
 +    
 +    for (i=0; i<natoms; i++)
 +    {
 +        ref_s_1[i][2]=0.0;
 +        act_s_1[i][2]=0.0;
 +    }
 +    
 +    /* Weight positions with sqrt(weight) */
 +    if (NULL != weight)
 +    {
 +        weigh_coords(ref_s_1, weight, natoms);
 +        weigh_coords(act_s_1, weight, natoms);
 +    }
 +    
 +    /* Calculate correlation matrices R=YXt (X=ref_s; Y=act_s) */
 +    calc_correl_matrix(ref_s_1, act_s_1, Rmat, natoms);
 +    
 +    /* Calculate RtR */
 +    RtR = allocate_square_matrix(3);
 +    for (i=0; i<3; i++)
 +    {
 +        for (j=0; j<3; j++)
 +        {
 +            for (k=0; k<3; k++)
 +            {
 +                RtR[i][j] += Rmat[k][i] * Rmat[k][j];
 +            }
 +        }
 +    }
 +    /* Diagonalize RtR */
 +    snew(eigvec,3);
 +    for (i=0; i<3; i++)
 +        snew(eigvec[i],3);
 +    
 +    diagonalize_symmetric(RtR, eigvec, eigval);
 +    swap_col(eigvec,0,1);
 +    swap_col(eigvec,1,2);
 +    swap_val(eigval,0,1);
 +    swap_val(eigval,1,2);
 +    
 +    /* Calculate V */
 +    for(i=0; i<3; i++)
 +    {
 +        for(j=0; j<3; j++)
 +        {
 +            V[i][j]  = 0.0;
 +            WS[i][j] = 0.0;
 +        }
 +    }
 +    
 +    for (i=0; i<2; i++)
 +        for (j=0; j<2; j++)
 +            WS[i][j] = eigvec[i][j] / sqrt(eigval[j]);
 +    
 +    for (i=0; i<3; i++)
 +    {
 +        for (j=0; j<3; j++)
 +        {
 +            for (k=0; k<3; k++)
 +            {
 +                V[i][j] += Rmat[i][k]*WS[k][j];
 +            }
 +        }
 +    }
 +    free_square_matrix(Rmat, 3);
 +    
 +    /* Calculate optimal rotation matrix */
 +    for (i=0; i<3; i++)
 +        for (j=0; j<3; j++)
 +            rot_matrix[i][j] = 0.0;
 +    
 +    for (i=0; i<3; i++)
 +    {
 +        for(j=0; j<3; j++)
 +        {
 +            for(k=0; k<3; k++){
 +                rot_matrix[i][j] += eigvec[i][k]*V[j][k];
 +            }
 +        }
 +    }
 +    rot_matrix[2][2] = 1.0;
 +        
 +    /* In some cases abs(rot_matrix[0][0]) can be slighly larger
 +     * than unity due to numerical inacurracies. To be able to calculate
 +     * the acos function, we put these values back in range. */
 +    if (rot_matrix[0][0] > 1.0)
 +    {
 +        rot_matrix[0][0] = 1.0;
 +    }
 +    else if (rot_matrix[0][0] < -1.0)
 +    {
 +        rot_matrix[0][0] = -1.0;
 +    }
 +
 +    /* Determine the optimal rotation angle: */
 +    opt_angle = (-1.0)*acos(rot_matrix[0][0])*180.0/M_PI;
 +    if (rot_matrix[0][1] < 0.0)
 +        opt_angle = (-1.0)*opt_angle;
 +        
 +    /* Give back some memory */
 +    free_square_matrix(RtR, 3);
 +    sfree(ref_s_1);
 +    sfree(act_s_1);
 +    for (i=0; i<3; i++)
 +        sfree(eigvec[i]);
 +    sfree(eigvec);
 +    
 +    return (real) opt_angle;
 +}
 +
 +
 +/* Determine angle of the group by RMSD fit to the reference */
 +/* Not parallelized, call this routine only on the master */
 +static real flex_fit_angle(t_rotgrp *rotg)
 +{
 +    int         i;
 +    rvec        *fitcoords=NULL;
 +    rvec        center;         /* Center of positions passed to the fit routine */
 +    real        fitangle;       /* Angle of the rotation group derived by fitting */
 +    rvec        coord;
 +    real        scal;
 +    gmx_enfrotgrp_t erg;        /* Pointer to enforced rotation group data */
 +
 +    
 +    erg=rotg->enfrotgrp;
 +
 +    /* Get the center of the rotation group.
 +     * Note, again, erg->xc has been sorted in do_flexible */
 +    get_center(erg->xc, erg->mc_sorted, rotg->nat, center);
 +
 +    /* === Determine the optimal fit angle for the rotation group === */
 +    if (rotg->eFittype == erotgFitNORM)
 +    {
 +        /* Normalize every position to it's reference length */
 +        for (i=0; i<rotg->nat; i++)
 +        {
 +            /* Put the center of the positions into the origin */
 +            rvec_sub(erg->xc[i], center, coord);
 +            /* Determine the scaling factor for the length: */
 +            scal = erg->xc_ref_length[erg->xc_sortind[i]] / norm(coord);
 +            /* Get position, multiply with the scaling factor and save  */
 +            svmul(scal, coord, erg->xc_norm[i]);
 +        }
 +        fitcoords = erg->xc_norm;
 +    }
 +    else
 +    {
 +        fitcoords = erg->xc;
 +    }
 +    /* From the point of view of the current positions, the reference has rotated
 +     * backwards. Since we output the angle relative to the fixed reference,
 +     * we need the minus sign. */
 +    fitangle = -opt_angle_analytic(erg->xc_ref_sorted, fitcoords, erg->mc_sorted,
 +                                   rotg->nat, erg->xc_ref_center, center, rotg->vec);
 +
 +    return fitangle;
 +}
 +
 +
 +/* Determine actual angle of each slab by RMSD fit to the reference */
 +/* Not parallelized, call this routine only on the master */
 +static void flex_fit_angle_perslab(
 +        int  g,
 +        t_rotgrp *rotg,
 +        double t,
 +        real degangle,
 +        FILE *fp)
 +{
 +    int         i,l,n,islab,ind;
 +    rvec        curr_x, ref_x;
 +    rvec        act_center;  /* Center of actual positions that are passed to the fit routine */
 +    rvec        ref_center;  /* Same for the reference positions */
 +    real        fitangle;    /* Angle of a slab derived from an RMSD fit to
 +                              * the reference structure at t=0  */
 +    t_gmx_slabdata *sd;
 +    gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data */
 +    real        OOm_av;      /* 1/average_mass of a rotation group atom */
 +    real        m_rel;       /* Relative mass of a rotation group atom  */
 +
 +
 +    erg=rotg->enfrotgrp;
 +
 +    /* Average mass of a rotation group atom: */
 +    OOm_av = erg->invmass*rotg->nat;
 +
 +    /**********************************/
 +    /* First collect the data we need */
 +    /**********************************/
 +
 +    /* Collect the data for the individual slabs */
 +    for (n = erg->slab_first; n <= erg->slab_last; n++)
 +    {
 +        islab = n - erg->slab_first; /* slab index */
 +        sd = &(rotg->enfrotgrp->slab_data[islab]);
 +        sd->nat = erg->lastatom[islab]-erg->firstatom[islab]+1;
 +        ind = 0;
 +
 +        /* Loop over the relevant atoms in the slab */
 +        for (l=erg->firstatom[islab]; l<=erg->lastatom[islab]; l++)
 +        {
 +            /* Current position of this atom: x[ii][XX/YY/ZZ] */
 +            copy_rvec(erg->xc[l], curr_x);
 +
 +            /* The (unrotated) reference position of this atom is copied to ref_x.
 +             * Beware, the xc coords have been sorted in do_flexible */
 +            copy_rvec(erg->xc_ref_sorted[l], ref_x);
 +
 +            /* Save data for doing angular RMSD fit later */
 +            /* Save the current atom position */
 +            copy_rvec(curr_x, sd->x[ind]);
 +            /* Save the corresponding reference position */
 +            copy_rvec(ref_x , sd->ref[ind]);
 +
 +            /* Maybe also mass-weighting was requested. If yes, additionally
 +             * multiply the weights with the relative mass of the atom. If not,
 +             * multiply with unity. */
 +            m_rel = erg->mc_sorted[l]*OOm_av;
 +
 +            /* Save the weight for this atom in this slab */
 +            sd->weight[ind] = gaussian_weight(curr_x, rotg, n) * m_rel;
 +
 +            /* Next atom in this slab */
 +            ind++;
 +        }
 +    }
 +
 +    /******************************/
 +    /* Now do the fit calculation */
 +    /******************************/
 +
 +    fprintf(fp, "%12.3e%6d%12.3f", t, g, degangle);
 +
 +    /* === Now do RMSD fitting for each slab === */
 +    /* We require at least SLAB_MIN_ATOMS in a slab, such that the fit makes sense. */
 +#define SLAB_MIN_ATOMS 4
 +
 +    for (n = erg->slab_first; n <= erg->slab_last; n++)
 +    {
 +        islab = n - erg->slab_first; /* slab index */
 +        sd = &(rotg->enfrotgrp->slab_data[islab]);
 +        if (sd->nat >= SLAB_MIN_ATOMS)
 +        {
 +            /* Get the center of the slabs reference and current positions */
 +            get_center(sd->ref, sd->weight, sd->nat, ref_center);
 +            get_center(sd->x  , sd->weight, sd->nat, act_center);
 +            if (rotg->eFittype == erotgFitNORM)
 +            {
 +                /* Normalize every position to it's reference length
 +                 * prior to performing the fit */
 +                for (i=0; i<sd->nat;i++) /* Center */
 +                {
 +                    rvec_dec(sd->ref[i], ref_center);
 +                    rvec_dec(sd->x[i]  , act_center);
 +                    /* Normalize x_i such that it gets the same length as ref_i */
 +                    svmul( norm(sd->ref[i])/norm(sd->x[i]), sd->x[i], sd->x[i] );
 +                }
 +                /* We already subtracted the centers */
 +                clear_rvec(ref_center);
 +                clear_rvec(act_center);
 +            }
 +            fitangle = -opt_angle_analytic(sd->ref, sd->x, sd->weight, sd->nat,
 +                                           ref_center, act_center, rotg->vec);
 +            fprintf(fp, "%6d%6d%12.3f", n, sd->nat, fitangle);
 +        }
 +    }
 +    fprintf(fp     , "\n");
 +
 +#undef SLAB_MIN_ATOMS
 +}
 +
 +
 +/* Shift x with is */
 +static gmx_inline void shift_single_coord(matrix box, rvec x, const ivec is)
 +{
 +    int tx,ty,tz;
 +
 +
 +    tx=is[XX];
 +    ty=is[YY];
 +    tz=is[ZZ];
 +
 +    if(TRICLINIC(box))
 +    {
 +        x[XX] += tx*box[XX][XX]+ty*box[YY][XX]+tz*box[ZZ][XX];
 +        x[YY] += ty*box[YY][YY]+tz*box[ZZ][YY];
 +        x[ZZ] += tz*box[ZZ][ZZ];
 +    } else
 +    {
 +        x[XX] += tx*box[XX][XX];
 +        x[YY] += ty*box[YY][YY];
 +        x[ZZ] += tz*box[ZZ][ZZ];
 +    }
 +}
 +
 +
 +/* Determine the 'home' slab of this atom which is the
 + * slab with the highest Gaussian weight of all */
 +#define round(a) (int)(a+0.5)
 +static gmx_inline int get_homeslab(
 +        rvec curr_x,   /* The position for which the home slab shall be determined */ 
 +        rvec rotvec,   /* The rotation vector */
 +        real slabdist) /* The slab distance */
 +{
 +    real dist;
 +    
 +    
 +    /* The distance of the atom to the coordinate center (where the
 +     * slab with index 0) is */
 +    dist = iprod(rotvec, curr_x);
 +    
 +    return round(dist / slabdist); 
 +}
 +
 +
 +/* For a local atom determine the relevant slabs, i.e. slabs in
 + * which the gaussian is larger than min_gaussian
 + */
 +static int get_single_atom_gaussians(
 +        rvec      curr_x,
 +        t_rotgrp  *rotg)
 +{
 +   int slab, homeslab;
 +   real g;
 +   int count = 0;
 +   gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +
 +   
 +   erg=rotg->enfrotgrp;
 +   
 +   /* Determine the 'home' slab of this atom: */
 +   homeslab = get_homeslab(curr_x, rotg->vec, rotg->slab_dist);
 +
 +   /* First determine the weight in the atoms home slab: */
 +   g = gaussian_weight(curr_x, rotg, homeslab);
 +   
 +   erg->gn_atom[count] = g;
 +   erg->gn_slabind[count] = homeslab;
 +   count++;
 +   
 +   
 +   /* Determine the max slab */
 +   slab = homeslab;
 +   while (g > rotg->min_gaussian)
 +   {
 +       slab++;
 +       g = gaussian_weight(curr_x, rotg, slab);
 +       erg->gn_slabind[count]=slab;
 +       erg->gn_atom[count]=g;
 +       count++;
 +   }
 +   count--;
 +   
 +   /* Determine the max slab */
 +   slab = homeslab;
 +   do
 +   {
 +       slab--;
 +       g = gaussian_weight(curr_x, rotg, slab);       
 +       erg->gn_slabind[count]=slab;
 +       erg->gn_atom[count]=g;
 +       count++;
 +   }
 +   while (g > rotg->min_gaussian);
 +   count--;
 +   
 +   return count;
 +}
 +
 +
 +static void flex2_precalc_inner_sum(t_rotgrp *rotg)
 +{
 +    int  i,n,islab;
 +    rvec  xi;                /* positions in the i-sum                        */
 +    rvec  xcn, ycn;          /* the current and the reference slab centers    */
 +    real gaussian_xi;
 +    rvec yi0;
 +    rvec  rin;               /* Helper variables                              */
 +    real  fac,fac2;
 +    rvec innersumvec;
 +    real OOpsii,OOpsiistar;
 +    real sin_rin;          /* s_ii.r_ii */
 +    rvec s_in,tmpvec,tmpvec2;
 +    real mi,wi;            /* Mass-weighting of the positions                 */
 +    real N_M;              /* N/M                                             */
 +    gmx_enfrotgrp_t erg;    /* Pointer to enforced rotation group data */
 +
 +
 +    erg=rotg->enfrotgrp;
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Loop over all slabs that contain something */
 +    for (n=erg->slab_first; n <= erg->slab_last; n++)
 +    {
 +        islab = n - erg->slab_first; /* slab index */
 +
 +        /* The current center of this slab is saved in xcn: */
 +        copy_rvec(erg->slab_center[islab], xcn);
 +        /* ... and the reference center in ycn: */
 +        copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
 +
 +        /*** D. Calculate the whole inner sum used for second and third sum */
 +        /* For slab n, we need to loop over all atoms i again. Since we sorted
 +         * the atoms with respect to the rotation vector, we know that it is sufficient
 +         * to calculate from firstatom to lastatom only. All other contributions will
 +         * be very small. */
 +        clear_rvec(innersumvec);
 +        for (i = erg->firstatom[islab]; i <= erg->lastatom[islab]; i++)
 +        {
 +            /* Coordinate xi of this atom */
 +            copy_rvec(erg->xc[i],xi);
 +
 +            /* The i-weights */
 +            gaussian_xi = gaussian_weight(xi,rotg,n);
 +            mi = erg->mc_sorted[i];  /* need the sorted mass here */
 +            wi = N_M*mi;
 +
 +            /* Calculate rin */
 +            copy_rvec(erg->xc_ref_sorted[i],yi0); /* Reference position yi0   */
 +            rvec_sub(yi0, ycn, tmpvec2);          /* tmpvec2 = yi0 - ycn      */
 +            mvmul(erg->rotmat, tmpvec2, rin);     /* rin = Omega.(yi0 - ycn)  */
 +
 +            /* Calculate psi_i* and sin */
 +            rvec_sub(xi, xcn, tmpvec2);           /* tmpvec2 = xi - xcn       */
 +            cprod(rotg->vec, tmpvec2, tmpvec);    /* tmpvec = v x (xi - xcn)  */
 +            OOpsiistar = norm2(tmpvec)+rotg->eps; /* OOpsii* = 1/psii* = |v x (xi-xcn)|^2 + eps */
 +            OOpsii = norm(tmpvec);                /* OOpsii = 1 / psii = |v x (xi - xcn)| */
 +
 +                                       /*         v x (xi - xcn)          */
 +            unitv(tmpvec, s_in);       /*  sin = ----------------         */
 +                                       /*        |v x (xi - xcn)|         */
 +
 +            sin_rin=iprod(s_in,rin);   /* sin_rin = sin . rin             */
 +
 +            /* Now the whole sum */
 +            fac = OOpsii/OOpsiistar;
 +            svmul(fac, rin, tmpvec);
 +            fac2 = fac*fac*OOpsii;
 +            svmul(fac2*sin_rin, s_in, tmpvec2);
 +            rvec_dec(tmpvec, tmpvec2);
 +
 +            svmul(wi*gaussian_xi*sin_rin, tmpvec, tmpvec2);
 +
 +            rvec_inc(innersumvec,tmpvec2);
 +        } /* now we have the inner sum, used both for sum2 and sum3 */
 +
 +        /* Save it to be used in do_flex2_lowlevel */
 +        copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
 +    } /* END of loop over slabs */
 +}
 +
 +
 +static void flex_precalc_inner_sum(t_rotgrp *rotg)
 +{
 +    int   i,n,islab;
 +    rvec  xi;                /* position                                      */
 +    rvec  xcn, ycn;          /* the current and the reference slab centers    */
 +    rvec  qin,rin;           /* q_i^n and r_i^n                               */
 +    real  bin;
 +    rvec  tmpvec;
 +    rvec  innersumvec;       /* Inner part of sum_n2                          */
 +    real  gaussian_xi;       /* Gaussian weight gn(xi)                        */
 +    real  mi,wi;             /* Mass-weighting of the positions               */
 +    real  N_M;               /* N/M                                           */
 +
 +    gmx_enfrotgrp_t erg;    /* Pointer to enforced rotation group data */
 +
 +
 +    erg=rotg->enfrotgrp;
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Loop over all slabs that contain something */
 +    for (n=erg->slab_first; n <= erg->slab_last; n++)
 +    {
 +        islab = n - erg->slab_first; /* slab index */
 +
 +        /* The current center of this slab is saved in xcn: */
 +        copy_rvec(erg->slab_center[islab], xcn);
 +        /* ... and the reference center in ycn: */
 +        copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
 +
 +        /* For slab n, we need to loop over all atoms i again. Since we sorted
 +         * the atoms with respect to the rotation vector, we know that it is sufficient
 +         * to calculate from firstatom to lastatom only. All other contributions will
 +         * be very small. */
 +        clear_rvec(innersumvec);
 +        for (i=erg->firstatom[islab]; i<=erg->lastatom[islab]; i++)
 +        {
 +            /* Coordinate xi of this atom */
 +            copy_rvec(erg->xc[i],xi);
 +
 +            /* The i-weights */
 +            gaussian_xi = gaussian_weight(xi,rotg,n);
 +            mi = erg->mc_sorted[i];  /* need the sorted mass here */
 +            wi = N_M*mi;
 +
 +            /* Calculate rin and qin */
 +            rvec_sub(erg->xc_ref_sorted[i], ycn, tmpvec); /* tmpvec = yi0-ycn */
 +            mvmul(erg->rotmat, tmpvec, rin);      /* rin = Omega.(yi0 - ycn)  */
 +            cprod(rotg->vec, rin, tmpvec);    /* tmpvec = v x Omega*(yi0-ycn) */
 +
 +                                             /*        v x Omega*(yi0-ycn)    */
 +            unitv(tmpvec, qin);              /* qin = ---------------------   */
 +                                             /*       |v x Omega*(yi0-ycn)|   */
 +
 +            /* Calculate bin */
 +            rvec_sub(xi, xcn, tmpvec);            /* tmpvec = xi-xcn          */
 +            bin = iprod(qin, tmpvec);             /* bin  = qin*(xi-xcn)      */
 +
 +            svmul(wi*gaussian_xi*bin, qin, tmpvec);
 +
 +            /* Add this contribution to the inner sum: */
 +            rvec_add(innersumvec, tmpvec, innersumvec);
 +        } /* now we have the inner sum vector S^n for this slab */
 +        /* Save it to be used in do_flex_lowlevel */
 +        copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
 +    }
 +}
 +
 +
 +static real do_flex2_lowlevel(
 +        t_rotgrp  *rotg,
 +        real      sigma,    /* The Gaussian width sigma */
 +        rvec      x[],
 +        gmx_bool  bOutstepRot,
 +        gmx_bool  bOutstepSlab,
 +        matrix    box)
 +{
 +    int  count,ic,ii,j,m,n,islab,iigrp,ifit;
 +    rvec xj;                 /* position in the i-sum                         */
 +    rvec yj0;                /* the reference position in the j-sum           */
 +    rvec xcn, ycn;           /* the current and the reference slab centers    */
 +    real V;                  /* This node's part of the rotation pot. energy  */
 +    real gaussian_xj;        /* Gaussian weight                               */
 +    real beta;
 +
 +    real  numerator,fit_numerator;
 +    rvec  rjn,fit_rjn;       /* Helper variables                              */
 +    real  fac,fac2;
 +
 +    real OOpsij,OOpsijstar;
 +    real OOsigma2;           /* 1/(sigma^2)                                   */
 +    real sjn_rjn;
 +    real betasigpsi;
 +    rvec sjn,tmpvec,tmpvec2,yj0_ycn;
 +    rvec sum1vec_part,sum1vec,sum2vec_part,sum2vec,sum3vec,sum4vec,innersumvec;
 +    real sum3,sum4;
 +    gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data       */
 +    real mj,wj;              /* Mass-weighting of the positions               */
 +    real N_M;                /* N/M                                           */
 +    real Wjn;                /* g_n(x_j) m_j / Mjn                            */
 +    gmx_bool bCalcPotFit;
 +
 +    /* To calculate the torque per slab */
 +    rvec slab_force;         /* Single force from slab n on one atom          */
 +    rvec slab_sum1vec_part;
 +    real slab_sum3part,slab_sum4part;
 +    rvec slab_sum1vec, slab_sum2vec, slab_sum3vec, slab_sum4vec;
 +
 +
 +    erg=rotg->enfrotgrp;
 +
 +    /* Pre-calculate the inner sums, so that we do not have to calculate
 +     * them again for every atom */
 +    flex2_precalc_inner_sum(rotg);
 +
 +    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
 +
 +    /********************************************************/
 +    /* Main loop over all local atoms of the rotation group */
 +    /********************************************************/
 +    N_M = rotg->nat * erg->invmass;
 +    V = 0.0;
 +    OOsigma2 = 1.0 / (sigma*sigma);
 +    for (j=0; j<erg->nat_loc; j++)
 +    {
 +        /* Local index of a rotation group atom  */
 +        ii = erg->ind_loc[j];
 +        /* Position of this atom in the collective array */
 +        iigrp = erg->xc_ref_ind[j];
 +        /* Mass-weighting */
 +        mj = erg->mc[iigrp];  /* need the unsorted mass here */
 +        wj = N_M*mj;
 +        
 +        /* Current position of this atom: x[ii][XX/YY/ZZ]
 +         * Note that erg->xc_center contains the center of mass in case the flex2-t
 +         * potential was chosen. For the flex2 potential erg->xc_center must be
 +         * zero. */
 +        rvec_sub(x[ii], erg->xc_center, xj);
 +
 +        /* Shift this atom such that it is near its reference */
 +        shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
 +
 +        /* Determine the slabs to loop over, i.e. the ones with contributions
 +         * larger than min_gaussian */
 +        count = get_single_atom_gaussians(xj, rotg);
 +        
 +        clear_rvec(sum1vec_part);
 +        clear_rvec(sum2vec_part);
 +        sum3 = 0.0;
 +        sum4 = 0.0;
 +        /* Loop over the relevant slabs for this atom */
 +        for (ic=0; ic < count; ic++)  
 +        {
 +            n = erg->gn_slabind[ic];
 +            
 +            /* Get the precomputed Gaussian value of curr_slab for curr_x */
 +            gaussian_xj = erg->gn_atom[ic];
 +
 +            islab = n - erg->slab_first; /* slab index */
 +            
 +            /* The (unrotated) reference position of this atom is copied to yj0: */
 +            copy_rvec(rotg->x_ref[iigrp], yj0);
 +
 +            beta = calc_beta(xj, rotg,n);
 +
 +            /* The current center of this slab is saved in xcn: */
 +            copy_rvec(erg->slab_center[islab], xcn);
 +            /* ... and the reference center in ycn: */
 +            copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
 +            
 +            rvec_sub(yj0, ycn, yj0_ycn);          /* yj0_ycn = yj0 - ycn      */
 +
 +            /* Rotate: */
 +            mvmul(erg->rotmat, yj0_ycn, rjn);     /* rjn = Omega.(yj0 - ycn)  */
 +            
 +            /* Subtract the slab center from xj */
 +            rvec_sub(xj, xcn, tmpvec2);           /* tmpvec2 = xj - xcn       */
 +
 +            /* Calculate sjn */
 +            cprod(rotg->vec, tmpvec2, tmpvec);    /* tmpvec = v x (xj - xcn)  */
 +
 +            OOpsijstar = norm2(tmpvec)+rotg->eps; /* OOpsij* = 1/psij* = |v x (xj-xcn)|^2 + eps */
 +
 +            numerator = sqr(iprod(tmpvec, rjn));
 +            
 +            /*********************************/
 +            /* Add to the rotation potential */
 +            /*********************************/
 +            V += 0.5*rotg->k*wj*gaussian_xj*numerator/OOpsijstar;
 +
 +            /* If requested, also calculate the potential for a set of angles
 +             * near the current reference angle */
 +            if (bCalcPotFit)
 +            {
 +                for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
 +                {
 +                    mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, fit_rjn);
 +                    fit_numerator = sqr(iprod(tmpvec, fit_rjn));
 +                    erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*gaussian_xj*fit_numerator/OOpsijstar;
 +                }
 +            }
 +
 +            /*************************************/
 +            /* Now calculate the force on atom j */
 +            /*************************************/
 +
 +            OOpsij = norm(tmpvec);    /* OOpsij = 1 / psij = |v x (xj - xcn)| */
 +
 +                                           /*         v x (xj - xcn)          */
 +            unitv(tmpvec, sjn);            /*  sjn = ----------------         */
 +                                           /*        |v x (xj - xcn)|         */
 +
 +            sjn_rjn=iprod(sjn,rjn);        /* sjn_rjn = sjn . rjn             */
 +
 +
 +            /*** A. Calculate the first of the four sum terms: ****************/
 +            fac = OOpsij/OOpsijstar;
 +            svmul(fac, rjn, tmpvec);
 +            fac2 = fac*fac*OOpsij;
 +            svmul(fac2*sjn_rjn, sjn, tmpvec2);
 +            rvec_dec(tmpvec, tmpvec2);
 +            fac2 = wj*gaussian_xj; /* also needed for sum4 */
 +            svmul(fac2*sjn_rjn, tmpvec, slab_sum1vec_part);
 +            /********************/
 +            /*** Add to sum1: ***/
 +            /********************/
 +            rvec_inc(sum1vec_part, slab_sum1vec_part); /* sum1 still needs to vector multiplied with v */
 +
 +            /*** B. Calculate the forth of the four sum terms: ****************/
 +            betasigpsi = beta*OOsigma2*OOpsij; /* this is also needed for sum3 */
 +            /********************/
 +            /*** Add to sum4: ***/
 +            /********************/
 +            slab_sum4part = fac2*betasigpsi*fac*sjn_rjn*sjn_rjn; /* Note that fac is still valid from above */
 +            sum4 += slab_sum4part;
 +
 +            /*** C. Calculate Wjn for second and third sum */
 +            /* Note that we can safely divide by slab_weights since we check in
 +             * get_slab_centers that it is non-zero. */
 +            Wjn = gaussian_xj*mj/erg->slab_weights[islab];
 +
 +            /* We already have precalculated the inner sum for slab n */
 +            copy_rvec(erg->slab_innersumvec[islab], innersumvec);
 +
 +            /* Weigh the inner sum vector with Wjn */
 +            svmul(Wjn, innersumvec, innersumvec);
 +
 +            /*** E. Calculate the second of the four sum terms: */
 +            /********************/
 +            /*** Add to sum2: ***/
 +            /********************/
 +            rvec_inc(sum2vec_part, innersumvec); /* sum2 still needs to be vector crossproduct'ed with v */
 +            
 +            /*** F. Calculate the third of the four sum terms: */
 +            slab_sum3part = betasigpsi * iprod(sjn, innersumvec);
 +            sum3 += slab_sum3part; /* still needs to be multiplied with v */
 +
 +            /*** G. Calculate the torque on the local slab's axis: */
 +            if (bOutstepRot)
 +            {
 +                /* Sum1 */
 +                cprod(slab_sum1vec_part, rotg->vec, slab_sum1vec);
 +                /* Sum2 */
 +                cprod(innersumvec, rotg->vec, slab_sum2vec);
 +                /* Sum3 */
 +                svmul(slab_sum3part, rotg->vec, slab_sum3vec);
 +                /* Sum4 */
 +                svmul(slab_sum4part, rotg->vec, slab_sum4vec);
 +
 +                /* The force on atom ii from slab n only: */
 +                for (m=0; m<DIM; m++)
 +                    slab_force[m] = rotg->k * (-slab_sum1vec[m] + slab_sum2vec[m] - slab_sum3vec[m] + 0.5*slab_sum4vec[m]);
 +
 +                erg->slab_torque_v[islab] += torque(rotg->vec, slab_force, xj, xcn);
 +            }
 +        } /* END of loop over slabs */
 +
 +        /* Construct the four individual parts of the vector sum: */
 +        cprod(sum1vec_part, rotg->vec, sum1vec);      /* sum1vec =   { } x v  */
 +        cprod(sum2vec_part, rotg->vec, sum2vec);      /* sum2vec =   { } x v  */
 +        svmul(sum3, rotg->vec, sum3vec);              /* sum3vec =   { } . v  */
 +        svmul(sum4, rotg->vec, sum4vec);              /* sum4vec =   { } . v  */
 +
 +        /* Store the additional force so that it can be added to the force
 +         * array after the normal forces have been evaluated */
 +        for (m=0; m<DIM; m++)
 +            erg->f_rot_loc[j][m] = rotg->k * (-sum1vec[m] + sum2vec[m] - sum3vec[m] + 0.5*sum4vec[m]);
 +
 +#ifdef SUM_PARTS
 +        fprintf(stderr, "sum1: %15.8f %15.8f %15.8f\n",    -rotg->k*sum1vec[XX],    -rotg->k*sum1vec[YY],    -rotg->k*sum1vec[ZZ]);
 +        fprintf(stderr, "sum2: %15.8f %15.8f %15.8f\n",     rotg->k*sum2vec[XX],     rotg->k*sum2vec[YY],     rotg->k*sum2vec[ZZ]);
 +        fprintf(stderr, "sum3: %15.8f %15.8f %15.8f\n",    -rotg->k*sum3vec[XX],    -rotg->k*sum3vec[YY],    -rotg->k*sum3vec[ZZ]);
 +        fprintf(stderr, "sum4: %15.8f %15.8f %15.8f\n", 0.5*rotg->k*sum4vec[XX], 0.5*rotg->k*sum4vec[YY], 0.5*rotg->k*sum4vec[ZZ]);
 +#endif
 +
 +        PRINT_FORCE_J
 +
 +    } /* END of loop over local atoms */
 +
 +    return V;
 +}
 +
 +
 +static real do_flex_lowlevel(
 +        t_rotgrp *rotg,
 +        real      sigma,     /* The Gaussian width sigma                      */
 +        rvec      x[],
 +        gmx_bool  bOutstepRot,
 +        gmx_bool  bOutstepSlab,
 +        matrix    box)
 +{
 +    int   count,ic,ifit,ii,j,m,n,islab,iigrp;
 +    rvec  xj,yj0;            /* current and reference position                */
 +    rvec  xcn, ycn;          /* the current and the reference slab centers    */
 +    rvec  yj0_ycn;           /* yj0 - ycn                                     */
 +    rvec  xj_xcn;            /* xj - xcn                                      */
 +    rvec  qjn,fit_qjn;       /* q_i^n                                         */
 +    rvec  sum_n1,sum_n2;     /* Two contributions to the rotation force       */
 +    rvec  innersumvec;       /* Inner part of sum_n2                          */
 +    rvec  s_n;
 +    rvec  force_n;           /* Single force from slab n on one atom          */
 +    rvec  force_n1,force_n2; /* First and second part of force_n              */
 +    rvec  tmpvec,tmpvec2,tmp_f;   /* Helper variables                         */
 +    real  V;                 /* The rotation potential energy                 */
 +    real  OOsigma2;          /* 1/(sigma^2)                                   */
 +    real  beta;              /* beta_n(xj)                                    */
 +    real  bjn, fit_bjn;      /* b_j^n                                         */
 +    real  gaussian_xj;       /* Gaussian weight gn(xj)                        */
 +    real  betan_xj_sigma2;
 +    real  mj,wj;             /* Mass-weighting of the positions               */
 +    real  N_M;               /* N/M                                           */
 +    gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data       */
 +    gmx_bool bCalcPotFit;
 +
 +    
 +    erg=rotg->enfrotgrp;
 +
 +    /* Pre-calculate the inner sums, so that we do not have to calculate
 +     * them again for every atom */
 +    flex_precalc_inner_sum(rotg);
 +
 +    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
 +
 +    /********************************************************/
 +    /* Main loop over all local atoms of the rotation group */
 +    /********************************************************/
 +    OOsigma2 = 1.0/(sigma*sigma);
 +    N_M = rotg->nat * erg->invmass;
 +    V = 0.0;
 +    for (j=0; j<erg->nat_loc; j++)
 +    {
 +        /* Local index of a rotation group atom  */
 +        ii = erg->ind_loc[j];
 +        /* Position of this atom in the collective array */
 +        iigrp = erg->xc_ref_ind[j];
 +        /* Mass-weighting */
 +        mj = erg->mc[iigrp];  /* need the unsorted mass here */
 +        wj = N_M*mj;
 +        
 +        /* Current position of this atom: x[ii][XX/YY/ZZ]
 +         * Note that erg->xc_center contains the center of mass in case the flex-t
 +         * potential was chosen. For the flex potential erg->xc_center must be
 +         * zero. */
 +        rvec_sub(x[ii], erg->xc_center, xj);
 +        
 +        /* Shift this atom such that it is near its reference */
 +        shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
 +
 +        /* Determine the slabs to loop over, i.e. the ones with contributions
 +         * larger than min_gaussian */
 +        count = get_single_atom_gaussians(xj, rotg);
 +
 +        clear_rvec(sum_n1);
 +        clear_rvec(sum_n2);
 +
 +        /* Loop over the relevant slabs for this atom */
 +        for (ic=0; ic < count; ic++)  
 +        {
 +            n = erg->gn_slabind[ic];
 +                
 +            /* Get the precomputed Gaussian for xj in slab n */
 +            gaussian_xj = erg->gn_atom[ic];
 +
 +            islab = n - erg->slab_first; /* slab index */
 +            
 +            /* The (unrotated) reference position of this atom is saved in yj0: */
 +            copy_rvec(rotg->x_ref[iigrp], yj0);
 +
 +            beta = calc_beta(xj, rotg, n);
 +
 +            /* The current center of this slab is saved in xcn: */
 +            copy_rvec(erg->slab_center[islab], xcn);
 +            /* ... and the reference center in ycn: */
 +            copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
 +            
 +            rvec_sub(yj0, ycn, yj0_ycn); /* yj0_ycn = yj0 - ycn */
 +
 +            /* Rotate: */
 +            mvmul(erg->rotmat, yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
 +            
 +            /* Subtract the slab center from xj */
 +            rvec_sub(xj, xcn, xj_xcn);           /* xj_xcn = xj - xcn         */
 +            
 +            /* Calculate qjn */
 +            cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec= v x Omega.(yj0-ycn) */
 +
 +                                 /*         v x Omega.(yj0-ycn)    */
 +            unitv(tmpvec,qjn);   /*  qjn = ---------------------   */
 +                                 /*        |v x Omega.(yj0-ycn)|   */
 +
 +            bjn = iprod(qjn, xj_xcn);   /* bjn = qjn * (xj - xcn) */
 +            
 +            /*********************************/
 +            /* Add to the rotation potential */
 +            /*********************************/
 +            V += 0.5*rotg->k*wj*gaussian_xj*sqr(bjn);
 +            
 +            /* If requested, also calculate the potential for a set of angles
 +             * near the current reference angle */
 +            if (bCalcPotFit)
 +            {
 +                for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
 +                {
 +                    /* As above calculate Omega.(yj0-ycn), now for the other angles */
 +                    mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
 +                    /* As above calculate qjn */
 +                    cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec= v x Omega.(yj0-ycn) */
 +                                             /*             v x Omega.(yj0-ycn)    */
 +                    unitv(tmpvec,fit_qjn);   /*  fit_qjn = ---------------------   */
 +                                             /*            |v x Omega.(yj0-ycn)|   */
 +                    fit_bjn = iprod(fit_qjn, xj_xcn);   /* fit_bjn = fit_qjn * (xj - xcn) */
 +                    /* Add to the rotation potential for this angle */
 +                    erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*gaussian_xj*sqr(fit_bjn);
 +                }
 +            }
 +
 +            /****************************************************************/
 +            /* sum_n1 will typically be the main contribution to the force: */
 +            /****************************************************************/
 +            betan_xj_sigma2 = beta*OOsigma2;  /*  beta_n(xj)/sigma^2  */
 +
 +            /* The next lines calculate
 +             *  qjn - (bjn*beta(xj)/(2sigma^2))v  */
 +            svmul(bjn*0.5*betan_xj_sigma2, rotg->vec, tmpvec2);
 +            rvec_sub(qjn,tmpvec2,tmpvec);
 +
 +            /* Multiply with gn(xj)*bjn: */
 +            svmul(gaussian_xj*bjn,tmpvec,tmpvec2);
 +
 +            /* Sum over n: */
 +            rvec_inc(sum_n1,tmpvec2);
 +            
 +            /* We already have precalculated the Sn term for slab n */
 +            copy_rvec(erg->slab_innersumvec[islab], s_n);
 +                                                                          /*          beta_n(xj)              */
 +            svmul(betan_xj_sigma2*iprod(s_n, xj_xcn), rotg->vec, tmpvec); /* tmpvec = ---------- s_n (xj-xcn) */
 +                                                                          /*            sigma^2               */
 +
 +            rvec_sub(s_n, tmpvec, innersumvec);
 +            
 +            /* We can safely divide by slab_weights since we check in get_slab_centers
 +             * that it is non-zero. */
 +            svmul(gaussian_xj/erg->slab_weights[islab], innersumvec, innersumvec);
 +
 +            rvec_add(sum_n2, innersumvec, sum_n2);
 +            
 +            /* Calculate the torque: */
 +            if (bOutstepRot)
 +            {
 +                /* The force on atom ii from slab n only: */
 +                svmul(-rotg->k*wj, tmpvec2    , force_n1); /* part 1 */
 +                svmul( rotg->k*mj, innersumvec, force_n2); /* part 2 */
 +                rvec_add(force_n1, force_n2, force_n);
 +                erg->slab_torque_v[islab] += torque(rotg->vec, force_n, xj, xcn);
 +            }
 +        } /* END of loop over slabs */
 +
 +        /* Put both contributions together: */
 +        svmul(wj, sum_n1, sum_n1);
 +        svmul(mj, sum_n2, sum_n2);
 +        rvec_sub(sum_n2,sum_n1,tmp_f); /* F = -grad V */
 +
 +        /* Store the additional force so that it can be added to the force
 +         * array after the normal forces have been evaluated */
 +        for(m=0; m<DIM; m++)
 +            erg->f_rot_loc[j][m] = rotg->k*tmp_f[m];
 +
 +        PRINT_FORCE_J
 +
 +    } /* END of loop over local atoms */
 +
 +    return V;
 +}
 +
 +#ifdef PRINT_COORDS
 +static void print_coordinates(t_rotgrp *rotg, rvec x[], matrix box, int step)
 +{
 +    int i;
 +    static FILE *fp;
 +    static char buf[STRLEN];
 +    static gmx_bool bFirst=1;
 +
 +
 +    if (bFirst)
 +    {
 +        sprintf(buf, "coords%d.txt", cr->nodeid);
 +        fp = fopen(buf, "w");
 +        bFirst = 0;
 +    }
 +
 +    fprintf(fp, "\nStep %d\n", step);
 +    fprintf(fp, "box: %f %f %f %f %f %f %f %f %f\n",
 +            box[XX][XX], box[XX][YY], box[XX][ZZ],
 +            box[YY][XX], box[YY][YY], box[YY][ZZ],
 +            box[ZZ][XX], box[ZZ][ZZ], box[ZZ][ZZ]);
 +    for (i=0; i<rotg->nat; i++)
 +    {
 +        fprintf(fp, "%4d  %f %f %f\n", i,
 +                erg->xc[i][XX], erg->xc[i][YY], erg->xc[i][ZZ]);
 +    }
 +    fflush(fp);
 +
 +}
 +#endif
 +
 +
 +static int projection_compare(const void *a, const void *b)
 +{
 +    sort_along_vec_t *xca, *xcb;
 +    
 +    
 +    xca = (sort_along_vec_t *)a;
 +    xcb = (sort_along_vec_t *)b;
 +    
 +    if (xca->xcproj < xcb->xcproj)
 +        return -1;
 +    else if (xca->xcproj > xcb->xcproj)
 +        return 1;
 +    else
 +        return 0;
 +}
 +
 +
 +static void sort_collective_coordinates(
 +        t_rotgrp *rotg,         /* Rotation group */
 +        sort_along_vec_t *data) /* Buffer for sorting the positions */
 +{
 +    int i;
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +
 +    
 +    erg=rotg->enfrotgrp;
 +    
 +    /* The projection of the position vector on the rotation vector is
 +     * the relevant value for sorting. Fill the 'data' structure */
 +    for (i=0; i<rotg->nat; i++)
 +    {
 +        data[i].xcproj = iprod(erg->xc[i], rotg->vec);  /* sort criterium */
 +        data[i].m      = erg->mc[i];
 +        data[i].ind    = i;
 +        copy_rvec(erg->xc[i]    , data[i].x    );
 +        copy_rvec(rotg->x_ref[i], data[i].x_ref);
 +    }
 +    /* Sort the 'data' structure */
 +    gmx_qsort(data, rotg->nat, sizeof(sort_along_vec_t), projection_compare);
 +    
 +    /* Copy back the sorted values */
 +    for (i=0; i<rotg->nat; i++)
 +    {
 +        copy_rvec(data[i].x    , erg->xc[i]           );
 +        copy_rvec(data[i].x_ref, erg->xc_ref_sorted[i]);
 +        erg->mc_sorted[i]  = data[i].m;
 +        erg->xc_sortind[i] = data[i].ind;
 +    }
 +}
 +
 +
 +/* For each slab, get the first and the last index of the sorted atom
 + * indices */
 +static void get_firstlast_atom_per_slab(t_rotgrp *rotg)
 +{
 +    int i,islab,n;
 +    real beta;
 +    gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data */
 +
 +    
 +    erg=rotg->enfrotgrp;
 +
 +    /* Find the first atom that needs to enter the calculation for each slab */
 +    n = erg->slab_first;  /* slab */
 +    i = 0; /* start with the first atom */
 +    do
 +    {
 +        /* Find the first atom that significantly contributes to this slab */
 +        do /* move forward in position until a large enough beta is found */
 +        {
 +            beta = calc_beta(erg->xc[i], rotg, n);
 +            i++;
 +        } while ((beta < -erg->max_beta) && (i < rotg->nat));
 +        i--;
 +        islab = n - erg->slab_first;  /* slab index */
 +        erg->firstatom[islab] = i;
 +        /* Proceed to the next slab */
 +        n++;
 +    } while (n <= erg->slab_last);
 +    
 +    /* Find the last atom for each slab */
 +     n = erg->slab_last; /* start with last slab */
 +     i = rotg->nat-1;  /* start with the last atom */
 +     do
 +     {
 +         do /* move backward in position until a large enough beta is found */
 +         {
 +             beta = calc_beta(erg->xc[i], rotg, n);
 +             i--;
 +         } while ((beta > erg->max_beta) && (i > -1));
 +         i++;
 +         islab = n - erg->slab_first;  /* slab index */
 +         erg->lastatom[islab] = i;
 +         /* Proceed to the next slab */
 +         n--;
 +     } while (n >= erg->slab_first);
 +}
 +
 +
 +/* Determine the very first and very last slab that needs to be considered 
 + * For the first slab that needs to be considered, we have to find the smallest
 + * n that obeys:
 + * 
 + * x_first * v - n*Delta_x <= beta_max
 + * 
 + * slab index n, slab distance Delta_x, rotation vector v. For the last slab we 
 + * have to find the largest n that obeys
 + * 
 + * x_last * v - n*Delta_x >= -beta_max
 + *  
 + */
 +static gmx_inline int get_first_slab(
 +        t_rotgrp *rotg,     /* The rotation group (inputrec data) */
 +        real     max_beta,  /* The max_beta value, instead of min_gaussian */
 +        rvec     firstatom) /* First atom after sorting along the rotation vector v */
 +{
 +    /* Find the first slab for the first atom */   
 +    return ceil((iprod(firstatom, rotg->vec) - max_beta)/rotg->slab_dist);
 +}
 +
 +
 +static gmx_inline int get_last_slab(
 +        t_rotgrp *rotg,     /* The rotation group (inputrec data) */
 +        real     max_beta,  /* The max_beta value, instead of min_gaussian */
 +        rvec     lastatom)  /* Last atom along v */
 +{
 +    /* Find the last slab for the last atom */
 +    return floor((iprod(lastatom, rotg->vec) + max_beta)/rotg->slab_dist);    
 +}
 +
 +
 +static void get_firstlast_slab_check(
 +        t_rotgrp        *rotg,     /* The rotation group (inputrec data) */
 +        t_gmx_enfrotgrp *erg,      /* The rotation group (data only accessible in this file) */
 +        rvec            firstatom, /* First atom after sorting along the rotation vector v */
 +        rvec            lastatom,  /* Last atom along v */
 +        int             g)         /* The rotation group number */
 +{
 +    erg->slab_first = get_first_slab(rotg, erg->max_beta, firstatom);
 +    erg->slab_last  = get_last_slab(rotg, erg->max_beta, lastatom);
 +
 +    /* Check whether we have reference data to compare against */
 +    if (erg->slab_first < erg->slab_first_ref)
 +        gmx_fatal(FARGS, "%s No reference data for first slab (n=%d), unable to proceed.",
 +                  RotStr, erg->slab_first);
 +    
 +    /* Check whether we have reference data to compare against */
 +    if (erg->slab_last > erg->slab_last_ref)
 +        gmx_fatal(FARGS, "%s No reference data for last slab (n=%d), unable to proceed.",
 +                  RotStr, erg->slab_last);
 +}
 +
 +
 +/* Enforced rotation with a flexible axis */
 +static void do_flexible(
 +        gmx_bool  bMaster,
 +        gmx_enfrot_t enfrot,    /* Other rotation data                        */
 +        t_rotgrp  *rotg,        /* The rotation group                         */
 +        int       g,            /* Group number                               */
 +        rvec      x[],          /* The local positions                        */
 +        matrix    box,
 +        double    t,            /* Time in picoseconds                        */
 +        gmx_large_int_t step,   /* The time step                              */
 +        gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
 +        gmx_bool  bOutstepSlab) /* Output per-slab data                       */
 +{
 +    int          l,nslabs;
 +    real         sigma;       /* The Gaussian width sigma */
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
 +
 +    
 +    erg=rotg->enfrotgrp;
 +
 +    /* Define the sigma value */
 +    sigma = 0.7*rotg->slab_dist;
 +    
 +    /* Sort the collective coordinates erg->xc along the rotation vector. This is
 +     * an optimization for the inner loop. */
 +    sort_collective_coordinates(rotg, enfrot->data);
 +    
 +    /* Determine the first relevant slab for the first atom and the last
 +     * relevant slab for the last atom */
 +    get_firstlast_slab_check(rotg, erg, erg->xc[0], erg->xc[rotg->nat-1], g);
 +    
 +    /* Determine for each slab depending on the min_gaussian cutoff criterium,
 +     * a first and a last atom index inbetween stuff needs to be calculated */
 +    get_firstlast_atom_per_slab(rotg);
 +
 +    /* Determine the gaussian-weighted center of positions for all slabs */
 +    get_slab_centers(rotg,erg->xc,erg->mc_sorted,g,t,enfrot->out_slabs,bOutstepSlab,FALSE);
 +        
 +    /* Clear the torque per slab from last time step: */
 +    nslabs = erg->slab_last - erg->slab_first + 1;
 +    for (l=0; l<nslabs; l++)
 +        erg->slab_torque_v[l] = 0.0;
 +    
 +    /* Call the rotational forces kernel */
 +    if (rotg->eType == erotgFLEX || rotg->eType == erotgFLEXT)
 +        erg->V = do_flex_lowlevel(rotg, sigma, x, bOutstepRot, bOutstepSlab, box);
 +    else if (rotg->eType == erotgFLEX2 || rotg->eType == erotgFLEX2T)
 +        erg->V = do_flex2_lowlevel(rotg, sigma, x, bOutstepRot, bOutstepSlab, box);
 +    else
 +        gmx_fatal(FARGS, "Unknown flexible rotation type");
 +    
 +    /* Determine angle by RMSD fit to the reference - Let's hope this */
 +    /* only happens once in a while, since this is not parallelized! */
 +    if ( bMaster && (erotgFitPOT != rotg->eFittype) )
 +    {
 +        if (bOutstepRot)
 +        {
 +            /* Fit angle of the whole rotation group */
 +            erg->angle_v = flex_fit_angle(rotg);
 +        }
 +        if (bOutstepSlab)
 +        {
 +            /* Fit angle of each slab */
 +            flex_fit_angle_perslab(g, rotg, t, erg->degangle, enfrot->out_angles);
 +        }
 +    }
 +
 +    /* Lump together the torques from all slabs: */
 +    erg->torque_v = 0.0;
 +    for (l=0; l<nslabs; l++)
 +         erg->torque_v += erg->slab_torque_v[l];
 +}
 +
 +
 +/* Calculate the angle between reference and actual rotation group atom,
 + * both projected into a plane perpendicular to the rotation vector: */
 +static void angle(t_rotgrp *rotg,
 +        rvec x_act,
 +        rvec x_ref,
 +        real *alpha,
 +        real *weight)  /* atoms near the rotation axis should count less than atoms far away */
 +{
 +    rvec xp, xrp;  /* current and reference positions projected on a plane perpendicular to pg->vec */
 +    rvec dum;
 +
 +
 +    /* Project x_ref and x into a plane through the origin perpendicular to rot_vec: */
 +    /* Project x_ref: xrp = x_ref - (vec * x_ref) * vec */
 +    svmul(iprod(rotg->vec, x_ref), rotg->vec, dum);
 +    rvec_sub(x_ref, dum, xrp);
 +    /* Project x_act: */
 +    svmul(iprod(rotg->vec, x_act), rotg->vec, dum);
 +    rvec_sub(x_act, dum, xp);
 +
 +    /* Retrieve information about which vector precedes. gmx_angle always
 +     * returns a positive angle. */
 +    cprod(xp, xrp, dum); /* if reference precedes, this is pointing into the same direction as vec */
 +
 +    if (iprod(rotg->vec, dum) >= 0)
 +        *alpha = -gmx_angle(xrp, xp);
 +    else
 +        *alpha = +gmx_angle(xrp, xp);
 +    
 +    /* Also return the weight */
 +    *weight = norm(xp);
 +}
 +
 +
 +/* Project first vector onto a plane perpendicular to the second vector 
 + * dr = dr - (dr.v)v
 + * Note that v must be of unit length.
 + */
 +static gmx_inline void project_onto_plane(rvec dr, const rvec v)
 +{
 +    rvec tmp;
 +    
 +    
 +    svmul(iprod(dr,v),v,tmp);  /* tmp = (dr.v)v */
 +    rvec_dec(dr, tmp);         /* dr = dr - (dr.v)v */
 +}
 +
 +
 +/* Fixed rotation: The rotation reference group rotates around the v axis. */
 +/* The atoms of the actual rotation group are attached with imaginary  */
 +/* springs to the reference atoms.                                     */
 +static void do_fixed(
 +        t_rotgrp  *rotg,        /* The rotation group                         */
 +        rvec      x[],          /* The positions                              */
 +        matrix    box,          /* The simulation box                         */
 +        double    t,            /* Time in picoseconds                        */
 +        gmx_large_int_t step,   /* The time step                              */
 +        gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
 +        gmx_bool  bOutstepSlab) /* Output per-slab data                       */
 +{
 +    int       ifit,j,jj,m;
 +    rvec      dr;
 +    rvec      tmp_f;           /* Force */
 +    real      alpha;           /* a single angle between an actual and a reference position */
 +    real      weight;          /* single weight for a single angle */
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +    rvec      xi_xc;           /* xi - xc */
 +    gmx_bool  bCalcPotFit;
 +    rvec      fit_xr_loc;
 +
 +    /* for mass weighting: */
 +    real      wi;              /* Mass-weighting of the positions */
 +    real      N_M;             /* N/M */
 +    real      k_wi;            /* k times wi */
 +
 +    gmx_bool  bProject;
 +
 +    
 +    erg=rotg->enfrotgrp;
 +    bProject = (rotg->eType==erotgPM) || (rotg->eType==erotgPMPF);
 +    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
 +
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Each process calculates the forces on its local atoms */
 +    for (j=0; j<erg->nat_loc; j++)
 +    {
 +        /* Calculate (x_i-x_c) resp. (x_i-u) */
 +        rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xi_xc);
 +
 +        /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
 +        rvec_sub(erg->xr_loc[j], xi_xc, dr);
 +        
 +        if (bProject)
 +            project_onto_plane(dr, rotg->vec);
 +            
 +        /* Mass-weighting */
 +        wi = N_M*erg->m_loc[j];
 +
 +        /* Store the additional force so that it can be added to the force
 +         * array after the normal forces have been evaluated */
 +        k_wi = rotg->k*wi;
 +        for (m=0; m<DIM; m++)
 +        {
 +            tmp_f[m]             = k_wi*dr[m];
 +            erg->f_rot_loc[j][m] = tmp_f[m];
 +            erg->V              += 0.5*k_wi*sqr(dr[m]);
 +        }
 +        
 +        /* If requested, also calculate the potential for a set of angles
 +         * near the current reference angle */
 +        if (bCalcPotFit)
 +        {
 +            for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
 +            {
 +                /* Index of this rotation group atom with respect to the whole rotation group */
 +                jj = erg->xc_ref_ind[j];
 +
 +                /* Rotate with the alternative angle. Like rotate_local_reference(),
 +                 * just for a single local atom */
 +                mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[jj], fit_xr_loc); /* fit_xr_loc = Omega*(y_i-y_c) */
 +
 +                /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
 +                rvec_sub(fit_xr_loc, xi_xc, dr);
 +
 +                if (bProject)
 +                    project_onto_plane(dr, rotg->vec);
 +
 +                /* Add to the rotation potential for this angle: */
 +                erg->PotAngleFit->V[ifit] += 0.5*k_wi*norm2(dr);
 +            }
 +        }
 +
 +        if (bOutstepRot)
 +        {
 +            /* Add to the torque of this rotation group */
 +            erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
 +            
 +            /* Calculate the angle between reference and actual rotation group atom. */
 +            angle(rotg, xi_xc, erg->xr_loc[j], &alpha, &weight);  /* angle in rad, weighted */
 +            erg->angle_v  += alpha * weight;
 +            erg->weight_v += weight;
 +        }
 +        /* If you want enforced rotation to contribute to the virial,
 +         * activate the following lines:
 +            if (MASTER(cr))
 +            {
 +               Add the rotation contribution to the virial
 +              for(j=0; j<DIM; j++)
 +                for(m=0;m<DIM;m++)
 +                  vir[j][m] += 0.5*f[ii][j]*dr[m];
 +            }
 +         */
 +
 +        PRINT_FORCE_J
 +
 +    } /* end of loop over local rotation group atoms */
 +}
 +
 +
 +/* Calculate the radial motion potential and forces */
 +static void do_radial_motion(
 +        t_rotgrp  *rotg,        /* The rotation group                         */
 +        rvec      x[],          /* The positions                              */
 +        matrix    box,          /* The simulation box                         */
 +        double    t,            /* Time in picoseconds                        */
 +        gmx_large_int_t step,   /* The time step                              */
 +        gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
 +        gmx_bool  bOutstepSlab) /* Output per-slab data                       */
 +{
 +    int       j,jj,ifit;
 +    rvec      tmp_f;           /* Force */
 +    real      alpha;           /* a single angle between an actual and a reference position */
 +    real      weight;          /* single weight for a single angle */
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +    rvec      xj_u;            /* xj - u */
 +    rvec      tmpvec,fit_tmpvec;
 +    real      fac,fac2,sum=0.0;
 +    rvec      pj;
 +    gmx_bool  bCalcPotFit;
 +
 +    /* For mass weighting: */
 +    real      wj;              /* Mass-weighting of the positions */
 +    real      N_M;             /* N/M */
 +
 +
 +    erg=rotg->enfrotgrp;
 +    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
 +
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Each process calculates the forces on its local atoms */
 +    for (j=0; j<erg->nat_loc; j++)
 +    {
 +        /* Calculate (xj-u) */
 +        rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xj_u);  /* xj_u = xj-u */
 +
 +        /* Calculate Omega.(yj0-u) */
 +        cprod(rotg->vec, erg->xr_loc[j], tmpvec);  /* tmpvec = v x Omega.(yj0-u) */
 +
 +                              /*         v x Omega.(yj0-u)     */
 +        unitv(tmpvec, pj);    /*  pj = ---------------------   */
 +                              /*       | v x Omega.(yj0-u) |   */
 +
 +        fac = iprod(pj, xj_u);  /* fac = pj.(xj-u) */
 +        fac2 = fac*fac;
 +
 +        /* Mass-weighting */
 +        wj = N_M*erg->m_loc[j];
 +
 +        /* Store the additional force so that it can be added to the force
 +         * array after the normal forces have been evaluated */
 +        svmul(-rotg->k*wj*fac, pj, tmp_f);
 +        copy_rvec(tmp_f, erg->f_rot_loc[j]);
 +        sum += wj*fac2;
 +
 +        /* If requested, also calculate the potential for a set of angles
 +         * near the current reference angle */
 +        if (bCalcPotFit)
 +        {
 +            for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
 +            {
 +                /* Index of this rotation group atom with respect to the whole rotation group */
 +                jj = erg->xc_ref_ind[j];
 +
 +                /* Rotate with the alternative angle. Like rotate_local_reference(),
 +                 * just for a single local atom */
 +                mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[jj], fit_tmpvec); /* fit_tmpvec = Omega*(yj0-u) */
 +
 +                /* Calculate Omega.(yj0-u) */
 +                cprod(rotg->vec, fit_tmpvec, tmpvec);  /* tmpvec = v x Omega.(yj0-u) */
 +                                      /*         v x Omega.(yj0-u)     */
 +                unitv(tmpvec, pj);    /*  pj = ---------------------   */
 +                                      /*       | v x Omega.(yj0-u) |   */
 +
 +                fac = iprod(pj, xj_u);  /* fac = pj.(xj-u) */
 +                fac2 = fac*fac;
 +
 +                /* Add to the rotation potential for this angle: */
 +                erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*fac2;
 +            }
 +        }
 +
 +        if (bOutstepRot)
 +        {
 +            /* Add to the torque of this rotation group */
 +            erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
 +
 +            /* Calculate the angle between reference and actual rotation group atom. */
 +            angle(rotg, xj_u, erg->xr_loc[j], &alpha, &weight);  /* angle in rad, weighted */
 +            erg->angle_v  += alpha * weight;
 +            erg->weight_v += weight;
 +        }
 +
 +        PRINT_FORCE_J
 +
 +    } /* end of loop over local rotation group atoms */
 +    erg->V = 0.5*rotg->k*sum;
 +}
 +
 +
 +/* Calculate the radial motion pivot-free potential and forces */
 +static void do_radial_motion_pf(
 +        t_rotgrp  *rotg,        /* The rotation group                         */
 +        rvec      x[],          /* The positions                              */
 +        matrix    box,          /* The simulation box                         */
 +        double    t,            /* Time in picoseconds                        */
 +        gmx_large_int_t step,   /* The time step                              */
 +        gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
 +        gmx_bool  bOutstepSlab) /* Output per-slab data                       */
 +{
 +    int       i,ii,iigrp,ifit,j;
 +    rvec      xj;              /* Current position */
 +    rvec      xj_xc;           /* xj  - xc  */
 +    rvec      yj0_yc0;         /* yj0 - yc0 */
 +    rvec      tmp_f;           /* Force */
 +    real      alpha;           /* a single angle between an actual and a reference position */
 +    real      weight;          /* single weight for a single angle */
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +    rvec      tmpvec, tmpvec2;
 +    rvec      innersumvec;     /* Precalculation of the inner sum */
 +    rvec      innersumveckM;
 +    real      fac,fac2,V=0.0;
 +    rvec      qi,qj;
 +    gmx_bool  bCalcPotFit;
 +
 +    /* For mass weighting: */
 +    real      mj,wi,wj;        /* Mass-weighting of the positions */
 +    real      N_M;             /* N/M */
 +
 +
 +    erg=rotg->enfrotgrp;
 +    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
 +
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Get the current center of the rotation group: */
 +    get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
 +
 +    /* Precalculate Sum_i [ wi qi.(xi-xc) qi ] which is needed for every single j */
 +    clear_rvec(innersumvec);
 +    for (i=0; i < rotg->nat; i++)
 +    {
 +        /* Mass-weighting */
 +        wi = N_M*erg->mc[i];
 +
 +        /* Calculate qi. Note that xc_ref_center has already been subtracted from
 +         * x_ref in init_rot_group.*/
 +        mvmul(erg->rotmat, rotg->x_ref[i], tmpvec);  /* tmpvec  = Omega.(yi0-yc0) */
 +
 +        cprod(rotg->vec, tmpvec, tmpvec2);          /* tmpvec2 = v x Omega.(yi0-yc0) */
 +
 +                              /*         v x Omega.(yi0-yc0)     */
 +        unitv(tmpvec2, qi);   /*  qi = -----------------------   */
 +                              /*       | v x Omega.(yi0-yc0) |   */
 +
 +        rvec_sub(erg->xc[i], erg->xc_center, tmpvec);  /* tmpvec = xi-xc */
 +
 +        svmul(wi*iprod(qi, tmpvec), qi, tmpvec2);
 +
 +        rvec_inc(innersumvec, tmpvec2);
 +    }
 +    svmul(rotg->k*erg->invmass, innersumvec, innersumveckM);
 +
 +    /* Each process calculates the forces on its local atoms */
 +    for (j=0; j<erg->nat_loc; j++)
 +    {
 +        /* Local index of a rotation group atom  */
 +        ii = erg->ind_loc[j];
 +        /* Position of this atom in the collective array */
 +        iigrp = erg->xc_ref_ind[j];
 +        /* Mass-weighting */
 +        mj = erg->mc[iigrp];  /* need the unsorted mass here */
 +        wj = N_M*mj;
 +
 +        /* Current position of this atom: x[ii][XX/YY/ZZ] */
 +        copy_rvec(x[ii], xj);
 +
 +        /* Shift this atom such that it is near its reference */
 +        shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
 +
 +        /* The (unrotated) reference position is yj0. yc0 has already
 +         * been subtracted in init_rot_group */
 +        copy_rvec(rotg->x_ref[iigrp], yj0_yc0);   /* yj0_yc0 = yj0 - yc0      */
 +
 +        /* Calculate Omega.(yj0-yc0) */
 +        mvmul(erg->rotmat, yj0_yc0, tmpvec2);     /* tmpvec2 = Omega.(yj0 - yc0)  */
 +
 +        cprod(rotg->vec, tmpvec2, tmpvec);  /* tmpvec = v x Omega.(yj0-yc0) */
 +
 +                              /*         v x Omega.(yj0-yc0)     */
 +        unitv(tmpvec, qj);    /*  qj = -----------------------   */
 +                              /*       | v x Omega.(yj0-yc0) |   */
 +
 +        /* Calculate (xj-xc) */
 +        rvec_sub(xj, erg->xc_center, xj_xc);  /* xj_xc = xj-xc */
 +
 +        fac = iprod(qj, xj_xc);  /* fac = qj.(xj-xc) */
 +        fac2 = fac*fac;
 +
 +        /* Store the additional force so that it can be added to the force
 +         * array after the normal forces have been evaluated */
 +        svmul(-rotg->k*wj*fac, qj, tmp_f); /* part 1 of force */
 +        svmul(mj, innersumveckM, tmpvec);  /* part 2 of force */
 +        rvec_inc(tmp_f, tmpvec);
 +        copy_rvec(tmp_f, erg->f_rot_loc[j]);
 +        V += wj*fac2;
 +
 +        /* If requested, also calculate the potential for a set of angles
 +         * near the current reference angle */
 +        if (bCalcPotFit)
 +        {
 +            for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
 +            {
 +                /* Rotate with the alternative angle. Like rotate_local_reference(),
 +                 * just for a single local atom */
 +                mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, tmpvec2); /* tmpvec2 = Omega*(yj0-yc0) */
 +
 +                /* Calculate Omega.(yj0-u) */
 +                cprod(rotg->vec, tmpvec2, tmpvec);  /* tmpvec = v x Omega.(yj0-yc0) */
 +                                      /*         v x Omega.(yj0-yc0)     */
 +                unitv(tmpvec, qj);    /*  qj = -----------------------   */
 +                                      /*       | v x Omega.(yj0-yc0) |   */
 +
 +                fac = iprod(qj, xj_xc);  /* fac = qj.(xj-xc) */
 +                fac2 = fac*fac;
 +
 +                /* Add to the rotation potential for this angle: */
 +                erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*fac2;
 +            }
 +        }
 +
 +        if (bOutstepRot)
 +        {
 +            /* Add to the torque of this rotation group */
 +            erg->torque_v += torque(rotg->vec, tmp_f, xj, erg->xc_center);
 +
 +            /* Calculate the angle between reference and actual rotation group atom. */
 +            angle(rotg, xj_xc, yj0_yc0, &alpha, &weight);  /* angle in rad, weighted */
 +            erg->angle_v  += alpha * weight;
 +            erg->weight_v += weight;
 +        }
 +
 +        PRINT_FORCE_J
 +
 +    } /* end of loop over local rotation group atoms */
 +    erg->V = 0.5*rotg->k*V;
 +}
 +
 +
 +/* Precalculate the inner sum for the radial motion 2 forces */
 +static void radial_motion2_precalc_inner_sum(t_rotgrp  *rotg, rvec innersumvec)
 +{
 +    int       i;
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +    rvec      xi_xc;           /* xj - xc */
 +    rvec      tmpvec,tmpvec2;
 +    real      fac,fac2;
 +    rvec      ri,si;
 +    real      siri;
 +    rvec      v_xi_xc;          /* v x (xj - u) */
 +    real      psii,psiistar;
 +    real      wi;              /* Mass-weighting of the positions */
 +    real      N_M;             /* N/M */
 +    rvec      sumvec;
 +
 +    erg=rotg->enfrotgrp;
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Loop over the collective set of positions */
 +    clear_rvec(sumvec);
 +    for (i=0; i<rotg->nat; i++)
 +    {
 +        /* Mass-weighting */
 +        wi = N_M*erg->mc[i];
 +
 +        rvec_sub(erg->xc[i], erg->xc_center, xi_xc); /* xi_xc = xi-xc         */
 +
 +        /* Calculate ri. Note that xc_ref_center has already been subtracted from
 +         * x_ref in init_rot_group.*/
 +        mvmul(erg->rotmat, rotg->x_ref[i], ri);      /* ri  = Omega.(yi0-yc0) */
 +
 +        cprod(rotg->vec, xi_xc, v_xi_xc);            /* v_xi_xc = v x (xi-u)  */
 +
 +        fac = norm2(v_xi_xc);
 +                                          /*                      1           */
 +        psiistar = 1.0/(fac + rotg->eps); /* psiistar = --------------------- */
 +                                          /*            |v x (xi-xc)|^2 + eps */
 +
 +        psii = gmx_invsqrt(fac);          /*                 1                */
 +                                          /*  psii    = -------------         */
 +                                          /*            |v x (xi-xc)|         */
 +
 +        svmul(psii, v_xi_xc, si);          /*  si = psii * (v x (xi-xc) )     */
 +
 +        fac = iprod(v_xi_xc, ri);                   /* fac = (v x (xi-xc)).ri */
 +        fac2 = fac*fac;
 +
 +        siri = iprod(si, ri);                       /* siri = si.ri           */
 +
 +        svmul(psiistar/psii, ri, tmpvec);
 +        svmul(psiistar*psiistar/(psii*psii*psii) * siri, si, tmpvec2);
 +        rvec_dec(tmpvec, tmpvec2);
 +        cprod(tmpvec, rotg->vec, tmpvec2);
 +
 +        svmul(wi*siri, tmpvec2, tmpvec);
 +
 +        rvec_inc(sumvec, tmpvec);
 +    }
 +    svmul(rotg->k*erg->invmass, sumvec, innersumvec);
 +}
 +
 +
 +/* Calculate the radial motion 2 potential and forces */
 +static void do_radial_motion2(
 +        t_rotgrp  *rotg,        /* The rotation group                         */
 +        rvec      x[],          /* The positions                              */
 +        matrix    box,          /* The simulation box                         */
 +        double    t,            /* Time in picoseconds                        */
 +        gmx_large_int_t step,   /* The time step                              */
 +        gmx_bool  bOutstepRot,  /* Output to main rotation output file        */
 +        gmx_bool  bOutstepSlab) /* Output per-slab data                       */
 +{
 +    int       ii,iigrp,ifit,j;
 +    rvec      xj;              /* Position */
 +    real      alpha;           /* a single angle between an actual and a reference position */
 +    real      weight;          /* single weight for a single angle */
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +    rvec      xj_u;            /* xj - u */
 +    rvec      yj0_yc0;         /* yj0 -yc0 */
 +    rvec      tmpvec,tmpvec2;
 +    real      fac,fit_fac,fac2,Vpart=0.0;
 +    rvec      rj,fit_rj,sj;
 +    real      sjrj;
 +    rvec      v_xj_u;          /* v x (xj - u) */
 +    real      psij,psijstar;
 +    real      mj,wj;           /* For mass-weighting of the positions */
 +    real      N_M;             /* N/M */
 +    gmx_bool  bPF;
 +    rvec      innersumvec;
 +    gmx_bool  bCalcPotFit;
 +
 +
 +    erg=rotg->enfrotgrp;
 +
 +    bPF = rotg->eType==erotgRM2PF;
 +    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
 +
 +
 +    clear_rvec(yj0_yc0); /* Make the compiler happy */
 +
 +    clear_rvec(innersumvec);
 +    if (bPF)
 +    {
 +        /* For the pivot-free variant we have to use the current center of
 +         * mass of the rotation group instead of the pivot u */
 +        get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
 +
 +        /* Also, we precalculate the second term of the forces that is identical
 +         * (up to the weight factor mj) for all forces */
 +        radial_motion2_precalc_inner_sum(rotg,innersumvec);
 +    }
 +
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Each process calculates the forces on its local atoms */
 +    for (j=0; j<erg->nat_loc; j++)
 +    {
 +        if (bPF)
 +        {
 +            /* Local index of a rotation group atom  */
 +            ii = erg->ind_loc[j];
 +            /* Position of this atom in the collective array */
 +            iigrp = erg->xc_ref_ind[j];
 +            /* Mass-weighting */
 +            mj = erg->mc[iigrp];
 +
 +            /* Current position of this atom: x[ii] */
 +            copy_rvec(x[ii], xj);
 +
 +            /* Shift this atom such that it is near its reference */
 +            shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
 +
 +            /* The (unrotated) reference position is yj0. yc0 has already
 +             * been subtracted in init_rot_group */
 +            copy_rvec(rotg->x_ref[iigrp], yj0_yc0);   /* yj0_yc0 = yj0 - yc0  */
 +
 +            /* Calculate Omega.(yj0-yc0) */
 +            mvmul(erg->rotmat, yj0_yc0, rj);         /* rj = Omega.(yj0-yc0)  */
 +        }
 +        else
 +        {
 +            mj = erg->m_loc[j];
 +            copy_rvec(erg->x_loc_pbc[j], xj);
 +            copy_rvec(erg->xr_loc[j], rj);           /* rj = Omega.(yj0-u)    */
 +        }
 +        /* Mass-weighting */
 +        wj = N_M*mj;
 +
 +        /* Calculate (xj-u) resp. (xj-xc) */
 +        rvec_sub(xj, erg->xc_center, xj_u);          /* xj_u = xj-u           */
 +
 +        cprod(rotg->vec, xj_u, v_xj_u);              /* v_xj_u = v x (xj-u)   */
 +
 +        fac = norm2(v_xj_u);
 +                                          /*                      1           */
 +        psijstar = 1.0/(fac + rotg->eps); /*  psistar = --------------------  */
 +                                          /*            |v x (xj-u)|^2 + eps  */
 +
 +        psij = gmx_invsqrt(fac);          /*                 1                */
 +                                          /*  psij    = ------------          */
 +                                          /*            |v x (xj-u)|          */
 +
 +        svmul(psij, v_xj_u, sj);          /*  sj = psij * (v x (xj-u) )       */
 +
 +        fac = iprod(v_xj_u, rj);                     /* fac = (v x (xj-u)).rj */
 +        fac2 = fac*fac;
 +
 +        sjrj = iprod(sj, rj);                        /* sjrj = sj.rj          */
 +
 +        svmul(psijstar/psij, rj, tmpvec);
 +        svmul(psijstar*psijstar/(psij*psij*psij) * sjrj, sj, tmpvec2);
 +        rvec_dec(tmpvec, tmpvec2);
 +        cprod(tmpvec, rotg->vec, tmpvec2);
 +
 +        /* Store the additional force so that it can be added to the force
 +         * array after the normal forces have been evaluated */
 +        svmul(-rotg->k*wj*sjrj, tmpvec2, tmpvec);
 +        svmul(mj, innersumvec, tmpvec2);  /* This is != 0 only for the pivot-free variant */
 +
 +        rvec_add(tmpvec2, tmpvec, erg->f_rot_loc[j]);
 +        Vpart += wj*psijstar*fac2;
 +
 +        /* If requested, also calculate the potential for a set of angles
 +         * near the current reference angle */
 +        if (bCalcPotFit)
 +        {
 +            for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
 +            {
 +                if (bPF)
 +                {
 +                    mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, fit_rj); /* fit_rj = Omega.(yj0-yc0) */
 +                }
 +                else
 +                {
 +                    /* Position of this atom in the collective array */
 +                    iigrp = erg->xc_ref_ind[j];
 +                    /* Rotate with the alternative angle. Like rotate_local_reference(),
 +                     * just for a single local atom */
 +                    mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[iigrp], fit_rj); /* fit_rj = Omega*(yj0-u) */
 +                }
 +                fit_fac = iprod(v_xj_u, fit_rj); /* fac = (v x (xj-u)).fit_rj */
 +                /* Add to the rotation potential for this angle: */
 +                erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*psijstar*fit_fac*fit_fac;
 +            }
 +        }
 +
 +        if (bOutstepRot)
 +        {
 +            /* Add to the torque of this rotation group */
 +            erg->torque_v += torque(rotg->vec, erg->f_rot_loc[j], xj, erg->xc_center);
 +
 +            /* Calculate the angle between reference and actual rotation group atom. */
 +            angle(rotg, xj_u, rj, &alpha, &weight);  /* angle in rad, weighted */
 +            erg->angle_v  += alpha * weight;
 +            erg->weight_v += weight;
 +        }
 +
 +        PRINT_FORCE_J
 +
 +    } /* end of loop over local rotation group atoms */
 +    erg->V = 0.5*rotg->k*Vpart;
 +}
 +
 +
 +/* Determine the smallest and largest position vector (with respect to the 
 + * rotation vector) for the reference group */
 +static void get_firstlast_atom_ref(
 +        t_rotgrp  *rotg, 
 +        int       *firstindex, 
 +        int       *lastindex)
 +{
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +    int i;
 +    real xcproj;               /* The projection of a reference position on the 
 +                                  rotation vector */
 +    real minproj, maxproj;     /* Smallest and largest projection on v */
 +    
 +
 +    
 +    erg=rotg->enfrotgrp;
 +
 +    /* Start with some value */
 +    minproj = iprod(rotg->x_ref[0], rotg->vec);
 +    maxproj = minproj;
 +    
 +    /* This is just to ensure that it still works if all the atoms of the 
 +     * reference structure are situated in a plane perpendicular to the rotation 
 +     * vector */
 +    *firstindex = 0;
 +    *lastindex  = rotg->nat-1;
 +    
 +    /* Loop over all atoms of the reference group, 
 +     * project them on the rotation vector to find the extremes */
 +    for (i=0; i<rotg->nat; i++)
 +    {
 +        xcproj = iprod(rotg->x_ref[i], rotg->vec);
 +        if (xcproj < minproj)
 +        {
 +            minproj = xcproj;
 +            *firstindex = i;
 +        }
 +        if (xcproj > maxproj)
 +        {
 +            maxproj = xcproj;
 +            *lastindex = i;
 +        }
 +    }
 +}
 +
 +
 +/* Allocate memory for the slabs */
 +static void allocate_slabs(
 +        t_rotgrp  *rotg, 
 +        FILE      *fplog, 
 +        int       g, 
 +        gmx_bool  bVerbose)
 +{
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
 +    int i, nslabs;
 +    
 +    
 +    erg=rotg->enfrotgrp;
 +    
 +    /* More slabs than are defined for the reference are never needed */
 +    nslabs = erg->slab_last_ref - erg->slab_first_ref + 1;
 +    
 +    /* Remember how many we allocated */
 +    erg->nslabs_alloc = nslabs;
 +
 +    if ( (NULL != fplog) && bVerbose )
 +        fprintf(fplog, "%s allocating memory to store data for %d slabs (rotation group %d).\n",
 +                RotStr, nslabs,g);
 +    snew(erg->slab_center     , nslabs);
 +    snew(erg->slab_center_ref , nslabs);
 +    snew(erg->slab_weights    , nslabs);
 +    snew(erg->slab_torque_v   , nslabs);
 +    snew(erg->slab_data       , nslabs);
 +    snew(erg->gn_atom         , nslabs);
 +    snew(erg->gn_slabind      , nslabs);
 +    snew(erg->slab_innersumvec, nslabs);
 +    for (i=0; i<nslabs; i++)
 +    {
 +        snew(erg->slab_data[i].x     , rotg->nat);
 +        snew(erg->slab_data[i].ref   , rotg->nat);
 +        snew(erg->slab_data[i].weight, rotg->nat);
 +    }
 +    snew(erg->xc_ref_sorted, rotg->nat);
 +    snew(erg->xc_sortind   , rotg->nat);
 +    snew(erg->firstatom    , nslabs);
 +    snew(erg->lastatom     , nslabs);
 +}
 +
 +
 +/* From the extreme coordinates of the reference group, determine the first 
 + * and last slab of the reference. We can never have more slabs in the real
 + * simulation than calculated here for the reference.
 + */
 +static void get_firstlast_slab_ref(t_rotgrp *rotg, real mc[], int ref_firstindex, int ref_lastindex)
 +{
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
 +    int first,last,firststart;
 +    rvec dummy;
 +
 +    
 +    erg=rotg->enfrotgrp;
 +    first = get_first_slab(rotg, erg->max_beta, rotg->x_ref[ref_firstindex]);
 +    last  = get_last_slab( rotg, erg->max_beta, rotg->x_ref[ref_lastindex ]);
 +    firststart = first;
 +
 +    while (get_slab_weight(first, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
 +    {
 +        first--;
 +    }
 +    erg->slab_first_ref = first+1;
 +    while (get_slab_weight(last, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
 +    {
 +        last++;
 +    }
 +    erg->slab_last_ref  = last-1;
 +    
 +    erg->slab_buffer = firststart - erg->slab_first_ref;
 +}
 +
 +
++/* Special version of copy_rvec:
++ * During the copy procedure of xcurr to b, the correct PBC image is chosen
++ * such that the copied vector ends up near its reference position xref */
++static inline void copy_correct_pbc_image(
++        const rvec  xcurr,            /* copy vector xcurr ...                */
++        rvec        b,                /* ... to b ...                         */
++        const rvec  xref,   /* choosing the PBC image such that b ends up near xref */
++        matrix      box,
++        int         npbcdim)
++{
++    rvec  dx;
++    int   d,m;
++    ivec  shift;
++
++
++    /* Shortest PBC distance between the atom and its reference */
++    rvec_sub(xcurr, xref, dx);
++
++    /* Determine the shift for this atom */
++    clear_ivec(shift);
++    for(m=npbcdim-1; m>=0; m--)
++    {
++        while (dx[m] < -0.5*box[m][m])
++        {
++            for(d=0; d<DIM; d++)
++                dx[d] += box[m][d];
++            shift[m]++;
++        }
++        while (dx[m] >= 0.5*box[m][m])
++        {
++            for(d=0; d<DIM; d++)
++                dx[d] -= box[m][d];
++            shift[m]--;
++        }
++    }
++
++    /* Apply the shift to the position */
++    copy_rvec(xcurr, b);
++    shift_single_coord(box, b, shift);
++}
++
 +
 +static void init_rot_group(FILE *fplog,t_commrec *cr,int g,t_rotgrp *rotg,
-                 copy_rvec(x[ii], erg->xc_old[i]);
++        rvec *x,gmx_mtop_t *mtop,gmx_bool bVerbose,FILE *out_slabs, matrix box,
++        gmx_bool bOutputCenters)
 +{
 +    int i,ii;
 +    rvec        coord,*xdum;
 +    gmx_bool    bFlex,bColl;
 +    t_atom      *atom;
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
 +    int         ref_firstindex, ref_lastindex;
 +    real        mass,totalmass;
 +    real        start=0.0;
 +    
 +
 +    /* Do we have a flexible axis? */
 +    bFlex = ISFLEX(rotg);
 +    /* Do we use a global set of coordinates? */
 +    bColl = ISCOLL(rotg);
 +
 +    erg=rotg->enfrotgrp;
 +    
 +    /* Allocate space for collective coordinates if needed */
 +    if (bColl)
 +    {
 +        snew(erg->xc        , rotg->nat);
 +        snew(erg->xc_shifts , rotg->nat);
 +        snew(erg->xc_eshifts, rotg->nat);
 +
 +        /* Save the original (whole) set of positions such that later the
 +         * molecule can always be made whole again */
 +        snew(erg->xc_old    , rotg->nat);        
 +        if (MASTER(cr))
 +        {
 +            for (i=0; i<rotg->nat; i++)
 +            {
 +                ii = rotg->ind[i];
-             init_rot_group(fplog,cr,g,rotg,x_pbc,mtop,bVerbose,er->out_slabs,
++                copy_correct_pbc_image(x[ii], erg->xc_old[i],rotg->x_ref[i],box,3);
 +            }
 +        }
 +#ifdef GMX_MPI
 +        if (PAR(cr))
 +            gmx_bcast(rotg->nat*sizeof(erg->xc_old[0]),erg->xc_old, cr);
 +#endif
 +
 +        if (rotg->eFittype == erotgFitNORM)
 +        {
 +            snew(erg->xc_ref_length, rotg->nat); /* in case fit type NORM is chosen */
 +            snew(erg->xc_norm      , rotg->nat);
 +        }
 +    }
 +    else
 +    {
 +        snew(erg->xr_loc   , rotg->nat);
 +        snew(erg->x_loc_pbc, rotg->nat);
 +    }
 +    
 +    snew(erg->f_rot_loc , rotg->nat);
 +    snew(erg->xc_ref_ind, rotg->nat);
 +    
 +    /* Make space for the calculation of the potential at other angles (used
 +     * for fitting only) */
 +    if (erotgFitPOT == rotg->eFittype)
 +    {
 +        snew(erg->PotAngleFit, 1);
 +        snew(erg->PotAngleFit->degangle, rotg->PotAngle_nstep);
 +        snew(erg->PotAngleFit->V       , rotg->PotAngle_nstep);
 +        snew(erg->PotAngleFit->rotmat  , rotg->PotAngle_nstep);
 +
 +        /* Get the set of angles around the reference angle */
 +        start = -0.5 * (rotg->PotAngle_nstep - 1)*rotg->PotAngle_step;
 +        for (i = 0; i < rotg->PotAngle_nstep; i++)
 +            erg->PotAngleFit->degangle[i] = start + i*rotg->PotAngle_step;
 +    }
 +    else
 +    {
 +        erg->PotAngleFit = NULL;
 +    }
 +
 +    /* xc_ref_ind needs to be set to identity in the serial case */
 +    if (!PAR(cr))
 +        for (i=0; i<rotg->nat; i++)
 +            erg->xc_ref_ind[i] = i;
 +
 +    /* Copy the masses so that the center can be determined. For all types of
 +     * enforced rotation, we store the masses in the erg->mc array. */
 +    snew(erg->mc, rotg->nat);
 +    if (bFlex)
 +        snew(erg->mc_sorted, rotg->nat);
 +    if (!bColl)
 +        snew(erg->m_loc, rotg->nat);
 +    totalmass=0.0;
 +    for (i=0; i<rotg->nat; i++)
 +    {
 +        if (rotg->bMassW)
 +        {
 +            gmx_mtop_atomnr_to_atom(mtop,rotg->ind[i],&atom);
 +            mass=atom->m;
 +        }
 +        else
 +        {
 +            mass=1.0;
 +        }
 +        erg->mc[i] = mass;
 +        totalmass += mass;
 +    }
 +    erg->invmass = 1.0/totalmass;
 +    
 +    /* Set xc_ref_center for any rotation potential */
 +    if ((rotg->eType==erotgISO) || (rotg->eType==erotgPM) || (rotg->eType==erotgRM) || (rotg->eType==erotgRM2))
 +    {
 +        /* Set the pivot point for the fixed, stationary-axis potentials. This
 +         * won't change during the simulation */
 +        copy_rvec(rotg->pivot, erg->xc_ref_center);
 +        copy_rvec(rotg->pivot, erg->xc_center    );
 +    }
 +    else
 +    {
 +        /* Center of the reference positions */
 +        get_center(rotg->x_ref, erg->mc, rotg->nat, erg->xc_ref_center);
 +
 +        /* Center of the actual positions */
 +        if (MASTER(cr))
 +        {
 +            snew(xdum, rotg->nat);
 +            for (i=0; i<rotg->nat; i++)
 +            {
 +                ii = rotg->ind[i];
 +                copy_rvec(x[ii], xdum[i]);
 +            }
 +            get_center(xdum, erg->mc, rotg->nat, erg->xc_center);
 +            sfree(xdum);
 +        }
 +#ifdef GMX_MPI
 +        if (PAR(cr))
 +            gmx_bcast(sizeof(erg->xc_center), erg->xc_center, cr);
 +#endif
 +    }
 +
 +    if ( (rotg->eType != erotgFLEX) && (rotg->eType != erotgFLEX2) )
 +    {
 +        /* Put the reference positions into origin: */
 +        for (i=0; i<rotg->nat; i++)
 +            rvec_dec(rotg->x_ref[i], erg->xc_ref_center);
 +    }
 +
 +    /* Enforced rotation with flexible axis */
 +    if (bFlex)
 +    {
 +        /* Calculate maximum beta value from minimum gaussian (performance opt.) */
 +        erg->max_beta = calc_beta_max(rotg->min_gaussian, rotg->slab_dist);
 +
 +        /* Determine the smallest and largest coordinate with respect to the rotation vector */
 +        get_firstlast_atom_ref(rotg, &ref_firstindex, &ref_lastindex);
 +        
 +        /* From the extreme coordinates of the reference group, determine the first 
 +         * and last slab of the reference. */
 +        get_firstlast_slab_ref(rotg, erg->mc, ref_firstindex, ref_lastindex);
 +                
 +        /* Allocate memory for the slabs */
 +        allocate_slabs(rotg, fplog, g, bVerbose);
 +
 +        /* Flexible rotation: determine the reference centers for the rest of the simulation */
 +        erg->slab_first = erg->slab_first_ref;
 +        erg->slab_last = erg->slab_last_ref;
 +        get_slab_centers(rotg,rotg->x_ref,erg->mc,g,-1,out_slabs,bOutputCenters,TRUE);
 +
 +        /* Length of each x_rotref vector from center (needed if fit routine NORM is chosen): */
 +        if (rotg->eFittype == erotgFitNORM)
 +        {
 +            for (i=0; i<rotg->nat; i++)
 +            {
 +                rvec_sub(rotg->x_ref[i], erg->xc_ref_center, coord);
 +                erg->xc_ref_length[i] = norm(coord);
 +            }
 +        }
 +    }
 +}
 +
 +
 +extern void dd_make_local_rotation_groups(gmx_domdec_t *dd,t_rot *rot)
 +{
 +    gmx_ga2la_t ga2la;
 +    int g;
 +    t_rotgrp *rotg;
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
 +    
 +    ga2la = dd->ga2la;
 +
 +    for(g=0; g<rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        erg  = rotg->enfrotgrp;
 +
 +
 +        dd_make_local_group_indices(ga2la,rotg->nat,rotg->ind,
 +                &erg->nat_loc,&erg->ind_loc,&erg->nalloc_loc,erg->xc_ref_ind);
 +    }
 +}
 +
 +
 +/* Calculate the size of the MPI buffer needed in reduce_output() */
 +static int calc_mpi_bufsize(t_rot *rot)
 +{
 +    int g;
 +    int count_group, count_total;
 +    t_rotgrp *rotg;
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
 +
 +
 +    count_total = 0;
 +    for (g=0; g<rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        erg  = rotg->enfrotgrp;
 +
 +        /* Count the items that are transferred for this group: */
 +        count_group = 4; /* V, torque, angle, weight */
 +
 +        /* Add the maximum number of slabs for flexible groups */
 +        if (ISFLEX(rotg))
 +            count_group += erg->slab_last_ref - erg->slab_first_ref + 1;
 +
 +        /* Add space for the potentials at different angles: */
 +        if (erotgFitPOT == rotg->eFittype)
 +            count_group += rotg->PotAngle_nstep;
 +
 +        /* Add to the total number: */
 +        count_total += count_group;
 +    }
 +
 +    return count_total;
 +}
 +
 +
 +extern void init_rot(FILE *fplog,t_inputrec *ir,int nfile,const t_filenm fnm[],
 +        t_commrec *cr, rvec *x, matrix box, gmx_mtop_t *mtop, const output_env_t oenv,
 +        gmx_bool bVerbose, unsigned long Flags)
 +{
 +    t_rot    *rot;
 +    t_rotgrp *rotg;
 +    int      g;
 +    int      nat_max=0;     /* Size of biggest rotation group */
 +    gmx_enfrot_t er;        /* Pointer to the enforced rotation buffer variables */    
 +    gmx_enfrotgrp_t erg;    /* Pointer to enforced rotation group data */
 +    rvec     *x_pbc=NULL;   /* Space for the pbc-correct atom positions */
 +
 +
 +    if ( (PAR(cr)) && !DOMAINDECOMP(cr) )
 +        gmx_fatal(FARGS, "Enforced rotation is only implemented for domain decomposition!");
 +
 +    if ( MASTER(cr) && bVerbose)
 +        fprintf(stdout, "%s Initializing ...\n", RotStr);
 +
 +    rot = ir->rot;
 +    snew(rot->enfrot, 1);
 +    er = rot->enfrot;
 +    er->Flags = Flags;
 +
 +    /* When appending, skip first output to avoid duplicate entries in the data files */
 +    if (er->Flags & MD_APPENDFILES)
 +        er->bOut = FALSE;
 +    else
 +        er->bOut = TRUE;
 +
 +    if ( MASTER(cr) && er->bOut )
 +        please_cite(fplog, "Kutzner2011");
 +
 +    /* Output every step for reruns */
 +    if (er->Flags & MD_RERUN)
 +    {
 +        if (NULL != fplog)
 +            fprintf(fplog, "%s rerun - will write rotation output every available step.\n", RotStr);
 +        rot->nstrout = 1;
 +        rot->nstsout = 1;
 +    }
 +
 +    er->out_slabs = NULL;
 +    if ( MASTER(cr) && HaveFlexibleGroups(rot) )
 +        er->out_slabs = open_slab_out(opt2fn("-rs",nfile,fnm), rot, oenv);
 +
 +    if (MASTER(cr))
 +    {
 +        /* Remove pbc, make molecule whole.
 +         * When ir->bContinuation=TRUE this has already been done, but ok. */
 +        snew(x_pbc,mtop->natoms);
 +        m_rveccopy(mtop->natoms,x,x_pbc);
 +        do_pbc_first_mtop(NULL,ir->ePBC,box,mtop,x_pbc);
++        /* All molecules will be whole now, but not necessarily in the home box.
++         * Additionally, if a rotation group consists of more than one molecule
++         * (e.g. two strands of DNA), each one of them can end up in a different
++         * periodic box. This is taken care of in init_rot_group.  */
 +    }
 +
 +    for (g=0; g<rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +
 +        if (NULL != fplog)
 +            fprintf(fplog,"%s group %d type '%s'\n", RotStr, g, erotg_names[rotg->eType]);
 +        
 +        if (rotg->nat > 0)
 +        {
 +            /* Allocate space for the rotation group's data: */
 +            snew(rotg->enfrotgrp, 1);
 +            erg  = rotg->enfrotgrp;
 +
 +            nat_max=max(nat_max, rotg->nat);
 +            
 +            if (PAR(cr))
 +            {
 +                erg->nat_loc    = 0;
 +                erg->nalloc_loc = 0;
 +                erg->ind_loc    = NULL;
 +            }
 +            else
 +            {
 +                erg->nat_loc = rotg->nat;
 +                erg->ind_loc = rotg->ind;
 +            }
-     int d,i,ii,m;
++            init_rot_group(fplog,cr,g,rotg,x_pbc,mtop,bVerbose,er->out_slabs,box,
 +                           !(er->Flags & MD_APPENDFILES) ); /* Do not output the reference centers
 +                                                             * again if we are appending */
 +        }
 +    }
 +    
 +    /* Allocate space for enforced rotation buffer variables */
 +    er->bufsize = nat_max;
 +    snew(er->data, nat_max);
 +    snew(er->xbuf, nat_max);
 +    snew(er->mbuf, nat_max);
 +
 +    /* Buffers for MPI reducing torques, angles, weights (for each group), and V */
 +    if (PAR(cr))
 +    {
 +        er->mpi_bufsize = calc_mpi_bufsize(rot) + 100; /* larger to catch errors */
 +        snew(er->mpi_inbuf , er->mpi_bufsize);
 +        snew(er->mpi_outbuf, er->mpi_bufsize);
 +    }
 +    else
 +    {
 +        er->mpi_bufsize = 0;
 +        er->mpi_inbuf = NULL;
 +        er->mpi_outbuf = NULL;
 +    }
 +
 +    /* Only do I/O on the MASTER */
 +    er->out_angles  = NULL;
 +    er->out_rot     = NULL;
 +    er->out_torque  = NULL;
 +    if (MASTER(cr))
 +    {
 +        er->out_rot = open_rot_out(opt2fn("-ro",nfile,fnm), rot, oenv);
 +
 +        if (rot->nstsout > 0)
 +        {
 +            if ( HaveFlexibleGroups(rot) || HavePotFitGroups(rot) )
 +                er->out_angles  = open_angles_out(opt2fn("-ra",nfile,fnm), rot, oenv);
 +            if ( HaveFlexibleGroups(rot) )
 +                er->out_torque  = open_torque_out(opt2fn("-rt",nfile,fnm), rot, oenv);
 +        }
 +
 +        sfree(x_pbc);
 +    }
 +}
 +
 +
 +extern void finish_rot(FILE *fplog,t_rot *rot)
 +{
 +    gmx_enfrot_t er;        /* Pointer to the enforced rotation buffer variables */    
 +
 +    
 +    er=rot->enfrot;
 +    if (er->out_rot)
 +        gmx_fio_fclose(er->out_rot);
 +    if (er->out_slabs)
 +        gmx_fio_fclose(er->out_slabs);
 +    if (er->out_angles)
 +        gmx_fio_fclose(er->out_angles);
 +    if (er->out_torque)
 +        gmx_fio_fclose(er->out_torque);
 +}
 +
 +
 +/* Rotate the local reference positions and store them in
 + * erg->xr_loc[0...(nat_loc-1)]
 + *
 + * Note that we already subtracted u or y_c from the reference positions
 + * in init_rot_group().
 + */
 +static void rotate_local_reference(t_rotgrp *rotg)
 +{
 +    gmx_enfrotgrp_t erg;
 +    int i,ii;
 +
 +    
 +    erg=rotg->enfrotgrp;
 +    
 +    for (i=0; i<erg->nat_loc; i++)
 +    {
 +        /* Index of this rotation group atom with respect to the whole rotation group */
 +        ii = erg->xc_ref_ind[i];
 +        /* Rotate */
 +        mvmul(erg->rotmat, rotg->x_ref[ii], erg->xr_loc[i]);
 +    }
 +}
 +
 +
 +/* Select the PBC representation for each local x position and store that
 + * for later usage. We assume the right PBC image of an x is the one nearest to
 + * its rotated reference */
 +static void choose_pbc_image(rvec x[], t_rotgrp *rotg, matrix box, int npbcdim)
 +{
-     rvec xref,xcurr,dx;
-     ivec shift;
++    int i,ii;
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
-     
++    rvec xref;
 +
 +
 +    erg=rotg->enfrotgrp;
-         clear_ivec(shift);
-         
++
 +    for (i=0; i<erg->nat_loc; i++)
 +    {
-         
-         /* Subtract the (old) center from the current positions
-          * (just to determine the shifts!) */
-         rvec_sub(x[ii], erg->xc_center, xcurr);
-         
-         /* Shortest PBC distance between the atom and its reference */
-         rvec_sub(xcurr, xref, dx);
-         
-         /* Determine the shift for this atom */
-         for(m=npbcdim-1; m>=0; m--)
-         {
-             while (dx[m] < -0.5*box[m][m])
-             {
-                 for(d=0; d<DIM; d++)
-                     dx[d] += box[m][d];
-                 shift[m]++;
-             }
-             while (dx[m] >= 0.5*box[m][m])
-             {
-                 for(d=0; d<DIM; d++)
-                     dx[d] -= box[m][d];
-                 shift[m]--;
-             }
-         }
-         
-         /* Apply the shift to the current atom */
-         copy_rvec(x[ii], erg->x_loc_pbc[i]);
-         shift_single_coord(box, erg->x_loc_pbc[i], shift); 
 +        /* Index of a rotation group atom  */
 +        ii = erg->ind_loc[i];
 +
 +        /* Get the reference position. The pivot was already
 +         * subtracted in init_rot_group() from the reference positions. Also,
 +         * the reference positions have already been rotated in
 +         * rotate_local_reference() */
 +        copy_rvec(erg->xr_loc[i], xref);
++
++        copy_correct_pbc_image(x[ii],erg->x_loc_pbc[i], xref, box, npbcdim);
 +    }
 +}
 +
 +
 +extern void do_rotation(
 +        t_commrec *cr,
 +        t_inputrec *ir,
 +        matrix box,
 +        rvec x[],
 +        real t,
 +        gmx_large_int_t step,
 +        gmx_wallcycle_t wcycle,
 +        gmx_bool bNS)
 +{
 +    int      g,i,ii;
 +    t_rot    *rot;
 +    t_rotgrp *rotg;
 +    gmx_bool outstep_slab, outstep_rot;
 +    gmx_bool bFlex,bColl;
 +    gmx_enfrot_t er;     /* Pointer to the enforced rotation buffer variables */
 +    gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data           */
 +    rvec     transvec;
 +    t_gmx_potfit *fit=NULL;     /* For fit type 'potential' determine the fit
 +                                   angle via the potential minimum            */
 +
 +    /* Enforced rotation cycle counting: */
 +    gmx_cycles_t cycles_comp;   /* Cycles for the enf. rotation computation
 +                                   only, does not count communication. This
 +                                   counter is used for load-balancing         */
 +
 +#ifdef TAKETIME
 +    double t0;
 +#endif
 +    
 +    rot=ir->rot;
 +    er=rot->enfrot;
 +    
 +    /* When to output in main rotation output file */
 +    outstep_rot  = do_per_step(step, rot->nstrout) && er->bOut;
 +    /* When to output per-slab data */
 +    outstep_slab = do_per_step(step, rot->nstsout) && er->bOut;
 +
 +    /* Output time into rotation output file */
 +    if (outstep_rot && MASTER(cr))
 +        fprintf(er->out_rot, "%12.3e",t);
 +
 +    /**************************************************************************/
 +    /* First do ALL the communication! */
 +    for(g=0; g<rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        erg=rotg->enfrotgrp;
 +
 +        /* Do we have a flexible axis? */
 +        bFlex = ISFLEX(rotg);
 +        /* Do we use a collective (global) set of coordinates? */
 +        bColl = ISCOLL(rotg);
 +
 +        /* Calculate the rotation matrix for this angle: */
 +        erg->degangle = rotg->rate * t;
 +        calc_rotmat(rotg->vec,erg->degangle,erg->rotmat);
 +
 +        if (bColl)
 +        {
 +            /* Transfer the rotation group's positions such that every node has
 +             * all of them. Every node contributes its local positions x and stores
 +             * it in the collective erg->xc array. */
 +            communicate_group_positions(cr,erg->xc, erg->xc_shifts, erg->xc_eshifts, bNS,
 +                    x, rotg->nat, erg->nat_loc, erg->ind_loc, erg->xc_ref_ind, erg->xc_old, box);
 +        }
 +        else
 +        {
 +            /* Fill the local masses array;
 +             * this array changes in DD/neighborsearching steps */
 +            if (bNS)
 +            {
 +                for (i=0; i<erg->nat_loc; i++)
 +                {
 +                    /* Index of local atom w.r.t. the collective rotation group */
 +                    ii = erg->xc_ref_ind[i];
 +                    erg->m_loc[i] = erg->mc[ii];
 +                }
 +            }
 +
 +            /* Calculate Omega*(y_i-y_c) for the local positions */
 +            rotate_local_reference(rotg);
 +
 +            /* Choose the nearest PBC images of the group atoms with respect
 +             * to the rotated reference positions */
 +            choose_pbc_image(x, rotg, box, 3);
 +
 +            /* Get the center of the rotation group */
 +            if ( (rotg->eType==erotgISOPF) || (rotg->eType==erotgPMPF) )
 +                get_center_comm(cr, erg->x_loc_pbc, erg->m_loc, erg->nat_loc, rotg->nat, erg->xc_center);
 +        }
 +
 +    } /* End of loop over rotation groups */
 +
 +    /**************************************************************************/
 +    /* Done communicating, we can start to count cycles for the load balancing now ... */
 +    cycles_comp = gmx_cycles_read();
 +
 +
 +#ifdef TAKETIME
 +    t0 = MPI_Wtime();
 +#endif
 +
 +    for(g=0; g<rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        erg=rotg->enfrotgrp;
 +
 +        bFlex = ISFLEX(rotg);
 +        bColl = ISCOLL(rotg);
 +
 +        if (outstep_rot && MASTER(cr))
 +            fprintf(er->out_rot, "%12.4f", erg->degangle);
 +
 +        /* Calculate angles and rotation matrices for potential fitting: */
 +        if ( (outstep_rot || outstep_slab) && (erotgFitPOT == rotg->eFittype) )
 +        {
 +            fit = erg->PotAngleFit;
 +            for (i = 0; i < rotg->PotAngle_nstep; i++)
 +            {
 +                calc_rotmat(rotg->vec, erg->degangle + fit->degangle[i], fit->rotmat[i]);
 +
 +                /* Clear value from last step */
 +                erg->PotAngleFit->V[i] = 0.0;
 +            }
 +        }
 +
 +        /* Clear values from last time step */
 +        erg->V        = 0.0;
 +        erg->torque_v = 0.0;
 +        erg->angle_v  = 0.0;
 +        erg->weight_v = 0.0;
 +
 +        switch(rotg->eType)
 +        {
 +            case erotgISO:
 +            case erotgISOPF:
 +            case erotgPM:
 +            case erotgPMPF:
 +                do_fixed(rotg,x,box,t,step,outstep_rot,outstep_slab);
 +                break;
 +            case erotgRM:
 +                do_radial_motion(rotg,x,box,t,step,outstep_rot,outstep_slab);
 +                break;
 +            case erotgRMPF:
 +                do_radial_motion_pf(rotg,x,box,t,step,outstep_rot,outstep_slab);
 +                break;
 +            case erotgRM2:
 +            case erotgRM2PF:
 +                do_radial_motion2(rotg,x,box,t,step,outstep_rot,outstep_slab);
 +                break;
 +            case erotgFLEXT:
 +            case erotgFLEX2T:
 +                /* Subtract the center of the rotation group from the collective positions array
 +                 * Also store the center in erg->xc_center since it needs to be subtracted
 +                 * in the low level routines from the local coordinates as well */
 +                get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
 +                svmul(-1.0, erg->xc_center, transvec);
 +                translate_x(erg->xc, rotg->nat, transvec);
 +                do_flexible(MASTER(cr),er,rotg,g,x,box,t,step,outstep_rot,outstep_slab);
 +                break;
 +            case erotgFLEX:
 +            case erotgFLEX2:
 +                /* Do NOT subtract the center of mass in the low level routines! */
 +                clear_rvec(erg->xc_center);
 +                do_flexible(MASTER(cr),er,rotg,g,x,box,t,step,outstep_rot,outstep_slab);
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "No such rotation potential.");
 +                break;
 +        }
 +    }
 +
 +#ifdef TAKETIME
 +    if (MASTER(cr))
 +        fprintf(stderr, "%s calculation (step %d) took %g seconds.\n", RotStr, step, MPI_Wtime()-t0);
 +#endif
 +
 +    /* Stop the enforced rotation cycle counter and add the computation-only
 +     * cycles to the force cycles for load balancing */
 +    cycles_comp  = gmx_cycles_read() - cycles_comp;
 +
 +    if (DOMAINDECOMP(cr) && wcycle)
 +        dd_cycles_add(cr->dd,cycles_comp,ddCyclF);
 +}
Simple merge
index f836e518c020ac2c1e62fd46b1c9f0197c65e2e1,0000000000000000000000000000000000000000..03310b2cd46dd7c027cb17922091975e3d7d91a6
mode 100644,000000..100644
--- /dev/null
@@@ -1,1050 -1,0 +1,1050 @@@
-   gmx_bool    bPredict;        /* Predict shell positions                  */
-   gmx_bool    bForceInit;      /* Force initialization of shell positions  */
 +/*
 + * 
 + *                This source code is part of
 + * 
 + *                 G   R   O   M   A   C   S
 + * 
 + *          GROningen MAchine for Chemical Simulations
 + * 
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2008, 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:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <string.h>
 +#include "typedefs.h"
 +#include "smalloc.h"
 +#include "gmx_fatal.h"
 +#include "vec.h"
 +#include "txtdump.h"
 +#include "mdrun.h"
 +#include "partdec.h"
 +#include "mdatoms.h"
 +#include "vsite.h"
 +#include "network.h"
 +#include "names.h"
 +#include "constr.h"
 +#include "domdec.h"
 +#include "partdec.h"
 +#include "physics.h"
 +#include "copyrite.h"
 +#include "shellfc.h"
 +#include "mtop_util.h"
 +#include "chargegroup.h"
 +#include "macros.h"
 +
 +
 +typedef struct {
 +  int     nnucl;
 +  atom_id shell;              /* The shell id                         */
 +  atom_id nucl1,nucl2,nucl3;  /* The nuclei connected to the shell    */
 +  /* gmx_bool    bInterCG; */       /* Coupled to nuclei outside cg?        */
 +  real    k;                  /* force constant                       */
 +  real    k_1;                        /* 1 over force constant                */
 +  rvec    xold;
 +  rvec    fold;
 +  rvec    step;
 +} t_shell;
 +
 +typedef struct gmx_shellfc {
 +  int     nshell_gl;       /* The number of shells in the system       */
 +  t_shell *shell_gl;       /* All the shells (for DD only)             */
 +  int     *shell_index_gl; /* Global shell index (for DD only)         */
 +  gmx_bool    bInterCG;        /* Are there inter charge-group shells?     */
 +  int     nshell;          /* The number of local shells               */
 +  t_shell *shell;          /* The local shells                         */
 +  int     shell_nalloc;    /* The allocation size of shell             */
-   shfc->bForceInit = FALSE;
++  gmx_bool bPredict;       /* Predict shell positions                  */
++  gmx_bool bRequireInit;   /* Require initialization of shell positions  */
 +  int     nflexcon;        /* The number of flexible constraints       */
 +  rvec    *x[2];           /* Array for iterative minimization         */
 +  rvec    *f[2];           /* Array for iterative minimization         */
 +  int     x_nalloc;        /* The allocation size of x and f           */
 +  rvec    *acc_dir;        /* Acceleration direction for flexcon       */
 +  rvec    *x_old;          /* Old coordinates for flexcon              */
 +  int     flex_nalloc;     /* The allocation size of acc_dir and x_old */
 +  rvec    *adir_xnold;     /* Work space for init_adir                 */
 +  rvec    *adir_xnew;      /* Work space for init_adir                 */
 +  int     adir_nalloc;     /* Work space for init_adir                 */
 +} t_gmx_shellfc;
 +
 +      
 +static void pr_shell(FILE *fplog,int ns,t_shell s[])
 +{
 +  int i;
 +  
 +  fprintf(fplog,"SHELL DATA\n");
 +  fprintf(fplog,"%5s  %8s  %5s  %5s  %5s\n",
 +        "Shell","Force k","Nucl1","Nucl2","Nucl3");
 +  for(i=0; (i<ns); i++) {
 +    fprintf(fplog,"%5d  %8.3f  %5d",s[i].shell,1.0/s[i].k_1,s[i].nucl1);
 +    if (s[i].nnucl == 2)
 +      fprintf(fplog,"  %5d\n",s[i].nucl2);
 +    else if (s[i].nnucl == 3)
 +      fprintf(fplog,"  %5d  %5d\n",s[i].nucl2,s[i].nucl3);
 +    else
 +      fprintf(fplog,"\n");
 +  }
 +}
 +
 +static void predict_shells(FILE *fplog,rvec x[],rvec v[],real dt,
 +                         int ns,t_shell s[],
 +                         real mass[],gmx_mtop_t *mtop,gmx_bool bInit)
 +{
 +  int  i,m,s1,n1,n2,n3;
 +  real dt_1,dt_2,dt_3,fudge,tm,m1,m2,m3;
 +  rvec *ptr;
 +  t_atom *atom;
 +  
 +  /* We introduce a fudge factor for performance reasons: with this choice
 +   * the initial force on the shells is about a factor of two lower than 
 +   * without
 +   */
 +  fudge = 1.0;
 +    
 +  if (bInit) {
 +    if (fplog)
 +      fprintf(fplog,"RELAX: Using prediction for initial shell placement\n");
 +    ptr  = x;
 +    dt_1 = 1;
 +  }
 +  else {
 +    ptr  = v;
 +    dt_1 = fudge*dt;
 +  }
 +    
 +  for(i=0; (i<ns); i++) {
 +    s1 = s[i].shell;
 +    if (bInit)
 +      clear_rvec(x[s1]);
 +    switch (s[i].nnucl) {
 +    case 1:
 +      n1 = s[i].nucl1;
 +      for(m=0; (m<DIM); m++)
 +      x[s1][m]+=ptr[n1][m]*dt_1;
 +      break;
 +    case 2:
 +      n1 = s[i].nucl1;
 +      n2 = s[i].nucl2;
 +      if (mass) {
 +      m1 = mass[n1];
 +      m2 = mass[n2];
 +      } else {
 +      /* Not the correct masses with FE, but it is just a prediction... */
 +      m1 = atom[n1].m;
 +      m2 = atom[n2].m;
 +      }
 +      tm = dt_1/(m1+m2);
 +      for(m=0; (m<DIM); m++)
 +      x[s1][m]+=(m1*ptr[n1][m]+m2*ptr[n2][m])*tm;
 +      break;
 +    case 3:
 +      n1 = s[i].nucl1;
 +      n2 = s[i].nucl2;
 +      n3 = s[i].nucl3;
 +      if (mass) {
 +      m1 = mass[n1];
 +      m2 = mass[n2];
 +      m3 = mass[n3];
 +      } else {
 +      /* Not the correct masses with FE, but it is just a prediction... */
 +      gmx_mtop_atomnr_to_atom(mtop,n1,&atom);
 +      m1 = atom->m;
 +      gmx_mtop_atomnr_to_atom(mtop,n2,&atom);
 +      m2 = atom->m;
 +      gmx_mtop_atomnr_to_atom(mtop,n3,&atom);
 +      m3 = atom->m;
 +      }
 +      tm = dt_1/(m1+m2+m3);
 +      for(m=0; (m<DIM); m++)
 +      x[s1][m]+=(m1*ptr[n1][m]+m2*ptr[n2][m]+m3*ptr[n3][m])*tm;
 +      break;
 +    default:
 +      gmx_fatal(FARGS,"Shell %d has %d nuclei!",i,s[i].nnucl);
 +    }
 +  }
 +}
 +
 +gmx_shellfc_t init_shell_flexcon(FILE *fplog,
 +                               gmx_mtop_t *mtop,int nflexcon,
 +                               rvec *x)
 +{
 +  struct gmx_shellfc *shfc;
 +  t_shell     *shell;
 +  int         *shell_index=NULL,*at2cg;
 +  t_atom      *atom;
 +  int         n[eptNR],ns,nshell,nsi;
 +  int         i,j,nmol,type,mb,mt,a_offset,cg,mol,ftype,nra;
 +  real        qS,alpha;
 +  int         aS,aN=0; /* Shell and nucleus */
 +  int         bondtypes[] = { F_BONDS, F_HARMONIC, F_CUBICBONDS, F_POLARIZATION, F_ANHARM_POL, F_WATER_POL };
 +#define NBT asize(bondtypes)
 +  t_iatom     *ia;
 +  gmx_mtop_atomloop_block_t aloopb;
 +  gmx_mtop_atomloop_all_t aloop;
 +  gmx_ffparams_t *ffparams;
 +  gmx_molblock_t *molb;
 +  gmx_moltype_t *molt;
 +  t_block     *cgs;
 +
 +  /* Count number of shells, and find their indices */
 +  for(i=0; (i<eptNR); i++) {
 +    n[i] = 0;
 +  }
 +
 +  aloopb = gmx_mtop_atomloop_block_init(mtop);
 +  while (gmx_mtop_atomloop_block_next(aloopb,&atom,&nmol)) {
 +    n[atom->ptype] += nmol;
 +  }
 +
 +  if (fplog) {
 +    /* Print the number of each particle type */  
 +    for(i=0; (i<eptNR); i++) {
 +      if (n[i] != 0) {
 +      fprintf(fplog,"There are: %d %ss\n",n[i],ptype_str[i]);
 +      }
 +    }
 +  }
 +
 +  nshell = n[eptShell];
 +  
 +  if (nshell == 0 && nflexcon == 0) {
 +    return NULL;
 +  }
 +
 +  snew(shfc,1);
 +  shfc->nflexcon = nflexcon;
 +
 +  if (nshell == 0) {
 +    return shfc;
 +  }
 +
 +  /* We have shells: fill the shell data structure */
 +
 +  /* Global system sized array, this should be avoided */
 +  snew(shell_index,mtop->natoms);
 +
 +  aloop = gmx_mtop_atomloop_all_init(mtop);
 +  nshell = 0;
 +  while (gmx_mtop_atomloop_all_next(aloop,&i,&atom)) {
 +    if (atom->ptype == eptShell) {
 +      shell_index[i] = nshell++;
 +    }
 +  }
 +
 +  snew(shell,nshell);
 +  
 +  /* Initiate the shell structures */    
 +  for(i=0; (i<nshell); i++) {
 +    shell[i].shell = NO_ATID;
 +    shell[i].nnucl = 0;
 +    shell[i].nucl1 = NO_ATID;
 +    shell[i].nucl2 = NO_ATID;
 +    shell[i].nucl3 = NO_ATID;
 +    /* shell[i].bInterCG=FALSE; */
 +    shell[i].k_1   = 0;
 +    shell[i].k     = 0;
 +  }
 +
 +  ffparams = &mtop->ffparams;
 +
 +  /* Now fill the structures */
 +  shfc->bInterCG = FALSE;
 +  ns = 0;
 +  a_offset = 0;
 +  for(mb=0; mb<mtop->nmolblock; mb++) {
 +    molb = &mtop->molblock[mb];
 +    molt = &mtop->moltype[molb->type];
 +
 +    cgs = &molt->cgs;
 +    snew(at2cg,molt->atoms.nr);
 +    for(cg=0; cg<cgs->nr; cg++) {
 +      for(i=cgs->index[cg]; i<cgs->index[cg+1]; i++) {
 +      at2cg[i] = cg;
 +      }
 +    }
 +
 +    atom = molt->atoms.atom;
 +    for(mol=0; mol<molb->nmol; mol++) {
 +      for(j=0; (j<NBT); j++) {
 +      ia = molt->ilist[bondtypes[j]].iatoms;
 +      for(i=0; (i<molt->ilist[bondtypes[j]].nr); ) {
 +        type  = ia[0];
 +        ftype = ffparams->functype[type];
 +        nra   = interaction_function[ftype].nratoms;
 +        
 +        /* Check whether we have a bond with a shell */
 +        aS = NO_ATID;
 +        
 +        switch (bondtypes[j]) {
 +        case F_BONDS:
 +        case F_HARMONIC:
 +        case F_CUBICBONDS:
 +        case F_POLARIZATION:
 +        case F_ANHARM_POL:
 +          if (atom[ia[1]].ptype == eptShell) {
 +            aS = ia[1];
 +            aN = ia[2];
 +          }
 +          else if (atom[ia[2]].ptype == eptShell) {
 +            aS = ia[2];
 +            aN = ia[1];
 +          }
 +          break;
 +        case F_WATER_POL:
 +          aN    = ia[4];  /* Dummy */
 +          aS    = ia[5];  /* Shell */
 +          break;
 +        default:
 +          gmx_fatal(FARGS,"Death Horror: %s, %d",__FILE__,__LINE__);
 +        }
 +        
 +        if (aS != NO_ATID) {    
 +          qS = atom[aS].q;
 +          
 +          /* Check whether one of the particles is a shell... */
 +          nsi = shell_index[a_offset+aS];
 +          if ((nsi < 0) || (nsi >= nshell))
 +            gmx_fatal(FARGS,"nsi is %d should be within 0 - %d. aS = %d",
 +                      nsi,nshell,aS);
 +          if (shell[nsi].shell == NO_ATID) {
 +            shell[nsi].shell = a_offset + aS;
 +            ns ++;
 +          }
 +          else if (shell[nsi].shell != a_offset+aS)
 +            gmx_fatal(FARGS,"Weird stuff in %s, %d",__FILE__,__LINE__);
 +          
 +          if      (shell[nsi].nucl1 == NO_ATID) {
 +            shell[nsi].nucl1 = a_offset + aN;
 +          } else if (shell[nsi].nucl2 == NO_ATID) {
 +            shell[nsi].nucl2 = a_offset + aN;
 +          } else if (shell[nsi].nucl3 == NO_ATID) {
 +            shell[nsi].nucl3 = a_offset + aN;
 +          } else {
 +            if (fplog)
 +              pr_shell(fplog,ns,shell);
 +            gmx_fatal(FARGS,"Can not handle more than three bonds per shell\n");
 +          }
 +          if (at2cg[aS] != at2cg[aN]) {
 +            /* shell[nsi].bInterCG = TRUE; */
 +            shfc->bInterCG = TRUE;
 +          }
 +          
 +          switch (bondtypes[j]) {
 +          case F_BONDS:
 +          case F_HARMONIC:
 +            shell[nsi].k    += ffparams->iparams[type].harmonic.krA;
 +            break;
 +          case F_CUBICBONDS:
 +            shell[nsi].k    += ffparams->iparams[type].cubic.kb;
 +            break;
 +          case F_POLARIZATION:
 +          case F_ANHARM_POL:
 +            if (!gmx_within_tol(qS, atom[aS].qB, GMX_REAL_EPS*10))
 +              gmx_fatal(FARGS,"polarize can not be used with qA(%e) != qB(%e) for atom %d of molecule block %d", qS, atom[aS].qB, aS+1, mb+1);
 +            shell[nsi].k    += sqr(qS)*ONE_4PI_EPS0/
 +              ffparams->iparams[type].polarize.alpha;
 +            break;
 +          case F_WATER_POL:
 +            if (!gmx_within_tol(qS, atom[aS].qB, GMX_REAL_EPS*10))
 +              gmx_fatal(FARGS,"water_pol can not be used with qA(%e) != qB(%e) for atom %d of molecule block %d", qS, atom[aS].qB, aS+1, mb+1);
 +            alpha          = (ffparams->iparams[type].wpol.al_x+
 +                              ffparams->iparams[type].wpol.al_y+
 +                              ffparams->iparams[type].wpol.al_z)/3.0;
 +            shell[nsi].k  += sqr(qS)*ONE_4PI_EPS0/alpha;
 +            break;
 +          default:
 +            gmx_fatal(FARGS,"Death Horror: %s, %d",__FILE__,__LINE__);
 +          }
 +          shell[nsi].nnucl++;
 +        }
 +        ia += nra+1;
 +        i  += nra+1;
 +      }
 +      }
 +      a_offset += molt->atoms.nr;
 +    }
 +    /* Done with this molecule type */
 +    sfree(at2cg);
 +  }
 +  
 +  /* Verify whether it's all correct */
 +  if (ns != nshell)
 +    gmx_fatal(FARGS,"Something weird with shells. They may not be bonded to something");
 +  
 +  for(i=0; (i<ns); i++)
 +    shell[i].k_1 = 1.0/shell[i].k;
 +  
 +  if (debug)
 +    pr_shell(debug,ns,shell);
 +
 +  
 +  shfc->nshell_gl      = ns;
 +  shfc->shell_gl       = shell;
 +  shfc->shell_index_gl = shell_index;
 +
 +  shfc->bPredict   = (getenv("GMX_NOPREDICT") == NULL);
-     shfc->bForceInit = (getenv("GMX_FORCEINIT") != NULL);
-     if (shfc->bForceInit && fplog)
++  shfc->bRequireInit = FALSE;
 +  if (!shfc->bPredict) {
 +    if (fplog)
 +      fprintf(fplog,"\nWill never predict shell positions\n");
 +  } else {
-   bInit        = (mdstep == inputrec->init_step) || shfc->bForceInit;
++    shfc->bRequireInit = (getenv("GMX_REQUIRE_SHELL_INIT") != NULL);
++    if (shfc->bRequireInit && fplog)
 +      fprintf(fplog,"\nWill always initiate shell positions\n");
 +  }
 +
 +  if (shfc->bPredict) {
 +    if (x) {
 +      predict_shells(fplog,x,NULL,0,shfc->nshell_gl,shfc->shell_gl,
 +                   NULL,mtop,TRUE);
 +    }
 +
 +    if (shfc->bInterCG) {
 +      if (fplog)
 +      fprintf(fplog,"\nNOTE: there all shells that are connected to particles outside thier own charge group, will not predict shells positions during the run\n\n");
 +      shfc->bPredict = FALSE;
 +    }
 +  }
 +
 +  return shfc;
 +}
 +
 +void make_local_shells(t_commrec *cr,t_mdatoms *md,
 +                     struct gmx_shellfc *shfc)
 +{
 +  t_shell *shell;
 +  int a0,a1,*ind,nshell,i;
 +  gmx_domdec_t *dd=NULL;
 +
 +  if (PAR(cr)) {
 +    if (DOMAINDECOMP(cr)) {
 +      dd = cr->dd;
 +      a0 = 0;
 +      a1 = dd->nat_home;
 +    } else {
 +      pd_at_range(cr,&a0,&a1);
 +    }
 +  } else {
 +    /* Single node: we need all shells, just copy the pointer */
 +    shfc->nshell = shfc->nshell_gl;
 +    shfc->shell  = shfc->shell_gl;
 +    
 +    return;
 +  }
 +
 +  ind = shfc->shell_index_gl;
 +
 +  nshell = 0;
 +  shell  = shfc->shell; 
 +  for(i=a0; i<a1; i++) {
 +    if (md->ptype[i] == eptShell) {
 +      if (nshell+1 > shfc->shell_nalloc) {
 +      shfc->shell_nalloc = over_alloc_dd(nshell+1);
 +      srenew(shell,shfc->shell_nalloc);
 +      }
 +      if (dd) {
 +      shell[nshell] = shfc->shell_gl[ind[dd->gatindex[i]]];
 +      } else {
 +      shell[nshell] = shfc->shell_gl[ind[i]];
 +      }
 +      /* With inter-cg shells we can no do shell prediction,
 +       * so we do not need the nuclei numbers.
 +       */
 +      if (!shfc->bInterCG) {
 +      shell[nshell].nucl1   = i + shell[nshell].nucl1 - shell[nshell].shell;
 +      if (shell[nshell].nnucl > 1)
 +        shell[nshell].nucl2 = i + shell[nshell].nucl2 - shell[nshell].shell;
 +      if (shell[nshell].nnucl > 2)
 +        shell[nshell].nucl3 = i + shell[nshell].nucl3 - shell[nshell].shell;
 +      }
 +      shell[nshell].shell = i;
 +      nshell++;
 +    }
 +  }
 +
 +  shfc->nshell = nshell;
 +  shfc->shell  = shell;
 +}
 +
 +static void do_1pos(rvec xnew,rvec xold,rvec f,real step)
 +{
 +  real xo,yo,zo;
 +  real dx,dy,dz;
 +  
 +  xo=xold[XX];
 +  yo=xold[YY];
 +  zo=xold[ZZ];
 +
 +  dx=f[XX]*step;
 +  dy=f[YY]*step;
 +  dz=f[ZZ]*step;
 +
 +  xnew[XX]=xo+dx;
 +  xnew[YY]=yo+dy;
 +  xnew[ZZ]=zo+dz;
 +}
 +
 +static void do_1pos3(rvec xnew,rvec xold,rvec f,rvec step)
 +{
 +  real xo,yo,zo;
 +  real dx,dy,dz;
 +  
 +  xo=xold[XX];
 +  yo=xold[YY];
 +  zo=xold[ZZ];
 +
 +  dx=f[XX]*step[XX];
 +  dy=f[YY]*step[YY];
 +  dz=f[ZZ]*step[ZZ];
 +
 +  xnew[XX]=xo+dx;
 +  xnew[YY]=yo+dy;
 +  xnew[ZZ]=zo+dz;
 +}
 +
 +static void directional_sd(FILE *log,rvec xold[],rvec xnew[],rvec acc_dir[],
 +                         int start,int homenr,real step)
 +{
 +  int  i;
 +
 +  for(i=start; i<homenr; i++)
 +    do_1pos(xnew[i],xold[i],acc_dir[i],step);
 +}
 +
 +static void shell_pos_sd(FILE *log,rvec xcur[],rvec xnew[],rvec f[],
 +                       int ns,t_shell s[],int count)
 +{
 +    const real step_scale_min = 0.8,
 +        step_scale_increment = 0.2,
 +        step_scale_max = 1.2,
 +        step_scale_multiple = (step_scale_max - step_scale_min) / step_scale_increment;
 +  int  i,shell,d;
 +  real dx,df,k_est;
 +#ifdef PRINT_STEP  
 +  real step_min,step_max;
 +
 +  step_min = 1e30;
 +  step_max = 0;
 +#endif
 +  for(i=0; (i<ns); i++) {
 +    shell = s[i].shell;
 +    if (count == 1) {
 +      for(d=0; d<DIM; d++) {
 +      s[i].step[d] = s[i].k_1;
 +#ifdef PRINT_STEP
 +      step_min = min(step_min,s[i].step[d]);
 +      step_max = max(step_max,s[i].step[d]);
 +#endif
 +      }
 +    } else {
 +      for(d=0; d<DIM; d++) {
 +      dx = xcur[shell][d] - s[i].xold[d];
 +      df =    f[shell][d] - s[i].fold[d];
 +    /* -dx/df gets used to generate an interpolated value, but would
 +     * cause a NaN if df were binary-equal to zero. Values close to
 +     * zero won't cause problems (because of the min() and max()), so
 +     * just testing for binary inequality is OK. */
 +    if (0.0 != df)
 +    {
 +        k_est = -dx/df;
 +        /* Scale the step size by a factor interpolated from
 +         * step_scale_min to step_scale_max, as k_est goes from 0 to
 +         * step_scale_multiple * s[i].step[d] */
 +        s[i].step[d] =
 +            step_scale_min * s[i].step[d] +
 +            step_scale_increment * min(step_scale_multiple * s[i].step[d], max(k_est, 0));
 +    }
 +    else
 +    {
 +        /* Here 0 == df */
 +        if (gmx_numzero(dx)) /* 0 == dx */
 +        {
 +            /* Likely this will never happen, but if it does just
 +             * don't scale the step. */
 +        }
 +        else /* 0 != dx */
 +        {
 +            s[i].step[d] *= step_scale_max;
 +        }
 +    }
 +#ifdef PRINT_STEP
 +      step_min = min(step_min,s[i].step[d]);
 +      step_max = max(step_max,s[i].step[d]);
 +#endif
 +      }
 +    }
 +    copy_rvec(xcur[shell],s[i].xold);
 +    copy_rvec(f[shell],   s[i].fold);
 +
 +    do_1pos3(xnew[shell],xcur[shell],f[shell],s[i].step);
 +
 +    if (gmx_debug_at) {
 +      fprintf(debug,"shell[%d] = %d\n",i,shell);
 +      pr_rvec(debug,0,"fshell",f[shell],DIM,TRUE);
 +      pr_rvec(debug,0,"xold",xcur[shell],DIM,TRUE);
 +      pr_rvec(debug,0,"step",s[i].step,DIM,TRUE);
 +      pr_rvec(debug,0,"xnew",xnew[shell],DIM,TRUE);
 +    }
 +  }
 +#ifdef PRINT_STEP
 +  printf("step %.3e %.3e\n",step_min,step_max);
 +#endif
 +}
 +
 +static void decrease_step_size(int nshell,t_shell s[])
 +{
 +  int i;
 +  
 +  for(i=0; i<nshell; i++)
 +    svmul(0.8,s[i].step,s[i].step);
 +}
 +
 +static void print_epot(FILE *fp,gmx_large_int_t mdstep,int count,real epot,real df,
 +                     int ndir,real sf_dir)
 +{
 +  char buf[22];
 +
 +  fprintf(fp,"MDStep=%5s/%2d EPot: %12.8e, rmsF: %6.2e",
 +        gmx_step_str(mdstep,buf),count,epot,df);
 +  if (ndir)
 +    fprintf(fp,", dir. rmsF: %6.2e\n",sqrt(sf_dir/ndir));
 +  else
 +    fprintf(fp,"\n");
 +}
 +
 +
 +static real rms_force(t_commrec *cr,rvec f[],int ns,t_shell s[],
 +                    int ndir,real *sf_dir,real *Epot)
 +{
 +  int  i,shell,ntot;
 +  double buf[4];
 +
 +  buf[0] = *sf_dir;
 +  for(i=0; i<ns; i++) {
 +    shell = s[i].shell;
 +    buf[0]  += norm2(f[shell]);
 +  }
 +  ntot = ns;
 +
 +  if (PAR(cr)) {
 +    buf[1] = ntot;
 +    buf[2] = *sf_dir;
 +    buf[3] = *Epot;
 +    gmx_sumd(4,buf,cr);
 +    ntot = (int)(buf[1] + 0.5);
 +    *sf_dir = buf[2];
 +    *Epot   = buf[3];
 +  }
 +  ntot += ndir;
 +
 +  return (ntot ? sqrt(buf[0]/ntot) : 0);
 +}
 +
 +static void check_pbc(FILE *fp,rvec x[],int shell)
 +{
 +  int m,now;
 +  
 +  now = shell-4;
 +  for(m=0; (m<DIM); m++)
 +    if (fabs(x[shell][m]-x[now][m]) > 0.3) {
 +      pr_rvecs(fp,0,"SHELL-X",x+now,5);
 +      break;
 +    }
 +}
 +
 +static void dump_shells(FILE *fp,rvec x[],rvec f[],real ftol,int ns,t_shell s[])
 +{
 +  int  i,shell;
 +  real ft2,ff2;
 +  
 +  ft2 = sqr(ftol);
 +  
 +  for(i=0; (i<ns); i++) {
 +    shell = s[i].shell;
 +    ff2   = iprod(f[shell],f[shell]);
 +    if (ff2 > ft2)
 +      fprintf(fp,"SHELL %5d, force %10.5f  %10.5f  %10.5f, |f| %10.5f\n",
 +            shell,f[shell][XX],f[shell][YY],f[shell][ZZ],sqrt(ff2));
 +    check_pbc(fp,x,shell);
 +  }
 +}
 +
 +static void init_adir(FILE *log,gmx_shellfc_t shfc,
 +                    gmx_constr_t constr,t_idef *idef,t_inputrec *ir,
 +                    t_commrec *cr,int dd_ac1,
 +                    gmx_large_int_t step,t_mdatoms *md,int start,int end,
 +                    rvec *x_old,rvec *x_init,rvec *x,
 +                    rvec *f,rvec *acc_dir,matrix box,
 +                    real *lambda,real *dvdlambda,t_nrnb *nrnb)
 +{
 +  rvec   *xnold,*xnew;
 +  double w_dt;
 +  int    gf,ga,gt;
 +  real   dt,scale;
 +  int    n,d; 
 +  unsigned short *ptype;
 +  rvec   p,dx;
 +  
 +  if (DOMAINDECOMP(cr))
 +    n = dd_ac1;
 +  else
 +    n = end - start;
 +  if (n > shfc->adir_nalloc) {
 +    shfc->adir_nalloc = over_alloc_dd(n);
 +    srenew(shfc->adir_xnold,shfc->adir_nalloc);
 +    srenew(shfc->adir_xnew ,shfc->adir_nalloc);
 +  }
 +  xnold = shfc->adir_xnold;
 +  xnew  = shfc->adir_xnew;
 +    
 +  ptype = md->ptype;
 +
 +  dt = ir->delta_t;
 +
 +  /* Does NOT work with freeze or acceleration groups (yet) */
 +  for (n=start; n<end; n++) {  
 +    w_dt = md->invmass[n]*dt;
 +    
 +    for (d=0; d<DIM; d++) {
 +      if ((ptype[n] != eptVSite) && (ptype[n] != eptShell)) {
 +      xnold[n-start][d] = x[n][d] - (x_init[n][d] - x_old[n][d]);
 +      xnew[n-start][d] = 2*x[n][d] - x_old[n][d] + f[n][d]*w_dt*dt;
 +      } else {
 +      xnold[n-start][d] = x[n][d];
 +      xnew[n-start][d] = x[n][d];
 +      }
 +    }
 +  }
 +  constrain(log,FALSE,FALSE,constr,idef,ir,NULL,cr,step,0,md,
 +          x,xnold-start,NULL,box,
 +          lambda[efptBONDED],&(dvdlambda[efptBONDED]),NULL,NULL,nrnb,econqCoord,FALSE,0,0);
 +  constrain(log,FALSE,FALSE,constr,idef,ir,NULL,cr,step,0,md,
 +          x,xnew-start,NULL,box,
 +          lambda[efptBONDED],&(dvdlambda[efptBONDED]),NULL,NULL,nrnb,econqCoord,FALSE,0,0);
 +
 +  /* Set xnew to minus the acceleration */
 +  for (n=start; n<end; n++) {
 +    for(d=0; d<DIM; d++)
 +      xnew[n-start][d] =
 +      -(2*x[n][d]-xnold[n-start][d]-xnew[n-start][d])/sqr(dt)
 +      - f[n][d]*md->invmass[n];
 +    clear_rvec(acc_dir[n]);
 +  }
 +
 +  /* Project the acceleration on the old bond directions */
 +  constrain(log,FALSE,FALSE,constr,idef,ir,NULL,cr,step,0,md,
 +          x_old,xnew-start,acc_dir,box,
 +          lambda[efptBONDED],&(dvdlambda[efptBONDED]),NULL,NULL,nrnb,econqDeriv_FlexCon,FALSE,0,0);
 +}
 +
 +int relax_shell_flexcon(FILE *fplog,t_commrec *cr,gmx_bool bVerbose,
 +                      gmx_large_int_t mdstep,t_inputrec *inputrec,
 +                      gmx_bool bDoNS,int force_flags,
 +                      gmx_bool bStopCM,
 +                      gmx_localtop_t *top,
 +                      gmx_mtop_t* mtop,
 +                      gmx_constr_t constr,
 +                      gmx_enerdata_t *enerd,t_fcdata *fcd,
 +                      t_state *state,rvec f[],
 +                      tensor force_vir,
 +                      t_mdatoms *md,
 +                      t_nrnb *nrnb,gmx_wallcycle_t wcycle,
 +                      t_graph *graph,
 +                      gmx_groups_t *groups,
 +                      struct gmx_shellfc *shfc,
 +                      t_forcerec *fr,
 +                      gmx_bool bBornRadii,
 +                      double t,rvec mu_tot,
 +                      int natoms,gmx_bool *bConverged,
 +                      gmx_vsite_t *vsite,
 +                      FILE *fp_field)
 +{
 +  int    nshell;
 +  t_shell *shell;
 +  t_idef *idef;
 +  rvec   *pos[2],*force[2],*acc_dir=NULL,*x_old=NULL;
 +  real   Epot[2],df[2];
 +  rvec   dx;
 +  real   sf_dir,invdt;
 +  real   ftol,xiH,xiS,dum=0;
 +  char   sbuf[22];
 +  gmx_bool   bCont,bInit;
 +  int    nat,dd_ac0,dd_ac1=0,i;
 +  int    start=md->start,homenr=md->homenr,end=start+homenr,cg0,cg1;
 +  int    nflexcon,g,number_steps,d,Min=0,count=0;
 +#define  Try (1-Min)             /* At start Try = 1 */
 +
 +  bCont        = (mdstep == inputrec->init_step) && inputrec->bContinuation;
++  bInit        = (mdstep == inputrec->init_step) || shfc->bRequireInit;
 +  ftol         = inputrec->em_tol;
 +  number_steps = inputrec->niter;
 +  nshell       = shfc->nshell;
 +  shell        = shfc->shell;
 +  nflexcon     = shfc->nflexcon;
 +
 +  idef = &top->idef;
 +
 +  if (DOMAINDECOMP(cr)) {
 +    nat = dd_natoms_vsite(cr->dd);
 +    if (nflexcon > 0) {
 +      dd_get_constraint_range(cr->dd,&dd_ac0,&dd_ac1);
 +      nat = max(nat,dd_ac1);
 +    }
 +  } else {
 +    nat = state->natoms;
 +  }
 +
 +  if (nat > shfc->x_nalloc) {
 +    /* Allocate local arrays */
 +    shfc->x_nalloc = over_alloc_dd(nat);
 +    for(i=0; (i<2); i++) {
 +      srenew(shfc->x[i],shfc->x_nalloc);
 +      srenew(shfc->f[i],shfc->x_nalloc);
 +    }
 +  }
 +  for(i=0; (i<2); i++) {
 +    pos[i]   = shfc->x[i];
 +    force[i] = shfc->f[i];
 +  }
 +     
 +  /* With particle decomposition this code only works
 +   * when all particles involved with each shell are in the same cg.
 +   */
 +
 +  if (bDoNS && inputrec->ePBC != epbcNONE && !DOMAINDECOMP(cr)) {
 +    /* This is the only time where the coordinates are used
 +     * before do_force is called, which normally puts all
 +     * charge groups in the box.
 +     */
 +    if (PARTDECOMP(cr)) {
 +      pd_cg_range(cr,&cg0,&cg1);
 +    } else {
 +      cg0 = 0;
 +      cg1 = top->cgs.nr;
 +    }
 +    put_charge_groups_in_box(fplog,cg0,cg1,fr->ePBC,state->box,
 +                           &(top->cgs),state->x,fr->cg_cm);
 +    if (graph)
 +      mk_mshift(fplog,graph,fr->ePBC,state->box,state->x);
 +  }
 +
 +  /* After this all coordinate arrays will contain whole molecules */
 +  if (graph)
 +    shift_self(graph,state->box,state->x);
 +
 +  if (nflexcon) {
 +    if (nat > shfc->flex_nalloc) {
 +      shfc->flex_nalloc = over_alloc_dd(nat);
 +      srenew(shfc->acc_dir,shfc->flex_nalloc);
 +      srenew(shfc->x_old,shfc->flex_nalloc);
 +    }
 +    acc_dir = shfc->acc_dir;
 +    x_old   = shfc->x_old;
 +    for(i=0; i<homenr; i++) {
 +      for(d=0; d<DIM; d++)
 +        shfc->x_old[i][d] =
 +        state->x[start+i][d] - state->v[start+i][d]*inputrec->delta_t;
 +    }
 +  }
 +
 +  /* Do a prediction of the shell positions */
 +  if (shfc->bPredict && !bCont) {
 +    predict_shells(fplog,state->x,state->v,inputrec->delta_t,nshell,shell,
 +                 md->massT,NULL,bInit);
 +  }
 +
 +  /* do_force expected the charge groups to be in the box */
 +  if (graph)
 +    unshift_self(graph,state->box,state->x);
 +
 +  /* Calculate the forces first time around */
 +  if (gmx_debug_at) {
 +    pr_rvecs(debug,0,"x b4 do_force",state->x + start,homenr);
 +  }
 +  do_force(fplog,cr,inputrec,mdstep,nrnb,wcycle,top,mtop,groups,
 +         state->box,state->x,&state->hist,
 +         force[Min],force_vir,md,enerd,fcd,
 +         state->lambda,graph,
 +         fr,vsite,mu_tot,t,fp_field,NULL,bBornRadii,
 +         (bDoNS ? GMX_FORCE_NS : 0) | force_flags);
 +
 +  sf_dir = 0;
 +  if (nflexcon) {
 +    init_adir(fplog,shfc,
 +            constr,idef,inputrec,cr,dd_ac1,mdstep,md,start,end,
 +            shfc->x_old-start,state->x,state->x,force[Min],
 +            shfc->acc_dir-start,state->box,state->lambda,&dum,nrnb);
 +
 +    for(i=start; i<end; i++)
 +      sf_dir += md->massT[i]*norm2(shfc->acc_dir[i-start]);
 +  }
 +
 +  Epot[Min] = enerd->term[F_EPOT];
 +
 +  df[Min]=rms_force(cr,shfc->f[Min],nshell,shell,nflexcon,&sf_dir,&Epot[Min]);
 +  df[Try]=0;
 +  if (debug) {
 +    fprintf(debug,"df = %g  %g\n",df[Min],df[Try]);
 +  }
 +
 +  if (gmx_debug_at) {
 +    pr_rvecs(debug,0,"force0",force[Min],md->nr);
 +  }
 +
 +  if (nshell+nflexcon > 0) {
 +    /* Copy x to pos[Min] & pos[Try]: during minimization only the
 +     * shell positions are updated, therefore the other particles must
 +     * be set here.
 +     */
 +    memcpy(pos[Min],state->x,nat*sizeof(state->x[0]));
 +    memcpy(pos[Try],state->x,nat*sizeof(state->x[0]));
 +  }
 +  
 +  if (bVerbose && MASTER(cr))
 +    print_epot(stdout,mdstep,0,Epot[Min],df[Min],nflexcon,sf_dir);
 +
 +  if (debug) {
 +    fprintf(debug,"%17s: %14.10e\n",
 +          interaction_function[F_EKIN].longname,enerd->term[F_EKIN]);
 +    fprintf(debug,"%17s: %14.10e\n",
 +          interaction_function[F_EPOT].longname,enerd->term[F_EPOT]);
 +    fprintf(debug,"%17s: %14.10e\n",
 +          interaction_function[F_ETOT].longname,enerd->term[F_ETOT]);
 +    fprintf(debug,"SHELLSTEP %s\n",gmx_step_str(mdstep,sbuf));
 +  }
 +  
 +  /* First check whether we should do shells, or whether the force is 
 +   * low enough even without minimization.
 +   */
 +  *bConverged = (df[Min] < ftol);
 +  
 +  for(count=1; (!(*bConverged) && (count < number_steps)); count++) {
 +    if (vsite)
 +      construct_vsites(fplog,vsite,pos[Min],nrnb,inputrec->delta_t,state->v,
 +                     idef->iparams,idef->il,
 +                     fr->ePBC,fr->bMolPBC,graph,cr,state->box);
 +     
 +    if (nflexcon) {
 +      init_adir(fplog,shfc,
 +              constr,idef,inputrec,cr,dd_ac1,mdstep,md,start,end,
 +              x_old-start,state->x,pos[Min],force[Min],acc_dir-start,
 +              state->box,state->lambda,&dum,nrnb);
 +      
 +      directional_sd(fplog,pos[Min],pos[Try],acc_dir-start,start,end,
 +                   fr->fc_stepsize);
 +    }
 +    
 +    /* New positions, Steepest descent */
 +    shell_pos_sd(fplog,pos[Min],pos[Try],force[Min],nshell,shell,count); 
 +
 +    /* do_force expected the charge groups to be in the box */
 +    if (graph)
 +      unshift_self(graph,state->box,pos[Try]);
 +
 +    if (gmx_debug_at) {
 +      pr_rvecs(debug,0,"RELAX: pos[Min]  ",pos[Min] + start,homenr);
 +      pr_rvecs(debug,0,"RELAX: pos[Try]  ",pos[Try] + start,homenr);
 +    }
 +    /* Try the new positions */
 +    do_force(fplog,cr,inputrec,1,nrnb,wcycle,
 +           top,mtop,groups,state->box,pos[Try],&state->hist,
 +           force[Try],force_vir,
 +           md,enerd,fcd,state->lambda,graph,
 +           fr,vsite,mu_tot,t,fp_field,NULL,bBornRadii,
 +           force_flags);
 +    
 +    if (gmx_debug_at) {
 +      pr_rvecs(debug,0,"RELAX: force[Min]",force[Min] + start,homenr);
 +      pr_rvecs(debug,0,"RELAX: force[Try]",force[Try] + start,homenr);
 +    }
 +    sf_dir = 0;
 +    if (nflexcon) {
 +      init_adir(fplog,shfc,
 +              constr,idef,inputrec,cr,dd_ac1,mdstep,md,start,end,
 +              x_old-start,state->x,pos[Try],force[Try],acc_dir-start,
 +              state->box,state->lambda,&dum,nrnb);
 +
 +      for(i=start; i<end; i++)
 +      sf_dir += md->massT[i]*norm2(acc_dir[i-start]);
 +    }
 +
 +    Epot[Try] = enerd->term[F_EPOT]; 
 +    
 +    df[Try]=rms_force(cr,force[Try],nshell,shell,nflexcon,&sf_dir,&Epot[Try]);
 +
 +    if (debug)
 +      fprintf(debug,"df = %g  %g\n",df[Min],df[Try]);
 +
 +    if (debug) {
 +      if (gmx_debug_at)
 +      pr_rvecs(debug,0,"F na do_force",force[Try] + start,homenr);
 +      if (gmx_debug_at) {
 +      fprintf(debug,"SHELL ITER %d\n",count);
 +      dump_shells(debug,pos[Try],force[Try],ftol,nshell,shell);
 +      }
 +    }
 +
 +    if (bVerbose && MASTER(cr))
 +      print_epot(stdout,mdstep,count,Epot[Try],df[Try],nflexcon,sf_dir);
 +      
 +    *bConverged = (df[Try] < ftol);
 +    
 +    if ((df[Try] < df[Min])) {
 +      if (debug)
 +      fprintf(debug,"Swapping Min and Try\n");
 +      if (nflexcon) {
 +      /* Correct the velocities for the flexible constraints */
 +      invdt = 1/inputrec->delta_t;
 +      for(i=start; i<end; i++) {
 +        for(d=0; d<DIM; d++)
 +          state->v[i][d] += (pos[Try][i][d] - pos[Min][i][d])*invdt;
 +      }
 +      }
 +      Min  = Try;
 +    } else {
 +      decrease_step_size(nshell,shell);
 +    }
 +  }
 +  if (MASTER(cr) && !(*bConverged)) {
 +    /* Note that the energies and virial are incorrect when not converged */
 +    if (fplog)
 +      fprintf(fplog,
 +            "step %s: EM did not converge in %d iterations, RMS force %.3f\n",
 +            gmx_step_str(mdstep,sbuf),number_steps,df[Min]);
 +    fprintf(stderr,
 +          "step %s: EM did not converge in %d iterations, RMS force %.3f\n",
 +          gmx_step_str(mdstep,sbuf),number_steps,df[Min]);
 +  }
 +
 +  /* Copy back the coordinates and the forces */
 +  memcpy(state->x,pos[Min],nat*sizeof(state->x[0]));
 +  memcpy(f,force[Min],nat*sizeof(f[0]));
 +
 +  return count; 
 +}
 +
index f5b862d0cf17ed7b805663b6d1a75edf1a735a5c,0000000000000000000000000000000000000000..23872d26111f6091ebfe2700a6ac4cbc4e3ed246
mode 100644,000000..100644
--- /dev/null
@@@ -1,982 -1,0 +1,982 @@@
-            init_rot(fplog,inputrec,nfile,fnm,cr,state->x,state->box,mtop,oenv,
 +/* -*- 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:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +#ifdef __linux
 +#define _GNU_SOURCE
 +#include <sched.h>
 +#include <sys/syscall.h>
 +#endif
 +#include <signal.h>
 +#include <stdlib.h>
 +#ifdef HAVE_UNISTD_H
 +#include <unistd.h>
 +#endif
 +
 +#include "typedefs.h"
 +#include "smalloc.h"
 +#include "sysstuff.h"
 +#include "statutil.h"
 +#include "mdrun.h"
 +#include "network.h"
 +#include "pull.h"
 +#include "pull_rotation.h"
 +#include "names.h"
 +#include "disre.h"
 +#include "orires.h"
 +#include "pme.h"
 +#include "mdatoms.h"
 +#include "repl_ex.h"
 +#include "qmmm.h"
 +#include "domdec.h"
 +#include "partdec.h"
 +#include "coulomb.h"
 +#include "constr.h"
 +#include "mvdata.h"
 +#include "checkpoint.h"
 +#include "mtop_util.h"
 +#include "sighandler.h"
 +#include "tpxio.h"
 +#include "txtdump.h"
 +#include "pull_rotation.h"
 +#include "membed.h"
 +#include "macros.h"
 +
 +#include "gmx_omp.h"
 +
 +#ifdef GMX_LIB_MPI
 +#include <mpi.h>
 +#endif
 +#ifdef GMX_THREAD_MPI
 +#include "tmpi.h"
 +#endif
 +
 +#ifdef GMX_FAHCORE
 +#include "corewrap.h"
 +#endif
 +
 +#ifdef GMX_OPENMM
 +#include "md_openmm.h"
 +#endif
 +
 +
 +typedef struct { 
 +    gmx_integrator_t *func;
 +} gmx_intp_t;
 +
 +/* The array should match the eI array in include/types/enums.h */
 +#ifdef GMX_OPENMM  /* FIXME do_md_openmm needs fixing */
 +const gmx_intp_t integrator[eiNR] = { {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm},{do_md_openmm}};
 +#else
 +const gmx_intp_t integrator[eiNR] = { {do_md}, {do_steep}, {do_cg}, {do_md}, {do_md}, {do_nm}, {do_lbfgs}, {do_tpi}, {do_tpi}, {do_md}, {do_md},{do_md}};
 +#endif
 +
 +gmx_large_int_t     deform_init_init_step_tpx;
 +matrix              deform_init_box_tpx;
 +#ifdef GMX_THREAD_MPI
 +tMPI_Thread_mutex_t deform_init_box_mutex=TMPI_THREAD_MUTEX_INITIALIZER;
 +#endif
 +
 +
 +#ifdef GMX_THREAD_MPI
 +struct mdrunner_arglist
 +{
 +    FILE *fplog;
 +    t_commrec *cr;
 +    int nfile;
 +    const t_filenm *fnm;
 +    output_env_t oenv;
 +    gmx_bool bVerbose;
 +    gmx_bool bCompact;
 +    int nstglobalcomm;
 +    ivec ddxyz;
 +    int dd_node_order;
 +    real rdd;
 +    real rconstr;
 +    const char *dddlb_opt;
 +    real dlb_scale;
 +    const char *ddcsx;
 +    const char *ddcsy;
 +    const char *ddcsz;
 +    int nstepout;
 +    int resetstep;
 +    int nmultisim;
 +    int repl_ex_nst;
 +    int repl_ex_nex;
 +    int repl_ex_seed;
 +    real pforce;
 +    real cpt_period;
 +    real max_hours;
 +    const char *deviceOptions;
 +    unsigned long Flags;
 +    int ret; /* return value */
 +};
 +
 +
 +/* The function used for spawning threads. Extracts the mdrunner() 
 +   arguments from its one argument and calls mdrunner(), after making
 +   a commrec. */
 +static void mdrunner_start_fn(void *arg)
 +{
 +    struct mdrunner_arglist *mda=(struct mdrunner_arglist*)arg;
 +    struct mdrunner_arglist mc=*mda; /* copy the arg list to make sure 
 +                                        that it's thread-local. This doesn't
 +                                        copy pointed-to items, of course,
 +                                        but those are all const. */
 +    t_commrec *cr;  /* we need a local version of this */
 +    FILE *fplog=NULL;
 +    t_filenm *fnm;
 +
 +    fnm = dup_tfn(mc.nfile, mc.fnm);
 +
 +    cr = init_par_threads(mc.cr);
 +
 +    if (MASTER(cr))
 +    {
 +        fplog=mc.fplog;
 +    }
 +
 +    mda->ret=mdrunner(cr->nnodes, fplog, cr, mc.nfile, fnm, mc.oenv, 
 +                      mc.bVerbose, mc.bCompact, mc.nstglobalcomm, 
 +                      mc.ddxyz, mc.dd_node_order, mc.rdd,
 +                      mc.rconstr, mc.dddlb_opt, mc.dlb_scale, 
 +                      mc.ddcsx, mc.ddcsy, mc.ddcsz, mc.nstepout, mc.resetstep, 
 +                      mc.nmultisim, mc.repl_ex_nst, mc.repl_ex_nex, mc.repl_ex_seed, mc.pforce,
 +                      mc.cpt_period, mc.max_hours, mc.deviceOptions, mc.Flags);
 +}
 +
 +/* called by mdrunner() to start a specific number of threads (including 
 +   the main thread) for thread-parallel runs. This in turn calls mdrunner()
 +   for each thread. 
 +   All options besides nthreads are the same as for mdrunner(). */
 +static t_commrec *mdrunner_start_threads(int nthreads, 
 +              FILE *fplog,t_commrec *cr,int nfile, 
 +              const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
 +              gmx_bool bCompact, int nstglobalcomm,
 +              ivec ddxyz,int dd_node_order,real rdd,real rconstr,
 +              const char *dddlb_opt,real dlb_scale,
 +              const char *ddcsx,const char *ddcsy,const char *ddcsz,
 +              int nstepout,int resetstep,int nmultisim,int repl_ex_nst,
 +              int repl_ex_nex, int repl_ex_seed, real pforce,real cpt_period, real max_hours,
 +              const char *deviceOptions, unsigned long Flags)
 +{
 +    int ret;
 +    struct mdrunner_arglist *mda;
 +    t_commrec *crn; /* the new commrec */
 +    t_filenm *fnmn;
 +
 +    /* first check whether we even need to start tMPI */
 +    if (nthreads<2)
 +        return cr;
 +
 +    /* a few small, one-time, almost unavoidable memory leaks: */
 +    snew(mda,1);
 +    fnmn=dup_tfn(nfile, fnm);
 +
 +    /* fill the data structure to pass as void pointer to thread start fn */
 +    mda->fplog=fplog;
 +    mda->cr=cr;
 +    mda->nfile=nfile;
 +    mda->fnm=fnmn;
 +    mda->oenv=oenv;
 +    mda->bVerbose=bVerbose;
 +    mda->bCompact=bCompact;
 +    mda->nstglobalcomm=nstglobalcomm;
 +    mda->ddxyz[XX]=ddxyz[XX];
 +    mda->ddxyz[YY]=ddxyz[YY];
 +    mda->ddxyz[ZZ]=ddxyz[ZZ];
 +    mda->dd_node_order=dd_node_order;
 +    mda->rdd=rdd;
 +    mda->rconstr=rconstr;
 +    mda->dddlb_opt=dddlb_opt;
 +    mda->dlb_scale=dlb_scale;
 +    mda->ddcsx=ddcsx;
 +    mda->ddcsy=ddcsy;
 +    mda->ddcsz=ddcsz;
 +    mda->nstepout=nstepout;
 +    mda->resetstep=resetstep;
 +    mda->nmultisim=nmultisim;
 +    mda->repl_ex_nst=repl_ex_nst;
 +    mda->repl_ex_nex=repl_ex_nex;
 +    mda->repl_ex_seed=repl_ex_seed;
 +    mda->pforce=pforce;
 +    mda->cpt_period=cpt_period;
 +    mda->max_hours=max_hours;
 +    mda->deviceOptions=deviceOptions;
 +    mda->Flags=Flags;
 +
 +    fprintf(stderr, "Starting %d threads\n",nthreads);
 +    fflush(stderr);
 +    /* now spawn new threads that start mdrunner_start_fn(), while 
 +       the main thread returns */
 +    ret=tMPI_Init_fn(TRUE, nthreads, mdrunner_start_fn, (void*)(mda) );
 +    if (ret!=TMPI_SUCCESS)
 +        return NULL;
 +
 +    /* make a new comm_rec to reflect the new situation */
 +    crn=init_par_threads(cr);
 +    return crn;
 +}
 +
 +
 +/* Get the number of threads to use for thread-MPI based on how many
 + * were requested, which algorithms we're using,
 + * and how many particles there are.
 + */
 +static int get_nthreads_mpi(int nthreads_requested, t_inputrec *inputrec,
 +                            gmx_mtop_t *mtop)
 +{
 +    int nthreads,nthreads_new;
 +    int min_atoms_per_thread;
 +    char *env;
 +
 +    nthreads = nthreads_requested;
 +
 +    /* determine # of hardware threads. */
 +    if (nthreads_requested < 1)
 +    {
 +        if ((env = getenv("GMX_MAX_THREADS")) != NULL)
 +        {
 +            nthreads = 0;
 +            sscanf(env,"%d",&nthreads);
 +            if (nthreads < 1)
 +            {
 +                gmx_fatal(FARGS,"GMX_MAX_THREADS (%d) should be larger than 0",
 +                          nthreads);
 +            }
 +        }
 +        else
 +        {
 +            nthreads = tMPI_Thread_get_hw_number();
 +        }
 +    }
 +
 +    if (inputrec->eI == eiNM || EI_TPI(inputrec->eI))
 +    {
 +        /* Steps are divided over the nodes iso splitting the atoms */
 +        min_atoms_per_thread = 0;
 +    }
 +    else
 +    {
 +        min_atoms_per_thread = MIN_ATOMS_PER_THREAD;
 +    }
 +
 +    /* Check if an algorithm does not support parallel simulation.  */
 +    if (nthreads != 1 && 
 +        ( inputrec->eI == eiLBFGS ||
 +          inputrec->coulombtype == eelEWALD ) )
 +    {
 +        fprintf(stderr,"\nThe integration or electrostatics algorithm doesn't support parallel runs. Not starting any threads.\n");
 +        nthreads = 1;
 +    }
 +    else if (nthreads_requested < 1 &&
 +             mtop->natoms/nthreads < min_atoms_per_thread)
 +    {
 +        /* the thread number was chosen automatically, but there are too many
 +           threads (too few atoms per thread) */
 +        nthreads_new = max(1,mtop->natoms/min_atoms_per_thread);
 +
 +        if (nthreads_new > 8 || (nthreads == 8 && nthreads_new > 4))
 +        {
 +            /* Use only multiples of 4 above 8 threads
 +             * or with an 8-core processor
 +             * (to avoid 6 threads on 8 core processors with 4 real cores).
 +             */
 +            nthreads_new = (nthreads_new/4)*4;
 +        }
 +        else if (nthreads_new > 4)
 +        {
 +            /* Avoid 5 or 7 threads */
 +            nthreads_new = (nthreads_new/2)*2;
 +        }
 +
 +        nthreads = nthreads_new;
 +
 +        fprintf(stderr,"\n");
 +        fprintf(stderr,"NOTE: Parallelization is limited by the small number of atoms,\n");
 +        fprintf(stderr,"      only starting %d threads.\n",nthreads);
 +        fprintf(stderr,"      You can use the -nt option to optimize the number of threads.\n\n");
 +    }
 +    return nthreads;
 +}
 +#endif
 +
 +
 +int mdrunner(int nthreads_requested, FILE *fplog,t_commrec *cr,int nfile,
 +             const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
 +             gmx_bool bCompact, int nstglobalcomm,
 +             ivec ddxyz,int dd_node_order,real rdd,real rconstr,
 +             const char *dddlb_opt,real dlb_scale,
 +             const char *ddcsx,const char *ddcsy,const char *ddcsz,
 +             int nstepout,int resetstep,int nmultisim, int repl_ex_nst, int repl_ex_nex,
 +             int repl_ex_seed, real pforce,real cpt_period,real max_hours,
 +             const char *deviceOptions, unsigned long Flags)
 +{
 +    double     nodetime=0,realtime;
 +    t_inputrec *inputrec;
 +    t_state    *state=NULL;
 +    matrix     box;
 +    gmx_ddbox_t ddbox={0};
 +    int        npme_major,npme_minor;
 +    real       tmpr1,tmpr2;
 +    t_nrnb     *nrnb;
 +    gmx_mtop_t *mtop=NULL;
 +    t_mdatoms  *mdatoms=NULL;
 +    t_forcerec *fr=NULL;
 +    t_fcdata   *fcd=NULL;
 +    real       ewaldcoeff=0;
 +    gmx_pme_t  *pmedata=NULL;
 +    gmx_vsite_t *vsite=NULL;
 +    gmx_constr_t constr;
 +    int        i,m,nChargePerturbed=-1,status,nalloc;
 +    char       *gro;
 +    gmx_wallcycle_t wcycle;
 +    gmx_bool       bReadRNG,bReadEkin;
 +    int        list;
 +    gmx_runtime_t runtime;
 +    int        rc;
 +    gmx_large_int_t reset_counters;
 +    gmx_edsam_t ed=NULL;
 +    t_commrec   *cr_old=cr; 
 +    int         nthreads_mpi=1;
 +    int         nthreads_pme=1;
 +    gmx_membed_t membed=NULL;
 +
 +    /* CAUTION: threads may be started later on in this function, so
 +       cr doesn't reflect the final parallel state right now */
 +    snew(inputrec,1);
 +    snew(mtop,1);
 +
 +    if (bVerbose && SIMMASTER(cr))
 +    {
 +        fprintf(stderr,"Getting Loaded...\n");
 +    }
 +    
 +    if (Flags & MD_APPENDFILES) 
 +    {
 +        fplog = NULL;
 +    }
 +
 +    snew(state,1);
 +    if (MASTER(cr)) 
 +    {
 +        /* Read (nearly) all data required for the simulation */
 +        read_tpx_state(ftp2fn(efTPX,nfile,fnm),inputrec,state,NULL,mtop);
 +
 +        /* NOW the threads will be started: */
 +#ifdef GMX_THREAD_MPI
 +        nthreads_mpi = get_nthreads_mpi(nthreads_requested, inputrec, mtop);
 +
 +        if (nthreads_mpi > 1)
 +        {
 +            /* now start the threads. */
 +            cr=mdrunner_start_threads(nthreads_mpi, fplog, cr_old, nfile, fnm,
 +                                      oenv, bVerbose, bCompact, nstglobalcomm, 
 +                                      ddxyz, dd_node_order, rdd, rconstr, 
 +                                      dddlb_opt, dlb_scale, ddcsx, ddcsy, ddcsz,
 +                                      nstepout, resetstep, nmultisim, 
 +                                      repl_ex_nst, repl_ex_nex, repl_ex_seed, pforce,
 +                                      cpt_period, max_hours, deviceOptions, 
 +                                      Flags);
 +            /* the main thread continues here with a new cr. We don't deallocate
 +               the old cr because other threads may still be reading it. */
 +            if (cr == NULL)
 +            {
 +                gmx_comm("Failed to spawn threads");
 +            }
 +        }
 +#endif
 +    }
 +    /* END OF CAUTION: cr is now reliable */
 +
 +    /* g_membed initialisation *
 +     * Because we change the mtop, init_membed is called before the init_parallel *
 +     * (in case we ever want to make it run in parallel) */
 +    if (opt2bSet("-membed",nfile,fnm))
 +    {
 +        if (MASTER(cr))
 +        {
 +            fprintf(stderr,"Initializing membed");
 +        }
 +        membed = init_membed(fplog,nfile,fnm,mtop,inputrec,state,cr,&cpt_period);
 +    }
 +
 +    if (PAR(cr))
 +    {
 +        /* now broadcast everything to the non-master nodes/threads: */
 +        init_parallel(fplog, cr, inputrec, mtop);
 +    }
 +    if (fplog != NULL)
 +    {
 +        pr_inputrec(fplog,0,"Input Parameters",inputrec,FALSE);
 +    }
 +
 +    /* now make sure the state is initialized and propagated */
 +    set_state_entries(state,inputrec,cr->nnodes);
 +
 +    /* remove when vv and rerun works correctly! */
 +    if (PAR(cr) && EI_VV(inputrec->eI) && ((Flags & MD_RERUN) || (Flags & MD_RERUN_VSITE)))
 +    {
 +        gmx_fatal(FARGS,
 +                  "Currently can't do velocity verlet with rerun in parallel.");
 +    }
 +
 +    /* A parallel command line option consistency check that we can
 +       only do after any threads have started. */
 +    if (!PAR(cr) &&
 +        (ddxyz[XX] > 1 || ddxyz[YY] > 1 || ddxyz[ZZ] > 1 || cr->npmenodes > 0))
 +    {
 +        gmx_fatal(FARGS,
 +                  "The -dd or -npme option request a parallel simulation, "
 +#ifndef GMX_MPI
 +                  "but mdrun was compiled without threads or MPI enabled"
 +#else
 +#ifdef GMX_THREAD_MPI
 +                  "but the number of threads (option -nt) is 1"
 +#else
 +                  "but mdrun was not started through mpirun/mpiexec or only one process was requested through mpirun/mpiexec" 
 +#endif
 +#endif
 +            );
 +    }
 +
 +    if ((Flags & MD_RERUN) &&
 +        (EI_ENERGY_MINIMIZATION(inputrec->eI) || eiNM == inputrec->eI))
 +    {
 +        gmx_fatal(FARGS, "The .mdp file specified an energy mininization or normal mode algorithm, and these are not compatible with mdrun -rerun");
 +    }
 +
 +    if (can_use_allvsall(inputrec,mtop,TRUE,cr,fplog))
 +    {
 +        /* All-vs-all loops do not work with domain decomposition */
 +        Flags |= MD_PARTDEC;
 +    }
 +
 +    if (!EEL_PME(inputrec->coulombtype) || (Flags & MD_PARTDEC))
 +    {
 +        if (cr->npmenodes > 0)
 +        {
 +            if (!EEL_PME(inputrec->coulombtype))
 +            {
 +                gmx_fatal_collective(FARGS,cr,NULL,
 +                                     "PME nodes are requested, but the system does not use PME electrostatics");
 +            }
 +            if (Flags & MD_PARTDEC)
 +            {
 +                gmx_fatal_collective(FARGS,cr,NULL,
 +                                     "PME nodes are requested, but particle decomposition does not support separate PME nodes");
 +            }
 +        }
 +
 +        cr->npmenodes = 0;
 +    }
 +
 +#ifdef GMX_FAHCORE
 +    fcRegisterSteps(inputrec->nsteps,inputrec->init_step);
 +#endif
 +
 +    /* NMR restraints must be initialized before load_checkpoint,
 +     * since with time averaging the history is added to t_state.
 +     * For proper consistency check we therefore need to extend
 +     * t_state here.
 +     * So the PME-only nodes (if present) will also initialize
 +     * the distance restraints.
 +     */
 +    snew(fcd,1);
 +
 +    /* This needs to be called before read_checkpoint to extend the state */
 +    init_disres(fplog,mtop,inputrec,cr,Flags & MD_PARTDEC,fcd,state);
 +
 +    if (gmx_mtop_ftype_count(mtop,F_ORIRES) > 0)
 +    {
 +        if (PAR(cr) && !(Flags & MD_PARTDEC))
 +        {
 +            gmx_fatal(FARGS,"Orientation restraints do not work (yet) with domain decomposition, use particle decomposition (mdrun option -pd)");
 +        }
 +        /* Orientation restraints */
 +        if (MASTER(cr))
 +        {
 +            init_orires(fplog,mtop,state->x,inputrec,cr->ms,&(fcd->orires),
 +                        state);
 +        }
 +    }
 +
 +    if (DEFORM(*inputrec))
 +    {
 +        /* Store the deform reference box before reading the checkpoint */
 +        if (SIMMASTER(cr))
 +        {
 +            copy_mat(state->box,box);
 +        }
 +        if (PAR(cr))
 +        {
 +            gmx_bcast(sizeof(box),box,cr);
 +        }
 +        /* Because we do not have the update struct available yet
 +         * in which the reference values should be stored,
 +         * we store them temporarily in static variables.
 +         * This should be thread safe, since they are only written once
 +         * and with identical values.
 +         */
 +#ifdef GMX_THREAD_MPI
 +        tMPI_Thread_mutex_lock(&deform_init_box_mutex);
 +#endif
 +        deform_init_init_step_tpx = inputrec->init_step;
 +        copy_mat(box,deform_init_box_tpx);
 +#ifdef GMX_THREAD_MPI
 +        tMPI_Thread_mutex_unlock(&deform_init_box_mutex);
 +#endif
 +    }
 +
 +    if (opt2bSet("-cpi",nfile,fnm)) 
 +    {
 +        /* Check if checkpoint file exists before doing continuation.
 +         * This way we can use identical input options for the first and subsequent runs...
 +         */
 +        if( gmx_fexist_master(opt2fn_master("-cpi",nfile,fnm,cr),cr) )
 +        {
 +            load_checkpoint(opt2fn_master("-cpi",nfile,fnm,cr),&fplog,
 +                            cr,Flags & MD_PARTDEC,ddxyz,
 +                            inputrec,state,&bReadRNG,&bReadEkin,
 +                            (Flags & MD_APPENDFILES),
 +                            (Flags & MD_APPENDFILESSET));
 +            
 +            if (bReadRNG)
 +            {
 +                Flags |= MD_READ_RNG;
 +            }
 +            if (bReadEkin)
 +            {
 +                Flags |= MD_READ_EKIN;
 +            }
 +        }
 +    }
 +
 +    if (((MASTER(cr) || (Flags & MD_SEPPOT)) && (Flags & MD_APPENDFILES))
 +#ifdef GMX_THREAD_MPI
 +        /* With thread MPI only the master node/thread exists in mdrun.c,
 +         * therefore non-master nodes need to open the "seppot" log file here.
 +         */
 +        || (!MASTER(cr) && (Flags & MD_SEPPOT))
 +#endif
 +        )
 +    {
 +        gmx_log_open(ftp2fn(efLOG,nfile,fnm),cr,!(Flags & MD_SEPPOT),
 +                             Flags,&fplog);
 +    }
 +
 +    if (SIMMASTER(cr)) 
 +    {
 +        copy_mat(state->box,box);
 +    }
 +
 +    if (PAR(cr)) 
 +    {
 +        gmx_bcast(sizeof(box),box,cr);
 +    }
 +
 +    /* Essential dynamics */
 +    if (opt2bSet("-ei",nfile,fnm))
 +    {
 +        /* Open input and output files, allocate space for ED data structure */
 +        ed = ed_open(nfile,fnm,Flags,cr);
 +    }
 +
 +    if (bVerbose && SIMMASTER(cr))
 +    {
 +        fprintf(stderr,"Loaded with Money\n\n");
 +    }
 +
 +    if (PAR(cr) && !((Flags & MD_PARTDEC) ||
 +                     EI_TPI(inputrec->eI) ||
 +                     inputrec->eI == eiNM))
 +    {
 +        cr->dd = init_domain_decomposition(fplog,cr,Flags,ddxyz,rdd,rconstr,
 +                                           dddlb_opt,dlb_scale,
 +                                           ddcsx,ddcsy,ddcsz,
 +                                           mtop,inputrec,
 +                                           box,state->x,
 +                                           &ddbox,&npme_major,&npme_minor);
 +
 +        make_dd_communicators(fplog,cr,dd_node_order);
 +
 +        /* Set overallocation to avoid frequent reallocation of arrays */
 +        set_over_alloc_dd(TRUE);
 +    }
 +    else
 +    {
 +        /* PME, if used, is done on all nodes with 1D decomposition */
 +        cr->npmenodes = 0;
 +        cr->duty = (DUTY_PP | DUTY_PME);
 +        npme_major = 1;
 +        npme_minor = 1;
 +        if (!EI_TPI(inputrec->eI))
 +        {
 +            npme_major = cr->nnodes;
 +        }
 +        
 +        if (inputrec->ePBC == epbcSCREW)
 +        {
 +            gmx_fatal(FARGS,
 +                      "pbc=%s is only implemented with domain decomposition",
 +                      epbc_names[inputrec->ePBC]);
 +        }
 +    }
 +
 +    if (PAR(cr))
 +    {
 +        /* After possible communicator splitting in make_dd_communicators.
 +         * we can set up the intra/inter node communication.
 +         */
 +        gmx_setup_nodecomm(fplog,cr);
 +    }
 +
 +    /* get number of OpenMP/PME threads
 +     * env variable should be read only on one node to make sure it is identical everywhere */
 +#ifdef GMX_OPENMP
 +    if (EEL_PME(inputrec->coulombtype))
 +    {
 +        if (MASTER(cr))
 +        {
 +            char *ptr;
 +            if ((ptr=getenv("GMX_PME_NTHREADS")) != NULL)
 +            {
 +                sscanf(ptr,"%d",&nthreads_pme);
 +            }
 +            if (fplog != NULL && nthreads_pme > 1)
 +            {
 +                fprintf(fplog,"Using %d threads for PME\n",nthreads_pme);
 +            }
 +        }
 +        if (PAR(cr))
 +        {
 +            gmx_bcast_sim(sizeof(nthreads_pme),&nthreads_pme,cr);
 +        }
 +    }
 +#endif
 +
 +    wcycle = wallcycle_init(fplog,resetstep,cr,nthreads_pme);
 +    if (PAR(cr))
 +    {
 +        /* Master synchronizes its value of reset_counters with all nodes 
 +         * including PME only nodes */
 +        reset_counters = wcycle_get_reset_counters(wcycle);
 +        gmx_bcast_sim(sizeof(reset_counters),&reset_counters,cr);
 +        wcycle_set_reset_counters(wcycle, reset_counters);
 +    }
 +
 +
 +    snew(nrnb,1);
 +    if (cr->duty & DUTY_PP)
 +    {
 +        /* For domain decomposition we allocate dynamically
 +         * in dd_partition_system.
 +         */
 +        if (DOMAINDECOMP(cr))
 +        {
 +            bcast_state_setup(cr,state);
 +        }
 +        else
 +        {
 +            if (PAR(cr))
 +            {
 +                bcast_state(cr,state,TRUE);
 +            }
 +        }
 +
 +        /* Initiate forcerecord */
 +        fr = mk_forcerec();
 +        init_forcerec(fplog,oenv,fr,fcd,inputrec,mtop,cr,box,FALSE,
 +                      opt2fn("-table",nfile,fnm),
 +                      opt2fn("-tabletf",nfile,fnm),
 +                      opt2fn("-tablep",nfile,fnm),
 +                      opt2fn("-tableb",nfile,fnm),FALSE,pforce);
 +
 +        /* version for PCA_NOT_READ_NODE (see md.c) */
 +        /*init_forcerec(fplog,fr,fcd,inputrec,mtop,cr,box,FALSE,
 +          "nofile","nofile","nofile","nofile",FALSE,pforce);
 +          */        
 +        fr->bSepDVDL = ((Flags & MD_SEPPOT) == MD_SEPPOT);
 +
 +        /* Initialize QM-MM */
 +        if(fr->bQMMM)
 +        {
 +            init_QMMMrec(cr,box,mtop,inputrec,fr);
 +        }
 +
 +        /* Initialize the mdatoms structure.
 +         * mdatoms is not filled with atom data,
 +         * as this can not be done now with domain decomposition.
 +         */
 +        mdatoms = init_mdatoms(fplog,mtop,inputrec->efep!=efepNO);
 +
 +        /* Initialize the virtual site communication */
 +        vsite = init_vsite(mtop,cr);
 +
 +        calc_shifts(box,fr->shift_vec);
 +
 +        /* With periodic molecules the charge groups should be whole at start up
 +         * and the virtual sites should not be far from their proper positions.
 +         */
 +        if (!inputrec->bContinuation && MASTER(cr) &&
 +            !(inputrec->ePBC != epbcNONE && inputrec->bPeriodicMols))
 +        {
 +            /* Make molecules whole at start of run */
 +            if (fr->ePBC != epbcNONE)
 +            {
 +                do_pbc_first_mtop(fplog,inputrec->ePBC,box,mtop,state->x);
 +            }
 +            if (vsite)
 +            {
 +                /* Correct initial vsite positions are required
 +                 * for the initial distribution in the domain decomposition
 +                 * and for the initial shell prediction.
 +                 */
 +                construct_vsites_mtop(fplog,vsite,mtop,state->x);
 +            }
 +        }
 +
 +        if (EEL_PME(fr->eeltype))
 +        {
 +            ewaldcoeff = fr->ewaldcoeff;
 +            pmedata = &fr->pmedata;
 +        }
 +        else
 +        {
 +            pmedata = NULL;
 +        }
 +    }
 +    else
 +    {
 +        /* This is a PME only node */
 +
 +        /* We don't need the state */
 +        done_state(state);
 +
 +        ewaldcoeff = calc_ewaldcoeff(inputrec->rcoulomb, inputrec->ewald_rtol);
 +        snew(pmedata,1);
 +    }
 +
 +    /* Initiate PME if necessary,
 +     * either on all nodes or on dedicated PME nodes only. */
 +    if (EEL_PME(inputrec->coulombtype))
 +    {
 +        if (mdatoms)
 +        {
 +            nChargePerturbed = mdatoms->nChargePerturbed;
 +        }
 +        if (cr->npmenodes > 0)
 +        {
 +            /* The PME only nodes need to know nChargePerturbed */
 +            gmx_bcast_sim(sizeof(nChargePerturbed),&nChargePerturbed,cr);
 +        }
 +
 +
 +        /* Set CPU affinity. Can be important for performance.
 +           On some systems (e.g. Cray) CPU Affinity is set by default.
 +           But default assigning doesn't work (well) with only some ranks
 +           having threads. This causes very low performance.
 +           External tools have cumbersome syntax for setting affinity
 +           in the case that only some ranks have threads.
 +           Thus it is important that GROMACS sets the affinity internally at
 +           if only PME is using threads.
 +        */
 +
 +#ifdef GMX_OPENMP
 +#ifdef __linux
 +#ifdef GMX_LIB_MPI
 +        {
 +            int core;
 +            MPI_Comm comm_intra; /* intra communicator (but different to nc.comm_intra includes PME nodes) */
 +            MPI_Comm_split(MPI_COMM_WORLD,gmx_hostname_num(),gmx_node_rank(),&comm_intra);
 +            int local_omp_nthreads = (cr->duty & DUTY_PME) ? nthreads_pme : 1; /* threads on this node */
 +            MPI_Scan(&local_omp_nthreads,&core, 1, MPI_INT, MPI_SUM, comm_intra);
 +            core-=local_omp_nthreads; /* make exclusive scan */
 +#pragma omp parallel firstprivate(core) num_threads(local_omp_nthreads)
 +            {
 +                cpu_set_t mask;
 +                CPU_ZERO(&mask);
 +                core+=gmx_omp_get_thread_num();
 +                CPU_SET(core,&mask);
 +                sched_setaffinity((pid_t) syscall (SYS_gettid),sizeof(cpu_set_t),&mask);
 +            }
 +        }
 +#endif /*GMX_MPI*/
 +#endif /*__linux*/
 +#endif /*GMX_OPENMP*/
 +
 +        if (cr->duty & DUTY_PME)
 +        {
 +            status = gmx_pme_init(pmedata,cr,npme_major,npme_minor,inputrec,
 +                                  mtop ? mtop->natoms : 0,nChargePerturbed,
 +                                  (Flags & MD_REPRODUCIBLE),nthreads_pme);
 +            if (status != 0) 
 +            {
 +                gmx_fatal(FARGS,"Error %d initializing PME",status);
 +            }
 +        }
 +    }
 +
 +
 +    if (integrator[inputrec->eI].func == do_md
 +#ifdef GMX_OPENMM
 +        ||
 +        integrator[inputrec->eI].func == do_md_openmm
 +#endif
 +        )
 +    {
 +        /* Turn on signal handling on all nodes */
 +        /*
 +         * (A user signal from the PME nodes (if any)
 +         * is communicated to the PP nodes.
 +         */
 +        signal_handler_install();
 +    }
 +
 +    if (cr->duty & DUTY_PP)
 +    {
 +        if (inputrec->ePull != epullNO)
 +        {
 +            /* Initialize pull code */
 +            init_pull(fplog,inputrec,nfile,fnm,mtop,cr,oenv, inputrec->fepvals->init_lambda,
 +                      EI_DYNAMICS(inputrec->eI) && MASTER(cr),Flags);
 +        }
 +        
 +        if (inputrec->bRot)
 +        {
 +           /* Initialize enforced rotation code */
++           init_rot(fplog,inputrec,nfile,fnm,cr,state->x,box,mtop,oenv,
 +                    bVerbose,Flags);
 +        }
 +
 +        constr = init_constraints(fplog,mtop,inputrec,ed,state,cr);
 +
 +        if (DOMAINDECOMP(cr))
 +        {
 +            dd_init_bondeds(fplog,cr->dd,mtop,vsite,constr,inputrec,
 +                            Flags & MD_DDBONDCHECK,fr->cginfo_mb);
 +
 +            set_dd_parameters(fplog,cr->dd,dlb_scale,inputrec,fr,&ddbox);
 +
 +            setup_dd_grid(fplog,cr->dd);
 +        }
 +
 +        /* Now do whatever the user wants us to do (how flexible...) */
 +        integrator[inputrec->eI].func(fplog,cr,nfile,fnm,
 +                                      oenv,bVerbose,bCompact,
 +                                      nstglobalcomm,
 +                                      vsite,constr,
 +                                      nstepout,inputrec,mtop,
 +                                      fcd,state,
 +                                      mdatoms,nrnb,wcycle,ed,fr,
 +                                      repl_ex_nst,repl_ex_nex,repl_ex_seed,
 +                                      membed,
 +                                      cpt_period,max_hours,
 +                                      deviceOptions,
 +                                      Flags,
 +                                      &runtime);
 +
 +        if (inputrec->ePull != epullNO)
 +        {
 +            finish_pull(fplog,inputrec->pull);
 +        }
 +        
 +        if (inputrec->bRot)
 +        {
 +            finish_rot(fplog,inputrec->rot);
 +        }
 +
 +    } 
 +    else 
 +    {
 +        /* do PME only */
 +        gmx_pmeonly(*pmedata,cr,nrnb,wcycle,ewaldcoeff,FALSE,inputrec);
 +    }
 +
 +    if (EI_DYNAMICS(inputrec->eI) || EI_TPI(inputrec->eI))
 +    {
 +        /* Some timing stats */  
 +        if (SIMMASTER(cr))
 +        {
 +            if (runtime.proc == 0)
 +            {
 +                runtime.proc = runtime.real;
 +            }
 +        }
 +        else
 +        {
 +            runtime.real = 0;
 +        }
 +    }
 +
 +    wallcycle_stop(wcycle,ewcRUN);
 +
 +    /* Finish up, write some stuff
 +     * if rerunMD, don't write last frame again 
 +     */
 +    finish_run(fplog,cr,ftp2fn(efSTO,nfile,fnm),
 +               inputrec,nrnb,wcycle,&runtime,
 +               EI_DYNAMICS(inputrec->eI) && !MULTISIM(cr));
 +
 +    if (opt2bSet("-membed",nfile,fnm))
 +    {
 +        sfree(membed);
 +    }
 +
 +    /* Does what it says */  
 +    print_date_and_time(fplog,cr->nodeid,"Finished mdrun",&runtime);
 +
 +    /* Close logfile already here if we were appending to it */
 +    if (MASTER(cr) && (Flags & MD_APPENDFILES))
 +    {
 +        gmx_log_close(fplog);
 +    } 
 +
 +    rc=(int)gmx_get_stop_condition();
 +
 +#ifdef GMX_THREAD_MPI
 +    /* we need to join all threads. The sub-threads join when they
 +       exit this function, but the master thread needs to be told to 
 +       wait for that. */
 +    if (PAR(cr) && MASTER(cr))
 +    {
 +        tMPI_Finalize();
 +    }
 +#endif
 +
 +    return rc;
 +}
Simple merge
Simple merge