Merge release-4-6 into release-5-0
authorRoland Schulz <roland@utk.edu>
Fri, 27 Jun 2014 23:22:28 +0000 (19:22 -0400)
committerRoland Schulz <roland@utk.edu>
Fri, 27 Jun 2014 23:22:28 +0000 (19:22 -0400)
Conflicts:
CMakeLists.txt
share/top/oplsaa.ff/watermodels.dat
src/gromacs/fileio/trxio.c
src/gromacs/gmxana/anadih.c
src/gromacs/gmxana/gmx_chi.c
src/gromacs/gmxana/gmx_tcaf.c
src/gromacs/gmxlib/checkpoint.c
src/gromacs/legacyheaders/tables.h
src/gromacs/mdlib/shellfc.c
src/gromacs/mdlib/tables.c
src/gromacs/pulling/pull.c
src/kernel/readpull.c -> applied to
             src/gromacs/gmxpreprocess/readpull.c
src/programs/mdrun/repl_ex.c

Change ignored because was already merged:
       src/gromacs/mdlib/sim_util.c

Deleted. No merge:
src/gmxlib/gpu_utils/dummy.cpp

Removal of INSTALL_NAME_DIR not necessary because
was already not there anymore:
src/gmxlib/CMakeLists.txt
src/tools/CMakeLists.txt
src/kernel/CMakeLists.txt
src/mdlib/CMakeLists.txt

Tools deleted - no merge:
src/tools/gmx_bond.c
src/tools/gmx_kinetics.c

Change-Id: Ibeb5a23cce94fb1765ebee271c9c92e001b83630

44 files changed:
1  2 
CMakeLists.txt
cmake/gmxManageGPU.cmake
cmake/gmxManageNvccConfig.cmake
share/top/charmm27.ff/ffnonbonded.itp
share/top/oplsaa.ff/watermodels.dat
src/gromacs/fileio/libxdrf.c
src/gromacs/fileio/trx.h
src/gromacs/fileio/trxio.c
src/gromacs/gmxana/anadih.c
src/gromacs/gmxana/gmx_anaeig.c
src/gromacs/gmxana/gmx_analyze.c
src/gromacs/gmxana/gmx_angle.c
src/gromacs/gmxana/gmx_chi.c
src/gromacs/gmxana/gmx_cluster.c
src/gromacs/gmxana/gmx_dos.c
src/gromacs/gmxana/gmx_enemat.c
src/gromacs/gmxana/gmx_energy.c
src/gromacs/gmxana/gmx_genpr.c
src/gromacs/gmxana/gmx_mdmat.c
src/gromacs/gmxana/gmx_mindist.c
src/gromacs/gmxana/gmx_polystat.c
src/gromacs/gmxana/gmx_rama.c
src/gromacs/gmxana/gmx_rms.c
src/gromacs/gmxana/gmx_tcaf.c
src/gromacs/gmxana/gmx_vanhove.c
src/gromacs/gmxana/gmx_wham.cpp
src/gromacs/gmxlib/bondfree.c
src/gromacs/gmxlib/checkpoint.c
src/gromacs/gmxpreprocess/grompp.c
src/gromacs/gmxpreprocess/readpull.c
src/gromacs/gmxpreprocess/toppush.c
src/gromacs/legacyheaders/oenv.h
src/gromacs/legacyheaders/shellfc.h
src/gromacs/legacyheaders/tables.h
src/gromacs/mdlib/pme.c
src/gromacs/mdlib/shellfc.c
src/gromacs/mdlib/tables.c
src/gromacs/mdlib/update.c
src/gromacs/mdlib/wall.c
src/gromacs/pulling/pull.c
src/gromacs/tools/compare.c
src/programs/mdrun/md.c
src/programs/mdrun/mdrun.cpp
src/programs/mdrun/repl_ex.c

diff --cc CMakeLists.txt
index 3d3c3f6791d8b02f54d611db0f1a1ea49e81d243,d575ab6e7a6b8cce79482aacbfd3e8feaab36329..ba6b3115054b053860b77d5e8747777477f89aeb
@@@ -796,28 -1162,55 +796,34 @@@ set(DATA_INSTALL_DIR ${GMX_INSTALL_PREF
  set(MAN_INSTALL_DIR  ${GMX_INSTALL_PREFIX}share/man)
  set(INCL_INSTALL_DIR ${GMX_INSTALL_PREFIX}include)
  
 -set(GMXLIBDIR        ${DATA_INSTALL_DIR}/top)
 +set(GMXLIB_SEARCH_DIR share/${GMX_DATA_INSTALL_DIR}/top)
 +set(GMXLIB_FALLBACK   ${CMAKE_INSTALL_PREFIX}/${DATA_INSTALL_DIR}/top)
  
 -########################################################################
 -# Set up binary and library suffixing
 -########################################################################
 -set(GMX_BINARY_SUFFIX "" CACHE STRING "Suffix for GROMACS binaries (default: _d for double, _mpi for MPI, _mpi_d for MPI and double).")
 -set(GMX_LIBS_SUFFIX ""
 -  CACHE STRING "Suffix for GROMACS libs (default: _d for double, _mpi for MPI, _mpi_d for MPI and double).")
 -if (GMX_DEFAULT_SUFFIX)
 -  set(GMX_BINARY_SUFFIX "")
 -  set(GMX_LIBS_SUFFIX "")
 -  if (GMX_LIB_MPI)
 -    set(GMX_BINARY_SUFFIX "_mpi")
 -    set(GMX_LIBS_SUFFIX "_mpi")
 -  endif()
 -  if (GMX_DOUBLE)
 -    set (GMX_BINARY_SUFFIX "${GMX_BINARY_SUFFIX}_d")
 -    set (GMX_LIBS_SUFFIX "${GMX_LIBS_SUFFIX}_d")
 -  endif(GMX_DOUBLE)
 -  mark_as_advanced(FORCE GMX_BINARY_SUFFIX GMX_LIBS_SUFFIX)
 -  if (NOT SUFFIX_QUIETLY)
 -    message(STATUS "Using default binary suffix: \"${GMX_BINARY_SUFFIX}\"")
 -    message(STATUS "Using default library suffix: \"${GMX_LIBS_SUFFIX}\"")
 -  endif (NOT SUFFIX_QUIETLY)
 -else(GMX_DEFAULT_SUFFIX)
 -  mark_as_advanced(CLEAR GMX_BINARY_SUFFIX GMX_LIBS_SUFFIX)
 -  if (NOT SUFFIX_QUIETLY)
 -    message(STATUS "Using manually set binary suffix: \"${GMX_BINARY_SUFFIX}\"")
 -    message(STATUS "Using manually set library suffix: \"${GMX_LIBS_SUFFIX}\"")
 -  endif (NOT SUFFIX_QUIETLY)
 -endif(GMX_DEFAULT_SUFFIX)
 -set(SUFFIX_QUIETLY TRUE CACHE INTERNAL "")
 +# Binary and library suffix options
 +include(gmxManageSuffixes)
  
- ##################################################################
+ ################################################################
  # Shared library settings
- ##################################################################
- if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+ ################################################################
+ if((NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") OR ((CMAKE_SYSTEM_VERSION VERSION_GREATER 8.0) AND (CMAKE_VERSION VERSION_GREATER 2.8.11)))
 -    set(CMAKE_SKIP_BUILD_RPATH  FALSE)
 -    set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
 +    if(GMX_LIB_INSTALL_DIR STREQUAL "lib")
 +        set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
 +    endif()
-     set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${GMX_LIB_INSTALL_DIR}")
+     if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin")
 -        set(CMAKE_INSTALL_RPATH "\\\$ORIGIN/../${GMXLIB}")
++        set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${GMX_LIB_INSTALL_DIR}")
+     else()
 -        set(CMAKE_INSTALL_RPATH "@executable_path/../${GMXLIB}")
++        set(CMAKE_INSTALL_RPATH "@executable_path/../${GMX_LIB_INSTALL_DIR}")
+     endif()
      set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
+     set(CMAKE_MACOSX_RPATH 1)
  else()
 -    set(CMAKE_INSTALL_NAME_DIR "${LIB_INSTALL_DIR}")
+     # We are on Darwin/OSX, and cmake cannot handle proper RPATHs
 +    if(CMAKE_SYSTEM_VERSION VERSION_GREATER 8.0) #rpath supported for >10.4
 +        set(CMAKE_INSTALL_NAME_DIR "@rpath")
 +        set(GMX_EXE_LINKER_FLAGS ${GMX_EXE_LINKER_FLAGS} "-Wl,-rpath,@executable_path/../${GMX_LIB_INSTALL_DIR}")
 +    else()
 +        set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}")
 +    endif()
  endif()
  
  #COPYING file: Only necessary for binary distributions.
index 569f028de41446f8ae36712c71a51b9494ca9b08,b8a610b56f7b1a20f94d9c59c64dec8f97014b0b..b7761de7f5ad1dd9523fe0a4d4170808ebf5e16b
@@@ -68,8 -68,24 +68,24 @@@ if(GMX_GPU OR GMX_GPU_AUTO
      if(MSVC)
          find_package(CUDA 4.1 ${FIND_CUDA_QUIETLY})
      else()
 -        find_package(CUDA 3.2 ${FIND_CUDA_QUIETLY})
 +        find_package(CUDA 4.0 ${FIND_CUDA_QUIETLY})
      endif()
+     # Cmake 2.8.12 (and CMake 3.0) introduced a new bug where the cuda
+     # library dir is added twice as an rpath on APPLE, which in turn causes
+     # the install_name_tool to wreck the binaries when it tries to remove this
+     # path. Since this is set inside the cuda module, we remove the extra rpath
+     # added in the library string - an rpath is not a library anyway, and at
+     # least for Gromacs this works on all CMake versions. This should be
+     # reasonably future-proof, since newer versions of CMake appear to handle
+     # the rpath automatically based on the provided library path, meaning
+     # the explicit rpath specification is no longer needed.
+     if(APPLE AND (CMAKE_VERSION VERSION_GREATER 2.8.11))
+         foreach(elem ${CUDA_LIBRARIES})
+             if(elem MATCHES "-Wl,.*")
+                 list(REMOVE_ITEM CUDA_LIBRARIES ${elem})
+             endif()
+         endforeach(elem)
+     endif()
  endif()
  
  # Depending on the current vale of GMX_GPU and GMX_GPU_AUTO:
Simple merge
Simple merge
index 44f5e9270a7602129556ac13a010a99016551c58,cf93b0691c63d346869d73f24009af585d689c8e..420bb9225a6f49715a12eb298cc276eb6244b75e
@@@ -1,6 -1,5 +1,6 @@@
  tip4p   TIP4P  TIP 4-point, recommended
  tip3p   TIP3P  TIP 3-point
- tip5p   TIP5P  TIP 5-point
+ tip5p   TIP5P  TIP 5-point (see http://redmine.gromacs.org/issues/1348 for issues)
 +tip5pe  TIP5P  TIP 5-point improved for Ewald sums
  spc     SPC    simple point charge
  spce    SPC/E  extended simple point charge
index 14c750190067f0181998b79d4d972f06d210c32a,0000000000000000000000000000000000000000..d83d7e6bd191e45f007e10cf8460ca4a90c0e5c6
mode 100644,000000..100644
--- /dev/null
@@@ -1,1667 -1,0 +1,1667 @@@
-         low = gmx_ftell(fp);
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <limits.h>
 +#include <math.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include "xdrf.h"
 +#include "xdr_datatype.h"
 +#include "futil.h"
 +
 +/* This is just for clarity - it can never be anything but 4! */
 +#define XDR_INT_SIZE 4
 +
 +/* same order as the definition of xdr_datatype */
 +const char *xdr_datatype_names[] =
 +{
 +    "int",
 +    "float",
 +    "double",
 +    "large int",
 +    "char",
 +    "string"
 +};
 +
 +
 +/*___________________________________________________________________________
 + |
 + | what follows are the C routine to read/write compressed coordinates together
 + | with some routines to assist in this task (those are marked
 + | static and cannot be called from user programs)
 + */
 +#define MAXABS INT_MAX-2
 +
 +#ifndef MIN
 +#define MIN(x, y) ((x) < (y) ? (x) : (y))
 +#endif
 +#ifndef MAX
 +#define MAX(x, y) ((x) > (y) ? (x) : (y))
 +#endif
 +#ifndef SQR
 +#define SQR(x) ((x)*(x))
 +#endif
 +static const int magicints[] = {
 +    0, 0, 0, 0, 0, 0, 0, 0, 0,
 +    8, 10, 12, 16, 20, 25, 32, 40, 50, 64,
 +    80, 101, 128, 161, 203, 256, 322, 406, 512, 645,
 +    812, 1024, 1290, 1625, 2048, 2580, 3250, 4096, 5060, 6501,
 +    8192, 10321, 13003, 16384, 20642, 26007, 32768, 41285, 52015, 65536,
 +    82570, 104031, 131072, 165140, 208063, 262144, 330280, 416127, 524287, 660561,
 +    832255, 1048576, 1321122, 1664510, 2097152, 2642245, 3329021, 4194304, 5284491, 6658042,
 +    8388607, 10568983, 13316085, 16777216
 +};
 +
 +#define FIRSTIDX 9
 +/* note that magicints[FIRSTIDX-1] == 0 */
 +#define LASTIDX (sizeof(magicints) / sizeof(*magicints))
 +
 +
 +/*____________________________________________________________________________
 + |
 + | sendbits - encode num into buf using the specified number of bits
 + |
 + | This routines appends the value of num to the bits already present in
 + | the array buf. You need to give it the number of bits to use and you
 + | better make sure that this number of bits is enough to hold the value
 + | Also num must be positive.
 + |
 + */
 +
 +static void sendbits(int buf[], int num_of_bits, int num)
 +{
 +
 +    unsigned int    cnt, lastbyte;
 +    int             lastbits;
 +    unsigned char * cbuf;
 +
 +    cbuf     = ((unsigned char *)buf) + 3 * sizeof(*buf);
 +    cnt      = (unsigned int) buf[0];
 +    lastbits = buf[1];
 +    lastbyte = (unsigned int) buf[2];
 +    while (num_of_bits >= 8)
 +    {
 +        lastbyte     = (lastbyte << 8) | ((num >> (num_of_bits -8)) /* & 0xff*/);
 +        cbuf[cnt++]  = lastbyte >> lastbits;
 +        num_of_bits -= 8;
 +    }
 +    if (num_of_bits > 0)
 +    {
 +        lastbyte  = (lastbyte << num_of_bits) | num;
 +        lastbits += num_of_bits;
 +        if (lastbits >= 8)
 +        {
 +            lastbits   -= 8;
 +            cbuf[cnt++] = lastbyte >> lastbits;
 +        }
 +    }
 +    buf[0] = cnt;
 +    buf[1] = lastbits;
 +    buf[2] = lastbyte;
 +    if (lastbits > 0)
 +    {
 +        cbuf[cnt] = lastbyte << (8 - lastbits);
 +    }
 +}
 +
 +/*_________________________________________________________________________
 + |
 + | sizeofint - calculate bitsize of an integer
 + |
 + | return the number of bits needed to store an integer with given max size
 + |
 + */
 +
 +static int sizeofint(const int size)
 +{
 +    int num         = 1;
 +    int num_of_bits = 0;
 +
 +    while (size >= num && num_of_bits < 32)
 +    {
 +        num_of_bits++;
 +        num <<= 1;
 +    }
 +    return num_of_bits;
 +}
 +
 +/*___________________________________________________________________________
 + |
 + | sizeofints - calculate 'bitsize' of compressed ints
 + |
 + | given the number of small unsigned integers and the maximum value
 + | return the number of bits needed to read or write them with the
 + | routines receiveints and sendints. You need this parameter when
 + | calling these routines. Note that for many calls I can use
 + | the variable 'smallidx' which is exactly the number of bits, and
 + | So I don't need to call 'sizeofints for those calls.
 + */
 +
 +static int sizeofints( const int num_of_ints, unsigned int sizes[])
 +{
 +    int          i, num;
 +    int          bytes[32];
 +    unsigned int num_of_bytes, num_of_bits, bytecnt, tmp;
 +    num_of_bytes = 1;
 +    bytes[0]     = 1;
 +    num_of_bits  = 0;
 +    for (i = 0; i < num_of_ints; i++)
 +    {
 +        tmp = 0;
 +        for (bytecnt = 0; bytecnt < num_of_bytes; bytecnt++)
 +        {
 +            tmp            = bytes[bytecnt] * sizes[i] + tmp;
 +            bytes[bytecnt] = tmp & 0xff;
 +            tmp          >>= 8;
 +        }
 +        while (tmp != 0)
 +        {
 +            bytes[bytecnt++] = tmp & 0xff;
 +            tmp            >>= 8;
 +        }
 +        num_of_bytes = bytecnt;
 +    }
 +    num = 1;
 +    num_of_bytes--;
 +    while (bytes[num_of_bytes] >= num)
 +    {
 +        num_of_bits++;
 +        num *= 2;
 +    }
 +    return num_of_bits + num_of_bytes * 8;
 +
 +}
 +
 +/*____________________________________________________________________________
 + |
 + | sendints - send a small set of small integers in compressed format
 + |
 + | this routine is used internally by xdr3dfcoord, to send a set of
 + | small integers to the buffer.
 + | Multiplication with fixed (specified maximum ) sizes is used to get
 + | to one big, multibyte integer. Allthough the routine could be
 + | modified to handle sizes bigger than 16777216, or more than just
 + | a few integers, this is not done, because the gain in compression
 + | isn't worth the effort. Note that overflowing the multiplication
 + | or the byte buffer (32 bytes) is unchecked and causes bad results.
 + |
 + */
 +
 +static void sendints(int buf[], const int num_of_ints, const int num_of_bits,
 +                     unsigned int sizes[], unsigned int nums[])
 +{
 +
 +    int          i, num_of_bytes, bytecnt;
 +    unsigned int bytes[32], tmp;
 +
 +    tmp          = nums[0];
 +    num_of_bytes = 0;
 +    do
 +    {
 +        bytes[num_of_bytes++] = tmp & 0xff;
 +        tmp                 >>= 8;
 +    }
 +    while (tmp != 0);
 +
 +    for (i = 1; i < num_of_ints; i++)
 +    {
 +        if (nums[i] >= sizes[i])
 +        {
 +            fprintf(stderr, "major breakdown in sendints num %u doesn't "
 +                    "match size %u\n", nums[i], sizes[i]);
 +            exit(1);
 +        }
 +        /* use one step multiply */
 +        tmp = nums[i];
 +        for (bytecnt = 0; bytecnt < num_of_bytes; bytecnt++)
 +        {
 +            tmp            = bytes[bytecnt] * sizes[i] + tmp;
 +            bytes[bytecnt] = tmp & 0xff;
 +            tmp          >>= 8;
 +        }
 +        while (tmp != 0)
 +        {
 +            bytes[bytecnt++] = tmp & 0xff;
 +            tmp            >>= 8;
 +        }
 +        num_of_bytes = bytecnt;
 +    }
 +    if (num_of_bits >= num_of_bytes * 8)
 +    {
 +        for (i = 0; i < num_of_bytes; i++)
 +        {
 +            sendbits(buf, 8, bytes[i]);
 +        }
 +        sendbits(buf, num_of_bits - num_of_bytes * 8, 0);
 +    }
 +    else
 +    {
 +        for (i = 0; i < num_of_bytes-1; i++)
 +        {
 +            sendbits(buf, 8, bytes[i]);
 +        }
 +        sendbits(buf, num_of_bits- (num_of_bytes -1) * 8, bytes[i]);
 +    }
 +}
 +
 +
 +/*___________________________________________________________________________
 + |
 + | receivebits - decode number from buf using specified number of bits
 + |
 + | extract the number of bits from the array buf and construct an integer
 + | from it. Return that value.
 + |
 + */
 +
 +static int receivebits(int buf[], int num_of_bits)
 +{
 +
 +    int             cnt, num, lastbits;
 +    unsigned int    lastbyte;
 +    unsigned char * cbuf;
 +    int             mask = (1 << num_of_bits) -1;
 +
 +    cbuf     = ((unsigned char *)buf) + 3 * sizeof(*buf);
 +    cnt      = buf[0];
 +    lastbits = (unsigned int) buf[1];
 +    lastbyte = (unsigned int) buf[2];
 +
 +    num = 0;
 +    while (num_of_bits >= 8)
 +    {
 +        lastbyte     = ( lastbyte << 8 ) | cbuf[cnt++];
 +        num         |=  (lastbyte >> lastbits) << (num_of_bits - 8);
 +        num_of_bits -= 8;
 +    }
 +    if (num_of_bits > 0)
 +    {
 +        if (lastbits < num_of_bits)
 +        {
 +            lastbits += 8;
 +            lastbyte  = (lastbyte << 8) | cbuf[cnt++];
 +        }
 +        lastbits -= num_of_bits;
 +        num      |= (lastbyte >> lastbits) & ((1 << num_of_bits) -1);
 +    }
 +    num   &= mask;
 +    buf[0] = cnt;
 +    buf[1] = lastbits;
 +    buf[2] = lastbyte;
 +    return num;
 +}
 +
 +/*____________________________________________________________________________
 + |
 + | receiveints - decode 'small' integers from the buf array
 + |
 + | this routine is the inverse from sendints() and decodes the small integers
 + | written to buf by calculating the remainder and doing divisions with
 + | the given sizes[]. You need to specify the total number of bits to be
 + | used from buf in num_of_bits.
 + |
 + */
 +
 +static void receiveints(int buf[], const int num_of_ints, int num_of_bits,
 +                        unsigned int sizes[], int nums[])
 +{
 +    int bytes[32];
 +    int i, j, num_of_bytes, p, num;
 +
 +    bytes[0]     = bytes[1] = bytes[2] = bytes[3] = 0;
 +    num_of_bytes = 0;
 +    while (num_of_bits > 8)
 +    {
 +        bytes[num_of_bytes++] = receivebits(buf, 8);
 +        num_of_bits          -= 8;
 +    }
 +    if (num_of_bits > 0)
 +    {
 +        bytes[num_of_bytes++] = receivebits(buf, num_of_bits);
 +    }
 +    for (i = num_of_ints-1; i > 0; i--)
 +    {
 +        num = 0;
 +        for (j = num_of_bytes-1; j >= 0; j--)
 +        {
 +            num      = (num << 8) | bytes[j];
 +            p        = num / sizes[i];
 +            bytes[j] = p;
 +            num      = num - p * sizes[i];
 +        }
 +        nums[i] = num;
 +    }
 +    nums[0] = bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
 +}
 +
 +/*____________________________________________________________________________
 + |
 + | xdr3dfcoord - read or write compressed 3d coordinates to xdr file.
 + |
 + | this routine reads or writes (depending on how you opened the file with
 + | xdropen() ) a large number of 3d coordinates (stored in *fp).
 + | The number of coordinates triplets to write is given by *size. On
 + | read this number may be zero, in which case it reads as many as were written
 + | or it may specify the number if triplets to read (which should match the
 + | number written).
 + | Compression is achieved by first converting all floating numbers to integer
 + | using multiplication by *precision and rounding to the nearest integer.
 + | Then the minimum and maximum value are calculated to determine the range.
 + | The limited range of integers so found, is used to compress the coordinates.
 + | In addition the differences between succesive coordinates is calculated.
 + | If the difference happens to be 'small' then only the difference is saved,
 + | compressing the data even more. The notion of 'small' is changed dynamically
 + | and is enlarged or reduced whenever needed or possible.
 + | Extra compression is achieved in the case of GROMOS and coordinates of
 + | water molecules. GROMOS first writes out the Oxygen position, followed by
 + | the two hydrogens. In order to make the differences smaller (and thereby
 + | compression the data better) the order is changed into first one hydrogen
 + | then the oxygen, followed by the other hydrogen. This is rather special, but
 + | it shouldn't harm in the general case.
 + |
 + */
 +
 +int xdr3dfcoord(XDR *xdrs, float *fp, int *size, float *precision)
 +{
 +    int     *ip  = NULL;
 +    int     *buf = NULL;
 +    gmx_bool bRead;
 +
 +    /* preallocate a small buffer and ip on the stack - if we need more
 +       we can always malloc(). This is faster for small values of size: */
 +    unsigned     prealloc_size = 3*16;
 +    int          prealloc_ip[3*16], prealloc_buf[3*20];
 +    int          we_should_free = 0;
 +
 +    int          minint[3], maxint[3], mindiff, *lip, diff;
 +    int          lint1, lint2, lint3, oldlint1, oldlint2, oldlint3, smallidx;
 +    int          minidx, maxidx;
 +    unsigned     sizeint[3], sizesmall[3], bitsizeint[3], size3, *luip;
 +    int          flag, k;
 +    int          smallnum, smaller, larger, i, is_small, is_smaller, run, prevrun;
 +    float       *lfp, lf;
 +    int          tmp, *thiscoord,  prevcoord[3];
 +    unsigned int tmpcoord[30];
 +
 +    int          bufsize, xdrid, lsize;
 +    unsigned int bitsize;
 +    float        inv_precision;
 +    int          errval = 1;
 +    int          rc;
 +
 +    bRead         = (xdrs->x_op == XDR_DECODE);
 +    bitsizeint[0] = bitsizeint[1] = bitsizeint[2] = 0;
 +    prevcoord[0]  = prevcoord[1]  = prevcoord[2]  = 0;
 +
 +    if (!bRead)
 +    {
 +        /* xdrs is open for writing */
 +
 +        if (xdr_int(xdrs, size) == 0)
 +        {
 +            return 0;
 +        }
 +        size3 = *size * 3;
 +        /* when the number of coordinates is small, don't try to compress; just
 +         * write them as floats using xdr_vector
 +         */
 +        if (*size <= 9)
 +        {
 +            return (xdr_vector(xdrs, (char *) fp, (unsigned int)size3,
 +                               (unsigned int)sizeof(*fp), (xdrproc_t)xdr_float));
 +        }
 +
 +        if (xdr_float(xdrs, precision) == 0)
 +        {
 +            return 0;
 +        }
 +
 +        if (size3 <= prealloc_size)
 +        {
 +            ip  = prealloc_ip;
 +            buf = prealloc_buf;
 +        }
 +        else
 +        {
 +            we_should_free = 1;
 +            bufsize        = size3 * 1.2;
 +            ip             = (int *)malloc((size_t)(size3 * sizeof(*ip)));
 +            buf            = (int *)malloc((size_t)(bufsize * sizeof(*buf)));
 +            if (ip == NULL || buf == NULL)
 +            {
 +                fprintf(stderr, "malloc failed\n");
 +                exit(1);
 +            }
 +        }
 +        /* buf[0-2] are special and do not contain actual data */
 +        buf[0]    = buf[1] = buf[2] = 0;
 +        minint[0] = minint[1] = minint[2] = INT_MAX;
 +        maxint[0] = maxint[1] = maxint[2] = INT_MIN;
 +        prevrun   = -1;
 +        lfp       = fp;
 +        lip       = ip;
 +        mindiff   = INT_MAX;
 +        oldlint1  = oldlint2 = oldlint3 = 0;
 +        while (lfp < fp + size3)
 +        {
 +            /* find nearest integer */
 +            if (*lfp >= 0.0)
 +            {
 +                lf = *lfp * *precision + 0.5;
 +            }
 +            else
 +            {
 +                lf = *lfp * *precision - 0.5;
 +            }
 +            if (fabs(lf) > MAXABS)
 +            {
 +                /* scaling would cause overflow */
 +                errval = 0;
 +            }
 +            lint1 = lf;
 +            if (lint1 < minint[0])
 +            {
 +                minint[0] = lint1;
 +            }
 +            if (lint1 > maxint[0])
 +            {
 +                maxint[0] = lint1;
 +            }
 +            *lip++ = lint1;
 +            lfp++;
 +            if (*lfp >= 0.0)
 +            {
 +                lf = *lfp * *precision + 0.5;
 +            }
 +            else
 +            {
 +                lf = *lfp * *precision - 0.5;
 +            }
 +            if (fabs(lf) > MAXABS)
 +            {
 +                /* scaling would cause overflow */
 +                errval = 0;
 +            }
 +            lint2 = lf;
 +            if (lint2 < minint[1])
 +            {
 +                minint[1] = lint2;
 +            }
 +            if (lint2 > maxint[1])
 +            {
 +                maxint[1] = lint2;
 +            }
 +            *lip++ = lint2;
 +            lfp++;
 +            if (*lfp >= 0.0)
 +            {
 +                lf = *lfp * *precision + 0.5;
 +            }
 +            else
 +            {
 +                lf = *lfp * *precision - 0.5;
 +            }
 +            if (fabs(lf) > MAXABS)
 +            {
 +                /* scaling would cause overflow */
 +                errval = 0;
 +            }
 +            lint3 = lf;
 +            if (lint3 < minint[2])
 +            {
 +                minint[2] = lint3;
 +            }
 +            if (lint3 > maxint[2])
 +            {
 +                maxint[2] = lint3;
 +            }
 +            *lip++ = lint3;
 +            lfp++;
 +            diff = abs(oldlint1-lint1)+abs(oldlint2-lint2)+abs(oldlint3-lint3);
 +            if (diff < mindiff && lfp > fp + 3)
 +            {
 +                mindiff = diff;
 +            }
 +            oldlint1 = lint1;
 +            oldlint2 = lint2;
 +            oldlint3 = lint3;
 +        }
 +        if ( (xdr_int(xdrs, &(minint[0])) == 0) ||
 +             (xdr_int(xdrs, &(minint[1])) == 0) ||
 +             (xdr_int(xdrs, &(minint[2])) == 0) ||
 +             (xdr_int(xdrs, &(maxint[0])) == 0) ||
 +             (xdr_int(xdrs, &(maxint[1])) == 0) ||
 +             (xdr_int(xdrs, &(maxint[2])) == 0))
 +        {
 +            if (we_should_free)
 +            {
 +                free(ip);
 +                free(buf);
 +            }
 +            return 0;
 +        }
 +
 +        if ((float)maxint[0] - (float)minint[0] >= MAXABS ||
 +            (float)maxint[1] - (float)minint[1] >= MAXABS ||
 +            (float)maxint[2] - (float)minint[2] >= MAXABS)
 +        {
 +            /* turning value in unsigned by subtracting minint
 +             * would cause overflow
 +             */
 +            errval = 0;
 +        }
 +        sizeint[0] = maxint[0] - minint[0]+1;
 +        sizeint[1] = maxint[1] - minint[1]+1;
 +        sizeint[2] = maxint[2] - minint[2]+1;
 +
 +        /* check if one of the sizes is to big to be multiplied */
 +        if ((sizeint[0] | sizeint[1] | sizeint[2] ) > 0xffffff)
 +        {
 +            bitsizeint[0] = sizeofint(sizeint[0]);
 +            bitsizeint[1] = sizeofint(sizeint[1]);
 +            bitsizeint[2] = sizeofint(sizeint[2]);
 +            bitsize       = 0; /* flag the use of large sizes */
 +        }
 +        else
 +        {
 +            bitsize = sizeofints(3, sizeint);
 +        }
 +        lip      = ip;
 +        luip     = (unsigned int *) ip;
 +        smallidx = FIRSTIDX;
 +        while (smallidx < LASTIDX && magicints[smallidx] < mindiff)
 +        {
 +            smallidx++;
 +        }
 +        if (xdr_int(xdrs, &smallidx) == 0)
 +        {
 +            if (we_should_free)
 +            {
 +                free(ip);
 +                free(buf);
 +            }
 +            return 0;
 +        }
 +
 +        maxidx       = MIN(LASTIDX, smallidx + 8);
 +        minidx       = maxidx - 8; /* often this equal smallidx */
 +        smaller      = magicints[MAX(FIRSTIDX, smallidx-1)] / 2;
 +        smallnum     = magicints[smallidx] / 2;
 +        sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx];
 +        larger       = magicints[maxidx] / 2;
 +        i            = 0;
 +        while (i < *size)
 +        {
 +            is_small  = 0;
 +            thiscoord = (int *)(luip) + i * 3;
 +            if (smallidx < maxidx && i >= 1 &&
 +                abs(thiscoord[0] - prevcoord[0]) < larger &&
 +                abs(thiscoord[1] - prevcoord[1]) < larger &&
 +                abs(thiscoord[2] - prevcoord[2]) < larger)
 +            {
 +                is_smaller = 1;
 +            }
 +            else if (smallidx > minidx)
 +            {
 +                is_smaller = -1;
 +            }
 +            else
 +            {
 +                is_smaller = 0;
 +            }
 +            if (i + 1 < *size)
 +            {
 +                if (abs(thiscoord[0] - thiscoord[3]) < smallnum &&
 +                    abs(thiscoord[1] - thiscoord[4]) < smallnum &&
 +                    abs(thiscoord[2] - thiscoord[5]) < smallnum)
 +                {
 +                    /* interchange first with second atom for better
 +                     * compression of water molecules
 +                     */
 +                    tmp          = thiscoord[0]; thiscoord[0] = thiscoord[3];
 +                    thiscoord[3] = tmp;
 +                    tmp          = thiscoord[1]; thiscoord[1] = thiscoord[4];
 +                    thiscoord[4] = tmp;
 +                    tmp          = thiscoord[2]; thiscoord[2] = thiscoord[5];
 +                    thiscoord[5] = tmp;
 +                    is_small     = 1;
 +                }
 +
 +            }
 +            tmpcoord[0] = thiscoord[0] - minint[0];
 +            tmpcoord[1] = thiscoord[1] - minint[1];
 +            tmpcoord[2] = thiscoord[2] - minint[2];
 +            if (bitsize == 0)
 +            {
 +                sendbits(buf, bitsizeint[0], tmpcoord[0]);
 +                sendbits(buf, bitsizeint[1], tmpcoord[1]);
 +                sendbits(buf, bitsizeint[2], tmpcoord[2]);
 +            }
 +            else
 +            {
 +                sendints(buf, 3, bitsize, sizeint, tmpcoord);
 +            }
 +            prevcoord[0] = thiscoord[0];
 +            prevcoord[1] = thiscoord[1];
 +            prevcoord[2] = thiscoord[2];
 +            thiscoord    = thiscoord + 3;
 +            i++;
 +
 +            run = 0;
 +            if (is_small == 0 && is_smaller == -1)
 +            {
 +                is_smaller = 0;
 +            }
 +            while (is_small && run < 8*3)
 +            {
 +                if (is_smaller == -1 && (
 +                        SQR(thiscoord[0] - prevcoord[0]) +
 +                        SQR(thiscoord[1] - prevcoord[1]) +
 +                        SQR(thiscoord[2] - prevcoord[2]) >= smaller * smaller))
 +                {
 +                    is_smaller = 0;
 +                }
 +
 +                tmpcoord[run++] = thiscoord[0] - prevcoord[0] + smallnum;
 +                tmpcoord[run++] = thiscoord[1] - prevcoord[1] + smallnum;
 +                tmpcoord[run++] = thiscoord[2] - prevcoord[2] + smallnum;
 +
 +                prevcoord[0] = thiscoord[0];
 +                prevcoord[1] = thiscoord[1];
 +                prevcoord[2] = thiscoord[2];
 +
 +                i++;
 +                thiscoord = thiscoord + 3;
 +                is_small  = 0;
 +                if (i < *size &&
 +                    abs(thiscoord[0] - prevcoord[0]) < smallnum &&
 +                    abs(thiscoord[1] - prevcoord[1]) < smallnum &&
 +                    abs(thiscoord[2] - prevcoord[2]) < smallnum)
 +                {
 +                    is_small = 1;
 +                }
 +            }
 +            if (run != prevrun || is_smaller != 0)
 +            {
 +                prevrun = run;
 +                sendbits(buf, 1, 1); /* flag the change in run-length */
 +                sendbits(buf, 5, run+is_smaller+1);
 +            }
 +            else
 +            {
 +                sendbits(buf, 1, 0); /* flag the fact that runlength did not change */
 +            }
 +            for (k = 0; k < run; k += 3)
 +            {
 +                sendints(buf, 3, smallidx, sizesmall, &tmpcoord[k]);
 +            }
 +            if (is_smaller != 0)
 +            {
 +                smallidx += is_smaller;
 +                if (is_smaller < 0)
 +                {
 +                    smallnum = smaller;
 +                    smaller  = magicints[smallidx-1] / 2;
 +                }
 +                else
 +                {
 +                    smaller  = smallnum;
 +                    smallnum = magicints[smallidx] / 2;
 +                }
 +                sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx];
 +            }
 +        }
 +        if (buf[1] != 0)
 +        {
 +            buf[0]++;
 +        }
 +        /* buf[0] holds the length in bytes */
 +        if (xdr_int(xdrs, &(buf[0])) == 0)
 +        {
 +            if (we_should_free)
 +            {
 +                free(ip);
 +                free(buf);
 +            }
 +            return 0;
 +        }
 +
 +
 +        rc = errval * (xdr_opaque(xdrs, (char *)&(buf[3]), (unsigned int)buf[0]));
 +        if (we_should_free)
 +        {
 +            free(ip);
 +            free(buf);
 +        }
 +        return rc;
 +
 +    }
 +    else
 +    {
 +
 +        /* xdrs is open for reading */
 +
 +        if (xdr_int(xdrs, &lsize) == 0)
 +        {
 +            return 0;
 +        }
 +        if (*size != 0 && lsize != *size)
 +        {
 +            fprintf(stderr, "wrong number of coordinates in xdr3dfcoord; "
 +                    "%d arg vs %d in file", *size, lsize);
 +        }
 +        *size = lsize;
 +        size3 = *size * 3;
 +        if (*size <= 9)
 +        {
 +            *precision = -1;
 +            return (xdr_vector(xdrs, (char *) fp, (unsigned int)size3,
 +                               (unsigned int)sizeof(*fp), (xdrproc_t)xdr_float));
 +        }
 +        if (xdr_float(xdrs, precision) == 0)
 +        {
 +            return 0;
 +        }
 +
 +        if (size3 <= prealloc_size)
 +        {
 +            ip  = prealloc_ip;
 +            buf = prealloc_buf;
 +        }
 +        else
 +        {
 +            we_should_free = 1;
 +            bufsize        = size3 * 1.2;
 +            ip             = (int *)malloc((size_t)(size3 * sizeof(*ip)));
 +            buf            = (int *)malloc((size_t)(bufsize * sizeof(*buf)));
 +            if (ip == NULL || buf == NULL)
 +            {
 +                fprintf(stderr, "malloc failed\n");
 +                exit(1);
 +            }
 +        }
 +
 +        buf[0] = buf[1] = buf[2] = 0;
 +
 +        if ( (xdr_int(xdrs, &(minint[0])) == 0) ||
 +             (xdr_int(xdrs, &(minint[1])) == 0) ||
 +             (xdr_int(xdrs, &(minint[2])) == 0) ||
 +             (xdr_int(xdrs, &(maxint[0])) == 0) ||
 +             (xdr_int(xdrs, &(maxint[1])) == 0) ||
 +             (xdr_int(xdrs, &(maxint[2])) == 0))
 +        {
 +            if (we_should_free)
 +            {
 +                free(ip);
 +                free(buf);
 +            }
 +            return 0;
 +        }
 +
 +        sizeint[0] = maxint[0] - minint[0]+1;
 +        sizeint[1] = maxint[1] - minint[1]+1;
 +        sizeint[2] = maxint[2] - minint[2]+1;
 +
 +        /* check if one of the sizes is to big to be multiplied */
 +        if ((sizeint[0] | sizeint[1] | sizeint[2] ) > 0xffffff)
 +        {
 +            bitsizeint[0] = sizeofint(sizeint[0]);
 +            bitsizeint[1] = sizeofint(sizeint[1]);
 +            bitsizeint[2] = sizeofint(sizeint[2]);
 +            bitsize       = 0; /* flag the use of large sizes */
 +        }
 +        else
 +        {
 +            bitsize = sizeofints(3, sizeint);
 +        }
 +
 +        if (xdr_int(xdrs, &smallidx) == 0)
 +        {
 +            if (we_should_free)
 +            {
 +                free(ip);
 +                free(buf);
 +            }
 +            return 0;
 +        }
 +
 +        maxidx       = MIN(LASTIDX, smallidx + 8);
 +        minidx       = maxidx - 8; /* often this equal smallidx */
 +        smaller      = magicints[MAX(FIRSTIDX, smallidx-1)] / 2;
 +        smallnum     = magicints[smallidx] / 2;
 +        sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx];
 +        larger       = magicints[maxidx];
 +
 +        /* buf[0] holds the length in bytes */
 +
 +        if (xdr_int(xdrs, &(buf[0])) == 0)
 +        {
 +            if (we_should_free)
 +            {
 +                free(ip);
 +                free(buf);
 +            }
 +            return 0;
 +        }
 +
 +
 +        if (xdr_opaque(xdrs, (char *)&(buf[3]), (unsigned int)buf[0]) == 0)
 +        {
 +            if (we_should_free)
 +            {
 +                free(ip);
 +                free(buf);
 +            }
 +            return 0;
 +        }
 +
 +
 +
 +        buf[0] = buf[1] = buf[2] = 0;
 +
 +        lfp           = fp;
 +        inv_precision = 1.0 / *precision;
 +        run           = 0;
 +        i             = 0;
 +        lip           = ip;
 +        while (i < lsize)
 +        {
 +            thiscoord = (int *)(lip) + i * 3;
 +
 +            if (bitsize == 0)
 +            {
 +                thiscoord[0] = receivebits(buf, bitsizeint[0]);
 +                thiscoord[1] = receivebits(buf, bitsizeint[1]);
 +                thiscoord[2] = receivebits(buf, bitsizeint[2]);
 +            }
 +            else
 +            {
 +                receiveints(buf, 3, bitsize, sizeint, thiscoord);
 +            }
 +
 +            i++;
 +            thiscoord[0] += minint[0];
 +            thiscoord[1] += minint[1];
 +            thiscoord[2] += minint[2];
 +
 +            prevcoord[0] = thiscoord[0];
 +            prevcoord[1] = thiscoord[1];
 +            prevcoord[2] = thiscoord[2];
 +
 +
 +            flag       = receivebits(buf, 1);
 +            is_smaller = 0;
 +            if (flag == 1)
 +            {
 +                run        = receivebits(buf, 5);
 +                is_smaller = run % 3;
 +                run       -= is_smaller;
 +                is_smaller--;
 +            }
 +            if (run > 0)
 +            {
 +                thiscoord += 3;
 +                for (k = 0; k < run; k += 3)
 +                {
 +                    receiveints(buf, 3, smallidx, sizesmall, thiscoord);
 +                    i++;
 +                    thiscoord[0] += prevcoord[0] - smallnum;
 +                    thiscoord[1] += prevcoord[1] - smallnum;
 +                    thiscoord[2] += prevcoord[2] - smallnum;
 +                    if (k == 0)
 +                    {
 +                        /* interchange first with second atom for better
 +                         * compression of water molecules
 +                         */
 +                        tmp          = thiscoord[0]; thiscoord[0] = prevcoord[0];
 +                        prevcoord[0] = tmp;
 +                        tmp          = thiscoord[1]; thiscoord[1] = prevcoord[1];
 +                        prevcoord[1] = tmp;
 +                        tmp          = thiscoord[2]; thiscoord[2] = prevcoord[2];
 +                        prevcoord[2] = tmp;
 +                        *lfp++       = prevcoord[0] * inv_precision;
 +                        *lfp++       = prevcoord[1] * inv_precision;
 +                        *lfp++       = prevcoord[2] * inv_precision;
 +                    }
 +                    else
 +                    {
 +                        prevcoord[0] = thiscoord[0];
 +                        prevcoord[1] = thiscoord[1];
 +                        prevcoord[2] = thiscoord[2];
 +                    }
 +                    *lfp++ = thiscoord[0] * inv_precision;
 +                    *lfp++ = thiscoord[1] * inv_precision;
 +                    *lfp++ = thiscoord[2] * inv_precision;
 +                }
 +            }
 +            else
 +            {
 +                *lfp++ = thiscoord[0] * inv_precision;
 +                *lfp++ = thiscoord[1] * inv_precision;
 +                *lfp++ = thiscoord[2] * inv_precision;
 +            }
 +            smallidx += is_smaller;
 +            if (is_smaller < 0)
 +            {
 +                smallnum = smaller;
 +                if (smallidx > FIRSTIDX)
 +                {
 +                    smaller = magicints[smallidx - 1] /2;
 +                }
 +                else
 +                {
 +                    smaller = 0;
 +                }
 +            }
 +            else if (is_smaller > 0)
 +            {
 +                smaller  = smallnum;
 +                smallnum = magicints[smallidx] / 2;
 +            }
 +            sizesmall[0] = sizesmall[1] = sizesmall[2] = magicints[smallidx];
 +        }
 +    }
 +    if (we_should_free)
 +    {
 +        free(ip);
 +        free(buf);
 +    }
 +    return 1;
 +}
 +
 +
 +
 +/******************************************************************
 +
 +   XTC files have a relatively simple structure.
 +   They have a header of 16 bytes and the rest are
 +   the compressed coordinates of the files. Due to the
 +   compression 00 is not present in the coordinates.
 +   The first 4 bytes of the header are the magic number
 +   1995 (0x000007CB). If we find this number we are guaranteed
 +   to be in the header, due to the presence of so many zeros.
 +   The second 4 bytes are the number of atoms in the frame, and is
 +   assumed to be constant. The third 4 bytes are the frame number.
 +   The last 4 bytes are a floating point representation of the time.
 +
 + ********************************************************************/
 +
 +/* Must match definition in xtcio.c */
 +#ifndef XTC_MAGIC
 +#define XTC_MAGIC 1995
 +#endif
 +
 +static const int header_size = 16;
 +
 +/* Check if we are at the header start.
 +   At the same time it will also read 1 int */
 +static int xtc_at_header_start(FILE *fp, XDR *xdrs,
 +                               int natoms, int * timestep, float * time)
 +{
 +    int       i_inp[3];
 +    float     f_inp[10];
 +    int       i;
 +    gmx_off_t off;
 +
 +
 +    if ((off = gmx_ftell(fp)) < 0)
 +    {
 +        return -1;
 +    }
 +    /* read magic natoms and timestep */
 +    for (i = 0; i < 3; i++)
 +    {
 +        if (!xdr_int(xdrs, &(i_inp[i])))
 +        {
 +            gmx_fseek(fp, off+XDR_INT_SIZE, SEEK_SET);
 +            return -1;
 +        }
 +    }
 +    /* quick return */
 +    if (i_inp[0] != XTC_MAGIC)
 +    {
 +        if (gmx_fseek(fp, off+XDR_INT_SIZE, SEEK_SET))
 +        {
 +            return -1;
 +        }
 +        return 0;
 +    }
 +    /* read time and box */
 +    for (i = 0; i < 10; i++)
 +    {
 +        if (!xdr_float(xdrs, &(f_inp[i])))
 +        {
 +            gmx_fseek(fp, off+XDR_INT_SIZE, SEEK_SET);
 +            return -1;
 +        }
 +    }
 +    /* Make a rigourous check to see if we are in the beggining of a header
 +       Hopefully there are no ambiguous cases */
 +    /* This check makes use of the fact that the box matrix has 3 zeroes on the upper
 +       right triangle and that the first element must be nonzero unless the entire matrix is zero
 +     */
 +    if (i_inp[1] == natoms &&
 +        ((f_inp[1] != 0 && f_inp[6] == 0) ||
 +         (f_inp[1] == 0 && f_inp[5] == 0 && f_inp[9] == 0)))
 +    {
 +        if (gmx_fseek(fp, off+XDR_INT_SIZE, SEEK_SET))
 +        {
 +            return -1;
 +        }
 +        *time     = f_inp[0];
 +        *timestep = i_inp[2];
 +        return 1;
 +    }
 +    if (gmx_fseek(fp, off+XDR_INT_SIZE, SEEK_SET))
 +    {
 +        return -1;
 +    }
 +    return 0;
 +}
 +
 +static int
 +xtc_get_next_frame_number(FILE *fp, XDR *xdrs, int natoms)
 +{
 +    gmx_off_t off;
 +    int       step;
 +    float     time;
 +    int       ret;
 +
 +    if ((off = gmx_ftell(fp)) < 0)
 +    {
 +        return -1;
 +    }
 +
 +    /* read one int just to make sure we dont read this frame but the next */
 +    xdr_int(xdrs, &step);
 +    while (1)
 +    {
 +        ret = xtc_at_header_start(fp, xdrs, natoms, &step, &time);
 +        if (ret == 1)
 +        {
 +            if (gmx_fseek(fp, off, SEEK_SET))
 +            {
 +                return -1;
 +            }
 +            return step;
 +        }
 +        else if (ret == -1)
 +        {
 +            if (gmx_fseek(fp, off, SEEK_SET))
 +            {
 +                return -1;
 +            }
 +        }
 +    }
 +    return -1;
 +}
 +
 +
 +static float xtc_get_next_frame_time(FILE *fp, XDR *xdrs, int natoms,
 +                                     gmx_bool * bOK)
 +{
 +    gmx_off_t off;
 +    float     time;
 +    int       step;
 +    int       ret;
 +    *bOK = 0;
 +
 +    if ((off = gmx_ftell(fp)) < 0)
 +    {
 +        return -1;
 +    }
 +    /* read one int just to make sure we dont read this frame but the next */
 +    xdr_int(xdrs, &step);
 +    while (1)
 +    {
 +        ret = xtc_at_header_start(fp, xdrs, natoms, &step, &time);
 +        if (ret == 1)
 +        {
 +            *bOK = 1;
 +            if (gmx_fseek(fp, off, SEEK_SET))
 +            {
 +                *bOK = 0;
 +                return -1;
 +            }
 +            return time;
 +        }
 +        else if (ret == -1)
 +        {
 +            if (gmx_fseek(fp, off, SEEK_SET))
 +            {
 +                return -1;
 +            }
 +            return -1;
 +        }
 +    }
 +    return -1;
 +}
 +
 +
 +static float
 +xtc_get_current_frame_time(FILE *fp, XDR *xdrs, int natoms, gmx_bool * bOK)
 +{
 +    gmx_off_t off;
 +    int       step;
 +    float     time;
 +    int       ret;
 +    *bOK = 0;
 +
 +    if ((off = gmx_ftell(fp)) < 0)
 +    {
 +        return -1;
 +    }
 +
 +    while (1)
 +    {
 +        ret = xtc_at_header_start(fp, xdrs, natoms, &step, &time);
 +        if (ret == 1)
 +        {
 +            *bOK = 1;
 +            if (gmx_fseek(fp, off, SEEK_SET))
 +            {
 +                *bOK = 0;
 +                return -1;
 +            }
 +            return time;
 +        }
 +        else if (ret == -1)
 +        {
 +            if (gmx_fseek(fp, off, SEEK_SET))
 +            {
 +                return -1;
 +            }
 +            return -1;
 +        }
 +        else if (ret == 0)
 +        {
 +            /*Go back.*/
 +            if (gmx_fseek(fp, -2*XDR_INT_SIZE, SEEK_CUR))
 +            {
 +                return -1;
 +            }
 +        }
 +    }
 +    return -1;
 +}
 +
 +/* Currently not used, just for completeness */
 +static int
 +xtc_get_current_frame_number(FILE *fp, XDR *xdrs, int natoms, gmx_bool * bOK)
 +{
 +    gmx_off_t off;
 +    int       ret;
 +    int       step;
 +    float     time;
 +    *bOK = 0;
 +
 +    if ((off = gmx_ftell(fp)) < 0)
 +    {
 +        return -1;
 +    }
 +
 +
 +    while (1)
 +    {
 +        ret = xtc_at_header_start(fp, xdrs, natoms, &step, &time);
 +        if (ret == 1)
 +        {
 +            *bOK = 1;
 +            if (gmx_fseek(fp, off, SEEK_SET))
 +            {
 +                *bOK = 0;
 +                return -1;
 +            }
 +            return step;
 +        }
 +        else if (ret == -1)
 +        {
 +            if (gmx_fseek(fp, off, SEEK_SET))
 +            {
 +                return -1;
 +            }
 +            return -1;
 +
 +        }
 +        else if (ret == 0)
 +        {
 +            /*Go back.*/
 +            if (gmx_fseek(fp, -2*XDR_INT_SIZE, SEEK_CUR))
 +            {
 +                return -1;
 +            }
 +        }
 +    }
 +    return -1;
 +}
 +
 +
 +
 +static gmx_off_t xtc_get_next_frame_start(FILE *fp, XDR *xdrs, int natoms)
 +{
 +    int       inp;
 +    gmx_off_t res;
 +    int       ret;
 +    int       step;
 +    float     time;
 +    /* read one int just to make sure we dont read this frame but the next */
 +    xdr_int(xdrs, &step);
 +    while (1)
 +    {
 +        ret = xtc_at_header_start(fp, xdrs, natoms, &step, &time);
 +        if (ret == 1)
 +        {
 +            if ((res = gmx_ftell(fp)) >= 0)
 +            {
 +                return res - XDR_INT_SIZE;
 +            }
 +            else
 +            {
 +                return res;
 +            }
 +        }
 +        else if (ret == -1)
 +        {
 +            return -1;
 +        }
 +    }
 +    return -1;
 +}
 +
 +
 +static
 +float
 +xdr_xtc_estimate_dt(FILE *fp, XDR *xdrs, int natoms, gmx_bool * bOK)
 +{
 +    float     res;
 +    float     tinit;
 +    gmx_off_t off;
 +
 +    *bOK = 0;
 +    if ((off   = gmx_ftell(fp)) < 0)
 +    {
 +        return -1;
 +    }
 +
 +    tinit = xtc_get_current_frame_time(fp, xdrs, natoms, bOK);
 +
 +    if (!(*bOK))
 +    {
 +        return -1;
 +    }
 +
 +    res = xtc_get_next_frame_time(fp, xdrs, natoms, bOK);
 +
 +    if (!(*bOK))
 +    {
 +        return -1;
 +    }
 +
 +    res -= tinit;
 +    if (0 != gmx_fseek(fp, off, SEEK_SET))
 +    {
 +        *bOK = 0;
 +        return -1;
 +    }
 +    return res;
 +}
 +
 +int
 +xdr_xtc_seek_frame(int frame, FILE *fp, XDR *xdrs, int natoms)
 +{
 +    gmx_off_t low = 0;
 +    gmx_off_t high, pos;
 +
 +
 +    /* round to 4 bytes */
 +    int        fr;
 +    gmx_off_t  offset;
 +    if (gmx_fseek(fp, 0, SEEK_END))
 +    {
 +        return -1;
 +    }
 +
 +    if ((high = gmx_ftell(fp)) < 0)
 +    {
 +        return -1;
 +    }
 +
 +    /* round to 4 bytes  */
 +    high  /= XDR_INT_SIZE;
 +    high  *= XDR_INT_SIZE;
 +    offset = ((high/2)/XDR_INT_SIZE)*XDR_INT_SIZE;
 +
 +    if (gmx_fseek(fp, offset, SEEK_SET))
 +    {
 +        return -1;
 +    }
 +
 +    while (1)
 +    {
 +        fr = xtc_get_next_frame_number(fp, xdrs, natoms);
 +        if (fr < 0)
 +        {
 +            return -1;
 +        }
 +        if (fr != frame && llabs(low-high) > header_size)
 +        {
 +            if (fr < frame)
 +            {
 +                low = offset;
 +            }
 +            else
 +            {
 +                high = offset;
 +            }
 +            /* round to 4 bytes */
 +            offset = (((high+low)/2)/4)*4;
 +
 +            if (gmx_fseek(fp, offset, SEEK_SET))
 +            {
 +                return -1;
 +            }
 +        }
 +        else
 +        {
 +            break;
 +        }
 +    }
 +    if (offset <= header_size)
 +    {
 +        offset = low;
 +    }
 +
 +    if (gmx_fseek(fp, offset, SEEK_SET))
 +    {
 +        return -1;
 +    }
 +
 +    if ((pos = xtc_get_next_frame_start(fp, xdrs, natoms)) < 0)
 +    {
 +        /* we probably hit an end of file */
 +        return -1;
 +    }
 +
 +    if (gmx_fseek(fp, pos, SEEK_SET))
 +    {
 +        return -1;
 +    }
 +
 +    return 0;
 +}
 +
 +
 +
 +int xdr_xtc_seek_time(real time, FILE *fp, XDR *xdrs, int natoms, gmx_bool bSeekForwardOnly)
 +{
 +    float     t;
 +    float     dt;
 +    gmx_bool  bOK = FALSE;
 +    gmx_off_t low = 0;
 +    gmx_off_t high, offset, pos;
 +    int       res;
 +    int       dt_sign = 0;
 +
 +    if (bSeekForwardOnly)
 +    {
++        low = gmx_ftell(fp)-header_size;
 +    }
 +    if (gmx_fseek(fp, 0, SEEK_END))
 +    {
 +        return -1;
 +    }
 +
 +    if ((high = gmx_ftell(fp)) < 0)
 +    {
 +        return -1;
 +    }
 +    /* round to int  */
 +    high  /= XDR_INT_SIZE;
 +    high  *= XDR_INT_SIZE;
 +    offset = (((high-low) / 2) / XDR_INT_SIZE) * XDR_INT_SIZE;
 +
 +    if (gmx_fseek(fp, offset, SEEK_SET))
 +    {
 +        return -1;
 +    }
 +
 +
 +    /*
 +     * No need to call xdr_xtc_estimate_dt here - since xdr_xtc_estimate_dt is called first thing in the loop
 +       dt = xdr_xtc_estimate_dt(fp, xdrs, natoms, &bOK);
 +
 +       if (!bOK)
 +       {
 +        return -1;
 +       }
 +     */
 +
 +    while (1)
 +    {
 +        dt = xdr_xtc_estimate_dt(fp, xdrs, natoms, &bOK);
 +        if (!bOK)
 +        {
 +            return -1;
 +        }
 +        else
 +        {
 +            if (dt > 0)
 +            {
 +                if (dt_sign == -1)
 +                {
 +                    /* Found a place in the trajectory that has positive time step while
 +                       other has negative time step */
 +                    return -2;
 +                }
 +                dt_sign = 1;
 +            }
 +            else if (dt < 0)
 +            {
 +                if (dt_sign == 1)
 +                {
 +                    /* Found a place in the trajectory that has positive time step while
 +                       other has negative time step */
 +                    return -2;
 +                }
 +                dt_sign = -1;
 +            }
 +        }
 +        t = xtc_get_next_frame_time(fp, xdrs, natoms, &bOK);
 +        if (!bOK)
 +        {
 +            return -1;
 +        }
 +
 +        /* If we are before the target time and the time step is positive or 0, or we have
 +           after the target time and the time step is negative, or the difference between
 +           the current time and the target time is bigger than dt and above all the distance between high
 +           and low is bigger than 1 frame, then do another step of binary search. Otherwise stop and check
 +           if we reached the solution */
 +        if ((((t < time && dt_sign >= 0) || (t > time && dt_sign == -1)) ||
 +             ((t - time) >= dt && dt_sign >= 0) || ((time - t) >= -dt && dt_sign < 0)) &&
 +            (llabs(low - high) > header_size))
 +        {
 +            if (dt >= 0 && dt_sign != -1)
 +            {
 +                if (t < time)
 +                {
 +                    low = offset;
 +                }
 +                else
 +                {
 +                    high = offset;
 +                }
 +            }
 +            else if (dt <= 0 && dt_sign == -1)
 +            {
 +                if (t >= time)
 +                {
 +                    low = offset;
 +                }
 +                else
 +                {
 +                    high = offset;
 +                }
 +            }
 +            else
 +            {
 +                /* We should never reach here */
 +                return -1;
 +            }
 +            /* round to 4 bytes and subtract header*/
 +            offset = (((high + low) / 2) / XDR_INT_SIZE) * XDR_INT_SIZE;
 +            if (gmx_fseek(fp, offset, SEEK_SET))
 +            {
 +                return -1;
 +            }
 +        }
 +        else
 +        {
 +            if (llabs(low - high) <= header_size)
 +            {
 +                break;
 +            }
 +            /* re-estimate dt */
 +            if (xdr_xtc_estimate_dt(fp, xdrs, natoms, &bOK) != dt)
 +            {
 +                if (bOK)
 +                {
 +                    dt = xdr_xtc_estimate_dt(fp, xdrs, natoms, &bOK);
 +                }
 +            }
 +            if (t >= time && t - time < dt)
 +            {
 +                break;
 +            }
 +        }
 +    }
 +
 +    if (offset <= header_size)
 +    {
 +        offset = low;
 +    }
 +
 +    gmx_fseek(fp, offset, SEEK_SET);
 +
 +    if ((pos = xtc_get_next_frame_start(fp, xdrs, natoms)) < 0)
 +    {
 +        return -1;
 +    }
 +
 +    if (gmx_fseek(fp, pos, SEEK_SET))
 +    {
 +        return -1;
 +    }
 +    return 0;
 +}
 +
 +float
 +xdr_xtc_get_last_frame_time(FILE *fp, XDR *xdrs, int natoms, gmx_bool * bOK)
 +{
 +    float      time;
 +    gmx_off_t  off;
 +    int        res;
 +    *bOK = 1;
 +    off  = gmx_ftell(fp);
 +    if (off < 0)
 +    {
 +        *bOK = 0;
 +        return -1;
 +    }
 +
 +    if ( (res = gmx_fseek(fp, -3*XDR_INT_SIZE, SEEK_END)) != 0)
 +    {
 +        *bOK = 0;
 +        return -1;
 +    }
 +
 +    time = xtc_get_current_frame_time(fp, xdrs, natoms, bOK);
 +    if (!(*bOK))
 +    {
 +        return -1;
 +    }
 +
 +    if ( (res = gmx_fseek(fp, off, SEEK_SET)) != 0)
 +    {
 +        *bOK = 0;
 +        return -1;
 +    }
 +    return time;
 +}
 +
 +
 +int
 +xdr_xtc_get_last_frame_number(FILE *fp, XDR *xdrs, int natoms, gmx_bool * bOK)
 +{
 +    int        frame;
 +    gmx_off_t  off;
 +    int        res;
 +    *bOK = 1;
 +
 +    if ((off = gmx_ftell(fp)) < 0)
 +    {
 +        *bOK = 0;
 +        return -1;
 +    }
 +
 +
 +    if (gmx_fseek(fp, -3*XDR_INT_SIZE, SEEK_END))
 +    {
 +        *bOK = 0;
 +        return -1;
 +    }
 +
 +    frame = xtc_get_current_frame_number(fp, xdrs, natoms, bOK);
 +    if (!bOK)
 +    {
 +        return -1;
 +    }
 +
 +
 +    if (gmx_fseek(fp, off, SEEK_SET))
 +    {
 +        *bOK = 0;
 +        return -1;
 +    }
 +
 +    return frame;
 +}
index 3a0727b18a9db569ae3ff0d85bb4b88e3518c895,0000000000000000000000000000000000000000..b0f5096f78abe4b6c13a7f6eb3b959befa3c3114
mode 100644,000000..100644
--- /dev/null
@@@ -1,98 -1,0 +1,99 @@@
-  * Copyright (c) 2001-2004, The GROMACS development team,
-  * Copyright (c) 2013, by the GROMACS development team, led by
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
++ * Copyright (c) 2001-2004, The GROMACS development team.
++ * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
 +/* The gmx_bools indicate whether a field was read from the trajectory.
 + * Do not try to use a pointer when its gmx_bool is FALSE, as memory might
 + * not be allocated.
 + */
 +
 +#ifndef GMX_FILEIO_TRX_H
 +#define GMX_FILEIO_TRX_H
 +
 +#include "../legacyheaders/types/atoms.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +typedef struct gmxvmdplugin t_gmxvmdplugin;
 +
 +typedef struct trxframe
 +{
 +    int      flags;            /* flags for read_first/next_frame  */
 +    int      not_ok;           /* integrity flags                  */
 +    gmx_bool bDouble;          /* Double precision?                */
 +    int      natoms;           /* number of atoms (atoms, x, v, f) */
 +    real     t0;               /* time of the first frame, needed  *
 +                                * for skipping frames with -dt     */
++    real     tf;               /* internal frame time - DO NOT CHANGE */
 +    real     tpf;              /* time of the previous frame, not  */
 +                               /* the read, but real file frames   */
 +    real     tppf;             /* time of two frames ago           */
 +                               /* tpf and tppf are needed to       */
 +                               /* correct rounding errors for -e   */
 +    gmx_bool        bTitle;
 +    const char     *title;     /* title of the frame            */
 +    gmx_bool        bStep;
 +    int             step;      /* MD step number                   */
 +    gmx_bool        bTime;
 +    real            time;      /* time of the frame                */
 +    gmx_bool        bLambda;
 +    gmx_bool        bFepState; /* does it contain fep_state?       */
 +    real            lambda;    /* free energy perturbation lambda  */
 +    int             fep_state; /* which fep state are we in? */
 +    gmx_bool        bAtoms;
 +    t_atoms        *atoms;     /* atoms struct (natoms)            */
 +    gmx_bool        bPrec;
 +    real            prec;      /* precision of x, fraction of 1 nm */
 +    gmx_bool        bX;
 +    rvec           *x;         /* coordinates (natoms)             */
 +    gmx_bool        bV;
 +    rvec           *v;         /* velocities (natoms)              */
 +    gmx_bool        bF;
 +    rvec           *f;         /* forces (natoms)                  */
 +    gmx_bool        bBox;
 +    matrix          box;       /* the 3 box vectors                */
 +    gmx_bool        bPBC;
 +    int             ePBC;      /* the type of pbc                  */
 +    t_gmxvmdplugin* vmdplugin;
 +} t_trxframe;
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif
index 25d3dd1fa4f57088401873eb716e39251bf9f23b,0000000000000000000000000000000000000000..2c9eb34e53000fa5875a6d23d43192125d4e8d5c
mode 100644,000000..100644
--- /dev/null
@@@ -1,1139 -1,0 +1,1142 @@@
-     pt   = fr->time;
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "trxio.h"
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <assert.h>
 +#include <math.h>
 +
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#ifdef GMX_USE_PLUGINS
 +#include "vmdio.h"
 +#endif
 +#include "gromacs/utility/smalloc.h"
 +#include "pbc.h"
 +#include "gmxfio.h"
 +#include "trxio.h"
 +#include "tpxio.h"
 +#include "trnio.h"
 +#include "tngio.h"
 +#include "tngio_for_tools.h"
 +#include "names.h"
 +#include "vec.h"
 +#include "futil.h"
 +#include "xtcio.h"
 +#include "pdbio.h"
 +#include "confio.h"
 +#include "checkpoint.h"
 +#include "xdrf.h"
 +
 +#include "gromacs/fileio/timecontrol.h"
 +#include "gromacs/legacyheaders/gmx_fatal.h"
 +
 +/* defines for frame counter output */
 +#define SKIP1   10
 +#define SKIP2  100
 +#define SKIP3 1000
 +
 +struct t_trxstatus
 +{
 +    int                     __frame;
 +    t_trxframe             *xframe;
 +    int                     nxframe;
 +    t_fileio               *fio;
 +    tng_trajectory_t        tng;
 +    int                     NATOMS;
 +    double                  DT, BOX[3];
 +    gmx_bool                bReadBox;
 +    char                   *persistent_line; /* Persistent line for reading g96 trajectories */
 +};
 +
 +/* utility functions */
 +
 +gmx_bool bRmod_fd(double a, double b, double c, gmx_bool bDouble)
 +{
 +    int    iq;
 +    double tol;
 +
 +    tol = 2*(bDouble ? GMX_DOUBLE_EPS : GMX_FLOAT_EPS);
 +
 +    iq = (int)((a - b + tol*a)/c);
 +
 +    if (fabs(a - b - c*iq) <= tol*fabs(a))
 +    {
 +        return TRUE;
 +    }
 +    else
 +    {
 +        return FALSE;
 +    }
 +}
 +
 +
 +
 +int check_times2(real t, real t0, gmx_bool bDouble)
 +{
 +    int  r;
 +
 +#ifndef GMX_DOUBLE
 +    /* since t is float, we can not use double precision for bRmod */
 +    bDouble = FALSE;
 +#endif
 +
 +    r = -1;
 +    if ((!bTimeSet(TBEGIN) || (t >= rTimeValue(TBEGIN)))  &&
 +        (!bTimeSet(TEND)   || (t <= rTimeValue(TEND))))
 +    {
 +        if (bTimeSet(TDELTA) && !bRmod_fd(t, t0, rTimeValue(TDELTA), bDouble))
 +        {
 +            r = -1;
 +        }
 +        else
 +        {
 +            r = 0;
 +        }
 +    }
 +    else if (bTimeSet(TEND) && (t >= rTimeValue(TEND)))
 +    {
 +        r = 1;
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "t=%g, t0=%g, b=%g, e=%g, dt=%g: r=%d\n",
 +                t, t0, rTimeValue(TBEGIN), rTimeValue(TEND), rTimeValue(TDELTA), r);
 +    }
 +    return r;
 +}
 +
 +int check_times(real t)
 +{
 +    return check_times2(t, t, FALSE);
 +}
 +
 +static void initcount(t_trxstatus *status)
 +{
 +    status->__frame = -1;
 +}
 +
 +static void status_init(t_trxstatus *status)
 +{
 +    status->nxframe         = 0;
 +    status->xframe          = NULL;
 +    status->fio             = NULL;
 +    status->__frame         = -1;
 +    status->persistent_line = NULL;
 +    status->tng             = NULL;
 +}
 +
 +
 +int nframes_read(t_trxstatus *status)
 +{
 +    return status->__frame;
 +}
 +
 +static void printcount_(t_trxstatus *status, const output_env_t oenv,
 +                        const char *l, real t)
 +{
 +    if ((status->__frame < 2*SKIP1 || status->__frame % SKIP1 == 0) &&
 +        (status->__frame < 2*SKIP2 || status->__frame % SKIP2 == 0) &&
 +        (status->__frame < 2*SKIP3 || status->__frame % SKIP3 == 0))
 +    {
 +        fprintf(stderr, "\r%-14s %6d time %8.3f   ", l, status->__frame,
 +                output_env_conv_time(oenv, t));
 +    }
 +}
 +
 +static void printcount(t_trxstatus *status, const output_env_t oenv, real t,
 +                       gmx_bool bSkip)
 +{
 +    status->__frame++;
 +    printcount_(status, oenv, bSkip ? "Skipping frame" : "Reading frame", t);
 +}
 +
 +static void printlast(t_trxstatus *status, const output_env_t oenv, real t)
 +{
 +    printcount_(status, oenv, "Last frame", t);
 +    fprintf(stderr, "\n");
 +}
 +
 +static void printincomp(t_trxstatus *status, t_trxframe *fr)
 +{
 +    if (fr->not_ok & HEADER_NOT_OK)
 +    {
 +        fprintf(stderr, "WARNING: Incomplete header: nr %d time %g\n",
 +                status->__frame+1, fr->time);
 +    }
 +    else if (fr->not_ok)
 +    {
 +        fprintf(stderr, "WARNING: Incomplete frame: nr %d time %g\n",
 +                status->__frame+1, fr->time);
 +    }
 +}
 +
 +int prec2ndec(real prec)
 +{
 +    if (prec <= 0)
 +    {
 +        gmx_fatal(FARGS, "DEATH HORROR prec (%g) <= 0 in prec2ndec", prec);
 +    }
 +
 +    return (int)(log(prec)/log(10.0)+0.5);
 +}
 +
 +real ndec2prec(int ndec)
 +{
 +    return pow(10.0, ndec);
 +}
 +
 +t_fileio *trx_get_fileio(t_trxstatus *status)
 +{
 +    return status->fio;
 +}
 +
 +float trx_get_time_of_final_frame(t_trxstatus *status)
 +{
 +    t_fileio *stfio    = trx_get_fileio(status);
 +    int       filetype = gmx_fio_getftp(stfio);
 +    int       bOK;
 +    float     lasttime = -1;
 +
 +    if (filetype == efXTC)
 +    {
 +        lasttime =
 +            xdr_xtc_get_last_frame_time(gmx_fio_getfp(stfio),
 +                                        gmx_fio_getxdr(stfio),
 +                                        status->xframe->natoms, &bOK);
 +        if (!bOK)
 +        {
 +            gmx_fatal(FARGS, "Error reading last frame. Maybe seek not supported." );
 +        }
 +    }
 +    else if (filetype == efTNG)
 +    {
 +        tng_trajectory_t tng = status->tng;
 +        if (!tng)
 +        {
 +            gmx_fatal(FARGS, "Error opening TNG file.");
 +        }
 +        lasttime = gmx_tng_get_time_of_final_frame(tng);
 +    }
 +    else
 +    {
 +        gmx_incons("Only supported for TNG and XTC");
 +    }
 +    return lasttime;
 +}
 +
 +void clear_trxframe(t_trxframe *fr, gmx_bool bFirst)
 +{
 +    fr->not_ok    = 0;
 +    fr->bTitle    = FALSE;
 +    fr->bStep     = FALSE;
 +    fr->bTime     = FALSE;
 +    fr->bLambda   = FALSE;
 +    fr->bFepState = FALSE;
 +    fr->bAtoms    = FALSE;
 +    fr->bPrec     = FALSE;
 +    fr->bX        = FALSE;
 +    fr->bV        = FALSE;
 +    fr->bF        = FALSE;
 +    fr->bBox      = FALSE;
 +    if (bFirst)
 +    {
 +        fr->flags     = 0;
 +        fr->bDouble   = FALSE;
 +        fr->natoms    = -1;
 +        fr->t0        = 0;
++        fr->tf        = 0;
 +        fr->tpf       = 0;
 +        fr->tppf      = 0;
 +        fr->title     = NULL;
 +        fr->step      = 0;
 +        fr->time      = 0;
 +        fr->lambda    = 0;
 +        fr->fep_state = 0;
 +        fr->atoms     = NULL;
 +        fr->prec      = 0;
 +        fr->x         = NULL;
 +        fr->v         = NULL;
 +        fr->f         = NULL;
 +        clear_mat(fr->box);
 +        fr->bPBC   = FALSE;
 +        fr->ePBC   = -1;
 +    }
 +}
 +
 +void set_trxframe_ePBC(t_trxframe *fr, int ePBC)
 +{
 +    fr->bPBC = (ePBC == -1);
 +    fr->ePBC = ePBC;
 +}
 +
 +int write_trxframe_indexed(t_trxstatus *status, t_trxframe *fr, int nind,
 +                           const atom_id *ind, gmx_conect gc)
 +{
 +    char  title[STRLEN];
 +    rvec *xout = NULL, *vout = NULL, *fout = NULL;
 +    int   i, ftp = -1;
 +    real  prec;
 +
 +    if (fr->bPrec)
 +    {
 +        prec = fr->prec;
 +    }
 +    else
 +    {
 +        prec = 1000.0;
 +    }
 +
 +    if (status->tng)
 +    {
 +        ftp = efTNG;
 +    }
 +    else if (status->fio)
 +    {
 +        ftp = gmx_fio_getftp(status->fio);
 +    }
 +    else
 +    {
 +        gmx_incons("No input file available");
 +    }
 +
 +    switch (ftp)
 +    {
 +        case efTRJ:
 +        case efTRR:
 +        case efTNG:
 +            break;
 +        default:
 +            if (!fr->bX)
 +            {
 +                gmx_fatal(FARGS, "Need coordinates to write a %s trajectory",
 +                          ftp2ext(ftp));
 +            }
 +            break;
 +    }
 +
 +    switch (ftp)
 +    {
 +        case efTRJ:
 +        case efTRR:
 +        case efTNG:
 +            if (fr->bV)
 +            {
 +                snew(vout, nind);
 +                for (i = 0; i < nind; i++)
 +                {
 +                    copy_rvec(fr->v[ind[i]], vout[i]);
 +                }
 +            }
 +            if (fr->bF)
 +            {
 +                snew(fout, nind);
 +                for (i = 0; i < nind; i++)
 +                {
 +                    copy_rvec(fr->f[ind[i]], fout[i]);
 +                }
 +            }
 +        /* no break */
 +        case efXTC:
 +            if (fr->bX)
 +            {
 +                snew(xout, nind);
 +                for (i = 0; i < nind; i++)
 +                {
 +                    copy_rvec(fr->x[ind[i]], xout[i]);
 +                }
 +            }
 +            break;
 +        default:
 +            break;
 +    }
 +
 +    switch (ftp)
 +    {
 +        case efTNG:
 +            gmx_write_tng_from_trxframe(status->tng, fr, nind);
 +            break;
 +        case efXTC:
 +            write_xtc(status->fio, nind, fr->step, fr->time, fr->box, xout, prec);
 +            break;
 +        case efTRJ:
 +        case efTRR:
 +            fwrite_trn(status->fio, nframes_read(status),
 +                       fr->time, fr->step, fr->box, nind, xout, vout, fout);
 +            break;
 +        case efGRO:
 +        case efPDB:
 +        case efBRK:
 +        case efENT:
 +            if (!fr->bAtoms)
 +            {
 +                gmx_fatal(FARGS, "Can not write a %s file without atom names",
 +                          ftp2ext(ftp));
 +            }
 +            sprintf(title, "frame t= %.3f", fr->time);
 +            if (ftp == efGRO)
 +            {
 +                write_hconf_indexed_p(gmx_fio_getfp(status->fio), title, fr->atoms, nind, ind,
 +                                      prec2ndec(prec),
 +                                      fr->x, fr->bV ? fr->v : NULL, fr->box);
 +            }
 +            else
 +            {
 +                write_pdbfile_indexed(gmx_fio_getfp(status->fio), title, fr->atoms,
 +                                      fr->x, -1, fr->box, ' ', fr->step, nind, ind, gc, TRUE);
 +            }
 +            break;
 +        case efG96:
 +            write_g96_conf(gmx_fio_getfp(status->fio), fr, nind, ind);
 +            break;
 +        default:
 +            gmx_fatal(FARGS, "Sorry, write_trxframe_indexed can not write %s",
 +                      ftp2ext(ftp));
 +            break;
 +    }
 +
 +    switch (ftp)
 +    {
 +        case efTRN:
 +        case efTRJ:
 +        case efTRR:
 +        case efTNG:
 +            if (vout)
 +            {
 +                sfree(vout);
 +            }
 +            if (fout)
 +            {
 +                sfree(fout);
 +            }
 +        /* no break */
 +        case efXTC:
 +            sfree(xout);
 +            break;
 +        default:
 +            break;
 +    }
 +
 +    return 0;
 +}
 +
 +void trjtools_gmx_prepare_tng_writing(const char       *filename,
 +                                      char              filemode,
 +                                      t_trxstatus      *in,
 +                                      t_trxstatus     **out,
 +                                      const char       *infile,
 +                                      const int         natoms,
 +                                      const gmx_mtop_t *mtop,
 +                                      const atom_id    *index,
 +                                      const char       *index_group_name)
 +{
 +    if (filemode != 'w' && filemode != 'a')
 +    {
 +        gmx_incons("Sorry, can only prepare for TNG output.");
 +    }
 +
 +    if (*out == NULL)
 +    {
 +        snew((*out), 1);
 +    }
 +    status_init(*out);
 +
 +    if (in != NULL)
 +    {
 +        gmx_prepare_tng_writing(filename,
 +                                filemode,
 +                                &in->tng,
 +                                &(*out)->tng,
 +                                natoms,
 +                                mtop,
 +                                index,
 +                                index_group_name);
 +    }
 +    else if (efTNG == fn2ftp(infile))
 +    {
 +        tng_trajectory_t tng_in;
 +        gmx_tng_open(infile, 'r', &tng_in);
 +
 +        gmx_prepare_tng_writing(filename,
 +                                filemode,
 +                                &tng_in,
 +                                &(*out)->tng,
 +                                natoms,
 +                                mtop,
 +                                index,
 +                                index_group_name);
 +    }
 +}
 +
 +void write_tng_frame(t_trxstatus *status,
 +                     t_trxframe  *frame)
 +{
 +    gmx_write_tng_from_trxframe(status->tng, frame, -1);
 +}
 +
 +int write_trxframe(t_trxstatus *status, t_trxframe *fr, gmx_conect gc)
 +{
 +    char title[STRLEN];
 +    real prec;
 +
 +    if (fr->bPrec)
 +    {
 +        prec = fr->prec;
 +    }
 +    else
 +    {
 +        prec = 1000.0;
 +    }
 +
 +    if (status->tng)
 +    {
 +        gmx_tng_set_compression_precision(status->tng, prec);
 +        write_tng_frame(status, fr);
 +
 +        return 0;
 +    }
 +
 +    switch (gmx_fio_getftp(status->fio))
 +    {
 +        case efTRJ:
 +        case efTRR:
 +            break;
 +        default:
 +            if (!fr->bX)
 +            {
 +                gmx_fatal(FARGS, "Need coordinates to write a %s trajectory",
 +                          ftp2ext(gmx_fio_getftp(status->fio)));
 +            }
 +            break;
 +    }
 +
 +    switch (gmx_fio_getftp(status->fio))
 +    {
 +        case efXTC:
 +            write_xtc(status->fio, fr->natoms, fr->step, fr->time, fr->box, fr->x, prec);
 +            break;
 +        case efTRJ:
 +        case efTRR:
 +            fwrite_trn(status->fio, fr->step, fr->time, fr->lambda, fr->box, fr->natoms,
 +                       fr->bX ? fr->x : NULL, fr->bV ? fr->v : NULL, fr->bF ? fr->f : NULL);
 +            break;
 +        case efGRO:
 +        case efPDB:
 +        case efBRK:
 +        case efENT:
 +            if (!fr->bAtoms)
 +            {
 +                gmx_fatal(FARGS, "Can not write a %s file without atom names",
 +                          ftp2ext(gmx_fio_getftp(status->fio)));
 +            }
 +            sprintf(title, "frame t= %.3f", fr->time);
 +            if (gmx_fio_getftp(status->fio) == efGRO)
 +            {
 +                write_hconf_p(gmx_fio_getfp(status->fio), title, fr->atoms,
 +                              prec2ndec(prec), fr->x, fr->bV ? fr->v : NULL, fr->box);
 +            }
 +            else
 +            {
 +                write_pdbfile(gmx_fio_getfp(status->fio), title,
 +                              fr->atoms, fr->x, fr->bPBC ? fr->ePBC : -1, fr->box,
 +                              ' ', fr->step, gc, TRUE);
 +            }
 +            break;
 +        case efG96:
 +            write_g96_conf(gmx_fio_getfp(status->fio), fr, -1, NULL);
 +            break;
 +        default:
 +            gmx_fatal(FARGS, "Sorry, write_trxframe can not write %s",
 +                      ftp2ext(gmx_fio_getftp(status->fio)));
 +            break;
 +    }
 +
 +    return 0;
 +}
 +
 +int write_trx(t_trxstatus *status, int nind, const atom_id *ind, t_atoms *atoms,
 +              int step, real time, matrix box, rvec x[], rvec *v,
 +              gmx_conect gc)
 +{
 +    t_trxframe fr;
 +
 +    clear_trxframe(&fr, TRUE);
 +    fr.bStep  = TRUE;
 +    fr.step   = step;
 +    fr.bTime  = TRUE;
 +    fr.time   = time;
 +    fr.bAtoms = atoms != NULL;
 +    fr.atoms  = atoms;
 +    fr.bX     = TRUE;
 +    fr.x      = x;
 +    fr.bV     = v != NULL;
 +    fr.v      = v;
 +    fr.bBox   = TRUE;
 +    copy_mat(box, fr.box);
 +
 +    return write_trxframe_indexed(status, &fr, nind, ind, gc);
 +}
 +
 +void close_trx(t_trxstatus *status)
 +{
 +    gmx_tng_close(&status->tng);
 +    if (status->fio)
 +    {
 +        gmx_fio_close(status->fio);
 +    }
 +    sfree(status);
 +}
 +
 +t_trxstatus *open_trx(const char *outfile, const char *filemode)
 +{
 +    t_trxstatus *stat;
 +    if (filemode[0] != 'w' && filemode[0] != 'a' && filemode[1] != '+')
 +    {
 +        gmx_fatal(FARGS, "Sorry, write_trx can only write");
 +    }
 +
 +    snew(stat, 1);
 +    status_init(stat);
 +
 +    stat->fio = gmx_fio_open(outfile, filemode);
 +    return stat;
 +}
 +
 +static gmx_bool gmx_next_frame(t_trxstatus *status, t_trxframe *fr)
 +{
 +    t_trnheader sh;
 +    gmx_bool    bOK, bRet;
 +
 +    bRet = FALSE;
 +
 +    if (fread_trnheader(status->fio, &sh, &bOK))
 +    {
 +        fr->bDouble   = sh.bDouble;
 +        fr->natoms    = sh.natoms;
 +        fr->bStep     = TRUE;
 +        fr->step      = sh.step;
 +        fr->bTime     = TRUE;
 +        fr->time      = sh.t;
 +        fr->bLambda   = TRUE;
 +        fr->bFepState = TRUE;
 +        fr->lambda    = sh.lambda;
 +        fr->bBox      = sh.box_size > 0;
 +        if (fr->flags & (TRX_READ_X | TRX_NEED_X))
 +        {
 +            if (fr->x == NULL)
 +            {
 +                snew(fr->x, sh.natoms);
 +            }
 +            fr->bX = sh.x_size > 0;
 +        }
 +        if (fr->flags & (TRX_READ_V | TRX_NEED_V))
 +        {
 +            if (fr->v == NULL)
 +            {
 +                snew(fr->v, sh.natoms);
 +            }
 +            fr->bV = sh.v_size > 0;
 +        }
 +        if (fr->flags & (TRX_READ_F | TRX_NEED_F))
 +        {
 +            if (fr->f == NULL)
 +            {
 +                snew(fr->f, sh.natoms);
 +            }
 +            fr->bF = sh.f_size > 0;
 +        }
 +        if (fread_htrn(status->fio, &sh, fr->box, fr->x, fr->v, fr->f))
 +        {
 +            bRet = TRUE;
 +        }
 +        else
 +        {
 +            fr->not_ok = DATA_NOT_OK;
 +        }
 +    }
 +    else
 +    if (!bOK)
 +    {
 +        fr->not_ok = HEADER_NOT_OK;
 +    }
 +
 +    return bRet;
 +}
 +
 +static gmx_bool pdb_next_x(t_trxstatus *status, FILE *fp, t_trxframe *fr)
 +{
 +    t_atoms   atoms;
 +    matrix    boxpdb;
 +    int       ePBC, model_nr, na;
 +    char      title[STRLEN], *time;
 +    double    dbl;
 +
 +    atoms.nr      = fr->natoms;
 +    atoms.atom    = NULL;
 +    atoms.pdbinfo = NULL;
 +    /* the other pointers in atoms should not be accessed if these are NULL */
 +    model_nr = NOTSET;
 +    na       = read_pdbfile(fp, title, &model_nr, &atoms, fr->x, &ePBC, boxpdb, TRUE, NULL);
 +    set_trxframe_ePBC(fr, ePBC);
 +    if (nframes_read(status) == 0)
 +    {
 +        fprintf(stderr, " '%s', %d atoms\n", title, fr->natoms);
 +    }
 +    fr->bPrec = TRUE;
 +    fr->prec  = 10000;
 +    fr->bX    = TRUE;
 +    fr->bBox  = (boxpdb[XX][XX] != 0.0);
 +    if (fr->bBox)
 +    {
 +        copy_mat(boxpdb, fr->box);
 +    }
 +
 +    if (model_nr != NOTSET)
 +    {
 +        fr->bStep = TRUE;
 +        fr->step  = model_nr;
 +    }
 +    time = strstr(title, " t= ");
 +    if (time)
 +    {
 +        fr->bTime = TRUE;
 +        sscanf(time+4, "%lf", &dbl);
 +        fr->time = (real)dbl;
 +    }
 +    else
 +    {
 +        fr->bTime = FALSE;
 +        /* this is a bit dirty, but it will work: if no time is read from
 +           comment line in pdb file, set time to current frame number */
 +        if (fr->bStep)
 +        {
 +            fr->time = (real)fr->step;
 +        }
 +        else
 +        {
 +            fr->time = (real)nframes_read(status);
 +        }
 +    }
 +    if (na == 0)
 +    {
 +        return FALSE;
 +    }
 +    else
 +    {
 +        if (na != fr->natoms)
 +        {
 +            gmx_fatal(FARGS, "Number of atoms in pdb frame %d is %d instead of %d",
 +                      nframes_read(status), na, fr->natoms);
 +        }
 +        return TRUE;
 +    }
 +}
 +
 +static int pdb_first_x(t_trxstatus *status, FILE *fp, t_trxframe *fr)
 +{
 +    initcount(status);
 +
 +    fprintf(stderr, "Reading frames from pdb file");
 +    frewind(fp);
 +    get_pdb_coordnum(fp, &fr->natoms);
 +    if (fr->natoms == 0)
 +    {
 +        gmx_fatal(FARGS, "\nNo coordinates in pdb file\n");
 +    }
 +    frewind(fp);
 +    snew(fr->x, fr->natoms);
 +    pdb_next_x(status, fp, fr);
 +
 +    return fr->natoms;
 +}
 +
 +gmx_bool read_next_frame(const output_env_t oenv, t_trxstatus *status, t_trxframe *fr)
 +{
 +    real     pt;
 +    int      ct;
 +    gmx_bool bOK, bRet, bMissingData = FALSE, bSkip = FALSE;
 +    int      dummy = 0;
 +    int      ftp;
 +
 +    bRet = FALSE;
-         fr->tpf  = fr->time;
++    pt   = fr->tf;
 +
 +    do
 +    {
 +        clear_trxframe(fr, FALSE);
 +        fr->tppf = fr->tpf;
-                 if (bTimeSet(TBEGIN) && (fr->time < rTimeValue(TBEGIN)))
++        fr->tpf  = fr->tf;
 +
 +        if (status->tng)
 +        {
 +            /* Special treatment for TNG files */
 +            ftp = efTNG;
 +        }
 +        else
 +        {
 +            ftp = gmx_fio_getftp(status->fio);
 +        }
 +        switch (ftp)
 +        {
 +            case efTRJ:
 +            case efTRR:
 +                bRet = gmx_next_frame(status, fr);
 +                break;
 +            case efCPT:
 +                /* Checkpoint files can not contain mulitple frames */
 +                break;
 +            case efG96:
 +                read_g96_conf(gmx_fio_getfp(status->fio), NULL, fr,
 +                              status->persistent_line);
 +                bRet = (fr->natoms > 0);
 +                break;
 +            case efXTC:
 +                /* B. Hess 2005-4-20
 +                 * Sometimes is off by one frame
 +                 * and sometimes reports frame not present/file not seekable
 +                 */
 +                /* DvdS 2005-05-31: this has been fixed along with the increased
 +                 * accuracy of the control over -b and -e options.
 +                 */
++                if (bTimeSet(TBEGIN) && (fr->tf < rTimeValue(TBEGIN)))
 +                {
 +                    if (xtc_seek_time(status->fio, rTimeValue(TBEGIN), fr->natoms, TRUE))
 +                    {
 +                        gmx_fatal(FARGS, "Specified frame (time %f) doesn't exist or file corrupt/inconsistent.",
 +                                  rTimeValue(TBEGIN));
 +                    }
 +                    initcount(status);
 +                }
 +                bRet = read_next_xtc(status->fio, fr->natoms, &fr->step, &fr->time, fr->box,
 +                                     fr->x, &fr->prec, &bOK);
 +                fr->bPrec = (bRet && fr->prec > 0);
 +                fr->bStep = bRet;
 +                fr->bTime = bRet;
 +                fr->bX    = bRet;
 +                fr->bBox  = bRet;
 +                if (!bOK)
 +                {
 +                    /* Actually the header could also be not ok,
 +                       but from bOK from read_next_xtc this can't be distinguished */
 +                    fr->not_ok = DATA_NOT_OK;
 +                }
 +                break;
 +            case efTNG:
 +                bRet = gmx_read_next_tng_frame(status->tng, fr, NULL, 0);
 +                break;
 +            case efPDB:
 +                bRet = pdb_next_x(status, gmx_fio_getfp(status->fio), fr);
 +                break;
 +            case efGRO:
 +                bRet = gro_next_x_or_v(gmx_fio_getfp(status->fio), fr);
 +                break;
 +            default:
 +#ifdef GMX_USE_PLUGINS
 +                bRet = read_next_vmd_frame(fr);
 +#else
 +                gmx_fatal(FARGS, "DEATH HORROR in read_next_frame ftp=%s,status=%s",
 +                          ftp2ext(gmx_fio_getftp(status->fio)),
 +                          gmx_fio_getname(status->fio));
 +#endif
 +        }
++        fr->tf = fr->time;
 +
 +        if (bRet)
 +        {
 +            bMissingData = (((fr->flags & TRX_NEED_X) && !fr->bX) ||
 +                            ((fr->flags & TRX_NEED_V) && !fr->bV) ||
 +                            ((fr->flags & TRX_NEED_F) && !fr->bF));
 +            bSkip = FALSE;
 +            if (!bMissingData)
 +            {
 +                ct = check_times2(fr->time, fr->t0, fr->bDouble);
 +                if (ct == 0 || ((fr->flags & TRX_DONT_SKIP) && ct < 0))
 +                {
 +                    printcount(status, oenv, fr->time, FALSE);
 +                }
 +                else if (ct > 0)
 +                {
 +                    bRet = FALSE;
 +                }
 +                else
 +                {
 +                    printcount(status, oenv, fr->time, TRUE);
 +                    bSkip = TRUE;
 +                }
 +            }
 +        }
 +
 +    }
 +    while (bRet && (bMissingData || bSkip));
 +
 +    if (!bRet)
 +    {
 +        printlast(status, oenv, pt);
 +        if (fr->not_ok)
 +        {
 +            printincomp(status, fr);
 +        }
 +    }
 +
 +    return bRet;
 +}
 +
 +int read_first_frame(const output_env_t oenv, t_trxstatus **status,
 +                     const char *fn, t_trxframe *fr, int flags)
 +{
 +    t_fileio      *fio;
 +    gmx_bool       bFirst, bOK;
 +    int            dummy = 0;
 +    int            ftp   = fn2ftp(fn);
 +    gmx_int64_t   *tng_ids;
 +
 +    clear_trxframe(fr, TRUE);
 +    fr->flags = flags;
 +
 +    bFirst = TRUE;
 +
 +    snew((*status), 1);
 +
 +    status_init( *status );
 +    (*status)->nxframe = 1;
 +    initcount(*status);
 +
 +    if (efTNG == ftp)
 +    {
 +        /* Special treatment for TNG files */
 +        gmx_tng_open(fn, 'r', &(*status)->tng);
 +    }
 +    else
 +    {
 +        fio = (*status)->fio = gmx_fio_open(fn, "r");
 +    }
 +    switch (ftp)
 +    {
 +        case efTRJ:
 +        case efTRR:
 +            break;
 +        case efCPT:
 +            read_checkpoint_trxframe(fio, fr);
 +            bFirst = FALSE;
 +            break;
 +        case efG96:
 +            /* Can not rewind a compressed file, so open it twice */
 +            if (!(*status)->persistent_line)
 +            {
 +                /* allocate the persistent line */
 +                snew((*status)->persistent_line, STRLEN+1);
 +            }
 +            read_g96_conf(gmx_fio_getfp(fio), fn, fr, (*status)->persistent_line);
 +            gmx_fio_close(fio);
 +            clear_trxframe(fr, FALSE);
 +            if (flags & (TRX_READ_X | TRX_NEED_X))
 +            {
 +                snew(fr->x, fr->natoms);
 +            }
 +            if (flags & (TRX_READ_V | TRX_NEED_V))
 +            {
 +                snew(fr->v, fr->natoms);
 +            }
 +            fio = (*status)->fio = gmx_fio_open(fn, "r");
 +            break;
 +        case efXTC:
 +            if (read_first_xtc(fio, &fr->natoms, &fr->step, &fr->time, fr->box, &fr->x,
 +                               &fr->prec, &bOK) == 0)
 +            {
 +                assert(!bOK);
 +                fr->not_ok = DATA_NOT_OK;
 +            }
 +            if (fr->not_ok)
 +            {
 +                fr->natoms = 0;
 +                printincomp(*status, fr);
 +            }
 +            else
 +            {
 +                fr->bPrec = (fr->prec > 0);
 +                fr->bStep = TRUE;
 +                fr->bTime = TRUE;
 +                fr->bX    = TRUE;
 +                fr->bBox  = TRUE;
 +                printcount(*status, oenv, fr->time, FALSE);
 +            }
 +            bFirst = FALSE;
 +            break;
 +        case efTNG:
 +            fr->step = -1;
 +            if (!gmx_read_next_tng_frame((*status)->tng, fr, NULL, 0))
 +            {
 +                fr->not_ok = DATA_NOT_OK;
 +                fr->natoms = 0;
 +                printincomp(*status, fr);
 +            }
 +            else
 +            {
 +                printcount(*status, oenv, fr->time, FALSE);
 +            }
 +            bFirst = FALSE;
 +            break;
 +        case efPDB:
 +            pdb_first_x(*status, gmx_fio_getfp(fio), fr);
 +            if (fr->natoms)
 +            {
 +                printcount(*status, oenv, fr->time, FALSE);
 +            }
 +            bFirst = FALSE;
 +            break;
 +        case efGRO:
 +            if (gro_first_x_or_v(gmx_fio_getfp(fio), fr))
 +            {
 +                printcount(*status, oenv, fr->time, FALSE);
 +            }
 +            bFirst = FALSE;
 +            break;
 +        default:
 +#ifdef GMX_USE_PLUGINS
 +            fprintf(stderr, "The file format of %s is not a known trajectory format to GROMACS.\n"
 +                    "Please make sure that the file is a trajectory!\n"
 +                    "GROMACS will now assume it to be a trajectory and will try to open it using the VMD plug-ins.\n"
 +                    "This will only work in case the VMD plugins are found and it is a trajectory format supported by VMD.\n", fn);
 +            gmx_fio_fp_close(fio); /*only close the file without removing FIO entry*/
 +            if (!read_first_vmd_frame(fn, fr))
 +            {
 +                gmx_fatal(FARGS, "Not supported in read_first_frame: %s", fn);
 +            }
 +#else
 +            gmx_fatal(FARGS, "Not supported in read_first_frame: %s. Please make sure that the file is a trajectory.\n"
 +                      "GROMACS is not compiled with plug-in support. Thus it cannot read non-GROMACS trajectory formats using the VMD plug-ins.\n"
 +                      "Please compile with plug-in support if you want to read non-GROMACS trajectory formats.\n", fn);
 +#endif
 +            break;
 +    }
++    fr->tf = fr->time;
 +
 +    /* Return FALSE if we read a frame that's past the set ending time. */
 +    if (!bFirst && (!(fr->flags & TRX_DONT_SKIP) && check_times(fr->time) > 0))
 +    {
 +        fr->t0 = fr->time;
 +        return FALSE;
 +    }
 +
 +    if (bFirst ||
 +        (!(fr->flags & TRX_DONT_SKIP) && check_times(fr->time) < 0))
 +    {
 +        /* Read a frame when no frame was read or the first was skipped */
 +        if (!read_next_frame(oenv, *status, fr))
 +        {
 +            return FALSE;
 +        }
 +    }
 +    fr->t0 = fr->time;
 +
 +    return (fr->natoms > 0);
 +}
 +
 +/***** C O O R D I N A T E   S T U F F *****/
 +
 +int read_first_x(const output_env_t oenv, t_trxstatus **status, const char *fn,
 +                 real *t, rvec **x, matrix box)
 +{
 +    t_trxframe fr;
 +
 +    read_first_frame(oenv, status, fn, &fr, TRX_NEED_X);
 +
 +    snew((*status)->xframe, 1);
 +    (*status)->nxframe   = 1;
 +    (*(*status)->xframe) = fr;
 +    *t                   = (*status)->xframe->time;
 +    *x                   = (*status)->xframe->x;
 +    copy_mat((*status)->xframe->box, box);
 +
 +    return (*status)->xframe->natoms;
 +}
 +
 +gmx_bool read_next_x(const output_env_t oenv, t_trxstatus *status, real *t,
 +                     rvec x[], matrix box)
 +{
 +    gmx_bool bRet;
 +
 +    status->xframe->x = x;
 +    /*xframe[status].x = x;*/
 +    bRet = read_next_frame(oenv, status, status->xframe);
 +    *t   = status->xframe->time;
 +    copy_mat(status->xframe->box, box);
 +
 +    return bRet;
 +}
 +
 +void close_trj(t_trxstatus *status)
 +{
 +    gmx_tng_close(&status->tng);
 +    if (status->fio)
 +    {
 +        gmx_fio_close(status->fio);
 +    }
 +
 +    /* The memory in status->xframe is lost here,
 +     * but the read_first_x/read_next_x functions are deprecated anyhow.
 +     * read_first_frame/read_next_frame and close_trx should be used.
 +     */
 +    sfree(status);
 +}
 +
 +void rewind_trj(t_trxstatus *status)
 +{
 +    initcount(status);
 +
 +    gmx_fio_rewind(status->fio);
 +}
 +
 +/***** T O P O L O G Y   S T U F F ******/
 +
 +t_topology *read_top(const char *fn, int *ePBC)
 +{
 +    int         epbc, natoms;
 +    t_topology *top;
 +
 +    snew(top, 1);
 +    epbc = read_tpx_top(fn, NULL, NULL, &natoms, NULL, NULL, NULL, top);
 +    if (ePBC)
 +    {
 +        *ePBC = epbc;
 +    }
 +
 +    return top;
 +}
index a9d28823e8d8af3d6cfd1a4054d8ae3191ef7f65,0000000000000000000000000000000000000000..de46abf7ff78fcef8c0126334f3d4796a6e842ab
mode 100644,000000..100644
--- /dev/null
@@@ -1,1008 -1,0 +1,1012 @@@
-     for (Dih = 0; (Dih < NONCHI+maxchi); Dih++)
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <stdio.h>
 +#include <string.h>
 +#include "physics.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "txtdump.h"
 +#include "bondf.h"
 +#include "xvgr.h"
 +#include "typedefs.h"
 +#include "vec.h"
 +#include "gstat.h"
 +#include "gromacs/fileio/confio.h"
 +#include "gromacs/fileio/trxio.h"
 +
 +#include "gromacs/legacyheaders/gmx_fatal.h"
 +
 +void print_one(const output_env_t oenv, const char *base, const char *name,
 +               const char *title, const char *ylabel, int nf, real time[],
 +               real data[])
 +{
 +    FILE *fp;
 +    char  buf[256], t2[256];
 +    int   k;
 +
 +    sprintf(buf, "%s%s.xvg", base, name);
 +    fprintf(stderr, "\rPrinting %s  ", buf);
 +    sprintf(t2, "%s %s", title, name);
 +    fp = xvgropen(buf, t2, "Time (ps)", ylabel, oenv);
 +    for (k = 0; (k < nf); k++)
 +    {
 +        fprintf(fp, "%10g  %10g\n", time[k], data[k]);
 +    }
 +    gmx_ffclose(fp);
 +}
 +
 +static int calc_RBbin(real phi, int gmx_unused multiplicity, real gmx_unused core_frac)
 +{
 +    /* multiplicity and core_frac NOT used,
 +     * just given to enable use of pt-to-fn in caller low_ana_dih_trans*/
 +    static const real r30  = M_PI/6.0;
 +    static const real r90  = M_PI/2.0;
 +    static const real r150 = M_PI*5.0/6.0;
 +
 +    if ((phi < r30) && (phi > -r30))
 +    {
 +        return 1;
 +    }
 +    else if ((phi > -r150) && (phi < -r90))
 +    {
 +        return 2;
 +    }
 +    else if ((phi < r150) && (phi > r90))
 +    {
 +        return 3;
 +    }
 +    return 0;
 +}
 +
 +static int calc_Nbin(real phi, int multiplicity, real core_frac)
 +{
 +    static const real r360 = 360*DEG2RAD;
 +    real              rot_width, core_width, core_offset, low, hi;
 +    int               bin;
 +    /* with multiplicity 3 and core_frac 0.5
 +     * 0<g(-)<120, 120<t<240, 240<g(+)<360
 +     * 0< bin0 < 30, 30<bin1<90, 90<bin0<150, 150<bin2<210, 210<bin0<270, 270<bin3<330, 330<bin0<360
 +     * so with multiplicity 3, bin1 is core g(-), bin2 is core t, bin3 is
 +       core g(+), bin0 is between rotamers */
 +    if (phi < 0)
 +    {
 +        phi += r360;
 +    }
 +
 +    rot_width   = 360/multiplicity;
 +    core_width  = core_frac * rot_width;
 +    core_offset = (rot_width - core_width)/2.0;
 +    for (bin = 1; bin <= multiplicity; bin++)
 +    {
 +        low  = ((bin - 1) * rot_width ) + core_offset;
 +        hi   = ((bin - 1) * rot_width ) + core_offset + core_width;
 +        low *= DEG2RAD;
 +        hi  *= DEG2RAD;
 +        if ((phi > low) && (phi < hi))
 +        {
 +            return bin;
 +        }
 +    }
 +    return 0;
 +}
 +
 +void ana_dih_trans(const char *fn_trans, const char *fn_histo,
 +                   real **dih, int nframes, int nangles,
 +                   const char *grpname, real *time, gmx_bool bRb,
 +                   const output_env_t oenv)
 +{
 +    /* just a wrapper; declare extra args, then chuck away at end. */
 +    int      maxchi = 0;
 +    t_dlist *dlist;
 +    int     *multiplicity;
 +    int      nlist = nangles;
 +    int      k;
 +
 +    snew(dlist, nlist);
 +    snew(multiplicity, nangles);
 +    for (k = 0; (k < nangles); k++)
 +    {
 +        multiplicity[k] = 3;
 +    }
 +
 +    low_ana_dih_trans(TRUE, fn_trans, TRUE, fn_histo, maxchi,
 +                      dih, nlist, dlist, nframes,
 +                      nangles, grpname, multiplicity, time, bRb, 0.5, oenv);
 +    sfree(dlist);
 +    sfree(multiplicity);
 +
 +}
 +
 +void low_ana_dih_trans(gmx_bool bTrans, const char *fn_trans,
 +                       gmx_bool bHisto, const char *fn_histo, int maxchi,
 +                       real **dih, int nlist, t_dlist dlist[], int nframes,
 +                       int nangles, const char *grpname, int multiplicity[],
 +                       real *time, gmx_bool bRb, real core_frac,
 +                       const output_env_t oenv)
 +{
 +    FILE *fp;
 +    int  *tr_f, *tr_h;
 +    char  title[256];
 +    int   i, j, k, Dih, ntrans;
 +    int   cur_bin, new_bin;
 +    real  ttime, tt;
 +    real *rot_occ[NROT];
 +    int   (*calc_bin)(real, int, real);
 +    real  dt;
 +
 +    if (1 <= nframes)
 +    {
 +        return;
 +    }
 +    /* Assumes the frames are equally spaced in time */
 +    dt = (time[nframes-1]-time[0])/(nframes-1);
 +
 +    /* Analysis of dihedral transitions */
 +    fprintf(stderr, "Now calculating transitions...\n");
 +
 +    if (bRb)
 +    {
 +        calc_bin = calc_RBbin;
 +    }
 +    else
 +    {
 +        calc_bin = calc_Nbin;
 +    }
 +
 +    for (k = 0; k < NROT; k++)
 +    {
 +        snew(rot_occ[k], nangles);
 +        for (i = 0; (i < nangles); i++)
 +        {
 +            rot_occ[k][i] = 0;
 +        }
 +    }
 +    snew(tr_h, nangles);
 +    snew(tr_f, nframes);
 +
 +    /* dih[i][j] is the dihedral angle i in frame j  */
 +    ntrans = 0;
 +    for (i = 0; (i < nangles); i++)
 +    {
 +
 +        /*#define OLDIE*/
 +#ifdef OLDIE
 +        mind = maxd = prev = dih[i][0];
 +#else
 +        cur_bin = calc_bin(dih[i][0], multiplicity[i], core_frac);
 +        rot_occ[cur_bin][i]++;
 +#endif
 +        for (j = 1; (j < nframes); j++)
 +        {
 +            new_bin = calc_bin(dih[i][j], multiplicity[i], core_frac);
 +            rot_occ[new_bin][i]++;
 +#ifndef OLDIE
 +            if (cur_bin == 0)
 +            {
 +                cur_bin = new_bin;
 +            }
 +            else if ((new_bin != 0) && (cur_bin != new_bin))
 +            {
 +                cur_bin = new_bin;
 +                tr_f[j]++;
 +                tr_h[i]++;
 +                ntrans++;
 +            }
 +#else
 +            /* why is all this md rubbish periodic? Remove 360 degree periodicity */
 +            if ( (dih[i][j] - prev) > M_PI)
 +            {
 +                dih[i][j] -= 2*M_PI;
 +            }
 +            else if ( (dih[i][j] - prev) < -M_PI)
 +            {
 +                dih[i][j] += 2*M_PI;
 +            }
 +
 +            prev = dih[i][j];
 +
 +            mind = min(mind, dih[i][j]);
 +            maxd = max(maxd, dih[i][j]);
 +            if ( (maxd - mind) > 2*M_PI/3) /* or 120 degrees, assuming       */
 +            {                              /* multiplicity 3. Not so general.*/
 +                tr_f[j]++;
 +                tr_h[i]++;
 +                maxd = mind = dih[i][j]; /* get ready for next transition  */
 +                ntrans++;
 +            }
 +#endif
 +        } /* end j */
 +        for (k = 0; k < NROT; k++)
 +        {
 +            rot_occ[k][i] /= nframes;
 +        }
 +    } /* end i */
 +    fprintf(stderr, "Total number of transitions: %10d\n", ntrans);
 +    if (ntrans > 0)
 +    {
 +        ttime = (dt*nframes*nangles)/ntrans;
 +        fprintf(stderr, "Time between transitions:    %10.3f ps\n", ttime);
 +    }
 +
 +    /* new by grs - copy transitions from tr_h[] to dlist->ntr[]
 +     * and rotamer populations from rot_occ to dlist->rot_occ[]
 +     * based on fn histogramming in g_chi. diff roles for i and j here */
 +
 +    j = 0;
 +    for (Dih = 0; (Dih < NONCHI+maxchi); Dih++)
 +    {
 +        for (i = 0; (i < nlist); i++)
 +        {
 +            if (((Dih  < edOmega) ) ||
 +                ((Dih == edOmega) && (has_dihedral(edOmega, &(dlist[i])))) ||
 +                ((Dih  > edOmega) && (dlist[i].atm.Cn[Dih-NONCHI+3] != -1)))
 +            {
 +                /* grs debug  printf("Not OK? i %d j %d Dih %d \n", i, j, Dih) ; */
 +                dlist[i].ntr[Dih] = tr_h[j];
 +                for (k = 0; k < NROT; k++)
 +                {
 +                    dlist[i].rot_occ[Dih][k] = rot_occ[k][j];
 +                }
 +                j++;
 +            }
 +        }
 +    }
 +
 +    /* end addition by grs */
 +
 +    if (bTrans)
 +    {
 +        sprintf(title, "Number of transitions: %s", grpname);
 +        fp = xvgropen(fn_trans, title, "Time (ps)", "# transitions/timeframe", oenv);
 +        for (j = 0; (j < nframes); j++)
 +        {
 +            fprintf(fp, "%10.3f  %10d\n", time[j], tr_f[j]);
 +        }
 +        gmx_ffclose(fp);
 +    }
 +
 +    /* Compute histogram from # transitions per dihedral */
 +    /* Use old array */
 +    for (j = 0; (j < nframes); j++)
 +    {
 +        tr_f[j] = 0;
 +    }
 +    for (i = 0; (i < nangles); i++)
 +    {
 +        tr_f[tr_h[i]]++;
 +    }
 +    for (j = nframes; ((tr_f[j-1] == 0) && (j > 0)); j--)
 +    {
 +        ;
 +    }
 +
 +    ttime = dt*nframes;
 +    if (bHisto)
 +    {
 +        sprintf(title, "Transition time: %s", grpname);
 +        fp = xvgropen(fn_histo, title, "Time (ps)", "#", oenv);
 +        for (i = j-1; (i > 0); i--)
 +        {
 +            if (tr_f[i] != 0)
 +            {
 +                fprintf(fp, "%10.3f  %10d\n", ttime/i, tr_f[i]);
 +            }
 +        }
 +        gmx_ffclose(fp);
 +    }
 +
 +    sfree(tr_f);
 +    sfree(tr_h);
 +    for (k = 0; k < NROT; k++)
 +    {
 +        sfree(rot_occ[k]);
 +    }
 +
 +}
 +
 +void mk_multiplicity_lookup (int *multiplicity, int maxchi,
 +                             int nlist, t_dlist dlist[], int nangles)
 +{
 +    /* new by grs - for dihedral j (as in dih[j]) get multiplicity from dlist
 +     * and store in multiplicity[j]
 +     */
 +
 +    int  j, Dih, i;
 +    char name[4];
 +
 +    j = 0;
 +    for (Dih = 0; (Dih < NONCHI+maxchi); Dih++)
 +    {
 +        for (i = 0; (i < nlist); i++)
 +        {
 +            strncpy(name, dlist[i].name, 3);
 +            name[3] = '\0';
 +            if (((Dih  < edOmega) ) ||
 +                ((Dih == edOmega) && (has_dihedral(edOmega, &(dlist[i])))) ||
 +                ((Dih  > edOmega) && (dlist[i].atm.Cn[Dih-NONCHI+3] != -1)))
 +            {
 +                /* default - we will correct the rest below */
 +                multiplicity[j] = 3;
 +
 +                /* make omegas 2fold, though doesn't make much more sense than 3 */
 +                if (Dih == edOmega && (has_dihedral(edOmega, &(dlist[i]))))
 +                {
 +                    multiplicity[j] = 2;
 +                }
 +
 +                /* dihedrals to aromatic rings, COO, CONH2 or guanidinium are 2fold*/
 +                if (Dih > edOmega && (dlist[i].atm.Cn[Dih-NONCHI+3] != -1))
 +                {
 +                    if ( ((strstr(name, "PHE") != NULL) && (Dih == edChi2))  ||
 +                         ((strstr(name, "TYR") != NULL) && (Dih == edChi2))  ||
 +                         ((strstr(name, "PTR") != NULL) && (Dih == edChi2))  ||
 +                         ((strstr(name, "TRP") != NULL) && (Dih == edChi2))  ||
 +                         ((strstr(name, "HIS") != NULL) && (Dih == edChi2))  ||
 +                         ((strstr(name, "GLU") != NULL) && (Dih == edChi3))  ||
 +                         ((strstr(name, "ASP") != NULL) && (Dih == edChi2))  ||
 +                         ((strstr(name, "GLN") != NULL) && (Dih == edChi3))  ||
 +                         ((strstr(name, "ASN") != NULL) && (Dih == edChi2))  ||
 +                         ((strstr(name, "ARG") != NULL) && (Dih == edChi4))  )
 +                    {
 +                        multiplicity[j] = 2;
 +                    }
 +                }
 +                j++;
 +            }
 +        }
 +    }
 +    if (j < nangles)
 +    {
 +        fprintf(stderr, "WARNING: not all dihedrals found in topology (only %d out of %d)!\n",
 +                j, nangles);
 +    }
 +    /* Check for remaining dihedrals */
 +    for (; (j < nangles); j++)
 +    {
 +        multiplicity[j] = 3;
 +    }
 +
 +}
 +
 +void mk_chi_lookup (int **lookup, int maxchi,
 +                    int nlist, t_dlist dlist[])
 +{
 +
 +    /* by grs. should rewrite everything to use this. (but haven't,
 +     * and at mmt only used in get_chi_product_traj
 +     * returns the dihed number given the residue number (from-0)
 +     * and chi (from-0) nr. -1 for chi undefined for that res (eg gly, ala..)*/
 +
 +    int i, j, Dih, Chi;
 +
 +    j = 0;
-                 fprintf(fp, "@ xaxis tick on\n");
-                 fprintf(fp, "@ xaxis tick major 1\n");
-                 fprintf(fp, "@ type xy\n");
++    /* NONCHI points to chi1, therefore we have to start counting there. */
++    for (Dih = NONCHI; (Dih < NONCHI+maxchi); Dih++)
 +    {
 +        for (i = 0; (i < nlist); i++)
 +        {
 +            Chi = Dih - NONCHI;
 +            if (((Dih  < edOmega) ) ||
 +                ((Dih == edOmega) && (has_dihedral(edOmega, &(dlist[i])))) ||
 +                ((Dih  > edOmega) && (dlist[i].atm.Cn[Dih-NONCHI+3] != -1)))
 +            {
 +                /* grs debug  printf("Not OK? i %d j %d Dih %d \n", i, j, Dih) ; */
 +                if (Dih > edOmega)
 +                {
 +                    lookup[i][Chi] = j;
 +                }
 +                j++;
 +            }
 +            else
 +            {
 +                lookup[i][Chi] = -1;
 +            }
 +        }
 +    }
 +
 +}
 +
 +
 +void get_chi_product_traj (real **dih, int nframes, int nlist,
 +                           int maxchi, t_dlist dlist[], real time[],
 +                           int **lookup, int *multiplicity, gmx_bool bRb, gmx_bool bNormalize,
 +                           real core_frac, gmx_bool bAll, const char *fnall,
 +                           const output_env_t oenv)
 +{
 +
 +    gmx_bool bRotZero, bHaveChi = FALSE;
 +    int      accum = 0, index, i, j, k, Xi, n, b;
 +    real    *chi_prtrj;
 +    int     *chi_prhist;
 +    int      nbin;
 +    FILE    *fp, *fpall;
 +    char     hisfile[256], histitle[256], *namept;
 +
 +    int      (*calc_bin)(real, int, real);
 +
 +    /* Analysis of dihedral transitions */
 +    fprintf(stderr, "Now calculating Chi product trajectories...\n");
 +
 +    if (bRb)
 +    {
 +        calc_bin = calc_RBbin;
 +    }
 +    else
 +    {
 +        calc_bin = calc_Nbin;
 +    }
 +
 +    snew(chi_prtrj, nframes);
 +
 +    /* file for info on all residues */
 +    if (bNormalize)
 +    {
 +        fpall = xvgropen(fnall, "Cumulative Rotamers", "Residue", "Probability", oenv);
 +    }
 +    else
 +    {
 +        fpall = xvgropen(fnall, "Cumulative Rotamers", "Residue", "# Counts", oenv);
 +    }
 +
 +    for (i = 0; (i < nlist); i++)
 +    {
 +
 +        /* get nbin, the nr. of cumulative rotamers that need to be considered */
 +        nbin = 1;
 +        for (Xi = 0; Xi < maxchi; Xi++)
 +        {
 +            index = lookup[i][Xi]; /* chi_(Xi+1) of res i (-1 if off end) */
 +            if (index >= 0)
 +            {
 +                n    = multiplicity[index];
 +                nbin = n*nbin;
 +            }
 +        }
 +        nbin += 1; /* for the "zero rotamer", outside the core region */
 +
 +        for (j = 0; (j < nframes); j++)
 +        {
 +
 +            bRotZero = FALSE;
 +            bHaveChi = TRUE;
 +            index    = lookup[i][0]; /* index into dih of chi1 of res i */
 +            if (index == -1)
 +            {
 +                b        = 0;
 +                bRotZero = TRUE;
 +                bHaveChi = FALSE;
 +            }
 +            else
 +            {
 +                b     = calc_bin(dih[index][j], multiplicity[index], core_frac);
 +                accum = b - 1;
 +                if (b == 0)
 +                {
 +                    bRotZero = TRUE;
 +                }
 +                for (Xi = 1; Xi < maxchi; Xi++)
 +                {
 +                    index = lookup[i][Xi]; /* chi_(Xi+1) of res i (-1 if off end) */
 +                    if (index >= 0)
 +                    {
 +                        n     = multiplicity[index];
 +                        b     = calc_bin(dih[index][j], n, core_frac);
 +                        accum = n * accum + b - 1;
 +                        if (b == 0)
 +                        {
 +                            bRotZero = TRUE;
 +                        }
 +                    }
 +                }
 +                accum++;
 +            }
 +            if (bRotZero)
 +            {
 +                chi_prtrj[j] = 0.0;
 +            }
 +            else
 +            {
 +                chi_prtrj[j] = accum;
 +                if (accum+1 > nbin)
 +                {
 +                    nbin = accum+1;
 +                }
 +            }
 +        }
 +        if (bHaveChi)
 +        {
 +
 +            if (bAll)
 +            {
 +                /* print cuml rotamer vs time */
 +                print_one(oenv, "chiproduct", dlist[i].name, "chi product for",
 +                          "cumulative rotamer", nframes, time, chi_prtrj);
 +            }
 +
 +            /* make a histogram pf culm. rotamer occupancy too */
 +            snew(chi_prhist, nbin);
 +            make_histo(NULL, nframes, chi_prtrj, nbin, chi_prhist, 0, nbin);
 +            if (bAll)
 +            {
 +                sprintf(hisfile, "histo-chiprod%s.xvg", dlist[i].name);
 +                sprintf(histitle, "cumulative rotamer distribution for %s", dlist[i].name);
 +                fprintf(stderr, "  and %s  ", hisfile);
 +                fp = xvgropen(hisfile, histitle, "number", "", oenv);
-                 fprintf(fp, "&\n");
++                if (output_env_get_print_xvgr_codes(oenv))
++                {
++                    fprintf(fp, "@ xaxis tick on\n");
++                    fprintf(fp, "@ xaxis tick major 1\n");
++                    fprintf(fp, "@ type xy\n");
++                }
 +                for (k = 0; (k < nbin); k++)
 +                {
 +                    if (bNormalize)
 +                    {
 +                        fprintf(fp, "%5d  %10g\n", k, (1.0*chi_prhist[k])/nframes);
 +                    }
 +                    else
 +                    {
 +                        fprintf(fp, "%5d  %10d\n", k, chi_prhist[k]);
 +                    }
 +                }
++                fprintf(fp, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +                gmx_ffclose(fp);
 +            }
 +
 +            /* and finally print out occupancies to a single file */
 +            /* get the gmx from-1 res nr by setting a ptr to the number part
 +             * of dlist[i].name - potential bug for 4-letter res names... */
 +            namept = dlist[i].name + 3;
 +            fprintf(fpall, "%5s ", namept);
 +            for (k = 0; (k < nbin); k++)
 +            {
 +                if (bNormalize)
 +                {
 +                    fprintf(fpall, "  %10g", (1.0*chi_prhist[k])/nframes);
 +                }
 +                else
 +                {
 +                    fprintf(fpall, "  %10d", chi_prhist[k]);
 +                }
 +            }
 +            fprintf(fpall, "\n");
 +
 +            sfree(chi_prhist);
 +            /* histogram done */
 +        }
 +    }
 +
 +    sfree(chi_prtrj);
 +    gmx_ffclose(fpall);
 +    fprintf(stderr, "\n");
 +
 +}
 +
 +void calc_distribution_props(int nh, int histo[], real start,
 +                             int nkkk, t_karplus kkk[],
 +                             real *S2)
 +{
 +    real d, dc, ds, c1, c2, tdc, tds;
 +    real fac, ang, invth, Jc;
 +    int  i, j, th;
 +
 +    if (nh == 0)
 +    {
 +        gmx_fatal(FARGS, "No points in histogram (%s, %d)", __FILE__, __LINE__);
 +    }
 +    fac = 2*M_PI/nh;
 +
 +    /* Compute normalisation factor */
 +    th = 0;
 +    for (j = 0; (j < nh); j++)
 +    {
 +        th += histo[j];
 +    }
 +    invth = 1.0/th;
 +
 +    for (i = 0; (i < nkkk); i++)
 +    {
 +        kkk[i].Jc    = 0;
 +        kkk[i].Jcsig = 0;
 +    }
 +    tdc = 0, tds = 0;
 +    for (j = 0; (j < nh); j++)
 +    {
 +        d    = invth*histo[j];
 +        ang  = j*fac-start;
 +        c1   = cos(ang);
 +        c2   = c1*c1;
 +        dc   = d*c1;
 +        ds   = d*sin(ang);
 +        tdc += dc;
 +        tds += ds;
 +        for (i = 0; (i < nkkk); i++)
 +        {
 +            c1            = cos(ang+kkk[i].offset);
 +            c2            = c1*c1;
 +            Jc            = (kkk[i].A*c2 + kkk[i].B*c1 + kkk[i].C);
 +            kkk[i].Jc    += histo[j]*Jc;
 +            kkk[i].Jcsig += histo[j]*sqr(Jc);
 +        }
 +    }
 +    for (i = 0; (i < nkkk); i++)
 +    {
 +        kkk[i].Jc    /= th;
 +        kkk[i].Jcsig  = sqrt(kkk[i].Jcsig/th-sqr(kkk[i].Jc));
 +    }
 +    *S2 = tdc*tdc+tds*tds;
 +}
 +
 +static void calc_angles(t_pbc *pbc,
 +                        int n3, atom_id index[], real ang[], rvec x_s[])
 +{
 +    int  i, ix, t1, t2;
 +    rvec r_ij, r_kj;
 +    real costh;
 +
 +    for (i = ix = 0; (ix < n3); i++, ix += 3)
 +    {
 +        ang[i] = bond_angle(x_s[index[ix]], x_s[index[ix+1]], x_s[index[ix+2]],
 +                            pbc, r_ij, r_kj, &costh, &t1, &t2);
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "Angle[0]=%g, costh=%g, index0 = %d, %d, %d\n",
 +                ang[0], costh, index[0], index[1], index[2]);
 +        pr_rvec(debug, 0, "rij", r_ij, DIM, TRUE);
 +        pr_rvec(debug, 0, "rkj", r_kj, DIM, TRUE);
 +    }
 +}
 +
 +static real calc_fraction(real angles[], int nangles)
 +{
 +    int  i;
 +    real trans = 0, gauche = 0;
 +    real angle;
 +
 +    for (i = 0; i < nangles; i++)
 +    {
 +        angle = angles[i] * RAD2DEG;
 +
 +        if (angle > 135 && angle < 225)
 +        {
 +            trans += 1.0;
 +        }
 +        else if (angle > 270 && angle < 330)
 +        {
 +            gauche += 1.0;
 +        }
 +        else if (angle < 90 && angle > 30)
 +        {
 +            gauche += 1.0;
 +        }
 +    }
 +    if (trans+gauche > 0)
 +    {
 +        return trans/(trans+gauche);
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +static void calc_dihs(t_pbc *pbc,
 +                      int n4, atom_id index[], real ang[], rvec x_s[])
 +{
 +    int  i, ix, t1, t2, t3;
 +    rvec r_ij, r_kj, r_kl, m, n;
 +    real sign, aaa;
 +
 +    for (i = ix = 0; (ix < n4); i++, ix += 4)
 +    {
 +        aaa = dih_angle(x_s[index[ix]], x_s[index[ix+1]], x_s[index[ix+2]],
 +                        x_s[index[ix+3]], pbc,
 +                        r_ij, r_kj, r_kl, m, n,
 +                        &sign, &t1, &t2, &t3);
 +
 +        ang[i] = aaa; /* not taking into account ryckaert bellemans yet */
 +    }
 +}
 +
 +void make_histo(FILE *log,
 +                int ndata, real data[], int npoints, int histo[],
 +                real minx, real maxx)
 +{
 +    double dx;
 +    int    i, ind;
 +
 +    if (minx == maxx)
 +    {
 +        minx = maxx = data[0];
 +        for (i = 1; (i < ndata); i++)
 +        {
 +            minx = min(minx, data[i]);
 +            maxx = max(maxx, data[i]);
 +        }
 +        fprintf(log, "Min data: %10g  Max data: %10g\n", minx, maxx);
 +    }
 +    dx = (double)npoints/(maxx-minx);
 +    if (debug)
 +    {
 +        fprintf(debug,
 +                "Histogramming: ndata=%d, nhisto=%d, minx=%g,maxx=%g,dx=%g\n",
 +                ndata, npoints, minx, maxx, dx);
 +    }
 +    for (i = 0; (i < ndata); i++)
 +    {
 +        ind = (data[i]-minx)*dx;
 +        if ((ind >= 0) && (ind < npoints))
 +        {
 +            histo[ind]++;
 +        }
 +        else
 +        {
 +            fprintf(log, "index = %d, data[%d] = %g\n", ind, i, data[i]);
 +        }
 +    }
 +}
 +
 +void normalize_histo(int npoints, int histo[], real dx, real normhisto[])
 +{
 +    int    i;
 +    double d, fac;
 +
 +    d = 0;
 +    for (i = 0; (i < npoints); i++)
 +    {
 +        d += dx*histo[i];
 +    }
 +    if (d == 0)
 +    {
 +        fprintf(stderr, "Empty histogram!\n");
 +        return;
 +    }
 +    fac = 1.0/d;
 +    for (i = 0; (i < npoints); i++)
 +    {
 +        normhisto[i] = fac*histo[i];
 +    }
 +}
 +
 +void read_ang_dih(const char *trj_fn,
 +                  gmx_bool bAngles, gmx_bool bSaveAll, gmx_bool bRb, gmx_bool bPBC,
 +                  int maxangstat, int angstat[],
 +                  int *nframes, real **time,
 +                  int isize, atom_id index[],
 +                  real **trans_frac,
 +                  real **aver_angle,
 +                  real *dih[],
 +                  const output_env_t oenv)
 +{
 +    t_pbc       *pbc;
 +    t_trxstatus *status;
 +    int          i, angind, natoms, total, teller;
 +    int          nangles, n_alloc;
 +    real         t, fraction, pifac, aa, angle;
 +    real        *angles[2];
 +    matrix       box;
 +    rvec        *x;
 +    int          cur = 0;
 +#define prev (1-cur)
 +
 +    snew(pbc, 1);
 +    natoms = read_first_x(oenv, &status, trj_fn, &t, &x, box);
 +
 +    if (bAngles)
 +    {
 +        nangles = isize/3;
 +        pifac   = M_PI;
 +    }
 +    else
 +    {
 +        nangles = isize/4;
 +        pifac   = 2.0*M_PI;
 +    }
 +    snew(angles[cur], nangles);
 +    snew(angles[prev], nangles);
 +
 +    /* Start the loop over frames */
 +    total       = 0;
 +    teller      = 0;
 +    n_alloc     = 0;
 +    *time       = NULL;
 +    *trans_frac = NULL;
 +    *aver_angle = NULL;
 +
 +    do
 +    {
 +        if (teller >= n_alloc)
 +        {
 +            n_alloc += 100;
 +            if (bSaveAll)
 +            {
 +                for (i = 0; (i < nangles); i++)
 +                {
 +                    srenew(dih[i], n_alloc);
 +                }
 +            }
 +            srenew(*time, n_alloc);
 +            srenew(*trans_frac, n_alloc);
 +            srenew(*aver_angle, n_alloc);
 +        }
 +
 +        (*time)[teller] = t;
 +
 +        if (pbc)
 +        {
 +            set_pbc(pbc, -1, box);
 +        }
 +
 +        if (bAngles)
 +        {
 +            calc_angles(pbc, isize, index, angles[cur], x);
 +        }
 +        else
 +        {
 +            calc_dihs(pbc, isize, index, angles[cur], x);
 +
 +            /* Trans fraction */
 +            fraction              = calc_fraction(angles[cur], nangles);
 +            (*trans_frac)[teller] = fraction;
 +
 +            /* Change Ryckaert-Bellemans dihedrals to polymer convention
 +             * Modified 990913 by Erik:
 +             * We actually shouldn't change the convention, since it's
 +             * calculated from polymer above, but we change the intervall
 +             * from [-180,180] to [0,360].
 +             */
 +            if (bRb)
 +            {
 +                for (i = 0; (i < nangles); i++)
 +                {
 +                    if (angles[cur][i] <= 0.0)
 +                    {
 +                        angles[cur][i] += 2*M_PI;
 +                    }
 +                }
 +            }
 +
 +            /* Periodicity in dihedral space... */
 +            if (bPBC)
 +            {
 +                for (i = 0; (i < nangles); i++)
 +                {
 +                    real dd = angles[cur][i];
 +                    angles[cur][i] = atan2(sin(dd), cos(dd));
 +                }
 +            }
 +            else
 +            {
 +                if (teller > 1)
 +                {
 +                    for (i = 0; (i < nangles); i++)
 +                    {
 +                        while (angles[cur][i] <= angles[prev][i] - M_PI)
 +                        {
 +                            angles[cur][i] += 2*M_PI;
 +                        }
 +                        while (angles[cur][i] > angles[prev][i] + M_PI)
 +                        {
 +                            angles[cur][i] -= 2*M_PI;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        /* Average angles */
 +        aa = 0;
 +        for (i = 0; (i < nangles); i++)
 +        {
 +            aa = aa+angles[cur][i];
 +
 +            /* angle in rad / 2Pi * max determines bin. bins go from 0 to maxangstat,
 +               even though scale goes from -pi to pi (dihedral) or -pi/2 to pi/2
 +               (angle) Basically: translate the x-axis by Pi. Translate it back by
 +               -Pi when plotting.
 +             */
 +
 +            angle = angles[cur][i];
 +            if (!bAngles)
 +            {
 +                while (angle < -M_PI)
 +                {
 +                    angle += 2*M_PI;
 +                }
 +                while (angle >= M_PI)
 +                {
 +                    angle -= 2*M_PI;
 +                }
 +
 +                angle += M_PI;
 +            }
 +
 +            /* Update the distribution histogram */
 +            angind = (int) ((angle*maxangstat)/pifac + 0.5);
 +            if (angind == maxangstat)
 +            {
 +                angind = 0;
 +            }
 +            if ( (angind < 0) || (angind >= maxangstat) )
 +            {
 +                /* this will never happen */
 +                gmx_fatal(FARGS, "angle (%f) index out of range (0..%d) : %d\n",
 +                          angle, maxangstat, angind);
 +            }
 +
 +            angstat[angind]++;
 +            if (angind == maxangstat)
 +            {
 +                fprintf(stderr, "angle %d fr %d = %g\n", i, cur, angle);
 +            }
 +
 +            total++;
 +        }
 +
 +        /* average over all angles */
 +        (*aver_angle)[teller] = (aa/nangles);
 +
 +        /* this copies all current dih. angles to dih[i], teller is frame */
 +        if (bSaveAll)
 +        {
 +            for (i = 0; i < nangles; i++)
 +            {
 +                dih[i][teller] = angles[cur][i];
 +            }
 +        }
 +
 +        /* Swap buffers */
 +        cur = prev;
 +
 +        /* Increment loop counter */
 +        teller++;
 +    }
 +    while (read_next_x(oenv, status, &t, x, box));
 +    close_trj(status);
 +
 +    sfree(x);
 +    sfree(angles[cur]);
 +    sfree(angles[prev]);
 +
 +    *nframes = teller;
 +}
index 21d4f836ceb3fa2930b5113986c01454450ffcd7,0000000000000000000000000000000000000000..f0d58de9f9c3dd920a79749be51d0f66fc280334
mode 100644,000000..100644
--- /dev/null
@@@ -1,1484 -1,0 +1,1480 @@@
-                     if (output_env_get_print_xvgr_codes(oenv))
-                     {
-                         fprintf(out, "&\n");
-                     }
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +#include <math.h>
 +#include <string.h>
 +#include "gromacs/commandline/pargs.h"
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "gmx_fatal.h"
 +#include "vec.h"
 +#include "pbc.h"
 +#include "gromacs/fileio/futil.h"
 +#include "index.h"
 +#include "gromacs/fileio/pdbio.h"
 +#include "gromacs/fileio/confio.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "gromacs/fileio/matio.h"
 +#include "mshift.h"
 +#include "xvgr.h"
 +#include "rmpbc.h"
 +#include "txtdump.h"
 +#include "eigio.h"
 +#include "physics.h"
 +#include "gmx_ana.h"
 +
 +#include "gromacs/math/do_fit.h"
 +
 +static void calc_entropy_qh(FILE *fp, int n, real eigval[], real temp, int nskip)
 +{
 +    int    i;
 +    double hwkT, w, dS, S = 0;
 +    double hbar, lambda;
 +
 +    hbar = PLANCK1/(2*M_PI);
 +    for (i = 0; (i < n-nskip); i++)
 +    {
 +        if (eigval[i] > 0)
 +        {
 +            lambda = eigval[i]*AMU;
 +            w      = sqrt(BOLTZMANN*temp/lambda)/NANO;
 +            hwkT   = (hbar*w)/(BOLTZMANN*temp);
 +            dS     = (hwkT/(exp(hwkT) - 1) - log(1-exp(-hwkT)));
 +            S     += dS;
 +            if (debug)
 +            {
 +                fprintf(debug, "i = %5d w = %10g lam = %10g hwkT = %10g dS = %10g\n",
 +                        i, w, lambda, hwkT, dS);
 +            }
 +        }
 +        else
 +        {
 +            fprintf(stderr, "eigval[%d] = %g\n", i, eigval[i]);
 +            w = 0;
 +        }
 +    }
 +    fprintf(fp, "The Entropy due to the Quasi Harmonic approximation is %g J/mol K\n",
 +            S*RGAS);
 +}
 +
 +static void calc_entropy_schlitter(FILE *fp, int n, int nskip,
 +                                   real *eigval, real temp)
 +{
 +    double  dd, deter;
 +    int    *indx;
 +    int     i, j, k, m;
 +    char    buf[256];
 +    double  hbar, kt, kteh, S;
 +
 +    hbar = PLANCK1/(2*M_PI);
 +    kt   = BOLTZMANN*temp;
 +    kteh = kt*exp(2.0)/(hbar*hbar)*AMU*(NANO*NANO);
 +    if (debug)
 +    {
 +        fprintf(debug, "n = %d, nskip = %d kteh = %g\n", n, nskip, kteh);
 +    }
 +
 +    deter = 0;
 +    for (i = 0; (i < n-nskip); i++)
 +    {
 +        dd     = 1+kteh*eigval[i];
 +        deter += log(dd);
 +    }
 +    S = 0.5*RGAS*deter;
 +
 +    fprintf(fp, "The Entropy due to the Schlitter formula is %g J/mol K\n", S);
 +}
 +
 +const char *proj_unit;
 +
 +static real tick_spacing(real range, int minticks)
 +{
 +    real sp;
 +
 +    if (range <= 0)
 +    {
 +        return 1.0;
 +    }
 +
 +    sp = 0.2*exp(log(10)*ceil(log(range)/log(10)));
 +    while (range/sp < minticks-1)
 +    {
 +        sp = sp/2;
 +    }
 +
 +    return sp;
 +}
 +
 +static void write_xvgr_graphs(const char *file, int ngraphs, int nsetspergraph,
 +                              const char *title, const char *subtitle,
 +                              const char *xlabel, const char **ylabel,
 +                              int n, real *x, real **y, real ***sy,
 +                              real scale_x, gmx_bool bZero, gmx_bool bSplit,
 +                              const output_env_t oenv)
 +{
 +    FILE *out;
 +    int   g, s, i;
 +    real  min, max, xsp, ysp;
 +
 +    out = gmx_ffopen(file, "w");
 +    if (output_env_get_xvg_format(oenv) == exvgXMGRACE)
 +    {
 +        fprintf(out, "@ autoscale onread none\n");
 +    }
 +    for (g = 0; g < ngraphs; g++)
 +    {
 +        if (y)
 +        {
 +            min = y[g][0];
 +            max = y[g][0];
 +            for (i = 0; i < n; i++)
 +            {
 +                if (y[g][i] < min)
 +                {
 +                    min = y[g][i];
 +                }
 +                if (y[g][i] > max)
 +                {
 +                    max = y[g][i];
 +                }
 +            }
 +        }
 +        else
 +        {
 +            min = sy[g][0][0];
 +            max = sy[g][0][0];
 +            for (s = 0; s < nsetspergraph; s++)
 +            {
 +                for (i = 0; i < n; i++)
 +                {
 +                    if (sy[g][s][i] < min)
 +                    {
 +                        min = sy[g][s][i];
 +                    }
 +                    if (sy[g][s][i] > max)
 +                    {
 +                        max = sy[g][s][i];
 +                    }
 +                }
 +            }
 +        }
 +        if (bZero)
 +        {
 +            min = 0;
 +        }
 +        else
 +        {
 +            min = min-0.1*(max-min);
 +        }
 +        max = max+0.1*(max-min);
 +        xsp = tick_spacing((x[n-1]-x[0])*scale_x, 4);
 +        ysp = tick_spacing(max-min, 3);
 +        if (output_env_get_print_xvgr_codes(oenv))
 +        {
 +            fprintf(out, "@ with g%d\n@ g%d on\n", g, g);
 +            if (g == 0)
 +            {
 +                fprintf(out, "@ title \"%s\"\n", title);
 +                if (subtitle)
 +                {
 +                    fprintf(out, "@ subtitle \"%s\"\n", subtitle);
 +                }
 +            }
 +            if (g == ngraphs-1)
 +            {
 +                fprintf(out, "@ xaxis  label \"%s\"\n", xlabel);
 +            }
 +            else
 +            {
 +                fprintf(out, "@ xaxis  ticklabel off\n");
 +            }
 +            if (n > 1)
 +            {
 +                fprintf(out, "@ world xmin %g\n", x[0]*scale_x);
 +                fprintf(out, "@ world xmax %g\n", x[n-1]*scale_x);
 +                fprintf(out, "@ world ymin %g\n", min);
 +                fprintf(out, "@ world ymax %g\n", max);
 +            }
 +            fprintf(out, "@ view xmin 0.15\n");
 +            fprintf(out, "@ view xmax 0.85\n");
 +            fprintf(out, "@ view ymin %g\n", 0.15+(ngraphs-1-g)*0.7/ngraphs);
 +            fprintf(out, "@ view ymax %g\n", 0.15+(ngraphs-g)*0.7/ngraphs);
 +            fprintf(out, "@ yaxis  label \"%s\"\n", ylabel[g]);
 +            fprintf(out, "@ xaxis tick major %g\n", xsp);
 +            fprintf(out, "@ xaxis tick minor %g\n", xsp/2);
 +            fprintf(out, "@ xaxis ticklabel start type spec\n");
 +            fprintf(out, "@ xaxis ticklabel start %g\n", ceil(min/xsp)*xsp);
 +            fprintf(out, "@ yaxis tick major %g\n", ysp);
 +            fprintf(out, "@ yaxis tick minor %g\n", ysp/2);
 +            fprintf(out, "@ yaxis ticklabel start type spec\n");
 +            fprintf(out, "@ yaxis ticklabel start %g\n", ceil(min/ysp)*ysp);
 +            if ((min < 0) && (max > 0))
 +            {
 +                fprintf(out, "@ zeroxaxis bar on\n");
 +                fprintf(out, "@ zeroxaxis bar linestyle 3\n");
 +            }
 +        }
 +        for (s = 0; s < nsetspergraph; s++)
 +        {
 +            for (i = 0; i < n; i++)
 +            {
 +                if (bSplit && i > 0 && fabs(x[i]) < 1e-5)
 +                {
-             if (output_env_get_print_xvgr_codes(oenv))
-             {
-                 fprintf(out, "&\n");
-             }
++                    fprintf(out, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +                }
 +                fprintf(out, "%10.4f %10.5f\n",
 +                        x[i]*scale_x, y ? y[g][i] : sy[g][s][i]);
 +            }
-     fprintf(out, "@ subtitle \"using %d eigenvectors of trajectory 1\"\n",
-             noutvec);
++            fprintf(out, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +        }
 +    }
 +    gmx_ffclose(out);
 +}
 +
 +static void
 +compare(int natoms, int n1, rvec **eigvec1, int n2, rvec **eigvec2,
 +        real *eigval1, int neig1, real *eigval2, int neig2)
 +{
 +    int    n, nrow;
 +    int    i, j, k;
 +    double sum1, sum2, trace1, trace2, sab, samsb2, tmp, ip;
 +
 +    n = min(n1, n2);
 +
 +    n = min(n, min(neig1, neig2));
 +    fprintf(stdout, "Will compare the covariance matrices using %d dimensions\n", n);
 +
 +    sum1 = 0;
 +    for (i = 0; i < n; i++)
 +    {
 +        if (eigval1[i] < 0)
 +        {
 +            eigval1[i] = 0;
 +        }
 +        sum1      += eigval1[i];
 +        eigval1[i] = sqrt(eigval1[i]);
 +    }
 +    trace1 = sum1;
 +    for (i = n; i < neig1; i++)
 +    {
 +        trace1 += eigval1[i];
 +    }
 +    sum2 = 0;
 +    for (i = 0; i < n; i++)
 +    {
 +        if (eigval2[i] < 0)
 +        {
 +            eigval2[i] = 0;
 +        }
 +        sum2      += eigval2[i];
 +        eigval2[i] = sqrt(eigval2[i]);
 +    }
 +    trace2 = sum2;
 +    for (i = n; i < neig2; i++)
 +    {
 +        trace2 += eigval2[i];
 +    }
 +
 +    fprintf(stdout, "Trace of the two matrices: %g and %g\n", sum1, sum2);
 +    if (neig1 != n || neig2 != n)
 +    {
 +        fprintf(stdout, "this is %d%% and %d%% of the total trace\n",
 +                (int)(100*sum1/trace1+0.5), (int)(100*sum2/trace2+0.5));
 +    }
 +    fprintf(stdout, "Square root of the traces: %g and %g\n",
 +            sqrt(sum1), sqrt(sum2));
 +
 +    sab = 0;
 +    for (i = 0; i < n; i++)
 +    {
 +        tmp = 0;
 +        for (j = 0; j < n; j++)
 +        {
 +            ip = 0;
 +            for (k = 0; k < natoms; k++)
 +            {
 +                ip += iprod(eigvec1[i][k], eigvec2[j][k]);
 +            }
 +            tmp += eigval2[j]*ip*ip;
 +        }
 +        sab += eigval1[i]*tmp;
 +    }
 +
 +    samsb2 = sum1+sum2-2*sab;
 +    if (samsb2 < 0)
 +    {
 +        samsb2 = 0;
 +    }
 +
 +    fprintf(stdout, "The overlap of the covariance matrices:\n");
 +    fprintf(stdout, "  normalized:  %.3f\n", 1-sqrt(samsb2/(sum1+sum2)));
 +    tmp = 1-sab/sqrt(sum1*sum2);
 +    if (tmp < 0)
 +    {
 +        tmp = 0;
 +    }
 +    fprintf(stdout, "       shape:  %.3f\n", 1-sqrt(tmp));
 +}
 +
 +
 +static void inprod_matrix(const char *matfile, int natoms,
 +                          int nvec1, int *eignr1, rvec **eigvec1,
 +                          int nvec2, int *eignr2, rvec **eigvec2,
 +                          gmx_bool bSelect, int noutvec, int *outvec)
 +{
 +    FILE   *out;
 +    real  **mat;
 +    int     i, x1, y1, x, y, nlevels;
 +    int     nx, ny;
 +    real    inp, *t_x, *t_y, max;
 +    t_rgb   rlo, rhi;
 +
 +    snew(t_y, nvec2);
 +    if (bSelect)
 +    {
 +        nx = noutvec;
 +        ny = 0;
 +        for (y1 = 0; y1 < nx; y1++)
 +        {
 +            if (outvec[y1] < nvec2)
 +            {
 +                t_y[ny] = eignr2[outvec[y1]]+1;
 +                ny++;
 +            }
 +        }
 +    }
 +    else
 +    {
 +        nx = nvec1;
 +        ny = nvec2;
 +        for (y = 0; y < ny; y++)
 +        {
 +            t_y[y] = eignr2[y]+1;
 +        }
 +    }
 +
 +    fprintf(stderr, "Calculating inner-product matrix of %dx%d eigenvectors\n",
 +            nx, nvec2);
 +
 +    snew(mat, nx);
 +    snew(t_x, nx);
 +    max = 0;
 +    for (x1 = 0; x1 < nx; x1++)
 +    {
 +        snew(mat[x1], ny);
 +        if (bSelect)
 +        {
 +            x = outvec[x1];
 +        }
 +        else
 +        {
 +            x = x1;
 +        }
 +        t_x[x1] = eignr1[x]+1;
 +        fprintf(stderr, " %d", eignr1[x]+1);
 +        for (y1 = 0; y1 < ny; y1++)
 +        {
 +            inp = 0;
 +            if (bSelect)
 +            {
 +                while (outvec[y1] >= nvec2)
 +                {
 +                    y1++;
 +                }
 +                y = outvec[y1];
 +            }
 +            else
 +            {
 +                y = y1;
 +            }
 +            for (i = 0; i < natoms; i++)
 +            {
 +                inp += iprod(eigvec1[x][i], eigvec2[y][i]);
 +            }
 +            mat[x1][y1] = fabs(inp);
 +            if (mat[x1][y1] > max)
 +            {
 +                max = mat[x1][y1];
 +            }
 +        }
 +    }
 +    fprintf(stderr, "\n");
 +    rlo.r   = 1; rlo.g = 1; rlo.b = 1;
 +    rhi.r   = 0; rhi.g = 0; rhi.b = 0;
 +    nlevels = 41;
 +    out     = gmx_ffopen(matfile, "w");
 +    write_xpm(out, 0, "Eigenvector inner-products", "in.prod.", "run 1", "run 2",
 +              nx, ny, t_x, t_y, mat, 0.0, max, rlo, rhi, &nlevels);
 +    gmx_ffclose(out);
 +}
 +
 +static void overlap(const char *outfile, int natoms,
 +                    rvec **eigvec1,
 +                    int nvec2, int *eignr2, rvec **eigvec2,
 +                    int noutvec, int *outvec,
 +                    const output_env_t oenv)
 +{
 +    FILE *out;
 +    int   i, v, vec, x;
 +    real  overlap, inp;
 +
 +    fprintf(stderr, "Calculating overlap between eigenvectors of set 2 with eigenvectors\n");
 +    for (i = 0; i < noutvec; i++)
 +    {
 +        fprintf(stderr, "%d ", outvec[i]+1);
 +    }
 +    fprintf(stderr, "\n");
 +
 +    out = xvgropen(outfile, "Subspace overlap",
 +                   "Eigenvectors of trajectory 2", "Overlap", oenv);
-                 fprintf(xvgrout, "&\n");
++    if (output_env_get_print_xvgr_codes(oenv))
++    {
++        fprintf(out, "@ subtitle \"using %d eigenvectors of trajectory 1\"\n", noutvec);
++    }
 +    overlap = 0;
 +    for (x = 0; x < nvec2; x++)
 +    {
 +        for (v = 0; v < noutvec; v++)
 +        {
 +            vec = outvec[v];
 +            inp = 0;
 +            for (i = 0; i < natoms; i++)
 +            {
 +                inp += iprod(eigvec1[vec][i], eigvec2[x][i]);
 +            }
 +            overlap += sqr(inp);
 +        }
 +        fprintf(out, "%5d  %5.3f\n", eignr2[x]+1, overlap/noutvec);
 +    }
 +
 +    gmx_ffclose(out);
 +}
 +
 +static void project(const char *trajfile, t_topology *top, int ePBC, matrix topbox,
 +                    const char *projfile, const char *twodplotfile,
 +                    const char *threedplotfile, const char *filterfile, int skip,
 +                    const char *extremefile, gmx_bool bExtrAll, real extreme,
 +                    int nextr, t_atoms *atoms, int natoms, atom_id *index,
 +                    gmx_bool bFit, rvec *xref, int nfit, atom_id *ifit, real *w_rls,
 +                    real *sqrtm, rvec *xav,
 +                    int *eignr, rvec **eigvec,
 +                    int noutvec, int *outvec, gmx_bool bSplit,
 +                    const output_env_t oenv)
 +{
 +    FILE        *xvgrout = NULL;
 +    int          nat, i, j, d, v, vec, nfr, nframes = 0, snew_size, frame;
 +    t_trxstatus *out = NULL;
 +    t_trxstatus *status;
 +    int          noutvec_extr, imin, imax;
 +    real        *pmin, *pmax;
 +    atom_id     *all_at;
 +    matrix       box;
 +    rvec        *xread, *x;
 +    real         t, inp, **inprod = NULL, min = 0, max = 0;
 +    char         str[STRLEN], str2[STRLEN], **ylabel, *c;
 +    real         fact;
 +    gmx_rmpbc_t  gpbc = NULL;
 +
 +    snew(x, natoms);
 +
 +    if (bExtrAll)
 +    {
 +        noutvec_extr = noutvec;
 +    }
 +    else
 +    {
 +        noutvec_extr = 1;
 +    }
 +
 +
 +    if (trajfile)
 +    {
 +        snew(inprod, noutvec+1);
 +
 +        if (filterfile)
 +        {
 +            fprintf(stderr, "Writing a filtered trajectory to %s using eigenvectors\n",
 +                    filterfile);
 +            for (i = 0; i < noutvec; i++)
 +            {
 +                fprintf(stderr, "%d ", outvec[i]+1);
 +            }
 +            fprintf(stderr, "\n");
 +            out = open_trx(filterfile, "w");
 +        }
 +        snew_size = 0;
 +        nfr       = 0;
 +        nframes   = 0;
 +        nat       = read_first_x(oenv, &status, trajfile, &t, &xread, box);
 +        if (nat > atoms->nr)
 +        {
 +            gmx_fatal(FARGS, "the number of atoms in your trajectory (%d) is larger than the number of atoms in your structure file (%d)", nat, atoms->nr);
 +        }
 +        snew(all_at, nat);
 +
 +        if (top)
 +        {
 +            gpbc = gmx_rmpbc_init(&top->idef, ePBC, nat);
 +        }
 +
 +        for (i = 0; i < nat; i++)
 +        {
 +            all_at[i] = i;
 +        }
 +        do
 +        {
 +            if (nfr % skip == 0)
 +            {
 +                if (top)
 +                {
 +                    gmx_rmpbc(gpbc, nat, box, xread);
 +                }
 +                if (nframes >= snew_size)
 +                {
 +                    snew_size += 100;
 +                    for (i = 0; i < noutvec+1; i++)
 +                    {
 +                        srenew(inprod[i], snew_size);
 +                    }
 +                }
 +                inprod[noutvec][nframes] = t;
 +                /* calculate x: a fitted struture of the selected atoms */
 +                if (bFit)
 +                {
 +                    reset_x(nfit, ifit, nat, NULL, xread, w_rls);
 +                    do_fit(nat, w_rls, xref, xread);
 +                }
 +                for (i = 0; i < natoms; i++)
 +                {
 +                    copy_rvec(xread[index[i]], x[i]);
 +                }
 +
 +                for (v = 0; v < noutvec; v++)
 +                {
 +                    vec = outvec[v];
 +                    /* calculate (mass-weighted) projection */
 +                    inp = 0;
 +                    for (i = 0; i < natoms; i++)
 +                    {
 +                        inp += (eigvec[vec][i][0]*(x[i][0]-xav[i][0])+
 +                                eigvec[vec][i][1]*(x[i][1]-xav[i][1])+
 +                                eigvec[vec][i][2]*(x[i][2]-xav[i][2]))*sqrtm[i];
 +                    }
 +                    inprod[v][nframes] = inp;
 +                }
 +                if (filterfile)
 +                {
 +                    for (i = 0; i < natoms; i++)
 +                    {
 +                        for (d = 0; d < DIM; d++)
 +                        {
 +                            /* misuse xread for output */
 +                            xread[index[i]][d] = xav[i][d];
 +                            for (v = 0; v < noutvec; v++)
 +                            {
 +                                xread[index[i]][d] +=
 +                                    inprod[v][nframes]*eigvec[outvec[v]][i][d]/sqrtm[i];
 +                            }
 +                        }
 +                    }
 +                    write_trx(out, natoms, index, atoms, 0, t, box, xread, NULL, NULL);
 +                }
 +                nframes++;
 +            }
 +            nfr++;
 +        }
 +        while (read_next_x(oenv, status, &t, xread, box));
 +        close_trx(status);
 +        sfree(x);
 +        if (filterfile)
 +        {
 +            close_trx(out);
 +        }
 +    }
 +    else
 +    {
 +        snew(xread, atoms->nr);
 +    }
 +
 +    if (top)
 +    {
 +        gmx_rmpbc_done(gpbc);
 +    }
 +
 +
 +    if (projfile)
 +    {
 +        snew(ylabel, noutvec);
 +        for (v = 0; v < noutvec; v++)
 +        {
 +            sprintf(str, "vec %d", eignr[outvec[v]]+1);
 +            ylabel[v] = strdup(str);
 +        }
 +        sprintf(str, "projection on eigenvectors (%s)", proj_unit);
 +        write_xvgr_graphs(projfile, noutvec, 1, str, NULL, output_env_get_xvgr_tlabel(oenv),
 +                          (const char **)ylabel,
 +                          nframes, inprod[noutvec], inprod, NULL,
 +                          output_env_get_time_factor(oenv), FALSE, bSplit, oenv);
 +    }
 +
 +    if (twodplotfile)
 +    {
 +        sprintf(str, "projection on eigenvector %d (%s)",
 +                eignr[outvec[0]]+1, proj_unit);
 +        sprintf(str2, "projection on eigenvector %d (%s)",
 +                eignr[outvec[noutvec-1]]+1, proj_unit);
 +        xvgrout = xvgropen(twodplotfile, "2D projection of trajectory", str, str2,
 +                           oenv);
 +        for (i = 0; i < nframes; i++)
 +        {
 +            if (bSplit && i > 0 && fabs(inprod[noutvec][i]) < 1e-5)
 +            {
++                fprintf(xvgrout, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +            }
 +            fprintf(xvgrout, "%10.5f %10.5f\n", inprod[0][i], inprod[noutvec-1][i]);
 +        }
 +        gmx_ffclose(xvgrout);
 +    }
 +
 +    if (threedplotfile)
 +    {
 +        t_atoms     atoms;
 +        rvec       *x;
 +        real       *b = NULL;
 +        matrix      box;
 +        char       *resnm, *atnm;
 +        gmx_bool    bPDB, b4D;
 +        FILE       *out;
 +
 +        if (noutvec < 3)
 +        {
 +            gmx_fatal(FARGS, "You have selected less than 3 eigenvectors");
 +        }
 +
 +        /* initialize */
 +        bPDB = fn2ftp(threedplotfile) == efPDB;
 +        clear_mat(box);
 +        box[XX][XX] = box[YY][YY] = box[ZZ][ZZ] = 1;
 +
 +        b4D = bPDB && (noutvec >= 4);
 +        if (b4D)
 +        {
 +            fprintf(stderr, "You have selected four or more eigenvectors:\n"
 +                    "fourth eigenvector will be plotted "
 +                    "in bfactor field of pdb file\n");
 +            sprintf(str, "4D proj. of traj. on eigenv. %d, %d, %d and %d",
 +                    eignr[outvec[0]]+1, eignr[outvec[1]]+1,
 +                    eignr[outvec[2]]+1, eignr[outvec[3]]+1);
 +        }
 +        else
 +        {
 +            sprintf(str, "3D proj. of traj. on eigenv. %d, %d and %d",
 +                    eignr[outvec[0]]+1, eignr[outvec[1]]+1, eignr[outvec[2]]+1);
 +        }
 +        init_t_atoms(&atoms, nframes, FALSE);
 +        snew(x, nframes);
 +        snew(b, nframes);
 +        atnm  = strdup("C");
 +        resnm = strdup("PRJ");
 +
 +        if (nframes > 10000)
 +        {
 +            fact = 10000.0/nframes;
 +        }
 +        else
 +        {
 +            fact = 1.0;
 +        }
 +
 +        for (i = 0; i < nframes; i++)
 +        {
 +            atoms.atomname[i]     = &atnm;
 +            atoms.atom[i].resind  = i;
 +            atoms.resinfo[i].name = &resnm;
 +            atoms.resinfo[i].nr   = ceil(i*fact);
 +            atoms.resinfo[i].ic   = ' ';
 +            x[i][XX]              = inprod[0][i];
 +            x[i][YY]              = inprod[1][i];
 +            x[i][ZZ]              = inprod[2][i];
 +            if (b4D)
 +            {
 +                b[i]  = inprod[3][i];
 +            }
 +        }
 +        if ( ( b4D || bSplit ) && bPDB)
 +        {
 +            out = gmx_ffopen(threedplotfile, "w");
 +            fprintf(out, "HEADER    %s\n", str);
 +            if (b4D)
 +            {
 +                fprintf(out, "REMARK    %s\n", "fourth dimension plotted as B-factor");
 +            }
 +            j = 0;
 +            for (i = 0; i < atoms.nr; i++)
 +            {
 +                if (j > 0 && bSplit && fabs(inprod[noutvec][i]) < 1e-5)
 +                {
 +                    fprintf(out, "TER\n");
 +                    j = 0;
 +                }
 +                gmx_fprintf_pdb_atomline(out, epdbATOM, i+1, "C", ' ', "PRJ", ' ', j+1, ' ',
 +                                         10*x[i][XX], 10*x[i][YY], 10*x[i][ZZ], 1.0, 10*b[i], "");
 +                if (j > 0)
 +                {
 +                    fprintf(out, "CONECT%5d%5d\n", i, i+1);
 +                }
 +                j++;
 +            }
 +            fprintf(out, "TER\n");
 +            gmx_ffclose(out);
 +        }
 +        else
 +        {
 +            write_sto_conf(threedplotfile, str, &atoms, x, NULL, ePBC, box);
 +        }
 +        free_t_atoms(&atoms, FALSE);
 +    }
 +
 +    if (extremefile)
 +    {
 +        snew(pmin, noutvec_extr);
 +        snew(pmax, noutvec_extr);
 +        if (extreme == 0)
 +        {
 +            fprintf(stderr, "%11s %17s %17s\n", "eigenvector", "Minimum", "Maximum");
 +            fprintf(stderr,
 +                    "%11s %10s %10s %10s %10s\n", "", "value", "frame", "value", "frame");
 +            imin = 0;
 +            imax = 0;
 +            for (v = 0; v < noutvec_extr; v++)
 +            {
 +                for (i = 0; i < nframes; i++)
 +                {
 +                    if (inprod[v][i] < inprod[v][imin])
 +                    {
 +                        imin = i;
 +                    }
 +                    if (inprod[v][i] > inprod[v][imax])
 +                    {
 +                        imax = i;
 +                    }
 +                }
 +                pmin[v] = inprod[v][imin];
 +                pmax[v] = inprod[v][imax];
 +                fprintf(stderr, "%7d     %10.6f %10d %10.6f %10d\n",
 +                        eignr[outvec[v]]+1,
 +                        pmin[v], imin, pmax[v], imax);
 +            }
 +        }
 +        else
 +        {
 +            pmin[0] = -extreme;
 +            pmax[0] =  extreme;
 +        }
 +        /* build format string for filename: */
 +        strcpy(str, extremefile);  /* copy filename */
 +        c = strrchr(str, '.');     /* find where extention begins */
 +        strcpy(str2, c);           /* get extention */
 +        sprintf(c, "%%d%s", str2); /* append '%s' and extention to filename */
 +        for (v = 0; v < noutvec_extr; v++)
 +        {
 +            /* make filename using format string */
 +            if (noutvec_extr == 1)
 +            {
 +                strcpy(str2, extremefile);
 +            }
 +            else
 +            {
 +                sprintf(str2, str, eignr[outvec[v]]+1);
 +            }
 +            fprintf(stderr, "Writing %d frames along eigenvector %d to %s\n",
 +                    nextr, outvec[v]+1, str2);
 +            out = open_trx(str2, "w");
 +            for (frame = 0; frame < nextr; frame++)
 +            {
 +                if ((extreme == 0) && (nextr <= 3))
 +                {
 +                    for (i = 0; i < natoms; i++)
 +                    {
 +                        atoms->resinfo[atoms->atom[index[i]].resind].chainid = 'A' + frame;
 +                    }
 +                }
 +                for (i = 0; i < natoms; i++)
 +                {
 +                    for (d = 0; d < DIM; d++)
 +                    {
 +                        xread[index[i]][d] =
 +                            (xav[i][d] + (pmin[v]*(nextr-frame-1)+pmax[v]*frame)/(nextr-1)
 +                             *eigvec[outvec[v]][i][d]/sqrtm[i]);
 +                    }
 +                }
 +                write_trx(out, natoms, index, atoms, 0, frame, topbox, xread, NULL, NULL);
 +            }
 +            close_trx(out);
 +        }
 +        sfree(pmin);
 +        sfree(pmax);
 +    }
 +    fprintf(stderr, "\n");
 +}
 +
 +static void components(const char *outfile, int natoms,
 +                       int *eignr, rvec **eigvec,
 +                       int noutvec, int *outvec,
 +                       const output_env_t oenv)
 +{
 +    int   g, s, v, i;
 +    real *x, ***y;
 +    char  str[STRLEN], **ylabel;
 +
 +    fprintf(stderr, "Writing eigenvector components to %s\n", outfile);
 +
 +    snew(ylabel, noutvec);
 +    snew(y, noutvec);
 +    snew(x, natoms);
 +    for (i = 0; i < natoms; i++)
 +    {
 +        x[i] = i+1;
 +    }
 +    for (g = 0; g < noutvec; g++)
 +    {
 +        v = outvec[g];
 +        sprintf(str, "vec %d", eignr[v]+1);
 +        ylabel[g] = strdup(str);
 +        snew(y[g], 4);
 +        for (s = 0; s < 4; s++)
 +        {
 +            snew(y[g][s], natoms);
 +        }
 +        for (i = 0; i < natoms; i++)
 +        {
 +            y[g][0][i] = norm(eigvec[v][i]);
 +            for (s = 0; s < 3; s++)
 +            {
 +                y[g][s+1][i] = eigvec[v][i][s];
 +            }
 +        }
 +    }
 +    write_xvgr_graphs(outfile, noutvec, 4, "Eigenvector components",
 +                      "black: total, red: x, green: y, blue: z",
 +                      "Atom number", (const char **)ylabel,
 +                      natoms, x, NULL, y, 1, FALSE, FALSE, oenv);
 +    fprintf(stderr, "\n");
 +}
 +
 +static void rmsf(const char *outfile, int natoms, real *sqrtm,
 +                 int *eignr, rvec **eigvec,
 +                 int noutvec, int *outvec,
 +                 real *eigval, int neig,
 +                 const output_env_t oenv)
 +{
 +    int    nrow, g, v, i;
 +    real  *x, **y;
 +    char   str[STRLEN], **ylabel;
 +
 +    for (i = 0; i < neig; i++)
 +    {
 +        if (eigval[i] < 0)
 +        {
 +            eigval[i] = 0;
 +        }
 +    }
 +
 +    fprintf(stderr, "Writing rmsf to %s\n", outfile);
 +
 +    snew(ylabel, noutvec);
 +    snew(y, noutvec);
 +    snew(x, natoms);
 +    for (i = 0; i < natoms; i++)
 +    {
 +        x[i] = i+1;
 +    }
 +    for (g = 0; g < noutvec; g++)
 +    {
 +        v = outvec[g];
 +        if (eignr[v] >= neig)
 +        {
 +            gmx_fatal(FARGS, "Selected vector %d is larger than the number of eigenvalues (%d)", eignr[v]+1, neig);
 +        }
 +        sprintf(str, "vec %d", eignr[v]+1);
 +        ylabel[g] = strdup(str);
 +        snew(y[g], natoms);
 +        for (i = 0; i < natoms; i++)
 +        {
 +            y[g][i] = sqrt(eigval[eignr[v]]*norm2(eigvec[v][i]))/sqrtm[i];
 +        }
 +    }
 +    write_xvgr_graphs(outfile, noutvec, 1, "RMS fluctuation (nm) ", NULL,
 +                      "Atom number", (const char **)ylabel,
 +                      natoms, x, y, NULL, 1, TRUE, FALSE, oenv);
 +    fprintf(stderr, "\n");
 +}
 +
 +int gmx_anaeig(int argc, char *argv[])
 +{
 +    static const char *desc[] = {
 +        "[THISMODULE] analyzes eigenvectors. The eigenvectors can be of a",
 +        "covariance matrix ([gmx-covar]) or of a Normal Modes analysis",
 +        "([gmx-nmeig]).[PAR]",
 +
 +        "When a trajectory is projected on eigenvectors, all structures are",
 +        "fitted to the structure in the eigenvector file, if present, otherwise",
 +        "to the structure in the structure file. When no run input file is",
 +        "supplied, periodicity will not be taken into account. Most analyses",
 +        "are performed on eigenvectors [TT]-first[tt] to [TT]-last[tt], but when",
 +        "[TT]-first[tt] is set to -1 you will be prompted for a selection.[PAR]",
 +
 +        "[TT]-comp[tt]: plot the vector components per atom of eigenvectors",
 +        "[TT]-first[tt] to [TT]-last[tt].[PAR]",
 +
 +        "[TT]-rmsf[tt]: plot the RMS fluctuation per atom of eigenvectors",
 +        "[TT]-first[tt] to [TT]-last[tt] (requires [TT]-eig[tt]).[PAR]",
 +
 +        "[TT]-proj[tt]: calculate projections of a trajectory on eigenvectors",
 +        "[TT]-first[tt] to [TT]-last[tt].",
 +        "The projections of a trajectory on the eigenvectors of its",
 +        "covariance matrix are called principal components (pc's).",
 +        "It is often useful to check the cosine content of the pc's,",
 +        "since the pc's of random diffusion are cosines with the number",
 +        "of periods equal to half the pc index.",
 +        "The cosine content of the pc's can be calculated with the program",
 +        "[gmx-analyze].[PAR]",
 +
 +        "[TT]-2d[tt]: calculate a 2d projection of a trajectory on eigenvectors",
 +        "[TT]-first[tt] and [TT]-last[tt].[PAR]",
 +
 +        "[TT]-3d[tt]: calculate a 3d projection of a trajectory on the first",
 +        "three selected eigenvectors.[PAR]",
 +
 +        "[TT]-filt[tt]: filter the trajectory to show only the motion along",
 +        "eigenvectors [TT]-first[tt] to [TT]-last[tt].[PAR]",
 +
 +        "[TT]-extr[tt]: calculate the two extreme projections along a trajectory",
 +        "on the average structure and interpolate [TT]-nframes[tt] frames",
 +        "between them, or set your own extremes with [TT]-max[tt]. The",
 +        "eigenvector [TT]-first[tt] will be written unless [TT]-first[tt] and",
 +        "[TT]-last[tt] have been set explicitly, in which case all eigenvectors",
 +        "will be written to separate files. Chain identifiers will be added",
 +        "when writing a [TT].pdb[tt] file with two or three structures (you",
 +        "can use [TT]rasmol -nmrpdb[tt] to view such a [TT].pdb[tt] file).[PAR]",
 +
 +        "  Overlap calculations between covariance analysis:[BR]",
 +        "  [BB]Note:[bb] the analysis should use the same fitting structure[PAR]",
 +
 +        "[TT]-over[tt]: calculate the subspace overlap of the eigenvectors in",
 +        "file [TT]-v2[tt] with eigenvectors [TT]-first[tt] to [TT]-last[tt]",
 +        "in file [TT]-v[tt].[PAR]",
 +
 +        "[TT]-inpr[tt]: calculate a matrix of inner-products between",
 +        "eigenvectors in files [TT]-v[tt] and [TT]-v2[tt]. All eigenvectors",
 +        "of both files will be used unless [TT]-first[tt] and [TT]-last[tt]",
 +        "have been set explicitly.[PAR]",
 +
 +        "When [TT]-v[tt], [TT]-eig[tt], [TT]-v2[tt] and [TT]-eig2[tt] are given,",
 +        "a single number for the overlap between the covariance matrices is",
 +        "generated. The formulas are:[BR]",
 +        "        difference = sqrt(tr((sqrt(M1) - sqrt(M2))^2))[BR]",
 +        "normalized overlap = 1 - difference/sqrt(tr(M1) + tr(M2))[BR]",
 +        "     shape overlap = 1 - sqrt(tr((sqrt(M1/tr(M1)) - sqrt(M2/tr(M2)))^2))[BR]",
 +        "where M1 and M2 are the two covariance matrices and tr is the trace",
 +        "of a matrix. The numbers are proportional to the overlap of the square",
 +        "root of the fluctuations. The normalized overlap is the most useful",
 +        "number, it is 1 for identical matrices and 0 when the sampled",
 +        "subspaces are orthogonal.[PAR]",
 +        "When the [TT]-entropy[tt] flag is given an entropy estimate will be",
 +        "computed based on the Quasiharmonic approach and based on",
 +        "Schlitter's formula."
 +    };
 +    static int         first  = 1, last = -1, skip = 1, nextr = 2, nskip = 6;
 +    static real        max    = 0.0, temp = 298.15;
 +    static gmx_bool    bSplit = FALSE, bEntropy = FALSE;
 +    t_pargs            pa[]   = {
 +        { "-first", FALSE, etINT, {&first},
 +          "First eigenvector for analysis (-1 is select)" },
 +        { "-last",  FALSE, etINT, {&last},
 +          "Last eigenvector for analysis (-1 is till the last)" },
 +        { "-skip",  FALSE, etINT, {&skip},
 +          "Only analyse every nr-th frame" },
 +        { "-max",  FALSE, etREAL, {&max},
 +          "Maximum for projection of the eigenvector on the average structure, "
 +          "max=0 gives the extremes" },
 +        { "-nframes",  FALSE, etINT, {&nextr},
 +          "Number of frames for the extremes output" },
 +        { "-split",   FALSE, etBOOL, {&bSplit},
 +          "Split eigenvector projections where time is zero" },
 +        { "-entropy", FALSE, etBOOL, {&bEntropy},
 +          "Compute entropy according to the Quasiharmonic formula or Schlitter's method." },
 +        { "-temp",    FALSE, etREAL, {&temp},
 +          "Temperature for entropy calculations" },
 +        { "-nevskip", FALSE, etINT, {&nskip},
 +          "Number of eigenvalues to skip when computing the entropy due to the quasi harmonic approximation. When you do a rotational and/or translational fit prior to the covariance analysis, you get 3 or 6 eigenvalues that are very close to zero, and which should not be taken into account when computing the entropy." }
 +    };
 +#define NPA asize(pa)
 +
 +    FILE          *out;
 +    int            status, trjout;
 +    t_topology     top;
 +    int            ePBC  = -1;
 +    t_atoms       *atoms = NULL;
 +    rvec          *xtop, *xref1, *xref2, *xrefp = NULL;
 +    gmx_bool       bDMR1, bDMA1, bDMR2, bDMA2;
 +    int            nvec1, nvec2, *eignr1 = NULL, *eignr2 = NULL;
 +    rvec          *x, *xread, *xav1, *xav2, **eigvec1 = NULL, **eigvec2 = NULL;
 +    matrix         topbox;
 +    real           xid, totmass, *sqrtm, *w_rls, t, lambda;
 +    int            natoms, step;
 +    char          *grpname;
 +    const char    *indexfile;
 +    char           title[STRLEN];
 +    int            i, j, d;
 +    int            nout, *iout, noutvec, *outvec, nfit;
 +    atom_id       *index, *ifit;
 +    const char    *VecFile, *Vec2File, *topfile;
 +    const char    *EigFile, *Eig2File;
 +    const char    *CompFile, *RmsfFile, *ProjOnVecFile;
 +    const char    *TwoDPlotFile, *ThreeDPlotFile;
 +    const char    *FilterFile, *ExtremeFile;
 +    const char    *OverlapFile, *InpMatFile;
 +    gmx_bool       bFit1, bFit2, bM, bIndex, bTPS, bTop, bVec2, bProj;
 +    gmx_bool       bFirstToLast, bFirstLastSet, bTraj, bCompare, bPDB3D;
 +    real          *eigval1 = NULL, *eigval2 = NULL;
 +    int            neig1, neig2;
 +    double       **xvgdata;
 +    output_env_t   oenv;
 +    gmx_rmpbc_t    gpbc;
 +
 +    t_filenm       fnm[] = {
 +        { efTRN, "-v",    "eigenvec",    ffREAD  },
 +        { efTRN, "-v2",   "eigenvec2",   ffOPTRD },
 +        { efTRX, "-f",    NULL,          ffOPTRD },
 +        { efTPS, NULL,    NULL,          ffOPTRD },
 +        { efNDX, NULL,    NULL,          ffOPTRD },
 +        { efXVG, "-eig", "eigenval",     ffOPTRD },
 +        { efXVG, "-eig2", "eigenval2",   ffOPTRD },
 +        { efXVG, "-comp", "eigcomp",     ffOPTWR },
 +        { efXVG, "-rmsf", "eigrmsf",     ffOPTWR },
 +        { efXVG, "-proj", "proj",        ffOPTWR },
 +        { efXVG, "-2d",   "2dproj",      ffOPTWR },
 +        { efSTO, "-3d",   "3dproj.pdb",  ffOPTWR },
 +        { efTRX, "-filt", "filtered",    ffOPTWR },
 +        { efTRX, "-extr", "extreme.pdb", ffOPTWR },
 +        { efXVG, "-over", "overlap",     ffOPTWR },
 +        { efXPM, "-inpr", "inprod",      ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +
 +    if (!parse_common_args(&argc, argv,
 +                           PCA_CAN_TIME | PCA_TIME_UNIT | PCA_CAN_VIEW | PCA_BE_NICE,
 +                           NFILE, fnm, NPA, pa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    indexfile = ftp2fn_null(efNDX, NFILE, fnm);
 +
 +    VecFile         = opt2fn("-v", NFILE, fnm);
 +    Vec2File        = opt2fn_null("-v2", NFILE, fnm);
 +    topfile         = ftp2fn(efTPS, NFILE, fnm);
 +    EigFile         = opt2fn_null("-eig", NFILE, fnm);
 +    Eig2File        = opt2fn_null("-eig2", NFILE, fnm);
 +    CompFile        = opt2fn_null("-comp", NFILE, fnm);
 +    RmsfFile        = opt2fn_null("-rmsf", NFILE, fnm);
 +    ProjOnVecFile   = opt2fn_null("-proj", NFILE, fnm);
 +    TwoDPlotFile    = opt2fn_null("-2d", NFILE, fnm);
 +    ThreeDPlotFile  = opt2fn_null("-3d", NFILE, fnm);
 +    FilterFile      = opt2fn_null("-filt", NFILE, fnm);
 +    ExtremeFile     = opt2fn_null("-extr", NFILE, fnm);
 +    OverlapFile     = opt2fn_null("-over", NFILE, fnm);
 +    InpMatFile      = ftp2fn_null(efXPM, NFILE, fnm);
 +
 +    bTop   = fn2bTPX(topfile);
 +    bProj  = ProjOnVecFile || TwoDPlotFile || ThreeDPlotFile
 +        || FilterFile || ExtremeFile;
 +    bFirstLastSet  =
 +        opt2parg_bSet("-first", NPA, pa) && opt2parg_bSet("-last", NPA, pa);
 +    bFirstToLast = CompFile || RmsfFile || ProjOnVecFile || FilterFile ||
 +        OverlapFile || ((ExtremeFile || InpMatFile) && bFirstLastSet);
 +    bVec2  = Vec2File || OverlapFile || InpMatFile;
 +    bM     = RmsfFile || bProj;
 +    bTraj  = ProjOnVecFile || FilterFile || (ExtremeFile && (max == 0))
 +        || TwoDPlotFile || ThreeDPlotFile;
 +    bIndex = bM || bProj;
 +    bTPS   = ftp2bSet(efTPS, NFILE, fnm) || bM || bTraj ||
 +        FilterFile  || (bIndex && indexfile);
 +    bCompare = Vec2File || Eig2File;
 +    bPDB3D   = fn2ftp(ThreeDPlotFile) == efPDB;
 +
 +    read_eigenvectors(VecFile, &natoms, &bFit1,
 +                      &xref1, &bDMR1, &xav1, &bDMA1,
 +                      &nvec1, &eignr1, &eigvec1, &eigval1);
 +    neig1 = DIM*natoms;
 +
 +    /* Overwrite eigenvalues from separate files if the user provides them */
 +    if (EigFile != NULL)
 +    {
 +        int neig_tmp = read_xvg(EigFile, &xvgdata, &i);
 +        if (neig_tmp != neig1)
 +        {
 +            fprintf(stderr, "Warning: number of eigenvalues in xvg file (%d) does not mtch trr file (%d)\n", neig1, natoms);
 +        }
 +        neig1 = neig_tmp;
 +        srenew(eigval1, neig1);
 +        for (j = 0; j < neig1; j++)
 +        {
 +            real tmp = eigval1[j];
 +            eigval1[j] = xvgdata[1][j];
 +            if (debug && (eigval1[j] != tmp))
 +            {
 +                fprintf(debug, "Replacing eigenvalue %d. From trr: %10g, from xvg: %10g\n",
 +                        j, tmp, eigval1[j]);
 +            }
 +        }
 +        for (j = 0; j < i; j++)
 +        {
 +            sfree(xvgdata[j]);
 +        }
 +        sfree(xvgdata);
 +        fprintf(stderr, "Read %d eigenvalues from %s\n", neig1, EigFile);
 +    }
 +
 +    if (bEntropy)
 +    {
 +        if (bDMA1)
 +        {
 +            gmx_fatal(FARGS, "Can not calculate entropies from mass-weighted eigenvalues, redo the analysis without mass-weighting");
 +        }
 +        calc_entropy_qh(stdout, neig1, eigval1, temp, nskip);
 +        calc_entropy_schlitter(stdout, neig1, nskip, eigval1, temp);
 +    }
 +
 +    if (bVec2)
 +    {
 +        if (!Vec2File)
 +        {
 +            gmx_fatal(FARGS, "Need a second eigenvector file to do this analysis.");
 +        }
 +        read_eigenvectors(Vec2File, &neig2, &bFit2,
 +                          &xref2, &bDMR2, &xav2, &bDMA2, &nvec2, &eignr2, &eigvec2, &eigval2);
 +
 +        neig2 = DIM*neig2;
 +        if (neig2 != neig1)
 +        {
 +            gmx_fatal(FARGS, "Dimensions in the eigenvector files don't match");
 +        }
 +    }
 +
 +    if (Eig2File != NULL)
 +    {
 +        neig2 = read_xvg(Eig2File, &xvgdata, &i);
 +        srenew(eigval2, neig2);
 +        for (j = 0; j < neig2; j++)
 +        {
 +            eigval2[j] = xvgdata[1][j];
 +        }
 +        for (j = 0; j < i; j++)
 +        {
 +            sfree(xvgdata[j]);
 +        }
 +        sfree(xvgdata);
 +        fprintf(stderr, "Read %d eigenvalues from %s\n", neig2, Eig2File);
 +    }
 +
 +
 +    if ((!bFit1 || xref1) && !bDMR1 && !bDMA1)
 +    {
 +        bM = FALSE;
 +    }
 +    if ((xref1 == NULL) && (bM || bTraj))
 +    {
 +        bTPS = TRUE;
 +    }
 +
 +    xtop  = NULL;
 +    nfit  = 0;
 +    ifit  = NULL;
 +    w_rls = NULL;
 +
 +    if (!bTPS)
 +    {
 +        bTop = FALSE;
 +    }
 +    else
 +    {
 +        bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm),
 +                             title, &top, &ePBC, &xtop, NULL, topbox, bM);
 +        atoms = &top.atoms;
 +        gpbc  = gmx_rmpbc_init(&top.idef, ePBC, atoms->nr);
 +        gmx_rmpbc(gpbc, atoms->nr, topbox, xtop);
 +        /* Fitting is only required for the projection */
 +        if (bProj && bFit1)
 +        {
 +            if (xref1 == NULL)
 +            {
 +                printf("\nNote: the structure in %s should be the same\n"
 +                       "      as the one used for the fit in g_covar\n", topfile);
 +            }
 +            printf("\nSelect the index group that was used for the least squares fit in g_covar\n");
 +            get_index(atoms, indexfile, 1, &nfit, &ifit, &grpname);
 +
 +            snew(w_rls, atoms->nr);
 +            for (i = 0; (i < nfit); i++)
 +            {
 +                if (bDMR1)
 +                {
 +                    w_rls[ifit[i]] = atoms->atom[ifit[i]].m;
 +                }
 +                else
 +                {
 +                    w_rls[ifit[i]] = 1.0;
 +                }
 +            }
 +
 +            snew(xrefp, atoms->nr);
 +            if (xref1 != NULL)
 +            {
 +                /* Safety check between selected fit-group and reference structure read from the eigenvector file */
 +                if (natoms != nfit)
 +                {
 +                    gmx_fatal(FARGS, "you selected a group with %d elements instead of %d, your selection does not fit the reference structure in the eigenvector file.", nfit, natoms);
 +                }
 +                for (i = 0; (i < nfit); i++)
 +                {
 +                    copy_rvec(xref1[i], xrefp[ifit[i]]);
 +                }
 +            }
 +            else
 +            {
 +                /* The top coordinates are the fitting reference */
 +                for (i = 0; (i < nfit); i++)
 +                {
 +                    copy_rvec(xtop[ifit[i]], xrefp[ifit[i]]);
 +                }
 +                reset_x(nfit, ifit, atoms->nr, NULL, xrefp, w_rls);
 +            }
 +        }
 +        gmx_rmpbc_done(gpbc);
 +    }
 +
 +    if (bIndex)
 +    {
 +        printf("\nSelect an index group of %d elements that corresponds to the eigenvectors\n", natoms);
 +        get_index(atoms, indexfile, 1, &i, &index, &grpname);
 +        if (i != natoms)
 +        {
 +            gmx_fatal(FARGS, "you selected a group with %d elements instead of %d", i, natoms);
 +        }
 +        printf("\n");
 +    }
 +
 +    snew(sqrtm, natoms);
 +    if (bM && bDMA1)
 +    {
 +        proj_unit = "u\\S1/2\\Nnm";
 +        for (i = 0; (i < natoms); i++)
 +        {
 +            sqrtm[i] = sqrt(atoms->atom[index[i]].m);
 +        }
 +    }
 +    else
 +    {
 +        proj_unit = "nm";
 +        for (i = 0; (i < natoms); i++)
 +        {
 +            sqrtm[i] = 1.0;
 +        }
 +    }
 +
 +    if (bVec2)
 +    {
 +        t       = 0;
 +        totmass = 0;
 +        for (i = 0; (i < natoms); i++)
 +        {
 +            for (d = 0; (d < DIM); d++)
 +            {
 +                t       += sqr((xav1[i][d]-xav2[i][d])*sqrtm[i]);
 +                totmass += sqr(sqrtm[i]);
 +            }
 +        }
 +        fprintf(stdout, "RMSD (without fit) between the two average structures:"
 +                " %.3f (nm)\n\n", sqrt(t/totmass));
 +    }
 +
 +    if (last == -1)
 +    {
 +        last = natoms*DIM;
 +    }
 +    if (first > -1)
 +    {
 +        if (bFirstToLast)
 +        {
 +            /* make an index from first to last */
 +            nout = last-first+1;
 +            snew(iout, nout);
 +            for (i = 0; i < nout; i++)
 +            {
 +                iout[i] = first-1+i;
 +            }
 +        }
 +        else if (ThreeDPlotFile)
 +        {
 +            /* make an index of first+(0,1,2) and last */
 +            nout = bPDB3D ? 4 : 3;
 +            nout = min(last-first+1, nout);
 +            snew(iout, nout);
 +            iout[0] = first-1;
 +            iout[1] = first;
 +            if (nout > 3)
 +            {
 +                iout[2] = first+1;
 +            }
 +            iout[nout-1] = last-1;
 +        }
 +        else
 +        {
 +            /* make an index of first and last */
 +            nout = 2;
 +            snew(iout, nout);
 +            iout[0] = first-1;
 +            iout[1] = last-1;
 +        }
 +    }
 +    else
 +    {
 +        printf("Select eigenvectors for output, end your selection with 0\n");
 +        nout = -1;
 +        iout = NULL;
 +
 +        do
 +        {
 +            nout++;
 +            srenew(iout, nout+1);
 +            if (1 != scanf("%d", &iout[nout]))
 +            {
 +                gmx_fatal(FARGS, "Error reading user input");
 +            }
 +            iout[nout]--;
 +        }
 +        while (iout[nout] >= 0);
 +
 +        printf("\n");
 +    }
 +    /* make an index of the eigenvectors which are present */
 +    snew(outvec, nout);
 +    noutvec = 0;
 +    for (i = 0; i < nout; i++)
 +    {
 +        j = 0;
 +        while ((j < nvec1) && (eignr1[j] != iout[i]))
 +        {
 +            j++;
 +        }
 +        if ((j < nvec1) && (eignr1[j] == iout[i]))
 +        {
 +            outvec[noutvec] = j;
 +            noutvec++;
 +        }
 +    }
 +    fprintf(stderr, "%d eigenvectors selected for output", noutvec);
 +    if (noutvec <= 100)
 +    {
 +        fprintf(stderr, ":");
 +        for (j = 0; j < noutvec; j++)
 +        {
 +            fprintf(stderr, " %d", eignr1[outvec[j]]+1);
 +        }
 +    }
 +    fprintf(stderr, "\n");
 +
 +    if (CompFile)
 +    {
 +        components(CompFile, natoms, eignr1, eigvec1, noutvec, outvec, oenv);
 +    }
 +
 +    if (RmsfFile)
 +    {
 +        rmsf(RmsfFile, natoms, sqrtm, eignr1, eigvec1, noutvec, outvec, eigval1,
 +             neig1, oenv);
 +    }
 +
 +    if (bProj)
 +    {
 +        project(bTraj ? opt2fn("-f", NFILE, fnm) : NULL,
 +                bTop ? &top : NULL, ePBC, topbox,
 +                ProjOnVecFile, TwoDPlotFile, ThreeDPlotFile, FilterFile, skip,
 +                ExtremeFile, bFirstLastSet, max, nextr, atoms, natoms, index,
 +                bFit1, xrefp, nfit, ifit, w_rls,
 +                sqrtm, xav1, eignr1, eigvec1, noutvec, outvec, bSplit,
 +                oenv);
 +    }
 +
 +    if (OverlapFile)
 +    {
 +        overlap(OverlapFile, natoms,
 +                eigvec1, nvec2, eignr2, eigvec2, noutvec, outvec, oenv);
 +    }
 +
 +    if (InpMatFile)
 +    {
 +        inprod_matrix(InpMatFile, natoms,
 +                      nvec1, eignr1, eigvec1, nvec2, eignr2, eigvec2,
 +                      bFirstLastSet, noutvec, outvec);
 +    }
 +
 +    if (bCompare)
 +    {
 +        compare(natoms, nvec1, eigvec1, nvec2, eigvec2, eigval1, neig1, eigval2, neig2);
 +    }
 +
 +
 +    if (!CompFile && !bProj && !OverlapFile && !InpMatFile &&
 +        !bCompare && !bEntropy)
 +    {
 +        fprintf(stderr, "\nIf you want some output,"
 +                " set one (or two or ...) of the output file options\n");
 +    }
 +
 +
 +    view_all(oenv, NFILE, fnm);
 +
 +    return 0;
 +}
index 494e19e3e0dca82b5429ccf6c1e89ebdb47c75f4,0000000000000000000000000000000000000000..480c1c67b2c164ebcab95e860b7e8afa63c5a8c2
mode 100644,000000..100644
--- /dev/null
@@@ -1,1420 -1,0 +1,1427 @@@
-             fprintf(fp, "&\n");
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +#include <math.h>
 +#include <string.h>
 +#include "gromacs/commandline/pargs.h"
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "gmx_fatal.h"
 +#include "vec.h"
 +#include "copyrite.h"
 +#include "gromacs/fileio/futil.h"
 +#include "readinp.h"
 +#include "txtdump.h"
 +#include "gstat.h"
 +#include "gromacs/statistics/statistics.h"
 +#include "xvgr.h"
 +#include "gmx_ana.h"
 +#include "geminate.h"
 +
 +#include "gromacs/linearalgebra/matrix.h"
 +
 +/* must correspond to char *avbar_opt[] declared in main() */
 +enum {
 +    avbarSEL, avbarNONE, avbarSTDDEV, avbarERROR, avbar90, avbarNR
 +};
 +
 +static void power_fit(int n, int nset, real **val, real *t)
 +{
 +    real *x, *y, quality, a, b, r;
 +    int   s, i;
 +
 +    snew(x, n);
 +    snew(y, n);
 +
 +    if (t[0] > 0)
 +    {
 +        for (i = 0; i < n; i++)
 +        {
 +            if (t[0] > 0)
 +            {
 +                x[i] = log(t[i]);
 +            }
 +        }
 +    }
 +    else
 +    {
 +        fprintf(stdout, "First time is not larger than 0, using index number as time for power fit\n");
 +        for (i = 0; i < n; i++)
 +        {
 +            x[i] = log(i+1);
 +        }
 +    }
 +
 +    for (s = 0; s < nset; s++)
 +    {
 +        i = 0;
 +        for (i = 0; i < n && val[s][i] >= 0; i++)
 +        {
 +            y[i] = log(val[s][i]);
 +        }
 +        if (i < n)
 +        {
 +            fprintf(stdout, "Will power fit up to point %d, since it is not larger than 0\n", i);
 +        }
 +        lsq_y_ax_b(i, x, y, &a, &b, &r, &quality);
 +        fprintf(stdout, "Power fit set %3d:  error %.3f  a %g  b %g\n",
 +                s+1, quality, a, exp(b));
 +    }
 +
 +    sfree(y);
 +    sfree(x);
 +}
 +
 +static real cosine_content(int nhp, int n, real *y)
 +/* Assumes n equidistant points */
 +{
 +    double fac, cosyint, yyint;
 +    int    i;
 +
 +    if (n < 2)
 +    {
 +        return 0;
 +    }
 +
 +    fac = M_PI*nhp/(n-1);
 +
 +    cosyint = 0;
 +    yyint   = 0;
 +    for (i = 0; i < n; i++)
 +    {
 +        cosyint += cos(fac*i)*y[i];
 +        yyint   += y[i]*y[i];
 +    }
 +
 +    return 2*cosyint*cosyint/(n*yyint);
 +}
 +
 +static void plot_coscont(const char *ccfile, int n, int nset, real **val,
 +                         const output_env_t oenv)
 +{
 +    FILE *fp;
 +    int   s;
 +    real  cc;
 +
 +    fp = xvgropen(ccfile, "Cosine content", "set / half periods", "cosine content",
 +                  oenv);
 +
 +    for (s = 0; s < nset; s++)
 +    {
 +        cc = cosine_content(s+1, n, val[s]);
 +        fprintf(fp, " %d %g\n", s+1, cc);
 +        fprintf(stdout, "Cosine content of set %d with %.1f periods: %g\n",
 +                s+1, 0.5*(s+1), cc);
 +    }
 +    fprintf(stdout, "\n");
 +
 +    gmx_ffclose(fp);
 +}
 +
 +static void regression_analysis(int n, gmx_bool bXYdy,
 +                                real *x, int nset, real **val)
 +{
 +    real S, chi2, a, b, da, db, r = 0;
 +    int  ok;
 +
 +    if (bXYdy || (nset == 1))
 +    {
 +        printf("Fitting data to a function f(x) = ax + b\n");
 +        printf("Minimizing residual chi2 = Sum_i w_i [f(x_i) - y_i]2\n");
 +        printf("Error estimates will be given if w_i (sigma) values are given\n");
 +        printf("(use option -xydy).\n\n");
 +        if (bXYdy)
 +        {
 +            if ((ok = lsq_y_ax_b_error(n, x, val[0], val[1], &a, &b, &da, &db, &r, &S)) != estatsOK)
 +            {
 +                gmx_fatal(FARGS, "Error fitting the data: %s",
 +                          gmx_stats_message(ok));
 +            }
 +        }
 +        else
 +        {
 +            if ((ok = lsq_y_ax_b(n, x, val[0], &a, &b, &r, &S)) != estatsOK)
 +            {
 +                gmx_fatal(FARGS, "Error fitting the data: %s",
 +                          gmx_stats_message(ok));
 +            }
 +        }
 +        chi2 = sqr((n-2)*S);
 +        printf("Chi2                    = %g\n", chi2);
 +        printf("S (Sqrt(Chi2/(n-2))     = %g\n", S);
 +        printf("Correlation coefficient = %.1f%%\n", 100*r);
 +        printf("\n");
 +        if (bXYdy)
 +        {
 +            printf("a    = %g +/- %g\n", a, da);
 +            printf("b    = %g +/- %g\n", b, db);
 +        }
 +        else
 +        {
 +            printf("a    = %g\n", a);
 +            printf("b    = %g\n", b);
 +        }
 +    }
 +    else
 +    {
 +        double chi2, *a, **xx, *y;
 +        int    i, j;
 +
 +        snew(y, n);
 +        snew(xx, nset-1);
 +        for (j = 0; (j < nset-1); j++)
 +        {
 +            snew(xx[j], n);
 +        }
 +        for (i = 0; (i < n); i++)
 +        {
 +            y[i] = val[0][i];
 +            for (j = 1; (j < nset); j++)
 +            {
 +                xx[j-1][i] = val[j][i];
 +            }
 +        }
 +        snew(a, nset-1);
 +        chi2 = multi_regression(NULL, n, y, nset-1, xx, a);
 +        printf("Fitting %d data points in %d sets\n", n, nset-1);
 +        printf("chi2 = %g\n", chi2);
 +        printf("A =");
 +        for (i = 0; (i < nset-1); i++)
 +        {
 +            printf("  %g", a[i]);
 +            sfree(xx[i]);
 +        }
 +        printf("\n");
 +        sfree(xx);
 +        sfree(y);
 +        sfree(a);
 +    }
 +}
 +
 +void histogram(const char *distfile, real binwidth, int n, int nset, real **val,
 +               const output_env_t oenv)
 +{
 +    FILE          *fp;
 +    int            i, s;
 +    double         min, max;
 +    int            nbin;
 +    gmx_int64_t   *histo;
 +
 +    min = val[0][0];
 +    max = val[0][0];
 +    for (s = 0; s < nset; s++)
 +    {
 +        for (i = 0; i < n; i++)
 +        {
 +            if (val[s][i] < min)
 +            {
 +                min = val[s][i];
 +            }
 +            else if (val[s][i] > max)
 +            {
 +                max = val[s][i];
 +            }
 +        }
 +    }
 +
 +    min = binwidth*floor(min/binwidth);
 +    max = binwidth*ceil(max/binwidth);
 +    if (min != 0)
 +    {
 +        min -= binwidth;
 +    }
 +    max += binwidth;
 +
 +    nbin = (int)((max - min)/binwidth + 0.5) + 1;
 +    fprintf(stderr, "Making distributions with %d bins\n", nbin);
 +    snew(histo, nbin);
 +    fp = xvgropen(distfile, "Distribution", "", "", oenv);
 +    for (s = 0; s < nset; s++)
 +    {
 +        for (i = 0; i < nbin; i++)
 +        {
 +            histo[i] = 0;
 +        }
 +        for (i = 0; i < n; i++)
 +        {
 +            histo[(int)((val[s][i] - min)/binwidth + 0.5)]++;
 +        }
 +        for (i = 0; i < nbin; i++)
 +        {
 +            fprintf(fp, " %g  %g\n", min+i*binwidth, (double)histo[i]/(n*binwidth));
 +        }
 +        if (s < nset-1)
 +        {
-         fprintf(fp, "@ legend string %d \"av %f\"\n", 2*s, av[s]);
-         fprintf(fp, "@ legend string %d \"ee %6g\"\n",
-                 2*s+1, sig[s]*anal_ee_inf(fitparm, n*dt));
++            fprintf(fp, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +        }
 +    }
 +    gmx_ffclose(fp);
 +}
 +
 +static int real_comp(const void *a, const void *b)
 +{
 +    real dif = *(real *)a - *(real *)b;
 +
 +    if (dif < 0)
 +    {
 +        return -1;
 +    }
 +    else if (dif > 0)
 +    {
 +        return 1;
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +static void average(const char *avfile, int avbar_opt,
 +                    int n, int nset, real **val, real *t)
 +{
 +    FILE   *fp;
 +    int     i, s, edge = 0;
 +    double  av, var, err;
 +    real   *tmp = NULL;
 +
 +    fp = gmx_ffopen(avfile, "w");
 +    if ((avbar_opt == avbarERROR) && (nset == 1))
 +    {
 +        avbar_opt = avbarNONE;
 +    }
 +    if (avbar_opt != avbarNONE)
 +    {
 +        if (avbar_opt == avbar90)
 +        {
 +            snew(tmp, nset);
 +            fprintf(fp, "@TYPE xydydy\n");
 +            edge = (int)(nset*0.05+0.5);
 +            fprintf(stdout, "Errorbars: discarding %d points on both sides: %d%%"
 +                    " interval\n", edge, (int)(100*(nset-2*edge)/nset+0.5));
 +        }
 +        else
 +        {
 +            fprintf(fp, "@TYPE xydy\n");
 +        }
 +    }
 +
 +    for (i = 0; i < n; i++)
 +    {
 +        av = 0;
 +        for (s = 0; s < nset; s++)
 +        {
 +            av += val[s][i];
 +        }
 +        av /= nset;
 +        fprintf(fp, " %g %g", t[i], av);
 +        var = 0;
 +        if (avbar_opt != avbarNONE)
 +        {
 +            if (avbar_opt == avbar90)
 +            {
 +                for (s = 0; s < nset; s++)
 +                {
 +                    tmp[s] = val[s][i];
 +                }
 +                qsort(tmp, nset, sizeof(tmp[0]), real_comp);
 +                fprintf(fp, " %g %g", tmp[nset-1-edge]-av, av-tmp[edge]);
 +            }
 +            else
 +            {
 +                for (s = 0; s < nset; s++)
 +                {
 +                    var += sqr(val[s][i]-av);
 +                }
 +                if (avbar_opt == avbarSTDDEV)
 +                {
 +                    err = sqrt(var/nset);
 +                }
 +                else
 +                {
 +                    err = sqrt(var/(nset*(nset-1)));
 +                }
 +                fprintf(fp, " %g", err);
 +            }
 +        }
 +        fprintf(fp, "\n");
 +    }
 +    gmx_ffclose(fp);
 +
 +    if (avbar_opt == avbar90)
 +    {
 +        sfree(tmp);
 +    }
 +}
 +
 +static real anal_ee_inf(real *parm, real T)
 +{
 +    return sqrt(parm[1]*2*parm[0]/T+parm[3]*2*parm[2]/T);
 +}
 +
 +static void estimate_error(const char *eefile, int nb_min, int resol, int n,
 +                           int nset, double *av, double *sig, real **val, real dt,
 +                           gmx_bool bFitAc, gmx_bool bSingleExpFit, gmx_bool bAllowNegLTCorr,
 +                           const output_env_t oenv)
 +{
 +    FILE    *fp;
 +    int      bs, prev_bs, nbs, nb;
 +    real     spacing, nbr;
 +    int      s, i, j;
 +    double   blav, var;
 +    char   **leg;
 +    real    *tbs, *ybs, rtmp, dens, *fitsig, twooe, tau1_est, tau_sig;
 +    real     fitparm[4];
 +    real     ee, a, tau1, tau2;
 +
 +    if (n < 4)
 +    {
 +        fprintf(stdout, "The number of points is smaller than 4, can not make an error estimate\n");
 +
 +        return;
 +    }
 +
 +    fp = xvgropen(eefile, "Error estimates",
 +                  "Block size (time)", "Error estimate", oenv);
 +    if (output_env_get_print_xvgr_codes(oenv))
 +    {
 +        fprintf(fp,
 +                "@ subtitle \"using block averaging, total time %g (%d points)\"\n",
 +                (n-1)*dt, n);
 +    }
 +    snew(leg, 2*nset);
 +    xvgr_legend(fp, 2*nset, (const char**)leg, oenv);
 +    sfree(leg);
 +
 +    spacing = pow(2, 1.0/resol);
 +    snew(tbs, n);
 +    snew(ybs, n);
 +    snew(fitsig, n);
 +    for (s = 0; s < nset; s++)
 +    {
 +        nbs     = 0;
 +        prev_bs = 0;
 +        nbr     = nb_min;
 +        while (nbr <= n)
 +        {
 +            bs = n/(int)nbr;
 +            if (bs != prev_bs)
 +            {
 +                nb  = n/bs;
 +                var = 0;
 +                for (i = 0; i < nb; i++)
 +                {
 +                    blav = 0;
 +                    for (j = 0; j < bs; j++)
 +                    {
 +                        blav += val[s][bs*i+j];
 +                    }
 +                    var += sqr(av[s] - blav/bs);
 +                }
 +                tbs[nbs] = bs*dt;
 +                if (sig[s] == 0)
 +                {
 +                    ybs[nbs] = 0;
 +                }
 +                else
 +                {
 +                    ybs[nbs] = var/(nb*(nb-1.0))*(n*dt)/(sig[s]*sig[s]);
 +                }
 +                nbs++;
 +            }
 +            nbr    *= spacing;
 +            nb      = (int)(nbr+0.5);
 +            prev_bs = bs;
 +        }
 +        if (sig[s] == 0)
 +        {
 +            ee   = 0;
 +            a    = 1;
 +            tau1 = 0;
 +            tau2 = 0;
 +        }
 +        else
 +        {
 +            for (i = 0; i < nbs/2; i++)
 +            {
 +                rtmp         = tbs[i];
 +                tbs[i]       = tbs[nbs-1-i];
 +                tbs[nbs-1-i] = rtmp;
 +                rtmp         = ybs[i];
 +                ybs[i]       = ybs[nbs-1-i];
 +                ybs[nbs-1-i] = rtmp;
 +            }
 +            /* The initial slope of the normalized ybs^2 is 1.
 +             * For a single exponential autocorrelation: ybs(tau1) = 2/e tau1
 +             * From this we take our initial guess for tau1.
 +             */
 +            twooe = 2/exp(1);
 +            i     = -1;
 +            do
 +            {
 +                i++;
 +                tau1_est = tbs[i];
 +            }
 +            while (i < nbs - 1 &&
 +                   (ybs[i] > ybs[i+1] || ybs[i] > twooe*tau1_est));
 +
 +            if (ybs[0] > ybs[1])
 +            {
 +                fprintf(stdout, "Data set %d has strange time correlations:\n"
 +                        "the std. error using single points is larger than that of blocks of 2 points\n"
 +                        "The error estimate might be inaccurate, check the fit\n",
 +                        s+1);
 +                /* Use the total time as tau for the fitting weights */
 +                tau_sig = (n - 1)*dt;
 +            }
 +            else
 +            {
 +                tau_sig = tau1_est;
 +            }
 +
 +            if (debug)
 +            {
 +                fprintf(debug, "set %d tau1 estimate %f\n", s+1, tau1_est);
 +            }
 +
 +            /* Generate more or less appropriate sigma's,
 +             * also taking the density of points into account.
 +             */
 +            for (i = 0; i < nbs; i++)
 +            {
 +                if (i == 0)
 +                {
 +                    dens = tbs[1]/tbs[0] - 1;
 +                }
 +                else if (i == nbs-1)
 +                {
 +                    dens = tbs[nbs-1]/tbs[nbs-2] - 1;
 +                }
 +                else
 +                {
 +                    dens = 0.5*(tbs[i+1]/tbs[i-1] - 1);
 +                }
 +                fitsig[i] = sqrt((tau_sig + tbs[i])/dens);
 +            }
 +
 +            if (!bSingleExpFit)
 +            {
 +                fitparm[0] = tau1_est;
 +                fitparm[1] = 0.95;
 +                /* We set the initial guess for tau2
 +                 * to halfway between tau1_est and the total time (on log scale).
 +                 */
 +                fitparm[2] = sqrt(tau1_est*(n-1)*dt);
 +                do_lmfit(nbs, ybs, fitsig, 0, tbs, 0, dt*n, oenv,
 +                         bDebugMode(), effnERREST, fitparm, 0);
 +                fitparm[3] = 1-fitparm[1];
 +            }
 +            if (bSingleExpFit || fitparm[0] < 0 || fitparm[2] < 0 || fitparm[1] < 0
 +                || (fitparm[1] > 1 && !bAllowNegLTCorr) || fitparm[2] > (n-1)*dt)
 +            {
 +                if (!bSingleExpFit)
 +                {
 +                    if (fitparm[2] > (n-1)*dt)
 +                    {
 +                        fprintf(stdout,
 +                                "Warning: tau2 is longer than the length of the data (%g)\n"
 +                                "         the statistics might be bad\n",
 +                                (n-1)*dt);
 +                    }
 +                    else
 +                    {
 +                        fprintf(stdout, "a fitted parameter is negative\n");
 +                    }
 +                    fprintf(stdout, "invalid fit:  e.e. %g  a %g  tau1 %g  tau2 %g\n",
 +                            sig[s]*anal_ee_inf(fitparm, n*dt),
 +                            fitparm[1], fitparm[0], fitparm[2]);
 +                    /* Do a fit with tau2 fixed at the total time.
 +                     * One could also choose any other large value for tau2.
 +                     */
 +                    fitparm[0] = tau1_est;
 +                    fitparm[1] = 0.95;
 +                    fitparm[2] = (n-1)*dt;
 +                    fprintf(stderr, "Will fix tau2 at the total time: %g\n", fitparm[2]);
 +                    do_lmfit(nbs, ybs, fitsig, 0, tbs, 0, dt*n, oenv, bDebugMode(),
 +                             effnERREST, fitparm, 4);
 +                    fitparm[3] = 1-fitparm[1];
 +                }
 +                if (bSingleExpFit || fitparm[0] < 0 || fitparm[1] < 0
 +                    || (fitparm[1] > 1 && !bAllowNegLTCorr))
 +                {
 +                    if (!bSingleExpFit)
 +                    {
 +                        fprintf(stdout, "a fitted parameter is negative\n");
 +                        fprintf(stdout, "invalid fit:  e.e. %g  a %g  tau1 %g  tau2 %g\n",
 +                                sig[s]*anal_ee_inf(fitparm, n*dt),
 +                                fitparm[1], fitparm[0], fitparm[2]);
 +                    }
 +                    /* Do a single exponential fit */
 +                    fprintf(stderr, "Will use a single exponential fit for set %d\n", s+1);
 +                    fitparm[0] = tau1_est;
 +                    fitparm[1] = 1.0;
 +                    fitparm[2] = 0.0;
 +                    do_lmfit(nbs, ybs, fitsig, 0, tbs, 0, dt*n, oenv, bDebugMode(),
 +                             effnERREST, fitparm, 6);
 +                    fitparm[3] = 1-fitparm[1];
 +                }
 +            }
 +            ee   = sig[s]*anal_ee_inf(fitparm, n*dt);
 +            a    = fitparm[1];
 +            tau1 = fitparm[0];
 +            tau2 = fitparm[2];
 +        }
 +        fprintf(stdout, "Set %3d:  err.est. %g  a %g  tau1 %g  tau2 %g\n",
 +                s+1, ee, a, tau1, tau2);
-             fprintf(fp, "&\n");
++        if (output_env_get_xvg_format(oenv) == exvgXMGR)
++        {
++            fprintf(fp, "@ legend string %d \"av %f\"\n", 2*s, av[s]);
++            fprintf(fp, "@ legend string %d \"ee %6g\"\n", 2*s+1, sig[s]*anal_ee_inf(fitparm, n*dt));
++        }
++        else if (output_env_get_xvg_format(oenv) == exvgXMGRACE)
++        {
++            fprintf(fp, "@ s%d legend \"av %f\"\n", 2*s, av[s]);
++            fprintf(fp, "@ s%d legend \"ee %6g\"\n", 2*s+1, sig[s]*anal_ee_inf(fitparm, n*dt));
++        }
 +        for (i = 0; i < nbs; i++)
 +        {
 +            fprintf(fp, "%g %g %g\n", tbs[i], sig[s]*sqrt(ybs[i]/(n*dt)),
 +                    sig[s]*sqrt(fit_function(effnERREST, fitparm, tbs[i])/(n*dt)));
 +        }
 +
 +        if (bFitAc)
 +        {
 +            int   fitlen;
 +            real *ac, acint, ac_fit[4];
 +
 +            snew(ac, n);
 +            for (i = 0; i < n; i++)
 +            {
 +                ac[i] = val[s][i] - av[s];
 +                if (i > 0)
 +                {
 +                    fitsig[i] = sqrt(i);
 +                }
 +                else
 +                {
 +                    fitsig[i] = 1;
 +                }
 +            }
 +            low_do_autocorr(NULL, oenv, NULL, n, 1, -1, &ac,
 +                            dt, eacNormal, 1, FALSE, TRUE,
 +                            FALSE, 0, 0, effnNONE);
 +
 +            fitlen = n/nb_min;
 +
 +            /* Integrate ACF only up to fitlen/2 to avoid integrating noise */
 +            acint = 0.5*ac[0];
 +            for (i = 1; i <= fitlen/2; i++)
 +            {
 +                acint += ac[i];
 +            }
 +            acint *= dt;
 +
 +            /* Generate more or less appropriate sigma's */
 +            for (i = 0; i <= fitlen; i++)
 +            {
 +                fitsig[i] = sqrt(acint + dt*i);
 +            }
 +
 +            ac_fit[0] = 0.5*acint;
 +            ac_fit[1] = 0.95;
 +            ac_fit[2] = 10*acint;
 +            do_lmfit(n/nb_min, ac, fitsig, dt, 0, 0, fitlen*dt, oenv,
 +                     bDebugMode(), effnEXP3, ac_fit, 0);
 +            ac_fit[3] = 1 - ac_fit[1];
 +
 +            fprintf(stdout, "Set %3d:  ac erest %g  a %g  tau1 %g  tau2 %g\n",
 +                    s+1, sig[s]*anal_ee_inf(ac_fit, n*dt),
 +                    ac_fit[1], ac_fit[0], ac_fit[2]);
 +
-             fprintf(fp, "&\n");
++            fprintf(fp, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +            for (i = 0; i < nbs; i++)
 +            {
 +                fprintf(fp, "%g %g\n", tbs[i],
 +                        sig[s]*sqrt(fit_function(effnERREST, ac_fit, tbs[i]))/(n*dt));
 +            }
 +
 +            sfree(ac);
 +        }
 +        if (s < nset-1)
 +        {
-                 fprintf(out, "&\n");
++            fprintf(fp, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +        }
 +    }
 +    sfree(fitsig);
 +    sfree(ybs);
 +    sfree(tbs);
 +    gmx_ffclose(fp);
 +}
 +
 +static void luzar_correl(int nn, real *time, int nset, real **val, real temp,
 +                         gmx_bool bError, real fit_start, real smooth_tail_start,
 +                         const output_env_t oenv)
 +{
 +    const real tol = 1e-8;
 +    real      *kt;
 +    real       weight, d2;
 +    int        j;
 +
 +    please_cite(stdout, "Spoel2006b");
 +
 +    /* Compute negative derivative k(t) = -dc(t)/dt */
 +    if (!bError)
 +    {
 +        snew(kt, nn);
 +        compute_derivative(nn, time, val[0], kt);
 +        for (j = 0; (j < nn); j++)
 +        {
 +            kt[j] = -kt[j];
 +        }
 +        if (debug)
 +        {
 +            d2 = 0;
 +            for (j = 0; (j < nn); j++)
 +            {
 +                d2 += sqr(kt[j] - val[3][j]);
 +            }
 +            fprintf(debug, "RMS difference in derivatives is %g\n", sqrt(d2/nn));
 +        }
 +        analyse_corr(nn, time, val[0], val[2], kt, NULL, NULL, NULL, fit_start,
 +                     temp, smooth_tail_start, oenv);
 +        sfree(kt);
 +    }
 +    else if (nset == 6)
 +    {
 +        analyse_corr(nn, time, val[0], val[2], val[4],
 +                     val[1], val[3], val[5], fit_start, temp, smooth_tail_start, oenv);
 +    }
 +    else
 +    {
 +        printf("Inconsistent input. I need c(t) sigma_c(t) n(t) sigma_n(t) K(t) sigma_K(t)\n");
 +        printf("Not doing anything. Sorry.\n");
 +    }
 +}
 +
 +static void filter(real flen, int n, int nset, real **val, real dt)
 +{
 +    int     f, s, i, j;
 +    double *filt, sum, vf, fluc, fluctot;
 +
 +    f = (int)(flen/(2*dt));
 +    snew(filt, f+1);
 +    filt[0] = 1;
 +    sum     = 1;
 +    for (i = 1; i <= f; i++)
 +    {
 +        filt[i] = cos(M_PI*dt*i/flen);
 +        sum    += 2*filt[i];
 +    }
 +    for (i = 0; i <= f; i++)
 +    {
 +        filt[i] /= sum;
 +    }
 +    fprintf(stdout, "Will calculate the fluctuation over %d points\n", n-2*f);
 +    fprintf(stdout, "  using a filter of length %g of %d points\n", flen, 2*f+1);
 +    fluctot = 0;
 +    for (s = 0; s < nset; s++)
 +    {
 +        fluc = 0;
 +        for (i = f; i < n-f; i++)
 +        {
 +            vf = filt[0]*val[s][i];
 +            for (j = 1; j <= f; j++)
 +            {
 +                vf += filt[j]*(val[s][i-f]+val[s][i+f]);
 +            }
 +            fluc += sqr(val[s][i] - vf);
 +        }
 +        fluc    /= n - 2*f;
 +        fluctot += fluc;
 +        fprintf(stdout, "Set %3d filtered fluctuation: %12.6e\n", s+1, sqrt(fluc));
 +    }
 +    fprintf(stdout, "Overall filtered fluctuation: %12.6e\n", sqrt(fluctot/nset));
 +    fprintf(stdout, "\n");
 +
 +    sfree(filt);
 +}
 +
 +static void do_fit(FILE *out, int n, gmx_bool bYdy,
 +                   int ny, real *x0, real **val,
 +                   int npargs, t_pargs *ppa, const output_env_t oenv)
 +{
 +    real *c1 = NULL, *sig = NULL, *fitparm;
 +    real  tendfit, tbeginfit;
 +    int   i, efitfn, nparm;
 +
 +    efitfn = get_acffitfn();
 +    nparm  = nfp_ffn[efitfn];
 +    fprintf(out, "Will fit to the following function:\n");
 +    fprintf(out, "%s\n", longs_ffn[efitfn]);
 +    c1 = val[n];
 +    if (bYdy)
 +    {
 +        c1  = val[n];
 +        sig = val[n+1];
 +        fprintf(out, "Using two columns as y and sigma values\n");
 +    }
 +    else
 +    {
 +        snew(sig, ny);
 +    }
 +    if (opt2parg_bSet("-beginfit", npargs, ppa))
 +    {
 +        tbeginfit = opt2parg_real("-beginfit", npargs, ppa);
 +    }
 +    else
 +    {
 +        tbeginfit = x0[0];
 +    }
 +    if (opt2parg_bSet("-endfit", npargs, ppa))
 +    {
 +        tendfit   = opt2parg_real("-endfit", npargs, ppa);
 +    }
 +    else
 +    {
 +        tendfit   = x0[ny-1];
 +    }
 +
 +    snew(fitparm, nparm);
 +    switch (efitfn)
 +    {
 +        case effnEXP1:
 +            fitparm[0] = 0.5;
 +            break;
 +        case effnEXP2:
 +            fitparm[0] = 0.5;
 +            fitparm[1] = c1[0];
 +            break;
 +        case effnEXP3:
 +            fitparm[0] = 1.0;
 +            fitparm[1] = 0.5*c1[0];
 +            fitparm[2] = 10.0;
 +            break;
 +        case effnEXP5:
 +            fitparm[0] = fitparm[2] = 0.5*c1[0];
 +            fitparm[1] = 10;
 +            fitparm[3] = 40;
 +            fitparm[4] = 0;
 +            break;
 +        case effnEXP7:
 +            fitparm[0] = fitparm[2] = fitparm[4] = 0.33*c1[0];
 +            fitparm[1] = 1;
 +            fitparm[3] = 10;
 +            fitparm[5] = 100;
 +            fitparm[6] = 0;
 +            break;
 +        case effnEXP9:
 +            fitparm[0] = fitparm[2] = fitparm[4] = fitparm[6] = 0.25*c1[0];
 +            fitparm[1] = 0.1;
 +            fitparm[3] = 1;
 +            fitparm[5] = 10;
 +            fitparm[7] = 100;
 +            fitparm[8] = 0;
 +            break;
 +        default:
 +            fprintf(out, "Warning: don't know how to initialize the parameters\n");
 +            for (i = 0; (i < nparm); i++)
 +            {
 +                fitparm[i] = 1;
 +            }
 +    }
 +    fprintf(out, "Starting parameters:\n");
 +    for (i = 0; (i < nparm); i++)
 +    {
 +        fprintf(out, "a%-2d = %12.5e\n", i+1, fitparm[i]);
 +    }
 +    if (do_lmfit(ny, c1, sig, 0, x0, tbeginfit, tendfit,
 +                 oenv, bDebugMode(), efitfn, fitparm, 0))
 +    {
 +        for (i = 0; (i < nparm); i++)
 +        {
 +            fprintf(out, "a%-2d = %12.5e\n", i+1, fitparm[i]);
 +        }
 +    }
 +    else
 +    {
 +        fprintf(out, "No solution was found\n");
 +    }
 +}
 +
 +static void do_ballistic(const char *balFile, int nData,
 +                         real *t, real **val, int nSet,
 +                         real balTime, int nBalExp,
 +                         const output_env_t oenv)
 +{
 +    double     **ctd   = NULL, *td = NULL;
 +    t_gemParams *GP    = init_gemParams(0, 0, t, nData, 0, 0, 0, balTime, nBalExp);
 +    static char *leg[] = {"Ac'(t)"};
 +    FILE        *fp;
 +    int          i, set;
 +
 +    if (GP->ballistic/GP->tDelta >= GP->nExpFit*2+1)
 +    {
 +        snew(ctd, nSet);
 +        snew(td,  nData);
 +
 +        fp = xvgropen(balFile, "Hydrogen Bond Autocorrelation", "Time (ps)", "C'(t)", oenv);
 +        xvgr_legend(fp, asize(leg), (const char**)leg, oenv);
 +
 +        for (set = 0; set < nSet; set++)
 +        {
 +            snew(ctd[set], nData);
 +            for (i = 0; i < nData; i++)
 +            {
 +                ctd[set][i] = (double)val[set][i];
 +                if (set == 0)
 +                {
 +                    td[i] = (double)t[i];
 +                }
 +            }
 +
 +            takeAwayBallistic(ctd[set], td, nData, GP->ballistic, GP->nExpFit, GP->bDt);
 +        }
 +
 +        for (i = 0; i < nData; i++)
 +        {
 +            fprintf(fp, "  %g", t[i]);
 +            for (set = 0; set < nSet; set++)
 +            {
 +                fprintf(fp, "  %g", ctd[set][i]);
 +            }
 +            fprintf(fp, "\n");
 +        }
 +
 +
 +        for (set = 0; set < nSet; set++)
 +        {
 +            sfree(ctd[set]);
 +        }
 +        sfree(ctd);
 +        sfree(td);
 +    }
 +    else
 +    {
 +        printf("Number of data points is less than the number of parameters to fit\n."
 +               "The system is underdetermined, hence no ballistic term can be found.\n\n");
 +    }
 +}
 +
 +static void do_geminate(const char *gemFile, int nData,
 +                        real *t, real **val, int nSet,
 +                        const real D, const real rcut, const real balTime,
 +                        const int nFitPoints, const real begFit, const real endFit,
 +                        const output_env_t oenv)
 +{
 +    double     **ctd = NULL, **ctdGem = NULL, *td = NULL;
 +    t_gemParams *GP  = init_gemParams(rcut, D, t, nData, nFitPoints,
 +                                      begFit, endFit, balTime, 1);
 +    const char  *leg[] = {"Ac\\sgem\\N(t)"};
 +    FILE        *fp;
 +    int          i, set;
 +
 +    snew(ctd,    nSet);
 +    snew(ctdGem, nSet);
 +    snew(td,  nData);
 +
 +    fp = xvgropen(gemFile, "Hydrogen Bond Autocorrelation", "Time (ps)", "C'(t)", oenv);
 +    xvgr_legend(fp, asize(leg), leg, oenv);
 +
 +    for (set = 0; set < nSet; set++)
 +    {
 +        snew(ctd[set],    nData);
 +        snew(ctdGem[set], nData);
 +        for (i = 0; i < nData; i++)
 +        {
 +            ctd[set][i] = (double)val[set][i];
 +            if (set == 0)
 +            {
 +                td[i] = (double)t[i];
 +            }
 +        }
 +        fitGemRecomb(ctd[set], td, &(ctd[set]), nData, GP);
 +    }
 +
 +    for (i = 0; i < nData; i++)
 +    {
 +        fprintf(fp, "  %g", t[i]);
 +        for (set = 0; set < nSet; set++)
 +        {
 +            fprintf(fp, "  %g", ctdGem[set][i]);
 +        }
 +        fprintf(fp, "\n");
 +    }
 +
 +    for (set = 0; set < nSet; set++)
 +    {
 +        sfree(ctd[set]);
 +        sfree(ctdGem[set]);
 +    }
 +    sfree(ctd);
 +    sfree(ctdGem);
 +    sfree(td);
 +}
 +
 +int gmx_analyze(int argc, char *argv[])
 +{
 +    static const char *desc[] = {
 +        "[THISMODULE] reads an ASCII file and analyzes data sets.",
 +        "A line in the input file may start with a time",
 +        "(see option [TT]-time[tt]) and any number of [IT]y[it]-values may follow.",
 +        "Multiple sets can also be",
 +        "read when they are separated by & (option [TT]-n[tt]);",
 +        "in this case only one [IT]y[it]-value is read from each line.",
 +        "All lines starting with # and @ are skipped.",
 +        "All analyses can also be done for the derivative of a set",
 +        "(option [TT]-d[tt]).[PAR]",
 +
 +        "All options, except for [TT]-av[tt] and [TT]-power[tt], assume that the",
 +        "points are equidistant in time.[PAR]",
 +
 +        "[THISMODULE] always shows the average and standard deviation of each",
 +        "set, as well as the relative deviation of the third",
 +        "and fourth cumulant from those of a Gaussian distribution with the same",
 +        "standard deviation.[PAR]",
 +
 +        "Option [TT]-ac[tt] produces the autocorrelation function(s).",
 +        "Be sure that the time interval between data points is",
 +        "much shorter than the time scale of the autocorrelation.[PAR]",
 +
 +        "Option [TT]-cc[tt] plots the resemblance of set i with a cosine of",
 +        "i/2 periods. The formula is:[BR]"
 +        "[MATH]2 ([INT][FROM]0[from][TO]T[to][int] y(t) [COS]i [GRK]pi[grk] t[cos] dt)^2 / [INT][FROM]0[from][TO]T[to][int] y^2(t) dt[math][BR]",
 +        "This is useful for principal components obtained from covariance",
 +        "analysis, since the principal components of random diffusion are",
 +        "pure cosines.[PAR]",
 +
 +        "Option [TT]-msd[tt] produces the mean square displacement(s).[PAR]",
 +
 +        "Option [TT]-dist[tt] produces distribution plot(s).[PAR]",
 +
 +        "Option [TT]-av[tt] produces the average over the sets.",
 +        "Error bars can be added with the option [TT]-errbar[tt].",
 +        "The errorbars can represent the standard deviation, the error",
 +        "(assuming the points are independent) or the interval containing",
 +        "90% of the points, by discarding 5% of the points at the top and",
 +        "the bottom.[PAR]",
 +
 +        "Option [TT]-ee[tt] produces error estimates using block averaging.",
 +        "A set is divided in a number of blocks and averages are calculated for",
 +        "each block. The error for the total average is calculated from",
 +        "the variance between averages of the m blocks B[SUB]i[sub] as follows:",
 +        "error^2 = [SUM][sum] (B[SUB]i[sub] - [CHEVRON]B[chevron])^2 / (m*(m-1)).",
 +        "These errors are plotted as a function of the block size.",
 +        "Also an analytical block average curve is plotted, assuming",
 +        "that the autocorrelation is a sum of two exponentials.",
 +        "The analytical curve for the block average is:[BR]",
 +        "[MATH]f(t) = [GRK]sigma[grk][TT]*[tt][SQRT]2/T (  [GRK]alpha[grk]   ([GRK]tau[grk][SUB]1[sub] (([EXP]-t/[GRK]tau[grk][SUB]1[sub][exp] - 1) [GRK]tau[grk][SUB]1[sub]/t + 1)) +[BR]",
 +        "                       (1-[GRK]alpha[grk]) ([GRK]tau[grk][SUB]2[sub] (([EXP]-t/[GRK]tau[grk][SUB]2[sub][exp] - 1) [GRK]tau[grk][SUB]2[sub]/t + 1)))[sqrt][math],[BR]"
 +        "where T is the total time.",
 +        "[GRK]alpha[grk], [GRK]tau[grk][SUB]1[sub] and [GRK]tau[grk][SUB]2[sub] are obtained by fitting f^2(t) to error^2.",
 +        "When the actual block average is very close to the analytical curve,",
 +        "the error is [MATH][GRK]sigma[grk][TT]*[tt][SQRT]2/T (a [GRK]tau[grk][SUB]1[sub] + (1-a) [GRK]tau[grk][SUB]2[sub])[sqrt][math].",
 +        "The complete derivation is given in",
 +        "B. Hess, J. Chem. Phys. 116:209-217, 2002.[PAR]",
 +
 +        "Option [TT]-bal[tt] finds and subtracts the ultrafast \"ballistic\"",
 +        "component from a hydrogen bond autocorrelation function by the fitting",
 +        "of a sum of exponentials, as described in e.g.",
 +        "O. Markovitch, J. Chem. Phys. 129:084505, 2008. The fastest term",
 +        "is the one with the most negative coefficient in the exponential,",
 +        "or with [TT]-d[tt], the one with most negative time derivative at time 0.",
 +        "[TT]-nbalexp[tt] sets the number of exponentials to fit.[PAR]",
 +
 +        "Option [TT]-gem[tt] fits bimolecular rate constants ka and kb",
 +        "(and optionally kD) to the hydrogen bond autocorrelation function",
 +        "according to the reversible geminate recombination model. Removal of",
 +        "the ballistic component first is strongly advised. The model is presented in",
 +        "O. Markovitch, J. Chem. Phys. 129:084505, 2008.[PAR]",
 +
 +        "Option [TT]-filter[tt] prints the RMS high-frequency fluctuation",
 +        "of each set and over all sets with respect to a filtered average.",
 +        "The filter is proportional to cos([GRK]pi[grk] t/len) where t goes from -len/2",
 +        "to len/2. len is supplied with the option [TT]-filter[tt].",
 +        "This filter reduces oscillations with period len/2 and len by a factor",
 +        "of 0.79 and 0.33 respectively.[PAR]",
 +
 +        "Option [TT]-g[tt] fits the data to the function given with option",
 +        "[TT]-fitfn[tt].[PAR]",
 +
 +        "Option [TT]-power[tt] fits the data to [MATH]b t^a[math], which is accomplished",
 +        "by fitting to [MATH]a t + b[math] on log-log scale. All points after the first",
 +        "zero or with a negative value are ignored.[PAR]"
 +
 +        "Option [TT]-luzar[tt] performs a Luzar & Chandler kinetics analysis",
 +        "on output from [gmx-hbond]. The input file can be taken directly",
 +        "from [TT]gmx hbond -ac[tt], and then the same result should be produced."
 +    };
 +    static real        tb         = -1, te = -1, frac = 0.5, filtlen = 0, binwidth = 0.1, aver_start = 0;
 +    static gmx_bool    bHaveT     = TRUE, bDer = FALSE, bSubAv = TRUE, bAverCorr = FALSE, bXYdy = FALSE;
 +    static gmx_bool    bEESEF     = FALSE, bEENLC = FALSE, bEeFitAc = FALSE, bPower = FALSE;
 +    static gmx_bool    bIntegrate = FALSE, bRegression = FALSE, bLuzar = FALSE, bLuzarError = FALSE;
 +    static int         nsets_in   = 1, d = 1, nb_min = 4, resol = 10, nBalExp = 4, nFitPoints = 100;
 +    static real        temp       = 298.15, fit_start = 1, fit_end = 60, smooth_tail_start = -1, balTime = 0.2, diffusion = 5e-5, rcut = 0.35;
 +
 +    /* must correspond to enum avbar* declared at beginning of file */
 +    static const char *avbar_opt[avbarNR+1] = {
 +        NULL, "none", "stddev", "error", "90", NULL
 +    };
 +
 +    t_pargs            pa[] = {
 +        { "-time",    FALSE, etBOOL, {&bHaveT},
 +          "Expect a time in the input" },
 +        { "-b",       FALSE, etREAL, {&tb},
 +          "First time to read from set" },
 +        { "-e",       FALSE, etREAL, {&te},
 +          "Last time to read from set" },
 +        { "-n",       FALSE, etINT, {&nsets_in},
 +          "Read this number of sets separated by &" },
 +        { "-d",       FALSE, etBOOL, {&bDer},
 +          "Use the derivative" },
 +        { "-dp",      FALSE, etINT, {&d},
 +          "HIDDENThe derivative is the difference over this number of points" },
 +        { "-bw",      FALSE, etREAL, {&binwidth},
 +          "Binwidth for the distribution" },
 +        { "-errbar",  FALSE, etENUM, {avbar_opt},
 +          "Error bars for [TT]-av[tt]" },
 +        { "-integrate", FALSE, etBOOL, {&bIntegrate},
 +          "Integrate data function(s) numerically using trapezium rule" },
 +        { "-aver_start", FALSE, etREAL, {&aver_start},
 +          "Start averaging the integral from here" },
 +        { "-xydy",    FALSE, etBOOL, {&bXYdy},
 +          "Interpret second data set as error in the y values for integrating" },
 +        { "-regression", FALSE, etBOOL, {&bRegression},
 +          "Perform a linear regression analysis on the data. If [TT]-xydy[tt] is set a second set will be interpreted as the error bar in the Y value. Otherwise, if multiple data sets are present a multilinear regression will be performed yielding the constant A that minimize [MATH][GRK]chi[grk]^2 = (y - A[SUB]0[sub] x[SUB]0[sub] - A[SUB]1[sub] x[SUB]1[sub] - ... - A[SUB]N[sub] x[SUB]N[sub])^2[math] where now Y is the first data set in the input file and x[SUB]i[sub] the others. Do read the information at the option [TT]-time[tt]." },
 +        { "-luzar",   FALSE, etBOOL, {&bLuzar},
 +          "Do a Luzar and Chandler analysis on a correlation function and "
 +          "related as produced by [gmx-hbond]. When in addition the "
 +          "[TT]-xydy[tt] flag is given the second and fourth column will be "
 +          "interpreted as errors in c(t) and n(t)." },
 +        { "-temp",    FALSE, etREAL, {&temp},
 +          "Temperature for the Luzar hydrogen bonding kinetics analysis (K)" },
 +        { "-fitstart", FALSE, etREAL, {&fit_start},
 +          "Time (ps) from which to start fitting the correlation functions in order to obtain the forward and backward rate constants for HB breaking and formation" },
 +        { "-fitend", FALSE, etREAL, {&fit_end},
 +          "Time (ps) where to stop fitting the correlation functions in order to obtain the forward and backward rate constants for HB breaking and formation. Only with [TT]-gem[tt]" },
 +        { "-smooth", FALSE, etREAL, {&smooth_tail_start},
 +          "If this value is >= 0, the tail of the ACF will be smoothed by fitting it to an exponential function: [MATH]y = A [EXP]-x/[GRK]tau[grk][exp][math]" },
 +        { "-nbmin",   FALSE, etINT, {&nb_min},
 +          "HIDDENMinimum number of blocks for block averaging" },
 +        { "-resol", FALSE, etINT, {&resol},
 +          "HIDDENResolution for the block averaging, block size increases with"
 +          " a factor 2^(1/resol)" },
 +        { "-eeexpfit", FALSE, etBOOL, {&bEESEF},
 +          "HIDDENAlways use a single exponential fit for the error estimate" },
 +        { "-eenlc", FALSE, etBOOL, {&bEENLC},
 +          "HIDDENAllow a negative long-time correlation" },
 +        { "-eefitac", FALSE, etBOOL, {&bEeFitAc},
 +          "HIDDENAlso plot analytical block average using a autocorrelation fit" },
 +        { "-filter",  FALSE, etREAL, {&filtlen},
 +          "Print the high-frequency fluctuation after filtering with a cosine filter of this length" },
 +        { "-power", FALSE, etBOOL, {&bPower},
 +          "Fit data to: b t^a" },
 +        { "-subav", FALSE, etBOOL, {&bSubAv},
 +          "Subtract the average before autocorrelating" },
 +        { "-oneacf", FALSE, etBOOL, {&bAverCorr},
 +          "Calculate one ACF over all sets" },
 +        { "-nbalexp", FALSE, etINT, {&nBalExp},
 +          "HIDDENNumber of exponentials to fit to the ultrafast component" },
 +        { "-baltime", FALSE, etREAL, {&balTime},
 +          "HIDDENTime up to which the ballistic component will be fitted" },
 +/*     { "-gemnp", FALSE, etINT, {&nFitPoints}, */
 +/*       "HIDDENNumber of data points taken from the ACF to use for fitting to rev. gem. recomb. model."}, */
 +/*     { "-rcut", FALSE, etREAL, {&rcut}, */
 +/*       "Cut-off for hydrogen bonds in geminate algorithms" }, */
 +/*     { "-gemtype", FALSE, etENUM, {gemType}, */
 +/*       "What type of gminate recombination to use"}, */
 +/*     { "-D", FALSE, etREAL, {&diffusion}, */
 +/*       "The self diffusion coefficient which is used for the reversible geminate recombination model."} */
 +    };
 +#define NPA asize(pa)
 +
 +    FILE           *out, *out_fit;
 +    int             n, nlast, s, nset, i, j = 0;
 +    real          **val, *t, dt, tot, error;
 +    double         *av, *sig, cum1, cum2, cum3, cum4, db;
 +    const char     *acfile, *msdfile, *ccfile, *distfile, *avfile, *eefile, *balfile, *gemfile, *fitfile;
 +    output_env_t    oenv;
 +
 +    t_filenm        fnm[] = {
 +        { efXVG, "-f",    "graph",    ffREAD   },
 +        { efXVG, "-ac",   "autocorr", ffOPTWR  },
 +        { efXVG, "-msd",  "msd",      ffOPTWR  },
 +        { efXVG, "-cc",   "coscont",  ffOPTWR  },
 +        { efXVG, "-dist", "distr",    ffOPTWR  },
 +        { efXVG, "-av",   "average",  ffOPTWR  },
 +        { efXVG, "-ee",   "errest",   ffOPTWR  },
 +        { efXVG, "-bal",  "ballisitc", ffOPTWR  },
 +/*     { efXVG, "-gem",  "geminate", ffOPTWR  }, */
 +        { efLOG, "-g",    "fitlog",   ffOPTWR  }
 +    };
 +#define NFILE asize(fnm)
 +
 +    int      npargs;
 +    t_pargs *ppa;
 +
 +    npargs = asize(pa);
 +    ppa    = add_acf_pargs(&npargs, pa);
 +
 +    if (!parse_common_args(&argc, argv, PCA_CAN_VIEW,
 +                           NFILE, fnm, npargs, ppa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    acfile   = opt2fn_null("-ac", NFILE, fnm);
 +    msdfile  = opt2fn_null("-msd", NFILE, fnm);
 +    ccfile   = opt2fn_null("-cc", NFILE, fnm);
 +    distfile = opt2fn_null("-dist", NFILE, fnm);
 +    avfile   = opt2fn_null("-av", NFILE, fnm);
 +    eefile   = opt2fn_null("-ee", NFILE, fnm);
 +    balfile  = opt2fn_null("-bal", NFILE, fnm);
 +/*   gemfile  = opt2fn_null("-gem",NFILE,fnm); */
 +    /* When doing autocorrelation we don't want a fitlog for fitting
 +     * the function itself (not the acf) when the user did not ask for it.
 +     */
 +    if (opt2parg_bSet("-fitfn", npargs, ppa) && acfile == NULL)
 +    {
 +        fitfile  = opt2fn("-g", NFILE, fnm);
 +    }
 +    else
 +    {
 +        fitfile  = opt2fn_null("-g", NFILE, fnm);
 +    }
 +
 +    val = read_xvg_time(opt2fn("-f", NFILE, fnm), bHaveT,
 +                        opt2parg_bSet("-b", npargs, ppa), tb,
 +                        opt2parg_bSet("-e", npargs, ppa), te,
 +                        nsets_in, &nset, &n, &dt, &t);
 +    printf("Read %d sets of %d points, dt = %g\n\n", nset, n, dt);
 +
 +    if (bDer)
 +    {
 +        printf("Calculating the derivative as (f[i+%d]-f[i])/(%d*dt)\n\n",
 +               d, d);
 +        n -= d;
 +        for (s = 0; s < nset; s++)
 +        {
 +            for (i = 0; (i < n); i++)
 +            {
 +                val[s][i] = (val[s][i+d]-val[s][i])/(d*dt);
 +            }
 +        }
 +    }
 +
 +    if (bIntegrate)
 +    {
 +        real sum, stddev;
 +
 +        printf("Calculating the integral using the trapezium rule\n");
 +
 +        if (bXYdy)
 +        {
 +            sum = evaluate_integral(n, t, val[0], val[1], aver_start, &stddev);
 +            printf("Integral %10.3f +/- %10.5f\n", sum, stddev);
 +        }
 +        else
 +        {
 +            for (s = 0; s < nset; s++)
 +            {
 +                sum = evaluate_integral(n, t, val[s], NULL, aver_start, &stddev);
 +                printf("Integral %d  %10.5f  +/- %10.5f\n", s+1, sum, stddev);
 +            }
 +        }
 +    }
 +
 +    if (fitfile != NULL)
 +    {
 +        out_fit = gmx_ffopen(fitfile, "w");
 +        if (bXYdy && nset >= 2)
 +        {
 +            do_fit(out_fit, 0, TRUE, n, t, val, npargs, ppa, oenv);
 +        }
 +        else
 +        {
 +            for (s = 0; s < nset; s++)
 +            {
 +                do_fit(out_fit, s, FALSE, n, t, val, npargs, ppa, oenv);
 +            }
 +        }
 +        gmx_ffclose(out_fit);
 +    }
 +
 +    printf("                                      std. dev.    relative deviation of\n");
 +    printf("                       standard       ---------   cumulants from those of\n");
 +    printf("set      average       deviation      sqrt(n-1)   a Gaussian distribition\n");
 +    printf("                                                      cum. 3   cum. 4\n");
 +    snew(av, nset);
 +    snew(sig, nset);
 +    for (s = 0; (s < nset); s++)
 +    {
 +        cum1 = 0;
 +        cum2 = 0;
 +        cum3 = 0;
 +        cum4 = 0;
 +        for (i = 0; (i < n); i++)
 +        {
 +            cum1 += val[s][i];
 +        }
 +        cum1 /= n;
 +        for (i = 0; (i < n); i++)
 +        {
 +            db    = val[s][i]-cum1;
 +            cum2 += db*db;
 +            cum3 += db*db*db;
 +            cum4 += db*db*db*db;
 +        }
 +        cum2  /= n;
 +        cum3  /= n;
 +        cum4  /= n;
 +        av[s]  = cum1;
 +        sig[s] = sqrt(cum2);
 +        if (n > 1)
 +        {
 +            error = sqrt(cum2/(n-1));
 +        }
 +        else
 +        {
 +            error = 0;
 +        }
 +        printf("SS%d  %13.6e   %12.6e   %12.6e      %6.3f   %6.3f\n",
 +               s+1, av[s], sig[s], error,
 +               sig[s] ? cum3/(sig[s]*sig[s]*sig[s]*sqrt(8/M_PI)) : 0,
 +               sig[s] ? cum4/(sig[s]*sig[s]*sig[s]*sig[s]*3)-1 : 0);
 +    }
 +    printf("\n");
 +
 +    if (filtlen)
 +    {
 +        filter(filtlen, n, nset, val, dt);
 +    }
 +
 +    if (msdfile)
 +    {
 +        out = xvgropen(msdfile, "Mean square displacement",
 +                       "time", "MSD (nm\\S2\\N)", oenv);
 +        nlast = (int)(n*frac);
 +        for (s = 0; s < nset; s++)
 +        {
 +            for (j = 0; j <= nlast; j++)
 +            {
 +                if (j % 100 == 0)
 +                {
 +                    fprintf(stderr, "\r%d", j);
 +                }
 +                tot = 0;
 +                for (i = 0; i < n-j; i++)
 +                {
 +                    tot += sqr(val[s][i]-val[s][i+j]);
 +                }
 +                tot /= (real)(n-j);
 +                fprintf(out, " %g %8g\n", dt*j, tot);
 +            }
 +            if (s < nset-1)
 +            {
++                fprintf(out, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +            }
 +        }
 +        gmx_ffclose(out);
 +        fprintf(stderr, "\r%d, time=%g\n", j-1, (j-1)*dt);
 +    }
 +    if (ccfile)
 +    {
 +        plot_coscont(ccfile, n, nset, val, oenv);
 +    }
 +
 +    if (distfile)
 +    {
 +        histogram(distfile, binwidth, n, nset, val, oenv);
 +    }
 +    if (avfile)
 +    {
 +        average(avfile, nenum(avbar_opt), n, nset, val, t);
 +    }
 +    if (eefile)
 +    {
 +        estimate_error(eefile, nb_min, resol, n, nset, av, sig, val, dt,
 +                       bEeFitAc, bEESEF, bEENLC, oenv);
 +    }
 +    if (balfile)
 +    {
 +        do_ballistic(balfile, n, t, val, nset, balTime, nBalExp, oenv);
 +    }
 +/*   if (gemfile) */
 +/*       do_geminate(gemfile,n,t,val,nset,diffusion,rcut,balTime, */
 +/*                   nFitPoints, fit_start, fit_end, oenv); */
 +    if (bPower)
 +    {
 +        power_fit(n, nset, val, t);
 +    }
 +
 +    if (acfile != NULL)
 +    {
 +        if (bSubAv)
 +        {
 +            for (s = 0; s < nset; s++)
 +            {
 +                for (i = 0; i < n; i++)
 +                {
 +                    val[s][i] -= av[s];
 +                }
 +            }
 +        }
 +        do_autocorr(acfile, oenv, "Autocorrelation", n, nset, val, dt,
 +                    eacNormal, bAverCorr);
 +    }
 +
 +    if (bRegression)
 +    {
 +        regression_analysis(n, bXYdy, t, nset, val);
 +    }
 +
 +    if (bLuzar)
 +    {
 +        luzar_correl(n, t, nset, val, temp, bXYdy, fit_start, smooth_tail_start, oenv);
 +    }
 +
 +    view_all(oenv, NFILE, fnm);
 +
 +    return 0;
 +}
index 65bfad1cc58973aa04ece639ec9bfd9ca565ae55,0000000000000000000000000000000000000000..534f9153cd47d73ea42158b76963101f4c939c12
mode 100644,000000..100644
--- /dev/null
@@@ -1,475 -1,0 +1,478 @@@
-         fprintf(out, "@with g0\n");
-         fprintf(out, "@    world xmin -180\n");
-         fprintf(out, "@    world xmax  180\n");
-         fprintf(out, "@    world ymin 0\n");
-         fprintf(out, "@    world ymax %g\n", maxstat*1.05);
-         fprintf(out, "@    xaxis  tick major 60\n");
-         fprintf(out, "@    xaxis  tick minor 30\n");
-         fprintf(out, "@    yaxis  tick major 0.005\n");
-         fprintf(out, "@    yaxis  tick minor 0.0025\n");
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +#include <math.h>
 +#include <string.h>
 +
 +#include "sysstuff.h"
 +#include "physics.h"
 +#include "typedefs.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "copyrite.h"
 +#include "vec.h"
 +#include "index.h"
 +#include "macros.h"
 +#include "gmx_fatal.h"
 +#include "xvgr.h"
 +#include "gstat.h"
 +#include "gromacs/fileio/trnio.h"
 +#include "gmx_ana.h"
 +
 +
 +static void dump_dih_trn(int nframes, int nangles, real **dih, const char *fn,
 +                         real *time)
 +{
 +    int       i, j, k, l, m, na;
 +    t_fileio *trn;
 +    rvec     *x;
 +    matrix    box = {{2, 0, 0}, {0, 2, 0}, {0, 0, 2}};
 +
 +    na = (nangles*2);
 +    if ((na % 3) != 0)
 +    {
 +        na = 1+na/3;
 +    }
 +    else
 +    {
 +        na = na/3;
 +    }
 +    printf("There are %d dihedrals. Will fill %d atom positions with cos/sin\n",
 +           nangles, na);
 +    snew(x, na);
 +    trn = open_trn(fn, "w");
 +    for (i = 0; (i < nframes); i++)
 +    {
 +        k = l = 0;
 +        for (j = 0; (j < nangles); j++)
 +        {
 +            for (m = 0; (m < 2); m++)
 +            {
 +                x[k][l] = (m == 0) ? cos(dih[j][i]) : sin(dih[j][i]);
 +                l++;
 +                if (l == DIM)
 +                {
 +                    l = 0;
 +                    k++;
 +                }
 +            }
 +        }
 +        fwrite_trn(trn, i, time[i], 0, box, na, x, NULL, NULL);
 +    }
 +    close_trn(trn);
 +    sfree(x);
 +}
 +
 +int gmx_g_angle(int argc, char *argv[])
 +{
 +    static const char *desc[] = {
 +        "[THISMODULE] computes the angle distribution for a number of angles",
 +        "or dihedrals.[PAR]",
 +        "With option [TT]-ov[tt], you can plot the average angle of",
 +        "a group of angles as a function of time. With the [TT]-all[tt] option,",
 +        "the first graph is the average and the rest are the individual angles.[PAR]",
 +        "With the [TT]-of[tt] option, [THISMODULE] also calculates the fraction of trans",
 +        "dihedrals (only for dihedrals) as function of time, but this is",
 +        "probably only fun for a select few.[PAR]",
 +        "With option [TT]-oc[tt], a dihedral correlation function is calculated.[PAR]",
 +        "It should be noted that the index file must contain",
 +        "atom triplets for angles or atom quadruplets for dihedrals.",
 +        "If this is not the case, the program will crash.[PAR]",
 +        "With option [TT]-or[tt], a trajectory file is dumped containing cos and",
 +        "sin of selected dihedral angles, which subsequently can be used as",
 +        "input for a principal components analysis using [gmx-covar].[PAR]",
 +        "Option [TT]-ot[tt] plots when transitions occur between",
 +        "dihedral rotamers of multiplicity 3 and [TT]-oh[tt]",
 +        "records a histogram of the times between such transitions,",
 +        "assuming the input trajectory frames are equally spaced in time."
 +    };
 +    static const char *opt[]    = { NULL, "angle", "dihedral", "improper", "ryckaert-bellemans", NULL };
 +    static gmx_bool    bALL     = FALSE, bChandler = FALSE, bAverCorr = FALSE, bPBC = TRUE;
 +    static real        binwidth = 1;
 +    t_pargs            pa[]     = {
 +        { "-type", FALSE, etENUM, {opt},
 +          "Type of angle to analyse" },
 +        { "-all",    FALSE,  etBOOL, {&bALL},
 +          "Plot all angles separately in the averages file, in the order of appearance in the index file." },
 +        { "-binwidth", FALSE, etREAL, {&binwidth},
 +          "binwidth (degrees) for calculating the distribution" },
 +        { "-periodic", FALSE, etBOOL, {&bPBC},
 +          "Print dihedral angles modulo 360 degrees" },
 +        { "-chandler", FALSE,  etBOOL, {&bChandler},
 +          "Use Chandler correlation function (N[trans] = 1, N[gauche] = 0) rather than cosine correlation function. Trans is defined as phi < -60 or phi > 60." },
 +        { "-avercorr", FALSE,  etBOOL, {&bAverCorr},
 +          "Average the correlation functions for the individual angles/dihedrals" }
 +    };
 +    static const char *bugs[] = {
 +        "Counting transitions only works for dihedrals with multiplicity 3"
 +    };
 +
 +    FILE              *out;
 +    real               tmp, dt;
 +    int                status, isize;
 +    atom_id           *index;
 +    char              *grpname;
 +    real               maxang, Jc, S2, norm_fac, maxstat;
 +    unsigned long      mode;
 +    int                nframes, maxangstat, mult, *angstat;
 +    int                i, j, total, nangles, natoms, nat2, first, last, angind;
 +    gmx_bool           bAver, bRb, bPeriodic,
 +                       bFrac,                           /* calculate fraction too?  */
 +                       bTrans,                          /* worry about transtions too? */
 +                       bCorr;                           /* correlation function ? */
 +    real         t, aa, aver, aver2, aversig, fraction; /* fraction trans dihedrals */
 +    double       tfrac = 0;
 +    char         title[256];
 +    real       **dih = NULL;      /* mega array with all dih. angles at all times*/
 +    char         buf[80];
 +    real        *time, *trans_frac, *aver_angle;
 +    t_filenm     fnm[] = {
 +        { efTRX, "-f", NULL,  ffREAD  },
 +        { efNDX, NULL, "angle",  ffREAD  },
 +        { efXVG, "-od", "angdist",  ffWRITE },
 +        { efXVG, "-ov", "angaver",  ffOPTWR },
 +        { efXVG, "-of", "dihfrac",  ffOPTWR },
 +        { efXVG, "-ot", "dihtrans", ffOPTWR },
 +        { efXVG, "-oh", "trhisto",  ffOPTWR },
 +        { efXVG, "-oc", "dihcorr",  ffOPTWR },
 +        { efTRR, "-or", NULL,       ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +    int          npargs;
 +    t_pargs     *ppa;
 +    output_env_t oenv;
 +
 +    npargs = asize(pa);
 +    ppa    = add_acf_pargs(&npargs, pa);
 +    if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
 +                           NFILE, fnm, npargs, ppa, asize(desc), desc, asize(bugs), bugs,
 +                           &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    mult   = 4;
 +    maxang = 360.0;
 +    bRb    = FALSE;
 +    switch (opt[0][0])
 +    {
 +        case 'a':
 +            mult   = 3;
 +            maxang = 180.0;
 +            break;
 +        case 'd':
 +            break;
 +        case 'i':
 +            break;
 +        case 'r':
 +            bRb = TRUE;
 +            break;
 +    }
 +
 +    if (opt2bSet("-or", NFILE, fnm))
 +    {
 +        if (mult != 4)
 +        {
 +            gmx_fatal(FARGS, "Can not combine angles with trn dump");
 +        }
 +        else
 +        {
 +            please_cite(stdout, "Mu2005a");
 +        }
 +    }
 +
 +    /* Calculate bin size */
 +    maxangstat = (int)(maxang/binwidth+0.5);
 +    binwidth   = maxang/maxangstat;
 +
 +    rd_index(ftp2fn(efNDX, NFILE, fnm), 1, &isize, &index, &grpname);
 +    nangles = isize/mult;
 +    if ((isize % mult) != 0)
 +    {
 +        gmx_fatal(FARGS, "number of index elements not multiple of %d, "
 +                  "these can not be %s\n",
 +                  mult, (mult == 3) ? "angle triplets" : "dihedral quadruplets");
 +    }
 +
 +
 +    /* Check whether specific analysis has to be performed */
 +    bCorr  = opt2bSet("-oc", NFILE, fnm);
 +    bAver  = opt2bSet("-ov", NFILE, fnm);
 +    bTrans = opt2bSet("-ot", NFILE, fnm);
 +    bFrac  = opt2bSet("-of", NFILE, fnm);
 +    if (bTrans && opt[0][0] != 'd')
 +    {
 +        fprintf(stderr, "Option -ot should only accompany -type dihedral. Disabling -ot.\n");
 +        bTrans = FALSE;
 +    }
 +
 +    if (bChandler && !bCorr)
 +    {
 +        bCorr = TRUE;
 +    }
 +
 +    if (bFrac && !bRb)
 +    {
 +        fprintf(stderr, "Warning:"
 +                " calculating fractions as defined in this program\n"
 +                "makes sense for Ryckaert Bellemans dihs. only. Ignoring -of\n\n");
 +        bFrac = FALSE;
 +    }
 +
 +    if ( (bTrans || bFrac || bCorr) && mult == 3)
 +    {
 +        gmx_fatal(FARGS, "Can only do transition, fraction or correlation\n"
 +                  "on dihedrals. Select -d\n");
 +    }
 +
 +    /*
 +     * We need to know the nr of frames so we can allocate memory for an array
 +     * with all dihedral angles at all timesteps. Works for me.
 +     */
 +    if (bTrans || bCorr  || bALL || opt2bSet("-or", NFILE, fnm))
 +    {
 +        snew(dih, nangles);
 +    }
 +
 +    snew(angstat, maxangstat);
 +
 +    read_ang_dih(ftp2fn(efTRX, NFILE, fnm), (mult == 3),
 +                 bALL || bCorr || bTrans || opt2bSet("-or", NFILE, fnm),
 +                 bRb, bPBC, maxangstat, angstat,
 +                 &nframes, &time, isize, index, &trans_frac, &aver_angle, dih,
 +                 oenv);
 +
 +    dt = (time[nframes-1]-time[0])/(nframes-1);
 +
 +    if (bAver)
 +    {
 +        sprintf(title, "Average Angle: %s", grpname);
 +        out = xvgropen(opt2fn("-ov", NFILE, fnm),
 +                       title, "Time (ps)", "Angle (degrees)", oenv);
 +        for (i = 0; (i < nframes); i++)
 +        {
 +            fprintf(out, "%10.5f  %8.3f", time[i], aver_angle[i]*RAD2DEG);
 +            if (bALL)
 +            {
 +                for (j = 0; (j < nangles); j++)
 +                {
 +                    if (bPBC)
 +                    {
 +                        real dd = dih[j][i];
 +                        fprintf(out, "  %8.3f", atan2(sin(dd), cos(dd))*RAD2DEG);
 +                    }
 +                    else
 +                    {
 +                        fprintf(out, "  %8.3f", dih[j][i]*RAD2DEG);
 +                    }
 +                }
 +            }
 +            fprintf(out, "\n");
 +        }
 +        gmx_ffclose(out);
 +    }
 +    if (opt2bSet("-or", NFILE, fnm))
 +    {
 +        dump_dih_trn(nframes, nangles, dih, opt2fn("-or", NFILE, fnm), time);
 +    }
 +
 +    if (bFrac)
 +    {
 +        sprintf(title, "Trans fraction: %s", grpname);
 +        out = xvgropen(opt2fn("-of", NFILE, fnm),
 +                       title, "Time (ps)", "Fraction", oenv);
 +        tfrac = 0.0;
 +        for (i = 0; (i < nframes); i++)
 +        {
 +            fprintf(out, "%10.5f  %10.3f\n", time[i], trans_frac[i]);
 +            tfrac += trans_frac[i];
 +        }
 +        gmx_ffclose(out);
 +
 +        tfrac /= nframes;
 +        fprintf(stderr, "Average trans fraction: %g\n", tfrac);
 +    }
 +    sfree(trans_frac);
 +
 +    if (bTrans)
 +    {
 +        ana_dih_trans(opt2fn("-ot", NFILE, fnm), opt2fn("-oh", NFILE, fnm),
 +                      dih, nframes, nangles, grpname, time, bRb, oenv);
 +    }
 +
 +    if (bCorr)
 +    {
 +        /* Autocorrelation function */
 +        if (nframes < 2)
 +        {
 +            fprintf(stderr, "Not enough frames for correlation function\n");
 +        }
 +        else
 +        {
 +
 +            if (bChandler)
 +            {
 +                real     dval, sixty = DEG2RAD*60;
 +                gmx_bool bTest;
 +
 +                for (i = 0; (i < nangles); i++)
 +                {
 +                    for (j = 0; (j < nframes); j++)
 +                    {
 +                        dval = dih[i][j];
 +                        if (bRb)
 +                        {
 +                            bTest = (dval > -sixty) && (dval < sixty);
 +                        }
 +                        else
 +                        {
 +                            bTest = (dval < -sixty) || (dval > sixty);
 +                        }
 +                        if (bTest)
 +                        {
 +                            dih[i][j] = dval-tfrac;
 +                        }
 +                        else
 +                        {
 +                            dih[i][j] = -tfrac;
 +                        }
 +                    }
 +                }
 +            }
 +            if (bChandler)
 +            {
 +                mode = eacNormal;
 +            }
 +            else
 +            {
 +                mode = eacCos;
 +            }
 +            do_autocorr(opt2fn("-oc", NFILE, fnm), oenv,
 +                        "Dihedral Autocorrelation Function",
 +                        nframes, nangles, dih, dt, mode, bAverCorr);
 +        }
 +    }
 +
 +
 +    /* Determine the non-zero part of the distribution */
 +    for (first = 0; (first < maxangstat-1) && (angstat[first+1] == 0); first++)
 +    {
 +        ;
 +    }
 +    for (last = maxangstat-1; (last > 0) && (angstat[last-1] == 0); last--)
 +    {
 +        ;
 +    }
 +
 +    aver = aver2 = 0;
 +    for (i = 0; (i < nframes); i++)
 +    {
 +        aver  += RAD2DEG*aver_angle[i];
 +        aver2 += sqr(RAD2DEG*aver_angle[i]);
 +    }
 +    aver   /= (real) nframes;
 +    aver2  /= (real) nframes;
 +    aversig = sqrt(aver2-sqr(aver));
 +    printf("Found points in the range from %d to %d (max %d)\n",
 +           first, last, maxangstat);
 +    printf(" < angle >  = %g\n", aver);
 +    printf("< angle^2 > = %g\n", aver2);
 +    printf("Std. Dev.   = %g\n", aversig);
 +
 +    if (mult == 3)
 +    {
 +        sprintf(title, "Angle Distribution: %s", grpname);
 +    }
 +    else
 +    {
 +        sprintf(title, "Dihedral Distribution: %s", grpname);
 +
 +        calc_distribution_props(maxangstat, angstat, -180.0, 0, NULL, &S2);
 +        fprintf(stderr, "Order parameter S^2 = %g\n", S2);
 +    }
 +
 +    bPeriodic = (mult == 4) && (first == 0) && (last == maxangstat-1);
 +
 +    out = xvgropen(opt2fn("-od", NFILE, fnm), title, "Degrees", "", oenv);
 +    if (output_env_get_print_xvgr_codes(oenv))
 +    {
 +        fprintf(out, "@    subtitle \"average angle: %g\\So\\N\"\n", aver);
 +    }
 +    norm_fac = 1.0/(nangles*nframes*binwidth);
 +    if (bPeriodic)
 +    {
 +        maxstat = 0;
 +        for (i = first; (i <= last); i++)
 +        {
 +            maxstat = max(maxstat, angstat[i]*norm_fac);
 +        }
++        if (output_env_get_print_xvgr_codes(oenv))
++        {
++            fprintf(out, "@with g0\n");
++            fprintf(out, "@    world xmin -180\n");
++            fprintf(out, "@    world xmax  180\n");
++            fprintf(out, "@    world ymin 0\n");
++            fprintf(out, "@    world ymax %g\n", maxstat*1.05);
++            fprintf(out, "@    xaxis  tick major 60\n");
++            fprintf(out, "@    xaxis  tick minor 30\n");
++            fprintf(out, "@    yaxis  tick major 0.005\n");
++            fprintf(out, "@    yaxis  tick minor 0.0025\n");
++        }
 +    }
 +    for (i = first; (i <= last); i++)
 +    {
 +        fprintf(out, "%10g  %10f\n", i*binwidth+180.0-maxang, angstat[i]*norm_fac);
 +    }
 +    if (bPeriodic)
 +    {
 +        /* print first bin again as last one */
 +        fprintf(out, "%10g  %10f\n", 180.0, angstat[0]*norm_fac);
 +    }
 +
 +    gmx_ffclose(out);
 +
 +    do_view(oenv, opt2fn("-od", NFILE, fnm), "-nxy");
 +    if (bAver)
 +    {
 +        do_view(oenv, opt2fn("-ov", NFILE, fnm), "-nxy");
 +    }
 +
 +    return 0;
 +}
index 90362c04d07e4e940727ffd0d90bfa4dff2fe589,0000000000000000000000000000000000000000..93a5e02c33184d59b039ff088948046023d320b2
mode 100644,000000..100644
--- /dev/null
@@@ -1,1584 -1,0 +1,1595 @@@
-                 fprintf(fp, "@ with g0\n");
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +#include <stdio.h>
 +#include <math.h>
 +
 +#include "gromacs/fileio/confio.h"
 +#include "gromacs/fileio/pdbio.h"
 +#include "gmx_fatal.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gstat.h"
 +#include "macros.h"
 +#include "gromacs/math/utilities.h"
 +#include "physics.h"
 +#include "index.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include <string.h>
 +#include "sysstuff.h"
 +#include "txtdump.h"
 +#include "typedefs.h"
 +#include "vec.h"
 +#include "xvgr.h"
 +#include "gromacs/fileio/matio.h"
 +#include "gmx_ana.h"
 +
 +static gmx_bool bAllowed(real phi, real psi)
 +{
 +    static const char *map[] = {
 +        "1100000000000000001111111000000000001111111111111111111111111",
 +        "1100000000000000001111110000000000011111111111111111111111111",
 +        "1100000000000000001111110000000000011111111111111111111111111",
 +        "1100000000000000001111100000000000111111111111111111111111111",
 +        "1100000000000000001111100000000000111111111111111111111111111",
 +        "1100000000000000001111100000000001111111111111111111111111111",
 +        "1100000000000000001111100000000001111111111111111111111111111",
 +        "1100000000000000001111100000000011111111111111111111111111111",
 +        "1110000000000000001111110000000111111111111111111111111111111",
 +        "1110000000000000001111110000001111111111111111111111111111111",
 +        "1110000000000000001111111000011111111111111111111111111111111",
 +        "1110000000000000001111111100111111111111111111111111111111111",
 +        "1110000000000000001111111111111111111111111111111111111111111",
 +        "1110000000000000001111111111111111111111111111111111111111111",
 +        "1110000000000000001111111111111111111111111111111111111111111",
 +        "1110000000000000001111111111111111111111111111111111111111111",
 +        "1110000000000000001111111111111110011111111111111111111111111",
 +        "1110000000000000001111111111111100000111111111111111111111111",
 +        "1110000000000000001111111111111000000000001111111111111111111",
 +        "1100000000000000001111111111110000000000000011111111111111111",
 +        "1100000000000000001111111111100000000000000011111111111111111",
 +        "1000000000000000001111111111000000000000000001111111111111110",
 +        "0000000000000000001111111110000000000000000000111111111111100",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000111111111111000000000000000",
 +        "1100000000000000000000000000000001111111111111100000000000111",
 +        "1100000000000000000000000000000001111111111111110000000000111",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000",
 +        "0000000000000000000000000000000000000000000000000000000000000"
 +    };
 +#define NPP asize(map)
 +    int                x, y;
 +
 +#define INDEX(ppp) ((((int) (360+ppp*RAD2DEG)) % 360)/6)
 +    x = INDEX(phi);
 +    y = INDEX(psi);
 +#undef INDEX
 +    return (gmx_bool) map[x][y];
 +}
 +
 +atom_id *make_chi_ind(int nl, t_dlist dl[], int *ndih)
 +{
 +    atom_id *id;
 +    int      i, Xi, n;
 +
 +    /* There are nl residues with max edMax dihedrals with 4 atoms each */
 +    snew(id, nl*edMax*4);
 +
 +    n = 0;
 +    for (i = 0; (i < nl); i++)
 +    {
 +        /* Phi, fake the first one */
 +        dl[i].j0[edPhi] = n/4;
 +        if (dl[i].atm.minC >= 0)
 +        {
 +            id[n++] = dl[i].atm.minC;
 +        }
 +        else
 +        {
 +            id[n++] = dl[i].atm.H;
 +        }
 +        id[n++] = dl[i].atm.N;
 +        id[n++] = dl[i].atm.Cn[1];
 +        id[n++] = dl[i].atm.C;
 +    }
 +    for (i = 0; (i < nl); i++)
 +    {
 +        /* Psi, fake the last one */
 +        dl[i].j0[edPsi] = n/4;
 +        id[n++]         = dl[i].atm.N;
 +        id[n++]         = dl[i].atm.Cn[1];
 +        id[n++]         = dl[i].atm.C;
 +        if (i < (nl-1) )
 +        {
 +            id[n++] = dl[i+1].atm.N;
 +        }
 +        else
 +        {
 +            id[n++] = dl[i].atm.O;
 +        }
 +    }
 +    for (i = 1; (i < nl); i++)
 +    {
 +        /* Omega */
 +        if (has_dihedral(edOmega, &(dl[i])))
 +        {
 +            dl[i].j0[edOmega] = n/4;
 +            id[n++]           = dl[i].atm.minCalpha;
 +            id[n++]           = dl[i].atm.minC;
 +            id[n++]           = dl[i].atm.N;
 +            id[n++]           = dl[i].atm.Cn[1];
 +        }
 +    }
 +    for (Xi = 0; (Xi < MAXCHI); Xi++)
 +    {
 +        /* Chi# */
 +        for (i = 0; (i < nl); i++)
 +        {
 +            if (dl[i].atm.Cn[Xi+3] != -1)
 +            {
 +                dl[i].j0[edChi1+Xi] = n/4;
 +                id[n++]             = dl[i].atm.Cn[Xi];
 +                id[n++]             = dl[i].atm.Cn[Xi+1];
 +                id[n++]             = dl[i].atm.Cn[Xi+2];
 +                id[n++]             = dl[i].atm.Cn[Xi+3];
 +            }
 +        }
 +    }
 +    *ndih = n/4;
 +
 +    return id;
 +}
 +
 +int bin(real chi, int mult)
 +{
 +    mult = 3;
 +
 +    return (int) (chi*mult/360.0);
 +}
 +
 +
 +static void do_dihcorr(const char *fn, int nf, int ndih, real **dih, real dt,
 +                       int nlist, t_dlist dlist[], real time[], int maxchi,
 +                       gmx_bool bPhi, gmx_bool bPsi, gmx_bool bChi, gmx_bool bOmega,
 +                       const output_env_t oenv)
 +{
 +    char name1[256], name2[256];
 +    int  i, j, Xi;
 +
 +    do_autocorr(fn, oenv, "Dihedral Autocorrelation Function",
 +                nf, ndih, dih, dt, eacCos, FALSE);
 +    /* Dump em all */
 +    j = 0;
 +    for (i = 0; (i < nlist); i++)
 +    {
 +        if (bPhi)
 +        {
 +            print_one(oenv, "corrphi", dlist[i].name, "Phi ACF for", "C(t)", nf/2, time,
 +                      dih[j]);
 +        }
 +        j++;
 +    }
 +    for (i = 0; (i < nlist); i++)
 +    {
 +        if (bPsi)
 +        {
 +            print_one(oenv, "corrpsi", dlist[i].name, "Psi ACF for", "C(t)", nf/2, time,
 +                      dih[j]);
 +        }
 +        j++;
 +    }
 +    for (i = 0; (i < nlist); i++)
 +    {
 +        if (has_dihedral(edOmega, &dlist[i]))
 +        {
 +            if (bOmega)
 +            {
 +                print_one(oenv, "corromega", dlist[i].name, "Omega ACF for", "C(t)",
 +                          nf/2, time, dih[j]);
 +            }
 +            j++;
 +        }
 +    }
 +    for (Xi = 0; (Xi < maxchi); Xi++)
 +    {
 +        sprintf(name1, "corrchi%d", Xi+1);
 +        sprintf(name2, "Chi%d ACF for", Xi+1);
 +        for (i = 0; (i < nlist); i++)
 +        {
 +            if (dlist[i].atm.Cn[Xi+3] != -1)
 +            {
 +                if (bChi)
 +                {
 +                    print_one(oenv, name1, dlist[i].name, name2, "C(t)", nf/2, time, dih[j]);
 +                }
 +                j++;
 +            }
 +        }
 +    }
 +    fprintf(stderr, "\n");
 +}
 +
 +static void copy_dih_data(real in[], real out[], int nf, gmx_bool bLEAVE)
 +{
 +    /* if bLEAVE, do nothing to data in copying to out
 +     * otherwise multiply by 180/pi to convert rad to deg */
 +    int  i;
 +    real mult;
 +    if (bLEAVE)
 +    {
 +        mult = 1;
 +    }
 +    else
 +    {
 +        mult = (180.0/M_PI);
 +    }
 +    for (i = 0; (i < nf); i++)
 +    {
 +        out[i] = in[i]*mult;
 +    }
 +}
 +
 +static void dump_em_all(int nlist, t_dlist dlist[], int nf, real time[],
 +                        real **dih, int maxchi,
 +                        gmx_bool bPhi, gmx_bool bPsi, gmx_bool bChi, gmx_bool bOmega, gmx_bool bRAD,
 +                        const output_env_t oenv)
 +{
 +    char  name[256], titlestr[256], ystr[256];
 +    real *data;
 +    int   i, j, Xi;
 +
 +    snew(data, nf);
 +    if (bRAD)
 +    {
 +        strcpy(ystr, "Angle (rad)");
 +    }
 +    else
 +    {
 +        strcpy(ystr, "Angle (degrees)");
 +    }
 +
 +    /* Dump em all */
 +    j = 0;
 +    for (i = 0; (i < nlist); i++)
 +    {
 +        /* grs debug  printf("OK i %d j %d\n", i, j) ; */
 +        if (bPhi)
 +        {
 +            copy_dih_data(dih[j], data, nf, bRAD);
 +            print_one(oenv, "phi", dlist[i].name, "\\xf\\f{}", ystr, nf, time, data);
 +        }
 +        j++;
 +    }
 +    for (i = 0; (i < nlist); i++)
 +    {
 +        if (bPsi)
 +        {
 +            copy_dih_data(dih[j], data, nf, bRAD);
 +            print_one(oenv, "psi", dlist[i].name, "\\xy\\f{}", ystr, nf, time, data);
 +        }
 +        j++;
 +    }
 +    for (i = 0; (i < nlist); i++)
 +    {
 +        if (has_dihedral(edOmega, &(dlist[i])))
 +        {
 +            if (bOmega)
 +            {
 +                copy_dih_data(dih[j], data, nf, bRAD);
 +                print_one(oenv, "omega", dlist[i].name, "\\xw\\f{}", ystr, nf, time, data);
 +            }
 +            j++;
 +        }
 +    }
 +
 +    for (Xi = 0; (Xi < maxchi); Xi++)
 +    {
 +        for (i = 0; (i < nlist); i++)
 +        {
 +            if (dlist[i].atm.Cn[Xi+3] != -1)
 +            {
 +                if (bChi)
 +                {
 +                    sprintf(name, "chi%d", Xi+1);
 +                    sprintf(titlestr, "\\xc\\f{}\\s%d\\N", Xi+1);
 +                    copy_dih_data(dih[j], data, nf, bRAD);
 +                    print_one(oenv, name, dlist[i].name, titlestr, ystr, nf, time, data);
 +                }
 +                j++;
 +            }
 +        }
 +    }
 +    fprintf(stderr, "\n");
 +}
 +
 +static void reset_one(real dih[], int nf, real phase)
 +{
 +    int j;
 +
 +    for (j = 0; (j < nf); j++)
 +    {
 +        dih[j] += phase;
 +        while (dih[j] < -M_PI)
 +        {
 +            dih[j] += 2*M_PI;
 +        }
 +        while (dih[j] >= M_PI)
 +        {
 +            dih[j] -= 2*M_PI;
 +        }
 +    }
 +}
 +
 +static int reset_em_all(int nlist, t_dlist dlist[], int nf,
 +                        real **dih, int maxchi)
 +{
 +    int  i, j, Xi;
 +
 +    /* Reset em all */
 +    j = 0;
 +    /* Phi */
 +    for (i = 0; (i < nlist); i++)
 +    {
 +        if (dlist[i].atm.minC == -1)
 +        {
 +            reset_one(dih[j++], nf, M_PI);
 +        }
 +        else
 +        {
 +            reset_one(dih[j++], nf, 0);
 +        }
 +    }
 +    /* Psi */
 +    for (i = 0; (i < nlist-1); i++)
 +    {
 +        reset_one(dih[j++], nf, 0);
 +    }
 +    /* last Psi is faked from O */
 +    reset_one(dih[j++], nf, M_PI);
 +
 +    /* Omega */
 +    for (i = 0; (i < nlist); i++)
 +    {
 +        if (has_dihedral(edOmega, &dlist[i]))
 +        {
 +            reset_one(dih[j++], nf, 0);
 +        }
 +    }
 +    /* Chi 1 thru maxchi */
 +    for (Xi = 0; (Xi < maxchi); Xi++)
 +    {
 +        for (i = 0; (i < nlist); i++)
 +        {
 +            if (dlist[i].atm.Cn[Xi+3] != -1)
 +            {
 +                reset_one(dih[j], nf, 0);
 +                j++;
 +            }
 +        }
 +    }
 +    fprintf(stderr, "j after resetting (nr. active dihedrals) = %d\n", j);
 +    return j;
 +}
 +
 +static void histogramming(FILE *log, int nbin, gmx_residuetype_t rt,
 +                          int nf, int maxchi, real **dih,
 +                          int nlist, t_dlist dlist[],
 +                          atom_id index[],
 +                          gmx_bool bPhi, gmx_bool bPsi, gmx_bool bOmega, gmx_bool bChi,
 +                          gmx_bool bNormalize, gmx_bool bSSHisto, const char *ssdump,
 +                          real bfac_max, t_atoms *atoms,
 +                          gmx_bool bDo_jc, const char *fn,
 +                          const output_env_t oenv)
 +{
 +    /* also gets 3J couplings and order parameters S2 */
 +    t_karplus kkkphi[] = {
 +        { "J_NHa1",    6.51, -1.76,  1.6, -M_PI/3,   0.0,  0.0 },
 +        { "J_NHa2",    6.51, -1.76,  1.6,  M_PI/3,   0.0,  0.0 },
 +        { "J_HaC'",    4.0,   1.1,   0.1,  0.0,      0.0,  0.0 },
 +        { "J_NHCb",    4.7,  -1.5,  -0.2,  M_PI/3,   0.0,  0.0 },
 +        { "J_Ci-1Hai", 4.5,  -1.3,  -1.2,  2*M_PI/3, 0.0,  0.0 }
 +    };
 +    t_karplus kkkpsi[] = {
 +        { "J_HaN",   -0.88, -0.61, -0.27, M_PI/3,  0.0,  0.0 }
 +    };
 +    t_karplus kkkchi1[] = {
 +        { "JHaHb2",       9.5, -1.6, 1.8, -M_PI/3, 0,  0.0 },
 +        { "JHaHb3",       9.5, -1.6, 1.8, 0, 0,  0.0 }
 +    };
 +#define NKKKPHI asize(kkkphi)
 +#define NKKKPSI asize(kkkpsi)
 +#define NKKKCHI asize(kkkchi1)
 +#define NJC (NKKKPHI+NKKKPSI+NKKKCHI)
 +
 +    FILE       *fp, *ssfp[3] = {NULL, NULL, NULL};
 +    const char *sss[3] = { "sheet", "helix", "coil" };
 +    real        S2;
 +    real       *normhisto;
 +    real      **Jc, **Jcsig;
 +    int     ****his_aa_ss = NULL;
 +    int      ***his_aa, **his_aa1, *histmp;
 +    int         i, j, k, m, n, nn, Dih, nres, hindex, angle;
 +    gmx_bool    bBfac, bOccup;
 +    char        hisfile[256], hhisfile[256], sshisfile[256], title[256], *ss_str = NULL;
 +    char      **leg;
 +    const char *residue_name;
 +    int         rt_size;
 +
 +    rt_size = gmx_residuetype_get_size(rt);
 +    if (bSSHisto)
 +    {
 +        fp = gmx_ffopen(ssdump, "r");
 +        if (1 != fscanf(fp, "%d", &nres))
 +        {
 +            gmx_fatal(FARGS, "Error reading from file %s", ssdump);
 +        }
 +
 +        snew(ss_str, nres+1);
 +        if (1 != fscanf(fp, "%s", ss_str))
 +        {
 +            gmx_fatal(FARGS, "Error reading from file %s", ssdump);
 +        }
 +
 +        gmx_ffclose(fp);
 +        /* Four dimensional array... Very cool */
 +        snew(his_aa_ss, 3);
 +        for (i = 0; (i < 3); i++)
 +        {
 +            snew(his_aa_ss[i], rt_size+1);
 +            for (j = 0; (j <= rt_size); j++)
 +            {
 +                snew(his_aa_ss[i][j], edMax);
 +                for (Dih = 0; (Dih < edMax); Dih++)
 +                {
 +                    snew(his_aa_ss[i][j][Dih], nbin+1);
 +                }
 +            }
 +        }
 +    }
 +    snew(his_aa, edMax);
 +    for (Dih = 0; (Dih < edMax); Dih++)
 +    {
 +        snew(his_aa[Dih], rt_size+1);
 +        for (i = 0; (i <= rt_size); i++)
 +        {
 +            snew(his_aa[Dih][i], nbin+1);
 +        }
 +    }
 +    snew(histmp, nbin);
 +
 +    snew(Jc, nlist);
 +    snew(Jcsig, nlist);
 +    for (i = 0; (i < nlist); i++)
 +    {
 +        snew(Jc[i], NJC);
 +        snew(Jcsig[i], NJC);
 +    }
 +
 +    j = 0;
 +    n = 0;
 +    for (Dih = 0; (Dih < NONCHI+maxchi); Dih++)
 +    {
 +        for (i = 0; (i < nlist); i++)
 +        {
 +            if (((Dih  < edOmega) ) ||
 +                ((Dih == edOmega) && (has_dihedral(edOmega, &(dlist[i])))) ||
 +                ((Dih  > edOmega) && (dlist[i].atm.Cn[Dih-NONCHI+3] != -1)))
 +            {
 +                make_histo(log, nf, dih[j], nbin, histmp, -M_PI, M_PI);
 +
 +                if (bSSHisto)
 +                {
 +                    /* Assume there is only one structure, the first.
 +                     * Compute index in histogram.
 +                     */
 +                    /* Check the atoms to see whether their B-factors are low enough
 +                     * Check atoms to see their occupancy is 1.
 +                     */
 +                    bBfac = bOccup = TRUE;
 +                    for (nn = 0; (nn < 4); nn++, n++)
 +                    {
 +                        bBfac  = bBfac  && (atoms->pdbinfo[index[n]].bfac <= bfac_max);
 +                        bOccup = bOccup && (atoms->pdbinfo[index[n]].occup == 1);
 +                    }
 +                    if (bOccup && ((bfac_max <= 0) || ((bfac_max > 0) && bBfac)))
 +                    {
 +                        hindex = ((dih[j][0]+M_PI)*nbin)/(2*M_PI);
 +                        range_check(hindex, 0, nbin);
 +
 +                        /* Assign dihedral to either of the structure determined
 +                         * histograms
 +                         */
 +                        switch (ss_str[dlist[i].resnr])
 +                        {
 +                            case 'E':
 +                                his_aa_ss[0][dlist[i].index][Dih][hindex]++;
 +                                break;
 +                            case 'H':
 +                                his_aa_ss[1][dlist[i].index][Dih][hindex]++;
 +                                break;
 +                            default:
 +                                his_aa_ss[2][dlist[i].index][Dih][hindex]++;
 +                                break;
 +                        }
 +                    }
 +                    else if (debug)
 +                    {
 +                        fprintf(debug, "Res. %d has imcomplete occupancy or bfacs > %g\n",
 +                                dlist[i].resnr, bfac_max);
 +                    }
 +                }
 +                else
 +                {
 +                    n += 4;
 +                }
 +
 +                switch (Dih)
 +                {
 +                    case edPhi:
 +                        calc_distribution_props(nbin, histmp, -M_PI, NKKKPHI, kkkphi, &S2);
 +
 +                        for (m = 0; (m < NKKKPHI); m++)
 +                        {
 +                            Jc[i][m]    = kkkphi[m].Jc;
 +                            Jcsig[i][m] = kkkphi[m].Jcsig;
 +                        }
 +                        break;
 +                    case edPsi:
 +                        calc_distribution_props(nbin, histmp, -M_PI, NKKKPSI, kkkpsi, &S2);
 +
 +                        for (m = 0; (m < NKKKPSI); m++)
 +                        {
 +                            Jc[i][NKKKPHI+m]    = kkkpsi[m].Jc;
 +                            Jcsig[i][NKKKPHI+m] = kkkpsi[m].Jcsig;
 +                        }
 +                        break;
 +                    case edChi1:
 +                        calc_distribution_props(nbin, histmp, -M_PI, NKKKCHI, kkkchi1, &S2);
 +                        for (m = 0; (m < NKKKCHI); m++)
 +                        {
 +                            Jc[i][NKKKPHI+NKKKPSI+m]    = kkkchi1[m].Jc;
 +                            Jcsig[i][NKKKPHI+NKKKPSI+m] = kkkchi1[m].Jcsig;
 +                        }
 +                        break;
 +                    default: /* covers edOmega and higher Chis than Chi1 */
 +                        calc_distribution_props(nbin, histmp, -M_PI, 0, NULL, &S2);
 +                        break;
 +                }
 +                dlist[i].S2[Dih]        = S2;
 +
 +                /* Sum distribution per amino acid type as well */
 +                for (k = 0; (k < nbin); k++)
 +                {
 +                    his_aa[Dih][dlist[i].index][k] += histmp[k];
 +                    histmp[k] = 0;
 +                }
 +                j++;
 +            }
 +            else /* dihed not defined */
 +            {
 +                dlist[i].S2[Dih] = 0.0;
 +            }
 +        }
 +    }
 +    sfree(histmp);
 +
 +    /* Print out Jcouplings */
 +    fprintf(log, "\n *** J-Couplings from simulation (plus std. dev.) ***\n\n");
 +    fprintf(log, "Residue   ");
 +    for (i = 0; (i < NKKKPHI); i++)
 +    {
 +        fprintf(log, "%7s   SD", kkkphi[i].name);
 +    }
 +    for (i = 0; (i < NKKKPSI); i++)
 +    {
 +        fprintf(log, "%7s   SD", kkkpsi[i].name);
 +    }
 +    for (i = 0; (i < NKKKCHI); i++)
 +    {
 +        fprintf(log, "%7s   SD", kkkchi1[i].name);
 +    }
 +    fprintf(log, "\n");
 +    for (i = 0; (i < NJC+1); i++)
 +    {
 +        fprintf(log, "------------");
 +    }
 +    fprintf(log, "\n");
 +    for (i = 0; (i < nlist); i++)
 +    {
 +        fprintf(log, "%-10s", dlist[i].name);
 +        for (j = 0; (j < NJC); j++)
 +        {
 +            fprintf(log, "  %5.2f %4.2f", Jc[i][j], Jcsig[i][j]);
 +        }
 +        fprintf(log, "\n");
 +    }
 +    fprintf(log, "\n");
 +
 +    /* and to -jc file... */
 +    if (bDo_jc)
 +    {
 +        fp = xvgropen(fn, "\\S3\\NJ-Couplings from Karplus Equation", "Residue",
 +                      "Coupling", oenv);
 +        snew(leg, NJC);
 +        for (i = 0; (i < NKKKPHI); i++)
 +        {
 +            leg[i] = strdup(kkkphi[i].name);
 +        }
 +        for (i = 0; (i < NKKKPSI); i++)
 +        {
 +            leg[i+NKKKPHI] = strdup(kkkpsi[i].name);
 +        }
 +        for (i = 0; (i < NKKKCHI); i++)
 +        {
 +            leg[i+NKKKPHI+NKKKPSI] = strdup(kkkchi1[i].name);
 +        }
 +        xvgr_legend(fp, NJC, (const char**)leg, oenv);
 +        fprintf(fp, "%5s ", "#Res.");
 +        for (i = 0; (i < NJC); i++)
 +        {
 +            fprintf(fp, "%10s ", leg[i]);
 +        }
 +        fprintf(fp, "\n");
 +        for (i = 0; (i < nlist); i++)
 +        {
 +            fprintf(fp, "%5d ", dlist[i].resnr);
 +            for (j = 0; (j < NJC); j++)
 +            {
 +                fprintf(fp, "  %8.3f", Jc[i][j]);
 +            }
 +            fprintf(fp, "\n");
 +        }
 +        gmx_ffclose(fp);
 +        for (i = 0; (i < NJC); i++)
 +        {
 +            sfree(leg[i]);
 +        }
 +    }
 +    /* finished -jc stuff */
 +
 +    snew(normhisto, nbin);
 +    for (i = 0; (i < rt_size); i++)
 +    {
 +        for (Dih = 0; (Dih < edMax); Dih++)
 +        {
 +            /* First check whether something is in there */
 +            for (j = 0; (j < nbin); j++)
 +            {
 +                if (his_aa[Dih][i][j] != 0)
 +                {
 +                    break;
 +                }
 +            }
 +            if ((j < nbin) &&
 +                ((bPhi && (Dih == edPhi)) ||
 +                 (bPsi && (Dih == edPsi)) ||
 +                 (bOmega && (Dih == edOmega)) ||
 +                 (bChi && (Dih >= edChi1))))
 +            {
 +                if (bNormalize)
 +                {
 +                    normalize_histo(nbin, his_aa[Dih][i], (360.0/nbin), normhisto);
 +                }
 +
 +                residue_name = gmx_residuetype_get_name(rt, i);
 +                switch (Dih)
 +                {
 +                    case edPhi:
 +                        sprintf(hisfile, "histo-phi%s", residue_name);
 +                        sprintf(title, "\\xf\\f{} Distribution for %s", residue_name);
 +                        break;
 +                    case edPsi:
 +                        sprintf(hisfile, "histo-psi%s", residue_name);
 +                        sprintf(title, "\\xy\\f{} Distribution for %s", residue_name);
 +                        break;
 +                    case edOmega:
 +                        sprintf(hisfile, "histo-omega%s", residue_name);
 +                        sprintf(title, "\\xw\\f{} Distribution for %s", residue_name);
 +                        break;
 +                    default:
 +                        sprintf(hisfile, "histo-chi%d%s", Dih-NONCHI+1, residue_name);
 +                        sprintf(title, "\\xc\\f{}\\s%d\\N Distribution for %s",
 +                                Dih-NONCHI+1, residue_name);
 +                }
 +                strcpy(hhisfile, hisfile);
 +                strcat(hhisfile, ".xvg");
 +                fp = xvgropen(hhisfile, title, "Degrees", "", oenv);
-                 fprintf(fp, "# this effort to set graph size fails unless you run with -autoscale none or -autoscale y flags\n");
-                 fprintf(fp, "@ xaxis tick on\n");
-                 fprintf(fp, "@ xaxis tick major 90\n");
-                 fprintf(fp, "@ xaxis tick minor 30\n");
-                 fprintf(fp, "@ xaxis ticklabel prec 0\n");
-                 fprintf(fp, "@ yaxis tick off\n");
-                 fprintf(fp, "@ yaxis ticklabel off\n");
-                 fprintf(fp, "@ type xy\n");
++                if (output_env_get_print_xvgr_codes(oenv))
++                {
++                    fprintf(fp, "@ with g0\n");
++                }
 +                xvgr_world(fp, -180, 0, 180, 0.1, oenv);
-                 fprintf(fp, "&\n");
++                if (output_env_get_print_xvgr_codes(oenv))
++                {
++                    fprintf(fp, "# this effort to set graph size fails unless you run with -autoscale none or -autoscale y flags\n");
++                    fprintf(fp, "@ xaxis tick on\n");
++                    fprintf(fp, "@ xaxis tick major 90\n");
++                    fprintf(fp, "@ xaxis tick minor 30\n");
++                    fprintf(fp, "@ xaxis ticklabel prec 0\n");
++                    fprintf(fp, "@ yaxis tick off\n");
++                    fprintf(fp, "@ yaxis ticklabel off\n");
++                    fprintf(fp, "@ type xy\n");
++                }
 +                if (bSSHisto)
 +                {
 +                    for (k = 0; (k < 3); k++)
 +                    {
 +                        sprintf(sshisfile, "%s-%s.xvg", hisfile, sss[k]);
 +                        ssfp[k] = gmx_ffopen(sshisfile, "w");
 +                    }
 +                }
 +                for (j = 0; (j < nbin); j++)
 +                {
 +                    angle = -180 + (360/nbin)*j;
 +                    if (bNormalize)
 +                    {
 +                        fprintf(fp, "%5d  %10g\n", angle, normhisto[j]);
 +                    }
 +                    else
 +                    {
 +                        fprintf(fp, "%5d  %10d\n", angle, his_aa[Dih][i][j]);
 +                    }
 +                    if (bSSHisto)
 +                    {
 +                        for (k = 0; (k < 3); k++)
 +                        {
 +                            fprintf(ssfp[k], "%5d  %10d\n", angle,
 +                                    his_aa_ss[k][i][Dih][j]);
 +                        }
 +                    }
 +                }
-                         fprintf(ssfp[k], "&\n");
++                fprintf(fp, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +                gmx_ffclose(fp);
 +                if (bSSHisto)
 +                {
 +                    for (k = 0; (k < 3); k++)
 +                    {
-     fprintf(fp, "@ with g0\n");
++                        fprintf(ssfp[k], "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +                        gmx_ffclose(ssfp[k]);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    sfree(normhisto);
 +
 +    if (bSSHisto)
 +    {
 +        /* Four dimensional array... Very cool */
 +        for (i = 0; (i < 3); i++)
 +        {
 +            for (j = 0; (j <= rt_size); j++)
 +            {
 +                for (Dih = 0; (Dih < edMax); Dih++)
 +                {
 +                    sfree(his_aa_ss[i][j][Dih]);
 +                }
 +                sfree(his_aa_ss[i][j]);
 +            }
 +            sfree(his_aa_ss[i]);
 +        }
 +        sfree(his_aa_ss);
 +        sfree(ss_str);
 +    }
 +}
 +
 +static FILE *rama_file(const char *fn, const char *title, const char *xaxis,
 +                       const char *yaxis, const output_env_t oenv)
 +{
 +    FILE *fp;
 +
 +    fp = xvgropen(fn, title, xaxis, yaxis, oenv);
-     fprintf(fp, "@ xaxis tick on\n");
-     fprintf(fp, "@ xaxis tick major 90\n");
-     fprintf(fp, "@ xaxis tick minor 30\n");
-     fprintf(fp, "@ xaxis ticklabel prec 0\n");
-     fprintf(fp, "@ yaxis tick on\n");
-     fprintf(fp, "@ yaxis tick major 90\n");
-     fprintf(fp, "@ yaxis tick minor 30\n");
-     fprintf(fp, "@ yaxis ticklabel prec 0\n");
-     fprintf(fp, "@    s0 type xy\n");
-     fprintf(fp, "@    s0 symbol 2\n");
-     fprintf(fp, "@    s0 symbol size 0.410000\n");
-     fprintf(fp, "@    s0 symbol fill 1\n");
-     fprintf(fp, "@    s0 symbol color 1\n");
-     fprintf(fp, "@    s0 symbol linewidth 1\n");
-     fprintf(fp, "@    s0 symbol linestyle 1\n");
-     fprintf(fp, "@    s0 symbol center false\n");
-     fprintf(fp, "@    s0 symbol char 0\n");
-     fprintf(fp, "@    s0 skip 0\n");
-     fprintf(fp, "@    s0 linestyle 0\n");
-     fprintf(fp, "@    s0 linewidth 1\n");
-     fprintf(fp, "@ type xy\n");
++    if (output_env_get_print_xvgr_codes(oenv))
++    {
++        fprintf(fp, "@ with g0\n");
++    }
 +    xvgr_world(fp, -180, -180, 180, 180, oenv);
++    if (output_env_get_print_xvgr_codes(oenv))
++    {
++        fprintf(fp, "@ xaxis tick on\n");
++        fprintf(fp, "@ xaxis tick major 90\n");
++        fprintf(fp, "@ xaxis tick minor 30\n");
++        fprintf(fp, "@ xaxis ticklabel prec 0\n");
++        fprintf(fp, "@ yaxis tick on\n");
++        fprintf(fp, "@ yaxis tick major 90\n");
++        fprintf(fp, "@ yaxis tick minor 30\n");
++        fprintf(fp, "@ yaxis ticklabel prec 0\n");
++        fprintf(fp, "@    s0 type xy\n");
++        fprintf(fp, "@    s0 symbol 2\n");
++        fprintf(fp, "@    s0 symbol size 0.410000\n");
++        fprintf(fp, "@    s0 symbol fill 1\n");
++        fprintf(fp, "@    s0 symbol color 1\n");
++        fprintf(fp, "@    s0 symbol linewidth 1\n");
++        fprintf(fp, "@    s0 symbol linestyle 1\n");
++        fprintf(fp, "@    s0 symbol center false\n");
++        fprintf(fp, "@    s0 symbol char 0\n");
++        fprintf(fp, "@    s0 skip 0\n");
++        fprintf(fp, "@    s0 linestyle 0\n");
++        fprintf(fp, "@    s0 linewidth 1\n");
++        fprintf(fp, "@ type xy\n");
++    }
 +    return fp;
 +}
 +
 +static void do_rama(int nf, int nlist, t_dlist dlist[], real **dih,
 +                    gmx_bool bViol, gmx_bool bRamOmega, const output_env_t oenv)
 +{
 +    FILE    *fp, *gp = NULL;
 +    gmx_bool bOm;
 +    char     fn[256];
 +    int      i, j, k, Xi1, Xi2, Phi, Psi, Om = 0, nlevels;
 +#define NMAT 120
 +    real   **mat  = NULL, phi, psi, omega, axis[NMAT], lo, hi;
 +    t_rgb    rlo  = { 1.0, 0.0, 0.0 };
 +    t_rgb    rmid = { 1.0, 1.0, 1.0 };
 +    t_rgb    rhi  = { 0.0, 0.0, 1.0 };
 +
 +    for (i = 0; (i < nlist); i++)
 +    {
 +        if ((has_dihedral(edPhi, &(dlist[i]))) &&
 +            (has_dihedral(edPsi, &(dlist[i]))))
 +        {
 +            sprintf(fn, "ramaPhiPsi%s.xvg", dlist[i].name);
 +            fp = rama_file(fn, "Ramachandran Plot",
 +                           "\\8f\\4 (deg)", "\\8y\\4 (deg)", oenv);
 +            bOm = bRamOmega && has_dihedral(edOmega, &(dlist[i]));
 +            if (bOm)
 +            {
 +                Om = dlist[i].j0[edOmega];
 +                snew(mat, NMAT);
 +                for (j = 0; (j < NMAT); j++)
 +                {
 +                    snew(mat[j], NMAT);
 +                    axis[j] = -180+(360*j)/NMAT;
 +                }
 +            }
 +            if (bViol)
 +            {
 +                sprintf(fn, "violPhiPsi%s.xvg", dlist[i].name);
 +                gp = gmx_ffopen(fn, "w");
 +            }
 +            Phi = dlist[i].j0[edPhi];
 +            Psi = dlist[i].j0[edPsi];
 +            for (j = 0; (j < nf); j++)
 +            {
 +                phi = RAD2DEG*dih[Phi][j];
 +                psi = RAD2DEG*dih[Psi][j];
 +                fprintf(fp, "%10g  %10g\n", phi, psi);
 +                if (bViol)
 +                {
 +                    fprintf(gp, "%d\n", !bAllowed(dih[Phi][j], RAD2DEG*dih[Psi][j]));
 +                }
 +                if (bOm)
 +                {
 +                    omega = RAD2DEG*dih[Om][j];
 +                    mat[(int)((phi*NMAT)/360)+NMAT/2][(int)((psi*NMAT)/360)+NMAT/2]
 +                        += omega;
 +                }
 +            }
 +            if (bViol)
 +            {
 +                gmx_ffclose(gp);
 +            }
 +            gmx_ffclose(fp);
 +            if (bOm)
 +            {
 +                sprintf(fn, "ramomega%s.xpm", dlist[i].name);
 +                fp = gmx_ffopen(fn, "w");
 +                lo = hi = 0;
 +                for (j = 0; (j < NMAT); j++)
 +                {
 +                    for (k = 0; (k < NMAT); k++)
 +                    {
 +                        mat[j][k] /= nf;
 +                        lo         = min(mat[j][k], lo);
 +                        hi         = max(mat[j][k], hi);
 +                    }
 +                }
 +                /* Symmetrise */
 +                if (fabs(lo) > fabs(hi))
 +                {
 +                    hi = -lo;
 +                }
 +                else
 +                {
 +                    lo = -hi;
 +                }
 +                /* Add 180 */
 +                for (j = 0; (j < NMAT); j++)
 +                {
 +                    for (k = 0; (k < NMAT); k++)
 +                    {
 +                        mat[j][k] += 180;
 +                    }
 +                }
 +                lo     += 180;
 +                hi     += 180;
 +                nlevels = 20;
 +                write_xpm3(fp, 0, "Omega/Ramachandran Plot", "Deg", "Phi", "Psi",
 +                           NMAT, NMAT, axis, axis, mat, lo, 180.0, hi, rlo, rmid, rhi, &nlevels);
 +                gmx_ffclose(fp);
 +                for (j = 0; (j < NMAT); j++)
 +                {
 +                    sfree(mat[j]);
 +                }
 +                sfree(mat);
 +            }
 +        }
 +        if ((has_dihedral(edChi1, &(dlist[i]))) &&
 +            (has_dihedral(edChi2, &(dlist[i]))))
 +        {
 +            sprintf(fn, "ramaX1X2%s.xvg", dlist[i].name);
 +            fp = rama_file(fn, "\\8c\\4\\s1\\N-\\8c\\4\\s2\\N Ramachandran Plot",
 +                           "\\8c\\4\\s1\\N (deg)", "\\8c\\4\\s2\\N (deg)", oenv);
 +            Xi1 = dlist[i].j0[edChi1];
 +            Xi2 = dlist[i].j0[edChi2];
 +            for (j = 0; (j < nf); j++)
 +            {
 +                fprintf(fp, "%10g  %10g\n", RAD2DEG*dih[Xi1][j], RAD2DEG*dih[Xi2][j]);
 +            }
 +            gmx_ffclose(fp);
 +        }
 +        else
 +        {
 +            fprintf(stderr, "No chi1 & chi2 angle for %s\n", dlist[i].name);
 +        }
 +    }
 +}
 +
 +
 +static void print_transitions(const char *fn, int maxchi, int nlist,
 +                              t_dlist dlist[], real dt,
 +                              const output_env_t oenv)
 +{
 +    /* based on order_params below */
 +    FILE *fp;
 +    int   nh[edMax];
 +    int   i, Dih, Xi;
 +
 +    /*  must correspond with enum in pp2shift.h:38 */
 +    char *leg[edMax];
 +#define NLEG asize(leg)
 +
 +    leg[0] = strdup("Phi");
 +    leg[1] = strdup("Psi");
 +    leg[2] = strdup("Omega");
 +    leg[3] = strdup("Chi1");
 +    leg[4] = strdup("Chi2");
 +    leg[5] = strdup("Chi3");
 +    leg[6] = strdup("Chi4");
 +    leg[7] = strdup("Chi5");
 +    leg[8] = strdup("Chi6");
 +
 +    /* Print order parameters */
 +    fp = xvgropen(fn, "Dihedral Rotamer Transitions", "Residue", "Transitions/ns",
 +                  oenv);
 +    xvgr_legend(fp, NONCHI+maxchi, (const char**)leg, oenv);
 +
 +    for (Dih = 0; (Dih < edMax); Dih++)
 +    {
 +        nh[Dih] = 0;
 +    }
 +
 +    fprintf(fp, "%5s ", "#Res.");
 +    fprintf(fp, "%10s %10s %10s ", leg[edPhi], leg[edPsi], leg[edOmega]);
 +    for (Xi = 0; Xi < maxchi; Xi++)
 +    {
 +        fprintf(fp, "%10s ", leg[NONCHI+Xi]);
 +    }
 +    fprintf(fp, "\n");
 +
 +    for (i = 0; (i < nlist); i++)
 +    {
 +        fprintf(fp, "%5d ", dlist[i].resnr);
 +        for (Dih = 0; (Dih < NONCHI+maxchi); Dih++)
 +        {
 +            fprintf(fp, "%10.3f ", dlist[i].ntr[Dih]/dt);
 +        }
 +        /* fprintf(fp,"%12s\n",dlist[i].name);  this confuses xmgrace */
 +        fprintf(fp, "\n");
 +    }
 +    gmx_ffclose(fp);
 +}
 +
 +static void order_params(FILE *log,
 +                         const char *fn, int maxchi, int nlist, t_dlist dlist[],
 +                         const char *pdbfn, real bfac_init,
 +                         t_atoms *atoms, rvec x[], int ePBC, matrix box,
 +                         gmx_bool bPhi, gmx_bool bPsi, gmx_bool bChi, const output_env_t oenv)
 +{
 +    FILE *fp;
 +    int   nh[edMax];
 +    int   i, Dih, Xi;
 +    real  S2Max, S2Min;
 +
 +    /* except for S2Min/Max, must correspond with enum in pp2shift.h:38 */
 +    const char *const_leg[2+edMax] = {
 +        "S2Min", "S2Max", "Phi", "Psi", "Omega",
 +        "Chi1", "Chi2", "Chi3", "Chi4", "Chi5",
 +        "Chi6"
 +    };
 +#define NLEG asize(leg)
 +
 +    char *leg[2+edMax];
 +
 +    for (i = 0; i < NLEG; i++)
 +    {
 +        leg[i] = strdup(const_leg[i]);
 +    }
 +
 +    /* Print order parameters */
 +    fp = xvgropen(fn, "Dihedral Order Parameters", "Residue", "S2", oenv);
 +    xvgr_legend(fp, 2+NONCHI+maxchi, const_leg, oenv);
 +
 +    for (Dih = 0; (Dih < edMax); Dih++)
 +    {
 +        nh[Dih] = 0;
 +    }
 +
 +    fprintf(fp, "%5s ", "#Res.");
 +    fprintf(fp, "%10s %10s ", leg[0], leg[1]);
 +    fprintf(fp, "%10s %10s %10s ", leg[2+edPhi], leg[2+edPsi], leg[2+edOmega]);
 +    for (Xi = 0; Xi < maxchi; Xi++)
 +    {
 +        fprintf(fp, "%10s ", leg[2+NONCHI+Xi]);
 +    }
 +    fprintf(fp, "\n");
 +
 +    for (i = 0; (i < nlist); i++)
 +    {
 +        S2Max = -10;
 +        S2Min = 10;
 +        for (Dih = 0; (Dih < NONCHI+maxchi); Dih++)
 +        {
 +            if (dlist[i].S2[Dih] != 0)
 +            {
 +                if (dlist[i].S2[Dih] > S2Max)
 +                {
 +                    S2Max = dlist[i].S2[Dih];
 +                }
 +                if (dlist[i].S2[Dih] < S2Min)
 +                {
 +                    S2Min = dlist[i].S2[Dih];
 +                }
 +            }
 +            if (dlist[i].S2[Dih] > 0.8)
 +            {
 +                nh[Dih]++;
 +            }
 +        }
 +        fprintf(fp, "%5d ", dlist[i].resnr);
 +        fprintf(fp, "%10.3f %10.3f ", S2Min, S2Max);
 +        for (Dih = 0; (Dih < NONCHI+maxchi); Dih++)
 +        {
 +            fprintf(fp, "%10.3f ", dlist[i].S2[Dih]);
 +        }
 +        fprintf(fp, "\n");
 +        /* fprintf(fp,"%12s\n",dlist[i].name);  this confuses xmgrace */
 +    }
 +    gmx_ffclose(fp);
 +
 +    if (NULL != pdbfn)
 +    {
 +        real x0, y0, z0;
 +
 +        if (NULL == atoms->pdbinfo)
 +        {
 +            snew(atoms->pdbinfo, atoms->nr);
 +        }
 +        for (i = 0; (i < atoms->nr); i++)
 +        {
 +            atoms->pdbinfo[i].bfac = bfac_init;
 +        }
 +
 +        for (i = 0; (i < nlist); i++)
 +        {
 +            atoms->pdbinfo[dlist[i].atm.N].bfac = -dlist[i].S2[0]; /* Phi */
 +            atoms->pdbinfo[dlist[i].atm.H].bfac = -dlist[i].S2[0]; /* Phi */
 +            atoms->pdbinfo[dlist[i].atm.C].bfac = -dlist[i].S2[1]; /* Psi */
 +            atoms->pdbinfo[dlist[i].atm.O].bfac = -dlist[i].S2[1]; /* Psi */
 +            for (Xi = 0; (Xi < maxchi); Xi++)                      /* Chi's */
 +            {
 +                if (dlist[i].atm.Cn[Xi+3] != -1)
 +                {
 +                    atoms->pdbinfo[dlist[i].atm.Cn[Xi+1]].bfac = -dlist[i].S2[NONCHI+Xi];
 +                }
 +            }
 +        }
 +
 +        fp = gmx_ffopen(pdbfn, "w");
 +        fprintf(fp, "REMARK generated by g_chi\n");
 +        fprintf(fp, "REMARK "
 +                "B-factor field contains negative of dihedral order parameters\n");
 +        write_pdbfile(fp, NULL, atoms, x, ePBC, box, ' ', 0, NULL, TRUE);
 +        x0 = y0 = z0 = 1000.0;
 +        for (i = 0; (i < atoms->nr); i++)
 +        {
 +            x0 = min(x0, x[i][XX]);
 +            y0 = min(y0, x[i][YY]);
 +            z0 = min(z0, x[i][ZZ]);
 +        }
 +        x0 *= 10.0; /* nm -> angstrom */
 +        y0 *= 10.0; /* nm -> angstrom */
 +        z0 *= 10.0; /* nm -> angstrom */
 +        for (i = 0; (i < 10); i++)
 +        {
 +            gmx_fprintf_pdb_atomline(fp, epdbATOM, atoms->nr+1+i, "CA", ' ', "LEG", ' ', atoms->nres+1, ' ',
 +                                     x0, y0, z0+(1.2*i), 0.0, -0.1*i, "");
 +        }
 +        gmx_ffclose(fp);
 +    }
 +
 +    fprintf(log, "Dihedrals with S2 > 0.8\n");
 +    fprintf(log, "Dihedral: ");
 +    if (bPhi)
 +    {
 +        fprintf(log, " Phi  ");
 +    }
 +    if (bPsi)
 +    {
 +        fprintf(log, " Psi ");
 +    }
 +    if (bChi)
 +    {
 +        for (Xi = 0; (Xi < maxchi); Xi++)
 +        {
 +            fprintf(log, " %s ", leg[2+NONCHI+Xi]);
 +        }
 +    }
 +    fprintf(log, "\nNumber:   ");
 +    if (bPhi)
 +    {
 +        fprintf(log, "%4d  ", nh[0]);
 +    }
 +    if (bPsi)
 +    {
 +        fprintf(log, "%4d  ", nh[1]);
 +    }
 +    if (bChi)
 +    {
 +        for (Xi = 0; (Xi < maxchi); Xi++)
 +        {
 +            fprintf(log, "%4d  ", nh[NONCHI+Xi]);
 +        }
 +    }
 +    fprintf(log, "\n");
 +
 +    for (i = 0; i < NLEG; i++)
 +    {
 +        sfree(leg[i]);
 +    }
 +
 +}
 +
 +int gmx_chi(int argc, char *argv[])
 +{
 +    const char *desc[] = {
 +        "[THISMODULE] computes [GRK]phi[grk], [GRK]psi[grk], [GRK]omega[grk],",
 +        "and [GRK]chi[grk] dihedrals for all your",
 +        "amino acid backbone and sidechains.",
 +        "It can compute dihedral angle as a function of time, and as",
 +        "histogram distributions.",
 +        "The distributions [TT](histo-(dihedral)(RESIDUE).xvg[tt]) are cumulative over all residues of each type.[PAR]",
 +        "If option [TT]-corr[tt] is given, the program will",
 +        "calculate dihedral autocorrelation functions. The function used",
 +        "is C(t) = [CHEVRON][COS][GRK]chi[grk]([GRK]tau[grk])[cos] [COS][GRK]chi[grk]([GRK]tau[grk]+t)[cos][chevron]. The use of cosines",
 +        "rather than angles themselves, resolves the problem of periodicity.",
 +        "(Van der Spoel & Berendsen (1997), Biophys. J. 72, 2032-2041).",
 +        "Separate files for each dihedral of each residue",
 +        "[TT](corr(dihedral)(RESIDUE)(nresnr).xvg[tt]) are output, as well as a",
 +        "file containing the information for all residues (argument of [TT]-corr[tt]).[PAR]",
 +        "With option [TT]-all[tt], the angles themselves as a function of time for",
 +        "each residue are printed to separate files [TT](dihedral)(RESIDUE)(nresnr).xvg[tt].",
 +        "These can be in radians or degrees.[PAR]",
 +        "A log file (argument [TT]-g[tt]) is also written. This contains [BR]",
 +        "(a) information about the number of residues of each type.[BR]",
 +        "(b) The NMR ^3J coupling constants from the Karplus equation.[BR]",
 +        "(c) a table for each residue of the number of transitions between ",
 +        "rotamers per nanosecond,  and the order parameter S^2 of each dihedral.[BR]",
 +        "(d) a table for each residue of the rotamer occupancy.[PAR]",
 +        "All rotamers are taken as 3-fold, except for [GRK]omega[grk] and [GRK]chi[grk] dihedrals",
 +        "to planar groups (i.e. [GRK]chi[grk][SUB]2[sub] of aromatics, Asp and Asn; [GRK]chi[grk][SUB]3[sub] of Glu",
 +        "and Gln; and [GRK]chi[grk][SUB]4[sub] of Arg), which are 2-fold. \"rotamer 0\" means ",
 +        "that the dihedral was not in the core region of each rotamer. ",
 +        "The width of the core region can be set with [TT]-core_rotamer[tt][PAR]",
 +
 +        "The S^2 order parameters are also output to an [TT].xvg[tt] file",
 +        "(argument [TT]-o[tt] ) and optionally as a [TT].pdb[tt] file with",
 +        "the S^2 values as B-factor (argument [TT]-p[tt]). ",
 +        "The total number of rotamer transitions per timestep",
 +        "(argument [TT]-ot[tt]), the number of transitions per rotamer",
 +        "(argument [TT]-rt[tt]), and the ^3J couplings (argument [TT]-jc[tt]), ",
 +        "can also be written to [TT].xvg[tt] files. Note that the analysis",
 +        "of rotamer transitions assumes that the supplied trajectory frames",
 +        "are equally spaced in time.[PAR]",
 +
 +        "If [TT]-chi_prod[tt] is set (and [TT]-maxchi[tt] > 0), cumulative rotamers, e.g.",
 +        "1+9([GRK]chi[grk][SUB]1[sub]-1)+3([GRK]chi[grk][SUB]2[sub]-1)+([GRK]chi[grk][SUB]3[sub]-1) (if the residue has three 3-fold ",
 +        "dihedrals and [TT]-maxchi[tt] >= 3)",
 +        "are calculated. As before, if any dihedral is not in the core region,",
 +        "the rotamer is taken to be 0. The occupancies of these cumulative ",
 +        "rotamers (starting with rotamer 0) are written to the file",
 +        "that is the argument of [TT]-cp[tt], and if the [TT]-all[tt] flag",
 +        "is given, the rotamers as functions of time",
 +        "are written to [TT]chiproduct(RESIDUE)(nresnr).xvg[tt] ",
 +        "and their occupancies to [TT]histo-chiproduct(RESIDUE)(nresnr).xvg[tt].[PAR]",
 +
 +        "The option [TT]-r[tt] generates a contour plot of the average [GRK]omega[grk] angle",
 +        "as a function of the [GRK]phi[grk] and [GRK]psi[grk] angles, that is, in a Ramachandran plot",
 +        "the average [GRK]omega[grk] angle is plotted using color coding.",
 +
 +    };
 +
 +    const char *bugs[] = {
 +        "Produces MANY output files (up to about 4 times the number of residues in the protein, twice that if autocorrelation functions are calculated). Typically several hundred files are output.",
 +        "[GRK]phi[grk] and [GRK]psi[grk] dihedrals are calculated in a "
 +        "non-standard way, using H-N-CA-C for [GRK]phi[grk] instead of "
 +        "C(-)-N-CA-C, and N-CA-C-O for [GRK]psi[grk] instead of N-CA-C-N(+). "
 +        "This causes (usually small) discrepancies with the output of other "
 +        "tools like [gmx-rama].",
 +        "[TT]-r0[tt] option does not work properly",
 +        "Rotamers with multiplicity 2 are printed in [TT]chi.log[tt] as if they had multiplicity 3, with the 3rd (g(+)) always having probability 0"
 +    };
 +
 +    /* defaults */
 +    static int         r0          = 1, ndeg = 1, maxchi = 2;
 +    static gmx_bool    bAll        = FALSE;
 +    static gmx_bool    bPhi        = FALSE, bPsi = FALSE, bOmega = FALSE;
 +    static real        bfac_init   = -1.0, bfac_max = 0;
 +    static const char *maxchistr[] = { NULL, "0", "1", "2", "3",  "4", "5", "6", NULL };
 +    static gmx_bool    bRama       = FALSE, bShift = FALSE, bViol = FALSE, bRamOmega = FALSE;
 +    static gmx_bool    bNormHisto  = TRUE, bChiProduct = FALSE, bHChi = FALSE, bRAD = FALSE, bPBC = TRUE;
 +    static real        core_frac   = 0.5;
 +    t_pargs            pa[]        = {
 +        { "-r0",  FALSE, etINT, {&r0},
 +          "starting residue" },
 +        { "-phi",  FALSE, etBOOL, {&bPhi},
 +          "Output for [GRK]phi[grk] dihedral angles" },
 +        { "-psi",  FALSE, etBOOL, {&bPsi},
 +          "Output for [GRK]psi[grk] dihedral angles" },
 +        { "-omega", FALSE, etBOOL, {&bOmega},
 +          "Output for [GRK]omega[grk] dihedrals (peptide bonds)" },
 +        { "-rama", FALSE, etBOOL, {&bRama},
 +          "Generate [GRK]phi[grk]/[GRK]psi[grk] and [GRK]chi[grk][SUB]1[sub]/[GRK]chi[grk][SUB]2[sub] Ramachandran plots" },
 +        { "-viol", FALSE, etBOOL, {&bViol},
 +          "Write a file that gives 0 or 1 for violated Ramachandran angles" },
 +        { "-periodic", FALSE, etBOOL, {&bPBC},
 +          "Print dihedral angles modulo 360 degrees" },
 +        { "-all",  FALSE, etBOOL, {&bAll},
 +          "Output separate files for every dihedral." },
 +        { "-rad",  FALSE, etBOOL, {&bRAD},
 +          "in angle vs time files, use radians rather than degrees."},
 +        { "-shift", FALSE, etBOOL, {&bShift},
 +          "Compute chemical shifts from [GRK]phi[grk]/[GRK]psi[grk] angles" },
 +        { "-binwidth", FALSE, etINT, {&ndeg},
 +          "bin width for histograms (degrees)" },
 +        { "-core_rotamer", FALSE, etREAL, {&core_frac},
 +          "only the central [TT]-core_rotamer[tt]*(360/multiplicity) belongs to each rotamer (the rest is assigned to rotamer 0)" },
 +        { "-maxchi", FALSE, etENUM, {maxchistr},
 +          "calculate first ndih [GRK]chi[grk] dihedrals" },
 +        { "-normhisto", FALSE, etBOOL, {&bNormHisto},
 +          "Normalize histograms" },
 +        { "-ramomega", FALSE, etBOOL, {&bRamOmega},
 +          "compute average omega as a function of [GRK]phi[grk]/[GRK]psi[grk] and plot it in an [TT].xpm[tt] plot" },
 +        { "-bfact", FALSE, etREAL, {&bfac_init},
 +          "B-factor value for [TT].pdb[tt] file for atoms with no calculated dihedral order parameter"},
 +        { "-chi_prod", FALSE, etBOOL, {&bChiProduct},
 +          "compute a single cumulative rotamer for each residue"},
 +        { "-HChi", FALSE, etBOOL, {&bHChi},
 +          "Include dihedrals to sidechain hydrogens"},
 +        { "-bmax",  FALSE, etREAL, {&bfac_max},
 +          "Maximum B-factor on any of the atoms that make up a dihedral, for the dihedral angle to be considere in the statistics. Applies to database work where a number of X-Ray structures is analyzed. [TT]-bmax[tt] <= 0 means no limit." }
 +    };
 +
 +    FILE              *log;
 +    int                natoms, nlist, idum, nbin;
 +    t_atoms            atoms;
 +    rvec              *x;
 +    int                ePBC;
 +    matrix             box;
 +    char               title[256], grpname[256];
 +    t_dlist           *dlist;
 +    gmx_bool           bChi, bCorr, bSSHisto;
 +    gmx_bool           bDo_rt, bDo_oh, bDo_ot, bDo_jc;
 +    real               dt = 0, traj_t_ns;
 +    output_env_t       oenv;
 +    gmx_residuetype_t  rt;
 +
 +    atom_id            isize, *index;
 +    int                ndih, nactdih, nf;
 +    real             **dih, *trans_frac, *aver_angle, *time;
 +    int                i, j, **chi_lookup, *multiplicity;
 +
 +    t_filenm           fnm[] = {
 +        { efSTX, "-s",  NULL,     ffREAD  },
 +        { efTRX, "-f",  NULL,     ffREAD  },
 +        { efXVG, "-o",  "order",  ffWRITE },
 +        { efPDB, "-p",  "order",  ffOPTWR },
 +        { efDAT, "-ss", "ssdump", ffOPTRD },
 +        { efXVG, "-jc", "Jcoupling", ffWRITE },
 +        { efXVG, "-corr",  "dihcorr", ffOPTWR },
 +        { efLOG, "-g",  "chi",    ffWRITE },
 +        /* add two more arguments copying from g_angle */
 +        { efXVG, "-ot", "dihtrans", ffOPTWR },
 +        { efXVG, "-oh", "trhisto",  ffOPTWR },
 +        { efXVG, "-rt", "restrans",  ffOPTWR },
 +        { efXVG, "-cp", "chiprodhisto",  ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +    int                npargs;
 +    t_pargs           *ppa;
 +
 +    npargs = asize(pa);
 +    ppa    = add_acf_pargs(&npargs, pa);
 +    if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
 +                           NFILE, fnm, npargs, ppa, asize(desc), desc, asize(bugs), bugs,
 +                           &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    /* Handle result from enumerated type */
 +    sscanf(maxchistr[0], "%d", &maxchi);
 +    bChi = (maxchi > 0);
 +
 +    log = gmx_ffopen(ftp2fn(efLOG, NFILE, fnm), "w");
 +
 +    if (bRamOmega)
 +    {
 +        bOmega = TRUE;
 +        bPhi   = TRUE;
 +        bPsi   = TRUE;
 +    }
 +
 +    /* set some options */
 +    bDo_rt = (opt2bSet("-rt", NFILE, fnm));
 +    bDo_oh = (opt2bSet("-oh", NFILE, fnm));
 +    bDo_ot = (opt2bSet("-ot", NFILE, fnm));
 +    bDo_jc = (opt2bSet("-jc", NFILE, fnm));
 +    bCorr  = (opt2bSet("-corr", NFILE, fnm));
 +    if (bCorr)
 +    {
 +        fprintf(stderr, "Will calculate autocorrelation\n");
 +    }
 +
 +    if (core_frac > 1.0)
 +    {
 +        fprintf(stderr, "core_rotamer fraction > 1.0 ; will use 1.0\n");
 +        core_frac = 1.0;
 +    }
 +    if (core_frac < 0.0)
 +    {
 +        fprintf(stderr, "core_rotamer fraction < 0.0 ; will use 0.0\n");
 +        core_frac = 0.0;
 +    }
 +
 +    if (maxchi > MAXCHI)
 +    {
 +        fprintf(stderr,
 +                "Will only calculate first %d Chi dihedrals in stead of %d.\n",
 +                MAXCHI, maxchi);
 +        maxchi = MAXCHI;
 +    }
 +    bSSHisto = ftp2bSet(efDAT, NFILE, fnm);
 +    nbin     = 360/ndeg;
 +
 +    /* Find the chi angles using atoms struct and a list of amino acids */
 +    get_stx_coordnum(ftp2fn(efSTX, NFILE, fnm), &natoms);
 +    init_t_atoms(&atoms, natoms, TRUE);
 +    snew(x, natoms);
 +    read_stx_conf(ftp2fn(efSTX, NFILE, fnm), title, &atoms, x, NULL, &ePBC, box);
 +    fprintf(log, "Title: %s\n", title);
 +
 +    gmx_residuetype_init(&rt);
 +    dlist = mk_dlist(log, &atoms, &nlist, bPhi, bPsi, bChi, bHChi, maxchi, r0, rt);
 +    fprintf(stderr, "%d residues with dihedrals found\n", nlist);
 +
 +    if (nlist == 0)
 +    {
 +        gmx_fatal(FARGS, "No dihedrals in your structure!\n");
 +    }
 +
 +    /* Make a linear index for reading all. */
 +    index = make_chi_ind(nlist, dlist, &ndih);
 +    isize = 4*ndih;
 +    fprintf(stderr, "%d dihedrals found\n", ndih);
 +
 +    snew(dih, ndih);
 +
 +    /* COMPUTE ALL DIHEDRALS! */
 +    read_ang_dih(ftp2fn(efTRX, NFILE, fnm), FALSE, TRUE, FALSE, bPBC, 1, &idum,
 +                 &nf, &time, isize, index, &trans_frac, &aver_angle, dih, oenv);
 +
 +    dt = (time[nf-1]-time[0])/(nf-1); /* might want this for corr or n. transit*/
 +    if (bCorr)
 +    {
 +        if (nf < 2)
 +        {
 +            gmx_fatal(FARGS, "Need at least 2 frames for correlation");
 +        }
 +    }
 +
 +    /* put angles in -M_PI to M_PI ! and correct phase factor for phi and psi
 +     * pass nactdih instead of ndih to low_ana_dih_trans and get_chi_product_traj
 +     * to prevent accessing off end of arrays when maxchi < 5 or 6. */
 +    nactdih = reset_em_all(nlist, dlist, nf, dih, maxchi);
 +
 +    if (bAll)
 +    {
 +        dump_em_all(nlist, dlist, nf, time, dih, maxchi, bPhi, bPsi, bChi, bOmega, bRAD, oenv);
 +    }
 +
 +    /* Histogramming & J coupling constants & calc of S2 order params */
 +    histogramming(log, nbin, rt, nf, maxchi, dih, nlist, dlist, index,
 +                  bPhi, bPsi, bOmega, bChi,
 +                  bNormHisto, bSSHisto, ftp2fn(efDAT, NFILE, fnm), bfac_max, &atoms,
 +                  bDo_jc, opt2fn("-jc", NFILE, fnm), oenv);
 +
 +    /* transitions
 +     *
 +     * added multiplicity */
 +
 +    snew(multiplicity, ndih);
 +    mk_multiplicity_lookup(multiplicity, maxchi, nlist, dlist, ndih);
 +
 +    strcpy(grpname, "All residues, ");
 +    if (bPhi)
 +    {
 +        strcat(grpname, "Phi ");
 +    }
 +    if (bPsi)
 +    {
 +        strcat(grpname, "Psi ");
 +    }
 +    if (bOmega)
 +    {
 +        strcat(grpname, "Omega ");
 +    }
 +    if (bChi)
 +    {
 +        strcat(grpname, "Chi 1-");
 +        sprintf(grpname + strlen(grpname), "%i", maxchi);
 +    }
 +
 +
 +    low_ana_dih_trans(bDo_ot, opt2fn("-ot", NFILE, fnm),
 +                      bDo_oh, opt2fn("-oh", NFILE, fnm), maxchi,
 +                      dih, nlist, dlist, nf, nactdih, grpname, multiplicity,
 +                      time, FALSE, core_frac, oenv);
 +
 +    /* Order parameters */
 +    order_params(log, opt2fn("-o", NFILE, fnm), maxchi, nlist, dlist,
 +                 ftp2fn_null(efPDB, NFILE, fnm), bfac_init,
 +                 &atoms, x, ePBC, box, bPhi, bPsi, bChi, oenv);
 +
 +    /* Print ramachandran maps! */
 +    if (bRama)
 +    {
 +        do_rama(nf, nlist, dlist, dih, bViol, bRamOmega, oenv);
 +    }
 +
 +    if (bShift)
 +    {
 +        do_pp2shifts(log, nf, nlist, dlist, dih);
 +    }
 +
 +    /* rprint S^2, transitions, and rotamer occupancies to log */
 +    traj_t_ns = 0.001 * (time[nf-1]-time[0]);
 +    pr_dlist(log, nlist, dlist, traj_t_ns, edPrintST, bPhi, bPsi, bChi, bOmega, maxchi);
 +    pr_dlist(log, nlist, dlist, traj_t_ns, edPrintRO, bPhi, bPsi, bChi, bOmega, maxchi);
 +    gmx_ffclose(log);
 +    /* transitions to xvg */
 +    if (bDo_rt)
 +    {
 +        print_transitions(opt2fn("-rt", NFILE, fnm), maxchi, nlist, dlist, traj_t_ns, oenv);
 +    }
 +
 +    /* chi_product trajectories (ie one "rotamer number" for each residue) */
 +    if (bChiProduct && bChi)
 +    {
 +        snew(chi_lookup, nlist);
 +        for (i = 0; i < nlist; i++)
 +        {
 +            snew(chi_lookup[i], maxchi);
 +        }
 +        mk_chi_lookup(chi_lookup, maxchi, nlist, dlist);
 +
 +        get_chi_product_traj(dih, nf, nactdih,
 +                             maxchi, dlist, time, chi_lookup, multiplicity,
 +                             FALSE, bNormHisto, core_frac, bAll,
 +                             opt2fn("-cp", NFILE, fnm), oenv);
 +
 +        for (i = 0; i < nlist; i++)
 +        {
 +            sfree(chi_lookup[i]);
 +        }
 +    }
 +
 +    /* Correlation comes last because it messes up the angles */
 +    if (bCorr)
 +    {
 +        do_dihcorr(opt2fn("-corr", NFILE, fnm), nf, ndih, dih, dt, nlist, dlist, time,
 +                   maxchi, bPhi, bPsi, bChi, bOmega, oenv);
 +    }
 +
 +
 +    do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
 +    do_view(oenv, opt2fn("-jc", NFILE, fnm), "-nxy");
 +    if (bCorr)
 +    {
 +        do_view(oenv, opt2fn("-corr", NFILE, fnm), "-nxy");
 +    }
 +
 +    gmx_residuetype_destroy(rt);
 +
 +    return 0;
 +}
index 1e5a9e6d5a96f092eb579d24b147657a9e2c65be,0000000000000000000000000000000000000000..4919de60897ffb785ffe390c425f728141cf093f
mode 100644,000000..100644
--- /dev/null
@@@ -1,2001 -1,0 +1,2007 @@@
-         fprintf(fp, "@    s0 symbol 2\n");
-         fprintf(fp, "@    s0 symbol size 0.2\n");
-         fprintf(fp, "@    s0 linestyle 0\n");
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <string.h>
 +
 +#include "macros.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "typedefs.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "vec.h"
 +#include "macros.h"
 +#include "index.h"
 +#include "gromacs/random/random.h"
 +#include "pbc.h"
 +#include "rmpbc.h"
 +#include "xvgr.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gromacs/fileio/matio.h"
 +#include "cmat.h"
 +#include "gromacs/fileio/trnio.h"
 +#include "viewit.h"
 +#include "gmx_ana.h"
 +
 +#include "gromacs/linearalgebra/eigensolver.h"
 +#include "gromacs/math/do_fit.h"
 +#include "gromacs/legacyheaders/gmx_fatal.h"
 +
 +/* print to two file pointers at once (i.e. stderr and log) */
 +static gmx_inline
 +void lo_ffprintf(FILE *fp1, FILE *fp2, const char *buf)
 +{
 +    fprintf(fp1, "%s", buf);
 +    fprintf(fp2, "%s", buf);
 +}
 +
 +/* just print a prepared buffer to fp1 and fp2 */
 +static gmx_inline
 +void ffprintf(FILE *fp1, FILE *fp2, const char *buf)
 +{
 +    lo_ffprintf(fp1, fp2, buf);
 +}
 +
 +/* prepare buffer with one argument, then print to fp1 and fp2 */
 +static gmx_inline
 +void ffprintf_d(FILE *fp1, FILE *fp2, char *buf, const char *fmt, int arg)
 +{
 +    sprintf(buf, fmt, arg);
 +    lo_ffprintf(fp1, fp2, buf);
 +}
 +
 +/* prepare buffer with one argument, then print to fp1 and fp2 */
 +static gmx_inline
 +void ffprintf_g(FILE *fp1, FILE *fp2, char *buf, const char *fmt, real arg)
 +{
 +    sprintf(buf, fmt, arg);
 +    lo_ffprintf(fp1, fp2, buf);
 +}
 +
 +/* prepare buffer with one argument, then print to fp1 and fp2 */
 +static gmx_inline
 +void ffprintf_s(FILE *fp1, FILE *fp2, char *buf, const char *fmt, const char *arg)
 +{
 +    sprintf(buf, fmt, arg);
 +    lo_ffprintf(fp1, fp2, buf);
 +}
 +
 +/* prepare buffer with two arguments, then print to fp1 and fp2 */
 +static gmx_inline
 +void ffprintf_dd(FILE *fp1, FILE *fp2, char *buf, const char *fmt, int arg1, int arg2)
 +{
 +    sprintf(buf, fmt, arg1, arg2);
 +    lo_ffprintf(fp1, fp2, buf);
 +}
 +
 +/* prepare buffer with two arguments, then print to fp1 and fp2 */
 +static gmx_inline
 +void ffprintf_gg(FILE *fp1, FILE *fp2, char *buf, const char *fmt, real arg1, real arg2)
 +{
 +    sprintf(buf, fmt, arg1, arg2);
 +    lo_ffprintf(fp1, fp2, buf);
 +}
 +
 +/* prepare buffer with two arguments, then print to fp1 and fp2 */
 +static gmx_inline
 +void ffprintf_ss(FILE *fp1, FILE *fp2, char *buf, const char *fmt, const char *arg1, const char *arg2)
 +{
 +    sprintf(buf, fmt, arg1, arg2);
 +    lo_ffprintf(fp1, fp2, buf);
 +}
 +
 +typedef struct {
 +    int  ncl;
 +    int *cl;
 +} t_clusters;
 +
 +typedef struct {
 +    int  nr;
 +    int *nb;
 +} t_nnb;
 +
 +void pr_energy(FILE *fp, real e)
 +{
 +    fprintf(fp, "Energy: %8.4f\n", e);
 +}
 +
 +void cp_index(int nn, int from[], int to[])
 +{
 +    int i;
 +
 +    for (i = 0; (i < nn); i++)
 +    {
 +        to[i] = from[i];
 +    }
 +}
 +
 +void mc_optimize(FILE *log, t_mat *m, real *time,
 +                 int maxiter, int nrandom,
 +                 int seed, real kT,
 +                 const char *conv, output_env_t oenv)
 +{
 +    FILE      *fp = NULL;
 +    real       ecur, enext, emin, prob, enorm;
 +    int        i, j, iswap, jswap, nn, nuphill = 0;
 +    gmx_rng_t  rng;
 +    t_mat     *minimum;
 +
 +    if (m->n1 != m->nn)
 +    {
 +        fprintf(stderr, "Can not do Monte Carlo optimization with a non-square matrix.\n");
 +        return;
 +    }
 +    printf("\nDoing Monte Carlo optimization to find the smoothest trajectory\n");
 +    printf("by reordering the frames to minimize the path between the two structures\n");
 +    printf("that have the largest pairwise RMSD.\n");
 +
 +    iswap = jswap = -1;
 +    enorm = m->mat[0][0];
 +    for (i = 0; (i < m->n1); i++)
 +    {
 +        for (j = 0; (j < m->nn); j++)
 +        {
 +            if (m->mat[i][j] > enorm)
 +            {
 +                enorm   = m->mat[i][j];
 +                iswap   = i;
 +                jswap   = j;
 +            }
 +        }
 +    }
 +    if ((iswap == -1) || (jswap == -1))
 +    {
 +        fprintf(stderr, "Matrix contains identical values in all fields\n");
 +        return;
 +    }
 +    swap_rows(m, 0, iswap);
 +    swap_rows(m, m->n1-1, jswap);
 +    emin = ecur = mat_energy(m);
 +    printf("Largest distance %g between %d and %d. Energy: %g.\n",
 +           enorm, iswap, jswap, emin);
 +
 +    rng = gmx_rng_init(seed);
 +    nn  = m->nn;
 +
 +    /* Initiate and store global minimum */
 +    minimum     = init_mat(nn, m->b1D);
 +    minimum->nn = nn;
 +    copy_t_mat(minimum, m);
 +
 +    if (NULL != conv)
 +    {
 +        fp = xvgropen(conv, "Convergence of the MC optimization",
 +                      "Energy", "Step", oenv);
 +    }
 +    for (i = 0; (i < maxiter); i++)
 +    {
 +        /* Generate new swapping candidates */
 +        do
 +        {
 +            iswap = 1+(nn-2)*gmx_rng_uniform_real(rng);
 +            jswap = 1+(nn-2)*gmx_rng_uniform_real(rng);
 +        }
 +        while ((iswap == jswap) || (iswap >= nn-1) || (jswap >= nn-1));
 +
 +        /* Apply swap and compute energy */
 +        swap_rows(m, iswap, jswap);
 +        enext = mat_energy(m);
 +
 +        /* Compute probability */
 +        prob = 0;
 +        if ((enext < ecur) || (i < nrandom))
 +        {
 +            prob = 1;
 +            if (enext < emin)
 +            {
 +                /* Store global minimum */
 +                copy_t_mat(minimum, m);
 +                emin = enext;
 +            }
 +        }
 +        else if (kT > 0)
 +        {
 +            /* Try Monte Carlo step */
 +            prob = exp(-(enext-ecur)/(enorm*kT));
 +        }
 +
 +        if ((prob == 1) || (gmx_rng_uniform_real(rng) < prob))
 +        {
 +            if (enext > ecur)
 +            {
 +                nuphill++;
 +            }
 +
 +            fprintf(log, "Iter: %d Swapped %4d and %4d (energy: %g prob: %g)\n",
 +                    i, iswap, jswap, enext, prob);
 +            if (NULL != fp)
 +            {
 +                fprintf(fp, "%6d  %10g\n", i, enext);
 +            }
 +            ecur = enext;
 +        }
 +        else
 +        {
 +            swap_rows(m, jswap, iswap);
 +        }
 +    }
 +    fprintf(log, "%d uphill steps were taken during optimization\n",
 +            nuphill);
 +
 +    /* Now swap the matrix to get it into global minimum mode */
 +    copy_t_mat(m, minimum);
 +
 +    fprintf(log, "Global minimum energy %g\n", mat_energy(minimum));
 +    fprintf(log, "Global minimum energy %g\n", mat_energy(m));
 +    fprintf(log, "Swapped time and frame indices and RMSD to next neighbor:\n");
 +    for (i = 0; (i < m->nn); i++)
 +    {
 +        fprintf(log, "%10g  %5d  %10g\n",
 +                time[m->m_ind[i]],
 +                m->m_ind[i],
 +                (i < m->nn-1) ? m->mat[m->m_ind[i]][m->m_ind[i+1]] : 0);
 +    }
 +
 +    if (NULL != fp)
 +    {
 +        fclose(fp);
 +    }
 +}
 +
 +static void calc_dist(int nind, rvec x[], real **d)
 +{
 +    int      i, j;
 +    real    *xi;
 +    rvec     dx;
 +
 +    for (i = 0; (i < nind-1); i++)
 +    {
 +        xi = x[i];
 +        for (j = i+1; (j < nind); j++)
 +        {
 +            /* Should use pbc_dx when analysing multiple molecueles,
 +             * but the box is not stored for every frame.
 +             */
 +            rvec_sub(xi, x[j], dx);
 +            d[i][j] = norm(dx);
 +        }
 +    }
 +}
 +
 +static real rms_dist(int isize, real **d, real **d_r)
 +{
 +    int  i, j;
 +    real r, r2;
 +
 +    r2 = 0.0;
 +    for (i = 0; (i < isize-1); i++)
 +    {
 +        for (j = i+1; (j < isize); j++)
 +        {
 +            r   = d[i][j]-d_r[i][j];
 +            r2 += r*r;
 +        }
 +    }
 +    r2 /= (isize*(isize-1))/2;
 +
 +    return sqrt(r2);
 +}
 +
 +static int rms_dist_comp(const void *a, const void *b)
 +{
 +    t_dist *da, *db;
 +
 +    da = (t_dist *)a;
 +    db = (t_dist *)b;
 +
 +    if (da->dist - db->dist < 0)
 +    {
 +        return -1;
 +    }
 +    else if (da->dist - db->dist > 0)
 +    {
 +        return 1;
 +    }
 +    return 0;
 +}
 +
 +static int clust_id_comp(const void *a, const void *b)
 +{
 +    t_clustid *da, *db;
 +
 +    da = (t_clustid *)a;
 +    db = (t_clustid *)b;
 +
 +    return da->clust - db->clust;
 +}
 +
 +static int nrnb_comp(const void *a, const void *b)
 +{
 +    t_nnb *da, *db;
 +
 +    da = (t_nnb *)a;
 +    db = (t_nnb *)b;
 +
 +    /* return the b-a, we want highest first */
 +    return db->nr - da->nr;
 +}
 +
 +void gather(t_mat *m, real cutoff, t_clusters *clust)
 +{
 +    t_clustid *c;
 +    t_dist    *d;
 +    int        i, j, k, nn, cid, n1, diff;
 +    gmx_bool   bChange;
 +
 +    /* First we sort the entries in the RMSD matrix */
 +    n1 = m->nn;
 +    nn = ((n1-1)*n1)/2;
 +    snew(d, nn);
 +    for (i = k = 0; (i < n1); i++)
 +    {
 +        for (j = i+1; (j < n1); j++, k++)
 +        {
 +            d[k].i    = i;
 +            d[k].j    = j;
 +            d[k].dist = m->mat[i][j];
 +        }
 +    }
 +    if (k != nn)
 +    {
 +        gmx_incons("gather algortihm");
 +    }
 +    qsort(d, nn, sizeof(d[0]), rms_dist_comp);
 +
 +    /* Now we make a cluster index for all of the conformations */
 +    c = new_clustid(n1);
 +
 +    /* Now we check the closest structures, and equalize their cluster numbers */
 +    fprintf(stderr, "Linking structures ");
 +    do
 +    {
 +        fprintf(stderr, "*");
 +        bChange = FALSE;
 +        for (k = 0; (k < nn) && (d[k].dist < cutoff); k++)
 +        {
 +            diff = c[d[k].j].clust - c[d[k].i].clust;
 +            if (diff)
 +            {
 +                bChange = TRUE;
 +                if (diff > 0)
 +                {
 +                    c[d[k].j].clust = c[d[k].i].clust;
 +                }
 +                else
 +                {
 +                    c[d[k].i].clust = c[d[k].j].clust;
 +                }
 +            }
 +        }
 +    }
 +    while (bChange);
 +    fprintf(stderr, "\nSorting and renumbering clusters\n");
 +    /* Sort on cluster number */
 +    qsort(c, n1, sizeof(c[0]), clust_id_comp);
 +
 +    /* Renumber clusters */
 +    cid = 1;
 +    for (k = 1; k < n1; k++)
 +    {
 +        if (c[k].clust != c[k-1].clust)
 +        {
 +            c[k-1].clust = cid;
 +            cid++;
 +        }
 +        else
 +        {
 +            c[k-1].clust = cid;
 +        }
 +    }
 +    c[k-1].clust = cid;
 +    if (debug)
 +    {
 +        for (k = 0; (k < n1); k++)
 +        {
 +            fprintf(debug, "Cluster index for conformation %d: %d\n",
 +                    c[k].conf, c[k].clust);
 +        }
 +    }
 +    clust->ncl = cid;
 +    for (k = 0; k < n1; k++)
 +    {
 +        clust->cl[c[k].conf] = c[k].clust;
 +    }
 +
 +    sfree(c);
 +    sfree(d);
 +}
 +
 +gmx_bool jp_same(int **nnb, int i, int j, int P)
 +{
 +    gmx_bool bIn;
 +    int      k, ii, jj, pp;
 +
 +    bIn = FALSE;
 +    for (k = 0; nnb[i][k] >= 0; k++)
 +    {
 +        bIn = bIn || (nnb[i][k] == j);
 +    }
 +    if (!bIn)
 +    {
 +        return FALSE;
 +    }
 +
 +    bIn = FALSE;
 +    for (k = 0; nnb[j][k] >= 0; k++)
 +    {
 +        bIn = bIn || (nnb[j][k] == i);
 +    }
 +    if (!bIn)
 +    {
 +        return FALSE;
 +    }
 +
 +    pp = 0;
 +    for (ii = 0; nnb[i][ii] >= 0; ii++)
 +    {
 +        for (jj = 0; nnb[j][jj] >= 0; jj++)
 +        {
 +            if ((nnb[i][ii] == nnb[j][jj]) && (nnb[i][ii] != -1))
 +            {
 +                pp++;
 +            }
 +        }
 +    }
 +
 +    return (pp >= P);
 +}
 +
 +static void jarvis_patrick(int n1, real **mat, int M, int P,
 +                           real rmsdcut, t_clusters *clust)
 +{
 +    t_dist     *row;
 +    t_clustid  *c;
 +    int       **nnb;
 +    int         i, j, k, cid, diff, max;
 +    gmx_bool    bChange;
 +    real      **mcpy = NULL;
 +
 +    if (rmsdcut < 0)
 +    {
 +        rmsdcut = 10000;
 +    }
 +
 +    /* First we sort the entries in the RMSD matrix row by row.
 +     * This gives us the nearest neighbor list.
 +     */
 +    snew(nnb, n1);
 +    snew(row, n1);
 +    for (i = 0; (i < n1); i++)
 +    {
 +        for (j = 0; (j < n1); j++)
 +        {
 +            row[j].j    = j;
 +            row[j].dist = mat[i][j];
 +        }
 +        qsort(row, n1, sizeof(row[0]), rms_dist_comp);
 +        if (M > 0)
 +        {
 +            /* Put the M nearest neighbors in the list */
 +            snew(nnb[i], M+1);
 +            for (j = k = 0; (k < M) && (j < n1) && (mat[i][row[j].j] < rmsdcut); j++)
 +            {
 +                if (row[j].j  != i)
 +                {
 +                    nnb[i][k]  = row[j].j;
 +                    k++;
 +                }
 +            }
 +            nnb[i][k] = -1;
 +        }
 +        else
 +        {
 +            /* Put all neighbors nearer than rmsdcut in the list */
 +            max = 0;
 +            k   = 0;
 +            for (j = 0; (j < n1) && (mat[i][row[j].j] < rmsdcut); j++)
 +            {
 +                if (row[j].j != i)
 +                {
 +                    if (k >= max)
 +                    {
 +                        max += 10;
 +                        srenew(nnb[i], max);
 +                    }
 +                    nnb[i][k] = row[j].j;
 +                    k++;
 +                }
 +            }
 +            if (k == max)
 +            {
 +                srenew(nnb[i], max+1);
 +            }
 +            nnb[i][k] = -1;
 +        }
 +    }
 +    sfree(row);
 +    if (debug)
 +    {
 +        fprintf(debug, "Nearest neighborlist. M = %d, P = %d\n", M, P);
 +        for (i = 0; (i < n1); i++)
 +        {
 +            fprintf(debug, "i:%5d nbs:", i);
 +            for (j = 0; nnb[i][j] >= 0; j++)
 +            {
 +                fprintf(debug, "%5d[%5.3f]", nnb[i][j], mat[i][nnb[i][j]]);
 +            }
 +            fprintf(debug, "\n");
 +        }
 +    }
 +
 +    c = new_clustid(n1);
 +    fprintf(stderr, "Linking structures ");
 +    /* Use mcpy for temporary storage of booleans */
 +    mcpy = mk_matrix(n1, n1, FALSE);
 +    for (i = 0; i < n1; i++)
 +    {
 +        for (j = i+1; j < n1; j++)
 +        {
 +            mcpy[i][j] = jp_same(nnb, i, j, P);
 +        }
 +    }
 +    do
 +    {
 +        fprintf(stderr, "*");
 +        bChange = FALSE;
 +        for (i = 0; i < n1; i++)
 +        {
 +            for (j = i+1; j < n1; j++)
 +            {
 +                if (mcpy[i][j])
 +                {
 +                    diff = c[j].clust - c[i].clust;
 +                    if (diff)
 +                    {
 +                        bChange = TRUE;
 +                        if (diff > 0)
 +                        {
 +                            c[j].clust = c[i].clust;
 +                        }
 +                        else
 +                        {
 +                            c[i].clust = c[j].clust;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    while (bChange);
 +
 +    fprintf(stderr, "\nSorting and renumbering clusters\n");
 +    /* Sort on cluster number */
 +    qsort(c, n1, sizeof(c[0]), clust_id_comp);
 +
 +    /* Renumber clusters */
 +    cid = 1;
 +    for (k = 1; k < n1; k++)
 +    {
 +        if (c[k].clust != c[k-1].clust)
 +        {
 +            c[k-1].clust = cid;
 +            cid++;
 +        }
 +        else
 +        {
 +            c[k-1].clust = cid;
 +        }
 +    }
 +    c[k-1].clust = cid;
 +    clust->ncl   = cid;
 +    for (k = 0; k < n1; k++)
 +    {
 +        clust->cl[c[k].conf] = c[k].clust;
 +    }
 +    if (debug)
 +    {
 +        for (k = 0; (k < n1); k++)
 +        {
 +            fprintf(debug, "Cluster index for conformation %d: %d\n",
 +                    c[k].conf, c[k].clust);
 +        }
 +    }
 +
 +/* Again, I don't see the point in this... (AF) */
 +/*   for(i=0; (i<n1); i++) { */
 +/*     for(j=0; (j<n1); j++) */
 +/*       mcpy[c[i].conf][c[j].conf] = mat[i][j]; */
 +/*   } */
 +/*   for(i=0; (i<n1); i++) { */
 +/*     for(j=0; (j<n1); j++) */
 +/*       mat[i][j] = mcpy[i][j]; */
 +/*   } */
 +    done_matrix(n1, &mcpy);
 +
 +    sfree(c);
 +    for (i = 0; (i < n1); i++)
 +    {
 +        sfree(nnb[i]);
 +    }
 +    sfree(nnb);
 +}
 +
 +static void dump_nnb (FILE *fp, const char *title, int n1, t_nnb *nnb)
 +{
 +    int i, j;
 +
 +    /* dump neighbor list */
 +    fprintf(fp, "%s", title);
 +    for (i = 0; (i < n1); i++)
 +    {
 +        fprintf(fp, "i:%5d #:%5d nbs:", i, nnb[i].nr);
 +        for (j = 0; j < nnb[i].nr; j++)
 +        {
 +            fprintf(fp, "%5d", nnb[i].nb[j]);
 +        }
 +        fprintf(fp, "\n");
 +    }
 +}
 +
 +static void gromos(int n1, real **mat, real rmsdcut, t_clusters *clust)
 +{
 +    t_dist *row;
 +    t_nnb  *nnb;
 +    int     i, j, k, j1, max;
 +
 +    /* Put all neighbors nearer than rmsdcut in the list */
 +    fprintf(stderr, "Making list of neighbors within cutoff ");
 +    snew(nnb, n1);
 +    snew(row, n1);
 +    for (i = 0; (i < n1); i++)
 +    {
 +        max = 0;
 +        k   = 0;
 +        /* put all neighbors within cut-off in list */
 +        for (j = 0; j < n1; j++)
 +        {
 +            if (mat[i][j] < rmsdcut)
 +            {
 +                if (k >= max)
 +                {
 +                    max += 10;
 +                    srenew(nnb[i].nb, max);
 +                }
 +                nnb[i].nb[k] = j;
 +                k++;
 +            }
 +        }
 +        /* store nr of neighbors, we'll need that */
 +        nnb[i].nr = k;
 +        if (i%(1+n1/100) == 0)
 +        {
 +            fprintf(stderr, "%3d%%\b\b\b\b", (i*100+1)/n1);
 +        }
 +    }
 +    fprintf(stderr, "%3d%%\n", 100);
 +    sfree(row);
 +
 +    /* sort neighbor list on number of neighbors, largest first */
 +    qsort(nnb, n1, sizeof(nnb[0]), nrnb_comp);
 +
 +    if (debug)
 +    {
 +        dump_nnb(debug, "Nearest neighborlist after sort.\n", n1, nnb);
 +    }
 +
 +    /* turn first structure with all its neighbors (largest) into cluster
 +       remove them from pool of structures and repeat for all remaining */
 +    fprintf(stderr, "Finding clusters %4d", 0);
 +    /* cluster id's start at 1: */
 +    k = 1;
 +    while (nnb[0].nr)
 +    {
 +        /* set cluster id (k) for first item in neighborlist */
 +        for (j = 0; j < nnb[0].nr; j++)
 +        {
 +            clust->cl[nnb[0].nb[j]] = k;
 +        }
 +        /* mark as done */
 +        nnb[0].nr = 0;
 +        sfree(nnb[0].nb);
 +
 +        /* adjust number of neighbors for others, taking removals into account: */
 +        for (i = 1; i < n1 && nnb[i].nr; i++)
 +        {
 +            j1 = 0;
 +            for (j = 0; j < nnb[i].nr; j++)
 +            {
 +                /* if this neighbor wasn't removed */
 +                if (clust->cl[nnb[i].nb[j]] == 0)
 +                {
 +                    /* shift the rest (j1<=j) */
 +                    nnb[i].nb[j1] = nnb[i].nb[j];
 +                    /* next */
 +                    j1++;
 +                }
 +            }
 +            /* now j1 is the new number of neighbors */
 +            nnb[i].nr = j1;
 +        }
 +        /* sort again on nnb[].nr, because we have new # neighbors: */
 +        /* but we only need to sort upto i, i.e. when nnb[].nr>0 */
 +        qsort(nnb, i, sizeof(nnb[0]), nrnb_comp);
 +
 +        fprintf(stderr, "\b\b\b\b%4d", k);
 +        /* new cluster id */
 +        k++;
 +    }
 +    fprintf(stderr, "\n");
 +    sfree(nnb);
 +    if (debug)
 +    {
 +        fprintf(debug, "Clusters (%d):\n", k);
 +        for (i = 0; i < n1; i++)
 +        {
 +            fprintf(debug, " %3d", clust->cl[i]);
 +        }
 +        fprintf(debug, "\n");
 +    }
 +
 +    clust->ncl = k-1;
 +}
 +
 +rvec **read_whole_trj(const char *fn, int isize, atom_id index[], int skip,
 +                      int *nframe, real **time, const output_env_t oenv, gmx_bool bPBC, gmx_rmpbc_t gpbc)
 +{
 +    rvec       **xx, *x;
 +    matrix       box;
 +    real         t;
 +    int          i, i0, j, max_nf;
 +    int          natom;
 +    t_trxstatus *status;
 +
 +
 +    max_nf = 0;
 +    xx     = NULL;
 +    *time  = NULL;
 +    natom  = read_first_x(oenv, &status, fn, &t, &x, box);
 +    i      = 0;
 +    i0     = 0;
 +    do
 +    {
 +        if (bPBC)
 +        {
 +            gmx_rmpbc(gpbc, natom, box, x);
 +        }
 +        if (i0 >= max_nf)
 +        {
 +            max_nf += 10;
 +            srenew(xx, max_nf);
 +            srenew(*time, max_nf);
 +        }
 +        if ((i % skip) == 0)
 +        {
 +            snew(xx[i0], isize);
 +            /* Store only the interesting atoms */
 +            for (j = 0; (j < isize); j++)
 +            {
 +                copy_rvec(x[index[j]], xx[i0][j]);
 +            }
 +            (*time)[i0] = t;
 +            i0++;
 +        }
 +        i++;
 +    }
 +    while (read_next_x(oenv, status, &t, x, box));
 +    fprintf(stderr, "Allocated %lu bytes for frames\n",
 +            (unsigned long) (max_nf*isize*sizeof(**xx)));
 +    fprintf(stderr, "Read %d frames from trajectory %s\n", i0, fn);
 +    *nframe = i0;
 +    sfree(x);
 +
 +    return xx;
 +}
 +
 +static int plot_clusters(int nf, real **mat, t_clusters *clust,
 +                         int minstruct)
 +{
 +    int  i, j, ncluster, ci;
 +    int *cl_id, *nstruct, *strind;
 +
 +    snew(cl_id, nf);
 +    snew(nstruct, nf);
 +    snew(strind, nf);
 +    for (i = 0; i < nf; i++)
 +    {
 +        strind[i] = 0;
 +        cl_id[i]  = clust->cl[i];
 +        nstruct[cl_id[i]]++;
 +    }
 +    ncluster = 0;
 +    for (i = 0; i < nf; i++)
 +    {
 +        if (nstruct[i] >= minstruct)
 +        {
 +            ncluster++;
 +            for (j = 0; (j < nf); j++)
 +            {
 +                if (cl_id[j] == i)
 +                {
 +                    strind[j] = ncluster;
 +                }
 +            }
 +        }
 +    }
 +    ncluster++;
 +    fprintf(stderr, "There are %d clusters with at least %d conformations\n",
 +            ncluster, minstruct);
 +
 +    for (i = 0; (i < nf); i++)
 +    {
 +        ci = cl_id[i];
 +        for (j = 0; j < i; j++)
 +        {
 +            if ((ci == cl_id[j]) && (nstruct[ci] >= minstruct))
 +            {
 +                /* color different clusters with different colors, as long as
 +                   we don't run out of colors */
 +                mat[i][j] = strind[i];
 +            }
 +            else
 +            {
 +                mat[i][j] = 0;
 +            }
 +        }
 +    }
 +    sfree(strind);
 +    sfree(nstruct);
 +    sfree(cl_id);
 +
 +    return ncluster;
 +}
 +
 +static void mark_clusters(int nf, real **mat, real val, t_clusters *clust)
 +{
 +    int i, j, v;
 +
 +    for (i = 0; i < nf; i++)
 +    {
 +        for (j = 0; j < i; j++)
 +        {
 +            if (clust->cl[i] == clust->cl[j])
 +            {
 +                mat[i][j] = val;
 +            }
 +            else
 +            {
 +                mat[i][j] = 0;
 +            }
 +        }
 +    }
 +}
 +
 +static char *parse_filename(const char *fn, int maxnr)
 +{
 +    int         i;
 +    char       *fnout;
 +    const char *ext;
 +    char        buf[STRLEN];
 +
 +    if (strchr(fn, '%'))
 +    {
 +        gmx_fatal(FARGS, "will not number filename %s containing '%c'", fn, '%');
 +    }
 +    /* number of digits needed in numbering */
 +    i = (int)(log(maxnr)/log(10)) + 1;
 +    /* split fn and ext */
 +    ext = strrchr(fn, '.');
 +    if (!ext)
 +    {
 +        gmx_fatal(FARGS, "cannot separate extension in filename %s", fn);
 +    }
 +    ext++;
 +    /* insert e.g. '%03d' between fn and ext */
 +    sprintf(buf, "%s%%0%dd.%s", fn, i, ext);
 +    snew(fnout, strlen(buf)+1);
 +    strcpy(fnout, buf);
 +
 +    return fnout;
 +}
 +
 +static void ana_trans(t_clusters *clust, int nf,
 +                      const char *transfn, const char *ntransfn, FILE *log,
 +                      t_rgb rlo, t_rgb rhi, const output_env_t oenv)
 +{
 +    FILE  *fp;
 +    real **trans, *axis;
 +    int   *ntrans;
 +    int    i, ntranst, maxtrans;
 +    char   buf[STRLEN];
 +
 +    snew(ntrans, clust->ncl);
 +    snew(trans, clust->ncl);
 +    snew(axis, clust->ncl);
 +    for (i = 0; i < clust->ncl; i++)
 +    {
 +        axis[i] = i+1;
 +        snew(trans[i], clust->ncl);
 +    }
 +    ntranst  = 0;
 +    maxtrans = 0;
 +    for (i = 1; i < nf; i++)
 +    {
 +        if (clust->cl[i] != clust->cl[i-1])
 +        {
 +            ntranst++;
 +            ntrans[clust->cl[i-1]-1]++;
 +            ntrans[clust->cl[i]-1]++;
 +            trans[clust->cl[i-1]-1][clust->cl[i]-1]++;
 +            maxtrans = max(maxtrans, trans[clust->cl[i]-1][clust->cl[i-1]-1]);
 +        }
 +    }
 +    ffprintf_dd(stderr, log, buf, "Counted %d transitions in total, "
 +                "max %d between two specific clusters\n", ntranst, maxtrans);
 +    if (transfn)
 +    {
 +        fp = gmx_ffopen(transfn, "w");
 +        i  = min(maxtrans+1, 80);
 +        write_xpm(fp, 0, "Cluster Transitions", "# transitions",
 +                  "from cluster", "to cluster",
 +                  clust->ncl, clust->ncl, axis, axis, trans,
 +                  0, maxtrans, rlo, rhi, &i);
 +        gmx_ffclose(fp);
 +    }
 +    if (ntransfn)
 +    {
 +        fp = xvgropen(ntransfn, "Cluster Transitions", "Cluster #", "# transitions",
 +                      oenv);
 +        for (i = 0; i < clust->ncl; i++)
 +        {
 +            fprintf(fp, "%5d %5d\n", i+1, ntrans[i]);
 +        }
 +        gmx_ffclose(fp);
 +    }
 +    sfree(ntrans);
 +    for (i = 0; i < clust->ncl; i++)
 +    {
 +        sfree(trans[i]);
 +    }
 +    sfree(trans);
 +    sfree(axis);
 +}
 +
 +static void analyze_clusters(int nf, t_clusters *clust, real **rmsd,
 +                             int natom, t_atoms *atoms, rvec *xtps,
 +                             real *mass, rvec **xx, real *time,
 +                             int ifsize, atom_id *fitidx,
 +                             int iosize, atom_id *outidx,
 +                             const char *trxfn, const char *sizefn,
 +                             const char *transfn, const char *ntransfn,
 +                             const char *clustidfn, gmx_bool bAverage,
 +                             int write_ncl, int write_nst, real rmsmin,
 +                             gmx_bool bFit, FILE *log, t_rgb rlo, t_rgb rhi,
 +                             const output_env_t oenv)
 +{
 +    FILE        *fp = NULL;
 +    char         buf[STRLEN], buf1[40], buf2[40], buf3[40], *trxsfn;
 +    t_trxstatus *trxout  = NULL;
 +    t_trxstatus *trxsout = NULL;
 +    int          i, i1, cl, nstr, *structure, first = 0, midstr;
 +    gmx_bool    *bWrite = NULL;
 +    real         r, clrmsd, midrmsd;
 +    rvec        *xav = NULL;
 +    matrix       zerobox;
 +
 +    clear_mat(zerobox);
 +
 +    ffprintf_d(stderr, log, buf, "\nFound %d clusters\n\n", clust->ncl);
 +    trxsfn = NULL;
 +    if (trxfn)
 +    {
 +        /* do we write all structures? */
 +        if (write_ncl)
 +        {
 +            trxsfn = parse_filename(trxfn, max(write_ncl, clust->ncl));
 +            snew(bWrite, nf);
 +        }
 +        ffprintf_ss(stderr, log, buf, "Writing %s structure for each cluster to %s\n",
 +                    bAverage ? "average" : "middle", trxfn);
 +        if (write_ncl)
 +        {
 +            /* find out what we want to tell the user:
 +               Writing [all structures|structures with rmsd > %g] for
 +               {all|first %d} clusters {with more than %d structures} to %s     */
 +            if (rmsmin > 0.0)
 +            {
 +                sprintf(buf1, "structures with rmsd > %g", rmsmin);
 +            }
 +            else
 +            {
 +                sprintf(buf1, "all structures");
 +            }
 +            buf2[0] = buf3[0] = '\0';
 +            if (write_ncl >= clust->ncl)
 +            {
 +                if (write_nst == 0)
 +                {
 +                    sprintf(buf2, "all ");
 +                }
 +            }
 +            else
 +            {
 +                sprintf(buf2, "the first %d ", write_ncl);
 +            }
 +            if (write_nst)
 +            {
 +                sprintf(buf3, " with more than %d structures", write_nst);
 +            }
 +            sprintf(buf, "Writing %s for %sclusters%s to %s\n", buf1, buf2, buf3, trxsfn);
 +            ffprintf(stderr, log, buf);
 +        }
 +
 +        /* Prepare a reference structure for the orientation of the clusters  */
 +        if (bFit)
 +        {
 +            reset_x(ifsize, fitidx, natom, NULL, xtps, mass);
 +        }
 +        trxout = open_trx(trxfn, "w");
 +        /* Calculate the average structure in each cluster,               *
 +         * all structures are fitted to the first struture of the cluster */
 +        snew(xav, natom);
 +    }
 +
 +    if (transfn || ntransfn)
 +    {
 +        ana_trans(clust, nf, transfn, ntransfn, log, rlo, rhi, oenv);
 +    }
 +
 +    if (clustidfn)
 +    {
 +        fp = xvgropen(clustidfn, "Clusters", output_env_get_xvgr_tlabel(oenv), "Cluster #", oenv);
-         fprintf(fp, "@g%d type %s\n", 0, "bar");
++        if (output_env_get_print_xvgr_codes(oenv))
++        {
++            fprintf(fp, "@    s0 symbol 2\n");
++            fprintf(fp, "@    s0 symbol size 0.2\n");
++            fprintf(fp, "@    s0 linestyle 0\n");
++        }
 +        for (i = 0; i < nf; i++)
 +        {
 +            fprintf(fp, "%8g %8d\n", time[i], clust->cl[i]);
 +        }
 +        gmx_ffclose(fp);
 +    }
 +    if (sizefn)
 +    {
 +        fp = xvgropen(sizefn, "Cluster Sizes", "Cluster #", "# Structures", oenv);
++        if (output_env_get_print_xvgr_codes(oenv))
++        {
++            fprintf(fp, "@g%d type %s\n", 0, "bar");
++        }
 +    }
 +    snew(structure, nf);
 +    fprintf(log, "\n%3s | %3s  %4s | %6s %4s | cluster members\n",
 +            "cl.", "#st", "rmsd", "middle", "rmsd");
 +    for (cl = 1; cl <= clust->ncl; cl++)
 +    {
 +        /* prepare structures (fit, middle, average) */
 +        if (xav)
 +        {
 +            for (i = 0; i < natom; i++)
 +            {
 +                clear_rvec(xav[i]);
 +            }
 +        }
 +        nstr = 0;
 +        for (i1 = 0; i1 < nf; i1++)
 +        {
 +            if (clust->cl[i1] == cl)
 +            {
 +                structure[nstr] = i1;
 +                nstr++;
 +                if (trxfn && (bAverage || write_ncl) )
 +                {
 +                    if (bFit)
 +                    {
 +                        reset_x(ifsize, fitidx, natom, NULL, xx[i1], mass);
 +                    }
 +                    if (nstr == 1)
 +                    {
 +                        first = i1;
 +                    }
 +                    else if (bFit)
 +                    {
 +                        do_fit(natom, mass, xx[first], xx[i1]);
 +                    }
 +                    if (xav)
 +                    {
 +                        for (i = 0; i < natom; i++)
 +                        {
 +                            rvec_inc(xav[i], xx[i1][i]);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        if (sizefn)
 +        {
 +            fprintf(fp, "%8d %8d\n", cl, nstr);
 +        }
 +        clrmsd  = 0;
 +        midstr  = 0;
 +        midrmsd = 10000;
 +        for (i1 = 0; i1 < nstr; i1++)
 +        {
 +            r = 0;
 +            if (nstr > 1)
 +            {
 +                for (i = 0; i < nstr; i++)
 +                {
 +                    if (i < i1)
 +                    {
 +                        r += rmsd[structure[i]][structure[i1]];
 +                    }
 +                    else
 +                    {
 +                        r += rmsd[structure[i1]][structure[i]];
 +                    }
 +                }
 +                r /= (nstr - 1);
 +            }
 +            if (r < midrmsd)
 +            {
 +                midstr  = structure[i1];
 +                midrmsd = r;
 +            }
 +            clrmsd += r;
 +        }
 +        clrmsd /= nstr;
 +
 +        /* dump cluster info to logfile */
 +        if (nstr > 1)
 +        {
 +            sprintf(buf1, "%6.3f", clrmsd);
 +            if (buf1[0] == '0')
 +            {
 +                buf1[0] = ' ';
 +            }
 +            sprintf(buf2, "%5.3f", midrmsd);
 +            if (buf2[0] == '0')
 +            {
 +                buf2[0] = ' ';
 +            }
 +        }
 +        else
 +        {
 +            sprintf(buf1, "%5s", "");
 +            sprintf(buf2, "%5s", "");
 +        }
 +        fprintf(log, "%3d | %3d %s | %6g%s |", cl, nstr, buf1, time[midstr], buf2);
 +        for (i = 0; i < nstr; i++)
 +        {
 +            if ((i % 7 == 0) && i)
 +            {
 +                sprintf(buf, "\n%3s | %3s  %4s | %6s %4s |", "", "", "", "", "");
 +            }
 +            else
 +            {
 +                buf[0] = '\0';
 +            }
 +            i1 = structure[i];
 +            fprintf(log, "%s %6g", buf, time[i1]);
 +        }
 +        fprintf(log, "\n");
 +
 +        /* write structures to trajectory file(s) */
 +        if (trxfn)
 +        {
 +            if (write_ncl)
 +            {
 +                for (i = 0; i < nstr; i++)
 +                {
 +                    bWrite[i] = FALSE;
 +                }
 +            }
 +            if (cl < write_ncl+1 && nstr > write_nst)
 +            {
 +                /* Dump all structures for this cluster */
 +                /* generate numbered filename (there is a %d in trxfn!) */
 +                sprintf(buf, trxsfn, cl);
 +                trxsout = open_trx(buf, "w");
 +                for (i = 0; i < nstr; i++)
 +                {
 +                    bWrite[i] = TRUE;
 +                    if (rmsmin > 0.0)
 +                    {
 +                        for (i1 = 0; i1 < i && bWrite[i]; i1++)
 +                        {
 +                            if (bWrite[i1])
 +                            {
 +                                bWrite[i] = rmsd[structure[i1]][structure[i]] > rmsmin;
 +                            }
 +                        }
 +                    }
 +                    if (bWrite[i])
 +                    {
 +                        write_trx(trxsout, iosize, outidx, atoms, i, time[structure[i]], zerobox,
 +                                  xx[structure[i]], NULL, NULL);
 +                    }
 +                }
 +                close_trx(trxsout);
 +            }
 +            /* Dump the average structure for this cluster */
 +            if (bAverage)
 +            {
 +                for (i = 0; i < natom; i++)
 +                {
 +                    svmul(1.0/nstr, xav[i], xav[i]);
 +                }
 +            }
 +            else
 +            {
 +                for (i = 0; i < natom; i++)
 +                {
 +                    copy_rvec(xx[midstr][i], xav[i]);
 +                }
 +                if (bFit)
 +                {
 +                    reset_x(ifsize, fitidx, natom, NULL, xav, mass);
 +                }
 +            }
 +            if (bFit)
 +            {
 +                do_fit(natom, mass, xtps, xav);
 +            }
 +            r = cl;
 +            write_trx(trxout, iosize, outidx, atoms, cl, time[midstr], zerobox, xav, NULL, NULL);
 +        }
 +    }
 +    /* clean up */
 +    if (trxfn)
 +    {
 +        close_trx(trxout);
 +        sfree(xav);
 +        if (write_ncl)
 +        {
 +            sfree(bWrite);
 +        }
 +    }
 +    sfree(structure);
 +    if (trxsfn)
 +    {
 +        sfree(trxsfn);
 +    }
 +}
 +
 +static void convert_mat(t_matrix *mat, t_mat *rms)
 +{
 +    int i, j;
 +
 +    rms->n1 = mat->nx;
 +    matrix2real(mat, rms->mat);
 +    /* free input xpm matrix data */
 +    for (i = 0; i < mat->nx; i++)
 +    {
 +        sfree(mat->matrix[i]);
 +    }
 +    sfree(mat->matrix);
 +
 +    for (i = 0; i < mat->nx; i++)
 +    {
 +        for (j = i; j < mat->nx; j++)
 +        {
 +            rms->sumrms += rms->mat[i][j];
 +            rms->maxrms  = max(rms->maxrms, rms->mat[i][j]);
 +            if (j != i)
 +            {
 +                rms->minrms = min(rms->minrms, rms->mat[i][j]);
 +            }
 +        }
 +    }
 +    rms->nn = mat->nx;
 +}
 +
 +int gmx_cluster(int argc, char *argv[])
 +{
 +    const char        *desc[] = {
 +        "[THISMODULE] can cluster structures using several different methods.",
 +        "Distances between structures can be determined from a trajectory",
 +        "or read from an [TT].xpm[tt] matrix file with the [TT]-dm[tt] option.",
 +        "RMS deviation after fitting or RMS deviation of atom-pair distances",
 +        "can be used to define the distance between structures.[PAR]",
 +
 +        "single linkage: add a structure to a cluster when its distance to any",
 +        "element of the cluster is less than [TT]cutoff[tt].[PAR]",
 +
 +        "Jarvis Patrick: add a structure to a cluster when this structure",
 +        "and a structure in the cluster have each other as neighbors and",
 +        "they have a least [TT]P[tt] neighbors in common. The neighbors",
 +        "of a structure are the M closest structures or all structures within",
 +        "[TT]cutoff[tt].[PAR]",
 +
 +        "Monte Carlo: reorder the RMSD matrix using Monte Carlo such that",
 +        "the order of the frames is using the smallest possible increments.",
 +        "With this it is possible to make a smooth animation going from one",
 +        "structure to another with the largest possible (e.g.) RMSD between",
 +        "them, however the intermediate steps should be as small as possible.",
 +        "Applications could be to visualize a potential of mean force",
 +        "ensemble of simulations or a pulling simulation. Obviously the user",
 +        "has to prepare the trajectory well (e.g. by not superimposing frames).",
 +        "The final result can be inspect visually by looking at the matrix",
 +        "[TT].xpm[tt] file, which should vary smoothly from bottom to top.[PAR]",
 +
 +        "diagonalization: diagonalize the RMSD matrix.[PAR]",
 +
 +        "gromos: use algorithm as described in Daura [IT]et al.[it]",
 +        "([IT]Angew. Chem. Int. Ed.[it] [BB]1999[bb], [IT]38[it], pp 236-240).",
 +        "Count number of neighbors using cut-off, take structure with",
 +        "largest number of neighbors with all its neighbors as cluster",
 +        "and eliminate it from the pool of clusters. Repeat for remaining",
 +        "structures in pool.[PAR]",
 +
 +        "When the clustering algorithm assigns each structure to exactly one",
 +        "cluster (single linkage, Jarvis Patrick and gromos) and a trajectory",
 +        "file is supplied, the structure with",
 +        "the smallest average distance to the others or the average structure",
 +        "or all structures for each cluster will be written to a trajectory",
 +        "file. When writing all structures, separate numbered files are made",
 +        "for each cluster.[PAR]",
 +
 +        "Two output files are always written:[BR]",
 +        "[TT]-o[tt] writes the RMSD values in the upper left half of the matrix",
 +        "and a graphical depiction of the clusters in the lower right half",
 +        "When [TT]-minstruct[tt] = 1 the graphical depiction is black",
 +        "when two structures are in the same cluster.",
 +        "When [TT]-minstruct[tt] > 1 different colors will be used for each",
 +        "cluster.[BR]",
 +        "[TT]-g[tt] writes information on the options used and a detailed list",
 +        "of all clusters and their members.[PAR]",
 +
 +        "Additionally, a number of optional output files can be written:[BR]",
 +        "[TT]-dist[tt] writes the RMSD distribution.[BR]",
 +        "[TT]-ev[tt] writes the eigenvectors of the RMSD matrix",
 +        "diagonalization.[BR]",
 +        "[TT]-sz[tt] writes the cluster sizes.[BR]",
 +        "[TT]-tr[tt] writes a matrix of the number transitions between",
 +        "cluster pairs.[BR]",
 +        "[TT]-ntr[tt] writes the total number of transitions to or from",
 +        "each cluster.[BR]",
 +        "[TT]-clid[tt] writes the cluster number as a function of time.[BR]",
 +        "[TT]-cl[tt] writes average (with option [TT]-av[tt]) or central",
 +        "structure of each cluster or writes numbered files with cluster members",
 +        "for a selected set of clusters (with option [TT]-wcl[tt], depends on",
 +        "[TT]-nst[tt] and [TT]-rmsmin[tt]). The center of a cluster is the",
 +        "structure with the smallest average RMSD from all other structures",
 +        "of the cluster.[BR]",
 +    };
 +
 +    FILE              *fp, *log;
 +    int                nf, i, i1, i2, j;
 +    gmx_int64_t        nrms = 0;
 +
 +    matrix             box;
 +    rvec              *xtps, *usextps, *x1, **xx = NULL;
 +    const char        *fn, *trx_out_fn;
 +    t_clusters         clust;
 +    t_mat             *rms, *orig = NULL;
 +    real              *eigenvalues;
 +    t_topology         top;
 +    int                ePBC;
 +    t_atoms            useatoms;
 +    t_matrix          *readmat = NULL;
 +    real              *eigenvectors;
 +
 +    int                isize = 0, ifsize = 0, iosize = 0;
 +    atom_id           *index = NULL, *fitidx, *outidx;
 +    char              *grpname;
 +    real               rmsd, **d1, **d2, *time = NULL, time_invfac, *mass = NULL;
 +    char               buf[STRLEN], buf1[80], title[STRLEN];
 +    gmx_bool           bAnalyze, bUseRmsdCut, bJP_RMSD = FALSE, bReadMat, bReadTraj, bPBC = TRUE;
 +
 +    int                method, ncluster = 0;
 +    static const char *methodname[] = {
 +        NULL, "linkage", "jarvis-patrick", "monte-carlo",
 +        "diagonalization", "gromos", NULL
 +    };
 +    enum {
 +        m_null, m_linkage, m_jarvis_patrick,
 +        m_monte_carlo, m_diagonalize, m_gromos, m_nr
 +    };
 +    /* Set colors for plotting: white = zero RMS, black = maximum */
 +    static t_rgb rlo_top  = { 1.0, 1.0, 1.0 };
 +    static t_rgb rhi_top  = { 0.0, 0.0, 0.0 };
 +    static t_rgb rlo_bot  = { 1.0, 1.0, 1.0 };
 +    static t_rgb rhi_bot  = { 0.0, 0.0, 1.0 };
 +    static int   nlevels  = 40, skip = 1;
 +    static real  scalemax = -1.0, rmsdcut = 0.1, rmsmin = 0.0;
 +    gmx_bool     bRMSdist = FALSE, bBinary = FALSE, bAverage = FALSE, bFit = TRUE;
 +    static int   niter    = 10000, nrandom = 0, seed = 1993, write_ncl = 0, write_nst = 1, minstruct = 1;
 +    static real  kT       = 1e-3;
 +    static int   M        = 10, P = 3;
 +    output_env_t oenv;
 +    gmx_rmpbc_t  gpbc = NULL;
 +
 +    t_pargs      pa[] = {
 +        { "-dista", FALSE, etBOOL, {&bRMSdist},
 +          "Use RMSD of distances instead of RMS deviation" },
 +        { "-nlevels", FALSE, etINT,  {&nlevels},
 +          "Discretize RMSD matrix in this number of levels" },
 +        { "-cutoff", FALSE, etREAL, {&rmsdcut},
 +          "RMSD cut-off (nm) for two structures to be neighbor" },
 +        { "-fit",   FALSE, etBOOL, {&bFit},
 +          "Use least squares fitting before RMSD calculation" },
 +        { "-max",   FALSE, etREAL, {&scalemax},
 +          "Maximum level in RMSD matrix" },
 +        { "-skip",  FALSE, etINT,  {&skip},
 +          "Only analyze every nr-th frame" },
 +        { "-av",    FALSE, etBOOL, {&bAverage},
 +          "Write average iso middle structure for each cluster" },
 +        { "-wcl",   FALSE, etINT,  {&write_ncl},
 +          "Write the structures for this number of clusters to numbered files" },
 +        { "-nst",   FALSE, etINT,  {&write_nst},
 +          "Only write all structures if more than this number of structures per cluster" },
 +        { "-rmsmin", FALSE, etREAL, {&rmsmin},
 +          "minimum rms difference with rest of cluster for writing structures" },
 +        { "-method", FALSE, etENUM, {methodname},
 +          "Method for cluster determination" },
 +        { "-minstruct", FALSE, etINT, {&minstruct},
 +          "Minimum number of structures in cluster for coloring in the [TT].xpm[tt] file" },
 +        { "-binary", FALSE, etBOOL, {&bBinary},
 +          "Treat the RMSD matrix as consisting of 0 and 1, where the cut-off "
 +          "is given by [TT]-cutoff[tt]" },
 +        { "-M",     FALSE, etINT,  {&M},
 +          "Number of nearest neighbors considered for Jarvis-Patrick algorithm, "
 +          "0 is use cutoff" },
 +        { "-P",     FALSE, etINT,  {&P},
 +          "Number of identical nearest neighbors required to form a cluster" },
 +        { "-seed",  FALSE, etINT,  {&seed},
 +          "Random number seed for Monte Carlo clustering algorithm: <= 0 means generate" },
 +        { "-niter", FALSE, etINT,  {&niter},
 +          "Number of iterations for MC" },
 +        { "-nrandom", FALSE, etINT,  {&nrandom},
 +          "The first iterations for MC may be done complete random, to shuffle the frames" },
 +        { "-kT",    FALSE, etREAL, {&kT},
 +          "Boltzmann weighting factor for Monte Carlo optimization "
 +          "(zero turns off uphill steps)" },
 +        { "-pbc", FALSE, etBOOL,
 +          { &bPBC }, "PBC check" }
 +    };
 +    t_filenm     fnm[] = {
 +        { efTRX, "-f",     NULL,        ffOPTRD },
 +        { efTPS, "-s",     NULL,        ffOPTRD },
 +        { efNDX, NULL,     NULL,        ffOPTRD },
 +        { efXPM, "-dm",   "rmsd",       ffOPTRD },
 +        { efXPM, "-om",   "rmsd-raw",   ffWRITE },
 +        { efXPM, "-o",    "rmsd-clust", ffWRITE },
 +        { efLOG, "-g",    "cluster",    ffWRITE },
 +        { efXVG, "-dist", "rmsd-dist",  ffOPTWR },
 +        { efXVG, "-ev",   "rmsd-eig",   ffOPTWR },
 +        { efXVG, "-conv", "mc-conv",    ffOPTWR },
 +        { efXVG, "-sz",   "clust-size", ffOPTWR},
 +        { efXPM, "-tr",   "clust-trans", ffOPTWR},
 +        { efXVG, "-ntr",  "clust-trans", ffOPTWR},
 +        { efXVG, "-clid", "clust-id.xvg", ffOPTWR},
 +        { efTRX, "-cl",   "clusters.pdb", ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +
 +    if (!parse_common_args(&argc, argv,
 +                           PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
 +                           NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL,
 +                           &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    /* parse options */
 +    bReadMat   = opt2bSet("-dm", NFILE, fnm);
 +    bReadTraj  = opt2bSet("-f", NFILE, fnm) || !bReadMat;
 +    if (opt2parg_bSet("-av", asize(pa), pa) ||
 +        opt2parg_bSet("-wcl", asize(pa), pa) ||
 +        opt2parg_bSet("-nst", asize(pa), pa) ||
 +        opt2parg_bSet("-rmsmin", asize(pa), pa) ||
 +        opt2bSet("-cl", NFILE, fnm) )
 +    {
 +        trx_out_fn = opt2fn("-cl", NFILE, fnm);
 +    }
 +    else
 +    {
 +        trx_out_fn = NULL;
 +    }
 +    if (bReadMat && output_env_get_time_factor(oenv) != 1)
 +    {
 +        fprintf(stderr,
 +                "\nWarning: assuming the time unit in %s is %s\n",
 +                opt2fn("-dm", NFILE, fnm), output_env_get_time_unit(oenv));
 +    }
 +    if (trx_out_fn && !bReadTraj)
 +    {
 +        fprintf(stderr, "\nWarning: "
 +                "cannot write cluster structures without reading trajectory\n"
 +                "         ignoring option -cl %s\n", trx_out_fn);
 +    }
 +
 +    method = 1;
 +    while (method < m_nr && gmx_strcasecmp(methodname[0], methodname[method]) != 0)
 +    {
 +        method++;
 +    }
 +    if (method == m_nr)
 +    {
 +        gmx_fatal(FARGS, "Invalid method");
 +    }
 +
 +    bAnalyze = (method == m_linkage || method == m_jarvis_patrick ||
 +                method == m_gromos );
 +
 +    /* Open log file */
 +    log = ftp2FILE(efLOG, NFILE, fnm, "w");
 +
 +    fprintf(stderr, "Using %s method for clustering\n", methodname[0]);
 +    fprintf(log, "Using %s method for clustering\n", methodname[0]);
 +
 +    /* check input and write parameters to log file */
 +    bUseRmsdCut = FALSE;
 +    if (method == m_jarvis_patrick)
 +    {
 +        bJP_RMSD = (M == 0) || opt2parg_bSet("-cutoff", asize(pa), pa);
 +        if ((M < 0) || (M == 1))
 +        {
 +            gmx_fatal(FARGS, "M (%d) must be 0 or larger than 1", M);
 +        }
 +        if (M < 2)
 +        {
 +            sprintf(buf1, "Will use P=%d and RMSD cutoff (%g)", P, rmsdcut);
 +            bUseRmsdCut = TRUE;
 +        }
 +        else
 +        {
 +            if (P >= M)
 +            {
 +                gmx_fatal(FARGS, "Number of neighbors required (P) must be less than M");
 +            }
 +            if (bJP_RMSD)
 +            {
 +                sprintf(buf1, "Will use P=%d, M=%d and RMSD cutoff (%g)", P, M, rmsdcut);
 +                bUseRmsdCut = TRUE;
 +            }
 +            else
 +            {
 +                sprintf(buf1, "Will use P=%d, M=%d", P, M);
 +            }
 +        }
 +        ffprintf_s(stderr, log, buf, "%s for determining the neighbors\n\n", buf1);
 +    }
 +    else /* method != m_jarvis */
 +    {
 +        bUseRmsdCut = ( bBinary || method == m_linkage || method == m_gromos );
 +    }
 +    if (bUseRmsdCut && method != m_jarvis_patrick)
 +    {
 +        fprintf(log, "Using RMSD cutoff %g nm\n", rmsdcut);
 +    }
 +    if (method == m_monte_carlo)
 +    {
 +        fprintf(log, "Using %d iterations\n", niter);
 +    }
 +
 +    if (skip < 1)
 +    {
 +        gmx_fatal(FARGS, "skip (%d) should be >= 1", skip);
 +    }
 +
 +    /* get input */
 +    if (bReadTraj)
 +    {
 +        /* don't read mass-database as masses (and top) are not used */
 +        read_tps_conf(ftp2fn(efTPS, NFILE, fnm), buf, &top, &ePBC, &xtps, NULL, box,
 +                      TRUE);
 +        if (bPBC)
 +        {
 +            gpbc = gmx_rmpbc_init(&top.idef, ePBC, top.atoms.nr);
 +        }
 +
 +        fprintf(stderr, "\nSelect group for least squares fit%s:\n",
 +                bReadMat ? "" : " and RMSD calculation");
 +        get_index(&(top.atoms), ftp2fn_null(efNDX, NFILE, fnm),
 +                  1, &ifsize, &fitidx, &grpname);
 +        if (trx_out_fn)
 +        {
 +            fprintf(stderr, "\nSelect group for output:\n");
 +            get_index(&(top.atoms), ftp2fn_null(efNDX, NFILE, fnm),
 +                      1, &iosize, &outidx, &grpname);
 +            /* merge and convert both index groups: */
 +            /* first copy outidx to index. let outidx refer to elements in index */
 +            snew(index, iosize);
 +            isize = iosize;
 +            for (i = 0; i < iosize; i++)
 +            {
 +                index[i]  = outidx[i];
 +                outidx[i] = i;
 +            }
 +            /* now lookup elements from fitidx in index, add them if necessary
 +               and also let fitidx refer to elements in index */
 +            for (i = 0; i < ifsize; i++)
 +            {
 +                j = 0;
 +                while (j < isize && index[j] != fitidx[i])
 +                {
 +                    j++;
 +                }
 +                if (j >= isize)
 +                {
 +                    /* slow this way, but doesn't matter much */
 +                    isize++;
 +                    srenew(index, isize);
 +                }
 +                index[j]  = fitidx[i];
 +                fitidx[i] = j;
 +            }
 +        }
 +        else /* !trx_out_fn */
 +        {
 +            isize = ifsize;
 +            snew(index, isize);
 +            for (i = 0; i < ifsize; i++)
 +            {
 +                index[i]  = fitidx[i];
 +                fitidx[i] = i;
 +            }
 +        }
 +    }
 +
 +    if (bReadTraj)
 +    {
 +        /* Loop over first coordinate file */
 +        fn = opt2fn("-f", NFILE, fnm);
 +
 +        xx = read_whole_trj(fn, isize, index, skip, &nf, &time, oenv, bPBC, gpbc);
 +        output_env_conv_times(oenv, nf, time);
 +        if (!bRMSdist || bAnalyze)
 +        {
 +            /* Center all frames on zero */
 +            snew(mass, isize);
 +            for (i = 0; i < ifsize; i++)
 +            {
 +                mass[fitidx[i]] = top.atoms.atom[index[fitidx[i]]].m;
 +            }
 +            if (bFit)
 +            {
 +                for (i = 0; i < nf; i++)
 +                {
 +                    reset_x(ifsize, fitidx, isize, NULL, xx[i], mass);
 +                }
 +            }
 +        }
 +        if (bPBC)
 +        {
 +            gmx_rmpbc_done(gpbc);
 +        }
 +    }
 +
 +    if (bReadMat)
 +    {
 +        fprintf(stderr, "Reading rms distance matrix ");
 +        read_xpm_matrix(opt2fn("-dm", NFILE, fnm), &readmat);
 +        fprintf(stderr, "\n");
 +        if (readmat[0].nx != readmat[0].ny)
 +        {
 +            gmx_fatal(FARGS, "Matrix (%dx%d) is not square",
 +                      readmat[0].nx, readmat[0].ny);
 +        }
 +        if (bReadTraj && bAnalyze && (readmat[0].nx != nf))
 +        {
 +            gmx_fatal(FARGS, "Matrix size (%dx%d) does not match the number of "
 +                      "frames (%d)", readmat[0].nx, readmat[0].ny, nf);
 +        }
 +
 +        nf = readmat[0].nx;
 +        sfree(time);
 +        time        = readmat[0].axis_x;
 +        time_invfac = output_env_get_time_invfactor(oenv);
 +        for (i = 0; i < nf; i++)
 +        {
 +            time[i] *= time_invfac;
 +        }
 +
 +        rms = init_mat(readmat[0].nx, method == m_diagonalize);
 +        convert_mat(&(readmat[0]), rms);
 +
 +        nlevels = readmat[0].nmap;
 +    }
 +    else   /* !bReadMat */
 +    {
 +        rms  = init_mat(nf, method == m_diagonalize);
 +        nrms = ((gmx_int64_t)nf*((gmx_int64_t)nf-1))/2;
 +        if (!bRMSdist)
 +        {
 +            fprintf(stderr, "Computing %dx%d RMS deviation matrix\n", nf, nf);
 +            /* Initialize work array */
 +            snew(x1, isize);
 +            for (i1 = 0; i1 < nf; i1++)
 +            {
 +                for (i2 = i1+1; i2 < nf; i2++)
 +                {
 +                    for (i = 0; i < isize; i++)
 +                    {
 +                        copy_rvec(xx[i1][i], x1[i]);
 +                    }
 +                    if (bFit)
 +                    {
 +                        do_fit(isize, mass, xx[i2], x1);
 +                    }
 +                    rmsd = rmsdev(isize, mass, xx[i2], x1);
 +                    set_mat_entry(rms, i1, i2, rmsd);
 +                }
 +                nrms -= (gmx_int64_t) (nf-i1-1);
 +                fprintf(stderr, "\r# RMSD calculations left: " "%"GMX_PRId64 "   ", nrms);
 +            }
 +            sfree(x1);
 +        }
 +        else /* bRMSdist */
 +        {
 +            fprintf(stderr, "Computing %dx%d RMS distance deviation matrix\n", nf, nf);
 +
 +            /* Initiate work arrays */
 +            snew(d1, isize);
 +            snew(d2, isize);
 +            for (i = 0; (i < isize); i++)
 +            {
 +                snew(d1[i], isize);
 +                snew(d2[i], isize);
 +            }
 +            for (i1 = 0; i1 < nf; i1++)
 +            {
 +                calc_dist(isize, xx[i1], d1);
 +                for (i2 = i1+1; (i2 < nf); i2++)
 +                {
 +                    calc_dist(isize, xx[i2], d2);
 +                    set_mat_entry(rms, i1, i2, rms_dist(isize, d1, d2));
 +                }
 +                nrms -= (nf-i1-1);
 +                fprintf(stderr, "\r# RMSD calculations left: " "%"GMX_PRId64 "   ", nrms);
 +            }
 +            /* Clean up work arrays */
 +            for (i = 0; (i < isize); i++)
 +            {
 +                sfree(d1[i]);
 +                sfree(d2[i]);
 +            }
 +            sfree(d1);
 +            sfree(d2);
 +        }
 +        fprintf(stderr, "\n\n");
 +    }
 +    ffprintf_gg(stderr, log, buf, "The RMSD ranges from %g to %g nm\n",
 +                rms->minrms, rms->maxrms);
 +    ffprintf_g(stderr, log, buf, "Average RMSD is %g\n", 2*rms->sumrms/(nf*(nf-1)));
 +    ffprintf_d(stderr, log, buf, "Number of structures for matrix %d\n", nf);
 +    ffprintf_g(stderr, log, buf, "Energy of the matrix is %g.\n", mat_energy(rms));
 +    if (bUseRmsdCut && (rmsdcut < rms->minrms || rmsdcut > rms->maxrms) )
 +    {
 +        fprintf(stderr, "WARNING: rmsd cutoff %g is outside range of rmsd values "
 +                "%g to %g\n", rmsdcut, rms->minrms, rms->maxrms);
 +    }
 +    if (bAnalyze && (rmsmin < rms->minrms) )
 +    {
 +        fprintf(stderr, "WARNING: rmsd minimum %g is below lowest rmsd value %g\n",
 +                rmsmin, rms->minrms);
 +    }
 +    if (bAnalyze && (rmsmin > rmsdcut) )
 +    {
 +        fprintf(stderr, "WARNING: rmsd minimum %g is above rmsd cutoff %g\n",
 +                rmsmin, rmsdcut);
 +    }
 +
 +    /* Plot the rmsd distribution */
 +    rmsd_distribution(opt2fn("-dist", NFILE, fnm), rms, oenv);
 +
 +    if (bBinary)
 +    {
 +        for (i1 = 0; (i1 < nf); i1++)
 +        {
 +            for (i2 = 0; (i2 < nf); i2++)
 +            {
 +                if (rms->mat[i1][i2] < rmsdcut)
 +                {
 +                    rms->mat[i1][i2] = 0;
 +                }
 +                else
 +                {
 +                    rms->mat[i1][i2] = 1;
 +                }
 +            }
 +        }
 +    }
 +
 +    snew(clust.cl, nf);
 +    switch (method)
 +    {
 +        case m_linkage:
 +            /* Now sort the matrix and write it out again */
 +            gather(rms, rmsdcut, &clust);
 +            break;
 +        case m_diagonalize:
 +            /* Do a diagonalization */
 +            snew(eigenvalues, nf);
 +            snew(eigenvectors, nf*nf);
 +            memcpy(eigenvectors, rms->mat[0], nf*nf*sizeof(real));
 +            eigensolver(eigenvectors, nf, 0, nf, eigenvalues, rms->mat[0]);
 +            sfree(eigenvectors);
 +
 +            fp = xvgropen(opt2fn("-ev", NFILE, fnm), "RMSD matrix Eigenvalues",
 +                          "Eigenvector index", "Eigenvalues (nm\\S2\\N)", oenv);
 +            for (i = 0; (i < nf); i++)
 +            {
 +                fprintf(fp, "%10d  %10g\n", i, eigenvalues[i]);
 +            }
 +            gmx_ffclose(fp);
 +            break;
 +        case m_monte_carlo:
 +            orig     = init_mat(rms->nn, FALSE);
 +            orig->nn = rms->nn;
 +            copy_t_mat(orig, rms);
 +            mc_optimize(log, rms, time, niter, nrandom, seed, kT,
 +                        opt2fn_null("-conv", NFILE, fnm), oenv);
 +            break;
 +        case m_jarvis_patrick:
 +            jarvis_patrick(rms->nn, rms->mat, M, P, bJP_RMSD ? rmsdcut : -1, &clust);
 +            break;
 +        case m_gromos:
 +            gromos(rms->nn, rms->mat, rmsdcut, &clust);
 +            break;
 +        default:
 +            gmx_fatal(FARGS, "DEATH HORROR unknown method \"%s\"", methodname[0]);
 +    }
 +
 +    if (method == m_monte_carlo || method == m_diagonalize)
 +    {
 +        fprintf(stderr, "Energy of the matrix after clustering is %g.\n",
 +                mat_energy(rms));
 +    }
 +
 +    if (bAnalyze)
 +    {
 +        if (minstruct > 1)
 +        {
 +            ncluster = plot_clusters(nf, rms->mat, &clust, minstruct);
 +        }
 +        else
 +        {
 +            mark_clusters(nf, rms->mat, rms->maxrms, &clust);
 +        }
 +        init_t_atoms(&useatoms, isize, FALSE);
 +        snew(usextps, isize);
 +        useatoms.resinfo = top.atoms.resinfo;
 +        for (i = 0; i < isize; i++)
 +        {
 +            useatoms.atomname[i]    = top.atoms.atomname[index[i]];
 +            useatoms.atom[i].resind = top.atoms.atom[index[i]].resind;
 +            useatoms.nres           = max(useatoms.nres, useatoms.atom[i].resind+1);
 +            copy_rvec(xtps[index[i]], usextps[i]);
 +        }
 +        useatoms.nr = isize;
 +        analyze_clusters(nf, &clust, rms->mat, isize, &useatoms, usextps, mass, xx, time,
 +                         ifsize, fitidx, iosize, outidx,
 +                         bReadTraj ? trx_out_fn : NULL,
 +                         opt2fn_null("-sz", NFILE, fnm),
 +                         opt2fn_null("-tr", NFILE, fnm),
 +                         opt2fn_null("-ntr", NFILE, fnm),
 +                         opt2fn_null("-clid", NFILE, fnm),
 +                         bAverage, write_ncl, write_nst, rmsmin, bFit, log,
 +                         rlo_bot, rhi_bot, oenv);
 +    }
 +    gmx_ffclose(log);
 +
 +    if (bBinary && !bAnalyze)
 +    {
 +        /* Make the clustering visible */
 +        for (i2 = 0; (i2 < nf); i2++)
 +        {
 +            for (i1 = i2+1; (i1 < nf); i1++)
 +            {
 +                if (rms->mat[i1][i2])
 +                {
 +                    rms->mat[i1][i2] = rms->maxrms;
 +                }
 +            }
 +        }
 +    }
 +
 +    fp = opt2FILE("-o", NFILE, fnm, "w");
 +    fprintf(stderr, "Writing rms distance/clustering matrix ");
 +    if (bReadMat)
 +    {
 +        write_xpm(fp, 0, readmat[0].title, readmat[0].legend, readmat[0].label_x,
 +                  readmat[0].label_y, nf, nf, readmat[0].axis_x, readmat[0].axis_y,
 +                  rms->mat, 0.0, rms->maxrms, rlo_top, rhi_top, &nlevels);
 +    }
 +    else
 +    {
 +        sprintf(buf, "Time (%s)", output_env_get_time_unit(oenv));
 +        sprintf(title, "RMS%sDeviation / Cluster Index",
 +                bRMSdist ? " Distance " : " ");
 +        if (minstruct > 1)
 +        {
 +            write_xpm_split(fp, 0, title, "RMSD (nm)", buf, buf,
 +                            nf, nf, time, time, rms->mat, 0.0, rms->maxrms, &nlevels,
 +                            rlo_top, rhi_top, 0.0, (real) ncluster,
 +                            &ncluster, TRUE, rlo_bot, rhi_bot);
 +        }
 +        else
 +        {
 +            write_xpm(fp, 0, title, "RMSD (nm)", buf, buf,
 +                      nf, nf, time, time, rms->mat, 0.0, rms->maxrms,
 +                      rlo_top, rhi_top, &nlevels);
 +        }
 +    }
 +    fprintf(stderr, "\n");
 +    gmx_ffclose(fp);
 +    if (NULL != orig)
 +    {
 +        fp = opt2FILE("-om", NFILE, fnm, "w");
 +        sprintf(buf, "Time (%s)", output_env_get_time_unit(oenv));
 +        sprintf(title, "RMS%sDeviation", bRMSdist ? " Distance " : " ");
 +        write_xpm(fp, 0, title, "RMSD (nm)", buf, buf,
 +                  nf, nf, time, time, orig->mat, 0.0, orig->maxrms,
 +                  rlo_top, rhi_top, &nlevels);
 +        gmx_ffclose(fp);
 +        done_mat(&orig);
 +        sfree(orig);
 +    }
 +    /* now show what we've done */
 +    do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
 +    do_view(oenv, opt2fn_null("-sz", NFILE, fnm), "-nxy");
 +    if (method == m_diagonalize)
 +    {
 +        do_view(oenv, opt2fn_null("-ev", NFILE, fnm), "-nxy");
 +    }
 +    do_view(oenv, opt2fn("-dist", NFILE, fnm), "-nxy");
 +    if (bAnalyze)
 +    {
 +        do_view(oenv, opt2fn_null("-tr", NFILE, fnm), "-nxy");
 +        do_view(oenv, opt2fn_null("-ntr", NFILE, fnm), "-nxy");
 +        do_view(oenv, opt2fn_null("-clid", NFILE, fnm), "-nxy");
 +    }
 +    do_view(oenv, opt2fn_null("-conv", NFILE, fnm), NULL);
 +
 +    return 0;
 +}
index 8d5438c3fb980ff29c877e8611eccd45b7229467,0000000000000000000000000000000000000000..281e500a2a25cd6ed22dd385326a7bcce8d3d1b0
mode 100644,000000..100644
--- /dev/null
@@@ -1,588 -1,0 +1,591 @@@
-     fprintf(fp, "@    world 1e-05, 0, 1000, 1\n");
-     fprintf(fp, "@    xaxes scale Logarithmic\n");
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 2011,2012,2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +#include <stdio.h>
 +#include <math.h>
 +
 +#include "gromacs/fileio/confio.h"
 +#include "copyrite.h"
 +#include "gmx_fatal.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gstat.h"
 +#include "macros.h"
 +#include "gromacs/math/utilities.h"
 +#include "physics.h"
 +#include "index.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "gromacs/commandline/pargs.h"
 +#include <string.h>
 +#include "sysstuff.h"
 +#include "txtdump.h"
 +#include "typedefs.h"
 +#include "vec.h"
 +#include "xvgr.h"
 +#include "correl.h"
 +#include "gmx_ana.h"
 +#include "gromacs/fft/fft.h"
 +#include "gromacs/fileio/trxio.h"
 +
 +enum {
 +    VACF, MVACF, DOS, DOS_SOLID, DOS_DIFF, DOS_CP, DOS_S, DOS_A, DOS_E, DOS_NR
 +};
 +
 +static double FD(double Delta, double f)
 +{
 +    return (2*pow(Delta, -4.5)*pow(f, 7.5) -
 +            6*pow(Delta, -3)*pow(f, 5) -
 +            pow(Delta, -1.5)*pow(f, 3.5) +
 +            6*pow(Delta, -1.5)*pow(f, 2.5) +
 +            2*f - 2);
 +}
 +
 +static double YYY(double f, double y)
 +{
 +    return (2*pow(y*f, 3) - sqr(f)*y*(1+6*y) +
 +            (2+6*y)*f - 2);
 +}
 +
 +static double calc_compress(double y)
 +{
 +    if (y == 1)
 +    {
 +        return 0;
 +    }
 +    return ((1+y+sqr(y)-pow(y, 3))/(pow(1-y, 3)));
 +}
 +
 +static double bisector(double Delta, double tol,
 +                       double ff0, double ff1,
 +                       double ff(double, double))
 +{
 +    double fd0, fd, fd1, f, f0, f1;
 +    double tolmin = 1e-8;
 +
 +    f0 = ff0;
 +    f1 = ff1;
 +    if (tol < tolmin)
 +    {
 +        fprintf(stderr, "Unrealistic tolerance %g for bisector. Setting it to %g\n", tol, tolmin);
 +        tol = tolmin;
 +    }
 +
 +    do
 +    {
 +        fd0 = ff(Delta, f0);
 +        fd1 = ff(Delta, f1);
 +        f   = (f0+f1)*0.5;
 +        fd  = ff(Delta, f);
 +        if (fd < 0)
 +        {
 +            f0 = f;
 +        }
 +        else if (fd > 0)
 +        {
 +            f1 = f;
 +        }
 +        else
 +        {
 +            return f;
 +        }
 +    }
 +    while ((f1-f0) > tol);
 +
 +    return f;
 +}
 +
 +static double calc_fluidicity(double Delta, double tol)
 +{
 +    return bisector(Delta, tol, 0, 1, FD);
 +}
 +
 +static double calc_y(double f, double Delta, double toler)
 +{
 +    double y1, y2;
 +
 +    y1 = pow(f/Delta, 1.5);
 +    y2 = bisector(f, toler, 0, 10000, YYY);
 +    if (fabs((y1-y2)/(y1+y2)) > 100*toler)
 +    {
 +        fprintf(stderr, "Inconsistency computing y: y1 = %f, y2 = %f, using y1.\n",
 +                y1, y2);
 +    }
 +
 +    return y1;
 +}
 +
 +static double calc_Shs(double f, double y)
 +{
 +    double fy  = f*y;
 +
 +    return BOLTZ*(log(calc_compress(fy)) + fy*(3*fy-4)/sqr(1-fy));
 +}
 +
 +static real wCsolid(real nu, real beta)
 +{
 +    real bhn = beta*PLANCK*nu;
 +    real ebn, koko;
 +
 +    if (bhn == 0)
 +    {
 +        return 1.0;
 +    }
 +    else
 +    {
 +        ebn  = exp(bhn);
 +        koko = sqr(1-ebn);
 +        return sqr(bhn)*ebn/koko;
 +    }
 +}
 +
 +static real wSsolid(real nu, real beta)
 +{
 +    real bhn = beta*PLANCK*nu;
 +
 +    if (bhn == 0)
 +    {
 +        return 1;
 +    }
 +    else
 +    {
 +        return bhn/(exp(bhn)-1) - log(1-exp(-bhn));
 +    }
 +}
 +
 +static real wAsolid(real nu, real beta)
 +{
 +    real bhn = beta*PLANCK*nu;
 +
 +    if (bhn == 0)
 +    {
 +        return 0;
 +    }
 +    else
 +    {
 +        return log((1-exp(-bhn))/(exp(-bhn/2))) - log(bhn);
 +    }
 +}
 +
 +static real wEsolid(real nu, real beta)
 +{
 +    real bhn = beta*PLANCK*nu;
 +
 +    if (bhn == 0)
 +    {
 +        return 1;
 +    }
 +    else
 +    {
 +        return bhn/2 + bhn/(exp(bhn)-1)-1;
 +    }
 +}
 +
 +static void dump_fy(output_env_t oenv, real toler)
 +{
 +    FILE       *fp;
 +    double      Delta, f, y, DD;
 +    const char *leg[] = { "f", "fy", "y" };
 +
 +    DD = pow(10.0, 0.125);
 +    fp = xvgropen("fy.xvg", "Fig. 2, Lin2003a", "Delta", "y or fy", oenv);
 +    xvgr_legend(fp, asize(leg), leg, oenv);
++    if (output_env_get_print_xvgr_codes(oenv))
++    {
++        fprintf(fp, "@    world 1e-05, 0, 1000, 1\n");
++        fprintf(fp, "@    xaxes scale Logarithmic\n");
++    }
 +    for (Delta = 1e-5; (Delta <= 1000); Delta *= DD)
 +    {
 +        f = calc_fluidicity(Delta, toler);
 +        y = calc_y(f, Delta, toler);
 +        fprintf(fp, "%10g  %10g  %10g  %10g\n", Delta, f, f*y, y);
 +    }
 +    xvgrclose(fp);
 +}
 +
 +static void dump_w(output_env_t oenv, real beta)
 +{
 +    FILE       *fp;
 +    double      nu;
 +    const char *leg[] = { "wCv", "wS", "wA", "wE" };
 +
 +    fp = xvgropen("w.xvg", "Fig. 1, Berens1983a", "\\f{12}b\\f{4}h\\f{12}n",
 +                  "w", oenv);
 +    xvgr_legend(fp, asize(leg), leg, oenv);
 +    for (nu = 1; (nu < 100); nu += 0.05)
 +    {
 +        fprintf(fp, "%10g  %10g  %10g  %10g  %10g\n", beta*PLANCK*nu,
 +                wCsolid(nu, beta), wSsolid(nu, beta),
 +                wAsolid(nu, beta), wEsolid(nu, beta));
 +    }
 +    xvgrclose(fp);
 +}
 +
 +int gmx_dos(int argc, char *argv[])
 +{
 +    const char         *desc[] = {
 +        "[THISMODULE] computes the Density of States from a simulations.",
 +        "In order for this to be meaningful the velocities must be saved",
 +        "in the trajecotry with sufficiently high frequency such as to cover",
 +        "all vibrations. For flexible systems that would be around a few fs",
 +        "between saving. Properties based on the DoS are printed on the",
 +        "standard output."
 +    };
 +    const char         *bugs[] = {
 +        "This program needs a lot of memory: total usage equals the number of atoms times 3 times number of frames times 4 (or 8 when run in double precision)."
 +    };
 +    FILE               *fp, *fplog;
 +    t_topology          top;
 +    int                 ePBC = -1;
 +    t_trxframe          fr;
 +    matrix              box;
 +    int                 gnx;
 +    char                title[256];
 +    real                t0, t1, m;
 +    t_trxstatus        *status;
 +    int                 nV, nframes, n_alloc, i, j, k, l, fftcode, Nmol, Natom;
 +    double              rho, dt, V2sum, Vsum, V, tmass, dostot, dos2, dosabs;
 +    real              **c1, **dos, mi, beta, bfac, *nu, *tt, stddev, c1j;
 +    output_env_t        oenv;
 +    gmx_fft_t           fft;
 +    double              cP, S, A, E, DiffCoeff, Delta, f, y, z, sigHS, Shs, Sig, DoS0, recip_fac;
 +    double              wCdiff, wSdiff, wAdiff, wEdiff;
 +
 +    static     gmx_bool bVerbose = TRUE, bAbsolute = FALSE, bNormalize = FALSE;
 +    static     gmx_bool bRecip   = FALSE, bDump = FALSE;
 +    static     real     Temp     = 298.15, toler = 1e-6;
 +    t_pargs             pa[]     = {
 +        { "-v", FALSE, etBOOL, {&bVerbose},
 +          "Be loud and noisy." },
 +        { "-recip", FALSE, etBOOL, {&bRecip},
 +          "Use cm^-1 on X-axis instead of 1/ps for DoS plots." },
 +        { "-abs", FALSE, etBOOL, {&bAbsolute},
 +          "Use the absolute value of the Fourier transform of the VACF as the Density of States. Default is to use the real component only" },
 +        { "-normdos", FALSE, etBOOL, {&bNormalize},
 +          "Normalize the DoS such that it adds up to 3N. This is a hack that should not be necessary." },
 +        { "-T", FALSE, etREAL, {&Temp},
 +          "Temperature in the simulation" },
 +        { "-toler", FALSE, etREAL, {&toler},
 +          "[HIDDEN]Tolerance when computing the fluidicity using bisection algorithm" },
 +        { "-dump", FALSE, etBOOL, {&bDump},
 +          "[HIDDEN]Dump the y/fy plot corresponding to Fig. 2 inLin2003a and the and the weighting functions corresponding to Fig. 1 in Berens1983a." }
 +    };
 +
 +    t_filenm            fnm[] = {
 +        { efTRN, "-f",    NULL,    ffREAD  },
 +        { efTPX, "-s",    NULL,    ffREAD  },
 +        { efNDX, NULL,    NULL,    ffOPTRD },
 +        { efXVG, "-vacf", "vacf",  ffWRITE },
 +        { efXVG, "-mvacf", "mvacf", ffWRITE },
 +        { efXVG, "-dos",  "dos",   ffWRITE },
 +        { efLOG, "-g",    "dos",   ffWRITE },
 +    };
 +#define NFILE asize(fnm)
 +    int                 npargs;
 +    t_pargs            *ppa;
 +    const char         *DoSlegend[] = {
 +        "DoS(v)", "DoS(v)[Solid]", "DoS(v)[Diff]"
 +    };
 +
 +    npargs = asize(pa);
 +    ppa    = add_acf_pargs(&npargs, pa);
 +    if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
 +                           NFILE, fnm, npargs, ppa, asize(desc), desc,
 +                           asize(bugs), bugs, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    beta = 1/(Temp*BOLTZ);
 +    if (bDump)
 +    {
 +        printf("Dumping reference figures. Thanks for your patience.\n");
 +        dump_fy(oenv, toler);
 +        dump_w(oenv, beta);
 +        exit(0);
 +    }
 +
 +    fplog = gmx_fio_fopen(ftp2fn(efLOG, NFILE, fnm), "w");
 +    fprintf(fplog, "Doing density of states analysis based on trajectory.\n");
 +    please_cite(fplog, "Pascal2011a");
 +    please_cite(fplog, "Caleman2011b");
 +
 +    read_tps_conf(ftp2fn(efTPX, NFILE, fnm), title, &top, &ePBC, NULL, NULL, box,
 +                  TRUE);
 +    V     = det(box);
 +    tmass = 0;
 +    for (i = 0; (i < top.atoms.nr); i++)
 +    {
 +        tmass += top.atoms.atom[i].m;
 +    }
 +
 +    Natom = top.atoms.nr;
 +    Nmol  = top.mols.nr;
 +    gnx   = Natom*DIM;
 +
 +    /* Correlation stuff */
 +    snew(c1, gnx);
 +    for (i = 0; (i < gnx); i++)
 +    {
 +        c1[i] = NULL;
 +    }
 +
 +    read_first_frame(oenv, &status, ftp2fn(efTRN, NFILE, fnm), &fr, TRX_NEED_V);
 +    t0 = fr.time;
 +
 +    n_alloc = 0;
 +    nframes = 0;
 +    Vsum    = V2sum = 0;
 +    nV      = 0;
 +    do
 +    {
 +        if (fr.bBox)
 +        {
 +            V      = det(fr.box);
 +            V2sum += V*V;
 +            Vsum  += V;
 +            nV++;
 +        }
 +        if (nframes >= n_alloc)
 +        {
 +            n_alloc += 100;
 +            for (i = 0; i < gnx; i++)
 +            {
 +                srenew(c1[i], n_alloc);
 +            }
 +        }
 +        for (i = 0; i < gnx; i += DIM)
 +        {
 +            c1[i+XX][nframes] = fr.v[i/DIM][XX];
 +            c1[i+YY][nframes] = fr.v[i/DIM][YY];
 +            c1[i+ZZ][nframes] = fr.v[i/DIM][ZZ];
 +        }
 +
 +        t1 = fr.time;
 +
 +        nframes++;
 +    }
 +    while (read_next_frame(oenv, status, &fr));
 +
 +    close_trj(status);
 +
 +    dt = (t1-t0)/(nframes-1);
 +    if (nV > 0)
 +    {
 +        V = Vsum/nV;
 +    }
 +    if (bVerbose)
 +    {
 +        printf("Going to do %d fourier transforms of length %d. Hang on.\n",
 +               gnx, nframes);
 +    }
 +    low_do_autocorr(NULL, oenv, NULL, nframes, gnx, nframes, c1, dt, eacNormal, 0, FALSE,
 +                    FALSE, FALSE, -1, -1, 0);
 +    snew(dos, DOS_NR);
 +    for (j = 0; (j < DOS_NR); j++)
 +    {
 +        snew(dos[j], nframes+4);
 +    }
 +
 +    if (bVerbose)
 +    {
 +        printf("Going to merge the ACFs into the mass-weighted and plain ACF\n");
 +    }
 +    for (i = 0; (i < gnx); i += DIM)
 +    {
 +        mi = top.atoms.atom[i/DIM].m;
 +        for (j = 0; (j < nframes/2); j++)
 +        {
 +            c1j            = (c1[i+XX][j] + c1[i+YY][j] + c1[i+ZZ][j]);
 +            dos[VACF][j]  += c1j/Natom;
 +            dos[MVACF][j] += mi*c1j;
 +        }
 +    }
 +    fp = xvgropen(opt2fn("-vacf", NFILE, fnm), "Velocity ACF",
 +                  "Time (ps)", "C(t)", oenv);
 +    snew(tt, nframes/2);
 +    for (j = 0; (j < nframes/2); j++)
 +    {
 +        tt[j] = j*dt;
 +        fprintf(fp, "%10g  %10g\n", tt[j], dos[VACF][j]);
 +    }
 +    xvgrclose(fp);
 +    fp = xvgropen(opt2fn("-mvacf", NFILE, fnm), "Mass-weighted velocity ACF",
 +                  "Time (ps)", "C(t)", oenv);
 +    for (j = 0; (j < nframes/2); j++)
 +    {
 +        fprintf(fp, "%10g  %10g\n", tt[j], dos[MVACF][j]);
 +    }
 +    xvgrclose(fp);
 +
 +    if ((fftcode = gmx_fft_init_1d_real(&fft, nframes/2,
 +                                        GMX_FFT_FLAG_NONE)) != 0)
 +    {
 +        gmx_fatal(FARGS, "gmx_fft_init_1d_real returned %d", fftcode);
 +    }
 +    if ((fftcode = gmx_fft_1d_real(fft, GMX_FFT_REAL_TO_COMPLEX,
 +                                   (void *)dos[MVACF], (void *)dos[DOS])) != 0)
 +    {
 +        gmx_fatal(FARGS, "gmx_fft_1d_real returned %d", fftcode);
 +    }
 +
 +    /* First compute the DoS */
 +    /* Magic factor of 8 included now. */
 +    bfac = 8*dt*beta/2;
 +    dos2 = 0;
 +    snew(nu, nframes/4);
 +    for (j = 0; (j < nframes/4); j++)
 +    {
 +        nu[j] = 2*j/(t1-t0);
 +        dos2 += sqr(dos[DOS][2*j]) + sqr(dos[DOS][2*j+1]);
 +        if (bAbsolute)
 +        {
 +            dos[DOS][j] = bfac*sqrt(sqr(dos[DOS][2*j]) + sqr(dos[DOS][2*j+1]));
 +        }
 +        else
 +        {
 +            dos[DOS][j] = bfac*dos[DOS][2*j];
 +        }
 +    }
 +    /* Normalize it */
 +    dostot = evaluate_integral(nframes/4, nu, dos[DOS], NULL, nframes/4, &stddev);
 +    if (bNormalize)
 +    {
 +        for (j = 0; (j < nframes/4); j++)
 +        {
 +            dos[DOS][j] *= 3*Natom/dostot;
 +        }
 +    }
 +
 +    /* Now analyze it */
 +    DoS0 = dos[DOS][0];
 +
 +    /* Note this eqn. is incorrect in Pascal2011a! */
 +    Delta = ((2*DoS0/(9*Natom))*sqrt(M_PI*BOLTZ*Temp*Natom/tmass)*
 +             pow((Natom/V), 1.0/3.0)*pow(6/M_PI, 2.0/3.0));
 +    f     = calc_fluidicity(Delta, toler);
 +    y     = calc_y(f, Delta, toler);
 +    z     = calc_compress(y);
 +    Sig   = BOLTZ*(5.0/2.0+log(2*M_PI*BOLTZ*Temp/(sqr(PLANCK))*V/(f*Natom)));
 +    Shs   = Sig+calc_Shs(f, y);
 +    rho   = (tmass*AMU)/(V*NANO*NANO*NANO);
 +    sigHS = pow(6*y*V/(M_PI*Natom), 1.0/3.0);
 +
 +    fprintf(fplog, "System = \"%s\"\n", title);
 +    fprintf(fplog, "Nmol = %d\n", Nmol);
 +    fprintf(fplog, "Natom = %d\n", Natom);
 +    fprintf(fplog, "dt = %g ps\n", dt);
 +    fprintf(fplog, "tmass = %g amu\n", tmass);
 +    fprintf(fplog, "V = %g nm^3\n", V);
 +    fprintf(fplog, "rho = %g g/l\n", rho);
 +    fprintf(fplog, "T = %g K\n", Temp);
 +    fprintf(fplog, "beta = %g mol/kJ\n", beta);
 +
 +    fprintf(fplog, "\nDoS parameters\n");
 +    fprintf(fplog, "Delta = %g\n", Delta);
 +    fprintf(fplog, "fluidicity = %g\n", f);
 +    fprintf(fplog, "hard sphere packing fraction = %g\n", y);
 +    fprintf(fplog, "hard sphere compressibility = %g\n", z);
 +    fprintf(fplog, "ideal gas entropy = %g\n", Sig);
 +    fprintf(fplog, "hard sphere entropy = %g\n", Shs);
 +    fprintf(fplog, "sigma_HS = %g nm\n", sigHS);
 +    fprintf(fplog, "DoS0 = %g\n", DoS0);
 +    fprintf(fplog, "Dos2 = %g\n", dos2);
 +    fprintf(fplog, "DoSTot = %g\n", dostot);
 +
 +    /* Now compute solid (2) and diffusive (3) components */
 +    fp = xvgropen(opt2fn("-dos", NFILE, fnm), "Density of states",
 +                  bRecip ? "E (cm\\S-1\\N)" : "\\f{12}n\\f{4} (1/ps)",
 +                  "\\f{4}S(\\f{12}n\\f{4})", oenv);
 +    xvgr_legend(fp, asize(DoSlegend), DoSlegend, oenv);
 +    recip_fac = bRecip ? (1e7/SPEED_OF_LIGHT) : 1.0;
 +    for (j = 0; (j < nframes/4); j++)
 +    {
 +        dos[DOS_DIFF][j]  = DoS0/(1+sqr(DoS0*M_PI*nu[j]/(6*f*Natom)));
 +        dos[DOS_SOLID][j] = dos[DOS][j]-dos[DOS_DIFF][j];
 +        fprintf(fp, "%10g  %10g  %10g  %10g\n",
 +                recip_fac*nu[j],
 +                dos[DOS][j]/recip_fac,
 +                dos[DOS_SOLID][j]/recip_fac,
 +                dos[DOS_DIFF][j]/recip_fac);
 +    }
 +    xvgrclose(fp);
 +
 +    /* Finally analyze the results! */
 +    wCdiff = 0.5;
 +    wSdiff = Shs/(3*BOLTZ); /* Is this correct? */
 +    wEdiff = 0.5;
 +    wAdiff = wEdiff-wSdiff;
 +    for (j = 0; (j < nframes/4); j++)
 +    {
 +        dos[DOS_CP][j] = (dos[DOS_DIFF][j]*wCdiff +
 +                          dos[DOS_SOLID][j]*wCsolid(nu[j], beta));
 +        dos[DOS_S][j]  = (dos[DOS_DIFF][j]*wSdiff +
 +                          dos[DOS_SOLID][j]*wSsolid(nu[j], beta));
 +        dos[DOS_A][j]  = (dos[DOS_DIFF][j]*wAdiff +
 +                          dos[DOS_SOLID][j]*wAsolid(nu[j], beta));
 +        dos[DOS_E][j]  = (dos[DOS_DIFF][j]*wEdiff +
 +                          dos[DOS_SOLID][j]*wEsolid(nu[j], beta));
 +    }
 +    DiffCoeff = evaluate_integral(nframes/2, tt, dos[VACF], NULL, nframes/2, &stddev);
 +    DiffCoeff = 1000*DiffCoeff/3.0;
 +    fprintf(fplog, "Diffusion coefficient from VACF %g 10^-5 cm^2/s\n",
 +            DiffCoeff);
 +    fprintf(fplog, "Diffusion coefficient from DoS %g 10^-5 cm^2/s\n",
 +            1000*DoS0/(12*tmass*beta));
 +
 +    cP = BOLTZ * evaluate_integral(nframes/4, nu, dos[DOS_CP], NULL,
 +                                   nframes/4, &stddev);
 +    fprintf(fplog, "Heat capacity %g J/mol K\n", 1000*cP/Nmol);
 +
 +    /*
 +       S  = BOLTZ * evaluate_integral(nframes/4,nu,dos[DOS_S],NULL,
 +                                   nframes/4,&stddev);
 +       fprintf(fplog,"Entropy %g J/mol K\n",1000*S/Nmol);
 +       A  = BOLTZ * evaluate_integral(nframes/4,nu,dos[DOS_A],NULL,
 +                                   nframes/4,&stddev);
 +       fprintf(fplog,"Helmholtz energy %g kJ/mol\n",A/Nmol);
 +       E  = BOLTZ * evaluate_integral(nframes/4,nu,dos[DOS_E],NULL,
 +                                   nframes/4,&stddev);
 +       fprintf(fplog,"Internal energy %g kJ/mol\n",E/Nmol);
 +     */
 +    fprintf(fplog, "\nArrivederci!\n");
 +    gmx_fio_fclose(fplog);
 +
 +    do_view(oenv, ftp2fn(efXVG, NFILE, fnm), "-nxy");
 +
 +    return 0;
 +}
index f0c7730eb74f6f1373f2cdf893977bd6676745c8,0000000000000000000000000000000000000000..4e57212642cc76a12c9c750c5c55be20a3f5e059
mode 100644,000000..100644
--- /dev/null
@@@ -1,593 -1,0 +1,609 @@@
-         for (m = 0; (m < egNR+egSP); m++)
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +#include <string.h>
 +#include <math.h>
 +
 +#include "gromacs/utility/cstringutil.h"
 +#include "typedefs.h"
 +#include "gmx_fatal.h"
 +#include "vec.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "gromacs/fileio/enxio.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "names.h"
 +#include "macros.h"
 +#include "xvgr.h"
 +#include "gstat.h"
 +#include "physics.h"
 +#include "gromacs/fileio/matio.h"
 +#include "gromacs/fileio/strdb.h"
 +#include "gmx_ana.h"
 +#include "gromacs/fileio/trxio.h"
 +
 +
 +static int search_str2(int nstr, char **str, char *key)
 +{
 +    int  i, n;
 +    int  keylen = strlen(key);
 +    /* Linear search */
 +    n = 0;
 +    while ( (n < keylen) && ((key[n] < '0') || (key[n] > '9')) )
 +    {
 +        n++;
 +    }
 +    for (i = 0; (i < nstr); i++)
 +    {
 +        if (gmx_strncasecmp(str[i], key, n) == 0)
 +        {
 +            return i;
 +        }
 +    }
 +
 +    return -1;
 +}
 +
 +int gmx_enemat(int argc, char *argv[])
 +{
 +    const char     *desc[] = {
 +        "[THISMODULE] extracts an energy matrix from the energy file ([TT]-f[tt]).",
 +        "With [TT]-groups[tt] a file must be supplied with on each",
 +        "line a group of atoms to be used. For these groups matrix of",
 +        "interaction energies will be extracted from the energy file",
 +        "by looking for energy groups with names corresponding to pairs",
 +        "of groups of atoms, e.g. if your [TT]-groups[tt] file contains:[BR]",
 +        "[TT]2[tt][BR]",
 +        "[TT]Protein[tt][BR]",
 +        "[TT]SOL[tt][BR]",
 +        "then energy groups with names like 'Coul-SR:Protein-SOL' and ",
 +        "'LJ:Protein-SOL' are expected in the energy file (although",
 +        "[THISMODULE] is most useful if many groups are analyzed",
 +        "simultaneously). Matrices for different energy types are written",
 +        "out separately, as controlled by the",
 +        "[TT]-[no]coul[tt], [TT]-[no]coulr[tt], [TT]-[no]coul14[tt], ",
 +        "[TT]-[no]lj[tt], [TT]-[no]lj14[tt], ",
 +        "[TT]-[no]bham[tt] and [TT]-[no]free[tt] options.",
 +        "Finally, the total interaction energy energy per group can be ",
 +        "calculated ([TT]-etot[tt]).[PAR]",
 +
 +        "An approximation of the free energy can be calculated using:",
 +        "[MATH]E[SUB]free[sub] = E[SUB]0[sub] + kT [LOG][CHEVRON][EXP](E-E[SUB]0[sub])/kT[exp][chevron][log][math], where '[MATH][CHEVRON][chevron][math]'",
 +        "stands for time-average. A file with reference free energies",
 +        "can be supplied to calculate the free energy difference",
 +        "with some reference state. Group names (e.g. residue names)",
 +        "in the reference file should correspond to the group names",
 +        "as used in the [TT]-groups[tt] file, but a appended number",
 +        "(e.g. residue number) in the [TT]-groups[tt] will be ignored",
 +        "in the comparison."
 +    };
 +    static gmx_bool bSum      = FALSE;
 +    static gmx_bool bMeanEmtx = TRUE;
 +    static int      skip      = 0, nlevels = 20;
 +    static real     cutmax    = 1e20, cutmin = -1e20, reftemp = 300.0;
 +    static gmx_bool bCoulSR   = TRUE, bCoulLR = FALSE, bCoul14 = FALSE;
 +    static gmx_bool bLJSR     = TRUE, bLJLR = FALSE, bLJ14 = FALSE, bBhamSR = FALSE, bBhamLR = FALSE,
 +                    bFree     = TRUE;
 +    t_pargs         pa[]      = {
 +        { "-sum",  FALSE, etBOOL, {&bSum},
 +          "Sum the energy terms selected rather than display them all" },
 +        { "-skip", FALSE, etINT,  {&skip},
 +          "Skip number of frames between data points" },
 +        { "-mean", FALSE, etBOOL, {&bMeanEmtx},
 +          "with [TT]-groups[tt] extracts matrix of mean energies instead of "
 +          "matrix for each timestep" },
 +        { "-nlevels", FALSE, etINT, {&nlevels}, "number of levels for matrix colors"},
 +        { "-max", FALSE, etREAL, {&cutmax}, "max value for energies"},
 +        { "-min", FALSE, etREAL, {&cutmin}, "min value for energies"},
 +        { "-coulsr", FALSE, etBOOL, {&bCoulSR}, "extract Coulomb SR energies"},
 +        { "-coullr", FALSE, etBOOL, {&bCoulLR}, "extract Coulomb LR energies"},
 +        { "-coul14", FALSE, etBOOL, {&bCoul14}, "extract Coulomb 1-4 energies"},
 +        { "-ljsr", FALSE, etBOOL, {&bLJSR}, "extract Lennard-Jones SR energies"},
 +        { "-ljlr", FALSE, etBOOL, {&bLJLR}, "extract Lennard-Jones LR energies"},
 +        { "-lj14", FALSE, etBOOL, {&bLJ14}, "extract Lennard-Jones 1-4 energies"},
 +        { "-bhamsr", FALSE, etBOOL, {&bBhamSR}, "extract Buckingham SR energies"},
 +        { "-bhamlr", FALSE, etBOOL, {&bBhamLR}, "extract Buckingham LR energies"},
 +        { "-free", FALSE, etBOOL, {&bFree}, "calculate free energy"},
 +        { "-temp", FALSE, etREAL, {&reftemp},
 +          "reference temperature for free energy calculation"}
 +    };
 +    /* We will define egSP more energy-groups:
 +       egTotal (total energy) */
 +#define egTotal egNR
 +#define egSP 1
 +    gmx_bool       egrp_use[egNR+egSP];
 +    ener_file_t    in;
 +    FILE          *out;
 +    int            timecheck = 0;
 +    gmx_enxnm_t   *enm       = NULL;
 +    t_enxframe    *fr;
 +    int            teller = 0;
 +    real           sum;
 +    gmx_bool       bCont, bRef;
 +    gmx_bool       bCutmax, bCutmin;
 +    real         **eneset, *time = NULL;
 +    int           *set, i, j, k, prevk, m = 0, n, nre, nset, nenergy;
 +    char         **groups = NULL;
 +    char           groupname[255], fn[255];
 +    int            ngroups;
 +    t_rgb          rlo, rhi, rmid;
 +    real           emax, emid, emin;
 +    real        ***emat, **etot, *groupnr;
 +    double         beta, expE, **e, *eaver, *efree = NULL, edum;
 +    char           label[234];
 +    char         **ereflines, **erefres = NULL;
 +    real          *eref  = NULL, *edif = NULL;
 +    int            neref = 0;
 +    output_env_t   oenv;
 +
 +    t_filenm       fnm[] = {
 +        { efEDR, "-f", NULL, ffOPTRD },
 +        { efDAT, "-groups", "groups.dat", ffREAD },
 +        { efDAT, "-eref",   "eref.dat", ffOPTRD },
 +        { efXPM, "-emat",   "emat", ffWRITE },
 +        { efXVG, "-etot",   "energy", ffWRITE }
 +    };
 +#define NFILE asize(fnm)
 +
 +    if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
 +                           NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    for (i = 0; (i < egNR+egSP); i++)
 +    {
 +        egrp_use[i] = FALSE;
 +    }
 +    egrp_use[egCOULSR] = bCoulSR;
 +    egrp_use[egLJSR]   = bLJSR;
 +    egrp_use[egBHAMSR] = bBhamSR;
 +    egrp_use[egCOULLR] = bCoulLR;
 +    egrp_use[egLJLR]   = bLJLR;
 +    egrp_use[egBHAMLR] = bBhamLR;
 +    egrp_use[egCOUL14] = bCoul14;
 +    egrp_use[egLJ14]   = bLJ14;
 +    egrp_use[egTotal]  = TRUE;
 +
 +    bRef = opt2bSet("-eref", NFILE, fnm);
 +    in   = open_enx(ftp2fn(efEDR, NFILE, fnm), "r");
 +    do_enxnms(in, &nre, &enm);
 +
 +    if (nre == 0)
 +    {
 +        gmx_fatal(FARGS, "No energies!\n");
 +    }
 +
 +    bCutmax = opt2parg_bSet("-max", asize(pa), pa);
 +    bCutmin = opt2parg_bSet("-min", asize(pa), pa);
 +
 +    nenergy = 0;
 +
 +    /* Read groupnames from input file and construct selection of
 +       energy groups from it*/
 +
 +    fprintf(stderr, "Will read groupnames from inputfile\n");
 +    ngroups = get_lines(opt2fn("-groups", NFILE, fnm), &groups);
 +    fprintf(stderr, "Read %d groups\n", ngroups);
 +    snew(set, sqr(ngroups)*egNR/2);
 +    n     = 0;
 +    prevk = 0;
 +    for (i = 0; (i < ngroups); i++)
 +    {
 +        fprintf(stderr, "\rgroup %d", i);
 +        for (j = i; (j < ngroups); j++)
 +        {
 +            for (m = 0; (m < egNR); m++)
 +            {
 +                if (egrp_use[m])
 +                {
 +                    sprintf(groupname, "%s:%s-%s", egrp_nm[m], groups[i], groups[j]);
 +#ifdef DEBUG
 +                    fprintf(stderr, "\r%-15s %5d", groupname, n);
 +#endif
 +                    for (k = prevk; (k < prevk+nre); k++)
 +                    {
 +                        if (strcmp(enm[k%nre].name, groupname) == 0)
 +                        {
 +                            set[n++] = k;
 +                            break;
 +                        }
 +                    }
 +                    if (k == prevk+nre)
 +                    {
 +                        fprintf(stderr, "WARNING! could not find group %s (%d,%d)"
 +                                "in energy file\n", groupname, i, j);
 +                    }
 +                    else
 +                    {
 +                        prevk = k;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    fprintf(stderr, "\n");
 +    nset = n;
 +    snew(eneset, nset+1);
 +    fprintf(stderr, "Will select half-matrix of energies with %d elements\n", n);
 +
 +    /* Start reading energy frames */
 +    snew(fr, 1);
 +    do
 +    {
 +        do
 +        {
 +            bCont = do_enx(in, fr);
 +            if (bCont)
 +            {
 +                timecheck = check_times(fr->t);
 +            }
 +        }
 +        while (bCont && (timecheck < 0));
 +
 +        if (timecheck == 0)
 +        {
 +#define DONTSKIP(cnt) (skip) ? ((cnt % skip) == 0) : TRUE
 +
 +            if (bCont)
 +            {
 +                fprintf(stderr, "\rRead frame: %d, Time: %.3f", teller, fr->t);
 +
 +                if ((nenergy % 1000) == 0)
 +                {
 +                    srenew(time, nenergy+1000);
 +                    for (i = 0; (i <= nset); i++)
 +                    {
 +                        srenew(eneset[i], nenergy+1000);
 +                    }
 +                }
 +                time[nenergy] = fr->t;
 +                sum           = 0;
 +                for (i = 0; (i < nset); i++)
 +                {
 +                    eneset[i][nenergy] = fr->ener[set[i]].e;
 +                    sum               += fr->ener[set[i]].e;
 +                }
 +                if (bSum)
 +                {
 +                    eneset[nset][nenergy] = sum;
 +                }
 +                nenergy++;
 +            }
 +            teller++;
 +        }
 +    }
 +    while (bCont && (timecheck == 0));
 +
 +    fprintf(stderr, "\n");
 +
 +    fprintf(stderr, "Will build energy half-matrix of %d groups, %d elements, "
 +            "over %d frames\n", ngroups, nset, nenergy);
 +
 +    snew(emat, egNR+egSP);
 +    for (j = 0; (j < egNR+egSP); j++)
 +    {
 +        if (egrp_use[m])
 +        {
 +            snew(emat[j], ngroups);
 +            for (i = 0; (i < ngroups); i++)
 +            {
 +                snew(emat[j][i], ngroups);
 +            }
 +        }
 +    }
 +    snew(groupnr, ngroups);
 +    for (i = 0; (i < ngroups); i++)
 +    {
 +        groupnr[i] = i+1;
 +    }
 +    rlo.r  = 1.0, rlo.g  = 0.0, rlo.b  = 0.0;
 +    rmid.r = 1.0, rmid.g = 1.0, rmid.b = 1.0;
 +    rhi.r  = 0.0, rhi.g  = 0.0, rhi.b  = 1.0;
 +    if (bMeanEmtx)
 +    {
 +        snew(e, ngroups);
 +        for (i = 0; (i < ngroups); i++)
 +        {
 +            snew(e[i], nenergy);
 +        }
 +        n = 0;
 +        for (i = 0; (i < ngroups); i++)
 +        {
 +            for (j = i; (j < ngroups); j++)
 +            {
 +                for (m = 0; (m < egNR); m++)
 +                {
 +                    if (egrp_use[m])
 +                    {
 +                        for (k = 0; (k < nenergy); k++)
 +                        {
 +                            emat[m][i][j] += eneset[n][k];
 +                            e[i][k]       += eneset[n][k]; /* *0.5; */
 +                            e[j][k]       += eneset[n][k]; /* *0.5; */
 +                        }
 +                        n++;
 +                        emat[egTotal][i][j] += emat[m][i][j];
 +                        emat[m][i][j]       /= nenergy;
 +                        emat[m][j][i]        = emat[m][i][j];
 +                    }
 +                }
 +                emat[egTotal][i][j] /= nenergy;
 +                emat[egTotal][j][i]  = emat[egTotal][i][j];
 +            }
 +        }
 +        if (bFree)
 +        {
 +            if (bRef)
 +            {
 +                fprintf(stderr, "Will read reference energies from inputfile\n");
 +                neref = get_lines(opt2fn("-eref", NFILE, fnm), &ereflines);
 +                fprintf(stderr, "Read %d reference energies\n", neref);
 +                snew(eref, neref);
 +                snew(erefres, neref);
 +                for (i = 0; (i < neref); i++)
 +                {
 +                    snew(erefres[i], 5);
 +                    sscanf(ereflines[i], "%s %lf", erefres[i], &edum);
 +                    eref[i] = edum;
 +                }
 +            }
 +            snew(eaver, ngroups);
 +            for (i = 0; (i < ngroups); i++)
 +            {
 +                for (k = 0; (k < nenergy); k++)
 +                {
 +                    eaver[i] += e[i][k];
 +                }
 +                eaver[i] /= nenergy;
 +            }
 +            beta = 1.0/(BOLTZ*reftemp);
 +            snew(efree, ngroups);
 +            snew(edif, ngroups);
 +            for (i = 0; (i < ngroups); i++)
 +            {
 +                expE = 0;
 +                for (k = 0; (k < nenergy); k++)
 +                {
 +                    expE += exp(beta*(e[i][k]-eaver[i]));
 +                }
 +                efree[i] = log(expE/nenergy)/beta + eaver[i];
 +                if (bRef)
 +                {
 +                    n = search_str2(neref, erefres, groups[i]);
 +                    if (n != -1)
 +                    {
 +                        edif[i] = efree[i]-eref[n];
 +                    }
 +                    else
 +                    {
 +                        edif[i] = efree[i];
 +                        fprintf(stderr, "WARNING: group %s not found "
 +                                "in reference energies.\n", groups[i]);
 +                    }
 +                }
 +                else
 +                {
 +                    edif[i] = 0;
 +                }
 +            }
 +        }
 +
 +        emid             = 0.0; /*(emin+emax)*0.5;*/
 +        egrp_nm[egTotal] = "total";
 +        for (m = 0; (m < egNR+egSP); m++)
 +        {
 +            if (egrp_use[m])
 +            {
 +                emin = 1e10;
 +                emax = -1e10;
 +                for (i = 0; (i < ngroups); i++)
 +                {
 +                    for (j = i; (j < ngroups); j++)
 +                    {
 +                        if (emat[m][i][j] > emax)
 +                        {
 +                            emax = emat[m][i][j];
 +                        }
 +                        else if (emat[m][i][j] < emin)
 +                        {
 +                            emin = emat[m][i][j];
 +                        }
 +                    }
 +                }
 +                if (emax == emin)
 +                {
 +                    fprintf(stderr, "Matrix of %s energy is uniform at %f "
 +                            "(will not produce output).\n", egrp_nm[m], emax);
 +                }
 +                else
 +                {
 +                    fprintf(stderr, "Matrix of %s energy ranges from %f to %f\n",
 +                            egrp_nm[m], emin, emax);
 +                    if ((bCutmax) || (emax > cutmax))
 +                    {
 +                        emax = cutmax;
 +                    }
 +                    if ((bCutmin) || (emin < cutmin))
 +                    {
 +                        emin = cutmin;
 +                    }
 +                    if ((emax == cutmax) || (emin == cutmin))
 +                    {
 +                        fprintf(stderr, "Energy range adjusted: %f to %f\n", emin, emax);
 +                    }
 +
 +                    sprintf(fn, "%s%s", egrp_nm[m], ftp2fn(efXPM, NFILE, fnm));
 +                    sprintf(label, "%s Interaction Energies", egrp_nm[m]);
 +                    out = gmx_ffopen(fn, "w");
 +                    if (emin >= emid)
 +                    {
 +                        write_xpm(out, 0, label, "Energy (kJ/mol)",
 +                                  "Residue Index", "Residue Index",
 +                                  ngroups, ngroups, groupnr, groupnr, emat[m],
 +                                  emid, emax, rmid, rhi, &nlevels);
 +                    }
 +                    else if (emax <= emid)
 +                    {
 +                        write_xpm(out, 0, label, "Energy (kJ/mol)",
 +                                  "Residue Index", "Residue Index",
 +                                  ngroups, ngroups, groupnr, groupnr, emat[m],
 +                                  emin, emid, rlo, rmid, &nlevels);
 +                    }
 +                    else
 +                    {
 +                        write_xpm3(out, 0, label, "Energy (kJ/mol)",
 +                                   "Residue Index", "Residue Index",
 +                                   ngroups, ngroups, groupnr, groupnr, emat[m],
 +                                   emin, emid, emax, rlo, rmid, rhi, &nlevels);
 +                    }
 +                    gmx_ffclose(out);
 +                }
 +            }
 +        }
 +        snew(etot, egNR+egSP);
 +        for (m = 0; (m < egNR+egSP); m++)
 +        {
 +            snew(etot[m], ngroups);
 +            for (i = 0; (i < ngroups); i++)
 +            {
 +                for (j = 0; (j < ngroups); j++)
 +                {
 +                    etot[m][i] += emat[m][i][j];
 +                }
 +            }
 +        }
 +
 +        out = xvgropen(ftp2fn(efXVG, NFILE, fnm), "Mean Energy", "Residue", "kJ/mol",
 +                       oenv);
 +        xvgr_legend(out, 0, NULL, oenv);
 +        j = 0;
-             if (egrp_use[m])
++        if (output_env_get_print_xvgr_codes(oenv))
 +        {
-                 fprintf(out, "@ legend string %d \"%s\"\n", j++, egrp_nm[m]);
++            char str1[STRLEN], str2[STRLEN];
++            if (output_env_get_xvg_format(oenv) == exvgXMGR)
 +            {
-         }
-         if (bFree)
-         {
-             fprintf(out, "@ legend string %d \"%s\"\n", j++, "Free");
-         }
-         if (bFree)
-         {
-             fprintf(out, "@ legend string %d \"%s\"\n", j++, "Diff");
-         }
-         fprintf(out, "@TYPE xy\n");
-         fprintf(out, "#%3s", "grp");
-         for (m = 0; (m < egNR+egSP); m++)
-         {
-             if (egrp_use[m])
++                sprintf(str1, "@ legend string ");
++                sprintf(str2, " ");
 +            }
-                 fprintf(out, " %9s", egrp_nm[m]);
++            else
 +            {
-         if (bFree)
-         {
-             fprintf(out, " %9s", "Free");
-         }
-         if (bFree)
-         {
-             fprintf(out, " %9s", "Diff");
-         }
-         fprintf(out, "\n");
++                sprintf(str1, "@ s");
++                sprintf(str2, " legend ");
 +            }
++
++            for (m = 0; (m < egNR+egSP); m++)
++            {
++                if (egrp_use[m])
++                {
++                    fprintf(out, "%s%d%s \"%s\"\n", str1, j++, str2, egrp_nm[m]);
++                }
++            }
++            if (bFree)
++            {
++                fprintf(out, "%s%d%s \"%s\"\n", str1, j++, str2, "Free");
++            }
++            if (bFree)
++            {
++                fprintf(out, "%s%d%s \"%s\"\n", str1, j++, str2, "Diff");
++            }
++            fprintf(out, "@TYPE xy\n");
++            fprintf(out, "#%3s", "grp");
++
++            for (m = 0; (m < egNR+egSP); m++)
++            {
++                if (egrp_use[m])
++                {
++                    fprintf(out, " %9s", egrp_nm[m]);
++                }
++            }
++            if (bFree)
++            {
++                fprintf(out, " %9s", "Free");
++            }
++            if (bFree)
++            {
++                fprintf(out, " %9s", "Diff");
++            }
++            fprintf(out, "\n");
 +        }
 +        for (i = 0; (i < ngroups); i++)
 +        {
 +            fprintf(out, "%3.0f", groupnr[i]);
 +            for (m = 0; (m < egNR+egSP); m++)
 +            {
 +                if (egrp_use[m])
 +                {
 +                    fprintf(out, " %9.5g", etot[m][i]);
 +                }
 +            }
 +            if (bFree)
 +            {
 +                fprintf(out, " %9.5g", efree[i]);
 +            }
 +            if (bRef)
 +            {
 +                fprintf(out, " %9.5g", edif[i]);
 +            }
 +            fprintf(out, "\n");
 +        }
 +        gmx_ffclose(out);
 +    }
 +    else
 +    {
 +        fprintf(stderr, "While typing at your keyboard, suddenly...\n"
 +                "...nothing happens.\nWARNING: Not Implemented Yet\n");
 +/*
 +    out=ftp2FILE(efMAT,NFILE,fnm,"w");
 +    n=0;
 +    emin=emax=0.0;
 +    for (k=0; (k<nenergy); k++) {
 +      for (i=0; (i<ngroups); i++)
 +    for (j=i+1; (j<ngroups); j++)
 +      emat[i][j]=eneset[n][k];
 +      sprintf(label,"t=%.0f ps",time[k]);
 +      write_matrix(out,ngroups,1,ngroups,groupnr,emat,label,emin,emax,nlevels);
 +      n++;
 +    }
 +    gmx_ffclose(out);
 + */
 +    }
 +    close_enx(in);
 +
 +    return 0;
 +}
index 884c45d7f5331866006890c2b07577fc97d5d9d4,0000000000000000000000000000000000000000..de70d3f40223c562be63cee6750a146fcefcebe8
mode 100644,000000..100644
--- /dev/null
@@@ -1,2845 -1,0 +1,2845 @@@
-                     if (bOrinst)
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <string.h>
 +
 +#include "typedefs.h"
 +#include "gmx_fatal.h"
 +#include "vec.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "gromacs/fileio/enxio.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "names.h"
 +#include "copyrite.h"
 +#include "macros.h"
 +#include "xvgr.h"
 +#include "gstat.h"
 +#include "physics.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "viewit.h"
 +#include "mtop_util.h"
 +#include "gmx_ana.h"
 +#include "mdebin.h"
 +
 +static real       minthird = -1.0/3.0, minsixth = -1.0/6.0;
 +
 +typedef struct {
 +    real sum;
 +    real sum2;
 +} exactsum_t;
 +
 +typedef struct {
 +    real       *ener;
 +    exactsum_t *es;
 +    gmx_bool    bExactStat;
 +    double      av;
 +    double      rmsd;
 +    double      ee;
 +    double      slope;
 +} enerdat_t;
 +
 +typedef struct {
 +    gmx_int64_t      nsteps;
 +    gmx_int64_t      npoints;
 +    int              nframes;
 +    int             *step;
 +    int             *steps;
 +    int             *points;
 +    enerdat_t       *s;
 +} enerdata_t;
 +
 +static double mypow(double x, double y)
 +{
 +    if (x > 0)
 +    {
 +        return pow(x, y);
 +    }
 +    else
 +    {
 +        return 0.0;
 +    }
 +}
 +
 +static int *select_it(int nre, char *nm[], int *nset)
 +{
 +    gmx_bool *bE;
 +    int       n, k, j, i;
 +    int      *set;
 +    gmx_bool  bVerbose = TRUE;
 +
 +    if ((getenv("GMX_ENER_VERBOSE")) != NULL)
 +    {
 +        bVerbose = FALSE;
 +    }
 +
 +    fprintf(stderr, "Select the terms you want from the following list\n");
 +    fprintf(stderr, "End your selection with 0\n");
 +
 +    if (bVerbose)
 +    {
 +        for (k = 0; (k < nre); )
 +        {
 +            for (j = 0; (j < 4) && (k < nre); j++, k++)
 +            {
 +                fprintf(stderr, " %3d=%14s", k+1, nm[k]);
 +            }
 +            fprintf(stderr, "\n");
 +        }
 +    }
 +
 +    snew(bE, nre);
 +    do
 +    {
 +        if (1 != scanf("%d", &n))
 +        {
 +            gmx_fatal(FARGS, "Error reading user input");
 +        }
 +        if ((n > 0) && (n <= nre))
 +        {
 +            bE[n-1] = TRUE;
 +        }
 +    }
 +    while (n != 0);
 +
 +    snew(set, nre);
 +    for (i = (*nset) = 0; (i < nre); i++)
 +    {
 +        if (bE[i])
 +        {
 +            set[(*nset)++] = i;
 +        }
 +    }
 +
 +    sfree(bE);
 +
 +    return set;
 +}
 +
 +static void chomp(char *buf)
 +{
 +    int len = strlen(buf);
 +
 +    while ((len > 0) && (buf[len-1] == '\n'))
 +    {
 +        buf[len-1] = '\0';
 +        len--;
 +    }
 +}
 +
 +static int *select_by_name(int nre, gmx_enxnm_t *nm, int *nset)
 +{
 +    gmx_bool   *bE;
 +    int         n, k, kk, j, i, nmatch, nind, nss;
 +    int        *set;
 +    gmx_bool    bEOF, bVerbose = TRUE, bLong = FALSE;
 +    char       *ptr, buf[STRLEN];
 +    const char *fm4   = "%3d  %-14s";
 +    const char *fm2   = "%3d  %-34s";
 +    char      **newnm = NULL;
 +
 +    if ((getenv("GMX_ENER_VERBOSE")) != NULL)
 +    {
 +        bVerbose = FALSE;
 +    }
 +
 +    fprintf(stderr, "\n");
 +    fprintf(stderr, "Select the terms you want from the following list by\n");
 +    fprintf(stderr, "selecting either (part of) the name or the number or a combination.\n");
 +    fprintf(stderr, "End your selection with an empty line or a zero.\n");
 +    fprintf(stderr, "-------------------------------------------------------------------\n");
 +
 +    snew(newnm, nre);
 +    j = 0;
 +    for (k = 0; k < nre; k++)
 +    {
 +        newnm[k] = strdup(nm[k].name);
 +        /* Insert dashes in all the names */
 +        while ((ptr = strchr(newnm[k], ' ')) != NULL)
 +        {
 +            *ptr = '-';
 +        }
 +        if (bVerbose)
 +        {
 +            if (j == 0)
 +            {
 +                if (k > 0)
 +                {
 +                    fprintf(stderr, "\n");
 +                }
 +                bLong = FALSE;
 +                for (kk = k; kk < k+4; kk++)
 +                {
 +                    if (kk < nre && strlen(nm[kk].name) > 14)
 +                    {
 +                        bLong = TRUE;
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                fprintf(stderr, " ");
 +            }
 +            if (!bLong)
 +            {
 +                fprintf(stderr, fm4, k+1, newnm[k]);
 +                j++;
 +                if (j == 4)
 +                {
 +                    j = 0;
 +                }
 +            }
 +            else
 +            {
 +                fprintf(stderr, fm2, k+1, newnm[k]);
 +                j++;
 +                if (j == 2)
 +                {
 +                    j = 0;
 +                }
 +            }
 +        }
 +    }
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "\n\n");
 +    }
 +
 +    snew(bE, nre);
 +
 +    bEOF = FALSE;
 +    while (!bEOF && (fgets2(buf, STRLEN-1, stdin)))
 +    {
 +        /* Remove newlines */
 +        chomp(buf);
 +
 +        /* Remove spaces */
 +        trim(buf);
 +
 +        /* Empty line means end of input */
 +        bEOF = (strlen(buf) == 0);
 +        if (!bEOF)
 +        {
 +            ptr = buf;
 +            do
 +            {
 +                if (!bEOF)
 +                {
 +                    /* First try to read an integer */
 +                    nss   = sscanf(ptr, "%d", &nind);
 +                    if (nss == 1)
 +                    {
 +                        /* Zero means end of input */
 +                        if (nind == 0)
 +                        {
 +                            bEOF = TRUE;
 +                        }
 +                        else if ((1 <= nind) && (nind <= nre))
 +                        {
 +                            bE[nind-1] = TRUE;
 +                        }
 +                        else
 +                        {
 +                            fprintf(stderr, "number %d is out of range\n", nind);
 +                        }
 +                    }
 +                    else
 +                    {
 +                        /* Now try to read a string */
 +                        i      = strlen(ptr);
 +                        nmatch = 0;
 +                        for (nind = 0; nind < nre; nind++)
 +                        {
 +                            if (gmx_strcasecmp(newnm[nind], ptr) == 0)
 +                            {
 +                                bE[nind] = TRUE;
 +                                nmatch++;
 +                            }
 +                        }
 +                        if (nmatch == 0)
 +                        {
 +                            i      = strlen(ptr);
 +                            nmatch = 0;
 +                            for (nind = 0; nind < nre; nind++)
 +                            {
 +                                if (gmx_strncasecmp(newnm[nind], ptr, i) == 0)
 +                                {
 +                                    bE[nind] = TRUE;
 +                                    nmatch++;
 +                                }
 +                            }
 +                            if (nmatch == 0)
 +                            {
 +                                fprintf(stderr, "String '%s' does not match anything\n", ptr);
 +                            }
 +                        }
 +                    }
 +                }
 +                /* Look for the first space, and remove spaces from there */
 +                if ((ptr = strchr(ptr, ' ')) != NULL)
 +                {
 +                    trim(ptr);
 +                }
 +            }
 +            while (!bEOF && (ptr && (strlen(ptr) > 0)));
 +        }
 +    }
 +
 +    snew(set, nre);
 +    for (i = (*nset) = 0; (i < nre); i++)
 +    {
 +        if (bE[i])
 +        {
 +            set[(*nset)++] = i;
 +        }
 +    }
 +
 +    sfree(bE);
 +
 +    if (*nset == 0)
 +    {
 +        gmx_fatal(FARGS, "No energy terms selected");
 +    }
 +
 +    for (i = 0; (i < nre); i++)
 +    {
 +        sfree(newnm[i]);
 +    }
 +    sfree(newnm);
 +
 +    return set;
 +}
 +
 +static void get_dhdl_parms(const char *topnm, t_inputrec *ir)
 +{
 +    gmx_mtop_t  mtop;
 +    int         natoms;
 +    t_iatom    *iatom;
 +    matrix      box;
 +
 +    /* all we need is the ir to be able to write the label */
 +    read_tpx(topnm, ir, box, &natoms, NULL, NULL, NULL, &mtop);
 +}
 +
 +static void get_orires_parms(const char *topnm,
 +                             int *nor, int *nex, int **label, real **obs)
 +{
 +    gmx_mtop_t      mtop;
 +    gmx_localtop_t *top;
 +    t_inputrec      ir;
 +    t_iparams      *ip;
 +    int             natoms, i;
 +    t_iatom        *iatom;
 +    int             nb;
 +    matrix          box;
 +
 +    read_tpx(topnm, &ir, box, &natoms, NULL, NULL, NULL, &mtop);
 +    top = gmx_mtop_generate_local_top(&mtop, &ir);
 +
 +    ip       = top->idef.iparams;
 +    iatom    = top->idef.il[F_ORIRES].iatoms;
 +
 +    /* Count how many distance restraint there are... */
 +    nb = top->idef.il[F_ORIRES].nr;
 +    if (nb == 0)
 +    {
 +        gmx_fatal(FARGS, "No orientation restraints in topology!\n");
 +    }
 +
 +    *nor = nb/3;
 +    *nex = 0;
 +    snew(*label, *nor);
 +    snew(*obs, *nor);
 +    for (i = 0; i < nb; i += 3)
 +    {
 +        (*label)[i/3] = ip[iatom[i]].orires.label;
 +        (*obs)[i/3]   = ip[iatom[i]].orires.obs;
 +        if (ip[iatom[i]].orires.ex >= *nex)
 +        {
 +            *nex = ip[iatom[i]].orires.ex+1;
 +        }
 +    }
 +    fprintf(stderr, "Found %d orientation restraints with %d experiments",
 +            *nor, *nex);
 +}
 +
 +static int get_bounds(const char *topnm,
 +                      real **bounds, int **index, int **dr_pair, int *npairs,
 +                      gmx_mtop_t *mtop, gmx_localtop_t **ltop, t_inputrec *ir)
 +{
 +    gmx_localtop_t *top;
 +    t_functype     *functype;
 +    t_iparams      *ip;
 +    int             natoms, i, j, k, type, ftype, natom;
 +    t_ilist        *disres;
 +    t_iatom        *iatom;
 +    real           *b;
 +    int            *ind, *pair;
 +    int             nb, label1;
 +    matrix          box;
 +
 +    read_tpx(topnm, ir, box, &natoms, NULL, NULL, NULL, mtop);
 +    snew(*ltop, 1);
 +    top   = gmx_mtop_generate_local_top(mtop, ir);
 +    *ltop = top;
 +
 +    functype = top->idef.functype;
 +    ip       = top->idef.iparams;
 +
 +    /* Count how many distance restraint there are... */
 +    nb = top->idef.il[F_DISRES].nr;
 +    if (nb == 0)
 +    {
 +        gmx_fatal(FARGS, "No distance restraints in topology!\n");
 +    }
 +
 +    /* Allocate memory */
 +    snew(b, nb);
 +    snew(ind, nb);
 +    snew(pair, nb+1);
 +
 +    /* Fill the bound array */
 +    nb = 0;
 +    for (i = 0; (i < top->idef.ntypes); i++)
 +    {
 +        ftype = functype[i];
 +        if (ftype == F_DISRES)
 +        {
 +
 +            label1  = ip[i].disres.label;
 +            b[nb]   = ip[i].disres.up1;
 +            ind[nb] = label1;
 +            nb++;
 +        }
 +    }
 +    *bounds = b;
 +
 +    /* Fill the index array */
 +    label1  = -1;
 +    disres  = &(top->idef.il[F_DISRES]);
 +    iatom   = disres->iatoms;
 +    for (i = j = k = 0; (i < disres->nr); )
 +    {
 +        type  = iatom[i];
 +        ftype = top->idef.functype[type];
 +        natom = interaction_function[ftype].nratoms+1;
 +        if (label1 != top->idef.iparams[type].disres.label)
 +        {
 +            pair[j] = k;
 +            label1  = top->idef.iparams[type].disres.label;
 +            j++;
 +        }
 +        k++;
 +        i += natom;
 +    }
 +    pair[j]  = k;
 +    *npairs  = k;
 +    if (j != nb)
 +    {
 +        gmx_incons("get_bounds for distance restraints");
 +    }
 +
 +    *index   = ind;
 +    *dr_pair = pair;
 +
 +    return nb;
 +}
 +
 +static void calc_violations(real rt[], real rav3[], int nb, int index[],
 +                            real bounds[], real *viol, double *st, double *sa)
 +{
 +    const   real sixth = 1.0/6.0;
 +    int          i, j;
 +    double       rsum, rav, sumaver, sumt;
 +
 +    sumaver = 0;
 +    sumt    = 0;
 +    for (i = 0; (i < nb); i++)
 +    {
 +        rsum = 0.0;
 +        rav  = 0.0;
 +        for (j = index[i]; (j < index[i+1]); j++)
 +        {
 +            if (viol)
 +            {
 +                viol[j] += mypow(rt[j], -3.0);
 +            }
 +            rav     += sqr(rav3[j]);
 +            rsum    += mypow(rt[j], -6);
 +        }
 +        rsum    = max(0.0, mypow(rsum, -sixth)-bounds[i]);
 +        rav     = max(0.0, mypow(rav, -sixth)-bounds[i]);
 +
 +        sumt    += rsum;
 +        sumaver += rav;
 +    }
 +    *st = sumt;
 +    *sa = sumaver;
 +}
 +
 +static void analyse_disre(const char *voutfn,    int nframes,
 +                          real violaver[], real bounds[], int index[],
 +                          int pair[],      int nbounds,
 +                          const output_env_t oenv)
 +{
 +    FILE   *vout;
 +    double  sum, sumt, sumaver;
 +    int     i, j;
 +
 +    /* Subtract bounds from distances, to calculate violations */
 +    calc_violations(violaver, violaver,
 +                    nbounds, pair, bounds, NULL, &sumt, &sumaver);
 +
 +#ifdef DEBUG
 +    fprintf(stdout, "\nSum of violations averaged over simulation: %g nm\n",
 +            sumaver);
 +    fprintf(stdout, "Largest violation averaged over simulation: %g nm\n\n",
 +            sumt);
 +#endif
 +    vout = xvgropen(voutfn, "r\\S-3\\N average violations", "DR Index", "nm",
 +                    oenv);
 +    sum  = 0.0;
 +    sumt = 0.0;
 +    for (i = 0; (i < nbounds); i++)
 +    {
 +        /* Do ensemble averaging */
 +        sumaver = 0;
 +        for (j = pair[i]; (j < pair[i+1]); j++)
 +        {
 +            sumaver += sqr(violaver[j]/nframes);
 +        }
 +        sumaver = max(0.0, mypow(sumaver, minsixth)-bounds[i]);
 +
 +        sumt   += sumaver;
 +        sum     = max(sum, sumaver);
 +        fprintf(vout, "%10d  %10.5e\n", index[i], sumaver);
 +    }
 +#ifdef DEBUG
 +    for (j = 0; (j < dr.ndr); j++)
 +    {
 +        fprintf(vout, "%10d  %10.5e\n", j, mypow(violaver[j]/nframes, minthird));
 +    }
 +#endif
 +    gmx_ffclose(vout);
 +
 +    fprintf(stdout, "\nSum of violations averaged over simulation: %g nm\n",
 +            sumt);
 +    fprintf(stdout, "Largest violation averaged over simulation: %g nm\n\n", sum);
 +
 +    do_view(oenv, voutfn, "-graphtype bar");
 +}
 +
 +static void einstein_visco(const char *fn, const char *fni, int nsets,
 +                           int nint, real **eneint,
 +                           real V, real T, double dt,
 +                           const output_env_t oenv)
 +{
 +    FILE  *fp0, *fp1;
 +    double av[4], avold[4];
 +    double fac, di;
 +    int    i, j, m, nf4;
 +
 +    nf4 = nint/4 + 1;
 +
 +    for (i = 0; i <= nsets; i++)
 +    {
 +        avold[i] = 0;
 +    }
 +    fp0 = xvgropen(fni, "Shear viscosity integral",
 +                   "Time (ps)", "(kg m\\S-1\\N s\\S-1\\N ps)", oenv);
 +    fp1 = xvgropen(fn, "Shear viscosity using Einstein relation",
 +                   "Time (ps)", "(kg m\\S-1\\N s\\S-1\\N)", oenv);
 +    for (i = 0; i < nf4; i++)
 +    {
 +        for (m = 0; m <= nsets; m++)
 +        {
 +            av[m] = 0;
 +        }
 +        for (j = 0; j < nint - i; j++)
 +        {
 +            for (m = 0; m < nsets; m++)
 +            {
 +                di   = sqr(eneint[m][j+i] - eneint[m][j]);
 +
 +                av[m]     += di;
 +                av[nsets] += di/nsets;
 +            }
 +        }
 +        /* Convert to SI for the viscosity */
 +        fac = (V*NANO*NANO*NANO*PICO*1e10)/(2*BOLTZMANN*T)/(nint - i);
 +        fprintf(fp0, "%10g", i*dt);
 +        for (m = 0; (m <= nsets); m++)
 +        {
 +            av[m] = fac*av[m];
 +            fprintf(fp0, "  %10g", av[m]);
 +        }
 +        fprintf(fp0, "\n");
 +        fprintf(fp1, "%10g", (i + 0.5)*dt);
 +        for (m = 0; (m <= nsets); m++)
 +        {
 +            fprintf(fp1, "  %10g", (av[m]-avold[m])/dt);
 +            avold[m] = av[m];
 +        }
 +        fprintf(fp1, "\n");
 +    }
 +    gmx_ffclose(fp0);
 +    gmx_ffclose(fp1);
 +}
 +
 +typedef struct {
 +    gmx_int64_t     np;
 +    double          sum;
 +    double          sav;
 +    double          sav2;
 +} ee_sum_t;
 +
 +typedef struct {
 +    int             b;
 +    ee_sum_t        sum;
 +    gmx_int64_t     nst;
 +    gmx_int64_t     nst_min;
 +} ener_ee_t;
 +
 +static void clear_ee_sum(ee_sum_t *ees)
 +{
 +    ees->sav  = 0;
 +    ees->sav2 = 0;
 +    ees->np   = 0;
 +    ees->sum  = 0;
 +}
 +
 +static void add_ee_sum(ee_sum_t *ees, double sum, int np)
 +{
 +    ees->np  += np;
 +    ees->sum += sum;
 +}
 +
 +static void add_ee_av(ee_sum_t *ees)
 +{
 +    double av;
 +
 +    av         = ees->sum/ees->np;
 +    ees->sav  += av;
 +    ees->sav2 += av*av;
 +    ees->np    = 0;
 +    ees->sum   = 0;
 +}
 +
 +static double calc_ee2(int nb, ee_sum_t *ees)
 +{
 +    return (ees->sav2/nb - dsqr(ees->sav/nb))/(nb - 1);
 +}
 +
 +static void set_ee_av(ener_ee_t *eee)
 +{
 +    if (debug)
 +    {
 +        char buf[STEPSTRSIZE];
 +        fprintf(debug, "Storing average for err.est.: %s steps\n",
 +                gmx_step_str(eee->nst, buf));
 +    }
 +    add_ee_av(&eee->sum);
 +    eee->b++;
 +    if (eee->b == 1 || eee->nst < eee->nst_min)
 +    {
 +        eee->nst_min = eee->nst;
 +    }
 +    eee->nst = 0;
 +}
 +
 +static void calc_averages(int nset, enerdata_t *edat, int nbmin, int nbmax)
 +{
 +    int             nb, i, f, nee;
 +    double          sum, sum2, sump, see2;
 +    gmx_int64_t     steps, np, p, bound_nb;
 +    enerdat_t      *ed;
 +    exactsum_t     *es;
 +    gmx_bool        bAllZero;
 +    double          x, sx, sy, sxx, sxy;
 +    ener_ee_t      *eee;
 +
 +    /* Check if we have exact statistics over all points */
 +    for (i = 0; i < nset; i++)
 +    {
 +        ed             = &edat->s[i];
 +        ed->bExactStat = FALSE;
 +        if (edat->npoints > 0)
 +        {
 +            /* All energy file sum entries 0 signals no exact sums.
 +             * But if all energy values are 0, we still have exact sums.
 +             */
 +            bAllZero = TRUE;
 +            for (f = 0; f < edat->nframes && !ed->bExactStat; f++)
 +            {
 +                if (ed->ener[i] != 0)
 +                {
 +                    bAllZero = FALSE;
 +                }
 +                ed->bExactStat = (ed->es[f].sum != 0);
 +            }
 +            if (bAllZero)
 +            {
 +                ed->bExactStat = TRUE;
 +            }
 +        }
 +    }
 +
 +    snew(eee, nbmax+1);
 +    for (i = 0; i < nset; i++)
 +    {
 +        ed = &edat->s[i];
 +
 +        sum  = 0;
 +        sum2 = 0;
 +        np   = 0;
 +        sx   = 0;
 +        sy   = 0;
 +        sxx  = 0;
 +        sxy  = 0;
 +        for (nb = nbmin; nb <= nbmax; nb++)
 +        {
 +            eee[nb].b     = 0;
 +            clear_ee_sum(&eee[nb].sum);
 +            eee[nb].nst     = 0;
 +            eee[nb].nst_min = 0;
 +        }
 +        for (f = 0; f < edat->nframes; f++)
 +        {
 +            es = &ed->es[f];
 +
 +            if (ed->bExactStat)
 +            {
 +                /* Add the sum and the sum of variances to the totals. */
 +                p     = edat->points[f];
 +                sump  = es->sum;
 +                sum2 += es->sum2;
 +                if (np > 0)
 +                {
 +                    sum2 += dsqr(sum/np - (sum + es->sum)/(np + p))
 +                        *np*(np + p)/p;
 +                }
 +            }
 +            else
 +            {
 +                /* Add a single value to the sum and sum of squares. */
 +                p     = 1;
 +                sump  = ed->ener[f];
 +                sum2 += dsqr(sump);
 +            }
 +
 +            /* sum has to be increased after sum2 */
 +            np  += p;
 +            sum += sump;
 +
 +            /* For the linear regression use variance 1/p.
 +             * Note that sump is the sum, not the average, so we don't need p*.
 +             */
 +            x    = edat->step[f] - 0.5*(edat->steps[f] - 1);
 +            sx  += p*x;
 +            sy  += sump;
 +            sxx += p*x*x;
 +            sxy += x*sump;
 +
 +            for (nb = nbmin; nb <= nbmax; nb++)
 +            {
 +                /* Check if the current end step is closer to the desired
 +                 * block boundary than the next end step.
 +                 */
 +                bound_nb = (edat->step[0]-1)*nb + edat->nsteps*(eee[nb].b+1);
 +                if (eee[nb].nst > 0 &&
 +                    bound_nb - edat->step[f-1]*nb < edat->step[f]*nb - bound_nb)
 +                {
 +                    set_ee_av(&eee[nb]);
 +                }
 +                if (f == 0)
 +                {
 +                    eee[nb].nst = 1;
 +                }
 +                else
 +                {
 +                    eee[nb].nst += edat->step[f] - edat->step[f-1];
 +                }
 +                if (ed->bExactStat)
 +                {
 +                    add_ee_sum(&eee[nb].sum, es->sum, edat->points[f]);
 +                }
 +                else
 +                {
 +                    add_ee_sum(&eee[nb].sum, edat->s[i].ener[f], 1);
 +                }
 +                bound_nb = (edat->step[0]-1)*nb + edat->nsteps*(eee[nb].b+1);
 +                if (edat->step[f]*nb >= bound_nb)
 +                {
 +                    set_ee_av(&eee[nb]);
 +                }
 +            }
 +        }
 +
 +        edat->s[i].av = sum/np;
 +        if (ed->bExactStat)
 +        {
 +            edat->s[i].rmsd = sqrt(sum2/np);
 +        }
 +        else
 +        {
 +            edat->s[i].rmsd = sqrt(sum2/np - dsqr(edat->s[i].av));
 +        }
 +
 +        if (edat->nframes > 1)
 +        {
 +            edat->s[i].slope = (np*sxy - sx*sy)/(np*sxx - sx*sx);
 +        }
 +        else
 +        {
 +            edat->s[i].slope = 0;
 +        }
 +
 +        nee  = 0;
 +        see2 = 0;
 +        for (nb = nbmin; nb <= nbmax; nb++)
 +        {
 +            /* Check if we actually got nb blocks and if the smallest
 +             * block is not shorter than 80% of the average.
 +             */
 +            if (debug)
 +            {
 +                char buf1[STEPSTRSIZE], buf2[STEPSTRSIZE];
 +                fprintf(debug, "Requested %d blocks, we have %d blocks, min %s nsteps %s\n",
 +                        nb, eee[nb].b,
 +                        gmx_step_str(eee[nb].nst_min, buf1),
 +                        gmx_step_str(edat->nsteps, buf2));
 +            }
 +            if (eee[nb].b == nb && 5*nb*eee[nb].nst_min >= 4*edat->nsteps)
 +            {
 +                see2 += calc_ee2(nb, &eee[nb].sum);
 +                nee++;
 +            }
 +        }
 +        if (nee > 0)
 +        {
 +            edat->s[i].ee = sqrt(see2/nee);
 +        }
 +        else
 +        {
 +            edat->s[i].ee = -1;
 +        }
 +    }
 +    sfree(eee);
 +}
 +
 +static enerdata_t *calc_sum(int nset, enerdata_t *edat, int nbmin, int nbmax)
 +{
 +    enerdata_t *esum;
 +    enerdat_t  *s;
 +    int         f, i;
 +    double      sum;
 +
 +    snew(esum, 1);
 +    *esum = *edat;
 +    snew(esum->s, 1);
 +    s = &esum->s[0];
 +    snew(s->ener, esum->nframes);
 +    snew(s->es, esum->nframes);
 +
 +    s->bExactStat = TRUE;
 +    s->slope      = 0;
 +    for (i = 0; i < nset; i++)
 +    {
 +        if (!edat->s[i].bExactStat)
 +        {
 +            s->bExactStat = FALSE;
 +        }
 +        s->slope += edat->s[i].slope;
 +    }
 +
 +    for (f = 0; f < edat->nframes; f++)
 +    {
 +        sum = 0;
 +        for (i = 0; i < nset; i++)
 +        {
 +            sum += edat->s[i].ener[f];
 +        }
 +        s->ener[f] = sum;
 +        sum        = 0;
 +        for (i = 0; i < nset; i++)
 +        {
 +            sum += edat->s[i].es[f].sum;
 +        }
 +        s->es[f].sum  = sum;
 +        s->es[f].sum2 = 0;
 +    }
 +
 +    calc_averages(1, esum, nbmin, nbmax);
 +
 +    return esum;
 +}
 +
 +static char *ee_pr(double ee, char *buf)
 +{
 +    char   tmp[100];
 +    double rnd;
 +
 +    if (ee < 0)
 +    {
 +        sprintf(buf, "%s", "--");
 +    }
 +    else
 +    {
 +        /* Round to two decimals by printing. */
 +        sprintf(tmp, "%.1e", ee);
 +        sscanf(tmp, "%lf", &rnd);
 +        sprintf(buf, "%g", rnd);
 +    }
 +
 +    return buf;
 +}
 +
 +static void remove_drift(int nset, int nbmin, int nbmax, real dt, enerdata_t *edat)
 +{
 +/* Remove the drift by performing a fit to y = ax+b.
 +   Uses 5 iterations. */
 +    int    i, j, k;
 +    double delta, d, sd, sd2;
 +
 +    edat->npoints = edat->nframes;
 +    edat->nsteps  = edat->nframes;
 +
 +    for (k = 0; (k < 5); k++)
 +    {
 +        for (i = 0; (i < nset); i++)
 +        {
 +            delta = edat->s[i].slope*dt;
 +
 +            if (NULL != debug)
 +            {
 +                fprintf(debug, "slope for set %d is %g\n", i, edat->s[i].slope);
 +            }
 +
 +            for (j = 0; (j < edat->nframes); j++)
 +            {
 +                edat->s[i].ener[j]   -= j*delta;
 +                edat->s[i].es[j].sum  = 0;
 +                edat->s[i].es[j].sum2 = 0;
 +            }
 +        }
 +        calc_averages(nset, edat, nbmin, nbmax);
 +    }
 +}
 +
 +static void calc_fluctuation_props(FILE *fp,
 +                                   gmx_bool bDriftCorr, real dt,
 +                                   int nset, int nmol,
 +                                   char **leg, enerdata_t *edat,
 +                                   int nbmin, int nbmax)
 +{
 +    int    i, j;
 +    double vv, v, h, varv, hh, varh, tt, cv, cp, alpha, kappa, dcp, et, varet;
 +    double NANO3;
 +    enum {
 +        eVol, eEnth, eTemp, eEtot, eNR
 +    };
 +    const char *my_ener[] = { "Volume", "Enthalpy", "Temperature", "Total Energy" };
 +    int         ii[eNR];
 +
 +    NANO3 = NANO*NANO*NANO;
 +    if (!bDriftCorr)
 +    {
 +        fprintf(fp, "\nYou may want to use the -driftcorr flag in order to correct\n"
 +                "for spurious drift in the graphs. Note that this is not\n"
 +                "a substitute for proper equilibration and sampling!\n");
 +    }
 +    else
 +    {
 +        remove_drift(nset, nbmin, nbmax, dt, edat);
 +    }
 +    for (i = 0; (i < eNR); i++)
 +    {
 +        for (ii[i] = 0; (ii[i] < nset &&
 +                         (gmx_strcasecmp(leg[ii[i]], my_ener[i]) != 0)); ii[i]++)
 +        {
 +            ;
 +        }
 +/*        if (ii[i] < nset)
 +            fprintf(fp,"Found %s data.\n",my_ener[i]);
 + */ }
 +    /* Compute it all! */
 +    alpha = kappa = cp = dcp = cv = NOTSET;
 +
 +    /* Temperature */
 +    tt = NOTSET;
 +    if (ii[eTemp] < nset)
 +    {
 +        tt    = edat->s[ii[eTemp]].av;
 +    }
 +    /* Volume */
 +    vv = varv = NOTSET;
 +    if ((ii[eVol] < nset) && (ii[eTemp] < nset))
 +    {
 +        vv    = edat->s[ii[eVol]].av*NANO3;
 +        varv  = dsqr(edat->s[ii[eVol]].rmsd*NANO3);
 +        kappa = (varv/vv)/(BOLTZMANN*tt);
 +    }
 +    /* Enthalpy */
 +    hh = varh = NOTSET;
 +    if ((ii[eEnth] < nset) && (ii[eTemp] < nset))
 +    {
 +        hh    = KILO*edat->s[ii[eEnth]].av/AVOGADRO;
 +        varh  = dsqr(KILO*edat->s[ii[eEnth]].rmsd/AVOGADRO);
 +        cp    = AVOGADRO*((varh/nmol)/(BOLTZMANN*tt*tt));
 +    }
 +    /* Total energy */
 +    et = varet = NOTSET;
 +    if ((ii[eEtot] < nset) && (hh == NOTSET) && (tt != NOTSET))
 +    {
 +        /* Only compute cv in constant volume runs, which we can test
 +           by checking whether the enthalpy was computed.
 +         */
 +        et    = edat->s[ii[eEtot]].av;
 +        varet = sqr(edat->s[ii[eEtot]].rmsd);
 +        cv    = KILO*((varet/nmol)/(BOLTZ*tt*tt));
 +    }
 +    /* Alpha, dcp */
 +    if ((ii[eVol] < nset) && (ii[eEnth] < nset) && (ii[eTemp] < nset))
 +    {
 +        double v_sum, h_sum, vh_sum, v_aver, h_aver, vh_aver;
 +        vh_sum = v_sum = h_sum = 0;
 +        for (j = 0; (j < edat->nframes); j++)
 +        {
 +            v       = edat->s[ii[eVol]].ener[j]*NANO3;
 +            h       = KILO*edat->s[ii[eEnth]].ener[j]/AVOGADRO;
 +            v_sum  += v;
 +            h_sum  += h;
 +            vh_sum += (v*h);
 +        }
 +        vh_aver = vh_sum / edat->nframes;
 +        v_aver  = v_sum  / edat->nframes;
 +        h_aver  = h_sum  / edat->nframes;
 +        alpha   = (vh_aver-v_aver*h_aver)/(v_aver*BOLTZMANN*tt*tt);
 +        dcp     = (v_aver*AVOGADRO/nmol)*tt*sqr(alpha)/(kappa);
 +    }
 +
 +    if (tt != NOTSET)
 +    {
 +        if (nmol < 2)
 +        {
 +            fprintf(fp, "\nWARNING: nmol = %d, this may not be what you want.\n",
 +                    nmol);
 +        }
 +        fprintf(fp, "\nTemperature dependent fluctuation properties at T = %g.\n", tt);
 +        fprintf(fp, "\nHeat capacities obtained from fluctuations do *not* include\n");
 +        fprintf(fp, "quantum corrections. If you want to get a more accurate estimate\n");
 +        fprintf(fp, "please use the g_dos program.\n\n");
 +        fprintf(fp, "WARNING: Please verify that your simulations are converged and perform\n"
 +                "a block-averaging error analysis (not implemented in g_energy yet)\n");
 +
 +        if (debug != NULL)
 +        {
 +            if (varv != NOTSET)
 +            {
 +                fprintf(fp, "varv  =  %10g (m^6)\n", varv*AVOGADRO/nmol);
 +            }
 +        }
 +        if (vv != NOTSET)
 +        {
 +            fprintf(fp, "Volume                                   = %10g m^3/mol\n",
 +                    vv*AVOGADRO/nmol);
 +        }
 +        if (varh != NOTSET)
 +        {
 +            fprintf(fp, "Enthalpy                                 = %10g kJ/mol\n",
 +                    hh*AVOGADRO/(KILO*nmol));
 +        }
 +        if (alpha != NOTSET)
 +        {
 +            fprintf(fp, "Coefficient of Thermal Expansion Alpha_P = %10g (1/K)\n",
 +                    alpha);
 +        }
 +        if (kappa != NOTSET)
 +        {
 +            fprintf(fp, "Isothermal Compressibility Kappa         = %10g (J/m^3)\n",
 +                    kappa);
 +            fprintf(fp, "Adiabatic bulk modulus                   = %10g (m^3/J)\n",
 +                    1.0/kappa);
 +        }
 +        if (cp != NOTSET)
 +        {
 +            fprintf(fp, "Heat capacity at constant pressure Cp    = %10g J/mol K\n",
 +                    cp);
 +        }
 +        if (cv != NOTSET)
 +        {
 +            fprintf(fp, "Heat capacity at constant volume Cv      = %10g J/mol K\n",
 +                    cv);
 +        }
 +        if (dcp != NOTSET)
 +        {
 +            fprintf(fp, "Cp-Cv                                    =  %10g J/mol K\n",
 +                    dcp);
 +        }
 +        please_cite(fp, "Allen1987a");
 +    }
 +    else
 +    {
 +        fprintf(fp, "You should select the temperature in order to obtain fluctuation properties.\n");
 +    }
 +}
 +
 +static void analyse_ener(gmx_bool bCorr, const char *corrfn,
 +                         gmx_bool bFee, gmx_bool bSum, gmx_bool bFluct,
 +                         gmx_bool bVisco, const char *visfn, int nmol,
 +                         gmx_int64_t start_step, double start_t,
 +                         gmx_int64_t step, double t,
 +                         real reftemp,
 +                         enerdata_t *edat,
 +                         int nset, int set[], gmx_bool *bIsEner,
 +                         char **leg, gmx_enxnm_t *enm,
 +                         real Vaver, real ezero,
 +                         int nbmin, int nbmax,
 +                         const output_env_t oenv)
 +{
 +    FILE           *fp;
 +    /* Check out the printed manual for equations! */
 +    double          Dt, aver, stddev, errest, delta_t, totaldrift;
 +    enerdata_t     *esum = NULL;
 +    real            xxx, integral, intBulk, Temp = 0, Pres = 0;
 +    real            sfrac, oldfrac, diffsum, diffav, fstep, pr_aver, pr_stddev, pr_errest;
 +    double          beta = 0, expE, expEtot, *fee = NULL;
 +    gmx_int64_t     nsteps;
 +    int             nexact, nnotexact;
 +    double          x1m, x1mk;
 +    int             i, j, k, nout;
 +    real            chi2;
 +    char            buf[256], eebuf[100];
 +
 +    nsteps  = step - start_step + 1;
 +    if (nsteps < 1)
 +    {
 +        fprintf(stdout, "Not enough steps (%s) for statistics\n",
 +                gmx_step_str(nsteps, buf));
 +    }
 +    else
 +    {
 +        /* Calculate the time difference */
 +        delta_t = t - start_t;
 +
 +        fprintf(stdout, "\nStatistics over %s steps [ %.4f through %.4f ps ], %d data sets\n",
 +                gmx_step_str(nsteps, buf), start_t, t, nset);
 +
 +        calc_averages(nset, edat, nbmin, nbmax);
 +
 +        if (bSum)
 +        {
 +            esum = calc_sum(nset, edat, nbmin, nbmax);
 +        }
 +
 +        if (edat->npoints == 0)
 +        {
 +            nexact    = 0;
 +            nnotexact = nset;
 +        }
 +        else
 +        {
 +            nexact    = 0;
 +            nnotexact = 0;
 +            for (i = 0; (i < nset); i++)
 +            {
 +                if (edat->s[i].bExactStat)
 +                {
 +                    nexact++;
 +                }
 +                else
 +                {
 +                    nnotexact++;
 +                }
 +            }
 +        }
 +
 +        if (nnotexact == 0)
 +        {
 +            fprintf(stdout, "All statistics are over %s points\n",
 +                    gmx_step_str(edat->npoints, buf));
 +        }
 +        else if (nexact == 0 || edat->npoints == edat->nframes)
 +        {
 +            fprintf(stdout, "All statistics are over %d points (frames)\n",
 +                    edat->nframes);
 +        }
 +        else
 +        {
 +            fprintf(stdout, "The term%s", nnotexact == 1 ? "" : "s");
 +            for (i = 0; (i < nset); i++)
 +            {
 +                if (!edat->s[i].bExactStat)
 +                {
 +                    fprintf(stdout, " '%s'", leg[i]);
 +                }
 +            }
 +            fprintf(stdout, " %s has statistics over %d points (frames)\n",
 +                    nnotexact == 1 ? "is" : "are", edat->nframes);
 +            fprintf(stdout, "All other statistics are over %s points\n",
 +                    gmx_step_str(edat->npoints, buf));
 +        }
 +        fprintf(stdout, "\n");
 +
 +        fprintf(stdout, "%-24s %10s %10s %10s %10s",
 +                "Energy", "Average", "Err.Est.", "RMSD", "Tot-Drift");
 +        if (bFee)
 +        {
 +            fprintf(stdout, "  %10s\n", "-kT ln<e^(E/kT)>");
 +        }
 +        else
 +        {
 +            fprintf(stdout, "\n");
 +        }
 +        fprintf(stdout, "-------------------------------------------------------------------------------\n");
 +
 +        /* Initiate locals, only used with -sum */
 +        expEtot = 0;
 +        if (bFee)
 +        {
 +            beta = 1.0/(BOLTZ*reftemp);
 +            snew(fee, nset);
 +        }
 +        for (i = 0; (i < nset); i++)
 +        {
 +            aver   = edat->s[i].av;
 +            stddev = edat->s[i].rmsd;
 +            errest = edat->s[i].ee;
 +
 +            if (bFee)
 +            {
 +                expE = 0;
 +                for (j = 0; (j < edat->nframes); j++)
 +                {
 +                    expE += exp(beta*(edat->s[i].ener[j] - aver)/nmol);
 +                }
 +                if (bSum)
 +                {
 +                    expEtot += expE/edat->nframes;
 +                }
 +
 +                fee[i] = log(expE/edat->nframes)/beta + aver/nmol;
 +            }
 +            if (strstr(leg[i], "empera") != NULL)
 +            {
 +                Temp = aver;
 +            }
 +            else if (strstr(leg[i], "olum") != NULL)
 +            {
 +                Vaver = aver;
 +            }
 +            else if (strstr(leg[i], "essure") != NULL)
 +            {
 +                Pres = aver;
 +            }
 +            if (bIsEner[i])
 +            {
 +                pr_aver   = aver/nmol-ezero;
 +                pr_stddev = stddev/nmol;
 +                pr_errest = errest/nmol;
 +            }
 +            else
 +            {
 +                pr_aver   = aver;
 +                pr_stddev = stddev;
 +                pr_errest = errest;
 +            }
 +
 +            /* Multiply the slope in steps with the number of steps taken */
 +            totaldrift = (edat->nsteps - 1)*edat->s[i].slope;
 +            if (bIsEner[i])
 +            {
 +                totaldrift /= nmol;
 +            }
 +
 +            fprintf(stdout, "%-24s %10g %10s %10g %10g",
 +                    leg[i], pr_aver, ee_pr(pr_errest, eebuf), pr_stddev, totaldrift);
 +            if (bFee)
 +            {
 +                fprintf(stdout, "  %10g", fee[i]);
 +            }
 +
 +            fprintf(stdout, "  (%s)\n", enm[set[i]].unit);
 +
 +            if (bFluct)
 +            {
 +                for (j = 0; (j < edat->nframes); j++)
 +                {
 +                    edat->s[i].ener[j] -= aver;
 +                }
 +            }
 +        }
 +        if (bSum)
 +        {
 +            totaldrift = (edat->nsteps - 1)*esum->s[0].slope;
 +            fprintf(stdout, "%-24s %10g %10s %10s %10g  (%s)",
 +                    "Total", esum->s[0].av/nmol, ee_pr(esum->s[0].ee/nmol, eebuf),
 +                    "--", totaldrift/nmol, enm[set[0]].unit);
 +            /* pr_aver,pr_stddev,a,totaldrift */
 +            if (bFee)
 +            {
 +                fprintf(stdout, "  %10g  %10g\n",
 +                        log(expEtot)/beta + esum->s[0].av/nmol, log(expEtot)/beta);
 +            }
 +            else
 +            {
 +                fprintf(stdout, "\n");
 +            }
 +        }
 +
 +        /* Do correlation function */
 +        if (edat->nframes > 1)
 +        {
 +            Dt = delta_t/(edat->nframes - 1);
 +        }
 +        else
 +        {
 +            Dt = 0;
 +        }
 +        if (bVisco)
 +        {
 +            const char* leg[] = { "Shear", "Bulk" };
 +            real        factor;
 +            real      **eneset;
 +            real      **eneint;
 +
 +            /* Assume pressure tensor is in Pxx Pxy Pxz Pyx Pyy Pyz Pzx Pzy Pzz */
 +
 +            /* Symmetrise tensor! (and store in first three elements)
 +             * And subtract average pressure!
 +             */
 +            snew(eneset, 12);
 +            for (i = 0; i < 12; i++)
 +            {
 +                snew(eneset[i], edat->nframes);
 +            }
 +            for (i = 0; (i < edat->nframes); i++)
 +            {
 +                eneset[0][i] = 0.5*(edat->s[1].ener[i]+edat->s[3].ener[i]);
 +                eneset[1][i] = 0.5*(edat->s[2].ener[i]+edat->s[6].ener[i]);
 +                eneset[2][i] = 0.5*(edat->s[5].ener[i]+edat->s[7].ener[i]);
 +                for (j = 3; j <= 11; j++)
 +                {
 +                    eneset[j][i] = edat->s[j].ener[i];
 +                }
 +                eneset[11][i] -= Pres;
 +            }
 +
 +            /* Determine integrals of the off-diagonal pressure elements */
 +            snew(eneint, 3);
 +            for (i = 0; i < 3; i++)
 +            {
 +                snew(eneint[i], edat->nframes + 1);
 +            }
 +            eneint[0][0] = 0;
 +            eneint[1][0] = 0;
 +            eneint[2][0] = 0;
 +            for (i = 0; i < edat->nframes; i++)
 +            {
 +                eneint[0][i+1] = eneint[0][i] + 0.5*(edat->s[1].es[i].sum + edat->s[3].es[i].sum)*Dt/edat->points[i];
 +                eneint[1][i+1] = eneint[1][i] + 0.5*(edat->s[2].es[i].sum + edat->s[6].es[i].sum)*Dt/edat->points[i];
 +                eneint[2][i+1] = eneint[2][i] + 0.5*(edat->s[5].es[i].sum + edat->s[7].es[i].sum)*Dt/edat->points[i];
 +            }
 +
 +            einstein_visco("evisco.xvg", "eviscoi.xvg",
 +                           3, edat->nframes+1, eneint, Vaver, Temp, Dt, oenv);
 +
 +            for (i = 0; i < 3; i++)
 +            {
 +                sfree(eneint[i]);
 +            }
 +            sfree(eneint);
 +
 +            /*do_autocorr(corrfn,buf,nenergy,3,eneset,Dt,eacNormal,TRUE);*/
 +            /* Do it for shear viscosity */
 +            strcpy(buf, "Shear Viscosity");
 +            low_do_autocorr(corrfn, oenv, buf, edat->nframes, 3,
 +                            (edat->nframes+1)/2, eneset, Dt,
 +                            eacNormal, 1, TRUE, FALSE, FALSE, 0.0, 0.0, 0);
 +
 +            /* Now for bulk viscosity */
 +            strcpy(buf, "Bulk Viscosity");
 +            low_do_autocorr(corrfn, oenv, buf, edat->nframes, 1,
 +                            (edat->nframes+1)/2, &(eneset[11]), Dt,
 +                            eacNormal, 1, TRUE, FALSE, FALSE, 0.0, 0.0, 0);
 +
 +            factor = (Vaver*1e-26/(BOLTZMANN*Temp))*Dt;
 +            fp     = xvgropen(visfn, buf, "Time (ps)", "\\8h\\4 (cp)", oenv);
 +            xvgr_legend(fp, asize(leg), leg, oenv);
 +
 +            /* Use trapezium rule for integration */
 +            integral = 0;
 +            intBulk  = 0;
 +            nout     = get_acfnout();
 +            if ((nout < 2) || (nout >= edat->nframes/2))
 +            {
 +                nout = edat->nframes/2;
 +            }
 +            for (i = 1; (i < nout); i++)
 +            {
 +                integral += 0.5*(eneset[0][i-1]  + eneset[0][i])*factor;
 +                intBulk  += 0.5*(eneset[11][i-1] + eneset[11][i])*factor;
 +                fprintf(fp, "%10g  %10g  %10g\n", (i*Dt), integral, intBulk);
 +            }
 +            gmx_ffclose(fp);
 +
 +            for (i = 0; i < 12; i++)
 +            {
 +                sfree(eneset[i]);
 +            }
 +            sfree(eneset);
 +        }
 +        else if (bCorr)
 +        {
 +            if (bFluct)
 +            {
 +                strcpy(buf, "Autocorrelation of Energy Fluctuations");
 +            }
 +            else
 +            {
 +                strcpy(buf, "Energy Autocorrelation");
 +            }
 +#if 0
 +            do_autocorr(corrfn, oenv, buf, edat->nframes,
 +                        bSum ? 1                 : nset,
 +                        bSum ? &edat->s[nset-1].ener : eneset,
 +                        (delta_t/edat->nframes), eacNormal, FALSE);
 +#endif
 +        }
 +    }
 +}
 +
 +static void print_time(FILE *fp, double t)
 +{
 +    fprintf(fp, "%12.6f", t);
 +}
 +
 +static void print1(FILE *fp, gmx_bool bDp, real e)
 +{
 +    if (bDp)
 +    {
 +        fprintf(fp, "  %16.12f", e);
 +    }
 +    else
 +    {
 +        fprintf(fp, "  %10.6f", e);
 +    }
 +}
 +
 +static void fec(const char *ene2fn, const char *runavgfn,
 +                real reftemp, int nset, int set[], char *leg[],
 +                enerdata_t *edat, double time[],
 +                const output_env_t oenv)
 +{
 +    const char * ravgleg[] = {
 +        "\\8D\\4E = E\\sB\\N-E\\sA\\N",
 +        "<e\\S-\\8D\\4E/kT\\N>\\s0..t\\N"
 +    };
 +    FILE        *fp;
 +    ener_file_t  enx;
 +    int          nre, timecheck, step, nenergy, nenergy2, maxenergy;
 +    int          i, j;
 +    gmx_bool     bCont;
 +    real         aver, beta;
 +    real       **eneset2;
 +    double       dE, sum;
 +    gmx_enxnm_t *enm = NULL;
 +    t_enxframe  *fr;
 +    char         buf[22];
 +
 +    /* read second energy file */
 +    snew(fr, 1);
 +    enm = NULL;
 +    enx = open_enx(ene2fn, "r");
 +    do_enxnms(enx, &(fr->nre), &enm);
 +
 +    snew(eneset2, nset+1);
 +    nenergy2  = 0;
 +    maxenergy = 0;
 +    timecheck = 0;
 +    do
 +    {
 +        /* This loop searches for the first frame (when -b option is given),
 +         * or when this has been found it reads just one energy frame
 +         */
 +        do
 +        {
 +            bCont = do_enx(enx, fr);
 +
 +            if (bCont)
 +            {
 +                timecheck = check_times(fr->t);
 +            }
 +
 +        }
 +        while (bCont && (timecheck < 0));
 +
 +        /* Store energies for analysis afterwards... */
 +        if ((timecheck == 0) && bCont)
 +        {
 +            if (fr->nre > 0)
 +            {
 +                if (nenergy2 >= maxenergy)
 +                {
 +                    maxenergy += 1000;
 +                    for (i = 0; i <= nset; i++)
 +                    {
 +                        srenew(eneset2[i], maxenergy);
 +                    }
 +                }
 +                if (fr->t != time[nenergy2])
 +                {
 +                    fprintf(stderr, "\nWARNING time mismatch %g!=%g at frame %s\n",
 +                            fr->t, time[nenergy2], gmx_step_str(fr->step, buf));
 +                }
 +                for (i = 0; i < nset; i++)
 +                {
 +                    eneset2[i][nenergy2] = fr->ener[set[i]].e;
 +                }
 +                nenergy2++;
 +            }
 +        }
 +    }
 +    while (bCont && (timecheck == 0));
 +
 +    /* check */
 +    if (edat->nframes != nenergy2)
 +    {
 +        fprintf(stderr, "\nWARNING file length mismatch %d!=%d\n",
 +                edat->nframes, nenergy2);
 +    }
 +    nenergy = min(edat->nframes, nenergy2);
 +
 +    /* calculate fe difference dF = -kT ln < exp(-(E_B-E_A)/kT) >_A */
 +    fp = NULL;
 +    if (runavgfn)
 +    {
 +        fp = xvgropen(runavgfn, "Running average free energy difference",
 +                      "Time (" unit_time ")", "\\8D\\4E (" unit_energy ")", oenv);
 +        xvgr_legend(fp, asize(ravgleg), ravgleg, oenv);
 +    }
 +    fprintf(stdout, "\n%-24s %10s\n",
 +            "Energy", "dF = -kT ln < exp(-(EB-EA)/kT) >A");
 +    sum  = 0;
 +    beta = 1.0/(BOLTZ*reftemp);
 +    for (i = 0; i < nset; i++)
 +    {
 +        if (gmx_strcasecmp(leg[i], enm[set[i]].name) != 0)
 +        {
 +            fprintf(stderr, "\nWARNING energy set name mismatch %s!=%s\n",
 +                    leg[i], enm[set[i]].name);
 +        }
 +        for (j = 0; j < nenergy; j++)
 +        {
 +            dE   = eneset2[i][j] - edat->s[i].ener[j];
 +            sum += exp(-dE*beta);
 +            if (fp)
 +            {
 +                fprintf(fp, "%10g %10g %10g\n",
 +                        time[j], dE, -BOLTZ*reftemp*log(sum/(j+1)) );
 +            }
 +        }
 +        aver = -BOLTZ*reftemp*log(sum/nenergy);
 +        fprintf(stdout, "%-24s %10g\n", leg[i], aver);
 +    }
 +    if (fp)
 +    {
 +        gmx_ffclose(fp);
 +    }
 +    sfree(fr);
 +}
 +
 +
 +static void do_dhdl(t_enxframe *fr, t_inputrec *ir, FILE **fp_dhdl,
 +                    const char *filename, gmx_bool bDp,
 +                    int *blocks, int *hists, int *samples, int *nlambdas,
 +                    const output_env_t oenv)
 +{
 +    const char  *dhdl = "dH/d\\lambda", *deltag = "\\DeltaH", *lambda = "\\lambda";
 +    char         title[STRLEN], label_x[STRLEN], label_y[STRLEN], legend[STRLEN];
 +    char         buf[STRLEN];
 +    gmx_bool     first       = FALSE;
 +    int          nblock_hist = 0, nblock_dh = 0, nblock_dhcoll = 0;
 +    int          i, j, k;
 +    /* coll data */
 +    double       temp              = 0, start_time = 0, delta_time = 0, start_lambda = 0, delta_lambda = 0;
 +    static int   setnr             = 0;
 +    double      *native_lambda_vec = NULL;
 +    const char **lambda_components = NULL;
 +    int          n_lambda_vec      = 0;
 +    gmx_bool     changing_lambda   = FALSE;
 +    int          lambda_fep_state;
 +
 +    /* now count the blocks & handle the global dh data */
 +    for (i = 0; i < fr->nblock; i++)
 +    {
 +        if (fr->block[i].id == enxDHHIST)
 +        {
 +            nblock_hist++;
 +        }
 +        else if (fr->block[i].id == enxDH)
 +        {
 +            nblock_dh++;
 +        }
 +        else if (fr->block[i].id == enxDHCOLL)
 +        {
 +            nblock_dhcoll++;
 +            if ( (fr->block[i].nsub < 1) ||
 +                 (fr->block[i].sub[0].type != xdr_datatype_double) ||
 +                 (fr->block[i].sub[0].nr < 5))
 +            {
 +                gmx_fatal(FARGS, "Unexpected block data");
 +            }
 +
 +            /* read the data from the DHCOLL block */
 +            temp            =         fr->block[i].sub[0].dval[0];
 +            start_time      =   fr->block[i].sub[0].dval[1];
 +            delta_time      =   fr->block[i].sub[0].dval[2];
 +            start_lambda    = fr->block[i].sub[0].dval[3];
 +            delta_lambda    = fr->block[i].sub[0].dval[4];
 +            changing_lambda = (delta_lambda != 0);
 +            if (fr->block[i].nsub > 1)
 +            {
 +                lambda_fep_state = fr->block[i].sub[1].ival[0];
 +                if (n_lambda_vec == 0)
 +                {
 +                    n_lambda_vec = fr->block[i].sub[1].ival[1];
 +                }
 +                else
 +                {
 +                    if (n_lambda_vec != fr->block[i].sub[1].ival[1])
 +                    {
 +                        gmx_fatal(FARGS,
 +                                  "Unexpected change of basis set in lambda");
 +                    }
 +                }
 +                if (lambda_components == NULL)
 +                {
 +                    snew(lambda_components, n_lambda_vec);
 +                }
 +                if (native_lambda_vec == NULL)
 +                {
 +                    snew(native_lambda_vec, n_lambda_vec);
 +                }
 +                for (j = 0; j < n_lambda_vec; j++)
 +                {
 +                    native_lambda_vec[j] = fr->block[i].sub[0].dval[5+j];
 +                    lambda_components[j] =
 +                        efpt_singular_names[fr->block[i].sub[1].ival[2+j]];
 +                }
 +            }
 +        }
 +    }
 +
 +    if (nblock_hist == 0 && nblock_dh == 0)
 +    {
 +        /* don't do anything */
 +        return;
 +    }
 +    if (nblock_hist > 0 && nblock_dh > 0)
 +    {
 +        gmx_fatal(FARGS, "This energy file contains both histogram dhdl data and non-histogram dhdl data. Don't know what to do.");
 +    }
 +    if (!*fp_dhdl)
 +    {
 +        if (nblock_dh > 0)
 +        {
 +            /* we have standard, non-histogram data --
 +               call open_dhdl to open the file */
 +            /* TODO this is an ugly hack that needs to be fixed: this will only
 +               work if the order of data is always the same and if we're
 +               only using the g_energy compiled with the mdrun that produced
 +               the ener.edr. */
 +            *fp_dhdl = open_dhdl(filename, ir, oenv);
 +        }
 +        else
 +        {
 +            sprintf(title, "N(%s)", deltag);
 +            sprintf(label_x, "%s (%s)", deltag, unit_energy);
 +            sprintf(label_y, "Samples");
 +            *fp_dhdl = xvgropen_type(filename, title, label_x, label_y, exvggtXNY, oenv);
 +            sprintf(buf, "T = %g (K), %s = %g", temp, lambda, start_lambda);
 +            xvgr_subtitle(*fp_dhdl, buf, oenv);
 +        }
 +    }
 +
 +    (*hists)   += nblock_hist;
 +    (*blocks)  += nblock_dh;
 +    (*nlambdas) = nblock_hist+nblock_dh;
 +
 +    /* write the data */
 +    if (nblock_hist > 0)
 +    {
 +        gmx_int64_t sum = 0;
 +        /* histograms */
 +        for (i = 0; i < fr->nblock; i++)
 +        {
 +            t_enxblock *blk = &(fr->block[i]);
 +            if (blk->id == enxDHHIST)
 +            {
 +                double          foreign_lambda, dx;
 +                gmx_int64_t     x0;
 +                int             nhist, derivative;
 +
 +                /* check the block types etc. */
 +                if ( (blk->nsub < 2) ||
 +                     (blk->sub[0].type != xdr_datatype_double) ||
 +                     (blk->sub[1].type != xdr_datatype_int64) ||
 +                     (blk->sub[0].nr < 2)  ||
 +                     (blk->sub[1].nr < 2) )
 +                {
 +                    gmx_fatal(FARGS, "Unexpected block data in file");
 +                }
 +                foreign_lambda = blk->sub[0].dval[0];
 +                dx             = blk->sub[0].dval[1];
 +                nhist          = blk->sub[1].lval[0];
 +                derivative     = blk->sub[1].lval[1];
 +                for (j = 0; j < nhist; j++)
 +                {
 +                    const char *lg[1];
 +                    x0 = blk->sub[1].lval[2+j];
 +
 +                    if (!derivative)
 +                    {
 +                        sprintf(legend, "N(%s(%s=%g) | %s=%g)",
 +                                deltag, lambda, foreign_lambda,
 +                                lambda, start_lambda);
 +                    }
 +                    else
 +                    {
 +                        sprintf(legend, "N(%s | %s=%g)",
 +                                dhdl, lambda, start_lambda);
 +                    }
 +
 +                    lg[0] = legend;
 +                    xvgr_new_dataset(*fp_dhdl, setnr, 1, lg, oenv);
 +                    setnr++;
 +                    for (k = 0; k < blk->sub[j+2].nr; k++)
 +                    {
 +                        int    hist;
 +                        double xmin, xmax;
 +
 +                        hist = blk->sub[j+2].ival[k];
 +                        xmin = (x0+k)*dx;
 +                        xmax = (x0+k+1)*dx;
 +                        fprintf(*fp_dhdl, "%g %d\n%g %d\n", xmin, hist,
 +                                xmax, hist);
 +                        sum += hist;
 +                    }
 +                    /* multiple histogram data blocks in one histogram
 +                       mean that the second one is the reverse of the first one:
 +                       for dhdl derivatives, it's important to know both the
 +                       maximum and minimum values */
 +                    dx = -dx;
 +                }
 +            }
 +        }
 +        (*samples) += (int)(sum/nblock_hist);
 +    }
 +    else
 +    {
 +        /* raw dh */
 +        int    len      = 0;
 +        char **setnames = NULL;
 +        int    nnames   = nblock_dh;
 +
 +        for (i = 0; i < fr->nblock; i++)
 +        {
 +            t_enxblock *blk = &(fr->block[i]);
 +            if (blk->id == enxDH)
 +            {
 +                if (len == 0)
 +                {
 +                    len = blk->sub[2].nr;
 +                }
 +                else
 +                {
 +                    if (len != blk->sub[2].nr)
 +                    {
 +                        gmx_fatal(FARGS, "Length inconsistency in dhdl data");
 +                    }
 +                }
 +            }
 +        }
 +        (*samples) += len;
 +
 +        for (i = 0; i < len; i++)
 +        {
 +            double time = start_time + delta_time*i;
 +
 +            fprintf(*fp_dhdl, "%.4f ", time);
 +
 +            for (j = 0; j < fr->nblock; j++)
 +            {
 +                t_enxblock *blk = &(fr->block[j]);
 +                if (blk->id == enxDH)
 +                {
 +                    double value;
 +                    if (blk->sub[2].type == xdr_datatype_float)
 +                    {
 +                        value = blk->sub[2].fval[i];
 +                    }
 +                    else
 +                    {
 +                        value = blk->sub[2].dval[i];
 +                    }
 +                    /* we need to decide which data type it is based on the count*/
 +
 +                    if (j == 1 && ir->bExpanded)
 +                    {
 +                        fprintf(*fp_dhdl, "%4d", (int)value);   /* if expanded ensembles and zero, this is a state value, it's an integer. We need a cleaner conditional than if j==1! */
 +                    }
 +                    else
 +                    {
 +                        if (bDp)
 +                        {
 +                            fprintf(*fp_dhdl, " %#.12g", value);   /* print normal precision */
 +                        }
 +                        else
 +                        {
 +                            fprintf(*fp_dhdl, " %#.8g", value);   /* print normal precision */
 +                        }
 +                    }
 +                }
 +            }
 +            fprintf(*fp_dhdl, "\n");
 +        }
 +    }
 +}
 +
 +
 +int gmx_energy(int argc, char *argv[])
 +{
 +    const char        *desc[] = {
 +        "[THISMODULE] extracts energy components or distance restraint",
 +        "data from an energy file. The user is prompted to interactively",
 +        "select the desired energy terms.[PAR]",
 +
 +        "Average, RMSD, and drift are calculated with full precision from the",
 +        "simulation (see printed manual). Drift is calculated by performing",
 +        "a least-squares fit of the data to a straight line. The reported total drift",
 +        "is the difference of the fit at the first and last point.",
 +        "An error estimate of the average is given based on a block averages",
 +        "over 5 blocks using the full-precision averages. The error estimate",
 +        "can be performed over multiple block lengths with the options",
 +        "[TT]-nbmin[tt] and [TT]-nbmax[tt].",
 +        "[BB]Note[bb] that in most cases the energy files contains averages over all",
 +        "MD steps, or over many more points than the number of frames in",
 +        "energy file. This makes the [THISMODULE] statistics output more accurate",
 +        "than the [TT].xvg[tt] output. When exact averages are not present in the energy",
 +        "file, the statistics mentioned above are simply over the single, per-frame",
 +        "energy values.[PAR]",
 +
 +        "The term fluctuation gives the RMSD around the least-squares fit.[PAR]",
 +
 +        "Some fluctuation-dependent properties can be calculated provided",
 +        "the correct energy terms are selected, and that the command line option",
 +        "[TT]-fluct_props[tt] is given. The following properties",
 +        "will be computed:[BR]",
 +        "Property                        Energy terms needed[BR]",
 +        "---------------------------------------------------[BR]",
 +        "Heat capacity C[SUB]p[sub] (NPT sims):    Enthalpy, Temp     [BR]",
 +        "Heat capacity C[SUB]v[sub] (NVT sims):    Etot, Temp         [BR]",
 +        "Thermal expansion coeff. (NPT): Enthalpy, Vol, Temp[BR]",
 +        "Isothermal compressibility:     Vol, Temp          [BR]",
 +        "Adiabatic bulk modulus:         Vol, Temp          [BR]",
 +        "---------------------------------------------------[BR]",
 +        "You always need to set the number of molecules [TT]-nmol[tt].",
 +        "The C[SUB]p[sub]/C[SUB]v[sub] computations do [BB]not[bb] include any corrections",
 +        "for quantum effects. Use the [gmx-dos] program if you need that (and you do).[PAR]"
 +        "When the [TT]-viol[tt] option is set, the time averaged",
 +        "violations are plotted and the running time-averaged and",
 +        "instantaneous sum of violations are recalculated. Additionally",
 +        "running time-averaged and instantaneous distances between",
 +        "selected pairs can be plotted with the [TT]-pairs[tt] option.[PAR]",
 +
 +        "Options [TT]-ora[tt], [TT]-ort[tt], [TT]-oda[tt], [TT]-odr[tt] and",
 +        "[TT]-odt[tt] are used for analyzing orientation restraint data.",
 +        "The first two options plot the orientation, the last three the",
 +        "deviations of the orientations from the experimental values.",
 +        "The options that end on an 'a' plot the average over time",
 +        "as a function of restraint. The options that end on a 't'",
 +        "prompt the user for restraint label numbers and plot the data",
 +        "as a function of time. Option [TT]-odr[tt] plots the RMS",
 +        "deviation as a function of restraint.",
 +        "When the run used time or ensemble averaged orientation restraints,",
 +        "option [TT]-orinst[tt] can be used to analyse the instantaneous,",
 +        "not ensemble-averaged orientations and deviations instead of",
 +        "the time and ensemble averages.[PAR]",
 +
 +        "Option [TT]-oten[tt] plots the eigenvalues of the molecular order",
 +        "tensor for each orientation restraint experiment. With option",
 +        "[TT]-ovec[tt] also the eigenvectors are plotted.[PAR]",
 +
 +        "Option [TT]-odh[tt] extracts and plots the free energy data",
 +        "(Hamiltoian differences and/or the Hamiltonian derivative dhdl)",
 +        "from the [TT]ener.edr[tt] file.[PAR]",
 +
 +        "With [TT]-fee[tt] an estimate is calculated for the free-energy",
 +        "difference with an ideal gas state: [BR]",
 +        "  [GRK]Delta[grk] A = A(N,V,T) - A[SUB]idealgas[sub](N,V,T) = kT [LN][CHEVRON][EXP]U[SUB]pot[sub]/kT[exp][chevron][ln][BR]",
 +        "  [GRK]Delta[grk] G = G(N,p,T) - G[SUB]idealgas[sub](N,p,T) = kT [LN][CHEVRON][EXP]U[SUB]pot[sub]/kT[exp][chevron][ln][BR]",
 +        "where k is Boltzmann's constant, T is set by [TT]-fetemp[tt] and",
 +        "the average is over the ensemble (or time in a trajectory).",
 +        "Note that this is in principle",
 +        "only correct when averaging over the whole (Boltzmann) ensemble",
 +        "and using the potential energy. This also allows for an entropy",
 +        "estimate using:[BR]",
 +        "  [GRK]Delta[grk] S(N,V,T) = S(N,V,T) - S[SUB]idealgas[sub](N,V,T) = ([CHEVRON]U[SUB]pot[sub][chevron] - [GRK]Delta[grk] A)/T[BR]",
 +        "  [GRK]Delta[grk] S(N,p,T) = S(N,p,T) - S[SUB]idealgas[sub](N,p,T) = ([CHEVRON]U[SUB]pot[sub][chevron] + pV - [GRK]Delta[grk] G)/T",
 +        "[PAR]",
 +
 +        "When a second energy file is specified ([TT]-f2[tt]), a free energy",
 +        "difference is calculated [BR] dF = -kT [LN][CHEVRON][EXP]-(E[SUB]B[sub]-E[SUB]A[sub])/kT[exp][chevron][SUB]A[sub][ln] ,",
 +        "where E[SUB]A[sub] and E[SUB]B[sub] are the energies from the first and second energy",
 +        "files, and the average is over the ensemble A. The running average",
 +        "of the free energy difference is printed to a file specified by [TT]-ravg[tt].",
 +        "[BB]Note[bb] that the energies must both be calculated from the same trajectory."
 +
 +    };
 +    static gmx_bool    bSum    = FALSE, bFee = FALSE, bPrAll = FALSE, bFluct = FALSE, bDriftCorr = FALSE;
 +    static gmx_bool    bDp     = FALSE, bMutot = FALSE, bOrinst = FALSE, bOvec = FALSE, bFluctProps = FALSE;
 +    static int         skip    = 0, nmol = 1, nbmin = 5, nbmax = 5;
 +    static real        reftemp = 300.0, ezero = 0;
 +    t_pargs            pa[]    = {
 +        { "-fee",   FALSE, etBOOL,  {&bFee},
 +          "Do a free energy estimate" },
 +        { "-fetemp", FALSE, etREAL, {&reftemp},
 +          "Reference temperature for free energy calculation" },
 +        { "-zero", FALSE, etREAL, {&ezero},
 +          "Subtract a zero-point energy" },
 +        { "-sum",  FALSE, etBOOL, {&bSum},
 +          "Sum the energy terms selected rather than display them all" },
 +        { "-dp",   FALSE, etBOOL, {&bDp},
 +          "Print energies in high precision" },
 +        { "-nbmin", FALSE, etINT, {&nbmin},
 +          "Minimum number of blocks for error estimate" },
 +        { "-nbmax", FALSE, etINT, {&nbmax},
 +          "Maximum number of blocks for error estimate" },
 +        { "-mutot", FALSE, etBOOL, {&bMutot},
 +          "Compute the total dipole moment from the components" },
 +        { "-skip", FALSE, etINT,  {&skip},
 +          "Skip number of frames between data points" },
 +        { "-aver", FALSE, etBOOL, {&bPrAll},
 +          "Also print the exact average and rmsd stored in the energy frames (only when 1 term is requested)" },
 +        { "-nmol", FALSE, etINT,  {&nmol},
 +          "Number of molecules in your sample: the energies are divided by this number" },
 +        { "-fluct_props", FALSE, etBOOL, {&bFluctProps},
 +          "Compute properties based on energy fluctuations, like heat capacity" },
 +        { "-driftcorr", FALSE, etBOOL, {&bDriftCorr},
 +          "Useful only for calculations of fluctuation properties. The drift in the observables will be subtracted before computing the fluctuation properties."},
 +        { "-fluc", FALSE, etBOOL, {&bFluct},
 +          "Calculate autocorrelation of energy fluctuations rather than energy itself" },
 +        { "-orinst", FALSE, etBOOL, {&bOrinst},
 +          "Analyse instantaneous orientation data" },
 +        { "-ovec", FALSE, etBOOL, {&bOvec},
 +          "Also plot the eigenvectors with [TT]-oten[tt]" }
 +    };
 +    const char       * drleg[] = {
 +        "Running average",
 +        "Instantaneous"
 +    };
 +    static const char *setnm[] = {
 +        "Pres-XX", "Pres-XY", "Pres-XZ", "Pres-YX", "Pres-YY",
 +        "Pres-YZ", "Pres-ZX", "Pres-ZY", "Pres-ZZ", "Temperature",
 +        "Volume",  "Pressure"
 +    };
 +
 +    FILE              *out     = NULL, *fp_pairs = NULL, *fort = NULL, *fodt = NULL, *foten = NULL;
 +    FILE              *fp_dhdl = NULL;
 +    FILE             **drout;
 +    ener_file_t        fp;
 +    int                timecheck = 0;
 +    gmx_mtop_t         mtop;
 +    gmx_localtop_t    *top = NULL;
 +    t_inputrec         ir;
 +    t_energy         **ee;
 +    enerdata_t         edat;
 +    gmx_enxnm_t       *enm = NULL;
 +    t_enxframe        *frame, *fr = NULL;
 +    int                cur = 0;
 +#define NEXT (1-cur)
 +    int                nre, teller, teller_disre, nfr;
 +    gmx_int64_t        start_step;
 +    int                nor = 0, nex = 0, norfr = 0, enx_i = 0;
 +    real               start_t;
 +    real              *bounds  = NULL, *violaver = NULL, *oobs = NULL, *orient = NULL, *odrms = NULL;
 +    int               *index   = NULL, *pair = NULL, norsel = 0, *orsel = NULL, *or_label = NULL;
 +    int                nbounds = 0, npairs;
 +    gmx_bool           bDisRe, bDRAll, bORA, bORT, bODA, bODR, bODT, bORIRE, bOTEN, bDHDL;
 +    gmx_bool           bFoundStart, bCont, bEDR, bVisco;
 +    double             sum, sumaver, sumt, ener, dbl;
 +    double            *time = NULL;
 +    real               Vaver;
 +    int               *set     = NULL, i, j, k, nset, sss;
 +    gmx_bool          *bIsEner = NULL;
 +    char             **pairleg, **odtleg, **otenleg;
 +    char             **leg = NULL;
 +    char             **nms;
 +    char              *anm_j, *anm_k, *resnm_j, *resnm_k;
 +    int                resnr_j, resnr_k;
 +    const char        *orinst_sub = "@ subtitle \"instantaneous\"\n";
 +    char               buf[256];
 +    output_env_t       oenv;
 +    t_enxblock        *blk       = NULL;
 +    t_enxblock        *blk_disre = NULL;
 +    int                ndisre    = 0;
 +    int                dh_blocks = 0, dh_hists = 0, dh_samples = 0, dh_lambdas = 0;
 +
 +    t_filenm           fnm[] = {
 +        { efEDR, "-f",    NULL,      ffREAD  },
 +        { efEDR, "-f2",   NULL,      ffOPTRD },
 +        { efTPX, "-s",    NULL,      ffOPTRD },
 +        { efXVG, "-o",    "energy",  ffWRITE },
 +        { efXVG, "-viol", "violaver", ffOPTWR },
 +        { efXVG, "-pairs", "pairs",   ffOPTWR },
 +        { efXVG, "-ora",  "orienta", ffOPTWR },
 +        { efXVG, "-ort",  "orientt", ffOPTWR },
 +        { efXVG, "-oda",  "orideva", ffOPTWR },
 +        { efXVG, "-odr",  "oridevr", ffOPTWR },
 +        { efXVG, "-odt",  "oridevt", ffOPTWR },
 +        { efXVG, "-oten", "oriten",  ffOPTWR },
 +        { efXVG, "-corr", "enecorr", ffOPTWR },
 +        { efXVG, "-vis",  "visco",   ffOPTWR },
 +        { efXVG, "-ravg", "runavgdf", ffOPTWR },
 +        { efXVG, "-odh",  "dhdl", ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +    int                npargs;
 +    t_pargs           *ppa;
 +
 +    npargs = asize(pa);
 +    ppa    = add_acf_pargs(&npargs, pa);
 +    if (!parse_common_args(&argc, argv,
 +                           PCA_CAN_VIEW | PCA_CAN_BEGIN | PCA_CAN_END | PCA_BE_NICE,
 +                           NFILE, fnm, npargs, ppa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    bDRAll = opt2bSet("-pairs", NFILE, fnm);
 +    bDisRe = opt2bSet("-viol", NFILE, fnm) || bDRAll;
 +    bORA   = opt2bSet("-ora", NFILE, fnm);
 +    bORT   = opt2bSet("-ort", NFILE, fnm);
 +    bODA   = opt2bSet("-oda", NFILE, fnm);
 +    bODR   = opt2bSet("-odr", NFILE, fnm);
 +    bODT   = opt2bSet("-odt", NFILE, fnm);
 +    bORIRE = bORA || bORT || bODA || bODR || bODT;
 +    bOTEN  = opt2bSet("-oten", NFILE, fnm);
 +    bDHDL  = opt2bSet("-odh", NFILE, fnm);
 +
 +    nset = 0;
 +
 +    snew(frame, 2);
 +    fp = open_enx(ftp2fn(efEDR, NFILE, fnm), "r");
 +    do_enxnms(fp, &nre, &enm);
 +
 +    Vaver = -1;
 +
 +    bVisco = opt2bSet("-vis", NFILE, fnm);
 +
 +    if ((!bDisRe) && (!bDHDL))
 +    {
 +        if (bVisco)
 +        {
 +            nset = asize(setnm);
 +            snew(set, nset);
 +            /* This is nasty code... To extract Pres tensor, Volume and Temperature */
 +            for (j = 0; j < nset; j++)
 +            {
 +                for (i = 0; i < nre; i++)
 +                {
 +                    if (strstr(enm[i].name, setnm[j]))
 +                    {
 +                        set[j] = i;
 +                        break;
 +                    }
 +                }
 +                if (i == nre)
 +                {
 +                    if (gmx_strcasecmp(setnm[j], "Volume") == 0)
 +                    {
 +                        printf("Enter the box volume (" unit_volume "): ");
 +                        if (1 != scanf("%lf", &dbl))
 +                        {
 +                            gmx_fatal(FARGS, "Error reading user input");
 +                        }
 +                        Vaver = dbl;
 +                    }
 +                    else
 +                    {
 +                        gmx_fatal(FARGS, "Could not find term %s for viscosity calculation",
 +                                  setnm[j]);
 +                    }
 +                }
 +            }
 +        }
 +        else
 +        {
 +            set = select_by_name(nre, enm, &nset);
 +        }
 +        /* Print all the different units once */
 +        sprintf(buf, "(%s)", enm[set[0]].unit);
 +        for (i = 1; i < nset; i++)
 +        {
 +            for (j = 0; j < i; j++)
 +            {
 +                if (strcmp(enm[set[i]].unit, enm[set[j]].unit) == 0)
 +                {
 +                    break;
 +                }
 +            }
 +            if (j == i)
 +            {
 +                strcat(buf, ", (");
 +                strcat(buf, enm[set[i]].unit);
 +                strcat(buf, ")");
 +            }
 +        }
 +        out = xvgropen(opt2fn("-o", NFILE, fnm), "Gromacs Energies", "Time (ps)", buf,
 +                       oenv);
 +
 +        snew(leg, nset+1);
 +        for (i = 0; (i < nset); i++)
 +        {
 +            leg[i] = enm[set[i]].name;
 +        }
 +        if (bSum)
 +        {
 +            leg[nset] = strdup("Sum");
 +            xvgr_legend(out, nset+1, (const char**)leg, oenv);
 +        }
 +        else
 +        {
 +            xvgr_legend(out, nset, (const char**)leg, oenv);
 +        }
 +
 +        snew(bIsEner, nset);
 +        for (i = 0; (i < nset); i++)
 +        {
 +            bIsEner[i] = FALSE;
 +            for (j = 0; (j <= F_ETOT); j++)
 +            {
 +                bIsEner[i] = bIsEner[i] ||
 +                    (gmx_strcasecmp(interaction_function[j].longname, leg[i]) == 0);
 +            }
 +        }
 +
 +        if (bPrAll && nset > 1)
 +        {
 +            gmx_fatal(FARGS, "Printing averages can only be done when a single set is selected");
 +        }
 +
 +        time = NULL;
 +
 +        if (bORIRE || bOTEN)
 +        {
 +            get_orires_parms(ftp2fn(efTPX, NFILE, fnm), &nor, &nex, &or_label, &oobs);
 +        }
 +
 +        if (bORIRE)
 +        {
 +            if (bOrinst)
 +            {
 +                enx_i = enxORI;
 +            }
 +            else
 +            {
 +                enx_i = enxOR;
 +            }
 +
 +            if (bORA || bODA)
 +            {
 +                snew(orient, nor);
 +            }
 +            if (bODR)
 +            {
 +                snew(odrms, nor);
 +            }
 +            if (bORT || bODT)
 +            {
 +                fprintf(stderr, "Select the orientation restraint labels you want (-1 is all)\n");
 +                fprintf(stderr, "End your selection with 0\n");
 +                j     = -1;
 +                orsel = NULL;
 +                do
 +                {
 +                    j++;
 +                    srenew(orsel, j+1);
 +                    if (1 != scanf("%d", &(orsel[j])))
 +                    {
 +                        gmx_fatal(FARGS, "Error reading user input");
 +                    }
 +                }
 +                while (orsel[j] > 0);
 +                if (orsel[0] == -1)
 +                {
 +                    fprintf(stderr, "Selecting all %d orientation restraints\n", nor);
 +                    norsel = nor;
 +                    srenew(orsel, nor);
 +                    for (i = 0; i < nor; i++)
 +                    {
 +                        orsel[i] = i;
 +                    }
 +                }
 +                else
 +                {
 +                    /* Build the selection */
 +                    norsel = 0;
 +                    for (i = 0; i < j; i++)
 +                    {
 +                        for (k = 0; k < nor; k++)
 +                        {
 +                            if (or_label[k] == orsel[i])
 +                            {
 +                                orsel[norsel] = k;
 +                                norsel++;
 +                                break;
 +                            }
 +                        }
 +                        if (k == nor)
 +                        {
 +                            fprintf(stderr, "Orientation restraint label %d not found\n",
 +                                    orsel[i]);
 +                        }
 +                    }
 +                }
 +                snew(odtleg, norsel);
 +                for (i = 0; i < norsel; i++)
 +                {
 +                    snew(odtleg[i], 256);
 +                    sprintf(odtleg[i], "%d", or_label[orsel[i]]);
 +                }
 +                if (bORT)
 +                {
 +                    fort = xvgropen(opt2fn("-ort", NFILE, fnm), "Calculated orientations",
 +                                    "Time (ps)", "", oenv);
-                     if (bOrinst)
++                    if (bOrinst && output_env_get_print_xvgr_codes(oenv))
 +                    {
 +                        fprintf(fort, "%s", orinst_sub);
 +                    }
 +                    xvgr_legend(fort, norsel, (const char**)odtleg, oenv);
 +                }
 +                if (bODT)
 +                {
 +                    fodt = xvgropen(opt2fn("-odt", NFILE, fnm),
 +                                    "Orientation restraint deviation",
 +                                    "Time (ps)", "", oenv);
-         if (bOrinst)
++                    if (bOrinst && output_env_get_print_xvgr_codes(oenv))
 +                    {
 +                        fprintf(fodt, "%s", orinst_sub);
 +                    }
 +                    xvgr_legend(fodt, norsel, (const char**)odtleg, oenv);
 +                }
 +            }
 +        }
 +        if (bOTEN)
 +        {
 +            foten = xvgropen(opt2fn("-oten", NFILE, fnm),
 +                             "Order tensor", "Time (ps)", "", oenv);
 +            snew(otenleg, bOvec ? nex*12 : nex*3);
 +            for (i = 0; i < nex; i++)
 +            {
 +                for (j = 0; j < 3; j++)
 +                {
 +                    sprintf(buf, "eig%d", j+1);
 +                    otenleg[(bOvec ? 12 : 3)*i+j] = strdup(buf);
 +                }
 +                if (bOvec)
 +                {
 +                    for (j = 0; j < 9; j++)
 +                    {
 +                        sprintf(buf, "vec%d%s", j/3+1, j%3 == 0 ? "x" : (j%3 == 1 ? "y" : "z"));
 +                        otenleg[12*i+3+j] = strdup(buf);
 +                    }
 +                }
 +            }
 +            xvgr_legend(foten, bOvec ? nex*12 : nex*3, (const char**)otenleg, oenv);
 +        }
 +    }
 +    else if (bDisRe)
 +    {
 +        nbounds = get_bounds(ftp2fn(efTPX, NFILE, fnm), &bounds, &index, &pair, &npairs,
 +                             &mtop, &top, &ir);
 +        snew(violaver, npairs);
 +        out = xvgropen(opt2fn("-o", NFILE, fnm), "Sum of Violations",
 +                       "Time (ps)", "nm", oenv);
 +        xvgr_legend(out, 2, drleg, oenv);
 +        if (bDRAll)
 +        {
 +            fp_pairs = xvgropen(opt2fn("-pairs", NFILE, fnm), "Pair Distances",
 +                                "Time (ps)", "Distance (nm)", oenv);
 +            if (output_env_get_print_xvgr_codes(oenv))
 +            {
 +                fprintf(fp_pairs, "@ subtitle \"averaged (tau=%g) and instantaneous\"\n",
 +                        ir.dr_tau);
 +            }
 +        }
 +    }
 +    else if (bDHDL)
 +    {
 +        get_dhdl_parms(ftp2fn(efTPX, NFILE, fnm), &ir);
 +    }
 +
 +    /* Initiate energies and set them to zero */
 +    edat.nsteps  = 0;
 +    edat.npoints = 0;
 +    edat.nframes = 0;
 +    edat.step    = NULL;
 +    edat.steps   = NULL;
 +    edat.points  = NULL;
 +    snew(edat.s, nset);
 +
 +    /* Initiate counters */
 +    teller       = 0;
 +    teller_disre = 0;
 +    bFoundStart  = FALSE;
 +    start_step   = 0;
 +    start_t      = 0;
 +    do
 +    {
 +        /* This loop searches for the first frame (when -b option is given),
 +         * or when this has been found it reads just one energy frame
 +         */
 +        do
 +        {
 +            bCont = do_enx(fp, &(frame[NEXT]));
 +            if (bCont)
 +            {
 +                timecheck = check_times(frame[NEXT].t);
 +            }
 +        }
 +        while (bCont && (timecheck < 0));
 +
 +        if ((timecheck == 0) && bCont)
 +        {
 +            /* We read a valid frame, so we can use it */
 +            fr = &(frame[NEXT]);
 +
 +            if (fr->nre > 0)
 +            {
 +                /* The frame contains energies, so update cur */
 +                cur  = NEXT;
 +
 +                if (edat.nframes % 1000 == 0)
 +                {
 +                    srenew(edat.step, edat.nframes+1000);
 +                    memset(&(edat.step[edat.nframes]), 0, 1000*sizeof(edat.step[0]));
 +                    srenew(edat.steps, edat.nframes+1000);
 +                    memset(&(edat.steps[edat.nframes]), 0, 1000*sizeof(edat.steps[0]));
 +                    srenew(edat.points, edat.nframes+1000);
 +                    memset(&(edat.points[edat.nframes]), 0, 1000*sizeof(edat.points[0]));
 +
 +                    for (i = 0; i < nset; i++)
 +                    {
 +                        srenew(edat.s[i].ener, edat.nframes+1000);
 +                        memset(&(edat.s[i].ener[edat.nframes]), 0,
 +                               1000*sizeof(edat.s[i].ener[0]));
 +                        srenew(edat.s[i].es, edat.nframes+1000);
 +                        memset(&(edat.s[i].es[edat.nframes]), 0,
 +                               1000*sizeof(edat.s[i].es[0]));
 +                    }
 +                }
 +
 +                nfr            = edat.nframes;
 +                edat.step[nfr] = fr->step;
 +
 +                if (!bFoundStart)
 +                {
 +                    bFoundStart = TRUE;
 +                    /* Initiate the previous step data */
 +                    start_step = fr->step;
 +                    start_t    = fr->t;
 +                    /* Initiate the energy sums */
 +                    edat.steps[nfr]  = 1;
 +                    edat.points[nfr] = 1;
 +                    for (i = 0; i < nset; i++)
 +                    {
 +                        sss                    = set[i];
 +                        edat.s[i].es[nfr].sum  = fr->ener[sss].e;
 +                        edat.s[i].es[nfr].sum2 = 0;
 +                    }
 +                    edat.nsteps  = 1;
 +                    edat.npoints = 1;
 +                }
 +                else
 +                {
 +                    edat.steps[nfr] = fr->nsteps;
 +                    {
 +                        if (fr->step - start_step + 1 == edat.nsteps + fr->nsteps)
 +                        {
 +                            if (fr->nsum <= 1)
 +                            {
 +                                edat.points[nfr] = 1;
 +                                for (i = 0; i < nset; i++)
 +                                {
 +                                    sss                    = set[i];
 +                                    edat.s[i].es[nfr].sum  = fr->ener[sss].e;
 +                                    edat.s[i].es[nfr].sum2 = 0;
 +                                }
 +                                edat.npoints += 1;
 +                            }
 +                            else
 +                            {
 +                                edat.points[nfr] = fr->nsum;
 +                                for (i = 0; i < nset; i++)
 +                                {
 +                                    sss                    = set[i];
 +                                    edat.s[i].es[nfr].sum  = fr->ener[sss].esum;
 +                                    edat.s[i].es[nfr].sum2 = fr->ener[sss].eav;
 +                                }
 +                                edat.npoints += fr->nsum;
 +                            }
 +                        }
 +                        else
 +                        {
 +                            /* The interval does not match fr->nsteps:
 +                             * can not do exact averages.
 +                             */
 +                            edat.npoints = 0;
 +                        }
 +                        edat.nsteps = fr->step - start_step + 1;
 +                    }
 +                }
 +                for (i = 0; i < nset; i++)
 +                {
 +                    edat.s[i].ener[nfr] = fr->ener[set[i]].e;
 +                }
 +            }
 +            /*
 +             * Define distance restraint legends. Can only be done after
 +             * the first frame has been read... (Then we know how many there are)
 +             */
 +            blk_disre = find_block_id_enxframe(fr, enxDISRE, NULL);
 +            if (bDisRe && bDRAll && !leg && blk_disre)
 +            {
 +                t_iatom   *fa;
 +                t_iparams *ip;
 +
 +                fa = top->idef.il[F_DISRES].iatoms;
 +                ip = top->idef.iparams;
 +                if (blk_disre->nsub != 2 ||
 +                    (blk_disre->sub[0].nr != blk_disre->sub[1].nr) )
 +                {
 +                    gmx_incons("Number of disre sub-blocks not equal to 2");
 +                }
 +
 +                ndisre = blk_disre->sub[0].nr;
 +                if (ndisre != top->idef.il[F_DISRES].nr/3)
 +                {
 +                    gmx_fatal(FARGS, "Number of disre pairs in the energy file (%d) does not match the number in the run input file (%d)\n",
 +                              ndisre, top->idef.il[F_DISRES].nr/3);
 +                }
 +                snew(pairleg, ndisre);
 +                for (i = 0; i < ndisre; i++)
 +                {
 +                    snew(pairleg[i], 30);
 +                    j = fa[3*i+1];
 +                    k = fa[3*i+2];
 +                    gmx_mtop_atominfo_global(&mtop, j, &anm_j, &resnr_j, &resnm_j);
 +                    gmx_mtop_atominfo_global(&mtop, k, &anm_k, &resnr_k, &resnm_k);
 +                    sprintf(pairleg[i], "%d %s %d %s (%d)",
 +                            resnr_j, anm_j, resnr_k, anm_k,
 +                            ip[fa[3*i]].disres.label);
 +                }
 +                set = select_it(ndisre, pairleg, &nset);
 +                snew(leg, 2*nset);
 +                for (i = 0; (i < nset); i++)
 +                {
 +                    snew(leg[2*i], 32);
 +                    sprintf(leg[2*i],  "a %s", pairleg[set[i]]);
 +                    snew(leg[2*i+1], 32);
 +                    sprintf(leg[2*i+1], "i %s", pairleg[set[i]]);
 +                }
 +                xvgr_legend(fp_pairs, 2*nset, (const char**)leg, oenv);
 +            }
 +
 +            /*
 +             * Store energies for analysis afterwards...
 +             */
 +            if (!bDisRe && !bDHDL && (fr->nre > 0))
 +            {
 +                if (edat.nframes % 1000 == 0)
 +                {
 +                    srenew(time, edat.nframes+1000);
 +                }
 +                time[edat.nframes] = fr->t;
 +                edat.nframes++;
 +            }
 +            /*
 +             * Printing time, only when we do not want to skip frames
 +             */
 +            if (!skip || teller % skip == 0)
 +            {
 +                if (bDisRe)
 +                {
 +                    /*******************************************
 +                     * D I S T A N C E   R E S T R A I N T S
 +                     *******************************************/
 +                    if (ndisre > 0)
 +                    {
 + #ifndef GMX_DOUBLE
 +                        float  *disre_rt     =     blk_disre->sub[0].fval;
 +                        float  *disre_rm3tav = blk_disre->sub[1].fval;
 + #else
 +                        double *disre_rt     =     blk_disre->sub[0].dval;
 +                        double *disre_rm3tav = blk_disre->sub[1].dval;
 + #endif
 +
 +                        print_time(out, fr->t);
 +                        if (violaver == NULL)
 +                        {
 +                            snew(violaver, ndisre);
 +                        }
 +
 +                        /* Subtract bounds from distances, to calculate violations */
 +                        calc_violations(disre_rt, disre_rm3tav,
 +                                        nbounds, pair, bounds, violaver, &sumt, &sumaver);
 +
 +                        fprintf(out, "  %8.4f  %8.4f\n", sumaver, sumt);
 +                        if (bDRAll)
 +                        {
 +                            print_time(fp_pairs, fr->t);
 +                            for (i = 0; (i < nset); i++)
 +                            {
 +                                sss = set[i];
 +                                fprintf(fp_pairs, "  %8.4f", mypow(disre_rm3tav[sss], minthird));
 +                                fprintf(fp_pairs, "  %8.4f", disre_rt[sss]);
 +                            }
 +                            fprintf(fp_pairs, "\n");
 +                        }
 +                        teller_disre++;
 +                    }
 +                }
 +                else if (bDHDL)
 +                {
 +                    do_dhdl(fr, &ir, &fp_dhdl, opt2fn("-odh", NFILE, fnm), bDp, &dh_blocks, &dh_hists, &dh_samples, &dh_lambdas, oenv);
 +                }
 +
 +                /*******************************************
 +                 * E N E R G I E S
 +                 *******************************************/
 +                else
 +                {
 +                    if (fr->nre > 0)
 +                    {
 +                        if (bPrAll)
 +                        {
 +                            /* We skip frames with single points (usually only the first frame),
 +                             * since they would result in an average plot with outliers.
 +                             */
 +                            if (fr->nsum > 1)
 +                            {
 +                                print_time(out, fr->t);
 +                                print1(out, bDp, fr->ener[set[0]].e);
 +                                print1(out, bDp, fr->ener[set[0]].esum/fr->nsum);
 +                                print1(out, bDp, sqrt(fr->ener[set[0]].eav/fr->nsum));
 +                                fprintf(out, "\n");
 +                            }
 +                        }
 +                        else
 +                        {
 +                            print_time(out, fr->t);
 +                            if (bSum)
 +                            {
 +                                sum = 0;
 +                                for (i = 0; i < nset; i++)
 +                                {
 +                                    sum += fr->ener[set[i]].e;
 +                                }
 +                                print1(out, bDp, sum/nmol-ezero);
 +                            }
 +                            else
 +                            {
 +                                for (i = 0; (i < nset); i++)
 +                                {
 +                                    if (bIsEner[i])
 +                                    {
 +                                        print1(out, bDp, (fr->ener[set[i]].e)/nmol-ezero);
 +                                    }
 +                                    else
 +                                    {
 +                                        print1(out, bDp, fr->ener[set[i]].e);
 +                                    }
 +                                }
 +                            }
 +                            fprintf(out, "\n");
 +                        }
 +                    }
 +                    blk = find_block_id_enxframe(fr, enx_i, NULL);
 +                    if (bORIRE && blk)
 +                    {
 +#ifndef GMX_DOUBLE
 +                        xdr_datatype dt = xdr_datatype_float;
 +#else
 +                        xdr_datatype dt = xdr_datatype_double;
 +#endif
 +                        real        *vals;
 +
 +                        if ( (blk->nsub != 1) || (blk->sub[0].type != dt) )
 +                        {
 +                            gmx_fatal(FARGS, "Orientational restraints read in incorrectly");
 +                        }
 +#ifndef GMX_DOUBLE
 +                        vals = blk->sub[0].fval;
 +#else
 +                        vals = blk->sub[0].dval;
 +#endif
 +
 +                        if (blk->sub[0].nr != (size_t)nor)
 +                        {
 +                            gmx_fatal(FARGS, "Number of orientation restraints in energy file (%d) does not match with the topology (%d)", blk->sub[0].nr);
 +                        }
 +                        if (bORA || bODA)
 +                        {
 +                            for (i = 0; i < nor; i++)
 +                            {
 +                                orient[i] += vals[i];
 +                            }
 +                        }
 +                        if (bODR)
 +                        {
 +                            for (i = 0; i < nor; i++)
 +                            {
 +                                odrms[i] += sqr(vals[i]-oobs[i]);
 +                            }
 +                        }
 +                        if (bORT)
 +                        {
 +                            fprintf(fort, "  %10f", fr->t);
 +                            for (i = 0; i < norsel; i++)
 +                            {
 +                                fprintf(fort, " %g", vals[orsel[i]]);
 +                            }
 +                            fprintf(fort, "\n");
 +                        }
 +                        if (bODT)
 +                        {
 +                            fprintf(fodt, "  %10f", fr->t);
 +                            for (i = 0; i < norsel; i++)
 +                            {
 +                                fprintf(fodt, " %g", vals[orsel[i]]-oobs[orsel[i]]);
 +                            }
 +                            fprintf(fodt, "\n");
 +                        }
 +                        norfr++;
 +                    }
 +                    blk = find_block_id_enxframe(fr, enxORT, NULL);
 +                    if (bOTEN && blk)
 +                    {
 +#ifndef GMX_DOUBLE
 +                        xdr_datatype dt = xdr_datatype_float;
 +#else
 +                        xdr_datatype dt = xdr_datatype_double;
 +#endif
 +                        real        *vals;
 +
 +                        if ( (blk->nsub != 1) || (blk->sub[0].type != dt) )
 +                        {
 +                            gmx_fatal(FARGS, "Orientational restraints read in incorrectly");
 +                        }
 +#ifndef GMX_DOUBLE
 +                        vals = blk->sub[0].fval;
 +#else
 +                        vals = blk->sub[0].dval;
 +#endif
 +
 +                        if (blk->sub[0].nr != (size_t)(nex*12))
 +                        {
 +                            gmx_fatal(FARGS, "Number of orientation experiments in energy file (%g) does not match with the topology (%d)",
 +                                      blk->sub[0].nr/12, nex);
 +                        }
 +                        fprintf(foten, "  %10f", fr->t);
 +                        for (i = 0; i < nex; i++)
 +                        {
 +                            for (j = 0; j < (bOvec ? 12 : 3); j++)
 +                            {
 +                                fprintf(foten, " %g", vals[i*12+j]);
 +                            }
 +                        }
 +                        fprintf(foten, "\n");
 +                    }
 +                }
 +            }
 +            teller++;
 +        }
 +    }
 +    while (bCont && (timecheck == 0));
 +
 +    fprintf(stderr, "\n");
 +    close_enx(fp);
 +    if (out)
 +    {
 +        gmx_ffclose(out);
 +    }
 +
 +    if (bDRAll)
 +    {
 +        gmx_ffclose(fp_pairs);
 +    }
 +
 +    if (bORT)
 +    {
 +        gmx_ffclose(fort);
 +    }
 +    if (bODT)
 +    {
 +        gmx_ffclose(fodt);
 +    }
 +    if (bORA)
 +    {
 +        out = xvgropen(opt2fn("-ora", NFILE, fnm),
 +                       "Average calculated orientations",
 +                       "Restraint label", "", oenv);
-         if (bOrinst)
++        if (bOrinst && output_env_get_print_xvgr_codes(oenv))
 +        {
 +            fprintf(out, "%s", orinst_sub);
 +        }
 +        for (i = 0; i < nor; i++)
 +        {
 +            fprintf(out, "%5d  %g\n", or_label[i], orient[i]/norfr);
 +        }
 +        gmx_ffclose(out);
 +    }
 +    if (bODA)
 +    {
 +        out = xvgropen(opt2fn("-oda", NFILE, fnm),
 +                       "Average restraint deviation",
 +                       "Restraint label", "", oenv);
-         if (bOrinst)
++        if (bOrinst && output_env_get_print_xvgr_codes(oenv))
 +        {
 +            fprintf(out, "%s", orinst_sub);
 +        }
 +        for (i = 0; i < nor; i++)
 +        {
 +            fprintf(out, "%5d  %g\n", or_label[i], orient[i]/norfr-oobs[i]);
 +        }
 +        gmx_ffclose(out);
 +    }
 +    if (bODR)
 +    {
 +        out = xvgropen(opt2fn("-odr", NFILE, fnm),
 +                       "RMS orientation restraint deviations",
 +                       "Restraint label", "", oenv);
++        if (bOrinst && output_env_get_print_xvgr_codes(oenv))
 +        {
 +            fprintf(out, "%s", orinst_sub);
 +        }
 +        for (i = 0; i < nor; i++)
 +        {
 +            fprintf(out, "%5d  %g\n", or_label[i], sqrt(odrms[i]/norfr));
 +        }
 +        gmx_ffclose(out);
 +    }
 +    if (bOTEN)
 +    {
 +        gmx_ffclose(foten);
 +    }
 +
 +    if (bDisRe)
 +    {
 +        analyse_disre(opt2fn("-viol", NFILE, fnm),
 +                      teller_disre, violaver, bounds, index, pair, nbounds, oenv);
 +    }
 +    else if (bDHDL)
 +    {
 +        if (fp_dhdl)
 +        {
 +            gmx_ffclose(fp_dhdl);
 +            printf("\n\nWrote %d lambda values with %d samples as ",
 +                   dh_lambdas, dh_samples);
 +            if (dh_hists > 0)
 +            {
 +                printf("%d dH histograms ", dh_hists);
 +            }
 +            if (dh_blocks > 0)
 +            {
 +                printf("%d dH data blocks ", dh_blocks);
 +            }
 +            printf("to %s\n", opt2fn("-odh", NFILE, fnm));
 +
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS, "No dH data in %s\n", opt2fn("-f", NFILE, fnm));
 +        }
 +
 +    }
 +    else
 +    {
 +        double dt = (frame[cur].t-start_t)/(edat.nframes-1);
 +        analyse_ener(opt2bSet("-corr", NFILE, fnm), opt2fn("-corr", NFILE, fnm),
 +                     bFee, bSum, opt2parg_bSet("-nmol", npargs, ppa),
 +                     bVisco, opt2fn("-vis", NFILE, fnm),
 +                     nmol,
 +                     start_step, start_t, frame[cur].step, frame[cur].t,
 +                     reftemp, &edat,
 +                     nset, set, bIsEner, leg, enm, Vaver, ezero, nbmin, nbmax,
 +                     oenv);
 +        if (bFluctProps)
 +        {
 +            calc_fluctuation_props(stdout, bDriftCorr, dt, nset, nmol, leg, &edat,
 +                                   nbmin, nbmax);
 +        }
 +    }
 +    if (opt2bSet("-f2", NFILE, fnm))
 +    {
 +        fec(opt2fn("-f2", NFILE, fnm), opt2fn("-ravg", NFILE, fnm),
 +            reftemp, nset, set, leg, &edat, time, oenv);
 +    }
 +
 +    {
 +        const char *nxy = "-nxy";
 +
 +        do_view(oenv, opt2fn("-o", NFILE, fnm), nxy);
 +        do_view(oenv, opt2fn_null("-ravg", NFILE, fnm), nxy);
 +        do_view(oenv, opt2fn_null("-ora", NFILE, fnm), nxy);
 +        do_view(oenv, opt2fn_null("-ort", NFILE, fnm), nxy);
 +        do_view(oenv, opt2fn_null("-oda", NFILE, fnm), nxy);
 +        do_view(oenv, opt2fn_null("-odr", NFILE, fnm), nxy);
 +        do_view(oenv, opt2fn_null("-odt", NFILE, fnm), nxy);
 +        do_view(oenv, opt2fn_null("-oten", NFILE, fnm), nxy);
 +        do_view(oenv, opt2fn_null("-odh", NFILE, fnm), nxy);
 +    }
 +
 +    return 0;
 +}
index 6257f6a6742d7f6b081e5dc79d56268455531a77,0000000000000000000000000000000000000000..718846578d1ceee1ebc68405de44b837744d77f2
mode 100644,000000..100644
--- /dev/null
@@@ -1,264 -1,0 +1,264 @@@
-                                 lo, hi, hi+1, 1.0);
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include "sysstuff.h"
 +#include "gromacs/commandline/pargs.h"
 +#include <string.h>
 +#include "gromacs/utility/smalloc.h"
 +#include "typedefs.h"
 +#include "gromacs/fileio/confio.h"
 +#include "gromacs/fileio/futil.h"
 +#include "macros.h"
 +#include "vec.h"
 +#include "index.h"
 +#include "gmx_fatal.h"
 +#include "gmx_ana.h"
 +
 +int gmx_genpr(int argc, char *argv[])
 +{
 +    const char        *desc[] = {
 +        "[THISMODULE] produces an #include file for a topology containing",
 +        "a list of atom numbers and three force constants for the",
 +        "[IT]x[it]-, [IT]y[it]-, and [IT]z[it]-direction based on",
 +        "the contents of the [TT]-f[tt] file. A single isotropic force constant may",
 +        "be given on the command line instead of three components.[PAR]",
 +        "WARNING: Position restraints are interactions within molecules, therefore",
 +        "they must be included within the correct [TT][ moleculetype ][tt]",
 +        "block in the topology. The atom indices within the",
 +        "[TT][ position_restraints ][tt] block must be within the range of the",
 +        "atom indices for that molecule type. Since the atom numbers in every",
 +        "moleculetype in the topology start at 1 and the numbers in the input file",
 +        "for [THISMODULE] number consecutively from 1, [THISMODULE] will only",
 +        "produce a useful file for the first molecule. You may wish to",
 +        "edit the resulting index file to remove the lines for later atoms,",
 +        "or construct a suitable index group to provide",
 +        "as input to [THISMODULE].[PAR]",
 +        "The [TT]-of[tt] option produces an index file that can be used for",
 +        "freezing atoms. In this case, the input file must be a [TT].pdb[tt] file.[PAR]",
 +        "With the [TT]-disre[tt] option, half a matrix of distance restraints",
 +        "is generated instead of position restraints. With this matrix, that",
 +        "one typically would apply to C[GRK]alpha[grk] atoms in a protein, one can",
 +        "maintain the overall conformation of a protein without tieing it to",
 +        "a specific position (as with position restraints)."
 +    };
 +    static rvec        fc           = {1000.0, 1000.0, 1000.0};
 +    static real        freeze_level = 0.0;
 +    static real        disre_dist   = 0.1;
 +    static real        disre_frac   = 0.0;
 +    static real        disre_up2    = 1.0;
 +    static gmx_bool    bDisre       = FALSE;
 +    static gmx_bool    bConstr      = FALSE;
 +    static real        cutoff       = -1.0;
 +
 +    t_pargs            pa[] = {
 +        { "-fc", FALSE, etRVEC, {fc},
 +          "Force constants (kJ/mol nm^2)" },
 +        { "-freeze", FALSE, etREAL, {&freeze_level},
 +          "If the [TT]-of[tt] option or this one is given an index file will be written containing atom numbers of all atoms that have a B-factor less than the level given here" },
 +        { "-disre", FALSE, etBOOL, {&bDisre},
 +          "Generate a distance restraint matrix for all the atoms in index" },
 +        { "-disre_dist", FALSE, etREAL, {&disre_dist},
 +          "Distance range around the actual distance for generating distance restraints" },
 +        { "-disre_frac", FALSE, etREAL, {&disre_frac},
 +          "Fraction of distance to be used as interval rather than a fixed distance. If the fraction of the distance that you specify here is less than the distance given in the previous option, that one is used instead." },
 +        { "-disre_up2", FALSE, etREAL, {&disre_up2},
 +          "Distance between upper bound for distance restraints, and the distance at which the force becomes constant (see manual)" },
 +        { "-cutoff", FALSE, etREAL, {&cutoff},
 +          "Only generate distance restraints for atoms pairs within cutoff (nm)" },
 +        { "-constr", FALSE, etBOOL, {&bConstr},
 +          "Generate a constraint matrix rather than distance restraints. Constraints of type 2 will be generated that do generate exclusions." }
 +    };
 +#define npargs asize(pa)
 +
 +    output_env_t     oenv;
 +    t_atoms         *atoms = NULL;
 +    int              i, j, k;
 +    FILE            *out;
 +    int              igrp;
 +    real             d, dd, lo, hi;
 +    atom_id         *ind_grp;
 +    const char      *xfn, *nfn;
 +    char            *gn_grp;
 +    char             title[STRLEN];
 +    matrix           box;
 +    gmx_bool         bFreeze;
 +    rvec             dx, *x = NULL, *v = NULL;
 +
 +    t_filenm         fnm[] = {
 +        { efSTX, "-f",  NULL,    ffREAD },
 +        { efNDX, "-n",  NULL,    ffOPTRD },
 +        { efITP, "-o",  "posre", ffWRITE },
 +        { efNDX, "-of", "freeze",    ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +
 +    if (!parse_common_args(&argc, argv, 0, NFILE, fnm, npargs, pa,
 +                           asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    bFreeze = opt2bSet("-of", NFILE, fnm) || opt2parg_bSet("-freeze", asize(pa), pa);
 +    bDisre  = bDisre || opt2parg_bSet("-disre_dist", npargs, pa);
 +    xfn     = opt2fn_null("-f", NFILE, fnm);
 +    nfn     = opt2fn_null("-n", NFILE, fnm);
 +
 +    if (( nfn == NULL ) && ( xfn == NULL))
 +    {
 +        gmx_fatal(FARGS, "no index file and no structure file supplied");
 +    }
 +
 +    if ((disre_frac < 0) || (disre_frac >= 1))
 +    {
 +        gmx_fatal(FARGS, "disre_frac should be between 0 and 1");
 +    }
 +    if (disre_dist < 0)
 +    {
 +        gmx_fatal(FARGS, "disre_dist should be >= 0");
 +    }
 +
 +    if (xfn != NULL)
 +    {
 +        snew(atoms, 1);
 +        get_stx_coordnum(xfn, &(atoms->nr));
 +        init_t_atoms(atoms, atoms->nr, TRUE);
 +        snew(x, atoms->nr);
 +        snew(v, atoms->nr);
 +        fprintf(stderr, "\nReading structure file\n");
 +        read_stx_conf(xfn, title, atoms, x, v, NULL, box);
 +    }
 +
 +    if (bFreeze)
 +    {
 +        if (!atoms || !atoms->pdbinfo)
 +        {
 +            gmx_fatal(FARGS, "No B-factors in input file %s, use a pdb file next time.",
 +                      xfn);
 +        }
 +
 +        out = opt2FILE("-of", NFILE, fnm, "w");
 +        fprintf(out, "[ freeze ]\n");
 +        for (i = 0; (i < atoms->nr); i++)
 +        {
 +            if (atoms->pdbinfo[i].bfac <= freeze_level)
 +            {
 +                fprintf(out, "%d\n", i+1);
 +            }
 +        }
 +        gmx_ffclose(out);
 +    }
 +    else if ((bDisre || bConstr) && x)
 +    {
 +        printf("Select group to generate %s matrix from\n",
 +               bConstr ? "constraint" : "distance restraint");
 +        get_index(atoms, nfn, 1, &igrp, &ind_grp, &gn_grp);
 +
 +        out = ftp2FILE(efITP, NFILE, fnm, "w");
 +        if (bConstr)
 +        {
 +            fprintf(out, "; constraints for %s of %s\n\n", gn_grp, title);
 +            fprintf(out, "[ constraints ]\n");
 +            fprintf(out, ";%4s %5s %1s %10s\n", "i", "j", "tp", "dist");
 +        }
 +        else
 +        {
 +            fprintf(out, "; distance restraints for %s of %s\n\n", gn_grp, title);
 +            fprintf(out, "[ distance_restraints ]\n");
 +            fprintf(out, ";%4s %5s %1s %5s %10s %10s %10s %10s %10s\n", "i", "j", "?",
 +                    "label", "funct", "lo", "up1", "up2", "weight");
 +        }
 +        for (i = k = 0; i < igrp; i++)
 +        {
 +            for (j = i+1; j < igrp; j++, k++)
 +            {
 +                rvec_sub(x[ind_grp[i]], x[ind_grp[j]], dx);
 +                d = norm(dx);
 +                if (bConstr)
 +                {
 +                    fprintf(out, "%5d %5d %1d %10g\n", ind_grp[i]+1, ind_grp[j]+1, 2, d);
 +                }
 +                else
 +                {
 +                    if (cutoff < 0 || d < cutoff)
 +                    {
 +                        if (disre_frac > 0)
 +                        {
 +                            dd = min(disre_dist, disre_frac*d);
 +                        }
 +                        else
 +                        {
 +                            dd = disre_dist;
 +                        }
 +                        lo = max(0, d-dd);
 +                        hi = d+dd;
 +                        fprintf(out, "%5d %5d %1d %5d %10d %10g %10g %10g %10g\n",
 +                                ind_grp[i]+1, ind_grp[j]+1, 1, k, 1,
++                                lo, hi, hi+disre_up2, 1.0);
 +                    }
 +                }
 +            }
 +        }
 +        gmx_ffclose(out);
 +    }
 +    else
 +    {
 +        printf("Select group to position restrain\n");
 +        get_index(atoms, nfn, 1, &igrp, &ind_grp, &gn_grp);
 +
 +        out = ftp2FILE(efITP, NFILE, fnm, "w");
 +        fprintf(out, "; position restraints for %s of %s\n\n", gn_grp, title);
 +        fprintf(out, "[ position_restraints ]\n");
 +        fprintf(out, ";%3s %5s %9s %10s %10s\n", "i", "funct", "fcx", "fcy", "fcz");
 +        for (i = 0; i < igrp; i++)
 +        {
 +            fprintf(out, "%4d %4d %10g %10g %10g\n",
 +                    ind_grp[i]+1, 1, fc[XX], fc[YY], fc[ZZ]);
 +        }
 +        gmx_ffclose(out);
 +    }
 +    if (xfn)
 +    {
 +        sfree(x);
 +        sfree(v);
 +    }
 +
 +    return 0;
 +}
index 09900e1ff4e0dafcc67534f0c320cf13f5593c42,0000000000000000000000000000000000000000..029edf26c28abb9980bf2972a55f3cf4386f370f
mode 100644,000000..100644
--- /dev/null
@@@ -1,402 -1,0 +1,404 @@@
-         fprintf(fp, "@ legend on\n");
-         fprintf(fp, "@ legend box on\n");
-         fprintf(fp, "@ legend loctype view\n");
-         fprintf(fp, "@ legend 0.75, 0.8\n");
-         fprintf(fp, "@ legend string 0 \"Total/mean\"\n");
-         fprintf(fp, "@ legend string 1 \"Total\"\n");
-         fprintf(fp, "@ legend string 2 \"Mean\"\n");
-         fprintf(fp, "@ legend string 3 \"# atoms\"\n");
-         fprintf(fp, "@ legend string 4 \"Mean/# atoms\"\n");
-         fprintf(fp, "#%3s %8s  %3s  %8s  %3s  %8s\n",
-                 "res", "ratio", "tot", "mean", "natm", "mean/atm");
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <string.h>
 +
 +#include "macros.h"
 +#include "vec.h"
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "gromacs/fileio/filenm.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gmx_fatal.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "gromacs/fileio/matio.h"
 +#include "xvgr.h"
 +#include "index.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "rmpbc.h"
 +#include "pbc.h"
 +#include "gmx_ana.h"
 +
 +
 +#define FARAWAY 10000
 +
 +int *res_ndx(t_atoms *atoms)
 +{
 +    int *rndx;
 +    int  i, r0;
 +
 +    if (atoms->nr <= 0)
 +    {
 +        return NULL;
 +    }
 +    snew(rndx, atoms->nr);
 +    r0 = atoms->atom[0].resind;
 +    for (i = 0; (i < atoms->nr); i++)
 +    {
 +        rndx[i] = atoms->atom[i].resind-r0;
 +    }
 +
 +    return rndx;
 +}
 +
 +int *res_natm(t_atoms *atoms)
 +{
 +    int *natm;
 +    int  i, j, r0;
 +
 +    if (atoms->nr <= 0)
 +    {
 +        return NULL;
 +    }
 +    snew(natm, atoms->nres);
 +    r0 = atoms->atom[0].resind;
 +    j  = 0;
 +    for (i = 0; (i < atoms->nres); i++)
 +    {
 +        while ((atoms->atom[j].resind)-r0 == i)
 +        {
 +            natm[i]++;
 +            j++;
 +        }
 +    }
 +
 +    return natm;
 +}
 +
 +static void calc_mat(int nres, int natoms, int rndx[],
 +                     rvec x[], atom_id *index,
 +                     real trunc, real **mdmat, int **nmat, int ePBC, matrix box)
 +{
 +    int   i, j, resi, resj;
 +    real  trunc2, r, r2;
 +    t_pbc pbc;
 +    rvec  ddx;
 +
 +    set_pbc(&pbc, ePBC, box);
 +    trunc2 = sqr(trunc);
 +    for (resi = 0; (resi < nres); resi++)
 +    {
 +        for (resj = 0; (resj < nres); resj++)
 +        {
 +            mdmat[resi][resj] = FARAWAY;
 +        }
 +    }
 +    for (i = 0; (i < natoms); i++)
 +    {
 +        resi = rndx[i];
 +        for (j = i+1; (j < natoms); j++)
 +        {
 +            resj = rndx[j];
 +            pbc_dx(&pbc, x[index[i]], x[index[j]], ddx);
 +            r2 = norm2(ddx);
 +            if (r2 < trunc2)
 +            {
 +                nmat[resi][j]++;
 +                nmat[resj][i]++;
 +            }
 +            mdmat[resi][resj] = min(r2, mdmat[resi][resj]);
 +        }
 +    }
 +
 +    for (resi = 0; (resi < nres); resi++)
 +    {
 +        mdmat[resi][resi] = 0;
 +        for (resj = resi+1; (resj < nres); resj++)
 +        {
 +            r                 = sqrt(mdmat[resi][resj]);
 +            mdmat[resi][resj] = r;
 +            mdmat[resj][resi] = r;
 +        }
 +    }
 +}
 +
 +static void tot_nmat(int nres, int natoms, int nframes, int **nmat,
 +                     int *tot_n, real *mean_n)
 +{
 +    int i, j;
 +
 +    for (i = 0; (i < nres); i++)
 +    {
 +        for (j = 0; (j < natoms); j++)
 +        {
 +            if (nmat[i][j] != 0)
 +            {
 +                tot_n[i]++;
 +                mean_n[i] += nmat[i][j];
 +            }
 +        }
 +        mean_n[i] /= nframes;
 +    }
 +}
 +
 +int gmx_mdmat(int argc, char *argv[])
 +{
 +    const char     *desc[] = {
 +        "[THISMODULE] makes distance matrices consisting of the smallest distance",
 +        "between residue pairs. With [TT]-frames[tt], these distance matrices can be",
 +        "stored in order to see differences in tertiary structure as a",
 +        "function of time. If you choose your options unwisely, this may generate",
 +        "a large output file. By default, only an averaged matrix over the whole",
 +        "trajectory is output.",
 +        "Also a count of the number of different atomic contacts between",
 +        "residues over the whole trajectory can be made.",
 +        "The output can be processed with [gmx-xpm2ps] to make a PostScript (tm) plot."
 +    };
 +    static real     truncate = 1.5;
 +    static gmx_bool bAtom    = FALSE;
 +    static int      nlevels  = 40;
 +    t_pargs         pa[]     = {
 +        { "-t",   FALSE, etREAL, {&truncate},
 +          "trunc distance" },
 +        { "-nlevels",   FALSE, etINT,  {&nlevels},
 +          "Discretize distance in this number of levels" }
 +    };
 +    t_filenm        fnm[] = {
 +        { efTRX, "-f",  NULL, ffREAD },
 +        { efTPS, NULL,  NULL, ffREAD },
 +        { efNDX, NULL,  NULL, ffOPTRD },
 +        { efXPM, "-mean", "dm", ffWRITE },
 +        { efXPM, "-frames", "dmf", ffOPTWR },
 +        { efXVG, "-no", "num", ffOPTWR },
 +    };
 +#define NFILE asize(fnm)
 +
 +    FILE          *out = NULL, *fp;
 +    t_topology     top;
 +    int            ePBC;
 +    t_atoms        useatoms;
 +    int            isize;
 +    atom_id       *index;
 +    char          *grpname;
 +    int           *rndx, *natm, prevres, newres;
 +
 +    int            i, j, nres, natoms, nframes, it, trxnat;
 +    t_trxstatus   *status;
 +    int            nr0;
 +    gmx_bool       bCalcN, bFrames;
 +    real           t, ratio;
 +    char           title[256], label[234];
 +    t_rgb          rlo, rhi;
 +    rvec          *x;
 +    real         **mdmat, *resnr, **totmdmat;
 +    int          **nmat, **totnmat;
 +    real          *mean_n;
 +    int           *tot_n;
 +    matrix         box;
 +    output_env_t   oenv;
 +    gmx_rmpbc_t    gpbc = NULL;
 +
 +    if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_BE_NICE, NFILE, fnm,
 +                           asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    fprintf(stderr, "Will truncate at %f nm\n", truncate);
 +    bCalcN  = opt2bSet("-no", NFILE, fnm);
 +    bFrames = opt2bSet("-frames", NFILE, fnm);
 +    if (bCalcN)
 +    {
 +        fprintf(stderr, "Will calculate number of different contacts\n");
 +    }
 +
 +    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), title, &top, &ePBC, &x, NULL, box, FALSE);
 +
 +    fprintf(stderr, "Select group for analysis\n");
 +    get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, &grpname);
 +
 +    natoms = isize;
 +    snew(useatoms.atom, natoms);
 +    snew(useatoms.atomname, natoms);
 +
 +    useatoms.nres = 0;
 +    snew(useatoms.resinfo, natoms);
 +
 +    prevres = top.atoms.atom[index[0]].resind;
 +    newres  = 0;
 +    for (i = 0; (i < isize); i++)
 +    {
 +        int ii = index[i];
 +        useatoms.atomname[i] = top.atoms.atomname[ii];
 +        if (top.atoms.atom[ii].resind != prevres)
 +        {
 +            prevres = top.atoms.atom[ii].resind;
 +            newres++;
 +            useatoms.resinfo[i] = top.atoms.resinfo[prevres];
 +            if (debug)
 +            {
 +                fprintf(debug, "New residue: atom %5s %5s %6d, index entry %5d, newres %5d\n",
 +                        *(top.atoms.resinfo[top.atoms.atom[ii].resind].name),
 +                        *(top.atoms.atomname[ii]),
 +                        ii, i, newres);
 +            }
 +        }
 +        useatoms.atom[i].resind = newres;
 +    }
 +    useatoms.nres = newres+1;
 +    useatoms.nr   = isize;
 +
 +    rndx = res_ndx(&(useatoms));
 +    natm = res_natm(&(useatoms));
 +    nres = useatoms.nres;
 +    fprintf(stderr, "There are %d residues with %d atoms\n", nres, natoms);
 +
 +    snew(resnr, nres);
 +    snew(mdmat, nres);
 +    snew(nmat, nres);
 +    snew(totnmat, nres);
 +    snew(mean_n, nres);
 +    snew(tot_n, nres);
 +    for (i = 0; (i < nres); i++)
 +    {
 +        snew(mdmat[i], nres);
 +        snew(nmat[i], natoms);
 +        snew(totnmat[i], natoms);
 +        resnr[i] = i+1;
 +    }
 +    snew(totmdmat, nres);
 +    for (i = 0; (i < nres); i++)
 +    {
 +        snew(totmdmat[i], nres);
 +    }
 +
 +    trxnat = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
 +
 +    nframes = 0;
 +
 +    rlo.r = 1.0, rlo.g = 1.0, rlo.b = 1.0;
 +    rhi.r = 0.0, rhi.g = 0.0, rhi.b = 0.0;
 +
 +    gpbc = gmx_rmpbc_init(&top.idef, ePBC, trxnat);
 +
 +    if (bFrames)
 +    {
 +        out = opt2FILE("-frames", NFILE, fnm, "w");
 +    }
 +    do
 +    {
 +        gmx_rmpbc(gpbc, trxnat, box, x);
 +        nframes++;
 +        calc_mat(nres, natoms, rndx, x, index, truncate, mdmat, nmat, ePBC, box);
 +        for (i = 0; (i < nres); i++)
 +        {
 +            for (j = 0; (j < natoms); j++)
 +            {
 +                if (nmat[i][j])
 +                {
 +                    totnmat[i][j]++;
 +                }
 +            }
 +        }
 +        for (i = 0; (i < nres); i++)
 +        {
 +            for (j = 0; (j < nres); j++)
 +            {
 +                totmdmat[i][j] += mdmat[i][j];
 +            }
 +        }
 +        if (bFrames)
 +        {
 +            sprintf(label, "t=%.0f ps", t);
 +            write_xpm(out, 0, label, "Distance (nm)", "Residue Index", "Residue Index",
 +                      nres, nres, resnr, resnr, mdmat, 0, truncate, rlo, rhi, &nlevels);
 +        }
 +    }
 +    while (read_next_x(oenv, status, &t, x, box));
 +    fprintf(stderr, "\n");
 +    close_trj(status);
 +    gmx_rmpbc_done(gpbc);
 +    if (bFrames)
 +    {
 +        gmx_ffclose(out);
 +    }
 +
 +    fprintf(stderr, "Processed %d frames\n", nframes);
 +
 +    for (i = 0; (i < nres); i++)
 +    {
 +        for (j = 0; (j < nres); j++)
 +        {
 +            totmdmat[i][j] /= nframes;
 +        }
 +    }
 +    write_xpm(opt2FILE("-mean", NFILE, fnm, "w"), 0, "Mean smallest distance",
 +              "Distance (nm)", "Residue Index", "Residue Index",
 +              nres, nres, resnr, resnr, totmdmat, 0, truncate, rlo, rhi, &nlevels);
 +
 +    if (bCalcN)
 +    {
++        char **legend;
++
++        snew(legend, 5);
++        for (i = 0; i < 5; i++)
++        {
++            snew(legend[i], STRLEN);
++        }
 +        tot_nmat(nres, natoms, nframes, totnmat, tot_n, mean_n);
 +        fp = xvgropen(ftp2fn(efXVG, NFILE, fnm),
 +                      "Increase in number of contacts", "Residue", "Ratio", oenv);
++        sprintf(legend[0], "Total/mean");
++        sprintf(legend[1], "Total");
++        sprintf(legend[2], "Mean");
++        sprintf(legend[3], "# atoms");
++        sprintf(legend[4], "Mean/# atoms");
++        xvgr_legend(fp, 5, (const char**)legend, oenv);
 +        for (i = 0; (i < nres); i++)
 +        {
 +            if (mean_n[i] == 0)
 +            {
 +                ratio = 1;
 +            }
 +            else
 +            {
 +                ratio = tot_n[i]/mean_n[i];
 +            }
 +            fprintf(fp, "%3d  %8.3f  %3d  %8.3f  %3d  %8.3f\n",
 +                    i+1, ratio, tot_n[i], mean_n[i], natm[i], mean_n[i]/natm[i]);
 +        }
 +        gmx_ffclose(fp);
 +    }
 +
 +    return 0;
 +}
index 311e3d687365422e2222e2d0eb08d85e9339cef3,0000000000000000000000000000000000000000..8fbb7d3712a6b426e4743d5e5b8adebadf2677dd
mode 100644,000000..100644
--- /dev/null
@@@ -1,798 -1,0 +1,816 @@@
- static void periodic_dist(matrix box, rvec x[], int n, atom_id index[],
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <stdlib.h>
 +
 +#include "sysstuff.h"
 +#include <string.h>
 +#include "typedefs.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "vec.h"
 +#include "xvgr.h"
 +#include "pbc.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "index.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "rmpbc.h"
 +#include "gmx_ana.h"
++#include "names.h"
 +
 +
- #define NSHIFT 26
-     int  sx, sy, sz, i, j, s;
++static void periodic_dist(int ePBC,
++                          matrix box, rvec x[], int n, atom_id index[],
 +                          real *rmin, real *rmax, int *min_ind)
 +{
-     rvec shift[NSHIFT], d0, d;
++#define NSHIFT_MAX 26
++    int  nsz, nshift, sx, sy, sz, i, j, s;
 +    real sqr_box, r2min, r2max, r2;
-     sqr_box = sqr(min(norm(box[XX]), min(norm(box[YY]), norm(box[ZZ]))));
++    rvec shift[NSHIFT_MAX], d0, d;
 +
-     s = 0;
-     for (sz = -1; sz <= 1; sz++)
++    sqr_box = min(norm2(box[XX]), norm2(box[YY]));
++    if (ePBC == epbcXYZ)
++    {
++        sqr_box = min(sqr_box, norm2(box[ZZ]));
++        nsz     = 1;
++    }
++    else if (ePBC == epbcXY)
++    {
++        nsz = 0;
++    }
++    else
++    {
++        gmx_fatal(FARGS, "pbc = %s is not supported by g_mindist",
++                  epbc_names[ePBC]);
++        nsz = 0; /* Keep compilers quiet */
++    }
 +
-                         shift[s][i] = sx*box[XX][i]+sy*box[YY][i]+sz*box[ZZ][i];
++    nshift = 0;
++    for (sz = -nsz; sz <= nsz; sz++)
 +    {
 +        for (sy = -1; sy <= 1; sy++)
 +        {
 +            for (sx = -1; sx <= 1; sx++)
 +            {
 +                if (sx != 0 || sy != 0 || sz != 0)
 +                {
 +                    for (i = 0; i < DIM; i++)
 +                    {
-                     s++;
++                        shift[nshift][i] =
++                            sx*box[XX][i] + sy*box[YY][i] + sz*box[ZZ][i];
 +                    }
-             for (s = 0; s < NSHIFT; s++)
++                    nshift++;
 +                }
 +            }
 +        }
 +    }
 +
 +    r2min = sqr_box;
 +    r2max = 0;
 +
 +    for (i = 0; i < n; i++)
 +    {
 +        for (j = i+1; j < n; j++)
 +        {
 +            rvec_sub(x[index[i]], x[index[j]], d0);
 +            r2 = norm2(d0);
 +            if (r2 > r2max)
 +            {
 +                r2max = r2;
 +            }
-         periodic_dist(box, x, n, index, &rmin, &rmax, ind_min);
++            for (s = 0; s < nshift; s++)
 +            {
 +                rvec_add(d0, shift[s], d);
 +                r2 = norm2(d);
 +                if (r2 < r2min)
 +                {
 +                    r2min      = r2;
 +                    min_ind[0] = i;
 +                    min_ind[1] = j;
 +                }
 +            }
 +        }
 +    }
 +
 +    *rmin = sqrt(r2min);
 +    *rmax = sqrt(r2max);
 +}
 +
 +static void periodic_mindist_plot(const char *trxfn, const char *outfn,
 +                                  t_topology *top, int ePBC,
 +                                  int n, atom_id index[], gmx_bool bSplit,
 +                                  const output_env_t oenv)
 +{
 +    FILE        *out;
 +    const char  *leg[5] = { "min per.", "max int.", "box1", "box2", "box3" };
 +    t_trxstatus *status;
 +    real         t;
 +    rvec        *x;
 +    matrix       box;
 +    int          natoms, ind_min[2] = {0, 0}, ind_mini = 0, ind_minj = 0;
 +    real         r, rmin, rmax, rmint, tmint;
 +    gmx_bool     bFirst;
 +    gmx_rmpbc_t  gpbc = NULL;
 +
 +    natoms = read_first_x(oenv, &status, trxfn, &t, &x, box);
 +
 +    check_index(NULL, n, index, NULL, natoms);
 +
 +    out = xvgropen(outfn, "Minimum distance to periodic image",
 +                   output_env_get_time_label(oenv), "Distance (nm)", oenv);
 +    if (output_env_get_print_xvgr_codes(oenv))
 +    {
 +        fprintf(out, "@ subtitle \"and maximum internal distance\"\n");
 +    }
 +    xvgr_legend(out, 5, leg, oenv);
 +
 +    rmint = box[XX][XX];
 +    tmint = 0;
 +
 +    if (NULL != top)
 +    {
 +        gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
 +    }
 +
 +    bFirst = TRUE;
 +    do
 +    {
 +        if (NULL != top)
 +        {
 +            gmx_rmpbc(gpbc, natoms, box, x);
 +        }
 +
-             fprintf(out, "&\n");
++        periodic_dist(ePBC, box, x, n, index, &rmin, &rmax, ind_min);
 +        if (rmin < rmint)
 +        {
 +            rmint    = rmin;
 +            tmint    = t;
 +            ind_mini = ind_min[0];
 +            ind_minj = ind_min[1];
 +        }
 +        if (bSplit && !bFirst && fabs(t/output_env_get_time_factor(oenv)) < 1e-5)
 +        {
-             fprintf(dist, "&\n");
++            fprintf(out, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +        }
 +        fprintf(out, "\t%g\t%6.3f %6.3f %6.3f %6.3f %6.3f\n",
 +                output_env_conv_time(oenv, t), rmin, rmax, norm(box[0]), norm(box[1]), norm(box[2]));
 +        bFirst = FALSE;
 +    }
 +    while (read_next_x(oenv, status, &t, x, box));
 +
 +    if (NULL != top)
 +    {
 +        gmx_rmpbc_done(gpbc);
 +    }
 +
 +    gmx_ffclose(out);
 +
 +    fprintf(stdout,
 +            "\nThe shortest periodic distance is %g (nm) at time %g (%s),\n"
 +            "between atoms %d and %d\n",
 +            rmint, output_env_conv_time(oenv, tmint), output_env_get_time_unit(oenv),
 +            index[ind_mini]+1, index[ind_minj]+1);
 +}
 +
 +static void calc_dist(real rcut, gmx_bool bPBC, int ePBC, matrix box, rvec x[],
 +                      int nx1, int nx2, atom_id index1[], atom_id index2[],
 +                      gmx_bool bGroup,
 +                      real *rmin, real *rmax, int *nmin, int *nmax,
 +                      int *ixmin, int *jxmin, int *ixmax, int *jxmax)
 +{
 +    int      i, j, i0 = 0, j1;
 +    int      ix, jx;
 +    atom_id *index3;
 +    rvec     dx;
 +    real     r2, rmin2, rmax2, rcut2;
 +    t_pbc    pbc;
 +    int      nmin_j, nmax_j;
 +
 +    *ixmin = -1;
 +    *jxmin = -1;
 +    *ixmax = -1;
 +    *jxmax = -1;
 +    *nmin  = 0;
 +    *nmax  = 0;
 +
 +    rcut2 = sqr(rcut);
 +
 +    /* Must init pbc every step because of pressure coupling */
 +    if (bPBC)
 +    {
 +        set_pbc(&pbc, ePBC, box);
 +    }
 +    if (index2)
 +    {
 +        i0     = 0;
 +        j1     = nx2;
 +        index3 = index2;
 +    }
 +    else
 +    {
 +        j1     = nx1;
 +        index3 = index1;
 +    }
 +
 +    rmin2 = 1e12;
 +    rmax2 = -1e12;
 +
 +    for (j = 0; (j < j1); j++)
 +    {
 +        jx = index3[j];
 +        if (index2 == NULL)
 +        {
 +            i0 = j + 1;
 +        }
 +        nmin_j = 0;
 +        nmax_j = 0;
 +        for (i = i0; (i < nx1); i++)
 +        {
 +            ix = index1[i];
 +            if (ix != jx)
 +            {
 +                if (bPBC)
 +                {
 +                    pbc_dx(&pbc, x[ix], x[jx], dx);
 +                }
 +                else
 +                {
 +                    rvec_sub(x[ix], x[jx], dx);
 +                }
 +                r2 = iprod(dx, dx);
 +                if (r2 < rmin2)
 +                {
 +                    rmin2  = r2;
 +                    *ixmin = ix;
 +                    *jxmin = jx;
 +                }
 +                if (r2 > rmax2)
 +                {
 +                    rmax2  = r2;
 +                    *ixmax = ix;
 +                    *jxmax = jx;
 +                }
 +                if (r2 <= rcut2)
 +                {
 +                    nmin_j++;
 +                }
 +                else if (r2 > rcut2)
 +                {
 +                    nmax_j++;
 +                }
 +            }
 +        }
 +        if (bGroup)
 +        {
 +            if (nmin_j > 0)
 +            {
 +                (*nmin)++;
 +            }
 +            if (nmax_j > 0)
 +            {
 +                (*nmax)++;
 +            }
 +        }
 +        else
 +        {
 +            *nmin += nmin_j;
 +            *nmax += nmax_j;
 +        }
 +    }
 +    *rmin = sqrt(rmin2);
 +    *rmax = sqrt(rmax2);
 +}
 +
 +void dist_plot(const char *fn, const char *afile, const char *dfile,
 +               const char *nfile, const char *rfile, const char *xfile,
 +               real rcut, gmx_bool bMat, t_atoms *atoms,
 +               int ng, atom_id *index[], int gnx[], char *grpn[], gmx_bool bSplit,
 +               gmx_bool bMin, int nres, atom_id *residue, gmx_bool bPBC, int ePBC,
 +               gmx_bool bGroup, gmx_bool bEachResEachTime, gmx_bool bPrintResName,
 +               const output_env_t oenv)
 +{
 +    FILE            *atm, *dist, *num;
 +    t_trxstatus     *trxout;
 +    char             buf[256];
 +    char           **leg;
 +    real             t, dmin, dmax, **mindres = NULL, **maxdres = NULL;
 +    int              nmin, nmax;
 +    t_trxstatus     *status;
 +    int              i = -1, j, k, natoms;
 +    int              min1, min2, max1, max2, min1r, min2r, max1r, max2r;
 +    atom_id          oindex[2];
 +    rvec            *x0;
 +    matrix           box;
 +    t_trxframe       frout;
 +    gmx_bool         bFirst;
 +    FILE            *respertime = NULL;
 +
 +    if ((natoms = read_first_x(oenv, &status, fn, &t, &x0, box)) == 0)
 +    {
 +        gmx_fatal(FARGS, "Could not read coordinates from statusfile\n");
 +    }
 +
 +    sprintf(buf, "%simum Distance", bMin ? "Min" : "Max");
 +    dist = xvgropen(dfile, buf, output_env_get_time_label(oenv), "Distance (nm)", oenv);
 +    sprintf(buf, "Number of Contacts %s %g nm", bMin ? "<" : ">", rcut);
 +    num    = nfile ? xvgropen(nfile, buf, output_env_get_time_label(oenv), "Number", oenv) : NULL;
 +    atm    = afile ? gmx_ffopen(afile, "w") : NULL;
 +    trxout = xfile ? open_trx(xfile, "w") : NULL;
 +
 +    if (bMat)
 +    {
 +        if (ng == 1)
 +        {
 +            snew(leg, 1);
 +            sprintf(buf, "Internal in %s", grpn[0]);
 +            leg[0] = strdup(buf);
 +            xvgr_legend(dist, 0, (const char**)leg, oenv);
 +            if (num)
 +            {
 +                xvgr_legend(num, 0, (const char**)leg, oenv);
 +            }
 +        }
 +        else
 +        {
 +            snew(leg, (ng*(ng-1))/2);
 +            for (i = j = 0; (i < ng-1); i++)
 +            {
 +                for (k = i+1; (k < ng); k++, j++)
 +                {
 +                    sprintf(buf, "%s-%s", grpn[i], grpn[k]);
 +                    leg[j] = strdup(buf);
 +                }
 +            }
 +            xvgr_legend(dist, j, (const char**)leg, oenv);
 +            if (num)
 +            {
 +                xvgr_legend(num, j, (const char**)leg, oenv);
 +            }
 +        }
 +    }
 +    else
 +    {
 +        snew(leg, ng-1);
 +        for (i = 0; (i < ng-1); i++)
 +        {
 +            sprintf(buf, "%s-%s", grpn[0], grpn[i+1]);
 +            leg[i] = strdup(buf);
 +        }
 +        xvgr_legend(dist, ng-1, (const char**)leg, oenv);
 +        if (num)
 +        {
 +            xvgr_legend(num, ng-1, (const char**)leg, oenv);
 +        }
 +    }
 +
 +    if (bEachResEachTime)
 +    {
 +        sprintf(buf, "%simum Distance", bMin ? "Min" : "Max");
 +        respertime = xvgropen(rfile, buf, output_env_get_time_label(oenv), "Distance (nm)", oenv);
 +        xvgr_legend(respertime, ng-1, (const char**)leg, oenv);
 +        if (bPrintResName)
 +        {
 +            fprintf(respertime, "# ");
 +        }
 +        for (j = 0; j < nres; j++)
 +        {
 +            fprintf(respertime, "%s%d ", *(atoms->resinfo[atoms->atom[index[0][residue[j]]].resind].name), atoms->atom[index[0][residue[j]]].resind);
 +        }
 +        fprintf(respertime, "\n");
 +
 +    }
 +
 +    j = 0;
 +    if (nres)
 +    {
 +        snew(mindres, ng-1);
 +        snew(maxdres, ng-1);
 +        for (i = 1; i < ng; i++)
 +        {
 +            snew(mindres[i-1], nres);
 +            snew(maxdres[i-1], nres);
 +            for (j = 0; j < nres; j++)
 +            {
 +                mindres[i-1][j] = 1e6;
 +            }
 +            /* maxdres[*][*] is already 0 */
 +        }
 +    }
 +    bFirst = TRUE;
 +    do
 +    {
 +        if (bSplit && !bFirst && fabs(t/output_env_get_time_factor(oenv)) < 1e-5)
 +        {
-                 fprintf(num, "&\n");
++            fprintf(dist, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +            if (num)
 +            {
-                 fprintf(atm, "&\n");
++                fprintf(num, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +            }
 +            if (atm)
 +            {
++                fprintf(atm, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +            }
 +        }
 +        fprintf(dist, "%12e", output_env_conv_time(oenv, t));
 +        if (num)
 +        {
 +            fprintf(num, "%12e", output_env_conv_time(oenv, t));
 +        }
 +
 +        if (bMat)
 +        {
 +            if (ng == 1)
 +            {
 +                calc_dist(rcut, bPBC, ePBC, box, x0, gnx[0], gnx[0], index[0], index[0], bGroup,
 +                          &dmin, &dmax, &nmin, &nmax, &min1, &min2, &max1, &max2);
 +                fprintf(dist, "  %12e", bMin ? dmin : dmax);
 +                if (num)
 +                {
 +                    fprintf(num, "  %8d", bMin ? nmin : nmax);
 +                }
 +            }
 +            else
 +            {
 +                for (i = 0; (i < ng-1); i++)
 +                {
 +                    for (k = i+1; (k < ng); k++)
 +                    {
 +                        calc_dist(rcut, bPBC, ePBC, box, x0, gnx[i], gnx[k], index[i], index[k],
 +                                  bGroup, &dmin, &dmax, &nmin, &nmax, &min1, &min2, &max1, &max2);
 +                        fprintf(dist, "  %12e", bMin ? dmin : dmax);
 +                        if (num)
 +                        {
 +                            fprintf(num, "  %8d", bMin ? nmin : nmax);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        else
 +        {
 +            for (i = 1; (i < ng); i++)
 +            {
 +                calc_dist(rcut, bPBC, ePBC, box, x0, gnx[0], gnx[i], index[0], index[i], bGroup,
 +                          &dmin, &dmax, &nmin, &nmax, &min1, &min2, &max1, &max2);
 +                fprintf(dist, "  %12e", bMin ? dmin : dmax);
 +                if (num)
 +                {
 +                    fprintf(num, "  %8d", bMin ? nmin : nmax);
 +                }
 +                if (nres)
 +                {
 +                    for (j = 0; j < nres; j++)
 +                    {
 +                        calc_dist(rcut, bPBC, ePBC, box, x0, residue[j+1]-residue[j], gnx[i],
 +                                  &(index[0][residue[j]]), index[i], bGroup,
 +                                  &dmin, &dmax, &nmin, &nmax, &min1r, &min2r, &max1r, &max2r);
 +                        mindres[i-1][j] = min(mindres[i-1][j], dmin);
 +                        maxdres[i-1][j] = max(maxdres[i-1][j], dmax);
 +                    }
 +                }
 +            }
 +        }
 +        fprintf(dist, "\n");
 +        if (num)
 +        {
 +            fprintf(num, "\n");
 +        }
 +        if ( (bMin ? min1 : max1) != -1)
 +        {
 +            if (atm)
 +            {
 +                fprintf(atm, "%12e  %12d  %12d\n",
 +                        output_env_conv_time(oenv, t), 1+(bMin ? min1 : max1),
 +                        1+(bMin ? min2 : max2));
 +            }
 +        }
 +
 +        if (trxout)
 +        {
 +            oindex[0] = bMin ? min1 : max1;
 +            oindex[1] = bMin ? min2 : max2;
 +            write_trx(trxout, 2, oindex, atoms, i, t, box, x0, NULL, NULL);
 +        }
 +        bFirst = FALSE;
 +        /*dmin should be minimum distance for residue and group*/
 +        if (bEachResEachTime)
 +        {
 +            fprintf(respertime, "%12e", t);
 +            for (i = 1; i < ng; i++)
 +            {
 +                for (j = 0; j < nres; j++)
 +                {
 +                    fprintf(respertime, " %7g", bMin ? mindres[i-1][j] : maxdres[i-1][j]);
 +                    /*reset distances for next time point*/
 +                    mindres[i-1][j] = 1e6;
 +                    maxdres[i-1][j] = 0;
 +                }
 +            }
 +            fprintf(respertime, "\n");
 +        }
 +    }
 +    while (read_next_x(oenv, status, &t, x0, box));
 +
 +    close_trj(status);
 +    gmx_ffclose(dist);
 +    if (num)
 +    {
 +        gmx_ffclose(num);
 +    }
 +    if (atm)
 +    {
 +        gmx_ffclose(atm);
 +    }
 +    if (trxout)
 +    {
 +        close_trx(trxout);
 +    }
 +
 +    if (nres && !bEachResEachTime)
 +    {
 +        FILE *res;
 +
 +        sprintf(buf, "%simum Distance", bMin ? "Min" : "Max");
 +        res = xvgropen(rfile, buf, "Residue (#)", "Distance (nm)", oenv);
 +        xvgr_legend(res, ng-1, (const char**)leg, oenv);
 +        for (j = 0; j < nres; j++)
 +        {
 +            fprintf(res, "%4d", j+1);
 +            for (i = 1; i < ng; i++)
 +            {
 +                fprintf(res, " %7g", bMin ? mindres[i-1][j] : maxdres[i-1][j]);
 +            }
 +            fprintf(res, "\n");
 +        }
 +    }
 +
 +    sfree(x0);
 +}
 +
 +int find_residues(t_atoms *atoms, int n, atom_id index[], atom_id **resindex)
 +{
 +    int  i;
 +    int  nres = 0, resnr, presnr;
 +    int *residx;
 +
 +    /* build index of first atom numbers for each residue */
 +    presnr = NOTSET;
 +    snew(residx, atoms->nres+1);
 +    for (i = 0; i < n; i++)
 +    {
 +        resnr = atoms->atom[index[i]].resind;
 +        if (resnr != presnr)
 +        {
 +            residx[nres] = i;
 +            nres++;
 +            presnr = resnr;
 +        }
 +    }
 +    if (debug)
 +    {
 +        printf("Found %d residues out of %d (%d/%d atoms)\n",
 +               nres, atoms->nres, atoms->nr, n);
 +    }
 +    srenew(residx, nres+1);
 +    /* mark end of last residue */
 +    residx[nres] = n;
 +    *resindex    = residx;
 +    return nres;
 +}
 +
 +void dump_res(FILE *out, int nres, atom_id *resindex, atom_id index[])
 +{
 +    int i, j;
 +
 +    for (i = 0; i < nres-1; i++)
 +    {
 +        fprintf(out, "Res %d (%d):", i, resindex[i+1]-resindex[i]);
 +        for (j = resindex[i]; j < resindex[i+1]; j++)
 +        {
 +            fprintf(out, " %d(%d)", j, index[j]);
 +        }
 +        fprintf(out, "\n");
 +    }
 +}
 +
 +int gmx_mindist(int argc, char *argv[])
 +{
 +    const char     *desc[] = {
 +        "[THISMODULE] computes the distance between one group and a number of",
 +        "other groups. Both the minimum distance",
 +        "(between any pair of atoms from the respective groups)",
 +        "and the number of contacts within a given",
 +        "distance are written to two separate output files.",
 +        "With the [TT]-group[tt] option a contact of an atom in another group",
 +        "with multiple atoms in the first group is counted as one contact",
 +        "instead of as multiple contacts.",
 +        "With [TT]-or[tt], minimum distances to each residue in the first",
 +        "group are determined and plotted as a function of residue number.[PAR]",
 +        "With option [TT]-pi[tt] the minimum distance of a group to its",
 +        "periodic image is plotted. This is useful for checking if a protein",
 +        "has seen its periodic image during a simulation. Only one shift in",
 +        "each direction is considered, giving a total of 26 shifts.",
 +        "It also plots the maximum distance within the group and the lengths",
 +        "of the three box vectors.[PAR]",
 +        "Also [gmx-distance] calculates distances."
 +    };
 +    const char     *bugs[] = {
 +        "The [TT]-pi[tt] option is very slow."
 +    };
 +
 +    static gmx_bool bMat             = FALSE, bPI = FALSE, bSplit = FALSE, bMax = FALSE, bPBC = TRUE;
 +    static gmx_bool bGroup           = FALSE;
 +    static real     rcutoff          = 0.6;
 +    static int      ng               = 1;
 +    static gmx_bool bEachResEachTime = FALSE, bPrintResName = FALSE;
 +    t_pargs         pa[]             = {
 +        { "-matrix", FALSE, etBOOL, {&bMat},
 +          "Calculate half a matrix of group-group distances" },
 +        { "-max",    FALSE, etBOOL, {&bMax},
 +          "Calculate *maximum* distance instead of minimum" },
 +        { "-d",      FALSE, etREAL, {&rcutoff},
 +          "Distance for contacts" },
 +        { "-group",      FALSE, etBOOL, {&bGroup},
 +          "Count contacts with multiple atoms in the first group as one" },
 +        { "-pi",     FALSE, etBOOL, {&bPI},
 +          "Calculate minimum distance with periodic images" },
 +        { "-split",  FALSE, etBOOL, {&bSplit},
 +          "Split graph where time is zero" },
 +        { "-ng",       FALSE, etINT, {&ng},
 +          "Number of secondary groups to compute distance to a central group" },
 +        { "-pbc",    FALSE, etBOOL, {&bPBC},
 +          "Take periodic boundary conditions into account" },
 +        { "-respertime",  FALSE, etBOOL, {&bEachResEachTime},
 +          "When writing per-residue distances, write distance for each time point" },
 +        { "-printresname",  FALSE, etBOOL, {&bPrintResName},
 +          "Write residue names" }
 +    };
 +    output_env_t    oenv;
 +    t_topology     *top  = NULL;
 +    int             ePBC = -1;
 +    char            title[256];
 +    real            t;
 +    rvec           *x;
 +    matrix          box;
 +    gmx_bool        bTop = FALSE;
 +
 +    FILE           *atm;
 +    int             i, j, nres = 0;
 +    const char     *trxfnm, *tpsfnm, *ndxfnm, *distfnm, *numfnm, *atmfnm, *oxfnm, *resfnm;
 +    char          **grpname;
 +    int            *gnx;
 +    atom_id       **index, *residues = NULL;
 +    t_filenm        fnm[] = {
 +        { efTRX, "-f",  NULL,      ffREAD },
 +        { efTPS,  NULL, NULL,      ffOPTRD },
 +        { efNDX,  NULL, NULL,      ffOPTRD },
 +        { efXVG, "-od", "mindist",  ffWRITE },
 +        { efXVG, "-on", "numcont",  ffOPTWR },
 +        { efOUT, "-o", "atm-pair", ffOPTWR },
 +        { efTRO, "-ox", "mindist",  ffOPTWR },
 +        { efXVG, "-or", "mindistres", ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +
 +    if (!parse_common_args(&argc, argv,
 +                           PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
 +                           NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    trxfnm  = ftp2fn(efTRX, NFILE, fnm);
 +    ndxfnm  = ftp2fn_null(efNDX, NFILE, fnm);
 +    distfnm = opt2fn("-od", NFILE, fnm);
 +    numfnm  = opt2fn_null("-on", NFILE, fnm);
 +    atmfnm  = ftp2fn_null(efOUT, NFILE, fnm);
 +    oxfnm   = opt2fn_null("-ox", NFILE, fnm);
 +    resfnm  = opt2fn_null("-or", NFILE, fnm);
 +    if (bPI || resfnm != NULL)
 +    {
 +        /* We need a tps file */
 +        tpsfnm = ftp2fn(efTPS, NFILE, fnm);
 +    }
 +    else
 +    {
 +        tpsfnm = ftp2fn_null(efTPS, NFILE, fnm);
 +    }
 +
 +    if (!tpsfnm && !ndxfnm)
 +    {
 +        gmx_fatal(FARGS, "You have to specify either the index file or a tpr file");
 +    }
 +
 +    if (bPI)
 +    {
 +        ng = 1;
 +        fprintf(stderr, "Choose a group for distance calculation\n");
 +    }
 +    else if (!bMat)
 +    {
 +        ng++;
 +    }
 +
 +    snew(gnx, ng);
 +    snew(index, ng);
 +    snew(grpname, ng);
 +
 +    if (tpsfnm || resfnm || !ndxfnm)
 +    {
 +        snew(top, 1);
 +        bTop = read_tps_conf(tpsfnm, title, top, &ePBC, &x, NULL, box, FALSE);
 +        if (bPI && !bTop)
 +        {
 +            printf("\nWARNING: Without a run input file a trajectory with broken molecules will not give the correct periodic image distance\n\n");
 +        }
 +    }
 +    get_index(top ? &(top->atoms) : NULL, ndxfnm, ng, gnx, index, grpname);
 +
 +    if (bMat && (ng == 1))
 +    {
 +        ng = gnx[0];
 +        printf("Special case: making distance matrix between all atoms in group %s\n",
 +               grpname[0]);
 +        srenew(gnx, ng);
 +        srenew(index, ng);
 +        srenew(grpname, ng);
 +        for (i = 1; (i < ng); i++)
 +        {
 +            gnx[i]      = 1;
 +            grpname[i]  = grpname[0];
 +            snew(index[i], 1);
 +            index[i][0] = index[0][i];
 +        }
 +        gnx[0] = 1;
 +    }
 +
 +    if (resfnm)
 +    {
 +        nres = find_residues(top ? &(top->atoms) : NULL,
 +                             gnx[0], index[0], &residues);
 +        if (debug)
 +        {
 +            dump_res(debug, nres, residues, index[0]);
 +        }
 +    }
 +
 +    if (bPI)
 +    {
 +        periodic_mindist_plot(trxfnm, distfnm, top, ePBC, gnx[0], index[0], bSplit, oenv);
 +    }
 +    else
 +    {
 +        dist_plot(trxfnm, atmfnm, distfnm, numfnm, resfnm, oxfnm,
 +                  rcutoff, bMat, top ? &(top->atoms) : NULL,
 +                  ng, index, gnx, grpname, bSplit, !bMax, nres, residues, bPBC, ePBC,
 +                  bGroup, bEachResEachTime, bPrintResName, oenv);
 +    }
 +
 +    do_view(oenv, distfnm, "-nxy");
 +    if (!bPI)
 +    {
 +        do_view(oenv, numfnm, "-nxy");
 +    }
 +
 +    return 0;
 +}
index 41d9eaf07761fa8c5b28fd20737bfab499b95bfb,0000000000000000000000000000000000000000..ff0bfb0b32a672ef441de04647f8d6667f0c5612
mode 100644,000000..100644
--- /dev/null
@@@ -1,531 -1,0 +1,534 @@@
-         fprintf(outi, "@    xaxes scale Logarithmic\n");
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +#include <math.h>
 +#include <string.h>
 +
 +#include "sysstuff.h"
 +#include "physics.h"
 +#include "typedefs.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "vec.h"
 +#include "index.h"
 +#include "macros.h"
 +#include "xvgr.h"
 +#include "rmpbc.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "gmx_ana.h"
 +
 +#include "gromacs/linearalgebra/nrjac.h"
 +
 +static void gyro_eigen(double **gyr, double *eig, double **eigv, int *ord)
 +{
 +    int nrot, d;
 +
 +    jacobi(gyr, DIM, eig, eigv, &nrot);
 +    /* Order the eigenvalues */
 +    ord[0] = 0;
 +    ord[2] = 2;
 +    for (d = 0; d < DIM; d++)
 +    {
 +        if (eig[d] > eig[ord[0]])
 +        {
 +            ord[0] = d;
 +        }
 +        if (eig[d] < eig[ord[2]])
 +        {
 +            ord[2] = d;
 +        }
 +    }
 +    for (d = 0; d < DIM; d++)
 +    {
 +        if (ord[0] != d && ord[2] != d)
 +        {
 +            ord[1] = d;
 +        }
 +    }
 +}
 +
 +/* Calculate mean square internal distances (Auhl et al., JCP 119, 12718) */
 +static void calc_int_dist(double *intd, rvec *x, int i0, int i1)
 +{
 +    int       ii, jj;
 +    const int ml = i1 - i0 + 1; /* Number of beads in molecule. */
 +    int       bd;               /* Distance between beads */
 +    double    d;
 +
 +    for (bd = 1; bd < ml; bd++)
 +    {
 +        d = 0.;
 +        for (ii = i0; ii <= i1 - bd; ii++)
 +        {
 +            d += distance2(x[ii], x[ii+bd]);
 +        }
 +        d            /= ml - bd;
 +        intd[bd - 1] += d;
 +    }
 +}
 +
 +int gmx_polystat(int argc, char *argv[])
 +{
 +    const char     *desc[] = {
 +        "[THISMODULE] plots static properties of polymers as a function of time",
 +        "and prints the average.[PAR]",
 +        "By default it determines the average end-to-end distance and radii",
 +        "of gyration of polymers. It asks for an index group and split this",
 +        "into molecules. The end-to-end distance is then determined using",
 +        "the first and the last atom in the index group for each molecules.",
 +        "For the radius of gyration the total and the three principal components",
 +        "for the average gyration tensor are written.",
 +        "With option [TT]-v[tt] the eigenvectors are written.",
 +        "With option [TT]-pc[tt] also the average eigenvalues of the individual",
 +        "gyration tensors are written.",
 +        "With option [TT]-i[tt] the mean square internal distances are",
 +        "written.[PAR]",
 +        "With option [TT]-p[tt] the persistence length is determined.",
 +        "The chosen index group should consist of atoms that are",
 +        "consecutively bonded in the polymer mainchains.",
 +        "The persistence length is then determined from the cosine of",
 +        "the angles between bonds with an index difference that is even,",
 +        "the odd pairs are not used, because straight polymer backbones",
 +        "are usually all trans and therefore only every second bond aligns.",
 +        "The persistence length is defined as number of bonds where",
 +        "the average cos reaches a value of 1/e. This point is determined",
 +        "by a linear interpolation of log(<cos>)."
 +    };
 +    static gmx_bool bMW  = TRUE, bPC = FALSE;
 +    t_pargs         pa[] = {
 +        { "-mw", FALSE, etBOOL, {&bMW},
 +          "Use the mass weighting for radii of gyration" },
 +        { "-pc", FALSE, etBOOL, {&bPC},
 +          "Plot average eigenvalues" }
 +    };
 +
 +    t_filenm        fnm[] = {
 +        { efTPX, NULL, NULL,  ffREAD  },
 +        { efTRX, "-f", NULL,  ffREAD  },
 +        { efNDX, NULL, NULL,  ffOPTRD },
 +        { efXVG, "-o", "polystat",  ffWRITE },
 +        { efXVG, "-v", "polyvec", ffOPTWR },
 +        { efXVG, "-p", "persist",  ffOPTWR },
 +        { efXVG, "-i", "intdist", ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +
 +    t_topology  *top;
 +    output_env_t oenv;
 +    int          ePBC;
 +    int          isize, *index, nmol, *molind, mol, nat_min = 0, nat_max = 0;
 +    char        *grpname;
 +    t_trxstatus *status;
 +    real         t;
 +    rvec        *x, *bond = NULL;
 +    matrix       box;
 +    int          natoms, i, j, frame, ind0, ind1, a, d, d2, ord[DIM] = {0};
 +    dvec         cm, sum_eig = {0, 0, 0};
 +    double     **gyr, **gyr_all, eig[DIM], **eigv;
 +    double       sum_eed2, sum_eed2_tot, sum_gyro, sum_gyro_tot, sum_pers_tot;
 +    int         *ninp    = NULL;
 +    double      *sum_inp = NULL, pers;
 +    double      *intd, ymax, ymin;
 +    double       mmol, m;
 +    char         title[STRLEN];
 +    FILE        *out, *outv, *outp, *outi;
 +    const char  *leg[8] = {
 +        "end to end", "<R\\sg\\N>",
 +        "<R\\sg\\N> eig1", "<R\\sg\\N> eig2", "<R\\sg\\N> eig3",
 +        "<R\\sg\\N eig1>", "<R\\sg\\N eig2>", "<R\\sg\\N eig3>"
 +    };
 +    char       **legp, buf[STRLEN];
 +    gmx_rmpbc_t  gpbc = NULL;
 +
 +    if (!parse_common_args(&argc, argv,
 +                           PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
 +                           NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    snew(top, 1);
 +    ePBC = read_tpx_top(ftp2fn(efTPX, NFILE, fnm),
 +                        NULL, box, &natoms, NULL, NULL, NULL, top);
 +
 +    fprintf(stderr, "Select a group of polymer mainchain atoms:\n");
 +    get_index(&top->atoms, ftp2fn_null(efNDX, NFILE, fnm),
 +              1, &isize, &index, &grpname);
 +
 +    snew(molind, top->mols.nr+1);
 +    nmol = 0;
 +    mol  = -1;
 +    for (i = 0; i < isize; i++)
 +    {
 +        if (i == 0 || index[i] >= top->mols.index[mol+1])
 +        {
 +            molind[nmol++] = i;
 +            do
 +            {
 +                mol++;
 +            }
 +            while (index[i] >= top->mols.index[mol+1]);
 +        }
 +    }
 +    molind[nmol] = i;
 +    nat_min      = top->atoms.nr;
 +    nat_max      = 0;
 +    for (mol = 0; mol < nmol; mol++)
 +    {
 +        nat_min = min(nat_min, molind[mol+1]-molind[mol]);
 +        nat_max = max(nat_max, molind[mol+1]-molind[mol]);
 +    }
 +    fprintf(stderr, "Group %s consists of %d molecules\n", grpname, nmol);
 +    fprintf(stderr, "Group size per molecule, min: %d atoms, max %d atoms\n",
 +            nat_min, nat_max);
 +
 +    sprintf(title, "Size of %d polymers", nmol);
 +    out = xvgropen(opt2fn("-o", NFILE, fnm), title, output_env_get_xvgr_tlabel(oenv), "(nm)",
 +                   oenv);
 +    xvgr_legend(out, bPC ? 8 : 5, leg, oenv);
 +
 +    if (opt2bSet("-v", NFILE, fnm))
 +    {
 +        outv = xvgropen(opt2fn("-v", NFILE, fnm), "Principal components",
 +                        output_env_get_xvgr_tlabel(oenv), "(nm)", oenv);
 +        snew(legp, DIM*DIM);
 +        for (d = 0; d < DIM; d++)
 +        {
 +            for (d2 = 0; d2 < DIM; d2++)
 +            {
 +                sprintf(buf, "eig%d %c", d+1, 'x'+d2);
 +                legp[d*DIM+d2] = strdup(buf);
 +            }
 +        }
 +        xvgr_legend(outv, DIM*DIM, (const char**)legp, oenv);
 +    }
 +    else
 +    {
 +        outv = NULL;
 +    }
 +
 +    if (opt2bSet("-p", NFILE, fnm))
 +    {
 +        outp = xvgropen(opt2fn("-p", NFILE, fnm), "Persistence length",
 +                        output_env_get_xvgr_tlabel(oenv), "bonds", oenv);
 +        snew(bond, nat_max-1);
 +        snew(sum_inp, nat_min/2);
 +        snew(ninp, nat_min/2);
 +    }
 +    else
 +    {
 +        outp = NULL;
 +    }
 +
 +    if (opt2bSet("-i", NFILE, fnm))
 +    {
 +        outi = xvgropen(opt2fn("-i", NFILE, fnm), "Internal distances",
 +                        "n", "<R\\S2\\N(n)>/n (nm\\S2\\N)", oenv);
 +        i = index[molind[1]-1] - index[molind[0]]; /* Length of polymer -1 */
 +        snew(intd, i);
 +    }
 +    else
 +    {
 +        intd = NULL;
 +        outi = NULL;
 +    }
 +
 +    natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
 +
 +    snew(gyr, DIM);
 +    snew(gyr_all, DIM);
 +    snew(eigv, DIM);
 +    for (d = 0; d < DIM; d++)
 +    {
 +        snew(gyr[d], DIM);
 +        snew(gyr_all[d], DIM);
 +        snew(eigv[d], DIM);
 +    }
 +
 +    frame        = 0;
 +    sum_eed2_tot = 0;
 +    sum_gyro_tot = 0;
 +    sum_pers_tot = 0;
 +
 +    gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
 +
 +    do
 +    {
 +        gmx_rmpbc(gpbc, natoms, box, x);
 +
 +        sum_eed2 = 0;
 +        for (d = 0; d < DIM; d++)
 +        {
 +            clear_dvec(gyr_all[d]);
 +        }
 +
 +        if (bPC)
 +        {
 +            clear_dvec(sum_eig);
 +        }
 +
 +        if (outp)
 +        {
 +            for (i = 0; i < nat_min/2; i++)
 +            {
 +                sum_inp[i] = 0;
 +                ninp[i]    = 0;
 +            }
 +        }
 +
 +        for (mol = 0; mol < nmol; mol++)
 +        {
 +            ind0 = molind[mol];
 +            ind1 = molind[mol+1];
 +
 +            /* Determine end to end distance */
 +            sum_eed2 += distance2(x[index[ind0]], x[index[ind1-1]]);
 +
 +            /* Determine internal distances */
 +            if (outi)
 +            {
 +                calc_int_dist(intd, x, index[ind0], index[ind1-1]);
 +            }
 +
 +            /* Determine the radius of gyration */
 +            clear_dvec(cm);
 +            for (d = 0; d < DIM; d++)
 +            {
 +                clear_dvec(gyr[d]);
 +            }
 +            mmol = 0;
 +
 +            for (i = ind0; i < ind1; i++)
 +            {
 +                a = index[i];
 +                if (bMW)
 +                {
 +                    m = top->atoms.atom[a].m;
 +                }
 +                else
 +                {
 +                    m = 1;
 +                }
 +                mmol += m;
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    cm[d] += m*x[a][d];
 +                    for (d2 = 0; d2 < DIM; d2++)
 +                    {
 +                        gyr[d][d2] += m*x[a][d]*x[a][d2];
 +                    }
 +                }
 +            }
 +            dsvmul(1/mmol, cm, cm);
 +            for (d = 0; d < DIM; d++)
 +            {
 +                for (d2 = 0; d2 < DIM; d2++)
 +                {
 +                    gyr[d][d2]      = gyr[d][d2]/mmol - cm[d]*cm[d2];
 +                    gyr_all[d][d2] += gyr[d][d2];
 +                }
 +            }
 +            if (bPC)
 +            {
 +                gyro_eigen(gyr, eig, eigv, ord);
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    sum_eig[d] += eig[ord[d]];
 +                }
 +            }
 +            if (outp)
 +            {
 +                for (i = ind0; i < ind1-1; i++)
 +                {
 +                    rvec_sub(x[index[i+1]], x[index[i]], bond[i-ind0]);
 +                    unitv(bond[i-ind0], bond[i-ind0]);
 +                }
 +                for (i = ind0; i < ind1-1; i++)
 +                {
 +                    for (j = 0; (i+j < ind1-1 && j < nat_min/2); j += 2)
 +                    {
 +                        sum_inp[j] += iprod(bond[i-ind0], bond[i-ind0+j]);
 +                        ninp[j]++;
 +                    }
 +                }
 +            }
 +        }
 +        sum_eed2 /= nmol;
 +
 +        sum_gyro = 0;
 +        for (d = 0; d < DIM; d++)
 +        {
 +            for (d2 = 0; d2 < DIM; d2++)
 +            {
 +                gyr_all[d][d2] /= nmol;
 +            }
 +            sum_gyro += gyr_all[d][d];
 +        }
 +
 +        gyro_eigen(gyr_all, eig, eigv, ord);
 +
 +        fprintf(out, "%10.3f %8.4f %8.4f %8.4f %8.4f %8.4f",
 +                t*output_env_get_time_factor(oenv),
 +                sqrt(sum_eed2), sqrt(sum_gyro),
 +                sqrt(eig[ord[0]]), sqrt(eig[ord[1]]), sqrt(eig[ord[2]]));
 +        if (bPC)
 +        {
 +            for (d = 0; d < DIM; d++)
 +            {
 +                fprintf(out, " %8.4f", sqrt(sum_eig[d]/nmol));
 +            }
 +        }
 +        fprintf(out, "\n");
 +
 +        if (outv)
 +        {
 +            fprintf(outv, "%10.3f", t*output_env_get_time_factor(oenv));
 +            for (d = 0; d < DIM; d++)
 +            {
 +                for (d2 = 0; d2 < DIM; d2++)
 +                {
 +                    fprintf(outv, " %6.3f", eigv[ord[d]][d2]);
 +                }
 +            }
 +            fprintf(outv, "\n");
 +        }
 +
 +        sum_eed2_tot += sum_eed2;
 +        sum_gyro_tot += sum_gyro;
 +
 +        if (outp)
 +        {
 +            i = -1;
 +            for (j = 0; j < nat_min/2; j += 2)
 +            {
 +                sum_inp[j] /= ninp[j];
 +                if (i == -1 && sum_inp[j] <= exp(-1.0))
 +                {
 +                    i = j;
 +                }
 +            }
 +            if (i == -1)
 +            {
 +                pers = j;
 +            }
 +            else
 +            {
 +                /* Do linear interpolation on a log scale */
 +                pers = i - 2
 +                    + 2*(log(sum_inp[i-2]) + 1)/(log(sum_inp[i-2]) - log(sum_inp[i]));
 +            }
 +            fprintf(outp, "%10.3f %8.4f\n", t*output_env_get_time_factor(oenv), pers);
 +            sum_pers_tot += pers;
 +        }
 +
 +        frame++;
 +    }
 +    while (read_next_x(oenv, status, &t, x, box));
 +
 +    gmx_rmpbc_done(gpbc);
 +
 +    close_trx(status);
 +
 +    gmx_ffclose(out);
 +    if (outv)
 +    {
 +        gmx_ffclose(outv);
 +    }
 +    if (outp)
 +    {
 +        gmx_ffclose(outp);
 +    }
 +
 +    sum_eed2_tot /= frame;
 +    sum_gyro_tot /= frame;
 +    sum_pers_tot /= frame;
 +    fprintf(stdout, "\nAverage end to end distance: %.3f (nm)\n",
 +            sqrt(sum_eed2_tot));
 +    fprintf(stdout, "\nAverage radius of gyration:  %.3f (nm)\n",
 +            sqrt(sum_gyro_tot));
 +    if (opt2bSet("-p", NFILE, fnm))
 +    {
 +        fprintf(stdout, "\nAverage persistence length:  %.2f bonds\n",
 +                sum_pers_tot);
 +    }
 +
 +    /* Handle printing of internal distances. */
 +    if (outi)
 +    {
++        if (output_env_get_print_xvgr_codes(oenv))
++        {
++            fprintf(outi, "@    xaxes scale Logarithmic\n");
++        }
 +        ymax = -1;
 +        ymin = 1e300;
 +        j    = index[molind[1]-1] - index[molind[0]]; /* Polymer length -1. */
 +        for (i = 0; i < j; i++)
 +        {
 +            intd[i] /= (i + 1) * frame * nmol;
 +            if (intd[i] > ymax)
 +            {
 +                ymax = intd[i];
 +            }
 +            if (intd[i] < ymin)
 +            {
 +                ymin = intd[i];
 +            }
 +        }
 +        xvgr_world(outi, 1, ymin, j, ymax, oenv);
 +        for (i = 0; i < j; i++)
 +        {
 +            fprintf(outi, "%d  %8.4f\n", i+1, intd[i]);
 +        }
 +        gmx_ffclose(outi);
 +    }
 +
 +    do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
 +    if (opt2bSet("-v", NFILE, fnm))
 +    {
 +        do_view(oenv, opt2fn("-v", NFILE, fnm), "-nxy");
 +    }
 +    if (opt2bSet("-p", NFILE, fnm))
 +    {
 +        do_view(oenv, opt2fn("-p", NFILE, fnm), "-nxy");
 +    }
 +
 +    return 0;
 +}
index b544a6a897dbb0878ddcbc914edab0a3f583fabd,0000000000000000000000000000000000000000..b9d708b837697050879725881ff0365104b4adf2
mode 100644,000000..100644
--- /dev/null
@@@ -1,122 -1,0 +1,124 @@@
-     fprintf(out, "@    xaxis  tick on\n@    xaxis  tick major 60\n@    xaxis  tick minor 30\n");
-     fprintf(out, "@    yaxis  tick on\n@    yaxis  tick major 60\n@    yaxis  tick minor 30\n");
-     fprintf(out, "@ s0 symbol 2\n@ s0 symbol size 0.4\n@ s0 symbol fill 1\n");
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include "sysstuff.h"
 +#include <string.h>
 +#include "typedefs.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "vec.h"
 +#include "xvgr.h"
 +#include "physics.h"
 +#include "pbc.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "index.h"
 +#include "nrama.h"
 +#include "gmx_ana.h"
 +
 +
 +static void plot_rama(FILE *out, t_xrama *xr)
 +{
 +    int  i;
 +    real phi, psi;
 +
 +    for (i = 0; (i < xr->npp); i++)
 +    {
 +        phi = xr->dih[xr->pp[i].iphi].ang*RAD2DEG;
 +        psi = xr->dih[xr->pp[i].ipsi].ang*RAD2DEG;
 +        fprintf(out, "%g  %g  %s\n", phi, psi, xr->pp[i].label);
 +    }
 +}
 +
 +int gmx_rama(int argc, char *argv[])
 +{
 +    const char  *desc[] = {
 +        "[THISMODULE] selects the [GRK]phi[grk]/[GRK]psi[grk] dihedral combinations from your topology file",
 +        "and computes these as a function of time.",
 +        "Using simple Unix tools such as [IT]grep[it] you can select out",
 +        "specific residues."
 +    };
 +
 +    FILE        *out;
 +    t_xrama     *xr;
 +    int          j;
 +    output_env_t oenv;
 +    t_filenm     fnm[] = {
 +        { efTRX, "-f", NULL,  ffREAD },
 +        { efTPX, NULL, NULL,  ffREAD },
 +        { efXVG, NULL, "rama", ffWRITE }
 +    };
 +#define NFILE asize(fnm)
 +
 +    if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
 +                           NFILE, fnm, 0, NULL, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +
 +    snew(xr, 1);
 +    init_rama(oenv, ftp2fn(efTRX, NFILE, fnm), ftp2fn(efTPX, NFILE, fnm), xr, 3);
 +
 +    out = xvgropen(ftp2fn(efXVG, NFILE, fnm), "Ramachandran Plot", "Phi", "Psi", oenv);
 +    xvgr_line_props(out, 0, elNone, ecFrank, oenv);
 +    xvgr_view(out, 0.2, 0.2, 0.8, 0.8, oenv);
 +    xvgr_world(out, -180, -180, 180, 180, oenv);
++    if (output_env_get_print_xvgr_codes(oenv))
++    {
++        fprintf(out, "@    xaxis  tick on\n@    xaxis  tick major 60\n@    xaxis  tick minor 30\n");
++        fprintf(out, "@    yaxis  tick on\n@    yaxis  tick major 60\n@    yaxis  tick minor 30\n");
++        fprintf(out, "@ s0 symbol 2\n@ s0 symbol size 0.4\n@ s0 symbol fill 1\n");
++    }
 +    j = 0;
 +    do
 +    {
 +        plot_rama(out, xr);
 +        j++;
 +    }
 +    while (new_data(xr));
 +    fprintf(stderr, "\n");
 +    gmx_ffclose(out);
 +
 +    do_view(oenv, ftp2fn(efXVG, NFILE, fnm), NULL);
 +
 +    return 0;
 +}
index 3962646be66226b11eadeef7d569ae65025032c9,0000000000000000000000000000000000000000..90605021a4c891c0b2e9a1b7f4019d98edaccd80
mode 100644,000000..100644
--- /dev/null
@@@ -1,1223 -1,0 +1,1225 @@@
-             fprintf(fp, "&\n");
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include "gromacs/utility/smalloc.h"
 +#include <math.h>
 +#include "macros.h"
 +#include "typedefs.h"
 +#include "xvgr.h"
 +#include "copyrite.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "vec.h"
 +#include "index.h"
 +#include "gmx_fatal.h"
 +#include "gromacs/fileio/futil.h"
 +#include "princ.h"
 +#include "rmpbc.h"
 +#include "gromacs/fileio/matio.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "cmat.h"
 +#include "viewit.h"
 +#include "gmx_ana.h"
 +
 +#include "gromacs/math/do_fit.h"
 +
 +static void norm_princ(t_atoms *atoms, int isize, atom_id *index, int natoms,
 +                       rvec *x)
 +{
 +    int  i, m;
 +    rvec princ, vec;
 +
 +    /* equalize principal components: */
 +    /* orient principal axes, get principal components */
 +    orient_princ(atoms, isize, index, natoms, x, NULL, princ);
 +
 +    /* calc our own principal components */
 +    clear_rvec(vec);
 +    for (m = 0; m < DIM; m++)
 +    {
 +        for (i = 0; i < isize; i++)
 +        {
 +            vec[m] += sqr(x[index[i]][m]);
 +        }
 +        vec[m] = sqrt(vec[m] / isize);
 +        /* calculate scaling constants */
 +        vec[m] = 1 / (sqrt(3) * vec[m]);
 +    }
 +
 +    /* scale coordinates */
 +    for (i = 0; i < natoms; i++)
 +    {
 +        for (m = 0; m < DIM; m++)
 +        {
 +            x[i][m] *= vec[m];
 +        }
 +    }
 +}
 +
 +int gmx_rms(int argc, char *argv[])
 +{
 +    const char     *desc[] =
 +    {
 +        "[THISMODULE] compares two structures by computing the root mean square",
 +        "deviation (RMSD), the size-independent [GRK]rho[grk] similarity parameter",
 +        "([TT]rho[tt]) or the scaled [GRK]rho[grk] ([TT]rhosc[tt]), ",
 +        "see Maiorov & Crippen, Proteins [BB]22[bb], 273 (1995).",
 +        "This is selected by [TT]-what[tt].[PAR]"
 +
 +        "Each structure from a trajectory ([TT]-f[tt]) is compared to a",
 +        "reference structure. The reference structure",
 +        "is taken from the structure file ([TT]-s[tt]).[PAR]",
 +
 +        "With option [TT]-mir[tt] also a comparison with the mirror image of",
 +        "the reference structure is calculated.",
 +        "This is useful as a reference for 'significant' values, see",
 +        "Maiorov & Crippen, Proteins [BB]22[bb], 273 (1995).[PAR]",
 +
 +        "Option [TT]-prev[tt] produces the comparison with a previous frame",
 +        "the specified number of frames ago.[PAR]",
 +
 +        "Option [TT]-m[tt] produces a matrix in [TT].xpm[tt] format of",
 +        "comparison values of each structure in the trajectory with respect to",
 +        "each other structure. This file can be visualized with for instance",
 +        "[TT]xv[tt] and can be converted to postscript with [gmx-xpm2ps].[PAR]",
 +
 +        "Option [TT]-fit[tt] controls the least-squares fitting of",
 +        "the structures on top of each other: complete fit (rotation and",
 +        "translation), translation only, or no fitting at all.[PAR]",
 +
 +        "Option [TT]-mw[tt] controls whether mass weighting is done or not.",
 +        "If you select the option (default) and ",
 +        "supply a valid [TT].tpr[tt] file masses will be taken from there, ",
 +        "otherwise the masses will be deduced from the [TT]atommass.dat[tt] file in",
 +        "[TT]GMXLIB[tt]. This is fine for proteins, but not",
 +        "necessarily for other molecules. A default mass of 12.011 amu (carbon)",
 +        "is assigned to unknown atoms. You can check whether this happend by",
 +        "turning on the [TT]-debug[tt] flag and inspecting the log file.[PAR]",
 +
 +        "With [TT]-f2[tt], the 'other structures' are taken from a second",
 +        "trajectory, this generates a comparison matrix of one trajectory",
 +        "versus the other.[PAR]",
 +
 +        "Option [TT]-bin[tt] does a binary dump of the comparison matrix.[PAR]",
 +
 +        "Option [TT]-bm[tt] produces a matrix of average bond angle deviations",
 +        "analogously to the [TT]-m[tt] option. Only bonds between atoms in the",
 +        "comparison group are considered."
 +    };
 +    static gmx_bool bPBC              = TRUE, bFitAll = TRUE, bSplit = FALSE;
 +    static gmx_bool bDeltaLog         = FALSE;
 +    static int      prev              = 0, freq = 1, freq2 = 1, nlevels = 80, avl = 0;
 +    static real     rmsd_user_max     = -1, rmsd_user_min = -1, bond_user_max = -1,
 +                    bond_user_min     = -1, delta_maxy = 0.0;
 +    /* strings and things for selecting difference method */
 +    enum
 +    {
 +        ewSel, ewRMSD, ewRho, ewRhoSc, ewNR
 +    };
 +    int         ewhat;
 +    const char *what[ewNR + 1] =
 +    { NULL, "rmsd", "rho", "rhosc", NULL };
 +    const char *whatname[ewNR] =
 +    { NULL, "RMSD", "Rho", "Rho sc" };
 +    const char *whatlabel[ewNR] =
 +    { NULL, "RMSD (nm)", "Rho", "Rho sc" };
 +    const char *whatxvgname[ewNR] =
 +    { NULL, "RMSD", "\\8r\\4", "\\8r\\4\\ssc\\N" };
 +    const char *whatxvglabel[ewNR] =
 +    { NULL, "RMSD (nm)", "\\8r\\4", "\\8r\\4\\ssc\\N" };
 +    /* strings and things for fitting methods */
 +    enum
 +    {
 +        efSel, efFit, efReset, efNone, efNR
 +    };
 +    int             efit;
 +    const char     *fit[efNR + 1] =
 +    { NULL, "rot+trans", "translation", "none", NULL };
 +    const char     *fitgraphlabel[efNR + 1] =
 +    { NULL, "lsq fit", "translational fit", "no fit" };
 +    static int      nrms          = 1;
 +    static gmx_bool bMassWeighted = TRUE;
 +    t_pargs         pa[]          =
 +    {
 +        { "-what", FALSE, etENUM,
 +          { what }, "Structural difference measure" },
 +        { "-pbc", FALSE, etBOOL,
 +          { &bPBC }, "PBC check" },
 +        { "-fit", FALSE, etENUM,
 +          { fit }, "Fit to reference structure" },
 +        { "-prev", FALSE, etINT,
 +          { &prev }, "Compare with previous frame" },
 +        { "-split", FALSE, etBOOL,
 +          { &bSplit }, "Split graph where time is zero" },
 +        { "-fitall", FALSE, etBOOL,
 +          { &bFitAll }, "HIDDENFit all pairs of structures in matrix" },
 +        { "-skip", FALSE, etINT,
 +          { &freq }, "Only write every nr-th frame to matrix" },
 +        { "-skip2", FALSE, etINT,
 +          { &freq2 }, "Only write every nr-th frame to matrix" },
 +        { "-max", FALSE, etREAL,
 +          { &rmsd_user_max }, "Maximum level in comparison matrix" },
 +        { "-min", FALSE, etREAL,
 +          { &rmsd_user_min }, "Minimum level in comparison matrix" },
 +        { "-bmax", FALSE, etREAL,
 +          { &bond_user_max }, "Maximum level in bond angle matrix" },
 +        { "-bmin", FALSE, etREAL,
 +          { &bond_user_min }, "Minimum level in bond angle matrix" },
 +        { "-mw", FALSE, etBOOL,
 +          { &bMassWeighted }, "Use mass weighting for superposition" },
 +        { "-nlevels", FALSE, etINT,
 +          { &nlevels }, "Number of levels in the matrices" },
 +        { "-ng", FALSE, etINT,
 +          { &nrms }, "Number of groups to compute RMS between" },
 +        { "-dlog", FALSE, etBOOL,
 +          { &bDeltaLog },
 +          "HIDDENUse a log x-axis in the delta t matrix" },
 +        { "-dmax", FALSE, etREAL,
 +          { &delta_maxy }, "HIDDENMaximum level in delta matrix" },
 +        { "-aver", FALSE, etINT,
 +          { &avl },
 +          "HIDDENAverage over this distance in the RMSD matrix" }
 +    };
 +    int             natoms_trx, natoms_trx2, natoms;
 +    int             i, j, k, m, teller, teller2, tel_mat, tel_mat2;
 +#define NFRAME 5000
 +    int             maxframe = NFRAME, maxframe2 = NFRAME;
 +    real            t, *w_rls, *w_rms, *w_rls_m = NULL, *w_rms_m = NULL;
 +    gmx_bool        bNorm, bAv, bFreq2, bFile2, bMat, bBond, bDelta, bMirror, bMass;
 +    gmx_bool        bFit, bReset;
 +    t_topology      top;
 +    int             ePBC;
 +    t_iatom        *iatom = NULL;
 +
 +    matrix          box;
 +    rvec           *x, *xp, *xm = NULL, **mat_x = NULL, **mat_x2, *mat_x2_j = NULL, vec1,
 +                    vec2;
 +    t_trxstatus    *status;
 +    char            buf[256], buf2[256];
 +    int             ncons = 0;
 +    FILE           *fp;
 +    real            rlstot = 0, **rls, **rlsm = NULL, *time, *time2, *rlsnorm = NULL,
 +    **rmsd_mat             = NULL, **bond_mat = NULL, *axis, *axis2, *del_xaxis,
 +    *del_yaxis, rmsd_max, rmsd_min, rmsd_avg, bond_max, bond_min, ang;
 +    real       **rmsdav_mat = NULL, av_tot, weight, weight_tot;
 +    real       **delta      = NULL, delta_max, delta_scalex = 0, delta_scaley = 0,
 +    *delta_tot;
 +    int          delta_xsize = 0, del_lev = 100, mx, my, abs_my;
 +    gmx_bool     bA1, bA2, bPrev, bTop, *bInMat = NULL;
 +    int          ifit, *irms, ibond = 0, *ind_bond1 = NULL, *ind_bond2 = NULL, n_ind_m =
 +        0;
 +    atom_id     *ind_fit, **ind_rms, *ind_m = NULL, *rev_ind_m = NULL, *ind_rms_m =
 +        NULL;
 +    char        *gn_fit, **gn_rms;
 +    t_rgb        rlo, rhi;
 +    output_env_t oenv;
 +    gmx_rmpbc_t  gpbc = NULL;
 +
 +    t_filenm     fnm[] =
 +    {
 +        { efTPS, NULL, NULL, ffREAD },
 +        { efTRX, "-f", NULL, ffREAD },
 +        { efTRX, "-f2", NULL, ffOPTRD },
 +        { efNDX, NULL, NULL, ffOPTRD },
 +        { efXVG, NULL, "rmsd", ffWRITE },
 +        { efXVG, "-mir", "rmsdmir", ffOPTWR },
 +        { efXVG, "-a", "avgrp", ffOPTWR },
 +        { efXVG, "-dist", "rmsd-dist", ffOPTWR },
 +        { efXPM, "-m", "rmsd", ffOPTWR },
 +        { efDAT, "-bin", "rmsd", ffOPTWR },
 +        { efXPM, "-bm", "bond", ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +
 +    if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_TIME_UNIT | PCA_CAN_VIEW
 +                           | PCA_BE_NICE, NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL,
 +                           &oenv))
 +    {
 +        return 0;
 +    }
 +    /* parse enumerated options: */
 +    ewhat = nenum(what);
 +    if (ewhat == ewRho || ewhat == ewRhoSc)
 +    {
 +        please_cite(stdout, "Maiorov95");
 +    }
 +    efit   = nenum(fit);
 +    bFit   = efit == efFit;
 +    bReset = efit == efReset;
 +    if (bFit)
 +    {
 +        bReset = TRUE; /* for fit, reset *must* be set */
 +    }
 +    else
 +    {
 +        bFitAll = FALSE;
 +    }
 +
 +    /* mark active cmdline options */
 +    bMirror = opt2bSet("-mir", NFILE, fnm); /* calc RMSD vs mirror of ref. */
 +    bFile2  = opt2bSet("-f2", NFILE, fnm);
 +    bMat    = opt2bSet("-m", NFILE, fnm);
 +    bBond   = opt2bSet("-bm", NFILE, fnm);
 +    bDelta  = (delta_maxy > 0); /* calculate rmsd vs delta t matrix from *
 +                                 *    your RMSD matrix (hidden option       */
 +    bNorm   = opt2bSet("-a", NFILE, fnm);
 +    bFreq2  = opt2parg_bSet("-skip2", asize(pa), pa);
 +    if (freq <= 0)
 +    {
 +        fprintf(stderr, "The number of frames to skip is <= 0. "
 +                "Writing out all frames.\n\n");
 +        freq = 1;
 +    }
 +    if (!bFreq2)
 +    {
 +        freq2 = freq;
 +    }
 +    else if (bFile2 && freq2 <= 0)
 +    {
 +        fprintf(stderr,
 +                "The number of frames to skip in second trajectory is <= 0.\n"
 +                "  Writing out all frames.\n\n");
 +        freq2 = 1;
 +    }
 +
 +    bPrev = (prev > 0);
 +    if (bPrev)
 +    {
++        fprintf(stderr, "WARNING: using option -prev with large trajectories will\n"
++                "         require a lot of memory and could lead to crashes\n");
 +        prev = abs(prev);
 +        if (freq != 1)
 +        {
 +            fprintf(stderr, "WARNING: option -skip also applies to -prev\n");
 +        }
 +    }
 +
 +    if (bFile2 && !bMat && !bBond)
 +    {
 +        fprintf(
 +                stderr,
 +                "WARNING: second trajectory (-f2) useless when not calculating matrix (-m/-bm),\n"
 +                "         will not read from %s\n", opt2fn("-f2", NFILE,
 +                                                           fnm));
 +        bFile2 = FALSE;
 +    }
 +
 +    if (bDelta)
 +    {
 +        bMat = TRUE;
 +        if (bFile2)
 +        {
 +            fprintf(stderr,
 +                    "WARNING: second trajectory (-f2) useless when making delta matrix,\n"
 +                    "         will not read from %s\n", opt2fn("-f2",
 +                                                               NFILE, fnm));
 +            bFile2 = FALSE;
 +        }
 +    }
 +
 +    bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), buf, &top, &ePBC, &xp,
 +                         NULL, box, TRUE);
 +    snew(w_rls, top.atoms.nr);
 +    snew(w_rms, top.atoms.nr);
 +
 +    if (!bTop && bBond)
 +    {
 +        fprintf(stderr,
 +                "WARNING: Need a run input file for bond angle matrix,\n"
 +                "         will not calculate bond angle matrix.\n");
 +        bBond = FALSE;
 +    }
 +
 +    if (bReset)
 +    {
 +        fprintf(stderr, "Select group for %s fit\n", bFit ? "least squares"
 +                : "translational");
 +        get_index(&(top.atoms), ftp2fn_null(efNDX, NFILE, fnm), 1, &ifit,
 +                  &ind_fit, &gn_fit);
 +    }
 +    else
 +    {
 +        ifit = 0;
 +    }
 +
 +    if (bReset)
 +    {
 +        if (bFit && ifit < 3)
 +        {
 +            gmx_fatal(FARGS, "Need >= 3 points to fit!\n" );
 +        }
 +
 +        bMass = FALSE;
 +        for (i = 0; i < ifit; i++)
 +        {
 +            if (bMassWeighted)
 +            {
 +                w_rls[ind_fit[i]] = top.atoms.atom[ind_fit[i]].m;
 +            }
 +            else
 +            {
 +                w_rls[ind_fit[i]] = 1;
 +            }
 +            bMass = bMass || (top.atoms.atom[ind_fit[i]].m != 0);
 +        }
 +        if (!bMass)
 +        {
 +            fprintf(stderr, "All masses in the fit group are 0, using masses of 1\n");
 +            for (i = 0; i < ifit; i++)
 +            {
 +                w_rls[ind_fit[i]] = 1;
 +            }
 +        }
 +    }
 +
 +    if (bMat || bBond)
 +    {
 +        nrms = 1;
 +    }
 +
 +    snew(gn_rms, nrms);
 +    snew(ind_rms, nrms);
 +    snew(irms, nrms);
 +
 +    fprintf(stderr, "Select group%s for %s calculation\n",
 +            (nrms > 1) ? "s" : "", whatname[ewhat]);
 +    get_index(&(top.atoms), ftp2fn_null(efNDX, NFILE, fnm),
 +              nrms, irms, ind_rms, gn_rms);
 +
 +    if (bNorm)
 +    {
 +        snew(rlsnorm, irms[0]);
 +    }
 +    snew(rls, nrms);
 +    for (j = 0; j < nrms; j++)
 +    {
 +        snew(rls[j], maxframe);
 +    }
 +    if (bMirror)
 +    {
 +        snew(rlsm, nrms);
 +        for (j = 0; j < nrms; j++)
 +        {
 +            snew(rlsm[j], maxframe);
 +        }
 +    }
 +    snew(time, maxframe);
 +    for (j = 0; j < nrms; j++)
 +    {
 +        bMass = FALSE;
 +        for (i = 0; i < irms[j]; i++)
 +        {
 +            if (bMassWeighted)
 +            {
 +                w_rms[ind_rms[j][i]] = top.atoms.atom[ind_rms[j][i]].m;
 +            }
 +            else
 +            {
 +                w_rms[ind_rms[j][i]] = 1.0;
 +            }
 +            bMass = bMass || (top.atoms.atom[ind_rms[j][i]].m != 0);
 +        }
 +        if (!bMass)
 +        {
 +            fprintf(stderr, "All masses in group %d are 0, using masses of 1\n", j);
 +            for (i = 0; i < irms[j]; i++)
 +            {
 +                w_rms[ind_rms[j][i]] = 1;
 +            }
 +        }
 +    }
 +    /* Prepare reference frame */
 +    if (bPBC)
 +    {
 +        gpbc = gmx_rmpbc_init(&top.idef, ePBC, top.atoms.nr);
 +        gmx_rmpbc(gpbc, top.atoms.nr, box, xp);
 +    }
 +    if (bReset)
 +    {
 +        reset_x(ifit, ind_fit, top.atoms.nr, NULL, xp, w_rls);
 +    }
 +    if (bMirror)
 +    {
 +        /* generate reference structure mirror image: */
 +        snew(xm, top.atoms.nr);
 +        for (i = 0; i < top.atoms.nr; i++)
 +        {
 +            copy_rvec(xp[i], xm[i]);
 +            xm[i][XX] = -xm[i][XX];
 +        }
 +    }
 +    if (ewhat == ewRhoSc)
 +    {
 +        norm_princ(&top.atoms, ifit, ind_fit, top.atoms.nr, xp);
 +    }
 +
 +    /* read first frame */
 +    natoms_trx = read_first_x(oenv, &status, opt2fn("-f", NFILE, fnm), &t, &x, box);
 +    if (natoms_trx != top.atoms.nr)
 +    {
 +        fprintf(stderr,
 +                "\nWARNING: topology has %d atoms, whereas trajectory has %d\n",
 +                top.atoms.nr, natoms_trx);
 +    }
 +    natoms = min(top.atoms.nr, natoms_trx);
 +    if (bMat || bBond || bPrev)
 +    {
 +        snew(mat_x, NFRAME);
 +
 +        if (bPrev)
 +        {
 +            /* With -prev we use all atoms for simplicity */
 +            n_ind_m = natoms;
 +        }
 +        else
 +        {
 +            /* Check which atoms we need (fit/rms) */
 +            snew(bInMat, natoms);
 +            for (i = 0; i < ifit; i++)
 +            {
 +                bInMat[ind_fit[i]] = TRUE;
 +            }
 +            n_ind_m = ifit;
 +            for (i = 0; i < irms[0]; i++)
 +            {
 +                if (!bInMat[ind_rms[0][i]])
 +                {
 +                    bInMat[ind_rms[0][i]] = TRUE;
 +                    n_ind_m++;
 +                }
 +            }
 +        }
 +        /* Make an index of needed atoms */
 +        snew(ind_m, n_ind_m);
 +        snew(rev_ind_m, natoms);
 +        j = 0;
 +        for (i = 0; i < natoms; i++)
 +        {
 +            if (bPrev || bInMat[i])
 +            {
 +                ind_m[j]     = i;
 +                rev_ind_m[i] = j;
 +                j++;
 +            }
 +        }
 +        snew(w_rls_m, n_ind_m);
 +        snew(ind_rms_m, irms[0]);
 +        snew(w_rms_m, n_ind_m);
 +        for (i = 0; i < ifit; i++)
 +        {
 +            w_rls_m[rev_ind_m[ind_fit[i]]] = w_rls[ind_fit[i]];
 +        }
 +        for (i = 0; i < irms[0]; i++)
 +        {
 +            ind_rms_m[i]          = rev_ind_m[ind_rms[0][i]];
 +            w_rms_m[ind_rms_m[i]] = w_rms[ind_rms[0][i]];
 +        }
 +        sfree(bInMat);
 +    }
 +
 +    if (bBond)
 +    {
 +        ncons = 0;
 +        for (k = 0; k < F_NRE; k++)
 +        {
 +            if (IS_CHEMBOND(k))
 +            {
 +                iatom  = top.idef.il[k].iatoms;
 +                ncons += top.idef.il[k].nr/3;
 +            }
 +        }
 +        fprintf(stderr, "Found %d bonds in topology\n", ncons);
 +        snew(ind_bond1, ncons);
 +        snew(ind_bond2, ncons);
 +        ibond = 0;
 +        for (k = 0; k < F_NRE; k++)
 +        {
 +            if (IS_CHEMBOND(k))
 +            {
 +                iatom = top.idef.il[k].iatoms;
 +                ncons = top.idef.il[k].nr/3;
 +                for (i = 0; i < ncons; i++)
 +                {
 +                    bA1 = FALSE;
 +                    bA2 = FALSE;
 +                    for (j = 0; j < irms[0]; j++)
 +                    {
 +                        if (iatom[3*i+1] == ind_rms[0][j])
 +                        {
 +                            bA1 = TRUE;
 +                        }
 +                        if (iatom[3*i+2] == ind_rms[0][j])
 +                        {
 +                            bA2 = TRUE;
 +                        }
 +                    }
 +                    if (bA1 && bA2)
 +                    {
 +                        ind_bond1[ibond] = rev_ind_m[iatom[3*i+1]];
 +                        ind_bond2[ibond] = rev_ind_m[iatom[3*i+2]];
 +                        ibond++;
 +                    }
 +                }
 +            }
 +        }
 +        fprintf(stderr, "Using %d bonds for bond angle matrix\n", ibond);
 +        if (ibond == 0)
 +        {
 +            gmx_fatal(FARGS, "0 bonds found");
 +        }
 +    }
 +
 +    /* start looping over frames: */
 +    tel_mat = 0;
 +    teller  = 0;
 +    do
 +    {
 +        if (bPBC)
 +        {
 +            gmx_rmpbc(gpbc, natoms, box, x);
 +        }
 +
 +        if (bReset)
 +        {
 +            reset_x(ifit, ind_fit, natoms, NULL, x, w_rls);
 +        }
 +        if (ewhat == ewRhoSc)
 +        {
 +            norm_princ(&top.atoms, ifit, ind_fit, natoms, x);
 +        }
 +
 +        if (bFit)
 +        {
 +            /*do the least squares fit to original structure*/
 +            do_fit(natoms, w_rls, xp, x);
 +        }
 +
 +        if (teller % freq == 0)
 +        {
 +            /* keep frame for matrix calculation */
 +            if (bMat || bBond || bPrev)
 +            {
 +                if (tel_mat >= NFRAME)
 +                {
 +                    srenew(mat_x, tel_mat+1);
 +                }
 +                snew(mat_x[tel_mat], n_ind_m);
 +                for (i = 0; i < n_ind_m; i++)
 +                {
 +                    copy_rvec(x[ind_m[i]], mat_x[tel_mat][i]);
 +                }
 +            }
 +            tel_mat++;
 +        }
 +
 +        /*calculate energy of root_least_squares*/
 +        if (bPrev)
 +        {
 +            j = tel_mat-prev-1;
 +            if (j < 0)
 +            {
 +                j = 0;
 +            }
 +            for (i = 0; i < n_ind_m; i++)
 +            {
 +                copy_rvec(mat_x[j][i], xp[ind_m[i]]);
 +            }
 +            if (bReset)
 +            {
 +                reset_x(ifit, ind_fit, natoms, NULL, xp, w_rls);
 +            }
 +            if (bFit)
 +            {
 +                do_fit(natoms, w_rls, x, xp);
 +            }
 +        }
 +        for (j = 0; (j < nrms); j++)
 +        {
 +            rls[j][teller] =
 +                calc_similar_ind(ewhat != ewRMSD, irms[j], ind_rms[j], w_rms, x, xp);
 +        }
 +        if (bNorm)
 +        {
 +            for (j = 0; (j < irms[0]); j++)
 +            {
 +                rlsnorm[j] +=
 +                    calc_similar_ind(ewhat != ewRMSD, 1, &(ind_rms[0][j]), w_rms, x, xp);
 +            }
 +        }
 +
 +        if (bMirror)
 +        {
 +            if (bFit)
 +            {
 +                /*do the least squares fit to mirror of original structure*/
 +                do_fit(natoms, w_rls, xm, x);
 +            }
 +
 +            for (j = 0; j < nrms; j++)
 +            {
 +                rlsm[j][teller] =
 +                    calc_similar_ind(ewhat != ewRMSD, irms[j], ind_rms[j], w_rms, x, xm);
 +            }
 +        }
 +        time[teller] = output_env_conv_time(oenv, t);
 +
 +        teller++;
 +        if (teller >= maxframe)
 +        {
 +            maxframe += NFRAME;
 +            srenew(time, maxframe);
 +            for (j = 0; (j < nrms); j++)
 +            {
 +                srenew(rls[j], maxframe);
 +            }
 +            if (bMirror)
 +            {
 +                for (j = 0; (j < nrms); j++)
 +                {
 +                    srenew(rlsm[j], maxframe);
 +                }
 +            }
 +        }
 +    }
 +    while (read_next_x(oenv, status, &t, x, box));
 +    close_trj(status);
 +
 +    if (bFile2)
 +    {
 +        snew(time2, maxframe2);
 +
 +        fprintf(stderr, "\nWill read second trajectory file\n");
 +        snew(mat_x2, NFRAME);
 +        natoms_trx2 =
 +            read_first_x(oenv, &status, opt2fn("-f2", NFILE, fnm), &t, &x, box);
 +        if (natoms_trx2 != natoms_trx)
 +        {
 +            gmx_fatal(FARGS,
 +                      "Second trajectory (%d atoms) does not match the first one"
 +                      " (%d atoms)", natoms_trx2, natoms_trx);
 +        }
 +        tel_mat2 = 0;
 +        teller2  = 0;
 +        do
 +        {
 +            if (bPBC)
 +            {
 +                gmx_rmpbc(gpbc, natoms, box, x);
 +            }
 +
 +            if (bReset)
 +            {
 +                reset_x(ifit, ind_fit, natoms, NULL, x, w_rls);
 +            }
 +            if (ewhat == ewRhoSc)
 +            {
 +                norm_princ(&top.atoms, ifit, ind_fit, natoms, x);
 +            }
 +
 +            if (bFit)
 +            {
 +                /*do the least squares fit to original structure*/
 +                do_fit(natoms, w_rls, xp, x);
 +            }
 +
 +            if (teller2 % freq2 == 0)
 +            {
 +                /* keep frame for matrix calculation */
 +                if (bMat)
 +                {
 +                    if (tel_mat2 >= NFRAME)
 +                    {
 +                        srenew(mat_x2, tel_mat2+1);
 +                    }
 +                    snew(mat_x2[tel_mat2], n_ind_m);
 +                    for (i = 0; i < n_ind_m; i++)
 +                    {
 +                        copy_rvec(x[ind_m[i]], mat_x2[tel_mat2][i]);
 +                    }
 +                }
 +                tel_mat2++;
 +            }
 +
 +            time2[teller2] = output_env_conv_time(oenv, t);
 +
 +            teller2++;
 +            if (teller2 >= maxframe2)
 +            {
 +                maxframe2 += NFRAME;
 +                srenew(time2, maxframe2);
 +            }
 +        }
 +        while (read_next_x(oenv, status, &t, x, box));
 +        close_trj(status);
 +    }
 +    else
 +    {
 +        mat_x2   = mat_x;
 +        time2    = time;
 +        tel_mat2 = tel_mat;
 +        freq2    = freq;
 +    }
 +    gmx_rmpbc_done(gpbc);
 +
 +    if (bMat || bBond)
 +    {
 +        /* calculate RMS matrix */
 +        fprintf(stderr, "\n");
 +        if (bMat)
 +        {
 +            fprintf(stderr, "Building %s matrix, %dx%d elements\n",
 +                    whatname[ewhat], tel_mat, tel_mat2);
 +            snew(rmsd_mat, tel_mat);
 +        }
 +        if (bBond)
 +        {
 +            fprintf(stderr, "Building bond angle matrix, %dx%d elements\n",
 +                    tel_mat, tel_mat2);
 +            snew(bond_mat, tel_mat);
 +        }
 +        snew(axis, tel_mat);
 +        snew(axis2, tel_mat2);
 +        rmsd_max = 0;
 +        if (bFile2)
 +        {
 +            rmsd_min = 1e10;
 +        }
 +        else
 +        {
 +            rmsd_min = 0;
 +        }
 +        rmsd_avg = 0;
 +        bond_max = 0;
 +        bond_min = 1e10;
 +        for (j = 0; j < tel_mat2; j++)
 +        {
 +            axis2[j] = time2[freq2*j];
 +        }
 +        if (bDelta)
 +        {
 +            if (bDeltaLog)
 +            {
 +                delta_scalex = 8.0/log(2.0);
 +                delta_xsize  = (int)(log(tel_mat/2)*delta_scalex+0.5)+1;
 +            }
 +            else
 +            {
 +                delta_xsize = tel_mat/2;
 +            }
 +            delta_scaley = 1.0/delta_maxy;
 +            snew(delta, delta_xsize);
 +            for (j = 0; j < delta_xsize; j++)
 +            {
 +                snew(delta[j], del_lev+1);
 +            }
 +            if (avl > 0)
 +            {
 +                snew(rmsdav_mat, tel_mat);
 +                for (j = 0; j < tel_mat; j++)
 +                {
 +                    snew(rmsdav_mat[j], tel_mat);
 +                }
 +            }
 +        }
 +
 +        if (bFitAll)
 +        {
 +            snew(mat_x2_j, natoms);
 +        }
 +        for (i = 0; i < tel_mat; i++)
 +        {
 +            axis[i] = time[freq*i];
 +            fprintf(stderr, "\r element %5d; time %5.2f  ", i, axis[i]);
 +            if (bMat)
 +            {
 +                snew(rmsd_mat[i], tel_mat2);
 +            }
 +            if (bBond)
 +            {
 +                snew(bond_mat[i], tel_mat2);
 +            }
 +            for (j = 0; j < tel_mat2; j++)
 +            {
 +                if (bFitAll)
 +                {
 +                    for (k = 0; k < n_ind_m; k++)
 +                    {
 +                        copy_rvec(mat_x2[j][k], mat_x2_j[k]);
 +                    }
 +                    do_fit(n_ind_m, w_rls_m, mat_x[i], mat_x2_j);
 +                }
 +                else
 +                {
 +                    mat_x2_j = mat_x2[j];
 +                }
 +                if (bMat)
 +                {
 +                    if (bFile2 || (i < j))
 +                    {
 +                        rmsd_mat[i][j] =
 +                            calc_similar_ind(ewhat != ewRMSD, irms[0], ind_rms_m,
 +                                             w_rms_m, mat_x[i], mat_x2_j);
 +                        if (rmsd_mat[i][j] > rmsd_max)
 +                        {
 +                            rmsd_max = rmsd_mat[i][j];
 +                        }
 +                        if (rmsd_mat[i][j] < rmsd_min)
 +                        {
 +                            rmsd_min = rmsd_mat[i][j];
 +                        }
 +                        rmsd_avg += rmsd_mat[i][j];
 +                    }
 +                    else
 +                    {
 +                        rmsd_mat[i][j] = rmsd_mat[j][i];
 +                    }
 +                }
 +                if (bBond)
 +                {
 +                    if (bFile2 || (i <= j))
 +                    {
 +                        ang = 0.0;
 +                        for (m = 0; m < ibond; m++)
 +                        {
 +                            rvec_sub(mat_x[i][ind_bond1[m]], mat_x[i][ind_bond2[m]], vec1);
 +                            rvec_sub(mat_x2_j[ind_bond1[m]], mat_x2_j[ind_bond2[m]], vec2);
 +                            ang += acos(cos_angle(vec1, vec2));
 +                        }
 +                        bond_mat[i][j] = ang*180.0/(M_PI*ibond);
 +                        if (bond_mat[i][j] > bond_max)
 +                        {
 +                            bond_max = bond_mat[i][j];
 +                        }
 +                        if (bond_mat[i][j] < bond_min)
 +                        {
 +                            bond_min = bond_mat[i][j];
 +                        }
 +                    }
 +                    else
 +                    {
 +                        bond_mat[i][j] = bond_mat[j][i];
 +                    }
 +                }
 +            }
 +        }
 +        if (bFile2)
 +        {
 +            rmsd_avg /= tel_mat*tel_mat2;
 +        }
 +        else
 +        {
 +            rmsd_avg /= tel_mat*(tel_mat - 1)/2;
 +        }
 +        if (bMat && (avl > 0))
 +        {
 +            rmsd_max = 0.0;
 +            rmsd_min = 0.0;
 +            rmsd_avg = 0.0;
 +            for (j = 0; j < tel_mat-1; j++)
 +            {
 +                for (i = j+1; i < tel_mat; i++)
 +                {
 +                    av_tot     = 0;
 +                    weight_tot = 0;
 +                    for (my = -avl; my <= avl; my++)
 +                    {
 +                        if ((j+my >= 0) && (j+my < tel_mat))
 +                        {
 +                            abs_my = abs(my);
 +                            for (mx = -avl; mx <= avl; mx++)
 +                            {
 +                                if ((i+mx >= 0) && (i+mx < tel_mat))
 +                                {
 +                                    weight      = (real)(avl+1-max(abs(mx), abs_my));
 +                                    av_tot     += weight*rmsd_mat[i+mx][j+my];
 +                                    weight_tot += weight;
 +                                }
 +                            }
 +                        }
 +                    }
 +                    rmsdav_mat[i][j] = av_tot/weight_tot;
 +                    rmsdav_mat[j][i] = rmsdav_mat[i][j];
 +                    if (rmsdav_mat[i][j] > rmsd_max)
 +                    {
 +                        rmsd_max = rmsdav_mat[i][j];
 +                    }
 +                }
 +            }
 +            rmsd_mat = rmsdav_mat;
 +        }
 +
 +        if (bMat)
 +        {
 +            fprintf(stderr, "\n%s: Min %f, Max %f, Avg %f\n",
 +                    whatname[ewhat], rmsd_min, rmsd_max, rmsd_avg);
 +            rlo.r = 1; rlo.g = 1; rlo.b = 1;
 +            rhi.r = 0; rhi.g = 0; rhi.b = 0;
 +            if (rmsd_user_max != -1)
 +            {
 +                rmsd_max = rmsd_user_max;
 +            }
 +            if (rmsd_user_min != -1)
 +            {
 +                rmsd_min = rmsd_user_min;
 +            }
 +            if ((rmsd_user_max !=  -1) || (rmsd_user_min != -1))
 +            {
 +                fprintf(stderr, "Min and Max value set to resp. %f and %f\n",
 +                        rmsd_min, rmsd_max);
 +            }
 +            sprintf(buf, "%s %s matrix", gn_rms[0], whatname[ewhat]);
 +            write_xpm(opt2FILE("-m", NFILE, fnm, "w"), 0, buf, whatlabel[ewhat],
 +                      output_env_get_time_label(oenv), output_env_get_time_label(oenv), tel_mat, tel_mat2,
 +                      axis, axis2, rmsd_mat, rmsd_min, rmsd_max, rlo, rhi, &nlevels);
 +            /* Print the distribution of RMSD values */
 +            if (opt2bSet("-dist", NFILE, fnm))
 +            {
 +                low_rmsd_dist(opt2fn("-dist", NFILE, fnm), rmsd_max, tel_mat, rmsd_mat, oenv);
 +            }
 +
 +            if (bDelta)
 +            {
 +                snew(delta_tot, delta_xsize);
 +                for (j = 0; j < tel_mat-1; j++)
 +                {
 +                    for (i = j+1; i < tel_mat; i++)
 +                    {
 +                        mx = i-j;
 +                        if (mx < tel_mat/2)
 +                        {
 +                            if (bDeltaLog)
 +                            {
 +                                mx = (int)(log(mx)*delta_scalex+0.5);
 +                            }
 +                            my             = (int)(rmsd_mat[i][j]*delta_scaley*del_lev+0.5);
 +                            delta_tot[mx] += 1.0;
 +                            if ((rmsd_mat[i][j] >= 0) && (rmsd_mat[i][j] <= delta_maxy))
 +                            {
 +                                delta[mx][my] += 1.0;
 +                            }
 +                        }
 +                    }
 +                }
 +                delta_max = 0;
 +                for (i = 0; i < delta_xsize; i++)
 +                {
 +                    if (delta_tot[i] > 0.0)
 +                    {
 +                        delta_tot[i] = 1.0/delta_tot[i];
 +                        for (j = 0; j <= del_lev; j++)
 +                        {
 +                            delta[i][j] *= delta_tot[i];
 +                            if (delta[i][j] > delta_max)
 +                            {
 +                                delta_max = delta[i][j];
 +                            }
 +                        }
 +                    }
 +                }
 +                fprintf(stderr, "Maximum in delta matrix: %f\n", delta_max);
 +                snew(del_xaxis, delta_xsize);
 +                snew(del_yaxis, del_lev+1);
 +                for (i = 0; i < delta_xsize; i++)
 +                {
 +                    del_xaxis[i] = axis[i]-axis[0];
 +                }
 +                for (i = 0; i < del_lev+1; i++)
 +                {
 +                    del_yaxis[i] = delta_maxy*i/del_lev;
 +                }
 +                sprintf(buf, "%s %s vs. delta t", gn_rms[0], whatname[ewhat]);
 +                fp = gmx_ffopen("delta.xpm", "w");
 +                write_xpm(fp, 0, buf, "density", output_env_get_time_label(oenv), whatlabel[ewhat],
 +                          delta_xsize, del_lev+1, del_xaxis, del_yaxis,
 +                          delta, 0.0, delta_max, rlo, rhi, &nlevels);
 +                gmx_ffclose(fp);
 +            }
 +            if (opt2bSet("-bin", NFILE, fnm))
 +            {
 +                /* NB: File must be binary if we use fwrite */
 +                fp = ftp2FILE(efDAT, NFILE, fnm, "wb");
 +                for (i = 0; i < tel_mat; i++)
 +                {
 +                    if (fwrite(rmsd_mat[i], sizeof(**rmsd_mat), tel_mat2, fp) != tel_mat2)
 +                    {
 +                        gmx_fatal(FARGS, "Error writing to output file");
 +                    }
 +                }
 +                gmx_ffclose(fp);
 +            }
 +        }
 +        if (bBond)
 +        {
 +            fprintf(stderr, "\nMin. angle: %f, Max. angle: %f\n", bond_min, bond_max);
 +            if (bond_user_max != -1)
 +            {
 +                bond_max = bond_user_max;
 +            }
 +            if (bond_user_min != -1)
 +            {
 +                bond_min = bond_user_min;
 +            }
 +            if ((bond_user_max !=  -1) || (bond_user_min != -1))
 +            {
 +                fprintf(stderr, "Bond angle Min and Max set to:\n"
 +                        "Min. angle: %f, Max. angle: %f\n", bond_min, bond_max);
 +            }
 +            rlo.r = 1; rlo.g = 1; rlo.b = 1;
 +            rhi.r = 0; rhi.g = 0; rhi.b = 0;
 +            sprintf(buf, "%s av. bond angle deviation", gn_rms[0]);
 +            write_xpm(opt2FILE("-bm", NFILE, fnm, "w"), 0, buf, "degrees",
 +                      output_env_get_time_label(oenv), output_env_get_time_label(oenv), tel_mat, tel_mat2,
 +                      axis, axis2, bond_mat, bond_min, bond_max, rlo, rhi, &nlevels);
 +        }
 +    }
 +
 +    bAv = opt2bSet("-a", NFILE, fnm);
 +
 +    /* Write the RMSD's to file */
 +    if (!bPrev)
 +    {
 +        sprintf(buf, "%s", whatxvgname[ewhat]);
 +    }
 +    else
 +    {
 +        sprintf(buf, "%s with frame %g %s ago", whatxvgname[ewhat],
 +                time[prev*freq]-time[0], output_env_get_time_label(oenv));
 +    }
 +    fp = xvgropen(opt2fn("-o", NFILE, fnm), buf, output_env_get_xvgr_tlabel(oenv),
 +                  whatxvglabel[ewhat], oenv);
 +    if (output_env_get_print_xvgr_codes(oenv))
 +    {
 +        fprintf(fp, "@ subtitle \"%s%s after %s%s%s\"\n",
 +                (nrms == 1) ? "" : "of ", gn_rms[0], fitgraphlabel[efit],
 +                bFit     ? " to " : "", bFit ? gn_fit : "");
 +    }
 +    if (nrms != 1)
 +    {
 +        xvgr_legend(fp, nrms, (const char**)gn_rms, oenv);
 +    }
 +    for (i = 0; (i < teller); i++)
 +    {
 +        if (bSplit && i > 0 &&
 +            fabs(time[bPrev ? freq*i : i]/output_env_get_time_factor(oenv)) < 1e-5)
 +        {
-                 fprintf(fp, "&\n");
++            fprintf(fp, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +        }
 +        fprintf(fp, "%12.7f", time[bPrev ? freq*i : i]);
 +        for (j = 0; (j < nrms); j++)
 +        {
 +            fprintf(fp, " %12.7f", rls[j][i]);
 +            if (bAv)
 +            {
 +                rlstot += rls[j][i];
 +            }
 +        }
 +        fprintf(fp, "\n");
 +    }
 +    gmx_ffclose(fp);
 +
 +    if (bMirror)
 +    {
 +        /* Write the mirror RMSD's to file */
 +        sprintf(buf, "%s with Mirror", whatxvgname[ewhat]);
 +        sprintf(buf2, "Mirror %s", whatxvglabel[ewhat]);
 +        fp = xvgropen(opt2fn("-mir", NFILE, fnm), buf, output_env_get_xvgr_tlabel(oenv),
 +                      buf2, oenv);
 +        if (nrms == 1)
 +        {
 +            if (output_env_get_print_xvgr_codes(oenv))
 +            {
 +                fprintf(fp, "@ subtitle \"of %s after lsq fit to mirror of %s\"\n",
 +                        gn_rms[0], gn_fit);
 +            }
 +        }
 +        else
 +        {
 +            if (output_env_get_print_xvgr_codes(oenv))
 +            {
 +                fprintf(fp, "@ subtitle \"after lsq fit to mirror %s\"\n", gn_fit);
 +            }
 +            xvgr_legend(fp, nrms, (const char**)gn_rms, oenv);
 +        }
 +        for (i = 0; (i < teller); i++)
 +        {
 +            if (bSplit && i > 0 && fabs(time[i]) < 1e-5)
 +            {
++                fprintf(fp, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +            }
 +            fprintf(fp, "%12.7f", time[i]);
 +            for (j = 0; (j < nrms); j++)
 +            {
 +                fprintf(fp, " %12.7f", rlsm[j][i]);
 +            }
 +            fprintf(fp, "\n");
 +        }
 +        gmx_ffclose(fp);
 +    }
 +
 +    if (bAv)
 +    {
 +        sprintf(buf, "Average %s", whatxvgname[ewhat]);
 +        sprintf(buf2, "Average %s", whatxvglabel[ewhat]);
 +        fp = xvgropen(opt2fn("-a", NFILE, fnm), buf, "Residue", buf2, oenv);
 +        for (j = 0; (j < nrms); j++)
 +        {
 +            fprintf(fp, "%10d  %10g\n", j, rlstot/teller);
 +        }
 +        gmx_ffclose(fp);
 +    }
 +
 +    if (bNorm)
 +    {
 +        fp = xvgropen("aver.xvg", gn_rms[0], "Residue", whatxvglabel[ewhat], oenv);
 +        for (j = 0; (j < irms[0]); j++)
 +        {
 +            fprintf(fp, "%10d  %10g\n", j, rlsnorm[j]/teller);
 +        }
 +        gmx_ffclose(fp);
 +    }
 +    do_view(oenv, opt2fn_null("-a", NFILE, fnm), "-graphtype bar");
 +    do_view(oenv, opt2fn("-o", NFILE, fnm), NULL);
 +    do_view(oenv, opt2fn_null("-mir", NFILE, fnm), NULL);
 +    do_view(oenv, opt2fn_null("-m", NFILE, fnm), NULL);
 +    do_view(oenv, opt2fn_null("-bm", NFILE, fnm), NULL);
 +    do_view(oenv, opt2fn_null("-dist", NFILE, fnm), NULL);
 +
 +    return 0;
 +}
index 15738d8c4fbce2b48742d28e6df2fcd8844ffa42,0000000000000000000000000000000000000000..f1922f55373cb0249a5f3e95f4d8b485da36195b
mode 100644,000000..100644
--- /dev/null
@@@ -1,496 -1,0 +1,499 @@@
-             fprintf(fp_cub, "&\n");
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +#include <stdio.h>
 +
 +#include <math.h>
 +#include "gromacs/fileio/confio.h"
 +#include "gmx_fatal.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gstat.h"
 +#include "macros.h"
 +#include "gromacs/math/utilities.h"
 +#include "physics.h"
 +#include "index.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "gromacs/commandline/pargs.h"
 +#include <string.h>
 +#include "sysstuff.h"
 +#include "txtdump.h"
 +#include "typedefs.h"
 +#include "vec.h"
 +#include "xvgr.h"
 +#include "pbc.h"
 +#include "gmx_ana.h"
 +#include "gromacs/fileio/trxio.h"
 +
 +
 +#define NK  24
 +#define NPK 4
 +
 +#define NKC  6
 +#define NKC0 4
 +int  kset_c[NKC+1] = { 0, 3, 9, 13, 16, 19, NK };
 +
 +rvec v0[NK] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {1, 1, 0}, {1, -1, 0}, {1, 0, 1}, {1, 0, -1}, {0, 1, 1}, {0, 1, -1}, {1, 1, 1}, {1, 1, -1}, {1, -1, 1}, {-1, 1, 1}, {2, 0, 0}, {0, 2, 0}, {0, 0, 2}, {3, 0, 0}, {0, 3, 0}, {0, 0, 3}, {4, 0, 0}, {0, 4, 0}, {0, 0, 4}};
 +rvec v1[NK] = {{0, 1, 0}, {0, 0, 1}, {1, 0, 0}, {0, 0, 1}, {0, 0, 1}, {0, 1, 0}, {0, 1, 0}, {1, 0, 0}, {1, 0, 0}, {1, -1, 0}, {1, -1, 0}, {1, 0, -1}, { 0, 1, -1}, {0, 1, 0}, {0, 0, 1}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {1, 0, 0}};
 +rvec v2[NK] = {{0, 0, 1}, {1, 0, 0}, {0, 1, 0}, {1, -1, 0}, {1, 1, 0}, {1, 0, -1}, {1, 0, 1}, {0, 1, -1}, {0, 1, 1}, {1, 1, -2}, {1, 1, 2}, {1, 2, 1}, { 2, 1, 1}, {0, 0, 1}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {1, 0, 0}, {0, 1, 0}};
 +
 +static void process_tcaf(int nframes, real dt, int nkc, real **tc, rvec *kfac,
 +                         real rho, real wt, const char *fn_trans,
 +                         const char *fn_tca, const char *fn_tc,
 +                         const char *fn_tcf, const char *fn_cub,
 +                         const char *fn_vk, const output_env_t oenv)
 +{
 +    FILE  *fp, *fp_vk, *fp_cub = NULL;
 +    int    nk, ntc;
 +    real **tcaf, **tcafc = NULL, eta;
 +    int    i, j, k, kc;
 +    int    ncorr;
 +    real   fitparms[3], *sig;
 +
 +    nk  = kset_c[nkc];
 +    ntc = nk*NPK;
 +
 +    if (fn_trans)
 +    {
 +        fp = xvgropen(fn_trans, "Transverse Current", "Time (ps)", "TC (nm/ps)",
 +                      oenv);
 +        for (i = 0; i < nframes; i++)
 +        {
 +            fprintf(fp, "%g", i*dt);
 +            for (j = 0; j < ntc; j++)
 +            {
 +                fprintf(fp, " %g", tc[j][i]);
 +            }
 +            fprintf(fp, "\n");
 +        }
 +        gmx_ffclose(fp);
 +        do_view(oenv, fn_trans, "-nxy");
 +    }
 +
 +    ncorr = (nframes+1)/2;
 +    if (ncorr > (int)(5*wt/dt+0.5))
 +    {
 +        ncorr = (int)(5*wt/dt+0.5)+1;
 +    }
 +    snew(tcaf, nk);
 +    for (k = 0; k < nk; k++)
 +    {
 +        snew(tcaf[k], ncorr);
 +    }
 +    if (fn_cub)
 +    {
 +        snew(tcafc, nkc);
 +        for (k = 0; k < nkc; k++)
 +        {
 +            snew(tcafc[k], ncorr);
 +        }
 +    }
 +    snew(sig, ncorr);
 +    for (i = 0; i < ncorr; i++)
 +    {
 +        sig[i] = exp(0.5*i*dt/wt);
 +    }
 +
 +    low_do_autocorr(fn_tca, oenv, "Transverse Current Autocorrelation Functions",
 +                    nframes, ntc, ncorr, tc, dt, eacNormal,
 +                    1, FALSE, FALSE, FALSE, 0, 0, 0);
 +    do_view(oenv, fn_tca, "-nxy");
 +
 +    fp = xvgropen(fn_tc, "Transverse Current Autocorrelation Functions",
 +                  "Time (ps)", "TCAF", oenv);
 +    for (i = 0; i < ncorr; i++)
 +    {
 +        kc = 0;
 +        fprintf(fp, "%g", i*dt);
 +        for (k = 0; k < nk; k++)
 +        {
 +            for (j = 0; j < NPK; j++)
 +            {
 +                tcaf[k][i] += tc[NPK*k+j][i];
 +            }
 +            if (fn_cub)
 +            {
 +                for (j = 0; j < NPK; j++)
 +                {
 +                    tcafc[kc][i] += tc[NPK*k+j][i];
 +                }
 +            }
 +            if (i == 0)
 +            {
 +                fprintf(fp, " %g", 1.0);
 +            }
 +            else
 +            {
 +                tcaf[k][i] /= tcaf[k][0];
 +                fprintf(fp, " %g", tcaf[k][i]);
 +            }
 +            if (k+1 == kset_c[kc+1])
 +            {
 +                kc++;
 +            }
 +        }
 +        fprintf(fp, "\n");
 +    }
 +    gmx_ffclose(fp);
 +    do_view(oenv, fn_tc, "-nxy");
 +
 +    if (fn_cub)
 +    {
 +        fp_cub = xvgropen(fn_cub, "TCAFs and fits", "Time (ps)", "TCAF", oenv);
 +        for (kc = 0; kc < nkc; kc++)
 +        {
 +            fprintf(fp_cub, "%g %g\n", 0.0, 1.0);
 +            for (i = 1; i < ncorr; i++)
 +            {
 +                tcafc[kc][i] /= tcafc[kc][0];
 +                fprintf(fp_cub, "%g %g\n", i*dt, tcafc[kc][i]);
 +            }
-     fprintf(fp_vk, "@    s0 symbol 2\n");
-     fprintf(fp_vk, "@    s0 symbol color 1\n");
-     fprintf(fp_vk, "@    s0 linestyle 0\n");
-     if (fn_cub)
++            fprintf(fp_cub, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +            tcafc[kc][0] = 1.0;
 +        }
 +    }
 +
 +    fp_vk = xvgropen(fn_vk, "Fits", "k (nm\\S-1\\N)",
 +                     "\\8h\\4 (10\\S-3\\N kg m\\S-1\\N s\\S-1\\N)", oenv);
-         fprintf(fp_vk, "@    s1 symbol 3\n");
-         fprintf(fp_vk, "@    s1 symbol color 2\n");
++    if (output_env_get_print_xvgr_codes(oenv))
 +    {
-         fprintf(fp, "&\n");
++        fprintf(fp_vk, "@    s0 symbol 2\n");
++        fprintf(fp_vk, "@    s0 symbol color 1\n");
++        fprintf(fp_vk, "@    s0 linestyle 0\n");
++        if (fn_cub)
++        {
++            fprintf(fp_vk, "@    s1 symbol 3\n");
++            fprintf(fp_vk, "@    s1 symbol color 2\n");
++        }
 +    }
 +    fp = xvgropen(fn_tcf, "TCAF Fits", "Time (ps)", "", oenv);
 +    for (k = 0; k < nk; k++)
 +    {
 +        tcaf[k][0]   = 1.0;
 +        fitparms[0]  = 1;
 +        fitparms[1]  = 1;
 +        do_lmfit(ncorr, tcaf[k], sig, dt, 0, 0, ncorr*dt,
 +                 oenv, bDebugMode(), effnVAC, fitparms, 0);
 +        eta = 1000*fitparms[1]*rho/
 +            (4*fitparms[0]*PICO*norm2(kfac[k])/(NANO*NANO));
 +        fprintf(stdout, "k %6.3f  tau %6.3f  eta %8.5f 10^-3 kg/(m s)\n",
 +                norm(kfac[k]), fitparms[0], eta);
 +        fprintf(fp_vk, "%6.3f %g\n", norm(kfac[k]), eta);
 +        for (i = 0; i < ncorr; i++)
 +        {
 +            fprintf(fp, "%g %g\n", i*dt, fit_function(effnVAC, fitparms, i*dt));
 +        }
-         fprintf(fp_vk, "&\n");
++        fprintf(fp, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +    }
 +    gmx_ffclose(fp);
 +    do_view(oenv, fn_tcf, "-nxy");
 +
 +    if (fn_cub)
 +    {
 +        fprintf(stdout, "Averaged over k-vectors:\n");
-             fprintf(fp_cub, "&\n");
++        fprintf(fp_vk, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +        for (k = 0; k < nkc; k++)
 +        {
 +            tcafc[k][0]  = 1.0;
 +            fitparms[0]  = 1;
 +            fitparms[1]  = 1;
 +            do_lmfit(ncorr, tcafc[k], sig, dt, 0, 0, ncorr*dt,
 +                     oenv, bDebugMode(), effnVAC, fitparms, 0);
 +            eta = 1000*fitparms[1]*rho/
 +                (4*fitparms[0]*PICO*norm2(kfac[kset_c[k]])/(NANO*NANO));
 +            fprintf(stdout,
 +                    "k %6.3f  tau %6.3f  Omega %6.3f  eta %8.5f 10^-3 kg/(m s)\n",
 +                    norm(kfac[kset_c[k]]), fitparms[0], fitparms[1], eta);
 +            fprintf(fp_vk, "%6.3f %g\n", norm(kfac[kset_c[k]]), eta);
 +            for (i = 0; i < ncorr; i++)
 +            {
 +                fprintf(fp_cub, "%g %g\n", i*dt, fit_function(effnVAC, fitparms, i*dt));
 +            }
-         fprintf(fp_vk, "&\n");
++            fprintf(fp_cub, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +        }
++        fprintf(fp_vk, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
 +        gmx_ffclose(fp_cub);
 +        do_view(oenv, fn_cub, "-nxy");
 +    }
 +    gmx_ffclose(fp_vk);
 +    do_view(oenv, fn_vk, "-nxy");
 +}
 +
 +
 +int gmx_tcaf(int argc, char *argv[])
 +{
 +    const char     *desc[] = {
 +        "[THISMODULE] computes tranverse current autocorrelations.",
 +        "These are used to estimate the shear viscosity, [GRK]eta[grk].",
 +        "For details see: Palmer, Phys. Rev. E 49 (1994) pp 359-366.[PAR]",
 +        "Transverse currents are calculated using the",
 +        "k-vectors (1,0,0) and (2,0,0) each also in the [IT]y[it]- and [IT]z[it]-direction,",
 +        "(1,1,0) and (1,-1,0) each also in the 2 other planes (these vectors",
 +        "are not independent) and (1,1,1) and the 3 other box diagonals (also",
 +        "not independent). For each k-vector the sine and cosine are used, in",
 +        "combination with the velocity in 2 perpendicular directions. This gives",
 +        "a total of 16*2*2=64 transverse currents. One autocorrelation is",
 +        "calculated fitted for each k-vector, which gives 16 TCAFs. Each of",
 +        "these TCAFs is fitted to [MATH]f(t) = [EXP]-v[exp]([COSH]Wv[cosh] + 1/W [SINH]Wv[sinh])[math],",
 +        "[MATH]v = -t/(2 [GRK]tau[grk])[math], [MATH]W = [SQRT]1 - 4 [GRK]tau[grk] [GRK]eta[grk]/[GRK]rho[grk] k^2[sqrt][math], which gives 16 values of [GRK]tau[grk]",
 +        "and [GRK]eta[grk]. The fit weights decay exponentially with time constant [MATH]w[math] (given with [TT]-wt[tt]) as [MATH][EXP]-t/w[exp][math], and the TCAF and",
 +        "fit are calculated up to time [MATH]5*w[math].",
 +        "The [GRK]eta[grk] values should be fitted to [MATH]1 - a [GRK]eta[grk](k) k^2[math], from which",
 +        "one can estimate the shear viscosity at k=0.[PAR]",
 +        "When the box is cubic, one can use the option [TT]-oc[tt], which",
 +        "averages the TCAFs over all k-vectors with the same length.",
 +        "This results in more accurate TCAFs.",
 +        "Both the cubic TCAFs and fits are written to [TT]-oc[tt]",
 +        "The cubic [GRK]eta[grk] estimates are also written to [TT]-ov[tt].[PAR]",
 +        "With option [TT]-mol[tt], the transverse current is determined of",
 +        "molecules instead of atoms. In this case, the index group should",
 +        "consist of molecule numbers instead of atom numbers.[PAR]",
 +        "The k-dependent viscosities in the [TT]-ov[tt] file should be",
 +        "fitted to [MATH][GRK]eta[grk](k) = [GRK]eta[grk][SUB]0[sub] (1 - a k^2)[math] to obtain the viscosity at",
 +        "infinite wavelength.[PAR]",
 +        "[BB]Note:[bb] make sure you write coordinates and velocities often enough.",
 +        "The initial, non-exponential, part of the autocorrelation function",
 +        "is very important for obtaining a good fit."
 +    };
 +
 +    static gmx_bool bMol = FALSE, bK34 = FALSE;
 +    static real     wt   = 5;
 +    t_pargs         pa[] = {
 +        { "-mol", FALSE, etBOOL, {&bMol},
 +          "Calculate TCAF of molecules" },
 +        { "-k34", FALSE, etBOOL, {&bK34},
 +          "Also use k=(3,0,0) and k=(4,0,0)" },
 +        { "-wt", FALSE, etREAL, {&wt},
 +          "Exponential decay time for the TCAF fit weights" }
 +    };
 +
 +    t_topology      top;
 +    int             ePBC;
 +    t_trxframe      fr;
 +    matrix          box;
 +    gmx_bool        bTPS, bTop; /* ,bCubic; */
 +    int             gnx;
 +    atom_id        *index, *atndx = NULL, at;
 +    char           *grpname;
 +    char            title[256];
 +    real            t0, t1, dt, m, mtot, sysmass, rho, sx, cx;
 +    t_trxstatus    *status;
 +    int             nframes, n_alloc, i, j, k, d;
 +    rvec            mv_mol, cm_mol, kfac[NK];
 +    int             nkc, nk, ntc;
 +    real          **c1, **tc;
 +    output_env_t    oenv;
 +
 +#define NHISTO 360
 +
 +    t_filenm  fnm[] = {
 +        { efTRN, "-f",    NULL,      ffREAD  },
 +        { efTPS, NULL,    NULL,      ffOPTRD },
 +        { efNDX, NULL,    NULL,      ffOPTRD },
 +        { efXVG, "-ot",  "transcur", ffOPTWR },
 +        { efXVG, "-oa",  "tcaf_all", ffWRITE },
 +        { efXVG, "-o",   "tcaf",     ffWRITE },
 +        { efXVG, "-of",  "tcaf_fit", ffWRITE },
 +        { efXVG, "-oc",  "tcaf_cub", ffOPTWR },
 +        { efXVG, "-ov",  "visc_k",   ffWRITE }
 +    };
 +#define NFILE asize(fnm)
 +    int       npargs;
 +    t_pargs  *ppa;
 +
 +    npargs = asize(pa);
 +    ppa    = add_acf_pargs(&npargs, pa);
 +
 +    if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
 +                           NFILE, fnm, npargs, ppa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), title, &top, &ePBC, NULL, NULL, box,
 +                         TRUE);
 +    get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &gnx, &index, &grpname);
 +
 +    if (bMol)
 +    {
 +        if (!bTop)
 +        {
 +            gmx_fatal(FARGS, "Need a topology to determine the molecules");
 +        }
 +        atndx = top.mols.index;
 +    }
 +
 +    if (bK34)
 +    {
 +        nkc = NKC;
 +    }
 +    else
 +    {
 +        nkc = NKC0;
 +    }
 +    nk  = kset_c[nkc];
 +    ntc = nk*NPK;
 +
 +    sprintf(title, "Velocity Autocorrelation Function for %s", grpname);
 +
 +    sysmass = 0;
 +    for (i = 0; i < nk; i++)
 +    {
 +        if (iprod(v0[i], v1[i]) != 0)
 +        {
 +            gmx_fatal(FARGS, "DEATH HORROR: vectors not orthogonal");
 +        }
 +        if (iprod(v0[i], v2[i]) != 0)
 +        {
 +            gmx_fatal(FARGS, "DEATH HORROR: vectors not orthogonal");
 +        }
 +        if (iprod(v1[i], v2[i]) != 0)
 +        {
 +            gmx_fatal(FARGS, "DEATH HORROR: vectors not orthogonal");
 +        }
 +        unitv(v1[i], v1[i]);
 +        unitv(v2[i], v2[i]);
 +    }
 +    snew(tc, ntc);
 +    for (i = 0; i < top.atoms.nr; i++)
 +    {
 +        sysmass += top.atoms.atom[i].m;
 +    }
 +
 +    read_first_frame(oenv, &status, ftp2fn(efTRN, NFILE, fnm), &fr,
 +                     TRX_NEED_X | TRX_NEED_V);
 +    t0 = fr.time;
 +
 +    n_alloc = 0;
 +    nframes = 0;
 +    rho     = 0;
 +    /* bCubic = TRUE; */
 +    do
 +    {
 +        /*
 +           bCubic = bCubic && !TRICLINIC(fr.box) &&
 +           fabs(fr.box[XX][XX]-fr.box[YY][YY]) < 0.001*fr.box[XX][XX] &&
 +           fabs(fr.box[XX][XX]-fr.box[ZZ][ZZ]) < 0.001*fr.box[XX][XX];
 +         */
 +
 +        if (nframes >= n_alloc)
 +        {
 +            n_alloc += 100;
 +            for (i = 0; i < ntc; i++)
 +            {
 +                srenew(tc[i], n_alloc);
 +            }
 +        }
 +
 +        rho += 1/det(fr.box);
 +        for (k = 0; k < nk; k++)
 +        {
 +            for (d = 0; d < DIM; d++)
 +            {
 +                kfac[k][d] = 2*M_PI*v0[k][d]/fr.box[d][d];
 +            }
 +        }
 +        for (i = 0; i < ntc; i++)
 +        {
 +            tc[i][nframes] = 0;
 +        }
 +
 +        for (i = 0; i < gnx; i++)
 +        {
 +            if (bMol)
 +            {
 +                clear_rvec(mv_mol);
 +                clear_rvec(cm_mol);
 +                mtot = 0;
 +                for (j = 0; j < atndx[index[i]+1] - atndx[index[i]]; j++)
 +                {
 +                    at          = atndx[index[i]] + j;
 +                    m           = top.atoms.atom[at].m;
 +                    mv_mol[XX] += m*fr.v[at][XX];
 +                    mv_mol[YY] += m*fr.v[at][YY];
 +                    mv_mol[ZZ] += m*fr.v[at][ZZ];
 +                    cm_mol[XX] += m*fr.x[at][XX];
 +                    cm_mol[YY] += m*fr.x[at][YY];
 +                    cm_mol[ZZ] += m*fr.x[at][ZZ];
 +                    mtot       += m;
 +                }
 +                svmul(1.0/mtot, cm_mol, cm_mol);
 +            }
 +            else
 +            {
 +                svmul(top.atoms.atom[index[i]].m, fr.v[index[i]], mv_mol);
 +            }
 +
 +            if (!bMol)
 +            {
 +                copy_rvec(fr.x[index[i]], cm_mol);
 +            }
 +            j = 0;
 +            for (k = 0; k < nk; k++)
 +            {
 +                sx              = sin(iprod(kfac[k], cm_mol));
 +                cx              = cos(iprod(kfac[k], cm_mol));
 +                tc[j][nframes] += sx*iprod(v1[k], mv_mol);
 +                j++;
 +                tc[j][nframes] += cx*iprod(v1[k], mv_mol);
 +                j++;
 +                tc[j][nframes] += sx*iprod(v2[k], mv_mol);
 +                j++;
 +                tc[j][nframes] += cx*iprod(v2[k], mv_mol);
 +                j++;
 +            }
 +        }
 +
 +        t1 = fr.time;
 +        nframes++;
 +    }
 +    while (read_next_frame(oenv, status, &fr));
 +    close_trj(status);
 +
 +    dt = (t1-t0)/(nframes-1);
 +
 +    rho *= sysmass/nframes*AMU/(NANO*NANO*NANO);
 +    fprintf(stdout, "Density = %g (kg/m^3)\n", rho);
 +    process_tcaf(nframes, dt, nkc, tc, kfac, rho, wt,
 +                 opt2fn_null("-ot", NFILE, fnm),
 +                 opt2fn("-oa", NFILE, fnm), opt2fn("-o", NFILE, fnm),
 +                 opt2fn("-of", NFILE, fnm), opt2fn_null("-oc", NFILE, fnm),
 +                 opt2fn("-ov", NFILE, fnm), oenv);
 +
 +    return 0;
 +}
index e3f14fa84fa94deca5bf347e6039391ae87c9dc7,0000000000000000000000000000000000000000..71c5c0ec6955223e9a7a407621273ad7300f8a29
mode 100644,000000..100644
--- /dev/null
@@@ -1,475 -1,0 +1,481 @@@
-         fprintf(fp, "@ subtitle \"for particles in group %s\"\n", grpname);
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <string.h>
 +
 +#include "sysstuff.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "gromacs/math/utilities.h"
 +#include "gromacs/fileio/futil.h"
 +#include "index.h"
 +#include "typedefs.h"
 +#include "xvgr.h"
 +#include "gstat.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "vec.h"
 +#include "gromacs/fileio/matio.h"
 +#include "gmx_ana.h"
 +
 +
 +int gmx_vanhove(int argc, char *argv[])
 +{
 +    const char *desc[] = {
 +        "[THISMODULE] computes the Van Hove correlation function.",
 +        "The Van Hove G(r,t) is the probability that a particle that is at r[SUB]0[sub]",
 +        "at time zero can be found at position r[SUB]0[sub]+r at time t.",
 +        "[THISMODULE] determines G not for a vector r, but for the length of r.",
 +        "Thus it gives the probability that a particle moves a distance of r",
 +        "in time t.",
 +        "Jumps across the periodic boundaries are removed.",
 +        "Corrections are made for scaling due to isotropic",
 +        "or anisotropic pressure coupling.",
 +        "[PAR]",
 +        "With option [TT]-om[tt] the whole matrix can be written as a function",
 +        "of t and r or as a function of [SQRT]t[sqrt] and r (option [TT]-sqrt[tt]).",
 +        "[PAR]",
 +        "With option [TT]-or[tt] the Van Hove function is plotted for one",
 +        "or more values of t. Option [TT]-nr[tt] sets the number of times,",
 +        "option [TT]-fr[tt] the number spacing between the times.",
 +        "The binwidth is set with option [TT]-rbin[tt]. The number of bins",
 +        "is determined automatically.",
 +        "[PAR]",
 +        "With option [TT]-ot[tt] the integral up to a certain distance",
 +        "(option [TT]-rt[tt]) is plotted as a function of time.",
 +        "[PAR]",
 +        "For all frames that are read the coordinates of the selected particles",
 +        "are stored in memory. Therefore the program may use a lot of memory.",
 +        "For options [TT]-om[tt] and [TT]-ot[tt] the program may be slow.",
 +        "This is because the calculation scales as the number of frames times",
 +        "[TT]-fm[tt] or [TT]-ft[tt].",
 +        "Note that with the [TT]-dt[tt] option the memory usage and calculation",
 +        "time can be reduced."
 +    };
 +    static int  fmmax = 0, ftmax = 0, nlev = 81, nr = 1, fshift = 0;
 +    static real sbin  = 0, rmax = 2, rbin = 0.01, mmax = 0, rint = 0;
 +    t_pargs     pa[]  = {
 +        { "-sqrt",    FALSE, etREAL, {&sbin},
 +          "Use [SQRT]t[sqrt] on the matrix axis which binspacing # in [SQRT]ps[sqrt]" },
 +        { "-fm",      FALSE, etINT, {&fmmax},
 +          "Number of frames in the matrix, 0 is plot all" },
 +        { "-rmax",    FALSE, etREAL, {&rmax},
 +          "Maximum r in the matrix (nm)" },
 +        { "-rbin",    FALSE, etREAL, {&rbin},
 +          "Binwidth in the matrix and for [TT]-or[tt] (nm)" },
 +        { "-mmax",    FALSE, etREAL, {&mmax},
 +          "Maximum density in the matrix, 0 is calculate (1/nm)" },
 +        { "-nlevels", FALSE, etINT,  {&nlev},
 +          "Number of levels in the matrix" },
 +        { "-nr",      FALSE, etINT, {&nr},
 +          "Number of curves for the [TT]-or[tt] output" },
 +        { "-fr",      FALSE, etINT, {&fshift},
 +          "Frame spacing for the [TT]-or[tt] output" },
 +        { "-rt",      FALSE, etREAL, {&rint},
 +          "Integration limit for the [TT]-ot[tt] output (nm)" },
 +        { "-ft",      FALSE, etINT, {&ftmax},
 +          "Number of frames in the [TT]-ot[tt] output, 0 is plot all" }
 +    };
 +#define NPA asize(pa)
 +
 +    t_filenm fnm[] = {
 +        { efTRX, NULL, NULL,  ffREAD },
 +        { efTPS, NULL, NULL,  ffREAD },
 +        { efNDX, NULL, NULL,  ffOPTRD },
 +        { efXPM, "-om", "vanhove", ffOPTWR },
 +        { efXVG, "-or", "vanhove_r", ffOPTWR },
 +        { efXVG, "-ot", "vanhove_t", ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +
 +    output_env_t oenv;
 +    const char  *matfile, *otfile, *orfile;
 +    char         title[256];
 +    t_topology   top;
 +    int          ePBC;
 +    matrix       boxtop, box, *sbox, avbox, corr;
 +    rvec        *xtop, *x, **sx;
 +    int          isize, nalloc, nallocn, natom;
 +    t_trxstatus *status;
 +    atom_id     *index;
 +    char        *grpname;
 +    int          nfr, f, ff, i, m, mat_nx = 0, nbin = 0, bin, mbin, fbin;
 +    real        *time, t, invbin = 0, rmax2 = 0, rint2 = 0, d2;
 +    real         invsbin = 0, matmax, normfac, dt, *tickx, *ticky;
 +    char         buf[STRLEN], **legend;
 +    real       **mat = NULL;
 +    int         *pt  = NULL, **pr = NULL, *mcount = NULL, *tcount = NULL, *rcount = NULL;
 +    FILE        *fp;
 +    t_rgb        rlo = {1, 1, 1}, rhi = {0, 0, 0};
 +
 +    if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
 +                           NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    matfile = opt2fn_null("-om", NFILE, fnm);
 +    if (opt2parg_bSet("-fr", NPA, pa))
 +    {
 +        orfile  = opt2fn("-or", NFILE, fnm);
 +    }
 +    else
 +    {
 +        orfile  = opt2fn_null("-or", NFILE, fnm);
 +    }
 +    if (opt2parg_bSet("-rt", NPA, pa))
 +    {
 +        otfile  = opt2fn("-ot", NFILE, fnm);
 +    }
 +    else
 +    {
 +        otfile  = opt2fn_null("-ot", NFILE, fnm);
 +    }
 +
 +    if (!matfile && !otfile && !orfile)
 +    {
 +        fprintf(stderr,
 +                "For output set one (or more) of the output file options\n");
 +        exit(0);
 +    }
 +
 +    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), title, &top, &ePBC, &xtop, NULL, boxtop,
 +                  FALSE);
 +    get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, &grpname);
 +
 +    nalloc = 0;
 +    time   = NULL;
 +    sbox   = NULL;
 +    sx     = NULL;
 +    clear_mat(avbox);
 +
 +    natom = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
 +    nfr   = 0;
 +    do
 +    {
 +        if (nfr >= nalloc)
 +        {
 +            nalloc += 100;
 +            srenew(time, nalloc);
 +            srenew(sbox, nalloc);
 +            srenew(sx, nalloc);
 +        }
 +
 +        time[nfr] = t;
 +        copy_mat(box, sbox[nfr]);
 +        /* This assumes that the off-diagonal box elements
 +         * are not affected by jumps across the periodic boundaries.
 +         */
 +        m_add(avbox, box, avbox);
 +        snew(sx[nfr], isize);
 +        for (i = 0; i < isize; i++)
 +        {
 +            copy_rvec(x[index[i]], sx[nfr][i]);
 +        }
 +
 +        nfr++;
 +    }
 +    while (read_next_x(oenv, status, &t, x, box));
 +
 +    /* clean up */
 +    sfree(x);
 +    close_trj(status);
 +
 +    fprintf(stderr, "Read %d frames\n", nfr);
 +
 +    dt = (time[nfr-1] - time[0])/(nfr - 1);
 +    /* Some ugly rounding to get nice nice times in the output */
 +    dt = (int)(10000.0*dt + 0.5)/10000.0;
 +
 +    invbin = 1.0/rbin;
 +
 +    if (matfile)
 +    {
 +        if (fmmax <= 0 || fmmax >= nfr)
 +        {
 +            fmmax = nfr - 1;
 +        }
 +        snew(mcount, fmmax);
 +        nbin = (int)(rmax*invbin + 0.5);
 +        if (sbin == 0)
 +        {
 +            mat_nx = fmmax + 1;
 +        }
 +        else
 +        {
 +            invsbin = 1.0/sbin;
 +            mat_nx  = sqrt(fmmax*dt)*invsbin + 1;
 +        }
 +        snew(mat, mat_nx);
 +        for (f = 0; f < mat_nx; f++)
 +        {
 +            snew(mat[f], nbin);
 +        }
 +        rmax2 = sqr(nbin*rbin);
 +        /* Initialize time zero */
 +        mat[0][0]  = nfr*isize;
 +        mcount[0] += nfr;
 +    }
 +    else
 +    {
 +        fmmax = 0;
 +    }
 +
 +    if (orfile)
 +    {
 +        snew(pr, nr);
 +        nalloc = 0;
 +        snew(rcount, nr);
 +    }
 +
 +    if (otfile)
 +    {
 +        if (ftmax <= 0)
 +        {
 +            ftmax = nfr - 1;
 +        }
 +        snew(tcount, ftmax);
 +        snew(pt, nfr);
 +        rint2 = rint*rint;
 +        /* Initialize time zero */
 +        pt[0]      = nfr*isize;
 +        tcount[0] += nfr;
 +    }
 +    else
 +    {
 +        ftmax = 0;
 +    }
 +
 +    msmul(avbox, 1.0/nfr, avbox);
 +    for (f = 0; f < nfr; f++)
 +    {
 +        if (f % 100 == 0)
 +        {
 +            fprintf(stderr, "\rProcessing frame %d", f);
 +        }
 +        /* Scale all the configuration to the average box */
 +        m_inv_ur0(sbox[f], corr);
 +        mmul_ur0(avbox, corr, corr);
 +        for (i = 0; i < isize; i++)
 +        {
 +            mvmul_ur0(corr, sx[f][i], sx[f][i]);
 +            if (f > 0)
 +            {
 +                /* Correct for periodic jumps */
 +                for (m = DIM-1; m >= 0; m--)
 +                {
 +                    while (sx[f][i][m] - sx[f-1][i][m] > 0.5*avbox[m][m])
 +                    {
 +                        rvec_dec(sx[f][i], avbox[m]);
 +                    }
 +                    while (sx[f][i][m] - sx[f-1][i][m] <= -0.5*avbox[m][m])
 +                    {
 +                        rvec_inc(sx[f][i], avbox[m]);
 +                    }
 +                }
 +            }
 +        }
 +        for (ff = 0; ff < f; ff++)
 +        {
 +            fbin = f - ff;
 +            if (fbin <= fmmax || fbin <= ftmax)
 +            {
 +                if (sbin == 0)
 +                {
 +                    mbin = fbin;
 +                }
 +                else
 +                {
 +                    mbin = (int)(sqrt(fbin*dt)*invsbin + 0.5);
 +                }
 +                for (i = 0; i < isize; i++)
 +                {
 +                    d2 = distance2(sx[f][i], sx[ff][i]);
 +                    if (mbin < mat_nx && d2 < rmax2)
 +                    {
 +                        bin = (int)(sqrt(d2)*invbin + 0.5);
 +                        if (bin < nbin)
 +                        {
 +                            mat[mbin][bin] += 1;
 +                        }
 +                    }
 +                    if (fbin <= ftmax && d2 <= rint2)
 +                    {
 +                        pt[fbin]++;
 +                    }
 +                }
 +                if (matfile)
 +                {
 +                    mcount[mbin]++;
 +                }
 +                if (otfile)
 +                {
 +                    tcount[fbin]++;
 +                }
 +            }
 +        }
 +        if (orfile)
 +        {
 +            for (fbin = 0; fbin < nr; fbin++)
 +            {
 +                ff = f - (fbin + 1)*fshift;
 +                if (ff >= 0)
 +                {
 +                    for (i = 0; i < isize; i++)
 +                    {
 +                        d2  = distance2(sx[f][i], sx[ff][i]);
 +                        bin = (int)(sqrt(d2)*invbin + 0.5);
 +                        if (bin >= nalloc)
 +                        {
 +                            nallocn = 10*(bin/10) + 11;
 +                            for (m = 0; m < nr; m++)
 +                            {
 +                                srenew(pr[m], nallocn);
 +                                for (i = nalloc; i < nallocn; i++)
 +                                {
 +                                    pr[m][i] = 0;
 +                                }
 +                            }
 +                            nalloc = nallocn;
 +                        }
 +                        pr[fbin][bin]++;
 +                    }
 +                    rcount[fbin]++;
 +                }
 +            }
 +        }
 +    }
 +    fprintf(stderr, "\n");
 +
 +    if (matfile)
 +    {
 +        matmax = 0;
 +        for (f = 0; f < mat_nx; f++)
 +        {
 +            normfac = 1.0/(mcount[f]*isize*rbin);
 +            for (i = 0; i < nbin; i++)
 +            {
 +                mat[f][i] *= normfac;
 +                if (mat[f][i] > matmax && (f != 0 || i != 0))
 +                {
 +                    matmax = mat[f][i];
 +                }
 +            }
 +        }
 +        fprintf(stdout, "Value at (0,0): %.3f, maximum of the rest %.3f\n",
 +                mat[0][0], matmax);
 +        if (mmax > 0)
 +        {
 +            matmax = mmax;
 +        }
 +        snew(tickx, mat_nx);
 +        for (f = 0; f < mat_nx; f++)
 +        {
 +            if (sbin == 0)
 +            {
 +                tickx[f] = f*dt;
 +            }
 +            else
 +            {
 +                tickx[f] = f*sbin;
 +            }
 +        }
 +        snew(ticky, nbin+1);
 +        for (i = 0; i <= nbin; i++)
 +        {
 +            ticky[i] = i*rbin;
 +        }
 +        fp = gmx_ffopen(matfile, "w");
 +        write_xpm(fp, MAT_SPATIAL_Y, "Van Hove function", "G (1/nm)",
 +                  sbin == 0 ? "time (ps)" : "sqrt(time) (ps^1/2)", "r (nm)",
 +                  mat_nx, nbin, tickx, ticky, mat, 0, matmax, rlo, rhi, &nlev);
 +        gmx_ffclose(fp);
 +    }
 +
 +    if (orfile)
 +    {
 +        fp = xvgropen(orfile, "Van Hove function", "r (nm)", "G (nm\\S-1\\N)", oenv);
-         fprintf(fp, "@ subtitle \"for particles in group %s\"\n", grpname);
++        if (output_env_get_print_xvgr_codes(oenv))
++        {
++            fprintf(fp, "@ subtitle \"for particles in group %s\"\n", grpname);
++        }
 +        snew(legend, nr);
 +        for (fbin = 0; fbin < nr; fbin++)
 +        {
 +            sprintf(buf, "%g ps", (fbin + 1)*fshift*dt);
 +            legend[fbin] = strdup(buf);
 +        }
 +        xvgr_legend(fp, nr, (const char**)legend, oenv);
 +        for (i = 0; i < nalloc; i++)
 +        {
 +            fprintf(fp, "%g", i*rbin);
 +            for (fbin = 0; fbin < nr; fbin++)
 +            {
 +                fprintf(fp, " %g",
 +                        (real)pr[fbin][i]/(rcount[fbin]*isize*rbin*(i == 0 ? 0.5 : 1)));
 +            }
 +            fprintf(fp, "\n");
 +        }
 +        gmx_ffclose(fp);
 +    }
 +
 +    if (otfile)
 +    {
 +        sprintf(buf, "Probability of moving less than %g nm", rint);
 +        fp = xvgropen(otfile, buf, "t (ps)", "", oenv);
++        if (output_env_get_print_xvgr_codes(oenv))
++        {
++            fprintf(fp, "@ subtitle \"for particles in group %s\"\n", grpname);
++        }
 +        for (f = 0; f <= ftmax; f++)
 +        {
 +            fprintf(fp, "%g %g\n", f*dt, (real)pt[f]/(tcount[f]*isize));
 +        }
 +        gmx_ffclose(fp);
 +    }
 +
 +    do_view(oenv, matfile, NULL);
 +    do_view(oenv, orfile, NULL);
 +    do_view(oenv, otfile, NULL);
 +
 +    return 0;
 +}
index 6e63e7a5c0372dfb6035c40f218e61c87dbf5538,0000000000000000000000000000000000000000..3d7bd72465fe0e150f42b1a761865b7e74c30e58
mode 100644,000000..100644
--- /dev/null
@@@ -1,3627 -1,0 +1,3637 @@@
-         fprintf(fp, "&\n");
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
 +/*! \internal \file
 + *  \brief Implementation of the Weighted Histogram Analysis Method (WHAM)
 + *
 + *  \author Jochen Hub <jhub@gwdg.de>
 + */
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <ctype.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include <sstream>
 +
 +#include "gromacs/commandline/pargs.h"
 +#include "typedefs.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "vec.h"
 +#include "copyrite.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "names.h"
 +#include "gromacs/random/random.h"
 +#include "gmx_ana.h"
 +#include "macros.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "xvgr.h"
 +
 +#include "gmx_fatal.h"
 +
 +//! longest file names allowed in input files
 +#define WHAM_MAXFILELEN 2048
 +
 +/*! \brief
 + * enum for energy units
 + */
 +enum {
 +    enSel, en_kJ, en_kCal, en_kT, enNr
 +};
 +/*! \brief
 + * enum for type of input files (pdos, tpr, or pullf)
 + */
 +enum {
 +    whamin_unknown, whamin_tpr, whamin_pullxf, whamin_pdo
 +};
 +
 +/*! \brief enum for bootstrapping method
 + *
 + * These bootstrap methods are supported:
 + *  - bootstrap complete histograms with continuous weights (Bayesian bootstrap)
 + *    (bsMethod_BayesianHist)
 + *  - bootstrap complete histograms (bsMethod_hist)
 + *  - bootstrap trajectories from given umbrella histograms. This generates new
 + *    "synthetic" histograms (bsMethod_traj)
 + *  - bootstrap trajectories from Gaussian with mu/sigma computed from
 + *    the respective histogram (bsMethod_trajGauss). This gives very similar
 + *    results compared to bsMethod_traj.
 + *
 + *  ********************************************************************
 + *  FOR MORE DETAILS ON THE BOOTSTRAP METHODS (INCLUDING EXAMPLES), SEE
 + *  JS Hub, BL de Groot, D van der Spoel
 + *  g_wham - A free weighted histogram analysis implementation including
 + *  robust error and autocorrelation estimates,
 + *  J Chem Theory Comput, 6(12), 3713-3720 (2010)
 + *  ********************************************************************
 + */
 +enum {
 +    bsMethod_unknown, bsMethod_BayesianHist, bsMethod_hist,
 +    bsMethod_traj, bsMethod_trajGauss
 +};
 +
 +
 +//! Parameters of the umbrella potentials
 +typedef struct
 +{
 +    /*!
 +     * \name Using umbrella pull code since gromacs 4.x
 +     */
 +    /*!\{*/
 +    int      npullcrds;     //!< nr of pull coordinates in tpr file
 +    int      pull_geometry; //!< such as distance, direction
 +    ivec     pull_dim;      //!< pull dimension with geometry distance
 +    int      pull_ndim;     //!< nr of pull_dim != 0
 +    gmx_bool bPrintRef;     //!< Coordinates of reference groups written to pullx.xvg ?
 +    real    *k;             //!< force constants in tpr file
 +    real    *init_dist;     //!< reference displacements
 +    real    *umbInitDist;   //!< reference displacement in umbrella direction
 +    /*!\}*/
 +    /*!
 +     * \name Using PDO files common until gromacs 3.x
 +     */
 +    /*!\{*/
 +    int    nSkip;
 +    char   Reference[256];
 +    int    nPull;
 +    int    nDim;
 +    ivec   Dims;
 +    char   PullName[4][256];
 +    double UmbPos[4][3];
 +    double UmbCons[4][3];
 +    /*!\}*/
 +} t_UmbrellaHeader;
 +
 +//! Data in the umbrella histograms
 +typedef struct
 +{
 +    int      nPull;       //!< nr of pull groups in this pdo or pullf/x file
 +    double **Histo;       //!< nPull histograms
 +    double **cum;         //!< nPull cumulative distribution functions
 +    int      nBin;        //!< nr of bins. identical to opt->bins
 +    double  *k;           //!< force constants for the nPull groups
 +    double  *pos;         //!< umbrella positions for the nPull groups
 +    double  *z;           //!< z=(-Fi/kT) for the nPull groups. These values are iteratively computed during wham
 +    int     *N;           //!< nr of data points in nPull histograms.
 +    int     *Ntot;        //!< also nr of data points. N and Ntot only differ if bHistEq==TRUE
 +
 +    /*! \brief  g = 1 + 2*tau[int]/dt where tau is the integrated autocorrelation time.
 +     *
 +     * Compare, e.g. Ferrenberg/Swendsen, PRL 63:1195 (1989),
 +     * Kumar et al, J Comp Chem 13, 1011-1021 (1992), eq. 28
 +     */
 +    double *g;
 +    double *tau;         //!< intetrated autocorrelation time (IACT)
 +    double *tausmooth;   //!< smoothed IACT
 +
 +    double  dt;          //!< timestep in the input data. Can be adapted with g_wham option -dt
 +
 +    /*! \brief TRUE, if any data point of the histogram is within min and max, otherwise FALSE */
 +    gmx_bool **bContrib;
 +    real     **ztime;     //!< input data z(t) as a function of time. Required to compute ACTs
 +
 +    /*! \brief average force estimated from average displacement, fAv=dzAv*k
 +     *
 +     *  Used for integration to guess the potential.
 +     */
 +    real   *forceAv;
 +    real   *aver;         //!< average of histograms
 +    real   *sigma;        //!< stddev of histograms
 +    double *bsWeight;     //!< for bootstrapping complete histograms with continuous weights
 +} t_UmbrellaWindow;
 +
 +//! Selection of pull groups to be used in WHAM (one structure for each tpr file)
 +typedef struct
 +{
 +    int       n;         //!< total nr of pull groups in this tpr file
 +    int       nUse;      //!< nr of pull groups used
 +    gmx_bool *bUse;      //!< boolean array of size n. =1 if used, =0 if not
 +} t_groupselection;
 +
 +//! Parameters of WHAM
 +typedef struct
 +{
 +    /*!
 +     * \name Input stuff
 +     */
 +    /*!\{*/
 +    const char       *fnTpr, *fnPullf, *fnGroupsel;
 +    const char       *fnPdo, *fnPullx;            //!< file names of input
 +    gmx_bool          bTpr, bPullf, bPdo, bPullx; //!< input file types given?
 +    real              tmin, tmax, dt;             //!< only read input within tmin and tmax with dt
 +
 +    gmx_bool          bInitPotByIntegration;      //!< before WHAM, guess potential by force integration. Yields 1.5 to 2 times faster convergence
 +    int               stepUpdateContrib;          //!< update contribution table every ... iterations. Accelerates WHAM.
 +    int               nGroupsel;                  //!< if >0: use only certain group in WHAM, if ==0: use all groups
 +    t_groupselection *groupsel;                   //!< for each tpr file: which pull groups to use in WHAM?
 +    /*!\}*/
 +    /*!
 +     * \name Basic WHAM options
 +     */
 +    /*!\{*/
 +    int      bins;                   //!< nr of bins, min, max, and dz of profile
 +    real     min, max, dz;
 +    real     Temperature, Tolerance; //!< temperature, converged when probability changes less than Tolerance
 +    gmx_bool bCycl;                  //!< generate cyclic (periodic) PMF
 +    /*!\}*/
 +    /*!
 +     * \name Output control
 +     */
 +    /*!\{*/
 +    gmx_bool bLog;                   //!< energy output (instead of probability) for profile
 +    int      unit;                   //!< unit for PMF output kJ/mol or kT or kCal/mol
 +    gmx_bool bSym;                   //!< symmetrize PMF around z=0 after WHAM, useful for membranes etc.
 +    /*! \brief after wham, set prof to zero at this z-position.
 +     * When bootstrapping, set zProf0 to a "stable" reference position.
 +     */
 +    real         zProf0;
 +    gmx_bool     bProf0Set;              //!< setting profile to 0 at zProf0?
 +
 +    gmx_bool     bBoundsOnly, bHistOnly; //!< determine min and max, or write histograms and exit
 +    gmx_bool     bAuto;                  //!< determine min and max automatically but do not exit
 +
 +    gmx_bool     verbose;                //!< more noisy wham mode
 +    int          stepchange;             //!< print maximum change in prof after how many interations
 +    output_env_t oenv;                   //!< xvgr options
 +    /*!\}*/
 +    /*!
 +     * \name Autocorrelation stuff
 +     */
 +    /*!\{*/
 +    gmx_bool bTauIntGiven, bCalcTauInt; //!< IACT given or should be calculated?
 +    real     sigSmoothIact;             //!< sigma of Gaussian to smooth ACTs
 +    gmx_bool bAllowReduceIact;          //!< Allow to reduce ACTs during smoothing. Otherwise ACT are only increased during smoothing
 +    real     acTrestart;                //!< when computing ACT, time between restarting points
 +
 +    /* \brief Enforce the same weight for each umbella window, that is
 +     *  calculate with the same number of data points for
 +     *  each window. That can be reasonable, if the histograms
 +     *  have different length, but due to autocorrelation,
 +     *  a longer simulation should not have larger weightin wham.
 +     */
 +    gmx_bool bHistEq;
 +    /*!\}*/
 +
 +    /*!
 +     * \name Bootstrapping stuff
 +     */
 +    /*!\{*/
 +    int nBootStrap;              //!< nr of bootstraps (50 is usually enough)
 +
 +    /* \brief bootstrap method
 +     *
 +     * if == bsMethod_hist, consider complete histograms as independent
 +     * data points and, hence, only mix complete histograms.
 +     * if == bsMethod_BayesianHist, consider complete histograms
 +     * as independent data points, but assign random weights
 +     * to the histograms during the bootstrapping ("Bayesian bootstrap")
 +     * In case of long correlations (e.g., inside a channel), these
 +     * will yield a more realistic error.
 +     * if == bsMethod_traj(Gauss), generate synthetic histograms
 +     * for each given
 +     * histogram by generating an autocorrelated random sequence
 +     * that is distributed according to the respective given
 +     * histogram. With bsMethod_trajGauss, bootstrap from a Gaussian
 +     * (instead of from the umbrella histogram) to generate a new
 +     * histogram.
 +     */
 +    int bsMethod;
 +
 +    /* \brief  autocorrelation time (ACT) used to generate synthetic histograms. If ==0, use calculated ACF */
 +    real tauBootStrap;
 +
 +    /* \brief when mixing histograms, mix only histograms withing blocks
 +              long the reaction coordinate xi. Avoids gaps along xi. */
 +    int histBootStrapBlockLength;
 +
 +    int bsSeed;                    //!< random seed for bootstrapping
 +
 +    /* \brief Write cumulative distribution functions (CDFs) of histograms
 +              and write the generated histograms for each bootstrap */
 +    gmx_bool bs_verbose;
 +    /*!\}*/
 +    /*!
 +     * \name tabulated umbrella potential stuff
 +     */
 +    /*!\{*/
 +    gmx_bool  bTab;
 +    double   *tabX, *tabY, tabMin, tabMax, tabDz;
 +    int       tabNbins;
 +    /*!\}*/
 +    gmx_rng_t rng;                  //!< gromacs random number generator
 +} t_UmbrellaOptions;
 +
 +//! Make an umbrella window (may contain several histograms)
 +t_UmbrellaWindow * initUmbrellaWindows(int nwin)
 +{
 +    t_UmbrellaWindow *win;
 +    int               i;
 +    snew(win, nwin);
 +    for (i = 0; i < nwin; i++)
 +    {
 +        win[i].Histo    = win[i].cum  = 0;
 +        win[i].k        = win[i].pos  = win[i].z = 0;
 +        win[i].N        = win[i].Ntot = 0;
 +        win[i].g        = win[i].tau  = win[i].tausmooth = 0;
 +        win[i].bContrib = 0;
 +        win[i].ztime    = 0;
 +        win[i].forceAv  = 0;
 +        win[i].aver     = win[i].sigma = 0;
 +        win[i].bsWeight = 0;
 +    }
 +    return win;
 +}
 +
 +//! Delete an umbrella window (may contain several histograms)
 +void freeUmbrellaWindows(t_UmbrellaWindow *win, int nwin)
 +{
 +    int i, j;
 +    for (i = 0; i < nwin; i++)
 +    {
 +        if (win[i].Histo)
 +        {
 +            for (j = 0; j < win[i].nPull; j++)
 +            {
 +                sfree(win[i].Histo[j]);
 +            }
 +        }
 +        if (win[i].cum)
 +        {
 +            for (j = 0; j < win[i].nPull; j++)
 +            {
 +                sfree(win[i].cum[j]);
 +            }
 +        }
 +        if (win[i].bContrib)
 +        {
 +            for (j = 0; j < win[i].nPull; j++)
 +            {
 +                sfree(win[i].bContrib[j]);
 +            }
 +        }
 +        sfree(win[i].Histo);
 +        sfree(win[i].cum);
 +        sfree(win[i].k);
 +        sfree(win[i].pos);
 +        sfree(win[i].z);
 +        sfree(win[i].N);
 +        sfree(win[i].Ntot);
 +        sfree(win[i].g);
 +        sfree(win[i].tau);
 +        sfree(win[i].tausmooth);
 +        sfree(win[i].bContrib);
 +        sfree(win[i].ztime);
 +        sfree(win[i].forceAv);
 +        sfree(win[i].aver);
 +        sfree(win[i].sigma);
 +        sfree(win[i].bsWeight);
 +    }
 +    sfree(win);
 +}
 +
 +/*! \brief
 + * Read and setup tabulated umbrella potential
 + */
 +void setup_tab(const char *fn, t_UmbrellaOptions *opt)
 +{
 +    int      i, ny, nl;
 +    double **y;
 +
 +    printf("Setting up tabulated potential from file %s\n", fn);
 +    nl            = read_xvg(fn, &y, &ny);
 +    opt->tabNbins = nl;
 +    if (ny != 2)
 +    {
 +        gmx_fatal(FARGS, "Found %d columns in %s. Expected 2.\n", ny, fn);
 +    }
 +    opt->tabMin = y[0][0];
 +    opt->tabMax = y[0][nl-1];
 +    opt->tabDz  = (opt->tabMax-opt->tabMin)/(nl-1);
 +    if (opt->tabDz <= 0)
 +    {
 +        gmx_fatal(FARGS, "The tabulated potential in %s must be provided in \n"
 +                  "ascending z-direction", fn);
 +    }
 +    for (i = 0; i < nl-1; i++)
 +    {
 +        if  (fabs(y[0][i+1]-y[0][i]-opt->tabDz) > opt->tabDz/1e6)
 +        {
 +            gmx_fatal(FARGS, "z-values in %s are not equally spaced.\n", ny, fn);
 +        }
 +    }
 +    snew(opt->tabY, nl);
 +    snew(opt->tabX, nl);
 +    for (i = 0; i < nl; i++)
 +    {
 +        opt->tabX[i] = y[0][i];
 +        opt->tabY[i] = y[1][i];
 +    }
 +    printf("Found equally spaced tabulated potential from %g to %g, spacing %g\n",
 +           opt->tabMin, opt->tabMax, opt->tabDz);
 +}
 +
 +//! Read the header of an PDO file (position, force const, nr of groups)
 +void read_pdo_header(FILE * file, t_UmbrellaHeader * header, t_UmbrellaOptions *opt)
 +{
 +    char               line[2048];
 +    char               Buffer0[256], Buffer1[256], Buffer2[256], Buffer3[256], Buffer4[256];
 +    int                i;
 +    std::istringstream ist;
 +
 +    /*  line 1 */
 +    if (fgets(line, 2048, file) == NULL)
 +    {
 +        gmx_fatal(FARGS, "Error reading header from pdo file\n");
 +    }
 +    ist.str(line);
 +    ist >> Buffer0 >> Buffer1 >> Buffer2;
 +    if (strcmp(Buffer1, "UMBRELLA"))
 +    {
 +        gmx_fatal(FARGS, "This does not appear to be a valid pdo file. Found %s, expected %s\n"
 +                  "(Found in first line: `%s')\n",
 +                  Buffer1, "UMBRELLA", line);
 +    }
 +    if (strcmp(Buffer2, "3.0"))
 +    {
 +        gmx_fatal(FARGS, "This does not appear to be a version 3.0 pdo file");
 +    }
 +
 +    /*  line 2 */
 +    if (fgets(line, 2048, file) == NULL)
 +    {
 +        gmx_fatal(FARGS, "Error reading header from pdo file\n");
 +    }
 +    ist.str(line);
 +    ist >> Buffer0 >> Buffer1 >> Buffer2 >> header->Dims[0] >> header->Dims[1] >> header->Dims[2];
 +    /* printf("%d %d %d\n", header->Dims[0],header->Dims[1],header->Dims[2]); */
 +
 +    header->nDim = header->Dims[0] + header->Dims[1] + header->Dims[2];
 +    if (header->nDim != 1)
 +    {
 +        gmx_fatal(FARGS, "Currently only supports one dimension");
 +    }
 +
 +    /* line3 */
 +    if (fgets(line, 2048, file) == NULL)
 +    {
 +        gmx_fatal(FARGS, "Error reading header from pdo file\n");
 +    }
 +    ist.str(line);
 +    ist >> Buffer0 >> Buffer1 >> header->nSkip;
 +
 +    /* line 4 */
 +    if (fgets(line, 2048, file) == NULL)
 +    {
 +        gmx_fatal(FARGS, "Error reading header from pdo file\n");
 +    }
 +    ist.str(line);
 +    ist >> Buffer0 >> Buffer1 >> Buffer2 >> header->Reference;
 +
 +    /* line 5 */
 +    if (fgets(line, 2048, file) == NULL)
 +    {
 +        gmx_fatal(FARGS, "Error reading header from pdo file\n");
 +    }
 +    ist.str(line);
 +    ist >> Buffer0 >> Buffer1 >> Buffer2 >> Buffer3 >> Buffer4 >> header->nPull;
 +
 +    if (opt->verbose)
 +    {
 +        printf("\tFound nPull=%d , nSkip=%d, ref=%s\n", header->nPull, header->nSkip,
 +               header->Reference);
 +    }
 +
 +    for (i = 0; i < header->nPull; ++i)
 +    {
 +        if (fgets(line, 2048, file) == NULL)
 +        {
 +            gmx_fatal(FARGS, "Error reading header from pdo file\n");
 +        }
 +        ist.str(line);
 +        ist >> Buffer0 >> Buffer1 >> Buffer2 >> header->PullName[i];
 +        ist >> Buffer0 >> Buffer1 >> header->UmbPos[i][0];
 +        ist >> Buffer0 >> Buffer1 >> header->UmbCons[i][0];
 +
 +        if (opt->verbose)
 +        {
 +            printf("\tpullgroup %d, pullname = %s, UmbPos = %g, UmbConst = %g\n",
 +                   i, header->PullName[i], header->UmbPos[i][0], header->UmbCons[i][0]);
 +        }
 +    }
 +
 +    if (fgets(line, 2048, file) == NULL)
 +    {
 +        gmx_fatal(FARGS, "Cannot read from file\n");
 +    }
 +    ist.str(line);
 +    ist >> Buffer3;
 +    if (strcmp(Buffer3, "#####") != 0)
 +    {
 +        gmx_fatal(FARGS, "Expected '#####', found %s. Hick.\n", Buffer3);
 +    }
 +}
 +
 +//! smarter fgets
 +static char *fgets3(FILE *fp, char ptr[], int *len)
 +{
 +    char *p;
 +    int   slen;
 +
 +    if (fgets(ptr, *len-1, fp) == NULL)
 +    {
 +        return NULL;
 +    }
 +    p = ptr;
 +    while ((strchr(ptr, '\n') == NULL) && (!feof(fp)))
 +    {
 +        /* This line is longer than len characters, let's increase len! */
 +        *len += STRLEN;
 +        p    += STRLEN;
 +        srenew(ptr, *len);
 +        if (fgets(p-1, STRLEN, fp) == NULL)
 +        {
 +            break;
 +        }
 +    }
 +    slen = strlen(ptr);
 +    if (ptr[slen-1] == '\n')
 +    {
 +        ptr[slen-1] = '\0';
 +    }
 +
 +    return ptr;
 +}
 +
 +/*! \brief Read the data columns of and PDO file.
 + *
 + *  TO DO: Get rid of the scanf function to avoid the clang warning.
 + *         At the moment, this warning is avoided by hiding the format string
 + *         the variable fmtlf.
 + */
 +void read_pdo_data(FILE * file, t_UmbrellaHeader * header,
 +                   int fileno, t_UmbrellaWindow * win,
 +                   t_UmbrellaOptions *opt,
 +                   gmx_bool bGetMinMax, real *mintmp, real *maxtmp)
 +{
 +    int                i, inttemp, bins, count, ntot;
 +    real               min, max, minfound = 1e20, maxfound = -1e20;
 +    double             temp, time, time0 = 0, dt;
 +    char              *ptr    = 0;
 +    t_UmbrellaWindow * window = 0;
 +    gmx_bool           timeok, dt_ok = 1;
 +    char              *tmpbuf   = 0, fmt[256], fmtign[256], fmtlf[5] = "%lf";
 +    int                len      = STRLEN, dstep = 1;
 +    const int          blocklen = 4096;
 +    int               *lennow   = 0;
 +
 +    if (!bGetMinMax)
 +    {
 +        bins = opt->bins;
 +        min  = opt->min;
 +        max  = opt->max;
 +
 +        window = win+fileno;
 +        /* Need to alocate memory and set up structure */
 +        window->nPull = header->nPull;
 +        window->nBin  = bins;
 +
 +        snew(window->Histo, window->nPull);
 +        snew(window->z, window->nPull);
 +        snew(window->k, window->nPull);
 +        snew(window->pos, window->nPull);
 +        snew(window->N, window->nPull);
 +        snew(window->Ntot, window->nPull);
 +        snew(window->g, window->nPull);
 +        snew(window->bsWeight, window->nPull);
 +
 +        window->bContrib = 0;
 +
 +        if (opt->bCalcTauInt)
 +        {
 +            snew(window->ztime, window->nPull);
 +        }
 +        else
 +        {
 +            window->ztime = 0;
 +        }
 +        snew(lennow, window->nPull);
 +
 +        for (i = 0; i < window->nPull; ++i)
 +        {
 +            window->z[i]        = 1;
 +            window->bsWeight[i] = 1.;
 +            snew(window->Histo[i], bins);
 +            window->k[i]    = header->UmbCons[i][0];
 +            window->pos[i]  = header->UmbPos[i][0];
 +            window->N[i]    = 0;
 +            window->Ntot[i] = 0;
 +            window->g[i]    = 1.;
 +            if (opt->bCalcTauInt)
 +            {
 +                window->ztime[i] = 0;
 +            }
 +        }
 +
 +        /* Done with setup */
 +    }
 +    else
 +    {
 +        minfound = 1e20;
 +        maxfound = -1e20;
 +        min      = max = bins = 0; /* Get rid of warnings */
 +    }
 +
 +    count = 0;
 +    snew(tmpbuf, len);
 +    while ( (ptr = fgets3(file, tmpbuf, &len)) != NULL)
 +    {
 +        trim(ptr);
 +
 +        if (ptr[0] == '#' || strlen(ptr) < 2)
 +        {
 +            continue;
 +        }
 +
 +        /* Initiate format string */
 +        fmtign[0] = '\0';
 +        strcat(fmtign, "%*s");
 +
 +        sscanf(ptr, fmtlf, &time); /* printf("Time %f\n",time); */
 +        /* Round time to fs */
 +        time = 1.0/1000*( static_cast<int> (time*1000+0.5) );
 +
 +        /* get time step of pdo file */
 +        if (count == 0)
 +        {
 +            time0 = time;
 +        }
 +        else if (count == 1)
 +        {
 +            dt = time-time0;
 +            if (opt->dt > 0.0)
 +            {
 +                dstep = static_cast<int>(opt->dt/dt+0.5);
 +                if (dstep == 0)
 +                {
 +                    dstep = 1;
 +                }
 +            }
 +            if (!bGetMinMax)
 +            {
 +                window->dt = dt*dstep;
 +            }
 +        }
 +        count++;
 +
 +        dt_ok  = ((count-1)%dstep == 0);
 +        timeok = (dt_ok && time >= opt->tmin && time <= opt->tmax);
 +        /* if (opt->verbose)
 +           printf(" time = %f, (tmin,tmax)=(%e,%e), dt_ok=%d timeok=%d\n",
 +           time,opt->tmin, opt->tmax, dt_ok,timeok); */
 +
 +        if (timeok)
 +        {
 +            for (i = 0; i < header->nPull; ++i)
 +            {
 +                strcpy(fmt, fmtign);
 +                strcat(fmt, "%lf");      /* Creating a format stings such as "%*s...%*s%lf" */
 +                strcat(fmtign, "%*s");   /* ignoring one more entry in the next loop */
 +                if (sscanf(ptr, fmt, &temp))
 +                {
 +                    temp += header->UmbPos[i][0];
 +                    if (bGetMinMax)
 +                    {
 +                        if (temp < minfound)
 +                        {
 +                            minfound = temp;
 +                        }
 +                        if (temp > maxfound)
 +                        {
 +                            maxfound = temp;
 +                        }
 +                    }
 +                    else
 +                    {
 +                        if (opt->bCalcTauInt)
 +                        {
 +                            /* save time series for autocorrelation analysis */
 +                            ntot = window->Ntot[i];
 +                            if (ntot >= lennow[i])
 +                            {
 +                                lennow[i] += blocklen;
 +                                srenew(window->ztime[i], lennow[i]);
 +                            }
 +                            window->ztime[i][ntot] = temp;
 +                        }
 +
 +                        temp -= min;
 +                        temp /= (max-min);
 +                        temp *= bins;
 +                        temp  = floor(temp);
 +
 +                        inttemp = static_cast<int> (temp);
 +                        if (opt->bCycl)
 +                        {
 +                            if (inttemp < 0)
 +                            {
 +                                inttemp += bins;
 +                            }
 +                            else if (inttemp >= bins)
 +                            {
 +                                inttemp -= bins;
 +                            }
 +                        }
 +
 +                        if (inttemp >= 0 && inttemp < bins)
 +                        {
 +                            window->Histo[i][inttemp] += 1.;
 +                            window->N[i]++;
 +                        }
 +                        window->Ntot[i]++;
 +                    }
 +                }
 +            }
 +        }
 +        if (time > opt->tmax)
 +        {
 +            if (opt->verbose)
 +            {
 +                printf("time %f larger than tmax %f, stop reading pdo file\n", time, opt->tmax);
 +            }
 +            break;
 +        }
 +    }
 +
 +    if (bGetMinMax)
 +    {
 +        *mintmp = minfound;
 +        *maxtmp = maxfound;
 +    }
 +
 +    sfree(lennow);
 +    sfree(tmpbuf);
 +}
 +
 +/*! \brief Set identical weights for all histograms
 + *
 + * Normally, the weight is given by the number data points in each
 + * histogram, together with the autocorrelation time. This can be overwritten
 + * by this routine (not recommended). Since we now support autocorrelations, it is better to set
 + * an appropriate autocorrelation times instead of using this function.
 + */
 +void enforceEqualWeights(t_UmbrellaWindow * window, int nWindows)
 +{
 +    int    i, k, j, NEnforced;
 +    double ratio;
 +
 +    NEnforced = window[0].Ntot[0];
 +    printf("\nFound -hist-eq. Enforcing equal weights for all histograms, \ni.e. doing a "
 +           "non-weighted histogram analysis method. Ndata = %d\n", NEnforced);
 +    /* enforce all histograms to have the same weight as the very first histogram */
 +
 +    for (j = 0; j < nWindows; ++j)
 +    {
 +        for (k = 0; k < window[j].nPull; ++k)
 +        {
 +            ratio = 1.0*NEnforced/window[j].Ntot[k];
 +            for (i = 0; i < window[0].nBin; ++i)
 +            {
 +                window[j].Histo[k][i] *= ratio;
 +            }
 +            window[j].N[k] = static_cast<int>(ratio*window[j].N[k] + 0.5);
 +        }
 +    }
 +}
 +
 +/*! \brief Simple linear interpolation between two given tabulated points
 + */
 +double tabulated_pot(double dist, t_UmbrellaOptions *opt)
 +{
 +    int    jl, ju;
 +    double pl, pu, dz, dp;
 +
 +    jl = static_cast<int> (floor((dist-opt->tabMin)/opt->tabDz));
 +    ju = jl+1;
 +    if (jl < 0 || ju >= opt->tabNbins)
 +    {
 +        gmx_fatal(FARGS, "Distance %f out of bounds of tabulated potential (jl=%d, ju=%d).\n"
 +                  "Provide an extended table.", dist, jl, ju);
 +    }
 +    pl = opt->tabY[jl];
 +    pu = opt->tabY[ju];
 +    dz = dist-opt->tabX[jl];
 +    dp = (pu-pl)*dz/opt->tabDz;
 +    return pl+dp;
 +}
 +
 +
 +/*! \brief
 + * Check which bins substiantially contribute (accelerates WHAM)
 + *
 + * Don't worry, that routine does not mean we compute the PMF in limited precision.
 + * After rapid convergence (using only substiantal contributions), we always switch to
 + * full precision.
 + */
 +void setup_acc_wham(double *profile, t_UmbrellaWindow * window, int nWindows,
 +                    t_UmbrellaOptions *opt)
 +{
 +    int           i, j, k, nGrptot = 0, nContrib = 0, nTot = 0;
 +    double        U, min = opt->min, dz = opt->dz, temp, ztot_half, distance, ztot, contrib1, contrib2;
 +    gmx_bool      bAnyContrib;
 +    static int    bFirst = 1;
 +    static double wham_contrib_lim;
 +
 +    if (bFirst)
 +    {
 +        for (i = 0; i < nWindows; ++i)
 +        {
 +            nGrptot += window[i].nPull;
 +        }
 +        wham_contrib_lim = opt->Tolerance/nGrptot;
 +    }
 +
 +    ztot      = opt->max-opt->min;
 +    ztot_half = ztot/2;
 +
 +    for (i = 0; i < nWindows; ++i)
 +    {
 +        if (!window[i].bContrib)
 +        {
 +            snew(window[i].bContrib, window[i].nPull);
 +        }
 +        for (j = 0; j < window[i].nPull; ++j)
 +        {
 +            if (!window[i].bContrib[j])
 +            {
 +                snew(window[i].bContrib[j], opt->bins);
 +            }
 +            bAnyContrib = FALSE;
 +            for (k = 0; k < opt->bins; ++k)
 +            {
 +                temp     = (1.0*k+0.5)*dz+min;
 +                distance = temp - window[i].pos[j];   /* distance to umbrella center */
 +                if (opt->bCycl)
 +                {                                     /* in cyclic wham:             */
 +                    if (distance > ztot_half)         /*    |distance| < ztot_half   */
 +                    {
 +                        distance -= ztot;
 +                    }
 +                    else if (distance < -ztot_half)
 +                    {
 +                        distance += ztot;
 +                    }
 +                }
 +                /* Note: there are two contributions to bin k in the wham equations:
 +                   i)  N[j]*exp(- U/(8.314e-3*opt->Temperature) + window[i].z[j])
 +                   ii) exp(- U/(8.314e-3*opt->Temperature))
 +                   where U is the umbrella potential
 +                   If any of these number is larger wham_contrib_lim, I set contrib=TRUE
 +                 */
 +
 +                if (!opt->bTab)
 +                {
 +                    U = 0.5*window[i].k[j]*sqr(distance);       /* harmonic potential assumed. */
 +                }
 +                else
 +                {
 +                    U = tabulated_pot(distance, opt);            /* Use tabulated potential     */
 +
 +                }
 +                contrib1                 = profile[k]*exp(-U/(8.314e-3*opt->Temperature));
 +                contrib2                 = window[i].N[j]*exp(-U/(8.314e-3*opt->Temperature) + window[i].z[j]);
 +                window[i].bContrib[j][k] = (contrib1 > wham_contrib_lim || contrib2 > wham_contrib_lim);
 +                bAnyContrib              = (bAnyContrib | window[i].bContrib[j][k]);
 +                if (window[i].bContrib[j][k])
 +                {
 +                    nContrib++;
 +                }
 +                nTot++;
 +            }
 +            /* If this histo is far outside min and max all bContrib may be FALSE,
 +               causing a floating point exception later on. To avoid that, switch
 +               them all to true.*/
 +            if (!bAnyContrib)
 +            {
 +                for (k = 0; k < opt->bins; ++k)
 +                {
 +                    window[i].bContrib[j][k] = TRUE;
 +                }
 +            }
 +        }
 +    }
 +    if (bFirst)
 +    {
 +        printf("Initialized rapid wham stuff (contrib tolerance %g)\n"
 +               "Evaluating only %d of %d expressions.\n\n", wham_contrib_lim, nContrib, nTot);
 +    }
 +
 +    if (opt->verbose)
 +    {
 +        printf("Updated rapid wham stuff. (evaluating only %d of %d contributions)\n",
 +               nContrib, nTot);
 +    }
 +    bFirst = 0;
 +}
 +
 +//! Compute the PMF (one of the two main WHAM routines)
 +void calc_profile(double *profile, t_UmbrellaWindow * window, int nWindows,
 +                  t_UmbrellaOptions *opt, gmx_bool bExact)
 +{
 +    int    i, k, j;
 +    double num, ztot_half, ztot, distance, min = opt->min, dz = opt->dz;
 +    double denom, U = 0, temp = 0, invg;
 +
 +    ztot      = opt->max-opt->min;
 +    ztot_half = ztot/2;
 +
 +    for (i = 0; i < opt->bins; ++i)
 +    {
 +        num = denom = 0.;
 +        for (j = 0; j < nWindows; ++j)
 +        {
 +            for (k = 0; k < window[j].nPull; ++k)
 +            {
 +                invg = 1.0/window[j].g[k] * window[j].bsWeight[k];
 +                temp = (1.0*i+0.5)*dz+min;
 +                num += invg*window[j].Histo[k][i];
 +
 +                if (!(bExact || window[j].bContrib[k][i]))
 +                {
 +                    continue;
 +                }
 +                distance = temp - window[j].pos[k];   /* distance to umbrella center */
 +                if (opt->bCycl)
 +                {                                     /* in cyclic wham:             */
 +                    if (distance > ztot_half)         /*    |distance| < ztot_half   */
 +                    {
 +                        distance -= ztot;
 +                    }
 +                    else if (distance < -ztot_half)
 +                    {
 +                        distance += ztot;
 +                    }
 +                }
 +
 +                if (!opt->bTab)
 +                {
 +                    U = 0.5*window[j].k[k]*sqr(distance);       /* harmonic potential assumed. */
 +                }
 +                else
 +                {
 +                    U = tabulated_pot(distance, opt);            /* Use tabulated potential     */
 +                }
 +                denom += invg*window[j].N[k]*exp(-U/(8.314e-3*opt->Temperature) + window[j].z[k]);
 +            }
 +        }
 +        profile[i] = num/denom;
 +    }
 +}
 +
 +//! Compute the free energy offsets z (one of the two main WHAM routines)
 +double calc_z(double * profile, t_UmbrellaWindow * window, int nWindows,
 +              t_UmbrellaOptions *opt, gmx_bool bExact)
 +{
 +    int    i, j, k;
 +    double U   = 0, min = opt->min, dz = opt->dz, temp, ztot_half, distance, ztot;
 +    double MAX = -1e20, total = 0;
 +
 +    ztot      = opt->max-opt->min;
 +    ztot_half = ztot/2;
 +
 +    for (i = 0; i < nWindows; ++i)
 +    {
 +        for (j = 0; j < window[i].nPull; ++j)
 +        {
 +            total = 0;
 +            for (k = 0; k < window[i].nBin; ++k)
 +            {
 +                if (!(bExact || window[i].bContrib[j][k]))
 +                {
 +                    continue;
 +                }
 +                temp     = (1.0*k+0.5)*dz+min;
 +                distance = temp - window[i].pos[j];   /* distance to umbrella center */
 +                if (opt->bCycl)
 +                {                                     /* in cyclic wham:             */
 +                    if (distance > ztot_half)         /*    |distance| < ztot_half   */
 +                    {
 +                        distance -= ztot;
 +                    }
 +                    else if (distance < -ztot_half)
 +                    {
 +                        distance += ztot;
 +                    }
 +                }
 +
 +                if (!opt->bTab)
 +                {
 +                    U = 0.5*window[i].k[j]*sqr(distance);       /* harmonic potential assumed. */
 +                }
 +                else
 +                {
 +                    U = tabulated_pot(distance, opt);            /* Use tabulated potential     */
 +
 +                }
 +                total += profile[k]*exp(-U/(8.314e-3*opt->Temperature));
 +            }
 +            /* Avoid floating point exception if window is far outside min and max */
 +            if (total != 0.0)
 +            {
 +                total = -log(total);
 +            }
 +            else
 +            {
 +                total = 1000.0;
 +            }
 +            temp = fabs(total - window[i].z[j]);
 +            if (temp > MAX)
 +            {
 +                MAX = temp;
 +            }
 +            window[i].z[j] = total;
 +        }
 +    }
 +    return MAX;
 +}
 +
 +//! Make PMF symmetric around 0 (useful e.g. for membranes)
 +void symmetrizeProfile(double* profile, t_UmbrellaOptions *opt)
 +{
 +    int     i, j, bins = opt->bins;
 +    double *prof2, min = opt->min, max = opt->max, dz = opt->dz, zsym, deltaz, profsym;
 +    double  z, z1;
 +
 +    if (min > 0. || max < 0.)
 +    {
 +        gmx_fatal(FARGS, "Cannot symmetrize profile around z=0 with min=%f and max=%f\n",
 +                  opt->min, opt->max);
 +    }
 +
 +    snew(prof2, bins);
 +
 +    for (i = 0; i < bins; i++)
 +    {
 +        z    = min+(i+0.5)*dz;
 +        zsym = -z;
 +        /* bin left of zsym */
 +        j = static_cast<int> (floor((zsym-min)/dz-0.5));
 +        if (j >= 0 && (j+1) < bins)
 +        {
 +            /* interpolate profile linearly between bins j and j+1 */
 +            z1      = min+(j+0.5)*dz;
 +            deltaz  = zsym-z1;
 +            profsym = profile[j] + (profile[j+1]-profile[j])/dz*deltaz;
 +            /* average between left and right */
 +            prof2[i] = 0.5*(profsym+profile[i]);
 +        }
 +        else
 +        {
 +            prof2[i] = profile[i];
 +        }
 +    }
 +
 +    memcpy(profile, prof2, bins*sizeof(double));
 +    sfree(prof2);
 +}
 +
 +//! Set energy unit (kJ/mol,kT,kCal/mol) and set it to zero at opt->zProf0
 +void prof_normalization_and_unit(double * profile, t_UmbrellaOptions *opt)
 +{
 +    int    i, bins, imin;
 +    double unit_factor = 1., R_MolarGasConst, diff;
 +
 +    R_MolarGasConst = 8.314472e-3; /* in kJ/(mol*K) */
 +    bins            = opt->bins;
 +
 +    /* Not log? Nothing to do! */
 +    if (!opt->bLog)
 +    {
 +        return;
 +    }
 +
 +    /* Get profile in units of kT, kJ/mol, or kCal/mol */
 +    if (opt->unit == en_kT)
 +    {
 +        unit_factor = 1.0;
 +    }
 +    else if (opt->unit == en_kJ)
 +    {
 +        unit_factor = R_MolarGasConst*opt->Temperature;
 +    }
 +    else if (opt->unit == en_kCal)
 +    {
 +        unit_factor = R_MolarGasConst*opt->Temperature/4.1868;
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "Sorry, I don't know this energy unit.");
 +    }
 +
 +    for (i = 0; i < bins; i++)
 +    {
 +        if (profile[i] > 0.0)
 +        {
 +            profile[i] = -log(profile[i])*unit_factor;
 +        }
 +    }
 +
 +    /* shift to zero at z=opt->zProf0 */
 +    if (!opt->bProf0Set)
 +    {
 +        diff = profile[0];
 +    }
 +    else
 +    {
 +        /* Get bin with shortest distance to opt->zProf0
 +           (-0.5 from bin position and +0.5 from rounding cancel) */
 +        imin = static_cast<int>((opt->zProf0-opt->min)/opt->dz);
 +        if (imin < 0)
 +        {
 +            imin = 0;
 +        }
 +        else if (imin >= bins)
 +        {
 +            imin = bins-1;
 +        }
 +        diff = profile[imin];
 +    }
 +
 +    /* Shift to zero */
 +    for (i = 0; i < bins; i++)
 +    {
 +        profile[i] -= diff;
 +    }
 +}
 +
 +//! Make an array of random integers (used for bootstrapping)
 +void getRandomIntArray(int nPull, int blockLength, int* randomArray, gmx_rng_t rng)
 +{
 +    int ipull, blockBase, nr, ipullRandom;
 +
 +    if (blockLength == 0)
 +    {
 +        blockLength = nPull;
 +    }
 +
 +    for (ipull = 0; ipull < nPull; ipull++)
 +    {
 +        blockBase = (ipull/blockLength)*blockLength;
 +        do
 +        {      /* make sure nothing bad happens in the last block */
 +            nr          = static_cast<int>(gmx_rng_uniform_real(rng)*blockLength);
 +            ipullRandom = blockBase + nr;
 +        }
 +        while (ipullRandom >= nPull);
 +        if (ipullRandom < 0 || ipullRandom >= nPull)
 +        {
 +            gmx_fatal(FARGS, "Ups, random iWin = %d, nPull = %d, nr = %d, "
 +                      "blockLength = %d, blockBase = %d\n",
 +                      ipullRandom, nPull, nr, blockLength, blockBase);
 +        }
 +        randomArray[ipull] = ipullRandom;
 +    }
 +    /*for (ipull=0; ipull<nPull; ipull++)
 +       printf("%d ",randomArray[ipull]); printf("\n"); */
 +}
 +
 +/*! \brief Set pull group information of a synthetic histogram
 + *
 + * This is used when bootstapping new trajectories and thereby create new histogtrams,
 + * but it is not required if we bootstrap complete histograms.
 + */
 +void copy_pullgrp_to_synthwindow(t_UmbrellaWindow *synthWindow,
 +                                 t_UmbrellaWindow *thisWindow, int pullid)
 +{
 +    synthWindow->N       [0] = thisWindow->N        [pullid];
 +    synthWindow->Histo   [0] = thisWindow->Histo    [pullid];
 +    synthWindow->pos     [0] = thisWindow->pos      [pullid];
 +    synthWindow->z       [0] = thisWindow->z        [pullid];
 +    synthWindow->k       [0] = thisWindow->k        [pullid];
 +    synthWindow->bContrib[0] = thisWindow->bContrib [pullid];
 +    synthWindow->g       [0] = thisWindow->g        [pullid];
 +    synthWindow->bsWeight[0] = thisWindow->bsWeight [pullid];
 +}
 +
 +/*! \brief Calculate cumulative distribution function of of all histograms.
 + *
 + * This allow to create random number sequences
 + * which are distributed according to the histograms. Required to generate
 + * the "synthetic" histograms for the Bootstrap method
 + */
 +void calc_cumulatives(t_UmbrellaWindow *window, int nWindows,
 +                      t_UmbrellaOptions *opt, const char *fnhist)
 +{
 +    int    i, j, k, nbin;
 +    double last;
 +    char  *fn = 0, *buf = 0;
 +    FILE  *fp = 0;
 +
 +    if (opt->bs_verbose)
 +    {
 +        snew(fn, strlen(fnhist)+10);
 +        snew(buf, strlen(fnhist)+10);
 +        sprintf(fn, "%s_cumul.xvg", strncpy(buf, fnhist, strlen(fnhist)-4));
 +        fp = xvgropen(fn, "CDFs of umbrella windows", "z", "CDF", opt->oenv);
 +    }
 +
 +    nbin = opt->bins;
 +    for (i = 0; i < nWindows; i++)
 +    {
 +        snew(window[i].cum, window[i].nPull);
 +        for (j = 0; j < window[i].nPull; j++)
 +        {
 +            snew(window[i].cum[j], nbin+1);
 +            window[i].cum[j][0] = 0.;
 +            for (k = 1; k <= nbin; k++)
 +            {
 +                window[i].cum[j][k] = window[i].cum[j][k-1]+window[i].Histo[j][k-1];
 +            }
 +
 +            /* normalize CDFs. Ensure cum[nbin]==1 */
 +            last = window[i].cum[j][nbin];
 +            for (k = 0; k <= nbin; k++)
 +            {
 +                window[i].cum[j][k] /= last;
 +            }
 +        }
 +    }
 +
 +    printf("Cumulative distriubtion functions of all histograms created.\n");
 +    if (opt->bs_verbose)
 +    {
 +        for (k = 0; k <= nbin; k++)
 +        {
 +            fprintf(fp, "%g\t", opt->min+k*opt->dz);
 +            for (i = 0; i < nWindows; i++)
 +            {
 +                for (j = 0; j < window[i].nPull; j++)
 +                {
 +                    fprintf(fp, "%g\t", window[i].cum[j][k]);
 +                }
 +            }
 +            fprintf(fp, "\n");
 +        }
 +        printf("Wrote cumulative distribution functions to %s\n", fn);
 +        gmx_ffclose(fp);
 +        sfree(fn);
 +        sfree(buf);
 +    }
 +}
 +
 +
 +/*! \brief Return j such that xx[j] <= x < xx[j+1]
 + *
 + *  This is used to generate a random sequence distributed according to a histogram
 + */
 +void searchCumulative(double xx[], int n, double x, int *j)
 +{
 +    int ju, jm, jl;
 +
 +    jl = -1;
 +    ju = n;
 +    while (ju-jl > 1)
 +    {
 +        jm = (ju+jl) >> 1;
 +        if (x >= xx[jm])
 +        {
 +            jl = jm;
 +        }
 +        else
 +        {
 +            ju = jm;
 +        }
 +    }
 +    if (x == xx[0])
 +    {
 +        *j = 0;
 +    }
 +    else if (x == xx[n-1])
 +    {
 +        *j = n-2;
 +    }
 +    else
 +    {
 +        *j = jl;
 +    }
 +}
 +
 +//! Bootstrap new trajectories and thereby generate new (bootstrapped) histograms
 +void create_synthetic_histo(t_UmbrellaWindow *synthWindow, t_UmbrellaWindow *thisWindow,
 +                            int pullid, t_UmbrellaOptions *opt)
 +{
 +    int    N, i, nbins, r_index, ibin;
 +    double r, tausteps = 0.0, a, ap, dt, x, invsqrt2, g, y, sig = 0., z, mu = 0.;
 +    char   errstr[1024];
 +
 +    N     = thisWindow->N[pullid];
 +    dt    = thisWindow->dt;
 +    nbins = thisWindow->nBin;
 +
 +    /* tau = autocorrelation time */
 +    if (opt->tauBootStrap > 0.0)
 +    {
 +        tausteps = opt->tauBootStrap/dt;
 +    }
 +    else if (opt->bTauIntGiven || opt->bCalcTauInt)
 +    {
 +        /* calc tausteps from g=1+2tausteps */
 +        g        = thisWindow->g[pullid];
 +        tausteps = (g-1)/2;
 +    }
 +    else
 +    {
 +        sprintf(errstr,
 +                "When generating hypothetical trajctories from given umbrella histograms,\n"
 +                "autocorrelation times (ACTs) are required. Otherwise the statistical error\n"
 +                "cannot be predicted. You have 3 options:\n"
 +                "1) Make g_wham estimate the ACTs (options -ac and -acsig).\n"
 +                "2) Calculate the ACTs by yourself (e.g. with g_analyze) and provide them\n");
 +        strcat(errstr,
 +               "   with option -iiact for all umbrella windows.\n"
 +               "3) If all ACTs are identical and know, you can define them with -bs-tau.\n"
 +               "   Use option (3) only if you are sure what you're doing, you may severely\n"
 +               "   underestimate the error if a too small ACT is given.\n");
 +        gmx_fatal(FARGS, errstr);
 +    }
 +
 +    synthWindow->N       [0] = N;
 +    synthWindow->pos     [0] = thisWindow->pos[pullid];
 +    synthWindow->z       [0] = thisWindow->z[pullid];
 +    synthWindow->k       [0] = thisWindow->k[pullid];
 +    synthWindow->bContrib[0] = thisWindow->bContrib[pullid];
 +    synthWindow->g       [0] = thisWindow->g       [pullid];
 +    synthWindow->bsWeight[0] = thisWindow->bsWeight[pullid];
 +
 +    for (i = 0; i < nbins; i++)
 +    {
 +        synthWindow->Histo[0][i] = 0.;
 +    }
 +
 +    if (opt->bsMethod == bsMethod_trajGauss)
 +    {
 +        sig = thisWindow->sigma [pullid];
 +        mu  = thisWindow->aver  [pullid];
 +    }
 +
 +    /* Genrate autocorrelated Gaussian random variable with autocorrelation time tau
 +       Use the following:
 +       If x and y are random numbers from N(0,1) (Gaussian with average 0 and sigma=1),
 +       then
 +       z = a*x + sqrt(1-a^2)*y
 +       is also from N(0,1), and cov(z,x) = a. Thus, by gerenating a sequence
 +       x' = a*x + sqrt(1-a^2)*y, the sequnce x(t) is from N(0,1) and has an autocorrelation
 +       function
 +       C(t) = exp(-t/tau) with tau=-1/ln(a)
 +
 +       Then, use error function to turn the Gaussian random variable into a uniformly
 +       distributed one in [0,1]. Eventually, use cumulative distribution function of
 +       histogram to get random variables distributed according to histogram.
 +       Note: The ACT of the flat distribution and of the generated histogram is not
 +       100% exactly tau, but near tau (my test was 3.8 instead of 4).
 +     */
 +    a        = exp(-1.0/tausteps);
 +    ap       = sqrt(1-a*a);
 +    invsqrt2 = 1./sqrt(2.0);
 +
 +    /* init random sequence */
 +    x = gmx_rng_gaussian_table(opt->rng);
 +
 +    if (opt->bsMethod == bsMethod_traj)
 +    {
 +        /* bootstrap points from the umbrella histograms */
 +        for (i = 0; i < N; i++)
 +        {
 +            y = gmx_rng_gaussian_table(opt->rng);
 +            x = a*x+ap*y;
 +            /* get flat distribution in [0,1] using cumulative distribution function of Gauusian
 +               Note: CDF(Gaussian) = 0.5*{1+erf[x/sqrt(2)]}
 +             */
 +            r = 0.5*(1+gmx_erf(x*invsqrt2));
 +            searchCumulative(thisWindow->cum[pullid], nbins+1, r, &r_index);
 +            synthWindow->Histo[0][r_index] += 1.;
 +        }
 +    }
 +    else if (opt->bsMethod == bsMethod_trajGauss)
 +    {
 +        /* bootstrap points from a Gaussian with the same average and sigma
 +           as the respective umbrella histogram. The idea was, that -given
 +           limited sampling- the bootstrapped histograms are otherwise biased
 +           from the limited sampling of the US histos. However, bootstrapping from
 +           the Gaussian seems to yield a similar estimate. */
 +        i = 0;
 +        while (i < N)
 +        {
 +            y    = gmx_rng_gaussian_table(opt->rng);
 +            x    = a*x+ap*y;
 +            z    = x*sig+mu;
 +            ibin = static_cast<int> (floor((z-opt->min)/opt->dz));
 +            if (opt->bCycl)
 +            {
 +                if (ibin < 0)
 +                {
 +                    while ( (ibin += nbins) < 0)
 +                    {
 +                        ;
 +                    }
 +                }
 +                else if (ibin >= nbins)
 +                {
 +                    while ( (ibin -= nbins) >= nbins)
 +                    {
 +                        ;
 +                    }
 +                }
 +            }
 +
 +            if (ibin >= 0 && ibin < nbins)
 +            {
 +                synthWindow->Histo[0][ibin] += 1.;
 +                i++;
 +            }
 +        }
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "Unknown bsMethod (id %d). That should not happen.\n", opt->bsMethod);
 +    }
 +}
 +
 +/*! \brief Write all histograms to a file
 + *
 + * If bs_index>=0, a number is added to the output file name to allow the ouput of all
 + * sets of bootstrapped histograms.
 + */
 +void print_histograms(const char *fnhist, t_UmbrellaWindow * window, int nWindows,
 +                      int bs_index, t_UmbrellaOptions *opt)
 +{
 +    char *fn = 0, *buf = 0, title[256];
 +    FILE *fp;
 +    int   bins, l, i, j;
 +
 +    if (bs_index >= 0)
 +    {
 +        snew(fn, strlen(fnhist)+10);
 +        snew(buf, strlen(fnhist)+1);
 +        sprintf(fn, "%s_bs%d.xvg", strncpy(buf, fnhist, strlen(fnhist)-4), bs_index);
 +        sprintf(title, "Umbrella histograms. Bootstrap #%d", bs_index);
 +    }
 +    else
 +    {
 +        fn = strdup(fnhist);
 +        strcpy(title, "Umbrella histograms");
 +    }
 +
 +    fp   = xvgropen(fn, title, "z", "count", opt->oenv);
 +    bins = opt->bins;
 +
 +    /* Write histograms */
 +    for (l = 0; l < bins; ++l)
 +    {
 +        fprintf(fp, "%e\t", (double)(l+0.5)*opt->dz+opt->min);
 +        for (i = 0; i < nWindows; ++i)
 +        {
 +            for (j = 0; j < window[i].nPull; ++j)
 +            {
 +                fprintf(fp, "%e\t", window[i].Histo[j][l]);
 +            }
 +        }
 +        fprintf(fp, "\n");
 +    }
 +
 +    gmx_ffclose(fp);
 +    printf("Wrote %s\n", fn);
 +    if (bs_index >= 0)
 +    {
 +        sfree(buf);
 +    }
 +    sfree(fn);
 +}
 +
 +//! Used for qsort to sort random numbers
 +int func_wham_is_larger(const void *a, const void *b)
 +{
 +    double *aa, *bb;
 +    aa = (double*)a;
 +    bb = (double*)b;
 +    if (*aa < *bb)
 +    {
 +        return -1;
 +    }
 +    else if (*aa > *bb)
 +    {
 +        return 1;
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +//! Make random weights for histograms for the Bayesian bootstrap of complete histograms)
 +void setRandomBsWeights(t_UmbrellaWindow *synthwin, int nAllPull, t_UmbrellaOptions *opt)
 +{
 +    int     i;
 +    double *r;
 +
 +    snew(r, nAllPull);
 +
 +    /* generate ordered random numbers between 0 and nAllPull  */
 +    for (i = 0; i < nAllPull-1; i++)
 +    {
 +        r[i] = gmx_rng_uniform_real(opt->rng) * nAllPull;
 +    }
 +    qsort((void *)r, nAllPull-1, sizeof(double), &func_wham_is_larger);
 +    r[nAllPull-1] = 1.0*nAllPull;
 +
 +    synthwin[0].bsWeight[0] = r[0];
 +    for (i = 1; i < nAllPull; i++)
 +    {
 +        synthwin[i].bsWeight[0] = r[i]-r[i-1];
 +    }
 +
 +    /* avoid to have zero weight by adding a tiny value */
 +    for (i = 0; i < nAllPull; i++)
 +    {
 +        if (synthwin[i].bsWeight[0] < 1e-5)
 +        {
 +            synthwin[i].bsWeight[0] = 1e-5;
 +        }
 +    }
 +
 +    sfree(r);
 +}
 +
 +//! The main bootstrapping routine
 +void do_bootstrapping(const char *fnres, const char* fnprof, const char *fnhist,
 +                      char* ylabel, double *profile,
 +                      t_UmbrellaWindow * window, int nWindows, t_UmbrellaOptions *opt)
 +{
 +    t_UmbrellaWindow * synthWindow;
 +    double            *bsProfile, *bsProfiles_av, *bsProfiles_av2, maxchange = 1e20, tmp, stddev;
 +    int                i, j, *randomArray = 0, winid, pullid, ib;
 +    int                iAllPull, nAllPull, *allPull_winId, *allPull_pullId;
 +    FILE              *fp;
 +    gmx_bool           bExact = FALSE;
 +
 +    /* init random generator */
 +    if (opt->bsSeed == -1)
 +    {
 +        opt->rng = gmx_rng_init(gmx_rng_make_seed());
 +    }
 +    else
 +    {
 +        opt->rng = gmx_rng_init(opt->bsSeed);
 +    }
 +
 +    snew(bsProfile,     opt->bins);
 +    snew(bsProfiles_av, opt->bins);
 +    snew(bsProfiles_av2, opt->bins);
 +
 +    /* Create array of all pull groups. Note that different windows
 +       may have different nr of pull groups
 +       First: Get total nr of pull groups */
 +    nAllPull = 0;
 +    for (i = 0; i < nWindows; i++)
 +    {
 +        nAllPull += window[i].nPull;
 +    }
 +    snew(allPull_winId, nAllPull);
 +    snew(allPull_pullId, nAllPull);
 +    iAllPull = 0;
 +    /* Setup one array of all pull groups */
 +    for (i = 0; i < nWindows; i++)
 +    {
 +        for (j = 0; j < window[i].nPull; j++)
 +        {
 +            allPull_winId[iAllPull]  = i;
 +            allPull_pullId[iAllPull] = j;
 +            iAllPull++;
 +        }
 +    }
 +
 +    /* setup stuff for synthetic windows */
 +    snew(synthWindow, nAllPull);
 +    for (i = 0; i < nAllPull; i++)
 +    {
 +        synthWindow[i].nPull = 1;
 +        synthWindow[i].nBin  = opt->bins;
 +        snew(synthWindow[i].Histo, 1);
 +        if (opt->bsMethod == bsMethod_traj || opt->bsMethod == bsMethod_trajGauss)
 +        {
 +            snew(synthWindow[i].Histo[0], opt->bins);
 +        }
 +        snew(synthWindow[i].N, 1);
 +        snew(synthWindow[i].pos, 1);
 +        snew(synthWindow[i].z, 1);
 +        snew(synthWindow[i].k, 1);
 +        snew(synthWindow[i].bContrib, 1);
 +        snew(synthWindow[i].g, 1);
 +        snew(synthWindow[i].bsWeight, 1);
 +    }
 +
 +    switch (opt->bsMethod)
 +    {
 +        case bsMethod_hist:
 +            snew(randomArray, nAllPull);
 +            printf("\n\nWhen computing statistical errors by bootstrapping entire histograms:\n");
 +            please_cite(stdout, "Hub2006");
 +            break;
 +        case bsMethod_BayesianHist:
 +            /* just copy all histogams into synthWindow array */
 +            for (i = 0; i < nAllPull; i++)
 +            {
 +                winid  = allPull_winId [i];
 +                pullid = allPull_pullId[i];
 +                copy_pullgrp_to_synthwindow(synthWindow+i, window+winid, pullid);
 +            }
 +            break;
 +        case bsMethod_traj:
 +        case bsMethod_trajGauss:
 +            calc_cumulatives(window, nWindows, opt, fnhist);
 +            break;
 +        default:
 +            gmx_fatal(FARGS, "Unknown bootstrap method. That should not have happened.\n");
 +    }
 +
 +    /* do bootstrapping */
 +    fp = xvgropen(fnprof, "Boot strap profiles", "z", ylabel, opt->oenv);
 +    for (ib = 0; ib < opt->nBootStrap; ib++)
 +    {
 +        printf("  *******************************************\n"
 +               "  ******** Start bootstrap nr %d ************\n"
 +               "  *******************************************\n", ib+1);
 +
 +        switch (opt->bsMethod)
 +        {
 +            case bsMethod_hist:
 +                /* bootstrap complete histograms from given histograms */
 +                getRandomIntArray(nAllPull, opt->histBootStrapBlockLength, randomArray, opt->rng);
 +                for (i = 0; i < nAllPull; i++)
 +                {
 +                    winid  = allPull_winId [randomArray[i]];
 +                    pullid = allPull_pullId[randomArray[i]];
 +                    copy_pullgrp_to_synthwindow(synthWindow+i, window+winid, pullid);
 +                }
 +                break;
 +            case bsMethod_BayesianHist:
 +                /* keep histos, but assign random weights ("Bayesian bootstrap") */
 +                setRandomBsWeights(synthWindow, nAllPull, opt);
 +                break;
 +            case bsMethod_traj:
 +            case bsMethod_trajGauss:
 +                /* create new histos from given histos, that is generate new hypothetical
 +                   trajectories */
 +                for (i = 0; i < nAllPull; i++)
 +                {
 +                    winid  = allPull_winId[i];
 +                    pullid = allPull_pullId[i];
 +                    create_synthetic_histo(synthWindow+i, window+winid, pullid, opt);
 +                }
 +                break;
 +        }
 +
 +        /* write histos in case of verbose output */
 +        if (opt->bs_verbose)
 +        {
 +            print_histograms(fnhist, synthWindow, nAllPull, ib, opt);
 +        }
 +
 +        /* do wham */
 +        i         = 0;
 +        bExact    = FALSE;
 +        maxchange = 1e20;
 +        memcpy(bsProfile, profile, opt->bins*sizeof(double)); /* use profile as guess */
 +        do
 +        {
 +            if ( (i%opt->stepUpdateContrib) == 0)
 +            {
 +                setup_acc_wham(bsProfile, synthWindow, nAllPull, opt);
 +            }
 +            if (maxchange < opt->Tolerance)
 +            {
 +                bExact = TRUE;
 +            }
 +            if (((i%opt->stepchange) == 0 || i == 1) && i != 0)
 +            {
 +                printf("\t%4d) Maximum change %e\n", i, maxchange);
 +            }
 +            calc_profile(bsProfile, synthWindow, nAllPull, opt, bExact);
 +            i++;
 +        }
 +        while ( (maxchange = calc_z(bsProfile, synthWindow, nAllPull, opt, bExact)) > opt->Tolerance || !bExact);
 +        printf("\tConverged in %d iterations. Final maximum change %g\n", i, maxchange);
 +
 +        if (opt->bLog)
 +        {
 +            prof_normalization_and_unit(bsProfile, opt);
 +        }
 +
 +        /* symmetrize profile around z=0 */
 +        if (opt->bSym)
 +        {
 +            symmetrizeProfile(bsProfile, opt);
 +        }
 +
 +        /* save stuff to get average and stddev */
 +        for (i = 0; i < opt->bins; i++)
 +        {
 +            tmp                = bsProfile[i];
 +            bsProfiles_av[i]  += tmp;
 +            bsProfiles_av2[i] += tmp*tmp;
 +            fprintf(fp, "%e\t%e\n", (i+0.5)*opt->dz+opt->min, tmp);
 +        }
-     fprintf(fp, "@TYPE xydy\n");
++        fprintf(fp, "%s\n", output_env_get_print_xvgr_codes(opt->oenv) ? "&" : "");
 +    }
 +    gmx_ffclose(fp);
 +
 +    /* write average and stddev */
 +    fp = xvgropen(fnres, "Average and stddev from bootstrapping", "z", ylabel, opt->oenv);
-                 fprintf(fpcorr, "&\n");
++    if (output_env_get_print_xvgr_codes(opt->oenv))
++    {
++        fprintf(fp, "@TYPE xydy\n");
++    }
 +    for (i = 0; i < opt->bins; i++)
 +    {
 +        bsProfiles_av [i] /= opt->nBootStrap;
 +        bsProfiles_av2[i] /= opt->nBootStrap;
 +        tmp                = bsProfiles_av2[i]-sqr(bsProfiles_av[i]);
 +        stddev             = (tmp >= 0.) ? sqrt(tmp) : 0.; /* Catch rouding errors */
 +        fprintf(fp, "%e\t%e\t%e\n", (i+0.5)*opt->dz+opt->min, bsProfiles_av [i], stddev);
 +    }
 +    gmx_ffclose(fp);
 +    printf("Wrote boot strap result to %s\n", fnres);
 +}
 +
 +//! Return type of input file based on file extension (xvg, pdo, or tpr)
 +int whaminFileType(char *fn)
 +{
 +    int len;
 +    len = strlen(fn);
 +    if (strcmp(fn+len-3, "tpr") == 0)
 +    {
 +        return whamin_tpr;
 +    }
 +    else if (strcmp(fn+len-3, "xvg") == 0 || strcmp(fn+len-6, "xvg.gz") == 0)
 +    {
 +        return whamin_pullxf;
 +    }
 +    else if (strcmp(fn+len-3, "pdo") == 0 || strcmp(fn+len-6, "pdo.gz") == 0)
 +    {
 +        return whamin_pdo;
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "Unknown file type of %s. Should be tpr, xvg, or pdo.\n", fn);
 +    }
 +    return whamin_unknown;
 +}
 +
 +//! Read the files names in pdo-files.dat, pullf/x-files.dat, tpr-files.dat
 +void read_wham_in(const char *fn, char ***filenamesRet, int *nfilesRet,
 +                  t_UmbrellaOptions *opt)
 +{
 +    char **filename = 0, tmp[WHAM_MAXFILELEN+2];
 +    int    nread, sizenow, i, block = 1;
 +    FILE  *fp;
 +
 +    fp      = gmx_ffopen(fn, "r");
 +    nread   = 0;
 +    sizenow = 0;
 +    while (fgets(tmp, sizeof(tmp), fp) != NULL)
 +    {
 +        if (strlen(tmp) >= WHAM_MAXFILELEN)
 +        {
 +            gmx_fatal(FARGS, "Filename too long in %s. Only %d characters allowed.\n", fn, WHAM_MAXFILELEN);
 +        }
 +        if (nread >= sizenow)
 +        {
 +            sizenow += block;
 +            srenew(filename, sizenow);
 +            for (i = sizenow-block; i < sizenow; i++)
 +            {
 +                snew(filename[i], WHAM_MAXFILELEN);
 +            }
 +        }
 +        /* remove newline if there is one */
 +        if (tmp[strlen(tmp)-1] == '\n')
 +        {
 +            tmp[strlen(tmp)-1] = '\0';
 +        }
 +        strcpy(filename[nread], tmp);
 +        if (opt->verbose)
 +        {
 +            printf("Found file %s in %s\n", filename[nread], fn);
 +        }
 +        nread++;
 +    }
 +    *filenamesRet = filename;
 +    *nfilesRet    = nread;
 +}
 +
 +//! Open a file or a pipe to a gzipped file
 +FILE *open_pdo_pipe(const char *fn, t_UmbrellaOptions *opt, gmx_bool *bPipeOpen)
 +{
 +    char            Buffer[1024], gunzip[1024], *Path = 0;
 +    FILE           *pipe   = 0;
 +    static gmx_bool bFirst = 1;
 +
 +    /* gzipped pdo file? */
 +    if ((strcmp(fn+strlen(fn)-3, ".gz") == 0))
 +    {
 +        /* search gunzip executable */
 +        if (!(Path = getenv("GMX_PATH_GZIP")))
 +        {
 +            if (gmx_fexist("/bin/gunzip"))
 +            {
 +                sprintf(gunzip, "%s", "/bin/gunzip");
 +            }
 +            else if (gmx_fexist("/usr/bin/gunzip"))
 +            {
 +                sprintf(gunzip, "%s", "/usr/bin/gunzip");
 +            }
 +            else
 +            {
 +                gmx_fatal(FARGS, "Cannot find executable gunzip in /bin or /usr/bin.\n"
 +                          "You may want to define the path to gunzip "
 +                          "with the environment variable GMX_PATH_GZIP.", gunzip);
 +            }
 +        }
 +        else
 +        {
 +            sprintf(gunzip, "%s/gunzip", Path);
 +            if (!gmx_fexist(gunzip))
 +            {
 +                gmx_fatal(FARGS, "Cannot find executable %s. Please define the path to gunzip"
 +                          " in the environmental varialbe GMX_PATH_GZIP.", gunzip);
 +            }
 +        }
 +        if (bFirst)
 +        {
 +            printf("Using gunzig executable %s\n", gunzip);
 +            bFirst = 0;
 +        }
 +        if (!gmx_fexist(fn))
 +        {
 +            gmx_fatal(FARGS, "File %s does not exist.\n", fn);
 +        }
 +        sprintf(Buffer, "%s -c < %s", gunzip, fn);
 +        if (opt->verbose)
 +        {
 +            printf("Executing command '%s'\n", Buffer);
 +        }
 +#ifdef HAVE_PIPES
 +        if ((pipe = popen(Buffer, "r")) == NULL)
 +        {
 +            gmx_fatal(FARGS, "Unable to open pipe to `%s'\n", Buffer);
 +        }
 +#else
 +        gmx_fatal(FARGS, "Cannot open a compressed file on platform without pipe support");
 +#endif
 +        *bPipeOpen = TRUE;
 +    }
 +    else
 +    {
 +        pipe       = gmx_ffopen(fn, "r");
 +        *bPipeOpen = FALSE;
 +    }
 +
 +    return pipe;
 +}
 +
 +//! Close file or pipe
 +void pdo_close_file(FILE *fp)
 +{
 +#ifdef HAVE_PIPES
 +    pclose(fp);
 +#else
 +    gmx_ffclose(fp);
 +#endif
 +}
 +
 +//! Reading all pdo files
 +void read_pdo_files(char **fn, int nfiles, t_UmbrellaHeader* header,
 +                    t_UmbrellaWindow *window, t_UmbrellaOptions *opt)
 +{
 +    FILE    *file;
 +    real     mintmp, maxtmp, done = 0.;
 +    int      i;
 +    gmx_bool bPipeOpen;
 +    /* char Buffer0[1000]; */
 +
 +    if (nfiles < 1)
 +    {
 +        gmx_fatal(FARGS, "No files found. Hick.");
 +    }
 +
 +    /* if min and max are not given, get min and max from the input files */
 +    if (opt->bAuto)
 +    {
 +        printf("Automatic determination of boundaries from %d pdo files...\n", nfiles);
 +        opt->min = 1e20;
 +        opt->max = -1e20;
 +        for (i = 0; i < nfiles; ++i)
 +        {
 +            file = open_pdo_pipe(fn[i], opt, &bPipeOpen);
 +            /*fgets(Buffer0,999,file);
 +               fprintf(stderr,"First line '%s'\n",Buffer0); */
 +            done = 100.0*(i+1)/nfiles;
 +            printf("\rOpening %s ... [%2.0f%%]", fn[i], done); fflush(stdout);
 +            if (opt->verbose)
 +            {
 +                printf("\n");
 +            }
 +            read_pdo_header(file, header, opt);
 +            /* here only determine min and max of this window */
 +            read_pdo_data(file, header, i, NULL, opt, TRUE, &mintmp, &maxtmp);
 +            if (maxtmp > opt->max)
 +            {
 +                opt->max = maxtmp;
 +            }
 +            if (mintmp < opt->min)
 +            {
 +                opt->min = mintmp;
 +            }
 +            if (bPipeOpen)
 +            {
 +                pdo_close_file(file);
 +            }
 +            else
 +            {
 +                gmx_ffclose(file);
 +            }
 +        }
 +        printf("\n");
 +        printf("\nDetermined boundaries to %f and %f\n\n", opt->min, opt->max);
 +        if (opt->bBoundsOnly)
 +        {
 +            printf("Found option -boundsonly, now exiting.\n");
 +            exit (0);
 +        }
 +    }
 +    /* store stepsize in profile */
 +    opt->dz = (opt->max-opt->min)/opt->bins;
 +
 +    /* Having min and max, we read in all files */
 +    /* Loop over all files */
 +    for (i = 0; i < nfiles; ++i)
 +    {
 +        done = 100.0*(i+1)/nfiles;
 +        printf("\rOpening %s ... [%2.0f%%]", fn[i], done); fflush(stdout);
 +        if (opt->verbose)
 +        {
 +            printf("\n");
 +        }
 +        file = open_pdo_pipe(fn[i], opt, &bPipeOpen);
 +        read_pdo_header(file, header, opt);
 +        /* load data into window */
 +        read_pdo_data(file, header, i, window, opt, FALSE, NULL, NULL);
 +        if ((window+i)->Ntot[0] == 0)
 +        {
 +            fprintf(stderr, "\nWARNING, no data points read from file %s (check -b option)\n", fn[i]);
 +        }
 +        if (bPipeOpen)
 +        {
 +            pdo_close_file(file);
 +        }
 +        else
 +        {
 +            gmx_ffclose(file);
 +        }
 +    }
 +    printf("\n");
 +    for (i = 0; i < nfiles; ++i)
 +    {
 +        sfree(fn[i]);
 +    }
 +    sfree(fn);
 +}
 +
 +//! translate 0/1 to N/Y to write pull dimensions
 +#define int2YN(a) (((a) == 0) ? ("N") : ("Y"))
 +
 +//! Read pull groups from a tpr file (including position, force const, geometry, number of groups)
 +void read_tpr_header(const char *fn, t_UmbrellaHeader* header, t_UmbrellaOptions *opt)
 +{
 +    t_inputrec  ir;
 +    int         i, ncrd;
 +    t_state     state;
 +    static int  first = 1;
 +
 +    /* printf("Reading %s \n",fn); */
 +    read_tpx_state(fn, &ir, &state, NULL, NULL);
 +
 +    if (ir.ePull != epullUMBRELLA)
 +    {
 +        gmx_fatal(FARGS, "This is not a tpr of an umbrella simulation. Found pull type \"%s\" "
 +                  " (ir.ePull = %d)\n", epull_names[ir.ePull], ir.ePull);
 +    }
 +
 +    /* nr of pull groups */
 +    ncrd = ir.pull->ncoord;
 +    if (ncrd < 1)
 +    {
 +        gmx_fatal(FARGS, "This is not a tpr of umbrella simulation. Found only %d pull coordinates\n", ncrd);
 +    }
 +
 +    header->npullcrds     = ir.pull->ncoord;
 +    header->pull_geometry = ir.pull->eGeom;
 +    header->bPrintRef     = ir.pull->bPrintRef;
 +    copy_ivec(ir.pull->dim, header->pull_dim);
 +    header->pull_ndim = header->pull_dim[0]+header->pull_dim[1]+header->pull_dim[2];
 +    snew(header->k, ncrd);
 +    snew(header->init_dist, ncrd);
 +    snew(header->umbInitDist, ncrd);
 +
 +    /* only z-direction with epullgCYL? */
 +    if (header->pull_geometry == epullgCYL)
 +    {
 +        if (header->pull_dim[XX] || header->pull_dim[YY] || (!header->pull_dim[ZZ]))
 +        {
 +            gmx_fatal(FARGS, "With pull geometry 'cylinder', expected pulling in Z direction only.\n"
 +                      "However, found dimensions [%s %s %s]\n",
 +                      int2YN(header->pull_dim[XX]), int2YN(header->pull_dim[YY]),
 +                      int2YN(header->pull_dim[ZZ]));
 +        }
 +    }
 +
 +    for (i = 0; i < ncrd; i++)
 +    {
 +        header->k[i] = ir.pull->coord[i].k;
 +        if (header->k[i] == 0.0)
 +        {
 +            gmx_fatal(FARGS, "Pull coordinate %d has force constant of of 0.0 in %s.\n"
 +                      "That doesn't seem to be an Umbrella tpr.\n",
 +                      i, fn);
 +        }
 +        header->init_dist[i] =  ir.pull->coord[i].init;
 +
 +        /* initial distance to reference */
 +        switch (header->pull_geometry)
 +        {
 +            case epullgCYL:
 +            /* umbrella distance stored in init_dist[i] for geometry cylinder (not in ...[i][ZZ]) */
 +            case epullgDIST:
 +            case epullgDIR:
 +            case epullgDIRPBC:
 +                header->umbInitDist[i] = header->init_dist[i];
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "Pull geometry %s not supported\n", epullg_names[header->pull_geometry]);
 +        }
 +    }
 +
 +    if (opt->verbose || first)
 +    {
 +        printf("File %s, %d coordinates, geometry \"%s\", dimensions [%s %s %s], (%d dimensions)\n"
 +               "\tPull group coordinates%s expected in pullx files.\n",
 +               fn, header->npullcrds, epullg_names[header->pull_geometry],
 +               int2YN(header->pull_dim[0]), int2YN(header->pull_dim[1]), int2YN(header->pull_dim[2]),
 +               header->pull_ndim, (header->bPrintRef ? "" : " not"));
 +        for (i = 0; i < ncrd; i++)
 +        {
 +            printf("\tcrd %d) k = %-5g  position = %g\n", i, header->k[i], header->umbInitDist[i]);
 +        }
 +    }
 +    if (!opt->verbose && first)
 +    {
 +        printf("\tUse option -v to see this output for all input tpr files\n\n");
 +    }
 +
 +    first = 0;
 +}
 +
 +//! 2-norm in a ndim-dimensional space
 +double dist_ndim(double **dx, int ndim, int line)
 +{
 +    int    i;
 +    double r2 = 0.;
 +    for (i = 0; i < ndim; i++)
 +    {
 +        r2 += sqr(dx[i][line]);
 +    }
 +    return sqrt(r2);
 +}
 +
 +//! Read pullx.xvg or pullf.xvg
 +void read_pull_xf(const char *fn, const char *fntpr, t_UmbrellaHeader * header,
 +                  t_UmbrellaWindow * window,
 +                  t_UmbrellaOptions *opt,
 +                  gmx_bool bGetMinMax, real *mintmp, real *maxtmp,
 +                  t_groupselection *groupsel)
 +{
 +    double        **y = 0, pos = 0., t, force, time0 = 0., dt;
 +    int             ny, nt, bins, ibin, i, g, gUsed, dstep = 1, nColPerCrd, nColRefEachCrd, nColExpect, ntot, column;
 +    real            min, max, minfound = 1e20, maxfound = -1e20;
 +    gmx_bool        dt_ok, timeok, bHaveForce;
 +    const char     *quantity;
 +    const int       blocklen = 4096;
 +    int            *lennow   = 0;
 +    static gmx_bool bFirst   = TRUE;
 +
 +    /*
 +     * Data columns in pull output:
 +     *  - in force output pullf.xvg:
 +     *    No reference columns, one column per pull coordinate
 +     *
 +     *  - in position output pullx.xvg
 +     *    bPrintRef == TRUE:  for each pull coordinate: ndim reference columns, and ndim dx columns
 +     *    bPrintRef == FALSE: for each pull coordinate: no   reference columns, but ndim dx columns
 +     */
 +
 +    nColPerCrd = opt->bPullx ? header->pull_ndim : 1;
 +    quantity   = opt->bPullx ? "position" : "force";
 +
 +    if (opt->bPullx && header->bPrintRef)
 +    {
 +        nColRefEachCrd = header->pull_ndim;
 +    }
 +    else
 +    {
 +        nColRefEachCrd = 0;
 +    }
 +
 +    nColExpect = 1 + header->npullcrds*(nColRefEachCrd+nColPerCrd);
 +    bHaveForce = opt->bPullf;
 +
 +    /* With geometry "distance" or "distance_periodic", only force reading is supported so far.
 +       That avoids the somewhat tedious extraction of the reaction coordinate from the pullx files.
 +       Sorry for the laziness, this is a To-do. */
 +    if  ( (header->pull_geometry == epullgDIR || header->pull_geometry == epullgDIRPBC)
 +          && opt->bPullx)
 +    {
 +        gmx_fatal(FARGS, "With pull geometries \"direction\" and \"direction_periodic\", only pull force "
 +                  "reading \n(option -if) is supported at present, "
 +                  "not pull position reading (options -ix).\nMake sure mdrun writes the pull "
 +                  "forces (pullf.xvg files)\nand provide them to g_wham with option -if.",
 +                  epullg_names[header->pull_geometry]);
 +    }
 +
 +    nt = read_xvg(fn, &y, &ny);
 +
 +    /* Check consistency */
 +    if (nt < 1)
 +    {
 +        gmx_fatal(FARGS, "Empty pull %s file %s\n", quantity, fn);
 +    }
 +    if (bFirst)
 +    {
 +        printf("Reading pull %s file with pull geometry %s and %d pull dimensions\n",
 +               bHaveForce ? "force" : "position", epullg_names[header->pull_geometry],
 +               header->pull_ndim);
 +        printf("Expecting these columns in pull file:\n"
 +               "\t%d reference columns for each individual pull coordinate\n"
 +               "\t%d data columns for each pull coordinate\n", nColRefEachCrd, nColPerCrd);
 +        printf("With %d pull groups, expect %d columns (including the time column)\n", header->npullcrds, nColExpect);
 +        bFirst = FALSE;
 +    }
 +    if (ny != nColExpect)
 +    {
 +        gmx_fatal(FARGS, "Found %d pull coodinates in %s,\n but %d data columns in %s (expected %d)\n"
 +                  "\nMaybe you confused options -ix and -if ?\n",
 +                  header->npullcrds, fntpr, ny-1, fn, nColExpect-1);
 +    }
 +
 +    if (opt->verbose)
 +    {
 +        printf("Found %d times and %d %s sets %s\n", nt, (ny-1)/nColPerCrd, quantity, fn);
 +    }
 +
 +    if (!bGetMinMax)
 +    {
 +        bins = opt->bins;
 +        min  = opt->min;
 +        max  = opt->max;
 +        if (nt > 1)
 +        {
 +            window->dt = y[0][1]-y[0][0];
 +        }
 +        else if (opt->nBootStrap && opt->tauBootStrap != 0.0)
 +        {
 +            fprintf(stderr, "\n *** WARNING, Could not determine time step in %s\n", fn);
 +        }
 +
 +        /* Need to alocate memory and set up structure */
 +
 +        if (groupsel)
 +        {
 +            /* Use only groups selected with option -is file */
 +            if (header->npullcrds != groupsel->n)
 +            {
 +                gmx_fatal(FARGS, "tpr file contains %d pull groups, but expected %d from group selection file\n",
 +                          header->npullcrds, groupsel->n);
 +            }
 +            window->nPull = groupsel->nUse;
 +        }
 +        else
 +        {
 +            window->nPull = header->npullcrds;
 +        }
 +
 +        window->nBin = bins;
 +        snew(window->Histo, window->nPull);
 +        snew(window->z, window->nPull);
 +        snew(window->k, window->nPull);
 +        snew(window->pos, window->nPull);
 +        snew(window->N, window->nPull);
 +        snew(window->Ntot, window->nPull);
 +        snew(window->g, window->nPull);
 +        snew(window->bsWeight, window->nPull);
 +        window->bContrib = 0;
 +
 +        if (opt->bCalcTauInt)
 +        {
 +            snew(window->ztime, window->nPull);
 +        }
 +        else
 +        {
 +            window->ztime = NULL;
 +        }
 +        snew(lennow, window->nPull);
 +
 +        for (g = 0; g < window->nPull; ++g)
 +        {
 +            window->z[g]        = 1;
 +            window->bsWeight[g] = 1.;
 +            snew(window->Histo[g], bins);
 +            window->N[g]    = 0;
 +            window->Ntot[g] = 0;
 +            window->g[g]    = 1.;
 +            if (opt->bCalcTauInt)
 +            {
 +                window->ztime[g] = NULL;
 +            }
 +        }
 +
 +        /* Copying umbrella center and force const is more involved since not
 +           all pull groups from header (tpr file) may be used in window variable */
 +        for (g = 0, gUsed = 0; g < header->npullcrds; ++g)
 +        {
 +            if (groupsel && (groupsel->bUse[g] == FALSE))
 +            {
 +                continue;
 +            }
 +            window->k[gUsed]   = header->k[g];
 +            window->pos[gUsed] = header->umbInitDist[g];
 +            gUsed++;
 +        }
 +    }
 +    else
 +    {   /* only determine min and max */
 +        minfound = 1e20;
 +        maxfound = -1e20;
 +        min      = max = bins = 0; /* Get rid of warnings */
 +    }
 +
 +
 +    for (i = 0; i < nt; i++)
 +    {
 +        /* Do you want that time frame? */
 +        t = 1.0/1000*( static_cast<int> ((y[0][i]*1000) + 0.5)); /* round time to fs */
 +
 +        /* get time step of pdo file and get dstep from opt->dt */
 +        if (i == 0)
 +        {
 +            time0 = t;
 +        }
 +        else if (i == 1)
 +        {
 +            dt = t-time0;
 +            if (opt->dt > 0.0)
 +            {
 +                dstep = static_cast<int>(opt->dt/dt+0.5);
 +                if (dstep == 0)
 +                {
 +                    dstep = 1;
 +                }
 +            }
 +            if (!bGetMinMax)
 +            {
 +                window->dt = dt*dstep;
 +            }
 +        }
 +
 +        dt_ok  = (i%dstep == 0);
 +        timeok = (dt_ok && t >= opt->tmin && t <= opt->tmax);
 +        /*if (opt->verbose)
 +           printf(" time = %f, (tmin,tmax)=(%e,%e), dt_ok=%d timeok=%d\n",
 +           t,opt->tmin, opt->tmax, dt_ok,timeok); */
 +        if (timeok)
 +        {
 +            /* Note: if groupsel == NULL:
 +             *          all groups in pullf/x file are stored in this window, and gUsed == g
 +             *       if groupsel != NULL:
 +             *          only groups with groupsel.bUse[g]==TRUE are stored. gUsed is not always equal g
 +             */
 +            gUsed = -1;
 +            for (g = 0; g < header->npullcrds; ++g)
 +            {
 +                /* was this group selected for application in WHAM? */
 +                if (groupsel && (groupsel->bUse[g] == FALSE))
 +                {
 +                    continue;
 +                }
 +
 +                gUsed++;
 +
 +                if (bHaveForce)
 +                {
 +                    /* y has 1 time column y[0] and one column per force y[1],...,y[nCrds] */
 +                    force = y[g+1][i];
 +                    pos   = -force/header->k[g] + header->umbInitDist[g];
 +                }
 +                else
 +                {
 +                    /* pick the right column from:
 +                     * time ref1[ndim] coord1[ndim] ref2[ndim] coord2[ndim] ...
 +                     */
 +                    column = 1 +  nColRefEachCrd + g*(nColRefEachCrd+nColPerCrd);
 +                    switch (header->pull_geometry)
 +                    {
 +                        case epullgDIST:
 +                            pos = dist_ndim(y + column, header->pull_ndim, i);
 +                            break;
 +                        case epullgCYL:
 +                            pos = y[column][i];
 +                            break;
 +                        default:
 +                            gmx_fatal(FARGS, "Bad error, this error should have been catched before. Ups.\n");
 +                    }
 +                }
 +
 +                /* printf("crd %d dpos %f poseq %f pos %f \n",g,dpos,poseq,pos); */
 +                if (bGetMinMax)
 +                {
 +                    if (pos < minfound)
 +                    {
 +                        minfound = pos;
 +                    }
 +                    if (pos > maxfound)
 +                    {
 +                        maxfound = pos;
 +                    }
 +                }
 +                else
 +                {
 +                    if (gUsed >= window->nPull)
 +                    {
 +                        gmx_fatal(FARGS, "gUsed too large (%d, nPull=%d). This error should have been catched before.\n",
 +                                  gUsed, window->nPull);
 +                    }
 +
 +                    if (opt->bCalcTauInt && !bGetMinMax)
 +                    {
 +                        /* save time series for autocorrelation analysis */
 +                        ntot = window->Ntot[gUsed];
 +                        /* printf("i %d, ntot %d, lennow[g] = %d\n",i,ntot,lennow[g]); */
 +                        if (ntot >= lennow[gUsed])
 +                        {
 +                            lennow[gUsed] += blocklen;
 +                            srenew(window->ztime[gUsed], lennow[gUsed]);
 +                        }
 +                        window->ztime[gUsed][ntot] = pos;
 +                    }
 +
 +                    ibin = static_cast<int> (floor((pos-min)/(max-min)*bins));
 +                    if (opt->bCycl)
 +                    {
 +                        if (ibin < 0)
 +                        {
 +                            while ( (ibin += bins) < 0)
 +                            {
 +                                ;
 +                            }
 +                        }
 +                        else if (ibin >= bins)
 +                        {
 +                            while ( (ibin -= bins) >= bins)
 +                            {
 +                                ;
 +                            }
 +                        }
 +                    }
 +                    if (ibin >= 0 && ibin < bins)
 +                    {
 +                        window->Histo[gUsed][ibin] += 1.;
 +                        window->N[gUsed]++;
 +                    }
 +                    window->Ntot[gUsed]++;
 +                }
 +            }
 +        }
 +        else if (t > opt->tmax)
 +        {
 +            if (opt->verbose)
 +            {
 +                printf("time %f larger than tmax %f, stop reading this pullx/pullf file\n", t, opt->tmax);
 +            }
 +            break;
 +        }
 +    }
 +
 +    if (bGetMinMax)
 +    {
 +        *mintmp = minfound;
 +        *maxtmp = maxfound;
 +    }
 +    sfree(lennow);
 +    for (i = 0; i < ny; i++)
 +    {
 +        sfree(y[i]);
 +    }
 +}
 +
 +//! read pullf-files.dat or pullx-files.dat and tpr-files.dat
 +void read_tpr_pullxf_files(char **fnTprs, char **fnPull, int nfiles,
 +                           t_UmbrellaHeader* header,
 +                           t_UmbrellaWindow *window, t_UmbrellaOptions *opt)
 +{
 +    int  i;
 +    real mintmp, maxtmp;
 +
 +    printf("Reading %d tpr and pullf files\n", nfiles/2);
 +
 +    /* min and max not given? */
 +    if (opt->bAuto)
 +    {
 +        printf("Automatic determination of boundaries...\n");
 +        opt->min = 1e20;
 +        opt->max = -1e20;
 +        for (i = 0; i < nfiles; i++)
 +        {
 +            if (whaminFileType(fnTprs[i]) != whamin_tpr)
 +            {
 +                gmx_fatal(FARGS, "Expected the %d'th file in input file to be a tpr file\n", i);
 +            }
 +            read_tpr_header(fnTprs[i], header, opt);
 +            if (whaminFileType(fnPull[i]) != whamin_pullxf)
 +            {
 +                gmx_fatal(FARGS, "Expected the %d'th file in input file to be a xvg (pullx/pullf) file\n", i);
 +            }
 +            read_pull_xf(fnPull[i], fnTprs[i], header, NULL, opt, TRUE, &mintmp, &maxtmp,
 +                         (opt->nGroupsel > 0) ? &opt->groupsel[i] : NULL);
 +            if (maxtmp > opt->max)
 +            {
 +                opt->max = maxtmp;
 +            }
 +            if (mintmp < opt->min)
 +            {
 +                opt->min = mintmp;
 +            }
 +        }
 +        printf("\nDetermined boundaries to %f and %f\n\n", opt->min, opt->max);
 +        if (opt->bBoundsOnly)
 +        {
 +            printf("Found option -boundsonly, now exiting.\n");
 +            exit (0);
 +        }
 +    }
 +    /* store stepsize in profile */
 +    opt->dz = (opt->max-opt->min)/opt->bins;
 +
 +    for (i = 0; i < nfiles; i++)
 +    {
 +        if (whaminFileType(fnTprs[i]) != whamin_tpr)
 +        {
 +            gmx_fatal(FARGS, "Expected the %d'th file in input file to be a tpr file\n", i);
 +        }
 +        read_tpr_header(fnTprs[i], header, opt);
 +        if (whaminFileType(fnPull[i]) != whamin_pullxf)
 +        {
 +            gmx_fatal(FARGS, "Expected the %d'th file in input file to be a xvg (pullx/pullf) file\n", i);
 +        }
 +        read_pull_xf(fnPull[i], fnTprs[i], header, window+i, opt, FALSE, NULL, NULL,
 +                     (opt->nGroupsel > 0) ? &opt->groupsel[i] : NULL);
 +        if (window[i].Ntot[0] == 0)
 +        {
 +            fprintf(stderr, "\nWARNING, no data points read from file %s (check -b option)\n", fnPull[i]);
 +        }
 +    }
 +
 +    for (i = 0; i < nfiles; i++)
 +    {
 +        sfree(fnTprs[i]);
 +        sfree(fnPull[i]);
 +    }
 +    sfree(fnTprs);
 +    sfree(fnPull);
 +}
 +
 +/*! \brief Read integrated autocorrelation times from input file (option -iiact)
 + *
 + * Note: Here we consider tau[int] := int_0^inf ACF(t) as the integrated autocorrelation times.
 + * The factor `g := 1 + 2*tau[int]` subsequently enters the uncertainty.
 + */
 +void readIntegratedAutocorrelationTimes(t_UmbrellaWindow *window, int nwins, const char* fn)
 +{
 +    int      nlines, ny, i, ig;
 +    double **iact;
 +
 +    printf("Readging Integrated autocorrelation times from %s ...\n", fn);
 +    nlines = read_xvg(fn, &iact, &ny);
 +    if (nlines != nwins)
 +    {
 +        gmx_fatal(FARGS, "Found %d lines with integrated autocorrelation times in %s.\nExpected %d",
 +                  nlines, fn, nwins);
 +    }
 +    for (i = 0; i < nlines; i++)
 +    {
 +        if (window[i].nPull != ny)
 +        {
 +            gmx_fatal(FARGS, "You are providing autocorrelation times with option -iiact and the\n"
 +                      "number of pull groups is different in different simulations. That is not\n"
 +                      "supported yet. Sorry.\n");
 +        }
 +        for (ig = 0; ig < window[i].nPull; ig++)
 +        {
 +            /* compare Kumar et al, J Comp Chem 13, 1011-1021 (1992) */
 +            window[i].g[ig] = 1+2*iact[ig][i]/window[i].dt;
 +
 +            if (iact[ig][i] <= 0.0)
 +            {
 +                fprintf(stderr, "\nWARNING, IACT = %f (window %d, group %d)\n", iact[ig][i], i, ig);
 +            }
 +        }
 +    }
 +}
 +
 +
 +/*! \brief Smooth autocorreltion times along the reaction coordinate.
 + *
 + * This is useful
 + * if the ACT is subject to high uncertainty in case if limited sampling. Note
 + * that -in case of limited sampling- the ACT may be severely underestimated.
 + * Note: the g=1+2tau are overwritten.
 + * If opt->bAllowReduceIact==FALSE, the ACTs are never reduced, only increased
 + * by the smoothing
 + */
 +void smoothIact(t_UmbrellaWindow *window, int nwins, t_UmbrellaOptions *opt)
 +{
 +    int    i, ig, j, jg;
 +    double pos, dpos2, siglim, siglim2, gaufact, invtwosig2, w, weight, tausm;
 +
 +    /* only evaluate within +- 3sigma of the Gausian */
 +    siglim  = 3.0*opt->sigSmoothIact;
 +    siglim2 = dsqr(siglim);
 +    /* pre-factor of Gaussian */
 +    gaufact    = 1.0/(sqrt(2*M_PI)*opt->sigSmoothIact);
 +    invtwosig2 = 0.5/dsqr(opt->sigSmoothIact);
 +
 +    for (i = 0; i < nwins; i++)
 +    {
 +        snew(window[i].tausmooth, window[i].nPull);
 +        for (ig = 0; ig < window[i].nPull; ig++)
 +        {
 +            tausm  = 0.;
 +            weight = 0;
 +            pos    = window[i].pos[ig];
 +            for (j = 0; j < nwins; j++)
 +            {
 +                for (jg = 0; jg < window[j].nPull; jg++)
 +                {
 +                    dpos2 = dsqr(window[j].pos[jg]-pos);
 +                    if (dpos2 < siglim2)
 +                    {
 +                        w       = gaufact*exp(-dpos2*invtwosig2);
 +                        weight += w;
 +                        tausm  += w*window[j].tau[jg];
 +                        /*printf("Weight %g dpos2=%g pos=%g gaufact=%g invtwosig2=%g\n",
 +                           w,dpos2,pos,gaufact,invtwosig2); */
 +                    }
 +                }
 +            }
 +            tausm /= weight;
 +            if (opt->bAllowReduceIact || tausm > window[i].tau[ig])
 +            {
 +                window[i].tausmooth[ig] = tausm;
 +            }
 +            else
 +            {
 +                window[i].tausmooth[ig] = window[i].tau[ig];
 +            }
 +            window[i].g[ig] = 1+2*tausm/window[i].dt;
 +        }
 +    }
 +}
 +
 +//! Stop integrating autoccorelation function when ACF drops under this value
 +#define WHAM_AC_ZERO_LIMIT 0.05
 +
 +/*! \brief Try to compute the autocorrelation time for each umbrealla window
 + */
 +void calcIntegratedAutocorrelationTimes(t_UmbrellaWindow *window, int nwins,
 +                                        t_UmbrellaOptions *opt, const char *fn)
 +{
 +    int   i, ig, ncorr, ntot, j, k, *count, restart;
 +    real *corr, c0, dt, tmp;
 +    real *ztime, av, tausteps;
 +    FILE *fp, *fpcorr = 0;
 +
 +    if (opt->verbose)
 +    {
 +        fpcorr = xvgropen("hist_autocorr.xvg", "Autocorrelation functions of umbrella windows",
 +                          "time [ps]", "autocorrelation function", opt->oenv);
 +    }
 +
 +    printf("\n");
 +    for (i = 0; i < nwins; i++)
 +    {
 +        printf("\rEstimating integrated autocorreltion times ... [%2.0f%%] ...", 100.*(i+1)/nwins);
 +        fflush(stdout);
 +        ntot = window[i].Ntot[0];
 +
 +        /* using half the maximum time as length of autocorrelation function */
 +        ncorr = ntot/2;
 +        if (ntot < 10)
 +        {
 +            gmx_fatal(FARGS, "Tryig to estimtate autocorrelation time from only %d"
 +                      " points. Provide more pull data!", ntot);
 +        }
 +        snew(corr, ncorr);
 +        /* snew(corrSq,ncorr); */
 +        snew(count, ncorr);
 +        dt = window[i].dt;
 +        snew(window[i].tau, window[i].nPull);
 +        restart = static_cast<int>(opt->acTrestart/dt+0.5);
 +        if (restart == 0)
 +        {
 +            restart = 1;
 +        }
 +
 +        for (ig = 0; ig < window[i].nPull; ig++)
 +        {
 +            if (ntot != window[i].Ntot[ig])
 +            {
 +                gmx_fatal(FARGS, "Encountered different nr of frames in different pull groups.\n"
 +                          "That should not happen. (%d and %d)\n", ntot, window[i].Ntot[ig]);
 +            }
 +            ztime = window[i].ztime[ig];
 +
 +            /* calc autocorrelation function C(t) = < [z(tau)-<z>]*[z(tau+t)-<z>]> */
 +            for (j = 0, av = 0; (j < ntot); j++)
 +            {
 +                av += ztime[j];
 +            }
 +            av /= ntot;
 +            for (k = 0; (k < ncorr); k++)
 +            {
 +                corr[k]  = 0.;
 +                count[k] = 0;
 +            }
 +            for (j = 0; (j < ntot); j += restart)
 +            {
 +                for (k = 0; (k < ncorr) && (j+k < ntot); k++)
 +                {
 +                    tmp        = (ztime[j]-av)*(ztime[j+k]-av);
 +                    corr  [k] += tmp;
 +                    /* corrSq[k] += tmp*tmp; */
 +                    count[k]++;
 +                }
 +            }
 +            /* divide by nr of frames for each time displacement */
 +            for (k = 0; (k < ncorr); k++)
 +            {
 +                /* count probably = (ncorr-k+(restart-1))/restart; */
 +                corr[k] = corr[k]/count[k];
 +                /* variance of autocorrelation function */
 +                /* corrSq[k]=corrSq[k]/count[k]; */
 +            }
 +            /* normalize such that corr[0] == 0 */
 +            c0 = 1./corr[0];
 +            for (k = 0; (k < ncorr); k++)
 +            {
 +                corr[k] *= c0;
 +                /* corrSq[k]*=c0*c0; */
 +            }
 +
 +            /* write ACFs in verbose mode */
 +            if (fpcorr)
 +            {
 +                for (k = 0; (k < ncorr); k++)
 +                {
 +                    fprintf(fpcorr, "%g  %g\n", k*dt, corr[k]);
 +                }
-     fprintf(fp, "@    s0 symbol 1\n@    s0 symbol size 0.5\n@    s0 line linestyle 0\n");
-     fprintf(fp, "#  WIN   tau(gr1)  tau(gr2) ...\n");
-     for (i = 0; i < nwins; i++)
++                fprintf(fpcorr, "%s\n", output_env_get_print_xvgr_codes(opt->oenv) ? "&" : "");
 +            }
 +
 +            /* esimate integrated correlation time, fitting is too unstable */
 +            tausteps = 0.5*corr[0];
 +            /* consider corr below WHAM_AC_ZERO_LIMIT as noise */
 +            for (j = 1; (j < ncorr) && (corr[j] > WHAM_AC_ZERO_LIMIT); j++)
 +            {
 +                tausteps += corr[j];
 +            }
 +
 +            /* g = 1+2*tau, see. Ferrenberg/Swendsen, PRL 63:1195 (1989) or
 +               Kumar et al, eq. 28 ff. */
 +            window[i].tau[ig] = tausteps*dt;
 +            window[i].g[ig]   = 1+2*tausteps;
 +            /* printf("win %d, group %d, estimated correlation time = %g ps\n",i,ig,window[i].tau[ig]); */
 +        } /* ig loop */
 +        sfree(corr);
 +        sfree(count);
 +    }
 +    printf(" done\n");
 +    if (fpcorr)
 +    {
 +        gmx_ffclose(fpcorr);
 +    }
 +
 +    /* plot IACT along reaction coordinate */
 +    fp = xvgropen(fn, "Integrated autocorrelation times", "z", "IACT [ps]", opt->oenv);
-         fprintf(fp, "# %3d   ", i);
-         for (ig = 0; ig < window[i].nPull; ig++)
++    if (output_env_get_print_xvgr_codes(opt->oenv))
 +    {
-             fprintf(fp, " %11g", window[i].tau[ig]);
++        fprintf(fp, "@    s0 symbol 1\n@    s0 symbol size 0.5\n@    s0 line linestyle 0\n");
++        fprintf(fp, "#  WIN   tau(gr1)  tau(gr2) ...\n");
++        for (i = 0; i < nwins; i++)
 +        {
-         fprintf(fp, "\n");
++            fprintf(fp, "# %3d   ", i);
++            for (ig = 0; ig < window[i].nPull; ig++)
++            {
++                fprintf(fp, " %11g", window[i].tau[ig]);
++            }
++            fprintf(fp, "\n");
 +        }
-         fprintf(fp, "&\n@    s1 symbol 1\n@    s1 symbol size 0.5\n@    s1 line linestyle 0\n");
-         fprintf(fp, "@    s1 symbol color 2\n");
 +    }
 +    for (i = 0; i < nwins; i++)
 +    {
 +        for (ig = 0; ig < window[i].nPull; ig++)
 +        {
 +            fprintf(fp, "%8g %8g\n", window[i].pos[ig], window[i].tau[ig]);
 +        }
 +    }
 +    if (opt->sigSmoothIact > 0.0)
 +    {
 +        printf("Smoothing autocorrelation times along reaction coordinate with Gaussian of sig = %g\n",
 +               opt->sigSmoothIact);
 +        /* smooth IACT along reaction coordinate and overwrite g=1+2tau */
 +        smoothIact(window, nwins, opt);
++        fprintf(fp, "%s\n", output_env_get_print_xvgr_codes(opt->oenv) ? "&" : "");
++        if (output_env_get_print_xvgr_codes(opt->oenv))
++        {
++            fprintf(fp, "@    s1 symbol 1\n@    s1 symbol size 0.5\n@    s1 line linestyle 0\n");
++            fprintf(fp, "@    s1 symbol color 2\n");
++        }
 +        for (i = 0; i < nwins; i++)
 +        {
 +            for (ig = 0; ig < window[i].nPull; ig++)
 +            {
 +                fprintf(fp, "%8g %8g\n", window[i].pos[ig], window[i].tausmooth[ig]);
 +            }
 +        }
 +    }
 +    gmx_ffclose(fp);
 +    printf("Wrote %s\n", fn);
 +}
 +
 +/*! \brief
 + * compute average and sigma of each umbrella histogram
 + */
 +void averageSigma(t_UmbrellaWindow *window, int nwins)
 +{
 +    int  i, ig, ntot, k;
 +    real av, sum2, sig, diff, *ztime, nSamplesIndep;
 +
 +    for (i = 0; i < nwins; i++)
 +    {
 +        snew(window[i].aver, window[i].nPull);
 +        snew(window[i].sigma, window[i].nPull);
 +
 +        ntot = window[i].Ntot[0];
 +        for (ig = 0; ig < window[i].nPull; ig++)
 +        {
 +            ztime = window[i].ztime[ig];
 +            for (k = 0, av = 0.; k < ntot; k++)
 +            {
 +                av += ztime[k];
 +            }
 +            av /= ntot;
 +            for (k = 0, sum2 = 0.; k < ntot; k++)
 +            {
 +                diff  = ztime[k]-av;
 +                sum2 += diff*diff;
 +            }
 +            sig                = sqrt(sum2/ntot);
 +            window[i].aver[ig] = av;
 +
 +            /* Note: This estimate for sigma is biased from the limited sampling.
 +               Correct sigma by n/(n-1) where n = number of independent
 +               samples. Only possible if IACT is known.
 +             */
 +            if (window[i].tau)
 +            {
 +                nSamplesIndep       = window[i].N[ig]/(window[i].tau[ig]/window[i].dt);
 +                window[i].sigma[ig] = sig * nSamplesIndep/(nSamplesIndep-1);
 +            }
 +            else
 +            {
 +                window[i].sigma[ig] = sig;
 +            }
 +            printf("win %d, aver = %f  sig = %f\n", i, av, window[i].sigma[ig]);
 +        }
 +    }
 +}
 +
 +
 +/*! \brief
 + * Use histograms to compute average force on pull group.
 + */
 +void computeAverageForce(t_UmbrellaWindow *window, int nWindows, t_UmbrellaOptions *opt)
 +{
 +    int    i, j, bins = opt->bins, k;
 +    double dz, min = opt->min, max = opt->max, displAv, temp, distance, ztot, ztot_half, w, weight;
 +    double posmirrored;
 +
 +    dz        = (max-min)/bins;
 +    ztot      = opt->max-min;
 +    ztot_half = ztot/2;
 +
 +    /* Compute average displacement from histograms */
 +    for (j = 0; j < nWindows; ++j)
 +    {
 +        snew(window[j].forceAv, window[j].nPull);
 +        for (k = 0; k < window[j].nPull; ++k)
 +        {
 +            displAv = 0.0;
 +            weight  = 0.0;
 +            for (i = 0; i < opt->bins; ++i)
 +            {
 +                temp     = (1.0*i+0.5)*dz+min;
 +                distance = temp - window[j].pos[k];
 +                if (opt->bCycl)
 +                {                                       /* in cyclic wham:             */
 +                    if (distance > ztot_half)           /*    |distance| < ztot_half   */
 +                    {
 +                        distance -= ztot;
 +                    }
 +                    else if (distance < -ztot_half)
 +                    {
 +                        distance += ztot;
 +                    }
 +                }
 +                w         = window[j].Histo[k][i]/window[j].g[k];
 +                displAv  += w*distance;
 +                weight   += w;
 +                /* Are we near min or max? We are getting wrong forces from the histgrams since
 +                   the histograms are zero outside [min,max). Therefore, assume that the position
 +                   on the other side of the histomgram center is equally likely. */
 +                if (!opt->bCycl)
 +                {
 +                    posmirrored = window[j].pos[k]-distance;
 +                    if (posmirrored >= max || posmirrored < min)
 +                    {
 +                        displAv  += -w*distance;
 +                        weight   += w;
 +                    }
 +                }
 +            }
 +            displAv  /= weight;
 +
 +            /* average force from average displacement */
 +            window[j].forceAv[k] = displAv*window[j].k[k];
 +            /* sigma from average square displacement */
 +            /* window[j].sigma  [k] = sqrt(displAv2); */
 +            /* printf("Win %d, sigma = %f\n",j,sqrt(displAv2)); */
 +        }
 +    }
 +}
 +
 +/*! \brief
 + * Check if the complete reaction coordinate is covered by the histograms
 + */
 +void  checkReactionCoordinateCovered(t_UmbrellaWindow *window, int nwins,
 +                                     t_UmbrellaOptions *opt)
 +{
 +    int  i, ig, j, bins = opt->bins, bBoundary;
 +    real avcount = 0, z, relcount, *count;
 +    snew(count, opt->bins);
 +
 +    for (j = 0; j < opt->bins; ++j)
 +    {
 +        for (i = 0; i < nwins; i++)
 +        {
 +            for (ig = 0; ig < window[i].nPull; ig++)
 +            {
 +                count[j] += window[i].Histo[ig][j];
 +            }
 +        }
 +        avcount += 1.0*count[j];
 +    }
 +    avcount /= bins;
 +    for (j = 0; j < bins; ++j)
 +    {
 +        relcount  = count[j]/avcount;
 +        z         = (j+0.5)*opt->dz+opt->min;
 +        bBoundary = ( j<bins/20 || (bins-j)>bins/20 );
 +        /* check for bins with no data */
 +        if (count[j] == 0)
 +        {
 +            fprintf(stderr, "\nWARNING, no data point in bin %d (z=%g) !\n"
 +                    "You may not get a reasonable profile. Check your histograms!\n", j, z);
 +        }
 +        /* and check for poor sampling */
 +        else if (relcount < 0.005 && !bBoundary)
 +        {
 +            fprintf(stderr, "Warning, poor sampling bin %d (z=%g). Check your histograms!\n", j, z);
 +        }
 +    }
 +    sfree(count);
 +}
 +
 +/*! \brief Compute initial potential by integrating the average force
 + *
 + * This speeds up the convergence by roughly a factor of 2
 + */
 +void guessPotByIntegration(t_UmbrellaWindow *window, int nWindows, t_UmbrellaOptions *opt)
 +{
 +    int    i, j, ig, bins = opt->bins, nHist, winmin, groupmin;
 +    double dz, min = opt->min, *pot, pos, hispos, dist, diff, fAv, distmin, *f;
 +    FILE  *fp;
 +
 +    dz = (opt->max-min)/bins;
 +
 +    printf("Getting initial potential by integration.\n");
 +
 +    /* Compute average displacement from histograms */
 +    computeAverageForce(window, nWindows, opt);
 +
 +    /* Get force for each bin from all histograms in this bin, or, alternatively,
 +       if no histograms are inside this bin, from the closest histogram */
 +    snew(pot, bins);
 +    snew(f, bins);
 +    for (j = 0; j < opt->bins; ++j)
 +    {
 +        pos      = (1.0*j+0.5)*dz+min;
 +        nHist    = 0;
 +        fAv      = 0.;
 +        distmin  = 1e20;
 +        groupmin = winmin = 0;
 +        for (i = 0; i < nWindows; i++)
 +        {
 +            for (ig = 0; ig < window[i].nPull; ig++)
 +            {
 +                hispos = window[i].pos[ig];
 +                dist   = fabs(hispos-pos);
 +                /* average force within bin */
 +                if (dist < dz/2)
 +                {
 +                    nHist++;
 +                    fAv += window[i].forceAv[ig];
 +                }
 +                /* at the same time, rememer closest histogram */
 +                if (dist < distmin)
 +                {
 +                    winmin   = i;
 +                    groupmin = ig;
 +                    distmin  = dist;
 +                }
 +            }
 +        }
 +        /* if no histogram found in this bin, use closest histogram */
 +        if (nHist > 0)
 +        {
 +            fAv = fAv/nHist;
 +        }
 +        else
 +        {
 +            fAv = window[winmin].forceAv[groupmin];
 +        }
 +        f[j] = fAv;
 +    }
 +    for (j = 1; j < opt->bins; ++j)
 +    {
 +        pot[j] = pot[j-1] - 0.5*dz*(f[j-1]+f[j]);
 +    }
 +
 +    /* cyclic wham: linearly correct possible offset */
 +    if (opt->bCycl)
 +    {
 +        diff = (pot[bins-1]-pot[0])/(bins-1);
 +        for (j = 1; j < opt->bins; ++j)
 +        {
 +            pot[j] -= j*diff;
 +        }
 +    }
 +    if (opt->verbose)
 +    {
 +        fp = xvgropen("pmfintegrated.xvg", "PMF from force integration", "z", "PMF [kJ/mol]", opt->oenv);
 +        for (j = 0; j < opt->bins; ++j)
 +        {
 +            fprintf(fp, "%g  %g\n", (j+0.5)*dz+opt->min, pot[j]);
 +        }
 +        gmx_ffclose(fp);
 +        printf("verbose mode: wrote %s with PMF from interated forces\n", "pmfintegrated.xvg");
 +    }
 +
 +    /* get initial z=exp(-F[i]/kT) from integrated potential, where F[i] denote the free
 +       energy offsets which are usually determined by wham
 +       First: turn pot into probabilities:
 +     */
 +    for (j = 0; j < opt->bins; ++j)
 +    {
 +        pot[j] = exp(-pot[j]/(8.314e-3*opt->Temperature));
 +    }
 +    calc_z(pot, window, nWindows, opt, TRUE);
 +
 +    sfree(pot);
 +    sfree(f);
 +}
 +
 +//! Count number of words in an line
 +static int wordcount(char *ptr)
 +{
 +    int i, n, is[2];
 +    int cur = 0;
 +
 +    if (strlen(ptr) == 0)
 +    {
 +        return 0;
 +    }
 +    /* fprintf(stderr,"ptr='%s'\n",ptr); */
 +    n = 1;
 +    for (i = 0; (ptr[i] != '\0'); i++)
 +    {
 +        is[cur] = isspace(ptr[i]);
 +        if ((i > 0)  && (is[cur] && !is[1-cur]))
 +        {
 +            n++;
 +        }
 +        cur = 1-cur;
 +    }
 +    return n;
 +}
 +
 +/*! \brief Read input file for pull group selection (option -is)
 + *
 + * TO DO: ptr=fgets(...) is never freed (small memory leak)
 + */
 +void readPullGroupSelection(t_UmbrellaOptions *opt, char **fnTpr, int nTpr)
 +{
 +    FILE *fp;
 +    int   i, iline, n, len = STRLEN, temp;
 +    char *ptr = 0, *tmpbuf = 0;
 +    char  fmt[1024], fmtign[1024];
 +    int   block = 1, sizenow;
 +
 +    fp            = gmx_ffopen(opt->fnGroupsel, "r");
 +    opt->groupsel = NULL;
 +
 +    snew(tmpbuf, len);
 +    sizenow = 0;
 +    iline   = 0;
 +    while ( (ptr = fgets3(fp, tmpbuf, &len)) != NULL)
 +    {
 +        trim(ptr);
 +        n = wordcount(ptr);
 +
 +        if (iline >= sizenow)
 +        {
 +            sizenow += block;
 +            srenew(opt->groupsel, sizenow);
 +        }
 +        opt->groupsel[iline].n    = n;
 +        opt->groupsel[iline].nUse = 0;
 +        snew(opt->groupsel[iline].bUse, n);
 +
 +        fmtign[0] = '\0';
 +        for (i = 0; i < n; i++)
 +        {
 +            strcpy(fmt, fmtign);
 +            strcat(fmt, "%d");
 +            if (sscanf(ptr, fmt, &temp))
 +            {
 +                opt->groupsel[iline].bUse[i] = (temp > 0);
 +                if (opt->groupsel[iline].bUse[i])
 +                {
 +                    opt->groupsel[iline].nUse++;
 +                }
 +            }
 +            strcat(fmtign, "%*s");
 +        }
 +        iline++;
 +    }
 +    opt->nGroupsel = iline;
 +    if (nTpr != opt->nGroupsel)
 +    {
 +        gmx_fatal(FARGS, "Found %d tpr files but %d lines in %s\n", nTpr, opt->nGroupsel,
 +                  opt->fnGroupsel);
 +    }
 +
 +    printf("\nUse only these pull groups:\n");
 +    for (iline = 0; iline < nTpr; iline++)
 +    {
 +        printf("%s (%d of %d groups):", fnTpr[iline], opt->groupsel[iline].nUse, opt->groupsel[iline].n);
 +        for (i = 0; i < opt->groupsel[iline].n; i++)
 +        {
 +            if (opt->groupsel[iline].bUse[i])
 +            {
 +                printf(" %d", i+1);
 +            }
 +        }
 +        printf("\n");
 +    }
 +    printf("\n");
 +
 +    sfree(tmpbuf);
 +}
 +
 +//! Boolean XOR
 +#define WHAMBOOLXOR(a, b) ( ((!(a)) && (b)) || ((a) && (!(b))))
 +
 +//! Number of elements in fnm (used for command line parsing)
 +#define NFILE asize(fnm)
 +
 +//! The main g_wham routine
 +int gmx_wham(int argc, char *argv[])
 +{
 +    const char              *desc[] = {
 +        "[THISMODULE] is an analysis program that implements the Weighted",
 +        "Histogram Analysis Method (WHAM). It is intended to analyze",
 +        "output files generated by umbrella sampling simulations to ",
 +        "compute a potential of mean force (PMF). [PAR] ",
 +        "At present, three input modes are supported.[BR]",
 +        "[TT]*[tt] With option [TT]-it[tt], the user provides a file which contains the",
 +        " file names of the umbrella simulation run-input files ([TT].tpr[tt] files),",
 +        " AND, with option [TT]-ix[tt], a file which contains file names of",
 +        " the pullx [TT]mdrun[tt] output files. The [TT].tpr[tt] and pullx files must",
 +        " be in corresponding order, i.e. the first [TT].tpr[tt] created the",
 +        " first pullx, etc.[BR]",
 +        "[TT]*[tt] Same as the previous input mode, except that the the user",
 +        " provides the pull force output file names ([TT]pullf.xvg[tt]) with option [TT]-if[tt].",
 +        " From the pull force the position in the umbrella potential is",
 +        " computed. This does not work with tabulated umbrella potentials.[BR]"
 +        "[TT]*[tt] With option [TT]-ip[tt], the user provides file names of (gzipped) [TT].pdo[tt] files, i.e.",
 +        " the GROMACS 3.3 umbrella output files. If you have some unusual"
 +        " reaction coordinate you may also generate your own [TT].pdo[tt] files and",
 +        " feed them with the [TT]-ip[tt] option into to [THISMODULE]. The [TT].pdo[tt] file header",
 +        " must be similar to the following:[PAR]",
 +        "[TT]# UMBRELLA      3.0[BR]",
 +        "# Component selection: 0 0 1[BR]",
 +        "# nSkip 1[BR]",
 +        "# Ref. Group 'TestAtom'[BR]",
 +        "# Nr. of pull groups 2[BR]",
 +        "# Group 1 'GR1'  Umb. Pos. 5.0 Umb. Cons. 1000.0[BR]",
 +        "# Group 2 'GR2'  Umb. Pos. 2.0 Umb. Cons. 500.0[BR]",
 +        "#####[tt][PAR]",
 +        "The number of pull groups, umbrella positions, force constants, and names ",
 +        "may (of course) differ. Following the header, a time column and ",
 +        "a data column for each pull group follows (i.e. the displacement",
 +        "with respect to the umbrella center). Up to four pull groups are possible ",
 +        "per [TT].pdo[tt] file at present.[PAR]",
 +        "By default, all pull groups found in all pullx/pullf files are used in WHAM. If only ",
 +        "some of the pull groups should be used, a pull group selection file (option [TT]-is[tt]) can ",
 +        "be provided. The selection file must contain one line for each tpr file in tpr-files.dat.",
 +        "Each of these lines must contain one digit (0 or 1) for each pull group in the tpr file. ",
 +        "Here, 1 indicates that the pull group is used in WHAM, and 0 means it is omitted. Example:",
 +        "If you have three tpr files, each containing 4 pull groups, but only pull group 1 and 2 should be ",
 +        "used, groupsel.dat looks like this:[BR]",
 +        "1 1 0 0[BR]",
 +        "1 1 0 0[BR]",
 +        "1 1 0 0[PAR]",
 +        "By default, the output files are[BR]",
 +        "  [TT]-o[tt]      PMF output file[BR]",
 +        "  [TT]-hist[tt]   Histograms output file[BR]",
 +        "Always check whether the histograms sufficiently overlap.[PAR]",
 +        "The umbrella potential is assumed to be harmonic and the force constants are ",
 +        "read from the [TT].tpr[tt] or [TT].pdo[tt] files. If a non-harmonic umbrella force was applied ",
 +        "a tabulated potential can be provided with [TT]-tab[tt].[PAR]",
 +        "WHAM OPTIONS[BR]------------[BR]",
 +        "  [TT]-bins[tt]   Number of bins used in analysis[BR]",
 +        "  [TT]-temp[tt]   Temperature in the simulations[BR]",
 +        "  [TT]-tol[tt]    Stop iteration if profile (probability) changed less than tolerance[BR]",
 +        "  [TT]-auto[tt]   Automatic determination of boundaries[BR]",
 +        "  [TT]-min,-max[tt]   Boundaries of the profile [BR]",
 +        "The data points that are used to compute the profile",
 +        "can be restricted with options [TT]-b[tt], [TT]-e[tt], and [TT]-dt[tt]. ",
 +        "Adjust [TT]-b[tt] to ensure sufficient equilibration in each ",
 +        "umbrella window.[PAR]",
 +        "With [TT]-log[tt] (default) the profile is written in energy units, otherwise ",
 +        "(with [TT]-nolog[tt]) as probability. The unit can be specified with [TT]-unit[tt]. ",
 +        "With energy output, the energy in the first bin is defined to be zero. ",
 +        "If you want the free energy at a different ",
 +        "position to be zero, set [TT]-zprof0[tt] (useful with bootstrapping, see below).[PAR]",
 +        "For cyclic or periodic reaction coordinates (dihedral angle, channel PMF",
 +        "without osmotic gradient), the option [TT]-cycl[tt] is useful.",
 +        "[THISMODULE] will make use of the",
 +        "periodicity of the system and generate a periodic PMF. The first and the last bin of the",
 +        "reaction coordinate will assumed be be neighbors.[PAR]",
 +        "Option [TT]-sym[tt] symmetrizes the profile around z=0 before output, ",
 +        "which may be useful for, e.g. membranes.[PAR]",
 +        "AUTOCORRELATIONS[BR]----------------[BR]",
 +        "With [TT]-ac[tt], [THISMODULE] estimates the integrated autocorrelation ",
 +        "time (IACT) [GRK]tau[grk] for each umbrella window and weights the respective ",
 +        "window with 1/[1+2*[GRK]tau[grk]/dt]. The IACTs are written ",
 +        "to the file defined with [TT]-oiact[tt]. In verbose mode, all ",
 +        "autocorrelation functions (ACFs) are written to [TT]hist_autocorr.xvg[tt]. ",
 +        "Because the IACTs can be severely underestimated in case of limited ",
 +        "sampling, option [TT]-acsig[tt] allows one to smooth the IACTs along the ",
 +        "reaction coordinate with a Gaussian ([GRK]sigma[grk] provided with [TT]-acsig[tt], ",
 +        "see output in [TT]iact.xvg[tt]). Note that the IACTs are estimated by simple ",
 +        "integration of the ACFs while the ACFs are larger 0.05.",
 +        "If you prefer to compute the IACTs by a more sophisticated (but possibly ",
 +        "less robust) method such as fitting to a double exponential, you can ",
 +        "compute the IACTs with [gmx-analyze] and provide them to [THISMODULE] with the file ",
 +        "[TT]iact-in.dat[tt] (option [TT]-iiact[tt]), which should contain one line per ",
 +        "input file ([TT].pdo[tt] or pullx/f file) and one column per pull group in the respective file.[PAR]",
 +        "ERROR ANALYSIS[BR]--------------[BR]",
 +        "Statistical errors may be estimated with bootstrap analysis. Use it with care, ",
 +        "otherwise the statistical error may be substantially underestimated. ",
 +        "More background and examples for the bootstrap technique can be found in ",
 +        "Hub, de Groot and Van der Spoel, JCTC (2010) 6: 3713-3720.[BR]",
 +        "[TT]-nBootstrap[tt] defines the number of bootstraps (use, e.g., 100). ",
 +        "Four bootstrapping methods are supported and ",
 +        "selected with [TT]-bs-method[tt].[BR]",
 +        "  (1) [TT]b-hist[tt]   Default: complete histograms are considered as independent ",
 +        "data points, and the bootstrap is carried out by assigning random weights to the ",
 +        "histograms (\"Bayesian bootstrap\"). Note that each point along the reaction coordinate",
 +        "must be covered by multiple independent histograms (e.g. 10 histograms), otherwise the ",
 +        "statistical error is underestimated.[BR]",
 +        "  (2) [TT]hist[tt]    Complete histograms are considered as independent data points. ",
 +        "For each bootstrap, N histograms are randomly chosen from the N given histograms ",
 +        "(allowing duplication, i.e. sampling with replacement).",
 +        "To avoid gaps without data along the reaction coordinate blocks of histograms ",
 +        "([TT]-histbs-block[tt]) may be defined. In that case, the given histograms are ",
 +        "divided into blocks and only histograms within each block are mixed. Note that ",
 +        "the histograms within each block must be representative for all possible histograms, ",
 +        "otherwise the statistical error is underestimated.[BR]",
 +        "  (3) [TT]traj[tt]  The given histograms are used to generate new random trajectories,",
 +        "such that the generated data points are distributed according the given histograms ",
 +        "and properly autocorrelated. The autocorrelation time (ACT) for each window must be ",
 +        "known, so use [TT]-ac[tt] or provide the ACT with [TT]-iiact[tt]. If the ACT of all ",
 +        "windows are identical (and known), you can also provide them with [TT]-bs-tau[tt]. ",
 +        "Note that this method may severely underestimate the error in case of limited sampling, ",
 +        "that is if individual histograms do not represent the complete phase space at ",
 +        "the respective positions.[BR]",
 +        "  (4) [TT]traj-gauss[tt]  The same as method [TT]traj[tt], but the trajectories are ",
 +        "not bootstrapped from the umbrella histograms but from Gaussians with the average ",
 +        "and width of the umbrella histograms. That method yields similar error estimates ",
 +        "like method [TT]traj[tt].[PAR]"
 +        "Bootstrapping output:[BR]",
 +        "  [TT]-bsres[tt]   Average profile and standard deviations[BR]",
 +        "  [TT]-bsprof[tt]  All bootstrapping profiles[BR]",
 +        "With [TT]-vbs[tt] (verbose bootstrapping), the histograms of each bootstrap are written, ",
 +        "and, with bootstrap method [TT]traj[tt], the cumulative distribution functions of ",
 +        "the histograms."
 +    };
 +
 +    const char              *en_unit[]       = {NULL, "kJ", "kCal", "kT", NULL};
 +    const char              *en_unit_label[] = {"", "E (kJ mol\\S-1\\N)", "E (kcal mol\\S-1\\N)", "E (kT)", NULL};
 +    const char              *en_bsMethod[]   = { NULL, "b-hist", "hist", "traj", "traj-gauss", NULL };
 +
 +    static t_UmbrellaOptions opt;
 +
 +    t_pargs                  pa[] = {
 +        { "-min", FALSE, etREAL, {&opt.min},
 +          "Minimum coordinate in profile"},
 +        { "-max", FALSE, etREAL, {&opt.max},
 +          "Maximum coordinate in profile"},
 +        { "-auto", FALSE, etBOOL, {&opt.bAuto},
 +          "Determine min and max automatically"},
 +        { "-bins", FALSE, etINT, {&opt.bins},
 +          "Number of bins in profile"},
 +        { "-temp", FALSE, etREAL, {&opt.Temperature},
 +          "Temperature"},
 +        { "-tol", FALSE, etREAL, {&opt.Tolerance},
 +          "Tolerance"},
 +        { "-v", FALSE, etBOOL, {&opt.verbose},
 +          "Verbose mode"},
 +        { "-b", FALSE, etREAL, {&opt.tmin},
 +          "First time to analyse (ps)"},
 +        { "-e", FALSE, etREAL, {&opt.tmax},
 +          "Last time to analyse (ps)"},
 +        { "-dt", FALSE, etREAL, {&opt.dt},
 +          "Analyse only every dt ps"},
 +        { "-histonly", FALSE, etBOOL, {&opt.bHistOnly},
 +          "Write histograms and exit"},
 +        { "-boundsonly", FALSE, etBOOL, {&opt.bBoundsOnly},
 +          "Determine min and max and exit (with [TT]-auto[tt])"},
 +        { "-log", FALSE, etBOOL, {&opt.bLog},
 +          "Calculate the log of the profile before printing"},
 +        { "-unit", FALSE,  etENUM, {en_unit},
 +          "Energy unit in case of log output" },
 +        { "-zprof0", FALSE, etREAL, {&opt.zProf0},
 +          "Define profile to 0.0 at this position (with [TT]-log[tt])"},
 +        { "-cycl", FALSE, etBOOL, {&opt.bCycl},
 +          "Create cyclic/periodic profile. Assumes min and max are the same point."},
 +        { "-sym", FALSE, etBOOL, {&opt.bSym},
 +          "Symmetrize profile around z=0"},
 +        { "-hist-eq", FALSE, etBOOL, {&opt.bHistEq},
 +          "HIDDENEnforce equal weight for all histograms. (Non-Weighed-HAM)"},
 +        { "-ac", FALSE, etBOOL, {&opt.bCalcTauInt},
 +          "Calculate integrated autocorrelation times and use in wham"},
 +        { "-acsig", FALSE, etREAL, {&opt.sigSmoothIact},
 +          "Smooth autocorrelation times along reaction coordinate with Gaussian of this [GRK]sigma[grk]"},
 +        { "-ac-trestart", FALSE, etREAL, {&opt.acTrestart},
 +          "When computing autocorrelation functions, restart computing every .. (ps)"},
 +        { "-acred", FALSE, etBOOL, {&opt.bAllowReduceIact},
 +          "HIDDENWhen smoothing the ACTs, allow to reduce ACTs. Otherwise, only increase ACTs "
 +          "during smoothing"},
 +        { "-nBootstrap", FALSE,  etINT, {&opt.nBootStrap},
 +          "nr of bootstraps to estimate statistical uncertainty (e.g., 200)" },
 +        { "-bs-method", FALSE,  etENUM, {en_bsMethod},
 +          "Bootstrap method" },
 +        { "-bs-tau", FALSE, etREAL, {&opt.tauBootStrap},
 +          "Autocorrelation time (ACT) assumed for all histograms. Use option [TT]-ac[tt] if ACT is unknown."},
 +        { "-bs-seed", FALSE, etINT, {&opt.bsSeed},
 +          "Seed for bootstrapping. (-1 = use time)"},
 +        { "-histbs-block", FALSE, etINT, {&opt.histBootStrapBlockLength},
 +          "When mixing histograms only mix within blocks of [TT]-histbs-block[tt]."},
 +        { "-vbs", FALSE, etBOOL, {&opt.bs_verbose},
 +          "Verbose bootstrapping. Print the CDFs and a histogram file for each bootstrap."},
 +        { "-stepout", FALSE, etINT, {&opt.stepchange},
 +          "HIDDENWrite maximum change every ... (set to 1 with [TT]-v[tt])"},
 +        { "-updateContr", FALSE, etINT, {&opt.stepUpdateContrib},
 +          "HIDDENUpdate table with significan contributions to WHAM every ... iterations"},
 +    };
 +
 +    t_filenm                 fnm[] = {
 +        { efDAT, "-ix", "pullx-files", ffOPTRD}, /* wham input: pullf.xvg's and tprs           */
 +        { efDAT, "-if", "pullf-files", ffOPTRD}, /* wham input: pullf.xvg's and tprs           */
 +        { efDAT, "-it", "tpr-files", ffOPTRD},   /* wham input: tprs                           */
 +        { efDAT, "-ip", "pdo-files", ffOPTRD},   /* wham input: pdo files (gmx3 style)         */
 +        { efDAT, "-is", "groupsel", ffOPTRD},    /* input: select pull groups to use           */
 +        { efXVG, "-o", "profile", ffWRITE },     /* output file for profile                     */
 +        { efXVG, "-hist", "histo", ffWRITE},     /* output file for histograms                  */
 +        { efXVG, "-oiact", "iact", ffOPTWR},     /* writing integrated autocorrelation times    */
 +        { efDAT, "-iiact", "iact-in", ffOPTRD},  /* reading integrated autocorrelation times   */
 +        { efXVG, "-bsres", "bsResult", ffOPTWR}, /* average and errors of bootstrap analysis    */
 +        { efXVG, "-bsprof", "bsProfs", ffOPTWR}, /* output file for bootstrap profiles          */
 +        { efDAT, "-tab", "umb-pot", ffOPTRD},    /* Tabulated umbrella potential (if not harmonic) */
 +    };
 +
 +    int                      i, j, l, nfiles, nwins, nfiles2;
 +    t_UmbrellaHeader         header;
 +    t_UmbrellaWindow       * window = NULL;
 +    double                  *profile, maxchange = 1e20;
 +    gmx_bool                 bMinSet, bMaxSet, bAutoSet, bExact = FALSE;
 +    char                   **fninTpr, **fninPull, **fninPdo;
 +    const char              *fnPull;
 +    FILE                    *histout, *profout;
 +    char                     ylabel[256], title[256];
 +
 +    opt.bins      = 200;
 +    opt.verbose   = FALSE;
 +    opt.bHistOnly = FALSE;
 +    opt.bCycl     = FALSE;
 +    opt.tmin      = 50;
 +    opt.tmax      = 1e20;
 +    opt.dt        = 0.0;
 +    opt.min       = 0;
 +    opt.max       = 0;
 +    opt.bAuto     = TRUE;
 +    opt.nGroupsel = 0;
 +
 +    /* bootstrapping stuff */
 +    opt.nBootStrap               = 0;
 +    opt.bsMethod                 = bsMethod_hist;
 +    opt.tauBootStrap             = 0.0;
 +    opt.bsSeed                   = -1;
 +    opt.histBootStrapBlockLength = 8;
 +    opt.bs_verbose               = FALSE;
 +
 +    opt.bLog                  = TRUE;
 +    opt.unit                  = en_kJ;
 +    opt.zProf0                = 0.;
 +    opt.Temperature           = 298;
 +    opt.Tolerance             = 1e-6;
 +    opt.bBoundsOnly           = FALSE;
 +    opt.bSym                  = FALSE;
 +    opt.bCalcTauInt           = FALSE;
 +    opt.sigSmoothIact         = 0.0;
 +    opt.bAllowReduceIact      = TRUE;
 +    opt.bInitPotByIntegration = TRUE;
 +    opt.acTrestart            = 1.0;
 +    opt.stepchange            = 100;
 +    opt.stepUpdateContrib     = 100;
 +
 +    if (!parse_common_args(&argc, argv, PCA_BE_NICE,
 +                           NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &opt.oenv))
 +    {
 +        return 0;
 +    }
 +
 +    opt.unit     = nenum(en_unit);
 +    opt.bsMethod = nenum(en_bsMethod);
 +
 +    opt.bProf0Set = opt2parg_bSet("-zprof0",  asize(pa), pa);
 +
 +    opt.bTab         = opt2bSet("-tab", NFILE, fnm);
 +    opt.bPdo         = opt2bSet("-ip", NFILE, fnm);
 +    opt.bTpr         = opt2bSet("-it", NFILE, fnm);
 +    opt.bPullx       = opt2bSet("-ix", NFILE, fnm);
 +    opt.bPullf       = opt2bSet("-if", NFILE, fnm);
 +    opt.bTauIntGiven = opt2bSet("-iiact", NFILE, fnm);
 +    if  (opt.bTab && opt.bPullf)
 +    {
 +        gmx_fatal(FARGS, "Force input does not work with tabulated potentials. "
 +                  "Provide pullx.xvg or pdo files!");
 +    }
 +
 +    if (!opt.bPdo && !WHAMBOOLXOR(opt.bPullx, opt.bPullf))
 +    {
 +        gmx_fatal(FARGS, "Give either pullx (-ix) OR pullf (-if) data. Not both.");
 +    }
 +    if (!opt.bPdo && !(opt.bTpr || opt.bPullf || opt.bPullx))
 +    {
 +        gmx_fatal(FARGS, "g_wham supports three input modes, pullx, pullf, or pdo file input."
 +                  "\n\n Check g_wham -h !");
 +    }
 +
 +    opt.fnPdo      = opt2fn("-ip", NFILE, fnm);
 +    opt.fnTpr      = opt2fn("-it", NFILE, fnm);
 +    opt.fnPullf    = opt2fn("-if", NFILE, fnm);
 +    opt.fnPullx    = opt2fn("-ix", NFILE, fnm);
 +    opt.fnGroupsel = opt2fn_null("-is", NFILE, fnm);
 +
 +    bMinSet  = opt2parg_bSet("-min",  asize(pa), pa);
 +    bMaxSet  = opt2parg_bSet("-max",  asize(pa), pa);
 +    bAutoSet = opt2parg_bSet("-auto",  asize(pa), pa);
 +    if ( (bMinSet || bMaxSet) && bAutoSet)
 +    {
 +        gmx_fatal(FARGS, "With -auto, do not give -min or -max\n");
 +    }
 +
 +    if ( (bMinSet && !bMaxSet) || (!bMinSet && bMaxSet))
 +    {
 +        gmx_fatal(FARGS, "When giving -min, you must give -max (and vice versa), too\n");
 +    }
 +
 +    if (bMinSet && opt.bAuto)
 +    {
 +        printf("Note: min and max given, switching off -auto.\n");
 +        opt.bAuto = FALSE;
 +    }
 +
 +    if (opt.bTauIntGiven && opt.bCalcTauInt)
 +    {
 +        gmx_fatal(FARGS, "Either read (option -iiact) or calculate (option -ac) the\n"
 +                  "the autocorrelation times. Not both.");
 +    }
 +
 +    if (opt.tauBootStrap > 0.0 && opt2parg_bSet("-ac", asize(pa), pa))
 +    {
 +        gmx_fatal(FARGS, "Either compute autocorrelation times (ACTs) (option -ac) or "
 +                  "provide it with -bs-tau for bootstrapping. Not Both.\n");
 +    }
 +    if (opt.tauBootStrap > 0.0 && opt2bSet("-iiact", NFILE, fnm))
 +    {
 +        gmx_fatal(FARGS, "Either provide autocorrelation times (ACTs) with file iact-in.dat "
 +                  "(option -iiact) or define all ACTs with -bs-tau for bootstrapping\n. Not Both.");
 +    }
 +
 +    /* Reading gmx4 pull output and tpr files */
 +    if (opt.bTpr || opt.bPullf || opt.bPullx)
 +    {
 +        read_wham_in(opt.fnTpr, &fninTpr, &nfiles, &opt);
 +
 +        fnPull = opt.bPullf ? opt.fnPullf : opt.fnPullx;
 +        read_wham_in(fnPull, &fninPull, &nfiles2, &opt);
 +        printf("Found %d tpr and %d pull %s files in %s and %s, respectively\n",
 +               nfiles, nfiles2, opt.bPullf ? "force" : "position", opt.fnTpr, fnPull);
 +        if (nfiles != nfiles2)
 +        {
 +            gmx_fatal(FARGS, "Found %d file names in %s, but %d in %s\n", nfiles,
 +                      opt.fnTpr, nfiles2, fnPull);
 +        }
 +
 +        /* Read file that selects the pull group to be used */
 +        if (opt.fnGroupsel != NULL)
 +        {
 +            readPullGroupSelection(&opt, fninTpr, nfiles);
 +        }
 +
 +        window = initUmbrellaWindows(nfiles);
 +        read_tpr_pullxf_files(fninTpr, fninPull, nfiles, &header, window, &opt);
 +    }
 +    else
 +    {   /* reading pdo files */
 +        if  (opt.fnGroupsel != NULL)
 +        {
 +            gmx_fatal(FARGS, "Reading a -is file is not supported with PDO input files.\n"
 +                      "Use awk or a similar tool to pick the required pull groups from your PDO files\n");
 +        }
 +        read_wham_in(opt.fnPdo, &fninPdo, &nfiles, &opt);
 +        printf("Found %d pdo files in %s\n", nfiles, opt.fnPdo);
 +        window = initUmbrellaWindows(nfiles);
 +        read_pdo_files(fninPdo, nfiles, &header, window, &opt);
 +    }
 +    nwins = nfiles;
 +
 +    /* enforce equal weight for all histograms? */
 +    if (opt.bHistEq)
 +    {
 +        enforceEqualWeights(window, nwins);
 +    }
 +
 +    /* write histograms */
 +    histout = xvgropen(opt2fn("-hist", NFILE, fnm), "Umbrella histograms",
 +                       "z", "count", opt.oenv);
 +    for (l = 0; l < opt.bins; ++l)
 +    {
 +        fprintf(histout, "%e\t", (double)(l+0.5)/opt.bins*(opt.max-opt.min)+opt.min);
 +        for (i = 0; i < nwins; ++i)
 +        {
 +            for (j = 0; j < window[i].nPull; ++j)
 +            {
 +                fprintf(histout, "%e\t", window[i].Histo[j][l]);
 +            }
 +        }
 +        fprintf(histout, "\n");
 +    }
 +    gmx_ffclose(histout);
 +    printf("Wrote %s\n", opt2fn("-hist", NFILE, fnm));
 +    if (opt.bHistOnly)
 +    {
 +        printf("Wrote histograms to %s, now exiting.\n", opt2fn("-hist", NFILE, fnm));
 +        return 0;
 +    }
 +
 +    /* Using tabulated umbrella potential */
 +    if (opt.bTab)
 +    {
 +        setup_tab(opt2fn("-tab", NFILE, fnm), &opt);
 +    }
 +
 +    /* Integrated autocorrelation times provided ? */
 +    if (opt.bTauIntGiven)
 +    {
 +        readIntegratedAutocorrelationTimes(window, nwins, opt2fn("-iiact", NFILE, fnm));
 +    }
 +
 +    /* Compute integrated autocorrelation times */
 +    if (opt.bCalcTauInt)
 +    {
 +        calcIntegratedAutocorrelationTimes(window, nwins, &opt, opt2fn("-oiact", NFILE, fnm));
 +    }
 +
 +    /* calc average and sigma for each histogram
 +       (maybe required for bootstrapping. If not, this is fast anyhow) */
 +    if (opt.nBootStrap && opt.bsMethod == bsMethod_trajGauss)
 +    {
 +        averageSigma(window, nwins);
 +    }
 +
 +    /* Get initial potential by simple integration */
 +    if (opt.bInitPotByIntegration)
 +    {
 +        guessPotByIntegration(window, nwins, &opt);
 +    }
 +
 +    /* Check if complete reaction coordinate is covered */
 +    checkReactionCoordinateCovered(window, nwins, &opt);
 +
 +    /* Calculate profile */
 +    snew(profile, opt.bins);
 +    if (opt.verbose)
 +    {
 +        opt.stepchange = 1;
 +    }
 +    i = 0;
 +    do
 +    {
 +        if ( (i%opt.stepUpdateContrib) == 0)
 +        {
 +            setup_acc_wham(profile, window, nwins, &opt);
 +        }
 +        if (maxchange < opt.Tolerance)
 +        {
 +            bExact = TRUE;
 +            /* if (opt.verbose) */
 +            printf("Switched to exact iteration in iteration %d\n", i);
 +        }
 +        calc_profile(profile, window, nwins, &opt, bExact);
 +        if (((i%opt.stepchange) == 0 || i == 1) && i != 0)
 +        {
 +            printf("\t%4d) Maximum change %e\n", i, maxchange);
 +        }
 +        i++;
 +    }
 +    while ( (maxchange = calc_z(profile, window, nwins, &opt, bExact)) > opt.Tolerance || !bExact);
 +    printf("Converged in %d iterations. Final maximum change %g\n", i, maxchange);
 +
 +    /* calc error from Kumar's formula */
 +    /* Unclear how the error propagates along reaction coordinate, therefore
 +       commented out  */
 +    /* calc_error_kumar(profile,window, nwins,&opt); */
 +
 +    /* Write profile in energy units? */
 +    if (opt.bLog)
 +    {
 +        prof_normalization_and_unit(profile, &opt);
 +        strcpy(ylabel, en_unit_label[opt.unit]);
 +        strcpy(title, "Umbrella potential");
 +    }
 +    else
 +    {
 +        strcpy(ylabel, "Density of states");
 +        strcpy(title, "Density of states");
 +    }
 +
 +    /* symmetrize profile around z=0? */
 +    if (opt.bSym)
 +    {
 +        symmetrizeProfile(profile, &opt);
 +    }
 +
 +    /* write profile or density of states */
 +    profout = xvgropen(opt2fn("-o", NFILE, fnm), title, "z", ylabel, opt.oenv);
 +    for (i = 0; i < opt.bins; ++i)
 +    {
 +        fprintf(profout, "%e\t%e\n", (double)(i+0.5)/opt.bins*(opt.max-opt.min)+opt.min, profile[i]);
 +    }
 +    gmx_ffclose(profout);
 +    printf("Wrote %s\n", opt2fn("-o", NFILE, fnm));
 +
 +    /* Bootstrap Method */
 +    if (opt.nBootStrap)
 +    {
 +        do_bootstrapping(opt2fn("-bsres", NFILE, fnm), opt2fn("-bsprof", NFILE, fnm),
 +                         opt2fn("-hist", NFILE, fnm),
 +                         ylabel, profile, window, nwins, &opt);
 +    }
 +
 +    sfree(profile);
 +    freeUmbrellaWindows(window, nfiles);
 +
 +    printf("\nIn case you use results from g_wham for a publication, please cite:\n");
 +    please_cite(stdout, "Hub2010");
 +
 +    return 0;
 +}
index 6d696e93f7ade05a3c9d51543dcef58109ea999e,0000000000000000000000000000000000000000..c465183bb3e1789bcfde934f888512bdecbdeff1
mode 100644,000000..100644
--- /dev/null
@@@ -1,4760 -1,0 +1,4770 @@@
-     int  i, m, aO, aH1, aH2, aD, aS, type, type0;
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <assert.h>
 +#include "physics.h"
 +#include "vec.h"
 +#include "gromacs/math/utilities.h"
 +#include "txtdump.h"
 +#include "bondf.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "pbc.h"
 +#include "ns.h"
 +#include "macros.h"
 +#include "names.h"
 +#include "gmx_fatal.h"
 +#include "mshift.h"
 +#include "main.h"
 +#include "disre.h"
 +#include "orires.h"
 +#include "force.h"
 +#include "nonbonded.h"
 +#include "restcbt.h"
 +
 +#include "gromacs/simd/simd.h"
 +#include "gromacs/simd/simd_math.h"
 +#include "gromacs/simd/vector_operations.h"
 +
 +/* Find a better place for this? */
 +const int cmap_coeff_matrix[] = {
 +    1, 0, -3,  2, 0, 0,  0,  0, -3,  0,  9, -6,  2,  0, -6,  4,
 +    0, 0,  0,  0, 0, 0,  0,  0,  3,  0, -9,  6, -2,  0,  6, -4,
 +    0, 0,  0,  0, 0, 0,  0,  0,  0,  0,  9, -6,  0,  0, -6,  4,
 +    0, 0,  3, -2, 0, 0,  0,  0,  0,  0, -9,  6,  0,  0,  6, -4,
 +    0, 0,  0,  0, 1, 0, -3,  2, -2,  0,  6, -4,  1,  0, -3,  2,
 +    0, 0,  0,  0, 0, 0,  0,  0, -1,  0,  3, -2,  1,  0, -3,  2,
 +    0, 0,  0,  0, 0, 0,  0,  0,  0,  0, -3,  2,  0,  0,  3, -2,
 +    0, 0,  0,  0, 0, 0,  3, -2,  0,  0, -6,  4,  0,  0,  3, -2,
 +    0, 1, -2,  1, 0, 0,  0,  0,  0, -3,  6, -3,  0,  2, -4,  2,
 +    0, 0,  0,  0, 0, 0,  0,  0,  0,  3, -6,  3,  0, -2,  4, -2,
 +    0, 0,  0,  0, 0, 0,  0,  0,  0,  0, -3,  3,  0,  0,  2, -2,
 +    0, 0, -1,  1, 0, 0,  0,  0,  0,  0,  3, -3,  0,  0, -2,  2,
 +    0, 0,  0,  0, 0, 1, -2,  1,  0, -2,  4, -2,  0,  1, -2,  1,
 +    0, 0,  0,  0, 0, 0,  0,  0,  0, -1,  2, -1,  0,  1, -2,  1,
 +    0, 0,  0,  0, 0, 0,  0,  0,  0,  0,  1, -1,  0,  0, -1,  1,
 +    0, 0,  0,  0, 0, 0, -1,  1,  0,  0,  2, -2,  0,  0, -1,  1
 +};
 +
 +
 +
 +int glatnr(int *global_atom_index, int i)
 +{
 +    int atnr;
 +
 +    if (global_atom_index == NULL)
 +    {
 +        atnr = i + 1;
 +    }
 +    else
 +    {
 +        atnr = global_atom_index[i] + 1;
 +    }
 +
 +    return atnr;
 +}
 +
 +static int pbc_rvec_sub(const t_pbc *pbc, const rvec xi, const rvec xj, rvec dx)
 +{
 +    if (pbc)
 +    {
 +        return pbc_dx_aiuc(pbc, xi, xj, dx);
 +    }
 +    else
 +    {
 +        rvec_sub(xi, xj, dx);
 +        return CENTRAL;
 +    }
 +}
 +
 +#ifdef GMX_SIMD_HAVE_REAL
 +
 +/* SIMD PBC data structure, containing 1/boxdiag and the box vectors */
 +typedef struct {
 +    gmx_simd_real_t inv_bzz;
 +    gmx_simd_real_t inv_byy;
 +    gmx_simd_real_t inv_bxx;
 +    gmx_simd_real_t bzx;
 +    gmx_simd_real_t bzy;
 +    gmx_simd_real_t bzz;
 +    gmx_simd_real_t byx;
 +    gmx_simd_real_t byy;
 +    gmx_simd_real_t bxx;
 +} pbc_simd_t;
 +
 +/* Set the SIMD pbc data from a normal t_pbc struct */
 +static void set_pbc_simd(const t_pbc *pbc, pbc_simd_t *pbc_simd)
 +{
 +    rvec inv_bdiag;
 +    int  d;
 +
 +    /* Setting inv_bdiag to 0 effectively turns off PBC */
 +    clear_rvec(inv_bdiag);
 +    if (pbc != NULL)
 +    {
 +        for (d = 0; d < pbc->ndim_ePBC; d++)
 +        {
 +            inv_bdiag[d] = 1.0/pbc->box[d][d];
 +        }
 +    }
 +
 +    pbc_simd->inv_bzz = gmx_simd_set1_r(inv_bdiag[ZZ]);
 +    pbc_simd->inv_byy = gmx_simd_set1_r(inv_bdiag[YY]);
 +    pbc_simd->inv_bxx = gmx_simd_set1_r(inv_bdiag[XX]);
 +
 +    if (pbc != NULL)
 +    {
 +        pbc_simd->bzx = gmx_simd_set1_r(pbc->box[ZZ][XX]);
 +        pbc_simd->bzy = gmx_simd_set1_r(pbc->box[ZZ][YY]);
 +        pbc_simd->bzz = gmx_simd_set1_r(pbc->box[ZZ][ZZ]);
 +        pbc_simd->byx = gmx_simd_set1_r(pbc->box[YY][XX]);
 +        pbc_simd->byy = gmx_simd_set1_r(pbc->box[YY][YY]);
 +        pbc_simd->bxx = gmx_simd_set1_r(pbc->box[XX][XX]);
 +    }
 +    else
 +    {
 +        pbc_simd->bzx = gmx_simd_setzero_r();
 +        pbc_simd->bzy = gmx_simd_setzero_r();
 +        pbc_simd->bzz = gmx_simd_setzero_r();
 +        pbc_simd->byx = gmx_simd_setzero_r();
 +        pbc_simd->byy = gmx_simd_setzero_r();
 +        pbc_simd->bxx = gmx_simd_setzero_r();
 +    }
 +}
 +
 +/* Correct distance vector *dx,*dy,*dz for PBC using SIMD */
 +static gmx_inline void
 +pbc_dx_simd(gmx_simd_real_t *dx, gmx_simd_real_t *dy, gmx_simd_real_t *dz,
 +            const pbc_simd_t *pbc)
 +{
 +    gmx_simd_real_t sh;
 +
 +    sh  = gmx_simd_round_r(gmx_simd_mul_r(*dz, pbc->inv_bzz));
 +    *dx = gmx_simd_fnmadd_r(sh, pbc->bzx, *dx);
 +    *dy = gmx_simd_fnmadd_r(sh, pbc->bzy, *dy);
 +    *dz = gmx_simd_fnmadd_r(sh, pbc->bzz, *dz);
 +
 +    sh  = gmx_simd_round_r(gmx_simd_mul_r(*dy, pbc->inv_byy));
 +    *dx = gmx_simd_fnmadd_r(sh, pbc->byx, *dx);
 +    *dy = gmx_simd_fnmadd_r(sh, pbc->byy, *dy);
 +
 +    sh  = gmx_simd_round_r(gmx_simd_mul_r(*dx, pbc->inv_bxx));
 +    *dx = gmx_simd_fnmadd_r(sh, pbc->bxx, *dx);
 +}
 +
 +#endif /* GMX_SIMD_HAVE_REAL */
 +
 +/*
 + * Morse potential bond by Frank Everdij
 + *
 + * Three parameters needed:
 + *
 + * b0 = equilibrium distance in nm
 + * be = beta in nm^-1 (actually, it's nu_e*Sqrt(2*pi*pi*mu/D_e))
 + * cb = well depth in kJ/mol
 + *
 + * Note: the potential is referenced to be +cb at infinite separation
 + *       and zero at the equilibrium distance!
 + */
 +
 +real morse_bonds(int nbonds,
 +                 const t_iatom forceatoms[], const t_iparams forceparams[],
 +                 const rvec x[], rvec f[], rvec fshift[],
 +                 const t_pbc *pbc, const t_graph *g,
 +                 real lambda, real *dvdlambda,
 +                 const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +                 int gmx_unused *global_atom_index)
 +{
 +    const real one = 1.0;
 +    const real two = 2.0;
 +    real       dr, dr2, temp, omtemp, cbomtemp, fbond, vbond, fij, vtot;
 +    real       b0, be, cb, b0A, beA, cbA, b0B, beB, cbB, L1;
 +    rvec       dx;
 +    int        i, m, ki, type, ai, aj;
 +    ivec       dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +
 +        b0A   = forceparams[type].morse.b0A;
 +        beA   = forceparams[type].morse.betaA;
 +        cbA   = forceparams[type].morse.cbA;
 +
 +        b0B   = forceparams[type].morse.b0B;
 +        beB   = forceparams[type].morse.betaB;
 +        cbB   = forceparams[type].morse.cbB;
 +
 +        L1 = one-lambda;                            /* 1 */
 +        b0 = L1*b0A + lambda*b0B;                   /* 3 */
 +        be = L1*beA + lambda*beB;                   /* 3 */
 +        cb = L1*cbA + lambda*cbB;                   /* 3 */
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx); /*   3          */
 +        dr2  = iprod(dx, dx);                       /*   5          */
 +        dr   = dr2*gmx_invsqrt(dr2);                /*  10          */
 +        temp = exp(-be*(dr-b0));                    /*  12          */
 +
 +        if (temp == one)
 +        {
 +            /* bonds are constrainted. This may _not_ include bond constraints if they are lambda dependent */
 +            *dvdlambda += cbB-cbA;
 +            continue;
 +        }
 +
 +        omtemp    = one-temp;                                                                                        /*   1          */
 +        cbomtemp  = cb*omtemp;                                                                                       /*   1          */
 +        vbond     = cbomtemp*omtemp;                                                                                 /*   1          */
 +        fbond     = -two*be*temp*cbomtemp*gmx_invsqrt(dr2);                                                          /*   9          */
 +        vtot     += vbond;                                                                                           /*   1          */
 +
 +        *dvdlambda += (cbB - cbA) * omtemp * omtemp - (2-2*omtemp)*omtemp * cb * ((b0B-b0A)*be - (beB-beA)*(dr-b0)); /* 15 */
 +
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +
 +        for (m = 0; (m < DIM); m++)                    /*  15          */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }                                         /*  83 TOTAL    */
 +    return vtot;
 +}
 +
 +real cubic_bonds(int nbonds,
 +                 const t_iatom forceatoms[], const t_iparams forceparams[],
 +                 const rvec x[], rvec f[], rvec fshift[],
 +                 const t_pbc *pbc, const t_graph *g,
 +                 real gmx_unused lambda, real gmx_unused *dvdlambda,
 +                 const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +                 int gmx_unused *global_atom_index)
 +{
 +    const real three = 3.0;
 +    const real two   = 2.0;
 +    real       kb, b0, kcub;
 +    real       dr, dr2, dist, kdist, kdist2, fbond, vbond, fij, vtot;
 +    rvec       dx;
 +    int        i, m, ki, type, ai, aj;
 +    ivec       dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +
 +        b0   = forceparams[type].cubic.b0;
 +        kb   = forceparams[type].cubic.kb;
 +        kcub = forceparams[type].cubic.kcub;
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx);     /*   3          */
 +        dr2  = iprod(dx, dx);                           /*   5          */
 +
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +        dr         = dr2*gmx_invsqrt(dr2);                  /*  10          */
 +        dist       = dr-b0;
 +        kdist      = kb*dist;
 +        kdist2     = kdist*dist;
 +
 +        vbond      = kdist2 + kcub*kdist2*dist;
 +        fbond      = -(two*kdist + three*kdist2*kcub)/dr;
 +
 +        vtot      += vbond;   /* 21 */
 +
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)                    /*  15          */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }                                         /*  54 TOTAL    */
 +    return vtot;
 +}
 +
 +real FENE_bonds(int nbonds,
 +                const t_iatom forceatoms[], const t_iparams forceparams[],
 +                const rvec x[], rvec f[], rvec fshift[],
 +                const t_pbc *pbc, const t_graph *g,
 +                real gmx_unused lambda, real gmx_unused *dvdlambda,
 +                const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +                int *global_atom_index)
 +{
 +    const real half = 0.5;
 +    const real one  = 1.0;
 +    real       bm, kb;
 +    real       dr, dr2, bm2, omdr2obm2, fbond, vbond, fij, vtot;
 +    rvec       dx;
 +    int        i, m, ki, type, ai, aj;
 +    ivec       dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +
 +        bm   = forceparams[type].fene.bm;
 +        kb   = forceparams[type].fene.kb;
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx);     /*   3          */
 +        dr2  = iprod(dx, dx);                           /*   5          */
 +
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +        bm2 = bm*bm;
 +
 +        if (dr2 >= bm2)
 +        {
 +            gmx_fatal(FARGS,
 +                      "r^2 (%f) >= bm^2 (%f) in FENE bond between atoms %d and %d",
 +                      dr2, bm2,
 +                      glatnr(global_atom_index, ai),
 +                      glatnr(global_atom_index, aj));
 +        }
 +
 +        omdr2obm2  = one - dr2/bm2;
 +
 +        vbond      = -half*kb*bm2*log(omdr2obm2);
 +        fbond      = -kb/omdr2obm2;
 +
 +        vtot      += vbond;   /* 35 */
 +
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)                    /*  15          */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }                                         /*  58 TOTAL    */
 +    return vtot;
 +}
 +
 +real harmonic(real kA, real kB, real xA, real xB, real x, real lambda,
 +              real *V, real *F)
 +{
 +    const real half = 0.5;
 +    real       L1, kk, x0, dx, dx2;
 +    real       v, f, dvdlambda;
 +
 +    L1    = 1.0-lambda;
 +    kk    = L1*kA+lambda*kB;
 +    x0    = L1*xA+lambda*xB;
 +
 +    dx    = x-x0;
 +    dx2   = dx*dx;
 +
 +    f          = -kk*dx;
 +    v          = half*kk*dx2;
 +    dvdlambda  = half*(kB-kA)*dx2 + (xA-xB)*kk*dx;
 +
 +    *F    = f;
 +    *V    = v;
 +
 +    return dvdlambda;
 +
 +    /* That was 19 flops */
 +}
 +
 +
 +real bonds(int nbonds,
 +           const t_iatom forceatoms[], const t_iparams forceparams[],
 +           const rvec x[], rvec f[], rvec fshift[],
 +           const t_pbc *pbc, const t_graph *g,
 +           real lambda, real *dvdlambda,
 +           const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +           int gmx_unused *global_atom_index)
 +{
 +    int  i, m, ki, ai, aj, type;
 +    real dr, dr2, fbond, vbond, fij, vtot;
 +    rvec dx;
 +    ivec dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx); /*   3      */
 +        dr2  = iprod(dx, dx);                       /*   5            */
 +        dr   = dr2*gmx_invsqrt(dr2);                /*  10            */
 +
 +        *dvdlambda += harmonic(forceparams[type].harmonic.krA,
 +                               forceparams[type].harmonic.krB,
 +                               forceparams[type].harmonic.rA,
 +                               forceparams[type].harmonic.rB,
 +                               dr, lambda, &vbond, &fbond); /*  19  */
 +
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +
 +        vtot  += vbond;            /* 1*/
 +        fbond *= gmx_invsqrt(dr2); /*   6             */
 +#ifdef DEBUG
 +        if (debug)
 +        {
 +            fprintf(debug, "BONDS: dr = %10g  vbond = %10g  fbond = %10g\n",
 +                    dr, vbond, fbond);
 +        }
 +#endif
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)     /*  15                */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }               /* 59 TOTAL       */
 +    return vtot;
 +}
 +
 +real restraint_bonds(int nbonds,
 +                     const t_iatom forceatoms[], const t_iparams forceparams[],
 +                     const rvec x[], rvec f[], rvec fshift[],
 +                     const t_pbc *pbc, const t_graph *g,
 +                     real lambda, real *dvdlambda,
 +                     const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +                     int gmx_unused *global_atom_index)
 +{
 +    int  i, m, ki, ai, aj, type;
 +    real dr, dr2, fbond, vbond, fij, vtot;
 +    real L1;
 +    real low, dlow, up1, dup1, up2, dup2, k, dk;
 +    real drh, drh2;
 +    rvec dx;
 +    ivec dt;
 +
 +    L1   = 1.0 - lambda;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx); /*   3      */
 +        dr2  = iprod(dx, dx);                       /*   5            */
 +        dr   = dr2*gmx_invsqrt(dr2);                /*  10            */
 +
 +        low  = L1*forceparams[type].restraint.lowA + lambda*forceparams[type].restraint.lowB;
 +        dlow =   -forceparams[type].restraint.lowA +        forceparams[type].restraint.lowB;
 +        up1  = L1*forceparams[type].restraint.up1A + lambda*forceparams[type].restraint.up1B;
 +        dup1 =   -forceparams[type].restraint.up1A +        forceparams[type].restraint.up1B;
 +        up2  = L1*forceparams[type].restraint.up2A + lambda*forceparams[type].restraint.up2B;
 +        dup2 =   -forceparams[type].restraint.up2A +        forceparams[type].restraint.up2B;
 +        k    = L1*forceparams[type].restraint.kA   + lambda*forceparams[type].restraint.kB;
 +        dk   =   -forceparams[type].restraint.kA   +        forceparams[type].restraint.kB;
 +        /* 24 */
 +
 +        if (dr < low)
 +        {
 +            drh         = dr - low;
 +            drh2        = drh*drh;
 +            vbond       = 0.5*k*drh2;
 +            fbond       = -k*drh;
 +            *dvdlambda += 0.5*dk*drh2 - k*dlow*drh;
 +        } /* 11 */
 +        else if (dr <= up1)
 +        {
 +            vbond = 0;
 +            fbond = 0;
 +        }
 +        else if (dr <= up2)
 +        {
 +            drh         = dr - up1;
 +            drh2        = drh*drh;
 +            vbond       = 0.5*k*drh2;
 +            fbond       = -k*drh;
 +            *dvdlambda += 0.5*dk*drh2 - k*dup1*drh;
 +        } /* 11       */
 +        else
 +        {
 +            drh         = dr - up2;
 +            vbond       = k*(up2 - up1)*(0.5*(up2 - up1) + drh);
 +            fbond       = -k*(up2 - up1);
 +            *dvdlambda += dk*(up2 - up1)*(0.5*(up2 - up1) + drh)
 +                + k*(dup2 - dup1)*(up2 - up1 + drh)
 +                - k*(up2 - up1)*dup2;
 +        }
 +
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +        vtot  += vbond;            /* 1*/
 +        fbond *= gmx_invsqrt(dr2); /*   6             */
 +#ifdef DEBUG
 +        if (debug)
 +        {
 +            fprintf(debug, "BONDS: dr = %10g  vbond = %10g  fbond = %10g\n",
 +                    dr, vbond, fbond);
 +        }
 +#endif
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)             /*  15                */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }                   /* 59 TOTAL   */
 +
 +    return vtot;
 +}
 +
 +real polarize(int nbonds,
 +              const t_iatom forceatoms[], const t_iparams forceparams[],
 +              const rvec x[], rvec f[], rvec fshift[],
 +              const t_pbc *pbc, const t_graph *g,
 +              real lambda, real *dvdlambda,
 +              const t_mdatoms *md, t_fcdata gmx_unused *fcd,
 +              int gmx_unused *global_atom_index)
 +{
 +    int  i, m, ki, ai, aj, type;
 +    real dr, dr2, fbond, vbond, fij, vtot, ksh;
 +    rvec dx;
 +    ivec dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ksh  = sqr(md->chargeA[aj])*ONE_4PI_EPS0/forceparams[type].polarize.alpha;
 +        if (debug)
 +        {
 +            fprintf(debug, "POL: local ai = %d aj = %d ksh = %.3f\n", ai, aj, ksh);
 +        }
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx);                         /*   3      */
 +        dr2  = iprod(dx, dx);                                               /*   5            */
 +        dr   = dr2*gmx_invsqrt(dr2);                                        /*  10            */
 +
 +        *dvdlambda += harmonic(ksh, ksh, 0, 0, dr, lambda, &vbond, &fbond); /*  19  */
 +
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +        vtot  += vbond;            /* 1*/
 +        fbond *= gmx_invsqrt(dr2); /*   6             */
 +
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)     /*  15                */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }               /* 59 TOTAL       */
 +    return vtot;
 +}
 +
 +real anharm_polarize(int nbonds,
 +                     const t_iatom forceatoms[], const t_iparams forceparams[],
 +                     const rvec x[], rvec f[], rvec fshift[],
 +                     const t_pbc *pbc, const t_graph *g,
 +                     real lambda, real *dvdlambda,
 +                     const t_mdatoms *md, t_fcdata gmx_unused *fcd,
 +                     int gmx_unused *global_atom_index)
 +{
 +    int  i, m, ki, ai, aj, type;
 +    real dr, dr2, fbond, vbond, fij, vtot, ksh, khyp, drcut, ddr, ddr3;
 +    rvec dx;
 +    ivec dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type  = forceatoms[i++];
 +        ai    = forceatoms[i++];
 +        aj    = forceatoms[i++];
 +        ksh   = sqr(md->chargeA[aj])*ONE_4PI_EPS0/forceparams[type].anharm_polarize.alpha; /* 7*/
 +        khyp  = forceparams[type].anharm_polarize.khyp;
 +        drcut = forceparams[type].anharm_polarize.drcut;
 +        if (debug)
 +        {
 +            fprintf(debug, "POL: local ai = %d aj = %d ksh = %.3f\n", ai, aj, ksh);
 +        }
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx);                         /*   3      */
 +        dr2  = iprod(dx, dx);                                               /*   5            */
 +        dr   = dr2*gmx_invsqrt(dr2);                                        /*  10            */
 +
 +        *dvdlambda += harmonic(ksh, ksh, 0, 0, dr, lambda, &vbond, &fbond); /*  19  */
 +
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +        if (dr > drcut)
 +        {
 +            ddr    = dr-drcut;
 +            ddr3   = ddr*ddr*ddr;
 +            vbond += khyp*ddr*ddr3;
 +            fbond -= 4*khyp*ddr3;
 +        }
 +        fbond *= gmx_invsqrt(dr2); /*   6             */
 +        vtot  += vbond;            /* 1*/
 +
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)     /*  15                */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }               /* 72 TOTAL       */
 +    return vtot;
 +}
 +
 +real water_pol(int nbonds,
 +               const t_iatom forceatoms[], const t_iparams forceparams[],
 +               const rvec x[], rvec f[], rvec gmx_unused fshift[],
 +               const t_pbc gmx_unused *pbc, const t_graph gmx_unused *g,
 +               real gmx_unused lambda, real gmx_unused *dvdlambda,
 +               const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +               int gmx_unused *global_atom_index)
 +{
 +    /* This routine implements anisotropic polarizibility for water, through
 +     * a shell connected to a dummy with spring constant that differ in the
 +     * three spatial dimensions in the molecular frame.
 +     */
-             rvec_sub(x[aH1], x[aO], dOH1);
-             rvec_sub(x[aH2], x[aO], dOH2);
-             rvec_sub(x[aH2], x[aH1], dHH);
-             rvec_sub(x[aD], x[aO], dOD);
-             rvec_sub(x[aS], x[aD], dDS);
++    int  i, m, aO, aH1, aH2, aD, aS, type, type0, ki;
++    ivec dt;
 +    rvec dOH1, dOH2, dHH, dOD, dDS, nW, kk, dx, kdx, proj;
 +#ifdef DEBUG
 +    rvec df;
 +#endif
 +    real vtot, fij, r_HH, r_OD, r_nW, tx, ty, tz, qS;
 +
 +    vtot = 0.0;
 +    if (nbonds > 0)
 +    {
 +        type0  = forceatoms[0];
 +        aS     = forceatoms[5];
 +        qS     = md->chargeA[aS];
 +        kk[XX] = sqr(qS)*ONE_4PI_EPS0/forceparams[type0].wpol.al_x;
 +        kk[YY] = sqr(qS)*ONE_4PI_EPS0/forceparams[type0].wpol.al_y;
 +        kk[ZZ] = sqr(qS)*ONE_4PI_EPS0/forceparams[type0].wpol.al_z;
 +        r_HH   = 1.0/forceparams[type0].wpol.rHH;
 +        r_OD   = 1.0/forceparams[type0].wpol.rOD;
 +        if (debug)
 +        {
 +            fprintf(debug, "WPOL: qS  = %10.5f aS = %5d\n", qS, aS);
 +            fprintf(debug, "WPOL: kk  = %10.3f        %10.3f        %10.3f\n",
 +                    kk[XX], kk[YY], kk[ZZ]);
 +            fprintf(debug, "WPOL: rOH = %10.3f  rHH = %10.3f  rOD = %10.3f\n",
 +                    forceparams[type0].wpol.rOH,
 +                    forceparams[type0].wpol.rHH,
 +                    forceparams[type0].wpol.rOD);
 +        }
 +        for (i = 0; (i < nbonds); i += 6)
 +        {
 +            type = forceatoms[i];
 +            if (type != type0)
 +            {
 +                gmx_fatal(FARGS, "Sorry, type = %d, type0 = %d, file = %s, line = %d",
 +                          type, type0, __FILE__, __LINE__);
 +            }
 +            aO   = forceatoms[i+1];
 +            aH1  = forceatoms[i+2];
 +            aH2  = forceatoms[i+3];
 +            aD   = forceatoms[i+4];
 +            aS   = forceatoms[i+5];
 +
 +            /* Compute vectors describing the water frame */
-                 f[aS][m] += fij;
-                 f[aD][m] -= fij;
++            pbc_rvec_sub(pbc, x[aH1], x[aO], dOH1);
++            pbc_rvec_sub(pbc, x[aH2], x[aO], dOH2);
++            pbc_rvec_sub(pbc, x[aH2], x[aH1], dHH);
++            pbc_rvec_sub(pbc, x[aD], x[aO], dOD);
++            ki = pbc_rvec_sub(pbc, x[aS], x[aD], dDS);
 +            cprod(dOH1, dOH2, nW);
 +
 +            /* Compute inverse length of normal vector
 +             * (this one could be precomputed, but I'm too lazy now)
 +             */
 +            r_nW = gmx_invsqrt(iprod(nW, nW));
 +            /* This is for precision, but does not make a big difference,
 +             * it can go later.
 +             */
 +            r_OD = gmx_invsqrt(iprod(dOD, dOD));
 +
 +            /* Normalize the vectors in the water frame */
 +            svmul(r_nW, nW, nW);
 +            svmul(r_HH, dHH, dHH);
 +            svmul(r_OD, dOD, dOD);
 +
 +            /* Compute displacement of shell along components of the vector */
 +            dx[ZZ] = iprod(dDS, dOD);
 +            /* Compute projection on the XY plane: dDS - dx[ZZ]*dOD */
 +            for (m = 0; (m < DIM); m++)
 +            {
 +                proj[m] = dDS[m]-dx[ZZ]*dOD[m];
 +            }
 +
 +            /*dx[XX] = iprod(dDS,nW);
 +               dx[YY] = iprod(dDS,dHH);*/
 +            dx[XX] = iprod(proj, nW);
 +            for (m = 0; (m < DIM); m++)
 +            {
 +                proj[m] -= dx[XX]*nW[m];
 +            }
 +            dx[YY] = iprod(proj, dHH);
 +            /*#define DEBUG*/
 +#ifdef DEBUG
 +            if (debug)
 +            {
 +                fprintf(debug, "WPOL: dx2=%10g  dy2=%10g  dz2=%10g  sum=%10g  dDS^2=%10g\n",
 +                        sqr(dx[XX]), sqr(dx[YY]), sqr(dx[ZZ]), iprod(dx, dx), iprod(dDS, dDS));
 +                fprintf(debug, "WPOL: dHH=(%10g,%10g,%10g)\n", dHH[XX], dHH[YY], dHH[ZZ]);
 +                fprintf(debug, "WPOL: dOD=(%10g,%10g,%10g), 1/r_OD = %10g\n",
 +                        dOD[XX], dOD[YY], dOD[ZZ], 1/r_OD);
 +                fprintf(debug, "WPOL: nW =(%10g,%10g,%10g), 1/r_nW = %10g\n",
 +                        nW[XX], nW[YY], nW[ZZ], 1/r_nW);
 +                fprintf(debug, "WPOL: dx  =%10g, dy  =%10g, dz  =%10g\n",
 +                        dx[XX], dx[YY], dx[ZZ]);
 +                fprintf(debug, "WPOL: dDSx=%10g, dDSy=%10g, dDSz=%10g\n",
 +                        dDS[XX], dDS[YY], dDS[ZZ]);
 +            }
 +#endif
 +            /* Now compute the forces and energy */
 +            kdx[XX] = kk[XX]*dx[XX];
 +            kdx[YY] = kk[YY]*dx[YY];
 +            kdx[ZZ] = kk[ZZ]*dx[ZZ];
 +            vtot   += iprod(dx, kdx);
++
++            if (g)
++            {
++                ivec_sub(SHIFT_IVEC(g, aS), SHIFT_IVEC(g, aD), dt);
++                ki = IVEC2IS(dt);
++            }
++
 +            for (m = 0; (m < DIM); m++)
 +            {
 +                /* This is a tensor operation but written out for speed */
 +                tx        =  nW[m]*kdx[XX];
 +                ty        = dHH[m]*kdx[YY];
 +                tz        = dOD[m]*kdx[ZZ];
 +                fij       = -tx-ty-tz;
 +#ifdef DEBUG
 +                df[m] = fij;
 +#endif
-                 -fm*dpdl[m];
++                f[aS][m]           += fij;
++                f[aD][m]           -= fij;
++                fshift[ki][m]      += fij;
++                fshift[CENTRAL][m] -= fij;
 +            }
 +#ifdef DEBUG
 +            if (debug)
 +            {
 +                fprintf(debug, "WPOL: vwpol=%g\n", 0.5*iprod(dx, kdx));
 +                fprintf(debug, "WPOL: df = (%10g, %10g, %10g)\n", df[XX], df[YY], df[ZZ]);
 +            }
 +#endif
 +        }
 +    }
 +    return 0.5*vtot;
 +}
 +
 +static real do_1_thole(const rvec xi, const rvec xj, rvec fi, rvec fj,
 +                       const t_pbc *pbc, real qq,
 +                       rvec fshift[], real afac)
 +{
 +    rvec r12;
 +    real r12sq, r12_1, r12n, r12bar, v0, v1, fscal, ebar, fff;
 +    int  m, t;
 +
 +    t      = pbc_rvec_sub(pbc, xi, xj, r12);                      /*  3 */
 +
 +    r12sq  = iprod(r12, r12);                                     /*  5 */
 +    r12_1  = gmx_invsqrt(r12sq);                                  /*  5 */
 +    r12bar = afac/r12_1;                                          /*  5 */
 +    v0     = qq*ONE_4PI_EPS0*r12_1;                               /*  2 */
 +    ebar   = exp(-r12bar);                                        /*  5 */
 +    v1     = (1-(1+0.5*r12bar)*ebar);                             /*  4 */
 +    fscal  = ((v0*r12_1)*v1 - v0*0.5*afac*ebar*(r12bar+1))*r12_1; /* 9 */
 +    if (debug)
 +    {
 +        fprintf(debug, "THOLE: v0 = %.3f v1 = %.3f r12= % .3f r12bar = %.3f fscal = %.3f  ebar = %.3f\n", v0, v1, 1/r12_1, r12bar, fscal, ebar);
 +    }
 +
 +    for (m = 0; (m < DIM); m++)
 +    {
 +        fff                 = fscal*r12[m];
 +        fi[m]              += fff;
 +        fj[m]              -= fff;
 +        fshift[t][m]       += fff;
 +        fshift[CENTRAL][m] -= fff;
 +    }             /* 15 */
 +
 +    return v0*v1; /* 1 */
 +    /* 54 */
 +}
 +
 +real thole_pol(int nbonds,
 +               const t_iatom forceatoms[], const t_iparams forceparams[],
 +               const rvec x[], rvec f[], rvec fshift[],
 +               const t_pbc *pbc, const t_graph gmx_unused *g,
 +               real gmx_unused lambda, real gmx_unused *dvdlambda,
 +               const t_mdatoms *md, t_fcdata gmx_unused *fcd,
 +               int gmx_unused *global_atom_index)
 +{
 +    /* Interaction between two pairs of particles with opposite charge */
 +    int  i, type, a1, da1, a2, da2;
 +    real q1, q2, qq, a, al1, al2, afac;
 +    real V = 0;
 +
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type  = forceatoms[i++];
 +        a1    = forceatoms[i++];
 +        da1   = forceatoms[i++];
 +        a2    = forceatoms[i++];
 +        da2   = forceatoms[i++];
 +        q1    = md->chargeA[da1];
 +        q2    = md->chargeA[da2];
 +        a     = forceparams[type].thole.a;
 +        al1   = forceparams[type].thole.alpha1;
 +        al2   = forceparams[type].thole.alpha2;
 +        qq    = q1*q2;
 +        afac  = a*pow(al1*al2, -1.0/6.0);
 +        V    += do_1_thole(x[a1], x[a2], f[a1], f[a2], pbc, qq, fshift, afac);
 +        V    += do_1_thole(x[da1], x[a2], f[da1], f[a2], pbc, -qq, fshift, afac);
 +        V    += do_1_thole(x[a1], x[da2], f[a1], f[da2], pbc, -qq, fshift, afac);
 +        V    += do_1_thole(x[da1], x[da2], f[da1], f[da2], pbc, qq, fshift, afac);
 +    }
 +    /* 290 flops */
 +    return V;
 +}
 +
 +real bond_angle(const rvec xi, const rvec xj, const rvec xk, const t_pbc *pbc,
 +                rvec r_ij, rvec r_kj, real *costh,
 +                int *t1, int *t2)
 +/* Return value is the angle between the bonds i-j and j-k */
 +{
 +    /* 41 FLOPS */
 +    real th;
 +
 +    *t1 = pbc_rvec_sub(pbc, xi, xj, r_ij); /*  3              */
 +    *t2 = pbc_rvec_sub(pbc, xk, xj, r_kj); /*  3              */
 +
 +    *costh = cos_angle(r_ij, r_kj);        /* 25              */
 +    th     = acos(*costh);                 /* 10              */
 +    /* 41 TOTAL       */
 +    return th;
 +}
 +
 +real angles(int nbonds,
 +            const t_iatom forceatoms[], const t_iparams forceparams[],
 +            const rvec x[], rvec f[], rvec fshift[],
 +            const t_pbc *pbc, const t_graph *g,
 +            real lambda, real *dvdlambda,
 +            const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +            int gmx_unused *global_atom_index)
 +{
 +    int  i, ai, aj, ak, t1, t2, type;
 +    rvec r_ij, r_kj;
 +    real cos_theta, cos_theta2, theta, dVdt, va, vtot;
 +    ivec jt, dt_ij, dt_kj;
 +
 +    vtot = 0.0;
 +    for (i = 0; i < nbonds; )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +
 +        theta  = bond_angle(x[ai], x[aj], x[ak], pbc,
 +                            r_ij, r_kj, &cos_theta, &t1, &t2);  /*  41                */
 +
 +        *dvdlambda += harmonic(forceparams[type].harmonic.krA,
 +                               forceparams[type].harmonic.krB,
 +                               forceparams[type].harmonic.rA*DEG2RAD,
 +                               forceparams[type].harmonic.rB*DEG2RAD,
 +                               theta, lambda, &va, &dVdt);  /*  21  */
 +        vtot += va;
 +
 +        cos_theta2 = sqr(cos_theta);
 +        if (cos_theta2 < 1)
 +        {
 +            int  m;
 +            real st, sth;
 +            real cik, cii, ckk;
 +            real nrkj2, nrij2;
 +            real nrkj_1, nrij_1;
 +            rvec f_i, f_j, f_k;
 +
 +            st  = dVdt*gmx_invsqrt(1 - cos_theta2); /*  12            */
 +            sth = st*cos_theta;                     /*   1            */
 +#ifdef DEBUG
 +            if (debug)
 +            {
 +                fprintf(debug, "ANGLES: theta = %10g  vth = %10g  dV/dtheta = %10g\n",
 +                        theta*RAD2DEG, va, dVdt);
 +            }
 +#endif
 +            nrij2 = iprod(r_ij, r_ij);      /*   5            */
 +            nrkj2 = iprod(r_kj, r_kj);      /*   5            */
 +
 +            nrij_1 = gmx_invsqrt(nrij2);    /*  10            */
 +            nrkj_1 = gmx_invsqrt(nrkj2);    /*  10            */
 +
 +            cik = st*nrij_1*nrkj_1;         /*   2            */
 +            cii = sth*nrij_1*nrij_1;        /*   2            */
 +            ckk = sth*nrkj_1*nrkj_1;        /*   2            */
 +
 +            for (m = 0; m < DIM; m++)
 +            {           /*  39                */
 +                f_i[m]    = -(cik*r_kj[m] - cii*r_ij[m]);
 +                f_k[m]    = -(cik*r_ij[m] - ckk*r_kj[m]);
 +                f_j[m]    = -f_i[m] - f_k[m];
 +                f[ai][m] += f_i[m];
 +                f[aj][m] += f_j[m];
 +                f[ak][m] += f_k[m];
 +            }
 +            if (g != NULL)
 +            {
 +                copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +                t1 = IVEC2IS(dt_ij);
 +                t2 = IVEC2IS(dt_kj);
 +            }
 +            rvec_inc(fshift[t1], f_i);
 +            rvec_inc(fshift[CENTRAL], f_j);
 +            rvec_inc(fshift[t2], f_k);
 +        }                                           /* 161 TOTAL      */
 +    }
 +
 +    return vtot;
 +}
 +
 +#ifdef GMX_SIMD_HAVE_REAL
 +
 +/* As angles, but using SIMD to calculate many dihedrals at once.
 + * This routines does not calculate energies and shift forces.
 + */
 +static gmx_inline void
 +angles_noener_simd(int nbonds,
 +                   const t_iatom forceatoms[], const t_iparams forceparams[],
 +                   const rvec x[], rvec f[],
 +                   const t_pbc *pbc, const t_graph gmx_unused *g,
 +                   real gmx_unused lambda,
 +                   const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +                   int gmx_unused *global_atom_index)
 +{
 +    const int            nfa1 = 4;
 +    int                  i, iu, s, m;
 +    int                  type, ai[GMX_SIMD_REAL_WIDTH], aj[GMX_SIMD_REAL_WIDTH];
 +    int                  ak[GMX_SIMD_REAL_WIDTH];
 +    real                 coeff_array[2*GMX_SIMD_REAL_WIDTH+GMX_SIMD_REAL_WIDTH], *coeff;
 +    real                 dr_array[2*DIM*GMX_SIMD_REAL_WIDTH+GMX_SIMD_REAL_WIDTH], *dr;
 +    real                 f_buf_array[6*GMX_SIMD_REAL_WIDTH+GMX_SIMD_REAL_WIDTH], *f_buf;
 +    gmx_simd_real_t      k_S, theta0_S;
 +    gmx_simd_real_t      rijx_S, rijy_S, rijz_S;
 +    gmx_simd_real_t      rkjx_S, rkjy_S, rkjz_S;
 +    gmx_simd_real_t      one_S;
 +    gmx_simd_real_t      min_one_plus_eps_S;
 +    gmx_simd_real_t      rij_rkj_S;
 +    gmx_simd_real_t      nrij2_S, nrij_1_S;
 +    gmx_simd_real_t      nrkj2_S, nrkj_1_S;
 +    gmx_simd_real_t      cos_S, invsin_S;
 +    gmx_simd_real_t      theta_S;
 +    gmx_simd_real_t      st_S, sth_S;
 +    gmx_simd_real_t      cik_S, cii_S, ckk_S;
 +    gmx_simd_real_t      f_ix_S, f_iy_S, f_iz_S;
 +    gmx_simd_real_t      f_kx_S, f_ky_S, f_kz_S;
 +    pbc_simd_t           pbc_simd;
 +
 +    /* Ensure register memory alignment */
 +    coeff = gmx_simd_align_r(coeff_array);
 +    dr    = gmx_simd_align_r(dr_array);
 +    f_buf = gmx_simd_align_r(f_buf_array);
 +
 +    set_pbc_simd(pbc, &pbc_simd);
 +
 +    one_S = gmx_simd_set1_r(1.0);
 +
 +    /* The smallest number > -1 */
 +    min_one_plus_eps_S = gmx_simd_set1_r(-1.0 + 2*GMX_REAL_EPS);
 +
 +    /* nbonds is the number of angles times nfa1, here we step GMX_SIMD_REAL_WIDTH angles */
 +    for (i = 0; (i < nbonds); i += GMX_SIMD_REAL_WIDTH*nfa1)
 +    {
 +        /* Collect atoms for GMX_SIMD_REAL_WIDTH angles.
 +         * iu indexes into forceatoms, we should not let iu go beyond nbonds.
 +         */
 +        iu = i;
 +        for (s = 0; s < GMX_SIMD_REAL_WIDTH; s++)
 +        {
 +            type  = forceatoms[iu];
 +            ai[s] = forceatoms[iu+1];
 +            aj[s] = forceatoms[iu+2];
 +            ak[s] = forceatoms[iu+3];
 +
 +            coeff[s]                     = forceparams[type].harmonic.krA;
 +            coeff[GMX_SIMD_REAL_WIDTH+s] = forceparams[type].harmonic.rA*DEG2RAD;
 +
 +            /* If you can't use pbc_dx_simd below for PBC, e.g. because
 +             * you can't round in SIMD, use pbc_rvec_sub here.
 +             */
 +            /* Store the non PBC corrected distances packed and aligned */
 +            for (m = 0; m < DIM; m++)
 +            {
 +                dr[s +      m *GMX_SIMD_REAL_WIDTH] = x[ai[s]][m] - x[aj[s]][m];
 +                dr[s + (DIM+m)*GMX_SIMD_REAL_WIDTH] = x[ak[s]][m] - x[aj[s]][m];
 +            }
 +
 +            /* At the end fill the arrays with identical entries */
 +            if (iu + nfa1 < nbonds)
 +            {
 +                iu += nfa1;
 +            }
 +        }
 +
 +        k_S       = gmx_simd_load_r(coeff);
 +        theta0_S  = gmx_simd_load_r(coeff+GMX_SIMD_REAL_WIDTH);
 +
 +        rijx_S    = gmx_simd_load_r(dr + 0*GMX_SIMD_REAL_WIDTH);
 +        rijy_S    = gmx_simd_load_r(dr + 1*GMX_SIMD_REAL_WIDTH);
 +        rijz_S    = gmx_simd_load_r(dr + 2*GMX_SIMD_REAL_WIDTH);
 +        rkjx_S    = gmx_simd_load_r(dr + 3*GMX_SIMD_REAL_WIDTH);
 +        rkjy_S    = gmx_simd_load_r(dr + 4*GMX_SIMD_REAL_WIDTH);
 +        rkjz_S    = gmx_simd_load_r(dr + 5*GMX_SIMD_REAL_WIDTH);
 +
 +        pbc_dx_simd(&rijx_S, &rijy_S, &rijz_S, &pbc_simd);
 +        pbc_dx_simd(&rkjx_S, &rkjy_S, &rkjz_S, &pbc_simd);
 +
 +        rij_rkj_S = gmx_simd_iprod_r(rijx_S, rijy_S, rijz_S,
 +                                     rkjx_S, rkjy_S, rkjz_S);
 +
 +        nrij2_S   = gmx_simd_norm2_r(rijx_S, rijy_S, rijz_S);
 +        nrkj2_S   = gmx_simd_norm2_r(rkjx_S, rkjy_S, rkjz_S);
 +
 +        nrij_1_S  = gmx_simd_invsqrt_r(nrij2_S);
 +        nrkj_1_S  = gmx_simd_invsqrt_r(nrkj2_S);
 +
 +        cos_S     = gmx_simd_mul_r(rij_rkj_S, gmx_simd_mul_r(nrij_1_S, nrkj_1_S));
 +
 +        /* To allow for 180 degrees, we take the max of cos and -1 + 1bit,
 +         * so we can safely get the 1/sin from 1/sqrt(1 - cos^2).
 +         * This also ensures that rounding errors would cause the argument
 +         * of gmx_simd_acos_r to be < -1.
 +         * Note that we do not take precautions for cos(0)=1, so the outer
 +         * atoms in an angle should not be on top of each other.
 +         */
 +        cos_S     = gmx_simd_max_r(cos_S, min_one_plus_eps_S);
 +
 +        theta_S   = gmx_simd_acos_r(cos_S);
 +
 +        invsin_S  = gmx_simd_invsqrt_r(gmx_simd_sub_r(one_S, gmx_simd_mul_r(cos_S, cos_S)));
 +
 +        st_S      = gmx_simd_mul_r(gmx_simd_mul_r(k_S, gmx_simd_sub_r(theta0_S, theta_S)),
 +                                   invsin_S);
 +        sth_S     = gmx_simd_mul_r(st_S, cos_S);
 +
 +        cik_S     = gmx_simd_mul_r(st_S,  gmx_simd_mul_r(nrij_1_S, nrkj_1_S));
 +        cii_S     = gmx_simd_mul_r(sth_S, gmx_simd_mul_r(nrij_1_S, nrij_1_S));
 +        ckk_S     = gmx_simd_mul_r(sth_S, gmx_simd_mul_r(nrkj_1_S, nrkj_1_S));
 +
 +        f_ix_S    = gmx_simd_mul_r(cii_S, rijx_S);
 +        f_ix_S    = gmx_simd_fnmadd_r(cik_S, rkjx_S, f_ix_S);
 +        f_iy_S    = gmx_simd_mul_r(cii_S, rijy_S);
 +        f_iy_S    = gmx_simd_fnmadd_r(cik_S, rkjy_S, f_iy_S);
 +        f_iz_S    = gmx_simd_mul_r(cii_S, rijz_S);
 +        f_iz_S    = gmx_simd_fnmadd_r(cik_S, rkjz_S, f_iz_S);
 +        f_kx_S    = gmx_simd_mul_r(ckk_S, rkjx_S);
 +        f_kx_S    = gmx_simd_fnmadd_r(cik_S, rijx_S, f_kx_S);
 +        f_ky_S    = gmx_simd_mul_r(ckk_S, rkjy_S);
 +        f_ky_S    = gmx_simd_fnmadd_r(cik_S, rijy_S, f_ky_S);
 +        f_kz_S    = gmx_simd_mul_r(ckk_S, rkjz_S);
 +        f_kz_S    = gmx_simd_fnmadd_r(cik_S, rijz_S, f_kz_S);
 +
 +        gmx_simd_store_r(f_buf + 0*GMX_SIMD_REAL_WIDTH, f_ix_S);
 +        gmx_simd_store_r(f_buf + 1*GMX_SIMD_REAL_WIDTH, f_iy_S);
 +        gmx_simd_store_r(f_buf + 2*GMX_SIMD_REAL_WIDTH, f_iz_S);
 +        gmx_simd_store_r(f_buf + 3*GMX_SIMD_REAL_WIDTH, f_kx_S);
 +        gmx_simd_store_r(f_buf + 4*GMX_SIMD_REAL_WIDTH, f_ky_S);
 +        gmx_simd_store_r(f_buf + 5*GMX_SIMD_REAL_WIDTH, f_kz_S);
 +
 +        iu = i;
 +        s  = 0;
 +        do
 +        {
 +            for (m = 0; m < DIM; m++)
 +            {
 +                f[ai[s]][m] += f_buf[s + m*GMX_SIMD_REAL_WIDTH];
 +                f[aj[s]][m] -= f_buf[s + m*GMX_SIMD_REAL_WIDTH] + f_buf[s + (DIM+m)*GMX_SIMD_REAL_WIDTH];
 +                f[ak[s]][m] += f_buf[s + (DIM+m)*GMX_SIMD_REAL_WIDTH];
 +            }
 +            s++;
 +            iu += nfa1;
 +        }
 +        while (s < GMX_SIMD_REAL_WIDTH && iu < nbonds);
 +    }
 +}
 +
 +#endif /* GMX_SIMD_HAVE_REAL */
 +
 +real linear_angles(int nbonds,
 +                   const t_iatom forceatoms[], const t_iparams forceparams[],
 +                   const rvec x[], rvec f[], rvec fshift[],
 +                   const t_pbc *pbc, const t_graph *g,
 +                   real lambda, real *dvdlambda,
 +                   const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +                   int gmx_unused *global_atom_index)
 +{
 +    int  i, m, ai, aj, ak, t1, t2, type;
 +    rvec f_i, f_j, f_k;
 +    real L1, kA, kB, aA, aB, dr, dr2, va, vtot, a, b, klin;
 +    ivec jt, dt_ij, dt_kj;
 +    rvec r_ij, r_kj, r_ik, dx;
 +
 +    L1   = 1-lambda;
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +
 +        kA   = forceparams[type].linangle.klinA;
 +        kB   = forceparams[type].linangle.klinB;
 +        klin = L1*kA + lambda*kB;
 +
 +        aA   = forceparams[type].linangle.aA;
 +        aB   = forceparams[type].linangle.aB;
 +        a    = L1*aA+lambda*aB;
 +        b    = 1-a;
 +
 +        t1 = pbc_rvec_sub(pbc, x[ai], x[aj], r_ij);
 +        t2 = pbc_rvec_sub(pbc, x[ak], x[aj], r_kj);
 +        rvec_sub(r_ij, r_kj, r_ik);
 +
 +        dr2 = 0;
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            dr        = -a * r_ij[m] - b * r_kj[m];
 +            dr2      += dr*dr;
 +            dx[m]     = dr;
 +            f_i[m]    = a*klin*dr;
 +            f_k[m]    = b*klin*dr;
 +            f_j[m]    = -(f_i[m]+f_k[m]);
 +            f[ai][m] += f_i[m];
 +            f[aj][m] += f_j[m];
 +            f[ak][m] += f_k[m];
 +        }
 +        va          = 0.5*klin*dr2;
 +        *dvdlambda += 0.5*(kB-kA)*dr2 + klin*(aB-aA)*iprod(dx, r_ik);
 +
 +        vtot += va;
 +
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +            ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +            ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +            t1 = IVEC2IS(dt_ij);
 +            t2 = IVEC2IS(dt_kj);
 +        }
 +        rvec_inc(fshift[t1], f_i);
 +        rvec_inc(fshift[CENTRAL], f_j);
 +        rvec_inc(fshift[t2], f_k);
 +    }                                         /* 57 TOTAL     */
 +    return vtot;
 +}
 +
 +real urey_bradley(int nbonds,
 +                  const t_iatom forceatoms[], const t_iparams forceparams[],
 +                  const rvec x[], rvec f[], rvec fshift[],
 +                  const t_pbc *pbc, const t_graph *g,
 +                  real lambda, real *dvdlambda,
 +                  const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +                  int gmx_unused *global_atom_index)
 +{
 +    int  i, m, ai, aj, ak, t1, t2, type, ki;
 +    rvec r_ij, r_kj, r_ik;
 +    real cos_theta, cos_theta2, theta;
 +    real dVdt, va, vtot, dr, dr2, vbond, fbond, fik;
 +    real kthA, th0A, kUBA, r13A, kthB, th0B, kUBB, r13B;
 +    ivec jt, dt_ij, dt_kj, dt_ik;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type  = forceatoms[i++];
 +        ai    = forceatoms[i++];
 +        aj    = forceatoms[i++];
 +        ak    = forceatoms[i++];
 +        th0A  = forceparams[type].u_b.thetaA*DEG2RAD;
 +        kthA  = forceparams[type].u_b.kthetaA;
 +        r13A  = forceparams[type].u_b.r13A;
 +        kUBA  = forceparams[type].u_b.kUBA;
 +        th0B  = forceparams[type].u_b.thetaB*DEG2RAD;
 +        kthB  = forceparams[type].u_b.kthetaB;
 +        r13B  = forceparams[type].u_b.r13B;
 +        kUBB  = forceparams[type].u_b.kUBB;
 +
 +        theta  = bond_angle(x[ai], x[aj], x[ak], pbc,
 +                            r_ij, r_kj, &cos_theta, &t1, &t2);                     /*  41             */
 +
 +        *dvdlambda += harmonic(kthA, kthB, th0A, th0B, theta, lambda, &va, &dVdt); /*  21  */
 +        vtot       += va;
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[ak], r_ik);                               /*   3      */
 +        dr2  = iprod(r_ik, r_ik);                                                   /*   5            */
 +        dr   = dr2*gmx_invsqrt(dr2);                                                /*  10            */
 +
 +        *dvdlambda += harmonic(kUBA, kUBB, r13A, r13B, dr, lambda, &vbond, &fbond); /*  19  */
 +
 +        cos_theta2 = sqr(cos_theta);                                                /*   1            */
 +        if (cos_theta2 < 1)
 +        {
 +            real st, sth;
 +            real cik, cii, ckk;
 +            real nrkj2, nrij2;
 +            rvec f_i, f_j, f_k;
 +
 +            st  = dVdt*gmx_invsqrt(1 - cos_theta2); /*  12            */
 +            sth = st*cos_theta;                     /*   1            */
 +#ifdef DEBUG
 +            if (debug)
 +            {
 +                fprintf(debug, "ANGLES: theta = %10g  vth = %10g  dV/dtheta = %10g\n",
 +                        theta*RAD2DEG, va, dVdt);
 +            }
 +#endif
 +            nrkj2 = iprod(r_kj, r_kj);  /*   5                */
 +            nrij2 = iprod(r_ij, r_ij);
 +
 +            cik = st*gmx_invsqrt(nrkj2*nrij2); /*  12         */
 +            cii = sth/nrij2;                   /*  10         */
 +            ckk = sth/nrkj2;                   /*  10         */
 +
 +            for (m = 0; (m < DIM); m++)        /*  39         */
 +            {
 +                f_i[m]    = -(cik*r_kj[m]-cii*r_ij[m]);
 +                f_k[m]    = -(cik*r_ij[m]-ckk*r_kj[m]);
 +                f_j[m]    = -f_i[m]-f_k[m];
 +                f[ai][m] += f_i[m];
 +                f[aj][m] += f_j[m];
 +                f[ak][m] += f_k[m];
 +            }
 +            if (g)
 +            {
 +                copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +                t1 = IVEC2IS(dt_ij);
 +                t2 = IVEC2IS(dt_kj);
 +            }
 +            rvec_inc(fshift[t1], f_i);
 +            rvec_inc(fshift[CENTRAL], f_j);
 +            rvec_inc(fshift[t2], f_k);
 +        }                                       /* 161 TOTAL  */
 +        /* Time for the bond calculations */
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +        vtot  += vbond;            /* 1*/
 +        fbond *= gmx_invsqrt(dr2); /*   6             */
 +
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, ak), dt_ik);
 +            ki = IVEC2IS(dt_ik);
 +        }
 +        for (m = 0; (m < DIM); m++)     /*  15                */
 +        {
 +            fik                 = fbond*r_ik[m];
 +            f[ai][m]           += fik;
 +            f[ak][m]           -= fik;
 +            fshift[ki][m]      += fik;
 +            fshift[CENTRAL][m] -= fik;
 +        }
 +    }
 +    return vtot;
 +}
 +
 +real quartic_angles(int nbonds,
 +                    const t_iatom forceatoms[], const t_iparams forceparams[],
 +                    const rvec x[], rvec f[], rvec fshift[],
 +                    const t_pbc *pbc, const t_graph *g,
 +                    real gmx_unused lambda, real gmx_unused *dvdlambda,
 +                    const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +                    int gmx_unused *global_atom_index)
 +{
 +    int  i, j, ai, aj, ak, t1, t2, type;
 +    rvec r_ij, r_kj;
 +    real cos_theta, cos_theta2, theta, dt, dVdt, va, dtp, c, vtot;
 +    ivec jt, dt_ij, dt_kj;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +
 +        theta  = bond_angle(x[ai], x[aj], x[ak], pbc,
 +                            r_ij, r_kj, &cos_theta, &t1, &t2); /*  41         */
 +
 +        dt = theta - forceparams[type].qangle.theta*DEG2RAD;   /* 2          */
 +
 +        dVdt = 0;
 +        va   = forceparams[type].qangle.c[0];
 +        dtp  = 1.0;
 +        for (j = 1; j <= 4; j++)
 +        {
 +            c     = forceparams[type].qangle.c[j];
 +            dVdt -= j*c*dtp;
 +            dtp  *= dt;
 +            va   += c*dtp;
 +        }
 +        /* 20 */
 +
 +        vtot += va;
 +
 +        cos_theta2 = sqr(cos_theta);            /*   1                */
 +        if (cos_theta2 < 1)
 +        {
 +            int  m;
 +            real st, sth;
 +            real cik, cii, ckk;
 +            real nrkj2, nrij2;
 +            rvec f_i, f_j, f_k;
 +
 +            st  = dVdt*gmx_invsqrt(1 - cos_theta2); /*  12            */
 +            sth = st*cos_theta;                     /*   1            */
 +#ifdef DEBUG
 +            if (debug)
 +            {
 +                fprintf(debug, "ANGLES: theta = %10g  vth = %10g  dV/dtheta = %10g\n",
 +                        theta*RAD2DEG, va, dVdt);
 +            }
 +#endif
 +            nrkj2 = iprod(r_kj, r_kj);  /*   5                */
 +            nrij2 = iprod(r_ij, r_ij);
 +
 +            cik = st*gmx_invsqrt(nrkj2*nrij2); /*  12         */
 +            cii = sth/nrij2;                   /*  10         */
 +            ckk = sth/nrkj2;                   /*  10         */
 +
 +            for (m = 0; (m < DIM); m++)        /*  39         */
 +            {
 +                f_i[m]    = -(cik*r_kj[m]-cii*r_ij[m]);
 +                f_k[m]    = -(cik*r_ij[m]-ckk*r_kj[m]);
 +                f_j[m]    = -f_i[m]-f_k[m];
 +                f[ai][m] += f_i[m];
 +                f[aj][m] += f_j[m];
 +                f[ak][m] += f_k[m];
 +            }
 +            if (g)
 +            {
 +                copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +                t1 = IVEC2IS(dt_ij);
 +                t2 = IVEC2IS(dt_kj);
 +            }
 +            rvec_inc(fshift[t1], f_i);
 +            rvec_inc(fshift[CENTRAL], f_j);
 +            rvec_inc(fshift[t2], f_k);
 +        }                                       /* 153 TOTAL  */
 +    }
 +    return vtot;
 +}
 +
 +real dih_angle(const rvec xi, const rvec xj, const rvec xk, const rvec xl,
 +               const t_pbc *pbc,
 +               rvec r_ij, rvec r_kj, rvec r_kl, rvec m, rvec n,
 +               real *sign, int *t1, int *t2, int *t3)
 +{
 +    real ipr, phi;
 +
 +    *t1 = pbc_rvec_sub(pbc, xi, xj, r_ij); /*  3        */
 +    *t2 = pbc_rvec_sub(pbc, xk, xj, r_kj); /*  3              */
 +    *t3 = pbc_rvec_sub(pbc, xk, xl, r_kl); /*  3              */
 +
 +    cprod(r_ij, r_kj, m);                  /*  9        */
 +    cprod(r_kj, r_kl, n);                  /*  9              */
 +    phi     = gmx_angle(m, n);             /* 49 (assuming 25 for atan2) */
 +    ipr     = iprod(r_ij, n);              /*  5        */
 +    (*sign) = (ipr < 0.0) ? -1.0 : 1.0;
 +    phi     = (*sign)*phi;                 /*  1              */
 +    /* 82 TOTAL       */
 +    return phi;
 +}
 +
 +
 +#ifdef GMX_SIMD_HAVE_REAL
 +
 +/* As dih_angle above, but calculates 4 dihedral angles at once using SIMD,
 + * also calculates the pre-factor required for the dihedral force update.
 + * Note that bv and buf should be register aligned.
 + */
 +static gmx_inline void
 +dih_angle_simd(const rvec *x,
 +               const int *ai, const int *aj, const int *ak, const int *al,
 +               const pbc_simd_t *pbc,
 +               real *dr,
 +               gmx_simd_real_t *phi_S,
 +               gmx_simd_real_t *mx_S, gmx_simd_real_t *my_S, gmx_simd_real_t *mz_S,
 +               gmx_simd_real_t *nx_S, gmx_simd_real_t *ny_S, gmx_simd_real_t *nz_S,
 +               gmx_simd_real_t *nrkj_m2_S,
 +               gmx_simd_real_t *nrkj_n2_S,
 +               real *p,
 +               real *q)
 +{
 +    int             s, m;
 +    gmx_simd_real_t rijx_S, rijy_S, rijz_S;
 +    gmx_simd_real_t rkjx_S, rkjy_S, rkjz_S;
 +    gmx_simd_real_t rklx_S, rkly_S, rklz_S;
 +    gmx_simd_real_t cx_S, cy_S, cz_S;
 +    gmx_simd_real_t cn_S;
 +    gmx_simd_real_t s_S;
 +    gmx_simd_real_t ipr_S;
 +    gmx_simd_real_t iprm_S, iprn_S;
 +    gmx_simd_real_t nrkj2_S, nrkj_1_S, nrkj_2_S, nrkj_S;
 +    gmx_simd_real_t toler_S;
 +    gmx_simd_real_t p_S, q_S;
 +    gmx_simd_real_t nrkj2_min_S;
 +    gmx_simd_real_t real_eps_S;
 +
 +    /* Used to avoid division by zero.
 +     * We take into acount that we multiply the result by real_eps_S.
 +     */
 +    nrkj2_min_S = gmx_simd_set1_r(GMX_REAL_MIN/(2*GMX_REAL_EPS));
 +
 +    /* The value of the last significant bit (GMX_REAL_EPS is half of that) */
 +    real_eps_S  = gmx_simd_set1_r(2*GMX_REAL_EPS);
 +
 +    for (s = 0; s < GMX_SIMD_REAL_WIDTH; s++)
 +    {
 +        /* If you can't use pbc_dx_simd below for PBC, e.g. because
 +         * you can't round in SIMD, use pbc_rvec_sub here.
 +         */
 +        for (m = 0; m < DIM; m++)
 +        {
 +            dr[s + (0*DIM + m)*GMX_SIMD_REAL_WIDTH] = x[ai[s]][m] - x[aj[s]][m];
 +            dr[s + (1*DIM + m)*GMX_SIMD_REAL_WIDTH] = x[ak[s]][m] - x[aj[s]][m];
 +            dr[s + (2*DIM + m)*GMX_SIMD_REAL_WIDTH] = x[ak[s]][m] - x[al[s]][m];
 +        }
 +    }
 +
 +    rijx_S = gmx_simd_load_r(dr + 0*GMX_SIMD_REAL_WIDTH);
 +    rijy_S = gmx_simd_load_r(dr + 1*GMX_SIMD_REAL_WIDTH);
 +    rijz_S = gmx_simd_load_r(dr + 2*GMX_SIMD_REAL_WIDTH);
 +    rkjx_S = gmx_simd_load_r(dr + 3*GMX_SIMD_REAL_WIDTH);
 +    rkjy_S = gmx_simd_load_r(dr + 4*GMX_SIMD_REAL_WIDTH);
 +    rkjz_S = gmx_simd_load_r(dr + 5*GMX_SIMD_REAL_WIDTH);
 +    rklx_S = gmx_simd_load_r(dr + 6*GMX_SIMD_REAL_WIDTH);
 +    rkly_S = gmx_simd_load_r(dr + 7*GMX_SIMD_REAL_WIDTH);
 +    rklz_S = gmx_simd_load_r(dr + 8*GMX_SIMD_REAL_WIDTH);
 +
 +    pbc_dx_simd(&rijx_S, &rijy_S, &rijz_S, pbc);
 +    pbc_dx_simd(&rkjx_S, &rkjy_S, &rkjz_S, pbc);
 +    pbc_dx_simd(&rklx_S, &rkly_S, &rklz_S, pbc);
 +
 +    gmx_simd_cprod_r(rijx_S, rijy_S, rijz_S,
 +                     rkjx_S, rkjy_S, rkjz_S,
 +                     mx_S, my_S, mz_S);
 +
 +    gmx_simd_cprod_r(rkjx_S, rkjy_S, rkjz_S,
 +                     rklx_S, rkly_S, rklz_S,
 +                     nx_S, ny_S, nz_S);
 +
 +    gmx_simd_cprod_r(*mx_S, *my_S, *mz_S,
 +                     *nx_S, *ny_S, *nz_S,
 +                     &cx_S, &cy_S, &cz_S);
 +
 +    cn_S       = gmx_simd_sqrt_r(gmx_simd_norm2_r(cx_S, cy_S, cz_S));
 +
 +    s_S        = gmx_simd_iprod_r(*mx_S, *my_S, *mz_S, *nx_S, *ny_S, *nz_S);
 +
 +    /* Determine the dihedral angle, the sign might need correction */
 +    *phi_S     = gmx_simd_atan2_r(cn_S, s_S);
 +
 +    ipr_S      = gmx_simd_iprod_r(rijx_S, rijy_S, rijz_S,
 +                                  *nx_S, *ny_S, *nz_S);
 +
 +    iprm_S     = gmx_simd_norm2_r(*mx_S, *my_S, *mz_S);
 +    iprn_S     = gmx_simd_norm2_r(*nx_S, *ny_S, *nz_S);
 +
 +    nrkj2_S    = gmx_simd_norm2_r(rkjx_S, rkjy_S, rkjz_S);
 +
 +    /* Avoid division by zero. When zero, the result is multiplied by 0
 +     * anyhow, so the 3 max below do not affect the final result.
 +     */
 +    nrkj2_S    = gmx_simd_max_r(nrkj2_S, nrkj2_min_S);
 +    nrkj_1_S   = gmx_simd_invsqrt_r(nrkj2_S);
 +    nrkj_2_S   = gmx_simd_mul_r(nrkj_1_S, nrkj_1_S);
 +    nrkj_S     = gmx_simd_mul_r(nrkj2_S, nrkj_1_S);
 +
 +    toler_S    = gmx_simd_mul_r(nrkj2_S, real_eps_S);
 +
 +    /* Here the plain-C code uses a conditional, but we can't do that in SIMD.
 +     * So we take a max with the tolerance instead. Since we multiply with
 +     * m or n later, the max does not affect the results.
 +     */
 +    iprm_S     = gmx_simd_max_r(iprm_S, toler_S);
 +    iprn_S     = gmx_simd_max_r(iprn_S, toler_S);
 +    *nrkj_m2_S = gmx_simd_mul_r(nrkj_S, gmx_simd_inv_r(iprm_S));
 +    *nrkj_n2_S = gmx_simd_mul_r(nrkj_S, gmx_simd_inv_r(iprn_S));
 +
 +    /* Set sign of phi_S with the sign of ipr_S; phi_S is currently positive */
 +    *phi_S     = gmx_simd_xor_sign_r(*phi_S, ipr_S);
 +    p_S        = gmx_simd_iprod_r(rijx_S, rijy_S, rijz_S,
 +                                  rkjx_S, rkjy_S, rkjz_S);
 +    p_S        = gmx_simd_mul_r(p_S, nrkj_2_S);
 +
 +    q_S        = gmx_simd_iprod_r(rklx_S, rkly_S, rklz_S,
 +                                  rkjx_S, rkjy_S, rkjz_S);
 +    q_S        = gmx_simd_mul_r(q_S, nrkj_2_S);
 +
 +    gmx_simd_store_r(p, p_S);
 +    gmx_simd_store_r(q, q_S);
 +}
 +
 +#endif /* GMX_SIMD_HAVE_REAL */
 +
 +
 +void do_dih_fup(int i, int j, int k, int l, real ddphi,
 +                rvec r_ij, rvec r_kj, rvec r_kl,
 +                rvec m, rvec n, rvec f[], rvec fshift[],
 +                const t_pbc *pbc, const t_graph *g,
 +                const rvec x[], int t1, int t2, int t3)
 +{
 +    /* 143 FLOPS */
 +    rvec f_i, f_j, f_k, f_l;
 +    rvec uvec, vvec, svec, dx_jl;
 +    real iprm, iprn, nrkj, nrkj2, nrkj_1, nrkj_2;
 +    real a, b, p, q, toler;
 +    ivec jt, dt_ij, dt_kj, dt_lj;
 +
 +    iprm  = iprod(m, m);       /*  5    */
 +    iprn  = iprod(n, n);       /*  5  */
 +    nrkj2 = iprod(r_kj, r_kj); /*  5  */
 +    toler = nrkj2*GMX_REAL_EPS;
 +    if ((iprm > toler) && (iprn > toler))
 +    {
 +        nrkj_1 = gmx_invsqrt(nrkj2); /* 10    */
 +        nrkj_2 = nrkj_1*nrkj_1;      /*  1    */
 +        nrkj   = nrkj2*nrkj_1;       /*  1    */
 +        a      = -ddphi*nrkj/iprm;   /* 11    */
 +        svmul(a, m, f_i);            /*  3    */
 +        b     = ddphi*nrkj/iprn;     /* 11    */
 +        svmul(b, n, f_l);            /*  3  */
 +        p     = iprod(r_ij, r_kj);   /*  5    */
 +        p    *= nrkj_2;              /*  1    */
 +        q     = iprod(r_kl, r_kj);   /*  5    */
 +        q    *= nrkj_2;              /*  1    */
 +        svmul(p, f_i, uvec);         /*  3    */
 +        svmul(q, f_l, vvec);         /*  3    */
 +        rvec_sub(uvec, vvec, svec);  /*  3    */
 +        rvec_sub(f_i, svec, f_j);    /*  3    */
 +        rvec_add(f_l, svec, f_k);    /*  3    */
 +        rvec_inc(f[i], f_i);         /*  3    */
 +        rvec_dec(f[j], f_j);         /*  3    */
 +        rvec_dec(f[k], f_k);         /*  3    */
 +        rvec_inc(f[l], f_l);         /*  3    */
 +
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, j), jt);
 +            ivec_sub(SHIFT_IVEC(g, i), jt, dt_ij);
 +            ivec_sub(SHIFT_IVEC(g, k), jt, dt_kj);
 +            ivec_sub(SHIFT_IVEC(g, l), jt, dt_lj);
 +            t1 = IVEC2IS(dt_ij);
 +            t2 = IVEC2IS(dt_kj);
 +            t3 = IVEC2IS(dt_lj);
 +        }
 +        else if (pbc)
 +        {
 +            t3 = pbc_rvec_sub(pbc, x[l], x[j], dx_jl);
 +        }
 +        else
 +        {
 +            t3 = CENTRAL;
 +        }
 +
 +        rvec_inc(fshift[t1], f_i);
 +        rvec_dec(fshift[CENTRAL], f_j);
 +        rvec_dec(fshift[t2], f_k);
 +        rvec_inc(fshift[t3], f_l);
 +    }
 +    /* 112 TOTAL    */
 +}
 +
 +/* As do_dih_fup above, but without shift forces */
 +static void
 +do_dih_fup_noshiftf(int i, int j, int k, int l, real ddphi,
 +                    rvec r_ij, rvec r_kj, rvec r_kl,
 +                    rvec m, rvec n, rvec f[])
 +{
 +    rvec f_i, f_j, f_k, f_l;
 +    rvec uvec, vvec, svec, dx_jl;
 +    real iprm, iprn, nrkj, nrkj2, nrkj_1, nrkj_2;
 +    real a, b, p, q, toler;
 +    ivec jt, dt_ij, dt_kj, dt_lj;
 +
 +    iprm  = iprod(m, m);       /*  5    */
 +    iprn  = iprod(n, n);       /*  5  */
 +    nrkj2 = iprod(r_kj, r_kj); /*  5  */
 +    toler = nrkj2*GMX_REAL_EPS;
 +    if ((iprm > toler) && (iprn > toler))
 +    {
 +        nrkj_1 = gmx_invsqrt(nrkj2); /* 10    */
 +        nrkj_2 = nrkj_1*nrkj_1;      /*  1    */
 +        nrkj   = nrkj2*nrkj_1;       /*  1    */
 +        a      = -ddphi*nrkj/iprm;   /* 11    */
 +        svmul(a, m, f_i);            /*  3    */
 +        b     = ddphi*nrkj/iprn;     /* 11    */
 +        svmul(b, n, f_l);            /*  3  */
 +        p     = iprod(r_ij, r_kj);   /*  5    */
 +        p    *= nrkj_2;              /*  1    */
 +        q     = iprod(r_kl, r_kj);   /*  5    */
 +        q    *= nrkj_2;              /*  1    */
 +        svmul(p, f_i, uvec);         /*  3    */
 +        svmul(q, f_l, vvec);         /*  3    */
 +        rvec_sub(uvec, vvec, svec);  /*  3    */
 +        rvec_sub(f_i, svec, f_j);    /*  3    */
 +        rvec_add(f_l, svec, f_k);    /*  3    */
 +        rvec_inc(f[i], f_i);         /*  3    */
 +        rvec_dec(f[j], f_j);         /*  3    */
 +        rvec_dec(f[k], f_k);         /*  3    */
 +        rvec_inc(f[l], f_l);         /*  3    */
 +    }
 +}
 +
 +/* As do_dih_fup_noshiftf above, but with pre-calculated pre-factors */
 +static gmx_inline void
 +do_dih_fup_noshiftf_precalc(int i, int j, int k, int l,
 +                            real p, real q,
 +                            real f_i_x, real f_i_y, real f_i_z,
 +                            real mf_l_x, real mf_l_y, real mf_l_z,
 +                            rvec f[])
 +{
 +    rvec f_i, f_j, f_k, f_l;
 +    rvec uvec, vvec, svec;
 +
 +    f_i[XX] = f_i_x;
 +    f_i[YY] = f_i_y;
 +    f_i[ZZ] = f_i_z;
 +    f_l[XX] = -mf_l_x;
 +    f_l[YY] = -mf_l_y;
 +    f_l[ZZ] = -mf_l_z;
 +    svmul(p, f_i, uvec);
 +    svmul(q, f_l, vvec);
 +    rvec_sub(uvec, vvec, svec);
 +    rvec_sub(f_i, svec, f_j);
 +    rvec_add(f_l, svec, f_k);
 +    rvec_inc(f[i], f_i);
 +    rvec_dec(f[j], f_j);
 +    rvec_dec(f[k], f_k);
 +    rvec_inc(f[l], f_l);
 +}
 +
 +
 +real dopdihs(real cpA, real cpB, real phiA, real phiB, int mult,
 +             real phi, real lambda, real *V, real *F)
 +{
 +    real v, dvdlambda, mdphi, v1, sdphi, ddphi;
 +    real L1   = 1.0 - lambda;
 +    real ph0  = (L1*phiA + lambda*phiB)*DEG2RAD;
 +    real dph0 = (phiB - phiA)*DEG2RAD;
 +    real cp   = L1*cpA + lambda*cpB;
 +
 +    mdphi =  mult*phi - ph0;
 +    sdphi = sin(mdphi);
 +    ddphi = -cp*mult*sdphi;
 +    v1    = 1.0 + cos(mdphi);
 +    v     = cp*v1;
 +
 +    dvdlambda  = (cpB - cpA)*v1 + cp*dph0*sdphi;
 +
 +    *V = v;
 +    *F = ddphi;
 +
 +    return dvdlambda;
 +
 +    /* That was 40 flops */
 +}
 +
 +static void
 +dopdihs_noener(real cpA, real cpB, real phiA, real phiB, int mult,
 +               real phi, real lambda, real *F)
 +{
 +    real mdphi, sdphi, ddphi;
 +    real L1   = 1.0 - lambda;
 +    real ph0  = (L1*phiA + lambda*phiB)*DEG2RAD;
 +    real cp   = L1*cpA + lambda*cpB;
 +
 +    mdphi = mult*phi - ph0;
 +    sdphi = sin(mdphi);
 +    ddphi = -cp*mult*sdphi;
 +
 +    *F = ddphi;
 +
 +    /* That was 20 flops */
 +}
 +
 +static void
 +dopdihs_mdphi(real cpA, real cpB, real phiA, real phiB, int mult,
 +              real phi, real lambda, real *cp, real *mdphi)
 +{
 +    real L1   = 1.0 - lambda;
 +    real ph0  = (L1*phiA + lambda*phiB)*DEG2RAD;
 +
 +    *cp    = L1*cpA + lambda*cpB;
 +
 +    *mdphi = mult*phi - ph0;
 +}
 +
 +static real dopdihs_min(real cpA, real cpB, real phiA, real phiB, int mult,
 +                        real phi, real lambda, real *V, real *F)
 +/* similar to dopdihs, except for a minus sign  *
 + * and a different treatment of mult/phi0       */
 +{
 +    real v, dvdlambda, mdphi, v1, sdphi, ddphi;
 +    real L1   = 1.0 - lambda;
 +    real ph0  = (L1*phiA + lambda*phiB)*DEG2RAD;
 +    real dph0 = (phiB - phiA)*DEG2RAD;
 +    real cp   = L1*cpA + lambda*cpB;
 +
 +    mdphi = mult*(phi-ph0);
 +    sdphi = sin(mdphi);
 +    ddphi = cp*mult*sdphi;
 +    v1    = 1.0-cos(mdphi);
 +    v     = cp*v1;
 +
 +    dvdlambda  = (cpB-cpA)*v1 + cp*dph0*sdphi;
 +
 +    *V = v;
 +    *F = ddphi;
 +
 +    return dvdlambda;
 +
 +    /* That was 40 flops */
 +}
 +
 +real pdihs(int nbonds,
 +           const t_iatom forceatoms[], const t_iparams forceparams[],
 +           const rvec x[], rvec f[], rvec fshift[],
 +           const t_pbc *pbc, const t_graph *g,
 +           real lambda, real *dvdlambda,
 +           const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +           int gmx_unused *global_atom_index)
 +{
 +    int  i, type, ai, aj, ak, al;
 +    int  t1, t2, t3;
 +    rvec r_ij, r_kj, r_kl, m, n;
 +    real phi, sign, ddphi, vpd, vtot;
 +
 +    vtot = 0.0;
 +
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        al   = forceatoms[i++];
 +
 +        phi = dih_angle(x[ai], x[aj], x[ak], x[al], pbc, r_ij, r_kj, r_kl, m, n,
 +                        &sign, &t1, &t2, &t3);  /*  84      */
 +        *dvdlambda += dopdihs(forceparams[type].pdihs.cpA,
 +                              forceparams[type].pdihs.cpB,
 +                              forceparams[type].pdihs.phiA,
 +                              forceparams[type].pdihs.phiB,
 +                              forceparams[type].pdihs.mult,
 +                              phi, lambda, &vpd, &ddphi);
 +
 +        vtot += vpd;
 +        do_dih_fup(ai, aj, ak, al, ddphi, r_ij, r_kj, r_kl, m, n,
 +                   f, fshift, pbc, g, x, t1, t2, t3); /* 112          */
 +
 +#ifdef DEBUG
 +        fprintf(debug, "pdih: (%d,%d,%d,%d) phi=%g\n",
 +                ai, aj, ak, al, phi);
 +#endif
 +    } /* 223 TOTAL  */
 +
 +    return vtot;
 +}
 +
 +void make_dp_periodic(real *dp)  /* 1 flop? */
 +{
 +    /* dp cannot be outside (-pi,pi) */
 +    if (*dp >= M_PI)
 +    {
 +        *dp -= 2*M_PI;
 +    }
 +    else if (*dp < -M_PI)
 +    {
 +        *dp += 2*M_PI;
 +    }
 +    return;
 +}
 +
 +/* As pdihs above, but without calculating energies and shift forces */
 +static void
 +pdihs_noener(int nbonds,
 +             const t_iatom forceatoms[], const t_iparams forceparams[],
 +             const rvec x[], rvec f[],
 +             const t_pbc gmx_unused *pbc, const t_graph gmx_unused *g,
 +             real lambda,
 +             const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +             int gmx_unused *global_atom_index)
 +{
 +    int  i, type, ai, aj, ak, al;
 +    int  t1, t2, t3;
 +    rvec r_ij, r_kj, r_kl, m, n;
 +    real phi, sign, ddphi_tot, ddphi;
 +
 +    for (i = 0; (i < nbonds); )
 +    {
 +        ai   = forceatoms[i+1];
 +        aj   = forceatoms[i+2];
 +        ak   = forceatoms[i+3];
 +        al   = forceatoms[i+4];
 +
 +        phi = dih_angle(x[ai], x[aj], x[ak], x[al], pbc, r_ij, r_kj, r_kl, m, n,
 +                        &sign, &t1, &t2, &t3);
 +
 +        ddphi_tot = 0;
 +
 +        /* Loop over dihedrals working on the same atoms,
 +         * so we avoid recalculating angles and force distributions.
 +         */
 +        do
 +        {
 +            type = forceatoms[i];
 +            dopdihs_noener(forceparams[type].pdihs.cpA,
 +                           forceparams[type].pdihs.cpB,
 +                           forceparams[type].pdihs.phiA,
 +                           forceparams[type].pdihs.phiB,
 +                           forceparams[type].pdihs.mult,
 +                           phi, lambda, &ddphi);
 +            ddphi_tot += ddphi;
 +
 +            i += 5;
 +        }
 +        while (i < nbonds &&
 +               forceatoms[i+1] == ai &&
 +               forceatoms[i+2] == aj &&
 +               forceatoms[i+3] == ak &&
 +               forceatoms[i+4] == al);
 +
 +        do_dih_fup_noshiftf(ai, aj, ak, al, ddphi_tot, r_ij, r_kj, r_kl, m, n, f);
 +    }
 +}
 +
 +
 +#ifdef GMX_SIMD_HAVE_REAL
 +
 +/* As pdihs_noner above, but using SIMD to calculate many dihedrals at once */
 +static void
 +pdihs_noener_simd(int nbonds,
 +                  const t_iatom forceatoms[], const t_iparams forceparams[],
 +                  const rvec x[], rvec f[],
 +                  const t_pbc *pbc, const t_graph gmx_unused *g,
 +                  real gmx_unused lambda,
 +                  const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +                  int gmx_unused *global_atom_index)
 +{
 +    const int             nfa1 = 5;
 +    int                   i, iu, s;
 +    int                   type, ai[GMX_SIMD_REAL_WIDTH], aj[GMX_SIMD_REAL_WIDTH], ak[GMX_SIMD_REAL_WIDTH], al[GMX_SIMD_REAL_WIDTH];
 +    int                   t1[GMX_SIMD_REAL_WIDTH], t2[GMX_SIMD_REAL_WIDTH], t3[GMX_SIMD_REAL_WIDTH];
 +    real                  ddphi;
 +    real                  dr_array[3*DIM*GMX_SIMD_REAL_WIDTH+GMX_SIMD_REAL_WIDTH], *dr;
 +    real                  buf_array[7*GMX_SIMD_REAL_WIDTH+GMX_SIMD_REAL_WIDTH], *buf;
 +    real                 *cp, *phi0, *mult, *phi, *p, *q, *sf_i, *msf_l;
 +    gmx_simd_real_t       phi0_S, phi_S;
 +    gmx_simd_real_t       mx_S, my_S, mz_S;
 +    gmx_simd_real_t       nx_S, ny_S, nz_S;
 +    gmx_simd_real_t       nrkj_m2_S, nrkj_n2_S;
 +    gmx_simd_real_t       cp_S, mdphi_S, mult_S;
 +    gmx_simd_real_t       sin_S, cos_S;
 +    gmx_simd_real_t       mddphi_S;
 +    gmx_simd_real_t       sf_i_S, msf_l_S;
 +    pbc_simd_t            pbc_simd;
 +
 +    /* Ensure SIMD register alignment */
 +    dr  = gmx_simd_align_r(dr_array);
 +    buf = gmx_simd_align_r(buf_array);
 +
 +    /* Extract aligned pointer for parameters and variables */
 +    cp    = buf + 0*GMX_SIMD_REAL_WIDTH;
 +    phi0  = buf + 1*GMX_SIMD_REAL_WIDTH;
 +    mult  = buf + 2*GMX_SIMD_REAL_WIDTH;
 +    p     = buf + 3*GMX_SIMD_REAL_WIDTH;
 +    q     = buf + 4*GMX_SIMD_REAL_WIDTH;
 +    sf_i  = buf + 5*GMX_SIMD_REAL_WIDTH;
 +    msf_l = buf + 6*GMX_SIMD_REAL_WIDTH;
 +
 +    set_pbc_simd(pbc, &pbc_simd);
 +
 +    /* nbonds is the number of dihedrals times nfa1, here we step GMX_SIMD_REAL_WIDTH dihs */
 +    for (i = 0; (i < nbonds); i += GMX_SIMD_REAL_WIDTH*nfa1)
 +    {
 +        /* Collect atoms quadruplets for GMX_SIMD_REAL_WIDTH dihedrals.
 +         * iu indexes into forceatoms, we should not let iu go beyond nbonds.
 +         */
 +        iu = i;
 +        for (s = 0; s < GMX_SIMD_REAL_WIDTH; s++)
 +        {
 +            type  = forceatoms[iu];
 +            ai[s] = forceatoms[iu+1];
 +            aj[s] = forceatoms[iu+2];
 +            ak[s] = forceatoms[iu+3];
 +            al[s] = forceatoms[iu+4];
 +
 +            cp[s]   = forceparams[type].pdihs.cpA;
 +            phi0[s] = forceparams[type].pdihs.phiA*DEG2RAD;
 +            mult[s] = forceparams[type].pdihs.mult;
 +
 +            /* At the end fill the arrays with identical entries */
 +            if (iu + nfa1 < nbonds)
 +            {
 +                iu += nfa1;
 +            }
 +        }
 +
 +        /* Caclulate GMX_SIMD_REAL_WIDTH dihedral angles at once */
 +        dih_angle_simd(x, ai, aj, ak, al, &pbc_simd,
 +                       dr,
 +                       &phi_S,
 +                       &mx_S, &my_S, &mz_S,
 +                       &nx_S, &ny_S, &nz_S,
 +                       &nrkj_m2_S,
 +                       &nrkj_n2_S,
 +                       p, q);
 +
 +        cp_S     = gmx_simd_load_r(cp);
 +        phi0_S   = gmx_simd_load_r(phi0);
 +        mult_S   = gmx_simd_load_r(mult);
 +
 +        mdphi_S  = gmx_simd_sub_r(gmx_simd_mul_r(mult_S, phi_S), phi0_S);
 +
 +        /* Calculate GMX_SIMD_REAL_WIDTH sines at once */
 +        gmx_simd_sincos_r(mdphi_S, &sin_S, &cos_S);
 +        mddphi_S = gmx_simd_mul_r(gmx_simd_mul_r(cp_S, mult_S), sin_S);
 +        sf_i_S   = gmx_simd_mul_r(mddphi_S, nrkj_m2_S);
 +        msf_l_S  = gmx_simd_mul_r(mddphi_S, nrkj_n2_S);
 +
 +        /* After this m?_S will contain f[i] */
 +        mx_S     = gmx_simd_mul_r(sf_i_S, mx_S);
 +        my_S     = gmx_simd_mul_r(sf_i_S, my_S);
 +        mz_S     = gmx_simd_mul_r(sf_i_S, mz_S);
 +
 +        /* After this m?_S will contain -f[l] */
 +        nx_S     = gmx_simd_mul_r(msf_l_S, nx_S);
 +        ny_S     = gmx_simd_mul_r(msf_l_S, ny_S);
 +        nz_S     = gmx_simd_mul_r(msf_l_S, nz_S);
 +
 +        gmx_simd_store_r(dr + 0*GMX_SIMD_REAL_WIDTH, mx_S);
 +        gmx_simd_store_r(dr + 1*GMX_SIMD_REAL_WIDTH, my_S);
 +        gmx_simd_store_r(dr + 2*GMX_SIMD_REAL_WIDTH, mz_S);
 +        gmx_simd_store_r(dr + 3*GMX_SIMD_REAL_WIDTH, nx_S);
 +        gmx_simd_store_r(dr + 4*GMX_SIMD_REAL_WIDTH, ny_S);
 +        gmx_simd_store_r(dr + 5*GMX_SIMD_REAL_WIDTH, nz_S);
 +
 +        iu = i;
 +        s  = 0;
 +        do
 +        {
 +            do_dih_fup_noshiftf_precalc(ai[s], aj[s], ak[s], al[s],
 +                                        p[s], q[s],
 +                                        dr[     XX *GMX_SIMD_REAL_WIDTH+s],
 +                                        dr[     YY *GMX_SIMD_REAL_WIDTH+s],
 +                                        dr[     ZZ *GMX_SIMD_REAL_WIDTH+s],
 +                                        dr[(DIM+XX)*GMX_SIMD_REAL_WIDTH+s],
 +                                        dr[(DIM+YY)*GMX_SIMD_REAL_WIDTH+s],
 +                                        dr[(DIM+ZZ)*GMX_SIMD_REAL_WIDTH+s],
 +                                        f);
 +            s++;
 +            iu += nfa1;
 +        }
 +        while (s < GMX_SIMD_REAL_WIDTH && iu < nbonds);
 +    }
 +}
 +
 +#endif /* GMX_SIMD_HAVE_REAL */
 +
 +
 +real idihs(int nbonds,
 +           const t_iatom forceatoms[], const t_iparams forceparams[],
 +           const rvec x[], rvec f[], rvec fshift[],
 +           const t_pbc *pbc, const t_graph *g,
 +           real lambda, real *dvdlambda,
 +           const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +           int gmx_unused *global_atom_index)
 +{
 +    int  i, type, ai, aj, ak, al;
 +    int  t1, t2, t3;
 +    real phi, phi0, dphi0, ddphi, sign, vtot;
 +    rvec r_ij, r_kj, r_kl, m, n;
 +    real L1, kk, dp, dp2, kA, kB, pA, pB, dvdl_term;
 +
 +    L1        = 1.0-lambda;
 +    dvdl_term = 0;
 +    vtot      = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        al   = forceatoms[i++];
 +
 +        phi = dih_angle(x[ai], x[aj], x[ak], x[al], pbc, r_ij, r_kj, r_kl, m, n,
 +                        &sign, &t1, &t2, &t3);  /*  84                */
 +
 +        /* phi can jump if phi0 is close to Pi/-Pi, which will cause huge
 +         * force changes if we just apply a normal harmonic.
 +         * Instead, we first calculate phi-phi0 and take it modulo (-Pi,Pi).
 +         * This means we will never have the periodicity problem, unless
 +         * the dihedral is Pi away from phiO, which is very unlikely due to
 +         * the potential.
 +         */
 +        kA = forceparams[type].harmonic.krA;
 +        kB = forceparams[type].harmonic.krB;
 +        pA = forceparams[type].harmonic.rA;
 +        pB = forceparams[type].harmonic.rB;
 +
 +        kk    = L1*kA + lambda*kB;
 +        phi0  = (L1*pA + lambda*pB)*DEG2RAD;
 +        dphi0 = (pB - pA)*DEG2RAD;
 +
 +        dp = phi-phi0;
 +
 +        make_dp_periodic(&dp);
 +
 +        dp2 = dp*dp;
 +
 +        vtot += 0.5*kk*dp2;
 +        ddphi = -kk*dp;
 +
 +        dvdl_term += 0.5*(kB - kA)*dp2 - kk*dphi0*dp;
 +
 +        do_dih_fup(ai, aj, ak, al, (real)(-ddphi), r_ij, r_kj, r_kl, m, n,
 +                   f, fshift, pbc, g, x, t1, t2, t3); /* 112          */
 +        /* 218 TOTAL  */
 +#ifdef DEBUG
 +        if (debug)
 +        {
 +            fprintf(debug, "idih: (%d,%d,%d,%d) phi=%g\n",
 +                    ai, aj, ak, al, phi);
 +        }
 +#endif
 +    }
 +
 +    *dvdlambda += dvdl_term;
 +    return vtot;
 +}
 +
 +
 +/*! \brief returns dx, rdist, and dpdl for functions posres() and fbposres()
 + */
 +static void posres_dx(const rvec x, const rvec pos0A, const rvec pos0B,
 +                      const rvec comA_sc, const rvec comB_sc,
 +                      real lambda,
 +                      t_pbc *pbc, int refcoord_scaling, int npbcdim,
 +                      rvec dx, rvec rdist, rvec dpdl)
 +{
 +    int  m, d;
 +    real posA, posB, L1, ref = 0.;
 +    rvec pos;
 +
 +    L1 = 1.0-lambda;
 +
 +    for (m = 0; m < DIM; m++)
 +    {
 +        posA = pos0A[m];
 +        posB = pos0B[m];
 +        if (m < npbcdim)
 +        {
 +            switch (refcoord_scaling)
 +            {
 +                case erscNO:
 +                    ref      = 0;
 +                    rdist[m] = L1*posA + lambda*posB;
 +                    dpdl[m]  = posB - posA;
 +                    break;
 +                case erscALL:
 +                    /* Box relative coordinates are stored for dimensions with pbc */
 +                    posA *= pbc->box[m][m];
 +                    posB *= pbc->box[m][m];
 +                    assert(npbcdim <= DIM);
 +                    for (d = m+1; d < npbcdim; d++)
 +                    {
 +                        posA += pos0A[d]*pbc->box[d][m];
 +                        posB += pos0B[d]*pbc->box[d][m];
 +                    }
 +                    ref      = L1*posA + lambda*posB;
 +                    rdist[m] = 0;
 +                    dpdl[m]  = posB - posA;
 +                    break;
 +                case erscCOM:
 +                    ref      = L1*comA_sc[m] + lambda*comB_sc[m];
 +                    rdist[m] = L1*posA       + lambda*posB;
 +                    dpdl[m]  = comB_sc[m] - comA_sc[m] + posB - posA;
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "No such scaling method implemented");
 +            }
 +        }
 +        else
 +        {
 +            ref      = L1*posA + lambda*posB;
 +            rdist[m] = 0;
 +            dpdl[m]  = posB - posA;
 +        }
 +
 +        /* We do pbc_dx with ref+rdist,
 +         * since with only ref we can be up to half a box vector wrong.
 +         */
 +        pos[m] = ref + rdist[m];
 +    }
 +
 +    if (pbc)
 +    {
 +        pbc_dx(pbc, x, pos, dx);
 +    }
 +    else
 +    {
 +        rvec_sub(x, pos, dx);
 +    }
 +}
 +
 +/*! \brief Adds forces of flat-bottomed positions restraints to f[]
 + *         and fixes vir_diag. Returns the flat-bottomed potential. */
 +real fbposres(int nbonds,
 +              const t_iatom forceatoms[], const t_iparams forceparams[],
 +              const rvec x[], rvec f[], rvec vir_diag,
 +              t_pbc *pbc,
 +              int refcoord_scaling, int ePBC, rvec com)
 +/* compute flat-bottomed positions restraints */
 +{
 +    int              i, ai, m, d, type, npbcdim = 0, fbdim;
 +    const t_iparams *pr;
 +    real             vtot, kk, v;
 +    real             ref = 0, dr, dr2, rpot, rfb, rfb2, fact, invdr;
 +    rvec             com_sc, rdist, pos, dx, dpdl, fm;
 +    gmx_bool         bInvert;
 +
 +    npbcdim = ePBC2npbcdim(ePBC);
 +
 +    if (refcoord_scaling == erscCOM)
 +    {
 +        clear_rvec(com_sc);
 +        for (m = 0; m < npbcdim; m++)
 +        {
 +            assert(npbcdim <= DIM);
 +            for (d = m; d < npbcdim; d++)
 +            {
 +                com_sc[m] += com[d]*pbc->box[d][m];
 +            }
 +        }
 +    }
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        pr   = &forceparams[type];
 +
 +        /* same calculation as for normal posres, but with identical A and B states, and lambda==0 */
 +        posres_dx(x[ai], forceparams[type].fbposres.pos0, forceparams[type].fbposres.pos0,
 +                  com_sc, com_sc, 0.0,
 +                  pbc, refcoord_scaling, npbcdim,
 +                  dx, rdist, dpdl);
 +
 +        clear_rvec(fm);
 +        v = 0.0;
 +
 +        kk   = pr->fbposres.k;
 +        rfb  = pr->fbposres.r;
 +        rfb2 = sqr(rfb);
 +
 +        /* with rfb<0, push particle out of the sphere/cylinder/layer */
 +        bInvert = FALSE;
 +        if (rfb < 0.)
 +        {
 +            bInvert = TRUE;
 +            rfb     = -rfb;
 +        }
 +
 +        switch (pr->fbposres.geom)
 +        {
 +            case efbposresSPHERE:
 +                /* spherical flat-bottom posres */
 +                dr2 = norm2(dx);
 +                if (dr2 > 0.0 &&
 +                    ( (dr2 > rfb2 && bInvert == FALSE ) || (dr2 < rfb2 && bInvert == TRUE ) )
 +                    )
 +                {
 +                    dr   = sqrt(dr2);
 +                    v    = 0.5*kk*sqr(dr - rfb);
 +                    fact = -kk*(dr-rfb)/dr; /* Force pointing to the center pos0 */
 +                    svmul(fact, dx, fm);
 +                }
 +                break;
 +            case efbposresCYLINDER:
 +                /* cylidrical flat-bottom posres in x-y plane. fm[ZZ] = 0. */
 +                dr2 = sqr(dx[XX])+sqr(dx[YY]);
 +                if  (dr2 > 0.0 &&
 +                     ( (dr2 > rfb2 && bInvert == FALSE ) || (dr2 < rfb2 && bInvert == TRUE ) )
 +                     )
 +                {
 +                    dr     = sqrt(dr2);
 +                    invdr  = 1./dr;
 +                    v      = 0.5*kk*sqr(dr - rfb);
 +                    fm[XX] = -kk*(dr-rfb)*dx[XX]*invdr; /* Force pointing to the center */
 +                    fm[YY] = -kk*(dr-rfb)*dx[YY]*invdr;
 +                }
 +                break;
 +            case efbposresX: /* fbdim=XX */
 +            case efbposresY: /* fbdim=YY */
 +            case efbposresZ: /* fbdim=ZZ */
 +                /* 1D flat-bottom potential */
 +                fbdim = pr->fbposres.geom - efbposresX;
 +                dr    = dx[fbdim];
 +                if ( ( dr > rfb && bInvert == FALSE ) || ( 0 < dr && dr < rfb && bInvert == TRUE )  )
 +                {
 +                    v         = 0.5*kk*sqr(dr - rfb);
 +                    fm[fbdim] = -kk*(dr - rfb);
 +                }
 +                else if ( (dr < (-rfb) && bInvert == FALSE ) || ( (-rfb) < dr && dr < 0 && bInvert == TRUE ))
 +                {
 +                    v         = 0.5*kk*sqr(dr + rfb);
 +                    fm[fbdim] = -kk*(dr + rfb);
 +                }
 +                break;
 +        }
 +
 +        vtot += v;
 +
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            f[ai][m]   += fm[m];
 +            /* Here we correct for the pbc_dx which included rdist */
 +            vir_diag[m] -= 0.5*(dx[m] + rdist[m])*fm[m];
 +        }
 +    }
 +
 +    return vtot;
 +}
 +
 +
 +real posres(int nbonds,
 +            const t_iatom forceatoms[], const t_iparams forceparams[],
 +            const rvec x[], rvec f[], rvec vir_diag,
 +            t_pbc *pbc,
 +            real lambda, real *dvdlambda,
 +            int refcoord_scaling, int ePBC, rvec comA, rvec comB)
 +{
 +    int              i, ai, m, d, type, ki, npbcdim = 0;
 +    const t_iparams *pr;
 +    real             L1;
 +    real             vtot, kk, fm;
 +    real             posA, posB, ref = 0;
 +    rvec             comA_sc, comB_sc, rdist, dpdl, pos, dx;
 +    gmx_bool         bForceValid = TRUE;
 +
 +    if ((f == NULL) || (vir_diag == NULL))    /* should both be null together! */
 +    {
 +        bForceValid = FALSE;
 +    }
 +
 +    npbcdim = ePBC2npbcdim(ePBC);
 +
 +    if (refcoord_scaling == erscCOM)
 +    {
 +        clear_rvec(comA_sc);
 +        clear_rvec(comB_sc);
 +        for (m = 0; m < npbcdim; m++)
 +        {
 +            assert(npbcdim <= DIM);
 +            for (d = m; d < npbcdim; d++)
 +            {
 +                comA_sc[m] += comA[d]*pbc->box[d][m];
 +                comB_sc[m] += comB[d]*pbc->box[d][m];
 +            }
 +        }
 +    }
 +
 +    L1 = 1.0 - lambda;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        pr   = &forceparams[type];
 +
 +        /* return dx, rdist, and dpdl */
 +        posres_dx(x[ai], forceparams[type].posres.pos0A, forceparams[type].posres.pos0B,
 +                  comA_sc, comB_sc, lambda,
 +                  pbc, refcoord_scaling, npbcdim,
 +                  dx, rdist, dpdl);
 +
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            kk          = L1*pr->posres.fcA[m] + lambda*pr->posres.fcB[m];
 +            fm          = -kk*dx[m];
 +            vtot       += 0.5*kk*dx[m]*dx[m];
 +            *dvdlambda +=
 +                0.5*(pr->posres.fcB[m] - pr->posres.fcA[m])*dx[m]*dx[m]
++                + fm*dpdl[m];
 +
 +            /* Here we correct for the pbc_dx which included rdist */
 +            if (bForceValid)
 +            {
 +                f[ai][m]    += fm;
 +                vir_diag[m] -= 0.5*(dx[m] + rdist[m])*fm;
 +            }
 +        }
 +    }
 +
 +    return vtot;
 +}
 +
 +static real low_angres(int nbonds,
 +                       const t_iatom forceatoms[], const t_iparams forceparams[],
 +                       const rvec x[], rvec f[], rvec fshift[],
 +                       const t_pbc *pbc, const t_graph *g,
 +                       real lambda, real *dvdlambda,
 +                       gmx_bool bZAxis)
 +{
 +    int  i, m, type, ai, aj, ak, al;
 +    int  t1, t2;
 +    real phi, cos_phi, cos_phi2, vid, vtot, dVdphi;
 +    rvec r_ij, r_kl, f_i, f_k = {0, 0, 0};
 +    real st, sth, nrij2, nrkl2, c, cij, ckl;
 +
 +    ivec dt;
 +    t2 = 0; /* avoid warning with gcc-3.3. It is never used uninitialized */
 +
 +    vtot = 0.0;
 +    ak   = al = 0; /* to avoid warnings */
 +    for (i = 0; i < nbonds; )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        t1   = pbc_rvec_sub(pbc, x[aj], x[ai], r_ij);       /*  3             */
 +        if (!bZAxis)
 +        {
 +            ak   = forceatoms[i++];
 +            al   = forceatoms[i++];
 +            t2   = pbc_rvec_sub(pbc, x[al], x[ak], r_kl);  /*  3              */
 +        }
 +        else
 +        {
 +            r_kl[XX] = 0;
 +            r_kl[YY] = 0;
 +            r_kl[ZZ] = 1;
 +        }
 +
 +        cos_phi = cos_angle(r_ij, r_kl); /* 25                */
 +        phi     = acos(cos_phi);         /* 10           */
 +
 +        *dvdlambda += dopdihs_min(forceparams[type].pdihs.cpA,
 +                                  forceparams[type].pdihs.cpB,
 +                                  forceparams[type].pdihs.phiA,
 +                                  forceparams[type].pdihs.phiB,
 +                                  forceparams[type].pdihs.mult,
 +                                  phi, lambda, &vid, &dVdphi); /*  40  */
 +
 +        vtot += vid;
 +
 +        cos_phi2 = sqr(cos_phi);                /*   1                */
 +        if (cos_phi2 < 1)
 +        {
 +            st    = -dVdphi*gmx_invsqrt(1 - cos_phi2); /*  12         */
 +            sth   = st*cos_phi;                        /*   1         */
 +            nrij2 = iprod(r_ij, r_ij);                 /*   5         */
 +            nrkl2 = iprod(r_kl, r_kl);                 /*   5          */
 +
 +            c   = st*gmx_invsqrt(nrij2*nrkl2);         /*  11         */
 +            cij = sth/nrij2;                           /*  10         */
 +            ckl = sth/nrkl2;                           /*  10         */
 +
 +            for (m = 0; m < DIM; m++)                  /*  18+18       */
 +            {
 +                f_i[m]    = (c*r_kl[m]-cij*r_ij[m]);
 +                f[ai][m] += f_i[m];
 +                f[aj][m] -= f_i[m];
 +                if (!bZAxis)
 +                {
 +                    f_k[m]    = (c*r_ij[m]-ckl*r_kl[m]);
 +                    f[ak][m] += f_k[m];
 +                    f[al][m] -= f_k[m];
 +                }
 +            }
 +
 +            if (g)
 +            {
 +                ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +                t1 = IVEC2IS(dt);
 +            }
 +            rvec_inc(fshift[t1], f_i);
 +            rvec_dec(fshift[CENTRAL], f_i);
 +            if (!bZAxis)
 +            {
 +                if (g)
 +                {
 +                    ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, al), dt);
 +                    t2 = IVEC2IS(dt);
 +                }
 +                rvec_inc(fshift[t2], f_k);
 +                rvec_dec(fshift[CENTRAL], f_k);
 +            }
 +        }
 +    }
 +
 +    return vtot; /*  184 / 157 (bZAxis)  total  */
 +}
 +
 +real angres(int nbonds,
 +            const t_iatom forceatoms[], const t_iparams forceparams[],
 +            const rvec x[], rvec f[], rvec fshift[],
 +            const t_pbc *pbc, const t_graph *g,
 +            real lambda, real *dvdlambda,
 +            const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +            int gmx_unused *global_atom_index)
 +{
 +    return low_angres(nbonds, forceatoms, forceparams, x, f, fshift, pbc, g,
 +                      lambda, dvdlambda, FALSE);
 +}
 +
 +real angresz(int nbonds,
 +             const t_iatom forceatoms[], const t_iparams forceparams[],
 +             const rvec x[], rvec f[], rvec fshift[],
 +             const t_pbc *pbc, const t_graph *g,
 +             real lambda, real *dvdlambda,
 +             const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +             int gmx_unused *global_atom_index)
 +{
 +    return low_angres(nbonds, forceatoms, forceparams, x, f, fshift, pbc, g,
 +                      lambda, dvdlambda, TRUE);
 +}
 +
 +real dihres(int nbonds,
 +            const t_iatom forceatoms[], const t_iparams forceparams[],
 +            const rvec x[], rvec f[], rvec fshift[],
 +            const t_pbc *pbc, const t_graph *g,
 +            real lambda, real *dvdlambda,
 +            const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +            int gmx_unused  *global_atom_index)
 +{
 +    real vtot = 0;
 +    int  ai, aj, ak, al, i, k, type, t1, t2, t3;
 +    real phi0A, phi0B, dphiA, dphiB, kfacA, kfacB, phi0, dphi, kfac;
 +    real phi, ddphi, ddp, ddp2, dp, sign, d2r, fc, L1;
 +    rvec r_ij, r_kj, r_kl, m, n;
 +
 +    L1 = 1.0-lambda;
 +
 +    d2r = DEG2RAD;
 +    k   = 0;
 +
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        al   = forceatoms[i++];
 +
 +        phi0A  = forceparams[type].dihres.phiA*d2r;
 +        dphiA  = forceparams[type].dihres.dphiA*d2r;
 +        kfacA  = forceparams[type].dihres.kfacA;
 +
 +        phi0B  = forceparams[type].dihres.phiB*d2r;
 +        dphiB  = forceparams[type].dihres.dphiB*d2r;
 +        kfacB  = forceparams[type].dihres.kfacB;
 +
 +        phi0  = L1*phi0A + lambda*phi0B;
 +        dphi  = L1*dphiA + lambda*dphiB;
 +        kfac  = L1*kfacA + lambda*kfacB;
 +
 +        phi = dih_angle(x[ai], x[aj], x[ak], x[al], pbc, r_ij, r_kj, r_kl, m, n,
 +                        &sign, &t1, &t2, &t3);
 +        /* 84 flops */
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "dihres[%d]: %d %d %d %d : phi=%f, dphi=%f, kfac=%f\n",
 +                    k++, ai, aj, ak, al, phi0, dphi, kfac);
 +        }
 +        /* phi can jump if phi0 is close to Pi/-Pi, which will cause huge
 +         * force changes if we just apply a normal harmonic.
 +         * Instead, we first calculate phi-phi0 and take it modulo (-Pi,Pi).
 +         * This means we will never have the periodicity problem, unless
 +         * the dihedral is Pi away from phiO, which is very unlikely due to
 +         * the potential.
 +         */
 +        dp = phi-phi0;
 +        make_dp_periodic(&dp);
 +
 +        if (dp > dphi)
 +        {
 +            ddp = dp-dphi;
 +        }
 +        else if (dp < -dphi)
 +        {
 +            ddp = dp+dphi;
 +        }
 +        else
 +        {
 +            ddp = 0;
 +        }
 +
 +        if (ddp != 0.0)
 +        {
 +            ddp2  = ddp*ddp;
 +            vtot += 0.5*kfac*ddp2;
 +            ddphi = kfac*ddp;
 +
 +            *dvdlambda += 0.5*(kfacB - kfacA)*ddp2;
 +            /* lambda dependence from changing restraint distances */
 +            if (ddp > 0)
 +            {
 +                *dvdlambda -= kfac*ddp*((dphiB - dphiA)+(phi0B - phi0A));
 +            }
 +            else if (ddp < 0)
 +            {
 +                *dvdlambda += kfac*ddp*((dphiB - dphiA)-(phi0B - phi0A));
 +            }
 +            do_dih_fup(ai, aj, ak, al, ddphi, r_ij, r_kj, r_kl, m, n,
 +                       f, fshift, pbc, g, x, t1, t2, t3);      /* 112         */
 +        }
 +    }
 +    return vtot;
 +}
 +
 +
 +real unimplemented(int gmx_unused nbonds,
 +                   const t_iatom gmx_unused forceatoms[], const t_iparams gmx_unused forceparams[],
 +                   const rvec gmx_unused x[], rvec gmx_unused f[], rvec gmx_unused fshift[],
 +                   const t_pbc gmx_unused *pbc, const t_graph  gmx_unused *g,
 +                   real gmx_unused lambda, real gmx_unused *dvdlambda,
 +                   const t_mdatoms  gmx_unused *md, t_fcdata gmx_unused *fcd,
 +                   int gmx_unused *global_atom_index)
 +{
 +    gmx_impl("*** you are using a not implemented function");
 +
 +    return 0.0; /* To make the compiler happy */
 +}
 +
 +real restrangles(int nbonds,
 +                 const t_iatom forceatoms[], const t_iparams forceparams[],
 +                 const rvec x[], rvec f[], rvec fshift[],
 +                 const t_pbc *pbc, const t_graph *g,
 +                 real gmx_unused lambda, real gmx_unused *dvdlambda,
 +                 const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +                 int gmx_unused *global_atom_index)
 +{
 +    int  i, d, ai, aj, ak, type, m;
 +    int  t1, t2;
 +    rvec r_ij, r_kj;
 +    real v, vtot;
 +    ivec jt, dt_ij, dt_kj;
 +    rvec f_i, f_j, f_k;
 +    real prefactor, ratio_ante, ratio_post;
 +    rvec delta_ante, delta_post, vec_temp;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +
 +        t1 = pbc_rvec_sub(pbc, x[ai], x[aj], vec_temp);
 +        pbc_rvec_sub(pbc, x[aj], x[ai], delta_ante);
 +        t2 = pbc_rvec_sub(pbc, x[ak], x[aj], delta_post);
 +
 +
 +        /* This function computes factors needed for restricted angle potential.
 +         * The restricted angle potential is used in coarse-grained simulations to avoid singularities
 +         * when three particles align and the dihedral angle and dihedral potential
 +         * cannot be calculated. This potential is calculated using the formula:
 +           real restrangles(int nbonds,
 +            const t_iatom forceatoms[],const t_iparams forceparams[],
 +            const rvec x[],rvec f[],rvec fshift[],
 +            const t_pbc *pbc,const t_graph *g,
 +            real gmx_unused lambda,real gmx_unused *dvdlambda,
 +            const t_mdatoms gmx_unused *md,t_fcdata gmx_unused *fcd,
 +            int gmx_unused *global_atom_index)
 +           {
 +           int  i, d, ai, aj, ak, type, m;
 +           int t1, t2;
 +           rvec r_ij,r_kj;
 +           real v, vtot;
 +           ivec jt,dt_ij,dt_kj;
 +           rvec f_i, f_j, f_k;
 +           real prefactor, ratio_ante, ratio_post;
 +           rvec delta_ante, delta_post, vec_temp;
 +
 +           vtot = 0.0;
 +           for(i=0; (i<nbonds); )
 +           {
 +           type = forceatoms[i++];
 +           ai   = forceatoms[i++];
 +           aj   = forceatoms[i++];
 +           ak   = forceatoms[i++];
 +
 +         * \f[V_{\rm ReB}(\theta_i) = \frac{1}{2} k_{\theta} \frac{(\cos\theta_i - \cos\theta_0)^2}
 +         * {\sin^2\theta_i}\f] ({eq:ReB} and ref \cite{MonicaGoga2013} from the manual).
 +         * For more explanations see comments file "restcbt.h". */
 +
 +        compute_factors_restangles(type, forceparams,  delta_ante, delta_post,
 +                                   &prefactor, &ratio_ante, &ratio_post, &v);
 +
 +        /*   Forces are computed per component */
 +        for (d = 0; d < DIM; d++)
 +        {
 +            f_i[d] = prefactor * (ratio_ante * delta_ante[d] - delta_post[d]);
 +            f_j[d] = prefactor * ((ratio_post + 1.0) * delta_post[d] - (ratio_ante + 1.0) * delta_ante[d]);
 +            f_k[d] = prefactor * (delta_ante[d] - ratio_post * delta_post[d]);
 +        }
 +
 +        /*   Computation of potential energy   */
 +
 +        vtot += v;
 +
 +        /*   Update forces */
 +
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            f[ai][m] += f_i[m];
 +            f[aj][m] += f_j[m];
 +            f[ak][m] += f_k[m];
 +        }
 +
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, aj), jt);
 +            ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +            ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +            t1 = IVEC2IS(dt_ij);
 +            t2 = IVEC2IS(dt_kj);
 +        }
 +
 +        rvec_inc(fshift[t1], f_i);
 +        rvec_inc(fshift[CENTRAL], f_j);
 +        rvec_inc(fshift[t2], f_k);
 +    }
 +    return vtot;
 +}
 +
 +
 +real restrdihs(int nbonds,
 +               const t_iatom forceatoms[], const t_iparams forceparams[],
 +               const rvec x[], rvec f[], rvec fshift[],
 +               const t_pbc *pbc, const t_graph *g,
 +               real gmx_unused lambda, real gmx_unused *dvlambda,
 +               const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +               int gmx_unused *global_atom_index)
 +{
 +    int  i, d, type, ai, aj, ak, al;
 +    rvec f_i, f_j, f_k, f_l;
 +    rvec dx_jl;
 +    ivec jt, dt_ij, dt_kj, dt_lj;
 +    int  t1, t2, t3;
 +    real v, vtot;
 +    rvec delta_ante,  delta_crnt, delta_post, vec_temp;
 +    real factor_phi_ai_ante, factor_phi_ai_crnt, factor_phi_ai_post;
 +    real factor_phi_aj_ante, factor_phi_aj_crnt, factor_phi_aj_post;
 +    real factor_phi_ak_ante, factor_phi_ak_crnt, factor_phi_ak_post;
 +    real factor_phi_al_ante, factor_phi_al_crnt, factor_phi_al_post;
 +    real prefactor_phi;
 +
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        al   = forceatoms[i++];
 +
 +        t1 = pbc_rvec_sub(pbc, x[ai], x[aj], vec_temp);
 +        pbc_rvec_sub(pbc, x[aj], x[ai], delta_ante);
 +        t2 = pbc_rvec_sub(pbc, x[ak], x[aj], delta_crnt);
 +        t3 = pbc_rvec_sub(pbc, x[ak], x[al], vec_temp);
 +        pbc_rvec_sub(pbc, x[al], x[ak], delta_post);
 +
 +        /* This function computes factors needed for restricted angle potential.
 +         * The restricted angle potential is used in coarse-grained simulations to avoid singularities
 +         * when three particles align and the dihedral angle and dihedral potential cannot be calculated.
 +         * This potential is calculated using the formula:
 +         * \f[V_{\rm ReB}(\theta_i) = \frac{1}{2} k_{\theta}
 +         * \frac{(\cos\theta_i - \cos\theta_0)^2}{\sin^2\theta_i}\f]
 +         * ({eq:ReB} and ref \cite{MonicaGoga2013} from the manual).
 +         * For more explanations see comments file "restcbt.h" */
 +
 +        compute_factors_restrdihs(type, forceparams,
 +                                  delta_ante, delta_crnt, delta_post,
 +                                  &factor_phi_ai_ante, &factor_phi_ai_crnt, &factor_phi_ai_post,
 +                                  &factor_phi_aj_ante, &factor_phi_aj_crnt, &factor_phi_aj_post,
 +                                  &factor_phi_ak_ante, &factor_phi_ak_crnt, &factor_phi_ak_post,
 +                                  &factor_phi_al_ante, &factor_phi_al_crnt, &factor_phi_al_post,
 +                                  &prefactor_phi, &v);
 +
 +
 +        /*      Computation of forces per component */
 +        for (d = 0; d < DIM; d++)
 +        {
 +            f_i[d] = prefactor_phi * (factor_phi_ai_ante * delta_ante[d] + factor_phi_ai_crnt * delta_crnt[d] + factor_phi_ai_post * delta_post[d]);
 +            f_j[d] = prefactor_phi * (factor_phi_aj_ante * delta_ante[d] + factor_phi_aj_crnt * delta_crnt[d] + factor_phi_aj_post * delta_post[d]);
 +            f_k[d] = prefactor_phi * (factor_phi_ak_ante * delta_ante[d] + factor_phi_ak_crnt * delta_crnt[d] + factor_phi_ak_post * delta_post[d]);
 +            f_l[d] = prefactor_phi * (factor_phi_al_ante * delta_ante[d] + factor_phi_al_crnt * delta_crnt[d] + factor_phi_al_post * delta_post[d]);
 +        }
 +        /*      Computation of the energy */
 +
 +        vtot += v;
 +
 +
 +
 +        /*    Updating the forces */
 +
 +        rvec_inc(f[ai], f_i);
 +        rvec_inc(f[aj], f_j);
 +        rvec_inc(f[ak], f_k);
 +        rvec_inc(f[al], f_l);
 +
 +
 +        /* Updating the fshift forces for the pressure coupling */
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, aj), jt);
 +            ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +            ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +            ivec_sub(SHIFT_IVEC(g, al), jt, dt_lj);
 +            t1 = IVEC2IS(dt_ij);
 +            t2 = IVEC2IS(dt_kj);
 +            t3 = IVEC2IS(dt_lj);
 +        }
 +        else if (pbc)
 +        {
 +            t3 = pbc_rvec_sub(pbc, x[al], x[aj], dx_jl);
 +        }
 +        else
 +        {
 +            t3 = CENTRAL;
 +        }
 +
 +        rvec_inc(fshift[t1], f_i);
 +        rvec_inc(fshift[CENTRAL], f_j);
 +        rvec_inc(fshift[t2], f_k);
 +        rvec_inc(fshift[t3], f_l);
 +
 +    }
 +
 +    return vtot;
 +}
 +
 +
 +real cbtdihs(int nbonds,
 +             const t_iatom forceatoms[], const t_iparams forceparams[],
 +             const rvec x[], rvec f[], rvec fshift[],
 +             const t_pbc *pbc, const t_graph *g,
 +             real gmx_unused lambda, real gmx_unused *dvdlambda,
 +             const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +             int gmx_unused *global_atom_index)
 +{
 +    int  type, ai, aj, ak, al, i, d;
 +    int  t1, t2, t3;
 +    real v, vtot;
 +    rvec vec_temp;
 +    rvec f_i, f_j, f_k, f_l;
 +    ivec jt, dt_ij, dt_kj, dt_lj;
 +    rvec dx_jl;
 +    rvec delta_ante, delta_crnt, delta_post;
 +    rvec f_phi_ai, f_phi_aj, f_phi_ak, f_phi_al;
 +    rvec f_theta_ante_ai, f_theta_ante_aj, f_theta_ante_ak;
 +    rvec f_theta_post_aj, f_theta_post_ak, f_theta_post_al;
 +
 +
 +
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        al   = forceatoms[i++];
 +
 +
 +        t1 = pbc_rvec_sub(pbc, x[ai], x[aj], vec_temp);
 +        pbc_rvec_sub(pbc, x[aj], x[ai], delta_ante);
 +        t2 = pbc_rvec_sub(pbc, x[ak], x[aj], vec_temp);
 +        pbc_rvec_sub(pbc, x[ak], x[aj], delta_crnt);
 +        t3 = pbc_rvec_sub(pbc, x[ak], x[al], vec_temp);
 +        pbc_rvec_sub(pbc, x[al], x[ak], delta_post);
 +
 +        /* \brief Compute factors for CBT potential
 +         * The combined bending-torsion potential goes to zero in a very smooth manner, eliminating the numerical
 +         * instabilities, when three coarse-grained particles align and the dihedral angle and standard
 +         * dihedral potentials cannot be calculated. The CBT potential is calculated using the formula:
 +         * \f[V_{\rm CBT}(\theta_{i-1}, \theta_i, \phi_i) = k_{\phi} \sin^3\theta_{i-1} \sin^3\theta_{i}
 +         * \sum_{n=0}^4 { a_n \cos^n\phi_i}\f] ({eq:CBT} and ref \cite{MonicaGoga2013} from the manual).
 +         * It contains in its expression not only the dihedral angle \f$\phi\f$
 +         * but also \f[\theta_{i-1}\f] (theta_ante bellow) and \f[\theta_{i}\f] (theta_post bellow)
 +         * --- the adjacent bending angles.
 +         * For more explanations see comments file "restcbt.h". */
 +
 +        compute_factors_cbtdihs(type, forceparams, delta_ante, delta_crnt, delta_post,
 +                                f_phi_ai, f_phi_aj, f_phi_ak, f_phi_al,
 +                                f_theta_ante_ai, f_theta_ante_aj, f_theta_ante_ak,
 +                                f_theta_post_aj, f_theta_post_ak, f_theta_post_al,
 +                                &v);
 +
 +
 +        /*      Acumulate the resuts per beads */
 +        for (d = 0; d < DIM; d++)
 +        {
 +            f_i[d] = f_phi_ai[d] + f_theta_ante_ai[d];
 +            f_j[d] = f_phi_aj[d] + f_theta_ante_aj[d] + f_theta_post_aj[d];
 +            f_k[d] = f_phi_ak[d] + f_theta_ante_ak[d] + f_theta_post_ak[d];
 +            f_l[d] = f_phi_al[d] + f_theta_post_al[d];
 +        }
 +
 +        /*      Compute the potential energy */
 +
 +        vtot += v;
 +
 +
 +        /*  Updating the forces */
 +        rvec_inc(f[ai], f_i);
 +        rvec_inc(f[aj], f_j);
 +        rvec_inc(f[ak], f_k);
 +        rvec_inc(f[al], f_l);
 +
 +
 +        /* Updating the fshift forces for the pressure coupling */
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, aj), jt);
 +            ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +            ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +            ivec_sub(SHIFT_IVEC(g, al), jt, dt_lj);
 +            t1 = IVEC2IS(dt_ij);
 +            t2 = IVEC2IS(dt_kj);
 +            t3 = IVEC2IS(dt_lj);
 +        }
 +        else if (pbc)
 +        {
 +            t3 = pbc_rvec_sub(pbc, x[al], x[aj], dx_jl);
 +        }
 +        else
 +        {
 +            t3 = CENTRAL;
 +        }
 +
 +        rvec_inc(fshift[t1], f_i);
 +        rvec_inc(fshift[CENTRAL], f_j);
 +        rvec_inc(fshift[t2], f_k);
 +        rvec_inc(fshift[t3], f_l);
 +    }
 +
 +    return vtot;
 +}
 +
 +real rbdihs(int nbonds,
 +            const t_iatom forceatoms[], const t_iparams forceparams[],
 +            const rvec x[], rvec f[], rvec fshift[],
 +            const t_pbc *pbc, const t_graph *g,
 +            real lambda, real *dvdlambda,
 +            const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +            int gmx_unused *global_atom_index)
 +{
 +    const real c0 = 0.0, c1 = 1.0, c2 = 2.0, c3 = 3.0, c4 = 4.0, c5 = 5.0;
 +    int        type, ai, aj, ak, al, i, j;
 +    int        t1, t2, t3;
 +    rvec       r_ij, r_kj, r_kl, m, n;
 +    real       parmA[NR_RBDIHS];
 +    real       parmB[NR_RBDIHS];
 +    real       parm[NR_RBDIHS];
 +    real       cos_phi, phi, rbp, rbpBA;
 +    real       v, sign, ddphi, sin_phi;
 +    real       cosfac, vtot;
 +    real       L1        = 1.0-lambda;
 +    real       dvdl_term = 0;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        al   = forceatoms[i++];
 +
 +        phi = dih_angle(x[ai], x[aj], x[ak], x[al], pbc, r_ij, r_kj, r_kl, m, n,
 +                        &sign, &t1, &t2, &t3);  /*  84                */
 +
 +        /* Change to polymer convention */
 +        if (phi < c0)
 +        {
 +            phi += M_PI;
 +        }
 +        else
 +        {
 +            phi -= M_PI;    /*   1            */
 +
 +        }
 +        cos_phi = cos(phi);
 +        /* Beware of accuracy loss, cannot use 1-sqrt(cos^2) ! */
 +        sin_phi = sin(phi);
 +
 +        for (j = 0; (j < NR_RBDIHS); j++)
 +        {
 +            parmA[j] = forceparams[type].rbdihs.rbcA[j];
 +            parmB[j] = forceparams[type].rbdihs.rbcB[j];
 +            parm[j]  = L1*parmA[j]+lambda*parmB[j];
 +        }
 +        /* Calculate cosine powers */
 +        /* Calculate the energy */
 +        /* Calculate the derivative */
 +
 +        v            = parm[0];
 +        dvdl_term   += (parmB[0]-parmA[0]);
 +        ddphi        = c0;
 +        cosfac       = c1;
 +
 +        rbp          = parm[1];
 +        rbpBA        = parmB[1]-parmA[1];
 +        ddphi       += rbp*cosfac;
 +        cosfac      *= cos_phi;
 +        v           += cosfac*rbp;
 +        dvdl_term   += cosfac*rbpBA;
 +        rbp          = parm[2];
 +        rbpBA        = parmB[2]-parmA[2];
 +        ddphi       += c2*rbp*cosfac;
 +        cosfac      *= cos_phi;
 +        v           += cosfac*rbp;
 +        dvdl_term   += cosfac*rbpBA;
 +        rbp          = parm[3];
 +        rbpBA        = parmB[3]-parmA[3];
 +        ddphi       += c3*rbp*cosfac;
 +        cosfac      *= cos_phi;
 +        v           += cosfac*rbp;
 +        dvdl_term   += cosfac*rbpBA;
 +        rbp          = parm[4];
 +        rbpBA        = parmB[4]-parmA[4];
 +        ddphi       += c4*rbp*cosfac;
 +        cosfac      *= cos_phi;
 +        v           += cosfac*rbp;
 +        dvdl_term   += cosfac*rbpBA;
 +        rbp          = parm[5];
 +        rbpBA        = parmB[5]-parmA[5];
 +        ddphi       += c5*rbp*cosfac;
 +        cosfac      *= cos_phi;
 +        v           += cosfac*rbp;
 +        dvdl_term   += cosfac*rbpBA;
 +
 +        ddphi = -ddphi*sin_phi;         /*  11                */
 +
 +        do_dih_fup(ai, aj, ak, al, ddphi, r_ij, r_kj, r_kl, m, n,
 +                   f, fshift, pbc, g, x, t1, t2, t3); /* 112          */
 +        vtot += v;
 +    }
 +    *dvdlambda += dvdl_term;
 +
 +    return vtot;
 +}
 +
 +int cmap_setup_grid_index(int ip, int grid_spacing, int *ipm1, int *ipp1, int *ipp2)
 +{
 +    int im1, ip1, ip2;
 +
 +    if (ip < 0)
 +    {
 +        ip = ip + grid_spacing - 1;
 +    }
 +    else if (ip > grid_spacing)
 +    {
 +        ip = ip - grid_spacing - 1;
 +    }
 +
 +    im1 = ip - 1;
 +    ip1 = ip + 1;
 +    ip2 = ip + 2;
 +
 +    if (ip == 0)
 +    {
 +        im1 = grid_spacing - 1;
 +    }
 +    else if (ip == grid_spacing-2)
 +    {
 +        ip2 = 0;
 +    }
 +    else if (ip == grid_spacing-1)
 +    {
 +        ip1 = 0;
 +        ip2 = 1;
 +    }
 +
 +    *ipm1 = im1;
 +    *ipp1 = ip1;
 +    *ipp2 = ip2;
 +
 +    return ip;
 +
 +}
 +
 +real cmap_dihs(int nbonds,
 +               const t_iatom forceatoms[], const t_iparams forceparams[],
 +               const gmx_cmap_t *cmap_grid,
 +               const rvec x[], rvec f[], rvec fshift[],
 +               const t_pbc *pbc, const t_graph *g,
 +               real gmx_unused lambda, real gmx_unused *dvdlambda,
 +               const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +               int  gmx_unused *global_atom_index)
 +{
 +    int         i, j, k, n, idx;
 +    int         ai, aj, ak, al, am;
 +    int         a1i, a1j, a1k, a1l, a2i, a2j, a2k, a2l;
 +    int         type, cmapA;
 +    int         t11, t21, t31, t12, t22, t32;
 +    int         iphi1, ip1m1, ip1p1, ip1p2;
 +    int         iphi2, ip2m1, ip2p1, ip2p2;
 +    int         l1, l2, l3, l4;
 +    int         pos1, pos2, pos3, pos4, tmp;
 +
 +    real        ty[4], ty1[4], ty2[4], ty12[4], tc[16], tx[16];
 +    real        phi1, psi1, cos_phi1, sin_phi1, sign1, xphi1;
 +    real        phi2, psi2, cos_phi2, sin_phi2, sign2, xphi2;
 +    real        dx, xx, tt, tu, e, df1, df2, ddf1, ddf2, ddf12, vtot;
 +    real        ra21, rb21, rg21, rg1, rgr1, ra2r1, rb2r1, rabr1;
 +    real        ra22, rb22, rg22, rg2, rgr2, ra2r2, rb2r2, rabr2;
 +    real        fg1, hg1, fga1, hgb1, gaa1, gbb1;
 +    real        fg2, hg2, fga2, hgb2, gaa2, gbb2;
 +    real        fac;
 +
 +    rvec        r1_ij, r1_kj, r1_kl, m1, n1;
 +    rvec        r2_ij, r2_kj, r2_kl, m2, n2;
 +    rvec        f1_i, f1_j, f1_k, f1_l;
 +    rvec        f2_i, f2_j, f2_k, f2_l;
 +    rvec        a1, b1, a2, b2;
 +    rvec        f1, g1, h1, f2, g2, h2;
 +    rvec        dtf1, dtg1, dth1, dtf2, dtg2, dth2;
 +    ivec        jt1, dt1_ij, dt1_kj, dt1_lj;
 +    ivec        jt2, dt2_ij, dt2_kj, dt2_lj;
 +
 +    const real *cmapd;
 +
 +    int         loop_index[4][4] = {
 +        {0, 4, 8, 12},
 +        {1, 5, 9, 13},
 +        {2, 6, 10, 14},
 +        {3, 7, 11, 15}
 +    };
 +
 +    /* Total CMAP energy */
 +    vtot = 0;
 +
 +    for (n = 0; n < nbonds; )
 +    {
 +        /* Five atoms are involved in the two torsions */
 +        type   = forceatoms[n++];
 +        ai     = forceatoms[n++];
 +        aj     = forceatoms[n++];
 +        ak     = forceatoms[n++];
 +        al     = forceatoms[n++];
 +        am     = forceatoms[n++];
 +
 +        /* Which CMAP type is this */
 +        cmapA = forceparams[type].cmap.cmapA;
 +        cmapd = cmap_grid->cmapdata[cmapA].cmap;
 +
 +        /* First torsion */
 +        a1i   = ai;
 +        a1j   = aj;
 +        a1k   = ak;
 +        a1l   = al;
 +
 +        phi1  = dih_angle(x[a1i], x[a1j], x[a1k], x[a1l], pbc, r1_ij, r1_kj, r1_kl, m1, n1,
 +                          &sign1, &t11, &t21, &t31);  /* 84 */
 +
 +        cos_phi1 = cos(phi1);
 +
 +        a1[0] = r1_ij[1]*r1_kj[2]-r1_ij[2]*r1_kj[1];
 +        a1[1] = r1_ij[2]*r1_kj[0]-r1_ij[0]*r1_kj[2];
 +        a1[2] = r1_ij[0]*r1_kj[1]-r1_ij[1]*r1_kj[0]; /* 9 */
 +
 +        b1[0] = r1_kl[1]*r1_kj[2]-r1_kl[2]*r1_kj[1];
 +        b1[1] = r1_kl[2]*r1_kj[0]-r1_kl[0]*r1_kj[2];
 +        b1[2] = r1_kl[0]*r1_kj[1]-r1_kl[1]*r1_kj[0]; /* 9 */
 +
 +        tmp = pbc_rvec_sub(pbc, x[a1l], x[a1k], h1);
 +
 +        ra21  = iprod(a1, a1);       /* 5 */
 +        rb21  = iprod(b1, b1);       /* 5 */
 +        rg21  = iprod(r1_kj, r1_kj); /* 5 */
 +        rg1   = sqrt(rg21);
 +
 +        rgr1  = 1.0/rg1;
 +        ra2r1 = 1.0/ra21;
 +        rb2r1 = 1.0/rb21;
 +        rabr1 = sqrt(ra2r1*rb2r1);
 +
 +        sin_phi1 = rg1 * rabr1 * iprod(a1, h1) * (-1);
 +
 +        if (cos_phi1 < -0.5 || cos_phi1 > 0.5)
 +        {
 +            phi1 = asin(sin_phi1);
 +
 +            if (cos_phi1 < 0)
 +            {
 +                if (phi1 > 0)
 +                {
 +                    phi1 = M_PI - phi1;
 +                }
 +                else
 +                {
 +                    phi1 = -M_PI - phi1;
 +                }
 +            }
 +        }
 +        else
 +        {
 +            phi1 = acos(cos_phi1);
 +
 +            if (sin_phi1 < 0)
 +            {
 +                phi1 = -phi1;
 +            }
 +        }
 +
 +        xphi1 = phi1 + M_PI; /* 1 */
 +
 +        /* Second torsion */
 +        a2i   = aj;
 +        a2j   = ak;
 +        a2k   = al;
 +        a2l   = am;
 +
 +        phi2  = dih_angle(x[a2i], x[a2j], x[a2k], x[a2l], pbc, r2_ij, r2_kj, r2_kl, m2, n2,
 +                          &sign2, &t12, &t22, &t32); /* 84 */
 +
 +        cos_phi2 = cos(phi2);
 +
 +        a2[0] = r2_ij[1]*r2_kj[2]-r2_ij[2]*r2_kj[1];
 +        a2[1] = r2_ij[2]*r2_kj[0]-r2_ij[0]*r2_kj[2];
 +        a2[2] = r2_ij[0]*r2_kj[1]-r2_ij[1]*r2_kj[0]; /* 9 */
 +
 +        b2[0] = r2_kl[1]*r2_kj[2]-r2_kl[2]*r2_kj[1];
 +        b2[1] = r2_kl[2]*r2_kj[0]-r2_kl[0]*r2_kj[2];
 +        b2[2] = r2_kl[0]*r2_kj[1]-r2_kl[1]*r2_kj[0]; /* 9 */
 +
 +        tmp = pbc_rvec_sub(pbc, x[a2l], x[a2k], h2);
 +
 +        ra22  = iprod(a2, a2);         /* 5 */
 +        rb22  = iprod(b2, b2);         /* 5 */
 +        rg22  = iprod(r2_kj, r2_kj);   /* 5 */
 +        rg2   = sqrt(rg22);
 +
 +        rgr2  = 1.0/rg2;
 +        ra2r2 = 1.0/ra22;
 +        rb2r2 = 1.0/rb22;
 +        rabr2 = sqrt(ra2r2*rb2r2);
 +
 +        sin_phi2 = rg2 * rabr2 * iprod(a2, h2) * (-1);
 +
 +        if (cos_phi2 < -0.5 || cos_phi2 > 0.5)
 +        {
 +            phi2 = asin(sin_phi2);
 +
 +            if (cos_phi2 < 0)
 +            {
 +                if (phi2 > 0)
 +                {
 +                    phi2 = M_PI - phi2;
 +                }
 +                else
 +                {
 +                    phi2 = -M_PI - phi2;
 +                }
 +            }
 +        }
 +        else
 +        {
 +            phi2 = acos(cos_phi2);
 +
 +            if (sin_phi2 < 0)
 +            {
 +                phi2 = -phi2;
 +            }
 +        }
 +
 +        xphi2 = phi2 + M_PI; /* 1 */
 +
 +        /* Range mangling */
 +        if (xphi1 < 0)
 +        {
 +            xphi1 = xphi1 + 2*M_PI;
 +        }
 +        else if (xphi1 >= 2*M_PI)
 +        {
 +            xphi1 = xphi1 - 2*M_PI;
 +        }
 +
 +        if (xphi2 < 0)
 +        {
 +            xphi2 = xphi2 + 2*M_PI;
 +        }
 +        else if (xphi2 >= 2*M_PI)
 +        {
 +            xphi2 = xphi2 - 2*M_PI;
 +        }
 +
 +        /* Number of grid points */
 +        dx = 2*M_PI / cmap_grid->grid_spacing;
 +
 +        /* Where on the grid are we */
 +        iphi1 = (int)(xphi1/dx);
 +        iphi2 = (int)(xphi2/dx);
 +
 +        iphi1 = cmap_setup_grid_index(iphi1, cmap_grid->grid_spacing, &ip1m1, &ip1p1, &ip1p2);
 +        iphi2 = cmap_setup_grid_index(iphi2, cmap_grid->grid_spacing, &ip2m1, &ip2p1, &ip2p2);
 +
 +        pos1    = iphi1*cmap_grid->grid_spacing+iphi2;
 +        pos2    = ip1p1*cmap_grid->grid_spacing+iphi2;
 +        pos3    = ip1p1*cmap_grid->grid_spacing+ip2p1;
 +        pos4    = iphi1*cmap_grid->grid_spacing+ip2p1;
 +
 +        ty[0]   = cmapd[pos1*4];
 +        ty[1]   = cmapd[pos2*4];
 +        ty[2]   = cmapd[pos3*4];
 +        ty[3]   = cmapd[pos4*4];
 +
 +        ty1[0]   = cmapd[pos1*4+1];
 +        ty1[1]   = cmapd[pos2*4+1];
 +        ty1[2]   = cmapd[pos3*4+1];
 +        ty1[3]   = cmapd[pos4*4+1];
 +
 +        ty2[0]   = cmapd[pos1*4+2];
 +        ty2[1]   = cmapd[pos2*4+2];
 +        ty2[2]   = cmapd[pos3*4+2];
 +        ty2[3]   = cmapd[pos4*4+2];
 +
 +        ty12[0]   = cmapd[pos1*4+3];
 +        ty12[1]   = cmapd[pos2*4+3];
 +        ty12[2]   = cmapd[pos3*4+3];
 +        ty12[3]   = cmapd[pos4*4+3];
 +
 +        /* Switch to degrees */
 +        dx    = 360.0 / cmap_grid->grid_spacing;
 +        xphi1 = xphi1 * RAD2DEG;
 +        xphi2 = xphi2 * RAD2DEG;
 +
 +        for (i = 0; i < 4; i++) /* 16 */
 +        {
 +            tx[i]    = ty[i];
 +            tx[i+4]  = ty1[i]*dx;
 +            tx[i+8]  = ty2[i]*dx;
 +            tx[i+12] = ty12[i]*dx*dx;
 +        }
 +
 +        idx = 0;
 +        for (i = 0; i < 4; i++) /* 1056 */
 +        {
 +            for (j = 0; j < 4; j++)
 +            {
 +                xx = 0;
 +                for (k = 0; k < 16; k++)
 +                {
 +                    xx = xx + cmap_coeff_matrix[k*16+idx]*tx[k];
 +                }
 +
 +                idx++;
 +                tc[i*4+j] = xx;
 +            }
 +        }
 +
 +        tt    = (xphi1-iphi1*dx)/dx;
 +        tu    = (xphi2-iphi2*dx)/dx;
 +
 +        e     = 0;
 +        df1   = 0;
 +        df2   = 0;
 +        ddf1  = 0;
 +        ddf2  = 0;
 +        ddf12 = 0;
 +
 +        for (i = 3; i >= 0; i--)
 +        {
 +            l1 = loop_index[i][3];
 +            l2 = loop_index[i][2];
 +            l3 = loop_index[i][1];
 +
 +            e     = tt * e    + ((tc[i*4+3]*tu+tc[i*4+2])*tu + tc[i*4+1])*tu+tc[i*4];
 +            df1   = tu * df1  + (3.0*tc[l1]*tt+2.0*tc[l2])*tt+tc[l3];
 +            df2   = tt * df2  + (3.0*tc[i*4+3]*tu+2.0*tc[i*4+2])*tu+tc[i*4+1];
 +            ddf1  = tu * ddf1 + 2.0*3.0*tc[l1]*tt+2.0*tc[l2];
 +            ddf2  = tt * ddf2 + 2.0*3.0*tc[4*i+3]*tu+2.0*tc[4*i+2];
 +        }
 +
 +        ddf12 = tc[5] + 2.0*tc[9]*tt + 3.0*tc[13]*tt*tt + 2.0*tu*(tc[6]+2.0*tc[10]*tt+3.0*tc[14]*tt*tt) +
 +            3.0*tu*tu*(tc[7]+2.0*tc[11]*tt+3.0*tc[15]*tt*tt);
 +
 +        fac     = RAD2DEG/dx;
 +        df1     = df1   * fac;
 +        df2     = df2   * fac;
 +        ddf1    = ddf1  * fac * fac;
 +        ddf2    = ddf2  * fac * fac;
 +        ddf12   = ddf12 * fac * fac;
 +
 +        /* CMAP energy */
 +        vtot += e;
 +
 +        /* Do forces - first torsion */
 +        fg1       = iprod(r1_ij, r1_kj);
 +        hg1       = iprod(r1_kl, r1_kj);
 +        fga1      = fg1*ra2r1*rgr1;
 +        hgb1      = hg1*rb2r1*rgr1;
 +        gaa1      = -ra2r1*rg1;
 +        gbb1      = rb2r1*rg1;
 +
 +        for (i = 0; i < DIM; i++)
 +        {
 +            dtf1[i]   = gaa1 * a1[i];
 +            dtg1[i]   = fga1 * a1[i] - hgb1 * b1[i];
 +            dth1[i]   = gbb1 * b1[i];
 +
 +            f1[i]     = df1  * dtf1[i];
 +            g1[i]     = df1  * dtg1[i];
 +            h1[i]     = df1  * dth1[i];
 +
 +            f1_i[i]   =  f1[i];
 +            f1_j[i]   = -f1[i] - g1[i];
 +            f1_k[i]   =  h1[i] + g1[i];
 +            f1_l[i]   = -h1[i];
 +
 +            f[a1i][i] = f[a1i][i] + f1_i[i];
 +            f[a1j][i] = f[a1j][i] + f1_j[i]; /* - f1[i] - g1[i] */
 +            f[a1k][i] = f[a1k][i] + f1_k[i]; /* h1[i] + g1[i] */
 +            f[a1l][i] = f[a1l][i] + f1_l[i]; /* h1[i] */
 +        }
 +
 +        /* Do forces - second torsion */
 +        fg2       = iprod(r2_ij, r2_kj);
 +        hg2       = iprod(r2_kl, r2_kj);
 +        fga2      = fg2*ra2r2*rgr2;
 +        hgb2      = hg2*rb2r2*rgr2;
 +        gaa2      = -ra2r2*rg2;
 +        gbb2      = rb2r2*rg2;
 +
 +        for (i = 0; i < DIM; i++)
 +        {
 +            dtf2[i]   = gaa2 * a2[i];
 +            dtg2[i]   = fga2 * a2[i] - hgb2 * b2[i];
 +            dth2[i]   = gbb2 * b2[i];
 +
 +            f2[i]     = df2  * dtf2[i];
 +            g2[i]     = df2  * dtg2[i];
 +            h2[i]     = df2  * dth2[i];
 +
 +            f2_i[i]   =  f2[i];
 +            f2_j[i]   = -f2[i] - g2[i];
 +            f2_k[i]   =  h2[i] + g2[i];
 +            f2_l[i]   = -h2[i];
 +
 +            f[a2i][i] = f[a2i][i] + f2_i[i]; /* f2[i] */
 +            f[a2j][i] = f[a2j][i] + f2_j[i]; /* - f2[i] - g2[i] */
 +            f[a2k][i] = f[a2k][i] + f2_k[i]; /* h2[i] + g2[i] */
 +            f[a2l][i] = f[a2l][i] + f2_l[i]; /* - h2[i] */
 +        }
 +
 +        /* Shift forces */
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, a1j), jt1);
 +            ivec_sub(SHIFT_IVEC(g, a1i),  jt1, dt1_ij);
 +            ivec_sub(SHIFT_IVEC(g, a1k),  jt1, dt1_kj);
 +            ivec_sub(SHIFT_IVEC(g, a1l),  jt1, dt1_lj);
 +            t11 = IVEC2IS(dt1_ij);
 +            t21 = IVEC2IS(dt1_kj);
 +            t31 = IVEC2IS(dt1_lj);
 +
 +            copy_ivec(SHIFT_IVEC(g, a2j), jt2);
 +            ivec_sub(SHIFT_IVEC(g, a2i),  jt2, dt2_ij);
 +            ivec_sub(SHIFT_IVEC(g, a2k),  jt2, dt2_kj);
 +            ivec_sub(SHIFT_IVEC(g, a2l),  jt2, dt2_lj);
 +            t12 = IVEC2IS(dt2_ij);
 +            t22 = IVEC2IS(dt2_kj);
 +            t32 = IVEC2IS(dt2_lj);
 +        }
 +        else if (pbc)
 +        {
 +            t31 = pbc_rvec_sub(pbc, x[a1l], x[a1j], h1);
 +            t32 = pbc_rvec_sub(pbc, x[a2l], x[a2j], h2);
 +        }
 +        else
 +        {
 +            t31 = CENTRAL;
 +            t32 = CENTRAL;
 +        }
 +
 +        rvec_inc(fshift[t11], f1_i);
 +        rvec_inc(fshift[CENTRAL], f1_j);
 +        rvec_inc(fshift[t21], f1_k);
 +        rvec_inc(fshift[t31], f1_l);
 +
 +        rvec_inc(fshift[t21], f2_i);
 +        rvec_inc(fshift[CENTRAL], f2_j);
 +        rvec_inc(fshift[t22], f2_k);
 +        rvec_inc(fshift[t32], f2_l);
 +    }
 +    return vtot;
 +}
 +
 +
 +
 +/***********************************************************
 + *
 + *   G R O M O S  9 6   F U N C T I O N S
 + *
 + ***********************************************************/
 +real g96harmonic(real kA, real kB, real xA, real xB, real x, real lambda,
 +                 real *V, real *F)
 +{
 +    const real half = 0.5;
 +    real       L1, kk, x0, dx, dx2;
 +    real       v, f, dvdlambda;
 +
 +    L1    = 1.0-lambda;
 +    kk    = L1*kA+lambda*kB;
 +    x0    = L1*xA+lambda*xB;
 +
 +    dx    = x-x0;
 +    dx2   = dx*dx;
 +
 +    f          = -kk*dx;
 +    v          = half*kk*dx2;
 +    dvdlambda  = half*(kB-kA)*dx2 + (xA-xB)*kk*dx;
 +
 +    *F    = f;
 +    *V    = v;
 +
 +    return dvdlambda;
 +
 +    /* That was 21 flops */
 +}
 +
 +real g96bonds(int nbonds,
 +              const t_iatom forceatoms[], const t_iparams forceparams[],
 +              const rvec x[], rvec f[], rvec fshift[],
 +              const t_pbc *pbc, const t_graph *g,
 +              real lambda, real *dvdlambda,
 +              const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +              int gmx_unused *global_atom_index)
 +{
 +    int  i, m, ki, ai, aj, type;
 +    real dr2, fbond, vbond, fij, vtot;
 +    rvec dx;
 +    ivec dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx); /*   3      */
 +        dr2  = iprod(dx, dx);                       /*   5            */
 +
 +        *dvdlambda += g96harmonic(forceparams[type].harmonic.krA,
 +                                  forceparams[type].harmonic.krB,
 +                                  forceparams[type].harmonic.rA,
 +                                  forceparams[type].harmonic.rB,
 +                                  dr2, lambda, &vbond, &fbond);
 +
 +        vtot  += 0.5*vbond;                         /* 1*/
 +#ifdef DEBUG
 +        if (debug)
 +        {
 +            fprintf(debug, "G96-BONDS: dr = %10g  vbond = %10g  fbond = %10g\n",
 +                    sqrt(dr2), vbond, fbond);
 +        }
 +#endif
 +
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)     /*  15                */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }               /* 44 TOTAL       */
 +    return vtot;
 +}
 +
 +real g96bond_angle(const rvec xi, const rvec xj, const rvec xk, const t_pbc *pbc,
 +                   rvec r_ij, rvec r_kj,
 +                   int *t1, int *t2)
 +/* Return value is the angle between the bonds i-j and j-k */
 +{
 +    real costh;
 +
 +    *t1 = pbc_rvec_sub(pbc, xi, xj, r_ij); /*  3              */
 +    *t2 = pbc_rvec_sub(pbc, xk, xj, r_kj); /*  3              */
 +
 +    costh = cos_angle(r_ij, r_kj);         /* 25              */
 +    /* 41 TOTAL       */
 +    return costh;
 +}
 +
 +real g96angles(int nbonds,
 +               const t_iatom forceatoms[], const t_iparams forceparams[],
 +               const rvec x[], rvec f[], rvec fshift[],
 +               const t_pbc *pbc, const t_graph *g,
 +               real lambda, real *dvdlambda,
 +               const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +               int gmx_unused *global_atom_index)
 +{
 +    int  i, ai, aj, ak, type, m, t1, t2;
 +    rvec r_ij, r_kj;
 +    real cos_theta, dVdt, va, vtot;
 +    real rij_1, rij_2, rkj_1, rkj_2, rijrkj_1;
 +    rvec f_i, f_j, f_k;
 +    ivec jt, dt_ij, dt_kj;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +
 +        cos_theta  = g96bond_angle(x[ai], x[aj], x[ak], pbc, r_ij, r_kj, &t1, &t2);
 +
 +        *dvdlambda += g96harmonic(forceparams[type].harmonic.krA,
 +                                  forceparams[type].harmonic.krB,
 +                                  forceparams[type].harmonic.rA,
 +                                  forceparams[type].harmonic.rB,
 +                                  cos_theta, lambda, &va, &dVdt);
 +        vtot    += va;
 +
 +        rij_1    = gmx_invsqrt(iprod(r_ij, r_ij));
 +        rkj_1    = gmx_invsqrt(iprod(r_kj, r_kj));
 +        rij_2    = rij_1*rij_1;
 +        rkj_2    = rkj_1*rkj_1;
 +        rijrkj_1 = rij_1*rkj_1;                 /* 23 */
 +
 +#ifdef DEBUG
 +        if (debug)
 +        {
 +            fprintf(debug, "G96ANGLES: costheta = %10g  vth = %10g  dV/dct = %10g\n",
 +                    cos_theta, va, dVdt);
 +        }
 +#endif
 +        for (m = 0; (m < DIM); m++)     /*  42        */
 +        {
 +            f_i[m]    = dVdt*(r_kj[m]*rijrkj_1 - r_ij[m]*rij_2*cos_theta);
 +            f_k[m]    = dVdt*(r_ij[m]*rijrkj_1 - r_kj[m]*rkj_2*cos_theta);
 +            f_j[m]    = -f_i[m]-f_k[m];
 +            f[ai][m] += f_i[m];
 +            f[aj][m] += f_j[m];
 +            f[ak][m] += f_k[m];
 +        }
 +
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +            ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +            ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +            t1 = IVEC2IS(dt_ij);
 +            t2 = IVEC2IS(dt_kj);
 +        }
 +        rvec_inc(fshift[t1], f_i);
 +        rvec_inc(fshift[CENTRAL], f_j);
 +        rvec_inc(fshift[t2], f_k);          /* 9 */
 +        /* 163 TOTAL  */
 +    }
 +    return vtot;
 +}
 +
 +real cross_bond_bond(int nbonds,
 +                     const t_iatom forceatoms[], const t_iparams forceparams[],
 +                     const rvec x[], rvec f[], rvec fshift[],
 +                     const t_pbc *pbc, const t_graph *g,
 +                     real gmx_unused lambda, real gmx_unused *dvdlambda,
 +                     const t_mdatoms gmx_unused *md, t_fcdata gmx_unused  *fcd,
 +                     int gmx_unused *global_atom_index)
 +{
 +    /* Potential from Lawrence and Skimmer, Chem. Phys. Lett. 372 (2003)
 +     * pp. 842-847
 +     */
 +    int  i, ai, aj, ak, type, m, t1, t2;
 +    rvec r_ij, r_kj;
 +    real vtot, vrr, s1, s2, r1, r2, r1e, r2e, krr;
 +    rvec f_i, f_j, f_k;
 +    ivec jt, dt_ij, dt_kj;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        r1e  = forceparams[type].cross_bb.r1e;
 +        r2e  = forceparams[type].cross_bb.r2e;
 +        krr  = forceparams[type].cross_bb.krr;
 +
 +        /* Compute distance vectors ... */
 +        t1 = pbc_rvec_sub(pbc, x[ai], x[aj], r_ij);
 +        t2 = pbc_rvec_sub(pbc, x[ak], x[aj], r_kj);
 +
 +        /* ... and their lengths */
 +        r1 = norm(r_ij);
 +        r2 = norm(r_kj);
 +
 +        /* Deviations from ideality */
 +        s1 = r1-r1e;
 +        s2 = r2-r2e;
 +
 +        /* Energy (can be negative!) */
 +        vrr   = krr*s1*s2;
 +        vtot += vrr;
 +
 +        /* Forces */
 +        svmul(-krr*s2/r1, r_ij, f_i);
 +        svmul(-krr*s1/r2, r_kj, f_k);
 +
 +        for (m = 0; (m < DIM); m++)     /*  12        */
 +        {
 +            f_j[m]    = -f_i[m] - f_k[m];
 +            f[ai][m] += f_i[m];
 +            f[aj][m] += f_j[m];
 +            f[ak][m] += f_k[m];
 +        }
 +
 +        /* Virial stuff */
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +            ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +            ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +            t1 = IVEC2IS(dt_ij);
 +            t2 = IVEC2IS(dt_kj);
 +        }
 +        rvec_inc(fshift[t1], f_i);
 +        rvec_inc(fshift[CENTRAL], f_j);
 +        rvec_inc(fshift[t2], f_k);          /* 9 */
 +        /* 163 TOTAL  */
 +    }
 +    return vtot;
 +}
 +
 +real cross_bond_angle(int nbonds,
 +                      const t_iatom forceatoms[], const t_iparams forceparams[],
 +                      const rvec x[], rvec f[], rvec fshift[],
 +                      const t_pbc *pbc, const t_graph *g,
 +                      real gmx_unused lambda, real gmx_unused *dvdlambda,
 +                      const t_mdatoms gmx_unused *md, t_fcdata gmx_unused *fcd,
 +                      int gmx_unused *global_atom_index)
 +{
 +    /* Potential from Lawrence and Skimmer, Chem. Phys. Lett. 372 (2003)
 +     * pp. 842-847
 +     */
 +    int  i, ai, aj, ak, type, m, t1, t2, t3;
 +    rvec r_ij, r_kj, r_ik;
 +    real vtot, vrt, s1, s2, s3, r1, r2, r3, r1e, r2e, r3e, krt, k1, k2, k3;
 +    rvec f_i, f_j, f_k;
 +    ivec jt, dt_ij, dt_kj;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        r1e  = forceparams[type].cross_ba.r1e;
 +        r2e  = forceparams[type].cross_ba.r2e;
 +        r3e  = forceparams[type].cross_ba.r3e;
 +        krt  = forceparams[type].cross_ba.krt;
 +
 +        /* Compute distance vectors ... */
 +        t1 = pbc_rvec_sub(pbc, x[ai], x[aj], r_ij);
 +        t2 = pbc_rvec_sub(pbc, x[ak], x[aj], r_kj);
 +        t3 = pbc_rvec_sub(pbc, x[ai], x[ak], r_ik);
 +
 +        /* ... and their lengths */
 +        r1 = norm(r_ij);
 +        r2 = norm(r_kj);
 +        r3 = norm(r_ik);
 +
 +        /* Deviations from ideality */
 +        s1 = r1-r1e;
 +        s2 = r2-r2e;
 +        s3 = r3-r3e;
 +
 +        /* Energy (can be negative!) */
 +        vrt   = krt*s3*(s1+s2);
 +        vtot += vrt;
 +
 +        /* Forces */
 +        k1 = -krt*(s3/r1);
 +        k2 = -krt*(s3/r2);
 +        k3 = -krt*(s1+s2)/r3;
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            f_i[m] = k1*r_ij[m] + k3*r_ik[m];
 +            f_k[m] = k2*r_kj[m] - k3*r_ik[m];
 +            f_j[m] = -f_i[m] - f_k[m];
 +        }
 +
 +        for (m = 0; (m < DIM); m++)     /*  12        */
 +        {
 +            f[ai][m] += f_i[m];
 +            f[aj][m] += f_j[m];
 +            f[ak][m] += f_k[m];
 +        }
 +
 +        /* Virial stuff */
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +            ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +            ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +            t1 = IVEC2IS(dt_ij);
 +            t2 = IVEC2IS(dt_kj);
 +        }
 +        rvec_inc(fshift[t1], f_i);
 +        rvec_inc(fshift[CENTRAL], f_j);
 +        rvec_inc(fshift[t2], f_k);          /* 9 */
 +        /* 163 TOTAL  */
 +    }
 +    return vtot;
 +}
 +
 +static real bonded_tab(const char *type, int table_nr,
 +                       const bondedtable_t *table, real kA, real kB, real r,
 +                       real lambda, real *V, real *F)
 +{
 +    real k, tabscale, *VFtab, rt, eps, eps2, Yt, Ft, Geps, Heps2, Fp, VV, FF;
 +    int  n0, nnn;
 +    real v, f, dvdlambda;
 +
 +    k = (1.0 - lambda)*kA + lambda*kB;
 +
 +    tabscale = table->scale;
 +    VFtab    = table->data;
 +
 +    rt    = r*tabscale;
 +    n0    = rt;
 +    if (n0 >= table->n)
 +    {
 +        gmx_fatal(FARGS, "A tabulated %s interaction table number %d is out of the table range: r %f, between table indices %d and %d, table length %d",
 +                  type, table_nr, r, n0, n0+1, table->n);
 +    }
 +    eps   = rt - n0;
 +    eps2  = eps*eps;
 +    nnn   = 4*n0;
 +    Yt    = VFtab[nnn];
 +    Ft    = VFtab[nnn+1];
 +    Geps  = VFtab[nnn+2]*eps;
 +    Heps2 = VFtab[nnn+3]*eps2;
 +    Fp    = Ft + Geps + Heps2;
 +    VV    = Yt + Fp*eps;
 +    FF    = Fp + Geps + 2.0*Heps2;
 +
 +    *F         = -k*FF*tabscale;
 +    *V         = k*VV;
 +    dvdlambda  = (kB - kA)*VV;
 +
 +    return dvdlambda;
 +
 +    /* That was 22 flops */
 +}
 +
 +real tab_bonds(int nbonds,
 +               const t_iatom forceatoms[], const t_iparams forceparams[],
 +               const rvec x[], rvec f[], rvec fshift[],
 +               const t_pbc *pbc, const t_graph *g,
 +               real lambda, real *dvdlambda,
 +               const t_mdatoms gmx_unused *md, t_fcdata *fcd,
 +               int gmx_unused  *global_atom_index)
 +{
 +    int  i, m, ki, ai, aj, type, table;
 +    real dr, dr2, fbond, vbond, fij, vtot;
 +    rvec dx;
 +    ivec dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx); /*   3      */
 +        dr2  = iprod(dx, dx);                       /*   5            */
 +        dr   = dr2*gmx_invsqrt(dr2);                /*  10            */
 +
 +        table = forceparams[type].tab.table;
 +
 +        *dvdlambda += bonded_tab("bond", table,
 +                                 &fcd->bondtab[table],
 +                                 forceparams[type].tab.kA,
 +                                 forceparams[type].tab.kB,
 +                                 dr, lambda, &vbond, &fbond); /*  22 */
 +
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +
 +        vtot  += vbond;            /* 1*/
 +        fbond *= gmx_invsqrt(dr2); /*   6             */
 +#ifdef DEBUG
 +        if (debug)
 +        {
 +            fprintf(debug, "TABBONDS: dr = %10g  vbond = %10g  fbond = %10g\n",
 +                    dr, vbond, fbond);
 +        }
 +#endif
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)     /*  15                */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }               /* 62 TOTAL       */
 +    return vtot;
 +}
 +
 +real tab_angles(int nbonds,
 +                const t_iatom forceatoms[], const t_iparams forceparams[],
 +                const rvec x[], rvec f[], rvec fshift[],
 +                const t_pbc *pbc, const t_graph *g,
 +                real lambda, real *dvdlambda,
 +                const t_mdatoms gmx_unused  *md, t_fcdata *fcd,
 +                int gmx_unused *global_atom_index)
 +{
 +    int  i, ai, aj, ak, t1, t2, type, table;
 +    rvec r_ij, r_kj;
 +    real cos_theta, cos_theta2, theta, dVdt, va, vtot;
 +    ivec jt, dt_ij, dt_kj;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +
 +        theta  = bond_angle(x[ai], x[aj], x[ak], pbc,
 +                            r_ij, r_kj, &cos_theta, &t1, &t2); /*  41         */
 +
 +        table = forceparams[type].tab.table;
 +
 +        *dvdlambda += bonded_tab("angle", table,
 +                                 &fcd->angletab[table],
 +                                 forceparams[type].tab.kA,
 +                                 forceparams[type].tab.kB,
 +                                 theta, lambda, &va, &dVdt); /*  22  */
 +        vtot += va;
 +
 +        cos_theta2 = sqr(cos_theta);            /*   1                */
 +        if (cos_theta2 < 1)
 +        {
 +            int  m;
 +            real snt, st, sth;
 +            real cik, cii, ckk;
 +            real nrkj2, nrij2;
 +            rvec f_i, f_j, f_k;
 +
 +            st  = dVdt*gmx_invsqrt(1 - cos_theta2); /*  12            */
 +            sth = st*cos_theta;                     /*   1            */
 +#ifdef DEBUG
 +            if (debug)
 +            {
 +                fprintf(debug, "ANGLES: theta = %10g  vth = %10g  dV/dtheta = %10g\n",
 +                        theta*RAD2DEG, va, dVdt);
 +            }
 +#endif
 +            nrkj2 = iprod(r_kj, r_kj);  /*   5                */
 +            nrij2 = iprod(r_ij, r_ij);
 +
 +            cik = st*gmx_invsqrt(nrkj2*nrij2); /*  12         */
 +            cii = sth/nrij2;                   /*  10         */
 +            ckk = sth/nrkj2;                   /*  10         */
 +
 +            for (m = 0; (m < DIM); m++)        /*  39         */
 +            {
 +                f_i[m]    = -(cik*r_kj[m]-cii*r_ij[m]);
 +                f_k[m]    = -(cik*r_ij[m]-ckk*r_kj[m]);
 +                f_j[m]    = -f_i[m]-f_k[m];
 +                f[ai][m] += f_i[m];
 +                f[aj][m] += f_j[m];
 +                f[ak][m] += f_k[m];
 +            }
 +            if (g)
 +            {
 +                copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +                t1 = IVEC2IS(dt_ij);
 +                t2 = IVEC2IS(dt_kj);
 +            }
 +            rvec_inc(fshift[t1], f_i);
 +            rvec_inc(fshift[CENTRAL], f_j);
 +            rvec_inc(fshift[t2], f_k);
 +        }                                       /* 169 TOTAL  */
 +    }
 +    return vtot;
 +}
 +
 +real tab_dihs(int nbonds,
 +              const t_iatom forceatoms[], const t_iparams forceparams[],
 +              const rvec x[], rvec f[], rvec fshift[],
 +              const t_pbc *pbc, const t_graph *g,
 +              real lambda, real *dvdlambda,
 +              const t_mdatoms gmx_unused *md, t_fcdata *fcd,
 +              int gmx_unused *global_atom_index)
 +{
 +    int  i, type, ai, aj, ak, al, table;
 +    int  t1, t2, t3;
 +    rvec r_ij, r_kj, r_kl, m, n;
 +    real phi, sign, ddphi, vpd, vtot;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        al   = forceatoms[i++];
 +
 +        phi = dih_angle(x[ai], x[aj], x[ak], x[al], pbc, r_ij, r_kj, r_kl, m, n,
 +                        &sign, &t1, &t2, &t3);  /*  84  */
 +
 +        table = forceparams[type].tab.table;
 +
 +        /* Hopefully phi+M_PI never results in values < 0 */
 +        *dvdlambda += bonded_tab("dihedral", table,
 +                                 &fcd->dihtab[table],
 +                                 forceparams[type].tab.kA,
 +                                 forceparams[type].tab.kB,
 +                                 phi+M_PI, lambda, &vpd, &ddphi);
 +
 +        vtot += vpd;
 +        do_dih_fup(ai, aj, ak, al, -ddphi, r_ij, r_kj, r_kl, m, n,
 +                   f, fshift, pbc, g, x, t1, t2, t3); /* 112  */
 +
 +#ifdef DEBUG
 +        fprintf(debug, "pdih: (%d,%d,%d,%d) phi=%g\n",
 +                ai, aj, ak, al, phi);
 +#endif
 +    } /* 227 TOTAL  */
 +
 +    return vtot;
 +}
 +
 +/* Return if this is a potential calculated in bondfree.c,
 + * i.e. an interaction that actually calculates a potential and
 + * works on multiple atoms (not e.g. a connection or a position restraint).
 + */
 +static gmx_inline gmx_bool ftype_is_bonded_potential(int ftype)
 +{
 +    return
 +        (interaction_function[ftype].flags & IF_BOND) &&
 +        !(ftype == F_CONNBONDS || ftype == F_POSRES || ftype == F_FBPOSRES) &&
 +        (ftype < F_GB12 || ftype > F_GB14);
 +}
 +
 +static void divide_bondeds_over_threads(t_idef *idef, int nthreads)
 +{
 +    int ftype;
 +    int nat1;
 +    int t;
 +    int il_nr_thread;
 +
 +    idef->nthreads = nthreads;
 +
 +    if (F_NRE*(nthreads+1) > idef->il_thread_division_nalloc)
 +    {
 +        idef->il_thread_division_nalloc = F_NRE*(nthreads+1);
 +        snew(idef->il_thread_division, idef->il_thread_division_nalloc);
 +    }
 +
 +    for (ftype = 0; ftype < F_NRE; ftype++)
 +    {
 +        if (ftype_is_bonded_potential(ftype))
 +        {
 +            nat1 = interaction_function[ftype].nratoms + 1;
 +
 +            for (t = 0; t <= nthreads; t++)
 +            {
 +                /* Divide the interactions equally over the threads.
 +                 * When the different types of bonded interactions
 +                 * are distributed roughly equally over the threads,
 +                 * this should lead to well localized output into
 +                 * the force buffer on each thread.
 +                 * If this is not the case, a more advanced scheme
 +                 * (not implemented yet) will do better.
 +                 */
 +                il_nr_thread = (((idef->il[ftype].nr/nat1)*t)/nthreads)*nat1;
 +
 +                /* Ensure that distance restraint pairs with the same label
 +                 * end up on the same thread.
 +                 * This is slighlty tricky code, since the next for iteration
 +                 * may have an initial il_nr_thread lower than the final value
 +                 * in the previous iteration, but this will anyhow be increased
 +                 * to the approriate value again by this while loop.
 +                 */
 +                while (ftype == F_DISRES &&
 +                       il_nr_thread > 0 &&
 +                       il_nr_thread < idef->il[ftype].nr &&
 +                       idef->iparams[idef->il[ftype].iatoms[il_nr_thread]].disres.label ==
 +                       idef->iparams[idef->il[ftype].iatoms[il_nr_thread-nat1]].disres.label)
 +                {
 +                    il_nr_thread += nat1;
 +                }
 +
 +                idef->il_thread_division[ftype*(nthreads+1)+t] = il_nr_thread;
 +            }
 +        }
 +    }
 +}
 +
 +static unsigned
 +calc_bonded_reduction_mask(const t_idef *idef,
 +                           int shift,
 +                           int t, int nt)
 +{
 +    unsigned mask;
 +    int      ftype, nb, nat1, nb0, nb1, i, a;
 +
 +    mask = 0;
 +
 +    for (ftype = 0; ftype < F_NRE; ftype++)
 +    {
 +        if (ftype_is_bonded_potential(ftype))
 +        {
 +            nb = idef->il[ftype].nr;
 +            if (nb > 0)
 +            {
 +                nat1 = interaction_function[ftype].nratoms + 1;
 +
 +                /* Divide this interaction equally over the threads.
 +                 * This is not stored: should match division in calc_bonds.
 +                 */
 +                nb0 = idef->il_thread_division[ftype*(nt+1)+t];
 +                nb1 = idef->il_thread_division[ftype*(nt+1)+t+1];
 +
 +                for (i = nb0; i < nb1; i += nat1)
 +                {
 +                    for (a = 1; a < nat1; a++)
 +                    {
 +                        mask |= (1U << (idef->il[ftype].iatoms[i+a]>>shift));
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    return mask;
 +}
 +
 +void setup_bonded_threading(t_forcerec   *fr, t_idef *idef)
 +{
 +#define MAX_BLOCK_BITS 32
 +    int t;
 +    int ctot, c, b;
 +
 +    assert(fr->nthreads >= 1);
 +
 +    /* Divide the bonded interaction over the threads */
 +    divide_bondeds_over_threads(idef, fr->nthreads);
 +
 +    if (fr->nthreads == 1)
 +    {
 +        fr->red_nblock = 0;
 +
 +        return;
 +    }
 +
 +    /* We divide the force array in a maximum of 32 blocks.
 +     * Minimum force block reduction size is 2^6=64.
 +     */
 +    fr->red_ashift = 6;
 +    while (fr->natoms_force > (int)(MAX_BLOCK_BITS*(1U<<fr->red_ashift)))
 +    {
 +        fr->red_ashift++;
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "bonded force buffer block atom shift %d bits\n",
 +                fr->red_ashift);
 +    }
 +
 +    /* Determine to which blocks each thread's bonded force calculation
 +     * contributes. Store this is a mask for each thread.
 +     */
 +#pragma omp parallel for num_threads(fr->nthreads) schedule(static)
 +    for (t = 1; t < fr->nthreads; t++)
 +    {
 +        fr->f_t[t].red_mask =
 +            calc_bonded_reduction_mask(idef, fr->red_ashift, t, fr->nthreads);
 +    }
 +
 +    /* Determine the maximum number of blocks we need to reduce over */
 +    fr->red_nblock = 0;
 +    ctot           = 0;
 +    for (t = 0; t < fr->nthreads; t++)
 +    {
 +        c = 0;
 +        for (b = 0; b < MAX_BLOCK_BITS; b++)
 +        {
 +            if (fr->f_t[t].red_mask & (1U<<b))
 +            {
 +                fr->red_nblock = max(fr->red_nblock, b+1);
 +                c++;
 +            }
 +        }
 +        if (debug)
 +        {
 +            fprintf(debug, "thread %d flags %x count %d\n",
 +                    t, fr->f_t[t].red_mask, c);
 +        }
 +        ctot += c;
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "Number of blocks to reduce: %d of size %d\n",
 +                fr->red_nblock, 1<<fr->red_ashift);
 +        fprintf(debug, "Reduction density %.2f density/#thread %.2f\n",
 +                ctot*(1<<fr->red_ashift)/(double)fr->natoms_force,
 +                ctot*(1<<fr->red_ashift)/(double)(fr->natoms_force*fr->nthreads));
 +    }
 +}
 +
 +static void zero_thread_forces(f_thread_t *f_t, int n,
 +                               int nblock, int blocksize)
 +{
 +    int b, a0, a1, a, i, j;
 +
 +    if (n > f_t->f_nalloc)
 +    {
 +        f_t->f_nalloc = over_alloc_large(n);
 +        srenew(f_t->f, f_t->f_nalloc);
 +    }
 +
 +    if (f_t->red_mask != 0)
 +    {
 +        for (b = 0; b < nblock; b++)
 +        {
 +            if (f_t->red_mask && (1U<<b))
 +            {
 +                a0 = b*blocksize;
 +                a1 = min((b+1)*blocksize, n);
 +                for (a = a0; a < a1; a++)
 +                {
 +                    clear_rvec(f_t->f[a]);
 +                }
 +            }
 +        }
 +    }
 +    for (i = 0; i < SHIFTS; i++)
 +    {
 +        clear_rvec(f_t->fshift[i]);
 +    }
 +    for (i = 0; i < F_NRE; i++)
 +    {
 +        f_t->ener[i] = 0;
 +    }
 +    for (i = 0; i < egNR; i++)
 +    {
 +        for (j = 0; j < f_t->grpp.nener; j++)
 +        {
 +            f_t->grpp.ener[i][j] = 0;
 +        }
 +    }
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        f_t->dvdl[i] = 0;
 +    }
 +}
 +
 +static void reduce_thread_force_buffer(int n, rvec *f,
 +                                       int nthreads, f_thread_t *f_t,
 +                                       int nblock, int block_size)
 +{
 +    /* The max thread number is arbitrary,
 +     * we used a fixed number to avoid memory management.
 +     * Using more than 16 threads is probably never useful performance wise.
 +     */
 +#define MAX_BONDED_THREADS 256
 +    int b;
 +
 +    if (nthreads > MAX_BONDED_THREADS)
 +    {
 +        gmx_fatal(FARGS, "Can not reduce bonded forces on more than %d threads",
 +                  MAX_BONDED_THREADS);
 +    }
 +
 +    /* This reduction can run on any number of threads,
 +     * independently of nthreads.
 +     */
 +#pragma omp parallel for num_threads(nthreads) schedule(static)
 +    for (b = 0; b < nblock; b++)
 +    {
 +        rvec *fp[MAX_BONDED_THREADS];
 +        int   nfb, ft, fb;
 +        int   a0, a1, a;
 +
 +        /* Determine which threads contribute to this block */
 +        nfb = 0;
 +        for (ft = 1; ft < nthreads; ft++)
 +        {
 +            if (f_t[ft].red_mask & (1U<<b))
 +            {
 +                fp[nfb++] = f_t[ft].f;
 +            }
 +        }
 +        if (nfb > 0)
 +        {
 +            /* Reduce force buffers for threads that contribute */
 +            a0 =  b   *block_size;
 +            a1 = (b+1)*block_size;
 +            a1 = min(a1, n);
 +            for (a = a0; a < a1; a++)
 +            {
 +                for (fb = 0; fb < nfb; fb++)
 +                {
 +                    rvec_inc(f[a], fp[fb][a]);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static void reduce_thread_forces(int n, rvec *f, rvec *fshift,
 +                                 real *ener, gmx_grppairener_t *grpp, real *dvdl,
 +                                 int nthreads, f_thread_t *f_t,
 +                                 int nblock, int block_size,
 +                                 gmx_bool bCalcEnerVir,
 +                                 gmx_bool bDHDL)
 +{
 +    if (nblock > 0)
 +    {
 +        /* Reduce the bonded force buffer */
 +        reduce_thread_force_buffer(n, f, nthreads, f_t, nblock, block_size);
 +    }
 +
 +    /* When necessary, reduce energy and virial using one thread only */
 +    if (bCalcEnerVir)
 +    {
 +        int t, i, j;
 +
 +        for (i = 0; i < SHIFTS; i++)
 +        {
 +            for (t = 1; t < nthreads; t++)
 +            {
 +                rvec_inc(fshift[i], f_t[t].fshift[i]);
 +            }
 +        }
 +        for (i = 0; i < F_NRE; i++)
 +        {
 +            for (t = 1; t < nthreads; t++)
 +            {
 +                ener[i] += f_t[t].ener[i];
 +            }
 +        }
 +        for (i = 0; i < egNR; i++)
 +        {
 +            for (j = 0; j < f_t[1].grpp.nener; j++)
 +            {
 +                for (t = 1; t < nthreads; t++)
 +                {
 +
 +                    grpp->ener[i][j] += f_t[t].grpp.ener[i][j];
 +                }
 +            }
 +        }
 +        if (bDHDL)
 +        {
 +            for (i = 0; i < efptNR; i++)
 +            {
 +
 +                for (t = 1; t < nthreads; t++)
 +                {
 +                    dvdl[i] += f_t[t].dvdl[i];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static real calc_one_bond(FILE *fplog, int thread,
 +                          int ftype, const t_idef *idef,
 +                          rvec x[], rvec f[], rvec fshift[],
 +                          t_forcerec *fr,
 +                          const t_pbc *pbc, const t_graph *g,
 +                          gmx_grppairener_t *grpp,
 +                          t_nrnb *nrnb,
 +                          real *lambda, real *dvdl,
 +                          const t_mdatoms *md, t_fcdata *fcd,
 +                          gmx_bool bCalcEnerVir,
 +                          int *global_atom_index, gmx_bool bPrintSepPot)
 +{
 +    int      nat1, nbonds, efptFTYPE;
 +    real     v = 0;
 +    t_iatom *iatoms;
 +    int      nb0, nbn;
 +
 +    if (IS_RESTRAINT_TYPE(ftype))
 +    {
 +        efptFTYPE = efptRESTRAINT;
 +    }
 +    else
 +    {
 +        efptFTYPE = efptBONDED;
 +    }
 +
 +    nat1      = interaction_function[ftype].nratoms + 1;
 +    nbonds    = idef->il[ftype].nr/nat1;
 +    iatoms    = idef->il[ftype].iatoms;
 +
 +    nb0 = idef->il_thread_division[ftype*(idef->nthreads+1)+thread];
 +    nbn = idef->il_thread_division[ftype*(idef->nthreads+1)+thread+1] - nb0;
 +
 +    if (!IS_LISTED_LJ_C(ftype))
 +    {
 +        if (ftype == F_CMAP)
 +        {
 +            v = cmap_dihs(nbn, iatoms+nb0,
 +                          idef->iparams, &idef->cmap_grid,
 +                          (const rvec*)x, f, fshift,
 +                          pbc, g, lambda[efptFTYPE], &(dvdl[efptFTYPE]),
 +                          md, fcd, global_atom_index);
 +        }
 +#ifdef GMX_SIMD_HAVE_REAL
 +        else if (ftype == F_ANGLES &&
 +                 !bCalcEnerVir && fr->efep == efepNO)
 +        {
 +            /* No energies, shift forces, dvdl */
 +            angles_noener_simd(nbn, idef->il[ftype].iatoms+nb0,
 +                               idef->iparams,
 +                               (const rvec*)x, f,
 +                               pbc, g, lambda[efptFTYPE], md, fcd,
 +                               global_atom_index);
 +            v = 0;
 +        }
 +#endif
 +        else if (ftype == F_PDIHS &&
 +                 !bCalcEnerVir && fr->efep == efepNO)
 +        {
 +            /* No energies, shift forces, dvdl */
 +#ifdef GMX_SIMD_HAVE_REAL
 +            pdihs_noener_simd
 +#else
 +            pdihs_noener
 +#endif
 +                (nbn, idef->il[ftype].iatoms+nb0,
 +                idef->iparams,
 +                (const rvec*)x, f,
 +                pbc, g, lambda[efptFTYPE], md, fcd,
 +                global_atom_index);
 +            v = 0;
 +        }
 +        else
 +        {
 +            v = interaction_function[ftype].ifunc(nbn, iatoms+nb0,
 +                                                  idef->iparams,
 +                                                  (const rvec*)x, f, fshift,
 +                                                  pbc, g, lambda[efptFTYPE], &(dvdl[efptFTYPE]),
 +                                                  md, fcd, global_atom_index);
 +        }
 +        if (bPrintSepPot)
 +        {
 +            fprintf(fplog, "  %-23s #%4d  V %12.5e  dVdl %12.5e\n",
 +                    interaction_function[ftype].longname,
 +                    nbonds, v, lambda[efptFTYPE]);
 +        }
 +    }
 +    else
 +    {
 +        v = do_nonbonded_listed(ftype, nbn, iatoms+nb0, idef->iparams, (const rvec*)x, f, fshift,
 +                                pbc, g, lambda, dvdl, md, fr, grpp, global_atom_index);
 +
 +        if (bPrintSepPot)
 +        {
 +            fprintf(fplog, "  %-5s + %-15s #%4d                  dVdl %12.5e\n",
 +                    interaction_function[ftype].longname,
 +                    interaction_function[F_LJ14].longname, nbonds, dvdl[efptVDW]);
 +            fprintf(fplog, "  %-5s + %-15s #%4d                  dVdl %12.5e\n",
 +                    interaction_function[ftype].longname,
 +                    interaction_function[F_COUL14].longname, nbonds, dvdl[efptCOUL]);
 +        }
 +    }
 +
 +    if (thread == 0)
 +    {
 +        inc_nrnb(nrnb, interaction_function[ftype].nrnb_ind, nbonds);
 +    }
 +
 +    return v;
 +}
 +
 +void calc_bonds(FILE *fplog, const gmx_multisim_t *ms,
 +                const t_idef *idef,
 +                rvec x[], history_t *hist,
 +                rvec f[], t_forcerec *fr,
 +                const t_pbc *pbc, const t_graph *g,
 +                gmx_enerdata_t *enerd, t_nrnb *nrnb,
 +                real *lambda,
 +                const t_mdatoms *md,
 +                t_fcdata *fcd, int *global_atom_index,
 +                t_atomtypes gmx_unused *atype, gmx_genborn_t gmx_unused *born,
 +                int force_flags,
 +                gmx_bool bPrintSepPot, gmx_int64_t step)
 +{
 +    gmx_bool      bCalcEnerVir;
 +    int           i;
 +    real          v, dvdl[efptNR], dvdl_dum[efptNR]; /* The dummy array is to have a place to store the dhdl at other values
 +                                                        of lambda, which will be thrown away in the end*/
 +    const  t_pbc *pbc_null;
 +    char          buf[22];
 +    int           thread;
 +
 +    assert(fr->nthreads == idef->nthreads);
 +
 +    bCalcEnerVir = (force_flags & (GMX_FORCE_VIRIAL | GMX_FORCE_ENERGY));
 +
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        dvdl[i] = 0.0;
 +    }
 +    if (fr->bMolPBC)
 +    {
 +        pbc_null = pbc;
 +    }
 +    else
 +    {
 +        pbc_null = NULL;
 +    }
 +    if (bPrintSepPot)
 +    {
 +        fprintf(fplog, "Step %s: bonded V and dVdl for this node\n",
 +                gmx_step_str(step, buf));
 +    }
 +
 +#ifdef DEBUG
 +    if (g && debug)
 +    {
 +        p_graph(debug, "Bondage is fun", g);
 +    }
 +#endif
 +
 +    /* Do pre force calculation stuff which might require communication */
 +    if (idef->il[F_ORIRES].nr)
 +    {
 +        enerd->term[F_ORIRESDEV] =
 +            calc_orires_dev(ms, idef->il[F_ORIRES].nr,
 +                            idef->il[F_ORIRES].iatoms,
 +                            idef->iparams, md, (const rvec*)x,
 +                            pbc_null, fcd, hist);
 +    }
 +    if (idef->il[F_DISRES].nr)
 +    {
 +        calc_disres_R_6(idef->il[F_DISRES].nr,
 +                        idef->il[F_DISRES].iatoms,
 +                        idef->iparams, (const rvec*)x, pbc_null,
 +                        fcd, hist);
 +#ifdef GMX_MPI
 +        if (fcd->disres.nsystems > 1)
 +        {
 +            gmx_sum_sim(2*fcd->disres.nres, fcd->disres.Rt_6, ms);
 +        }
 +#endif
 +    }
 +
 +#pragma omp parallel for num_threads(fr->nthreads) schedule(static)
 +    for (thread = 0; thread < fr->nthreads; thread++)
 +    {
 +        int                ftype;
 +        real              *epot, v;
 +        /* thread stuff */
 +        rvec              *ft, *fshift;
 +        real              *dvdlt;
 +        gmx_grppairener_t *grpp;
 +
 +        if (thread == 0)
 +        {
 +            ft     = f;
 +            fshift = fr->fshift;
 +            epot   = enerd->term;
 +            grpp   = &enerd->grpp;
 +            dvdlt  = dvdl;
 +        }
 +        else
 +        {
 +            zero_thread_forces(&fr->f_t[thread], fr->natoms_force,
 +                               fr->red_nblock, 1<<fr->red_ashift);
 +
 +            ft     = fr->f_t[thread].f;
 +            fshift = fr->f_t[thread].fshift;
 +            epot   = fr->f_t[thread].ener;
 +            grpp   = &fr->f_t[thread].grpp;
 +            dvdlt  = fr->f_t[thread].dvdl;
 +        }
 +        /* Loop over all bonded force types to calculate the bonded forces */
 +        for (ftype = 0; (ftype < F_NRE); ftype++)
 +        {
 +            if (idef->il[ftype].nr > 0 && ftype_is_bonded_potential(ftype))
 +            {
 +                v = calc_one_bond(fplog, thread, ftype, idef, x,
 +                                  ft, fshift, fr, pbc_null, g, grpp,
 +                                  nrnb, lambda, dvdlt,
 +                                  md, fcd, bCalcEnerVir,
 +                                  global_atom_index, bPrintSepPot);
 +                epot[ftype] += v;
 +            }
 +        }
 +    }
 +    if (fr->nthreads > 1)
 +    {
 +        reduce_thread_forces(fr->natoms_force, f, fr->fshift,
 +                             enerd->term, &enerd->grpp, dvdl,
 +                             fr->nthreads, fr->f_t,
 +                             fr->red_nblock, 1<<fr->red_ashift,
 +                             bCalcEnerVir,
 +                             force_flags & GMX_FORCE_DHDL);
 +    }
 +    if (force_flags & GMX_FORCE_DHDL)
 +    {
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            enerd->dvdl_nonlin[i] += dvdl[i];
 +        }
 +    }
 +
 +    /* Copy the sum of violations for the distance restraints from fcd */
 +    if (fcd)
 +    {
 +        enerd->term[F_DISRESVIOL] = fcd->disres.sumviol;
 +
 +    }
 +}
 +
 +void calc_bonds_lambda(FILE *fplog,
 +                       const t_idef *idef,
 +                       rvec x[],
 +                       t_forcerec *fr,
 +                       const t_pbc *pbc, const t_graph *g,
 +                       gmx_grppairener_t *grpp, real *epot, t_nrnb *nrnb,
 +                       real *lambda,
 +                       const t_mdatoms *md,
 +                       t_fcdata *fcd,
 +                       int *global_atom_index)
 +{
 +    int           i, ftype, nr_nonperturbed, nr;
 +    real          v;
 +    real          dvdl_dum[efptNR];
 +    rvec         *f, *fshift;
 +    const  t_pbc *pbc_null;
 +    t_idef        idef_fe;
 +
 +    if (fr->bMolPBC)
 +    {
 +        pbc_null = pbc;
 +    }
 +    else
 +    {
 +        pbc_null = NULL;
 +    }
 +
 +    /* Copy the whole idef, so we can modify the contents locally */
 +    idef_fe          = *idef;
 +    idef_fe.nthreads = 1;
 +    snew(idef_fe.il_thread_division, F_NRE*(idef_fe.nthreads+1));
 +
 +    /* We already have the forces, so we use temp buffers here */
 +    snew(f, fr->natoms_force);
 +    snew(fshift, SHIFTS);
 +
 +    /* Loop over all bonded force types to calculate the bonded energies */
 +    for (ftype = 0; (ftype < F_NRE); ftype++)
 +    {
 +        if (ftype_is_bonded_potential(ftype))
 +        {
 +            /* Set the work range of thread 0 to the perturbed bondeds only */
 +            nr_nonperturbed                       = idef->il[ftype].nr_nonperturbed;
 +            nr                                    = idef->il[ftype].nr;
 +            idef_fe.il_thread_division[ftype*2+0] = nr_nonperturbed;
 +            idef_fe.il_thread_division[ftype*2+1] = nr;
 +
 +            /* This is only to get the flop count correct */
 +            idef_fe.il[ftype].nr = nr - nr_nonperturbed;
 +
 +            if (nr - nr_nonperturbed > 0)
 +            {
 +                v = calc_one_bond(fplog, 0, ftype, &idef_fe,
 +                                  x, f, fshift, fr, pbc_null, g,
 +                                  grpp, nrnb, lambda, dvdl_dum,
 +                                  md, fcd, TRUE,
 +                                  global_atom_index, FALSE);
 +                epot[ftype] += v;
 +            }
 +        }
 +    }
 +
 +    sfree(fshift);
 +    sfree(f);
 +
 +    sfree(idef_fe.il_thread_division);
 +}
index 1dd000c09eb9573876d7cffe64693afd91e9864f,0000000000000000000000000000000000000000..825a8968dad1cbb59ef6e5b94ce8b750b8019395
mode 100644,000000..100644
--- /dev/null
@@@ -1,2643 -1,0 +1,2681 @@@
-     gmx_bool mm;
-     mm = FALSE;
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 2008,2009,2010,2011,2012,2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
 +/* The source code in this file should be thread-safe.
 +   Please keep it that way. */
 +
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <string.h>
 +#include <time.h>
 +
 +#ifdef HAVE_SYS_TIME_H
 +#include <sys/time.h>
 +#endif
 +
 +#ifdef HAVE_UNISTD_H
 +#include <unistd.h>
 +#endif
 +
 +#ifdef GMX_NATIVE_WINDOWS
 +/* _chsize_s */
 +#include <io.h>
 +#include <sys/locking.h>
 +#endif
 +
 +#include "copyrite.h"
 +#include "names.h"
 +#include "typedefs.h"
 +#include "types/commrec.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "txtdump.h"
 +#include "vec.h"
 +#include "network.h"
 +#include "checkpoint.h"
 +#include "main.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include <fcntl.h>
 +
 +#include "gromacs/fileio/filenm.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gromacs/fileio/gmxfio.h"
 +#include "gromacs/fileio/xdrf.h"
 +#include "gromacs/fileio/xdr_datatype.h"
 +#include "gromacs/utility/baseversion.h"
 +#include "gmx_fatal.h"
 +
 +#include "buildinfo.h"
 +
 +#ifdef GMX_FAHCORE
 +#include "corewrap.h"
 +#endif
 +
 +#define CPT_MAGIC1 171817
 +#define CPT_MAGIC2 171819
 +#define CPTSTRLEN 1024
 +
 +#ifdef GMX_DOUBLE
 +#define GMX_CPT_BUILD_DP 1
 +#else
 +#define GMX_CPT_BUILD_DP 0
 +#endif
 +
 +/* cpt_version should normally only be changed
 + * when the header of footer format changes.
 + * The state data format itself is backward and forward compatible.
 + * But old code can not read a new entry that is present in the file
 + * (but can read a new format when new entries are not present).
 + */
 +static const int cpt_version = 16;
 +
 +
 +const char *est_names[estNR] =
 +{
 +    "FE-lambda",
 +    "box", "box-rel", "box-v", "pres_prev",
 +    "nosehoover-xi", "thermostat-integral",
 +    "x", "v", "SDx", "CGp", "LD-rng", "LD-rng-i",
 +    "disre_initf", "disre_rm3tav",
 +    "orire_initf", "orire_Dtav",
 +    "svir_prev", "nosehoover-vxi", "v_eta", "vol0", "nhpres_xi", "nhpres_vxi", "fvir_prev", "fep_state", "MC-rng", "MC-rng-i"
 +};
 +
 +enum {
 +    eeksEKIN_N, eeksEKINH, eeksDEKINDL, eeksMVCOS, eeksEKINF, eeksEKINO, eeksEKINSCALEF, eeksEKINSCALEH, eeksVSCALE, eeksEKINTOTAL, eeksNR
 +};
 +
 +const char *eeks_names[eeksNR] =
 +{
 +    "Ekin_n", "Ekinh", "dEkindlambda", "mv_cos",
 +    "Ekinf", "Ekinh_old", "EkinScaleF_NHC", "EkinScaleH_NHC", "Vscale_NHC", "Ekin_Total"
 +};
 +
 +enum {
 +    eenhENERGY_N, eenhENERGY_AVER, eenhENERGY_SUM, eenhENERGY_NSUM,
 +    eenhENERGY_SUM_SIM, eenhENERGY_NSUM_SIM,
 +    eenhENERGY_NSTEPS, eenhENERGY_NSTEPS_SIM,
 +    eenhENERGY_DELTA_H_NN,
 +    eenhENERGY_DELTA_H_LIST,
 +    eenhENERGY_DELTA_H_STARTTIME,
 +    eenhENERGY_DELTA_H_STARTLAMBDA,
 +    eenhNR
 +};
 +
 +const char *eenh_names[eenhNR] =
 +{
 +    "energy_n", "energy_aver", "energy_sum", "energy_nsum",
 +    "energy_sum_sim", "energy_nsum_sim",
 +    "energy_nsteps", "energy_nsteps_sim",
 +    "energy_delta_h_nn",
 +    "energy_delta_h_list",
 +    "energy_delta_h_start_time",
 +    "energy_delta_h_start_lambda"
 +};
 +
 +/* free energy history variables -- need to be preserved over checkpoint */
 +enum {
 +    edfhBEQUIL, edfhNATLAMBDA, edfhWLHISTO, edfhWLDELTA, edfhSUMWEIGHTS, edfhSUMDG, edfhSUMMINVAR, edfhSUMVAR,
 +    edfhACCUMP, edfhACCUMM, edfhACCUMP2, edfhACCUMM2, edfhTIJ, edfhTIJEMP, edfhNR
 +};
 +/* free energy history variable names  */
 +const char *edfh_names[edfhNR] =
 +{
 +    "bEquilibrated", "N_at_state", "Wang-Landau Histogram", "Wang-Landau Delta", "Weights", "Free Energies", "minvar", "variance",
 +    "accumulated_plus", "accumulated_minus", "accumulated_plus_2",  "accumulated_minus_2", "Tij", "Tij_empirical"
 +};
 +
 +#ifdef GMX_NATIVE_WINDOWS
 +static int
 +gmx_wintruncate(const char *filename, __int64 size)
 +{
 +#ifdef GMX_FAHCORE
 +    /*we do this elsewhere*/
 +    return 0;
 +#else
 +    FILE *fp;
 +    int   rc;
 +
 +    fp = fopen(filename, "rb+");
 +
 +    if (fp == NULL)
 +    {
 +        return -1;
 +    }
 +
 +    return _chsize_s( fileno(fp), size);
 +#endif
 +}
 +#endif
 +
 +
 +enum {
 +    ecprREAL, ecprRVEC, ecprMATRIX
 +};
 +
 +enum {
 +    cptpEST, cptpEEKS, cptpEENH, cptpEDFH
 +};
 +/* enums for the different components of checkpoint variables, replacing the hard coded ones.
 +   cptpEST - state variables.
 +   cptpEEKS - Kinetic energy state variables.
 +   cptpEENH - Energy history state variables.
 +   cptpEDFH - free energy history variables.
 + */
 +
 +
 +static const char *st_names(int cptp, int ecpt)
 +{
 +    switch (cptp)
 +    {
 +        case cptpEST: return est_names [ecpt]; break;
 +        case cptpEEKS: return eeks_names[ecpt]; break;
 +        case cptpEENH: return eenh_names[ecpt]; break;
 +        case cptpEDFH: return edfh_names[ecpt]; break;
 +    }
 +
 +    return NULL;
 +}
 +
 +static void cp_warning(FILE *fp)
 +{
 +    fprintf(fp, "\nWARNING: Checkpoint file is corrupted or truncated\n\n");
 +}
 +
 +static void cp_error()
 +{
 +    gmx_fatal(FARGS, "Checkpoint file corrupted/truncated, or maybe you are out of disk space?");
 +}
 +
 +static void do_cpt_string_err(XDR *xd, gmx_bool bRead, const char *desc, char **s, FILE *list)
 +{
 +    bool_t res = 0;
 +
 +    if (bRead)
 +    {
 +        snew(*s, CPTSTRLEN);
 +    }
 +    res = xdr_string(xd, s, CPTSTRLEN);
 +    if (res == 0)
 +    {
 +        cp_error();
 +    }
 +    if (list)
 +    {
 +        fprintf(list, "%s = %s\n", desc, *s);
 +        sfree(*s);
 +    }
 +}
 +
 +static int do_cpt_int(XDR *xd, const char *desc, int *i, FILE *list)
 +{
 +    bool_t res = 0;
 +
 +    res = xdr_int(xd, i);
 +    if (res == 0)
 +    {
 +        return -1;
 +    }
 +    if (list)
 +    {
 +        fprintf(list, "%s = %d\n", desc, *i);
 +    }
 +    return 0;
 +}
 +
 +static int do_cpt_u_chars(XDR *xd, const char *desc, int n, unsigned char *i, FILE *list)
 +{
 +    bool_t res = 1;
 +    int    j;
 +    if (list)
 +    {
 +        fprintf(list, "%s = ", desc);
 +    }
 +    for (j = 0; j < n && res; j++)
 +    {
 +        res &= xdr_u_char(xd, &i[j]);
 +        if (list)
 +        {
 +            fprintf(list, "%02x", i[j]);
 +        }
 +    }
 +    if (list)
 +    {
 +        fprintf(list, "\n");
 +    }
 +    if (res == 0)
 +    {
 +        return -1;
 +    }
 +
 +    return 0;
 +}
 +
 +static void do_cpt_int_err(XDR *xd, const char *desc, int *i, FILE *list)
 +{
 +    if (do_cpt_int(xd, desc, i, list) < 0)
 +    {
 +        cp_error();
 +    }
 +}
 +
 +static void do_cpt_step_err(XDR *xd, const char *desc, gmx_int64_t *i, FILE *list)
 +{
 +    bool_t res = 0;
 +    char   buf[STEPSTRSIZE];
 +
 +    res = xdr_int64(xd, i);
 +    if (res == 0)
 +    {
 +        cp_error();
 +    }
 +    if (list)
 +    {
 +        fprintf(list, "%s = %s\n", desc, gmx_step_str(*i, buf));
 +    }
 +}
 +
 +static void do_cpt_double_err(XDR *xd, const char *desc, double *f, FILE *list)
 +{
 +    bool_t res = 0;
 +
 +    res = xdr_double(xd, f);
 +    if (res == 0)
 +    {
 +        cp_error();
 +    }
 +    if (list)
 +    {
 +        fprintf(list, "%s = %f\n", desc, *f);
 +    }
 +}
 +
 +static void do_cpt_real_err(XDR *xd, real *f)
 +{
 +    bool_t res = 0;
 +
 +#ifdef GMX_DOUBLE
 +    res = xdr_double(xd, f);
 +#else
 +    res = xdr_float(xd, f);
 +#endif
 +    if (res == 0)
 +    {
 +        cp_error();
 +    }
 +}
 +
 +static void do_cpt_n_rvecs_err(XDR *xd, const char *desc, int n, rvec f[], FILE *list)
 +{
 +    int i, j;
 +
 +    for (i = 0; i < n; i++)
 +    {
 +        for (j = 0; j < DIM; j++)
 +        {
 +            do_cpt_real_err(xd, &f[i][j]);
 +        }
 +    }
 +
 +    if (list)
 +    {
 +        pr_rvecs(list, 0, desc, f, n);
 +    }
 +}
 +
 +/* If nval >= 0, nval is used; on read this should match the passed value.
 + * If nval n<0, *nptr is used; on read the value is stored in nptr
 + */
 +static int do_cpte_reals_low(XDR *xd, int cptp, int ecpt, int sflags,
 +                             int nval, int *nptr, real **v,
 +                             FILE *list, int erealtype)
 +{
 +    bool_t  res = 0;
 +#ifndef GMX_DOUBLE
 +    int     dtc = xdr_datatype_float;
 +#else
 +    int     dtc = xdr_datatype_double;
 +#endif
 +    real   *vp, *va = NULL;
 +    float  *vf;
 +    double *vd;
 +    int     nf, dt, i;
 +
 +    if (list == NULL)
 +    {
 +        if (nval >= 0)
 +        {
 +            nf = nval;
 +        }
 +        else
 +        {
 +            if (nptr == NULL)
 +            {
 +                gmx_incons("*ntpr=NULL in do_cpte_reals_low");
 +            }
 +            nf = *nptr;
 +        }
 +    }
 +    res = xdr_int(xd, &nf);
 +    if (res == 0)
 +    {
 +        return -1;
 +    }
 +    if (list == NULL)
 +    {
 +        if (nval >= 0)
 +        {
 +            if (nf != nval)
 +            {
 +                gmx_fatal(FARGS, "Count mismatch for state entry %s, code count is %d, file count is %d\n", st_names(cptp, ecpt), nval, nf);
 +            }
 +        }
 +        else
 +        {
 +            *nptr = nf;
 +        }
 +    }
 +    dt  = dtc;
 +    res = xdr_int(xd, &dt);
 +    if (res == 0)
 +    {
 +        return -1;
 +    }
 +    if (dt != dtc)
 +    {
 +        fprintf(stderr, "Precision mismatch for state entry %s, code precision is %s, file precision is %s\n",
 +                st_names(cptp, ecpt), xdr_datatype_names[dtc],
 +                xdr_datatype_names[dt]);
 +    }
 +    if (list || !(sflags & (1<<ecpt)))
 +    {
 +        snew(va, nf);
 +        vp = va;
 +    }
 +    else
 +    {
 +        if (*v == NULL)
 +        {
 +            snew(*v, nf);
 +        }
 +        vp = *v;
 +    }
 +    if (dt == xdr_datatype_float)
 +    {
 +        if (dtc == xdr_datatype_float)
 +        {
 +            vf = (float *)vp;
 +        }
 +        else
 +        {
 +            snew(vf, nf);
 +        }
 +        res = xdr_vector(xd, (char *)vf, nf,
 +                         (unsigned int)sizeof(float), (xdrproc_t)xdr_float);
 +        if (res == 0)
 +        {
 +            return -1;
 +        }
 +        if (dtc != xdr_datatype_float)
 +        {
 +            for (i = 0; i < nf; i++)
 +            {
 +                vp[i] = vf[i];
 +            }
 +            sfree(vf);
 +        }
 +    }
 +    else
 +    {
 +        if (dtc == xdr_datatype_double)
 +        {
 +            vd = (double *)vp;
 +        }
 +        else
 +        {
 +            snew(vd, nf);
 +        }
 +        res = xdr_vector(xd, (char *)vd, nf,
 +                         (unsigned int)sizeof(double), (xdrproc_t)xdr_double);
 +        if (res == 0)
 +        {
 +            return -1;
 +        }
 +        if (dtc != xdr_datatype_double)
 +        {
 +            for (i = 0; i < nf; i++)
 +            {
 +                vp[i] = vd[i];
 +            }
 +            sfree(vd);
 +        }
 +    }
 +
 +    if (list)
 +    {
 +        switch (erealtype)
 +        {
 +            case ecprREAL:
 +                pr_reals(list, 0, st_names(cptp, ecpt), vp, nf);
 +                break;
 +            case ecprRVEC:
 +                pr_rvecs(list, 0, st_names(cptp, ecpt), (rvec *)vp, nf/3);
 +                break;
 +            default:
 +                gmx_incons("Unknown checkpoint real type");
 +        }
 +    }
 +    if (va)
 +    {
 +        sfree(va);
 +    }
 +
 +    return 0;
 +}
 +
 +
 +/* This function stores n along with the reals for reading,
 + * but on reading it assumes that n matches the value in the checkpoint file,
 + * a fatal error is generated when this is not the case.
 + */
 +static int do_cpte_reals(XDR *xd, int cptp, int ecpt, int sflags,
 +                         int n, real **v, FILE *list)
 +{
 +    return do_cpte_reals_low(xd, cptp, ecpt, sflags, n, NULL, v, list, ecprREAL);
 +}
 +
 +/* This function does the same as do_cpte_reals,
 + * except that on reading it ignores the passed value of *n
 + * and stored the value read from the checkpoint file in *n.
 + */
 +static int do_cpte_n_reals(XDR *xd, int cptp, int ecpt, int sflags,
 +                           int *n, real **v, FILE *list)
 +{
 +    return do_cpte_reals_low(xd, cptp, ecpt, sflags, -1, n, v, list, ecprREAL);
 +}
 +
 +static int do_cpte_real(XDR *xd, int cptp, int ecpt, int sflags,
 +                        real *r, FILE *list)
 +{
 +    int n;
 +
 +    return do_cpte_reals_low(xd, cptp, ecpt, sflags, 1, NULL, &r, list, ecprREAL);
 +}
 +
 +static int do_cpte_ints(XDR *xd, int cptp, int ecpt, int sflags,
 +                        int n, int **v, FILE *list)
 +{
 +    bool_t res = 0;
 +    int    dtc = xdr_datatype_int;
 +    int   *vp, *va = NULL;
 +    int    nf, dt, i;
 +
 +    nf  = n;
 +    res = xdr_int(xd, &nf);
 +    if (res == 0)
 +    {
 +        return -1;
 +    }
 +    if (list == NULL && v != NULL && nf != n)
 +    {
 +        gmx_fatal(FARGS, "Count mismatch for state entry %s, code count is %d, file count is %d\n", st_names(cptp, ecpt), n, nf);
 +    }
 +    dt  = dtc;
 +    res = xdr_int(xd, &dt);
 +    if (res == 0)
 +    {
 +        return -1;
 +    }
 +    if (dt != dtc)
 +    {
 +        gmx_fatal(FARGS, "Type mismatch for state entry %s, code type is %s, file type is %s\n",
 +                  st_names(cptp, ecpt), xdr_datatype_names[dtc],
 +                  xdr_datatype_names[dt]);
 +    }
 +    if (list || !(sflags & (1<<ecpt)) || v == NULL)
 +    {
 +        snew(va, nf);
 +        vp = va;
 +    }
 +    else
 +    {
 +        if (*v == NULL)
 +        {
 +            snew(*v, nf);
 +        }
 +        vp = *v;
 +    }
 +    res = xdr_vector(xd, (char *)vp, nf,
 +                     (unsigned int)sizeof(int), (xdrproc_t)xdr_int);
 +    if (res == 0)
 +    {
 +        return -1;
 +    }
 +    if (list)
 +    {
 +        pr_ivec(list, 0, st_names(cptp, ecpt), vp, nf, TRUE);
 +    }
 +    if (va)
 +    {
 +        sfree(va);
 +    }
 +
 +    return 0;
 +}
 +
 +static int do_cpte_int(XDR *xd, int cptp, int ecpt, int sflags,
 +                       int *i, FILE *list)
 +{
 +    return do_cpte_ints(xd, cptp, ecpt, sflags, 1, &i, list);
 +}
 +
 +static int do_cpte_doubles(XDR *xd, int cptp, int ecpt, int sflags,
 +                           int n, double **v, FILE *list)
 +{
 +    bool_t  res = 0;
 +    int     dtc = xdr_datatype_double;
 +    double *vp, *va = NULL;
 +    int     nf, dt, i;
 +
 +    nf  = n;
 +    res = xdr_int(xd, &nf);
 +    if (res == 0)
 +    {
 +        return -1;
 +    }
 +    if (list == NULL && nf != n)
 +    {
 +        gmx_fatal(FARGS, "Count mismatch for state entry %s, code count is %d, file count is %d\n", st_names(cptp, ecpt), n, nf);
 +    }
 +    dt  = dtc;
 +    res = xdr_int(xd, &dt);
 +    if (res == 0)
 +    {
 +        return -1;
 +    }
 +    if (dt != dtc)
 +    {
 +        gmx_fatal(FARGS, "Precision mismatch for state entry %s, code precision is %s, file precision is %s\n",
 +                  st_names(cptp, ecpt), xdr_datatype_names[dtc],
 +                  xdr_datatype_names[dt]);
 +    }
 +    if (list || !(sflags & (1<<ecpt)))
 +    {
 +        snew(va, nf);
 +        vp = va;
 +    }
 +    else
 +    {
 +        if (*v == NULL)
 +        {
 +            snew(*v, nf);
 +        }
 +        vp = *v;
 +    }
 +    res = xdr_vector(xd, (char *)vp, nf,
 +                     (unsigned int)sizeof(double), (xdrproc_t)xdr_double);
 +    if (res == 0)
 +    {
 +        return -1;
 +    }
 +    if (list)
 +    {
 +        pr_doubles(list, 0, st_names(cptp, ecpt), vp, nf);
 +    }
 +    if (va)
 +    {
 +        sfree(va);
 +    }
 +
 +    return 0;
 +}
 +
 +static int do_cpte_double(XDR *xd, int cptp, int ecpt, int sflags,
 +                          double *r, FILE *list)
 +{
 +    return do_cpte_doubles(xd, cptp, ecpt, sflags, 1, &r, list);
 +}
 +
 +
 +static int do_cpte_rvecs(XDR *xd, int cptp, int ecpt, int sflags,
 +                         int n, rvec **v, FILE *list)
 +{
 +    int n3;
 +
 +    return do_cpte_reals_low(xd, cptp, ecpt, sflags,
 +                             n*DIM, NULL, (real **)v, list, ecprRVEC);
 +}
 +
 +static int do_cpte_matrix(XDR *xd, int cptp, int ecpt, int sflags,
 +                          matrix v, FILE *list)
 +{
 +    real *vr;
 +    real  ret;
 +
 +    vr  = (real *)&(v[0][0]);
 +    ret = do_cpte_reals_low(xd, cptp, ecpt, sflags,
 +                            DIM*DIM, NULL, &vr, NULL, ecprMATRIX);
 +
 +    if (list && ret == 0)
 +    {
 +        pr_rvecs(list, 0, st_names(cptp, ecpt), v, DIM);
 +    }
 +
 +    return ret;
 +}
 +
 +
 +static int do_cpte_nmatrix(XDR *xd, int cptp, int ecpt, int sflags,
 +                           int n, real **v, FILE *list)
 +{
 +    int   i;
 +    real *vr;
 +    real  ret, reti;
 +    char  name[CPTSTRLEN];
 +
 +    ret = 0;
 +    if (v == NULL)
 +    {
 +        snew(v, n);
 +    }
 +    for (i = 0; i < n; i++)
 +    {
 +        reti = 0;
 +        vr   = v[i];
 +        reti = do_cpte_reals_low(xd, cptp, ecpt, sflags, n, NULL, &(v[i]), NULL, ecprREAL);
 +        if (list && reti == 0)
 +        {
 +            sprintf(name, "%s[%d]", st_names(cptp, ecpt), i);
 +            pr_reals(list, 0, name, v[i], n);
 +        }
 +        if (reti == 0)
 +        {
 +            ret = 0;
 +        }
 +    }
 +    return ret;
 +}
 +
 +static int do_cpte_matrices(XDR *xd, int cptp, int ecpt, int sflags,
 +                            int n, matrix **v, FILE *list)
 +{
 +    bool_t  res = 0;
 +    matrix *vp, *va = NULL;
 +    real   *vr;
 +    int     nf, i, j, k;
 +    int     ret;
 +
 +    nf  = n;
 +    res = xdr_int(xd, &nf);
 +    if (res == 0)
 +    {
 +        return -1;
 +    }
 +    if (list == NULL && nf != n)
 +    {
 +        gmx_fatal(FARGS, "Count mismatch for state entry %s, code count is %d, file count is %d\n", st_names(cptp, ecpt), n, nf);
 +    }
 +    if (list || !(sflags & (1<<ecpt)))
 +    {
 +        snew(va, nf);
 +        vp = va;
 +    }
 +    else
 +    {
 +        if (*v == NULL)
 +        {
 +            snew(*v, nf);
 +        }
 +        vp = *v;
 +    }
 +    snew(vr, nf*DIM*DIM);
 +    for (i = 0; i < nf; i++)
 +    {
 +        for (j = 0; j < DIM; j++)
 +        {
 +            for (k = 0; k < DIM; k++)
 +            {
 +                vr[(i*DIM+j)*DIM+k] = vp[i][j][k];
 +            }
 +        }
 +    }
 +    ret = do_cpte_reals_low(xd, cptp, ecpt, sflags,
 +                            nf*DIM*DIM, NULL, &vr, NULL, ecprMATRIX);
 +    for (i = 0; i < nf; i++)
 +    {
 +        for (j = 0; j < DIM; j++)
 +        {
 +            for (k = 0; k < DIM; k++)
 +            {
 +                vp[i][j][k] = vr[(i*DIM+j)*DIM+k];
 +            }
 +        }
 +    }
 +    sfree(vr);
 +
 +    if (list && ret == 0)
 +    {
 +        for (i = 0; i < nf; i++)
 +        {
 +            pr_rvecs(list, 0, st_names(cptp, ecpt), vp[i], DIM);
 +        }
 +    }
 +    if (va)
 +    {
 +        sfree(va);
 +    }
 +
 +    return ret;
 +}
 +
 +static void do_cpt_header(XDR *xd, gmx_bool bRead, int *file_version,
 +                          char **version, char **btime, char **buser, char **bhost,
 +                          int *double_prec,
 +                          char **fprog, char **ftime,
 +                          int *eIntegrator, int *simulation_part,
 +                          gmx_int64_t *step, double *t,
 +                          int *nnodes, int *dd_nc, int *npme,
 +                          int *natoms, int *ngtc, int *nnhpres, int *nhchainlength,
 +                          int *nlambda, int *flags_state,
 +                          int *flags_eks, int *flags_enh, int *flags_dfh,
 +                          int *nED, int *eSwapCoords,
 +                          FILE *list)
 +{
 +    bool_t res = 0;
 +    int    magic;
 +    int    idum = 0;
 +    int    i;
 +    char  *fhost;
 +
 +    if (bRead)
 +    {
 +        magic = -1;
 +    }
 +    else
 +    {
 +        magic = CPT_MAGIC1;
 +    }
 +    res = xdr_int(xd, &magic);
 +    if (res == 0)
 +    {
 +        gmx_fatal(FARGS, "The checkpoint file is empty/corrupted, or maybe you are out of disk space?");
 +    }
 +    if (magic != CPT_MAGIC1)
 +    {
 +        gmx_fatal(FARGS, "Start of file magic number mismatch, checkpoint file has %d, should be %d\n"
 +                  "The checkpoint file is corrupted or not a checkpoint file",
 +                  magic, CPT_MAGIC1);
 +    }
 +    if (!bRead)
 +    {
 +        snew(fhost, 255);
 +        gmx_gethostname(fhost, 255);
 +    }
 +    do_cpt_string_err(xd, bRead, "GROMACS version", version, list);
 +    do_cpt_string_err(xd, bRead, "GROMACS build time", btime, list);
 +    do_cpt_string_err(xd, bRead, "GROMACS build user", buser, list);
 +    do_cpt_string_err(xd, bRead, "GROMACS build host", bhost, list);
 +    do_cpt_string_err(xd, bRead, "generating program", fprog, list);
 +    do_cpt_string_err(xd, bRead, "generation time", ftime, list);
 +    *file_version = cpt_version;
 +    do_cpt_int_err(xd, "checkpoint file version", file_version, list);
 +    if (*file_version > cpt_version)
 +    {
 +        gmx_fatal(FARGS, "Attempting to read a checkpoint file of version %d with code of version %d\n", *file_version, cpt_version);
 +    }
 +    if (*file_version >= 13)
 +    {
 +        do_cpt_int_err(xd, "GROMACS double precision", double_prec, list);
 +    }
 +    else
 +    {
 +        *double_prec = -1;
 +    }
 +    if (*file_version >= 12)
 +    {
 +        do_cpt_string_err(xd, bRead, "generating host", &fhost, list);
 +        if (list == NULL)
 +        {
 +            sfree(fhost);
 +        }
 +    }
 +    do_cpt_int_err(xd, "#atoms", natoms, list);
 +    do_cpt_int_err(xd, "#T-coupling groups", ngtc, list);
 +    if (*file_version >= 10)
 +    {
 +        do_cpt_int_err(xd, "#Nose-Hoover T-chains", nhchainlength, list);
 +    }
 +    else
 +    {
 +        *nhchainlength = 1;
 +    }
 +    if (*file_version >= 11)
 +    {
 +        do_cpt_int_err(xd, "#Nose-Hoover T-chains for barostat ", nnhpres, list);
 +    }
 +    else
 +    {
 +        *nnhpres = 0;
 +    }
 +    if (*file_version >= 14)
 +    {
 +        do_cpt_int_err(xd, "# of total lambda states ", nlambda, list);
 +    }
 +    else
 +    {
 +        *nlambda = 0;
 +    }
 +    do_cpt_int_err(xd, "integrator", eIntegrator, list);
 +    if (*file_version >= 3)
 +    {
 +        do_cpt_int_err(xd, "simulation part #", simulation_part, list);
 +    }
 +    else
 +    {
 +        *simulation_part = 1;
 +    }
 +    if (*file_version >= 5)
 +    {
 +        do_cpt_step_err(xd, "step", step, list);
 +    }
 +    else
 +    {
 +        do_cpt_int_err(xd, "step", &idum, list);
 +        *step = idum;
 +    }
 +    do_cpt_double_err(xd, "t", t, list);
 +    do_cpt_int_err(xd, "#PP-nodes", nnodes, list);
 +    idum = 1;
 +    do_cpt_int_err(xd, "dd_nc[x]", dd_nc ? &(dd_nc[0]) : &idum, list);
 +    do_cpt_int_err(xd, "dd_nc[y]", dd_nc ? &(dd_nc[1]) : &idum, list);
 +    do_cpt_int_err(xd, "dd_nc[z]", dd_nc ? &(dd_nc[2]) : &idum, list);
 +    do_cpt_int_err(xd, "#PME-only nodes", npme, list);
 +    do_cpt_int_err(xd, "state flags", flags_state, list);
 +    if (*file_version >= 4)
 +    {
 +        do_cpt_int_err(xd, "ekin data flags", flags_eks, list);
 +        do_cpt_int_err(xd, "energy history flags", flags_enh, list);
 +    }
 +    else
 +    {
 +        *flags_eks   = 0;
 +        *flags_enh   = (*flags_state >> (estORIRE_DTAV+1));
 +        *flags_state = (*flags_state & ~((1<<(estORIRE_DTAV+1)) |
 +                                         (1<<(estORIRE_DTAV+2)) |
 +                                         (1<<(estORIRE_DTAV+3))));
 +    }
 +    if (*file_version >= 14)
 +    {
 +        do_cpt_int_err(xd, "df history flags", flags_dfh, list);
 +    }
 +    else
 +    {
 +        *flags_dfh = 0;
 +    }
 +
 +    if (*file_version >= 15)
 +    {
 +        do_cpt_int_err(xd, "ED data sets", nED, list);
 +    }
 +    else
 +    {
 +        *nED = 0;
 +    }
 +    if (*file_version >= 16)
 +    {
 +        do_cpt_int_err(xd, "swap", eSwapCoords, list);
 +    }
 +}
 +
 +static int do_cpt_footer(XDR *xd, int file_version)
 +{
 +    bool_t res = 0;
 +    int    magic;
 +
 +    if (file_version >= 2)
 +    {
 +        magic = CPT_MAGIC2;
 +        res   = xdr_int(xd, &magic);
 +        if (res == 0)
 +        {
 +            cp_error();
 +        }
 +        if (magic != CPT_MAGIC2)
 +        {
 +            return -1;
 +        }
 +    }
 +
 +    return 0;
 +}
 +
 +static int do_cpt_state(XDR *xd, gmx_bool bRead,
 +                        int fflags, t_state *state,
 +                        FILE *list)
 +{
 +    int    sflags;
 +    int    i;
 +    int    ret;
 +    int    nnht, nnhtp;
 +
 +    ret = 0;
 +
 +    nnht  = state->nhchainlength*state->ngtc;
 +    nnhtp = state->nhchainlength*state->nnhpres;
 +
 +    if (bRead) /* we need to allocate space for dfhist if we are reading */
 +    {
 +        init_df_history(&state->dfhist, state->dfhist.nlambda);
 +    }
 +
 +    sflags = state->flags;
 +    for (i = 0; (i < estNR && ret == 0); i++)
 +    {
 +        if (fflags & (1<<i))
 +        {
 +            switch (i)
 +            {
 +                case estLAMBDA:  ret      = do_cpte_reals(xd, cptpEST, i, sflags, efptNR, &(state->lambda), list); break;
 +                case estFEPSTATE: ret     = do_cpte_int (xd, cptpEST, i, sflags, &state->fep_state, list); break;
 +                case estBOX:     ret      = do_cpte_matrix(xd, cptpEST, i, sflags, state->box, list); break;
 +                case estBOX_REL: ret      = do_cpte_matrix(xd, cptpEST, i, sflags, state->box_rel, list); break;
 +                case estBOXV:    ret      = do_cpte_matrix(xd, cptpEST, i, sflags, state->boxv, list); break;
 +                case estPRES_PREV: ret    = do_cpte_matrix(xd, cptpEST, i, sflags, state->pres_prev, list); break;
 +                case estSVIR_PREV:  ret   = do_cpte_matrix(xd, cptpEST, i, sflags, state->svir_prev, list); break;
 +                case estFVIR_PREV:  ret   = do_cpte_matrix(xd, cptpEST, i, sflags, state->fvir_prev, list); break;
 +                case estNH_XI:   ret      = do_cpte_doubles(xd, cptpEST, i, sflags, nnht, &state->nosehoover_xi, list); break;
 +                case estNH_VXI:  ret      = do_cpte_doubles(xd, cptpEST, i, sflags, nnht, &state->nosehoover_vxi, list); break;
 +                case estNHPRES_XI:   ret  = do_cpte_doubles(xd, cptpEST, i, sflags, nnhtp, &state->nhpres_xi, list); break;
 +                case estNHPRES_VXI:  ret  = do_cpte_doubles(xd, cptpEST, i, sflags, nnhtp, &state->nhpres_vxi, list); break;
 +                case estTC_INT:  ret      = do_cpte_doubles(xd, cptpEST, i, sflags, state->ngtc, &state->therm_integral, list); break;
 +                case estVETA:    ret      = do_cpte_real(xd, cptpEST, i, sflags, &state->veta, list); break;
 +                case estVOL0:    ret      = do_cpte_real(xd, cptpEST, i, sflags, &state->vol0, list); break;
 +                case estX:       ret      = do_cpte_rvecs(xd, cptpEST, i, sflags, state->natoms, &state->x, list); break;
 +                case estV:       ret      = do_cpte_rvecs(xd, cptpEST, i, sflags, state->natoms, &state->v, list); break;
 +                case estSDX:     ret      = do_cpte_rvecs(xd, cptpEST, i, sflags, state->natoms, &state->sd_X, list); break;
 +                /* The RNG entries are no longer written,
 +                 * the next 4 lines are only for reading old files.
 +                 */
 +                case estLD_RNG:  ret      = do_cpte_ints(xd, cptpEST, i, sflags, 0, NULL, list); break;
 +                case estLD_RNGI: ret      = do_cpte_ints(xd, cptpEST, i, sflags, 0, NULL, list); break;
 +                case estMC_RNG:  ret      = do_cpte_ints(xd, cptpEST, i, sflags, 0, NULL, list); break;
 +                case estMC_RNGI: ret      = do_cpte_ints(xd, cptpEST, i, sflags, 0, NULL, list); break;
 +                case estDISRE_INITF:  ret = do_cpte_real (xd, cptpEST, i, sflags, &state->hist.disre_initf, list); break;
 +                case estDISRE_RM3TAV: ret = do_cpte_n_reals(xd, cptpEST, i, sflags, &state->hist.ndisrepairs, &state->hist.disre_rm3tav, list); break;
 +                case estORIRE_INITF:  ret = do_cpte_real (xd, cptpEST, i, sflags, &state->hist.orire_initf, list); break;
 +                case estORIRE_DTAV:   ret = do_cpte_n_reals(xd, cptpEST, i, sflags, &state->hist.norire_Dtav, &state->hist.orire_Dtav, list); break;
 +                default:
 +                    gmx_fatal(FARGS, "Unknown state entry %d\n"
 +                              "You are probably reading a new checkpoint file with old code", i);
 +            }
 +        }
 +    }
 +
 +    return ret;
 +}
 +
 +static int do_cpt_ekinstate(XDR *xd, int fflags, ekinstate_t *ekins,
 +                            FILE *list)
 +{
 +    int  i;
 +    int  ret;
 +
 +    ret = 0;
 +
 +    for (i = 0; (i < eeksNR && ret == 0); i++)
 +    {
 +        if (fflags & (1<<i))
 +        {
 +            switch (i)
 +            {
 +
 +                case eeksEKIN_N:     ret = do_cpte_int(xd, cptpEEKS, i, fflags, &ekins->ekin_n, list); break;
 +                case eeksEKINH:     ret  = do_cpte_matrices(xd, cptpEEKS, i, fflags, ekins->ekin_n, &ekins->ekinh, list); break;
 +                case eeksEKINF:      ret = do_cpte_matrices(xd, cptpEEKS, i, fflags, ekins->ekin_n, &ekins->ekinf, list); break;
 +                case eeksEKINO:      ret = do_cpte_matrices(xd, cptpEEKS, i, fflags, ekins->ekin_n, &ekins->ekinh_old, list); break;
 +                case eeksEKINTOTAL:  ret = do_cpte_matrix(xd, cptpEEKS, i, fflags, ekins->ekin_total, list); break;
 +                case eeksEKINSCALEF: ret = do_cpte_doubles(xd, cptpEEKS, i, fflags, ekins->ekin_n, &ekins->ekinscalef_nhc, list); break;
 +                case eeksVSCALE:     ret = do_cpte_doubles(xd, 1, cptpEEKS, fflags, ekins->ekin_n, &ekins->vscale_nhc, list); break;
 +                case eeksEKINSCALEH: ret = do_cpte_doubles(xd, 1, cptpEEKS, fflags, ekins->ekin_n, &ekins->ekinscaleh_nhc, list); break;
 +                case eeksDEKINDL:   ret  = do_cpte_real(xd, 1, cptpEEKS, fflags, &ekins->dekindl, list); break;
 +                case eeksMVCOS:      ret = do_cpte_real(xd, 1, cptpEEKS, fflags, &ekins->mvcos, list); break;
 +                default:
 +                    gmx_fatal(FARGS, "Unknown ekin data state entry %d\n"
 +                              "You are probably reading a new checkpoint file with old code", i);
 +            }
 +        }
 +    }
 +
 +    return ret;
 +}
 +
 +
 +static int do_cpt_swapstate(XDR *xd, gmx_bool bRead, swapstate_t *swapstate, FILE *list)
 +{
 +    int ii, ic, j;
 +    int ret              = 0;
 +    int swap_cpt_version = 1;
 +
 +
 +    if (eswapNO == swapstate->eSwapCoords)
 +    {
 +        return ret;
 +    }
 +
 +    swapstate->bFromCpt = bRead;
 +
 +    do_cpt_int_err(xd, "swap checkpoint version", &swap_cpt_version, list);
 +    do_cpt_int_err(xd, "swap coupling steps", &swapstate->nAverage, list);
 +
 +    /* When reading, init_swapcoords has not been called yet,
 +     * so we have to allocate memory first. */
 +
 +    for (ic = 0; ic < eCompNR; ic++)
 +    {
 +        for (ii = 0; ii < eIonNR; ii++)
 +        {
 +            if (bRead)
 +            {
 +                do_cpt_int_err(xd, "swap requested atoms", &swapstate->nat_req[ic][ii], list);
 +            }
 +            else
 +            {
 +                do_cpt_int_err(xd, "swap requested atoms p", swapstate->nat_req_p[ic][ii], list);
 +            }
 +
 +            if (bRead)
 +            {
 +                do_cpt_int_err(xd, "swap influx netto", &swapstate->inflow_netto[ic][ii], list);
 +            }
 +            else
 +            {
 +                do_cpt_int_err(xd, "swap influx netto p", swapstate->inflow_netto_p[ic][ii], list);
 +            }
 +
 +            if (bRead && (NULL == swapstate->nat_past[ic][ii]) )
 +            {
 +                snew(swapstate->nat_past[ic][ii], swapstate->nAverage);
 +            }
 +
 +            for (j = 0; j < swapstate->nAverage; j++)
 +            {
 +                if (bRead)
 +                {
 +                    do_cpt_int_err(xd, "swap past atom counts", &swapstate->nat_past[ic][ii][j], list);
 +                }
 +                else
 +                {
 +                    do_cpt_int_err(xd, "swap past atom counts p", &swapstate->nat_past_p[ic][ii][j], list);
 +                }
 +            }
 +        }
 +    }
 +
 +    /* Ion flux per channel */
 +    for (ic = 0; ic < eChanNR; ic++)
 +    {
 +        for (ii = 0; ii < eIonNR; ii++)
 +        {
 +            if (bRead)
 +            {
 +                do_cpt_int_err(xd, "channel flux", &swapstate->fluxfromAtoB[ic][ii], list);
 +            }
 +            else
 +            {
 +                do_cpt_int_err(xd, "channel flux p", swapstate->fluxfromAtoB_p[ic][ii], list);
 +            }
 +        }
 +    }
 +
 +    /* Ion flux leakage */
 +    if (bRead)
 +    {
 +        snew(swapstate->fluxleak, 1);
 +    }
 +    do_cpt_int_err(xd, "flux leakage", swapstate->fluxleak, list);
 +
 +    /* Ion history */
 +    do_cpt_int_err(xd, "number of ions", &swapstate->nions, list);
 +
 +    if (bRead)
 +    {
 +        snew(swapstate->channel_label, swapstate->nions);
 +        snew(swapstate->comp_from, swapstate->nions);
 +    }
 +
 +    do_cpt_u_chars(xd, "channel history", swapstate->nions, swapstate->channel_label, list);
 +    do_cpt_u_chars(xd, "domain history", swapstate->nions, swapstate->comp_from, list);
 +
 +    /* Save the last known whole positions to checkpoint
 +     * file to be able to also make multimeric channels whole in PBC */
 +    do_cpt_int_err(xd, "Ch0 atoms", &swapstate->nat[eChan0], list);
 +    do_cpt_int_err(xd, "Ch1 atoms", &swapstate->nat[eChan1], list);
 +    if (bRead)
 +    {
 +        snew(swapstate->xc_old_whole[eChan0], swapstate->nat[eChan0]);
 +        snew(swapstate->xc_old_whole[eChan1], swapstate->nat[eChan1]);
 +        do_cpt_n_rvecs_err(xd, "Ch0 whole x", swapstate->nat[eChan0], swapstate->xc_old_whole[eChan0], list);
 +        do_cpt_n_rvecs_err(xd, "Ch1 whole x", swapstate->nat[eChan1], swapstate->xc_old_whole[eChan1], list);
 +    }
 +    else
 +    {
 +        do_cpt_n_rvecs_err(xd, "Ch0 whole x", swapstate->nat[eChan0], *swapstate->xc_old_whole_p[eChan0], list);
 +        do_cpt_n_rvecs_err(xd, "Ch1 whole x", swapstate->nat[eChan1], *swapstate->xc_old_whole_p[eChan1], list);
 +    }
 +
 +    return ret;
 +}
 +
 +
 +static int do_cpt_enerhist(XDR *xd, gmx_bool bRead,
 +                           int fflags, energyhistory_t *enerhist,
 +                           FILE *list)
 +{
 +    int  i;
 +    int  j;
 +    int  ret;
 +
 +    ret = 0;
 +
 +    if (bRead)
 +    {
 +        enerhist->nsteps     = 0;
 +        enerhist->nsum       = 0;
 +        enerhist->nsteps_sim = 0;
 +        enerhist->nsum_sim   = 0;
 +        enerhist->dht        = NULL;
 +
 +        if (fflags & (1<< eenhENERGY_DELTA_H_NN) )
 +        {
 +            snew(enerhist->dht, 1);
 +            enerhist->dht->ndh              = NULL;
 +            enerhist->dht->dh               = NULL;
 +            enerhist->dht->start_lambda_set = FALSE;
 +        }
 +    }
 +
 +    for (i = 0; (i < eenhNR && ret == 0); i++)
 +    {
 +        if (fflags & (1<<i))
 +        {
 +            switch (i)
 +            {
 +                case eenhENERGY_N:     ret = do_cpte_int(xd, cptpEENH, i, fflags, &enerhist->nener, list); break;
 +                case eenhENERGY_AVER:  ret = do_cpte_doubles(xd, cptpEENH, i, fflags, enerhist->nener, &enerhist->ener_ave, list); break;
 +                case eenhENERGY_SUM:   ret = do_cpte_doubles(xd, cptpEENH, i, fflags, enerhist->nener, &enerhist->ener_sum, list); break;
 +                case eenhENERGY_NSUM:  do_cpt_step_err(xd, eenh_names[i], &enerhist->nsum, list); break;
 +                case eenhENERGY_SUM_SIM: ret = do_cpte_doubles(xd, cptpEENH, i, fflags, enerhist->nener, &enerhist->ener_sum_sim, list); break;
 +                case eenhENERGY_NSUM_SIM:   do_cpt_step_err(xd, eenh_names[i], &enerhist->nsum_sim, list); break;
 +                case eenhENERGY_NSTEPS:     do_cpt_step_err(xd, eenh_names[i], &enerhist->nsteps, list); break;
 +                case eenhENERGY_NSTEPS_SIM: do_cpt_step_err(xd, eenh_names[i], &enerhist->nsteps_sim, list); break;
 +                case eenhENERGY_DELTA_H_NN: do_cpt_int_err(xd, eenh_names[i], &(enerhist->dht->nndh), list);
 +                    if (bRead) /* now allocate memory for it */
 +                    {
 +                        snew(enerhist->dht->dh, enerhist->dht->nndh);
 +                        snew(enerhist->dht->ndh, enerhist->dht->nndh);
 +                        for (j = 0; j < enerhist->dht->nndh; j++)
 +                        {
 +                            enerhist->dht->ndh[j] = 0;
 +                            enerhist->dht->dh[j]  = NULL;
 +                        }
 +                    }
 +                    break;
 +                case eenhENERGY_DELTA_H_LIST:
 +                    for (j = 0; j < enerhist->dht->nndh; j++)
 +                    {
 +                        ret = do_cpte_n_reals(xd, cptpEENH, i, fflags, &enerhist->dht->ndh[j], &(enerhist->dht->dh[j]), list);
 +                    }
 +                    break;
 +                case eenhENERGY_DELTA_H_STARTTIME:
 +                    ret = do_cpte_double(xd, cptpEENH, i, fflags, &(enerhist->dht->start_time), list); break;
 +                case eenhENERGY_DELTA_H_STARTLAMBDA:
 +                    ret = do_cpte_double(xd, cptpEENH, i, fflags, &(enerhist->dht->start_lambda), list); break;
 +                default:
 +                    gmx_fatal(FARGS, "Unknown energy history entry %d\n"
 +                              "You are probably reading a new checkpoint file with old code", i);
 +            }
 +        }
 +    }
 +
 +    if ((fflags & (1<<eenhENERGY_SUM)) && !(fflags & (1<<eenhENERGY_SUM_SIM)))
 +    {
 +        /* Assume we have an old file format and copy sum to sum_sim */
 +        srenew(enerhist->ener_sum_sim, enerhist->nener);
 +        for (i = 0; i < enerhist->nener; i++)
 +        {
 +            enerhist->ener_sum_sim[i] = enerhist->ener_sum[i];
 +        }
 +        fflags |= (1<<eenhENERGY_SUM_SIM);
 +    }
 +
 +    if ( (fflags & (1<<eenhENERGY_NSUM)) &&
 +         !(fflags & (1<<eenhENERGY_NSTEPS)))
 +    {
 +        /* Assume we have an old file format and copy nsum to nsteps */
 +        enerhist->nsteps = enerhist->nsum;
 +        fflags          |= (1<<eenhENERGY_NSTEPS);
 +    }
 +    if ( (fflags & (1<<eenhENERGY_NSUM_SIM)) &&
 +         !(fflags & (1<<eenhENERGY_NSTEPS_SIM)))
 +    {
 +        /* Assume we have an old file format and copy nsum to nsteps */
 +        enerhist->nsteps_sim = enerhist->nsum_sim;
 +        fflags              |= (1<<eenhENERGY_NSTEPS_SIM);
 +    }
 +
 +    return ret;
 +}
 +
 +static int do_cpt_df_hist(XDR *xd, int fflags, df_history_t *dfhist, FILE *list)
 +{
 +    int  i, nlambda;
 +    int  ret;
 +
 +    nlambda = dfhist->nlambda;
 +    ret     = 0;
 +
 +    for (i = 0; (i < edfhNR && ret == 0); i++)
 +    {
 +        if (fflags & (1<<i))
 +        {
 +            switch (i)
 +            {
 +                case edfhBEQUIL:       ret = do_cpte_int(xd, cptpEDFH, i, fflags, &dfhist->bEquil, list); break;
 +                case edfhNATLAMBDA:    ret = do_cpte_ints(xd, cptpEDFH, i, fflags, nlambda, &dfhist->n_at_lam, list); break;
 +                case edfhWLHISTO:      ret = do_cpte_reals(xd, cptpEDFH, i, fflags, nlambda, &dfhist->wl_histo, list); break;
 +                case edfhWLDELTA:      ret = do_cpte_real(xd, cptpEDFH, i, fflags, &dfhist->wl_delta, list); break;
 +                case edfhSUMWEIGHTS:   ret = do_cpte_reals(xd, cptpEDFH, i, fflags, nlambda, &dfhist->sum_weights, list); break;
 +                case edfhSUMDG:        ret = do_cpte_reals(xd, cptpEDFH, i, fflags, nlambda, &dfhist->sum_dg, list); break;
 +                case edfhSUMMINVAR:    ret = do_cpte_reals(xd, cptpEDFH, i, fflags, nlambda, &dfhist->sum_minvar, list); break;
 +                case edfhSUMVAR:       ret = do_cpte_reals(xd, cptpEDFH, i, fflags, nlambda, &dfhist->sum_variance, list); break;
 +                case edfhACCUMP:       ret = do_cpte_nmatrix(xd, cptpEDFH, i, fflags, nlambda, dfhist->accum_p, list); break;
 +                case edfhACCUMM:       ret = do_cpte_nmatrix(xd, cptpEDFH, i, fflags, nlambda, dfhist->accum_m, list); break;
 +                case edfhACCUMP2:      ret = do_cpte_nmatrix(xd, cptpEDFH, i, fflags, nlambda, dfhist->accum_p2, list); break;
 +                case edfhACCUMM2:      ret = do_cpte_nmatrix(xd, cptpEDFH, i, fflags, nlambda, dfhist->accum_m2, list); break;
 +                case edfhTIJ:          ret = do_cpte_nmatrix(xd, cptpEDFH, i, fflags, nlambda, dfhist->Tij, list); break;
 +                case edfhTIJEMP:       ret = do_cpte_nmatrix(xd, cptpEDFH, i, fflags, nlambda, dfhist->Tij_empirical, list); break;
 +
 +                default:
 +                    gmx_fatal(FARGS, "Unknown df history entry %d\n"
 +                              "You are probably reading a new checkpoint file with old code", i);
 +            }
 +        }
 +    }
 +
 +    return ret;
 +}
 +
 +
 +/* This function stores the last whole configuration of the reference and
 + * average structure in the .cpt file
 + */
 +static int do_cpt_EDstate(XDR *xd, gmx_bool bRead,
 +                          edsamstate_t *EDstate, FILE *list)
 +{
 +    int  i, j;
 +    int  ret = 0;
 +    char buf[STRLEN];
 +
 +
 +    EDstate->bFromCpt = bRead;
 +
 +    if (EDstate->nED <= 0)
 +    {
 +        return ret;
 +    }
 +
 +    /* When reading, init_edsam has not been called yet,
 +     * so we have to allocate memory first. */
 +    if (bRead)
 +    {
 +        snew(EDstate->nref, EDstate->nED);
 +        snew(EDstate->old_sref, EDstate->nED);
 +        snew(EDstate->nav, EDstate->nED);
 +        snew(EDstate->old_sav, EDstate->nED);
 +    }
 +
 +    /* Read/write the last whole conformation of SREF and SAV for each ED dataset (usually only one) */
 +    for (i = 0; i < EDstate->nED; i++)
 +    {
 +        /* Reference structure SREF */
 +        sprintf(buf, "ED%d # of atoms in reference structure", i+1);
 +        do_cpt_int_err(xd, buf, &EDstate->nref[i], list);
 +        sprintf(buf, "ED%d x_ref", i+1);
 +        if (bRead)
 +        {
 +            snew(EDstate->old_sref[i], EDstate->nref[i]);
 +            do_cpt_n_rvecs_err(xd, buf, EDstate->nref[i], EDstate->old_sref[i], list);
 +        }
 +        else
 +        {
 +            do_cpt_n_rvecs_err(xd, buf, EDstate->nref[i], EDstate->old_sref_p[i], list);
 +        }
 +
 +        /* Average structure SAV */
 +        sprintf(buf, "ED%d # of atoms in average structure", i+1);
 +        do_cpt_int_err(xd, buf, &EDstate->nav[i], list);
 +        sprintf(buf, "ED%d x_av", i+1);
 +        if (bRead)
 +        {
 +            snew(EDstate->old_sav[i], EDstate->nav[i]);
 +            do_cpt_n_rvecs_err(xd, buf, EDstate->nav[i], EDstate->old_sav[i], list);
 +        }
 +        else
 +        {
 +            do_cpt_n_rvecs_err(xd, buf, EDstate->nav[i], EDstate->old_sav_p[i], list);
 +        }
 +    }
 +
 +    return ret;
 +}
 +
 +
 +static int do_cpt_files(XDR *xd, gmx_bool bRead,
 +                        gmx_file_position_t **p_outputfiles, int *nfiles,
 +                        FILE *list, int file_version)
 +{
 +    int                  i, j;
 +    gmx_off_t            offset;
 +    gmx_off_t            mask = 0xFFFFFFFFL;
 +    int                  offset_high, offset_low;
 +    char                *buf;
 +    gmx_file_position_t *outputfiles;
 +
 +    if (do_cpt_int(xd, "number of output files", nfiles, list) != 0)
 +    {
 +        return -1;
 +    }
 +
 +    if (bRead)
 +    {
 +        snew(*p_outputfiles, *nfiles);
 +    }
 +
 +    outputfiles = *p_outputfiles;
 +
 +    for (i = 0; i < *nfiles; i++)
 +    {
 +        /* 64-bit XDR numbers are not portable, so it is stored as separate high/low fractions */
 +        if (bRead)
 +        {
 +            do_cpt_string_err(xd, bRead, "output filename", &buf, list);
 +            strncpy(outputfiles[i].filename, buf, CPTSTRLEN-1);
 +            if (list == NULL)
 +            {
 +                sfree(buf);
 +            }
 +
 +            if (do_cpt_int(xd, "file_offset_high", &offset_high, list) != 0)
 +            {
 +                return -1;
 +            }
 +            if (do_cpt_int(xd, "file_offset_low", &offset_low, list) != 0)
 +            {
 +                return -1;
 +            }
 +            outputfiles[i].offset = ( ((gmx_off_t) offset_high) << 32 ) | ( (gmx_off_t) offset_low & mask );
 +        }
 +        else
 +        {
 +            buf = outputfiles[i].filename;
 +            do_cpt_string_err(xd, bRead, "output filename", &buf, list);
 +            /* writing */
 +            offset      = outputfiles[i].offset;
 +            if (offset == -1)
 +            {
 +                offset_low  = -1;
 +                offset_high = -1;
 +            }
 +            else
 +            {
 +                offset_low  = (int) (offset & mask);
 +                offset_high = (int) ((offset >> 32) & mask);
 +            }
 +            if (do_cpt_int(xd, "file_offset_high", &offset_high, list) != 0)
 +            {
 +                return -1;
 +            }
 +            if (do_cpt_int(xd, "file_offset_low", &offset_low, list) != 0)
 +            {
 +                return -1;
 +            }
 +        }
 +        if (file_version >= 8)
 +        {
 +            if (do_cpt_int(xd, "file_checksum_size", &(outputfiles[i].chksum_size),
 +                           list) != 0)
 +            {
 +                return -1;
 +            }
 +            if (do_cpt_u_chars(xd, "file_checksum", 16, outputfiles[i].chksum, list) != 0)
 +            {
 +                return -1;
 +            }
 +        }
 +        else
 +        {
 +            outputfiles[i].chksum_size = -1;
 +        }
 +    }
 +    return 0;
 +}
 +
 +
 +void write_checkpoint(const char *fn, gmx_bool bNumberAndKeep,
 +                      FILE *fplog, t_commrec *cr,
 +                      int eIntegrator, int simulation_part,
 +                      gmx_bool bExpanded, int elamstats,
 +                      gmx_int64_t step, double t, t_state *state)
 +{
 +    t_fileio            *fp;
 +    int                  file_version;
 +    char                *version;
 +    char                *btime;
 +    char                *buser;
 +    char                *bhost;
 +    int                  double_prec;
 +    char                *fprog;
 +    char                *fntemp; /* the temporary checkpoint file name */
 +    time_t               now;
 +    char                 timebuf[STRLEN];
 +    int                  nppnodes, npmenodes, flag_64bit;
 +    char                 buf[1024], suffix[5+STEPSTRSIZE], sbuf[STEPSTRSIZE];
 +    gmx_file_position_t *outputfiles;
 +    int                  noutputfiles;
 +    char                *ftime;
 +    int                  flags_eks, flags_enh, flags_dfh, i;
 +    t_fileio            *ret;
 +
 +    if (DOMAINDECOMP(cr))
 +    {
 +        nppnodes  = cr->dd->nnodes;
 +        npmenodes = cr->npmenodes;
 +    }
 +    else
 +    {
 +        nppnodes  = 1;
 +        npmenodes = 0;
 +    }
 +
 +#ifndef GMX_NO_RENAME
 +    /* make the new temporary filename */
 +    snew(fntemp, strlen(fn)+5+STEPSTRSIZE);
 +    strcpy(fntemp, fn);
 +    fntemp[strlen(fn) - strlen(ftp2ext(fn2ftp(fn))) - 1] = '\0';
 +    sprintf(suffix, "_%s%s", "step", gmx_step_str(step, sbuf));
 +    strcat(fntemp, suffix);
 +    strcat(fntemp, fn+strlen(fn) - strlen(ftp2ext(fn2ftp(fn))) - 1);
 +#else
 +    /* if we can't rename, we just overwrite the cpt file.
 +     * dangerous if interrupted.
 +     */
 +    snew(fntemp, strlen(fn));
 +    strcpy(fntemp, fn);
 +#endif
 +    time(&now);
 +    gmx_ctime_r(&now, timebuf, STRLEN);
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog, "Writing checkpoint, step %s at %s\n\n",
 +                gmx_step_str(step, buf), timebuf);
 +    }
 +
 +    /* Get offsets for open files */
 +    gmx_fio_get_output_file_positions(&outputfiles, &noutputfiles);
 +
 +    fp = gmx_fio_open(fntemp, "w");
 +
 +    if (state->ekinstate.bUpToDate)
 +    {
 +        flags_eks =
 +            ((1<<eeksEKIN_N) | (1<<eeksEKINH) | (1<<eeksEKINF) |
 +             (1<<eeksEKINO) | (1<<eeksEKINSCALEF) | (1<<eeksEKINSCALEH) |
 +             (1<<eeksVSCALE) | (1<<eeksDEKINDL) | (1<<eeksMVCOS));
 +    }
 +    else
 +    {
 +        flags_eks = 0;
 +    }
 +
 +    flags_enh = 0;
 +    if (state->enerhist.nsum > 0 || state->enerhist.nsum_sim > 0)
 +    {
 +        flags_enh |= (1<<eenhENERGY_N);
 +        if (state->enerhist.nsum > 0)
 +        {
 +            flags_enh |= ((1<<eenhENERGY_AVER) | (1<<eenhENERGY_SUM) |
 +                          (1<<eenhENERGY_NSTEPS) | (1<<eenhENERGY_NSUM));
 +        }
 +        if (state->enerhist.nsum_sim > 0)
 +        {
 +            flags_enh |= ((1<<eenhENERGY_SUM_SIM) | (1<<eenhENERGY_NSTEPS_SIM) |
 +                          (1<<eenhENERGY_NSUM_SIM));
 +        }
 +        if (state->enerhist.dht)
 +        {
 +            flags_enh |= ( (1<< eenhENERGY_DELTA_H_NN) |
 +                           (1<< eenhENERGY_DELTA_H_LIST) |
 +                           (1<< eenhENERGY_DELTA_H_STARTTIME) |
 +                           (1<< eenhENERGY_DELTA_H_STARTLAMBDA) );
 +        }
 +    }
 +
 +    if (bExpanded)
 +    {
 +        flags_dfh = ((1<<edfhBEQUIL) | (1<<edfhNATLAMBDA) | (1<<edfhSUMWEIGHTS) |  (1<<edfhSUMDG)  |
 +                     (1<<edfhTIJ) | (1<<edfhTIJEMP));
 +        if (EWL(elamstats))
 +        {
 +            flags_dfh |= ((1<<edfhWLDELTA) | (1<<edfhWLHISTO));
 +        }
 +        if ((elamstats == elamstatsMINVAR) || (elamstats == elamstatsBARKER) || (elamstats == elamstatsMETROPOLIS))
 +        {
 +            flags_dfh |= ((1<<edfhACCUMP) | (1<<edfhACCUMM) | (1<<edfhACCUMP2) | (1<<edfhACCUMM2)
 +                          | (1<<edfhSUMMINVAR) | (1<<edfhSUMVAR));
 +        }
 +    }
 +    else
 +    {
 +        flags_dfh = 0;
 +    }
 +
 +    /* We can check many more things now (CPU, acceleration, etc), but
 +     * it is highly unlikely to have two separate builds with exactly
 +     * the same version, user, time, and build host!
 +     */
 +
 +    version = gmx_strdup(gmx_version());
 +    btime   = gmx_strdup(BUILD_TIME);
 +    buser   = gmx_strdup(BUILD_USER);
 +    bhost   = gmx_strdup(BUILD_HOST);
 +
 +    double_prec = GMX_CPT_BUILD_DP;
 +    fprog       = gmx_strdup(Program());
 +
 +    ftime   = &(timebuf[0]);
 +
 +    do_cpt_header(gmx_fio_getxdr(fp), FALSE, &file_version,
 +                  &version, &btime, &buser, &bhost, &double_prec, &fprog, &ftime,
 +                  &eIntegrator, &simulation_part, &step, &t, &nppnodes,
 +                  DOMAINDECOMP(cr) ? cr->dd->nc : NULL, &npmenodes,
 +                  &state->natoms, &state->ngtc, &state->nnhpres,
 +                  &state->nhchainlength, &(state->dfhist.nlambda), &state->flags, &flags_eks, &flags_enh, &flags_dfh,
 +                  &state->edsamstate.nED, &state->swapstate.eSwapCoords,
 +                  NULL);
 +
 +    sfree(version);
 +    sfree(btime);
 +    sfree(buser);
 +    sfree(bhost);
 +    sfree(fprog);
 +
 +    if ((do_cpt_state(gmx_fio_getxdr(fp), FALSE, state->flags, state, NULL) < 0)        ||
 +        (do_cpt_ekinstate(gmx_fio_getxdr(fp), flags_eks, &state->ekinstate, NULL) < 0) ||
 +        (do_cpt_enerhist(gmx_fio_getxdr(fp), FALSE, flags_enh, &state->enerhist, NULL) < 0)  ||
 +        (do_cpt_df_hist(gmx_fio_getxdr(fp), flags_dfh, &state->dfhist, NULL) < 0)  ||
 +        (do_cpt_EDstate(gmx_fio_getxdr(fp), FALSE, &state->edsamstate, NULL) < 0)      ||
 +        (do_cpt_swapstate(gmx_fio_getxdr(fp), FALSE, &state->swapstate, NULL) < 0) ||
 +        (do_cpt_files(gmx_fio_getxdr(fp), FALSE, &outputfiles, &noutputfiles, NULL,
 +                      file_version) < 0))
 +    {
 +        gmx_file("Cannot read/write checkpoint; corrupt file, or maybe you are out of disk space?");
 +    }
 +
 +    do_cpt_footer(gmx_fio_getxdr(fp), file_version);
 +
 +    /* we really, REALLY, want to make sure to physically write the checkpoint,
 +       and all the files it depends on, out to disk. Because we've
 +       opened the checkpoint with gmx_fio_open(), it's in our list
 +       of open files.  */
 +    ret = gmx_fio_all_output_fsync();
 +
 +    if (ret)
 +    {
 +        char buf[STRLEN];
 +        sprintf(buf,
 +                "Cannot fsync '%s'; maybe you are out of disk space?",
 +                gmx_fio_getname(ret));
 +
 +        if (getenv(GMX_IGNORE_FSYNC_FAILURE_ENV) == NULL)
 +        {
 +            gmx_file(buf);
 +        }
 +        else
 +        {
 +            gmx_warning(buf);
 +        }
 +    }
 +
 +    if (gmx_fio_close(fp) != 0)
 +    {
 +        gmx_file("Cannot read/write checkpoint; corrupt file, or maybe you are out of disk space?");
 +    }
 +
 +    /* we don't move the checkpoint if the user specified they didn't want it,
 +       or if the fsyncs failed */
 +#ifndef GMX_NO_RENAME
 +    if (!bNumberAndKeep && !ret)
 +    {
 +        if (gmx_fexist(fn))
 +        {
 +            /* Rename the previous checkpoint file */
 +            strcpy(buf, fn);
 +            buf[strlen(fn) - strlen(ftp2ext(fn2ftp(fn))) - 1] = '\0';
 +            strcat(buf, "_prev");
 +            strcat(buf, fn+strlen(fn) - strlen(ftp2ext(fn2ftp(fn))) - 1);
 +#ifndef GMX_FAHCORE
 +            /* we copy here so that if something goes wrong between now and
 +             * the rename below, there's always a state.cpt.
 +             * If renames are atomic (such as in POSIX systems),
 +             * this copying should be unneccesary.
 +             */
 +            gmx_file_copy(fn, buf, FALSE);
 +            /* We don't really care if this fails:
 +             * there's already a new checkpoint.
 +             */
 +#else
 +            gmx_file_rename(fn, buf);
 +#endif
 +        }
 +        if (gmx_file_rename(fntemp, fn) != 0)
 +        {
 +            gmx_file("Cannot rename checkpoint file; maybe you are out of disk space?");
 +        }
 +    }
 +#endif  /* GMX_NO_RENAME */
 +
 +    sfree(outputfiles);
 +    sfree(fntemp);
 +
 +#ifdef GMX_FAHCORE
 +    /*code for alternate checkpointing scheme.  moved from top of loop over
 +       steps */
 +    fcRequestCheckPoint();
 +    if (fcCheckPointParallel( cr->nodeid, NULL, 0) == 0)
 +    {
 +        gmx_fatal( 3, __FILE__, __LINE__, "Checkpoint error on step %d\n", step );
 +    }
 +#endif /* end GMX_FAHCORE block */
 +}
 +
 +static void print_flag_mismatch(FILE *fplog, int sflags, int fflags)
 +{
 +    int i;
 +
 +    fprintf(fplog, "\nState entry mismatch between the simulation and the checkpoint file\n");
 +    fprintf(fplog, "Entries which are not present in the checkpoint file will not be updated\n");
 +    fprintf(fplog, "  %24s    %11s    %11s\n", "", "simulation", "checkpoint");
 +    for (i = 0; i < estNR; i++)
 +    {
 +        if ((sflags & (1<<i)) || (fflags & (1<<i)))
 +        {
 +            fprintf(fplog, "  %24s    %11s    %11s\n",
 +                    est_names[i],
 +                    (sflags & (1<<i)) ? "  present  " : "not present",
 +                    (fflags & (1<<i)) ? "  present  " : "not present");
 +        }
 +    }
 +}
 +
 +static void check_int(FILE *fplog, const char *type, int p, int f, gmx_bool *mm)
 +{
 +    FILE *fp = fplog ? fplog : stderr;
 +
 +    if (p != f)
 +    {
 +        fprintf(fp, "  %s mismatch,\n", type);
 +        fprintf(fp, "    current program: %d\n", p);
 +        fprintf(fp, "    checkpoint file: %d\n", f);
 +        fprintf(fp, "\n");
 +        *mm = TRUE;
 +    }
 +}
 +
 +static void check_string(FILE *fplog, const char *type, const char *p,
 +                         const char *f, gmx_bool *mm)
 +{
 +    FILE *fp = fplog ? fplog : stderr;
 +
 +    if (strcmp(p, f) != 0)
 +    {
 +        fprintf(fp, "  %s mismatch,\n", type);
 +        fprintf(fp, "    current program: %s\n", p);
 +        fprintf(fp, "    checkpoint file: %s\n", f);
 +        fprintf(fp, "\n");
 +        *mm = TRUE;
 +    }
 +}
 +
 +static void check_match(FILE *fplog,
 +                        char *version,
 +                        char *btime, char *buser, char *bhost, int double_prec,
 +                        char *fprog,
 +                        t_commrec *cr, int npp_f, int npme_f,
 +                        ivec dd_nc, ivec dd_nc_f)
 +{
 +    int      npp;
-         fprintf(stderr,
-                 "Gromacs binary or parallel settings not identical to previous run.\n"
-                 "Continuation is exact, but is not guaranteed to be binary identical%s.\n\n",
-                 fplog ? ",\n see the log file for details" : "");
++    gmx_bool mm                 = FALSE;
++    gmx_bool patchlevel_differs = FALSE;
++    gmx_bool version_differs    = FALSE;
 +
 +    check_string(fplog, "Version", gmx_version(), version, &mm);
++    patchlevel_differs = mm;
++
++    if (patchlevel_differs)
++    {
++        /* Gromacs should be able to continue from checkpoints between
++         * different patch level versions, but we do not guarantee
++         * compatibility between different major/minor versions - check this.
++         */
++        int   gmx_major, gmx_minor;
++        int   cpt_major, cpt_minor;
++        sscanf(gmx_version(), "VERSION %d.%d", &gmx_major, &gmx_minor);
++        sscanf(version, "VERSION %d.%d", &cpt_major, &cpt_minor);
++        version_differs = (gmx_major != cpt_major || gmx_minor != cpt_minor);
++    }
++
 +    check_string(fplog, "Build time", BUILD_TIME, btime, &mm);
 +    check_string(fplog, "Build user", BUILD_USER, buser, &mm);
 +    check_string(fplog, "Build host", BUILD_HOST, bhost, &mm);
 +    check_int   (fplog, "Double prec.", GMX_CPT_BUILD_DP, double_prec, &mm);
 +    check_string(fplog, "Program name", Program(), fprog, &mm);
 +
 +    check_int   (fplog, "#nodes", cr->nnodes, npp_f+npme_f, &mm);
 +    if (cr->nnodes > 1)
 +    {
 +        check_int (fplog, "#PME-nodes", cr->npmenodes, npme_f, &mm);
 +
 +        npp = cr->nnodes;
 +        if (cr->npmenodes >= 0)
 +        {
 +            npp -= cr->npmenodes;
 +        }
 +        if (npp == npp_f)
 +        {
 +            check_int (fplog, "#DD-cells[x]", dd_nc[XX], dd_nc_f[XX], &mm);
 +            check_int (fplog, "#DD-cells[y]", dd_nc[YY], dd_nc_f[YY], &mm);
 +            check_int (fplog, "#DD-cells[z]", dd_nc[ZZ], dd_nc_f[ZZ], &mm);
 +        }
 +    }
 +
 +    if (mm)
 +    {
-         if (fplog)
++        const char msg_version_difference[] =
++            "The current Gromacs major & minor version are not identical to those that\n"
++            "generated the checkpoint file. In principle Gromacs does not support\n"
++            "continuation from checkpoints between different versions, so we advise\n"
++            "against this. If you still want to try your luck we recommend that you use\n"
++            "the -noappend flag to keep your output files from the two versions separate.\n"
++            "This might also work around errors where the output fields in the energy\n"
++            "file have changed between the different major & minor versions.\n";
 +
-             fprintf(fplog,
-                     "Gromacs binary or parallel settings not identical to previous run.\n"
-                     "Continuation is exact, but is not guaranteed to be binary identical.\n\n");
++        const char msg_mismatch_notice[] =
++            "Gromacs patchlevel, binary or parallel settings differ from previous run.\n"
++            "Continuation is exact, but not guaranteed to be binary identical.\n";
++
++        const char msg_logdetails[] =
++            "See the log file for details.\n";
++
++        if (version_differs)
 +        {
-                     gmx_fatal(FARGS, "File appending requested, but only %d of the %d output files are present", nexist, nfiles);
++            fprintf(stderr, "%s%s\n", msg_version_difference, fplog ? msg_logdetails : "");
++
++            if (fplog)
++            {
++                fprintf(fplog, "%s\n", msg_version_difference);
++            }
++        }
++        else
++        {
++            /* Major & minor versions match at least, but something is different. */
++            fprintf(stderr, "%s%s\n", msg_mismatch_notice, fplog ? msg_logdetails : "");
++            if (fplog)
++            {
++                fprintf(fplog, "%s\n", msg_mismatch_notice);
++            }
 +        }
 +    }
 +}
 +
 +static void read_checkpoint(const char *fn, FILE **pfplog,
 +                            t_commrec *cr, ivec dd_nc,
 +                            int eIntegrator, int *init_fep_state, gmx_int64_t *step, double *t,
 +                            t_state *state, gmx_bool *bReadEkin,
 +                            int *simulation_part,
 +                            gmx_bool bAppendOutputFiles, gmx_bool bForceAppend)
 +{
 +    t_fileio            *fp;
 +    int                  i, j, rc;
 +    int                  file_version;
 +    char                *version, *btime, *buser, *bhost, *fprog, *ftime;
 +    int                  double_prec;
 +    char                 filename[STRLEN], buf[STEPSTRSIZE];
 +    int                  nppnodes, eIntegrator_f, nppnodes_f, npmenodes_f;
 +    ivec                 dd_nc_f;
 +    int                  natoms, ngtc, nnhpres, nhchainlength, nlambda, fflags, flags_eks, flags_enh, flags_dfh;
 +    int                  d;
 +    int                  ret;
 +    gmx_file_position_t *outputfiles;
 +    int                  nfiles;
 +    t_fileio            *chksum_file;
 +    FILE               * fplog = *pfplog;
 +    unsigned char        digest[16];
 +#if !defined __native_client__ && !defined GMX_NATIVE_WINDOWS
 +    struct flock         fl; /* don't initialize here: the struct order is OS
 +                                dependent! */
 +#endif
 +
 +    const char *int_warn =
 +        "WARNING: The checkpoint file was generated with integrator %s,\n"
 +        "         while the simulation uses integrator %s\n\n";
 +
 +#if !defined __native_client__ && !defined GMX_NATIVE_WINDOWS
 +    fl.l_type   = F_WRLCK;
 +    fl.l_whence = SEEK_SET;
 +    fl.l_start  = 0;
 +    fl.l_len    = 0;
 +    fl.l_pid    = 0;
 +#endif
 +
 +    fp = gmx_fio_open(fn, "r");
 +    do_cpt_header(gmx_fio_getxdr(fp), TRUE, &file_version,
 +                  &version, &btime, &buser, &bhost, &double_prec, &fprog, &ftime,
 +                  &eIntegrator_f, simulation_part, step, t,
 +                  &nppnodes_f, dd_nc_f, &npmenodes_f,
 +                  &natoms, &ngtc, &nnhpres, &nhchainlength, &nlambda,
 +                  &fflags, &flags_eks, &flags_enh, &flags_dfh,
 +                  &state->edsamstate.nED, &state->swapstate.eSwapCoords, NULL);
 +
 +    if (bAppendOutputFiles &&
 +        file_version >= 13 && double_prec != GMX_CPT_BUILD_DP)
 +    {
 +        gmx_fatal(FARGS, "Output file appending requested, but the code and checkpoint file precision (single/double) don't match");
 +    }
 +
 +    if (cr == NULL || MASTER(cr))
 +    {
 +        fprintf(stderr, "\nReading checkpoint file %s generated: %s\n\n",
 +                fn, ftime);
 +    }
 +
 +    /* This will not be written if we do appending, since fplog is still NULL then */
 +    if (fplog)
 +    {
 +        fprintf(fplog, "\n");
 +        fprintf(fplog, "Reading checkpoint file %s\n", fn);
 +        fprintf(fplog, "  file generated by:     %s\n", fprog);
 +        fprintf(fplog, "  file generated at:     %s\n", ftime);
 +        fprintf(fplog, "  GROMACS build time:    %s\n", btime);
 +        fprintf(fplog, "  GROMACS build user:    %s\n", buser);
 +        fprintf(fplog, "  GROMACS build host:    %s\n", bhost);
 +        fprintf(fplog, "  GROMACS double prec.:  %d\n", double_prec);
 +        fprintf(fplog, "  simulation part #:     %d\n", *simulation_part);
 +        fprintf(fplog, "  step:                  %s\n", gmx_step_str(*step, buf));
 +        fprintf(fplog, "  time:                  %f\n", *t);
 +        fprintf(fplog, "\n");
 +    }
 +
 +    if (natoms != state->natoms)
 +    {
 +        gmx_fatal(FARGS, "Checkpoint file is for a system of %d atoms, while the current system consists of %d atoms", natoms, state->natoms);
 +    }
 +    if (ngtc != state->ngtc)
 +    {
 +        gmx_fatal(FARGS, "Checkpoint file is for a system of %d T-coupling groups, while the current system consists of %d T-coupling groups", ngtc, state->ngtc);
 +    }
 +    if (nnhpres != state->nnhpres)
 +    {
 +        gmx_fatal(FARGS, "Checkpoint file is for a system of %d NH-pressure-coupling variables, while the current system consists of %d NH-pressure-coupling variables", nnhpres, state->nnhpres);
 +    }
 +
 +    if (nlambda != state->dfhist.nlambda)
 +    {
 +        gmx_fatal(FARGS, "Checkpoint file is for a system with %d lambda states, while the current system consists of %d lambda states", nlambda, state->dfhist.nlambda);
 +    }
 +
 +    init_gtc_state(state, state->ngtc, state->nnhpres, nhchainlength); /* need to keep this here to keep the tpr format working */
 +    /* write over whatever was read; we use the number of Nose-Hoover chains from the checkpoint */
 +
 +    if (eIntegrator_f != eIntegrator)
 +    {
 +        if (MASTER(cr))
 +        {
 +            fprintf(stderr, int_warn, EI(eIntegrator_f), EI(eIntegrator));
 +        }
 +        if (bAppendOutputFiles)
 +        {
 +            gmx_fatal(FARGS,
 +                      "Output file appending requested, but input/checkpoint integrators do not match.\n"
 +                      "Stopping the run to prevent you from ruining all your data...\n"
 +                      "If you _really_ know what you are doing, try with the -noappend option.\n");
 +        }
 +        if (fplog)
 +        {
 +            fprintf(fplog, int_warn, EI(eIntegrator_f), EI(eIntegrator));
 +        }
 +    }
 +
 +    if (!PAR(cr))
 +    {
 +        nppnodes      = 1;
 +        cr->npmenodes = 0;
 +    }
 +    else if (cr->nnodes == nppnodes_f + npmenodes_f)
 +    {
 +        if (cr->npmenodes < 0)
 +        {
 +            cr->npmenodes = npmenodes_f;
 +        }
 +        nppnodes = cr->nnodes - cr->npmenodes;
 +        if (nppnodes == nppnodes_f)
 +        {
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if (dd_nc[d] == 0)
 +                {
 +                    dd_nc[d] = dd_nc_f[d];
 +                }
 +            }
 +        }
 +    }
 +    else
 +    {
 +        /* The number of PP nodes has not been set yet */
 +        nppnodes = -1;
 +    }
 +
 +    if (fflags != state->flags)
 +    {
 +
 +        if (MASTER(cr))
 +        {
 +            if (bAppendOutputFiles)
 +            {
 +                gmx_fatal(FARGS,
 +                          "Output file appending requested, but input and checkpoint states are not identical.\n"
 +                          "Stopping the run to prevent you from ruining all your data...\n"
 +                          "You can try with the -noappend option, and get more info in the log file.\n");
 +            }
 +
 +            if (getenv("GMX_ALLOW_CPT_MISMATCH") == NULL)
 +            {
 +                gmx_fatal(FARGS, "You seem to have switched ensemble, integrator, T and/or P-coupling algorithm between the cpt and tpr file. The recommended way of doing this is passing the cpt file to grompp (with option -t) instead of to mdrun. If you know what you are doing, you can override this error by setting the env.var. GMX_ALLOW_CPT_MISMATCH");
 +            }
 +            else
 +            {
 +                fprintf(stderr,
 +                        "WARNING: The checkpoint state entries do not match the simulation,\n"
 +                        "         see the log file for details\n\n");
 +            }
 +        }
 +
 +        if (fplog)
 +        {
 +            print_flag_mismatch(fplog, state->flags, fflags);
 +        }
 +    }
 +    else
 +    {
 +        if (MASTER(cr))
 +        {
 +            check_match(fplog, version, btime, buser, bhost, double_prec, fprog,
 +                        cr, nppnodes_f, npmenodes_f, dd_nc, dd_nc_f);
 +        }
 +    }
 +    ret             = do_cpt_state(gmx_fio_getxdr(fp), TRUE, fflags, state, NULL);
 +    *init_fep_state = state->fep_state;  /* there should be a better way to do this than setting it here.
 +                                            Investigate for 5.0. */
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +    ret = do_cpt_ekinstate(gmx_fio_getxdr(fp), flags_eks, &state->ekinstate, NULL);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +    *bReadEkin = ((flags_eks & (1<<eeksEKINH)) || (flags_eks & (1<<eeksEKINF)) || (flags_eks & (1<<eeksEKINO)) ||
 +                  ((flags_eks & (1<<eeksEKINSCALEF)) | (flags_eks & (1<<eeksEKINSCALEH)) | (flags_eks & (1<<eeksVSCALE))));
 +
 +    ret = do_cpt_enerhist(gmx_fio_getxdr(fp), TRUE,
 +                          flags_enh, &state->enerhist, NULL);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +
 +    if (file_version < 6)
 +    {
 +        const char *warn = "Reading checkpoint file in old format, assuming that the run that generated this file started at step 0, if this is not the case the averages stored in the energy file will be incorrect.";
 +
 +        fprintf(stderr, "\nWARNING: %s\n\n", warn);
 +        if (fplog)
 +        {
 +            fprintf(fplog, "\nWARNING: %s\n\n", warn);
 +        }
 +        state->enerhist.nsum     = *step;
 +        state->enerhist.nsum_sim = *step;
 +    }
 +
 +    ret = do_cpt_df_hist(gmx_fio_getxdr(fp), flags_dfh, &state->dfhist, NULL);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +
 +    ret = do_cpt_swapstate(gmx_fio_getxdr(fp), TRUE, &state->swapstate, NULL);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +
 +    ret = do_cpt_EDstate(gmx_fio_getxdr(fp), TRUE, &state->edsamstate, NULL);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +
 +    ret = do_cpt_files(gmx_fio_getxdr(fp), TRUE, &outputfiles, &nfiles, NULL, file_version);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +
 +    ret = do_cpt_footer(gmx_fio_getxdr(fp), file_version);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +    if (gmx_fio_close(fp) != 0)
 +    {
 +        gmx_file("Cannot read/write checkpoint; corrupt file, or maybe you are out of disk space?");
 +    }
 +
 +    sfree(fprog);
 +    sfree(ftime);
 +    sfree(btime);
 +    sfree(buser);
 +    sfree(bhost);
 +
 +    /* If the user wants to append to output files,
 +     * we use the file pointer positions of the output files stored
 +     * in the checkpoint file and truncate the files such that any frames
 +     * written after the checkpoint time are removed.
 +     * All files are md5sum checked such that we can be sure that
 +     * we do not truncate other (maybe imprortant) files.
 +     */
 +    if (bAppendOutputFiles)
 +    {
 +        if (fn2ftp(outputfiles[0].filename) != efLOG)
 +        {
 +            /* make sure first file is log file so that it is OK to use it for
 +             * locking
 +             */
 +            gmx_fatal(FARGS, "The first output file should always be the log "
 +                      "file but instead is: %s. Cannot do appending because of this condition.", outputfiles[0].filename);
 +        }
 +        for (i = 0; i < nfiles; i++)
 +        {
 +            if (outputfiles[i].offset < 0)
 +            {
 +                gmx_fatal(FARGS, "The original run wrote a file called '%s' which "
 +                          "is larger than 2 GB, but mdrun did not support large file"
 +                          " offsets. Can not append. Run mdrun with -noappend",
 +                          outputfiles[i].filename);
 +            }
 +#ifdef GMX_FAHCORE
 +            chksum_file = gmx_fio_open(outputfiles[i].filename, "a");
 +
 +#else
 +            chksum_file = gmx_fio_open(outputfiles[i].filename, "r+");
 +
 +            /* lock log file */
 +            if (i == 0)
 +            {
 +                /* Note that there are systems where the lock operation
 +                 * will succeed, but a second process can also lock the file.
 +                 * We should probably try to detect this.
 +                 */
 +#if defined __native_client__
 +                errno = ENOSYS;
 +                if (1)
 +
 +#elif defined GMX_NATIVE_WINDOWS
 +                if (_locking(fileno(gmx_fio_getfp(chksum_file)), _LK_NBLCK, LONG_MAX) == -1)
 +#else
 +                if (fcntl(fileno(gmx_fio_getfp(chksum_file)), F_SETLK, &fl) == -1)
 +#endif
 +                {
 +                    if (errno == ENOSYS)
 +                    {
 +                        if (!bForceAppend)
 +                        {
 +                            gmx_fatal(FARGS, "File locking is not supported on this system. Use -noappend or specify -append explicitly to append anyhow.");
 +                        }
 +                        else
 +                        {
 +                            fprintf(stderr, "\nNOTE: File locking is not supported on this system, will not lock %s\n\n", outputfiles[i].filename);
 +                            if (fplog)
 +                            {
 +                                fprintf(fplog, "\nNOTE: File locking not supported on this system, will not lock %s\n\n", outputfiles[i].filename);
 +                            }
 +                        }
 +                    }
 +                    else if (errno == EACCES || errno == EAGAIN)
 +                    {
 +                        gmx_fatal(FARGS, "Failed to lock: %s. Already running "
 +                                  "simulation?", outputfiles[i].filename);
 +                    }
 +                    else
 +                    {
 +                        gmx_fatal(FARGS, "Failed to lock: %s. %s.",
 +                                  outputfiles[i].filename, strerror(errno));
 +                    }
 +                }
 +            }
 +
 +            /* compute md5 chksum */
 +            if (outputfiles[i].chksum_size != -1)
 +            {
 +                if (gmx_fio_get_file_md5(chksum_file, outputfiles[i].offset,
 +                                         digest) != outputfiles[i].chksum_size) /*at the end of the call the file position is at the end of the file*/
 +                {
 +                    gmx_fatal(FARGS, "Can't read %d bytes of '%s' to compute checksum. The file has been replaced or its contents have been modified. Cannot do appending because of this condition.",
 +                              outputfiles[i].chksum_size,
 +                              outputfiles[i].filename);
 +                }
 +            }
 +            if (i == 0)  /*log file needs to be seeked in case we need to truncate (other files are truncated below)*/
 +            {
 +                if (gmx_fio_seek(chksum_file, outputfiles[i].offset))
 +                {
 +                    gmx_fatal(FARGS, "Seek error! Failed to truncate log-file: %s.", strerror(errno));
 +                }
 +            }
 +#endif
 +
 +            if (i == 0) /*open log file here - so that lock is never lifted
 +                           after chksum is calculated */
 +            {
 +                *pfplog = gmx_fio_getfp(chksum_file);
 +            }
 +            else
 +            {
 +                gmx_fio_close(chksum_file);
 +            }
 +#ifndef GMX_FAHCORE
 +            /* compare md5 chksum */
 +            if (outputfiles[i].chksum_size != -1 &&
 +                memcmp(digest, outputfiles[i].chksum, 16) != 0)
 +            {
 +                if (debug)
 +                {
 +                    fprintf(debug, "chksum for %s: ", outputfiles[i].filename);
 +                    for (j = 0; j < 16; j++)
 +                    {
 +                        fprintf(debug, "%02x", digest[j]);
 +                    }
 +                    fprintf(debug, "\n");
 +                }
 +                gmx_fatal(FARGS, "Checksum wrong for '%s'. The file has been replaced or its contents have been modified. Cannot do appending because of this condition.",
 +                          outputfiles[i].filename);
 +            }
 +#endif
 +
 +
 +            if (i != 0) /*log file is already seeked to correct position */
 +            {
 +#ifdef GMX_NATIVE_WINDOWS
 +                rc = gmx_wintruncate(outputfiles[i].filename, outputfiles[i].offset);
 +#else
 +                rc = truncate(outputfiles[i].filename, outputfiles[i].offset);
 +#endif
 +                if (rc != 0)
 +                {
 +                    gmx_fatal(FARGS, "Truncation of file %s failed. Cannot do appending because of this failure.", outputfiles[i].filename);
 +                }
 +            }
 +        }
 +    }
 +
 +    sfree(outputfiles);
 +}
 +
 +
 +void load_checkpoint(const char *fn, FILE **fplog,
 +                     t_commrec *cr, ivec dd_nc,
 +                     t_inputrec *ir, t_state *state,
 +                     gmx_bool *bReadEkin,
 +                     gmx_bool bAppend, gmx_bool bForceAppend)
 +{
 +    gmx_int64_t     step;
 +    double          t;
 +
 +    if (SIMMASTER(cr))
 +    {
 +        /* Read the state from the checkpoint file */
 +        read_checkpoint(fn, fplog,
 +                        cr, dd_nc,
 +                        ir->eI, &(ir->fepvals->init_fep_state), &step, &t, state, bReadEkin,
 +                        &ir->simulation_part, bAppend, bForceAppend);
 +    }
 +    if (PAR(cr))
 +    {
 +        gmx_bcast(sizeof(cr->npmenodes), &cr->npmenodes, cr);
 +        gmx_bcast(DIM*sizeof(dd_nc[0]), dd_nc, cr);
 +        gmx_bcast(sizeof(step), &step, cr);
 +        gmx_bcast(sizeof(*bReadEkin), bReadEkin, cr);
 +    }
 +    ir->bContinuation    = TRUE;
 +    if (ir->nsteps >= 0)
 +    {
 +        ir->nsteps          += ir->init_step - step;
 +    }
 +    ir->init_step        = step;
 +    ir->simulation_part += 1;
 +}
 +
 +static void read_checkpoint_data(t_fileio *fp, int *simulation_part,
 +                                 gmx_int64_t *step, double *t, t_state *state,
 +                                 int *nfiles, gmx_file_position_t **outputfiles)
 +{
 +    int                  file_version;
 +    char                *version, *btime, *buser, *bhost, *fprog, *ftime;
 +    int                  double_prec;
 +    int                  eIntegrator;
 +    int                  nppnodes, npme;
 +    ivec                 dd_nc;
 +    int                  flags_eks, flags_enh, flags_dfh;
 +    int                  nfiles_loc;
 +    gmx_file_position_t *files_loc = NULL;
 +    int                  ret;
 +
 +    do_cpt_header(gmx_fio_getxdr(fp), TRUE, &file_version,
 +                  &version, &btime, &buser, &bhost, &double_prec, &fprog, &ftime,
 +                  &eIntegrator, simulation_part, step, t, &nppnodes, dd_nc, &npme,
 +                  &state->natoms, &state->ngtc, &state->nnhpres, &state->nhchainlength,
 +                  &(state->dfhist.nlambda), &state->flags, &flags_eks, &flags_enh, &flags_dfh,
 +                  &state->edsamstate.nED, &state->swapstate.eSwapCoords, NULL);
 +    ret =
 +        do_cpt_state(gmx_fio_getxdr(fp), TRUE, state->flags, state, NULL);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +    ret = do_cpt_ekinstate(gmx_fio_getxdr(fp), flags_eks, &state->ekinstate, NULL);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +    ret = do_cpt_enerhist(gmx_fio_getxdr(fp), TRUE,
 +                          flags_enh, &state->enerhist, NULL);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +    ret = do_cpt_df_hist(gmx_fio_getxdr(fp), flags_dfh, &state->dfhist, NULL);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +
 +    ret = do_cpt_EDstate(gmx_fio_getxdr(fp), TRUE, &state->edsamstate, NULL);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +
 +    ret = do_cpt_swapstate(gmx_fio_getxdr(fp), TRUE, &state->swapstate, NULL);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +
 +    ret = do_cpt_files(gmx_fio_getxdr(fp), TRUE,
 +                       outputfiles != NULL ? outputfiles : &files_loc,
 +                       outputfiles != NULL ? nfiles : &nfiles_loc,
 +                       NULL, file_version);
 +    if (files_loc != NULL)
 +    {
 +        sfree(files_loc);
 +    }
 +
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +
 +    ret = do_cpt_footer(gmx_fio_getxdr(fp), file_version);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +
 +    sfree(fprog);
 +    sfree(ftime);
 +    sfree(btime);
 +    sfree(buser);
 +    sfree(bhost);
 +}
 +
 +void
 +read_checkpoint_state(const char *fn, int *simulation_part,
 +                      gmx_int64_t *step, double *t, t_state *state)
 +{
 +    t_fileio *fp;
 +
 +    fp = gmx_fio_open(fn, "r");
 +    read_checkpoint_data(fp, simulation_part, step, t, state, NULL, NULL);
 +    if (gmx_fio_close(fp) != 0)
 +    {
 +        gmx_file("Cannot read/write checkpoint; corrupt file, or maybe you are out of disk space?");
 +    }
 +}
 +
 +void read_checkpoint_trxframe(t_fileio *fp, t_trxframe *fr)
 +{
 +    /* This next line is nasty because the sub-structures of t_state
 +     * cannot be assumed to be zeroed (or even initialized in ways the
 +     * rest of the code might assume). Using snew would be better, but
 +     * this will all go away for 5.0. */
 +    t_state         state;
 +    int             simulation_part;
 +    gmx_int64_t     step;
 +    double          t;
 +
 +    init_state(&state, 0, 0, 0, 0, 0);
 +
 +    read_checkpoint_data(fp, &simulation_part, &step, &t, &state, NULL, NULL);
 +
 +    fr->natoms  = state.natoms;
 +    fr->bTitle  = FALSE;
 +    fr->bStep   = TRUE;
 +    fr->step    = gmx_int64_to_int(step,
 +                                   "conversion of checkpoint to trajectory");
 +    fr->bTime      = TRUE;
 +    fr->time       = t;
 +    fr->bLambda    = TRUE;
 +    fr->lambda     = state.lambda[efptFEP];
 +    fr->fep_state  = state.fep_state;
 +    fr->bAtoms     = FALSE;
 +    fr->bX         = (state.flags & (1<<estX));
 +    if (fr->bX)
 +    {
 +        fr->x     = state.x;
 +        state.x   = NULL;
 +    }
 +    fr->bV      = (state.flags & (1<<estV));
 +    if (fr->bV)
 +    {
 +        fr->v     = state.v;
 +        state.v   = NULL;
 +    }
 +    fr->bF      = FALSE;
 +    fr->bBox    = (state.flags & (1<<estBOX));
 +    if (fr->bBox)
 +    {
 +        copy_mat(state.box, fr->box);
 +    }
 +    done_state(&state);
 +}
 +
 +void list_checkpoint(const char *fn, FILE *out)
 +{
 +    t_fileio            *fp;
 +    int                  file_version;
 +    char                *version, *btime, *buser, *bhost, *fprog, *ftime;
 +    int                  double_prec;
 +    int                  eIntegrator, simulation_part, nppnodes, npme;
 +    gmx_int64_t          step;
 +    double               t;
 +    ivec                 dd_nc;
 +    t_state              state;
 +    int                  flags_eks, flags_enh, flags_dfh;
 +    int                  indent;
 +    int                  i, j;
 +    int                  ret;
 +    gmx_file_position_t *outputfiles;
 +    int                  nfiles;
 +
 +    init_state(&state, -1, -1, -1, -1, 0);
 +
 +    fp = gmx_fio_open(fn, "r");
 +    do_cpt_header(gmx_fio_getxdr(fp), TRUE, &file_version,
 +                  &version, &btime, &buser, &bhost, &double_prec, &fprog, &ftime,
 +                  &eIntegrator, &simulation_part, &step, &t, &nppnodes, dd_nc, &npme,
 +                  &state.natoms, &state.ngtc, &state.nnhpres, &state.nhchainlength,
 +                  &(state.dfhist.nlambda), &state.flags,
 +                  &flags_eks, &flags_enh, &flags_dfh, &state.edsamstate.nED,
 +                  &state.swapstate.eSwapCoords, out);
 +    ret = do_cpt_state(gmx_fio_getxdr(fp), TRUE, state.flags, &state, out);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +    ret = do_cpt_ekinstate(gmx_fio_getxdr(fp), flags_eks, &state.ekinstate, out);
 +    if (ret)
 +    {
 +        cp_error();
 +    }
 +    ret = do_cpt_enerhist(gmx_fio_getxdr(fp), TRUE,
 +                          flags_enh, &state.enerhist, out);
 +
 +    if (ret == 0)
 +    {
 +        ret = do_cpt_df_hist(gmx_fio_getxdr(fp),
 +                             flags_dfh, &state.dfhist, out);
 +    }
 +
 +    if (ret == 0)
 +    {
 +        ret = do_cpt_EDstate(gmx_fio_getxdr(fp), TRUE, &state.edsamstate, out);
 +    }
 +
 +    if (ret == 0)
 +    {
 +        ret = do_cpt_swapstate(gmx_fio_getxdr(fp), TRUE, &state.swapstate, out);
 +    }
 +
 +    if (ret == 0)
 +    {
 +        do_cpt_files(gmx_fio_getxdr(fp), TRUE, &outputfiles, &nfiles, out, file_version);
 +    }
 +
 +    if (ret == 0)
 +    {
 +        ret = do_cpt_footer(gmx_fio_getxdr(fp), file_version);
 +    }
 +
 +    if (ret)
 +    {
 +        cp_warning(out);
 +    }
 +    if (gmx_fio_close(fp) != 0)
 +    {
 +        gmx_file("Cannot read/write checkpoint; corrupt file, or maybe you are out of disk space?");
 +    }
 +
 +    done_state(&state);
 +}
 +
 +
 +static gmx_bool exist_output_file(const char *fnm_cp, int nfile, const t_filenm fnm[])
 +{
 +    int i;
 +
 +    /* Check if the output file name stored in the checkpoint file
 +     * is one of the output file names of mdrun.
 +     */
 +    i = 0;
 +    while (i < nfile &&
 +           !(is_output(&fnm[i]) && strcmp(fnm_cp, fnm[i].fns[0]) == 0))
 +    {
 +        i++;
 +    }
 +
 +    return (i < nfile && gmx_fexist(fnm_cp));
 +}
 +
 +/* This routine cannot print tons of data, since it is called before the log file is opened. */
 +gmx_bool read_checkpoint_simulation_part(const char *filename, int *simulation_part,
 +                                         gmx_int64_t *cpt_step, t_commrec *cr,
 +                                         gmx_bool bAppendReq,
 +                                         int nfile, const t_filenm fnm[],
 +                                         const char *part_suffix, gmx_bool *bAddPart)
 +{
 +    t_fileio            *fp;
 +    gmx_int64_t          step = 0;
 +    double               t;
 +    /* This next line is nasty because the sub-structures of t_state
 +     * cannot be assumed to be zeroed (or even initialized in ways the
 +     * rest of the code might assume). Using snew would be better, but
 +     * this will all go away for 5.0. */
 +    t_state              state;
 +    int                  nfiles;
 +    gmx_file_position_t *outputfiles;
 +    int                  nexist, f;
 +    gmx_bool             bAppend;
 +    char                *fn, suf_up[STRLEN];
 +
 +    bAppend = FALSE;
 +
 +    if (SIMMASTER(cr))
 +    {
 +        if (!gmx_fexist(filename) || (!(fp = gmx_fio_open(filename, "r")) ))
 +        {
 +            *simulation_part = 0;
 +        }
 +        else
 +        {
 +            init_state(&state, 0, 0, 0, 0, 0);
 +
 +            read_checkpoint_data(fp, simulation_part, &step, &t, &state,
 +                                 &nfiles, &outputfiles);
 +            if (gmx_fio_close(fp) != 0)
 +            {
 +                gmx_file("Cannot read/write checkpoint; corrupt file, or maybe you are out of disk space?");
 +            }
 +            done_state(&state);
 +
 +            if (bAppendReq)
 +            {
 +                nexist = 0;
 +                for (f = 0; f < nfiles; f++)
 +                {
 +                    if (exist_output_file(outputfiles[f].filename, nfile, fnm))
 +                    {
 +                        nexist++;
 +                    }
 +                }
 +                if (nexist == nfiles)
 +                {
 +                    bAppend = bAppendReq;
 +                }
 +                else if (nexist > 0)
 +                {
 +                    fprintf(stderr,
 +                            "Output file appending has been requested,\n"
 +                            "but some output files listed in the checkpoint file %s\n"
 +                            "are not present or are named differently by the current program:\n",
 +                            filename);
 +                    fprintf(stderr, "output files present:");
 +                    for (f = 0; f < nfiles; f++)
 +                    {
 +                        if (exist_output_file(outputfiles[f].filename,
 +                                              nfile, fnm))
 +                        {
 +                            fprintf(stderr, " %s", outputfiles[f].filename);
 +                        }
 +                    }
 +                    fprintf(stderr, "\n");
 +                    fprintf(stderr, "output files not present or named differently:");
 +                    for (f = 0; f < nfiles; f++)
 +                    {
 +                        if (!exist_output_file(outputfiles[f].filename,
 +                                               nfile, fnm))
 +                        {
 +                            fprintf(stderr, " %s", outputfiles[f].filename);
 +                        }
 +                    }
 +                    fprintf(stderr, "\n");
 +
++                    gmx_fatal(FARGS, "File appending requested, but %d of the %d output files are not present or are named differently", nfiles-nexist, nfiles);
 +                }
 +            }
 +
 +            if (bAppend)
 +            {
 +                if (nfiles == 0)
 +                {
 +                    gmx_fatal(FARGS, "File appending requested, but no output file information is stored in the checkpoint file");
 +                }
 +                fn = outputfiles[0].filename;
 +                if (strlen(fn) < 4 ||
 +                    gmx_strcasecmp(fn+strlen(fn)-4, ftp2ext(efLOG)) == 0)
 +                {
 +                    gmx_fatal(FARGS, "File appending requested, but the log file is not the first file listed in the checkpoint file");
 +                }
 +                /* Set bAddPart to whether the suffix string '.part' is present
 +                 * in the log file name.
 +                 */
 +                strcpy(suf_up, part_suffix);
 +                upstring(suf_up);
 +                *bAddPart = (strstr(fn, part_suffix) != NULL ||
 +                             strstr(fn, suf_up) != NULL);
 +            }
 +
 +            sfree(outputfiles);
 +        }
 +    }
 +    if (PAR(cr))
 +    {
 +        gmx_bcast(sizeof(*simulation_part), simulation_part, cr);
 +
 +        if (*simulation_part > 0 && bAppendReq)
 +        {
 +            gmx_bcast(sizeof(bAppend), &bAppend, cr);
 +            gmx_bcast(sizeof(*bAddPart), bAddPart, cr);
 +        }
 +    }
 +    if (NULL != cpt_step)
 +    {
 +        *cpt_step = step;
 +    }
 +
 +    return bAppend;
 +}
index 4c1e6d888b1d87f78ec401907d5bd23fb0829074,0000000000000000000000000000000000000000..04ee47de4053d37c9ffca49f81165448a4312224
mode 100644,000000..100644
--- /dev/null
@@@ -1,2059 -1,0 +1,2098 @@@
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "grompp.h"
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <sys/types.h>
 +#include <math.h>
 +#include <string.h>
 +#include <errno.h>
 +#include <limits.h>
 +#include <assert.h>
 +
 +#include "sysstuff.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "readir.h"
 +#include "toputil.h"
 +#include "topio.h"
 +#include "gromacs/fileio/confio.h"
 +#include "readir.h"
 +#include "symtab.h"
 +#include "names.h"
 +#include "grompp-impl.h"
 +#include "gromacs/random/random.h"
 +#include "gromacs/gmxpreprocess/gen_maxwell_velocities.h"
 +#include "vec.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "splitter.h"
 +#include "gromacs/gmxpreprocess/sortwater.h"
 +#include "convparm.h"
 +#include "gmx_fatal.h"
 +#include "warninp.h"
 +#include "index.h"
 +#include "gromacs/fileio/gmxfio.h"
 +#include "gromacs/fileio/trnio.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "vsite_parm.h"
 +#include "txtdump.h"
 +#include "calcgrid.h"
 +#include "add_par.h"
 +#include "gromacs/fileio/enxio.h"
 +#include "perf_est.h"
 +#include "compute_io.h"
 +#include "gpp_atomtype.h"
 +#include "mtop_util.h"
 +#include "genborn.h"
 +#include "calc_verletbuf.h"
 +#include "tomorse.h"
 +#include "gromacs/imd/imd.h"
++#include "gromacs/utility/cstringutil.h"
 +
 +
 +static int rm_interactions(int ifunc, int nrmols, t_molinfo mols[])
 +{
 +    int  i, n;
 +
 +    n = 0;
 +    /* For all the molecule types */
 +    for (i = 0; i < nrmols; i++)
 +    {
 +        n += mols[i].plist[ifunc].nr;
 +        mols[i].plist[ifunc].nr = 0;
 +    }
 +    return n;
 +}
 +
 +static int check_atom_names(const char *fn1, const char *fn2,
 +                            gmx_mtop_t *mtop, t_atoms *at)
 +{
 +    int      mb, m, i, j, nmismatch;
 +    t_atoms *tat;
 +#define MAXMISMATCH 20
 +
 +    if (mtop->natoms != at->nr)
 +    {
 +        gmx_incons("comparing atom names");
 +    }
 +
 +    nmismatch = 0;
 +    i         = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        tat = &mtop->moltype[mtop->molblock[mb].type].atoms;
 +        for (m = 0; m < mtop->molblock[mb].nmol; m++)
 +        {
 +            for (j = 0; j < tat->nr; j++)
 +            {
 +                if (strcmp( *(tat->atomname[j]), *(at->atomname[i]) ) != 0)
 +                {
 +                    if (nmismatch < MAXMISMATCH)
 +                    {
 +                        fprintf(stderr,
 +                                "Warning: atom name %d in %s and %s does not match (%s - %s)\n",
 +                                i+1, fn1, fn2, *(tat->atomname[j]), *(at->atomname[i]));
 +                    }
 +                    else if (nmismatch == MAXMISMATCH)
 +                    {
 +                        fprintf(stderr, "(more than %d non-matching atom names)\n", MAXMISMATCH);
 +                    }
 +                    nmismatch++;
 +                }
 +                i++;
 +            }
 +        }
 +    }
 +
 +    return nmismatch;
 +}
 +
 +static void check_eg_vs_cg(gmx_mtop_t *mtop)
 +{
 +    int            astart, mb, m, cg, j, firstj;
 +    unsigned char  firsteg, eg;
 +    gmx_moltype_t *molt;
 +
 +    /* Go through all the charge groups and make sure all their
 +     * atoms are in the same energy group.
 +     */
 +
 +    astart = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molt = &mtop->moltype[mtop->molblock[mb].type];
 +        for (m = 0; m < mtop->molblock[mb].nmol; m++)
 +        {
 +            for (cg = 0; cg < molt->cgs.nr; cg++)
 +            {
 +                /* Get the energy group of the first atom in this charge group */
 +                firstj  = astart + molt->cgs.index[cg];
 +                firsteg = ggrpnr(&mtop->groups, egcENER, firstj);
 +                for (j = molt->cgs.index[cg]+1; j < molt->cgs.index[cg+1]; j++)
 +                {
 +                    eg = ggrpnr(&mtop->groups, egcENER, astart+j);
 +                    if (eg != firsteg)
 +                    {
 +                        gmx_fatal(FARGS, "atoms %d and %d in charge group %d of molecule type '%s' are in different energy groups",
 +                                  firstj+1, astart+j+1, cg+1, *molt->name);
 +                    }
 +                }
 +            }
 +            astart += molt->atoms.nr;
 +        }
 +    }
 +}
 +
 +static void check_cg_sizes(const char *topfn, t_block *cgs, warninp_t wi)
 +{
 +    int  maxsize, cg;
 +    char warn_buf[STRLEN];
 +
 +    maxsize = 0;
 +    for (cg = 0; cg < cgs->nr; cg++)
 +    {
 +        maxsize = max(maxsize, cgs->index[cg+1]-cgs->index[cg]);
 +    }
 +
 +    if (maxsize > MAX_CHARGEGROUP_SIZE)
 +    {
 +        gmx_fatal(FARGS, "The largest charge group contains %d atoms. The maximum is %d.", maxsize, MAX_CHARGEGROUP_SIZE);
 +    }
 +    else if (maxsize > 10)
 +    {
 +        set_warning_line(wi, topfn, -1);
 +        sprintf(warn_buf,
 +                "The largest charge group contains %d atoms.\n"
 +                "Since atoms only see each other when the centers of geometry of the charge groups they belong to are within the cut-off distance, too large charge groups can lead to serious cut-off artifacts.\n"
 +                "For efficiency and accuracy, charge group should consist of a few atoms.\n"
 +                "For all-atom force fields use: CH3, CH2, CH, NH2, NH, OH, CO2, CO, etc.",
 +                maxsize);
 +        warning_note(wi, warn_buf);
 +    }
 +}
 +
 +static void check_bonds_timestep(gmx_mtop_t *mtop, double dt, warninp_t wi)
 +{
 +    /* This check is not intended to ensure accurate integration,
 +     * rather it is to signal mistakes in the mdp settings.
 +     * A common mistake is to forget to turn on constraints
 +     * for MD after energy minimization with flexible bonds.
 +     * This check can also detect too large time steps for flexible water
 +     * models, but such errors will often be masked by the constraints
 +     * mdp options, which turns flexible water into water with bond constraints,
 +     * but without an angle constraint. Unfortunately such incorrect use
 +     * of water models can not easily be detected without checking
 +     * for specific model names.
 +     *
 +     * The stability limit of leap-frog or velocity verlet is 4.44 steps
 +     * per oscillational period.
 +     * But accurate bonds distributions are lost far before that limit.
 +     * To allow relatively common schemes (although not common with Gromacs)
 +     * of dt=1 fs without constraints and dt=2 fs with only H-bond constraints
 +     * we set the note limit to 10.
 +     */
 +    int            min_steps_warn = 5;
 +    int            min_steps_note = 10;
 +    t_iparams     *ip;
 +    int            molt;
 +    gmx_moltype_t *moltype, *w_moltype;
 +    t_atom        *atom;
 +    t_ilist       *ilist, *ilb, *ilc, *ils;
 +    int            ftype;
 +    int            i, a1, a2, w_a1, w_a2, j;
 +    real           twopi2, limit2, fc, re, m1, m2, period2, w_period2;
 +    gmx_bool       bFound, bWater, bWarn;
 +    char           warn_buf[STRLEN];
 +
 +    ip = mtop->ffparams.iparams;
 +
 +    twopi2 = sqr(2*M_PI);
 +
 +    limit2 = sqr(min_steps_note*dt);
 +
 +    w_a1      = w_a2 = -1;
 +    w_period2 = -1.0;
 +
 +    w_moltype = NULL;
 +    for (molt = 0; molt < mtop->nmoltype; molt++)
 +    {
 +        moltype = &mtop->moltype[molt];
 +        atom    = moltype->atoms.atom;
 +        ilist   = moltype->ilist;
 +        ilc     = &ilist[F_CONSTR];
 +        ils     = &ilist[F_SETTLE];
 +        for (ftype = 0; ftype < F_NRE; ftype++)
 +        {
 +            if (!(ftype == F_BONDS || ftype == F_G96BONDS || ftype == F_HARMONIC))
 +            {
 +                continue;
 +            }
 +
 +            ilb = &ilist[ftype];
 +            for (i = 0; i < ilb->nr; i += 3)
 +            {
 +                fc = ip[ilb->iatoms[i]].harmonic.krA;
 +                re = ip[ilb->iatoms[i]].harmonic.rA;
 +                if (ftype == F_G96BONDS)
 +                {
 +                    /* Convert squared sqaure fc to harmonic fc */
 +                    fc = 2*fc*re;
 +                }
 +                a1 = ilb->iatoms[i+1];
 +                a2 = ilb->iatoms[i+2];
 +                m1 = atom[a1].m;
 +                m2 = atom[a2].m;
 +                if (fc > 0 && m1 > 0 && m2 > 0)
 +                {
 +                    period2 = twopi2*m1*m2/((m1 + m2)*fc);
 +                }
 +                else
 +                {
 +                    period2 = GMX_FLOAT_MAX;
 +                }
 +                if (debug)
 +                {
 +                    fprintf(debug, "fc %g m1 %g m2 %g period %g\n",
 +                            fc, m1, m2, sqrt(period2));
 +                }
 +                if (period2 < limit2)
 +                {
 +                    bFound = FALSE;
 +                    for (j = 0; j < ilc->nr; j += 3)
 +                    {
 +                        if ((ilc->iatoms[j+1] == a1 && ilc->iatoms[j+2] == a2) ||
 +                            (ilc->iatoms[j+1] == a2 && ilc->iatoms[j+2] == a1))
 +                        {
 +                            bFound = TRUE;
 +                        }
 +                    }
 +                    for (j = 0; j < ils->nr; j += 4)
 +                    {
 +                        if ((a1 == ils->iatoms[j+1] || a1 == ils->iatoms[j+2] || a1 == ils->iatoms[j+3]) &&
 +                            (a2 == ils->iatoms[j+1] || a2 == ils->iatoms[j+2] || a2 == ils->iatoms[j+3]))
 +                        {
 +                            bFound = TRUE;
 +                        }
 +                    }
 +                    if (!bFound &&
 +                        (w_moltype == NULL || period2 < w_period2))
 +                    {
 +                        w_moltype = moltype;
 +                        w_a1      = a1;
 +                        w_a2      = a2;
 +                        w_period2 = period2;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    if (w_moltype != NULL)
 +    {
 +        bWarn = (w_period2 < sqr(min_steps_warn*dt));
 +        /* A check that would recognize most water models */
 +        bWater = ((*w_moltype->atoms.atomname[0])[0] == 'O' &&
 +                  w_moltype->atoms.nr <= 5);
 +        sprintf(warn_buf, "The bond in molecule-type %s between atoms %d %s and %d %s has an estimated oscillational period of %.1e ps, which is less than %d times the time step of %.1e ps.\n"
 +                "%s",
 +                *w_moltype->name,
 +                w_a1+1, *w_moltype->atoms.atomname[w_a1],
 +                w_a2+1, *w_moltype->atoms.atomname[w_a2],
 +                sqrt(w_period2), bWarn ? min_steps_warn : min_steps_note, dt,
 +                bWater ?
 +                "Maybe you asked for fexible water." :
 +                "Maybe you forgot to change the constraints mdp option.");
 +        if (bWarn)
 +        {
 +            warning(wi, warn_buf);
 +        }
 +        else
 +        {
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +}
 +
 +static void check_vel(gmx_mtop_t *mtop, rvec v[])
 +{
 +    gmx_mtop_atomloop_all_t aloop;
 +    t_atom                 *atom;
 +    int                     a;
 +
 +    aloop = gmx_mtop_atomloop_all_init(mtop);
 +    while (gmx_mtop_atomloop_all_next(aloop, &a, &atom))
 +    {
 +        if (atom->ptype == eptShell ||
 +            atom->ptype == eptBond  ||
 +            atom->ptype == eptVSite)
 +        {
 +            clear_rvec(v[a]);
 +        }
 +    }
 +}
 +
++static void check_shells_inputrec(gmx_mtop_t *mtop,
++                                  t_inputrec *ir,
++                                  warninp_t   wi)
++{
++    gmx_mtop_atomloop_all_t aloop;
++    t_atom                 *atom;
++    int                     a, nshells = 0;
++    char                    warn_buf[STRLEN];
++
++    aloop = gmx_mtop_atomloop_all_init(mtop);
++    while (gmx_mtop_atomloop_all_next(aloop, &a, &atom))
++    {
++        if (atom->ptype == eptShell ||
++            atom->ptype == eptBond)
++        {
++            nshells++;
++        }
++    }
++    if (IR_TWINRANGE(*ir) && (nshells > 0))
++    {
++        snprintf(warn_buf, STRLEN,
++                 "The combination of using shells and a twin-range cut-off is not supported");
++        warning_error(wi, warn_buf);
++    }
++    if ((nshells > 0) && (ir->nstcalcenergy != 1))
++    {
++        set_warning_line(wi, "unknown", -1);
++        snprintf(warn_buf, STRLEN,
++                 "There are %d shells, changing nstcalcenergy from %d to 1",
++                 nshells, ir->nstcalcenergy);
++        ir->nstcalcenergy = 1;
++        warning(wi, warn_buf);
++    }
++}
++
 +static gmx_bool nint_ftype(gmx_mtop_t *mtop, t_molinfo *mi, int ftype)
 +{
 +    int nint, mb;
 +
 +    nint = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        nint += mtop->molblock[mb].nmol*mi[mtop->molblock[mb].type].plist[ftype].nr;
 +    }
 +
 +    return nint;
 +}
 +
 +/* This routine reorders the molecule type array
 + * in the order of use in the molblocks,
 + * unused molecule types are deleted.
 + */
 +static void renumber_moltypes(gmx_mtop_t *sys,
 +                              int *nmolinfo, t_molinfo **molinfo)
 +{
 +    int       *order, norder, i;
 +    int        mb, mi;
 +    t_molinfo *minew;
 +
 +    snew(order, *nmolinfo);
 +    norder = 0;
 +    for (mb = 0; mb < sys->nmolblock; mb++)
 +    {
 +        for (i = 0; i < norder; i++)
 +        {
 +            if (order[i] == sys->molblock[mb].type)
 +            {
 +                break;
 +            }
 +        }
 +        if (i == norder)
 +        {
 +            /* This type did not occur yet, add it */
 +            order[norder] = sys->molblock[mb].type;
 +            /* Renumber the moltype in the topology */
 +            norder++;
 +        }
 +        sys->molblock[mb].type = i;
 +    }
 +
 +    /* We still need to reorder the molinfo structs */
 +    snew(minew, norder);
 +    for (mi = 0; mi < *nmolinfo; mi++)
 +    {
 +        for (i = 0; i < norder; i++)
 +        {
 +            if (order[i] == mi)
 +            {
 +                break;
 +            }
 +        }
 +        if (i == norder)
 +        {
 +            done_mi(&(*molinfo)[mi]);
 +        }
 +        else
 +        {
 +            minew[i] = (*molinfo)[mi];
 +        }
 +    }
 +    sfree(*molinfo);
 +
 +    *nmolinfo = norder;
 +    *molinfo  = minew;
 +}
 +
 +static void molinfo2mtop(int nmi, t_molinfo *mi, gmx_mtop_t *mtop)
 +{
 +    int            m;
 +    gmx_moltype_t *molt;
 +
 +    mtop->nmoltype = nmi;
 +    snew(mtop->moltype, nmi);
 +    for (m = 0; m < nmi; m++)
 +    {
 +        molt        = &mtop->moltype[m];
 +        molt->name  = mi[m].name;
 +        molt->atoms = mi[m].atoms;
 +        /* ilists are copied later */
 +        molt->cgs   = mi[m].cgs;
 +        molt->excls = mi[m].excls;
 +    }
 +}
 +
 +static void
 +new_status(const char *topfile, const char *topppfile, const char *confin,
 +           t_gromppopts *opts, t_inputrec *ir, gmx_bool bZero,
 +           gmx_bool bGenVel, gmx_bool bVerbose, t_state *state,
 +           gpp_atomtype_t atype, gmx_mtop_t *sys,
 +           int *nmi, t_molinfo **mi, t_params plist[],
 +           int *comb, double *reppow, real *fudgeQQ,
 +           gmx_bool bMorse,
 +           warninp_t wi)
 +{
 +    t_molinfo      *molinfo = NULL;
 +    int             nmolblock;
 +    gmx_molblock_t *molblock, *molbs;
 +    t_atoms        *confat;
 +    int             mb, i, nrmols, nmismatch;
 +    char            buf[STRLEN];
 +    gmx_bool        bGB = FALSE;
 +    char            warn_buf[STRLEN];
 +
 +    init_mtop(sys);
 +
 +    /* Set gmx_boolean for GB */
 +    if (ir->implicit_solvent)
 +    {
 +        bGB = TRUE;
 +    }
 +
 +    /* TOPOLOGY processing */
 +    sys->name = do_top(bVerbose, topfile, topppfile, opts, bZero, &(sys->symtab),
 +                       plist, comb, reppow, fudgeQQ,
 +                       atype, &nrmols, &molinfo, ir,
 +                       &nmolblock, &molblock, bGB,
 +                       wi);
 +
 +    sys->nmolblock = 0;
 +    snew(sys->molblock, nmolblock);
 +
 +    sys->natoms = 0;
 +    for (mb = 0; mb < nmolblock; mb++)
 +    {
 +        if (sys->nmolblock > 0 &&
 +            molblock[mb].type == sys->molblock[sys->nmolblock-1].type)
 +        {
 +            /* Merge consecutive blocks with the same molecule type */
 +            sys->molblock[sys->nmolblock-1].nmol += molblock[mb].nmol;
 +            sys->natoms += molblock[mb].nmol*sys->molblock[sys->nmolblock-1].natoms_mol;
 +        }
 +        else if (molblock[mb].nmol > 0)
 +        {
 +            /* Add a new molblock to the topology */
 +            molbs             = &sys->molblock[sys->nmolblock];
 +            *molbs            = molblock[mb];
 +            molbs->natoms_mol = molinfo[molbs->type].atoms.nr;
 +            molbs->nposres_xA = 0;
 +            molbs->nposres_xB = 0;
 +            sys->natoms      += molbs->nmol*molbs->natoms_mol;
 +            sys->nmolblock++;
 +        }
 +    }
 +    if (sys->nmolblock == 0)
 +    {
 +        gmx_fatal(FARGS, "No molecules were defined in the system");
 +    }
 +
 +    renumber_moltypes(sys, &nrmols, &molinfo);
 +
 +    if (bMorse)
 +    {
 +        convert_harmonics(nrmols, molinfo, atype);
 +    }
 +
 +    if (ir->eDisre == edrNone)
 +    {
 +        i = rm_interactions(F_DISRES, nrmols, molinfo);
 +        if (i > 0)
 +        {
 +            set_warning_line(wi, "unknown", -1);
 +            sprintf(warn_buf, "disre = no, removed %d distance restraints", i);
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +    if (opts->bOrire == FALSE)
 +    {
 +        i = rm_interactions(F_ORIRES, nrmols, molinfo);
 +        if (i > 0)
 +        {
 +            set_warning_line(wi, "unknown", -1);
 +            sprintf(warn_buf, "orire = no, removed %d orientation restraints", i);
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +
 +    /* Copy structures from msys to sys */
 +    molinfo2mtop(nrmols, molinfo, sys);
 +
 +    gmx_mtop_finalize(sys);
 +
 +    /* COORDINATE file processing */
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "processing coordinates...\n");
 +    }
 +
 +    get_stx_coordnum(confin, &state->natoms);
 +    if (state->natoms != sys->natoms)
 +    {
 +        gmx_fatal(FARGS, "number of coordinates in coordinate file (%s, %d)\n"
 +                  "             does not match topology (%s, %d)",
 +                  confin, state->natoms, topfile, sys->natoms);
 +    }
 +    else
 +    {
 +        /* make space for coordinates and velocities */
 +        char title[STRLEN];
 +        snew(confat, 1);
 +        init_t_atoms(confat, state->natoms, FALSE);
 +        init_state(state, state->natoms, 0, 0, 0, 0);
 +        read_stx_conf(confin, title, confat, state->x, state->v, NULL, state->box);
 +        /* This call fixes the box shape for runs with pressure scaling */
 +        set_box_rel(ir, state);
 +
 +        nmismatch = check_atom_names(topfile, confin, sys, confat);
 +        free_t_atoms(confat, TRUE);
 +        sfree(confat);
 +
 +        if (nmismatch)
 +        {
 +            sprintf(buf, "%d non-matching atom name%s\n"
 +                    "atom names from %s will be used\n"
 +                    "atom names from %s will be ignored\n",
 +                    nmismatch, (nmismatch == 1) ? "" : "s", topfile, confin);
 +            warning(wi, buf);
 +        }
 +        if (bVerbose)
 +        {
 +            fprintf(stderr, "double-checking input for internal consistency...\n");
 +        }
 +        double_check(ir, state->box, nint_ftype(sys, molinfo, F_CONSTR), wi);
 +    }
 +
 +    if (bGenVel)
 +    {
 +        real                   *mass;
 +        gmx_mtop_atomloop_all_t aloop;
 +        t_atom                 *atom;
 +        unsigned int            useed;
 +
 +        snew(mass, state->natoms);
 +        aloop = gmx_mtop_atomloop_all_init(sys);
 +        while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
 +        {
 +            mass[i] = atom->m;
 +        }
 +
 +        useed = opts->seed;
 +        if (opts->seed == -1)
 +        {
 +            useed = (int)gmx_rng_make_seed();
 +            fprintf(stderr, "Setting gen_seed to %u\n", useed);
 +        }
 +        maxwell_speed(opts->tempi, useed, sys, state->v);
 +
 +        stop_cm(stdout, state->natoms, mass, state->x, state->v);
 +        sfree(mass);
 +    }
 +
 +    *nmi = nrmols;
 +    *mi  = molinfo;
 +}
 +
 +static void copy_state(const char *slog, t_trxframe *fr,
 +                       gmx_bool bReadVel, t_state *state,
 +                       double *use_time)
 +{
 +    int i;
 +
 +    if (fr->not_ok & FRAME_NOT_OK)
 +    {
 +        gmx_fatal(FARGS, "Can not start from an incomplete frame");
 +    }
 +    if (!fr->bX)
 +    {
 +        gmx_fatal(FARGS, "Did not find a frame with coordinates in file %s",
 +                  slog);
 +    }
 +
 +    for (i = 0; i < state->natoms; i++)
 +    {
 +        copy_rvec(fr->x[i], state->x[i]);
 +    }
 +    if (bReadVel)
 +    {
 +        if (!fr->bV)
 +        {
 +            gmx_incons("Trajecory frame unexpectedly does not contain velocities");
 +        }
 +        for (i = 0; i < state->natoms; i++)
 +        {
 +            copy_rvec(fr->v[i], state->v[i]);
 +        }
 +    }
 +    if (fr->bBox)
 +    {
 +        copy_mat(fr->box, state->box);
 +    }
 +
 +    *use_time = fr->time;
 +}
 +
 +static void cont_status(const char *slog, const char *ener,
 +                        gmx_bool bNeedVel, gmx_bool bGenVel, real fr_time,
 +                        t_inputrec *ir, t_state *state,
 +                        gmx_mtop_t *sys,
 +                        const output_env_t oenv)
 +/* If fr_time == -1 read the last frame available which is complete */
 +{
 +    gmx_bool     bReadVel;
 +    t_trxframe   fr;
 +    t_trxstatus *fp;
 +    int          i;
 +    double       use_time;
 +
 +    bReadVel = (bNeedVel && !bGenVel);
 +
 +    fprintf(stderr,
 +            "Reading Coordinates%s and Box size from old trajectory\n",
 +            bReadVel ? ", Velocities" : "");
 +    if (fr_time == -1)
 +    {
 +        fprintf(stderr, "Will read whole trajectory\n");
 +    }
 +    else
 +    {
 +        fprintf(stderr, "Will read till time %g\n", fr_time);
 +    }
 +    if (!bReadVel)
 +    {
 +        if (bGenVel)
 +        {
 +            fprintf(stderr, "Velocities generated: "
 +                    "ignoring velocities in input trajectory\n");
 +        }
 +        read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X);
 +    }
 +    else
 +    {
 +        read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X | TRX_NEED_V);
 +
 +        if (!fr.bV)
 +        {
 +            fprintf(stderr,
 +                    "\n"
 +                    "WARNING: Did not find a frame with velocities in file %s,\n"
 +                    "         all velocities will be set to zero!\n\n", slog);
 +            for (i = 0; i < sys->natoms; i++)
 +            {
 +                clear_rvec(state->v[i]);
 +            }
 +            close_trj(fp);
 +            /* Search for a frame without velocities */
 +            bReadVel = FALSE;
 +            read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X);
 +        }
 +    }
 +
 +    state->natoms = fr.natoms;
 +
 +    if (sys->natoms != state->natoms)
 +    {
 +        gmx_fatal(FARGS, "Number of atoms in Topology "
 +                  "is not the same as in Trajectory");
 +    }
 +    copy_state(slog, &fr, bReadVel, state, &use_time);
 +
 +    /* Find the appropriate frame */
 +    while ((fr_time == -1 || fr.time < fr_time) &&
 +           read_next_frame(oenv, fp, &fr))
 +    {
 +        copy_state(slog, &fr, bReadVel, state, &use_time);
 +    }
 +
 +    close_trj(fp);
 +
 +    /* Set the relative box lengths for preserving the box shape.
 +     * Note that this call can lead to differences in the last bit
 +     * with respect to using gmx convert-tpr to create a [TT].tpx[tt] file.
 +     */
 +    set_box_rel(ir, state);
 +
 +    fprintf(stderr, "Using frame at t = %g ps\n", use_time);
 +    fprintf(stderr, "Starting time for run is %g ps\n", ir->init_t);
 +
 +    if ((ir->epc != epcNO  || ir->etc == etcNOSEHOOVER) && ener)
 +    {
 +        get_enx_state(ener, use_time, &sys->groups, ir, state);
 +        preserve_box_shape(ir, state->box_rel, state->boxv);
 +    }
 +}
 +
 +static void read_posres(gmx_mtop_t *mtop, t_molinfo *molinfo, gmx_bool bTopB,
 +                        char *fn,
 +                        int rc_scaling, int ePBC,
 +                        rvec com,
 +                        warninp_t wi)
 +{
 +    gmx_bool        bFirst = TRUE, *hadAtom;
 +    rvec           *x, *v, *xp;
 +    dvec            sum;
 +    double          totmass;
 +    t_atoms         dumat;
 +    matrix          box, invbox;
 +    int             natoms, npbcdim = 0;
 +    char            warn_buf[STRLEN], title[STRLEN];
 +    int             a, i, ai, j, k, mb, nat_molb;
 +    gmx_molblock_t *molb;
 +    t_params       *pr, *prfb;
 +    t_atom         *atom;
 +
 +    get_stx_coordnum(fn, &natoms);
 +    if (natoms != mtop->natoms)
 +    {
 +        sprintf(warn_buf, "The number of atoms in %s (%d) does not match the number of atoms in the topology (%d). Will assume that the first %d atoms in the topology and %s match.", fn, natoms, mtop->natoms, min(mtop->natoms, natoms), fn);
 +        warning(wi, warn_buf);
 +    }
 +    snew(x, natoms);
 +    snew(v, natoms);
 +    init_t_atoms(&dumat, natoms, FALSE);
 +    read_stx_conf(fn, title, &dumat, x, v, NULL, box);
 +
 +    npbcdim = ePBC2npbcdim(ePBC);
 +    clear_rvec(com);
 +    if (rc_scaling != erscNO)
 +    {
 +        copy_mat(box, invbox);
 +        for (j = npbcdim; j < DIM; j++)
 +        {
 +            clear_rvec(invbox[j]);
 +            invbox[j][j] = 1;
 +        }
 +        m_inv_ur0(invbox, invbox);
 +    }
 +
 +    /* Copy the reference coordinates to mtop */
 +    clear_dvec(sum);
 +    totmass = 0;
 +    a       = 0;
 +    snew(hadAtom, natoms);
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molb     = &mtop->molblock[mb];
 +        nat_molb = molb->nmol*mtop->moltype[molb->type].atoms.nr;
 +        pr       = &(molinfo[molb->type].plist[F_POSRES]);
 +        prfb     = &(molinfo[molb->type].plist[F_FBPOSRES]);
 +        if (pr->nr > 0 || prfb->nr > 0)
 +        {
 +            atom = mtop->moltype[molb->type].atoms.atom;
 +            for (i = 0; (i < pr->nr); i++)
 +            {
 +                ai = pr->param[i].AI;
 +                if (ai >= natoms)
 +                {
 +                    gmx_fatal(FARGS, "Position restraint atom index (%d) in moltype '%s' is larger than number of atoms in %s (%d).\n",
 +                              ai+1, *molinfo[molb->type].name, fn, natoms);
 +                }
 +                hadAtom[ai] = TRUE;
 +                if (rc_scaling == erscCOM)
 +                {
 +                    /* Determine the center of mass of the posres reference coordinates */
 +                    for (j = 0; j < npbcdim; j++)
 +                    {
 +                        sum[j] += atom[ai].m*x[a+ai][j];
 +                    }
 +                    totmass  += atom[ai].m;
 +                }
 +            }
 +            /* Same for flat-bottomed posres, but do not count an atom twice for COM */
 +            for (i = 0; (i < prfb->nr); i++)
 +            {
 +                ai = prfb->param[i].AI;
 +                if (ai >= natoms)
 +                {
 +                    gmx_fatal(FARGS, "Position restraint atom index (%d) in moltype '%s' is larger than number of atoms in %s (%d).\n",
 +                              ai+1, *molinfo[molb->type].name, fn, natoms);
 +                }
 +                if (rc_scaling == erscCOM && hadAtom[ai] == FALSE)
 +                {
 +                    /* Determine the center of mass of the posres reference coordinates */
 +                    for (j = 0; j < npbcdim; j++)
 +                    {
 +                        sum[j] += atom[ai].m*x[a+ai][j];
 +                    }
 +                    totmass  += atom[ai].m;
 +                }
 +            }
 +            if (!bTopB)
 +            {
 +                molb->nposres_xA = nat_molb;
 +                snew(molb->posres_xA, molb->nposres_xA);
 +                for (i = 0; i < nat_molb; i++)
 +                {
 +                    copy_rvec(x[a+i], molb->posres_xA[i]);
 +                }
 +            }
 +            else
 +            {
 +                molb->nposres_xB = nat_molb;
 +                snew(molb->posres_xB, molb->nposres_xB);
 +                for (i = 0; i < nat_molb; i++)
 +                {
 +                    copy_rvec(x[a+i], molb->posres_xB[i]);
 +                }
 +            }
 +        }
 +        a += nat_molb;
 +    }
 +    if (rc_scaling == erscCOM)
 +    {
 +        if (totmass == 0)
 +        {
 +            gmx_fatal(FARGS, "The total mass of the position restraint atoms is 0");
 +        }
 +        for (j = 0; j < npbcdim; j++)
 +        {
 +            com[j] = sum[j]/totmass;
 +        }
 +        fprintf(stderr, "The center of mass of the position restraint coord's is %6.3f %6.3f %6.3f\n", com[XX], com[YY], com[ZZ]);
 +    }
 +
 +    if (rc_scaling != erscNO)
 +    {
 +        assert(npbcdim <= DIM);
 +
 +        for (mb = 0; mb < mtop->nmolblock; mb++)
 +        {
 +            molb     = &mtop->molblock[mb];
 +            nat_molb = molb->nmol*mtop->moltype[molb->type].atoms.nr;
 +            if (molb->nposres_xA > 0 || molb->nposres_xB > 0)
 +            {
 +                xp = (!bTopB ? molb->posres_xA : molb->posres_xB);
 +                for (i = 0; i < nat_molb; i++)
 +                {
 +                    for (j = 0; j < npbcdim; j++)
 +                    {
 +                        if (rc_scaling == erscALL)
 +                        {
 +                            /* Convert from Cartesian to crystal coordinates */
 +                            xp[i][j] *= invbox[j][j];
 +                            for (k = j+1; k < npbcdim; k++)
 +                            {
 +                                xp[i][j] += invbox[k][j]*xp[i][k];
 +                            }
 +                        }
 +                        else if (rc_scaling == erscCOM)
 +                        {
 +                            /* Subtract the center of mass */
 +                            xp[i][j] -= com[j];
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        if (rc_scaling == erscCOM)
 +        {
 +            /* Convert the COM from Cartesian to crystal coordinates */
 +            for (j = 0; j < npbcdim; j++)
 +            {
 +                com[j] *= invbox[j][j];
 +                for (k = j+1; k < npbcdim; k++)
 +                {
 +                    com[j] += invbox[k][j]*com[k];
 +                }
 +            }
 +        }
 +    }
 +
 +    free_t_atoms(&dumat, TRUE);
 +    sfree(x);
 +    sfree(v);
 +    sfree(hadAtom);
 +}
 +
 +static void gen_posres(gmx_mtop_t *mtop, t_molinfo *mi,
 +                       char *fnA, char *fnB,
 +                       int rc_scaling, int ePBC,
 +                       rvec com, rvec comB,
 +                       warninp_t wi)
 +{
 +    int i, j;
 +
 +    read_posres  (mtop, mi, FALSE, fnA, rc_scaling, ePBC, com, wi);
 +    /* It is safer to simply read the b-state posres rather than trying
 +     * to be smart and copy the positions.
 +     */
 +    read_posres(mtop, mi, TRUE, fnB, rc_scaling, ePBC, comB, wi);
 +}
 +
 +static void set_wall_atomtype(gpp_atomtype_t at, t_gromppopts *opts,
 +                              t_inputrec *ir, warninp_t wi)
 +{
 +    int  i;
 +    char warn_buf[STRLEN];
 +
 +    if (ir->nwall > 0)
 +    {
 +        fprintf(stderr, "Searching the wall atom type(s)\n");
 +    }
 +    for (i = 0; i < ir->nwall; i++)
 +    {
 +        ir->wall_atomtype[i] = get_atomtype_type(opts->wall_atomtype[i], at);
 +        if (ir->wall_atomtype[i] == NOTSET)
 +        {
 +            sprintf(warn_buf, "Specified wall atom type %s is not defined", opts->wall_atomtype[i]);
 +            warning_error(wi, warn_buf);
 +        }
 +    }
 +}
 +
 +static int nrdf_internal(t_atoms *atoms)
 +{
 +    int i, nmass, nrdf;
 +
 +    nmass = 0;
 +    for (i = 0; i < atoms->nr; i++)
 +    {
 +        /* Vsite ptype might not be set here yet, so also check the mass */
 +        if ((atoms->atom[i].ptype == eptAtom ||
 +             atoms->atom[i].ptype == eptNucleus)
 +            && atoms->atom[i].m > 0)
 +        {
 +            nmass++;
 +        }
 +    }
 +    switch (nmass)
 +    {
 +        case 0:  nrdf = 0; break;
 +        case 1:  nrdf = 0; break;
 +        case 2:  nrdf = 1; break;
 +        default: nrdf = nmass*3 - 6; break;
 +    }
 +
 +    return nrdf;
 +}
 +
 +void
 +spline1d( double        dx,
 +          double *      y,
 +          int           n,
 +          double *      u,
 +          double *      y2 )
 +{
 +    int    i;
 +    double p, q;
 +
 +    y2[0] = 0.0;
 +    u[0]  = 0.0;
 +
 +    for (i = 1; i < n-1; i++)
 +    {
 +        p     = 0.5*y2[i-1]+2.0;
 +        y2[i] = -0.5/p;
 +        q     = (y[i+1]-2.0*y[i]+y[i-1])/dx;
 +        u[i]  = (3.0*q/dx-0.5*u[i-1])/p;
 +    }
 +
 +    y2[n-1] = 0.0;
 +
 +    for (i = n-2; i >= 0; i--)
 +    {
 +        y2[i] = y2[i]*y2[i+1]+u[i];
 +    }
 +}
 +
 +
 +void
 +interpolate1d( double     xmin,
 +               double     dx,
 +               double *   ya,
 +               double *   y2a,
 +               double     x,
 +               double *   y,
 +               double *   y1)
 +{
 +    int    ix;
 +    double a, b;
 +
 +    ix = (x-xmin)/dx;
 +
 +    a = (xmin+(ix+1)*dx-x)/dx;
 +    b = (x-xmin-ix*dx)/dx;
 +
 +    *y  = a*ya[ix]+b*ya[ix+1]+((a*a*a-a)*y2a[ix]+(b*b*b-b)*y2a[ix+1])*(dx*dx)/6.0;
 +    *y1 = (ya[ix+1]-ya[ix])/dx-(3.0*a*a-1.0)/6.0*dx*y2a[ix]+(3.0*b*b-1.0)/6.0*dx*y2a[ix+1];
 +}
 +
 +
 +void
 +setup_cmap (int              grid_spacing,
 +            int              nc,
 +            real *           grid,
 +            gmx_cmap_t *     cmap_grid)
 +{
 +    double *tmp_u, *tmp_u2, *tmp_yy, *tmp_y1, *tmp_t2, *tmp_grid;
 +
 +    int     i, j, k, ii, jj, kk, idx;
 +    int     offset;
 +    double  dx, xmin, v, v1, v2, v12;
 +    double  phi, psi;
 +
 +    snew(tmp_u, 2*grid_spacing);
 +    snew(tmp_u2, 2*grid_spacing);
 +    snew(tmp_yy, 2*grid_spacing);
 +    snew(tmp_y1, 2*grid_spacing);
 +    snew(tmp_t2, 2*grid_spacing*2*grid_spacing);
 +    snew(tmp_grid, 2*grid_spacing*2*grid_spacing);
 +
 +    dx   = 360.0/grid_spacing;
 +    xmin = -180.0-dx*grid_spacing/2;
 +
 +    for (kk = 0; kk < nc; kk++)
 +    {
 +        /* Compute an offset depending on which cmap we are using
 +         * Offset will be the map number multiplied with the
 +         * grid_spacing * grid_spacing * 2
 +         */
 +        offset = kk * grid_spacing * grid_spacing * 2;
 +
 +        for (i = 0; i < 2*grid_spacing; i++)
 +        {
 +            ii = (i+grid_spacing-grid_spacing/2)%grid_spacing;
 +
 +            for (j = 0; j < 2*grid_spacing; j++)
 +            {
 +                jj = (j+grid_spacing-grid_spacing/2)%grid_spacing;
 +                tmp_grid[i*grid_spacing*2+j] = grid[offset+ii*grid_spacing+jj];
 +            }
 +        }
 +
 +        for (i = 0; i < 2*grid_spacing; i++)
 +        {
 +            spline1d(dx, &(tmp_grid[2*grid_spacing*i]), 2*grid_spacing, tmp_u, &(tmp_t2[2*grid_spacing*i]));
 +        }
 +
 +        for (i = grid_spacing/2; i < grid_spacing+grid_spacing/2; i++)
 +        {
 +            ii  = i-grid_spacing/2;
 +            phi = ii*dx-180.0;
 +
 +            for (j = grid_spacing/2; j < grid_spacing+grid_spacing/2; j++)
 +            {
 +                jj  = j-grid_spacing/2;
 +                psi = jj*dx-180.0;
 +
 +                for (k = 0; k < 2*grid_spacing; k++)
 +                {
 +                    interpolate1d(xmin, dx, &(tmp_grid[2*grid_spacing*k]),
 +                                  &(tmp_t2[2*grid_spacing*k]), psi, &tmp_yy[k], &tmp_y1[k]);
 +                }
 +
 +                spline1d(dx, tmp_yy, 2*grid_spacing, tmp_u, tmp_u2);
 +                interpolate1d(xmin, dx, tmp_yy, tmp_u2, phi, &v, &v1);
 +                spline1d(dx, tmp_y1, 2*grid_spacing, tmp_u, tmp_u2);
 +                interpolate1d(xmin, dx, tmp_y1, tmp_u2, phi, &v2, &v12);
 +
 +                idx = ii*grid_spacing+jj;
 +                cmap_grid->cmapdata[kk].cmap[idx*4]   = grid[offset+ii*grid_spacing+jj];
 +                cmap_grid->cmapdata[kk].cmap[idx*4+1] = v1;
 +                cmap_grid->cmapdata[kk].cmap[idx*4+2] = v2;
 +                cmap_grid->cmapdata[kk].cmap[idx*4+3] = v12;
 +            }
 +        }
 +    }
 +}
 +
 +void init_cmap_grid(gmx_cmap_t *cmap_grid, int ngrid, int grid_spacing)
 +{
 +    int i, k, nelem;
 +
 +    cmap_grid->ngrid        = ngrid;
 +    cmap_grid->grid_spacing = grid_spacing;
 +    nelem                   = cmap_grid->grid_spacing*cmap_grid->grid_spacing;
 +
 +    snew(cmap_grid->cmapdata, ngrid);
 +
 +    for (i = 0; i < cmap_grid->ngrid; i++)
 +    {
 +        snew(cmap_grid->cmapdata[i].cmap, 4*nelem);
 +    }
 +}
 +
 +
 +static int count_constraints(gmx_mtop_t *mtop, t_molinfo *mi, warninp_t wi)
 +{
 +    int             count, count_mol, i, mb;
 +    gmx_molblock_t *molb;
 +    t_params       *plist;
 +    char            buf[STRLEN];
 +
 +    count = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        count_mol = 0;
 +        molb      = &mtop->molblock[mb];
 +        plist     = mi[molb->type].plist;
 +
 +        for (i = 0; i < F_NRE; i++)
 +        {
 +            if (i == F_SETTLE)
 +            {
 +                count_mol += 3*plist[i].nr;
 +            }
 +            else if (interaction_function[i].flags & IF_CONSTRAINT)
 +            {
 +                count_mol += plist[i].nr;
 +            }
 +        }
 +
 +        if (count_mol > nrdf_internal(&mi[molb->type].atoms))
 +        {
 +            sprintf(buf,
 +                    "Molecule type '%s' has %d constraints.\n"
 +                    "For stability and efficiency there should not be more constraints than internal number of degrees of freedom: %d.\n",
 +                    *mi[molb->type].name, count_mol,
 +                    nrdf_internal(&mi[molb->type].atoms));
 +            warning(wi, buf);
 +        }
 +        count += molb->nmol*count_mol;
 +    }
 +
 +    return count;
 +}
 +
 +static void check_gbsa_params_charged(gmx_mtop_t *sys, gpp_atomtype_t atype)
 +{
 +    int            i, nmiss, natoms, mt;
 +    real           q;
 +    const t_atoms *atoms;
 +
 +    nmiss = 0;
 +    for (mt = 0; mt < sys->nmoltype; mt++)
 +    {
 +        atoms  = &sys->moltype[mt].atoms;
 +        natoms = atoms->nr;
 +
 +        for (i = 0; i < natoms; i++)
 +        {
 +            q = atoms->atom[i].q;
 +            if ((get_atomtype_radius(atoms->atom[i].type, atype)    == 0  ||
 +                 get_atomtype_vol(atoms->atom[i].type, atype)       == 0  ||
 +                 get_atomtype_surftens(atoms->atom[i].type, atype)  == 0  ||
 +                 get_atomtype_gb_radius(atoms->atom[i].type, atype) == 0  ||
 +                 get_atomtype_S_hct(atoms->atom[i].type, atype)     == 0) &&
 +                q != 0)
 +            {
 +                fprintf(stderr, "\nGB parameter(s) zero for atom type '%s' while charge is %g\n",
 +                        get_atomtype_name(atoms->atom[i].type, atype), q);
 +                nmiss++;
 +            }
 +        }
 +    }
 +
 +    if (nmiss > 0)
 +    {
 +        gmx_fatal(FARGS, "Can't do GB electrostatics; the implicit_genborn_params section of the forcefield has parameters with value zero for %d atomtypes that occur as charged atoms.", nmiss);
 +    }
 +}
 +
 +
 +static void check_gbsa_params(gpp_atomtype_t atype)
 +{
 +    int  nmiss, i;
 +
 +    /* If we are doing GBSA, check that we got the parameters we need
 +     * This checking is to see if there are GBSA paratmeters for all
 +     * atoms in the force field. To go around this for testing purposes
 +     * comment out the nerror++ counter temporarily
 +     */
 +    nmiss = 0;
 +    for (i = 0; i < get_atomtype_ntypes(atype); i++)
 +    {
 +        if (get_atomtype_radius(i, atype)    < 0 ||
 +            get_atomtype_vol(i, atype)       < 0 ||
 +            get_atomtype_surftens(i, atype)  < 0 ||
 +            get_atomtype_gb_radius(i, atype) < 0 ||
 +            get_atomtype_S_hct(i, atype)     < 0)
 +        {
 +            fprintf(stderr, "\nGB parameter(s) missing or negative for atom type '%s'\n",
 +                    get_atomtype_name(i, atype));
 +            nmiss++;
 +        }
 +    }
 +
 +    if (nmiss > 0)
 +    {
 +        gmx_fatal(FARGS, "Can't do GB electrostatics; the implicit_genborn_params section of the forcefield is missing parameters for %d atomtypes or they might be negative.", nmiss);
 +    }
 +
 +}
 +
 +static real calc_temp(const gmx_mtop_t *mtop,
 +                      const t_inputrec *ir,
 +                      rvec             *v)
 +{
 +    double                  sum_mv2;
 +    gmx_mtop_atomloop_all_t aloop;
 +    t_atom                 *atom;
 +    int                     a;
 +    int                     nrdf, g;
 +
 +    sum_mv2 = 0;
 +
 +    aloop = gmx_mtop_atomloop_all_init(mtop);
 +    while (gmx_mtop_atomloop_all_next(aloop, &a, &atom))
 +    {
 +        sum_mv2 += atom->m*norm2(v[a]);
 +    }
 +
 +    nrdf = 0;
 +    for (g = 0; g < ir->opts.ngtc; g++)
 +    {
 +        nrdf += ir->opts.nrdf[g];
 +    }
 +
 +    return sum_mv2/(nrdf*BOLTZ);
 +}
 +
 +static real get_max_reference_temp(const t_inputrec *ir,
 +                                   warninp_t         wi)
 +{
 +    real     ref_t;
 +    int      i;
 +    gmx_bool bNoCoupl;
 +
 +    ref_t    = 0;
 +    bNoCoupl = FALSE;
 +    for (i = 0; i < ir->opts.ngtc; i++)
 +    {
 +        if (ir->opts.tau_t[i] < 0)
 +        {
 +            bNoCoupl = TRUE;
 +        }
 +        else
 +        {
 +            ref_t = max(ref_t, ir->opts.ref_t[i]);
 +        }
 +    }
 +
 +    if (bNoCoupl)
 +    {
 +        char buf[STRLEN];
 +
 +        sprintf(buf, "Some temperature coupling groups do not use temperature coupling. We will assume their temperature is not more than %.3f K. If their temperature is higher, the energy error and the Verlet buffer might be underestimated.",
 +                ref_t);
 +        warning(wi, buf);
 +    }
 +
 +    return ref_t;
 +}
 +
 +static void set_verlet_buffer(const gmx_mtop_t *mtop,
 +                              t_inputrec       *ir,
 +                              real              buffer_temp,
 +                              matrix            box,
 +                              warninp_t         wi)
 +{
 +    int                    i;
 +    verletbuf_list_setup_t ls;
 +    real                   rlist_1x1;
 +    int                    n_nonlin_vsite;
 +    char                   warn_buf[STRLEN];
 +
 +    printf("Determining Verlet buffer for a tolerance of %g kJ/mol/ps at %g K\n", ir->verletbuf_tol, buffer_temp);
 +
 +    /* Calculate the buffer size for simple atom vs atoms list */
 +    ls.cluster_size_i = 1;
 +    ls.cluster_size_j = 1;
 +    calc_verlet_buffer_size(mtop, det(box), ir, buffer_temp,
 +                            &ls, &n_nonlin_vsite, &rlist_1x1);
 +
 +    /* Set the pair-list buffer size in ir */
 +    verletbuf_get_list_setup(FALSE, &ls);
 +    calc_verlet_buffer_size(mtop, det(box), ir, buffer_temp,
 +                            &ls, &n_nonlin_vsite, &ir->rlist);
 +
 +    if (n_nonlin_vsite > 0)
 +    {
 +        sprintf(warn_buf, "There are %d non-linear virtual site constructions. Their contribution to the energy error is approximated. In most cases this does not affect the error significantly.", n_nonlin_vsite);
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    printf("Calculated rlist for %dx%d atom pair-list as %.3f nm, buffer size %.3f nm\n",
 +           1, 1, rlist_1x1, rlist_1x1-max(ir->rvdw, ir->rcoulomb));
 +
 +    ir->rlistlong = ir->rlist;
 +    printf("Set rlist, assuming %dx%d atom pair-list, to %.3f nm, buffer size %.3f nm\n",
 +           ls.cluster_size_i, ls.cluster_size_j,
 +           ir->rlist, ir->rlist-max(ir->rvdw, ir->rcoulomb));
 +
 +    if (sqr(ir->rlistlong) >= max_cutoff2(ir->ePBC, box))
 +    {
 +        gmx_fatal(FARGS, "The pair-list cut-off (%g nm) is longer than half the shortest box vector or longer than the smallest box diagonal element (%g nm). Increase the box size or decrease nstlist or increase verlet-buffer-tolerance.", ir->rlistlong, sqrt(max_cutoff2(ir->ePBC, box)));
 +    }
 +}
 +
 +int gmx_grompp(int argc, char *argv[])
 +{
 +    static const char *desc[] = {
 +        "[THISMODULE] (the gromacs preprocessor)",
 +        "reads a molecular topology file, checks the validity of the",
 +        "file, expands the topology from a molecular description to an atomic",
 +        "description. The topology file contains information about",
 +        "molecule types and the number of molecules, the preprocessor",
 +        "copies each molecule as needed. ",
 +        "There is no limitation on the number of molecule types. ",
 +        "Bonds and bond-angles can be converted into constraints, separately",
 +        "for hydrogens and heavy atoms.",
 +        "Then a coordinate file is read and velocities can be generated",
 +        "from a Maxwellian distribution if requested.",
 +        "[THISMODULE] also reads parameters for [gmx-mdrun] ",
 +        "(eg. number of MD steps, time step, cut-off), and others such as",
 +        "NEMD parameters, which are corrected so that the net acceleration",
 +        "is zero.",
 +        "Eventually a binary file is produced that can serve as the sole input",
 +        "file for the MD program.[PAR]",
 +
 +        "[THISMODULE] uses the atom names from the topology file. The atom names",
 +        "in the coordinate file (option [TT]-c[tt]) are only read to generate",
 +        "warnings when they do not match the atom names in the topology.",
 +        "Note that the atom names are irrelevant for the simulation as",
 +        "only the atom types are used for generating interaction parameters.[PAR]",
 +
 +        "[THISMODULE] uses a built-in preprocessor to resolve includes, macros, ",
 +        "etc. The preprocessor supports the following keywords:[PAR]",
 +        "#ifdef VARIABLE[BR]",
 +        "#ifndef VARIABLE[BR]",
 +        "#else[BR]",
 +        "#endif[BR]",
 +        "#define VARIABLE[BR]",
 +        "#undef VARIABLE[BR]"
 +        "#include \"filename\"[BR]",
 +        "#include <filename>[PAR]",
 +        "The functioning of these statements in your topology may be modulated by",
 +        "using the following two flags in your [TT].mdp[tt] file:[PAR]",
 +        "[TT]define = -DVARIABLE1 -DVARIABLE2[BR]",
 +        "include = -I/home/john/doe[tt][BR]",
 +        "For further information a C-programming textbook may help you out.",
 +        "Specifying the [TT]-pp[tt] flag will get the pre-processed",
 +        "topology file written out so that you can verify its contents.[PAR]",
 +
 +        /* cpp has been unnecessary for some time, hasn't it?
 +            "If your system does not have a C-preprocessor, you can still",
 +            "use [TT]grompp[tt], but you do not have access to the features ",
 +            "from the cpp. Command line options to the C-preprocessor can be given",
 +            "in the [TT].mdp[tt] file. See your local manual (man cpp).[PAR]",
 +         */
 +
 +        "When using position restraints a file with restraint coordinates",
 +        "can be supplied with [TT]-r[tt], otherwise restraining will be done",
 +        "with respect to the conformation from the [TT]-c[tt] option.",
 +        "For free energy calculation the the coordinates for the B topology",
 +        "can be supplied with [TT]-rb[tt], otherwise they will be equal to",
 +        "those of the A topology.[PAR]",
 +
 +        "Starting coordinates can be read from trajectory with [TT]-t[tt].",
 +        "The last frame with coordinates and velocities will be read,",
 +        "unless the [TT]-time[tt] option is used. Only if this information",
 +        "is absent will the coordinates in the [TT]-c[tt] file be used.",
 +        "Note that these velocities will not be used when [TT]gen_vel = yes[tt]",
 +        "in your [TT].mdp[tt] file. An energy file can be supplied with",
 +        "[TT]-e[tt] to read Nose-Hoover and/or Parrinello-Rahman coupling",
 +        "variables.[PAR]",
 +
 +        "[THISMODULE] can be used to restart simulations (preserving",
 +        "continuity) by supplying just a checkpoint file with [TT]-t[tt].",
 +        "However, for simply changing the number of run steps to extend",
 +        "a run, using [gmx-convert-tpr] is more convenient than [THISMODULE].",
 +        "You then supply the old checkpoint file directly to [gmx-mdrun]",
 +        "with [TT]-cpi[tt]. If you wish to change the ensemble or things",
 +        "like output frequency, then supplying the checkpoint file to",
 +        "[THISMODULE] with [TT]-t[tt] along with a new [TT].mdp[tt] file",
 +        "with [TT]-f[tt] is the recommended procedure.[PAR]",
 +
 +        "By default, all bonded interactions which have constant energy due to",
 +        "virtual site constructions will be removed. If this constant energy is",
 +        "not zero, this will result in a shift in the total energy. All bonded",
 +        "interactions can be kept by turning off [TT]-rmvsbds[tt]. Additionally,",
 +        "all constraints for distances which will be constant anyway because",
 +        "of virtual site constructions will be removed. If any constraints remain",
 +        "which involve virtual sites, a fatal error will result.[PAR]"
 +
 +        "To verify your run input file, please take note of all warnings",
 +        "on the screen, and correct where necessary. Do also look at the contents",
 +        "of the [TT]mdout.mdp[tt] file; this contains comment lines, as well as",
 +        "the input that [THISMODULE] has read. If in doubt, you can start [THISMODULE]",
 +        "with the [TT]-debug[tt] option which will give you more information",
 +        "in a file called [TT]grompp.log[tt] (along with real debug info). You",
 +        "can see the contents of the run input file with the [gmx-dump]",
 +        "program. [gmx-check] can be used to compare the contents of two",
 +        "run input files.[PAR]"
 +
 +        "The [TT]-maxwarn[tt] option can be used to override warnings printed",
 +        "by [THISMODULE] that otherwise halt output. In some cases, warnings are",
 +        "harmless, but usually they are not. The user is advised to carefully",
 +        "interpret the output messages before attempting to bypass them with",
 +        "this option."
 +    };
 +    t_gromppopts      *opts;
 +    gmx_mtop_t        *sys;
 +    int                nmi;
 +    t_molinfo         *mi;
 +    gpp_atomtype_t     atype;
 +    t_inputrec        *ir;
 +    int                natoms, nvsite, comb, mt;
 +    t_params          *plist;
 +    t_state            state;
 +    matrix             box;
 +    real               max_spacing, fudgeQQ;
 +    double             reppow;
 +    char               fn[STRLEN], fnB[STRLEN];
 +    const char        *mdparin;
 +    int                ntype;
 +    gmx_bool           bNeedVel, bGenVel;
 +    gmx_bool           have_atomnumber;
 +    int                n12, n13, n14;
 +    t_params          *gb_plist = NULL;
 +    gmx_genborn_t     *born     = NULL;
 +    output_env_t       oenv;
 +    gmx_bool           bVerbose = FALSE;
 +    warninp_t          wi;
 +    char               warn_buf[STRLEN];
 +    unsigned int       useed;
 +    t_atoms            IMDatoms;   /* Atoms to be operated on interactively (IMD) */
 +
 +    t_filenm           fnm[] = {
 +        { efMDP, NULL,  NULL,        ffREAD  },
 +        { efMDP, "-po", "mdout",     ffWRITE },
 +        { efSTX, "-c",  NULL,        ffREAD  },
 +        { efSTX, "-r",  NULL,        ffOPTRD },
 +        { efSTX, "-rb", NULL,        ffOPTRD },
 +        { efNDX, NULL,  NULL,        ffOPTRD },
 +        { efTOP, NULL,  NULL,        ffREAD  },
 +        { efTOP, "-pp", "processed", ffOPTWR },
 +        { efTPX, "-o",  NULL,        ffWRITE },
 +        { efTRN, "-t",  NULL,        ffOPTRD },
 +        { efEDR, "-e",  NULL,        ffOPTRD },
 +        /* This group is needed by the VMD viewer as the start configuration for IMD sessions: */
 +        { efGRO, "-imd", "imdgroup", ffOPTWR },
 +        { efTRN, "-ref", "rotref",   ffOPTRW }
 +    };
 +#define NFILE asize(fnm)
 +
 +    /* Command line options */
 +    static gmx_bool bRenum   = TRUE;
 +    static gmx_bool bRmVSBds = TRUE, bZero = FALSE;
 +    static int      i, maxwarn = 0;
 +    static real     fr_time = -1;
 +    t_pargs         pa[]    = {
 +        { "-v",       FALSE, etBOOL, {&bVerbose},
 +          "Be loud and noisy" },
 +        { "-time",    FALSE, etREAL, {&fr_time},
 +          "Take frame at or first after this time." },
 +        { "-rmvsbds", FALSE, etBOOL, {&bRmVSBds},
 +          "Remove constant bonded interactions with virtual sites" },
 +        { "-maxwarn", FALSE, etINT,  {&maxwarn},
 +          "Number of allowed warnings during input processing. Not for normal use and may generate unstable systems" },
 +        { "-zero",    FALSE, etBOOL, {&bZero},
 +          "Set parameters for bonded interactions without defaults to zero instead of generating an error" },
 +        { "-renum",   FALSE, etBOOL, {&bRenum},
 +          "Renumber atomtypes and minimize number of atomtypes" }
 +    };
 +
 +    /* Initiate some variables */
 +    snew(ir, 1);
 +    snew(opts, 1);
 +    init_ir(ir, opts);
 +
 +    /* Parse the command line */
 +    if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa,
 +                           asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    wi = init_warning(TRUE, maxwarn);
 +
 +    /* PARAMETER file processing */
 +    mdparin = opt2fn("-f", NFILE, fnm);
 +    set_warning_line(wi, mdparin, -1);
 +    get_ir(mdparin, opt2fn("-po", NFILE, fnm), ir, opts, wi);
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "checking input for internal consistency...\n");
 +    }
 +    check_ir(mdparin, ir, opts, wi);
 +
 +    if (ir->ld_seed == -1)
 +    {
 +        ir->ld_seed = (gmx_int64_t)gmx_rng_make_seed();
 +        fprintf(stderr, "Setting the LD random seed to %"GMX_PRId64 "\n", ir->ld_seed);
 +    }
 +
 +    if (ir->expandedvals->lmc_seed == -1)
 +    {
 +        ir->expandedvals->lmc_seed = (int)gmx_rng_make_seed();
 +        fprintf(stderr, "Setting the lambda MC random seed to %d\n", ir->expandedvals->lmc_seed);
 +    }
 +
 +    bNeedVel = EI_STATE_VELOCITY(ir->eI);
 +    bGenVel  = (bNeedVel && opts->bGenVel);
 +    if (bGenVel && ir->bContinuation)
 +    {
 +        sprintf(warn_buf,
 +                "Generating velocities is inconsistent with attempting "
 +                "to continue a previous run. Choose only one of "
 +                "gen-vel = yes and continuation = yes.");
 +        warning_error(wi, warn_buf);
 +    }
 +
 +    snew(plist, F_NRE);
 +    init_plist(plist);
 +    snew(sys, 1);
 +    atype = init_atomtype();
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "Just opened", &sys->symtab);
 +    }
 +
 +    strcpy(fn, ftp2fn(efTOP, NFILE, fnm));
 +    if (!gmx_fexist(fn))
 +    {
 +        gmx_fatal(FARGS, "%s does not exist", fn);
 +    }
 +    new_status(fn, opt2fn_null("-pp", NFILE, fnm), opt2fn("-c", NFILE, fnm),
 +               opts, ir, bZero, bGenVel, bVerbose, &state,
 +               atype, sys, &nmi, &mi, plist, &comb, &reppow, &fudgeQQ,
 +               opts->bMorse,
 +               wi);
 +
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After new_status", &sys->symtab);
 +    }
 +
 +    nvsite = 0;
 +    /* set parameters for virtual site construction (not for vsiten) */
 +    for (mt = 0; mt < sys->nmoltype; mt++)
 +    {
 +        nvsite +=
 +            set_vsites(bVerbose, &sys->moltype[mt].atoms, atype, mi[mt].plist);
 +    }
 +    /* now throw away all obsolete bonds, angles and dihedrals: */
 +    /* note: constraints are ALWAYS removed */
 +    if (nvsite)
 +    {
 +        for (mt = 0; mt < sys->nmoltype; mt++)
 +        {
 +            clean_vsite_bondeds(mi[mt].plist, sys->moltype[mt].atoms.nr, bRmVSBds);
 +        }
 +    }
 +
 +    if (nvsite && ir->eI == eiNM)
 +    {
 +        gmx_fatal(FARGS, "Normal Mode analysis is not supported with virtual sites.\nIf you'd like to help with adding support, we have an open discussion at http://redmine.gromacs.org/issues/879\n");
 +    }
 +
 +    if (ir->cutoff_scheme == ecutsVERLET)
 +    {
 +        fprintf(stderr, "Removing all charge groups because cutoff-scheme=%s\n",
 +                ecutscheme_names[ir->cutoff_scheme]);
 +
 +        /* Remove all charge groups */
 +        gmx_mtop_remove_chargegroups(sys);
 +    }
 +
 +    if (count_constraints(sys, mi, wi) && (ir->eConstrAlg == econtSHAKE))
 +    {
 +        if (ir->eI == eiCG || ir->eI == eiLBFGS)
 +        {
 +            sprintf(warn_buf, "Can not do %s with %s, use %s",
 +                    EI(ir->eI), econstr_names[econtSHAKE], econstr_names[econtLINCS]);
 +            warning_error(wi, warn_buf);
 +        }
 +        if (ir->bPeriodicMols)
 +        {
 +            sprintf(warn_buf, "Can not do periodic molecules with %s, use %s",
 +                    econstr_names[econtSHAKE], econstr_names[econtLINCS]);
 +            warning_error(wi, warn_buf);
 +        }
 +    }
 +
 +    if (EI_SD (ir->eI) &&  ir->etc != etcNO)
 +    {
 +        warning_note(wi, "Temperature coupling is ignored with SD integrators.");
 +    }
 +
 +    /* If we are doing QM/MM, check that we got the atom numbers */
 +    have_atomnumber = TRUE;
 +    for (i = 0; i < get_atomtype_ntypes(atype); i++)
 +    {
 +        have_atomnumber = have_atomnumber && (get_atomtype_atomnumber(i, atype) >= 0);
 +    }
 +    if (!have_atomnumber && ir->bQMMM)
 +    {
 +        warning_error(wi,
 +                      "\n"
 +                      "It appears as if you are trying to run a QM/MM calculation, but the force\n"
 +                      "field you are using does not contain atom numbers fields. This is an\n"
 +                      "optional field (introduced in Gromacs 3.3) for general runs, but mandatory\n"
 +                      "for QM/MM. The good news is that it is easy to add - put the atom number as\n"
 +                      "an integer just before the mass column in ffXXXnb.itp.\n"
 +                      "NB: United atoms have the same atom numbers as normal ones.\n\n");
 +    }
 +
 +    if (ir->bAdress)
 +    {
 +        if ((ir->adress->const_wf > 1) || (ir->adress->const_wf < 0))
 +        {
 +            warning_error(wi, "AdResS contant weighting function should be between 0 and 1\n\n");
 +        }
 +        /** TODO check size of ex+hy width against box size */
 +    }
 +
 +    /* Check for errors in the input now, since they might cause problems
 +     * during processing further down.
 +     */
 +    check_warning_error(wi, FARGS);
 +
 +    if (opt2bSet("-r", NFILE, fnm))
 +    {
 +        sprintf(fn, "%s", opt2fn("-r", NFILE, fnm));
 +    }
 +    else
 +    {
 +        sprintf(fn, "%s", opt2fn("-c", NFILE, fnm));
 +    }
 +    if (opt2bSet("-rb", NFILE, fnm))
 +    {
 +        sprintf(fnB, "%s", opt2fn("-rb", NFILE, fnm));
 +    }
 +    else
 +    {
 +        strcpy(fnB, fn);
 +    }
 +
 +    if (nint_ftype(sys, mi, F_POSRES) > 0 || nint_ftype(sys, mi, F_FBPOSRES) > 0)
 +    {
 +        if (bVerbose)
 +        {
 +            fprintf(stderr, "Reading position restraint coords from %s", fn);
 +            if (strcmp(fn, fnB) == 0)
 +            {
 +                fprintf(stderr, "\n");
 +            }
 +            else
 +            {
 +                fprintf(stderr, " and %s\n", fnB);
 +            }
 +        }
 +        gen_posres(sys, mi, fn, fnB,
 +                   ir->refcoord_scaling, ir->ePBC,
 +                   ir->posres_com, ir->posres_comB,
 +                   wi);
 +    }
 +
 +    /* If we are using CMAP, setup the pre-interpolation grid */
 +    if (plist[F_CMAP].ncmap > 0)
 +    {
 +        init_cmap_grid(&sys->ffparams.cmap_grid, plist[F_CMAP].nc, plist[F_CMAP].grid_spacing);
 +        setup_cmap(plist[F_CMAP].grid_spacing, plist[F_CMAP].nc, plist[F_CMAP].cmap, &sys->ffparams.cmap_grid);
 +    }
 +
 +    set_wall_atomtype(atype, opts, ir, wi);
 +    if (bRenum)
 +    {
 +        renum_atype(plist, sys, ir->wall_atomtype, atype, bVerbose);
 +        ntype = get_atomtype_ntypes(atype);
 +    }
 +
 +    if (ir->implicit_solvent != eisNO)
 +    {
 +        /* Now we have renumbered the atom types, we can check the GBSA params */
 +        check_gbsa_params(atype);
 +
 +        /* Check that all atoms that have charge and/or LJ-parameters also have
 +         * sensible GB-parameters
 +         */
 +        check_gbsa_params_charged(sys, atype);
 +    }
 +
 +    /* PELA: Copy the atomtype data to the topology atomtype list */
 +    copy_atomtype_atomtypes(atype, &(sys->atomtypes));
 +
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After renum_atype", &sys->symtab);
 +    }
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "converting bonded parameters...\n");
 +    }
 +
 +    ntype = get_atomtype_ntypes(atype);
 +    convert_params(ntype, plist, mi, comb, reppow, fudgeQQ, sys);
 +
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After convert_params", &sys->symtab);
 +    }
 +
 +    /* set ptype to VSite for virtual sites */
 +    for (mt = 0; mt < sys->nmoltype; mt++)
 +    {
 +        set_vsites_ptype(FALSE, &sys->moltype[mt]);
 +    }
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After virtual sites", &sys->symtab);
 +    }
 +    /* Check velocity for virtual sites and shells */
 +    if (bGenVel)
 +    {
 +        check_vel(sys, state.v);
 +    }
 +
++    /* check for shells and inpurecs */
++    check_shells_inputrec(sys, ir, wi);
++
 +    /* check masses */
 +    check_mol(sys, wi);
 +
 +    for (i = 0; i < sys->nmoltype; i++)
 +    {
 +        check_cg_sizes(ftp2fn(efTOP, NFILE, fnm), &sys->moltype[i].cgs, wi);
 +    }
 +
 +    if (EI_DYNAMICS(ir->eI) && ir->eI != eiBD)
 +    {
 +        check_bonds_timestep(sys, ir->delta_t, wi);
 +    }
 +
 +    if (EI_ENERGY_MINIMIZATION(ir->eI) && 0 == ir->nsteps)
 +    {
 +        warning_note(wi, "Zero-step energy minimization will alter the coordinates before calculating the energy. If you just want the energy of a single point, try zero-step MD (with unconstrained_start = yes). To do multiple single-point energy evaluations of different configurations of the same topology, use mdrun -rerun.");
 +    }
 +
 +    check_warning_error(wi, FARGS);
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "initialising group options...\n");
 +    }
 +    do_index(mdparin, ftp2fn_null(efNDX, NFILE, fnm),
 +             sys, bVerbose, ir,
 +             bGenVel ? state.v : NULL,
 +             wi);
 +
 +    if (ir->cutoff_scheme == ecutsVERLET && ir->verletbuf_tol > 0 &&
 +        ir->nstlist > 1)
 +    {
 +        if (EI_DYNAMICS(ir->eI) && inputrec2nboundeddim(ir) == 3)
 +        {
 +            real buffer_temp;
 +
 +            if (EI_MD(ir->eI) && ir->etc == etcNO)
 +            {
 +                if (bGenVel)
 +                {
 +                    buffer_temp = opts->tempi;
 +                }
 +                else
 +                {
 +                    buffer_temp = calc_temp(sys, ir, state.v);
 +                }
 +                if (buffer_temp > 0)
 +                {
 +                    sprintf(warn_buf, "NVE simulation: will use the initial temperature of %.3f K for determining the Verlet buffer size", buffer_temp);
 +                    warning_note(wi, warn_buf);
 +                }
 +                else
 +                {
 +                    sprintf(warn_buf, "NVE simulation with an initial temperature of zero: will use a Verlet buffer of %d%%. Check your energy drift!",
 +                            (int)(verlet_buffer_ratio_NVE_T0*100 + 0.5));
 +                    warning_note(wi, warn_buf);
 +                }
 +            }
 +            else
 +            {
 +                buffer_temp = get_max_reference_temp(ir, wi);
 +            }
 +
 +            if (EI_MD(ir->eI) && ir->etc == etcNO && buffer_temp == 0)
 +            {
 +                /* NVE with initial T=0: we add a fixed ratio to rlist.
 +                 * Since we don't actually use verletbuf_tol, we set it to -1
 +                 * so it can't be misused later.
 +                 */
 +                ir->rlist         *= 1.0 + verlet_buffer_ratio_NVE_T0;
 +                ir->verletbuf_tol  = -1;
 +            }
 +            else
 +            {
 +                /* We warn for NVE simulations with >1(.1)% drift tolerance */
 +                const real drift_tol = 0.01;
 +                real       ener_runtime;
 +
 +                /* We use 2 DOF per atom = 2kT pot+kin energy, to be on
 +                 * the safe side with constraints (without constraints: 3 DOF).
 +                 */
 +                ener_runtime = 2*BOLTZ*buffer_temp/(ir->nsteps*ir->delta_t);
 +
 +                if (EI_MD(ir->eI) && ir->etc == etcNO && ir->nstlist > 1 &&
 +                    ir->nsteps > 0 &&
 +                    ir->verletbuf_tol > 1.1*drift_tol*ener_runtime)
 +                {
 +                    sprintf(warn_buf, "You are using a Verlet buffer tolerance of %g kJ/mol/ps for an NVE simulation of length %g ps, which can give a final drift of %d%%. For conserving energy to %d%%, you might need to set verlet-buffer-tolerance to %.1e.",
 +                            ir->verletbuf_tol, ir->nsteps*ir->delta_t,
 +                            (int)(ir->verletbuf_tol/ener_runtime*100 + 0.5),
 +                            (int)(100*drift_tol + 0.5),
 +                            drift_tol*ener_runtime);
 +                    warning_note(wi, warn_buf);
 +                }
 +
 +                set_verlet_buffer(sys, ir, buffer_temp, state.box, wi);
 +            }
 +        }
 +    }
 +
 +    /* Init the temperature coupling state */
 +    init_gtc_state(&state, ir->opts.ngtc, 0, ir->opts.nhchainlength); /* need to add nnhpres here? */
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "Checking consistency between energy and charge groups...\n");
 +    }
 +    check_eg_vs_cg(sys);
 +
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After index", &sys->symtab);
 +    }
 +
 +    triple_check(mdparin, ir, sys, wi);
 +    close_symtab(&sys->symtab);
 +    if (debug)
 +    {
 +        pr_symtab(debug, 0, "After close", &sys->symtab);
 +    }
 +
 +    /* make exclusions between QM atoms */
 +    if (ir->bQMMM)
 +    {
 +        if (ir->QMMMscheme == eQMMMschemenormal && ir->ns_type == ensSIMPLE)
 +        {
 +            gmx_fatal(FARGS, "electrostatic embedding only works with grid neighboursearching, use ns-type=grid instead\n");
 +        }
 +        else
 +        {
 +            generate_qmexcl(sys, ir, wi);
 +        }
 +    }
 +
 +    if (ftp2bSet(efTRN, NFILE, fnm))
 +    {
 +        if (bVerbose)
 +        {
 +            fprintf(stderr, "getting data from old trajectory ...\n");
 +        }
 +        cont_status(ftp2fn(efTRN, NFILE, fnm), ftp2fn_null(efEDR, NFILE, fnm),
 +                    bNeedVel, bGenVel, fr_time, ir, &state, sys, oenv);
 +    }
 +
 +    if (ir->ePBC == epbcXY && ir->nwall != 2)
 +    {
 +        clear_rvec(state.box[ZZ]);
 +    }
 +
 +    if (ir->cutoff_scheme != ecutsVERLET && ir->rlist > 0)
 +    {
 +        set_warning_line(wi, mdparin, -1);
 +        check_chargegroup_radii(sys, ir, state.x, wi);
 +    }
 +
 +    if (EEL_FULL(ir->coulombtype) || EVDW_PME(ir->vdwtype))
 +    {
 +        /* Calculate the optimal grid dimensions */
 +        copy_mat(state.box, box);
 +        if (ir->ePBC == epbcXY && ir->nwall == 2)
 +        {
 +            svmul(ir->wall_ewald_zfac, box[ZZ], box[ZZ]);
 +        }
 +        if (ir->nkx > 0 && ir->nky > 0 && ir->nkz > 0)
 +        {
 +            /* Mark fourier_spacing as not used */
 +            ir->fourier_spacing = 0;
 +        }
 +        else if (ir->nkx != 0 && ir->nky != 0 && ir->nkz != 0)
 +        {
 +            set_warning_line(wi, mdparin, -1);
 +            warning_error(wi, "Some of the Fourier grid sizes are set, but all of them need to be set.");
 +        }
 +        max_spacing = calc_grid(stdout, box, ir->fourier_spacing,
 +                                &(ir->nkx), &(ir->nky), &(ir->nkz));
 +    }
 +
 +    /* MRS: eventually figure out better logic for initializing the fep
 +       values that makes declaring the lambda and declaring the state not
 +       potentially conflict if not handled correctly. */
 +    if (ir->efep != efepNO)
 +    {
 +        state.fep_state = ir->fepvals->init_fep_state;
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            /* init_lambda trumps state definitions*/
 +            if (ir->fepvals->init_lambda >= 0)
 +            {
 +                state.lambda[i] = ir->fepvals->init_lambda;
 +            }
 +            else
 +            {
 +                if (ir->fepvals->all_lambda[i] == NULL)
 +                {
 +                    gmx_fatal(FARGS, "Values of lambda not set for a free energy calculation!");
 +                }
 +                else
 +                {
 +                    state.lambda[i] = ir->fepvals->all_lambda[i][state.fep_state];
 +                }
 +            }
 +        }
 +    }
 +
 +    if (ir->ePull != epullNO)
 +    {
 +        set_pull_init(ir, sys, state.x, state.box, state.lambda[efptMASS], oenv, opts->pull_start);
 +    }
 +
 +    if (ir->bRot)
 +    {
 +        set_reference_positions(ir->rot, state.x, state.box,
 +                                opt2fn("-ref", NFILE, fnm), opt2bSet("-ref", NFILE, fnm),
 +                                wi);
 +    }
 +
 +    /*  reset_multinr(sys); */
 +
 +    if (EEL_PME(ir->coulombtype))
 +    {
 +        float ratio = pme_load_estimate(sys, ir, state.box);
 +        fprintf(stderr, "Estimate for the relative computational load of the PME mesh part: %.2f\n", ratio);
 +        /* With free energy we might need to do PME both for the A and B state
 +         * charges. This will double the cost, but the optimal performance will
 +         * then probably be at a slightly larger cut-off and grid spacing.
 +         */
 +        if ((ir->efep == efepNO && ratio > 1.0/2.0) ||
 +            (ir->efep != efepNO && ratio > 2.0/3.0))
 +        {
 +            warning_note(wi,
 +                         "The optimal PME mesh load for parallel simulations is below 0.5\n"
 +                         "and for highly parallel simulations between 0.25 and 0.33,\n"
 +                         "for higher performance, increase the cut-off and the PME grid spacing.\n");
 +            if (ir->efep != efepNO)
 +            {
 +                warning_note(wi,
 +                             "For free energy simulations, the optimal load limit increases from 0.5 to 0.667\n");
 +            }
 +        }
 +    }
 +
 +    {
 +        char   warn_buf[STRLEN];
 +        double cio = compute_io(ir, sys->natoms, &sys->groups, F_NRE, 1);
 +        sprintf(warn_buf, "This run will generate roughly %.0f Mb of data", cio);
 +        if (cio > 2000)
 +        {
 +            set_warning_line(wi, mdparin, -1);
 +            warning_note(wi, warn_buf);
 +        }
 +        else
 +        {
 +            printf("%s\n", warn_buf);
 +        }
 +    }
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "writing run input file...\n");
 +    }
 +
 +    done_warning(wi, FARGS);
 +    write_tpx_state(ftp2fn(efTPX, NFILE, fnm), ir, &state, sys);
 +
 +    /* Output IMD group, if bIMD is TRUE */
 +    write_IMDgroup_to_file(ir->bIMD, ir, &state, sys, NFILE, fnm);
 +
 +    done_atomtype(atype);
 +    done_mtop(sys, TRUE);
 +    done_inputrec_strings();
 +
 +    return 0;
 +}
index 9f8b5769bcfec7d3f3e62898739ecb9ae64af694,0000000000000000000000000000000000000000..aebd0d9e679e174678dc410085eb7d02f46dd270
mode 100644,000000..100644
--- /dev/null
@@@ -1,448 -1,0 +1,453 @@@
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <string.h>
 +#include <stdlib.h>
 +
 +#include "gromacs/utility/cstringutil.h"
 +#include "sysstuff.h"
 +#include "princ.h"
 +#include "gromacs/fileio/futil.h"
 +#include "vec.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "typedefs.h"
 +#include "names.h"
 +#include "gmx_fatal.h"
 +#include "macros.h"
 +#include "index.h"
 +#include "symtab.h"
 +#include "readinp.h"
 +#include "readir.h"
 +#include "mdatoms.h"
 +#include "pbc.h"
 +#include "gromacs/pulling/pull.h"
 +
 +
 +static char pulldim[STRLEN];
 +
 +static void string2dvec(const char buf[], dvec nums)
 +{
 +    double dum;
 +
 +    if (sscanf(buf, "%lf%lf%lf%lf", &nums[0], &nums[1], &nums[2], &dum) != 3)
 +    {
 +        gmx_fatal(FARGS, "Expected three numbers at input line %s", buf);
 +    }
 +}
 +
 +static void init_pull_group(t_pull_group *pg,
 +                            const char   *wbuf)
 +{
 +    double d;
 +    int    n, m;
 +
 +    pg->nweight = 0;
 +    while (sscanf(wbuf, "%lf %n", &d, &n) == 1)
 +    {
 +        if (pg->nweight % 100 == 0)
 +        {
 +            srenew(pg->weight, pg->nweight+100);
 +        }
 +        pg->weight[pg->nweight++] = d;
 +        wbuf += n;
 +    }
 +}
 +
 +static void init_pull_coord(t_pull_coord *pcrd, int eGeom,
 +                            const char *origin_buf, const char *vec_buf)
 +{
 +    int    m;
 +    dvec   origin, vec;
 +
 +    string2dvec(origin_buf, origin);
 +    if (pcrd->group[0] != 0 && dnorm(origin) > 0)
 +    {
 +        gmx_fatal(FARGS, "The pull origin can only be set with an absolute reference");
 +    }
 +
 +    if (eGeom == epullgDIST)
 +    {
 +        clear_dvec(vec);
 +    }
 +    else
 +    {
 +        string2dvec(vec_buf, vec);
 +        if (eGeom == epullgDIR || eGeom == epullgCYL)
 +        {
 +            /* Normalize the direction vector */
 +            dsvmul(1/dnorm(vec), vec, vec);
 +        }
 +    }
 +    for (m = 0; m < DIM; m++)
 +    {
 +        pcrd->origin[m] = origin[m];
 +        pcrd->vec[m]    = vec[m];
 +    }
 +}
 +
 +char **read_pullparams(int *ninp_p, t_inpfile **inp_p,
 +                       t_pull *pull, gmx_bool *bStart,
 +                       warninp_t wi)
 +{
 +    int           ninp, nerror = 0, i, nchar, nscan, m, idum;
 +    t_inpfile    *inp;
 +    const char   *tmp;
 +    char        **grpbuf;
 +    char          dummy[STRLEN], buf[STRLEN], groups[STRLEN], init[STRLEN];
 +    const char   *init_def1 = "0.0", *init_def3 = "0.0 0.0 0.0";
 +    char          wbuf[STRLEN], origin_buf[STRLEN], vec_buf[STRLEN];
 +
 +    t_pull_group *pgrp;
 +    t_pull_coord *pcrd;
 +
 +    ninp   = *ninp_p;
 +    inp    = *inp_p;
 +
 +    /* read pull parameters */
 +    CTYPE("Pull geometry: distance, direction, direction-periodic or cylinder");
 +    EETYPE("pull-geometry",   pull->eGeom, epullg_names);
 +    CTYPE("Select components for the pull vector. default: Y Y Y");
 +    STYPE("pull-dim",         pulldim, "Y Y Y");
 +    CTYPE("Cylinder radius for dynamic reaction force groups (nm)");
 +    RTYPE("pull-r1",          pull->cyl_r1, 1.0);
 +    CTYPE("Switch from r1 to r0 in case of dynamic reaction force");
 +    RTYPE("pull-r0",          pull->cyl_r0, 1.5);
 +    RTYPE("pull-constr-tol",  pull->constr_tol, 1E-6);
 +    EETYPE("pull-start",      *bStart, yesno_names);
 +    EETYPE("pull-print-reference", pull->bPrintRef, yesno_names);
 +    ITYPE("pull-nstxout",     pull->nstxout, 10);
 +    ITYPE("pull-nstfout",     pull->nstfout,  1);
 +    CTYPE("Number of pull groups");
 +    ITYPE("pull-ngroups",     pull->ngroup, 1);
 +    CTYPE("Number of pull coordinates");
 +    ITYPE("pull-ncoords",     pull->ncoord, 1);
 +
 +    if (pull->cyl_r1 > pull->cyl_r0)
 +    {
 +        warning_error(wi, "pull-r1 > pull_r0");
 +    }
 +
 +    if (pull->ngroup < 1)
 +    {
 +        gmx_fatal(FARGS, "pull-ngroups should be >= 1");
 +    }
 +    /* We always add an absolute reference group (index 0), even if not used */
 +    pull->ngroup += 1;
 +
 +    if (pull->ncoord < 1)
 +    {
 +        gmx_fatal(FARGS, "pull-ncoords should be >= 1");
 +    }
 +
 +    snew(pull->group, pull->ngroup);
 +
 +    snew(pull->coord, pull->ncoord);
 +
 +    /* pull group options */
 +    CTYPE("Group name, weight (default all 1), vector, init, rate (nm/ps), kJ/(mol*nm^2)");
 +
 +    /* Read the pull groups */
 +    snew(grpbuf, pull->ngroup);
 +    /* Group 0 is the absolute reference, we don't read anything for 0 */
 +    for (i = 1; i < pull->ngroup; i++)
 +    {
 +        pgrp = &pull->group[i];
 +        snew(grpbuf[i], STRLEN);
 +        sprintf(buf, "pull-group%d-name", i);
 +        STYPE(buf,              grpbuf[i], "");
 +        sprintf(buf, "pull-group%d-weights", i);
 +        STYPE(buf,              wbuf, "");
 +        sprintf(buf, "pull-group%d-pbcatom", i);
 +        ITYPE(buf,              pgrp->pbcatom, 0);
 +
 +        /* Initialize the pull group */
 +        init_pull_group(pgrp, wbuf);
 +    }
 +
 +    /* Read the pull coordinates */
 +    for (i = 1; i < pull->ncoord + 1; i++)
 +    {
 +        pcrd = &pull->coord[i-1];
 +        sprintf(buf, "pull-coord%d-groups", i);
 +        STYPE(buf,              groups, "");
 +        nscan = sscanf(groups, "%d %d %d", &pcrd->group[0], &pcrd->group[1], &idum);
 +        if (nscan != 2)
 +        {
 +            fprintf(stderr, "ERROR: %s should have %d components\n", buf, 2);
 +            nerror++;
 +        }
 +        sprintf(buf, "pull-coord%d-origin", i);
 +        STYPE(buf,              origin_buf, "0.0 0.0 0.0");
 +        sprintf(buf, "pull-coord%d-vec", i);
 +        STYPE(buf,              vec_buf,    "0.0 0.0 0.0");
 +        sprintf(buf, "pull-coord%d-init", i);
 +        RTYPE(buf,              pcrd->init, 0.0);
 +        sprintf(buf, "pull-coord%d-rate", i);
 +        RTYPE(buf,              pcrd->rate, 0.0);
 +        sprintf(buf, "pull-coord%d-k", i);
 +        RTYPE(buf,              pcrd->k, 0.0);
 +        sprintf(buf, "pull-coord%d-kB", i);
 +        RTYPE(buf,              pcrd->kB, pcrd->k);
 +
 +        /* Initialize the pull coordinate */
 +        init_pull_coord(pcrd, pull->eGeom, origin_buf, vec_buf);
 +    }
 +
 +    *ninp_p   = ninp;
 +    *inp_p    = inp;
 +
 +    return grpbuf;
 +}
 +
 +void make_pull_groups(t_pull *pull,
 +                      char **pgnames,
 +                      const t_blocka *grps, char **gnames)
 +{
 +    int           g, ig = -1, i;
 +    t_pull_group *pgrp;
 +
 +    /* Absolute reference group (might not be used) is special */
 +    pgrp          = &pull->group[0];
 +    pgrp->nat     = 0;
 +    pgrp->pbcatom = -1;
 +
 +    for (g = 1; g < pull->ngroup; g++)
 +    {
 +        pgrp = &pull->group[g];
 +
++        if (strcmp(pgnames[g], "") == 0)
++        {
++            gmx_fatal(FARGS, "Group pull_group%d required by grompp was undefined.", g);
++        }
++
 +        ig        = search_string(pgnames[g], grps->nr, gnames);
 +        pgrp->nat = grps->index[ig+1] - grps->index[ig];
 +
 +        fprintf(stderr, "Pull group %d '%s' has %d atoms\n",
 +                g, pgnames[g], pgrp->nat);
 +
 +        if (pgrp->nat == 0)
 +        {
 +            gmx_fatal(FARGS, "Pull group %d '%s' is empty", g, pgnames[g]);
 +        }
 +
 +        snew(pgrp->ind, pgrp->nat);
 +        for (i = 0; i < pgrp->nat; i++)
 +        {
 +            pgrp->ind[i] = grps->a[grps->index[ig]+i];
 +        }
 +
 +        if (pull->eGeom == epullgCYL && g == 1 && pgrp->nweight > 0)
 +        {
 +            gmx_fatal(FARGS, "Weights are not supported for the reference group with cylinder pulling");
 +        }
 +        if (pgrp->nweight > 0 && pgrp->nweight != pgrp->nat)
 +        {
 +            gmx_fatal(FARGS, "Number of weights (%d) for pull group %d '%s' does not match the number of atoms (%d)",
 +                      pgrp->nweight, g, pgnames[g], pgrp->nat);
 +        }
 +
 +        if (pgrp->nat == 1)
 +        {
 +            /* No pbc is required for this group */
 +            pgrp->pbcatom = -1;
 +        }
 +        else
 +        {
 +            if (pgrp->pbcatom > 0)
 +            {
 +                pgrp->pbcatom -= 1;
 +            }
 +            else if (pgrp->pbcatom == 0)
 +            {
 +                pgrp->pbcatom = pgrp->ind[(pgrp->nat-1)/2];
 +            }
 +            else
 +            {
 +                /* Use cosine weighting */
 +                pgrp->pbcatom = -1;
 +            }
 +        }
 +    }
 +}
 +
 +void make_pull_coords(t_pull *pull)
 +{
 +    int           ndim, d, nchar, c;
 +    char         *ptr, pulldim1[STRLEN];
 +    t_pull_coord *pcrd;
 +
 +    ptr  = pulldim;
 +    ndim = 0;
 +    for (d = 0; d < DIM; d++)
 +    {
 +        if (sscanf(ptr, "%s%n", pulldim1, &nchar) != 1)
 +        {
 +            gmx_fatal(FARGS, "Less than 3 pull dimensions given in pull_dim: '%s'",
 +                      pulldim);
 +        }
 +
 +        if (gmx_strncasecmp(pulldim1, "N", 1) == 0)
 +        {
 +            pull->dim[d] = 0;
 +        }
 +        else if (gmx_strncasecmp(pulldim1, "Y", 1) == 0)
 +        {
 +            pull->dim[d] = 1;
 +            ndim++;
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS, "Please use Y(ES) or N(O) for pull_dim only (not %s)",
 +                      pulldim1);
 +        }
 +        ptr += nchar;
 +    }
 +    if (ndim == 0)
 +    {
 +        gmx_fatal(FARGS, "All entries in pull_dim are N");
 +    }
 +
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        pcrd = &pull->coord[c];
 +
 +        if (pcrd->group[0] < 0 || pcrd->group[0] >= pull->ngroup ||
 +            pcrd->group[1] < 0 || pcrd->group[1] >= pull->ngroup)
 +        {
 +            gmx_fatal(FARGS, "Pull group index in pull-coord%d-groups out of range, should be between %d and %d", c+1, 0, pull->ngroup+1);
 +        }
 +
 +        if (pcrd->group[0] == pcrd->group[1])
 +        {
 +            gmx_fatal(FARGS, "Identical pull group indices in pull-coord%d-groups", c+1);
 +        }
 +
 +        if (pull->eGeom == epullgCYL && pcrd->group[0] != 1)
 +        {
 +            gmx_fatal(FARGS, "With pull geometry %s, the first pull group should always be 1", EPULLGEOM(pull->eGeom));
 +        }
 +
 +        if (pull->eGeom != epullgDIST)
 +        {
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if (pcrd->vec[d] != 0 && pull->dim[d] == 0)
 +                {
 +                    gmx_fatal(FARGS, "ERROR: pull-group%d-vec has non-zero %c-component while pull_dim for the %c-dimension is N\n", c+1, 'x'+d, 'x'+d);
 +                }
 +            }
 +        }
 +
 +        if ((pull->eGeom == epullgDIR || pull->eGeom == epullgCYL) &&
 +            norm2(pcrd->vec) == 0)
 +        {
 +            gmx_fatal(FARGS, "pull-group%d-vec can not be zero with geometry %s",
 +                      c+1, EPULLGEOM(pull->eGeom));
 +        }
 +    }
 +}
 +
 +void set_pull_init(t_inputrec *ir, gmx_mtop_t *mtop, rvec *x, matrix box, real lambda,
 +                   const output_env_t oenv, gmx_bool bStart)
 +{
 +    t_mdatoms    *md;
 +    t_pull       *pull;
 +    t_pull_coord *pcrd;
 +    t_pull_group *pgrp0, *pgrp1;
 +    t_pbc         pbc;
 +    int           c, m;
 +    double        t_start, tinvrate;
 +    real          init;
 +    dvec          dr;
 +    double        dev;
 +
 +    init_pull(NULL, ir, 0, NULL, mtop, NULL, oenv, lambda, FALSE, 0);
 +    md = init_mdatoms(NULL, mtop, ir->efep);
 +    atoms2md(mtop, ir, 0, NULL, mtop->natoms, md);
 +    if (ir->efep)
 +    {
 +        update_mdatoms(md, lambda);
 +    }
 +    pull = ir->pull;
 +
 +    set_pbc(&pbc, ir->ePBC, box);
 +
 +    t_start = ir->init_t + ir->init_step*ir->delta_t;
 +
 +    pull_calc_coms(NULL, pull, md, &pbc, t_start, x, NULL);
 +
 +    fprintf(stderr, "Pull group  natoms  pbc atom  distance at start     reference at t=0\n");
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        pcrd  = &pull->coord[c];
 +
 +        pgrp0 = &pull->group[pcrd->group[0]];
 +        pgrp1 = &pull->group[pcrd->group[1]];
 +        fprintf(stderr, "%8d  %8d  %8d\n",
 +                pcrd->group[0], pgrp0->nat, pgrp0->pbcatom+1);
 +        fprintf(stderr, "%8d  %8d  %8d ",
 +                pcrd->group[1], pgrp1->nat, pgrp1->pbcatom+1);
 +
 +        init       = pcrd->init;
 +        pcrd->init = 0;
 +
 +        if (pcrd->rate == 0)
 +        {
 +            tinvrate = 0;
 +        }
 +        else
 +        {
 +            tinvrate = t_start/pcrd->rate;
 +        }
 +        get_pull_coord_distance(pull, c, &pbc, 0, dr, &dev);
 +        fprintf(stderr, " %6.3f ", dev);
 +
 +        if (bStart)
 +        {
 +            pcrd->init = init + dev - tinvrate;
 +        }
 +        else
 +        {
 +            pcrd->init = init;
 +        }
 +        fprintf(stderr, " %6.3f\n", pcrd->init);
 +    }
 +}
index bc7f299e551dd888f80cf00673cd510384c3abbb,0000000000000000000000000000000000000000..fd2a8a1595bbed7f9349c10757948f2d25bd5855
mode 100644,000000..100644
--- /dev/null
@@@ -1,2693 -1,0 +1,2707 @@@
-                     /* c0 and c1 are epsilon and sigma */
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <ctype.h>
 +#include <math.h>
 +#include <assert.h>
 +
 +#include "sysstuff.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "macros.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "names.h"
 +#include "toputil.h"
 +#include "toppush.h"
 +#include "topdirs.h"
 +#include "readir.h"
 +#include "symtab.h"
 +#include "gmx_fatal.h"
 +#include "warninp.h"
 +#include "gpp_atomtype.h"
 +#include "gpp_bond_atomtype.h"
 +
 +void generate_nbparams(int comb, int ftype, t_params *plist, gpp_atomtype_t atype,
 +                       warninp_t wi)
 +{
 +    int   i, j, k = -1, nf;
 +    int   nr, nrfp;
 +    real  c, bi, bj, ci, cj, ci0, ci1, ci2, cj0, cj1, cj2;
 +    char  errbuf[256];
 +
 +    /* Lean mean shortcuts */
 +    nr   = get_atomtype_ntypes(atype);
 +    nrfp = NRFP(ftype);
 +    snew(plist->param, nr*nr);
 +    plist->nr = nr*nr;
 +
 +    /* Fill the matrix with force parameters */
 +    switch (ftype)
 +    {
 +        case F_LJ:
 +            switch (comb)
 +            {
 +                case eCOMB_GEOMETRIC:
 +                    /* Gromos rules */
 +                    for (i = k = 0; (i < nr); i++)
 +                    {
 +                        for (j = 0; (j < nr); j++, k++)
 +                        {
 +                            for (nf = 0; (nf < nrfp); nf++)
 +                            {
 +                                ci = get_atomtype_nbparam(i, nf, atype);
 +                                cj = get_atomtype_nbparam(j, nf, atype);
 +                                c  = sqrt(ci * cj);
 +                                plist->param[k].c[nf]      = c;
 +                            }
 +                        }
 +                    }
 +                    break;
 +
 +                case eCOMB_ARITHMETIC:
-                             plist->param[k].c[0] = (ci0+cj0)*0.5;
++                    /* c0 and c1 are sigma and epsilon */
 +                    for (i = k = 0; (i < nr); i++)
 +                    {
 +                        for (j = 0; (j < nr); j++, k++)
 +                        {
 +                            ci0                  = get_atomtype_nbparam(i, 0, atype);
 +                            cj0                  = get_atomtype_nbparam(j, 0, atype);
 +                            ci1                  = get_atomtype_nbparam(i, 1, atype);
 +                            cj1                  = get_atomtype_nbparam(j, 1, atype);
-                     /* c0 and c1 are epsilon and sigma */
++                            plist->param[k].c[0] = (fabs(ci0) + fabs(cj0))*0.5;
++                            /* Negative sigma signals that c6 should be set to zero later,
++                             * so we need to propagate that through the combination rules.
++                             */
++                            if (ci0 < 0 || cj0 < 0)
++                            {
++                                plist->param[k].c[0] *= -1;
++                            }
 +                            plist->param[k].c[1] = sqrt(ci1*cj1);
 +                        }
 +                    }
 +
 +                    break;
 +                case eCOMB_GEOM_SIG_EPS:
-                             plist->param[k].c[0] = sqrt(ci0*cj0);
++                    /* c0 and c1 are sigma and epsilon */
 +                    for (i = k = 0; (i < nr); i++)
 +                    {
 +                        for (j = 0; (j < nr); j++, k++)
 +                        {
 +                            ci0                  = get_atomtype_nbparam(i, 0, atype);
 +                            cj0                  = get_atomtype_nbparam(j, 0, atype);
 +                            ci1                  = get_atomtype_nbparam(i, 1, atype);
 +                            cj1                  = get_atomtype_nbparam(j, 1, atype);
++                            plist->param[k].c[0] = sqrt(fabs(ci0*cj0));
++                            /* Negative sigma signals that c6 should be set to zero later,
++                             * so we need to propagate that through the combination rules.
++                             */
++                            if (ci0 < 0 || cj0 < 0)
++                            {
++                                plist->param[k].c[0] *= -1;
++                            }
 +                            plist->param[k].c[1] = sqrt(ci1*cj1);
 +                        }
 +                    }
 +
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "No such combination rule %d", comb);
 +            }
 +            if (plist->nr != k)
 +            {
 +                gmx_incons("Topology processing, generate nb parameters");
 +            }
 +            break;
 +
 +        case F_BHAM:
 +            /* Buckingham rules */
 +            for (i = k = 0; (i < nr); i++)
 +            {
 +                for (j = 0; (j < nr); j++, k++)
 +                {
 +                    ci0                  = get_atomtype_nbparam(i, 0, atype);
 +                    cj0                  = get_atomtype_nbparam(j, 0, atype);
 +                    ci2                  = get_atomtype_nbparam(i, 2, atype);
 +                    cj2                  = get_atomtype_nbparam(j, 2, atype);
 +                    bi                   = get_atomtype_nbparam(i, 1, atype);
 +                    bj                   = get_atomtype_nbparam(j, 1, atype);
 +                    plist->param[k].c[0] = sqrt(ci0 * cj0);
 +                    if ((bi == 0) || (bj == 0))
 +                    {
 +                        plist->param[k].c[1] = 0;
 +                    }
 +                    else
 +                    {
 +                        plist->param[k].c[1] = 2.0/(1/bi+1/bj);
 +                    }
 +                    plist->param[k].c[2] = sqrt(ci2 * cj2);
 +                }
 +            }
 +
 +            break;
 +        default:
 +            sprintf(errbuf, "Invalid nonbonded type %s",
 +                    interaction_function[ftype].longname);
 +            warning_error(wi, errbuf);
 +    }
 +}
 +
 +static void realloc_nb_params(gpp_atomtype_t at,
 +                              t_nbparam ***nbparam, t_nbparam ***pair)
 +{
 +    /* Add space in the non-bonded parameters matrix */
 +    int atnr = get_atomtype_ntypes(at);
 +    srenew(*nbparam, atnr);
 +    snew((*nbparam)[atnr-1], atnr);
 +    if (pair)
 +    {
 +        srenew(*pair, atnr);
 +        snew((*pair)[atnr-1], atnr);
 +    }
 +}
 +
 +static void copy_B_from_A(int ftype, double *c)
 +{
 +    int nrfpA, nrfpB, i;
 +
 +    nrfpA = NRFPA(ftype);
 +    nrfpB = NRFPB(ftype);
 +
 +    /* Copy the B parameters from the first nrfpB A parameters */
 +    for (i = 0; (i < nrfpB); i++)
 +    {
 +        c[nrfpA+i] = c[i];
 +    }
 +}
 +
 +void push_at (t_symtab *symtab, gpp_atomtype_t at, t_bond_atomtype bat,
 +              char *line, int nb_funct,
 +              t_nbparam ***nbparam, t_nbparam ***pair,
 +              warninp_t wi)
 +{
 +    typedef struct {
 +        const char *entry;
 +        int         ptype;
 +    } t_xlate;
 +    t_xlate    xl[eptNR] = {
 +        { "A",   eptAtom },
 +        { "N",   eptNucleus },
 +        { "S",   eptShell },
 +        { "B",   eptBond },
 +        { "V",   eptVSite },
 +    };
 +
 +    int        nr, i, nfields, j, pt, nfp0 = -1;
 +    int        batype_nr, nread;
 +    char       type[STRLEN], btype[STRLEN], ptype[STRLEN];
 +    double     m, q;
 +    double     c[MAXFORCEPARAM];
 +    double     radius, vol, surftens, gb_radius, S_hct;
 +    char       tmpfield[12][100]; /* Max 12 fields of width 100 */
 +    char       errbuf[256];
 +    t_atom    *atom;
 +    t_param   *param;
 +    int        atomnr;
 +    gmx_bool   have_atomic_number;
 +    gmx_bool   have_bonded_type;
 +
 +    snew(atom, 1);
 +    snew(param, 1);
 +
 +    /* First assign input line to temporary array */
 +    nfields = sscanf(line, "%s%s%s%s%s%s%s%s%s%s%s%s",
 +                     tmpfield[0], tmpfield[1], tmpfield[2], tmpfield[3], tmpfield[4], tmpfield[5],
 +                     tmpfield[6], tmpfield[7], tmpfield[8], tmpfield[9], tmpfield[10], tmpfield[11]);
 +
 +    /* Comments on optional fields in the atomtypes section:
 +     *
 +     * The force field format is getting a bit old. For OPLS-AA we needed
 +     * to add a special bonded atomtype, and for Gerrit Groenhofs QM/MM stuff
 +     * we also needed the atomic numbers.
 +     * To avoid making all old or user-generated force fields unusable we
 +     * have introduced both these quantities as optional columns, and do some
 +     * acrobatics to check whether they are present or not.
 +     * This will all look much nicer when we switch to XML... sigh.
 +     *
 +     * Field 0 (mandatory) is the nonbonded type name. (string)
 +     * Field 1 (optional)  is the bonded type (string)
 +     * Field 2 (optional)  is the atomic number (int)
 +     * Field 3 (mandatory) is the mass (numerical)
 +     * Field 4 (mandatory) is the charge (numerical)
 +     * Field 5 (mandatory) is the particle type (single character)
 +     * This is followed by a number of nonbonded parameters.
 +     *
 +     * The safest way to identify the format is the particle type field.
 +     *
 +     * So, here is what we do:
 +     *
 +     * A. Read in the first six fields as strings
 +     * B. If field 3 (starting from 0) is a single char, we have neither
 +     *    bonded_type or atomic numbers.
 +     * C. If field 5 is a single char we have both.
 +     * D. If field 4 is a single char we check field 1. If this begins with
 +     *    an alphabetical character we have bonded types, otherwise atomic numbers.
 +     */
 +
 +    if (nfields < 6)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +
 +    if ( (strlen(tmpfield[5]) == 1) && isalpha(tmpfield[5][0]) )
 +    {
 +        have_bonded_type   = TRUE;
 +        have_atomic_number = TRUE;
 +    }
 +    else if ( (strlen(tmpfield[3]) == 1) && isalpha(tmpfield[3][0]) )
 +    {
 +        have_bonded_type   = FALSE;
 +        have_atomic_number = FALSE;
 +    }
 +    else
 +    {
 +        have_bonded_type   = ( isalpha(tmpfield[1][0]) != 0 );
 +        have_atomic_number = !have_bonded_type;
 +    }
 +
 +    /* optional fields */
 +    surftens  = -1;
 +    vol       = -1;
 +    radius    = -1;
 +    gb_radius = -1;
 +    atomnr    = -1;
 +    S_hct     = -1;
 +
 +    switch (nb_funct)
 +    {
 +
 +        case F_LJ:
 +            nfp0 = 2;
 +
 +            if (have_atomic_number)
 +            {
 +                if (have_bonded_type)
 +                {
 +                    nread = sscanf(line, "%s%s%d%lf%lf%s%lf%lf%lf%lf%lf%lf",
 +                                   type, btype, &atomnr, &m, &q, ptype, &c[0], &c[1],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 8)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +                else
 +                {
 +                    /* have_atomic_number && !have_bonded_type */
 +                    nread = sscanf(line, "%s%d%lf%lf%s%lf%lf%lf%lf%lf%lf",
 +                                   type, &atomnr, &m, &q, ptype, &c[0], &c[1],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 7)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                if (have_bonded_type)
 +                {
 +                    /* !have_atomic_number && have_bonded_type */
 +                    nread = sscanf(line, "%s%s%lf%lf%s%lf%lf%lf%lf%lf%lf",
 +                                   type, btype, &m, &q, ptype, &c[0], &c[1],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 7)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +                else
 +                {
 +                    /* !have_atomic_number && !have_bonded_type */
 +                    nread = sscanf(line, "%s%lf%lf%s%lf%lf%lf%lf%lf%lf",
 +                                   type, &m, &q, ptype, &c[0], &c[1],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 6)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +            }
 +
 +            if (!have_bonded_type)
 +            {
 +                strcpy(btype, type);
 +            }
 +
 +            if (!have_atomic_number)
 +            {
 +                atomnr = -1;
 +            }
 +
 +            break;
 +
 +        case F_BHAM:
 +            nfp0 = 3;
 +
 +            if (have_atomic_number)
 +            {
 +                if (have_bonded_type)
 +                {
 +                    nread = sscanf(line, "%s%s%d%lf%lf%s%lf%lf%lf%lf%lf%lf%lf",
 +                                   type, btype, &atomnr, &m, &q, ptype, &c[0], &c[1], &c[2],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 9)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +                else
 +                {
 +                    /* have_atomic_number && !have_bonded_type */
 +                    nread = sscanf(line, "%s%d%lf%lf%s%lf%lf%lf%lf%lf%lf%lf",
 +                                   type, &atomnr, &m, &q, ptype, &c[0], &c[1], &c[2],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 8)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                if (have_bonded_type)
 +                {
 +                    /* !have_atomic_number && have_bonded_type */
 +                    nread = sscanf(line, "%s%s%lf%lf%s%lf%lf%lf%lf%lf%lf%lf",
 +                                   type, btype, &m, &q, ptype, &c[0], &c[1], &c[2],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 8)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +                else
 +                {
 +                    /* !have_atomic_number && !have_bonded_type */
 +                    nread = sscanf(line, "%s%lf%lf%s%lf%lf%lf%lf%lf%lf%lf",
 +                                   type, &m, &q, ptype, &c[0], &c[1], &c[2],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 7)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +            }
 +
 +            if (!have_bonded_type)
 +            {
 +                strcpy(btype, type);
 +            }
 +
 +            if (!have_atomic_number)
 +            {
 +                atomnr = -1;
 +            }
 +
 +            break;
 +
 +        default:
 +            gmx_fatal(FARGS, "Invalid function type %d in push_at %s %d", nb_funct,
 +                      __FILE__, __LINE__);
 +    }
 +    for (j = nfp0; (j < MAXFORCEPARAM); j++)
 +    {
 +        c[j] = 0.0;
 +    }
 +
 +    if (strlen(type) == 1 && isdigit(type[0]))
 +    {
 +        gmx_fatal(FARGS, "Atom type names can't be single digits.");
 +    }
 +
 +    if (strlen(btype) == 1 && isdigit(btype[0]))
 +    {
 +        gmx_fatal(FARGS, "Bond atom type names can't be single digits.");
 +    }
 +
 +    /* Hack to read old topologies */
 +    if (gmx_strcasecmp(ptype, "D") == 0)
 +    {
 +        sprintf(ptype, "V");
 +    }
 +    for (j = 0; (j < eptNR); j++)
 +    {
 +        if (gmx_strcasecmp(ptype, xl[j].entry) == 0)
 +        {
 +            break;
 +        }
 +    }
 +    if (j == eptNR)
 +    {
 +        gmx_fatal(FARGS, "Invalid particle type %s on line %s",
 +                  ptype, line);
 +    }
 +    pt = xl[j].ptype;
 +    if (debug)
 +    {
 +        fprintf(debug, "ptype: %s\n", ptype_str[pt]);
 +    }
 +
 +    atom->q     = q;
 +    atom->m     = m;
 +    atom->ptype = pt;
 +    for (i = 0; (i < MAXFORCEPARAM); i++)
 +    {
 +        param->c[i] = c[i];
 +    }
 +
 +    if ((batype_nr = get_bond_atomtype_type(btype, bat)) == NOTSET)
 +    {
 +        add_bond_atomtype(bat, symtab, btype);
 +    }
 +    batype_nr = get_bond_atomtype_type(btype, bat);
 +
 +    if ((nr = get_atomtype_type(type, at)) != NOTSET)
 +    {
 +        sprintf(errbuf, "Overriding atomtype %s", type);
 +        warning(wi, errbuf);
 +        if ((nr = set_atomtype(nr, at, symtab, atom, type, param, batype_nr,
 +                               radius, vol, surftens, atomnr, gb_radius, S_hct)) == NOTSET)
 +        {
 +            gmx_fatal(FARGS, "Replacing atomtype %s failed", type);
 +        }
 +    }
 +    else if ((nr = add_atomtype(at, symtab, atom, type, param,
 +                                batype_nr, radius, vol,
 +                                surftens, atomnr, gb_radius, S_hct)) == NOTSET)
 +    {
 +        gmx_fatal(FARGS, "Adding atomtype %s failed", type);
 +    }
 +    else
 +    {
 +        /* Add space in the non-bonded parameters matrix */
 +        realloc_nb_params(at, nbparam, pair);
 +    }
 +    sfree(atom);
 +    sfree(param);
 +}
 +
 +static void push_bondtype(t_params     *       bt,
 +                          t_param     *        b,
 +                          int                  nral,
 +                          int                  ftype,
 +                          gmx_bool             bAllowRepeat,
 +                          char     *           line,
 +                          warninp_t            wi)
 +{
 +    int      i, j;
 +    gmx_bool bTest, bFound, bCont, bId;
 +    int      nr   = bt->nr;
 +    int      nrfp = NRFP(ftype);
 +    char     errbuf[256];
 +
 +    /* If bAllowRepeat is TRUE, we allow multiple entries as long as they
 +       are on directly _adjacent_ lines.
 +     */
 +
 +    /* First check if our atomtypes are _identical_ (not reversed) to the previous
 +       entry. If they are not identical we search for earlier duplicates. If they are
 +       we can skip it, since we already searched for the first line
 +       in this group.
 +     */
 +
 +    bFound = FALSE;
 +    bCont  = FALSE;
 +
 +    if (bAllowRepeat && nr > 1)
 +    {
 +        for (j = 0, bCont = TRUE; (j < nral); j++)
 +        {
 +            bCont = bCont && (b->a[j] == bt->param[nr-2].a[j]);
 +        }
 +    }
 +
 +    /* Search for earlier duplicates if this entry was not a continuation
 +       from the previous line.
 +     */
 +    if (!bCont)
 +    {
 +        bFound = FALSE;
 +        for (i = 0; (i < nr); i++)
 +        {
 +            bTest = TRUE;
 +            for (j = 0; (j < nral); j++)
 +            {
 +                bTest = (bTest && (b->a[j] == bt->param[i].a[j]));
 +            }
 +            if (!bTest)
 +            {
 +                bTest = TRUE;
 +                for (j = 0; (j < nral); j++)
 +                {
 +                    bTest = (bTest && (b->a[nral-1-j] == bt->param[i].a[j]));
 +                }
 +            }
 +            if (bTest)
 +            {
 +                if (!bFound)
 +                {
 +                    bId = TRUE;
 +                    for (j = 0; (j < nrfp); j++)
 +                    {
 +                        bId = bId && (bt->param[i].c[j] == b->c[j]);
 +                    }
 +                    if (!bId)
 +                    {
 +                        sprintf(errbuf, "Overriding %s parameters.%s",
 +                                interaction_function[ftype].longname,
 +                                (ftype == F_PDIHS) ? "\nUse dihedraltype 4 to allow several multiplicity terms." : "");
 +                        warning(wi, errbuf);
 +                        fprintf(stderr, "  old:");
 +                        for (j = 0; (j < nrfp); j++)
 +                        {
 +                            fprintf(stderr, " %g", bt->param[i].c[j]);
 +                        }
 +                        fprintf(stderr, " \n  new: %s\n\n", line);
 +                    }
 +                }
 +                /* Overwrite it! */
 +                for (j = 0; (j < nrfp); j++)
 +                {
 +                    bt->param[i].c[j] = b->c[j];
 +                }
 +                bFound = TRUE;
 +            }
 +        }
 +    }
 +    if (!bFound)
 +    {
 +        /* alloc */
 +        pr_alloc (2, bt);
 +
 +        /* fill the arrays up and down */
 +        memcpy(bt->param[bt->nr].c,  b->c, sizeof(b->c));
 +        memcpy(bt->param[bt->nr].a,  b->a, sizeof(b->a));
 +        memcpy(bt->param[bt->nr+1].c, b->c, sizeof(b->c));
 +
 +        /* The definitions of linear angles depend on the order of atoms,
 +         * that means that for atoms i-j-k, with certain parameter a, the
 +         * corresponding k-j-i angle will have parameter 1-a.
 +         */
 +        if (ftype == F_LINEAR_ANGLES)
 +        {
 +            bt->param[bt->nr+1].c[0] = 1-bt->param[bt->nr+1].c[0];
 +            bt->param[bt->nr+1].c[2] = 1-bt->param[bt->nr+1].c[2];
 +        }
 +
 +        for (j = 0; (j < nral); j++)
 +        {
 +            bt->param[bt->nr+1].a[j] = b->a[nral-1-j];
 +        }
 +
 +        bt->nr += 2;
 +    }
 +}
 +
 +void push_bt(directive d, t_params bt[], int nral,
 +             gpp_atomtype_t at,
 +             t_bond_atomtype bat, char *line,
 +             warninp_t wi)
 +{
 +    const char *formal[MAXATOMLIST+1] = {
 +        "%s",
 +        "%s%s",
 +        "%s%s%s",
 +        "%s%s%s%s",
 +        "%s%s%s%s%s",
 +        "%s%s%s%s%s%s",
 +        "%s%s%s%s%s%s%s"
 +    };
 +    const char *formnl[MAXATOMLIST+1] = {
 +        "%*s",
 +        "%*s%*s",
 +        "%*s%*s%*s",
 +        "%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s%*s"
 +    };
 +    const char *formlf = "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf";
 +    int         i, ft, ftype, nn, nrfp, nrfpA, nrfpB;
 +    char        f1[STRLEN];
 +    char        alc[MAXATOMLIST+1][20];
 +    /* One force parameter more, so we can check if we read too many */
 +    double      c[MAXFORCEPARAM+1];
 +    t_param     p;
 +    char        errbuf[256];
 +
 +    if ((bat && at) || (!bat && !at))
 +    {
 +        gmx_incons("You should pass either bat or at to push_bt");
 +    }
 +
 +    /* Make format string (nral ints+functype) */
 +    if ((nn = sscanf(line, formal[nral],
 +                     alc[0], alc[1], alc[2], alc[3], alc[4], alc[5])) != nral+1)
 +    {
 +        sprintf(errbuf, "Not enough atomtypes (%d instead of %d)", nn-1, nral);
 +        warning_error(wi, errbuf);
 +        return;
 +    }
 +
 +    ft    = strtol(alc[nral], NULL, 10);
 +    ftype = ifunc_index(d, ft);
 +    nrfp  = NRFP(ftype);
 +    nrfpA = interaction_function[ftype].nrfpA;
 +    nrfpB = interaction_function[ftype].nrfpB;
 +    strcpy(f1, formnl[nral]);
 +    strcat(f1, formlf);
 +    if ((nn = sscanf(line, f1, &c[0], &c[1], &c[2], &c[3], &c[4], &c[5], &c[6], &c[7], &c[8], &c[9], &c[10], &c[11], &c[12]))
 +        != nrfp)
 +    {
 +        if (nn == nrfpA)
 +        {
 +            /* Copy the B-state from the A-state */
 +            copy_B_from_A(ftype, c);
 +        }
 +        else
 +        {
 +            if (nn < nrfpA)
 +            {
 +                warning_error(wi, "Not enough parameters");
 +            }
 +            else if (nn > nrfpA && nn < nrfp)
 +            {
 +                warning_error(wi, "Too many parameters or not enough parameters for topology B");
 +            }
 +            else if (nn > nrfp)
 +            {
 +                warning_error(wi, "Too many parameters");
 +            }
 +            for (i = nn; (i < nrfp); i++)
 +            {
 +                c[i] = 0.0;
 +            }
 +        }
 +    }
 +    for (i = 0; (i < nral); i++)
 +    {
 +        if (at && ((p.a[i] = get_atomtype_type(alc[i], at)) == NOTSET))
 +        {
 +            gmx_fatal(FARGS, "Unknown atomtype %s\n", alc[i]);
 +        }
 +        else if (bat && ((p.a[i] = get_bond_atomtype_type(alc[i], bat)) == NOTSET))
 +        {
 +            gmx_fatal(FARGS, "Unknown bond_atomtype %s\n", alc[i]);
 +        }
 +    }
 +    for (i = 0; (i < nrfp); i++)
 +    {
 +        p.c[i] = c[i];
 +    }
 +    push_bondtype (&(bt[ftype]), &p, nral, ftype, FALSE, line, wi);
 +}
 +
 +
 +void push_dihedraltype(directive d, t_params bt[],
 +                       t_bond_atomtype bat, char *line,
 +                       warninp_t wi)
 +{
 +    const char  *formal[MAXATOMLIST+1] = {
 +        "%s",
 +        "%s%s",
 +        "%s%s%s",
 +        "%s%s%s%s",
 +        "%s%s%s%s%s",
 +        "%s%s%s%s%s%s",
 +        "%s%s%s%s%s%s%s"
 +    };
 +    const char  *formnl[MAXATOMLIST+1] = {
 +        "%*s",
 +        "%*s%*s",
 +        "%*s%*s%*s",
 +        "%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s%*s"
 +    };
 +    const char  *formlf[MAXFORCEPARAM] = {
 +        "%lf",
 +        "%lf%lf",
 +        "%lf%lf%lf",
 +        "%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
 +    };
 +    int          i, ft, ftype, nn, nrfp, nrfpA, nrfpB, nral;
 +    char         f1[STRLEN];
 +    char         alc[MAXATOMLIST+1][20];
 +    double       c[MAXFORCEPARAM];
 +    t_param      p;
 +    gmx_bool     bAllowRepeat;
 +    char         errbuf[256];
 +
 +    /* This routine accepts dihedraltypes defined from either 2 or 4 atoms.
 +     *
 +     * We first check for 2 atoms with the 3th column being an integer
 +     * defining the type. If this isn't the case, we try it with 4 atoms
 +     * and the 5th column defining the dihedral type.
 +     */
 +    nn = sscanf(line, formal[4], alc[0], alc[1], alc[2], alc[3], alc[4]);
 +    if (nn >= 3 && strlen(alc[2]) == 1 && isdigit(alc[2][0]))
 +    {
 +        nral  = 2;
 +        ft    = strtol(alc[nral], NULL, 10);
 +        /* Move atom types around a bit and use 'X' for wildcard atoms
 +         * to create a 4-atom dihedral definition with arbitrary atoms in
 +         * position 1 and 4.
 +         */
 +        if (alc[2][0] == '2')
 +        {
 +            /* improper - the two atomtypes are 1,4. Use wildcards for 2,3 */
 +            strcpy(alc[3], alc[1]);
 +            sprintf(alc[2], "X");
 +            sprintf(alc[1], "X");
 +            /* alc[0] stays put */
 +        }
 +        else
 +        {
 +            /* proper - the two atomtypes are 2,3. Use wildcards for 1,4 */
 +            sprintf(alc[3], "X");
 +            strcpy(alc[2], alc[1]);
 +            strcpy(alc[1], alc[0]);
 +            sprintf(alc[0], "X");
 +        }
 +    }
 +    else if (nn == 5 && strlen(alc[4]) == 1 && isdigit(alc[4][0]))
 +    {
 +        nral  = 4;
 +        ft    = strtol(alc[nral], NULL, 10);
 +    }
 +    else
 +    {
 +        sprintf(errbuf, "Incorrect number of atomtypes for dihedral (%d instead of 2 or 4)", nn-1);
 +        warning_error(wi, errbuf);
 +        return;
 +    }
 +
 +    if (ft == 9)
 +    {
 +        /* Previously, we have always overwritten parameters if e.g. a torsion
 +           with the same atomtypes occurs on multiple lines. However, CHARMM and
 +           some other force fields specify multiple dihedrals over some bonds,
 +           including cosines with multiplicity 6 and somethimes even higher.
 +           Thus, they cannot be represented with Ryckaert-Bellemans terms.
 +           To add support for these force fields, Dihedral type 9 is identical to
 +           normal proper dihedrals, but repeated entries are allowed.
 +         */
 +        bAllowRepeat = TRUE;
 +        ft           = 1;
 +    }
 +    else
 +    {
 +        bAllowRepeat = FALSE;
 +    }
 +
 +
 +    ftype = ifunc_index(d, ft);
 +    nrfp  = NRFP(ftype);
 +    nrfpA = interaction_function[ftype].nrfpA;
 +    nrfpB = interaction_function[ftype].nrfpB;
 +
 +    strcpy(f1, formnl[nral]);
 +    strcat(f1, formlf[nrfp-1]);
 +
 +    /* Check number of parameters given */
 +    if ((nn = sscanf(line, f1, &c[0], &c[1], &c[2], &c[3], &c[4], &c[5], &c[6], &c[7], &c[8], &c[9], &c[10], &c[11]))
 +        != nrfp)
 +    {
 +        if (nn == nrfpA)
 +        {
 +            /* Copy the B-state from the A-state */
 +            copy_B_from_A(ftype, c);
 +        }
 +        else
 +        {
 +            if (nn < nrfpA)
 +            {
 +                warning_error(wi, "Not enough parameters");
 +            }
 +            else if (nn > nrfpA && nn < nrfp)
 +            {
 +                warning_error(wi, "Too many parameters or not enough parameters for topology B");
 +            }
 +            else if (nn > nrfp)
 +            {
 +                warning_error(wi, "Too many parameters");
 +            }
 +            for (i = nn; (i < nrfp); i++)
 +            {
 +                c[i] = 0.0;
 +            }
 +        }
 +    }
 +
 +    for (i = 0; (i < 4); i++)
 +    {
 +        if (!strcmp(alc[i], "X"))
 +        {
 +            p.a[i] = -1;
 +        }
 +        else
 +        {
 +            if ((p.a[i] = get_bond_atomtype_type(alc[i], bat)) == NOTSET)
 +            {
 +                gmx_fatal(FARGS, "Unknown bond_atomtype %s", alc[i]);
 +            }
 +        }
 +    }
 +    for (i = 0; (i < nrfp); i++)
 +    {
 +        p.c[i] = c[i];
 +    }
 +    /* Always use 4 atoms here, since we created two wildcard atoms
 +     * if there wasn't of them 4 already.
 +     */
 +    push_bondtype (&(bt[ftype]), &p, 4, ftype, bAllowRepeat, line, wi);
 +}
 +
 +
 +void push_nbt(directive d, t_nbparam **nbt, gpp_atomtype_t atype,
 +              char *pline, int nb_funct,
 +              warninp_t wi)
 +{
 +    /* swap the atoms */
 +    const char *form2 = "%*s%*s%*s%lf%lf";
 +    const char *form3 = "%*s%*s%*s%lf%lf%lf";
 +    const char *form4 = "%*s%*s%*s%lf%lf%lf%lf";
 +    const char *form5 = "%*s%*s%*s%lf%lf%lf%lf%lf";
 +    char        a0[80], a1[80];
 +    int         i, f, n, ftype, atnr, nrfp;
 +    double      c[4], dum;
 +    real        cr[4], sig6;
 +    atom_id     ai, aj;
 +    t_nbparam  *nbp;
 +    gmx_bool    bId;
 +    char        errbuf[256];
 +
 +    if (sscanf (pline, "%s%s%d", a0, a1, &f) != 3)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +
 +    ftype = ifunc_index(d, f);
 +
 +    if (ftype != nb_funct)
 +    {
 +        sprintf(errbuf, "Trying to add %s while the default nonbond type is %s",
 +                interaction_function[ftype].longname,
 +                interaction_function[nb_funct].longname);
 +        warning_error(wi, errbuf);
 +        return;
 +    }
 +
 +    /* Get the force parameters */
 +    nrfp = NRFP(ftype);
 +    if (ftype == F_LJ14)
 +    {
 +        n = sscanf(pline, form4, &c[0], &c[1], &c[2], &c[3]);
 +        if (n < 2)
 +        {
 +            too_few(wi);
 +            return;
 +        }
 +        /* When the B topology parameters are not set,
 +         * copy them from topology A
 +         */
 +        assert(nrfp <= 4);
 +        for (i = n; i < nrfp; i++)
 +        {
 +            c[i] = c[i-2];
 +        }
 +    }
 +    else if (ftype == F_LJC14_Q)
 +    {
 +        n = sscanf(pline, form5, &c[0], &c[1], &c[2], &c[3], &dum);
 +        if (n != 4)
 +        {
 +            incorrect_n_param(wi);
 +            return;
 +        }
 +    }
 +    else if (nrfp == 2)
 +    {
 +        if (sscanf(pline, form3, &c[0], &c[1], &dum) != 2)
 +        {
 +            incorrect_n_param(wi);
 +            return;
 +        }
 +    }
 +    else if (nrfp == 3)
 +    {
 +        if (sscanf(pline, form4, &c[0], &c[1], &c[2], &dum) != 3)
 +        {
 +            incorrect_n_param(wi);
 +            return;
 +        }
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "Number of force parameters for nonbonded interactions is %d"
 +                  " in file %s, line %d", nrfp, __FILE__, __LINE__);
 +    }
 +    for (i = 0; (i < nrfp); i++)
 +    {
 +        cr[i] = c[i];
 +    }
 +
 +    /* Put the parameters in the matrix */
 +    if ((ai = get_atomtype_type (a0, atype)) == NOTSET)
 +    {
 +        gmx_fatal(FARGS, "Atomtype %s not found", a0);
 +    }
 +    if ((aj = get_atomtype_type (a1, atype)) == NOTSET)
 +    {
 +        gmx_fatal(FARGS, "Atomtype %s not found", a1);
 +    }
 +    nbp = &(nbt[max(ai, aj)][min(ai, aj)]);
 +
 +    if (nbp->bSet)
 +    {
 +        bId = TRUE;
 +        for (i = 0; i < nrfp; i++)
 +        {
 +            bId = bId && (nbp->c[i] == cr[i]);
 +        }
 +        if (!bId)
 +        {
 +            sprintf(errbuf, "Overriding non-bonded parameters,");
 +            warning(wi, errbuf);
 +            fprintf(stderr, "  old:");
 +            for (i = 0; i < nrfp; i++)
 +            {
 +                fprintf(stderr, " %g", nbp->c[i]);
 +            }
 +            fprintf(stderr, " new\n%s\n", pline);
 +        }
 +    }
 +    nbp->bSet = TRUE;
 +    for (i = 0; i < nrfp; i++)
 +    {
 +        nbp->c[i] = cr[i];
 +    }
 +}
 +
 +void
 +push_gb_params (gpp_atomtype_t at, char *line,
 +                warninp_t wi)
 +{
 +    int    nfield;
 +    int    atype;
 +    double radius, vol, surftens, gb_radius, S_hct;
 +    char   atypename[STRLEN];
 +    char   errbuf[STRLEN];
 +
 +    if ( (nfield = sscanf(line, "%s%lf%lf%lf%lf%lf", atypename, &radius, &vol, &surftens, &gb_radius, &S_hct)) != 6)
 +    {
 +        sprintf(errbuf, "Too few gb parameters for type %s\n", atypename);
 +        warning(wi, errbuf);
 +    }
 +
 +    /* Search for atomtype */
 +    atype = get_atomtype_type(atypename, at);
 +
 +    if (atype == NOTSET)
 +    {
 +        printf("Couldn't find topology match for atomtype %s\n", atypename);
 +        abort();
 +    }
 +
 +    set_atomtype_gbparam(at, atype, radius, vol, surftens, gb_radius, S_hct);
 +}
 +
 +void
 +push_cmaptype(directive d, t_params bt[], int nral, gpp_atomtype_t at,
 +              t_bond_atomtype bat, char *line,
 +              warninp_t wi)
 +{
 +    const char  *formal = "%s%s%s%s%s%s%s%s";
 +
 +    int          i, j, ft, ftype, nn, nrfp, nrfpA, nrfpB;
 +    int          start;
 +    int          nxcmap, nycmap, ncmap, read_cmap, sl, nct;
 +    char         s[20], alc[MAXATOMLIST+2][20];
 +    t_param      p;
 +    gmx_bool     bAllowRepeat;
 +    char         errbuf[256];
 +
 +    /* Keep the compiler happy */
 +    read_cmap = 0;
 +    start     = 0;
 +
 +    if ((nn = sscanf(line, formal, alc[0], alc[1], alc[2], alc[3], alc[4], alc[5], alc[6], alc[7])) != nral+3)
 +    {
 +        sprintf(errbuf, "Incorrect number of atomtypes for cmap (%d instead of 5)", nn-1);
 +        warning_error(wi, errbuf);
 +        return;
 +    }
 +
 +    /* Compute an offset for each line where the cmap parameters start
 +     * ie. where the atom types and grid spacing information ends
 +     */
 +    for (i = 0; i < nn; i++)
 +    {
 +        start += (int)strlen(alc[i]);
 +    }
 +
 +    /* There are nn-1 spaces between the atom types and the grid spacing info in the cmap.itp file */
 +    /* start is the position on the line where we start to read the actual cmap grid data from the itp file */
 +    start = start + nn -1;
 +
 +    ft     = strtol(alc[nral], NULL, 10);
 +    nxcmap = strtol(alc[nral+1], NULL, 10);
 +    nycmap = strtol(alc[nral+2], NULL, 10);
 +
 +    /* Check for equal grid spacing in x and y dims */
 +    if (nxcmap != nycmap)
 +    {
 +        gmx_fatal(FARGS, "Not the same grid spacing in x and y for cmap grid: x=%d, y=%d", nxcmap, nycmap);
 +    }
 +
 +    ncmap  = nxcmap*nycmap;
 +    ftype  = ifunc_index(d, ft);
 +    nrfpA  = strtol(alc[6], NULL, 10)*strtol(alc[6], NULL, 10);
 +    nrfpB  = strtol(alc[7], NULL, 10)*strtol(alc[7], NULL, 10);
 +    nrfp   = nrfpA+nrfpB;
 +
 +    /* Allocate memory for the CMAP grid */
 +    bt[F_CMAP].ncmap += nrfp;
 +    srenew(bt[F_CMAP].cmap, bt[F_CMAP].ncmap);
 +
 +    /* Read in CMAP parameters */
 +    sl = 0;
 +    for (i = 0; i < ncmap; i++)
 +    {
 +        while (isspace(*(line+start+sl)))
 +        {
 +            sl++;
 +        }
 +        nn  = sscanf(line+start+sl, " %s ", s);
 +        sl += strlen(s);
 +        bt[F_CMAP].cmap[i+(bt[F_CMAP].ncmap)-nrfp] = strtod(s, NULL);
 +
 +        if (nn == 1)
 +        {
 +            read_cmap++;
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS, "Error in reading cmap parameter for angle %s %s %s %s %s", alc[0], alc[1], alc[2], alc[3], alc[4]);
 +        }
 +
 +    }
 +
 +    /* Check do that we got the number of parameters we expected */
 +    if (read_cmap == nrfpA)
 +    {
 +        for (i = 0; i < ncmap; i++)
 +        {
 +            bt[F_CMAP].cmap[i+ncmap] = bt[F_CMAP].cmap[i];
 +        }
 +    }
 +    else
 +    {
 +        if (read_cmap < nrfpA)
 +        {
 +            warning_error(wi, "Not enough cmap parameters");
 +        }
 +        else if (read_cmap > nrfpA && read_cmap < nrfp)
 +        {
 +            warning_error(wi, "Too many cmap parameters or not enough parameters for topology B");
 +        }
 +        else if (read_cmap > nrfp)
 +        {
 +            warning_error(wi, "Too many cmap parameters");
 +        }
 +    }
 +
 +
 +    /* Set grid spacing and the number of grids (we assume these numbers to be the same for all grids
 +     * so we can safely assign them each time
 +     */
 +    bt[F_CMAP].grid_spacing = nxcmap;            /* Or nycmap, they need to be equal */
 +    bt[F_CMAP].nc           = bt[F_CMAP].nc + 1; /* Since we are incrementing here, we need to subtract later, see (*****) */
 +    nct                     = (nral+1) * bt[F_CMAP].nc;
 +
 +    /* Allocate memory for the cmap_types information */
 +    srenew(bt[F_CMAP].cmap_types, nct);
 +
 +    for (i = 0; (i < nral); i++)
 +    {
 +        if (at && ((p.a[i] = get_bond_atomtype_type(alc[i], bat)) == NOTSET))
 +        {
 +            gmx_fatal(FARGS, "Unknown atomtype %s\n", alc[i]);
 +        }
 +        else if (bat && ((p.a[i] = get_bond_atomtype_type(alc[i], bat)) == NOTSET))
 +        {
 +            gmx_fatal(FARGS, "Unknown bond_atomtype %s\n", alc[i]);
 +        }
 +
 +        /* Assign a grid number to each cmap_type */
 +        bt[F_CMAP].cmap_types[bt[F_CMAP].nct++] = get_bond_atomtype_type(alc[i], bat);
 +    }
 +
 +    /* Assign a type number to this cmap */
 +    bt[F_CMAP].cmap_types[bt[F_CMAP].nct++] = bt[F_CMAP].nc-1; /* Since we inremented earlier, we need to subtrac here, to get the types right (****) */
 +
 +    /* Check for the correct number of atoms (again) */
 +    if (bt[F_CMAP].nct != nct)
 +    {
 +        gmx_fatal(FARGS, "Incorrect number of atom types (%d) in cmap type %d\n", nct, bt[F_CMAP].nc);
 +    }
 +
 +    /* Is this correct?? */
 +    for (i = 0; i < MAXFORCEPARAM; i++)
 +    {
 +        p.c[i] = NOTSET;
 +    }
 +
 +    /* Push the bond to the bondlist */
 +    push_bondtype (&(bt[ftype]), &p, nral, ftype, FALSE, line, wi);
 +}
 +
 +
 +static void push_atom_now(t_symtab *symtab, t_atoms *at, int atomnr,
 +                          int atomicnumber,
 +                          int type, char *ctype, int ptype,
 +                          char *resnumberic,
 +                          char *resname, char *name, real m0, real q0,
 +                          int typeB, char *ctypeB, real mB, real qB)
 +{
 +    int           j, resind = 0, resnr;
 +    unsigned char ric;
 +    int           nr = at->nr;
 +
 +    if (((nr == 0) && (atomnr != 1)) || (nr && (atomnr != at->nr+1)))
 +    {
 +        gmx_fatal(FARGS, "Atoms in the .top are not numbered consecutively from 1 (rather, atomnr = %d, while at->nr = %d)", atomnr, at->nr);
 +    }
 +
 +    j = strlen(resnumberic) - 1;
 +    if (isdigit(resnumberic[j]))
 +    {
 +        ric = ' ';
 +    }
 +    else
 +    {
 +        ric = resnumberic[j];
 +        if (j == 0 || !isdigit(resnumberic[j-1]))
 +        {
 +            gmx_fatal(FARGS, "Invalid residue number '%s' for atom %d",
 +                      resnumberic, atomnr);
 +        }
 +    }
 +    resnr = strtol(resnumberic, NULL, 10);
 +
 +    if (nr > 0)
 +    {
 +        resind = at->atom[nr-1].resind;
 +    }
 +    if (nr == 0 || strcmp(resname, *at->resinfo[resind].name) != 0 ||
 +        resnr != at->resinfo[resind].nr ||
 +        ric   != at->resinfo[resind].ic)
 +    {
 +        if (nr == 0)
 +        {
 +            resind = 0;
 +        }
 +        else
 +        {
 +            resind++;
 +        }
 +        at->nres = resind + 1;
 +        srenew(at->resinfo, at->nres);
 +        at->resinfo[resind].name = put_symtab(symtab, resname);
 +        at->resinfo[resind].nr   = resnr;
 +        at->resinfo[resind].ic   = ric;
 +    }
 +    else
 +    {
 +        resind = at->atom[at->nr-1].resind;
 +    }
 +
 +    /* New atom instance
 +     * get new space for arrays
 +     */
 +    srenew(at->atom, nr+1);
 +    srenew(at->atomname, nr+1);
 +    srenew(at->atomtype, nr+1);
 +    srenew(at->atomtypeB, nr+1);
 +
 +    /* fill the list */
 +    at->atom[nr].type  = type;
 +    at->atom[nr].ptype = ptype;
 +    at->atom[nr].q     = q0;
 +    at->atom[nr].m     = m0;
 +    at->atom[nr].typeB = typeB;
 +    at->atom[nr].qB    = qB;
 +    at->atom[nr].mB    = mB;
 +
 +    at->atom[nr].resind     = resind;
 +    at->atom[nr].atomnumber = atomicnumber;
 +    at->atomname[nr]        = put_symtab(symtab, name);
 +    at->atomtype[nr]        = put_symtab(symtab, ctype);
 +    at->atomtypeB[nr]       = put_symtab(symtab, ctypeB);
 +    at->nr++;
 +}
 +
 +void push_cg(t_block *block, int *lastindex, int index, int a)
 +{
 +    if (debug)
 +    {
 +        fprintf (debug, "Index %d, Atom %d\n", index, a);
 +    }
 +
 +    if (((block->nr) && (*lastindex != index)) || (!block->nr))
 +    {
 +        /* add a new block */
 +        block->nr++;
 +        srenew(block->index, block->nr+1);
 +    }
 +    block->index[block->nr] = a + 1;
 +    *lastindex              = index;
 +}
 +
 +void push_atom(t_symtab *symtab, t_block *cgs,
 +               t_atoms *at, gpp_atomtype_t atype, char *line, int *lastcg,
 +               warninp_t wi)
 +{
 +    int           nr, ptype;
 +    int           cgnumber, atomnr, type, typeB, nscan;
 +    char          id[STRLEN], ctype[STRLEN], ctypeB[STRLEN],
 +                  resnumberic[STRLEN], resname[STRLEN], name[STRLEN], check[STRLEN];
 +    double        m, q, mb, qb;
 +    real          m0, q0, mB, qB;
 +
 +    /* Make a shortcut for writing in this molecule  */
 +    nr = at->nr;
 +
 +    /* Fixed parameters */
 +    if (sscanf(line, "%s%s%s%s%s%d",
 +               id, ctype, resnumberic, resname, name, &cgnumber) != 6)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +    sscanf(id, "%d", &atomnr);
 +    if ((type  = get_atomtype_type(ctype, atype)) == NOTSET)
 +    {
 +        gmx_fatal(FARGS, "Atomtype %s not found", ctype);
 +    }
 +    ptype = get_atomtype_ptype(type, atype);
 +
 +    /* Set default from type */
 +    q0    = get_atomtype_qA(type, atype);
 +    m0    = get_atomtype_massA(type, atype);
 +    typeB = type;
 +    qB    = q0;
 +    mB    = m0;
 +
 +    /* Optional parameters */
 +    nscan = sscanf(line, "%*s%*s%*s%*s%*s%*s%lf%lf%s%lf%lf%s",
 +                   &q, &m, ctypeB, &qb, &mb, check);
 +
 +    /* Nasty switch that falls thru all the way down! */
 +    if (nscan > 0)
 +    {
 +        q0 = qB = q;
 +        if (nscan > 1)
 +        {
 +            m0 = mB = m;
 +            if (nscan > 2)
 +            {
 +                if ((typeB = get_atomtype_type(ctypeB, atype)) == NOTSET)
 +                {
 +                    gmx_fatal(FARGS, "Atomtype %s not found", ctypeB);
 +                }
 +                qB = get_atomtype_qA(typeB, atype);
 +                mB = get_atomtype_massA(typeB, atype);
 +                if (nscan > 3)
 +                {
 +                    qB = qb;
 +                    if (nscan > 4)
 +                    {
 +                        mB = mb;
 +                        if (nscan > 5)
 +                        {
 +                            warning_error(wi, "Too many parameters");
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "mB=%g, qB=%g, typeB=%d\n", mB, qB, typeB);
 +    }
 +
 +    push_cg(cgs, lastcg, cgnumber, nr);
 +
 +    push_atom_now(symtab, at, atomnr, get_atomtype_atomnumber(type, atype),
 +                  type, ctype, ptype, resnumberic,
 +                  resname, name, m0, q0, typeB,
 +                  typeB == type ? ctype : ctypeB, mB, qB);
 +}
 +
 +void push_molt(t_symtab *symtab, int *nmol, t_molinfo **mol, char *line,
 +               warninp_t wi)
 +{
 +    char       type[STRLEN];
 +    int        nrexcl, i;
 +    t_molinfo *newmol;
 +
 +    if ((sscanf(line, "%s%d", type, &nrexcl)) != 2)
 +    {
 +        warning_error(wi, "Expected a molecule type name and nrexcl");
 +    }
 +
 +    /* Test if this atomtype overwrites another */
 +    i = 0;
 +    while (i < *nmol)
 +    {
 +        if (gmx_strcasecmp(*((*mol)[i].name), type) == 0)
 +        {
 +            gmx_fatal(FARGS, "moleculetype %s is redefined", type);
 +        }
 +        i++;
 +    }
 +
 +    (*nmol)++;
 +    srenew(*mol, *nmol);
 +    newmol = &((*mol)[*nmol-1]);
 +    init_molinfo(newmol);
 +
 +    /* Fill in the values */
 +    newmol->name     = put_symtab(symtab, type);
 +    newmol->nrexcl   = nrexcl;
 +    newmol->excl_set = FALSE;
 +}
 +
 +static gmx_bool default_nb_params(int ftype, t_params bt[], t_atoms *at,
 +                                  t_param *p, int c_start, gmx_bool bB, gmx_bool bGenPairs)
 +{
 +    int          i, j, ti, tj, ntype;
 +    gmx_bool     bFound;
 +    t_param     *pi    = NULL;
 +    int          nr    = bt[ftype].nr;
 +    int          nral  = NRAL(ftype);
 +    int          nrfp  = interaction_function[ftype].nrfpA;
 +    int          nrfpB = interaction_function[ftype].nrfpB;
 +
 +    if ((!bB && nrfp == 0) || (bB && nrfpB == 0))
 +    {
 +        return TRUE;
 +    }
 +
 +    bFound = FALSE;
 +    if (bGenPairs)
 +    {
 +        /* First test the generated-pair position to save
 +         * time when we have 1000*1000 entries for e.g. OPLS...
 +         */
 +        ntype = sqrt(nr);
 +        if (bB)
 +        {
 +            ti = at->atom[p->a[0]].typeB;
 +            tj = at->atom[p->a[1]].typeB;
 +        }
 +        else
 +        {
 +            ti = at->atom[p->a[0]].type;
 +            tj = at->atom[p->a[1]].type;
 +        }
 +        pi     = &(bt[ftype].param[ntype*ti+tj]);
 +        bFound = ((ti == pi->a[0]) && (tj == pi->a[1]));
 +    }
 +
 +    /* Search explicitly if we didnt find it */
 +    if (!bFound)
 +    {
 +        for (i = 0; ((i < nr) && !bFound); i++)
 +        {
 +            pi = &(bt[ftype].param[i]);
 +            if (bB)
 +            {
 +                for (j = 0; ((j < nral) &&
 +                             (at->atom[p->a[j]].typeB == pi->a[j])); j++)
 +                {
 +                    ;
 +                }
 +            }
 +            else
 +            {
 +                for (j = 0; ((j < nral) &&
 +                             (at->atom[p->a[j]].type == pi->a[j])); j++)
 +                {
 +                    ;
 +                }
 +            }
 +            bFound = (j == nral);
 +        }
 +    }
 +
 +    if (bFound)
 +    {
 +        if (bB)
 +        {
 +            if (nrfp+nrfpB > MAXFORCEPARAM)
 +            {
 +                gmx_incons("Too many force parameters");
 +            }
 +            for (j = c_start; (j < nrfpB); j++)
 +            {
 +                p->c[nrfp+j] = pi->c[j];
 +            }
 +        }
 +        else
 +        {
 +            for (j = c_start; (j < nrfp); j++)
 +            {
 +                p->c[j] = pi->c[j];
 +            }
 +        }
 +    }
 +    else
 +    {
 +        for (j = c_start; (j < nrfp); j++)
 +        {
 +            p->c[j] = 0.0;
 +        }
 +    }
 +    return bFound;
 +}
 +
 +static gmx_bool default_cmap_params(t_params bondtype[],
 +                                    t_atoms *at, gpp_atomtype_t atype,
 +                                    t_param *p, gmx_bool bB,
 +                                    int *cmap_type, int *nparam_def)
 +{
 +    int      i, j, nparam_found;
 +    int      ct;
 +    gmx_bool bFound = FALSE;
 +
 +    nparam_found = 0;
 +    ct           = 0;
 +
 +    /* Match the current cmap angle against the list of cmap_types */
 +    for (i = 0; i < bondtype[F_CMAP].nct && !bFound; i += 6)
 +    {
 +        if (bB)
 +        {
 +
 +        }
 +        else
 +        {
 +            if (
 +                (get_atomtype_batype(at->atom[p->a[0]].type, atype) == bondtype[F_CMAP].cmap_types[i])   &&
 +                (get_atomtype_batype(at->atom[p->a[1]].type, atype) == bondtype[F_CMAP].cmap_types[i+1]) &&
 +                (get_atomtype_batype(at->atom[p->a[2]].type, atype) == bondtype[F_CMAP].cmap_types[i+2]) &&
 +                (get_atomtype_batype(at->atom[p->a[3]].type, atype) == bondtype[F_CMAP].cmap_types[i+3]) &&
 +                (get_atomtype_batype(at->atom[p->a[4]].type, atype) == bondtype[F_CMAP].cmap_types[i+4]))
 +            {
 +                /* Found cmap torsion */
 +                bFound       = TRUE;
 +                ct           = bondtype[F_CMAP].cmap_types[i+5];
 +                nparam_found = 1;
 +            }
 +        }
 +    }
 +
 +    /* If we did not find a matching type for this cmap torsion */
 +    if (!bFound)
 +    {
 +        gmx_fatal(FARGS, "Unknown cmap torsion between atoms %d %d %d %d %d\n",
 +                  p->a[0]+1, p->a[1]+1, p->a[2]+1, p->a[3]+1, p->a[4]+1);
 +    }
 +
 +    *nparam_def = nparam_found;
 +    *cmap_type  = ct;
 +
 +    return bFound;
 +}
 +
 +static gmx_bool default_params(int ftype, t_params bt[],
 +                               t_atoms *at, gpp_atomtype_t atype,
 +                               t_param *p, gmx_bool bB,
 +                               t_param **param_def,
 +                               int *nparam_def)
 +{
 +    int          i, j, nparam_found;
 +    gmx_bool     bFound, bSame;
 +    t_param     *pi    = NULL;
 +    t_param     *pj    = NULL;
 +    int          nr    = bt[ftype].nr;
 +    int          nral  = NRAL(ftype);
 +    int          nrfpA = interaction_function[ftype].nrfpA;
 +    int          nrfpB = interaction_function[ftype].nrfpB;
 +
 +    if ((!bB && nrfpA == 0) || (bB && nrfpB == 0))
 +    {
 +        return TRUE;
 +    }
 +
 +
 +    /* We allow wildcards now. The first type (with or without wildcards) that
 +     * fits is used, so you should probably put the wildcarded bondtypes
 +     * at the end of each section.
 +     */
 +    bFound       = FALSE;
 +    nparam_found = 0;
 +    /* OPLS uses 1000s of dihedraltypes, so in order to speed up the scanning we have a
 +     * special case for this. Check for B state outside loop to speed it up.
 +     */
 +    if (ftype == F_PDIHS || ftype == F_RBDIHS || ftype == F_IDIHS || ftype == F_PIDIHS)
 +    {
 +        if (bB)
 +        {
 +            for (i = 0; ((i < nr) && !bFound); i++)
 +            {
 +                pi     = &(bt[ftype].param[i]);
 +                bFound =
 +                    (
 +                        ((pi->AI == -1) || (get_atomtype_batype(at->atom[p->AI].typeB, atype) == pi->AI)) &&
 +                        ((pi->AJ == -1) || (get_atomtype_batype(at->atom[p->AJ].typeB, atype) == pi->AJ)) &&
 +                        ((pi->AK == -1) || (get_atomtype_batype(at->atom[p->AK].typeB, atype) == pi->AK)) &&
 +                        ((pi->AL == -1) || (get_atomtype_batype(at->atom[p->AL].typeB, atype) == pi->AL))
 +                    );
 +            }
 +        }
 +        else
 +        {
 +            /* State A */
 +            for (i = 0; ((i < nr) && !bFound); i++)
 +            {
 +                pi     = &(bt[ftype].param[i]);
 +                bFound =
 +                    (
 +                        ((pi->AI == -1) || (get_atomtype_batype(at->atom[p->AI].type, atype) == pi->AI)) &&
 +                        ((pi->AJ == -1) || (get_atomtype_batype(at->atom[p->AJ].type, atype) == pi->AJ)) &&
 +                        ((pi->AK == -1) || (get_atomtype_batype(at->atom[p->AK].type, atype) == pi->AK)) &&
 +                        ((pi->AL == -1) || (get_atomtype_batype(at->atom[p->AL].type, atype) == pi->AL))
 +                    );
 +            }
 +        }
 +        /* Find additional matches for this dihedral - necessary for ftype==9 which is used e.g. for charmm.
 +         * The rules in that case is that additional matches HAVE to be on adjacent lines!
 +         */
 +        if (bFound == TRUE)
 +        {
 +            nparam_found++;
 +            bSame = TRUE;
 +            /* Continue from current i value */
 +            for (j = i+1; j < nr && bSame; j += 2)
 +            {
 +                pj    = &(bt[ftype].param[j]);
 +                bSame = (pi->AI == pj->AI && pi->AJ == pj->AJ && pi->AK == pj->AK && pi->AL == pj->AL);
 +                if (bSame)
 +                {
 +                    nparam_found++;
 +                }
 +                /* nparam_found will be increased as long as the numbers match */
 +            }
 +        }
 +    }
 +    else   /* Not a dihedral */
 +    {
 +        for (i = 0; ((i < nr) && !bFound); i++)
 +        {
 +            pi = &(bt[ftype].param[i]);
 +            if (bB)
 +            {
 +                for (j = 0; ((j < nral) &&
 +                             (get_atomtype_batype(at->atom[p->a[j]].typeB, atype) == pi->a[j])); j++)
 +                {
 +                    ;
 +                }
 +            }
 +            else
 +            {
 +                for (j = 0; ((j < nral) &&
 +                             (get_atomtype_batype(at->atom[p->a[j]].type, atype) == pi->a[j])); j++)
 +                {
 +                    ;
 +                }
 +            }
 +            bFound = (j == nral);
 +        }
 +        if (bFound)
 +        {
 +            nparam_found = 1;
 +        }
 +    }
 +
 +    *param_def  = pi;
 +    *nparam_def = nparam_found;
 +
 +    return bFound;
 +}
 +
 +
 +
 +void push_bond(directive d, t_params bondtype[], t_params bond[],
 +               t_atoms *at, gpp_atomtype_t atype, char *line,
 +               gmx_bool bBonded, gmx_bool bGenPairs, real fudgeQQ,
 +               gmx_bool bZero, gmx_bool *bWarn_copy_A_B,
 +               warninp_t wi)
 +{
 +    const char  *aaformat[MAXATOMLIST] = {
 +        "%d%d",
 +        "%d%d%d",
 +        "%d%d%d%d",
 +        "%d%d%d%d%d",
 +        "%d%d%d%d%d%d",
 +        "%d%d%d%d%d%d%d"
 +    };
 +    const char  *asformat[MAXATOMLIST] = {
 +        "%*s%*s",
 +        "%*s%*s%*s",
 +        "%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s%*s"
 +    };
 +    const char  *ccformat = "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf";
 +    int          nr, i, j, nral, nral_fmt, nread, ftype;
 +    char         format[STRLEN];
 +    /* One force parameter more, so we can check if we read too many */
 +    double       cc[MAXFORCEPARAM+1];
 +    int          aa[MAXATOMLIST+1];
 +    t_param      param, paramB, *param_defA, *param_defB;
 +    gmx_bool     bFoundA = FALSE, bFoundB = FALSE, bDef, bPert, bSwapParity = FALSE;
 +    int          nparam_defA, nparam_defB;
 +    char         errbuf[256];
 +
 +    nparam_defA = nparam_defB = 0;
 +
 +    ftype = ifunc_index(d, 1);
 +    nral  = NRAL(ftype);
 +    for (j = 0; j < MAXATOMLIST; j++)
 +    {
 +        aa[j] = NOTSET;
 +    }
 +    bDef = (NRFP(ftype) > 0);
 +
 +    if (ftype == F_SETTLE)
 +    {
 +        /* SETTLE acts on 3 atoms, but the topology format only specifies
 +         * the first atom (for historical reasons).
 +         */
 +        nral_fmt = 1;
 +    }
 +    else
 +    {
 +        nral_fmt = nral;
 +    }
 +
 +    nread = sscanf(line, aaformat[nral_fmt-1],
 +                   &aa[0], &aa[1], &aa[2], &aa[3], &aa[4], &aa[5]);
 +
 +    if (ftype == F_SETTLE)
 +    {
 +        aa[3] = aa[1];
 +        aa[1] = aa[0] + 1;
 +        aa[2] = aa[0] + 2;
 +    }
 +
 +    if (nread < nral_fmt)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +    else if (nread > nral_fmt)
 +    {
 +        /* this is a hack to allow for virtual sites with swapped parity */
 +        bSwapParity = (aa[nral] < 0);
 +        if (bSwapParity)
 +        {
 +            aa[nral] = -aa[nral];
 +        }
 +        ftype = ifunc_index(d, aa[nral]);
 +        if (bSwapParity)
 +        {
 +            switch (ftype)
 +            {
 +                case F_VSITE3FAD:
 +                case F_VSITE3OUT:
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "Negative function types only allowed for %s and %s",
 +                              interaction_function[F_VSITE3FAD].longname,
 +                              interaction_function[F_VSITE3OUT].longname);
 +            }
 +        }
 +    }
 +
 +
 +    /* Check for double atoms and atoms out of bounds */
 +    for (i = 0; (i < nral); i++)
 +    {
 +        if (aa[i] < 1 || aa[i] > at->nr)
 +        {
 +            gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                      "Atom index (%d) in %s out of bounds (1-%d).\n"
 +                      "This probably means that you have inserted topology section \"%s\"\n"
 +                      "in a part belonging to a different molecule than you intended to.\n"
 +                      "In that case move the \"%s\" section to the right molecule.",
 +                      get_warning_file(wi), get_warning_line(wi),
 +                      aa[i], dir2str(d), at->nr, dir2str(d), dir2str(d));
 +        }
 +        for (j = i+1; (j < nral); j++)
 +        {
 +            if (aa[i] == aa[j])
 +            {
 +                sprintf(errbuf, "Duplicate atom index (%d) in %s", aa[i], dir2str(d));
 +                warning(wi, errbuf);
 +            }
 +        }
 +    }
 +
 +    /* default force parameters  */
 +    for (j = 0; (j < MAXATOMLIST); j++)
 +    {
 +        param.a[j] = aa[j]-1;
 +    }
 +    for (j = 0; (j < MAXFORCEPARAM); j++)
 +    {
 +        param.c[j] = 0.0;
 +    }
 +
 +    /* Get force params for normal and free energy perturbation
 +     * studies, as determined by types!
 +     */
 +
 +    if (bBonded)
 +    {
 +        bFoundA = default_params(ftype, bondtype, at, atype, &param, FALSE, &param_defA, &nparam_defA);
 +        if (bFoundA)
 +        {
 +            /* Copy the A-state and B-state default parameters. */
 +            assert(NRFPA(ftype)+NRFPB(ftype) <= MAXFORCEPARAM);
 +            for (j = 0; (j < NRFPA(ftype)+NRFPB(ftype)); j++)
 +            {
 +                param.c[j] = param_defA->c[j];
 +            }
 +        }
 +        bFoundB = default_params(ftype, bondtype, at, atype, &param, TRUE, &param_defB, &nparam_defB);
 +        if (bFoundB)
 +        {
 +            /* Copy only the B-state default parameters */
 +            for (j = NRFPA(ftype); (j < NRFP(ftype)); j++)
 +            {
 +                param.c[j] = param_defB->c[j];
 +            }
 +        }
 +    }
 +    else if (ftype == F_LJ14)
 +    {
 +        bFoundA = default_nb_params(ftype, bondtype, at, &param, 0, FALSE, bGenPairs);
 +        bFoundB = default_nb_params(ftype, bondtype, at, &param, 0, TRUE, bGenPairs);
 +    }
 +    else if (ftype == F_LJC14_Q)
 +    {
 +        param.c[0] = fudgeQQ;
 +        /* Fill in the A-state charges as default parameters */
 +        param.c[1] = at->atom[param.a[0]].q;
 +        param.c[2] = at->atom[param.a[1]].q;
 +        /* The default LJ parameters are the standard 1-4 parameters */
 +        bFoundA = default_nb_params(F_LJ14, bondtype, at, &param, 3, FALSE, bGenPairs);
 +        bFoundB = TRUE;
 +    }
 +    else if (ftype == F_LJC_PAIRS_NB)
 +    {
 +        /* Defaults are not supported here */
 +        bFoundA = FALSE;
 +        bFoundB = TRUE;
 +    }
 +    else
 +    {
 +        gmx_incons("Unknown function type in push_bond");
 +    }
 +
 +    if (nread > nral_fmt)
 +    {
 +        /* Manually specified parameters - in this case we discard multiple torsion info! */
 +
 +        strcpy(format, asformat[nral_fmt-1]);
 +        strcat(format, ccformat);
 +
 +        nread = sscanf(line, format, &cc[0], &cc[1], &cc[2], &cc[3], &cc[4], &cc[5],
 +                       &cc[6], &cc[7], &cc[8], &cc[9], &cc[10], &cc[11], &cc[12]);
 +
 +        if ((nread == NRFPA(ftype)) && (NRFPB(ftype) != 0))
 +        {
 +            /* We only have to issue a warning if these atoms are perturbed! */
 +            bPert = FALSE;
 +            for (j = 0; (j < nral); j++)
 +            {
 +                bPert = bPert || PERTURBED(at->atom[param.a[j]]);
 +            }
 +
 +            if (bPert && *bWarn_copy_A_B)
 +            {
 +                sprintf(errbuf,
 +                        "Some parameters for bonded interaction involving perturbed atoms are specified explicitly in state A, but not B - copying A to B");
 +                warning(wi, errbuf);
 +                *bWarn_copy_A_B = FALSE;
 +            }
 +
 +            /* If only the A parameters were specified, copy them to the B state */
 +            /* The B-state parameters correspond to the first nrfpB
 +             * A-state parameters.
 +             */
 +            for (j = 0; (j < NRFPB(ftype)); j++)
 +            {
 +                cc[nread++] = cc[j];
 +            }
 +        }
 +
 +        /* If nread was 0 or EOF, no parameters were read => use defaults.
 +         * If nread was nrfpA we copied above so nread=nrfp.
 +         * If nread was nrfp we are cool.
 +         * For F_LJC14_Q we allow supplying fudgeQQ only.
 +         * Anything else is an error!
 +         */
 +        if ((nread != 0) && (nread != EOF) && (nread != NRFP(ftype)) &&
 +            !(ftype == F_LJC14_Q && nread == 1))
 +        {
 +            gmx_fatal(FARGS, "Incorrect number of parameters - found %d, expected %d or %d for %s.",
 +                      nread, NRFPA(ftype), NRFP(ftype),
 +                      interaction_function[ftype].longname);
 +        }
 +
 +        for (j = 0; (j < nread); j++)
 +        {
 +            param.c[j] = cc[j];
 +        }
 +
 +        /* Check whether we have to use the defaults */
 +        if (nread == NRFP(ftype))
 +        {
 +            bDef = FALSE;
 +        }
 +    }
 +    else
 +    {
 +        nread = 0;
 +    }
 +    /* nread now holds the number of force parameters read! */
 +
 +    if (bDef)
 +    {
 +        /* Use defaults */
 +        /* When we have multiple terms it would be very dangerous to allow perturbations to a different atom type! */
 +        if (ftype == F_PDIHS)
 +        {
 +            if ((nparam_defA != nparam_defB) || ((nparam_defA > 1 || nparam_defB > 1) && (param_defA != param_defB)))
 +            {
 +                sprintf(errbuf,
 +                        "Cannot automatically perturb a torsion with multiple terms to different form.\n"
 +                        "Please specify perturbed parameters manually for this torsion in your topology!");
 +                warning_error(wi, errbuf);
 +            }
 +        }
 +
 +        if (nread > 0 && nread < NRFPA(ftype))
 +        {
 +            /* Issue an error, do not use defaults */
 +            sprintf(errbuf, "Not enough parameters, there should be at least %d (or 0 for defaults)", NRFPA(ftype));
 +            warning_error(wi, errbuf);
 +        }
 +
 +        if (nread == 0 || nread == EOF)
 +        {
 +            if (!bFoundA)
 +            {
 +                if (interaction_function[ftype].flags & IF_VSITE)
 +                {
 +                    /* set them to NOTSET, will be calculated later */
 +                    for (j = 0; (j < MAXFORCEPARAM); j++)
 +                    {
 +                        param.c[j] = NOTSET;
 +                    }
 +
 +                    if (bSwapParity)
 +                    {
 +                        param.C1 = -1; /* flag to swap parity of vsite construction */
 +                    }
 +                }
 +                else
 +                {
 +                    if (bZero)
 +                    {
 +                        fprintf(stderr, "NOTE: No default %s types, using zeroes\n",
 +                                interaction_function[ftype].longname);
 +                    }
 +                    else
 +                    {
 +                        sprintf(errbuf, "No default %s types", interaction_function[ftype].longname);
 +                        warning_error(wi, errbuf);
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                if (bSwapParity)
 +                {
 +                    switch (ftype)
 +                    {
 +                        case F_VSITE3FAD:
 +                            param.C0 = 360 - param.C0;
 +                            break;
 +                        case F_VSITE3OUT:
 +                            param.C2 = -param.C2;
 +                            break;
 +                    }
 +                }
 +            }
 +            if (!bFoundB)
 +            {
 +                /* We only have to issue a warning if these atoms are perturbed! */
 +                bPert = FALSE;
 +                for (j = 0; (j < nral); j++)
 +                {
 +                    bPert = bPert || PERTURBED(at->atom[param.a[j]]);
 +                }
 +
 +                if (bPert)
 +                {
 +                    sprintf(errbuf, "No default %s types for perturbed atoms, "
 +                            "using normal values", interaction_function[ftype].longname);
 +                    warning(wi, errbuf);
 +                }
 +            }
 +        }
 +    }
 +
 +    if ((ftype == F_PDIHS || ftype == F_ANGRES || ftype == F_ANGRESZ)
 +        && param.c[5] != param.c[2])
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             %s multiplicity can not be perturbed %f!=%f",
 +                  get_warning_file(wi), get_warning_line(wi),
 +                  interaction_function[ftype].longname,
 +                  param.c[2], param.c[5]);
 +    }
 +
 +    if (IS_TABULATED(ftype) && param.c[2] != param.c[0])
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             %s table number can not be perturbed %d!=%d",
 +                  get_warning_file(wi), get_warning_line(wi),
 +                  interaction_function[ftype].longname,
 +                  (int)(param.c[0]+0.5), (int)(param.c[2]+0.5));
 +    }
 +
 +    /* Dont add R-B dihedrals where all parameters are zero (no interaction) */
 +    if (ftype == F_RBDIHS)
 +    {
 +        nr = 0;
 +        for (i = 0; i < NRFP(ftype); i++)
 +        {
 +            if (param.c[i] != 0)
 +            {
 +                nr++;
 +            }
 +        }
 +        if (nr == 0)
 +        {
 +            return;
 +        }
 +    }
 +
 +    /* Put the values in the appropriate arrays */
 +    add_param_to_list (&bond[ftype], &param);
 +
 +    /* Push additional torsions from FF for ftype==9 if we have them.
 +     * We have already checked that the A/B states do not differ in this case,
 +     * so we do not have to double-check that again, or the vsite stuff.
 +     * In addition, those torsions cannot be automatically perturbed.
 +     */
 +    if (bDef && ftype == F_PDIHS)
 +    {
 +        for (i = 1; i < nparam_defA; i++)
 +        {
 +            /* Advance pointer! */
 +            param_defA += 2;
 +            for (j = 0; (j < NRFPA(ftype)+NRFPB(ftype)); j++)
 +            {
 +                param.c[j] = param_defA->c[j];
 +            }
 +            /* And push the next term for this torsion */
 +            add_param_to_list (&bond[ftype], &param);
 +        }
 +    }
 +}
 +
 +void push_cmap(directive d, t_params bondtype[], t_params bond[],
 +               t_atoms *at, gpp_atomtype_t atype, char *line,
 +               warninp_t wi)
 +{
 +    const char *aaformat[MAXATOMLIST+1] =
 +    {
 +        "%d",
 +        "%d%d",
 +        "%d%d%d",
 +        "%d%d%d%d",
 +        "%d%d%d%d%d",
 +        "%d%d%d%d%d%d",
 +        "%d%d%d%d%d%d%d"
 +    };
 +
 +    int      i, j, nr, ftype, nral, nread, ncmap_params;
 +    int      cmap_type;
 +    int      aa[MAXATOMLIST+1];
 +    char     errbuf[256];
 +    gmx_bool bFound;
 +    t_param  param, paramB, *param_defA, *param_defB;
 +
 +    ftype        = ifunc_index(d, 1);
 +    nral         = NRAL(ftype);
 +    nr           = bondtype[ftype].nr;
 +    ncmap_params = 0;
 +
 +    nread = sscanf(line, aaformat[nral-1],
 +                   &aa[0], &aa[1], &aa[2], &aa[3], &aa[4], &aa[5]);
 +
 +    if (nread < nral)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +    else if (nread == nral)
 +    {
 +        ftype = ifunc_index(d, 1);
 +    }
 +
 +    /* Check for double atoms and atoms out of bounds */
 +    for (i = 0; i < nral; i++)
 +    {
 +        if (aa[i] < 1 || aa[i] > at->nr)
 +        {
 +            gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                      "Atom index (%d) in %s out of bounds (1-%d).\n"
 +                      "This probably means that you have inserted topology section \"%s\"\n"
 +                      "in a part belonging to a different molecule than you intended to.\n"
 +                      "In that case move the \"%s\" section to the right molecule.",
 +                      get_warning_file(wi), get_warning_line(wi),
 +                      aa[i], dir2str(d), at->nr, dir2str(d), dir2str(d));
 +        }
 +
 +        for (j = i+1; (j < nral); j++)
 +        {
 +            if (aa[i] == aa[j])
 +            {
 +                sprintf(errbuf, "Duplicate atom index (%d) in %s", aa[i], dir2str(d));
 +                warning(wi, errbuf);
 +            }
 +        }
 +    }
 +
 +    /* default force parameters  */
 +    for (j = 0; (j < MAXATOMLIST); j++)
 +    {
 +        param.a[j] = aa[j]-1;
 +    }
 +    for (j = 0; (j < MAXFORCEPARAM); j++)
 +    {
 +        param.c[j] = 0.0;
 +    }
 +
 +    /* Get the cmap type for this cmap angle */
 +    bFound = default_cmap_params(bondtype, at, atype, &param, FALSE, &cmap_type, &ncmap_params);
 +
 +    /* We want exactly one parameter (the cmap type in state A (currently no state B) back */
 +    if (bFound && ncmap_params == 1)
 +    {
 +        /* Put the values in the appropriate arrays */
 +        param.c[0] = cmap_type;
 +        add_param_to_list(&bond[ftype], &param);
 +    }
 +    else
 +    {
 +        /* This is essentially the same check as in default_cmap_params() done one more time */
 +        gmx_fatal(FARGS, "Unable to assign a cmap type to torsion %d %d %d %d and %d\n",
 +                  param.a[0]+1, param.a[1]+1, param.a[2]+1, param.a[3]+1, param.a[4]+1);
 +    }
 +}
 +
 +
 +
 +void push_vsitesn(directive d, t_params bond[],
 +                  t_atoms *at, char *line,
 +                  warninp_t wi)
 +{
 +    char   *ptr;
 +    int     type, ftype, j, n, ret, nj, a;
 +    int    *atc    = NULL;
 +    double *weight = NULL, weight_tot;
 +    t_param param;
 +
 +    /* default force parameters  */
 +    for (j = 0; (j < MAXATOMLIST); j++)
 +    {
 +        param.a[j] = NOTSET;
 +    }
 +    for (j = 0; (j < MAXFORCEPARAM); j++)
 +    {
 +        param.c[j] = 0.0;
 +    }
 +
 +    ptr  = line;
 +    ret  = sscanf(ptr, "%d%n", &a, &n);
 +    ptr += n;
 +    if (ret == 0)
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             Expected an atom index in section \"%s\"",
 +                  get_warning_file(wi), get_warning_line(wi),
 +                  dir2str(d));
 +    }
 +
 +    param.a[0] = a - 1;
 +
 +    ret   = sscanf(ptr, "%d%n", &type, &n);
 +    ptr  += n;
 +    ftype = ifunc_index(d, type);
 +
 +    weight_tot = 0;
 +    nj         = 0;
 +    do
 +    {
 +        ret  = sscanf(ptr, "%d%n", &a, &n);
 +        ptr += n;
 +        if (ret > 0)
 +        {
 +            if (nj % 20 == 0)
 +            {
 +                srenew(atc, nj+20);
 +                srenew(weight, nj+20);
 +            }
 +            atc[nj] = a - 1;
 +            switch (type)
 +            {
 +                case 1:
 +                    weight[nj] = 1;
 +                    break;
 +                case 2:
 +                    /* Here we use the A-state mass as a parameter.
 +                     * Note that the B-state mass has no influence.
 +                     */
 +                    weight[nj] = at->atom[atc[nj]].m;
 +                    break;
 +                case 3:
 +                    weight[nj] = -1;
 +                    ret        = sscanf(ptr, "%lf%n", &(weight[nj]), &n);
 +                    ptr       += n;
 +                    if (weight[nj] < 0)
 +                    {
 +                        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                                  "             No weight or negative weight found for vsiten constructing atom %d (atom index %d)",
 +                                  get_warning_file(wi), get_warning_line(wi),
 +                                  nj+1, atc[nj]+1);
 +                    }
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "Unknown vsiten type %d", type);
 +            }
 +            weight_tot += weight[nj];
 +            nj++;
 +        }
 +    }
 +    while (ret > 0);
 +
 +    if (nj == 0)
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             Expected more than one atom index in section \"%s\"",
 +                  get_warning_file(wi), get_warning_line(wi),
 +                  dir2str(d));
 +    }
 +
 +    if (weight_tot == 0)
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             The total mass of the construting atoms is zero",
 +                  get_warning_file(wi), get_warning_line(wi));
 +    }
 +
 +    for (j = 0; j < nj; j++)
 +    {
 +        param.a[1] = atc[j];
 +        param.c[0] = nj;
 +        param.c[1] = weight[j]/weight_tot;
 +        /* Put the values in the appropriate arrays */
 +        add_param_to_list (&bond[ftype], &param);
 +    }
 +
 +    sfree(atc);
 +    sfree(weight);
 +}
 +
 +void push_mol(int nrmols, t_molinfo mols[], char *pline, int *whichmol,
 +              int *nrcopies,
 +              warninp_t wi)
 +{
 +    int  i, copies;
 +    char type[STRLEN];
 +
 +    *nrcopies = 0;
 +    if (sscanf(pline, "%s%d", type, &copies) != 2)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +
 +    /* search moleculename */
 +    for (i = 0; ((i < nrmols) && gmx_strcasecmp(type, *(mols[i].name))); i++)
 +    {
 +        ;
 +    }
 +
 +    if (i < nrmols)
 +    {
 +        *nrcopies        = copies;
 +        *whichmol        = i;
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "No such moleculetype %s", type);
 +    }
 +}
 +
 +void init_block2(t_block2 *b2, int natom)
 +{
 +    int i;
 +
 +    b2->nr = natom;
 +    snew(b2->nra, b2->nr);
 +    snew(b2->a, b2->nr);
 +    for (i = 0; (i < b2->nr); i++)
 +    {
 +        b2->a[i] = NULL;
 +    }
 +}
 +
 +void done_block2(t_block2 *b2)
 +{
 +    int i;
 +
 +    if (b2->nr)
 +    {
 +        for (i = 0; (i < b2->nr); i++)
 +        {
 +            sfree(b2->a[i]);
 +        }
 +        sfree(b2->a);
 +        sfree(b2->nra);
 +        b2->nr = 0;
 +    }
 +}
 +
 +void push_excl(char *line, t_block2 *b2)
 +{
 +    int  i, j;
 +    int  n;
 +    char base[STRLEN], format[STRLEN];
 +
 +    if (sscanf(line, "%d", &i) == 0)
 +    {
 +        return;
 +    }
 +
 +    if ((1 <= i) && (i <= b2->nr))
 +    {
 +        i--;
 +    }
 +    else
 +    {
 +        if (debug)
 +        {
 +            fprintf(debug, "Unbound atom %d\n", i-1);
 +        }
 +        return;
 +    }
 +    strcpy(base, "%*d");
 +    do
 +    {
 +        strcpy(format, base);
 +        strcat(format, "%d");
 +        n = sscanf(line, format, &j);
 +        if (n == 1)
 +        {
 +            if ((1 <= j) && (j <= b2->nr))
 +            {
 +                j--;
 +                srenew(b2->a[i], ++(b2->nra[i]));
 +                b2->a[i][b2->nra[i]-1] = j;
 +                /* also add the reverse exclusion! */
 +                srenew(b2->a[j], ++(b2->nra[j]));
 +                b2->a[j][b2->nra[j]-1] = i;
 +                strcat(base, "%*d");
 +            }
 +            else
 +            {
 +                gmx_fatal(FARGS, "Invalid Atomnr j: %d, b2->nr: %d\n", j, b2->nr);
 +            }
 +        }
 +    }
 +    while (n == 1);
 +}
 +
 +void b_to_b2(t_blocka *b, t_block2 *b2)
 +{
 +    int     i;
 +    atom_id j, a;
 +
 +    for (i = 0; (i < b->nr); i++)
 +    {
 +        for (j = b->index[i]; (j < b->index[i+1]); j++)
 +        {
 +            a = b->a[j];
 +            srenew(b2->a[i], ++b2->nra[i]);
 +            b2->a[i][b2->nra[i]-1] = a;
 +        }
 +    }
 +}
 +
 +void b2_to_b(t_block2 *b2, t_blocka *b)
 +{
 +    int     i, nra;
 +    atom_id j;
 +
 +    nra = 0;
 +    for (i = 0; (i < b2->nr); i++)
 +    {
 +        b->index[i] = nra;
 +        for (j = 0; (j < b2->nra[i]); j++)
 +        {
 +            b->a[nra+j] = b2->a[i][j];
 +        }
 +        nra += b2->nra[i];
 +    }
 +    /* terminate list */
 +    b->index[i] = nra;
 +}
 +
 +static int icomp(const void *v1, const void *v2)
 +{
 +    return (*((atom_id *) v1))-(*((atom_id *) v2));
 +}
 +
 +void merge_excl(t_blocka *excl, t_block2 *b2)
 +{
 +    int     i, k;
 +    atom_id j;
 +    int     nra;
 +
 +    if (!b2->nr)
 +    {
 +        return;
 +    }
 +    else if (b2->nr != excl->nr)
 +    {
 +        gmx_fatal(FARGS, "DEATH HORROR: b2->nr = %d, while excl->nr = %d",
 +                  b2->nr, excl->nr);
 +    }
 +    else if (debug)
 +    {
 +        fprintf(debug, "Entering merge_excl\n");
 +    }
 +
 +    /* First copy all entries from excl to b2 */
 +    b_to_b2(excl, b2);
 +
 +    /* Count and sort the exclusions */
 +    nra = 0;
 +    for (i = 0; (i < b2->nr); i++)
 +    {
 +        if (b2->nra[i] > 0)
 +        {
 +            /* remove double entries */
 +            qsort(b2->a[i], (size_t)b2->nra[i], (size_t)sizeof(b2->a[i][0]), icomp);
 +            k = 1;
 +            for (j = 1; (j < b2->nra[i]); j++)
 +            {
 +                if (b2->a[i][j] != b2->a[i][k-1])
 +                {
 +                    b2->a[i][k] = b2->a[i][j];
 +                    k++;
 +                }
 +            }
 +            b2->nra[i] = k;
 +            nra       += b2->nra[i];
 +        }
 +    }
 +    excl->nra = nra;
 +    srenew(excl->a, excl->nra);
 +
 +    b2_to_b(b2, excl);
 +}
 +
 +int add_atomtype_decoupled(t_symtab *symtab, gpp_atomtype_t at,
 +                           t_nbparam ***nbparam, t_nbparam ***pair)
 +{
 +    t_atom  atom;
 +    t_param param;
 +    int     i, nr;
 +
 +    /* Define an atom type with all parameters set to zero (no interactions) */
 +    atom.q = 0.0;
 +    atom.m = 0.0;
 +    /* Type for decoupled atoms could be anything,
 +     * this should be changed automatically later when required.
 +     */
 +    atom.ptype = eptAtom;
 +    for (i = 0; (i < MAXFORCEPARAM); i++)
 +    {
 +        param.c[i] = 0.0;
 +    }
 +
 +    nr = add_atomtype(at, symtab, &atom, "decoupled", &param, -1, 0.0, 0.0, 0.0, 0, 0, 0);
 +
 +    /* Add space in the non-bonded parameters matrix */
 +    realloc_nb_params(at, nbparam, pair);
 +
 +    return nr;
 +}
 +
 +static void convert_pairs_to_pairsQ(t_params *plist,
 +                                    real fudgeQQ, t_atoms *atoms)
 +{
 +    t_param *paramp1, *paramp2, *paramnew;
 +    int      i, j, p1nr, p2nr, p2newnr;
 +
 +    /* Add the pair list to the pairQ list */
 +    p1nr    = plist[F_LJ14].nr;
 +    p2nr    = plist[F_LJC14_Q].nr;
 +    p2newnr = p1nr + p2nr;
 +    snew(paramnew, p2newnr);
 +
 +    paramp1             = plist[F_LJ14].param;
 +    paramp2             = plist[F_LJC14_Q].param;
 +
 +    /* Fill in the new F_LJC14_Q array with the old one. NOTE:
 +       it may be possible to just ADD the converted F_LJ14 array
 +       to the old F_LJC14_Q array, but since we have to create
 +       a new sized memory structure, better just to deep copy it all.
 +     */
 +
 +    for (i = 0; i < p2nr; i++)
 +    {
 +        /* Copy over parameters */
 +        for (j = 0; j < 5; j++) /* entries are 0-4 for F_LJC14_Q */
 +        {
 +            paramnew[i].c[j] = paramp2[i].c[j];
 +        }
 +
 +        /* copy over atoms */
 +        for (j = 0; j < 2; j++)
 +        {
 +            paramnew[i].a[j] = paramp2[i].a[j];
 +        }
 +    }
 +
 +    for (i = p2nr; i < p2newnr; i++)
 +    {
 +        j             = i-p2nr;
 +
 +        /* Copy over parameters */
 +        paramnew[i].c[0] = fudgeQQ;
 +        paramnew[i].c[1] = atoms->atom[paramp1[j].a[0]].q;
 +        paramnew[i].c[2] = atoms->atom[paramp1[j].a[1]].q;
 +        paramnew[i].c[3] = paramp1[j].c[0];
 +        paramnew[i].c[4] = paramp1[j].c[1];
 +
 +        /* copy over atoms */
 +        paramnew[i].a[0] = paramp1[j].a[0];
 +        paramnew[i].a[1] = paramp1[j].a[1];
 +    }
 +
 +    /* free the old pairlists */
 +    sfree(plist[F_LJC14_Q].param);
 +    sfree(plist[F_LJ14].param);
 +
 +    /* now assign the new data to the F_LJC14_Q structure */
 +    plist[F_LJC14_Q].param   = paramnew;
 +    plist[F_LJC14_Q].nr      = p2newnr;
 +
 +    /* Empty the LJ14 pairlist */
 +    plist[F_LJ14].nr    = 0;
 +    plist[F_LJ14].param = NULL;
 +}
 +
 +static void generate_LJCpairsNB(t_molinfo *mol, int nb_funct, t_params *nbp)
 +{
 +    int       n, ntype, i, j, k;
 +    t_atom   *atom;
 +    t_blocka *excl;
 +    gmx_bool  bExcl;
 +    t_param   param;
 +
 +    n    = mol->atoms.nr;
 +    atom = mol->atoms.atom;
 +
 +    ntype = sqrt(nbp->nr);
 +
 +    for (i = 0; i < MAXATOMLIST; i++)
 +    {
 +        param.a[i] = NOTSET;
 +    }
 +    for (i = 0; i < MAXFORCEPARAM; i++)
 +    {
 +        param.c[i] = NOTSET;
 +    }
 +
 +    /* Add a pair interaction for all non-excluded atom pairs */
 +    excl = &mol->excls;
 +    for (i = 0; i < n; i++)
 +    {
 +        for (j = i+1; j < n; j++)
 +        {
 +            bExcl = FALSE;
 +            for (k = excl->index[i]; k < excl->index[i+1]; k++)
 +            {
 +                if (excl->a[k] == j)
 +                {
 +                    bExcl = TRUE;
 +                }
 +            }
 +            if (!bExcl)
 +            {
 +                if (nb_funct != F_LJ)
 +                {
 +                    gmx_fatal(FARGS, "Can only generate non-bonded pair interactions for Van der Waals type Lennard-Jones");
 +                }
 +                param.a[0] = i;
 +                param.a[1] = j;
 +                param.c[0] = atom[i].q;
 +                param.c[1] = atom[j].q;
 +                param.c[2] = nbp->param[ntype*atom[i].type+atom[j].type].c[0];
 +                param.c[3] = nbp->param[ntype*atom[i].type+atom[j].type].c[1];
 +                add_param_to_list(&mol->plist[F_LJC_PAIRS_NB], &param);
 +            }
 +        }
 +    }
 +}
 +
 +static void set_excl_all(t_blocka *excl)
 +{
 +    int nat, i, j, k;
 +
 +    /* Get rid of the current exclusions and exclude all atom pairs */
 +    nat       = excl->nr;
 +    excl->nra = nat*nat;
 +    srenew(excl->a, excl->nra);
 +    k = 0;
 +    for (i = 0; i < nat; i++)
 +    {
 +        excl->index[i] = k;
 +        for (j = 0; j < nat; j++)
 +        {
 +            excl->a[k++] = j;
 +        }
 +    }
 +    excl->index[nat] = k;
 +}
 +
 +static void decouple_atoms(t_atoms *atoms, int atomtype_decouple,
 +                           int couple_lam0, int couple_lam1)
 +{
 +    int i;
 +
 +    for (i = 0; i < atoms->nr; i++)
 +    {
 +        if (couple_lam0 == ecouplamNONE || couple_lam0 == ecouplamVDW)
 +        {
 +            atoms->atom[i].q     = 0.0;
 +        }
 +        if (couple_lam0 == ecouplamNONE || couple_lam0 == ecouplamQ)
 +        {
 +            atoms->atom[i].type  = atomtype_decouple;
 +        }
 +        if (couple_lam1 == ecouplamNONE || couple_lam1 == ecouplamVDW)
 +        {
 +            atoms->atom[i].qB    = 0.0;
 +        }
 +        if (couple_lam1 == ecouplamNONE || couple_lam1 == ecouplamQ)
 +        {
 +            atoms->atom[i].typeB = atomtype_decouple;
 +        }
 +    }
 +}
 +
 +void convert_moltype_couple(t_molinfo *mol, int atomtype_decouple, real fudgeQQ,
 +                            int couple_lam0, int couple_lam1,
 +                            gmx_bool bCoupleIntra, int nb_funct, t_params *nbp)
 +{
 +    convert_pairs_to_pairsQ(mol->plist, fudgeQQ, &mol->atoms);
 +    if (!bCoupleIntra)
 +    {
 +        generate_LJCpairsNB(mol, nb_funct, nbp);
 +        set_excl_all(&mol->excls);
 +    }
 +    decouple_atoms(&mol->atoms, atomtype_decouple, couple_lam0, couple_lam1);
 +}
index efd24f896e8cdc0f6a7ef6df7ee05ab8102b418f,0000000000000000000000000000000000000000..c8fdf66b7a05f59ebc42b3fc3df3cf5b1c2c6f3f
mode 100644,000000..100644
--- /dev/null
@@@ -1,140 -1,0 +1,140 @@@
- /* return inverse time conversion factor from ps (i.e. 1e3 for ps->ns) */
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2012,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
 +#ifndef _oenv_h
 +#define _oenv_h
 +
 +#include "types/simple.h"
 +#include "types/oenv.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +#if 0 /* avoid screwing up indentation */
 +}
 +#endif
 +
 +
 +/* output_env member functions */
 +
 +/* The output_env structure holds information about program name, cmd line,
 +   default times, etc.
 +
 +   There are still legacy functions for the program name, and the command
 +   line, but the output_env versions are now preferred.*/
 +
 +typedef enum
 +{
 +    timeNULL, time_fs, time_ps, time_ns, time_us, time_ms, time_s
 +} time_unit_t;
 +/* the time units. For the time being, ps means no conversion. */
 +
 +typedef enum {
 +    exvgNULL, exvgXMGRACE, exvgXMGR, exvgNONE
 +} xvg_format_t;
 +/* the xvg output formattings */
 +
 +
 +void output_env_init_default(output_env_t *oenvp);
 +/* initialize an output_env structure, with reasonable default settings.
 +    (the time unit is set to time_ps, which means no conversion).  */
 +
 +void output_env_done(output_env_t oenv);
 +/* free memory allocated for an output_env structure. */
 +
 +
 +int output_env_get_verbosity(const output_env_t oenv);
 +/* return the verbosity */
 +
 +int output_env_get_debug_level(const output_env_t oenv);
 +/* return the debug level */
 +
 +const char *output_env_get_time_unit(const output_env_t oenv);
 +/* return time unit (e.g. ps or ns) */
 +
 +const char *output_env_get_time_label(const output_env_t oenv);
 +/* return time unit label (e.g. "Time (ps)") */
 +
 +const char *output_env_get_xvgr_tlabel(const output_env_t oenv);
 +/* retrun x-axis time label for xmgr */
 +
 +real output_env_get_time_factor(const output_env_t oenv);
 +/* return time conversion factor from ps (i.e. 1e-3 for ps->ns) */
 +
 +real output_env_get_time_invfactor(const output_env_t oenv);
++/* return inverse time conversion factor to ps (i.e. 1e3 for ns->ps) */
 +
 +real output_env_conv_time(const output_env_t oenv, real time);
 +/* return converted time */
 +
 +void output_env_conv_times(const output_env_t oenv, int n, real *time);
 +/* convert array of times */
 +
 +gmx_bool output_env_get_view(const output_env_t oenv);
 +/* Return TRUE when user requested viewing of the file */
 +
 +xvg_format_t output_env_get_xvg_format(const output_env_t oenv);
 +/* Returns enum (see above) for xvg output formatting */
 +
 +const char *output_env_get_program_name(const output_env_t oenv);
 +/* return the program name */
 +
 +const char *output_env_get_cmd_line(const output_env_t oenv);
 +/* return the command line */
 +
 +const char *output_env_get_short_program_name(const output_env_t oenv);
 +/* get the short version (without path component) of the program name */
 +
 +#ifdef __cplusplus
 +}
 +
 +namespace gmx
 +{
 +class ProgramContextInterface;
 +} // namespace gmx
 +
 +void output_env_init(output_env_t *oenvp,
 +                     const gmx::ProgramContextInterface &context,
 +                     time_unit_t tmu, gmx_bool view, xvg_format_t xvg_format,
 +                     int verbosity, int debug_level);
 +/* initialize an output_env structure, setting the command line,
 +   the default time value a gmx_boolean view that is set to TRUE when the
 +   user requests direct viewing of graphs,
 +   the graph formatting type, the verbosity, and debug level */
 +#endif
 +
 +#endif
index 22cd489e4b30756fb577b5330818248f29929a92,0000000000000000000000000000000000000000..0c1126e8e5c8bee8c8875eea3e6c9769fd6204a4
mode 100644,000000..100644
--- /dev/null
@@@ -1,81 -1,0 +1,80 @@@
-                                  gmx_bool bCutoffSchemeIsVerlet,
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2008, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
 +#include "typedefs.h"
 +#include "vsite.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +/* Initialization function, also predicts the initial shell postions.
 + * If x!=NULL, the shells are predict for the global coordinates x.
 + */
 +gmx_shellfc_t init_shell_flexcon(FILE *fplog,
 +                                 gmx_mtop_t *mtop, int nflexcon,
 +                                 rvec *x);
 +
 +/* Get the local shell with domain decomposition */
 +void make_local_shells(t_commrec *cr, t_mdatoms *md,
 +                       gmx_shellfc_t shfc);
 +
 +/* Optimize shell positions */
 +int relax_shell_flexcon(FILE *log, t_commrec *cr, gmx_bool bVerbose,
 +                        gmx_int64_t mdstep, t_inputrec *inputrec,
 +                        gmx_bool bDoNS, int force_flags,
 +                        gmx_localtop_t *top,
 +                        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,
 +                        gmx_shellfc_t shfc,
 +                        t_forcerec *fr,
 +                        gmx_bool bBornRadii,
 +                        double t, rvec mu_tot,
 +                        gmx_bool *bConverged,
 +                        gmx_vsite_t *vsite,
 +                        FILE *fp_field);
 +
 +
 +#ifdef __cplusplus
 +}
 +#endif
index affbf51ac9e0b300324eebc9a01082fb11c3a269,0000000000000000000000000000000000000000..b16abe9141e9d5427073880cdef23ae63697f26a
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,80 @@@
-                                  real                                  dx,
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 2012,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
 +#ifndef _tables_h
 +#define _tables_h
 +#include "types/simple.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +#if 0
 +}
 +#endif
 +
 +typedef double (*real_space_grid_contribution_computer)(double, double);
 +/* Function pointer used to tell table_spline3_fill_ewald_lr whether it
 + * should calculate the grid contribution for electrostatics or LJ.
 + */
 +
 +void table_spline3_fill_ewald_lr(real                                 *table_F,
 +                                 real                                 *table_V,
 +                                 real                                 *table_FDV0,
 +                                 int                                   ntab,
++                                 double                                dx,
 +                                 real                                  beta,
 +                                 real_space_grid_contribution_computer v_lr);
 +/* Fill tables of ntab points with spacing dr with the ewald long-range
 + * (mesh) force.
 + * There are three separate tables with format FDV0, F, and V.
 + * This function interpolates the Ewald mesh potential contribution
 + * with coefficient beta using a quadratic spline.
 + * The force can then be interpolated linearly.
 + */
 +
 +real ewald_spline3_table_scale(real ewaldcoeff, real rc);
 +/* Return the scaling for the Ewald quadratic spline tables. */
 +
 +double v_q_ewald_lr(double beta, double r);
 +/* Return the real space grid contribution for Ewald*/
 +
 +double v_lj_ewald_lr(double beta, double r);
 +/* Return the real space grid contribution for LJ-Ewald*/
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif  /* _tables\_h */
index 90a43f97ef1232991218c5c3ede2ca9800abab33,0000000000000000000000000000000000000000..c244e7e3a0e21bf620f7f85ba2dc6038ce723e4b
mode 100644,000000..100644
--- /dev/null
@@@ -1,5359 -1,0 +1,5347 @@@
-     /* Note that ffy_nx/y is equal to the number of grid points
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +/* IMPORTANT FOR DEVELOPERS:
 + *
 + * Triclinic pme stuff isn't entirely trivial, and we've experienced
 + * some bugs during development (many of them due to me). To avoid
 + * this in the future, please check the following things if you make
 + * changes in this file:
 + *
 + * 1. You should obtain identical (at least to the PME precision)
 + *    energies, forces, and virial for
 + *    a rectangular box and a triclinic one where the z (or y) axis is
 + *    tilted a whole box side. For instance you could use these boxes:
 + *
 + *    rectangular       triclinic
 + *     2  0  0           2  0  0
 + *     0  2  0           0  2  0
 + *     0  0  6           2  2  6
 + *
 + * 2. You should check the energy conservation in a triclinic box.
 + *
 + * It might seem an overkill, but better safe than sorry.
 + * /Erik 001109
 + */
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <stdio.h>
 +#include <string.h>
 +#include <math.h>
 +#include <assert.h>
 +#include "typedefs.h"
 +#include "txtdump.h"
 +#include "vec.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "coulomb.h"
 +#include "gmx_fatal.h"
 +#include "pme.h"
 +#include "network.h"
 +#include "physics.h"
 +#include "nrnb.h"
 +#include "macros.h"
 +
 +#include "gromacs/fft/parallel_3dfft.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gromacs/fileio/pdbio.h"
 +#include "gromacs/math/gmxcomplex.h"
 +#include "gromacs/timing/cyclecounter.h"
 +#include "gromacs/timing/wallcycle.h"
 +#include "gromacs/utility/gmxmpi.h"
 +#include "gromacs/utility/gmxomp.h"
 +
 +/* Include the SIMD macro file and then check for support */
 +#include "gromacs/simd/simd.h"
 +#include "gromacs/simd/simd_math.h"
 +#ifdef GMX_SIMD_HAVE_REAL
 +/* Turn on arbitrary width SIMD intrinsics for PME solve */
 +#    define PME_SIMD_SOLVE
 +#endif
 +
 +#define PME_GRID_QA    0 /* Gridindex for A-state for Q */
 +#define PME_GRID_C6A   2 /* Gridindex for A-state for LJ */
 +#define DO_Q           2 /* Electrostatic grids have index q<2 */
 +#define DO_Q_AND_LJ    4 /* non-LB LJ grids have index 2 <= q < 4 */
 +#define DO_Q_AND_LJ_LB 9 /* With LB rules we need a total of 2+7 grids */
 +
 +/* Pascal triangle coefficients scaled with (1/2)^6 for LJ-PME with LB-rules */
 +const real lb_scale_factor[] = {
 +    1.0/64, 6.0/64, 15.0/64, 20.0/64,
 +    15.0/64, 6.0/64, 1.0/64
 +};
 +
 +/* Pascal triangle coefficients used in solve_pme_lj_yzx, only need to do 4 calculations due to symmetry */
 +const real lb_scale_factor_symm[] = { 2.0/64, 12.0/64, 30.0/64, 20.0/64 };
 +
 +/* Check if we have 4-wide SIMD macro support */
 +#if (defined GMX_SIMD4_HAVE_REAL)
 +/* Do PME spread and gather with 4-wide SIMD.
 + * NOTE: SIMD is only used with PME order 4 and 5 (which are the most common).
 + */
 +#    define PME_SIMD4_SPREAD_GATHER
 +
 +#    if (defined GMX_SIMD_HAVE_LOADU) && (defined GMX_SIMD_HAVE_STOREU)
 +/* With PME-order=4 on x86, unaligned load+store is slightly faster
 + * than doubling all SIMD operations when using aligned load+store.
 + */
 +#        define PME_SIMD4_UNALIGNED
 +#    endif
 +#endif
 +
 +#define DFT_TOL 1e-7
 +/* #define PRT_FORCE */
 +/* conditions for on the fly time-measurement */
 +/* #define TAKETIME (step > 1 && timesteps < 10) */
 +#define TAKETIME FALSE
 +
 +/* #define PME_TIME_THREADS */
 +
 +#ifdef GMX_DOUBLE
 +#define mpi_type MPI_DOUBLE
 +#else
 +#define mpi_type MPI_FLOAT
 +#endif
 +
 +#ifdef PME_SIMD4_SPREAD_GATHER
 +#    define SIMD4_ALIGNMENT  (GMX_SIMD4_WIDTH*sizeof(real))
 +#else
 +/* We can use any alignment, apart from 0, so we use 4 reals */
 +#    define SIMD4_ALIGNMENT  (4*sizeof(real))
 +#endif
 +
 +/* GMX_CACHE_SEP should be a multiple of the SIMD and SIMD4 register size
 + * to preserve alignment.
 + */
 +#define GMX_CACHE_SEP 64
 +
 +/* We only define a maximum to be able to use local arrays without allocation.
 + * An order larger than 12 should never be needed, even for test cases.
 + * If needed it can be changed here.
 + */
 +#define PME_ORDER_MAX 12
 +
 +/* Internal datastructures */
 +typedef struct {
 +    int send_index0;
 +    int send_nindex;
 +    int recv_index0;
 +    int recv_nindex;
 +    int recv_size;   /* Receive buffer width, used with OpenMP */
 +} pme_grid_comm_t;
 +
 +typedef struct {
 +#ifdef GMX_MPI
 +    MPI_Comm         mpi_comm;
 +#endif
 +    int              nnodes, nodeid;
 +    int             *s2g0;
 +    int             *s2g1;
 +    int              noverlap_nodes;
 +    int             *send_id, *recv_id;
 +    int              send_size; /* Send buffer width, used with OpenMP */
 +    pme_grid_comm_t *comm_data;
 +    real            *sendbuf;
 +    real            *recvbuf;
 +} pme_overlap_t;
 +
 +typedef struct {
 +    int *n;      /* Cumulative counts of the number of particles per thread */
 +    int  nalloc; /* Allocation size of i */
 +    int *i;      /* Particle indices ordered on thread index (n) */
 +} thread_plist_t;
 +
 +typedef struct {
 +    int      *thread_one;
 +    int       n;
 +    int      *ind;
 +    splinevec theta;
 +    real     *ptr_theta_z;
 +    splinevec dtheta;
 +    real     *ptr_dtheta_z;
 +} splinedata_t;
 +
 +typedef struct {
 +    int      dimind;        /* The index of the dimension, 0=x, 1=y */
 +    int      nslab;
 +    int      nodeid;
 +#ifdef GMX_MPI
 +    MPI_Comm mpi_comm;
 +#endif
 +
 +    int     *node_dest;     /* The nodes to send x and q to with DD */
 +    int     *node_src;      /* The nodes to receive x and q from with DD */
 +    int     *buf_index;     /* Index for commnode into the buffers */
 +
 +    int      maxshift;
 +
 +    int      npd;
 +    int      pd_nalloc;
 +    int     *pd;
 +    int     *count;         /* The number of atoms to send to each node */
 +    int    **count_thread;
 +    int     *rcount;        /* The number of atoms to receive */
 +
 +    int      n;
 +    int      nalloc;
 +    rvec    *x;
 +    real    *coefficient;
 +    rvec    *f;
 +    gmx_bool bSpread;       /* These coordinates are used for spreading */
 +    int      pme_order;
 +    ivec    *idx;
 +    rvec    *fractx;            /* Fractional coordinate relative to
 +                                 * the lower cell boundary
 +                                 */
 +    int             nthread;
 +    int            *thread_idx; /* Which thread should spread which coefficient */
 +    thread_plist_t *thread_plist;
 +    splinedata_t   *spline;
 +} pme_atomcomm_t;
 +
 +#define FLBS  3
 +#define FLBSZ 4
 +
 +typedef struct {
 +    ivec  ci;     /* The spatial location of this grid         */
 +    ivec  n;      /* The used size of *grid, including order-1 */
 +    ivec  offset; /* The grid offset from the full node grid   */
 +    int   order;  /* PME spreading order                       */
 +    ivec  s;      /* The allocated size of *grid, s >= n       */
 +    real *grid;   /* The grid local thread, size n             */
 +} pmegrid_t;
 +
 +typedef struct {
 +    pmegrid_t  grid;         /* The full node grid (non thread-local)            */
 +    int        nthread;      /* The number of threads operating on this grid     */
 +    ivec       nc;           /* The local spatial decomposition over the threads */
 +    pmegrid_t *grid_th;      /* Array of grids for each thread                   */
 +    real      *grid_all;     /* Allocated array for the grids in *grid_th        */
 +    int      **g2t;          /* The grid to thread index                         */
 +    ivec       nthread_comm; /* The number of threads to communicate with        */
 +} pmegrids_t;
 +
 +typedef struct {
 +#ifdef PME_SIMD4_SPREAD_GATHER
 +    /* Masks for 4-wide SIMD aligned spreading and gathering */
 +    gmx_simd4_bool_t mask_S0[6], mask_S1[6];
 +#else
 +    int              dummy; /* C89 requires that struct has at least one member */
 +#endif
 +} pme_spline_work_t;
 +
 +typedef struct {
 +    /* work data for solve_pme */
 +    int      nalloc;
 +    real *   mhx;
 +    real *   mhy;
 +    real *   mhz;
 +    real *   m2;
 +    real *   denom;
 +    real *   tmp1_alloc;
 +    real *   tmp1;
 +    real *   tmp2;
 +    real *   eterm;
 +    real *   m2inv;
 +
 +    real     energy_q;
 +    matrix   vir_q;
 +    real     energy_lj;
 +    matrix   vir_lj;
 +} pme_work_t;
 +
 +typedef struct gmx_pme {
 +    int           ndecompdim; /* The number of decomposition dimensions */
 +    int           nodeid;     /* Our nodeid in mpi->mpi_comm */
 +    int           nodeid_major;
 +    int           nodeid_minor;
 +    int           nnodes;    /* The number of nodes doing PME */
 +    int           nnodes_major;
 +    int           nnodes_minor;
 +
 +    MPI_Comm      mpi_comm;
 +    MPI_Comm      mpi_comm_d[2]; /* Indexed on dimension, 0=x, 1=y */
 +#ifdef GMX_MPI
 +    MPI_Datatype  rvec_mpi;      /* the pme vector's MPI type */
 +#endif
 +
 +    gmx_bool   bUseThreads;   /* Does any of the PME ranks have nthread>1 ?  */
 +    int        nthread;       /* The number of threads doing PME on our rank */
 +
 +    gmx_bool   bPPnode;       /* Node also does particle-particle forces */
 +    gmx_bool   bFEP;          /* Compute Free energy contribution */
 +    gmx_bool   bFEP_q;
 +    gmx_bool   bFEP_lj;
 +    int        nkx, nky, nkz; /* Grid dimensions */
 +    gmx_bool   bP3M;          /* Do P3M: optimize the influence function */
 +    int        pme_order;
 +    real       epsilon_r;
 +
 +    int        ljpme_combination_rule;  /* Type of combination rule in LJ-PME */
 +
 +    int        ngrids;                  /* number of grids we maintain for pmegrid, (c)fftgrid and pfft_setups*/
 +
 +    pmegrids_t pmegrid[DO_Q_AND_LJ_LB]; /* Grids on which we do spreading/interpolation,
 +                                         * includes overlap Grid indices are ordered as
 +                                         * follows:
 +                                         * 0: Coloumb PME, state A
 +                                         * 1: Coloumb PME, state B
 +                                         * 2-8: LJ-PME
 +                                         * This can probably be done in a better way
 +                                         * but this simple hack works for now
 +                                         */
 +    /* The PME coefficient spreading grid sizes/strides, includes pme_order-1 */
 +    int        pmegrid_nx, pmegrid_ny, pmegrid_nz;
 +    /* pmegrid_nz might be larger than strictly necessary to ensure
 +     * memory alignment, pmegrid_nz_base gives the real base size.
 +     */
 +    int     pmegrid_nz_base;
 +    /* The local PME grid starting indices */
 +    int     pmegrid_start_ix, pmegrid_start_iy, pmegrid_start_iz;
 +
 +    /* Work data for spreading and gathering */
 +    pme_spline_work_t     *spline_work;
 +
 +    real                 **fftgrid; /* Grids for FFT. With 1D FFT decomposition this can be a pointer */
 +    /* inside the interpolation grid, but separate for 2D PME decomp. */
 +    int                    fftgrid_nx, fftgrid_ny, fftgrid_nz;
 +
 +    t_complex            **cfftgrid;  /* Grids for complex FFT data */
 +
 +    int                    cfftgrid_nx, cfftgrid_ny, cfftgrid_nz;
 +
 +    gmx_parallel_3dfft_t  *pfft_setup;
 +
 +    int                   *nnx, *nny, *nnz;
 +    real                  *fshx, *fshy, *fshz;
 +
 +    pme_atomcomm_t         atc[2]; /* Indexed on decomposition index */
 +    matrix                 recipbox;
 +    splinevec              bsp_mod;
 +    /* Buffers to store data for local atoms for L-B combination rule
 +     * calculations in LJ-PME. lb_buf1 stores either the coefficients
 +     * for spreading/gathering (in serial), or the C6 coefficient for
 +     * local atoms (in parallel).  lb_buf2 is only used in parallel,
 +     * and stores the sigma values for local atoms. */
 +    real                 *lb_buf1, *lb_buf2;
 +    int                   lb_buf_nalloc; /* Allocation size for the above buffers. */
 +
 +    pme_overlap_t         overlap[2];    /* Indexed on dimension, 0=x, 1=y */
 +
 +    pme_atomcomm_t        atc_energy;    /* Only for gmx_pme_calc_energy */
 +
 +    rvec                 *bufv;          /* Communication buffer */
 +    real                 *bufr;          /* Communication buffer */
 +    int                   buf_nalloc;    /* The communication buffer size */
 +
 +    /* thread local work data for solve_pme */
 +    pme_work_t *work;
 +
 +    /* Work data for sum_qgrid */
 +    real *   sum_qgrid_tmp;
 +    real *   sum_qgrid_dd_tmp;
 +} t_gmx_pme;
 +
 +static void calc_interpolation_idx(gmx_pme_t pme, pme_atomcomm_t *atc,
 +                                   int start, int grid_index, int end, int thread)
 +{
 +    int             i;
 +    int            *idxptr, tix, tiy, tiz;
 +    real           *xptr, *fptr, tx, ty, tz;
 +    real            rxx, ryx, ryy, rzx, rzy, rzz;
 +    int             nx, ny, nz;
 +    int             start_ix, start_iy, start_iz;
 +    int            *g2tx, *g2ty, *g2tz;
 +    gmx_bool        bThreads;
 +    int            *thread_idx = NULL;
 +    thread_plist_t *tpl        = NULL;
 +    int            *tpl_n      = NULL;
 +    int             thread_i;
 +
 +    nx  = pme->nkx;
 +    ny  = pme->nky;
 +    nz  = pme->nkz;
 +
 +    start_ix = pme->pmegrid_start_ix;
 +    start_iy = pme->pmegrid_start_iy;
 +    start_iz = pme->pmegrid_start_iz;
 +
 +    rxx = pme->recipbox[XX][XX];
 +    ryx = pme->recipbox[YY][XX];
 +    ryy = pme->recipbox[YY][YY];
 +    rzx = pme->recipbox[ZZ][XX];
 +    rzy = pme->recipbox[ZZ][YY];
 +    rzz = pme->recipbox[ZZ][ZZ];
 +
 +    g2tx = pme->pmegrid[grid_index].g2t[XX];
 +    g2ty = pme->pmegrid[grid_index].g2t[YY];
 +    g2tz = pme->pmegrid[grid_index].g2t[ZZ];
 +
 +    bThreads = (atc->nthread > 1);
 +    if (bThreads)
 +    {
 +        thread_idx = atc->thread_idx;
 +
 +        tpl   = &atc->thread_plist[thread];
 +        tpl_n = tpl->n;
 +        for (i = 0; i < atc->nthread; i++)
 +        {
 +            tpl_n[i] = 0;
 +        }
 +    }
 +
 +    for (i = start; i < end; i++)
 +    {
 +        xptr   = atc->x[i];
 +        idxptr = atc->idx[i];
 +        fptr   = atc->fractx[i];
 +
 +        /* Fractional coordinates along box vectors, add 2.0 to make 100% sure we are positive for triclinic boxes */
 +        tx = nx * ( xptr[XX] * rxx + xptr[YY] * ryx + xptr[ZZ] * rzx + 2.0 );
 +        ty = ny * (                  xptr[YY] * ryy + xptr[ZZ] * rzy + 2.0 );
 +        tz = nz * (                                   xptr[ZZ] * rzz + 2.0 );
 +
 +        tix = (int)(tx);
 +        tiy = (int)(ty);
 +        tiz = (int)(tz);
 +
 +        /* Because decomposition only occurs in x and y,
 +         * we never have a fraction correction in z.
 +         */
 +        fptr[XX] = tx - tix + pme->fshx[tix];
 +        fptr[YY] = ty - tiy + pme->fshy[tiy];
 +        fptr[ZZ] = tz - tiz;
 +
 +        idxptr[XX] = pme->nnx[tix];
 +        idxptr[YY] = pme->nny[tiy];
 +        idxptr[ZZ] = pme->nnz[tiz];
 +
 +#ifdef DEBUG
 +        range_check(idxptr[XX], 0, pme->pmegrid_nx);
 +        range_check(idxptr[YY], 0, pme->pmegrid_ny);
 +        range_check(idxptr[ZZ], 0, pme->pmegrid_nz);
 +#endif
 +
 +        if (bThreads)
 +        {
 +            thread_i      = g2tx[idxptr[XX]] + g2ty[idxptr[YY]] + g2tz[idxptr[ZZ]];
 +            thread_idx[i] = thread_i;
 +            tpl_n[thread_i]++;
 +        }
 +    }
 +
 +    if (bThreads)
 +    {
 +        /* Make a list of particle indices sorted on thread */
 +
 +        /* Get the cumulative count */
 +        for (i = 1; i < atc->nthread; i++)
 +        {
 +            tpl_n[i] += tpl_n[i-1];
 +        }
 +        /* The current implementation distributes particles equally
 +         * over the threads, so we could actually allocate for that
 +         * in pme_realloc_atomcomm_things.
 +         */
 +        if (tpl_n[atc->nthread-1] > tpl->nalloc)
 +        {
 +            tpl->nalloc = over_alloc_large(tpl_n[atc->nthread-1]);
 +            srenew(tpl->i, tpl->nalloc);
 +        }
 +        /* Set tpl_n to the cumulative start */
 +        for (i = atc->nthread-1; i >= 1; i--)
 +        {
 +            tpl_n[i] = tpl_n[i-1];
 +        }
 +        tpl_n[0] = 0;
 +
 +        /* Fill our thread local array with indices sorted on thread */
 +        for (i = start; i < end; i++)
 +        {
 +            tpl->i[tpl_n[atc->thread_idx[i]]++] = i;
 +        }
 +        /* Now tpl_n contains the cummulative count again */
 +    }
 +}
 +
 +static void make_thread_local_ind(pme_atomcomm_t *atc,
 +                                  int thread, splinedata_t *spline)
 +{
 +    int             n, t, i, start, end;
 +    thread_plist_t *tpl;
 +
 +    /* Combine the indices made by each thread into one index */
 +
 +    n     = 0;
 +    start = 0;
 +    for (t = 0; t < atc->nthread; t++)
 +    {
 +        tpl = &atc->thread_plist[t];
 +        /* Copy our part (start - end) from the list of thread t */
 +        if (thread > 0)
 +        {
 +            start = tpl->n[thread-1];
 +        }
 +        end = tpl->n[thread];
 +        for (i = start; i < end; i++)
 +        {
 +            spline->ind[n++] = tpl->i[i];
 +        }
 +    }
 +
 +    spline->n = n;
 +}
 +
 +
 +static void pme_calc_pidx(int start, int end,
 +                          matrix recipbox, rvec x[],
 +                          pme_atomcomm_t *atc, int *count)
 +{
 +    int   nslab, i;
 +    int   si;
 +    real *xptr, s;
 +    real  rxx, ryx, rzx, ryy, rzy;
 +    int  *pd;
 +
 +    /* Calculate PME task index (pidx) for each grid index.
 +     * Here we always assign equally sized slabs to each node
 +     * for load balancing reasons (the PME grid spacing is not used).
 +     */
 +
 +    nslab = atc->nslab;
 +    pd    = atc->pd;
 +
 +    /* Reset the count */
 +    for (i = 0; i < nslab; i++)
 +    {
 +        count[i] = 0;
 +    }
 +
 +    if (atc->dimind == 0)
 +    {
 +        rxx = recipbox[XX][XX];
 +        ryx = recipbox[YY][XX];
 +        rzx = recipbox[ZZ][XX];
 +        /* Calculate the node index in x-dimension */
 +        for (i = start; i < end; i++)
 +        {
 +            xptr   = x[i];
 +            /* Fractional coordinates along box vectors */
 +            s     = nslab*(xptr[XX]*rxx + xptr[YY]*ryx + xptr[ZZ]*rzx);
 +            si    = (int)(s + 2*nslab) % nslab;
 +            pd[i] = si;
 +            count[si]++;
 +        }
 +    }
 +    else
 +    {
 +        ryy = recipbox[YY][YY];
 +        rzy = recipbox[ZZ][YY];
 +        /* Calculate the node index in y-dimension */
 +        for (i = start; i < end; i++)
 +        {
 +            xptr   = x[i];
 +            /* Fractional coordinates along box vectors */
 +            s     = nslab*(xptr[YY]*ryy + xptr[ZZ]*rzy);
 +            si    = (int)(s + 2*nslab) % nslab;
 +            pd[i] = si;
 +            count[si]++;
 +        }
 +    }
 +}
 +
 +static void pme_calc_pidx_wrapper(int natoms, matrix recipbox, rvec x[],
 +                                  pme_atomcomm_t *atc)
 +{
 +    int nthread, thread, slab;
 +
 +    nthread = atc->nthread;
 +
 +#pragma omp parallel for num_threads(nthread) schedule(static)
 +    for (thread = 0; thread < nthread; thread++)
 +    {
 +        pme_calc_pidx(natoms* thread   /nthread,
 +                      natoms*(thread+1)/nthread,
 +                      recipbox, x, atc, atc->count_thread[thread]);
 +    }
 +    /* Non-parallel reduction, since nslab is small */
 +
 +    for (thread = 1; thread < nthread; thread++)
 +    {
 +        for (slab = 0; slab < atc->nslab; slab++)
 +        {
 +            atc->count_thread[0][slab] += atc->count_thread[thread][slab];
 +        }
 +    }
 +}
 +
 +static void realloc_splinevec(splinevec th, real **ptr_z, int nalloc)
 +{
 +    const int padding = 4;
 +    int       i;
 +
 +    srenew(th[XX], nalloc);
 +    srenew(th[YY], nalloc);
 +    /* In z we add padding, this is only required for the aligned SIMD code */
 +    sfree_aligned(*ptr_z);
 +    snew_aligned(*ptr_z, nalloc+2*padding, SIMD4_ALIGNMENT);
 +    th[ZZ] = *ptr_z + padding;
 +
 +    for (i = 0; i < padding; i++)
 +    {
 +        (*ptr_z)[               i] = 0;
 +        (*ptr_z)[padding+nalloc+i] = 0;
 +    }
 +}
 +
 +static void pme_realloc_splinedata(splinedata_t *spline, pme_atomcomm_t *atc)
 +{
 +    int i, d;
 +
 +    srenew(spline->ind, atc->nalloc);
 +    /* Initialize the index to identity so it works without threads */
 +    for (i = 0; i < atc->nalloc; i++)
 +    {
 +        spline->ind[i] = i;
 +    }
 +
 +    realloc_splinevec(spline->theta, &spline->ptr_theta_z,
 +                      atc->pme_order*atc->nalloc);
 +    realloc_splinevec(spline->dtheta, &spline->ptr_dtheta_z,
 +                      atc->pme_order*atc->nalloc);
 +}
 +
 +static void pme_realloc_atomcomm_things(pme_atomcomm_t *atc)
 +{
 +    int nalloc_old, i, j, nalloc_tpl;
 +
 +    /* We have to avoid a NULL pointer for atc->x to avoid
 +     * possible fatal errors in MPI routines.
 +     */
 +    if (atc->n > atc->nalloc || atc->nalloc == 0)
 +    {
 +        nalloc_old  = atc->nalloc;
 +        atc->nalloc = over_alloc_dd(max(atc->n, 1));
 +
 +        if (atc->nslab > 1)
 +        {
 +            srenew(atc->x, atc->nalloc);
 +            srenew(atc->coefficient, atc->nalloc);
 +            srenew(atc->f, atc->nalloc);
 +            for (i = nalloc_old; i < atc->nalloc; i++)
 +            {
 +                clear_rvec(atc->f[i]);
 +            }
 +        }
 +        if (atc->bSpread)
 +        {
 +            srenew(atc->fractx, atc->nalloc);
 +            srenew(atc->idx, atc->nalloc);
 +
 +            if (atc->nthread > 1)
 +            {
 +                srenew(atc->thread_idx, atc->nalloc);
 +            }
 +
 +            for (i = 0; i < atc->nthread; i++)
 +            {
 +                pme_realloc_splinedata(&atc->spline[i], atc);
 +            }
 +        }
 +    }
 +}
 +
 +static void pme_dd_sendrecv(pme_atomcomm_t gmx_unused *atc,
 +                            gmx_bool gmx_unused bBackward, int gmx_unused shift,
 +                            void gmx_unused *buf_s, int gmx_unused nbyte_s,
 +                            void gmx_unused *buf_r, int gmx_unused nbyte_r)
 +{
 +#ifdef GMX_MPI
 +    int        dest, src;
 +    MPI_Status stat;
 +
 +    if (bBackward == FALSE)
 +    {
 +        dest = atc->node_dest[shift];
 +        src  = atc->node_src[shift];
 +    }
 +    else
 +    {
 +        dest = atc->node_src[shift];
 +        src  = atc->node_dest[shift];
 +    }
 +
 +    if (nbyte_s > 0 && nbyte_r > 0)
 +    {
 +        MPI_Sendrecv(buf_s, nbyte_s, MPI_BYTE,
 +                     dest, shift,
 +                     buf_r, nbyte_r, MPI_BYTE,
 +                     src, shift,
 +                     atc->mpi_comm, &stat);
 +    }
 +    else if (nbyte_s > 0)
 +    {
 +        MPI_Send(buf_s, nbyte_s, MPI_BYTE,
 +                 dest, shift,
 +                 atc->mpi_comm);
 +    }
 +    else if (nbyte_r > 0)
 +    {
 +        MPI_Recv(buf_r, nbyte_r, MPI_BYTE,
 +                 src, shift,
 +                 atc->mpi_comm, &stat);
 +    }
 +#endif
 +}
 +
 +static void dd_pmeredist_pos_coeffs(gmx_pme_t pme,
 +                                    int n, gmx_bool bX, rvec *x, real *data,
 +                                    pme_atomcomm_t *atc)
 +{
 +    int *commnode, *buf_index;
 +    int  nnodes_comm, i, nsend, local_pos, buf_pos, node, scount, rcount;
 +
 +    commnode  = atc->node_dest;
 +    buf_index = atc->buf_index;
 +
 +    nnodes_comm = min(2*atc->maxshift, atc->nslab-1);
 +
 +    nsend = 0;
 +    for (i = 0; i < nnodes_comm; i++)
 +    {
 +        buf_index[commnode[i]] = nsend;
 +        nsend                 += atc->count[commnode[i]];
 +    }
 +    if (bX)
 +    {
 +        if (atc->count[atc->nodeid] + nsend != n)
 +        {
 +            gmx_fatal(FARGS, "%d particles communicated to PME node %d are more than 2/3 times the cut-off out of the domain decomposition cell of their charge group in dimension %c.\n"
 +                      "This usually means that your system is not well equilibrated.",
 +                      n - (atc->count[atc->nodeid] + nsend),
 +                      pme->nodeid, 'x'+atc->dimind);
 +        }
 +
 +        if (nsend > pme->buf_nalloc)
 +        {
 +            pme->buf_nalloc = over_alloc_dd(nsend);
 +            srenew(pme->bufv, pme->buf_nalloc);
 +            srenew(pme->bufr, pme->buf_nalloc);
 +        }
 +
 +        atc->n = atc->count[atc->nodeid];
 +        for (i = 0; i < nnodes_comm; i++)
 +        {
 +            scount = atc->count[commnode[i]];
 +            /* Communicate the count */
 +            if (debug)
 +            {
 +                fprintf(debug, "dimind %d PME node %d send to node %d: %d\n",
 +                        atc->dimind, atc->nodeid, commnode[i], scount);
 +            }
 +            pme_dd_sendrecv(atc, FALSE, i,
 +                            &scount, sizeof(int),
 +                            &atc->rcount[i], sizeof(int));
 +            atc->n += atc->rcount[i];
 +        }
 +
 +        pme_realloc_atomcomm_things(atc);
 +    }
 +
 +    local_pos = 0;
 +    for (i = 0; i < n; i++)
 +    {
 +        node = atc->pd[i];
 +        if (node == atc->nodeid)
 +        {
 +            /* Copy direct to the receive buffer */
 +            if (bX)
 +            {
 +                copy_rvec(x[i], atc->x[local_pos]);
 +            }
 +            atc->coefficient[local_pos] = data[i];
 +            local_pos++;
 +        }
 +        else
 +        {
 +            /* Copy to the send buffer */
 +            if (bX)
 +            {
 +                copy_rvec(x[i], pme->bufv[buf_index[node]]);
 +            }
 +            pme->bufr[buf_index[node]] = data[i];
 +            buf_index[node]++;
 +        }
 +    }
 +
 +    buf_pos = 0;
 +    for (i = 0; i < nnodes_comm; i++)
 +    {
 +        scount = atc->count[commnode[i]];
 +        rcount = atc->rcount[i];
 +        if (scount > 0 || rcount > 0)
 +        {
 +            if (bX)
 +            {
 +                /* Communicate the coordinates */
 +                pme_dd_sendrecv(atc, FALSE, i,
 +                                pme->bufv[buf_pos], scount*sizeof(rvec),
 +                                atc->x[local_pos], rcount*sizeof(rvec));
 +            }
 +            /* Communicate the coefficients */
 +            pme_dd_sendrecv(atc, FALSE, i,
 +                            pme->bufr+buf_pos, scount*sizeof(real),
 +                            atc->coefficient+local_pos, rcount*sizeof(real));
 +            buf_pos   += scount;
 +            local_pos += atc->rcount[i];
 +        }
 +    }
 +}
 +
 +static void dd_pmeredist_f(gmx_pme_t pme, pme_atomcomm_t *atc,
 +                           int n, rvec *f,
 +                           gmx_bool bAddF)
 +{
 +    int *commnode, *buf_index;
 +    int  nnodes_comm, local_pos, buf_pos, i, scount, rcount, node;
 +
 +    commnode  = atc->node_dest;
 +    buf_index = atc->buf_index;
 +
 +    nnodes_comm = min(2*atc->maxshift, atc->nslab-1);
 +
 +    local_pos = atc->count[atc->nodeid];
 +    buf_pos   = 0;
 +    for (i = 0; i < nnodes_comm; i++)
 +    {
 +        scount = atc->rcount[i];
 +        rcount = atc->count[commnode[i]];
 +        if (scount > 0 || rcount > 0)
 +        {
 +            /* Communicate the forces */
 +            pme_dd_sendrecv(atc, TRUE, i,
 +                            atc->f[local_pos], scount*sizeof(rvec),
 +                            pme->bufv[buf_pos], rcount*sizeof(rvec));
 +            local_pos += scount;
 +        }
 +        buf_index[commnode[i]] = buf_pos;
 +        buf_pos               += rcount;
 +    }
 +
 +    local_pos = 0;
 +    if (bAddF)
 +    {
 +        for (i = 0; i < n; i++)
 +        {
 +            node = atc->pd[i];
 +            if (node == atc->nodeid)
 +            {
 +                /* Add from the local force array */
 +                rvec_inc(f[i], atc->f[local_pos]);
 +                local_pos++;
 +            }
 +            else
 +            {
 +                /* Add from the receive buffer */
 +                rvec_inc(f[i], pme->bufv[buf_index[node]]);
 +                buf_index[node]++;
 +            }
 +        }
 +    }
 +    else
 +    {
 +        for (i = 0; i < n; i++)
 +        {
 +            node = atc->pd[i];
 +            if (node == atc->nodeid)
 +            {
 +                /* Copy from the local force array */
 +                copy_rvec(atc->f[local_pos], f[i]);
 +                local_pos++;
 +            }
 +            else
 +            {
 +                /* Copy from the receive buffer */
 +                copy_rvec(pme->bufv[buf_index[node]], f[i]);
 +                buf_index[node]++;
 +            }
 +        }
 +    }
 +}
 +
 +#ifdef GMX_MPI
 +static void gmx_sum_qgrid_dd(gmx_pme_t pme, real *grid, int direction)
 +{
 +    pme_overlap_t *overlap;
 +    int            send_index0, send_nindex;
 +    int            recv_index0, recv_nindex;
 +    MPI_Status     stat;
 +    int            i, j, k, ix, iy, iz, icnt;
 +    int            ipulse, send_id, recv_id, datasize;
 +    real          *p;
 +    real          *sendptr, *recvptr;
 +
 +    /* Start with minor-rank communication. This is a bit of a pain since it is not contiguous */
 +    overlap = &pme->overlap[1];
 +
 +    for (ipulse = 0; ipulse < overlap->noverlap_nodes; ipulse++)
 +    {
 +        /* Since we have already (un)wrapped the overlap in the z-dimension,
 +         * we only have to communicate 0 to nkz (not pmegrid_nz).
 +         */
 +        if (direction == GMX_SUM_GRID_FORWARD)
 +        {
 +            send_id       = overlap->send_id[ipulse];
 +            recv_id       = overlap->recv_id[ipulse];
 +            send_index0   = overlap->comm_data[ipulse].send_index0;
 +            send_nindex   = overlap->comm_data[ipulse].send_nindex;
 +            recv_index0   = overlap->comm_data[ipulse].recv_index0;
 +            recv_nindex   = overlap->comm_data[ipulse].recv_nindex;
 +        }
 +        else
 +        {
 +            send_id       = overlap->recv_id[ipulse];
 +            recv_id       = overlap->send_id[ipulse];
 +            send_index0   = overlap->comm_data[ipulse].recv_index0;
 +            send_nindex   = overlap->comm_data[ipulse].recv_nindex;
 +            recv_index0   = overlap->comm_data[ipulse].send_index0;
 +            recv_nindex   = overlap->comm_data[ipulse].send_nindex;
 +        }
 +
 +        /* Copy data to contiguous send buffer */
 +        if (debug)
 +        {
 +            fprintf(debug, "PME send node %d %d -> %d grid start %d Communicating %d to %d\n",
 +                    pme->nodeid, overlap->nodeid, send_id,
 +                    pme->pmegrid_start_iy,
 +                    send_index0-pme->pmegrid_start_iy,
 +                    send_index0-pme->pmegrid_start_iy+send_nindex);
 +        }
 +        icnt = 0;
 +        for (i = 0; i < pme->pmegrid_nx; i++)
 +        {
 +            ix = i;
 +            for (j = 0; j < send_nindex; j++)
 +            {
 +                iy = j + send_index0 - pme->pmegrid_start_iy;
 +                for (k = 0; k < pme->nkz; k++)
 +                {
 +                    iz = k;
 +                    overlap->sendbuf[icnt++] = grid[ix*(pme->pmegrid_ny*pme->pmegrid_nz)+iy*(pme->pmegrid_nz)+iz];
 +                }
 +            }
 +        }
 +
 +        datasize      = pme->pmegrid_nx * pme->nkz;
 +
 +        MPI_Sendrecv(overlap->sendbuf, send_nindex*datasize, GMX_MPI_REAL,
 +                     send_id, ipulse,
 +                     overlap->recvbuf, recv_nindex*datasize, GMX_MPI_REAL,
 +                     recv_id, ipulse,
 +                     overlap->mpi_comm, &stat);
 +
 +        /* Get data from contiguous recv buffer */
 +        if (debug)
 +        {
 +            fprintf(debug, "PME recv node %d %d <- %d grid start %d Communicating %d to %d\n",
 +                    pme->nodeid, overlap->nodeid, recv_id,
 +                    pme->pmegrid_start_iy,
 +                    recv_index0-pme->pmegrid_start_iy,
 +                    recv_index0-pme->pmegrid_start_iy+recv_nindex);
 +        }
 +        icnt = 0;
 +        for (i = 0; i < pme->pmegrid_nx; i++)
 +        {
 +            ix = i;
 +            for (j = 0; j < recv_nindex; j++)
 +            {
 +                iy = j + recv_index0 - pme->pmegrid_start_iy;
 +                for (k = 0; k < pme->nkz; k++)
 +                {
 +                    iz = k;
 +                    if (direction == GMX_SUM_GRID_FORWARD)
 +                    {
 +                        grid[ix*(pme->pmegrid_ny*pme->pmegrid_nz)+iy*(pme->pmegrid_nz)+iz] += overlap->recvbuf[icnt++];
 +                    }
 +                    else
 +                    {
 +                        grid[ix*(pme->pmegrid_ny*pme->pmegrid_nz)+iy*(pme->pmegrid_nz)+iz]  = overlap->recvbuf[icnt++];
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    /* Major dimension is easier, no copying required,
 +     * but we might have to sum to separate array.
 +     * Since we don't copy, we have to communicate up to pmegrid_nz,
 +     * not nkz as for the minor direction.
 +     */
 +    overlap = &pme->overlap[0];
 +
 +    for (ipulse = 0; ipulse < overlap->noverlap_nodes; ipulse++)
 +    {
 +        if (direction == GMX_SUM_GRID_FORWARD)
 +        {
 +            send_id       = overlap->send_id[ipulse];
 +            recv_id       = overlap->recv_id[ipulse];
 +            send_index0   = overlap->comm_data[ipulse].send_index0;
 +            send_nindex   = overlap->comm_data[ipulse].send_nindex;
 +            recv_index0   = overlap->comm_data[ipulse].recv_index0;
 +            recv_nindex   = overlap->comm_data[ipulse].recv_nindex;
 +            recvptr       = overlap->recvbuf;
 +        }
 +        else
 +        {
 +            send_id       = overlap->recv_id[ipulse];
 +            recv_id       = overlap->send_id[ipulse];
 +            send_index0   = overlap->comm_data[ipulse].recv_index0;
 +            send_nindex   = overlap->comm_data[ipulse].recv_nindex;
 +            recv_index0   = overlap->comm_data[ipulse].send_index0;
 +            recv_nindex   = overlap->comm_data[ipulse].send_nindex;
 +            recvptr       = grid + (recv_index0-pme->pmegrid_start_ix)*(pme->pmegrid_ny*pme->pmegrid_nz);
 +        }
 +
 +        sendptr       = grid + (send_index0-pme->pmegrid_start_ix)*(pme->pmegrid_ny*pme->pmegrid_nz);
 +        datasize      = pme->pmegrid_ny * pme->pmegrid_nz;
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "PME send node %d %d -> %d grid start %d Communicating %d to %d\n",
 +                    pme->nodeid, overlap->nodeid, send_id,
 +                    pme->pmegrid_start_ix,
 +                    send_index0-pme->pmegrid_start_ix,
 +                    send_index0-pme->pmegrid_start_ix+send_nindex);
 +            fprintf(debug, "PME recv node %d %d <- %d grid start %d Communicating %d to %d\n",
 +                    pme->nodeid, overlap->nodeid, recv_id,
 +                    pme->pmegrid_start_ix,
 +                    recv_index0-pme->pmegrid_start_ix,
 +                    recv_index0-pme->pmegrid_start_ix+recv_nindex);
 +        }
 +
 +        MPI_Sendrecv(sendptr, send_nindex*datasize, GMX_MPI_REAL,
 +                     send_id, ipulse,
 +                     recvptr, recv_nindex*datasize, GMX_MPI_REAL,
 +                     recv_id, ipulse,
 +                     overlap->mpi_comm, &stat);
 +
 +        /* ADD data from contiguous recv buffer */
 +        if (direction == GMX_SUM_GRID_FORWARD)
 +        {
 +            p = grid + (recv_index0-pme->pmegrid_start_ix)*(pme->pmegrid_ny*pme->pmegrid_nz);
 +            for (i = 0; i < recv_nindex*datasize; i++)
 +            {
 +                p[i] += overlap->recvbuf[i];
 +            }
 +        }
 +    }
 +}
 +#endif
 +
 +
 +static int copy_pmegrid_to_fftgrid(gmx_pme_t pme, real *pmegrid, real *fftgrid, int grid_index)
 +{
 +    ivec    local_fft_ndata, local_fft_offset, local_fft_size;
 +    ivec    local_pme_size;
 +    int     i, ix, iy, iz;
 +    int     pmeidx, fftidx;
 +
 +    /* Dimensions should be identical for A/B grid, so we just use A here */
 +    gmx_parallel_3dfft_real_limits(pme->pfft_setup[grid_index],
 +                                   local_fft_ndata,
 +                                   local_fft_offset,
 +                                   local_fft_size);
 +
 +    local_pme_size[0] = pme->pmegrid_nx;
 +    local_pme_size[1] = pme->pmegrid_ny;
 +    local_pme_size[2] = pme->pmegrid_nz;
 +
 +    /* The fftgrid is always 'justified' to the lower-left corner of the PME grid,
 +       the offset is identical, and the PME grid always has more data (due to overlap)
 +     */
 +    {
 +#ifdef DEBUG_PME
 +        FILE *fp, *fp2;
 +        char  fn[STRLEN];
 +        real  val;
 +        sprintf(fn, "pmegrid%d.pdb", pme->nodeid);
 +        fp = gmx_ffopen(fn, "w");
 +        sprintf(fn, "pmegrid%d.txt", pme->nodeid);
 +        fp2 = gmx_ffopen(fn, "w");
 +#endif
 +
 +        for (ix = 0; ix < local_fft_ndata[XX]; ix++)
 +        {
 +            for (iy = 0; iy < local_fft_ndata[YY]; iy++)
 +            {
 +                for (iz = 0; iz < local_fft_ndata[ZZ]; iz++)
 +                {
 +                    pmeidx          = ix*(local_pme_size[YY]*local_pme_size[ZZ])+iy*(local_pme_size[ZZ])+iz;
 +                    fftidx          = ix*(local_fft_size[YY]*local_fft_size[ZZ])+iy*(local_fft_size[ZZ])+iz;
 +                    fftgrid[fftidx] = pmegrid[pmeidx];
 +#ifdef DEBUG_PME
 +                    val = 100*pmegrid[pmeidx];
 +                    if (pmegrid[pmeidx] != 0)
 +                    {
 +                        gmx_fprintf_pdb_atomline(fp, epdbATOM, pmeidx, "CA", ' ', "GLY", ' ', pmeidx, ' ',
 +                                                 5.0*ix, 5.0*iy, 5.0*iz, 1.0, val, "");
 +                    }
 +                    if (pmegrid[pmeidx] != 0)
 +                    {
 +                        fprintf(fp2, "%-12s  %5d  %5d  %5d  %12.5e\n",
 +                                "qgrid",
 +                                pme->pmegrid_start_ix + ix,
 +                                pme->pmegrid_start_iy + iy,
 +                                pme->pmegrid_start_iz + iz,
 +                                pmegrid[pmeidx]);
 +                    }
 +#endif
 +                }
 +            }
 +        }
 +#ifdef DEBUG_PME
 +        gmx_ffclose(fp);
 +        gmx_ffclose(fp2);
 +#endif
 +    }
 +    return 0;
 +}
 +
 +
 +static gmx_cycles_t omp_cyc_start()
 +{
 +    return gmx_cycles_read();
 +}
 +
 +static gmx_cycles_t omp_cyc_end(gmx_cycles_t c)
 +{
 +    return gmx_cycles_read() - c;
 +}
 +
 +
 +static int copy_fftgrid_to_pmegrid(gmx_pme_t pme, const real *fftgrid, real *pmegrid, int grid_index,
 +                                   int nthread, int thread)
 +{
 +    ivec          local_fft_ndata, local_fft_offset, local_fft_size;
 +    ivec          local_pme_size;
 +    int           ixy0, ixy1, ixy, ix, iy, iz;
 +    int           pmeidx, fftidx;
 +#ifdef PME_TIME_THREADS
 +    gmx_cycles_t  c1;
 +    static double cs1 = 0;
 +    static int    cnt = 0;
 +#endif
 +
 +#ifdef PME_TIME_THREADS
 +    c1 = omp_cyc_start();
 +#endif
 +    /* Dimensions should be identical for A/B grid, so we just use A here */
 +    gmx_parallel_3dfft_real_limits(pme->pfft_setup[grid_index],
 +                                   local_fft_ndata,
 +                                   local_fft_offset,
 +                                   local_fft_size);
 +
 +    local_pme_size[0] = pme->pmegrid_nx;
 +    local_pme_size[1] = pme->pmegrid_ny;
 +    local_pme_size[2] = pme->pmegrid_nz;
 +
 +    /* The fftgrid is always 'justified' to the lower-left corner of the PME grid,
 +       the offset is identical, and the PME grid always has more data (due to overlap)
 +     */
 +    ixy0 = ((thread  )*local_fft_ndata[XX]*local_fft_ndata[YY])/nthread;
 +    ixy1 = ((thread+1)*local_fft_ndata[XX]*local_fft_ndata[YY])/nthread;
 +
 +    for (ixy = ixy0; ixy < ixy1; ixy++)
 +    {
 +        ix = ixy/local_fft_ndata[YY];
 +        iy = ixy - ix*local_fft_ndata[YY];
 +
 +        pmeidx = (ix*local_pme_size[YY] + iy)*local_pme_size[ZZ];
 +        fftidx = (ix*local_fft_size[YY] + iy)*local_fft_size[ZZ];
 +        for (iz = 0; iz < local_fft_ndata[ZZ]; iz++)
 +        {
 +            pmegrid[pmeidx+iz] = fftgrid[fftidx+iz];
 +        }
 +    }
 +
 +#ifdef PME_TIME_THREADS
 +    c1   = omp_cyc_end(c1);
 +    cs1 += (double)c1;
 +    cnt++;
 +    if (cnt % 20 == 0)
 +    {
 +        printf("copy %.2f\n", cs1*1e-9);
 +    }
 +#endif
 +
 +    return 0;
 +}
 +
 +
 +static void wrap_periodic_pmegrid(gmx_pme_t pme, real *pmegrid)
 +{
 +    int     nx, ny, nz, pnx, pny, pnz, ny_x, overlap, ix, iy, iz;
 +
 +    nx = pme->nkx;
 +    ny = pme->nky;
 +    nz = pme->nkz;
 +
 +    pnx = pme->pmegrid_nx;
 +    pny = pme->pmegrid_ny;
 +    pnz = pme->pmegrid_nz;
 +
 +    overlap = pme->pme_order - 1;
 +
 +    /* Add periodic overlap in z */
 +    for (ix = 0; ix < pme->pmegrid_nx; ix++)
 +    {
 +        for (iy = 0; iy < pme->pmegrid_ny; iy++)
 +        {
 +            for (iz = 0; iz < overlap; iz++)
 +            {
 +                pmegrid[(ix*pny+iy)*pnz+iz] +=
 +                    pmegrid[(ix*pny+iy)*pnz+nz+iz];
 +            }
 +        }
 +    }
 +
 +    if (pme->nnodes_minor == 1)
 +    {
 +        for (ix = 0; ix < pme->pmegrid_nx; ix++)
 +        {
 +            for (iy = 0; iy < overlap; iy++)
 +            {
 +                for (iz = 0; iz < nz; iz++)
 +                {
 +                    pmegrid[(ix*pny+iy)*pnz+iz] +=
 +                        pmegrid[(ix*pny+ny+iy)*pnz+iz];
 +                }
 +            }
 +        }
 +    }
 +
 +    if (pme->nnodes_major == 1)
 +    {
 +        ny_x = (pme->nnodes_minor == 1 ? ny : pme->pmegrid_ny);
 +
 +        for (ix = 0; ix < overlap; ix++)
 +        {
 +            for (iy = 0; iy < ny_x; iy++)
 +            {
 +                for (iz = 0; iz < nz; iz++)
 +                {
 +                    pmegrid[(ix*pny+iy)*pnz+iz] +=
 +                        pmegrid[((nx+ix)*pny+iy)*pnz+iz];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +
 +static void unwrap_periodic_pmegrid(gmx_pme_t pme, real *pmegrid)
 +{
 +    int     nx, ny, nz, pnx, pny, pnz, ny_x, overlap, ix;
 +
 +    nx = pme->nkx;
 +    ny = pme->nky;
 +    nz = pme->nkz;
 +
 +    pnx = pme->pmegrid_nx;
 +    pny = pme->pmegrid_ny;
 +    pnz = pme->pmegrid_nz;
 +
 +    overlap = pme->pme_order - 1;
 +
 +    if (pme->nnodes_major == 1)
 +    {
 +        ny_x = (pme->nnodes_minor == 1 ? ny : pme->pmegrid_ny);
 +
 +        for (ix = 0; ix < overlap; ix++)
 +        {
 +            int iy, iz;
 +
 +            for (iy = 0; iy < ny_x; iy++)
 +            {
 +                for (iz = 0; iz < nz; iz++)
 +                {
 +                    pmegrid[((nx+ix)*pny+iy)*pnz+iz] =
 +                        pmegrid[(ix*pny+iy)*pnz+iz];
 +                }
 +            }
 +        }
 +    }
 +
 +    if (pme->nnodes_minor == 1)
 +    {
 +#pragma omp parallel for num_threads(pme->nthread) schedule(static)
 +        for (ix = 0; ix < pme->pmegrid_nx; ix++)
 +        {
 +            int iy, iz;
 +
 +            for (iy = 0; iy < overlap; iy++)
 +            {
 +                for (iz = 0; iz < nz; iz++)
 +                {
 +                    pmegrid[(ix*pny+ny+iy)*pnz+iz] =
 +                        pmegrid[(ix*pny+iy)*pnz+iz];
 +                }
 +            }
 +        }
 +    }
 +
 +    /* Copy periodic overlap in z */
 +#pragma omp parallel for num_threads(pme->nthread) schedule(static)
 +    for (ix = 0; ix < pme->pmegrid_nx; ix++)
 +    {
 +        int iy, iz;
 +
 +        for (iy = 0; iy < pme->pmegrid_ny; iy++)
 +        {
 +            for (iz = 0; iz < overlap; iz++)
 +            {
 +                pmegrid[(ix*pny+iy)*pnz+nz+iz] =
 +                    pmegrid[(ix*pny+iy)*pnz+iz];
 +            }
 +        }
 +    }
 +}
 +
 +
 +/* This has to be a macro to enable full compiler optimization with xlC (and probably others too) */
 +#define DO_BSPLINE(order)                            \
 +    for (ithx = 0; (ithx < order); ithx++)                    \
 +    {                                                    \
 +        index_x = (i0+ithx)*pny*pnz;                     \
 +        valx    = coefficient*thx[ithx];                          \
 +                                                     \
 +        for (ithy = 0; (ithy < order); ithy++)                \
 +        {                                                \
 +            valxy    = valx*thy[ithy];                   \
 +            index_xy = index_x+(j0+ithy)*pnz;            \
 +                                                     \
 +            for (ithz = 0; (ithz < order); ithz++)            \
 +            {                                            \
 +                index_xyz        = index_xy+(k0+ithz);   \
 +                grid[index_xyz] += valxy*thz[ithz];      \
 +            }                                            \
 +        }                                                \
 +    }
 +
 +
 +static void spread_coefficients_bsplines_thread(pmegrid_t                    *pmegrid,
 +                                                pme_atomcomm_t               *atc,
 +                                                splinedata_t                 *spline,
 +                                                pme_spline_work_t gmx_unused *work)
 +{
 +
 +    /* spread coefficients from home atoms to local grid */
 +    real          *grid;
 +    pme_overlap_t *ol;
 +    int            b, i, nn, n, ithx, ithy, ithz, i0, j0, k0;
 +    int       *    idxptr;
 +    int            order, norder, index_x, index_xy, index_xyz;
 +    real           valx, valxy, coefficient;
 +    real          *thx, *thy, *thz;
 +    int            localsize, bndsize;
 +    int            pnx, pny, pnz, ndatatot;
 +    int            offx, offy, offz;
 +
 +#if defined PME_SIMD4_SPREAD_GATHER && !defined PME_SIMD4_UNALIGNED
 +    real           thz_buffer[GMX_SIMD4_WIDTH*3], *thz_aligned;
 +
 +    thz_aligned = gmx_simd4_align_r(thz_buffer);
 +#endif
 +
 +    pnx = pmegrid->s[XX];
 +    pny = pmegrid->s[YY];
 +    pnz = pmegrid->s[ZZ];
 +
 +    offx = pmegrid->offset[XX];
 +    offy = pmegrid->offset[YY];
 +    offz = pmegrid->offset[ZZ];
 +
 +    ndatatot = pnx*pny*pnz;
 +    grid     = pmegrid->grid;
 +    for (i = 0; i < ndatatot; i++)
 +    {
 +        grid[i] = 0;
 +    }
 +
 +    order = pmegrid->order;
 +
 +    for (nn = 0; nn < spline->n; nn++)
 +    {
 +        n           = spline->ind[nn];
 +        coefficient = atc->coefficient[n];
 +
 +        if (coefficient != 0)
 +        {
 +            idxptr = atc->idx[n];
 +            norder = nn*order;
 +
 +            i0   = idxptr[XX] - offx;
 +            j0   = idxptr[YY] - offy;
 +            k0   = idxptr[ZZ] - offz;
 +
 +            thx = spline->theta[XX] + norder;
 +            thy = spline->theta[YY] + norder;
 +            thz = spline->theta[ZZ] + norder;
 +
 +            switch (order)
 +            {
 +                case 4:
 +#ifdef PME_SIMD4_SPREAD_GATHER
 +#ifdef PME_SIMD4_UNALIGNED
 +#define PME_SPREAD_SIMD4_ORDER4
 +#else
 +#define PME_SPREAD_SIMD4_ALIGNED
 +#define PME_ORDER 4
 +#endif
 +#include "pme_simd4.h"
 +#else
 +                    DO_BSPLINE(4);
 +#endif
 +                    break;
 +                case 5:
 +#ifdef PME_SIMD4_SPREAD_GATHER
 +#define PME_SPREAD_SIMD4_ALIGNED
 +#define PME_ORDER 5
 +#include "pme_simd4.h"
 +#else
 +                    DO_BSPLINE(5);
 +#endif
 +                    break;
 +                default:
 +                    DO_BSPLINE(order);
 +                    break;
 +            }
 +        }
 +    }
 +}
 +
 +static void set_grid_alignment(int gmx_unused *pmegrid_nz, int gmx_unused pme_order)
 +{
 +#ifdef PME_SIMD4_SPREAD_GATHER
 +    if (pme_order == 5
 +#ifndef PME_SIMD4_UNALIGNED
 +        || pme_order == 4
 +#endif
 +        )
 +    {
 +        /* Round nz up to a multiple of 4 to ensure alignment */
 +        *pmegrid_nz = ((*pmegrid_nz + 3) & ~3);
 +    }
 +#endif
 +}
 +
 +static void set_gridsize_alignment(int gmx_unused *gridsize, int gmx_unused pme_order)
 +{
 +#ifdef PME_SIMD4_SPREAD_GATHER
 +#ifndef PME_SIMD4_UNALIGNED
 +    if (pme_order == 4)
 +    {
 +        /* Add extra elements to ensured aligned operations do not go
 +         * beyond the allocated grid size.
 +         * Note that for pme_order=5, the pme grid z-size alignment
 +         * ensures that we will not go beyond the grid size.
 +         */
 +        *gridsize += 4;
 +    }
 +#endif
 +#endif
 +}
 +
 +static void pmegrid_init(pmegrid_t *grid,
 +                         int cx, int cy, int cz,
 +                         int x0, int y0, int z0,
 +                         int x1, int y1, int z1,
 +                         gmx_bool set_alignment,
 +                         int pme_order,
 +                         real *ptr)
 +{
 +    int nz, gridsize;
 +
 +    grid->ci[XX]     = cx;
 +    grid->ci[YY]     = cy;
 +    grid->ci[ZZ]     = cz;
 +    grid->offset[XX] = x0;
 +    grid->offset[YY] = y0;
 +    grid->offset[ZZ] = z0;
 +    grid->n[XX]      = x1 - x0 + pme_order - 1;
 +    grid->n[YY]      = y1 - y0 + pme_order - 1;
 +    grid->n[ZZ]      = z1 - z0 + pme_order - 1;
 +    copy_ivec(grid->n, grid->s);
 +
 +    nz = grid->s[ZZ];
 +    set_grid_alignment(&nz, pme_order);
 +    if (set_alignment)
 +    {
 +        grid->s[ZZ] = nz;
 +    }
 +    else if (nz != grid->s[ZZ])
 +    {
 +        gmx_incons("pmegrid_init call with an unaligned z size");
 +    }
 +
 +    grid->order = pme_order;
 +    if (ptr == NULL)
 +    {
 +        gridsize = grid->s[XX]*grid->s[YY]*grid->s[ZZ];
 +        set_gridsize_alignment(&gridsize, pme_order);
 +        snew_aligned(grid->grid, gridsize, SIMD4_ALIGNMENT);
 +    }
 +    else
 +    {
 +        grid->grid = ptr;
 +    }
 +}
 +
 +static int div_round_up(int enumerator, int denominator)
 +{
 +    return (enumerator + denominator - 1)/denominator;
 +}
 +
 +static void make_subgrid_division(const ivec n, int ovl, int nthread,
 +                                  ivec nsub)
 +{
 +    int gsize_opt, gsize;
 +    int nsx, nsy, nsz;
 +    char *env;
 +
 +    gsize_opt = -1;
 +    for (nsx = 1; nsx <= nthread; nsx++)
 +    {
 +        if (nthread % nsx == 0)
 +        {
 +            for (nsy = 1; nsy <= nthread; nsy++)
 +            {
 +                if (nsx*nsy <= nthread && nthread % (nsx*nsy) == 0)
 +                {
 +                    nsz = nthread/(nsx*nsy);
 +
 +                    /* Determine the number of grid points per thread */
 +                    gsize =
 +                        (div_round_up(n[XX], nsx) + ovl)*
 +                        (div_round_up(n[YY], nsy) + ovl)*
 +                        (div_round_up(n[ZZ], nsz) + ovl);
 +
 +                    /* Minimize the number of grids points per thread
 +                     * and, secondarily, the number of cuts in minor dimensions.
 +                     */
 +                    if (gsize_opt == -1 ||
 +                        gsize < gsize_opt ||
 +                        (gsize == gsize_opt &&
 +                         (nsz < nsub[ZZ] || (nsz == nsub[ZZ] && nsy < nsub[YY]))))
 +                    {
 +                        nsub[XX]  = nsx;
 +                        nsub[YY]  = nsy;
 +                        nsub[ZZ]  = nsz;
 +                        gsize_opt = gsize;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    env = getenv("GMX_PME_THREAD_DIVISION");
 +    if (env != NULL)
 +    {
 +        sscanf(env, "%d %d %d", &nsub[XX], &nsub[YY], &nsub[ZZ]);
 +    }
 +
 +    if (nsub[XX]*nsub[YY]*nsub[ZZ] != nthread)
 +    {
 +        gmx_fatal(FARGS, "PME grid thread division (%d x %d x %d) does not match the total number of threads (%d)", nsub[XX], nsub[YY], nsub[ZZ], nthread);
 +    }
 +}
 +
 +static void pmegrids_init(pmegrids_t *grids,
 +                          int nx, int ny, int nz, int nz_base,
 +                          int pme_order,
 +                          gmx_bool bUseThreads,
 +                          int nthread,
 +                          int overlap_x,
 +                          int overlap_y)
 +{
 +    ivec n, n_base, g0, g1;
 +    int t, x, y, z, d, i, tfac;
 +    int max_comm_lines = -1;
 +
 +    n[XX] = nx - (pme_order - 1);
 +    n[YY] = ny - (pme_order - 1);
 +    n[ZZ] = nz - (pme_order - 1);
 +
 +    copy_ivec(n, n_base);
 +    n_base[ZZ] = nz_base;
 +
 +    pmegrid_init(&grids->grid, 0, 0, 0, 0, 0, 0, n[XX], n[YY], n[ZZ], FALSE, pme_order,
 +                 NULL);
 +
 +    grids->nthread = nthread;
 +
 +    make_subgrid_division(n_base, pme_order-1, grids->nthread, grids->nc);
 +
 +    if (bUseThreads)
 +    {
 +        ivec nst;
 +        int gridsize;
 +
 +        for (d = 0; d < DIM; d++)
 +        {
 +            nst[d] = div_round_up(n[d], grids->nc[d]) + pme_order - 1;
 +        }
 +        set_grid_alignment(&nst[ZZ], pme_order);
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "pmegrid thread local division: %d x %d x %d\n",
 +                    grids->nc[XX], grids->nc[YY], grids->nc[ZZ]);
 +            fprintf(debug, "pmegrid %d %d %d max thread pmegrid %d %d %d\n",
 +                    nx, ny, nz,
 +                    nst[XX], nst[YY], nst[ZZ]);
 +        }
 +
 +        snew(grids->grid_th, grids->nthread);
 +        t        = 0;
 +        gridsize = nst[XX]*nst[YY]*nst[ZZ];
 +        set_gridsize_alignment(&gridsize, pme_order);
 +        snew_aligned(grids->grid_all,
 +                     grids->nthread*gridsize+(grids->nthread+1)*GMX_CACHE_SEP,
 +                     SIMD4_ALIGNMENT);
 +
 +        for (x = 0; x < grids->nc[XX]; x++)
 +        {
 +            for (y = 0; y < grids->nc[YY]; y++)
 +            {
 +                for (z = 0; z < grids->nc[ZZ]; z++)
 +                {
 +                    pmegrid_init(&grids->grid_th[t],
 +                                 x, y, z,
 +                                 (n[XX]*(x  ))/grids->nc[XX],
 +                                 (n[YY]*(y  ))/grids->nc[YY],
 +                                 (n[ZZ]*(z  ))/grids->nc[ZZ],
 +                                 (n[XX]*(x+1))/grids->nc[XX],
 +                                 (n[YY]*(y+1))/grids->nc[YY],
 +                                 (n[ZZ]*(z+1))/grids->nc[ZZ],
 +                                 TRUE,
 +                                 pme_order,
 +                                 grids->grid_all+GMX_CACHE_SEP+t*(gridsize+GMX_CACHE_SEP));
 +                    t++;
 +                }
 +            }
 +        }
 +    }
 +    else
 +    {
 +        grids->grid_th = NULL;
 +    }
 +
 +    snew(grids->g2t, DIM);
 +    tfac = 1;
 +    for (d = DIM-1; d >= 0; d--)
 +    {
 +        snew(grids->g2t[d], n[d]);
 +        t = 0;
 +        for (i = 0; i < n[d]; i++)
 +        {
 +            /* The second check should match the parameters
 +             * of the pmegrid_init call above.
 +             */
 +            while (t + 1 < grids->nc[d] && i >= (n[d]*(t+1))/grids->nc[d])
 +            {
 +                t++;
 +            }
 +            grids->g2t[d][i] = t*tfac;
 +        }
 +
 +        tfac *= grids->nc[d];
 +
 +        switch (d)
 +        {
 +            case XX: max_comm_lines = overlap_x;     break;
 +            case YY: max_comm_lines = overlap_y;     break;
 +            case ZZ: max_comm_lines = pme_order - 1; break;
 +        }
 +        grids->nthread_comm[d] = 0;
 +        while ((n[d]*grids->nthread_comm[d])/grids->nc[d] < max_comm_lines &&
 +               grids->nthread_comm[d] < grids->nc[d])
 +        {
 +            grids->nthread_comm[d]++;
 +        }
 +        if (debug != NULL)
 +        {
 +            fprintf(debug, "pmegrid thread grid communication range in %c: %d\n",
 +                    'x'+d, grids->nthread_comm[d]);
 +        }
 +        /* It should be possible to make grids->nthread_comm[d]==grids->nc[d]
 +         * work, but this is not a problematic restriction.
 +         */
 +        if (grids->nc[d] > 1 && grids->nthread_comm[d] > grids->nc[d])
 +        {
 +            gmx_fatal(FARGS, "Too many threads for PME (%d) compared to the number of grid lines, reduce the number of threads doing PME", grids->nthread);
 +        }
 +    }
 +}
 +
 +
 +static void pmegrids_destroy(pmegrids_t *grids)
 +{
 +    int t;
 +
 +    if (grids->grid.grid != NULL)
 +    {
 +        sfree(grids->grid.grid);
 +
 +        if (grids->nthread > 0)
 +        {
 +            for (t = 0; t < grids->nthread; t++)
 +            {
 +                sfree(grids->grid_th[t].grid);
 +            }
 +            sfree(grids->grid_th);
 +        }
 +    }
 +}
 +
 +
 +static void realloc_work(pme_work_t *work, int nkx)
 +{
 +    int simd_width;
 +
 +    if (nkx > work->nalloc)
 +    {
 +        work->nalloc = nkx;
 +        srenew(work->mhx, work->nalloc);
 +        srenew(work->mhy, work->nalloc);
 +        srenew(work->mhz, work->nalloc);
 +        srenew(work->m2, work->nalloc);
 +        /* Allocate an aligned pointer for SIMD operations, including extra
 +         * elements at the end for padding.
 +         */
 +#ifdef PME_SIMD_SOLVE
 +        simd_width = GMX_SIMD_REAL_WIDTH;
 +#else
 +        /* We can use any alignment, apart from 0, so we use 4 */
 +        simd_width = 4;
 +#endif
 +        sfree_aligned(work->denom);
 +        sfree_aligned(work->tmp1);
 +        sfree_aligned(work->tmp2);
 +        sfree_aligned(work->eterm);
 +        snew_aligned(work->denom, work->nalloc+simd_width, simd_width*sizeof(real));
 +        snew_aligned(work->tmp1,  work->nalloc+simd_width, simd_width*sizeof(real));
 +        snew_aligned(work->tmp2,  work->nalloc+simd_width, simd_width*sizeof(real));
 +        snew_aligned(work->eterm, work->nalloc+simd_width, simd_width*sizeof(real));
 +        srenew(work->m2inv, work->nalloc);
 +    }
 +}
 +
 +
 +static void free_work(pme_work_t *work)
 +{
 +    sfree(work->mhx);
 +    sfree(work->mhy);
 +    sfree(work->mhz);
 +    sfree(work->m2);
 +    sfree_aligned(work->denom);
 +    sfree_aligned(work->tmp1);
 +    sfree_aligned(work->tmp2);
 +    sfree_aligned(work->eterm);
 +    sfree(work->m2inv);
 +}
 +
 +
 +#if defined PME_SIMD_SOLVE
 +/* Calculate exponentials through SIMD */
 +gmx_inline static void calc_exponentials_q(int gmx_unused start, int end, real f, real *d_aligned, real *r_aligned, real *e_aligned)
 +{
 +    {
 +        const gmx_simd_real_t two = gmx_simd_set1_r(2.0);
 +        gmx_simd_real_t f_simd;
 +        gmx_simd_real_t lu;
 +        gmx_simd_real_t tmp_d1, d_inv, tmp_r, tmp_e;
 +        int kx;
 +        f_simd = gmx_simd_set1_r(f);
 +        /* We only need to calculate from start. But since start is 0 or 1
 +         * and we want to use aligned loads/stores, we always start from 0.
 +         */
 +        for (kx = 0; kx < end; kx += GMX_SIMD_REAL_WIDTH)
 +        {
 +            tmp_d1   = gmx_simd_load_r(d_aligned+kx);
 +            d_inv    = gmx_simd_inv_r(tmp_d1);
 +            tmp_r    = gmx_simd_load_r(r_aligned+kx);
 +            tmp_r    = gmx_simd_exp_r(tmp_r);
 +            tmp_e    = gmx_simd_mul_r(f_simd, d_inv);
 +            tmp_e    = gmx_simd_mul_r(tmp_e, tmp_r);
 +            gmx_simd_store_r(e_aligned+kx, tmp_e);
 +        }
 +    }
 +}
 +#else
 +gmx_inline static void calc_exponentials_q(int start, int end, real f, real *d, real *r, real *e)
 +{
 +    int kx;
 +    for (kx = start; kx < end; kx++)
 +    {
 +        d[kx] = 1.0/d[kx];
 +    }
 +    for (kx = start; kx < end; kx++)
 +    {
 +        r[kx] = exp(r[kx]);
 +    }
 +    for (kx = start; kx < end; kx++)
 +    {
 +        e[kx] = f*r[kx]*d[kx];
 +    }
 +}
 +#endif
 +
 +#if defined PME_SIMD_SOLVE
 +/* Calculate exponentials through SIMD */
 +gmx_inline static void calc_exponentials_lj(int gmx_unused start, int end, real *r_aligned, real *factor_aligned, real *d_aligned)
 +{
 +    gmx_simd_real_t tmp_r, tmp_d, tmp_fac, d_inv, tmp_mk;
 +    const gmx_simd_real_t sqr_PI = gmx_simd_sqrt_r(gmx_simd_set1_r(M_PI));
 +    int kx;
 +    for (kx = 0; kx < end; kx += GMX_SIMD_REAL_WIDTH)
 +    {
 +        /* We only need to calculate from start. But since start is 0 or 1
 +         * and we want to use aligned loads/stores, we always start from 0.
 +         */
 +        tmp_d = gmx_simd_load_r(d_aligned+kx);
 +        d_inv = gmx_simd_inv_r(tmp_d);
 +        gmx_simd_store_r(d_aligned+kx, d_inv);
 +        tmp_r = gmx_simd_load_r(r_aligned+kx);
 +        tmp_r = gmx_simd_exp_r(tmp_r);
 +        gmx_simd_store_r(r_aligned+kx, tmp_r);
 +        tmp_mk  = gmx_simd_load_r(factor_aligned+kx);
 +        tmp_fac = gmx_simd_mul_r(sqr_PI, gmx_simd_mul_r(tmp_mk, gmx_simd_erfc_r(tmp_mk)));
 +        gmx_simd_store_r(factor_aligned+kx, tmp_fac);
 +    }
 +}
 +#else
 +gmx_inline static void calc_exponentials_lj(int start, int end, real *r, real *tmp2, real *d)
 +{
 +    int kx;
 +    real mk;
 +    for (kx = start; kx < end; kx++)
 +    {
 +        d[kx] = 1.0/d[kx];
 +    }
 +
 +    for (kx = start; kx < end; kx++)
 +    {
 +        r[kx] = exp(r[kx]);
 +    }
 +
 +    for (kx = start; kx < end; kx++)
 +    {
 +        mk       = tmp2[kx];
 +        tmp2[kx] = sqrt(M_PI)*mk*gmx_erfc(mk);
 +    }
 +}
 +#endif
 +
 +static int solve_pme_yzx(gmx_pme_t pme, t_complex *grid,
 +                         real ewaldcoeff, real vol,
 +                         gmx_bool bEnerVir,
 +                         int nthread, int thread)
 +{
 +    /* do recip sum over local cells in grid */
 +    /* y major, z middle, x minor or continuous */
 +    t_complex *p0;
 +    int     kx, ky, kz, maxkx, maxky, maxkz;
 +    int     nx, ny, nz, iyz0, iyz1, iyz, iy, iz, kxstart, kxend;
 +    real    mx, my, mz;
 +    real    factor = M_PI*M_PI/(ewaldcoeff*ewaldcoeff);
 +    real    ets2, struct2, vfactor, ets2vf;
 +    real    d1, d2, energy = 0;
 +    real    by, bz;
 +    real    virxx = 0, virxy = 0, virxz = 0, viryy = 0, viryz = 0, virzz = 0;
 +    real    rxx, ryx, ryy, rzx, rzy, rzz;
 +    pme_work_t *work;
 +    real    *mhx, *mhy, *mhz, *m2, *denom, *tmp1, *eterm, *m2inv;
 +    real    mhxk, mhyk, mhzk, m2k;
 +    real    corner_fac;
 +    ivec    complex_order;
 +    ivec    local_ndata, local_offset, local_size;
 +    real    elfac;
 +
 +    elfac = ONE_4PI_EPS0/pme->epsilon_r;
 +
 +    nx = pme->nkx;
 +    ny = pme->nky;
 +    nz = pme->nkz;
 +
 +    /* Dimensions should be identical for A/B grid, so we just use A here */
 +    gmx_parallel_3dfft_complex_limits(pme->pfft_setup[PME_GRID_QA],
 +                                      complex_order,
 +                                      local_ndata,
 +                                      local_offset,
 +                                      local_size);
 +
 +    rxx = pme->recipbox[XX][XX];
 +    ryx = pme->recipbox[YY][XX];
 +    ryy = pme->recipbox[YY][YY];
 +    rzx = pme->recipbox[ZZ][XX];
 +    rzy = pme->recipbox[ZZ][YY];
 +    rzz = pme->recipbox[ZZ][ZZ];
 +
 +    maxkx = (nx+1)/2;
 +    maxky = (ny+1)/2;
 +    maxkz = nz/2+1;
 +
 +    work  = &pme->work[thread];
 +    mhx   = work->mhx;
 +    mhy   = work->mhy;
 +    mhz   = work->mhz;
 +    m2    = work->m2;
 +    denom = work->denom;
 +    tmp1  = work->tmp1;
 +    eterm = work->eterm;
 +    m2inv = work->m2inv;
 +
 +    iyz0 = local_ndata[YY]*local_ndata[ZZ]* thread   /nthread;
 +    iyz1 = local_ndata[YY]*local_ndata[ZZ]*(thread+1)/nthread;
 +
 +    for (iyz = iyz0; iyz < iyz1; iyz++)
 +    {
 +        iy = iyz/local_ndata[ZZ];
 +        iz = iyz - iy*local_ndata[ZZ];
 +
 +        ky = iy + local_offset[YY];
 +
 +        if (ky < maxky)
 +        {
 +            my = ky;
 +        }
 +        else
 +        {
 +            my = (ky - ny);
 +        }
 +
 +        by = M_PI*vol*pme->bsp_mod[YY][ky];
 +
 +        kz = iz + local_offset[ZZ];
 +
 +        mz = kz;
 +
 +        bz = pme->bsp_mod[ZZ][kz];
 +
 +        /* 0.5 correction for corner points */
 +        corner_fac = 1;
 +        if (kz == 0 || kz == (nz+1)/2)
 +        {
 +            corner_fac = 0.5;
 +        }
 +
 +        p0 = grid + iy*local_size[ZZ]*local_size[XX] + iz*local_size[XX];
 +
 +        /* We should skip the k-space point (0,0,0) */
 +        /* Note that since here x is the minor index, local_offset[XX]=0 */
 +        if (local_offset[XX] > 0 || ky > 0 || kz > 0)
 +        {
 +            kxstart = local_offset[XX];
 +        }
 +        else
 +        {
 +            kxstart = local_offset[XX] + 1;
 +            p0++;
 +        }
 +        kxend = local_offset[XX] + local_ndata[XX];
 +
 +        if (bEnerVir)
 +        {
 +            /* More expensive inner loop, especially because of the storage
 +             * of the mh elements in array's.
 +             * Because x is the minor grid index, all mh elements
 +             * depend on kx for triclinic unit cells.
 +             */
 +
 +            /* Two explicit loops to avoid a conditional inside the loop */
 +            for (kx = kxstart; kx < maxkx; kx++)
 +            {
 +                mx = kx;
 +
 +                mhxk      = mx * rxx;
 +                mhyk      = mx * ryx + my * ryy;
 +                mhzk      = mx * rzx + my * rzy + mz * rzz;
 +                m2k       = mhxk*mhxk + mhyk*mhyk + mhzk*mhzk;
 +                mhx[kx]   = mhxk;
 +                mhy[kx]   = mhyk;
 +                mhz[kx]   = mhzk;
 +                m2[kx]    = m2k;
 +                denom[kx] = m2k*bz*by*pme->bsp_mod[XX][kx];
 +                tmp1[kx]  = -factor*m2k;
 +            }
 +
 +            for (kx = maxkx; kx < kxend; kx++)
 +            {
 +                mx = (kx - nx);
 +
 +                mhxk      = mx * rxx;
 +                mhyk      = mx * ryx + my * ryy;
 +                mhzk      = mx * rzx + my * rzy + mz * rzz;
 +                m2k       = mhxk*mhxk + mhyk*mhyk + mhzk*mhzk;
 +                mhx[kx]   = mhxk;
 +                mhy[kx]   = mhyk;
 +                mhz[kx]   = mhzk;
 +                m2[kx]    = m2k;
 +                denom[kx] = m2k*bz*by*pme->bsp_mod[XX][kx];
 +                tmp1[kx]  = -factor*m2k;
 +            }
 +
 +            for (kx = kxstart; kx < kxend; kx++)
 +            {
 +                m2inv[kx] = 1.0/m2[kx];
 +            }
 +
 +            calc_exponentials_q(kxstart, kxend, elfac, denom, tmp1, eterm);
 +
 +            for (kx = kxstart; kx < kxend; kx++, p0++)
 +            {
 +                d1      = p0->re;
 +                d2      = p0->im;
 +
 +                p0->re  = d1*eterm[kx];
 +                p0->im  = d2*eterm[kx];
 +
 +                struct2 = 2.0*(d1*d1+d2*d2);
 +
 +                tmp1[kx] = eterm[kx]*struct2;
 +            }
 +
 +            for (kx = kxstart; kx < kxend; kx++)
 +            {
 +                ets2     = corner_fac*tmp1[kx];
 +                vfactor  = (factor*m2[kx] + 1.0)*2.0*m2inv[kx];
 +                energy  += ets2;
 +
 +                ets2vf   = ets2*vfactor;
 +                virxx   += ets2vf*mhx[kx]*mhx[kx] - ets2;
 +                virxy   += ets2vf*mhx[kx]*mhy[kx];
 +                virxz   += ets2vf*mhx[kx]*mhz[kx];
 +                viryy   += ets2vf*mhy[kx]*mhy[kx] - ets2;
 +                viryz   += ets2vf*mhy[kx]*mhz[kx];
 +                virzz   += ets2vf*mhz[kx]*mhz[kx] - ets2;
 +            }
 +        }
 +        else
 +        {
 +            /* We don't need to calculate the energy and the virial.
 +             * In this case the triclinic overhead is small.
 +             */
 +
 +            /* Two explicit loops to avoid a conditional inside the loop */
 +
 +            for (kx = kxstart; kx < maxkx; kx++)
 +            {
 +                mx = kx;
 +
 +                mhxk      = mx * rxx;
 +                mhyk      = mx * ryx + my * ryy;
 +                mhzk      = mx * rzx + my * rzy + mz * rzz;
 +                m2k       = mhxk*mhxk + mhyk*mhyk + mhzk*mhzk;
 +                denom[kx] = m2k*bz*by*pme->bsp_mod[XX][kx];
 +                tmp1[kx]  = -factor*m2k;
 +            }
 +
 +            for (kx = maxkx; kx < kxend; kx++)
 +            {
 +                mx = (kx - nx);
 +
 +                mhxk      = mx * rxx;
 +                mhyk      = mx * ryx + my * ryy;
 +                mhzk      = mx * rzx + my * rzy + mz * rzz;
 +                m2k       = mhxk*mhxk + mhyk*mhyk + mhzk*mhzk;
 +                denom[kx] = m2k*bz*by*pme->bsp_mod[XX][kx];
 +                tmp1[kx]  = -factor*m2k;
 +            }
 +
 +            calc_exponentials_q(kxstart, kxend, elfac, denom, tmp1, eterm);
 +
 +            for (kx = kxstart; kx < kxend; kx++, p0++)
 +            {
 +                d1      = p0->re;
 +                d2      = p0->im;
 +
 +                p0->re  = d1*eterm[kx];
 +                p0->im  = d2*eterm[kx];
 +            }
 +        }
 +    }
 +
 +    if (bEnerVir)
 +    {
 +        /* Update virial with local values.
 +         * The virial is symmetric by definition.
 +         * this virial seems ok for isotropic scaling, but I'm
 +         * experiencing problems on semiisotropic membranes.
 +         * IS THAT COMMENT STILL VALID??? (DvdS, 2001/02/07).
 +         */
 +        work->vir_q[XX][XX] = 0.25*virxx;
 +        work->vir_q[YY][YY] = 0.25*viryy;
 +        work->vir_q[ZZ][ZZ] = 0.25*virzz;
 +        work->vir_q[XX][YY] = work->vir_q[YY][XX] = 0.25*virxy;
 +        work->vir_q[XX][ZZ] = work->vir_q[ZZ][XX] = 0.25*virxz;
 +        work->vir_q[YY][ZZ] = work->vir_q[ZZ][YY] = 0.25*viryz;
 +
 +        /* This energy should be corrected for a charged system */
 +        work->energy_q = 0.5*energy;
 +    }
 +
 +    /* Return the loop count */
 +    return local_ndata[YY]*local_ndata[XX];
 +}
 +
 +static int solve_pme_lj_yzx(gmx_pme_t pme, t_complex **grid, gmx_bool bLB,
 +                            real ewaldcoeff, real vol,
 +                            gmx_bool bEnerVir, int nthread, int thread)
 +{
 +    /* do recip sum over local cells in grid */
 +    /* y major, z middle, x minor or continuous */
 +    int     ig, gcount;
 +    int     kx, ky, kz, maxkx, maxky, maxkz;
 +    int     nx, ny, nz, iy, iyz0, iyz1, iyz, iz, kxstart, kxend;
 +    real    mx, my, mz;
 +    real    factor = M_PI*M_PI/(ewaldcoeff*ewaldcoeff);
 +    real    ets2, ets2vf;
 +    real    eterm, vterm, d1, d2, energy = 0;
 +    real    by, bz;
 +    real    virxx = 0, virxy = 0, virxz = 0, viryy = 0, viryz = 0, virzz = 0;
 +    real    rxx, ryx, ryy, rzx, rzy, rzz;
 +    real    *mhx, *mhy, *mhz, *m2, *denom, *tmp1, *tmp2;
 +    real    mhxk, mhyk, mhzk, m2k;
 +    real    mk;
 +    pme_work_t *work;
 +    real    corner_fac;
 +    ivec    complex_order;
 +    ivec    local_ndata, local_offset, local_size;
 +    nx = pme->nkx;
 +    ny = pme->nky;
 +    nz = pme->nkz;
 +
 +    /* Dimensions should be identical for A/B grid, so we just use A here */
 +    gmx_parallel_3dfft_complex_limits(pme->pfft_setup[PME_GRID_C6A],
 +                                      complex_order,
 +                                      local_ndata,
 +                                      local_offset,
 +                                      local_size);
 +    rxx = pme->recipbox[XX][XX];
 +    ryx = pme->recipbox[YY][XX];
 +    ryy = pme->recipbox[YY][YY];
 +    rzx = pme->recipbox[ZZ][XX];
 +    rzy = pme->recipbox[ZZ][YY];
 +    rzz = pme->recipbox[ZZ][ZZ];
 +
 +    maxkx = (nx+1)/2;
 +    maxky = (ny+1)/2;
 +    maxkz = nz/2+1;
 +
 +    work  = &pme->work[thread];
 +    mhx   = work->mhx;
 +    mhy   = work->mhy;
 +    mhz   = work->mhz;
 +    m2    = work->m2;
 +    denom = work->denom;
 +    tmp1  = work->tmp1;
 +    tmp2  = work->tmp2;
 +
 +    iyz0 = local_ndata[YY]*local_ndata[ZZ]* thread   /nthread;
 +    iyz1 = local_ndata[YY]*local_ndata[ZZ]*(thread+1)/nthread;
 +
 +    for (iyz = iyz0; iyz < iyz1; iyz++)
 +    {
 +        iy = iyz/local_ndata[ZZ];
 +        iz = iyz - iy*local_ndata[ZZ];
 +
 +        ky = iy + local_offset[YY];
 +
 +        if (ky < maxky)
 +        {
 +            my = ky;
 +        }
 +        else
 +        {
 +            my = (ky - ny);
 +        }
 +
 +        by = 3.0*vol*pme->bsp_mod[YY][ky]
 +            / (M_PI*sqrt(M_PI)*ewaldcoeff*ewaldcoeff*ewaldcoeff);
 +
 +        kz = iz + local_offset[ZZ];
 +
 +        mz = kz;
 +
 +        bz = pme->bsp_mod[ZZ][kz];
 +
 +        /* 0.5 correction for corner points */
 +        corner_fac = 1;
 +        if (kz == 0 || kz == (nz+1)/2)
 +        {
 +            corner_fac = 0.5;
 +        }
 +
 +        kxstart = local_offset[XX];
 +        kxend   = local_offset[XX] + local_ndata[XX];
 +        if (bEnerVir)
 +        {
 +            /* More expensive inner loop, especially because of the
 +             * storage of the mh elements in array's.  Because x is the
 +             * minor grid index, all mh elements depend on kx for
 +             * triclinic unit cells.
 +             */
 +
 +            /* Two explicit loops to avoid a conditional inside the loop */
 +            for (kx = kxstart; kx < maxkx; kx++)
 +            {
 +                mx = kx;
 +
 +                mhxk      = mx * rxx;
 +                mhyk      = mx * ryx + my * ryy;
 +                mhzk      = mx * rzx + my * rzy + mz * rzz;
 +                m2k       = mhxk*mhxk + mhyk*mhyk + mhzk*mhzk;
 +                mhx[kx]   = mhxk;
 +                mhy[kx]   = mhyk;
 +                mhz[kx]   = mhzk;
 +                m2[kx]    = m2k;
 +                denom[kx] = bz*by*pme->bsp_mod[XX][kx];
 +                tmp1[kx]  = -factor*m2k;
 +                tmp2[kx]  = sqrt(factor*m2k);
 +            }
 +
 +            for (kx = maxkx; kx < kxend; kx++)
 +            {
 +                mx = (kx - nx);
 +
 +                mhxk      = mx * rxx;
 +                mhyk      = mx * ryx + my * ryy;
 +                mhzk      = mx * rzx + my * rzy + mz * rzz;
 +                m2k       = mhxk*mhxk + mhyk*mhyk + mhzk*mhzk;
 +                mhx[kx]   = mhxk;
 +                mhy[kx]   = mhyk;
 +                mhz[kx]   = mhzk;
 +                m2[kx]    = m2k;
 +                denom[kx] = bz*by*pme->bsp_mod[XX][kx];
 +                tmp1[kx]  = -factor*m2k;
 +                tmp2[kx]  = sqrt(factor*m2k);
 +            }
 +
 +            calc_exponentials_lj(kxstart, kxend, tmp1, tmp2, denom);
 +
 +            for (kx = kxstart; kx < kxend; kx++)
 +            {
 +                m2k   = factor*m2[kx];
 +                eterm = -((1.0 - 2.0*m2k)*tmp1[kx]
 +                          + 2.0*m2k*tmp2[kx]);
 +                vterm    = 3.0*(-tmp1[kx] + tmp2[kx]);
 +                tmp1[kx] = eterm*denom[kx];
 +                tmp2[kx] = vterm*denom[kx];
 +            }
 +
 +            if (!bLB)
 +            {
 +                t_complex *p0;
 +                real       struct2;
 +
 +                p0 = grid[0] + iy*local_size[ZZ]*local_size[XX] + iz*local_size[XX];
 +                for (kx = kxstart; kx < kxend; kx++, p0++)
 +                {
 +                    d1      = p0->re;
 +                    d2      = p0->im;
 +
 +                    eterm   = tmp1[kx];
 +                    vterm   = tmp2[kx];
 +                    p0->re  = d1*eterm;
 +                    p0->im  = d2*eterm;
 +
 +                    struct2 = 2.0*(d1*d1+d2*d2);
 +
 +                    tmp1[kx] = eterm*struct2;
 +                    tmp2[kx] = vterm*struct2;
 +                }
 +            }
 +            else
 +            {
 +                real *struct2 = denom;
 +                real  str2;
 +
 +                for (kx = kxstart; kx < kxend; kx++)
 +                {
 +                    struct2[kx] = 0.0;
 +                }
 +                /* Due to symmetry we only need to calculate 4 of the 7 terms */
 +                for (ig = 0; ig <= 3; ++ig)
 +                {
 +                    t_complex *p0, *p1;
 +                    real       scale;
 +
 +                    p0    = grid[ig] + iy*local_size[ZZ]*local_size[XX] + iz*local_size[XX];
 +                    p1    = grid[6-ig] + iy*local_size[ZZ]*local_size[XX] + iz*local_size[XX];
 +                    scale = 2.0*lb_scale_factor_symm[ig];
 +                    for (kx = kxstart; kx < kxend; ++kx, ++p0, ++p1)
 +                    {
 +                        struct2[kx] += scale*(p0->re*p1->re + p0->im*p1->im);
 +                    }
 +
 +                }
 +                for (ig = 0; ig <= 6; ++ig)
 +                {
 +                    t_complex *p0;
 +
 +                    p0 = grid[ig] + iy*local_size[ZZ]*local_size[XX] + iz*local_size[XX];
 +                    for (kx = kxstart; kx < kxend; kx++, p0++)
 +                    {
 +                        d1     = p0->re;
 +                        d2     = p0->im;
 +
 +                        eterm  = tmp1[kx];
 +                        p0->re = d1*eterm;
 +                        p0->im = d2*eterm;
 +                    }
 +                }
 +                for (kx = kxstart; kx < kxend; kx++)
 +                {
 +                    eterm    = tmp1[kx];
 +                    vterm    = tmp2[kx];
 +                    str2     = struct2[kx];
 +                    tmp1[kx] = eterm*str2;
 +                    tmp2[kx] = vterm*str2;
 +                }
 +            }
 +
 +            for (kx = kxstart; kx < kxend; kx++)
 +            {
 +                ets2     = corner_fac*tmp1[kx];
 +                vterm    = 2.0*factor*tmp2[kx];
 +                energy  += ets2;
 +                ets2vf   = corner_fac*vterm;
 +                virxx   += ets2vf*mhx[kx]*mhx[kx] - ets2;
 +                virxy   += ets2vf*mhx[kx]*mhy[kx];
 +                virxz   += ets2vf*mhx[kx]*mhz[kx];
 +                viryy   += ets2vf*mhy[kx]*mhy[kx] - ets2;
 +                viryz   += ets2vf*mhy[kx]*mhz[kx];
 +                virzz   += ets2vf*mhz[kx]*mhz[kx] - ets2;
 +            }
 +        }
 +        else
 +        {
 +            /* We don't need to calculate the energy and the virial.
 +             *  In this case the triclinic overhead is small.
 +             */
 +
 +            /* Two explicit loops to avoid a conditional inside the loop */
 +
 +            for (kx = kxstart; kx < maxkx; kx++)
 +            {
 +                mx = kx;
 +
 +                mhxk      = mx * rxx;
 +                mhyk      = mx * ryx + my * ryy;
 +                mhzk      = mx * rzx + my * rzy + mz * rzz;
 +                m2k       = mhxk*mhxk + mhyk*mhyk + mhzk*mhzk;
 +                m2[kx]    = m2k;
 +                denom[kx] = bz*by*pme->bsp_mod[XX][kx];
 +                tmp1[kx]  = -factor*m2k;
 +                tmp2[kx]  = sqrt(factor*m2k);
 +            }
 +
 +            for (kx = maxkx; kx < kxend; kx++)
 +            {
 +                mx = (kx - nx);
 +
 +                mhxk      = mx * rxx;
 +                mhyk      = mx * ryx + my * ryy;
 +                mhzk      = mx * rzx + my * rzy + mz * rzz;
 +                m2k       = mhxk*mhxk + mhyk*mhyk + mhzk*mhzk;
 +                m2[kx]    = m2k;
 +                denom[kx] = bz*by*pme->bsp_mod[XX][kx];
 +                tmp1[kx]  = -factor*m2k;
 +                tmp2[kx]  = sqrt(factor*m2k);
 +            }
 +
 +            calc_exponentials_lj(kxstart, kxend, tmp1, tmp2, denom);
 +
 +            for (kx = kxstart; kx < kxend; kx++)
 +            {
 +                m2k    = factor*m2[kx];
 +                eterm  = -((1.0 - 2.0*m2k)*tmp1[kx]
 +                           + 2.0*m2k*tmp2[kx]);
 +                tmp1[kx] = eterm*denom[kx];
 +            }
 +            gcount = (bLB ? 7 : 1);
 +            for (ig = 0; ig < gcount; ++ig)
 +            {
 +                t_complex *p0;
 +
 +                p0 = grid[ig] + iy*local_size[ZZ]*local_size[XX] + iz*local_size[XX];
 +                for (kx = kxstart; kx < kxend; kx++, p0++)
 +                {
 +                    d1      = p0->re;
 +                    d2      = p0->im;
 +
 +                    eterm   = tmp1[kx];
 +
 +                    p0->re  = d1*eterm;
 +                    p0->im  = d2*eterm;
 +                }
 +            }
 +        }
 +    }
 +    if (bEnerVir)
 +    {
 +        work->vir_lj[XX][XX] = 0.25*virxx;
 +        work->vir_lj[YY][YY] = 0.25*viryy;
 +        work->vir_lj[ZZ][ZZ] = 0.25*virzz;
 +        work->vir_lj[XX][YY] = work->vir_lj[YY][XX] = 0.25*virxy;
 +        work->vir_lj[XX][ZZ] = work->vir_lj[ZZ][XX] = 0.25*virxz;
 +        work->vir_lj[YY][ZZ] = work->vir_lj[ZZ][YY] = 0.25*viryz;
 +
 +        /* This energy should be corrected for a charged system */
 +        work->energy_lj = 0.5*energy;
 +    }
 +    /* Return the loop count */
 +    return local_ndata[YY]*local_ndata[XX];
 +}
 +
 +static void get_pme_ener_vir_q(const gmx_pme_t pme, int nthread,
 +                               real *mesh_energy, matrix vir)
 +{
 +    /* This function sums output over threads and should therefore
 +     * only be called after thread synchronization.
 +     */
 +    int thread;
 +
 +    *mesh_energy = pme->work[0].energy_q;
 +    copy_mat(pme->work[0].vir_q, vir);
 +
 +    for (thread = 1; thread < nthread; thread++)
 +    {
 +        *mesh_energy += pme->work[thread].energy_q;
 +        m_add(vir, pme->work[thread].vir_q, vir);
 +    }
 +}
 +
 +static void get_pme_ener_vir_lj(const gmx_pme_t pme, int nthread,
 +                                real *mesh_energy, matrix vir)
 +{
 +    /* This function sums output over threads and should therefore
 +     * only be called after thread synchronization.
 +     */
 +    int thread;
 +
 +    *mesh_energy = pme->work[0].energy_lj;
 +    copy_mat(pme->work[0].vir_lj, vir);
 +
 +    for (thread = 1; thread < nthread; thread++)
 +    {
 +        *mesh_energy += pme->work[thread].energy_lj;
 +        m_add(vir, pme->work[thread].vir_lj, vir);
 +    }
 +}
 +
 +
 +#define DO_FSPLINE(order)                      \
 +    for (ithx = 0; (ithx < order); ithx++)              \
 +    {                                              \
 +        index_x = (i0+ithx)*pny*pnz;               \
 +        tx      = thx[ithx];                       \
 +        dx      = dthx[ithx];                      \
 +                                               \
 +        for (ithy = 0; (ithy < order); ithy++)          \
 +        {                                          \
 +            index_xy = index_x+(j0+ithy)*pnz;      \
 +            ty       = thy[ithy];                  \
 +            dy       = dthy[ithy];                 \
 +            fxy1     = fz1 = 0;                    \
 +                                               \
 +            for (ithz = 0; (ithz < order); ithz++)      \
 +            {                                      \
 +                gval  = grid[index_xy+(k0+ithz)];  \
 +                fxy1 += thz[ithz]*gval;            \
 +                fz1  += dthz[ithz]*gval;           \
 +            }                                      \
 +            fx += dx*ty*fxy1;                      \
 +            fy += tx*dy*fxy1;                      \
 +            fz += tx*ty*fz1;                       \
 +        }                                          \
 +    }
 +
 +
 +static void gather_f_bsplines(gmx_pme_t pme, real *grid,
 +                              gmx_bool bClearF, pme_atomcomm_t *atc,
 +                              splinedata_t *spline,
 +                              real scale)
 +{
 +    /* sum forces for local particles */
 +    int     nn, n, ithx, ithy, ithz, i0, j0, k0;
 +    int     index_x, index_xy;
 +    int     nx, ny, nz, pnx, pny, pnz;
 +    int *   idxptr;
 +    real    tx, ty, dx, dy, coefficient;
 +    real    fx, fy, fz, gval;
 +    real    fxy1, fz1;
 +    real    *thx, *thy, *thz, *dthx, *dthy, *dthz;
 +    int     norder;
 +    real    rxx, ryx, ryy, rzx, rzy, rzz;
 +    int     order;
 +
 +    pme_spline_work_t *work;
 +
 +#if defined PME_SIMD4_SPREAD_GATHER && !defined PME_SIMD4_UNALIGNED
 +    real           thz_buffer[GMX_SIMD4_WIDTH*3],  *thz_aligned;
 +    real           dthz_buffer[GMX_SIMD4_WIDTH*3], *dthz_aligned;
 +
 +    thz_aligned  = gmx_simd4_align_r(thz_buffer);
 +    dthz_aligned = gmx_simd4_align_r(dthz_buffer);
 +#endif
 +
 +    work = pme->spline_work;
 +
 +    order = pme->pme_order;
 +    thx   = spline->theta[XX];
 +    thy   = spline->theta[YY];
 +    thz   = spline->theta[ZZ];
 +    dthx  = spline->dtheta[XX];
 +    dthy  = spline->dtheta[YY];
 +    dthz  = spline->dtheta[ZZ];
 +    nx    = pme->nkx;
 +    ny    = pme->nky;
 +    nz    = pme->nkz;
 +    pnx   = pme->pmegrid_nx;
 +    pny   = pme->pmegrid_ny;
 +    pnz   = pme->pmegrid_nz;
 +
 +    rxx   = pme->recipbox[XX][XX];
 +    ryx   = pme->recipbox[YY][XX];
 +    ryy   = pme->recipbox[YY][YY];
 +    rzx   = pme->recipbox[ZZ][XX];
 +    rzy   = pme->recipbox[ZZ][YY];
 +    rzz   = pme->recipbox[ZZ][ZZ];
 +
 +    for (nn = 0; nn < spline->n; nn++)
 +    {
 +        n           = spline->ind[nn];
 +        coefficient = scale*atc->coefficient[n];
 +
 +        if (bClearF)
 +        {
 +            atc->f[n][XX] = 0;
 +            atc->f[n][YY] = 0;
 +            atc->f[n][ZZ] = 0;
 +        }
 +        if (coefficient != 0)
 +        {
 +            fx     = 0;
 +            fy     = 0;
 +            fz     = 0;
 +            idxptr = atc->idx[n];
 +            norder = nn*order;
 +
 +            i0   = idxptr[XX];
 +            j0   = idxptr[YY];
 +            k0   = idxptr[ZZ];
 +
 +            /* Pointer arithmetic alert, next six statements */
 +            thx  = spline->theta[XX] + norder;
 +            thy  = spline->theta[YY] + norder;
 +            thz  = spline->theta[ZZ] + norder;
 +            dthx = spline->dtheta[XX] + norder;
 +            dthy = spline->dtheta[YY] + norder;
 +            dthz = spline->dtheta[ZZ] + norder;
 +
 +            switch (order)
 +            {
 +                case 4:
 +#ifdef PME_SIMD4_SPREAD_GATHER
 +#ifdef PME_SIMD4_UNALIGNED
 +#define PME_GATHER_F_SIMD4_ORDER4
 +#else
 +#define PME_GATHER_F_SIMD4_ALIGNED
 +#define PME_ORDER 4
 +#endif
 +#include "pme_simd4.h"
 +#else
 +                    DO_FSPLINE(4);
 +#endif
 +                    break;
 +                case 5:
 +#ifdef PME_SIMD4_SPREAD_GATHER
 +#define PME_GATHER_F_SIMD4_ALIGNED
 +#define PME_ORDER 5
 +#include "pme_simd4.h"
 +#else
 +                    DO_FSPLINE(5);
 +#endif
 +                    break;
 +                default:
 +                    DO_FSPLINE(order);
 +                    break;
 +            }
 +
 +            atc->f[n][XX] += -coefficient*( fx*nx*rxx );
 +            atc->f[n][YY] += -coefficient*( fx*nx*ryx + fy*ny*ryy );
 +            atc->f[n][ZZ] += -coefficient*( fx*nx*rzx + fy*ny*rzy + fz*nz*rzz );
 +        }
 +    }
 +    /* Since the energy and not forces are interpolated
 +     * the net force might not be exactly zero.
 +     * This can be solved by also interpolating F, but
 +     * that comes at a cost.
 +     * A better hack is to remove the net force every
 +     * step, but that must be done at a higher level
 +     * since this routine doesn't see all atoms if running
 +     * in parallel. Don't know how important it is?  EL 990726
 +     */
 +}
 +
 +
 +static real gather_energy_bsplines(gmx_pme_t pme, real *grid,
 +                                   pme_atomcomm_t *atc)
 +{
 +    splinedata_t *spline;
 +    int     n, ithx, ithy, ithz, i0, j0, k0;
 +    int     index_x, index_xy;
 +    int *   idxptr;
 +    real    energy, pot, tx, ty, coefficient, gval;
 +    real    *thx, *thy, *thz;
 +    int     norder;
 +    int     order;
 +
 +    spline = &atc->spline[0];
 +
 +    order = pme->pme_order;
 +
 +    energy = 0;
 +    for (n = 0; (n < atc->n); n++)
 +    {
 +        coefficient      = atc->coefficient[n];
 +
 +        if (coefficient != 0)
 +        {
 +            idxptr = atc->idx[n];
 +            norder = n*order;
 +
 +            i0   = idxptr[XX];
 +            j0   = idxptr[YY];
 +            k0   = idxptr[ZZ];
 +
 +            /* Pointer arithmetic alert, next three statements */
 +            thx  = spline->theta[XX] + norder;
 +            thy  = spline->theta[YY] + norder;
 +            thz  = spline->theta[ZZ] + norder;
 +
 +            pot = 0;
 +            for (ithx = 0; (ithx < order); ithx++)
 +            {
 +                index_x = (i0+ithx)*pme->pmegrid_ny*pme->pmegrid_nz;
 +                tx      = thx[ithx];
 +
 +                for (ithy = 0; (ithy < order); ithy++)
 +                {
 +                    index_xy = index_x+(j0+ithy)*pme->pmegrid_nz;
 +                    ty       = thy[ithy];
 +
 +                    for (ithz = 0; (ithz < order); ithz++)
 +                    {
 +                        gval  = grid[index_xy+(k0+ithz)];
 +                        pot  += tx*ty*thz[ithz]*gval;
 +                    }
 +
 +                }
 +            }
 +
 +            energy += pot*coefficient;
 +        }
 +    }
 +
 +    return energy;
 +}
 +
 +/* Macro to force loop unrolling by fixing order.
 + * This gives a significant performance gain.
 + */
 +#define CALC_SPLINE(order)                     \
 +    {                                              \
 +        int j, k, l;                                 \
 +        real dr, div;                               \
 +        real data[PME_ORDER_MAX];                  \
 +        real ddata[PME_ORDER_MAX];                 \
 +                                               \
 +        for (j = 0; (j < DIM); j++)                     \
 +        {                                          \
 +            dr  = xptr[j];                         \
 +                                               \
 +            /* dr is relative offset from lower cell limit */ \
 +            data[order-1] = 0;                     \
 +            data[1]       = dr;                          \
 +            data[0]       = 1 - dr;                      \
 +                                               \
 +            for (k = 3; (k < order); k++)               \
 +            {                                      \
 +                div       = 1.0/(k - 1.0);               \
 +                data[k-1] = div*dr*data[k-2];      \
 +                for (l = 1; (l < (k-1)); l++)           \
 +                {                                  \
 +                    data[k-l-1] = div*((dr+l)*data[k-l-2]+(k-l-dr)* \
 +                                       data[k-l-1]);                \
 +                }                                  \
 +                data[0] = div*(1-dr)*data[0];      \
 +            }                                      \
 +            /* differentiate */                    \
 +            ddata[0] = -data[0];                   \
 +            for (k = 1; (k < order); k++)               \
 +            {                                      \
 +                ddata[k] = data[k-1] - data[k];    \
 +            }                                      \
 +                                               \
 +            div           = 1.0/(order - 1);                 \
 +            data[order-1] = div*dr*data[order-2];  \
 +            for (l = 1; (l < (order-1)); l++)           \
 +            {                                      \
 +                data[order-l-1] = div*((dr+l)*data[order-l-2]+    \
 +                                       (order-l-dr)*data[order-l-1]); \
 +            }                                      \
 +            data[0] = div*(1 - dr)*data[0];        \
 +                                               \
 +            for (k = 0; k < order; k++)                 \
 +            {                                      \
 +                theta[j][i*order+k]  = data[k];    \
 +                dtheta[j][i*order+k] = ddata[k];   \
 +            }                                      \
 +        }                                          \
 +    }
 +
 +void make_bsplines(splinevec theta, splinevec dtheta, int order,
 +                   rvec fractx[], int nr, int ind[], real coefficient[],
 +                   gmx_bool bDoSplines)
 +{
 +    /* construct splines for local atoms */
 +    int  i, ii;
 +    real *xptr;
 +
 +    for (i = 0; i < nr; i++)
 +    {
 +        /* With free energy we do not use the coefficient check.
 +         * In most cases this will be more efficient than calling make_bsplines
 +         * twice, since usually more than half the particles have non-zero coefficients.
 +         */
 +        ii = ind[i];
 +        if (bDoSplines || coefficient[ii] != 0.0)
 +        {
 +            xptr = fractx[ii];
 +            switch (order)
 +            {
 +                case 4:  CALC_SPLINE(4);     break;
 +                case 5:  CALC_SPLINE(5);     break;
 +                default: CALC_SPLINE(order); break;
 +            }
 +        }
 +    }
 +}
 +
 +
 +void make_dft_mod(real *mod, real *data, int ndata)
 +{
 +    int i, j;
 +    real sc, ss, arg;
 +
 +    for (i = 0; i < ndata; i++)
 +    {
 +        sc = ss = 0;
 +        for (j = 0; j < ndata; j++)
 +        {
 +            arg = (2.0*M_PI*i*j)/ndata;
 +            sc += data[j]*cos(arg);
 +            ss += data[j]*sin(arg);
 +        }
 +        mod[i] = sc*sc+ss*ss;
 +    }
 +    for (i = 0; i < ndata; i++)
 +    {
 +        if (mod[i] < 1e-7)
 +        {
 +            mod[i] = (mod[i-1]+mod[i+1])*0.5;
 +        }
 +    }
 +}
 +
 +
 +static void make_bspline_moduli(splinevec bsp_mod,
 +                                int nx, int ny, int nz, int order)
 +{
 +    int nmax = max(nx, max(ny, nz));
 +    real *data, *ddata, *bsp_data;
 +    int i, k, l;
 +    real div;
 +
 +    snew(data, order);
 +    snew(ddata, order);
 +    snew(bsp_data, nmax);
 +
 +    data[order-1] = 0;
 +    data[1]       = 0;
 +    data[0]       = 1;
 +
 +    for (k = 3; k < order; k++)
 +    {
 +        div       = 1.0/(k-1.0);
 +        data[k-1] = 0;
 +        for (l = 1; l < (k-1); l++)
 +        {
 +            data[k-l-1] = div*(l*data[k-l-2]+(k-l)*data[k-l-1]);
 +        }
 +        data[0] = div*data[0];
 +    }
 +    /* differentiate */
 +    ddata[0] = -data[0];
 +    for (k = 1; k < order; k++)
 +    {
 +        ddata[k] = data[k-1]-data[k];
 +    }
 +    div           = 1.0/(order-1);
 +    data[order-1] = 0;
 +    for (l = 1; l < (order-1); l++)
 +    {
 +        data[order-l-1] = div*(l*data[order-l-2]+(order-l)*data[order-l-1]);
 +    }
 +    data[0] = div*data[0];
 +
 +    for (i = 0; i < nmax; i++)
 +    {
 +        bsp_data[i] = 0;
 +    }
 +    for (i = 1; i <= order; i++)
 +    {
 +        bsp_data[i] = data[i-1];
 +    }
 +
 +    make_dft_mod(bsp_mod[XX], bsp_data, nx);
 +    make_dft_mod(bsp_mod[YY], bsp_data, ny);
 +    make_dft_mod(bsp_mod[ZZ], bsp_data, nz);
 +
 +    sfree(data);
 +    sfree(ddata);
 +    sfree(bsp_data);
 +}
 +
 +
 +/* Return the P3M optimal influence function */
 +static double do_p3m_influence(double z, int order)
 +{
 +    double z2, z4;
 +
 +    z2 = z*z;
 +    z4 = z2*z2;
 +
 +    /* The formula and most constants can be found in:
 +     * Ballenegger et al., JCTC 8, 936 (2012)
 +     */
 +    switch (order)
 +    {
 +        case 2:
 +            return 1.0 - 2.0*z2/3.0;
 +            break;
 +        case 3:
 +            return 1.0 - z2 + 2.0*z4/15.0;
 +            break;
 +        case 4:
 +            return 1.0 - 4.0*z2/3.0 + 2.0*z4/5.0 + 4.0*z2*z4/315.0;
 +            break;
 +        case 5:
 +            return 1.0 - 5.0*z2/3.0 + 7.0*z4/9.0 - 17.0*z2*z4/189.0 + 2.0*z4*z4/2835.0;
 +            break;
 +        case 6:
 +            return 1.0 - 2.0*z2 + 19.0*z4/15.0 - 256.0*z2*z4/945.0 + 62.0*z4*z4/4725.0 + 4.0*z2*z4*z4/155925.0;
 +            break;
 +        case 7:
 +            return 1.0 - 7.0*z2/3.0 + 28.0*z4/15.0 - 16.0*z2*z4/27.0 + 26.0*z4*z4/405.0 - 2.0*z2*z4*z4/1485.0 + 4.0*z4*z4*z4/6081075.0;
 +        case 8:
 +            return 1.0 - 8.0*z2/3.0 + 116.0*z4/45.0 - 344.0*z2*z4/315.0 + 914.0*z4*z4/4725.0 - 248.0*z4*z4*z2/22275.0 + 21844.0*z4*z4*z4/212837625.0 - 8.0*z4*z4*z4*z2/638512875.0;
 +            break;
 +    }
 +
 +    return 0.0;
 +}
 +
 +/* Calculate the P3M B-spline moduli for one dimension */
 +static void make_p3m_bspline_moduli_dim(real *bsp_mod, int n, int order)
 +{
 +    double zarg, zai, sinzai, infl;
 +    int    maxk, i;
 +
 +    if (order > 8)
 +    {
 +        gmx_fatal(FARGS, "The current P3M code only supports orders up to 8");
 +    }
 +
 +    zarg = M_PI/n;
 +
 +    maxk = (n + 1)/2;
 +
 +    for (i = -maxk; i < 0; i++)
 +    {
 +        zai          = zarg*i;
 +        sinzai       = sin(zai);
 +        infl         = do_p3m_influence(sinzai, order);
 +        bsp_mod[n+i] = infl*infl*pow(sinzai/zai, -2.0*order);
 +    }
 +    bsp_mod[0] = 1.0;
 +    for (i = 1; i < maxk; i++)
 +    {
 +        zai        = zarg*i;
 +        sinzai     = sin(zai);
 +        infl       = do_p3m_influence(sinzai, order);
 +        bsp_mod[i] = infl*infl*pow(sinzai/zai, -2.0*order);
 +    }
 +}
 +
 +/* Calculate the P3M B-spline moduli */
 +static void make_p3m_bspline_moduli(splinevec bsp_mod,
 +                                    int nx, int ny, int nz, int order)
 +{
 +    make_p3m_bspline_moduli_dim(bsp_mod[XX], nx, order);
 +    make_p3m_bspline_moduli_dim(bsp_mod[YY], ny, order);
 +    make_p3m_bspline_moduli_dim(bsp_mod[ZZ], nz, order);
 +}
 +
 +
 +static void setup_coordinate_communication(pme_atomcomm_t *atc)
 +{
 +    int nslab, n, i;
 +    int fw, bw;
 +
 +    nslab = atc->nslab;
 +
 +    n = 0;
 +    for (i = 1; i <= nslab/2; i++)
 +    {
 +        fw = (atc->nodeid + i) % nslab;
 +        bw = (atc->nodeid - i + nslab) % nslab;
 +        if (n < nslab - 1)
 +        {
 +            atc->node_dest[n] = fw;
 +            atc->node_src[n]  = bw;
 +            n++;
 +        }
 +        if (n < nslab - 1)
 +        {
 +            atc->node_dest[n] = bw;
 +            atc->node_src[n]  = fw;
 +            n++;
 +        }
 +    }
 +}
 +
 +int gmx_pme_destroy(FILE *log, gmx_pme_t *pmedata)
 +{
 +    int thread, i;
 +
 +    if (NULL != log)
 +    {
 +        fprintf(log, "Destroying PME data structures.\n");
 +    }
 +
 +    sfree((*pmedata)->nnx);
 +    sfree((*pmedata)->nny);
 +    sfree((*pmedata)->nnz);
 +
 +    for (i = 0; i < (*pmedata)->ngrids; ++i)
 +    {
 +        pmegrids_destroy(&(*pmedata)->pmegrid[i]);
 +        sfree((*pmedata)->fftgrid[i]);
 +        sfree((*pmedata)->cfftgrid[i]);
 +        gmx_parallel_3dfft_destroy((*pmedata)->pfft_setup[i]);
 +    }
 +
 +    sfree((*pmedata)->lb_buf1);
 +    sfree((*pmedata)->lb_buf2);
 +
 +    for (thread = 0; thread < (*pmedata)->nthread; thread++)
 +    {
 +        free_work(&(*pmedata)->work[thread]);
 +    }
 +    sfree((*pmedata)->work);
 +
 +    sfree(*pmedata);
 +    *pmedata = NULL;
 +
 +    return 0;
 +}
 +
 +static int mult_up(int n, int f)
 +{
 +    return ((n + f - 1)/f)*f;
 +}
 +
 +
 +static double pme_load_imbalance(gmx_pme_t pme)
 +{
 +    int    nma, nmi;
 +    double n1, n2, n3;
 +
 +    nma = pme->nnodes_major;
 +    nmi = pme->nnodes_minor;
 +
 +    n1 = mult_up(pme->nkx, nma)*mult_up(pme->nky, nmi)*pme->nkz;
 +    n2 = mult_up(pme->nkx, nma)*mult_up(pme->nkz, nmi)*pme->nky;
 +    n3 = mult_up(pme->nky, nma)*mult_up(pme->nkz, nmi)*pme->nkx;
 +
 +    /* pme_solve is roughly double the cost of an fft */
 +
 +    return (n1 + n2 + 3*n3)/(double)(6*pme->nkx*pme->nky*pme->nkz);
 +}
 +
 +static void init_atomcomm(gmx_pme_t pme, pme_atomcomm_t *atc,
 +                          int dimind, gmx_bool bSpread)
 +{
 +    int nk, k, s, thread;
 +
 +    atc->dimind    = dimind;
 +    atc->nslab     = 1;
 +    atc->nodeid    = 0;
 +    atc->pd_nalloc = 0;
 +#ifdef GMX_MPI
 +    if (pme->nnodes > 1)
 +    {
 +        atc->mpi_comm = pme->mpi_comm_d[dimind];
 +        MPI_Comm_size(atc->mpi_comm, &atc->nslab);
 +        MPI_Comm_rank(atc->mpi_comm, &atc->nodeid);
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "For PME atom communication in dimind %d: nslab %d rank %d\n", atc->dimind, atc->nslab, atc->nodeid);
 +    }
 +#endif
 +
 +    atc->bSpread   = bSpread;
 +    atc->pme_order = pme->pme_order;
 +
 +    if (atc->nslab > 1)
 +    {
 +        snew(atc->node_dest, atc->nslab);
 +        snew(atc->node_src, atc->nslab);
 +        setup_coordinate_communication(atc);
 +
 +        snew(atc->count_thread, pme->nthread);
 +        for (thread = 0; thread < pme->nthread; thread++)
 +        {
 +            snew(atc->count_thread[thread], atc->nslab);
 +        }
 +        atc->count = atc->count_thread[0];
 +        snew(atc->rcount, atc->nslab);
 +        snew(atc->buf_index, atc->nslab);
 +    }
 +
 +    atc->nthread = pme->nthread;
 +    if (atc->nthread > 1)
 +    {
 +        snew(atc->thread_plist, atc->nthread);
 +    }
 +    snew(atc->spline, atc->nthread);
 +    for (thread = 0; thread < atc->nthread; thread++)
 +    {
 +        if (atc->nthread > 1)
 +        {
 +            snew(atc->thread_plist[thread].n, atc->nthread+2*GMX_CACHE_SEP);
 +            atc->thread_plist[thread].n += GMX_CACHE_SEP;
 +        }
 +        snew(atc->spline[thread].thread_one, pme->nthread);
 +        atc->spline[thread].thread_one[thread] = 1;
 +    }
 +}
 +
 +static void
 +init_overlap_comm(pme_overlap_t *  ol,
 +                  int              norder,
 +#ifdef GMX_MPI
 +                  MPI_Comm         comm,
 +#endif
 +                  int              nnodes,
 +                  int              nodeid,
 +                  int              ndata,
 +                  int              commplainsize)
 +{
 +    int lbnd, rbnd, maxlr, b, i;
 +    int exten;
 +    int nn, nk;
 +    pme_grid_comm_t *pgc;
 +    gmx_bool bCont;
 +    int fft_start, fft_end, send_index1, recv_index1;
 +#ifdef GMX_MPI
 +    MPI_Status stat;
 +
 +    ol->mpi_comm = comm;
 +#endif
 +
 +    ol->nnodes = nnodes;
 +    ol->nodeid = nodeid;
 +
 +    /* Linear translation of the PME grid won't affect reciprocal space
 +     * calculations, so to optimize we only interpolate "upwards",
 +     * which also means we only have to consider overlap in one direction.
 +     * I.e., particles on this node might also be spread to grid indices
 +     * that belong to higher nodes (modulo nnodes)
 +     */
 +
 +    snew(ol->s2g0, ol->nnodes+1);
 +    snew(ol->s2g1, ol->nnodes);
 +    if (debug)
 +    {
 +        fprintf(debug, "PME slab boundaries:");
 +    }
 +    for (i = 0; i < nnodes; i++)
 +    {
 +        /* s2g0 the local interpolation grid start.
 +         * s2g1 the local interpolation grid end.
 +         * Because grid overlap communication only goes forward,
 +         * the grid the slabs for fft's should be rounded down.
 +         */
 +        ol->s2g0[i] = ( i   *ndata + 0       )/nnodes;
 +        ol->s2g1[i] = ((i+1)*ndata + nnodes-1)/nnodes + norder - 1;
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "  %3d %3d", ol->s2g0[i], ol->s2g1[i]);
 +        }
 +    }
 +    ol->s2g0[nnodes] = ndata;
 +    if (debug)
 +    {
 +        fprintf(debug, "\n");
 +    }
 +
 +    /* Determine with how many nodes we need to communicate the grid overlap */
 +    b = 0;
 +    do
 +    {
 +        b++;
 +        bCont = FALSE;
 +        for (i = 0; i < nnodes; i++)
 +        {
 +            if ((i+b <  nnodes && ol->s2g1[i] > ol->s2g0[i+b]) ||
 +                (i+b >= nnodes && ol->s2g1[i] > ol->s2g0[i+b-nnodes] + ndata))
 +            {
 +                bCont = TRUE;
 +            }
 +        }
 +    }
 +    while (bCont && b < nnodes);
 +    ol->noverlap_nodes = b - 1;
 +
 +    snew(ol->send_id, ol->noverlap_nodes);
 +    snew(ol->recv_id, ol->noverlap_nodes);
 +    for (b = 0; b < ol->noverlap_nodes; b++)
 +    {
 +        ol->send_id[b] = (ol->nodeid + (b + 1)) % ol->nnodes;
 +        ol->recv_id[b] = (ol->nodeid - (b + 1) + ol->nnodes) % ol->nnodes;
 +    }
 +    snew(ol->comm_data, ol->noverlap_nodes);
 +
 +    ol->send_size = 0;
 +    for (b = 0; b < ol->noverlap_nodes; b++)
 +    {
 +        pgc = &ol->comm_data[b];
 +        /* Send */
 +        fft_start        = ol->s2g0[ol->send_id[b]];
 +        fft_end          = ol->s2g0[ol->send_id[b]+1];
 +        if (ol->send_id[b] < nodeid)
 +        {
 +            fft_start += ndata;
 +            fft_end   += ndata;
 +        }
 +        send_index1       = ol->s2g1[nodeid];
 +        send_index1       = min(send_index1, fft_end);
 +        pgc->send_index0  = fft_start;
 +        pgc->send_nindex  = max(0, send_index1 - pgc->send_index0);
 +        ol->send_size    += pgc->send_nindex;
 +
 +        /* We always start receiving to the first index of our slab */
 +        fft_start        = ol->s2g0[ol->nodeid];
 +        fft_end          = ol->s2g0[ol->nodeid+1];
 +        recv_index1      = ol->s2g1[ol->recv_id[b]];
 +        if (ol->recv_id[b] > nodeid)
 +        {
 +            recv_index1 -= ndata;
 +        }
 +        recv_index1      = min(recv_index1, fft_end);
 +        pgc->recv_index0 = fft_start;
 +        pgc->recv_nindex = max(0, recv_index1 - pgc->recv_index0);
 +    }
 +
 +#ifdef GMX_MPI
 +    /* Communicate the buffer sizes to receive */
 +    for (b = 0; b < ol->noverlap_nodes; b++)
 +    {
 +        MPI_Sendrecv(&ol->send_size, 1, MPI_INT, ol->send_id[b], b,
 +                     &ol->comm_data[b].recv_size, 1, MPI_INT, ol->recv_id[b], b,
 +                     ol->mpi_comm, &stat);
 +    }
 +#endif
 +
 +    /* For non-divisible grid we need pme_order iso pme_order-1 */
 +    snew(ol->sendbuf, norder*commplainsize);
 +    snew(ol->recvbuf, norder*commplainsize);
 +}
 +
 +static void
 +make_gridindex5_to_localindex(int n, int local_start, int local_range,
 +                              int **global_to_local,
 +                              real **fraction_shift)
 +{
 +    int i;
 +    int * gtl;
 +    real * fsh;
 +
 +    snew(gtl, 5*n);
 +    snew(fsh, 5*n);
 +    for (i = 0; (i < 5*n); i++)
 +    {
 +        /* Determine the global to local grid index */
 +        gtl[i] = (i - local_start + n) % n;
 +        /* For coordinates that fall within the local grid the fraction
 +         * is correct, we don't need to shift it.
 +         */
 +        fsh[i] = 0;
 +        if (local_range < n)
 +        {
 +            /* Due to rounding issues i could be 1 beyond the lower or
 +             * upper boundary of the local grid. Correct the index for this.
 +             * If we shift the index, we need to shift the fraction by
 +             * the same amount in the other direction to not affect
 +             * the weights.
 +             * Note that due to this shifting the weights at the end of
 +             * the spline might change, but that will only involve values
 +             * between zero and values close to the precision of a real,
 +             * which is anyhow the accuracy of the whole mesh calculation.
 +             */
 +            /* With local_range=0 we should not change i=local_start */
 +            if (i % n != local_start)
 +            {
 +                if (gtl[i] == n-1)
 +                {
 +                    gtl[i] = 0;
 +                    fsh[i] = -1;
 +                }
 +                else if (gtl[i] == local_range)
 +                {
 +                    gtl[i] = local_range - 1;
 +                    fsh[i] = 1;
 +                }
 +            }
 +        }
 +    }
 +
 +    *global_to_local = gtl;
 +    *fraction_shift  = fsh;
 +}
 +
 +static pme_spline_work_t *make_pme_spline_work(int gmx_unused order)
 +{
 +    pme_spline_work_t *work;
 +
 +#ifdef PME_SIMD4_SPREAD_GATHER
 +    real             tmp[GMX_SIMD4_WIDTH*3], *tmp_aligned;
 +    gmx_simd4_real_t zero_S;
 +    gmx_simd4_real_t real_mask_S0, real_mask_S1;
 +    int              of, i;
 +
 +    snew_aligned(work, 1, SIMD4_ALIGNMENT);
 +
 +    tmp_aligned = gmx_simd4_align_r(tmp);
 +
 +    zero_S = gmx_simd4_setzero_r();
 +
 +    /* Generate bit masks to mask out the unused grid entries,
 +     * as we only operate on order of the 8 grid entries that are
 +     * load into 2 SIMD registers.
 +     */
 +    for (of = 0; of < 2*GMX_SIMD4_WIDTH-(order-1); of++)
 +    {
 +        for (i = 0; i < 2*GMX_SIMD4_WIDTH; i++)
 +        {
 +            tmp_aligned[i] = (i >= of && i < of+order ? -1.0 : 1.0);
 +        }
 +        real_mask_S0      = gmx_simd4_load_r(tmp_aligned);
 +        real_mask_S1      = gmx_simd4_load_r(tmp_aligned+GMX_SIMD4_WIDTH);
 +        work->mask_S0[of] = gmx_simd4_cmplt_r(real_mask_S0, zero_S);
 +        work->mask_S1[of] = gmx_simd4_cmplt_r(real_mask_S1, zero_S);
 +    }
 +#else
 +    work = NULL;
 +#endif
 +
 +    return work;
 +}
 +
 +void gmx_pme_check_restrictions(int pme_order,
 +                                int nkx, int nky, int nkz,
 +                                int nnodes_major,
 +                                int nnodes_minor,
 +                                gmx_bool bUseThreads,
 +                                gmx_bool bFatal,
 +                                gmx_bool *bValidSettings)
 +{
 +    if (pme_order > PME_ORDER_MAX)
 +    {
 +        if (!bFatal)
 +        {
 +            *bValidSettings = FALSE;
 +            return;
 +        }
 +        gmx_fatal(FARGS, "pme_order (%d) is larger than the maximum allowed value (%d). Modify and recompile the code if you really need such a high order.",
 +                  pme_order, PME_ORDER_MAX);
 +    }
 +
 +    if (nkx <= pme_order*(nnodes_major > 1 ? 2 : 1) ||
 +        nky <= pme_order*(nnodes_minor > 1 ? 2 : 1) ||
 +        nkz <= pme_order)
 +    {
 +        if (!bFatal)
 +        {
 +            *bValidSettings = FALSE;
 +            return;
 +        }
 +        gmx_fatal(FARGS, "The PME grid sizes need to be larger than pme_order (%d) and for dimensions with domain decomposition larger than 2*pme_order",
 +                  pme_order);
 +    }
 +
 +    /* Check for a limitation of the (current) sum_fftgrid_dd code.
 +     * We only allow multiple communication pulses in dim 1, not in dim 0.
 +     */
 +    if (bUseThreads && (nkx < nnodes_major*pme_order &&
 +                        nkx != nnodes_major*(pme_order - 1)))
 +    {
 +        if (!bFatal)
 +        {
 +            *bValidSettings = FALSE;
 +            return;
 +        }
 +        gmx_fatal(FARGS, "The number of PME grid lines per node along x is %g. But when using OpenMP threads, the number of grid lines per node along x should be >= pme_order (%d) or = pmeorder-1. To resolve this issue, use less nodes along x (and possibly more along y and/or z) by specifying -dd manually.",
 +                  nkx/(double)nnodes_major, pme_order);
 +    }
 +
 +    if (bValidSettings != NULL)
 +    {
 +        *bValidSettings = TRUE;
 +    }
 +
 +    return;
 +}
 +
 +int gmx_pme_init(gmx_pme_t *         pmedata,
 +                 t_commrec *         cr,
 +                 int                 nnodes_major,
 +                 int                 nnodes_minor,
 +                 t_inputrec *        ir,
 +                 int                 homenr,
 +                 gmx_bool            bFreeEnergy_q,
 +                 gmx_bool            bFreeEnergy_lj,
 +                 gmx_bool            bReproducible,
 +                 int                 nthread)
 +{
 +    gmx_pme_t pme = NULL;
 +
 +    int  use_threads, sum_use_threads, i;
 +    ivec ndata;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Creating PME data structures.\n");
 +    }
 +    snew(pme, 1);
 +
 +    pme->sum_qgrid_tmp       = NULL;
 +    pme->sum_qgrid_dd_tmp    = NULL;
 +    pme->buf_nalloc          = 0;
 +
 +    pme->nnodes              = 1;
 +    pme->bPPnode             = TRUE;
 +
 +    pme->nnodes_major        = nnodes_major;
 +    pme->nnodes_minor        = nnodes_minor;
 +
 +#ifdef GMX_MPI
 +    if (nnodes_major*nnodes_minor > 1)
 +    {
 +        pme->mpi_comm = cr->mpi_comm_mygroup;
 +
 +        MPI_Comm_rank(pme->mpi_comm, &pme->nodeid);
 +        MPI_Comm_size(pme->mpi_comm, &pme->nnodes);
 +        if (pme->nnodes != nnodes_major*nnodes_minor)
 +        {
 +            gmx_incons("PME node count mismatch");
 +        }
 +    }
 +    else
 +    {
 +        pme->mpi_comm = MPI_COMM_NULL;
 +    }
 +#endif
 +
 +    if (pme->nnodes == 1)
 +    {
 +#ifdef GMX_MPI
 +        pme->mpi_comm_d[0] = MPI_COMM_NULL;
 +        pme->mpi_comm_d[1] = MPI_COMM_NULL;
 +#endif
 +        pme->ndecompdim   = 0;
 +        pme->nodeid_major = 0;
 +        pme->nodeid_minor = 0;
 +#ifdef GMX_MPI
 +        pme->mpi_comm_d[0] = pme->mpi_comm_d[1] = MPI_COMM_NULL;
 +#endif
 +    }
 +    else
 +    {
 +        if (nnodes_minor == 1)
 +        {
 +#ifdef GMX_MPI
 +            pme->mpi_comm_d[0] = pme->mpi_comm;
 +            pme->mpi_comm_d[1] = MPI_COMM_NULL;
 +#endif
 +            pme->ndecompdim   = 1;
 +            pme->nodeid_major = pme->nodeid;
 +            pme->nodeid_minor = 0;
 +
 +        }
 +        else if (nnodes_major == 1)
 +        {
 +#ifdef GMX_MPI
 +            pme->mpi_comm_d[0] = MPI_COMM_NULL;
 +            pme->mpi_comm_d[1] = pme->mpi_comm;
 +#endif
 +            pme->ndecompdim   = 1;
 +            pme->nodeid_major = 0;
 +            pme->nodeid_minor = pme->nodeid;
 +        }
 +        else
 +        {
 +            if (pme->nnodes % nnodes_major != 0)
 +            {
 +                gmx_incons("For 2D PME decomposition, #PME nodes must be divisible by the number of nodes in the major dimension");
 +            }
 +            pme->ndecompdim = 2;
 +
 +#ifdef GMX_MPI
 +            MPI_Comm_split(pme->mpi_comm, pme->nodeid % nnodes_minor,
 +                           pme->nodeid, &pme->mpi_comm_d[0]);  /* My communicator along major dimension */
 +            MPI_Comm_split(pme->mpi_comm, pme->nodeid/nnodes_minor,
 +                           pme->nodeid, &pme->mpi_comm_d[1]);  /* My communicator along minor dimension */
 +
 +            MPI_Comm_rank(pme->mpi_comm_d[0], &pme->nodeid_major);
 +            MPI_Comm_size(pme->mpi_comm_d[0], &pme->nnodes_major);
 +            MPI_Comm_rank(pme->mpi_comm_d[1], &pme->nodeid_minor);
 +            MPI_Comm_size(pme->mpi_comm_d[1], &pme->nnodes_minor);
 +#endif
 +        }
 +        pme->bPPnode = (cr->duty & DUTY_PP);
 +    }
 +
 +    pme->nthread = nthread;
 +
 +    /* Check if any of the PME MPI ranks uses threads */
 +    use_threads = (pme->nthread > 1 ? 1 : 0);
 +#ifdef GMX_MPI
 +    if (pme->nnodes > 1)
 +    {
 +        MPI_Allreduce(&use_threads, &sum_use_threads, 1, MPI_INT,
 +                      MPI_SUM, pme->mpi_comm);
 +    }
 +    else
 +#endif
 +    {
 +        sum_use_threads = use_threads;
 +    }
 +    pme->bUseThreads = (sum_use_threads > 0);
 +
 +    if (ir->ePBC == epbcSCREW)
 +    {
 +        gmx_fatal(FARGS, "pme does not (yet) work with pbc = screw");
 +    }
 +
 +    pme->bFEP_q      = ((ir->efep != efepNO) && bFreeEnergy_q);
 +    pme->bFEP_lj     = ((ir->efep != efepNO) && bFreeEnergy_lj);
 +    pme->bFEP        = (pme->bFEP_q || pme->bFEP_lj);
 +    pme->nkx         = ir->nkx;
 +    pme->nky         = ir->nky;
 +    pme->nkz         = ir->nkz;
 +    pme->bP3M        = (ir->coulombtype == eelP3M_AD || getenv("GMX_PME_P3M") != NULL);
 +    pme->pme_order   = ir->pme_order;
 +
 +    /* Always constant electrostatics coefficients */
 +    pme->epsilon_r   = ir->epsilon_r;
 +
 +    /* Always constant LJ coefficients */
 +    pme->ljpme_combination_rule = ir->ljpme_combination_rule;
 +
 +    /* If we violate restrictions, generate a fatal error here */
 +    gmx_pme_check_restrictions(pme->pme_order,
 +                               pme->nkx, pme->nky, pme->nkz,
 +                               pme->nnodes_major,
 +                               pme->nnodes_minor,
 +                               pme->bUseThreads,
 +                               TRUE,
 +                               NULL);
 +
 +    if (pme->nnodes > 1)
 +    {
 +        double imbal;
 +
 +#ifdef GMX_MPI
 +        MPI_Type_contiguous(DIM, mpi_type, &(pme->rvec_mpi));
 +        MPI_Type_commit(&(pme->rvec_mpi));
 +#endif
 +
 +        /* Note that the coefficient spreading and force gathering, which usually
 +         * takes about the same amount of time as FFT+solve_pme,
 +         * is always fully load balanced
 +         * (unless the coefficient distribution is inhomogeneous).
 +         */
 +
 +        imbal = pme_load_imbalance(pme);
 +        if (imbal >= 1.2 && pme->nodeid_major == 0 && pme->nodeid_minor == 0)
 +        {
 +            fprintf(stderr,
 +                    "\n"
 +                    "NOTE: The load imbalance in PME FFT and solve is %d%%.\n"
 +                    "      For optimal PME load balancing\n"
 +                    "      PME grid_x (%d) and grid_y (%d) should be divisible by #PME_nodes_x (%d)\n"
 +                    "      and PME grid_y (%d) and grid_z (%d) should be divisible by #PME_nodes_y (%d)\n"
 +                    "\n",
 +                    (int)((imbal-1)*100 + 0.5),
 +                    pme->nkx, pme->nky, pme->nnodes_major,
 +                    pme->nky, pme->nkz, pme->nnodes_minor);
 +        }
 +    }
 +
 +    /* For non-divisible grid we need pme_order iso pme_order-1 */
 +    /* In sum_qgrid_dd x overlap is copied in place: take padding into account.
 +     * y is always copied through a buffer: we don't need padding in z,
 +     * but we do need the overlap in x because of the communication order.
 +     */
 +    init_overlap_comm(&pme->overlap[0], pme->pme_order,
 +#ifdef GMX_MPI
 +                      pme->mpi_comm_d[0],
 +#endif
 +                      pme->nnodes_major, pme->nodeid_major,
 +                      pme->nkx,
 +                      (div_round_up(pme->nky, pme->nnodes_minor)+pme->pme_order)*(pme->nkz+pme->pme_order-1));
 +
 +    /* Along overlap dim 1 we can send in multiple pulses in sum_fftgrid_dd.
 +     * We do this with an offset buffer of equal size, so we need to allocate
 +     * extra for the offset. That's what the (+1)*pme->nkz is for.
 +     */
 +    init_overlap_comm(&pme->overlap[1], pme->pme_order,
 +#ifdef GMX_MPI
 +                      pme->mpi_comm_d[1],
 +#endif
 +                      pme->nnodes_minor, pme->nodeid_minor,
 +                      pme->nky,
 +                      (div_round_up(pme->nkx, pme->nnodes_major)+pme->pme_order+1)*pme->nkz);
 +
 +    /* Double-check for a limitation of the (current) sum_fftgrid_dd code.
 +     * Note that gmx_pme_check_restrictions checked for this already.
 +     */
 +    if (pme->bUseThreads && pme->overlap[0].noverlap_nodes > 1)
 +    {
 +        gmx_incons("More than one communication pulse required for grid overlap communication along the major dimension while using threads");
 +    }
 +
 +    snew(pme->bsp_mod[XX], pme->nkx);
 +    snew(pme->bsp_mod[YY], pme->nky);
 +    snew(pme->bsp_mod[ZZ], pme->nkz);
 +
 +    /* The required size of the interpolation grid, including overlap.
 +     * The allocated size (pmegrid_n?) might be slightly larger.
 +     */
 +    pme->pmegrid_nx = pme->overlap[0].s2g1[pme->nodeid_major] -
 +        pme->overlap[0].s2g0[pme->nodeid_major];
 +    pme->pmegrid_ny = pme->overlap[1].s2g1[pme->nodeid_minor] -
 +        pme->overlap[1].s2g0[pme->nodeid_minor];
 +    pme->pmegrid_nz_base = pme->nkz;
 +    pme->pmegrid_nz      = pme->pmegrid_nz_base + pme->pme_order - 1;
 +    set_grid_alignment(&pme->pmegrid_nz, pme->pme_order);
 +
 +    pme->pmegrid_start_ix = pme->overlap[0].s2g0[pme->nodeid_major];
 +    pme->pmegrid_start_iy = pme->overlap[1].s2g0[pme->nodeid_minor];
 +    pme->pmegrid_start_iz = 0;
 +
 +    make_gridindex5_to_localindex(pme->nkx,
 +                                  pme->pmegrid_start_ix,
 +                                  pme->pmegrid_nx - (pme->pme_order-1),
 +                                  &pme->nnx, &pme->fshx);
 +    make_gridindex5_to_localindex(pme->nky,
 +                                  pme->pmegrid_start_iy,
 +                                  pme->pmegrid_ny - (pme->pme_order-1),
 +                                  &pme->nny, &pme->fshy);
 +    make_gridindex5_to_localindex(pme->nkz,
 +                                  pme->pmegrid_start_iz,
 +                                  pme->pmegrid_nz_base,
 +                                  &pme->nnz, &pme->fshz);
 +
 +    pme->spline_work = make_pme_spline_work(pme->pme_order);
 +
 +    ndata[0]    = pme->nkx;
 +    ndata[1]    = pme->nky;
 +    ndata[2]    = pme->nkz;
 +    /* It doesn't matter if we allocate too many grids here,
 +     * we only allocate and use the ones we need.
 +     */
 +    if (EVDW_PME(ir->vdwtype))
 +    {
 +        pme->ngrids = ((ir->ljpme_combination_rule == eljpmeLB) ? DO_Q_AND_LJ_LB : DO_Q_AND_LJ);
 +    }
 +    else
 +    {
 +        pme->ngrids = DO_Q;
 +    }
 +    snew(pme->fftgrid, pme->ngrids);
 +    snew(pme->cfftgrid, pme->ngrids);
 +    snew(pme->pfft_setup, pme->ngrids);
 +
 +    for (i = 0; i < pme->ngrids; ++i)
 +    {
 +        if ((i <  DO_Q && EEL_PME(ir->coulombtype) && (i == 0 ||
 +                                                       bFreeEnergy_q)) ||
 +            (i >= DO_Q && EVDW_PME(ir->vdwtype) && (i == 2 ||
 +                                                    bFreeEnergy_lj ||
 +                                                    ir->ljpme_combination_rule == eljpmeLB)))
 +        {
 +            pmegrids_init(&pme->pmegrid[i],
 +                          pme->pmegrid_nx, pme->pmegrid_ny, pme->pmegrid_nz,
 +                          pme->pmegrid_nz_base,
 +                          pme->pme_order,
 +                          pme->bUseThreads,
 +                          pme->nthread,
 +                          pme->overlap[0].s2g1[pme->nodeid_major]-pme->overlap[0].s2g0[pme->nodeid_major+1],
 +                          pme->overlap[1].s2g1[pme->nodeid_minor]-pme->overlap[1].s2g0[pme->nodeid_minor+1]);
 +            /* This routine will allocate the grid data to fit the FFTs */
 +            gmx_parallel_3dfft_init(&pme->pfft_setup[i], ndata,
 +                                    &pme->fftgrid[i], &pme->cfftgrid[i],
 +                                    pme->mpi_comm_d,
 +                                    bReproducible, pme->nthread);
 +
 +        }
 +    }
 +
 +    if (!pme->bP3M)
 +    {
 +        /* Use plain SPME B-spline interpolation */
 +        make_bspline_moduli(pme->bsp_mod, pme->nkx, pme->nky, pme->nkz, pme->pme_order);
 +    }
 +    else
 +    {
 +        /* Use the P3M grid-optimized influence function */
 +        make_p3m_bspline_moduli(pme->bsp_mod, pme->nkx, pme->nky, pme->nkz, pme->pme_order);
 +    }
 +
 +    /* Use atc[0] for spreading */
 +    init_atomcomm(pme, &pme->atc[0], nnodes_major > 1 ? 0 : 1, TRUE);
 +    if (pme->ndecompdim >= 2)
 +    {
 +        init_atomcomm(pme, &pme->atc[1], 1, FALSE);
 +    }
 +
 +    if (pme->nnodes == 1)
 +    {
 +        pme->atc[0].n = homenr;
 +        pme_realloc_atomcomm_things(&pme->atc[0]);
 +    }
 +
 +    pme->lb_buf1       = NULL;
 +    pme->lb_buf2       = NULL;
 +    pme->lb_buf_nalloc = 0;
 +
 +    {
 +        int thread;
 +
 +        /* Use fft5d, order after FFT is y major, z, x minor */
 +
 +        snew(pme->work, pme->nthread);
 +        for (thread = 0; thread < pme->nthread; thread++)
 +        {
 +            realloc_work(&pme->work[thread], pme->nkx);
 +        }
 +    }
 +
 +    *pmedata = pme;
 +
 +    return 0;
 +}
 +
 +static void reuse_pmegrids(const pmegrids_t *old, pmegrids_t *new)
 +{
 +    int d, t;
 +
 +    for (d = 0; d < DIM; d++)
 +    {
 +        if (new->grid.n[d] > old->grid.n[d])
 +        {
 +            return;
 +        }
 +    }
 +
 +    sfree_aligned(new->grid.grid);
 +    new->grid.grid = old->grid.grid;
 +
 +    if (new->grid_th != NULL && new->nthread == old->nthread)
 +    {
 +        sfree_aligned(new->grid_all);
 +        for (t = 0; t < new->nthread; t++)
 +        {
 +            new->grid_th[t].grid = old->grid_th[t].grid;
 +        }
 +    }
 +}
 +
 +int gmx_pme_reinit(gmx_pme_t *         pmedata,
 +                   t_commrec *         cr,
 +                   gmx_pme_t           pme_src,
 +                   const t_inputrec *  ir,
 +                   ivec                grid_size)
 +{
 +    t_inputrec irc;
 +    int homenr;
 +    int ret;
 +
 +    irc     = *ir;
 +    irc.nkx = grid_size[XX];
 +    irc.nky = grid_size[YY];
 +    irc.nkz = grid_size[ZZ];
 +
 +    if (pme_src->nnodes == 1)
 +    {
 +        homenr = pme_src->atc[0].n;
 +    }
 +    else
 +    {
 +        homenr = -1;
 +    }
 +
 +    ret = gmx_pme_init(pmedata, cr, pme_src->nnodes_major, pme_src->nnodes_minor,
 +                       &irc, homenr, pme_src->bFEP_q, pme_src->bFEP_lj, FALSE, pme_src->nthread);
 +
 +    if (ret == 0)
 +    {
 +        /* We can easily reuse the allocated pme grids in pme_src */
 +        reuse_pmegrids(&pme_src->pmegrid[PME_GRID_QA], &(*pmedata)->pmegrid[PME_GRID_QA]);
 +        /* We would like to reuse the fft grids, but that's harder */
 +    }
 +
 +    return ret;
 +}
 +
 +
 +static void copy_local_grid(gmx_pme_t pme, pmegrids_t *pmegrids,
 +                            int grid_index, int thread, real *fftgrid)
 +{
 +    ivec local_fft_ndata, local_fft_offset, local_fft_size;
 +    int  fft_my, fft_mz;
 +    int  nsx, nsy, nsz;
 +    ivec nf;
 +    int  offx, offy, offz, x, y, z, i0, i0t;
 +    int  d;
 +    pmegrid_t *pmegrid;
 +    real *grid_th;
 +
 +    gmx_parallel_3dfft_real_limits(pme->pfft_setup[grid_index],
 +                                   local_fft_ndata,
 +                                   local_fft_offset,
 +                                   local_fft_size);
 +    fft_my = local_fft_size[YY];
 +    fft_mz = local_fft_size[ZZ];
 +
 +    pmegrid = &pmegrids->grid_th[thread];
 +
 +    nsx = pmegrid->s[XX];
 +    nsy = pmegrid->s[YY];
 +    nsz = pmegrid->s[ZZ];
 +
 +    for (d = 0; d < DIM; d++)
 +    {
 +        nf[d] = min(pmegrid->n[d] - (pmegrid->order - 1),
 +                    local_fft_ndata[d] - pmegrid->offset[d]);
 +    }
 +
 +    offx = pmegrid->offset[XX];
 +    offy = pmegrid->offset[YY];
 +    offz = pmegrid->offset[ZZ];
 +
 +    /* Directly copy the non-overlapping parts of the local grids.
 +     * This also initializes the full grid.
 +     */
 +    grid_th = pmegrid->grid;
 +    for (x = 0; x < nf[XX]; x++)
 +    {
 +        for (y = 0; y < nf[YY]; y++)
 +        {
 +            i0  = ((offx + x)*fft_my + (offy + y))*fft_mz + offz;
 +            i0t = (x*nsy + y)*nsz;
 +            for (z = 0; z < nf[ZZ]; z++)
 +            {
 +                fftgrid[i0+z] = grid_th[i0t+z];
 +            }
 +        }
 +    }
 +}
 +
 +static void
 +reduce_threadgrid_overlap(gmx_pme_t pme,
 +                          const pmegrids_t *pmegrids, int thread,
 +                          real *fftgrid, real *commbuf_x, real *commbuf_y,
 +                          int grid_index)
 +{
 +    ivec local_fft_ndata, local_fft_offset, local_fft_size;
 +    int  fft_nx, fft_ny, fft_nz;
 +    int  fft_my, fft_mz;
 +    int  buf_my = -1;
 +    int  nsx, nsy, nsz;
 +    ivec ne;
 +    int  offx, offy, offz, x, y, z, i0, i0t;
 +    int  sx, sy, sz, fx, fy, fz, tx1, ty1, tz1, ox, oy, oz;
 +    gmx_bool bClearBufX, bClearBufY, bClearBufXY, bClearBuf;
 +    gmx_bool bCommX, bCommY;
 +    int  d;
 +    int  thread_f;
 +    const pmegrid_t *pmegrid, *pmegrid_g, *pmegrid_f;
 +    const real *grid_th;
 +    real *commbuf = NULL;
 +
 +    gmx_parallel_3dfft_real_limits(pme->pfft_setup[grid_index],
 +                                   local_fft_ndata,
 +                                   local_fft_offset,
 +                                   local_fft_size);
 +    fft_nx = local_fft_ndata[XX];
 +    fft_ny = local_fft_ndata[YY];
 +    fft_nz = local_fft_ndata[ZZ];
 +
 +    fft_my = local_fft_size[YY];
 +    fft_mz = local_fft_size[ZZ];
 +
 +    /* This routine is called when all thread have finished spreading.
 +     * Here each thread sums grid contributions calculated by other threads
 +     * to the thread local grid volume.
 +     * To minimize the number of grid copying operations,
 +     * this routines sums immediately from the pmegrid to the fftgrid.
 +     */
 +
 +    /* Determine which part of the full node grid we should operate on,
 +     * this is our thread local part of the full grid.
 +     */
 +    pmegrid = &pmegrids->grid_th[thread];
 +
 +    for (d = 0; d < DIM; d++)
 +    {
 +        ne[d] = min(pmegrid->offset[d]+pmegrid->n[d]-(pmegrid->order-1),
 +                    local_fft_ndata[d]);
 +    }
 +
 +    offx = pmegrid->offset[XX];
 +    offy = pmegrid->offset[YY];
 +    offz = pmegrid->offset[ZZ];
 +
 +
 +    bClearBufX  = TRUE;
 +    bClearBufY  = TRUE;
 +    bClearBufXY = TRUE;
 +
 +    /* Now loop over all the thread data blocks that contribute
 +     * to the grid region we (our thread) are operating on.
 +     */
-         if (!bCommX)
-         {
-             tx1 = min(ox + pmegrid_g->n[XX], ne[XX]);
-         }
-         else
-         {
-             tx1 = min(ox + pmegrid_g->n[XX], pme->pme_order);
-         }
++    /* Note that fft_nx/y is equal to the number of grid points
 +     * between the first point of our node grid and the one of the next node.
 +     */
 +    for (sx = 0; sx >= -pmegrids->nthread_comm[XX]; sx--)
 +    {
 +        fx     = pmegrid->ci[XX] + sx;
 +        ox     = 0;
 +        bCommX = FALSE;
 +        if (fx < 0)
 +        {
 +            fx    += pmegrids->nc[XX];
 +            ox    -= fft_nx;
 +            bCommX = (pme->nnodes_major > 1);
 +        }
 +        pmegrid_g = &pmegrids->grid_th[fx*pmegrids->nc[YY]*pmegrids->nc[ZZ]];
 +        ox       += pmegrid_g->offset[XX];
-             if (!bCommY)
-             {
-                 ty1 = min(oy + pmegrid_g->n[YY], ne[YY]);
-             }
-             else
-             {
-                 ty1 = min(oy + pmegrid_g->n[YY], pme->pme_order);
-             }
++        /* Determine the end of our part of the source grid */
++        tx1 = min(ox + pmegrid_g->n[XX], ne[XX]);
 +
 +        for (sy = 0; sy >= -pmegrids->nthread_comm[YY]; sy--)
 +        {
 +            fy     = pmegrid->ci[YY] + sy;
 +            oy     = 0;
 +            bCommY = FALSE;
 +            if (fy < 0)
 +            {
 +                fy    += pmegrids->nc[YY];
 +                oy    -= fft_ny;
 +                bCommY = (pme->nnodes_minor > 1);
 +            }
 +            pmegrid_g = &pmegrids->grid_th[fy*pmegrids->nc[ZZ]];
 +            oy       += pmegrid_g->offset[YY];
++            /* Determine the end of our part of the source grid */
++            ty1 = min(oy + pmegrid_g->n[YY], ne[YY]);
 +
 +            for (sz = 0; sz >= -pmegrids->nthread_comm[ZZ]; sz--)
 +            {
 +                fz = pmegrid->ci[ZZ] + sz;
 +                oz = 0;
 +                if (fz < 0)
 +                {
 +                    fz += pmegrids->nc[ZZ];
 +                    oz -= fft_nz;
 +                }
 +                pmegrid_g = &pmegrids->grid_th[fz];
 +                oz       += pmegrid_g->offset[ZZ];
 +                tz1       = min(oz + pmegrid_g->n[ZZ], ne[ZZ]);
 +
 +                if (sx == 0 && sy == 0 && sz == 0)
 +                {
 +                    /* We have already added our local contribution
 +                     * before calling this routine, so skip it here.
 +                     */
 +                    continue;
 +                }
 +
 +                thread_f = (fx*pmegrids->nc[YY] + fy)*pmegrids->nc[ZZ] + fz;
 +
 +                pmegrid_f = &pmegrids->grid_th[thread_f];
 +
 +                grid_th = pmegrid_f->grid;
 +
 +                nsx = pmegrid_f->s[XX];
 +                nsy = pmegrid_f->s[YY];
 +                nsz = pmegrid_f->s[ZZ];
 +
 +#ifdef DEBUG_PME_REDUCE
 +                printf("n%d t%d add %d  %2d %2d %2d  %2d %2d %2d  %2d-%2d %2d-%2d, %2d-%2d %2d-%2d, %2d-%2d %2d-%2d\n",
 +                       pme->nodeid, thread, thread_f,
 +                       pme->pmegrid_start_ix,
 +                       pme->pmegrid_start_iy,
 +                       pme->pmegrid_start_iz,
 +                       sx, sy, sz,
 +                       offx-ox, tx1-ox, offx, tx1,
 +                       offy-oy, ty1-oy, offy, ty1,
 +                       offz-oz, tz1-oz, offz, tz1);
 +#endif
 +
 +                if (!(bCommX || bCommY))
 +                {
 +                    /* Copy from the thread local grid to the node grid */
 +                    for (x = offx; x < tx1; x++)
 +                    {
 +                        for (y = offy; y < ty1; y++)
 +                        {
 +                            i0  = (x*fft_my + y)*fft_mz;
 +                            i0t = ((x - ox)*nsy + (y - oy))*nsz - oz;
 +                            for (z = offz; z < tz1; z++)
 +                            {
 +                                fftgrid[i0+z] += grid_th[i0t+z];
 +                            }
 +                        }
 +                    }
 +                }
 +                else
 +                {
 +                    /* The order of this conditional decides
 +                     * where the corner volume gets stored with x+y decomp.
 +                     */
 +                    if (bCommY)
 +                    {
 +                        commbuf = commbuf_y;
 +                        buf_my  = ty1 - offy;
 +                        if (bCommX)
 +                        {
 +                            /* We index commbuf modulo the local grid size */
 +                            commbuf += buf_my*fft_nx*fft_nz;
 +
 +                            bClearBuf   = bClearBufXY;
 +                            bClearBufXY = FALSE;
 +                        }
 +                        else
 +                        {
 +                            bClearBuf  = bClearBufY;
 +                            bClearBufY = FALSE;
 +                        }
 +                    }
 +                    else
 +                    {
 +                        commbuf    = commbuf_x;
 +                        buf_my     = fft_ny;
 +                        bClearBuf  = bClearBufX;
 +                        bClearBufX = FALSE;
 +                    }
 +
 +                    /* Copy to the communication buffer */
 +                    for (x = offx; x < tx1; x++)
 +                    {
 +                        for (y = offy; y < ty1; y++)
 +                        {
 +                            i0  = (x*buf_my + y)*fft_nz;
 +                            i0t = ((x - ox)*nsy + (y - oy))*nsz - oz;
 +
 +                            if (bClearBuf)
 +                            {
 +                                /* First access of commbuf, initialize it */
 +                                for (z = offz; z < tz1; z++)
 +                                {
 +                                    commbuf[i0+z]  = grid_th[i0t+z];
 +                                }
 +                            }
 +                            else
 +                            {
 +                                for (z = offz; z < tz1; z++)
 +                                {
 +                                    commbuf[i0+z] += grid_th[i0t+z];
 +                                }
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +
 +static void sum_fftgrid_dd(gmx_pme_t pme, real *fftgrid, int grid_index)
 +{
 +    ivec local_fft_ndata, local_fft_offset, local_fft_size;
 +    pme_overlap_t *overlap;
 +    int  send_index0, send_nindex;
 +    int  recv_nindex;
 +#ifdef GMX_MPI
 +    MPI_Status stat;
 +#endif
 +    int  send_size_y, recv_size_y;
 +    int  ipulse, send_id, recv_id, datasize, gridsize, size_yx;
 +    real *sendptr, *recvptr;
 +    int  x, y, z, indg, indb;
 +
 +    /* Note that this routine is only used for forward communication.
 +     * Since the force gathering, unlike the coefficient spreading,
 +     * can be trivially parallelized over the particles,
 +     * the backwards process is much simpler and can use the "old"
 +     * communication setup.
 +     */
 +
 +    gmx_parallel_3dfft_real_limits(pme->pfft_setup[grid_index],
 +                                   local_fft_ndata,
 +                                   local_fft_offset,
 +                                   local_fft_size);
 +
 +    if (pme->nnodes_minor > 1)
 +    {
 +        /* Major dimension */
 +        overlap = &pme->overlap[1];
 +
 +        if (pme->nnodes_major > 1)
 +        {
 +            size_yx = pme->overlap[0].comm_data[0].send_nindex;
 +        }
 +        else
 +        {
 +            size_yx = 0;
 +        }
 +        datasize = (local_fft_ndata[XX] + size_yx)*local_fft_ndata[ZZ];
 +
 +        send_size_y = overlap->send_size;
 +
 +        for (ipulse = 0; ipulse < overlap->noverlap_nodes; ipulse++)
 +        {
 +            send_id       = overlap->send_id[ipulse];
 +            recv_id       = overlap->recv_id[ipulse];
 +            send_index0   =
 +                overlap->comm_data[ipulse].send_index0 -
 +                overlap->comm_data[0].send_index0;
 +            send_nindex   = overlap->comm_data[ipulse].send_nindex;
 +            /* We don't use recv_index0, as we always receive starting at 0 */
 +            recv_nindex   = overlap->comm_data[ipulse].recv_nindex;
 +            recv_size_y   = overlap->comm_data[ipulse].recv_size;
 +
 +            sendptr = overlap->sendbuf + send_index0*local_fft_ndata[ZZ];
 +            recvptr = overlap->recvbuf;
 +
 +#ifdef GMX_MPI
 +            MPI_Sendrecv(sendptr, send_size_y*datasize, GMX_MPI_REAL,
 +                         send_id, ipulse,
 +                         recvptr, recv_size_y*datasize, GMX_MPI_REAL,
 +                         recv_id, ipulse,
 +                         overlap->mpi_comm, &stat);
 +#endif
 +
 +            for (x = 0; x < local_fft_ndata[XX]; x++)
 +            {
 +                for (y = 0; y < recv_nindex; y++)
 +                {
 +                    indg = (x*local_fft_size[YY] + y)*local_fft_size[ZZ];
 +                    indb = (x*recv_size_y        + y)*local_fft_ndata[ZZ];
 +                    for (z = 0; z < local_fft_ndata[ZZ]; z++)
 +                    {
 +                        fftgrid[indg+z] += recvptr[indb+z];
 +                    }
 +                }
 +            }
 +
 +            if (pme->nnodes_major > 1)
 +            {
 +                /* Copy from the received buffer to the send buffer for dim 0 */
 +                sendptr = pme->overlap[0].sendbuf;
 +                for (x = 0; x < size_yx; x++)
 +                {
 +                    for (y = 0; y < recv_nindex; y++)
 +                    {
 +                        indg = (x*local_fft_ndata[YY] + y)*local_fft_ndata[ZZ];
 +                        indb = ((local_fft_ndata[XX] + x)*recv_size_y + y)*local_fft_ndata[ZZ];
 +                        for (z = 0; z < local_fft_ndata[ZZ]; z++)
 +                        {
 +                            sendptr[indg+z] += recvptr[indb+z];
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    /* We only support a single pulse here.
 +     * This is not a severe limitation, as this code is only used
 +     * with OpenMP and with OpenMP the (PME) domains can be larger.
 +     */
 +    if (pme->nnodes_major > 1)
 +    {
 +        /* Major dimension */
 +        overlap = &pme->overlap[0];
 +
 +        datasize = local_fft_ndata[YY]*local_fft_ndata[ZZ];
 +        gridsize = local_fft_size[YY] *local_fft_size[ZZ];
 +
 +        ipulse = 0;
 +
 +        send_id       = overlap->send_id[ipulse];
 +        recv_id       = overlap->recv_id[ipulse];
 +        send_nindex   = overlap->comm_data[ipulse].send_nindex;
 +        /* We don't use recv_index0, as we always receive starting at 0 */
 +        recv_nindex   = overlap->comm_data[ipulse].recv_nindex;
 +
 +        sendptr = overlap->sendbuf;
 +        recvptr = overlap->recvbuf;
 +
 +        if (debug != NULL)
 +        {
 +            fprintf(debug, "PME fftgrid comm %2d x %2d x %2d\n",
 +                    send_nindex, local_fft_ndata[YY], local_fft_ndata[ZZ]);
 +        }
 +
 +#ifdef GMX_MPI
 +        MPI_Sendrecv(sendptr, send_nindex*datasize, GMX_MPI_REAL,
 +                     send_id, ipulse,
 +                     recvptr, recv_nindex*datasize, GMX_MPI_REAL,
 +                     recv_id, ipulse,
 +                     overlap->mpi_comm, &stat);
 +#endif
 +
 +        for (x = 0; x < recv_nindex; x++)
 +        {
 +            for (y = 0; y < local_fft_ndata[YY]; y++)
 +            {
 +                indg = (x*local_fft_size[YY]  + y)*local_fft_size[ZZ];
 +                indb = (x*local_fft_ndata[YY] + y)*local_fft_ndata[ZZ];
 +                for (z = 0; z < local_fft_ndata[ZZ]; z++)
 +                {
 +                    fftgrid[indg+z] += recvptr[indb+z];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +
 +static void spread_on_grid(gmx_pme_t pme,
 +                           pme_atomcomm_t *atc, pmegrids_t *grids,
 +                           gmx_bool bCalcSplines, gmx_bool bSpread,
 +                           real *fftgrid, gmx_bool bDoSplines, int grid_index)
 +{
 +    int nthread, thread;
 +#ifdef PME_TIME_THREADS
 +    gmx_cycles_t c1, c2, c3, ct1a, ct1b, ct1c;
 +    static double cs1     = 0, cs2 = 0, cs3 = 0;
 +    static double cs1a[6] = {0, 0, 0, 0, 0, 0};
 +    static int cnt        = 0;
 +#endif
 +
 +    nthread = pme->nthread;
 +    assert(nthread > 0);
 +
 +#ifdef PME_TIME_THREADS
 +    c1 = omp_cyc_start();
 +#endif
 +    if (bCalcSplines)
 +    {
 +#pragma omp parallel for num_threads(nthread) schedule(static)
 +        for (thread = 0; thread < nthread; thread++)
 +        {
 +            int start, end;
 +
 +            start = atc->n* thread   /nthread;
 +            end   = atc->n*(thread+1)/nthread;
 +
 +            /* Compute fftgrid index for all atoms,
 +             * with help of some extra variables.
 +             */
 +            calc_interpolation_idx(pme, atc, start, grid_index, end, thread);
 +        }
 +    }
 +#ifdef PME_TIME_THREADS
 +    c1   = omp_cyc_end(c1);
 +    cs1 += (double)c1;
 +#endif
 +
 +#ifdef PME_TIME_THREADS
 +    c2 = omp_cyc_start();
 +#endif
 +#pragma omp parallel for num_threads(nthread) schedule(static)
 +    for (thread = 0; thread < nthread; thread++)
 +    {
 +        splinedata_t *spline;
 +        pmegrid_t *grid = NULL;
 +
 +        /* make local bsplines  */
 +        if (grids == NULL || !pme->bUseThreads)
 +        {
 +            spline = &atc->spline[0];
 +
 +            spline->n = atc->n;
 +
 +            if (bSpread)
 +            {
 +                grid = &grids->grid;
 +            }
 +        }
 +        else
 +        {
 +            spline = &atc->spline[thread];
 +
 +            if (grids->nthread == 1)
 +            {
 +                /* One thread, we operate on all coefficients */
 +                spline->n = atc->n;
 +            }
 +            else
 +            {
 +                /* Get the indices our thread should operate on */
 +                make_thread_local_ind(atc, thread, spline);
 +            }
 +
 +            grid = &grids->grid_th[thread];
 +        }
 +
 +        if (bCalcSplines)
 +        {
 +            make_bsplines(spline->theta, spline->dtheta, pme->pme_order,
 +                          atc->fractx, spline->n, spline->ind, atc->coefficient, bDoSplines);
 +        }
 +
 +        if (bSpread)
 +        {
 +            /* put local atoms on grid. */
 +#ifdef PME_TIME_SPREAD
 +            ct1a = omp_cyc_start();
 +#endif
 +            spread_coefficients_bsplines_thread(grid, atc, spline, pme->spline_work);
 +
 +            if (pme->bUseThreads)
 +            {
 +                copy_local_grid(pme, grids, grid_index, thread, fftgrid);
 +            }
 +#ifdef PME_TIME_SPREAD
 +            ct1a          = omp_cyc_end(ct1a);
 +            cs1a[thread] += (double)ct1a;
 +#endif
 +        }
 +    }
 +#ifdef PME_TIME_THREADS
 +    c2   = omp_cyc_end(c2);
 +    cs2 += (double)c2;
 +#endif
 +
 +    if (bSpread && pme->bUseThreads)
 +    {
 +#ifdef PME_TIME_THREADS
 +        c3 = omp_cyc_start();
 +#endif
 +#pragma omp parallel for num_threads(grids->nthread) schedule(static)
 +        for (thread = 0; thread < grids->nthread; thread++)
 +        {
 +            reduce_threadgrid_overlap(pme, grids, thread,
 +                                      fftgrid,
 +                                      pme->overlap[0].sendbuf,
 +                                      pme->overlap[1].sendbuf,
 +                                      grid_index);
 +        }
 +#ifdef PME_TIME_THREADS
 +        c3   = omp_cyc_end(c3);
 +        cs3 += (double)c3;
 +#endif
 +
 +        if (pme->nnodes > 1)
 +        {
 +            /* Communicate the overlapping part of the fftgrid.
 +             * For this communication call we need to check pme->bUseThreads
 +             * to have all ranks communicate here, regardless of pme->nthread.
 +             */
 +            sum_fftgrid_dd(pme, fftgrid, grid_index);
 +        }
 +    }
 +
 +#ifdef PME_TIME_THREADS
 +    cnt++;
 +    if (cnt % 20 == 0)
 +    {
 +        printf("idx %.2f spread %.2f red %.2f",
 +               cs1*1e-9, cs2*1e-9, cs3*1e-9);
 +#ifdef PME_TIME_SPREAD
 +        for (thread = 0; thread < nthread; thread++)
 +        {
 +            printf(" %.2f", cs1a[thread]*1e-9);
 +        }
 +#endif
 +        printf("\n");
 +    }
 +#endif
 +}
 +
 +
 +static void dump_grid(FILE *fp,
 +                      int sx, int sy, int sz, int nx, int ny, int nz,
 +                      int my, int mz, const real *g)
 +{
 +    int x, y, z;
 +
 +    for (x = 0; x < nx; x++)
 +    {
 +        for (y = 0; y < ny; y++)
 +        {
 +            for (z = 0; z < nz; z++)
 +            {
 +                fprintf(fp, "%2d %2d %2d %6.3f\n",
 +                        sx+x, sy+y, sz+z, g[(x*my + y)*mz + z]);
 +            }
 +        }
 +    }
 +}
 +
 +static void dump_local_fftgrid(gmx_pme_t pme, const real *fftgrid)
 +{
 +    ivec local_fft_ndata, local_fft_offset, local_fft_size;
 +
 +    gmx_parallel_3dfft_real_limits(pme->pfft_setup[PME_GRID_QA],
 +                                   local_fft_ndata,
 +                                   local_fft_offset,
 +                                   local_fft_size);
 +
 +    dump_grid(stderr,
 +              pme->pmegrid_start_ix,
 +              pme->pmegrid_start_iy,
 +              pme->pmegrid_start_iz,
 +              pme->pmegrid_nx-pme->pme_order+1,
 +              pme->pmegrid_ny-pme->pme_order+1,
 +              pme->pmegrid_nz-pme->pme_order+1,
 +              local_fft_size[YY],
 +              local_fft_size[ZZ],
 +              fftgrid);
 +}
 +
 +
 +void gmx_pme_calc_energy(gmx_pme_t pme, int n, rvec *x, real *q, real *V)
 +{
 +    pme_atomcomm_t *atc;
 +    pmegrids_t *grid;
 +
 +    if (pme->nnodes > 1)
 +    {
 +        gmx_incons("gmx_pme_calc_energy called in parallel");
 +    }
 +    if (pme->bFEP_q > 1)
 +    {
 +        gmx_incons("gmx_pme_calc_energy with free energy");
 +    }
 +
 +    atc            = &pme->atc_energy;
 +    atc->nthread   = 1;
 +    if (atc->spline == NULL)
 +    {
 +        snew(atc->spline, atc->nthread);
 +    }
 +    atc->nslab     = 1;
 +    atc->bSpread   = TRUE;
 +    atc->pme_order = pme->pme_order;
 +    atc->n         = n;
 +    pme_realloc_atomcomm_things(atc);
 +    atc->x           = x;
 +    atc->coefficient = q;
 +
 +    /* We only use the A-charges grid */
 +    grid = &pme->pmegrid[PME_GRID_QA];
 +
 +    /* Only calculate the spline coefficients, don't actually spread */
 +    spread_on_grid(pme, atc, NULL, TRUE, FALSE, pme->fftgrid[PME_GRID_QA], FALSE, PME_GRID_QA);
 +
 +    *V = gather_energy_bsplines(pme, grid->grid.grid, atc);
 +}
 +
 +
 +static void reset_pmeonly_counters(gmx_wallcycle_t wcycle,
 +                                   gmx_walltime_accounting_t walltime_accounting,
 +                                   t_nrnb *nrnb, t_inputrec *ir,
 +                                   gmx_int64_t step)
 +{
 +    /* Reset all the counters related to performance over the run */
 +    wallcycle_stop(wcycle, ewcRUN);
 +    wallcycle_reset_all(wcycle);
 +    init_nrnb(nrnb);
 +    if (ir->nsteps >= 0)
 +    {
 +        /* ir->nsteps is not used here, but we update it for consistency */
 +        ir->nsteps -= step - ir->init_step;
 +    }
 +    ir->init_step = step;
 +    wallcycle_start(wcycle, ewcRUN);
 +    walltime_accounting_start(walltime_accounting);
 +}
 +
 +
 +static void gmx_pmeonly_switch(int *npmedata, gmx_pme_t **pmedata,
 +                               ivec grid_size,
 +                               t_commrec *cr, t_inputrec *ir,
 +                               gmx_pme_t *pme_ret)
 +{
 +    int ind;
 +    gmx_pme_t pme = NULL;
 +
 +    ind = 0;
 +    while (ind < *npmedata)
 +    {
 +        pme = (*pmedata)[ind];
 +        if (pme->nkx == grid_size[XX] &&
 +            pme->nky == grid_size[YY] &&
 +            pme->nkz == grid_size[ZZ])
 +        {
 +            *pme_ret = pme;
 +
 +            return;
 +        }
 +
 +        ind++;
 +    }
 +
 +    (*npmedata)++;
 +    srenew(*pmedata, *npmedata);
 +
 +    /* Generate a new PME data structure, copying part of the old pointers */
 +    gmx_pme_reinit(&((*pmedata)[ind]), cr, pme, ir, grid_size);
 +
 +    *pme_ret = (*pmedata)[ind];
 +}
 +
 +int gmx_pmeonly(gmx_pme_t pme,
 +                t_commrec *cr,    t_nrnb *mynrnb,
 +                gmx_wallcycle_t wcycle,
 +                gmx_walltime_accounting_t walltime_accounting,
 +                real ewaldcoeff_q, real ewaldcoeff_lj,
 +                t_inputrec *ir)
 +{
 +    int npmedata;
 +    gmx_pme_t *pmedata;
 +    gmx_pme_pp_t pme_pp;
 +    int  ret;
 +    int  natoms;
 +    matrix box;
 +    rvec *x_pp      = NULL, *f_pp = NULL;
 +    real *chargeA   = NULL, *chargeB = NULL;
 +    real *c6A       = NULL, *c6B = NULL;
 +    real *sigmaA    = NULL, *sigmaB = NULL;
 +    real lambda_q   = 0;
 +    real lambda_lj  = 0;
 +    int  maxshift_x = 0, maxshift_y = 0;
 +    real energy_q, energy_lj, dvdlambda_q, dvdlambda_lj;
 +    matrix vir_q, vir_lj;
 +    float cycles;
 +    int  count;
 +    gmx_bool bEnerVir;
 +    int pme_flags;
 +    gmx_int64_t step, step_rel;
 +    ivec grid_switch;
 +
 +    /* This data will only use with PME tuning, i.e. switching PME grids */
 +    npmedata = 1;
 +    snew(pmedata, npmedata);
 +    pmedata[0] = pme;
 +
 +    pme_pp = gmx_pme_pp_init(cr);
 +
 +    init_nrnb(mynrnb);
 +
 +    count = 0;
 +    do /****** this is a quasi-loop over time steps! */
 +    {
 +        /* The reason for having a loop here is PME grid tuning/switching */
 +        do
 +        {
 +            /* Domain decomposition */
 +            ret = gmx_pme_recv_coeffs_coords(pme_pp,
 +                                             &natoms,
 +                                             &chargeA, &chargeB,
 +                                             &c6A, &c6B,
 +                                             &sigmaA, &sigmaB,
 +                                             box, &x_pp, &f_pp,
 +                                             &maxshift_x, &maxshift_y,
 +                                             &pme->bFEP_q, &pme->bFEP_lj,
 +                                             &lambda_q, &lambda_lj,
 +                                             &bEnerVir,
 +                                             &pme_flags,
 +                                             &step,
 +                                             grid_switch, &ewaldcoeff_q, &ewaldcoeff_lj);
 +
 +            if (ret == pmerecvqxSWITCHGRID)
 +            {
 +                /* Switch the PME grid to grid_switch */
 +                gmx_pmeonly_switch(&npmedata, &pmedata, grid_switch, cr, ir, &pme);
 +            }
 +
 +            if (ret == pmerecvqxRESETCOUNTERS)
 +            {
 +                /* Reset the cycle and flop counters */
 +                reset_pmeonly_counters(wcycle, walltime_accounting, mynrnb, ir, step);
 +            }
 +        }
 +        while (ret == pmerecvqxSWITCHGRID || ret == pmerecvqxRESETCOUNTERS);
 +
 +        if (ret == pmerecvqxFINISH)
 +        {
 +            /* We should stop: break out of the loop */
 +            break;
 +        }
 +
 +        step_rel = step - ir->init_step;
 +
 +        if (count == 0)
 +        {
 +            wallcycle_start(wcycle, ewcRUN);
 +            walltime_accounting_start(walltime_accounting);
 +        }
 +
 +        wallcycle_start(wcycle, ewcPMEMESH);
 +
 +        dvdlambda_q  = 0;
 +        dvdlambda_lj = 0;
 +        clear_mat(vir_q);
 +        clear_mat(vir_lj);
 +
 +        gmx_pme_do(pme, 0, natoms, x_pp, f_pp,
 +                   chargeA, chargeB, c6A, c6B, sigmaA, sigmaB, box,
 +                   cr, maxshift_x, maxshift_y, mynrnb, wcycle,
 +                   vir_q, ewaldcoeff_q, vir_lj, ewaldcoeff_lj,
 +                   &energy_q, &energy_lj, lambda_q, lambda_lj, &dvdlambda_q, &dvdlambda_lj,
 +                   pme_flags | GMX_PME_DO_ALL_F | (bEnerVir ? GMX_PME_CALC_ENER_VIR : 0));
 +
 +        cycles = wallcycle_stop(wcycle, ewcPMEMESH);
 +
 +        gmx_pme_send_force_vir_ener(pme_pp,
 +                                    f_pp, vir_q, energy_q, vir_lj, energy_lj,
 +                                    dvdlambda_q, dvdlambda_lj, cycles);
 +
 +        count++;
 +    } /***** end of quasi-loop, we stop with the break above */
 +    while (TRUE);
 +
 +    walltime_accounting_end(walltime_accounting);
 +
 +    return 0;
 +}
 +
 +static void
 +calc_initial_lb_coeffs(gmx_pme_t pme, real *local_c6, real *local_sigma)
 +{
 +    int  i;
 +
 +    for (i = 0; i < pme->atc[0].n; ++i)
 +    {
 +        real sigma4;
 +
 +        sigma4                     = local_sigma[i];
 +        sigma4                     = sigma4*sigma4;
 +        sigma4                     = sigma4*sigma4;
 +        pme->atc[0].coefficient[i] = local_c6[i] / sigma4;
 +    }
 +}
 +
 +static void
 +calc_next_lb_coeffs(gmx_pme_t pme, real *local_sigma)
 +{
 +    int  i;
 +
 +    for (i = 0; i < pme->atc[0].n; ++i)
 +    {
 +        pme->atc[0].coefficient[i] *= local_sigma[i];
 +    }
 +}
 +
 +static void
 +do_redist_pos_coeffs(gmx_pme_t pme, t_commrec *cr, int start, int homenr,
 +                     gmx_bool bFirst, rvec x[], real *data)
 +{
 +    int      d;
 +    pme_atomcomm_t *atc;
 +    atc = &pme->atc[0];
 +
 +    for (d = pme->ndecompdim - 1; d >= 0; d--)
 +    {
 +        int             n_d;
 +        rvec           *x_d;
 +        real           *param_d;
 +
 +        if (d == pme->ndecompdim - 1)
 +        {
 +            n_d     = homenr;
 +            x_d     = x + start;
 +            param_d = data;
 +        }
 +        else
 +        {
 +            n_d     = pme->atc[d + 1].n;
 +            x_d     = atc->x;
 +            param_d = atc->coefficient;
 +        }
 +        atc      = &pme->atc[d];
 +        atc->npd = n_d;
 +        if (atc->npd > atc->pd_nalloc)
 +        {
 +            atc->pd_nalloc = over_alloc_dd(atc->npd);
 +            srenew(atc->pd, atc->pd_nalloc);
 +        }
 +        pme_calc_pidx_wrapper(n_d, pme->recipbox, x_d, atc);
 +        where();
 +        /* Redistribute x (only once) and qA/c6A or qB/c6B */
 +        if (DOMAINDECOMP(cr))
 +        {
 +            dd_pmeredist_pos_coeffs(pme, n_d, bFirst, x_d, param_d, atc);
 +        }
 +    }
 +}
 +
 +int gmx_pme_do(gmx_pme_t pme,
 +               int start,       int homenr,
 +               rvec x[],        rvec f[],
 +               real *chargeA,   real *chargeB,
 +               real *c6A,       real *c6B,
 +               real *sigmaA,    real *sigmaB,
 +               matrix box, t_commrec *cr,
 +               int  maxshift_x, int maxshift_y,
 +               t_nrnb *nrnb,    gmx_wallcycle_t wcycle,
 +               matrix vir_q,      real ewaldcoeff_q,
 +               matrix vir_lj,   real ewaldcoeff_lj,
 +               real *energy_q,  real *energy_lj,
 +               real lambda_q, real lambda_lj,
 +               real *dvdlambda_q, real *dvdlambda_lj,
 +               int flags)
 +{
 +    int     d, i, j, k, ntot, npme, grid_index, max_grid_index;
 +    int     nx, ny, nz;
 +    int     n_d, local_ny;
 +    pme_atomcomm_t *atc = NULL;
 +    pmegrids_t *pmegrid = NULL;
 +    real    *grid       = NULL;
 +    real    *ptr;
 +    rvec    *x_d, *f_d;
 +    real    *coefficient = NULL;
 +    real    energy_AB[4];
 +    matrix  vir_AB[4];
 +    real    scale, lambda;
 +    gmx_bool bClearF;
 +    gmx_parallel_3dfft_t pfft_setup;
 +    real *  fftgrid;
 +    t_complex * cfftgrid;
 +    int     thread;
 +    gmx_bool bFirst, bDoSplines;
 +    int fep_state;
 +    int fep_states_lj           = pme->bFEP_lj ? 2 : 1;
 +    const gmx_bool bCalcEnerVir = flags & GMX_PME_CALC_ENER_VIR;
 +    const gmx_bool bCalcF       = flags & GMX_PME_CALC_F;
 +
 +    assert(pme->nnodes > 0);
 +    assert(pme->nnodes == 1 || pme->ndecompdim > 0);
 +
 +    if (pme->nnodes > 1)
 +    {
 +        atc      = &pme->atc[0];
 +        atc->npd = homenr;
 +        if (atc->npd > atc->pd_nalloc)
 +        {
 +            atc->pd_nalloc = over_alloc_dd(atc->npd);
 +            srenew(atc->pd, atc->pd_nalloc);
 +        }
 +        for (d = pme->ndecompdim-1; d >= 0; d--)
 +        {
 +            atc           = &pme->atc[d];
 +            atc->maxshift = (atc->dimind == 0 ? maxshift_x : maxshift_y);
 +        }
 +    }
 +    else
 +    {
 +        atc = &pme->atc[0];
 +        /* This could be necessary for TPI */
 +        pme->atc[0].n = homenr;
 +        if (DOMAINDECOMP(cr))
 +        {
 +            pme_realloc_atomcomm_things(atc);
 +        }
 +        atc->x = x;
 +        atc->f = f;
 +    }
 +
 +    m_inv_ur0(box, pme->recipbox);
 +    bFirst = TRUE;
 +
 +    /* For simplicity, we construct the splines for all particles if
 +     * more than one PME calculations is needed. Some optimization
 +     * could be done by keeping track of which atoms have splines
 +     * constructed, and construct new splines on each pass for atoms
 +     * that don't yet have them.
 +     */
 +
 +    bDoSplines = pme->bFEP || ((flags & GMX_PME_DO_COULOMB) && (flags & GMX_PME_DO_LJ));
 +
 +    /* We need a maximum of four separate PME calculations:
 +     * grid_index=0: Coulomb PME with charges from state A
 +     * grid_index=1: Coulomb PME with charges from state B
 +     * grid_index=2: LJ PME with C6 from state A
 +     * grid_index=3: LJ PME with C6 from state B
 +     * For Lorentz-Berthelot combination rules, a separate loop is used to
 +     * calculate all the terms
 +     */
 +
 +    /* If we are doing LJ-PME with LB, we only do Q here */
 +    max_grid_index = (pme->ljpme_combination_rule == eljpmeLB) ? DO_Q : DO_Q_AND_LJ;
 +
 +    for (grid_index = 0; grid_index < max_grid_index; ++grid_index)
 +    {
 +        /* Check if we should do calculations at this grid_index
 +         * If grid_index is odd we should be doing FEP
 +         * If grid_index < 2 we should be doing electrostatic PME
 +         * If grid_index >= 2 we should be doing LJ-PME
 +         */
 +        if ((grid_index <  DO_Q && (!(flags & GMX_PME_DO_COULOMB) ||
 +                                    (grid_index == 1 && !pme->bFEP_q))) ||
 +            (grid_index >= DO_Q && (!(flags & GMX_PME_DO_LJ) ||
 +                                    (grid_index == 3 && !pme->bFEP_lj))))
 +        {
 +            continue;
 +        }
 +        /* Unpack structure */
 +        pmegrid    = &pme->pmegrid[grid_index];
 +        fftgrid    = pme->fftgrid[grid_index];
 +        cfftgrid   = pme->cfftgrid[grid_index];
 +        pfft_setup = pme->pfft_setup[grid_index];
 +        switch (grid_index)
 +        {
 +            case 0: coefficient = chargeA + start; break;
 +            case 1: coefficient = chargeB + start; break;
 +            case 2: coefficient = c6A + start; break;
 +            case 3: coefficient = c6B + start; break;
 +        }
 +
 +        grid = pmegrid->grid.grid;
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "PME: nnodes = %d, nodeid = %d\n",
 +                    cr->nnodes, cr->nodeid);
 +            fprintf(debug, "Grid = %p\n", (void*)grid);
 +            if (grid == NULL)
 +            {
 +                gmx_fatal(FARGS, "No grid!");
 +            }
 +        }
 +        where();
 +
 +        if (pme->nnodes == 1)
 +        {
 +            atc->coefficient = coefficient;
 +        }
 +        else
 +        {
 +            wallcycle_start(wcycle, ewcPME_REDISTXF);
 +            do_redist_pos_coeffs(pme, cr, start, homenr, bFirst, x, coefficient);
 +            where();
 +
 +            wallcycle_stop(wcycle, ewcPME_REDISTXF);
 +        }
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "Node= %6d, pme local particles=%6d\n",
 +                    cr->nodeid, atc->n);
 +        }
 +
 +        if (flags & GMX_PME_SPREAD)
 +        {
 +            wallcycle_start(wcycle, ewcPME_SPREADGATHER);
 +
 +            /* Spread the coefficients on a grid */
 +            spread_on_grid(pme, &pme->atc[0], pmegrid, bFirst, TRUE, fftgrid, bDoSplines, grid_index);
 +
 +            if (bFirst)
 +            {
 +                inc_nrnb(nrnb, eNR_WEIGHTS, DIM*atc->n);
 +            }
 +            inc_nrnb(nrnb, eNR_SPREADBSP,
 +                     pme->pme_order*pme->pme_order*pme->pme_order*atc->n);
 +
 +            if (!pme->bUseThreads)
 +            {
 +                wrap_periodic_pmegrid(pme, grid);
 +
 +                /* sum contributions to local grid from other nodes */
 +#ifdef GMX_MPI
 +                if (pme->nnodes > 1)
 +                {
 +                    gmx_sum_qgrid_dd(pme, grid, GMX_SUM_GRID_FORWARD);
 +                    where();
 +                }
 +#endif
 +
 +                copy_pmegrid_to_fftgrid(pme, grid, fftgrid, grid_index);
 +            }
 +
 +            wallcycle_stop(wcycle, ewcPME_SPREADGATHER);
 +
 +            /*
 +               dump_local_fftgrid(pme,fftgrid);
 +               exit(0);
 +             */
 +        }
 +
 +        /* Here we start a large thread parallel region */
 +#pragma omp parallel num_threads(pme->nthread) private(thread)
 +        {
 +            thread = gmx_omp_get_thread_num();
 +            if (flags & GMX_PME_SOLVE)
 +            {
 +                int loop_count;
 +
 +                /* do 3d-fft */
 +                if (thread == 0)
 +                {
 +                    wallcycle_start(wcycle, ewcPME_FFT);
 +                }
 +                gmx_parallel_3dfft_execute(pfft_setup, GMX_FFT_REAL_TO_COMPLEX,
 +                                           thread, wcycle);
 +                if (thread == 0)
 +                {
 +                    wallcycle_stop(wcycle, ewcPME_FFT);
 +                }
 +                where();
 +
 +                /* solve in k-space for our local cells */
 +                if (thread == 0)
 +                {
 +                    wallcycle_start(wcycle, (grid_index < DO_Q ? ewcPME_SOLVE : ewcLJPME));
 +                }
 +                if (grid_index < DO_Q)
 +                {
 +                    loop_count =
 +                        solve_pme_yzx(pme, cfftgrid, ewaldcoeff_q,
 +                                      box[XX][XX]*box[YY][YY]*box[ZZ][ZZ],
 +                                      bCalcEnerVir,
 +                                      pme->nthread, thread);
 +                }
 +                else
 +                {
 +                    loop_count =
 +                        solve_pme_lj_yzx(pme, &cfftgrid, FALSE, ewaldcoeff_lj,
 +                                         box[XX][XX]*box[YY][YY]*box[ZZ][ZZ],
 +                                         bCalcEnerVir,
 +                                         pme->nthread, thread);
 +                }
 +
 +                if (thread == 0)
 +                {
 +                    wallcycle_stop(wcycle, (grid_index < DO_Q ? ewcPME_SOLVE : ewcLJPME));
 +                    where();
 +                    inc_nrnb(nrnb, eNR_SOLVEPME, loop_count);
 +                }
 +            }
 +
 +            if (bCalcF)
 +            {
 +                /* do 3d-invfft */
 +                if (thread == 0)
 +                {
 +                    where();
 +                    wallcycle_start(wcycle, ewcPME_FFT);
 +                }
 +                gmx_parallel_3dfft_execute(pfft_setup, GMX_FFT_COMPLEX_TO_REAL,
 +                                           thread, wcycle);
 +                if (thread == 0)
 +                {
 +                    wallcycle_stop(wcycle, ewcPME_FFT);
 +
 +                    where();
 +
 +                    if (pme->nodeid == 0)
 +                    {
 +                        ntot  = pme->nkx*pme->nky*pme->nkz;
 +                        npme  = ntot*log((real)ntot)/log(2.0);
 +                        inc_nrnb(nrnb, eNR_FFT, 2*npme);
 +                    }
 +
 +                    /* Note: this wallcycle region is closed below
 +                       outside an OpenMP region, so take care if
 +                       refactoring code here. */
 +                    wallcycle_start(wcycle, ewcPME_SPREADGATHER);
 +                }
 +
 +                copy_fftgrid_to_pmegrid(pme, fftgrid, grid, grid_index, pme->nthread, thread);
 +            }
 +        }
 +        /* End of thread parallel section.
 +         * With MPI we have to synchronize here before gmx_sum_qgrid_dd.
 +         */
 +
 +        if (bCalcF)
 +        {
 +            /* distribute local grid to all nodes */
 +#ifdef GMX_MPI
 +            if (pme->nnodes > 1)
 +            {
 +                gmx_sum_qgrid_dd(pme, grid, GMX_SUM_GRID_BACKWARD);
 +            }
 +#endif
 +            where();
 +
 +            unwrap_periodic_pmegrid(pme, grid);
 +
 +            /* interpolate forces for our local atoms */
 +
 +            where();
 +
 +            /* If we are running without parallelization,
 +             * atc->f is the actual force array, not a buffer,
 +             * therefore we should not clear it.
 +             */
 +            lambda  = grid_index < DO_Q ? lambda_q : lambda_lj;
 +            bClearF = (bFirst && PAR(cr));
 +#pragma omp parallel for num_threads(pme->nthread) schedule(static)
 +            for (thread = 0; thread < pme->nthread; thread++)
 +            {
 +                gather_f_bsplines(pme, grid, bClearF, atc,
 +                                  &atc->spline[thread],
 +                                  pme->bFEP ? (grid_index % 2 == 0 ? 1.0-lambda : lambda) : 1.0);
 +            }
 +
 +            where();
 +
 +            inc_nrnb(nrnb, eNR_GATHERFBSP,
 +                     pme->pme_order*pme->pme_order*pme->pme_order*pme->atc[0].n);
 +            /* Note: this wallcycle region is opened above inside an OpenMP
 +               region, so take care if refactoring code here. */
 +            wallcycle_stop(wcycle, ewcPME_SPREADGATHER);
 +        }
 +
 +        if (bCalcEnerVir)
 +        {
 +            /* This should only be called on the master thread
 +             * and after the threads have synchronized.
 +             */
 +            if (grid_index < 2)
 +            {
 +                get_pme_ener_vir_q(pme, pme->nthread, &energy_AB[grid_index], vir_AB[grid_index]);
 +            }
 +            else
 +            {
 +                get_pme_ener_vir_lj(pme, pme->nthread, &energy_AB[grid_index], vir_AB[grid_index]);
 +            }
 +        }
 +        bFirst = FALSE;
 +    } /* of grid_index-loop */
 +
 +    /* For Lorentz-Berthelot combination rules in LJ-PME, we need to calculate
 +     * seven terms. */
 +
 +    if ((flags & GMX_PME_DO_LJ) && pme->ljpme_combination_rule == eljpmeLB)
 +    {
 +        /* Loop over A- and B-state if we are doing FEP */
 +        for (fep_state = 0; fep_state < fep_states_lj; ++fep_state)
 +        {
 +            real *local_c6 = NULL, *local_sigma = NULL, *RedistC6 = NULL, *RedistSigma = NULL;
 +            if (pme->nnodes == 1)
 +            {
 +                if (pme->lb_buf1 == NULL)
 +                {
 +                    pme->lb_buf_nalloc = pme->atc[0].n;
 +                    snew(pme->lb_buf1, pme->lb_buf_nalloc);
 +                }
 +                pme->atc[0].coefficient = pme->lb_buf1;
 +                switch (fep_state)
 +                {
 +                    case 0:
 +                        local_c6      = c6A;
 +                        local_sigma   = sigmaA;
 +                        break;
 +                    case 1:
 +                        local_c6      = c6B;
 +                        local_sigma   = sigmaB;
 +                        break;
 +                    default:
 +                        gmx_incons("Trying to access wrong FEP-state in LJ-PME routine");
 +                }
 +            }
 +            else
 +            {
 +                atc = &pme->atc[0];
 +                switch (fep_state)
 +                {
 +                    case 0:
 +                        RedistC6      = c6A;
 +                        RedistSigma   = sigmaA;
 +                        break;
 +                    case 1:
 +                        RedistC6      = c6B;
 +                        RedistSigma   = sigmaB;
 +                        break;
 +                    default:
 +                        gmx_incons("Trying to access wrong FEP-state in LJ-PME routine");
 +                }
 +                wallcycle_start(wcycle, ewcPME_REDISTXF);
 +
 +                do_redist_pos_coeffs(pme, cr, start, homenr, bFirst, x, RedistC6);
 +                if (pme->lb_buf_nalloc < atc->n)
 +                {
 +                    pme->lb_buf_nalloc = atc->nalloc;
 +                    srenew(pme->lb_buf1, pme->lb_buf_nalloc);
 +                    srenew(pme->lb_buf2, pme->lb_buf_nalloc);
 +                }
 +                local_c6 = pme->lb_buf1;
 +                for (i = 0; i < atc->n; ++i)
 +                {
 +                    local_c6[i] = atc->coefficient[i];
 +                }
 +                where();
 +
 +                do_redist_pos_coeffs(pme, cr, start, homenr, FALSE, x, RedistSigma);
 +                local_sigma = pme->lb_buf2;
 +                for (i = 0; i < atc->n; ++i)
 +                {
 +                    local_sigma[i] = atc->coefficient[i];
 +                }
 +                where();
 +
 +                wallcycle_stop(wcycle, ewcPME_REDISTXF);
 +            }
 +            calc_initial_lb_coeffs(pme, local_c6, local_sigma);
 +
 +            /*Seven terms in LJ-PME with LB, grid_index < 2 reserved for electrostatics*/
 +            for (grid_index = 2; grid_index < 9; ++grid_index)
 +            {
 +                /* Unpack structure */
 +                pmegrid    = &pme->pmegrid[grid_index];
 +                fftgrid    = pme->fftgrid[grid_index];
 +                cfftgrid   = pme->cfftgrid[grid_index];
 +                pfft_setup = pme->pfft_setup[grid_index];
 +                calc_next_lb_coeffs(pme, local_sigma);
 +                grid = pmegrid->grid.grid;
 +                where();
 +
 +                if (flags & GMX_PME_SPREAD)
 +                {
 +                    wallcycle_start(wcycle, ewcPME_SPREADGATHER);
 +                    /* Spread the c6 on a grid */
 +                    spread_on_grid(pme, &pme->atc[0], pmegrid, bFirst, TRUE, fftgrid, bDoSplines, grid_index);
 +
 +                    if (bFirst)
 +                    {
 +                        inc_nrnb(nrnb, eNR_WEIGHTS, DIM*atc->n);
 +                    }
 +
 +                    inc_nrnb(nrnb, eNR_SPREADBSP,
 +                             pme->pme_order*pme->pme_order*pme->pme_order*atc->n);
 +                    if (pme->nthread == 1)
 +                    {
 +                        wrap_periodic_pmegrid(pme, grid);
 +                        /* sum contributions to local grid from other nodes */
 +#ifdef GMX_MPI
 +                        if (pme->nnodes > 1)
 +                        {
 +                            gmx_sum_qgrid_dd(pme, grid, GMX_SUM_GRID_FORWARD);
 +                            where();
 +                        }
 +#endif
 +                        copy_pmegrid_to_fftgrid(pme, grid, fftgrid, grid_index);
 +                    }
 +                    wallcycle_stop(wcycle, ewcPME_SPREADGATHER);
 +                }
 +                /*Here we start a large thread parallel region*/
 +#pragma omp parallel num_threads(pme->nthread) private(thread)
 +                {
 +                    thread = gmx_omp_get_thread_num();
 +                    if (flags & GMX_PME_SOLVE)
 +                    {
 +                        /* do 3d-fft */
 +                        if (thread == 0)
 +                        {
 +                            wallcycle_start(wcycle, ewcPME_FFT);
 +                        }
 +
 +                        gmx_parallel_3dfft_execute(pfft_setup, GMX_FFT_REAL_TO_COMPLEX,
 +                                                   thread, wcycle);
 +                        if (thread == 0)
 +                        {
 +                            wallcycle_stop(wcycle, ewcPME_FFT);
 +                        }
 +                        where();
 +                    }
 +                }
 +                bFirst = FALSE;
 +            }
 +            if (flags & GMX_PME_SOLVE)
 +            {
 +                /* solve in k-space for our local cells */
 +#pragma omp parallel num_threads(pme->nthread) private(thread)
 +                {
 +                    int loop_count;
 +                    thread = gmx_omp_get_thread_num();
 +                    if (thread == 0)
 +                    {
 +                        wallcycle_start(wcycle, ewcLJPME);
 +                    }
 +
 +                    loop_count =
 +                        solve_pme_lj_yzx(pme, &pme->cfftgrid[2], TRUE, ewaldcoeff_lj,
 +                                         box[XX][XX]*box[YY][YY]*box[ZZ][ZZ],
 +                                         bCalcEnerVir,
 +                                         pme->nthread, thread);
 +                    if (thread == 0)
 +                    {
 +                        wallcycle_stop(wcycle, ewcLJPME);
 +                        where();
 +                        inc_nrnb(nrnb, eNR_SOLVEPME, loop_count);
 +                    }
 +                }
 +            }
 +
 +            if (bCalcEnerVir)
 +            {
 +                /* This should only be called on the master thread and
 +                 * after the threads have synchronized.
 +                 */
 +                get_pme_ener_vir_lj(pme, pme->nthread, &energy_AB[2+fep_state], vir_AB[2+fep_state]);
 +            }
 +
 +            if (bCalcF)
 +            {
 +                bFirst = !(flags & GMX_PME_DO_COULOMB);
 +                calc_initial_lb_coeffs(pme, local_c6, local_sigma);
 +                for (grid_index = 8; grid_index >= 2; --grid_index)
 +                {
 +                    /* Unpack structure */
 +                    pmegrid    = &pme->pmegrid[grid_index];
 +                    fftgrid    = pme->fftgrid[grid_index];
 +                    cfftgrid   = pme->cfftgrid[grid_index];
 +                    pfft_setup = pme->pfft_setup[grid_index];
 +                    grid       = pmegrid->grid.grid;
 +                    calc_next_lb_coeffs(pme, local_sigma);
 +                    where();
 +#pragma omp parallel num_threads(pme->nthread) private(thread)
 +                    {
 +                        thread = gmx_omp_get_thread_num();
 +                        /* do 3d-invfft */
 +                        if (thread == 0)
 +                        {
 +                            where();
 +                            wallcycle_start(wcycle, ewcPME_FFT);
 +                        }
 +
 +                        gmx_parallel_3dfft_execute(pfft_setup, GMX_FFT_COMPLEX_TO_REAL,
 +                                                   thread, wcycle);
 +                        if (thread == 0)
 +                        {
 +                            wallcycle_stop(wcycle, ewcPME_FFT);
 +
 +                            where();
 +
 +                            if (pme->nodeid == 0)
 +                            {
 +                                ntot  = pme->nkx*pme->nky*pme->nkz;
 +                                npme  = ntot*log((real)ntot)/log(2.0);
 +                                inc_nrnb(nrnb, eNR_FFT, 2*npme);
 +                            }
 +                            wallcycle_start(wcycle, ewcPME_SPREADGATHER);
 +                        }
 +
 +                        copy_fftgrid_to_pmegrid(pme, fftgrid, grid, grid_index, pme->nthread, thread);
 +
 +                    } /*#pragma omp parallel*/
 +
 +                    /* distribute local grid to all nodes */
 +#ifdef GMX_MPI
 +                    if (pme->nnodes > 1)
 +                    {
 +                        gmx_sum_qgrid_dd(pme, grid, GMX_SUM_GRID_BACKWARD);
 +                    }
 +#endif
 +                    where();
 +
 +                    unwrap_periodic_pmegrid(pme, grid);
 +
 +                    /* interpolate forces for our local atoms */
 +                    where();
 +                    bClearF = (bFirst && PAR(cr));
 +                    scale   = pme->bFEP ? (fep_state < 1 ? 1.0-lambda_lj : lambda_lj) : 1.0;
 +                    scale  *= lb_scale_factor[grid_index-2];
 +#pragma omp parallel for num_threads(pme->nthread) schedule(static)
 +                    for (thread = 0; thread < pme->nthread; thread++)
 +                    {
 +                        gather_f_bsplines(pme, grid, bClearF, &pme->atc[0],
 +                                          &pme->atc[0].spline[thread],
 +                                          scale);
 +                    }
 +                    where();
 +
 +                    inc_nrnb(nrnb, eNR_GATHERFBSP,
 +                             pme->pme_order*pme->pme_order*pme->pme_order*pme->atc[0].n);
 +                    wallcycle_stop(wcycle, ewcPME_SPREADGATHER);
 +
 +                    bFirst = FALSE;
 +                } /* for (grid_index = 8; grid_index >= 2; --grid_index) */
 +            }     /* if (bCalcF) */
 +        }         /* for (fep_state = 0; fep_state < fep_states_lj; ++fep_state) */
 +    }             /* if ((flags & GMX_PME_DO_LJ) && pme->ljpme_combination_rule == eljpmeLB) */
 +
 +    if (bCalcF && pme->nnodes > 1)
 +    {
 +        wallcycle_start(wcycle, ewcPME_REDISTXF);
 +        for (d = 0; d < pme->ndecompdim; d++)
 +        {
 +            atc = &pme->atc[d];
 +            if (d == pme->ndecompdim - 1)
 +            {
 +                n_d = homenr;
 +                f_d = f + start;
 +            }
 +            else
 +            {
 +                n_d = pme->atc[d+1].n;
 +                f_d = pme->atc[d+1].f;
 +            }
 +            if (DOMAINDECOMP(cr))
 +            {
 +                dd_pmeredist_f(pme, atc, n_d, f_d,
 +                               d == pme->ndecompdim-1 && pme->bPPnode);
 +            }
 +        }
 +
 +        wallcycle_stop(wcycle, ewcPME_REDISTXF);
 +    }
 +    where();
 +
 +    if (bCalcEnerVir)
 +    {
 +        if (flags & GMX_PME_DO_COULOMB)
 +        {
 +            if (!pme->bFEP_q)
 +            {
 +                *energy_q = energy_AB[0];
 +                m_add(vir_q, vir_AB[0], vir_q);
 +            }
 +            else
 +            {
 +                *energy_q       = (1.0-lambda_q)*energy_AB[0] + lambda_q*energy_AB[1];
 +                *dvdlambda_q   += energy_AB[1] - energy_AB[0];
 +                for (i = 0; i < DIM; i++)
 +                {
 +                    for (j = 0; j < DIM; j++)
 +                    {
 +                        vir_q[i][j] += (1.0-lambda_q)*vir_AB[0][i][j] +
 +                            lambda_q*vir_AB[1][i][j];
 +                    }
 +                }
 +            }
 +            if (debug)
 +            {
 +                fprintf(debug, "Electrostatic PME mesh energy: %g\n", *energy_q);
 +            }
 +        }
 +        else
 +        {
 +            *energy_q = 0;
 +        }
 +
 +        if (flags & GMX_PME_DO_LJ)
 +        {
 +            if (!pme->bFEP_lj)
 +            {
 +                *energy_lj = energy_AB[2];
 +                m_add(vir_lj, vir_AB[2], vir_lj);
 +            }
 +            else
 +            {
 +                *energy_lj     = (1.0-lambda_lj)*energy_AB[2] + lambda_lj*energy_AB[3];
 +                *dvdlambda_lj += energy_AB[3] - energy_AB[2];
 +                for (i = 0; i < DIM; i++)
 +                {
 +                    for (j = 0; j < DIM; j++)
 +                    {
 +                        vir_lj[i][j] += (1.0-lambda_lj)*vir_AB[2][i][j] + lambda_lj*vir_AB[3][i][j];
 +                    }
 +                }
 +            }
 +            if (debug)
 +            {
 +                fprintf(debug, "Lennard-Jones PME mesh energy: %g\n", *energy_lj);
 +            }
 +        }
 +        else
 +        {
 +            *energy_lj = 0;
 +        }
 +    }
 +    return 0;
 +}
index 531b88b61673dc519f888287f881b43600db2a5b,0000000000000000000000000000000000000000..7c1f8679f44741ccb1d2d16ad8c5842106623e89
mode 100644,000000..100644
--- /dev/null
@@@ -1,1259 -1,0 +1,1264 @@@
-                                  gmx_bool bCutoffSchemeIsVerlet,
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2008, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <string.h>
 +#include "typedefs.h"
 +#include "types/commrec.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "gmx_fatal.h"
 +#include "vec.h"
 +#include "txtdump.h"
 +#include "force.h"
 +#include "mdrun.h"
 +#include "mdatoms.h"
 +#include "vsite.h"
 +#include "network.h"
 +#include "names.h"
 +#include "constr.h"
 +#include "domdec.h"
 +#include "physics.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             */
 +    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;
 +    gmx_mtop_atomlookup_t alook = NULL;
 +    t_atom               *atom;
 +
 +    if (mass == NULL)
 +    {
 +        alook = gmx_mtop_atomlookup_init(mtop);
 +    }
 +
 +    /* 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(alook, n1, &atom);
 +                    m1 = atom->m;
 +                    gmx_mtop_atomnr_to_atom(alook, n2, &atom);
 +                    m2 = atom->m;
 +                    gmx_mtop_atomnr_to_atom(alook, 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);
 +        }
 +    }
 +
 +    if (mass == NULL)
 +    {
 +        gmx_mtop_atomlookup_destroy(alook);
 +    }
 +}
 +
 +gmx_shellfc_t init_shell_flexcon(FILE *fplog,
-     if (bCutoffSchemeIsVerlet)
-     {
-         gmx_fatal(FARGS, "The shell code does not work with the Verlet cut-off scheme.\n");
-     }
 +                                 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)
 +    {
 +        /* We're not doing shells or flexible constraints */
 +        return NULL;
 +    }
 +
-     /* When we had particle decomposition, this code only worked with
-      * PD when all particles involved with each shell were in the same
-      * charge group. Not sure if this is still relevant. */
 +    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->bRequireInit = FALSE;
 +    if (!shfc->bPredict)
 +    {
 +        if (fplog)
 +        {
 +            fprintf(fplog, "\nWill never predict shell positions\n");
 +        }
 +    }
 +    else
 +    {
 +        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");
 +            }
++            /* Prediction improves performance, so we should implement either:
++             * 1. communication for the atoms needed for prediction
++             * 2. prediction using the velocities of shells; currently the
++             *    shell velocities are zeroed, it's a bit tricky to keep
++             *    track of the shell displacements and thus the velocity.
++             */
 +            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 (DOMAINDECOMP(cr))
 +    {
 +        dd = cr->dd;
 +        a0 = 0;
 +        a1 = dd->nat_home;
 +    }
 +    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(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(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_int64_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_int64_t step, t_mdatoms *md, int start, int end,
 +                      rvec *x_old, rvec *x_init, rvec *x,
 +                      rvec *f, rvec *acc_dir,
 +                      gmx_bool bMolPBC, 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, bMolPBC, 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, bMolPBC, box,
 +              lambda[efptBONDED], &(dvdlambda[efptBONDED]),
 +              NULL, NULL, nrnb, econqCoord, FALSE, 0, 0);
 +
 +    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, bMolPBC, 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_int64_t mdstep, t_inputrec *inputrec,
 +                        gmx_bool bDoNS, int force_flags,
 +                        gmx_localtop_t *top,
 +                        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,
 +                        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 = 0, 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];
 +    }
 +
-         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 (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.
 +         */
-     /* After this all coordinate arrays will contain whole molecules */
++        if (inputrec->cutoff_scheme == ecutsVERLET)
++        {
++            put_atoms_in_box_omp(fr->ePBC, state->box, md->homenr, state->x);
++        }
++        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 charge groups */
 +    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, 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,
 +                  fr->bMolPBC, 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(vsite, pos[Min], inputrec->delta_t, state->v,
 +                             idef->iparams, idef->il,
 +                             fr->ePBC, fr->bMolPBC, 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,
 +                      fr->bMolPBC, state->box, state->lambda, &dum, nrnb);
 +
 +            directional_sd(pos[Min], pos[Try], acc_dir-start, start, end,
 +                           fr->fc_stepsize);
 +        }
 +
 +        /* New positions, Steepest descent */
 +        shell_pos_sd(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, 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,
 +                      fr->bMolPBC, 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 404516c4674eb205ae11425bddb2938921799571,0000000000000000000000000000000000000000..feb36f60f4aa88027a332b5218133d1337e64cf2
mode 100644,000000..100644
--- /dev/null
@@@ -1,1777 -1,0 +1,1777 @@@
-                                  real                                  dx,
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include "gromacs/math/utilities.h"
 +#include "typedefs.h"
 +#include "names.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "gmx_fatal.h"
 +#include "gromacs/fileio/futil.h"
 +#include "xvgr.h"
 +#include "vec.h"
 +#include "main.h"
 +#include "network.h"
 +#include "physics.h"
 +#include "force.h"
 +#include "gromacs/fileio/gmxfio.h"
 +#include "macros.h"
 +#include "tables.h"
 +
 +/* All the possible (implemented) table functions */
 +enum {
 +    etabLJ6,
 +    etabLJ12,
 +    etabLJ6Shift,
 +    etabLJ12Shift,
 +    etabShift,
 +    etabRF,
 +    etabRF_ZERO,
 +    etabCOUL,
 +    etabEwald,
 +    etabEwaldSwitch,
 +    etabEwaldUser,
 +    etabEwaldUserSwitch,
 +    etabLJ6Ewald,
 +    etabLJ6Switch,
 +    etabLJ12Switch,
 +    etabCOULSwitch,
 +    etabLJ6Encad,
 +    etabLJ12Encad,
 +    etabCOULEncad,
 +    etabEXPMIN,
 +    etabUSER,
 +    etabNR
 +};
 +
 +/** Evaluates to true if the table type contains user data. */
 +#define ETAB_USER(e)  ((e) == etabUSER || \
 +                       (e) == etabEwaldUser || (e) == etabEwaldUserSwitch)
 +
 +typedef struct {
 +    const char *name;
 +    gmx_bool    bCoulomb;
 +} t_tab_props;
 +
 +/* This structure holds name and a flag that tells whether
 +   this is a Coulomb type funtion */
 +static const t_tab_props tprops[etabNR] = {
 +    { "LJ6",  FALSE },
 +    { "LJ12", FALSE },
 +    { "LJ6Shift", FALSE },
 +    { "LJ12Shift", FALSE },
 +    { "Shift", TRUE },
 +    { "RF", TRUE },
 +    { "RF-zero", TRUE },
 +    { "COUL", TRUE },
 +    { "Ewald", TRUE },
 +    { "Ewald-Switch", TRUE },
 +    { "Ewald-User", TRUE },
 +    { "Ewald-User-Switch", TRUE },
 +    { "LJ6Ewald", FALSE },
 +    { "LJ6Switch", FALSE },
 +    { "LJ12Switch", FALSE },
 +    { "COULSwitch", TRUE },
 +    { "LJ6-Encad shift", FALSE },
 +    { "LJ12-Encad shift", FALSE },
 +    { "COUL-Encad shift",  TRUE },
 +    { "EXPMIN", FALSE },
 +    { "USER", FALSE },
 +};
 +
 +/* Index in the table that says which function to use */
 +enum {
 +    etiCOUL, etiLJ6, etiLJ12, etiNR
 +};
 +
 +typedef struct {
 +    int     nx, nx0;
 +    double  tabscale;
 +    double *x, *v, *f;
 +} t_tabledata;
 +
 +#define pow2(x) ((x)*(x))
 +#define pow3(x) ((x)*(x)*(x))
 +#define pow4(x) ((x)*(x)*(x)*(x))
 +#define pow5(x) ((x)*(x)*(x)*(x)*(x))
 +
 +double v_q_ewald_lr(double beta, double r)
 +{
 +    if (r == 0)
 +    {
 +        return beta*2/sqrt(M_PI);
 +    }
 +    else
 +    {
 +        return gmx_erfd(beta*r)/r;
 +    }
 +}
 +
 +double v_lj_ewald_lr(double beta, double r)
 +{
 +    double br, br2, br4, r6, factor;
 +    if (r == 0)
 +    {
 +        return pow(beta, 6)/6;
 +    }
 +    else
 +    {
 +        br     = beta*r;
 +        br2    = br*br;
 +        br4    = br2*br2;
 +        r6     = pow(r, 6.0);
 +        factor = (1.0 - exp(-br2)*(1 + br2 + 0.5*br4))/r6;
 +        return factor;
 +    }
 +}
 +
 +void table_spline3_fill_ewald_lr(real                                 *table_f,
 +                                 real                                 *table_v,
 +                                 real                                 *table_fdv0,
 +                                 int                                   ntab,
++                                 double                                dx,
 +                                 real                                  beta,
 +                                 real_space_grid_contribution_computer v_lr)
 +{
 +    real     tab_max;
 +    int      i, i_inrange;
 +    double   dc, dc_new;
 +    gmx_bool bOutOfRange;
 +    double   v_r0, v_r1, v_inrange, vi, a0, a1, a2dx;
 +    double   x_r0;
 +
 +    /* This function is called using either v_ewald_lr or v_lj_ewald_lr as a function argument
 +     * depending on wether we should create electrostatic or Lennard-Jones Ewald tables.
 +     */
 +
 +    if (ntab < 2)
 +    {
 +        gmx_fatal(FARGS, "Can not make a spline table with less than 2 points");
 +    }
 +
 +    /* We need some margin to be able to divide table values by r
 +     * in the kernel and also to do the integration arithmetics
 +     * without going out of range. Furthemore, we divide by dx below.
 +     */
 +    tab_max = GMX_REAL_MAX*0.0001;
 +
 +    /* This function produces a table with:
 +     * maximum energy error: V'''/(6*12*sqrt(3))*dx^3
 +     * maximum force error:  V'''/(6*4)*dx^2
 +     * The rms force error is the max error times 1/sqrt(5)=0.45.
 +     */
 +
 +    bOutOfRange = FALSE;
 +    i_inrange   = ntab;
 +    v_inrange   = 0;
 +    dc          = 0;
 +    for (i = ntab-1; i >= 0; i--)
 +    {
 +        x_r0 = i*dx;
 +
 +        v_r0 = (*v_lr)(beta, x_r0);
 +
 +        if (!bOutOfRange)
 +        {
 +            i_inrange = i;
 +            v_inrange = v_r0;
 +
 +            vi = v_r0;
 +        }
 +        else
 +        {
 +            /* Linear continuation for the last point in range */
 +            vi = v_inrange - dc*(i - i_inrange)*dx;
 +        }
 +
 +        if (table_v != NULL)
 +        {
 +            table_v[i] = vi;
 +        }
 +
 +        if (i == 0)
 +        {
 +            continue;
 +        }
 +
 +        /* Get the potential at table point i-1 */
 +        v_r1 = (*v_lr)(beta, (i-1)*dx);
 +
 +        if (v_r1 != v_r1 || v_r1 < -tab_max || v_r1 > tab_max)
 +        {
 +            bOutOfRange = TRUE;
 +        }
 +
 +        if (!bOutOfRange)
 +        {
 +            /* Calculate the average second derivative times dx over interval i-1 to i.
 +             * Using the function values at the end points and in the middle.
 +             */
 +            a2dx = (v_r0 + v_r1 - 2*(*v_lr)(beta, x_r0-0.5*dx))/(0.25*dx);
 +            /* Set the derivative of the spline to match the difference in potential
 +             * over the interval plus the average effect of the quadratic term.
 +             * This is the essential step for minimizing the error in the force.
 +             */
 +            dc = (v_r0 - v_r1)/dx + 0.5*a2dx;
 +        }
 +
 +        if (i == ntab - 1)
 +        {
 +            /* Fill the table with the force, minus the derivative of the spline */
 +            table_f[i] = -dc;
 +        }
 +        else
 +        {
 +            /* tab[i] will contain the average of the splines over the two intervals */
 +            table_f[i] += -0.5*dc;
 +        }
 +
 +        if (!bOutOfRange)
 +        {
 +            /* Make spline s(x) = a0 + a1*(x - xr) + 0.5*a2*(x - xr)^2
 +             * matching the potential at the two end points
 +             * and the derivative dc at the end point xr.
 +             */
 +            a0   = v_r0;
 +            a1   = dc;
 +            a2dx = (a1*dx + v_r1 - a0)*2/dx;
 +
 +            /* Set dc to the derivative at the next point */
 +            dc_new = a1 - a2dx;
 +
 +            if (dc_new != dc_new || dc_new < -tab_max || dc_new > tab_max)
 +            {
 +                bOutOfRange = TRUE;
 +            }
 +            else
 +            {
 +                dc = dc_new;
 +            }
 +        }
 +
 +        table_f[(i-1)] = -0.5*dc;
 +    }
 +    /* Currently the last value only contains half the force: double it */
 +    table_f[0] *= 2;
 +
 +    if (table_v != NULL && table_fdv0 != NULL)
 +    {
 +        /* Copy to FDV0 table too. Allocation occurs in forcerec.c,
 +         * init_ewald_f_table().
 +         */
 +        for (i = 0; i < ntab-1; i++)
 +        {
 +            table_fdv0[4*i]     = table_f[i];
 +            table_fdv0[4*i+1]   = table_f[i+1]-table_f[i];
 +            table_fdv0[4*i+2]   = table_v[i];
 +            table_fdv0[4*i+3]   = 0.0;
 +        }
 +        table_fdv0[4*(ntab-1)]    = table_f[(ntab-1)];
 +        table_fdv0[4*(ntab-1)+1]  = -table_f[(ntab-1)];
 +        table_fdv0[4*(ntab-1)+2]  = table_v[(ntab-1)];
 +        table_fdv0[4*(ntab-1)+3]  = 0.0;
 +    }
 +}
 +
 +/* The scale (1/spacing) for third order spline interpolation
 + * of the Ewald mesh contribution which needs to be subtracted
 + * from the non-bonded interactions.
 + */
 +real ewald_spline3_table_scale(real ewaldcoeff, real rc)
 +{
 +    double erf_x_d3 = 1.0522; /* max of (erf(x)/x)''' */
 +    double ftol, etol;
 +    double sc_f, sc_e;
 +
 +    /* Force tolerance: single precision accuracy */
 +    ftol = GMX_FLOAT_EPS;
 +    sc_f = sqrt(erf_x_d3/(6*4*ftol*ewaldcoeff))*ewaldcoeff;
 +
 +    /* Energy tolerance: 10x more accurate than the cut-off jump */
 +    etol = 0.1*gmx_erfc(ewaldcoeff*rc);
 +    etol = max(etol, GMX_REAL_EPS);
 +    sc_e = pow(erf_x_d3/(6*12*sqrt(3)*etol), 1.0/3.0)*ewaldcoeff;
 +
 +    return max(sc_f, sc_e);
 +}
 +
 +/* Calculate the potential and force for an r value
 + * in exactly the same way it is done in the inner loop.
 + * VFtab is a pointer to the table data, offset is
 + * the point where we should begin and stride is
 + * 4 if we have a buckingham table, 3 otherwise.
 + * If you want to evaluate table no N, set offset to 4*N.
 + *
 + * We use normal precision here, since that is what we
 + * will use in the inner loops.
 + */
 +static void evaluate_table(real VFtab[], int offset, int stride,
 +                           real tabscale, real r, real *y, real *yp)
 +{
 +    int  n;
 +    real rt, eps, eps2;
 +    real Y, F, Geps, Heps2, Fp;
 +
 +    rt       =  r*tabscale;
 +    n        =  (int)rt;
 +    eps      =  rt - n;
 +    eps2     =  eps*eps;
 +    n        =  offset+stride*n;
 +    Y        =  VFtab[n];
 +    F        =  VFtab[n+1];
 +    Geps     =  eps*VFtab[n+2];
 +    Heps2    =  eps2*VFtab[n+3];
 +    Fp       =  F+Geps+Heps2;
 +    *y       =  Y+eps*Fp;
 +    *yp      =  (Fp+Geps+2.0*Heps2)*tabscale;
 +}
 +
 +static void copy2table(int n, int offset, int stride,
 +                       double x[], double Vtab[], double Ftab[], real scalefactor,
 +                       real dest[])
 +{
 +/* Use double prec. for the intermediary variables
 + * and temporary x/vtab/vtab2 data to avoid unnecessary
 + * loss of precision.
 + */
 +    int    i, nn0;
 +    double F, G, H, h;
 +
 +    h = 0;
 +    for (i = 0; (i < n); i++)
 +    {
 +        if (i < n-1)
 +        {
 +            h   = x[i+1] - x[i];
 +            F   = -Ftab[i]*h;
 +            G   =  3*(Vtab[i+1] - Vtab[i]) + (Ftab[i+1] + 2*Ftab[i])*h;
 +            H   = -2*(Vtab[i+1] - Vtab[i]) - (Ftab[i+1] +   Ftab[i])*h;
 +        }
 +        else
 +        {
 +            /* Fill the last entry with a linear potential,
 +             * this is mainly for rounding issues with angle and dihedral potentials.
 +             */
 +            F   = -Ftab[i]*h;
 +            G   = 0;
 +            H   = 0;
 +        }
 +        nn0         = offset + i*stride;
 +        dest[nn0]   = scalefactor*Vtab[i];
 +        dest[nn0+1] = scalefactor*F;
 +        dest[nn0+2] = scalefactor*G;
 +        dest[nn0+3] = scalefactor*H;
 +    }
 +}
 +
 +static void init_table(int n, int nx0,
 +                       double tabscale, t_tabledata *td, gmx_bool bAlloc)
 +{
 +    int i;
 +
 +    td->nx       = n;
 +    td->nx0      = nx0;
 +    td->tabscale = tabscale;
 +    if (bAlloc)
 +    {
 +        snew(td->x, td->nx);
 +        snew(td->v, td->nx);
 +        snew(td->f, td->nx);
 +    }
 +    for (i = 0; (i < td->nx); i++)
 +    {
 +        td->x[i] = i/tabscale;
 +    }
 +}
 +
 +static void spline_forces(int nx, double h, double v[], gmx_bool bS3, gmx_bool bE3,
 +                          double f[])
 +{
 +    int    start, end, i;
 +    double v3, b_s, b_e, b;
 +    double beta, *gamma;
 +
 +    /* Formulas can be found in:
 +     * H.J.C. Berendsen, Simulating the Physical World, Cambridge 2007
 +     */
 +
 +    if (nx < 4 && (bS3 || bE3))
 +    {
 +        gmx_fatal(FARGS, "Can not generate splines with third derivative boundary conditions with less than 4 (%d) points", nx);
 +    }
 +
 +    /* To make life easy we initially set the spacing to 1
 +     * and correct for this at the end.
 +     */
 +    beta = 2;
 +    if (bS3)
 +    {
 +        /* Fit V''' at the start */
 +        v3  = v[3] - 3*v[2] + 3*v[1] - v[0];
 +        if (debug)
 +        {
 +            fprintf(debug, "The left third derivative is %g\n", v3/(h*h*h));
 +        }
 +        b_s   = 2*(v[1] - v[0]) + v3/6;
 +        start = 0;
 +
 +        if (FALSE)
 +        {
 +            /* Fit V'' at the start */
 +            real v2;
 +
 +            v2  = -v[3] + 4*v[2] - 5*v[1] + 2*v[0];
 +            /* v2  = v[2] - 2*v[1] + v[0]; */
 +            if (debug)
 +            {
 +                fprintf(debug, "The left second derivative is %g\n", v2/(h*h));
 +            }
 +            b_s   = 3*(v[1] - v[0]) - v2/2;
 +            start = 0;
 +        }
 +    }
 +    else
 +    {
 +        b_s   = 3*(v[2] - v[0]) + f[0]*h;
 +        start = 1;
 +    }
 +    if (bE3)
 +    {
 +        /* Fit V''' at the end */
 +        v3  = v[nx-1] - 3*v[nx-2] + 3*v[nx-3] - v[nx-4];
 +        if (debug)
 +        {
 +            fprintf(debug, "The right third derivative is %g\n", v3/(h*h*h));
 +        }
 +        b_e = 2*(v[nx-1] - v[nx-2]) + v3/6;
 +        end = nx;
 +    }
 +    else
 +    {
 +        /* V'=0 at the end */
 +        b_e = 3*(v[nx-1] - v[nx-3]) + f[nx-1]*h;
 +        end = nx - 1;
 +    }
 +
 +    snew(gamma, nx);
 +    beta = (bS3 ? 1 : 4);
 +
 +    /* For V'' fitting */
 +    /* beta = (bS3 ? 2 : 4); */
 +
 +    f[start] = b_s/beta;
 +    for (i = start+1; i < end; i++)
 +    {
 +        gamma[i] = 1/beta;
 +        beta     = 4 - gamma[i];
 +        b        =  3*(v[i+1] - v[i-1]);
 +        f[i]     = (b - f[i-1])/beta;
 +    }
 +    gamma[end-1] = 1/beta;
 +    beta         = (bE3 ? 1 : 4) - gamma[end-1];
 +    f[end-1]     = (b_e - f[end-2])/beta;
 +
 +    for (i = end-2; i >= start; i--)
 +    {
 +        f[i] -= gamma[i+1]*f[i+1];
 +    }
 +    sfree(gamma);
 +
 +    /* Correct for the minus sign and the spacing */
 +    for (i = start; i < end; i++)
 +    {
 +        f[i] = -f[i]/h;
 +    }
 +}
 +
 +static void set_forces(FILE *fp, int angle,
 +                       int nx, double h, double v[], double f[],
 +                       int table)
 +{
 +    int start, end;
 +
 +    if (angle == 2)
 +    {
 +        gmx_fatal(FARGS,
 +                  "Force generation for dihedral tables is not (yet) implemented");
 +    }
 +
 +    start = 0;
 +    while (v[start] == 0)
 +    {
 +        start++;
 +    }
 +
 +    end = nx;
 +    while (v[end-1] == 0)
 +    {
 +        end--;
 +    }
 +    if (end > nx - 2)
 +    {
 +        end = nx;
 +    }
 +    else
 +    {
 +        end++;
 +    }
 +
 +    if (fp)
 +    {
 +        fprintf(fp, "Generating forces for table %d, boundary conditions: V''' at %g, %s at %g\n",
 +                table+1, start*h, end == nx ? "V'''" : "V'=0", (end-1)*h);
 +    }
 +    spline_forces(end-start, h, v+start, TRUE, end == nx, f+start);
 +}
 +
 +static void read_tables(FILE *fp, const char *fn,
 +                        int ntab, int angle, t_tabledata td[])
 +{
 +    char    *libfn;
 +    char     buf[STRLEN];
 +    double **yy = NULL, start, end, dx0, dx1, ssd, vm, vp, f, numf;
 +    int      k, i, nx, nx0 = 0, ny, nny, ns;
 +    gmx_bool bAllZero, bZeroV, bZeroF;
 +    double   tabscale;
 +
 +    nny   = 2*ntab+1;
 +    libfn = gmxlibfn(fn);
 +    nx    = read_xvg(libfn, &yy, &ny);
 +    if (ny != nny)
 +    {
 +        gmx_fatal(FARGS, "Trying to read file %s, but nr columns = %d, should be %d",
 +                  libfn, ny, nny);
 +    }
 +    if (angle == 0)
 +    {
 +        if (yy[0][0] != 0.0)
 +        {
 +            gmx_fatal(FARGS,
 +                      "The first distance in file %s is %f nm instead of %f nm",
 +                      libfn, yy[0][0], 0.0);
 +        }
 +    }
 +    else
 +    {
 +        if (angle == 1)
 +        {
 +            start = 0.0;
 +        }
 +        else
 +        {
 +            start = -180.0;
 +        }
 +        end = 180.0;
 +        if (yy[0][0] != start || yy[0][nx-1] != end)
 +        {
 +            gmx_fatal(FARGS, "The angles in file %s should go from %f to %f instead of %f to %f\n",
 +                      libfn, start, end, yy[0][0], yy[0][nx-1]);
 +        }
 +    }
 +
 +    tabscale = (nx-1)/(yy[0][nx-1] - yy[0][0]);
 +
 +    if (fp)
 +    {
 +        fprintf(fp, "Read user tables from %s with %d data points.\n", libfn, nx);
 +        if (angle == 0)
 +        {
 +            fprintf(fp, "Tabscale = %g points/nm\n", tabscale);
 +        }
 +    }
 +
 +    bAllZero = TRUE;
 +    for (k = 0; k < ntab; k++)
 +    {
 +        bZeroV = TRUE;
 +        bZeroF = TRUE;
 +        for (i = 0; (i < nx); i++)
 +        {
 +            if (i >= 2)
 +            {
 +                dx0 = yy[0][i-1] - yy[0][i-2];
 +                dx1 = yy[0][i]   - yy[0][i-1];
 +                /* Check for 1% deviation in spacing */
 +                if (fabs(dx1 - dx0) >= 0.005*(fabs(dx0) + fabs(dx1)))
 +                {
 +                    gmx_fatal(FARGS, "In table file '%s' the x values are not equally spaced: %f %f %f", fn, yy[0][i-2], yy[0][i-1], yy[0][i]);
 +                }
 +            }
 +            if (yy[1+k*2][i] != 0)
 +            {
 +                bZeroV = FALSE;
 +                if (bAllZero)
 +                {
 +                    bAllZero = FALSE;
 +                    nx0      = i;
 +                }
 +                if (yy[1+k*2][i] >  0.01*GMX_REAL_MAX ||
 +                    yy[1+k*2][i] < -0.01*GMX_REAL_MAX)
 +                {
 +                    gmx_fatal(FARGS, "Out of range potential value %g in file '%s'",
 +                              yy[1+k*2][i], fn);
 +                }
 +            }
 +            if (yy[1+k*2+1][i] != 0)
 +            {
 +                bZeroF = FALSE;
 +                if (bAllZero)
 +                {
 +                    bAllZero = FALSE;
 +                    nx0      = i;
 +                }
 +                if (yy[1+k*2+1][i] >  0.01*GMX_REAL_MAX ||
 +                    yy[1+k*2+1][i] < -0.01*GMX_REAL_MAX)
 +                {
 +                    gmx_fatal(FARGS, "Out of range force value %g in file '%s'",
 +                              yy[1+k*2+1][i], fn);
 +                }
 +            }
 +        }
 +
 +        if (!bZeroV && bZeroF)
 +        {
 +            set_forces(fp, angle, nx, 1/tabscale, yy[1+k*2], yy[1+k*2+1], k);
 +        }
 +        else
 +        {
 +            /* Check if the second column is close to minus the numerical
 +             * derivative of the first column.
 +             */
 +            ssd = 0;
 +            ns  = 0;
 +            for (i = 1; (i < nx-1); i++)
 +            {
 +                vm = yy[1+2*k][i-1];
 +                vp = yy[1+2*k][i+1];
 +                f  = yy[1+2*k+1][i];
 +                if (vm != 0 && vp != 0 && f != 0)
 +                {
 +                    /* Take the centered difference */
 +                    numf = -(vp - vm)*0.5*tabscale;
 +                    ssd += fabs(2*(f - numf)/(f + numf));
 +                    ns++;
 +                }
 +            }
 +            if (ns > 0)
 +            {
 +                ssd /= ns;
 +                sprintf(buf, "For the %d non-zero entries for table %d in %s the forces deviate on average %d%% from minus the numerical derivative of the potential\n", ns, k, libfn, (int)(100*ssd+0.5));
 +                if (debug)
 +                {
 +                    fprintf(debug, "%s", buf);
 +                }
 +                if (ssd > 0.2)
 +                {
 +                    if (fp)
 +                    {
 +                        fprintf(fp, "\nWARNING: %s\n", buf);
 +                    }
 +                    fprintf(stderr, "\nWARNING: %s\n", buf);
 +                }
 +            }
 +        }
 +    }
 +    if (bAllZero && fp)
 +    {
 +        fprintf(fp, "\nNOTE: All elements in table %s are zero\n\n", libfn);
 +    }
 +
 +    for (k = 0; (k < ntab); k++)
 +    {
 +        init_table(nx, nx0, tabscale, &(td[k]), TRUE);
 +        for (i = 0; (i < nx); i++)
 +        {
 +            td[k].x[i] = yy[0][i];
 +            td[k].v[i] = yy[2*k+1][i];
 +            td[k].f[i] = yy[2*k+2][i];
 +        }
 +    }
 +    for (i = 0; (i < ny); i++)
 +    {
 +        sfree(yy[i]);
 +    }
 +    sfree(yy);
 +    sfree(libfn);
 +}
 +
 +static void done_tabledata(t_tabledata *td)
 +{
 +    int i;
 +
 +    if (!td)
 +    {
 +        return;
 +    }
 +
 +    sfree(td->x);
 +    sfree(td->v);
 +    sfree(td->f);
 +}
 +
 +static void fill_table(t_tabledata *td, int tp, const t_forcerec *fr,
 +                       gmx_bool b14only)
 +{
 +    /* Fill the table according to the formulas in the manual.
 +     * In principle, we only need the potential and the second
 +     * derivative, but then we would have to do lots of calculations
 +     * in the inner loop. By precalculating some terms (see manual)
 +     * we get better eventual performance, despite a larger table.
 +     *
 +     * Since some of these higher-order terms are very small,
 +     * we always use double precision to calculate them here, in order
 +     * to avoid unnecessary loss of precision.
 +     */
 +#ifdef DEBUG_SWITCH
 +    FILE    *fp;
 +#endif
 +    int      i;
 +    double   reppow, p;
 +    double   r1, rc, r12, r13;
 +    double   r, r2, r6, rc2, rc6, rc12;
 +    double   expr, Vtab, Ftab;
 +    /* Parameters for David's function */
 +    double   A = 0, B = 0, C = 0, A_3 = 0, B_4 = 0;
 +    /* Parameters for the switching function */
 +    double   ksw, swi, swi1;
 +    /* Temporary parameters */
 +    gmx_bool bPotentialSwitch, bForceSwitch, bPotentialShift;
 +    double   ewc   = fr->ewaldcoeff_q;
 +    double   ewclj = fr->ewaldcoeff_lj;
 +    double   Vcut  = 0;
 +
 +    if (b14only)
 +    {
 +        bPotentialSwitch = FALSE;
 +        bForceSwitch     = FALSE;
 +        bPotentialShift  = FALSE;
 +    }
 +    else
 +    {
 +        bPotentialSwitch = ((tp == etabLJ6Switch) || (tp == etabLJ12Switch) ||
 +                            (tp == etabCOULSwitch) ||
 +                            (tp == etabEwaldSwitch) || (tp == etabEwaldUserSwitch) ||
 +                            (tprops[tp].bCoulomb && (fr->coulomb_modifier == eintmodPOTSWITCH)) ||
 +                            (!tprops[tp].bCoulomb && (fr->vdw_modifier == eintmodPOTSWITCH)));
 +        bForceSwitch  = ((tp == etabLJ6Shift) || (tp == etabLJ12Shift) ||
 +                         (tp == etabShift) ||
 +                         (tprops[tp].bCoulomb && (fr->coulomb_modifier == eintmodFORCESWITCH)) ||
 +                         (!tprops[tp].bCoulomb && (fr->vdw_modifier == eintmodFORCESWITCH)));
 +        bPotentialShift = ((tprops[tp].bCoulomb && (fr->coulomb_modifier == eintmodPOTSHIFT)) ||
 +                           (!tprops[tp].bCoulomb && (fr->vdw_modifier == eintmodPOTSHIFT)));
 +    }
 +
 +    reppow = fr->reppow;
 +
 +    if (tprops[tp].bCoulomb)
 +    {
 +        r1 = fr->rcoulomb_switch;
 +        rc = fr->rcoulomb;
 +    }
 +    else
 +    {
 +        r1 = fr->rvdw_switch;
 +        rc = fr->rvdw;
 +    }
 +    if (bPotentialSwitch)
 +    {
 +        ksw  = 1.0/(pow5(rc-r1));
 +    }
 +    else
 +    {
 +        ksw  = 0.0;
 +    }
 +    if (bForceSwitch)
 +    {
 +        if (tp == etabShift)
 +        {
 +            p = 1;
 +        }
 +        else if (tp == etabLJ6Shift)
 +        {
 +            p = 6;
 +        }
 +        else
 +        {
 +            p = reppow;
 +        }
 +
 +        A = p * ((p+1)*r1-(p+4)*rc)/(pow(rc, p+2)*pow2(rc-r1));
 +        B = -p * ((p+1)*r1-(p+3)*rc)/(pow(rc, p+2)*pow3(rc-r1));
 +        C = 1.0/pow(rc, p)-A/3.0*pow3(rc-r1)-B/4.0*pow4(rc-r1);
 +        if (tp == etabLJ6Shift)
 +        {
 +            A = -A;
 +            B = -B;
 +            C = -C;
 +        }
 +        A_3 = A/3.0;
 +        B_4 = B/4.0;
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "Setting up tables\n"); fflush(debug);
 +    }
 +
 +#ifdef DEBUG_SWITCH
 +    fp = xvgropen("switch.xvg", "switch", "r", "s");
 +#endif
 +
 +    if (bPotentialShift)
 +    {
 +        rc2   = rc*rc;
 +        rc6   = 1.0/(rc2*rc2*rc2);
 +        if (gmx_within_tol(reppow, 12.0, 10*GMX_DOUBLE_EPS))
 +        {
 +            rc12 = rc6*rc6;
 +        }
 +        else
 +        {
 +            rc12 = pow(rc, -reppow);
 +        }
 +
 +        switch (tp)
 +        {
 +            case etabLJ6:
 +                /* Dispersion */
 +                Vcut = -rc6;
 +                break;
 +            case etabLJ6Ewald:
 +                Vcut  = -rc6*exp(-ewclj*ewclj*rc2)*(1 + ewclj*ewclj*rc2 + pow4(ewclj)*rc2*rc2/2);
 +                break;
 +            case etabLJ12:
 +                /* Repulsion */
 +                Vcut  = rc12;
 +                break;
 +            case etabCOUL:
 +                Vcut  = 1.0/rc;
 +                break;
 +            case etabEwald:
 +            case etabEwaldSwitch:
 +                Vtab  = gmx_erfc(ewc*rc)/rc;
 +                break;
 +            case etabEwaldUser:
 +                /* Only calculate minus the reciprocal space contribution */
 +                Vtab  = -gmx_erf(ewc*rc)/rc;
 +                break;
 +            case etabRF:
 +            case etabRF_ZERO:
 +                /* No need for preventing the usage of modifiers with RF */
 +                Vcut  = 0.0;
 +                break;
 +            case etabEXPMIN:
 +                Vcut  = exp(-rc);
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "Cannot apply new potential-shift modifier to interaction type '%s' yet. (%s,%d)",
 +                          tprops[tp].name, __FILE__, __LINE__);
 +        }
 +    }
 +
 +    for (i = td->nx0; (i < td->nx); i++)
 +    {
 +        r     = td->x[i];
 +        r2    = r*r;
 +        r6    = 1.0/(r2*r2*r2);
 +        if (gmx_within_tol(reppow, 12.0, 10*GMX_DOUBLE_EPS))
 +        {
 +            r12 = r6*r6;
 +        }
 +        else
 +        {
 +            r12 = pow(r, -reppow);
 +        }
 +        Vtab  = 0.0;
 +        Ftab  = 0.0;
 +        if (bPotentialSwitch)
 +        {
 +            /* swi is function, swi1 1st derivative and swi2 2nd derivative */
 +            /* The switch function is 1 for r<r1, 0 for r>rc, and smooth for
 +             * r1<=r<=rc. The 1st and 2nd derivatives are both zero at
 +             * r1 and rc.
 +             * ksw is just the constant 1/(rc-r1)^5, to save some calculations...
 +             */
 +            if (r <= r1)
 +            {
 +                swi  = 1.0;
 +                swi1 = 0.0;
 +            }
 +            else if (r >= rc)
 +            {
 +                swi  = 0.0;
 +                swi1 = 0.0;
 +            }
 +            else
 +            {
 +                swi      = 1 - 10*pow3(r-r1)*ksw*pow2(rc-r1)
 +                    + 15*pow4(r-r1)*ksw*(rc-r1) - 6*pow5(r-r1)*ksw;
 +                swi1     = -30*pow2(r-r1)*ksw*pow2(rc-r1)
 +                    + 60*pow3(r-r1)*ksw*(rc-r1) - 30*pow4(r-r1)*ksw;
 +            }
 +        }
 +        else /* not really needed, but avoids compiler warnings... */
 +        {
 +            swi  = 1.0;
 +            swi1 = 0.0;
 +        }
 +#ifdef DEBUG_SWITCH
 +        fprintf(fp, "%10g  %10g  %10g  %10g\n", r, swi, swi1, swi2);
 +#endif
 +
 +        rc6 = rc*rc*rc;
 +        rc6 = 1.0/(rc6*rc6);
 +
 +        switch (tp)
 +        {
 +            case etabLJ6:
 +                /* Dispersion */
 +                Vtab = -r6;
 +                Ftab = 6.0*Vtab/r;
 +                break;
 +            case etabLJ6Switch:
 +            case etabLJ6Shift:
 +                /* Dispersion */
 +                if (r < rc)
 +                {
 +                    Vtab = -r6;
 +                    Ftab = 6.0*Vtab/r;
 +                    break;
 +                }
 +                break;
 +            case etabLJ12:
 +                /* Repulsion */
 +                Vtab  = r12;
 +                Ftab  = reppow*Vtab/r;
 +                break;
 +            case etabLJ12Switch:
 +            case etabLJ12Shift:
 +                /* Repulsion */
 +                if (r < rc)
 +                {
 +                    Vtab  = r12;
 +                    Ftab  = reppow*Vtab/r;
 +                }
 +                break;
 +            case etabLJ6Encad:
 +                if (r < rc)
 +                {
 +                    Vtab  = -(r6-6.0*(rc-r)*rc6/rc-rc6);
 +                    Ftab  = -(6.0*r6/r-6.0*rc6/rc);
 +                }
 +                else /* r>rc */
 +                {
 +                    Vtab  = 0;
 +                    Ftab  = 0;
 +                }
 +                break;
 +            case etabLJ12Encad:
 +                if (r < rc)
 +                {
 +                    Vtab  = -(r6-6.0*(rc-r)*rc6/rc-rc6);
 +                    Ftab  = -(6.0*r6/r-6.0*rc6/rc);
 +                }
 +                else /* r>rc */
 +                {
 +                    Vtab  = 0;
 +                    Ftab  = 0;
 +                }
 +                break;
 +            case etabCOUL:
 +                Vtab  = 1.0/r;
 +                Ftab  = 1.0/r2;
 +                break;
 +            case etabCOULSwitch:
 +            case etabShift:
 +                if (r < rc)
 +                {
 +                    Vtab  = 1.0/r;
 +                    Ftab  = 1.0/r2;
 +                }
 +                break;
 +            case etabEwald:
 +            case etabEwaldSwitch:
 +                Vtab  = gmx_erfc(ewc*r)/r;
 +                Ftab  = gmx_erfc(ewc*r)/r2+exp(-(ewc*ewc*r2))*ewc*M_2_SQRTPI/r;
 +                break;
 +            case etabEwaldUser:
 +            case etabEwaldUserSwitch:
 +                /* Only calculate the negative of the reciprocal space contribution */
 +                Vtab  = -gmx_erf(ewc*r)/r;
 +                Ftab  = -gmx_erf(ewc*r)/r2+exp(-(ewc*ewc*r2))*ewc*M_2_SQRTPI/r;
 +                break;
 +            case etabLJ6Ewald:
 +                Vtab  = -r6*exp(-ewclj*ewclj*r2)*(1 + ewclj*ewclj*r2 + pow4(ewclj)*r2*r2/2);
 +                Ftab  = 6.0*Vtab/r - r6*exp(-ewclj*ewclj*r2)*pow5(ewclj)*ewclj*r2*r2*r;
 +                break;
 +            case etabRF:
 +            case etabRF_ZERO:
 +                Vtab  = 1.0/r      +   fr->k_rf*r2 - fr->c_rf;
 +                Ftab  = 1.0/r2     - 2*fr->k_rf*r;
 +                if (tp == etabRF_ZERO && r >= rc)
 +                {
 +                    Vtab = 0;
 +                    Ftab = 0;
 +                }
 +                break;
 +            case etabEXPMIN:
 +                expr  = exp(-r);
 +                Vtab  = expr;
 +                Ftab  = expr;
 +                break;
 +            case etabCOULEncad:
 +                if (r < rc)
 +                {
 +                    Vtab  = 1.0/r-(rc-r)/(rc*rc)-1.0/rc;
 +                    Ftab  = 1.0/r2-1.0/(rc*rc);
 +                }
 +                else /* r>rc */
 +                {
 +                    Vtab  = 0;
 +                    Ftab  = 0;
 +                }
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "Table type %d not implemented yet. (%s,%d)",
 +                          tp, __FILE__, __LINE__);
 +        }
 +        if (bForceSwitch)
 +        {
 +            /* Normal coulomb with cut-off correction for potential */
 +            if (r < rc)
 +            {
 +                Vtab -= C;
 +                /* If in Shifting range add something to it */
 +                if (r > r1)
 +                {
 +                    r12    = (r-r1)*(r-r1);
 +                    r13    = (r-r1)*r12;
 +                    Vtab  += -A_3*r13 - B_4*r12*r12;
 +                    Ftab  +=   A*r12 + B*r13;
 +                }
 +            }
 +            else
 +            {
 +                /* Make sure interactions are zero outside cutoff with modifiers */
 +                Vtab = 0;
 +                Ftab = 0;
 +            }
 +        }
 +        if (bPotentialShift)
 +        {
 +            if (r < rc)
 +            {
 +                Vtab -= Vcut;
 +            }
 +            else
 +            {
 +                /* Make sure interactions are zero outside cutoff with modifiers */
 +                Vtab = 0;
 +                Ftab = 0;
 +            }
 +        }
 +
 +        if (ETAB_USER(tp))
 +        {
 +            Vtab += td->v[i];
 +            Ftab += td->f[i];
 +        }
 +
 +        if (bPotentialSwitch)
 +        {
 +            if (r >= rc)
 +            {
 +                /* Make sure interactions are zero outside cutoff with modifiers */
 +                Vtab = 0;
 +                Ftab = 0;
 +            }
 +            else if (r > r1)
 +            {
 +                Ftab = Ftab*swi - Vtab*swi1;
 +                Vtab = Vtab*swi;
 +            }
 +        }
 +        /* Convert to single precision when we store to mem */
 +        td->v[i]  = Vtab;
 +        td->f[i]  = Ftab;
 +    }
 +
 +    /* Continue the table linearly from nx0 to 0.
 +     * These values are only required for energy minimization with overlap or TPI.
 +     */
 +    for (i = td->nx0-1; i >= 0; i--)
 +    {
 +        td->v[i] = td->v[i+1] + td->f[i+1]*(td->x[i+1] - td->x[i]);
 +        td->f[i] = td->f[i+1];
 +    }
 +
 +#ifdef DEBUG_SWITCH
 +    gmx_fio_fclose(fp);
 +#endif
 +}
 +
 +static void set_table_type(int tabsel[], const t_forcerec *fr, gmx_bool b14only)
 +{
 +    int eltype, vdwtype;
 +
 +    /* Set the different table indices.
 +     * Coulomb first.
 +     */
 +
 +
 +    if (b14only)
 +    {
 +        switch (fr->eeltype)
 +        {
 +            case eelRF_NEC:
 +                eltype = eelRF;
 +                break;
 +            case eelUSER:
 +            case eelPMEUSER:
 +            case eelPMEUSERSWITCH:
 +                eltype = eelUSER;
 +                break;
 +            default:
 +                eltype = eelCUT;
 +        }
 +    }
 +    else
 +    {
 +        eltype = fr->eeltype;
 +    }
 +
 +    switch (eltype)
 +    {
 +        case eelCUT:
 +            tabsel[etiCOUL] = etabCOUL;
 +            break;
 +        case eelPOISSON:
 +            tabsel[etiCOUL] = etabShift;
 +            break;
 +        case eelSHIFT:
 +            if (fr->rcoulomb > fr->rcoulomb_switch)
 +            {
 +                tabsel[etiCOUL] = etabShift;
 +            }
 +            else
 +            {
 +                tabsel[etiCOUL] = etabCOUL;
 +            }
 +            break;
 +        case eelEWALD:
 +        case eelPME:
 +        case eelP3M_AD:
 +            tabsel[etiCOUL] = etabEwald;
 +            break;
 +        case eelPMESWITCH:
 +            tabsel[etiCOUL] = etabEwaldSwitch;
 +            break;
 +        case eelPMEUSER:
 +            tabsel[etiCOUL] = etabEwaldUser;
 +            break;
 +        case eelPMEUSERSWITCH:
 +            tabsel[etiCOUL] = etabEwaldUserSwitch;
 +            break;
 +        case eelRF:
 +        case eelGRF:
 +        case eelRF_NEC:
 +            tabsel[etiCOUL] = etabRF;
 +            break;
 +        case eelRF_ZERO:
 +            tabsel[etiCOUL] = etabRF_ZERO;
 +            break;
 +        case eelSWITCH:
 +            tabsel[etiCOUL] = etabCOULSwitch;
 +            break;
 +        case eelUSER:
 +            tabsel[etiCOUL] = etabUSER;
 +            break;
 +        case eelENCADSHIFT:
 +            tabsel[etiCOUL] = etabCOULEncad;
 +            break;
 +        default:
 +            gmx_fatal(FARGS, "Invalid eeltype %d", eltype);
 +    }
 +
 +    /* Van der Waals time */
 +    if (fr->bBHAM && !b14only)
 +    {
 +        tabsel[etiLJ6]  = etabLJ6;
 +        tabsel[etiLJ12] = etabEXPMIN;
 +    }
 +    else
 +    {
 +        if (b14only && fr->vdwtype != evdwUSER)
 +        {
 +            vdwtype = evdwCUT;
 +        }
 +        else
 +        {
 +            vdwtype = fr->vdwtype;
 +        }
 +
 +        switch (vdwtype)
 +        {
 +            case evdwSWITCH:
 +                tabsel[etiLJ6]  = etabLJ6Switch;
 +                tabsel[etiLJ12] = etabLJ12Switch;
 +                break;
 +            case evdwSHIFT:
 +                tabsel[etiLJ6]  = etabLJ6Shift;
 +                tabsel[etiLJ12] = etabLJ12Shift;
 +                break;
 +            case evdwUSER:
 +                tabsel[etiLJ6]  = etabUSER;
 +                tabsel[etiLJ12] = etabUSER;
 +                break;
 +            case evdwCUT:
 +                tabsel[etiLJ6]  = etabLJ6;
 +                tabsel[etiLJ12] = etabLJ12;
 +                break;
 +            case evdwENCADSHIFT:
 +                tabsel[etiLJ6]  = etabLJ6Encad;
 +                tabsel[etiLJ12] = etabLJ12Encad;
 +                break;
 +            case evdwPME:
 +                tabsel[etiLJ6]  = etabLJ6Ewald;
 +                tabsel[etiLJ12] = etabLJ12;
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "Invalid vdwtype %d in %s line %d", vdwtype,
 +                          __FILE__, __LINE__);
 +        }
 +
 +        if (!b14only && fr->vdw_modifier != eintmodNONE)
 +        {
 +            if (fr->vdw_modifier != eintmodPOTSHIFT &&
 +                fr->vdwtype != evdwCUT)
 +            {
 +                gmx_incons("Potential modifiers other than potential-shift are only implemented for LJ cut-off");
 +            }
 +
 +            /* LJ-PME and other (shift-only) modifiers are handled by applying the modifiers
 +             * to the original interaction forms when we fill the table, so we only check cutoffs here.
 +             */
 +            if (fr->vdwtype == evdwCUT)
 +            {
 +                switch (fr->vdw_modifier)
 +                {
 +                    case eintmodNONE:
 +                    case eintmodPOTSHIFT:
 +                    case eintmodEXACTCUTOFF:
 +                        /* No modification */
 +                        break;
 +                    case eintmodPOTSWITCH:
 +                        tabsel[etiLJ6]  = etabLJ6Switch;
 +                        tabsel[etiLJ12] = etabLJ12Switch;
 +                        break;
 +                    case eintmodFORCESWITCH:
 +                        tabsel[etiLJ6]  = etabLJ6Shift;
 +                        tabsel[etiLJ12] = etabLJ12Shift;
 +                        break;
 +                    default:
 +                        gmx_incons("Unsupported vdw_modifier");
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +t_forcetable make_tables(FILE *out, const output_env_t oenv,
 +                         const t_forcerec *fr,
 +                         gmx_bool bVerbose, const char *fn,
 +                         real rtab, int flags)
 +{
 +    const char     *fns[3]   = { "ctab.xvg", "dtab.xvg", "rtab.xvg" };
 +    const char     *fns14[3] = { "ctab14.xvg", "dtab14.xvg", "rtab14.xvg" };
 +    FILE           *fp;
 +    t_tabledata    *td;
 +    gmx_bool        b14only, bReadTab, bGenTab;
 +    real            x0, y0, yp;
 +    int             i, j, k, nx, nx0, tabsel[etiNR];
 +    real            scalefactor;
 +
 +    t_forcetable    table;
 +
 +    b14only = (flags & GMX_MAKETABLES_14ONLY);
 +
 +    if (flags & GMX_MAKETABLES_FORCEUSER)
 +    {
 +        tabsel[etiCOUL] = etabUSER;
 +        tabsel[etiLJ6]  = etabUSER;
 +        tabsel[etiLJ12] = etabUSER;
 +    }
 +    else
 +    {
 +        set_table_type(tabsel, fr, b14only);
 +    }
 +    snew(td, etiNR);
 +    table.r         = rtab;
 +    table.scale     = 0;
 +    table.n         = 0;
 +    table.scale_exp = 0;
 +    nx0             = 10;
 +    nx              = 0;
 +
 +    table.interaction   = GMX_TABLE_INTERACTION_ELEC_VDWREP_VDWDISP;
 +    table.format        = GMX_TABLE_FORMAT_CUBICSPLINE_YFGH;
 +    table.formatsize    = 4;
 +    table.ninteractions = 3;
 +    table.stride        = table.formatsize*table.ninteractions;
 +
 +    /* Check whether we have to read or generate */
 +    bReadTab = FALSE;
 +    bGenTab  = FALSE;
 +    for (i = 0; (i < etiNR); i++)
 +    {
 +        if (ETAB_USER(tabsel[i]))
 +        {
 +            bReadTab = TRUE;
 +        }
 +        if (tabsel[i] != etabUSER)
 +        {
 +            bGenTab  = TRUE;
 +        }
 +    }
 +    if (bReadTab)
 +    {
 +        read_tables(out, fn, etiNR, 0, td);
 +        if (rtab == 0 || (flags & GMX_MAKETABLES_14ONLY))
 +        {
 +            rtab      = td[0].x[td[0].nx-1];
 +            table.n   = td[0].nx;
 +            nx        = table.n;
 +        }
 +        else
 +        {
 +            if (td[0].x[td[0].nx-1] < rtab)
 +            {
 +                gmx_fatal(FARGS, "Tables in file %s not long enough for cut-off:\n"
 +                          "\tshould be at least %f nm\n", fn, rtab);
 +            }
 +            nx        = table.n = (int)(rtab*td[0].tabscale + 0.5);
 +        }
 +        table.scale = td[0].tabscale;
 +        nx0         = td[0].nx0;
 +    }
 +    if (bGenTab)
 +    {
 +        if (!bReadTab)
 +        {
 +#ifdef GMX_DOUBLE
 +            table.scale = 2000.0;
 +#else
 +            table.scale = 500.0;
 +#endif
 +            nx = table.n = rtab*table.scale;
 +        }
 +    }
 +    if (fr->bBHAM)
 +    {
 +        if (fr->bham_b_max != 0)
 +        {
 +            table.scale_exp = table.scale/fr->bham_b_max;
 +        }
 +        else
 +        {
 +            table.scale_exp = table.scale;
 +        }
 +    }
 +
 +    /* Each table type (e.g. coul,lj6,lj12) requires four
 +     * numbers per nx+1 data points. For performance reasons we want
 +     * the table data to be aligned to 16-byte.
 +     */
 +    snew_aligned(table.data, 12*(nx+1)*sizeof(real), 32);
 +
 +    for (k = 0; (k < etiNR); k++)
 +    {
 +        if (tabsel[k] != etabUSER)
 +        {
 +            init_table(nx, nx0,
 +                       (tabsel[k] == etabEXPMIN) ? table.scale_exp : table.scale,
 +                       &(td[k]), !bReadTab);
 +            fill_table(&(td[k]), tabsel[k], fr, b14only);
 +            if (out)
 +            {
 +                fprintf(out, "%s table with %d data points for %s%s.\n"
 +                        "Tabscale = %g points/nm\n",
 +                        ETAB_USER(tabsel[k]) ? "Modified" : "Generated",
 +                        td[k].nx, b14only ? "1-4 " : "", tprops[tabsel[k]].name,
 +                        td[k].tabscale);
 +            }
 +        }
 +
 +        /* Set scalefactor for c6/c12 tables. This is because we save flops in the non-table kernels
 +         * by including the derivative constants (6.0 or 12.0) in the parameters, since
 +         * we no longer calculate force in most steps. This means the c6/c12 parameters
 +         * have been scaled up, so we need to scale down the table interactions too.
 +         * It comes here since we need to scale user tables too.
 +         */
 +        if (k == etiLJ6)
 +        {
 +            scalefactor = 1.0/6.0;
 +        }
 +        else if (k == etiLJ12 && tabsel[k] != etabEXPMIN)
 +        {
 +            scalefactor = 1.0/12.0;
 +        }
 +        else
 +        {
 +            scalefactor = 1.0;
 +        }
 +
 +        copy2table(table.n, k*4, 12, td[k].x, td[k].v, td[k].f, scalefactor, table.data);
 +
 +        if (bDebugMode() && bVerbose)
 +        {
 +            if (b14only)
 +            {
 +                fp = xvgropen(fns14[k], fns14[k], "r", "V", oenv);
 +            }
 +            else
 +            {
 +                fp = xvgropen(fns[k], fns[k], "r", "V", oenv);
 +            }
 +            /* plot the output 5 times denser than the table data */
 +            for (i = 5*((nx0+1)/2); i < 5*table.n; i++)
 +            {
 +                x0 = i*table.r/(5*(table.n-1));
 +                evaluate_table(table.data, 4*k, 12, table.scale, x0, &y0, &yp);
 +                fprintf(fp, "%15.10e  %15.10e  %15.10e\n", x0, y0, yp);
 +            }
 +            gmx_fio_fclose(fp);
 +        }
 +        done_tabledata(&(td[k]));
 +    }
 +    sfree(td);
 +
 +    return table;
 +}
 +
 +t_forcetable make_gb_table(const output_env_t oenv,
 +                           const t_forcerec  *fr)
 +{
 +    const char     *fns[3]   = { "gbctab.xvg", "gbdtab.xvg", "gbrtab.xvg" };
 +    const char     *fns14[3] = { "gbctab14.xvg", "gbdtab14.xvg", "gbrtab14.xvg" };
 +    FILE           *fp;
 +    t_tabledata    *td;
 +    gmx_bool        bReadTab, bGenTab;
 +    real            x0, y0, yp;
 +    int             i, j, k, nx, nx0, tabsel[etiNR];
 +    double          r, r2, Vtab, Ftab, expterm;
 +
 +    t_forcetable    table;
 +
 +    double          abs_error_r, abs_error_r2;
 +    double          rel_error_r, rel_error_r2;
 +    double          rel_error_r_old = 0, rel_error_r2_old = 0;
 +    double          x0_r_error, x0_r2_error;
 +
 +
 +    /* Only set a Coulomb table for GB */
 +    /*
 +       tabsel[0]=etabGB;
 +       tabsel[1]=-1;
 +       tabsel[2]=-1;
 +     */
 +
 +    /* Set the table dimensions for GB, not really necessary to
 +     * use etiNR (since we only have one table, but ...)
 +     */
 +    snew(td, 1);
 +    table.interaction   = GMX_TABLE_INTERACTION_ELEC;
 +    table.format        = GMX_TABLE_FORMAT_CUBICSPLINE_YFGH;
 +    table.r             = fr->gbtabr;
 +    table.scale         = fr->gbtabscale;
 +    table.scale_exp     = 0;
 +    table.n             = table.scale*table.r;
 +    table.formatsize    = 4;
 +    table.ninteractions = 1;
 +    table.stride        = table.formatsize*table.ninteractions;
 +    nx0                 = 0;
 +    nx                  = table.scale*table.r;
 +
 +    /* Check whether we have to read or generate
 +     * We will always generate a table, so remove the read code
 +     * (Compare with original make_table function
 +     */
 +    bReadTab = FALSE;
 +    bGenTab  = TRUE;
 +
 +    /* Each table type (e.g. coul,lj6,lj12) requires four
 +     * numbers per datapoint. For performance reasons we want
 +     * the table data to be aligned to 16-byte. This is accomplished
 +     * by allocating 16 bytes extra to a temporary pointer, and then
 +     * calculating an aligned pointer. This new pointer must not be
 +     * used in a free() call, but thankfully we're sloppy enough not
 +     * to do this :-)
 +     */
 +
 +    snew_aligned(table.data, 4*nx, 32);
 +
 +    init_table(nx, nx0, table.scale, &(td[0]), !bReadTab);
 +
 +    /* Local implementation so we don't have to use the etabGB
 +     * enum above, which will cause problems later when
 +     * making the other tables (right now even though we are using
 +     * GB, the normal Coulomb tables will be created, but this
 +     * will cause a problem since fr->eeltype==etabGB which will not
 +     * be defined in fill_table and set_table_type
 +     */
 +
 +    for (i = nx0; i < nx; i++)
 +    {
 +        r       = td->x[i];
 +        r2      = r*r;
 +        expterm = exp(-0.25*r2);
 +
 +        Vtab = 1/sqrt(r2+expterm);
 +        Ftab = (r-0.25*r*expterm)/((r2+expterm)*sqrt(r2+expterm));
 +
 +        /* Convert to single precision when we store to mem */
 +        td->v[i]  = Vtab;
 +        td->f[i]  = Ftab;
 +
 +    }
 +
 +    copy2table(table.n, 0, 4, td[0].x, td[0].v, td[0].f, 1.0, table.data);
 +
 +    if (bDebugMode())
 +    {
 +        fp = xvgropen(fns[0], fns[0], "r", "V", oenv);
 +        /* plot the output 5 times denser than the table data */
 +        /* for(i=5*nx0;i<5*table.n;i++) */
 +        for (i = nx0; i < table.n; i++)
 +        {
 +            /* x0=i*table.r/(5*table.n); */
 +            x0 = i*table.r/table.n;
 +            evaluate_table(table.data, 0, 4, table.scale, x0, &y0, &yp);
 +            fprintf(fp, "%15.10e  %15.10e  %15.10e\n", x0, y0, yp);
 +
 +        }
 +        gmx_fio_fclose(fp);
 +    }
 +
 +    /*
 +       for(i=100*nx0;i<99.81*table.n;i++)
 +       {
 +       r = i*table.r/(100*table.n);
 +       r2      = r*r;
 +       expterm = exp(-0.25*r2);
 +
 +       Vtab = 1/sqrt(r2+expterm);
 +       Ftab = (r-0.25*r*expterm)/((r2+expterm)*sqrt(r2+expterm));
 +
 +
 +       evaluate_table(table.data,0,4,table.scale,r,&y0,&yp);
 +       printf("gb: i=%d, x0=%g, y0=%15.15f, Vtab=%15.15f, yp=%15.15f, Ftab=%15.15f\n",i,r, y0, Vtab, yp, Ftab);
 +
 +       abs_error_r=fabs(y0-Vtab);
 +       abs_error_r2=fabs(yp-(-1)*Ftab);
 +
 +       rel_error_r=abs_error_r/y0;
 +       rel_error_r2=fabs(abs_error_r2/yp);
 +
 +
 +       if(rel_error_r>rel_error_r_old)
 +       {
 +       rel_error_r_old=rel_error_r;
 +       x0_r_error=x0;
 +       }
 +
 +       if(rel_error_r2>rel_error_r2_old)
 +       {
 +       rel_error_r2_old=rel_error_r2;
 +       x0_r2_error=x0;
 +       }
 +       }
 +
 +       printf("gb: MAX REL ERROR IN R=%15.15f, MAX REL ERROR IN R2=%15.15f\n",rel_error_r_old, rel_error_r2_old);
 +       printf("gb: XO_R=%g, X0_R2=%g\n",x0_r_error, x0_r2_error);
 +
 +       exit(1); */
 +    done_tabledata(&(td[0]));
 +    sfree(td);
 +
 +    return table;
 +
 +
 +}
 +
 +t_forcetable make_atf_table(FILE *out, const output_env_t oenv,
 +                            const t_forcerec *fr,
 +                            const char *fn,
 +                            matrix box)
 +{
 +    const char  *fns[3] = { "tf_tab.xvg", "atfdtab.xvg", "atfrtab.xvg" };
 +    FILE        *fp;
 +    t_tabledata *td;
 +    real         x0, y0, yp, rtab;
 +    int          i, nx, nx0;
 +    real         rx, ry, rz, box_r;
 +
 +    t_forcetable table;
 +
 +
 +    /* Set the table dimensions for ATF, not really necessary to
 +     * use etiNR (since we only have one table, but ...)
 +     */
 +    snew(td, 1);
 +
 +    if (fr->adress_type == eAdressSphere)
 +    {
 +        /* take half box diagonal direction as tab range */
 +        rx    = 0.5*box[0][0]+0.5*box[1][0]+0.5*box[2][0];
 +        ry    = 0.5*box[0][1]+0.5*box[1][1]+0.5*box[2][1];
 +        rz    = 0.5*box[0][2]+0.5*box[1][2]+0.5*box[2][2];
 +        box_r = sqrt(rx*rx+ry*ry+rz*rz);
 +
 +    }
 +    else
 +    {
 +        /* xsplit: take half box x direction as tab range */
 +        box_r        = box[0][0]/2;
 +    }
 +    table.r         = box_r;
 +    table.scale     = 0;
 +    table.n         = 0;
 +    table.scale_exp = 0;
 +    nx0             = 10;
 +    nx              = 0;
 +
 +    read_tables(out, fn, 1, 0, td);
 +    rtab      = td[0].x[td[0].nx-1];
 +
 +    if (fr->adress_type == eAdressXSplit && (rtab < box[0][0]/2))
 +    {
 +        gmx_fatal(FARGS, "AdResS full box therm force table in file %s extends to %f:\n"
 +                  "\tshould extend to at least half the length of the box in x-direction"
 +                  "%f\n", fn, rtab, box[0][0]/2);
 +    }
 +    if (rtab < box_r)
 +    {
 +        gmx_fatal(FARGS, "AdResS full box therm force table in file %s extends to %f:\n"
 +                  "\tshould extend to at least for spherical adress"
 +                  "%f (=distance from center to furthermost point in box \n", fn, rtab, box_r);
 +    }
 +
 +
 +    table.n     = td[0].nx;
 +    nx          = table.n;
 +    table.scale = td[0].tabscale;
 +    nx0         = td[0].nx0;
 +
 +    /* Each table type (e.g. coul,lj6,lj12) requires four
 +     * numbers per datapoint. For performance reasons we want
 +     * the table data to be aligned to 16-byte. This is accomplished
 +     * by allocating 16 bytes extra to a temporary pointer, and then
 +     * calculating an aligned pointer. This new pointer must not be
 +     * used in a free() call, but thankfully we're sloppy enough not
 +     * to do this :-)
 +     */
 +
 +    snew_aligned(table.data, 4*nx, 32);
 +
 +    copy2table(table.n, 0, 4, td[0].x, td[0].v, td[0].f, 1.0, table.data);
 +
 +    if (bDebugMode())
 +    {
 +        fp = xvgropen(fns[0], fns[0], "r", "V", oenv);
 +        /* plot the output 5 times denser than the table data */
 +        /* for(i=5*nx0;i<5*table.n;i++) */
 +
 +        for (i = 5*((nx0+1)/2); i < 5*table.n; i++)
 +        {
 +            /* x0=i*table.r/(5*table.n); */
 +            x0 = i*table.r/(5*(table.n-1));
 +            evaluate_table(table.data, 0, 4, table.scale, x0, &y0, &yp);
 +            fprintf(fp, "%15.10e  %15.10e  %15.10e\n", x0, y0, yp);
 +
 +        }
 +        gmx_ffclose(fp);
 +    }
 +
 +    done_tabledata(&(td[0]));
 +    sfree(td);
 +
 +    table.interaction   = GMX_TABLE_INTERACTION_ELEC_VDWREP_VDWDISP;
 +    table.format        = GMX_TABLE_FORMAT_CUBICSPLINE_YFGH;
 +    table.formatsize    = 4;
 +    table.ninteractions = 3;
 +    table.stride        = table.formatsize*table.ninteractions;
 +
 +
 +    return table;
 +}
 +
 +bondedtable_t make_bonded_table(FILE *fplog, char *fn, int angle)
 +{
 +    t_tabledata   td;
 +    double        start;
 +    int           i;
 +    bondedtable_t tab;
 +
 +    if (angle < 2)
 +    {
 +        start = 0;
 +    }
 +    else
 +    {
 +        start = -180.0;
 +    }
 +    read_tables(fplog, fn, 1, angle, &td);
 +    if (angle > 0)
 +    {
 +        /* Convert the table from degrees to radians */
 +        for (i = 0; i < td.nx; i++)
 +        {
 +            td.x[i] *= DEG2RAD;
 +            td.f[i] *= RAD2DEG;
 +        }
 +        td.tabscale *= RAD2DEG;
 +    }
 +    tab.n     = td.nx;
 +    tab.scale = td.tabscale;
 +    snew(tab.data, tab.n*4);
 +    copy2table(tab.n, 0, 4, td.x, td.v, td.f, 1.0, tab.data);
 +    done_tabledata(&td);
 +
 +    return tab;
 +}
index 3d6d040bd4c9dcdf68eb6e53deca46e1b7e1d61b,0000000000000000000000000000000000000000..7f5d4c8594cd4a71d17517e6f4258698a8007a6c
mode 100644,000000..100644
--- /dev/null
@@@ -1,2025 -1,0 +1,2030 @@@
-             dekindl += 0.5*(md->massB[n] - md->massA[n])*iprod(v_corrt, v_corrt);
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +
 +#include <stdio.h>
 +#include <math.h>
 +
 +#include "types/commrec.h"
 +#include "sysstuff.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "typedefs.h"
 +#include "nrnb.h"
 +#include "physics.h"
 +#include "macros.h"
 +#include "vec.h"
 +#include "main.h"
 +#include "update.h"
 +#include "gromacs/random/random.h"
 +#include "mshift.h"
 +#include "tgroup.h"
 +#include "force.h"
 +#include "names.h"
 +#include "txtdump.h"
 +#include "mdrun.h"
 +#include "constr.h"
 +#include "disre.h"
 +#include "orires.h"
 +#include "gmx_omp_nthreads.h"
 +
 +#include "gromacs/fileio/confio.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gromacs/timing/wallcycle.h"
 +#include "gromacs/utility/gmxomp.h"
 +#include "gromacs/pulling/pull.h"
 +
 +/*For debugging, start at v(-dt/2) for velolcity verlet -- uncomment next line */
 +/*#define STARTFROMDT2*/
 +
 +typedef struct {
 +    double gdt;
 +    double eph;
 +    double emh;
 +    double em;
 +    double b;
 +    double c;
 +    double d;
 +} gmx_sd_const_t;
 +
 +typedef struct {
 +    real V;
 +    real X;
 +    real Yv;
 +    real Yx;
 +} gmx_sd_sigma_t;
 +
 +typedef struct {
 +    /* BD stuff */
 +    real           *bd_rf;
 +    /* SD stuff */
 +    gmx_sd_const_t *sdc;
 +    gmx_sd_sigma_t *sdsig;
 +    rvec           *sd_V;
 +    int             sd_V_nalloc;
 +    /* andersen temperature control stuff */
 +    gmx_bool       *randomize_group;
 +    real           *boltzfac;
 +} gmx_stochd_t;
 +
 +typedef struct gmx_update
 +{
 +    gmx_stochd_t *sd;
 +    /* xprime for constraint algorithms */
 +    rvec         *xp;
 +    int           xp_nalloc;
 +
 +    /* Variables for the deform algorithm */
 +    gmx_int64_t     deformref_step;
 +    matrix          deformref_box;
 +} t_gmx_update;
 +
 +
 +static void do_update_md(int start, int nrend, double dt,
 +                         t_grp_tcstat *tcstat,
 +                         double nh_vxi[],
 +                         gmx_bool bNEMD, t_grp_acc *gstat, rvec accel[],
 +                         ivec nFreeze[],
 +                         real invmass[],
 +                         unsigned short ptype[], unsigned short cFREEZE[],
 +                         unsigned short cACC[], unsigned short cTC[],
 +                         rvec x[], rvec xprime[], rvec v[],
 +                         rvec f[], matrix M,
 +                         gmx_bool bNH, gmx_bool bPR)
 +{
 +    double imass, w_dt;
 +    int    gf = 0, ga = 0, gt = 0;
 +    rvec   vrel;
 +    real   vn, vv, va, vb, vnrel;
 +    real   lg, vxi = 0, u;
 +    int    n, d;
 +
 +    if (bNH || bPR)
 +    {
 +        /* Update with coupling to extended ensembles, used for
 +         * Nose-Hoover and Parrinello-Rahman coupling
 +         * Nose-Hoover uses the reversible leap-frog integrator from
 +         * Holian et al. Phys Rev E 52(3) : 2338, 1995
 +         */
 +        for (n = start; n < nrend; n++)
 +        {
 +            imass = invmass[n];
 +            if (cFREEZE)
 +            {
 +                gf   = cFREEZE[n];
 +            }
 +            if (cACC)
 +            {
 +                ga   = cACC[n];
 +            }
 +            if (cTC)
 +            {
 +                gt   = cTC[n];
 +            }
 +            lg   = tcstat[gt].lambda;
 +            if (bNH)
 +            {
 +                vxi   = nh_vxi[gt];
 +            }
 +            rvec_sub(v[n], gstat[ga].u, vrel);
 +
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 +                {
 +                    vnrel = (lg*vrel[d] + dt*(imass*f[n][d] - 0.5*vxi*vrel[d]
 +                                              - iprod(M[d], vrel)))/(1 + 0.5*vxi*dt);
 +                    /* do not scale the mean velocities u */
 +                    vn             = gstat[ga].u[d] + accel[ga][d]*dt + vnrel;
 +                    v[n][d]        = vn;
 +                    xprime[n][d]   = x[n][d]+vn*dt;
 +                }
 +                else
 +                {
 +                    v[n][d]        = 0.0;
 +                    xprime[n][d]   = x[n][d];
 +                }
 +            }
 +        }
 +    }
 +    else if (cFREEZE != NULL ||
 +             nFreeze[0][XX] || nFreeze[0][YY] || nFreeze[0][ZZ] ||
 +             bNEMD)
 +    {
 +        /* Update with Berendsen/v-rescale coupling and freeze or NEMD */
 +        for (n = start; n < nrend; n++)
 +        {
 +            w_dt = invmass[n]*dt;
 +            if (cFREEZE)
 +            {
 +                gf   = cFREEZE[n];
 +            }
 +            if (cACC)
 +            {
 +                ga   = cACC[n];
 +            }
 +            if (cTC)
 +            {
 +                gt   = cTC[n];
 +            }
 +            lg   = tcstat[gt].lambda;
 +
 +            for (d = 0; d < DIM; d++)
 +            {
 +                vn             = v[n][d];
 +                if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 +                {
 +                    vv             = lg*vn + f[n][d]*w_dt;
 +
 +                    /* do not scale the mean velocities u */
 +                    u              = gstat[ga].u[d];
 +                    va             = vv + accel[ga][d]*dt;
 +                    vb             = va + (1.0-lg)*u;
 +                    v[n][d]        = vb;
 +                    xprime[n][d]   = x[n][d]+vb*dt;
 +                }
 +                else
 +                {
 +                    v[n][d]        = 0.0;
 +                    xprime[n][d]   = x[n][d];
 +                }
 +            }
 +        }
 +    }
 +    else
 +    {
 +        /* Plain update with Berendsen/v-rescale coupling */
 +        for (n = start; n < nrend; n++)
 +        {
 +            if ((ptype[n] != eptVSite) && (ptype[n] != eptShell))
 +            {
 +                w_dt = invmass[n]*dt;
 +                if (cTC)
 +                {
 +                    gt = cTC[n];
 +                }
 +                lg = tcstat[gt].lambda;
 +
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    vn           = lg*v[n][d] + f[n][d]*w_dt;
 +                    v[n][d]      = vn;
 +                    xprime[n][d] = x[n][d] + vn*dt;
 +                }
 +            }
 +            else
 +            {
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    v[n][d]        = 0.0;
 +                    xprime[n][d]   = x[n][d];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static void do_update_vv_vel(int start, int nrend, double dt,
 +                             rvec accel[], ivec nFreeze[], real invmass[],
 +                             unsigned short ptype[], unsigned short cFREEZE[],
 +                             unsigned short cACC[], rvec v[], rvec f[],
 +                             gmx_bool bExtended, real veta, real alpha)
 +{
 +    double imass, w_dt;
 +    int    gf = 0, ga = 0;
 +    rvec   vrel;
 +    real   u, vn, vv, va, vb, vnrel;
 +    int    n, d;
 +    double g, mv1, mv2;
 +
 +    if (bExtended)
 +    {
 +        g        = 0.25*dt*veta*alpha;
 +        mv1      = exp(-g);
 +        mv2      = series_sinhx(g);
 +    }
 +    else
 +    {
 +        mv1      = 1.0;
 +        mv2      = 1.0;
 +    }
 +    for (n = start; n < nrend; n++)
 +    {
 +        w_dt = invmass[n]*dt;
 +        if (cFREEZE)
 +        {
 +            gf   = cFREEZE[n];
 +        }
 +        if (cACC)
 +        {
 +            ga   = cACC[n];
 +        }
 +
 +        for (d = 0; d < DIM; d++)
 +        {
 +            if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 +            {
 +                v[n][d]             = mv1*(mv1*v[n][d] + 0.5*(w_dt*mv2*f[n][d]))+0.5*accel[ga][d]*dt;
 +            }
 +            else
 +            {
 +                v[n][d]        = 0.0;
 +            }
 +        }
 +    }
 +} /* do_update_vv_vel */
 +
 +static void do_update_vv_pos(int start, int nrend, double dt,
 +                             ivec nFreeze[],
 +                             unsigned short ptype[], unsigned short cFREEZE[],
 +                             rvec x[], rvec xprime[], rvec v[],
 +                             gmx_bool bExtended, real veta)
 +{
 +    double imass, w_dt;
 +    int    gf = 0;
 +    int    n, d;
 +    double g, mr1, mr2;
 +
 +    /* Would it make more sense if Parrinello-Rahman was put here? */
 +    if (bExtended)
 +    {
 +        g        = 0.5*dt*veta;
 +        mr1      = exp(g);
 +        mr2      = series_sinhx(g);
 +    }
 +    else
 +    {
 +        mr1      = 1.0;
 +        mr2      = 1.0;
 +    }
 +
 +    for (n = start; n < nrend; n++)
 +    {
 +
 +        if (cFREEZE)
 +        {
 +            gf   = cFREEZE[n];
 +        }
 +
 +        for (d = 0; d < DIM; d++)
 +        {
 +            if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 +            {
 +                xprime[n][d]   = mr1*(mr1*x[n][d]+mr2*dt*v[n][d]);
 +            }
 +            else
 +            {
 +                xprime[n][d]   = x[n][d];
 +            }
 +        }
 +    }
 +} /* do_update_vv_pos */
 +
 +static void do_update_visc(int start, int nrend, double dt,
 +                           t_grp_tcstat *tcstat,
 +                           double nh_vxi[],
 +                           real invmass[],
 +                           unsigned short ptype[], unsigned short cTC[],
 +                           rvec x[], rvec xprime[], rvec v[],
 +                           rvec f[], matrix M, matrix box, real
 +                           cos_accel, real vcos,
 +                           gmx_bool bNH, gmx_bool bPR)
 +{
 +    double imass, w_dt;
 +    int    gt = 0;
 +    real   vn, vc;
 +    real   lg, vxi = 0, vv;
 +    real   fac, cosz;
 +    rvec   vrel;
 +    int    n, d;
 +
 +    fac = 2*M_PI/(box[ZZ][ZZ]);
 +
 +    if (bNH || bPR)
 +    {
 +        /* Update with coupling to extended ensembles, used for
 +         * Nose-Hoover and Parrinello-Rahman coupling
 +         */
 +        for (n = start; n < nrend; n++)
 +        {
 +            imass = invmass[n];
 +            if (cTC)
 +            {
 +                gt   = cTC[n];
 +            }
 +            lg   = tcstat[gt].lambda;
 +            cosz = cos(fac*x[n][ZZ]);
 +
 +            copy_rvec(v[n], vrel);
 +
 +            vc            = cosz*vcos;
 +            vrel[XX]     -= vc;
 +            if (bNH)
 +            {
 +                vxi        = nh_vxi[gt];
 +            }
 +            for (d = 0; d < DIM; d++)
 +            {
 +                vn             = v[n][d];
 +
 +                if ((ptype[n] != eptVSite) && (ptype[n] != eptShell))
 +                {
 +                    vn  = (lg*vrel[d] + dt*(imass*f[n][d] - 0.5*vxi*vrel[d]
 +                                            - iprod(M[d], vrel)))/(1 + 0.5*vxi*dt);
 +                    if (d == XX)
 +                    {
 +                        vn += vc + dt*cosz*cos_accel;
 +                    }
 +                    v[n][d]        = vn;
 +                    xprime[n][d]   = x[n][d]+vn*dt;
 +                }
 +                else
 +                {
 +                    xprime[n][d]   = x[n][d];
 +                }
 +            }
 +        }
 +    }
 +    else
 +    {
 +        /* Classic version of update, used with berendsen coupling */
 +        for (n = start; n < nrend; n++)
 +        {
 +            w_dt = invmass[n]*dt;
 +            if (cTC)
 +            {
 +                gt   = cTC[n];
 +            }
 +            lg   = tcstat[gt].lambda;
 +            cosz = cos(fac*x[n][ZZ]);
 +
 +            for (d = 0; d < DIM; d++)
 +            {
 +                vn             = v[n][d];
 +
 +                if ((ptype[n] != eptVSite) && (ptype[n] != eptShell))
 +                {
 +                    if (d == XX)
 +                    {
 +                        vc           = cosz*vcos;
 +                        /* Do not scale the cosine velocity profile */
 +                        vv           = vc + lg*(vn - vc + f[n][d]*w_dt);
 +                        /* Add the cosine accelaration profile */
 +                        vv          += dt*cosz*cos_accel;
 +                    }
 +                    else
 +                    {
 +                        vv           = lg*(vn + f[n][d]*w_dt);
 +                    }
 +                    v[n][d]        = vv;
 +                    xprime[n][d]   = x[n][d]+vv*dt;
 +                }
 +                else
 +                {
 +                    v[n][d]        = 0.0;
 +                    xprime[n][d]   = x[n][d];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static gmx_stochd_t *init_stochd(t_inputrec *ir)
 +{
 +    gmx_stochd_t   *sd;
 +    gmx_sd_const_t *sdc;
 +    int             ngtc, n, th;
 +    real            y;
 +
 +    snew(sd, 1);
 +
 +    ngtc = ir->opts.ngtc;
 +
 +    if (ir->eI == eiBD)
 +    {
 +        snew(sd->bd_rf, ngtc);
 +    }
 +    else if (EI_SD(ir->eI))
 +    {
 +        snew(sd->sdc, ngtc);
 +        snew(sd->sdsig, ngtc);
 +
 +        sdc = sd->sdc;
 +        for (n = 0; n < ngtc; n++)
 +        {
 +            if (ir->opts.tau_t[n] > 0)
 +            {
 +                sdc[n].gdt = ir->delta_t/ir->opts.tau_t[n];
 +                sdc[n].eph = exp(sdc[n].gdt/2);
 +                sdc[n].emh = exp(-sdc[n].gdt/2);
 +                sdc[n].em  = exp(-sdc[n].gdt);
 +            }
 +            else
 +            {
 +                /* No friction and noise on this group */
 +                sdc[n].gdt = 0;
 +                sdc[n].eph = 1;
 +                sdc[n].emh = 1;
 +                sdc[n].em  = 1;
 +            }
 +            if (sdc[n].gdt >= 0.05)
 +            {
 +                sdc[n].b = sdc[n].gdt*(sdc[n].eph*sdc[n].eph - 1)
 +                    - 4*(sdc[n].eph - 1)*(sdc[n].eph - 1);
 +                sdc[n].c = sdc[n].gdt - 3 + 4*sdc[n].emh - sdc[n].em;
 +                sdc[n].d = 2 - sdc[n].eph - sdc[n].emh;
 +            }
 +            else
 +            {
 +                y = sdc[n].gdt/2;
 +                /* Seventh order expansions for small y */
 +                sdc[n].b = y*y*y*y*(1/3.0+y*(1/3.0+y*(17/90.0+y*7/9.0)));
 +                sdc[n].c = y*y*y*(2/3.0+y*(-1/2.0+y*(7/30.0+y*(-1/12.0+y*31/1260.0))));
 +                sdc[n].d = y*y*(-1+y*y*(-1/12.0-y*y/360.0));
 +            }
 +            if (debug)
 +            {
 +                fprintf(debug, "SD const tc-grp %d: b %g  c %g  d %g\n",
 +                        n, sdc[n].b, sdc[n].c, sdc[n].d);
 +            }
 +        }
 +    }
 +    else if (ETC_ANDERSEN(ir->etc))
 +    {
 +        int        ngtc;
 +        t_grpopts *opts;
 +        real       reft;
 +
 +        opts = &ir->opts;
 +        ngtc = opts->ngtc;
 +
 +        snew(sd->randomize_group, ngtc);
 +        snew(sd->boltzfac, ngtc);
 +
 +        /* for now, assume that all groups, if randomized, are randomized at the same rate, i.e. tau_t is the same. */
 +        /* since constraint groups don't necessarily match up with temperature groups! This is checked in readir.c */
 +
 +        for (n = 0; n < ngtc; n++)
 +        {
 +            reft = max(0.0, opts->ref_t[n]);
 +            if ((opts->tau_t[n] > 0) && (reft > 0))  /* tau_t or ref_t = 0 means that no randomization is done */
 +            {
 +                sd->randomize_group[n] = TRUE;
 +                sd->boltzfac[n]        = BOLTZ*opts->ref_t[n];
 +            }
 +            else
 +            {
 +                sd->randomize_group[n] = FALSE;
 +            }
 +        }
 +    }
 +    return sd;
 +}
 +
 +gmx_update_t init_update(t_inputrec *ir)
 +{
 +    t_gmx_update *upd;
 +
 +    snew(upd, 1);
 +
 +    if (ir->eI == eiBD || EI_SD(ir->eI) || ir->etc == etcVRESCALE || ETC_ANDERSEN(ir->etc))
 +    {
 +        upd->sd    = init_stochd(ir);
 +    }
 +
 +    upd->xp        = NULL;
 +    upd->xp_nalloc = 0;
 +
 +    return upd;
 +}
 +
 +static void do_update_sd1(gmx_stochd_t *sd,
 +                          int start, int nrend, double dt,
 +                          rvec accel[], ivec nFreeze[],
 +                          real invmass[], unsigned short ptype[],
 +                          unsigned short cFREEZE[], unsigned short cACC[],
 +                          unsigned short cTC[],
 +                          rvec x[], rvec xprime[], rvec v[], rvec f[],
 +                          int ngtc, real tau_t[], real ref_t[],
 +                          gmx_int64_t step, int seed, int* gatindex)
 +{
 +    gmx_sd_const_t *sdc;
 +    gmx_sd_sigma_t *sig;
 +    real            kT;
 +    int             gf = 0, ga = 0, gt = 0;
 +    real            ism, sd_V;
 +    int             n, d;
 +
 +    sdc = sd->sdc;
 +    sig = sd->sdsig;
 +
 +    for (n = 0; n < ngtc; n++)
 +    {
 +        kT = BOLTZ*ref_t[n];
 +        /* The mass is encounted for later, since this differs per atom */
 +        sig[n].V  = sqrt(kT*(1 - sdc[n].em*sdc[n].em));
 +    }
 +
 +    for (n = start; n < nrend; n++)
 +    {
 +        real rnd[3];
 +        int  ng  = gatindex ? gatindex[n] : n;
 +        ism = sqrt(invmass[n]);
 +        if (cFREEZE)
 +        {
 +            gf  = cFREEZE[n];
 +        }
 +        if (cACC)
 +        {
 +            ga  = cACC[n];
 +        }
 +        if (cTC)
 +        {
 +            gt  = cTC[n];
 +        }
 +
 +        gmx_rng_cycle_3gaussian_table(step, ng, seed, RND_SEED_UPDATE, rnd);
 +        for (d = 0; d < DIM; d++)
 +        {
 +            if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 +            {
 +                sd_V = ism*sig[gt].V*rnd[d];
 +
 +                v[n][d] = v[n][d]*sdc[gt].em
 +                    + (invmass[n]*f[n][d] + accel[ga][d])*tau_t[gt]*(1 - sdc[gt].em)
 +                    + sd_V;
 +
 +                xprime[n][d] = x[n][d] + v[n][d]*dt;
 +            }
 +            else
 +            {
 +                v[n][d]      = 0.0;
 +                xprime[n][d] = x[n][d];
 +            }
 +        }
 +    }
 +}
 +
 +static void check_sd2_work_data_allocation(gmx_stochd_t *sd, int nrend)
 +{
 +    if (nrend > sd->sd_V_nalloc)
 +    {
 +        sd->sd_V_nalloc = over_alloc_dd(nrend);
 +        srenew(sd->sd_V, sd->sd_V_nalloc);
 +    }
 +}
 +
 +static void do_update_sd2_Tconsts(gmx_stochd_t *sd,
 +                                  int           ngtc,
 +                                  const real    tau_t[],
 +                                  const real    ref_t[])
 +{
 +    /* This is separated from the update below, because it is single threaded */
 +    gmx_sd_const_t *sdc;
 +    gmx_sd_sigma_t *sig;
 +    int             gt;
 +    real            kT;
 +
 +    sdc = sd->sdc;
 +    sig = sd->sdsig;
 +
 +    for (gt = 0; gt < ngtc; gt++)
 +    {
 +        kT = BOLTZ*ref_t[gt];
 +        /* The mass is encounted for later, since this differs per atom */
 +        sig[gt].V  = sqrt(kT*(1-sdc[gt].em));
 +        sig[gt].X  = sqrt(kT*sqr(tau_t[gt])*sdc[gt].c);
 +        sig[gt].Yv = sqrt(kT*sdc[gt].b/sdc[gt].c);
 +        sig[gt].Yx = sqrt(kT*sqr(tau_t[gt])*sdc[gt].b/(1-sdc[gt].em));
 +    }
 +}
 +
 +static void do_update_sd2(gmx_stochd_t *sd,
 +                          gmx_bool bInitStep,
 +                          int start, int nrend,
 +                          rvec accel[], ivec nFreeze[],
 +                          real invmass[], unsigned short ptype[],
 +                          unsigned short cFREEZE[], unsigned short cACC[],
 +                          unsigned short cTC[],
 +                          rvec x[], rvec xprime[], rvec v[], rvec f[],
 +                          rvec sd_X[],
 +                          const real tau_t[],
 +                          gmx_bool bFirstHalf, gmx_int64_t step, int seed,
 +                          int* gatindex)
 +{
 +    gmx_sd_const_t *sdc;
 +    gmx_sd_sigma_t *sig;
 +    /* The random part of the velocity update, generated in the first
 +     * half of the update, needs to be remembered for the second half.
 +     */
 +    rvec  *sd_V;
 +    real   kT;
 +    int    gf = 0, ga = 0, gt = 0;
 +    real   vn = 0, Vmh, Xmh;
 +    real   ism;
 +    int    n, d, ng;
 +
 +    sdc  = sd->sdc;
 +    sig  = sd->sdsig;
 +    sd_V = sd->sd_V;
 +
 +    for (n = start; n < nrend; n++)
 +    {
 +        real rnd[6], rndi[3];
 +        ng  = gatindex ? gatindex[n] : n;
 +        ism = sqrt(invmass[n]);
 +        if (cFREEZE)
 +        {
 +            gf  = cFREEZE[n];
 +        }
 +        if (cACC)
 +        {
 +            ga  = cACC[n];
 +        }
 +        if (cTC)
 +        {
 +            gt  = cTC[n];
 +        }
 +
 +        gmx_rng_cycle_6gaussian_table(step*2+(bFirstHalf ? 1 : 2), ng, seed, RND_SEED_UPDATE, rnd);
 +        if (bInitStep)
 +        {
 +            gmx_rng_cycle_3gaussian_table(step*2, ng, seed, RND_SEED_UPDATE, rndi);
 +        }
 +        for (d = 0; d < DIM; d++)
 +        {
 +            if (bFirstHalf)
 +            {
 +                vn             = v[n][d];
 +            }
 +            if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 +            {
 +                if (bFirstHalf)
 +                {
 +                    if (bInitStep)
 +                    {
 +                        sd_X[n][d] = ism*sig[gt].X*rndi[d];
 +                    }
 +                    Vmh = sd_X[n][d]*sdc[gt].d/(tau_t[gt]*sdc[gt].c)
 +                        + ism*sig[gt].Yv*rnd[d*2];
 +                    sd_V[n][d] = ism*sig[gt].V*rnd[d*2+1];
 +
 +                    v[n][d] = vn*sdc[gt].em
 +                        + (invmass[n]*f[n][d] + accel[ga][d])*tau_t[gt]*(1 - sdc[gt].em)
 +                        + sd_V[n][d] - sdc[gt].em*Vmh;
 +
 +                    xprime[n][d] = x[n][d] + v[n][d]*tau_t[gt]*(sdc[gt].eph - sdc[gt].emh);
 +                }
 +                else
 +                {
 +                    /* Correct the velocities for the constraints.
 +                     * This operation introduces some inaccuracy,
 +                     * since the velocity is determined from differences in coordinates.
 +                     */
 +                    v[n][d] =
 +                        (xprime[n][d] - x[n][d])/(tau_t[gt]*(sdc[gt].eph - sdc[gt].emh));
 +
 +                    Xmh = sd_V[n][d]*tau_t[gt]*sdc[gt].d/(sdc[gt].em-1)
 +                        + ism*sig[gt].Yx*rnd[d*2];
 +                    sd_X[n][d] = ism*sig[gt].X*rnd[d*2+1];
 +
 +                    xprime[n][d] += sd_X[n][d] - Xmh;
 +
 +                }
 +            }
 +            else
 +            {
 +                if (bFirstHalf)
 +                {
 +                    v[n][d]        = 0.0;
 +                    xprime[n][d]   = x[n][d];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static void do_update_bd_Tconsts(double dt, real friction_coefficient,
 +                                 int ngtc, const real ref_t[],
 +                                 real *rf)
 +{
 +    /* This is separated from the update below, because it is single threaded */
 +    int gt;
 +
 +    if (friction_coefficient != 0)
 +    {
 +        for (gt = 0; gt < ngtc; gt++)
 +        {
 +            rf[gt] = sqrt(2.0*BOLTZ*ref_t[gt]/(friction_coefficient*dt));
 +        }
 +    }
 +    else
 +    {
 +        for (gt = 0; gt < ngtc; gt++)
 +        {
 +            rf[gt] = sqrt(2.0*BOLTZ*ref_t[gt]);
 +        }
 +    }
 +}
 +
 +static void do_update_bd(int start, int nrend, double dt,
 +                         ivec nFreeze[],
 +                         real invmass[], unsigned short ptype[],
 +                         unsigned short cFREEZE[], unsigned short cTC[],
 +                         rvec x[], rvec xprime[], rvec v[],
 +                         rvec f[], real friction_coefficient,
 +                         real *rf, gmx_int64_t step, int seed,
 +                         int* gatindex)
 +{
 +    /* note -- these appear to be full step velocities . . .  */
 +    int    gf = 0, gt = 0;
 +    real   vn;
 +    real   invfr = 0;
 +    int    n, d;
 +
 +    if (friction_coefficient != 0)
 +    {
 +        invfr = 1.0/friction_coefficient;
 +    }
 +
 +    for (n = start; (n < nrend); n++)
 +    {
 +        real rnd[3];
 +        int  ng  = gatindex ? gatindex[n] : n;
 +
 +        if (cFREEZE)
 +        {
 +            gf = cFREEZE[n];
 +        }
 +        if (cTC)
 +        {
 +            gt = cTC[n];
 +        }
 +        gmx_rng_cycle_3gaussian_table(step, ng, seed, RND_SEED_UPDATE, rnd);
 +        for (d = 0; (d < DIM); d++)
 +        {
 +            if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 +            {
 +                if (friction_coefficient != 0)
 +                {
 +                    vn = invfr*f[n][d] + rf[gt]*rnd[d];
 +                }
 +                else
 +                {
 +                    /* NOTE: invmass = 2/(mass*friction_constant*dt) */
 +                    vn = 0.5*invmass[n]*f[n][d]*dt
 +                        + sqrt(0.5*invmass[n])*rf[gt]*rnd[d];
 +                }
 +
 +                v[n][d]      = vn;
 +                xprime[n][d] = x[n][d]+vn*dt;
 +            }
 +            else
 +            {
 +                v[n][d]      = 0.0;
 +                xprime[n][d] = x[n][d];
 +            }
 +        }
 +    }
 +}
 +
 +static void dump_it_all(FILE gmx_unused *fp, const char gmx_unused *title,
 +                        int gmx_unused natoms, rvec gmx_unused x[], rvec gmx_unused xp[],
 +                        rvec gmx_unused v[], rvec gmx_unused f[])
 +{
 +#ifdef DEBUG
 +    if (fp)
 +    {
 +        fprintf(fp, "%s\n", title);
 +        pr_rvecs(fp, 0, "x", x, natoms);
 +        pr_rvecs(fp, 0, "xp", xp, natoms);
 +        pr_rvecs(fp, 0, "v", v, natoms);
 +        pr_rvecs(fp, 0, "f", f, natoms);
 +    }
 +#endif
 +}
 +
 +static void calc_ke_part_normal(rvec v[], t_grpopts *opts, t_mdatoms *md,
 +                                gmx_ekindata_t *ekind, t_nrnb *nrnb, gmx_bool bEkinAveVel,
 +                                gmx_bool bSaveEkinOld)
 +{
 +    int           g;
 +    t_grp_tcstat *tcstat  = ekind->tcstat;
 +    t_grp_acc    *grpstat = ekind->grpstat;
 +    int           nthread, thread;
 +
 +    /* three main: VV with AveVel, vv with AveEkin, leap with AveEkin.  Leap with AveVel is also
 +       an option, but not supported now.  Additionally, if we are doing iterations.
 +       bEkinAveVel: If TRUE, we sum into ekin, if FALSE, into ekinh.
 +       bSavEkinOld: If TRUE (in the case of iteration = bIterate is TRUE), we don't copy over the ekinh_old.
 +       If FALSE, we overrwrite it.
 +     */
 +
 +    /* group velocities are calculated in update_ekindata and
 +     * accumulated in acumulate_groups.
 +     * Now the partial global and groups ekin.
 +     */
 +    for (g = 0; (g < opts->ngtc); g++)
 +    {
 +
 +        if (!bSaveEkinOld)
 +        {
 +            copy_mat(tcstat[g].ekinh, tcstat[g].ekinh_old);
 +        }
 +        if (bEkinAveVel)
 +        {
 +            clear_mat(tcstat[g].ekinf);
 +        }
 +        else
 +        {
 +            clear_mat(tcstat[g].ekinh);
 +        }
 +        if (bEkinAveVel)
 +        {
 +            tcstat[g].ekinscalef_nhc = 1.0; /* need to clear this -- logic is complicated! */
 +        }
 +    }
 +    ekind->dekindl_old = ekind->dekindl;
 +
 +    nthread = gmx_omp_nthreads_get(emntUpdate);
 +
 +#pragma omp parallel for num_threads(nthread) schedule(static)
 +    for (thread = 0; thread < nthread; thread++)
 +    {
 +        int     start_t, end_t, n;
 +        int     ga, gt;
 +        rvec    v_corrt;
 +        real    hm;
 +        int     d, m;
 +        matrix *ekin_sum;
 +        real   *dekindl_sum;
 +
 +        start_t = ((thread+0)*md->homenr)/nthread;
 +        end_t   = ((thread+1)*md->homenr)/nthread;
 +
 +        ekin_sum    = ekind->ekin_work[thread];
 +        dekindl_sum = ekind->dekindl_work[thread];
 +
 +        for (gt = 0; gt < opts->ngtc; gt++)
 +        {
 +            clear_mat(ekin_sum[gt]);
 +        }
 +        *dekindl_sum = 0.0;
 +
 +        ga = 0;
 +        gt = 0;
 +        for (n = start_t; n < end_t; n++)
 +        {
 +            if (md->cACC)
 +            {
 +                ga = md->cACC[n];
 +            }
 +            if (md->cTC)
 +            {
 +                gt = md->cTC[n];
 +            }
 +            hm   = 0.5*md->massT[n];
 +
 +            for (d = 0; (d < DIM); d++)
 +            {
 +                v_corrt[d]  = v[n][d]  - grpstat[ga].u[d];
 +            }
 +            for (d = 0; (d < DIM); d++)
 +            {
 +                for (m = 0; (m < DIM); m++)
 +                {
 +                    /* if we're computing a full step velocity, v_corrt[d] has v(t).  Otherwise, v(t+dt/2) */
 +                    ekin_sum[gt][m][d] += hm*v_corrt[m]*v_corrt[d];
 +                }
 +            }
 +            if (md->nMassPerturbed && md->bPerturbed[n])
 +            {
 +                *dekindl_sum +=
 +                    0.5*(md->massB[n] - md->massA[n])*iprod(v_corrt, v_corrt);
 +            }
 +        }
 +    }
 +
 +    ekind->dekindl = 0;
 +    for (thread = 0; thread < nthread; thread++)
 +    {
 +        for (g = 0; g < opts->ngtc; g++)
 +        {
 +            if (bEkinAveVel)
 +            {
 +                m_add(tcstat[g].ekinf, ekind->ekin_work[thread][g],
 +                      tcstat[g].ekinf);
 +            }
 +            else
 +            {
 +                m_add(tcstat[g].ekinh, ekind->ekin_work[thread][g],
 +                      tcstat[g].ekinh);
 +            }
 +        }
 +
 +        ekind->dekindl += *ekind->dekindl_work[thread];
 +    }
 +
 +    inc_nrnb(nrnb, eNR_EKIN, md->homenr);
 +}
 +
 +static void calc_ke_part_visc(matrix box, rvec x[], rvec v[],
 +                              t_grpopts *opts, t_mdatoms *md,
 +                              gmx_ekindata_t *ekind,
 +                              t_nrnb *nrnb, gmx_bool bEkinAveVel)
 +{
 +    int           start = 0, homenr = md->homenr;
 +    int           g, d, n, m, gt = 0;
 +    rvec          v_corrt;
 +    real          hm;
 +    t_grp_tcstat *tcstat = ekind->tcstat;
 +    t_cos_acc    *cosacc = &(ekind->cosacc);
 +    real          dekindl;
 +    real          fac, cosz;
 +    double        mvcos;
 +
 +    for (g = 0; g < opts->ngtc; g++)
 +    {
 +        copy_mat(ekind->tcstat[g].ekinh, ekind->tcstat[g].ekinh_old);
 +        clear_mat(ekind->tcstat[g].ekinh);
 +    }
 +    ekind->dekindl_old = ekind->dekindl;
 +
 +    fac     = 2*M_PI/box[ZZ][ZZ];
 +    mvcos   = 0;
 +    dekindl = 0;
 +    for (n = start; n < start+homenr; n++)
 +    {
 +        if (md->cTC)
 +        {
 +            gt = md->cTC[n];
 +        }
 +        hm   = 0.5*md->massT[n];
 +
 +        /* Note that the times of x and v differ by half a step */
 +        /* MRS -- would have to be changed for VV */
 +        cosz         = cos(fac*x[n][ZZ]);
 +        /* Calculate the amplitude of the new velocity profile */
 +        mvcos       += 2*cosz*md->massT[n]*v[n][XX];
 +
 +        copy_rvec(v[n], v_corrt);
 +        /* Subtract the profile for the kinetic energy */
 +        v_corrt[XX] -= cosz*cosacc->vcos;
 +        for (d = 0; (d < DIM); d++)
 +        {
 +            for (m = 0; (m < DIM); m++)
 +            {
 +                /* if we're computing a full step velocity, v_corrt[d] has v(t).  Otherwise, v(t+dt/2) */
 +                if (bEkinAveVel)
 +                {
 +                    tcstat[gt].ekinf[m][d] += hm*v_corrt[m]*v_corrt[d];
 +                }
 +                else
 +                {
 +                    tcstat[gt].ekinh[m][d] += hm*v_corrt[m]*v_corrt[d];
 +                }
 +            }
 +        }
 +        if (md->nPerturbed && md->bPerturbed[n])
 +        {
++            /* The minus sign here might be confusing.
++             * The kinetic contribution from dH/dl doesn't come from
++             * d m(l)/2 v^2 / dl, but rather from d p^2/2m(l) / dl,
++             * where p are the momenta. The difference is only a minus sign.
++             */
++            dekindl -= 0.5*(md->massB[n] - md->massA[n])*iprod(v_corrt, v_corrt);
 +        }
 +    }
 +    ekind->dekindl = dekindl;
 +    cosacc->mvcos  = mvcos;
 +
 +    inc_nrnb(nrnb, eNR_EKIN, homenr);
 +}
 +
 +void calc_ke_part(t_state *state, t_grpopts *opts, t_mdatoms *md,
 +                  gmx_ekindata_t *ekind, t_nrnb *nrnb, gmx_bool bEkinAveVel, gmx_bool bSaveEkinOld)
 +{
 +    if (ekind->cosacc.cos_accel == 0)
 +    {
 +        calc_ke_part_normal(state->v, opts, md, ekind, nrnb, bEkinAveVel, bSaveEkinOld);
 +    }
 +    else
 +    {
 +        calc_ke_part_visc(state->box, state->x, state->v, opts, md, ekind, nrnb, bEkinAveVel);
 +    }
 +}
 +
 +extern void init_ekinstate(ekinstate_t *ekinstate, const t_inputrec *ir)
 +{
 +    ekinstate->ekin_n = ir->opts.ngtc;
 +    snew(ekinstate->ekinh, ekinstate->ekin_n);
 +    snew(ekinstate->ekinf, ekinstate->ekin_n);
 +    snew(ekinstate->ekinh_old, ekinstate->ekin_n);
 +    snew(ekinstate->ekinscalef_nhc, ekinstate->ekin_n);
 +    snew(ekinstate->ekinscaleh_nhc, ekinstate->ekin_n);
 +    snew(ekinstate->vscale_nhc, ekinstate->ekin_n);
 +    ekinstate->dekindl = 0;
 +    ekinstate->mvcos   = 0;
 +}
 +
 +void update_ekinstate(ekinstate_t *ekinstate, gmx_ekindata_t *ekind)
 +{
 +    int i;
 +
 +    for (i = 0; i < ekinstate->ekin_n; i++)
 +    {
 +        copy_mat(ekind->tcstat[i].ekinh, ekinstate->ekinh[i]);
 +        copy_mat(ekind->tcstat[i].ekinf, ekinstate->ekinf[i]);
 +        copy_mat(ekind->tcstat[i].ekinh_old, ekinstate->ekinh_old[i]);
 +        ekinstate->ekinscalef_nhc[i] = ekind->tcstat[i].ekinscalef_nhc;
 +        ekinstate->ekinscaleh_nhc[i] = ekind->tcstat[i].ekinscaleh_nhc;
 +        ekinstate->vscale_nhc[i]     = ekind->tcstat[i].vscale_nhc;
 +    }
 +
 +    copy_mat(ekind->ekin, ekinstate->ekin_total);
 +    ekinstate->dekindl = ekind->dekindl;
 +    ekinstate->mvcos   = ekind->cosacc.mvcos;
 +
 +}
 +
 +void restore_ekinstate_from_state(t_commrec *cr,
 +                                  gmx_ekindata_t *ekind, ekinstate_t *ekinstate)
 +{
 +    int i, n;
 +
 +    if (MASTER(cr))
 +    {
 +        for (i = 0; i < ekinstate->ekin_n; i++)
 +        {
 +            copy_mat(ekinstate->ekinh[i], ekind->tcstat[i].ekinh);
 +            copy_mat(ekinstate->ekinf[i], ekind->tcstat[i].ekinf);
 +            copy_mat(ekinstate->ekinh_old[i], ekind->tcstat[i].ekinh_old);
 +            ekind->tcstat[i].ekinscalef_nhc = ekinstate->ekinscalef_nhc[i];
 +            ekind->tcstat[i].ekinscaleh_nhc = ekinstate->ekinscaleh_nhc[i];
 +            ekind->tcstat[i].vscale_nhc     = ekinstate->vscale_nhc[i];
 +        }
 +
 +        copy_mat(ekinstate->ekin_total, ekind->ekin);
 +
 +        ekind->dekindl      = ekinstate->dekindl;
 +        ekind->cosacc.mvcos = ekinstate->mvcos;
 +        n                   = ekinstate->ekin_n;
 +    }
 +
 +    if (PAR(cr))
 +    {
 +        gmx_bcast(sizeof(n), &n, cr);
 +        for (i = 0; i < n; i++)
 +        {
 +            gmx_bcast(DIM*DIM*sizeof(ekind->tcstat[i].ekinh[0][0]),
 +                      ekind->tcstat[i].ekinh[0], cr);
 +            gmx_bcast(DIM*DIM*sizeof(ekind->tcstat[i].ekinf[0][0]),
 +                      ekind->tcstat[i].ekinf[0], cr);
 +            gmx_bcast(DIM*DIM*sizeof(ekind->tcstat[i].ekinh_old[0][0]),
 +                      ekind->tcstat[i].ekinh_old[0], cr);
 +
 +            gmx_bcast(sizeof(ekind->tcstat[i].ekinscalef_nhc),
 +                      &(ekind->tcstat[i].ekinscalef_nhc), cr);
 +            gmx_bcast(sizeof(ekind->tcstat[i].ekinscaleh_nhc),
 +                      &(ekind->tcstat[i].ekinscaleh_nhc), cr);
 +            gmx_bcast(sizeof(ekind->tcstat[i].vscale_nhc),
 +                      &(ekind->tcstat[i].vscale_nhc), cr);
 +        }
 +        gmx_bcast(DIM*DIM*sizeof(ekind->ekin[0][0]),
 +                  ekind->ekin[0], cr);
 +
 +        gmx_bcast(sizeof(ekind->dekindl), &ekind->dekindl, cr);
 +        gmx_bcast(sizeof(ekind->cosacc.mvcos), &ekind->cosacc.mvcos, cr);
 +    }
 +}
 +
 +void set_deform_reference_box(gmx_update_t upd, gmx_int64_t step, matrix box)
 +{
 +    upd->deformref_step = step;
 +    copy_mat(box, upd->deformref_box);
 +}
 +
 +static void deform(gmx_update_t upd,
 +                   int start, int homenr, rvec x[], matrix box, matrix *scale_tot,
 +                   const t_inputrec *ir, gmx_int64_t step)
 +{
 +    matrix bnew, invbox, mu;
 +    real   elapsed_time;
 +    int    i, j;
 +
 +    elapsed_time = (step + 1 - upd->deformref_step)*ir->delta_t;
 +    copy_mat(box, bnew);
 +    for (i = 0; i < DIM; i++)
 +    {
 +        for (j = 0; j < DIM; j++)
 +        {
 +            if (ir->deform[i][j] != 0)
 +            {
 +                bnew[i][j] =
 +                    upd->deformref_box[i][j] + elapsed_time*ir->deform[i][j];
 +            }
 +        }
 +    }
 +    /* We correct the off-diagonal elements,
 +     * which can grow indefinitely during shearing,
 +     * so the shifts do not get messed up.
 +     */
 +    for (i = 1; i < DIM; i++)
 +    {
 +        for (j = i-1; j >= 0; j--)
 +        {
 +            while (bnew[i][j] - box[i][j] > 0.5*bnew[j][j])
 +            {
 +                rvec_dec(bnew[i], bnew[j]);
 +            }
 +            while (bnew[i][j] - box[i][j] < -0.5*bnew[j][j])
 +            {
 +                rvec_inc(bnew[i], bnew[j]);
 +            }
 +        }
 +    }
 +    m_inv_ur0(box, invbox);
 +    copy_mat(bnew, box);
 +    mmul_ur0(box, invbox, mu);
 +
 +    for (i = start; i < start+homenr; i++)
 +    {
 +        x[i][XX] = mu[XX][XX]*x[i][XX]+mu[YY][XX]*x[i][YY]+mu[ZZ][XX]*x[i][ZZ];
 +        x[i][YY] = mu[YY][YY]*x[i][YY]+mu[ZZ][YY]*x[i][ZZ];
 +        x[i][ZZ] = mu[ZZ][ZZ]*x[i][ZZ];
 +    }
 +    if (*scale_tot)
 +    {
 +        /* The transposes of the scaling matrices are stored,
 +         * so we need to do matrix multiplication in the inverse order.
 +         */
 +        mmul_ur0(*scale_tot, mu, *scale_tot);
 +    }
 +}
 +
 +void update_tcouple(gmx_int64_t       step,
 +                    t_inputrec       *inputrec,
 +                    t_state          *state,
 +                    gmx_ekindata_t   *ekind,
 +                    t_extmass        *MassQ,
 +                    t_mdatoms        *md)
 +
 +{
 +    gmx_bool   bTCouple = FALSE;
 +    real       dttc;
 +    int        i, start, end, homenr, offset;
 +
 +    /* if using vv with trotter decomposition methods, we do this elsewhere in the code */
 +    if (inputrec->etc != etcNO &&
 +        !(IR_NVT_TROTTER(inputrec) || IR_NPT_TROTTER(inputrec) || IR_NPH_TROTTER(inputrec)))
 +    {
 +        /* We should only couple after a step where energies were determined (for leapfrog versions)
 +           or the step energies are determined, for velocity verlet versions */
 +
 +        if (EI_VV(inputrec->eI))
 +        {
 +            offset = 0;
 +        }
 +        else
 +        {
 +            offset = 1;
 +        }
 +        bTCouple = (inputrec->nsttcouple == 1 ||
 +                    do_per_step(step+inputrec->nsttcouple-offset,
 +                                inputrec->nsttcouple));
 +    }
 +
 +    if (bTCouple)
 +    {
 +        dttc = inputrec->nsttcouple*inputrec->delta_t;
 +
 +        switch (inputrec->etc)
 +        {
 +            case etcNO:
 +                break;
 +            case etcBERENDSEN:
 +                berendsen_tcoupl(inputrec, ekind, dttc);
 +                break;
 +            case etcNOSEHOOVER:
 +                nosehoover_tcoupl(&(inputrec->opts), ekind, dttc,
 +                                  state->nosehoover_xi, state->nosehoover_vxi, MassQ);
 +                break;
 +            case etcVRESCALE:
 +                vrescale_tcoupl(inputrec, step, ekind, dttc,
 +                                state->therm_integral);
 +                break;
 +        }
 +        /* rescale in place here */
 +        if (EI_VV(inputrec->eI))
 +        {
 +            rescale_velocities(ekind, md, 0, md->homenr, state->v);
 +        }
 +    }
 +    else
 +    {
 +        /* Set the T scaling lambda to 1 to have no scaling */
 +        for (i = 0; (i < inputrec->opts.ngtc); i++)
 +        {
 +            ekind->tcstat[i].lambda = 1.0;
 +        }
 +    }
 +}
 +
 +void update_pcouple(FILE             *fplog,
 +                    gmx_int64_t       step,
 +                    t_inputrec       *inputrec,
 +                    t_state          *state,
 +                    matrix            pcoupl_mu,
 +                    matrix            M,
 +                    gmx_bool          bInitStep)
 +{
 +    gmx_bool   bPCouple = FALSE;
 +    real       dtpc     = 0;
 +    int        i;
 +
 +    /* if using Trotter pressure, we do this in coupling.c, so we leave it false. */
 +    if (inputrec->epc != epcNO && (!(IR_NPT_TROTTER(inputrec) || IR_NPH_TROTTER(inputrec))))
 +    {
 +        /* We should only couple after a step where energies were determined */
 +        bPCouple = (inputrec->nstpcouple == 1 ||
 +                    do_per_step(step+inputrec->nstpcouple-1,
 +                                inputrec->nstpcouple));
 +    }
 +
 +    clear_mat(pcoupl_mu);
 +    for (i = 0; i < DIM; i++)
 +    {
 +        pcoupl_mu[i][i] = 1.0;
 +    }
 +
 +    clear_mat(M);
 +
 +    if (bPCouple)
 +    {
 +        dtpc = inputrec->nstpcouple*inputrec->delta_t;
 +
 +        switch (inputrec->epc)
 +        {
 +            /* We can always pcoupl, even if we did not sum the energies
 +             * the previous step, since state->pres_prev is only updated
 +             * when the energies have been summed.
 +             */
 +            case (epcNO):
 +                break;
 +            case (epcBERENDSEN):
 +                if (!bInitStep)
 +                {
 +                    berendsen_pcoupl(fplog, step, inputrec, dtpc, state->pres_prev, state->box,
 +                                     pcoupl_mu);
 +                }
 +                break;
 +            case (epcPARRINELLORAHMAN):
 +                parrinellorahman_pcoupl(fplog, step, inputrec, dtpc, state->pres_prev,
 +                                        state->box, state->box_rel, state->boxv,
 +                                        M, pcoupl_mu, bInitStep);
 +                break;
 +            default:
 +                break;
 +        }
 +    }
 +}
 +
 +static rvec *get_xprime(const t_state *state, gmx_update_t upd)
 +{
 +    if (state->nalloc > upd->xp_nalloc)
 +    {
 +        upd->xp_nalloc = state->nalloc;
 +        srenew(upd->xp, upd->xp_nalloc);
 +    }
 +
 +    return upd->xp;
 +}
 +
 +static void combine_forces(gmx_update_t upd,
 +                           int nstcalclr,
 +                           gmx_constr_t constr,
 +                           t_inputrec *ir, t_mdatoms *md, t_idef *idef,
 +                           t_commrec *cr,
 +                           gmx_int64_t step,
 +                           t_state *state, gmx_bool bMolPBC,
 +                           int start, int nrend,
 +                           rvec f[], rvec f_lr[],
 +                           tensor *vir_lr_constr,
 +                           t_nrnb *nrnb)
 +{
 +    int  i, d;
 +
 +    /* f contains the short-range forces + the long range forces
 +     * which are stored separately in f_lr.
 +     */
 +
 +    if (constr != NULL && vir_lr_constr != NULL &&
 +        !(ir->eConstrAlg == econtSHAKE && ir->epc == epcNO))
 +    {
 +        /* We need to constrain the LR forces separately,
 +         * because due to the different pre-factor for the SR and LR
 +         * forces in the update algorithm, we have to correct
 +         * the constraint virial for the nstcalclr-1 extra f_lr.
 +         * Constrain only the additional LR part of the force.
 +         */
 +        /* MRS -- need to make sure this works with trotter integration -- the constraint calls may not be right.*/
 +        rvec *xp;
 +        real  fac;
 +        int   gf = 0;
 +
 +        xp  = get_xprime(state, upd);
 +
 +        fac = (nstcalclr - 1)*ir->delta_t*ir->delta_t;
 +
 +        for (i = 0; i < md->homenr; i++)
 +        {
 +            if (md->cFREEZE != NULL)
 +            {
 +                gf = md->cFREEZE[i];
 +            }
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if ((md->ptype[i] != eptVSite) &&
 +                    (md->ptype[i] != eptShell) &&
 +                    !ir->opts.nFreeze[gf][d])
 +                {
 +                    xp[i][d] = state->x[i][d] + fac*f_lr[i][d]*md->invmass[i];
 +                }
 +            }
 +        }
 +        constrain(NULL, FALSE, FALSE, constr, idef, ir, NULL, cr, step, 0, md,
 +                  state->x, xp, xp, bMolPBC, state->box, state->lambda[efptBONDED], NULL,
 +                  NULL, vir_lr_constr, nrnb, econqCoord, ir->epc == epcMTTK, state->veta, state->veta);
 +    }
 +
 +    /* Add nstcalclr-1 times the LR force to the sum of both forces
 +     * and store the result in forces_lr.
 +     */
 +    for (i = start; i < nrend; i++)
 +    {
 +        for (d = 0; d < DIM; d++)
 +        {
 +            f_lr[i][d] = f[i][d] + (nstcalclr - 1)*f_lr[i][d];
 +        }
 +    }
 +}
 +
 +void update_constraints(FILE             *fplog,
 +                        gmx_int64_t       step,
 +                        real             *dvdlambda, /* the contribution to be added to the bonded interactions */
 +                        t_inputrec       *inputrec,  /* input record and box stuff    */
 +                        gmx_ekindata_t   *ekind,
 +                        t_mdatoms        *md,
 +                        t_state          *state,
 +                        gmx_bool          bMolPBC,
 +                        t_graph          *graph,
 +                        rvec              force[],   /* forces on home particles */
 +                        t_idef           *idef,
 +                        tensor            vir_part,
 +                        t_commrec        *cr,
 +                        t_nrnb           *nrnb,
 +                        gmx_wallcycle_t   wcycle,
 +                        gmx_update_t      upd,
 +                        gmx_constr_t      constr,
 +                        gmx_bool          bFirstHalf,
 +                        gmx_bool          bCalcVir,
 +                        real              vetanew)
 +{
 +    gmx_bool             bExtended, bLastStep, bLog = FALSE, bEner = FALSE, bDoConstr = FALSE;
 +    double               dt;
 +    real                 dt_1;
 +    int                  start, homenr, nrend, i, n, m, g, d;
 +    tensor               vir_con;
 +    rvec                *vbuf, *xprime = NULL;
 +    int                  nth, th;
 +
 +    if (constr)
 +    {
 +        bDoConstr = TRUE;
 +    }
 +    if (bFirstHalf && !EI_VV(inputrec->eI))
 +    {
 +        bDoConstr = FALSE;
 +    }
 +
 +    /* for now, SD update is here -- though it really seems like it
 +       should be reformulated as a velocity verlet method, since it has two parts */
 +
 +    start  = 0;
 +    homenr = md->homenr;
 +    nrend  = start+homenr;
 +
 +    dt   = inputrec->delta_t;
 +    dt_1 = 1.0/dt;
 +
 +    /*
 +     *  Steps (7C, 8C)
 +     *  APPLY CONSTRAINTS:
 +     *  BLOCK SHAKE
 +
 +     * When doing PR pressure coupling we have to constrain the
 +     * bonds in each iteration. If we are only using Nose-Hoover tcoupling
 +     * it is enough to do this once though, since the relative velocities
 +     * after this will be normal to the bond vector
 +     */
 +
 +    if (bDoConstr)
 +    {
 +        /* clear out constraints before applying */
 +        clear_mat(vir_part);
 +
 +        xprime = get_xprime(state, upd);
 +
 +        bLastStep = (step == inputrec->init_step+inputrec->nsteps);
 +        bLog      = (do_per_step(step, inputrec->nstlog) || bLastStep || (step < 0));
 +        bEner     = (do_per_step(step, inputrec->nstenergy) || bLastStep);
 +        /* Constrain the coordinates xprime */
 +        wallcycle_start(wcycle, ewcCONSTR);
 +        if (EI_VV(inputrec->eI) && bFirstHalf)
 +        {
 +            constrain(NULL, bLog, bEner, constr, idef,
 +                      inputrec, ekind, cr, step, 1, md,
 +                      state->x, state->v, state->v,
 +                      bMolPBC, state->box,
 +                      state->lambda[efptBONDED], dvdlambda,
 +                      NULL, bCalcVir ? &vir_con : NULL, nrnb, econqVeloc,
 +                      inputrec->epc == epcMTTK, state->veta, vetanew);
 +        }
 +        else
 +        {
 +            constrain(NULL, bLog, bEner, constr, idef,
 +                      inputrec, ekind, cr, step, 1, md,
 +                      state->x, xprime, NULL,
 +                      bMolPBC, state->box,
 +                      state->lambda[efptBONDED], dvdlambda,
 +                      state->v, bCalcVir ? &vir_con : NULL, nrnb, econqCoord,
 +                      inputrec->epc == epcMTTK, state->veta, state->veta);
 +        }
 +        wallcycle_stop(wcycle, ewcCONSTR);
 +
 +        where();
 +
 +        dump_it_all(fplog, "After Shake",
 +                    state->natoms, state->x, xprime, state->v, force);
 +
 +        if (bCalcVir)
 +        {
 +            if (inputrec->eI == eiSD2)
 +            {
 +                /* A correction factor eph is needed for the SD constraint force */
 +                /* Here we can, unfortunately, not have proper corrections
 +                 * for different friction constants, so we use the first one.
 +                 */
 +                for (i = 0; i < DIM; i++)
 +                {
 +                    for (m = 0; m < DIM; m++)
 +                    {
 +                        vir_part[i][m] += upd->sd->sdc[0].eph*vir_con[i][m];
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                m_add(vir_part, vir_con, vir_part);
 +            }
 +            if (debug)
 +            {
 +                pr_rvecs(debug, 0, "constraint virial", vir_part, DIM);
 +            }
 +        }
 +    }
 +
 +    where();
 +    if ((inputrec->eI == eiSD2) && !(bFirstHalf))
 +    {
 +        xprime = get_xprime(state, upd);
 +
 +        nth = gmx_omp_nthreads_get(emntUpdate);
 +
 +#pragma omp parallel for num_threads(nth) schedule(static)
 +        for (th = 0; th < nth; th++)
 +        {
 +            int start_th, end_th;
 +
 +            start_th = start + ((nrend-start)* th   )/nth;
 +            end_th   = start + ((nrend-start)*(th+1))/nth;
 +
 +            /* The second part of the SD integration */
 +            do_update_sd2(upd->sd,
 +                          FALSE, start_th, end_th,
 +                          inputrec->opts.acc, inputrec->opts.nFreeze,
 +                          md->invmass, md->ptype,
 +                          md->cFREEZE, md->cACC, md->cTC,
 +                          state->x, xprime, state->v, force, state->sd_X,
 +                          inputrec->opts.tau_t,
 +                          FALSE, step, inputrec->ld_seed,
 +                          DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 +        }
 +        inc_nrnb(nrnb, eNR_UPDATE, homenr);
 +
 +        if (bDoConstr)
 +        {
 +            /* Constrain the coordinates xprime */
 +            wallcycle_start(wcycle, ewcCONSTR);
 +            constrain(NULL, bLog, bEner, constr, idef,
 +                      inputrec, NULL, cr, step, 1, md,
 +                      state->x, xprime, NULL,
 +                      bMolPBC, state->box,
 +                      state->lambda[efptBONDED], dvdlambda,
 +                      NULL, NULL, nrnb, econqCoord, FALSE, 0, 0);
 +            wallcycle_stop(wcycle, ewcCONSTR);
 +        }
 +    }
 +
 +    /* We must always unshift after updating coordinates; if we did not shake
 +       x was shifted in do_force */
 +
 +    if (!(bFirstHalf)) /* in the first half of vv, no shift. */
 +    {
 +        if (graph && (graph->nnodes > 0))
 +        {
 +            unshift_x(graph, state->box, state->x, upd->xp);
 +            if (TRICLINIC(state->box))
 +            {
 +                inc_nrnb(nrnb, eNR_SHIFTX, 2*graph->nnodes);
 +            }
 +            else
 +            {
 +                inc_nrnb(nrnb, eNR_SHIFTX, graph->nnodes);
 +            }
 +        }
 +        else
 +        {
 +#pragma omp parallel for num_threads(gmx_omp_nthreads_get(emntUpdate)) schedule(static)
 +            for (i = start; i < nrend; i++)
 +            {
 +                copy_rvec(upd->xp[i], state->x[i]);
 +            }
 +        }
 +
 +        dump_it_all(fplog, "After unshift",
 +                    state->natoms, state->x, upd->xp, state->v, force);
 +    }
 +/* ############# END the update of velocities and positions ######### */
 +}
 +
 +void update_box(FILE             *fplog,
 +                gmx_int64_t       step,
 +                t_inputrec       *inputrec,  /* input record and box stuff    */
 +                t_mdatoms        *md,
 +                t_state          *state,
 +                rvec              force[],   /* forces on home particles */
 +                matrix           *scale_tot,
 +                matrix            pcoupl_mu,
 +                t_nrnb           *nrnb,
 +                gmx_update_t      upd)
 +{
 +    gmx_bool             bExtended, bLastStep, bLog = FALSE, bEner = FALSE;
 +    double               dt;
 +    real                 dt_1;
 +    int                  start, homenr, nrend, i, n, m, g;
 +    tensor               vir_con;
 +
 +    start  = 0;
 +    homenr = md->homenr;
 +    nrend  = start+homenr;
 +
 +    bExtended =
 +        (inputrec->etc == etcNOSEHOOVER) ||
 +        (inputrec->epc == epcPARRINELLORAHMAN) ||
 +        (inputrec->epc == epcMTTK);
 +
 +    dt = inputrec->delta_t;
 +
 +    where();
 +
 +    /* now update boxes */
 +    switch (inputrec->epc)
 +    {
 +        case (epcNO):
 +            break;
 +        case (epcBERENDSEN):
 +            berendsen_pscale(inputrec, pcoupl_mu, state->box, state->box_rel,
 +                             start, homenr, state->x, md->cFREEZE, nrnb);
 +            break;
 +        case (epcPARRINELLORAHMAN):
 +            /* The box velocities were updated in do_pr_pcoupl in the update
 +             * iteration, but we dont change the box vectors until we get here
 +             * since we need to be able to shift/unshift above.
 +             */
 +            for (i = 0; i < DIM; i++)
 +            {
 +                for (m = 0; m <= i; m++)
 +                {
 +                    state->box[i][m] += dt*state->boxv[i][m];
 +                }
 +            }
 +            preserve_box_shape(inputrec, state->box_rel, state->box);
 +
 +            /* Scale the coordinates */
 +            for (n = start; (n < start+homenr); n++)
 +            {
 +                tmvmul_ur0(pcoupl_mu, state->x[n], state->x[n]);
 +            }
 +            break;
 +        case (epcMTTK):
 +            switch (inputrec->epct)
 +            {
 +                case (epctISOTROPIC):
 +                    /* DIM * eta = ln V.  so DIM*eta_new = DIM*eta_old + DIM*dt*veta =>
 +                       ln V_new = ln V_old + 3*dt*veta => V_new = V_old*exp(3*dt*veta) =>
 +                       Side length scales as exp(veta*dt) */
 +
 +                    msmul(state->box, exp(state->veta*dt), state->box);
 +
 +                    /* Relate veta to boxv.  veta = d(eta)/dT = (1/DIM)*1/V dV/dT.
 +                       o               If we assume isotropic scaling, and box length scaling
 +                       factor L, then V = L^DIM (det(M)).  So dV/dt = DIM
 +                       L^(DIM-1) dL/dt det(M), and veta = (1/L) dL/dt.  The
 +                       determinant of B is L^DIM det(M), and the determinant
 +                       of dB/dt is (dL/dT)^DIM det (M).  veta will be
 +                       (det(dB/dT)/det(B))^(1/3).  Then since M =
 +                       B_new*(vol_new)^(1/3), dB/dT_new = (veta_new)*B(new). */
 +
 +                    msmul(state->box, state->veta, state->boxv);
 +                    break;
 +                default:
 +                    break;
 +            }
 +            break;
 +        default:
 +            break;
 +    }
 +
 +    if ((!(IR_NPT_TROTTER(inputrec) || IR_NPH_TROTTER(inputrec))) && scale_tot)
 +    {
 +        /* The transposes of the scaling matrices are stored,
 +         * therefore we need to reverse the order in the multiplication.
 +         */
 +        mmul_ur0(*scale_tot, pcoupl_mu, *scale_tot);
 +    }
 +
 +    if (DEFORM(*inputrec))
 +    {
 +        deform(upd, start, homenr, state->x, state->box, scale_tot, inputrec, step);
 +    }
 +    where();
 +    dump_it_all(fplog, "After update",
 +                state->natoms, state->x, upd->xp, state->v, force);
 +}
 +
 +void update_coords(FILE             *fplog,
 +                   gmx_int64_t       step,
 +                   t_inputrec       *inputrec,  /* input record and box stuff */
 +                   t_mdatoms        *md,
 +                   t_state          *state,
 +                   gmx_bool          bMolPBC,
 +                   rvec             *f,    /* forces on home particles */
 +                   gmx_bool          bDoLR,
 +                   rvec             *f_lr,
 +                   tensor           *vir_lr_constr,
 +                   t_fcdata         *fcd,
 +                   gmx_ekindata_t   *ekind,
 +                   matrix            M,
 +                   gmx_update_t      upd,
 +                   gmx_bool          bInitStep,
 +                   int               UpdatePart,
 +                   t_commrec        *cr, /* these shouldn't be here -- need to think about it */
 +                   t_nrnb           *nrnb,
 +                   gmx_constr_t      constr,
 +                   t_idef           *idef)
 +{
 +    gmx_bool          bNH, bPR, bLastStep, bLog = FALSE, bEner = FALSE;
 +    double            dt, alpha;
 +    real             *imass, *imassin;
 +    rvec             *force;
 +    real              dt_1;
 +    int               start, homenr, nrend, i, j, d, n, m, g;
 +    int               blen0, blen1, iatom, jatom, nshake, nsettle, nconstr, nexpand;
 +    int              *icom = NULL;
 +    tensor            vir_con;
 +    rvec             *vcom, *xcom, *vall, *xall, *xin, *vin, *forcein, *fall, *xpall, *xprimein, *xprime;
 +    int               nth, th;
 +
 +    /* Running the velocity half does nothing except for velocity verlet */
 +    if ((UpdatePart == etrtVELOCITY1 || UpdatePart == etrtVELOCITY2) &&
 +        !EI_VV(inputrec->eI))
 +    {
 +        gmx_incons("update_coords called for velocity without VV integrator");
 +    }
 +
 +    start  = 0;
 +    homenr = md->homenr;
 +    nrend  = start+homenr;
 +
 +    xprime = get_xprime(state, upd);
 +
 +    dt   = inputrec->delta_t;
 +    dt_1 = 1.0/dt;
 +
 +    /* We need to update the NMR restraint history when time averaging is used */
 +    if (state->flags & (1<<estDISRE_RM3TAV))
 +    {
 +        update_disres_history(fcd, &state->hist);
 +    }
 +    if (state->flags & (1<<estORIRE_DTAV))
 +    {
 +        update_orires_history(fcd, &state->hist);
 +    }
 +
 +
 +    bNH = inputrec->etc == etcNOSEHOOVER;
 +    bPR = ((inputrec->epc == epcPARRINELLORAHMAN) || (inputrec->epc == epcMTTK));
 +
 +    if (bDoLR && inputrec->nstcalclr > 1 && !EI_VV(inputrec->eI))  /* get this working with VV? */
 +    {
 +        /* Store the total force + nstcalclr-1 times the LR force
 +         * in forces_lr, so it can be used in a normal update algorithm
 +         * to produce twin time stepping.
 +         */
 +        /* is this correct in the new construction? MRS */
 +        combine_forces(upd,
 +                       inputrec->nstcalclr, constr, inputrec, md, idef, cr,
 +                       step, state, bMolPBC,
 +                       start, nrend, f, f_lr, vir_lr_constr, nrnb);
 +        force = f_lr;
 +    }
 +    else
 +    {
 +        force = f;
 +    }
 +
 +    /* ############# START The update of velocities and positions ######### */
 +    where();
 +    dump_it_all(fplog, "Before update",
 +                state->natoms, state->x, xprime, state->v, force);
 +
 +    if (inputrec->eI == eiSD2)
 +    {
 +        check_sd2_work_data_allocation(upd->sd, nrend);
 +
 +        do_update_sd2_Tconsts(upd->sd,
 +                              inputrec->opts.ngtc,
 +                              inputrec->opts.tau_t,
 +                              inputrec->opts.ref_t);
 +    }
 +    if (inputrec->eI == eiBD)
 +    {
 +        do_update_bd_Tconsts(dt, inputrec->bd_fric,
 +                             inputrec->opts.ngtc, inputrec->opts.ref_t,
 +                             upd->sd->bd_rf);
 +    }
 +
 +    nth = gmx_omp_nthreads_get(emntUpdate);
 +
 +#pragma omp parallel for num_threads(nth) schedule(static) private(alpha)
 +    for (th = 0; th < nth; th++)
 +    {
 +        int start_th, end_th;
 +
 +        start_th = start + ((nrend-start)* th   )/nth;
 +        end_th   = start + ((nrend-start)*(th+1))/nth;
 +
 +        switch (inputrec->eI)
 +        {
 +            case (eiMD):
 +                if (ekind->cosacc.cos_accel == 0)
 +                {
 +                    do_update_md(start_th, end_th, dt,
 +                                 ekind->tcstat, state->nosehoover_vxi,
 +                                 ekind->bNEMD, ekind->grpstat, inputrec->opts.acc,
 +                                 inputrec->opts.nFreeze,
 +                                 md->invmass, md->ptype,
 +                                 md->cFREEZE, md->cACC, md->cTC,
 +                                 state->x, xprime, state->v, force, M,
 +                                 bNH, bPR);
 +                }
 +                else
 +                {
 +                    do_update_visc(start_th, end_th, dt,
 +                                   ekind->tcstat, state->nosehoover_vxi,
 +                                   md->invmass, md->ptype,
 +                                   md->cTC, state->x, xprime, state->v, force, M,
 +                                   state->box,
 +                                   ekind->cosacc.cos_accel,
 +                                   ekind->cosacc.vcos,
 +                                   bNH, bPR);
 +                }
 +                break;
 +            case (eiSD1):
 +                do_update_sd1(upd->sd,
 +                              start_th, end_th, dt,
 +                              inputrec->opts.acc, inputrec->opts.nFreeze,
 +                              md->invmass, md->ptype,
 +                              md->cFREEZE, md->cACC, md->cTC,
 +                              state->x, xprime, state->v, force,
 +                              inputrec->opts.ngtc, inputrec->opts.tau_t, inputrec->opts.ref_t,
 +                              step, inputrec->ld_seed, DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 +                break;
 +            case (eiSD2):
 +                /* The SD update is done in 2 parts, because an extra constraint step
 +                 * is needed
 +                 */
 +                do_update_sd2(upd->sd,
 +                              bInitStep, start_th, end_th,
 +                              inputrec->opts.acc, inputrec->opts.nFreeze,
 +                              md->invmass, md->ptype,
 +                              md->cFREEZE, md->cACC, md->cTC,
 +                              state->x, xprime, state->v, force, state->sd_X,
 +                              inputrec->opts.tau_t,
 +                              TRUE, step, inputrec->ld_seed,
 +                              DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 +                break;
 +            case (eiBD):
 +                do_update_bd(start_th, end_th, dt,
 +                             inputrec->opts.nFreeze, md->invmass, md->ptype,
 +                             md->cFREEZE, md->cTC,
 +                             state->x, xprime, state->v, force,
 +                             inputrec->bd_fric,
 +                             upd->sd->bd_rf,
 +                             step, inputrec->ld_seed, DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 +                break;
 +            case (eiVV):
 +            case (eiVVAK):
 +                alpha = 1.0 + DIM/((double)inputrec->opts.nrdf[0]); /* assuming barostat coupled to group 0. */
 +                switch (UpdatePart)
 +                {
 +                    case etrtVELOCITY1:
 +                    case etrtVELOCITY2:
 +                        do_update_vv_vel(start_th, end_th, dt,
 +                                         inputrec->opts.acc, inputrec->opts.nFreeze,
 +                                         md->invmass, md->ptype,
 +                                         md->cFREEZE, md->cACC,
 +                                         state->v, force,
 +                                         (bNH || bPR), state->veta, alpha);
 +                        break;
 +                    case etrtPOSITION:
 +                        do_update_vv_pos(start_th, end_th, dt,
 +                                         inputrec->opts.nFreeze,
 +                                         md->ptype, md->cFREEZE,
 +                                         state->x, xprime, state->v,
 +                                         (bNH || bPR), state->veta);
 +                        break;
 +                }
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "Don't know how to update coordinates");
 +                break;
 +        }
 +    }
 +
 +}
 +
 +
 +void correct_ekin(FILE *log, int start, int end, rvec v[], rvec vcm, real mass[],
 +                  real tmass, tensor ekin)
 +{
 +    /*
 +     * This is a debugging routine. It should not be called for production code
 +     *
 +     * The kinetic energy should calculated according to:
 +     *   Ekin = 1/2 m (v-vcm)^2
 +     * However the correction is not always applied, since vcm may not be
 +     * known in time and we compute
 +     *   Ekin' = 1/2 m v^2 instead
 +     * This can be corrected afterwards by computing
 +     *   Ekin = Ekin' + 1/2 m ( -2 v vcm + vcm^2)
 +     * or in hsorthand:
 +     *   Ekin = Ekin' - m v vcm + 1/2 m vcm^2
 +     */
 +    int    i, j, k;
 +    real   m, tm;
 +    rvec   hvcm, mv;
 +    tensor dekin;
 +
 +    /* Local particles */
 +    clear_rvec(mv);
 +
 +    /* Processor dependent part. */
 +    tm = 0;
 +    for (i = start; (i < end); i++)
 +    {
 +        m      = mass[i];
 +        tm    += m;
 +        for (j = 0; (j < DIM); j++)
 +        {
 +            mv[j] += m*v[i][j];
 +        }
 +    }
 +    /* Shortcut */
 +    svmul(1/tmass, vcm, vcm);
 +    svmul(0.5, vcm, hvcm);
 +    clear_mat(dekin);
 +    for (j = 0; (j < DIM); j++)
 +    {
 +        for (k = 0; (k < DIM); k++)
 +        {
 +            dekin[j][k] += vcm[k]*(tm*hvcm[j]-mv[j]);
 +        }
 +    }
 +    pr_rvecs(log, 0, "dekin", dekin, DIM);
 +    pr_rvecs(log, 0, " ekin", ekin, DIM);
 +    fprintf(log, "dekin = %g, ekin = %g  vcm = (%8.4f %8.4f %8.4f)\n",
 +            trace(dekin), trace(ekin), vcm[XX], vcm[YY], vcm[ZZ]);
 +    fprintf(log, "mv = (%8.4f %8.4f %8.4f)\n",
 +            mv[XX], mv[YY], mv[ZZ]);
 +}
 +
 +extern gmx_bool update_randomize_velocities(t_inputrec *ir, gmx_int64_t step, const t_commrec *cr,
 +                                            t_mdatoms *md, t_state *state, gmx_update_t upd, gmx_constr_t constr)
 +{
 +
 +    int  i;
 +    real rate = (ir->delta_t)/ir->opts.tau_t[0];
 +
 +    if (ir->etc == etcANDERSEN && constr != NULL)
 +    {
 +        gmx_fatal(FARGS, "Normal Andersen is currently not supported with constraints, use massive Andersen instead");
 +    }
 +
 +    /* proceed with andersen if 1) it's fixed probability per
 +       particle andersen or 2) it's massive andersen and it's tau_t/dt */
 +    if ((ir->etc == etcANDERSEN) || do_per_step(step, (int)(1.0/rate)))
 +    {
 +        andersen_tcoupl(ir, step, cr, md, state, rate,
 +                        upd->sd->randomize_group, upd->sd->boltzfac);
 +        return TRUE;
 +    }
 +    return FALSE;
 +}
index 85a74eb4aef035394a95ba6b97b3be308294bca1,0000000000000000000000000000000000000000..6de4e9e4478e65991a0d5b327c0bde341234699e
mode 100644,000000..100644
--- /dev/null
@@@ -1,333 -1,0 +1,333 @@@
-                 lamfac = 0;
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2008, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <string.h>
 +#include "gromacs/math/utilities.h"
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "macros.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "force.h"
 +#include "gromacs/fileio/filenm.h"
 +#include "nrnb.h"
 +#include "vec.h"
 +
 +void make_wall_tables(FILE *fplog, const output_env_t oenv,
 +                      const t_inputrec *ir, const char *tabfn,
 +                      const gmx_groups_t *groups,
 +                      t_forcerec *fr)
 +{
 +    int           w, negp_pp, egp, i, j;
 +    int          *nm_ind;
 +    char          buf[STRLEN];
 +    t_forcetable *tab;
 +
 +    negp_pp = ir->opts.ngener - ir->nwall;
 +    nm_ind  = groups->grps[egcENER].nm_ind;
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog, "Reading user tables for %d energy groups with %d walls\n",
 +                negp_pp, ir->nwall);
 +    }
 +
 +    snew(fr->wall_tab, ir->nwall);
 +    for (w = 0; w < ir->nwall; w++)
 +    {
 +        snew(fr->wall_tab[w], negp_pp);
 +        for (egp = 0; egp < negp_pp; egp++)
 +        {
 +            /* If the energy group pair is excluded, we don't need a table */
 +            if (!(fr->egp_flags[egp*ir->opts.ngener+negp_pp+w] & EGP_EXCL))
 +            {
 +                tab = &fr->wall_tab[w][egp];
 +                sprintf(buf, "%s", tabfn);
 +                sprintf(buf + strlen(tabfn) - strlen(ftp2ext(efXVG)) - 1, "_%s_%s.%s",
 +                        *groups->grpname[nm_ind[egp]],
 +                        *groups->grpname[nm_ind[negp_pp+w]],
 +                        ftp2ext(efXVG));
 +                *tab = make_tables(fplog, oenv, fr, FALSE, buf, 0, GMX_MAKETABLES_FORCEUSER);
 +                /* Since wall have no charge, we can compress the table */
 +                for (i = 0; i <= tab->n; i++)
 +                {
 +                    for (j = 0; j < 8; j++)
 +                    {
 +                        tab->data[8*i+j] = tab->data[12*i+4+j];
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static void wall_error(int a, rvec *x, real r)
 +{
 +    gmx_fatal(FARGS,
 +              "An atom is beyond the wall: coordinates %f %f %f, distance %f\n"
 +              "You might want to use the mdp option wall_r_linpot",
 +              x[a][XX], x[a][YY], x[a][ZZ], r);
 +}
 +
 +real do_walls(t_inputrec *ir, t_forcerec *fr, matrix box, t_mdatoms *md,
 +              rvec x[], rvec f[], real lambda, real Vlj[], t_nrnb *nrnb)
 +{
 +    int             nwall, w, lam, i;
 +    int             ntw[2], at, ntype, ngid, ggid, *egp_flags, *type;
 +    real           *nbfp, lamfac, fac_d[2], fac_r[2], Cd, Cr, Vtot, Fwall[2];
 +    real            wall_z[2], r, mr, r1, r2, r4, Vd, Vr, V = 0, Fd, Fr, F = 0, dvdlambda;
 +    dvec            xf_z;
 +    int             n0, nnn;
 +    real            tabscale, *VFtab, rt, eps, eps2, Yt, Ft, Geps, Heps, Heps2, Fp, VV, FF;
 +    unsigned short *gid = md->cENER;
 +    t_forcetable   *tab;
 +
 +    nwall     = ir->nwall;
 +    ngid      = ir->opts.ngener;
 +    ntype     = fr->ntype;
 +    nbfp      = fr->nbfp;
 +    egp_flags = fr->egp_flags;
 +
 +    for (w = 0; w < nwall; w++)
 +    {
 +        ntw[w] = 2*ntype*ir->wall_atomtype[w];
 +        switch (ir->wall_type)
 +        {
 +            case ewt93:
 +                fac_d[w] = ir->wall_density[w]*M_PI/6;
 +                fac_r[w] = ir->wall_density[w]*M_PI/45;
 +                break;
 +            case ewt104:
 +                fac_d[w] = ir->wall_density[w]*M_PI/2;
 +                fac_r[w] = ir->wall_density[w]*M_PI/5;
 +                break;
 +            default:
 +                break;
 +        }
 +        Fwall[w] = 0;
 +    }
 +    wall_z[0] = 0;
 +    wall_z[1] = box[ZZ][ZZ];
 +
 +    Vtot      = 0;
 +    dvdlambda = 0;
 +    clear_dvec(xf_z);
 +    for (lam = 0; lam < (md->nPerturbed ? 2 : 1); lam++)
 +    {
 +        if (md->nPerturbed)
 +        {
 +            if (lam == 0)
 +            {
 +                lamfac = 1 - lambda;
 +                type   = md->typeA;
 +            }
 +            else
 +            {
++                lamfac = lambda;
 +                type   = md->typeB;
 +            }
 +        }
 +        else
 +        {
 +            lamfac = 1;
 +            type   = md->typeA;
 +        }
 +        for (i = 0; i < md->homenr; i++)
 +        {
 +            for (w = 0; w < nwall; w++)
 +            {
 +                /* The wall energy groups are always at the end of the list */
 +                ggid = gid[i]*ngid + ngid - nwall + w;
 +                at   = type[i];
 +                /* nbfp now includes the 6.0/12.0 derivative prefactors */
 +                Cd = nbfp[ntw[w]+2*at]/6.0;
 +                Cr = nbfp[ntw[w]+2*at+1]/12.0;
 +                if (!((Cd == 0 && Cr == 0) || (egp_flags[ggid] & EGP_EXCL)))
 +                {
 +                    if (w == 0)
 +                    {
 +                        r = x[i][ZZ];
 +                    }
 +                    else
 +                    {
 +                        r = wall_z[1] - x[i][ZZ];
 +                    }
 +                    if (r < ir->wall_r_linpot)
 +                    {
 +                        mr = ir->wall_r_linpot - r;
 +                        r  = ir->wall_r_linpot;
 +                    }
 +                    else
 +                    {
 +                        mr = 0;
 +                    }
 +                    switch (ir->wall_type)
 +                    {
 +                        case ewtTABLE:
 +                            if (r < 0)
 +                            {
 +                                wall_error(i, x, r);
 +                            }
 +                            tab      = &(fr->wall_tab[w][gid[i]]);
 +                            tabscale = tab->scale;
 +                            VFtab    = tab->data;
 +
 +                            rt    = r*tabscale;
 +                            n0    = rt;
 +                            if (n0 >= tab->n)
 +                            {
 +                                /* Beyond the table range, set V and F to zero */
 +                                V     = 0;
 +                                F     = 0;
 +                            }
 +                            else
 +                            {
 +                                eps   = rt - n0;
 +                                eps2  = eps*eps;
 +                                /* Dispersion */
 +                                nnn   = 8*n0;
 +                                Yt    = VFtab[nnn];
 +                                Ft    = VFtab[nnn+1];
 +                                Geps  = VFtab[nnn+2]*eps;
 +                                Heps2 = VFtab[nnn+3]*eps2;
 +                                Fp    = Ft + Geps + Heps2;
 +                                VV    = Yt + Fp*eps;
 +                                FF    = Fp + Geps + 2.0*Heps2;
 +                                Vd    = Cd*VV;
 +                                Fd    = Cd*FF;
 +                                /* Repulsion */
 +                                nnn   = nnn + 4;
 +                                Yt    = VFtab[nnn];
 +                                Ft    = VFtab[nnn+1];
 +                                Geps  = VFtab[nnn+2]*eps;
 +                                Heps2 = VFtab[nnn+3]*eps2;
 +                                Fp    = Ft + Geps + Heps2;
 +                                VV    = Yt + Fp*eps;
 +                                FF    = Fp + Geps + 2.0*Heps2;
 +                                Vr    = Cr*VV;
 +                                Fr    = Cr*FF;
 +                                V     = Vd + Vr;
 +                                F     = -lamfac*(Fd + Fr)*tabscale;
 +                            }
 +                            break;
 +                        case ewt93:
 +                            if (r <= 0)
 +                            {
 +                                wall_error(i, x, r);
 +                            }
 +                            r1 = 1/r;
 +                            r2 = r1*r1;
 +                            r4 = r2*r2;
 +                            Vd = fac_d[w]*Cd*r2*r1;
 +                            Vr = fac_r[w]*Cr*r4*r4*r1;
 +                            V  = Vr - Vd;
 +                            F  = lamfac*(9*Vr - 3*Vd)*r1;
 +                            break;
 +                        case ewt104:
 +                            if (r <= 0)
 +                            {
 +                                wall_error(i, x, r);
 +                            }
 +                            r1 = 1/r;
 +                            r2 = r1*r1;
 +                            r4 = r2*r2;
 +                            Vd = fac_d[w]*Cd*r4;
 +                            Vr = fac_r[w]*Cr*r4*r4*r2;
 +                            V  = Vr - Vd;
 +                            F  = lamfac*(10*Vr - 4*Vd)*r1;
 +                            break;
 +                        case ewt126:
 +                            if (r <= 0)
 +                            {
 +                                wall_error(i, x, r);
 +                            }
 +                            r1 = 1/r;
 +                            r2 = r1*r1;
 +                            r4 = r2*r2;
 +                            Vd = Cd*r4*r2;
 +                            Vr = Cr*r4*r4*r4;
 +                            V  = Vr - Vd;
 +                            F  = lamfac*(12*Vr - 6*Vd)*r1;
 +                            break;
 +                        default:
 +                            break;
 +                    }
 +                    if (mr > 0)
 +                    {
 +                        V += mr*F;
 +                    }
 +                    if (w == 1)
 +                    {
 +                        F = -F;
 +                    }
 +                    Vlj[ggid] += lamfac*V;
 +                    Vtot      += V;
 +                    f[i][ZZ]  += F;
 +                    /* Because of the single sum virial calculation we need
 +                     * to add  the full virial contribution of the walls.
 +                     * Since the force only has a z-component, there is only
 +                     * a contribution to the z component of the virial tensor.
 +                     * We could also determine the virial contribution directly,
 +                     * which would be cheaper here, but that would require extra
 +                     * communication for f_novirsum for with virtual sites
 +                     * in parallel.
 +                     */
 +                    xf_z[XX]  -= x[i][XX]*F;
 +                    xf_z[YY]  -= x[i][YY]*F;
 +                    xf_z[ZZ]  -= wall_z[w]*F;
 +                }
 +            }
 +        }
 +        if (md->nPerturbed)
 +        {
 +            dvdlambda += (lam == 0 ? -1 : 1)*Vtot;
 +        }
 +
 +        inc_nrnb(nrnb, eNR_WALLS, md->homenr);
 +    }
 +
 +    for (i = 0; i < DIM; i++)
 +    {
 +        fr->vir_wall_z[i] = -0.5*xf_z[i];
 +    }
 +
 +    return dvdlambda;
 +}
index a4500a4d4e24a749eb2ae0e9a0509245787190b5,0000000000000000000000000000000000000000..64bdbfd3a5b3c96264a23a1a43b3b17a9cfb220b
mode 100644,000000..100644
--- /dev/null
@@@ -1,1272 -1,0 +1,1273 @@@
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +
 +#include <math.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include "gromacs/fileio/futil.h"
 +#include "index.h"
 +#include "gromacs/fileio/gmxfio.h"
 +#include "vec.h"
 +#include "typedefs.h"
 +#include "types/commrec.h"
 +#include "network.h"
 +#include "gromacs/fileio/filenm.h"
 +#include <string.h>
 +#include "gromacs/utility/smalloc.h"
 +#include "pull.h"
 +#include "xvgr.h"
 +#include "names.h"
 +#include "pbc.h"
 +#include "mtop_util.h"
 +#include "mdrun.h"
 +#include "gmx_ga2la.h"
 +#include "copyrite.h"
 +#include "macros.h"
 +#include "vec.h"
 +
 +static void pull_print_group_x(FILE *out, ivec dim, const t_pull_group *pgrp)
 +{
 +    int m;
 +
 +    for (m = 0; m < DIM; m++)
 +    {
 +        if (dim[m])
 +        {
 +            fprintf(out, "\t%g", pgrp->x[m]);
 +        }
 +    }
 +}
 +
 +static void pull_print_coord_dr(FILE *out, ivec dim, const t_pull_coord *pcrd)
 +{
 +    int m;
 +
 +    for (m = 0; m < DIM; m++)
 +    {
 +        if (dim[m])
 +        {
 +            fprintf(out, "\t%g", pcrd->dr[m]);
 +        }
 +    }
 +}
 +
 +static void pull_print_x(FILE *out, t_pull *pull, double t)
 +{
 +    int                 c;
 +    const t_pull_coord *pcrd;
 +
 +    fprintf(out, "%.4f", t);
 +
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        pcrd = &pull->coord[c];
 +
 +        if (pull->bPrintRef)
 +        {
 +            if (PULL_CYL(pull))
 +            {
 +                pull_print_group_x(out, pull->dim, &pull->dyna[c]);
 +            }
 +            else
 +            {
 +                pull_print_group_x(out, pull->dim, &pull->group[pcrd->group[0]]);
 +            }
 +        }
 +        pull_print_coord_dr(out, pull->dim, pcrd);
 +    }
 +    fprintf(out, "\n");
 +}
 +
 +static void pull_print_f(FILE *out, t_pull *pull, double t)
 +{
 +    int c, d;
 +
 +    fprintf(out, "%.4f", t);
 +
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        fprintf(out, "\t%g", pull->coord[c].f_scal);
 +    }
 +    fprintf(out, "\n");
 +}
 +
 +void pull_print_output(t_pull *pull, gmx_int64_t step, double time)
 +{
 +    if ((pull->nstxout != 0) && (step % pull->nstxout == 0))
 +    {
 +        pull_print_x(pull->out_x, pull, time);
 +    }
 +
 +    if ((pull->nstfout != 0) && (step % pull->nstfout == 0))
 +    {
 +        pull_print_f(pull->out_f, pull, time);
 +    }
 +}
 +
 +static FILE *open_pull_out(const char *fn, t_pull *pull, const output_env_t oenv,
 +                           gmx_bool bCoord, unsigned long Flags)
 +{
 +    FILE  *fp;
 +    int    nsets, c, m;
 +    char **setname, buf[10];
 +
 +    if (Flags & MD_APPENDFILES)
 +    {
 +        fp = gmx_fio_fopen(fn, "a+");
 +    }
 +    else
 +    {
 +        fp = gmx_fio_fopen(fn, "w+");
 +        if (bCoord)
 +        {
 +            xvgr_header(fp, "Pull COM",  "Time (ps)", "Position (nm)",
 +                        exvggtXNY, oenv);
 +        }
 +        else
 +        {
 +            xvgr_header(fp, "Pull force", "Time (ps)", "Force (kJ/mol/nm)",
 +                        exvggtXNY, oenv);
 +        }
 +
 +        snew(setname, 2*pull->ncoord*DIM);
 +        nsets = 0;
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            if (bCoord)
 +            {
 +                if (pull->bPrintRef)
 +                {
 +                    for (m = 0; m < DIM; m++)
 +                    {
 +                        if (pull->dim[m])
 +                        {
 +                            sprintf(buf, "%d %s%c", c+1, "c", 'X'+m);
 +                            setname[nsets] = strdup(buf);
 +                            nsets++;
 +                        }
 +                    }
 +                }
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    if (pull->dim[m])
 +                    {
 +                        sprintf(buf, "%d %s%c", c+1, "d", 'X'+m);
 +                        setname[nsets] = strdup(buf);
 +                        nsets++;
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                sprintf(buf, "%d", c+1);
 +                setname[nsets] = strdup(buf);
 +                nsets++;
 +            }
 +        }
 +        if (nsets > 1)
 +        {
 +            xvgr_legend(fp, nsets, (const char**)setname, oenv);
 +        }
 +        for (c = 0; c < nsets; c++)
 +        {
 +            sfree(setname[c]);
 +        }
 +        sfree(setname);
 +    }
 +
 +    return fp;
 +}
 +
 +/* Apply forces in a mass weighted fashion */
 +static void apply_forces_grp(const t_pull_group *pgrp, const t_mdatoms *md,
 +                             const dvec f_pull, int sign, rvec *f)
 +{
 +    int    i, ii, m;
 +    double wmass, inv_wm;
 +
 +    inv_wm = pgrp->wscale*pgrp->invtm;
 +
 +    for (i = 0; i < pgrp->nat_loc; i++)
 +    {
 +        ii    = pgrp->ind_loc[i];
 +        wmass = md->massT[ii];
 +        if (pgrp->weight_loc)
 +        {
 +            wmass *= pgrp->weight_loc[i];
 +        }
 +
 +        for (m = 0; m < DIM; m++)
 +        {
 +            f[ii][m] += sign * wmass * f_pull[m] * inv_wm;
 +        }
 +    }
 +}
 +
 +/* Apply forces in a mass weighted fashion */
 +static void apply_forces(t_pull * pull, t_mdatoms * md, rvec *f)
 +{
 +    int                 c;
 +    const t_pull_coord *pcrd;
 +
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        pcrd = &pull->coord[c];
 +
 +        if (PULL_CYL(pull))
 +        {
 +            apply_forces_grp(&pull->dyna[c], md, pcrd->f, -1, f);
 +        }
 +        else
 +        {
 +            if (pull->group[pcrd->group[0]].nat > 0)
 +            {
 +                apply_forces_grp(&pull->group[pcrd->group[0]], md, pcrd->f, -1, f);
 +            }
 +        }
 +        apply_forces_grp(&pull->group[pcrd->group[1]], md, pcrd->f, 1, f);
 +    }
 +}
 +
 +static double max_pull_distance2(const t_pull *pull, const t_pbc *pbc)
 +{
 +    double max_d2;
 +    int    m;
 +
 +    max_d2 = GMX_DOUBLE_MAX;
 +
 +    if (pull->eGeom != epullgDIRPBC)
 +    {
 +        for (m = 0; m < pbc->ndim_ePBC; m++)
 +        {
 +            if (pull->dim[m] != 0)
 +            {
 +                max_d2 = min(max_d2, norm2(pbc->box[m]));
 +            }
 +        }
 +    }
 +
 +    return 0.25*max_d2;
 +}
 +
 +static void low_get_pull_coord_dr(const t_pull *pull,
 +                                  const t_pull_coord *pcrd,
 +                                  const t_pbc *pbc, double t,
 +                                  dvec xg, dvec xref, double max_dist2,
 +                                  dvec dr)
 +{
 +    const t_pull_group *pgrp0, *pgrp1;
 +    int                 m;
 +    dvec                xrefr, dref = {0, 0, 0};
 +    double              dr2;
 +
 +    pgrp0 = &pull->group[pcrd->group[0]];
 +    pgrp1 = &pull->group[pcrd->group[1]];
 +
 +    /* Only the first group can be an absolute reference, in that case nat=0 */
 +    if (pgrp0->nat == 0)
 +    {
 +        for (m = 0; m < DIM; m++)
 +        {
 +            xref[m] = pcrd->origin[m];
 +        }
 +    }
 +
 +    copy_dvec(xref, xrefr);
 +
 +    if (pull->eGeom == epullgDIRPBC)
 +    {
 +        for (m = 0; m < DIM; m++)
 +        {
 +            dref[m] = (pcrd->init + pcrd->rate*t)*pcrd->vec[m];
 +        }
 +        /* Add the reference position, so we use the correct periodic image */
 +        dvec_inc(xrefr, dref);
 +    }
 +
 +    pbc_dx_d(pbc, xg, xrefr, dr);
 +    dr2 = 0;
 +    for (m = 0; m < DIM; m++)
 +    {
 +        dr[m] *= pull->dim[m];
 +        dr2   += dr[m]*dr[m];
 +    }
 +    if (max_dist2 >= 0 && dr2 > 0.98*0.98*max_dist2)
 +    {
 +        gmx_fatal(FARGS, "Distance between pull groups %d and %d (%f nm) is larger than 0.49 times the box size (%f).\nYou might want to consider using \"pull-geometry = direction-periodic\" instead.\n",
 +                  pcrd->group[0], pcrd->group[1], sqrt(dr2), sqrt(max_dist2));
 +    }
 +
 +    if (pull->eGeom == epullgDIRPBC)
 +    {
 +        dvec_inc(dr, dref);
 +    }
 +}
 +
 +static void get_pull_coord_dr(const t_pull *pull,
 +                              int coord_ind,
 +                              const t_pbc *pbc, double t,
 +                              dvec dr)
 +{
 +    double              md2;
 +    const t_pull_coord *pcrd;
 +
 +    if (pull->eGeom == epullgDIRPBC)
 +    {
 +        md2 = -1;
 +    }
 +    else
 +    {
 +        md2 = max_pull_distance2(pull, pbc);
 +    }
 +
 +    pcrd = &pull->coord[coord_ind];
 +
 +    low_get_pull_coord_dr(pull, pcrd, pbc, t,
 +                          pull->group[pcrd->group[1]].x,
 +                          PULL_CYL(pull) ? pull->dyna[coord_ind].x : pull->group[pcrd->group[0]].x,
 +                          md2,
 +                          dr);
 +}
 +
 +void get_pull_coord_distance(const t_pull *pull,
 +                             int coord_ind,
 +                             const t_pbc *pbc, double t,
 +                             dvec dr, double *dev)
 +{
 +    static gmx_bool     bWarned = FALSE; /* TODO: this should be fixed for thread-safety,
 +                                            but is fairly benign */
 +    const t_pull_coord *pcrd;
 +    int                 m;
 +    double              ref, drs, inpr;
 +
 +    pcrd = &pull->coord[coord_ind];
 +
 +    get_pull_coord_dr(pull, coord_ind, pbc, t, dr);
 +
 +    ref = pcrd->init + pcrd->rate*t;
 +
 +    switch (pull->eGeom)
 +    {
 +        case epullgDIST:
 +            /* Pull along the vector between the com's */
 +            if (ref < 0 && !bWarned)
 +            {
 +                fprintf(stderr, "\nPull reference distance for coordinate %d is negative (%f)\n", coord_ind+1, ref);
 +                bWarned = TRUE;
 +            }
 +            drs = dnorm(dr);
 +            if (drs == 0)
 +            {
 +                /* With no vector we can not determine the direction for the force,
 +                 * so we set the force to zero.
 +                 */
 +                *dev = 0;
 +            }
 +            else
 +            {
 +                /* Determine the deviation */
 +                *dev = drs - ref;
 +            }
 +            break;
 +        case epullgDIR:
 +        case epullgDIRPBC:
 +        case epullgCYL:
 +            /* Pull along vec */
 +            inpr = 0;
 +            for (m = 0; m < DIM; m++)
 +            {
 +                inpr += pcrd->vec[m]*dr[m];
 +            }
 +            *dev = inpr - ref;
 +            break;
 +    }
 +}
 +
 +void clear_pull_forces(t_pull *pull)
 +{
 +    int i;
 +
 +    /* Zeroing the forces is only required for constraint pulling.
 +     * It can happen that multiple constraint steps need to be applied
 +     * and therefore the constraint forces need to be accumulated.
 +     */
 +    for (i = 0; i < pull->ncoord; i++)
 +    {
 +        clear_dvec(pull->coord[i].f);
 +        pull->coord[i].f_scal = 0;
 +    }
 +}
 +
 +/* Apply constraint using SHAKE */
 +static void do_constraint(t_pull *pull, t_pbc *pbc,
 +                          rvec *x, rvec *v,
 +                          gmx_bool bMaster, tensor vir,
 +                          double dt, double t)
 +{
 +
 +    dvec         *r_ij;   /* x[i] com of i in prev. step. Obeys constr. -> r_ij[i] */
 +    dvec          unc_ij; /* xp[i] com of i this step, before constr.   -> unc_ij  */
 +    dvec         *rnew;   /* current 'new' positions of the groups */
 +    double       *dr_tot; /* the total update of the coords */
 +    double        ref;
 +    dvec          vec;
 +    double        d0, inpr;
 +    double        lambda, rm, mass, invdt = 0;
 +    gmx_bool      bConverged_all, bConverged = FALSE;
 +    int           niter = 0, g, c, ii, j, m, max_iter = 100;
 +    double        a;
 +    dvec          f;       /* the pull force */
 +    dvec          tmp, tmp3;
 +    t_pull_group *pdyna, *pgrp0, *pgrp1;
 +    t_pull_coord *pcrd;
 +
 +    snew(r_ij,   pull->ncoord);
 +    snew(dr_tot, pull->ncoord);
 +
 +    snew(rnew, pull->ngroup);
 +
 +    /* copy the current unconstrained positions for use in iterations. We
 +       iterate until rinew[i] and rjnew[j] obey the constraints. Then
 +       rinew - pull.x_unc[i] is the correction dr to group i */
 +    for (g = 0; g < pull->ngroup; g++)
 +    {
 +        copy_dvec(pull->group[g].xp, rnew[g]);
 +    }
 +    if (PULL_CYL(pull))
 +    {
 +        /* There is only one pull coordinate and reference group */
 +        copy_dvec(pull->dyna[0].xp, rnew[pull->coord[0].group[0]]);
 +    }
 +
 +    /* Determine the constraint directions from the old positions */
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        get_pull_coord_dr(pull, c, pbc, t, r_ij[c]);
 +        /* Store the difference vector at time t for printing */
 +        copy_dvec(r_ij[c], pull->coord[c].dr);
 +        if (debug)
 +        {
 +            fprintf(debug, "Pull coord %d dr %f %f %f\n",
 +                    c, r_ij[c][XX], r_ij[c][YY], r_ij[c][ZZ]);
 +        }
 +
 +        if (pull->eGeom == epullgDIR || pull->eGeom == epullgDIRPBC)
 +        {
 +            /* Select the component along vec */
 +            a = 0;
 +            for (m = 0; m < DIM; m++)
 +            {
 +                a += pull->coord[c].vec[m]*r_ij[c][m];
 +            }
 +            for (m = 0; m < DIM; m++)
 +            {
 +                r_ij[c][m] = a*pull->coord[c].vec[m];
 +            }
 +        }
 +    }
 +
 +    bConverged_all = FALSE;
 +    while (!bConverged_all && niter < max_iter)
 +    {
 +        bConverged_all = TRUE;
 +
 +        /* loop over all constraints */
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            dvec dr0, dr1;
 +
 +            pcrd  = &pull->coord[c];
 +            pgrp0 = &pull->group[pcrd->group[0]];
 +            pgrp1 = &pull->group[pcrd->group[1]];
 +
 +            /* Get the current difference vector */
 +            low_get_pull_coord_dr(pull, pcrd, pbc, t,
 +                                  rnew[pcrd->group[1]],
 +                                  rnew[pcrd->group[0]],
 +                                  -1, unc_ij);
 +
 +            ref = pcrd->init + pcrd->rate*t;
 +
 +            if (debug)
 +            {
 +                fprintf(debug, "Pull coord %d, iteration %d\n", c, niter);
 +            }
 +
 +            rm = 1.0/(pgrp0->invtm + pgrp1->invtm);
 +
 +            switch (pull->eGeom)
 +            {
 +                case epullgDIST:
 +                    if (ref <= 0)
 +                    {
 +                        gmx_fatal(FARGS, "The pull constraint reference distance for group %d is <= 0 (%f)", c, ref);
 +                    }
 +
 +                    {
 +                        double q, c_a, c_b, c_c;
 +
 +                        c_a = diprod(r_ij[c], r_ij[c]);
 +                        c_b = diprod(unc_ij, r_ij[c])*2;
 +                        c_c = diprod(unc_ij, unc_ij) - dsqr(ref);
 +
 +                        if (c_b < 0)
 +                        {
 +                            q      = -0.5*(c_b - sqrt(c_b*c_b - 4*c_a*c_c));
 +                            lambda = -q/c_a;
 +                        }
 +                        else
 +                        {
 +                            q      = -0.5*(c_b + sqrt(c_b*c_b - 4*c_a*c_c));
 +                            lambda = -c_c/q;
 +                        }
 +
 +                        if (debug)
 +                        {
 +                            fprintf(debug,
 +                                    "Pull ax^2+bx+c=0: a=%e b=%e c=%e lambda=%e\n",
 +                                    c_a, c_b, c_c, lambda);
 +                        }
 +                    }
 +
 +                    /* The position corrections dr due to the constraints */
 +                    dsvmul(-lambda*rm*pgrp1->invtm, r_ij[c], dr1);
 +                    dsvmul( lambda*rm*pgrp0->invtm, r_ij[c], dr0);
 +                    dr_tot[c] += -lambda*dnorm(r_ij[c]);
 +                    break;
 +                case epullgDIR:
 +                case epullgDIRPBC:
 +                case epullgCYL:
 +                    /* A 1-dimensional constraint along a vector */
 +                    a = 0;
 +                    for (m = 0; m < DIM; m++)
 +                    {
 +                        vec[m] = pcrd->vec[m];
 +                        a     += unc_ij[m]*vec[m];
 +                    }
 +                    /* Select only the component along the vector */
 +                    dsvmul(a, vec, unc_ij);
 +                    lambda = a - ref;
 +                    if (debug)
 +                    {
 +                        fprintf(debug, "Pull inpr %e lambda: %e\n", a, lambda);
 +                    }
 +
 +                    /* The position corrections dr due to the constraints */
 +                    dsvmul(-lambda*rm*pgrp1->invtm, vec, dr1);
 +                    dsvmul( lambda*rm*pgrp0->invtm, vec, dr0);
 +                    dr_tot[c] += -lambda;
 +                    break;
 +            }
 +
 +            /* DEBUG */
 +            if (debug)
 +            {
 +                int g0, g1;
 +
 +                g0 = pcrd->group[0];
 +                g1 = pcrd->group[1];
 +                low_get_pull_coord_dr(pull, pcrd, pbc, t, rnew[g1], rnew[g0], -1, tmp);
 +                low_get_pull_coord_dr(pull, pcrd, pbc, t, dr1, dr0, -1, tmp3);
 +                fprintf(debug,
 +                        "Pull cur %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
 +                        rnew[g0][0], rnew[g0][1], rnew[g0][2],
 +                        rnew[g1][0], rnew[g1][1], rnew[g1][2], dnorm(tmp));
 +                fprintf(debug,
 +                        "Pull ref %8s %8s %8s   %8s %8s %8s d: %8.5f\n",
 +                        "", "", "", "", "", "", ref);
 +                fprintf(debug,
 +                        "Pull cor %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
 +                        dr0[0], dr0[1], dr0[2],
 +                        dr1[0], dr1[1], dr1[2],
 +                        dnorm(tmp3));
 +            } /* END DEBUG */
 +
 +            /* Update the COMs with dr */
 +            dvec_inc(rnew[pcrd->group[1]], dr1);
 +            dvec_inc(rnew[pcrd->group[0]], dr0);
 +        }
 +
 +        /* Check if all constraints are fullfilled now */
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            pcrd = &pull->coord[c];
++            ref  = pcrd->init + pcrd->rate*t;
 +
 +            low_get_pull_coord_dr(pull, pcrd, pbc, t,
 +                                  rnew[pcrd->group[1]],
 +                                  rnew[pcrd->group[0]],
 +                                  -1, unc_ij);
 +
 +            switch (pull->eGeom)
 +            {
 +                case epullgDIST:
 +                    bConverged = fabs(dnorm(unc_ij) - ref) < pull->constr_tol;
 +                    break;
 +                case epullgDIR:
 +                case epullgDIRPBC:
 +                case epullgCYL:
 +                    for (m = 0; m < DIM; m++)
 +                    {
 +                        vec[m] = pcrd->vec[m];
 +                    }
 +                    inpr = diprod(unc_ij, vec);
 +                    dsvmul(inpr, vec, unc_ij);
 +                    bConverged =
 +                        fabs(diprod(unc_ij, vec) - ref) < pull->constr_tol;
 +                    break;
 +            }
 +
 +            if (!bConverged)
 +            {
 +                if (debug)
 +                {
 +                    fprintf(debug, "NOT CONVERGED YET: Group %d:"
 +                            "d_ref = %f, current d = %f\n",
 +                            g, ref, dnorm(unc_ij));
 +                }
 +
 +                bConverged_all = FALSE;
 +            }
 +        }
 +
 +        niter++;
 +        /* if after all constraints are dealt with and bConverged is still TRUE
 +           we're finished, if not we do another iteration */
 +    }
 +    if (niter > max_iter)
 +    {
 +        gmx_fatal(FARGS, "Too many iterations for constraint run: %d", niter);
 +    }
 +
 +    /* DONE ITERATING, NOW UPDATE COORDINATES AND CALC. CONSTRAINT FORCES */
 +
 +    if (v)
 +    {
 +        invdt = 1/dt;
 +    }
 +
 +    /* update atoms in the groups */
 +    for (g = 0; g < pull->ngroup; g++)
 +    {
 +        const t_pull_group *pgrp;
 +        dvec                dr;
 +
 +        if (PULL_CYL(pull) && g == pull->coord[0].group[0])
 +        {
 +            pgrp = &pull->dyna[0];
 +        }
 +        else
 +        {
 +            pgrp = &pull->group[g];
 +        }
 +
 +        /* get the final constraint displacement dr for group g */
 +        dvec_sub(rnew[g], pgrp->xp, dr);
 +        /* select components of dr */
 +        for (m = 0; m < DIM; m++)
 +        {
 +            dr[m] *= pull->dim[m];
 +        }
 +
 +        /* update the atom positions */
 +        copy_dvec(dr, tmp);
 +        for (j = 0; j < pgrp->nat_loc; j++)
 +        {
 +            ii = pgrp->ind_loc[j];
 +            if (pgrp->weight_loc)
 +            {
 +                dsvmul(pgrp->wscale*pgrp->weight_loc[j], dr, tmp);
 +            }
 +            for (m = 0; m < DIM; m++)
 +            {
 +                x[ii][m] += tmp[m];
 +            }
 +            if (v)
 +            {
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    v[ii][m] += invdt*tmp[m];
 +                }
 +            }
 +        }
 +    }
 +
 +    /* calculate the constraint forces, used for output and virial only */
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        pcrd         = &pull->coord[c];
 +        pcrd->f_scal = dr_tot[c]/((pull->group[pcrd->group[0]].invtm + pull->group[pcrd->group[1]].invtm)*dt*dt);
 +
 +        if (vir && bMaster)
 +        {
 +            double f_invr;
 +
 +            /* Add the pull contribution to the virial */
 +            f_invr = pcrd->f_scal/dnorm(r_ij[c]);
 +
 +            for (j = 0; j < DIM; j++)
 +            {
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    vir[j][m] -= 0.5*f_invr*r_ij[c][j]*r_ij[c][m];
 +                }
 +            }
 +        }
 +    }
 +
 +    /* finished! I hope. Give back some memory */
 +    sfree(r_ij);
 +    sfree(dr_tot);
 +    sfree(rnew);
 +}
 +
 +/* Pulling with a harmonic umbrella potential or constant force */
 +static void do_pull_pot(int ePull,
 +                        t_pull *pull, t_pbc *pbc, double t, real lambda,
 +                        real *V, tensor vir, real *dVdl)
 +{
 +    int           c, j, m;
 +    double        dev, ndr, invdr;
 +    real          k, dkdl;
 +    t_pull_coord *pcrd;
 +
 +    /* loop over the pull coordinates */
 +    *V    = 0;
 +    *dVdl = 0;
 +    for (c = 0; c < pull->ncoord; c++)
 +    {
 +        pcrd = &pull->coord[c];
 +
 +        get_pull_coord_distance(pull, c, pbc, t, pcrd->dr, &dev);
 +
 +        k    = (1.0 - lambda)*pcrd->k + lambda*pcrd->kB;
 +        dkdl = pcrd->kB - pcrd->k;
 +
 +        switch (pull->eGeom)
 +        {
 +            case epullgDIST:
 +                ndr   = dnorm(pcrd->dr);
 +                invdr = 1/ndr;
 +                if (ePull == epullUMBRELLA)
 +                {
 +                    pcrd->f_scal  =       -k*dev;
 +                    *V           += 0.5*   k*dsqr(dev);
 +                    *dVdl        += 0.5*dkdl*dsqr(dev);
 +                }
 +                else
 +                {
 +                    pcrd->f_scal  =   -k;
 +                    *V           +=    k*ndr;
 +                    *dVdl        += dkdl*ndr;
 +                }
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    pcrd->f[m]    = pcrd->f_scal*pcrd->dr[m]*invdr;
 +                }
 +                break;
 +            case epullgDIR:
 +            case epullgDIRPBC:
 +            case epullgCYL:
 +                if (ePull == epullUMBRELLA)
 +                {
 +                    pcrd->f_scal  =       -k*dev;
 +                    *V           += 0.5*   k*dsqr(dev);
 +                    *dVdl        += 0.5*dkdl*dsqr(dev);
 +                }
 +                else
 +                {
 +                    ndr = 0;
 +                    for (m = 0; m < DIM; m++)
 +                    {
 +                        ndr += pcrd->vec[m]*pcrd->dr[m];
 +                    }
 +                    pcrd->f_scal  =   -k;
 +                    *V           +=    k*ndr;
 +                    *dVdl        += dkdl*ndr;
 +                }
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    pcrd->f[m]    = pcrd->f_scal*pcrd->vec[m];
 +                }
 +                break;
 +        }
 +
 +        if (vir)
 +        {
 +            /* Add the pull contribution to the virial */
 +            for (j = 0; j < DIM; j++)
 +            {
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    vir[j][m] -= 0.5*pcrd->f[j]*pcrd->dr[m];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +real pull_potential(int ePull, t_pull *pull, t_mdatoms *md, t_pbc *pbc,
 +                    t_commrec *cr, double t, real lambda,
 +                    rvec *x, rvec *f, tensor vir, real *dvdlambda)
 +{
 +    real V, dVdl;
 +
 +    pull_calc_coms(cr, pull, md, pbc, t, x, NULL);
 +
 +    do_pull_pot(ePull, pull, pbc, t, lambda,
 +                &V, pull->bVirial && MASTER(cr) ? vir : NULL, &dVdl);
 +
 +    /* Distribute forces over pulled groups */
 +    apply_forces(pull, md, f);
 +
 +    if (MASTER(cr))
 +    {
 +        *dvdlambda += dVdl;
 +    }
 +
 +    return (MASTER(cr) ? V : 0.0);
 +}
 +
 +void pull_constraint(t_pull *pull, t_mdatoms *md, t_pbc *pbc,
 +                     t_commrec *cr, double dt, double t,
 +                     rvec *x, rvec *xp, rvec *v, tensor vir)
 +{
 +    pull_calc_coms(cr, pull, md, pbc, t, x, xp);
 +
 +    do_constraint(pull, pbc, xp, v, pull->bVirial && MASTER(cr), vir, dt, t);
 +}
 +
 +static void make_local_pull_group(gmx_ga2la_t ga2la,
 +                                  t_pull_group *pg, int start, int end)
 +{
 +    int i, ii;
 +
 +    pg->nat_loc = 0;
 +    for (i = 0; i < pg->nat; i++)
 +    {
 +        ii = pg->ind[i];
 +        if (ga2la)
 +        {
 +            if (!ga2la_get_home(ga2la, ii, &ii))
 +            {
 +                ii = -1;
 +            }
 +        }
 +        if (ii >= start && ii < end)
 +        {
 +            /* This is a home atom, add it to the local pull group */
 +            if (pg->nat_loc >= pg->nalloc_loc)
 +            {
 +                pg->nalloc_loc = over_alloc_dd(pg->nat_loc+1);
 +                srenew(pg->ind_loc, pg->nalloc_loc);
 +                if (pg->epgrppbc == epgrppbcCOS || pg->weight)
 +                {
 +                    srenew(pg->weight_loc, pg->nalloc_loc);
 +                }
 +            }
 +            pg->ind_loc[pg->nat_loc] = ii;
 +            if (pg->weight)
 +            {
 +                pg->weight_loc[pg->nat_loc] = pg->weight[i];
 +            }
 +            pg->nat_loc++;
 +        }
 +    }
 +}
 +
 +void dd_make_local_pull_groups(gmx_domdec_t *dd, t_pull *pull, t_mdatoms *md)
 +{
 +    gmx_ga2la_t ga2la;
 +    int         g;
 +
 +    if (dd)
 +    {
 +        ga2la = dd->ga2la;
 +    }
 +    else
 +    {
 +        ga2la = NULL;
 +    }
 +
 +    for (g = 0; g < pull->ngroup; g++)
 +    {
 +        make_local_pull_group(ga2la, &pull->group[g],
 +                              0, md->homenr);
 +    }
 +}
 +
 +static void init_pull_group_index(FILE *fplog, t_commrec *cr,
 +                                  int g, t_pull_group *pg, ivec pulldims,
 +                                  gmx_mtop_t *mtop, t_inputrec *ir, real lambda)
 +{
 +    int                   i, ii, d, nfrozen, ndim;
 +    real                  m, w, mbd;
 +    double                tmass, wmass, wwmass;
 +    gmx_groups_t         *groups;
 +    gmx_mtop_atomlookup_t alook;
 +    t_atom               *atom;
 +
 +    if (EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
 +    {
 +        /* There are no masses in the integrator.
 +         * But we still want to have the correct mass-weighted COMs.
 +         * So we store the real masses in the weights.
 +         * We do not set nweight, so these weights do not end up in the tpx file.
 +         */
 +        if (pg->nweight == 0)
 +        {
 +            snew(pg->weight, pg->nat);
 +        }
 +    }
 +
 +    if (cr && PAR(cr))
 +    {
 +        pg->nat_loc    = 0;
 +        pg->nalloc_loc = 0;
 +        pg->ind_loc    = NULL;
 +        pg->weight_loc = NULL;
 +    }
 +    else
 +    {
 +        pg->nat_loc = pg->nat;
 +        pg->ind_loc = pg->ind;
 +        if (pg->epgrppbc == epgrppbcCOS)
 +        {
 +            snew(pg->weight_loc, pg->nat);
 +        }
 +        else
 +        {
 +            pg->weight_loc = pg->weight;
 +        }
 +    }
 +
 +    groups = &mtop->groups;
 +
 +    alook = gmx_mtop_atomlookup_init(mtop);
 +
 +    nfrozen = 0;
 +    tmass   = 0;
 +    wmass   = 0;
 +    wwmass  = 0;
 +    for (i = 0; i < pg->nat; i++)
 +    {
 +        ii = pg->ind[i];
 +        gmx_mtop_atomnr_to_atom(alook, ii, &atom);
 +        if (ir->opts.nFreeze)
 +        {
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if (pulldims[d] && ir->opts.nFreeze[ggrpnr(groups, egcFREEZE, ii)][d])
 +                {
 +                    nfrozen++;
 +                }
 +            }
 +        }
 +        if (ir->efep == efepNO)
 +        {
 +            m = atom->m;
 +        }
 +        else
 +        {
 +            m = (1 - lambda)*atom->m + lambda*atom->mB;
 +        }
 +        if (pg->nweight > 0)
 +        {
 +            w = pg->weight[i];
 +        }
 +        else
 +        {
 +            w = 1;
 +        }
 +        if (EI_ENERGY_MINIMIZATION(ir->eI))
 +        {
 +            /* Move the mass to the weight */
 +            w            *= m;
 +            m             = 1;
 +            pg->weight[i] = w;
 +        }
 +        else if (ir->eI == eiBD)
 +        {
 +            if (ir->bd_fric)
 +            {
 +                mbd = ir->bd_fric*ir->delta_t;
 +            }
 +            else
 +            {
 +                if (groups->grpnr[egcTC] == NULL)
 +                {
 +                    mbd = ir->delta_t/ir->opts.tau_t[0];
 +                }
 +                else
 +                {
 +                    mbd = ir->delta_t/ir->opts.tau_t[groups->grpnr[egcTC][ii]];
 +                }
 +            }
 +            w            *= m/mbd;
 +            m             = mbd;
 +            pg->weight[i] = w;
 +        }
 +        tmass  += m;
 +        wmass  += m*w;
 +        wwmass += m*w*w;
 +    }
 +
 +    gmx_mtop_atomlookup_destroy(alook);
 +
 +    if (wmass == 0)
 +    {
 +        gmx_fatal(FARGS, "The total%s mass of pull group %d is zero",
 +                  pg->weight ? " weighted" : "", g);
 +    }
 +    if (fplog)
 +    {
 +        fprintf(fplog,
 +                "Pull group %d: %5d atoms, mass %9.3f", g, pg->nat, tmass);
 +        if (pg->weight || EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
 +        {
 +            fprintf(fplog, ", weighted mass %9.3f", wmass*wmass/wwmass);
 +        }
 +        if (pg->epgrppbc == epgrppbcCOS)
 +        {
 +            fprintf(fplog, ", cosine weighting will be used");
 +        }
 +        fprintf(fplog, "\n");
 +    }
 +
 +    if (nfrozen == 0)
 +    {
 +        /* A value > 0 signals not frozen, it is updated later */
 +        pg->invtm  = 1.0;
 +    }
 +    else
 +    {
 +        ndim = 0;
 +        for (d = 0; d < DIM; d++)
 +        {
 +            ndim += pulldims[d]*pg->nat;
 +        }
 +        if (fplog && nfrozen > 0 && nfrozen < ndim)
 +        {
 +            fprintf(fplog,
 +                    "\nWARNING: In pull group %d some, but not all of the degrees of freedom\n"
 +                    "         that are subject to pulling are frozen.\n"
 +                    "         For pulling the whole group will be frozen.\n\n",
 +                    g);
 +        }
 +        pg->invtm  = 0.0;
 +        pg->wscale = 1.0;
 +    }
 +}
 +
 +void init_pull(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
 +               gmx_mtop_t *mtop, t_commrec *cr, const output_env_t oenv, real lambda,
 +               gmx_bool bOutFile, unsigned long Flags)
 +{
 +    t_pull       *pull;
 +    t_pull_group *pgrp;
 +    int           c, g, start = 0, end = 0, m;
 +
 +    pull = ir->pull;
 +
 +    pull->ePBC = ir->ePBC;
 +    switch (pull->ePBC)
 +    {
 +        case epbcNONE: pull->npbcdim = 0; break;
 +        case epbcXY:   pull->npbcdim = 2; break;
 +        default:       pull->npbcdim = 3; break;
 +    }
 +
 +    if (fplog)
 +    {
 +        gmx_bool bAbs, bCos;
 +
 +        bAbs = FALSE;
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            if (pull->group[pull->coord[c].group[0]].nat == 0 ||
 +                pull->group[pull->coord[c].group[1]].nat == 0)
 +            {
 +                bAbs = TRUE;
 +            }
 +        }
 +
 +        fprintf(fplog, "\nWill apply %s COM pulling in geometry '%s'\n",
 +                EPULLTYPE(ir->ePull), EPULLGEOM(pull->eGeom));
 +        fprintf(fplog, "with %d pull coordinate%s and %d group%s\n",
 +                pull->ncoord, pull->ncoord == 1 ? "" : "s",
 +                pull->ngroup, pull->ngroup == 1 ? "" : "s");
 +        if (bAbs)
 +        {
 +            fprintf(fplog, "with an absolute reference\n");
 +        }
 +        bCos = FALSE;
 +        for (g = 0; g < pull->ngroup; g++)
 +        {
 +            if (pull->group[g].nat > 1 &&
 +                pull->group[g].pbcatom < 0)
 +            {
 +                /* We are using cosine weighting */
 +                fprintf(fplog, "Cosine weighting is used for group %d\n", g);
 +                bCos = TRUE;
 +            }
 +        }
 +        if (bCos)
 +        {
 +            please_cite(fplog, "Engin2010");
 +        }
 +    }
 +
 +    /* We always add the virial contribution,
 +     * except for geometry = direction_periodic where this is impossible.
 +     */
 +    pull->bVirial = (pull->eGeom != epullgDIRPBC);
 +    if (getenv("GMX_NO_PULLVIR") != NULL)
 +    {
 +        if (fplog)
 +        {
 +            fprintf(fplog, "Found env. var., will not add the virial contribution of the COM pull forces\n");
 +        }
 +        pull->bVirial = FALSE;
 +    }
 +
 +    pull->rbuf     = NULL;
 +    pull->dbuf     = NULL;
 +    pull->dbuf_cyl = NULL;
 +    pull->bRefAt   = FALSE;
 +    pull->cosdim   = -1;
 +    for (g = 0; g < pull->ngroup; g++)
 +    {
 +        pgrp           = &pull->group[g];
 +        pgrp->epgrppbc = epgrppbcNONE;
 +        if (pgrp->nat > 0)
 +        {
 +            /* Determine if we need to take PBC into account for calculating
 +             * the COM's of the pull groups.
 +             */
 +            for (m = 0; m < pull->npbcdim; m++)
 +            {
 +                if (pull->dim[m] && pgrp->nat > 1)
 +                {
 +                    if (pgrp->pbcatom >= 0)
 +                    {
 +                        pgrp->epgrppbc = epgrppbcREFAT;
 +                        pull->bRefAt   = TRUE;
 +                    }
 +                    else
 +                    {
 +                        if (pgrp->weight)
 +                        {
 +                            gmx_fatal(FARGS, "Pull groups can not have relative weights and cosine weighting at same time");
 +                        }
 +                        pgrp->epgrppbc = epgrppbcCOS;
 +                        if (pull->cosdim >= 0 && pull->cosdim != m)
 +                        {
 +                            gmx_fatal(FARGS, "Can only use cosine weighting with pulling in one dimension (use mdp option pull_dim)");
 +                        }
 +                        pull->cosdim = m;
 +                    }
 +                }
 +            }
 +            /* Set the indices */
 +            init_pull_group_index(fplog, cr, g, pgrp, pull->dim, mtop, ir, lambda);
 +            if (PULL_CYL(pull) && pgrp->invtm == 0)
 +            {
 +                gmx_fatal(FARGS, "Can not have frozen atoms in a cylinder pull group");
 +            }
 +        }
 +        else
 +        {
 +            /* Absolute reference, set the inverse mass to zero */
 +            pgrp->invtm  = 0;
 +            pgrp->wscale = 1;
 +        }
 +    }
 +
 +    /* if we use dynamic reference groups, do some initialising for them */
 +    if (PULL_CYL(pull))
 +    {
 +        if (ir->ePull == epullCONSTRAINT && pull->ncoord > 1)
 +        {
 +            /* We can't easily update the single reference group with multiple
 +             * constraints. This would require recalculating COMs.
 +             */
 +            gmx_fatal(FARGS, "Constraint COM pulling supports only one coordinate with geometry=cylinder, you can use umbrella pulling with multiple coordinates");
 +        }
 +
 +        for (c = 0; c < pull->ncoord; c++)
 +        {
 +            if (pull->group[pull->coord[c].group[0]].nat == 0)
 +            {
 +                gmx_fatal(FARGS, "Dynamic reference groups are not supported when using absolute reference!\n");
 +            }
 +        }
 +
 +        snew(pull->dyna, pull->ncoord);
 +    }
 +
 +    /* Only do I/O when we are doing dynamics and if we are the MASTER */
 +    pull->out_x = NULL;
 +    pull->out_f = NULL;
 +    if (bOutFile)
 +    {
 +        if (pull->nstxout > 0)
 +        {
 +            pull->out_x = open_pull_out(opt2fn("-px", nfile, fnm), pull, oenv, TRUE, Flags);
 +        }
 +        if (pull->nstfout > 0)
 +        {
 +            pull->out_f = open_pull_out(opt2fn("-pf", nfile, fnm), pull, oenv,
 +                                        FALSE, Flags);
 +        }
 +    }
 +}
 +
 +void finish_pull(t_pull *pull)
 +{
 +    if (pull->out_x)
 +    {
 +        gmx_fio_fclose(pull->out_x);
 +    }
 +    if (pull->out_f)
 +    {
 +        gmx_fio_fclose(pull->out_f);
 +    }
 +}
index fd2297792588e6a62d3cc01df957219ff60752e8,0000000000000000000000000000000000000000..cbbf9f8ee473253c8c4c12adaea955b9dcfeb0f9
mode 100644,000000..100644
--- /dev/null
@@@ -1,1486 -1,0 +1,1493 @@@
-         cmp_rvecs_rmstol(fp, "f", min(fr1->natoms, fr2->natoms), fr1->f, fr2->f, ftol, abstol);
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +/* This file is completely threadsafe - keep it that way! */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <stdio.h>
 +#include <string.h>
 +#include "main.h"
 +#include "macros.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "gromacs/fileio/futil.h"
 +#include "sysstuff.h"
 +#include "txtdump.h"
 +#include "gmx_fatal.h"
 +#include "names.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "gromacs/fileio/enxio.h"
 +#include "mtop_util.h"
 +#include "gromacs/utility/cstringutil.h"
 +
 +static void cmp_int(FILE *fp, const char *s, int index, int i1, int i2)
 +{
 +    if (i1 != i2)
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%d] (%d - %d)\n", s, index, i1, i2);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%d - %d)\n", s, i1, i2);
 +        }
 +    }
 +}
 +
 +static void cmp_int64(FILE *fp, const char *s, gmx_int64_t i1, gmx_int64_t i2)
 +{
 +    if (i1 != i2)
 +    {
 +        fprintf(fp, "%s (", s);
 +        fprintf(fp, "%"GMX_PRId64, i1);
 +        fprintf(fp, " - ");
 +        fprintf(fp, "%"GMX_PRId64, i2);
 +        fprintf(fp, ")\n");
 +    }
 +}
 +
 +static void cmp_us(FILE *fp, const char *s, int index, unsigned short i1, unsigned short i2)
 +{
 +    if (i1 != i2)
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%d] (%u - %u)\n", s, index, i1, i2);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%u - %u)\n", s, i1, i2);
 +        }
 +    }
 +}
 +
 +static void cmp_uc(FILE *fp, const char *s, int index, unsigned char i1, unsigned char i2)
 +{
 +    if (i1 != i2)
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%d] (%d - %d)\n", s, index, i1, i2);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%d - %d)\n", s, i1, i2);
 +        }
 +    }
 +}
 +
 +static gmx_bool cmp_bool(FILE *fp, const char *s, int index, gmx_bool b1, gmx_bool b2)
 +{
 +    if (b1)
 +    {
 +        b1 = 1;
 +    }
 +    else
 +    {
 +        b1 = 0;
 +    }
 +    if (b2)
 +    {
 +        b2 = 1;
 +    }
 +    else
 +    {
 +        b2 = 0;
 +    }
 +    if (b1 != b2)
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%d] (%s - %s)\n", s, index,
 +                    bool_names[b1], bool_names[b2]);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%s - %s)\n", s,
 +                    bool_names[b1], bool_names[b2]);
 +        }
 +    }
 +    return b1 && b2;
 +}
 +
 +static void cmp_str(FILE *fp, const char *s, int index,
 +                    const char *s1, const char *s2)
 +{
 +    if (strcmp(s1, s2) != 0)
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%d] (%s - %s)\n", s, index, s1, s2);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%s - %s)\n", s, s1, s2);
 +        }
 +    }
 +}
 +
 +static gmx_bool equal_real(real i1, real i2, real ftol, real abstol)
 +{
 +    return ( ( 2*fabs(i1 - i2) <= (fabs(i1) + fabs(i2))*ftol ) || fabs(i1-i2) <= abstol );
 +}
 +
 +static gmx_bool equal_float(float i1, float i2, float ftol, float abstol)
 +{
 +    return ( ( 2*fabs(i1 - i2) <= (fabs(i1) + fabs(i2))*ftol ) || fabs(i1-i2) <= abstol );
 +}
 +
 +static gmx_bool equal_double(double i1, double i2, real ftol, real abstol)
 +{
 +    return ( ( 2*fabs(i1 - i2) <= (fabs(i1) + fabs(i2))*ftol ) || fabs(i1-i2) <= abstol );
 +}
 +
 +static void
 +cmp_real(FILE *fp, const char *s, int index, real i1, real i2, real ftol, real abstol)
 +{
 +    if (!equal_real(i1, i2, ftol, abstol))
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%2d] (%e - %e)\n", s, index, i1, i2);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%e - %e)\n", s, i1, i2);
 +        }
 +    }
 +}
 +
 +static void
 +cmp_float(FILE *fp, const char *s, int index, float i1, float i2, float ftol, float abstol)
 +{
 +    if (!equal_float(i1, i2, ftol, abstol))
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%2d] (%e - %e)\n", s, index, i1, i2);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%e - %e)\n", s, i1, i2);
 +        }
 +    }
 +}
 +
 +
 +
 +static void
 +cmp_double(FILE *fp, const char *s, int index, double i1, double i2, double ftol, double abstol)
 +{
 +    if (!equal_double(i1, i2, ftol, abstol))
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%2d] (%16.9e - %16.9e)\n", s, index, i1, i2);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%16.9e - %16.9e)\n", s, i1, i2);
 +        }
 +    }
 +}
 +
 +static void cmp_rvec(FILE *fp, const char *s, int index, rvec i1, rvec i2, real ftol, real abstol)
 +{
 +    if (!equal_real(i1[XX], i2[XX], ftol, abstol) ||
 +        !equal_real(i1[YY], i2[YY], ftol, abstol) ||
 +        !equal_real(i1[ZZ], i2[ZZ], ftol, abstol))
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%5d] (%12.5e %12.5e %12.5e) - (%12.5e %12.5e %12.5e)\n",
 +                    s, index, i1[XX], i1[YY], i1[ZZ], i2[XX], i2[YY], i2[ZZ]);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%12.5e %12.5e %12.5e) - (%12.5e %12.5e %12.5e)\n",
 +                    s, i1[XX], i1[YY], i1[ZZ], i2[XX], i2[YY], i2[ZZ]);
 +        }
 +    }
 +}
 +
 +static void cmp_ivec(FILE *fp, const char *s, int index, ivec i1, ivec i2)
 +{
 +    if ((i1[XX] != i2[XX]) || (i1[YY] != i2[YY]) || (i1[ZZ] != i2[ZZ]))
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%5d] (%8d,%8d,%8d - %8d,%8d,%8d)\n", s, index,
 +                    i1[XX], i1[YY], i1[ZZ], i2[XX], i2[YY], i2[ZZ]);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%8d,%8d,%8d - %8d,%8d,%8d)\n", s,
 +                    i1[XX], i1[YY], i1[ZZ], i2[XX], i2[YY], i2[ZZ]);
 +        }
 +    }
 +}
 +
 +static void cmp_ilist(FILE *fp, int ftype, t_ilist *il1, t_ilist *il2)
 +{
 +    int  i;
 +    char buf[256];
 +
 +    fprintf(fp, "comparing ilist %s\n", interaction_function[ftype].name);
 +    sprintf(buf, "%s->nr", interaction_function[ftype].name);
 +    cmp_int(fp, buf, -1, il1->nr, il2->nr);
 +    sprintf(buf, "%s->iatoms", interaction_function[ftype].name);
 +    if (((il1->nr > 0) && (!il1->iatoms)) ||
 +        ((il2->nr > 0) && (!il2->iatoms)) ||
 +        ((il1->nr != il2->nr)))
 +    {
 +        fprintf(fp, "Comparing radically different topologies - %s is different\n",
 +                buf);
 +    }
 +    else
 +    {
 +        for (i = 0; (i < il1->nr); i++)
 +        {
 +            cmp_int(fp, buf, i, il1->iatoms[i], il2->iatoms[i]);
 +        }
 +    }
 +}
 +
 +void cmp_iparm(FILE *fp, const char *s, t_functype ft,
 +               t_iparams ip1, t_iparams ip2, real ftol, real abstol)
 +{
 +    int      i;
 +    gmx_bool bDiff;
 +
 +    bDiff = FALSE;
 +    for (i = 0; i < MAXFORCEPARAM && !bDiff; i++)
 +    {
 +        bDiff = !equal_real(ip1.generic.buf[i], ip2.generic.buf[i], ftol, abstol);
 +    }
 +    if (bDiff)
 +    {
 +        fprintf(fp, "%s1: ", s);
 +        pr_iparams(fp, ft, &ip1);
 +        fprintf(fp, "%s2: ", s);
 +        pr_iparams(fp, ft, &ip2);
 +    }
 +}
 +
 +void cmp_iparm_AB(FILE *fp, const char *s, t_functype ft, t_iparams ip1, real ftol, real abstol)
 +{
 +    int      nrfpA, nrfpB, p0, i;
 +    gmx_bool bDiff;
 +
 +    /* Normally the first parameter is perturbable */
 +    p0    = 0;
 +    nrfpA = interaction_function[ft].nrfpA;
 +    nrfpB = interaction_function[ft].nrfpB;
 +    if (ft == F_PDIHS)
 +    {
 +        nrfpB = 2;
 +    }
 +    else if (interaction_function[ft].flags & IF_TABULATED)
 +    {
 +        /* For tabulated interactions only the second parameter is perturbable */
 +        p0    = 1;
 +        nrfpB = 1;
 +    }
 +    bDiff = FALSE;
 +    for (i = 0; i < nrfpB && !bDiff; i++)
 +    {
 +        bDiff = !equal_real(ip1.generic.buf[p0+i], ip1.generic.buf[nrfpA+i], ftol, abstol);
 +    }
 +    if (bDiff)
 +    {
 +        fprintf(fp, "%s: ", s);
 +        pr_iparams(fp, ft, &ip1);
 +    }
 +}
 +
 +static void cmp_idef(FILE *fp, t_idef *id1, t_idef *id2, real ftol, real abstol)
 +{
 +    int  i;
 +    char buf1[64], buf2[64];
 +
 +    fprintf(fp, "comparing idef\n");
 +    if (id2)
 +    {
 +        cmp_int(fp, "idef->ntypes", -1, id1->ntypes, id2->ntypes);
 +        cmp_int(fp, "idef->atnr",  -1, id1->atnr, id2->atnr);
 +        for (i = 0; (i < min(id1->ntypes, id2->ntypes)); i++)
 +        {
 +            sprintf(buf1, "idef->functype[%d]", i);
 +            sprintf(buf2, "idef->iparam[%d]", i);
 +            cmp_int(fp, buf1, i, (int)id1->functype[i], (int)id2->functype[i]);
 +            cmp_iparm(fp, buf2, id1->functype[i],
 +                      id1->iparams[i], id2->iparams[i], ftol, abstol);
 +        }
 +        cmp_real(fp, "fudgeQQ", -1, id1->fudgeQQ, id2->fudgeQQ, ftol, abstol);
 +        for (i = 0; (i < F_NRE); i++)
 +        {
 +            cmp_ilist(fp, i, &(id1->il[i]), &(id2->il[i]));
 +        }
 +    }
 +    else
 +    {
 +        for (i = 0; (i < id1->ntypes); i++)
 +        {
 +            cmp_iparm_AB(fp, "idef->iparam", id1->functype[i], id1->iparams[i], ftol, abstol);
 +        }
 +    }
 +}
 +
 +static void cmp_block(FILE *fp, t_block *b1, t_block *b2, const char *s)
 +{
 +    int  i, j, k;
 +    char buf[32];
 +
 +    fprintf(fp, "comparing block %s\n", s);
 +    sprintf(buf, "%s.nr", s);
 +    cmp_int(fp, buf, -1, b1->nr, b2->nr);
 +}
 +
 +static void cmp_blocka(FILE *fp, t_blocka *b1, t_blocka *b2, const char *s)
 +{
 +    int  i, j, k;
 +    char buf[32];
 +
 +    fprintf(fp, "comparing blocka %s\n", s);
 +    sprintf(buf, "%s.nr", s);
 +    cmp_int(fp, buf, -1, b1->nr, b2->nr);
 +    sprintf(buf, "%s.nra", s);
 +    cmp_int(fp, buf, -1, b1->nra, b2->nra);
 +}
 +
 +static void cmp_atom(FILE *fp, int index, t_atom *a1, t_atom *a2, real ftol, real abstol)
 +{
 +    int  i;
 +    char buf[256];
 +
 +    if (a2)
 +    {
 +        cmp_us(fp, "atom.type", index, a1->type, a2->type);
 +        cmp_us(fp, "atom.ptype", index, a1->ptype, a2->ptype);
 +        cmp_int(fp, "atom.resind", index, a1->resind, a2->resind);
 +        cmp_int(fp, "atom.atomnumber", index, a1->atomnumber, a2->atomnumber);
 +        cmp_real(fp, "atom.m", index, a1->m, a2->m, ftol, abstol);
 +        cmp_real(fp, "atom.q", index, a1->q, a2->q, ftol, abstol);
 +        cmp_us(fp, "atom.typeB", index, a1->typeB, a2->typeB);
 +        cmp_real(fp, "atom.mB", index, a1->mB, a2->mB, ftol, abstol);
 +        cmp_real(fp, "atom.qB", index, a1->qB, a2->qB, ftol, abstol);
 +    }
 +    else
 +    {
 +        cmp_us(fp, "atom.type", index, a1->type, a1->typeB);
 +        cmp_real(fp, "atom.m", index, a1->m, a1->mB, ftol, abstol);
 +        cmp_real(fp, "atom.q", index, a1->q, a1->qB, ftol, abstol);
 +    }
 +}
 +
 +static void cmp_atoms(FILE *fp, t_atoms *a1, t_atoms *a2, real ftol, real abstol)
 +{
 +    int i;
 +
 +    fprintf(fp, "comparing atoms\n");
 +
 +    if (a2)
 +    {
 +        cmp_int(fp, "atoms->nr", -1, a1->nr, a2->nr);
 +        for (i = 0; (i < a1->nr); i++)
 +        {
 +            cmp_atom(fp, i, &(a1->atom[i]), &(a2->atom[i]), ftol, abstol);
 +        }
 +    }
 +    else
 +    {
 +        for (i = 0; (i < a1->nr); i++)
 +        {
 +            cmp_atom(fp, i, &(a1->atom[i]), NULL, ftol, abstol);
 +        }
 +    }
 +}
 +
 +static void cmp_top(FILE *fp, t_topology *t1, t_topology *t2, real ftol, real abstol)
 +{
 +    int i;
 +
 +    fprintf(fp, "comparing top\n");
 +    if (t2)
 +    {
 +        cmp_idef(fp, &(t1->idef), &(t2->idef), ftol, abstol);
 +        cmp_atoms(fp, &(t1->atoms), &(t2->atoms), ftol, abstol);
 +        cmp_block(fp, &t1->cgs, &t2->cgs, "cgs");
 +        cmp_block(fp, &t1->mols, &t2->mols, "mols");
 +        cmp_blocka(fp, &t1->excls, &t2->excls, "excls");
 +    }
 +    else
 +    {
 +        cmp_idef(fp, &(t1->idef), NULL, ftol, abstol);
 +        cmp_atoms(fp, &(t1->atoms), NULL, ftol, abstol);
 +    }
 +}
 +
 +static void cmp_groups(FILE *fp, gmx_groups_t *g0, gmx_groups_t *g1,
 +                       int natoms0, int natoms1)
 +{
 +    int  i, j, ndiff;
 +    char buf[32];
 +
 +    fprintf(fp, "comparing groups\n");
 +
 +    for (i = 0; i < egcNR; i++)
 +    {
 +        sprintf(buf, "grps[%d].nr", i);
 +        cmp_int(fp, buf, -1, g0->grps[i].nr, g1->grps[i].nr);
 +        if (g0->grps[i].nr == g1->grps[i].nr)
 +        {
 +            for (j = 0; j < g0->grps[i].nr; j++)
 +            {
 +                sprintf(buf, "grps[%d].name[%d]", i, j);
 +                cmp_str(fp, buf, -1,
 +                        *g0->grpname[g0->grps[i].nm_ind[j]],
 +                        *g1->grpname[g1->grps[i].nm_ind[j]]);
 +            }
 +        }
 +        cmp_int(fp, "ngrpnr", i, g0->ngrpnr[i], g1->ngrpnr[i]);
 +        if (g0->ngrpnr[i] == g1->ngrpnr[i] && natoms0 == natoms1 &&
 +            (g0->grpnr[i] != NULL || g1->grpnr[i] != NULL))
 +        {
 +            for (j = 0; j < natoms0; j++)
 +            {
 +                cmp_int(fp, gtypes[i], j, ggrpnr(g0, i, j), ggrpnr(g1, i, j));
 +            }
 +        }
 +    }
 +    /* We have compared the names in the groups lists,
 +     * so we can skip the grpname list comparison.
 +     */
 +}
 +
 +static void cmp_rvecs(FILE *fp, const char *title, int n, rvec x1[], rvec x2[],
 +                      gmx_bool bRMSD, real ftol, real abstol)
 +{
 +    int    i, m;
 +    double d, ssd;
 +
 +    if (bRMSD)
 +    {
 +        ssd = 0;
 +        for (i = 0; (i < n); i++)
 +        {
 +            for (m = 0; m < DIM; m++)
 +            {
 +                d    = x1[i][m] - x2[i][m];
 +                ssd += d*d;
 +            }
 +        }
 +        fprintf(fp, "%s RMSD %g\n", title, sqrt(ssd/n));
 +    }
 +    else
 +    {
 +        for (i = 0; (i < n); i++)
 +        {
 +            cmp_rvec(fp, title, i, x1[i], x2[i], ftol, abstol);
 +        }
 +    }
 +}
 +
 +
 +/* Similar to cmp_rvecs, but this routine scales the allowed absolute tolerance
 + * by the RMS of the force components of x1.
 + */
 +static void cmp_rvecs_rmstol(FILE *fp, const char *title, int n, rvec x1[], rvec x2[],
 +                             real ftol, real abstol)
 +{
 +    int    i, m;
 +    double d;
 +    double ave_x1, rms_x1;
 +
 +    /* It is tricky to compare real values, in particular forces that
 +     * are sums of lots of terms where the final value might be close to 0.0.
 +     * To get a reference magnitude we calculate the RMS value of each
 +     * component in x1, and then set the allowed absolute tolerance to the
 +     * relative tolerance times this RMS magnitude.
 +     */
 +    ave_x1 = 0.0;
 +    for (i = 0; i < n; i++)
 +    {
 +        for (m = 0; m < DIM; m++)
 +        {
 +            ave_x1 += x1[i][m];
 +        }
 +    }
 +    ave_x1 /= n*DIM;
 +
 +    rms_x1 = 0.0;
 +    for (i = 0; (i < n); i++)
 +    {
 +        for (m = 0; m < DIM; m++)
 +        {
 +            d       = x1[i][m] - ave_x1;
 +            rms_x1 += d*d;
 +        }
 +    }
 +    rms_x1 = sqrt(rms_x1/(DIM*n));
 +    /* And now do the actual comparision with a hopefully realistic abstol. */
 +    for (i = 0; (i < n); i++)
 +    {
 +        cmp_rvec(fp, title, i, x1[i], x2[i], ftol, abstol*rms_x1);
 +    }
 +}
 +
 +static void cmp_grpopts(FILE *fp, t_grpopts *opt1, t_grpopts *opt2, real ftol, real abstol)
 +{
 +    int  i, j;
 +    char buf1[256], buf2[256];
 +
 +    cmp_int(fp, "inputrec->grpopts.ngtc", -1,  opt1->ngtc, opt2->ngtc);
 +    cmp_int(fp, "inputrec->grpopts.ngacc", -1, opt1->ngacc, opt2->ngacc);
 +    cmp_int(fp, "inputrec->grpopts.ngfrz", -1, opt1->ngfrz, opt2->ngfrz);
 +    cmp_int(fp, "inputrec->grpopts.ngener", -1, opt1->ngener, opt2->ngener);
 +    for (i = 0; (i < min(opt1->ngtc, opt2->ngtc)); i++)
 +    {
 +        cmp_real(fp, "inputrec->grpopts.nrdf", i, opt1->nrdf[i], opt2->nrdf[i], ftol, abstol);
 +        cmp_real(fp, "inputrec->grpopts.ref_t", i, opt1->ref_t[i], opt2->ref_t[i], ftol, abstol);
 +        cmp_real(fp, "inputrec->grpopts.tau_t", i, opt1->tau_t[i], opt2->tau_t[i], ftol, abstol);
 +        cmp_int(fp, "inputrec->grpopts.annealing", i, opt1->annealing[i], opt2->annealing[i]);
 +        cmp_int(fp, "inputrec->grpopts.anneal_npoints", i,
 +                opt1->anneal_npoints[i], opt2->anneal_npoints[i]);
 +        if (opt1->anneal_npoints[i] == opt2->anneal_npoints[i])
 +        {
 +            sprintf(buf1, "inputrec->grpopts.anneal_time[%d]", i);
 +            sprintf(buf2, "inputrec->grpopts.anneal_temp[%d]", i);
 +            for (j = 0; j < opt1->anneal_npoints[i]; j++)
 +            {
 +                cmp_real(fp, buf1, j, opt1->anneal_time[i][j], opt2->anneal_time[i][j], ftol, abstol);
 +                cmp_real(fp, buf2, j, opt1->anneal_temp[i][j], opt2->anneal_temp[i][j], ftol, abstol);
 +            }
 +        }
 +    }
 +    if (opt1->ngener == opt2->ngener)
 +    {
 +        for (i = 0; i < opt1->ngener; i++)
 +        {
 +            for (j = i; j < opt1->ngener; j++)
 +            {
 +                sprintf(buf1, "inputrec->grpopts.egp_flags[%d]", i);
 +                cmp_int(fp, buf1, j,
 +                        opt1->egp_flags[opt1->ngener*i+j],
 +                        opt2->egp_flags[opt1->ngener*i+j]);
 +            }
 +        }
 +    }
 +    for (i = 0; (i < min(opt1->ngacc, opt2->ngacc)); i++)
 +    {
 +        cmp_rvec(fp, "inputrec->grpopts.acc", i, opt1->acc[i], opt2->acc[i], ftol, abstol);
 +    }
 +    for (i = 0; (i < min(opt1->ngfrz, opt2->ngfrz)); i++)
 +    {
 +        cmp_ivec(fp, "inputrec->grpopts.nFreeze", i, opt1->nFreeze[i], opt2->nFreeze[i]);
 +    }
 +}
 +
 +static void cmp_cosines(FILE *fp, const char *s, t_cosines c1[DIM], t_cosines c2[DIM], real ftol, real abstol)
 +{
 +    int  i, m;
 +    char buf[256];
 +
 +    for (m = 0; (m < DIM); m++)
 +    {
 +        sprintf(buf, "inputrec->%s[%d]", s, m);
 +        cmp_int(fp, buf, 0, c1->n, c2->n);
 +        for (i = 0; (i < min(c1->n, c2->n)); i++)
 +        {
 +            cmp_real(fp, buf, i, c1->a[i], c2->a[i], ftol, abstol);
 +            cmp_real(fp, buf, i, c1->phi[i], c2->phi[i], ftol, abstol);
 +        }
 +    }
 +}
 +static void cmp_adress(FILE *fp, t_adress *ad1, t_adress *ad2,
 +                       real ftol, real abstol)
 +{
 +    cmp_int(fp, "ir->adress->type", -1, ad1->type, ad2->type);
 +    cmp_real(fp, "ir->adress->const_wf", -1, ad1->const_wf, ad2->const_wf, ftol, abstol);
 +    cmp_real(fp, "ir->adress->ex_width", -1, ad1->ex_width, ad2->ex_width, ftol, abstol);
 +    cmp_real(fp, "ir->adress->hy_width", -1, ad1->hy_width, ad2->hy_width, ftol, abstol);
 +    cmp_int(fp, "ir->adress->icor", -1, ad1->icor, ad2->icor);
 +    cmp_int(fp, "ir->adress->site", -1, ad1->site, ad2->site);
 +    cmp_rvec(fp, "ir->adress->refs", -1, ad1->refs, ad2->refs, ftol, abstol);
 +    cmp_real(fp, "ir->adress->ex_forcecap", -1, ad1->ex_forcecap, ad2->ex_forcecap, ftol, abstol);
 +}
 +
 +static void cmp_pull(FILE *fp)
 +{
 +    fprintf(fp, "WARNING: Both files use COM pulling, but comparing of the pull struct is not implemented (yet). The pull parameters could be the same or different.\n");
 +}
 +
 +static void cmp_simtempvals(FILE *fp, t_simtemp *simtemp1, t_simtemp *simtemp2, int n_lambda, real ftol, real abstol)
 +{
 +    int i;
 +    cmp_int(fp, "inputrec->simtempvals->eSimTempScale", -1, simtemp1->eSimTempScale, simtemp2->eSimTempScale);
 +    cmp_real(fp, "inputrec->simtempvals->simtemp_high", -1, simtemp1->simtemp_high, simtemp2->simtemp_high, ftol, abstol);
 +    cmp_real(fp, "inputrec->simtempvals->simtemp_low", -1, simtemp1->simtemp_low, simtemp2->simtemp_low, ftol, abstol);
 +    for (i = 0; i < n_lambda; i++)
 +    {
 +        cmp_real(fp, "inputrec->simtempvals->temperatures", -1, simtemp1->temperatures[i], simtemp2->temperatures[i], ftol, abstol);
 +    }
 +}
 +
 +static void cmp_expandedvals(FILE *fp, t_expanded *expand1, t_expanded *expand2, int n_lambda, real ftol, real abstol)
 +{
 +    int i;
 +
 +    cmp_bool(fp, "inputrec->fepvals->bInit_weights", -1, expand1->bInit_weights, expand2->bInit_weights);
 +    cmp_bool(fp, "inputrec->fepvals->bWLoneovert", -1, expand1->bWLoneovert, expand2->bWLoneovert);
 +
 +    for (i = 0; i < n_lambda; i++)
 +    {
 +        cmp_real(fp, "inputrec->expandedvals->init_lambda_weights", -1,
 +                 expand1->init_lambda_weights[i], expand2->init_lambda_weights[i], ftol, abstol);
 +    }
 +
 +    cmp_int(fp, "inputrec->expandedvals->lambda-stats", -1, expand1->elamstats, expand2->elamstats);
 +    cmp_int(fp, "inputrec->expandedvals->lambda-mc-move", -1, expand1->elmcmove, expand2->elmcmove);
 +    cmp_int(fp, "inputrec->expandedvals->lmc-repeats", -1, expand1->lmc_repeats, expand2->lmc_repeats);
 +    cmp_int(fp, "inputrec->expandedvals->lmc-gibbsdelta", -1, expand1->gibbsdeltalam, expand2->gibbsdeltalam);
 +    cmp_int(fp, "inputrec->expandedvals->lmc-forced-nstart", -1, expand1->lmc_forced_nstart, expand2->lmc_forced_nstart);
 +    cmp_int(fp, "inputrec->expandedvals->lambda-weights-equil", -1, expand1->elmceq, expand2->elmceq);
 +    cmp_int(fp, "inputrec->expandedvals->,weight-equil-number-all-lambda", -1, expand1->equil_n_at_lam, expand2->equil_n_at_lam);
 +    cmp_int(fp, "inputrec->expandedvals->weight-equil-number-samples", -1, expand1->equil_samples, expand2->equil_samples);
 +    cmp_int(fp, "inputrec->expandedvals->weight-equil-number-steps", -1, expand1->equil_steps, expand2->equil_steps);
 +    cmp_real(fp, "inputrec->expandedvals->weight-equil-wl-delta", -1, expand1->equil_wl_delta, expand2->equil_wl_delta, ftol, abstol);
 +    cmp_real(fp, "inputrec->expandedvals->weight-equil-count-ratio", -1, expand1->equil_ratio, expand2->equil_ratio, ftol, abstol);
 +    cmp_bool(fp, "inputrec->expandedvals->symmetrized-transition-matrix", -1, expand1->bSymmetrizedTMatrix, expand2->bSymmetrizedTMatrix);
 +    cmp_int(fp, "inputrec->expandedvals->nstTij", -1, expand1->nstTij, expand2->nstTij);
 +    cmp_int(fp, "inputrec->expandedvals->mininum-var-min", -1, expand1->minvarmin, expand2->minvarmin); /*default is reasonable */
 +    cmp_int(fp, "inputrec->expandedvals->weight-c-range", -1, expand1->c_range, expand2->c_range);      /* default is just C=0 */
 +    cmp_real(fp, "inputrec->expandedvals->wl-scale", -1, expand1->wl_scale, expand2->wl_scale, ftol, abstol);
 +    cmp_real(fp, "inputrec->expandedvals->init-wl-delta", -1, expand1->init_wl_delta, expand2->init_wl_delta, ftol, abstol);
 +    cmp_real(fp, "inputrec->expandedvals->wl-ratio", -1, expand1->wl_ratio, expand2->wl_ratio, ftol, abstol);
 +    cmp_int(fp, "inputrec->expandedvals->nstexpanded", -1, expand1->nstexpanded, expand2->nstexpanded);
 +    cmp_int(fp, "inputrec->expandedvals->lmc-seed", -1, expand1->lmc_seed, expand2->lmc_seed);
 +    cmp_real(fp, "inputrec->expandedvals->mc-temperature", -1, expand1->mc_temp, expand2->mc_temp, ftol, abstol);
 +}
 +
 +static void cmp_fepvals(FILE *fp, t_lambda *fep1, t_lambda *fep2, real ftol, real abstol)
 +{
 +    int i, j;
 +    cmp_int(fp, "inputrec->nstdhdl", -1, fep1->nstdhdl, fep2->nstdhdl);
 +    cmp_double(fp, "inputrec->fepvals->init_fep_state", -1, fep1->init_fep_state, fep2->init_fep_state, ftol, abstol);
 +    cmp_double(fp, "inputrec->fepvals->delta_lambda", -1, fep1->delta_lambda, fep2->delta_lambda, ftol, abstol);
 +    cmp_int(fp, "inputrec->fepvals->n_lambda", -1, fep1->n_lambda, fep2->n_lambda);
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        for (j = 0; j < min(fep1->n_lambda, fep2->n_lambda); j++)
 +        {
 +            cmp_double(fp, "inputrec->fepvals->all_lambda", -1, fep1->all_lambda[i][j], fep2->all_lambda[i][j], ftol, abstol);
 +        }
 +    }
 +    cmp_int(fp, "inputrec->fepvals->lambda_neighbors", 1, fep1->lambda_neighbors,
 +            fep2->lambda_neighbors);
 +    cmp_real(fp, "inputrec->fepvals->sc_alpha", -1, fep1->sc_alpha, fep2->sc_alpha, ftol, abstol);
 +    cmp_int(fp, "inputrec->fepvals->sc_power", -1, fep1->sc_power, fep2->sc_power);
 +    cmp_real(fp, "inputrec->fepvals->sc_r_power", -1, fep1->sc_r_power, fep2->sc_r_power, ftol, abstol);
 +    cmp_real(fp, "inputrec->fepvals->sc_sigma", -1, fep1->sc_sigma, fep2->sc_sigma, ftol, abstol);
 +    cmp_bool(fp, "inputrec->fepvals->bPrintEnergy", -1, fep1->bPrintEnergy, fep1->bPrintEnergy);
 +    cmp_bool(fp, "inputrec->fepvals->bScCoul", -1, fep1->bScCoul, fep1->bScCoul);
 +    cmp_int(fp, "inputrec->separate_dhdl_file", -1, fep1->separate_dhdl_file, fep2->separate_dhdl_file);
 +    cmp_int(fp, "inputrec->dhdl_derivatives", -1, fep1->dhdl_derivatives, fep2->dhdl_derivatives);
 +    cmp_int(fp, "inputrec->dh_hist_size", -1, fep1->dh_hist_size, fep2->dh_hist_size);
 +    cmp_double(fp, "inputrec->dh_hist_spacing", -1, fep1->dh_hist_spacing, fep2->dh_hist_spacing, ftol, abstol);
 +}
 +
 +static void cmp_inputrec(FILE *fp, t_inputrec *ir1, t_inputrec *ir2, real ftol, real abstol)
 +{
 +    fprintf(fp, "comparing inputrec\n");
 +
 +    /* gcc 2.96 doesnt like these defines at all, but issues a huge list
 +     * of warnings. Maybe it will change in future versions, but for the
 +     * moment I've spelled them out instead. /EL 000820
 +     * #define CIB(s) cmp_int(fp,"inputrec->"#s,0,ir1->##s,ir2->##s)
 +     * #define CII(s) cmp_int(fp,"inputrec->"#s,0,ir1->##s,ir2->##s)
 +     * #define CIR(s) cmp_real(fp,"inputrec->"#s,0,ir1->##s,ir2->##s,ftol)
 +     */
 +    cmp_int(fp, "inputrec->eI", -1, ir1->eI, ir2->eI);
 +    cmp_int64(fp, "inputrec->nsteps", ir1->nsteps, ir2->nsteps);
 +    cmp_int64(fp, "inputrec->init_step", ir1->init_step, ir2->init_step);
 +    cmp_int(fp, "inputrec->simulation_part", -1, ir1->simulation_part, ir2->simulation_part);
 +    cmp_int(fp, "inputrec->ePBC", -1, ir1->ePBC, ir2->ePBC);
 +    cmp_int(fp, "inputrec->bPeriodicMols", -1, ir1->bPeriodicMols, ir2->bPeriodicMols);
 +    cmp_int(fp, "inputrec->cutoff_scheme", -1, ir1->cutoff_scheme, ir2->cutoff_scheme);
 +    cmp_int(fp, "inputrec->ns_type", -1, ir1->ns_type, ir2->ns_type);
 +    cmp_int(fp, "inputrec->nstlist", -1, ir1->nstlist, ir2->nstlist);
 +    cmp_int(fp, "inputrec->nstcomm", -1, ir1->nstcomm, ir2->nstcomm);
 +    cmp_int(fp, "inputrec->comm_mode", -1, ir1->comm_mode, ir2->comm_mode);
 +    cmp_int(fp, "inputrec->nstlog", -1, ir1->nstlog, ir2->nstlog);
 +    cmp_int(fp, "inputrec->nstxout", -1, ir1->nstxout, ir2->nstxout);
 +    cmp_int(fp, "inputrec->nstvout", -1, ir1->nstvout, ir2->nstvout);
 +    cmp_int(fp, "inputrec->nstfout", -1, ir1->nstfout, ir2->nstfout);
 +    cmp_int(fp, "inputrec->nstcalcenergy", -1, ir1->nstcalcenergy, ir2->nstcalcenergy);
 +    cmp_int(fp, "inputrec->nstenergy", -1, ir1->nstenergy, ir2->nstenergy);
 +    cmp_int(fp, "inputrec->nstxout_compressed", -1, ir1->nstxout_compressed, ir2->nstxout_compressed);
 +    cmp_double(fp, "inputrec->init_t", -1, ir1->init_t, ir2->init_t, ftol, abstol);
 +    cmp_double(fp, "inputrec->delta_t", -1, ir1->delta_t, ir2->delta_t, ftol, abstol);
 +    cmp_real(fp, "inputrec->x_compression_precision", -1, ir1->x_compression_precision, ir2->x_compression_precision, ftol, abstol);
 +    cmp_real(fp, "inputrec->fourierspacing", -1, ir1->fourier_spacing, ir2->fourier_spacing, ftol, abstol);
 +    cmp_int(fp, "inputrec->nkx", -1, ir1->nkx, ir2->nkx);
 +    cmp_int(fp, "inputrec->nky", -1, ir1->nky, ir2->nky);
 +    cmp_int(fp, "inputrec->nkz", -1, ir1->nkz, ir2->nkz);
 +    cmp_int(fp, "inputrec->pme_order", -1, ir1->pme_order, ir2->pme_order);
 +    cmp_real(fp, "inputrec->ewald_rtol", -1, ir1->ewald_rtol, ir2->ewald_rtol, ftol, abstol);
 +    cmp_int(fp, "inputrec->ewald_geometry", -1, ir1->ewald_geometry, ir2->ewald_geometry);
 +    cmp_real(fp, "inputrec->epsilon_surface", -1, ir1->epsilon_surface, ir2->epsilon_surface, ftol, abstol);
 +    cmp_int(fp, "inputrec->bContinuation", -1, ir1->bContinuation, ir2->bContinuation);
 +    cmp_int(fp, "inputrec->bShakeSOR", -1, ir1->bShakeSOR, ir2->bShakeSOR);
 +    cmp_int(fp, "inputrec->etc", -1, ir1->etc, ir2->etc);
 +    cmp_int(fp, "inputrec->bPrintNHChains", -1, ir1->bPrintNHChains, ir2->bPrintNHChains);
 +    cmp_int(fp, "inputrec->epc", -1, ir1->epc, ir2->epc);
 +    cmp_int(fp, "inputrec->epct", -1, ir1->epct, ir2->epct);
 +    cmp_real(fp, "inputrec->tau_p", -1, ir1->tau_p, ir2->tau_p, ftol, abstol);
 +    cmp_rvec(fp, "inputrec->ref_p(x)", -1, ir1->ref_p[XX], ir2->ref_p[XX], ftol, abstol);
 +    cmp_rvec(fp, "inputrec->ref_p(y)", -1, ir1->ref_p[YY], ir2->ref_p[YY], ftol, abstol);
 +    cmp_rvec(fp, "inputrec->ref_p(z)", -1, ir1->ref_p[ZZ], ir2->ref_p[ZZ], ftol, abstol);
 +    cmp_rvec(fp, "inputrec->compress(x)", -1, ir1->compress[XX], ir2->compress[XX], ftol, abstol);
 +    cmp_rvec(fp, "inputrec->compress(y)", -1, ir1->compress[YY], ir2->compress[YY], ftol, abstol);
 +    cmp_rvec(fp, "inputrec->compress(z)", -1, ir1->compress[ZZ], ir2->compress[ZZ], ftol, abstol);
 +    cmp_int(fp, "refcoord_scaling", -1, ir1->refcoord_scaling, ir2->refcoord_scaling);
 +    cmp_rvec(fp, "inputrec->posres_com", -1, ir1->posres_com, ir2->posres_com, ftol, abstol);
 +    cmp_rvec(fp, "inputrec->posres_comB", -1, ir1->posres_comB, ir2->posres_comB, ftol, abstol);
 +    cmp_real(fp, "inputrec->verletbuf_tol", -1, ir1->verletbuf_tol, ir2->verletbuf_tol, ftol, abstol);
 +    cmp_real(fp, "inputrec->rlist", -1, ir1->rlist, ir2->rlist, ftol, abstol);
 +    cmp_real(fp, "inputrec->rlistlong", -1, ir1->rlistlong, ir2->rlistlong, ftol, abstol);
 +    cmp_int(fp, "inputrec->nstcalclr", -1, ir1->nstcalclr, ir2->nstcalclr);
 +    cmp_real(fp, "inputrec->rtpi", -1, ir1->rtpi, ir2->rtpi, ftol, abstol);
 +    cmp_int(fp, "inputrec->coulombtype", -1, ir1->coulombtype, ir2->coulombtype);
 +    cmp_int(fp, "inputrec->coulomb_modifier", -1, ir1->coulomb_modifier, ir2->coulomb_modifier);
 +    cmp_real(fp, "inputrec->rcoulomb_switch", -1, ir1->rcoulomb_switch, ir2->rcoulomb_switch, ftol, abstol);
 +    cmp_real(fp, "inputrec->rcoulomb", -1, ir1->rcoulomb, ir2->rcoulomb, ftol, abstol);
 +    cmp_int(fp, "inputrec->vdwtype", -1, ir1->vdwtype, ir2->vdwtype);
 +    cmp_int(fp, "inputrec->vdw_modifier", -1, ir1->vdw_modifier, ir2->vdw_modifier);  cmp_real(fp, "inputrec->rvdw_switch", -1, ir1->rvdw_switch, ir2->rvdw_switch, ftol, abstol);
 +    cmp_real(fp, "inputrec->rvdw", -1, ir1->rvdw, ir2->rvdw, ftol, abstol);
 +    cmp_real(fp, "inputrec->epsilon_r", -1, ir1->epsilon_r, ir2->epsilon_r, ftol, abstol);
 +    cmp_real(fp, "inputrec->epsilon_rf", -1, ir1->epsilon_rf, ir2->epsilon_rf, ftol, abstol);
 +    cmp_real(fp, "inputrec->tabext", -1, ir1->tabext, ir2->tabext, ftol, abstol);
 +    cmp_int(fp, "inputrec->implicit_solvent", -1, ir1->implicit_solvent, ir2->implicit_solvent);
 +    cmp_int(fp, "inputrec->gb_algorithm", -1, ir1->gb_algorithm, ir2->gb_algorithm);
 +    cmp_int(fp, "inputrec->nstgbradii", -1, ir1->nstgbradii, ir2->nstgbradii);
 +    cmp_real(fp, "inputrec->rgbradii", -1, ir1->rgbradii, ir2->rgbradii, ftol, abstol);
 +    cmp_real(fp, "inputrec->gb_saltconc", -1, ir1->gb_saltconc, ir2->gb_saltconc, ftol, abstol);
 +    cmp_real(fp, "inputrec->gb_epsilon_solvent", -1, ir1->gb_epsilon_solvent, ir2->gb_epsilon_solvent, ftol, abstol);
 +    cmp_real(fp, "inputrec->gb_obc_alpha", -1, ir1->gb_obc_alpha, ir2->gb_obc_alpha, ftol, abstol);
 +    cmp_real(fp, "inputrec->gb_obc_beta", -1, ir1->gb_obc_beta, ir2->gb_obc_beta, ftol, abstol);
 +    cmp_real(fp, "inputrec->gb_obc_gamma", -1, ir1->gb_obc_gamma, ir2->gb_obc_gamma, ftol, abstol);
 +    cmp_real(fp, "inputrec->gb_dielectric_offset", -1, ir1->gb_dielectric_offset, ir2->gb_dielectric_offset, ftol, abstol);
 +    cmp_int(fp, "inputrec->sa_algorithm", -1, ir1->sa_algorithm, ir2->sa_algorithm);
 +    cmp_real(fp, "inputrec->sa_surface_tension", -1, ir1->sa_surface_tension, ir2->sa_surface_tension, ftol, abstol);
 +
 +    cmp_int(fp, "inputrec->eDispCorr", -1, ir1->eDispCorr, ir2->eDispCorr);
 +    cmp_real(fp, "inputrec->shake_tol", -1, ir1->shake_tol, ir2->shake_tol, ftol, abstol);
 +    cmp_int(fp, "inputrec->efep", -1, ir1->efep, ir2->efep);
 +    cmp_fepvals(fp, ir1->fepvals, ir2->fepvals, ftol, abstol);
 +    cmp_int(fp, "inputrec->bSimTemp", -1, ir1->bSimTemp, ir2->bSimTemp);
 +    if ((ir1->bSimTemp == ir2->bSimTemp) && (ir1->bSimTemp))
 +    {
 +        cmp_simtempvals(fp, ir1->simtempvals, ir2->simtempvals, min(ir1->fepvals->n_lambda, ir2->fepvals->n_lambda), ftol, abstol);
 +    }
 +    cmp_int(fp, "inputrec->bExpanded", -1, ir1->bExpanded, ir2->bExpanded);
 +    if ((ir1->bExpanded == ir2->bExpanded) && (ir1->bExpanded))
 +    {
 +        cmp_expandedvals(fp, ir1->expandedvals, ir2->expandedvals, min(ir1->fepvals->n_lambda, ir2->fepvals->n_lambda), ftol, abstol);
 +    }
 +    cmp_int(fp, "inputrec->nwall", -1, ir1->nwall, ir2->nwall);
 +    cmp_int(fp, "inputrec->wall_type", -1, ir1->wall_type, ir2->wall_type);
 +    cmp_int(fp, "inputrec->wall_atomtype[0]", -1, ir1->wall_atomtype[0], ir2->wall_atomtype[0]);
 +    cmp_int(fp, "inputrec->wall_atomtype[1]", -1, ir1->wall_atomtype[1], ir2->wall_atomtype[1]);
 +    cmp_real(fp, "inputrec->wall_density[0]", -1, ir1->wall_density[0], ir2->wall_density[0], ftol, abstol);
 +    cmp_real(fp, "inputrec->wall_density[1]", -1, ir1->wall_density[1], ir2->wall_density[1], ftol, abstol);
 +    cmp_real(fp, "inputrec->wall_ewald_zfac", -1, ir1->wall_ewald_zfac, ir2->wall_ewald_zfac, ftol, abstol);
 +
 +    cmp_int(fp, "inputrec->ePull", -1, ir1->ePull, ir2->ePull);
 +    if (ir1->ePull == ir2->ePull && ir1->ePull != epullNO)
 +    {
 +        cmp_pull(fp);
 +    }
 +
 +    cmp_int(fp, "inputrec->eDisre", -1, ir1->eDisre, ir2->eDisre);
 +    cmp_real(fp, "inputrec->dr_fc", -1, ir1->dr_fc, ir2->dr_fc, ftol, abstol);
 +    cmp_int(fp, "inputrec->eDisreWeighting", -1, ir1->eDisreWeighting, ir2->eDisreWeighting);
 +    cmp_int(fp, "inputrec->bDisreMixed", -1, ir1->bDisreMixed, ir2->bDisreMixed);
 +    cmp_int(fp, "inputrec->nstdisreout", -1, ir1->nstdisreout, ir2->nstdisreout);
 +    cmp_real(fp, "inputrec->dr_tau", -1, ir1->dr_tau, ir2->dr_tau, ftol, abstol);
 +    cmp_real(fp, "inputrec->orires_fc", -1, ir1->orires_fc, ir2->orires_fc, ftol, abstol);
 +    cmp_real(fp, "inputrec->orires_tau", -1, ir1->orires_tau, ir2->orires_tau, ftol, abstol);
 +    cmp_int(fp, "inputrec->nstorireout", -1, ir1->nstorireout, ir2->nstorireout);
 +    cmp_real(fp, "inputrec->em_stepsize", -1, ir1->em_stepsize, ir2->em_stepsize, ftol, abstol);
 +    cmp_real(fp, "inputrec->em_tol", -1, ir1->em_tol, ir2->em_tol, ftol, abstol);
 +    cmp_int(fp, "inputrec->niter", -1, ir1->niter, ir2->niter);
 +    cmp_real(fp, "inputrec->fc_stepsize", -1, ir1->fc_stepsize, ir2->fc_stepsize, ftol, abstol);
 +    cmp_int(fp, "inputrec->nstcgsteep", -1, ir1->nstcgsteep, ir2->nstcgsteep);
 +    cmp_int(fp, "inputrec->nbfgscorr", 0, ir1->nbfgscorr, ir2->nbfgscorr);
 +    cmp_int(fp, "inputrec->eConstrAlg", -1, ir1->eConstrAlg, ir2->eConstrAlg);
 +    cmp_int(fp, "inputrec->nProjOrder", -1, ir1->nProjOrder, ir2->nProjOrder);
 +    cmp_real(fp, "inputrec->LincsWarnAngle", -1, ir1->LincsWarnAngle, ir2->LincsWarnAngle, ftol, abstol);
 +    cmp_int(fp, "inputrec->nLincsIter", -1, ir1->nLincsIter, ir2->nLincsIter);
 +    cmp_real(fp, "inputrec->bd_fric", -1, ir1->bd_fric, ir2->bd_fric, ftol, abstol);
 +    cmp_int64(fp, "inputrec->ld_seed", ir1->ld_seed, ir2->ld_seed);
 +    cmp_real(fp, "inputrec->cos_accel", -1, ir1->cos_accel, ir2->cos_accel, ftol, abstol);
 +    cmp_rvec(fp, "inputrec->deform(a)", -1, ir1->deform[XX], ir2->deform[XX], ftol, abstol);
 +    cmp_rvec(fp, "inputrec->deform(b)", -1, ir1->deform[YY], ir2->deform[YY], ftol, abstol);
 +    cmp_rvec(fp, "inputrec->deform(c)", -1, ir1->deform[ZZ], ir2->deform[ZZ], ftol, abstol);
 +
 +
 +    cmp_bool(fp, "ir->bAdress->type", -1, ir1->bAdress, ir2->bAdress);
 +    if (ir1->bAdress && ir2->bAdress)
 +    {
 +        cmp_adress(fp, ir1->adress, ir2->adress, ftol, abstol);
 +    }
 +
 +    cmp_int(fp, "inputrec->userint1", -1, ir1->userint1, ir2->userint1);
 +    cmp_int(fp, "inputrec->userint2", -1, ir1->userint2, ir2->userint2);
 +    cmp_int(fp, "inputrec->userint3", -1, ir1->userint3, ir2->userint3);
 +    cmp_int(fp, "inputrec->userint4", -1, ir1->userint4, ir2->userint4);
 +    cmp_real(fp, "inputrec->userreal1", -1, ir1->userreal1, ir2->userreal1, ftol, abstol);
 +    cmp_real(fp, "inputrec->userreal2", -1, ir1->userreal2, ir2->userreal2, ftol, abstol);
 +    cmp_real(fp, "inputrec->userreal3", -1, ir1->userreal3, ir2->userreal3, ftol, abstol);
 +    cmp_real(fp, "inputrec->userreal4", -1, ir1->userreal4, ir2->userreal4, ftol, abstol);
 +    cmp_grpopts(fp, &(ir1->opts), &(ir2->opts), ftol, abstol);
 +    cmp_cosines(fp, "ex", ir1->ex, ir2->ex, ftol, abstol);
 +    cmp_cosines(fp, "et", ir1->et, ir2->et, ftol, abstol);
 +}
 +
 +static void comp_pull_AB(FILE *fp, t_pull *pull, real ftol, real abstol)
 +{
 +    int i;
 +
 +    for (i = 0; i < pull->ncoord; i++)
 +    {
 +        fprintf(fp, "comparing pull coord %d\n", i);
 +        cmp_real(fp, "pull-coord->k", -1, pull->coord[i].k, pull->coord[i].kB, ftol, abstol);
 +    }
 +}
 +
 +static void comp_state(t_state *st1, t_state *st2,
 +                       gmx_bool bRMSD, real ftol, real abstol)
 +{
 +    int i, j, nc;
 +
 +    fprintf(stdout, "comparing flags\n");
 +    cmp_int(stdout, "flags", -1, st1->flags, st2->flags);
 +    fprintf(stdout, "comparing box\n");
 +    cmp_rvecs(stdout, "box", DIM, st1->box, st2->box, FALSE, ftol, abstol);
 +    fprintf(stdout, "comparing box_rel\n");
 +    cmp_rvecs(stdout, "box_rel", DIM, st1->box_rel, st2->box_rel, FALSE, ftol, abstol);
 +    fprintf(stdout, "comparing boxv\n");
 +    cmp_rvecs(stdout, "boxv", DIM, st1->boxv, st2->boxv, FALSE, ftol, abstol);
 +    if (st1->flags & (1<<estSVIR_PREV))
 +    {
 +        fprintf(stdout, "comparing shake vir_prev\n");
 +        cmp_rvecs_rmstol(stdout, "svir_prev", DIM, st1->svir_prev, st2->svir_prev, ftol, abstol);
 +    }
 +    if (st1->flags & (1<<estFVIR_PREV))
 +    {
 +        fprintf(stdout, "comparing force vir_prev\n");
 +        cmp_rvecs_rmstol(stdout, "fvir_prev", DIM, st1->fvir_prev, st2->fvir_prev, ftol, abstol);
 +    }
 +    if (st1->flags & (1<<estPRES_PREV))
 +    {
 +        fprintf(stdout, "comparing prev_pres\n");
 +        cmp_rvecs_rmstol(stdout, "pres_prev", DIM, st1->pres_prev, st2->pres_prev, ftol, abstol);
 +    }
 +    cmp_int(stdout, "ngtc", -1, st1->ngtc, st2->ngtc);
 +    cmp_int(stdout, "nhchainlength", -1, st1->nhchainlength, st2->nhchainlength);
 +    if (st1->ngtc == st2->ngtc && st1->nhchainlength == st2->nhchainlength)
 +    {
 +        for (i = 0; i < st1->ngtc; i++)
 +        {
 +            nc = i*st1->nhchainlength;
 +            for (j = 0; j < nc; j++)
 +            {
 +                cmp_real(stdout, "nosehoover_xi",
 +                         i, st1->nosehoover_xi[nc+j], st2->nosehoover_xi[nc+j], ftol, abstol);
 +            }
 +        }
 +    }
 +    cmp_int(stdout, "nnhpres", -1, st1->nnhpres, st2->nnhpres);
 +    if (st1->nnhpres == st2->nnhpres && st1->nhchainlength == st2->nhchainlength)
 +    {
 +        for (i = 0; i < st1->nnhpres; i++)
 +        {
 +            nc = i*st1->nhchainlength;
 +            for (j = 0; j < nc; j++)
 +            {
 +                cmp_real(stdout, "nosehoover_xi",
 +                         i, st1->nhpres_xi[nc+j], st2->nhpres_xi[nc+j], ftol, abstol);
 +            }
 +        }
 +    }
 +
 +    cmp_int(stdout, "natoms", -1, st1->natoms, st2->natoms);
 +    if (st1->natoms == st2->natoms)
 +    {
 +        if ((st1->flags & (1<<estX)) && (st2->flags & (1<<estX)))
 +        {
 +            fprintf(stdout, "comparing x\n");
 +            cmp_rvecs(stdout, "x", st1->natoms, st1->x, st2->x, bRMSD, ftol, abstol);
 +        }
 +        if ((st1->flags & (1<<estV)) && (st2->flags & (1<<estV)))
 +        {
 +            fprintf(stdout, "comparing v\n");
 +            cmp_rvecs(stdout, "v", st1->natoms, st1->v, st2->v, bRMSD, ftol, abstol);
 +        }
 +    }
 +}
 +
 +void comp_tpx(const char *fn1, const char *fn2,
 +              gmx_bool bRMSD, real ftol, real abstol)
 +{
 +    const char  *ff[2];
 +    t_tpxheader  sh[2];
 +    t_inputrec   ir[2];
 +    t_state      state[2];
 +    gmx_mtop_t   mtop[2];
 +    t_topology   top[2];
 +    int          i;
 +
 +    ff[0] = fn1;
 +    ff[1] = fn2;
 +    for (i = 0; i < (fn2 ? 2 : 1); i++)
 +    {
 +        read_tpx_state(ff[i], &(ir[i]), &state[i], NULL, &(mtop[i]));
 +    }
 +    if (fn2)
 +    {
 +        cmp_inputrec(stdout, &ir[0], &ir[1], ftol, abstol);
 +        /* Convert gmx_mtop_t to t_topology.
 +         * We should implement direct mtop comparison,
 +         * but it might be useful to keep t_topology comparison as an option.
 +         */
 +        top[0] = gmx_mtop_t_to_t_topology(&mtop[0]);
 +        top[1] = gmx_mtop_t_to_t_topology(&mtop[1]);
 +        cmp_top(stdout, &top[0], &top[1], ftol, abstol);
 +        cmp_groups(stdout, &mtop[0].groups, &mtop[1].groups,
 +                   mtop[0].natoms, mtop[1].natoms);
 +        comp_state(&state[0], &state[1], bRMSD, ftol, abstol);
 +    }
 +    else
 +    {
 +        if (ir[0].efep == efepNO)
 +        {
 +            fprintf(stdout, "inputrec->efep = %s\n", efep_names[ir[0].efep]);
 +        }
 +        else
 +        {
 +            if (ir[0].ePull != epullNO)
 +            {
 +                comp_pull_AB(stdout, ir->pull, ftol, abstol);
 +            }
 +            /* Convert gmx_mtop_t to t_topology.
 +             * We should implement direct mtop comparison,
 +             * but it might be useful to keep t_topology comparison as an option.
 +             */
 +            top[0] = gmx_mtop_t_to_t_topology(&mtop[0]);
 +            cmp_top(stdout, &top[0], NULL, ftol, abstol);
 +        }
 +    }
 +}
 +
 +void comp_frame(FILE *fp, t_trxframe *fr1, t_trxframe *fr2,
 +                gmx_bool bRMSD, real ftol, real abstol)
 +{
 +    fprintf(fp, "\n");
 +    cmp_int(fp, "flags", -1, fr1->flags, fr2->flags);
 +    cmp_int(fp, "not_ok", -1, fr1->not_ok, fr2->not_ok);
 +    cmp_int(fp, "natoms", -1, fr1->natoms, fr2->natoms);
 +    cmp_real(fp, "t0", -1, fr1->t0, fr2->t0, ftol, abstol);
 +    if (cmp_bool(fp, "bTitle", -1, fr1->bTitle, fr2->bTitle))
 +    {
 +        cmp_str(fp, "title", -1, fr1->title, fr2->title);
 +    }
 +    if (cmp_bool(fp, "bStep", -1, fr1->bStep, fr2->bStep))
 +    {
 +        cmp_int(fp, "step", -1, fr1->step, fr2->step);
 +    }
 +    cmp_int(fp, "step", -1, fr1->step, fr2->step);
 +    if (cmp_bool(fp, "bTime", -1, fr1->bTime, fr2->bTime))
 +    {
 +        cmp_real(fp, "time", -1, fr1->time, fr2->time, ftol, abstol);
 +    }
 +    if (cmp_bool(fp, "bLambda", -1, fr1->bLambda, fr2->bLambda))
 +    {
 +        cmp_real(fp, "lambda", -1, fr1->lambda, fr2->lambda, ftol, abstol);
 +    }
 +    if (cmp_bool(fp, "bAtoms", -1, fr1->bAtoms, fr2->bAtoms))
 +    {
 +        cmp_atoms(fp, fr1->atoms, fr2->atoms, ftol, abstol);
 +    }
 +    if (cmp_bool(fp, "bPrec", -1, fr1->bPrec, fr2->bPrec))
 +    {
 +        cmp_real(fp, "prec", -1, fr1->prec, fr2->prec, ftol, abstol);
 +    }
 +    if (cmp_bool(fp, "bX", -1, fr1->bX, fr2->bX))
 +    {
 +        cmp_rvecs(fp, "x", min(fr1->natoms, fr2->natoms), fr1->x, fr2->x, bRMSD, ftol, abstol);
 +    }
 +    if (cmp_bool(fp, "bV", -1, fr1->bV, fr2->bV))
 +    {
 +        cmp_rvecs(fp, "v", min(fr1->natoms, fr2->natoms), fr1->v, fr2->v, bRMSD, ftol, abstol);
 +    }
 +    if (cmp_bool(fp, "bF", -1, fr1->bF, fr2->bF))
 +    {
++        if (bRMSD)
++        {
++            cmp_rvecs(fp, "f", min(fr1->natoms, fr2->natoms), fr1->f, fr2->f, bRMSD, ftol, abstol);
++        }
++        else
++        {
++            cmp_rvecs_rmstol(fp, "f", min(fr1->natoms, fr2->natoms), fr1->f, fr2->f, ftol, abstol);
++        }
 +    }
 +    if (cmp_bool(fp, "bBox", -1, fr1->bBox, fr2->bBox))
 +    {
 +        cmp_rvecs(fp, "box", 3, fr1->box, fr2->box, FALSE, ftol, abstol);
 +    }
 +}
 +
 +void comp_trx(const output_env_t oenv, const char *fn1, const char *fn2,
 +              gmx_bool bRMSD, real ftol, real abstol)
 +{
 +    int          i;
 +    const char  *fn[2];
 +    t_trxframe   fr[2];
 +    t_trxstatus *status[2];
 +    gmx_bool     b[2];
 +
 +    fn[0] = fn1;
 +    fn[1] = fn2;
 +    fprintf(stderr, "Comparing trajectory files %s and %s\n", fn1, fn2);
 +    for (i = 0; i < 2; i++)
 +    {
 +        b[i] = read_first_frame(oenv, &status[i], fn[i], &fr[i], TRX_READ_X|TRX_READ_V|TRX_READ_F);
 +    }
 +
 +    if (b[0] && b[1])
 +    {
 +        do
 +        {
 +            comp_frame(stdout, &(fr[0]), &(fr[1]), bRMSD, ftol, abstol);
 +
 +            for (i = 0; i < 2; i++)
 +            {
 +                b[i] = read_next_frame(oenv, status[i], &fr[i]);
 +            }
 +        }
 +        while (b[0] && b[1]);
 +
 +        for (i = 0; i < 2; i++)
 +        {
 +            if (b[i] && !b[1-i])
 +            {
 +                fprintf(stdout, "\nEnd of file on %s but not on %s\n", fn[1-i], fn[i]);
 +            }
 +            close_trj(status[i]);
 +        }
 +    }
 +    if (!b[0] && !b[1])
 +    {
 +        fprintf(stdout, "\nBoth files read correctly\n");
 +    }
 +}
 +
 +static real ener_tensor_diag(int n, int *ind1, int *ind2,
 +                             gmx_enxnm_t *enm1,
 +                             int *tensi, int i,
 +                             t_energy e1[], t_energy e2[])
 +{
 +    int  d1, d2;
 +    int  len;
 +    int  j;
 +    real prod1, prod2;
 +    int  nfound;
 +
 +    d1 = tensi[i]/DIM;
 +    d2 = tensi[i] - d1*DIM;
 +
 +    /* Find the diagonal elements d1 and d2 */
 +    len    = strlen(enm1[ind1[i]].name);
 +    prod1  = 1;
 +    prod2  = 1;
 +    nfound = 0;
 +    for (j = 0; j < n; j++)
 +    {
 +        if (tensi[j] >= 0 &&
 +            strlen(enm1[ind1[j]].name) == len &&
 +            strncmp(enm1[ind1[i]].name, enm1[ind1[j]].name, len-2) == 0 &&
 +            (tensi[j] == d1*DIM+d1 || tensi[j] == d2*DIM+d2))
 +        {
 +            prod1 *= fabs(e1[ind1[j]].e);
 +            prod2 *= fabs(e2[ind2[j]].e);
 +            nfound++;
 +        }
 +    }
 +
 +    if (nfound == 2)
 +    {
 +        return 0.5*(sqrt(prod1) + sqrt(prod2));
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +static gmx_bool enernm_equal(const char *nm1, const char *nm2)
 +{
 +    int len1, len2;
 +
 +    len1 = strlen(nm1);
 +    len2 = strlen(nm2);
 +
 +    /* Remove " (bar)" at the end of a name */
 +    if (len1 > 6 && strcmp(nm1+len1-6, " (bar)") == 0)
 +    {
 +        len1 -= 6;
 +    }
 +    if (len2 > 6 && strcmp(nm2+len2-6, " (bar)") == 0)
 +    {
 +        len2 -= 6;
 +    }
 +
 +    return (len1 == len2 && gmx_strncasecmp(nm1, nm2, len1) == 0);
 +}
 +
 +static void cmp_energies(FILE *fp, int step1, int step2,
 +                         t_energy e1[], t_energy e2[],
 +                         gmx_enxnm_t *enm1,
 +                         real ftol, real abstol,
 +                         int nre, int *ind1, int *ind2, int maxener)
 +{
 +    int   i, ii;
 +    int  *tensi, len, d1, d2;
 +    real  ftol_i, abstol_i;
 +
 +    snew(tensi, maxener);
 +    /* Check for tensor elements ending on "-XX", "-XY", ... , "-ZZ" */
 +    for (i = 0; (i < maxener); i++)
 +    {
 +        ii       = ind1[i];
 +        tensi[i] = -1;
 +        len      = strlen(enm1[ii].name);
 +        if (len > 3 && enm1[ii].name[len-3] == '-')
 +        {
 +            d1 = enm1[ii].name[len-2] - 'X';
 +            d2 = enm1[ii].name[len-1] - 'X';
 +            if (d1 >= 0 && d1 < DIM &&
 +                d2 >= 0 && d2 < DIM)
 +            {
 +                tensi[i] = d1*DIM + d2;
 +            }
 +        }
 +    }
 +
 +    for (i = 0; (i < maxener); i++)
 +    {
 +        /* Check if this is an off-diagonal tensor element */
 +        if (tensi[i] >= 0 && tensi[i] != 0 && tensi[i] != 4 && tensi[i] != 8)
 +        {
 +            /* Turn on the relative tolerance check (4 is maximum relative diff.) */
 +            ftol_i = 5;
 +            /* Do the relative tolerance through an absolute tolerance times
 +             * the size of diagonal components of the tensor.
 +             */
 +            abstol_i = ftol*ener_tensor_diag(nre, ind1, ind2, enm1, tensi, i, e1, e2);
 +            if (debug)
 +            {
 +                fprintf(debug, "tensor '%s' val %f diag %f\n",
 +                        enm1[i].name, e1[i].e, abstol_i/ftol);
 +            }
 +            if (abstol_i > 0)
 +            {
 +                /* We found a diagonal, we need to check with the minimum tolerance */
 +                abstol_i = min(abstol_i, abstol);
 +            }
 +            else
 +            {
 +                /* We did not find a diagonal, ignore the relative tolerance check */
 +                abstol_i = abstol;
 +            }
 +        }
 +        else
 +        {
 +            ftol_i   = ftol;
 +            abstol_i = abstol;
 +        }
 +        if (!equal_real(e1[ind1[i]].e, e2[ind2[i]].e, ftol_i, abstol_i))
 +        {
 +            fprintf(fp, "%-15s  step %3d:  %12g,  step %3d: %12g\n",
 +                    enm1[ind1[i]].name,
 +                    step1, e1[ind1[i]].e,
 +                    step2, e2[ind2[i]].e);
 +        }
 +    }
 +
 +    sfree(tensi);
 +}
 +
 +#if 0
 +static void cmp_disres(t_enxframe *fr1, t_enxframe *fr2, real ftol, real abstol)
 +{
 +    int  i;
 +    char bav[64], bt[64], bs[22];
 +
 +    cmp_int(stdout, "ndisre", -1, fr1->ndisre, fr2->ndisre);
 +    if ((fr1->ndisre == fr2->ndisre) && (fr1->ndisre > 0))
 +    {
 +        sprintf(bav, "step %s: disre rav", gmx_step_str(fr1->step, bs));
 +        sprintf(bt, "step %s: disre  rt", gmx_step_str(fr1->step, bs));
 +        for (i = 0; (i < fr1->ndisre); i++)
 +        {
 +            cmp_real(stdout, bav, i, fr1->disre_rm3tav[i], fr2->disre_rm3tav[i], ftol, abstol);
 +            cmp_real(stdout, bt, i, fr1->disre_rt[i], fr2->disre_rt[i], ftol, abstol);
 +        }
 +    }
 +}
 +#endif
 +
 +static void cmp_eblocks(t_enxframe *fr1, t_enxframe *fr2, real ftol, real abstol)
 +{
 +    int  i, j, k;
 +    char buf[64], bs[22];
 +
 +    cmp_int(stdout, "nblock", -1, fr1->nblock, fr2->nblock);
 +    if ((fr1->nblock == fr2->nblock) && (fr1->nblock > 0))
 +    {
 +        for (j = 0; (j < fr1->nblock); j++)
 +        {
 +            t_enxblock *b1, *b2; /* convenience vars */
 +
 +            b1 = &(fr1->block[j]);
 +            b2 = &(fr2->block[j]);
 +
 +            sprintf(buf, "step %s: block[%d]", gmx_step_str(fr1->step, bs), j);
 +            cmp_int(stdout, buf, -1, b1->nsub, b2->nsub);
 +            cmp_int(stdout, buf, -1, b1->id, b2->id);
 +
 +            if ( (b1->nsub == b2->nsub) && (b1->id == b2->id) )
 +            {
 +                for (i = 0; i < b1->nsub; i++)
 +                {
 +                    t_enxsubblock *s1, *s2;
 +
 +                    s1 = &(b1->sub[i]);
 +                    s2 = &(b2->sub[i]);
 +
 +                    cmp_int(stdout, buf, -1, (int)s1->type, (int)s2->type);
 +                    cmp_int64(stdout, buf, s1->nr, s2->nr);
 +
 +                    if ((s1->type == s2->type) && (s1->nr == s2->nr))
 +                    {
 +                        switch (s1->type)
 +                        {
 +                            case xdr_datatype_float:
 +                                for (k = 0; k < s1->nr; k++)
 +                                {
 +                                    cmp_float(stdout, buf, i,
 +                                              s1->fval[k], s2->fval[k],
 +                                              ftol, abstol);
 +                                }
 +                                break;
 +                            case xdr_datatype_double:
 +                                for (k = 0; k < s1->nr; k++)
 +                                {
 +                                    cmp_double(stdout, buf, i,
 +                                               s1->dval[k], s2->dval[k],
 +                                               ftol, abstol);
 +                                }
 +                                break;
 +                            case xdr_datatype_int:
 +                                for (k = 0; k < s1->nr; k++)
 +                                {
 +                                    cmp_int(stdout, buf, i,
 +                                            s1->ival[k], s2->ival[k]);
 +                                }
 +                                break;
 +                            case xdr_datatype_int64:
 +                                for (k = 0; k < s1->nr; k++)
 +                                {
 +                                    cmp_int64(stdout, buf,
 +                                              s1->lval[k], s2->lval[k]);
 +                                }
 +                                break;
 +                            case xdr_datatype_char:
 +                                for (k = 0; k < s1->nr; k++)
 +                                {
 +                                    cmp_uc(stdout, buf, i,
 +                                           s1->cval[k], s2->cval[k]);
 +                                }
 +                                break;
 +                            case xdr_datatype_string:
 +                                for (k = 0; k < s1->nr; k++)
 +                                {
 +                                    cmp_str(stdout, buf, i,
 +                                            s1->sval[k], s2->sval[k]);
 +                                }
 +                                break;
 +                            default:
 +                                gmx_incons("Unknown data type!!");
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +void comp_enx(const char *fn1, const char *fn2, real ftol, real abstol, const char *lastener)
 +{
 +    int            nre, nre1, nre2, block;
 +    ener_file_t    in1, in2;
 +    int            i, j, maxener, *ind1, *ind2, *have;
 +    char           buf[256];
 +    gmx_enxnm_t   *enm1 = NULL, *enm2 = NULL;
 +    t_enxframe    *fr1, *fr2;
 +    gmx_bool       b1, b2;
 +
 +    fprintf(stdout, "comparing energy file %s and %s\n\n", fn1, fn2);
 +
 +    in1 = open_enx(fn1, "r");
 +    in2 = open_enx(fn2, "r");
 +    do_enxnms(in1, &nre1, &enm1);
 +    do_enxnms(in2, &nre2, &enm2);
 +    if (nre1 != nre2)
 +    {
 +        fprintf(stdout, "There are %d and %d terms in the energy files\n\n",
 +                nre1, nre2);
 +    }
 +    else
 +    {
 +        fprintf(stdout, "There are %d terms in the energy files\n\n", nre1);
 +    }
 +
 +    snew(ind1, nre1);
 +    snew(ind2, nre2);
 +    snew(have, nre2);
 +    nre = 0;
 +    for (i = 0; i < nre1; i++)
 +    {
 +        for (j = 0; j < nre2; j++)
 +        {
 +            if (enernm_equal(enm1[i].name, enm2[j].name))
 +            {
 +                ind1[nre] = i;
 +                ind2[nre] = j;
 +                have[j]   = 1;
 +                nre++;
 +                break;
 +            }
 +        }
 +        if (nre == 0 || ind1[nre-1] != i)
 +        {
 +            cmp_str(stdout, "enm", i, enm1[i].name, "-");
 +        }
 +    }
 +    for (i = 0; i < nre2; i++)
 +    {
 +        if (have[i] == 0)
 +        {
 +            cmp_str(stdout, "enm", i, "-", enm2[i].name);
 +        }
 +    }
 +
 +    maxener = nre;
 +    for (i = 0; i < nre; i++)
 +    {
 +        if ((lastener != NULL) && (strstr(enm1[i].name, lastener) != NULL))
 +        {
 +            maxener = i+1;
 +            break;
 +        }
 +    }
 +
 +    fprintf(stdout, "There are %d terms to compare in the energy files\n\n",
 +            maxener);
 +
 +    for (i = 0; i < maxener; i++)
 +    {
 +        cmp_str(stdout, "unit", i, enm1[ind1[i]].unit, enm2[ind2[i]].unit);
 +    }
 +
 +    snew(fr1, 1);
 +    snew(fr2, 1);
 +    do
 +    {
 +        b1 = do_enx(in1, fr1);
 +        b2 = do_enx(in2, fr2);
 +        if (b1 && !b2)
 +        {
 +            fprintf(stdout, "\nEnd of file on %s but not on %s\n", fn2, fn1);
 +        }
 +        else if (!b1 && b2)
 +        {
 +            fprintf(stdout, "\nEnd of file on %s but not on %s\n", fn1, fn2);
 +        }
 +        else if (!b1 && !b2)
 +        {
 +            fprintf(stdout, "\nFiles read successfully\n");
 +        }
 +        else
 +        {
 +            cmp_real(stdout, "t", -1, fr1->t, fr2->t, ftol, abstol);
 +            cmp_int(stdout, "step", -1, fr1->step, fr2->step);
 +            /* We don't want to print the nre mismatch for every frame */
 +            /* cmp_int(stdout,"nre",-1,fr1->nre,fr2->nre); */
 +            if ((fr1->nre >= nre) && (fr2->nre >= nre))
 +            {
 +                cmp_energies(stdout, fr1->step, fr1->step, fr1->ener, fr2->ener,
 +                             enm1, ftol, abstol, nre, ind1, ind2, maxener);
 +            }
 +            /*cmp_disres(fr1,fr2,ftol,abstol);*/
 +            cmp_eblocks(fr1, fr2, ftol, abstol);
 +        }
 +    }
 +    while (b1 && b2);
 +
 +    close_enx(in1);
 +    close_enx(in2);
 +
 +    free_enxframe(fr2);
 +    sfree(fr2);
 +    free_enxframe(fr1);
 +    sfree(fr1);
 +}
index b7dab3cf200677fa778e29ec6b4e3906ce302b8e,0000000000000000000000000000000000000000..5fb9b70118317957d50ee5ad803b94dd42aab242
mode 100644,000000..100644
--- /dev/null
@@@ -1,2015 -1,0 +1,2022 @@@
-     shellfc = init_shell_flexcon(fplog, fr->cutoff_scheme == ecutsVERLET,
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2011,2012,2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include "typedefs.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "sysstuff.h"
 +#include "vec.h"
 +#include "vcm.h"
 +#include "mdebin.h"
 +#include "nrnb.h"
 +#include "calcmu.h"
 +#include "index.h"
 +#include "vsite.h"
 +#include "update.h"
 +#include "ns.h"
 +#include "mdrun.h"
 +#include "md_support.h"
 +#include "md_logging.h"
 +#include "network.h"
 +#include "xvgr.h"
 +#include "physics.h"
 +#include "names.h"
 +#include "force.h"
 +#include "disre.h"
 +#include "orires.h"
 +#include "pme.h"
 +#include "mdatoms.h"
 +#include "repl_ex.h"
 +#include "deform.h"
 +#include "qmmm.h"
 +#include "domdec.h"
 +#include "domdec_network.h"
 +#include "gromacs/gmxlib/topsort.h"
 +#include "coulomb.h"
 +#include "constr.h"
 +#include "shellfc.h"
 +#include "gromacs/gmxpreprocess/compute_io.h"
 +#include "checkpoint.h"
 +#include "mtop_util.h"
 +#include "sighandler.h"
 +#include "txtdump.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "pme_loadbal.h"
 +#include "bondf.h"
 +#include "membed.h"
 +#include "types/nlistheuristics.h"
 +#include "types/iteratedconstraints.h"
 +#include "nbnxn_cuda_data_mgmt.h"
 +
 +#include "gromacs/utility/gmxmpi.h"
 +#include "gromacs/fileio/confio.h"
 +#include "gromacs/fileio/trajectory_writing.h"
 +#include "gromacs/fileio/trnio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "gromacs/fileio/xtcio.h"
 +#include "gromacs/timing/wallcycle.h"
 +#include "gromacs/timing/walltime_accounting.h"
 +#include "gromacs/pulling/pull.h"
 +#include "gromacs/swap/swapcoords.h"
 +#include "gromacs/imd/imd.h"
 +
 +#ifdef GMX_FAHCORE
 +#include "corewrap.h"
 +#endif
 +
 +static void reset_all_counters(FILE *fplog, t_commrec *cr,
 +                               gmx_int64_t step,
 +                               gmx_int64_t *step_rel, t_inputrec *ir,
 +                               gmx_wallcycle_t wcycle, t_nrnb *nrnb,
 +                               gmx_walltime_accounting_t walltime_accounting,
 +                               nbnxn_cuda_ptr_t cu_nbv)
 +{
 +    char sbuf[STEPSTRSIZE];
 +
 +    /* Reset all the counters related to performance over the run */
 +    md_print_warn(cr, fplog, "step %s: resetting all time and cycle counters\n",
 +                  gmx_step_str(step, sbuf));
 +
 +    if (cu_nbv)
 +    {
 +        nbnxn_cuda_reset_timings(cu_nbv);
 +    }
 +
 +    wallcycle_stop(wcycle, ewcRUN);
 +    wallcycle_reset_all(wcycle);
 +    if (DOMAINDECOMP(cr))
 +    {
 +        reset_dd_statistics_counters(cr->dd);
 +    }
 +    init_nrnb(nrnb);
 +    ir->init_step += *step_rel;
 +    ir->nsteps    -= *step_rel;
 +    *step_rel      = 0;
 +    wallcycle_start(wcycle, ewcRUN);
 +    walltime_accounting_start(walltime_accounting);
 +    print_date_and_time(fplog, cr->nodeid, "Restarted time", gmx_gettime());
 +}
 +
 +double do_md(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 *ir,
 +             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 gmx_unused *deviceOptions,
 +             int imdport,
 +             unsigned long Flags,
 +             gmx_walltime_accounting_t walltime_accounting)
 +{
 +    gmx_mdoutf_t    outf = NULL;
 +    gmx_int64_t     step, step_rel;
 +    double          elapsed_time;
 +    double          t, t0, lam0[efptNR];
 +    gmx_bool        bGStatEveryStep, bGStat, bCalcVir, bCalcEner;
 +    gmx_bool        bNS, bNStList, bSimAnn, bStopCM, bRerunMD, bNotLastFrame = FALSE,
 +                    bFirstStep, bStateFromCP, bStateFromTPX, bInitStep, bLastStep,
 +                    bBornRadii, bStartingFromCpt;
 +    gmx_bool          bDoDHDL = FALSE, bDoFEP = FALSE, bDoExpanded = FALSE;
 +    gmx_bool          do_ene, do_log, do_verbose, bRerunWarnNoV = TRUE,
 +                      bForceUpdate = FALSE, bCPT;
 +    gmx_bool          bMasterState;
 +    int               force_flags, cglo_flags;
 +    tensor            force_vir, shake_vir, total_vir, tmp_vir, pres;
 +    int               i, m;
 +    t_trxstatus      *status;
 +    rvec              mu_tot;
 +    t_vcm            *vcm;
 +    t_state          *bufstate = NULL;
 +    matrix           *scale_tot, pcoupl_mu, M, ebox;
 +    gmx_nlheur_t      nlh;
 +    t_trxframe        rerun_fr;
 +    gmx_repl_ex_t     repl_ex = NULL;
 +    int               nchkpt  = 1;
 +    gmx_localtop_t   *top;
 +    t_mdebin         *mdebin   = NULL;
 +    t_state          *state    = NULL;
 +    rvec             *f_global = NULL;
 +    gmx_enerdata_t   *enerd;
 +    rvec             *f = NULL;
 +    gmx_global_stat_t gstat;
 +    gmx_update_t      upd   = NULL;
 +    t_graph          *graph = NULL;
 +    globsig_t         gs;
 +    gmx_groups_t     *groups;
 +    gmx_ekindata_t   *ekind, *ekind_save;
 +    gmx_shellfc_t     shellfc;
 +    int               count, nconverged = 0;
 +    real              timestep   = 0;
 +    double            tcount     = 0;
 +    gmx_bool          bConverged = TRUE, bOK, bSumEkinhOld, bDoReplEx, bExchanged, bNeedRepartition;
 +    gmx_bool          bAppend;
 +    gmx_bool          bResetCountersHalfMaxH = FALSE;
 +    gmx_bool          bVV, bIterativeCase, bFirstIterate, bTemp, bPres, bTrotter;
 +    gmx_bool          bUpdateDoLR;
 +    real              dvdl_constr;
 +    rvec             *cbuf = NULL;
 +    matrix            lastbox;
 +    real              veta_save, scalevir, tracevir;
 +    real              vetanew = 0;
 +    int               lamnew  = 0;
 +    /* for FEP */
 +    int               nstfep;
 +    double            cycles;
 +    real              saved_conserved_quantity = 0;
 +    real              last_ekin                = 0;
 +    int               iter_i;
 +    t_extmass         MassQ;
 +    int             **trotter_seq;
 +    char              sbuf[STEPSTRSIZE], sbuf2[STEPSTRSIZE];
 +    int               handled_stop_condition = gmx_stop_cond_none; /* compare to get_stop_condition*/
 +    gmx_iterate_t     iterate;
 +    gmx_int64_t       multisim_nsteps = -1;                        /* number of steps to do  before first multisim
 +                                                                          simulation stops. If equal to zero, don't
 +                                                                          communicate any more between multisims.*/
 +    /* PME load balancing data for GPU kernels */
 +    pme_load_balancing_t pme_loadbal = NULL;
 +    double               cycles_pmes;
 +    gmx_bool             bPMETuneTry = FALSE, bPMETuneRunning = FALSE;
 +
 +    /* Interactive MD */
 +    gmx_bool          bIMDstep = FALSE;
 +
 +#ifdef GMX_FAHCORE
 +    /* Temporary addition for FAHCORE checkpointing */
 +    int chkpt_ret;
 +#endif
 +
 +    /* Check for special mdrun options */
 +    bRerunMD = (Flags & MD_RERUN);
 +    bAppend  = (Flags & MD_APPENDFILES);
 +    if (Flags & MD_RESETCOUNTERSHALFWAY)
 +    {
 +        if (ir->nsteps > 0)
 +        {
 +            /* Signal to reset the counters half the simulation steps. */
 +            wcycle_set_reset_counters(wcycle, ir->nsteps/2);
 +        }
 +        /* Signal to reset the counters halfway the simulation time. */
 +        bResetCountersHalfMaxH = (max_hours > 0);
 +    }
 +
 +    /* md-vv uses averaged full step velocities for T-control
 +       md-vv-avek uses averaged half step velocities for T-control (but full step ekin for P control)
 +       md uses averaged half step kinetic energies to determine temperature unless defined otherwise by GMX_EKIN_AVE_VEL; */
 +    bVV = EI_VV(ir->eI);
 +    if (bVV) /* to store the initial velocities while computing virial */
 +    {
 +        snew(cbuf, top_global->natoms);
 +    }
 +    /* all the iteratative cases - only if there are constraints */
 +    bIterativeCase = ((IR_NPH_TROTTER(ir) || IR_NPT_TROTTER(ir)) && (constr) && (!bRerunMD));
 +    gmx_iterate_init(&iterate, FALSE); /* The default value of iterate->bIterationActive is set to
 +                                          false in this step.  The correct value, true or false,
 +                                          is set at each step, as it depends on the frequency of temperature
 +                                          and pressure control.*/
 +    bTrotter = (bVV && (IR_NPT_TROTTER(ir) || IR_NPH_TROTTER(ir) || IR_NVT_TROTTER(ir)));
 +
 +    if (bRerunMD)
 +    {
 +        /* Since we don't know if the frames read are related in any way,
 +         * rebuild the neighborlist at every step.
 +         */
 +        ir->nstlist       = 1;
 +        ir->nstcalcenergy = 1;
 +        nstglobalcomm     = 1;
 +    }
 +
 +    check_ir_old_tpx_versions(cr, fplog, ir, top_global);
 +
 +    nstglobalcomm   = check_nstglobalcomm(fplog, cr, nstglobalcomm, ir);
 +    bGStatEveryStep = (nstglobalcomm == 1);
 +
 +    if (!bGStatEveryStep && ir->nstlist == -1 && fplog != NULL)
 +    {
 +        fprintf(fplog,
 +                "To reduce the energy communication with nstlist = -1\n"
 +                "the neighbor list validity should not be checked at every step,\n"
 +                "this means that exact integration is not guaranteed.\n"
 +                "The neighbor list validity is checked after:\n"
 +                "  <n.list life time> - 2*std.dev.(n.list life time)  steps.\n"
 +                "In most cases this will result in exact integration.\n"
 +                "This reduces the energy communication by a factor of 2 to 3.\n"
 +                "If you want less energy communication, set nstlist > 3.\n\n");
 +    }
 +
 +    if (bRerunMD)
 +    {
 +        ir->nstxout_compressed = 0;
 +    }
 +    groups = &top_global->groups;
 +
 +    /* Initial values */
 +    init_md(fplog, cr, ir, oenv, &t, &t0, state_global->lambda,
 +            &(state_global->fep_state), lam0,
 +            nrnb, top_global, &upd,
 +            nfile, fnm, &outf, &mdebin,
 +            force_vir, shake_vir, mu_tot, &bSimAnn, &vcm, Flags);
 +
 +    clear_mat(total_vir);
 +    clear_mat(pres);
 +    /* Energy terms and groups */
 +    snew(enerd, 1);
 +    init_enerdata(top_global->groups.grps[egcENER].nr, ir->fepvals->n_lambda,
 +                  enerd);
 +    if (DOMAINDECOMP(cr))
 +    {
 +        f = NULL;
 +    }
 +    else
 +    {
 +        snew(f, top_global->natoms);
 +    }
 +
 +    /* Kinetic energy data */
 +    snew(ekind, 1);
 +    init_ekindata(fplog, top_global, &(ir->opts), ekind);
 +    /* needed for iteration of constraints */
 +    snew(ekind_save, 1);
 +    init_ekindata(fplog, top_global, &(ir->opts), ekind_save);
 +    /* Copy the cos acceleration to the groups struct */
 +    ekind->cosacc.cos_accel = ir->cos_accel;
 +
 +    gstat = global_stat_init(ir);
 +    debug_gmx();
 +
 +    /* Check for polarizable models and flexible constraints */
++    shellfc = init_shell_flexcon(fplog,
 +                                 top_global, n_flexible_constraints(constr),
 +                                 (ir->bContinuation ||
 +                                  (DOMAINDECOMP(cr) && !MASTER(cr))) ?
 +                                 NULL : state_global->x);
++    if (shellfc && ir->nstcalcenergy != 1)
++    {
++        gmx_fatal(FARGS, "You have nstcalcenergy set to a value (%d) that is different from 1.\nThis is not supported in combinations with shell particles.\nPlease make a new tpr file.", ir->nstcalcenergy);
++    }
++    if (shellfc && DOMAINDECOMP(cr))
++    {
++        gmx_fatal(FARGS, "Shell particles are not implemented with domain decomposition, use a single rank");
++    }
 +    if (shellfc && ir->eI == eiNM)
 +    {
 +        /* Currently shells don't work with Normal Modes */
 +        gmx_fatal(FARGS, "Normal Mode analysis is not supported with shells.\nIf you'd like to help with adding support, we have an open discussion at http://redmine.gromacs.org/issues/879\n");
 +    }
 +
 +    if (vsite && ir->eI == eiNM)
 +    {
 +        /* Currently virtual sites don't work with Normal Modes */
 +        gmx_fatal(FARGS, "Normal Mode analysis is not supported with virtual sites.\nIf you'd like to help with adding support, we have an open discussion at http://redmine.gromacs.org/issues/879\n");
 +    }
 +
 +    if (DEFORM(*ir))
 +    {
 +        tMPI_Thread_mutex_lock(&deform_init_box_mutex);
 +        set_deform_reference_box(upd,
 +                                 deform_init_init_step_tpx,
 +                                 deform_init_box_tpx);
 +        tMPI_Thread_mutex_unlock(&deform_init_box_mutex);
 +    }
 +
 +    {
 +        double io = compute_io(ir, top_global->natoms, groups, mdebin->ebin->nener, 1);
 +        if ((io > 2000) && MASTER(cr))
 +        {
 +            fprintf(stderr,
 +                    "\nWARNING: This run will generate roughly %.0f Mb of data\n\n",
 +                    io);
 +        }
 +    }
 +
 +    if (DOMAINDECOMP(cr))
 +    {
 +        top = dd_init_local_top(top_global);
 +
 +        snew(state, 1);
 +        dd_init_local_state(cr->dd, state_global, state);
 +
 +        if (DDMASTER(cr->dd) && ir->nstfout)
 +        {
 +            snew(f_global, state_global->natoms);
 +        }
 +    }
 +    else
 +    {
 +        top = gmx_mtop_generate_local_top(top_global, ir);
 +
 +        forcerec_set_excl_load(fr, top);
 +
 +        state    = serial_init_local_state(state_global);
 +        f_global = f;
 +
 +        atoms2md(top_global, ir, 0, NULL, top_global->natoms, mdatoms);
 +
 +        if (vsite)
 +        {
 +            set_vsite_top(vsite, top, mdatoms, cr);
 +        }
 +
 +        if (ir->ePBC != epbcNONE && !fr->bMolPBC)
 +        {
 +            graph = mk_graph(fplog, &(top->idef), 0, top_global->natoms, FALSE, FALSE);
 +        }
 +
 +        if (shellfc)
 +        {
 +            make_local_shells(cr, mdatoms, shellfc);
 +        }
 +
 +        setup_bonded_threading(fr, &top->idef);
 +    }
 +
 +    /* Set up interactive MD (IMD) */
 +    init_IMD(ir, cr, top_global, fplog, ir->nstcalcenergy, state_global->x,
 +             nfile, fnm, oenv, imdport, Flags);
 +
 +    if (DOMAINDECOMP(cr))
 +    {
 +        /* 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,
 +                            state, &f, mdatoms, top, fr,
 +                            vsite, shellfc, constr,
 +                            nrnb, wcycle, FALSE);
 +
 +    }
 +
 +    update_mdatoms(mdatoms, state->lambda[efptMASS]);
 +
 +    if (opt2bSet("-cpi", nfile, fnm))
 +    {
 +        bStateFromCP = gmx_fexist_master(opt2fn_master("-cpi", nfile, fnm, cr), cr);
 +    }
 +    else
 +    {
 +        bStateFromCP = FALSE;
 +    }
 +
 +    if (ir->bExpanded)
 +    {
 +        init_expanded_ensemble(bStateFromCP, ir, &state->dfhist);
 +    }
 +
 +    if (MASTER(cr))
 +    {
 +        if (bStateFromCP)
 +        {
 +            /* Update mdebin with energy history if appending to output files */
 +            if (Flags & MD_APPENDFILES)
 +            {
 +                restore_energyhistory_from_state(mdebin, &state_global->enerhist);
 +            }
 +            else
 +            {
 +                /* We might have read an energy history from checkpoint,
 +                 * free the allocated memory and reset the counts.
 +                 */
 +                done_energyhistory(&state_global->enerhist);
 +                init_energyhistory(&state_global->enerhist);
 +            }
 +        }
 +        /* Set the initial energy history in state by updating once */
 +        update_energyhistory(&state_global->enerhist, mdebin);
 +    }
 +
 +    /* Initialize constraints */
 +    if (constr && !DOMAINDECOMP(cr))
 +    {
 +        set_constraints(constr, top, ir, mdatoms, cr);
 +    }
 +
 +    if (repl_ex_nst > 0 && MASTER(cr))
 +    {
 +        repl_ex = init_replica_exchange(fplog, cr->ms, state_global, ir,
 +                                        repl_ex_nst, repl_ex_nex, repl_ex_seed);
 +    }
 +
 +    /* PME tuning is only supported with GPUs or PME nodes and not with rerun.
 +     * PME tuning is not supported with PME only for LJ and not for Coulomb.
 +     */
 +    if ((Flags & MD_TUNEPME) &&
 +        EEL_PME(fr->eeltype) &&
 +        ( (fr->cutoff_scheme == ecutsVERLET && fr->nbv->bUseGPU) || !(cr->duty & DUTY_PME)) &&
 +        !bRerunMD)
 +    {
 +        pme_loadbal_init(&pme_loadbal, ir, state->box, fr->ic, fr->pmedata);
 +        cycles_pmes = 0;
 +        if (cr->duty & DUTY_PME)
 +        {
 +            /* Start tuning right away, as we can't measure the load */
 +            bPMETuneRunning = TRUE;
 +        }
 +        else
 +        {
 +            /* Separate PME nodes, we can measure the PP/PME load balance */
 +            bPMETuneTry = TRUE;
 +        }
 +    }
 +
 +    if (!ir->bContinuation && !bRerunMD)
 +    {
 +        if (mdatoms->cFREEZE && (state->flags & (1<<estV)))
 +        {
 +            /* Set the velocities of frozen particles to zero */
 +            for (i = 0; i < mdatoms->homenr; i++)
 +            {
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    if (ir->opts.nFreeze[mdatoms->cFREEZE[i]][m])
 +                    {
 +                        state->v[i][m] = 0;
 +                    }
 +                }
 +            }
 +        }
 +
 +        if (constr)
 +        {
 +            /* Constrain the initial coordinates and velocities */
 +            do_constrain_first(fplog, constr, ir, mdatoms, state,
 +                               cr, nrnb, fr, top);
 +        }
 +        if (vsite)
 +        {
 +            /* Construct the virtual sites for the initial configuration */
 +            construct_vsites(vsite, state->x, ir->delta_t, NULL,
 +                             top->idef.iparams, top->idef.il,
 +                             fr->ePBC, fr->bMolPBC, cr, state->box);
 +        }
 +    }
 +
 +    debug_gmx();
 +
 +    /* set free energy calculation frequency as the minimum
 +       greatest common denominator of nstdhdl, nstexpanded, and repl_ex_nst*/
 +    nstfep = ir->fepvals->nstdhdl;
 +    if (ir->bExpanded)
 +    {
 +        nstfep = gmx_greatest_common_divisor(ir->fepvals->nstdhdl, nstfep);
 +    }
 +    if (repl_ex_nst > 0)
 +    {
 +        nstfep = gmx_greatest_common_divisor(repl_ex_nst, nstfep);
 +    }
 +
 +    /* I'm assuming we need global communication the first time! MRS */
 +    cglo_flags = (CGLO_TEMPERATURE | CGLO_GSTAT
 +                  | ((ir->comm_mode != ecmNO) ? CGLO_STOPCM : 0)
 +                  | (bVV ? CGLO_PRESSURE : 0)
 +                  | (bVV ? CGLO_CONSTRAINT : 0)
 +                  | (bRerunMD ? CGLO_RERUNMD : 0)
 +                  | ((Flags & MD_READ_EKIN) ? CGLO_READEKIN : 0));
 +
 +    bSumEkinhOld = FALSE;
 +    compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                    NULL, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                    constr, NULL, FALSE, state->box,
 +                    top_global, &bSumEkinhOld, cglo_flags);
 +    if (ir->eI == eiVVAK)
 +    {
 +        /* a second call to get the half step temperature initialized as well */
 +        /* we do the same call as above, but turn the pressure off -- internally to
 +           compute_globals, this is recognized as a velocity verlet half-step
 +           kinetic energy calculation.  This minimized excess variables, but
 +           perhaps loses some logic?*/
 +
 +        compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                        NULL, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                        constr, NULL, FALSE, state->box,
 +                        top_global, &bSumEkinhOld,
 +                        cglo_flags &~(CGLO_STOPCM | CGLO_PRESSURE));
 +    }
 +
 +    /* Calculate the initial half step temperature, and save the ekinh_old */
 +    if (!(Flags & MD_STARTFROMCPT))
 +    {
 +        for (i = 0; (i < ir->opts.ngtc); i++)
 +        {
 +            copy_mat(ekind->tcstat[i].ekinh, ekind->tcstat[i].ekinh_old);
 +        }
 +    }
 +    if (ir->eI != eiVV)
 +    {
 +        enerd->term[F_TEMP] *= 2; /* result of averages being done over previous and current step,
 +                                     and there is no previous step */
 +    }
 +
 +    /* if using an iterative algorithm, we need to create a working directory for the state. */
 +    if (bIterativeCase)
 +    {
 +        bufstate = init_bufstate(state);
 +    }
 +
 +    /* need to make an initiation call to get the Trotter variables set, as well as other constants for non-trotter
 +       temperature control */
 +    trotter_seq = init_npt_vars(ir, state, &MassQ, bTrotter);
 +
 +    if (MASTER(cr))
 +    {
 +        if (constr && !ir->bContinuation && ir->eConstrAlg == econtLINCS)
 +        {
 +            fprintf(fplog,
 +                    "RMS relative constraint deviation after constraining: %.2e\n",
 +                    constr_rmsd(constr, FALSE));
 +        }
 +        if (EI_STATE_VELOCITY(ir->eI))
 +        {
 +            fprintf(fplog, "Initial temperature: %g K\n", enerd->term[F_TEMP]);
 +        }
 +        if (bRerunMD)
 +        {
 +            fprintf(stderr, "starting md rerun '%s', reading coordinates from"
 +                    " input trajectory '%s'\n\n",
 +                    *(top_global->name), opt2fn("-rerun", nfile, fnm));
 +            if (bVerbose)
 +            {
 +                fprintf(stderr, "Calculated time to finish depends on nsteps from "
 +                        "run input file,\nwhich may not correspond to the time "
 +                        "needed to process input trajectory.\n\n");
 +            }
 +        }
 +        else
 +        {
 +            char tbuf[20];
 +            fprintf(stderr, "starting mdrun '%s'\n",
 +                    *(top_global->name));
 +            if (ir->nsteps >= 0)
 +            {
 +                sprintf(tbuf, "%8.1f", (ir->init_step+ir->nsteps)*ir->delta_t);
 +            }
 +            else
 +            {
 +                sprintf(tbuf, "%s", "infinite");
 +            }
 +            if (ir->init_step > 0)
 +            {
 +                fprintf(stderr, "%s steps, %s ps (continuing from step %s, %8.1f ps).\n",
 +                        gmx_step_str(ir->init_step+ir->nsteps, sbuf), tbuf,
 +                        gmx_step_str(ir->init_step, sbuf2),
 +                        ir->init_step*ir->delta_t);
 +            }
 +            else
 +            {
 +                fprintf(stderr, "%s steps, %s ps.\n",
 +                        gmx_step_str(ir->nsteps, sbuf), tbuf);
 +            }
 +        }
 +        fprintf(fplog, "\n");
 +    }
 +
 +    walltime_accounting_start(walltime_accounting);
 +    wallcycle_start(wcycle, ewcRUN);
 +    print_start(fplog, cr, walltime_accounting, "mdrun");
 +
 +    /* safest point to do file checkpointing is here.  More general point would be immediately before integrator call */
 +#ifdef GMX_FAHCORE
 +    chkpt_ret = fcCheckPointParallel( cr->nodeid,
 +                                      NULL, 0);
 +    if (chkpt_ret == 0)
 +    {
 +        gmx_fatal( 3, __FILE__, __LINE__, "Checkpoint error on step %d\n", 0 );
 +    }
 +#endif
 +
 +    debug_gmx();
 +    /***********************************************************
 +     *
 +     *             Loop over MD steps
 +     *
 +     ************************************************************/
 +
 +    /* if rerunMD then read coordinates and velocities from input trajectory */
 +    if (bRerunMD)
 +    {
 +        if (getenv("GMX_FORCE_UPDATE"))
 +        {
 +            bForceUpdate = TRUE;
 +        }
 +
 +        rerun_fr.natoms = 0;
 +        if (MASTER(cr))
 +        {
 +            bNotLastFrame = read_first_frame(oenv, &status,
 +                                             opt2fn("-rerun", nfile, fnm),
 +                                             &rerun_fr, TRX_NEED_X | TRX_READ_V);
 +            if (rerun_fr.natoms != top_global->natoms)
 +            {
 +                gmx_fatal(FARGS,
 +                          "Number of atoms in trajectory (%d) does not match the "
 +                          "run input file (%d)\n",
 +                          rerun_fr.natoms, top_global->natoms);
 +            }
 +            if (ir->ePBC != epbcNONE)
 +            {
 +                if (!rerun_fr.bBox)
 +                {
 +                    gmx_fatal(FARGS, "Rerun trajectory frame step %d time %f does not contain a box, while pbc is used", rerun_fr.step, rerun_fr.time);
 +                }
 +                if (max_cutoff2(ir->ePBC, rerun_fr.box) < sqr(fr->rlistlong))
 +                {
 +                    gmx_fatal(FARGS, "Rerun trajectory frame step %d time %f has too small box dimensions", rerun_fr.step, rerun_fr.time);
 +                }
 +            }
 +        }
 +
 +        if (PAR(cr))
 +        {
 +            rerun_parallel_comm(cr, &rerun_fr, &bNotLastFrame);
 +        }
 +
 +        if (ir->ePBC != epbcNONE)
 +        {
 +            /* Set the shift vectors.
 +             * Necessary here when have a static box different from the tpr box.
 +             */
 +            calc_shifts(rerun_fr.box, fr->shift_vec);
 +        }
 +    }
 +
 +    /* loop over MD steps or if rerunMD to end of input trajectory */
 +    bFirstStep = TRUE;
 +    /* Skip the first Nose-Hoover integration when we get the state from tpx */
 +    bStateFromTPX    = !bStateFromCP;
 +    bInitStep        = bFirstStep && (bStateFromTPX || bVV);
 +    bStartingFromCpt = (Flags & MD_STARTFROMCPT) && bInitStep;
 +    bLastStep        = FALSE;
 +    bSumEkinhOld     = FALSE;
 +    bDoReplEx        = FALSE;
 +    bExchanged       = FALSE;
 +    bNeedRepartition = FALSE;
 +
 +    init_global_signals(&gs, cr, ir, repl_ex_nst);
 +
 +    step     = ir->init_step;
 +    step_rel = 0;
 +
 +    if (ir->nstlist == -1)
 +    {
 +        init_nlistheuristics(&nlh, bGStatEveryStep, step);
 +    }
 +
 +    if (MULTISIM(cr) && (repl_ex_nst <= 0 ))
 +    {
 +        /* check how many steps are left in other sims */
 +        multisim_nsteps = get_multisim_nsteps(cr, ir->nsteps);
 +    }
 +
 +
 +    /* and stop now if we should */
 +    bLastStep = (bRerunMD || (ir->nsteps >= 0 && step_rel > ir->nsteps) ||
 +                 ((multisim_nsteps >= 0) && (step_rel >= multisim_nsteps )));
 +    while (!bLastStep || (bRerunMD && bNotLastFrame))
 +    {
 +
 +        wallcycle_start(wcycle, ewcSTEP);
 +
 +        if (bRerunMD)
 +        {
 +            if (rerun_fr.bStep)
 +            {
 +                step     = rerun_fr.step;
 +                step_rel = step - ir->init_step;
 +            }
 +            if (rerun_fr.bTime)
 +            {
 +                t = rerun_fr.time;
 +            }
 +            else
 +            {
 +                t = step;
 +            }
 +        }
 +        else
 +        {
 +            bLastStep = (step_rel == ir->nsteps);
 +            t         = t0 + step*ir->delta_t;
 +        }
 +
 +        if (ir->efep != efepNO || ir->bSimTemp)
 +        {
 +            /* find and set the current lambdas.  If rerunning, we either read in a state, or a lambda value,
 +               requiring different logic. */
 +
 +            set_current_lambdas(step, ir->fepvals, bRerunMD, &rerun_fr, state_global, state, lam0);
 +            bDoDHDL      = do_per_step(step, ir->fepvals->nstdhdl);
 +            bDoFEP       = (do_per_step(step, nstfep) && (ir->efep != efepNO));
 +            bDoExpanded  = (do_per_step(step, ir->expandedvals->nstexpanded)
 +                            && (ir->bExpanded) && (step > 0) && (!bStartingFromCpt));
 +        }
 +
 +        bDoReplEx = ((repl_ex_nst > 0) && (step > 0) && !bLastStep &&
 +                     do_per_step(step, repl_ex_nst));
 +
 +        if (bSimAnn)
 +        {
 +            update_annealing_target_temp(&(ir->opts), t);
 +        }
 +
 +        if (bRerunMD)
 +        {
 +            if (!DOMAINDECOMP(cr) || MASTER(cr))
 +            {
 +                for (i = 0; i < state_global->natoms; i++)
 +                {
 +                    copy_rvec(rerun_fr.x[i], state_global->x[i]);
 +                }
 +                if (rerun_fr.bV)
 +                {
 +                    for (i = 0; i < state_global->natoms; i++)
 +                    {
 +                        copy_rvec(rerun_fr.v[i], state_global->v[i]);
 +                    }
 +                }
 +                else
 +                {
 +                    for (i = 0; i < state_global->natoms; i++)
 +                    {
 +                        clear_rvec(state_global->v[i]);
 +                    }
 +                    if (bRerunWarnNoV)
 +                    {
 +                        fprintf(stderr, "\nWARNING: Some frames do not contain velocities.\n"
 +                                "         Ekin, temperature and pressure are incorrect,\n"
 +                                "         the virial will be incorrect when constraints are present.\n"
 +                                "\n");
 +                        bRerunWarnNoV = FALSE;
 +                    }
 +                }
 +            }
 +            copy_mat(rerun_fr.box, state_global->box);
 +            copy_mat(state_global->box, state->box);
 +
 +            if (vsite && (Flags & MD_RERUN_VSITE))
 +            {
 +                if (DOMAINDECOMP(cr))
 +                {
 +                    gmx_fatal(FARGS, "Vsite recalculation with -rerun is not implemented with domain decomposition, use a single rank");
 +                }
 +                if (graph)
 +                {
 +                    /* Following is necessary because the graph may get out of sync
 +                     * with the coordinates if we only have every N'th coordinate set
 +                     */
 +                    mk_mshift(fplog, graph, fr->ePBC, state->box, state->x);
 +                    shift_self(graph, state->box, state->x);
 +                }
 +                construct_vsites(vsite, state->x, ir->delta_t, state->v,
 +                                 top->idef.iparams, top->idef.il,
 +                                 fr->ePBC, fr->bMolPBC, cr, state->box);
 +                if (graph)
 +                {
 +                    unshift_self(graph, state->box, state->x);
 +                }
 +            }
 +        }
 +
 +        /* Stop Center of Mass motion */
 +        bStopCM = (ir->comm_mode != ecmNO && do_per_step(step, ir->nstcomm));
 +
 +        if (bRerunMD)
 +        {
 +            /* for rerun MD always do Neighbour Searching */
 +            bNS      = (bFirstStep || ir->nstlist != 0);
 +            bNStList = bNS;
 +        }
 +        else
 +        {
 +            /* Determine whether or not to do Neighbour Searching and LR */
 +            bNStList = (ir->nstlist > 0  && step % ir->nstlist == 0);
 +
 +            bNS = (bFirstStep || bExchanged || bNeedRepartition || bNStList || bDoFEP ||
 +                   (ir->nstlist == -1 && nlh.nabnsb > 0));
 +
 +            if (bNS && ir->nstlist == -1)
 +            {
 +                set_nlistheuristics(&nlh, bFirstStep || bExchanged || bNeedRepartition || bDoFEP, step);
 +            }
 +        }
 +
 +        /* check whether we should stop because another simulation has
 +           stopped. */
 +        if (MULTISIM(cr))
 +        {
 +            if ( (multisim_nsteps >= 0) &&  (step_rel >= multisim_nsteps)  &&
 +                 (multisim_nsteps != ir->nsteps) )
 +            {
 +                if (bNS)
 +                {
 +                    if (MASTER(cr))
 +                    {
 +                        fprintf(stderr,
 +                                "Stopping simulation %d because another one has finished\n",
 +                                cr->ms->sim);
 +                    }
 +                    bLastStep         = TRUE;
 +                    gs.sig[eglsCHKPT] = 1;
 +                }
 +            }
 +        }
 +
 +        /* < 0 means stop at next step, > 0 means stop at next NS step */
 +        if ( (gs.set[eglsSTOPCOND] < 0) ||
 +             ( (gs.set[eglsSTOPCOND] > 0) && (bNStList || ir->nstlist == 0) ) )
 +        {
 +            bLastStep = TRUE;
 +        }
 +
 +        /* Determine whether or not to update the Born radii if doing GB */
 +        bBornRadii = bFirstStep;
 +        if (ir->implicit_solvent && (step % ir->nstgbradii == 0))
 +        {
 +            bBornRadii = TRUE;
 +        }
 +
 +        do_log     = do_per_step(step, ir->nstlog) || bFirstStep || bLastStep;
 +        do_verbose = bVerbose &&
 +            (step % stepout == 0 || bFirstStep || bLastStep);
 +
 +        if (bNS && !(bFirstStep && ir->bContinuation && !bRerunMD))
 +        {
 +            if (bRerunMD)
 +            {
 +                bMasterState = TRUE;
 +            }
 +            else
 +            {
 +                bMasterState = FALSE;
 +                /* Correct the new box if it is too skewed */
 +                if (DYNAMIC_BOX(*ir))
 +                {
 +                    if (correct_box(fplog, step, state->box, graph))
 +                    {
 +                        bMasterState = TRUE;
 +                    }
 +                }
 +                if (DOMAINDECOMP(cr) && bMasterState)
 +                {
 +                    dd_collect_state(cr->dd, state, state_global);
 +                }
 +            }
 +
 +            if (DOMAINDECOMP(cr))
 +            {
 +                /* Repartition the domain decomposition */
 +                wallcycle_start(wcycle, ewcDOMDEC);
 +                dd_partition_system(fplog, step, cr,
 +                                    bMasterState, nstglobalcomm,
 +                                    state_global, top_global, ir,
 +                                    state, &f, mdatoms, top, fr,
 +                                    vsite, shellfc, constr,
 +                                    nrnb, wcycle,
 +                                    do_verbose && !bPMETuneRunning);
 +                wallcycle_stop(wcycle, ewcDOMDEC);
 +                /* If using an iterative integrator, reallocate space to match the decomposition */
 +            }
 +        }
 +
 +        if (MASTER(cr) && do_log)
 +        {
 +            print_ebin_header(fplog, step, t, state->lambda[efptFEP]); /* can we improve the information printed here? */
 +        }
 +
 +        if (ir->efep != efepNO)
 +        {
 +            update_mdatoms(mdatoms, state->lambda[efptMASS]);
 +        }
 +
 +        if ((bRerunMD && rerun_fr.bV) || bExchanged)
 +        {
 +
 +            /* We need the kinetic energy at minus the half step for determining
 +             * the full step kinetic energy and possibly for T-coupling.*/
 +            /* This may not be quite working correctly yet . . . . */
 +            compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                            wcycle, enerd, NULL, NULL, NULL, NULL, mu_tot,
 +                            constr, NULL, FALSE, state->box,
 +                            top_global, &bSumEkinhOld,
 +                            CGLO_RERUNMD | CGLO_GSTAT | CGLO_TEMPERATURE);
 +        }
 +        clear_mat(force_vir);
 +
 +        /* We write a checkpoint at this MD step when:
 +         * either at an NS step when we signalled through gs,
 +         * or at the last step (but not when we do not want confout),
 +         * but never at the first step or with rerun.
 +         */
 +        bCPT = (((gs.set[eglsCHKPT] && (bNS || ir->nstlist == 0)) ||
 +                 (bLastStep && (Flags & MD_CONFOUT))) &&
 +                step > ir->init_step && !bRerunMD);
 +        if (bCPT)
 +        {
 +            gs.set[eglsCHKPT] = 0;
 +        }
 +
 +        /* Determine the energy and pressure:
 +         * at nstcalcenergy steps and at energy output steps (set below).
 +         */
 +        if (EI_VV(ir->eI) && (!bInitStep))
 +        {
 +            /* for vv, the first half of the integration actually corresponds
 +               to the previous step.  bCalcEner is only required to be evaluated on the 'next' step,
 +               but the virial needs to be calculated on both the current step and the 'next' step. Future
 +               reorganization may be able to get rid of one of the bCalcVir=TRUE steps. */
 +
 +            bCalcEner = do_per_step(step-1, ir->nstcalcenergy);
 +            bCalcVir  = bCalcEner ||
 +                (ir->epc != epcNO && (do_per_step(step, ir->nstpcouple) || do_per_step(step-1, ir->nstpcouple)));
 +        }
 +        else
 +        {
 +            bCalcEner = do_per_step(step, ir->nstcalcenergy);
 +            bCalcVir  = bCalcEner ||
 +                (ir->epc != epcNO && do_per_step(step, ir->nstpcouple));
 +        }
 +
 +        /* Do we need global communication ? */
 +        bGStat = (bCalcVir || bCalcEner || bStopCM ||
 +                  do_per_step(step, nstglobalcomm) || (bVV && IR_NVT_TROTTER(ir) && do_per_step(step-1, nstglobalcomm)) ||
 +                  (ir->nstlist == -1 && !bRerunMD && step >= nlh.step_nscheck));
 +
 +        do_ene = (do_per_step(step, ir->nstenergy) || bLastStep);
 +
 +        if (do_ene || do_log || bDoReplEx)
 +        {
 +            bCalcVir  = TRUE;
 +            bCalcEner = TRUE;
 +            bGStat    = TRUE;
 +        }
 +
 +        /* these CGLO_ options remain the same throughout the iteration */
 +        cglo_flags = ((bRerunMD ? CGLO_RERUNMD : 0) |
 +                      (bGStat ? CGLO_GSTAT : 0)
 +                      );
 +
 +        force_flags = (GMX_FORCE_STATECHANGED |
 +                       ((DYNAMIC_BOX(*ir) || bRerunMD) ? GMX_FORCE_DYNAMICBOX : 0) |
 +                       GMX_FORCE_ALLFORCES |
 +                       GMX_FORCE_SEPLRF |
 +                       (bCalcVir ? GMX_FORCE_VIRIAL : 0) |
 +                       (bCalcEner ? GMX_FORCE_ENERGY : 0) |
 +                       (bDoFEP ? GMX_FORCE_DHDL : 0)
 +                       );
 +
 +        if (fr->bTwinRange)
 +        {
 +            if (do_per_step(step, ir->nstcalclr))
 +            {
 +                force_flags |= GMX_FORCE_DO_LR;
 +            }
 +        }
 +
 +        if (shellfc)
 +        {
 +            /* Now is the time to relax the shells */
 +            count = relax_shell_flexcon(fplog, cr, bVerbose, step,
 +                                        ir, bNS, force_flags,
 +                                        top,
 +                                        constr, enerd, fcd,
 +                                        state, f, force_vir, mdatoms,
 +                                        nrnb, wcycle, graph, groups,
 +                                        shellfc, fr, bBornRadii, t, mu_tot,
 +                                        &bConverged, vsite,
 +                                        mdoutf_get_fp_field(outf));
 +            tcount += count;
 +
 +            if (bConverged)
 +            {
 +                nconverged++;
 +            }
 +        }
 +        else
 +        {
 +            /* The coordinates (x) are shifted (to get whole molecules)
 +             * in do_force.
 +             * This is parallellized as well, and does communication too.
 +             * Check comments in sim_util.c
 +             */
 +            do_force(fplog, cr, ir, step, nrnb, wcycle, top, groups,
 +                     state->box, state->x, &state->hist,
 +                     f, force_vir, mdatoms, enerd, fcd,
 +                     state->lambda, graph,
 +                     fr, vsite, mu_tot, t, mdoutf_get_fp_field(outf), ed, bBornRadii,
 +                     (bNS ? GMX_FORCE_NS : 0) | force_flags);
 +        }
 +
 +        if (bVV && !bStartingFromCpt && !bRerunMD)
 +        /*  ############### START FIRST UPDATE HALF-STEP FOR VV METHODS############### */
 +        {
 +            if (ir->eI == eiVV && bInitStep)
 +            {
 +                /* if using velocity verlet with full time step Ekin,
 +                 * take the first half step only to compute the
 +                 * virial for the first step. From there,
 +                 * revert back to the initial coordinates
 +                 * so that the input is actually the initial step.
 +                 */
 +                copy_rvecn(state->v, cbuf, 0, state->natoms); /* should make this better for parallelizing? */
 +            }
 +            else
 +            {
 +                /* this is for NHC in the Ekin(t+dt/2) version of vv */
 +                trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ1);
 +            }
 +
 +            /* If we are using twin-range interactions where the long-range component
 +             * is only evaluated every nstcalclr>1 steps, we should do a special update
 +             * step to combine the long-range forces on these steps.
 +             * For nstcalclr=1 this is not done, since the forces would have been added
 +             * directly to the short-range forces already.
 +             *
 +             * TODO Remove various aspects of VV+twin-range in master
 +             * branch, because VV integrators did not ever support
 +             * twin-range multiple time stepping with constraints.
 +             */
 +            bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr));
 +
 +            update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC,
 +                          f, bUpdateDoLR, fr->f_twin, bCalcVir ? &fr->vir_twin_constr : NULL, fcd,
 +                          ekind, M, upd, bInitStep, etrtVELOCITY1,
 +                          cr, nrnb, constr, &top->idef);
 +
 +            if (bIterativeCase && do_per_step(step-1, ir->nstpcouple) && !bInitStep)
 +            {
 +                gmx_iterate_init(&iterate, TRUE);
 +            }
 +            /* for iterations, we save these vectors, as we will be self-consistently iterating
 +               the calculations */
 +
 +            /*#### UPDATE EXTENDED VARIABLES IN TROTTER FORMULATION */
 +
 +            /* save the state */
 +            if (iterate.bIterationActive)
 +            {
 +                copy_coupling_state(state, bufstate, ekind, ekind_save, &(ir->opts));
 +            }
 +
 +            bFirstIterate = TRUE;
 +            while (bFirstIterate || iterate.bIterationActive)
 +            {
 +                if (iterate.bIterationActive)
 +                {
 +                    copy_coupling_state(bufstate, state, ekind_save, ekind, &(ir->opts));
 +                    if (bFirstIterate && bTrotter)
 +                    {
 +                        /* The first time through, we need a decent first estimate
 +                           of veta(t+dt) to compute the constraints.  Do
 +                           this by computing the box volume part of the
 +                           trotter integration at this time. Nothing else
 +                           should be changed by this routine here.  If
 +                           !(first time), we start with the previous value
 +                           of veta.  */
 +
 +                        veta_save = state->veta;
 +                        trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ0);
 +                        vetanew     = state->veta;
 +                        state->veta = veta_save;
 +                    }
 +                }
 +
 +                bOK = TRUE;
 +                if (!bRerunMD || rerun_fr.bV || bForceUpdate)     /* Why is rerun_fr.bV here?  Unclear. */
 +                {
 +                    update_constraints(fplog, step, NULL, ir, ekind, mdatoms,
 +                                       state, fr->bMolPBC, graph, f,
 +                                       &top->idef, shake_vir,
 +                                       cr, nrnb, wcycle, upd, constr,
 +                                       TRUE, bCalcVir, vetanew);
 +
 +                    if (bCalcVir && bUpdateDoLR && ir->nstcalclr > 1)
 +                    {
 +                        /* Correct the virial for multiple time stepping */
 +                        m_sub(shake_vir, fr->vir_twin_constr, shake_vir);
 +                    }
 +
 +                    if (!bOK)
 +                    {
 +                        gmx_fatal(FARGS, "Constraint error: Shake, Lincs or Settle could not solve the constrains");
 +                    }
 +
 +                }
 +                else if (graph)
 +                {
 +                    /* Need to unshift here if a do_force has been
 +                       called in the previous step */
 +                    unshift_self(graph, state->box, state->x);
 +                }
 +
 +                /* if VV, compute the pressure and constraints */
 +                /* For VV2, we strictly only need this if using pressure
 +                 * control, but we really would like to have accurate pressures
 +                 * printed out.
 +                 * Think about ways around this in the future?
 +                 * For now, keep this choice in comments.
 +                 */
 +                /*bPres = (ir->eI==eiVV || IR_NPT_TROTTER(ir)); */
 +                /*bTemp = ((ir->eI==eiVV &&(!bInitStep)) || (ir->eI==eiVVAK && IR_NPT_TROTTER(ir)));*/
 +                bPres = TRUE;
 +                bTemp = ((ir->eI == eiVV && (!bInitStep)) || (ir->eI == eiVVAK));
 +                if (bCalcEner && ir->eI == eiVVAK)  /*MRS:  7/9/2010 -- this still doesn't fix it?*/
 +                {
 +                    bSumEkinhOld = TRUE;
 +                }
 +                /* for vv, the first half of the integration actually corresponds to the previous step.
 +                   So we need information from the last step in the first half of the integration */
 +                if (bGStat || do_per_step(step-1, nstglobalcomm))
 +                {
 +                    compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                                    wcycle, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                                    constr, NULL, FALSE, state->box,
 +                                    top_global, &bSumEkinhOld,
 +                                    cglo_flags
 +                                    | CGLO_ENERGY
 +                                    | (bTemp ? CGLO_TEMPERATURE : 0)
 +                                    | (bPres ? CGLO_PRESSURE : 0)
 +                                    | (bPres ? CGLO_CONSTRAINT : 0)
 +                                    | ((iterate.bIterationActive) ? CGLO_ITERATE : 0)
 +                                    | (bFirstIterate ? CGLO_FIRSTITERATE : 0)
 +                                    | CGLO_SCALEEKIN
 +                                    );
 +                    /* explanation of above:
 +                       a) We compute Ekin at the full time step
 +                       if 1) we are using the AveVel Ekin, and it's not the
 +                       initial step, or 2) if we are using AveEkin, but need the full
 +                       time step kinetic energy for the pressure (always true now, since we want accurate statistics).
 +                       b) If we are using EkinAveEkin for the kinetic energy for the temperature control, we still feed in
 +                       EkinAveVel because it's needed for the pressure */
 +                }
 +                /* temperature scaling and pressure scaling to produce the extended variables at t+dt */
 +                if (!bInitStep)
 +                {
 +                    if (bTrotter)
 +                    {
 +                        m_add(force_vir, shake_vir, total_vir); /* we need the un-dispersion corrected total vir here */
 +                        trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ2);
 +                    }
 +                    else
 +                    {
 +                        if (bExchanged)
 +                        {
 +
 +                            /* We need the kinetic energy at minus the half step for determining
 +                             * the full step kinetic energy and possibly for T-coupling.*/
 +                            /* This may not be quite working correctly yet . . . . */
 +                            compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                                            wcycle, enerd, NULL, NULL, NULL, NULL, mu_tot,
 +                                            constr, NULL, FALSE, state->box,
 +                                            top_global, &bSumEkinhOld,
 +                                            CGLO_RERUNMD | CGLO_GSTAT | CGLO_TEMPERATURE);
 +                        }
 +                    }
 +                }
 +
 +                if (iterate.bIterationActive &&
 +                    done_iterating(cr, fplog, step, &iterate, bFirstIterate,
 +                                   state->veta, &vetanew))
 +                {
 +                    break;
 +                }
 +                bFirstIterate = FALSE;
 +            }
 +
 +            if (bTrotter && !bInitStep)
 +            {
 +                copy_mat(shake_vir, state->svir_prev);
 +                copy_mat(force_vir, state->fvir_prev);
 +                if (IR_NVT_TROTTER(ir) && ir->eI == eiVV)
 +                {
 +                    /* update temperature and kinetic energy now that step is over - this is the v(t+dt) point */
 +                    enerd->term[F_TEMP] = sum_ekin(&(ir->opts), ekind, NULL, (ir->eI == eiVV), FALSE);
 +                    enerd->term[F_EKIN] = trace(ekind->ekin);
 +                }
 +            }
 +            /* if it's the initial step, we performed this first step just to get the constraint virial */
 +            if (bInitStep && ir->eI == eiVV)
 +            {
 +                copy_rvecn(cbuf, state->v, 0, state->natoms);
 +            }
 +        }
 +
 +        /* MRS -- now done iterating -- compute the conserved quantity */
 +        if (bVV)
 +        {
 +            saved_conserved_quantity = compute_conserved_from_auxiliary(ir, state, &MassQ);
 +            if (ir->eI == eiVV)
 +            {
 +                last_ekin = enerd->term[F_EKIN];
 +            }
 +            if ((ir->eDispCorr != edispcEnerPres) && (ir->eDispCorr != edispcAllEnerPres))
 +            {
 +                saved_conserved_quantity -= enerd->term[F_DISPCORR];
 +            }
 +            /* sum up the foreign energy and dhdl terms for vv.  currently done every step so that dhdl is correct in the .edr */
 +            if (!bRerunMD)
 +            {
 +                sum_dhdl(enerd, state->lambda, ir->fepvals);
 +            }
 +        }
 +
 +        /* ########  END FIRST UPDATE STEP  ############## */
 +        /* ########  If doing VV, we now have v(dt) ###### */
 +        if (bDoExpanded)
 +        {
 +            /* perform extended ensemble sampling in lambda - we don't
 +               actually move to the new state before outputting
 +               statistics, but if performing simulated tempering, we
 +               do update the velocities and the tau_t. */
 +
 +            lamnew = ExpandedEnsembleDynamics(fplog, ir, enerd, state, &MassQ, state->fep_state, &state->dfhist, step, state->v, mdatoms);
 +            /* history is maintained in state->dfhist, but state_global is what is sent to trajectory and log output */
 +            copy_df_history(&state_global->dfhist, &state->dfhist);
 +        }
 +
 +        /* Now we have the energies and forces corresponding to the
 +         * coordinates at time t. We must output all of this before
 +         * the update.
 +         */
 +        do_md_trajectory_writing(fplog, cr, nfile, fnm, step, step_rel, t,
 +                                 ir, state, state_global, top_global, fr,
 +                                 outf, mdebin, ekind, f, f_global,
 +                                 wcycle, &nchkpt,
 +                                 bCPT, bRerunMD, bLastStep, (Flags & MD_CONFOUT),
 +                                 bSumEkinhOld);
 +        /* Check if IMD step and do IMD communication, if bIMD is TRUE. */
 +        bIMDstep = do_IMD(ir->bIMD, step, cr, bNS, state->box, state->x, ir, t, wcycle);
 +
 +        /* kludge -- virial is lost with restart for NPT control. Must restart */
 +        if (bStartingFromCpt && bVV)
 +        {
 +            copy_mat(state->svir_prev, shake_vir);
 +            copy_mat(state->fvir_prev, force_vir);
 +        }
 +
 +        elapsed_time = walltime_accounting_get_current_elapsed_time(walltime_accounting);
 +
 +        /* Check whether everything is still allright */
 +        if (((int)gmx_get_stop_condition() > handled_stop_condition)
 +#ifdef GMX_THREAD_MPI
 +            && MASTER(cr)
 +#endif
 +            )
 +        {
 +            /* this is just make gs.sig compatible with the hack
 +               of sending signals around by MPI_Reduce with together with
 +               other floats */
 +            if (gmx_get_stop_condition() == gmx_stop_cond_next_ns)
 +            {
 +                gs.sig[eglsSTOPCOND] = 1;
 +            }
 +            if (gmx_get_stop_condition() == gmx_stop_cond_next)
 +            {
 +                gs.sig[eglsSTOPCOND] = -1;
 +            }
 +            /* < 0 means stop at next step, > 0 means stop at next NS step */
 +            if (fplog)
 +            {
 +                fprintf(fplog,
 +                        "\n\nReceived the %s signal, stopping at the next %sstep\n\n",
 +                        gmx_get_signal_name(),
 +                        gs.sig[eglsSTOPCOND] == 1 ? "NS " : "");
 +                fflush(fplog);
 +            }
 +            fprintf(stderr,
 +                    "\n\nReceived the %s signal, stopping at the next %sstep\n\n",
 +                    gmx_get_signal_name(),
 +                    gs.sig[eglsSTOPCOND] == 1 ? "NS " : "");
 +            fflush(stderr);
 +            handled_stop_condition = (int)gmx_get_stop_condition();
 +        }
 +        else if (MASTER(cr) && (bNS || ir->nstlist <= 0) &&
 +                 (max_hours > 0 && elapsed_time > max_hours*60.0*60.0*0.99) &&
 +                 gs.sig[eglsSTOPCOND] == 0 && gs.set[eglsSTOPCOND] == 0)
 +        {
 +            /* Signal to terminate the run */
 +            gs.sig[eglsSTOPCOND] = 1;
 +            if (fplog)
 +            {
 +                fprintf(fplog, "\nStep %s: Run time exceeded %.3f hours, will terminate the run\n", gmx_step_str(step, sbuf), max_hours*0.99);
 +            }
 +            fprintf(stderr, "\nStep %s: Run time exceeded %.3f hours, will terminate the run\n", gmx_step_str(step, sbuf), max_hours*0.99);
 +        }
 +
 +        if (bResetCountersHalfMaxH && MASTER(cr) &&
 +            elapsed_time > max_hours*60.0*60.0*0.495)
 +        {
 +            gs.sig[eglsRESETCOUNTERS] = 1;
 +        }
 +
 +        if (ir->nstlist == -1 && !bRerunMD)
 +        {
 +            /* When bGStatEveryStep=FALSE, global_stat is only called
 +             * when we check the atom displacements, not at NS steps.
 +             * This means that also the bonded interaction count check is not
 +             * performed immediately after NS. Therefore a few MD steps could
 +             * be performed with missing interactions.
 +             * But wrong energies are never written to file,
 +             * since energies are only written after global_stat
 +             * has been called.
 +             */
 +            if (step >= nlh.step_nscheck)
 +            {
 +                nlh.nabnsb = natoms_beyond_ns_buffer(ir, fr, &top->cgs,
 +                                                     nlh.scale_tot, state->x);
 +            }
 +            else
 +            {
 +                /* This is not necessarily true,
 +                 * but step_nscheck is determined quite conservatively.
 +                 */
 +                nlh.nabnsb = 0;
 +            }
 +        }
 +
 +        /* In parallel we only have to check for checkpointing in steps
 +         * where we do global communication,
 +         *  otherwise the other nodes don't know.
 +         */
 +        if (MASTER(cr) && ((bGStat || !PAR(cr)) &&
 +                           cpt_period >= 0 &&
 +                           (cpt_period == 0 ||
 +                            elapsed_time >= nchkpt*cpt_period*60.0)) &&
 +            gs.set[eglsCHKPT] == 0)
 +        {
 +            gs.sig[eglsCHKPT] = 1;
 +        }
 +
 +        /* at the start of step, randomize or scale the velocities (trotter done elsewhere) */
 +        if (EI_VV(ir->eI))
 +        {
 +            if (!bInitStep)
 +            {
 +                update_tcouple(step, ir, state, ekind, &MassQ, mdatoms);
 +            }
 +            if (ETC_ANDERSEN(ir->etc)) /* keep this outside of update_tcouple because of the extra info required to pass */
 +            {
 +                gmx_bool bIfRandomize;
 +                bIfRandomize = update_randomize_velocities(ir, step, cr, mdatoms, state, upd, constr);
 +                /* if we have constraints, we have to remove the kinetic energy parallel to the bonds */
 +                if (constr && bIfRandomize)
 +                {
 +                    update_constraints(fplog, step, NULL, ir, ekind, mdatoms,
 +                                       state, fr->bMolPBC, graph, f,
 +                                       &top->idef, tmp_vir,
 +                                       cr, nrnb, wcycle, upd, constr,
 +                                       TRUE, bCalcVir, vetanew);
 +                }
 +            }
 +        }
 +
 +        if (bIterativeCase && do_per_step(step, ir->nstpcouple))
 +        {
 +            gmx_iterate_init(&iterate, TRUE);
 +            /* for iterations, we save these vectors, as we will be redoing the calculations */
 +            copy_coupling_state(state, bufstate, ekind, ekind_save, &(ir->opts));
 +        }
 +
 +        bFirstIterate = TRUE;
 +        while (bFirstIterate || iterate.bIterationActive)
 +        {
 +            /* We now restore these vectors to redo the calculation with improved extended variables */
 +            if (iterate.bIterationActive)
 +            {
 +                copy_coupling_state(bufstate, state, ekind_save, ekind, &(ir->opts));
 +            }
 +
 +            /* We make the decision to break or not -after- the calculation of Ekin and Pressure,
 +               so scroll down for that logic */
 +
 +            /* #########   START SECOND UPDATE STEP ################# */
 +            /* Box is changed in update() when we do pressure coupling,
 +             * but we should still use the old box for energy corrections and when
 +             * writing it to the energy file, so it matches the trajectory files for
 +             * the same timestep above. Make a copy in a separate array.
 +             */
 +            copy_mat(state->box, lastbox);
 +
 +            bOK         = TRUE;
 +            dvdl_constr = 0;
 +
 +            if (!(bRerunMD && !rerun_fr.bV && !bForceUpdate))
 +            {
 +                wallcycle_start(wcycle, ewcUPDATE);
 +                /* UPDATE PRESSURE VARIABLES IN TROTTER FORMULATION WITH CONSTRAINTS */
 +                if (bTrotter)
 +                {
 +                    if (iterate.bIterationActive)
 +                    {
 +                        if (bFirstIterate)
 +                        {
 +                            scalevir = 1;
 +                        }
 +                        else
 +                        {
 +                            /* we use a new value of scalevir to converge the iterations faster */
 +                            scalevir = tracevir/trace(shake_vir);
 +                        }
 +                        msmul(shake_vir, scalevir, shake_vir);
 +                        m_add(force_vir, shake_vir, total_vir);
 +                        clear_mat(shake_vir);
 +                    }
 +                    trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ3);
 +                    /* We can only do Berendsen coupling after we have summed
 +                     * the kinetic energy or virial. Since the happens
 +                     * in global_state after update, we should only do it at
 +                     * step % nstlist = 1 with bGStatEveryStep=FALSE.
 +                     */
 +                }
 +                else
 +                {
 +                    update_tcouple(step, ir, state, ekind, &MassQ, mdatoms);
 +                    update_pcouple(fplog, step, ir, state, pcoupl_mu, M, bInitStep);
 +                }
 +
 +                if (bVV)
 +                {
 +                    bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr));
 +
 +                    /* velocity half-step update */
 +                    update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC, f,
 +                                  bUpdateDoLR, fr->f_twin, bCalcVir ? &fr->vir_twin_constr : NULL, fcd,
 +                                  ekind, M, upd, FALSE, etrtVELOCITY2,
 +                                  cr, nrnb, constr, &top->idef);
 +                }
 +
 +                /* Above, initialize just copies ekinh into ekin,
 +                 * it doesn't copy position (for VV),
 +                 * and entire integrator for MD.
 +                 */
 +
 +                if (ir->eI == eiVVAK)
 +                {
 +                    copy_rvecn(state->x, cbuf, 0, state->natoms);
 +                }
 +                bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr));
 +
 +                update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC, f,
 +                              bUpdateDoLR, fr->f_twin, bCalcVir ? &fr->vir_twin_constr : NULL, fcd,
 +                              ekind, M, upd, bInitStep, etrtPOSITION, cr, nrnb, constr, &top->idef);
 +                wallcycle_stop(wcycle, ewcUPDATE);
 +
 +                update_constraints(fplog, step, &dvdl_constr, ir, ekind, mdatoms, state,
 +                                   fr->bMolPBC, graph, f,
 +                                   &top->idef, shake_vir,
 +                                   cr, nrnb, wcycle, upd, constr,
 +                                   FALSE, bCalcVir, state->veta);
 +
 +                if (bCalcVir && bUpdateDoLR && ir->nstcalclr > 1)
 +                {
 +                    /* Correct the virial for multiple time stepping */
 +                    m_sub(shake_vir, fr->vir_twin_constr, shake_vir);
 +                }
 +
 +                if (ir->eI == eiVVAK)
 +                {
 +                    /* erase F_EKIN and F_TEMP here? */
 +                    /* just compute the kinetic energy at the half step to perform a trotter step */
 +                    compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                                    wcycle, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                                    constr, NULL, FALSE, lastbox,
 +                                    top_global, &bSumEkinhOld,
 +                                    cglo_flags | CGLO_TEMPERATURE
 +                                    );
 +                    wallcycle_start(wcycle, ewcUPDATE);
 +                    trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ4);
 +                    /* now we know the scaling, we can compute the positions again again */
 +                    copy_rvecn(cbuf, state->x, 0, state->natoms);
 +
 +                    bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr));
 +
 +                    update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC, f,
 +                                  bUpdateDoLR, fr->f_twin, bCalcVir ? &fr->vir_twin_constr : NULL, fcd,
 +                                  ekind, M, upd, bInitStep, etrtPOSITION, cr, nrnb, constr, &top->idef);
 +                    wallcycle_stop(wcycle, ewcUPDATE);
 +
 +                    /* do we need an extra constraint here? just need to copy out of state->v to upd->xp? */
 +                    /* are the small terms in the shake_vir here due
 +                     * to numerical errors, or are they important
 +                     * physically? I'm thinking they are just errors, but not completely sure.
 +                     * For now, will call without actually constraining, constr=NULL*/
 +                    update_constraints(fplog, step, NULL, ir, ekind, mdatoms,
 +                                       state, fr->bMolPBC, graph, f,
 +                                       &top->idef, tmp_vir,
 +                                       cr, nrnb, wcycle, upd, NULL,
 +                                       FALSE, bCalcVir,
 +                                       state->veta);
 +                }
 +                if (!bOK)
 +                {
 +                    gmx_fatal(FARGS, "Constraint error: Shake, Lincs or Settle could not solve the constrains");
 +                }
 +
 +                if (fr->bSepDVDL && fplog && do_log)
 +                {
 +                    gmx_print_sepdvdl(fplog, "Constraint dV/dl", 0.0, dvdl_constr);
 +                }
 +                if (bVV)
 +                {
 +                    /* this factor or 2 correction is necessary
 +                       because half of the constraint force is removed
 +                       in the vv step, so we have to double it.  See
 +                       the Redmine issue #1255.  It is not yet clear
 +                       if the factor of 2 is exact, or just a very
 +                       good approximation, and this will be
 +                       investigated.  The next step is to see if this
 +                       can be done adding a dhdl contribution from the
 +                       rattle step, but this is somewhat more
 +                       complicated with the current code. Will be
 +                       investigated, hopefully for 4.6.3. However,
 +                       this current solution is much better than
 +                       having it completely wrong.
 +                     */
 +                    enerd->term[F_DVDL_CONSTR] += 2*dvdl_constr;
 +                }
 +                else
 +                {
 +                    enerd->term[F_DVDL_CONSTR] += dvdl_constr;
 +                }
 +            }
 +            else if (graph)
 +            {
 +                /* Need to unshift here */
 +                unshift_self(graph, state->box, state->x);
 +            }
 +
 +            if (vsite != NULL)
 +            {
 +                wallcycle_start(wcycle, ewcVSITECONSTR);
 +                if (graph != NULL)
 +                {
 +                    shift_self(graph, state->box, state->x);
 +                }
 +                construct_vsites(vsite, state->x, ir->delta_t, state->v,
 +                                 top->idef.iparams, top->idef.il,
 +                                 fr->ePBC, fr->bMolPBC, cr, state->box);
 +
 +                if (graph != NULL)
 +                {
 +                    unshift_self(graph, state->box, state->x);
 +                }
 +                wallcycle_stop(wcycle, ewcVSITECONSTR);
 +            }
 +
 +            /* ############## IF NOT VV, Calculate globals HERE, also iterate constraints  ############ */
 +            /* With Leap-Frog we can skip compute_globals at
 +             * non-communication steps, but we need to calculate
 +             * the kinetic energy one step before communication.
 +             */
 +            if (bGStat || (!EI_VV(ir->eI) && do_per_step(step+1, nstglobalcomm)))
 +            {
 +                if (ir->nstlist == -1 && bFirstIterate)
 +                {
 +                    gs.sig[eglsNABNSB] = nlh.nabnsb;
 +                }
 +                compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm,
 +                                wcycle, enerd, force_vir, shake_vir, total_vir, pres, mu_tot,
 +                                constr,
 +                                bFirstIterate ? &gs : NULL,
 +                                (step_rel % gs.nstms == 0) &&
 +                                (multisim_nsteps < 0 || (step_rel < multisim_nsteps)),
 +                                lastbox,
 +                                top_global, &bSumEkinhOld,
 +                                cglo_flags
 +                                | (!EI_VV(ir->eI) || bRerunMD ? CGLO_ENERGY : 0)
 +                                | (!EI_VV(ir->eI) && bStopCM ? CGLO_STOPCM : 0)
 +                                | (!EI_VV(ir->eI) ? CGLO_TEMPERATURE : 0)
 +                                | (!EI_VV(ir->eI) || bRerunMD ? CGLO_PRESSURE : 0)
 +                                | (iterate.bIterationActive ? CGLO_ITERATE : 0)
 +                                | (bFirstIterate ? CGLO_FIRSTITERATE : 0)
 +                                | CGLO_CONSTRAINT
 +                                );
 +                if (ir->nstlist == -1 && bFirstIterate)
 +                {
 +                    nlh.nabnsb         = gs.set[eglsNABNSB];
 +                    gs.set[eglsNABNSB] = 0;
 +                }
 +            }
 +            /* bIterate is set to keep it from eliminating the old ekin kinetic energy terms */
 +            /* #############  END CALC EKIN AND PRESSURE ################# */
 +
 +            /* Note: this is OK, but there are some numerical precision issues with using the convergence of
 +               the virial that should probably be addressed eventually. state->veta has better properies,
 +               but what we actually need entering the new cycle is the new shake_vir value. Ideally, we could
 +               generate the new shake_vir, but test the veta value for convergence.  This will take some thought. */
 +
 +            if (iterate.bIterationActive &&
 +                done_iterating(cr, fplog, step, &iterate, bFirstIterate,
 +                               trace(shake_vir), &tracevir))
 +            {
 +                break;
 +            }
 +            bFirstIterate = FALSE;
 +        }
 +
 +        if (!bVV || bRerunMD)
 +        {
 +            /* sum up the foreign energy and dhdl terms for md and sd. currently done every step so that dhdl is correct in the .edr */
 +            sum_dhdl(enerd, state->lambda, ir->fepvals);
 +        }
 +        update_box(fplog, step, ir, mdatoms, state, f,
 +                   ir->nstlist == -1 ? &nlh.scale_tot : NULL, pcoupl_mu, nrnb, upd);
 +
 +        /* ################# END UPDATE STEP 2 ################# */
 +        /* #### We now have r(t+dt) and v(t+dt/2)  ############# */
 +
 +        /* The coordinates (x) were unshifted in update */
 +        if (!bGStat)
 +        {
 +            /* We will not sum ekinh_old,
 +             * so signal that we still have to do it.
 +             */
 +            bSumEkinhOld = TRUE;
 +        }
 +
 +        /* #########  BEGIN PREPARING EDR OUTPUT  ###########  */
 +
 +        /* use the directly determined last velocity, not actually the averaged half steps */
 +        if (bTrotter && ir->eI == eiVV)
 +        {
 +            enerd->term[F_EKIN] = last_ekin;
 +        }
 +        enerd->term[F_ETOT] = enerd->term[F_EPOT] + enerd->term[F_EKIN];
 +
 +        if (bVV)
 +        {
 +            enerd->term[F_ECONSERVED] = enerd->term[F_ETOT] + saved_conserved_quantity;
 +        }
 +        else
 +        {
 +            enerd->term[F_ECONSERVED] = enerd->term[F_ETOT] + compute_conserved_from_auxiliary(ir, state, &MassQ);
 +        }
 +        /* #########  END PREPARING EDR OUTPUT  ###########  */
 +
 +        /* Output stuff */
 +        if (MASTER(cr))
 +        {
 +            gmx_bool do_dr, do_or;
 +
 +            if (fplog && do_log && bDoExpanded)
 +            {
 +                /* only needed if doing expanded ensemble */
 +                PrintFreeEnergyInfoToFile(fplog, ir->fepvals, ir->expandedvals, ir->bSimTemp ? ir->simtempvals : NULL,
 +                                          &state_global->dfhist, state->fep_state, ir->nstlog, step);
 +            }
 +            if (!(bStartingFromCpt && (EI_VV(ir->eI))))
 +            {
 +                if (bCalcEner)
 +                {
 +                    upd_mdebin(mdebin, bDoDHDL, TRUE,
 +                               t, mdatoms->tmass, enerd, state,
 +                               ir->fepvals, ir->expandedvals, lastbox,
 +                               shake_vir, force_vir, total_vir, pres,
 +                               ekind, mu_tot, constr);
 +                }
 +                else
 +                {
 +                    upd_mdebin_step(mdebin);
 +                }
 +
 +                do_dr  = do_per_step(step, ir->nstdisreout);
 +                do_or  = do_per_step(step, ir->nstorireout);
 +
 +                print_ebin(mdoutf_get_fp_ene(outf), do_ene, do_dr, do_or, do_log ? fplog : NULL,
 +                           step, t,
 +                           eprNORMAL, bCompact, mdebin, fcd, groups, &(ir->opts));
 +            }
 +            if (ir->ePull != epullNO)
 +            {
 +                pull_print_output(ir->pull, step, t);
 +            }
 +
 +            if (do_per_step(step, ir->nstlog))
 +            {
 +                if (fflush(fplog) != 0)
 +                {
 +                    gmx_fatal(FARGS, "Cannot flush logfile - maybe you are out of disk space?");
 +                }
 +            }
 +        }
 +        if (bDoExpanded)
 +        {
 +            /* Have to do this part _after_ outputting the logfile and the edr file */
 +            /* Gets written into the state at the beginning of next loop*/
 +            state->fep_state = lamnew;
 +        }
 +        /* Print the remaining wall clock time for the run */
 +        if (MULTIMASTER(cr) && (do_verbose || gmx_got_usr_signal()) && !bPMETuneRunning)
 +        {
 +            if (shellfc)
 +            {
 +                fprintf(stderr, "\n");
 +            }
 +            print_time(stderr, walltime_accounting, step, ir, cr);
 +        }
 +
 +        /* Ion/water position swapping.
 +         * Not done in last step since trajectory writing happens before this call
 +         * in the MD loop and exchanges would be lost anyway. */
 +        bNeedRepartition = FALSE;
 +        if ((ir->eSwapCoords != eswapNO) && (step > 0) && !bLastStep &&
 +            do_per_step(step, ir->swap->nstswap))
 +        {
 +            bNeedRepartition = do_swapcoords(cr, step, t, ir, wcycle,
 +                                             bRerunMD ? rerun_fr.x   : state->x,
 +                                             bRerunMD ? rerun_fr.box : state->box,
 +                                             top_global, MASTER(cr) && bVerbose, bRerunMD);
 +
 +            if (bNeedRepartition && DOMAINDECOMP(cr))
 +            {
 +                dd_collect_state(cr->dd, state, state_global);
 +            }
 +        }
 +
 +        /* Replica exchange */
 +        bExchanged = FALSE;
 +        if (bDoReplEx)
 +        {
 +            bExchanged = replica_exchange(fplog, cr, repl_ex,
 +                                          state_global, enerd,
 +                                          state, step, t);
 +        }
 +
 +        if ( (bExchanged || bNeedRepartition) && DOMAINDECOMP(cr) )
 +        {
 +            dd_partition_system(fplog, step, cr, TRUE, 1,
 +                                state_global, top_global, ir,
 +                                state, &f, mdatoms, top, fr,
 +                                vsite, shellfc, constr,
 +                                nrnb, wcycle, FALSE);
 +        }
 +
 +        bFirstStep       = FALSE;
 +        bInitStep        = FALSE;
 +        bStartingFromCpt = FALSE;
 +
 +        /* #######  SET VARIABLES FOR NEXT ITERATION IF THEY STILL NEED IT ###### */
 +        /* With all integrators, except VV, we need to retain the pressure
 +         * at the current step for coupling at the next step.
 +         */
 +        if ((state->flags & (1<<estPRES_PREV)) &&
 +            (bGStatEveryStep ||
 +             (ir->nstpcouple > 0 && step % ir->nstpcouple == 0)))
 +        {
 +            /* Store the pressure in t_state for pressure coupling
 +             * at the next MD step.
 +             */
 +            copy_mat(pres, state->pres_prev);
 +        }
 +
 +        /* #######  END SET VARIABLES FOR NEXT ITERATION ###### */
 +
 +        if ( (membed != NULL) && (!bLastStep) )
 +        {
 +            rescale_membed(step_rel, membed, state_global->x);
 +        }
 +
 +        if (bRerunMD)
 +        {
 +            if (MASTER(cr))
 +            {
 +                /* read next frame from input trajectory */
 +                bNotLastFrame = read_next_frame(oenv, status, &rerun_fr);
 +            }
 +
 +            if (PAR(cr))
 +            {
 +                rerun_parallel_comm(cr, &rerun_fr, &bNotLastFrame);
 +            }
 +        }
 +
 +        if (!bRerunMD || !rerun_fr.bStep)
 +        {
 +            /* increase the MD step number */
 +            step++;
 +            step_rel++;
 +        }
 +
 +        cycles = wallcycle_stop(wcycle, ewcSTEP);
 +        if (DOMAINDECOMP(cr) && wcycle)
 +        {
 +            dd_cycles_add(cr->dd, cycles, ddCyclStep);
 +        }
 +
 +        if (bPMETuneRunning || bPMETuneTry)
 +        {
 +            /* PME grid + cut-off optimization with GPUs or PME nodes */
 +
 +            /* Count the total cycles over the last steps */
 +            cycles_pmes += cycles;
 +
 +            /* We can only switch cut-off at NS steps */
 +            if (step % ir->nstlist == 0)
 +            {
 +                /* PME grid + cut-off optimization with GPUs or PME nodes */
 +                if (bPMETuneTry)
 +                {
 +                    if (DDMASTER(cr->dd))
 +                    {
 +                        /* PME node load is too high, start tuning */
 +                        bPMETuneRunning = (dd_pme_f_ratio(cr->dd) >= 1.05);
 +                    }
 +                    dd_bcast(cr->dd, sizeof(gmx_bool), &bPMETuneRunning);
 +
 +                    if (bPMETuneRunning || step_rel > ir->nstlist*50)
 +                    {
 +                        bPMETuneTry     = FALSE;
 +                    }
 +                }
 +                if (bPMETuneRunning)
 +                {
 +                    /* init_step might not be a multiple of nstlist,
 +                     * but the first cycle is always skipped anyhow.
 +                     */
 +                    bPMETuneRunning =
 +                        pme_load_balance(pme_loadbal, cr,
 +                                         (bVerbose && MASTER(cr)) ? stderr : NULL,
 +                                         fplog,
 +                                         ir, state, cycles_pmes,
 +                                         fr->ic, fr->nbv, &fr->pmedata,
 +                                         step);
 +
 +                    /* Update constants in forcerec/inputrec to keep them in sync with fr->ic */
 +                    fr->ewaldcoeff_q  = fr->ic->ewaldcoeff_q;
 +                    fr->ewaldcoeff_lj = fr->ic->ewaldcoeff_lj;
 +                    fr->rlist         = fr->ic->rlist;
 +                    fr->rlistlong     = fr->ic->rlistlong;
 +                    fr->rcoulomb      = fr->ic->rcoulomb;
 +                    fr->rvdw          = fr->ic->rvdw;
 +
 +                    if (ir->eDispCorr != edispcNO)
 +                    {
 +                        calc_enervirdiff(NULL, ir->eDispCorr, fr);
 +                    }
 +                }
 +                cycles_pmes = 0;
 +            }
 +        }
 +
 +        if (step_rel == wcycle_get_reset_counters(wcycle) ||
 +            gs.set[eglsRESETCOUNTERS] != 0)
 +        {
 +            /* Reset all the counters related to performance over the run */
 +            reset_all_counters(fplog, cr, step, &step_rel, ir, wcycle, nrnb, walltime_accounting,
 +                               fr->nbv != NULL && fr->nbv->bUseGPU ? fr->nbv->cu_nbv : NULL);
 +            wcycle_set_reset_counters(wcycle, -1);
 +            if (!(cr->duty & DUTY_PME))
 +            {
 +                /* Tell our PME node to reset its counters */
 +                gmx_pme_send_resetcounters(cr, step);
 +            }
 +            /* Correct max_hours for the elapsed time */
 +            max_hours                -= elapsed_time/(60.0*60.0);
 +            bResetCountersHalfMaxH    = FALSE;
 +            gs.set[eglsRESETCOUNTERS] = 0;
 +        }
 +
 +        /* If bIMD is TRUE, the master updates the IMD energy record and sends positions to VMD client */
 +        IMD_prep_energies_send_positions(ir->bIMD && MASTER(cr), bIMDstep, ir->imd, enerd, step, bCalcEner, wcycle);
 +
 +    }
 +    /* End of main MD loop */
 +    debug_gmx();
 +
 +    /* Stop measuring walltime */
 +    walltime_accounting_end(walltime_accounting);
 +
 +    if (bRerunMD && MASTER(cr))
 +    {
 +        close_trj(status);
 +    }
 +
 +    if (!(cr->duty & DUTY_PME))
 +    {
 +        /* Tell the PME only node to finish */
 +        gmx_pme_send_finish(cr);
 +    }
 +
 +    if (MASTER(cr))
 +    {
 +        if (ir->nstcalcenergy > 0 && !bRerunMD)
 +        {
 +            print_ebin(mdoutf_get_fp_ene(outf), FALSE, FALSE, FALSE, fplog, step, t,
 +                       eprAVER, FALSE, mdebin, fcd, groups, &(ir->opts));
 +        }
 +    }
 +
 +    done_mdoutf(outf);
 +    debug_gmx();
 +
 +    if (ir->nstlist == -1 && nlh.nns > 0 && fplog)
 +    {
 +        fprintf(fplog, "Average neighborlist lifetime: %.1f steps, std.dev.: %.1f steps\n", nlh.s1/nlh.nns, sqrt(nlh.s2/nlh.nns - sqr(nlh.s1/nlh.nns)));
 +        fprintf(fplog, "Average number of atoms that crossed the half buffer length: %.1f\n\n", nlh.ab/nlh.nns);
 +    }
 +
 +    if (pme_loadbal != NULL)
 +    {
 +        pme_loadbal_done(pme_loadbal, cr, fplog,
 +                         fr->nbv != NULL && fr->nbv->bUseGPU);
 +    }
 +
 +    if (shellfc && fplog)
 +    {
 +        fprintf(fplog, "Fraction of iterations that converged:           %.2f %%\n",
 +                (nconverged*100.0)/step_rel);
 +        fprintf(fplog, "Average number of force evaluations per MD step: %.2f\n\n",
 +                tcount/step_rel);
 +    }
 +
 +    if (repl_ex_nst > 0 && MASTER(cr))
 +    {
 +        print_replica_exchange_statistics(fplog, repl_ex);
 +    }
 +
 +    /* IMD cleanup, if bIMD is TRUE. */
 +    IMD_finalize(ir->bIMD, ir->imd);
 +
 +    walltime_accounting_set_nsteps_done(walltime_accounting, step_rel);
 +
 +    return 0;
 +}
index 41a4021775e2facce77370a5d7b5341c4ffdcfb4,0000000000000000000000000000000000000000..cf69f96a2b53a93ae57c370eedabe0e494c42f03
mode 100644,000000..100644
--- /dev/null
@@@ -1,797 -1,0 +1,800 @@@
-         "this is computationally more efficient starting at about 12 nodes.",
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2011,2012,2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "mdrun_main.h"
 +
 +#ifdef HAVE_CONFIG_H
 +#include "config.h"
 +#endif
 +
 +#include <stdio.h>
 +
 +#include "gromacs/legacyheaders/checkpoint.h"
 +#include "gromacs/legacyheaders/copyrite.h"
 +#include "gromacs/legacyheaders/gmx_fatal.h"
 +#include "gromacs/legacyheaders/macros.h"
 +#include "gromacs/legacyheaders/main.h"
 +#include "gromacs/legacyheaders/mdrun.h"
 +#include "gromacs/legacyheaders/network.h"
 +#include "gromacs/legacyheaders/readinp.h"
 +#include "gromacs/legacyheaders/typedefs.h"
 +#include "gromacs/legacyheaders/types/commrec.h"
 +
 +#include "gromacs/commandline/pargs.h"
 +#include "gromacs/fileio/filenm.h"
 +
 +int gmx_mdrun(int argc, char *argv[])
 +{
 +    const char   *desc[] = {
 +        "[THISMODULE] is the main computational chemistry engine",
 +        "within GROMACS. Obviously, it performs Molecular Dynamics simulations,",
 +        "but it can also perform Stochastic Dynamics, Energy Minimization,",
 +        "test particle insertion or (re)calculation of energies.",
 +        "Normal mode analysis is another option. In this case [TT]mdrun[tt]",
 +        "builds a Hessian matrix from single conformation.",
 +        "For usual Normal Modes-like calculations, make sure that",
 +        "the structure provided is properly energy-minimized.",
 +        "The generated matrix can be diagonalized by [gmx-nmeig].[PAR]",
 +        "The [TT]mdrun[tt] program reads the run input file ([TT]-s[tt])",
 +        "and distributes the topology over nodes if needed.",
 +        "[TT]mdrun[tt] produces at least four output files.",
 +        "A single log file ([TT]-g[tt]) is written, unless the option",
 +        "[TT]-seppot[tt] is used, in which case each node writes a log file.",
 +        "The trajectory file ([TT]-o[tt]), contains coordinates, velocities and",
 +        "optionally forces.",
 +        "The structure file ([TT]-c[tt]) contains the coordinates and",
 +        "velocities of the last step.",
 +        "The energy file ([TT]-e[tt]) contains energies, the temperature,",
 +        "pressure, etc, a lot of these things are also printed in the log file.",
 +        "Optionally coordinates can be written to a compressed trajectory file",
 +        "([TT]-x[tt]).[PAR]",
 +        "The option [TT]-dhdl[tt] is only used when free energy calculation is",
 +        "turned on.[PAR]",
 +        "A simulation can be run in parallel using two different parallelization",
 +        "schemes: MPI parallelization and/or OpenMP thread parallelization.",
 +        "The MPI parallelization uses multiple processes when [TT]mdrun[tt] is",
 +        "compiled with a normal MPI library or threads when [TT]mdrun[tt] is",
 +        "compiled with the GROMACS built-in thread-MPI library. OpenMP threads",
 +        "are supported when [TT]mdrun[tt] is compiled with OpenMP. Full OpenMP support",
 +        "is only available with the Verlet cut-off scheme, with the (older)",
 +        "group scheme only PME-only processes can use OpenMP parallelization.",
 +        "In all cases [TT]mdrun[tt] will by default try to use all the available",
 +        "hardware resources. With a normal MPI library only the options",
 +        "[TT]-ntomp[tt] (with the Verlet cut-off scheme) and [TT]-ntomp_pme[tt],",
 +        "for PME-only processes, can be used to control the number of threads.",
 +        "With thread-MPI there are additional options [TT]-nt[tt], which sets",
 +        "the total number of threads, and [TT]-ntmpi[tt], which sets the number",
 +        "of thread-MPI threads.",
 +        "The number of OpenMP threads used by [TT]mdrun[tt] can also be set with",
 +        "the standard environment variable, [TT]OMP_NUM_THREADS[tt].",
 +        "The [TT]GMX_PME_NUM_THREADS[tt] environment variable can be used to specify",
 +        "the number of threads used by the PME-only processes.[PAR]",
 +        "Note that combined MPI+OpenMP parallelization is in many cases",
 +        "slower than either on its own. However, at high parallelization, using the",
 +        "combination is often beneficial as it reduces the number of domains and/or",
 +        "the number of MPI ranks. (Less and larger domains can improve scaling,",
 +        "with separate PME processes fewer MPI ranks reduces communication cost.)",
 +        "OpenMP-only parallelization is typically faster than MPI-only parallelization",
 +        "on a single CPU(-die). Since we currently don't have proper hardware",
 +        "topology detection, [TT]mdrun[tt] compiled with thread-MPI will only",
 +        "automatically use OpenMP-only parallelization when you use up to 4",
 +        "threads, up to 12 threads with Intel Nehalem/Westmere, or up to 16",
 +        "threads with Intel Sandy Bridge or newer CPUs. Otherwise MPI-only",
 +        "parallelization is used (except with GPUs, see below).",
 +        "[PAR]",
 +        "To quickly test the performance of the new Verlet cut-off scheme",
 +        "with old [TT].tpr[tt] files, either on CPUs or CPUs+GPUs, you can use",
 +        "the [TT]-testverlet[tt] option. This should not be used for production,",
 +        "since it can slightly modify potentials and it will remove charge groups",
 +        "making analysis difficult, as the [TT].tpr[tt] file will still contain",
 +        "charge groups. For production simulations it is highly recommended",
 +        "to specify [TT]cutoff-scheme = Verlet[tt] in the [TT].mdp[tt] file.",
 +        "[PAR]",
 +        "With GPUs (only supported with the Verlet cut-off scheme), the number",
 +        "of GPUs should match the number of MPI processes or MPI threads,",
 +        "excluding PME-only processes/threads. With thread-MPI, unless set on the command line, the number",
 +        "of MPI threads will automatically be set to the number of GPUs detected.",
 +        "To use a subset of the available GPUs, or to manually provide a mapping of",
 +        "GPUs to PP ranks, you can use the [TT]-gpu_id[tt] option. The argument of [TT]-gpu_id[tt] is",
 +        "a string of digits (without delimiter) representing device id-s of the GPUs to be used.",
 +        "For example, \"[TT]02[tt]\" specifies using GPUs 0 and 2 in the first and second PP ranks per compute node",
 +        "respectively. To select different sets of GPU-s",
 +        "on different nodes of a compute cluster, use the [TT]GMX_GPU_ID[tt] environment",
 +        "variable instead. The format for [TT]GMX_GPU_ID[tt] is identical to ",
 +        "[TT]-gpu_id[tt], with the difference that an environment variable can have",
 +        "different values on different compute nodes. Multiple MPI ranks on each node",
 +        "can share GPUs. This is accomplished by specifying the id(s) of the GPU(s)",
 +        "multiple times, e.g. \"[TT]0011[tt]\" for four ranks sharing two GPUs in this node.",
 +        "This works within a single simulation, or a multi-simulation, with any form of MPI.",
 +        "[PAR]",
 +        "With the Verlet cut-off scheme and verlet-buffer-tolerance set,",
 +        "the pair-list update interval nstlist can be chosen freely with",
 +        "the option [TT]-nstlist[tt]. [TT]mdrun[tt] will then adjust",
 +        "the pair-list cut-off to maintain accuracy, and not adjust nstlist.",
 +        "Otherwise, by default, [TT]mdrun[tt] will try to increase the",
 +        "value of nstlist set in the [TT].mdp[tt] file to improve the",
 +        "performance. For CPU-only runs, nstlist might increase to 20, for",
 +        "GPU runs up to 40. For medium to high parallelization or with",
 +        "fast GPUs, a (user-supplied) larger nstlist value can give much",
 +        "better performance.",
 +        "[PAR]",
 +        "When using PME with separate PME nodes or with a GPU, the two major",
 +        "compute tasks, the non-bonded force calculation and the PME calculation",
 +        "run on different compute resources. If this load is not balanced,",
 +        "some of the resources will be idle part of time. With the Verlet",
 +        "cut-off scheme this load is automatically balanced when the PME load",
 +        "is too high (but not when it is too low). This is done by scaling",
 +        "the Coulomb cut-off and PME grid spacing by the same amount. In the first",
 +        "few hundred steps different settings are tried and the fastest is chosen",
 +        "for the rest of the simulation. This does not affect the accuracy of",
 +        "the results, but it does affect the decomposition of the Coulomb energy",
 +        "into particle and mesh contributions. The auto-tuning can be turned off",
 +        "with the option [TT]-notunepme[tt].",
 +        "[PAR]",
 +        "[TT]mdrun[tt] pins (sets affinity of) threads to specific cores,",
 +        "when all (logical) cores on a compute node are used by [TT]mdrun[tt],",
 +        "even when no multi-threading is used,",
 +        "as this usually results in significantly better performance.",
 +        "If the queuing systems or the OpenMP library pinned threads, we honor",
 +        "this and don't pin again, even though the layout may be sub-optimal.",
 +        "If you want to have [TT]mdrun[tt] override an already set thread affinity",
 +        "or pin threads when using less cores, use [TT]-pin on[tt].",
 +        "With SMT (simultaneous multithreading), e.g. Intel Hyper-Threading,",
 +        "there are multiple logical cores per physical core.",
 +        "The option [TT]-pinstride[tt] sets the stride in logical cores for",
 +        "pinning consecutive threads. Without SMT, 1 is usually the best choice.",
 +        "With Intel Hyper-Threading 2 is best when using half or less of the",
 +        "logical cores, 1 otherwise. The default value of 0 do exactly that:",
 +        "it minimizes the threads per logical core, to optimize performance.",
 +        "If you want to run multiple [TT]mdrun[tt] jobs on the same physical node,"
 +        "you should set [TT]-pinstride[tt] to 1 when using all logical cores.",
 +        "When running multiple [TT]mdrun[tt] (or other) simulations on the same physical",
 +        "node, some simulations need to start pinning from a non-zero core",
 +        "to avoid overloading cores; with [TT]-pinoffset[tt] you can specify",
 +        "the offset in logical cores for pinning.",
 +        "[PAR]",
 +        "When [TT]mdrun[tt] is started using MPI with more than 1 process",
 +        "or with thread-MPI with more than 1 thread, MPI parallelization is used.",
 +        "Domain decomposition is always used with MPI parallelism.",
 +        "[PAR]",
 +        "With domain decomposition, the spatial decomposition can be set",
 +        "with option [TT]-dd[tt]. By default [TT]mdrun[tt] selects a good decomposition.",
 +        "The user only needs to change this when the system is very inhomogeneous.",
 +        "Dynamic load balancing is set with the option [TT]-dlb[tt],",
 +        "which can give a significant performance improvement,",
 +        "especially for inhomogeneous systems. The only disadvantage of",
 +        "dynamic load balancing is that runs are no longer binary reproducible,",
 +        "but in most cases this is not important.",
 +        "By default the dynamic load balancing is automatically turned on",
 +        "when the measured performance loss due to load imbalance is 5% or more.",
 +        "At low parallelization these are the only important options",
 +        "for domain decomposition.",
 +        "At high parallelization the options in the next two sections",
 +        "could be important for increasing the performace.",
 +        "[PAR]",
 +        "When PME is used with domain decomposition, separate nodes can",
 +        "be assigned to do only the PME mesh calculation;",
-         "nodes when the number of nodes is larger than 11 or performance wise",
-         "not compatible with the PME grid x dimension.",
-         "But the user should optimize npme. Performance statistics on this issue",
++        "this is computationally more efficient starting at about 12 nodes",
++        "or even fewer when OpenMP parallelization is used.",
 +        "The number of PME nodes is set with option [TT]-npme[tt],",
 +        "this can not be more than half of the nodes.",
 +        "By default [TT]mdrun[tt] makes a guess for the number of PME",
++        "nodes when the number of nodes is larger than 16. With GPUs,",
++        "PME nodes are not selected automatically, since the optimal setup",
++        "depends very much on the details of the hardware.",
++        "In all cases you might gain performance by optimizing [TT]-npme[tt].",
++        "Performance statistics on this issue",
 +        "are written at the end of the log file.",
 +        "For good load balancing at high parallelization, the PME grid x and y",
 +        "dimensions should be divisible by the number of PME nodes",
 +        "(the simulation will run correctly also when this is not the case).",
 +        "[PAR]",
 +        "This section lists all options that affect the domain decomposition.",
 +        "[PAR]",
 +        "Option [TT]-rdd[tt] can be used to set the required maximum distance",
 +        "for inter charge-group bonded interactions.",
 +        "Communication for two-body bonded interactions below the non-bonded",
 +        "cut-off distance always comes for free with the non-bonded communication.",
 +        "Atoms beyond the non-bonded cut-off are only communicated when they have",
 +        "missing bonded interactions; this means that the extra cost is minor",
 +        "and nearly indepedent of the value of [TT]-rdd[tt].",
 +        "With dynamic load balancing option [TT]-rdd[tt] also sets",
 +        "the lower limit for the domain decomposition cell sizes.",
 +        "By default [TT]-rdd[tt] is determined by [TT]mdrun[tt] based on",
 +        "the initial coordinates. The chosen value will be a balance",
 +        "between interaction range and communication cost.",
 +        "[PAR]",
 +        "When inter charge-group bonded interactions are beyond",
 +        "the bonded cut-off distance, [TT]mdrun[tt] terminates with an error message.",
 +        "For pair interactions and tabulated bonds",
 +        "that do not generate exclusions, this check can be turned off",
 +        "with the option [TT]-noddcheck[tt].",
 +        "[PAR]",
 +        "When constraints are present, option [TT]-rcon[tt] influences",
 +        "the cell size limit as well.",
 +        "Atoms connected by NC constraints, where NC is the LINCS order plus 1,",
 +        "should not be beyond the smallest cell size. A error message is",
 +        "generated when this happens and the user should change the decomposition",
 +        "or decrease the LINCS order and increase the number of LINCS iterations.",
 +        "By default [TT]mdrun[tt] estimates the minimum cell size required for P-LINCS",
 +        "in a conservative fashion. For high parallelization it can be useful",
 +        "to set the distance required for P-LINCS with the option [TT]-rcon[tt].",
 +        "[PAR]",
 +        "The [TT]-dds[tt] option sets the minimum allowed x, y and/or z scaling",
 +        "of the cells with dynamic load balancing. [TT]mdrun[tt] will ensure that",
 +        "the cells can scale down by at least this factor. This option is used",
 +        "for the automated spatial decomposition (when not using [TT]-dd[tt])",
 +        "as well as for determining the number of grid pulses, which in turn",
 +        "sets the minimum allowed cell size. Under certain circumstances",
 +        "the value of [TT]-dds[tt] might need to be adjusted to account for",
 +        "high or low spatial inhomogeneity of the system.",
 +        "[PAR]",
 +        "The option [TT]-gcom[tt] can be used to only do global communication",
 +        "every n steps.",
 +        "This can improve performance for highly parallel simulations",
 +        "where this global communication step becomes the bottleneck.",
 +        "For a global thermostat and/or barostat the temperature",
 +        "and/or pressure will also only be updated every [TT]-gcom[tt] steps.",
 +        "By default it is set to the minimum of nstcalcenergy and nstlist.[PAR]",
 +        "With [TT]-rerun[tt] an input trajectory can be given for which ",
 +        "forces and energies will be (re)calculated. Neighbor searching will be",
 +        "performed for every frame, unless [TT]nstlist[tt] is zero",
 +        "(see the [TT].mdp[tt] file).[PAR]",
 +        "ED (essential dynamics) sampling and/or additional flooding potentials",
 +        "are switched on by using the [TT]-ei[tt] flag followed by an [TT].edi[tt]",
 +        "file. The [TT].edi[tt] file can be produced with the [TT]make_edi[tt] tool",
 +        "or by using options in the essdyn menu of the WHAT IF program.",
 +        "[TT]mdrun[tt] produces a [TT].xvg[tt] output file that",
 +        "contains projections of positions, velocities and forces onto selected",
 +        "eigenvectors.[PAR]",
 +        "When user-defined potential functions have been selected in the",
 +        "[TT].mdp[tt] file the [TT]-table[tt] option is used to pass [TT]mdrun[tt]",
 +        "a formatted table with potential functions. The file is read from",
 +        "either the current directory or from the [TT]GMXLIB[tt] directory.",
 +        "A number of pre-formatted tables are presented in the [TT]GMXLIB[tt] dir,",
 +        "for 6-8, 6-9, 6-10, 6-11, 6-12 Lennard-Jones potentials with",
 +        "normal Coulomb.",
 +        "When pair interactions are present, a separate table for pair interaction",
 +        "functions is read using the [TT]-tablep[tt] option.[PAR]",
 +        "When tabulated bonded functions are present in the topology,",
 +        "interaction functions are read using the [TT]-tableb[tt] option.",
 +        "For each different tabulated interaction type the table file name is",
 +        "modified in a different way: before the file extension an underscore is",
 +        "appended, then a 'b' for bonds, an 'a' for angles or a 'd' for dihedrals",
 +        "and finally the table number of the interaction type.[PAR]",
 +        "The options [TT]-px[tt] and [TT]-pf[tt] are used for writing pull COM",
 +        "coordinates and forces when pulling is selected",
 +        "in the [TT].mdp[tt] file.[PAR]",
 +        "With [TT]-multi[tt] or [TT]-multidir[tt], multiple systems can be ",
 +        "simulated in parallel.",
 +        "As many input files/directories are required as the number of systems. ",
 +        "The [TT]-multidir[tt] option takes a list of directories (one for each ",
 +        "system) and runs in each of them, using the input/output file names, ",
 +        "such as specified by e.g. the [TT]-s[tt] option, relative to these ",
 +        "directories.",
 +        "With [TT]-multi[tt], the system number is appended to the run input ",
 +        "and each output filename, for instance [TT]topol.tpr[tt] becomes",
 +        "[TT]topol0.tpr[tt], [TT]topol1.tpr[tt] etc.",
 +        "The number of nodes per system is the total number of nodes",
 +        "divided by the number of systems.",
 +        "One use of this option is for NMR refinement: when distance",
 +        "or orientation restraints are present these can be ensemble averaged",
 +        "over all the systems.[PAR]",
 +        "With [TT]-replex[tt] replica exchange is attempted every given number",
 +        "of steps. The number of replicas is set with the [TT]-multi[tt] or ",
 +        "[TT]-multidir[tt] option, described above.",
 +        "All run input files should use a different coupling temperature,",
 +        "the order of the files is not important. The random seed is set with",
 +        "[TT]-reseed[tt]. The velocities are scaled and neighbor searching",
 +        "is performed after every exchange.[PAR]",
 +        "Finally some experimental algorithms can be tested when the",
 +        "appropriate options have been given. Currently under",
 +        "investigation are: polarizability.",
 +        "[PAR]",
 +        "The option [TT]-membed[tt] does what used to be g_membed, i.e. embed",
 +        "a protein into a membrane. The data file should contain the options",
 +        "that where passed to g_membed before. The [TT]-mn[tt] and [TT]-mp[tt]",
 +        "both apply to this as well.",
 +        "[PAR]",
 +        "The option [TT]-pforce[tt] is useful when you suspect a simulation",
 +        "crashes due to too large forces. With this option coordinates and",
 +        "forces of atoms with a force larger than a certain value will",
 +        "be printed to stderr.",
 +        "[PAR]",
 +        "Checkpoints containing the complete state of the system are written",
 +        "at regular intervals (option [TT]-cpt[tt]) to the file [TT]-cpo[tt],",
 +        "unless option [TT]-cpt[tt] is set to -1.",
 +        "The previous checkpoint is backed up to [TT]state_prev.cpt[tt] to",
 +        "make sure that a recent state of the system is always available,",
 +        "even when the simulation is terminated while writing a checkpoint.",
 +        "With [TT]-cpnum[tt] all checkpoint files are kept and appended",
 +        "with the step number.",
 +        "A simulation can be continued by reading the full state from file",
 +        "with option [TT]-cpi[tt]. This option is intelligent in the way that",
 +        "if no checkpoint file is found, Gromacs just assumes a normal run and",
 +        "starts from the first step of the [TT].tpr[tt] file. By default the output",
 +        "will be appending to the existing output files. The checkpoint file",
 +        "contains checksums of all output files, such that you will never",
 +        "loose data when some output files are modified, corrupt or removed.",
 +        "There are three scenarios with [TT]-cpi[tt]:[PAR]",
 +        "[TT]*[tt] no files with matching names are present: new output files are written[PAR]",
 +        "[TT]*[tt] all files are present with names and checksums matching those stored",
 +        "in the checkpoint file: files are appended[PAR]",
 +        "[TT]*[tt] otherwise no files are modified and a fatal error is generated[PAR]",
 +        "With [TT]-noappend[tt] new output files are opened and the simulation",
 +        "part number is added to all output file names.",
 +        "Note that in all cases the checkpoint file itself is not renamed",
 +        "and will be overwritten, unless its name does not match",
 +        "the [TT]-cpo[tt] option.",
 +        "[PAR]",
 +        "With checkpointing the output is appended to previously written",
 +        "output files, unless [TT]-noappend[tt] is used or none of the previous",
 +        "output files are present (except for the checkpoint file).",
 +        "The integrity of the files to be appended is verified using checksums",
 +        "which are stored in the checkpoint file. This ensures that output can",
 +        "not be mixed up or corrupted due to file appending. When only some",
 +        "of the previous output files are present, a fatal error is generated",
 +        "and no old output files are modified and no new output files are opened.",
 +        "The result with appending will be the same as from a single run.",
 +        "The contents will be binary identical, unless you use a different number",
 +        "of nodes or dynamic load balancing or the FFT library uses optimizations",
 +        "through timing.",
 +        "[PAR]",
 +        "With option [TT]-maxh[tt] a simulation is terminated and a checkpoint",
 +        "file is written at the first neighbor search step where the run time",
 +        "exceeds [TT]-maxh[tt]*0.99 hours.",
 +        "[PAR]",
 +        "When [TT]mdrun[tt] receives a TERM signal, it will set nsteps to the current",
 +        "step plus one. When [TT]mdrun[tt] receives an INT signal (e.g. when ctrl+C is",
 +        "pressed), it will stop after the next neighbor search step ",
 +        "(with nstlist=0 at the next step).",
 +        "In both cases all the usual output will be written to file.",
 +        "When running with MPI, a signal to one of the [TT]mdrun[tt] processes",
 +        "is sufficient, this signal should not be sent to mpirun or",
 +        "the [TT]mdrun[tt] process that is the parent of the others.",
 +        "[PAR]",
 +        "Interactive molecular dynamics (IMD) can be activated by using at least one",
 +        "of the three IMD switches: The [TT]-imdterm[tt] switch allows to terminate the",
 +        "simulation from the molecular viewer (e.g. VMD). With [TT]-imdwait[tt],",
 +        "[TT]mdrun[tt] pauses whenever no IMD client is connected. Pulling from the",
 +        "IMD remote can be turned on by [TT]-imdpull[tt].",
 +        "The port [TT]mdrun[tt] listens to can be altered by [TT]-imdport[tt].The",
 +        "file pointed to by [TT]-if[tt] contains atom indices and forces if IMD",
 +        "pulling is used."
 +        "[PAR]",
 +        "When [TT]mdrun[tt] is started with MPI, it does not run niced by default."
 +    };
 +    t_commrec    *cr;
 +    t_filenm      fnm[] = {
 +        { efTPX, NULL,      NULL,       ffREAD },
 +        { efTRN, "-o",      NULL,       ffWRITE },
 +        { efCOMPRESSED, "-x", NULL,     ffOPTWR },
 +        { efCPT, "-cpi",    NULL,       ffOPTRD },
 +        { efCPT, "-cpo",    NULL,       ffOPTWR },
 +        { efSTO, "-c",      "confout",  ffWRITE },
 +        { efEDR, "-e",      "ener",     ffWRITE },
 +        { efLOG, "-g",      "md",       ffWRITE },
 +        { efXVG, "-dhdl",   "dhdl",     ffOPTWR },
 +        { efXVG, "-field",  "field",    ffOPTWR },
 +        { efXVG, "-table",  "table",    ffOPTRD },
 +        { efXVG, "-tabletf", "tabletf",    ffOPTRD },
 +        { efXVG, "-tablep", "tablep",   ffOPTRD },
 +        { efXVG, "-tableb", "table",    ffOPTRD },
 +        { efTRX, "-rerun",  "rerun",    ffOPTRD },
 +        { efXVG, "-tpi",    "tpi",      ffOPTWR },
 +        { efXVG, "-tpid",   "tpidist",  ffOPTWR },
 +        { efEDI, "-ei",     "sam",      ffOPTRD },
 +        { efXVG, "-eo",     "edsam",    ffOPTWR },
 +        { efXVG, "-devout", "deviatie", ffOPTWR },
 +        { efXVG, "-runav",  "runaver",  ffOPTWR },
 +        { efXVG, "-px",     "pullx",    ffOPTWR },
 +        { efXVG, "-pf",     "pullf",    ffOPTWR },
 +        { efXVG, "-ro",     "rotation", ffOPTWR },
 +        { efLOG, "-ra",     "rotangles", ffOPTWR },
 +        { efLOG, "-rs",     "rotslabs", ffOPTWR },
 +        { efLOG, "-rt",     "rottorque", ffOPTWR },
 +        { efMTX, "-mtx",    "nm",       ffOPTWR },
 +        { efNDX, "-dn",     "dipole",   ffOPTWR },
 +        { efRND, "-multidir", NULL,      ffOPTRDMULT},
 +        { efDAT, "-membed", "membed",   ffOPTRD },
 +        { efTOP, "-mp",     "membed",   ffOPTRD },
 +        { efNDX, "-mn",     "membed",   ffOPTRD },
 +        { efXVG, "-if",     "imdforces", ffOPTWR },
 +        { efXVG, "-swap",   "swapions", ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +
 +    /* Command line options ! */
 +    gmx_bool        bDDBondCheck  = TRUE;
 +    gmx_bool        bDDBondComm   = TRUE;
 +    gmx_bool        bTunePME      = TRUE;
 +    gmx_bool        bTestVerlet   = FALSE;
 +    gmx_bool        bVerbose      = FALSE;
 +    gmx_bool        bCompact      = TRUE;
 +    gmx_bool        bSepPot       = FALSE;
 +    gmx_bool        bRerunVSite   = FALSE;
 +    gmx_bool        bConfout      = TRUE;
 +    gmx_bool        bReproducible = FALSE;
 +    gmx_bool        bIMDwait      = FALSE;
 +    gmx_bool        bIMDterm      = FALSE;
 +    gmx_bool        bIMDpull      = FALSE;
 +
 +    int             npme          = -1;
 +    int             nstlist       = 0;
 +    int             nmultisim     = 0;
 +    int             nstglobalcomm = -1;
 +    int             repl_ex_nst   = 0;
 +    int             repl_ex_seed  = -1;
 +    int             repl_ex_nex   = 0;
 +    int             nstepout      = 100;
 +    int             resetstep     = -1;
 +    gmx_int64_t     nsteps        = -2;   /* the value -2 means that the mdp option will be used */
 +    int             imdport       = 8888; /* can be almost anything, 8888 is easy to remember */
 +
 +    rvec            realddxyz          = {0, 0, 0};
 +    const char     *ddno_opt[ddnoNR+1] =
 +    { NULL, "interleave", "pp_pme", "cartesian", NULL };
 +    const char     *dddlb_opt[] =
 +    { NULL, "auto", "no", "yes", NULL };
 +    const char     *thread_aff_opt[threadaffNR+1] =
 +    { NULL, "auto", "on", "off", NULL };
 +    const char     *nbpu_opt[] =
 +    { NULL, "auto", "cpu", "gpu", "gpu_cpu", NULL };
 +    real            rdd                   = 0.0, rconstr = 0.0, dlb_scale = 0.8, pforce = -1;
 +    char           *ddcsx                 = NULL, *ddcsy = NULL, *ddcsz = NULL;
 +    real            cpt_period            = 15.0, max_hours = -1;
 +    gmx_bool        bAppendFiles          = TRUE;
 +    gmx_bool        bKeepAndNumCPT        = FALSE;
 +    gmx_bool        bResetCountersHalfWay = FALSE;
 +    output_env_t    oenv                  = NULL;
 +    const char     *deviceOptions         = "";
 +
 +    /* Non transparent initialization of a complex gmx_hw_opt_t struct.
 +     * But unfortunately we are not allowed to call a function here,
 +     * since declarations follow below.
 +     */
 +    gmx_hw_opt_t    hw_opt = {
 +        0, 0, 0, 0, threadaffSEL, 0, 0,
 +        { NULL, FALSE, 0, NULL }
 +    };
 +
 +    t_pargs         pa[] = {
 +
 +        { "-dd",      FALSE, etRVEC, {&realddxyz},
 +          "Domain decomposition grid, 0 is optimize" },
 +        { "-ddorder", FALSE, etENUM, {ddno_opt},
 +          "DD node order" },
 +        { "-npme",    FALSE, etINT, {&npme},
 +          "Number of separate nodes to be used for PME, -1 is guess" },
 +        { "-nt",      FALSE, etINT, {&hw_opt.nthreads_tot},
 +          "Total number of threads to start (0 is guess)" },
 +        { "-ntmpi",   FALSE, etINT, {&hw_opt.nthreads_tmpi},
 +          "Number of thread-MPI threads to start (0 is guess)" },
 +        { "-ntomp",   FALSE, etINT, {&hw_opt.nthreads_omp},
 +          "Number of OpenMP threads per MPI process/thread to start (0 is guess)" },
 +        { "-ntomp_pme", FALSE, etINT, {&hw_opt.nthreads_omp_pme},
 +          "Number of OpenMP threads per MPI process/thread to start (0 is -ntomp)" },
 +        { "-pin",     FALSE, etENUM, {thread_aff_opt},
 +          "Fix threads (or processes) to specific cores" },
 +        { "-pinoffset", FALSE, etINT, {&hw_opt.core_pinning_offset},
 +          "The starting logical core number for pinning to cores; used to avoid pinning threads from different mdrun instances to the same core" },
 +        { "-pinstride", FALSE, etINT, {&hw_opt.core_pinning_stride},
 +          "Pinning distance in logical cores for threads, use 0 to minimize the number of threads per physical core" },
 +        { "-gpu_id",  FALSE, etSTR, {&hw_opt.gpu_opt.gpu_id},
 +          "List of GPU device id-s to use, specifies the per-node PP rank to GPU mapping" },
 +        { "-ddcheck", FALSE, etBOOL, {&bDDBondCheck},
 +          "Check for all bonded interactions with DD" },
 +        { "-ddbondcomm", FALSE, etBOOL, {&bDDBondComm},
 +          "HIDDENUse special bonded atom communication when [TT]-rdd[tt] > cut-off" },
 +        { "-rdd",     FALSE, etREAL, {&rdd},
 +          "The maximum distance for bonded interactions with DD (nm), 0 is determine from initial coordinates" },
 +        { "-rcon",    FALSE, etREAL, {&rconstr},
 +          "Maximum distance for P-LINCS (nm), 0 is estimate" },
 +        { "-dlb",     FALSE, etENUM, {dddlb_opt},
 +          "Dynamic load balancing (with DD)" },
 +        { "-dds",     FALSE, etREAL, {&dlb_scale},
 +          "Fraction in (0,1) by whose reciprocal the initial DD cell size will be increased in order to "
 +          "provide a margin in which dynamic load balancing can act while preserving the minimum cell size." },
 +        { "-ddcsx",   FALSE, etSTR, {&ddcsx},
 +          "HIDDENA string containing a vector of the relative sizes in the x "
 +          "direction of the corresponding DD cells. Only effective with static "
 +          "load balancing." },
 +        { "-ddcsy",   FALSE, etSTR, {&ddcsy},
 +          "HIDDENA string containing a vector of the relative sizes in the y "
 +          "direction of the corresponding DD cells. Only effective with static "
 +          "load balancing." },
 +        { "-ddcsz",   FALSE, etSTR, {&ddcsz},
 +          "HIDDENA string containing a vector of the relative sizes in the z "
 +          "direction of the corresponding DD cells. Only effective with static "
 +          "load balancing." },
 +        { "-gcom",    FALSE, etINT, {&nstglobalcomm},
 +          "Global communication frequency" },
 +        { "-nb",      FALSE, etENUM, {&nbpu_opt},
 +          "Calculate non-bonded interactions on" },
 +        { "-nstlist", FALSE, etINT, {&nstlist},
 +          "Set nstlist when using a Verlet buffer tolerance (0 is guess)" },
 +        { "-tunepme", FALSE, etBOOL, {&bTunePME},
 +          "Optimize PME load between PP/PME nodes or GPU/CPU" },
 +        { "-testverlet", FALSE, etBOOL, {&bTestVerlet},
 +          "Test the Verlet non-bonded scheme" },
 +        { "-v",       FALSE, etBOOL, {&bVerbose},
 +          "Be loud and noisy" },
 +        { "-compact", FALSE, etBOOL, {&bCompact},
 +          "Write a compact log file" },
 +        { "-seppot",  FALSE, etBOOL, {&bSepPot},
 +          "Write separate V and dVdl terms for each interaction type and node to the log file(s)" },
 +        { "-pforce",  FALSE, etREAL, {&pforce},
 +          "Print all forces larger than this (kJ/mol nm)" },
 +        { "-reprod",  FALSE, etBOOL, {&bReproducible},
 +          "Try to avoid optimizations that affect binary reproducibility" },
 +        { "-cpt",     FALSE, etREAL, {&cpt_period},
 +          "Checkpoint interval (minutes)" },
 +        { "-cpnum",   FALSE, etBOOL, {&bKeepAndNumCPT},
 +          "Keep and number checkpoint files" },
 +        { "-append",  FALSE, etBOOL, {&bAppendFiles},
 +          "Append to previous output files when continuing from checkpoint instead of adding the simulation part number to all file names" },
 +        { "-nsteps",  FALSE, etINT64, {&nsteps},
 +          "Run this number of steps, overrides .mdp file option" },
 +        { "-maxh",   FALSE, etREAL, {&max_hours},
 +          "Terminate after 0.99 times this time (hours)" },
 +        { "-multi",   FALSE, etINT, {&nmultisim},
 +          "Do multiple simulations in parallel" },
 +        { "-replex",  FALSE, etINT, {&repl_ex_nst},
 +          "Attempt replica exchange periodically with this period (steps)" },
 +        { "-nex",  FALSE, etINT, {&repl_ex_nex},
 +          "Number of random exchanges to carry out each exchange interval (N^3 is one suggestion).  -nex zero or not specified gives neighbor replica exchange." },
 +        { "-reseed",  FALSE, etINT, {&repl_ex_seed},
 +          "Seed for replica exchange, -1 is generate a seed" },
 +        { "-imdport",    FALSE, etINT, {&imdport},
 +          "HIDDENIMD listening port" },
 +        { "-imdwait",  FALSE, etBOOL, {&bIMDwait},
 +          "HIDDENPause the simulation while no IMD client is connected" },
 +        { "-imdterm",  FALSE, etBOOL, {&bIMDterm},
 +          "HIDDENAllow termination of the simulation from IMD client" },
 +        { "-imdpull",  FALSE, etBOOL, {&bIMDpull},
 +          "HIDDENAllow pulling in the simulation from IMD client" },
 +        { "-rerunvsite", FALSE, etBOOL, {&bRerunVSite},
 +          "HIDDENRecalculate virtual site coordinates with [TT]-rerun[tt]" },
 +        { "-confout", FALSE, etBOOL, {&bConfout},
 +          "HIDDENWrite the last configuration with [TT]-c[tt] and force checkpointing at the last step" },
 +        { "-stepout", FALSE, etINT, {&nstepout},
 +          "HIDDENFrequency of writing the remaining wall clock time for the run" },
 +        { "-resetstep", FALSE, etINT, {&resetstep},
 +          "HIDDENReset cycle counters after these many time steps" },
 +        { "-resethway", FALSE, etBOOL, {&bResetCountersHalfWay},
 +          "HIDDENReset the cycle counters after half the number of steps or halfway [TT]-maxh[tt]" }
 +    };
 +    unsigned long   Flags, PCA_Flags;
 +    ivec            ddxyz;
 +    int             dd_node_order;
 +    gmx_bool        bAddPart;
 +    FILE           *fplog, *fpmulti;
 +    int             sim_part, sim_part_fn;
 +    const char     *part_suffix = ".part";
 +    char            suffix[STRLEN];
 +    int             rc;
 +    char          **multidir = NULL;
 +
 +
 +    cr = init_commrec();
 +
 +    PCA_Flags = (PCA_CAN_SET_DEFFNM | (MASTER(cr) ? 0 : PCA_QUIET));
 +
 +    /* Comment this in to do fexist calls only on master
 +     * works not with rerun or tables at the moment
 +     * also comment out the version of init_forcerec in md.c
 +     * with NULL instead of opt2fn
 +     */
 +    /*
 +       if (!MASTER(cr))
 +       {
 +       PCA_Flags |= PCA_NOT_READ_NODE;
 +       }
 +     */
 +
 +    if (!parse_common_args(&argc, argv, PCA_Flags, NFILE, fnm, asize(pa), pa,
 +                           asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +
 +    /* we set these early because they might be used in init_multisystem()
 +       Note that there is the potential for npme>nnodes until the number of
 +       threads is set later on, if there's thread parallelization. That shouldn't
 +       lead to problems. */
 +    dd_node_order = nenum(ddno_opt);
 +    cr->npmenodes = npme;
 +
 +    hw_opt.thread_affinity = nenum(thread_aff_opt);
 +
 +    /* now check the -multi and -multidir option */
 +    if (opt2bSet("-multidir", NFILE, fnm))
 +    {
 +        if (nmultisim > 0)
 +        {
 +            gmx_fatal(FARGS, "mdrun -multi and -multidir options are mutually exclusive.");
 +        }
 +        nmultisim = opt2fns(&multidir, "-multidir", NFILE, fnm);
 +    }
 +
 +
 +    if (repl_ex_nst != 0 && nmultisim < 2)
 +    {
 +        gmx_fatal(FARGS, "Need at least two replicas for replica exchange (option -multi)");
 +    }
 +
 +    if (repl_ex_nex < 0)
 +    {
 +        gmx_fatal(FARGS, "Replica exchange number of exchanges needs to be positive");
 +    }
 +
 +    if (nmultisim > 1)
 +    {
 +#ifndef GMX_THREAD_MPI
 +        gmx_bool bParFn = (multidir == NULL);
 +        init_multisystem(cr, nmultisim, multidir, NFILE, fnm, bParFn);
 +#else
 +        gmx_fatal(FARGS, "mdrun -multi is not supported with the thread library. "
 +                  "Please compile GROMACS with MPI support");
 +#endif
 +    }
 +
 +    bAddPart = !bAppendFiles;
 +
 +    /* Check if there is ANY checkpoint file available */
 +    sim_part    = 1;
 +    sim_part_fn = sim_part;
 +    if (opt2bSet("-cpi", NFILE, fnm))
 +    {
 +        if (bSepPot && bAppendFiles)
 +        {
 +            gmx_fatal(FARGS, "Output file appending is not supported with -seppot");
 +        }
 +
 +        bAppendFiles =
 +            read_checkpoint_simulation_part(opt2fn_master("-cpi", NFILE,
 +                                                          fnm, cr),
 +                                            &sim_part_fn, NULL, cr,
 +                                            bAppendFiles, NFILE, fnm,
 +                                            part_suffix, &bAddPart);
 +        if (sim_part_fn == 0 && MULTIMASTER(cr))
 +        {
 +            fprintf(stdout, "No previous checkpoint file present, assuming this is a new run.\n");
 +        }
 +        else
 +        {
 +            sim_part = sim_part_fn + 1;
 +        }
 +
 +        if (MULTISIM(cr) && MASTER(cr))
 +        {
 +            if (MULTIMASTER(cr))
 +            {
 +                /* Log file is not yet available, so if there's a
 +                 * problem we can only write to stderr. */
 +                fpmulti = stderr;
 +            }
 +            else
 +            {
 +                fpmulti = NULL;
 +            }
 +            check_multi_int(fpmulti, cr->ms, sim_part, "simulation part", TRUE);
 +        }
 +    }
 +    else
 +    {
 +        bAppendFiles = FALSE;
 +    }
 +
 +    if (!bAppendFiles)
 +    {
 +        sim_part_fn = sim_part;
 +    }
 +
 +    if (bAddPart)
 +    {
 +        /* Rename all output files (except checkpoint files) */
 +        /* create new part name first (zero-filled) */
 +        sprintf(suffix, "%s%04d", part_suffix, sim_part_fn);
 +
 +        add_suffix_to_output_names(fnm, NFILE, suffix);
 +        if (MULTIMASTER(cr))
 +        {
 +            fprintf(stdout, "Checkpoint file is from part %d, new output files will be suffixed '%s'.\n", sim_part-1, suffix);
 +        }
 +    }
 +
 +    Flags = opt2bSet("-rerun", NFILE, fnm) ? MD_RERUN : 0;
 +    Flags = Flags | (bSepPot       ? MD_SEPPOT       : 0);
 +    Flags = Flags | (bDDBondCheck  ? MD_DDBONDCHECK  : 0);
 +    Flags = Flags | (bDDBondComm   ? MD_DDBONDCOMM   : 0);
 +    Flags = Flags | (bTunePME      ? MD_TUNEPME      : 0);
 +    Flags = Flags | (bTestVerlet   ? MD_TESTVERLET   : 0);
 +    Flags = Flags | (bConfout      ? MD_CONFOUT      : 0);
 +    Flags = Flags | (bRerunVSite   ? MD_RERUN_VSITE  : 0);
 +    Flags = Flags | (bReproducible ? MD_REPRODUCIBLE : 0);
 +    Flags = Flags | (bAppendFiles  ? MD_APPENDFILES  : 0);
 +    Flags = Flags | (opt2parg_bSet("-append", asize(pa), pa) ? MD_APPENDFILESSET : 0);
 +    Flags = Flags | (bKeepAndNumCPT ? MD_KEEPANDNUMCPT : 0);
 +    Flags = Flags | (sim_part > 1    ? MD_STARTFROMCPT : 0);
 +    Flags = Flags | (bResetCountersHalfWay ? MD_RESETCOUNTERSHALFWAY : 0);
 +    Flags = Flags | (bIMDwait      ? MD_IMDWAIT      : 0);
 +    Flags = Flags | (bIMDterm      ? MD_IMDTERM      : 0);
 +    Flags = Flags | (bIMDpull      ? MD_IMDPULL      : 0);
 +
 +    /* We postpone opening the log file if we are appending, so we can
 +       first truncate the old log file and append to the correct position
 +       there instead.  */
 +    if ((MASTER(cr) || bSepPot) && !bAppendFiles)
 +    {
 +        gmx_log_open(ftp2fn(efLOG, NFILE, fnm), cr,
 +                     !bSepPot, Flags & MD_APPENDFILES, &fplog);
 +        please_cite(fplog, "Hess2008b");
 +        please_cite(fplog, "Spoel2005a");
 +        please_cite(fplog, "Lindahl2001a");
 +        please_cite(fplog, "Berendsen95a");
 +    }
 +    else if (!MASTER(cr) && bSepPot)
 +    {
 +        gmx_log_open(ftp2fn(efLOG, NFILE, fnm), cr, !bSepPot, Flags, &fplog);
 +    }
 +    else
 +    {
 +        fplog = NULL;
 +    }
 +
 +    ddxyz[XX] = (int)(realddxyz[XX] + 0.5);
 +    ddxyz[YY] = (int)(realddxyz[YY] + 0.5);
 +    ddxyz[ZZ] = (int)(realddxyz[ZZ] + 0.5);
 +
 +    rc = mdrunner(&hw_opt, fplog, cr, NFILE, fnm, oenv, bVerbose, bCompact,
 +                  nstglobalcomm, ddxyz, dd_node_order, rdd, rconstr,
 +                  dddlb_opt[0], dlb_scale, ddcsx, ddcsy, ddcsz,
 +                  nbpu_opt[0], nstlist,
 +                  nsteps, nstepout, resetstep,
 +                  nmultisim, repl_ex_nst, repl_ex_nex, repl_ex_seed,
 +                  pforce, cpt_period, max_hours, deviceOptions, imdport, Flags);
 +
 +    /* Log file has to be closed in mdrunner if we are appending to it
 +       (fplog not set here) */
 +    if (MASTER(cr) && !bAppendFiles)
 +    {
 +        gmx_log_close(fplog);
 +    }
 +
 +    return rc;
 +}
index d52bf5b95f9b250afb598d4f027ca04d3d6a275b,0000000000000000000000000000000000000000..46a9bc0113cd1cd6755cca7903cc71128c2386f4
mode 100644,000000..100644
--- /dev/null
@@@ -1,1434 -1,0 +1,1439 @@@
-             fprintf(fplog, "  dpV = %10.3e  d = %10.3e\nb", dpV, delta + dpV);
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2011,2012,2013,2014, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include "repl_ex.h"
 +#include "network.h"
 +#include "gromacs/random/random.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "physics.h"
 +#include "copyrite.h"
 +#include "macros.h"
 +#include "vec.h"
 +#include "names.h"
 +#include "domdec.h"
 +#include "gromacs/random/random.h"
 +
 +#define PROBABILITYCUTOFF 100
 +/* we don't bother evaluating if events are more rare than exp(-100) = 3.7x10^-44 */
 +
 +enum {
 +    ereTEMP, ereLAMBDA, ereENDSINGLE, ereTL, ereNR
 +};
 +const char *erename[ereNR] = { "temperature", "lambda", "end_single_marker", "temperature and lambda"};
 +/* end_single_marker merely notes the end of single variable replica exchange. All types higher than
 +   it are multiple replica exchange methods */
 +/* Eventually, should add 'pressure', 'temperature and pressure', 'lambda_and_pressure', 'temperature_lambda_pressure'?;
 +   Let's wait until we feel better about the pressure control methods giving exact ensembles.  Right now, we assume constant pressure  */
 +
 +typedef struct gmx_repl_ex
 +{
 +    int       repl;
 +    int       nrepl;
 +    real      temp;
 +    int       type;
 +    real    **q;
 +    gmx_bool  bNPT;
 +    real     *pres;
 +    int      *ind;
 +    int      *allswaps;
 +    int       nst;
 +    int       nex;
 +    int       seed;
 +    int       nattempt[2];
 +    real     *prob_sum;
 +    int     **nmoves;
 +    int      *nexchange;
 +    gmx_rng_t rng;
 +
 +    /* these are helper arrays for replica exchange; allocated here so they
 +       don't have to be allocated each time */
 +    int      *destinations;
 +    int     **cyclic;
 +    int     **order;
 +    int      *tmpswap;
 +    gmx_bool *incycle;
 +    gmx_bool *bEx;
 +
 +    /* helper arrays to hold the quantities that are exchanged */
 +    real  *prob;
 +    real  *Epot;
 +    real  *beta;
 +    real  *Vol;
 +    real **de;
 +
 +} t_gmx_repl_ex;
 +
 +static gmx_bool repl_quantity(const gmx_multisim_t *ms,
 +                              struct gmx_repl_ex *re, int ere, real q)
 +{
 +    real    *qall;
 +    gmx_bool bDiff;
 +    int      i, s;
 +
 +    snew(qall, ms->nsim);
 +    qall[re->repl] = q;
 +    gmx_sum_sim(ms->nsim, qall, ms);
 +
 +    bDiff = FALSE;
 +    for (s = 1; s < ms->nsim; s++)
 +    {
 +        if (qall[s] != qall[0])
 +        {
 +            bDiff = TRUE;
 +        }
 +    }
 +
 +    if (bDiff)
 +    {
 +        /* Set the replica exchange type and quantities */
 +        re->type = ere;
 +
 +        snew(re->q[ere], re->nrepl);
 +        for (s = 0; s < ms->nsim; s++)
 +        {
 +            re->q[ere][s] = qall[s];
 +        }
 +    }
 +    sfree(qall);
 +    return bDiff;
 +}
 +
 +gmx_repl_ex_t init_replica_exchange(FILE *fplog,
 +                                    const gmx_multisim_t *ms,
 +                                    const t_state *state,
 +                                    const t_inputrec *ir,
 +                                    int nst, int nex, int init_seed)
 +{
 +    real                temp, pres;
 +    int                 i, j, k;
 +    struct gmx_repl_ex *re;
 +    gmx_bool            bTemp;
 +    gmx_bool            bLambda = FALSE;
 +
 +    fprintf(fplog, "\nInitializing Replica Exchange\n");
 +
 +    if (ms == NULL || ms->nsim == 1)
 +    {
 +        gmx_fatal(FARGS, "Nothing to exchange with only one replica, maybe you forgot to set the -multi option of mdrun?");
 +    }
 +    if (!EI_DYNAMICS(ir->eI))
 +    {
 +        gmx_fatal(FARGS, "Replica exchange is only supported by dynamical simulations");
 +        /* Note that PAR(cr) is defined by cr->nnodes > 1, which is
 +         * distinct from MULTISIM(cr). A multi-simulation only runs
 +         * with real MPI parallelism, but this does not imply PAR(cr)
 +         * is true!
 +         *
 +         * Since we are using a dynamical integrator, the only
 +         * decomposition is DD, so PAR(cr) and DOMAINDECOMP(cr) are
 +         * synonymous. The only way for cr->nnodes > 1 to be true is
 +         * if we are using DD. */
 +    }
 +
 +    snew(re, 1);
 +
 +    re->repl     = ms->sim;
 +    re->nrepl    = ms->nsim;
 +    snew(re->q, ereENDSINGLE);
 +
 +    fprintf(fplog, "Repl  There are %d replicas:\n", re->nrepl);
 +
 +    check_multi_int(fplog, ms, state->natoms, "the number of atoms", FALSE);
 +    check_multi_int(fplog, ms, ir->eI, "the integrator", FALSE);
 +    check_multi_int64(fplog, ms, ir->init_step+ir->nsteps, "init_step+nsteps", FALSE);
 +    check_multi_int64(fplog, ms, (ir->init_step+nst-1)/nst,
 +                      "first exchange step: init_step/-replex", FALSE);
 +    check_multi_int(fplog, ms, ir->etc, "the temperature coupling", FALSE);
 +    check_multi_int(fplog, ms, ir->opts.ngtc,
 +                    "the number of temperature coupling groups", FALSE);
 +    check_multi_int(fplog, ms, ir->epc, "the pressure coupling", FALSE);
 +    check_multi_int(fplog, ms, ir->efep, "free energy", FALSE);
 +    check_multi_int(fplog, ms, ir->fepvals->n_lambda, "number of lambda states", FALSE);
 +
 +    re->temp = ir->opts.ref_t[0];
 +    for (i = 1; (i < ir->opts.ngtc); i++)
 +    {
 +        if (ir->opts.ref_t[i] != re->temp)
 +        {
 +            fprintf(fplog, "\nWARNING: The temperatures of the different temperature coupling groups are not identical\n\n");
 +            fprintf(stderr, "\nWARNING: The temperatures of the different temperature coupling groups are not identical\n\n");
 +        }
 +    }
 +
 +    re->type = -1;
 +    bTemp    = repl_quantity(ms, re, ereTEMP, re->temp);
 +    if (ir->efep != efepNO)
 +    {
 +        bLambda = repl_quantity(ms, re, ereLAMBDA, (real)ir->fepvals->init_fep_state);
 +    }
 +    if (re->type == -1)  /* nothing was assigned */
 +    {
 +        gmx_fatal(FARGS, "The properties of the %d systems are all the same, there is nothing to exchange", re->nrepl);
 +    }
 +    if (bLambda && bTemp)
 +    {
 +        re->type = ereTL;
 +    }
 +
 +    if (bTemp)
 +    {
 +        please_cite(fplog, "Sugita1999a");
 +        if (ir->epc != epcNO)
 +        {
 +            re->bNPT = TRUE;
 +            fprintf(fplog, "Repl  Using Constant Pressure REMD.\n");
 +            please_cite(fplog, "Okabe2001a");
 +        }
 +        if (ir->etc == etcBERENDSEN)
 +        {
 +            gmx_fatal(FARGS, "REMD with the %s thermostat does not produce correct potential energy distributions, consider using the %s thermostat instead",
 +                      ETCOUPLTYPE(ir->etc), ETCOUPLTYPE(etcVRESCALE));
 +        }
 +    }
 +    if (bLambda)
 +    {
 +        if (ir->fepvals->delta_lambda != 0)   /* check this? */
 +        {
 +            gmx_fatal(FARGS, "delta_lambda is not zero");
 +        }
 +    }
 +    if (re->bNPT)
 +    {
 +        snew(re->pres, re->nrepl);
 +        if (ir->epct == epctSURFACETENSION)
 +        {
 +            pres = ir->ref_p[ZZ][ZZ];
 +        }
 +        else
 +        {
 +            pres = 0;
 +            j    = 0;
 +            for (i = 0; i < DIM; i++)
 +            {
 +                if (ir->compress[i][i] != 0)
 +                {
 +                    pres += ir->ref_p[i][i];
 +                    j++;
 +                }
 +            }
 +            pres /= j;
 +        }
 +        re->pres[re->repl] = pres;
 +        gmx_sum_sim(re->nrepl, re->pres, ms);
 +    }
 +
 +    /* Make an index for increasing replica order */
 +    /* only makes sense if one or the other is varying, not both!
 +       if both are varying, we trust the order the person gave. */
 +    snew(re->ind, re->nrepl);
 +    for (i = 0; i < re->nrepl; i++)
 +    {
 +        re->ind[i] = i;
 +    }
 +
 +    if (re->type < ereENDSINGLE)
 +    {
 +
 +        for (i = 0; i < re->nrepl; i++)
 +        {
 +            for (j = i+1; j < re->nrepl; j++)
 +            {
 +                if (re->q[re->type][re->ind[j]] < re->q[re->type][re->ind[i]])
 +                {
++                    /* Unordered replicas are supposed to work, but there
++                     * is still an issues somewhere.
++                     * Note that at this point still re->ind[i]=i.
++                     */
++                    gmx_fatal(FARGS, "Replicas with indices %d < %d have %ss %g > %g, please order your replicas on increasing %s",
++                              i, j,
++                              erename[re->type],
++                              re->q[re->type][i], re->q[re->type][j],
++                              erename[re->type]);
++
 +                    k          = re->ind[i];
 +                    re->ind[i] = re->ind[j];
 +                    re->ind[j] = k;
 +                }
 +                else if (re->q[re->type][re->ind[j]] == re->q[re->type][re->ind[i]])
 +                {
 +                    gmx_fatal(FARGS, "Two replicas have identical %ss", erename[re->type]);
 +                }
 +            }
 +        }
 +    }
 +
 +    /* keep track of all the swaps, starting with the initial placement. */
 +    snew(re->allswaps, re->nrepl);
 +    for (i = 0; i < re->nrepl; i++)
 +    {
 +        re->allswaps[i] = re->ind[i];
 +    }
 +
 +    switch (re->type)
 +    {
 +        case ereTEMP:
 +            fprintf(fplog, "\nReplica exchange in temperature\n");
 +            for (i = 0; i < re->nrepl; i++)
 +            {
 +                fprintf(fplog, " %5.1f", re->q[re->type][re->ind[i]]);
 +            }
 +            fprintf(fplog, "\n");
 +            break;
 +        case ereLAMBDA:
 +            fprintf(fplog, "\nReplica exchange in lambda\n");
 +            for (i = 0; i < re->nrepl; i++)
 +            {
 +                fprintf(fplog, " %3d", (int)re->q[re->type][re->ind[i]]);
 +            }
 +            fprintf(fplog, "\n");
 +            break;
 +        case ereTL:
 +            fprintf(fplog, "\nReplica exchange in temperature and lambda state\n");
 +            for (i = 0; i < re->nrepl; i++)
 +            {
 +                fprintf(fplog, " %5.1f", re->q[ereTEMP][re->ind[i]]);
 +            }
 +            fprintf(fplog, "\n");
 +            for (i = 0; i < re->nrepl; i++)
 +            {
 +                fprintf(fplog, " %5d", (int)re->q[ereLAMBDA][re->ind[i]]);
 +            }
 +            fprintf(fplog, "\n");
 +            break;
 +        default:
 +            gmx_incons("Unknown replica exchange quantity");
 +    }
 +    if (re->bNPT)
 +    {
 +        fprintf(fplog, "\nRepl  p");
 +        for (i = 0; i < re->nrepl; i++)
 +        {
 +            fprintf(fplog, " %5.2f", re->pres[re->ind[i]]);
 +        }
 +
 +        for (i = 0; i < re->nrepl; i++)
 +        {
 +            if ((i > 0) && (re->pres[re->ind[i]] < re->pres[re->ind[i-1]]))
 +            {
 +                fprintf(fplog, "\nWARNING: The reference pressures decrease with increasing temperatures\n\n");
 +                fprintf(stderr, "\nWARNING: The reference pressures decrease with increasing temperatures\n\n");
 +            }
 +        }
 +    }
 +    re->nst = nst;
 +    if (init_seed == -1)
 +    {
 +        if (MASTERSIM(ms))
 +        {
 +            re->seed = (int)gmx_rng_make_seed();
 +        }
 +        else
 +        {
 +            re->seed = 0;
 +        }
 +        gmx_sumi_sim(1, &(re->seed), ms);
 +    }
 +    else
 +    {
 +        re->seed = init_seed;
 +    }
 +    fprintf(fplog, "\nReplica exchange interval: %d\n", re->nst);
 +    fprintf(fplog, "\nReplica random seed: %d\n", re->seed);
 +    re->rng = gmx_rng_init(re->seed);
 +
 +    re->nattempt[0] = 0;
 +    re->nattempt[1] = 0;
 +
 +    snew(re->prob_sum, re->nrepl);
 +    snew(re->nexchange, re->nrepl);
 +    snew(re->nmoves, re->nrepl);
 +    for (i = 0; i < re->nrepl; i++)
 +    {
 +        snew(re->nmoves[i], re->nrepl);
 +    }
 +    fprintf(fplog, "Replica exchange information below: x=exchange, pr=probability\n");
 +
 +    /* generate space for the helper functions so we don't have to snew each time */
 +
 +    snew(re->destinations, re->nrepl);
 +    snew(re->incycle, re->nrepl);
 +    snew(re->tmpswap, re->nrepl);
 +    snew(re->cyclic, re->nrepl);
 +    snew(re->order, re->nrepl);
 +    for (i = 0; i < re->nrepl; i++)
 +    {
 +        snew(re->cyclic[i], re->nrepl);
 +        snew(re->order[i], re->nrepl);
 +    }
 +    /* allocate space for the functions storing the data for the replicas */
 +    /* not all of these arrays needed in all cases, but they don't take
 +       up much space, since the max size is nrepl**2 */
 +    snew(re->prob, re->nrepl);
 +    snew(re->bEx, re->nrepl);
 +    snew(re->beta, re->nrepl);
 +    snew(re->Vol, re->nrepl);
 +    snew(re->Epot, re->nrepl);
 +    snew(re->de, re->nrepl);
 +    for (i = 0; i < re->nrepl; i++)
 +    {
 +        snew(re->de[i], re->nrepl);
 +    }
 +    re->nex = nex;
 +    return re;
 +}
 +
 +static void exchange_reals(const gmx_multisim_t gmx_unused *ms, int gmx_unused b, real *v, int n)
 +{
 +    real *buf;
 +    int   i;
 +
 +    if (v)
 +    {
 +        snew(buf, n);
 +#ifdef GMX_MPI
 +        /*
 +           MPI_Sendrecv(v,  n*sizeof(real),MPI_BYTE,MSRANK(ms,b),0,
 +           buf,n*sizeof(real),MPI_BYTE,MSRANK(ms,b),0,
 +           ms->mpi_comm_masters,MPI_STATUS_IGNORE);
 +         */
 +        {
 +            MPI_Request mpi_req;
 +
 +            MPI_Isend(v, n*sizeof(real), MPI_BYTE, MSRANK(ms, b), 0,
 +                      ms->mpi_comm_masters, &mpi_req);
 +            MPI_Recv(buf, n*sizeof(real), MPI_BYTE, MSRANK(ms, b), 0,
 +                     ms->mpi_comm_masters, MPI_STATUS_IGNORE);
 +            MPI_Wait(&mpi_req, MPI_STATUS_IGNORE);
 +        }
 +#endif
 +        for (i = 0; i < n; i++)
 +        {
 +            v[i] = buf[i];
 +        }
 +        sfree(buf);
 +    }
 +}
 +
 +
 +static void exchange_ints(const gmx_multisim_t gmx_unused *ms, int gmx_unused b, int *v, int n)
 +{
 +    int *buf;
 +    int  i;
 +
 +    if (v)
 +    {
 +        snew(buf, n);
 +#ifdef GMX_MPI
 +        /*
 +           MPI_Sendrecv(v,  n*sizeof(int),MPI_BYTE,MSRANK(ms,b),0,
 +             buf,n*sizeof(int),MPI_BYTE,MSRANK(ms,b),0,
 +             ms->mpi_comm_masters,MPI_STATUS_IGNORE);
 +         */
 +        {
 +            MPI_Request mpi_req;
 +
 +            MPI_Isend(v, n*sizeof(int), MPI_BYTE, MSRANK(ms, b), 0,
 +                      ms->mpi_comm_masters, &mpi_req);
 +            MPI_Recv(buf, n*sizeof(int), MPI_BYTE, MSRANK(ms, b), 0,
 +                     ms->mpi_comm_masters, MPI_STATUS_IGNORE);
 +            MPI_Wait(&mpi_req, MPI_STATUS_IGNORE);
 +        }
 +#endif
 +        for (i = 0; i < n; i++)
 +        {
 +            v[i] = buf[i];
 +        }
 +        sfree(buf);
 +    }
 +}
 +
 +static void exchange_doubles(const gmx_multisim_t gmx_unused *ms, int gmx_unused b, double *v, int n)
 +{
 +    double *buf;
 +    int     i;
 +
 +    if (v)
 +    {
 +        snew(buf, n);
 +#ifdef GMX_MPI
 +        /*
 +           MPI_Sendrecv(v,  n*sizeof(double),MPI_BYTE,MSRANK(ms,b),0,
 +           buf,n*sizeof(double),MPI_BYTE,MSRANK(ms,b),0,
 +           ms->mpi_comm_masters,MPI_STATUS_IGNORE);
 +         */
 +        {
 +            MPI_Request mpi_req;
 +
 +            MPI_Isend(v, n*sizeof(double), MPI_BYTE, MSRANK(ms, b), 0,
 +                      ms->mpi_comm_masters, &mpi_req);
 +            MPI_Recv(buf, n*sizeof(double), MPI_BYTE, MSRANK(ms, b), 0,
 +                     ms->mpi_comm_masters, MPI_STATUS_IGNORE);
 +            MPI_Wait(&mpi_req, MPI_STATUS_IGNORE);
 +        }
 +#endif
 +        for (i = 0; i < n; i++)
 +        {
 +            v[i] = buf[i];
 +        }
 +        sfree(buf);
 +    }
 +}
 +
 +static void exchange_rvecs(const gmx_multisim_t gmx_unused *ms, int gmx_unused b, rvec *v, int n)
 +{
 +    rvec *buf;
 +    int   i;
 +
 +    if (v)
 +    {
 +        snew(buf, n);
 +#ifdef GMX_MPI
 +        /*
 +           MPI_Sendrecv(v[0],  n*sizeof(rvec),MPI_BYTE,MSRANK(ms,b),0,
 +           buf[0],n*sizeof(rvec),MPI_BYTE,MSRANK(ms,b),0,
 +           ms->mpi_comm_masters,MPI_STATUS_IGNORE);
 +         */
 +        {
 +            MPI_Request mpi_req;
 +
 +            MPI_Isend(v[0], n*sizeof(rvec), MPI_BYTE, MSRANK(ms, b), 0,
 +                      ms->mpi_comm_masters, &mpi_req);
 +            MPI_Recv(buf[0], n*sizeof(rvec), MPI_BYTE, MSRANK(ms, b), 0,
 +                     ms->mpi_comm_masters, MPI_STATUS_IGNORE);
 +            MPI_Wait(&mpi_req, MPI_STATUS_IGNORE);
 +        }
 +#endif
 +        for (i = 0; i < n; i++)
 +        {
 +            copy_rvec(buf[i], v[i]);
 +        }
 +        sfree(buf);
 +    }
 +}
 +
 +static void exchange_state(const gmx_multisim_t *ms, int b, t_state *state)
 +{
 +    /* When t_state changes, this code should be updated. */
 +    int ngtc, nnhpres;
 +    ngtc    = state->ngtc * state->nhchainlength;
 +    nnhpres = state->nnhpres* state->nhchainlength;
 +    exchange_rvecs(ms, b, state->box, DIM);
 +    exchange_rvecs(ms, b, state->box_rel, DIM);
 +    exchange_rvecs(ms, b, state->boxv, DIM);
 +    exchange_reals(ms, b, &(state->veta), 1);
 +    exchange_reals(ms, b, &(state->vol0), 1);
 +    exchange_rvecs(ms, b, state->svir_prev, DIM);
 +    exchange_rvecs(ms, b, state->fvir_prev, DIM);
 +    exchange_rvecs(ms, b, state->pres_prev, DIM);
 +    exchange_doubles(ms, b, state->nosehoover_xi, ngtc);
 +    exchange_doubles(ms, b, state->nosehoover_vxi, ngtc);
 +    exchange_doubles(ms, b, state->nhpres_xi, nnhpres);
 +    exchange_doubles(ms, b, state->nhpres_vxi, nnhpres);
 +    exchange_doubles(ms, b, state->therm_integral, state->ngtc);
 +    exchange_rvecs(ms, b, state->x, state->natoms);
 +    exchange_rvecs(ms, b, state->v, state->natoms);
 +    exchange_rvecs(ms, b, state->sd_X, state->natoms);
 +}
 +
 +static void copy_rvecs(rvec *s, rvec *d, int n)
 +{
 +    int i;
 +
 +    if (d != NULL)
 +    {
 +        for (i = 0; i < n; i++)
 +        {
 +            copy_rvec(s[i], d[i]);
 +        }
 +    }
 +}
 +
 +static void copy_doubles(const double *s, double *d, int n)
 +{
 +    int i;
 +
 +    if (d != NULL)
 +    {
 +        for (i = 0; i < n; i++)
 +        {
 +            d[i] = s[i];
 +        }
 +    }
 +}
 +
 +static void copy_reals(const real *s, real *d, int n)
 +{
 +    int i;
 +
 +    if (d != NULL)
 +    {
 +        for (i = 0; i < n; i++)
 +        {
 +            d[i] = s[i];
 +        }
 +    }
 +}
 +
 +static void copy_ints(const int *s, int *d, int n)
 +{
 +    int i;
 +
 +    if (d != NULL)
 +    {
 +        for (i = 0; i < n; i++)
 +        {
 +            d[i] = s[i];
 +        }
 +    }
 +}
 +
 +#define scopy_rvecs(v, n)   copy_rvecs(state->v, state_local->v, n);
 +#define scopy_doubles(v, n) copy_doubles(state->v, state_local->v, n);
 +#define scopy_reals(v, n) copy_reals(state->v, state_local->v, n);
 +#define scopy_ints(v, n)   copy_ints(state->v, state_local->v, n);
 +
 +static void copy_state_nonatomdata(t_state *state, t_state *state_local)
 +{
 +    /* When t_state changes, this code should be updated. */
 +    int ngtc, nnhpres;
 +    ngtc    = state->ngtc * state->nhchainlength;
 +    nnhpres = state->nnhpres* state->nhchainlength;
 +    scopy_rvecs(box, DIM);
 +    scopy_rvecs(box_rel, DIM);
 +    scopy_rvecs(boxv, DIM);
 +    state_local->veta = state->veta;
 +    state_local->vol0 = state->vol0;
 +    scopy_rvecs(svir_prev, DIM);
 +    scopy_rvecs(fvir_prev, DIM);
 +    scopy_rvecs(pres_prev, DIM);
 +    scopy_doubles(nosehoover_xi, ngtc);
 +    scopy_doubles(nosehoover_vxi, ngtc);
 +    scopy_doubles(nhpres_xi, nnhpres);
 +    scopy_doubles(nhpres_vxi, nnhpres);
 +    scopy_doubles(therm_integral, state->ngtc);
 +    scopy_rvecs(x, state->natoms);
 +    scopy_rvecs(v, state->natoms);
 +    scopy_rvecs(sd_X, state->natoms);
 +    copy_ints(&(state->fep_state), &(state_local->fep_state), 1);
 +    scopy_reals(lambda, efptNR);
 +}
 +
 +static void scale_velocities(t_state *state, real fac)
 +{
 +    int i;
 +
 +    if (state->v)
 +    {
 +        for (i = 0; i < state->natoms; i++)
 +        {
 +            svmul(fac, state->v[i], state->v[i]);
 +        }
 +    }
 +}
 +
 +static void print_transition_matrix(FILE *fplog, int n, int **nmoves, int *nattempt)
 +{
 +    int   i, j, ntot;
 +    float Tprint;
 +
 +    ntot = nattempt[0] + nattempt[1];
 +    fprintf(fplog, "\n");
 +    fprintf(fplog, "Repl");
 +    for (i = 0; i < n; i++)
 +    {
 +        fprintf(fplog, "    ");  /* put the title closer to the center */
 +    }
 +    fprintf(fplog, "Empirical Transition Matrix\n");
 +
 +    fprintf(fplog, "Repl");
 +    for (i = 0; i < n; i++)
 +    {
 +        fprintf(fplog, "%8d", (i+1));
 +    }
 +    fprintf(fplog, "\n");
 +
 +    for (i = 0; i < n; i++)
 +    {
 +        fprintf(fplog, "Repl");
 +        for (j = 0; j < n; j++)
 +        {
 +            Tprint = 0.0;
 +            if (nmoves[i][j] > 0)
 +            {
 +                Tprint = nmoves[i][j]/(2.0*ntot);
 +            }
 +            fprintf(fplog, "%8.4f", Tprint);
 +        }
 +        fprintf(fplog, "%3d\n", i);
 +    }
 +}
 +
 +static void print_ind(FILE *fplog, const char *leg, int n, int *ind, gmx_bool *bEx)
 +{
 +    int i;
 +
 +    fprintf(fplog, "Repl %2s %2d", leg, ind[0]);
 +    for (i = 1; i < n; i++)
 +    {
 +        fprintf(fplog, " %c %2d", (bEx != 0 && bEx[i]) ? 'x' : ' ', ind[i]);
 +    }
 +    fprintf(fplog, "\n");
 +}
 +
 +static void print_allswitchind(FILE *fplog, int n, int *pind, int *allswaps, int *tmpswap)
 +{
 +    int i;
 +
 +    for (i = 0; i < n; i++)
 +    {
 +        tmpswap[i] = allswaps[i];
 +    }
 +    for (i = 0; i < n; i++)
 +    {
 +        allswaps[i] = tmpswap[pind[i]];
 +    }
 +
 +    fprintf(fplog, "\nAccepted Exchanges:   ");
 +    for (i = 0; i < n; i++)
 +    {
 +        fprintf(fplog, "%d ", pind[i]);
 +    }
 +    fprintf(fplog, "\n");
 +
 +    /* the "Order After Exchange" is the state label corresponding to the configuration that
 +       started in state listed in order, i.e.
 +
 +       3 0 1 2
 +
 +       means that the:
 +       configuration starting in simulation 3 is now in simulation 0,
 +       configuration starting in simulation 0 is now in simulation 1,
 +       configuration starting in simulation 1 is now in simulation 2,
 +       configuration starting in simulation 2 is now in simulation 3
 +     */
 +    fprintf(fplog, "Order After Exchange: ");
 +    for (i = 0; i < n; i++)
 +    {
 +        fprintf(fplog, "%d ", allswaps[i]);
 +    }
 +    fprintf(fplog, "\n\n");
 +}
 +
 +static void print_prob(FILE *fplog, const char *leg, int n, real *prob)
 +{
 +    int  i;
 +    char buf[8];
 +
 +    fprintf(fplog, "Repl %2s ", leg);
 +    for (i = 1; i < n; i++)
 +    {
 +        if (prob[i] >= 0)
 +        {
 +            sprintf(buf, "%4.2f", prob[i]);
 +            fprintf(fplog, "  %3s", buf[0] == '1' ? "1.0" : buf+1);
 +        }
 +        else
 +        {
 +            fprintf(fplog, "     ");
 +        }
 +    }
 +    fprintf(fplog, "\n");
 +}
 +
 +static void print_count(FILE *fplog, const char *leg, int n, int *count)
 +{
 +    int i;
 +
 +    fprintf(fplog, "Repl %2s ", leg);
 +    for (i = 1; i < n; i++)
 +    {
 +        fprintf(fplog, " %4d", count[i]);
 +    }
 +    fprintf(fplog, "\n");
 +}
 +
 +static real calc_delta(FILE *fplog, gmx_bool bPrint, struct gmx_repl_ex *re, int a, int b, int ap, int bp)
 +{
 +
 +    real   ediff, dpV, delta = 0;
 +    real  *Epot = re->Epot;
 +    real  *Vol  = re->Vol;
 +    real **de   = re->de;
 +    real  *beta = re->beta;
 +
 +    /* Two cases; we are permuted and not.  In all cases, setting ap = a and bp = b will reduce
 +       to the non permuted case */
 +
 +    switch (re->type)
 +    {
 +        case ereTEMP:
 +            /*
 +             * Okabe et. al. Chem. Phys. Lett. 335 (2001) 435-439
 +             */
 +            ediff = Epot[b] - Epot[a];
 +            delta = -(beta[bp] - beta[ap])*ediff;
 +            break;
 +        case ereLAMBDA:
 +            /* two cases:  when we are permuted, and not.  */
 +            /* non-permuted:
 +               ediff =  E_new - E_old
 +                     =  [H_b(x_a) + H_a(x_b)] - [H_b(x_b) + H_a(x_a)]
 +                     =  [H_b(x_a) - H_a(x_a)] + [H_a(x_b) - H_b(x_b)]
 +                     =  de[b][a] + de[a][b] */
 +
 +            /* permuted:
 +               ediff =  E_new - E_old
 +                     =  [H_bp(x_a) + H_ap(x_b)] - [H_bp(x_b) + H_ap(x_a)]
 +                     =  [H_bp(x_a) - H_ap(x_a)] + [H_ap(x_b) - H_bp(x_b)]
 +                     =  [H_bp(x_a) - H_a(x_a) + H_a(x_a) - H_ap(x_a)] + [H_ap(x_b) - H_b(x_b) + H_b(x_b) - H_bp(x_b)]
 +                     =  [H_bp(x_a) - H_a(x_a)] - [H_ap(x_a) - H_a(x_a)] + [H_ap(x_b) - H_b(x_b)] - H_bp(x_b) - H_b(x_b)]
 +                     =  (de[bp][a] - de[ap][a]) + (de[ap][b] - de[bp][b])    */
 +            /* but, in the current code implementation, we flip configurations, not indices . . .
 +               So let's examine that.
 +                     =  [H_b(x_ap) - H_a(x_a)] - [H_a(x_ap) - H_a(x_a)] + [H_a(x_bp) - H_b(x_b)] - H_b(x_bp) - H_b(x_b)]
 +                     =  [H_b(x_ap) - H_a(x_ap)]  + [H_a(x_bp) - H_b(x_pb)]
 +                     = (de[b][ap] - de[a][ap]) + (de[a][bp] - de[b][bp]
 +                     So, if we exchange b<=> bp and a<=> ap, we return to the same result.
 +                     So the simple solution is to flip the
 +                     position of perturbed and original indices in the tests.
 +             */
 +
 +            ediff = (de[bp][a] - de[ap][a]) + (de[ap][b] - de[bp][b]);
 +            delta = ediff*beta[a]; /* assume all same temperature in this case */
 +            break;
 +        case ereTL:
 +            /* not permuted:  */
 +            /* delta =  reduced E_new - reduced E_old
 +                     =  [beta_b H_b(x_a) + beta_a H_a(x_b)] - [beta_b H_b(x_b) + beta_a H_a(x_a)]
 +                     =  [beta_b H_b(x_a) - beta_a H_a(x_a)] + [beta_a H_a(x_b) - beta_b H_b(x_b)]
 +                     =  [beta_b dH_b(x_a) + beta_b H_a(x_a) - beta_a H_a(x_a)] +
 +                        [beta_a dH_a(x_b) + beta_a H_b(x_b) - beta_b H_b(x_b)]
 +                     =  [beta_b dH_b(x_a) + [beta_a dH_a(x_b) +
 +                        beta_b (H_a(x_a) - H_b(x_b)]) - beta_a (H_a(x_a) - H_b(x_b))
 +                     =  beta_b dH_b(x_a) + beta_a dH_a(x_b) - (beta_b - beta_a)(H_b(x_b) - H_a(x_a) */
 +            /* delta = beta[b]*de[b][a] + beta[a]*de[a][b] - (beta[b] - beta[a])*(Epot[b] - Epot[a]; */
 +            /* permuted (big breath!) */
 +            /*   delta =  reduced E_new - reduced E_old
 +                     =  [beta_bp H_bp(x_a) + beta_ap H_ap(x_b)] - [beta_bp H_bp(x_b) + beta_ap H_ap(x_a)]
 +                     =  [beta_bp H_bp(x_a) - beta_ap H_ap(x_a)] + [beta_ap H_ap(x_b) - beta_bp H_bp(x_b)]
 +                     =  [beta_bp H_bp(x_a) - beta_ap H_ap(x_a)] + [beta_ap H_ap(x_b) - beta_bp H_bp(x_b)]
 +                        - beta_pb H_a(x_a) + beta_ap H_a(x_a) + beta_pb H_a(x_a) - beta_ap H_a(x_a)
 +                        - beta_ap H_b(x_b) + beta_bp H_b(x_b) + beta_ap H_b(x_b) - beta_bp H_b(x_b)
 +                     =  [(beta_bp H_bp(x_a) - beta_bp H_a(x_a)) - (beta_ap H_ap(x_a) - beta_ap H_a(x_a))] +
 +                        [(beta_ap H_ap(x_b)  - beta_ap H_b(x_b)) - (beta_bp H_bp(x_b) - beta_bp H_b(x_b))]
 +             + beta_pb H_a(x_a) - beta_ap H_a(x_a) + beta_ap H_b(x_b) - beta_bp H_b(x_b)
 +                     =  [beta_bp (H_bp(x_a) - H_a(x_a)) - beta_ap (H_ap(x_a) - H_a(x_a))] +
 +                        [beta_ap (H_ap(x_b) - H_b(x_b)) - beta_bp (H_bp(x_b) - H_b(x_b))]
 +             + beta_pb (H_a(x_a) - H_b(x_b))  - beta_ap (H_a(x_a) - H_b(x_b))
 +                     =  ([beta_bp de[bp][a] - beta_ap de[ap][a]) + beta_ap de[ap][b]  - beta_bp de[bp][b])
 +             + (beta_pb-beta_ap)(H_a(x_a) - H_b(x_b))  */
 +            delta = beta[bp]*(de[bp][a] - de[bp][b]) + beta[ap]*(de[ap][b] - de[ap][a]) - (beta[bp]-beta[ap])*(Epot[b]-Epot[a]);
 +            break;
 +        default:
 +            gmx_incons("Unknown replica exchange quantity");
 +    }
 +    if (bPrint)
 +    {
 +        fprintf(fplog, "Repl %d <-> %d  dE_term = %10.3e (kT)\n", a, b, delta);
 +    }
 +    if (re->bNPT)
 +    {
 +        /* revist the calculation for 5.0.  Might be some improvements. */
 +        dpV = (beta[ap]*re->pres[ap]-beta[bp]*re->pres[bp])*(Vol[b]-Vol[a])/PRESFAC;
 +        if (bPrint)
 +        {
-     fprintf(fplog, "Replica exchange at step " "%"GMX_PRId64 " time %g\n", step, time);
++            fprintf(fplog, "  dpV = %10.3e  d = %10.3e\n", dpV, delta + dpV);
 +        }
 +        delta += dpV;
 +    }
 +    return delta;
 +}
 +
 +static void
 +test_for_replica_exchange(FILE                 *fplog,
 +                          const gmx_multisim_t *ms,
 +                          struct gmx_repl_ex   *re,
 +                          gmx_enerdata_t       *enerd,
 +                          real                  vol,
 +                          gmx_int64_t           step,
 +                          real                  time)
 +{
 +    int       m, i, j, a, b, ap, bp, i0, i1, tmp;
 +    real      ediff = 0, delta = 0, dpV = 0;
 +    gmx_bool  bPrint, bMultiEx;
 +    gmx_bool *bEx      = re->bEx;
 +    real     *prob     = re->prob;
 +    int      *pind     = re->destinations; /* permuted index */
 +    gmx_bool  bEpot    = FALSE;
 +    gmx_bool  bDLambda = FALSE;
 +    gmx_bool  bVol     = FALSE;
 +    gmx_rng_t rng;
 +
 +    bMultiEx = (re->nex > 1);  /* multiple exchanges at each state */
- prepare_to_do_exchange(FILE      *fplog,
-                        const int *destinations,
-                        const int  replica_id,
-                        const int  nrepl,
-                        int       *maxswap,
-                        int      **order,
-                        int      **cyclic,
-                        int       *incycle,
-                        gmx_bool  *bThisReplicaExchanged)
++    fprintf(fplog, "Replica exchange at step " "%"GMX_PRId64 " time %.5f\n", step, time);
 +
 +    if (re->bNPT)
 +    {
 +        for (i = 0; i < re->nrepl; i++)
 +        {
 +            re->Vol[i] = 0;
 +        }
 +        bVol               = TRUE;
 +        re->Vol[re->repl]  = vol;
 +    }
 +    if ((re->type == ereTEMP || re->type == ereTL))
 +    {
 +        for (i = 0; i < re->nrepl; i++)
 +        {
 +            re->Epot[i] = 0;
 +        }
 +        bEpot              = TRUE;
 +        re->Epot[re->repl] = enerd->term[F_EPOT];
 +        /* temperatures of different states*/
 +        for (i = 0; i < re->nrepl; i++)
 +        {
 +            re->beta[i] = 1.0/(re->q[ereTEMP][i]*BOLTZ);
 +        }
 +    }
 +    else
 +    {
 +        for (i = 0; i < re->nrepl; i++)
 +        {
 +            re->beta[i] = 1.0/(re->temp*BOLTZ);  /* we have a single temperature */
 +        }
 +    }
 +    if (re->type == ereLAMBDA || re->type == ereTL)
 +    {
 +        bDLambda = TRUE;
 +        /* lambda differences. */
 +        /* de[i][j] is the energy of the jth simulation in the ith Hamiltonian
 +           minus the energy of the jth simulation in the jth Hamiltonian */
 +        for (i = 0; i < re->nrepl; i++)
 +        {
 +            for (j = 0; j < re->nrepl; j++)
 +            {
 +                re->de[i][j] = 0;
 +            }
 +        }
 +        for (i = 0; i < re->nrepl; i++)
 +        {
 +            re->de[i][re->repl] = (enerd->enerpart_lambda[(int)re->q[ereLAMBDA][i]+1]-enerd->enerpart_lambda[0]);
 +        }
 +    }
 +
 +    /* now actually do the communication */
 +    if (bVol)
 +    {
 +        gmx_sum_sim(re->nrepl, re->Vol, ms);
 +    }
 +    if (bEpot)
 +    {
 +        gmx_sum_sim(re->nrepl, re->Epot, ms);
 +    }
 +    if (bDLambda)
 +    {
 +        for (i = 0; i < re->nrepl; i++)
 +        {
 +            gmx_sum_sim(re->nrepl, re->de[i], ms);
 +        }
 +    }
 +
 +    /* make a duplicate set of indices for shuffling */
 +    for (i = 0; i < re->nrepl; i++)
 +    {
 +        pind[i] = re->ind[i];
 +    }
 +
 +    if (bMultiEx)
 +    {
 +        /* multiple random switch exchange */
 +        int nself = 0;
 +        for (i = 0; i < re->nex + nself; i++)
 +        {
 +            double rnd[2];
 +
 +            gmx_rng_cycle_2uniform(step, i*2, re->seed, RND_SEED_REPLEX, rnd);
 +            /* randomly select a pair  */
 +            /* in theory, could reduce this by identifying only which switches had a nonneglibible
 +               probability of occurring (log p > -100) and only operate on those switches */
 +            /* find out which state it is from, and what label that state currently has. Likely
 +               more work that useful. */
 +            i0 = (int)(re->nrepl*rnd[0]);
 +            i1 = (int)(re->nrepl*rnd[1]);
 +            if (i0 == i1)
 +            {
 +                nself++;
 +                continue;  /* self-exchange, back up and do it again */
 +            }
 +
 +            a  = re->ind[i0]; /* what are the indices of these states? */
 +            b  = re->ind[i1];
 +            ap = pind[i0];
 +            bp = pind[i1];
 +
 +            bPrint = FALSE; /* too noisy */
 +            /* calculate the energy difference */
 +            /* if the code changes to flip the STATES, rather than the configurations,
 +               use the commented version of the code */
 +            /* delta = calc_delta(fplog,bPrint,re,a,b,ap,bp); */
 +            delta = calc_delta(fplog, bPrint, re, ap, bp, a, b);
 +
 +            /* we actually only use the first space in the prob and bEx array,
 +               since there are actually many switches between pairs. */
 +
 +            if (delta <= 0)
 +            {
 +                /* accepted */
 +                prob[0] = 1;
 +                bEx[0]  = TRUE;
 +            }
 +            else
 +            {
 +                if (delta > PROBABILITYCUTOFF)
 +                {
 +                    prob[0] = 0;
 +                }
 +                else
 +                {
 +                    prob[0] = exp(-delta);
 +                }
 +                /* roll a number to determine if accepted */
 +                gmx_rng_cycle_2uniform(step, i*2+1, re->seed, RND_SEED_REPLEX, rnd);
 +                bEx[0] = rnd[0] < prob[0];
 +            }
 +            re->prob_sum[0] += prob[0];
 +
 +            if (bEx[0])
 +            {
 +                /* swap the states */
 +                tmp      = pind[i0];
 +                pind[i0] = pind[i1];
 +                pind[i1] = tmp;
 +            }
 +        }
 +        re->nattempt[0]++;  /* keep track of total permutation trials here */
 +        print_allswitchind(fplog, re->nrepl, pind, re->allswaps, re->tmpswap);
 +    }
 +    else
 +    {
 +        /* standard nearest neighbor replica exchange */
 +
 +        m = (step / re->nst) % 2;
 +        for (i = 1; i < re->nrepl; i++)
 +        {
 +            a = re->ind[i-1];
 +            b = re->ind[i];
 +
 +            bPrint = (re->repl == a || re->repl == b);
 +            if (i % 2 == m)
 +            {
 +                delta = calc_delta(fplog, bPrint, re, a, b, a, b);
 +                if (delta <= 0)
 +                {
 +                    /* accepted */
 +                    prob[i] = 1;
 +                    bEx[i]  = TRUE;
 +                }
 +                else
 +                {
 +                    double rnd[2];
 +
 +                    if (delta > PROBABILITYCUTOFF)
 +                    {
 +                        prob[i] = 0;
 +                    }
 +                    else
 +                    {
 +                        prob[i] = exp(-delta);
 +                    }
 +                    /* roll a number to determine if accepted */
 +                    gmx_rng_cycle_2uniform(step, i, re->seed, RND_SEED_REPLEX, rnd);
 +                    bEx[i] = rnd[0] < prob[i];
 +                }
 +                re->prob_sum[i] += prob[i];
 +
 +                if (bEx[i])
 +                {
 +                    /* swap these two */
 +                    tmp       = pind[i-1];
 +                    pind[i-1] = pind[i];
 +                    pind[i]   = tmp;
 +                    re->nexchange[i]++;  /* statistics for back compatibility */
 +                }
 +            }
 +            else
 +            {
 +                prob[i] = -1;
 +                bEx[i]  = FALSE;
 +            }
 +        }
 +        /* print some statistics */
 +        print_ind(fplog, "ex", re->nrepl, re->ind, bEx);
 +        print_prob(fplog, "pr", re->nrepl, prob);
 +        fprintf(fplog, "\n");
 +        re->nattempt[m]++;
 +    }
 +
 +    /* record which moves were made and accepted */
 +    for (i = 0; i < re->nrepl; i++)
 +    {
 +        re->nmoves[re->ind[i]][pind[i]] += 1;
 +        re->nmoves[pind[i]][re->ind[i]] += 1;
 +    }
 +    fflush(fplog); /* make sure we can see what the last exchange was */
 +}
 +
 +static void write_debug_x(t_state *state)
 +{
 +    int i;
 +
 +    if (debug)
 +    {
 +        for (i = 0; i < state->natoms; i += 10)
 +        {
 +            fprintf(debug, "dx %5d %10.5f %10.5f %10.5f\n", i, state->x[i][XX], state->x[i][YY], state->x[i][ZZ]);
 +        }
 +    }
 +}
 +
 +static void
 +cyclic_decomposition(const int *destinations,
 +                     int      **cyclic,
 +                     gmx_bool  *incycle,
 +                     const int  nrepl,
 +                     int       *nswap)
 +{
 +
 +    int i, j, c, p;
 +    int maxlen = 1;
 +    for (i = 0; i < nrepl; i++)
 +    {
 +        incycle[i] = FALSE;
 +    }
 +    for (i = 0; i < nrepl; i++)  /* one cycle for each replica */
 +    {
 +        if (incycle[i])
 +        {
 +            cyclic[i][0] = -1;
 +            continue;
 +        }
 +        cyclic[i][0] = i;
 +        incycle[i]   = TRUE;
 +        c            = 1;
 +        p            = i;
 +        for (j = 0; j < nrepl; j++) /* potentially all cycles are part, but we will break first */
 +        {
 +            p = destinations[p];    /* start permuting */
 +            if (p == i)
 +            {
 +                cyclic[i][c] = -1;
 +                if (c > maxlen)
 +                {
 +                    maxlen = c;
 +                }
 +                break; /* we've reached the original element, the cycle is complete, and we marked the end. */
 +            }
 +            else
 +            {
 +                cyclic[i][c] = p;  /* each permutation gives a new member of the cycle */
 +                incycle[p]   = TRUE;
 +                c++;
 +            }
 +        }
 +    }
 +    *nswap = maxlen - 1;
 +
 +    if (debug)
 +    {
 +        for (i = 0; i < nrepl; i++)
 +        {
 +            fprintf(debug, "Cycle %d:", i);
 +            for (j = 0; j < nrepl; j++)
 +            {
 +                if (cyclic[i][j] < 0)
 +                {
 +                    break;
 +                }
 +                fprintf(debug, "%2d", cyclic[i][j]);
 +            }
 +            fprintf(debug, "\n");
 +        }
 +        fflush(debug);
 +    }
 +}
 +
 +static void
 +compute_exchange_order(FILE     *fplog,
 +                       int     **cyclic,
 +                       int     **order,
 +                       const int nrepl,
 +                       const int maxswap)
 +{
 +    int i, j;
 +
 +    for (j = 0; j < maxswap; j++)
 +    {
 +        for (i = 0; i < nrepl; i++)
 +        {
 +            if (cyclic[i][j+1] >= 0)
 +            {
 +                order[cyclic[i][j+1]][j] = cyclic[i][j];
 +                order[cyclic[i][j]][j]   = cyclic[i][j+1];
 +            }
 +        }
 +        for (i = 0; i < nrepl; i++)
 +        {
 +            if (order[i][j] < 0)
 +            {
 +                order[i][j] = i; /* if it's not exchanging, it should stay this round*/
 +            }
 +        }
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(fplog, "Replica Exchange Order\n");
 +        for (i = 0; i < nrepl; i++)
 +        {
 +            fprintf(fplog, "Replica %d:", i);
 +            for (j = 0; j < maxswap; j++)
 +            {
 +                if (order[i][j] < 0)
 +                {
 +                    break;
 +                }
 +                fprintf(debug, "%2d", order[i][j]);
 +            }
 +            fprintf(fplog, "\n");
 +        }
 +        fflush(fplog);
 +    }
 +}
 +
 +static void
-     for (i = 0; i < nrepl; i++)
++prepare_to_do_exchange(FILE               *fplog,
++                       struct gmx_repl_ex *re,
++                       const int           replica_id,
++                       int                *maxswap,
++                       gmx_bool           *bThisReplicaExchanged)
 +{
 +    int i, j;
 +    /* Hold the cyclic decomposition of the (multiple) replica
 +     * exchange. */
 +    gmx_bool bAnyReplicaExchanged = FALSE;
 +    *bThisReplicaExchanged = FALSE;
 +
-         if (destinations[i] != i)
++    for (i = 0; i < re->nrepl; i++)
 +    {
-         for (i = 0; i < nrepl; i++)
++        if (re->destinations[i] != re->ind[i])
 +        {
 +            /* only mark as exchanged if the index has been shuffled */
 +            bAnyReplicaExchanged = TRUE;
 +            break;
 +        }
 +    }
 +    if (bAnyReplicaExchanged)
 +    {
 +        /* reinitialize the placeholder arrays */
-             for (j = 0; j < nrepl; j++)
++        for (i = 0; i < re->nrepl; i++)
 +        {
-                 cyclic[i][j] = -1;
-                 order[i][j]  = -1;
++            for (j = 0; j < re->nrepl; j++)
 +            {
-         cyclic_decomposition(destinations, cyclic, incycle, nrepl, maxswap);
++                re->cyclic[i][j] = -1;
++                re->order[i][j]  = -1;
 +            }
 +        }
 +
 +        /* Identify the cyclic decomposition of the permutation (very
 +         * fast if neighbor replica exchange). */
-         compute_exchange_order(fplog, cyclic, order, nrepl, *maxswap);
++        cyclic_decomposition(re->destinations, re->cyclic, re->incycle, re->nrepl, maxswap);
 +
 +        /* Now translate the decomposition into a replica exchange
 +         * order at each step. */
-             if (replica_id != order[replica_id][j])
++        compute_exchange_order(fplog, re->cyclic, re->order, re->nrepl, *maxswap);
 +
 +        /* Did this replica do any exchange at any point? */
 +        for (j = 0; j < *maxswap; j++)
 +        {
-         prepare_to_do_exchange(fplog, re->destinations, replica_id, re->nrepl, &maxswap,
-                                re->order, re->cyclic, re->incycle, &bThisReplicaExchanged);
++            if (replica_id != re->order[replica_id][j])
 +            {
 +                *bThisReplicaExchanged = TRUE;
 +                break;
 +            }
 +        }
 +    }
 +}
 +
 +gmx_bool replica_exchange(FILE *fplog, const t_commrec *cr, struct gmx_repl_ex *re,
 +                          t_state *state, gmx_enerdata_t *enerd,
 +                          t_state *state_local, gmx_int64_t step, real time)
 +{
 +    int i, j;
 +    int replica_id = 0;
 +    int exchange_partner;
 +    int maxswap = 0;
 +    /* Number of rounds of exchanges needed to deal with any multiple
 +     * exchanges. */
 +    /* Where each replica ends up after the exchange attempt(s). */
 +    /* The order in which multiple exchanges will occur. */
 +    gmx_bool bThisReplicaExchanged = FALSE;
 +
 +    if (MASTER(cr))
 +    {
 +        replica_id  = re->repl;
 +        test_for_replica_exchange(fplog, cr->ms, re, enerd, det(state_local->box), step, time);
++        prepare_to_do_exchange(fplog, re, replica_id, &maxswap, &bThisReplicaExchanged);
 +    }
 +    /* Do intra-simulation broadcast so all processors belonging to
 +     * each simulation know whether they need to participate in
 +     * collecting the state. Otherwise, they might as well get on with
 +     * the next thing to do. */
 +    if (DOMAINDECOMP(cr))
 +    {
 +#ifdef GMX_MPI
 +        MPI_Bcast(&bThisReplicaExchanged, sizeof(gmx_bool), MPI_BYTE, MASTERRANK(cr),
 +                  cr->mpi_comm_mygroup);
 +#endif
 +    }
 +
 +    if (bThisReplicaExchanged)
 +    {
 +        /* Exchange the states */
 +        /* Collect the global state on the master node */
 +        if (DOMAINDECOMP(cr))
 +        {
 +            dd_collect_state(cr->dd, state_local, state);
 +        }
 +        else
 +        {
 +            copy_state_nonatomdata(state_local, state);
 +        }
 +
 +        if (MASTER(cr))
 +        {
 +            /* There will be only one swap cycle with standard replica
 +             * exchange, but there may be multiple swap cycles if we
 +             * allow multiple swaps. */
 +
 +            for (j = 0; j < maxswap; j++)
 +            {
 +                exchange_partner = re->order[replica_id][j];
 +
 +                if (exchange_partner != replica_id)
 +                {
 +                    /* Exchange the global states between the master nodes */
 +                    if (debug)
 +                    {
 +                        fprintf(debug, "Exchanging %d with %d\n", replica_id, exchange_partner);
 +                    }
 +                    exchange_state(cr->ms, exchange_partner, state);
 +                }
 +            }
 +            /* For temperature-type replica exchange, we need to scale
 +             * the velocities. */
 +            if (re->type == ereTEMP || re->type == ereTL)
 +            {
 +                scale_velocities(state, sqrt(re->q[ereTEMP][replica_id]/re->q[ereTEMP][re->destinations[replica_id]]));
 +            }
 +
 +        }
 +
 +        /* With domain decomposition the global state is distributed later */
 +        if (!DOMAINDECOMP(cr))
 +        {
 +            /* Copy the global state to the local state data structure */
 +            copy_state_nonatomdata(state, state_local);
 +        }
 +    }
 +
 +    return bThisReplicaExchanged;
 +}
 +
 +void print_replica_exchange_statistics(FILE *fplog, struct gmx_repl_ex *re)
 +{
 +    int  i;
 +
 +    fprintf(fplog, "\nReplica exchange statistics\n");
 +
 +    if (re->nex == 0)
 +    {
 +        fprintf(fplog, "Repl  %d attempts, %d odd, %d even\n",
 +                re->nattempt[0]+re->nattempt[1], re->nattempt[1], re->nattempt[0]);
 +
 +        fprintf(fplog, "Repl  average probabilities:\n");
 +        for (i = 1; i < re->nrepl; i++)
 +        {
 +            if (re->nattempt[i%2] == 0)
 +            {
 +                re->prob[i] = 0;
 +            }
 +            else
 +            {
 +                re->prob[i] =  re->prob_sum[i]/re->nattempt[i%2];
 +            }
 +        }
 +        print_ind(fplog, "", re->nrepl, re->ind, NULL);
 +        print_prob(fplog, "", re->nrepl, re->prob);
 +
 +        fprintf(fplog, "Repl  number of exchanges:\n");
 +        print_ind(fplog, "", re->nrepl, re->ind, NULL);
 +        print_count(fplog, "", re->nrepl, re->nexchange);
 +
 +        fprintf(fplog, "Repl  average number of exchanges:\n");
 +        for (i = 1; i < re->nrepl; i++)
 +        {
 +            if (re->nattempt[i%2] == 0)
 +            {
 +                re->prob[i] = 0;
 +            }
 +            else
 +            {
 +                re->prob[i] =  ((real)re->nexchange[i])/re->nattempt[i%2];
 +            }
 +        }
 +        print_ind(fplog, "", re->nrepl, re->ind, NULL);
 +        print_prob(fplog, "", re->nrepl, re->prob);
 +
 +        fprintf(fplog, "\n");
 +    }
 +    /* print the transition matrix */
 +    print_transition_matrix(fplog, re->nrepl, re->nmoves, re->nattempt);
 +}