This merges changes more than resolving a conflict.
The reason is that Ic142a690 moved the files to a location
not suitable for master.
molfile_plugin.h, vmddlopen.h, vmdio.h, and vmdplugin.h
were in
- "include" before Ic142a690
- "src/gmxlib" after Ic142a690
- "src/gromacs/legacyheaders" in master
Moved all but vmdio.h to "src/external/vmd_molfile"
Moved vmdio.h to "src/gromacs/gmxlib"
Moved vmddlopen.c (in src/gmxlib or src/gromacs/gmxlib) also to
"src/external/vmd_molfile" (was not moved by Ic142a690).
Removed "#ifdef GMX_USE_PLUGINS" from vmddlopen.c
This makes the files in src/external/vmd_molfile identical to
VMD 1.8.6 besides the Copyright notice.
Instead of the #ifdef, only compile vmdio.c and vmddlopen.c if
GMX_USE_PLUGINS is set (in src/gromacs/gmxlib/CMakeLists.txt)
Trivial conflict in:
src/gromacs/mdlib/pull_rotation.c
Change-Id: If9bda639e4f680c09d785b046fc0382c16688beb
--- /dev/null
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
-
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This file is part of Gromacs Copyright (c) 1991-2008
+ * David van der Spoel, Erik Lindahl, Berk Hess, University of Groningen.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org
+ *
+ * And Hey:
+ * Gnomes, ROck Monsters And Chili Sauce
+ */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
-
-
- #ifdef GMX_USE_PLUGINS
+
+/***************************************************************************
+ *cr
+ *cr (C) Copyright 1995-2009 The Board of Trustees of the
+ *cr University of Illinois
+ *cr All Rights Reserved
+ *cr
+Developed by: Theoretical and Computational Biophysics Group
+ University of Illinois at Urbana-Champaign
+ http://www.ks.uiuc.edu/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the Software), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to
+do so, subject to the following conditions:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimers.
+
+Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimers in the documentation
+and/or other materials provided with the distribution.
+
+Neither the names of Theoretical and Computational Biophysics Group,
+University of Illinois at Urbana-Champaign, nor the names of its contributors
+may be used to endorse or promote products derived from this Software without
+specific prior written permission.
+
+THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS WITH THE SOFTWARE.
+ ***************************************************************************/
+
+/***************************************************************************
+ * RCS INFORMATION:
+ *
+ * $RCSfile: vmddlopen.c,v $
+ * $Author: johns $ $Locker: $ $State: Exp $
+ * $Revision: 1.18 $ $Date: 2009/07/07 02:40:05 $
+ *
+ ***************************************************************************
+ * DESCRIPTION:
+ * Routines for loading dynamic link libraries and shared object files
+ * on various platforms, abstracting from machine dependent APIs.
+ *
+ ***************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "vmddlopen.h"
+
- #else
- void *vmddlopen(const char *fname) {
- return NULL;
- }
- const char *vmddlerror(void) {
- return NULL;
- }
- void *vmddlsym(void *h, const char *sym) {
- return NULL;
- }
- int vmddlclose(void *h) {
- return 0;
- }
- #endif
+#if defined(__hpux)
+
+#include <dl.h>
+#include <errno.h>
+#include <string.h>
+
+void *vmddlopen( const char *path) {
+ void *ret;
+ ret = shl_load( path, BIND_IMMEDIATE | BIND_FIRST | BIND_VERBOSE, 0);
+ return ret;
+}
+
+int vmddlclose( void *handle ) {
+ return shl_unload( (shl_t) handle );
+}
+
+void *vmddlsym( void *handle, const char *sym ) {
+ void *value=0;
+
+ if ( shl_findsym( (shl_t*)&handle, sym, TYPE_UNDEFINED, &value ) != 0 )
+ return 0;
+ return value;
+}
+
+const char *vmddlerror( void ) {
+ return strerror( errno );
+}
+
+#elif 0 && defined(__APPLE__)
+/*
+ * This is only needed for MacOS X version 10.3 or older
+ */
+#include <mach-o/dyld.h>
+
+void *vmddlopen( const char *path) {
+ NSObjectFileImage image;
+ NSObjectFileImageReturnCode retval;
+ NSModule module;
+
+ retval = NSCreateObjectFileImageFromFile(path, &image);
+ if (retval != NSObjectFileImageSuccess)
+ return NULL;
+
+ module = NSLinkModule(image, path,
+ NSLINKMODULE_OPTION_BINDNOW | NSLINKMODULE_OPTION_PRIVATE
+ | NSLINKMODULE_OPTION_RETURN_ON_ERROR);
+ return module; /* module will be NULL on error */
+}
+
+int vmddlclose( void *handle ) {
+ NSModule module = (NSModule *)handle;
+ NSUnLinkModule(module, NSUNLINKMODULE_OPTION_NONE);
+ return 0;
+}
+
+void *vmddlsym( void *handle, const char *symname ) {
+ char *realsymname;
+ NSModule module;
+ NSSymbol sym;
+ /* Hack around the leading underscore in the symbol name */
+ realsymname = (char *)malloc(strlen(symname)+2);
+ strcpy(realsymname, "_");
+ strcat(realsymname, symname);
+ module = (NSModule)handle;
+ sym = NSLookupSymbolInModule(module, realsymname);
+ free(realsymname);
+ if (sym)
+ return (void *)(NSAddressOfSymbol(sym));
+ return NULL;
+}
+
+const char *vmddlerror( void ) {
+ NSLinkEditErrors c;
+ int errorNumber;
+ const char *fileName;
+ const char *errorString = NULL;
+ NSLinkEditError(&c, &errorNumber, &fileName, &errorString);
+ return errorString;
+}
+
+#elif defined(_MSC_VER)
+
+#include <windows.h>
+
+void *vmddlopen(const char *fname) {
+ return (void *)LoadLibrary(fname);
+}
+
+const char *vmddlerror(void) {
+ static CHAR szBuf[80];
+ DWORD dw = GetLastError();
+
+ sprintf(szBuf, "vmddlopen failed: GetLastError returned %u\n", dw);
+ return szBuf;
+}
+
+void *vmddlsym(void *h, const char *sym) {
+ return (void *)GetProcAddress((HINSTANCE)h, sym);
+}
+
+int vmddlclose(void *h) {
+ /* FreeLibrary returns nonzero on success */
+ return !FreeLibrary((HINSTANCE)h);
+}
+
+#else
+
+/* All remaining platforms (not Windows, HP-UX, or MacOS X <= 10.3) */
+#include <dlfcn.h>
+
+void *vmddlopen(const char *fname) {
+ return dlopen(fname, RTLD_NOW);
+}
+const char *vmddlerror(void) {
+ return dlerror();
+}
+void *vmddlsym(void *h, const char *sym) {
+ return dlsym(h, sym);
+}
+int vmddlclose(void *h) {
+ return dlclose(h);
+}
+#endif
--- /dev/null
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+
+# The nonbonded directory contains subdirectories that are only
+# conditionally built, so we cannot use a GLOB_RECURSE here.
+file(GLOB GMXLIB_SOURCES *.c *.cpp
+ statistics/*.c nonbonded/*.c nonbonded/nb_kernel_c/*.c
+ nonbonded/nb_kernel_adress_c/*.c)
+
+if(GMX_DOUBLE)
+ set(SSETYPE sse2)
+else()
+ set(SSETYPE sse)
+endif()
+
+if(GMX_IA32_ASM)
+ file(GLOB GMX_SSEKERNEL_C_SRC nonbonded/nb_kernel_ia32_${SSETYPE}/*.c)
+ if(GMX_ASM_USEASM_NASM)
+ file(GLOB GMX_SSEKERNEL_ASM_SRC nonbonded/nb_kernel_ia32_${SSETYPE}/*intel_syntax*.s)
+ else()
+ file(GLOB GMX_SSEKERNEL_ASM_SRC nonbonded/nb_kernel_ia32_${SSETYPE}/*${SSETYPE}.s nonbonded/nb_kernel_ia32_${SSETYPE}/*asm.s)
+ endif()
+endif(GMX_IA32_ASM)
+
+if(GMX_X86_64_ASM)
+ file(GLOB GMX_SSEKERNEL_C_SRC nonbonded/nb_kernel_x86_64_${SSETYPE}/*.c)
+ if(GMX_ASM_USEASM_NASM)
+ file(GLOB GMX_SSEKERNEL_ASM_SRC nonbonded/nb_kernel_x86_64_${SSETYPE}/*intel_syntax*.s)
+ else()
+ file(GLOB GMX_SSEKERNEL_ASM_SRC nonbonded/nb_kernel_x86_64_${SSETYPE}/*${SSETYPE}.s nonbonded/nb_kernel_x86_64_${SSETYPE}/*asm.s)
+ endif()
+endif(GMX_X86_64_ASM)
+
+if(GMX_FORTRAN)
+ if (GMX_DOUBLE)
+ file(GLOB FORTRAN_SOURCES nonbonded/nb_kernel_f77_double/*.[cf])
+ else(GMX_DOUBLE)
+ file(GLOB FORTRAN_SOURCES nonbonded/nb_kernel_f77_single/*.[cf])
+ endif(GMX_DOUBLE)
+endif(GMX_FORTRAN)
+
+if(GMX_POWER6)
+ file(GLOB FORTRAN_SOURCES nonbonded/nb_kernel_power6/*.[cF])
+endif(GMX_POWER6)
+
+if(GMX_BLUEGENE)
+ file(GLOB GMX_BLUEGENE_C_SRC nonbonded/nb_kernel_bluegene/*.c)
+endif(GMX_BLUEGENE)
+
+if(GMX_PPC_ALTIVEC)
+ file(GLOB GMX_PPC_ALTIVEC_SRC nonbonded/nb_kernel_ppc_altivec/*.c)
+endif(GMX_PPC_ALTIVEC)
+
+if(NOT GMX_EXTERNAL_BLAS)
+ file(GLOB BLAS_SOURCES gmx_blas/*.c)
+endif(NOT GMX_EXTERNAL_BLAS)
+
+if(NOT GMX_EXTERNAL_LAPACK)
+ file(GLOB LAPACK_SOURCES gmx_lapack/*.c)
+endif(NOT GMX_EXTERNAL_LAPACK)
+
+# This would be the standard way to include thread_mpi, but we want libgmx
+# to link the functions directly
+#if(GMX_THREAD_MPI)
+# add_subdirectory(thread_mpi)
+#endif(GMX_THREAD_MPI)
+#target_link_libraries(gmx ${GMX_EXTRA_LIBRARIES} ${THREAD_MPI_LIB})
+
+# Files called xxx_test.c are test drivers with a main() function for module xxx.c,
+# so they should not be included in the library
+file(GLOB_RECURSE NOT_GMXLIB_SOURCES *_test.c *\#*)
+list(REMOVE_ITEM GMXLIB_SOURCES ${NOT_GMXLIB_SOURCES})
+
++if(GMX_USE_PLUGINS)
++ set(GMXLIB_SOURCES ${GMXLIB_SOURCES} ${CMAKE_SOURCE_DIR}/src/external/vmd_molfile/vmddlopen.c)
++else()
++ list(REMOVE_ITEM GMXLIB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/vmdio.c)
++endif()
++
+# An ugly hack to get absolute paths...
+file(GLOB THREAD_MPI_SOURCES ${THREAD_MPI_SRC})
+
+set(GMX_SSEKERNEL_ASM_SRC ${GMX_SSEKERNEL_ASM_SRC} PARENT_SCOPE)
+set(GMXLIB_SOURCES ${GMXLIB_SOURCES} ${BLAS_SOURCES} ${LAPACK_SOURCES}
+ ${GMX_SSEKERNEL_C_SRC} ${FORTRAN_SOURCES}
+ ${GMX_BLUEGENE_C_SRC} ${GMX_PPC_ALTIVEC_SRC} ${THREAD_MPI_SOURCES}
+ PARENT_SCOPE)
--- /dev/null
- /* this is not strictly thread-safe, but it's only written to at the beginning
- of the simulation, once by each thread with the same value. We assume
- that writing to an int is atomic.*/
- static gmx_bool parallel_env_val;
- #ifdef GMX_THREAD_MPI
- tMPI_Thread_mutex_t parallel_env_mutex=TMPI_THREAD_MUTEX_INITIALIZER;
- #endif
-
-
- /* returns 1 when running in a parallel environment, so could also be 1 if
- mdrun was started with: mpirun -np 1.
-
- Use this function only to check whether a parallel environment has
- been initialized, for example when checking whether gmx_finalize()
- needs to be called. Use PAR(cr) to check whether the simulation actually
- has more than one node/thread. */
- gmx_bool gmx_parallel_env_initialized(void)
- {
- gmx_bool ret;
- #ifdef GMX_THREAD_MPI
- tMPI_Thread_mutex_lock(¶llel_env_mutex);
- #endif
- ret=parallel_env_val;
- #ifdef GMX_THREAD_MPI
- tMPI_Thread_mutex_unlock(¶llel_env_mutex);
- #endif
- return ret;
- }
-
- static void set_parallel_env(gmx_bool val)
- {
- #ifdef GMX_THREAD_MPI
- tMPI_Thread_mutex_lock(¶llel_env_mutex);
- #endif
- if (!parallel_env_val)
- {
- /* we only allow it to be set, not unset */
- parallel_env_val=val;
- }
- #ifdef GMX_THREAD_MPI
- tMPI_Thread_mutex_unlock(¶llel_env_mutex);
- #endif
- }
-
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROningen Mixture of Alchemy and Childrens' Stories
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "gromacs/utility/gmx_header_config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include "smalloc.h"
+#include "gmx_fatal.h"
+#include "network.h"
+#include "main.h"
+#include "macros.h"
+#include "futil.h"
+#include "filenm.h"
+#include "mdrun.h"
+#include "gmxfio.h"
+#include "string2.h"
+
+#ifdef GMX_THREAD_MPI
+#include "thread_mpi.h"
+#endif
+
+/* The source code in this file should be thread-safe.
+ Please keep it that way. */
+
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef GMX_NATIVE_WINDOWS
+#include <process.h>
+#endif
+
+
+/* Portable version of ctime_r implemented in src/gmxlib/string2.c, but we do not want it declared in public installed headers */
+char *
+gmx_ctime_r(const time_t *clock,char *buf, int n);
+
+
+#define BUFSIZE 1024
+
- #ifdef GMX_MPI
- #ifdef GMX_LIB_MPI
- pe = TRUE;
- #ifdef GMX_CHECK_MPI_ENV
- /* Do not use MPI calls when env.var. GMX_CHECK_MPI_ENV is not set */
- if (getenv(GMX_CHECK_MPI_ENV) == NULL)
- pe = FALSE;
- #endif /* GMX_CHECK_MPI_ENV */
- #endif /* GMX_LIB_MPI */
- set_parallel_env(pe);
- if (pe) {
- cr->sim_nodeid = gmx_setup(argc,argv,&cr->nnodes);
- } else {
- cr->nnodes = 1;
- cr->sim_nodeid = 0;
- }
- #else /* GMX_MPI */
- pe=FALSE;
- set_parallel_env(pe);
- cr->sim_nodeid = 0;
- cr->nnodes = 1;
- #endif /* GMX_MPI */
+
+static void par_fn(char *base,int ftp,const t_commrec *cr,
+ gmx_bool bAppendSimId,gmx_bool bAppendNodeId,
+ char buf[],int bufsize)
+{
+ int n;
+
+ if((size_t)bufsize<(strlen(base)+10))
+ gmx_mem("Character buffer too small!");
+
+ /* Copy to buf, and strip extension */
+ strcpy(buf,base);
+ buf[strlen(base) - strlen(ftp2ext(fn2ftp(base))) - 1] = '\0';
+
+ if (bAppendSimId) {
+ sprintf(buf+strlen(buf),"%d",cr->ms->sim);
+ }
+ if (bAppendNodeId) {
+ strcat(buf,"_node");
+ sprintf(buf+strlen(buf),"%d",cr->nodeid);
+ }
+ strcat(buf,".");
+
+ /* Add extension again */
+ strcat(buf,(ftp == efTPX) ? "tpr" : (ftp == efEDR) ? "edr" : ftp2ext(ftp));
+ if (cr->nodeid == 0) {
+ printf("node %d par_fn '%s'\n",cr->nodeid,buf);
+ if (fn2ftp(buf) == efLOG) {
+ printf("log\n");
+ }
+ }
+}
+
+void check_multi_int(FILE *log,const gmx_multisim_t *ms,int val,
+ const char *name)
+{
+ int *ibuf,p;
+ gmx_bool bCompatible;
+
+ if (NULL != log)
+ fprintf(log,"Multi-checking %s ... ",name);
+
+ if (ms == NULL)
+ gmx_fatal(FARGS,
+ "check_multi_int called with a NULL communication pointer");
+
+ snew(ibuf,ms->nsim);
+ ibuf[ms->sim] = val;
+ gmx_sumi_sim(ms->nsim,ibuf,ms);
+
+ bCompatible = TRUE;
+ for(p=1; p<ms->nsim; p++)
+ bCompatible = bCompatible && (ibuf[p-1] == ibuf[p]);
+
+ if (bCompatible)
+ {
+ if (NULL != log)
+ fprintf(log,"OK\n");
+ }
+ else
+ {
+ if (NULL != log)
+ {
+ fprintf(log,"\n%s is not equal for all subsystems\n",name);
+ for(p=0; p<ms->nsim; p++)
+ fprintf(log," subsystem %d: %d\n",p,ibuf[p]);
+ }
+ gmx_fatal(FARGS,"The %d subsystems are not compatible\n",ms->nsim);
+ }
+
+ sfree(ibuf);
+}
+
+void check_multi_large_int(FILE *log,const gmx_multisim_t *ms,
+ gmx_large_int_t val, const char *name)
+{
+ gmx_large_int_t *ibuf;
+ int p;
+ gmx_bool bCompatible;
+
+ if (NULL != log)
+ fprintf(log,"Multi-checking %s ... ",name);
+
+ if (ms == NULL)
+ gmx_fatal(FARGS,
+ "check_multi_int called with a NULL communication pointer");
+
+ snew(ibuf,ms->nsim);
+ ibuf[ms->sim] = val;
+ gmx_sumli_sim(ms->nsim,ibuf,ms);
+
+ bCompatible = TRUE;
+ for(p=1; p<ms->nsim; p++)
+ bCompatible = bCompatible && (ibuf[p-1] == ibuf[p]);
+
+ if (bCompatible)
+ {
+ if (NULL != log)
+ fprintf(log,"OK\n");
+ }
+ else
+ {
+ if (NULL != log)
+ {
+ fprintf(log,"\n%s is not equal for all subsystems\n",name);
+ for(p=0; p<ms->nsim; p++)
+ {
+ char strbuf[255];
+ /* first make the format string */
+ snprintf(strbuf, 255, " subsystem %%d: %s\n",
+ gmx_large_int_pfmt);
+ fprintf(log,strbuf,p,ibuf[p]);
+ }
+ }
+ gmx_fatal(FARGS,"The %d subsystems are not compatible\n",ms->nsim);
+ }
+
+ sfree(ibuf);
+}
+
+
+void gmx_log_open(const char *lognm,const t_commrec *cr,gmx_bool bMasterOnly,
+ unsigned long Flags, FILE** fplog)
+{
+ int len,testlen,pid;
+ char buf[256],host[256];
+ time_t t;
+ char timebuf[STRLEN];
+ FILE *fp=*fplog;
+ char *tmpnm;
+
+ gmx_bool bAppend = Flags & MD_APPENDFILES;
+
+ debug_gmx();
+
+ /* Communicate the filename for logfile */
+ if (cr->nnodes > 1 && !bMasterOnly
+#ifdef GMX_THREAD_MPI
+ /* With thread MPI the non-master log files are opened later
+ * when the files names are already known on all nodes.
+ */
+ && FALSE
+#endif
+ )
+ {
+ if (MASTER(cr))
+ {
+ len = strlen(lognm) + 1;
+ }
+ gmx_bcast(sizeof(len),&len,cr);
+ if (!MASTER(cr))
+ {
+ snew(tmpnm,len+8);
+ }
+ else
+ {
+ tmpnm=gmx_strdup(lognm);
+ }
+ gmx_bcast(len*sizeof(*tmpnm),tmpnm,cr);
+ }
+ else
+ {
+ tmpnm=gmx_strdup(lognm);
+ }
+
+ debug_gmx();
+
+ if (!bMasterOnly && !MASTER(cr))
+ {
+ /* Since log always ends with '.log' let's use this info */
+ par_fn(tmpnm,efLOG,cr,FALSE,!bMasterOnly,buf,255);
+ fp = gmx_fio_fopen(buf, bAppend ? "a+" : "w+" );
+ }
+ else if (!bAppend)
+ {
+ fp = gmx_fio_fopen(tmpnm, bAppend ? "a+" : "w+" );
+ }
+
+ sfree(tmpnm);
+
+ gmx_fatal_set_log_file(fp);
+
+ /* Get some machine parameters */
+#ifdef HAVE_UNISTD_H
+ if (gethostname(host,255) != 0)
+ {
+ sprintf(host,"unknown");
+ }
+#else
+ sprintf(host,"unknown");
+#endif
+
+ time(&t);
+
+#ifndef NO_GETPID
+# ifdef GMX_NATIVE_WINDOWS
+ pid = _getpid();
+# else
+ pid = getpid();
+# endif
+#else
+ pid = 0;
+#endif
+
+ if (bAppend)
+ {
+ fprintf(fp,
+ "\n"
+ "\n"
+ "-----------------------------------------------------------\n"
+ "Restarting from checkpoint, appending to previous log file.\n"
+ "\n"
+ );
+ }
+
+ gmx_ctime_r(&t,timebuf,STRLEN);
+
+ fprintf(fp,
+ "Log file opened on %s"
+ "Host: %s pid: %d nodeid: %d nnodes: %d\n",
+ timebuf,host,pid,cr->nodeid,cr->nnodes);
+
+#if (defined BUILD_MACHINE && defined BUILD_TIME && defined BUILD_USER)
+ fprintf(fp,
+ "The Gromacs distribution was built %s by\n"
+ "%s (%s)\n\n\n",BUILD_TIME,BUILD_USER,BUILD_MACHINE);
+#endif
+
+ fflush(fp);
+ debug_gmx();
+
+ *fplog = fp;
+}
+
+void gmx_log_close(FILE *fp)
+{
+ if (fp) {
+ gmx_fatal_set_log_file(NULL);
+ gmx_fio_fclose(fp);
+ }
+}
+
+static void comm_args(const t_commrec *cr,int *argc,char ***argv)
+{
+ int i,len;
+
+ if (PAR(cr))
+ gmx_bcast(sizeof(*argc),argc,cr);
+
+ if (!MASTER(cr))
+ snew(*argv,*argc+1);
+ fprintf(stderr,"NODEID=%d argc=%d\n",cr->nodeid,*argc);
+ for(i=0; (i<*argc); i++) {
+ if (MASTER(cr))
+ len = strlen((*argv)[i])+1;
+ gmx_bcast(sizeof(len),&len,cr);
+ if (!MASTER(cr))
+ snew((*argv)[i],len);
+ /*gmx_bcast(len*sizeof((*argv)[i][0]),(*argv)[i],cr);*/
+ gmx_bcast(len*sizeof(char),(*argv)[i],cr);
+ }
+ debug_gmx();
+}
+
+void init_multisystem(t_commrec *cr,int nsim, char **multidirs,
+ int nfile, const t_filenm fnm[],gmx_bool bParFn)
+{
+ gmx_multisim_t *ms;
+ int nnodes,nnodpersim,sim,i,ftp;
+ char buf[256];
+#ifdef GMX_MPI
+ MPI_Group mpi_group_world;
+#endif
+ int *rank;
+
+#ifndef GMX_MPI
+ if (nsim > 1)
+ {
+ gmx_fatal(FARGS,"This binary is compiled without MPI support, can not do multiple simulations.");
+ }
+#endif
+
+ nnodes = cr->nnodes;
+ if (nnodes % nsim != 0)
+ {
+ gmx_fatal(FARGS,"The number of nodes (%d) is not a multiple of the number of simulations (%d)",nnodes,nsim);
+ }
+
+ nnodpersim = nnodes/nsim;
+ sim = cr->nodeid/nnodpersim;
+
+ if (debug)
+ {
+ fprintf(debug,"We have %d simulations, %d nodes per simulation, local simulation is %d\n",nsim,nnodpersim,sim);
+ }
+
+ snew(ms,1);
+ cr->ms = ms;
+ ms->nsim = nsim;
+ ms->sim = sim;
+#ifdef GMX_MPI
+ /* Create a communicator for the master nodes */
+ snew(rank,ms->nsim);
+ for(i=0; i<ms->nsim; i++)
+ {
+ rank[i] = i*nnodpersim;
+ }
+ MPI_Comm_group(MPI_COMM_WORLD,&mpi_group_world);
+ MPI_Group_incl(mpi_group_world,nsim,rank,&ms->mpi_group_masters);
+ sfree(rank);
+ MPI_Comm_create(MPI_COMM_WORLD,ms->mpi_group_masters,
+ &ms->mpi_comm_masters);
+
+#if !defined(GMX_THREAD_MPI) && !defined(MPI_IN_PLACE_EXISTS)
+ /* initialize the MPI_IN_PLACE replacement buffers */
+ snew(ms->mpb, 1);
+ ms->mpb->ibuf=NULL;
+ ms->mpb->libuf=NULL;
+ ms->mpb->fbuf=NULL;
+ ms->mpb->dbuf=NULL;
+ ms->mpb->ibuf_alloc=0;
+ ms->mpb->libuf_alloc=0;
+ ms->mpb->fbuf_alloc=0;
+ ms->mpb->dbuf_alloc=0;
+#endif
+
+#endif
+
+ /* Reduce the intra-simulation communication */
+ cr->sim_nodeid = cr->nodeid % nnodpersim;
+ cr->nnodes = nnodpersim;
+#ifdef GMX_MPI
+ MPI_Comm_split(MPI_COMM_WORLD,sim,cr->sim_nodeid,&cr->mpi_comm_mysim);
+ cr->mpi_comm_mygroup = cr->mpi_comm_mysim;
+ cr->nodeid = cr->sim_nodeid;
+#endif
+
+ if (debug)
+ {
+ fprintf(debug,"This is simulation %d",cr->ms->sim);
+ if (PAR(cr))
+ {
+ fprintf(debug,", local number of nodes %d, local nodeid %d",
+ cr->nnodes,cr->sim_nodeid);
+ }
+ fprintf(debug,"\n\n");
+ }
+
+ if (multidirs)
+ {
+ int ret;
+ if (debug)
+ {
+ fprintf(debug,"Changing to directory %s\n",multidirs[cr->ms->sim]);
+ }
+ gmx_chdir(multidirs[cr->ms->sim]);
+ }
+ else if (bParFn)
+ {
+ /* Patch output and tpx, cpt and rerun input file names */
+ for(i=0; (i<nfile); i++)
+ {
+ /* Because of possible multiple extensions per type we must look
+ * at the actual file name
+ */
+ if (is_output(&fnm[i]) ||
+ fnm[i].ftp == efTPX || fnm[i].ftp == efCPT ||
+ strcmp(fnm[i].opt,"-rerun") == 0)
+ {
+ ftp = fn2ftp(fnm[i].fns[0]);
+ par_fn(fnm[i].fns[0],ftp,cr,TRUE,FALSE,buf,255);
+ sfree(fnm[i].fns[0]);
+ fnm[i].fns[0] = gmx_strdup(buf);
+ }
+ }
+ }
+}
+
+t_commrec *init_par(int *argc,char ***argv_ptr)
+{
+ t_commrec *cr;
+ char **argv;
+ int i;
+ gmx_bool pe=FALSE;
+
+ snew(cr,1);
+
+ argv = *argv_ptr;
+
- gmx_comm("(!PAR(cr) && (cr->sim_nodeid != 0))");
-
- if (PAR(cr))
++#if defined GMX_MPI && !defined GMX_THREAD_MPI
++ cr->sim_nodeid = gmx_setup(argc,argv,&cr->nnodes);
+
+ if (!PAR(cr) && (cr->sim_nodeid != 0))
- #ifdef GMX_MPI
- cr->mpi_comm_mysim = MPI_COMM_WORLD;
- cr->mpi_comm_mygroup = cr->mpi_comm_mysim;
- #endif /* GMX_MPI */
+ {
- set_parallel_env(TRUE);
++ gmx_comm("(!PAR(cr) && (cr->sim_nodeid != 0))");
+ }
++
++ cr->mpi_comm_mysim = MPI_COMM_WORLD;
++ cr->mpi_comm_mygroup = cr->mpi_comm_mysim;
++#else
++ /* These should never be accessed */
++ cr->mpi_comm_mysim = NULL;
++ cr->mpi_comm_mygroup = NULL;
++ cr->nnodes = 1;
++ cr->sim_nodeid = 0;
++#endif
++
+ cr->nodeid = cr->sim_nodeid;
+
+ cr->duty = (DUTY_PP | DUTY_PME);
+
+ /* Communicate arguments if parallel */
+#ifndef GMX_THREAD_MPI
+ if (PAR(cr))
++ {
+ comm_args(cr,argc,argv_ptr);
++ }
+#endif /* GMX_THREAD_MPI */
+
+#ifdef GMX_MPI
+#if !defined(GMX_THREAD_MPI) && !defined(MPI_IN_PLACE_EXISTS)
+ /* initialize the MPI_IN_PLACE replacement buffers */
+ snew(cr->mpb, 1);
+ cr->mpb->ibuf=NULL;
+ cr->mpb->libuf=NULL;
+ cr->mpb->fbuf=NULL;
+ cr->mpb->dbuf=NULL;
+ cr->mpb->ibuf_alloc=0;
+ cr->mpb->libuf_alloc=0;
+ cr->mpb->fbuf_alloc=0;
+ cr->mpb->dbuf_alloc=0;
+#endif
+#endif
+
+ return cr;
+}
+
+t_commrec *init_par_threads(const t_commrec *cro)
+{
+#ifdef GMX_THREAD_MPI
+ int initialized;
+ t_commrec *cr;
+
+ /* make a thread-specific commrec */
+ snew(cr,1);
+ /* now copy the whole thing, so settings like the number of PME nodes
+ get propagated. */
+ *cr=*cro;
+
+ /* and we start setting our own thread-specific values for things */
+ MPI_Initialized(&initialized);
+ if (!initialized)
++ {
+ gmx_comm("Initializing threads without comm");
-
-
- t_commrec *init_cr_nopar(void)
- {
- t_commrec *cr;
-
- snew(cr,1);
-
- cr->nnodes = 1;
- /* cr->nthreads = 1; */
- cr->sim_nodeid = 0;
- cr->nodeid = 0;
- /* cr->threadid = 0; */
- cr->duty = (DUTY_PP | DUTY_PME);
-
- return cr;
- }
++ }
+ /* once threads will be used together with MPI, we'll
+ fill the cr structure with distinct data here. This might even work: */
+ cr->sim_nodeid = gmx_setup(0,NULL, &cr->nnodes);
+
+ cr->mpi_comm_mysim = MPI_COMM_WORLD;
+ cr->mpi_comm_mygroup = cr->mpi_comm_mysim;
+ cr->nodeid = cr->sim_nodeid;
+ cr->duty = (DUTY_PP | DUTY_PME);
+
+ return cr;
+#else
+ return NULL;
+#endif
+}
--- /dev/null
- void gmx_finalize(void)
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROningen Mixture of Alchemy and Childrens' Stories
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include "gmx_fatal.h"
+#include "main.h"
+#include "smalloc.h"
+#include "network.h"
+#include "copyrite.h"
+#include "statutil.h"
+#include <ctype.h>
+#include "macros.h"
+
+#ifdef GMX_LIB_MPI
+#include <mpi.h>
+#endif
+
+#ifdef GMX_THREAD_MPI
+#include "tmpi.h"
+#endif
+
+
+/* The source code in this file should be thread-safe.
+ Please keep it that way. */
+
+gmx_bool gmx_mpi_initialized(void)
+{
+ int n;
+#ifndef GMX_MPI
+ return 0;
+#else
+ MPI_Initialized(&n);
+
+ return n;
+#endif
+}
+
+int gmx_setup(int *argc,char **argv,int *nnodes)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_setup");
+ return 0;
+#else
+ char buf[256];
+ int resultlen; /* actual length of node name */
+ int i,flag;
+ int mpi_num_nodes;
+ int mpi_my_rank;
+ char mpi_hostname[MPI_MAX_PROCESSOR_NAME];
+
+ /* Call the MPI routines */
+#ifdef GMX_LIB_MPI
+#ifdef GMX_FAHCORE
+ (void) fah_MPI_Init(argc,&argv);
+#else
+ (void) MPI_Init(argc,&argv);
+#endif
+#endif
+ (void) MPI_Comm_size( MPI_COMM_WORLD, &mpi_num_nodes );
+ (void) MPI_Comm_rank( MPI_COMM_WORLD, &mpi_my_rank );
+ (void) MPI_Get_processor_name( mpi_hostname, &resultlen );
+
+#ifdef GMX_LIB_MPI
+ fprintf(stderr,"NNODES=%d, MYRANK=%d, HOSTNAME=%s\n",
+ mpi_num_nodes,mpi_my_rank,mpi_hostname);
+#endif
+
+ *nnodes=mpi_num_nodes;
+
+ return mpi_my_rank;
+#endif
+}
+
+int gmx_node_num(void)
+{
+#ifndef GMX_MPI
+ return 1;
+#else
+ int i;
+ (void) MPI_Comm_size(MPI_COMM_WORLD, &i);
+ return i;
+#endif
+}
+
+int gmx_node_rank(void)
+{
+#ifndef GMX_MPI
+ return 0;
+#else
+ int i;
+ (void) MPI_Comm_rank(MPI_COMM_WORLD, &i);
+ return i;
+#endif
+}
+
+
+int gmx_hostname_num()
+{
+#ifndef GMX_MPI
+ return 0;
+#else
+ int resultlen,hostnum,i,j;
+ char mpi_hostname[MPI_MAX_PROCESSOR_NAME],hostnum_str[MPI_MAX_PROCESSOR_NAME];
+
+ MPI_Get_processor_name(mpi_hostname,&resultlen);
+ /* This procedure can only differentiate nodes with host names
+ * that end on unique numbers.
+ */
+ i = 0;
+ j = 0;
+ /* Only parse the host name up to the first dot */
+ while(i < resultlen && mpi_hostname[i] != '.') {
+ if (isdigit(mpi_hostname[i])) {
+ hostnum_str[j++] = mpi_hostname[i];
+ }
+ i++;
+ }
+ hostnum_str[j] = '\0';
+ if (j == 0) {
+ hostnum = 0;
+ } else {
+ /* Use only the last 9 decimals, so we don't overflow an int */
+ hostnum = strtol(hostnum_str + max(0,j-9), NULL, 10);
+ }
+
+ if (debug) {
+ fprintf(debug,"In gmx_setup_nodecomm: hostname '%s', hostnum %d\n",
+ mpi_hostname,hostnum);
+ }
+ return hostnum;
+#endif
+}
+
+void gmx_setup_nodecomm(FILE *fplog,t_commrec *cr)
+{
+ gmx_nodecomm_t *nc;
+ int n,rank,hostnum,ng,ni;
+
+ /* Many MPI implementations do not optimize MPI_Allreduce
+ * (and probably also other global communication calls)
+ * for multi-core nodes connected by a network.
+ * We can optimize such communication by using one MPI call
+ * within each node and one between the nodes.
+ * For MVAPICH2 and Intel MPI this reduces the time for
+ * the global_stat communication by 25%
+ * for 2x2-core 3 GHz Woodcrest connected by mixed DDR/SDR Infiniband.
+ * B. Hess, November 2007
+ */
+
+ nc = &cr->nc;
+
+ nc->bUse = FALSE;
+#ifndef GMX_THREAD_MPI
+ if (getenv("GMX_NO_NODECOMM") == NULL) {
+#ifdef GMX_MPI
+ MPI_Comm_size(cr->mpi_comm_mygroup,&n);
+ MPI_Comm_rank(cr->mpi_comm_mygroup,&rank);
+
+ hostnum = gmx_hostname_num();
+
+ if (debug) {
+ fprintf(debug,
+ "In gmx_setup_nodecomm: splitting communicator of size %d\n",
+ n);
+ }
+
+
+ /* The intra-node communicator, split on node number */
+ MPI_Comm_split(cr->mpi_comm_mygroup,hostnum,rank,&nc->comm_intra);
+ MPI_Comm_rank(nc->comm_intra,&nc->rank_intra);
+ if (debug) {
+ fprintf(debug,"In gmx_setup_nodecomm: node rank %d rank_intra %d\n",
+ rank,nc->rank_intra);
+ }
+ /* The inter-node communicator, split on rank_intra.
+ * We actually only need the one for rank=0,
+ * but it is easier to create them all.
+ */
+ MPI_Comm_split(cr->mpi_comm_mygroup,nc->rank_intra,rank,&nc->comm_inter);
+ /* Check if this really created two step communication */
+ MPI_Comm_size(nc->comm_inter,&ng);
+ MPI_Comm_size(nc->comm_intra,&ni);
+ if (debug) {
+ fprintf(debug,"In gmx_setup_nodecomm: groups %d, my group size %d\n",
+ ng,ni);
+ }
+ if ((ng > 1 && ng < n) || (ni > 1 && ni < n)) {
+ nc->bUse = TRUE;
+ if (fplog)
+ fprintf(fplog,"Using two step summing over %d groups of on average %.1f processes\n\n",ng,(real)n/(real)ng);
+ if (nc->rank_intra > 0)
+ MPI_Comm_free(&nc->comm_inter);
+ } else {
+ /* One group or all processes in a separate group, use normal summing */
+ MPI_Comm_free(&nc->comm_inter);
+ MPI_Comm_free(&nc->comm_intra);
+ }
+#endif
+ }
+#endif
+}
+
+void gmx_barrier(const t_commrec *cr)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_barrier");
+#else
+ MPI_Barrier(cr->mpi_comm_mygroup);
+#endif
+}
+
+void gmx_abort(int noderank,int nnodes,int errorno)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_abort");
+#else
+#ifdef GMX_THREAD_MPI
+ fprintf(stderr,"Halting program %s\n",ShortProgram());
+ thanx(stderr);
+ exit(1);
+#else
+ if (nnodes > 1)
+ {
+ fprintf(stderr,"Halting parallel program %s on CPU %d out of %d\n",
+ ShortProgram(),noderank,nnodes);
+ }
+ else
+ {
+ fprintf(stderr,"Halting program %s\n",ShortProgram());
+ }
+
+ thanx(stderr);
+ MPI_Abort(MPI_COMM_WORLD,errorno);
+ exit(1);
+#endif
+#endif
+}
+
+void gmx_bcast(int nbytes,void *b,const t_commrec *cr)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_bast");
+#else
+ MPI_Bcast(b,nbytes,MPI_BYTE,MASTERRANK(cr),cr->mpi_comm_mygroup);
+#endif
+}
+
+void gmx_bcast_sim(int nbytes,void *b,const t_commrec *cr)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_bast");
+#else
+ MPI_Bcast(b,nbytes,MPI_BYTE,MASTERRANK(cr),cr->mpi_comm_mysim);
+#endif
+}
+
+void gmx_sumd(int nr,double r[],const t_commrec *cr)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_sumd");
+#else
+#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREAD_MPI)
+ if (cr->nc.bUse) {
+ if (cr->nc.rank_intra == 0)
+ {
+ /* Use two step summing. */
+ MPI_Reduce(MPI_IN_PLACE,r,nr,MPI_DOUBLE,MPI_SUM,0,
+ cr->nc.comm_intra);
+ /* Sum the roots of the internal (intra) buffers. */
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_DOUBLE,MPI_SUM,
+ cr->nc.comm_inter);
+ }
+ else
+ {
+ /* This is here because of the silly MPI specification
+ that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
+ MPI_Reduce(r,NULL,nr,MPI_DOUBLE,MPI_SUM,0,cr->nc.comm_intra);
+ }
+ MPI_Bcast(r,nr,MPI_DOUBLE,0,cr->nc.comm_intra);
+ }
+ else
+ {
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_DOUBLE,MPI_SUM,
+ cr->mpi_comm_mygroup);
+ }
+#else
+ int i;
+
+ if (nr > cr->mpb->dbuf_alloc) {
+ cr->mpb->dbuf_alloc = nr;
+ srenew(cr->mpb->dbuf,cr->mpb->dbuf_alloc);
+ }
+ if (cr->nc.bUse) {
+ /* Use two step summing */
+ MPI_Allreduce(r,cr->mpb->dbuf,nr,MPI_DOUBLE,MPI_SUM,cr->nc.comm_intra);
+ if (cr->nc.rank_intra == 0) {
+ /* Sum with the buffers reversed */
+ MPI_Allreduce(cr->mpb->dbuf,r,nr,MPI_DOUBLE,MPI_SUM,
+ cr->nc.comm_inter);
+ }
+ MPI_Bcast(r,nr,MPI_DOUBLE,0,cr->nc.comm_intra);
+ } else {
+ MPI_Allreduce(r,cr->mpb->dbuf,nr,MPI_DOUBLE,MPI_SUM,
+ cr->mpi_comm_mygroup);
+ for(i=0; i<nr; i++)
+ r[i] = cr->mpb->dbuf[i];
+ }
+#endif
+#endif
+}
+
+void gmx_sumf(int nr,float r[],const t_commrec *cr)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_sumf");
+#else
+#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREAD_MPI)
+ if (cr->nc.bUse) {
+ /* Use two step summing. */
+ if (cr->nc.rank_intra == 0)
+ {
+ MPI_Reduce(MPI_IN_PLACE,r,nr,MPI_FLOAT,MPI_SUM,0,
+ cr->nc.comm_intra);
+ /* Sum the roots of the internal (intra) buffers */
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_FLOAT,MPI_SUM,
+ cr->nc.comm_inter);
+ }
+ else
+ {
+ /* This is here because of the silly MPI specification
+ that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
+ MPI_Reduce(r,NULL,nr,MPI_FLOAT,MPI_SUM,0,cr->nc.comm_intra);
+ }
+ MPI_Bcast(r,nr,MPI_FLOAT,0,cr->nc.comm_intra);
+ }
+ else
+ {
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_FLOAT,MPI_SUM,cr->mpi_comm_mygroup);
+ }
+#else
+ int i;
+
+ if (nr > cr->mpb->fbuf_alloc) {
+ cr->mpb->fbuf_alloc = nr;
+ srenew(cr->mpb->fbuf,cr->mpb->fbuf_alloc);
+ }
+ if (cr->nc.bUse) {
+ /* Use two step summing */
+ MPI_Allreduce(r,cr->mpb->fbuf,nr,MPI_FLOAT,MPI_SUM,cr->nc.comm_intra);
+ if (cr->nc.rank_intra == 0) {
+ /* Sum with the buffers reversed */
+ MPI_Allreduce(cr->mpb->fbuf,r,nr,MPI_FLOAT,MPI_SUM,
+ cr->nc.comm_inter);
+ }
+ MPI_Bcast(r,nr,MPI_FLOAT,0,cr->nc.comm_intra);
+ } else {
+ MPI_Allreduce(r,cr->mpb->fbuf,nr,MPI_FLOAT,MPI_SUM,
+ cr->mpi_comm_mygroup);
+ for(i=0; i<nr; i++)
+ r[i] = cr->mpb->fbuf[i];
+ }
+#endif
+#endif
+}
+
+void gmx_sumi(int nr,int r[],const t_commrec *cr)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_sumi");
+#else
+#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREAD_MPI)
+ if (cr->nc.bUse) {
+ /* Use two step summing */
+ if (cr->nc.rank_intra == 0)
+ {
+ MPI_Reduce(MPI_IN_PLACE,r,nr,MPI_INT,MPI_SUM,0,cr->nc.comm_intra);
+ /* Sum with the buffers reversed */
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_INT,MPI_SUM,cr->nc.comm_inter);
+ }
+ else
+ {
+ /* This is here because of the silly MPI specification
+ that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
+ MPI_Reduce(r,NULL,nr,MPI_INT,MPI_SUM,0,cr->nc.comm_intra);
+ }
+ MPI_Bcast(r,nr,MPI_INT,0,cr->nc.comm_intra);
+ }
+ else
+ {
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_INT,MPI_SUM,cr->mpi_comm_mygroup);
+ }
+#else
+ int i;
+
+ if (nr > cr->mpb->ibuf_alloc) {
+ cr->mpb->ibuf_alloc = nr;
+ srenew(cr->mpb->ibuf,cr->mpb->ibuf_alloc);
+ }
+ if (cr->nc.bUse) {
+ /* Use two step summing */
+ MPI_Allreduce(r,cr->mpb->ibuf,nr,MPI_INT,MPI_SUM,cr->nc.comm_intra);
+ if (cr->nc.rank_intra == 0) {
+ /* Sum with the buffers reversed */
+ MPI_Allreduce(cr->mpb->ibuf,r,nr,MPI_INT,MPI_SUM,cr->nc.comm_inter);
+ }
+ MPI_Bcast(r,nr,MPI_INT,0,cr->nc.comm_intra);
+ } else {
+ MPI_Allreduce(r,cr->mpb->ibuf,nr,MPI_INT,MPI_SUM,cr->mpi_comm_mygroup);
+ for(i=0; i<nr; i++)
+ r[i] = cr->mpb->ibuf[i];
+ }
+#endif
+#endif
+}
+
+void gmx_sumli(int nr,gmx_large_int_t r[],const t_commrec *cr)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_sumli");
+#else
+#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREAD_MPI)
+ if (cr->nc.bUse) {
+ /* Use two step summing */
+ if (cr->nc.rank_intra == 0)
+ {
+ MPI_Reduce(MPI_IN_PLACE,r,nr,GMX_MPI_LARGE_INT,MPI_SUM,0,
+ cr->nc.comm_intra);
+ /* Sum with the buffers reversed */
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,GMX_MPI_LARGE_INT,MPI_SUM,
+ cr->nc.comm_inter);
+ }
+ else
+ {
+ /* This is here because of the silly MPI specification
+ that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
+ MPI_Reduce(r,NULL,nr,GMX_MPI_LARGE_INT,MPI_SUM,0,cr->nc.comm_intra);
+ }
+ MPI_Bcast(r,nr,GMX_MPI_LARGE_INT,0,cr->nc.comm_intra);
+ }
+ else
+ {
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,GMX_MPI_LARGE_INT,MPI_SUM,cr->mpi_comm_mygroup);
+ }
+#else
+ int i;
+
+ if (nr > cr->mpb->libuf_alloc) {
+ cr->mpb->libuf_alloc = nr;
+ srenew(cr->mpb->libuf,cr->mpb->libuf_alloc);
+ }
+ if (cr->nc.bUse) {
+ /* Use two step summing */
+ MPI_Allreduce(r,cr->mpb->libuf,nr,GMX_MPI_LARGE_INT,MPI_SUM,
+ cr->nc.comm_intra);
+ if (cr->nc.rank_intra == 0) {
+ /* Sum with the buffers reversed */
+ MPI_Allreduce(cr->mpb->libuf,r,nr,GMX_MPI_LARGE_INT,MPI_SUM,
+ cr->nc.comm_inter);
+ }
+ MPI_Bcast(r,nr,GMX_MPI_LARGE_INT,0,cr->nc.comm_intra);
+ } else {
+ MPI_Allreduce(r,cr->mpb->libuf,nr,GMX_MPI_LARGE_INT,MPI_SUM,
+ cr->mpi_comm_mygroup);
+ for(i=0; i<nr; i++)
+ r[i] = cr->mpb->libuf[i];
+ }
+#endif
+#endif
+}
+
+
+
+#ifdef GMX_MPI
+void gmx_sumd_comm(int nr,double r[],MPI_Comm mpi_comm)
+{
+#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREAD_MPI)
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_DOUBLE,MPI_SUM,mpi_comm);
+#else
+ /* this function is only used in code that is not performance critical,
+ (during setup, when comm_rec is not the appropriate communication
+ structure), so this isn't as bad as it looks. */
+ double *buf;
+ int i;
+
+ snew(buf, nr);
+ MPI_Allreduce(r,buf,nr,MPI_DOUBLE,MPI_SUM,mpi_comm);
+ for(i=0; i<nr; i++)
+ r[i] = buf[i];
+ sfree(buf);
+#endif
+}
+#endif
+
+#ifdef GMX_MPI
+void gmx_sumf_comm(int nr,float r[],MPI_Comm mpi_comm)
+{
+#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREAD_MPI)
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_FLOAT,MPI_SUM,mpi_comm);
+#else
+ /* this function is only used in code that is not performance critical,
+ (during setup, when comm_rec is not the appropriate communication
+ structure), so this isn't as bad as it looks. */
+ float *buf;
+ int i;
+
+ snew(buf, nr);
+ MPI_Allreduce(r,buf,nr,MPI_FLOAT,MPI_SUM,mpi_comm);
+ for(i=0; i<nr; i++)
+ r[i] = buf[i];
+ sfree(buf);
+#endif
+}
+#endif
+
+void gmx_sumd_sim(int nr,double r[],const gmx_multisim_t *ms)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_sumd_sim");
+#else
+ gmx_sumd_comm(nr,r,ms->mpi_comm_masters);
+#endif
+}
+
+void gmx_sumf_sim(int nr,float r[],const gmx_multisim_t *ms)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_sumf_sim");
+#else
+ gmx_sumf_comm(nr,r,ms->mpi_comm_masters);
+#endif
+}
+
+void gmx_sumi_sim(int nr,int r[], const gmx_multisim_t *ms)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_sumi_sim");
+#else
+#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREAD_MPI)
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,MPI_INT,MPI_SUM,ms->mpi_comm_masters);
+#else
+ /* this is thread-unsafe, but it will do for now: */
+ int i;
+
+ if (nr > ms->mpb->ibuf_alloc) {
+ ms->mpb->ibuf_alloc = nr;
+ srenew(ms->mpb->ibuf,ms->mpb->ibuf_alloc);
+ }
+ MPI_Allreduce(r,ms->mpb->ibuf,nr,MPI_INT,MPI_SUM,ms->mpi_comm_masters);
+ for(i=0; i<nr; i++)
+ r[i] = ms->mpb->ibuf[i];
+#endif
+#endif
+}
+
+void gmx_sumli_sim(int nr,gmx_large_int_t r[], const gmx_multisim_t *ms)
+{
+#ifndef GMX_MPI
+ gmx_call("gmx_sumli_sim");
+#else
+#if defined(MPI_IN_PLACE_EXISTS) || defined(GMX_THREAD_MPI)
+ MPI_Allreduce(MPI_IN_PLACE,r,nr,GMX_MPI_LARGE_INT,MPI_SUM,
+ ms->mpi_comm_masters);
+#else
+ /* this is thread-unsafe, but it will do for now: */
+ int i;
+
+ if (nr > ms->mpb->libuf_alloc) {
+ ms->mpb->libuf_alloc = nr;
+ srenew(ms->mpb->libuf,ms->mpb->libuf_alloc);
+ }
+ MPI_Allreduce(r,ms->mpb->libuf,nr,GMX_MPI_LARGE_INT,MPI_SUM,
+ ms->mpi_comm_masters);
+ for(i=0; i<nr; i++)
+ r[i] = ms->mpb->libuf[i];
+#endif
+#endif
+}
+
+
- gmx_call("gmx_finalize");
++void gmx_finalize_par(void)
+{
+#ifndef GMX_MPI
- int ret;
++ /* Compiled without MPI, no MPI finalizing needed */
++ return;
+#else
- /* just as a check; we don't want to finalize twice */
- int finalized;
- MPI_Finalized(&finalized);
- if (finalized)
- return;
++ int initialized,finalized;
++ int ret;
+
++ MPI_Initialized(&initialized);
++ if (!initialized)
++ {
++ return;
++ }
++ /* just as a check; we don't want to finalize twice */
++ MPI_Finalized(&finalized);
++ if (finalized)
++ {
++ return;
++ }
+
+ /* We sync the processes here to try to avoid problems
+ * with buggy MPI implementations that could cause
+ * unfinished processes to terminate.
+ */
+ MPI_Barrier(MPI_COMM_WORLD);
+
+ /*
+ if (DOMAINDECOMP(cr)) {
+ if (cr->npmenodes > 0 || cr->dd->bCartesian)
+ MPI_Comm_free(&cr->mpi_comm_mygroup);
+ if (cr->dd->bCartesian)
+ MPI_Comm_free(&cr->mpi_comm_mysim);
+ }
+ */
+
+ /* Apparently certain mpich implementations cause problems
+ * with MPI_Finalize. In that case comment out MPI_Finalize.
+ */
+ if (debug)
+ fprintf(debug,"Will call MPI_Finalize now\n");
+
+ ret = MPI_Finalize();
+ if (debug)
+ fprintf(debug,"Return code from MPI_Finalize = %d\n",ret);
+#endif
+}
+
--- /dev/null
- fprintf(fp,"bLimitDistance = %s\n",BOOL(pbc->bLimitDistance));
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROningen Mixture of Alchemy and Childrens' Stories
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include "sysstuff.h"
+#include "typedefs.h"
+#include "vec.h"
+#include "maths.h"
+#include "main.h"
+#include "pbc.h"
+#include "smalloc.h"
+#include "txtdump.h"
+#include "gmx_fatal.h"
+#include "names.h"
+#include "macros.h"
+
+/* Skip 0 so we have more chance of detecting if we forgot to call set_pbc. */
+enum { epbcdxRECTANGULAR=1, epbcdxTRICLINIC,
+ epbcdx2D_RECT, epbcdx2D_TRIC,
+ epbcdx1D_RECT, epbcdx1D_TRIC,
+ epbcdxSCREW_RECT, epbcdxSCREW_TRIC,
+ epbcdxNOPBC, epbcdxUNSUPPORTED };
+
+/* Margin factor for error message and correction if the box is too skewed */
+#define BOX_MARGIN 1.0010
+#define BOX_MARGIN_CORRECT 1.0005
+
+int ePBC2npbcdim(int ePBC)
+{
+ int npbcdim=0;
+
+ switch(ePBC) {
+ case epbcXYZ: npbcdim = 3; break;
+ case epbcXY: npbcdim = 2; break;
+ case epbcSCREW: npbcdim = 3; break;
+ case epbcNONE: npbcdim = 0; break;
+ default: gmx_fatal(FARGS,"Unknown ePBC=%d in ePBC2npbcdim",ePBC);
+ }
+
+ return npbcdim;
+}
+
+int inputrec2nboundeddim(t_inputrec *ir)
+{
+ if (ir->nwall == 2 && ir->ePBC == epbcXY)
+ {
+ return 3;
+ }
+ else
+ {
+ return ePBC2npbcdim(ir->ePBC);
+ }
+}
+
+void dump_pbc(FILE *fp,t_pbc *pbc)
+{
+ rvec sum_box;
+
+ fprintf(fp,"ePBCDX = %d\n",pbc->ePBCDX);
+ pr_rvecs(fp,0,"box",pbc->box,DIM);
+ pr_rvecs(fp,0,"fbox_diag",&pbc->fbox_diag,1);
+ pr_rvecs(fp,0,"hbox_diag",&pbc->hbox_diag,1);
+ pr_rvecs(fp,0,"mhbox_diag",&pbc->mhbox_diag,1);
+ rvec_add(pbc->hbox_diag,pbc->mhbox_diag,sum_box);
+ pr_rvecs(fp,0,"sum of the above two",&sum_box,1);
+ fprintf(fp,"max_cutoff2 = %g\n",pbc->max_cutoff2);
++ fprintf(fp,"bLimitDistance = %s\n",EBOOL(pbc->bLimitDistance));
+ fprintf(fp,"limit_distance2 = %g\n",pbc->limit_distance2);
+ fprintf(fp,"ntric_vec = %d\n",pbc->ntric_vec);
+ if (pbc->ntric_vec > 0) {
+ pr_ivecs(fp,0,"tric_shift",pbc->tric_shift,pbc->ntric_vec,FALSE);
+ pr_rvecs(fp,0,"tric_vec",pbc->tric_vec,pbc->ntric_vec);
+ }
+}
+
+const char *check_box(int ePBC,matrix box)
+{
+ const char *ptr;
+
+ if (ePBC == -1)
+ ePBC = guess_ePBC(box);
+
+ if (ePBC == epbcNONE)
+ return NULL;
+
+ if ((box[XX][YY] != 0) || (box[XX][ZZ] != 0) || (box[YY][ZZ] != 0)) {
+ ptr = "Only triclinic boxes with the first vector parallel to the x-axis and the second vector in the xy-plane are supported.";
+ } else if (ePBC == epbcSCREW && (box[YY][XX] != 0 || box[ZZ][XX] != 0)) {
+ ptr = "The unit cell can not have off-diagonal x-components with screw pbc";
+ } else if (fabs(box[YY][XX]) > BOX_MARGIN*0.5*box[XX][XX] ||
+ (ePBC != epbcXY &&
+ (fabs(box[ZZ][XX]) > BOX_MARGIN*0.5*box[XX][XX] ||
+ fabs(box[ZZ][YY]) > BOX_MARGIN*0.5*box[YY][YY]))) {
+ ptr = "Triclinic box is too skewed.";
+ } else {
+ ptr = NULL;
+ }
+
+ return ptr;
+}
+
+real max_cutoff2(int ePBC,matrix box)
+{
+ real min_hv2,min_ss;
+
+ /* Physical limitation of the cut-off
+ * by half the length of the shortest box vector.
+ */
+ min_hv2 = min(0.25*norm2(box[XX]),0.25*norm2(box[YY]));
+ if (ePBC != epbcXY)
+ min_hv2 = min(min_hv2,0.25*norm2(box[ZZ]));
+
+ /* Limitation to the smallest diagonal element due to optimizations:
+ * checking only linear combinations of single box-vectors (2 in x)
+ * in the grid search and pbc_dx is a lot faster
+ * than checking all possible combinations.
+ */
+ if (ePBC == epbcXY) {
+ min_ss = min(box[XX][XX],box[YY][YY]);
+ } else {
+ min_ss = min(box[XX][XX],min(box[YY][YY]-fabs(box[ZZ][YY]),box[ZZ][ZZ]));
+ }
+
+ return min(min_hv2,min_ss*min_ss);
+}
+
+/* this one is mostly harmless... */
+static gmx_bool bWarnedGuess=FALSE;
+
+int guess_ePBC(matrix box)
+{
+ int ePBC;
+
+ if (box[XX][XX]>0 && box[YY][YY]>0 && box[ZZ][ZZ]>0) {
+ ePBC = epbcXYZ;
+ } else if (box[XX][XX]>0 && box[YY][YY]>0 && box[ZZ][ZZ]==0) {
+ ePBC = epbcXY;
+ } else if (box[XX][XX]==0 && box[YY][YY]==0 && box[ZZ][ZZ]==0) {
+ ePBC = epbcNONE;
+ } else {
+ if (!bWarnedGuess) {
+ fprintf(stderr,"WARNING: Unsupported box diagonal %f %f %f, "
+ "will not use periodic boundary conditions\n\n",
+ box[XX][XX],box[YY][YY],box[ZZ][ZZ]);
+ bWarnedGuess = TRUE;
+ }
+ ePBC = epbcNONE;
+ }
+
+ if (debug)
+ fprintf(debug,"Guessed pbc = %s from the box matrix\n",epbc_names[ePBC]);
+
+ return ePBC;
+}
+
+static int correct_box_elem(FILE *fplog,int step,tensor box,int v,int d)
+{
+ int shift,maxshift=10;
+
+ shift = 0;
+
+ /* correct elem d of vector v with vector d */
+ while (box[v][d] > BOX_MARGIN_CORRECT*0.5*box[d][d]) {
+ if (fplog) {
+ fprintf(fplog,"Step %d: correcting invalid box:\n",step);
+ pr_rvecs(fplog,0,"old box",box,DIM);
+ }
+ rvec_dec(box[v],box[d]);
+ shift--;
+ if (fplog) {
+ pr_rvecs(fplog,0,"new box",box,DIM);
+ }
+ if (shift <= -maxshift)
+ gmx_fatal(FARGS,
+ "Box was shifted at least %d times. Please see log-file.",
+ maxshift);
+ }
+ while (box[v][d] < -BOX_MARGIN_CORRECT*0.5*box[d][d]) {
+ if (fplog) {
+ fprintf(fplog,"Step %d: correcting invalid box:\n",step);
+ pr_rvecs(fplog,0,"old box",box,DIM);
+ }
+ rvec_inc(box[v],box[d]);
+ shift++;
+ if (fplog) {
+ pr_rvecs(fplog,0,"new box",box,DIM);
+ }
+ if (shift >= maxshift)
+ gmx_fatal(FARGS,
+ "Box was shifted at least %d times. Please see log-file.",
+ maxshift);
+ }
+
+ return shift;
+}
+
+gmx_bool correct_box(FILE *fplog,int step,tensor box,t_graph *graph)
+{
+ int zy,zx,yx,i;
+ gmx_bool bCorrected;
+
+ /* check if the box still obeys the restrictions, if not, correct it */
+ zy = correct_box_elem(fplog,step,box,ZZ,YY);
+ zx = correct_box_elem(fplog,step,box,ZZ,XX);
+ yx = correct_box_elem(fplog,step,box,YY,XX);
+
+ bCorrected = (zy || zx || yx);
+
+ if (bCorrected && graph) {
+ /* correct the graph */
+ for(i=graph->at_start; i<graph->at_end; i++) {
+ graph->ishift[i][YY] -= graph->ishift[i][ZZ]*zy;
+ graph->ishift[i][XX] -= graph->ishift[i][ZZ]*zx;
+ graph->ishift[i][XX] -= graph->ishift[i][YY]*yx;
+ }
+ }
+
+ return bCorrected;
+}
+
+int ndof_com(t_inputrec *ir)
+{
+ int n=0;
+
+ switch (ir->ePBC) {
+ case epbcXYZ:
+ case epbcNONE:
+ n = 3;
+ break;
+ case epbcXY:
+ n = (ir->nwall == 0 ? 3 : 2);
+ break;
+ case epbcSCREW:
+ n = 1;
+ break;
+ default:
+ gmx_incons("Unknown pbc in calc_nrdf");
+ }
+
+ return n;
+}
+
+static void low_set_pbc(t_pbc *pbc,int ePBC,ivec *dd_nc,matrix box)
+{
+ int order[5]={0,-1,1,-2,2};
+ int ii,jj,kk,i,j,k,d,dd,jc,kc,npbcdim,shift;
+ ivec bPBC;
+ real d2old,d2new,d2new_c;
+ rvec trial,pos;
+ gmx_bool bXY,bUse;
+ const char *ptr;
+
+ pbc->ndim_ePBC = ePBC2npbcdim(ePBC);
+
+ copy_mat(box,pbc->box);
+ pbc->bLimitDistance = FALSE;
+ pbc->max_cutoff2 = 0;
+ pbc->dim = -1;
+
+ for(i=0; (i<DIM); i++) {
+ pbc->fbox_diag[i] = box[i][i];
+ pbc->hbox_diag[i] = pbc->fbox_diag[i]*0.5;
+ pbc->mhbox_diag[i] = -pbc->hbox_diag[i];
+ }
+
+ ptr = check_box(ePBC,box);
+ if (ePBC == epbcNONE) {
+ pbc->ePBCDX = epbcdxNOPBC;
+ } else if (ptr) {
+ fprintf(stderr, "Warning: %s\n",ptr);
+ pr_rvecs(stderr,0," Box",box,DIM);
+ fprintf(stderr, " Can not fix pbc.\n");
+ pbc->ePBCDX = epbcdxUNSUPPORTED;
+ pbc->bLimitDistance = TRUE;
+ pbc->limit_distance2 = 0;
+ } else {
+ if (ePBC == epbcSCREW && dd_nc) {
+ /* This combinated should never appear here */
+ gmx_incons("low_set_pbc called with screw pbc and dd_nc != NULL");
+ }
+
+ npbcdim = 0;
+ for(i=0; i<DIM; i++) {
+ if ((dd_nc && (*dd_nc)[i] > 1) || (ePBC == epbcXY && i == ZZ)) {
+ bPBC[i] = 0;
+ } else {
+ bPBC[i] = 1;
+ npbcdim++;
+ }
+ }
+ switch (npbcdim) {
+ case 1:
+ /* 1D pbc is not an mdp option and it is therefore only used
+ * with single shifts.
+ */
+ pbc->ePBCDX = epbcdx1D_RECT;
+ for(i=0; i<DIM; i++)
+ if (bPBC[i])
+ pbc->dim = i;
+ for(i=0; i<pbc->dim; i++)
+ if (pbc->box[pbc->dim][i] != 0)
+ pbc->ePBCDX = epbcdx1D_TRIC;
+ break;
+ case 2:
+ pbc->ePBCDX = epbcdx2D_RECT;
+ for(i=0; i<DIM; i++)
+ if (!bPBC[i])
+ pbc->dim = i;
+ for(i=0; i<DIM; i++)
+ if (bPBC[i])
+ for(j=0; j<i; j++)
+ if (pbc->box[i][j] != 0)
+ pbc->ePBCDX = epbcdx2D_TRIC;
+ break;
+ case 3:
+ if (ePBC != epbcSCREW) {
+ if (TRICLINIC(box)) {
+ pbc->ePBCDX = epbcdxTRICLINIC;
+ } else {
+ pbc->ePBCDX = epbcdxRECTANGULAR;
+ }
+ } else {
+ pbc->ePBCDX = (box[ZZ][YY]==0 ? epbcdxSCREW_RECT : epbcdxSCREW_TRIC);
+ if (pbc->ePBCDX == epbcdxSCREW_TRIC) {
+ fprintf(stderr,
+ "Screw pbc is not yet implemented for triclinic boxes.\n"
+ "Can not fix pbc.\n");
+ pbc->ePBCDX = epbcdxUNSUPPORTED;
+ }
+ }
+ break;
+ default:
+ gmx_fatal(FARGS,"Incorrect number of pbc dimensions with DD: %d",
+ npbcdim);
+ }
+ pbc->max_cutoff2 = max_cutoff2(ePBC,box);
+
+ if (pbc->ePBCDX == epbcdxTRICLINIC ||
+ pbc->ePBCDX == epbcdx2D_TRIC ||
+ pbc->ePBCDX == epbcdxSCREW_TRIC) {
+ if (debug) {
+ pr_rvecs(debug,0,"Box",box,DIM);
+ fprintf(debug,"max cutoff %.3f\n",sqrt(pbc->max_cutoff2));
+ }
+ pbc->ntric_vec = 0;
+ /* We will only use single shifts, but we will check a few
+ * more shifts to see if there is a limiting distance
+ * above which we can not be sure of the correct distance.
+ */
+ for(kk=0; kk<5; kk++) {
+ k = order[kk];
+ if (!bPBC[ZZ] && k != 0)
+ continue;
+ for(jj=0; jj<5; jj++) {
+ j = order[jj];
+ if (!bPBC[YY] && j != 0)
+ continue;
+ for(ii=0; ii<3; ii++) {
+ i = order[ii];
+ if (!bPBC[XX] && i != 0)
+ continue;
+ /* A shift is only useful when it is trilinic */
+ if (j != 0 || k != 0) {
+ d2old = 0;
+ d2new = 0;
+ for(d=0; d<DIM; d++) {
+ trial[d] = i*box[XX][d] + j*box[YY][d] + k*box[ZZ][d];
+ /* Choose the vector within the brick around 0,0,0 that
+ * will become the shortest due to shift try.
+ */
+ if (d == pbc->dim) {
+ trial[d] = 0;
+ pos[d] = 0;
+ } else {
+ if (trial[d] < 0)
+ pos[d] = min( pbc->hbox_diag[d],-trial[d]);
+ else
+ pos[d] = max(-pbc->hbox_diag[d],-trial[d]);
+ }
+ d2old += sqr(pos[d]);
+ d2new += sqr(pos[d] + trial[d]);
+ }
+ if (BOX_MARGIN*d2new < d2old) {
+ if (j < -1 || j > 1 || k < -1 || k > 1) {
+ /* Check if there is a single shift vector
+ * that decreases this distance even more.
+ */
+ jc = 0;
+ kc = 0;
+ if (j < -1 || j > 1)
+ jc = j/2;
+ if (k < -1 || k > 1)
+ kc = k/2;
+ d2new_c = 0;
+ for(d=0; d<DIM; d++)
+ d2new_c += sqr(pos[d] + trial[d]
+ - jc*box[YY][d] - kc*box[ZZ][d]);
+ if (d2new_c > BOX_MARGIN*d2new) {
+ /* Reject this shift vector, as there is no a priori limit
+ * to the number of shifts that decrease distances.
+ */
+ if (!pbc->bLimitDistance || d2new < pbc->limit_distance2)
+ pbc->limit_distance2 = d2new;
+ pbc->bLimitDistance = TRUE;
+ }
+ } else {
+ /* Check if shifts with one box vector less do better */
+ bUse = TRUE;
+ for(dd=0; dd<DIM; dd++) {
+ shift = (dd==0 ? i : (dd==1 ? j : k));
+ if (shift) {
+ d2new_c = 0;
+ for(d=0; d<DIM; d++)
+ d2new_c += sqr(pos[d] + trial[d] - shift*box[dd][d]);
+ if (d2new_c <= BOX_MARGIN*d2new)
+ bUse = FALSE;
+ }
+ }
+ if (bUse) {
+ /* Accept this shift vector. */
+ if (pbc->ntric_vec >= MAX_NTRICVEC) {
+ fprintf(stderr,"\nWARNING: Found more than %d triclinic correction vectors, ignoring some.\n"
+ " There is probably something wrong with your box.\n",MAX_NTRICVEC);
+ pr_rvecs(stderr,0," Box",box,DIM);
+ } else {
+ copy_rvec(trial,pbc->tric_vec[pbc->ntric_vec]);
+ pbc->tric_shift[pbc->ntric_vec][XX] = i;
+ pbc->tric_shift[pbc->ntric_vec][YY] = j;
+ pbc->tric_shift[pbc->ntric_vec][ZZ] = k;
+ pbc->ntric_vec++;
+ }
+ }
+ }
+ if (debug) {
+ fprintf(debug," tricvec %2d = %2d %2d %2d %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\n",
+ pbc->ntric_vec,i,j,k,
+ sqrt(d2old),sqrt(d2new),
+ trial[XX],trial[YY],trial[ZZ],
+ pos[XX],pos[YY],pos[ZZ]);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void set_pbc(t_pbc *pbc,int ePBC,matrix box)
+{
+ if (ePBC == -1)
+ ePBC = guess_ePBC(box);
+
+ low_set_pbc(pbc,ePBC,NULL,box);
+}
+
+t_pbc *set_pbc_dd(t_pbc *pbc,int ePBC,
+ gmx_domdec_t *dd,gmx_bool bSingleDir,matrix box)
+{
+ ivec nc2;
+ int npbcdim,i;
+
+ if (dd == NULL) {
+ npbcdim = DIM;
+ } else {
+ if (ePBC == epbcSCREW && dd->nc[XX] > 1) {
+ /* The rotation has been taken care of during coordinate communication */
+ ePBC = epbcXYZ;
+ }
+ npbcdim = 0;
+ for(i=0; i<DIM; i++) {
+ if (dd->nc[i] <= (bSingleDir ? 1 : 2)) {
+ nc2[i] = 1;
+ if (!(ePBC == epbcXY && i == ZZ))
+ npbcdim++;
+ } else {
+ nc2[i] = dd->nc[i];
+ }
+ }
+ }
+
+ if (npbcdim > 0)
+ low_set_pbc(pbc,ePBC,npbcdim<DIM ? &nc2 : NULL,box);
+
+ return (npbcdim > 0 ? pbc : NULL);
+}
+
+void pbc_dx(const t_pbc *pbc,const rvec x1, const rvec x2, rvec dx)
+{
+ int i,j;
+ rvec dx_start,trial;
+ real d2min,d2trial;
+ gmx_bool bRot;
+
+ rvec_sub(x1,x2,dx);
+
+ switch (pbc->ePBCDX) {
+ case epbcdxRECTANGULAR:
+ for(i=0; i<DIM; i++) {
+ while (dx[i] > pbc->hbox_diag[i]) {
+ dx[i] -= pbc->fbox_diag[i];
+ }
+ while (dx[i] <= pbc->mhbox_diag[i]) {
+ dx[i] += pbc->fbox_diag[i];
+ }
+ }
+ break;
+ case epbcdxTRICLINIC:
+ for(i=DIM-1; i>=0; i--) {
+ while (dx[i] > pbc->hbox_diag[i]) {
+ for (j=i; j>=0; j--)
+ dx[j] -= pbc->box[i][j];
+ }
+ while (dx[i] <= pbc->mhbox_diag[i]) {
+ for (j=i; j>=0; j--)
+ dx[j] += pbc->box[i][j];
+ }
+ }
+ /* dx is the distance in a rectangular box */
+ d2min = norm2(dx);
+ if (d2min > pbc->max_cutoff2) {
+ copy_rvec(dx,dx_start);
+ d2min = norm2(dx);
+ /* Now try all possible shifts, when the distance is within max_cutoff
+ * it must be the shortest possible distance.
+ */
+ i = 0;
+ while ((d2min > pbc->max_cutoff2) && (i < pbc->ntric_vec)) {
+ rvec_add(dx_start,pbc->tric_vec[i],trial);
+ d2trial = norm2(trial);
+ if (d2trial < d2min) {
+ copy_rvec(trial,dx);
+ d2min = d2trial;
+ }
+ i++;
+ }
+ }
+ break;
+ case epbcdx2D_RECT:
+ for(i=0; i<DIM; i++) {
+ if (i != pbc->dim) {
+ while (dx[i] > pbc->hbox_diag[i]) {
+ dx[i] -= pbc->fbox_diag[i];
+ }
+ while (dx[i] <= pbc->mhbox_diag[i]) {
+ dx[i] += pbc->fbox_diag[i];
+ }
+ }
+ }
+ break;
+ case epbcdx2D_TRIC:
+ d2min = 0;
+ for(i=DIM-1; i>=0; i--) {
+ if (i != pbc->dim) {
+ while (dx[i] > pbc->hbox_diag[i]) {
+ for (j=i; j>=0; j--)
+ dx[j] -= pbc->box[i][j];
+ }
+ while (dx[i] <= pbc->mhbox_diag[i]) {
+ for (j=i; j>=0; j--)
+ dx[j] += pbc->box[i][j];
+ }
+ d2min += dx[i]*dx[i];
+ }
+ }
+ if (d2min > pbc->max_cutoff2) {
+ copy_rvec(dx,dx_start);
+ d2min = norm2(dx);
+ /* Now try all possible shifts, when the distance is within max_cutoff
+ * it must be the shortest possible distance.
+ */
+ i = 0;
+ while ((d2min > pbc->max_cutoff2) && (i < pbc->ntric_vec)) {
+ rvec_add(dx_start,pbc->tric_vec[i],trial);
+ d2trial = 0;
+ for(j=0; j<DIM; j++) {
+ if (j != pbc->dim) {
+ d2trial += trial[j]*trial[j];
+ }
+ }
+ if (d2trial < d2min) {
+ copy_rvec(trial,dx);
+ d2min = d2trial;
+ }
+ i++;
+ }
+ }
+ break;
+ case epbcdxSCREW_RECT:
+ /* The shift definition requires x first */
+ bRot = FALSE;
+ while (dx[XX] > pbc->hbox_diag[XX]) {
+ dx[XX] -= pbc->fbox_diag[XX];
+ bRot = !bRot;
+ }
+ while (dx[XX] <= pbc->mhbox_diag[XX]) {
+ dx[XX] += pbc->fbox_diag[YY];
+ bRot = !bRot;
+ }
+ if (bRot) {
+ /* Rotate around the x-axis in the middle of the box */
+ dx[YY] = pbc->box[YY][YY] - x1[YY] - x2[YY];
+ dx[ZZ] = pbc->box[ZZ][ZZ] - x1[ZZ] - x2[ZZ];
+ }
+ /* Normal pbc for y and z */
+ for(i=YY; i<=ZZ; i++) {
+ while (dx[i] > pbc->hbox_diag[i]) {
+ dx[i] -= pbc->fbox_diag[i];
+ }
+ while (dx[i] <= pbc->mhbox_diag[i]) {
+ dx[i] += pbc->fbox_diag[i];
+ }
+ }
+ break;
+ case epbcdxNOPBC:
+ case epbcdxUNSUPPORTED:
+ break;
+ default:
+ gmx_fatal(FARGS,"Internal error in pbc_dx, set_pbc has not been called");
+ break;
+ }
+}
+
+int pbc_dx_aiuc(const t_pbc *pbc,const rvec x1, const rvec x2, rvec dx)
+{
+ int i,j,is;
+ rvec dx_start,trial;
+ real d2min,d2trial;
+ ivec ishift,ishift_start;
+
+ rvec_sub(x1,x2,dx);
+ clear_ivec(ishift);
+
+ switch (pbc->ePBCDX) {
+ case epbcdxRECTANGULAR:
+ for(i=0; i<DIM; i++) {
+ if (dx[i] > pbc->hbox_diag[i]) {
+ dx[i] -= pbc->fbox_diag[i];
+ ishift[i]--;
+ } else if (dx[i] <= pbc->mhbox_diag[i]) {
+ dx[i] += pbc->fbox_diag[i];
+ ishift[i]++;
+ }
+ }
+ break;
+ case epbcdxTRICLINIC:
+ /* For triclinic boxes the performance difference between
+ * if/else and two while loops is negligible.
+ * However, the while version can cause extreme delays
+ * before a simulation crashes due to large forces which
+ * can cause unlimited displacements.
+ * Also allowing multiple shifts would index fshift beyond bounds.
+ */
+ for(i=DIM-1; i>=1; i--) {
+ if (dx[i] > pbc->hbox_diag[i]) {
+ for (j=i; j>=0; j--)
+ dx[j] -= pbc->box[i][j];
+ ishift[i]--;
+ } else if (dx[i] <= pbc->mhbox_diag[i]) {
+ for (j=i; j>=0; j--)
+ dx[j] += pbc->box[i][j];
+ ishift[i]++;
+ }
+ }
+ /* Allow 2 shifts in x */
+ if (dx[XX] > pbc->hbox_diag[XX]) {
+ dx[XX] -= pbc->fbox_diag[XX];
+ ishift[XX]--;
+ if (dx[XX] > pbc->hbox_diag[XX]) {
+ dx[XX] -= pbc->fbox_diag[XX];
+ ishift[XX]--;
+ }
+ } else if (dx[XX] <= pbc->mhbox_diag[XX]) {
+ dx[XX] += pbc->fbox_diag[XX];
+ ishift[XX]++;
+ if (dx[XX] <= pbc->mhbox_diag[XX]) {
+ dx[XX] += pbc->fbox_diag[XX];
+ ishift[XX]++;
+ }
+ }
+ /* dx is the distance in a rectangular box */
+ d2min = norm2(dx);
+ if (d2min > pbc->max_cutoff2) {
+ copy_rvec(dx,dx_start);
+ copy_ivec(ishift,ishift_start);
+ d2min = norm2(dx);
+ /* Now try all possible shifts, when the distance is within max_cutoff
+ * it must be the shortest possible distance.
+ */
+ i = 0;
+ while ((d2min > pbc->max_cutoff2) && (i < pbc->ntric_vec)) {
+ rvec_add(dx_start,pbc->tric_vec[i],trial);
+ d2trial = norm2(trial);
+ if (d2trial < d2min) {
+ copy_rvec(trial,dx);
+ ivec_add(ishift_start,pbc->tric_shift[i],ishift);
+ d2min = d2trial;
+ }
+ i++;
+ }
+ }
+ break;
+ case epbcdx2D_RECT:
+ for(i=0; i<DIM; i++) {
+ if (i != pbc->dim) {
+ if (dx[i] > pbc->hbox_diag[i]) {
+ dx[i] -= pbc->fbox_diag[i];
+ ishift[i]--;
+ } else if (dx[i] <= pbc->mhbox_diag[i]) {
+ dx[i] += pbc->fbox_diag[i];
+ ishift[i]++;
+ }
+ }
+ }
+ break;
+ case epbcdx2D_TRIC:
+ d2min = 0;
+ for(i=DIM-1; i>=1; i--) {
+ if (i != pbc->dim) {
+ if (dx[i] > pbc->hbox_diag[i]) {
+ for (j=i; j>=0; j--)
+ dx[j] -= pbc->box[i][j];
+ ishift[i]--;
+ } else if (dx[i] <= pbc->mhbox_diag[i]) {
+ for (j=i; j>=0; j--)
+ dx[j] += pbc->box[i][j];
+ ishift[i]++;
+ }
+ d2min += dx[i]*dx[i];
+ }
+ }
+ if (pbc->dim != XX) {
+ /* Allow 2 shifts in x */
+ if (dx[XX] > pbc->hbox_diag[XX]) {
+ dx[XX] -= pbc->fbox_diag[XX];
+ ishift[XX]--;
+ if (dx[XX] > pbc->hbox_diag[XX]) {
+ dx[XX] -= pbc->fbox_diag[XX];
+ ishift[XX]--;
+ }
+ } else if (dx[XX] <= pbc->mhbox_diag[XX]) {
+ dx[XX] += pbc->fbox_diag[XX];
+ ishift[XX]++;
+ if (dx[XX] <= pbc->mhbox_diag[XX]) {
+ dx[XX] += pbc->fbox_diag[XX];
+ ishift[XX]++;
+ }
+ }
+ d2min += dx[XX]*dx[XX];
+ }
+ if (d2min > pbc->max_cutoff2) {
+ copy_rvec(dx,dx_start);
+ copy_ivec(ishift,ishift_start);
+ /* Now try all possible shifts, when the distance is within max_cutoff
+ * it must be the shortest possible distance.
+ */
+ i = 0;
+ while ((d2min > pbc->max_cutoff2) && (i < pbc->ntric_vec)) {
+ rvec_add(dx_start,pbc->tric_vec[i],trial);
+ d2trial = 0;
+ for(j=0; j<DIM; j++) {
+ if (j != pbc->dim) {
+ d2trial += trial[j]*trial[j];
+ }
+ }
+ if (d2trial < d2min) {
+ copy_rvec(trial,dx);
+ ivec_add(ishift_start,pbc->tric_shift[i],ishift);
+ d2min = d2trial;
+ }
+ i++;
+ }
+ }
+ break;
+ case epbcdx1D_RECT:
+ i = pbc->dim;
+ if (dx[i] > pbc->hbox_diag[i]) {
+ dx[i] -= pbc->fbox_diag[i];
+ ishift[i]--;
+ } else if (dx[i] <= pbc->mhbox_diag[i]) {
+ dx[i] += pbc->fbox_diag[i];
+ ishift[i]++;
+ }
+ break;
+ case epbcdx1D_TRIC:
+ i = pbc->dim;
+ if (dx[i] > pbc->hbox_diag[i]) {
+ rvec_dec(dx,pbc->box[i]);
+ ishift[i]--;
+ } else if (dx[i] <= pbc->mhbox_diag[i]) {
+ rvec_inc(dx,pbc->box[i]);
+ ishift[i]++;
+ }
+ break;
+ case epbcdxSCREW_RECT:
+ /* The shift definition requires x first */
+ if (dx[XX] > pbc->hbox_diag[XX]) {
+ dx[XX] -= pbc->fbox_diag[XX];
+ ishift[XX]--;
+ } else if (dx[XX] <= pbc->mhbox_diag[XX]) {
+ dx[XX] += pbc->fbox_diag[XX];
+ ishift[XX]++;
+ }
+ if (ishift[XX] == 1 || ishift[XX] == -1) {
+ /* Rotate around the x-axis in the middle of the box */
+ dx[YY] = pbc->box[YY][YY] - x1[YY] - x2[YY];
+ dx[ZZ] = pbc->box[ZZ][ZZ] - x1[ZZ] - x2[ZZ];
+ }
+ /* Normal pbc for y and z */
+ for(i=YY; i<=ZZ; i++) {
+ if (dx[i] > pbc->hbox_diag[i]) {
+ dx[i] -= pbc->fbox_diag[i];
+ ishift[i]--;
+ } else if (dx[i] <= pbc->mhbox_diag[i]) {
+ dx[i] += pbc->fbox_diag[i];
+ ishift[i]++;
+ }
+ }
+ break;
+ case epbcdxNOPBC:
+ case epbcdxUNSUPPORTED:
+ break;
+ default:
+ gmx_fatal(FARGS,"Internal error in pbc_dx_aiuc, set_pbc_dd or set_pbc has not been called");
+ break;
+ }
+
+ is = IVEC2IS(ishift);
+ if (debug)
+ {
+ range_check_mesg(is,0,SHIFTS,"PBC shift vector index range check.");
+ }
+
+ return is;
+}
+
+void pbc_dx_d(const t_pbc *pbc,const dvec x1, const dvec x2, dvec dx)
+{
+ int i,j;
+ dvec dx_start,trial;
+ double d2min,d2trial;
+ gmx_bool bRot;
+
+ dvec_sub(x1,x2,dx);
+
+ switch (pbc->ePBCDX) {
+ case epbcdxRECTANGULAR:
+ case epbcdx2D_RECT:
+ for(i=0; i<DIM; i++) {
+ if (i != pbc->dim) {
+ while (dx[i] > pbc->hbox_diag[i]) {
+ dx[i] -= pbc->fbox_diag[i];
+ }
+ while (dx[i] <= pbc->mhbox_diag[i]) {
+ dx[i] += pbc->fbox_diag[i];
+ }
+ }
+ }
+ break;
+ case epbcdxTRICLINIC:
+ case epbcdx2D_TRIC:
+ d2min = 0;
+ for(i=DIM-1; i>=0; i--) {
+ if (i != pbc->dim) {
+ while (dx[i] > pbc->hbox_diag[i]) {
+ for (j=i; j>=0; j--)
+ dx[j] -= pbc->box[i][j];
+ }
+ while (dx[i] <= pbc->mhbox_diag[i]) {
+ for (j=i; j>=0; j--)
+ dx[j] += pbc->box[i][j];
+ }
+ d2min += dx[i]*dx[i];
+ }
+ }
+ if (d2min > pbc->max_cutoff2) {
+ copy_dvec(dx,dx_start);
+ /* Now try all possible shifts, when the distance is within max_cutoff
+ * it must be the shortest possible distance.
+ */
+ i = 0;
+ while ((d2min > pbc->max_cutoff2) && (i < pbc->ntric_vec)) {
+ for(j=0; j<DIM; j++) {
+ trial[j] = dx_start[j] + pbc->tric_vec[i][j];
+ }
+ d2trial = 0;
+ for(j=0; j<DIM; j++) {
+ if (j != pbc->dim) {
+ d2trial += trial[j]*trial[j];
+ }
+ }
+ if (d2trial < d2min) {
+ copy_dvec(trial,dx);
+ d2min = d2trial;
+ }
+ i++;
+ }
+ }
+ break;
+ case epbcdxSCREW_RECT:
+ /* The shift definition requires x first */
+ bRot = FALSE;
+ while (dx[XX] > pbc->hbox_diag[XX]) {
+ dx[XX] -= pbc->fbox_diag[XX];
+ bRot = !bRot;
+ }
+ while (dx[XX] <= pbc->mhbox_diag[XX]) {
+ dx[XX] += pbc->fbox_diag[YY];
+ bRot = !bRot;
+ }
+ if (bRot) {
+ /* Rotate around the x-axis in the middle of the box */
+ dx[YY] = pbc->box[YY][YY] - x1[YY] - x2[YY];
+ dx[ZZ] = pbc->box[ZZ][ZZ] - x1[ZZ] - x2[ZZ];
+ }
+ /* Normal pbc for y and z */
+ for(i=YY; i<=ZZ; i++) {
+ while (dx[i] > pbc->hbox_diag[i]) {
+ dx[i] -= pbc->fbox_diag[i];
+ }
+ while (dx[i] <= pbc->mhbox_diag[i]) {
+ dx[i] += pbc->fbox_diag[i];
+ }
+ }
+ break;
+ case epbcdxNOPBC:
+ case epbcdxUNSUPPORTED:
+ break;
+ default:
+ gmx_fatal(FARGS,"Internal error in pbc_dx, set_pbc has not been called");
+ break;
+ }
+}
+
+gmx_bool image_rect(ivec xi,ivec xj,ivec box_size,real rlong2,int *shift,real *r2)
+{
+ int m,t;
+ int dx,b,b_2;
+ real dxr,rij2;
+
+ rij2=0.0;
+ t=0;
+ for(m=0; (m<DIM); m++) {
+ dx=xi[m]-xj[m];
+ t*=DIM;
+ b=box_size[m];
+ b_2=b/2;
+ if (dx < -b_2) {
+ t+=2;
+ dx+=b;
+ }
+ else if (dx > b_2)
+ dx-=b;
+ else
+ t+=1;
+ dxr=dx;
+ rij2+=dxr*dxr;
+ if (rij2 >= rlong2)
+ return FALSE;
+ }
+
+ *shift = t;
+ *r2 = rij2;
+ return TRUE;
+}
+
+gmx_bool image_cylindric(ivec xi,ivec xj,ivec box_size,real rlong2,
+ int *shift,real *r2)
+{
+ int m,t;
+ int dx,b,b_2;
+ real dxr,rij2;
+
+ rij2=0.0;
+ t=0;
+ for(m=0; (m<DIM); m++) {
+ dx=xi[m]-xj[m];
+ t*=DIM;
+ b=box_size[m];
+ b_2=b/2;
+ if (dx < -b_2) {
+ t+=2;
+ dx+=b;
+ }
+ else if (dx > b_2)
+ dx-=b;
+ else
+ t+=1;
+
+ dxr=dx;
+ rij2+=dxr*dxr;
+ if (m < ZZ) {
+ if (rij2 >= rlong2)
+ return FALSE;
+ }
+ }
+
+ *shift = t;
+ *r2 = rij2;
+ return TRUE;
+}
+
+void calc_shifts(matrix box,rvec shift_vec[])
+{
+ int k,l,m,d,n,test;
+
+ n=0;
+ for(m = -D_BOX_Z; m <= D_BOX_Z; m++)
+ for(l = -D_BOX_Y; l <= D_BOX_Y; l++)
+ for(k = -D_BOX_X; k <= D_BOX_X; k++,n++) {
+ test = XYZ2IS(k,l,m);
+ if (n != test)
+ gmx_incons("inconsistent shift numbering");
+ for(d = 0; d < DIM; d++)
+ shift_vec[n][d] = k*box[XX][d] + l*box[YY][d] + m*box[ZZ][d];
+ }
+}
+
+void calc_box_center(int ecenter,matrix box,rvec box_center)
+{
+ int d,m;
+
+ clear_rvec(box_center);
+ switch (ecenter) {
+ case ecenterTRIC:
+ for(m=0; (m<DIM); m++)
+ for(d=0; d<DIM; d++)
+ box_center[d] += 0.5*box[m][d];
+ break;
+ case ecenterRECT:
+ for(d=0; d<DIM; d++)
+ box_center[d] = 0.5*box[d][d];
+ break;
+ case ecenterZERO:
+ break;
+ default:
+ gmx_fatal(FARGS,"Unsupported value %d for ecenter",ecenter);
+ }
+}
+
+void calc_triclinic_images(matrix box,rvec img[])
+{
+ int i;
+
+ /* Calculate 3 adjacent images in the xy-plane */
+ copy_rvec(box[0],img[0]);
+ copy_rvec(box[1],img[1]);
+ if (img[1][XX] < 0)
+ svmul(-1,img[1],img[1]);
+ rvec_sub(img[1],img[0],img[2]);
+
+ /* Get the next 3 in the xy-plane as mirror images */
+ for(i=0; i<3; i++)
+ svmul(-1,img[i],img[3+i]);
+
+ /* Calculate the first 4 out of xy-plane images */
+ copy_rvec(box[2],img[6]);
+ if (img[6][XX] < 0)
+ svmul(-1,img[6],img[6]);
+ for(i=0; i<3; i++)
+ rvec_add(img[6],img[i+1],img[7+i]);
+
+ /* Mirror the last 4 from the previous in opposite rotation */
+ for(i=0; i<4; i++)
+ svmul(-1,img[6 + (2+i) % 4],img[10+i]);
+}
+
+void calc_compact_unitcell_vertices(int ecenter,matrix box,rvec vert[])
+{
+ rvec img[NTRICIMG],box_center;
+ int n,i,j,tmp[4],d;
+
+ calc_triclinic_images(box,img);
+
+ n=0;
+ for(i=2; i<=5; i+=3) {
+ tmp[0] = i-1;
+ if (i==2)
+ tmp[1] = 8;
+ else
+ tmp[1] = 6;
+ tmp[2] = (i+1) % 6;
+ tmp[3] = tmp[1]+4;
+ for(j=0; j<4; j++) {
+ for(d=0; d<DIM; d++)
+ vert[n][d] = img[i][d]+img[tmp[j]][d]+img[tmp[(j+1)%4]][d];
+ n++;
+ }
+ }
+ for(i=7; i<=13; i+=6) {
+ tmp[0] = (i-7)/2;
+ tmp[1] = tmp[0]+1;
+ if (i==7)
+ tmp[2] = 8;
+ else
+ tmp[2] = 10;
+ tmp[3] = i-1;
+ for(j=0; j<4; j++) {
+ for(d=0; d<DIM; d++)
+ vert[n][d] = img[i][d]+img[tmp[j]][d]+img[tmp[(j+1)%4]][d];
+ n++;
+ }
+ }
+ for(i=9; i<=11; i+=2) {
+ if (i==9)
+ tmp[0] = 3;
+ else
+ tmp[0] = 0;
+ tmp[1] = tmp[0]+1;
+ if (i==9)
+ tmp[2] = 6;
+ else
+ tmp[2] = 12;
+ tmp[3] = i-1;
+ for(j=0; j<4; j++) {
+ for(d=0; d<DIM; d++)
+ vert[n][d] = img[i][d]+img[tmp[j]][d]+img[tmp[(j+1)%4]][d];
+ n++;
+ }
+ }
+
+ calc_box_center(ecenter,box,box_center);
+ for(i=0; i<NCUCVERT; i++)
+ for(d=0; d<DIM; d++)
+ vert[i][d] = vert[i][d]*0.25+box_center[d];
+}
+
+int *compact_unitcell_edges()
+{
+ /* this is an index in vert[] (see calc_box_vertices) */
+ /*static int edge[NCUCEDGE*2];*/
+ int *edge;
+ static const int hexcon[24] = { 0,9, 1,19, 2,15, 3,21,
+ 4,17, 5,11, 6,23, 7,13,
+ 8,20, 10,18, 12,16, 14,22 };
+ int e,i,j;
+ gmx_bool bFirst = TRUE;
+
+ snew(edge,NCUCEDGE*2);
+
+ if (bFirst) {
+ e = 0;
+ for(i=0; i<6; i++)
+ for(j=0; j<4; j++) {
+ edge[e++] = 4*i + j;
+ edge[e++] = 4*i + (j+1) % 4;
+ }
+ for(i=0; i<12*2; i++)
+ edge[e++] = hexcon[i];
+
+ bFirst = FALSE;
+ }
+
+ return edge;
+}
+
+void put_atom_in_box(matrix box,rvec x)
+{
+ int i,m,d;
+
+ for(m=DIM-1; m>=0; m--) {
+ while (x[m] < 0)
+ for(d=0; d<=m; d++)
+ x[d] += box[m][d];
+ while (x[m] >= box[m][m])
+ for(d=0; d<=m; d++)
+ x[d] -= box[m][d];
+ }
+}
+
+void put_atoms_in_box(matrix box,int natoms,rvec x[])
+{
+ int i,m,d;
+
+ for(i=0; (i<natoms); i++)
+ put_atom_in_box(box,x[i]);
+}
+
+void put_atoms_in_triclinic_unitcell(int ecenter,matrix box,
+ int natoms,rvec x[])
+{
+ rvec box_center,shift_center;
+ real shm01,shm02,shm12,shift;
+ int i,m,d;
+
+ calc_box_center(ecenter,box,box_center);
+
+ /* The product of matrix shm with a coordinate gives the shift vector
+ which is required determine the periodic cell position */
+ shm01 = box[1][0]/box[1][1];
+ shm02 = (box[1][1]*box[2][0] - box[2][1]*box[1][0])/(box[1][1]*box[2][2]);
+ shm12 = box[2][1]/box[2][2];
+
+ clear_rvec(shift_center);
+ for(d=0; d<DIM; d++)
+ rvec_inc(shift_center,box[d]);
+ svmul(0.5,shift_center,shift_center);
+ rvec_sub(box_center,shift_center,shift_center);
+
+ shift_center[0] = shm01*shift_center[1] + shm02*shift_center[2];
+ shift_center[1] = shm12*shift_center[2];
+ shift_center[2] = 0;
+
+ for(i=0; (i<natoms); i++)
+ for(m=DIM-1; m>=0; m--) {
+ shift = shift_center[m];
+ if (m == 0) {
+ shift += shm01*x[i][1] + shm02*x[i][2];
+ } else if (m == 1) {
+ shift += shm12*x[i][2];
+ }
+ while (x[i][m]-shift < 0)
+ for(d=0; d<=m; d++)
+ x[i][d] += box[m][d];
+ while (x[i][m]-shift >= box[m][m])
+ for(d=0; d<=m; d++)
+ x[i][d] -= box[m][d];
+ }
+}
+
+const char *
+put_atoms_in_compact_unitcell(int ePBC,int ecenter,matrix box,
+ int natoms,rvec x[])
+ {
+ t_pbc pbc;
+ rvec box_center,dx;
+ int i;
+
+ set_pbc(&pbc,ePBC,box);
+ calc_box_center(ecenter,box,box_center);
+ for(i=0; i<natoms; i++) {
+ pbc_dx(&pbc,x[i],box_center,dx);
+ rvec_add(box_center,dx,x[i]);
+ }
+
+ return pbc.bLimitDistance ?
+ "WARNING: Could not put all atoms in the compact unitcell\n"
+ : NULL;
+ }
+
--- /dev/null
- static double sqr(double x)
- {
- return x*x;
- }
-
- static int gmx_nint(double x)
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*- */
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Green Red Orange Magenta Azure Cyan Skyblue
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include "typedefs.h"
+#include "smalloc.h"
++#include "vec.h"
+#include "gmx_statistics.h"
+
- d2 += sqr(stats->x[i]-stats->y[i]);
++static int gmx_dnint(double x)
+{
+ return (int) (x+0.5);
+}
+
+typedef struct gmx_stats {
+ double aa,a,b,sigma_aa,sigma_a,sigma_b,aver,sigma_aver,error;
+ double rmsd,Rdata,Rfit,Rfitaa,chi2,chi2aa;
+ double *x,*y,*dx,*dy;
+ int computed;
+ int np,np_c,nalloc;
+} gmx_stats;
+
+gmx_stats_t gmx_stats_init()
+{
+ gmx_stats *stats;
+
+ snew(stats,1);
+
+ return (gmx_stats_t) stats;
+}
+
+int gmx_stats_get_npoints(gmx_stats_t gstats, int *N)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+
+ *N = stats->np;
+
+ return estatsOK;
+}
+
+int gmx_stats_done(gmx_stats_t gstats)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+
+ sfree(stats->x);
+ stats->x = NULL;
+ sfree(stats->y);
+ stats->y = NULL;
+ sfree(stats->dx);
+ stats->dx = NULL;
+ sfree(stats->dy);
+ stats->dy = NULL;
+
+ return estatsOK;
+}
+
+int gmx_stats_add_point(gmx_stats_t gstats,double x,double y,
+ double dx,double dy)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int i;
+
+ if (stats->np+1 >= stats->nalloc)
+ {
+ if (stats->nalloc == 0)
+ stats->nalloc = 1024;
+ else
+ stats->nalloc *= 2;
+ srenew(stats->x,stats->nalloc);
+ srenew(stats->y,stats->nalloc);
+ srenew(stats->dx,stats->nalloc);
+ srenew(stats->dy,stats->nalloc);
+ for(i=stats->np; (i<stats->nalloc); i++)
+ {
+ stats->x[i] = 0;
+ stats->y[i] = 0;
+ stats->dx[i] = 0;
+ stats->dy[i] = 0;
+ }
+ }
+ stats->x[stats->np] = x;
+ stats->y[stats->np] = y;
+ stats->dx[stats->np] = dx;
+ stats->dy[stats->np] = dy;
+ stats->np++;
+ stats->computed = 0;
+
+ return estatsOK;
+}
+
+int gmx_stats_get_point(gmx_stats_t gstats,real *x,real *y,
+ real *dx,real *dy,real level)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok,outlier;
+ real rmsd,r;
+
+ if ((ok = gmx_stats_get_rmsd(gstats,&rmsd)) != estatsOK)
+ {
+ return ok;
+ }
+ outlier = 0;
+ while ((outlier == 0) && (stats->np_c < stats->np))
+ {
+ r = fabs(stats->x[stats->np_c] - stats->y[stats->np_c]);
+ outlier = (r > rmsd*level);
+ if (outlier)
+ {
+ if (NULL != x) *x = stats->x[stats->np_c];
+ if (NULL != y) *y = stats->y[stats->np_c];
+ if (NULL != dx) *dx = stats->dx[stats->np_c];
+ if (NULL != dy) *dy = stats->dy[stats->np_c];
+ }
+ stats->np_c++;
+
+ if (outlier)
+ return estatsOK;
+ }
+
+ stats->np_c = 0;
+
+ return estatsNO_POINTS;
+}
+
+int gmx_stats_add_points(gmx_stats_t gstats,int n,real *x,real *y,
+ real *dx,real *dy)
+{
+ int i,ok;
+
+ for(i=0; (i<n); i++)
+ {
+ if ((ok = gmx_stats_add_point(gstats,x[i],y[i],
+ (NULL != dx) ? dx[i] : 0,
+ (NULL != dy) ? dy[i] : 0)) != estatsOK)
+ {
+ return ok;
+ }
+ }
+ return estatsOK;
+}
+
+static int gmx_stats_compute(gmx_stats *stats,int weight)
+{
+ double yy,yx,xx,sx,sy,dy,chi2,chi2aa,d2;
+ double ssxx,ssyy,ssxy;
+ double w,wtot,yx_nw,sy_nw,sx_nw,yy_nw,xx_nw,dx2,dy2;
+ int i,N;
+
+ N = stats->np;
+ if (stats->computed == 0)
+ {
+ if (N < 1)
+ {
+ return estatsNO_POINTS;
+ }
+
+ xx = xx_nw = 0;
+ yy = yy_nw = 0;
+ yx = yx_nw = 0;
+ sx = sx_nw = 0;
+ sy = sy_nw = 0;
+ wtot = 0;
+ d2 = 0;
+ for(i=0; (i<N); i++)
+ {
- w = 1/sqr(stats->dy[i]);
++ d2 += dsqr(stats->x[i]-stats->y[i]);
+ if ((stats->dy[i]) && (weight == elsqWEIGHT_Y))
+ {
- xx += w*sqr(stats->x[i]);
- xx_nw += sqr(stats->x[i]);
++ w = 1/dsqr(stats->dy[i]);
+ }
+ else
+ {
+ w = 1;
+ }
+
+ wtot += w;
+
- yy += w*sqr(stats->y[i]);
- yy_nw += sqr(stats->y[i]);
++ xx += w*dsqr(stats->x[i]);
++ xx_nw += dsqr(stats->x[i]);
+
- stats->sigma_aver = sqrt(yy_nw/N - sqr(sy_nw/N));
++ yy += w*dsqr(stats->y[i]);
++ yy_nw += dsqr(stats->y[i]);
+
+ yx += w*stats->y[i]*stats->x[i];
+ yx_nw += stats->y[i]*stats->x[i];
+
+ sx += w*stats->x[i];
+ sx_nw += stats->x[i];
+
+ sy += w*stats->y[i];
+ sy_nw += stats->y[i];
+ }
+
+ /* Compute average, sigma and error */
+ stats->aver = sy_nw/N;
- ssxx = N*(xx_nw - sqr(sx_nw));
- ssyy = N*(yy_nw - sqr(sy_nw));
++ stats->sigma_aver = sqrt(yy_nw/N - dsqr(sy_nw/N));
+ stats->error = stats->sigma_aver/sqrt(N);
+
+ /* Compute RMSD between x and y */
+ stats->rmsd = sqrt(d2/N);
+
+ /* Correlation coefficient for data */
+ yx_nw /= N;
+ xx_nw /= N;
+ yy_nw /= N;
+ sx_nw /= N;
+ sy_nw /= N;
- stats->Rdata = sqrt(sqr(ssxy)/(ssxx*ssyy));
++ ssxx = N*(xx_nw - dsqr(sx_nw));
++ ssyy = N*(yy_nw - dsqr(sy_nw));
+ ssxy = N*(yx_nw - (sx_nw*sy_nw));
- chi2aa += sqr((stats->y[i]-(stats->aa*stats->x[i]))/dy);
- chi2 += sqr((stats->y[i]-(stats->a*stats->x[i]+stats->b))/dy);
++ stats->Rdata = sqrt(dsqr(ssxy)/(ssxx*ssyy));
+
+ /* Compute straight line through datapoints, either with intercept
+ zero (result in aa) or with intercept variable (results in a
+ and b) */
+ yx = yx/wtot;
+ xx = xx/wtot;
+ sx = sx/wtot;
+ sy = sy/wtot;
+
+ stats->aa = (yx/xx);
+ stats->a = (yx-sx*sy)/(xx-sx*sx);
+ stats->b = (sy)-(stats->a)*(sx);
+
+ /* Compute chi2, deviation from a line y = ax+b. Also compute
+ chi2aa which returns the deviation from a line y = ax. */
+ chi2 = 0;
+ chi2aa = 0;
+ for(i=0; (i<N); i++)
+ {
+ if (stats->dy[i] > 0)
+ {
+ dy = stats->dy[i];
+ }
+ else
+ {
+ dy = 1;
+ }
- nbins = gmx_nint((delta)/binwidth + 0.5);
++ chi2aa += dsqr((stats->y[i]-(stats->aa*stats->x[i]))/dy);
++ chi2 += dsqr((stats->y[i]-(stats->a*stats->x[i]+stats->b))/dy);
+ }
+ if (N > 2)
+ {
+ stats->chi2 = sqrt(chi2/(N-2));
+ stats->chi2aa = sqrt(chi2aa/(N-2));
+
+ /* Look up equations! */
+ dx2 = (xx-sx*sx);
+ dy2 = (yy-sy*sy);
+ stats->sigma_a = sqrt(stats->chi2/((N-2)*dx2));
+ stats->sigma_b = stats->sigma_a*sqrt(xx);
+ stats->Rfit = fabs(ssxy)/sqrt(ssxx*ssyy);
+ /*stats->a*sqrt(dx2/dy2);*/
+ stats->Rfitaa = stats->aa*sqrt(dx2/dy2);
+ }
+ else
+ {
+ stats->chi2 = 0;
+ stats->chi2aa = 0;
+ stats->sigma_a = 0;
+ stats->sigma_b = 0;
+ stats->Rfit = 0;
+ stats->Rfitaa = 0;
+ }
+
+ stats->computed = 1;
+ }
+
+ return estatsOK;
+}
+
+int gmx_stats_get_ab(gmx_stats_t gstats,int weight,
+ real *a,real *b,real *da,real *db,
+ real *chi2,real *Rfit)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,weight)) != estatsOK)
+ return ok;
+ if (NULL != a)
+ {
+ *a = stats->a;
+ }
+ if (NULL != b)
+ {
+ *b = stats->b;
+ }
+ if (NULL != da)
+ {
+ *da = stats->sigma_a;
+ }
+ if (NULL != db)
+ {
+ *db = stats->sigma_b;
+ }
+ if (NULL != chi2)
+ {
+ *chi2 = stats->chi2;
+ }
+ if (NULL != Rfit)
+ {
+ *Rfit = stats->Rfit;
+ }
+
+ return estatsOK;
+}
+
+int gmx_stats_get_a(gmx_stats_t gstats,int weight,real *a,real *da,
+ real *chi2,real *Rfit)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,weight)) != estatsOK)
+ return ok;
+ if (NULL != a) *a = stats->aa;
+ if (NULL != da) *da = stats->sigma_aa;
+ if (NULL != chi2) *chi2 = stats->chi2aa;
+ if (NULL != Rfit) *Rfit = stats->Rfitaa;
+
+ return estatsOK;
+}
+
+int gmx_stats_get_average(gmx_stats_t gstats,real *aver)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
+ {
+ return ok;
+ }
+
+ *aver = stats->aver;
+
+ return estatsOK;
+}
+
+int gmx_stats_get_ase(gmx_stats_t gstats,real *aver,real *sigma,real *error)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
+ {
+ return ok;
+ }
+
+ if (NULL != aver)
+ {
+ *aver = stats->aver;
+ }
+ if (NULL != sigma)
+ {
+ *sigma = stats->sigma_aver;
+ }
+ if (NULL != error)
+ {
+ *error = stats->error;
+ }
+
+ return estatsOK;
+}
+
+int gmx_stats_get_sigma(gmx_stats_t gstats,real *sigma)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
+ return ok;
+
+ *sigma = stats->sigma_aver;
+
+ return estatsOK;
+}
+
+int gmx_stats_get_error(gmx_stats_t gstats,real *error)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
+ return ok;
+
+ *error = stats->error;
+
+ return estatsOK;
+}
+
+int gmx_stats_get_corr_coeff(gmx_stats_t gstats,real *R)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
+ return ok;
+
+ *R = stats->Rdata;
+
+ return estatsOK;
+}
+
+int gmx_stats_get_rmsd(gmx_stats_t gstats,real *rmsd)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int ok;
+
+ if ((ok = gmx_stats_compute(stats,elsqWEIGHT_NONE)) != estatsOK)
+ {
+ return ok;
+ }
+
+ *rmsd = stats->rmsd;
+
+ return estatsOK;
+}
+
+int gmx_stats_dump_xy(gmx_stats_t gstats,FILE *fp)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int i,ok;
+
+ for(i=0; (i<stats->np); i++)
+ {
+ fprintf(fp,"%12g %12g %12g %12g\n",stats->x[i],stats->y[i],
+ stats->dx[i],stats->dy[i]);
+ }
+
+ return estatsOK;
+}
+
+int gmx_stats_remove_outliers(gmx_stats_t gstats,double level)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int i,iter=1,done=0,ok;
+ real rmsd,r;
+
+ while ((stats->np >= 10) && !done)
+ {
+ if ((ok = gmx_stats_get_rmsd(gstats,&rmsd)) != estatsOK)
+ {
+ return ok;
+ }
+ done = 1;
+ for(i=0; (i<stats->np); )
+ {
+ r = fabs(stats->x[i]-stats->y[i]);
+ if (r > level*rmsd)
+ {
+ fprintf(stderr,"Removing outlier, iter = %d, rmsd = %g, x = %g, y = %g\n",
+ iter,rmsd,stats->x[i],stats->y[i]);
+ if (i < stats->np-1)
+ {
+ stats->x[i] = stats->x[stats->np-1];
+ stats->y[i] = stats->y[stats->np-1];
+ stats->dx[i] = stats->dx[stats->np-1];
+ stats->dy[i] = stats->dy[stats->np-1];
+ }
+ stats->np--;
+ done = 0;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ iter++;
+ }
+
+ return estatsOK;
+}
+
+int gmx_stats_make_histogram(gmx_stats_t gstats,real binwidth,int *nb,
+ int ehisto,int normalized,real **x,real **y)
+{
+ gmx_stats *stats = (gmx_stats *) gstats;
+ int i,ok,index=0,nbins=*nb,*nindex;
+ double minx,maxx,maxy,miny,delta,dd,minh;
+
+ if (((binwidth <= 0) && (nbins <= 0)) ||
+ ((binwidth > 0) && (nbins > 0)))
+ {
+ return estatsINVALID_INPUT;
+ }
+ if (stats->np <= 2)
+ {
+ return estatsNO_POINTS;
+ }
+ minx = maxx = stats->x[0];
+ miny = maxy = stats->y[0];
+ for(i=1; (i<stats->np); i++)
+ {
+ miny = (stats->y[i] < miny) ? stats->y[i] : miny;
+ maxy = (stats->y[i] > maxy) ? stats->y[i] : maxy;
+ minx = (stats->x[i] < minx) ? stats->x[i] : minx;
+ maxx = (stats->x[i] > maxx) ? stats->x[i] : maxx;
+ }
+ if (ehisto == ehistoX)
+ {
+ delta = maxx-minx;
+ minh = minx;
+ }
+ else if (ehisto == ehistoY)
+ {
+ delta = maxy-miny;
+ minh = miny;
+ }
+ else
+ return estatsINVALID_INPUT;
+
+ if (binwidth == 0)
+ {
+ binwidth = (delta)/nbins;
+ }
+ else
+ {
- chi2 += sqr(yr[i] - ((*a)*xd[i] + (*b)));
++ nbins = gmx_dnint((delta)/binwidth + 0.5);
+ }
+ snew(*x,nbins);
+ snew(nindex,nbins);
+ for(i=0; (i<nbins); i++)
+ {
+ (*x)[i] = minh + binwidth*(i+0.5);
+ }
+ if (normalized == 0)
+ {
+ dd = 1;
+ }
+ else
+ {
+ dd = 1.0/(binwidth*stats->np);
+ }
+
+ snew(*y,nbins);
+ for(i=0; (i<stats->np); i++)
+ {
+ if (ehisto == ehistoY)
+ index = (stats->y[i]-miny)/binwidth;
+ else if (ehisto == ehistoX)
+ index = (stats->x[i]-minx)/binwidth;
+ if (index<0)
+ {
+ index = 0;
+ }
+ if (index>nbins-1)
+ {
+ index = nbins-1;
+ }
+ (*y)[index] += dd;
+ nindex[index]++;
+ }
+ if (*nb == 0)
+ *nb = nbins;
+ for(i=0; (i<nbins); i++)
+ if (nindex[i] > 0)
+ (*y)[i] /= nindex[i];
+
+ sfree(nindex);
+
+ return estatsOK;
+}
+
+static const char *stats_error[estatsNR] =
+{
+ "All well in STATS land",
+ "No points",
+ "Not enough memory",
+ "Invalid histogram input",
+ "Unknown error",
+ "Not implemented yet"
+};
+
+const char *gmx_stats_message(int estats)
+{
+ if ((estats >= 0) && (estats < estatsNR))
+ {
+ return stats_error[estats];
+ }
+ else
+ {
+ return stats_error[estatsERROR];
+ }
+}
+
+/* Old convenience functions, should be merged with the core
+ statistics above. */
+int lsq_y_ax(int n, real x[], real y[], real *a)
+{
+ gmx_stats_t lsq = gmx_stats_init();
+ int ok;
+ real da,chi2,Rfit;
+
+ gmx_stats_add_points(lsq,n,x,y,0,0);
+ if ((ok = gmx_stats_get_a(lsq,elsqWEIGHT_NONE,a,&da,&chi2,&Rfit)) != estatsOK)
+ {
+ return ok;
+ }
+
+ /* int i;
+ double xx,yx;
+
+ yx=xx=0.0;
+ for (i=0; i<n; i++) {
+ yx+=y[i]*x[i];
+ xx+=x[i]*x[i];
+ }
+ *a=yx/xx;
+ */
+ return estatsOK;
+}
+
+static int low_lsq_y_ax_b(int n, real *xr, double *xd, real yr[],
+ real *a, real *b,real *r,real *chi2)
+{
+ int i,ok;
+ gmx_stats_t lsq;
+
+ lsq = gmx_stats_init();
+ for(i=0; (i<n); i++)
+ {
+ if ((ok = gmx_stats_add_point(lsq,(NULL != xd) ? xd[i] : xr[i],yr[i],0,0))
+ != estatsOK)
+ {
+ return ok;
+ }
+ }
+ if ((ok = gmx_stats_get_ab(lsq,elsqWEIGHT_NONE,a,b,NULL,NULL,chi2,r)) != estatsOK)
+ {
+ return ok;
+ }
+
+ return estatsOK;
+ /*
+ double x,y,yx,xx,yy,sx,sy,chi2;
+
+ yx=xx=yy=sx=sy=0.0;
+ for (i=0; i<n; i++) {
+ if (xd != NULL) {
+ x = xd[i];
+ } else {
+ x = xr[i];
+ }
+ y = yr[i];
+
+ yx += y*x;
+ xx += x*x;
+ yy += y*y;
+ sx += x;
+ sy += y;
+ }
+ *a = (n*yx-sy*sx)/(n*xx-sx*sx);
+ *b = (sy-(*a)*sx)/n;
+ *r = sqrt((xx-sx*sx)/(yy-sy*sy));
+
+ chi2 = 0;
+ if (xd != NULL) {
+ for(i=0; i<n; i++)
- chi2 += sqr(yr[i] - ((*a)*xr[i] + (*b)));
++ chi2 += dsqr(yr[i] - ((*a)*xd[i] + (*b)));
+ } else {
+ for(i=0; i<n; i++)
- s_2 = sqr(1.0/dy[i]);
- sxx += s_2*sqr(x[i]);
++ chi2 += dsqr(yr[i] - ((*a)*xr[i] + (*b)));
+ }
+
+ if (n > 2)
+ return sqrt(chi2/(n-2));
+ else
+ return 0;
+ */
+}
+
+int lsq_y_ax_b(int n, real x[], real y[], real *a, real *b,real *r,real *chi2)
+{
+ return low_lsq_y_ax_b(n,x,NULL,y,a,b,r,chi2);
+}
+
+int lsq_y_ax_b_xdouble(int n, double x[], real y[], real *a, real *b,
+ real *r,real *chi2)
+{
+ return low_lsq_y_ax_b(n,NULL,x,y,a,b,r,chi2);
+}
+
+int lsq_y_ax_b_error(int n, real x[], real y[], real dy[],
+ real *a, real *b, real *da, real *db,
+ real *r,real *chi2)
+{
+ gmx_stats_t lsq;
+ int i,ok;
+
+ lsq = gmx_stats_init();
+ for(i=0; (i<n); i++)
+ {
+ if ((ok = gmx_stats_add_point(lsq,x[i],y[i],0,dy[i])) != estatsOK)
+ {
+ return ok;
+ }
+ }
+ if ((ok = gmx_stats_get_ab(lsq,elsqWEIGHT_Y,a,b,da,db,chi2,r)) != estatsOK)
+ {
+ return ok;
+ }
+ if ((ok = gmx_stats_done(lsq)) != estatsOK)
+ {
+ return ok;
+ }
+ sfree(lsq);
+
+ return estatsOK;
+ /*
+ double sxy,sxx,syy,sx,sy,w,s_2,dx2,dy2,mins;
+
+ sxy=sxx=syy=sx=sy=w=0.0;
+ mins = dy[0];
+ for(i=1; (i<n); i++)
+ mins = min(mins,dy[i]);
+ if (mins <= 0)
+ gmx_fatal(FARGS,"Zero or negative weigths in linear regression analysis");
+
+ for (i=0; i<n; i++) {
- syy += s_2*sqr(y[i]);
++ s_2 = dsqr(1.0/dy[i]);
++ sxx += s_2*dsqr(x[i]);
+ sxy += s_2*y[i]*x[i];
- *chi2+=sqr((y[i]-((*a)*x[i]+(*b)))/dy[i]);
++ syy += s_2*dsqr(y[i]);
+ sx += s_2*x[i];
+ sy += s_2*y[i];
+ w += s_2;
+ }
+ sxx = sxx/w;
+ sxy = sxy/w;
+ syy = syy/w;
+ sx = sx/w;
+ sy = sy/w;
+ dx2 = (sxx-sx*sx);
+ dy2 = (syy-sy*sy);
+ *a=(sxy-sy*sx)/dx2;
+ *b=(sy-(*a)*sx);
+
+ *chi2=0;
+ for(i=0; i<n; i++)
++ *chi2+=dsqr((y[i]-((*a)*x[i]+(*b)))/dy[i]);
+ *chi2 = *chi2/w;
+
+ *da = sqrt(*chi2/((n-2)*dx2));
+ *db = *da*sqrt(sxx);
+ *r = *a*sqrt(dx2/dy2);
+
+ if (debug)
+ fprintf(debug,"sx = %g, sy = %g, sxy = %g, sxx = %g, w = %g\n"
+ "chi2 = %g, dx2 = %g\n",
+ sx,sy,sxy,sxx,w,*chi2,dx2);
+
+ if (n > 2)
+ *chi2 = sqrt(*chi2/(n-2));
+ else
+ *chi2 = 0;
+ */
+}
+
+
--- /dev/null
- if (bExit) {
- if (gmx_parallel_env_initialized())
- /*gmx_abort(gmx_node_rank(),gmx_node_num(),0);*/
- gmx_finalize();
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROningen Mixture of Alchemy and Childrens' Stories
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <assert.h>
+#include "copyrite.h"
+#include "sysstuff.h"
+#include "macros.h"
+#include "string2.h"
+#include "smalloc.h"
+#include "pbc.h"
+#include "statutil.h"
+#include "names.h"
+#include "vec.h"
+#include "futil.h"
+#include "wman.h"
+#include "tpxio.h"
+#include "gmx_fatal.h"
+#include "network.h"
+#include "vec.h"
+#include "mtop_util.h"
+#include "gmxfio.h"
+
+#ifdef GMX_THREAD_MPI
+#include "thread_mpi.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* used for npri */
+#ifdef __sgi
+#include <sys/schedctl.h>
+#include <sys/sysmp.h>
+#endif
+
+/* The source code in this file should be thread-safe.
+ Please keep it that way. */
+
+/******************************************************************
+ *
+ * T R A J E C T O R Y S T U F F
+ *
+ ******************************************************************/
+
+/* inherently globally shared names: */
+static const char *program_name=NULL;
+static char *cmd_line=NULL;
+
+#ifdef GMX_THREAD_MPI
+/* For now, some things here are simply not re-entrant, so
+ we have to actively lock them. */
+static tMPI_Thread_mutex_t init_mutex=TMPI_THREAD_MUTEX_INITIALIZER;
+#endif
+
+
+/****************************************************************
+ *
+ * E X P O R T E D F U N C T I O N S
+ *
+ ****************************************************************/
+
+
+/* progam names, etc. */
+
+const char *ShortProgram(void)
+{
+ const char *pr,*ret;
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_lock(&init_mutex);
+#endif
+ pr=ret=program_name;
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_unlock(&init_mutex);
+#endif
+ if (ret == NULL)
+ return "GROMACS";
+ if ((pr=strrchr(ret,DIR_SEPARATOR)) != NULL)
+ ret=pr+1;
+ return ret;
+}
+
+const char *Program(void)
+{
+ const char *ret;
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_lock(&init_mutex);
+#endif
+ ret=program_name;
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_unlock(&init_mutex);
+#endif
+ return ret;
+}
+
+const char *command_line(void)
+{
+ const char *ret;
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_lock(&init_mutex);
+#endif
+ ret=cmd_line;
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_unlock(&init_mutex);
+#endif
+ return ret;
+}
+
+void set_program_name(const char *argvzero)
+{
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_lock(&init_mutex);
+#endif
+ if (program_name == NULL)
+ {
+ program_name = strdup(argvzero);
+ }
+ if (program_name == NULL)
+ program_name="GROMACS";
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_unlock(&init_mutex);
+#endif
+}
+
+
+void set_command_line(int argc, char *argv[])
+{
+ int i;
+ size_t cmdlength;
+
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_lock(&init_mutex);
+#endif
+ if (cmd_line==NULL)
+ {
+ cmdlength = strlen(argv[0]);
+ for (i=1; i<argc; i++)
+ {
+ cmdlength += strlen(argv[i]);
+ }
+
+ /* Fill the cmdline string */
+ snew(cmd_line,cmdlength+argc+1);
+ for (i=0; i<argc; i++)
+ {
+ strcat(cmd_line,argv[i]);
+ strcat(cmd_line," ");
+ }
+ }
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_unlock(&init_mutex);
+#endif
+
+}
+
+/* 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 = (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,real tp, real tpp, gmx_bool bDouble)
+{
+ int r;
+ real margin;
+
+#ifndef GMX_DOUBLE
+ /* since t is float, we can not use double precision for bRmod */
+ bDouble = FALSE;
+#endif
+
+ if (t-tp>0 && tp-tpp>0)
+ margin = 0.1*min(t-tp,tp-tpp);
+ else
+ margin = 0;
+
+ 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,t,t,FALSE);
+}
+
+
+
+
+static void set_default_time_unit(const char *time_list[], gmx_bool bCanTime)
+{
+ int i=0,j;
+ const char *select;
+
+ if (bCanTime)
+ {
+ select = getenv("GMXTIMEUNIT");
+ if (select != NULL)
+ {
+ i = 1;
+ while(time_list[i] && strcmp(time_list[i], select) != 0)
+ {
+ i++;
+ }
+ }
+ }
+ if (!bCanTime || select == NULL ||
+ time_list[i]==NULL || strcmp(time_list[i], select) != 0)
+ {
+ /* Set it to the default: ps */
+ i = 1;
+ while(time_list[i] && strcmp(time_list[i], "ps") != 0)
+ {
+ i++;
+ }
+
+ }
+ time_list[0] = time_list[i];
+}
+
+
+static void set_default_xvg_format(const char *xvg_list[])
+{
+ int i,j;
+ const char *select,*tmp;
+
+ select = getenv("GMX_VIEW_XVG");
+ if (select == NULL)
+ {
+ /* The default is the first option */
+ xvg_list[0] = xvg_list[1];
+ }
+ else
+ {
+ i = 1;
+ while (xvg_list[i] && strcmp(xvg_list[i], select) != 0)
+ {
+ i++;
+ }
+ if (xvg_list[i] != NULL)
+ {
+ xvg_list[0] = xvg_list[i];
+ }
+ else
+ {
+ xvg_list[0] = xvg_list[exvgNONE];
+ }
+ }
+}
+
+
+/***** 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;
+}
+
+/*************************************************************
+ *
+ * P A R S I N G S T U F F
+ *
+ *************************************************************/
+
+static void usage(const char *type,const char *arg)
+{
+ assert(arg);
+ gmx_fatal(FARGS,"Expected %s argument for option %s\n",type,arg);
+}
+
+int iscan(int argc,char *argv[],int *i)
+{
+ int var;
+
+ if (argc > (*i)+1) {
+ if (!sscanf(argv[++(*i)],"%d",&var))
+ usage("an integer",argv[(*i)-1]);
+ } else
+ usage("an integer",argv[*i]);
+
+ return var;
+}
+
+gmx_large_int_t istepscan(int argc,char *argv[],int *i)
+{
+ gmx_large_int_t var;
+
+ if (argc > (*i)+1) {
+ if (!sscanf(argv[++(*i)],gmx_large_int_pfmt,&var))
+ usage("an integer",argv[(*i)-1]);
+ } else
+ usage("an integer",argv[*i]);
+
+ return var;
+}
+
+double dscan(int argc,char *argv[],int *i)
+{
+ double var;
+
+ if (argc > (*i)+1) {
+ if (!sscanf(argv[++(*i)],"%lf",&var))
+ usage("a real",argv[(*i)-1]);
+ } else
+ usage("a real",argv[*i]);
+
+ return var;
+}
+
+char *sscan(int argc,char *argv[],int *i)
+{
+ if (argc > (*i)+1)
+ {
+ if ( (argv[(*i)+1][0]=='-') && (argc > (*i)+2) &&
+ (argv[(*i)+2][0]!='-') )
+ {
+ fprintf(stderr,"Possible missing string argument for option %s\n\n",
+ argv[*i]);
+ }
+ }
+ else
+ usage("a string",argv[*i]);
+
+ return argv[++(*i)];
+}
+
+int nenum(const char *const enumc[])
+{
+ int i;
+
+ i=1;
+ /* we *can* compare pointers directly here! */
+ while(enumc[i] && enumc[0]!=enumc[i])
+ i++;
+
+ return i;
+}
+
+static void pdesc(char *desc)
+{
+ char *ptr,*nptr;
+
+ ptr=desc;
+ if ((int)strlen(ptr) < 70)
+ fprintf(stderr,"\t%s\n",ptr);
+ else {
+ for(nptr=ptr+70; (nptr != ptr) && (!isspace(*nptr)); nptr--)
+ ;
+ if (nptr == ptr)
+ fprintf(stderr,"\t%s\n",ptr);
+ else {
+ *nptr='\0';
+ nptr++;
+ fprintf(stderr,"\t%s\n",ptr);
+ pdesc(nptr);
+ }
+ }
+}
+
+static FILE *man_file(const output_env_t oenv,const char *mantp)
+{
+ FILE *fp;
+ char buf[256];
+ const char *pr = output_env_get_short_program_name(oenv);
+
+ if (strcmp(mantp,"ascii") != 0)
+ sprintf(buf,"%s.%s",pr,mantp);
+ else
+ sprintf(buf,"%s.txt",pr);
+ fp = gmx_fio_fopen(buf,"w");
+
+ return fp;
+}
+
+static int add_parg(int npargs,t_pargs *pa,t_pargs *pa_add)
+{
+ memcpy(&(pa[npargs]),pa_add,sizeof(*pa_add));
+
+ return npargs+1;
+}
+
+static char *mk_desc(t_pargs *pa, const char *time_unit_str)
+{
+ char *newdesc=NULL,*ndesc=NULL,*nptr=NULL;
+ const char*ptr=NULL;
+ int len,k;
+
+ /* First compute length for description */
+ len = strlen(pa->desc)+1;
+ if ((ptr = strstr(pa->desc,"HIDDEN")) != NULL)
+ len += 4;
+ if (pa->type == etENUM) {
+ len += 10;
+ for(k=1; (pa->u.c[k] != NULL); k++) {
+ len += strlen(pa->u.c[k])+12;
+ }
+ }
+ snew(newdesc,len);
+
+ /* add label for hidden options */
+ if (is_hidden(pa))
+ sprintf(newdesc,"[hidden] %s",ptr+6);
+ else
+ strcpy(newdesc,pa->desc);
+
+ /* change '%t' into time_unit */
+#define TUNITLABEL "%t"
+#define NTUNIT strlen(TUNITLABEL)
+ if (pa->type == etTIME)
+ while( (nptr=strstr(newdesc,TUNITLABEL)) != NULL ) {
+ nptr[0]='\0';
+ nptr+=NTUNIT;
+ len+=strlen(time_unit_str)-NTUNIT;
+ snew(ndesc,len);
+ strcpy(ndesc,newdesc);
+ strcat(ndesc,time_unit_str);
+ strcat(ndesc,nptr);
+ sfree(newdesc);
+ newdesc=ndesc;
+ ndesc=NULL;
+ }
+#undef TUNITLABEL
+#undef NTUNIT
+
+ /* Add extra comment for enumerateds */
+ if (pa->type == etENUM) {
+ strcat(newdesc,": ");
+ for(k=1; (pa->u.c[k] != NULL); k++) {
+ strcat(newdesc,"[TT]");
+ strcat(newdesc,pa->u.c[k]);
+ strcat(newdesc,"[tt]");
+ /* Print a comma everywhere but at the last one */
+ if (pa->u.c[k+1] != NULL) {
+ if (pa->u.c[k+2] == NULL)
+ strcat(newdesc," or ");
+ else
+ strcat(newdesc,", ");
+ }
+ }
+ }
+ return newdesc;
+}
+
+
+void parse_common_args(int *argc,char *argv[],unsigned long Flags,
+ int nfile,t_filenm fnm[],int npargs,t_pargs *pa,
+ int ndesc,const char **desc,
+ int nbugs,const char **bugs,
+ output_env_t *oenv)
+{
+ gmx_bool bHelp=FALSE,bHidden=FALSE,bQuiet=FALSE,bVersion=FALSE;
+ const char *manstr[] = { NULL, "no", "html", "tex", "nroff", "ascii",
+ "completion", "py", "xml", "wiki", NULL };
+ /* This array should match the order of the enum in oenv.h */
+ const char *xvg_format[] = { NULL, "xmgrace", "xmgr", "none", NULL };
+ /* This array should match the order of the enum in oenv.h */
+ const char *time_units[] = { NULL, "fs", "ps", "ns", "us", "ms", "s",
+ NULL };
+ int nicelevel=0,mantp=0,npri=0,debug_level=0,verbose_level=0;
+ char *deffnm=NULL;
+ real tbegin=0,tend=0,tdelta=0;
+ gmx_bool bView=FALSE;
+
+ t_pargs *all_pa=NULL;
+
+ t_pargs npri_pa = { "-npri", FALSE, etINT, {&npri},
+ "HIDDEN Set non blocking priority (try 128)" };
+ t_pargs nice_pa = { "-nice", FALSE, etINT, {&nicelevel},
+ "Set the nicelevel" };
+ t_pargs deffnm_pa = { "-deffnm", FALSE, etSTR, {&deffnm},
+ "Set the default filename for all file options" };
+ t_pargs begin_pa = { "-b", FALSE, etTIME, {&tbegin},
+ "First frame (%t) to read from trajectory" };
+ t_pargs end_pa = { "-e", FALSE, etTIME, {&tend},
+ "Last frame (%t) to read from trajectory" };
+ t_pargs dt_pa = { "-dt", FALSE, etTIME, {&tdelta},
+ "Only use frame when t MOD dt = first time (%t)" };
+ t_pargs view_pa = { "-w", FALSE, etBOOL, {&bView},
+ "View output [TT].xvg[tt], [TT].xpm[tt], [TT].eps[tt] and [TT].pdb[tt] files" };
+ t_pargs xvg_pa = { "-xvg", FALSE, etENUM, {xvg_format},
+ "xvg plot formatting" };
+ t_pargs time_pa = { "-tu", FALSE, etENUM, {time_units},
+ "Time unit" };
+ /* Maximum number of extra arguments */
+#define EXTRA_PA 16
+
+ t_pargs pca_pa[] = {
+ { "-h", FALSE, etBOOL, {&bHelp},
+ "Print help info and quit" },
+ { "-version", FALSE, etBOOL, {&bVersion},
+ "Print version info and quit" },
+ { "-verb", FALSE, etINT, {&verbose_level},
+ "HIDDENLevel of verbosity for this program" },
+ { "-hidden", FALSE, etBOOL, {&bHidden},
+ "HIDDENPrint hidden options" },
+ { "-quiet",FALSE, etBOOL, {&bQuiet},
+ "HIDDENDo not print help info" },
+ { "-man", FALSE, etENUM, {manstr},
+ "HIDDENWrite manual and quit" },
+ { "-debug",FALSE, etINT, {&debug_level},
+ "HIDDENWrite file with debug information, 1: short, 2: also x and f" },
+ };
+#define NPCA_PA asize(pca_pa)
+ FILE *fp;
+ gmx_bool bPrint,bExit,bXvgr;
+ int i,j,k,npall,max_pa,cmdlength;
+ char *ptr,*newdesc;
+ const char *envstr;
+
+#define FF(arg) ((Flags & arg)==arg)
+
+ cmdlength = strlen(argv[0]);
+ /* Check for double arguments */
+ for (i=1; (i<*argc); i++)
+ {
+ cmdlength += strlen(argv[i]);
+ if (argv[i] && (strlen(argv[i]) > 1) && (!isdigit(argv[i][1])))
+ {
+ for (j=i+1; (j<*argc); j++)
+ {
+ if ( (argv[i][0]=='-') && (argv[j][0]=='-') &&
+ (strcmp(argv[i],argv[j])==0) )
+ {
+ if (FF(PCA_NOEXIT_ON_ARGS))
+ fprintf(stderr,"Double command line argument %s\n",
+ argv[i]);
+ else
+ gmx_fatal(FARGS,"Double command line argument %s\n",
+ argv[i]);
+ }
+ }
+ }
+ }
+ debug_gmx();
+ set_program_name(argv[0]);
+ set_command_line(*argc, argv);
+
+ /* Handle the flags argument, which is a bit field
+ * The FF macro returns whether or not the bit is set
+ */
+ bPrint = !FF(PCA_SILENT);
+
+ /* Check ALL the flags ... */
+ max_pa = NPCA_PA + EXTRA_PA + npargs+1;
+ snew(all_pa,max_pa);
+
+ for(i=npall=0; (i<NPCA_PA); i++)
+ npall = add_parg(npall,all_pa,&(pca_pa[i]));
+
+#ifdef __sgi
+ envstr = getenv("GMXNPRIALL");
+ if (envstr)
+ npri=strtol(envstr,NULL,10);
+ if (FF(PCA_BE_NICE)) {
+ envstr = getenv("GMXNPRI");
+ if (envstr)
+ npri=strtol(envstr,NULL,10);
+ }
+ npall = add_parg(npall,all_pa,&npri_pa);
+#endif
+
+ if (FF(PCA_BE_NICE))
+ nicelevel=19;
+ npall = add_parg(npall,all_pa,&nice_pa);
+
+ if (FF(PCA_CAN_SET_DEFFNM))
+ npall = add_parg(npall,all_pa,&deffnm_pa);
+ if (FF(PCA_CAN_BEGIN))
+ npall = add_parg(npall,all_pa,&begin_pa);
+ if (FF(PCA_CAN_END))
+ npall = add_parg(npall,all_pa,&end_pa);
+ if (FF(PCA_CAN_DT))
+ {
+ npall = add_parg(npall,all_pa,&dt_pa);
+ }
+ if (FF(PCA_TIME_UNIT)) {
+ npall = add_parg(npall,all_pa,&time_pa);
+ }
+ if (FF(PCA_CAN_VIEW))
+ npall = add_parg(npall,all_pa,&view_pa);
+
+ bXvgr = FALSE;
+ for(i=0; (i<nfile); i++)
+ {
+ bXvgr = bXvgr || (fnm[i].ftp == efXVG);
+ }
+ if (bXvgr)
+ {
+ npall = add_parg(npall,all_pa,&xvg_pa);
+ }
+
+ /* Now append the program specific arguments */
+ for(i=0; (i<npargs); i++)
+ npall = add_parg(npall,all_pa,&(pa[i]));
+
+ /* set etENUM options to default */
+ for(i=0; (i<npall); i++)
+ {
+ if (all_pa[i].type==etENUM)
+ {
+ all_pa[i].u.c[0]=all_pa[i].u.c[1];
+ }
+ }
+ set_default_time_unit(time_units,FF(PCA_TIME_UNIT));
+ set_default_xvg_format(xvg_format);
+
+ /* Now parse all the command-line options */
+ get_pargs(argc,argv,npall,all_pa,FF(PCA_KEEP_ARGS));
+
+ /* set program name, command line, and default values for output options */
+ output_env_init(oenv, *argc, argv, (time_unit_t)nenum(time_units), bView,
+ (xvg_format_t)nenum(xvg_format), verbose_level, debug_level);
+
+ if (bVersion) {
+ printf("Program: %s\n",output_env_get_program_name(*oenv));
+ gmx_print_version_info(stdout);
+ exit(0);
+ }
+
+ if (FF(PCA_CAN_SET_DEFFNM) && (deffnm!=NULL))
+ set_default_file_name(deffnm);
+
+ /* Parse the file args */
+ parse_file_args(argc,argv,nfile,fnm,FF(PCA_KEEP_ARGS),!FF(PCA_NOT_READ_NODE));
+
+ /* Open the debug file */
+ if (debug_level > 0) {
+ char buf[256];
+
+ if (gmx_mpi_initialized())
+ sprintf(buf,"%s%d.debug",output_env_get_short_program_name(*oenv),
+ gmx_node_rank());
+ else
+ sprintf(buf,"%s.debug",output_env_get_short_program_name(*oenv));
+
+ init_debug(debug_level,buf);
+ fprintf(stderr,"Opening debug file %s (src code file %s, line %d)\n",
+ buf,__FILE__,__LINE__);
+ }
+
+ /* Now copy the results back... */
+ for(i=0,k=npall-npargs; (i<npargs); i++,k++)
+ memcpy(&(pa[i]),&(all_pa[k]),(size_t)sizeof(pa[i]));
+
+
+ for(i=0; (i<npall); i++)
+ all_pa[i].desc = mk_desc(&(all_pa[i]), output_env_get_time_unit(*oenv));
+
+ bExit = bHelp || (strcmp(manstr[0],"no") != 0);
+
+#if (defined __sgi && USE_SGI_FPE)
+ doexceptions();
+#endif
+
+ /* Set the nice level */
+#ifdef __sgi
+ if (npri != 0 && !bExit) {
+ schedctl(MPTS_RTPRI,0,npri);
+ }
+#endif
+
+#ifdef HAVE_UNISTD_H
+
+#ifndef GMX_NO_NICE
+ /* The some system, e.g. the catamount kernel on cray xt3 do not have nice(2). */
+ if (nicelevel != 0 && !bExit)
+ {
+#ifdef GMX_THREAD_MPI
+ static gmx_bool nice_set=FALSE; /* only set it once */
+ tMPI_Thread_mutex_lock(&init_mutex);
+ if (!nice_set)
+ {
+#endif
+ i=nice(nicelevel); /* assign ret value to avoid warnings */
+#ifdef GMX_THREAD_MPI
+ nice_set=TRUE;
+ }
+ tMPI_Thread_mutex_unlock(&init_mutex);
+#endif
+ }
+#endif
+#endif
+
+ if (!(FF(PCA_QUIET) || bQuiet )) {
+ if (bHelp)
+ write_man(stderr,"help",output_env_get_program_name(*oenv),
+ ndesc,desc,nfile, fnm,npall,all_pa, nbugs,bugs,bHidden);
+ else if (bPrint) {
+ pr_fns(stderr,nfile,fnm);
+ print_pargs(stderr,npall,all_pa,FALSE);
+ }
+ }
+
+ if (strcmp(manstr[0],"no") != 0) {
+ if(!strcmp(manstr[0],"completion")) {
+ /* one file each for csh, bash and zsh if we do completions */
+ fp=man_file(*oenv,"completion-zsh");
+
+ write_man(fp,"completion-zsh",output_env_get_program_name(*oenv),
+ ndesc,desc,nfile, fnm, npall,all_pa,nbugs,bugs,bHidden);
+ gmx_fio_fclose(fp);
+ fp=man_file(*oenv,"completion-bash");
+ write_man(fp,"completion-bash",output_env_get_program_name(*oenv),
+ ndesc,desc,nfile, fnm, npall,all_pa,nbugs,bugs,bHidden);
+ gmx_fio_fclose(fp);
+ fp=man_file(*oenv,"completion-csh");
+ write_man(fp,"completion-csh",output_env_get_program_name(*oenv),
+ ndesc,desc,nfile, fnm, npall,all_pa,nbugs,bugs,bHidden);
+ gmx_fio_fclose(fp);
+ } else {
+ fp=man_file(*oenv,manstr[0]);
+ write_man(fp,manstr[0],output_env_get_program_name(*oenv),
+ ndesc,desc,nfile,fnm, npall, all_pa,nbugs,bugs,bHidden);
+ gmx_fio_fclose(fp);
+ }
+ }
+
+ /* convert time options, must be done after printing! */
+
+ for(i=0; i<npall; i++) {
+ if ((all_pa[i].type == etTIME) && (*all_pa[i].u.r >= 0)) {
+ *all_pa[i].u.r *= output_env_get_time_invfactor(*oenv);
+ }
+ }
+
+ /* Extract Time info from arguments */
+ if (FF(PCA_CAN_BEGIN) && opt2parg_bSet("-b",npall,all_pa))
+ setTimeValue(TBEGIN,opt2parg_real("-b",npall,all_pa));
+
+ if (FF(PCA_CAN_END) && opt2parg_bSet("-e",npall,all_pa))
+ setTimeValue(TEND,opt2parg_real("-e",npall,all_pa));
+
+ if (FF(PCA_CAN_DT) && opt2parg_bSet("-dt",npall,all_pa))
+ setTimeValue(TDELTA,opt2parg_real("-dt",npall,all_pa));
+
+ /* clear memory */
+ for (i = 0; i < npall; ++i)
+ sfree((void *)all_pa[i].desc);
+ sfree(all_pa);
+
+ if (!FF(PCA_NOEXIT_ON_ARGS)) {
+ if (*argc > 1) {
+ gmx_cmd(argv[1]);
+ }
+ }
++ if (bExit)
++ {
++ gmx_finalize_par();
++
+ exit(0);
+ }
+#undef FF
+}
+
--- /dev/null
- BOOL(vec[i]));
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROningen Mixture of Alchemy and Childrens' Stories
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* This file is completely threadsafe - please keep it that way! */
+#ifdef GMX_THREAD_MPI
+#include <thread_mpi.h>
+#endif
+
+
+#include <stdio.h>
+#include "smalloc.h"
+#include "typedefs.h"
+#include "names.h"
+#include "txtdump.h"
+#include "string2.h"
+#include "vec.h"
+#include "macros.h"
+
+
+int pr_indent(FILE *fp,int n)
+{
+ int i;
+
+ for (i=0; i<n; i++) (void) fprintf(fp," ");
+ return n;
+}
+
+int available(FILE *fp,void *p,int indent,const char *title)
+{
+ if (!p) {
+ if (indent > 0)
+ pr_indent(fp,indent);
+ (void) fprintf(fp,"%s: not available\n",title);
+ }
+ return (p!=NULL);
+}
+
+int pr_title(FILE *fp,int indent,const char *title)
+{
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s:\n",title);
+ return (indent+INDENT);
+}
+
+int pr_title_n(FILE *fp,int indent,const char *title,int n)
+{
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s (%d):\n",title,n);
+ return (indent+INDENT);
+}
+
+int pr_title_nxn(FILE *fp,int indent,const char *title,int n1,int n2)
+{
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s (%dx%d):\n",title,n1,n2);
+ return (indent+INDENT);
+}
+
+void pr_ivec(FILE *fp,int indent,const char *title,int vec[],int n, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,vec,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++)
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d]=%d\n",title,bShowNumbers?i:-1,vec[i]);
+ }
+ }
+}
+
+void pr_ivec_block(FILE *fp,int indent,const char *title,int vec[],int n, gmx_bool bShowNumbers)
+{
+ int i,j;
+
+ if (available(fp,vec,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ i = 0;
+ while (i < n)
+ {
+ j = i+1;
+ while (j < n && vec[j] == vec[j-1]+1)
+ {
+ j++;
+ }
+ /* Print consecutive groups of 3 or more as blocks */
+ if (j - i < 3)
+ {
+ while(i < j)
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d]=%d\n",
+ title,bShowNumbers?i:-1,vec[i]);
+ i++;
+ }
+ }
+ else
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d,...,%d] = {%d,...,%d}\n",
+ title,
+ bShowNumbers?i:-1,
+ bShowNumbers?j-1:-1,
+ vec[i],vec[j-1]);
+ i = j;
+ }
+ }
+ }
+}
+
+void pr_bvec(FILE *fp,int indent,const char *title,gmx_bool vec[],int n, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,vec,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++)
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d]=%s\n",title,bShowNumbers?i:-1,
- PS("massw",BOOL(rotg->bMassW));
++ EBOOL(vec[i]));
+ }
+ }
+}
+
+void pr_ivecs(FILE *fp,int indent,const char *title,ivec vec[],int n, gmx_bool bShowNumbers)
+{
+ int i,j;
+
+ if (available(fp,vec,indent,title))
+ {
+ indent=pr_title_nxn(fp,indent,title,n,DIM);
+ for (i=0; i<n; i++)
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d]={",title,bShowNumbers?i:-1);
+ for (j=0; j<DIM; j++)
+ {
+ if (j!=0) (void) fprintf(fp,", ");
+ fprintf(fp,"%d",vec[i][j]);
+ }
+ (void) fprintf(fp,"}\n");
+ }
+ }
+}
+
+void pr_rvec(FILE *fp,int indent,const char *title,real vec[],int n, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,vec,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++)
+ {
+ pr_indent(fp,indent);
+ fprintf(fp,"%s[%d]=%12.5e\n",title,bShowNumbers?i:-1,vec[i]);
+ }
+ }
+}
+
+void pr_dvec(FILE *fp,int indent,const char *title,double vec[],int n, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,vec,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++)
+ {
+ pr_indent(fp,indent);
+ fprintf(fp,"%s[%d]=%12.5e\n",title,bShowNumbers?i:-1,vec[i]);
+ }
+ }
+}
+
+
+/*
+void pr_mat(FILE *fp,int indent,char *title,matrix m)
+{
+ int i,j;
+
+ if (available(fp,m,indent,title)) {
+ indent=pr_title_n(fp,indent,title,n);
+ for(i=0; i<n; i++) {
+ pr_indent(fp,indent);
+ fprintf(fp,"%s[%d]=%12.5e %12.5e %12.5e\n",
+ title,bShowNumbers?i:-1,m[i][XX],m[i][YY],m[i][ZZ]);
+ }
+ }
+}
+*/
+
+void pr_rvecs_len(FILE *fp,int indent,const char *title,rvec vec[],int n)
+{
+ int i,j;
+
+ if (available(fp,vec,indent,title)) {
+ indent=pr_title_nxn(fp,indent,title,n,DIM);
+ for (i=0; i<n; i++) {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%5d]={",title,i);
+ for (j=0; j<DIM; j++) {
+ if (j != 0)
+ (void) fprintf(fp,", ");
+ (void) fprintf(fp,"%12.5e",vec[i][j]);
+ }
+ (void) fprintf(fp,"} len=%12.5e\n",norm(vec[i]));
+ }
+ }
+}
+
+void pr_rvecs(FILE *fp,int indent,const char *title,rvec vec[],int n)
+{
+ const char *fshort = "%12.5e";
+ const char *flong = "%15.8e";
+ const char *format;
+ int i,j;
+
+ if (getenv("LONGFORMAT") != NULL)
+ format = flong;
+ else
+ format = fshort;
+
+ if (available(fp,vec,indent,title)) {
+ indent=pr_title_nxn(fp,indent,title,n,DIM);
+ for (i=0; i<n; i++) {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%5d]={",title,i);
+ for (j=0; j<DIM; j++) {
+ if (j != 0)
+ (void) fprintf(fp,", ");
+ (void) fprintf(fp,format,vec[i][j]);
+ }
+ (void) fprintf(fp,"}\n");
+ }
+ }
+}
+
+
+void pr_reals(FILE *fp,int indent,const char *title,real *vec,int n)
+{
+ int i;
+
+ if (available(fp,vec,indent,title)) {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s:\t",title);
+ for(i=0; i<n; i++)
+ fprintf(fp," %10g",vec[i]);
+ (void) fprintf(fp,"\n");
+ }
+}
+
+void pr_doubles(FILE *fp,int indent,const char *title,double *vec,int n)
+{
+ int i;
+
+ if (available(fp,vec,indent,title)) {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s:\t",title);
+ for(i=0; i<n; i++)
+ fprintf(fp," %10g",vec[i]);
+ (void) fprintf(fp,"\n");
+ }
+}
+
+static void pr_int(FILE *fp,int indent,const char *title,int i)
+{
+ pr_indent(fp,indent);
+ fprintf(fp,"%-20s = %d\n",title,i);
+}
+
+static void pr_gmx_large_int(FILE *fp,int indent,const char *title,gmx_large_int_t i)
+{
+ char buf[STEPSTRSIZE];
+
+ pr_indent(fp,indent);
+ fprintf(fp,"%-20s = %s\n",title,gmx_step_str(i,buf));
+}
+
+static void pr_real(FILE *fp,int indent,const char *title,real r)
+{
+ pr_indent(fp,indent);
+ fprintf(fp,"%-20s = %g\n",title,r);
+}
+
+static void pr_double(FILE *fp,int indent,const char *title,double d)
+{
+ pr_indent(fp,indent);
+ fprintf(fp,"%-20s = %g\n",title,d);
+}
+
+static void pr_str(FILE *fp,int indent,const char *title,const char *s)
+{
+ pr_indent(fp,indent);
+ fprintf(fp,"%-20s = %s\n",title,s);
+}
+
+void pr_qm_opts(FILE *fp,int indent,const char *title,t_grpopts *opts)
+{
+ int i,m,j;
+
+ fprintf(fp,"%s:\n",title);
+
+ pr_int(fp,indent,"ngQM",opts->ngQM);
+ if (opts->ngQM > 0) {
+ pr_ivec(fp,indent,"QMmethod",opts->QMmethod,opts->ngQM,FALSE);
+ pr_ivec(fp,indent,"QMbasis",opts->QMbasis,opts->ngQM,FALSE);
+ pr_ivec(fp,indent,"QMcharge",opts->QMcharge,opts->ngQM,FALSE);
+ pr_ivec(fp,indent,"QMmult",opts->QMmult,opts->ngQM,FALSE);
+ pr_bvec(fp,indent,"bSH",opts->bSH,opts->ngQM,FALSE);
+ pr_ivec(fp,indent,"CASorbitals",opts->CASorbitals,opts->ngQM,FALSE);
+ pr_ivec(fp,indent,"CASelectrons",opts->CASelectrons,opts->ngQM,FALSE);
+ pr_rvec(fp,indent,"SAon",opts->SAon,opts->ngQM,FALSE);
+ pr_rvec(fp,indent,"SAon",opts->SAon,opts->ngQM,FALSE);
+ pr_ivec(fp,indent,"SAsteps",opts->SAsteps,opts->ngQM,FALSE);
+ pr_bvec(fp,indent,"bOPT",opts->bOPT,opts->ngQM,FALSE);
+ pr_bvec(fp,indent,"bTS",opts->bTS,opts->ngQM,FALSE);
+ }
+}
+
+static void pr_grp_opts(FILE *out,int indent,const char *title,t_grpopts *opts,
+ gmx_bool bMDPformat)
+{
+ int i,m,j;
+
+ if (!bMDPformat)
+ fprintf(out,"%s:\n",title);
+
+ pr_indent(out,indent);
+ fprintf(out,"nrdf%s",bMDPformat ? " = " : ":");
+ for(i=0; (i<opts->ngtc); i++)
+ fprintf(out," %10g",opts->nrdf[i]);
+ fprintf(out,"\n");
+
+ pr_indent(out,indent);
+ fprintf(out,"ref-t%s",bMDPformat ? " = " : ":");
+ for(i=0; (i<opts->ngtc); i++)
+ fprintf(out," %10g",opts->ref_t[i]);
+ fprintf(out,"\n");
+
+ pr_indent(out,indent);
+ fprintf(out,"tau-t%s",bMDPformat ? " = " : ":");
+ for(i=0; (i<opts->ngtc); i++)
+ fprintf(out," %10g",opts->tau_t[i]);
+ fprintf(out,"\n");
+
+ /* Pretty-print the simulated annealing info */
+ fprintf(out,"anneal%s",bMDPformat ? " = " : ":");
+ for(i=0; (i<opts->ngtc); i++)
+ fprintf(out," %10s",EANNEAL(opts->annealing[i]));
+ fprintf(out,"\n");
+
+ fprintf(out,"ann-npoints%s",bMDPformat ? " = " : ":");
+ for(i=0; (i<opts->ngtc); i++)
+ fprintf(out," %10d",opts->anneal_npoints[i]);
+ fprintf(out,"\n");
+
+ for(i=0; (i<opts->ngtc); i++) {
+ if(opts->anneal_npoints[i]>0) {
+ fprintf(out,"ann. times [%d]:\t",i);
+ for(j=0; (j<opts->anneal_npoints[i]); j++)
+ fprintf(out," %10.1f",opts->anneal_time[i][j]);
+ fprintf(out,"\n");
+ fprintf(out,"ann. temps [%d]:\t",i);
+ for(j=0; (j<opts->anneal_npoints[i]); j++)
+ fprintf(out," %10.1f",opts->anneal_temp[i][j]);
+ fprintf(out,"\n");
+ }
+ }
+
+ pr_indent(out,indent);
+ fprintf(out,"acc:\t");
+ for(i=0; (i<opts->ngacc); i++)
+ for(m=0; (m<DIM); m++)
+ fprintf(out," %10g",opts->acc[i][m]);
+ fprintf(out,"\n");
+
+ pr_indent(out,indent);
+ fprintf(out,"nfreeze:");
+ for(i=0; (i<opts->ngfrz); i++)
+ for(m=0; (m<DIM); m++)
+ fprintf(out," %10s",opts->nFreeze[i][m] ? "Y" : "N");
+ fprintf(out,"\n");
+
+
+ for(i=0; (i<opts->ngener); i++) {
+ pr_indent(out,indent);
+ fprintf(out,"energygrp-flags[%3d]:",i);
+ for(m=0; (m<opts->ngener); m++)
+ fprintf(out," %d",opts->egp_flags[opts->ngener*i+m]);
+ fprintf(out,"\n");
+ }
+
+ fflush(out);
+}
+
+static void pr_matrix(FILE *fp,int indent,const char *title,rvec *m,
+ gmx_bool bMDPformat)
+{
+ if (bMDPformat)
+ fprintf(fp,"%-10s = %g %g %g %g %g %g\n",title,
+ m[XX][XX],m[YY][YY],m[ZZ][ZZ],m[XX][YY],m[XX][ZZ],m[YY][ZZ]);
+ else
+ pr_rvecs(fp,indent,title,m,DIM);
+}
+
+static void pr_cosine(FILE *fp,int indent,const char *title,t_cosines *cos,
+ gmx_bool bMDPformat)
+{
+ int j;
+
+ if (bMDPformat) {
+ fprintf(fp,"%s = %d\n",title,cos->n);
+ }
+ else {
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"n = %d\n",cos->n);
+ if (cos->n > 0) {
+ (void) pr_indent(fp,indent+2);
+ fprintf(fp,"a =");
+ for(j=0; (j<cos->n); j++)
+ fprintf(fp," %e",cos->a[j]);
+ fprintf(fp,"\n");
+ (void) pr_indent(fp,indent+2);
+ fprintf(fp,"phi =");
+ for(j=0; (j<cos->n); j++)
+ fprintf(fp," %e",cos->phi[j]);
+ fprintf(fp,"\n");
+ }
+ }
+}
+
+#define PS(t,s) pr_str(fp,indent,t,s)
+#define PI(t,s) pr_int(fp,indent,t,s)
+#define PSTEP(t,s) pr_gmx_large_int(fp,indent,t,s)
+#define PR(t,s) pr_real(fp,indent,t,s)
+#define PD(t,s) pr_double(fp,indent,t,s)
+
+static void pr_pullgrp(FILE *fp,int indent,int g,t_pullgrp *pg)
+{
+ pr_indent(fp,indent);
+ fprintf(fp,"pull-group %d:\n",g);
+ indent += 2;
+ pr_ivec_block(fp,indent,"atom",pg->ind,pg->nat,TRUE);
+ pr_rvec(fp,indent,"weight",pg->weight,pg->nweight,TRUE);
+ PI("pbcatom",pg->pbcatom);
+ pr_rvec(fp,indent,"vec",pg->vec,DIM,TRUE);
+ pr_rvec(fp,indent,"init",pg->init,DIM,TRUE);
+ PR("rate",pg->rate);
+ PR("k",pg->k);
+ PR("kB",pg->kB);
+}
+
+static void pr_pull(FILE *fp,int indent,t_pull *pull)
+{
+ int g;
+
+ PS("pull-geometry",EPULLGEOM(pull->eGeom));
+ pr_ivec(fp,indent,"pull-dim",pull->dim,DIM,TRUE);
+ PR("pull-r1",pull->cyl_r1);
+ PR("pull-r0",pull->cyl_r0);
+ PR("pull-constr-tol",pull->constr_tol);
+ PI("pull-nstxout",pull->nstxout);
+ PI("pull-nstfout",pull->nstfout);
+ PI("pull-ngrp",pull->ngrp);
+ for(g=0; g<pull->ngrp+1; g++)
+ pr_pullgrp(fp,indent,g,&pull->grp[g]);
+}
+
+static void pr_rotgrp(FILE *fp,int indent,int g,t_rotgrp *rotg)
+{
+ pr_indent(fp,indent);
+ fprintf(fp,"rotation_group %d:\n",g);
+ indent += 2;
+ PS("type",EROTGEOM(rotg->eType));
- PS("optimize-fft",BOOL(ir->bOptFFT));
++ PS("massw",EBOOL(rotg->bMassW));
+ pr_ivec_block(fp,indent,"atom",rotg->ind,rotg->nat,TRUE);
+ pr_rvecs(fp,indent,"x_ref",rotg->x_ref,rotg->nat);
+ pr_rvec(fp,indent,"vec",rotg->vec,DIM,TRUE);
+ pr_rvec(fp,indent,"pivot",rotg->pivot,DIM,TRUE);
+ PR("rate",rotg->rate);
+ PR("k",rotg->k);
+ PR("slab_dist",rotg->slab_dist);
+ PR("min_gaussian",rotg->min_gaussian);
+ PR("epsilon",rotg->eps);
+ PS("fit_method",EROTFIT(rotg->eFittype));
+ PI("potfitangle_nstep",rotg->PotAngle_nstep);
+ PR("potfitangle_step",rotg->PotAngle_step);
+}
+
+static void pr_rot(FILE *fp,int indent,t_rot *rot)
+{
+ int g;
+
+ PI("rot_nstrout",rot->nstrout);
+ PI("rot_nstsout",rot->nstsout);
+ PI("rot_ngrp",rot->ngrp);
+ for(g=0; g<rot->ngrp; g++)
+ pr_rotgrp(fp,indent,g,&rot->grp[g]);
+}
+
+void pr_inputrec(FILE *fp,int indent,const char *title,t_inputrec *ir,
+ gmx_bool bMDPformat)
+{
+ const char *infbuf="inf";
+ int i;
+
+ if (available(fp,ir,indent,title)) {
+ if (!bMDPformat)
+ indent=pr_title(fp,indent,title);
+ PS("integrator",EI(ir->eI));
+ PSTEP("nsteps",ir->nsteps);
+ PSTEP("init-step",ir->init_step);
+ PS("ns-type",ENS(ir->ns_type));
+ PI("nstlist",ir->nstlist);
+ PI("ndelta",ir->ndelta);
+ PI("nstcomm",ir->nstcomm);
+ PS("comm-mode",ECOM(ir->comm_mode));
+ PI("nstlog",ir->nstlog);
+ PI("nstxout",ir->nstxout);
+ PI("nstvout",ir->nstvout);
+ PI("nstfout",ir->nstfout);
+ PI("nstcalcenergy",ir->nstcalcenergy);
+ PI("nstenergy",ir->nstenergy);
+ PI("nstxtcout",ir->nstxtcout);
+ PR("init-t",ir->init_t);
+ PR("delta-t",ir->delta_t);
+
+ PR("xtcprec",ir->xtcprec);
+ PI("nkx",ir->nkx);
+ PI("nky",ir->nky);
+ PI("nkz",ir->nkz);
+ PI("pme-order",ir->pme_order);
+ PR("ewald-rtol",ir->ewald_rtol);
+ PR("ewald-geometry",ir->ewald_geometry);
+ PR("epsilon-surface",ir->epsilon_surface);
- PS("bPeriodicMols",BOOL(ir->bPeriodicMols));
- PS("bContinuation",BOOL(ir->bContinuation));
- PS("bShakeSOR",BOOL(ir->bShakeSOR));
++ PS("optimize-fft",EBOOL(ir->bOptFFT));
+ PS("ePBC",EPBC(ir->ePBC));
- PS("rotation",BOOL(ir->bRot));
++ PS("bPeriodicMols",EBOOL(ir->bPeriodicMols));
++ PS("bContinuation",EBOOL(ir->bContinuation));
++ PS("bShakeSOR",EBOOL(ir->bShakeSOR));
+ PS("etc",ETCOUPLTYPE(ir->etc));
+ PI("nsttcouple",ir->nsttcouple);
+ PS("epc",EPCOUPLTYPE(ir->epc));
+ PS("epctype",EPCOUPLTYPETYPE(ir->epct));
+ PI("nstpcouple",ir->nstpcouple);
+ PR("tau-p",ir->tau_p);
+ pr_matrix(fp,indent,"ref-p",ir->ref_p,bMDPformat);
+ pr_matrix(fp,indent,"compress",ir->compress,bMDPformat);
+ PS("refcoord-scaling",EREFSCALINGTYPE(ir->refcoord_scaling));
+ if (bMDPformat)
+ fprintf(fp,"posres-com = %g %g %g\n",ir->posres_com[XX],
+ ir->posres_com[YY],ir->posres_com[ZZ]);
+ else
+ pr_rvec(fp,indent,"posres-com",ir->posres_com,DIM,TRUE);
+ if (bMDPformat)
+ fprintf(fp,"posres-comB = %g %g %g\n",ir->posres_comB[XX],
+ ir->posres_comB[YY],ir->posres_comB[ZZ]);
+ else
+ pr_rvec(fp,indent,"posres-comB",ir->posres_comB,DIM,TRUE);
+ PI("andersen-seed",ir->andersen_seed);
+ PR("rlist",ir->rlist);
+ PR("rlistlong",ir->rlistlong);
+ PR("rtpi",ir->rtpi);
+ PS("coulombtype",EELTYPE(ir->coulombtype));
+ PR("rcoulomb-switch",ir->rcoulomb_switch);
+ PR("rcoulomb",ir->rcoulomb);
+ PS("vdwtype",EVDWTYPE(ir->vdwtype));
+ PR("rvdw-switch",ir->rvdw_switch);
+ PR("rvdw",ir->rvdw);
+ if (ir->epsilon_r != 0)
+ PR("epsilon-r",ir->epsilon_r);
+ else
+ PS("epsilon-r",infbuf);
+ if (ir->epsilon_rf != 0)
+ PR("epsilon-rf",ir->epsilon_rf);
+ else
+ PS("epsilon-rf",infbuf);
+ PR("tabext",ir->tabext);
+ PS("implicit-solvent",EIMPLICITSOL(ir->implicit_solvent));
+ PS("gb-algorithm",EGBALGORITHM(ir->gb_algorithm));
+ PR("gb-epsilon-solvent",ir->gb_epsilon_solvent);
+ PI("nstgbradii",ir->nstgbradii);
+ PR("rgbradii",ir->rgbradii);
+ PR("gb-saltconc",ir->gb_saltconc);
+ PR("gb-obc-alpha",ir->gb_obc_alpha);
+ PR("gb-obc-beta",ir->gb_obc_beta);
+ PR("gb-obc-gamma",ir->gb_obc_gamma);
+ PR("gb-dielectric-offset",ir->gb_dielectric_offset);
+ PS("sa-algorithm",ESAALGORITHM(ir->gb_algorithm));
+ PR("sa-surface-tension",ir->sa_surface_tension);
+
+ PS("DispCorr",EDISPCORR(ir->eDispCorr));
+ PS("free-energy",EFEPTYPE(ir->efep));
+ PR("init-lambda",ir->init_lambda);
+ PR("delta-lambda",ir->delta_lambda);
+ if (!bMDPformat)
+ {
+ PI("n-foreign-lambda",ir->n_flambda);
+ }
+ if (ir->n_flambda > 0)
+ {
+ pr_indent(fp,indent);
+ fprintf(fp,"foreign-lambda%s",bMDPformat ? " = " : ":");
+ for(i=0; i<ir->n_flambda; i++)
+ {
+ fprintf(fp," %10g",ir->flambda[i]);
+ }
+ fprintf(fp,"\n");
+ }
+ PR("sc-alpha",ir->sc_alpha);
+ PI("sc-power",ir->sc_power);
+ PR("sc-sigma",ir->sc_sigma);
+ PR("sc-sigma-min",ir->sc_sigma_min);
+ PI("nstdhdl", ir->nstdhdl);
+ PS("separate-dhdl-file", SEPDHDLFILETYPE(ir->separate_dhdl_file));
+ PS("dhdl-derivatives", DHDLDERIVATIVESTYPE(ir->dhdl_derivatives));
+ PI("dh-hist-size", ir->dh_hist_size);
+ PD("dh-hist-spacing", ir->dh_hist_spacing);
+
+ PI("nwall",ir->nwall);
+ PS("wall-type",EWALLTYPE(ir->wall_type));
+ PI("wall-atomtype[0]",ir->wall_atomtype[0]);
+ PI("wall-atomtype[1]",ir->wall_atomtype[1]);
+ PR("wall-density[0]",ir->wall_density[0]);
+ PR("wall-density[1]",ir->wall_density[1]);
+ PR("wall-ewald-zfac",ir->wall_ewald_zfac);
+
+ PS("pull",EPULLTYPE(ir->ePull));
+ if (ir->ePull != epullNO)
+ pr_pull(fp,indent,ir->pull);
+
- PS("disre-mixed",BOOL(ir->bDisreMixed));
++ PS("rotation",EBOOL(ir->bRot));
+ if (ir->bRot)
+ pr_rot(fp,indent,ir->rot);
+
+ PS("disre",EDISRETYPE(ir->eDisre));
+ PS("disre-weighting",EDISREWEIGHTING(ir->eDisreWeighting));
- PS("adress",BOOL(ir->bAdress));
++ PS("disre-mixed",EBOOL(ir->bDisreMixed));
+ PR("dr-fc",ir->dr_fc);
+ PR("dr-tau",ir->dr_tau);
+ PR("nstdisreout",ir->nstdisreout);
+ PR("orires-fc",ir->orires_fc);
+ PR("orires-tau",ir->orires_tau);
+ PR("nstorireout",ir->nstorireout);
+
+ PR("dihre-fc",ir->dihre_fc);
+
+ PR("em-stepsize",ir->em_stepsize);
+ PR("em-tol",ir->em_tol);
+ PI("niter",ir->niter);
+ PR("fc-stepsize",ir->fc_stepsize);
+ PI("nstcgsteep",ir->nstcgsteep);
+ PI("nbfgscorr",ir->nbfgscorr);
+
+ PS("ConstAlg",ECONSTRTYPE(ir->eConstrAlg));
+ PR("shake-tol",ir->shake_tol);
+ PI("lincs-order",ir->nProjOrder);
+ PR("lincs-warnangle",ir->LincsWarnAngle);
+ PI("lincs-iter",ir->nLincsIter);
+ PR("bd-fric",ir->bd_fric);
+ PI("ld-seed",ir->ld_seed);
+ PR("cos-accel",ir->cos_accel);
+ pr_matrix(fp,indent,"deform",ir->deform,bMDPformat);
+
- PS("adress_do_hybridpairs", BOOL(ir->adress->do_hybridpairs));
++ PS("adress",EBOOL(ir->bAdress));
+ if (ir->bAdress){
+ PS("adress_type",EADRESSTYPE(ir->adress->type));
+ PR("adress_const_wf",ir->adress->const_wf);
+ PR("adress_ex_width",ir->adress->ex_width);
+ PR("adress_hy_width",ir->adress->hy_width);
+ PS("adress_interface_correction",EADRESSICTYPE(ir->adress->icor));
+ PS("adress_site",EADRESSSITETYPE(ir->adress->site));
+ PR("adress_ex_force_cap",ir->adress->ex_forcecap);
- PS("bQMMM",BOOL(ir->bQMMM));
++ PS("adress_do_hybridpairs", EBOOL(ir->adress->do_hybridpairs));
+
+ pr_rvec(fp,indent,"adress_reference_coords",ir->adress->refs,DIM,TRUE);
+ }
+ PI("userint1",ir->userint1);
+ PI("userint2",ir->userint2);
+ PI("userint3",ir->userint3);
+ PI("userint4",ir->userint4);
+ PR("userreal1",ir->userreal1);
+ PR("userreal2",ir->userreal2);
+ PR("userreal3",ir->userreal3);
+ PR("userreal4",ir->userreal4);
+ pr_grp_opts(fp,indent,"grpopts",&(ir->opts),bMDPformat);
+ pr_cosine(fp,indent,"efield-x",&(ir->ex[XX]),bMDPformat);
+ pr_cosine(fp,indent,"efield-xt",&(ir->et[XX]),bMDPformat);
+ pr_cosine(fp,indent,"efield-y",&(ir->ex[YY]),bMDPformat);
+ pr_cosine(fp,indent,"efield-yt",&(ir->et[YY]),bMDPformat);
+ pr_cosine(fp,indent,"efield-z",&(ir->ex[ZZ]),bMDPformat);
+ pr_cosine(fp,indent,"efield-zt",&(ir->et[ZZ]),bMDPformat);
++ PS("bQMMM",EBOOL(ir->bQMMM));
+ PI("QMconstraints",ir->QMconstraints);
+ PI("QMMMscheme",ir->QMMMscheme);
+ PR("scalefactor",ir->scalefactor);
+ pr_qm_opts(fp,indent,"qm-opts",&(ir->opts));
+ }
+}
+#undef PS
+#undef PR
+#undef PI
+
+static void pr_harm(FILE *fp,t_iparams *iparams,const char *r,const char *kr)
+{
+ fprintf(fp,"%sA=%12.5e, %sA=%12.5e, %sB=%12.5e, %sB=%12.5e\n",
+ r,iparams->harmonic.rA,kr,iparams->harmonic.krA,
+ r,iparams->harmonic.rB,kr,iparams->harmonic.krB);
+}
+
+void pr_iparams(FILE *fp,t_functype ftype,t_iparams *iparams)
+{
+ int i;
+ real VA[4],VB[4],*rbcA,*rbcB;
+
+ switch (ftype) {
+ case F_ANGLES:
+ case F_G96ANGLES:
+ pr_harm(fp,iparams,"th","ct");
+ break;
+ case F_CROSS_BOND_BONDS:
+ fprintf(fp,"r1e=%15.8e, r2e=%15.8e, krr=%15.8e\n",
+ iparams->cross_bb.r1e,iparams->cross_bb.r2e,
+ iparams->cross_bb.krr);
+ break;
+ case F_CROSS_BOND_ANGLES:
+ fprintf(fp,"r1e=%15.8e, r1e=%15.8e, r3e=%15.8e, krt=%15.8e\n",
+ iparams->cross_ba.r1e,iparams->cross_ba.r2e,
+ iparams->cross_ba.r3e,iparams->cross_ba.krt);
+ break;
+ case F_LINEAR_ANGLES:
+ fprintf(fp,"klinA=%15.8e, aA=%15.8e, klinB=%15.8e, aB=%15.8e\n",
+ iparams->linangle.klinA,iparams->linangle.aA,
+ iparams->linangle.klinB,iparams->linangle.aB);
+ break;
+ case F_UREY_BRADLEY:
+ fprintf(fp,"theta=%15.8e, ktheta=%15.8e, r13=%15.8e, kUB=%15.8e\n",
+ iparams->u_b.theta,iparams->u_b.ktheta,iparams->u_b.r13,iparams->u_b.kUB);
+ break;
+ case F_QUARTIC_ANGLES:
+ fprintf(fp,"theta=%15.8e",iparams->qangle.theta);
+ for(i=0; i<5; i++)
+ fprintf(fp,", c%c=%15.8e",'0'+i,iparams->qangle.c[i]);
+ fprintf(fp,"\n");
+ break;
+ case F_BHAM:
+ fprintf(fp,"a=%15.8e, b=%15.8e, c=%15.8e\n",
+ iparams->bham.a,iparams->bham.b,iparams->bham.c);
+ break;
+ case F_BONDS:
+ case F_G96BONDS:
+ case F_HARMONIC:
+ pr_harm(fp,iparams,"b0","cb");
+ break;
+ case F_IDIHS:
+ pr_harm(fp,iparams,"xi","cx");
+ break;
+ case F_MORSE:
+ fprintf(fp,"b0=%15.8e, cb=%15.8e, beta=%15.8e\n",
+ iparams->morse.b0,iparams->morse.cb,iparams->morse.beta);
+ break;
+ case F_CUBICBONDS:
+ fprintf(fp,"b0=%15.8e, kb=%15.8e, kcub=%15.8e\n",
+ iparams->cubic.b0,iparams->cubic.kb,iparams->cubic.kcub);
+ break;
+ case F_CONNBONDS:
+ fprintf(fp,"\n");
+ break;
+ case F_FENEBONDS:
+ fprintf(fp,"bm=%15.8e, kb=%15.8e\n",iparams->fene.bm,iparams->fene.kb);
+ break;
+ case F_RESTRBONDS:
+ fprintf(fp,"lowA=%15.8e, up1A=%15.8e, up2A=%15.8e, kA=%15.8e, lowB=%15.8e, up1B=%15.8e, up2B=%15.8e, kB=%15.8e,\n",
+ iparams->restraint.lowA,iparams->restraint.up1A,
+ iparams->restraint.up2A,iparams->restraint.kA,
+ iparams->restraint.lowB,iparams->restraint.up1B,
+ iparams->restraint.up2B,iparams->restraint.kB);
+ break;
+ case F_TABBONDS:
+ case F_TABBONDSNC:
+ case F_TABANGLES:
+ case F_TABDIHS:
+ fprintf(fp,"tab=%d, kA=%15.8e, kB=%15.8e\n",
+ iparams->tab.table,iparams->tab.kA,iparams->tab.kB);
+ break;
+ case F_POLARIZATION:
+ fprintf(fp,"alpha=%15.8e\n",iparams->polarize.alpha);
+ break;
+ case F_ANHARM_POL:
+ fprintf(fp,"alpha=%15.8e drcut=%15.8e khyp=%15.8e\n",
+ iparams->anharm_polarize.alpha,
+ iparams->anharm_polarize.drcut,
+ iparams->anharm_polarize.khyp);
+ break;
+ case F_THOLE_POL:
+ fprintf(fp,"a=%15.8e, alpha1=%15.8e, alpha2=%15.8e, rfac=%15.8e\n",
+ iparams->thole.a,iparams->thole.alpha1,iparams->thole.alpha2,
+ iparams->thole.rfac);
+ break;
+ case F_WATER_POL:
+ fprintf(fp,"al_x=%15.8e, al_y=%15.8e, al_z=%15.8e, rOH=%9.6f, rHH=%9.6f, rOD=%9.6f\n",
+ iparams->wpol.al_x,iparams->wpol.al_y,iparams->wpol.al_z,
+ iparams->wpol.rOH,iparams->wpol.rHH,iparams->wpol.rOD);
+ break;
+ case F_LJ:
+ fprintf(fp,"c6=%15.8e, c12=%15.8e\n",iparams->lj.c6,iparams->lj.c12);
+ break;
+ case F_LJ14:
+ fprintf(fp,"c6A=%15.8e, c12A=%15.8e, c6B=%15.8e, c12B=%15.8e\n",
+ iparams->lj14.c6A,iparams->lj14.c12A,
+ iparams->lj14.c6B,iparams->lj14.c12B);
+ break;
+ case F_LJC14_Q:
+ fprintf(fp,"fqq=%15.8e, qi=%15.8e, qj=%15.8e, c6=%15.8e, c12=%15.8e\n",
+ iparams->ljc14.fqq,
+ iparams->ljc14.qi,iparams->ljc14.qj,
+ iparams->ljc14.c6,iparams->ljc14.c12);
+ break;
+ case F_LJC_PAIRS_NB:
+ fprintf(fp,"qi=%15.8e, qj=%15.8e, c6=%15.8e, c12=%15.8e\n",
+ iparams->ljcnb.qi,iparams->ljcnb.qj,
+ iparams->ljcnb.c6,iparams->ljcnb.c12);
+ break;
+ case F_PDIHS:
+ case F_PIDIHS:
+ case F_ANGRES:
+ case F_ANGRESZ:
+ fprintf(fp,"phiA=%15.8e, cpA=%15.8e, phiB=%15.8e, cpB=%15.8e, mult=%d\n",
+ iparams->pdihs.phiA,iparams->pdihs.cpA,
+ iparams->pdihs.phiB,iparams->pdihs.cpB,
+ iparams->pdihs.mult);
+ break;
+ case F_DISRES:
+ fprintf(fp,"label=%4d, type=%1d, low=%15.8e, up1=%15.8e, up2=%15.8e, fac=%15.8e)\n",
+ iparams->disres.label,iparams->disres.type,
+ iparams->disres.low,iparams->disres.up1,
+ iparams->disres.up2,iparams->disres.kfac);
+ break;
+ case F_ORIRES:
+ fprintf(fp,"ex=%4d, label=%d, power=%4d, c=%15.8e, obs=%15.8e, kfac=%15.8e)\n",
+ iparams->orires.ex,iparams->orires.label,iparams->orires.power,
+ iparams->orires.c,iparams->orires.obs,iparams->orires.kfac);
+ break;
+ case F_DIHRES:
+ fprintf(fp,"label=%d, power=%4d phi=%15.8e, dphi=%15.8e, kfac=%15.8e)\n",
+ iparams->dihres.label,iparams->dihres.power,
+ iparams->dihres.phi,iparams->dihres.dphi,iparams->dihres.kfac);
+ break;
+ case F_POSRES:
+ fprintf(fp,"pos0A=(%15.8e,%15.8e,%15.8e), fcA=(%15.8e,%15.8e,%15.8e), pos0B=(%15.8e,%15.8e,%15.8e), fcB=(%15.8e,%15.8e,%15.8e)\n",
+ iparams->posres.pos0A[XX],iparams->posres.pos0A[YY],
+ iparams->posres.pos0A[ZZ],iparams->posres.fcA[XX],
+ iparams->posres.fcA[YY],iparams->posres.fcA[ZZ],
+ iparams->posres.pos0B[XX],iparams->posres.pos0B[YY],
+ iparams->posres.pos0B[ZZ],iparams->posres.fcB[XX],
+ iparams->posres.fcB[YY],iparams->posres.fcB[ZZ]);
+ break;
+ case F_RBDIHS:
+ for (i=0; i<NR_RBDIHS; i++)
+ fprintf(fp,"%srbcA[%d]=%15.8e",i==0?"":", ",i,iparams->rbdihs.rbcA[i]);
+ fprintf(fp,"\n");
+ for (i=0; i<NR_RBDIHS; i++)
+ fprintf(fp,"%srbcB[%d]=%15.8e",i==0?"":", ",i,iparams->rbdihs.rbcB[i]);
+ fprintf(fp,"\n");
+ break;
+ case F_FOURDIHS:
+ /* Use the OPLS -> Ryckaert-Bellemans formula backwards to get the
+ * OPLS potential constants back.
+ */
+ rbcA = iparams->rbdihs.rbcA;
+ rbcB = iparams->rbdihs.rbcB;
+
+ VA[3] = -0.25*rbcA[4];
+ VA[2] = -0.5*rbcA[3];
+ VA[1] = 4.0*VA[3]-rbcA[2];
+ VA[0] = 3.0*VA[2]-2.0*rbcA[1];
+
+ VB[3] = -0.25*rbcB[4];
+ VB[2] = -0.5*rbcB[3];
+ VB[1] = 4.0*VB[3]-rbcB[2];
+ VB[0] = 3.0*VB[2]-2.0*rbcB[1];
+
+ for (i=0; i<NR_FOURDIHS; i++)
+ fprintf(fp,"%sFourA[%d]=%15.8e",i==0?"":", ",i,VA[i]);
+ fprintf(fp,"\n");
+ for (i=0; i<NR_FOURDIHS; i++)
+ fprintf(fp,"%sFourB[%d]=%15.8e",i==0?"":", ",i,VB[i]);
+ fprintf(fp,"\n");
+ break;
+
+ case F_CONSTR:
+ case F_CONSTRNC:
+ fprintf(fp,"dA=%15.8e, dB=%15.8e\n",iparams->constr.dA,iparams->constr.dB);
+ break;
+ case F_SETTLE:
+ fprintf(fp,"doh=%15.8e, dhh=%15.8e\n",iparams->settle.doh,
+ iparams->settle.dhh);
+ break;
+ case F_VSITE2:
+ fprintf(fp,"a=%15.8e\n",iparams->vsite.a);
+ break;
+ case F_VSITE3:
+ case F_VSITE3FD:
+ case F_VSITE3FAD:
+ fprintf(fp,"a=%15.8e, b=%15.8e\n",iparams->vsite.a,iparams->vsite.b);
+ break;
+ case F_VSITE3OUT:
+ case F_VSITE4FD:
+ case F_VSITE4FDN:
+ fprintf(fp,"a=%15.8e, b=%15.8e, c=%15.8e\n",
+ iparams->vsite.a,iparams->vsite.b,iparams->vsite.c);
+ break;
+ case F_VSITEN:
+ fprintf(fp,"n=%2d, a=%15.8e\n",iparams->vsiten.n,iparams->vsiten.a);
+ break;
+ case F_GB12:
+ case F_GB13:
+ case F_GB14:
+ fprintf(fp, "sar=%15.8e, st=%15.8e, pi=%15.8e, gbr=%15.8e, bmlt=%15.8e\n",iparams->gb.sar,iparams->gb.st,iparams->gb.pi,iparams->gb.gbr,iparams->gb.bmlt);
+ break;
+ case F_CMAP:
+ fprintf(fp, "cmapA=%1d, cmapB=%1d\n",iparams->cmap.cmapA, iparams->cmap.cmapB);
+ break;
+ default:
+ gmx_fatal(FARGS,"unknown function type %d (%s) in %s line %d",
+ ftype,interaction_function[ftype].name,__FILE__,__LINE__);
+ }
+}
+
+void pr_ilist(FILE *fp,int indent,const char *title,
+ t_functype *functype,t_ilist *ilist, gmx_bool bShowNumbers)
+{
+ int i,j,k,type,ftype;
+ t_iatom *iatoms;
+
+ if (available(fp,ilist,indent,title) && ilist->nr > 0)
+ {
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"nr: %d\n",ilist->nr);
+ if (ilist->nr > 0) {
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"iatoms:\n");
+ iatoms=ilist->iatoms;
+ for (i=j=0; i<ilist->nr;) {
+#ifndef DEBUG
+ (void) pr_indent(fp,indent+INDENT);
+ type=*(iatoms++);
+ ftype=functype[type];
+ (void) fprintf(fp,"%d type=%d (%s)",
+ bShowNumbers?j:-1,bShowNumbers?type:-1,
+ interaction_function[ftype].name);
+ j++;
+ for (k=0; k<interaction_function[ftype].nratoms; k++)
+ (void) fprintf(fp," %u",*(iatoms++));
+ (void) fprintf(fp,"\n");
+ i+=1+interaction_function[ftype].nratoms;
+#else
+ fprintf(fp,"%5d%5d\n",i,iatoms[i]);
+ i++;
+#endif
+ }
+ }
+ }
+}
+
+static void pr_cmap(FILE *fp, int indent, const char *title,
+ gmx_cmap_t *cmap_grid, gmx_bool bShowNumbers)
+{
+ int i,j,nelem;
+ real dx,idx;
+
+ dx = 360.0 / cmap_grid->grid_spacing;
+ nelem = cmap_grid->grid_spacing*cmap_grid->grid_spacing;
+
+ if(available(fp,cmap_grid,indent,title))
+ {
+ fprintf(fp,"%s\n",title);
+
+ for(i=0;i<cmap_grid->ngrid;i++)
+ {
+ idx = -180.0;
+ fprintf(fp,"%8s %8s %8s %8s\n","V","dVdx","dVdy","d2dV");
+
+ fprintf(fp,"grid[%3d]={\n",bShowNumbers?i:-1);
+
+ for(j=0;j<nelem;j++)
+ {
+ if( (j%cmap_grid->grid_spacing)==0)
+ {
+ fprintf(fp,"%8.1f\n",idx);
+ idx+=dx;
+ }
+
+ fprintf(fp,"%8.3f ",cmap_grid->cmapdata[i].cmap[j*4]);
+ fprintf(fp,"%8.3f ",cmap_grid->cmapdata[i].cmap[j*4+1]);
+ fprintf(fp,"%8.3f ",cmap_grid->cmapdata[i].cmap[j*4+2]);
+ fprintf(fp,"%8.3f\n",cmap_grid->cmapdata[i].cmap[j*4+3]);
+ }
+ fprintf(fp,"\n");
+ }
+ }
+
+}
+
+void pr_ffparams(FILE *fp,int indent,const char *title,
+ gmx_ffparams_t *ffparams,
+ gmx_bool bShowNumbers)
+{
+ int i,j;
+
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"atnr=%d\n",ffparams->atnr);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"ntypes=%d\n",ffparams->ntypes);
+ for (i=0; i<ffparams->ntypes; i++) {
+ (void) pr_indent(fp,indent+INDENT);
+ (void) fprintf(fp,"functype[%d]=%s, ",
+ bShowNumbers?i:-1,
+ interaction_function[ffparams->functype[i]].name);
+ pr_iparams(fp,ffparams->functype[i],&ffparams->iparams[i]);
+ }
+ (void) pr_double(fp,indent,"reppow",ffparams->reppow);
+ (void) pr_real(fp,indent,"fudgeQQ",ffparams->fudgeQQ);
+ pr_cmap(fp,indent,"cmap",&ffparams->cmap_grid,bShowNumbers);
+}
+
+void pr_idef(FILE *fp,int indent,const char *title,t_idef *idef, gmx_bool bShowNumbers)
+{
+ int i,j;
+
+ if (available(fp,idef,indent,title)) {
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"atnr=%d\n",idef->atnr);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"ntypes=%d\n",idef->ntypes);
+ for (i=0; i<idef->ntypes; i++) {
+ (void) pr_indent(fp,indent+INDENT);
+ (void) fprintf(fp,"functype[%d]=%s, ",
+ bShowNumbers?i:-1,
+ interaction_function[idef->functype[i]].name);
+ pr_iparams(fp,idef->functype[i],&idef->iparams[i]);
+ }
+ (void) pr_real(fp,indent,"fudgeQQ",idef->fudgeQQ);
+
+ for(j=0; (j<F_NRE); j++)
+ pr_ilist(fp,indent,interaction_function[j].longname,
+ idef->functype,&idef->il[j],bShowNumbers);
+ }
+}
+
+static int pr_block_title(FILE *fp,int indent,const char *title,t_block *block)
+{
+ int i;
+
+ if (available(fp,block,indent,title))
+ {
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"nr=%d\n",block->nr);
+ }
+ return indent;
+}
+
+static int pr_blocka_title(FILE *fp,int indent,const char *title,t_blocka *block)
+{
+ int i;
+
+ if (available(fp,block,indent,title))
+ {
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"nr=%d\n",block->nr);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"nra=%d\n",block->nra);
+ }
+ return indent;
+}
+
+static void low_pr_blocka(FILE *fp,int indent,const char *title,t_blocka *block, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,block,indent,title))
+ {
+ indent=pr_blocka_title(fp,indent,title,block);
+ for (i=0; i<=block->nr; i++)
+ {
+ (void) pr_indent(fp,indent+INDENT);
+ (void) fprintf(fp,"%s->index[%d]=%u\n",
+ title,bShowNumbers?i:-1,block->index[i]);
+ }
+ for (i=0; i<block->nra; i++)
+ {
+ (void) pr_indent(fp,indent+INDENT);
+ (void) fprintf(fp,"%s->a[%d]=%u\n",
+ title,bShowNumbers?i:-1,block->a[i]);
+ }
+ }
+}
+
+void pr_block(FILE *fp,int indent,const char *title,t_block *block,gmx_bool bShowNumbers)
+{
+ int i,j,ok,size,start,end;
+
+ if (available(fp,block,indent,title))
+ {
+ indent=pr_block_title(fp,indent,title,block);
+ start=0;
+ end=start;
+ if ((ok=(block->index[start]==0))==0)
+ (void) fprintf(fp,"block->index[%d] should be 0\n",start);
+ else
+ for (i=0; i<block->nr; i++)
+ {
+ end=block->index[i+1];
+ size=pr_indent(fp,indent);
+ if (end<=start)
+ size+=fprintf(fp,"%s[%d]={}\n",title,i);
+ else
+ size+=fprintf(fp,"%s[%d]={%d..%d}\n",
+ title,bShowNumbers?i:-1,
+ bShowNumbers?start:-1,bShowNumbers?end-1:-1);
+ start=end;
+ }
+ }
+}
+
+void pr_blocka(FILE *fp,int indent,const char *title,t_blocka *block,gmx_bool bShowNumbers)
+{
+ int i,j,ok,size,start,end;
+
+ if (available(fp,block,indent,title))
+ {
+ indent=pr_blocka_title(fp,indent,title,block);
+ start=0;
+ end=start;
+ if ((ok=(block->index[start]==0))==0)
+ (void) fprintf(fp,"block->index[%d] should be 0\n",start);
+ else
+ for (i=0; i<block->nr; i++)
+ {
+ end=block->index[i+1];
+ size=pr_indent(fp,indent);
+ if (end<=start)
+ size+=fprintf(fp,"%s[%d]={",title,i);
+ else
+ size+=fprintf(fp,"%s[%d][%d..%d]={",
+ title,bShowNumbers?i:-1,
+ bShowNumbers?start:-1,bShowNumbers?end-1:-1);
+ for (j=start; j<end; j++)
+ {
+ if (j>start) size+=fprintf(fp,", ");
+ if ((size)>(USE_WIDTH))
+ {
+ (void) fprintf(fp,"\n");
+ size=pr_indent(fp,indent+INDENT);
+ }
+ size+=fprintf(fp,"%u",block->a[j]);
+ }
+ (void) fprintf(fp,"}\n");
+ start=end;
+ }
+ if ((end!=block->nra)||(!ok))
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"tables inconsistent, dumping complete tables:\n");
+ low_pr_blocka(fp,indent,title,block,bShowNumbers);
+ }
+ }
+}
+
+static void pr_strings(FILE *fp,int indent,const char *title,char ***nm,int n, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,nm,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++)
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d]={name=\"%s\"}\n",
+ title,bShowNumbers?i:-1,*(nm[i]));
+ }
+ }
+}
+
+static void pr_strings2(FILE *fp,int indent,const char *title,
+ char ***nm,char ***nmB,int n, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,nm,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++)
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d]={name=\"%s\",nameB=\"%s\"}\n",
+ title,bShowNumbers?i:-1,*(nm[i]),*(nmB[i]));
+ }
+ }
+}
+
+static void pr_resinfo(FILE *fp,int indent,const char *title,t_resinfo *resinfo,int n, gmx_bool bShowNumbers)
+{
+ int i;
+
+ if (available(fp,resinfo,indent,title))
+ {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++)
+ {
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%s[%d]={name=\"%s\", nr=%d, ic='%c'}\n",
+ title,bShowNumbers?i:-1,
+ *(resinfo[i].name),resinfo[i].nr,
+ (resinfo[i].ic == '\0') ? ' ' : resinfo[i].ic);
+ }
+ }
+}
+
+static void pr_atom(FILE *fp,int indent,const char *title,t_atom *atom,int n)
+{
+ int i,j;
+
+ if (available(fp,atom,indent,title)) {
+ indent=pr_title_n(fp,indent,title,n);
+ for (i=0; i<n; i++) {
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"%s[%6d]={type=%3d, typeB=%3d, ptype=%8s, m=%12.5e, "
+ "q=%12.5e, mB=%12.5e, qB=%12.5e, resind=%5d, atomnumber=%3d}\n",
+ title,i,atom[i].type,atom[i].typeB,ptype_str[atom[i].ptype],
+ atom[i].m,atom[i].q,atom[i].mB,atom[i].qB,
+ atom[i].resind,atom[i].atomnumber);
+ }
+ }
+}
+
+static void pr_grps(FILE *fp,int indent,const char *title,t_grps grps[],
+ char **grpname[], gmx_bool bShowNumbers)
+{
+ int i,j;
+
+ for(i=0; (i<egcNR); i++)
+ {
+ fprintf(fp,"%s[%-12s] nr=%d, name=[",title,gtypes[i],grps[i].nr);
+ for(j=0; (j<grps[i].nr); j++)
+ {
+ fprintf(fp," %s",*(grpname[grps[i].nm_ind[j]]));
+ }
+ fprintf(fp,"]\n");
+ }
+}
+
+static void pr_groups(FILE *fp,int indent,const char *title,
+ gmx_groups_t *groups,
+ gmx_bool bShowNumbers)
+{
+ int grpnr[egcNR];
+ int nat_max,i,g;
+
+ pr_grps(fp,indent,"grp",groups->grps,groups->grpname,bShowNumbers);
+ pr_strings(fp,indent,"grpname",groups->grpname,groups->ngrpname,bShowNumbers);
+
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"groups ");
+ for(g=0; g<egcNR; g++)
+ {
+ printf(" %5.5s",gtypes[g]);
+ }
+ printf("\n");
+
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"allocated ");
+ nat_max = 0;
+ for(g=0; g<egcNR; g++)
+ {
+ printf(" %5d",groups->ngrpnr[g]);
+ nat_max = max(nat_max,groups->ngrpnr[g]);
+ }
+ printf("\n");
+
+ if (nat_max == 0)
+ {
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"groupnr[%5s] =","*");
+ for(g=0; g<egcNR; g++)
+ {
+ fprintf(fp," %3d ",0);
+ }
+ fprintf(fp,"\n");
+ }
+ else
+ {
+ for(i=0; i<nat_max; i++)
+ {
+ (void) pr_indent(fp,indent);
+ fprintf(fp,"groupnr[%5d] =",i);
+ for(g=0; g<egcNR; g++)
+ {
+ fprintf(fp," %3d ",
+ groups->grpnr[g] ? groups->grpnr[g][i] : 0);
+ }
+ fprintf(fp,"\n");
+ }
+ }
+}
+
+void pr_atoms(FILE *fp,int indent,const char *title,t_atoms *atoms,
+ gmx_bool bShownumbers)
+{
+ if (available(fp,atoms,indent,title))
+ {
+ indent=pr_title(fp,indent,title);
+ pr_atom(fp,indent,"atom",atoms->atom,atoms->nr);
+ pr_strings(fp,indent,"atom",atoms->atomname,atoms->nr,bShownumbers);
+ pr_strings2(fp,indent,"type",atoms->atomtype,atoms->atomtypeB,atoms->nr,bShownumbers);
+ pr_resinfo(fp,indent,"residue",atoms->resinfo,atoms->nres,bShownumbers);
+ }
+}
+
+
+void pr_atomtypes(FILE *fp,int indent,const char *title,t_atomtypes *atomtypes,
+ gmx_bool bShowNumbers)
+{
+ int i;
+ if (available(fp,atomtypes,indent,title))
+ {
+ indent=pr_title(fp,indent,title);
+ for(i=0;i<atomtypes->nr;i++) {
+ pr_indent(fp,indent);
+ fprintf(fp,
+ "atomtype[%3d]={radius=%12.5e, volume=%12.5e, gb_radius=%12.5e, surftens=%12.5e, atomnumber=%4d, S_hct=%12.5e)}\n",
+ bShowNumbers?i:-1,atomtypes->radius[i],atomtypes->vol[i],
+ atomtypes->gb_radius[i],
+ atomtypes->surftens[i],atomtypes->atomnumber[i],atomtypes->S_hct[i]);
+ }
+ }
+}
+
+static void pr_moltype(FILE *fp,int indent,const char *title,
+ gmx_moltype_t *molt,int n,
+ gmx_ffparams_t *ffparams,
+ gmx_bool bShowNumbers)
+{
+ int j;
+
+ indent = pr_title_n(fp,indent,title,n);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"name=\"%s\"\n",*(molt->name));
+ pr_atoms(fp,indent,"atoms",&(molt->atoms),bShowNumbers);
+ pr_block(fp,indent,"cgs",&molt->cgs, bShowNumbers);
+ pr_blocka(fp,indent,"excls",&molt->excls, bShowNumbers);
+ for(j=0; (j<F_NRE); j++) {
+ pr_ilist(fp,indent,interaction_function[j].longname,
+ ffparams->functype,&molt->ilist[j],bShowNumbers);
+ }
+}
+
+static void pr_molblock(FILE *fp,int indent,const char *title,
+ gmx_molblock_t *molb,int n,
+ gmx_moltype_t *molt,
+ gmx_bool bShowNumbers)
+{
+ indent = pr_title_n(fp,indent,title,n);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"%-20s = %d \"%s\"\n",
+ "moltype",molb->type,*(molt[molb->type].name));
+ pr_int(fp,indent,"#molecules",molb->nmol);
+ pr_int(fp,indent,"#atoms_mol",molb->natoms_mol);
+ pr_int(fp,indent,"#posres_xA",molb->nposres_xA);
+ if (molb->nposres_xA > 0) {
+ pr_rvecs(fp,indent,"posres_xA",molb->posres_xA,molb->nposres_xA);
+ }
+ pr_int(fp,indent,"#posres_xB",molb->nposres_xB);
+ if (molb->nposres_xB > 0) {
+ pr_rvecs(fp,indent,"posres_xB",molb->posres_xB,molb->nposres_xB);
+ }
+}
+
+void pr_mtop(FILE *fp,int indent,const char *title,gmx_mtop_t *mtop,
+ gmx_bool bShowNumbers)
+{
+ int mt,mb;
+
+ if (available(fp,mtop,indent,title)) {
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"name=\"%s\"\n",*(mtop->name));
+ pr_int(fp,indent,"#atoms",mtop->natoms);
+ for(mb=0; mb<mtop->nmolblock; mb++) {
+ pr_molblock(fp,indent,"molblock",&mtop->molblock[mb],mb,
+ mtop->moltype,bShowNumbers);
+ }
+ pr_ffparams(fp,indent,"ffparams",&(mtop->ffparams),bShowNumbers);
+ pr_atomtypes(fp,indent,"atomtypes",&(mtop->atomtypes),bShowNumbers);
+ for(mt=0; mt<mtop->nmoltype; mt++) {
+ pr_moltype(fp,indent,"moltype",&mtop->moltype[mt],mt,
+ &mtop->ffparams,bShowNumbers);
+ }
+ pr_groups(fp,indent,"groups",&mtop->groups,bShowNumbers);
+ }
+}
+
+void pr_top(FILE *fp,int indent,const char *title,t_topology *top, gmx_bool bShowNumbers)
+{
+ if (available(fp,top,indent,title)) {
+ indent=pr_title(fp,indent,title);
+ (void) pr_indent(fp,indent);
+ (void) fprintf(fp,"name=\"%s\"\n",*(top->name));
+ pr_atoms(fp,indent,"atoms",&(top->atoms),bShowNumbers);
+ pr_atomtypes(fp,indent,"atomtypes",&(top->atomtypes),bShowNumbers);
+ pr_block(fp,indent,"cgs",&top->cgs, bShowNumbers);
+ pr_block(fp,indent,"mols",&top->mols, bShowNumbers);
+ pr_blocka(fp,indent,"excls",&top->excls, bShowNumbers);
+ pr_idef(fp,indent,"idef",&top->idef,bShowNumbers);
+ }
+}
+
+void pr_header(FILE *fp,int indent,const char *title,t_tpxheader *sh)
+{
+ char buf[22];
+
+ if (available(fp,sh,indent,title))
+ {
+ indent=pr_title(fp,indent,title);
+ pr_indent(fp,indent);
+ fprintf(fp,"bIr = %spresent\n",sh->bIr?"":"not ");
+ pr_indent(fp,indent);
+ fprintf(fp,"bBox = %spresent\n",sh->bBox?"":"not ");
+ pr_indent(fp,indent);
+ fprintf(fp,"bTop = %spresent\n",sh->bTop?"":"not ");
+ pr_indent(fp,indent);
+ fprintf(fp,"bX = %spresent\n",sh->bX?"":"not ");
+ pr_indent(fp,indent);
+ fprintf(fp,"bV = %spresent\n",sh->bV?"":"not ");
+ pr_indent(fp,indent);
+ fprintf(fp,"bF = %spresent\n",sh->bF?"":"not ");
+
+ pr_indent(fp,indent);
+ fprintf(fp,"natoms = %d\n",sh->natoms);
+ pr_indent(fp,indent);
+ fprintf(fp,"lambda = %e\n",sh->lambda);
+ }
+}
+
+void pr_commrec(FILE *fp,int indent,t_commrec *cr)
+{
+ pr_indent(fp,indent);
+ fprintf(fp,"commrec:\n");
+ indent+=2;
+ pr_indent(fp,indent);
+ fprintf(fp,"nodeid = %d\n",cr->nodeid);
+ pr_indent(fp,indent);
+ fprintf(fp,"nnodes = %d\n",cr->nnodes);
+ pr_indent(fp,indent);
+ fprintf(fp,"npmenodes = %d\n",cr->npmenodes);
+ /*
+ pr_indent(fp,indent);
+ fprintf(fp,"threadid = %d\n",cr->threadid);
+ pr_indent(fp,indent);
+ fprintf(fp,"nthreads = %d\n",cr->nthreads);
+ */
+}
--- /dev/null
- static void init_ekinstate(ekinstate_t *eks)
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROningen Mixture of Alchemy and Childrens' Stories
+ */
+/* This file is completely threadsafe - keep it that way! */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "smalloc.h"
+#include "symtab.h"
+#include "vec.h"
+#include "pbc.h"
+#include "macros.h"
+#include <string.h>
+
+#ifdef GMX_THREAD_MPI
+#include "thread_mpi.h"
+#endif
+
+/* The source code in this file should be thread-safe.
+ Please keep it that way. */
+
+
+
+static gmx_bool bOverAllocDD=FALSE;
+#ifdef GMX_THREAD_MPI
+static tMPI_Thread_mutex_t over_alloc_mutex=TMPI_THREAD_MUTEX_INITIALIZER;
+#endif
+
+
+void set_over_alloc_dd(gmx_bool set)
+{
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_lock(&over_alloc_mutex);
+ /* we just make sure that we don't set this at the same time.
+ We don't worry too much about reading this rarely-set variable */
+#endif
+ bOverAllocDD = set;
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_unlock(&over_alloc_mutex);
+#endif
+}
+
+int over_alloc_dd(int n)
+{
+ if (bOverAllocDD)
+ return OVER_ALLOC_FAC*n + 100;
+ else
+ return n;
+}
+
+int gmx_large_int_to_int(gmx_large_int_t step,const char *warn)
+{
+ int i;
+
+ i = (int)step;
+
+ if (warn != NULL && (step < INT_MIN || step > INT_MAX)) {
+ fprintf(stderr,"\nWARNING during %s:\n",warn);
+ fprintf(stderr,"step value ");
+ fprintf(stderr,gmx_large_int_pfmt,step);
+ fprintf(stderr," does not fit in int, converted to %d\n\n",i);
+ }
+
+ return i;
+}
+
+char *gmx_step_str(gmx_large_int_t i,char *buf)
+{
+ sprintf(buf,gmx_large_int_pfmt,i);
+
+ return buf;
+}
+
+void init_block(t_block *block)
+{
+ int i;
+
+ block->nr = 0;
+ block->nalloc_index = 1;
+ snew(block->index,block->nalloc_index);
+ block->index[0] = 0;
+}
+
+void init_blocka(t_blocka *block)
+{
+ int i;
+
+ block->nr = 0;
+ block->nra = 0;
+ block->nalloc_index = 1;
+ snew(block->index,block->nalloc_index);
+ block->index[0] = 0;
+ block->nalloc_a = 0;
+ block->a = NULL;
+}
+
+void init_atom(t_atoms *at)
+{
+ int i;
+
+ at->nr = 0;
+ at->nres = 0;
+ at->atom = NULL;
+ at->resinfo = NULL;
+ at->atomname = NULL;
+ at->atomtype = NULL;
+ at->atomtypeB= NULL;
+ at->pdbinfo = NULL;
+}
+
+void init_atomtypes(t_atomtypes *at)
+{
+ at->nr = 0;
+ at->radius = NULL;
+ at->vol = NULL;
+ at->atomnumber = NULL;
+ at->gb_radius = NULL;
+ at->S_hct = NULL;
+}
+
+void init_groups(gmx_groups_t *groups)
+{
+ int g;
+
+ groups->ngrpname = 0;
+ groups->grpname = NULL;
+ for(g=0; (g<egcNR); g++) {
+ groups->grps[g].nm_ind = NULL;
+ groups->ngrpnr[g] = 0;
+ groups->grpnr[g] = NULL;
+ }
+
+}
+
+void init_mtop(gmx_mtop_t *mtop)
+{
+ mtop->name = NULL;
+ mtop->nmoltype = 0;
+ mtop->moltype = NULL;
+ mtop->nmolblock = 0;
+ mtop->molblock = NULL;
+ mtop->maxres_renum = 0;
+ mtop->maxresnr = -1;
+ init_groups(&mtop->groups);
+ init_block(&mtop->mols);
+ open_symtab(&mtop->symtab);
+}
+
+void init_top (t_topology *top)
+{
+ int i;
+
+ top->name = NULL;
+ init_atom (&(top->atoms));
+ init_atomtypes(&(top->atomtypes));
+ init_block(&top->cgs);
+ init_block(&top->mols);
+ init_blocka(&top->excls);
+ open_symtab(&top->symtab);
+}
+
+void init_inputrec(t_inputrec *ir)
+{
+ memset(ir,0,(size_t)sizeof(*ir));
+}
+
+void stupid_fill_block(t_block *grp,int natom,gmx_bool bOneIndexGroup)
+{
+ int i;
+
+ if (bOneIndexGroup) {
+ grp->nalloc_index = 2;
+ snew(grp->index,grp->nalloc_index);
+ grp->index[0]=0;
+ grp->index[1]=natom;
+ grp->nr=1;
+ }
+ else {
+ grp->nalloc_index = natom+1;
+ snew(grp->index,grp->nalloc_index);
+ snew(grp->index,natom+1);
+ for(i=0; (i<=natom); i++)
+ grp->index[i]=i;
+ grp->nr=natom;
+ }
+}
+
+void stupid_fill_blocka(t_blocka *grp,int natom)
+{
+ int i;
+
+ grp->nalloc_a = natom;
+ snew(grp->a,grp->nalloc_a);
+ for(i=0; (i<natom); i++)
+ grp->a[i]=i;
+ grp->nra=natom;
+
+ grp->nalloc_index = natom + 1;
+ snew(grp->index,grp->nalloc_index);
+ for(i=0; (i<=natom); i++)
+ grp->index[i]=i;
+ grp->nr=natom;
+}
+
+void copy_blocka(const t_blocka *src,t_blocka *dest)
+{
+ int i;
+
+ dest->nr = src->nr;
+ dest->nalloc_index = dest->nr + 1;
+ snew(dest->index,dest->nalloc_index);
+ for(i=0; i<dest->nr+1; i++) {
+ dest->index[i] = src->index[i];
+ }
+ dest->nra = src->nra;
+ dest->nalloc_a = dest->nra + 1;
+ snew(dest->a,dest->nalloc_a);
+ for(i=0; i<dest->nra+1; i++) {
+ dest->a[i] = src->a[i];
+ }
+}
+
+void done_block(t_block *block)
+{
+ block->nr = 0;
+ sfree(block->index);
+ block->nalloc_index = 0;
+}
+
+void done_blocka(t_blocka *block)
+{
+ block->nr = 0;
+ block->nra = 0;
+ sfree(block->index);
+ if (block->a)
+ sfree(block->a);
+ block->nalloc_index = 0;
+ block->nalloc_a = 0;
+}
+
+void done_atom (t_atoms *at)
+{
+ at->nr = 0;
+ at->nres = 0;
+ sfree(at->atom);
+ sfree(at->resinfo);
+ sfree(at->atomname);
+ sfree(at->atomtype);
+ sfree(at->atomtypeB);
+}
+
+void done_atomtypes(t_atomtypes *atype)
+{
+ atype->nr = 0;
+ sfree(atype->radius);
+ sfree(atype->vol);
+ sfree(atype->surftens);
+ sfree(atype->atomnumber);
+ sfree(atype->gb_radius);
+ sfree(atype->S_hct);
+}
+
+void done_moltype(gmx_moltype_t *molt)
+{
+ int f;
+
+ done_atom(&molt->atoms);
+ done_block(&molt->cgs);
+ done_blocka(&molt->excls);
+
+ for(f=0; f<F_NRE; f++) {
+ sfree(molt->ilist[f].iatoms);
+ molt->ilist[f].nalloc = 0;
+ }
+}
+
+void done_molblock(gmx_molblock_t *molb)
+{
+ if (molb->nposres_xA > 0) {
+ molb->nposres_xA = 0;
+ free(molb->posres_xA);
+ }
+ if (molb->nposres_xB > 0) {
+ molb->nposres_xB = 0;
+ free(molb->posres_xB);
+ }
+}
+
+void done_mtop(gmx_mtop_t *mtop,gmx_bool bDoneSymtab)
+{
+ int i;
+
+ if (bDoneSymtab) {
+ done_symtab(&mtop->symtab);
+ }
+
+ sfree(mtop->ffparams.functype);
+ sfree(mtop->ffparams.iparams);
+
+ for(i=0; i<mtop->nmoltype; i++) {
+ done_moltype(&mtop->moltype[i]);
+ }
+ sfree(mtop->moltype);
+ for(i=0; i<mtop->nmolblock; i++) {
+ done_molblock(&mtop->molblock[i]);
+ }
+ sfree(mtop->molblock);
+ done_block(&mtop->mols);
+}
+
+void done_top(t_topology *top)
+{
+ int f;
+
+ sfree(top->idef.functype);
+ sfree(top->idef.iparams);
+ for (f = 0; f < F_NRE; ++f)
+ {
+ sfree(top->idef.il[f].iatoms);
+ top->idef.il[f].iatoms = NULL;
+ top->idef.il[f].nalloc = 0;
+ }
+
+ done_atom (&(top->atoms));
+
+ /* For GB */
+ done_atomtypes(&(top->atomtypes));
+
+ done_symtab(&(top->symtab));
+ done_block(&(top->cgs));
+ done_block(&(top->mols));
+ done_blocka(&(top->excls));
+}
+
+static void done_pullgrp(t_pullgrp *pgrp)
+{
+ sfree(pgrp->ind);
+ sfree(pgrp->ind_loc);
+ sfree(pgrp->weight);
+ sfree(pgrp->weight_loc);
+}
+
+static void done_pull(t_pull *pull)
+{
+ int i;
+
+ for(i=0; i<pull->ngrp+1; i++) {
+ done_pullgrp(pull->grp);
+ done_pullgrp(pull->dyna);
+ }
+}
+
+void done_inputrec(t_inputrec *ir)
+{
+ int m;
+
+ for(m=0; (m<DIM); m++) {
+ if (ir->ex[m].a) sfree(ir->ex[m].a);
+ if (ir->ex[m].phi) sfree(ir->ex[m].phi);
+ if (ir->et[m].a) sfree(ir->et[m].a);
+ if (ir->et[m].phi) sfree(ir->et[m].phi);
+ }
+
+ sfree(ir->opts.nrdf);
+ sfree(ir->opts.ref_t);
+ sfree(ir->opts.annealing);
+ sfree(ir->opts.anneal_npoints);
+ sfree(ir->opts.anneal_time);
+ sfree(ir->opts.anneal_temp);
+ sfree(ir->opts.tau_t);
+ sfree(ir->opts.acc);
+ sfree(ir->opts.nFreeze);
+ sfree(ir->opts.QMmethod);
+ sfree(ir->opts.QMbasis);
+ sfree(ir->opts.QMcharge);
+ sfree(ir->opts.QMmult);
+ sfree(ir->opts.bSH);
+ sfree(ir->opts.CASorbitals);
+ sfree(ir->opts.CASelectrons);
+ sfree(ir->opts.SAon);
+ sfree(ir->opts.SAoff);
+ sfree(ir->opts.SAsteps);
+ sfree(ir->opts.bOPT);
+ sfree(ir->opts.bTS);
+
+ if (ir->pull) {
+ done_pull(ir->pull);
+ sfree(ir->pull);
+ }
+}
+
- init_ekinstate(&state->ekinstate);
++static void zero_ekinstate(ekinstate_t *eks)
+{
+ eks->ekin_n = 0;
+ eks->ekinh = NULL;
+ eks->ekinf = NULL;
+ eks->ekinh_old = NULL;
+ eks->ekinscalef_nhc = NULL;
+ eks->ekinscaleh_nhc = NULL;
+ eks->vscale_nhc = NULL;
+ eks->dekindl = 0;
+ eks->mvcos = 0;
+}
+
+void init_energyhistory(energyhistory_t * enerhist)
+{
+ enerhist->nener = 0;
+
+ enerhist->ener_ave = NULL;
+ enerhist->ener_sum = NULL;
+ enerhist->ener_sum_sim = NULL;
+ enerhist->dht = NULL;
+
+ enerhist->nsteps = 0;
+ enerhist->nsum = 0;
+ enerhist->nsteps_sim = 0;
+ enerhist->nsum_sim = 0;
+
+ enerhist->dht = NULL;
+}
+
+static void done_delta_h_history(delta_h_history_t *dht)
+{
+ int i;
+
+ for(i=0; i<dht->nndh; i++)
+ {
+ sfree(dht->dh[i]);
+ }
+ sfree(dht->dh);
+ sfree(dht->ndh);
+}
+
+void done_energyhistory(energyhistory_t * enerhist)
+{
+ sfree(enerhist->ener_ave);
+ sfree(enerhist->ener_sum);
+ sfree(enerhist->ener_sum_sim);
+
+ if (enerhist->dht != NULL)
+ {
+ done_delta_h_history(enerhist->dht);
+ sfree(enerhist->dht);
+ }
+}
+
+void init_gtc_state(t_state *state, int ngtc, int nnhpres, int nhchainlength)
+{
+ int i,j;
+
+ state->ngtc = ngtc;
+ state->nnhpres = nnhpres;
+ state->nhchainlength = nhchainlength;
+ if (state->ngtc > 0)
+ {
+ snew(state->nosehoover_xi,state->nhchainlength*state->ngtc);
+ snew(state->nosehoover_vxi,state->nhchainlength*state->ngtc);
+ snew(state->therm_integral,state->ngtc);
+ for(i=0; i<state->ngtc; i++)
+ {
+ for (j=0;j<state->nhchainlength;j++)
+ {
+ state->nosehoover_xi[i*state->nhchainlength + j] = 0.0;
+ state->nosehoover_vxi[i*state->nhchainlength + j] = 0.0;
+ }
+ }
+ for(i=0; i<state->ngtc; i++) {
+ state->therm_integral[i] = 0.0;
+ }
+ }
+ else
+ {
+ state->nosehoover_xi = NULL;
+ state->nosehoover_vxi = NULL;
+ state->therm_integral = NULL;
+ }
+
+ if (state->nnhpres > 0)
+ {
+ snew(state->nhpres_xi,state->nhchainlength*nnhpres);
+ snew(state->nhpres_vxi,state->nhchainlength*nnhpres);
+ for(i=0; i<nnhpres; i++)
+ {
+ for (j=0;j<state->nhchainlength;j++)
+ {
+ state->nhpres_xi[i*nhchainlength + j] = 0.0;
+ state->nhpres_vxi[i*nhchainlength + j] = 0.0;
+ }
+ }
+ }
+ else
+ {
+ state->nhpres_xi = NULL;
+ state->nhpres_vxi = NULL;
+ }
+}
+
+
+void init_state(t_state *state, int natoms, int ngtc, int nnhpres, int nhchainlength)
+{
+ int i;
+
+ state->natoms = natoms;
+ state->nrng = 0;
+ state->flags = 0;
+ state->lambda = 0;
+ state->veta = 0;
+ clear_mat(state->box);
+ clear_mat(state->box_rel);
+ clear_mat(state->boxv);
+ clear_mat(state->pres_prev);
+ clear_mat(state->svir_prev);
+ clear_mat(state->fvir_prev);
+ init_gtc_state(state,ngtc,nnhpres,nhchainlength);
+ state->nalloc = state->natoms;
+ if (state->nalloc > 0) {
+ snew(state->x,state->nalloc);
+ snew(state->v,state->nalloc);
+ } else {
+ state->x = NULL;
+ state->v = NULL;
+ }
+ state->sd_X = NULL;
+ state->cg_p = NULL;
+
++ zero_ekinstate(&state->ekinstate);
+
+ init_energyhistory(&state->enerhist);
+
+ state->ddp_count = 0;
+ state->ddp_count_cg_gl = 0;
+ state->cg_gl = NULL;
+ state->cg_gl_nalloc = 0;
+}
+
+void done_state(t_state *state)
+{
+ if (state->nosehoover_xi) sfree(state->nosehoover_xi);
+ if (state->x) sfree(state->x);
+ if (state->v) sfree(state->v);
+ if (state->sd_X) sfree(state->sd_X);
+ if (state->cg_p) sfree(state->cg_p);
+ state->nalloc = 0;
+ if (state->cg_gl) sfree(state->cg_gl);
+ state->cg_gl_nalloc = 0;
+}
+
+static void do_box_rel(t_inputrec *ir,matrix box_rel,matrix b,gmx_bool bInit)
+{
+ int d,d2;
+
+ for(d=YY; d<=ZZ; d++) {
+ for(d2=XX; d2<=(ir->epct==epctSEMIISOTROPIC ? YY : ZZ); d2++) {
+ /* We need to check if this box component is deformed
+ * or if deformation of another component might cause
+ * changes in this component due to box corrections.
+ */
+ if (ir->deform[d][d2] == 0 &&
+ !(d == ZZ && d2 == XX && ir->deform[d][YY] != 0 &&
+ (b[YY][d2] != 0 || ir->deform[YY][d2] != 0))) {
+ if (bInit) {
+ box_rel[d][d2] = b[d][d2]/b[XX][XX];
+ } else {
+ b[d][d2] = b[XX][XX]*box_rel[d][d2];
+ }
+ }
+ }
+ }
+}
+
+void set_box_rel(t_inputrec *ir,t_state *state)
+{
+ /* Make sure the box obeys the restrictions before we fix the ratios */
+ correct_box(NULL,0,state->box,NULL);
+
+ clear_mat(state->box_rel);
+
+ if (PRESERVE_SHAPE(*ir))
+ do_box_rel(ir,state->box_rel,state->box,TRUE);
+}
+
+void preserve_box_shape(t_inputrec *ir,matrix box_rel,matrix b)
+{
+ if (PRESERVE_SHAPE(*ir))
+ do_box_rel(ir,box_rel,b,FALSE);
+}
+
+void add_t_atoms(t_atoms *atoms,int natom_extra,int nres_extra)
+{
+ int i;
+
+ if (natom_extra > 0)
+ {
+ srenew(atoms->atomname,atoms->nr+natom_extra);
+ srenew(atoms->atom,atoms->nr+natom_extra);
+ if (NULL != atoms->pdbinfo)
+ srenew(atoms->pdbinfo,atoms->nr+natom_extra);
+ if (NULL != atoms->atomtype)
+ srenew(atoms->atomtype,atoms->nr+natom_extra);
+ if (NULL != atoms->atomtypeB)
+ srenew(atoms->atomtypeB,atoms->nr+natom_extra);
+ for(i=atoms->nr; (i<atoms->nr+natom_extra); i++) {
+ atoms->atomname[i] = NULL;
+ memset(&atoms->atom[i],0,sizeof(atoms->atom[i]));
+ if (NULL != atoms->pdbinfo)
+ memset(&atoms->pdbinfo[i],0,sizeof(atoms->pdbinfo[i]));
+ if (NULL != atoms->atomtype)
+ atoms->atomtype[i] = NULL;
+ if (NULL != atoms->atomtypeB)
+ atoms->atomtypeB[i] = NULL;
+ }
+ atoms->nr += natom_extra;
+ }
+ if (nres_extra > 0)
+ {
+ srenew(atoms->resinfo,atoms->nres+nres_extra);
+ for(i=atoms->nres; (i<atoms->nres+nres_extra); i++) {
+ memset(&atoms->resinfo[i],0,sizeof(atoms->resinfo[i]));
+ }
+ atoms->nres += nres_extra;
+ }
+}
+
+void init_t_atoms(t_atoms *atoms, int natoms, gmx_bool bPdbinfo)
+{
+ atoms->nr=natoms;
+ atoms->nres=0;
+ snew(atoms->atomname,natoms);
+ atoms->atomtype=NULL;
+ atoms->atomtypeB=NULL;
+ snew(atoms->resinfo,natoms);
+ snew(atoms->atom,natoms);
+ if (bPdbinfo)
+ snew(atoms->pdbinfo,natoms);
+ else
+ atoms->pdbinfo=NULL;
+}
+
+t_atoms *copy_t_atoms(t_atoms *src)
+{
+ t_atoms *dst;
+ int i;
+
+ snew(dst,1);
+ init_t_atoms(dst,src->nr,(NULL != src->pdbinfo));
+ dst->nr = src->nr;
+ if (NULL != src->atomname)
+ snew(dst->atomname,src->nr);
+ if (NULL != src->atomtype)
+ snew(dst->atomtype,src->nr);
+ if (NULL != src->atomtypeB)
+ snew(dst->atomtypeB,src->nr);
+ for(i=0; (i<src->nr); i++) {
+ dst->atom[i] = src->atom[i];
+ if (NULL != src->pdbinfo)
+ dst->pdbinfo[i] = src->pdbinfo[i];
+ if (NULL != src->atomname)
+ dst->atomname[i] = src->atomname[i];
+ if (NULL != src->atomtype)
+ dst->atomtype[i] = src->atomtype[i];
+ if (NULL != src->atomtypeB)
+ dst->atomtypeB[i] = src->atomtypeB[i];
+ }
+ dst->nres = src->nres;
+ for(i=0; (i<src->nres); i++) {
+ dst->resinfo[i] = src->resinfo[i];
+ }
+ return dst;
+}
+
+void t_atoms_set_resinfo(t_atoms *atoms,int atom_ind,t_symtab *symtab,
+ const char *resname,int resnr,unsigned char ic,
+ int chainnum, char chainid)
+{
+ t_resinfo *ri;
+
+ ri = &atoms->resinfo[atoms->atom[atom_ind].resind];
+ ri->name = put_symtab(symtab,resname);
+ ri->rtp = NULL;
+ ri->nr = resnr;
+ ri->ic = ic;
+ ri->chainnum = chainnum;
+ ri->chainid = chainid;
+}
+
+void free_t_atoms(t_atoms *atoms,gmx_bool bFreeNames)
+{
+ int i;
+
+ if (bFreeNames) {
+ for(i=0; i<atoms->nr; i++) {
+ sfree(*atoms->atomname[i]);
+ *atoms->atomname[i]=NULL;
+ }
+ for(i=0; i<atoms->nres; i++) {
+ sfree(*atoms->resinfo[i].name);
+ *atoms->resinfo[i].name=NULL;
+ }
+ }
+ sfree(atoms->atomname);
+ /* Do we need to free atomtype and atomtypeB as well ? */
+ sfree(atoms->resinfo);
+ sfree(atoms->atom);
+ if (atoms->pdbinfo)
+ sfree(atoms->pdbinfo);
+ atoms->nr=0;
+ atoms->nres=0;
+}
+
+real max_cutoff(real cutoff1,real cutoff2)
+{
+ if (cutoff1 == 0 || cutoff2 == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return max(cutoff1,cutoff2);
+ }
+}
--- /dev/null
- #include "molfile_plugin.h"
- #include "vmddlopen.h"
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This file is part of Gromacs Copyright (c) 1991-2008
+ * David van der Spoel, Erik Lindahl, Berk Hess, University of Groningen.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org
+ *
+ * And Hey:
+ * Gnomes, ROck Monsters And Chili Sauce
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "gromacs/utility/gmx_header_config.h"
+
+
+
+/* Derived from PluginMgr.C and catdcd.c */
+
+/* PluginMgr.C: Copyright: */
+/***************************************************************************
+ *cr
+ *cr (C) Copyright 1995-2009 The Board of Trustees of the
+ *cr University of Illinois
+ *cr All Rights Reserved
+ *cr
+Developed by: Theoretical and Computational Biophysics Group
+ University of Illinois at Urbana-Champaign
+ http://www.ks.uiuc.edu/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the Software), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to
+do so, subject to the following conditions:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimers.
+
+Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimers in the documentation
+and/or other materials provided with the distribution.
+
+Neither the names of Theoretical and Computational Biophysics Group,
+University of Illinois at Urbana-Champaign, nor the names of its contributors
+may be used to endorse or promote products derived from this Software without
+specific prior written permission.
+
+THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS WITH THE SOFTWARE.
+ ***************************************************************************/
+
+/* catdcd.c: Copyright: */
+/*****************************************************************************/
+/* */
+/* (C) Copyright 2001-2005 Justin Gullingsrud and the University of Illinois.*/
+/* */
+/*****************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+/*
+ * Plugin header files; get plugin source from www.ks.uiuc.edu/Research/vmd"
+ */
- fr->bV = fr->vmdplugin.bV;
++#include "external/vmd_molfile/molfile_plugin.h"
++#include "external/vmd_molfile/vmddlopen.h"
+#ifndef GMX_NATIVE_WINDOWS
+#include <glob.h>
+#else
+#include <windows.h>
+#include <shlobj.h>
+#endif
+#include "smalloc.h"
+#include "futil.h"
+#include "vmdio.h"
+
+
+#include "types/simple.h"
+#include "vec.h"
+#include "gmxfio.h"
+
+
+typedef int (*initfunc)(void);
+typedef int (*regfunc)(void *, vmdplugin_register_cb);
+typedef int (*finifunc)(void);
+
+
+
+static int register_cb(void *v, vmdplugin_t *p) {
+ const char *key = p->name;
+ t_gmxvmdplugin *vmdplugin = (t_gmxvmdplugin*)v;
+
+ if (strcmp(key,vmdplugin->filetype)==0)
+ {
+ vmdplugin->api = (molfile_plugin_t *)p;
+ }
+ return VMDPLUGIN_SUCCESS;
+}
+
+static int load_sharedlibrary_plugins(const char *fullpath,t_gmxvmdplugin* vmdplugin) {
+ /* Open the dll; try to execute the init function. */
+ void *handle, *ifunc, *registerfunc;
+ handle = vmddlopen(fullpath);
+ if (!handle) {
+ if (debug) fprintf(debug, "\nUnable to open dynamic library %s.\n%s\n", fullpath, vmddlerror()); /*only to debug because of stdc++ erros */
+ return 0;
+ }
+
+ ifunc = vmddlsym(handle, "vmdplugin_init");
+ if (ifunc && ((initfunc)(ifunc))()) {
+ printf("\nvmdplugin_init() for %s returned an error; plugin(s) not loaded.\n", fullpath);
+ vmddlclose(handle);
+ return 0;
+ }
+
+ registerfunc = vmddlsym(handle, "vmdplugin_register");
+ if (!registerfunc) {
+ printf("\nDidn't find the register function in %s; plugin(s) not loaded.\n", fullpath);
+ vmddlclose(handle);
+ return 0;
+ } else {
+ /* Load plugins from the library.*/
+ ((regfunc)registerfunc)(vmdplugin, register_cb);
+ }
+
+ /* in case this library does not support the filetype, close it */
+ if (vmdplugin->api == NULL)
+ {
+ vmddlclose(handle);
+ }
+
+ return 1;
+}
+
+/*return: 1: success, 0: last frame, -1: error*/
+gmx_bool read_next_vmd_frame(int status,t_trxframe *fr)
+{
+ int rc,i;
+ rvec vec, angle;
+ molfile_timestep_t ts;
+
+
- rc = fr->vmdplugin.api->read_next_timestep(fr->vmdplugin.handle, fr->natoms, &ts);
++ fr->bV = fr->vmdplugin->bV;
+
+#ifdef GMX_DOUBLE
+ snew(ts.coords, fr->natoms*3);
+ if (fr->bV)
+ {
+ snew(ts.velocities, fr->natoms*3);
+ }
+#else
+ ts.coords = (float*)fr->x;
+ if (fr->bV)
+ {
+ ts.velocities = (float*)fr->v;
+ }
+#endif
+
- fr->vmdplugin.api->close_file_read(fr->vmdplugin.handle);
++ rc = fr->vmdplugin->api->read_next_timestep(fr->vmdplugin->handle, fr->natoms, &ts);
+
+ if (rc < -1) {
+ fprintf(stderr, "\nError reading input file (error code %d)\n", rc);
+ }
+ if (rc < 0)
+ {
- if (fr->vmdplugin.api->abiversion>10)
++ fr->vmdplugin->api->close_file_read(fr->vmdplugin->handle);
+ return 0;
+ }
+
+#ifdef GMX_DOUBLE
+ for (i=0;i<fr->natoms;i++)
+ {
+ fr->x[i][0] = .1*ts.coords[i*3];
+ fr->x[i][1] = .1*ts.coords[i*3+1];
+ fr->x[i][2] = .1*ts.coords[i*3+2];
+ if (fr->bV)
+ {
+ fr->v[i][0] = .1*ts.velocities[i*3];
+ fr->v[i][1] = .1*ts.velocities[i*3+1];
+ fr->v[i][2] = .1*ts.velocities[i*3+2];
+ }
+ }
+ sfree(ts.coords);
+ if (fr->bV)
+ {
+ sfree(ts.velocities);
+ }
+#else
+ for (i=0;i<fr->natoms;i++)
+ {
+ svmul(.1,fr->x[i],fr->x[i]);
+ if (fr->bV)
+ {
+ svmul(.1,fr->v[i],fr->v[i]);
+ }
+ }
+#endif
+
+ fr->bX = 1;
+ fr->bBox = 1;
+ vec[0] = .1*ts.A; vec[1] = .1*ts.B; vec[2] = .1*ts.C;
+ angle[0] = ts.alpha; angle[1] = ts.beta; angle[2] = ts.gamma;
+ matrix_convert(fr->box,vec,angle);
- if (!load_vmd_library(fn,&(fr->vmdplugin)))
++ if (fr->vmdplugin->api->abiversion>10)
+ {
+ fr->bTime = TRUE;
+ fr->time = ts.physical_time;
+ }
+ else
+ {
+ fr->bTime = FALSE;
+ }
+
+
+ return 1;
+}
+
+int load_vmd_library(const char *fn, t_gmxvmdplugin *vmdplugin)
+{
+ char pathname[GMX_PATH_MAX],filename[GMX_PATH_MAX];
+ const char *pathenv;
+ const char *err;
+ int i;
+ int ret=0;
+ char pathenv_buffer[GMX_PATH_MAX];
+#ifndef GMX_NATIVE_WINDOWS
+ glob_t globbuf;
+ const char *defpath_suffix = "/plugins/*/molfile";
+ const char *defpathenv = GMX_VMD_PLUGIN_PATH;
+#else
+ WIN32_FIND_DATA ffd;
+ HANDLE hFind = INVALID_HANDLE_VALUE;
+ char progfolder[GMX_PATH_MAX];
+ char defpathenv[GMX_PATH_MAX];
+ const char *defpath_suffix = "\\plugins\\WIN32\\molfile";
+ SHGetFolderPath(NULL,CSIDL_PROGRAM_FILES,NULL,SHGFP_TYPE_CURRENT,progfolder);
+ sprintf(defpathenv,"%s\\University of Illinois\\VMD\\plugins\\WIN32\\molfile",progfolder);
+#endif
+
+ vmdplugin->api = NULL;
+ vmdplugin->filetype = strrchr(fn,'.');
+ if (!vmdplugin->filetype)
+ {
+ return 0;
+ }
+ vmdplugin->filetype++;
+
+ /* First look for an explicit path given at run time for the
+ * plugins, then an implicit run-time path, and finally for one
+ * given at configure time. This last might be hard-coded to the
+ * default for VMD installs. */
+ pathenv = getenv("VMD_PLUGIN_PATH");
+ if (pathenv==NULL)
+ {
+ pathenv = getenv("VMDDIR");
+ if (NULL == pathenv)
+ {
+ printf("\nNeither VMD_PLUGIN_PATH or VMDDIR set. ");
+ printf("Using default location:\n%s\n",defpathenv);
+ pathenv=defpathenv;
+ }
+ else
+ {
+ printf("\nVMD_PLUGIN_PATH no set, but VMDDIR is set. ");
+#ifdef _MSC_VER
+ _snprintf_s(pathenv_buffer, sizeof(pathenv_buffer), _TRUNCATE, "%s%s", pathenv, defpath_suffix);
+#else
+ snprintf(pathenv_buffer, sizeof(pathenv_buffer), "%s%s", pathenv, defpath_suffix);
+#endif
+ printf("Using semi-default location:\n%s\n",pathenv_buffer);
+ pathenv = pathenv_buffer;
+ }
+ }
+ strncpy(pathname,pathenv,sizeof(pathname));
+#ifndef GMX_NATIVE_WINDOWS
+ strcat(pathname,"/*.so");
+ glob(pathname, 0, NULL, &globbuf);
+ if (globbuf.gl_pathc == 0)
+ {
+ printf("\nNo VMD Plugins found\n"
+ "Set the environment variable VMD_PLUGIN_PATH to the molfile folder within the\n"
+ "VMD installation.\n"
+ "The architecture (e.g. 32bit versus 64bit) of Gromacs and VMD has to match.\n");
+ return 0;
+ }
+ for (i=0; i<globbuf.gl_pathc && vmdplugin->api == NULL; i++)
+ {
+ /* FIXME: Undefined which plugin is chosen if more than one plugin
+ can read a certain file ending. Requires some additional command
+ line option or enviroment variable to specify which plugin should
+ be picked.
+ */
+ ret|=load_sharedlibrary_plugins(globbuf.gl_pathv[i],vmdplugin);
+ }
+ globfree(&globbuf);
+#else
+ strcat(pathname,"\\*.so");
+ hFind = FindFirstFile(pathname, &ffd);
+ if (INVALID_HANDLE_VALUE == hFind)
+ {
+ printf("\nNo VMD Plugins found\n");
+ return 0;
+ }
+ do
+ {
+ sprintf(filename,"%s\\%s",pathenv,ffd.cFileName);
+ ret|=load_sharedlibrary_plugins(filename,vmdplugin);
+ }
+ while (FindNextFile(hFind, &ffd ) != 0 && vmdplugin->api == NULL );
+ FindClose(hFind);
+#endif
+
+ if (!ret)
+ {
+ printf("\nCould not open any VMD library.\n");
+ err = vmddlerror();
+ if (!err)
+ {
+ printf("Compiled with dlopen?\n");
+ }
+ else
+ {
+ printf("Last error:\n%s\n",err);
+ }
+ return 0;
+ }
+
+ if (vmdplugin->api == NULL)
+ {
+ printf("\nNo plugin for %s found\n",vmdplugin->filetype);
+ return 0;
+ }
+
+ if (vmdplugin->api->abiversion < 10)
+ {
+ printf("\nPlugin and/or VMD is too old. At least VMD 1.8.6 is required.\n");
+ return 0;
+ }
+
+ printf("\nUsing VMD plugin: %s (%s)\n",vmdplugin->api->name,vmdplugin->api->prettyname);
+
+ return 1;
+
+}
+
+int read_first_vmd_frame(int *status,const char *fn,t_trxframe *fr,int flags)
+{
+ molfile_timestep_metadata_t *metadata=NULL;
+
- fr->vmdplugin.handle = fr->vmdplugin.api->open_file_read(fn, fr->vmdplugin.filetype, &fr->natoms);
++ snew(fr->vmdplugin,1);
++ if (!load_vmd_library(fn,fr->vmdplugin))
+ {
+ return 0;
+ }
+
- if (!fr->vmdplugin.handle) {
++ fr->vmdplugin->handle = fr->vmdplugin->api->open_file_read(fn, fr->vmdplugin->filetype, &fr->natoms);
+
- fr->vmdplugin.bV = 0;
- if (fr->vmdplugin.api->abiversion > 10 && fr->vmdplugin.api->read_timestep_metadata)
++ if (!fr->vmdplugin->handle) {
+ fprintf(stderr, "\nError: could not open file '%s' for reading.\n",
+ fn);
+ return 0;
+ }
+
+ if (fr->natoms == MOLFILE_NUMATOMS_UNKNOWN) {
+ fprintf(stderr, "\nFormat of file %s does not record number of atoms.\n", fn);
+ return 0;
+ } else if (fr->natoms == MOLFILE_NUMATOMS_NONE) {
+ fprintf(stderr, "\nNo atoms found by VMD plugin in file %s.\n", fn );
+ return 0;
+ } else if (fr->natoms < 1) { /*should not be reached*/
+ fprintf(stderr, "\nUnknown number of atoms %d for VMD plugin opening file %s.\n",
+ fr->natoms, fn );
+ return 0;
+ }
+
+ snew(fr->x,fr->natoms);
+
- fr->vmdplugin.api->read_timestep_metadata(fr->vmdplugin.handle, metadata);
++ fr->vmdplugin->bV = 0;
++ if (fr->vmdplugin->api->abiversion > 10 && fr->vmdplugin->api->read_timestep_metadata)
+ {
- fr->vmdplugin.bV = metadata->has_velocities;
- if (fr->vmdplugin.bV)
++ fr->vmdplugin->api->read_timestep_metadata(fr->vmdplugin->handle, metadata);
+ assert(metadata);
-
-
-
++ fr->vmdplugin->bV = metadata->has_velocities;
++ if (fr->vmdplugin->bV)
+ {
+ snew(fr->v,fr->natoms);
+ }
+ }
+ else
+ {
+ fprintf(stderr,
+ "\nThis trajectory is being read with a VMD plug-in from before VMD"
+ "\nversion 1.8, or from a trajectory that lacks time step metadata."
+ "\nEither way, GROMACS cannot tell whether the trajectory has velocities.\n");
+ }
+ return 1;
+
+}
--- /dev/null
- #include "molfile_plugin.h"
- #include "types/simple.h"
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This file is part of Gromacs Copyright (c) 1991-2008
+ * David van der Spoel, Erik Lindahl, Berk Hess, University of Groningen.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org
+ *
+ * And Hey:
+ * Gnomes, ROck Monsters And Chili Sauce
+ */
+
+#ifndef VMDIO_H_
+#define VMDIO_H_
+
- struct trxframe;
-
- typedef struct
++#include "external/vmd_molfile/molfile_plugin.h"
++#include "types/trx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
- } t_gmxvmdplugin;
++struct gmxvmdplugin
+{
+ molfile_plugin_t *api;
+ const char* filetype;
+ void* handle;
+ gmx_bool bV;
++};
+
+int read_first_vmd_frame(int *status,const char *fn, struct trxframe *fr,int flags);
+gmx_bool read_next_vmd_frame(int status,struct trxframe *fr);
+int load_vmd_library(const char *fn, t_gmxvmdplugin *vmdplugin);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* VMDIO_H_ */
--- /dev/null
- #include "gmx_cyclecounter.h"
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "domdec.h"
+#include "gmx_wallcycle.h"
++#include "gmx_cyclecounter.h"
+#include "trnio.h"
+#include "smalloc.h"
+#include "network.h"
+#include "pbc.h"
+#include "futil.h"
+#include "mdrun.h"
+#include "txtdump.h"
+#include "names.h"
+#include "mtop_util.h"
+#include "names.h"
+#include "nrjac.h"
+#include "vec.h"
+#include "gmx_ga2la.h"
+#include "xvgr.h"
+#include "gmxfio.h"
+#include "groupcoord.h"
+#include "pull_rotation.h"
+#include "gmx_sort.h"
+#include "copyrite.h"
+#include "macros.h"
+
+
+static char *RotStr = {"Enforced rotation:"};
+
+
+/* Set the minimum weight for the determination of the slab centers */
+#define WEIGHT_MIN (10*GMX_FLOAT_MIN)
+
+/* Helper structure for sorting positions along rotation vector */
+typedef struct {
+ real xcproj; /* Projection of xc on the rotation vector */
+ int ind; /* Index of xc */
+ real m; /* Mass */
+ rvec x; /* Position */
+ rvec x_ref; /* Reference position */
+} sort_along_vec_t;
+
+
+/* Enforced rotation / flexible: determine the angle of each slab */
+typedef struct gmx_slabdata
+{
+ int nat; /* Number of atoms belonging to this slab */
+ rvec *x; /* The positions belonging to this slab. In
+ general, this should be all positions of the
+ whole rotation group, but we leave those away
+ that have a small enough weight */
+ rvec *ref; /* Same for reference */
+ real *weight; /* The weight for each atom */
+} t_gmx_slabdata;
+
+
+/* Helper structure for potential fitting */
+typedef struct gmx_potfit
+{
+ real *degangle; /* Set of angles for which the potential is
+ calculated. The optimum fit is determined as
+ the angle for with the potential is minimal */
+ real *V; /* Potential for the different angles */
+ matrix *rotmat; /* Rotation matrix corresponding to the angles */
+} t_gmx_potfit;
+
+
+/* Enforced rotation data for all groups */
+typedef struct gmx_enfrot
+{
+ FILE *out_rot; /* Output file for rotation data */
+ FILE *out_torque; /* Output file for torque data */
+ FILE *out_angles; /* Output file for slab angles for flexible type */
+ FILE *out_slabs; /* Output file for slab centers */
+ int bufsize; /* Allocation size of buf */
+ rvec *xbuf; /* Coordinate buffer variable for sorting */
+ real *mbuf; /* Masses buffer variable for sorting */
+ sort_along_vec_t *data; /* Buffer variable needed for position sorting */
+ real *mpi_inbuf; /* MPI buffer */
+ real *mpi_outbuf; /* MPI buffer */
+ int mpi_bufsize; /* Allocation size of in & outbuf */
+ unsigned long Flags; /* mdrun flags */
+ gmx_bool bOut; /* Used to skip first output when appending to
+ * avoid duplicate entries in rotation outfiles */
+} t_gmx_enfrot;
+
+
+/* Global enforced rotation data for a single rotation group */
+typedef struct gmx_enfrotgrp
+{
+ real degangle; /* Rotation angle in degrees */
+ matrix rotmat; /* Rotation matrix */
+ atom_id *ind_loc; /* Local rotation indices */
+ int nat_loc; /* Number of local group atoms */
+ int nalloc_loc; /* Allocation size for ind_loc and weight_loc */
+
+ real V; /* Rotation potential for this rotation group */
+ rvec *f_rot_loc; /* Array to store the forces on the local atoms
+ resulting from enforced rotation potential */
+
+ /* Collective coordinates for the whole rotation group */
+ real *xc_ref_length; /* Length of each x_rotref vector after x_rotref
+ has been put into origin */
+ int *xc_ref_ind; /* Position of each local atom in the collective
+ array */
+ rvec xc_center; /* Center of the rotation group positions, may
+ be mass weighted */
+ rvec xc_ref_center; /* dito, for the reference positions */
+ rvec *xc; /* Current (collective) positions */
+ ivec *xc_shifts; /* Current (collective) shifts */
+ ivec *xc_eshifts; /* Extra shifts since last DD step */
+ rvec *xc_old; /* Old (collective) positions */
+ rvec *xc_norm; /* Normalized form of the current positions */
+ rvec *xc_ref_sorted; /* Reference positions (sorted in the same order
+ as xc when sorted) */
+ int *xc_sortind; /* Where is a position found after sorting? */
+ real *mc; /* Collective masses */
+ real *mc_sorted;
+ real invmass; /* one over the total mass of the rotation group */
+
+ real torque_v; /* Torque in the direction of rotation vector */
+ real angle_v; /* Actual angle of the whole rotation group */
+ /* Fixed rotation only */
+ real weight_v; /* Weights for angle determination */
+ rvec *xr_loc; /* Local reference coords, correctly rotated */
+ rvec *x_loc_pbc; /* Local current coords, correct PBC image */
+ real *m_loc; /* Masses of the current local atoms */
+
+ /* Flexible rotation only */
+ int nslabs_alloc; /* For this many slabs memory is allocated */
+ int slab_first; /* Lowermost slab for that the calculation needs
+ to be performed at a given time step */
+ int slab_last; /* Uppermost slab ... */
+ int slab_first_ref; /* First slab for which ref. center is stored */
+ int slab_last_ref; /* Last ... */
+ int slab_buffer; /* Slab buffer region around reference slabs */
+ int *firstatom; /* First relevant atom for a slab */
+ int *lastatom; /* Last relevant atom for a slab */
+ rvec *slab_center; /* Gaussian-weighted slab center */
+ rvec *slab_center_ref; /* Gaussian-weighted slab center for the
+ reference positions */
+ real *slab_weights; /* Sum of gaussian weights in a slab */
+ real *slab_torque_v; /* Torque T = r x f for each slab. */
+ /* torque_v = m.v = angular momentum in the
+ direction of v */
+ real max_beta; /* min_gaussian from inputrec->rotgrp is the
+ minimum value the gaussian must have so that
+ the force is actually evaluated max_beta is
+ just another way to put it */
+ real *gn_atom; /* Precalculated gaussians for a single atom */
+ int *gn_slabind; /* Tells to which slab each precalculated gaussian
+ belongs */
+ rvec *slab_innersumvec;/* Inner sum of the flexible2 potential per slab;
+ this is precalculated for optimization reasons */
+ t_gmx_slabdata *slab_data; /* Holds atom positions and gaussian weights
+ of atoms belonging to a slab */
+
+ /* For potential fits with varying angle: */
+ t_gmx_potfit *PotAngleFit; /* Used for fit type 'potential' */
+} t_gmx_enfrotgrp;
+
+
+/* Activate output of forces for correctness checks */
+/* #define PRINT_FORCES */
+#ifdef PRINT_FORCES
+#define PRINT_FORCE_J fprintf(stderr,"f%d = %15.8f %15.8f %15.8f\n",erg->xc_ref_ind[j],erg->f_rot_loc[j][XX], erg->f_rot_loc[j][YY], erg->f_rot_loc[j][ZZ]);
+#define PRINT_POT_TAU if (MASTER(cr)) { \
+ fprintf(stderr,"potential = %15.8f\n" "torque = %15.8f\n", erg->V, erg->torque_v); \
+ }
+#else
+#define PRINT_FORCE_J
+#define PRINT_POT_TAU
+#endif
+
+/* Shortcuts for often used queries */
+#define ISFLEX(rg) ( (rg->eType==erotgFLEX) || (rg->eType==erotgFLEXT) || (rg->eType==erotgFLEX2) || (rg->eType==erotgFLEX2T) )
+#define ISCOLL(rg) ( (rg->eType==erotgFLEX) || (rg->eType==erotgFLEXT) || (rg->eType==erotgFLEX2) || (rg->eType==erotgFLEX2T) || (rg->eType==erotgRMPF) || (rg->eType==erotgRM2PF) )
+
+
+/* Does any of the rotation groups use slab decomposition? */
+static gmx_bool HaveFlexibleGroups(t_rot *rot)
+{
+ int g;
+ t_rotgrp *rotg;
+
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ if (ISFLEX(rotg))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/* Is for any group the fit angle determined by finding the minimum of the
+ * rotation potential? */
+static gmx_bool HavePotFitGroups(t_rot *rot)
+{
+ int g;
+ t_rotgrp *rotg;
+
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ if (erotgFitPOT == rotg->eFittype)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static double** allocate_square_matrix(int dim)
+{
+ int i;
+ double** mat = NULL;
+
+
+ snew(mat, dim);
+ for(i=0; i<dim; i++)
+ snew(mat[i], dim);
+
+ return mat;
+}
+
+
+static void free_square_matrix(double** mat, int dim)
+{
+ int i;
+
+
+ for (i=0; i<dim; i++)
+ sfree(mat[i]);
+ sfree(mat);
+}
+
+
+/* Return the angle for which the potential is minimal */
+static real get_fitangle(t_rotgrp *rotg, gmx_enfrotgrp_t erg)
+{
+ int i;
+ real fitangle = -999.9;
+ real pot_min = GMX_FLOAT_MAX;
+ t_gmx_potfit *fit;
+
+
+ fit = erg->PotAngleFit;
+
+ for (i = 0; i < rotg->PotAngle_nstep; i++)
+ {
+ if (fit->V[i] < pot_min)
+ {
+ pot_min = fit->V[i];
+ fitangle = fit->degangle[i];
+ }
+ }
+
+ return fitangle;
+}
+
+
+/* Reduce potential angle fit data for this group at this time step? */
+static gmx_inline gmx_bool bPotAngle(t_rot *rot, t_rotgrp *rotg, gmx_large_int_t step)
+{
+ return ( (erotgFitPOT==rotg->eFittype) && (do_per_step(step, rot->nstsout) || do_per_step(step, rot->nstrout)) );
+}
+
+/* Reduce slab torqe data for this group at this time step? */
+static gmx_inline gmx_bool bSlabTau(t_rot *rot, t_rotgrp *rotg, gmx_large_int_t step)
+{
+ return ( (ISFLEX(rotg)) && do_per_step(step, rot->nstsout) );
+}
+
+/* Output rotation energy, torques, etc. for each rotation group */
+static void reduce_output(t_commrec *cr, t_rot *rot, real t, gmx_large_int_t step)
+{
+ int g,i,islab,nslabs=0;
+ int count; /* MPI element counter */
+ t_rotgrp *rotg;
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ real fitangle;
+ gmx_bool bFlex;
+
+
+ er=rot->enfrot;
+
+ /* Fill the MPI buffer with stuff to reduce. If items are added for reduction
+ * here, the MPI buffer size has to be enlarged also in calc_mpi_bufsize() */
+ if (PAR(cr))
+ {
+ count=0;
+ for (g=0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+ nslabs = erg->slab_last - erg->slab_first + 1;
+ er->mpi_inbuf[count++] = erg->V;
+ er->mpi_inbuf[count++] = erg->torque_v;
+ er->mpi_inbuf[count++] = erg->angle_v;
+ er->mpi_inbuf[count++] = erg->weight_v; /* weights are not needed for flex types, but this is just a single value */
+
+ if (bPotAngle(rot, rotg, step))
+ {
+ for (i = 0; i < rotg->PotAngle_nstep; i++)
+ er->mpi_inbuf[count++] = erg->PotAngleFit->V[i];
+ }
+ if (bSlabTau(rot, rotg, step))
+ {
+ for (i=0; i<nslabs; i++)
+ er->mpi_inbuf[count++] = erg->slab_torque_v[i];
+ }
+ }
+ if (count > er->mpi_bufsize)
+ gmx_fatal(FARGS, "%s MPI buffer overflow, please report this error.", RotStr);
+
+#ifdef GMX_MPI
+ MPI_Reduce(er->mpi_inbuf, er->mpi_outbuf, count, GMX_MPI_REAL, MPI_SUM, MASTERRANK(cr), cr->mpi_comm_mygroup);
+#endif
+
+ /* Copy back the reduced data from the buffer on the master */
+ if (MASTER(cr))
+ {
+ count=0;
+ for (g=0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+ nslabs = erg->slab_last - erg->slab_first + 1;
+ erg->V = er->mpi_outbuf[count++];
+ erg->torque_v = er->mpi_outbuf[count++];
+ erg->angle_v = er->mpi_outbuf[count++];
+ erg->weight_v = er->mpi_outbuf[count++];
+
+ if (bPotAngle(rot, rotg, step))
+ {
+ for (i = 0; i < rotg->PotAngle_nstep; i++)
+ erg->PotAngleFit->V[i] = er->mpi_outbuf[count++];
+ }
+ if (bSlabTau(rot, rotg, step))
+ {
+ for (i=0; i<nslabs; i++)
+ erg->slab_torque_v[i] = er->mpi_outbuf[count++];
+ }
+ }
+ }
+ }
+
+ /* Output */
+ if (MASTER(cr))
+ {
+ /* Angle and torque for each rotation group */
+ for (g=0; g < rot->ngrp; g++)
+ {
+ rotg=&rot->grp[g];
+ bFlex = ISFLEX(rotg);
+
+ erg=rotg->enfrotgrp;
+
+ /* Output to main rotation output file: */
+ if ( do_per_step(step, rot->nstrout) )
+ {
+ if (erotgFitPOT == rotg->eFittype)
+ {
+ fitangle = get_fitangle(rotg, erg);
+ }
+ else
+ {
+ if (bFlex)
+ fitangle = erg->angle_v; /* RMSD fit angle */
+ else
+ fitangle = (erg->angle_v/erg->weight_v)*180.0*M_1_PI;
+ }
+ fprintf(er->out_rot, "%12.4f", fitangle);
+ fprintf(er->out_rot, "%12.3e", erg->torque_v);
+ fprintf(er->out_rot, "%12.3e", erg->V);
+ }
+
+ if ( do_per_step(step, rot->nstsout) )
+ {
+ /* Output to torque log file: */
+ if (bFlex)
+ {
+ fprintf(er->out_torque, "%12.3e%6d", t, g);
+ for (i=erg->slab_first; i<=erg->slab_last; i++)
+ {
+ islab = i - erg->slab_first; /* slab index */
+ /* Only output if enough weight is in slab */
+ if (erg->slab_weights[islab] > rotg->min_gaussian)
+ fprintf(er->out_torque, "%6d%12.3e", i, erg->slab_torque_v[islab]);
+ }
+ fprintf(er->out_torque , "\n");
+ }
+
+ /* Output to angles log file: */
+ if (erotgFitPOT == rotg->eFittype)
+ {
+ fprintf(er->out_angles, "%12.3e%6d%12.4f", t, g, erg->degangle);
+ /* Output energies at a set of angles around the reference angle */
+ for (i = 0; i < rotg->PotAngle_nstep; i++)
+ fprintf(er->out_angles, "%12.3e", erg->PotAngleFit->V[i]);
+ fprintf(er->out_angles, "\n");
+ }
+ }
+ }
+ if ( do_per_step(step, rot->nstrout) )
+ fprintf(er->out_rot, "\n");
+ }
+}
+
+
+/* Add the forces from enforced rotation potential to the local forces.
+ * Should be called after the SR forces have been evaluated */
+extern real add_rot_forces(t_rot *rot, rvec f[], t_commrec *cr, gmx_large_int_t step, real t)
+{
+ int g,l,ii;
+ t_rotgrp *rotg;
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ real Vrot = 0.0; /* If more than one rotation group is present, Vrot
+ assembles the local parts from all groups */
+
+
+ er=rot->enfrot;
+
+ /* Loop over enforced rotation groups (usually 1, though)
+ * Apply the forces from rotation potentials */
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg=rotg->enfrotgrp;
+ Vrot += erg->V; /* add the local parts from the nodes */
+ for (l=0; l<erg->nat_loc; l++)
+ {
+ /* Get the right index of the local force */
+ ii = erg->ind_loc[l];
+ /* Add */
+ rvec_inc(f[ii],erg->f_rot_loc[l]);
+ }
+ }
+
+ /* Reduce energy,torque, angles etc. to get the sum values (per rotation group)
+ * on the master and output these values to file. */
+ if ( (do_per_step(step, rot->nstrout) || do_per_step(step, rot->nstsout)) && er->bOut)
+ reduce_output(cr, rot, t, step);
+
+ /* When appending, er->bOut is FALSE the first time to avoid duplicate entries */
+ er->bOut = TRUE;
+
+ PRINT_POT_TAU
+
+ return Vrot;
+}
+
+
+/* The Gaussian norm is chosen such that the sum of the gaussian functions
+ * over the slabs is approximately 1.0 everywhere */
+#define GAUSS_NORM 0.569917543430618
+
+
+/* Calculate the maximum beta that leads to a gaussian larger min_gaussian,
+ * also does some checks
+ */
+static double calc_beta_max(real min_gaussian, real slab_dist)
+{
+ double sigma;
+ double arg;
+
+
+ /* Actually the next two checks are already made in grompp */
+ if (slab_dist <= 0)
+ gmx_fatal(FARGS, "Slab distance of flexible rotation groups must be >=0 !");
+ if (min_gaussian <= 0)
+ gmx_fatal(FARGS, "Cutoff value for Gaussian must be > 0. (You requested %f)");
+
+ /* Define the sigma value */
+ sigma = 0.7*slab_dist;
+
+ /* Calculate the argument for the logarithm and check that the log() result is negative or 0 */
+ arg = min_gaussian/GAUSS_NORM;
+ if (arg > 1.0)
+ gmx_fatal(FARGS, "min_gaussian of flexible rotation groups must be <%g", GAUSS_NORM);
+
+ return sqrt(-2.0*sigma*sigma*log(min_gaussian/GAUSS_NORM));
+}
+
+
+static gmx_inline real calc_beta(rvec curr_x, t_rotgrp *rotg, int n)
+{
+ return iprod(curr_x, rotg->vec) - rotg->slab_dist * n;
+}
+
+
+static gmx_inline real gaussian_weight(rvec curr_x, t_rotgrp *rotg, int n)
+{
+ const real norm = GAUSS_NORM;
+ real sigma;
+
+
+ /* Define the sigma value */
+ sigma = 0.7*rotg->slab_dist;
+ /* Calculate the Gaussian value of slab n for position curr_x */
+ return norm * exp( -0.5 * sqr( calc_beta(curr_x, rotg, n)/sigma ) );
+}
+
+
+/* Returns the weight in a single slab, also calculates the Gaussian- and mass-
+ * weighted sum of positions for that slab */
+static real get_slab_weight(int j, t_rotgrp *rotg, rvec xc[], real mc[], rvec *x_weighted_sum)
+{
+ rvec curr_x; /* The position of an atom */
+ rvec curr_x_weighted; /* The gaussian-weighted position */
+ real gaussian; /* A single gaussian weight */
+ real wgauss; /* gaussian times current mass */
+ real slabweight = 0.0; /* The sum of weights in the slab */
+ int i,islab;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+ clear_rvec(*x_weighted_sum);
+
+ /* Slab index */
+ islab = j - erg->slab_first;
+
+ /* Loop over all atoms in the rotation group */
+ for (i=0; i<rotg->nat; i++)
+ {
+ copy_rvec(xc[i], curr_x);
+ gaussian = gaussian_weight(curr_x, rotg, j);
+ wgauss = gaussian * mc[i];
+ svmul(wgauss, curr_x, curr_x_weighted);
+ rvec_add(*x_weighted_sum, curr_x_weighted, *x_weighted_sum);
+ slabweight += wgauss;
+ } /* END of loop over rotation group atoms */
+
+ return slabweight;
+}
+
+
+static void get_slab_centers(
+ t_rotgrp *rotg, /* The rotation group information */
+ rvec *xc, /* The rotation group positions; will
+ typically be enfrotgrp->xc, but at first call
+ it is enfrotgrp->xc_ref */
+ real *mc, /* The masses of the rotation group atoms */
+ int g, /* The number of the rotation group */
+ real time, /* Used for output only */
+ FILE *out_slabs, /* For outputting center per slab information */
+ gmx_bool bOutStep, /* Is this an output step? */
+ gmx_bool bReference) /* If this routine is called from
+ init_rot_group we need to store
+ the reference slab centers */
+{
+ int j,islab;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Loop over slabs */
+ for (j = erg->slab_first; j <= erg->slab_last; j++)
+ {
+ islab = j - erg->slab_first;
+ erg->slab_weights[islab] = get_slab_weight(j, rotg, xc, mc, &erg->slab_center[islab]);
+
+ /* We can do the calculations ONLY if there is weight in the slab! */
+ if (erg->slab_weights[islab] > WEIGHT_MIN)
+ {
+ svmul(1.0/erg->slab_weights[islab], erg->slab_center[islab], erg->slab_center[islab]);
+ }
+ else
+ {
+ /* We need to check this here, since we divide through slab_weights
+ * in the flexible low-level routines! */
+ gmx_fatal(FARGS, "Not enough weight in slab %d. Slab center cannot be determined!", j);
+ }
+
+ /* At first time step: save the centers of the reference structure */
+ if (bReference)
+ copy_rvec(erg->slab_center[islab], erg->slab_center_ref[islab]);
+ } /* END of loop over slabs */
+
+ /* Output on the master */
+ if ( (NULL != out_slabs) && bOutStep)
+ {
+ fprintf(out_slabs, "%12.3e%6d", time, g);
+ for (j = erg->slab_first; j <= erg->slab_last; j++)
+ {
+ islab = j - erg->slab_first;
+ fprintf(out_slabs, "%6d%12.3e%12.3e%12.3e",
+ j,erg->slab_center[islab][XX],erg->slab_center[islab][YY],erg->slab_center[islab][ZZ]);
+ }
+ fprintf(out_slabs, "\n");
+ }
+}
+
+
+static void calc_rotmat(
+ rvec vec,
+ real degangle, /* Angle alpha of rotation at time t in degrees */
+ matrix rotmat) /* Rotation matrix */
+{
+ real radangle; /* Rotation angle in radians */
+ real cosa; /* cosine alpha */
+ real sina; /* sine alpha */
+ real OMcosa; /* 1 - cos(alpha) */
+ real dumxy, dumxz, dumyz; /* save computations */
+ rvec rot_vec; /* Rotate around rot_vec ... */
+
+
+ radangle = degangle * M_PI/180.0;
+ copy_rvec(vec , rot_vec );
+
+ /* Precompute some variables: */
+ cosa = cos(radangle);
+ sina = sin(radangle);
+ OMcosa = 1.0 - cosa;
+ dumxy = rot_vec[XX]*rot_vec[YY]*OMcosa;
+ dumxz = rot_vec[XX]*rot_vec[ZZ]*OMcosa;
+ dumyz = rot_vec[YY]*rot_vec[ZZ]*OMcosa;
+
+ /* Construct the rotation matrix for this rotation group: */
+ /* 1st column: */
+ rotmat[XX][XX] = cosa + rot_vec[XX]*rot_vec[XX]*OMcosa;
+ rotmat[YY][XX] = dumxy + rot_vec[ZZ]*sina;
+ rotmat[ZZ][XX] = dumxz - rot_vec[YY]*sina;
+ /* 2nd column: */
+ rotmat[XX][YY] = dumxy - rot_vec[ZZ]*sina;
+ rotmat[YY][YY] = cosa + rot_vec[YY]*rot_vec[YY]*OMcosa;
+ rotmat[ZZ][YY] = dumyz + rot_vec[XX]*sina;
+ /* 3rd column: */
+ rotmat[XX][ZZ] = dumxz + rot_vec[YY]*sina;
+ rotmat[YY][ZZ] = dumyz - rot_vec[XX]*sina;
+ rotmat[ZZ][ZZ] = cosa + rot_vec[ZZ]*rot_vec[ZZ]*OMcosa;
+
+#ifdef PRINTMATRIX
+ int iii,jjj;
+
+ for (iii=0; iii<3; iii++) {
+ for (jjj=0; jjj<3; jjj++)
+ fprintf(stderr, " %10.8f ", rotmat[iii][jjj]);
+ fprintf(stderr, "\n");
+ }
+#endif
+}
+
+
+/* Calculates torque on the rotation axis tau = position x force */
+static gmx_inline real torque(
+ rvec rotvec, /* rotation vector; MUST be normalized! */
+ rvec force, /* force */
+ rvec x, /* position of atom on which the force acts */
+ rvec pivot) /* pivot point of rotation axis */
+{
+ rvec vectmp, tau;
+
+
+ /* Subtract offset */
+ rvec_sub(x,pivot,vectmp);
+
+ /* position x force */
+ cprod(vectmp, force, tau);
+
+ /* Return the part of the torque which is parallel to the rotation vector */
+ return iprod(tau, rotvec);
+}
+
+
+/* Right-aligned output of value with standard width */
+static void print_aligned(FILE *fp, char *str)
+{
+ fprintf(fp, "%12s", str);
+}
+
+
+/* Right-aligned output of value with standard short width */
+static void print_aligned_short(FILE *fp, char *str)
+{
+ fprintf(fp, "%6s", str);
+}
+
+
+static FILE *open_output_file(const char *fn, int steps, const char what[])
+{
+ FILE *fp;
+
+
+ fp = ffopen(fn, "w");
+
+ fprintf(fp, "# Output of %s is written in intervals of %d time step%s.\n#\n",
+ what,steps, steps>1 ? "s":"");
+
+ return fp;
+}
+
+
+/* Open output file for slab center data. Call on master only */
+static FILE *open_slab_out(const char *fn, t_rot *rot, const output_env_t oenv)
+{
+ FILE *fp;
+ int g,i;
+ t_rotgrp *rotg;
+
+
+ if (rot->enfrot->Flags & MD_APPENDFILES)
+ {
+ fp = gmx_fio_fopen(fn,"a");
+ }
+ else
+ {
+ fp = open_output_file(fn, rot->nstsout, "gaussian weighted slab centers");
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ if (ISFLEX(rotg))
+ {
+ fprintf(fp, "# Rotation group %d (%s), slab distance %f nm, %s.\n",
+ g, erotg_names[rotg->eType], rotg->slab_dist,
+ rotg->bMassW? "centers of mass":"geometrical centers");
+ }
+ }
+
+ fprintf(fp, "# Reference centers are listed first (t=-1).\n");
+ fprintf(fp, "# The following columns have the syntax:\n");
+ fprintf(fp, "# ");
+ print_aligned_short(fp, "t");
+ print_aligned_short(fp, "grp");
+ /* Print legend for the first two entries only ... */
+ for (i=0; i<2; i++)
+ {
+ print_aligned_short(fp, "slab");
+ print_aligned(fp, "X center");
+ print_aligned(fp, "Y center");
+ print_aligned(fp, "Z center");
+ }
+ fprintf(fp, " ...\n");
+ fflush(fp);
+ }
+
+ return fp;
+}
+
+
+/* Adds 'buf' to 'str' */
+static void add_to_string(char **str, char *buf)
+{
+ int len;
+
+
+ len = strlen(*str) + strlen(buf) + 1;
+ srenew(*str, len);
+ strcat(*str, buf);
+}
+
+
+static void add_to_string_aligned(char **str, char *buf)
+{
+ char buf_aligned[STRLEN];
+
+ sprintf(buf_aligned, "%12s", buf);
+ add_to_string(str, buf_aligned);
+}
+
+
+/* Open output file and print some general information about the rotation groups.
+ * Call on master only */
+static FILE *open_rot_out(const char *fn, t_rot *rot, const output_env_t oenv)
+{
+ FILE *fp;
+ int g,nsets;
+ t_rotgrp *rotg;
+ const char **setname;
+ char buf[50], buf2[75];
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ gmx_bool bFlex;
+ char *LegendStr=NULL;
+
+
+ if (rot->enfrot->Flags & MD_APPENDFILES)
+ {
+ fp = gmx_fio_fopen(fn,"a");
+ }
+ else
+ {
+ fp = xvgropen(fn, "Rotation angles and energy", "Time (ps)", "angles (degrees) and energies (kJ/mol)", oenv);
+ fprintf(fp, "# Output of enforced rotation data is written in intervals of %d time step%s.\n#\n", rot->nstrout, rot->nstrout > 1 ? "s":"");
+ fprintf(fp, "# The scalar tau is the torque (kJ/mol) in the direction of the rotation vector v.\n");
+ fprintf(fp, "# To obtain the vectorial torque, multiply tau with the group's rot_vec.\n");
+ fprintf(fp, "# For flexible groups, tau(t,n) from all slabs n have been summed in a single value tau(t) here.\n");
+ fprintf(fp, "# The torques tau(t,n) are found in the rottorque.log (-rt) output file\n");
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg=rotg->enfrotgrp;
+ bFlex = ISFLEX(rotg);
+
+ fprintf(fp, "#\n");
+ fprintf(fp, "# ROTATION GROUP %d, potential type '%s':\n" , g, erotg_names[rotg->eType]);
+ fprintf(fp, "# rot_massw%d %s\n" , g, yesno_names[rotg->bMassW]);
+ fprintf(fp, "# rot_vec%d %12.5e %12.5e %12.5e\n" , g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
+ fprintf(fp, "# rot_rate%d %12.5e degrees/ps\n" , g, rotg->rate);
+ fprintf(fp, "# rot_k%d %12.5e kJ/(mol*nm^2)\n" , g, rotg->k);
+ if ( rotg->eType==erotgISO || rotg->eType==erotgPM || rotg->eType==erotgRM || rotg->eType==erotgRM2)
+ fprintf(fp, "# rot_pivot%d %12.5e %12.5e %12.5e nm\n", g, rotg->pivot[XX], rotg->pivot[YY], rotg->pivot[ZZ]);
+
+ if (bFlex)
+ {
+ fprintf(fp, "# rot_slab_distance%d %f nm\n", g, rotg->slab_dist);
+ fprintf(fp, "# rot_min_gaussian%d %12.5e\n", g, rotg->min_gaussian);
+ }
+
+ /* Output the centers of the rotation groups for the pivot-free potentials */
+ if ((rotg->eType==erotgISOPF) || (rotg->eType==erotgPMPF) || (rotg->eType==erotgRMPF) || (rotg->eType==erotgRM2PF
+ || (rotg->eType==erotgFLEXT) || (rotg->eType==erotgFLEX2T)) )
+ {
+ fprintf(fp, "# ref. grp. %d center %12.5e %12.5e %12.5e\n", g,
+ erg->xc_ref_center[XX], erg->xc_ref_center[YY], erg->xc_ref_center[ZZ]);
+
+ fprintf(fp, "# grp. %d init.center %12.5e %12.5e %12.5e\n", g,
+ erg->xc_center[XX], erg->xc_center[YY], erg->xc_center[ZZ]);
+ }
+
+ if ( (rotg->eType == erotgRM2) || (rotg->eType==erotgFLEX2) || (rotg->eType==erotgFLEX2T) )
+ {
+ fprintf(fp, "# rot_eps%d %12.5e nm^2\n", g, rotg->eps);
+ }
+ if (erotgFitPOT == rotg->eFittype)
+ {
+ fprintf(fp, "#\n");
+ fprintf(fp, "# theta_fit%d is determined by first evaluating the potential for %d angles around theta_ref%d.\n",
+ g, rotg->PotAngle_nstep, g);
+ fprintf(fp, "# The fit angle is the one with the smallest potential. It is given as the deviation\n");
+ fprintf(fp, "# from the reference angle, i.e. if theta_ref=X and theta_fit=Y, then the angle with\n");
+ fprintf(fp, "# minimal value of the potential is X+Y. Angular resolution is %g degrees.\n", rotg->PotAngle_step);
+ }
+ }
+
+ /* Print a nice legend */
+ snew(LegendStr, 1);
+ LegendStr[0] = '\0';
+ sprintf(buf, "# %6s", "time");
+ add_to_string_aligned(&LegendStr, buf);
+
+ nsets = 0;
+ snew(setname, 4*rot->ngrp);
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ sprintf(buf, "theta_ref%d", g);
+ add_to_string_aligned(&LegendStr, buf);
+
+ sprintf(buf2, "%s (degrees)", buf);
+ setname[nsets] = strdup(buf2);
+ nsets++;
+ }
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ bFlex = ISFLEX(rotg);
+
+ /* For flexible axis rotation we use RMSD fitting to determine the
+ * actual angle of the rotation group */
+ if (bFlex || erotgFitPOT == rotg->eFittype)
+ sprintf(buf, "theta_fit%d", g);
+ else
+ sprintf(buf, "theta_av%d", g);
+ add_to_string_aligned(&LegendStr, buf);
+ sprintf(buf2, "%s (degrees)", buf);
+ setname[nsets] = strdup(buf2);
+ nsets++;
+
+ sprintf(buf, "tau%d", g);
+ add_to_string_aligned(&LegendStr, buf);
+ sprintf(buf2, "%s (kJ/mol)", buf);
+ setname[nsets] = strdup(buf2);
+ nsets++;
+
+ sprintf(buf, "energy%d", g);
+ add_to_string_aligned(&LegendStr, buf);
+ sprintf(buf2, "%s (kJ/mol)", buf);
+ setname[nsets] = strdup(buf2);
+ nsets++;
+ }
+ fprintf(fp, "#\n");
+
+ if (nsets > 1)
+ xvgr_legend(fp, nsets, setname, oenv);
+ sfree(setname);
+
+ fprintf(fp, "#\n# Legend for the following data columns:\n");
+ fprintf(fp, "%s\n", LegendStr);
+ sfree(LegendStr);
+
+ fflush(fp);
+ }
+
+ return fp;
+}
+
+
+/* Call on master only */
+static FILE *open_angles_out(const char *fn, t_rot *rot, const output_env_t oenv)
+{
+ int g,i;
+ FILE *fp;
+ t_rotgrp *rotg;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ char buf[100];
+
+
+ if (rot->enfrot->Flags & MD_APPENDFILES)
+ {
+ fp = gmx_fio_fopen(fn,"a");
+ }
+ else
+ {
+ /* Open output file and write some information about it's structure: */
+ fp = open_output_file(fn, rot->nstsout, "rotation group angles");
+ fprintf(fp, "# All angles given in degrees, time in ps.\n");
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg=rotg->enfrotgrp;
+
+ /* Output for this group happens only if potential type is flexible or
+ * if fit type is potential! */
+ if ( ISFLEX(rotg) || (erotgFitPOT == rotg->eFittype) )
+ {
+ if (ISFLEX(rotg))
+ sprintf(buf, " slab distance %f nm, ", rotg->slab_dist);
+ else
+ buf[0] = '\0';
+
+ fprintf(fp, "#\n# ROTATION GROUP %d '%s',%s fit type '%s'.\n",
+ g, erotg_names[rotg->eType], buf, erotg_fitnames[rotg->eFittype]);
+
+ /* Special type of fitting using the potential minimum. This is
+ * done for the whole group only, not for the individual slabs. */
+ if (erotgFitPOT == rotg->eFittype)
+ {
+ fprintf(fp, "# To obtain theta_fit%d, the potential is evaluated for %d angles around theta_ref%d\n", g, rotg->PotAngle_nstep, g);
+ fprintf(fp, "# The fit angle in the rotation standard outfile is the one with minimal energy E(theta_fit) [kJ/mol].\n");
+ fprintf(fp, "#\n");
+ }
+
+ fprintf(fp, "# Legend for the group %d data columns:\n", g);
+ fprintf(fp, "# ");
+ print_aligned_short(fp, "time");
+ print_aligned_short(fp, "grp");
+ print_aligned(fp, "theta_ref");
+
+ if (erotgFitPOT == rotg->eFittype)
+ {
+ /* Output the set of angles around the reference angle */
+ for (i = 0; i < rotg->PotAngle_nstep; i++)
+ {
+ sprintf(buf, "E(%g)", erg->PotAngleFit->degangle[i]);
+ print_aligned(fp, buf);
+ }
+ }
+ else
+ {
+ /* Output fit angle for each slab */
+ print_aligned_short(fp, "slab");
+ print_aligned_short(fp, "atoms");
+ print_aligned(fp, "theta_fit");
+ print_aligned_short(fp, "slab");
+ print_aligned_short(fp, "atoms");
+ print_aligned(fp, "theta_fit");
+ fprintf(fp, " ...");
+ }
+ fprintf(fp, "\n");
+ }
+ }
+ fflush(fp);
+ }
+
+ return fp;
+}
+
+
+/* Open torque output file and write some information about it's structure.
+ * Call on master only */
+static FILE *open_torque_out(const char *fn, t_rot *rot, const output_env_t oenv)
+{
+ FILE *fp;
+ int g;
+ t_rotgrp *rotg;
+
+
+ if (rot->enfrot->Flags & MD_APPENDFILES)
+ {
+ fp = gmx_fio_fopen(fn,"a");
+ }
+ else
+ {
+ fp = open_output_file(fn, rot->nstsout,"torques");
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ if (ISFLEX(rotg))
+ {
+ fprintf(fp, "# Rotation group %d (%s), slab distance %f nm.\n", g, erotg_names[rotg->eType], rotg->slab_dist);
+ fprintf(fp, "# The scalar tau is the torque (kJ/mol) in the direction of the rotation vector.\n");
+ fprintf(fp, "# To obtain the vectorial torque, multiply tau with\n");
+ fprintf(fp, "# rot_vec%d %10.3e %10.3e %10.3e\n", g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
+ fprintf(fp, "#\n");
+ }
+ }
+ fprintf(fp, "# Legend for the following data columns: (tau=torque for that slab):\n");
+ fprintf(fp, "# ");
+ print_aligned_short(fp, "t");
+ print_aligned_short(fp, "grp");
+ print_aligned_short(fp, "slab");
+ print_aligned(fp, "tau");
+ print_aligned_short(fp, "slab");
+ print_aligned(fp, "tau");
+ fprintf(fp, " ...\n");
+ fflush(fp);
+ }
+
+ return fp;
+}
+
+
+static void swap_val(double* vec, int i, int j)
+{
+ double tmp = vec[j];
+
+
+ vec[j]=vec[i];
+ vec[i]=tmp;
+}
+
+
+static void swap_col(double **mat, int i, int j)
+{
+ double tmp[3] = {mat[0][j], mat[1][j], mat[2][j]};
+
+
+ mat[0][j]=mat[0][i];
+ mat[1][j]=mat[1][i];
+ mat[2][j]=mat[2][i];
+
+ mat[0][i]=tmp[0];
+ mat[1][i]=tmp[1];
+ mat[2][i]=tmp[2];
+}
+
+
+/* Eigenvectors are stored in columns of eigen_vec */
+static void diagonalize_symmetric(
+ double **matrix,
+ double **eigen_vec,
+ double eigenval[3])
+{
+ int n_rot;
+
+
+ jacobi(matrix,3,eigenval,eigen_vec,&n_rot);
+
+ /* sort in ascending order */
+ if (eigenval[0] > eigenval[1])
+ {
+ swap_val(eigenval, 0, 1);
+ swap_col(eigen_vec, 0, 1);
+ }
+ if (eigenval[1] > eigenval[2])
+ {
+ swap_val(eigenval, 1, 2);
+ swap_col(eigen_vec, 1, 2);
+ }
+ if (eigenval[0] > eigenval[1])
+ {
+ swap_val(eigenval, 0, 1);
+ swap_col(eigen_vec, 0, 1);
+ }
+}
+
+
+static void align_with_z(
+ rvec* s, /* Structure to align */
+ int natoms,
+ rvec axis)
+{
+ int i, j, k;
+ rvec zet = {0.0, 0.0, 1.0};
+ rvec rot_axis={0.0, 0.0, 0.0};
+ rvec *rotated_str=NULL;
+ real ooanorm;
+ real angle;
+ matrix rotmat;
+
+
+ snew(rotated_str, natoms);
+
+ /* Normalize the axis */
+ ooanorm = 1.0/norm(axis);
+ svmul(ooanorm, axis, axis);
+
+ /* Calculate the angle for the fitting procedure */
+ cprod(axis, zet, rot_axis);
+ angle = acos(axis[2]);
+ if (angle < 0.0)
+ angle += M_PI;
+
+ /* Calculate the rotation matrix */
+ calc_rotmat(rot_axis, angle*180.0/M_PI, rotmat);
+
+ /* Apply the rotation matrix to s */
+ for (i=0; i<natoms; i++)
+ {
+ for(j=0; j<3; j++)
+ {
+ for(k=0; k<3; k++)
+ {
+ rotated_str[i][j] += rotmat[j][k]*s[i][k];
+ }
+ }
+ }
+
+ /* Rewrite the rotated structure to s */
+ for(i=0; i<natoms; i++)
+ {
+ for(j=0; j<3; j++)
+ {
+ s[i][j]=rotated_str[i][j];
+ }
+ }
+
+ sfree(rotated_str);
+}
+
+
+static void calc_correl_matrix(rvec* Xstr, rvec* Ystr, double** Rmat, int natoms)
+{
+ int i, j, k;
+
+
+ for (i=0; i<3; i++)
+ for (j=0; j<3; j++)
+ Rmat[i][j] = 0.0;
+
+ for (i=0; i<3; i++)
+ for (j=0; j<3; j++)
+ for (k=0; k<natoms; k++)
+ Rmat[i][j] += Ystr[k][i] * Xstr[k][j];
+}
+
+
+static void weigh_coords(rvec* str, real* weight, int natoms)
+{
+ int i, j;
+
+
+ for(i=0; i<natoms; i++)
+ {
+ for(j=0; j<3; j++)
+ str[i][j] *= sqrt(weight[i]);
+ }
+}
+
+
+static real opt_angle_analytic(
+ rvec* ref_s,
+ rvec* act_s,
+ real* weight,
+ int natoms,
+ rvec ref_com,
+ rvec act_com,
+ rvec axis)
+{
+ int i, j, k;
+ rvec *ref_s_1=NULL;
+ rvec *act_s_1=NULL;
+ rvec shift;
+ double **Rmat, **RtR, **eigvec;
+ double eigval[3];
+ double V[3][3], WS[3][3];
+ double rot_matrix[3][3];
+ double opt_angle;
+
+
+ /* Do not change the original coordinates */
+ snew(ref_s_1, natoms);
+ snew(act_s_1, natoms);
+ for(i=0; i<natoms; i++)
+ {
+ copy_rvec(ref_s[i], ref_s_1[i]);
+ copy_rvec(act_s[i], act_s_1[i]);
+ }
+
+ /* Translate the structures to the origin */
+ shift[XX] = -ref_com[XX];
+ shift[YY] = -ref_com[YY];
+ shift[ZZ] = -ref_com[ZZ];
+ translate_x(ref_s_1, natoms, shift);
+
+ shift[XX] = -act_com[XX];
+ shift[YY] = -act_com[YY];
+ shift[ZZ] = -act_com[ZZ];
+ translate_x(act_s_1, natoms, shift);
+
+ /* Align rotation axis with z */
+ align_with_z(ref_s_1, natoms, axis);
+ align_with_z(act_s_1, natoms, axis);
+
+ /* Correlation matrix */
+ Rmat = allocate_square_matrix(3);
+
+ for (i=0; i<natoms; i++)
+ {
+ ref_s_1[i][2]=0.0;
+ act_s_1[i][2]=0.0;
+ }
+
+ /* Weight positions with sqrt(weight) */
+ if (NULL != weight)
+ {
+ weigh_coords(ref_s_1, weight, natoms);
+ weigh_coords(act_s_1, weight, natoms);
+ }
+
+ /* Calculate correlation matrices R=YXt (X=ref_s; Y=act_s) */
+ calc_correl_matrix(ref_s_1, act_s_1, Rmat, natoms);
+
+ /* Calculate RtR */
+ RtR = allocate_square_matrix(3);
+ for (i=0; i<3; i++)
+ {
+ for (j=0; j<3; j++)
+ {
+ for (k=0; k<3; k++)
+ {
+ RtR[i][j] += Rmat[k][i] * Rmat[k][j];
+ }
+ }
+ }
+ /* Diagonalize RtR */
+ snew(eigvec,3);
+ for (i=0; i<3; i++)
+ snew(eigvec[i],3);
+
+ diagonalize_symmetric(RtR, eigvec, eigval);
+ swap_col(eigvec,0,1);
+ swap_col(eigvec,1,2);
+ swap_val(eigval,0,1);
+ swap_val(eigval,1,2);
+
+ /* Calculate V */
+ for(i=0; i<3; i++)
+ {
+ for(j=0; j<3; j++)
+ {
+ V[i][j] = 0.0;
+ WS[i][j] = 0.0;
+ }
+ }
+
+ for (i=0; i<2; i++)
+ for (j=0; j<2; j++)
+ WS[i][j] = eigvec[i][j] / sqrt(eigval[j]);
+
+ for (i=0; i<3; i++)
+ {
+ for (j=0; j<3; j++)
+ {
+ for (k=0; k<3; k++)
+ {
+ V[i][j] += Rmat[i][k]*WS[k][j];
+ }
+ }
+ }
+ free_square_matrix(Rmat, 3);
+
+ /* Calculate optimal rotation matrix */
+ for (i=0; i<3; i++)
+ for (j=0; j<3; j++)
+ rot_matrix[i][j] = 0.0;
+
+ for (i=0; i<3; i++)
+ {
+ for(j=0; j<3; j++)
+ {
+ for(k=0; k<3; k++){
+ rot_matrix[i][j] += eigvec[i][k]*V[j][k];
+ }
+ }
+ }
+ rot_matrix[2][2] = 1.0;
+
+ /* In some cases abs(rot_matrix[0][0]) can be slighly larger
+ * than unity due to numerical inacurracies. To be able to calculate
+ * the acos function, we put these values back in range. */
+ if (rot_matrix[0][0] > 1.0)
+ {
+ rot_matrix[0][0] = 1.0;
+ }
+ else if (rot_matrix[0][0] < -1.0)
+ {
+ rot_matrix[0][0] = -1.0;
+ }
+
+ /* Determine the optimal rotation angle: */
+ opt_angle = (-1.0)*acos(rot_matrix[0][0])*180.0/M_PI;
+ if (rot_matrix[0][1] < 0.0)
+ opt_angle = (-1.0)*opt_angle;
+
+ /* Give back some memory */
+ free_square_matrix(RtR, 3);
+ sfree(ref_s_1);
+ sfree(act_s_1);
+ for (i=0; i<3; i++)
+ sfree(eigvec[i]);
+ sfree(eigvec);
+
+ return (real) opt_angle;
+}
+
+
+/* Determine angle of the group by RMSD fit to the reference */
+/* Not parallelized, call this routine only on the master */
+static real flex_fit_angle(t_rotgrp *rotg)
+{
+ int i;
+ rvec *fitcoords=NULL;
+ rvec center; /* Center of positions passed to the fit routine */
+ real fitangle; /* Angle of the rotation group derived by fitting */
+ rvec coord;
+ real scal;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Get the center of the rotation group.
+ * Note, again, erg->xc has been sorted in do_flexible */
+ get_center(erg->xc, erg->mc_sorted, rotg->nat, center);
+
+ /* === Determine the optimal fit angle for the rotation group === */
+ if (rotg->eFittype == erotgFitNORM)
+ {
+ /* Normalize every position to it's reference length */
+ for (i=0; i<rotg->nat; i++)
+ {
+ /* Put the center of the positions into the origin */
+ rvec_sub(erg->xc[i], center, coord);
+ /* Determine the scaling factor for the length: */
+ scal = erg->xc_ref_length[erg->xc_sortind[i]] / norm(coord);
+ /* Get position, multiply with the scaling factor and save */
+ svmul(scal, coord, erg->xc_norm[i]);
+ }
+ fitcoords = erg->xc_norm;
+ }
+ else
+ {
+ fitcoords = erg->xc;
+ }
+ /* From the point of view of the current positions, the reference has rotated
+ * backwards. Since we output the angle relative to the fixed reference,
+ * we need the minus sign. */
+ fitangle = -opt_angle_analytic(erg->xc_ref_sorted, fitcoords, erg->mc_sorted,
+ rotg->nat, erg->xc_ref_center, center, rotg->vec);
+
+ return fitangle;
+}
+
+
+/* Determine actual angle of each slab by RMSD fit to the reference */
+/* Not parallelized, call this routine only on the master */
+static void flex_fit_angle_perslab(
+ int g,
+ t_rotgrp *rotg,
+ double t,
+ real degangle,
+ FILE *fp)
+{
+ int i,l,n,islab,ind;
+ rvec curr_x, ref_x;
+ rvec act_center; /* Center of actual positions that are passed to the fit routine */
+ rvec ref_center; /* Same for the reference positions */
+ real fitangle; /* Angle of a slab derived from an RMSD fit to
+ * the reference structure at t=0 */
+ t_gmx_slabdata *sd;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ real OOm_av; /* 1/average_mass of a rotation group atom */
+ real m_rel; /* Relative mass of a rotation group atom */
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Average mass of a rotation group atom: */
+ OOm_av = erg->invmass*rotg->nat;
+
+ /**********************************/
+ /* First collect the data we need */
+ /**********************************/
+
+ /* Collect the data for the individual slabs */
+ for (n = erg->slab_first; n <= erg->slab_last; n++)
+ {
+ islab = n - erg->slab_first; /* slab index */
+ sd = &(rotg->enfrotgrp->slab_data[islab]);
+ sd->nat = erg->lastatom[islab]-erg->firstatom[islab]+1;
+ ind = 0;
+
+ /* Loop over the relevant atoms in the slab */
+ for (l=erg->firstatom[islab]; l<=erg->lastatom[islab]; l++)
+ {
+ /* Current position of this atom: x[ii][XX/YY/ZZ] */
+ copy_rvec(erg->xc[l], curr_x);
+
+ /* The (unrotated) reference position of this atom is copied to ref_x.
+ * Beware, the xc coords have been sorted in do_flexible */
+ copy_rvec(erg->xc_ref_sorted[l], ref_x);
+
+ /* Save data for doing angular RMSD fit later */
+ /* Save the current atom position */
+ copy_rvec(curr_x, sd->x[ind]);
+ /* Save the corresponding reference position */
+ copy_rvec(ref_x , sd->ref[ind]);
+
+ /* Maybe also mass-weighting was requested. If yes, additionally
+ * multiply the weights with the relative mass of the atom. If not,
+ * multiply with unity. */
+ m_rel = erg->mc_sorted[l]*OOm_av;
+
+ /* Save the weight for this atom in this slab */
+ sd->weight[ind] = gaussian_weight(curr_x, rotg, n) * m_rel;
+
+ /* Next atom in this slab */
+ ind++;
+ }
+ }
+
+ /******************************/
+ /* Now do the fit calculation */
+ /******************************/
+
+ fprintf(fp, "%12.3e%6d%12.3f", t, g, degangle);
+
+ /* === Now do RMSD fitting for each slab === */
+ /* We require at least SLAB_MIN_ATOMS in a slab, such that the fit makes sense. */
+#define SLAB_MIN_ATOMS 4
+
+ for (n = erg->slab_first; n <= erg->slab_last; n++)
+ {
+ islab = n - erg->slab_first; /* slab index */
+ sd = &(rotg->enfrotgrp->slab_data[islab]);
+ if (sd->nat >= SLAB_MIN_ATOMS)
+ {
+ /* Get the center of the slabs reference and current positions */
+ get_center(sd->ref, sd->weight, sd->nat, ref_center);
+ get_center(sd->x , sd->weight, sd->nat, act_center);
+ if (rotg->eFittype == erotgFitNORM)
+ {
+ /* Normalize every position to it's reference length
+ * prior to performing the fit */
+ for (i=0; i<sd->nat;i++) /* Center */
+ {
+ rvec_dec(sd->ref[i], ref_center);
+ rvec_dec(sd->x[i] , act_center);
+ /* Normalize x_i such that it gets the same length as ref_i */
+ svmul( norm(sd->ref[i])/norm(sd->x[i]), sd->x[i], sd->x[i] );
+ }
+ /* We already subtracted the centers */
+ clear_rvec(ref_center);
+ clear_rvec(act_center);
+ }
+ fitangle = -opt_angle_analytic(sd->ref, sd->x, sd->weight, sd->nat,
+ ref_center, act_center, rotg->vec);
+ fprintf(fp, "%6d%6d%12.3f", n, sd->nat, fitangle);
+ }
+ }
+ fprintf(fp , "\n");
+
+#undef SLAB_MIN_ATOMS
+}
+
+
+/* Shift x with is */
+static gmx_inline void shift_single_coord(matrix box, rvec x, const ivec is)
+{
+ int tx,ty,tz;
+
+
+ tx=is[XX];
+ ty=is[YY];
+ tz=is[ZZ];
+
+ if(TRICLINIC(box))
+ {
+ x[XX] += tx*box[XX][XX]+ty*box[YY][XX]+tz*box[ZZ][XX];
+ x[YY] += ty*box[YY][YY]+tz*box[ZZ][YY];
+ x[ZZ] += tz*box[ZZ][ZZ];
+ } else
+ {
+ x[XX] += tx*box[XX][XX];
+ x[YY] += ty*box[YY][YY];
+ x[ZZ] += tz*box[ZZ][ZZ];
+ }
+}
+
+
+/* Determine the 'home' slab of this atom which is the
+ * slab with the highest Gaussian weight of all */
+#define round(a) (int)(a+0.5)
+static gmx_inline int get_homeslab(
+ rvec curr_x, /* The position for which the home slab shall be determined */
+ rvec rotvec, /* The rotation vector */
+ real slabdist) /* The slab distance */
+{
+ real dist;
+
+
+ /* The distance of the atom to the coordinate center (where the
+ * slab with index 0) is */
+ dist = iprod(rotvec, curr_x);
+
+ return round(dist / slabdist);
+}
+
+
+/* For a local atom determine the relevant slabs, i.e. slabs in
+ * which the gaussian is larger than min_gaussian
+ */
+static int get_single_atom_gaussians(
+ rvec curr_x,
+ t_rotgrp *rotg)
+{
+ int slab, homeslab;
+ real g;
+ int count = 0;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Determine the 'home' slab of this atom: */
+ homeslab = get_homeslab(curr_x, rotg->vec, rotg->slab_dist);
+
+ /* First determine the weight in the atoms home slab: */
+ g = gaussian_weight(curr_x, rotg, homeslab);
+
+ erg->gn_atom[count] = g;
+ erg->gn_slabind[count] = homeslab;
+ count++;
+
+
+ /* Determine the max slab */
+ slab = homeslab;
+ while (g > rotg->min_gaussian)
+ {
+ slab++;
+ g = gaussian_weight(curr_x, rotg, slab);
+ erg->gn_slabind[count]=slab;
+ erg->gn_atom[count]=g;
+ count++;
+ }
+ count--;
+
+ /* Determine the max slab */
+ slab = homeslab;
+ do
+ {
+ slab--;
+ g = gaussian_weight(curr_x, rotg, slab);
+ erg->gn_slabind[count]=slab;
+ erg->gn_atom[count]=g;
+ count++;
+ }
+ while (g > rotg->min_gaussian);
+ count--;
+
+ return count;
+}
+
+
+static void flex2_precalc_inner_sum(t_rotgrp *rotg)
+{
+ int i,n,islab;
+ rvec xi; /* positions in the i-sum */
+ rvec xcn, ycn; /* the current and the reference slab centers */
+ real gaussian_xi;
+ rvec yi0;
+ rvec rin; /* Helper variables */
+ real fac,fac2;
+ rvec innersumvec;
+ real OOpsii,OOpsiistar;
+ real sin_rin; /* s_ii.r_ii */
+ rvec s_in,tmpvec,tmpvec2;
+ real mi,wi; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+ N_M = rotg->nat * erg->invmass;
+
+ /* Loop over all slabs that contain something */
+ for (n=erg->slab_first; n <= erg->slab_last; n++)
+ {
+ islab = n - erg->slab_first; /* slab index */
+
+ /* The current center of this slab is saved in xcn: */
+ copy_rvec(erg->slab_center[islab], xcn);
+ /* ... and the reference center in ycn: */
+ copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+
+ /*** D. Calculate the whole inner sum used for second and third sum */
+ /* For slab n, we need to loop over all atoms i again. Since we sorted
+ * the atoms with respect to the rotation vector, we know that it is sufficient
+ * to calculate from firstatom to lastatom only. All other contributions will
+ * be very small. */
+ clear_rvec(innersumvec);
+ for (i = erg->firstatom[islab]; i <= erg->lastatom[islab]; i++)
+ {
+ /* Coordinate xi of this atom */
+ copy_rvec(erg->xc[i],xi);
+
+ /* The i-weights */
+ gaussian_xi = gaussian_weight(xi,rotg,n);
+ mi = erg->mc_sorted[i]; /* need the sorted mass here */
+ wi = N_M*mi;
+
+ /* Calculate rin */
+ copy_rvec(erg->xc_ref_sorted[i],yi0); /* Reference position yi0 */
+ rvec_sub(yi0, ycn, tmpvec2); /* tmpvec2 = yi0 - ycn */
+ mvmul(erg->rotmat, tmpvec2, rin); /* rin = Omega.(yi0 - ycn) */
+
+ /* Calculate psi_i* and sin */
+ rvec_sub(xi, xcn, tmpvec2); /* tmpvec2 = xi - xcn */
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec = v x (xi - xcn) */
+ OOpsiistar = norm2(tmpvec)+rotg->eps; /* OOpsii* = 1/psii* = |v x (xi-xcn)|^2 + eps */
+ OOpsii = norm(tmpvec); /* OOpsii = 1 / psii = |v x (xi - xcn)| */
+
+ /* v x (xi - xcn) */
+ unitv(tmpvec, s_in); /* sin = ---------------- */
+ /* |v x (xi - xcn)| */
+
+ sin_rin=iprod(s_in,rin); /* sin_rin = sin . rin */
+
+ /* Now the whole sum */
+ fac = OOpsii/OOpsiistar;
+ svmul(fac, rin, tmpvec);
+ fac2 = fac*fac*OOpsii;
+ svmul(fac2*sin_rin, s_in, tmpvec2);
+ rvec_dec(tmpvec, tmpvec2);
+
+ svmul(wi*gaussian_xi*sin_rin, tmpvec, tmpvec2);
+
+ rvec_inc(innersumvec,tmpvec2);
+ } /* now we have the inner sum, used both for sum2 and sum3 */
+
+ /* Save it to be used in do_flex2_lowlevel */
+ copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
+ } /* END of loop over slabs */
+}
+
+
+static void flex_precalc_inner_sum(t_rotgrp *rotg)
+{
+ int i,n,islab;
+ rvec xi; /* position */
+ rvec xcn, ycn; /* the current and the reference slab centers */
+ rvec qin,rin; /* q_i^n and r_i^n */
+ real bin;
+ rvec tmpvec;
+ rvec innersumvec; /* Inner part of sum_n2 */
+ real gaussian_xi; /* Gaussian weight gn(xi) */
+ real mi,wi; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+ N_M = rotg->nat * erg->invmass;
+
+ /* Loop over all slabs that contain something */
+ for (n=erg->slab_first; n <= erg->slab_last; n++)
+ {
+ islab = n - erg->slab_first; /* slab index */
+
+ /* The current center of this slab is saved in xcn: */
+ copy_rvec(erg->slab_center[islab], xcn);
+ /* ... and the reference center in ycn: */
+ copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+
+ /* For slab n, we need to loop over all atoms i again. Since we sorted
+ * the atoms with respect to the rotation vector, we know that it is sufficient
+ * to calculate from firstatom to lastatom only. All other contributions will
+ * be very small. */
+ clear_rvec(innersumvec);
+ for (i=erg->firstatom[islab]; i<=erg->lastatom[islab]; i++)
+ {
+ /* Coordinate xi of this atom */
+ copy_rvec(erg->xc[i],xi);
+
+ /* The i-weights */
+ gaussian_xi = gaussian_weight(xi,rotg,n);
+ mi = erg->mc_sorted[i]; /* need the sorted mass here */
+ wi = N_M*mi;
+
+ /* Calculate rin and qin */
+ rvec_sub(erg->xc_ref_sorted[i], ycn, tmpvec); /* tmpvec = yi0-ycn */
+ mvmul(erg->rotmat, tmpvec, rin); /* rin = Omega.(yi0 - ycn) */
+ cprod(rotg->vec, rin, tmpvec); /* tmpvec = v x Omega*(yi0-ycn) */
+
+ /* v x Omega*(yi0-ycn) */
+ unitv(tmpvec, qin); /* qin = --------------------- */
+ /* |v x Omega*(yi0-ycn)| */
+
+ /* Calculate bin */
+ rvec_sub(xi, xcn, tmpvec); /* tmpvec = xi-xcn */
+ bin = iprod(qin, tmpvec); /* bin = qin*(xi-xcn) */
+
+ svmul(wi*gaussian_xi*bin, qin, tmpvec);
+
+ /* Add this contribution to the inner sum: */
+ rvec_add(innersumvec, tmpvec, innersumvec);
+ } /* now we have the inner sum vector S^n for this slab */
+ /* Save it to be used in do_flex_lowlevel */
+ copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
+ }
+}
+
+
+static real do_flex2_lowlevel(
+ t_rotgrp *rotg,
+ real sigma, /* The Gaussian width sigma */
+ rvec x[],
+ gmx_bool bOutstepRot,
+ gmx_bool bOutstepSlab,
+ matrix box)
+{
+ int count,ic,ii,j,m,n,islab,iigrp,ifit;
+ rvec xj; /* position in the i-sum */
+ rvec yj0; /* the reference position in the j-sum */
+ rvec xcn, ycn; /* the current and the reference slab centers */
+ real V; /* This node's part of the rotation pot. energy */
+ real gaussian_xj; /* Gaussian weight */
+ real beta;
+
+ real numerator,fit_numerator;
+ rvec rjn,fit_rjn; /* Helper variables */
+ real fac,fac2;
+
+ real OOpsij,OOpsijstar;
+ real OOsigma2; /* 1/(sigma^2) */
+ real sjn_rjn;
+ real betasigpsi;
+ rvec sjn,tmpvec,tmpvec2,yj0_ycn;
+ rvec sum1vec_part,sum1vec,sum2vec_part,sum2vec,sum3vec,sum4vec,innersumvec;
+ real sum3,sum4;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ real mj,wj; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ real Wjn; /* g_n(x_j) m_j / Mjn */
+ gmx_bool bCalcPotFit;
+
+ /* To calculate the torque per slab */
+ rvec slab_force; /* Single force from slab n on one atom */
+ rvec slab_sum1vec_part;
+ real slab_sum3part,slab_sum4part;
+ rvec slab_sum1vec, slab_sum2vec, slab_sum3vec, slab_sum4vec;
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Pre-calculate the inner sums, so that we do not have to calculate
+ * them again for every atom */
+ flex2_precalc_inner_sum(rotg);
+
+ bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
+
+ /********************************************************/
+ /* Main loop over all local atoms of the rotation group */
+ /********************************************************/
+ N_M = rotg->nat * erg->invmass;
+ V = 0.0;
+ OOsigma2 = 1.0 / (sigma*sigma);
+ for (j=0; j<erg->nat_loc; j++)
+ {
+ /* Local index of a rotation group atom */
+ ii = erg->ind_loc[j];
+ /* Position of this atom in the collective array */
+ iigrp = erg->xc_ref_ind[j];
+ /* Mass-weighting */
+ mj = erg->mc[iigrp]; /* need the unsorted mass here */
+ wj = N_M*mj;
+
+ /* Current position of this atom: x[ii][XX/YY/ZZ]
+ * Note that erg->xc_center contains the center of mass in case the flex2-t
+ * potential was chosen. For the flex2 potential erg->xc_center must be
+ * zero. */
+ rvec_sub(x[ii], erg->xc_center, xj);
+
+ /* Shift this atom such that it is near its reference */
+ shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+ /* Determine the slabs to loop over, i.e. the ones with contributions
+ * larger than min_gaussian */
+ count = get_single_atom_gaussians(xj, rotg);
+
+ clear_rvec(sum1vec_part);
+ clear_rvec(sum2vec_part);
+ sum3 = 0.0;
+ sum4 = 0.0;
+ /* Loop over the relevant slabs for this atom */
+ for (ic=0; ic < count; ic++)
+ {
+ n = erg->gn_slabind[ic];
+
+ /* Get the precomputed Gaussian value of curr_slab for curr_x */
+ gaussian_xj = erg->gn_atom[ic];
+
+ islab = n - erg->slab_first; /* slab index */
+
+ /* The (unrotated) reference position of this atom is copied to yj0: */
+ copy_rvec(rotg->x_ref[iigrp], yj0);
+
+ beta = calc_beta(xj, rotg,n);
+
+ /* The current center of this slab is saved in xcn: */
+ copy_rvec(erg->slab_center[islab], xcn);
+ /* ... and the reference center in ycn: */
+ copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+
+ rvec_sub(yj0, ycn, yj0_ycn); /* yj0_ycn = yj0 - ycn */
+
+ /* Rotate: */
+ mvmul(erg->rotmat, yj0_ycn, rjn); /* rjn = Omega.(yj0 - ycn) */
+
+ /* Subtract the slab center from xj */
+ rvec_sub(xj, xcn, tmpvec2); /* tmpvec2 = xj - xcn */
+
+ /* Calculate sjn */
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec = v x (xj - xcn) */
+
+ OOpsijstar = norm2(tmpvec)+rotg->eps; /* OOpsij* = 1/psij* = |v x (xj-xcn)|^2 + eps */
+
+ numerator = sqr(iprod(tmpvec, rjn));
+
+ /*********************************/
+ /* Add to the rotation potential */
+ /*********************************/
+ V += 0.5*rotg->k*wj*gaussian_xj*numerator/OOpsijstar;
+
+ /* If requested, also calculate the potential for a set of angles
+ * near the current reference angle */
+ if (bCalcPotFit)
+ {
+ for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+ {
+ mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, fit_rjn);
+ fit_numerator = sqr(iprod(tmpvec, fit_rjn));
+ erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*gaussian_xj*fit_numerator/OOpsijstar;
+ }
+ }
+
+ /*************************************/
+ /* Now calculate the force on atom j */
+ /*************************************/
+
+ OOpsij = norm(tmpvec); /* OOpsij = 1 / psij = |v x (xj - xcn)| */
+
+ /* v x (xj - xcn) */
+ unitv(tmpvec, sjn); /* sjn = ---------------- */
+ /* |v x (xj - xcn)| */
+
+ sjn_rjn=iprod(sjn,rjn); /* sjn_rjn = sjn . rjn */
+
+
+ /*** A. Calculate the first of the four sum terms: ****************/
+ fac = OOpsij/OOpsijstar;
+ svmul(fac, rjn, tmpvec);
+ fac2 = fac*fac*OOpsij;
+ svmul(fac2*sjn_rjn, sjn, tmpvec2);
+ rvec_dec(tmpvec, tmpvec2);
+ fac2 = wj*gaussian_xj; /* also needed for sum4 */
+ svmul(fac2*sjn_rjn, tmpvec, slab_sum1vec_part);
+ /********************/
+ /*** Add to sum1: ***/
+ /********************/
+ rvec_inc(sum1vec_part, slab_sum1vec_part); /* sum1 still needs to vector multiplied with v */
+
+ /*** B. Calculate the forth of the four sum terms: ****************/
+ betasigpsi = beta*OOsigma2*OOpsij; /* this is also needed for sum3 */
+ /********************/
+ /*** Add to sum4: ***/
+ /********************/
+ slab_sum4part = fac2*betasigpsi*fac*sjn_rjn*sjn_rjn; /* Note that fac is still valid from above */
+ sum4 += slab_sum4part;
+
+ /*** C. Calculate Wjn for second and third sum */
+ /* Note that we can safely divide by slab_weights since we check in
+ * get_slab_centers that it is non-zero. */
+ Wjn = gaussian_xj*mj/erg->slab_weights[islab];
+
+ /* We already have precalculated the inner sum for slab n */
+ copy_rvec(erg->slab_innersumvec[islab], innersumvec);
+
+ /* Weigh the inner sum vector with Wjn */
+ svmul(Wjn, innersumvec, innersumvec);
+
+ /*** E. Calculate the second of the four sum terms: */
+ /********************/
+ /*** Add to sum2: ***/
+ /********************/
+ rvec_inc(sum2vec_part, innersumvec); /* sum2 still needs to be vector crossproduct'ed with v */
+
+ /*** F. Calculate the third of the four sum terms: */
+ slab_sum3part = betasigpsi * iprod(sjn, innersumvec);
+ sum3 += slab_sum3part; /* still needs to be multiplied with v */
+
+ /*** G. Calculate the torque on the local slab's axis: */
+ if (bOutstepRot)
+ {
+ /* Sum1 */
+ cprod(slab_sum1vec_part, rotg->vec, slab_sum1vec);
+ /* Sum2 */
+ cprod(innersumvec, rotg->vec, slab_sum2vec);
+ /* Sum3 */
+ svmul(slab_sum3part, rotg->vec, slab_sum3vec);
+ /* Sum4 */
+ svmul(slab_sum4part, rotg->vec, slab_sum4vec);
+
+ /* The force on atom ii from slab n only: */
+ for (m=0; m<DIM; m++)
+ slab_force[m] = rotg->k * (-slab_sum1vec[m] + slab_sum2vec[m] - slab_sum3vec[m] + 0.5*slab_sum4vec[m]);
+
+ erg->slab_torque_v[islab] += torque(rotg->vec, slab_force, xj, xcn);
+ }
+ } /* END of loop over slabs */
+
+ /* Construct the four individual parts of the vector sum: */
+ cprod(sum1vec_part, rotg->vec, sum1vec); /* sum1vec = { } x v */
+ cprod(sum2vec_part, rotg->vec, sum2vec); /* sum2vec = { } x v */
+ svmul(sum3, rotg->vec, sum3vec); /* sum3vec = { } . v */
+ svmul(sum4, rotg->vec, sum4vec); /* sum4vec = { } . v */
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ for (m=0; m<DIM; m++)
+ erg->f_rot_loc[j][m] = rotg->k * (-sum1vec[m] + sum2vec[m] - sum3vec[m] + 0.5*sum4vec[m]);
+
+#ifdef SUM_PARTS
+ fprintf(stderr, "sum1: %15.8f %15.8f %15.8f\n", -rotg->k*sum1vec[XX], -rotg->k*sum1vec[YY], -rotg->k*sum1vec[ZZ]);
+ fprintf(stderr, "sum2: %15.8f %15.8f %15.8f\n", rotg->k*sum2vec[XX], rotg->k*sum2vec[YY], rotg->k*sum2vec[ZZ]);
+ fprintf(stderr, "sum3: %15.8f %15.8f %15.8f\n", -rotg->k*sum3vec[XX], -rotg->k*sum3vec[YY], -rotg->k*sum3vec[ZZ]);
+ fprintf(stderr, "sum4: %15.8f %15.8f %15.8f\n", 0.5*rotg->k*sum4vec[XX], 0.5*rotg->k*sum4vec[YY], 0.5*rotg->k*sum4vec[ZZ]);
+#endif
+
+ PRINT_FORCE_J
+
+ } /* END of loop over local atoms */
+
+ return V;
+}
+
+
+static real do_flex_lowlevel(
+ t_rotgrp *rotg,
+ real sigma, /* The Gaussian width sigma */
+ rvec x[],
+ gmx_bool bOutstepRot,
+ gmx_bool bOutstepSlab,
+ matrix box)
+{
+ int count,ic,ifit,ii,j,m,n,islab,iigrp;
+ rvec xj,yj0; /* current and reference position */
+ rvec xcn, ycn; /* the current and the reference slab centers */
+ rvec yj0_ycn; /* yj0 - ycn */
+ rvec xj_xcn; /* xj - xcn */
+ rvec qjn,fit_qjn; /* q_i^n */
+ rvec sum_n1,sum_n2; /* Two contributions to the rotation force */
+ rvec innersumvec; /* Inner part of sum_n2 */
+ rvec s_n;
+ rvec force_n; /* Single force from slab n on one atom */
+ rvec force_n1,force_n2; /* First and second part of force_n */
+ rvec tmpvec,tmpvec2,tmp_f; /* Helper variables */
+ real V; /* The rotation potential energy */
+ real OOsigma2; /* 1/(sigma^2) */
+ real beta; /* beta_n(xj) */
+ real bjn, fit_bjn; /* b_j^n */
+ real gaussian_xj; /* Gaussian weight gn(xj) */
+ real betan_xj_sigma2;
+ real mj,wj; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ gmx_bool bCalcPotFit;
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Pre-calculate the inner sums, so that we do not have to calculate
+ * them again for every atom */
+ flex_precalc_inner_sum(rotg);
+
+ bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
+
+ /********************************************************/
+ /* Main loop over all local atoms of the rotation group */
+ /********************************************************/
+ OOsigma2 = 1.0/(sigma*sigma);
+ N_M = rotg->nat * erg->invmass;
+ V = 0.0;
+ for (j=0; j<erg->nat_loc; j++)
+ {
+ /* Local index of a rotation group atom */
+ ii = erg->ind_loc[j];
+ /* Position of this atom in the collective array */
+ iigrp = erg->xc_ref_ind[j];
+ /* Mass-weighting */
+ mj = erg->mc[iigrp]; /* need the unsorted mass here */
+ wj = N_M*mj;
+
+ /* Current position of this atom: x[ii][XX/YY/ZZ]
+ * Note that erg->xc_center contains the center of mass in case the flex-t
+ * potential was chosen. For the flex potential erg->xc_center must be
+ * zero. */
+ rvec_sub(x[ii], erg->xc_center, xj);
+
+ /* Shift this atom such that it is near its reference */
+ shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+ /* Determine the slabs to loop over, i.e. the ones with contributions
+ * larger than min_gaussian */
+ count = get_single_atom_gaussians(xj, rotg);
+
+ clear_rvec(sum_n1);
+ clear_rvec(sum_n2);
+
+ /* Loop over the relevant slabs for this atom */
+ for (ic=0; ic < count; ic++)
+ {
+ n = erg->gn_slabind[ic];
+
+ /* Get the precomputed Gaussian for xj in slab n */
+ gaussian_xj = erg->gn_atom[ic];
+
+ islab = n - erg->slab_first; /* slab index */
+
+ /* The (unrotated) reference position of this atom is saved in yj0: */
+ copy_rvec(rotg->x_ref[iigrp], yj0);
+
+ beta = calc_beta(xj, rotg, n);
+
+ /* The current center of this slab is saved in xcn: */
+ copy_rvec(erg->slab_center[islab], xcn);
+ /* ... and the reference center in ycn: */
+ copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+
+ rvec_sub(yj0, ycn, yj0_ycn); /* yj0_ycn = yj0 - ycn */
+
+ /* Rotate: */
+ mvmul(erg->rotmat, yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
+
+ /* Subtract the slab center from xj */
+ rvec_sub(xj, xcn, xj_xcn); /* xj_xcn = xj - xcn */
+
+ /* Calculate qjn */
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec= v x Omega.(yj0-ycn) */
+
+ /* v x Omega.(yj0-ycn) */
+ unitv(tmpvec,qjn); /* qjn = --------------------- */
+ /* |v x Omega.(yj0-ycn)| */
+
+ bjn = iprod(qjn, xj_xcn); /* bjn = qjn * (xj - xcn) */
+
+ /*********************************/
+ /* Add to the rotation potential */
+ /*********************************/
+ V += 0.5*rotg->k*wj*gaussian_xj*sqr(bjn);
+
+ /* If requested, also calculate the potential for a set of angles
+ * near the current reference angle */
+ if (bCalcPotFit)
+ {
+ for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+ {
+ /* As above calculate Omega.(yj0-ycn), now for the other angles */
+ mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
+ /* As above calculate qjn */
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec= v x Omega.(yj0-ycn) */
+ /* v x Omega.(yj0-ycn) */
+ unitv(tmpvec,fit_qjn); /* fit_qjn = --------------------- */
+ /* |v x Omega.(yj0-ycn)| */
+ fit_bjn = iprod(fit_qjn, xj_xcn); /* fit_bjn = fit_qjn * (xj - xcn) */
+ /* Add to the rotation potential for this angle */
+ erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*gaussian_xj*sqr(fit_bjn);
+ }
+ }
+
+ /****************************************************************/
+ /* sum_n1 will typically be the main contribution to the force: */
+ /****************************************************************/
+ betan_xj_sigma2 = beta*OOsigma2; /* beta_n(xj)/sigma^2 */
+
+ /* The next lines calculate
+ * qjn - (bjn*beta(xj)/(2sigma^2))v */
+ svmul(bjn*0.5*betan_xj_sigma2, rotg->vec, tmpvec2);
+ rvec_sub(qjn,tmpvec2,tmpvec);
+
+ /* Multiply with gn(xj)*bjn: */
+ svmul(gaussian_xj*bjn,tmpvec,tmpvec2);
+
+ /* Sum over n: */
+ rvec_inc(sum_n1,tmpvec2);
+
+ /* We already have precalculated the Sn term for slab n */
+ copy_rvec(erg->slab_innersumvec[islab], s_n);
+ /* beta_n(xj) */
+ svmul(betan_xj_sigma2*iprod(s_n, xj_xcn), rotg->vec, tmpvec); /* tmpvec = ---------- s_n (xj-xcn) */
+ /* sigma^2 */
+
+ rvec_sub(s_n, tmpvec, innersumvec);
+
+ /* We can safely divide by slab_weights since we check in get_slab_centers
+ * that it is non-zero. */
+ svmul(gaussian_xj/erg->slab_weights[islab], innersumvec, innersumvec);
+
+ rvec_add(sum_n2, innersumvec, sum_n2);
+
+ /* Calculate the torque: */
+ if (bOutstepRot)
+ {
+ /* The force on atom ii from slab n only: */
+ svmul(-rotg->k*wj, tmpvec2 , force_n1); /* part 1 */
+ svmul( rotg->k*mj, innersumvec, force_n2); /* part 2 */
+ rvec_add(force_n1, force_n2, force_n);
+ erg->slab_torque_v[islab] += torque(rotg->vec, force_n, xj, xcn);
+ }
+ } /* END of loop over slabs */
+
+ /* Put both contributions together: */
+ svmul(wj, sum_n1, sum_n1);
+ svmul(mj, sum_n2, sum_n2);
+ rvec_sub(sum_n2,sum_n1,tmp_f); /* F = -grad V */
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ for(m=0; m<DIM; m++)
+ erg->f_rot_loc[j][m] = rotg->k*tmp_f[m];
+
+ PRINT_FORCE_J
+
+ } /* END of loop over local atoms */
+
+ return V;
+}
+
+#ifdef PRINT_COORDS
+static void print_coordinates(t_rotgrp *rotg, rvec x[], matrix box, int step)
+{
+ int i;
+ static FILE *fp;
+ static char buf[STRLEN];
+ static gmx_bool bFirst=1;
+
+
+ if (bFirst)
+ {
+ sprintf(buf, "coords%d.txt", cr->nodeid);
+ fp = fopen(buf, "w");
+ bFirst = 0;
+ }
+
+ fprintf(fp, "\nStep %d\n", step);
+ fprintf(fp, "box: %f %f %f %f %f %f %f %f %f\n",
+ box[XX][XX], box[XX][YY], box[XX][ZZ],
+ box[YY][XX], box[YY][YY], box[YY][ZZ],
+ box[ZZ][XX], box[ZZ][ZZ], box[ZZ][ZZ]);
+ for (i=0; i<rotg->nat; i++)
+ {
+ fprintf(fp, "%4d %f %f %f\n", i,
+ erg->xc[i][XX], erg->xc[i][YY], erg->xc[i][ZZ]);
+ }
+ fflush(fp);
+
+}
+#endif
+
+
+static int projection_compare(const void *a, const void *b)
+{
+ sort_along_vec_t *xca, *xcb;
+
+
+ xca = (sort_along_vec_t *)a;
+ xcb = (sort_along_vec_t *)b;
+
+ if (xca->xcproj < xcb->xcproj)
+ return -1;
+ else if (xca->xcproj > xcb->xcproj)
+ return 1;
+ else
+ return 0;
+}
+
+
+static void sort_collective_coordinates(
+ t_rotgrp *rotg, /* Rotation group */
+ sort_along_vec_t *data) /* Buffer for sorting the positions */
+{
+ int i;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+
+ /* The projection of the position vector on the rotation vector is
+ * the relevant value for sorting. Fill the 'data' structure */
+ for (i=0; i<rotg->nat; i++)
+ {
+ data[i].xcproj = iprod(erg->xc[i], rotg->vec); /* sort criterium */
+ data[i].m = erg->mc[i];
+ data[i].ind = i;
+ copy_rvec(erg->xc[i] , data[i].x );
+ copy_rvec(rotg->x_ref[i], data[i].x_ref);
+ }
+ /* Sort the 'data' structure */
+ gmx_qsort(data, rotg->nat, sizeof(sort_along_vec_t), projection_compare);
+
+ /* Copy back the sorted values */
+ for (i=0; i<rotg->nat; i++)
+ {
+ copy_rvec(data[i].x , erg->xc[i] );
+ copy_rvec(data[i].x_ref, erg->xc_ref_sorted[i]);
+ erg->mc_sorted[i] = data[i].m;
+ erg->xc_sortind[i] = data[i].ind;
+ }
+}
+
+
+/* For each slab, get the first and the last index of the sorted atom
+ * indices */
+static void get_firstlast_atom_per_slab(t_rotgrp *rotg)
+{
+ int i,islab,n;
+ real beta;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Find the first atom that needs to enter the calculation for each slab */
+ n = erg->slab_first; /* slab */
+ i = 0; /* start with the first atom */
+ do
+ {
+ /* Find the first atom that significantly contributes to this slab */
+ do /* move forward in position until a large enough beta is found */
+ {
+ beta = calc_beta(erg->xc[i], rotg, n);
+ i++;
+ } while ((beta < -erg->max_beta) && (i < rotg->nat));
+ i--;
+ islab = n - erg->slab_first; /* slab index */
+ erg->firstatom[islab] = i;
+ /* Proceed to the next slab */
+ n++;
+ } while (n <= erg->slab_last);
+
+ /* Find the last atom for each slab */
+ n = erg->slab_last; /* start with last slab */
+ i = rotg->nat-1; /* start with the last atom */
+ do
+ {
+ do /* move backward in position until a large enough beta is found */
+ {
+ beta = calc_beta(erg->xc[i], rotg, n);
+ i--;
+ } while ((beta > erg->max_beta) && (i > -1));
+ i++;
+ islab = n - erg->slab_first; /* slab index */
+ erg->lastatom[islab] = i;
+ /* Proceed to the next slab */
+ n--;
+ } while (n >= erg->slab_first);
+}
+
+
+/* Determine the very first and very last slab that needs to be considered
+ * For the first slab that needs to be considered, we have to find the smallest
+ * n that obeys:
+ *
+ * x_first * v - n*Delta_x <= beta_max
+ *
+ * slab index n, slab distance Delta_x, rotation vector v. For the last slab we
+ * have to find the largest n that obeys
+ *
+ * x_last * v - n*Delta_x >= -beta_max
+ *
+ */
+static gmx_inline int get_first_slab(
+ t_rotgrp *rotg, /* The rotation group (inputrec data) */
+ real max_beta, /* The max_beta value, instead of min_gaussian */
+ rvec firstatom) /* First atom after sorting along the rotation vector v */
+{
+ /* Find the first slab for the first atom */
+ return ceil((iprod(firstatom, rotg->vec) - max_beta)/rotg->slab_dist);
+}
+
+
+static gmx_inline int get_last_slab(
+ t_rotgrp *rotg, /* The rotation group (inputrec data) */
+ real max_beta, /* The max_beta value, instead of min_gaussian */
+ rvec lastatom) /* Last atom along v */
+{
+ /* Find the last slab for the last atom */
+ return floor((iprod(lastatom, rotg->vec) + max_beta)/rotg->slab_dist);
+}
+
+
+static void get_firstlast_slab_check(
+ t_rotgrp *rotg, /* The rotation group (inputrec data) */
+ t_gmx_enfrotgrp *erg, /* The rotation group (data only accessible in this file) */
+ rvec firstatom, /* First atom after sorting along the rotation vector v */
+ rvec lastatom, /* Last atom along v */
+ int g) /* The rotation group number */
+{
+ erg->slab_first = get_first_slab(rotg, erg->max_beta, firstatom);
+ erg->slab_last = get_last_slab(rotg, erg->max_beta, lastatom);
+
+ /* Check whether we have reference data to compare against */
+ if (erg->slab_first < erg->slab_first_ref)
+ gmx_fatal(FARGS, "%s No reference data for first slab (n=%d), unable to proceed.",
+ RotStr, erg->slab_first);
+
+ /* Check whether we have reference data to compare against */
+ if (erg->slab_last > erg->slab_last_ref)
+ gmx_fatal(FARGS, "%s No reference data for last slab (n=%d), unable to proceed.",
+ RotStr, erg->slab_last);
+}
+
+
+/* Enforced rotation with a flexible axis */
+static void do_flexible(
+ gmx_bool bMaster,
+ gmx_enfrot_t enfrot, /* Other rotation data */
+ t_rotgrp *rotg, /* The rotation group */
+ int g, /* Group number */
+ rvec x[], /* The local positions */
+ matrix box,
+ double t, /* Time in picoseconds */
+ gmx_large_int_t step, /* The time step */
+ gmx_bool bOutstepRot, /* Output to main rotation output file */
+ gmx_bool bOutstepSlab) /* Output per-slab data */
+{
+ int l,nslabs;
+ real sigma; /* The Gaussian width sigma */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Define the sigma value */
+ sigma = 0.7*rotg->slab_dist;
+
+ /* Sort the collective coordinates erg->xc along the rotation vector. This is
+ * an optimization for the inner loop. */
+ sort_collective_coordinates(rotg, enfrot->data);
+
+ /* Determine the first relevant slab for the first atom and the last
+ * relevant slab for the last atom */
+ get_firstlast_slab_check(rotg, erg, erg->xc[0], erg->xc[rotg->nat-1], g);
+
+ /* Determine for each slab depending on the min_gaussian cutoff criterium,
+ * a first and a last atom index inbetween stuff needs to be calculated */
+ get_firstlast_atom_per_slab(rotg);
+
+ /* Determine the gaussian-weighted center of positions for all slabs */
+ get_slab_centers(rotg,erg->xc,erg->mc_sorted,g,t,enfrot->out_slabs,bOutstepSlab,FALSE);
+
+ /* Clear the torque per slab from last time step: */
+ nslabs = erg->slab_last - erg->slab_first + 1;
+ for (l=0; l<nslabs; l++)
+ erg->slab_torque_v[l] = 0.0;
+
+ /* Call the rotational forces kernel */
+ if (rotg->eType == erotgFLEX || rotg->eType == erotgFLEXT)
+ erg->V = do_flex_lowlevel(rotg, sigma, x, bOutstepRot, bOutstepSlab, box);
+ else if (rotg->eType == erotgFLEX2 || rotg->eType == erotgFLEX2T)
+ erg->V = do_flex2_lowlevel(rotg, sigma, x, bOutstepRot, bOutstepSlab, box);
+ else
+ gmx_fatal(FARGS, "Unknown flexible rotation type");
+
+ /* Determine angle by RMSD fit to the reference - Let's hope this */
+ /* only happens once in a while, since this is not parallelized! */
+ if ( bMaster && (erotgFitPOT != rotg->eFittype) )
+ {
+ if (bOutstepRot)
+ {
+ /* Fit angle of the whole rotation group */
+ erg->angle_v = flex_fit_angle(rotg);
+ }
+ if (bOutstepSlab)
+ {
+ /* Fit angle of each slab */
+ flex_fit_angle_perslab(g, rotg, t, erg->degangle, enfrot->out_angles);
+ }
+ }
+
+ /* Lump together the torques from all slabs: */
+ erg->torque_v = 0.0;
+ for (l=0; l<nslabs; l++)
+ erg->torque_v += erg->slab_torque_v[l];
+}
+
+
+/* Calculate the angle between reference and actual rotation group atom,
+ * both projected into a plane perpendicular to the rotation vector: */
+static void angle(t_rotgrp *rotg,
+ rvec x_act,
+ rvec x_ref,
+ real *alpha,
+ real *weight) /* atoms near the rotation axis should count less than atoms far away */
+{
+ rvec xp, xrp; /* current and reference positions projected on a plane perpendicular to pg->vec */
+ rvec dum;
+
+
+ /* Project x_ref and x into a plane through the origin perpendicular to rot_vec: */
+ /* Project x_ref: xrp = x_ref - (vec * x_ref) * vec */
+ svmul(iprod(rotg->vec, x_ref), rotg->vec, dum);
+ rvec_sub(x_ref, dum, xrp);
+ /* Project x_act: */
+ svmul(iprod(rotg->vec, x_act), rotg->vec, dum);
+ rvec_sub(x_act, dum, xp);
+
+ /* Retrieve information about which vector precedes. gmx_angle always
+ * returns a positive angle. */
+ cprod(xp, xrp, dum); /* if reference precedes, this is pointing into the same direction as vec */
+
+ if (iprod(rotg->vec, dum) >= 0)
+ *alpha = -gmx_angle(xrp, xp);
+ else
+ *alpha = +gmx_angle(xrp, xp);
+
+ /* Also return the weight */
+ *weight = norm(xp);
+}
+
+
+/* Project first vector onto a plane perpendicular to the second vector
+ * dr = dr - (dr.v)v
+ * Note that v must be of unit length.
+ */
+static gmx_inline void project_onto_plane(rvec dr, const rvec v)
+{
+ rvec tmp;
+
+
+ svmul(iprod(dr,v),v,tmp); /* tmp = (dr.v)v */
+ rvec_dec(dr, tmp); /* dr = dr - (dr.v)v */
+}
+
+
+/* Fixed rotation: The rotation reference group rotates around the v axis. */
+/* The atoms of the actual rotation group are attached with imaginary */
+/* springs to the reference atoms. */
+static void do_fixed(
+ t_rotgrp *rotg, /* The rotation group */
+ rvec x[], /* The positions */
+ matrix box, /* The simulation box */
+ double t, /* Time in picoseconds */
+ gmx_large_int_t step, /* The time step */
+ gmx_bool bOutstepRot, /* Output to main rotation output file */
+ gmx_bool bOutstepSlab) /* Output per-slab data */
+{
+ int ifit,j,jj,m;
+ rvec dr;
+ rvec tmp_f; /* Force */
+ real alpha; /* a single angle between an actual and a reference position */
+ real weight; /* single weight for a single angle */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec xi_xc; /* xi - xc */
+ gmx_bool bCalcPotFit;
+ rvec fit_xr_loc;
+
+ /* for mass weighting: */
+ real wi; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ real k_wi; /* k times wi */
+
+ gmx_bool bProject;
+
+
+ erg=rotg->enfrotgrp;
+ bProject = (rotg->eType==erotgPM) || (rotg->eType==erotgPMPF);
+ bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
+
+ N_M = rotg->nat * erg->invmass;
+
+ /* Each process calculates the forces on its local atoms */
+ for (j=0; j<erg->nat_loc; j++)
+ {
+ /* Calculate (x_i-x_c) resp. (x_i-u) */
+ rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xi_xc);
+
+ /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
+ rvec_sub(erg->xr_loc[j], xi_xc, dr);
+
+ if (bProject)
+ project_onto_plane(dr, rotg->vec);
+
+ /* Mass-weighting */
+ wi = N_M*erg->m_loc[j];
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ k_wi = rotg->k*wi;
+ for (m=0; m<DIM; m++)
+ {
+ tmp_f[m] = k_wi*dr[m];
+ erg->f_rot_loc[j][m] = tmp_f[m];
+ erg->V += 0.5*k_wi*sqr(dr[m]);
+ }
+
+ /* If requested, also calculate the potential for a set of angles
+ * near the current reference angle */
+ if (bCalcPotFit)
+ {
+ for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+ {
+ /* Index of this rotation group atom with respect to the whole rotation group */
+ jj = erg->xc_ref_ind[j];
+
+ /* Rotate with the alternative angle. Like rotate_local_reference(),
+ * just for a single local atom */
+ mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[jj], fit_xr_loc); /* fit_xr_loc = Omega*(y_i-y_c) */
+
+ /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
+ rvec_sub(fit_xr_loc, xi_xc, dr);
+
+ if (bProject)
+ project_onto_plane(dr, rotg->vec);
+
+ /* Add to the rotation potential for this angle: */
+ erg->PotAngleFit->V[ifit] += 0.5*k_wi*norm2(dr);
+ }
+ }
+
+ if (bOutstepRot)
+ {
+ /* Add to the torque of this rotation group */
+ erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
+
+ /* Calculate the angle between reference and actual rotation group atom. */
+ angle(rotg, xi_xc, erg->xr_loc[j], &alpha, &weight); /* angle in rad, weighted */
+ erg->angle_v += alpha * weight;
+ erg->weight_v += weight;
+ }
+ /* If you want enforced rotation to contribute to the virial,
+ * activate the following lines:
+ if (MASTER(cr))
+ {
+ Add the rotation contribution to the virial
+ for(j=0; j<DIM; j++)
+ for(m=0;m<DIM;m++)
+ vir[j][m] += 0.5*f[ii][j]*dr[m];
+ }
+ */
+
+ PRINT_FORCE_J
+
+ } /* end of loop over local rotation group atoms */
+}
+
+
+/* Calculate the radial motion potential and forces */
+static void do_radial_motion(
+ t_rotgrp *rotg, /* The rotation group */
+ rvec x[], /* The positions */
+ matrix box, /* The simulation box */
+ double t, /* Time in picoseconds */
+ gmx_large_int_t step, /* The time step */
+ gmx_bool bOutstepRot, /* Output to main rotation output file */
+ gmx_bool bOutstepSlab) /* Output per-slab data */
+{
+ int j,jj,ifit;
+ rvec tmp_f; /* Force */
+ real alpha; /* a single angle between an actual and a reference position */
+ real weight; /* single weight for a single angle */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec xj_u; /* xj - u */
+ rvec tmpvec,fit_tmpvec;
+ real fac,fac2,sum=0.0;
+ rvec pj;
+ gmx_bool bCalcPotFit;
+
+ /* For mass weighting: */
+ real wj; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+
+
+ erg=rotg->enfrotgrp;
+ bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
+
+ N_M = rotg->nat * erg->invmass;
+
+ /* Each process calculates the forces on its local atoms */
+ for (j=0; j<erg->nat_loc; j++)
+ {
+ /* Calculate (xj-u) */
+ rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xj_u); /* xj_u = xj-u */
+
+ /* Calculate Omega.(yj0-u) */
+ cprod(rotg->vec, erg->xr_loc[j], tmpvec); /* tmpvec = v x Omega.(yj0-u) */
+
+ /* v x Omega.(yj0-u) */
+ unitv(tmpvec, pj); /* pj = --------------------- */
+ /* | v x Omega.(yj0-u) | */
+
+ fac = iprod(pj, xj_u); /* fac = pj.(xj-u) */
+ fac2 = fac*fac;
+
+ /* Mass-weighting */
+ wj = N_M*erg->m_loc[j];
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ svmul(-rotg->k*wj*fac, pj, tmp_f);
+ copy_rvec(tmp_f, erg->f_rot_loc[j]);
+ sum += wj*fac2;
+
+ /* If requested, also calculate the potential for a set of angles
+ * near the current reference angle */
+ if (bCalcPotFit)
+ {
+ for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+ {
+ /* Index of this rotation group atom with respect to the whole rotation group */
+ jj = erg->xc_ref_ind[j];
+
+ /* Rotate with the alternative angle. Like rotate_local_reference(),
+ * just for a single local atom */
+ mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[jj], fit_tmpvec); /* fit_tmpvec = Omega*(yj0-u) */
+
+ /* Calculate Omega.(yj0-u) */
+ cprod(rotg->vec, fit_tmpvec, tmpvec); /* tmpvec = v x Omega.(yj0-u) */
+ /* v x Omega.(yj0-u) */
+ unitv(tmpvec, pj); /* pj = --------------------- */
+ /* | v x Omega.(yj0-u) | */
+
+ fac = iprod(pj, xj_u); /* fac = pj.(xj-u) */
+ fac2 = fac*fac;
+
+ /* Add to the rotation potential for this angle: */
+ erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*fac2;
+ }
+ }
+
+ if (bOutstepRot)
+ {
+ /* Add to the torque of this rotation group */
+ erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
+
+ /* Calculate the angle between reference and actual rotation group atom. */
+ angle(rotg, xj_u, erg->xr_loc[j], &alpha, &weight); /* angle in rad, weighted */
+ erg->angle_v += alpha * weight;
+ erg->weight_v += weight;
+ }
+
+ PRINT_FORCE_J
+
+ } /* end of loop over local rotation group atoms */
+ erg->V = 0.5*rotg->k*sum;
+}
+
+
+/* Calculate the radial motion pivot-free potential and forces */
+static void do_radial_motion_pf(
+ t_rotgrp *rotg, /* The rotation group */
+ rvec x[], /* The positions */
+ matrix box, /* The simulation box */
+ double t, /* Time in picoseconds */
+ gmx_large_int_t step, /* The time step */
+ gmx_bool bOutstepRot, /* Output to main rotation output file */
+ gmx_bool bOutstepSlab) /* Output per-slab data */
+{
+ int i,ii,iigrp,ifit,j;
+ rvec xj; /* Current position */
+ rvec xj_xc; /* xj - xc */
+ rvec yj0_yc0; /* yj0 - yc0 */
+ rvec tmp_f; /* Force */
+ real alpha; /* a single angle between an actual and a reference position */
+ real weight; /* single weight for a single angle */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec tmpvec, tmpvec2;
+ rvec innersumvec; /* Precalculation of the inner sum */
+ rvec innersumveckM;
+ real fac,fac2,V=0.0;
+ rvec qi,qj;
+ gmx_bool bCalcPotFit;
+
+ /* For mass weighting: */
+ real mj,wi,wj; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+
+
+ erg=rotg->enfrotgrp;
+ bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
+
+ N_M = rotg->nat * erg->invmass;
+
+ /* Get the current center of the rotation group: */
+ get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
+
+ /* Precalculate Sum_i [ wi qi.(xi-xc) qi ] which is needed for every single j */
+ clear_rvec(innersumvec);
+ for (i=0; i < rotg->nat; i++)
+ {
+ /* Mass-weighting */
+ wi = N_M*erg->mc[i];
+
+ /* Calculate qi. Note that xc_ref_center has already been subtracted from
+ * x_ref in init_rot_group.*/
+ mvmul(erg->rotmat, rotg->x_ref[i], tmpvec); /* tmpvec = Omega.(yi0-yc0) */
+
+ cprod(rotg->vec, tmpvec, tmpvec2); /* tmpvec2 = v x Omega.(yi0-yc0) */
+
+ /* v x Omega.(yi0-yc0) */
+ unitv(tmpvec2, qi); /* qi = ----------------------- */
+ /* | v x Omega.(yi0-yc0) | */
+
+ rvec_sub(erg->xc[i], erg->xc_center, tmpvec); /* tmpvec = xi-xc */
+
+ svmul(wi*iprod(qi, tmpvec), qi, tmpvec2);
+
+ rvec_inc(innersumvec, tmpvec2);
+ }
+ svmul(rotg->k*erg->invmass, innersumvec, innersumveckM);
+
+ /* Each process calculates the forces on its local atoms */
+ for (j=0; j<erg->nat_loc; j++)
+ {
+ /* Local index of a rotation group atom */
+ ii = erg->ind_loc[j];
+ /* Position of this atom in the collective array */
+ iigrp = erg->xc_ref_ind[j];
+ /* Mass-weighting */
+ mj = erg->mc[iigrp]; /* need the unsorted mass here */
+ wj = N_M*mj;
+
+ /* Current position of this atom: x[ii][XX/YY/ZZ] */
+ copy_rvec(x[ii], xj);
+
+ /* Shift this atom such that it is near its reference */
+ shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+ /* The (unrotated) reference position is yj0. yc0 has already
+ * been subtracted in init_rot_group */
+ copy_rvec(rotg->x_ref[iigrp], yj0_yc0); /* yj0_yc0 = yj0 - yc0 */
+
+ /* Calculate Omega.(yj0-yc0) */
+ mvmul(erg->rotmat, yj0_yc0, tmpvec2); /* tmpvec2 = Omega.(yj0 - yc0) */
+
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec = v x Omega.(yj0-yc0) */
+
+ /* v x Omega.(yj0-yc0) */
+ unitv(tmpvec, qj); /* qj = ----------------------- */
+ /* | v x Omega.(yj0-yc0) | */
+
+ /* Calculate (xj-xc) */
+ rvec_sub(xj, erg->xc_center, xj_xc); /* xj_xc = xj-xc */
+
+ fac = iprod(qj, xj_xc); /* fac = qj.(xj-xc) */
+ fac2 = fac*fac;
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ svmul(-rotg->k*wj*fac, qj, tmp_f); /* part 1 of force */
+ svmul(mj, innersumveckM, tmpvec); /* part 2 of force */
+ rvec_inc(tmp_f, tmpvec);
+ copy_rvec(tmp_f, erg->f_rot_loc[j]);
+ V += wj*fac2;
+
+ /* If requested, also calculate the potential for a set of angles
+ * near the current reference angle */
+ if (bCalcPotFit)
+ {
+ for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+ {
+ /* Rotate with the alternative angle. Like rotate_local_reference(),
+ * just for a single local atom */
+ mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, tmpvec2); /* tmpvec2 = Omega*(yj0-yc0) */
+
+ /* Calculate Omega.(yj0-u) */
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec = v x Omega.(yj0-yc0) */
+ /* v x Omega.(yj0-yc0) */
+ unitv(tmpvec, qj); /* qj = ----------------------- */
+ /* | v x Omega.(yj0-yc0) | */
+
+ fac = iprod(qj, xj_xc); /* fac = qj.(xj-xc) */
+ fac2 = fac*fac;
+
+ /* Add to the rotation potential for this angle: */
+ erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*fac2;
+ }
+ }
+
+ if (bOutstepRot)
+ {
+ /* Add to the torque of this rotation group */
+ erg->torque_v += torque(rotg->vec, tmp_f, xj, erg->xc_center);
+
+ /* Calculate the angle between reference and actual rotation group atom. */
+ angle(rotg, xj_xc, yj0_yc0, &alpha, &weight); /* angle in rad, weighted */
+ erg->angle_v += alpha * weight;
+ erg->weight_v += weight;
+ }
+
+ PRINT_FORCE_J
+
+ } /* end of loop over local rotation group atoms */
+ erg->V = 0.5*rotg->k*V;
+}
+
+
+/* Precalculate the inner sum for the radial motion 2 forces */
+static void radial_motion2_precalc_inner_sum(t_rotgrp *rotg, rvec innersumvec)
+{
+ int i;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec xi_xc; /* xj - xc */
+ rvec tmpvec,tmpvec2;
+ real fac,fac2;
+ rvec ri,si;
+ real siri;
+ rvec v_xi_xc; /* v x (xj - u) */
+ real psii,psiistar;
+ real wi; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ rvec sumvec;
+
+ erg=rotg->enfrotgrp;
+ N_M = rotg->nat * erg->invmass;
+
+ /* Loop over the collective set of positions */
+ clear_rvec(sumvec);
+ for (i=0; i<rotg->nat; i++)
+ {
+ /* Mass-weighting */
+ wi = N_M*erg->mc[i];
+
+ rvec_sub(erg->xc[i], erg->xc_center, xi_xc); /* xi_xc = xi-xc */
+
+ /* Calculate ri. Note that xc_ref_center has already been subtracted from
+ * x_ref in init_rot_group.*/
+ mvmul(erg->rotmat, rotg->x_ref[i], ri); /* ri = Omega.(yi0-yc0) */
+
+ cprod(rotg->vec, xi_xc, v_xi_xc); /* v_xi_xc = v x (xi-u) */
+
+ fac = norm2(v_xi_xc);
+ /* 1 */
+ psiistar = 1.0/(fac + rotg->eps); /* psiistar = --------------------- */
+ /* |v x (xi-xc)|^2 + eps */
+
+ psii = gmx_invsqrt(fac); /* 1 */
+ /* psii = ------------- */
+ /* |v x (xi-xc)| */
+
+ svmul(psii, v_xi_xc, si); /* si = psii * (v x (xi-xc) ) */
+
+ fac = iprod(v_xi_xc, ri); /* fac = (v x (xi-xc)).ri */
+ fac2 = fac*fac;
+
+ siri = iprod(si, ri); /* siri = si.ri */
+
+ svmul(psiistar/psii, ri, tmpvec);
+ svmul(psiistar*psiistar/(psii*psii*psii) * siri, si, tmpvec2);
+ rvec_dec(tmpvec, tmpvec2);
+ cprod(tmpvec, rotg->vec, tmpvec2);
+
+ svmul(wi*siri, tmpvec2, tmpvec);
+
+ rvec_inc(sumvec, tmpvec);
+ }
+ svmul(rotg->k*erg->invmass, sumvec, innersumvec);
+}
+
+
+/* Calculate the radial motion 2 potential and forces */
+static void do_radial_motion2(
+ t_rotgrp *rotg, /* The rotation group */
+ rvec x[], /* The positions */
+ matrix box, /* The simulation box */
+ double t, /* Time in picoseconds */
+ gmx_large_int_t step, /* The time step */
+ gmx_bool bOutstepRot, /* Output to main rotation output file */
+ gmx_bool bOutstepSlab) /* Output per-slab data */
+{
+ int ii,iigrp,ifit,j;
+ rvec xj; /* Position */
+ real alpha; /* a single angle between an actual and a reference position */
+ real weight; /* single weight for a single angle */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec xj_u; /* xj - u */
+ rvec yj0_yc0; /* yj0 -yc0 */
+ rvec tmpvec,tmpvec2;
+ real fac,fit_fac,fac2,Vpart=0.0;
+ rvec rj,fit_rj,sj;
+ real sjrj;
+ rvec v_xj_u; /* v x (xj - u) */
+ real psij,psijstar;
+ real mj,wj; /* For mass-weighting of the positions */
+ real N_M; /* N/M */
+ gmx_bool bPF;
+ rvec innersumvec;
+ gmx_bool bCalcPotFit;
+
+
+ erg=rotg->enfrotgrp;
+
+ bPF = rotg->eType==erotgRM2PF;
+ bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT==rotg->eFittype);
+
+
+ clear_rvec(yj0_yc0); /* Make the compiler happy */
+
+ clear_rvec(innersumvec);
+ if (bPF)
+ {
+ /* For the pivot-free variant we have to use the current center of
+ * mass of the rotation group instead of the pivot u */
+ get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
+
+ /* Also, we precalculate the second term of the forces that is identical
+ * (up to the weight factor mj) for all forces */
+ radial_motion2_precalc_inner_sum(rotg,innersumvec);
+ }
+
+ N_M = rotg->nat * erg->invmass;
+
+ /* Each process calculates the forces on its local atoms */
+ for (j=0; j<erg->nat_loc; j++)
+ {
+ if (bPF)
+ {
+ /* Local index of a rotation group atom */
+ ii = erg->ind_loc[j];
+ /* Position of this atom in the collective array */
+ iigrp = erg->xc_ref_ind[j];
+ /* Mass-weighting */
+ mj = erg->mc[iigrp];
+
+ /* Current position of this atom: x[ii] */
+ copy_rvec(x[ii], xj);
+
+ /* Shift this atom such that it is near its reference */
+ shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+ /* The (unrotated) reference position is yj0. yc0 has already
+ * been subtracted in init_rot_group */
+ copy_rvec(rotg->x_ref[iigrp], yj0_yc0); /* yj0_yc0 = yj0 - yc0 */
+
+ /* Calculate Omega.(yj0-yc0) */
+ mvmul(erg->rotmat, yj0_yc0, rj); /* rj = Omega.(yj0-yc0) */
+ }
+ else
+ {
+ mj = erg->m_loc[j];
+ copy_rvec(erg->x_loc_pbc[j], xj);
+ copy_rvec(erg->xr_loc[j], rj); /* rj = Omega.(yj0-u) */
+ }
+ /* Mass-weighting */
+ wj = N_M*mj;
+
+ /* Calculate (xj-u) resp. (xj-xc) */
+ rvec_sub(xj, erg->xc_center, xj_u); /* xj_u = xj-u */
+
+ cprod(rotg->vec, xj_u, v_xj_u); /* v_xj_u = v x (xj-u) */
+
+ fac = norm2(v_xj_u);
+ /* 1 */
+ psijstar = 1.0/(fac + rotg->eps); /* psistar = -------------------- */
+ /* |v x (xj-u)|^2 + eps */
+
+ psij = gmx_invsqrt(fac); /* 1 */
+ /* psij = ------------ */
+ /* |v x (xj-u)| */
+
+ svmul(psij, v_xj_u, sj); /* sj = psij * (v x (xj-u) ) */
+
+ fac = iprod(v_xj_u, rj); /* fac = (v x (xj-u)).rj */
+ fac2 = fac*fac;
+
+ sjrj = iprod(sj, rj); /* sjrj = sj.rj */
+
+ svmul(psijstar/psij, rj, tmpvec);
+ svmul(psijstar*psijstar/(psij*psij*psij) * sjrj, sj, tmpvec2);
+ rvec_dec(tmpvec, tmpvec2);
+ cprod(tmpvec, rotg->vec, tmpvec2);
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ svmul(-rotg->k*wj*sjrj, tmpvec2, tmpvec);
+ svmul(mj, innersumvec, tmpvec2); /* This is != 0 only for the pivot-free variant */
+
+ rvec_add(tmpvec2, tmpvec, erg->f_rot_loc[j]);
+ Vpart += wj*psijstar*fac2;
+
+ /* If requested, also calculate the potential for a set of angles
+ * near the current reference angle */
+ if (bCalcPotFit)
+ {
+ for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+ {
+ if (bPF)
+ {
+ mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, fit_rj); /* fit_rj = Omega.(yj0-yc0) */
+ }
+ else
+ {
+ /* Position of this atom in the collective array */
+ iigrp = erg->xc_ref_ind[j];
+ /* Rotate with the alternative angle. Like rotate_local_reference(),
+ * just for a single local atom */
+ mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[iigrp], fit_rj); /* fit_rj = Omega*(yj0-u) */
+ }
+ fit_fac = iprod(v_xj_u, fit_rj); /* fac = (v x (xj-u)).fit_rj */
+ /* Add to the rotation potential for this angle: */
+ erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*psijstar*fit_fac*fit_fac;
+ }
+ }
+
+ if (bOutstepRot)
+ {
+ /* Add to the torque of this rotation group */
+ erg->torque_v += torque(rotg->vec, erg->f_rot_loc[j], xj, erg->xc_center);
+
+ /* Calculate the angle between reference and actual rotation group atom. */
+ angle(rotg, xj_u, rj, &alpha, &weight); /* angle in rad, weighted */
+ erg->angle_v += alpha * weight;
+ erg->weight_v += weight;
+ }
+
+ PRINT_FORCE_J
+
+ } /* end of loop over local rotation group atoms */
+ erg->V = 0.5*rotg->k*Vpart;
+}
+
+
+/* Determine the smallest and largest position vector (with respect to the
+ * rotation vector) for the reference group */
+static void get_firstlast_atom_ref(
+ t_rotgrp *rotg,
+ int *firstindex,
+ int *lastindex)
+{
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ int i;
+ real xcproj; /* The projection of a reference position on the
+ rotation vector */
+ real minproj, maxproj; /* Smallest and largest projection on v */
+
+
+
+ erg=rotg->enfrotgrp;
+
+ /* Start with some value */
+ minproj = iprod(rotg->x_ref[0], rotg->vec);
+ maxproj = minproj;
+
+ /* This is just to ensure that it still works if all the atoms of the
+ * reference structure are situated in a plane perpendicular to the rotation
+ * vector */
+ *firstindex = 0;
+ *lastindex = rotg->nat-1;
+
+ /* Loop over all atoms of the reference group,
+ * project them on the rotation vector to find the extremes */
+ for (i=0; i<rotg->nat; i++)
+ {
+ xcproj = iprod(rotg->x_ref[i], rotg->vec);
+ if (xcproj < minproj)
+ {
+ minproj = xcproj;
+ *firstindex = i;
+ }
+ if (xcproj > maxproj)
+ {
+ maxproj = xcproj;
+ *lastindex = i;
+ }
+ }
+}
+
+
+/* Allocate memory for the slabs */
+static void allocate_slabs(
+ t_rotgrp *rotg,
+ FILE *fplog,
+ int g,
+ gmx_bool bVerbose)
+{
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ int i, nslabs;
+
+
+ erg=rotg->enfrotgrp;
+
+ /* More slabs than are defined for the reference are never needed */
+ nslabs = erg->slab_last_ref - erg->slab_first_ref + 1;
+
+ /* Remember how many we allocated */
+ erg->nslabs_alloc = nslabs;
+
+ if ( (NULL != fplog) && bVerbose )
+ fprintf(fplog, "%s allocating memory to store data for %d slabs (rotation group %d).\n",
+ RotStr, nslabs,g);
+ snew(erg->slab_center , nslabs);
+ snew(erg->slab_center_ref , nslabs);
+ snew(erg->slab_weights , nslabs);
+ snew(erg->slab_torque_v , nslabs);
+ snew(erg->slab_data , nslabs);
+ snew(erg->gn_atom , nslabs);
+ snew(erg->gn_slabind , nslabs);
+ snew(erg->slab_innersumvec, nslabs);
+ for (i=0; i<nslabs; i++)
+ {
+ snew(erg->slab_data[i].x , rotg->nat);
+ snew(erg->slab_data[i].ref , rotg->nat);
+ snew(erg->slab_data[i].weight, rotg->nat);
+ }
+ snew(erg->xc_ref_sorted, rotg->nat);
+ snew(erg->xc_sortind , rotg->nat);
+ snew(erg->firstatom , nslabs);
+ snew(erg->lastatom , nslabs);
+}
+
+
+/* From the extreme coordinates of the reference group, determine the first
+ * and last slab of the reference. We can never have more slabs in the real
+ * simulation than calculated here for the reference.
+ */
+static void get_firstlast_slab_ref(t_rotgrp *rotg, real mc[], int ref_firstindex, int ref_lastindex)
+{
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ int first,last,firststart;
+ rvec dummy;
+
+
+ erg=rotg->enfrotgrp;
+ first = get_first_slab(rotg, erg->max_beta, rotg->x_ref[ref_firstindex]);
+ last = get_last_slab( rotg, erg->max_beta, rotg->x_ref[ref_lastindex ]);
+ firststart = first;
+
+ while (get_slab_weight(first, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
+ {
+ first--;
+ }
+ erg->slab_first_ref = first+1;
+ while (get_slab_weight(last, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
+ {
+ last++;
+ }
+ erg->slab_last_ref = last-1;
+
+ erg->slab_buffer = firststart - erg->slab_first_ref;
+}
+
+
+
+static void init_rot_group(FILE *fplog,t_commrec *cr,int g,t_rotgrp *rotg,
+ rvec *x,gmx_mtop_t *mtop,gmx_bool bVerbose,FILE *out_slabs, gmx_bool bOutputCenters)
+{
+ int i,ii;
+ rvec coord,*xdum;
+ gmx_bool bFlex,bColl;
+ t_atom *atom;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ int ref_firstindex, ref_lastindex;
+ real mass,totalmass;
+ real start=0.0;
+
+
+ /* Do we have a flexible axis? */
+ bFlex = ISFLEX(rotg);
+ /* Do we use a global set of coordinates? */
+ bColl = ISCOLL(rotg);
+
+ erg=rotg->enfrotgrp;
+
+ /* Allocate space for collective coordinates if needed */
+ if (bColl)
+ {
+ snew(erg->xc , rotg->nat);
+ snew(erg->xc_shifts , rotg->nat);
+ snew(erg->xc_eshifts, rotg->nat);
+
+ /* Save the original (whole) set of positions such that later the
+ * molecule can always be made whole again */
+ snew(erg->xc_old , rotg->nat);
+ if (MASTER(cr))
+ {
+ for (i=0; i<rotg->nat; i++)
+ {
+ ii = rotg->ind[i];
+ copy_rvec(x[ii], erg->xc_old[i]);
+ }
+ }
+#ifdef GMX_MPI
+ if (PAR(cr))
+ gmx_bcast(rotg->nat*sizeof(erg->xc_old[0]),erg->xc_old, cr);
+#endif
+
+ if (rotg->eFittype == erotgFitNORM)
+ {
+ snew(erg->xc_ref_length, rotg->nat); /* in case fit type NORM is chosen */
+ snew(erg->xc_norm , rotg->nat);
+ }
+ }
+ else
+ {
+ snew(erg->xr_loc , rotg->nat);
+ snew(erg->x_loc_pbc, rotg->nat);
+ }
+
+ snew(erg->f_rot_loc , rotg->nat);
+ snew(erg->xc_ref_ind, rotg->nat);
+
+ /* Make space for the calculation of the potential at other angles (used
+ * for fitting only) */
+ if (erotgFitPOT == rotg->eFittype)
+ {
+ snew(erg->PotAngleFit, 1);
+ snew(erg->PotAngleFit->degangle, rotg->PotAngle_nstep);
+ snew(erg->PotAngleFit->V , rotg->PotAngle_nstep);
+ snew(erg->PotAngleFit->rotmat , rotg->PotAngle_nstep);
+
+ /* Get the set of angles around the reference angle */
+ start = -0.5 * (rotg->PotAngle_nstep - 1)*rotg->PotAngle_step;
+ for (i = 0; i < rotg->PotAngle_nstep; i++)
+ erg->PotAngleFit->degangle[i] = start + i*rotg->PotAngle_step;
+ }
+ else
+ {
+ erg->PotAngleFit = NULL;
+ }
+
+ /* xc_ref_ind needs to be set to identity in the serial case */
+ if (!PAR(cr))
+ for (i=0; i<rotg->nat; i++)
+ erg->xc_ref_ind[i] = i;
+
+ /* Copy the masses so that the center can be determined. For all types of
+ * enforced rotation, we store the masses in the erg->mc array. */
+ snew(erg->mc, rotg->nat);
+ if (bFlex)
+ snew(erg->mc_sorted, rotg->nat);
+ if (!bColl)
+ snew(erg->m_loc, rotg->nat);
+ totalmass=0.0;
+ for (i=0; i<rotg->nat; i++)
+ {
+ if (rotg->bMassW)
+ {
+ gmx_mtop_atomnr_to_atom(mtop,rotg->ind[i],&atom);
+ mass=atom->m;
+ }
+ else
+ {
+ mass=1.0;
+ }
+ erg->mc[i] = mass;
+ totalmass += mass;
+ }
+ erg->invmass = 1.0/totalmass;
+
+ /* Set xc_ref_center for any rotation potential */
+ if ((rotg->eType==erotgISO) || (rotg->eType==erotgPM) || (rotg->eType==erotgRM) || (rotg->eType==erotgRM2))
+ {
+ /* Set the pivot point for the fixed, stationary-axis potentials. This
+ * won't change during the simulation */
+ copy_rvec(rotg->pivot, erg->xc_ref_center);
+ copy_rvec(rotg->pivot, erg->xc_center );
+ }
+ else
+ {
+ /* Center of the reference positions */
+ get_center(rotg->x_ref, erg->mc, rotg->nat, erg->xc_ref_center);
+
+ /* Center of the actual positions */
+ if (MASTER(cr))
+ {
+ snew(xdum, rotg->nat);
+ for (i=0; i<rotg->nat; i++)
+ {
+ ii = rotg->ind[i];
+ copy_rvec(x[ii], xdum[i]);
+ }
+ get_center(xdum, erg->mc, rotg->nat, erg->xc_center);
+ sfree(xdum);
+ }
+#ifdef GMX_MPI
+ if (PAR(cr))
+ gmx_bcast(sizeof(erg->xc_center), erg->xc_center, cr);
+#endif
+ }
+
+ if ( (rotg->eType != erotgFLEX) && (rotg->eType != erotgFLEX2) )
+ {
+ /* Put the reference positions into origin: */
+ for (i=0; i<rotg->nat; i++)
+ rvec_dec(rotg->x_ref[i], erg->xc_ref_center);
+ }
+
+ /* Enforced rotation with flexible axis */
+ if (bFlex)
+ {
+ /* Calculate maximum beta value from minimum gaussian (performance opt.) */
+ erg->max_beta = calc_beta_max(rotg->min_gaussian, rotg->slab_dist);
+
+ /* Determine the smallest and largest coordinate with respect to the rotation vector */
+ get_firstlast_atom_ref(rotg, &ref_firstindex, &ref_lastindex);
+
+ /* From the extreme coordinates of the reference group, determine the first
+ * and last slab of the reference. */
+ get_firstlast_slab_ref(rotg, erg->mc, ref_firstindex, ref_lastindex);
+
+ /* Allocate memory for the slabs */
+ allocate_slabs(rotg, fplog, g, bVerbose);
+
+ /* Flexible rotation: determine the reference centers for the rest of the simulation */
+ erg->slab_first = erg->slab_first_ref;
+ erg->slab_last = erg->slab_last_ref;
+ get_slab_centers(rotg,rotg->x_ref,erg->mc,g,-1,out_slabs,bOutputCenters,TRUE);
+
+ /* Length of each x_rotref vector from center (needed if fit routine NORM is chosen): */
+ if (rotg->eFittype == erotgFitNORM)
+ {
+ for (i=0; i<rotg->nat; i++)
+ {
+ rvec_sub(rotg->x_ref[i], erg->xc_ref_center, coord);
+ erg->xc_ref_length[i] = norm(coord);
+ }
+ }
+ }
+}
+
+
+extern void dd_make_local_rotation_groups(gmx_domdec_t *dd,t_rot *rot)
+{
+ gmx_ga2la_t ga2la;
+ int g;
+ t_rotgrp *rotg;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+ ga2la = dd->ga2la;
+
+ for(g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+
+
+ dd_make_local_group_indices(ga2la,rotg->nat,rotg->ind,
+ &erg->nat_loc,&erg->ind_loc,&erg->nalloc_loc,erg->xc_ref_ind);
+ }
+}
+
+
+/* Calculate the size of the MPI buffer needed in reduce_output() */
+static int calc_mpi_bufsize(t_rot *rot)
+{
+ int g;
+ int count_group, count_total;
+ t_rotgrp *rotg;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ count_total = 0;
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+
+ /* Count the items that are transferred for this group: */
+ count_group = 4; /* V, torque, angle, weight */
+
+ /* Add the maximum number of slabs for flexible groups */
+ if (ISFLEX(rotg))
+ count_group += erg->slab_last_ref - erg->slab_first_ref + 1;
+
+ /* Add space for the potentials at different angles: */
+ if (erotgFitPOT == rotg->eFittype)
+ count_group += rotg->PotAngle_nstep;
+
+ /* Add to the total number: */
+ count_total += count_group;
+ }
+
+ return count_total;
+}
+
+
+extern void init_rot(FILE *fplog,t_inputrec *ir,int nfile,const t_filenm fnm[],
+ t_commrec *cr, rvec *x, matrix box, gmx_mtop_t *mtop, const output_env_t oenv,
+ gmx_bool bVerbose, unsigned long Flags)
+{
+ t_rot *rot;
+ t_rotgrp *rotg;
+ int g;
+ int nat_max=0; /* Size of biggest rotation group */
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec *x_pbc=NULL; /* Space for the pbc-correct atom positions */
+
+
+ if ( (PAR(cr)) && !DOMAINDECOMP(cr) )
+ gmx_fatal(FARGS, "Enforced rotation is only implemented for domain decomposition!");
+
+ if ( MASTER(cr) && bVerbose)
+ fprintf(stdout, "%s Initializing ...\n", RotStr);
+
+ rot = ir->rot;
+ snew(rot->enfrot, 1);
+ er = rot->enfrot;
+ er->Flags = Flags;
+
+ /* When appending, skip first output to avoid duplicate entries in the data files */
+ if (er->Flags & MD_APPENDFILES)
+ er->bOut = FALSE;
+ else
+ er->bOut = TRUE;
+
+ if ( MASTER(cr) && er->bOut )
+ please_cite(fplog, "Kutzner2011");
+
+ /* Output every step for reruns */
+ if (er->Flags & MD_RERUN)
+ {
+ if (NULL != fplog)
+ fprintf(fplog, "%s rerun - will write rotation output every available step.\n", RotStr);
+ rot->nstrout = 1;
+ rot->nstsout = 1;
+ }
+
+ er->out_slabs = NULL;
+ if ( MASTER(cr) && HaveFlexibleGroups(rot) )
+ er->out_slabs = open_slab_out(opt2fn("-rs",nfile,fnm), rot, oenv);
+
+ if (MASTER(cr))
+ {
+ /* Remove pbc, make molecule whole.
+ * When ir->bContinuation=TRUE this has already been done, but ok. */
+ snew(x_pbc,mtop->natoms);
+ m_rveccopy(mtop->natoms,x,x_pbc);
+ do_pbc_first_mtop(NULL,ir->ePBC,box,mtop,x_pbc);
+ }
+
+ for (g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+
+ if (NULL != fplog)
+ fprintf(fplog,"%s group %d type '%s'\n", RotStr, g, erotg_names[rotg->eType]);
+
+ if (rotg->nat > 0)
+ {
+ /* Allocate space for the rotation group's data: */
+ snew(rotg->enfrotgrp, 1);
+ erg = rotg->enfrotgrp;
+
+ nat_max=max(nat_max, rotg->nat);
+
+ if (PAR(cr))
+ {
+ erg->nat_loc = 0;
+ erg->nalloc_loc = 0;
+ erg->ind_loc = NULL;
+ }
+ else
+ {
+ erg->nat_loc = rotg->nat;
+ erg->ind_loc = rotg->ind;
+ }
+ init_rot_group(fplog,cr,g,rotg,x_pbc,mtop,bVerbose,er->out_slabs,
+ !(er->Flags & MD_APPENDFILES) ); /* Do not output the reference centers
+ * again if we are appending */
+ }
+ }
+
+ /* Allocate space for enforced rotation buffer variables */
+ er->bufsize = nat_max;
+ snew(er->data, nat_max);
+ snew(er->xbuf, nat_max);
+ snew(er->mbuf, nat_max);
+
+ /* Buffers for MPI reducing torques, angles, weights (for each group), and V */
+ if (PAR(cr))
+ {
+ er->mpi_bufsize = calc_mpi_bufsize(rot) + 100; /* larger to catch errors */
+ snew(er->mpi_inbuf , er->mpi_bufsize);
+ snew(er->mpi_outbuf, er->mpi_bufsize);
+ }
+ else
+ {
+ er->mpi_bufsize = 0;
+ er->mpi_inbuf = NULL;
+ er->mpi_outbuf = NULL;
+ }
+
+ /* Only do I/O on the MASTER */
+ er->out_angles = NULL;
+ er->out_rot = NULL;
+ er->out_torque = NULL;
+ if (MASTER(cr))
+ {
+ er->out_rot = open_rot_out(opt2fn("-ro",nfile,fnm), rot, oenv);
+
+ if (rot->nstsout > 0)
+ {
+ if ( HaveFlexibleGroups(rot) || HavePotFitGroups(rot) )
+ er->out_angles = open_angles_out(opt2fn("-ra",nfile,fnm), rot, oenv);
+ if ( HaveFlexibleGroups(rot) )
+ er->out_torque = open_torque_out(opt2fn("-rt",nfile,fnm), rot, oenv);
+ }
+
+ sfree(x_pbc);
+ }
+}
+
+
+extern void finish_rot(FILE *fplog,t_rot *rot)
+{
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+
+
+ er=rot->enfrot;
+ if (er->out_rot)
+ gmx_fio_fclose(er->out_rot);
+ if (er->out_slabs)
+ gmx_fio_fclose(er->out_slabs);
+ if (er->out_angles)
+ gmx_fio_fclose(er->out_angles);
+ if (er->out_torque)
+ gmx_fio_fclose(er->out_torque);
+}
+
+
+/* Rotate the local reference positions and store them in
+ * erg->xr_loc[0...(nat_loc-1)]
+ *
+ * Note that we already subtracted u or y_c from the reference positions
+ * in init_rot_group().
+ */
+static void rotate_local_reference(t_rotgrp *rotg)
+{
+ gmx_enfrotgrp_t erg;
+ int i,ii;
+
+
+ erg=rotg->enfrotgrp;
+
+ for (i=0; i<erg->nat_loc; i++)
+ {
+ /* Index of this rotation group atom with respect to the whole rotation group */
+ ii = erg->xc_ref_ind[i];
+ /* Rotate */
+ mvmul(erg->rotmat, rotg->x_ref[ii], erg->xr_loc[i]);
+ }
+}
+
+
+/* Select the PBC representation for each local x position and store that
+ * for later usage. We assume the right PBC image of an x is the one nearest to
+ * its rotated reference */
+static void choose_pbc_image(rvec x[], t_rotgrp *rotg, matrix box, int npbcdim)
+{
+ int d,i,ii,m;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec xref,xcurr,dx;
+ ivec shift;
+
+
+ erg=rotg->enfrotgrp;
+
+ for (i=0; i<erg->nat_loc; i++)
+ {
+ clear_ivec(shift);
+
+ /* Index of a rotation group atom */
+ ii = erg->ind_loc[i];
+
+ /* Get the reference position. The pivot was already
+ * subtracted in init_rot_group() from the reference positions. Also,
+ * the reference positions have already been rotated in
+ * rotate_local_reference() */
+ copy_rvec(erg->xr_loc[i], xref);
+
+ /* Subtract the (old) center from the current positions
+ * (just to determine the shifts!) */
+ rvec_sub(x[ii], erg->xc_center, xcurr);
+
+ /* Shortest PBC distance between the atom and its reference */
+ rvec_sub(xcurr, xref, dx);
+
+ /* Determine the shift for this atom */
+ for(m=npbcdim-1; m>=0; m--)
+ {
+ while (dx[m] < -0.5*box[m][m])
+ {
+ for(d=0; d<DIM; d++)
+ dx[d] += box[m][d];
+ shift[m]++;
+ }
+ while (dx[m] >= 0.5*box[m][m])
+ {
+ for(d=0; d<DIM; d++)
+ dx[d] -= box[m][d];
+ shift[m]--;
+ }
+ }
+
+ /* Apply the shift to the current atom */
+ copy_rvec(x[ii], erg->x_loc_pbc[i]);
+ shift_single_coord(box, erg->x_loc_pbc[i], shift);
+ }
+}
+
+
+extern void do_rotation(
+ t_commrec *cr,
+ t_inputrec *ir,
+ matrix box,
+ rvec x[],
+ real t,
+ gmx_large_int_t step,
+ gmx_wallcycle_t wcycle,
+ gmx_bool bNS)
+{
+ int g,i,ii;
+ t_rot *rot;
+ t_rotgrp *rotg;
+ gmx_bool outstep_slab, outstep_rot;
+ gmx_bool bFlex,bColl;
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec transvec;
+ t_gmx_potfit *fit=NULL; /* For fit type 'potential' determine the fit
+ angle via the potential minimum */
+
+ /* Enforced rotation cycle counting: */
+ gmx_cycles_t cycles_comp; /* Cycles for the enf. rotation computation
+ only, does not count communication. This
+ counter is used for load-balancing */
+
+#ifdef TAKETIME
+ double t0;
+#endif
+
+ rot=ir->rot;
+ er=rot->enfrot;
+
+ /* When to output in main rotation output file */
+ outstep_rot = do_per_step(step, rot->nstrout) && er->bOut;
+ /* When to output per-slab data */
+ outstep_slab = do_per_step(step, rot->nstsout) && er->bOut;
+
+ /* Output time into rotation output file */
+ if (outstep_rot && MASTER(cr))
+ fprintf(er->out_rot, "%12.3e",t);
+
+ /**************************************************************************/
+ /* First do ALL the communication! */
+ for(g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg=rotg->enfrotgrp;
+
+ /* Do we have a flexible axis? */
+ bFlex = ISFLEX(rotg);
+ /* Do we use a collective (global) set of coordinates? */
+ bColl = ISCOLL(rotg);
+
+ /* Calculate the rotation matrix for this angle: */
+ erg->degangle = rotg->rate * t;
+ calc_rotmat(rotg->vec,erg->degangle,erg->rotmat);
+
+ if (bColl)
+ {
+ /* Transfer the rotation group's positions such that every node has
+ * all of them. Every node contributes its local positions x and stores
+ * it in the collective erg->xc array. */
+ communicate_group_positions(cr,erg->xc, erg->xc_shifts, erg->xc_eshifts, bNS,
+ x, rotg->nat, erg->nat_loc, erg->ind_loc, erg->xc_ref_ind, erg->xc_old, box);
+ }
+ else
+ {
+ /* Fill the local masses array;
+ * this array changes in DD/neighborsearching steps */
+ if (bNS)
+ {
+ for (i=0; i<erg->nat_loc; i++)
+ {
+ /* Index of local atom w.r.t. the collective rotation group */
+ ii = erg->xc_ref_ind[i];
+ erg->m_loc[i] = erg->mc[ii];
+ }
+ }
+
+ /* Calculate Omega*(y_i-y_c) for the local positions */
+ rotate_local_reference(rotg);
+
+ /* Choose the nearest PBC images of the group atoms with respect
+ * to the rotated reference positions */
+ choose_pbc_image(x, rotg, box, 3);
+
+ /* Get the center of the rotation group */
+ if ( (rotg->eType==erotgISOPF) || (rotg->eType==erotgPMPF) )
+ get_center_comm(cr, erg->x_loc_pbc, erg->m_loc, erg->nat_loc, rotg->nat, erg->xc_center);
+ }
+
+ } /* End of loop over rotation groups */
+
+ /**************************************************************************/
+ /* Done communicating, we can start to count cycles for the load balancing now ... */
+ cycles_comp = gmx_cycles_read();
+
+
+#ifdef TAKETIME
+ t0 = MPI_Wtime();
+#endif
+
+ for(g=0; g<rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg=rotg->enfrotgrp;
+
+ bFlex = ISFLEX(rotg);
+ bColl = ISCOLL(rotg);
+
+ if (outstep_rot && MASTER(cr))
+ fprintf(er->out_rot, "%12.4f", erg->degangle);
+
+ /* Calculate angles and rotation matrices for potential fitting: */
+ if ( (outstep_rot || outstep_slab) && (erotgFitPOT == rotg->eFittype) )
+ {
+ fit = erg->PotAngleFit;
+ for (i = 0; i < rotg->PotAngle_nstep; i++)
+ {
+ calc_rotmat(rotg->vec, erg->degangle + fit->degangle[i], fit->rotmat[i]);
+
+ /* Clear value from last step */
+ erg->PotAngleFit->V[i] = 0.0;
+ }
+ }
+
+ /* Clear values from last time step */
+ erg->V = 0.0;
+ erg->torque_v = 0.0;
+ erg->angle_v = 0.0;
+ erg->weight_v = 0.0;
+
+ switch(rotg->eType)
+ {
+ case erotgISO:
+ case erotgISOPF:
+ case erotgPM:
+ case erotgPMPF:
+ do_fixed(rotg,x,box,t,step,outstep_rot,outstep_slab);
+ break;
+ case erotgRM:
+ do_radial_motion(rotg,x,box,t,step,outstep_rot,outstep_slab);
+ break;
+ case erotgRMPF:
+ do_radial_motion_pf(rotg,x,box,t,step,outstep_rot,outstep_slab);
+ break;
+ case erotgRM2:
+ case erotgRM2PF:
+ do_radial_motion2(rotg,x,box,t,step,outstep_rot,outstep_slab);
+ break;
+ case erotgFLEXT:
+ case erotgFLEX2T:
+ /* Subtract the center of the rotation group from the collective positions array
+ * Also store the center in erg->xc_center since it needs to be subtracted
+ * in the low level routines from the local coordinates as well */
+ get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
+ svmul(-1.0, erg->xc_center, transvec);
+ translate_x(erg->xc, rotg->nat, transvec);
+ do_flexible(MASTER(cr),er,rotg,g,x,box,t,step,outstep_rot,outstep_slab);
+ break;
+ case erotgFLEX:
+ case erotgFLEX2:
+ /* Do NOT subtract the center of mass in the low level routines! */
+ clear_rvec(erg->xc_center);
+ do_flexible(MASTER(cr),er,rotg,g,x,box,t,step,outstep_rot,outstep_slab);
+ break;
+ default:
+ gmx_fatal(FARGS, "No such rotation potential.");
+ break;
+ }
+ }
+
+#ifdef TAKETIME
+ if (MASTER(cr))
+ fprintf(stderr, "%s calculation (step %d) took %g seconds.\n", RotStr, step, MPI_Wtime()-t0);
+#endif
+
+ /* Stop the enforced rotation cycle counter and add the computation-only
+ * cycles to the force cycles for load balancing */
+ cycles_comp = gmx_cycles_read() - cycles_comp;
+
+ if (DOMAINDECOMP(cr) && wcycle)
+ dd_cycles_add(cr->dd,cycles_comp,ddCyclF);
+}
--- /dev/null
- mdatoms->nr,state->x,state->box)) {
- if (gmx_parallel_env_initialized())
- {
- gmx_finalize();
- }
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "typedefs.h"
+#include "smalloc.h"
+#include "sysstuff.h"
+#include "vec.h"
+#include "statutil.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 "trnio.h"
+#include "xtcio.h"
+#include "mdrun.h"
+#include "confio.h"
+#include "network.h"
+#include "pull.h"
+#include "xvgr.h"
+#include "physics.h"
+#include "names.h"
+#include "xmdrun.h"
+#include "ionize.h"
+#include "disre.h"
+#include "orires.h"
+#include "dihre.h"
+#include "pme.h"
+#include "mdatoms.h"
+#include "repl_ex.h"
+#include "qmmm.h"
+#include "domdec.h"
+#include "partdec.h"
+#include "topsort.h"
+#include "coulomb.h"
+#include "constr.h"
+#include "shellfc.h"
+#include "compute_io.h"
+#include "mvdata.h"
+#include "checkpoint.h"
+#include "mtop_util.h"
+#include "sighandler.h"
+#include "string2.h"
+#include "membed.h"
+
+#ifdef GMX_LIB_MPI
+#include <mpi.h>
+#endif
+#ifdef GMX_THREAD_MPI
+#include "tmpi.h"
+#endif
+
+#ifdef GMX_FAHCORE
+#include "corewrap.h"
+#endif
+
+
+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_seed,gmx_membed_t membed,
+ real cpt_period,real max_hours,
+ const char *deviceOptions,
+ unsigned long Flags,
+ gmx_runtime_t *runtime)
+{
+ gmx_mdoutf_t *outf;
+ gmx_large_int_t step,step_rel;
+ double run_time;
+ double t,t0,lam0;
+ gmx_bool bGStatEveryStep,bGStat,bNstEner,bCalcEnerPres;
+ gmx_bool bNS,bNStList,bSimAnn,bStopCM,bRerunMD,bNotLastFrame=FALSE,
+ bFirstStep,bStateFromTPX,bInitStep,bLastStep,
+ bBornRadii,bStartingFromCpt;
+ gmx_bool bDoDHDL=FALSE;
+ gmx_bool do_ene,do_log,do_verbose,bRerunWarnNoV=TRUE,
+ bForceUpdate=FALSE,bCPT;
+ int mdof_flags;
+ 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;
+ int n_xtc=-1;
+ rvec *x_xtc=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_bool bFFscan;
+ 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 bIonize=FALSE;
+ gmx_bool bTCR=FALSE,bConverged=TRUE,bOK,bSumEkinhOld,bExchanged;
+ gmx_bool bAppend;
+ gmx_bool bResetCountersHalfMaxH=FALSE;
+ gmx_bool bVV,bIterations,bFirstIterate,bTemp,bPres,bTrotter;
+ real mu_aver=0,dvdl;
+ int a0,a1,gnx=0,ii;
+ atom_id *grpindex=NULL;
+ char *grpname;
+ t_coupl_rec *tcr=NULL;
+ rvec *xcopy=NULL,*vcopy=NULL,*cbuf=NULL;
+ matrix boxcopy={{0}},lastbox;
+ tensor tmpvir;
+ real fom,oldfom,veta_save,pcurr,scalevir,tracevir;
+ real vetanew = 0;
+ 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_large_int_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.*/
+#ifdef GMX_FAHCORE
+ /* Temporary addition for FAHCORE checkpointing */
+ int chkpt_ret;
+#endif
+
+ /* Check for special mdrun options */
+ bRerunMD = (Flags & MD_RERUN);
+ bIonize = (Flags & MD_IONIZE);
+ bFFscan = (Flags & MD_FFSCAN);
+ 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 */
+ bIterations = ((IR_NPT_TROTTER(ir)) && (constr) && (!bRerunMD));
+ bTrotter = (bVV && (IR_NPT_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 || bFFscan)
+ {
+ ir->nstxtcout = 0;
+ }
+ groups = &top_global->groups;
+
+ /* Initial values */
+ init_md(fplog,cr,ir,oenv,&t,&t0,&state_global->lambda,&lam0,
+ nrnb,top_global,&upd,
+ nfile,fnm,&outf,&mdebin,
+ force_vir,shake_vir,mu_tot,&bSimAnn,&vcm,state_global,Flags);
+
+ clear_mat(total_vir);
+ clear_mat(pres);
+ /* Energy terms and groups */
+ snew(enerd,1);
+ init_enerdata(top_global->groups.grps[egcENER].nr,ir->n_flambda,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 (DEFORM(*ir))
+ {
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_lock(&deform_init_box_mutex);
+#endif
+ set_deform_reference_box(upd,
+ deform_init_init_step_tpx,
+ deform_init_box_tpx);
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_unlock(&deform_init_box_mutex);
+#endif
+ }
+
+ {
+ 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 {
+ if (PAR(cr)) {
+ /* Initialize the particle decomposition and split the topology */
+ top = split_system(fplog,top_global,ir,cr);
+
+ pd_cg_range(cr,&fr->cg0,&fr->hcg);
+ pd_at_range(cr,&a0,&a1);
+ } else {
+ top = gmx_mtop_generate_local_top(top_global,ir);
+
+ a0 = 0;
+ a1 = top_global->natoms;
+ }
+
+ state = partdec_init_local_state(cr,state_global);
+ f_global = f;
+
+ atoms2md(top_global,ir,0,NULL,a0,a1-a0,mdatoms);
+
+ if (vsite) {
+ set_vsite_top(vsite,top,mdatoms,cr);
+ }
+
+ if (ir->ePBC != epbcNONE && !ir->bPeriodicMols) {
+ graph = mk_graph(fplog,&(top->idef),0,top_global->natoms,FALSE,FALSE);
+ }
+
+ if (shellfc) {
+ make_local_shells(cr,mdatoms,shellfc);
+ }
+
+ if (ir->pull && PAR(cr)) {
+ dd_make_local_pull_groups(NULL,ir->pull,mdatoms);
+ }
+ }
+
+ 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);
+
+ if (MASTER(cr))
+ {
+ if (opt2bSet("-cpi",nfile,fnm))
+ {
+ /* 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);
+ }
+
+ if ((state->flags & (1<<estLD_RNG)) && (Flags & MD_READ_RNG)) {
+ /* Set the random state if we read a checkpoint file */
+ set_stochd_state(upd,state);
+ }
+
+ /* Initialize constraints */
+ if (constr) {
+ if (!DOMAINDECOMP(cr))
+ set_constraints(constr,top,ir,mdatoms,cr);
+ }
+
+ /* Check whether we have to GCT stuff */
+ bTCR = ftp2bSet(efGCT,nfile,fnm);
+ if (bTCR) {
+ if (MASTER(cr)) {
+ fprintf(stderr,"Will do General Coupling Theory!\n");
+ }
+ gnx = top_global->mols.nr;
+ snew(grpindex,gnx);
+ for(i=0; (i<gnx); i++) {
+ grpindex[i] = i;
+ }
+ }
+
+ if (repl_ex_nst > 0)
+ {
+ /* We need to be sure replica exchange can only occur
+ * when the energies are current */
+ check_nst_param(fplog,cr,"nstcalcenergy",ir->nstcalcenergy,
+ "repl_ex_nst",&repl_ex_nst);
+ /* This check needs to happen before inter-simulation
+ * signals are initialized, too */
+ }
+ if (repl_ex_nst > 0 && MASTER(cr))
+ repl_ex = init_replica_exchange(fplog,cr->ms,state_global,ir,
+ repl_ex_nst,repl_ex_seed);
+
+ if (!ir->bContinuation && !bRerunMD)
+ {
+ if (mdatoms->cFREEZE && (state->flags & (1<<estV)))
+ {
+ /* Set the velocities of frozen particles to zero */
+ for(i=mdatoms->start; i<mdatoms->start+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,f,
+ graph,cr,nrnb,fr,top,shake_vir);
+ }
+ if (vsite)
+ {
+ /* Construct the virtual sites for the initial configuration */
+ construct_vsites(fplog,vsite,state->x,nrnb,ir->delta_t,NULL,
+ top->idef.iparams,top->idef.il,
+ fr->ePBC,fr->bMolPBC,graph,cr,state->box);
+ }
+ }
+
+ debug_gmx();
+
+ /* 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,&pcurr,top_global->natoms,&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,&pcurr,top_global->natoms,&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 (bIterations)
+ {
+ bufstate = init_bufstate(state);
+ }
+ if (bFFscan)
+ {
+ snew(xcopy,state->natoms);
+ snew(vcopy,state->natoms);
+ copy_rvecn(state->x,xcopy,0,state->natoms);
+ copy_rvecn(state->v,vcopy,0,state->natoms);
+ copy_mat(state->box,boxcopy);
+ }
+
+ /* 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");
+ }
+
+ /* Set and write start time */
+ runtime_start(runtime);
+ print_date_and_time(fplog,cr->nodeid,"Started mdrun",runtime);
+ wallcycle_start(wcycle,ewcRUN);
+ if (fplog)
+ fprintf(fplog,"\n");
+
+ /* 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 = !opt2bSet("-cpi",nfile,fnm);
+ bInitStep = bFirstStep && (bStateFromTPX || bVV);
+ bStartingFromCpt = (Flags & MD_STARTFROMCPT) && bInitStep;
+ bLastStep = FALSE;
+ bSumEkinhOld = FALSE;
+ bExchanged = 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)
+ {
+ if (bRerunMD && rerun_fr.bLambda && (ir->delta_lambda!=0))
+ {
+ state_global->lambda = rerun_fr.lambda;
+ }
+ else
+ {
+ state_global->lambda = lam0 + step*ir->delta_lambda;
+ }
+ state->lambda = state_global->lambda;
+ bDoDHDL = do_per_step(step,ir->nstdhdl);
+ }
+
+ 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 for domain decomposition, use particle decomposition");
+ }
+ 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(fplog,vsite,state->x,nrnb,ir->delta_t,state->v,
+ top->idef.iparams,top->idef.il,
+ fr->ePBC,fr->bMolPBC,graph,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));
+
+ /* Copy back starting coordinates in case we're doing a forcefield scan */
+ if (bFFscan)
+ {
+ for(ii=0; (ii<state->natoms); ii++)
+ {
+ copy_rvec(xcopy[ii],state->x[ii]);
+ copy_rvec(vcopy[ii],state->v[ii]);
+ }
+ copy_mat(boxcopy,state->box);
+ }
+
+ 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 || bNStList ||
+ (ir->nstlist == -1 && nlh.nabnsb > 0));
+
+ if (bNS && ir->nstlist == -1)
+ {
+ set_nlistheuristics(&nlh,bFirstStep || bExchanged,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 ) && ( bNS || 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);
+ wallcycle_stop(wcycle,ewcDOMDEC);
+ /* If using an iterative integrator, reallocate space to match the decomposition */
+ }
+ }
+
+ if (MASTER(cr) && do_log && !bFFscan)
+ {
+ print_ebin_header(fplog,step,t,state->lambda);
+ }
+
+ if (ir->efep != efepNO)
+ {
+ update_mdatoms(mdatoms,state->lambda);
+ }
+
+ if (bRerunMD && rerun_fr.bV)
+ {
+
+ /* 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,&pcurr,top_global->natoms,&bSumEkinhOld,
+ CGLO_RERUNMD | CGLO_GSTAT | CGLO_TEMPERATURE);
+ }
+ clear_mat(force_vir);
+
+ /* Ionize the atoms if necessary */
+ if (bIonize)
+ {
+ ionize(fplog,oenv,mdatoms,top_global,t,ir,state->x,state->v,
+ mdatoms->start,mdatoms->start+mdatoms->homenr,state->box,cr);
+ }
+
+ /* Update force field in ffscan program */
+ if (bFFscan)
+ {
+ if (update_forcefield(fplog,
+ nfile,fnm,fr,
- if (gmx_parallel_env_initialized())
- {
- gmx_finalize();
- }
++ mdatoms->nr,state->x,state->box))
++ {
++ gmx_finalize_par();
++
+ exit(0);
+ }
+ }
+
+ /* 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).
+ */
+ bNstEner = do_per_step(step,ir->nstcalcenergy);
+ bCalcEnerPres =
+ (bNstEner ||
+ (ir->epc != epcNO && do_per_step(step,ir->nstpcouple)));
+
+ /* Do we need global communication ? */
+ bGStat = (bCalcEnerPres || bStopCM ||
+ do_per_step(step,nstglobalcomm) ||
+ (ir->nstlist == -1 && !bRerunMD && step >= nlh.step_nscheck));
+
+ do_ene = (do_per_step(step,ir->nstenergy) || bLastStep);
+
+ if (do_ene || do_log)
+ {
+ bCalcEnerPres = 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 |
+ (bNStList ? GMX_FORCE_DOLR : 0) |
+ GMX_FORCE_SEPLRF |
+ (bCalcEnerPres ? GMX_FORCE_VIRIAL : 0) |
+ (bDoDHDL ? GMX_FORCE_DHDL : 0)
+ );
+
+ if (shellfc)
+ {
+ /* Now is the time to relax the shells */
+ count=relax_shell_flexcon(fplog,cr,bVerbose,bFFscan ? step+1 : step,
+ ir,bNS,force_flags,
+ bStopCM,top,top_global,
+ constr,enerd,fcd,
+ state,f,force_vir,mdatoms,
+ nrnb,wcycle,graph,groups,
+ shellfc,fr,bBornRadii,t,mu_tot,
+ state->natoms,&bConverged,vsite,
+ outf->fp_field);
+ 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,top_global,groups,
+ state->box,state->x,&state->hist,
+ f,force_vir,mdatoms,enerd,fcd,
+ state->lambda,graph,
+ fr,vsite,mu_tot,t,outf->fp_field,ed,bBornRadii,
+ (bNS ? GMX_FORCE_NS : 0) | force_flags);
+ }
+
+ if (bTCR)
+ {
+ mu_aver = calc_mu_aver(cr,state->x,mdatoms->chargeA,
+ mu_tot,&top_global->mols,mdatoms,gnx,grpindex);
+ }
+
+ if (bTCR && bFirstStep)
+ {
+ tcr=init_coupling(fplog,nfile,fnm,cr,fr,mdatoms,&(top->idef));
+ fprintf(fplog,"Done init_coupling\n");
+ fflush(fplog);
+ }
+
+ 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);
+ }
+
+ update_coords(fplog,step,ir,mdatoms,state,
+ f,fr->bTwinRange && bNStList,fr->f_twin,fcd,
+ ekind,M,wcycle,upd,bInitStep,etrtVELOCITY1,
+ cr,nrnb,constr,&top->idef);
+
+ if (bIterations)
+ {
+ gmx_iterate_init(&iterate,bIterations && !bInitStep);
+ }
+ /* 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 (bIterations && iterate.bIterate) {
+ copy_coupling_state(state,bufstate,ekind,ekind_save,&(ir->opts));
+ }
+
+ bFirstIterate = TRUE;
+ while (bFirstIterate || (bIterations && iterate.bIterate))
+ {
+ if (bIterations && iterate.bIterate)
+ {
+ 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. */
+ dvdl = 0;
+
+ update_constraints(fplog,step,&dvdl,ir,ekind,mdatoms,state,graph,f,
+ &top->idef,shake_vir,NULL,
+ cr,nrnb,wcycle,upd,constr,
+ bInitStep,TRUE,bCalcEnerPres,vetanew);
+
+ if (!bOK && !bFFscan)
+ {
+ 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));
+ 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,&pcurr,top_global->natoms,&bSumEkinhOld,
+ cglo_flags
+ | CGLO_ENERGY
+ | (bStopCM ? CGLO_STOPCM : 0)
+ | (bTemp ? CGLO_TEMPERATURE:0)
+ | (bPres ? CGLO_PRESSURE : 0)
+ | (bPres ? CGLO_CONSTRAINT : 0)
+ | ((bIterations && iterate.bIterate) ? 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 temperture 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)
+ {
+ trotter_update(ir,step,ekind,enerd,state,total_vir,mdatoms,&MassQ,trotter_seq,ettTSEQ2);
+ }
+ else
+ {
+ update_tcouple(fplog,step,ir,state,ekind,wcycle,upd,&MassQ,mdatoms);
+ }
+ }
+
+ if (bIterations &&
+ 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,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);
+ }
+
+ if (fr->bSepDVDL && fplog && do_log)
+ {
+ fprintf(fplog,sepdvdlformat,"Constraint",0.0,dvdl);
+ }
+ enerd->term[F_DHDL_CON] += dvdl;
+ }
+
+ /* 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]; /* does this get preserved through checkpointing? */
+ }
+ if ((ir->eDispCorr != edispcEnerPres) && (ir->eDispCorr != edispcAllEnerPres))
+ {
+ saved_conserved_quantity -= enerd->term[F_DISPCORR];
+ }
+ }
+
+ /* ######## END FIRST UPDATE STEP ############## */
+ /* ######## If doing VV, we now have v(dt) ###### */
+
+ /* ################## START TRAJECTORY OUTPUT ################# */
+
+ /* Now we have the energies and forces corresponding to the
+ * coordinates at time t. We must output all of this before
+ * the update.
+ * for RerunMD t is read from input trajectory
+ */
+ mdof_flags = 0;
+ if (do_per_step(step,ir->nstxout)) { mdof_flags |= MDOF_X; }
+ if (do_per_step(step,ir->nstvout)) { mdof_flags |= MDOF_V; }
+ if (do_per_step(step,ir->nstfout)) { mdof_flags |= MDOF_F; }
+ if (do_per_step(step,ir->nstxtcout)) { mdof_flags |= MDOF_XTC; }
+ if (bCPT) { mdof_flags |= MDOF_CPT; };
+
+#if defined(GMX_FAHCORE) || defined(GMX_WRITELASTSTEP)
+ if (bLastStep)
+ {
+ /* Enforce writing positions and velocities at end of run */
+ mdof_flags |= (MDOF_X | MDOF_V);
+ }
+#endif
+#ifdef GMX_FAHCORE
+ if (MASTER(cr))
+ fcReportProgress( ir->nsteps, step );
+
+ /* sync bCPT and fc record-keeping */
+ if (bCPT && MASTER(cr))
+ fcRequestCheckPoint();
+#endif
+
+ if (mdof_flags != 0)
+ {
+ wallcycle_start(wcycle,ewcTRAJ);
+ if (bCPT)
+ {
+ if (state->flags & (1<<estLD_RNG))
+ {
+ get_stochd_state(upd,state);
+ }
+ if (MASTER(cr))
+ {
+ if (bSumEkinhOld)
+ {
+ state_global->ekinstate.bUpToDate = FALSE;
+ }
+ else
+ {
+ update_ekinstate(&state_global->ekinstate,ekind);
+ state_global->ekinstate.bUpToDate = TRUE;
+ }
+ update_energyhistory(&state_global->enerhist,mdebin);
+ }
+ }
+ write_traj(fplog,cr,outf,mdof_flags,top_global,
+ step,t,state,state_global,f,f_global,&n_xtc,&x_xtc);
+ if (bCPT)
+ {
+ nchkpt++;
+ bCPT = FALSE;
+ }
+ debug_gmx();
+ if (bLastStep && step_rel == ir->nsteps &&
+ (Flags & MD_CONFOUT) && MASTER(cr) &&
+ !bRerunMD && !bFFscan)
+ {
+ /* x and v have been collected in write_traj,
+ * because a checkpoint file will always be written
+ * at the last step.
+ */
+ fprintf(stderr,"\nWriting final coordinates.\n");
+ if (ir->ePBC != epbcNONE && !ir->bPeriodicMols &&
+ DOMAINDECOMP(cr))
+ {
+ /* Make molecules whole only for confout writing */
+ do_pbc_mtop(fplog,ir->ePBC,state->box,top_global,state_global->x);
+ }
+ write_sto_conf_mtop(ftp2fn(efSTO,nfile,fnm),
+ *top_global->name,top_global,
+ state_global->x,state_global->v,
+ ir->ePBC,state->box);
+ debug_gmx();
+ }
+ wallcycle_stop(wcycle,ewcTRAJ);
+ }
+
+ /* 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);
+ }
+ /* ################## END TRAJECTORY OUTPUT ################ */
+
+ /* Determine the wallclock run time up till now */
+ run_time = gmx_gettime() - (double)runtime->real;
+
+ /* 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 && run_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) &&
+ run_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 ||
+ run_time >= nchkpt*cpt_period*60.0)) &&
+ gs.set[eglsCHKPT] == 0)
+ {
+ gs.sig[eglsCHKPT] = 1;
+ }
+
+ if (bIterations)
+ {
+ gmx_iterate_init(&iterate,bIterations);
+ }
+
+ /* for iterations, we save these vectors, as we will be redoing the calculations */
+ if (bIterations && iterate.bIterate)
+ {
+ copy_coupling_state(state,bufstate,ekind,ekind_save,&(ir->opts));
+ }
+ bFirstIterate = TRUE;
+ while (bFirstIterate || (bIterations && iterate.bIterate))
+ {
+ /* We now restore these vectors to redo the calculation with improved extended variables */
+ if (bIterations)
+ {
+ 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;
+ if (!(bRerunMD && !rerun_fr.bV && !bForceUpdate))
+ {
+ wallcycle_start(wcycle,ewcUPDATE);
+ dvdl = 0;
+ /* UPDATE PRESSURE VARIABLES IN TROTTER FORMULATION WITH CONSTRAINTS */
+ if (bTrotter)
+ {
+ if (bIterations && iterate.bIterate)
+ {
+ 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(fplog,step,ir,state,ekind,wcycle,upd,&MassQ,mdatoms);
+ update_pcouple(fplog,step,ir,state,pcoupl_mu,M,wcycle,
+ upd,bInitStep);
+ }
+
+ if (bVV)
+ {
+ /* velocity half-step update */
+ update_coords(fplog,step,ir,mdatoms,state,f,
+ fr->bTwinRange && bNStList,fr->f_twin,fcd,
+ ekind,M,wcycle,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);
+ }
+
+ update_coords(fplog,step,ir,mdatoms,state,f,fr->bTwinRange && bNStList,fr->f_twin,fcd,
+ ekind,M,wcycle,upd,bInitStep,etrtPOSITION,cr,nrnb,constr,&top->idef);
+ wallcycle_stop(wcycle,ewcUPDATE);
+
+ update_constraints(fplog,step,&dvdl,ir,ekind,mdatoms,state,graph,f,
+ &top->idef,shake_vir,force_vir,
+ cr,nrnb,wcycle,upd,constr,
+ bInitStep,FALSE,bCalcEnerPres,state->veta);
+
+ 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,&pcurr,top_global->natoms,&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);
+
+ update_coords(fplog,step,ir,mdatoms,state,f,fr->bTwinRange && bNStList,fr->f_twin,fcd,
+ ekind,M,wcycle,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,&dvdl,ir,ekind,mdatoms,state,graph,f,
+ &top->idef,tmp_vir,force_vir,
+ cr,nrnb,wcycle,upd,NULL,
+ bInitStep,FALSE,bCalcEnerPres,
+ state->veta);
+ }
+ if (!bOK && !bFFscan)
+ {
+ gmx_fatal(FARGS,"Constraint error: Shake, Lincs or Settle could not solve the constrains");
+ }
+
+ if (fr->bSepDVDL && fplog && do_log)
+ {
+ fprintf(fplog,sepdvdlformat,"Constraint",0.0,dvdl);
+ }
+ enerd->term[F_DHDL_CON] += dvdl;
+ }
+ 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(fplog,vsite,state->x,nrnb,ir->delta_t,state->v,
+ top->idef.iparams,top->idef.il,
+ fr->ePBC,fr->bMolPBC,graph,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 ############ */
+ 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,&pcurr,top_global->natoms,&bSumEkinhOld,
+ cglo_flags
+ | (!EI_VV(ir->eI) ? 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)
+ | (bIterations && iterate.bIterate ? 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 (bIterations &&
+ done_iterating(cr,fplog,step,&iterate,bFirstIterate,
+ trace(shake_vir),&tracevir))
+ {
+ break;
+ }
+ bFirstIterate = FALSE;
+ }
+
+ update_box(fplog,step,ir,mdatoms,state,graph,f,
+ ir->nstlist==-1 ? &nlh.scale_tot : NULL,pcoupl_mu,nrnb,wcycle,upd,bInitStep,FALSE);
+
+ /* ################# END UPDATE STEP 2 ################# */
+ /* #### We now have r(t+dt) and v(t+dt/2) ############# */
+
+ /* The coordinates (x) were unshifted in update */
+ if (bFFscan && (shellfc==NULL || bConverged))
+ {
+ if (print_forcefield(fplog,enerd->term,mdatoms->homenr,
+ f,NULL,xcopy,
+ &(top_global->mols),mdatoms->massT,pres))
+ {
++ gmx_finalize_par();
++
+ fprintf(stderr,"\n");
+ exit(0);
+ }
+ }
+ if (!bGStat)
+ {
+ /* We will not sum ekinh_old,
+ * so signal that we still have to do it.
+ */
+ bSumEkinhOld = TRUE;
+ }
+
+ if (bTCR)
+ {
+ /* Only do GCT when the relaxation of shells (minimization) has converged,
+ * otherwise we might be coupling to bogus energies.
+ * In parallel we must always do this, because the other sims might
+ * update the FF.
+ */
+
+ /* Since this is called with the new coordinates state->x, I assume
+ * we want the new box state->box too. / EL 20040121
+ */
+ do_coupling(fplog,oenv,nfile,fnm,tcr,t,step,enerd->term,fr,
+ ir,MASTER(cr),
+ mdatoms,&(top->idef),mu_aver,
+ top_global->mols.nr,cr,
+ state->box,total_vir,pres,
+ mu_tot,state->x,f,bConverged);
+ debug_gmx();
+ }
+
+ /* ######### BEGIN PREPARING EDR OUTPUT ########### */
+
+ /* sum up the foreign energy and dhdl terms */
+ sum_dhdl(enerd,state->lambda,ir);
+
+ /* 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);
+ }
+ /* Check for excessively large energies */
+ if (bIonize)
+ {
+#ifdef GMX_DOUBLE
+ real etot_max = 1e200;
+#else
+ real etot_max = 1e30;
+#endif
+ if (fabs(enerd->term[F_ETOT]) > etot_max)
+ {
+ fprintf(stderr,"Energy too large (%g), giving up\n",
+ enerd->term[F_ETOT]);
+ }
+ }
+ /* ######### END PREPARING EDR OUTPUT ########### */
+
+ /* Time for performance */
+ if (((step % stepout) == 0) || bLastStep)
+ {
+ runtime_upd_proc(runtime);
+ }
+
+ /* Output stuff */
+ if (MASTER(cr))
+ {
+ gmx_bool do_dr,do_or;
+
+ if (!(bStartingFromCpt && (EI_VV(ir->eI))))
+ {
+ if (bNstEner)
+ {
+ upd_mdebin(mdebin,bDoDHDL, TRUE,
+ t,mdatoms->tmass,enerd,state,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(outf->fp_ene,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?");
+ }
+ }
+ }
+
+
+ /* Remaining runtime */
+ if (MULTIMASTER(cr) && (do_verbose || gmx_got_usr_signal() ))
+ {
+ if (shellfc)
+ {
+ fprintf(stderr,"\n");
+ }
+ print_time(stderr,runtime,step,ir,cr);
+ }
+
+ /* Replica exchange */
+ bExchanged = FALSE;
+ if ((repl_ex_nst > 0) && (step > 0) && !bLastStep &&
+ do_per_step(step,repl_ex_nst))
+ {
+ bExchanged = replica_exchange(fplog,cr,repl_ex,
+ state_global,enerd->term,
+ state,step,t);
+
+ if (bExchanged && 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 (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,runtime);
+ wcycle_set_reset_counters(wcycle,-1);
+ /* Correct max_hours for the elapsed time */
+ max_hours -= run_time/(60.0*60.0);
+ bResetCountersHalfMaxH = FALSE;
+ gs.set[eglsRESETCOUNTERS] = 0;
+ }
+
+ }
+ /* End of main MD loop */
+ debug_gmx();
+
+ /* Stop the time */
+ runtime_end(runtime);
+
+ if (bRerunMD && MASTER(cr))
+ {
+ close_trj(status);
+ }
+
+ if (!(cr->duty & DUTY_PME))
+ {
+ /* Tell the PME only node to finish */
+ gmx_pme_finish(cr);
+ }
+
+ if (MASTER(cr))
+ {
+ if (ir->nstcalcenergy > 0 && !bRerunMD)
+ {
+ print_ebin(outf->fp_ene,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 (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);
+ }
+
+ runtime->nsteps_done = step_rel;
+
+ return 0;
+}