CCL Home Page
Up Directory CCL moldy-2.12g.com
$! This is a DCL shar-type archive created by Unix dclshar.
$!
$CREATE Aaaa_Read.Me
$DECK
         
		#     # ####### #       ######  #     #
		##   ## #     # #       #     #  #   # 
		# # # # #     # #       #     #   # #  
		#  #  # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # ####### ####### ######     #   

                Copyright Keith Refson January 1990
		All rights reserved


Moldy is a general-purpose molecular dynamics simulation program which
I wrote initially for my own research into aqueous solutions at
mineral surfaces.  However it is sufficiently flexible that it ought
to be useful for a wide range of simulation calculations of atomic,
ionic and molecular systems.

Moldy is available by anonymous file transfer from Oxford.  Connect to
"earth.ox.ac.uk" using "ftp", with an account name of "anonymous" and
your email address as password.  The relevant files are all in the
"/pub" directory and are

* moldy-2.8.tar.Z     - The Unix distribution (also for MSDOS)
* moldy-2.8.com       - The VMS distribution
* moldy-manual.ps.Z   - The Manual in PostScript form.  Note that
                        the distribution files already contain 
                        the LaTeX source.

Please note that moldy is copyrighted and distributed under the GNU
public license which is designed to encourage its distribution and
modification.  This is to ensure that the source code of moldy and any
improvements made to it by anybody remains available to anyone who
wishes to use it.  If you change or improve Moldy, please tell me 
and if practical and appropriate I will incorporate your modifications
into a future release.  I hope that as time goes on Moldy will become
yet more comprehensiva as a result of your input.

I am also keeping a list of email addresses of anyone who uses the
program for notification of updates, bugs and so forth.  Please notify
me if you would like to be added to this list, preferably by email to
Keith.Refson@earth.ox.ac.uk.


Contents of the distribution:

accel.c
algorith.c
alloc.c
aux.c
beeman.c		These files are the source code for Moldy.
convert.c
dump.c
ewald.c
force.c
input.c
eigens.c
kernel.c
main.c
matrix.c
output.c
quaterns.c
rdf.c
restart.c
startup.c
values.c
xdr.c
parallel.c
   
defs.h
messages.h		'Include' files for above files
structs.h
xdr.h

stddef.h
time.h			replacement ANSI C include files for non-ANSI systems.
stdlib.h
string.h


dumpanal.c
dumpconv.c
dumpext.c		Source code for 'utility' programs.
mdshak.c
mextract.c

getopt.c		Support routine for utility progs.

Makefile		Make file for Moldy.

compile.com		Master compile file for VMS.  Calls all the others.
compile_moldy.com	Compile file for "moldy" itself.
compile_utils.com	Link file for "moldy" itself.       
link_moldy.com		Compile file for utilities.
link_utils.com		Link file for utilities.
defcomm.com		Defines comands - execute from your LOGIN.COM

moldy.tex		LaTeX source for manual
moldy.bbl

mcy.in
methane.in
mgclh2o.in		Example system specification files
tip4p.in
tips2.in

control.mgclh2o
control.tip4p		Example control files
control.water
control.100

UNPACKING
---------
A. Unix distribution

This distribution of Moldy takes the form of a bourne shell (shar)
archive, or a compressed tar archive.  To unpack the former, cd to the
directory where you wish the files to go and type /bin/sh moldy.shar
(or whatever the name of this file is).  The tar archive is unpacked
with
% uncompress moldy.tar
% tar xvf moldy.tar

B. VMS 

The VMS version of Moldy comes as a DCL archive, moldy.com. To unpack:
$ @moldy
This creates all the files needed in the current directory.

Alternatively, versions of "uncompress" and "tar" are available for
VMS, though they are not standard.  If you have them then you can
unpack the "moldy.tar.Z" archive (suitably renamed) in the same way.

COMPILING
---------

A. UNIX

Stage 1.
You ought to be able to type "make moldy utilities" on just about any
flavour of unix and build a working version.  However if you want to
get the best performance you will have to work a bit harder with
compiler options.  You may also find that the compile fails for
obscure reasons.  Don't worry, some compile options will probably sort
that out too.

Stage 2.  
Edit "Makefile", choose a suitable set of compiler options for your
machine and uncomment them. Moldy has been test compiled on most
modern workstations as well as vector super and minisuper-computers,
so you only have to select the preset options.

N.B. IBM RS6000 This compiler does not supply any "identification"
macro so you MUST have -DRS6000 in the c89 command line.  Otherwise
the XDR stuff won't work.

Stage 3.  
This section describes how to "hand-tune" compiler options.
If you get this far you have probably got a different machine or
compiler system from any of the tested ones.  Moldy recognises a
number of C preprocessor macros which adjust its expectations of the
compiler and operating system.  These are best defined using the
"-DMACRO-NAME" option of most unix C compilers.

MACRO		Purpose
-----           -------
	The following two macros are used to select the ANSI C
	"stdarg" mechanism. Default is older "varargs" mechanism. 

__STDC__	Automatically defined by ANSI compilers in strict mode.
ANSI		Alternative for ANSI compiler in non-strict mode which
		does not define __STDC__.

ANSI_LIBS	Set this if your libraries and header files conform
		to those expected in the ANSI 89 Standard.  Otherwise
		extra replacement routines and header files to
		remedy the deficiencies of older systems are included.

		Set this if at all possible.  Only in an ANSI
		environment can you be sure that all needed headers and
		library routines will be present.  The alternative is
		a kludge which works most of the time on most machines.

		This is automatically set in "defs.h" for some
		machines/compilers vhich are known to have ANSI libraries.

USE_XDR		Turn on support for the portable binary dump and
		restart files using the XDR mechanism.  This is the
		only area where Moldy departs substantially from the
		ANSI C standard so it is optional.  Nevertheless it
		is so massively useful that it is on by default.

		As this is not part of the standard it may not compile
		correctly on some systems if the compiler is in
		"strict ANSI" mode.  Use the default  or the "relaxed"
		or "extended" ansi mode often provided. 

		You may well need to add a library to the link using
		the LDFLAGS variable in the Makefile.  For
		example, solaris 2 on Suns needs the "-lnsl"
		option and SGI's need the "-lsun" option.

		You may also need to define some other macro to get
		the headers correctly included viz:

_ALL_SOURCE 	(IBM RS6000)
_HPUX_SOURCE	(HPUX)
		These are needed to enable stuff needed for the XDR
		routines and headers on IBM and HP machines.  
		N.B. These are actually set automatically in the Moldy
		header "defs.h" (but see note about need for -DRS6000
		on IBM).  However other systems may require something
		similar .

SPMD		Compile for SPMD (Single Program Multiple Data) parallel
		execution.  You must also specify one of the macros
		BSP, MPI or TCGMSG to select a message-passing library
		and have the appropriate include paths and libraries
		defined in the Makefile.

B. VMS (VAX/VMS and OpenVMS/AXP)

Just execute the "compile.com" DCL command file which will build Moldy
and the utilities.  All the required macros are set in "defs.h".  It
also executes the command file "defcomm.com" which defines the command
symbols to execute the programs.  It is a good idea to execute this
file from your LOGIN.COM to make them available every time you log in.

N.B. Depending on how your VMS system is set up you may need to take
additional steps to link moldy with the C runtime library.  Consult
your local documentation or systems staff.  If the C library isn't linked
by default the command

   $ assign sys$library:vaxcrtl lnk$library
   
before the executing the compile command file

   $ @compile

may well do the trick.


	RUNNING
	-------

Try it out by typing

	moldy control.water

to do a 10 timestep simulation of water.

PRINTING THE MANUAL
-------------------

The LaTeX source, "moldy.tex" and bibliography file, "moldy.bbl"
are supplied. If you have LaTeX, "latex moldy" a couple of times
to get the cross-references correct and print the dvi file using
dvips or dvi2ps, or whatever dvi output you normally use.

There is a "moldy.dvi" target in the make file so just "make
moldy.dvi" ought to do the trick.

PARALLEL VERSION
----------------

A.  Shared memory.
    -------------
    The sources contain separate versions of ewald.c and force.c with
    the appropriate code and compiler directives for compilation on
    certain shared-memory parallel machines including Stardent Titan,
    Convex and Cray architectures.  To use, you must REPLACE ewald.c
    and force.c with the file ewald_parallel.c and force_parallel.c
    respecively.  You must also define the preprocessor symbol
    PARALLEL during the compilation (eg with the compiler option
    -DPARALLEL).

    The code reads the environment to decide how many processors
    to execute on.  The name of the env variable is usually the
    same one as the manufacturers use for the same purpose
    NCPUS for the CRAY and THREADS on everything else.  Use
    the SETENV command (for c-chell) or the bourne-shell equivalent
    to before starting a run.

    Note. The Stardent Titan version does not work as shipped because
    the system supplied version of "malloc" can not be safely called
    from a parallel program.  Contact the author for a "thread-safe"
    version which can.

B.  Distributed Memory
    ------------------

    To build this version you must have one of the three supported
    message-passing libraries installed on the target system.  These
    are the Oxford BSP library, MPI (the new standardised
    message-passing library interface) and TCGMSG (the Theoretical
    Chamistry message-passing system).  Then define the macro PARLIBC
    in the Makefile to contain the "include" path for the library
    header files, and the preprocessor symbols SPMD and one of MPI,
    BSP and TCGMSG.  The macro PARLIBL should be defined to link with
    the appropriate libraries.

    To find out how to set up a parallel run, consult the documentation
    for your parallel system, as this varies.  

    The parallel performance increses with system size as the amount
    of work in the force and ewald sum loops increases as a proportion
    of the total work and with respect to the communication overhead.
    A speedup of nearly 7 on an 8-processor IBM SP1 has been
    demonstrated for the run "control.big", and in general the larger
    the system the better the parallel performance.  

    The parallel interface is contained within a single file
    "parallel.c" and versions for other MP libraries should be
    relatively easy to add with a few hours of programming effort.

    Alternative Ewald:
    ------------------

    There is an alternative version of Ewald.c which uses W. Smith's
    RIL paralellization strategy in ewald-RIL.c.  This uses far less
    memory but at the cost of doing parallel communication in the
    inner loops.  This may work reasonably on parallel machines
    with very short latencies, but on many it serializes the whole
    code!
$EOD
$!
$CREATE compile.com
$DECK
$ write sys$output "===========Building Moldy============"
$ @compile_moldy
$ @link_moldy
$ write sys$output "===========Building utilities============"
$ @compile_utils
$ @link_utils
$ @defcomm
$ write sys$output "===========Done============"
$EOD
$!
$CREATE accel.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * Accel     This file contains the 'driver' function for a single timestep - *
 *           "do_step" and other functions which need access to the system    *
 *           and species structs, "rescale" and "distant_const". 	      *
 ******************************************************************************
 *      Revision Log
 *       $Log: accel.c,v $
 *       Revision 2.17  1996/11/05 16:47:19  keith
 *       Optimized site and site_force arrays to avoid cache conflicts.
 *
 *       Revision 2.16  1996/08/23 15:06:01  keith
 *       Fixed bug whereby rot elements of temp_value[] were uninitialized.
 *       This caused a crash on non-ieee machines.
 *
 *       Revision 2.15  1996/08/14 16:23:24  keith
 *       Fixed error in thermoststat implementation and integration.
 *
 *       Revision 2.14  1996/03/14 14:42:27  keith
 *       Altered "rescale()" to suntract net velocity in case of
 *       separate-species rescaling, since that doesn't conserve momentum.
 *
 *       Revision 2.13  1996/01/15 15:14:00  keith
 *       De "lint"-ed the code.
 *       Removed "old" RDF code call.
 *
 *       Revision 2.12  1995/12/07 17:43:53  keith
 *       Reworked V. Murashov's thermostat code.  Created new functions
 *       nhtherm() and gtherm() to calculate alphadot.
 *       Changed to Program units.
 *
 *       Revision 2.11  1995/12/05 11:24:57  keith
 *       Added new function "poteval" which calls "kernel" to evaluate
 *       potential at single point and modified "distant_const" to
 *       correctly evaluate long-range pressure correction.
 *
 *       Revision 2.10  1995/12/04 11:45:49  keith
 *       Nose-Hoover and Gaussian (Hoover constrained) thermostats added.
 *       Thanks to V. Murashov.
 *
 * Revision 2.9  1994/07/12  16:20:26  keith
 * Fixed bug whereby "dip_mom" left uninitialized for non-coulomb system.
 *
 * Revision 2.8  1994/07/07  16:57:01  keith
 * Updated for parallel execution on SPMD machines.
 * Interface to MP library routines hidden by par_*() calls.
 * Compile with -DSPMD to activate
 *
 * Revision 2.7  1994/06/08  13:07:39  keith
 * New version of array allocator which breaks up requests for DOS.
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".Changed size_t to own typedef size_mt == ulong.
 *
 * Revision 2.5  94/01/18  13:31:46  keith
 * Null update for XDR portability release
 * 
 * Revision 2.4  93/12/16  18:14:11  keith
 * Fixed divide-by-zero bug when rescaling on atomic systems.
 * (Only showed up on FPE trapping architectures.)
 * 
 * Revision 2.3  93/10/28  10:27:32  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.0  93/03/15  14:47:36  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.8.1.26  93/03/15  14:39:59  keith
 * Added GPL copyleft notice to permit release and distribution.
 * N.B.  Previous versions were copyright (C) by author and 
 * only licensed by explicit permission.
 * 
 * Revision 1.8.1.25  93/03/09  14:15:09  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.8.1.24  92/10/28  14:09:42  keith
 * Changed "site_[tp]" typedefs to avoid name clash on HP.
 * 
 * Revision 1.8.1.23  92/10/01  18:08:09  keith
 * Added mol_radius().  Function used in force.c for cutoff calculation.
 * 
 * Revision 1.8.1.22  92/09/22  14:48:19  keith
 * Tidied up calls to improve "lint" rating.
 * 
 * Revision 1.8.1.21  92/06/26  17:01:18  keith
 * Got rid of assumption that memory returned by talloc() or
 * arralloc() is zeroed.  This enhances ANSI compatibility.
 * Removed memory zeroing from alloc.c() in consequence.
 * 
 * Revision 1.8.1.20  92/03/19  15:46:28  keith
 * Removed spurious varlaibe errptr.
 * 
 * Revision 1.8.1.19  92/03/11  12:56:16  keith
 * Changed "scale-separately" parameter to "scale options"
 * 
 * Revision 1.8.1.18  91/11/26  10:22:58  keith
 * Corrections to calculate framework pressure/stress correctly.
 * Corrected calculation of distant-stress term  (Only 1 1/v necessary).
 * 
 * Revision 1.8.1.17  91/10/17  14:22:38  keith
 * Fixed bug in set up pointers in "torque" array.
 * 
 * Revision 1.8.1.16  91/08/15  18:09:16  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_mt and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.8.1.15  91/03/12  15:41:07  keith
 * Tidied up typedefs size_mt and include file 
 * Added explicit function declarations.
 * 
 * Revision 1.8.1.14  91/02/19  14:50:13  keith
 * Rewrote loop to work around titan compiler bug
 * 
 * Revision 1.8.1.13  90/12/19  12:04:56  keith
 * Added test to protect against infinite looping for velocity non-convergence.
 * 
 * Revision 1.8.1.12  90/10/25  18:05:55  keith
 * Modified rescale() to correctly handle separate scaling in case
 * of framework with no dynamics.  Also call thermalise if T=0.
 * 
 * Revision 1.8.1.11  90/10/23  20:12:09  keith
 * Added dummy function call to inhibit vectorization.
 * This allows use of 'ivdep' compiler options and also
 * works round certain bugs in cray's scc compiler.
 * 
 * Revision 1.8.1.10  90/10/16  14:47:06  keith
 * Workaround added to inhibit (incorrect) vectorization of loop 
 * at line 411 under cray scc 1.0
 * 
 * Revision 1.8.1.9  90/08/02  15:51:53  keith
 * Modified to exclude framework-framework interactions.
 * N.B. Excluded from pe and stress but NOT forces (as they sum to 0).
 * 
 * Revision 1.8.1.8  90/07/16  15:54:51  keith
 * Fixed bugs in constant-stress code
 * 
 * Revision 1.8.1.7  90/05/16  18:38:30  keith
 * Renamed own freer from cfree to tfree.
 * 
 * Revision 1.8.1.6  90/05/16  14:18:58  keith
 * *** empty log message ***
 * 
 * Revision 1.8.1.5  90/04/16  18:18:58  keith
 * Changed call of "rahman" by adding "strain-mask" parameter.
 * 
 * Revision 1.8.1.4  90/01/15  12:23:27  keith
 * Corrected declaration of arralloc from void* to char* to keep lint happy.
 * 
 * Revision 1.8.1.3  89/12/22  19:31:56  keith
 * New version of arralloc() orders memory so that pointers come FIRST.
 * This means you can simply free() the pointer returned (if l.b. = 0).
 * 
 * Revision 1.8.1.2  89/12/22  11:14:38  keith
 * Reversed indices in 'site' and 'site_force' to allow stride of 1 in ewald.
 * 
 * Revision 1.8.1.1  89/10/06  16:22:51  keith
 * Make_sites() modified to wrap sites of framework back into MD box.
 * 
 * Revision 1.8  89/09/04  18:51:43  keith
 * Made De Leeuw surface dipole term optional (off by default).
 * This term SHOULD NOT be included for ionic systems.
 * 
 * Revision 1.7  89/08/10  17:28:05  keith
 * Fixed if statement so that rdf's started on rather than after 'begin-rdf'
 * 
 * Revision 1.6  89/07/03  18:17:25  keith
 * Made code to add dipole energy and force conditional, like Ewald.
 * 
 * Revision 1.5  89/06/22  15:42:17  keith
 * Tidied up loops over species to use one pointer as countes
 * 
 * Revision 1.4  89/06/14  18:18:12  keith
 * Removed call to SCILIB function VCOPY and equivalents - use memcpy instead.
 * 
 * Revision 1.3  89/05/22  18:37:04  keith
 * Added option to scale velocities of each species separately
 * 
 * Revision 1.2  89/04/20  17:49:08  keith
 * Added code for surface dipole part of Ewald sum (After De Leeuw et al).
 * 
 * Revision 1.1  89/04/20  15:58:41  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/accel.c,v 2.17 1996/11/05 16:47:19 keith Exp $";
#endif
/*========================== Library include files ===========================*/
#include	"defs.h"
/*========================== Library include files ===========================*/
#include	
#include	"string.h"
#include	"stddef.h"
#if defined(DEBUG10) || defined(DEBUG2)
#include	
#endif
/*========================== program include files ===========================*/
#include "structs.h"
#include "messages.h"
/*========================== External function declarations ==================*/
gptr            *talloc();	       /* Interface to memory allocator       */
void            tfree();	       /* Free allocated memory	      	      */
void            afree();	       /* Free allocated array	      	      */
void            step_1();	       /* Step co-ordinates by Beeman algrthm */
void            step_2();	       /* Step velocities at above            */
void            beeman_2();	       /* As above for individual components  */
void            make_sites();	       /* Construct site coordinate arrays    */
void            mol_force();	       /* Calculare molecular from site force */
void            mol_torque();	       /* Calculate torques from site forces  */
void            rotate();	       /* Perform rotations given quaternions */
void            newton();	       /* Calculate accelerations from forces */
void            euler();	       /* Get quat 2nd derivatives            */
void            parinello();	       /* Get correction to c of m accns      */
void            rahman();	       /* Get h 2nd derivatives	              */
void            energy_dyad();	       /* Calculate mvv for stress term       */
void            force_calc();	       /* Calculate direct-space forces       */
double          dist_pot();	       /* Returns integrated potential fn     */
void            ewald();	       /* Get Ewald sum forces		      */
void            dump();		       /* Maintain and write dump data files  */
void            zero_real();	       /* Clear area of memory		      */
void            zero_double();	       /* Clear area of memory		      */
void            invert();	       /* Matrix inverter		      */
double          det();		       /* Returns matrix determinant	      */
void            mat_vec_mul();	       /* 3 x 3 Matrix by Vector multiplier   */
void            mean_square();	       /* Caluculates mean square of args     */
void            rdf_calc();	       /* Accumulate and bin rdf	      */
double 		value();	       /* Return thermodynamic average	      */
double 		roll_av();	       /* Return thermodynamic average	      */
double          vdot();		       /* Fast vector dot product	      */
double		sum();		       /* Fast vector sum.		      */
void            vscale();	       /* Vector by constant multiply	      */
double          vec_dist();	       /* normalised vector distance	      */
void		thermalise();	       /* Randomize velocities to given temp  */
double		trans_ke();	       /* Compute translational kinetic en.   */
double		rot_ke();	       /* Compute rotational kinetic en.      */
void            hoover_tr();           /* Corrects forces due to thermostat   */
void            hoover_rot();          /* Corrects forces due to thermostat   */
double          gaussiant();           /* Return Force*vel                    */
double          gaussianr1();          /* Return Torque*omega                 */
double          gaussianr2();          /* Return omega*I*omega                */
void            q_conj_mul();          /* Quat. conjugated x by quat. dot     */
void	inhibit_vectorization();       /* Self-explanatory dummy              */
void            kernel();              /* Potential function evaluation       */
#ifdef SPMD
void		par_rsum();
void		par_dsum();
#endif
#if defined(ANSI) || defined(__STDC__)
gptr		*arralloc(size_mt,int,...); /* Array allocator		      */
void		note(char *, ...);	/* Write a message to the output file */
void		message(int *, ...);	/* Write a warning or error message   */
#else
gptr		*arralloc();	        /* Array allocator		      */
void		note();			/* Write a message to the output file */
void		message();		/* Write a warning or error message   */
#endif
/*========================== External data references ========================*/
extern contr_mt control;                    /* Main simulation control parms. */
extern int 	ithread, nthreads;
/*========================== Macros ==========================================*/
#define ITER_MAX 10
#define	CONVRG	1.0e-7
/*  Can't rely on ANSI yet. */
#ifndef DBL_MIN
#   define DBL_MIN 1.0e-36
#endif
/*========================== Cache Parameters=================================*/
/* The default values are for the Cray T3D but are probably good enough 
 *  for most other systems too. */
#ifndef NCACHE
#   define NCACHE (256*sizeof(double)/sizeof(real))
#endif
#ifndef NLINE
#   define NLINE  (4*sizeof(double)/sizeof(real))
#endif
/*============================================================================*/
/******************************************************************************
 *   rescale    rescale velocities and quaternion derivatives to desired temp.*
 *   Exact behaviour is controlled by flag "control.scale_options".	      *
 *   This is a bit flag with the following meanings:			      *
 *	bit 0:	scale temperature for each species separately.		      *
 *	bit 1:  scale rotational and translational velocities separately      *
 *      bit 2:	use rolling averages rather than instantaneous "temperatures" *
 *	bit 3:  don't scale at all, but re-initialize from MB distribution.   *
 ******************************************************************************/
void
rescale(system, species)
system_mp	system;
spec_mp		species;
{
   spec_mp	spec;
   int		ispec, imol, i;
   double 	*temp_value = dalloc(2*system->nspecies);
   double	min_temp=MIN(value(t_n,0),roll_av(t_n,0));
   double	rtemp = 0.0, ttemp = 0.0, scale;
   double	total_mass;
   vec_mt	momentum;
   int		tdof=0, rdof=0;

   /*
    *  First get trans. and rot. "temperatures", either instantaneous
    *  or rolling averages for each species individually.
    */
   for(ispec = 0, spec = species; ispec < system->nspecies; ispec++, spec++)
   {
      if( control.scale_options & 0x4 )
      {
	 temp_value[2*ispec  ] = roll_av(tt_n,ispec);
	 temp_value[2*ispec+1] = roll_av(rt_n,ispec);
      }
      else
      {
	 temp_value[2*ispec  ] = value(tt_n,ispec);
	 temp_value[2*ispec+1] = value(rt_n,ispec);
      }
      if( ! spec->framework )
      {
	 if( temp_value[2*ispec  ] < min_temp )
	    min_temp = temp_value[2*ispec  ];
	 if( spec->rdof > 0 && temp_value[2*ispec+1] < min_temp )
	    min_temp = temp_value[2*ispec+1];
      }
   }

   /*
    *  Re initialize from Maxwell-Boltzmann distribution if explicitly
    *  requested, or if any temperature is zero.
    */
   if( min_temp < 1.0e5*DBL_MIN ||
       (control.scale_options & 0x8) )
   {
      thermalise(system, species);
      return;
   }

   /*
    *  Get average of translational and rotational temps (per species)
    */
   if( ! (control.scale_options & 0x2) )
      for(ispec = 0, spec = species; ispec < system->nspecies; ispec++, spec++)
	 if( ! spec->framework )
	    temp_value[2*ispec  ] = temp_value[2*ispec+1] = 
	       (3*temp_value[2*ispec  ] + spec->rdof*temp_value[2*ispec+1]) /
		  (3+spec->rdof);
	 
   /*
    *  Perform average over species if scaling together.
    */
   if( ! (control.scale_options & 0x1) )
   {
      for(ispec = 0, spec = species; ispec < system->nspecies; ispec++, spec++)
	 if( ! spec->framework )
	 {
	    ttemp += 3*spec->nmols*temp_value[2*ispec  ]; 
	    tdof += 3*spec->nmols;
	    rtemp += spec->rdof*spec->nmols*temp_value[2*ispec+1]; 
	    rdof += spec->rdof*spec->nmols;
	 }
      ttemp /= tdof;
      if( rdof > 0 )
	 rtemp /= rdof;
      for(ispec = 0, spec = species; ispec < system->nspecies; ispec++, spec++)
	 if( ! spec->framework )
	 {
	    temp_value[2*ispec  ] = ttemp;
	    temp_value[2*ispec+1] = rtemp;
	 }
   }

   /*
    *  Actually do the scaling
    */
#ifdef DEBUG6
   fprintf(stderr,"Trans T\t\tRot T\n");
#endif
   for(ispec = 0, spec = species; ispec < system->nspecies; ispec++, spec++)
   {
#ifdef DEBUG6
      fprintf(stderr,"%8.2f\t%8.2f\n",
	      temp_value[2*ispec],temp_value[2*ispec+1]);
#endif
      if( ! spec->framework )
      {
	 scale = sqrt(control.temp / temp_value[2*ispec]);
	 vscale(3 * spec->nmols,   scale, spec->vel[0], 1);
	 if( spec->rdof > 0 )
	 { 
	    scale = sqrt(control.temp / temp_value[2*ispec+1]);
	    vscale(4 * spec->nmols, scale, spec->qdot[0], 1);
	    vscale(4 * spec->nmols, scale*scale, spec->qddot[0], 1);
	    vscale(4 * spec->nmols, scale*scale, spec->qddoto[0], 1);
	 }
	 
      }
   }
   /* 
    * Subtract spurious net velocity introduced by scaling species
    * separately.  This will introduce an apparent error into the
    * instantaneous "temperature".  But the error was there anyway
    * since net linear velocity shouldn't be included in the
    * calculation.  Do nothing for joint rescaling since that does
    * conserve momentum or for a framework system.
    */
   if( control.scale_options & 0x1 )
   {
      total_mass = 0.0;
      zero_real(momentum, 3);
      for (spec = species; spec < species+system->nspecies && ! spec->framework;
	   spec++)
      {
	 total_mass += spec->mass*spec->nmols;
	 for(i = 0; i < 3; i++)	
	    momentum[i] += spec->mass*sum(spec->nmols, spec->vel[0]+i,3);
      }
      if(spec == species+system->nspecies)/* Normal loop exit => no framework */
	 for (spec = species; spec < species+system->nspecies; spec++)
	    for(i = 0; i < 3; i++)	    
	       for(imol = 0; imol < spec->nmols; imol++)
		  spec->vel[imol][i] -= momentum[i] / total_mass;
   }
   tfree((gptr*)temp_value);
}

/******************************************************************************
 *   nhtherm Calculate acceleration term for Nose-hoover variable             *
 *   Exact behaviour is controlled by flag "control.scale_options".	      *
 *   This is a bit flag with the following meanings:			      *
 *	bit 0:	scale temperature for each species separately.		      *
 *	bit 1:  scale rotational and translational velocities separately      *
 *      bit 2:	use rolling averages rather than instantaneous "temperatures" *
 *	bit 3:  don't scale at all, but re-initialize from MB distribution.   *
 ******************************************************************************/
void
nhtherm(sys, species)
system_mp	sys;
spec_mp		species;
{
   int             ispec;
   int             nspecies = sys->nspecies;
   int             tdof=0, rdof=0;
   spec_mt	   *spec;
   double 	   *temp_value = dalloc(2*nspecies);
   double          rtemp_mass, ttemp_mass;
   double          ttemp = 0.0, rtemp = 0.0;

   for(spec=species, ispec = 0; ispec < nspecies; spec++, ispec++)
   {
      temp_value[2*ispec  ] = 
	 trans_ke(sys->h, spec->velp, spec->mass, spec->nmols)
	 /(1.5*spec->nmols*kB);
      if(spec->rdof > 0)                       /* Only if polyatomic species */
         temp_value[2*ispec+1] = 
	    rot_ke(spec->quat, spec->qdotp, spec->inertia, spec->nmols)
	    /(0.5*kB*spec->rdof*spec->nmols);
      else
	 temp_value[2*ispec+1] = 0.0;
   }
   /*
    *  Get average of translational and rotational temps (per species)
    */
   if( ! (control.scale_options & 0x2) )
   {   
      for(ispec = 0, spec = species; ispec < nspecies; ispec++, spec++)
      {
	 if( ! spec->framework )
	    temp_value[2*ispec  ] = temp_value[2*ispec+1] = 
	       (3*temp_value[2*ispec  ] + spec->rdof*temp_value[2*ispec+1]) /
	       (3+spec->rdof);
      }
      ttemp_mass = rtemp_mass = control.ttmass;
   }
   else 
   {
      ttemp_mass = control.ttmass;
      rtemp_mass = control.rtmass;
      /*
       * ttemp_mass and rtemp_mass are used here to make easier introduction
       * of different thermal masses for different species later on, provided 
       * such necessity rises 
       */
   }
	 
   /*
    *  Perform average over species if thermostatting together.
    */
   if( ! (control.scale_options & 0x1) )
   {
      for(ispec = 0, spec = species; ispec < nspecies; ispec++, spec++)
	 if( ! spec->framework )
	 {
	    ttemp += 3*spec->nmols*temp_value[2*ispec  ]; 
	    tdof += 3*spec->nmols;
	    rtemp += spec->rdof*spec->nmols*temp_value[2*ispec+1]; 
	    rdof += spec->rdof*spec->nmols;
	 }
      ttemp /= tdof;
      if( rdof > 0 )
	 rtemp /= rdof;
      for(ispec = 0, spec = species; ispec < nspecies; ispec++, spec++)
	 if( ! spec->framework )
	 {
	    temp_value[2*ispec  ] = ttemp;
	    temp_value[2*ispec+1] = rtemp;
	 }
   }
   /*
    * It might be necessary to zero total momenta of species of each type
    * if thermostating together
    */

   /*
    *  Find alphadot for Nose-Hoover thermostat
    */
   for(ispec = 0, spec = species; ispec < nspecies; ispec++, spec++)
   {
      if( ! spec->framework )
      {
	 sys->tadot[ispec] = 3*spec->nmols*kB / ttemp_mass 
                              * (temp_value[2*ispec] - control.temp);
	 sys->radot[ispec] = spec->rdof*spec->nmols*kB / rtemp_mass 
                              * (temp_value[2*ispec+1] - control.temp);
      }
   }
   tfree((gptr*)temp_value);
}

/******************************************************************************
 *   gtherm Calculate acceleration term for Gaussian thermostat variable      *
 *   Exact behaviour is controlled by flag "control.scale_options".	      *
 *   This is a bit flag with the following meanings:			      *
 *	bit 0:	scale temperature for each species separately.		      *
 *	bit 1:  scale rotational and translational velocities separately      *
 *      bit 2:	use rolling averages rather than instantaneous "temperatures" *
 *	bit 3:  don't scale at all, but re-initialize from MB distribution.   *
 ******************************************************************************/
void
gtherm(sys, species, force, torque)
system_mp	sys;
spec_mp		species;
vec_mp		force[];
vec_mp		torque[];
{
   int             j, ispec;
   int             nspecies = sys->nspecies;
   spec_mt	   *spec;
   vec_mp	   vel_tmp = ralloc(sys->nmols);
   quat_mp         qd_tmp;             /* Temporary for velocities   	      */
   double 	   *temp_value = dalloc(2*nspecies);
   double          ttemp = 0.0, rtemp = 0.0, alphat = 0.0, alphar = 0.0;

      mat_vec_mul(sys->h, sys->velp, vel_tmp, sys->nmols);
      for(ispec = 0, spec = species, j = 0; ispec < nspecies; ispec++, spec++)
      {
	 if( ! spec->framework )
         {
            sys->tap[ispec] = gaussiant(force[ispec], vel_tmp+j,  
                                       spec->nmols);
	    temp_value[2*ispec] = spec->mass *  gaussiant(vel_tmp+j, vel_tmp+j,
                                       spec->nmols);
                                           
	    if (spec->rdof > 0)
            {
               qd_tmp = qalloc(spec->nmols);
               q_conj_mul(spec->quat, spec->qdotp, qd_tmp, spec->nmols); 
	       vscale(4*spec->nmols, 2.0, qd_tmp[0], 1);
               sys->rap[ispec] = gaussianr1(torque[spec-species], qd_tmp,
                                          spec->nmols);
	       temp_value[2*ispec+1] = gaussianr2(qd_tmp,  
                                                  spec->inertia, spec->nmols);
               xfree(qd_tmp);
            } 
            else 
            {
               temp_value[2*ispec+1] = 0.0;
               sys->rap[ispec] = 0.0;
            }
         }
         j+= spec->nmols;
      }
   /*
    *  Get average of translational and rotational alpha's (per species)
    */
      if( ! (control.scale_options & 0x2) )
         for(ispec = 0, spec = species; ispec < nspecies; ispec++, spec++)
	    if( ! spec->framework )
            {
               sys->tap[ispec] = sys->rap[ispec] += sys->tap[ispec]; 
	       temp_value[2*ispec] = temp_value[2*ispec+1] += 
	                             temp_value[2*ispec];
            }
   /*
    *  Perform average over species if thermostatting together.
    */
      if( ! (control.scale_options & 0x1) )
      {
         ttemp = 0.0;
         rtemp = 0.0;
         for(ispec = 0, spec = species; ispec < nspecies; ispec++, spec++)
	    if( ! spec->framework )
	    {
	       ttemp += temp_value[2*ispec  ]; 
	       rtemp += temp_value[2*ispec+1]; 
               alphat += sys->tap[ispec];
               alphar += sys->rap[ispec];
	    }
         for(ispec = 0, spec = species; ispec < nspecies; ispec++, spec++)
         {
	    if( ! spec->framework )
	    {
	       temp_value[2*ispec  ] = ttemp;
	       temp_value[2*ispec+1] = rtemp;
               sys->tap[ispec] = alphat;
               sys->rap[ispec] = alphar;
	    }
	 }
      }
      /*
       * It might be necessary to zero total momenta of species of each type
       * if thermostating together
       */

   /*
    * Finally find alpha = alpha1/alpha2
    */
      for(ispec = 0, spec = species; ispec < nspecies; ispec++, spec++)
	 if( ! spec->framework )
	 {
            if (temp_value[2*ispec] != 0.0)
               sys->tap[ispec] /= temp_value[2*ispec];
            else  sys->tap[ispec] = 0.0;
                                       
            if (temp_value[2*ispec+1] != 0.0)
               sys->rap[ispec] /= temp_value[2*ispec+1];
            else  sys->rap[ispec] = 0.0;
                
         }
   xfree(vel_tmp);
   tfree((gptr*)temp_value);
}
/******************************************************************************
 * Poteval	      Return potential evaluated at a single point.           *
 ******************************************************************************/
static double
poteval(potpar, r, ptype)
real	potpar[];			/* Array of potential parameters      */
double	r;				/* Cutoff distance		      */
int	ptype;				/* Potential type selector	      */
{
   double pe = 0.0;
   real f,rr;
   real *pp[NPOTP];
   int  i;

   for(i=0; imax_id);	/* Numbers of each site
							 * type */
   double          c = 0.0;	       		/* Accumulator for result     */
/*
 * Count the sites
 */
   memst(site_count,0,system->max_id*sizeof(int));
   for (spec = species; spec < &species[system->nspecies]; spec++)
   {
NOVECTOR
      for (isite = 0; isite < spec->nsites; isite++)
      {
	 inhibit_vectorization();
	 site_count[spec->site_id[isite]] += spec->nmols;
      }
   }

   for (id = 1; id < system->max_id; id++)	/* Loops for sum over i,j     */
      for (jd = 1; jd < system->max_id; jd++)
      {
	 c -= 2 * PI * site_count[id] * site_count[jd]
	    * dist_pot(potpar[id + system->max_id*jd].p, cutoff, system->ptype);
	 if( iflag )
	    c += 2.0/3.0 * PI * site_count[id] * site_count[jd]
	    * CUBE(cutoff) * poteval(potpar[id + system->max_id*jd].p, cutoff, 
				     system->ptype);
      }

   xfree(site_count);
   return (c);
}
/******************************************************************************
 *  Shuffle    move down the 'acceleration' co-ordinates                      *
 *  current->old, old->very old, very old->oblivion to make room for the new  *
 *  ones at the next timestep.  Only the pointers are actually moved          *
 ******************************************************************************/
static vec_mp   v_tmp;
static quat_mp  q_tmp;
static real     *atmp;
#define shuffle(a, ao, avo, tmp)	{tmp = avo; \
					avo = ao; \
					ao  = a; \
					a   = tmp; }

/******************************************************************************
 *   do_step       This routine controls the main part of the calculation for *
 *   each timestep.   It performs the following actions:  It                  *
 *   1)  Allocates space for the dynamic arrays for site co-ordinates,        *
 *       site forces, molecular forces and torques.                           *
 *   2)  Builds the array, site, of site co-ordinates from the principal-frame*
 *       co-ordinates, c of m positions and molecular quaternions.            *
 *   3)  Steps the co-ordinates using the Beeman algorithm		      *
 *   4)  Calls the main inter-site force calculating routine, force_calc.     *
 *   5)  Calls the Ewald sum routine for charged systems.                     *
 *   6)  Evaluates the molecular forces and torques from the site forces      *
 *   7)  'shuffles' all the accelerations down to make room for the new ones  *
 *        about to be evaluated.                                              *
 *   8)  Applies Newton's equations of motion to calculate accelerations,     *
 *       Euler's equations and 2nd order quaternion method to get the         *
 *       quaternion accelerations and calculates the h-matrix accelerations   *
 *       in the Parinello and Rahman constant-stress method.                  *
 *   9)  Applies the P & R correction to the c of m accelerations.            *
 *   10) Steps the velocities using the Beeman algorithm,		      *
 *       and iterates steps 8-10 until convergence.			      *
 *   11) Deallocates all the dynamic arrays and returns the space to the heap *
 ******************************************************************************/
void 
do_step(sys, species, site_info, potpar, meansq_f_t, pe, dip_mom, stress,
	restart_header, backup_restart)
system_mp       sys;		       /* Pointer to system info	 (in) */
spec_mt         species[];	       /* Array of species info	 (in) */
site_mt         site_info[];	       /* Array of site info structures (in) */
pot_mt          potpar[];	       /* Array of potential parameters (in) */
vec_mt          meansq_f_t[][2];       /* Mean square force and torque (out) */
double          pe[];		       /* Potential energy real/Ewald  (out) */
vec_mt          dip_mom;	       /* Total system dipole moment   (out) */
mat_mt          stress;		       /* Virial part of stress	(out) */
restrt_mt	*restart_header;       /* What the name says. (in)	     */
int		backup_restart;	       /* Flag signalling backup restart (in)*/
{
/*
 * The following declarations are arrays of pointers to the forces
 * etc for each species.  That is force[i] is a pointer to the force on
 * molecule 0 of species i
 */
   vec_mp	*force = palloc(sys->nspecies),
		*torque = palloc(sys->nspecies);
   real		***site_sp = (real***)arralloc((size_mt)sizeof(real*), 2,
					       0, sys->nspecies-1, 0, 2),
  		***force_sp = (real***)arralloc((size_mt)sizeof(real*), 2,
					       0, sys->nspecies-1, 0, 2); 
/*
 * The following declarations are pointers to the force etc for the whole
 * system, and are set equal to (eg) force[0]
 */
   vec_mp          force_base = ralloc(sys->nmols),
		   torque_base = sys->nmols_r?ralloc(sys->nmols_r):0;
   int		nsarray = (sys->nsites - 1 | NCACHE - 1) + 1+NLINE;
   real		**site = (real**)arralloc((size_mt)sizeof(real), 2,
					  0, 2, 0, nsarray-1);
   real		**site_force = (real**)arralloc((size_mt)sizeof(real), 2,
						0, 2, 0, nsarray-1);
/*
 * Other local variables
 */
   real           *chg = dalloc(sys->nsites), *chg_ptr;
   vec_mp		   c_of_m = ralloc(sys->nmols);
   spec_mp         spec, fspec /*Framework species */;
   int             nspecies = sys->nspecies;
   int             ispec, imol, imol_r, isite;
   int             i, j;
   static boolean  init = true;
   static double   dist, distp;
   double          vol = det(sys->h);
   int             iter;
   mat_mt          ke_dyad, hinv;
   quat_mp         qd_tmp;             /* Temporary for velocities   	      */
   vec_mp          acc_tmp, vel_tmp;   /* Temporaries for iteration	      */
   int		   nsitesxf, nmolsxf;  /* Count of non-framework sites, mols. */
/*
 * Initialisation of distant potential constant - executed first time only
 */
   if (init)
   {
      dist  = distant_const(sys, species, potpar, control.cutoff,0);
      distp = distant_const(sys, species, potpar, control.cutoff,1);

      if( ithread == 0 )
	 note("Distant potential correction = %f, Pressure correction = %f",
	      CONV_E * dist / vol, CONV_P * distp / (vol * vol));
      init = false;
   }
/*
 * The next chunk of code sets up the dynamic arrays for the centre of mass
 * forces, torques, site vectors and site forces.  It is a little complex but
 * results in great simplicity in all of the called routines. The arrays
 * themselves are indicated by pointers *_base (*=force etc), and have
 * dimensions [n][3], n= number of molecules, polyatomics and sites. They are
 * subdivided into the appropriate segments for each molecular species eg
 * site_base[nspecies][nsites][3], BUT nsites depends on the species so it is
 * NOT a 3 dimensional array.  Instead the array of pointer 'site' is used.
 * Site[ispec] is a pointer to the sub-array for species ispec of dimension
 * [nsites][3]. If any or all of the molecular species are monatomic, then no
 * space is allocated for the torque array, and all the pointers are NULL.
 */
/*
 *  Count non-framework sites and molecules.
 */
   fspec = species; nsitesxf = 0; nmolsxf = 0;
   while (fspec < species+nspecies && ! fspec->framework)
   {
      nsitesxf += fspec->nsites * fspec->nmols;
      nmolsxf  += fspec->nmols;
      fspec++;
   }   
/*
 * Set up arrays of pointers to sites, forces etc for each species
 */
   isite = 0; imol = 0; imol_r = 0;
   for (ispec = 0, spec = species; ispec < nspecies; ispec++, spec++)
   {
      force[ispec] = force_base+imol;
      if (spec->quat)
	 torque[ispec] = torque_base+imol_r;
      for( i = 0; i < 3; i++ )
      {
	 site_sp[ispec][i] = site[i]+isite;
	 force_sp[ispec][i] = site_force[i]+isite;
      }
      imol += spec->nmols;
      if (spec->quat)
	 imol_r += spec->nmols;
      isite += spec->nmols * spec->nsites;
   }
/*
 * Set up array of site charges
 */
   chg_ptr = chg;
   for (spec = species; spec < species+nspecies; spec++)
      for (imol = 0; imol < spec->nmols; imol++)
	 for (isite = 0; isite < spec->nsites; isite++)
	    *chg_ptr++ = site_info[spec->site_id[isite]].charge;
   mat_vec_mul(sys->h, sys->c_of_m, c_of_m, sys->nmols);
/*
 * Set some accumulators to zero
 */
   zero_real(stress[0], 9);	       /* Initialise stress tensor   */
   zero_real(meansq_f_t[0][0], 6 * sys->nspecies);
   zero_real(dip_mom, 3);
   zero_real(site_force[0], nsarray);
   zero_real(site_force[1], nsarray);
   zero_real(site_force[2], nsarray);
   zero_double(pe, NPE);
/*
 * Initial co-ordinate step of Beeman algorithm.
 */
   step_1(sys);
/*
 * Calculate the site positions at this timestep - loop over species
 */
   for (spec = species; spec < &species[nspecies]; spec++)
   {
      make_sites(sys->h, spec->c_of_m, spec->quat, spec->p_f_sites,
		spec->framework,site_sp[spec-species],spec->nmols,spec->nsites);
#ifdef DEBUG1
   { int is;
     printf("%s co-ordinates\n",spec->name);
     for(is = 0; is < spec->nsites*spec->nmols; is++)
	printf("%24.15f %24.15f %24.15f\n", site_sp[spec-species][0][is],
	       		                   site_sp[spec-species][1][is],
	                                   site_sp[spec-species][2][is]);
  }
#endif
   }


/*
 * Real-space part of force evaluation - no loop over species for efficiency
 */
   force_calc(site, site_force, sys, species, chg, potpar, pe, stress);
/*
 * Reciprocal-space part of Ewald sum
 */
   if (control.alpha > ALPHAMIN)
   {
      ewald(site, site_force, sys, species, chg, pe + 1, stress);
   }
/*
 *  Sum Pot, energies, forces and stress from each parallel invocation
 */
#ifdef SPMD
   par_dsum(pe, NPE);
   par_rsum(stress[0], 9);
   par_rsum(site_force[0], 3*nsarray);
#endif
   if (control.alpha > ALPHAMIN)
   {
/*
 * Dipole moment contribution to forces and potential (De Leeuw, Perram
 * and Smith Proc Roy Soc A373, 27-56 (1980)
 */
      for (i = 0; i < 3; i++)
	 dip_mom[i] = vdot(sys->nsites, site[i], 1, chg, 1);
      if( control.surface_dipole )
      {
	 for (i = 0; i < 3; i++)
	    for ( isite = 0; isite < sys->nsites; isite++ )
	       site_force[i][isite] -= 4.0*PI/(3.0*vol)*dip_mom[i] * chg[isite];
	 
	 pe[1] += 2.0*PI/(3.0*vol) * SUMSQ(dip_mom);
      }
   }

/*
 * Calculate the centre of mass forces and torques from the site forces
 */
   for (spec = species; spec < &species[nspecies]; spec++)
   {
      ispec = spec-species;
      mol_force(force_sp[ispec], force[ispec], spec->nsites, spec->nmols);
      if (spec->rdof > 0)
	 mol_torque(force_sp[ispec], spec->p_f_sites,
		    torque[ispec], spec->quat, spec->nsites, spec->nmols);
   }

/*
 * Add correction term to convert from site to molecular virial
 */
   for (i = 0; i < 3; i++)
   {
      for (j = i + 1; j < 3; j++)
	 stress[j][i] = stress[i][j];
      for (j = 0; j < 3; j++)
      {
        /*
	 * Non-framework sites sum f.s = sum f.r - sum F.R
	 */
	 stress[i][j] -=
		  vdot(nsitesxf, site_force[i], 1, site[j], 1)
	        - vdot(nmolsxf, force_base[0] + i, 3, c_of_m[0] + j, 3);
	/*
         *  Framework sites -- can't use "site" array as sites have been
         *  relocated by pbc's back into md cell.
	 */
	if( fspec < species+nspecies )
	   for(imol=0; imol < fspec->nmols; imol++)
	      stress[i][j] -= vdot(fspec->nsites,
				   site_force[i]+nsitesxf+imol*fspec->nsites,1,
				   fspec->p_f_sites[0]+j,3);
      }
		                            
   }

/*
 * Calculate distant stress and potential terms and add
 */
   pe[0] += dist / vol;
   for (i = 0; i < 3; i++)
      stress[i][i] += distp / vol;

/*
 * Shuffle the accelerations (acc -> acco, acco-> accvo, accvo -> acc) Don't
 * actually move the data - just the pointers
 */
   shuffle(sys->acc, sys->acco, sys->accvo, v_tmp);
   shuffle(sys->qddot, sys->qddoto, sys->qddotvo, q_tmp);
   if (control.const_pressure)
      shuffle(sys->hddot, sys->hddoto, sys->hddotvo, v_tmp);
   if (control.const_temp == 1)
   {
      shuffle(sys->tadot, sys->tadoto, sys->tadotvo, atmp);
      shuffle(sys->radot, sys->radoto, sys->radotvo, atmp);
   }
   for (spec = species; spec < &species[nspecies]; spec++)
   {
      inhibit_vectorization();      /* Inhibits (incorrect) vectorization */
      shuffle(spec->acc, spec->acco, spec->accvo, v_tmp);
      if (spec->rdof > 0)
	 shuffle(spec->qddot, spec->qddoto, spec->qddotvo, q_tmp);
   }

/*
 * Now apply the Newton/Euler equations to find the accelerations and
 * quaternion second derivatives.
 */
   for (spec = species; spec < &species[nspecies]; spec++)
      newton(force[spec-species], spec->acc, spec->mass, spec->nmols);

/*
 * Correction to centre of mass accelerations for constant pressure algorithm
 * First get scaled accelerations by multiplying by the inverse h matrix and
 * add P&R term. Then calculate the 'accelerations' of the unit cell matrix
 * and use the velocity predictor to step the cell "velocities".
 */
   invert(sys->h, hinv);
   mat_vec_mul(hinv, sys->acc, sys->acc, sys->nmols);
   if (control.const_pressure)
   {
      zero_real(ke_dyad[0], 9);
      for (spec = species; spec < &species[nspecies]; spec++)
	 energy_dyad(ke_dyad, sys->h, spec->velp, spec->mass, spec->nmols);
      rahman(stress, sys->h, sys->hddot, ke_dyad,
	     control.pressure, control.pmass, control.strain_mask);
      beeman_2(sys->hdot[0], sys->hdotp[0], sys->hddot[0], sys->hddoto[0],
	       sys->hddotvo[0], 9);
   }
/*
 * Iterate linear velocity dependant parts with beeman step 2 until convergence
 */
   if (control.const_pressure || control.const_temp)
   {
      iter = 0;
      acc_tmp = ralloc(sys->nmols);
      vel_tmp = ralloc(sys->nmols);
      do
      {
	 iter++;
	 if(iter > ITER_MAX)
	    message(NULLI, NULLP, FATAL, NCNVRG, iter, 
		    vec_dist(vel_tmp[0], sys->velp[0], 3 * sys->nmols));
         if (control.const_pressure)
	    parinello(sys->h, sys->hdotp, sys->velp, sys->acc, acc_tmp,
		      sys->nmols);
         else 
	    memcp(acc_tmp[0], sys->acc[0], 3 * sys->nmols * sizeof(real));
/*
 * Nose-Hoover thermostat added by VVMurashov , started on 20.10.95
 *
 * Gaussian thermostat added by VVM , started on 3/11/95
 * General formular alpha = alpha1/alpha2, where alpha1 = SUM force*vel
 * and alpha2 = SUM mass * vel ^ 2. Sys->tap(rap) is used to store 
 * alpha1's and temp_value is used to store alpha2's temporarily.
 */
 	 if (control.const_temp == 1)
	 {
	    nhtherm(sys, species);
	    beeman_2(sys->ta, sys->tap, sys->tadot, sys->tadoto,
		     sys->tadotvo, nspecies);
#ifdef DEBUG3
   printf("ta %8.4f tap %8.4f tadot %8.4f tadoto %8.4f\n", sys->ta[0],
						 sys->tap[0],
						 sys->tadot[0],
						 sys->tadoto[0]);
#endif
	 }
	 if (control.const_temp == 2)
	    gtherm(sys, species, force, torque);
         if (control.const_temp)
         {
            for (ispec = 0, j = 0, spec = species; ispec < nspecies; ispec++,
                 spec++)
            {
	       if( ! spec->framework )
	       {
                 hoover_tr(sys->tap[ispec], acc_tmp+j, acc_tmp+j, 
                         sys->velp+j, spec->nmols);
               }
               j+=spec->nmols;
            }
         }
	 memcp(vel_tmp[0], sys->velp[0], 3 * sys->nmols * sizeof(real));
	 beeman_2(sys->vel[0], sys->velp[0], acc_tmp[0], sys->acco[0],
		  sys->accvo[0], 3 * sys->nmols);
      } while (vec_dist(vel_tmp[0], sys->velp[0], 3 * sys->nmols) > CONVRG);
#ifdef DEBUG
      printf("Velocities converged in %d iterations \n", iter);
#endif
      memcp(sys->acc, acc_tmp, 3*sys->nmols * sizeof(real));
      xfree(acc_tmp);
      xfree(vel_tmp);
   }
/*
 * Iterate angular velocity dependant parts with beeman step 2 until convergence
 */
   if (sys->nmols_r > 0)
   {
      iter = 0;
      qd_tmp = qalloc(sys->nmols_r);
      acc_tmp = ralloc(sys->nmols_r);
      do
      {
	 iter++;
	 if(iter > ITER_MAX)
	    message(NULLI, NULLP, FATAL, NCNVRG, iter, 
		    vec_dist(qd_tmp[0], sys->qdotp[0], 4 * sys->nmols_r));
 	 if (control.const_temp == 1)
	 {
	    nhtherm(sys, species);
	    beeman_2(sys->ra, sys->rap, sys->radot, sys->radoto,
		     sys->radotvo, nspecies);
	 }
	 if (control.const_temp == 2)
	    gtherm(sys, species, force, torque);
#ifdef DEBUG3
   printf("ra %8.4f rap %8.4f radot %8.4f radoto %8.4f\n", sys->ra[0],
						 sys->rap[0],
						 sys->radot[0],
						 sys->radoto[0]);
#endif
         for (ispec = 0, spec = species; ispec < nspecies; ispec++, spec++)
	    if (spec->rdof > 0)
            {
               if (control.const_temp)
               {
        /****************************************************************
         *  VVM uses qd_tmp as a temp array to store angular velocities *
         ****************************************************************/
                  q_conj_mul(spec->quat, spec->qdotp, qd_tmp, spec->nmols); 
		  vscale(4*spec->nmols, 2.0, qd_tmp[0], 1);
                  hoover_rot(sys->rap[ispec], spec->inertia, 
                             torque[spec-species], acc_tmp,
                             qd_tmp, spec->nmols);
               }
               else
	          memcp(acc_tmp[0], torque[spec-species], 3 * spec->nmols * 
                        sizeof(real)); 
	       euler(acc_tmp, spec->quat, spec->qdotp,
		     spec->qddot, spec->inertia, spec->nmols);
            }
	 memcp(qd_tmp[0], sys->qdotp[0], 4 * sys->nmols_r * sizeof(real));
	 beeman_2(sys->qdot[0], sys->qdotp[0], sys->qddot[0], sys->qddoto[0],
		  sys->qddotvo[0], 4 * sys->nmols_r);
      } while (vec_dist(qd_tmp[0], sys->qdotp[0], 4 * sys->nmols_r) > CONVRG);
#ifdef DEBUG
      printf("Quaternion derivatives converged in %d iterations \n", iter);
#endif
      xfree(qd_tmp);
      xfree(acc_tmp);
   }
/*
 *  Apply constraint to any framework molecules.
 */
   for (spec = species; spec < &species[nspecies]; spec++)
      if( spec->framework )
      {
	 zero_real(spec->acc[0], 3*spec->nmols);
	 zero_real(spec->vel[0], 3*spec->nmols);
      }
/*
 * Final MD update step
 */

   step_2(sys);

/*
 * Calculate mean-square forces and torques
 */
   for (ispec = 0, spec = species; ispec < nspecies; ispec++, spec++)
   {
      mean_square(force[ispec], meansq_f_t[ispec][0], spec->nmols);
      if (spec->rdof > 0)
	 mean_square(torque[ispec], meansq_f_t[ispec][1], spec->nmols);
   }

   if( ithread == 0 )
   {
#ifdef OLDRDF
/*
 * Accumulate radial distribution functions
 */
      if (control.rdf_interval > 0 && control.istep >= control.begin_rdf &&
	  control.istep % control.rdf_interval == 0)
	 rdf_calc(site, sys, species);
#endif
/*
 * Perform periodic dump of dynamic data
 */
      if (control.dump_interval > 0 && control.dump_level != 0 &&
	 control.istep >= control.begin_dump &&
	  (control.istep - control.begin_dump) % control.dump_interval == 0)
       dump(sys, force_base, torque_base, stress, pe[0] + pe[1], restart_header,
	      backup_restart);
   }
/*
 * Deallocate the dynamic storage before exiting
 */
   xfree(force);
   xfree(torque);
   afree((gptr*)site);
   afree((gptr*)site_force);
   xfree(force_base);
   if (torque_base != NULL)
      xfree(torque_base);
   afree((gptr*)site_sp);
   afree((gptr*)force_sp);
   xfree(chg);
   xfree(c_of_m);
}
/*******************************************************************************
 *  Mol_radius.  Determine and return the greatest molecular radius of any     *
 *  species in the system.  That is, the largest c-of-mass - site distance.    *
 *******************************************************************************/
double mol_radius(species, nspecies)
spec_mt	species[];
int	nspecies;
{
   spec_mp spec;
   static double radius = -1.0;
   double r;
   int	isite;

   if( radius >= 0.0 )
      return radius;

   radius = 0.0;
   for(spec = species; spec < species+nspecies; spec++)
      if( !spec->framework )
      {
	 for( isite = 0; isite < spec->nsites; isite++ )
	 {
	    r = SUMSQ(spec->p_f_sites[isite]);
	    radius = MAX(radius, r);
	 }
      }
   return radius;
}

$EOD
$!
$CREATE algorith.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * Algorith  This file contains functions to implement the simulation algor - *
 *           ithms and other functions which do not need access to the system *
 *           and species structs.  Contents:                     	      *
 * rotate()		Perform co-ordinate transformation from quaternions   *
 * Vec_dist()		Return 'distance' between 2 long vectors	      *
 * mol_force()		Calculate molecular centre of mass forces	      *
 * mol_torque()		Calculate molecular torques			      *
 * make_sites()		Generate atomic site co-ordinates from c-of-m etc     *
 * newton()		Calculate accelerations from forces		      *
 * euler()		Calculate quaternion accelerations from torques	      *
 * parinello()          Calculate P&R c-of-m acceleration term                * 
 * rahman()		Calculate unit cell matrix accelerations from stress  *
 * trans_ke()		Return translational kinetic energy.		      *
 * rot_ke()		Return rotational kinetic energy		      *
 * energy_dyad()	Calculate kinetic energy part of stress tensor	      *
 * gaussiant()          Return alpha in Gaussian thermostat(trans)            *
 * gaussianr1()         Return alpha1 in Gaussian thermostat(rot)             *
 * gaussianr2()         Return alpha2 in Gaussian thermostat(rot)             *
 * hoover_tr()          Calculate correction to the forces(accelerations)     *
 * hoover_rot()         due to thermostat                                     *
 ******************************************************************************
 *      Revision Log
 *       $Log: algorith.c,v $
 *       Revision 2.9  1995/12/04 11:45:49  keith
 *       Nose-Hoover and Gaussian (Hoover constrained) thermostats added.
 *       Thanks to V. Murashov.
 *
 * Revision 2.8  1994/07/07  16:52:14  keith
 * Performance optimization to mol_force.
 *
 * Revision 2.7  1994/06/08  13:22:31  keith
 * Null update for version compatibility
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".Changed size_t to own typedef size_mt == ulong.
 *
 * Revision 2.5  1994/01/18  13:32:05  keith
 * Null update for XDR portability release
 *
 * Revision 2.3  93/10/28  10:27:35  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.0  93/03/15  14:48:51  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.1.1.12  93/03/15  14:41:28  keith
 * Added GPL copyleft notice to permit release and distribution.
 * N.B.  Previous versions were copyright (C) by author and 
 * only licensed by explicit permission.
 * 
 * Revision 1.1.1.11  93/03/09  15:58:12  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.1.1.10  91/08/15  18:11:39  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_t and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.1.1.9  91/03/12  15:42:03  keith
 * Tidied up typedefs size_t and include file 
 * Added explicit function declarations.
 * 
 * Revision 1.1.1.8  90/10/22  16:41:43  keith
 * Make vec_dist() robust in case of all-zero vectors.
 * 
 * Revision 1.1.1.7  90/09/28  13:28:09  keith
 * Inserted braces around VECTORIZE directives and changed include files
 * for STARDtardent 3000 series (via cond. comp symbol "ardent").
 * 
 * Revision 1.1.1.6  90/07/16  15:55:25  keith
 * Fixed bugs in constant-stress code
 * 
 * Revision 1.1.1.5  90/05/16  18:39:06  keith
 * *** empty log message ***
 * 
 * Revision 1.1.1.4  90/04/16  18:19:29  keith
 * Modified rahman() to arbitrarily constrain h matrix by new parameter "mask".
 * 
 * Revision 1.1.1.3  89/12/21  16:29:43  keith
 * Reversed indices in 'site' and 'site_force' to allow stride of 1 in ewald.
 * 
 * Revision 1.1.1.2  89/10/24  17:17:25  keith
 * Modified pbc algorithm to use floor() library function.
 * Now works with non-orthorhombic cell.
 * 
 * Revision 1.1.1.1  89/10/06  16:23:57  keith
 * Make_sites() modified to wrap sites of framework back into MD box.
 * 
 * Revision 1.1  89/04/20  16:00:19  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/algorith.c,v 2.9 1995/12/04 11:45:49 keith Exp $";
#endif
/*========================== program include files ===========================*/
#include 	"defs.h"
#include 	"messages.h"
/*========================== Library include files ===========================*/
#include 	
#include 	"string.h"
#include	"stddef.h"
/*========================== External function declarations ==================*/
gptr            *talloc();	       /* Interface to memory allocator       */
void            tfree();	       /* Free allocated memory	      	      */
void	mat_vec_mul();			/* 3 x 3 Matrix by Vector multiplier  */
void	mat_mul();	          	/* 3 x 3 matrix multiplier	      */
void	mat_add();			/* Add 2 3x3 matrices                 */
void	mat_sca_mul();			/* Multiply 3x3 matrix by scalar      */
void	transpose();			/* transpose a 3x3 matrix	      */
void	invert();			/* invert a 3x3 matrix		      */
double	det();				/* Determinant of 3x3 matrix	      */
void	q_to_rot();			/* Make rotation matrix from quat'n   */
void	q_mul();
void	q_conj_mul();
double	vdot();				/* Vector dot product		      */
double	sum();				/* Vector sum			      */
void	vscale();
#if defined(ANSI) || defined(__STDC__)
void	note(char *, ...);		/* Write a message to the output file */
void	message(int *, ...);		/* Write a warning or error message   */
#else
void	note();				/* Write a message to the output file */
void	message();			/* Write a warning or error message   */
#endif
/*========================== Macros ==========================================*/
#define MATMUL(i, m, r, o) (m[i][0]*r[0][o] + m[i][1]*r[1][o] + m[i][2]*r[2][o])
/*============================================================================*/
/******************************************************************************
 *  rotate        Perform the rotation described by the quaternions in the    *
 *  second parameter on each of the co-ordinates in the first, putting the    *
 *  result in the third.  (Pawley,  Mol. Phys. 43, 1321-1330 (1981))          *
 *  NB this is different to Evans' formulation.                               *
 *  Apply each rotation to nvec/nquat vectors.                                *
 ******************************************************************************/
static
void rotate(r_in, r_out, nvec, quat, nquat, inv_mat)
vec_mp		r_in,		/* Co-ordinates to be rotated [n][3] (in)     */
		r_out;		/* Resulting co-ordinates [n][3]    (out)     */
quat_mp		quat;		/* Quaternions for the rotation.     (in)     */
int		nvec,		/* Number of co-ordinates.           (in)     */
		nquat;		/* Number of quaternions             (in)     */
invrot		inv_mat;	/* Flag to do inverse rotations      (in)     */
{
   mat_mt	rot;
   int		iquat;

   if(nvec % nquat != 0)
      message(NULLI, NULLP, FATAL, ROTLEN, nvec, nquat);

   for(iquat = 0; iquat < nquat; iquat++)
   {
      q_to_rot(quat[iquat], rot);
      if(inv_mat == inv)    transpose(rot, rot);
      mat_vec_mul(rot, r_in, r_out, nvec / nquat);
      r_in += nvec/nquat;   r_out += nvec/nquat;
   }
}
/******************************************************************************
 *  mean_square  Calculate the mean square of list of vectors for each cmpnt  *
 ******************************************************************************/
void	mean_square(x, meansq, nmols)
vec_mt	x[];
vec_mt	meansq;
int	nmols;
{
   int		i;
   for(i = 0; i < 3; i++)
      meansq[i] = vdot(nmols, x[0]+i, 3, x[0]+i, 3) / nmols;
}
/******************************************************************************
 *   vec_dist      Return the normalised distance between 2 vectors           *
 ******************************************************************************/
double	vec_dist(v1, v2, n)
real	*v1, *v2;		/* Input vectors			      */
int	n;			/* Length ie v1[n], v2[n]		      */
{
   register	double s=0, s1=0,s2=0;	/* Accumulators for sums	      */
   while(n-- > 0)
   {
      s  += (*v1 - *v2) * (*v1 - *v2);
      s1 += *v1 * *v1;
      s2 += *v2 * *v2;
      v1++; v2++;
   }
   s1 = MAX(s1,s2);
   if( s1 == 0.0 )
      return s1;
   else
      return(sqrt(s / s1));
}
/******************************************************************************
 *  molecule_force     Calculate the centre of mass forces on a number of     *
 *  molecules given the site forces and the site co-ordinates.                *
 ******************************************************************************/
void mol_force(site_force, force, nsites, nmols)
real		**site_force;	/* Site forces [nsites*nmols][3]        (in)  */
vec_mp		force;		/* Centre of mass forces [nmols][3]    (out)  */
int		nsites,		/* Number of sites on one molecule      (in)  */
		nmols;		/* Number of molecules                  (in)  */
{
   int	i,  imol, isite;
   double	f;

   for(imol = 0; imol < nmols; imol++)
      for(i = 0; i < 3; i++)
      {
	 f = 0;
	 for(isite=0; isite < nsites; isite++)
	    f += site_force[i][isite+imol*nsites];
	 force[imol][i] = f;
      }
}
/******************************************************************************
 *  molecule_torque    Calculate the torque on a number of identical          *
 *  molecules given the space frame site forces and co-ordinates.             *
 ******************************************************************************/
void mol_torque(site_force, site, torque, quat, nsites, nmols)
real		**site_force;	/* Principal frame site forces          (in)  */
vec_mp		site,		/* Principal frame site co-ordinates    (in)  */
		torque;		/* Molecular torques [nmols][3]        (out)  */
quat_mp		quat;		/* Molecular quaternions [nmols][4]     (in)  */
int		nsites,		/* Number of sites on one molecule      (in)  */
		nmols;		/* Number of molecules                  (in)  */
{
   vec_mp	princ_force = ralloc(nsites);
   int	i, j, k, imol, isite;
   register     double torq;

   for(imol = 0; imol < nmols; imol++)
   {
      for(i = 0; i < 3; i++)
      {
VECTORIZE
	 for(isite = 0; isite < nsites; isite++)
	    princ_force[isite][i] = site_force[i][isite+imol*nsites];
      }
      rotate(princ_force, princ_force, nsites, quat+imol, 1, inv);
      for(i = 0, j = 1, k = 2; i < 3; i++, j=(j+1)%3, k=(k+1)%3)
      {
         torq = 0.0;
VECTORIZE
	 for(isite = 0; isite < nsites; isite++)
	    torq += site[isite][j]*princ_force[isite][k]
	           -site[isite][k]*princ_force[isite][j];
         torque[imol][i] = torq;
      }
   }
   xfree(princ_force);
}
/******************************************************************************
 *  make_sites     Calculate the atomic site co-ordinates for nmols identical *
 *  molecules from the principal-frame sites, the quaternions and the centre  *
 *  of mass co-ordinates.  Called once for each molecular species.            *
 ******************************************************************************/
void make_sites(h, c_of_m_s , quat, p_f_sites, framework, site, nmols, nsites)
mat_mt		h;		/* Unit cell matrix h		     (in)     */
vec_mp		c_of_m_s,	/* Centre of mass co-ords [nmols][3] (in)     */
		p_f_sites;	/* Principal-frame sites [nsites][3] (in)     */
real		**site;		/* Sites [nmols*nsites][3]          (out)     */
int		framework;	/* Flag to signal framework structure (in)    */
quat_mp		quat;		/* Quaternions [nmols][4]            (in)     */
int		nmols,		/* Number of molecules                        */
		nsites;		/* Number of sites on each molecule           */
{
   int		imol, isite, i;	/* Counters				      */
   vec_mt	c_of_m;		/* Unscaled centre of mass co-ordinates       */
   vec_mt	*ssite = ralloc(nsites);
   register double	t;
   mat_mt	hinv;
   double	lx   = h[0][0], lxy  = h[0][1],
		ly   = h[1][1], lxz  = h[0][2],
		lz   = h[2][2], lyz  = h[1][2];
   invert(h,hinv);

   for(imol = 0; imol < nmols; imol++)
   {
      mat_vec_mul(h,c_of_m_s+imol,(vec_mp)c_of_m, 1);/* Get real c-of-m co-ords*/
      if(quat)
      {
         rotate(p_f_sites,ssite,nsites,quat+imol,1,noinv);
	 for(i = 0; i < 3; i++)
	    for(isite = 0; isite < nsites; isite++)
	       site[i][imol*nsites+isite] = ssite[isite][i] + c_of_m[i];
      }
      else
      {
	 for(i = 0; i < 3; i++)
	    for(isite = 0; isite < nsites; isite++)
	       site[i][imol*nsites+isite] = p_f_sites[isite][i] + c_of_m[i];
      }
   }

   if( framework )			/* Apply pbc's to put sites into cell */
      for( isite = 0; isite < nmols*nsites; isite++ )
      {
          site[0][isite] -= lx  *      floor(MATMUL(0,hinv,site,isite) + 0.5);
          site[0][isite] -= lxy * (t = floor(MATMUL(1,hinv,site,isite) + 0.5));
          site[1][isite] -= ly  * t;
          site[0][isite] -= lxz * (t = floor(MATMUL(2,hinv,site,isite) + 0.5));
          site[1][isite] -= lyz * t;
          site[2][isite] -= lz  * t;
      }
   xfree(ssite);
}
/******************************************************************************
 *  newton   Apply newton's equation to calculate the acceleration of a       *
 *  number of molecules given the force.                                      *
 ******************************************************************************/
void newton(force, acc, mass, nmols)
vec_mp		force,		/* Centre of mass forces [nmols][3]      (in) */
		acc;		/* Accelerations [nmols][3]             (out) */
double		mass;		/* Molecular mass                        (in) */
int		nmols;		/* Number of molecules                   (in) */
{
   int	imol, i;
   double	rmass = 1.0/mass;
   for(i=0; i < 3; i++)
      for(imol = 0; imol < nmols; imol++)
      {
         acc[imol][i] = force[imol][i] * rmass;
#ifdef DEBUG2
   printf("Newton accelerations for %4i mol %4i orient %8.4f \n",imol,
           i, acc[imol][i]);
#endif
      }
}
/******************************************************************************
 * euler  Use the Euler equations and the second-order quaternion method to   *
 * calculate the second derivatives of the molecular quaternions from the     *
 * torques etc.  Test for zero moments of inertia is to handle case of linear *
 * molecule.                                                                  *
 ******************************************************************************/
void euler(torque, quat, qdot, qddot, inertia, nmols)
vec_mp		torque;		/* Space frame torques [nmols][3]        (in) */
quat_mp		quat, qdot,     /* Quaternions for this species and d/dt (in) */
		qddot;		/* Quaternion second derivatives        (out) */
vec_mt		inertia;	/* Principal moments of inertia          (in) */
int		nmols;		/* Number of molecules                   (in) */
{
   /* The following two quantities, though vectors, are stored in the last 3  *
    * components of a quaternion array to allow easy application of the       *
    * quaternion multiplication in the equations of motion.                   */
   quat_mp	omega,		/* Principal frame angular velocities         */
   		ang_acc=qalloc(nmols);	/* Principal frame ang accelerations  */
   real		*qp;
   register int	imol;
   int		i, j, k;
   register double	Iir, Ijk;	/* Temporaries for moments of inertia */

   if(quat == NULL) return;  /* Molecule is point atom or ion - no action     */

   omega = qddot;	/* Use the qddot array as workspace for angular vels  */

   q_conj_mul(quat, qdot, omega, nmols);
   vscale(4 * nmols, 2.0, omega[0], 1);

   for(imol = 0; imol < nmols; imol++)
   {
      qp = qdot[imol];
      ang_acc[imol][0] = -2.0 * 
	 (qp[0]*qp[0] + qp[1]*qp[1] + qp[2]*qp[2] + qp[3]*qp[3]);
   }
 
   for(i=1, j=2, k=3; i<4; i++, j=i%3+1, k=j%3+1)
      if(inertia[i-1] != 0.0)
      {
         Iir = 1.0/inertia[i-1];  Ijk = inertia[j-1] - inertia[k-1];
         for(imol = 0; imol < nmols; imol++)
            ang_acc[imol][i] = Iir * 
                      (torque[imol][i-1] + Ijk * omega[imol][j]*omega[imol][k]);
      }
      else
         for(imol = 0; imol < nmols; imol++)
            ang_acc[imol][i] = 0.0;

   omega = NULL;		/* Omega not needed any more - re use as qddot*/

   q_mul(quat, ang_acc, qddot, nmols);
   vscale(4 * nmols, 0.5, qddot[0], 1);

   xfree(ang_acc);
}
/******************************************************************************
 *  Parinello   Calculate the correction to the scaled centre of mass         *
 *  accelerations in the Parinello and Rahman zero-stress method.             *
 *  Parinello M. and Rahman A. J. Appl. Phys. 52(12), 7182-7190 (1981)        *
 ******************************************************************************/
void parinello(h, h_dot, vel, acc, acc_out, nmols)
mat_mt	h,			/* P and R's unit cell matrix            (in) */
	h_dot;			/* Derivative of h matrix                (in) */
vec_mp	vel,			/* Centre of mass scaled velocities      (in) */
	acc, acc_out;		/* C of M accelerations              (in/out) */
int	nmols;			/* Size of vel and acc/ number molecules (in) */
{
   mat_mt	h_tr,		/* Transpose of h			      */
   		h_tr_dot,	/* Transpose of h_dot			      */
   		h_tmp_1,	/* Store for intermediate terms		      */
   		h_tmp_2,	/* Store for intermediate terms		      */
   		G,		/* h_tr * h	(metric tensor)		      */
   		G_inv,		/* Inverse of G				      */
   		G_dot,		/* Derivative of G			      */
   		G_i_d;		/* G_inv * G_dot			      */
   vec_mp	acc_corr=ralloc(nmols);	/* Correction term to accelerations   */
   int		i, imol;	/* Counters				      */

   transpose(h,h_tr);
   mat_mul(h_tr,h,G);				/* We now have the G matrix   */
   invert(G, G_inv);				/* G (-1) done                */
   transpose(h_dot, h_tr_dot);
   mat_mul(h_tr_dot, h, h_tmp_1);
   mat_mul(h_tr, h_dot, h_tmp_2);
   mat_add(h_tmp_1, h_tmp_2, G_dot);		/* G dot now complete         */
   mat_mul(G_inv, G_dot, G_i_d);		/* G_inv * G_dot              */

   mat_vec_mul(G_i_d, vel, acc_corr, nmols);    /* Calculate correction term  */

   for(i = 0; i < 3; i++)                       /* Add correction term        */
      for(imol = 0; imol < nmols; imol++)       /* to accelerations           */
         acc_out[imol][i] = acc[imol][i] - acc_corr[imol][i];

   xfree(acc_corr);
}
/******************************************************************************
 *  Trans_ke  calculate and return the translational kinetic energy           *
 ******************************************************************************/
double	trans_ke(h, vel_s, mass, nmols)
mat_mt	h;			/* Unit cell matrix			 (in) */
vec_mt	vel_s[];		/* Scaled c of m velocities		 (in) */
double	mass;			/* Mass of a molecule of this species	 (in) */
int	nmols;			/* Number of molecules			 (in) */
{
   double	ke;
   vec_mp	vel = ralloc(nmols);	/* Unscaled (real) velocities         */
   
   mat_vec_mul(h, vel_s, vel, nmols);   /* Calculate unscaled velocities      */

   ke = vdot(3*nmols, vel[0], 1, vel[0], 1);

   xfree(vel);
   return(0.5 * mass * ke);
}
   
/******************************************************************************
 *  rot_ke  calculate and return the rotational kinetic energy                *
 ******************************************************************************/
double	rot_ke(quat, qdot, inertia, nmols)
quat_mt	quat[],			/* Molecular quaternions		 (in) */
	qdot[];			/* Quaternion derivatives		 (in) */
vec_mt	inertia;		/* Principal moments of inertia		 (in) */
int	nmols;			/* Number of molecules			 (in) */
{
   double	ke = 0.0;
   quat_mp	omega_p = qalloc(nmols);   /* Principal angular velocities    */
   int		i;
   
   q_conj_mul(quat, qdot, omega_p, nmols); /* Calculate angular velocities    */
   vscale(4 * nmols, 2.0, omega_p[0], 1);  /* omega = 2*q~*qdot               */
   for(i = 0; i < 3; i++)
      ke += inertia[i] * vdot(nmols, omega_p[0]+i+1, 4, omega_p[0]+i+1, 4);

   xfree(omega_p);
   return(0.5 * ke);
}
/******************************************************************************
 * energy_dyad.  Calculate the dyadic sum m V V (dyad over V) for zero stress *
 ******************************************************************************/
void energy_dyad(ke_dyad, h, vels, mass, nmols)
mat_mt	ke_dyad,			/* Dyad is accumulated here  (in/out) */
	h;				/* Unit cell matrix		(in)  */
vec_mp	vels;				/* Scaled velocities		(in)  */
double	mass;				/* Mass of particles		(in)  */
int	nmols;				/* Number of molecules		(in)  */
{
   int		i, j;				/* Counters		      */
   vec_mp	vel = ralloc(nmols);		/* Real velocities	      */

   mat_vec_mul(h, vels, vel, nmols);	/* Calculate unscaled velocities      */

   for(i = 0; i < 3; i++)
      for(j = 0; j < 3; j++)
      {
         ke_dyad[i][j] += mass * vdot(nmols, vel[0]+i, 3, vel[0]+j, 3);
      }     

   xfree(vel);
}
/******************************************************************************
 * Rahman   Calculate the unit cell matrix accelerations                      *
 ******************************************************************************/
void rahman(stress_vir, h, hddot, ke_dyad, press, W, mask)
mat_mt	stress_vir,			/* Stress virial		      */
	h,				/* Unit cell matrix		      */
	hddot,				/* Unit cell accelerations            */
	ke_dyad;			/* Translational kinetic energy dyad  */
double	press,				/* Externally applied pressure	      */
	W;				/* Piston mass parameter	      */
int	mask;				/* Mask constrained el's of h matrix  */
{
   double	vol = det(h);		/* Unit cell volume		      */
   mat_mt	stress,			/* Stress tensor		      */
   		h_tr,			/* Transpose of h		      */
   		h_tr_inv,		/* Inverse of transpose of h	      */
   		sigma;			/* P & R sigma matrix 		      */
   int		i, j;			/* Counters			      */

   for(i = 0; i < 3; i++)
      for(j = 0; j < 3; j++)
         stress[i][j] = (ke_dyad[i][j] + stress_vir[i][j]) / vol;

   for(i = 0; i < 3; i++)
      stress[i][i] -= press;	/* Subtract applied pressure from diagonal    */

   transpose(h, h_tr);          /* Calculate sigma = vol*h transpose inverse  */
   invert(h_tr, h_tr_inv);
   mat_sca_mul(vol, h_tr_inv, sigma);

   mat_mul(stress, sigma, hddot);	/* Calculate unit cell accelerations  */
   mat_sca_mul(1.0/W, hddot, hddot);

   /* 
    * Zero unwanted degrees of freedom. Refson PhD Thesis (1986)
    */   
   for(i = 0; i < 9; i++)
   {
      if( mask & 1 )
	 hddot[0][i] = 0.0;		/* Access as [9] rather than [3][3]   */
      mask >>= 1;
   }
}
/******************************************************************************
 * Hoover_tr() function corrects forces to realize thermostat                 *
 * main formula: mass * accel = force - alpha * mass * vel                    *
 * Function is added by VVMurashov on 22/10/95                                *
 ******************************************************************************/
void hoover_tr(alpha, accel_in, accel_out, vel, nmols)
double  alpha;                  /* Thermostat  multiplier                     */
vec_mp	vel,			/* Centre of mass scaled velocities      (in) */
        accel_in,               /* Array of forces                   (in/out) */
        accel_out;
int	nmols;			/* Size of vel and force/number molecules(in) */
{
   int		i, j;   	/* Counters				      */

   for(i = 0; i < 3; i++)                       /* Add correction term        */
      for(j = 0; j < nmols; j++)                /* to accelerations           */
         accel_out[j][i] = accel_in[j][i] - alpha*vel[j][i];
}
/******************************************************************************
 * Hoover_rot() function corrects forces to realize thermostat                *
 * main formula: inertia * accel = torque - alpha * inertia * omega           *
 * Function is added by VVMurashov on 22/10/95                                *
 ******************************************************************************/
void hoover_rot(alpha, inertia, force_in, force_out, omega, nmols)
double   alpha;                 /* Nose-Hoover multiplier                     */
vec_mt   inertia;               /* Inertia vector                             */
quat_mp	 omega;		        /* Angular velocities                    (in) */
vec_mp   force_in,              /* Array of forces                   (in/out) */
         force_out;
int	 nmols;			/* Size of vel and force/number molecules(in) */
{
   int		i, imols;   	/* Counters				      */
   for(i = 0; i < 3; i++)                       /* Add correction term        */
      for(imols = 0; imols < nmols; imols++)    /* to accelerations           */
         force_out[imols][i] = force_in[imols][i] - alpha*inertia[i]*
                               omega[imols][i+1];
}
/*****************************************************************************
 * Gaussiant() function calculates alpha1 or alpha2 for the Gaussian         * 
 * thermostat. Principle formula: alpha = alpha1 / alpha2                    *
 * alpha1 = (SUM force * vel), and alpha2 = (SUM mass * vel^2)               *
 * Function is added by VVMurashov on 3/11/95                                *
 *****************************************************************************/
double gaussiant(vec1, vec2, nmols)
vec_mp   vec1,                  /* First array                           (in) */
         vec2;                  /* Second array                          (in) */
int	 nmols; 		/* Size of arrays [nmols][n]             (in) */
{
   double   alpha = 0.0;        /* Sum of products                      (out) */
   int		i;      	/* Counters				      */
   for(i = 0; i < 3; i++)
      alpha += vdot(nmols, vec1[0]+i, 3, vec2[0]+i, 3);
   return(alpha);
}
/*****************************************************************************
 * Gaussianr1() function is the same as above, but allows for dealing with   *
 * quaternion array. Function is added by VVMurashov on 3/11/95              *
 *****************************************************************************/
double gaussianr1(vec1, vec2, nmols)
vec_mp   vec1;                  /* First array                           (in) */
quat_mp  vec2;                  /* Second array                          (in) */
int	 nmols; 		/* Size of arrays [nmols][n]             (in) */
{
   double   alpha = 0.0;        /* Sum of products                      (out) */
   int		i;      	/* Counters				      */
   for(i = 0; i < 3; i++)
      alpha += vdot(nmols, vec1[0]+i, 3, vec2[0]+i+1, 4);
   return(alpha);
}
/*****************************************************************************
 * Gaussianr2() function calculates alpha2 for the Gaussian thermostat       *
 * Principle formula: alpha = alpha1/alpha2                                  *
 * where alpha1 = (SUM torque * omega), and alpha2 = (SUM inertia * omega^2) *
 * Function is added by VVMurashov on 3/11/95                                *
 *****************************************************************************/
double gaussianr2(omega, inertia, nmols)
vec_mt   inertia;               /* Inertia vector                             */
quat_mp	 omega;		        /* Angular velocities                    (in) */
int	 nmols;			/* Size of vel and force/number molecules(in) */
{
   double   alpha = 0.0;        /* Gaussian multiplier                  (out) */
   int		i;      	/* Counters				      */
   for(i = 0; i < 3; i++)
      alpha += inertia[i] * vdot(nmols, omega[0]+i+1, 4, omega[0]+i+1, 4);
   return(alpha);
}
$EOD
$!
$CREATE alloc.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * Alloc	Interface functions to dynamic store allocators.	      *
 * talloc()	Allocate storage and test for success or failure	      *
 * arralloc()	Allocate rectangular dope-vector (ie using pointers) array    *
 *									      *
 * N.B.         Portability.						      *
 *    These functions make some assumptions which are not guaranteed by the   *
 * ANSI standard.							      *
 * 1)   arralloc() relies on a common format for pointers to different data   *
 *      types, and assumes that the representation of a "data" pointer is the *
 *      same as of an integer pointer.  (N.B.  it can not be used to allocate *
 *      character data). It will not work (and cannot be made to work) on     *
 * 	machines for which this is false. 				      *
 ******************************************************************************
 *      Revision Log
 *       $Log: alloc.c,v $
 *       Revision 2.10  1998/01/27 15:41:42  keith
 *       Got rid of copying of arg ptr ap and replaced with a rescan of
 *       the argument list in 'arralloc()'.  This is because va_list
 *       isn't necessarily an lvalue in ANSI/ISO C (and really isn't in
 *       WATCOM C.
 *
 *       Revision 2.9  1997/11/27 16:24:49  keith
 *       Removed titan-specific THREAD_SYS stuff for cleanliness.
 *
 *       Revision 2.8  1994/06/08 13:08:08  keith
 *       New version of array allocator which breaks up requests for DOS.
 *       Now must use specific "afree()" paired with arralloc().
 *
 * Revision 2.6  1994/02/21  16:55:58  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Added sanity test for 16-bit architectures.
 *
 * Revision 2.5  1994/01/18  13:32:09  keith
 * Null update for XDR portability release
 *
 * Revision 2.3  93/10/28  10:27:37  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.0  93/03/15  14:48:54  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.22  93/03/15  14:41:32  keith
 * Added GPL copyleft notice to permit release and distribution.
 * N.B.  Previous versions were copyright (C) by author and 
 * only licensed by explicit permission.
 * 
 * Revision 1.21  93/03/12  20:11:49  keith
 * Fixed mistake of typing word_mt to double -- must be int.
 * Documented it better.
 * 
 * Revision 1.20  93/03/09  15:58:16  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.19  93/03/05  15:00:30  keith
 * Added generic type "word_t" for portability.
 * Moved include line for security in non-ansi gcc environments
 * 
 * Revision 1.18  92/06/26  17:02:42  keith
 * Got rid of assumption that memory returned by talloc() or
 * arralloc() is zeroed.  This enhances ANSI compatibility.
 * Removed memory zeroing from alloc.c() in consequence.
 * 
 * Revision 1.17  92/06/05  13:37:02  keith
 * Conditionally undefed va_dcl for ANSI, stdarg.h case --
 * just prevents warning from gcc.
 * 
 * Revision 1.16  91/10/17  14:22:21  keith
 * Added debugging code
 * 
 * Revision 1.15  91/08/19  16:44:11  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_mt and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * 
 * Revision 1.14  91/03/12  15:42:10  keith
 * Tidied up typedefs size_mt and include file 
 * Added explicit function declarations.
 * 
 * Revision 1.13  91/03/07  17:52:32  keith
 * Macros in support of parallel version for titan added.
 * 
 * Revision 1.12  90/10/23  20:13:14  keith
 * Added dummy function call to inhibit vectorization.
 * This allows use of 'ivdep' compiler options and also
 * works round certain bugs in cray's scc compiler.
 * 
 * Revision 1.11  90/08/29  18:21:09  keith
 * Replaced calloc() call with malloc() and memset().
 * On the CRAY XMP calloc() is very inefficient.
 * 
 * 
 * Revision 1.10  90/08/23  12:46:39  keith
 * Re-implemented arralloc() using more elegant recursive algorithm.
 * 
 * Revision 1.9  90/05/16  18:39:36  keith
 * Renamed own freer from cfree to tfree.
 * 
 * Revision 1.8  90/05/16  14:19:23  keith
 * *** empty log message ***
 * 
 * Revision 1.7  90/05/02  17:51:09  keith
 * Include of stddef.h added to get size_mt (removed from defs.h)
 * 
 * Revision 1.6  90/04/25  14:20:16  keith
 * Modified to allow for machines with word ptr != char ptr.
 * 
 * Revision 1.5  90/03/26  16:54:46  keith
 * Added portability warning to header comments.
 * 
 * Revision 1.4  90/01/15  17:22:25  keith
 * New version of arralloc() orders memory so that pointers come FIRST.
 * This means you can simply free() the pointer returned (if l.b. = 0).
 * 
 * Revision 1.3  89/09/21  14:56:01  keith
 * Modified talloc() to return null rather than exit if 0 bytes requested.
 * 
 * Revision 1.2  89/05/24  13:54:26  keith
 * Changed ifdef's to select on defined(__STDC__) macro
 * 
 * Revision 1.1  89/04/27  16:52:17  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/alloc.c,v 2.10 1998/01/27 15:41:42 keith Exp $";
#endif
/*========================== program include files ===========================*/
#include "defs.h"
#include "messages.h"
/*========================== Library include files ===========================*/
#if defined(ANSI) || defined(__STDC__)
#   include 
#else
#   include 
#endif
#include "stdlib.h"
#include "stddef.h"
#include "string.h"
#ifdef DEBUGX
#include 
#endif
#ifdef DBMALLOC
typedef size_mt size_t;
#include 
#endif
/*========================== External function declarations ==================*/
void	inhibit_vectorization();		/* Self-explanatory dummy     */
#ifdef	DEBUG
int	malloc_verify();
int	malloc_debug();
#endif
#if defined(ANSI) || defined(__STDC__)
void	note(char *, ...);			/* Write a message to the output file */
void	message(int *, ...);		/* Write a warning or error message   */
#else
void	note();				/* Write a message to the output file */
void	message();			/* Write a warning or error message   */
#endif
/*============================================================================*/
/******************************************************************************
 * word_mt								      *
 * This is the core of the non-ANSI conformance of "arralloc" and *must* be   *
 * right for portability.  Word_mt must be typed to the *smallest* size of    *
 * object to be allocated.  On a word-addressed machine  it ought to be the   *
 * smallest addressible object.  Char is only safe for byte-addressible       *
 * architectures (with the exception of the Cray, which works).  Moldy doesn't*
 * call it for anything smaller than an int, which being the "naturally" sized*
 * object is the optimum.  If pointer representations of any actually required*
 * object (ie NOT char) do differ then these functions will have to be        *
 * rewritten.								      *
 *									      *
 * Wide_mt is the widest type for alignment purposes.  Try double.	      *
 ******************************************************************************/
typedef int word_mt;
typedef double wide_mt;
/******************************************************************************
 * talloc()	Call Calloc to allocate memory, test result and stop if failed*
 ******************************************************************************/
gptr	*talloc(n, size, line, file)
int	n;
size_mt	size;
int	line;
char	*file;
{
   gptr *p;
#ifdef ANSI_LIBS
   /*
    * Test for malloc arg which would overflow.  Since size_mt is long
    * and size_t may be int this could happen on 16 bit machines.
    * We can only rely on size_t as parameter to malloc if libs are
    * ANSI conformant.
    */
   if( (size_mt)(size_t)(n*size) != n*size )
      message(NULLI, NULLP, FATAL, NOMEM, line, file,
	       (int)n, (unsigned long)size);
#endif
   p = malloc(n*size);
#ifdef DEBUGX
   fprintf(stderr,"Alloc: %16s line %3d: %d x %lu bytes (%p to %p)\n", 
	   file, line, n, size, p, p+n*size);
#endif
   if(p == NULL && (n*size != 0))
     message(NULLI, NULLP, FATAL, NOMEM, line, file,
	     (int)n, (unsigned long)size);
#ifdef DEBUGZ
   (void)memset((gptr*)p,0x10,n*size);
#endif
   return(p);
}
/******************************************************************************
 * Cfree - synonym to free()						      *
 ******************************************************************************/
void	tfree(p)
gptr	*p;
{
#ifdef DEBUG
   if( ! malloc_verify() )
      message(NULLI, NULLP, FATAL, "Internal Error: Heap corrupt");
#endif
   free((gptr*)p);
}

#ifdef ALLOC_SEPARATELY
union u {struct {int ndim, noffset, len;} b; word_mt * p; wide_mt w;};
#define bsize (sizeof(union u)/sizeof(word_mt*))
#define bwsize (sizeof(union u)/sizeof(word_mt))
void afree(pp)
gptr *pp;
{
   word_mt **p = (word_mt**)pp;
   int i; union u *up = (union u *)(p-bsize);
   if( up->b.ndim > 1 )
   {
      for( i=0; i < up->b.len; i++)
	 afree((gptr*)(p[i] + up->b.noffset));
   }
   tfree((gptr*)(p-bsize));
}
      
#else /* ALLOC_SEPARATELY*/
void 	afree(p)
gptr	*p;
{
   tfree(p);
}
#endif /* ALLOC_SEPARATELY*/
/******************************************************************************
 *  arralloc.   Allocate a psuedo array of any dimensionality and type with   *
 *  specified lower and upper bounds for each dimension.  Each dimension is   *
 *  an array of pointers, and the actual data is laid out in standard 'c'     *
 *  fashion ie last index varies most rapidly.  All storage is got in one     *
 *  block, and so can be freed in one go.  				      *
 *  array = (double*) arralloc(sizeof(double), 3, 0, 10, -10, 10, 0, 5);      *
 *  xfree(array);					     	      *
 *  (N.B. if lower bound of 1st dimension != 0 then free array+l.b.           *
 ******************************************************************************/
#ifdef ALLOC_SEPARATELY

word_mt **subarray(size, ndim, len, ap)
size_mt	size;
int	ndim, len;
va_list	ap;
{
   word_mt **p;
   word_mt  *d;
   union u *up;
   int blen, i, lb = va_arg(ap, int), ub = va_arg(ap,int);

   if( ndim > 1 )
   {
#ifdef DEBUGY
      fprintf(stderr,"[%d...%d]", lb, ub);
#endif
      blen = len+bsize;
      p = (word_mt**)talloc(blen, (size_mt)sizeof(word_mt *), 
			    __LINE__, __FILE__);
      up = (union u *)p;      p += bsize;
      up->b.ndim = ndim;	
      up->b.len = len;	
      up->b.noffset = lb*(ndim>2?sizeof(word_mt*):size)/sizeof(word_mt);
      for( i=0; ib.noffset;
      return p;
   } else 
   {
      blen = len*(size/sizeof(word_mt))+bwsize;
      d = (word_mt*)talloc(blen, (size_mt)sizeof(word_mt), __LINE__, __FILE__);
      up = (union u *)d;      d += bwsize;
      up->b.ndim = ndim;
      return (word_mt **)d;
   }      
}

#if defined(ANSI) || defined(__STDC__)
#   undef va_alist
#   define	va_alist size_mt size, int ndim, ...
#   ifdef va_dcl
#      undef va_dcl
#   endif
#   define va_dcl /* */
#endif
                /*VARARGS*/
gptr		*arralloc(va_alist)
va_dcl
{
   va_list	ap;
   word_mt		*p;
   int		lb, ub;
#if defined(ANSI) || defined(__STDC__)
   va_start(ap, ndim);
#else
   size_mt	size;			/* size of array element	      */
   int		ndim;			/* Number of dimensions		      */

   va_start(ap);
   size = va_arg(ap, size_mt);
   ndim = va_arg(ap, int);
#endif

#ifdef DEBUGY
   fprintf(stderr,"%dD array of %lu byte elements:", ndim, size);
#endif
   if( size % sizeof(word_mt) != 0 )  /* Code only works for 'word' objects */
      message(NULLI, NULLP, FATAL, WDPTR, size);

   lb = va_arg(ap, int); ub = va_arg(ap, int);
#ifdef DEBUGY
   fprintf(stderr,"[%d...%d]", lb, ub);
#endif
   
   p=(word_mt*)subarray(size, ndim, ub-lb+1, ap) 
      - lb*(ndim>1?sizeof(word_mt*):size)/sizeof(word_mt);

#ifdef DEBUGY
   putc('\n',stderr);
#endif

   va_end(ap);   

   return (gptr*)p;
}
#else /* ALLOC_SEPARATELY*/
#define CSA(a) ((char*)(a))
#define ALIGN(a,base,b)	((word_mt*)(CSA(base)+((CSA(a)-CSA(base))+(b)-1)/(b)*(b) ))
static
void 	subarray(size, ndim, prdim, pp, qq, base, ap)
size_mt  size;
int	ndim;
long	prdim;
word_mt	***pp, **qq, *base;
va_list	ap;
{
   word_mt	*dd = ALIGN(qq,base,size),	**dpp = (word_mt**)pp;
   int i,	lb = va_arg(ap, int),
		dim = va_arg(ap, int) - lb + 1;

   if(ndim > 0)		/* General case - set up pointers to pointers  */
   {
      for( i = 0; i < prdim; i++)
      {
	 inhibit_vectorization();  /* Circumvent bug in cray compiler v4.1   */
	 pp[i] = qq + i*dim - lb;
      }

      subarray(size, ndim-1, prdim*dim, (word_mt***)qq, qq+prdim*dim, base, ap);
   }
   else			/* Last recursion - set up pointers to data   */
      for( i = 0; i < prdim; i++)
	 dpp[i] = dd + (i*dim - lb)*size/sizeof(word_mt);
}
            
#if defined(ANSI) || defined(__STDC__)
#   undef va_alist
#   define	va_alist size_mt size, int ndim, ...
#   ifdef va_dcl
#      undef va_dcl
#   endif
#   define va_dcl /* */
#endif
                /*VARARGS*/
gptr		*arralloc(va_alist)
va_dcl
{
   va_list	ap;
   word_mt		**p, **start;
   int		lb, ub, idim;
   long		n_ptr = 0, n_data = 1;
#if defined(ANSI) || defined(__STDC__)
   va_start(ap, ndim);
#else
   size_mt	size;			/* size of array element	      */
   int		ndim;			/* Number of dimensions		      */

   va_start(ap);
   size = va_arg(ap, size_mt);
   ndim = va_arg(ap, int);
#endif

#ifdef DEBUGY
   fprintf(stderr,"%dD array of %lu byte elements:", ndim, size);
#endif
   if( size % sizeof(word_mt) != 0 )  /* Code only works for 'word' objects */
      message(NULLI, NULLP, FATAL, WDPTR, size);
   /*
    * Cycle over dims, checking bounds and accumulate # pointers & data items.
    */
   for(idim = 0; idim < ndim; idim++)
   {
      lb = va_arg(ap, int);
      ub = va_arg(ap,int);
#ifdef DEBUGY
      fprintf(stderr,"[%d...%d]", lb, ub);
#endif
      if(ub < lb)
         message(NULLI, NULLP, FATAL, INSIDE, lb, ub);
      n_data *= ub - lb + 1;
      if( idim < ndim-1 )
	 n_ptr  += n_data;
   }
#ifdef DEBUGY
   putc('\n',stderr);
#endif
   /*
    *  Allocate space  for pointers and data.
    */
#ifdef DEBUGY
   {
      size_mt mallen=(n_data+1)*size+n_ptr*sizeof(word_mt**);
      fprintf(stderr,"Calling talloc(%lu)....\n", mallen);
   }
#endif
   start = (word_mt**)talloc(1,
			     (size_mt)((n_data+1)*size+n_ptr*sizeof(word_mt**)),
		
	     __LINE__, __FILE__);
   /*
    * Rescan argument list to pass to subarray()
    */
   va_end(ap);
#if defined(ANSI) || defined(__STDC__)
   va_start(ap, ndim);
#else
   va_start(ap);
   size = va_arg(ap, size_mt);
   ndim = va_arg(ap, int);
#endif

   /*
    * Set up pointers to form dope-vector array.
    */
   subarray(size, ndim-1, 1L, &p, start, (word_mt*)start, ap);

   va_end(ap);   

   return (gptr*)p;
}
#endif /* ALLOC_SEPARATELY*/
$EOD
$!
$CREATE ansi.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993, 1995 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * Ansi		This file contains implementations of some ANSI C library     *
 *              functions for machines which don't have them.                 *
 ******************************************************************************/
/*========================== program include files ===========================*/
#include	"defs.h"
#ifndef _POSIX_SOURCE
#   define _POSIX_SOURCE
#endif
/*========================== Library include files ===========================*/
#if defined(ANSI) || defined(__STDC__)
#include 	
#else
#include 	
#endif
#include 	"string.h"
#include	"stddef.h"
#include	"stdlib.h"
#include	
#ifndef ANSI_LIBS
/******************************************************************************
 * strerror for pre-ANSI unix machines					      *
 ******************************************************************************/
#if defined(unix) || defined(__unix__)
char	*strerror(i)
int i;
{
   extern int sys_nerr;
   extern char *sys_errlist[];
   if( i >= 0 && i < sys_nerr )
      return sys_errlist[i];
   else
      return "invalid error code";
}   
#endif
/******************************************************************************
 * raise for pre-ANSI unix machines					      *
 ******************************************************************************/
#if defined(unix) || defined(__unix__)
int 	raise(sig)
int sig;
{
   extern int getpid();
   extern int kill();

   return(kill(getpid(), sig));
}   
#endif
/******************************************************************************
 * strstr replacement for pre-ANSI machines which don't have it.              *
 ******************************************************************************/
char *strstr(cs, ct)
CONST char *cs, *ct;
{
   int  i, sl = strlen(cs)-strlen(ct);

   for(i = 0; i <= sl; i++)
      if( !strcmp(cs+i,ct) )
	 return (char*)cs+i;
   return 0;      
}
/******************************************************************************
 * mem{cpy,set} and strchr replacement for BSD machines which don't have them.*
 ******************************************************************************/
#ifdef BSD
char *strchr(s, c)
CONST char	*s;
int	c;
{
   extern char	*index();
   return index(s,c);
}
gptr *memcpy(s1, s2, n)
gptr *s1, *s2;
int n;
{
   int bcopy();
   (void)bcopy((char *)s2, (char *)s1, n);
   return(s1);
}
gptr *memset(s, c, n)
gptr *s;
int c, n;
{
   void bzero();
   char	*sp;
   if( c == 0 )
      (void)bzero(s, n);
   else
   {
      for( sp=s; sp < (char*)s+n; sp++)
	 *sp = c;
   }
   return(s);
}
#endif
/******************************************************************************
 * remove.  delete (unlink) a file.   (ANSI replacement)		      *
 ******************************************************************************/
#if defined(unix) || defined(__unix__)
int remove(file)
CONST char	*file;
{
   int unlink();
   return (unlink(file));
}
#endif
/******************************************************************************
 * raise for VMS.  Earlier VAX/VMS libs don't it have it.  Assume that any vsn*
 * late enough to have __DECC is OK. Don't believe ANSI_LIBS -- kludge.       *
 ******************************************************************************/
#if defined(vms) && ! defined(__DECC)
int 	raise(sig)
int sig;
{
   extern int getpid();
   extern int kill();

   return(kill(getpid(), sig));
}   
#endif
/******************************************************************************
 *  vprintf for those machines which don't have it			      *
 ******************************************************************************/
#ifndef HAVE_VPRINTF

#ifdef	HAVE_DOPRNT

int	vprintf (fmt, args)
char	*fmt;
va_list	args;
{
   return(_doprnt(fmt, args, stdout));
}


#else		/* No _doprnt or equivalent */

#include 
int	vprintf (format, ap)
CONST char	*format;
va_list	ap;
{
    int     pos, charsout, fpos, error, modflag;
    char    fmt[1024], temps[1024];
    char    ch;

    pos = charsout = error = 0;
    while (format[pos] != 0)
    {
        modflag = 0;
        if (format[pos] != '%')
        {
            putchar (format[pos++]);
        }
        else
        {
            fmt[0] = '%';
            pos++;
            fpos = 1;
            while (format[pos] != 0 && !islower (format[pos]) &&
                format[pos] != '%')
                fmt[fpos++] = format[pos++];
	    if( format[pos] == 'l' || format[pos] == 'L' || format[pos] == 'h')
	    {
	       modflag = format[pos];
	       fmt[fpos++] = format[pos++];
	    }
            if (format[pos] == 0)
            {
            /* error in format string */
                error++;
            }
            ch = fmt[fpos++] = format[pos++];
            fmt[fpos] = 0;
#ifdef DEBUG
            printf ("Format is \"%s\"\n", fmt);
#endif
        /* Now fmt contains the format for this part of the output and ch
           contains the conversion code. */
            temps[0] = 0;
            switch (ch)
            {
              case 'n':
                *va_arg (ap, int *) = charsout;
                break;
              case 's':
                sprintf (temps, fmt, va_arg (ap, char *));
                break;
              case 'p':
                sprintf (temps, fmt, va_arg (ap, void *));
                break;
              case 'c':
              case 'd':
              case 'i':
              case 'o':
              case 'u':
              case 'x':
              case 'X':
		if( modflag == 'l')
		   sprintf (temps, fmt, va_arg (ap, long));
		else
		   sprintf (temps, fmt, va_arg (ap, int));
                break;
              case 'e':
              case 'E':
              case 'f':
              case 'g':
              case 'G':
                sprintf (temps, fmt, va_arg (ap, double));
                break;
              default:
                (void)strcpy (temps, fmt);
                error++;
            }
#ifdef DEBUG
            printf ("temps is \"%s\"\n", temps);
#endif
            fputs (temps, stdout);
            charsout += strlen (temps);
#ifdef DEBUG
            printf ("still to interpret \"%s\"\n", (char *) &format[pos]);
#endif
        }
    }
    if (error)
        return (-1);
    return (charsout);
}
#endif		/* HAVE_DOPRNT       */
#endif		/* Vprintf needed    */

#endif
$EOD
$!
$CREATE auxil.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * Aux		This file contains auxilliary service functions and (almost)  *
 *		all machine-dependant stuff.  The ANSI standard interface is  *
 *              used as much as possible for library functions; if a machine  *
 *		is missing something an ANSI interface routine is supplied.   *
 ******************************************************************************
 *      Revision Log
 *       $Log: auxil.c,v $
 *       Revision 2.10  1996/11/18 15:58:48  keith
 *       Revised Cray macro tests to use _CRAY1 to select vector architecture.
 *       Use "scalar" versions rather than SCILIB functions on Cray T3D
 *       to avoid parallel divergence bug.  SCILIB can return different
 *       results on different processors which is unacceptable.
 *       Added optimised "sum()".
 *       Removed "gatheri()" and "wheneq()" as they are no longer needed.
 *
 *       Revision 2.9  1996/03/06 15:24:46  keith
 *       Modified CRAY defs to pick up SCILIB stuff on MPP archs.
 *
 *       Protected "precision()" against very clever optimisers keeping
 *       eps in register - caused failure on Intels w/ long fp regs.
 *
 *       Revision 2.8  1995/12/05 20:55:10  keith
 *       Separated ANSI replacement routines from Auxil.c into Ansi.c
 *       Removed all COS functionality.
 *
 *       Revision 2.7  1994/06/08 13:09:00  keith
 *       Added "zero_dbls()" function.
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Moved declaration of "match" structure from input.c
 * Moved a few sanity tests & modifications of "control"
 * members to here from other modules.
 *
 * Changed size_t to own typedef size_mt == ulong.
 *
 * Declared as "static"  all functions which should be.
 *
 * Added CONST qualifier to (re-)declarations of ANSI library
 * emulation routines to give reliable compilation even
 * without ANSI_LIBS macro. (#define's away for K&R
 * compilers)
 *
 * Renamed Aux.c to Auxil.c for DOS (ugh).
 * Revamped cpu time interface to use POSIX "times()" whenever
 * available - including for BSD systems - and got rid of
 * "getrusage()" version entirely.
 * Thus eliminating various marginally portable header
 * kludges.
 *
 * Added memset() emulation for *really* old BSD systems.
 *
 * Revision 2.5  94/01/29  11:09:53  keith
 * Fixed bug in "strstr()" replacement for non-ANSI libraries
 * 
 * Revision 2.4  94/01/18  13:23:04  keith
 * Incorporated all portability experience to multiple platforms since 2.2.
 * Including ports to VAX/VMS and Open VMS on Alpha AXP and Solaris.
 * 
 * Revision 2.3  93/10/28  10:27:38  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.2  93/09/06  14:43:28  keith
 * Fixed portability problems/bugs in XDR code.
 * 
 * Revision 2.0  93/03/15  14:48:56  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.7.1.32  93/03/15  14:41:33  keith
 * Added GPL copyleft notice to permit release and distribution.
 * N.B.  Previous versions were copyright (C) by author and 
 * only licensed by explicit permission.
 * 
 * Revision 1.7.1.31  93/03/12  12:19:22  keith
 * Reorganized defines to recognise all ANSI (__type__) forms.
 * Moved spxpy() from aux.c to force.c and force_parallel.c
 * 
 * Revision 1.7.1.30  93/03/09  15:58:18  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.7.1.29  93/03/05  15:02:58  keith
 * Now is POSIX - safe wrt declaration of times().
 * 
 * Revision 1.7.1.28  92/09/22  14:48:22  keith
 * Tidied up calls to improve "lint" rating.
 * 
 * Revision 1.7.1.27  92/06/26  17:02:46  keith
 * Got rid of assumption that memory returned by talloc() or
 * arralloc() is zeroed.  This enhances ANSI compatibility.
 * Removed memory zeroing from alloc.c() in consequence.
 * 
 * Revision 1.7.1.26  92/06/19  17:28:40  keith
 * Added faster version of "search_lt" for Titan.
 * 
 * Revision 1.7.1.25  92/06/12  12:55:41  keith
 * Mods to make it work on VMS again.  Ugh.
 * 
 * Revision 1.7.1.24  92/06/11  20:31:04  keith
 * Added file locking against multiple runs using same dump or backup files.
 * 
 * Revision 1.7.1.23  91/11/26  10:24:31  keith
 * Modified timing routines to work under POSIX.
 * Replaced saxpy() with sxpy() -- UNVECTORIZED as indices may be repeated.
 * 
 * Revision 1.7.1.22  91/08/17  16:24:15  keith
 * Added strerror() for pre-ANSI unix systems
 * Updated #defines with __unix__ symbol and protection of sys/time.h for convex.
 * 
 * Revision 1.7.1.21  91/08/15  18:11:44  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_t and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.7.1.20  91/05/29  10:58:00  keith
 * Modified wheneq to generate better code on Stardent TITAN
 * Added #ifdefe'd "const" qualifier in "remove" for ANSI compilers
 * without ANSI libraries.
 * 
 * Revision 1.7.1.19  91/03/12  15:42:12  keith
 * Tidied up typedefs size_t and include file 
 * Added explicit function declarations.
 * 
 * Revision 1.7.1.18  91/02/07  16:48:36  keith
 * Modified BLAS functions to vectorise better undar Stardent Titan compiler.
 * 
 * Revision 1.7.1.17  90/10/23  20:10:47  keith
 * Added inhibit_vectorization() dummy function.
 * 
 * Revision 1.7.1.16  90/10/18  17:24:26  keith
 * Added extra arg to gather() for (conditional) bounds check.
 * C.f. force.c 1.8.1.17.
 * 
 * Revision 1.7.1.15  90/09/28  13:29:08  keith
 * Inserted braces around VECTORIZE directives and changed include files
 * for STARDtardent 3000 series (via cond. comp symbol "ardent").
 * 
 * Revision 1.7.1.14  90/08/30  16:00:21  keith
 * Replaced malloc() calls with calls to own allocator 'aalloc()'
 * Ifdefed out 'remove' and 'memset/memcpy' if ANSI_LIBS is set.
 * 
 * Revision 1.7.1.13  90/05/16  18:39:46  keith
 * Renamed own freer from cfree to tfree.
 * 
 * Revision 1.7.1.12  90/05/15  19:00:16  keith
 * Incorporate new ANSI CLOCKDS_PER_SEC instead of CLK_TCK.
 * 
 * Revision 1.7.1.11  90/05/02  18:15:12  keith
 * Tidied up include files, added "time.h".
 * 
 * Revision 1.7.1.10  90/04/16  13:04:08  keith
 * Improved sysV version of rt_clock() by using result of times().
 * Coded zero-real explicitly for ANSI compliance.
 * Added strchr() for BSD systems (calls index()).
 * 
 * Revision 1.7.1.9  90/03/27  17:34:46  keith
 * Moved selection of own `vprintf into defs.h - 
 * Define symbol HAVE_VPRINTF if not needed. (Also HAVE_DOPRNT).
 * 
 * Revision 1.7.1.8  90/03/09  17:32:35  keith
 * Modified preprocessor conditionals to handle unicos.
 * 
 * Revision 1.7.1.7  90/01/02  19:01:03  keith
 * Rewrote loops to go from zero to n again - substantially faster on Stellar
 * 
 * Revision 1.7.1.6  89/12/18  17:50:23  keith
 * Sum() tests for special case of stride=1 for efficiency.
 * Rt_clock() added (returns time in seconds).
 * 
 * Revision 1.7.1.5  89/11/21  15:51:10  keith
 * Added purge() for cray and fixed replace() for standard case.
 * 
 * Revision 1.7.1.4  89/10/25  10:05:24  keith
 * Added spaxpy() in support of change in force.c.
 * 
 * Revision 1.7.1.3  89/09/20  17:08:26  keith
 * Added wheneq() and gatheri() to veclib calls for convex.
 * Modified search_lt() for cray to return zero if called with n=0.
 * 
 * Revision 1.7.1.2  89/09/18  17:39:37  keith
 * Fixed error in vprintf which used puts() instead of fputs() giving extra \n.
 * Made 'eps' static in precision() to give correct answer on subsequent calls.
 * 
 * Revision 1.7.1.1  89/08/25  15:21:36  keith
 * Mods to add framework structures to simulation model
 * 
 * Revision 1.7  89/08/15  18:29:58  keith
 * Fixed bug which assumed clock tick for Sys V machines = 1/60s
 * 
 * Revision 1.6  89/08/10  17:29:42  keith
 * Fixed search_lt() to return correct result for non-unit stride.
 * 
 * Revision 1.5  89/06/14  14:14:44  keith
 * Fixed ifdef's for CRAY to handle case of unicos.
 * Fixed mistake in #define's for typedef clash in sysV CPU().
 * 
 * Revision 1.4  89/06/09  17:01:53  keith
 * Made zero_real() call memset/bzero
 * Fixed vprintf() for machines which use _doprnt()
 * Added alliant vector functions
 * Modified cpu() to avoid typedef clash on Stellar GS 1000
 * Rewrote 'scalar' versions of vector functions to allow vectorisation.
 * 
 * Revision 1.3  89/06/01  21:23:04  keith
 * Control.out eliminated, use printf and freopen instead to direct output.
 * 
 * Revision 1.2  89/06/01  18:00:42  keith
 * Moved `vadd()' from aux.c to force.c for ease of vectorisation.
 * Now no need to compile aux.c with vectorisation.
 * 
 * Revision 1.1  89/04/27  15:01:14  keith
 * Initial revision
 * 
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/auxil.c,v 2.10 1996/11/18 15:58:48 keith Exp $";
#endif
/*========================== program include files ===========================*/
#include	"defs.h"
#ifndef _POSIX_SOURCE
#   define _POSIX_SOURCE
#endif
/*========================== Library include files ===========================*/
#if defined(ANSI) || defined(__STDC__)
#include 	
#else
#include 	
#endif
#include	
#include	
#include 	"string.h"
#include	"stddef.h"
#include	"stdlib.h"
#include	"time.h"
#include	
/*================= System Library include files - unix only ================*/
#if defined(unix) || defined(__unix__)
   /*
    *  We must protect the inclusion of .  
    */
#ifndef SYS_TYPES_INCLUDED
#define SYS_TYPES_INCLUDED
#   include 
#endif
#include 
#include 

#ifndef CLK_TCK
#   include 
#   ifdef HZ
#      define CLK_TCK HZ
#   else
#      define CLK_TCK 60	/* Really old unices defined 60. May be wrong.*/
#   endif
#endif
#endif
/*========================== External function declarations ==================*/
gptr            *talloc();	       /* Interface to memory allocator       */
void            tfree();	       /* Free allocated memory	      	      */
/*============================================================================*/
/******************************************************************************
 *  Vector routines - special cases for cray and convex to call vectorised    *
 *  library versions.  Other machines do it the hard way.		      *
 *  CRAY versions.							      *
 ******************************************************************************/
#if defined(_CRAY1)
#define VECTS_DEFINED

double vdot(n,x,ix,y,iy)
int	n;
real	*x, *y;
int	ix, iy;
{
   double SDOT();
   return(SDOT(&n, x, &ix, y, &iy));
}
double sum(n,x,ix)
int	n;
real	*x;
int	ix;
{
   double SSUM();
   return(SSUM(&n, x, &ix));
}
void	vscale(n,s,x,ix)
int	n;
double	s;
real	*x;
int	ix;
{
   void SSCAL();
   SSCAL(&n, &s, x, &ix);
}
int	search_lt(n, x, ix, s)
int	n;
real	x[];
int	ix;
double	s;
{
   int	ISRCHFLT();
   if( n <= 0 )
      return(0);
   return( ISRCHFLT(&n, x, &ix, &s) - 1);
}
void	gather(n, a, b, ix, lim)
int	n;
real	a[], b[];
int	ix[];
{
   void GATHER();
   GATHER(&n, a, b+1, ix);
}
#endif
/******************************************************************************
 *  Vector routines - CONVEX versions.  The calls are to VECLIB functions.    *
 ******************************************************************************/
#if defined(__convexc__)
#define VECTS_DEFINED

#define SINGLE (sizeof(real) == sizeof(float))
double vdot(n,x,ix,y,iy)
int	n;
real	*x, *y;
int	ix, iy;
{
   double ddot_(); float sdot_();
   if(SINGLE)
     return(sdot_(&n, x, &ix, y, &iy));
   else
     return(ddot_(&n, x, &ix, y, &iy));
}
double sum(n,x,ix)
int	n;
real	*x;
int	ix;
{
   double dsum_(); float ssum_();
   if(SINGLE)
      return(ssum_(&n, x, &ix));
   else
      return(dsum_(&n, x, &ix));
}
void	vscale(n,s,x,ix)
int	n;
double	s;
real	*x;
int	ix;
{
   float s2 = s;
   void dscal_(), sscal_();
   if(SINGLE)
      sscal_(&n, &s2, x, &ix);
   else
      dscal_(&n, &s, x, &ix);
}
int	search_lt(n, x, ix, s)
int	n;
real	x[];
int	ix;
double	s;
{
   float s2 = s;
   int	i, idsvlt_(), issvlt_();
   if(SINGLE)
      return( (i = issvlt_(&n, x, &ix, &s2)) ? i - 1 : n);
   else
      return( (i = idsvlt_(&n, x, &ix, &s)) ? i - 1 : n);
}
                            /*ARGSUSED*/
void	gather(n, a, b, ix, lim)
int	n;
real	a[], b[];
int	ix[];
{
   void dgthr_(), sgthr_();
   if(SINGLE)
      sgthr_(&n, b+1, a, ix);
   else
      dgthr_(&n, b+1, a, ix);
}
#endif
/******************************************************************************
 * alliant versions - with cray librarries				      *
 ******************************************************************************/
#if defined(alliant)
#define VECTS_DEFINED

double vdot(n,x,ix,y,iy)
int	n;
real	*x, *y;
int	ix, iy;
{
   double sdot_();
   return(sdot_(&n, x, &ix, y, &iy));
}
double sum(n,x,ix)
int	n;
real	*x;
int	ix;
{
   double ssum_();
   return(ssum_(&n, x, &ix));
}
void	vscale(n,s,x,ix)
int	n;
double	s;
real	*x;
int	ix;
{
   void sscal_();
   sscal_(&n, &s, x, &ix);
}
int	search_lt(n, x, ix, s)
int	n;
real	x[];
int	ix;
double	s;
{
   int	isrchflt_();
   return( isrchflt_(&n, x, &ix, &s) - 1);
}
                            /*ARGSUSED*/
void	gather(n, a, b, ix, lim)
int	n;
real	a[], b[];
int	ix[];
{
   void gather_();
   gather_(&n, a, b+1, ix);
}
#endif
/******************************************************************************
 *  Vector handling functions - C versions. 		     		      *
 ******************************************************************************/
#ifndef VECTS_DEFINED
double vdot(n,x,ix,y,iy)
int	n;
real	x[], y[];
int	ix, iy;
{
   register double	dot=0.0;
   register int i, j;
   if( ix == iy && ix == 1)
   {
VECTORIZE
      for(i = 0; i < n; i++)
         dot += x[i] * y[i];
   }
   else
   {
VECTORIZE
      for(i = j = 0; i != n*ix; i += ix)
      {
         dot += x[i] * y[j];
         j += iy;
      }
   }
   return(dot);
}
double sum(n,x,ix)
register int	n;
register double	x[];
register int	ix;
{
   register double	sa=0.0,sb=0.0,sc=0.0,sd=0.0,s1=0.0,s2=0.0,s3=0.0,s4=0.0;
   int i;

   if( ix == 1 )
   {

     for( i = 0; i < n-3; i+=4)
     {
	s1 += sa;
	sa = x[i];
	s2 += sb;
	sb = x[i+1];
	s3 += sc;
	sc = x[i+2];
	s4 += sd;
	sd = x[i+3];
	
     }
     s1 += sa;
     s2 += sb;
     s3 += sc;
     s4 += sd;
     for( ; i < n; i++)
       s1 += x[i];
   } 
   else
   {

      for(i = 0; i != n*ix; i += ix)
	s1 += x[i];
   }
   return(s1+s2+s3+s4);
}

void	vscale(n,s,x,ix)
register int	n;
register double	s;
register real	x[];
register int	ix;
{
   int i;
VECTORIZE
   for(i = 0; i != n*ix; i += ix)
      x[i] *= s;
}

/*
 *  Two alternatives for search_lt.  The first can vectorize using an
 *  IDFMIN type function, the second using an isrchtl.  They don't
 *  do QUITE the same thing; the first returns the index of the 
 *  smallest value,, the second that of the first one less than s.
 */
#ifdef ardent
int	search_lt(n, x, ix, s)
int	n;
real 	x[];
int	ix;
double	s;
{
   int i, im=n*ix;
   double min = x[0];
VECTORIZE
   for( i = 0; i != n*ix; i += ix )
      if( x[i] < min )
      {
	 im = i;
	 min = x[i];
      }
   if ( min < s )
      return(im/ix);
   else
      return n;
}
#else
int	search_lt(n, x, ix, s)
int	n;
real	x[];
int	ix;
double	s;
{
   int i, im=n*ix;
VECTORIZE
   for( i = (n-1)*ix; i >= 0; i -= ix )
      if( x[i] < s )
	 im = i;
   return(im/ix);
}
#endif
                            /*ARGSUSED*/
void	gather(n, a, b, ix, lim)
int	n, lim;
real	a[], b[];
int	ix[];
{
   int i;
VECTORIZE
   for( i = 0; i < n; i++)
   {
#ifdef DEBUG
      if(ix[i] < 0 || ix[i] >= lim )
	 message(0,0,2, "Gather index out of bounds %d %d\n", i, ix[i]);
#endif
      a[i] = b[ix[i]];
   }
}

#endif
/******************************************************************************
 *  random number generators. Note they assume 'unsigned' of at least 32 bits *
 ******************************************************************************/
#define  im  1771875L
#define  ia  2416L
#define  ic  374441L
static unsigned long jran = 1;
void	smdrand(seed)
unsigned long seed;
{
   jran = seed;
}
double	mdrand()
{
   jran = (jran*ia + ic) % im;
   return (double)jran/(double)im;
}
/******************************************************************************
 *  Precision. Return smallest eps s.t. 1.0+eps > 1			      *
 ******************************************************************************/
double	precision()
{
   static	int	first=1;
   static	double	eps = 0.5;
   double VOLATILE	eps2, *eps1 = &eps2;	/* Use pointer to force store */
   double VOLATILE      junk, *ptr = &junk;
   
   if(first)
   {
      first = 0;
      do
      {
         eps = eps * 0.5;
         *eps1 = 1.0 + eps;
	 *ptr  = 1.0;		/* This prevents optimization of *eps1 */
      }   while(*eps1 > 1.0);
      eps *= 2.0;
   }
   return(eps);
}
/******************************************************************************
 *  cpu.  Return (double) the current cpu time in seconds.		      *
 *  rt_clock(). Return elapsed time in s.				      *
 ******************************************************************************/
#if defined(unix) || defined(__unix__)
double        cpu()
{
   struct tms buf;
 
   (void)times(&buf);

   return (buf.tms_utime + buf.tms_stime)/(double)CLK_TCK;
}

/*
 * The BSD version way to get the rt clock is via gettimeofday.  But
 * some systems , noticably Ultrix, DON'T include the
 * necessary struct definitions if _POSIX_SOURCE is defined!.  Aargh.
 * However in that case we can rely on POSIX behaviour of times -
 * (ie it's return value) - so the alternative rt_clock ought to
 * work.   The test is for the macro associated with the "timezone"
 * struct definition.
 */
#if defined(BSD) && defined(DST_NONE)

double rt_clock()
{
   int gettimeofday();
   struct timeval tp;
   (void)gettimeofday(&tp,(struct timezone *)0);
   return(tp.tv_sec + tp.tv_usec*0.000001);
}

#else			/* System V or POSIX.			      */

double rt_clock()
{
   struct tms buf;
 
   return times(&buf)/(double)CLK_TCK;
}

#endif			/* USG or BSG					      */
#else			/* Not Unix					      */
#if !defined(CLOCKS_PER_SEC) && defined(CLK_TCK)
#   define  CLOCKS_PER_SEC CLK_TCK
#endif
double	cpu()
{
   return((double)clock() / CLOCKS_PER_SEC);
}
double rt_clock()
{
   return time((time_t *)0);
}
#endif
/******************************************************************************
 *  cctime()  Return ctime() but without the newline			      *
 ******************************************************************************/
char	*cctime(timeloc)
time_mt	*timeloc;
{
   time_t  loc = *timeloc;
   char	*p = ctime(&loc);
   *strchr(p, '\n') = '\0';
   return(p);
}
/******************************************************************************
 *  atime.  Return a pointer to the time and date string		      *
 ******************************************************************************/
char	*atime()
{
   time_mt t = time((time_t*)0);
   return(cctime(&t));
}
/******************************************************************************
 * Zero real.  Set array to floating-point zero.			      *
 ******************************************************************************/
void	zero_real(r,n)
real	r[];
int	n;
{
   int i;
VECTORIZE
   for(i = 0; i < n; i++)
      r[i] = 0.0;
}
void	zero_double(r,n)
double	r[];
int	n;
{
   int i;
VECTORIZE
   for(i = 0; i < n; i++)
      r[i] = 0.0;
}
void	zero_dbls(r,n)
double	r[];
size_mt	n;
{
   long i;
VECTORIZE
   for(i = 0; i < n; i++)
      r[i] = 0.0;
}
/******************************************************************************
 *  replace - replace file1 by file2, renaming or overwriting		      *
 ******************************************************************************/
#if defined(unix) || defined(__unix__)
int	replace(file1, file2)
char	*file1, *file2;
{
   int f;
   char *backup = aalloc(strlen(file2)+2, char);
   (void)strcat(strcpy(backup, file2), "%");

   f = rename(file2, backup);		/* Backup old version - ignore failure*/
   f = rename(file1, file2);		/* Rename old version to new	      */
   xfree(backup);
   return(f);
}
#   else
int	replace(file1, file2)
char	*file1, *file2;
{
   int f;
   int rename();
   f = rename(file1, file2);
   if(f != 0 && remove(file2) == 0)	/* If already exists try remove target*/
      f = rename(file1, file2);
   return(f);
}
#endif
/******************************************************************************
 *  Purge Remove 1 old version of a file.  (Do nothing if O/S has no versions)*
 ******************************************************************************/
#ifdef vms 
void	purge(file)
char	*file;
{
   char *name = aalloc(strlen(file)+4,char);
   strcpy(name, file);
   if( strchr(name, ';') == NULL)
      (void)remove(strcat(name,";-1"));
   tfree(name);
}
#else				/*  VMS				*/
#   if defined(unix) || defined(__unix__)
void	purge(file)
char	*file;
{
   int unlink();
   char *name = aalloc(strlen(file)+2,char);
   (void)strcat(strcpy(name, file), "%");

   (void)unlink(name);
   xfree(name);
}
#      else
/*ARGSUSED*/
void	purge(file)
char	*file;
{
}
#   endif			
#endif				/*  VMS				*/
/******************************************************************************
 * err_fn  error function. See Abramowitz & Stegun p299.		      *
 ******************************************************************************/
#define E1	 0.254829592
#define E2	-0.284496736
#define E3	 1.421413741
#define E4	-1.453152027
#define E5	 1.061405429

#define PP	 0.3275911
#define POLY5(t)   ((t)*(E1 + (t)*(E2 + (t)*(E3 + (t)*(E4 + (t)*E5)))))

double	err_fn(x)
double	x;
{
   double t = 1.0/(1.0 + PP * x);
   if ( x >= 0.0 )
      return( 1.0 - POLY5(t) * exp(-x*x) );
   else
      return( -err_fn(-x) );
}
/******************************************************************************
 * inhibit_vectorization().  Self explanatory dummy function		      *
 ******************************************************************************/
void inhibit_vectorization()
{
}
$EOD
$!
$CREATE beeman.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 *   Beeman - Routines to implement the modified Beeman algorithm for         *
 *            stepping the co-ordinates in a MD simulation including rigid    *
 *            molecules                                                       *
 *   Original algorithm - D. Beeman J. Comp. Phys 20, 131-139 (1976)          *
 *   Modified with velocity predictor to handle velocity-dependant forces     *
 *                      - K. Refson Physica 131B 256-266 (1985)               *
 *                                                                            *
 *   External routines:          step_1(system), step_2(system), beeman_2()   *
 *   External references:        none                                         *
 *   External data:              none                                         *
 *   External data references:   control                                      *
 *                                                                            *
 *   Note:  The routines beeman_1, beeman_2 and predict treat their arguments *
 *          as one dimensional arrays for simplicity.  Thus the 'number'      *
 *          argument is 3 (for c_of_m) or 4 (for quaternions) times the number*
 *          of particles.  This may cause 'consistency' messages from lint.   *
 ******************************************************************************
 *      Revision Log
 *       $Log: beeman.c,v $
 *       Revision 2.9  1996/08/14 16:23:24  keith
 *       Fixed error in thermoststat implementation and integration.
 *
 *       Revision 2.8  1995/12/04 11:45:49  keith
 *       Nose-Hoover and Gaussian (Hoover constrained) thermostats added.
 *       Thanks to V. Murashov.
 *
 * Revision 2.7  1994/06/08  13:09:29  keith
 * Protected against possible bus error for systems with no
 * rotational freedom by making all references to "quat" etc conditional
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Revision 2.5  1994/01/18  13:32:13  keith
 * Null update for XDR portability release
 *
 * Revision 2.3  93/10/28  10:27:45  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.0  93/03/15  14:48:58  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.5  93/03/15  14:41:37  keith
 * Added GPL copyleft notice to permit release and distribution.
 * N.B.  Previous versions were copyright (C) by author and 
 * only licensed by explicit permission.
 * 
 * Revision 1.4  93/03/09  15:58:21  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.3  91/08/15  18:11:47  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_t and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.2  89/10/24  17:18:37  keith
 * Modified pbc algorithm to use floor() library function.
 * Now works with non-orthorhombic cell.
 * 
 * Revision 1.1  89/04/20  16:00:35  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/beeman.c,v 2.9 1996/08/14 16:23:24 keith Exp $";
#endif
/*========================== Program include files ===========================*/
#include	"defs.h"
/*========================== Library include files ===========================*/
#include 
/*========================== Program include files ===========================*/
#include "structs.h"
#include "messages.h"
/*========================== External function declarations ==================*/
#if defined(ANSI) || defined(__STDC__)
void	note(char *, ...);		/* Write a message to the output file */
void	message(int *, ...);		/* Write a warning or error message   */
#else
void	note();				/* Write a message to the output file */
void	message();			/* Write a warning or error message   */
#endif
/*========================== External data references ========================*/
extern	contr_mt	control;            /* Main simulation control parms. */
/*========================== Macros ==========================================*/
#define	TOLERANCE	1.0e-4
/*============================================================================*/
/******************************************************************************
 *   Normalise the new quaternions                                            *
 ******************************************************************************/
static void normalise(quat,n)
int		n;			/* Number of quaternions  (in)        */
quat_mp		quat;			/* Quaternions       (update)         */
{
   register int		i, j;
   register double	norm;

   for(i=0; i TOLERANCE)
         message(NULLI, NULLP, FATAL, QNORM2, i,
		 quat[i][0],quat[i][1],quat[i][2],quat[i][3]);
      for(j=0; j<4; j++)  quat[i][j] /= norm;
   }
}
/******************************************************************************
 *   Constrain the quaternion derivatives				      *
 ******************************************************************************/
void constrain(quat, qdot ,n)
int		n;			/* Number of quaternions  (in)        */
quat_mp		quat;			/* Quaternions            (in)        */
quat_mp		qdot;			/* Quaternion derivatives (update)    */
{
   register int		i, j;
   register double	delta;

   for(i=0; i TOLERANCE)
         message(NULLI, NULLP, FATAL, QCONST, i, delta);
      for(j=0; j<4; j++)  qdot[i][j] -= delta * quat[i][j];
   }
}
/******************************************************************************
 *   Apply periodic boundary conditions to put particles back in MD box       *
 ******************************************************************************/
static
void escape(c_of_m, nmols)
int		nmols;		/* First dimension of c-of-m                  */
vec_mp		c_of_m;		/* Centre of mass co-ordinates (updat)        */

{
   int	imol;			/* Molecule counter			      */

   for(imol = 0; imol < nmols; imol++)
   {
      c_of_m[imol][0] -= floor(c_of_m[imol][0] + 0.5);
      c_of_m[imol][1] -= floor(c_of_m[imol][1] + 0.5);
      c_of_m[imol][2] -= floor(c_of_m[imol][2] + 0.5);
   }
}
/******************************************************************************
 *   Step co-ordinates as first  (P(r)) stage of Beeman algorithm             *
 ******************************************************************************/
static void beeman_1(x, xdot, xddot, xddoto, n) 
int		n;			/* Length of vectors       (in)       */
real		x[],			/* Co-ordinate array   (update)       */
		xdot[],			/* First derivatives       (in)       */
		xddot[],		/* Accelerations           (in)       */
		xddoto[];		/* Old accelerations       (in)       */
{
     register int	i;
     register real	step = control.step,
			step_sq = step * step / 6.0;
            
      for(i=0; ic_of_m[0],sys->vel[0],sys->acc[0],sys->acco[0], 3*sys->nmols);
   escape(sys->c_of_m, sys->nmols);
   if( sys->nmols_r > 0 )
   {
      beeman_1(sys->quat[0],sys->qdot[0],sys->qddot[0],sys->qddoto[0],
	       4*sys->nmols_r);
      normalise(sys->quat, sys->nmols_r);
   
   }
   if(control.const_pressure)
      beeman_1(sys->h[0], sys->hdot[0], sys->hddot[0], sys->hddoto[0], 9);

   predict(sys->vel[0], sys->velp[0], sys->acc[0], 
           sys->acco[0], sys->accvo[0], 3*sys->nmols);
   if( sys->nmols_r > 0 )
   {
      predict(sys->qdot[0], sys->qdotp[0], sys->qddot[0], 
	      sys->qddoto[0], sys->qddotvo[0], 4*sys->nmols_r);
      constrain(sys->quat, sys->qdotp, sys->nmols_r);
   
      if (control.const_temp)
         predict(sys->ra, sys->rap, sys->radot, sys->radoto,
                 sys->radotvo, sys->nspecies);
   }
   if(control.const_pressure)
      predict(sys->h[0], sys->hdotp[0], sys->hdot[0],
              sys->hddoto[0], sys->hddotvo[0], 9);

   if (control.const_temp)
      predict(sys->ta, sys->tap, sys->tadot, sys->tadoto,
              sys->tadotvo, sys->nspecies);
#ifdef DEBUG3
   printf("Step1\n");
#endif 
}
   
/******************************************************************************
 *   Apply second stage of modified beeman algorithm to the whole system      *
 *   Step the centre of mass velocities, and the derivatives of the           *
 *   quaternions and Parinello & Rahman h matrix (if const pressure           *
 *   simulation), and predict the corresponding velocities.                   *
 ******************************************************************************/
void step_2(sys)
system_mp	sys;			/* pointer to whole-system record     */
{
   beeman_2(sys->vel[0], sys->vel[0], sys->acc[0], sys->acco[0],sys->accvo[0],
	    3*sys->nmols);
   if( sys->nmols_r > 0 )
   {
      beeman_2(sys->qdot[0], sys->qdot[0], sys->qddot[0], sys->qddoto[0],
	    sys->qddotvo[0], 4*sys->nmols_r);
      constrain(sys->quat, sys->qdot, sys->nmols_r);

      if (control.const_temp == 1)
         beeman_2(sys->ra, sys->ra, sys->radot, sys->radoto,
                  sys->radotvo, sys->nspecies);

   }
   if(control.const_pressure)
      beeman_2(sys->hdot[0], sys->hdot[0], sys->hddot[0], sys->hddoto[0],
	       sys->hddotvo[0], 9);

   if (control.const_temp == 1)
      beeman_2(sys->ta, sys->ta, sys->tadot, sys->tadoto,
               sys->tadotvo, sys->nspecies);
#ifdef DEBUG3
   printf("Step2\n");
#endif 
}
$EOD
$!
$CREATE convert.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * Convert	Functions for conversion of units of input parameters.	      *
 ******************************************************************************
 *      Revision Log
 *       $Log: convert.c,v $
 *       Revision 2.8  1995/12/07 17:47:59  keith
 *       Reworked V. Murashov's thermostat code.
 *       Convert mass params from kJ/mol ps^2 to prog units. Defaults=1.
 *
 *       Revision 2.8  1995/12/06 17:49:03  keith
 *       Updated conversion table for thermostat parameters.
 *
 *       Revision 2.7  1994/06/08 13:22:31  keith
 *       Null update for version compatibility
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Revision 2.5  94/01/18  13:32:14  keith
 * Null update for XDR portability release
 * 
 * Revision 2.3  93/10/28  10:27:46  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.0  93/03/15  14:49:00  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.5  93/03/09  15:58:24  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.4  92/10/28  14:10:01  keith
 * Changed "site_[tp]" typedefs to avoid name clash on HP.
 * 
 * Revision 1.3  91/08/15  18:11:49  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_t and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.2  90/05/21  15:29:28  keith
 * Moved definition of struct pot_dim[][] from convert.c to kernel.c.
 * 
 * Revision 1.1  89/04/20  16:00:36  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/convert.c,v 2.8 1995/12/07 17:47:59 keith Exp $";
#endif
/*========================== Program include files ===========================*/
#include	"defs.h"
/*========================== Library include files ===========================*/
#include	
/*========================== Program include files ===========================*/
#include	"structs.h"
#include	"messages.h"
/*========================== External data references ========================*/
extern	      contr_mt	control;            /* Main simulation control parms. */
extern  CONST dim_mt	pot_dim[][NPOTP];    /* Pot'l dimension specification */
/*========================== External function declarations ==================*/
#if defined(ANSI) || defined(__STDC__)
void	note(char *, ...);		/* Write a message to the output file */
void	message(int *, ...);		/* Write a warning or error message   */
#else
void	note();				/* Write a message to the output file */
void	message();			/* Write a warning or error message   */
#endif
/*========================== Structs local to module =========================*/
/* This struct array contains all details for conversions in 'control' struct */
typedef struct
{
  double	*d;			/* Pointer to item to be converted    */
  dim_mt		dim;			/* Dimensions of quantity (eg MLT(-2))*/
  unit_mt	unit;			/* Units (in MKS) for conversion from */
}  conv_mt,  *conv_mp;
/*========================== Global variables ================================*/
#define TMUNIT (MUNIT/CONV_TM)
static CONST conv_mt 
	conv[] = {  {&control.step,	{0,0,1,0},	{1,1,TUNIT,1}},
                    {&control.pressure,	{1,-1,-2,0},	{1e6,1,1,1}},
		    {&control.pmass,	{1,0,0,0},	{MUNIT,1,1,1}},
		    {&control.ttmass,	{1,2,0,0},	{TMUNIT,LUNIT,1,1}},
		    {&control.rtmass,	{1,2,0,0},	{TMUNIT,LUNIT,1,1}},
		    {&control.cutoff,	{0,1,0,0},	{1,LUNIT,1,1}},
		    {&control.density,	{1,-3,0,0},	{.001,.01,1,1}},
		    {&control.alpha,	{0,-1,0,0},	{1,LUNIT,1,1}},
		    {&control.limit,	{0,-1,0,0},	{1,LUNIT,1,1}}};
#define	NCONV	(sizeof conv / sizeof(conv_mt))
/*============================================================================*/
/******************************************************************************
 *  unit_scale  Takes the dimensions of a quantity (dim), the units it is in  *
 *  (unit_from) and returns the scale by which it must be multiplied to       *
 *  convert it to new units (unit_to).  Uses logarithms to avoid overflow.    *
 ******************************************************************************/
#define	MAX_SCALE	80			/* 1e35 - safe for any machine*/
static double	unit_scale(dim, unit_from, unit_to)
CONST dim_mp	dim;			/* Dimensions			      */
unit_mp	unit_from, unit_to;		/* Units to convert from/to	      */
{
   double	lnscale = 	dim->m*(log(unit_from->m) - log(unit_to->m))
			      + dim->l*(log(unit_from->l) - log(unit_to->l))
			      + dim->t*(log(unit_from->t) - log(unit_to->t))
			      + dim->q*(log(unit_from->q) - log(unit_to->q));
   if(lnscale < -MAX_SCALE || lnscale > MAX_SCALE)
      message(NULLI,NULLP,FATAL,BADUNI,lnscale);
   return(exp(lnscale));
}
/******************************************************************************
 *  Convert_potentials   Scale potential parameters, site masses and charges  *
 *  from input units to program units.					      *
 ******************************************************************************/
void	conv_potentials(unit_from, unit_to, potpar, npotpar, ptype,
			   site_info, max_id)
CONST unit_mp	unit_from, unit_to;	/* Values of units for conversion     */
pot_mt		potpar[];		/* Array of potpar records[max_id**2] */
int		npotpar;		/* Number of 'active' parameters      */
int		ptype;			/* Potential type		      */
site_mt		site_info[];		/* Site specification array[max_id]   */
int		max_id;			/* How many site id's		      */
{
   int		idi, idj, ip;		/* Counters for id's and potpar	      */
   static dim_mt	mass_dim   = {1,0,0,0},	/* Dimensions of mass		      */
                charge_dim = {0,0,0,1};	/* Dimensions of charge		      */
   double	mscale = unit_scale(&mass_dim,   unit_from, unit_to),
                qscale = unit_scale(&charge_dim, unit_from, unit_to);
					/* Scale factors for mass and charge  */
   double	potscale[NPOTP];	/* Scale factors for pot'l parameters */

   for(ip = 0; ip < npotpar;ip++)	/* Work out scale factors for pot'l   */
      potscale[ip] = unit_scale(&pot_dim[ptype][ip],unit_from, unit_to);

   for(idi = 0; idi < max_id; idi++)
   {
      site_info[idi].mass   *= mscale;	/* Scale mass of each site	      */
      site_info[idi].charge *= qscale;	/* Scale charge of each site	      */
      for(idj = 0; idj < max_id; idj++)
          for(ip = 0; ip < npotpar; ip++)	/* Scale potential parameters */
             potpar[idi + max_id*idj].p[ip] *= potscale[ip];
   }
}
/******************************************************************************
 *   convert_control  Convert various quantities in the control record between*
 *   input and program units.  Which to convert are indicated by the array    *
 *   'conv' which contains a pointer to the data and a dimension struct.      *
 ******************************************************************************/
void	conv_control(unit, direction)
CONST unit_mp	unit;  			/* Units conversion is to/from	      */
boolean		direction;		/* True=from input, false=to input    */
{
   int		ic;			/* Counter			      */
   for(ic = 0; ic < NCONV; ic++)
      if(direction)
         *conv[ic].d *= unit_scale(&conv[ic].dim, &conv[ic].unit, unit);
      else
         *conv[ic].d *= unit_scale(&conv[ic].dim, unit, &conv[ic].unit);
}
$EOD
$!
$CREATE dump.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 *  dump   Does a periodic dump of simulation data to a file.	In fact most  *
 *  of this function is concerned with the bookkeeping associated with 	      *
 *  maintaining a sequentially numbered set of contiguous dump files, 	      *
 *  ensuring contiguity and dealing with errors. The actual writing is done   *
 *  by 'write_dump_record'.
 * 									      *
 *    The user must supply a prototype filename (control.dump_file, read in   *
 *  by keyword "dump-name".  This should contain a numeric conversion code    *
 *  in 'printf' format (eg %3.3d ) which will be replaced by a sequential file*
 *  number.  (If it is absent the file number is added to the end).	      *
 *  Each dump file consists of a header record which is a struct (type dump_t)*
 *  followed by up to control.ndumps dump records.  A file may be incomplete- *
 *  the ndumps field of the header struct records the actual number.  It is   *
 *  not necessary to write one file per run - records are added to the end    *
 *  of an existing file and a new one is started only when it is full. The    *
 *  contents of the record are determined by the "dump-level" parameter and   *
 *  consist of binary data.  Each header struct contains a unique timestamp - *
 *  "dump_init" which is the same for all files in a sequence of dumps. (Note *
 *  that the previous dump file MUST be accessible in order to read and       *
 *  propagate this number - so when archiving dumps, always leave the very    *
 *  last one.)								      *
 ******************************************************************************
 *      Revision Log
 *       $Log: dump.c,v $
 *       Revision 2.9  1996/03/06 18:20:04  keith
 *       Added cast in xdr_vector() call to supress spurious warning message.
 *
 *       Revision 2.8  1994/10/17 10:57:11  keith
 *       Fixed bug in sequence code to check timesteps.
 *
 * Revision 2.7  1994/06/08  13:11:43  keith
 * Protected against possible bus error for systems with no
 * rotational freedom by making all references to "quat" etc conditional
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Revision 2.5  1994/02/01  14:33:45  keith
 * Revised consistency checks to safeguard existing files:
 * Now checks timestep in dump header.
 * Reverted behaviour on dump-level change to start new run
 *  - acidentally botched in 2.4.
 * Changed behaviour on restart-file consistency check failure
 * to start new run too.
 * Improved messages.
 * 
 * Revision 2.4  1994/01/24  18:32:32  keith
 * Don't rename dump run if restarting from backup file and
 * dump file exists and matches.
 *
 * Revision 2.3  93/10/28  10:27:47  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.1  93/08/18  20:52:08  keith
 * Added support for dumps in XDR format.
 * 
 * Revision 2.0  93/03/15  14:49:01  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.19  93/03/09  15:58:26  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.18  92/06/02  10:37:52  keith
 * Added check of ferror() as well as return from fwrite().  Talk
 * about belt and braces.
 * 
 * Revision 1.17  92/04/21  17:49:16  keith
 * Corrected error message
 * 
 * Revision 1.16  92/03/02  15:28:32  keith
 * Fixed bug which caused dump/restart consistency check to erroneously
 * report an error on the second dump write.
 * Relaxed check to allow dump run to abort and restart from beginning.
 * 
 * Revision 1.15  91/08/16  15:23:57  keith
 * Checked error returns from fread, fwrite, fseek and fclose more
 * rigourously.   Called strerror() to report errors.
 * 
 * Revision 1.14  91/08/15  18:11:51  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_t and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.13  91/03/12  15:42:24  keith
 * Tidied up typedefs size_t and include file 
 * Added explicit function declarations.
 * 
 * Revision 1.12  91/02/19  14:51:05  keith
 * Minor changes to get rid of misleading compiler warnings.
 * 
 * Revision 1.11  90/05/16  18:40:01  keith
 * Renamed own freer from cfree to tfree.
 * 
 * Revision 1.10  90/05/16  14:10:18  keith
 * Modified so that mutates always apply to rest of run.
 * 
 * Revision 1.9  90/05/02  15:28:52  keith
 * Removed references to size_t and time_t typedefs, no longer in "defs.h"
 * 
 * Revision 1.8  90/04/25  17:09:23  keith
 * Corrected test for mutation failure.
 * 
 * Revision 1.7  90/04/17  10:49:47  keith
 * Corrected test for dump and restart consistency.
 * 
 * Revision 1.6  90/04/09  14:49:33  keith
 * Now tests for failure of mutate() and gives up rather than lloping.
 * 
 * Revision 1.5  89/10/16  15:47:37  keith
 * Fixed DUMP_SIZE macro to be correct!
 * 
 * Revision 1.4  89/07/05  18:42:06  keith
 * Eliminated 'dump-offset' - renumbering starts at 1 for new dump run.
 * 
 * Revision 1.3  89/05/15  17:54:12  keith
 * Added members 'vsn' and 'dump_size' to header (cf structs.h r1.3)
 * Fixed bugs in call of write and ftell to write data correctly.
 * 
 * Revision 1.2  89/05/11  13:56:05  keith
 * Tidied up code using flags rather than GOTO's
 * Fixed bug in 'dump_convert' which wrote outside array bounds
 * Put PE in dump-level 1 rather than 8
 * 
 * Revision 1.1  89/04/27  15:13:56  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/dump.c,v 2.9 1996/03/06 18:20:04 keith Exp $";
#endif
/*========================== program include files ===========================*/
#include	"defs.h"
/*========================== Library include files ===========================*/
#include	
#include 	"string.h"
#include	"stddef.h"
#include	"time.h"
#include	
/*========================== program include files ===========================*/
#include	"structs.h"
#include	"messages.h"
#include	"xdr.h"
/*========================== External function declarations ==================*/
gptr            *talloc();	       /* Interface to memory allocator       */
void            tfree();	       /* Free allocated memory	      	      */
static char	*mutate();
double		mdrand();
void		mat_vec_mul();
static void	dump_convert();
static void	real_to_float();
#if defined(ANSI) || defined(__STDC__)
void	note(char *, ...);		/* Write a message to the output file */
void	message(int *, ...);		/* Write a warning or error message   */
#else
void	note();				/* Write a message to the output file */
void	message();			/* Write a warning or error message   */
#endif
/*========================== External data references ========================*/
extern contr_mt	control;                    /* Main simulation control parms. */
#ifdef USE_XDR
static   XDR		xdrs;
#endif
/*========================== Macros ==========================================*/
#define DUMP_SIZE(level)  (( (level & 1) + (level>>1 & 1) + (level>>2 & 1) ) * \
			            (3*system->nmols + 4*system->nmols_r + 9)+ \
			     (level>>3 & 1) * \
			            (3*system->nmols + 3*system->nmols_r + 9) +\
			     (level & 1))
/*============================================================================*/
static
int read_dump_hdr(fname, dumpf, hdr_p, xdr_write)
char	*fname;
FILE	**dumpf;
dump_mt	*hdr_p;
boolean	*xdr_write;
{
   int      errflg = true;	/* Provisionally !!   */

   *xdr_write = false;
   if( (*dumpf = fopen(fname, "r+b")) == NULL)	/* Open dump file     */
      message(NULLI, NULLP, WARNING, DOERRR, fname, strerror(errno));
   else 
   {
#ifdef USE_XDR
      /*
       * Attempt to read dump header in XDR format
       */
      xdrstdio_create(&xdrs, *dumpf, XDR_DECODE);
      if( xdr_dump(&xdrs, hdr_p) )
      {
	 hdr_p->vsn[sizeof hdr_p->vsn - 1] = '\0';
	 if( strstr(hdr_p->vsn,"(XDR)") )
	 {
	    errflg = false;
	    *xdr_write = true;
	 }
      }
#endif
      /*
       * If we failed, try to read header as native struct image.
       */
      if( ! *xdr_write )
      {
	 if( fseek(*dumpf, 0L, 0) ) 
	    message(NULLI, NULLP, WARNING, SEFAIL, fname, strerror(errno));
	 else if( fread((gptr*)&*hdr_p, sizeof(dump_mt), 1, *dumpf) == 0 )
	    message(NULLI, NULLP, WARNING, DRERR, fname, strerror(errno));
	 else
	    errflg = false;
      }
   }
   return errflg;
}

/*============================================================================*/

void	dump(system, force, torque, stress, pe, restart_header, backup_restart)
restrt_mt	*restart_header;
int		backup_restart;
system_mp	system;
vec_mt		force[], torque[];
mat_mt		stress;
double		pe;
{
   FILE		*dumpf=NULL;		/* File pointer to dump files	      */
   dump_mt	dump_header,		/* Header record proforma	      */
   		hdr_tmp;
   char		cur_file[L_name],	/* Names of current and previous      */
   		prev_file[L_name],	/* dump files.			      */
   		*fname;			/* Pointer to one of above filenames  */
   int		filenum=(control.istep-control.begin_dump) /
   		        (control.dump_interval * control.maxdumps),
		ndumps =(control.istep-control.begin_dump) /
   			 control.dump_interval % control.maxdumps; 
   int		istep_hdr;		/* Timestep for dump header	      */
   unsigned     dump_size = DUMP_SIZE(control.dump_level);
                                        /* Size in floats of each dump record */
   float	*dump_buf=aalloc(dump_size,float);      /* For converted data */
   long		file_pos=0,		/* Offset within file of current rec. */
   		file_len;		/* Length of file		      */
   		boolean errflg = false;
#define		NMUTATES 10   		/* Max number of mutation attempts.   */
   int		nmutates = 0;   	/* Number of mutation attempts.	      */
   int		junk;
   boolean	xdr_write = false;	/* Is current dump in XDR format?     */
   static int	firsttime = 1;

   if( ! strchr(control.dump_file, '%') )
      	(void)strcat(control.dump_file, "%d");
   (void)sprintf(cur_file,  control.dump_file, filenum);
   (void)sprintf(prev_file, control.dump_file, filenum-1);

   if( control.istep > control.begin_dump )	/* Continue existing dump run */
   {						/* Get last dump's header     */
      if( ndumps == 0 )		fname = prev_file;
                        else    fname = cur_file;

      istep_hdr = control.istep - ndumps*control.dump_interval;
      if( ndumps == 0 )
	 istep_hdr -= control.maxdumps*control.dump_interval;
      errflg = true;
      /*
       * Attempt to read header and perform consistency checks which,
       * if failed, are "fatal" to dump run and initiate a *NEW* run
       * by setting errflg. 
       */
      if( read_dump_hdr(fname, &dumpf, &dump_header, &xdr_write) /* fails */ )
	 errflg = true;		                 /* message already printed */
      else if( control.dump_level != dump_header.dump_level )	/* Level    */
	 message(NULLI, NULLP, WARNING, DMPALT);
      else if( istep_hdr != dump_header.istep ) 		/* Timeslice */
	 message(NULLI, NULLP, WARNING, DUMPTS, fname, 
		 istep_hdr,dump_header.istep);
      else if( firsttime && 				  /* Matches Restart */
	    dump_header.timestamp < restart_header->timestamp &&
	    dump_header.restart_timestamp != restart_header->prev_timestamp &&
	    dump_header.restart_timestamp != restart_header->timestamp )
	 message(NULLI, NULLP, WARNING, CONTIG, fname);
      else
	 errflg = false;
      /*
       * At this point we think we have a matching dump header.  
       * Check to see if amount of data in file matches and whether
       * length of file is consistent with header.  Any errors now are
       * too serious to just start another dump sequence and so abort
       * the run. 
       */
      if( !errflg )
      {
	 if( dump_header.ndumps < (ndumps ? ndumps : control.maxdumps ) )
            message(NULLI,NULLP,FATAL, SHTDMP,
		    fname, dump_header.ndumps, ndumps);
	 else if( dump_header.ndumps >  (ndumps ? ndumps : control.maxdumps) )
            message(NULLI,NULLP,WARNING,LNGDMP,
		    fname, dump_header.ndumps, ndumps);

	 dump_header.ndumps = ndumps;
      
	 if( fseek(dumpf, 0L, SEEK_END) )
	    message(NULLI, NULLP, FATAL, SEFAIL, fname, strerror(errno));
	 file_len = ftell(dumpf);		/* Get length of file	      */
#ifdef USE_XDR
	 if( xdr_write )
	    file_pos = XDR_DUMP_SIZE
	       + ndumps*dump_size*XDR_FLOAT_SIZE;	/* Expected length    */
	 else
#endif
	    file_pos = sizeof(dump_mt)
	       + ndumps*dump_size*sizeof(float);	/* Expected length    */
	 if( file_len < file_pos )
         	message(NULLI, NULLP, FATAL, CORUPT, fname, file_pos, file_len);
      }
      else
      {
	 if( dumpf )
	 {
#ifdef USE_XDR
	    if( xdr_write )
	       xdr_destroy(&xdrs);
#endif
	    (void)fclose(dumpf);
	 }
	 if( ndumps != 0 )
	 {
	    ndumps = 0; filenum = 0;
	    control.begin_dump = control.istep;
            (void)sprintf(cur_file, control.dump_file, filenum);
	 }
	 message(NULLI,NULLP,WARNING,DRESET);
      }
   }
   if( errflg || control.istep == control.begin_dump )
   {
      (void)strcpy(dump_header.title, control.title);
      (void)strncpy(dump_header.vsn, "$Revision: 2.9 $"+11,
		                     sizeof dump_header.vsn-1);
#ifdef USE_XDR
      if( control.xdr_write )
      {
	 (void)strncat(dump_header.vsn, " (XDR)",
		                     sizeof dump_header.vsn-1);
	 xdr_write = true;
      }
#endif
      dump_header.dump_interval = control.dump_interval;
      dump_header.dump_level    = control.dump_level;
      dump_header.maxdumps	= control.maxdumps;
      dump_header.dump_size	= dump_size;
      dump_header.dump_init     = time((time_t *)0);

      note(DUMPST, cur_file, control.istep);
   }
      
   if( ndumps == 0)				/* Start of new dump file     */
   {
      if( dumpf ) 		/* Finished with old file     */
      {
#ifdef USE_XDR
	 if( xdr_write )
	    xdr_destroy(&xdrs);
#endif
	 (void)fclose(dumpf);
      }
      while( (dumpf = fopen(cur_file, "r")) != 0 )	/* File of that name */
      { 						/* already exists    */
	 (void)fclose(dumpf);
	 /*
	  *  Test whether existing file belongs to current dump
	  *  sequence in case of run restarted from backup.  In that
	  *  case, OVERWRITE it. 
          */
	 if( backup_restart )
	 {
	    errflg = read_dump_hdr(cur_file, &dumpf, &hdr_tmp, &junk);
	    if( errflg == false && (hdr_tmp.dump_init == dump_header.dump_init) )
	    {
	       (void)fclose(dumpf);
	       break;
	    }
	 }
	 
	 if( nmutates++ >= NMUTATES || mutate(control.dump_file) == NULL)
	    message(NULLI, NULLP, FATAL, MUFAIL, control.dump_file, nmutates);
	 message(NULLI, NULLP, WARNING, DMPEXS, cur_file, control.dump_file);
	 (void)sprintf(cur_file, control.dump_file, filenum);
      }

      dump_header.ndumps = 0;	dump_header.istep = control.istep;
      dump_header.timestamp = time((time_t *)0);
      dump_header.restart_timestamp = 0;

      if( (dumpf = fopen(cur_file, "w+b")) == 0)
	 message(NULLI, NULLP, FATAL, DOERRW, cur_file, strerror(errno));
#ifdef USE_XDR
      if( xdr_write )
      {
	 xdrstdio_create(&xdrs, dumpf, XDR_ENCODE);
	 if( ! xdr_dump(&xdrs, &dump_header) )
	    message(NULLI, NULLP, FATAL, DWERR, cur_file, strerror(errno));
	 if( (file_pos = xdr_getpos(&xdrs)) == -1)
	    message(NULLI, NULLP, FATAL, GPFAIL, cur_file, strerror(errno));
      }
      else
#endif
      {
	 if( fwrite((gptr*)&dump_header, sizeof(dump_mt), 1, dumpf) == 0)
	    message(NULLI, NULLP, FATAL, DWERR, cur_file, strerror(errno));
	 if( (file_pos = ftell(dumpf)) == -1)
	    message(NULLI, NULLP, FATAL, GPFAIL, cur_file, strerror(errno));
      }
   }
#ifdef USE_XDR
   else
   {
      if( xdr_write )
      {
	 xdr_destroy(&xdrs);
	 xdrstdio_create(&xdrs, dumpf, XDR_ENCODE);
      }
   }
#endif

   dump_convert(dump_buf, system, force, torque, stress, pe);
   dump_header.ndumps++;
   dump_header.restart_timestamp = restart_header->timestamp;

#ifdef USE_XDR
   if( xdr_write )
   {
      if( ! xdr_setpos(&xdrs, file_pos) )		/* Write data at end */
	 message(NULLI, NULLP, FATAL, SEFAIL, cur_file, strerror(errno));
      if( ! xdr_vector(&xdrs, (gptr*)dump_buf, dump_size, sizeof(float), 
		     (xdrproc_t)xdr_float) )
	 message(NULLI, NULLP, FATAL, DWERR, cur_file, strerror(errno));
   }
   else
#endif
   {
      if( fseek(dumpf, file_pos, SEEK_SET) )		/* Write data at end */
	 message(NULLI, NULLP, FATAL, SEFAIL, cur_file, strerror(errno));
      if( fwrite((gptr*)dump_buf, sizeof(float), dump_size, dumpf) < dump_size )
	 message(NULLI, NULLP, FATAL, DWERR, cur_file, strerror(errno));
   }

#ifdef USE_XDR
   if( xdr_write )
   {
      (void)xdr_setpos(&xdrs, 0);
      if( ! xdr_dump(&xdrs, &dump_header) )
	 message(NULLI, NULLP, FATAL, DWERR, cur_file, strerror(errno));
      xdr_destroy(&xdrs);
   }
   else
#endif
   {
      (void)fseek(dumpf, 0L, SEEK_SET);			/* Write header      */
      if( fwrite((gptr*)&dump_header, sizeof(dump_mt), 1, dumpf) == 0)
	 message(NULLI, NULLP, FATAL, DWERR, cur_file, strerror(errno));
   }
   if( ferror(dumpf) || fclose(dumpf) )
      message(NULLI, NULLP, FATAL, DWERR, cur_file, strerror(errno));

   firsttime = 0;
   xfree(dump_buf);
}
/******************************************************************************
 *  mutate  Take a string defining a file name and randomly alter characters  *
 *  to generate another, hopefully valid file name.  Assumes that a printf    *
 *  conversion code is present and only alters contiguously alphabetic chars  *
 *  before the percent.							      *
 ******************************************************************************/
#define	N_MUTATE	3			/* Max number of mutated chars*/
static char	*mutate(name)
char	*name;
{
   char	*pc_pos = strchr(name,'%'),
   	*begin  = pc_pos;
   static char	alpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZZ";

   while( begin > name && (pc_pos - begin) < N_MUTATE && isalnum(*(begin-1)) )
      begin--;

   if(begin >= pc_pos)				/* No characters to mutate    */
      return NULL;

   while( begin < pc_pos )
      *begin++ = alpha[(int)(mdrand() * 26.0)];

   return name;
}
/******************************************************************************
 *  dump_convert  format data and place in buffer for dumping to file         *
 *  Quantities dumped depend on bits set in 'control.dump_level' as follows:  *
 *	bit 0:	centre of mass co-ords, quaternions, unit cell matrix.	      *
 *	bit 1:	derivatives of above.					      *.
 *	bit 2:	second derivatives of above.				      *
 *	bit 3:	forces, torques, stress and potential energy.		      *
 *  All quantities are converted to floats (from real, whatever that is) and  *
 *  c_of_m and its derivatives are converted to unscaled co-ordinates.	      *
 ******************************************************************************/
static void	dump_convert(buf, system, force, torque, stress, pe)
float		*buf;
system_mp	system;
vec_mt		force[], torque[];
mat_mt		stress;
double		pe;
{
   int		nmols   = system->nmols,
   		nmols_r = system->nmols_r;
   vec_mt	*scale_buf = ralloc(nmols);
   real		ppe = pe;

   if( control.dump_level & 1)
   {
      mat_vec_mul(system->h, system->c_of_m, scale_buf, nmols);
      real_to_float(scale_buf[0],    buf, 3*nmols);	buf += 3*nmols;
      if( system->nmols_r > 0 )
      {
	 real_to_float(system->quat[0], buf, 4*nmols_r);	
	 buf += 4*nmols_r;
      }
      real_to_float(system->h[0],    buf, 9);		buf += 9;
      real_to_float(&ppe, buf, 1);		buf += 1;
   }
   if( control.dump_level & 2)
   {
      mat_vec_mul(system->h, system->vel, scale_buf, nmols);
      real_to_float(scale_buf[0],    buf, 3*nmols);	buf += 3*nmols;
      if( system->nmols_r > 0 )
      {
	 real_to_float(system->qdot[0], buf, 4*nmols_r);
	 buf += 4*nmols_r;
      }
      real_to_float(system->hdot[0],    buf, 9);	buf += 9;
   }
   if( control.dump_level & 4)
   {
      mat_vec_mul(system->h, system->acc, scale_buf, nmols);
      real_to_float(scale_buf[0],     buf, 3*nmols);	buf += 3*nmols;
      if( system->nmols_r > 0 )
      {
	 real_to_float(system->qddot[0], buf, 4*nmols_r);
	 buf += 4*nmols_r;
      }
      real_to_float(system->hddot[0], buf, 9);		buf += 9;
   }
   if( control.dump_level & 8)
   {
      real_to_float(force[0],  buf, 3*nmols);	buf += 3*nmols;
      if( system->nmols_r > 0 )
      {
	 real_to_float(torque[0], buf, 3*nmols_r);
	 buf += 3*nmols_r;
      }
      real_to_float(stress[0], buf, 9);		buf += 9;
   }
   xfree(scale_buf);
}
/******************************************************************************
 *  real_to_float  Copy data from one array to another, converting from       *
 *  typedef real to type float in the interests of space.  (may be null op'n) *
 ******************************************************************************/
static void	real_to_float(b, a, n)
float	a[];
real	b[];
int	n;
{
   int i;
   for( i=0; i for stellar
 * 
 * Revision 1.5  89/11/01  17:29:10  keith
 * Sin and cos loop vectorised - mat_vec_mul extracted from loop.
 * 'Uniform charge sheet' term added in case of electrically charged system.
 * 
 * Revision 1.5  89/10/26  11:29:26  keith
 * Sin and cos loop vectorised - mat_vec_mul extracted from loop.
 * 'Uniform charge sheet' term added in case of electrically charged layer.
 * 
 * Revision 1.5  89/10/26  11:27:31  keith
 * Sin and cos loop vectorised - mat_vec_mul extracted from loop.
 * 
 * Revision 1.4  89/10/02  11:39:14  keith
 * Fixed error in *star macros which assumed rlv's were cols of h(-1) r.t. rows.
 * 
 * Revision 1.3  89/06/09  13:38:17  keith
 * Older code for computation of q cos/sin k.r restored conditionally by
 * use of macro OLDEWALD.  This is for more primitive vectorising compilers.
 * 
 * Revision 1.2  89/06/08  10:42:51  keith
 * Modified to circumvent compiler bug in VMS/VAXC 2.4-026
 * 
 * Revision 1.1  89/04/20  16:00:39  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/ewald.c,v 2.15 1998/01/27 15:46:45 keith Exp $";
#endif
/*========================== Program include files ===========================*/
#include 	"defs.h"
/*========================== Library include files ===========================*/
#ifdef stellar
#   include 	
#else
#ifdef titan
#   include 	
#else
#   include 	
#endif
#endif
#include	"stddef.h"
#include 	"stdlib.h"
#include        "string.h"
/*========================== Program include files ===========================*/
#include 	"structs.h"
#include 	"messages.h"
/*========================== External function declarations ==================*/
gptr            *talloc();	       /* Interface to memory allocator       */
void            tfree();	       /* Free allocated memory	      	      */
void            afree();	       /* Free allocated array	      	      */
double	err_fn();			/* Error function		      */
double	det();				/* Determinant of 3x3 matrix	      */
void	invert();			/* Inverts a 3x3 matrix		      */
void	mat_vec_mul();			/* Multiplies a 3x3 matrix by 3xN vect*/
void	mat_sca_mul();			/* Multiplies a 3x3 matrix by scalar  */
void	transpose();			/* Transposes a 3x3 matrix	      */
void    zero_real();            	/* Initialiser                        */
void    zero_double();          	/* Initialiser                        */
double	sum();				/* Sum of elements of 'real' vector   */
#if defined(ANSI) || defined(__STDC__)
gptr	*arralloc(size_mt,int,...); 	/* Array allocator		      */
void	note(char *,...);		/* Write a message to the output file */
void	message(int *,...);		/* Write a warning or error message   */
#else
gptr	*arralloc();	        	/* Array allocator		      */
void	note();				/* Write a message to the output file */
void	message();			/* Write a warning or error message   */
#endif
/*========================== External data references ========================*/
extern	contr_mt	control;       	/* Main simulation control record     */
extern int		ithread, nthreads;
/*========================== Macros ==========================================*/
#define astar hinvp[0]
#define bstar hinvp[1]
#define cstar hinvp[2]
#define moda(hmat) (hmat[0][0])
#define modb(hmat) sqrt(SQR(hmat[0][1]) + SQR(hmat[1][1]))
#define modc(hmat) sqrt(SQR(hmat[0][2]) + SQR(hmat[1][2]) + SQR(hmat[2][2]))
/*========================== Cache Parameters=================================*/
/* The default values are for the Cray T3D but are probably good enough 
 *  for most other systems too. */
#ifndef NCACHE
#   define NCACHE (256*sizeof(double)/sizeof(real))
#endif
#ifndef NLINE
#   define NLINE  (4*sizeof(double)/sizeof(real))
#endif
/*============================================================================*/
   struct s_hkl {double kx, ky, kz; int h,k,l;};
/*****************************************************************************
 * qsincos().  Evaluate q sin(k.r) and q cos(k.r).  This is in a separate    *
 * function because some compilers (notably Stellar's) generate MUCH better  *
 * vector code this way. 						     *
 *****************************************************************************/
static
void      qsincos(coshx,sinhx,cosky,sinky,coslz,sinlz,chg,
		  qcoskr,qsinkr,k,l,nsites)
real coshx[], sinhx[], cosky[], sinky[], coslz[], sinlz[],
     chg[], qcoskr[], qsinkr[];
int  k,l,nsites;
{
   int is;
   real qckr;
   
   if( k >= 0 )
      if( l >= 0 )
      {
VECTORIZE
	 for(is = 0; is < nsites; is++)
	 {
	    qckr = chg[is]*(
		  (coshx[is]*cosky[is] - sinhx[is]*sinky[is])*coslz[is] 
                - (sinhx[is]*cosky[is] + coshx[is]*sinky[is])*sinlz[is]);
	    qsinkr[is] = chg[is]*(
                  (sinhx[is]*cosky[is] + coshx[is]*sinky[is])*coslz[is] 
		+ (coshx[is]*cosky[is] - sinhx[is]*sinky[is])*sinlz[is]);
	    qcoskr[is] = qckr;
	 }
      }
      else
      {
VECTORIZE
	 for(is = 0; is < nsites; is++)
	 {
	    qckr = chg[is]*(
		  (coshx[is]*cosky[is] - sinhx[is]*sinky[is])*coslz[is] 
                + (sinhx[is]*cosky[is] + coshx[is]*sinky[is])*sinlz[is]);
	    qsinkr[is] = chg[is]*(
                  (sinhx[is]*cosky[is] + coshx[is]*sinky[is])*coslz[is] 
		- (coshx[is]*cosky[is] - sinhx[is]*sinky[is])*sinlz[is]);
	    qcoskr[is] = qckr;
	 }
      }
   else
      if( l >= 0 )
      {
VECTORIZE
	 for(is = 0; is < nsites; is++)
	 {
	    qckr = chg[is]*(
		  (coshx[is]*cosky[is] + sinhx[is]*sinky[is])*coslz[is] 
                - (sinhx[is]*cosky[is] - coshx[is]*sinky[is])*sinlz[is]);
	    qsinkr[is] = chg[is]*(
                  (sinhx[is]*cosky[is] - coshx[is]*sinky[is])*coslz[is] 
		+ (coshx[is]*cosky[is] + sinhx[is]*sinky[is])*sinlz[is]);
	    qcoskr[is] = qckr;
	 }
      }
      else
      {
VECTORIZE
	 for(is = 0; is < nsites; is++)
	 {
	    qckr = chg[is]*(
		  (coshx[is]*cosky[is] + sinhx[is]*sinky[is])*coslz[is] 
                + (sinhx[is]*cosky[is] - coshx[is]*sinky[is])*sinlz[is]);
	    qsinkr[is] = chg[is]*(
                  (sinhx[is]*cosky[is] - coshx[is]*sinky[is])*coslz[is] 
		- (coshx[is]*cosky[is] + sinhx[is]*sinky[is])*sinlz[is]);
	    qcoskr[is] = qckr;
	 }
     }
}

/******************************************************************************
 * Calculate cos and sin of astar*x, bstar*y & cstar*z for each charged site  *
 * Use addition formulae to get sin(h*astar*x)=sin(Kx*x) etc for each site    *
 ******************************************************************************/
static
void trig_recur(chx,shx,sin1x,cos1x,site0,site1,site2,kstar,hmax,ns0,ns1)
real **chx, **shx, sin1x[],cos1x[], *site0, *site1, *site2, *kstar;
int ns0, ns1, hmax;
{
   int h, is;
   real *coshx, *sinhx, *cm1, *sm1, coss, kr;
   real ksx = kstar[0], ksy = kstar[1], ksz = kstar[2];

   sinhx = shx[0];    coshx = chx[0]; 
VECTORIZE
   for(is = ns0; is < ns1; is++)
   {
      coshx[is] = 1.0;
      sinhx[is] = 0.0;
   }      

   if( hmax >= 1 )
   {
      coshx = chx[1]; sinhx = shx[1];
VECTORIZE
      for(is = ns0; is < ns1; is++)
      {
	 kr = ksx*site0[is]+ksy*site1[is]+ksz*site2[is];
	 coshx[is] = cos(kr); sinhx[is] = sin(kr);
      }

      memcp(cos1x+ns0, coshx+ns0, (ns1-ns0)*sizeof(real));
      memcp(sin1x+ns0, sinhx+ns0, (ns1-ns0)*sizeof(real));

      for(h = 2; h <= hmax; h++)
      {
         coshx = chx[h]; sinhx = shx[h];
         cm1 = chx[h-1]; sm1 = shx[h-1];
VECTORIZE
         for(is = ns0; is < ns1; is++)
      	 {
      	    coss      = cm1[is]*cos1x[is] - sm1[is]*sin1x[is];
      	    sinhx[is] = sm1[is]*cos1x[is] + cm1[is]*sin1x[is];
      	    coshx[is] = coss;
      	 }
      }
   }
}

/******************************************************************************
 * allocate_arrays(). Memory allocation for Ewald's arrays.  We malloc one    *
 * large block and divide it out so as to precisely control the relative      *
 * offsets of the arrays.  This is to avoid cache conflicts.                  *
 * Revert to bog-standard method for MSDOS				      *
 ******************************************************************************/
static real*  allocate_arrays(nsarray, hmax, kmax, lmax,
			      chx, cky, clz, shx, sky, slz, chg,
			      cos1x, cos1y, cos1z, sin1x, sin1y, sin1z, 
			      qcoskr, qsinkr)
int	nsarray, hmax, kmax, lmax;
real	***chx, ***cky, ***clz, ***shx, ***sky, ***slz;
real	**cos1x, **cos1y, **cos1z, **sin1x, **sin1y, **sin1z;
real	**chg, **qcoskr, **qsinkr;
{
   int h, k,l; 
   real *csp, *base;

#ifndef ALLOC_SEPARATELY
   /*
    * Attempt to cache-align these arrays for optimum performance.
    * This requires NON-ANSI pointer conversion and arithmetic.
    * It should work with any UNIX address space, so make it conditional.
    */
   base = dalloc((2*(hmax+kmax+lmax)+15)*nsarray+10*NLINE+NCACHE);
#if defined(ALLOC_ALIGN) && !defined(__BOUNDS_CHECKING_ON)
   csp = (real*)0 + (((base - (real*)0) - 1 | NCACHE-1) + 1);
#else
   csp = base;
#endif

   *chx = (real**)arralloc(sizeof(real*),1,0,hmax);
   *cky = (real**)arralloc(sizeof(real*),1,0,kmax);
   *clz = (real**)arralloc(sizeof(real*),1,0,lmax);
   *shx = (real**)arralloc(sizeof(real*),1,0,hmax);
   *sky = (real**)arralloc(sizeof(real*),1,0,kmax);
   *slz = (real**)arralloc(sizeof(real*),1,0,lmax);
 
   *qcoskr = csp; csp += nsarray + NLINE;
   *qsinkr = csp; csp += nsarray + NLINE;
   *chg = csp;    csp += nsarray + NLINE;
   for(h = 0; h <= hmax; h++, csp += nsarray)
     (*chx)[h] = csp;
   csp += NLINE;
   for(h = 0; h <= hmax; h++, csp += nsarray)
     (*shx)[h] = csp;
   csp += NLINE;
   for(k = 0; k <= kmax; k++, csp += nsarray)
     (*cky)[k] = csp;
   csp += NLINE;
   for(k = 0; k <= kmax; k++, csp += nsarray)
     (*sky)[k] = csp;
   csp += NLINE;
   for(l = 0; l <= lmax; l++, csp += nsarray)
     (*clz)[l] = csp;
   csp += NLINE;
   for(l = 0; l <= lmax; l++, csp += nsarray)
     (*slz)[l] = csp;
   csp += NLINE;
   *cos1x = csp;  csp += nsarray;
   *cos1y = csp;  csp += nsarray;
   *cos1z = csp;  csp += nsarray + NLINE;
   *sin1x = csp;  csp += nsarray;
   *sin1y = csp;  csp += nsarray;
   *sin1z = csp;  csp += nsarray;
#else
   *chx = (real**)arralloc((size_mt)sizeof(real),2, 0, hmax, 0, nsarray-1);
   *cky = (real**)arralloc((size_mt)sizeof(real),2, 0, kmax, 0, nsarray-1);
   *clz = (real**)arralloc((size_mt)sizeof(real),2, 0, lmax, 0, nsarray-1);
   *shx = (real**)arralloc((size_mt)sizeof(real),2, 0, hmax, 0, nsarray-1);
   *sky = (real**)arralloc((size_mt)sizeof(real),2, 0, kmax, 0, nsarray-1);
   *slz = (real**)arralloc((size_mt)sizeof(real),2, 0, lmax, 0, nsarray-1);
   *cos1x = (*chx)[1]; *cos1y = (*cky)[1]; *cos1z = (*clz)[1];
   *sin1x = (*shx)[1]; *sin1y = (*sky)[1]; *sin1z = (*slz)[1];

   csp = base = dalloc(3*nsarray);
   *chg = csp;    csp += nsarray;
   *qcoskr = csp; csp += nsarray;
   *qsinkr = csp; csp += nsarray;
#endif

   return base;
}
/******************************************************************************
 *  Ewald  Calculate reciprocal-space part of coulombic forces		      *
 ******************************************************************************/
void	ewald(site,site_force,system,species,chgx,pe,stress)
real		**site,			/* Site co-ordinate arrays	 (in) */
		**site_force;		/* Site force arrays		(out) */
system_mp	system;			/* System record		 (in) */
spec_mt	species[];			/* Array of species records	 (in) */
real		chgx[];			/* Array of site charges	 (in) */
double		*pe;			/* Potential energy		(out) */
mat_mt		stress;			/* Stress virial		(out) */
{
   mat_mt	hinvp;			/* Matrix of reciprocal lattice vects*/
   int		h, k, l;		/* Recip. lattice vector indices     */
   int		i, j, is, ssite;	/* Counters.			     */
   spec_mp	spec;			/* species[ispec]		     */
   int		nsites = system->nsites;
   double	pe_k,			/* Pot'l energy for current K vector */
		coeff, coeff2;		/* 2/(e0V) * A(K) & similar	     */
   double	r_4_alpha = -1.0/(4.0 * control.alpha * control.alpha);
   double	sqcoskr,sqsinkr,	/* Sum q(i) sin/cos(K.r(i))          */
		sqcoskrn, sqsinkrn,
		sqcoskrf, sqsinkrf;
   double	ksq,			/* Squared magnitude of K vector     */
		kcsq = SQR(control.k_cutoff);
   double	kx,ky,kz,kzt;
   vec_mt	kv;			/* (Kx,Ky,Kz)  			     */
   real		force_comp, kv0, kv1, kv2, sfx, sfy;
   struct	s_hkl *hkl, *phkl;
   int		nhkl = 0;
/*
 * Maximum values of h, k, l  s.t. |k| < k_cutoff
 */
   int		hmax = floor(control.k_cutoff/(2*PI)*moda(system->h)),
		kmax = floor(control.k_cutoff/(2*PI)*modb(system->h)),
		lmax = floor(control.k_cutoff/(2*PI)*modc(system->h));
/*
 * lower and upper limits for parallel loops.   
 */
#if defined(SPMD) && defined(MPPMANY)
   int  nsnode = (nsites+nthreads-1)/nthreads;
   int  nsarr0 = nsnode * nthreads;
   int  ns0 = MIN(nsites, nsnode * ithread), 
        ns1 = MIN(nsites, nsnode * (ithread+1));
#else	    
   int		ns0 = 0, ns1 = nsites;
   int		nsarr0 = nsites;
#endif
/*
 * Round up size of arrays to cache sub-multiple size.
 */
#if 1
   int		nsarray = (nsarr0 - 1 | NCACHE - 1) + 1;
#else
   int		nsarray = nsarr0;
#endif
/*
 * Arrays for cos & sin (h x(i)), (k y(i)) and (l z(i)) eg chx[h][isite]
 * and pointers to a particular h,k or l eg coshx[is] = chh[2][is]
 */
   real		**chx, **cky, **clz, **shx, **sky, **slz;
   real		*cshkl;
   real		*coshx, *cosky, *coslz, *sinhx, *sinky, *sinlz;
   real		*cos1x, *cos1y, *cos1z, *sin1x, *sin1y, *sin1z;
   real		*chg;
   real		*site_fx = site_force[0],
   		*site_fy = site_force[1],
   		*site_fz = site_force[2];
   real		*qcoskr,		/* q(i) cos(K.R(i))	      */
		*qsinkr;		/* q(i) sin(K.R(i))	      */
   double	vol = det(system->h);	/* Volume of MD cell		      */
   static	double	self_energy,	/* Constant self energy term	      */
   			sheet_energy;	/* Correction for non-neutral system. */
   static	boolean init = true;	/* Flag for the first call of function*/
   static       int     nsitesxf;       /* Number of non-framework sites.     */
/*
 * Trig array set-up.  The precise storage layout is to avoid cache conflicts.
 */
   cshkl = allocate_arrays(nsarray, hmax, kmax, lmax,
			   &chx, &cky, &clz, &shx, &sky, &slz, &chg,
			   &cos1x, &cos1y, &cos1z, &sin1x, &sin1y, &sin1z, 
			   &qcoskr, &qsinkr);
   memcp(chg, chgx, nsites*lsizeof(real));
/*
 * First call only - evaluate self energy term and store for subsequent calls
 * Self energy includes terms for non-framework sites only.
 */
   if(init)
   {
      double	sqsq = 0, sq = 0, sqxf, intra, r;
      int	js, frame_flag;

      self_energy = sheet_energy = 0;
      ssite = 0;
      spec = species; 
      while( spec < species+system->nspecies && ! spec->framework)
      {
	 intra = 0.0;
	 for(is = 0; is < spec->nsites; is++)
	    for(js = is+1; js < spec->nsites; js++)
	    {
	       r = DISTANCE(spec->p_f_sites[is], spec->p_f_sites[js]);
	       intra += chg[ssite+is] * chg[ssite+js]
		       *err_fn(control.alpha * r) / r;
	    }
	 self_energy += spec->nmols * intra;
	 ssite += spec->nsites*spec->nmols;
	 spec++;
      }
      nsitesxf = ssite;
      frame_flag = (spec != species+system->nspecies);

      for(is = 0; is < nsitesxf; is++)
      {
	 sq += chg[is];
	 sqsq += SQR(chg[is]);
      }
      self_energy += control.alpha / sqrt(PI) * sqsq;
      /*
       * Sqxf is total non-framework charge.  Calculate grand total in sq.
       */
      sqxf = sq;
      for(; is < nsites; is++)
	 sq += chg[is];
      /*
       *  Charged-system/uniform sheet correction terms (really in direct
       *  space term but included here for convenience).
       *  1) For charged framework only.
       */
      if( frame_flag )
      {
	 sheet_energy = PI*SQR(sq-sqxf) / (2.0*SQR(control.alpha));
	 message(NULLI, NULLP, INFO, FRACHG, 
		 (sq-sqxf)*CONV_Q, sheet_energy/vol*CONV_E);
      }
      /* 
       *  2) Case of entire system non-neutral.
       */
      if( fabs(sq)*CONV_Q > 1.0e-5)
      {
	 sheet_energy -= intra = PI*SQR(sq) / (2.0*SQR(control.alpha));
	 message(NULLI, NULLP, WARNING, SYSCHG, sq*CONV_Q, intra/vol*CONV_E);
      }

      note("Ewald self-energy = %f kJ/mol",self_energy*CONV_E);
   }

   if( ithread == 0 )
   {
      *pe -= self_energy;		/* Subtract self energy term	      */
      *pe += sheet_energy/vol;		/* Uniform charge correction	      */
      for(i=0; i<3; i++)
	 stress[i][i] += sheet_energy/vol;
   }
   
   invert(system->h, hinvp);		/* Inverse of h is matrix of r.l.v.'s */
   mat_sca_mul(2*PI, hinvp, hinvp);
   /*
    * Build array hkl[] of k vectors within cutoff. 
    * N. B. This code presupposes upper-triangular H matrix.
    */
   hkl = aalloc(4*(hmax+1)*(kmax+1)*(lmax+1), struct s_hkl);
   for(h = 0; h <= hmax; h++)
      for(k = (h==0 ? 0 : -kmax); k <= kmax; k++)
      {
	 kx = h*astar[0] + k*bstar[0];
	 ky = h*astar[1] + k*bstar[1];
	 kzt = h*astar[2] + k*bstar[2];
	 ksq = SQR(kx) + SQR(ky);
	 for(l = (h==0 && k==0 ? 1 : -lmax); l <= lmax; l++)
	 {
	    kz = kzt + l*cstar[2];
	    if( SQR(kz)+ksq < kcsq )
	    {
	       hkl[nhkl].h = h; hkl[nhkl].k = k; hkl[nhkl].l = l;
	       hkl[nhkl].kx = kx;
	       hkl[nhkl].ky = ky;
	       hkl[nhkl].kz = kz;
	       nhkl++;
	    }
	 }
      }
   if( init )
      note("%d K-vectors included in reciprocal-space sum",nhkl);
   init = false;
/*
 * Calculate cos and sin of astar*x, bstar*y & cstar*z for each charged site
 * Use addition formulae to get sin(h*astar*x)=sin(Kx*x) etc for each site
 */
   trig_recur(chx,shx,sin1x,cos1x,site[0],site[1],site[2],astar,hmax,ns0,ns1);
   trig_recur(cky,sky,sin1y,cos1y,site[0],site[1],site[2],bstar,kmax,ns0,ns1);
   trig_recur(clz,slz,sin1z,cos1z,site[0],site[1],site[2],cstar,lmax,ns0,ns1);

#if defined(SPMD) && defined(MPPMANY)
   par_collect_all(chx[0]+ns0, chx[0], nsnode, nsarray, hmax+1);
   par_collect_all(shx[0]+ns0, shx[0], nsnode, nsarray, hmax+1);
   par_collect_all(cky[0]+ns0, cky[0], nsnode, nsarray, kmax+1);
   par_collect_all(sky[0]+ns0, sky[0], nsnode, nsarray, kmax+1);
   par_collect_all(clz[0]+ns0, clz[0], nsnode, nsarray, lmax+1);
   par_collect_all(slz[0]+ns0, slz[0], nsnode, nsarray, lmax+1);
#endif
/*
 * Start of main loops over K vector indices h, k, l between -*max, *max etc.
 * To avoid calculating K and -K, only half of the K-space box is covered. 
 * Points on the axes are included once and only once. (0,0,0) is omitted.
 */
   for(phkl = hkl+ithread; phkl < hkl+nhkl; phkl += nthreads)
   {
/*
 * Calculate actual K vector and its squared magnitude.
 */
      h  = phkl->h;	    k     = phkl->k;  l     = phkl->l;
      kv0 = kv[0] = phkl->kx; 
      kv1 = kv[1] = phkl->ky; 
      kv2 = kv[2] = phkl->kz;

      ksq = SUMSQ(kv);

/*
 * Calculate pre-factors A(K) etc
 */
      coeff  = 2.0 / (EPS0 * vol) * exp(ksq * r_4_alpha) / ksq;
      coeff2 = 2.0 * (1.0 - ksq * r_4_alpha) / ksq;
      
/*
 * Set pointers to array of cos (h*astar*x) (for efficiency & vectorisation)
 */
      coshx = chx[h];  cosky = cky[abs(k)]; coslz = clz[abs(l)];
      sinhx = shx[h];  sinky = sky[abs(k)]; sinlz = slz[abs(l)];

/*
 * Calculate q(i)*cos/sin(K.R(i)) by addition formulae. Note handling of
 * negative k and l by using sin(-x) = -sin(x), cos(-x) = cos(x). For
 * efficiency & vectorisation there is a loop for each case.
 */
      qsincos(coshx,sinhx,cosky,sinky,coslz,sinlz,chg,
	      qcoskr,qsinkr,k,l,nsites);
      sqcoskrn = sum(nsitesxf, qcoskr, 1);
      sqsinkrn = sum(nsitesxf, qsinkr, 1);
      sqcoskrf = sum(nsites-nsitesxf, qcoskr+nsitesxf, 1);
      sqsinkrf = sum(nsites-nsitesxf, qsinkr+nsitesxf, 1);
      sqcoskr = sqcoskrn + sqcoskrf;
      sqsinkr = sqsinkrn + sqsinkrf;
      
/*
 * Evaluate potential energy contribution for this K and add to total.
 * Exclude frame-frame interaction terms.
 */
      pe_k = 0.5 * coeff * (sqcoskrn*(sqcoskrn+sqcoskrf+sqcoskrf) +
			    sqsinkrn*(sqsinkrn+sqsinkrf+sqsinkrf));
      *pe += pe_k;

      sqsinkr *= coeff; sqcoskr *= coeff;
      sqsinkrn *= coeff; sqcoskrn *= coeff;
/*
 * Calculate long-range coulombic contribution to stress tensor
 */
NOVECTOR
      for(i = 0; i < 3; i++)
      {
	 stress[i][i] += pe_k;
NOVECTOR
	 for(j = i; j < 3; j++)
	    stress[i][j] -= pe_k * coeff2 * kv[i] * kv[j];
      }
/*
 * Evaluation of site forces.   Non-framework sites interact with all others
 */
VECTORIZE
      for(is = 0; is < nsitesxf; is++)
      {
	 force_comp = qsinkr[is]*sqcoskr - qcoskr[is]*sqsinkr;
	 sfx =  site_fx[is] + kv0 * force_comp;
	 sfy =  site_fy[is] + kv1 * force_comp;
	 site_fz[is] +=       kv2 * force_comp;
	 site_fx[is] = sfx;
	 site_fy[is] = sfy;
      }
/*
 *  Framework sites -- only interact with non-framework sites
 */
VECTORIZE
      for(is = nsitesxf; is < nsites; is++)
      {
	 force_comp = qsinkr[is]*sqcoskrn - qcoskr[is]*sqsinkrn;
	 sfx =  site_fx[is] + kv0 * force_comp;
	 sfy =  site_fy[is] + kv1 * force_comp;
	 site_fz[is] +=       kv2 * force_comp;
	 site_fx[is] = sfx;
	 site_fy[is] = sfy;
      }
/*
 * End of loop over K vectors.
 */
   }
   afree((gptr*)chx); afree((gptr*)cky); afree((gptr*)clz); 
   afree((gptr*)shx); afree((gptr*)sky); afree((gptr*)slz);
   xfree(cshkl);
   xfree(hkl);
}
$EOD
$!
$CREATE force.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * Force        This module contains functions to implement the 'link cell'   *
 *              interatomic force calculation (Hockney, R.W. & Eastwood J.W.  *
 *              "Computer Simulation Using Particles" McGraw-Hill (1981), 277)*
 *              It is vectorised and optimised for a CRAY XMP and Convex C1,  *
 *              but should run well on any vector machine with reasonably     *
 *              efficient scatter/gather. (And of course on scalar machines!) *
 *              The actual calculation of the potential is in a different     *
 *              module (kernel.c) for ease of modification.                   *
 ******************************************************************************
 *       $Log: force.c,v $
 *       Revision 2.16  1998/01/27 15:47:15  keith
 *       tidied up macro __STDC__ to include ANSI for consistency.
 *
 *       Revision 2.15  1997/11/26 10:22:27  keith
 *       Reordered declarations so that local structs come before
 *       function declarations. Otherwise "protoize" broke!
 *
 *       Revision 2.14  1996/11/04 17:34:30  keith
 *       Moderate rewriting and code re-organization.
 *       1. Simplified PBC relocation calculation, got rid of large
 *          arrays reloc[] etc, saving 32 MB for 8192 waters on T3D.
 *          There is now NO LIMIT to cutoff or RDF limit and macro
 *          parameter NSH is removed.
 *       2. Rewrote site_neighbour_list to be more transparent and got
 *          rid of silly vector sort calls. There are now separate versions for
 *          scalar and vector machines.  Code is now optimized for usual non-
 *          framework case, but it's faster than 2.10 even for frameworks.
 *       3. Corrected misleading comments, reorganized code in force_calc()
 *          to be more transparent.  Commented local variables MUCH better.
 *       4. It's also a bit faster.
 *
 *       Revision 2.13  1996/08/14 16:46:04  keith
 *       Workaround for T3D Cray compiler bug real*int/int ==> int division.
 *       Got rid of unnccessary par_abort() calls - rplaced with exit().
 *       (message on thread 0 calls par_abort()).
 *
 *       Revision 2.12  1996/05/03 16:14:20  keith
 *       Fixed bug whereby reloc could overflow in strict cutoff mode by
 *       tightening up test condition. Also fixed the calculation of
 *       n_nab_sites to reflect the increased cutoff in strict mode.
 *
 *       Revision 2.11  1996/01/17 17:12:47  keith
 *       Incorporated rdf accumulation into forces and parallelized.
 *       New functions rdf_inner(), calls rdf_accum() from rdf.c
 *
 * Revision 1.8.1.8  89/11/01  17:34:15  keith
 * Modified to use SPAXPY vectorised scattered add.
 * 
 * Revision 1.8.1.6  89/10/12  16:28:39  keith
 * Added conditional code to produce histogram of interaction distances
 * and calculate 'minimum image' energy.
 * Added preprocessor constant NSH to fix size of relocation arrays.
 * Fixed mistake in metric G in neighbour_list() (G=h'h not hh').
 * 
 * Revision 1.8.1.5  89/10/02  17:12:59  keith
 * New version of neighbour_list() which works for arbitrary cutoff radii.
 * site_neighbour_list checks for overflow.
 * Main loop limits modified, (in conjunction with mods in site_neighbour_list)
 * to correctly include all molecule-framework interactions.
 * 
 * Revision 1.8.1.4  89/09/12  16:14:41  keith
 * Fixed bug in fill_cells() which didn't increment spec properly in imol loop
 * 
 * Revision 1.8.1.3  89/08/31  11:58:04  keith
 * Fixed bug in 'BIN' macro to correctly handle case of rc<0.
 * 
 * Revision 1.8.1.2  89/08/30  17:00:33  keith
 * Fixed memory overlap bug in site_neighbour_list
 * 
 * Revision 1.8.1.1  89/08/25  15:24:43  keith
 * Mods to add framework structures to simulation model
 * 
 * Revision 1.7  89/08/22  14:48:39  keith
 * Created new variable 'n_nab_sites' for max size of vector arrays.
 * 
 * Revision 1.6  89/07/04  18:43:14  keith
 * Fixed error in kernel and force which led to sites being allocated the
 * wrong potential parameters.  Needed extra parameter to kernel.
 * 
 * Revision 1.5  89/06/22  15:44:23  keith
 * Tidied up loops over species to use one pointer as counter.
 * 
 * Revision 1.4  89/06/14  14:18:49  keith
 * Fixed #ifdef s for CRAY to handle case of UNICOS
 * Fix mistake in VCALLS conditional code.
 * 
 * Revision 1.3  89/06/01  18:01:46  keith
 * Moved `vadd()' from aux.c to force.c for ease of vectorisation.
 * Now no need to compile aux.c with vectorisation.
 * 
 * Revision 1.2  89/05/17  13:53:49  keith
 * Reorganised neighbour list construction in preparation for framework.
 * (Also goes slighty faster)
 * 
 * Revision 1.1  89/04/20  16:00:40  keith
 * Initial revision
 * 
 * Revision 1.3  90/03/29  15:44:51  keith
 * Merged force.c revisions 1.8.1.11-1.8.1.13
 * 
 * Revision 1.2  90/03/09  17:30:29  keith
 * Modified FKERNEL ifdefs for UNICOS.
 * 
 * Revision 1.1  90/01/31  13:19:28  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/force.c,v 2.16 1998/01/27 15:47:15 keith Exp $";
#endif
/*========================== Program include files ===========================*/
#include        "defs.h"
/*========================== Library include files ===========================*/
#ifdef  stellar
#include        
#else
#include        
#endif
#include        "stddef.h"
#include        "string.h"
#include        
/*========================== Program include files ===========================*/
#include        "structs.h"
#include        "messages.h"
/*========================== Structs local to module =========================*/
typedef struct cell_s                   /* Prototype element of linked list of*/
{                                       /* molecules within interaction range */
   int          isite, num, frame_type;
   struct cell_s *next;
}               cell_mt;

typedef struct                          /* Prototype of neighbour cell list   */
{                                       /* element.                           */
   int          i, j, k;
}               ivec_mt;

typedef struct                          /* Prototype of neighbour cell list   */
{                                       /* element.                           */
   real         i, j, k;
}               rvec_mt;

typedef struct                          /* Prototype of neighbour cell list   */
{                                       /* element.                           */
   real         x, y, z;
   int          i, j, k;
}               irvec_mt;
/*========================== External function declarations ==================*/
gptr            *talloc();             /* Interface to memory allocator       */
void            tfree();               /* Free allocated memory               */
void            afree();               /* Free allocated array                */
int             search_lt();            /* Search a vector for el. < scalar   */
void            gather();               /* Interface to CRAY gather routine   */
void            mat_mul();              /* Matrix multiplier                  */
double          det();                  /* Determinant of 3x3 matrix          */
void            invert();               /* 3x3 matrix inverter                */
void            mat_vec_mul();          /* Matrix by vector multiplier        */
void            transpose();            /* Generate 3x3 matrix transpose      */
void            zero_real();            /* Initialiser                        */
void            force_inner();          /* Inner loop forward reference       */
void            rdf_inner();            /* RDF calc forward reference         */
double          precision();            /* Floating pt precision.             */
void            kernel();               /* Force kernel routine               */
double          mol_radius();           /* Radius of largest molecule.        */
void            rdf_accum();            /* Bin distances for rdf evaluation.  */
#if defined(ANSI) || defined(__STDC__)
gptr            *arralloc(size_mt,int,...); /* Array allocator                */
void            note(char *, ...);      /* Write a message to the output file */
void            message(int *, ...);    /* Write a warning or error message   */
#else
gptr            *arralloc();            /* Array allocator                    */
void            note();                 /* Write a message to the output file */
void            message();              /* Write a warning or error message   */
#endif
/*========================== External data references ========================*/
extern  contr_mt control;                   /* Main simulation control parms. */
extern int              ithread, nthreads;
/*========================== Global variables ================================*/
static irvec_mt *ifloor; /*Lookup tables for int "floor()"    */
/*========================== Macros ==========================================*/
/*
 * Multiplication factor for size of neighbour list arrays.  If you need
 * to increase this from 1, your system must be *highly* inhomogeneous
 * and may not make sense!
 */
#define         NMULT 3.0
#define         TOO_CLOSE       0.25    /* Error signalled if r**2 < this     */
#define         NCELL(ix,iy,iz) ((iz)+(nz)*((iy)+(ny)*(ix)))
#define         LOCATE(r,eps)   NCELL(cellbin(r[0], nx, eps), \
                                      cellbin(r[1], ny, eps), \
                                      cellbin(r[2], nz, eps))
#define         BIGINT 32768
#define         IFLOOR(i,n)     ((i+BIGINT*n)/n-BIGINT)
#define moda(hmat) sqrt(SQR(hmat[0][0]) + SQR(hmat[1][0]) + SQR(hmat[2][0]))
#define modb(hmat) sqrt(SQR(hmat[0][1]) + SQR(hmat[1][1]) + SQR(hmat[2][1]))
#define modc(hmat) sqrt(SQR(hmat[0][2]) + SQR(hmat[1][2]) + SQR(hmat[2][2]))
#ifdef __BOUNDS_CHECKING_ON /* The declaration of "array" potp has a lower */
#   define P0 0             /* bound of 1.  This is a violation of strict  */
#else                       /* ANSI, so turn it off when bounds checking.  */
#   define P0 1
#endif
/*============================================================================*/
/******************************************************************************
 * spxpy    Sparse add for force.c.  N.B.  MUST NOT BE VECTORIZED as ix may   *
 *          contain duplicate entries.  This occurs if a site interacts with  *
 *          more than one periodic copy of another site.                      *
 ******************************************************************************/
static void spxpy(n, sx, sy, ix)
int     n, ix[];
real    sx[], sy[];
{
   int i;
NOVECTOR
#if defined(ANSI) || defined(__STDC__)
#pragma novector
#endif
   for( i = 0; i < n; i++)
   {
      sy[ix[i]] += sx[i];
   }
}

/******************************************************************************
 *  cellbin.    Safe binning function for putting molecules/sites into cells. *
 *  Any error at the boundaries is disasterous and hard to detect.            *
 *  Results may depend on machine-dependant rounding etc.                     *
 ******************************************************************************/
int cellbin(rc, nc, eps)
double rc, eps;
int nc;
{
   int ibin;

   if(rc < -0.5+eps || rc >= 0.5-eps)
   {
      if(rc < -0.5+eps && rc >= -0.5-eps)
         rc = -0.5;
      else if(rc >= 0.5-eps && rc <= 0.5+eps)
         rc = 0.5-eps;
      else 
         message(NULLI, NULLP, ERROR, 
                 "Co-ordinate out of range in BIN (fill_cells) %.17g\n",rc);
   }
   if( (ibin = ((rc+0.5)*nc)) >= nc || ibin < 0)
         message(NULLI, NULLP, ERROR, 
                 "Rounding problem in BIN (fill_cells) %.17g\n",rc);
   return ibin;
}

/******************************************************************************
 ******************************************************************************/
#ifdef DEBUG2
#define NBINS 200
static int bins[NBINS];

hist(jmin, jmax, rr)
int jmin, jmax;
real rr[];
{
   double rbin = 10.0;
   int j;

   for(j = jmin; j < jmax; j++)
      if(rr[j] < SQR(NBINS/rbin))
         bins[(int)(rbin*sqrt(rr[j]))]++;
}
void histout()
{
   int j;
   for(j = 0; j < NBINS; j++)
   {
      printf("%d%c",bins[j],(j+1)%10?' ':'\n');
      bins[j] = 0;
   }
}
#endif
/******************************************************************************
 *  Neighbour_list.  Build the list of cells within cutoff radius of cell 0   *
 ******************************************************************************/
static ivec_mt   *neighbour_list(nnabor, h, cutoff, nx, ny, nz, icheck)
int     *nnabor;
mat_mt  h;
double  cutoff;
int     nx, ny, nz;
int     icheck;
{
   double               dist;
   int                  i, j, ix, iy, iz, mx, my, mz, inabor = 0, nnab;
   static int           onabor=0;
   ivec_mt              *nabor;
   vec_mt               s;
   mat_mt               G, htr, htrinv;

   transpose(h, htr);
   mat_mul(htr, h, G);
   invert(htr, htrinv);

   mx = ceil(cutoff*nx*moda(htrinv));
   my = ceil(cutoff*ny*modb(htrinv));
   mz = ceil(cutoff*nz*modc(htrinv));

   nnab = 4*mx*my*mz;
   nabor = aalloc(nnab, ivec_mt);
#ifdef DEBUG1
   printf("  Distance    ix    iy    iz      sx        sy          sz\n");
#endif
   for(ix = 0; ix < mx; ix++)
      for(iy = (ix == 0 ? 0 : -my); iy < my; iy++)
         for(iz = (ix == 0 && iy == 0 ? 0 : -mz); iz < mz; iz++)
         {
            s[0] = (double)ix/nx;
            s[1] = (double)iy/ny;
            s[2] = (double)iz/nz;
            dist = 0.0;
            for(i = 0; i < 3; i++)
               for(j = 0; j < 3; j++)
                  dist += s[i]*G[i][j]*s[j];
            if(dist < SQR(cutoff))
            {
               if( inabor >= nnab )
                  message(NULLI, NULLP, FATAL,
                          "Internal error in neighbour_list()");
               nabor[inabor].i = ix;
               nabor[inabor].j = iy;
               nabor[inabor].k = iz;
               inabor++;
#ifdef DEBUG1
               printf("%12f %4d %4d %4d %12f %12f %12f\n",
                      dist,ix,iy,iz,s[0],s[1],s[2]);
#endif
            }
         }
   if( icheck )
   {
      if( inabor != onabor )
         note(NABORS,2 * inabor);
      onabor = inabor;
   }
   *nnabor = inabor;
   return(nabor);
}
/******************************************************************************
 *  Strict_Neighbour_list.  Build the list of cells within cutoff radius      *
 *  This is the strict version and includes every cell which has an interior  *
 *  point at a distance less than the cutoff from any interior point of the   *
 *  reference cell.  In fact the distance criterion is cutoff+2*(maximum mol- *
 *  ecular radius).  This ensures that all *sites* which might be closer to-  *
 *  gether than the cutoff are included.                                      *
 *     The method used is based on the fact that the closest interior points  *
 *  of a pair of parallelopiped cells are either at corners of both cells or  *
 *  at the ends of a line perpendicular to the faces of both parallelopipeds. *
 *  The face-face distance is always the shortest, if the perpendicular       *
 *  projection of the faces onto a common plane intersect with each other     *
 *  The goal is therefore to build a list containing all cells which have a   *
 *  corner-corner or face-face distance to the reference cell which is less   *
 *  than the cutoff.                                                          *
 *  Cells may be admitted to the list by multiple corner-corner or face-face  *
 *  contact criteria but must only be recorded in the final list once.  The   *
 *  easiest way to do this is to use a "map" of all the cells potentially     *
 *  within the cutoff radius and to flag occupancy.                           *
 *  It is easier to loop over all grid vectors within the cutoff and assign   *
 *  cells which have that vector as some corner-corner vector with the        *
 *  reference cell, rather than to loop over cells and calculate all corner   *
 *  pair distances.  This method calculates each distance only once instead   *
 *  of 27 times (the number of distinct corner-corner vectors between 2       *
 *  cells).                                                                   *
 *  To exploit Newton's third law the list should contain only the positive   *
 *  hemisphere (in the x direction).                                          *
 *                                                                            *
 *  The algorithm is as follows.                                              *
 *  1) Set up an empty "map"                                                  *
 *  2) Loop over all points on a grid with points at the link-cell corners    * 
 *     choose only points which are closer to the origin than the cutoff.     *
 *     Set the occupancy flag for all cells which have that as a corner-pair  *
 *     vector to the reference cell.   This is a 3x3x3 block of cells centred *
 *     on the cell whose index is the same as the gridpoint being considered. *
 *     Because we only want the "positive x" cells 2x3x3 will suffice.        *
 *  3) Add cells which have a perpendicular face-face separation within the   *
 *     cutoff.  Only the outermost cells need be considered since inner ones  *
 *     are already admitted by corner-pair distance.  Thus                    *
 *     3a) project the facing corner points of the reference cell onto the    *
 *         plane just within the cutoff.                                      *
 *     3b) add the cells with faces which overlap the projection.  Zero, two  *
 *         or four cells are added depending on whether the projected points  *
 *         coincide with the corner points, the edges or none of the faces.   *
 *  4) The final list is built by scanning the map.                           *
 ******************************************************************************/
static ivec_mt   *strict_neighbour_list(nnabor, h, cutoff, nx, ny, nz, icheck)
int     *nnabor;
mat_mt  h;
double  cutoff;
int     nx, ny, nz;
int     icheck;
{
   double               dist;
   int                  i, j, k, ix, iy, iz, mx, my, mz, inabor = 0, nnab;
   static int           onabor=0;
   int                  ***cellmap;
   ivec_mt              *nabor;
   vec_mt               s;
   mat_mt               G, htr, htrinv;
   int                  face_cells[4][3],mxyz[3], nxyz[3], ixyz, jxyz, kxyz;
   double               proj[3], modabc;

   transpose(h, htr);
   mat_mul(htr, h, G);
   invert(htr, htrinv);

   mx = ceil(cutoff*nx*moda(htrinv));
   my = ceil(cutoff*ny*modb(htrinv));
   mz = ceil(cutoff*nz*modc(htrinv));

   /*
    * Allocate and clear array for map of cells
    */
   nnab = 4*(mx+1)*(my+1)*(mz+1);
   cellmap = (int***)arralloc((size_mt)sizeof ***cellmap, 3, 
                              0, mx, -my-1, my, -mz-1, mz);
   memst(cellmap[0][-my-1]-mz-1,0, nnab*sizeof ***cellmap);

   /*
    * Add cells with corner-pair distances < cutoff
    */
#ifdef DEBUG1
   printf("  Distance    ix    iy    iz      sx        sy          sz\n");
#endif
   for(ix = 0; ix < mx; ix++)
      for(iy = (ix == 0 ? 0 : -my); iy < my; iy++)
         for(iz = (ix == 0 && iy == 0 ? 0 : -mz); iz < mz; iz++)
         {
            s[0] = (double)ix/nx;
            s[1] = (double)iy/ny;
            s[2] = (double)iz/nz;
            dist = 0.0;
            for(i = 0; i < 3; i++)
               for(j = 0; j < 3; j++)
                  dist += s[i]*G[i][j]*s[j];
            if(dist < SQR(cutoff))
            {
               for(i=0; i<=1; i++)
                  for(j = -1; j <= 1; j++)
                     for(k = -1; k <= 1; k++)
                        cellmap[ix+i][iy+j][iz+k] = 1;

#ifdef DEBUG1
               printf("%12f %4d %4d %4d %12f %12f %12f\n",
                      dist,ix,iy,iz,s[0],s[1],s[2]);
#endif
            }
         }
   /*
    * Add cells with face-face distance < cutoff.  Cells along x,y,z axes
    * are added in +/- directions, but only +ve ix indices added to map.
    */
   nxyz[0] = nx; nxyz[1] = ny; nxyz[2] = nz;
   mxyz[0] = mx; mxyz[1] = my; mxyz[2] = mz;
   for( ixyz=0; ixyz < 3; ixyz++)       /* Loop over directions */
   {
      jxyz = (ixyz+1) % 3; kxyz = (jxyz+1) % 3;
      proj[0] = proj[1] = proj[2] = 0.0;
      modabc = 0.0;
      for( i=0; i<3; i++ )
      {
         modabc += htrinv[i][ixyz];
         proj[i] += htrinv[i][ixyz]*htrinv[i][(ixyz+i) % 3];
      }
      for( i=0; i<3; i++ )
         proj[i] *= (mxyz[ixyz]-1)*nxyz[i]/(nxyz[ixyz] * modabc);
      /*
       * proj now contains projection vector.  Construct 4 candidate
       * cells.
       */
      for( i=0; i<3; i++ )
         face_cells[0][i] = face_cells[1][i] = face_cells[2][i] = 
            face_cells[3][i] = floor(proj[i]);
   
      face_cells[0][ixyz] = face_cells[1][ixyz] = face_cells[2][ixyz] = 
         face_cells[3][ixyz] = mxyz[ixyz];

      face_cells[1][jxyz] = face_cells[3][jxyz] = ceil(proj[jxyz]);
      face_cells[2][kxyz] = face_cells[3][kxyz] = ceil(proj[kxyz]);
      /*
       *  Now make sure we add only cells with +ve x index.  Add the
       *  inverse cell if ix<0.
       */
      for( i=0; i < 4; i++)
         if( face_cells[i][0] < 0 )
            for( j=0; j<3; j++ )
               face_cells[i][j] = -face_cells[i][j];
      /*
       * Now add the cells to the map.
       */
      for(  i=0; i < 4; i++ )
      {
         cellmap[face_cells[i][0]][face_cells[i][1]][face_cells[i][2]] = 1;
#ifdef DEBUG1
               printf("%12f %4d %4d %4d %12f %12f %12f\n",
                      0.5,face_cells[i][0],face_cells[i][1],face_cells[i][2],
                      0.0,0.0,0.0);
#endif
      }
   }
   /*
    * Scan map and build list.  N.B.  Loop indices are 1 greater than when
    * list built since we added cells outside original loop limits.
    */   
   nabor = aalloc(nnab, ivec_mt);
   for(ix = 0; ix <= mx; ix++)
      for(iy = (ix == 0 ? 0 : -my-1); iy <= my; iy++)
         for(iz = (ix == 0 && iy == 0 ? 0 : -mz-1); iz <= mz; iz++)
         {
            if( cellmap[ix][iy][iz] )
            {
               if( inabor >= nnab )
                  message(NULLI, NULLP, FATAL,
                          "Internal error in neighbour_list()");
               nabor[inabor].i = ix;
               nabor[inabor].j = iy;
               nabor[inabor].k = iz;
               inabor++;
#ifdef DEBUG1
               printf("%12f %4d %4d %4d %12f %12f %12f\n",
                      1.0,ix,iy,iz,0.0,0.0,0.0);
#endif
            }
         }

   if( icheck )
   {
      if( inabor != onabor )
         note(NABORS,2 * inabor);
      onabor = inabor;
   }
   *nnabor = inabor;
   afree((gptr*)cellmap);
   return(nabor);
}
/******************************************************************************
 *  Fill_cells.  Allocate all the sites to cells depending on their centre of *
 *  mass co-ordinate by binning.                                              *
 ******************************************************************************/
static void    fill_cells(c_of_m, nmols, site, species, h, nx, ny, nz, 
                          lst, cell, frame_type)
vec_mt  c_of_m[];                       /* Centre of mass co-ords        (in) */
int     nmols;                          /* Number of molecules           (in) */
real    **site;                         /* Atomic site co-ordinates      (in) */
spec_mp species;                        /* Pointer to species array      (in) */
mat_mt  h;                              /* Unit cell matrix              (in) */
int     nx, ny, nz;
cell_mt *lst;                           /* Pile of cell structs          (in) */
cell_mt *cell[];                        /* Array of cells (assume zeroed)(out)*/
int     *frame_type;                    /* Framework type counter        (out)*/
{
   int icell, imol, im=0, is, isite = 0;
   double eps = 8.0*precision();
   spec_mp spec = species;
   cell_mt *list = lst;
   vec_mt ssite;
   mat_mt hinv;

   *frame_type=1;
   invert(h, hinv);

   for(imol = 0, im = 0; imol < nmols; imol++, im++)
   {
      if(im == spec->nmols)
      {
         im = 0;
         spec++;
      }

      if( spec->framework )
      {
         for( is = 0; is < spec->nsites; is++)
         {
            ssite[0] = site[0][isite];
            ssite[1] = site[1][isite];
            ssite[2] = site[2][isite];
            mat_vec_mul(hinv, (vec_mt*)ssite, (vec_mt*)ssite, 1);
            icell = LOCATE(ssite, eps);
            list->isite = isite++;
            list->num   = 1;
            list->frame_type = *frame_type;
            list->next = cell[icell];
            cell[icell] = list++;
            list->next = NULL;
         }
         (*frame_type)++;
      }
      else
      {
         icell = LOCATE(c_of_m[imol], eps);
         list->isite = isite;
         list->num   = spec->nsites;
         list->frame_type = 0;
         list->next = cell[icell];
         cell[icell] = list++;
         list->next = NULL;
         isite += spec->nsites;
      }
   }
}
/******************************************************************************
 *  site_neightbour list.  Build the list of sites withing interaction radius *
 *                         from the lists of sites in cells.                  *
 ******************************************************************************/
int     site_neighbour_list(nab, reloc, n_nab_sites, nfnab, n_frame_types,
                            n_nabors, ix, iy, iz, nx, ny, nz, nabor, cell)
int     *nab;                           /* Array of sites in list      (out) */
rvec_mt reloc[];                        /* Relocation indices for list (out) */
int     n_nab_sites;                    /* Size of above arrays         (in) */
int     nfnab[];                        /* N frame sites index by type (out) */
int     n_frame_types;                  /* Number of distinct frameworks(in) */
int     n_nabors;                       /* Number of neighbour cells    (in) */
int     ix, iy, iz, nx, ny, nz;         /* Labels of current cell       (in) */
ivec_mt *nabor;                         /* List of neighbour cells      (in) */
cell_mt **cell;                         /* Head of cell list            (in) */
{
   int  jx, jy, jz;                     /* Labels of cell in neighbour list  */
   int  j0, jnab, jsite;                /* Counters for cells etc            */
   int  nnab = 0;                       /* Counter for size of nab           */
   int  ftype;
   cell_mt      *cmol;                  /* Pointer to current cell element   */

#ifndef VECTOR
   /*
    * Usual version for scalar machines. 
    */
   irvec_mt *ifl = ifloor;
   real ri, rj, rk;
   int jcell;

   nnab = 0;
   for(ftype = 0; ftype < n_frame_types; ftype++) /* Do Framework types first */
   {
      j0 = (ftype == 0) ? 0: 1;
      for(jnab = j0; jnab < n_nabors; jnab++)    /* Loop over neighbour cells  */
      {
         jx = ix + nabor[jnab].i;       /* jx-jz are indices of neighbour cell*/
         jy = iy + nabor[jnab].j;
         jz = iz + nabor[jnab].k;
         ri  = ifl[jx].x;       /* Compute PBC relocation vector -            */
         jx -= ifl[jx].i;       /* Actually we use a lookup table for speed.  */
         rj  = ifl[jy].y;       /* Ifl[] contains integer and real versions   */
         jy -= ifl[jy].j;       /* of value since conversions are expensive.  */
         rk  = ifl[jz].z;       /* Float value is floor(jx/nx).               */
         jz -= ifl[jz].k;       /* Int value is nx*floor(jx/nx).              */
         jcell = NCELL(jx,jy,jz);
#ifdef DEBUG1
         if(jx<0 || jx>=nx || jy<0 || jy>=ny || jz<0 || jz>=nz)
            message(NULLI,NULLP,FATAL,"Bounds error on reloc (%d,%d,%d)",jx,jy,jz);
#endif
         /* Loop over molecules in this cell, filling 'nab' with its sites    */
         for(cmol = cell[jcell]; cmol != 0; cmol = cmol->next)
         {
            if( cmol->frame_type == ftype )
            {
               for(jsite = 0; jsite < cmol->num; jsite++)
               {
                  nab[nnab] = cmol->isite + jsite;
                  reloc[nnab].i = ri;
                  reloc[nnab].j = rj;
                  reloc[nnab].k = rk;
                  nnab++;
               }
            }
            if(nnab > n_nab_sites) 
               message(NULLI,NULLP,FATAL,TONAB,nnab,n_nab_sites);
         }
      }
      nfnab[ftype] = nnab;
   }
#else
   /* 
    * Version optimized for vector machines, particularly Cray PVP
    */
   int ti,tj,tk;
   static int nnarray = 0;
   static real *ri, *rj, *rk;
   static int  *jcell;

   if( n_nabors > nnarray )
   {
      /*
       * Malloc workspace arrays. Keep them around and only deallocate if
       * required size changes.
       */
      if (ri) 
      {
	 xfree(ri); xfree(jcell);
      }
      ri = dalloc(3*n_nabors);
      rj = ri + n_nabors;
      rk = rj + n_nabors;
      jcell = ialloc(n_nabors);
      nnarray = n_nabors;
   }
   
   nnab = 0;
   for(jnab = 0; jnab < n_nabors; jnab++)    /* Loop over neighbour cells  */
   {
      jx = ix + nabor[jnab].i;       /* jx-jz are indices of neighbour cell*/
      jy = iy + nabor[jnab].j;
      jz = iz + nabor[jnab].k;
      ri[jnab] = ti = IFLOOR(jx,nx);
      rj[jnab] = tj = IFLOOR(jy,ny);
      rk[jnab] = tk = IFLOOR(jz,nz);
      jcell[jnab] = NCELL(jx-ti*nx,jy-tj*ny,jz-tk*nz);
#ifdef DEBUG1
      if(jx<0 || jx>=nx || jy<0 || jy>=ny || jz<0 || jz>=nz)
	 message(NULLI,NULLP,FATAL,"Bounds error on reloc (%d,%d,%d)",jx,jy,jz);
#endif
   }
   for(ftype = 0; ftype < n_frame_types; ftype++) /* Do Framework types first */
   {
      j0 = (ftype == 0) ? 0: 1;
      for(jnab = j0 ; jnab < n_nabors; jnab++)    /* Loop over neighbour cells  */
      {
         /* Loop over molecules in this cell, filling 'nab' with its sites    */
         for(cmol = cell[jcell[jnab]]; cmol != 0; cmol = cmol->next)
         {
            if( cmol->frame_type == ftype)
            {
               for(jsite = 0; jsite < cmol->num; jsite++)
               {
                  nab[nnab] = cmol->isite + jsite;
                  reloc[nnab].i = ri[jnab];
                  reloc[nnab].j = rj[jnab];
                  reloc[nnab].k = rk[jnab];
                  nnab++;
               }
            }
         }
      }
      if(nnab > n_nab_sites) 
	 message(NULLI,NULLP,FATAL,TONAB,nnab,n_nab_sites);
      nfnab[ftype] = nnab;
   }
#endif

   return nnab;
}
/******************************************************************************
 * Force_calc.   This is the main intermolecular site force calculation       *
 * routine                                                                    *
 ******************************************************************************/
void force_calc(site, site_force, system, species, chg, potpar, pe, stress)
real            **site,                 /* Site co-ordinate arrays       (in) */
                **site_force;           /* Site force arrays            (out) */
system_mt       *system;                /* System struct                 (in) */
spec_mt         species[];              /* Array of species records      (in) */
real            chg[];                  /* Array of site charges         (in) */
pot_mt          potpar[];               /* Array of potential parameters (in) */
double          *pe;                    /* Potential energy             (out) */
mat_mt          stress;                 /* Stress virial                (out) */
{
   int          isite, imol, i,         /* Site counter i,j                   */
                i_id, ipot;             /* Miscellaneous                      */
   int          n_frame_types;          /* ==1 for no fw, 2 if fw present.    */
   int          nsites = system->nsites,/* Local copy to keep optimiser happy */
                n_potpar = system->n_potpar,
                max_id = system->max_id;
   double       mol_diam = 2.0*mol_radius(species, system->nspecies),
                cutoff = control.cutoff + (control.strict_cutoff?mol_diam:0);
   double       reloc_lim = MAX(cutoff, control.limit+mol_diam);
   double       subcell = control.subcell; /* Local copy. May change it.      */
   int          *id   = ialloc(nsites), /* Array of site_id[nsites]           */
                *id_ptr;                /* Pointer to 'id' array              */
   int          n_nab_sites = nsites*   /* Dimension of site n'bor list arrays*/
#ifdef DEBUG2 
                                MAX(1.0,NMULT*4.19*CUBE(cutoff)/det(system->h));
#else
                                NMULT*4.19*CUBE(cutoff)/det(system->h);
#endif
   ivec_mt      *nabor, *rdf_nabor;     /* Lists of neighbour cells           */
   int          n_nabors, n_rdf_nabors; /* Number of elements in lists.       */
   int          icell, ncells;          /* Subcell counter and total = nxnynz */
   int          n_cell_list;            /* Size of link-cells "heap"          */
   int          nx, ny, nz, nmax;       /* Number of subcells in MD cell      */
   static int   onx=0, ony=0, onz=0;    /* Saved values of nx, ny, nz.        */
   static int   mmax;                   /* Saved offset of ifloor for free(). */
   int          mx, my, mz;             /* Limits for ifloor array.           */
   real         ***potp                 /* Expanded potential parameter array */
                      = (real***)arralloc((size_mt)sizeof(real), 3, P0,max_id-1,
                                          0, n_potpar-1, 0, nsites-1);
   cell_mt      *c_ptr;                 /* Heap of link cell entries for list */
   cell_mt      **cell;                 /* Array of list heads for subcells   */
   spec_mt      *spec;                  /* Temp. loop pointer to species.     */
   mat_mt       htr, htrinv;            /* Transpos and inverse of h matrix   */
   
#ifdef DEBUG2
   double ppe, rr[3], ss[3];
   int im, is, i;
   mat_mp h = system->h;
   mat_mt hinv;
#endif

   /*
    * Choose a partition into subcells if none specified.
    */
   if(subcell <= 0.0) subcell = control.cutoff/5.0;
   nx = system->h[0][0]/subcell+0.5;
   ny = system->h[1][1]/subcell+0.5;
   nz = system->h[2][2]/subcell+0.5;
   ncells = nx*ny*nz;
   if( nx != onx || ny != ony || nz != onz )
   {
      note("MD cell divided into %d subcells (%dx%dx%d)",ncells,nx,ny,nz);
      onx = nx; ony = ny; onz = nz;
      /*
       * Allocate and fill lookup tables for floor(jx/nx) etc.
       */
      if( ifloor )
         xfree(ifloor-mmax);
      
      transpose(system->h, htr);
      invert(htr, htrinv);
      mx = (int)ceil(reloc_lim*nx*moda(htrinv))+1;
      my = (int)ceil(reloc_lim*ny*modb(htrinv))+1;
      mz = (int)ceil(reloc_lim*nz*modc(htrinv))+1;
      mmax = MAX3(mx, my, mz);
      nmax = MAX3(nx, ny, nz);

      ifloor = aalloc(2*mmax+nmax, irvec_mt)+mmax;
      for(i = -mmax; i < mmax+nmax; i++)
      {
         ifloor[i].x = IFLOOR(i,nx);
         ifloor[i].y = IFLOOR(i,ny);
         ifloor[i].z = IFLOOR(i,nz);
         ifloor[i].i = nx*IFLOOR(i,nx);
         ifloor[i].j = ny*IFLOOR(i,ny);
         ifloor[i].k = nz*IFLOOR(i,nz);
      }
   }

   /*  
    * Construct and fill expanded site-identifier array, id   
    */
   id_ptr = id;
   for (spec = species; spec < species+system->nspecies; spec++)
      for(imol = 0; imol < spec->nmols; imol++)
      {
         memcp(id_ptr, spec->site_id, spec->nsites*sizeof(int));
         id_ptr += spec->nsites;
      }
   /*   
    * Build arrays of pot. pars [max_id][nsites] for access in vector loops
    */
   for(ipot = 0; ipot < n_potpar; ipot++)
      for(i_id = 1; i_id < max_id; i_id++)
      {
#ifdef titan
NOVECTOR
#endif
         for(isite = 0; isite < nsites; isite++)
            potp[i_id][ipot][isite] = potpar[i_id*max_id+id[isite]].p[ipot];
      }
   /*
    * Allocate "heap" of list entries to build linked lists from.
    */
   n_cell_list = 1;
   for(spec = species; spec < species+system->nspecies; spec++)
      if( spec->framework )
         n_cell_list += spec->nmols*spec->nsites;
      else 
         n_cell_list += spec->nmols;
   c_ptr = aalloc(n_cell_list, cell_mt); 
   /*
    * Build a linked list of molecules/sites for each subcell.
    * "cell" is the array of list heads NX x NY x NZ.
    */
   cell = aalloc(ncells, cell_mt *);     
   for( icell=0; icell < ncells; icell++)
      cell[icell] = NULL;
   fill_cells(system->c_of_m, system->nmols, site, species, system->h,
              nx, ny, nz, c_ptr, cell, &n_frame_types);
   if( n_frame_types > 2 )
      message(NULLI, NULLP, FATAL,
              "Multiple framework molecules are not supported");
   /*
    * Build lists of cells within cutoff of reference cell.
    */
   if( control.strict_cutoff )
      nabor = strict_neighbour_list(&n_nabors, system->h, cutoff, nx, ny, nz, 1);
   else
      nabor = neighbour_list(&n_nabors, system->h, cutoff, nx, ny, nz, 1);
   
   
#ifdef DEBUG2
   ppe = 0;
   spec = species; isite = 0;
   invert(h, hinv);
   for(imol = 0, im = 0; imol < system->nmols; imol++, im++)
   {
      if(im == spec->nmols)
      {
         im = 0;
         spec++;
      }
      for(is = isite; is < isite+spec->nsites; is++)
      {
         for(jsite = 0; jsite < isite; jsite++)
         {
            for( i=0; i<3; i++)
               rr[i] = site[i][jsite] - site[i][is];
            mat_vec_mul(hinv, rr, ss, 1);
            for( i=0; i<3; i++)
               ss[i] -= floor(ss[i]+0.5);
            mat_vec_mul(h, ss, rr, 1);

            r_sqr[jsite] = SUMSQ(rr);
            if( control.strict_cutoff && r_sqr[jsite] > cutoffsq )
                  r_sqr[jsite] = cutoff100sq;
         }

         hist(0,isite,r_sqr);
         kernel(0,isite,forceij,&ppe,r_sqr,chg,chg[is],
                norm,control.alpha,system->ptype,potp[id[is]]);
      }
      isite += spec->nsites;
   }
   histout();
   note("Direct pot. energy = %g",ppe*CONV_E);
#endif
   force_inner(ithread, nthreads, site, chg, potp, id, n_nab_sites, 
               n_nabors, nabor, nx, ny, nz, cell, n_frame_types, system,
               stress, pe, site_force);

   /*
    * Accumulate radial distribution functions
    */
   if (control.rdf_interval > 0 && 
       control.istep >= control.begin_rdf &&
       control.istep % control.rdf_interval == 0)
   {
      n_nab_sites = nsites*
                    NMULT*4.19*CUBE(control.limit+mol_diam)/det(system->h);
      rdf_nabor = strict_neighbour_list(&n_rdf_nabors, system->h, 
                                        control.limit+mol_diam, nx, ny, nz, 0);
      rdf_inner(ithread, nthreads, site, id, n_nab_sites, n_rdf_nabors, 
                rdf_nabor, nx, ny, nz, cell, n_frame_types, system);
      xfree(rdf_nabor);
   }
#ifdef DEBUG2
   histout();
#endif
   afree((gptr*)(potp+P0));  xfree(c_ptr); 
   xfree(cell);        xfree(id); 
   xfree(nabor);
}
/******************************************************************************
 *  Force_inner() Paralellised inner loops of force_calc.  Loops over cells   *
 *  in MD cell with stride = nomber of processors available.  Should be       *
 *  called once for each parallel thread.                                     *
 ******************************************************************************/
void
force_inner(ithread, nthreads, site, chg, potp, id, n_nab_sites, n_nabors, 
            nabor, nx, ny, nz, cell, n_frame_types, system,
            stress, pe, site_force)
int             ithread, nthreads;      /* Parallel node variables.      (in) */
real            **site;                 /* Site co-ordinate arrays       (in) */
real            chg[];                  /* Array of site charges         (in) */
real            ***potp;                /* Expanded potential parameter array */
int             id[];                   /* Array of site_id[nsites]      (in) */
int             n_nab_sites;            /* Dimension of site n'bor list arrays*/
int             n_nabors;               /* Number of elements in lists.   (in)*/
ivec_mt         *nabor;                 /* Lists of neighbour cells       (in)*/
int             nx, ny, nz;             /* Number of subcells in MD cell  (in)*/
cell_mt         **cell;                 /* Array of list heads of subcells(in)*/
int             n_frame_types;          /* ==1 for no fw, 2 if fw present (in)*/
system_mt       *system;                /* System struct                 (in) */
mat_mt          stress;                 /* Stress virial                (out) */
double          *pe;                    /* Potential energy             (out) */
real            **site_force;           /* Site force arrays            (out) */
{
                /*
                 * The following arrays are for 'neighbour site list'
                 * quantities and should be dimensioned to the max value of
                 * 'nnab'.  A rough approx is the ratio of the volume of
                 * the "cutoff sphere" to that of the MD cell times nsites.
                 * This may be too small for inhomogeneous systems, but at
                 * least it scales with the cutoff radius.
                 */
   int          *nab  = ialloc(n_nab_sites);    /* Neigbour site gather vector*/
   rvec_mt      *reloc = aalloc(n_nab_sites, rvec_mt); /* Site PBC shifts     */
   real         *nab_sx  = dalloc(n_nab_sites), /* 'Gathered' list of         */
                *nab_sy  = dalloc(n_nab_sites), /*   neighbour site co-ords   */
                *nab_sz  = dalloc(n_nab_sites), /*   - x,y,z components.      */
                *forcejx = dalloc(n_nab_sites), /* List of neighbour site     */
                *forcejy = dalloc(n_nab_sites), /*  forces in gathered form   */
                *forcejz = dalloc(n_nab_sites), /*  - xyz components.         */
                *rx      = dalloc(n_nab_sites), /* Reference to neigbour site */
                *ry      = dalloc(n_nab_sites), /* - site vector adjusted for */
                *rz      = dalloc(n_nab_sites), /*  periodic boundaries. xyz. */
                *r_sqr   = dalloc(n_nab_sites), /* Squared site-site distance */
                *nab_chg = dalloc(n_nab_sites), /* Gathered neig. site charges*/
                *forceij = dalloc(n_nab_sites); /* -V'(r) / r                 */
   real         **nab_pot                       /* Gathered pot par array     */
                = (real**)arralloc((size_mt)sizeof(real), 2,
                                   0, system->n_potpar-1, 0, n_nab_sites-1);
   real         **pp, **ppp;            /* Loop pointer variables for potp.   */
   real         force_cpt, site0, site1, site2, s00, s01, s02, s11, s12, s22;
                                   /* Accumulators for forces and stresses.   */
   real         rrx, rry, rrz;                  /* Scalar loop temporaries    */
   real         h00, h01, h02, h11, h12, h22;   /* Temp copies of system->h   */
   double       norm = 2.0*control.alpha/sqrt(PI);      /* Coulombic prefactor*/
   double       cutoffsq = SQR(control.cutoff), /* Temporary copy for optim'n */
                cutoff100sq = 10000.0*cutoffsq;
   int          ix, iy, iz;             /* 3-d cell indices for ref and neig. */
   int          icell,                  /* Index for cells of molecule pair   */
                nnab, jbeg, jmin, jmax, /* Number of sites in neighbour list  */
                isite, jsite, ipot, lim;/* Counters.                          */
   int          nsites = system -> nsites;      /* Temporary copy for optim'n */
   int          nfnab[2];               /* Number of non-fw and fw neighbours */
   cell_mt      *cmol;                  /* Loop counter for link cells.       */

   s00 = s01 = s02 = s11 = s12 = s22 = 0.0;     /* Accumulators for stress    */

/******************************************************************************
 *  Start of main loop over all subcells.                                     *
 *  First build "site neighbour list" containing all sites belonging to       *
 *  molecules in this subcell and all others in the cell neighbour list.      *
 *  Use "gather" to construct corresponding arrays of co-ordinates, charges   *
 *  and potential parameters.                                                 *
 *  Then loop over all sites in THIS cell and calculate pair distances,       *
 *  potential forces and stress.                                              *
 ******************************************************************************/
   for( icell = ithread; icell < nx*ny*nz; icell += nthreads)
   {
      if(cell[icell] == NULL) continue;       /* Empty cell - go on to next */
      ix = icell/ (ny*nz);
      iy = icell/nz - ny*ix;
      iz = icell - nz*(iy + ny*ix);
      nnab = 0;
#ifdef DEBUG1
      printf("Working on cell %4d (%d,%d,%d) (sites %4d to %4d)\n", icell,
             ix,iy,iz,cell[icell]->isite,cell[icell]->isite+cell[icell]->num-1);
      printf("\n jcell\tjx jy jz\tNsites\n");
#endif
      /*
       * Build site neighbour list 'nab' from cell list.
       */ 
      nnab = site_neighbour_list(nab, reloc, n_nab_sites, nfnab, n_frame_types, 
                                 n_nabors, ix, iy, iz, nx, ny, nz, nabor, cell);
#ifdef DEBUG4
      for(jsite=0; jsiteh[0][0];   h01 = system->h[0][1];   h02 = system->h[0][2];
      h11 = system->h[1][1];   h12 = system->h[1][2];   h22 = system->h[2][2];
VECTORIZE
      for(jsite=0; jsitenext)
      {
         if( cmol->frame_type )
         {
            jmin = 0;
            jmax = nfnab[0];
         }
         else
         {
            jmin = jbeg += cmol->num;
            jmax = nnab;
         }
         lim = cmol->isite + cmol->num;
         for(isite = cmol->isite; isite < lim; isite++)
         {                                   /* Loop over sites in molecule */
            /*
             * Construct pot'l param arrays corresponding to neighbour sites.
             */
            pp = potp[id[isite]];
            ppp = nab_pot;
            for(ipot = 0; ipot < system->n_potpar; ipot++)
            {
               gather(jmax, *ppp++, *pp++, nab, nsites);
            }
#ifdef DEBUG1
            if(isite == 100)
#endif
#if defined(DEBUG1) || defined(DEBUG5)
            { int jnab;
            for(jnab = jmin; jnab < jmax; jnab++)
               printf("%4d %4d\n", jnab,nab[jnab]);
           }
#endif
            site0=site[0][isite]; site1=site[1][isite]; site2=site[2][isite];
VECTORIZE
            for(jsite=jmin; jsite < jmax; jsite++)
            {
               rrx = nab_sx[jsite] - site0;
               rry = nab_sy[jsite] - site1;
               rrz = nab_sz[jsite] - site2;
               r_sqr[jsite] = rrx*rrx+rry*rry+rrz*rrz;
               rx[jsite] = rrx;
               ry[jsite] = rry;
               rz[jsite] = rrz;
            }
            if( (jsite = jmin+search_lt(jmax-jmin, r_sqr+jmin, 1, TOO_CLOSE))
               < jmax )
               message(NULLI, NULLP, WARNING, TOOCLS,
                       isite, nab[jsite], sqrt(TOO_CLOSE));

            if( control.strict_cutoff )
               for(jsite = jmin; jsite < jmax; jsite++)
                  if( r_sqr[jsite] > cutoffsq )
                     r_sqr[jsite] = cutoff100sq;

#ifdef DEBUG2
            hist(jmin, jmax, r_sqr);
#endif
               
            /*  Call the potential function kernel                            */
            kernel(jmin, jmax, forceij, pe, r_sqr, nab_chg, chg[isite],
                   norm, control.alpha, system->ptype, nab_pot);
            site0 = site1 = site2 = 0.0;
VECTORIZE
            for(jsite=jmin; jsite < jmax; jsite++)
            {
               force_cpt  = forceij[jsite]*rx[jsite];
               s00         += force_cpt * rx[jsite];
               s02         += force_cpt * rz[jsite];
               s01         += force_cpt * ry[jsite];
               site0           -= force_cpt;
               forcejx[jsite]  += force_cpt;
            }
VECTORIZE
            for(jsite=jmin; jsite < jmax; jsite++)
            {
               force_cpt = forceij[jsite]*ry[jsite];
               s11         += force_cpt * ry[jsite];
               s12         += force_cpt * rz[jsite];
               site1           -= force_cpt;
               forcejy[jsite]  += force_cpt;
            }
VECTORIZE
            for(jsite=jmin; jsite < jmax; jsite++)
            {
               force_cpt = forceij[jsite]*rz[jsite];
               s22         += force_cpt * rz[jsite];
               site2           -= force_cpt;
               forcejz[jsite]  += force_cpt;
            }
            site_force[0][isite] += site0;
            site_force[1][isite] += site1;
            site_force[2][isite] += site2;
#ifdef DEBUG3
            printf("PE = %f\n",pe[0]);
#endif
         }
      }
      spxpy(nnab, forcejx, site_force[0], nab);
      spxpy(nnab, forcejy, site_force[1], nab);
      spxpy(nnab, forcejz, site_force[2], nab);
   }
   stress[0][0]  += s00;
   stress[0][1]  += s01;
   stress[0][2]  += s02;
   stress[1][1]  += s11;
   stress[1][2]  += s12;
   stress[2][2]  += s22;

   afree((gptr*)nab_pot);
   xfree(nab);     xfree(reloc);  xfree(nab_chg);
   xfree(r_sqr);   xfree(forceij);
   xfree(rx);      xfree(ry);      xfree(rz);
   xfree(forcejx); xfree(forcejy); xfree(forcejz);
   xfree(nab_sx);  xfree(nab_sy);  xfree(nab_sz);
}
/******************************************************************************
 *  Rdf_inner() Paralellised inner loops of force_calc.  Based on force_inner *
 *     but only calls rdf_accum().                                            *
 ******************************************************************************/
void
rdf_inner(ithread, nthreads, site, id, n_nab_sites, n_nabors, 
            nabor, nx, ny, nz, cell, n_frame_types, system)
int             ithread, nthreads;      /* Parallel node variables.      (in) */
real            **site;                 /* Site co-ordinate arrays       (in) */
int             id[];                   /* Array of site_id[nsites]      (in) */
int             n_nab_sites;            /* Dimension of site n'bor list arrays*/
int             n_nabors;               /* Number of elements in lists.   (in)*/
ivec_mt         *nabor;                 /* Lists of neighbour cells       (in)*/
int             nx, ny, nz;             /* Number of subcells in MD cell  (in)*/
cell_mt         **cell;                 /* Array of list heads of subcells(in)*/
int             n_frame_types;          /* ==1 for no fw, 2 if fw present (in)*/
system_mt       *system;                /* System struct                  (in)*/
{
   int          *nab  = ialloc(n_nab_sites);    /* Neigbour site gather vector*/
   rvec_mt      *reloc = aalloc(n_nab_sites, rvec_mt); /* Site PBC shifts     */
   real         *nab_sx  = dalloc(n_nab_sites), /* 'Gathered' list of         */
                *nab_sy  = dalloc(n_nab_sites), /*   neighbour site co-ords   */
                *nab_sz  = dalloc(n_nab_sites), /*   - x,y,z components.      */
                *r_sqr   = dalloc(n_nab_sites); /* Squared site-site distance */
   real         site0, site1, site2;
   real         rrx, rry, rrz;                  /* Scalar loop temporaries    */
   real         h00, h01, h02, h11, h12, h22;   /* Temp copies of system-> h  */
   int          ix, iy, iz;             /* 3-d cell indices for ref and neig. */
   int          icell,                  /* Index for cells of molecule pair   */
                nnab, jbeg, jmin, jmax, /* Number of sites in neighbour list  */
                isite, jsite, lim;      /* Counters.                          */
   int          nsites = system -> nsites;      /* Temporary copy for optim'n */
   int          nfnab[2];               /* Number of non-fw and fw neighbours */
   cell_mt      *cmol;                  /* Loop counter for link cells.       */

/******************************************************************************
 *  Start of main loop over subcells.                                         *
 ******************************************************************************/
   for( icell = ithread; icell < nx*ny*nz; icell += nthreads)
   {
      if(cell[icell] == NULL) continue;       /* Empty cell - go on to next */
      ix = icell/ (ny*nz);
      iy = icell/nz - ny*ix;
      iz = icell - nz*(iy + ny*ix);

      /*
       * Build site neighbour list 'nab' from cell list.
       */ 
      nnab = site_neighbour_list(nab, reloc, n_nab_sites, nfnab, n_frame_types, 
                                 n_nabors, ix, iy, iz, nx, ny, nz, nabor, cell);
      gather(nnab, nab_sx, site[0], nab, nsites); /* Construct list of site  */
      gather(nnab, nab_sy, site[1], nab, nsites); /* co-ordinates from nabo  */
      gather(nnab, nab_sz, site[2], nab, nsites); /* list.                   */

      /*
       * Apply periodic boundary conditions to neighbour site co-ords.
       * Assume h matrix is upper triangular.
       */
      h00 = system->h[0][0];   h01 = system->h[0][1];   h02 = system->h[0][2];
      h11 = system->h[1][1];   h12 = system->h[1][2];   h22 = system->h[2][2];
VECTORIZE
      for(jsite=0; jsitenext)
      {
         if( cmol->frame_type )
         {
            jmin = 0;
            jmax = nfnab[0];
         }
         else
         {
            jmin = jbeg += cmol->num;
            jmax = nnab;
         }
         lim = cmol->isite + cmol->num;
         for(isite = cmol->isite; isite < lim; isite++)
         {                                   /* Loop over sites in molecule */
            site0=site[0][isite]; site1=site[1][isite]; site2=site[2][isite];
VECTORIZE
            for(jsite=jmin; jsite < jmax; jsite++)
            {
               rrx = nab_sx[jsite] - site0;
               rry = nab_sy[jsite] - site1;
               rrz = nab_sz[jsite] - site2;
               r_sqr[jsite] = rrx*rrx+rry*rry+rrz*rrz;
            }
            /*
             * Accumulate radial distribution functions
             */
            rdf_accum(jmin, jmax, r_sqr, id[isite], id, nab);
         }
      }
   }
   xfree(nab);     xfree(reloc);
   xfree(r_sqr);
   xfree(nab_sx);  xfree(nab_sy);  xfree(nab_sz);
}
$EOD
$!
$CREATE input.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * Input	Functions for reading and verifying the input files (except   *
 *		the restart file). Contents:				      *
 * Strlower();		Convert a string to lowercase (Internal use only)     *
 * Get_line();		Read next input line.         (Internal use only)     *
 * Read_sysdef()       	Read the system specification file     	       	      *
 * Lattice_start()	Read initial crystal structure and set it up	      *
 * Read_control()       Read control file				      *
 ******************************************************************************
 *      Revision Log
 *       $Log: input.c,v $
 *       Revision 2.9  1996/11/04 17:32:28  keith
 *       Fixed non-ANSI compliance in get_line() which decremented pointer to
 *       1 element below start of array and compared with beginning. I doubt
 *       this could ever cause a failure in reality but standards are standards.
 *
 *       Revision 2.8  1995/10/25 11:59:00  keith
 *       Fixed input parser bug which didn't notice missing "=" and silently
 *       used incorrect value.
 *
 *       Revision 2.7.1.2  1995/10/25 11:49:49  keith
 *       Fixed input parser bug which didn't notice missing "=" and silently
 *       used incorrect value.
 *
 * Revision 2.7.1.1  1994/12/16  12:02:34  keith
 * Experimental version with partial Ewald sum evaluated at rlv's only.
 *
 * Revision 2.7  1994/06/08  13:22:31  keith
 * Null update for version compatibility
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Added CONST qualifier to (re-)declarations of ANSI library
 * emulation routines to give reliable compilation even
 * without ANSI_LIBS macro. (#define's away for K&R
 * compilers)
 *
 *
 * moved "match" to startup.c and passed as param.
 * Added check for end-of-file in 2 cases where I missed before.
 *
 * Revision 2.5  94/01/25  10:57:11  keith
 * Null update for XDR portability release
 * 
 * Revision 2.4  94/01/25  10:56:58  keith
 * Changed default for "xdr" parameter to "on".
 * 
 * Revision 2.3  93/10/28  10:27:52  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.1  93/07/19  13:27:53  keith
 * Added XDR capability for backup and dump files.
 * 
 * Revision 2.0  93/03/15  14:49:08  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.8.1.21  93/03/12  12:14:14  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.8.1.21  93/03/09  15:58:36  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.8.1.20  92/10/28  14:09:48  keith
 * Changed "site_[tp]" typedefs to avoid name clash on HP.
 * 
 * Revision 1.8.1.19  92/09/22  14:48:15  keith
 * Tidied up calls to improve "lint" rating.
 * 
 * Revision 1.8.1.18  92/06/26  17:03:10  keith
 * Got rid of assumption that memory returned by talloc() or
 * arralloc() is zeroed.  This enhances ANSI compatibility.
 * Removed memory zeroing from alloc.c() in consequence.
 * 
 * Revision 1.8.1.17  92/03/11  12:56:21  keith
 * Changed "scale-separately" parameter to "scale options"
 * 
 * Revision 1.8.1.16  91/08/19  16:46:39  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_t and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * 
 * Revision 1.8.1.15  91/08/16  15:25:30  keith
 * Checked error returns from fread, fwrite, fseek and fclose more
 * rigourously.   Called strerror() to report errors.
 * 
 * Revision 1.8.1.13  91/03/12  15:42:49  keith
 * Tidied up typedefs size_t and include file 
 * Added explicit function declarations.
 * 
 * Revision 1.8.1.12  90/10/22  17:47:25  keith
 * Corrected conversion of unit cell angles and lengths to vectors
 * in lattice_start().
 * 
 * Revision 1.8.1.11  90/08/20  17:25:45  keith
 * Modified to order species so that frameworks are last. 
 * 
 * Revision 1.8.1.10  90/05/03  16:41:24  keith
 * Fixed sys-spec parsing to cope with GEC and other broken scanf's which
 * return too many items matched.
 * 
 * Revision 1.8.1.9  90/04/16  18:18:16  keith
 * Added "strain-mask" field to input parse table.
 * 
 * Revision 1.8.1.8  90/04/12  16:27:09  keith
 * Added include of  which was removed from "structs.h"
 * 
 * Revision 1.8.1.7  90/03/26  18:05:50  keith
 * Moved system-dependant backup and temp file names to "defs.h"
 * 
 * Revision 1.8.1.6  89/11/22  14:34:44  keith
 * Changed default values of begin-rdf and average-interval.
 * 
 * Revision 1.8.1.5  89/11/21  16:32:30  keith
 * Removed member out_file from control and all uses. (Now command parameter).
 * 
 * Revision 1.8.1.4  89/11/20  18:06:23  keith
 * Modified form of match[] to include default values.
 * 
 * Revision 1.8.1.3  89/11/20  13:30:00  keith
 * Replaced separate arrays "types" and "npotp" with array of structs "potspec"
 * 
 * Revision 1.8.1.2  89/09/04  18:56:08  keith
 * Added 'surface-dipole' keyword to control file.
 * 
 * Revision 1.8.1.1  89/08/30  12:55:44  keith
 * Mods to add framework structures to simulation model
 * 
 * Revision 1.8  89/08/30  12:51:41  keith
 * Fixed read_sysdef() to keep original input line when reading potentials
 * to make error message informative.
 * Modified lattice_start() to fix bug which only considered rotations
 * of one species.  In conjunction with change in startup.
 * 
 * Revision 1.7  89/07/07  10:49:56  keith
 * Fixed lattice_start() so as not to test quaternion normalisation for
 * monatomic sopecies.
 * 
 * Revision 1.6  89/06/26  13:55:34  keith
 * Tidied up loops over species to use one pointer as counter.
 * Incorrect code to print control params removed from read_control()
 * 
 * Revision 1.5  89/06/21  13:36:42  keith
 * Moved pot. par. defs arrays types[], npotp[] and npott to kernel.c
 * Moved print_sysdef() to output.c
 * Made match[] external and its dimension nmatch into an external int
 * 
 * Revision 1.4  89/06/16  16:56:08  keith
 * Corrected bug in lattice_start() which crashed for point atoms/ions
 * Added message for successful lattice start
 * 
 * Revision 1.3  89/06/01  21:24:24  keith
 * Control.out eliminated, use printf and freopen instead to direct output.
 * 
 * Revision 1.2  89/05/22  14:05:38  keith
 * Added rescale-separately option, changed 'contr_t' format.
 * 
 * Revision 1.1  89/04/20  16:00:42  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/input.c,v 2.9 1996/11/04 17:32:28 keith Exp $";
#endif
/*========================== program include files ===========================*/
#include	"defs.h"
/*========================== Library include files ===========================*/
#include	
#include	
#include 	"string.h"
#include	"stddef.h"
#include	
/*========================== Program include files ===========================*/
#include	"structs.h"
#include	"messages.h"
/*========================== External function declarations ==================*/
gptr            *talloc();	       /* Interface to memory allocator       */
void            tfree();	       /* Free allocated memory	      	      */
void		q_mul_1();
void    	zero_real();            /* Initialiser                        */
#if defined(ANSI) || defined(__STDC__)
void		message(int *, ...);	/* Write a warning or error message   */
#else
void		message();		/* Write a warning or error message   */
#endif
/*========================== External data references ========================*/
extern	      contr_mt	control;	/* Main simulation control record     */
extern	CONST pots_mt	potspec[];	/* Potential type specification       */
/*========================== Macros ==========================================*/
#define		LLEN		132
		/* Flags to indicate status of potpar and site_info records   */
#define		S_USED		0x01
#define		S_MASS		0x02
#define		S_CHARGE	0x04
#define		S_NAME		0x08
/*=============================================================================
 |   Start of functions							      |
 =============================================================================*/
/******************************************************************************
 *  get_line  read an input line skipping blank and comment lines	      *
 ******************************************************************************/
static
char	*get_line(line, len, file)
char	*line;
int	len;
FILE	*file;
{
   char	*s, *t;
   int  i;
   do
   {
      s = fgets(line, len, file);		/* Read one line of input     */
      if(s == NULL) break;			/* exit if end of file        */
      i = strlen(s) - 1;
      while(i >= 0 && (s[i] == ' ' || s[i] == '\t' || s[i] == '\n'))
         s[i--] = '\0';				/* Strip trailing white space */
   }
   while(*s == '\0' || *s == '#');		/* Repeat if blank or comment */
   if(s == NULL)
      *line = '\0';				/* Return null at eof         */
   return(line);
}
/******************************************************************************
 * strlower   convert a string to lowercase anr return a pointer to it        *
 ******************************************************************************/
char	*strlower(s)
char	*s;
{
   char	*t;
   for(t = s; *t != '\0'; t++)
      *t = isupper(*t) ? tolower(*t) : *t;
   return(s);
}
/******************************************************************************
 *  Sort array of species structs so frameworks are at end.		      *
******************************************************************************/
static
void sort_species(species, nspecies)
spec_mt	*species;
int	nspecies;
{
   spec_mt tmp, *lo=species, *hi=species+nspecies-1;

   while( lo < hi )
   {
      while( lo < hi && ! lo->framework)
	 lo++;
      while( lo < hi && hi->framework)
	 hi--;

      if( lo >= hi )
	 break;

      tmp = *hi;
      *hi = *lo;
      *lo = tmp;

      lo++;
      hi--;
   }
}

/******************************************************************************
 *  read_sysdef    Read the system specification file which must be open and  *
 *  pointed to by parameter 'file'.  Set up the structures system and species *
 *  and arrays site_info and potpar (allocating space) and read in and check  *
 *  the supplied values.  The reading is done in two passes.  Pass 1 simply   *
 *  counts the number of species, number of sites on each species and the     *
 *  largest site identifier index in order to allocate the dynamic arrays.    *
 *  Pass 2 does the actual reading and checking.                              *
 ******************************************************************************/
void	read_sysdef(file, system, spec_pp, site_info, pot_ptr)
FILE		*file;			/* File pointer to read info from     */
system_mp	system;			/* Pointer to system array (in main)  */
spec_mp		*spec_pp;		/* Pointer to be set to species array */
site_mp		*site_info;		/* To be pointed at site_info array   */
pot_mp		*pot_ptr;		/* To be pointed at potpar array      */
{
   int		nspecies = 0,		/* Number of distinct species         */
   		max_id = 0,		/* Largest site identifier index      */
   		id, idi, idj,		/* Temp. site identifier index        */
   		isite,			/* species and site counters	      */
		sflag,			/* Temporary flag		      */
		i,			/* Counter			      */
		n_potpar,		/* Number of parameters for this pot'l*/
   		n_items;		/* How many items scanf found in input*/
   struct list_mt {int n; struct list_mt *p;};/* Template for linked list nsites*/
   struct list_mt nsites_base,		/* Head of list (contains no datum)   */
   		*nsites,		/* List entry for current species     */
                *last = &nsites_base;	/* List entry for previous species    */
   int		nerrs = 0;		/* Accumulated error count	      */
   int		flag;			/* Used to test 'fseek' result        */
   long		start_pos = ftell(file);/* Rewind marker for second pass      */
   char		name[LLEN],		/* Species name temporary             */
   		keywd[LLEN],		/* Species attribute keywords	      */
   		line[LLEN],		/* Store for input line from file     */
   		pline[LLEN];		/* Used in pot'l paramater parsing    */
   double	mass, charge, p_tmp;	/* Local temporaries		      */
   double	p_f_sites[3];		/* Local temporary		      */
   pot_mp	pp1;			/* Used for acces to potpar ij and ji */
   spec_mp	species, spec;		/* Local pointer to species array     */
   site_mp	s_ptr;			/* Local pointer to site info array   */
   static pot_mt	pot = {S_USED};	/* Local storage for potentials       */

   message(&nerrs,NULLP,INFO,SYSRD);
   /* First pass - read system definition and count nspecies, nsites, max_id  */
   (void)get_line(line,LLEN,file);		/* Read first line.	      */
   while(sscanf(line, "%s", name) > 0 && strcmp(strlower(name), "end") != 0)
   {						/* Loop, parsing 'line' for   */
      nspecies++;				/* name of new species.       */
      nsites = aalloc(1, struct list_mt); 	/* Make new list element      */
      last->p = nsites;				/* Link it in		      */
      last = nsites;				/* Backwards pointer for link */
      nsites->p=NULL; nsites->n=0;
      while(sscanf(get_line(line,LLEN,file), "%d", &id) > 0)
      {						/* Loop, reading and parsing  */
         nsites->n++;				/* for integer ie new site id.*/
         max_id = MAX(max_id, id);		/* Count nsites, greatest id. */
      }						/* Leave 'line' if parse fails*/
   }
   if(nspecies == 0)				/* Empty file??		      */
      message(&nerrs,NULLP,FATAL,NOSPEC);

   /* Allocate arrays of species and site info records */
   max_id++;
   system->max_id = max_id;
   *spec_pp    = aalloc(nspecies, spec_mt );
   *site_info  = aalloc(max_id, site_mt );
   memst(*site_info, 0, max_id*sizeof(site_mt));
   *pot_ptr    = aalloc(SQR(max_id), pot_mt );
   for( i = 0; i < SQR(max_id); i++)
   {
      (*pot_ptr)[i].flag = 0;
      zero_real((*pot_ptr)[i].p,NPOTP);
   }
   species = *spec_pp;   			/* Local pointer for neatness.*/
   system->nspecies = nspecies;

   flag = fseek(file, start_pos, 0);		/* Prepare to reread input.   */
   if(flag)
      message(NULLI, NULLP, FATAL, SEFAIL, "control file", strerror(errno));
   nsites = &nsites_base;
   /* Pass 2.  read system definition and set up species and site_info arrays */
   for (spec = species; spec < species+system->nspecies; spec++)
   {						/* Loop over all species.     */
      n_items = sscanf(get_line(line,LLEN,file),"%s %d %s",
		       name, &spec->nmols, keywd);
      name[sizeof spec->name-1] = '\0';		/* Truncate before copying    */
      (void)strcpy(spec->name, name);		/* to avoid overflow.         */
      nsites = nsites->p;			/* Find next element of list  */
      spec->nsites = nsites->n;			/* which contains nsites.     */
      switch(n_items)
      {				   /* Relies on fall-through: do not re-order */
       case 3:
	 if(! strcmp(strlower(keywd), "framework"))
	    spec->framework = true;
	 else if(*keywd != '\0')   /* Kludge for broken scanf's.              */
	    message(&nerrs, NULLP, ERROR, UNKEY, keywd);
	 break;
       case 2:
	 spec->framework = false;
	 break;					/* Normal exit from switch    */
       default:
         message(&nerrs,line, ERROR, NONUM, name);
      }
      if(spec->nmols <= 0)			/* Can't have <=0 molecules   */
         message(&nerrs,line,ERROR, NOMOLS, spec->nmols, name);
      if(spec->nsites <=0)			/* or ghost molecules!        */
         message(&nerrs,NULLP,ERROR,NOSITE,spec->nsites,name);
      spec->p_f_sites = ralloc(spec->nsites);	/* Allocate space and set     */
      spec->site_id   = ialloc(spec->nsites+1);	/* pointers for each species. */

      for(isite = 0; isite < spec->nsites; isite++)
      {						/* Loop over sites on molecule*/
        n_items =sscanf(get_line(line,LLEN,file), "%d %lf %lf %lf %lf %lf %s",
                        &id,			/* Get and parse line of input*/
                        p_f_sites,
                        p_f_sites + 1,
                        p_f_sites + 2,
                        &mass,  &charge,  name);
        if(id <= 0)				/* Test for valid site index. */
        {
           message(&nerrs,line, ERROR, INVSID, id);
           id = 0;
        }
        spec->site_id[isite] = id;		/* Put id into rightful place.*/
        name[sizeof s_ptr->name - 1] = '\0';	/* Truncate site name.        */
        s_ptr = *site_info + id;		/* Reference (*site_info)[id].*/
        s_ptr->flag |= S_USED;
        switch (n_items)			/* Handle input items in      */
        {					/* reverse order.             */
           case 7:				/* Site name supplied.	      */
              if(s_ptr->flag & S_NAME && strcmp(name, s_ptr->name) != 0)
                 message(&nerrs,line,ERROR,NCONF,id,s_ptr->name);
              else
                 (void)strcpy(s_ptr->name, name);
              s_ptr->flag |= S_NAME;
           case 6:				/* Site charge supplied.      */
              if(s_ptr->flag & S_CHARGE && charge != s_ptr->charge)
                 message(&nerrs,line,ERROR,CCONF,id, s_ptr->charge);
              else
                 s_ptr->charge = charge;
              s_ptr->flag |= S_CHARGE;
           case 5:				/* Site mass supplied.        */
              if(s_ptr->flag & S_MASS && mass != s_ptr->mass)
                 message(&nerrs,line,ERROR,MCONF,id, s_ptr->mass);
              else if(mass < 0.0)
                 message(&nerrs,NULLP,ERROR,INVMAS,id,mass);
              else
                 s_ptr->mass = mass;
              s_ptr->flag |= S_MASS;
           case 4:				/* All site co-ordinates      */
	      for( i = 0; i < 3; i++ )
	         spec->p_f_sites[isite][i] = p_f_sites[i];
              break;				/* Normal exit from switch    */
           case 3:                      	/* One or more site 	      */
           case 2:                      	/* co-ordinates are missing.  */
           case 1:
              message(&nerrs,line,ERROR,MISSCO,n_items-1,id);
              break;
           default:				/* Should never occur.	      */
              message(&nerrs,NULLP, FATAL, BROKEN, n_items);
        }
      }
   }
   /*
    *  Order species structs with frameworks last
    */
   sort_species(species, nspecies);
   /*
    * Check that all sites have been fully defined, and for gaps in ordering.
    */
   for(id = 1; id < max_id; id++)
   {
      sflag = (*site_info)[id].flag;
      if(sflag & S_USED)
      {
         if(! (sflag & S_MASS))
            message(&nerrs,NULLP,ERROR,NOMASS,id);
         if(! (sflag & S_CHARGE))
            message(&nerrs,NULLP,ERROR,NOCGRG,id);
         if(! (sflag & S_NAME))
            message(&nerrs,NULLP,ERROR,NONAME,id);
      }
      else
         message(&nerrs,NULLP, WARNING, NOTUSD,id);
   }
   (void)get_line(line,LLEN,file);		/* read "end" -left by pass 1 */

   /* Next line is keyword indicating type of potentials to be used	      */
   n_items = sscanf(get_line(line,LLEN,file), "%s", name);
   if( n_items <= 0 )
      message(NULLI,NULLP,FATAL,SYSEOF,"potential type specification");
   for(i = 0; potspec[i].name; i++)		/* Is 'name' a known type?    */
      if(strcmp(strlower(name), potspec[i].name) == 0)
         break;
   if(! potspec[i].name)	       		/* Did the loop find 'name'?  */
      message(&nerrs,line,FATAL,UNKPOT,name);	/* no			      */
   system->ptype = i;				/* yes		              */
   n_potpar = system->n_potpar = potspec[system->ptype].npar;
   						/* Now read in parameters     */
   while(sscanf(get_line(line,LLEN,file),"%s",name) > 0
                    && strcmp(strlower(name), "end") != 0)
   {
      n_items = 0;
      if(sscanf(line,"%d %d %[^#]",&idi,&idj,pline) <= 2)/* Not enough values */
         message(&nerrs,line,ERROR,NOPAIR);
      else
      {						/* Parse potential parameters */
	 (void)strcat(pline, "$");		/* Add marker to end	      */
         while(n_items < NPOTP && sscanf(pline,"%lf %[^#]", &p_tmp, pline) > 1 )
	    pot.p[n_items++] = p_tmp;
      }
      if (n_items < n_potpar)
         message(&nerrs,line,ERROR,NOPOTP,n_potpar);
      else				/* Test id pair and if OK store values*/
      {
         if(idi < 1 || idi >= max_id)
            message(&nerrs,line,ERROR,IDOUTR,idi);
         if(idj < 1 || idj >= max_id)
            message(&nerrs,line,ERROR,IDOUTR,idj);
         if(!(  (*site_info)[idi].flag & S_USED
              &&(*site_info)[idj].flag & S_USED))
            message(&nerrs,line,WARNING,EXTPOT);
         pp1 = (*pot_ptr) + (idi + idj * system->max_id);
         if(pp1->flag & S_USED)		/* pot'l for this id pair already set?*/
            message(&nerrs,line,ERROR,DUPPOT);
         else				/* Put values into pp1  and  pp2      */
         {
            (*pot_ptr)[idi + idj * system->max_id] = pot;
            (*pot_ptr)[idj + idi * system->max_id] = pot;
         }
      }
   }
   /* Check whether potentials have been defined for all 'used' site id's     */
   for(idi = 0; idi < max_id; idi++)
      for(idj = idi; idj < max_id; idj++)
         if( (   (*site_info)[idi].flag & S_USED    /* True if sites idi, idj */
              && (*site_info)[idj].flag & S_USED)   /* both used but pot'l not*/
            && !((*pot_ptr)[idi + max_id * idj].flag & S_USED))
            message(&nerrs,NULLP,WARNING,NOPOT,idi,idj);

   if(nerrs > 0)			/* if any errors have been detected   */
      message(&nerrs,NULLP,FATAL,ERRS,nerrs);
   else
      message(&nerrs,NULLP,INFO,SUCCES);
}
/******************************************************************************
 * lattice_start   Initialse the simulation co-ordinates on a lattice. Read   *
 * from the end of the system specification file.  The format is one line     *
 * specifying the unit cell (6 x floating point + 3 x int # cells)	      *
 *    a  b  c  alpha  beta  gamma  nx ny nz                                   *
 * followed by one line for each molecule in the unit cell:                   *
 *    species  x  y  z  q0  q1  q2  q3                                        *
 * 'species'  is the name,  x, y, z are FRACTIONAL co-ords and 4 quaternions. *
 ******************************************************************************/
void	lattice_start(file, system, species, qpf)
FILE	*file; 				/* File to read info from	      */
system_mp system;			/* System info struct		      */
spec_mp	species;			/* Array of species info structs      */
quat_mt	qpf[];				/* Princ frame rotation quaternion    */
{
   typedef struct init_s {int species;  struct init_s *next;
                  double r[3], q[4];} init_mt; 	/* For linked list of coords  */
   init_mt	*cur, *init = NULL;		/* Current and header of list */
   double	a, b, c, calpha, cbeta, cgamma;	/* Unit cell lengths, angles  */
   int		ix, iy, iz, nx, ny, nz;		/* Number of unit cells in MDC*/
   spec_mp	spec;
   char		line[LLEN], name[LLEN];
   int		n_items, nerrs = 0, ispec, imol, i;
   int		*nmols = ialloc(system->nspecies);
   real		ca, cb, cg, sg;
   quat_mt	q;

   memst(nmols,0,system->nspecies*sizeof(int));
   n_items = sscanf(get_line(line,LLEN,file),"%lf%lf%lf%lf%lf%lf%d%d%d",
		    &a, &b, &c, &calpha, &cbeta, &cgamma, &nx, &ny, &nz);
   if(n_items <= 0 )
      message(NULLI, NULLP, FATAL, SYSEOF, "lattice start file");
   if(n_items < 9)
      message(&nerrs, line, ERROR, NOCELL);
   if( ! (a > 0 && b > 0 && c > 0 && nx > 0 && ny > 0 && nz > 0 &&
	  calpha > 0 && calpha < 180.0 && cbeta > 0 && cbeta < 180.0 &&
	  cgamma > 0 && cgamma < 180.0))
      message(&nerrs, line,  ERROR, INVCEL);

   ca = cos(calpha*DTOR); cb = cos(cbeta*DTOR); cg = cos(cgamma*DTOR);
   sg = sin(cgamma*DTOR);

   system->h[0][0] = nx*a;			/* Set up MD cell matrix      */
   system->h[0][1] = ny*b * cg;			/* from lengths and angles.   */
   system->h[1][1] = ny*b * sg;
   system->h[0][2] = nz*c * cb;
   system->h[1][2] = nz*c / sg * (ca - cb*cg);
   system->h[2][2] = nz*c / sg * sqrt(1 - ca*ca - cb*cb - cg*cg + 2*ca*cb*cg);

   while(sscanf(get_line(line,LLEN,file), "%s", name) > 0 &&
	 strcmp(strlower(name), "end") != 0)	/* Cycle over lines in file   */
   {
      cur =aalloc(1, init_mt );			/* Get struct for new molecule*/
      cur->next = init;  init = cur;		/* Link it into list	      */
      n_items = sscanf(line, "%s%lf%lf%lf%lf%lf%lf%lf",
		       name, &cur->r[0], &cur->r[1], &cur->r[2],
		       &cur->q[0], &cur->q[1], &cur->q[2], &cur->q[3]);
      if(n_items > 1)				/* Have name of molecule      */
      {
	 for (spec = species; spec < species+system->nspecies; spec++)
	    if(strcmp(strlower(name),strlower(spec->name)) == 0)
	       break;
	 if(spec >= species+system->nspecies)	/* Didn't find it	      */
	    message(&nerrs,NULLP,ERROR,UNKSPE,name);
	 else					/* Found it - check values    */
	 {
	    cur->species = spec-species;
	    if(n_items < 4)
	       message(&nerrs, line, ERROR, FEWCOO, name);
	    if(cur->r[0] < 0 || cur->r[0] >= 1 ||
	       cur->r[1] < 0 || cur->r[1] >= 1 ||
	       cur->r[2] < 0 || cur->r[2] >= 1)
	       message(&nerrs,NULLP,ERROR,FRACCO,cur->r[0],cur->r[1],cur->r[2]);

	    if(species[cur->species].rdof != 0)
	    {
	       if( n_items < 8 )
		  message(&nerrs, line, ERROR, FEWQUA, name);
	       if(fabs(1.0 - SQR(cur->q[0]) - SQR(cur->q[1])
		           - SQR(cur->q[2]) - SQR(cur->q[3])) > 1e-4)
		  message(&nerrs,NULLP,ERROR,QNORM,
			  cur->q[0],cur->q[1],cur->q[2],cur->q[3]);
	    }
	    nmols[cur->species] += nx*ny*nz;
	 }
      }
   }
   for (spec = species; spec < species+system->nspecies; spec++)
   {
      ispec = spec-species;
      if(nmols[ispec] != spec->nmols)
         message(&nerrs,NULLP,ERROR,NIMOLS,spec->name,
		 nmols[ispec],spec->nmols);
      nmols[ispec] = 0;
   }

   if(nerrs > 0)				/* Is file all correct?       */
      message(NULLI, NULLP, FATAL, INITER, nerrs);

   for(cur = init; cur != NULL; cur = cur->next)
   {
      spec = species + cur->species;
      for(ix = 0; ix < nx; ix++)
         for(iy = 0; iy < ny; iy++)
            for(iz = 0; iz < nz; iz++)
	    {
	       imol = nmols[cur->species];
	       spec->c_of_m[imol][0] = (cur->r[0]+ix)/nx - 0.5;
	       spec->c_of_m[imol][1] = (cur->r[1]+iy)/ny - 0.5;
	       spec->c_of_m[imol][2] = (cur->r[2]+iz)/nz - 0.5;
	       if(spec->rdof > 0 )
	       {
		  for( i = 0; i < 4; i++ )
		     q[i] = cur->q[i];		/* Convert type to 'real'     */
		  q_mul_1(q, qpf[cur->species], spec->quat[imol]);
	       }
	       nmols[cur->species]++;
	    }
   }
   message(NULLI, NULLP, INFO, LATTIC);
}
/*******************************************************************************
 * assign()  Convert string value by format and assign to pointer location.    *
 ******************************************************************************/
static
int assign(strval, fmt, ptr)
char	*strval, *fmt;
gptr	*ptr;
{
   int len = strlen(fmt);
   int code = fmt[MAX(0,len-1)];
   if( len > 2 && fmt[len-2] == 'l' ) code = toupper(code);

   switch(code)
   {
    case 's':
    case ']':
      return sscanf(strval, fmt, (char*)ptr);
    case 'd':
      return sscanf(strval, fmt, (int*)ptr);
    case 'D':
      return sscanf(strval, fmt, (long*)ptr);
    case 'f':
      return sscanf(strval, fmt, (float*)ptr);
    case 'F':
      return sscanf(strval, fmt, (double*)ptr);
    default:
      message(NULLI, NULLP, FATAL,
	      "Scanf code \"%s\" not catered for", fmt);
   }
   return -1;		/* This statement is never reached		*/
}
/******************************************************************************
 *  read_control.   Read the control parameters from the standard input.      *
 *  Input lines are of the form " key = value ", one per line.  The struct    *
 *  'match' is searched for a matching key, and if found converts the value   *
 *  according to the format string and stores it at the value of the pointer  *
 *  in 'match'.	"name=" with no value means assign a null string. 	      *
 ******************************************************************************/
void	read_control(file,match)
FILE	 *file;
CONST match_mt *match;
{
   char		line[LLEN],
   		name[LLEN],
   		value[LLEN];
   int		n_items;
   int		nerrs = 0;
   CONST match_mt	*match_p;

   while( *get_line(line,LLEN,file) != '\0' )
   {
      n_items = sscanf(line, " %[^= ] = %127[^#]",name, value);
      if(!strcmp(strlower(name),"end"))
         break;
      if( n_items < 1 )
         message(&nerrs,line,ERROR,NOKEY);
      else
      {
	 for( match_p = match; match_p->key; match_p++ )
	    if(! strcmp(strlower(name), match_p->key))
	       break;
	 if( ! match_p->key )			/* Reached end without success*/
            message(&nerrs,line,ERROR,NOTFND,name);
         else					/* Found it, so convert value */
         {
            if( n_items == 1 )
	    {
	       if(strcmp(match_p->format,SFORM) == 0 )
		  *(char*)match_p->ptr = '\0';	/* name= - assign null */
	       else
		  message(&nerrs,line,ERROR,NOVAL,name);
	    }
	    else
	    {
               n_items = assign(value, match_p->format, match_p->ptr);
               if( n_items < 1 )
                  message(&nerrs,NULLP,ERROR,BADVAL,value,name);
	    }
         }
      }
   }
   if( nerrs > 0 )
      message(&nerrs,NULLP,FATAL,ERRCON,nerrs,(nerrs>1)?'s':' ');
   else
      message(&nerrs,NULLP,INFO,SUCCON);
}
$EOD
$!
$CREATE eigens.c
$DECK
#include    
#include    "defs.h"

/*							eigens.c
 *
 *	Eigenvalues and eigenvectors of a real symmetric matrix
 *
 *
 *
 * SYNOPSIS:
 *
 * int n;
 * double A[n*(n+1)/2], EV[n*n], E[n];
 * void eigens( A, EV, E, n );
 *
 *
 *
 * DESCRIPTION:
 *
 * The algorithm is due to J. vonNeumann.
 *                   -     -
 * A[] is a symmetric matrix stored in lower triangular form.
 * That is, A[ row, column ] = A[ (row*row+row)/2 + column ]
 * or equivalently with row and column interchanged.  The
 * indices row and column run from 0 through n-1.
 *
 * EV[] is the output matrix of eigenvectors stored columnwise.
 * That is, the elements of each eigenvector appear in sequential
 * memory order.  The jth element of the ith eigenvector is
 * EV[ n*i+j ] = EV[i][j].
 *
 * E[] is the output matrix of eigenvalues.  The ith element
 * of E corresponds to the ith eigenvector (the ith row of EV).
 *
 * On output, the matrix A will have been diagonalized and its
 * orginal contents are destroyed.
 *
 * ACCURACY:
 *
 * The error is controlled by an internal parameter called RANGE
 * which is set to 1e-10.  After diagonalization, the
 * off-diagonal elements of A will have been reduced by
 * this factor.
 *
 * ERROR MESSAGES:
 *
 * None.
 *
 */
/*
Copyright 1973, 1991 by Stephen L. Moshier
Copyleft version.
*/

void eigens( A, RR, E, N )
real A[], RR[], E[];
int N;
{
int IND, L, LL, LM, M, MM, MQ, I, J, IA, LQ;
int IQ, IM, IL, NLI, NMI;
real ANORM, ANORMX, AIA, THR, ALM, ALL, AMM, X, Y;
real SINX, SINX2, COSX, COSX2, SINCS, AIL, AIM;
real RLI, RMI;
static real RANGE;
double precision();

RANGE=precision();
/* Initialize identity matrix in RR[] */
for( J=0; J ANORMX )
{
THR=THR/N;

do
{ /* while IND != 0 */
IND = 0;

for( L=0; L M)
			IM=M+IQ;
		else
			IM=I+MQ;
		if(I >= L)
			IL=L+IQ;
		else
			IL=I+LQ;
		AIL=A[IL];
		AIM=A[IM];
		X=AIL*COSX-AIM*SINX;
		A[IM]=AIL*SINX+AIM*COSX;
		A[IL]=X;
		}
	NLI = N*L + I;
	NMI = N*M + I;
	RLI = RR[ NLI ];
	RMI = RR[ NMI ];
	RR[NLI]=RLI*COSX-RMI*SINX;
	RR[NMI]=RLI*SINX+RMI*COSX;
	}

	X=2.0*ALM*SINCS;
	A[LL]=ALL*COSX2+AMM*SINX2-X;
	A[MM]=ALL*SINX2+AMM*COSX2+X;
	A[LM]=(ALL-AMM)*SINCS+ALM*(COSX2-SINX2);
	} /* for M=L+1 to N-1 */
	} /* for L=0 to N-2 */

	}
while( IND != 0 );

} /* while THR > ANORMX */

done:	;

/* Extract eigenvalues from the reduced matrix */
L=0;
for( J=1; J<=N; J++ )
	{
	L=L+J;
	E[J-1]=A[L-1];
	}
}
$EOD
$!
$CREATE kernel.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * kernel	Functions to calculate the forces, potential and distant pot'l*
 *		correction for various potentials.  ALL POTENTIAL-DEPENDANT   *
 *		CODE is in this module.					      *
 * dist_pot()		Return distant-potential correction		      *
 * kernel()		Calculate pe and forces				      *
 * types[]		Array of names of potential function types.	      *
 * npotp[]		Array containing number of parameters for each type.  *
 * npott		size of above two arrays.			      *
 *		Since CRAY CC 4.0 won't vectorise library function calls, a   *
 *		FORTRAN equivalent to kernel() is provided in kernel.f. To    *
 *		use it, compile force.c with macro FKERNEL defined and link   *
 *		with it AND THIS MODULE.				      *
 ******************************************************************************
 *      Revision Log
 *       $Log: kernel.c,v $
 *       Revision 2.8  1996/10/04 17:27:24  keith
 *       Rescheduled line order to overlap divide/computation on DEC Alpha/T3D.
 *
 *       Revision 2.7  1994/06/08 13:22:31  keith
 *       Null update for version compatibility
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Added CONST qualifier to (re-)declarations of ANSI library
 * emulation routines to give reliable compilation even
 * without ANSI_LIBS macro. (#define's away for K&R
 * compilers)
 *
 * Revision 2.5  94/01/18  13:32:38  keith
 * Null update for XDR portability release
 * 
 * Revision 2.4  94/01/13  12:46:53  keith
 * Aedded distant porential correction for GENPOT potential (NTS 13/1/94.
 * 
 * 
 * Revision 2.3  93/10/28  10:27:57  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.0  93/03/15  14:49:11  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.17  93/03/12  12:24:46  keith
 * Got rid of unneccesary convex special case.
 * 
 * Revision 1.16  93/03/09  15:58:41  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.15  92/09/22  14:45:20  keith
 * A few efficiency improvements for the Titan
 * 
 * Revision 1.14  92/06/10  15:53:16  keith
 * Added new potential type "generic" for Neal.
 * 
 * Revision 1.13  91/08/16  15:25:35  keith
 * Checked error returns from fread, fwrite, fseek and fclose more
 * rigourously.   Called strerror() to report errors.
 * 
 * Revision 1.12  91/08/15  18:12:03  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_t and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.11  91/02/19  14:51:26  keith
 * Minor changes to get rid of misleading compiler warnings.
 * 
 * Revision 1.10  90/09/28  13:29:39  keith
 * Inserted braces around VECTORIZE directives and changed include files
 * for STARDtardent 3000 series (via cond. comp symbol "ardent").
 * 
 * Revision 1.9  90/05/21  15:29:00  keith
 * Moved definition of struct pot_dim[][] from convert.c to kernel.c.
 * 
 * Revision 1.8  90/05/02  13:04:52  keith
 * Tidied up and restructured code to improve readability.
 * Reduced number of scalar temporaries in vector loops - this is
 * necessary to vectorize under CRAY CC 5.0.
 * 
 * Revision 1.7  89/12/22  19:33:44  keith
 * Added p0-p3 - temporaries for pot[0..3] pointers within loops.
 * Some machines (eg stellar) generate better code this way.
 * 
 * Revision 1.6  89/12/15  12:56:53  keith
 * Added conditional ionclusion of  for stellar
 * 
 * Revision 1.5  89/11/20  13:29:26  keith
 * Replaced separate arrays "types" and "npotp" with array of structs "potspec"
 * 
 * Revision 1.4  89/10/12  16:42:37  keith
 * Eleminated a few inefficiencies in non-coulombic LJ and MCY loops.
 * 
 * Revision 1.3  89/07/04  18:42:07  keith
 * Fixed error in kernel and force which led to sites being allocated the
 * wrong potential parameters.  Needed extra parameter to kernel.
 * 
 * Revision 1.2  89/06/20  18:22:31  keith
 * Moved pot. par defs arrays 'types', 'npotp' and npott from input.c to kernel.c
 * 
 * Revision 1.1  89/04/20  16:00:45  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/kernel.c,v 2.8 1996/10/04 17:27:24 keith Exp $";
#endif
/*========================== Program include files ===========================*/
#include	"defs.h"
/*========================== Library include files ===========================*/
#ifdef stellar
#   include 
#else
#ifdef ardent
#   include 
#else
#   include 
#endif
#endif
/*========================== Program include files ===========================*/
#include "structs.h"
#include "messages.h"
/*========================== External function declarations ==================*/
#if defined(ANSI) || defined(__STDC__)
void	message(int *,...);		/* Write a warning or error message   */
#else
void	message();			/* Write a warning or error message   */
#endif
/*========================== Potential type specification ====================*/
CONST pots_mt	potspec[]  = {{"lennard-jones",2},  /* Name, index & # parms  */
		              {"buckingham",3},
                              {"mcy",4},
		              {"generic",6},
		              {0,0}};	            /* MUST be null-terminated*/
/*
 *  Array of dimensions of pot'l parameters.  Powers of {m,l,t} per parameter.
 */
CONST dim_mt   pot_dim[][NPOTP]= {{{1,2,-2},{0,1,0}},
                                 {{1,8,-2},{1,2,-2},{0,-1,0}},
		                 {{1,2,-2},{0,-1,0},{1,2,-2},{0,-1,0}},
			         {{1,2,-2},{0,-1,0},{1,14,-2},
			          {1,6,-2},{1,8,-2},{1,10,-2}}};

/*========================== Macros ==========================================*/

#define E1	 0.254829592		/* Polynomial Constants used in	      */
#define E2	-0.284496736		/* Evaluation of the complementary    */
#define E3	 1.421413741		/* Error function.  		      */
#define E4	-1.453152027		/* Approximation used is that of      */
#define E5	 1.061405429		/* Abramowitz & Stegun p299.	      */

#define PP	 0.3275911

#define POLY5(t)   ((t)*(E1 + (t)*(E2 + (t)*(E3 + (t)*(E4 + (t)*E5)))))

#define LJPOT 0
#define E6POT 1
#define MCYPOT 2
#define GENPOT 3
/*============================================================================*/
/******************************************************************************
 *  dist_pot   return attractive part of potential integrated from cutoff to  *
 *  infinity for distant pressure calculation.				      *
 ******************************************************************************/
double	dist_pot(potpar, cutoff, ptype)
real	potpar[];			/* Array of potential parameters      */
double	cutoff;				/* Cutoff distance		      */
int	ptype;				/* Potential type selector	      */
{
   switch(ptype)
   {
    default:
      message(NULLI, NULLP, FATAL, UNKPTY, ptype);
    case LJPOT:
      return(potpar[0]*CUBE(SQR(potpar[1])/cutoff) / 3.0);
    case E6POT:
      return(potpar[0] / ( 3.0*CUBE(cutoff)));
    case MCYPOT:
      if( potpar[3] != 0.0 )
         return( potpar[2] * (SQR(cutoff)/potpar[3] + 2*cutoff/SQR(potpar[3])
	   		   + 2.0 / CUBE(potpar[3])) * exp(-potpar[3]*cutoff));
      else
         return( 0.0 );
    case GENPOT:
      return ( potpar[4] / ( 3.0*CUBE(cutoff)) + potpar[3] / cutoff);
   }
}
/******************************************************************************
 *  kernel   Innermost loop of force calculation.  Takes a vector of squared  *
 *  atomic distances (r_sqr), charges (chg) , pot'l parameters (pot[which]),  *
 *  and returns a vector of forces (forceij) and the potential energy (pe)    *
 ******************************************************************************/
void	kernel(jmin, nnab, forceij, pe, r_sqr, nab_chg, chg, norm,
	       alpha, ptype, pot)
int	jmin, nnab;		/* Lower and upper limits for vectors.   (in) */
int	ptype;			/* Index of potential type in potspec[]. (in) */
double	*pe;			/* Potential energy accumulator.     (in/out) */
double	alpha, norm;		/* Ewald parameter and 2*alpha/sqrt(pi). (in) */
double	chg;			/* Electric charge of reference site.    (in) */
real	forceij[];		/* Vector of dU(r)/dr for each in r_sqr.(out) */
real	r_sqr[];		/* Vector of site-site distances (**2).  (in) */
real	nab_chg[];		/* Vector of charges of neighbour sites. (in) */
real	*pot[];			/* Vectors of potential parameters.	 (in) */
{
   register real t, ar;			/* Argument of erfc() polynomial.     */
   register real r;			/* Site-site distance.		      */
   register real r_r, r_6_r, r_sqr_r, r_12_r,	/* Reciprocal powers of r.    */
                 r_4_r, r_8_r;
   register real erfc_term;		/* Intermediates in erfc calculation. */
   	    real ppe = 0.0;		/* Local accumulator of pot. energy.  */
   	    real exp_f1, exp_f2;	/* Temporary for b*exp(-cr) etc       */
   register int	jsite;			/* Loop counter for vectors.	      */
   real *p0 = pot[0], *p1 = pot[1],     /* Local bases for arrays of pot'l    */
        *p2 = pot[2], *p3 = pot[3],     /* parameters.			      */
        *p4 = pot[4], *p5 = pot[5];

   if(alpha > 0.0)
      switch(ptype)
      {
       default:
	 message(NULLI, NULLP, FATAL, UNKPTY, ptype);
       case LJPOT:
VECTORIZE
         for(jsite=jmin; jsite < nnab; jsite++)
	 {
	    /*
	     * Calculate r and coulombic part
	     */
	    r       = sqrt(r_sqr[jsite]);
	    ar	    = alpha*r;
	    t = 1.0/(1.0+PP*ar);
	    erfc_term = nab_chg[jsite]* chg * exp(-SQR(ar));
	    r_r	 = 1.0 / r;
	    t = POLY5(t) * erfc_term * r_r;
	    erfc_term = t + norm * erfc_term;
	    r_sqr_r = SQR(r_r);
	    /*
	     * Non-coulombic ie potential-specific part
	     */
	    r_6_r = SQR(p1[jsite])* r_sqr_r;
	    r_6_r   = CUBE(r_6_r);
	    r_12_r  = SQR(r_6_r);
	    ppe += t + p0[jsite]*(r_12_r - r_6_r);
	    forceij[jsite] = r_sqr_r*(6.0*p0[jsite]*(2*r_12_r - r_6_r)
				      + erfc_term);
	 }
	 break;
       case E6POT:
VECTORIZE
         for(jsite=jmin; jsite < nnab; jsite++)
	 {
	    /*
	     * Calculate r and coulombic part
	     */
	    r       = sqrt(r_sqr[jsite]);
	    ar	    = alpha*r;
	    t = 1.0/(1.0+PP*ar);
	    erfc_term = nab_chg[jsite]* chg * exp(-SQR(ar));
	    r_r	 = 1.0 / r;
	    t = POLY5(t) * erfc_term * r_r;
	    erfc_term = t + norm * erfc_term;
	    r_sqr_r = SQR(r_r);
	    /*
	     * Non-coulombic ie potential-specific part
	     */
	    exp_f1 = p1[jsite] * exp(-p2[jsite] * r);
	    r_6_r   = p0[jsite] * CUBE(r_sqr_r);
	    ppe += t - r_6_r + exp_f1;
	    forceij[jsite] = r_sqr_r*(-6.0* r_6_r+ erfc_term)
	                             + p2[jsite]*exp_f1 * r_r;
	 }
	 break;
       case MCYPOT:
VECTORIZE
         for(jsite=jmin; jsite < nnab; jsite++)
	 {
	    /*
	     * Calculate r and coulombic part
	     */
	    r       = sqrt(r_sqr[jsite]);
	    ar	    = alpha*r;
	    t = 1.0/(1.0+PP*ar);
	    erfc_term = nab_chg[jsite]* chg * exp(-SQR(ar));
	    r_r	 = 1.0 / r;
	    t = POLY5(t) * erfc_term * r_r;
	    erfc_term = t + norm * erfc_term;
	    r_sqr_r = SQR(r_r);
            /*
	     * Non-coulombic ie potential-specific part
	     */
	    exp_f1 =  p0[jsite] * exp(-p1[jsite]*r);
	    exp_f2 = -p2[jsite] * exp(-p3[jsite]*r);
	    ppe +=t + exp_f1 + exp_f2;
	    forceij[jsite] = (p1[jsite]*exp_f1 + p3[jsite]*exp_f2) * r_r
	    + erfc_term * r_sqr_r;
	 }
	 break;      
       case GENPOT:
VECTORIZE
         for(jsite=jmin; jsite < nnab; jsite++)
	 {
	    /*
	     * Calculate r and coulombic part
	     */
	    r       = sqrt(r_sqr[jsite]);
	    ar	    = alpha*r;
	    t = 1.0/(1.0+PP*ar);
	    erfc_term = nab_chg[jsite]* chg * exp(-SQR(ar));
	    r_r	 = 1.0 / r;
	    t = POLY5(t) * erfc_term * r_r;
	    erfc_term = t + norm * erfc_term;
	    r_sqr_r = SQR(r_r);
            /*
	     * Non-coulombic ie potential-specific part
	     */
	    exp_f1 =  p0[jsite] * exp(-p1[jsite]*r);
	    r_4_r = SQR(r_sqr_r);
	    r_6_r = r_sqr_r * r_4_r;
	    r_8_r = p5[jsite] * SQR(r_4_r);
	    r_12_r = p2[jsite] * SQR(r_6_r);
	    r_4_r *= p3[jsite];
	    r_6_r *= p4[jsite];

	    ppe += t + exp_f1 + r_12_r -r_4_r - r_6_r - r_8_r;
	    forceij[jsite] = r_sqr_r*( 12.0*r_12_r - 4.0*r_4_r - 6.0*r_6_r 
				      - 8.0*r_8_r + erfc_term)
	                   + p1[jsite]*exp_f1 * r_r;
	 }
	 break;      
      }
   else
      switch(ptype)
      {
       default:
	 message(NULLI, NULLP, FATAL, UNKPTY, ptype);
       case LJPOT:
VECTORIZE
         for(jsite=jmin; jsite < nnab; jsite++)
	 {
	    r_sqr_r = 1.0 / r_sqr[jsite];
	    r_6_r = SQR(p1[jsite])* r_sqr_r;
	    r_6_r   = CUBE(r_6_r);
	    r_12_r  = SQR(r_6_r);
	    ppe += p0[jsite]*(r_12_r - r_6_r);
	    forceij[jsite] = r_sqr_r*6.0*p0[jsite]*(2*r_12_r - r_6_r);
	 }
	 break;
       case E6POT:
VECTORIZE
         for(jsite=jmin; jsite < nnab; jsite++)
	 {
	    r       = sqrt(r_sqr[jsite]);
	    r_r	 = 1.0 / r;
	    exp_f1 = p1[jsite] * exp(-p2[jsite] * r);
	    r_sqr_r = SQR(r_r);
	    r_6_r   = p0[jsite] * r_sqr_r * r_sqr_r * r_sqr_r;
	    ppe +=  - r_6_r + exp_f1;
	    forceij[jsite] = -r_sqr_r * 6.0 * r_6_r
	                     + p2[jsite]*exp_f1 * r_r;
	 }
	 break;
       case MCYPOT:
VECTORIZE
         for(jsite=jmin; jsite < nnab; jsite++)
	 {
	    r       = sqrt(r_sqr[jsite]);
	    r_r	 = 1.0 / r;
	    exp_f1 =  p0[jsite] * exp(-p1[jsite]*r);
	    exp_f2 = -p2[jsite] * exp(-p3[jsite]*r);
	    ppe += exp_f1 + exp_f2;
	    forceij[jsite] = (p1[jsite]*exp_f1 + p3[jsite]*exp_f2) *r_r;
	 }
	 break; 
       case GENPOT:
VECTORIZE
         for(jsite=jmin; jsite < nnab; jsite++)
	 {
	    r       = sqrt(r_sqr[jsite]);
	    r_r	 = 1.0 / r;
	    exp_f1 =  p0[jsite] * exp(-p1[jsite]*r);
	    r_sqr_r = SQR(r_r);
	    r_4_r = SQR(r_sqr_r);
	    r_6_r = r_sqr_r * r_4_r;
	    r_8_r = p5[jsite] * SQR(r_4_r);
	    r_12_r = p2[jsite] * SQR(r_6_r);
	    r_4_r *= p3[jsite];
	    r_6_r *= p4[jsite];

	    ppe += exp_f1 + r_12_r -r_4_r - r_6_r - r_8_r;
	    forceij[jsite] = r_sqr_r*( 12.0*r_12_r - 4.0*r_4_r - 6.0*r_6_r 
				      - 8.0*r_8_r)
	                   + p1[jsite]*exp_f1 * r_r;
	 }
	 break;      
      }     
   *pe += ppe;
}
$EOD
$!
$CREATE main.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * MAIN		Driver program for MOLDY.  Calls startup to read input and    *
 *		control parameters and set up the simulation variables and    *
 *		executes the main cycle over timesteps.  It also contains     *
 *		the definition of the 'control' struct with the default	      *
 *		values of the simulation control parameters.		      *
 ******************************************************************************
 *      Revision Log
 * $Log: main.c,v $
 * Revision 2.12  1996/09/03 15:01:12  keith
 * Added test for divergent parallel trajectories.
 *
 * Revision 2.11  1996/03/19 15:48:56  keith
 * Timing message now printed on thread zero only.
 *
 * Revision 2.11  1996/03/19 15:48:22  keith
 * *** empty log message ***
 *
 * Revision 2.10  1996/01/15 16:54:43  keith
 * De lint-ed code, added prototypes etc.
 *
 * Made averages calculation output on thread zero only.
 * Added code for parallel RDF calculation, summing data from each thread
 *
 * Revision 2.9  1995/10/25 11:59:45  keith
 * Added test to avoid attemted open of backup file with null name.
 *
 * Revision 2.8  1994/07/07  16:57:01  keith
 * Updated for parallel execution on SPMD machines.
 * Interface to MP library routines hidden by par_*() calls.
 * Compile with -DSPMD to activate
 *
 * Revision 2.7  1994/06/08  13:14:37  keith
 * Changed all timestep-related parameters to type "long". This means
 * that 16-bit DOS compilers can do more than 32767 timesteps.
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Added CONST qualifier to (re-)declarations of ANSI library
 * emulation routines to give reliable compilation even
 * without ANSI_LIBS macro. (#define's away for K&R
 * compilers)
 *
 * Revision 2.5  94/01/18  13:32:42  keith
 * Null update for XDR portability release
 * 
 * Revision 2.3  93/10/28  10:27:59  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.0  93/03/15  14:49:13  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.20  93/03/09  15:58:58  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.19  92/10/28  14:09:30  keith
 * Changed "site_[tp]" typedefs to avoid name clash on HP.
 * 
 * Revision 1.18  92/06/12  12:55:58  keith
 * Mods to make it work on VMS again.  Ugh.
 * 
 * Revision 1.17  92/06/11  20:31:49  keith
 * Added file locking against multiple runs using same dump or backup files.
 * 
 * Revision 1.16  91/08/15  18:12:06  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_t and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.15  91/03/12  15:43:04  keith
 * Tidied up typedefs size_t and include file 
 * Added explicit function declarations.
 * 
 * Revision 1.14  91/02/21  15:27:19  keith
 * Mods for parallel version for titan added
 * 
 * Revision 1.13  90/05/16  14:20:04  keith
 * *** empty log message ***
 * 
 * Revision 1.12  90/04/14  17:53:41  keith
 * Added signal handler to catch CPU exceeded and TERM signal.
 * 
 * Revision 1.11  89/12/15  12:57:00  keith
 * Now prints elapsed as well as cpu time.
 * 
 * Revision 1.10  89/11/20  18:04:15  keith
 * Moved initialisation of control and units to 'input.c'
 * Added 2nd command line arg to specify output file.
 * 
 * Revision 1.9  89/11/20  12:02:09  keith
 * Changed interface to print_rdf.  cf rdf.c 1.6
 * Modified write of restart and backup files - added 'purge' call.
 * 
 * Revision 1.8  89/09/04  18:48:31  keith
 * Chhanged initialisation of 'control' commensurate with structs 1.6.1.2
 * 
 * Revision 1.7  89/08/10  17:30:54  keith
 * Fixed if statement so that rdf's started on rather than after 'begin-rdf'
 * 
 * Revision 1.6  89/07/05  18:19:37  keith
 * Code to support the portable text mode save configuration added.
 * Calculation of when to output averages and rdf data fixed to print
 * when (istep-start+1) % interval == 0. 
 * 
 * Revision 1.5  89/06/01  21:24:38  keith
 * Control.out eliminated, use printf and freopen instead to direct output.
 * 
 * Revision 1.4  89/05/24  11:08:19  keith
 * Fixed bug which called 'averages()' before and at begin-average.
 * Velocities are now rescaled up to and including scale_end.
 * Put better defaults for 'control' parameters.
 * 
 * Revision 1.3  89/05/22  14:05:34  keith
 * Added rescale-separately option, changed 'contr_t' format.
 * 
 * Revision 1.2  89/04/21  10:48:38  keith
 * Corrected bug which left step counter 1 too high at end of run
 * 
 * Revision 1.1  89/04/20  16:00:48  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/main.c,v 2.12 1996/09/03 15:01:12 keith Exp $";
#endif
/*========================== Program include files ===========================*/
#include	"defs.h"
/*========================== System include files ============================*/
#include	
#ifdef  SIGCPULIM			/* Alternative name to SIGXCPU.	      */
#define SIGXCPU SIGCPULIM
#endif		/* Unicos uses SIGCPULIM not SIGXCPU. */
#include	
/*========================== Program include files ===========================*/
#include	"structs.h"
#include	"messages.h"
/*========================== External function declarations ==================*/
void	start_up();
void	do_step();
void	values();
double	value();
void	averages();
void	output();
void	rescale();
void	dump();
void	print_rdf();
void	print_config();
double	cpu();
void	write_restart();
void	purge();
double  rt_clock();
gptr    *talloc();		       /* Interface to memory allocator       */
void    tfree();		       /* Free allocated memory	      	      */
#ifdef SPMD
void    par_begin();
void    par_sigintreset();
void    par_finish();
void    par_isum();
void    par_imax();
void    par_broadcast();
void    replicate();
#endif
#if defined(ANSI) || defined(__STDC__)
void	note(char *, ...);		/* Write a message to the output file */
void	message(int *, ...);		/* Write a warning or error message   */
#else
void	note();				/* Write a message to the output file */
void	message();			/* Write a warning or error message   */
#endif
void	rmlockfiles();			/* Delete all lock files.	      */
gptr    *rdf_ptr();                     /* Return ptr to start of rdf data    */
/*========================== External data definition ========================*/
contr_mt control;                           /* Main simulation control parms. */
int ithread=0, nthreads=1;
/*============================================================================*/
/******************************************************************************
 *  Signal handler.  Just set flag and return.				      *
 ******************************************************************************/
static int	sig_flag = 0;
static
void	shutdown(sig)
int sig;
{
   sig_flag = sig;
}
static
void	siglock(sig)
int sig;
{
   rmlockfiles();
#ifdef SPMD
   if( sig == SIGINT )
      par_sigintreset();
   else   
#endif
   signal(sig, SIG_DFL);
   raise(sig);
}
/******************************************************************************
 *  Main program.							      *
 ******************************************************************************/
int main(argc, argv)
int	argc;
char	*argv[];
{
   system_mt	system;
   spec_mt	*species;
   site_mt	*site_info;
   pot_mt	*potpar;
   restrt_mt	restart_header;
   int		backup_restart;
   static mat_mt	stress_vir;
   static double	pe[NPE];
   double t0, t00;
   double	delta_cpu = 0.0, cpu_base = cpu();
   double	rt = rt_clock();
   vec_mt	(*meansq_f_t)[2];
   vec_mt	dip_mom;
   int          *rdf_base;
   int          rdf_size;

#ifdef SPMD
   par_begin(&argc, &argv, &ithread, &nthreads);
#endif
#ifdef PARALLEL
# ifdef ardent
   int nthreads = nprocessors();
   int stacksize = 65536;
# endif
#endif

#if defined(SPMD) && !defined(READALL)
   if( ithread == 0 )
#endif
      start_up((argc>1)?argv[1]:"", (argc>2)?argv[2]:"",
	       &system, &species, &site_info, &potpar, 
	       &restart_header, &backup_restart);
#if defined(SPMD) && !defined(READALL)
   replicate(&control, &system, &species, &site_info, &potpar, 
	     &restart_header);
#endif
   rdf_base = (int*)rdf_ptr(&rdf_size);

   meansq_f_t = (vec_mt (*)[2])ralloc(2*system.nspecies);
   
   /*
    *  Set signal handlers -- attempt clean shutdown
    */
   (void)signal(SIGTERM, shutdown);
#ifdef SIGXCPU
   (void)signal(SIGXCPU, shutdown);
#endif
   /*
    *  Set signal handlers -- remove lock files and exit
    *  Check that the signal is actually defined if it is non-ansi
    */
#ifdef SIGHUP
   (void)signal(SIGHUP, siglock);
#endif
   (void)signal(SIGINT, siglock);
#ifdef SIGQUIT
   (void)signal(SIGQUIT, siglock);
#endif
#ifdef SIGABRT
   (void)signal(SIGABRT, siglock);
#endif
#ifdef PARALLEL
# ifdef ardent
   MT_SET_THREAD_NUMBER(&nthreads);
   MT_INIT(&stacksize);
# endif
#endif
   /*
    *  Main MD timestep loop
    */
   while( control.istep < control.nsteps && sig_flag == 0)
   {
      control.istep++;
      do_step(&system, species, site_info, potpar,
	      meansq_f_t, pe, dip_mom, stress_vir, 
	      &restart_header, backup_restart);
      
      values(&system, species, meansq_f_t, pe, dip_mom, stress_vir);
   
      if( ithread == 0 && control.istep % control.print_interval == 0)
	 output();

      if(control.scale_interval > 0)
      {
	 if(control.istep <= control.scale_end &&
	    control.istep % control.scale_interval == 0)
	    rescale(&system, species);
	 if( ithread == 0 && control.istep == control.scale_end)
	    note("Temperature scaling turned off after step %ld", control.istep);
      }

      if(control.average_interval > 0 && 
	 control.istep >= control.begin_average &&
	 ithread == 0)
      {
         if( control.istep == control.begin_average )
	 {
	    note("started accumulating thermodynamic averages on timestep %ld", 
		 control.istep);
	 }
         else if ( (control.istep-control.begin_average + 1) %
		    control.average_interval == 0)
            averages();
      }

      if(control.rdf_interval > 0 && control.istep >= control.begin_rdf && 
	 (control.istep-control.begin_rdf+1) % control.rdf_out == 0)
      {
#if defined(SPMD) && ! defined OLDRDF
	 par_isum(rdf_base, rdf_size);
	 if( ithread == 0 )
	    print_rdf(&system, species, site_info);
	 memst((gptr*)rdf_base, 0, rdf_size*sizeof(int));
#else
	 if( ithread == 0 )
	    print_rdf(&system, species, site_info);
#endif
      }

      if(control.backup_interval > 0 && control.backup_file[0] &&
	 control.istep % control.backup_interval == 0)
      {
#if defined(SPMD) && ! defined OLDRDF
	 par_isum(rdf_base, rdf_size);
	 if( ithread != 0 )
	    memst((gptr*)rdf_base, 0, rdf_size*sizeof(int));
#endif
	 if( ithread == 0 )
	 {
	    write_restart(control.backup_file, &restart_header,
			  &system, species, site_info, potpar);
	    purge(control.backup_file);
	 }
      }
      if(delta_cpu == 0.0) delta_cpu = cpu() - cpu_base;/* Time for a timestep*/
      if(  cpu()-cpu_base+delta_cpu >= control.cpu_limit )
	 sig_flag++; 	/* Cheat */
#ifdef SPMD
      /*
       * Better make sure every process wants to stop at the same time!
       */
      par_imax(&sig_flag);
      /*
       * Vital consistency check that all threads are absolutely in sync.
       * Use Temperature as it's a function of all KEs & therefore trajs.
       */
      if( control.istep%10 == 0)
      {
	 t0 = t00 = value(t_n,0);
	 par_broadcast(&t0, 1, sizeof(double), 0);
	 if( t0 != t00)
	    message(NULLI, NULLP, FATAL, DESYNC, ithread, t0, t00);
      }
#endif
   }					/* End of main MD timestep loop	      */
   
#if defined(SPMD) && ! defined OLDRDF
   par_isum(rdf_base, rdf_size);
#endif
   if( ithread == 0 )
   {
      if(control.istep < control.nsteps) /* Run ended prematurely	      */
      {
	 if(sig_flag == SIGTERM)
	    note("Run ended after step %ld - SIGTERM received", control.istep);
	 else
	    note("Run ended after step %ld - cpu limit exceeded", control.istep);
	 write_restart(control.backup_file, &restart_header, 
		       &system, species, site_info, potpar);
      }
      else if(control.save_file[0] != '\0')
      {
	 if( control.print_sysdef )
	    print_config(control.save_file, &system, species, site_info, potpar);
	 else
	    write_restart(control.save_file, &restart_header,
			  &system, species, site_info, potpar);
	 (void)remove(control.backup_file);		/* Get rid of backup */
      }
      else
	 (void)remove(control.backup_file);		/* Get rid of backup */

      rmlockfiles();
      printf(" *I* Run used %.2fs of CPU time and %.2fs elapsed\n", 
	     cpu()-cpu_base, rt_clock()-rt);
   }
#ifdef SPMD
   par_finish();
#endif
   return(0);
}
$EOD
$!
$CREATE matrix.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * matrix	Functions for manipulating 3x3 matrices			      *
 *		Contents:						      *
 * mat_vec_mul()	Multiply matrix by array of vectors		      *
 * mat_mul()		Multiply two 3x3 matrices			      *
 * mat_sca_mul()	Multiply 3x3 matrix by scalar			      *
 * mat_add()		Add two 3x3 matrices				      *
 * transpose()		Transpose a 3x3 matrix				      *
 * det()		Return determinant of 3x3 matrix		      *
 * invert()		Invert a 3x3 matrix				      *
 ******************************************************************************
 *      Revision Log
 *       $Log: matrix.c,v $
 *       Revision 2.7  1994/06/08 13:22:31  keith
 *       Null update for version compatibility
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Added CONST qualifier to (re-)declarations of ANSI library
 * emulation routines to give reliable compilation even
 * without ANSI_LIBS macro. (#define's away for K&R
 * compilers)
 *
 * Eliminated non-ANSI (and not portable to DOS) pointer
 * comparison.
 *
 * Revision 2.5  1994/01/18  13:32:44  keith
 * Null update for XDR portability release
 *
 * Revision 2.3  93/10/28  10:28:00  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.1  93/08/18  20:54:18  keith
 * Tidied up clashes over ABS, MIN, MAX macros.
 * 
 * Revision 2.0  93/03/15  14:49:14  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.7  93/03/09  15:59:01  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.6  91/08/15  18:12:08  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_t and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.5  90/09/28  13:29:43  keith
 * Inserted braces around VECTORIZE directives and changed include files
 * for STARDtardent 3000 series (via cond. comp symbol "ardent").
 * 
 * Revision 1.4  89/12/18  17:54:23  keith
 * Rewrote transpose() to compile correctly under -va option (for Stellar).
 * 
 * Revision 1.3  89/10/26  11:26:55  keith
 * Mat_vec_mul() vectorised.
 * 
 * Revision 1.2  89/10/24  17:16:21  keith
 * Modified transpose() so as not to vectorise under '-va' option on convex.
 * 
 * Revision 1.1  89/04/20  16:00:50  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/matrix.c,v 2.7 1994/06/08 13:22:31 keith stab $";
#endif
/*========================== Program include files ===========================*/
#include 	"defs.h"
#include 	"messages.h"
/*========================== Library include files ===========================*/
#include	"string.h"
/*========================== External function declarations ==================*/
#if defined(ANSI) || defined(__STDC__)
void		message(int *, ...);	/* Write a warning or error message   */
#else
void		message();		/* Write a warning or error message   */
#endif
/*============================================================================*/
#define ABS(x)		((x) > 0 ? (x) : -(x))
/******************************************************************************
 * 3 x 3  Matrix - vector multiply  (of multiple vectors)                     *
 * The input and output vectors need not necessarily be distinct              *
 ******************************************************************************/
void mat_vec_mul(m, in_vec, out_vec, number)
int		number;		/* Number of vectors to be multiplied         */
mat_mt		m;		/* Matrix                                     */
vec_mp		in_vec,		/* Input vector, [number][3]          (in/out)*/
		out_vec;	/* Output vector.  CAN BE SAME AS INPUT  (out)*/
{
   int i;
   register real	a0, a1, a2;
   
   /*
    * There are two blocks of code depending on whether the input and output  *
    * arrays are distinct.  If they are not, in_vec is explicitly assigned    *
    * otherwise there could be problems with optimising compilers. Also,      *
    * temporary storage is required to avoid writing over the input vector    *
    * and trying to re-use the 'old' values.
    */
   if(in_vec == out_vec)	/* ie parameters point to the same array      */
   {
VECTORIZE
      for(i = 0; i < number; i++)
      {
         a0 = in_vec[i][0];  a1 = in_vec[i][1];  a2 = in_vec[i][2];

         in_vec[i][0] = m[0][0]*a0 + m[0][1]*a1 + m[0][2]*a2;
         in_vec[i][1] = m[1][0]*a0 + m[1][1]*a1 + m[1][2]*a2;
         in_vec[i][2] = m[2][0]*a0 + m[2][1]*a1 + m[2][2]*a2;
      }
   }
   else 				 /* parameters are distinct arrays  */
   {
VECTORIZE
      for(i = 0; i < number; i++)
      {
         a0 = in_vec[i][0];  a1 = in_vec[i][1];  a2 = in_vec[i][2];

         out_vec[i][0] = m[0][0]*a0 + m[0][1]*a1 + m[0][2]*a2;
         out_vec[i][1] = m[1][0]*a0 + m[1][1]*a1 + m[1][2]*a2;
         out_vec[i][2] = m[2][0]*a0 + m[2][1]*a1 + m[2][2]*a2;
      }
   }
#if 0
   /* Can not test for overlap portably in ANSI C.			*/
   else			/* in_vec and out_vec overlap, but are not identical  */
      message(NULLI, NULLP, FATAL, OVRLP1, in_vec, out_vec, number);
#endif
}
/******************************************************************************
 * mat_mul   Multiply two 3 x 3 matrices. Result can NOT overwrite input.     *
 ******************************************************************************/
void mat_mul(a, b, c)
mat_mt	a,					/* Input matrix 1        (in) */
	b,					/* Input matrix 2        (in) */
	c;					/* Result matrix        (out) */
{
   register int	i, j;				/* Counters		      */
   
   if(c == a || c == b)
      message(NULLI, NULLP, FATAL, OVRLAP, "mat_mul");

   for(i = 0; i < 3; i++)
      for(j = 0; j < 3; j++)
         c[i][j] = a[i][0]*b[0][j] + a[i][1]*b[1][j] + a[i][2]*b[2][j];
}
/******************************************************************************
 *  mat_sca_mul.  Multiply a 3x3 matrix by a scalar                           *
 ******************************************************************************/
void mat_sca_mul(s, a, b)
register real	s;			/* Scalar			(in)  */
mat_mt	a, 				/* Input matrix			(in)  */
	b;				/* Result matrix	       (out)  */
{	 
   register int i, j;
   for(i = 0; i < 3; i++)
      for(j = 0; j < 3; j++)
         b[i][j] = s * a[i][j];
}            
/******************************************************************************
 * mat_add   Add two 3 x 3 matrices.                                          *
 ******************************************************************************/
void mat_add(a, b, c)
mat_mt	a,					/* Input matrix 1        (in) */
	b,					/* Input matrix 2        (in) */
	c;					/* Result matrix        (out) */
{
   register int	i, j;				/* Counters		      */
   
   for(i = 0; i < 3; i++)
      for(j = 0; j < 3; j++)
         c[i][j] = a[i][j] + b[i][j];
}
/******************************************************************************
 * Transpose  Transpose a 3 x 3 matrix.  Will handle case of a = b            *
 ******************************************************************************/
void transpose(a, b)
mat_mt	a,					/* Input matrix          (in) */
	b;					/* Transposed matrix    (out) */
{
   mat_mt tmp;

   memcp(tmp, a, sizeof tmp);

   b[0][0] = tmp[0][0];	b[1][1] = tmp[1][1];	b[2][2] = tmp[2][2];
   b[0][1] = tmp[1][0];	b[1][0] = tmp[0][1];
   b[0][2] = tmp[2][0];	b[2][0] = tmp[0][2];
   b[1][2] = tmp[2][1];	b[2][1] = tmp[1][2];
}
/******************************************************************************
 *  Det.  Determinant of a 3 x 3 matrix   				      *
 ******************************************************************************/
double det(a)
mat_mt	a;					/* Matrix		 (in) */
{
   int	i, j, k;				/* Counters		      */
   register double	deter = 0.0;
   
   for(i = 0, j = 1, k = 2; i < 3; i++, j=(j+1)%3, k=(k+1)%3)
      deter += a[0][i] * (a[1][j]*a[2][k] - a[1][k]*a[2][j]);
   return(deter);
}      
/******************************************************************************
 * invert.  Calculate the inverse of a 3x3 matrix.  Adjoint method.           *
 ******************************************************************************/
void invert(a, b)
mat_mt	a,					/* Input matrix          (in) */
	b;					/* Inverse matrix	(out) */
{
   int	i, j, k, l, m, n;			/* Counters		      */
   register real	deter;			/* Reciprocal of determinant  */
	
   if(a == b)
      message(NULLI, NULLP, FATAL, OVRLAP, "invert");
   if((deter = det(a)) == 0.0)
      message(NULLI, NULLP, FATAL, SNGMAT, "invert");
   deter = 1.0 / deter;
   for(i = 0, j = 1, k = 2; i < 3; i++, j=(j+1)%3, k=(k+1)%3)
      for(l = 0, m = 1, n = 2; l < 3; l++, m=(m+1)%3, n=(n+1)%3)
         b[l][i] = deter*(a[j][m]*a[k][n] - a[j][n]*a[k][m]);
}
$EOD
$!
$CREATE output.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * output	Contains various output and error handling functions, except  *
 *		for 'print_frame' and 'output' which, because of their	      *
 *		intimate connection with the averages/values database are     *
 *		located in "values.c".  Contents:			      *
 * new_line()		Write new line & manage page length		      *
 * new_page()		Start new output page				      *
 * put_line()		Write a line of symbols				      *
 * note()		Write a message to the output file		      *
 * message()		Write an error or warning message, possibly exiting   *
 * print_array()	\						      *
 * format_int()		 \   Internal (static) procedures for use by	      *
 * format_dbl()		 /		banner_page()			      *
 * format_vec()		/						      *
 * banner_page()	Write main startup banner and simulation parameters   *
 * print_sysdef()	Print system specification readable by read_sysdef()  *
 ******************************************************************************
 *      Revision Log
 *       $Log: output.c,v $
 *       Revision 2.11  1996/03/05 18:48:00  keith
 *       Removed a couplt of CONST declarations because IBL xlc compiler
 *       complained about them.
 *
 *       Revision 2.10  1995/12/07 17:54:06  keith
 *       Reworked V. Murashov's thermostat code.
 *       Convert mass params from kJ/mol ps^2 to prog units. Defaults=100.
 *
 *       Revision 2.9  1995/12/04 11:45:49  keith
 *       Nose-Hoover and Gaussian (Hoover constrained) thermostats added.
 *       Thanks to V. Murashov.
 *
 * Revision 2.8  1994/07/07  17:04:29  keith
 * Updated for parallel execution on SPMD machines.
 * Interface to MP library routines hidden by par_*() calls.
 * Compile with -DSPMD to activate.
 *
 * Revision 2.7  1994/06/08  13:15:58  keith
 * Changed all timestep-related parameters to type "long". This means
 * that 16-bit DOS compilers can do more than 32767 timesteps.
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Added CONST qualifier to (re-)declarations of ANSI library
 * emulation routines to give reliable compilation even
 * without ANSI_LIBS macro. (#define's away for K&R
 * compilers)
 *
 * Revision 2.5  94/01/21  12:21:22  keith
 * Corrected trivial and latent bug in print_config()
 * 
 * Revision 2.3  93/10/28  10:28:01  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.0  93/03/15  14:49:16  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.8.1.15  93/03/12  12:14:23  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.8.1.15  93/03/09  15:59:03  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.8.1.14  93/03/05  15:03:25  keith
 * Moved include of stdlib above stdio for non-ANSI gcc environments.
 * 
 * Revision 1.8.1.13  92/10/28  14:10:02  keith
 * Changed "site_[tp]" typedefs to avoid name clash on HP.
 * 
 * Revision 1.8.1.12  92/06/11  20:31:52  keith
 * Added file locking against multiple runs using same dump or backup files.
 * 
 * Revision 1.8.1.11  92/06/05  13:37:44  keith
 * Conditionally undefed va_dcl for ANSI, stdarg.h case --
 * just prevents warning from gcc.
 * 
 * Revision 1.8.1.10  92/03/11  12:56:18  keith
 * Changed "scale-separately" parameter to "scale options"
 * 
 * Revision 1.8.1.9  91/08/19  16:47:37  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_mt and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * 
 * Revision 1.8.1.8  91/08/16  15:25:59  keith
 * Checked error returns from fread, fwrite, fseek and fclose more
 * rigourously.   Called strerror() to report errors.
 * 
 * Revision 1.8.1.6  90/05/16  18:40:32  keith
 * Renamed own freer from cfree to tfree.
 * 
 * Revision 1.8.1.5  90/05/16  14:20:17  keith
 * *** empty log message ***
 * 
 * Revision 1.8.1.4  90/05/15  19:00:54  keith
 * Fixed error line 497 which wrapped on unsigned "strlen"
 * 
 * Revision 1.8.1.3  89/11/21  15:52:52  keith
 * Fixed format of "special" in accordance with altered struct type match_t.
 * 
 * Revision 1.8.1.1  89/11/20  13:30:06  keith
 * Replaced separate arrays "types" and "npotp" with array of structs "potspec"
 * 
 * Revision 1.7.1.2  89/09/04  17:56:35  keith
 * Added charge and dipole info to per-species output in banner_page()
 * 
 * Revision 1.8  89/09/04  17:53:43  keith
 * Added charge and dipole info to per-species output in banner_page()
 * 
 * Revision 1.7  89/08/11  10:53:38  keith
 * Tidied up loops over species to use pointer as counter
 * Fixed print_config() to convert control parameters to correct units
 * before outputting them.
 * Explicitly included stdio.h before varargs to get round VMS C problem.
 * 
 * Revision 1.6  89/07/04  18:46:01  keith
 * Print_config() added. Prints control, sys-spec and configurational info
 * which can be reread as a lattice start, for portable restart.
 * 
 * Revision 1.4  89/06/20  18:30:36  keith
 * moved print_sysdef() from input.c to output.c
 * made definitions of 'types[]' and 'npotp[]' external (in kernel).
 * Updated banner_page() to provide more info
 * 
 * Revision 1.3  89/06/01  21:25:07  keith
 * Control.out eliminated, use printf and freopen instead to direct output.
 * 
 * Revision 1.2  89/05/24  13:55:03  keith
 * Changed ifdef's to select on __STDC__ macro
 * Message() now prints to user specified output file after initial set up
 * 
 * Revision 1.1  89/04/27  16:52:19  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/output.c,v 2.11 1996/03/05 18:48:00 keith Exp $";
#endif
/*========================== Program include files ===========================*/
#include "defs.h"
/*========================== Library include files ===========================*/
#if defined(ANSI) || defined(__STDC__)
#include 	
#else
#include 	
#endif
#include 	
#include 	"stdlib.h"
#include	"stddef.h"
#include 	"string.h"
#include        
/*========================== Program include files ===========================*/
#include "structs.h"
#include "messages.h"
/*========================== External function declarations ==================*/
void	conv_control();			/* Unit conversion for 'control'      */
char	*atime();			/* Current date and time in ASCII     */
char	*cctime();			/* Convert long time to ASCII.	      */
void	rmlockfiles();
/*========================== External data references ========================*/
extern	      contr_mt 	control;	    /* Main simulation control parms. */
extern	CONST match_mt	match[];	    /* Control file keyword table.    */
extern  CONST pots_mt	potspec[];	    /* Potential type specification   */
extern int ithread, nthreads;
/*========================== External data definitions  ======================*/
static  int	out_page = 1;		    /* Which page of output we are on */
static  int	out_line = 999999;	    /* Which line of output           */
/*========================== Macros ==========================================*/
#define		S_USED		0x01
/*========================== Special Control output cases ====================*/
static        int	one=1;
extern	      unit_mt	prog_unit;
static	CONST match_mt	special[] = {
        {"lattice-start",	"%d", "",	(gptr*)&one},
	{"restart-file",	"%s", "",	(gptr*)""},
	{"sys-spec-file",	"%s", "",	(gptr*)""},
	{"mass-unit",		"%lf", "",	(gptr*)&prog_unit.m},
	{"length-unit",		"%lf", "",	(gptr*)&prog_unit.l},
	{"time-unit",		"%lf", "",	(gptr*)&prog_unit.t},
	{"charge-unit",		"%lf", "",	(gptr*)&prog_unit.q}
		      };
static	CONST int	nspecial = sizeof(special) / sizeof(match_mt);
/******************************************************************************
 * lines_left().  How many lines are left on page?			      *
 ******************************************************************************/
int lines_left()
{
   if( control.page_length > 0) 
      return MAX(0,control.page_length - out_line);
   else
      return 999999;
}
/******************************************************************************
 * new_line.   print a newline and update line counter                        *
 ******************************************************************************/
void	new_line()
{
   void	new_page();
   (void)putchar('\n');
   out_line++;
   if(out_line > control.page_length && control.page_length > 0)   new_page();
}
void	new_lins(n)
int	n;
{
   while(n-- > 0)
      new_line();
}
/******************************************************************************
 * new_page   Take a new page on the output and print a header                * 
 ******************************************************************************/
void	new_page()
{
   (void)putchar('\f');					/* Take new page      */
   out_line = 0;					/* Print page header  */
   (void)printf("\t%s\t%s\tPage %d", atime(), control.title, out_page++);
   new_line();
}
/******************************************************************************
 *  Banner line.							      *
 ******************************************************************************/
void	put_line(c)
int	c;
{
   int n = control.page_width;
   while(n-- > 0)
      (void)putchar(c);
   new_line();
}
/******************************************************************************
 *  message.   Deliver error message to possibly exiting.  It can be called   *
 *	       BEFORE output file is opened, in which case outt to stderr.    *
 ******************************************************************************/
#if defined(ANSI) || defined(__STDC__)
#   undef  va_alist
#   define	va_alist int *nerrs, ...
#   ifdef va_dcl
#      undef va_dcl
#   endif
#   define va_dcl /* */
#endif
/*VARARGS*/
void	message(va_alist)
va_dcl
{
   va_list	ap;
   char		*buff;
   int		sev;
   char		*format;
   static char	*sev_txt[] = {" *I* "," *W* "," *E* "," *F* "};
#if defined(ANSI) || defined(__STDC__)
   va_start(ap, nerrs);
#else
   int		*nerrs;

   va_start(ap);
   nerrs = va_arg(ap, int *);
#endif

   buff  = va_arg(ap, char *);
   sev   = va_arg(ap, int);
   format= va_arg(ap, char *);

   if( ithread == 0 || abs(sev) == FATAL)
   {
      (void)printf(sev_txt[abs(sev)]);
      (void)vprintf(format, ap);
      new_line();		      /* To maintain pagination	      */

      if(buff != NULL)                /* null ptr means don't print buffer  */
      {
	 (void)printf("     buffer contents=\"%s\"",buff);
	 new_line();
      }
   }
   va_end(ap);
   if(sev >= ERROR && nerrs != NULL)
      (*nerrs)++;
   if(sev == FATAL)
      rmlockfiles();
   if(abs(sev) == FATAL)
   {
#ifdef SPMD
      par_abort(3);
#endif
      exit(3);
   }
}
/******************************************************************************
 *  note   write a message to the output file				      *
 ******************************************************************************/
#if defined(ANSI) || defined(__STDC__)
#undef  va_alist
#define	va_alist char *text, ...
#define va_dcl /* */
#endif
/*VARARGS*/
void	note(va_alist)
va_dcl
{
   va_list	ap;
#if defined(ANSI) || defined(__STDC__)
   va_start(ap, text);
#else
   char		*text;

   va_start(ap);
   text = va_arg(ap, char *);
#endif

   if( ithread > 0 )
      return;

   (void)printf(" *I* "); 
   (void)vprintf( text, ap);  new_line();
   va_end(ap);
}
/******************************************************************************
 *  Print_array    Print out an array of strings in a common format 	      *
 ******************************************************************************/
static void	print_array(text, n)
char	*text[];
size_mt	n;
{
   int i;
   for(i=0; ih;
   char		version[132], *vsn=version;

   new_page(); new_lins(2);
   print_array( banner, lsizeof banner / sizeof(char*));
   (void)sprintf(version, "Version %.*s (%.*s) %.*s",
		 	(int)strlen(Revision+11)-1,      Revision+11,
		 	(int)strlen(Revision_State+8)-1, Revision_State+8,
		 	(int)strlen(Revision_Date+7)-1,  Revision_Date+7);
   print_array( &vsn, (size_mt)1);
   print_array( name_addr, lsizeof name_addr / sizeof(char*));
   print_array( copy_notice, lsizeof copy_notice / sizeof(char*));
   if(control.restart_file[0] != '\0')
      if(control.new_sysdef)
         (void)printf( " New system specification read in from file %s",
		       control.sysdef);
      else
         (void)printf( " System specification read in from restart file %s",
		       control.restart_file);
   else
      (void)printf( " System specification read in from file %s",
		    control.sysdef);
   new_line();

   for(spec = species; spec < &species[system->nspecies]; spec++)
   {
      (void)printf(" %s", spec->name); new_line();
      format_int("Number of molecules",spec->nmols);
      format_int("Number of sites",spec->nsites);
      format_dbl("Mass",spec->mass,MUNIT_N);
      format_dbl("Electric Charge", spec->charge*CONV_Q,CONV_Q_N);
      if(spec->nsites > 1 )
	 format_dbl("Dipole moment",spec->dipole*CONV_D,CONV_D_N);
      if(spec->rdof == 0)
      {
	 (void)printf(
	     "\t%s molecule has no rotational degrees of freedom", spec->name);
	 new_line();
      }
      else
      {
	 if(spec->rdof == 2)
	 {
	    (void)printf("\t%s molecule is linear",spec->name);
	    new_line();
	 }
	 format_vec("Moments of inertia",
		    spec->inertia[0],spec->inertia[1],spec->inertia[2],IUNIT_N);
      }
   }
   new_line();
   (void)printf(" MD cell vectors"); new_line();
   format_vec("a",h[0][0],h[1][0],h[2][0],LUNIT_N);
   format_vec("b",h[0][1],h[1][1],h[2][1],LUNIT_N);
   format_vec("c",h[0][2],h[1][2],h[2][2],LUNIT_N);
   (void)printf(" Run parameters"); new_line();
   if(control.istep > 0)
      format_long("Initial step",control.istep); 
   format_long("Final step",control.nsteps);
   format_dbl("Size of step",control.step,TUNIT_N);
   format_dbl("CPU limit",control.cpu_limit,"s");
   if(control.scale_interval > 0)
   {
      if( control.scale_options & 0x8 )
      {
	 (void)printf(" Velocities to be periodically RESET from MB distribution");
	 new_line();
      }
      else
      {
	 (void)printf(" Temperature will be scaled using %s kinetic energy",
		      control.scale_options & 0x4 ? 
		      "rolling average" : "instantaneous");
	 new_line();
	 if( control.scale_options & 0x3)
	 {
	    (void)printf(" (for ");
	    if( control.scale_options & 0x2 )
	    {
	       (void)printf("transl. and rotl.");
	       if( control.scale_options & 0x1 )
		  (void)printf(" and ");
	    }
	    if( control.scale_options & 0x1 )
	       (void)printf("each species");
	    
	    (void)printf(" individually)");
	    new_line();
	 }
      }
      format_long("No. steps between scalings",control.scale_interval);
      format_long("End scaling at step",control.scale_end);
   }
   if((control.scale_interval > 0) || (control.const_temp == 1))
      format_dbl("Applied Temperature",control.temp,"K");
   if(control.const_temp)
   {
      (void)printf(" %s thermostat will be used", control.const_temp == 1
                     ? "Nose-Hoover" : "Gaussian");
      new_line();
      if( control.scale_options & 0x3)
      {
         (void)printf(" (for ");
         if( control.scale_options & 0x2 )
         {
	    (void)printf("transl. and rotl.");
	    if( control.scale_options & 0x1 )
	       (void)printf(" and ");
         }
         if( control.scale_options & 0x1 )
	    (void)printf("each species");
	    
	 (void)printf(" individually)");
	 new_line();
      }
      if(control.const_temp == 1)
      {
         format_dbl("Translational temperature mass parameter ",
		    control.ttmass*CONV_TM, CONV_TM_N);
         format_dbl("Rotational temperature mass parameter    ",
		    control.rtmass*CONV_TM, CONV_TM_N);
      }
   }

   if(control.const_pressure)
   {
      (void)printf(" Constant stress ensemble will be used");
      new_line();
      format_dbl("Applied pressure", CONV_P*control.pressure,CONV_P_N);
      format_dbl("Mass parameter W",control.pmass,MUNIT_N);

      if(control.const_temp == 2)
         message(NULLI, NULLP, WARNING, GANDP);
   }
   format_dbl("Interaction cut-off",control.cutoff,LUNIT_N);
   if(control.alpha != 0.0)
   {
      format_dbl("Alpha parameter for Ewald sum",control.alpha,RLUNIT_N);
      format_dbl("Reciprocal space cut-off",control.k_cutoff,RLUNIT_N);
   }

   if( control.rdf_interval > 0 )
   {
      (void)printf(" Radial distribution functions will be calculated");
      new_line();
      format_long("Starting at timestep", control.begin_rdf);
      format_long("No. steps between binnings", control.rdf_interval);
      format_long("Calculate and print after", control.rdf_out);
   }

   if( control.dump_level > 0 && control.dump_interval > 0 )
   {
      (void)printf(" Configurational data will be dumped to file(s) %s",
		   control.dump_file);
      new_line();
      format_long("Starting at timestep", control.begin_dump);
      format_long("No. steps between dumps", control.dump_interval);
      format_int("Dump level", control.dump_level);
   }
   
   if(control.restart_file[0] == '\0')
   {
      (void)printf( " New run entitled \"%s\" started %s",
	      restart_header->title, restart_header->init_date);
      new_line();
   }
   else
   {
      (void)printf( " Run initialised from restart file %s written %s",
		    control.restart_file, cctime(&restart_header->timestamp));
      new_line();
      (void)printf( " This is restart No %d of run \"%s\" started %s",
	    restart_header->seq, restart_header->title, 
		                 restart_header->init_date);
      new_line();
   }
   (void)fflush(stdout);
}
/******************************************************************************
 *  print sysdef   Print out the definition of the system, in the format that *
 *  read_sysdef can interpret.                                                *
 ******************************************************************************/
static
void    print_sysdef(file, system, species, site_info, potpar)
FILE		*file;
system_mp       system;                 /* Pointer to system array (in main)  */
spec_mt         species[];              /* Pointer to species array           */
site_mp        site_info;              /* pointer to site_info array         */
pot_mt          potpar[];               /* Potential parameter array          */
{
   spec_mp      spec;
   int  isite, idi, idj, idij, ip;
   int  n_potpar = potspec[system->ptype].npar;
   for(spec = species; spec < &species[system->nspecies]; spec++)
   {
      (void)fprintf(file, " %-16s  %d  %s\n", spec->name, spec->nmols,
		    spec->framework ? "framework" : "");
      for(isite=0; isite < spec->nsites; isite++)
         (void)fprintf(file, " %6d %9g %9g %9g %9g %9g %s\n",
                        spec->site_id[isite],
                        spec->p_f_sites[isite][0],
                        spec->p_f_sites[isite][1],
                        spec->p_f_sites[isite][2],
                        site_info[spec->site_id[isite]].mass,
                        site_info[spec->site_id[isite]].charge,
                        site_info[spec->site_id[isite]].name);
   }
   (void)fprintf(file, " end\n");
   (void)fprintf(file," %s potential parameters\n",potspec[system->ptype].name);
   for(idi = 1; idi < system->max_id; idi++)
      for(idj = idi; idj < system->max_id; idj++)
      {
         idij = idj + idi * system->max_id;
         if(potpar[idij].flag & S_USED)
         {
            (void)fprintf(file, " %6d %6d", idi, idj);
            for(ip = 0; ip < n_potpar; ip++)
               (void)fprintf(file, " %9g",potpar[idij].p[ip]);
            (void)fputc('\n',file);
         }
      }
   (void)fprintf(file, " end\n");
}
/******************************************************************************
 * Print_config()	Print out the configuration of the system in a text   *
 * format.  Control parameters system definition and 'lattice start' are      *
 * output allowing a portable restart.					      *
 ******************************************************************************/
void	print_config(save_name, system, species, site_info, potpar)
char		*save_name;		/* Name of save file to be written    */
system_mp	system;			/* Pointer to system array (in main)  */
spec_mp		species;		/* Pointer to be set to species array */
site_mp		site_info;		/* To be pointed at site_info array   */
pot_mp		potpar;			/* To be pointed at potpar array      */
{
   FILE 	*out;
   CONST match_mt	*match_p, *cur, *special_p;
   spec_mp	spec;
   int		imol, code, i, j, k;
   double	cell_length[3], cell_angle[3];
   mat_mp	h = system->h;

   /*
    * Convert 'control' to input units for correct rereading.
    * Save current values and restore afterwards.
    */
   conv_control(&prog_unit, false);

   if( (out = fopen(save_name, "w")) == NULL )
      message(NULLI, NULLP, FATAL, OSFAIL, save_name);

   for( match_p = match; match_p->key; match_p++)
   {
      for( special_p = special; special_p < &special[nspecial]; special_p++)
	 if( ! strcmp(match_p->key, special_p->key) )
	    break;

      if( special_p < &special[nspecial] )
	 cur = special_p;
      else
	 cur = match_p;

      code = cur->format[MAX(0, (long)strlen(cur->format)-1)];
      switch(code)
      {
       case 's':
       case ']':
	 (void)fprintf(out, "%s = %s\n", cur->key, (char*)cur->ptr);
	 break;
       case 'd':
	 (void)fprintf(out, "%s = %d\n", cur->key, *(int*)cur->ptr);
	 break;
       case 'f':
	 (void)fprintf(out, "%s = %.7g\n", cur->key, *(double*)cur->ptr);
	 break;
       default:
	 message(NULLI, NULLP, FATAL,
		 "Printf code \"%s\" not catered for", cur->format);
      }
   }
   (void)fprintf(out, "end\n");

   for( i = 0; i < 3; i++)
      cell_length[i] = sqrt(SQR(h[0][i]) + SQR(h[1][i]) + SQR(h[2][i]));
   for( i=0, j=1, k=2; i < 3; i++, j=(i+1)%3, k=(j+1)%3)
      cell_angle[i] = acos(
			 (h[0][j]*h[0][k] + h[1][j]*h[1][k] + h[2][j]*h[2][k])/
			 (cell_length[j]*cell_length[k])) / DTOR;
   
   print_sysdef(out, system, species, site_info, potpar);

   (void)fprintf(out, "%g %g %g %g %g %g 1 1 1\n",
		 cell_length[0], cell_length[1], cell_length[2],
		 cell_angle[0], cell_angle[1], cell_angle[2]);
   for( spec = species; spec < &species[system->nspecies]; spec++ )
      for( imol = 0; imol < spec->nmols; imol++ )
      {
	 (void)fprintf(out, "%s %g %g %g",
		       spec->name,                spec->c_of_m[imol][0]+0.5,
		       spec->c_of_m[imol][1]+0.5, spec->c_of_m[imol][2]+0.5);
	 if( spec->rdof > 0 )
	    (void)fprintf(out, " %g %g %g %g\n",
			  spec->quat[imol][0], spec->quat[imol][1],
			  spec->quat[imol][2], spec->quat[imol][3]);
	 else
	    (void)fputc('\n', out);
      }
   (void)fprintf(out, "end\n");
   
   if( ferror(out) || fclose(out) )
      message(NULLI,NULLP,FATAL,REWRT,strerror(errno));

   conv_control(&prog_unit, true);
}

	 
$EOD
$!
$CREATE quaterns.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * quaterns	Functions for manipulating quaternions			      *
 ******************************************************************************
 *      Revision Log
 *       $Log: quaterns.c,v $
 *       Revision 2.7  1994/06/08 13:22:31  keith
 *       Null update for version compatibility
 *
 * Revision 2.5  1994/01/18  13:32:54  keith
 * Null update for XDR portability release
 *
 * Revision 2.3  93/10/28  10:28:05  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.0  93/03/15  14:49:18  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.2  93/03/09  15:59:06  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.1  89/04/20  16:00:52  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/quaterns.c,v 2.7 1994/06/08 13:22:31 keith stab $";
#endif
/*========================== Library include files ===========================*/
#include 
/*========================== Program include files ===========================*/
#include "defs.h"
/*========================== External function declarations ==================*/
double precision();
/*============================================================================*/
/******************************************************************************
 * Quaternion multiplier.  Multiplies arrays of quaternions p by q to give r  *
 * Can be called with  r the same as p or q.                                  *
 ******************************************************************************/
void q_mul(p, q, r, n)
quat_mp	p,			/* First Quaternion array [n][4]        (in)  */
	q,			/* Second quaternion array [n][4]       (in)  */
	r;			/* Resultant quaternions [n][4]        (out)  */
int	n;			/* Number of quaternions in the arrays  (in)  */
{
   register	int	i;
   register	real	p0, p1, p2, p3;
   register	real	q0, q1, q2, q3;
   
   for(i = 0; i < n; i++)
   {
      p0 =  p[i][0]; p1 =  p[i][1]; p2 =  p[i][2]; p3 =  p[i][3];
      q0 =  q[i][0]; q1 =  q[i][1]; q2 =  q[i][2]; q3 =  q[i][3];

      r[i][0] = p0*q0 - p1*q1 - p2*q2 - p3*q3;
      r[i][1] = p1*q0 + p0*q1 - p3*q2 + p2*q3;
      r[i][2] = p2*q0 + p3*q1 + p0*q2 - p1*q3;
      r[i][3] = p3*q0 - p2*q1 + p1*q2 + p0*q3;
   }
}
/******************************************************************************
 * Quaternion multiplier.  Multiplies quaternions p by q to give r  	      *
 * Can be called with  r the same as p or q.                                  *
 ******************************************************************************/
void q_mul_1(p, q, r)
quat_mt	p,			/* First Quaternion array [n][4]        (in)  */
	q,			/* Second quaternion array [n][4]       (in)  */
	r;			/* Resultant quaternions [n][4]        (out)  */
{
   register	real	p0, p1, p2, p3;
   register	real	q0, q1, q2, q3;
   
   p0 =  p[0]; p1 =  p[1]; p2 =  p[2]; p3 =  p[3];
   q0 =  q[0]; q1 =  q[1]; q2 =  q[2]; q3 =  q[3];

   r[0] = p0*q0 - p1*q1 - p2*q2 - p3*q3;
   r[1] = p1*q0 + p0*q1 - p3*q2 + p2*q3;
   r[2] = p2*q0 + p3*q1 + p0*q2 - p1*q3;
   r[3] = p3*q0 - p2*q1 + p1*q2 + p0*q3;
}
/******************************************************************************
 * Quaternion multiplier.  Multiplies arrays of quaternions p(-1) by q to     *
 * give r. Can be called with  r the same as p or q.                          *
 ******************************************************************************/
void q_conj_mul(p, q, r, n)
quat_mp	p,			/* First Quaternion array [n][4]        (in)  */
	q,			/* Second quaternion array [n][4]       (in)  */
	r;			/* Resultant quaternions [n][4]        (out)  */
int	n;			/* Number of quaternions in the arrays  (in)  */
{
   register	int	i;
   register	real	p0, p1, p2, p3;
   register	real	q0, q1, q2, q3;
   
   for(i = 0; i < n; i++)
   {
      p0 =  p[i][0]; p1 = -p[i][1]; p2 = -p[i][2]; p3 = -p[i][3];
      q0 =  q[i][0]; q1 =  q[i][1]; q2 =  q[i][2]; q3 =  q[i][3];

      r[i][0] = p0*q0 - p1*q1 - p2*q2 - p3*q3;
      r[i][1] = p1*q0 + p0*q1 - p3*q2 + p2*q3;
      r[i][2] = p2*q0 + p3*q1 + p0*q2 - p1*q3;
      r[i][3] = p3*q0 - p2*q1 + p1*q2 + p0*q3;
   }
}
/******************************************************************************
 *  q_to_mat  Make the rotation matrix corresponding to quaternion q          *
 ******************************************************************************/
void q_to_rot(quat, rot)
quat_mt	quat;				/* Input quaternion              (in) */
mat_mt	rot;				/* Rotation matrix		(out) */
{
   register	real	q0, q1, q2, q3;
   register	real	a01, a02, a03,
			     a12, a13,
			          a23;

   q0 = quat[0];  q1 = quat[1]; q2 = quat[2];  q3 = quat[3];

   a01 = 2.0*q0*q1;  a02 = 2.0*q0*q2;  a03 = 2.0*q0*q3;
                     a12 = 2.0*q1*q2;  a13 = 2.0*q1*q3;
                                       a23 = 2.0*q2*q3;
   rot[0][1] = a12 - a03;
   rot[0][2] = a13 + a02;
   rot[1][0] = a12 + a03;
   rot[1][2] = a23 - a01;
   rot[2][0] = a13 - a02;
   rot[2][1] = a23 + a01;

   q0 = q0*q0;  q1 = q1*q1;  q2 = q2*q2;  q3 = q3*q3;

   rot[0][0] = q0 + q1 - q2 - q3;
   rot[1][1] = q0 - q1 + q2 - q3;
   rot[2][2] = q0 - q1 - q2 + q3;
}
/******************************************************************************
 * rot_to_q. Inverse of above.  Will fall over badly If rot is not orthogonal.*
 ******************************************************************************/
void	rot_to_q(rot, quat)
mat_mt	rot;				/* Rotation matrix		 (in) */
quat_mt	quat;				/* Input quaternion             (out) */
{
   int i, j, k;
   real sign;
   double eps = 100*precision();	/* Safety margin for square roots     */
   
   quat[0] = 0.5 * sqrt(1.0 + eps + rot[0][0] + rot[1][1] + rot[2][2]);

   for( i = 0, j = 1, k = 2; i < 3; i++, j=(i+1)%3, k=(j+1)%3 )
   {
      if( quat[0] > 0 )
         sign = (rot[j][k] - rot[k][j]) >= 0.0 ? 1.0 : -1.0;
      else
         sign = (rot[i][j] + rot[j][i] >= 0) ^ (rot[i][k] + rot[k][i] >= 0)
	        ? -1.0 : 1.0;

      quat[i+1] = sign*0.5*sqrt(1.0+eps + rot[i][i] - rot[j][j] - rot[k][k]);
   }
}
$EOD
$!
$CREATE rdf.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * rdf		Functions to accumulate and calculate radial distribution     *
 *		functions. Contents:					      *
 * init_rdf()		Prepare to collect rdf's. Must be called first	      *
 * rdf_calc() (NOT USED)Bin site-site distances and accumulate RDF's	      *
 * rdf_accum()		Bin site-site distances and accumulate RDF's	      *
 * print_rdf()		Calculate RDF from binned data and output it.	      *
 * rdf[idi][idj][ibin]	RDF database (also accessed by 'restart')	      *
 ******************************************************************************
 *      Revision Log
 *       $Log: rdf.c,v $
 *       Revision 2.8  1996/01/15 15:19:12  keith
 *       New function rdf_accum for parallel accululation or RDF data.
 *       rdf_ptr() now takes one param, *size, which returns # items.
 *
 *       Revision 2.7  1994/06/08 13:22:31  keith
 *       Null update for version compatibility
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Added CONST qualifier to (re-)declarations of ANSI library
 * emulation routines to give reliable compilation even
 * without ANSI_LIBS macro. (#define's away for K&R
 * compilers)
 *
 * Revision 2.5  94/01/18  13:32:55  keith
 * Null update for XDR portability release
 * 
 * Revision 2.3  93/10/28  10:28:08  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.1  93/07/29  17:37:11  keith
 * Changed evaluation of rdf to be in exact accordance with
 * manual.  That is, replaced (nsites-1) with nsites and
 * 3(b+1/2)^2 with [(b+1)^3 - b^3].  Should make negligible
 * difference to results. 
 * 
 * Revision 2.0  93/03/15  14:49:19  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.19  93/03/12  12:25:17  keith
 * Got rid of unneccesary convex special case.
 * 
 * Revision 1.18  93/03/09  15:59:09  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.17  92/11/02  17:31:17  keith
 * Fixed bug where counter array "nfrac" was not initialised.
 * 
 * Revision 1.16  92/10/28  14:09:52  keith
 * Changed "site_[tp]" typedefs to avoid name clash on HP.
 * 
 * Revision 1.15  92/09/22  14:48:13  keith
 * Tidied up calls to improve "lint" rating.
 * 
 * Revision 1.14  92/06/26  17:03:25  keith
 * Got rid of assumption that memory returned by talloc() or
 * arralloc() is zeroed.  This enhances ANSI compatibility.
 * Removed memory zeroing from alloc.c() in consequence.
 * 
 * Revision 1.13  91/08/15  18:12:12  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_t and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.12  91/03/12  15:43:14  keith
 * Tidied up typedefs size_t and include file 
 * Added explicit function declarations.
 * 
 * Revision 1.11  90/10/23  20:13:18  keith
 * Added dummy function call to inhibit vectorization.
 * This allows use of 'ivdep' compiler options and also
 * works round certain bugs in cray's scc compiler.
 * 
 * Revision 1.10  90/05/16  18:40:38  keith
 * Renamed own freer from cfree to tfree.
 * 
 * Revision 1.9  90/05/01  12:51:19  keith
 * Corrected line-breaking algorithm in print_rdf() to never exceed line length.
 * 
 * Revision 1.8  90/02/02  15:30:19  keith
 * Split inner loop in rdf_calc() into one large, vectorisable loop and
 * one smaller, unvectorisable binning loop. 
 * Replaced library function floor() by (vectorisable) macro.
 * 
 * Revision 1.7  89/12/21  16:30:02  keith
 * Reversed indices in 'site' and 'site_force' to allow stride of 1 in ewald.
 * 
 * Revision 1.6  89/11/20  11:59:19  keith
 * Corrected normalisation in calculation of rdf.  Changed interface (cf main).
 * 
 * Revision 1.5  89/10/24  17:18:33  keith
 * Modified pbc algorithm to use floor() library function.
 * Now works with non-orthorhombic cell.
 * 
 * Revision 1.4  89/07/07  10:37:02  keith
 * Added check for uniformly zero RDF which otherwise gave divide by zero.
 * 
 * Revision 1.3  89/06/22  15:45:10  keith
 * Tidied up loops over species to use one pointer as counter.
 * 
 * Revision 1.2  89/06/01  21:25:16  keith
 * Control.out eliminated, use printf and freopen instead to direct output.
 * 
 * Revision 1.1  89/04/20  16:00:53  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/rdf.c,v 2.8 1996/01/15 15:19:12 keith Exp $";
#endif
/*========================== program include files ===========================*/
#include	"defs.h"
/*========================== Library include files ===========================*/
#ifdef stellar
#include 
#else
#include 
#endif
#include 	"string.h"
#include	
/*========================== Program include files ===========================*/
#include	"structs.h"
/*========================== External function declarations ==================*/
gptr            *talloc();	       /* Interface to memory allocator       */
void            tfree();	       /* Free allocated memory	      	      */
double	det();
void	invert();
void	new_line();
void	put_line();
double	precision();
void	inhibit_vectorization();		/* Self-explanatory dummy     */
/*========================== External data references ========================*/
extern contr_mt	control;    		    /* Main simulation control parms. */
/*====================================data definitions  ======================*/
static int	***rdf;				/* The RDF 'array'	      */
static   int	*rdf_base;			/* base of data area          */
static   int    rdf_size;
gptr *rdf_ptr(size)
int *size;
{
   *size = rdf_size;
   return (gptr*)rdf_base;
}
/*========================== Macros ==========================================*/
#define MATMUL(i, m, r) (m[i][0]*r[0] + m[i][1]*r[1] + m[i][2]*r[2])
#define floor(x)  (double)((int)((x) + 10) - 10) /* Vectorisable macro floor()*/
/*============================================================================*/

/******************************************************************************
 *  rdf_init.  Prepare to bin rdf's.  Allocate memory and pointers	      *
 ******************************************************************************/
void	init_rdf(system)
system_mp	system;				/* System info struct	      */
{
   int		max_id = system->max_id;
   int		idi, idj;
   int		*base;

   rdf = aalloc(max_id, int ** );
   rdf_size = control.nbins * max_id * (max_id - 1) / 2;
   base = rdf_base = ialloc(rdf_size);
   memst(base, 0, rdf_size*sizeof(int));
   for(idi = 1; idi < max_id; idi++)
      rdf[idi] = aalloc(max_id, int * );
   for(idi = 1; idi < max_id; idi++)
      for(idj = idi; idj < max_id; idj++)
      {
         rdf[idi][idj] = rdf[idj][idi] = base;
         base += control.nbins;
      }
}
/******************************************************************************
 *  rdf_calc.  Calculate site pair distances and bin for RDF.                 *
 ******************************************************************************/
void	rdf_calc(site, system, species)
real		**site;				/* Site co-ordinate array     */
system_mp	system;				/* System info struct	      */
spec_mt	species[];			/* Species info struct array  */
{
   spec_mp	spec;
   register double	t;
   double	r;
   real		*site0 = site[0], *site1 = site[1], *site2 = site[2];
   vec_mt	rij;
   double	rbin;				/* 1.0/bin width	      */
   int		imol, isite, jsite, nsites = system->nsites;
   int		*id = ialloc(system->nsites),
                *bind = ialloc(system->nsites);
   int		*id_ptr;
   mat_mt	hinv;
   double	lx   = system->h[0][0], lxy  = system->h[0][1],
		ly   = system->h[1][1], lxz  = system->h[0][2],
		lz   = system->h[2][2], lyz  = system->h[1][2];
   invert(system->h,hinv);

   rbin = control.nbins / control.limit;
   
/*  Construct and fill expanded site-identifier array, id                     */
   id_ptr = id;
   for (spec = species; spec < &species[system->nspecies]; spec++)
      for(imol = 0; imol < spec->nmols; imol++)
      {
         memcp(id_ptr, spec->site_id, spec->nsites*sizeof(int));
         id_ptr += spec->nsites;
      }

    for(isite = 0; isite < nsites; isite++)
    {
VECTORIZE
       for(jsite = isite+1; jsite < nsites; jsite++)
       {
          rij[0] = site0[jsite] - site0[isite];
          rij[1] = site1[jsite] - site1[isite];
          rij[2] = site2[jsite] - site2[isite];
          
          rij[0] -= lx  *      floor(MATMUL(0,hinv,rij) + 0.5);
          rij[0] -= lxy * (t = floor(MATMUL(1,hinv,rij) + 0.5));
          rij[1] -= ly  * t;
          rij[0] -= lxz * (t = floor(MATMUL(2,hinv,rij) + 0.5));
          rij[1] -= lyz * t;
          rij[2] -= lz  * t;
           
          r = sqrt(rij[0]*rij[0] + rij[1]*rij[1] + rij[2]*rij[2]);
	  bind[jsite] = rbin*r;
       }
       for(jsite = isite+1; jsite < nsites; jsite++)
       {
	  inhibit_vectorization();
	  if( bind[jsite] < control.nbins )
             rdf[id[isite]][id[jsite]][bind[jsite]]++;
       }
    }
    xfree(id);    xfree(bind);
}
/******************************************************************************
 *  rdf_accum.  Calculate site pair distances and bin for RDF.                *
 ******************************************************************************/
void	rdf_accum(lo, hi, rsq, iid, id, nab)
int	lo, hi;
real	rsq[];
int	iid;
int	id[];
int	nab[];
{
   int  j, bin;
   int  *nj = nab + lo;
   int  **rdfi = rdf[iid];
   double rbin = control.nbins / control.limit;

   for(j=lo; jmax_id);  /* Per site count of system*/
   spec_mt	*spec;
   double	bin = control.limit/control.nbins,
   		bincb = bin*bin*bin,
   		rho = system->nsites/det(system->h);
   double	norm;
   char		buf[32];
   
   memst(nfrac,0,system->max_id*sizeof(*nfrac));
   for(spec = species; spec < species+system->nspecies; spec++)
      for(is = 0; is < spec->nsites; is++)
	 nfrac[spec->site_id[is]] += spec->nmols;

   put_line('_');
   (void)printf("\tRadial Distribution Functions\tBin width=%g", bin);
   new_line();

   for(idi = 1; idi < system->max_id; idi++)
      for(idj = idi; idj < system->max_id; idj++)
      {
         (void)printf("\t%s-%s RDF", site_info[idi].name, site_info[idj].name);
         new_line();
	 col = 0;

	 norm = 3.0*system->nsites*control.rdf_interval /
	    (4.0*PI*bincb*rho*nfrac[idi]*nfrac[idj]*control.rdf_out);
	 if( idi == idj )
	    norm += norm;
         for(ibin = 0; ibin < control.nbins; ibin++)
         {
            sprintf(buf, " %7f",rdf[idi][idj][ibin]*norm/
		    ( 3*(SQR(ibin) + ibin) + 1));
	    col += strlen(buf);
            if(col > control.page_width)
            {
               col = strlen(buf);
	       new_line();
	    }
	    fputs(buf, stdout);
            rdf[idi][idj][ibin] = 0;				/* Reset      */
         }
	 new_line();
      }
   put_line('_');
   xfree(nfrac);
}
$EOD
$!
$CREATE restart.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * restart	functions for writing program configurations to a data file   *
 *		and rereading them.  Because of the way they are used, the    *
 *		writing function is unitary, but the reading part is split    *
 *		between three routines.		Contents:		      *
 * cread()		Read data record from file, check length	      *
 * cwrite()		Write length and data as record to file		      *
 * re_re_header()	Read the restart header and control structs	      *
 * re_re_sysdef()	Read system definition records from restart file      *
 * read_restart()	Read simulation dynamic variables, averages & rdf's   *
 ******************************************************************************
 *      Revision Log
 *       $Log: restart.c,v $
 *       Revision 2.10  1996/10/24 13:06:49  keith
 *       Fixed restart structure correctly - broken in prev version.
 *       Thermostat parameters may not be properly read.
 *
 *       Revision 2.9  1996/01/12 15:45:52  keith
 *       Reworked V. Murashov's thermostat code.
 *       Convert mass params from kJ/mol ps^2 to prog units. Defaults=1.
 *       Restored defaults of zero for Ewald sum params.
 *       Modified Alpha/cutoff initialization to handle uncharged case correctly.
 *
 *       Changed to macros from defs.h for input units.
 *
 *       Revision 2.8  1995/12/04 11:45:49  keith
 *       Nose-Hoover and Gaussian (Hoover constrained) thermostats added.
 *       Thanks to V. Murashov.
 *
 * Revision 2.7  1994/06/08  13:22:31  keith
 * Null update for version compatibility
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Added CONST qualifier to (re-)declarations of ANSI library
 * emulation routines to give reliable compilation even
 * without ANSI_LIBS macro. (#define's away for K&R
 * compilers)
 *
 * Revision 2.5  94/01/18  13:32:56  keith
 * Null update for XDR portability release
 * 
 * Revision 2.3  93/10/28  10:28:11  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.2  1993/10/22  12:44:23  keith
 * Fixed initialization bug which caused XDR write to fail on 64-bit Alphas.
 *
 * Revision 2.1  93/07/19  13:28:11  keith
 * Added XDR capability for backup and dump files.
 * 
 * Revision 2.0  93/03/15  14:49:20  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.19  93/03/09  15:59:12  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.18  92/10/29  15:20:35  keith
 * Changed "site_[tp]" typedefs to avoid name clash on HP.
 * 
 * Revision 1.17  92/06/10  15:52:40  keith
 * Added capability to read restart files where NPOTP has changed.
 * 
 * Revision 1.16  92/06/02  10:38:27  keith
 * Added check of ferror() as well as return from fwrite().  Talk
 * about belt and braces.
 * 
 * Revision 1.15  92/03/24  12:41:07  keith
 * Moved reset-averages code to values.c and did it properly.
 * 
 * Revision 1.14  92/03/19  15:14:12  keith
 * Added support for dynamic allocation of rolling average arrays,
 * conversion of existing restart files is done on fly.
 * 
 * Revision 1.13  91/08/23  14:12:46  keith
 * Fixed declaration of av_ptr to agree with definition in values.c
 * 
 * Revision 1.12  91/08/16  15:59:48  keith
 * Checked error returns from fread, fwrite, fseek and fclose more
 * rigourously.   Called strerror() to report errors.
 * 
 * Revision 1.11  91/08/15  18:12:14  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_mt and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.10  91/03/12  15:43:16  keith
 * Tidied up typedefs size_mt and include file 
 * Added explicit function declarations.
 * 
 * Revision 1.9  91/02/19  14:51:31  keith
 * Minor changes to get rid of misleading compiler warnings.
 * 
 * Revision 1.8  90/08/24  17:47:26  keith
 * Made 'reset-averages' parameter actually do something.
 * 
 * Revision 1.7  90/05/02  15:28:55  keith
 * Removed references to size_mt and time_t typedefs, no longer in "defs.h"
 * 
 * Revision 1.6  89/11/01  17:40:36  keith
 * Read_restart modified to allow skip of read of averages data -
 * specified by asize=0.
 * 
 * Revision 1.4  89/06/22  15:45:14  keith
 * Tidied up loops over species to use one pointer as counter.
 * 
 * Revision 1.3  89/05/22  14:05:43  keith
 * Added rescale-separately option, changed 'contr_t' format.
 * 
 * Revision 1.2  89/05/22  13:53:20  keith
 * Fixed to put correct RCS Revision into restart file header
 * 
 * Revision 1.1  89/04/27  15:16:09  keith
 * Initial revision
 * 
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/restart.c,v 2.10 1996/10/24 13:06:49 keith Exp $";
#endif
/*========================== program include files ===========================*/
#include	"defs.h"
/*========================== Library include files ===========================*/
#include 	"string.h"
#include	"stddef.h"
#include	"time.h"
#include	
/*========================== Program include files ===========================*/
#include	"structs.h"
#include	"messages.h"
#include	"xdr.h"
/*========================== External function declarations ==================*/
gptr            *talloc();	       /* Interface to memory allocator       */
void            tfree();	       /* Free allocated memory	      	      */
int		replace();
gptr		*av_ptr();
char		*atime();
char		*cctime();
gptr		*rdf_ptr();
#if defined(ANSI) || defined(__STDC__)
void		note(char *, ...);	/* Write a message to the output file */
void		message(int *, ...);	/* Write a warning or error message   */
#else
void		note();			/* Write a message to the output file */
void		message();		/* Write a warning or error message   */
#endif
/*========================== External data references ========================*/
extern contr_mt control;		    /* Main simulation control parms. */
/*============================================================================*/
typedef struct {FILE *fp; XDR *xp;} xfp_mt;
static size_mt stored_size = 0;
static boolean xdr_read;
static int    size_flg = 0;
/******************************************************************************
 * creset() reset and rewind input stream.				      *
 ******************************************************************************/
static
void creset(fp)
FILE 	*fp;
{
   size_flg = 0;
   if( fseek(fp,0L,0) )
      message(NULLI,NULLP,FATAL,SEFAIL,"",strerror(errno));      
}
/******************************************************************************
 *   cnext.  Read the size of the next 'record' stored in the file.           *
 *   Leave file pointer unchanged.  ie do lookahead.			      *
 ******************************************************************************/
static
size_mt cnext(xfp)
xfp_mt	xfp;
{
   (void)fread((gptr*)&stored_size, sizeof stored_size, 1, xfp.fp);
   if(ferror(xfp.fp))
      message(NULLI,NULLP,FATAL,REREAD,ftell(xfp.fp),strerror(errno));
   else if(feof(xfp.fp))
      message(NULLI,NULLP,FATAL,REEOF);
   size_flg++;
   return stored_size;
}
#ifdef USE_XDR
static
size_mt xdr_cnext(xfp)
xfp_mt	xfp;
{
   if( xdr_read ) {
      if(!xdr_u_long(xfp.xp,&stored_size))
	 message(NULLI,NULLP,FATAL,REREAD,xdr_getpos(xfp.xp),strerror(errno));
      size_flg++;
      return stored_size;
   } else
      return cnext(xfp);
}
#endif
/******************************************************************************
 *   cread.   read a word from the binary restart file.  This should be the   *
 *   size of the next 'record' stored in the file.  Check it against the      *
 *   expected value.  Finally call fread to read in this data and check for   *
 *   error and end of file						      *
 ******************************************************************************/
static
void	cread(xfp, ptr, size, nitems, proc)
xfp_mt	xfp;
gptr	*ptr;
size_mt	size;
int	nitems;
xdrproc_t proc;
{
   int status;
   if( size_flg )
      size_flg = 0;
   else
   {
      status = fread((gptr*)&stored_size, sizeof stored_size, 1, xfp.fp);
      if(ferror(xfp.fp))
	 message(NULLI,NULLP,FATAL,REREAD,ftell(xfp.fp),strerror(errno));
      else if(feof(xfp.fp))
	 message(NULLI,NULLP,FATAL,REEOF);
      else 
      if( status == 0 )
         message(NULLI,NULLP,FATAL,"Unknown read error: %s",strerror(errno));
   }
   if(stored_size != (unsigned long)size * nitems)
      message(NULLI,NULLP,FATAL,REFORM, ftell(xfp.fp),
              stored_size, size * nitems);
   status = fread(ptr, size, nitems, xfp.fp);
   if(ferror(xfp.fp))
      message(NULLI,NULLP,FATAL,REREAD,ftell(xfp.fp),strerror(errno));
   else if(feof(xfp.fp))
      message(NULLI,NULLP,FATAL,REEOF);
   else if ( status < nitems )
      message(NULLI,NULLP,FATAL,"Unknown write error");
}
#ifdef USE_XDR
static
void	xdr_cread(xfp, ptr, size, nitems, proc)
xfp_mt	xfp;
gptr	*ptr;
size_mt	size;
int	nitems;
xdrproc_t proc;
{
   unsigned int	       begin_data, end_data;

   if( xdr_read ) {
      if( size_flg )
	 size_flg = 0;
      else
      {
	 if( xdr_u_long(xfp.xp,&stored_size) == 0 )
	    message(NULLI,NULLP,FATAL,REREAD,xdr_getpos(xfp.xp),strerror(errno));
      }
#if 0
      if(stored_size != (unsigned long)size * nitems)
	 message(NULLI,NULLP,FATAL,REFORM, ftell(xfp.fp),
		 stored_size, size * nitems);
#endif
      begin_data = xdr_getpos(xfp.xp);
      while(nitems > 0)
      {
	 if (! (*proc)(xfp.xp,ptr) )
	    break;
	 ptr = (gptr*) ((char*)ptr+size);
	 nitems--;
      }
      end_data = xdr_getpos(xfp.xp);
      if ( nitems > 0 )
	 if(feof(xfp.fp))
	    message(NULLI,NULLP,FATAL,REEOF);
	 else
	    message(NULLI,NULLP,FATAL,"Unknown read error");
      if(stored_size != end_data-begin_data)
	 message(NULLI,NULLP,FATAL,REFORM, xdr_getpos(xfp.xp),
		 stored_size, end_data-begin_data);
   } else
      cread(xfp, ptr, size, nitems, proc);
}
#endif
/******************************************************************************
 *  cwrite.  opposite of cread.  write the length followed by the data	      *
 ******************************************************************************/
static
void	cwrite(xfp, ptr, size, nitems, proc)
xfp_mt	xfp;
gptr	*ptr;
size_mt	size;
int	nitems;
xdrproc_t proc;
{
   unsigned long       length = (unsigned long)size*nitems;
   if( fwrite((gptr*)&length, sizeof length, 1, xfp.fp) == 0 )
      message(NULLI,NULLP,FATAL,REWRT,strerror(errno));
   if( fwrite(ptr, size, nitems, xfp.fp) < nitems )
      message(NULLI,NULLP,FATAL,REWRT,strerror(errno));
}
#ifdef USE_XDR
static
void	xdr_cwrite(xfp, ptr, size, nitems, proc)
xfp_mt	xfp;
gptr	*ptr;
size_mt	size;
int	nitems;
xdrproc_t proc;
{
   unsigned long       length=0;
   unsigned int	       begin_length, begin_data, end_data;
   if( control.xdr_write ) {   
      /* 
       * We can't calculate length in advance.  Instead we post-calculate
       * it from "getpos" and backspace in order to write it.
       */
      begin_length = xdr_getpos(xfp.xp);
      if( xdr_u_long(xfp.xp,&length) == 0)
	 message(NULLI,NULLP,FATAL,REWRT,strerror(errno));
      begin_data = xdr_getpos(xfp.xp);
      while(nitems > 0)
      {
	 if( !(*proc)(xfp.xp,ptr) )
	    break;
	 ptr = (gptr*) ((char*)ptr+size);
	 nitems--;
      }
      if( nitems > 0 )
	 message(NULLI,NULLP,FATAL,REWRT,strerror(errno));
      end_data = xdr_getpos(xfp.xp);
      length = end_data-begin_data;
      if( ! xdr_setpos(xfp.xp, begin_length) || ! xdr_u_long(xfp.xp,&length)
	 || ! xdr_setpos(xfp.xp, end_data) )
	 message(NULLI,NULLP,FATAL,REWRT,strerror(errno));
   } else
      cwrite(xfp, ptr, size, nitems, proc);
}
#endif

#ifdef USE_XDR
#define cread	xdr_cread
#define cwrite	xdr_cwrite
#define cnext	xdr_cnext
#endif
/******************************************************************************
 *  re_re_header   Read from the (already opened) restart file, the	      *
 *  restart header and control structs.                                       *
 ******************************************************************************/
static   XDR		xdrs;
void	re_re_header(restart, header, contr)
FILE	  *restart;
restrt_mt *header;
contr_mt  *contr;
{
   int		vmajor,vminor;
   xfp_mt	xfp;
   xfp.xp =     &xdrs;

#ifdef USE_XDR
   xdr_read = TRUE;
   xdrstdio_create(xfp.xp, restart, XDR_DECODE);
   if( cnext(xfp) == XDR_RESTRT_SIZE )
   {
      cread(xfp,  (gptr*)header, lsizeof(restrt_mt), 1, xdr_restrt);
      header->vsn[15] = '\0';   /* Add terminator in case vsn is garbage*/
      if( ! strstr(header->vsn,"(XDR)") )
	 xdr_read = FALSE;
   }
   else
      xdr_read=FALSE;

   if( ! xdr_read )		     /* Didn't find XDR flag		*/
      creset(restart);
#endif
   xfp.fp=restart;
   if( ! xdr_read )
      cread(xfp,  (gptr*)header, lsizeof(restrt_mt), 1, xdr_restrt);
   cread(xfp,  (gptr*)contr, lsizeof(contr_mt), 1, xdr_contr); 
   /*
    * Parse header version
    */
   if( sscanf(header->vsn, "%d.%d", &vmajor, &vminor) < 2 )
      message(NULLI, NULLP, FATAL, INRVSN, header->vsn);
   /*
    * Bodge up botched control struct in restart files written by 
    * Moldy 2.10 ir restart.c 2.9
    */
   if( vmajor == 2 && vminor == 9 )
   {
      contr->ttmass = contr->rtmass = 10000.0;
      contr->const_temp = 0;
      message(NULLI,NULLP,WARNING,BODGUP,contr->ttmass);
   }
}
/******************************************************************************
 *  conv_potsize    Convert potential parameters array if NPOTP has changed   *
 ******************************************************************************/
static
void conv_potsize(pot, old_pot_size, old_npotp, npotpar, npotrecs)
pot_mt	*pot;
size_mt   old_pot_size;
int     old_npotp, npotpar, npotrecs;
{
   int		i;
   char		*tmp_pot;

   if( old_npotp == NPOTP )
      return;
   else if ( npotpar <= NPOTP )
   {
      note("Old potential parameter array size = %d, new = %d",old_npotp,NPOTP);
      tmp_pot = aalloc( old_pot_size*npotrecs, char);
      memcp(tmp_pot, pot, old_pot_size*npotrecs);
      for( i=0; i < npotrecs; i++)
	 memcp(pot+i, tmp_pot+old_pot_size*i, MIN(old_pot_size,sizeof(pot_mt)));
      xfree(tmp_pot);
   }
   else
      message(NULLI, NULLP, FATAL, CPOTFL, NPOTP, npotpar);
}
/******************************************************************************
 *  re_re_sysdef    Read the system specification from the restart file       *
 *  which must be open and pointed to by parameter 'file'.  Set up the        *
 *  structures system and species and arrays site_info and potpar (allocating *
 *  space) and read in the supplied values.  				      *
 ******************************************************************************/
void	re_re_sysdef(restart, vsn, system, spec_ptr, site_info, pot_ptr)
FILE		*restart;		/* File pointer to read info from     */
char		*vsn;			/* Version string file written with */
system_mp	system;			/* Pointer to system array (in main)  */
spec_mp		*spec_ptr;		/* Pointer to be set to species array */
site_mp		*site_info;		/* To be pointed at site_info array   */
pot_mp		*pot_ptr;		/* To be pointed at potpar array      */
{
   spec_mp	spec;
   size_mt	old_pot_size;
   int		old_npotp, n_pot_recs;
   int		vmajor,vminor;
   xfp_mt	xfp;

   xfp.xp = &xdrs;
   xfp.fp = restart;

   /*
    * Parse header version
    */
   if( sscanf(vsn, "%d.%d", &vmajor, &vminor) < 2 )
      message(NULLI, NULLP, FATAL, INRVSN, vsn);

   /*
    *  Read in system structure.
    *  Size changed in 2.11, read old files (XDR only).
    */
   if( vmajor > 2 || (vmajor == 2 && vminor > 9) )
     cread(xfp,  (gptr*)system, lsizeof(system_mt), 1, xdr_system);
   else
     cread(xfp,  (gptr*)system, lsizeof(system_mt)-10*lsizeof(real*),
	   1, xdr_system_2);

   /* Allocate space for species, site_info and potpar arrays and set pointers*/
   *spec_ptr  = aalloc(system->nspecies,                spec_mt );
   *site_info = aalloc(system->max_id,                  site_mt );
   *pot_ptr   = aalloc(system->max_id * system->max_id, pot_mt );
   /*  read species array into allocated space				      */
   cread(xfp, (gptr*)*spec_ptr, lsizeof(spec_mt), system->nspecies, xdr_species);
   
   for (spec = *spec_ptr; spec < &(*spec_ptr)[system->nspecies]; spec++)
   {
      spec->p_f_sites = ralloc(spec->nsites);	/* Allocate the species -     */
      spec->site_id   = ialloc(spec->nsites);	/* specific arrays	      */
      cread(xfp,(gptr*)spec->p_f_sites, lsizeof(real), 3*spec->nsites, xdr_real);
      cread(xfp,(gptr*)spec->site_id,   lsizeof(int), spec->nsites, xdr_int);
   }
   cread(xfp,  (gptr*)*site_info, lsizeof(site_mt), system->max_id, xdr_site);
   /*
    * Potential Parameters -- complicated by need to convert if NPOTP changed.
    */
   n_pot_recs = SQR(system->max_id);
#ifdef USE_XDR
   if( xdr_read )
      old_npotp = (cnext(xfp)/ n_pot_recs - 2*XDR_INT_SIZE)/XDR_REAL_SIZE;
   else
#endif
      old_npotp = ((long)cnext(xfp)/ n_pot_recs - (long)sizeof(pot_mt))/
                       (long)sizeof(real) + NPOTP;
   old_pot_size = sizeof(pot_mt) + (old_npotp - NPOTP) * sizeof(real);
   *pot_ptr = (pot_mt*)aalloc(n_pot_recs*MAX(sizeof(pot_mt),old_pot_size), char);
   xdr_set_npotpar(old_npotp);		/* Pass npotpar to xdr_pot. Ugh! */
   cread(xfp,  (gptr*)*pot_ptr, old_pot_size, n_pot_recs, xdr_pot);
   conv_potsize(*pot_ptr, old_pot_size, old_npotp, system->n_potpar, n_pot_recs);
}
/******************************************************************************
 *  read_restart.   read the dynamic simulation variables from restart file.  *
 ******************************************************************************/
void	read_restart(restart, vsn, system, av_convert)
FILE		*restart;
char		*vsn;			/* Version string file written with */
system_mp	system;
int		av_convert; 
{
   gptr		*ap;			/* Pointer to averages database       */
   size_mt	asize;			/* Size of averages database	      */
   boolean	rdf_flag;		/* Indicates whether file contains rdf*/
   int   	rdf_size;
   gptr		*rdf_base;

   int		vmajor,vminor;
   xfp_mt	xfp;

   /*
    * Parse header version
    */
   if( sscanf(vsn, "%d.%d", &vmajor, &vminor) < 2 )
      message(NULLI, NULLP, FATAL, INRVSN, vsn);
      
   xfp.xp= &xdrs;
   xfp.fp=restart;

   cread(xfp,  (gptr*)system->c_of_m, lsizeof(real), 3*system->nmols, xdr_real);
   cread(xfp,  (gptr*)system->vel,    lsizeof(real), 3*system->nmols, xdr_real);
   cread(xfp,  (gptr*)system->velp,   lsizeof(real), 3*system->nmols, xdr_real);
   cread(xfp,  (gptr*)system->acc,    lsizeof(real), 3*system->nmols, xdr_real);
   cread(xfp,  (gptr*)system->acco,   lsizeof(real), 3*system->nmols, xdr_real);
   cread(xfp,  (gptr*)system->accvo,  lsizeof(real), 3*system->nmols, xdr_real);
   if(system->nmols_r > 0)
   {
      cread(xfp,  (gptr*)system->quat,    lsizeof(real), 4*system->nmols_r, xdr_real);
      cread(xfp,  (gptr*)system->qdot,    lsizeof(real), 4*system->nmols_r, xdr_real);
      cread(xfp,  (gptr*)system->qdotp,   lsizeof(real), 4*system->nmols_r, xdr_real);
      cread(xfp,  (gptr*)system->qddot,   lsizeof(real), 4*system->nmols_r, xdr_real);
      cread(xfp,  (gptr*)system->qddoto,  lsizeof(real), 4*system->nmols_r, xdr_real);
      cread(xfp,  (gptr*)system->qddotvo, lsizeof(real), 4*system->nmols_r, xdr_real);
   }
   cread(xfp,  (gptr*)system->h,       lsizeof(real), 9, xdr_real);
   cread(xfp,  (gptr*)system->hdot,    lsizeof(real), 9, xdr_real);
   cread(xfp,  (gptr*)system->hdotp,   lsizeof(real), 9, xdr_real);
   cread(xfp,  (gptr*)system->hddot,   lsizeof(real), 9, xdr_real);
   cread(xfp,  (gptr*)system->hddoto,  lsizeof(real), 9, xdr_real);
   cread(xfp,  (gptr*)system->hddotvo, lsizeof(real), 9, xdr_real);

   if( vmajor > 2 || (vmajor == 2 && vminor > 7) )
   {
      cread(xfp,  (gptr*)system->ta,      lsizeof(real), system->nspecies, xdr_real);
      cread(xfp,  (gptr*)system->tap,     lsizeof(real), system->nspecies, xdr_real);
      cread(xfp,  (gptr*)system->tadot,   lsizeof(real), system->nspecies, xdr_real);
      cread(xfp,  (gptr*)system->tadoto,  lsizeof(real), system->nspecies, xdr_real);
      cread(xfp,  (gptr*)system->tadotvo, lsizeof(real), system->nspecies, xdr_real);

      cread(xfp,  (gptr*)system->ra,      lsizeof(real), system->nspecies, xdr_real);
      cread(xfp,  (gptr*)system->rap,     lsizeof(real), system->nspecies, xdr_real);
      cread(xfp,  (gptr*)system->radot,   lsizeof(real), system->nspecies, xdr_real);
      cread(xfp,  (gptr*)system->radoto,  lsizeof(real), system->nspecies, xdr_real);
      cread(xfp,  (gptr*)system->radotvo, lsizeof(real), system->nspecies, xdr_real);
   }

   ap = av_ptr(&asize,av_convert);	      /* get addr, size of database   */
   xdr_set_av_size_conv(asize,av_convert);    /* Pass  to xdr_averages.  Ugh! */
   cread(xfp, ap, asize, 1, xdr_averages);

   cread(xfp,  (gptr*)&rdf_flag, lsizeof rdf_flag, 1, xdr_bool); 
   				    /* Read flag signalling stored RDF data.  */
   if(rdf_flag && control.rdf_interval>0)/* Only read if data there and needed*/
   {
      rdf_base = rdf_ptr(&rdf_size);
      cread(xfp, rdf_base, lsizeof(int), rdf_size, xdr_int);
   }
#ifdef USE_XDR
   if( xdr_read )
      xdr_destroy(xfp.xp);
#endif

}
/******************************************************************************
 *  write_restart.  Write the restart file.  Included (in order) are          *
 *  the 'restart_header' and 'control' structs, the system-specification      *
 *  (system species, site_info and potpar data) and the dynamic variables.    *
 ******************************************************************************/
void	write_restart(save_name, header, system, species, site_info, potpar)
char		*save_name;		/* Name of save file to be written    */
restrt_mt	*header;		/* Restart header struct.	      */
system_mp	system;			/* Pointer to system array (in main)  */
spec_mp		species;		/* Pointer to be set to species array */
site_mp		site_info;		/* To be pointed at site_info array   */
pot_mp		potpar;			/* To be pointed at potpar array      */
{
   spec_mp	spec;
   gptr		*ap;			/* Pointer to averages database       */
   size_mt	asize;			/* Size of averages database	      */
   int  	rdf_size;
   gptr		*rdf_base;
   int		zero = 0, one = 1;
   restrt_mt	save_header;
   FILE		*save;
   XDR		xdrsw;
   xfp_mt	xfp;
   char		*vsn = "$Revision: 2.10 $"+11;

   save = fopen(control.temp_file, "wb");
   if(save == NULL)
   {
      message(NULLI, NULLP, ERROR, OSFAIL, control.temp_file);
      return;
   }
   xfp.xp = &xdrsw;
   xfp.fp = save;
#ifdef USE_XDR
   if( control.xdr_write )
      xdrstdio_create(xfp.xp, save, XDR_ENCODE);
#endif

   save_header = *header;
   (void)strncpy(save_header.vsn, vsn, sizeof save_header.vsn-1);
   save_header.vsn[strlen(save_header.vsn)-2] = '\0'; /* Strip trailing $     */
   if( control.xdr_write )
      (void)strncat(save_header.vsn," (XDR)",16);
   save_header.prev_timestamp = header->timestamp;
   save_header.timestamp = time((time_t*)0);		/* Update header      */
   save_header.seq++;
   
   cwrite(xfp,  (gptr*)&save_header, lsizeof save_header, 1, xdr_restrt);
   cwrite(xfp,  (gptr*)&control, lsizeof control, 1, xdr_contr); 

   cwrite(xfp,  (gptr*)system, lsizeof(system_mt), 1, xdr_system);
   cwrite(xfp,  (gptr*)species, lsizeof(spec_mt), system->nspecies, xdr_species);
   
   for (spec = species; spec < &species[system->nspecies]; spec++)
   {
      cwrite(xfp,(gptr*)spec->p_f_sites,lsizeof(real), 3*spec->nsites, xdr_real);
      cwrite(xfp,(gptr*)spec->site_id,  lsizeof(int), spec->nsites, xdr_int);
   }
   cwrite(xfp,  (gptr*)site_info, lsizeof(site_mt), system->max_id, xdr_site);
   xdr_set_npotpar(NPOTP);	/* Pass npotpar to xdr_pot. Ugh! */
   cwrite(xfp,  (gptr*)potpar, lsizeof(pot_mt), SQR(system->max_id), xdr_pot);

   cwrite(xfp,  (gptr*)system->c_of_m, lsizeof(real), 3*system->nmols, xdr_real);
   cwrite(xfp,  (gptr*)system->vel,    lsizeof(real), 3*system->nmols, xdr_real);
   cwrite(xfp,  (gptr*)system->velp,   lsizeof(real), 3*system->nmols, xdr_real);
   cwrite(xfp,  (gptr*)system->acc,    lsizeof(real), 3*system->nmols, xdr_real);
   cwrite(xfp,  (gptr*)system->acco,   lsizeof(real), 3*system->nmols, xdr_real);
   cwrite(xfp,  (gptr*)system->accvo,  lsizeof(real), 3*system->nmols, xdr_real);
   if(system->nmols_r > 0)
   {
      cwrite(xfp, (gptr*)system->quat,    lsizeof(real), 4*system->nmols_r, xdr_real);
      cwrite(xfp, (gptr*)system->qdot,    lsizeof(real), 4*system->nmols_r, xdr_real);
      cwrite(xfp, (gptr*)system->qdotp,   lsizeof(real), 4*system->nmols_r, xdr_real); 
      cwrite(xfp, (gptr*)system->qddot,   lsizeof(real), 4*system->nmols_r, xdr_real);
      cwrite(xfp, (gptr*)system->qddoto,  lsizeof(real), 4*system->nmols_r, xdr_real);
      cwrite(xfp, (gptr*)system->qddotvo, lsizeof(real), 4*system->nmols_r, xdr_real);
   }
   cwrite(xfp,  (gptr*)system->h,       lsizeof(real), 9, xdr_real);
   cwrite(xfp,  (gptr*)system->hdot,    lsizeof(real), 9, xdr_real);
   cwrite(xfp,  (gptr*)system->hdotp,   lsizeof(real), 9, xdr_real);
   cwrite(xfp,  (gptr*)system->hddot,   lsizeof(real), 9, xdr_real);
   cwrite(xfp,  (gptr*)system->hddoto,  lsizeof(real), 9, xdr_real);
   cwrite(xfp,  (gptr*)system->hddotvo, lsizeof(real), 9, xdr_real);

   cwrite(xfp,  (gptr*)system->ta,      lsizeof(real), system->nspecies, xdr_real);
   cwrite(xfp,  (gptr*)system->tap,     lsizeof(real), system->nspecies, xdr_real);
   cwrite(xfp,  (gptr*)system->tadot,   lsizeof(real), system->nspecies, xdr_real);
   cwrite(xfp,  (gptr*)system->tadoto,  lsizeof(real), system->nspecies, xdr_real);
   cwrite(xfp,  (gptr*)system->tadotvo, lsizeof(real), system->nspecies, xdr_real);

      cwrite(xfp,  (gptr*)system->ra,      lsizeof(real), system->nspecies, xdr_real);
      cwrite(xfp,  (gptr*)system->rap,     lsizeof(real), system->nspecies, xdr_real);
      cwrite(xfp,  (gptr*)system->radot,   lsizeof(real), system->nspecies, xdr_real);
      cwrite(xfp,  (gptr*)system->radoto,  lsizeof(real), system->nspecies, xdr_real);
      cwrite(xfp,  (gptr*)system->radotvo, lsizeof(real), system->nspecies, xdr_real);

   ap = av_ptr(&asize,0);			/* get addr, size of database */
   xdr_set_av_size_conv(asize,0);	 /* Pass asize to xdr_averages.  Ugh! */
   cwrite(xfp, ap, asize, 1, xdr_averages);
   
   if(control.rdf_interval > 0)			/* If we have rdf data	      */
   {
      cwrite(xfp,  (gptr*)&one, lsizeof(int), 1, xdr_bool);/* Flag rdf data   */
      rdf_base = rdf_ptr(&rdf_size);
      cwrite(xfp, rdf_base, lsizeof(int), rdf_size, xdr_int);/* write data   */
   }
   else
      cwrite(xfp,  (gptr*)&zero, lsizeof(int), 1, xdr_bool);/*flag no rdf data*/
      
#ifdef USE_XDR
   if( control.xdr_write )
      xdr_destroy(xfp.xp);
#endif

   if( ferror(save) || fclose(save) )            /* Delayed write error?       */
      message(NULLI, NULLP, FATAL, REWRT, strerror(errno));

   if(replace(control.temp_file, save_name) == 0)/* Rename temp file to output*/
   {						/* Don't signal backup write  */
      if(strcmp(save_name, control.backup_file))
         note("Configuration written to save file \"%s\" at %s, Seq no %d",
	      save_name, cctime(&save_header.timestamp), save_header.seq);
   }
   else
      message(NULLI, NULLP, ERROR, RNFAIL, save_name, control.temp_file,
	      strerror(errno));
}
$EOD
$!
$CREATE startup.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * startup	Functions to perform operations to set up and initialise the  *
 *		simulation and control the reading of control parameters,     *
 *		system specification and restart files.  Contents:	      *
 * start_up()		Master start-up control function		      *
 * allocate_dynamics()	Set up dynamic variable arrays by memory allocation   *
 * initialise_sysdef()	Calculate 'whole-system' quantities and more	      *
 * check_sysdef()	Check new system specification is consistent with old *
 * interp()		Scale derivatives when changing timestep	      *
 * skew_start()		Set up skew-cyclic initial configuration	      *
 * random_start()	Set up random initial configuration (not used)	      *
 *   			(N.B) lattice_start in "input.c"		      *
 * thermalise()		Set up Maxwell-Boltzmann velocity distribution	      *
 * select_spec()	Randomly choose a species			      *
 * random_quat()	Generate quaternion  for uniform random rotation      *
 * gauss_rand()		Return random sample from univariant gaussian         *
 ******************************************************************************
 *      Revision Log
 *      $Log: startup.c,v $
 *      Revision 2.12  1996/08/15 14:35:39  keith
 *      Fixed restart structure correctly - broken in prev version.
 *      Thermostat parameters may not be properly read.
 *
 *      Revision 2.11  1996/01/15 15:23:30  keith
 *      Changed input to new units
 *      Reset defaults for cutoff, alpha to zero for "auto" select.
 *      (Murashov's code missed this)
 *      Added safety checks to auto-ewald routine.
 *
 * Revision 2.10  1995/12/04 11:45:49  keith
 * Nose-Hoover and Gaussian (Hoover constrained) thermostats added.
 * Thanks to V. Murashov.
 *
 * Revision 2.9  1995/01/03  13:56:39  keith
 * Added auto-generation of Ewald Sum parameters using Fincham's formulae.
 *
 * Revision 2.8  1994/07/07  16:57:01  keith
 * Updated for parallel execution on SPMD machines.
 * Interface to MP library routines hidden by par_*() calls.
 * Compile with -DSPMD to activate
 *
 * Revision 2.7  1994/06/08  13:16:34  keith
 * Changed all timestep-related parameters to type "long". This means
 * that 16-bit DOS compilers can do more than 32767 timesteps.
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Added CONST qualifier to (re-)declarations of ANSI library
 * emulation routines to give reliable compilation even
 * without ANSI_LIBS macro. (#define's away for K&R
 * compilers)
 *
 *
 * Moved declaration of "match" structure from input.c
 * Moved a few sanity tests & modifications of "control"
 * members to here from other modules.
 *
 * Revision 2.5  94/01/24  18:20:11  keith
 * Null checkin for release compatibility.
 * 
 * Revision 2.4  94/01/24  18:18:35  keith
 * Deleted commented-out code for NR jacobi() function. Eigens() is
 * now well-tested.  Eliminated compiler warnings.
 * 
 * Added external "backup_restart" to flag that run started from a
 * backup file.  For dump().
 * 
 * 
 * Revision 2.3  93/10/28  10:28:13  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.0  93/03/15  14:49:22  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.6.1.27  93/03/13  01:45:58  keith
 * Replaced NR "jacobi.c" with "eigens.c" for public release.
 * 
 * Revision 1.6.1.26  93/03/12  12:14:28  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.6.1.26  93/03/09  15:59:15  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.6.1.25  92/10/28  14:09:53  keith
 * Changed "site_[tp]" typedefs to avoid name clash on HP.
 * 
 * Revision 1.6.1.24  92/09/22  14:48:08  keith
 * Tidied up calls to improve "lint" rating.
 * 
 * Revision 1.6.1.23  92/06/26  17:03:28  keith
 * Got rid of assumption that memory returned by talloc() or
 * arralloc() is zeroed.  This enhances ANSI compatibility.
 * Removed memory zeroing from alloc.c() in consequence.
 * 
 * Revision 1.6.1.22  92/06/16  12:42:49  keith
 * Zeroed unit cell velocities/accns if constraint changed.
 * 
 * Revision 1.6.1.21  92/06/11  21:39:58  keith
 * Fixed bug which meant you couldn't change backup file name in restart if backup existed.
 * Added file locking against multiple runs using same dump or backup files.
 * 
 * Revision 1.6.1.20  92/04/21  17:50:08  keith
 * Fixed bug which compared char to NULL instead of 0.
 * 
 * Revision 1.6.1.19  92/03/19  15:45:54  keith
 * Added support for dynamic allocation of rolling average arrays,
 * conversion of existing restart files is done on fly.
 * 
 * Revision 1.6.1.18  92/03/11  12:56:11  keith
 * Changed "scale-separately" parameter to "scale options"
 * 
 * Revision 1.6.1.17  91/08/16  15:26:09  keith
 * Checked error returns from fread, fwrite, fseek and fclose more
 * rigourously.   Called strerror() to report errors.
 * 
 * Revision 1.6.1.16  91/08/15  18:12:16  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_t and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.6.1.15  91/03/12  15:43:19  keith
 * Tidied up typedefs size_t and include file 
 * Added explicit function declarations.
 * 
 * Revision 1.6.1.14  91/02/04  18:20:08  keith
 * Call to inhibit vectorization() added on line 509 for titan.
 * Alloas use of vector_c option which otherwise caused crash.
 * 
 * Revision 1.6.1.13  90/10/25  19:00:26  keith
 * Made interpolate_derivatives() robust against interpolation from zero.
 * 
 * Revision 1.6.1.12  90/10/23  20:13:20  keith
 * Added dummy function call to inhibit vectorization.
 * This allows use of 'ivdep' compiler options and also
 * works round certain bugs in cray's scc compiler.
 * 
 * Revision 1.6.1.11  90/08/24  17:46:42  keith
 * Made 'reset-averages' parameter actually do something.
 * 
 * Revision 1.6.1.10  90/05/16  18:40:46  keith
 * Renamed own freer from cfree to tfree.
 * 
 * Revision 1.6.1.9  90/05/16  14:20:28  keith
 * *** empty log message ***
 * 
 * Revision 1.6.1.8  90/02/22  18:03:40  keith
 * Modified backup-restart code so as not to copy whole backup header into
 * restart header.  This erroneously wrote a timestamp to dumps after restrart.
 * 
 * Revision 1.6.1.7  89/11/21  16:32:38  keith
 * Removed member out_file from control and all uses. (Now command parameter).
 * Added new argument to specify output file.
 * Added default_control() to initialise control struct.
 * Fixed bug in start_up which tried to free unallocated var qpf.
 * 
 * Revision 1.6.1.6  89/11/01  17:06:24  keith
 * Made allocate_dynamics() externally visible to allow link with 'mdshak'.
 * 
 * Revision 1.6.1.5  89/10/05  18:18:02  keith
 * Modified initialise_sysdef() to stop rotation to princ frame of framework.
 * 
 * Revision 1.6.1.4  89/09/21  15:02:57  keith
 * Test for old timestep of zero when altering timestep, in which case do nothing.
 * 
 * Revision 1.6.1.3  89/09/19  14:50:15  keith
 * Fixed bug in check_sysdef which didn't increment species pointer.
 * 
 * Revision 1.6.1.2  89/09/05  10:28:10  keith
 * Fixed bug in code to disable Ewald sum for uncharged system (init.._sysdef()
 * Added calculation of species total charge.
 * 
 * Revision 1.6.1.1  89/08/31  10:14:40  keith
 * Mods to simulate framework structure
 * 
 * Revision 1.6  89/08/30  12:36:26  keith
 * Modified start_up to fix bug which only considered rotations
 * of one species.  In conjunction with change in lattice_start().
 * 
 * Revision 1.5  89/07/06  16:23:56  keith
 * Eliminated 'dump-offset' - renumbering starts at 1 for new dump run.
 * 
 * Revision 1.4  89/06/23  15:35:10  keith
 * print-control option deleted.
 * 
 * Revision 1.3  89/06/22  15:45:19  keith
 * Tidied up loops over species to use one pointer as counter.
 * 
 * Revision 1.2  89/06/14  17:57:48  keith
 * Control.out eliminated, use printf and freopen instead to direct output.
 * 
 * Revision 1.1  89/04/27  15:16:41  keith
 * Initial revision
 * 
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/startup.c,v 2.12 1996/08/15 14:35:39 keith Exp $";
#endif
/*========================== program include files ===========================*/
#include	"defs.h"
/*========================== Library include files ===========================*/
#include	
#include 	"string.h"
#include	"stddef.h"
#include	"stdlib.h"
#include	
/*========================== Program include files ===========================*/
#include	"structs.h"
#include	"messages.h"
/*========================== External function declarations ==================*/
gptr            *talloc();	       /* Interface to memory allocator       */
void            tfree();	       /* Free allocated memory	      	      */
void		read_control();
void		read_sysdef();
void		lattice_start();
void		conv_control();
void		conv_potentials();
void		init_averages();
void		convert_averages();
void		init_rdf();
void		re_re_header();
void		re_re_sysdef();
void		read_restart();
void		banner_page();
void		zero_real();
void		eigens();
void		transpose();
void		mat_vec_mul();
double          det();
void		q_mul();
void		rot_to_q();
void		print_sysdef();
char		*atime();
double		mdrand();
double		precision();
void		smdrand();
void		inhibit_vectorization();	/* Self-explanatory dummy     */
#if defined(ANSI) || defined(__STDC__)
void		note(char *, ...);	/* Write a message to the output file */
void		message(int *, ...);	/* Write a warning or error message   */
#else
void		note();			/* Write a message to the output file */
void		message();		/* Write a warning or error message   */
#endif
/*========================== External data references ========================*/
extern	contr_mt	control;       /* Main simulation control parms. */
extern int 		ithread, nthreads;
/*========================== GLOBAL variables ================================*/
CONST   unit_mt	prog_unit = {MUNIT, LUNIT, TUNIT, QUNIT};
static	unit_mt	input_unit;		/* Unit specification (see Convert.c) */
static	char		backup_lockname[L_name];
static	char		dump_lockname[L_name];
#ifdef	DEBUG
static	char	afmt[] = "    %8s = %8X %8s = %8X %8s = %8X %8s = %8X\
 %8s = %8X %8s = %8X\n";
#endif
/*
 *  Default backup and temporary file names if not set in "defs.h"
 */
#ifndef BACKUP_FILE
#define BACKUP_FILE	"MDBACKUP"
#endif
#ifndef TEMP_FILE
#define TEMP_FILE	"MDTEMPX"
#endif
/*========================== Control file keyword template ===================*/
/*
 * format SFORM is defined as %NAMLENs in structs.h, to avoid overflow.
 */
CONST match_mt	match[] = {
{"title",            SFORM,  "Test Simulation",(gptr*) control.title},
{"nsteps",           "%ld",  "0",            (gptr*)&control.nsteps},
{"step",             "%lf",  "0.005",        (gptr*)&control.step},
{"text-mode-save",   "%d",   "0",            (gptr*)&control.print_sysdef},
{"new-sys-spec",     "%d",   "0",            (gptr*)&control.new_sysdef},
{"scale-options"   , "%d",   "0",            (gptr*)&control.scale_options},
{"therm-options"   , "%d",   "0",            (gptr*)&control.scale_options},
{"surface-dipole",   "%d",   "0",            (gptr*)&control.surface_dipole},
{"lattice-start",    "%d",   "0",            (gptr*)&control.lattice_start},
{"sys-spec-file",    SFORM,  "",             (gptr*)control.sysdef},
{"restart-file",     SFORM,  "",             (gptr*)control.restart_file},
{"save-file",        SFORM,  "",             (gptr*)control.save_file},
{"dump-file",        SFORM,  "",             (gptr*)control.dump_file},
{"backup-file",      SFORM,  BACKUP_FILE,    (gptr*)control.backup_file},
{"temp-file",        SFORM,  TEMP_FILE,      (gptr*)control.temp_file},
{"strict-cutoff",    "%d",   "0",            (gptr*)&control.strict_cutoff},
{"xdr",    	     "%d",   "1",            (gptr*)&control.xdr_write},
{"strain-mask",	     "%d",   "200",	     (gptr*)&control.strain_mask},
{"nbins",            "%d",   "100",          (gptr*)&control.nbins},
{"seed",             "%ld",  "1234567",      (gptr*)&control.seed},
{"page-width",       "%d",   "132",          (gptr*)&control.page_width},
{"page-length",      "%d",   "44",           (gptr*)&control.page_length},
{"scale-interval",   "%ld",  "10",           (gptr*)&control.scale_interval},
{"const-pressure",   "%d",   "0",            (gptr*)&control.const_pressure},
{"const-temp",       "%d",   "0",            (gptr*)&control.const_temp},
{"reset-averages",   "%d",   "0",            (gptr*)&control.reset_averages},
{"scale-end",        "%ld",  "1000000",      (gptr*)&control.scale_end},
{"begin-average",    "%ld",  "1001",         (gptr*)&control.begin_average},
{"average-interval", "%ld",  "5000",         (gptr*)&control.average_interval},
{"begin-dump",       "%ld",  "1",            (gptr*)&control.begin_dump},
{"dump-interval",    "%ld",  "20",           (gptr*)&control.dump_interval},
{"dump-level",       "%d",   "0",            (gptr*)&control.dump_level},
{"ndumps",           "%d",   "250",          (gptr*)&control.maxdumps},
{"backup-interval",  "%ld",  "500",          (gptr*)&control.backup_interval},
{"roll-interval",    "%ld",  "10",           (gptr*)&control.roll_interval},
{"print-interval",   "%ld",  "10",           (gptr*)&control.print_interval},
{"begin-rdf",        "%ld",  "1000000",      (gptr*)&control.begin_rdf},
{"rdf-interval",     "%ld",  "20",           (gptr*)&control.rdf_interval},
{"rdf-out",          "%ld",  "5000",         (gptr*)&control.rdf_out},
{"temperature",      "%lf",  "0.0",          (gptr*)&control.temp},
{"pressure",         "%lf",  "0.0",          (gptr*)&control.pressure},
{"w",                "%lf",  "100.0",        (gptr*)&control.pmass},
{"rtmass",           "%lf",  "100.0",        (gptr*)&control.rtmass},
{"ttmass",           "%lf",  "100.0",        (gptr*)&control.ttmass},
{"cutoff",           "%lf",  "0.0",          (gptr*)&control.cutoff},
{"subcell",          "%lf",  "0.0",          (gptr*)&control.subcell},
{"density",          "%lf",  "1.0",          (gptr*)&control.density},
{"alpha",            "%lf",  "0.0",          (gptr*)&control.alpha},
{"k-cutoff",         "%lf",  "0.0",          (gptr*)&control.k_cutoff},
{"rdf-limit",        "%lf",  "10.0",         (gptr*)&control.limit},
{"cpu-limit",        "%lf",  "1.0e20",       (gptr*)&control.cpu_limit},
{"mass-unit",        "%lf",  AMUSTR,         (gptr*)&input_unit.m},
{"length-unit",      "%lf",  "1.0e-10",      (gptr*)&input_unit.l},
{"time-unit",        "%lf",  "1.0e-13",      (gptr*)&input_unit.t},
{"charge-unit",      "%lf",  ECSTR,          (gptr*)&input_unit.q},
{0,0,0,0}	     }; 		/* Null termination essential.	*/

/*============================================================================*
 * Functions for external access to globals.		      *
 *============================================================================*/
void	rmlockfiles()
{
   if( backup_lockname[0] )
      (void)remove(backup_lockname);
   if( dump_lockname[0] )
      (void)remove(dump_lockname);
}
/*============================================================================*/
/******************************************************************************
 *  default_control.   Initialise 'control' with default values from 'match' *
 ******************************************************************************/
static
void	default_control()
{
   CONST match_mt	*match_p;
   char	tmp[64];

   for( match_p = match; match_p->key; match_p++)
      (void)sscanf(strncpy(tmp,match_p->defalt, sizeof tmp),
		   match_p->format, match_p->ptr);
}
/******************************************************************************
 * gauss_rand. Return a random variable from a gaussian distribution with uni *
 * variance.  After Press, Plannery, Teulkolsk & Vetterling p202.             *
 ******************************************************************************/
static
double	gauss_rand()
{
   static int		set = 1;
   static double	gset;
   double		v1, v2, r, fac;
   if(set)
   {
      do
      {
         v1 = 2.0*mdrand()-1.0;  v2 = 2.0*mdrand() - 1.0;
         r = v1*v1 + v2*v2;
      } while(r > 1.0);
      fac = sqrt(-2.0 * log(r) / r);
      gset = v1 * fac;
      set = 0;
      return(v2*fac);
   }
   else
   {
      set = 1;
      return(gset);
   }
}
/******************************************************************************
 * random_quat.   Generate quaternions to represent uniform random rotations. *
 * If q=(cos(psi/2), l sin(psi/2)) , l is a unit vector, uniformity requires  *
 * that q(0) be distributed linearly ie p(q(0)) = q(0). L is a random unit    *
 * vector, generated from spherical co-ordinates theta and phi with           *
 * distribution p(theta) = sin(theta) 					      *
 ******************************************************************************/
static
void	random_quat(q, n)
quat_mp	q;				/* First quaternion		(out) */
int	n;				/* Number to be generated.       (in) */
{
   double	phi, cos_theta, sin_theta, st2;
   while(n-- > 0)
   {
      phi = 2.0*PI*mdrand();		/* Phi is uniform on [0, 2pi)	      */
      cos_theta = 1.0 - 2.0*mdrand();	/* 0 <= theta < pi, p(theta)=sin()    */
      sin_theta = sqrt(1.0 - SQR(cos_theta));
      (*q)[0] = sqrt(mdrand());
      st2 = sqrt(1.0 - SQR((*q)[0]));
      (*q)[1] = st2*sin_theta*sin(phi);
      (*q)[2] = st2*sin_theta*cos(phi);
      (*q)[3] = st2*cos_theta;
      q++;
   }
}
/******************************************************************************
 *  select_spec  Choose a species at random, weighted by proportion of whole  *
 ******************************************************************************/
static
spec_mp	select_spec(system, species)
system_mp	system;
spec_mt		species[];
{
   int		sel = mdrand() * system->nmols;
   spec_mp	spec;

   for (spec = species; spec < species+system->nspecies; spec++)
   {
      if(sel < spec->nmols)
         return(spec);
      sel -= spec->nmols;
   }
   return((spec_mp)-1);
}
/******************************************************************************
 *  skew_start.  Make a starting configuration with all molecules arranged at *
 *  regular intervals on a line passing at an angle through the MD cell.  This*
 *  permits a reasonable spacing between molecules without restricting the    *
 *  number allowed as in a lattice start.                                     *
 ******************************************************************************/
static
void	skew_start(system, species)
system_mp	system;
spec_mt	species[];
{
   int		ispec, imol;		/* Counters for species, molecules etc*/
   spec_mp	spec;
   double	mass = 0.0;		/* Whole system mass		      */
   double	n_third = pow((double)system->nmols,1.0/3.0);
   int		nz = 1, ny = (int)(n_third+0.5), nx = (int)(SQR(n_third)+0.5);
   int		*nmols = ialloc(system->nspecies), nm;
   double	delta_x = (double)nx / system->nmols,
                delta_y = (double)ny / system->nmols,
                delta_z = (double)nz / system->nmols;

   for (spec = species; spec < species+system->nspecies; spec++)
   {
      inhibit_vectorization();      /* Circumvent cray scc bug  */
      mass += spec->mass * spec->nmols;
   }
      
   system->h[0][0] = system->h[1][1] = system->h[2][2] 
                   = pow(mass/control.density, 1.0/3.0);
 					/* L = cube root of mass/density      */
   memst(nmols, 0, system->nspecies*sizeof(int));
   for(imol = 0; imol < system->nmols; imol++)
   {
      do
      {
         spec = select_spec(system, species);		/* Choose species     */
	 ispec = spec-species;
      }
      while(nmols[ispec] >= spec->nmols);	/* Repeat if all set  */

      nm = nmols[ispec];
      spec->c_of_m[nm][0] = imol*delta_x;
      spec->c_of_m[nm][1] = imol*delta_y;
      spec->c_of_m[nm][2] = imol*delta_z;
      nmols[ispec]++;
   }

   random_quat(system->quat, system->nmols_r);
   xfree(nmols);
}
/******************************************************************************
 * random_start.  This function generates a completely random starting        *
 * configuration.  Molecules are placed at random locations and orientations  *
 * in md box, whose size is chosed to give the required density.	      *
 ******************************************************************************/
#ifdef NOT_FOR_NOW
void	random_start(system, species)
system_mp	system;
spec_mt	species[];
{
   int		imol, i;		/* Counters for species, molecules etc*/
   double	mass = 0.0;		/* Whole system mass		      */

   for (spec = species; spec < species+system->nspecies; spec++)
      mass += spec->mass * spec->nmols;
      
   system->h[0][0] = system->h[1][1] = system->h[2][2] 
                   = pow(mass/control.density, 1.0/3.0);
    					/* L = cube root of mass/density      */
   for(imol = 0; imol < system->nmols; imol++)
      for(i = 0; i < 3; i++)		/* Centre of mass co-ords -1 < x < 1  */
         system->c_of_m[imol][i] = mdrand() - 1.0;
   random_quat(system->quat, system->nmols_r);
}
#endif
/******************************************************************************
 *  thermalise  set velocities and quaternion derivatives to values sampled   *
 *  at random from the Boltzmann distribution for the required temperature.   *
 ******************************************************************************/
void	thermalise(system, species)
system_mp	system;
spec_mt	species[];
{
   int		imol, i;		/* Counters for species, molecules etc*/
   spec_mp	spec;			/* Pointer to species[ispec]	      */
   double	omega_sq;		/* |omega|squared / 4		      */
   double	root_ktm, root_kti[3];	/* Gaussian widths of MB distribution */
   double	total_mass = 0;
   vec_mt	momentum;	      	/* Whole system momentum	      */

   zero_real(momentum, 3);
   /*
    *  Set accelerations to zero.
    */
   zero_real(system->vel[0],   3*system->nmols);
   zero_real(system->acc[0],   3*system->nmols);
   zero_real(system->acco[0],  3*system->nmols);
   zero_real(system->accvo[0], 3*system->nmols);

   zero_real(system->ta,      system->nspecies);
   zero_real(system->tap,     system->nspecies);
   zero_real(system->tadot,   system->nspecies);
   zero_real(system->tadoto,  system->nspecies);
   zero_real(system->tadotvo, system->nspecies);
    
   zero_real(system->ra,      system->nspecies);
   zero_real(system->rap,     system->nspecies);
   zero_real(system->radot,   system->nspecies);
   zero_real(system->radoto,  system->nspecies);
   zero_real(system->radotvo, system->nspecies);
   
   for (spec = species; spec < species+system->nspecies; spec++)
   {
      if( !spec->framework)
      {
	 root_ktm = sqrt(kB * control.temp / spec->mass) / system->h[0][0];
	 total_mass += spec->mass*spec->nmols;
	 for(imol = 0; imol < spec->nmols; imol++)
	    for(i = 0; i < 3; i++)	/* Centre of mass co-ords -1 < x < 1  */
	    {
	       spec->vel[imol][i]    = root_ktm * gauss_rand();
	       momentum[i] += spec->mass*spec->vel[imol][i];
	    }
	 
	 if(spec->rdof > 0)
	 {
	    for(i = 0; i < 3; i++)
	       if(spec->inertia[i] != 0.0)
		  root_kti[i] = 0.5*sqrt(kB * control.temp / spec->inertia[i]);
	       else
		  root_kti[i] = 0.0;
	    
	    for(imol = 0; imol < spec->nmols; imol++)
	    {
	       spec->qdot[imol][0] = 0.0;
	       for(i = 0; i < 3; i++)	/* Centre of mass co-ords -1 < x < 1  */
		  spec->qdot[imol][i+1] = root_kti[i] * gauss_rand();
	       omega_sq = -SUMSQ2(spec->qdot[imol]);
	       for(i = 0; i < 4; i++)
		  spec->qddotvo[imol][i] = spec->qddoto[imol][i] =
		     spec->qddot[imol][i] = omega_sq*spec->quat[imol][i];
	    }
	 }
      }
   }
   q_mul(system->quat, system->qdot, system->qdot, system->nmols_r);
   for (spec = species; spec < species+system->nspecies; spec++)
      if( !spec->framework)
      {
	 for(i = 0; i < 3; i++)
	    
	    for(imol = 0; imol < spec->nmols; imol++)
	       spec->vel[imol][i] -= momentum[i] / total_mass;
      }
}
/******************************************************************************
 *  initialise_sysdef    Computes various quanities and completes the set-up  *
 *  of the system once it has been read in.  Quantities computed are :-       *
 *  Total numbers of molecules, sites, degrees of freedom in whole system,    *
 *  mass and inertia tensor for each species.  The molecular site co-         *
 *  ordinates are re-expressed wrt the molecular centre of mass, the inertia  *
 *  tensor is diagonalised and the site co-ordinates rotated to the principal *
 *  frame.								      *
 ******************************************************************************/
#define LTR(i,j) (((i)*(i)+(i))/2+(j))
void	initialise_sysdef(system, species, site_info, qpf)
system_mp	system;
spec_mt		species[];
site_mt		site_info[];
quat_mt		qpf[];			/* Quaternion rotation to princ.frame*/
{
   vec_mt	c_of_m;			/* Co-ordinates of centre of mass    */
   vec_mt	dipole;			/* Molecular dipole moment	     */
   real 	inertia[6];		/* Inertia tensor		     */
   mat_mt	v;			/* Transformation matrix to prin. fr.*/
   spec_mp	spec;			/* Used for looping over species     */
   double	mass;			/* Temporary for site mass	     */
   int		nz;			/* Count of zero moments of inertia  */
   int		i, j, isite, id; 	/* Various loop counters	     */
   boolean	flag;			/* Used to test for charges	     */
   double	imax;			/* Largest moment of inertia	     */
   double	eps = 10.0*precision(); /* Criterion for "zero" moment.	     */

   system->nsites  = 0;  system->nmols  = 0;
   system->nmols_r = 0;  system->d_of_f = 0;

   for (spec = species; spec < species+system->nspecies; spec++)
   {					/* Loop over molecular species       */
      system->nmols  += spec->nmols;
      system->nsites += spec->nmols * spec->nsites;
      if( !spec->framework )
	 system->d_of_f += spec->nmols * 3;

      zero_real(c_of_m,3);		/* Initialise C_of_M for this species*/
      zero_real(dipole,3);		/* And dipole moment		     */
      spec->mass = 0.0;			/* Mass				     */
      spec->charge = 0.0;		/* And total charge		     */
      for(isite=0; isite < spec->nsites; isite++) /* Calculate (sum m*r) and */
      {					/* molecular mass.		     */
         for(i=0; i<3; i++)
            c_of_m[i] += spec->p_f_sites[isite][i] 
                         * site_info[spec->site_id[isite]].mass;
         spec->mass += site_info[spec->site_id[isite]].mass;
	 spec->charge += site_info[spec->site_id[isite]].charge;
      }

      if(spec->mass < 1.0)		/* Lighter than 1 amu ?              */
         message(NULLI,NULLP,FATAL,ZMASS,spec->name,spec->mass);

      for(i=0; i < 3; i++)		/* Finish calculation of c. of mass. */
         c_of_m[i] /= spec->mass;

      for(isite=0; isite < spec->nsites; isite++)
         for(i=0; i < 3; i++)		/* Subtract c_of_m from co-ordinates */
            spec->p_f_sites[isite][i] -= c_of_m[i];

      if(spec->nsites > 1)		/* If molecule is polyatomic         */
      {
         zero_real(inertia,6);	/* Initialise inertia tensor	     */
         for(isite=0; isite < spec->nsites; isite++)
         {
            mass = site_info[spec->site_id[isite]].mass;
            for(i=0; i < 3; i++)	/* Calculate inertia tensor	     */
            {
               inertia[LTR(i,i)] += mass * SUMSQ(spec->p_f_sites[isite]);
               for(j=0; j <= i; j++)
         	  inertia[LTR(i,j)] -= mass * spec->p_f_sites[isite][i] 
         	                            * spec->p_f_sites[isite][j];
            }
         }
#ifdef	DEBUG
         printf(" *D* Molecule type %d, mass = %g, C of M = (%g,%g,%g)\n",
                spec-species, spec->mass, c_of_m[0], c_of_m[1], c_of_m[2]);
         print_mat(inertia, " *D* Inertia Tensor");
#endif
	 eigens(inertia,v[0],spec->inertia,3);
	 rot_to_q(v, qpf[spec-species]);	/* make equivalent quaternion*/
#ifdef	DEBUG
         print_mat(v," *D* Rotation Mat.");
#endif
	 imax = MAX3(spec->inertia[0],spec->inertia[1],spec->inertia[2]);
	 nz = 0;
	 for( i=0; i<3; i++)			 /* Count zero  moments.     */ 
	    if( spec->inertia[i] < eps*imax )
	    {
	       nz++;
	       spec->inertia[i] = 0.0;
	    }
	    
         spec->rdof = 3-nz;			/* Rotational deg. of freedom*/
	 if( spec->framework )			/* Frameworks can't rotate   */
	 {
	    spec->rdof = 0;
	    spec->quat = 0;
	 }
         if(spec->rdof > 0)			/* Count molecules with      */
	 {
            system->nmols_r += spec->nmols;     /* rotational freedom.       */
	    mat_vec_mul(v,spec->p_f_sites, spec->p_f_sites, spec->nsites);
	 }
         system->d_of_f += spec->rdof * spec->nmols;/* Count total d of f    */

	 for(i = 0; i < 3; i++)			/* Calculate molecular dipole*/
	    for(isite = 0; isite < spec->nsites; isite++)
	       dipole[i] += spec->p_f_sites[isite][i] * 
	       		    site_info[spec->site_id[isite]].charge;
	 spec->dipole = sqrt(SUMSQ(dipole));
      }
      else
      {
	 spec->dipole = 0;
	 spec->rdof = 0;
	 zero_real(spec->inertia,3);
	 spec->quat = 0;
      }
   }
#ifdef	DEBUG
   printf(" *D* Totals: nsites = %d, nmols = %d, nmols_r = %d, dof = %d\n",
          system->nsites, system->nmols, system->nmols_r, system->d_of_f);
#endif
   
   flag = false;			/* Test to see if any charges present */
   for(id = 1; id < system->max_id; id++)
      flag |= (site_info[id].charge != 0.0);
   if(!flag) control.alpha = -1.0;	/* Don't call Ewald sum if not	      */
}
/******************************************************************************
 *  allocate_dynamics	 Allocate memory for the dynamic MD variables         *
 ******************************************************************************/
void	allocate_dynamics(system, species)
system_mp	system;
spec_mt	species[];
{
   spec_mp	spec;			/* Alias for species[ispec]           */
   int		nmol_cum = 0,		/* Cumulative number of molecules     */
   		nmolr_cum = 0;		/* As above excluding point atoms     */

   system->c_of_m = ralloc(system->nmols);
   system->vel    = ralloc(system->nmols);
   system->velp   = ralloc(system->nmols);
   system->acc    = ralloc(system->nmols);
   system->acco   = ralloc(system->nmols);
   system->accvo  = ralloc(system->nmols);
#ifdef	DEBUG
   printf(" *D* System Dynamic variables (all %d x 3 reals)\n",system->nmols);
   printf(afmt,"c_of_m",system->c_of_m,"vel",system->vel,"velp",system->velp,
          "acc",system->acc,"acco",system->acco,"accvo",system->accvo);
#endif

   system->ta      = dalloc(system->nspecies);
   system->tap     = dalloc(system->nspecies);
   system->tadot   = dalloc(system->nspecies);
   system->tadoto  = dalloc(system->nspecies);
   system->tadotvo = dalloc(system->nspecies);

   system->ra      = dalloc(system->nspecies);
   system->rap     = dalloc(system->nspecies);
   system->radot   = dalloc(system->nspecies);
   system->radoto  = dalloc(system->nspecies);
   system->radotvo = dalloc(system->nspecies);

   if(system->nmols_r > 0)
   {
      system->quat   = qalloc(system->nmols_r);
      system->qdot   = qalloc(system->nmols_r);
      system->qdotp  = qalloc(system->nmols_r);
      system->qddot  = qalloc(system->nmols_r);
      system->qddoto = qalloc(system->nmols_r);
      system->qddotvo= qalloc(system->nmols_r);
   }
   else
      system->quat = system->qdot = system->qdotp = 
	             system->qddot = system->qddoto = system->qddotvo= 0;


   system->h       = ralloc(3);
   system->hdot    = ralloc(3);
   system->hdotp   = ralloc(3);
   system->hddot   = ralloc(3);
   system->hddoto  = ralloc(3);
   system->hddotvo = ralloc(3);
   zero_real(system->h[0],       9);
   zero_real(system->hdot[0],    9);
   zero_real(system->hddot[0],   9);
   zero_real(system->hddoto[0],  9);
   zero_real(system->hddotvo[0], 9);
#ifdef	DEBUG
   printf(" *D* System Dynamic variables (all 9 reals)\n");
   printf(afmt,"h",system->h, "hdot",system->hdot, "hdotp",system->hdotp,
       "hddot",system->hddot,"hddoto",system->hddoto,"hddotvo",system->hddotvo);
#endif

   for (spec = species; spec < species+system->nspecies; spec++)
   {
      inhibit_vectorization();
      spec->c_of_m = system->c_of_m + nmol_cum;
      spec->vel    = system->vel    + nmol_cum;
      spec->velp   = system->velp   + nmol_cum;
      spec->acc    = system->acc    + nmol_cum;
      spec->acco   = system->acco   + nmol_cum;
      spec->accvo  = system->accvo  + nmol_cum;
#ifdef	DEBUG
      printf(" *D* Species %d Dynamic variables (all %d x 3 reals)\n",
	     spec-species, spec->nmols);
      printf(afmt,"c_of_m",spec->c_of_m,"vel",spec->vel,"velp",spec->velp,
          "acc",spec->acc,"acco",spec->acco,"accvo",spec->accvo);
#endif
      if(spec->rdof > 0)
      {
         spec->quat    = system->quat    + nmolr_cum;
         spec->qdot    = system->qdot    + nmolr_cum;
         spec->qdotp   = system->qdotp   + nmolr_cum;
         spec->qddot   = system->qddot   + nmolr_cum;
         spec->qddoto  = system->qddoto  + nmolr_cum;
         spec->qddotvo = system->qddotvo + nmolr_cum;
         nmolr_cum += spec->nmols;
#ifdef	DEBUG
         printf(" *D* Species %d Dynamic variables (all %d x 4 reals)\n",
		spec-species, spec->nmols);
         printf(afmt,"quat",spec->quat, "qdot",spec->qdot, "qdotp",spec->qdotp,
             "qddot",spec->qddot,"qddoto",spec->qddoto,"qddotvo",spec->qddotvo);
#endif
      }
      else
	 spec->quat = spec->qdot = spec->qdotp = 
	              spec->qddot = spec->qddoto = spec->qddotvo= 0;
      nmol_cum += spec->nmols;
   }
   /*
    * These may not be initialised if reading an old restart file,
    * so zero them here for safety.
    */
   zero_real(system->ta,      system->nspecies);
   zero_real(system->tap,     system->nspecies);
   zero_real(system->tadot,   system->nspecies);
   zero_real(system->tadoto,  system->nspecies);
   zero_real(system->tadotvo, system->nspecies);
    
   zero_real(system->ra,      system->nspecies);
   zero_real(system->rap,     system->nspecies);
   zero_real(system->radot,   system->nspecies);
   zero_real(system->radoto,  system->nspecies);
   zero_real(system->radotvo, system->nspecies);
}
/******************************************************************************
 *  Interpolate_derivatives & interp.    Interp is a quadratic interpolation  *
 *  routine for scaling derivatives for a new timestep.                       *
 *  Interpolate_derivatives calls it for all the dynamic variables.	      *
 ******************************************************************************/
static
void	interp(ratio, x, xo, xvo, n)
double	ratio;				/* Between old and new timesteps      */
real	*x, *xo, *xvo;			/* Pointers to 1st dynamic variable   */
int	n;				/* Size of arrays x, xo, xvo          */
{
   double	c1 = 1.0 - 1.5*ratio + 0.5*SQR(ratio),
   		c2 = 2.0*ratio - SQR(ratio),
   		c3 = -0.5*ratio + 0.5*SQR(ratio),
   		c4 = 1.0 - 3.0*ratio + 2.0*SQR(ratio),
   		c5 = 4.0*ratio - 4.0*SQR(ratio),
   		c6 = -1.0*ratio + 2.0*SQR(ratio);
   double	tmp;
   
   while(n-- > 0)
   {
      tmp	= *xo;
      *xo	= c1 * *x + c2 * *xo + c3 * *xvo;
      *xvo	= c4 * *x + c5 * tmp + c6 * *xvo;
      x++; xo++; xvo++;
   }
}
static
void	interpolate_derivatives(sys, step, step1)
system_mp	sys;
double		step, step1;
{
   double	ratio;
   if( step == 0.0 )
   {
      message(NULLI, NULLP, WARNING, ZEROTS, step1);
      return;
   }
   ratio = step1/step;
   message(NULLI, NULLP, INFO, NEWTS, step, step1);
   interp(ratio, sys->acc[0],   sys->acco[0],  sys->accvo[0],   3*sys->nmols);
   if( sys->nmols_r > 0 )
      interp(ratio, sys->qddot[0], sys->qddoto[0],sys->qddotvo[0], 
	     4*sys->nmols_r);
   interp(ratio, sys->hddot[0], sys->hddoto[0],sys->hddotvo[0], 9);
   interp(ratio, sys->tadot, sys->tadoto, sys->tadotvo, sys->nspecies);
   interp(ratio, sys->radot, sys->radoto, sys->radotvo, sys->nspecies);
}
/******************************************************************************
 *  check_sysdef.   		Read in system specification from the restart *
 *  file and check its consistency with the previously defined spec.  This is *
 *  for use when replacing the system spec in the middle of a run.  The system*
 *  spec from restart is discarded.  Only the minimum checks are applied for  *
 *  the dynamic variables stored in the restart file to make sense with the   *
 *  new system spec.  Checked are a) Number of species, b) Number of molecules*
 *  of each species and c) that the molecules have the same rotational degrees*
 *  of freedom.  The number of sites can change subject to c).                *
 ******************************************************************************/
static
void	check_sysdef(restart, vsn, system, species)
FILE		*restart;		/* Restart file pointer		      */
char            *vsn;                   /* restart file version.              */
system_mp	system;			/* NEW 'system' struct		      */
spec_mt	species[];		        /* NEW 'species' struct array	      */
{
   system_mt	sys_tmp;		/* Local temporaries of system,       */
   spec_mp	spec_tmp, spec;		/* species, site_info and potpar      */
   site_mp	site_tmp;		/* used when overwriting restart      */
   pot_mp	pot_tmp;		/* sysdef.			      */

   re_re_sysdef(restart, vsn, &sys_tmp, &spec_tmp, &site_tmp, &pot_tmp);

   if(system->nspecies != sys_tmp.nspecies)
      message(NULLI, NULLP, FATAL, NSPCON, system->nspecies, sys_tmp.nspecies);
   for (spec = species; spec < species+system->nspecies; spec++, spec_tmp++)
   {
      if(spec->nmols != spec_tmp->nmols)
         message(NULLI, NULLP, FATAL, NMLCON, spec_tmp->name,
                 spec->nmols, spec_tmp->nmols);
      if(spec->rdof != spec_tmp->rdof)
         message(NULLI, NULLP, FATAL, NDFCON, spec_tmp->name,
                 spec->rdof, spec_tmp->rdof);
      xfree(spec_tmp->p_f_sites);
      xfree(spec_tmp->site_id);
   }
   xfree((spec_tmp-system->nspecies));
   xfree(site_tmp);
   xfree(pot_tmp);
}
/******************************************************************************
 * Init_cutoffs.  Set the initial values of the alpha parameter and cutoffs   *
 * for the Ewald Sum according to the formulae in Fincham, (1993) CCP5 38, p17*
 * alpha = (nsites*pi^3/V^2 tr/tf)^1/6 is the optmum value		      *
 * r_c = p^1/2 / alpha and k_c = 2 * alpha / p^1/2                            *
 * are the cutoffs which giva an accuracy of exp(-p). Here use 10^-5.         *
 * Any value explicity specified in the control file is left alone.           *
 ******************************************************************************/
#define LOGACC   11.5 /* p =11.5 <=> acc = 10^-5 */
#define TRoverTF 5.5  /* Ratio of indiv. interaction times - approx */
void      init_cutoffs(alpha, cutoff, k_cutoff, h, nsites)
double   *alpha, *cutoff, *k_cutoff;
mat_mt   h;
int      nsites;
{
   double max_cutoff = MIN3(h[0][0], h[1][1], h[2][2]);
   double vol = det(h);

   if( *alpha == 0.0 )
      *alpha = pow(nsites*CUBE(PI)/SQR(vol)*TRoverTF, 1.0/6.0);
   if( *alpha > 0.0 )
   {
      if( *cutoff == 0.0 )
      {
	 *cutoff = sqrt(LOGACC)/ *alpha;
	 if( *cutoff > max_cutoff )
	 {
	    message( NULLI, NULLP, WARNING, MAXCUT, *cutoff, max_cutoff);
	    *cutoff = max_cutoff;
	 }
      }
      if( *k_cutoff == 0.0 )
	 *k_cutoff = 2.0* (*alpha) * sqrt(LOGACC);
   }
   else if ( *cutoff <= 0.0 )
      message(NULLI, NULLP, FATAL, NOCUT, *cutoff);
}
/******************************************************************************
 *  startup	This function sets up everything that is needed to start a    *
 *  run.  It controls the reading in of the control, system specification and *
 *  restart files, conversions to program units, calculation of molecular     *
 *  mass & moments of inertia and evaluation of 'whole system' quantities eg  *
 *  number of molecules.  						      *
 ******************************************************************************/
void	start_up(contr_name, out_name, system, species, site_info, 
		 potpar, restart_header, backup_restart)
char		*contr_name,		/* Name of control file "" for stdin  */
   		*out_name;		/* Name of output file "" for stdout  */
system_mp	system;			/* Pointer to system struct	      */
spec_mp		*species;		/* Pointer to species array	      */
site_mp		*site_info;		/* Pointer to site_info array	      */
pot_mp		*potpar;		/* Pointer to pot'l parameter array   */
restrt_mt	*restart_header;	/* Pointer to restart hdr info (out)  */
int		*backup_restart;	/* (ptr to) flag said purpose   (out) */
{
   FILE		*contr_file,		/* File pointer for control read      */
   		*sysdef,		/* File pointer for sysdef file read  */
   		*backup = NULL,		/* File pointer for backup file read  */
   		*restart = NULL,	/* File pointer for restart file read */
   		*lock;			/* File pointer for lockfile	      */
   double	old_step;		/* Timestep read from restart file    */
   long		old_dump_interval;	/* To check if altered on restart     */
   int		old_max_dumps;		/* To check if altered on restart     */
   long		old_roll_interval;	/* To check if altered on restart     */
   boolean	flag;			/* Used to test 'fseek'		      */
   long		pos;			/* Where control info starts on input */
   restrt_mt	backup_header;		/* To read backup file header into    */
   contr_mt	backup_control;		/* Control struct from backup file    */
   quat_mt	*qpf=0;			/* Quat of rotation to princ. frame   */
   int		av_convert;		/* Flag for old-fmt averages in restrt*/
   int		i;
   *backup_restart = 0;
   (void)memst(restart_header,0,sizeof(*restart_header));

   if(contr_name[0] == '\0')		/* Null name - read control from      */
      contr_file = stdin;		/* standard input.		      */
   else					/* Open named file for reading control*/
   {
      contr_file = fopen(contr_name,"r");
      if(contr_file == NULL)
         message(NULLI, NULLP, FATAL, OCFAIL, contr_name, strerror(errno));
   }
   pos = ftell(contr_file);		/* Current file pos needed for cray   */
   default_control();			/* Set up default values of params    */
   read_control(contr_file, match);	/* Do keyword read of control params  */
   conv_control(&prog_unit,true);	/* Convert to program units           */

   if(control.restart_file[0] != '\0')	/* Open restart file, get backup name */
   {
      if((restart = fopen(control.restart_file,"rb")) == NULL)
         message(NULLI, NULLP, FATAL, ORFAIL, control.restart_file, 
		 strerror(errno));
      re_re_header(restart, restart_header, &control);
      /*
       *  Now reread control file to override restart file defaults.
       *  Need to do this here in case backup file name changed.
       */
      old_step               = control.step; 		/* Needed for scaling */
      old_roll_interval	     = control.roll_interval;   /* In case it changed */
      old_dump_interval	     = control.dump_interval;	/* Check if these par */
      old_max_dumps	     = control.maxdumps;	/* -amaters altered.  */
      conv_control(&prog_unit, false);
      control.scale_end     -= control.istep;		/* These parameters   */
      control.begin_average -= control.istep;		/* are respecified    */
      control.begin_rdf     -= control.istep;		/* RELATIVE to current*/
      control.begin_dump    -= control.istep;		/* time step.	      */
      control.nsteps	    -= control.istep;

      flag = fseek(contr_file,pos,0);			/* Rewind control file*/
      if( flag )
         message(NULLI,NULLP,FATAL,SEFAIL,contr_name[0]?contr_name:"stdin",
		 strerror(errno));
      read_control(contr_file, match);			/* Reread control file*/
      conv_control(&prog_unit,true);			/* Back to prog units */
   }
   /*
    *  At this point we have read the control file, read the control
    *  parameters from the restart file (if specified).  Now we
    *  know the name of the backup file (Whew!).  Now we have
    *  all the parameter values we can set up and test the lockfiles.
    */
   if( ithread == 0 && control.backup_file[0] )
      (void)strcat(strncpy(backup_lockname,control.backup_file,L_name-5),
		   LOCKEX);
   if( ithread == 0 && control.dump_file[0] )
   {
      (void)sprintf(dump_lockname, control.dump_file, 0);
      (void)strncat(dump_lockname, LOCKEX, L_name-1);
   }
   if( backup_lockname[0] )
   {
      if( fopen(backup_lockname,"r") )
	 message(NULLI, NULLP, -FATAL, LOCKED, "backup", backup_lockname);
      if( (lock=fopen(backup_lockname,"w")) != 0 )
	 (void)fclose(lock);
      else
	 message(NULLI, NULLP, WARNING, LOCKFL, backup_lockname);
   }
   if( dump_lockname[0] )
   {
      if( fopen(dump_lockname,"r") )
      {
	 if( backup_lockname[0] )
	    remove(backup_lockname);
	 message(NULLI, NULLP, -FATAL, LOCKED, "dump", dump_lockname);
      }
      if( (lock=fopen(dump_lockname,"w")) != 0 )
	 (void)fclose(lock);
      else
	 message(NULLI, NULLP, WARNING, LOCKFL, dump_lockname);
   }

   /*
    *  Check for the existance of a backup file and restart from it.
    */
   if( control.backup_file[0] != '\0' &&
      ( backup = fopen(control.backup_file,"rb")) != NULL )  /* Backup exists */
   {
      re_re_header(backup, &backup_header, &backup_control);
      if(restart && (backup_header.timestamp < restart_header->timestamp))
         backup = NULL;			/* Backup older than restart-don't use*/
   }

   if( backup )
   /* We are restarting from backup file. File is already open and header and *
    * control struct have been read into backup_header and backup_control resp*
    * 1) Set number of steps to number yet to do.			      *
    * 2) Copy backup header and control into the correct structs.	      *
    * 3) Read rest of backup file as for ordinary restart, omitting reread of *
    *    control and scaling since nothing can have changed.		      */
   {
      control = backup_control;
      (void)strcpy(restart_header->init_date, backup_header.init_date);
      (void)strcpy(restart_header->title,backup_header.title);
      re_re_sysdef(backup,backup_header.vsn,system,species,site_info,potpar);
      allocate_dynamics(system, *species);/* Memory for dynamic variables     */
      init_averages(system->nspecies, backup_header.vsn,
		    control.roll_interval, control.roll_interval,&av_convert);
      if(control.rdf_interval > 0)
         init_rdf(system);		/* Prepare to calculate rdf	      */
      read_restart(backup, backup_header.vsn, system, av_convert); 
                                        /* Saved dynamic vars etc             */
      convert_averages(control.roll_interval, control.roll_interval,av_convert);
      (void)fclose(backup);
      (*backup_restart)++;
      message(NULLI, NULLP, INFO, BACKUP, control.backup_file);
   }
   else if( !restart )			
   /* Initiate a new run from scratch, ie not restart			      *
    * 1) Read the system specification.  This is either in a file of its own  *
    *    or follows the control info.                                         *
    * 2) Convert potential parameters, site masses and charges to prog. units.*
    * 3) Calculate molecular masses, moments of inertia and nmols etc.        *
    * 4) Allocate memory for the MD dynamic variables.			      *
    * 5) Call the dynamic var initialisation routine.			      *
    * 6) Set up averages data structures.				      */
   {
      if( control.sysdef[0] == '\0' || strcmp(control.sysdef,contr_name) == 0 )
         sysdef = contr_file;		/* Sys def'n is tacked onto control   */
      else				/* Sys def'n is in separate file      */
      {					/* Open system specification file     */
         sysdef = fopen(control.sysdef,"r");
         if(sysdef == NULL)
            message(NULLI, NULLP, FATAL, ODFAIL,control.sysdef,strerror(errno));
      }
					/* Read system specification file     */
      read_sysdef(sysdef, system, species, site_info, potpar);

      conv_potentials(&input_unit, &prog_unit, *potpar, system->n_potpar,
                         system->ptype, *site_info, system->max_id);
      qpf = qalloc(system->nspecies);
      initialise_sysdef(system, *species, *site_info, qpf);
      allocate_dynamics(system, *species);	/* Allocate dynamic arrays    */

      smdrand(control.seed);			/* Seed random number generato*/
      if( control.lattice_start )		/* Choose startup method      */
	 lattice_start(sysdef, system, *species, qpf); /* Lattice - from file */
      else	
         skew_start(system, *species);		/* Start from skew lattice    */
      thermalise(system, *species);		/* Initialise velocities      */
      
      init_averages(system->nspecies, (char*)0,
		    control.roll_interval, control.roll_interval ,&av_convert);
      if( control.rdf_interval > 0 )
         init_rdf(system);			/* Prepare to calculate rdf   */
      if(control.limit <= 0.0)			/* Choose RDF limit           */
	 control.limit = 0.5*MIN3(system->h[0][0],system->h[1][1],
				  system->h[2][2]);

      init_cutoffs(&control.alpha, &control.cutoff, &control.k_cutoff, 
		   system->h, system->nsites);

      (void)strcpy(restart_header->init_date, atime());
      (void)strcpy(restart_header->title,control.title);

      if(sysdef != contr_file)
         (void)fclose(sysdef);			/* Finished with sys spec file*/
   }
   else
   /* Continue from a restart file.  The restart file contains a header, the  *
    * saved 'control' struct, system specification, dynamic variables and     *
    * averages.  The old 'control' values act as defaults for this run. To do *
    * this they are read into 'control', and the control file is rewound and  *
    * reread.  To keep the units consistent, the old control is converted to  *
    * the NEW input units before rereading, and then back to program units.   *
    * Also the scale_end and begin_average parameters are incremented by the  *
    * initial timestep only if they are explicitly specified.                 *
    * By the time we get here, this has all been done.			      *
    *									      *
    * The next thing to be read is the system spec.  Normally this is just    *
    * taken from the restart file, but a facility is provided to allow its    *
    * replacement with a setup from a new system spec 'source' file.  Its     *
    * consistency with the old sys-spec is only checked in so far as the size *
    * and shape of the dynamic variable arrays are identical. Beware.         *
    * Memory is allocated for the dynamic variables and averages which are    *
    * read from the input file.  Finally if the timestep has been altered the *
    * accelerations at previous timesteps are adjusted to the new step.       */
   {
      control.scale_end     += control.istep;
      control.begin_average += control.istep;
      control.begin_rdf     += control.istep;
      control.begin_dump    += control.istep;
      control.nsteps	    += control.istep;
      if( control.maxdumps != old_max_dumps ||		/* Need to restart    */
	  control.dump_interval != old_dump_interval )	/* dump seq if changed*/
      {
         control.begin_dump = control.istep + 1;	/* Set new beginning  */
      }

      if( !control.new_sysdef )		/* Usual case, get sysdef from restart*/
         re_re_sysdef(restart, restart_header->vsn, 
		      system, species, site_info, potpar);
      else				/* Get sysdef from new sys-spec file  */
      {
         if( control.sysdef[0]=='\0' || strcmp(control.sysdef,contr_name) == 0 )
            sysdef = contr_file;	/* Sys def'n is tacked onto control   */
         else				/* Sys def'n is in separate file      */
         {				/* Open system specification file     */
            sysdef = fopen(control.sysdef,"r");
            if( sysdef == NULL )
               message(NULLI,NULLP,FATAL,OSFAIL,control.sysdef,strerror(errno));
         }
	 /* Read in and set up new system spec (just as in case of new run)   */
         read_sysdef(sysdef, system, species, site_info, potpar);
         conv_potentials(&input_unit, &prog_unit, *potpar, system->n_potpar,
                            system->ptype, *site_info, system->max_id);
#ifdef	DEBUG
         printf(" *D* Read and converted new system specification\n");
#endif
	 qpf = qalloc(system->nspecies);
         initialise_sysdef(system, *species, *site_info, qpf);
	 /* Consistent with saved one?*/
         check_sysdef(restart, restart_header->vsn, system, *species);
         if(sysdef != contr_file)
            (void)fclose(sysdef);
         control.reset_averages = 1;	/* Averages invalid if sysdef changed */
      }
      allocate_dynamics(system, *species);/* Memory for dynamic variables     */
      init_averages(system->nspecies, restart_header->vsn,
		    control.roll_interval, old_roll_interval, &av_convert);
      if(control.rdf_interval > 0)
         init_rdf(system);		/* Prepare to calculate rdf	      */
      read_restart(restart, restart_header->vsn, system, av_convert);  
                                        /* Saved dynamic vars etc             */
      convert_averages(control.roll_interval, old_roll_interval, av_convert);
      control.reset_averages = 0;                /* This flag never propagated.*/

      if(control.step != old_step)
         interpolate_derivatives(system, old_step, control.step);
      for(i = 0; i < 9; i++)		/* Zap cell velocities if constrained */
	 if( (control.strain_mask >> i) & 1 )
	    system->hddot[0][i] = system->hddoto[0][i] = system->hdot[0][i] = 0;

      (void)fclose(restart);
      message(NULLI, NULLP, INFO, RESUCC, control.restart_file);
   }               
   (void)fclose(contr_file);

   if( out_name[0] != '\0' )	/* Open output file (or use stdout)   */
   {
      (void)fflush(stdout);		/* Purge buffer before opening file   */
      if( freopen(out_name, "a", stdout) == NULL )
         message(NULLI, NULLP, FATAL, OOFAIL, out_name, strerror(errno));
   }
   if( ithread == 0 )
      banner_page(system, *species, restart_header);
   if( qpf != NULL )
      xfree(qpf);
}
$EOD
$!
$CREATE values.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 *  values.  	Calculate various thermodynamic quantities and their averages *
 *  		ant print periodic output.	Contents:		      *
 * init_averages()	Allocate and set up averages database		      *
 * av_ptr()		Return pointer and size of database		      *
 * add_average()	Update an average with a new datum		      *
 * value()		Return current value of specified quantity	      *
 * roll_av()		Return rolling averagee of specified quantity	      *
 * roll_sd()		Return standard deviation of rolling average	      *
 * values()		Calculate all values and update database	      *
 * print_frame()	Print periodic output in formatted form		      *
 * output()		Call print_frame for values, rolling averages & sd's  *
 * averages()		Calculate and print averages()			      *
 ******************************************************************************
 *      Revision Log
 *       $Log: values.c,v $
 *       Revision 2.7  1994/06/08 13:17:00  keith
 *       Made database size a size_mt (unsigned long) for 16 bit machines.
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Added CONST qualifier to (re-)declarations of ANSI library
 * emulation routines to give reliable compilation even
 * without ANSI_LIBS macro. (#define's away for K&R
 * compilers)
 *
 * Revision 2.5  94/01/18  13:33:05  keith
 * Null update for XDR portability release
 * 
 * Revision 2.3  93/10/28  10:28:15  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.1  93/07/19  13:28:25  keith
 * Added XDR capability for backup and dump files.
 * 
 * Revision 2.0  93/03/15  14:49:24  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.15  93/03/09  15:59:19  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.14  92/09/22  14:47:36  keith
 * Tidied up calls to improve "lint" rating.
 * 
 * Revision 1.13  92/06/26  17:03:38  keith
 * Got rid of assumption that memory returned by talloc() or
 * arralloc() is zeroed.  This enhances ANSI compatibility.
 * Removed memory zeroing from alloc.c() in consequence.
 * 
 * Revision 1.12  92/03/24  12:41:31  keith
 * Fixed bug introduced in last revision which calculated
 * averages wrongly.
 * Added code to zero averages database if reset-averages
 * is set OR if the info in restart averages database is
 * out of date.  (ie ig begin-averages is within current run).
 * 
 * Revision 1.11  92/03/19  15:45:42  keith
 * Added support for dynamic allocation of rolling average arrays,
 * conversion of existing restart files is done on fly.
 * 
 * Revision 1.10  91/08/19  16:48:51  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_mt and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * 
 * Revision 1.9  91/03/12  15:43:29  keith
 * Tidied up typedefs size_mt and include file 
 * Added explicit function declarations.
 * 
 * Revision 1.8  90/05/16  18:40:53  keith
 * Renamed own freer from cfree to tfree.
 * 
 * Revision 1.7  90/05/16  14:20:40  keith
 * *** empty log message ***
 * 
 * Revision 1.6  90/05/08  17:19:09  keith
 * Fixed bug which got indexing of av_info[] wrong in test for -ve variance.
 * 
 * Revision 1.5  90/04/16  18:17:19  keith
 * Rearranged expression as workaround for CRAY CC4.1 bug.
 * 
 * Revision 1.4  89/06/22  15:45:29  keith
 * Tidied up loops over species to use one pointer as counter.
 * 
 * Revision 1.3  89/06/01  21:25:40  keith
 * Control.out eliminated, use printf and freopen instead to direct output.
 * 
 * Revision 1.2  89/05/19  10:35:28  keith
 * Fixed bug which printed to stdout rather than control.out.
 * 
 * Revision 1.1  89/04/20  16:00:58  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/values.c,v 2.7 1994/06/08 13:17:00 keith stab $";
#endif
/*========================== Program include files ===========================*/
#include	"defs.h"
#include	"structs.h"
#include	"messages.h"
/*========================== Library include files ===========================*/
#include	
#include	"string.h"
#include	"stddef.h"
#include	
/*========================== External function declarations ==================*/
void	mat_vec_mul();
void	q_conj_mul();			/* Quaternion multiply conjugated     */
double	det();				/* Determinant of 3x3 matrix	      */
double	vdot();				/* Vector dot product		      */
double	sum();				/* Vector sum			      */
void	zero_real();
void	zero_double();
void	zero_dbls();
void	energy_dyad();
double	trans_ke();
double	rot_ke();
double	precision();			/* Machine precision constant	      */
char	*atime();
void	new_line();
void	new_lins();
void	new_page();
gptr    *talloc();		       /* Interface to memory allocator       */
void    tfree();		       /* Free allocated memory	      	      */
int	lines_left();		       /* on current page of output	      */
#if defined(ANSI) || defined(__STDC__)
void		note(char *, ...);	/* Write a message to the output file */
void		message(int *, ...);	/* Write a warning or error message   */
#else
void		note();			/* Write a message to the output file */
void		message();		/* Write a warning or error message   */
#endif
/*========================== External data references ========================*/
extern	contr_mt	control;            /* Main simulation control parms. */
/*========================== Structs local to module =========================*/

typedef struct
{   double	value,
   		sum,
   		sum_sq,
		mean,
		sd,
   		roll[1];
} av_mt;

typedef struct
{
   av_n		id;
   char		*name, *unit;
   int		field_width;
   char		*format;
   int		mult;
   av_mt		**p;
} av_info_t;
/*========================== Global variables ================================*/
static
av_info_t av_info[] = { {tke_n, "Trans KE",   CONV_E_N,	11, "%11.5g",-1, NULL},
			{rke_n, "Rot KE",     CONV_E_N,	11, "%11.5g",-1, NULL},
			{pe_n,  "Pot Energy", CONV_E_N,	11, "%11.5g",NPE,NULL},
			{e_n,   "Tot Energy", CONV_E_N,	11, "%11.5g", 1, NULL},
			{tt_n,  "TTemp",      CONV_T_N,	 6, "%6.1f", -1, NULL},
			{rt_n,  "RTemp",      CONV_T_N,	 6, "%6.1f", -1, NULL},
			{t_n,   "Temp",	      CONV_T_N,	 6, "%6.1f", 1,  NULL},
			{h0_n,  "h(1,*)",     LUNIT_N,	 6, "%6.2f", 3,  NULL},
			{h1_n,  "h(2,*)",     LUNIT_N,	 6, "%6.2f", 3,  NULL},
			{h2_n,  "h(3,*)",     LUNIT_N,	 6, "%6.2f", 3,  NULL},
			{stress0_n,"Stress",  CONV_P_N,	10, "%10.3g",3,  NULL},
			{stress1_n,"Stress",  CONV_P_N,	10, "%10.3g",3,  NULL},
			{stress2_n,"Stress",  CONV_P_N,	10, "%10.3g",3,  NULL},
			{press_n,"Pressure",  CONV_P_N,	10, "%10.3g",1,  NULL},
			{vir_n, "Virial",     CONV_E_N,	11, "%11.5g",1,  NULL},
			{msqf_n,"",     CONV_F_N,	10, "%10.5g",-3, NULL},
			{msqt_n,"",     CONV_N_N,	10, "%10.5g",-3, NULL},
			{dip_n, "Dip Mom",    CONV_D_N,	 8, "%8.2g", 3,  NULL}};
static  size_mt	av_size;		/* Size of averages database          */
static  size_mt	av_mt_size;		/* Size of entry inaverages database  */
static  av_head_mt  *av_head;
static	av_mt	*av;			/* Dynamic array of averages structs  */
static	int	navs = 0;		/* Size of array av                   */
static	int	max_row = 0;		/* Largest number of components       */
static  int	max_col = (int)press_n;	/* Number to print across page        */
static  size_mt av_tmp_size;
static  gptr    *av_tmp;
/*========================== Macros ==========================================*/
#define NAVT			(int)end
#define INC(av_mp)    (av_mp = (av_mt*)((char*)av_mp + av_mt_size))
/*============================================================================*/
/******************************************************************************
 *  init_averages  Allocate space for and initialise the averages database.   *
 *  The first word is the 'count' of numbers summed to date, (Pointed at by   *
 *  global av_cnt) which is followed by an array of av_t structs of dimension *
 *  navs. 								      *
 *  Struct array av_info is set up with .av_p pointing to the appropriate     *
 *  entry in av.  The multiplicity (number of components) is also set by the  *
 *  following rule - a positive entry is the true multiplicity and a negative *
 *  one is multiplied by the number of species for the true multiplicity.     *
 ******************************************************************************/
void	init_averages(nspecies, vsn, roll_interval, old_roll_interval,av_convert)
int	nspecies;
char	*vsn;
long	roll_interval, old_roll_interval;
int	*av_convert;
{
   av_mt		*av_mp;
   int		i, imult;
   int		major, minor, cmajor=1, cminor=13;

   for(i = 0; i < (int)end; i++)	/* cycle over enum types av_n         */
   {
      if(av_info[i].mult < 0)		/* Set true multiplicity of each type */
         av_info[i].mult = -nspecies * av_info[i].mult;
      navs += av_info[i].mult;		/* Count total			      */
      if(i < max_col && av_info[i].mult > max_row)
         max_row = av_info[i].mult;
   }

   /* Determine size of database, Allocate space and set pointers             */
   av_mt_size = sizeof(av_mt)+(roll_interval-1)*sizeof(double);
   av_size = sizeof(av_head_mt) + navs*av_mt_size;
   av_head  = (av_head_mt*)balloc(1, av_size);
   av_head->nav = av_head->nroll = av_head->iroll = 0;
   av       = (av_mt*)(av_head+1);
   zero_dbls((double*)av, navs*av_mt_size/sizeof(double));

   av_mp = av;
   for(i = 0; i < (int)end; i++)	/* Set up pointers to area of array   */
   {					/* reserved for each type, size=mult. */
      av_info[i].p = aalloc(av_info[i].mult, av_mt*);
      for(imult = 0; imult < av_info[i].mult; imult++)
      {
	 av_info[i].p[imult] = av_mp;
	 INC(av_mp);
      }
   } 
   /*
    * Do we have to do any conversion on averages read from restart file?
    * We just allocate buffers and set flags here.
    */
   *av_convert = 0;
   if( vsn )
   {
      /*
       * First check whether restart was written by 1.13 or earlier.
       */
      if( sscanf(vsn, "%d.%d", &major, &minor) < 2 )
	 message(NULLI, NULLP, FATAL, INRVSN, vsn);
      if( major < cmajor || (major==cmajor && minor <= cminor ) )
      {
	 av_tmp_size = (navs+1)*sizeof(old_av_u_mt);
	 av_tmp = balloc(1,av_tmp_size);
	 *av_convert = 1;
      }
      /*
       * Has size of rolling average store changed?
       */
      else if (roll_interval != old_roll_interval )
      {
	 av_tmp_size =  sizeof(av_head_mt) 
	             + navs*(sizeof(av_mt)+(old_roll_interval-1)*sizeof(double));
	 av_tmp = balloc(1, av_tmp_size);
	 *av_convert = 2;
      }
   }
}
/******************************************************************************
 * convert_averages.  Update averages database if roll_interval changed or    *
 * if restart file written using old "static" scheme.			      *
 * Also a convenient place to implement reset_averages.			      *
 ******************************************************************************/
void	convert_averages(roll_interval, old_roll_interval, av_convert)
long	roll_interval, old_roll_interval;
int	av_convert;
{
   int iav, old_nroll, old_iroll, rbl;
   size_mt prev_av_mt_size;
   old_av_u_mt *old_av_mp=(old_av_u_mt *)av_tmp;
   av_head_mt	*prev_av_head = (av_head_mt *)av_tmp;
   av_mt	        *av_mp, *prev_av_mp;

   switch(av_convert)
   {
    case 0:					/* No conversion needed       */
      break;
    case 1:					/* Convert from static scheme */
      old_nroll = old_av_mp[0].cnt.roll;
      old_iroll = control.istep % old_roll_interval;
      av_head->nroll = MIN(old_nroll,roll_interval);
      av_head->iroll = av_head->nroll % roll_interval;
      av_head->nav   = old_av_mp[0].cnt.av;
      rbl = MIN(old_iroll, av_head->nroll);
      old_av_mp++;
      av_mp = av_info[0].p[0];
      for(iav = 0; iav < navs; iav++)
      {
	 av_mp->value  = old_av_mp->av.value;
	 av_mp->sum    = old_av_mp->av.sum;
	 av_mp->sum_sq = old_av_mp->av.sum_sq;
	 av_mp->mean   = old_av_mp->av.mean;
	 av_mp->sd     = old_av_mp->av.sd;
	 memcp(av_mp->roll+av_head->nroll-rbl, old_av_mp->av.roll+old_iroll-rbl,
	       rbl*sizeof(double));
	 memcp(av_mp->roll, old_av_mp->av.roll+old_nroll-av_head->nroll+rbl,
	       (av_head->nroll-rbl)*sizeof(double));
	 INC(av_mp);
	 old_av_mp++;
      }
      break;
    case 2:					/* Change roll_interval       */
      prev_av_mt_size = sizeof(av_mt)+(old_roll_interval-1)*sizeof(double);
      old_nroll = prev_av_head->nroll;
      old_iroll = prev_av_head->iroll;
      av_head->nroll = MIN(old_nroll,roll_interval);
      av_head->iroll = av_head->nroll % roll_interval;
      av_head->nav   = prev_av_head->nav;
      rbl = MIN(old_iroll, av_head->nroll);
      prev_av_mp = (av_mt *)(prev_av_head+1);
      av_mp = av_info[0].p[0];
      for(iav = 0; iav < navs; iav++)
      {
	 /*
	  * Can do a struct copy -- will only pick up 1st roll entry
	  */
	 *av_mp = *prev_av_mp;
	 memcp(av_mp->roll+av_head->nroll-rbl, prev_av_mp->roll+old_iroll-rbl,
	       rbl*sizeof(double));
	 memcp(av_mp->roll, prev_av_mp->roll+old_nroll-av_head->nroll+rbl,
	       (av_head->nroll-rbl)*sizeof(double));

	 INC(av_mp);
	 prev_av_mp = (av_mt*)((char*)prev_av_mp + prev_av_mt_size);
      }
      break;
   }
   /*
    *  Reset averages and counters to zero if a) requested
    *  or b) we have not yet reached begin_average. (The latter 
    *  avoids the situation of non-contiguous averages).
    *  Yes I know we just set them up, but this way is clearest.
    */
   if( control.reset_averages || control.istep+1 <= control.begin_average )
   {
      av_mp = av_info[0].p[0];
      for(iav = 0; iav < navs; iav++)
      {
	 av_mp->sum = av_mp->sum_sq = av_mp->mean = av_mp->sd = 0.0;
	 INC(av_mp);
      }
      av_head->nav = 0;
   }
}
/******************************************************************************
 * av_ptr   Return a pointer to averages database and its size (for restart)  *
 ******************************************************************************/
gptr	*av_ptr(size, av_convert)
size_mt	*size;
int	av_convert;
{
   switch(av_convert)
   {
    case 0:
      *size = av_size;
      if(av_head != NULL)
	 return((gptr*)av_head);
      break;
    case 1:
    case 2:
      *size = av_tmp_size;
      if(av_tmp != NULL)
	 return(av_tmp);
   }
   message(NULLI, NULLP, FATAL, AVNOC, "av_ptr");
   return(NULL);					/* To satisfy lint    */
}
/******************************************************************************
 * add_average  update the averages database with new datum                   *
 ******************************************************************************/
static void	add_average(datum, type, offset)
double	datum;				/* Datum to store and accumulate sums */
av_n	type;				/* What kind (ie where to store)      */
int	offset;				/* Sub-type or which component        */
{
   av_mt		*av_mp;
   if(offset < 0 || offset > av_info[(int)type].mult - 1)
      message(NULLI, NULLP, FATAL, AVBNDS, offset, av_info[(int)type].name);

   av_mp = av_info[(int)type].p[offset];
 
   av_mp->value = datum;
   if(control.istep >= control.begin_average)
   {
      av_mp->sum += datum;
      av_mp->sum_sq += datum * datum;
   }
   av_mp->roll[av_head->iroll] = datum;
}
/******************************************************************************
 * values   Calculate the values of the thermodynamic quantities, maintain and*
 * if necessary print them, their averages and rolling averages.              *
 ******************************************************************************/
void	values(system, species, meansq_f_t, pe, dipole, stress_vir)
system_mp	system;		/* record of system info		      */
spec_mt	species[];	/* Records of info for each species	      */
vec_mt		meansq_f_t[][2];/* mean square forces and torques             */
double		pe[];		/* potential energy real/reciprocal space     */
vec_mt		dipole;		/* dipole moment of whole system              */
mat_mt		stress_vir;	/* 'Potential' part of stress, or virial      */
{
   spec_mp	spec;
   int		ispec, ipe;
   double	e, tot_ke = 0.0, tot_pe = 0.0;
   int		i, j, k;
   mat_mt	ke_dyad,
                stress;
   double	vol = det(system->h);

   for(ipe = 0; ipe < NPE; ipe++)
   {
      add_average(CONV_E * pe[ipe], pe_n, ipe);
      tot_pe += pe[ipe];
   }

   for (spec = species; spec < &species[system->nspecies]; spec++)
   {
      ispec = spec-species;
      e = trans_ke(system->h, spec->vel, spec->mass, spec->nmols);
      add_average(CONV_E * e, tke_n, ispec);	/* c of m  kinetic energy     */
      tot_ke += e;

      add_average(e/(1.5*spec->nmols*kB), tt_n, ispec);
						/* c of mass temp.	      */
      if(spec->rdof > 0)			/* Only if polyatomic species */
      {
         e = rot_ke(spec->quat, spec->qdot, spec->inertia, spec->nmols);
         add_average(CONV_E * e, rke_n, ispec);	/* Rotational kinetic energy  */
         tot_ke += e;
         add_average(e/(0.5*kB*spec->rdof*spec->nmols), rt_n, ispec);
      }						/* and temperature            */
   }   						/* Overall temperature        */
   add_average(CONV_E*(tot_ke+tot_pe), e_n, 0);	/* Total energy               */
   add_average(tot_ke/(0.5*kB*system->d_of_f), t_n, 0);
   for(i = 0; i < 3; i++)			/* Non-zero (upper triangle)  */
   {
      add_average(system->h[i][0], h0_n, i);
      add_average(system->h[i][1], h1_n, i);
      add_average(system->h[i][2], h2_n, i);
   }

   zero_real(ke_dyad[0],9);
   for (spec = species; spec < &species[system->nspecies]; spec++)
      energy_dyad(ke_dyad, system->h, spec->vel, spec->mass, spec->nmols);

   k = 0;
   for(i = 0; i < 3; i++)
   {
      for(j = i; j < 3; j++)
      {
         stress[j][i] = stress[i][j] = CONV_P * 0.5/vol * 
          (ke_dyad[i][j] + ke_dyad[j][i] + stress_vir[i][j] + stress_vir[j][i]);
      }
      add_average(stress[i][0], stress0_n, i);
      add_average(stress[i][1], stress1_n, i);
      add_average(stress[i][2], stress2_n, i);
   }
   add_average((stress[0][0] + stress[1][1] + stress[2][2])/3.0, press_n, 0);
   add_average((stress_vir[0][0] + stress_vir[1][1] + stress_vir[2][2])
               *CONV_V/3.0, vir_n, 0);
   
   k = 0;
   for(ispec = 0; ispec < system->nspecies; ispec++)
      for(i = 0; i < 3; i++)
      {
         add_average(CONV_F*CONV_F*meansq_f_t[ispec][0][i], msqf_n, k);
         add_average(CONV_N*CONV_N*meansq_f_t[ispec][1][i], msqt_n, k++);
      }

   for(i = 0; i < 3; i++)
      add_average(CONV_D * dipole[i], dip_n, i);
   /*
    * Update counters.
    */
   if(control.istep >= control.begin_average)
      (av_head->nav)++;
   if(av_head->nroll < control.roll_interval)
      (av_head->nroll)++;
   av_head->iroll = (av_head->iroll+1) % control.roll_interval;
}
/******************************************************************************
 *  value,  roll_av,  roll_sd.   Functions returning the value, rolling       *
 *  average, or s.d for rolling average indicated by pointer p.               *
 ******************************************************************************/
double value(type, comp)
av_n	type;
int	comp;
{
   return(av_info[(int)type].p[comp]->value);
}

double	roll_av(type, comp)
av_n	type;
int	comp;
{
   int	i;
   double	mean = 0.0;

   for(i = 0; i < av_head->nroll; i++)
      mean += av_info[(int)type].p[comp]->roll[i];
   return(mean/ av_head->nroll);
}

static
double	roll_sd(type, comp)
av_n	type;
int	comp;
{
   int	i;
   double	*roll, ssq = 0.0, mean = roll_av(type, comp), var,
		bottom = -32.0*sqrt((double)control.roll_interval)*precision();

   roll = av_info[(int)type].p[comp]->roll;
   for(i = 0; i < av_head->nroll; i++)
      ssq += roll[i] * roll[i];

   var = ssq/ av_head->nroll - mean*mean;
   if( var * av_head->nroll < ssq * bottom)
      message(NULLI, NULLP, WARNING, NEGVAR, "roll_sd", var,
	      av_info[(int)type].name);

   return(var > 0.0 ? sqrt(var): 0.0);
}
/******************************************************************************
 *  Print frame.   Print the values from the structs pointed at by            *
 *  av_info[i].p in a reasonable format.  Function parameter allows various   *
 *  info from struct to be printed in the same format.  av_info contains      *
 *  the field width and format to use for each data type.                     *
 ******************************************************************************/
static
void print_frame(header_sym, header_text, f)
int	header_sym;
char	*header_text;
double	(*f)();
{
   int	row, col, icol;
   static int	out_width = 1;
   static boolean	initial = true;
   if(initial)
   {
      for(icol = 0; icol < max_col; icol++)	/* Count total width	      */
         out_width  += av_info[icol].field_width + 1;
   }
   if( initial || lines_left() < max_row + 1 )	/* If near end of page*/
   {
      new_page();
      for(icol = 0; icol < max_col; icol++)             /* Print column titles*/
         (void)printf(" %*s", av_info[icol].field_width, av_info[icol].name);
      new_line();
      initial = false;
   }
   col = 0;					/* Print row of 'header_sym'  */
   while(col++ < 8)				/* with 'header_text' in the  */
      (void)putchar(header_sym);		/* middle.		      */
   col += printf(" %s ", header_text);
   while(col++ < out_width)
      (void)putchar(header_sym);
   new_line();
   
   for(row = 0; row < max_row; row++)		/* Print 'max_col' fields     */
   {						/* across page, up to max_row */
      for(icol = 0; icol < max_col; icol++)	/* down.  Print value returned*/
      {						/* by (*f) in field or fill   */
         (void)putchar(' ');			/* with spaces                */
         if(row < av_info[icol].mult)
            (void)printf( av_info[icol].format, (*f)((av_n)icol, row));
         else
            for(col = 0; col < av_info[icol].field_width; col++)
               (void)putchar(' ');
      }
      new_line();
   }
}
/******************************************************************************
 *  output    Main output routine to be called periodically.                  *
 *  Calls print_frame which does most of the real work.                       *
 ******************************************************************************/
void	output()
{
   char	s[64];

   (void)sprintf(s, "Timestep %ld      Current values", control.istep);
   print_frame('=', s, value);

   (void)sprintf(s,"Rolling averages over last %d timesteps", av_head->nroll);
   print_frame('-', s, roll_av);
   print_frame('-', "Standard deviations", roll_sd);
   (void)fflush(stdout);
}
/******************************************************************************
 *  averages   calculate and print averages, reset counter.		      *
 ******************************************************************************/
void	averages()
{
   int	i, iav, col;
   double	variance,
		bottom = -32.0*sqrt((double)av_head->nav)*precision();
   av_mt	*av_mp;

   if(av_head == NULL)
      message(NULLI, NULLP, FATAL, AVNOC, "averages");

   if(av_head->nav == 0)
   {
      note("no sums accumulated for averages");
      return;
   }   
     
   if(lines_left() < NAVT + 4 )	/* If near end of page*/
      new_page();
   else
      new_lins(2);
   (void)printf( "   Averages over last %d timesteps",av_head->nav);
   new_line();

   av_mp = av;
   for(iav = 0; iav < NAVT; iav++)
   {
      for(i = 0; i < av_info[iav].mult; i++)
      {
	 av_mp = av_info[iav].p[i];
	 av_mp->mean = av_mp->sum / av_head->nav;
	 variance = av_mp->sum_sq/ av_head->nav - av_mp->mean * av_mp->mean;
	 if(variance * av_head->nav  < av_mp->sum_sq * bottom)
	    message(NULLI, NULLP, WARNING, NEGVAR, "averages", variance,
		    av_info[(int)iav].name);
	 av_mp->sd   = variance > 0.0 ? sqrt(variance) : 0.0;
	 av_mp->sum = 0.0;  av_mp->sum_sq = 0.0;
      }
   }
   av_head->nav = 0;

   for(iav = 0; iav < NAVT; iav++)
   {
      col = 0;
      col += printf( "   %-16s= ",av_info[iav].name);
      for(i = 0; i < av_info[iav].mult; i++)
      {
         if(col + 2 * av_info[iav].field_width + 6 > control.page_width)
         {
            new_line();
            col = printf("                     ");
         }
         col += printf( av_info[iav].format, av_info[iav].p[i]->mean);
         col += printf(" +/- ");
         col += printf(av_info[iav].format, av_info[iav].p[i]->sd);
         if(i < av_info[iav].mult - 1)
            col += printf(",");
      }
      if(col + 10 > control.page_width) new_line();
      col += printf("  %s", av_info[iav].unit);
      new_line();
   }
   (void)fflush(stdout);
}
$EOD
$!
$CREATE xdr.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */

/******************************************************************************
 * xdr  Moldy-specific xdr routines for storing binary data in machine-       *
 *      independent format.	For compatibility with existing binary 	      *
 *      formats, strings are stored as fixed-length opaque data.	      *
 ******************************************************************************
 *      Revision Log
 *       $Log: xdr.c,v $
 *       Revision 2.11  1996/10/23 13:06:51  keith
 *       Fixed restart structure correctly - broken in prev version.
 *       Thermostat parameters may not be properly read.
 *
 *       Revision 2.10  1996/03/06 18:20:45  keith
 *       Added cast in xdr_vector() call to supress spurious warning message.
 *
 *       Revision 2.9  1994/10/17 10:54:06  keith
 *       Got rid of dummy xdr_array which really screwed things up!
 *
 * Revision 2.8  1994/07/07  17:03:39  keith
 * Fixed up missing xdr_vector to be compiled in only if NEED_XDR_VECTOR defined.
 *
 * Revision 2.7  1994/06/08  13:17:38  keith
 * Changed all timestep-related parameters to type "long". This means
 * that 16-bit DOS compilers can do more than 32767 timesteps.
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Added CONST qualifier to (re-)declarations of ANSI library
 * emulation routines to give reliable compilation even
 * without ANSI_LIBS macro. (#define's away for K&R
 * compilers)
 *
 * Revision 2.5  94/01/18  13:33:07  keith
 * Null update for XDR portability release
 * 
 * Revision 2.4  94/01/18  13:15:18  keith
 * Put casts in function calls to satisfy picky-picky-picky SGI compiler.
 * Added return values to dummy xdr functions to get rid of VMS warnings.
 * 
 * Revision 2.4  93/12/20  16:42:04  keith
 * Put casts in function calls to satisfy picky-picky-picky SGI compiler.
 * 
 * Revision 2.3  93/10/28  10:28:17  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.2  93/09/06  14:42:46  keith
 * Fixed portability problems/bugs in XDR code.
 * 
 * Revision 2.1  93/07/19  13:29:08  keith
 * Support for XDR backup/dump routines.
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/xdr.c,v 2.11 1996/10/23 13:06:51 keith Exp $";
#endif
/*========================== program include files ===========================*/
#include	"structs.h"
/*========================== Library include files ===========================*/
#include	"stddef.h"
#include 	"xdr.h"
/*============================================================================*/

#ifdef USE_XDR

bool_t xdr_real(xdrs, rp)
XDR     *xdrs;
real    *rp;
{
   if( sizeof(real) == sizeof(double) )
      return xdr_double(xdrs, (double*)rp);
   else if( sizeof(real) == sizeof(float) )
      return xdr_float(xdrs, (float*)rp);
   else
      return FALSE;
}

bool_t xdr_contr(xdrs, cp)
XDR      *xdrs;
contr_mt *cp;
{
   return
      xdr_opaque(xdrs, cp->title, L_name) &&
      xdr_long(xdrs, &cp->istep) &&
      xdr_long(xdrs, &cp->nsteps) &&
      xdr_double(xdrs, &cp->step) &&
      xdr_vector(xdrs, (gptr*)&cp->print_sysdef, 7, sizeof(boolean), (xdrproc_t)xdr_bool) &&
      xdr_opaque(xdrs, cp->sysdef, 6*L_name) &&
      xdr_vector(xdrs, (gptr*)cp->spare, 23, sizeof(int), (xdrproc_t)xdr_int) &&
      xdr_double(xdrs, &cp->ttmass) &&
      xdr_double(xdrs, &cp->rtmass) &&
      xdr_int(xdrs, &cp->pad) &&
      xdr_int(xdrs, &cp->const_temp) &&
      xdr_bool(xdrs, &cp->xdr_write) &&
      xdr_bool(xdrs, &cp->strict_cutoff) &&
      xdr_int(xdrs, &cp->strain_mask) &&
      xdr_int(xdrs, &cp->nbins) &&
      xdr_u_long(xdrs, &cp->seed) &&
      xdr_vector(xdrs, (gptr*)&cp->page_width, 2, sizeof(int), (xdrproc_t)xdr_int) &&
      xdr_vector(xdrs, (gptr*)&cp->scale_interval, 7, sizeof(long), (xdrproc_t)xdr_long) &&
      xdr_vector(xdrs, (gptr*)&cp->dump_level, 2, sizeof(int), (xdrproc_t)xdr_int) &&
      xdr_vector(xdrs, (gptr*)&cp->backup_interval, 6, sizeof(long), (xdrproc_t)xdr_long) &&
      xdr_vector(xdrs, (gptr*)&cp->temp, 10, sizeof(double), (xdrproc_t)xdr_double);
}

bool_t xdr_system(xdrs, sp)
XDR      *xdrs;
system_mt *sp;
{
   return
      xdr_vector(xdrs, (gptr*)&sp->nsites, 8, sizeof(int), (xdrproc_t)xdr_int) &&
      /*
       * This is an awful hack.  There are 28 real[3]* pointers
       * next.  Their stored values are NEVER re-used so we just
       * output a placeholder.  For compatibility of XDR/non-XDR
       * files on 4 byte big-endian ieee architectures we emit
       * 4 bytes each.  DON'T use sizeof as that would make XDR
       * file M/C dependent.
       */
      xdr_opaque(xdrs, (gptr*)&sp->c_of_m, 28*XDR_4PTR_SIZE);
}

/*
 * This version for reading restart files written by 2.10 or before.
 */
bool_t xdr_system_2(xdrs, sp)
XDR      *xdrs;
system_mt *sp;
{
   return
      xdr_vector(xdrs, (gptr*)&sp->nsites, 8, sizeof(int), (xdrproc_t)xdr_int) &&
      xdr_opaque(xdrs, (gptr*)&sp->c_of_m, 18*XDR_4PTR_SIZE);
}

bool_t xdr_species(xdrs, sp)
XDR      *xdrs;
spec_mt *sp;
{
   return
      xdr_vector(xdrs, (gptr*)sp->inertia, 6, sizeof(real), (xdrproc_t)xdr_real) &&
      xdr_vector(xdrs, (gptr*)&sp->nsites, 4, sizeof(int), (xdrproc_t)xdr_int) &&
      xdr_opaque(xdrs, sp->name, 32) &&
      /*
       * This is an awful hack.  There are 14 real[3]* pointers
       * next.  Their stored values are NEVER re-used so we just
       * output a placeholder.  For compatibility of XDR/non-XDR
       * files on 4 byte big-endian ieee architectures we emit
       * 4 bytes each.  DON'T use sizeof as that would make XDR
       * file M/C dependent.
       */
      xdr_opaque(xdrs, (gptr*)&sp->site_id, 14*XDR_4PTR_SIZE) &&
      xdr_int(xdrs,(int*)&sp->pad[0]) &&
      xdr_int(xdrs,(int*)&sp->pad[1]);
}

bool_t xdr_site(xdrs, sp)
XDR      *xdrs;
site_mt  *sp;
{
   return
      xdr_double(xdrs, &sp->mass) &&
      xdr_double(xdrs, &sp->charge) &&
      xdr_opaque(xdrs, sp->name, 8) &&
      xdr_int(xdrs, &sp->flag) &&
      xdr_int(xdrs, &sp->pad);
}

static unsigned int xdr_npotpar;

void xdr_set_npotpar(npotpar)
int	npotpar;
{
   xdr_npotpar = npotpar;
}

bool_t xdr_pot(xdrs, sp)
XDR      *xdrs;
pot_mt   *sp;
{
   return
      xdr_int(xdrs, &sp->flag) &&
      xdr_int(xdrs, &sp->pad) &&
      xdr_vector(xdrs, (gptr*)sp->p, xdr_npotpar, sizeof(real), (xdrproc_t)xdr_real);
}


bool_t xdr_restrt(xdrs, sp)
XDR         *xdrs;
restrt_mt   *sp;
{
   return
      xdr_u_long(xdrs, &sp->timestamp) &&
      xdr_u_long(xdrs, &sp->prev_timestamp) &&
      xdr_opaque(xdrs, sp->init_date, DLEN+L_name+16) &&
      xdr_int(xdrs, &sp->seq);
}

bool_t xdr_dump(xdrs, sp)
XDR         *xdrs;
dump_mt   *sp;
{
   return
      xdr_opaque(xdrs, sp->title, L_name+16) &&
      xdr_vector(xdrs, (gptr*)&sp->istep, 2, sizeof(long), (xdrproc_t)xdr_long) &&
      xdr_vector(xdrs, (gptr*)&sp->dump_level, 4, sizeof(int), (xdrproc_t)xdr_int) &&
      xdr_vector(xdrs, (gptr*)&sp->timestamp, 3, sizeof(unsigned long), (xdrproc_t)xdr_u_long);
}

static
bool_t xdr_av_head_t(xdrs,ap)
XDR	  *xdrs;
av_head_mt *ap;
{
   return
      xdr_vector(xdrs, (gptr*)&ap->nav, 4, sizeof(int), (xdrproc_t)xdr_int) &&
      xdr_double(xdrs, &ap->align);
}

static
bool_t xdr_old_av_u_t(xdrs,ap)
XDR	  *xdrs;
old_av_u_mt *ap;
{
   return
      xdr_vector(xdrs, (gptr*)&ap->cnt.av, 2, sizeof(int), (xdrproc_t)xdr_int) &&
      xdr_opaque(xdrs, (gptr*)(&ap->cnt.av+2*sizeof(int)), 
		 (7+MAX_ROLL_INTERVAL)*XDR_DOUBLE_SIZE-2*XDR_INT_SIZE);
}

static size_mt  xdr_av_size;
static int    av_convert;

void   xdr_set_av_size_conv(size, av_conv)
size_mt	   size;
int	   av_conv;
{
   xdr_av_size = size;
   av_convert = av_conv;
}

bool_t xdr_averages(xdrs, ap)
XDR	   *xdrs;
gptr	   *ap;
{
   /*
    * The global flag av_convert requires explanation.  It is
    * set by init_averages() which is always called before
    * this function.  av_convert=1 if we are reading data in
    * the old (fixed length = MAX_ROLL_INTERVAL) format and
    * 0 or 2 otherwise. 
    */
   unsigned    navst;		/* Total # of average data items */
   old_av_u_mt *apo = (old_av_u_mt *)ap;
   av_head_mt  *aph = (av_head_mt  *)ap;

   if( xdrs->x_op == XDR_DECODE && av_convert == 1 )
   {
      navst = (xdr_av_size-sizeof(old_av_u_mt)) / sizeof(double);
      return
	 xdr_old_av_u_t(xdrs,apo) &&
	 xdr_vector(xdrs, (gptr*)(apo+1), navst, sizeof(double), (xdrproc_t)xdr_double);
      }
   else
   {
      navst = (xdr_av_size-sizeof(av_head_mt)) / sizeof(double);
      return
	 xdr_av_head_t(xdrs,aph) &&
	 xdr_vector(xdrs, (gptr*)(aph+1), navst, sizeof(double), (xdrproc_t)xdr_double);
   }
}

#ifdef NEED_XDR_VECTOR
#define LASTUNSIGNED	((u_int)0-1)
/*
 * xdr_vector():
 *
 * XDR a fixed length array. Unlike variable-length arrays,
 * the storage of fixed length arrays is static and unfreeable.
 * > basep: base of the array
 * > size: size of the array
 * > elemsize: size of each element
 * > xdr_elem: routine to XDR each element
 */
bool_t
xdr_vector(xdrs, basep, nelem, elemsize, xdr_elem)
	register XDR *xdrs;
	register char *basep;
	register u_int nelem;
	register u_int elemsize;
	register xdrproc_t xdr_elem;	
{
	register u_int i;
	register char *elptr;

	elptr = basep;
	for (i = 0; i < nelem; i++) {
		if (! (*xdr_elem)(xdrs, elptr, LASTUNSIGNED)) {
			return(FALSE);
		}
		elptr += elemsize;
	}
	return(TRUE);	
}
#endif
#else
void	xdr_set_npotpar (npotpar) int npotpar; {}
void	xdr_set_av_size_conv (size, av_conv) size_mt size; int av_conv; {}
bool_t	xdr_site () {return 0;}
bool_t	xdr_restrt () {return 0;}
bool_t	xdr_averages () {return 0;}
bool_t	xdr_real () {return 0;}
bool_t	xdr_contr () {return 0;}
bool_t	xdr_system () {return 0;}
bool_t	xdr_system_2 () {return 0;}
bool_t	xdr_species () {return 0;}
bool_t	xdr_pot () {return 0;}
bool_t	xdr_int () {return 0;}
bool_t	xdr_bool () {return 0;}
#endif
$EOD
$!
$CREATE parallel.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * Parallel - support and interface routines to parallel MP libraries.	      *
 ******************************************************************************
 *       $Log: parallel.c,v $
 *       Revision 2.20  1997/08/26 16:40:59  keith
 *       Updated with interface to new BSPlib standard and tested with Oxford
 *       BSPlib.
 *       Entries for old BSP remain - define preprocessor symbol BSP0 to use.
 *
 *       Revision 2.19  1996/10/15 13:50:45  keith
 *       Corrections to Cray SHMEM library interface.
 *
 *       Revision 2.18  1996/09/03 15:04:51  keith
 *       Added optional code for MPI global sums to guarantee identical
 *       results on all processors. Compile with -DUNSYMM to activate.
 *
 *       Revision 2.17  1996/03/19 12:33:50  keith
 *       Optimised T3D par_collect_all.  Now does not call barrier()
 *       within loop, but uses pt-to-pt synchronization.
 *       (b) Works "in-place" in memory rather than in static buffers.
 *       This may well not be robust because of sbreak() call which
 *       may break malloc().
 *
 *       Revision 2.14  1996/01/17 17:08:42  keith
 *       Added "par_isum()" for rdf calculation.
 *       Added security "exit()" call to par_abort().
 *       Corrected bug where par_dsum called vradd.
 *       Added "init_rdf" call for all threads.
 *
 *       Revision 2.13  1995/12/22 11:42:04  keith
 *       Modified buffer handling for BSP interface.  It used to complain and
 *       stop if buffer was too small. Now it divides data into chunks smaller
 *       than the buffer and transfers them one at a time.
 *
 *       Revision 2.12  1995/12/06 10:44:50  keith
 *       Nose-Hoover and Gaussian (Hoover constrained) thermostats added.
 *
 *       Revision 2.11  1994/11/24 14:48:17  keith
 *       Fixed problem with arg lists for TCGMSG
 *
 * Revision 2.10  1994/10/17  10:49:41  keith
 * Changed arg list of bspstart to match changed library version.
 *
 * Revision 2.9  1994/07/11  11:15:30  keith
 * Tidied up startup routine with par_broadcast() function.
 * Documented parallel routine interface calls for porting.
 *
 * Revision 2.8  1994/07/07  17:00:26  keith
 * Interface to BSP, TCGMSG and MPI message-passing libraries.
 *
 * Revision 1.1  1994/07/07  16:59:44  keith
 * Initial revision
 *
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/parallel.c,v 2.20 1997/08/26 16:40:59 keith Exp $";
#endif
/*========================== program include files ===========================*/
#include	"defs.h"
#include	"structs.h"
#include	"messages.h"
/*========================== system  include files ===========================*/
#include	
#include	"string.h"
#ifdef BSP
#include	
#include	
#endif
#ifdef DEBUG
#include        
#endif
#ifdef TCGMSG
#include	
#endif
#ifdef MPI
#include	
#endif
#ifdef SHMEM
#include	
#include	
#include        
#endif
/*========================== External function declarations ==================*/
gptr            *talloc();	       /* Interface to memory allocator       */
gptr		*av_ptr();
gptr            *rdf_ptr();
void		init_averages();
void		allocate_dynamics();
extern int 	ithread, nthreads;
/*====================== Utilities for interface functions ===================*/
#ifdef TCGMSG
static long	lval;
#define		ADDR(expr) (lval=(expr),&lval)
#endif
#ifdef BSP0
/*
 * BSP version 0 doesn't handle auto & heap vars.  This interface
 * copies to static storage.  A 1MB buffer is large enough to
 * handle up to 44000 atomic sites in one go, and data for larger 
 * systems is parcelled up appropriately and sent in chunks.
 */
#define NBUFMXW 131072
#define NBUFMAX (NBUFMXW*sizeof(double))
static double tmpbuf[NBUFMXW];
#endif
#ifdef SHMEM
/*
 * SHMEM  doesn't handle auto & heap vars either. Handle as for BSP.
 * Make sure buffer is a good power-of 2 words and cache aligned.
 */
#pragma _CRI cache_align tmpbuf, pWrk, pSync, pSyncb
#define NBUFMXW 131072
#define NBUFMAX (NBUFMXW*sizeof(double))
static double tmpbuf[NBUFMXW];
/* 
 * SHMEM synchronization and work arrays. pWrk must be bigger than
 * _SHMEM_REDUCE_MIN_WRKDATA_SIZE.  Since we have no compile-time MAX
 * function we declare pSync inelegantly as the sum of the sizes.
 */
static double pWrk[2][NBUFMXW/2+1];
static long pSync[2][_SHMEM_REDUCE_SYNC_SIZE];
static long pSyncb[_SHMEM_BCAST_SYNC_SIZE];
static int  psi = 0;
#endif
#ifdef MPI
#define M_REAL (sizeof(real)==sizeof(double)?MPI_DOUBLE:MPI_FLOAT)
#endif
/*====================== Parallel lib interface functions ====================*
 *  The following set of functions define the interface between moldy and     *
 *  a message-passing parallel library.  To port to a new library it suffices *
 *  to implement these in terms of the library primitives.  Thus all of the   *
 *  implementation-dependent parallel code is confined to this file.          *
 *									      *
 *  The functions required are:						      *
 *  par_sigintreset()	:  Moldy sets a handler for SIGINT.  This fn is called*
 *			   from the signal handler to restore the default.    *
 *  par_begin(int *argc, char ***argv, int *ithread, int *nthreads) 	      *
 *                      :  Initialize the library and return the number of    *
 *			   processes and the ID of this process.	      *
 *  par_finish()	:  Terminate the parallel run normally.		      *
 *  par_abort(int code) :  Terminate the run abnormally.  Return code if poss.*
 *  par_broadcast(void *buf, int n, size_mt size, int ifrom)		      *
 *			:  Broadcast the specified buffer from ifrom to all.  *
 *  par_{r,d}sum(void *buf, int n) :  Perform a global parallel sum reduction *
 *			   on the buffer containing n {reals,doubles}.	      *
 *  par_imax(int *idat) :  Perform a global "maximum" reduction on the single *
 *       		   int argument.				      *
 *									      *
 *  Note that there is no provision for heterogeneous execution by way of     *
 *  type identification.  Though some MP libraries (eg MPI) do provide the    *
 *  hooks it is too hard to implement for the control and other structs.      *
 *  It's also hard to see why this might ever be useful for a MD run.	      *
 *============================================================================*/
/******************************************************************************
 * par_sigintreset().  Reset signal handler to parallel lib default upon trap *
 *		       of SIGINT.					      *
 ******************************************************************************/
#ifdef TCGMSG
extern	void    SigintHandler();

void
par_sigintreset()
{
   signal(SIGINT, SigintHandler);
}
#endif
#ifdef BSP0
void
par_sigintreset()
{
   signal(SIGINT, SIG_DFL);
}
#endif
#ifdef BSP
void
par_sigintreset()
{
   signal(SIGINT, SIG_DFL);
}
#endif
#ifdef SHMEM
void
par_sigintreset()
{
   signal(SIGINT, SIG_DFL);
}
#endif
#ifdef MPI
void
par_sigintreset()
{
   signal(SIGINT, SIG_DFL);
}
#endif
/******************************************************************************
 * par_imax().  Calculate global maximum over all processors.		      *
 ******************************************************************************/
#ifdef TCGMSG
void
par_imax(idat)
int *idat;
{
       IGOP_(ADDR(10+MSGINT), idat, ADDR(1), "max");
}
#endif
#ifdef BSP
void
par_imax(idat)
int *idat;
{
   *idat = bsp_maxI(*idat);
}
#endif
#ifdef BSP0
static void imax(i1, i2, i3, size)
int *i1, *i2, *i3;
int	size;
{
  *i1 = MAX(*i2,*i3);
}

void
par_imax(idat)
int *idat;
{
   bspreduce(imax, idat, idat, sizeof(int));
}
#endif
#ifdef SHMEM
void
par_imax(idat)
int *idat;
{
   shmem_int_max_to_all(idat, idat, 1, 0, 0, nthreads, (int*)pWrk[psi], pSync[psi]);
   psi = ! psi;
#ifdef SHMEM_BARRIER
   barrier();
#endif
}
#endif
#ifdef MPI
void
par_imax(idat)
int *idat;
{
   int result;
   MPI_Allreduce(idat, &result, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
   *idat = result;
}
#endif
/******************************************************************************
 * par_isum().  Calculate sum of int array over all processors.               *
 ******************************************************************************/
#ifdef TCGMSG
void
par_isum(buf, n)
int *buf;
int  n;
{
   IGOP_(ADDR(MSGINT), buf, &n, "+");
}
#endif
#ifdef BSP
static void viadd(res, x, y, nb)
int res[], x[], y[];
int *nb;
{
   int i, n=*nb/sizeof(int);
   for(i = 0; i < n; i++)
      res[i] = x[i] + y[i];
}

void
par_isum(buf, n)
int *buf;
int  n;
{
   bsp_fold(viadd, buf, buf, n*sizeof(int));
}
#endif
#ifdef BSP0
static void viadd(res, x, y, nb)
int res[], x[], y[];
int nb;
{
   int i, n=nb/sizeof(int);
   for(i = 0; i < n; i++)
      res[i] = x[i] + y[i];
}

void
par_isum(buf, n)
int *buf;
int  n;
{
   int m;
   
   /*
    * BSP only allows operations on statically allocated buffers. *sigh*
    * Use loop to perform general operation copying in and out of a
    * fixed-size, static buffer.
    */
   while( n > 0 )
   {
      m = MIN(n, NBUFMAX/sizeof(int));
      memcp(tmpbuf, buf, m*sizeof(int));
      bspreduce(viadd, tmpbuf, tmpbuf, m*sizeof(int));
      memcp(buf, tmpbuf, m*sizeof(int));
      buf += m;
      n -= m;
   }
}
#endif
#ifdef SHMEM
void
par_isum(buf, n)
int *buf;
int  n;
{
   int m;
   
   /*
    * SHMEM only allows operations on statically allocated buffers. *sigh*
    * Use loop to perform general operation copying in and out of a
    * fixed-size, static buffer.
    */
   while( n > 0 )
   {
      m = MIN(n, NBUFMAX/sizeof(int));
      memcp(tmpbuf, buf, m*sizeof(int));
      shmem_int_sum_to_all((int*)tmpbuf, (int*)tmpbuf, m, 0, 0, nthreads, 
			   (int*)pWrk[psi], pSync[psi]);
      memcp(buf, tmpbuf, m*sizeof(int));
      psi = ! psi;
      buf += m;
      n -= m;
#ifdef SHMEM_BARRIER
      barrier();
#endif
   }
}
#endif
#ifdef MPI
/*
 * MPI demands seperate send and receive buffers.  Malloc one and keep
 * it around.  Extend if necessary.
 */
void
par_isum(buf, n)
int *buf;
int  n;
{
   static int *tmpbuf = 0;
   static int  tmpsize = 0;

   if( n <= 0 )
      return;
   if(n > tmpsize)
   {
      if( tmpbuf )
	 free(tmpbuf);
      tmpbuf = aalloc(n, int);
      tmpsize = n;
   }
   MPI_Allreduce(buf, tmpbuf, n, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
   memcp(buf, tmpbuf, n*sizeof(int));
}
#endif
/******************************************************************************
 * par_rsum()/dsum.  Calculate sum of "reals"/doubles  over all processors.   *
 ******************************************************************************/
#ifdef TCGMSG
void
par_rsum(buf, n)
real *buf;
int  n;
{
   DGOP_(ADDR(MSGDBL), buf, &n, "+");
}
void
par_dsum(buf, n)
real *buf;
int  n;
{
   DGOP_(ADDR(MSGDBL), buf, &n, "+");
}
#endif
#ifdef BSP
static void vradd(res, x, y, nb)
real res[], x[], y[];
int *nb;
{
   int i, n=*nb/sizeof(real);
   for(i = 0; i < n; i++)
      res[i] = x[i] + y[i];
}
static void vdadd(res, x, y, nb)
double res[], x[], y[];
int *nb;
{
   int i, n=*nb/sizeof(double);
   for(i = 0; i < n; i++)
      res[i] = x[i] + y[i];
}

void
par_rsum(buf, n)
real *buf;
int  n;
{
   bsp_fold(vradd, buf, buf, n*sizeof(real));
}
void
par_dsum(buf, n)
double *buf;
int  n;
{
   bsp_fold(vradd, buf, buf, n*sizeof(double));
}
#endif
#ifdef BSP0
static void vradd(res, x, y, nb)
real res[], x[], y[];
int nb;
{
   int i, n=nb/sizeof(real);
   for(i = 0; i < n; i++)
      res[i] = x[i] + y[i];
}
static void vdadd(res, x, y, nb)
double res[], x[], y[];
int nb;
{
   int i, n=nb/sizeof(double);
   for(i = 0; i < n; i++)
      res[i] = x[i] + y[i];
}

void
par_rsum(buf, n)
real *buf;
int  n;
{
   int m;
   
   /*
    * BSP only allows operations on statically allocated buffers. *sigh*
    * Use loop to perform general operation copying in and out of a
    * fixed-size, static buffer.
    */
   while( n > 0 )
   {
      m = MIN(n, NBUFMAX/sizeof(real));
      memcp(tmpbuf, buf, m*sizeof(real));
      bspreduce(vradd, tmpbuf, tmpbuf, m*sizeof(real));
      memcp(buf, tmpbuf, m*sizeof(real));
      buf += m;
      n -= m;
   }
}
void
par_dsum(buf, n)
double *buf;
int  n;
{
   int m;
   
   /*
    * BSP only allows operations on statically allocated buffers. *sigh*
    * Use loop to perform general operation copying in and out of a
    * fixed-size, static buffer.
    */
   while( n > 0 )
   {
      m = MIN(n, NBUFMAX/sizeof(double));
      memcp(tmpbuf, buf, m*sizeof(double));
      bspreduce(vdadd, tmpbuf, tmpbuf, m*sizeof(double));
      memcp(buf, tmpbuf, m*sizeof(double));
      buf += m;
      n -= m;
   }
}
#endif
#ifdef SHMEM
void
par_rsum(buf, n)
real *buf;
int  n;
{
   int m;
   
   if( sizeof(real) == sizeof(float))
   {
      /*
       * Use loop to perform general operation copying in and out of a
       * fixed-size, static buffer.
       */
      while( n > 0 )
      {
         m = MIN(n, NBUFMAX/sizeof(real));
         memcp(tmpbuf, buf, m*sizeof(real));
         shmem_float_sum_to_all((float*)tmpbuf, (float*)tmpbuf, m,0,0, 
				nthreads, (float*)pWrk[psi], pSync[psi]);
         memcp(buf, tmpbuf, m*sizeof(real));
	 psi = ! psi;
         buf += m;
         n -= m;
#ifdef SHMEM_BARRIER
	 barrier();
#endif
      }
    }
    else if ( sizeof(real) == sizeof(double))
    {
      while( n > 0 )
      {
         m = MIN(n, NBUFMAX/sizeof(real));
         memcp(tmpbuf, buf, m*sizeof(real));
         shmem_double_sum_to_all((double*)tmpbuf, (double*)tmpbuf, m,0,0, 
				 nthreads, (double*)pWrk[psi], pSync[psi]);
         memcp(buf, tmpbuf, m*sizeof(real));
	 psi = ! psi;
         buf += m;
         n -= m;
#ifdef SHMEM_BARRIER
	 barrier();
#endif
      }
    }      
}
void
par_dsum(buf, n)
double *buf;
int  n;
{
   int m;
   
   /*
    * Use loop to perform general operation copying in and out of a
    * fixed-size, static buffer.
    */
   while( n > 0 )
   {
      m = MIN(n, NBUFMAX/sizeof(double));
      memcp(tmpbuf, buf, m*sizeof(double));
      shmem_double_sum_to_all((double*)tmpbuf, (double*)tmpbuf, m, 0, 0, 
			      nthreads, (double*)pWrk[psi], pSync[psi]);
      memcp(buf, tmpbuf, m*sizeof(double));
      psi = ! psi;
      buf += m;
      n -= m;
#ifdef SHMEM_BARRIER
      barrier();
#endif
   }
}
#endif
#ifdef MPI
/*
 * MPI demands seperate send and receive buffers.  Malloc one and keep
 * it around.  Extend if necessary.
 *
 * The MPI standard recommends but does not guarantee that the results 
 * of an Allreduce operation are identical on all threads.  For
 * efficiency we assume that it is here, but provide conditional code
 * in case it isn't. The test on accel.c will trap if results differ,
 * in which case recompile with -DUNSYMM.
 */
void
par_rsum(buf, n)
real *buf;
int  n;
{
   static real *tmpbuf = 0;
   static int  tmpsize = 0;
   if(n > tmpsize)
   {
      if( tmpbuf )
	 free(tmpbuf);
      tmpbuf = dalloc(n);
      tmpsize = n;
   }
#ifdef UNSYMM
   MPI_Reduce(buf, tmpbuf, n, M_REAL, MPI_SUM, 0, MPI_COMM_WORLD);
   MPI_Bcast(tmpbuf, n, M_REAL, 0, MPI_COMM_WORLD);
#else
   MPI_Allreduce(buf, tmpbuf, n, M_REAL, MPI_SUM, MPI_COMM_WORLD);
#endif
   memcp(buf, tmpbuf, n*sizeof(real));
}
void
par_dsum(buf, n)
double *buf;
int  n;
{
   static double *tmpbuf = 0;
   static int  tmpsize = 0;
   if(n > tmpsize)
   {
      if( tmpbuf )
	 free(tmpbuf);
      tmpbuf = aalloc(n, double);
      tmpsize = n;
   }
#ifdef UNSYMM
   MPI_Reduce(buf, tmpbuf, n, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
   MPI_Bcast(tmpbuf, n, MPI_DOUBLE, 0, MPI_COMM_WORLD);
#else
   MPI_Allreduce(buf, tmpbuf, n, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
#endif
   memcp(buf, tmpbuf, n*sizeof(double));
}
#endif
/******************************************************************************
 * par_broadcast(). Broadcast data to all processors.			      *
 ******************************************************************************/
#ifdef TCGMSG
void
par_broadcast(buf, n, size, ifrom)
gptr	*buf;
int	n;
size_mt	size;
int	ifrom;
{
   long type = 0;
   long	lenbuf = n*size;
   long ifrm = ifrom;
   BRDCST_(&type, buf, &lenbuf, &ifrm);
}
#endif
#ifdef BSP
void
par_broadcast(buf, n, size, ifrom)
gptr	*buf;
int	n;
size_mt	size;
int	ifrom;
{
   bsp_push_reg(buf, n*size);
   bsp_bcast(ifrom, buf, buf, n*size);
   bsp_pop_reg(buf);
}
#endif
#ifdef BSP0
void
par_broadcast(buf, n, size, ifrom)
gptr	*buf;
int	n;
size_mt	size;
int	ifrom;
{
   int m;
   long nbyt = n*size;	/* Must have a signed type for loop test */
   
   /*
    * BSP only allows operations on statically allocated buffers. *sigh*
    * Use loop to perform general operation copying in and out of a
    * fixed-size, static buffer.
    */
   while( nbyt > 0 )
   {
      m = MIN(nbyt, NBUFMAX);
      memcp(tmpbuf, buf, m);
      bspbroadcast(ifrom, tmpbuf, tmpbuf, m);
      memcp(buf, tmpbuf, m);
      buf = (char*)buf + m;
      nbyt -= m;
   }
}
#endif
#ifdef SHMEM
void
par_broadcast(buf, n, size, ifrom)
gptr	*buf;
int	n;
size_mt	size;
int	ifrom;
{
   int m;
   long nbyt = n*size;	/* Must have a signed type for loop test */
   
   /*
    * Usual comments about fixed-size buffers apply.  The shmem
    * broadcast routine works in 8 byte word units, but that's OK
    * since we copy the exact length in and out of the real arrays.
    */
   while( nbyt > 0 )
   {
      m = MIN(nbyt, NBUFMAX);
      if( ithread == ifrom )
	 memcp(tmpbuf, buf, m);
      shmem_broadcast((long*)tmpbuf, (long*)tmpbuf, 
		      (m+sizeof(long)-1)/sizeof(long), 
		      ifrom, 0, 0, nthreads, pSyncb);
      if( ithread != ifrom )
	 memcp(buf, tmpbuf, m);
      buf = (char*)buf + m;
      nbyt -= m;
      barrier();
   }
}
#endif
#ifdef MPI
void
par_broadcast(buf, n, size, ifrom)
gptr	*buf;
int	n;
size_mt	size;
int	ifrom;
{
   MPI_Bcast(buf, n*size, MPI_BYTE, ifrom, MPI_COMM_WORLD);
}
#endif
/******************************************************************************
 * par_collect_all().  Global gather to all.                                  *
 ******************************************************************************/
#ifdef BSP
void
par_collect_all(send, recv, n, stride, nblk)
real	*send, *recv;
int	n, nblk, stride;
{
   int  i, right, left, iblk, ibeg;
   
   bsp_push_reg(recv, nblk*stride*sizeof(real));
   /*
    * Copy send data to proper place in receive buffer unless it is
    * already there.
    */
   if( recv+ithread*n != send )
   {
      for(iblk = 0; iblk < nblk; iblk++)
	 memcp(recv+ithread*n+iblk*stride, send+iblk*stride, n*sizeof(real));
   }

   for (i=1; i= 0 )
	 for(iblk = 0; iblk < nblk; iblk++)
	    bsp_hpput(right, recv+ibeg*n+iblk*stride, recv,
                            (ibeg*n+iblk*stride)*sizeof(real), i*n*sizeof(real));
      else
	 for(iblk = 0; iblk < nblk; iblk++)
	 {
	    bsp_hpput(right, recv+(ibeg+nthreads)*n+iblk*stride, recv,
                            ((ibeg+nthreads)*n+iblk*stride)*sizeof(real), 
		            -ibeg*n*sizeof(real));
	    bsp_hpput(right, recv+iblk*stride, recv,
                            (iblk*stride)*sizeof(real), 
                            (ithread+1)*n*sizeof(real));
	 }
      bsp_sync();
   }
   bsp_pop_reg(recv);
}
#endif
#ifdef BSP0
void
par_collect_all(send, recv, n, stride, nblk)
real	*send, *recv;
int	n, nblk, stride;
{
   int  i, right, left, iblk, ibeg;
   real *recvbuf = (real*)tmpbuf;
   int  nbuf = NBUFMAX/sizeof(real);

   if(nblk*stride > nbuf)
      message(NULLI, NULLP, FATAL, "Par_Collect_All: Buffer too small %d,%d\n",
	      nblk*stride,nbuf);
   
   for(iblk = 0; iblk < nblk; iblk++)
      memcp(recvbuf+ithread*n+iblk*stride, send+iblk*stride, n*sizeof(real));
  
   for (i=1; i= 0 )
	 for(iblk = 0; iblk < nblk; iblk++)
	    bspstore(right, recvbuf+ibeg*n+iblk*stride, 
		            recvbuf+ibeg*n+iblk*stride, i*n*sizeof(real));
      else
	 for(iblk = 0; iblk < nblk; iblk++)
	 {
	    bspstore(right, recvbuf+(ibeg+nthreads)*n+iblk*stride, 
		            recvbuf+(ibeg+nthreads)*n+iblk*stride, 
		            -ibeg*n*sizeof(real));
	    bspstore(right, recvbuf+iblk*stride, 
		            recvbuf+iblk*stride, (ithread+1)*n*sizeof(real));
	 }
      bspsstep_end(111);
   }
   memcp(recv, recvbuf, nblk*stride*sizeof(real));
}
#endif
#ifdef SHMEM
#define NSYNC 16
void
par_collect_all(send, recv, n, stride, nblk)
real	*send, *recv;
int	n, nblk, stride;
{
   int  i, right, left, iblk, ibeg, iSync;
   static long *recvs;
   long        *recvbuf;
   static volatile long SyncB[NSYNC];
                   long Sync0 = ithread;
   long        *cur_break;
   
   for(iSync=0; iSync < NSYNC; iSync++)
      SyncB[iSync] =  _SHMEM_SYNC_VALUE;
   iSync = 0;

   recvs = (long*)recv;
   /*
    * Copy send data to proper place in receive buffer unless it is
    * already there.
    */
   if( recv+ithread*n != send )
   {
      for(iblk = 0; iblk < nblk; iblk++)
	 memcp(recv+ithread*n+iblk*stride, send+iblk*stride, n*sizeof(real));
   }

   barrier();		/* Ensure all PEs ready to receive data */
   shmem_set_cache_inv();
   
   /*
    * This version lives dangerously and works "in-place" with
    * the existing receive buffers.  
    */
   for (i=1; i cur_break )
      {
	 if( sbreak(recvbuf+nblk*stride - cur_break) == 0)
	    message(NULLI, NULLP, FATAL, 
		    "Par_collect_all (%d): sbreak() failed\n");
      }

      if( ibeg >= 0 )
	 for(iblk = 0; iblk < nblk; iblk++)
	    shmem_put(recvbuf+ibeg*n+iblk*stride, 
		      recvs+ibeg*n+iblk*stride, i*n, right);
      else
	 for(iblk = 0; iblk < nblk; iblk++)
	 {
	    shmem_put(recvbuf+(ibeg+nthreads)*n+iblk*stride, 
		      recvs+(ibeg+nthreads)*n+iblk*stride, -ibeg*n, right);
	    shmem_put(recvbuf+iblk*stride, 
		      recvs+iblk*stride, (ithread+1)*n, right);
	 }
      shmem_quiet();	                   /* Wait for puts to complete  */
      shmem_put((long*)&SyncB[iSync], &Sync0, 1, right); 
                                           /* Signal to "right" its done */
      while( SyncB[iSync] == _SHMEM_SYNC_VALUE )
	 ;			/* Spin wait until data has arrived here */
#ifdef DEBUG
      if( SyncB[iSync] != left )
	 message(NULLI, NULLP, FATAL, 
		 "Par Collect: (%d) Sync out of order. Expected %d, got %d\n",
		 ithread, left, SyncB[iSync]);
#endif
      SyncB[iSync] = _SHMEM_SYNC_VALUE;
      iSync++;
   }
   shmem_clear_cache_inv();
}
#endif
#ifdef MPI
void
par_collect_all(send, recv, n, stride, nblk)
real	*send, *recv;
int	n, nblk, stride;
{
   int i;
   int  blens[2];
   MPI_Datatype vtype, block, types[2];
   MPI_Aint displs[2];

   /*
    * Use the defined datatypes of MPI to collect the whole array from
    * distributed slices across processors.  The "vtype" vector defines
    * the actual data, "nblk" blocks of "n" elements with a stride of "stride".
    * "block" is a struct with identical data to "vtype" BUT with a upper
    * bound of exactly 1 block.  This fools MPI_Allgather into assembling
    * the scattered data into an interleaved array.
    */
   MPI_Type_vector(nblk, n, stride, M_REAL, &vtype);
   blens[0]  = 1;     blens[1]  = 1;
   types[0]  = vtype; types[1]  = MPI_UB;
   displs[0] = 0;     displs[1] = n*sizeof(real);
   MPI_Type_struct(2, blens, displs, types, &block);
   MPI_Type_commit(&block);
   
   MPI_Allgather(send, 1, block, recv, 1, block, MPI_COMM_WORLD); 
   MPI_Type_free(&vtype);
   MPI_Type_free(&block);   
}
#endif
/******************************************************************************
 * par_begin().  Initialize parallel libs.				      *
 ******************************************************************************/
#ifdef TCGMSG
void
par_begin(argc, argv, ithread, nthreads)
int	*argc;
char	***argv;
int	*ithread;
int	*nthreads;
{
   int i;
   PBEGIN_(*argc, *argv);
   *nthreads = NNODES_();
   *ithread  = NODEID_();
   for(i = 1; i < *argc; i++ )
      if( !strcmp((*argv)[i],"-master"))
      {
	 *argc = i-1;
	 (*argv)++;
	 break;
      }
}
#endif
#ifdef BSP
void
par_begin(argc, argv, ithread, nthreads)
int	*argc;
char	***argv;
int	*ithread;
int	*nthreads;
{
   bsp_begin(bsp_nprocs());
   *nthreads = bsp_nprocs();
   *ithread  = bsp_pid();
}
#endif
#ifdef BSP0
void
par_begin(argc, argv, ithread, nthreads)
int	*argc;
char	***argv;
int	*ithread;
int	*nthreads;
{
   bspstart(*argc, *argv, 0, nthreads, ithread);
}
#endif
#ifdef SHMEM
void
par_begin(argc, argv, ithread, nthreads)
int	*argc;
char	***argv;
int	*ithread;
int	*nthreads;
{
   int i;
   *nthreads = _num_pes();
   *ithread  = _my_pe();
   
   for(i=0; i < _SHMEM_BCAST_SYNC_SIZE; i++)
     pSyncb[i] = _SHMEM_SYNC_VALUE;
   for(i=0; i < _SHMEM_REDUCE_SYNC_SIZE; i++)
     pSync[0][i] = pSync[1][i] =  _SHMEM_SYNC_VALUE;
   barrier();
}
#endif
#ifdef MPI
void
par_begin(argc, argv, ithread, nthreads)
int	*argc;
char	***argv;
int	*ithread;
int	*nthreads;
{
   MPI_Init(argc, argv);
   MPI_Comm_size(MPI_COMM_WORLD, nthreads);
   MPI_Comm_rank(MPI_COMM_WORLD, ithread);
}
#endif
/******************************************************************************
 * par_finish().  Parallel lib wind-up function.			      *
 ******************************************************************************/
#ifdef TCGMSG
void
par_finish()
{
   PEND_();
}
#endif
#ifdef BSP
void
par_finish()
{
   bsp_end();
}
#endif
#ifdef BSP0
void
par_finish()
{
   bspfinish();
}
#endif
#ifdef SHMEM
void
par_finish()
{
  barrier();
}
#endif
#ifdef MPI
void
par_finish()
{
   MPI_Finalize();
}
#endif
/******************************************************************************
 * par_abort().  Parallel lib abort function.				      *
 ******************************************************************************/
#ifdef TCGMSG
void
par_abort(code)
int code;
{
   Error("",code);
   exit(code);
}
#endif
#ifdef BSP
void
par_abort(code)
int code;
{
   bsp_abort("");
   exit(code);
}
#endif
#ifdef BSP0
void
par_abort(code)
int code;
{
#ifdef NOTYET
   bspabort(code);
#endif
   bspfinish();
   exit(code);
}
#endif
#ifdef SHMEM
void
par_abort(code)
int code;
{
   globalexit(code);
}
#endif
#ifdef MPI
void
par_abort(code)
int code;
{
   MPI_Abort(MPI_COMM_WORLD, code);
   exit(code);
}
#endif
/******************************************************************************
 *  copy_sysdef                                                            *
 ******************************************************************************/
void	copy_sysdef(system, spec_ptr, site_info, pot_ptr)
system_mp	system;			/* Pointer to system array (in main)  */
spec_mp		*spec_ptr;		/* Pointer to be set to species array */
site_mp		*site_info;		/* To be pointed at site_info array   */
pot_mp		*pot_ptr;		/* To be pointed at potpar array      */
{
   spec_mp	spec;
   int		n_pot_recs;
#ifdef SPMD
   /*
    * Fetch "system" struct
    */
   par_broadcast((gptr*)system, 1, lsizeof(system_mt), 0);
   /* Allocate space for species, site_info and potpar arrays and set pointers*/
   if( ithread > 0 )
   {
      *spec_ptr  = aalloc(system->nspecies,                spec_mt );
      *site_info = aalloc(system->max_id,                  site_mt );
      *pot_ptr   = aalloc(system->max_id * system->max_id, pot_mt );
   }
   /*  read species array into allocated space				      */
   par_broadcast((gptr*)*spec_ptr, system->nspecies, lsizeof(spec_mt),0);
   /* Fill p_f_sites and site_id arrays for each species */
   for (spec = *spec_ptr; spec < &(*spec_ptr)[system->nspecies]; spec++)
   {
      if( ithread > 0 )
      {      
	 spec->p_f_sites = ralloc(spec->nsites);  /* Allocate the species -     */
	 spec->site_id   = ialloc(spec->nsites);  /* specific arrays	      */
      }
      par_broadcast((gptr*)spec->p_f_sites, 3*spec->nsites, sizeof(real), 0);
      par_broadcast((gptr*)spec->site_id,     spec->nsites, sizeof(int), 0);
   }
   /* Fill site_info array */
   par_broadcast((gptr*)*site_info, system->max_id, sizeof(site_mt), 0);
   /*
    * Potential Parameters.
    */
   n_pot_recs = SQR(system->max_id);
   if( ithread > 0 )
      *pot_ptr = (pot_mt*)aalloc(n_pot_recs*sizeof(pot_mt), char);
   par_broadcast((gptr*)*pot_ptr, n_pot_recs, sizeof(pot_mt), 0);
#endif
}
/******************************************************************************
 *  copy_dynamics()							      *
 ******************************************************************************/
void	copy_dynamics(system)
system_mp	system;
{
   gptr		*ap;			/* Pointer to averages database       */
   size_mt	asize;			/* Size of averages database	      */

#ifdef SPMD
   par_broadcast((gptr*)system->c_of_m,3*system->nmols, sizeof(real), 0);
   par_broadcast((gptr*)system->vel,   3*system->nmols, sizeof(real), 0);
   par_broadcast((gptr*)system->velp,  3*system->nmols, sizeof(real), 0);
   par_broadcast((gptr*)system->acc,   3*system->nmols, sizeof(real), 0);
   par_broadcast((gptr*)system->acco,  3*system->nmols, sizeof(real), 0);
   par_broadcast((gptr*)system->accvo, 3*system->nmols, sizeof(real), 0);
   if(system->nmols_r > 0)
   {
      par_broadcast((gptr*)system->quat,    4*system->nmols_r, sizeof(real), 0);
      par_broadcast((gptr*)system->qdot,    4*system->nmols_r, sizeof(real), 0);
      par_broadcast((gptr*)system->qdotp,   4*system->nmols_r, sizeof(real), 0);
      par_broadcast((gptr*)system->qddot,   4*system->nmols_r, sizeof(real), 0);
      par_broadcast((gptr*)system->qddoto,  4*system->nmols_r, sizeof(real), 0);
      par_broadcast((gptr*)system->qddotvo, 4*system->nmols_r, sizeof(real), 0);
   }
   par_broadcast((gptr*)system->h,       9, sizeof(real), 0); 
   par_broadcast((gptr*)system->hdot,    9, sizeof(real), 0);
   par_broadcast((gptr*)system->hdotp,   9, sizeof(real), 0); 
   par_broadcast((gptr*)system->hddot,   9, sizeof(real), 0);
   par_broadcast((gptr*)system->hddoto,  9, sizeof(real), 0); 
   par_broadcast((gptr*)system->hddotvo, 9, sizeof(real), 0);

   par_broadcast((gptr*)system->ta,      system->nspecies, sizeof(real), 0);
   par_broadcast((gptr*)system->tap,     system->nspecies, sizeof(real), 0);
   par_broadcast((gptr*)system->tadot,   system->nspecies, sizeof(real), 0);
   par_broadcast((gptr*)system->tadoto,  system->nspecies, sizeof(real), 0);
   par_broadcast((gptr*)system->tadotvo, system->nspecies, sizeof(real), 0);
					                   	      
   par_broadcast((gptr*)system->ra,      system->nspecies, sizeof(real), 0);
   par_broadcast((gptr*)system->rap,     system->nspecies, sizeof(real), 0);
   par_broadcast((gptr*)system->radot,   system->nspecies, sizeof(real), 0);
   par_broadcast((gptr*)system->radoto,  system->nspecies, sizeof(real), 0);
   par_broadcast((gptr*)system->radotvo, system->nspecies, sizeof(real), 0);

   ap = av_ptr(&asize,0);	      /* get addr, size of database   */
   par_broadcast(ap, 1, asize,0);

   /*
    * N.B. We do NOT broadcast the accumulated RDF info.  That would
    * be incorrect since it is later summed. Leave on thread zero and
    * zeros on other threads.  When globally summed it will be correct.
    */
#endif
}
/******************************************************************************
 * replicate().  Make a copy of all Moldy's constant and dynamic data.        *
 *               This is for parallel implementations and allows "start_up"   *
 *		 to be called on one processor.                               *
 ******************************************************************************/
void replicate(control, system, spec_ptr, site_info, pot_ptr, restart_header)
contr_mt  *control;
system_mt *system;
spec_mt   **spec_ptr;
site_mt	  **site_info;
pot_mt    **pot_ptr;
restrt_mt *restart_header;
{
   int av_convert;
   /*
    *  Fetch the top-level structs
    */
#ifdef SPMD
   par_broadcast((gptr*)control, 1, sizeof *control, 0);
   par_broadcast((gptr*)restart_header, 1, sizeof *restart_header, 0);
   /*
    * Now get "species" struct array and internal dynamic arrays
    */
   copy_sysdef(system, spec_ptr, site_info, pot_ptr);
   if( ithread > 0 )
   {
      /*
       * Now we have all the information to create the dynamic variables
       */
      allocate_dynamics(system, *spec_ptr);
      /*
       * Initialise averages database.
       */
      init_averages(system->nspecies, (char*)0,
		    control->roll_interval, control->roll_interval,&av_convert);
      /*
       * Initialise radial distribution function database.
       */
      if(control->rdf_interval > 0)
         init_rdf(system);
   }
   /*
    * Copy the dynamic vars from the other processes.
    */
   copy_dynamics(system);
#endif
}
$EOD
$!
$CREATE structs.h
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/*
 * $Header: /home/eeyore_data/keith/md/moldy/RCS/structs.h,v 2.9 1996/10/19 11:55:24 keith Exp $
 *
 * $Log: structs.h,v $
 * Revision 2.9  1996/10/19 11:55:24  keith
 * Corrected bug in control struct and xdr write of thermostat vars.
 *
 * Revision 2.8  1996/01/15 15:25:28  keith
 * Corrected definition of thermostat dynamic vars.
 *
 * Revision 2.7  1995/12/04 11:45:49  keith
 * Nose-Hoover and Gaussian (Hoover constrained) thermostats added.
 * Thanks to V. Murashov.
 *
 * Revision 2.6  1994/06/08  13:16:34  keith
 * Changed all timestep-related parameters to type "long". This means
 * that 16-bit DOS compilers can do more than 32767 timesteps.
 *
 * Revision 2.5  1994/01/18  13:33:02  keith
 * Null update for XDR portability release
 *
 * Revision 2.5  1994/01/18  13:33:02  keith
 * Null update for XDR portability release
 *
 * Revision 2.3  93/10/28  10:28:19  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.1  93/07/19  13:28:21  keith
 * Added XDR capability for backup and dump files.
 * 
 * Revision 2.0  93/03/15  14:49:26  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.6.1.15  93/03/12  12:14:34  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.6.1.15  93/03/09  15:59:22  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.6.1.14  92/10/28  14:09:40  keith
 * Changed "site_[tp]" typedefs to avoid name clash on HP.
 * 
 * Revision 1.6.1.13  92/09/22  14:55:12  keith
 * Added support for "strict cutoff" mode.
 * 
 * Revision 1.6.1.12  92/03/11  12:56:24  keith
 * Changed "scale-separately" parameter to "scale options"
 * 
 * Revision 1.6.1.11  91/08/23  11:34:49  keith
 * Added extra padding to struct spec_t to round size up to 8 byte
 * boundary.  This promotes portability of restart files.  Rounded
 * up rather than down for compatibility of existing restart files.
 * 
 * Revision 1.6.1.10  91/08/15  18:12:21  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_t and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.6.1.9  90/05/02  15:44:36  keith
 * Got rid of typedefs time_t and size_t. 
 * 
 * Revision 1.6.1.8  90/04/16  18:20:40  keith
 * Added new field "strain-mask" to control.
 * 
 * Revision 1.6.1.7  90/04/12  16:29:10  keith
 * removed unneccessary include of 
 * 
 * Revision 1.6.1.6  90/04/06  11:09:49  keith
 * Moved definition of NPOTP to defs.h
 * 
 * Revision 1.6.1.5  89/11/21  16:31:32  keith
 * Removed member out_file from control and all uses. (Now command parameter).
 * 
 * Revision 1.6.1.4  89/11/20  18:10:41  keith
 * Added "defalt" field to match_t.
 * 
 * Revision 1.6.1.3  89/11/20  13:30:14  keith
 * Replaced separate arrays "types" and "npotp" with array of structs "potspec"
 * 
 * Revision 1.6.1.2  89/09/04  18:40:19  keith
 * Added 'surface_dipole' to control_t (& removed pad), moved 'scale_separately'
 * Added field 'charge' to spec_t.
 * 
 * Revision 1.6.1.1  89/08/25  15:24:21  keith
 * Mods to add framework structures to simulation model
 * 
 * Revision 1.6  89/06/20  18:25:36  keith
 * Moved definition of match_t to structs.h 
 * 
 * Revision 1.5  89/06/01  21:25:33  keith
 * Control.out eliminated, use printf and freopen instead to direct output.
 * 
 * Revision 1.4  89/05/22  14:05:48  keith
 * Added rescale-separately option, changed 'contr_t' format.
 * 
 * Revision 1.3  89/05/15  16:12:04  keith
 * Added new members, 'vsn' and 'dump_size' to dump_t.
 * * Must use with r1.3 or later of 'dump.c'.
 * 
 * Revision 1.2  89/05/11  13:50:32  keith
 * Modified restrt_t to allow commensurate sun3/sun4 padding
 * 
 * Revision 1.1  89/04/27  14:44:58  keith
 * Initial revision
 * 
 * 
 */
#ifndef STRUCT_ALREADY
#define STRUCT_ALREADY

#include "defs.h"

#define         SFORM   "%127[^#]" /* Format for scanf to read strings safely */
typedef struct                  /* Control parameters for simulation          */
{
   char         title[L_name];  /* Job title                                  */
   long         istep,          /* Current timestep - used as loop counter    */
                nsteps;         /* Number of timesteps to execute             */
   double       step;           /* Value of timestep in program units         */
   boolean      print_sysdef,   /* Flag to print out system specification file*/
                new_sysdef,     /* Read new sysdef instead of restart file one*/
                const_pressure, /* Flag to turn on P&R CP method              */
                reset_averages, /* Flag to set average counters to zero       */
                scale_options,  /* Scale each species separately              */
                surface_dipole, /* Flag surface dipole term in Ewald sum      */
                lattice_start;  /* Flag to read starting state from sysdef    */
   char         sysdef[L_name],         /* Name of system specification file  */
                restart_file[L_name],   /* Name of restart configuration file */
                save_file[L_name],      /* Name of file to write restart conf */
                dump_file[L_name],      /* Name of file 'dump' writes to      */
                backup_file[L_name],    /* Name of backup save file           */
                temp_file[L_name];      /* Temporary file for writing restart */
   int          spare[23];      /* Extra space for expansion (should be ODD)  */
   double       ttmass,         /* Nose-Hoover trans temp mass parameter      */
                rtmass;         /* Nose-Hoover rotat temp mass parameter      */
   int		pad;		/* To keep alignment of struct.		      */
   int          const_temp;     /* Flag to turn on N&H CT method              */
   boolean      xdr_write,      /* Write restart, dump files in portable way. */
                strict_cutoff;  /* Perform real-space cutoff rigorously       */
   int          strain_mask;    /* Mask of constrained elements of h matrix   */
   int          nbins;          /* Number of bins for rdf calculation         */
   unsigned long seed;          /* Seed for random number generator           */
   int          page_width,     /* Line width for output file                 */
                page_length;    /* Length of page on output file              */
   long         scale_interval, /* Number of timesteps between scales         */
                scale_end,      /* Stop scaling after n timesteps             */
                begin_average,  /* Number of 'equilibration' steps            */
                average_interval,/* Frequency of averages calculation         */
                begin_dump,     /* When to start storing dumps for analysis   */
                dump_offset,    /* Used in for dump file names - internal only*/
                dump_interval;  /* Frequency of configuration dumps           */
   int          dump_level,     /* What to dump to file                       */
                maxdumps;       /* How many dump records in a dump file       */
   long         backup_interval,/* Frequency to save state to backup file     */
                roll_interval,  /* Number of timesteps for rolling avgs       */
                print_interval, /* Number of timesteps between printouts      */
                begin_rdf,      /* When to start RDF calculation              */
                rdf_interval,   /* Frequency to accumulate rdf data           */
                rdf_out;        /* Frequency to calculate and output rdf      */
   double       temp,           /* Required temperature                       */
                pressure,       /* Required pressure                          */
                pmass,          /* Parinello and Rahman W parameter           */
                cutoff,         /* Cut off radius                             */
                subcell,        /* Size of side of interaction cells          */
                density,        /* Initial density on set-up                  */
                alpha,          /* Convergence factor for Ewald sum           */
                k_cutoff,       /* Cutoff in k space for ewald sum            */
                limit,          /* Limiting distance for rdf calculation      */
                cpu_limit;      /* Maximum CPU allowed before run is stopped  */
} contr_mt, *contr_mp;

typedef struct                  /* Whole system information                   */
{
   int          nsites,         /* Total number of sites                      */
                nmols,          /* Total number of molecules/atoms            */
                nmols_r,        /* Total number of polyatomics                */
                nspecies,       /* Number of different molecule types         */
                max_id,         /* Last dimension of potpar array             */
                d_of_f;         /* Degrees of freedom of whole system         */
   int          ptype,          /* 0 = LJ, 1= buckingham, 2= MCY              */
                n_potpar;       /* # parameters for this potential            */
                /* Dynamic variable arrays for whole system                   */
                /* Dimensions for C of M quantities are [nmols][3]            */
                /* and for quaternions and derivatives, [nmols_r][4]          */
   vec_mp       c_of_m,         /* Centre of mass positions                   */
                vel,            /* " " " velocities                           */
                velp,           /* Predicted C of M velocities                */
                acc,            /* C of M accelerations                       */
                acco,           /* " " at previous timestep                   */
                accvo;          /* " " two timesteps before                   */
   quat_mp      quat,           /* Quaternions for this component             */
                qdot,           /* Quaternion derivatives                     */
                qdotp,          /* Predicted quaternion derivatives           */
                qddot,          /* Quaternion second derivatives              */
                qddoto,         /* Old quaternion second derivatives          */
                qddotvo;        /* Second derivatives two timesteps before    */
   mat_mp       h,              /* Unit cell for zero-stress simulation       */
                hdot,           /* Unit cell derivatives                      */
                hdotp,          /* Predicted unit cell derivatives            */
                hddot,          /* Unit cell second derivatives               */
                hddoto,         /* Old unit cell second derivatives           */
                hddotvo;        /* Very old unit cell second derivatives      */
                /*
                 *  Following variables ta..., ra.. have been introduced
                 *  by VVM and have dimensions of [nspecies]
                 */
   real        *ta,             /* N-H alpha for trans temp                   */
               *tap,            /* Predicted N-H alpha for trans temp         */
               *tadot,          /* Derivative of trans alpha                  */
               *tadoto,         /* Old derivative of trans alpha              */
               *tadotvo;        /* Very old derivative of trans alpha         */
   real        *ra,             /* N-H alpha for trans temp                   */
               *rap,            /* Predicted N-H alpha for trans temp         */
               *radot,          /* Derivative of trans alpha                  */
               *radoto,         /* Old derivative of trans alpha              */
               *radotvo;        /* Very old derivative of trans alpha         */
} system_mt, *system_mp;


typedef struct                  /* Information for one species                */
{
   real         inertia[3],     /* Principal moments of inertia               */
                mass,           /* Mass of whole molecule                     */
                dipole,         /* Dipole Moment                              */
                charge;         /* Total charge                               */
   int          nsites,         /* Number of sites on this species            */
                nmols;          /* Number of molecules of this species        */
   int          rdof,           /* Rotational degrees of freedom (2=linear)   */
                framework;      /* Flag to signal this is a framework species */
   char         name[32];       /* Name of this species                       */
   int          *site_id;       /* site identifier array                      */
   vec_mp       p_f_sites;      /* Site co-ordinates in principal frame       */
                /* Dynamic variable arrays for this species                   */
                /* These point to a subset of the whole-system arrays         */
                /* Dimensions for C of M quantities are [nmols][3]            */
                /* and for quaternions and derivatives, [nmols][4]            */
                /* If species is monatomic, quaternion pointers are null      */
   vec_mp       c_of_m,         /* Centre of mass positions                   */
                vel,            /* " " " velocities                           */
                velp,           /* Predicted C of M velocities                */
                acc,            /* C of M accelerations                       */
                acco,           /* " " at previous timestep                   */
                accvo;          /* " " two timesteps before                   */
   quat_mp      quat,           /* Quaternions for this species               */
                qdot,           /* Quaternion derivatives                     */
                qdotp,          /* Predicted quaternion derivatives           */
                qddot,          /* Quaternion second derivatives              */
                qddoto,         /* Old quaternion second derivatives          */
                qddotvo;        /* Second derivatives two timesteps before    */
   int          pad[2];         /* Needed for compatibility of binary restart */
                                /* files due to historical cock-up.           */ 
} spec_mt, *spec_mp;

typedef struct                  /* site info template.                        */
{
   double       mass,
                charge;
   char         name[8];
   int          flag;
   int          pad;
}       site_mt,  *site_mp;
                
typedef struct                  /* Holds potential parameter information      */
{
   int          flag;
   int          pad;
   real         p[NPOTP];
} pot_mt, *pot_mp;

typedef struct
{
   char *name;
   int  npar;
} pots_mt;

typedef struct                  /* Units used for program input               */
{
   double       m,              /* mass                                       */
                l,              /* length                                     */
                t,              /* time                                       */
                q;              /* charge                                     */

} unit_mt, *unit_mp;

typedef struct                  /* Record of dimensions of physical quantity  */
{
  int           m,              /* Number of powers of mass in unit           */
                l,
                t,
                q;
} dim_mt, *dim_mp;

typedef struct                          /* Struct template for keyword        */
{                                       /* in read_control.                   */
   char *key,
        *format,
        *defalt;
   gptr *ptr;
}       match_mt;

#define DLEN    28              /* Length of date/time string                 */
typedef struct                  /* Restart file header format                 */
{
   time_mt      timestamp,      /* Date and time restart file was written     */
                prev_timestamp; /* Timestamp of preceding restart file        */
   char         init_date[DLEN],/* Date run was initiated (propagated through)*/
                title[L_name],  /* Title when run was initiated               */
                vsn[16];        /* Version SID of program that wrote restart  */
   int          seq;            /* Sequence NO.  eg 5th restart in run        */
}       restrt_mt;

typedef struct                  /* Dump file header format                    */
{
   char         title[L_name],  /* Run title at beginning of dump run         */
                vsn[16];        /* RCS Revision number                        */
   long         istep,          /* Timestep at beginning of this file         */
                dump_interval;  /* How many steps between dumps               */
   int          dump_level,     /* Parameter determining contents of record   */
                maxdumps,       /* Maximum number of dump records in file     */
                ndumps,         /* How many dump records in file              */
                dump_size;      /* Size of a dump record                      */
   time_mt      timestamp,      /* Time file was written                      */
                dump_init,      /* Time dump run was started (ie first file)  */
                restart_timestamp;/* Time corresponding restart file written  */
}       dump_mt;

#define MAX_ROLL_INTERVAL       100
typedef struct
{   double      value,
                sum,
                sum_sq,
                mean,
                sd,
                roll[MAX_ROLL_INTERVAL],
                roll_mean,
                roll_sd;
} old_av_mt;

typedef union
{
   old_av_mt            av;
   struct
   {    
      int       av, roll;
   }            cnt;
} old_av_u_mt;

typedef struct
{       
   int          nav, 
                nroll, 
                iroll, 
                pad;
   double align;
} av_head_mt;

#endif
$EOD
$!
$CREATE defs.h
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/*
 * $Header: /home/eeyore_data/keith/md/moldy/RCS/defs.h,v 2.12.1.2 1998/01/27 15:44:56 keith Exp $
 *
 * $Log: defs.h,v $
 * Revision 2.12.1.2  1998/01/27 15:44:56  keith
 * Restructured configuration macros to be more rational.  There should
 * now be V. few refs to OS or machine-specific macros in actual code.
 * Standardised on __unix__, __WIN32__, vms variants of macros.
 *
 * New macros HAVE_GETOPS, ALLOC_SEPARATELY, ALLOC_ALIGN.
 * Changed HAS_POPEN to HAVE_POPEN
 *
 * Revision 2.12.1.1  1998/01/15 12:05:37  keith
 * Changed to "HAVE_POPEN" macro from system-specifics.
 * Defined HAVE_POPEN for unix and VMS only so far.
 *
 * Corrected __sgi__ macro to __sgi.
 *
 * Revision 2.13  1998/01/09 11:36:01  keith
 * Changed to "HAVE_POPEN" macro from system-specifics.
 * Defined HAVE_POPEN for unix and VMS only so far.
 *
 * Revision 2.12  1997/10/15 14:07:15  keith
 * Null update for version release.
 *
 * Revision 2.11  1996/11/05 16:50:29  keith
 * Release for 2.11.
 * Added ANSI_LIBS for Sun Solaris2
 * Defs modified for Convex/HP SPP.
 * Vector selection macro VECTOR added for site_neighbour_list() and
 *   cray PVP tested for by _CRAY1 macro.
 * Added cache-tuning parameters NLINE and NCACHE.
 *
 * Revision 2.10  1996/03/06 18:16:21  keith
 * Minor mods assuming ANSI behaviour on MS_DOS
 * Removed all COS functionality.
 * Updated IBM AIX macro selection
 * Added ANSI_LIBS functionality for Linux, OSF and SGI
 *
 * Added conditional defn of VOLATILE for the precision() bugfix
 * Nose-Hoover and Gaussian (Hoover constrained) thermostats added.
 *
 * Updated constants from CODATA 1986. Compile with -DOLDCONSTS for
 * compatibility.
 *
 * Revision 2.10  1995/12/22 14:00:52  keith
 * Minor mods assuming ANSI behaviour on MS_DOS
 * Removed all COS functionality.
 *
 * Nose-Hoover and Gaussian (Hoover constrained) thermostats added.
 *
 * Updated constants from CODATA 1986. Compile with -DOLDCONSTS for
 * compatibility
 *
 * Revision 2.9  1995/01/05  09:56:35  keith
 * Null update to increment version number
 *
 * Revision 2.8  1994/07/07  17:03:39  keith
 * Fixed up missing xdr_vector to be compiled in only if NEED_XDR_VECTOR defined.
 *
 * Revision 2.8  1994/07/07  17:03:39  keith
 * Fixed up missing xdr_vector to be compiled in only if NEED_XDR_VECTOR defined.
 *
 * Revision 2.7  1994/06/08  13:10:43  keith
 * New macro "balloc(n,size)" for cases when type isn't explicit.
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Added CONST qualifier to (re-)declarations of ANSI library
 * emulation routines to give reliable compilation even
 * without ANSI_LIBS macro. (#define's away for K&R
 * compilers)
 *
 *
 * Now recognises _UNICOS macro and defines "unix".
 * Specific MSDOS file names added on __MSDOS__ macro.
 * Evaluates CONST macro to const or nil depending on
 * ANSI/KR environment.
 * Added size_mt typedef (ulong) for interfacing with lib fns.
 *
 * Revision 2.5  94/01/25  16:49:41  keith
 * Incorporated all portability experience to multiple platforms since 2.2.
 * Including ports to VAX/VMS and Open VMS on Alpha AXP and Solaris.
 * 
 * Revision 2.4  94/01/18  13:13:42  keith
 * Workaround for bugs and defined symbol _HPUX_SOURCE needed to compile xdr.
 * 
 * Revision 2.3.1.1  93/12/21  19:02:17  keith
 * Mods to allow HP's ANSI compiler to work.  I think it's broken.
 * Not tested on other architectures yet so don't incorporate
 * into main line.
 * NB Added _POSIX_SOURCE and _XOPEN_SOURCE symbols for *all* architectures.
 * This might or might no avoid problems.
 * 
 * Revision 2.3  93/10/28  17:41:01  keith
 * Added __unix to the list of recognised macros
 * 
 * Revision 2.3  93/10/28  10:28:29  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.2  93/10/14  18:17:56  keith
 * Fixed prortability problems to IBM RS6000
 * 
 * Revision 2.1  93/08/18  20:54:03  keith
 * Tidied up clashes over ABS, MIN, MAX macros.
 * 
 * Revision 2.1  93/07/19  13:27:15  keith
 * Added XDR capability for backup and dump files.
 * 
 * Revision 2.0  93/03/15  14:49:28  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.24  93/03/12  12:26:12  keith
 * Reorganized defines to recognise all ANSI (__type__) forms.
 * Fixed up Cray by defining old symbol.
 * 
 * Revision 1.23  93/03/09  15:59:24  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.22  92/06/15  16:52:00  keith
 * Put parens round arg on xfree macro to make it safe with expr.
 * 
 * Revision 1.21  92/06/12  12:55:56  keith
 * Mods to make it work on VMS again.  Ugh.
 * 
 * Revision 1.20  92/06/11  21:40:41  keith
 * Added LOCKEX macro for system-dependent lock extension.
 * 
 * Revision 1.19  92/06/10  15:53:33  keith
 * Added new potential type "generic" for Neal.
 * 
 * Revision 1.18  92/02/26  14:29:04  keith
 * Updated vectorization directive substitution for Convex C vsn 4.3
 * 
 * Revision 1.17  91/08/19  16:49:34  keith
 * Moved #if so that errno.h is included for system V.
 * 
 * Revision 1.16  91/08/17  13:58:59  keith
 * Added "__unix__" symbol for ANSI unix compilers.
 * 
 * Revision 1.15  91/08/15  18:12:22  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_mt and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.14  91/03/12  15:43:31  keith
 * Tidied up typedefs size_mt and include file 
 * Added explicit function declarations.
 * 
 * Revision 1.13  90/09/28  13:29:45  keith
 * Inserted braces around VECTORIZE directives and changed include files
 * for STARDtardent 3000 series (via cond. comp symbol "ardent").
 * 
 * Revision 1.12  90/09/05  10:30:57  keith
 * Support for cray scc added - directives and ANSI_LIBS macro set.
 * 
 * Revision 1.11  90/08/22  10:58:42  keith
 * Changed ANSI libraries conditional compilation to rely on own
 * symbol ANSI_LIBS rather than __STDC__.
 * Corrected test for cray scc compiler in vectorization directives.
 * 
 * Revision 1.10  90/05/02  15:43:52  keith
 * Got rid of typedefs time_t and size_mt. 
 * 
 * Revision 1.9  90/04/12  16:23:44  keith
 * Used  to check for Berkeley unix and define symbol BSD
 * 
 * Revision 1.8  90/04/06  11:09:22  keith
 * Aquired definition of NPOTP from structs.
 * 
 * Revision 1.7  90/03/27  17:36:12  keith
 * Moved O/S dependent conditionals to here, esp VPRINTF.
 * Reorganised configuration conditionals into one block.
 * 
 * Revision 1.6  90/03/26  18:04:33  keith
 * Tidied up system dependant includes.
 * Added system-dependant backup and temp file names (for input.c).
 * 
 * Revision 1.5  90/03/09  17:34:45  keith
 * Added preprocessor directives to define USG (ie system V) for unicos.
 * 
 * Revision 1.4  89/09/04  18:41:49  keith
 * Added conversion constants for charges.
 * 
 * Revision 1.3  89/06/14  14:16:35  keith
 * Added vectorisation for stellar and recognised sysV & SysV macros
 * 
 * Revision 1.2  89/05/22  14:05:51  keith
 * Added rescale-separately option, changed 'contr_t' format.
 * 
 * Revision 1.1  89/05/02  10:51:58  keith
 * Initial revision
 * 
 * 
 */

#ifndef	DEFS_ALREADY
#define DEFS_ALREADY

/*
 * Version ID strings
 */
#define          REVISION         "$Revision: 2.12.1.2 $"
#define		 REVISION_DATE    "$Date: 1998/01/27 15:44:56 $"
#define		 REVISION_STATE   "$State: Exp $"
/******************************************************************************
 *  Vendor/compiler specific Configurational information.  	              *
 ******************************************************************************/
/*
 * See if we can detect IBM RS6000.
 * _ALL_SOURCE is necessary to make XDR stuff work.
 */
#ifdef _AIX
#   define __unix__
#   define ANSI_LIBS
#   ifndef _ALL_SOURCE
#      define _ALL_SOURCE
#   endif
#endif
/*
 * To allow XDR stuff to work on HP.  Surely there's a more general
 * way of doing this? I think that HPs header files are broken.
 */
#if defined(__hpux) && ! defined(__convex_spp)
#define _HPUX_SOURCE
#endif

#ifdef ardent
#   define NEED_XDR_VECTOR
#endif

#if defined(_CRAY) && ! defined(CRAY)
#   define CRAY
#endif

/*
 * Set ANSI_LIBS only if you have the standard ANSI headers and libraries
 */
#if defined(CRAY)	/* scc compiler comes with libraries*/
#   define ANSI_LIBS
#endif
#ifdef __linux__
#   define ANSI_LIBS
#endif
#ifdef __osf__
#   define ANSI_LIBS
#endif
#ifdef __sgi
#   define ANSI_LIBS
#endif
#if defined(__sun) && defined(__SVR4) /* Solaris 2 libraries are ANSI */
#   define ANSI_LIBS
#endif
/*
 * New convex compiler is ANSI, but doesn't define __STDC__ or  convexvc.
 * It has a silly macro __stdc__ which we will use instead.  convexvc
 * may not be necessary as  is no longer required.  But there
 * is still veclib.
 */
#if defined(__convexc__)
#   if defined(__stdc__) /* Anything but "-pcc" mode */
#      define ANSI
#      define ANSI_LIBS
#   endif
#endif
/*
 * Set HAVE_VPRINTF if this function is in target machine's library.
 */
#ifdef ANSI_LIBS			/* ANSI has it			*/
#define HAVE_VPRINTF
#endif
#if defined(sun) || defined(stellar) || defined(titan) /* So do these     */
#define HAVE_VPRINTF
#endif
/*
 *  Get rid of "const" keyword for non-ANSI compilers.
 */
#if defined(__STDC__) ||  defined(ANSI)
#   define CONST const
#   define VOLATILE volatile
#else
#   define CONST /* */
#   define VOLATILE /* */
#endif
/******************************************************************************
 *  OS- specific Configurational information.  	              *
 ******************************************************************************/
#if (defined(__unix__) || defined(__unix) || defined(_unix_) || defined(_unix) || defined(_UNICOS))
#   ifndef __unix__
#      define __unix__
#   endif
#   define ALLOC_ALIGN
#   define HAVE_POPEN
#   define HAVE_GETOPT
#endif

#if defined(__vms) && !defined(vms)
#   define vms
#   define HAVE_POPEN
#   define HAVE_GETOPT
#   define ANSI
#   define ANSI_LIBS
#endif

#if defined(__MSDOS__)
#   define ALLOC_SEPARATELY
#   define ANSI
#   define ANSI_LIBS
#endif

#if defined(_WIN32) || defined(__WIN32) || defined(__WIN32__)
#   ifndef __WIN32__
#      define __WIN32__
#   endif
#   define ANSI
#   define ANSI_LIBS
#endif
/*
 * Define operating-system dependant default filenames
 */
#if defined(__MSDOS__)
#   define BACKUP_FILE	"MDBCK"
#   define TEMP_FILE	"MDTEMPX"
#   define LOCKEX		"$LK"
#endif

#ifdef vms
#   define BACKUP_FILE	"MDBACKUP.DAT"
#   define TEMP_FILE	"MDTEMPXXXX.DAT"
#   define LOCKEX		"$LCK"
#endif

#ifdef CMS
#   define BACKUP_FILE	"MDBACKUP MOLDY A1"
#   define TEMP_FILE	"MDTEMP XXXXXXXX A1"
#endif

#ifdef __unix__
#   define LOCKEX		".lck"
#endif

#ifdef __WIN32__
#   define LOCKEX		"_lck"
#endif

#ifndef LOCKEX
#   define LOCKEX		"LK"
#endif

/*
 *  Set symbol USG to identify system V variant of unix, BSD for Berkeley.
 */
#include 
/*
 * Berkeley error numbers appear to be very regular, so the following is
 * pretty likely to get it right.  If it doesn't, do the define by hand.
 * BSD is used to signal use of getrusage() rather than times() (unix only)
 * and absence of mem*() and strchr() functions from library.
 */
#if defined(__unix__) && !defined(USG)
#if EWOULDBLOCK==35 && EINPROGRESS==36 && EALREADY==37
# define BSD
#else
# define USG
#endif
#endif
/*
 * Hardware configuration.  Specify cache-tuning params NCACHE and NLINE
 * NCACHE is minimum size of "sites" arrays and should be a sub-multiple
 *   of the cache length in WORDS (sizeof(real)).  It MUST be a power
 *   of two since the alignment algorithm relies on this.
 * NLINE is padding (in words) between arrays to avoid cache conflicts.
 * Theoretically NCACHE should equal the size of the cache and NLINE
 * the size of the cache line.  But too large an NCACHE wastes memory
 * (arrays are padded to a multiple of this) and is actually slower
 * for small systems presumably because it causes TLB misses. 512
 * works well for HP/PA and IBM RISC/POWER.
 * NLINE ought really to be a multiple of the cache line size but it
 *   doesn't seem to matter as long as it's not zero.
 * 512/4 works well on HP/PA/IBM RISC/POWER/SPARC/ALPHA/MIPS so use as
 * default.  Hardware-specific values could be set here conditionally.
 */
#define NCACHE 256
#define NLINE 4
/* 
 * Vectorisation directive translation.  N.B. Most preprocessors munge 
 * directives so the #define must substitute the preprocessor OUTPUT.
 */
#if defined(_CRAY1)	/* This seems to be defined for all CRI PVP systems */
#   ifdef __STDC__
#      define VECTORIZE
#      define NOVECTOR
#   else
#      define VECTORIZE ## ivdep
#      define NOVECTOR  ## novector
#   endif
#   define VECTOR    /* To choose vector vsn of site_neighbour_list */
#else
#if defined(__convexc__) && ! defined(__convex_spp)
#   define VECTORIZE ;/* $dir no_recurrence */;
#   define NOVECTOR  ;/* $dir scalar */;
#   define VECTOR    /* To choose vector vsn of site_neighbour_list */
#else
#ifdef stellar
#   define VECTORIZE __dir NO_RECURRENCE :
#   define NOVECTOR  __dir SCALAR :
#   define VECTOR    /* To choose vector vsn of site_neighbour_list */
#else
#ifdef ardent
#   define VECTORIZE # pragma ivdep
#   define NOVECTOR  # pragma novector
#   define VECTOR    /* To choose vector vsn of site_neighbour_list */
#else
#   define VECTORIZE /* Canny  vectorise on this machine!*/
#   define NOVECTOR  /* */
#endif
#endif
#endif
#endif
/******************************************************************************
 *  End of machine/OS configuration.					      *
 ******************************************************************************/
#define NPOTP 7                 /* Must be number of doubles in pot_mt       */

#ifndef SEEK_END
#define	SEEK_SET	0
#define	SEEK_CUR	1
#define SEEK_END	2
#endif

#define	L_name		128			/* Max Length of file names  */
#define	NPE		2			/* real & Ewald PE's	     */
#undef  MAX
#define MAX(x,y)	((x) > (y) ? (x) : (y))
#define MAX3(x,y,z)	MAX(x, MAX(y,z))
#undef  MIN
#define MIN(x,y)	((x) < (y) ? (x) : (y))
#define	MIN3(x,y,z)	MIN(((x) < (y) ? (x) : (y)),z)
#define SUMSQ(x)	(x[0]*x[0] + x[1]*x[1] +x[2]*x[2])
#define SUMSQ2(x)	(x[1]*x[1] + x[2]*x[2] +x[3]*x[3])
#define	SQR(x)		((x) * (x))
#define CUBE(x)		((x) * (x) * (x))
#define DISTANCE(x,y)	sqrt(SQR(x[0]-y[0])+SQR(x[1]-y[1])+SQR(x[2]-y[2]))
#define	SIGN(x, y)	((y) > 0 ? fabs(x) : -fabs(x))
#define ALPHAMIN	1e-7
/* Fundamental constants '_' denotes MKS units.  SHOULD NEVER BE ALTERED      */
#define	PI		3.14159265358979323846
#define DTOR		(PI/180.0)
#ifdef OLDCONSTS
#define	_kB		1.380662e-23
#define _kcal		4184.0
#define	_EPS0		8.85418782e-12
#define	_ELCHG		1.6021892e-19
#define ECSTR           "1.6021892e-19"
#define	_ROOT_4_PI_EPS	1.0548222865e-5
#define AVOGAD		6.022045e23
#define AMU		1.6605655e-27
#define AMUSTR          "1.6605655e-27"
#define RTAMU           4.0745e-14		/*sqrt(amu) */
#else
/* Values from CODATA 1986 */
#define	_kB		1.380658e-23
#define _kcal		4184.0
#define	_EPS0		8.854187817e-12
#define	_ELCHG		1.60217733e-19
#define ECSTR           "1.60217733e-19"
#define	_ROOT_4_PI_EPS	1.05482230112e-05
#define AVOGAD		6.0221367e23
#define AMU		1.6605402e-27
#define AMUSTR          "1.6605402e-27"
#define RTAMU           4.07497263794495e-14	/*sqrt(amu) */
#endif
#define RGAS		(AVOGAD*_kB)
/* Program units relative to MKS units ONLY M, L, T, Q UNIT SHOULD BE CHANGED */
#define MUNIT		AMU				/* atomic mass units  */
#define	LUNIT		1.0e-10				/* Angstrom	      */
#define	TUNIT		1.0e-12				/* Picosecond	      */
#define	EUNIT		(MUNIT * (LUNIT/TUNIT) * (LUNIT/TUNIT))
#define	QUNIT		/*sqrt(EUNIT*LUNIT)*/ (RTAMU*1.e-3*_ROOT_4_PI_EPS)
#define MUNIT_N		"amu"
#define LUNIT_N		"A"
#define RLUNIT_N	"A(-1)"
#define	TUNIT_N		"ps"
#define	IUNIT_N		"amuA**2"
#define DIPUNIT_N	"Prog units"
/* Constants in program units						     */
#define	kB		(_kB / EUNIT)
#define EPS0		(0.25 / PI)
/* Output conversion units */
#define	CONV_E		(0.001*AVOGAD*EUNIT)			/* kJ/mol     */
#define	CONV_T		(kB / CONV_E)				/* kB / E unit*/
#define	CONV_P		(MUNIT/(LUNIT*TUNIT*TUNIT)/1.0e6) 	/* MPa	      */
#define	CONV_V		CONV_E
#define	CONV_F		(MUNIT*LUNIT/(TUNIT*TUNIT)*sqrt(AVOGAD))/* N/mol      */
#define	CONV_N		(CONV_F*LUNIT)				/* Nm/mol     */
#define	CONV_D		(QUNIT*LUNIT*4.8e10/_ELCHG)
#define CONV_Q		(QUNIT/_ELCHG)
#define CONV_TM		0.01
#define	CONV_E_N	"kJ/mol"
#define	CONV_T_N	"K"
#define	CONV_P_N	"Mpa"
#define	CONV_F_N	"N**2/mol"
#define	CONV_N_N	"(Nm)**2/mol"
#define	CONV_D_N	"D"
#define CONV_Q_N	"Qe"
#define CONV_TM_N	"kJ/mol*ps**2"


#define	false			0
#define	true			1
typedef	unsigned long int time_mt;/* Larger than any possible time_t */
typedef unsigned long size_mt;    /* Wide type for passing sizeof	      */

#if (defined(ANSI) || defined(__STDC__)) && !defined(vms)
typedef void	gptr;
#else
typedef char	gptr;
#endif

typedef	double			real;
typedef	int			boolean;
typedef enum {inv, noinv} 	invrot;
typedef enum {tke_n, rke_n, pe_n, e_n, tt_n, rt_n, t_n, 
              h0_n, h1_n, h2_n, stress0_n, stress1_n,  stress2_n, press_n, 
              vir_n, msqf_n, msqt_n, dip_n, end} av_n;
           
typedef real    vec_mt[3];
typedef	vec_mt	*vec_mp;
typedef	real	quat_mt[4];
typedef quat_mt	*quat_mp;
typedef real    mat_mt[3][3];
typedef vec_mt	*mat_mp;

#define balloc(n,size)  talloc((int)(n),(size_mt)(size), __LINE__, __FILE__)
#define aalloc(n, type) (type *)balloc((n), sizeof(type))
				       
#define ialloc(n) aalloc(n, int)
#define dalloc(n) aalloc(n, real)
#define ralloc(n) aalloc(n, vec_mt)
#define palloc(n) aalloc(n, vec_mt *)
#define qalloc(n) aalloc(n, quat_mt)

#define	xfree( ptr )	tfree( (gptr *) (ptr) )
#define lsizeof         (size_mt)sizeof

#ifdef ANSI_LIBS
#   define memcp(s1,s2,n) (void)memcpy( (gptr*)(s1), (gptr*)(s2), (size_mt)(n))
#   define memst(s,c,n)   (void)memset( (gptr*)(s), (c), (size_mt)(n))
#else
#   define memcp(s1,s2,n) (void)memcpy( (gptr*)(s1), (gptr*)(s2), (int)(n))
#   define memst(s,c,n)   (void)memset( (gptr*)(s), (c), (int)(n))
#endif
#endif
$EOD
$!
$CREATE string.h
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/*
 * $Header: /home/eeyore_data/keith/md/moldy/RCS/string.h,v 2.8 1996/03/06 16:02:13 keith Exp $
 *
 * $Log: string.h,v $
 * Revision 2.8  1996/03/06 16:02:13  keith
 * Moved tests/#defines of m/c include macros into non-ansi case only.
 *
 * Revision 2.7  1994/06/08 13:22:31  keith
 * Null update for version compatibility
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Added RS6000 macro for inclusion protection.
 *
 * Revision 2.5  94/01/18  13:33:00  keith
 * Null update for XDR portability release
 * 
 * Revision 2.4  94/01/18  13:23:17  keith
 * Incorporated all portability experience to multiple platforms since 2.2.
 * Including ports to VAX/VMS and Open VMS on Alpha AXP and Solaris.
 * 
 * Revision 2.3  93/10/28  10:28:31  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.1  93/07/19  13:28:19  keith
 * Added XDR capability for backup and dump files.
 * 
 * Revision 2.0  93/03/15  14:49:29  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.10  93/03/09  15:59:26  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.9  91/08/16  15:26:17  keith
 * Checked error returns from fread, fwrite, fseek and fclose more
 * rigourously.   Called strerror() to report errors.
 * 
 * Revision 1.8  91/08/15  18:12:24  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_t and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.7  91/03/12  15:43:40  keith
 * Tidied up typedefs size_t and include file 
 * Added explicit function declarations.
 * 
 * Revision 1.6  90/04/14  16:02:06  keith
 * Replaced calls to system headers with own definitions for portability.
 * 
 * 
 */
#ifdef ANSI_LIBS
#   include 
#else

#ifndef __string_h
#ifndef _STRING_H
#ifndef _STRING_H_
#ifndef __STRING_H
#ifndef _H_STRING
#ifndef __STRING_H__
#ifndef _STRING_INCLUDED

#include "stddef.h"

extern char
	*strcat(),
	*strncat(),
        *strcpy(),
        *strncpy(),
	*strchr(),
	*strtok(),
	*strdup(),
	*strstr(),
        *strerror();

extern int
	strcmp();
#ifdef __GNUC__
extern unsigned long
#else
extern int
#endif
	strlen();

extern gptr
	*memcpy(),
	*memset();

#define __string_h
#define _STRING_H
#define _STRING_H_
#define __STRING_H
#define __STRING_H__
#define  _H_STRING
#define _STRING_INCLUDED


#endif
#endif
#endif
#endif
#endif
#endif
#endif
#endif
$EOD
$!
$CREATE time.h
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/*
 *  Time.h   replacement for ANSI one
 */
#ifdef ANSI_LIBS
#include 
#else

#ifndef __time_h
#ifndef _TIME_H
#ifndef _TIME_H_
#ifndef __TIME_H
#ifndef _H_TIME
#ifndef __TIME_H__
#ifndef _TIME_INCLUDED

#include 
#   if defined(unix) || defined(__unix__)
/*
 *  We must protect the inclusion of .  
 */
#      ifndef SYS_TYPES_INCLUDED
#         define SYS_TYPES_INCLUDED
#         include 
#      endif
       extern time_t	time();
       extern char * ctime();
#   endif
#define __time_h
#define _TIME_H
#define _TIME_H_
#define __TIME_H
#define _H_TIME
#define __TIME_H__
#define _TIME_INCLUDED

#endif
#endif
#endif
#endif
#endif
#endif
#endif

#endif





$EOD
$!
$CREATE stddef.h
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */

#ifdef ANSI_LIBS
#   include 
#else
#ifndef __stddef_h
#ifndef _STDDEF_H
#ifndef _STDDEF_H_
#ifndef __STDDEF_H
#ifndef __STDDEF_H__
#ifndef _STDDEF_INCLUDED

#   ifndef NULL
#      define NULL 0
#   endif

#endif
#endif
#endif
#endif
#endif
#endif
#endif





$EOD
$!
$CREATE stdlib.h
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
#ifdef ANSI_LIBS
#   include 
#else

#ifndef __stdlib_h
#ifndef _STDLIB_H
#ifndef _STDLIB_H_
#ifndef __STDLIB_H
#ifndef __STDLIB_H__
#ifndef _H_STDLIB
#ifndef _STDLIB_INCLUDED

#  include "stddef.h"

   extern char * calloc();
   extern char * malloc();
   extern char * realloc();

   extern void free ();
   extern void exit ();
   extern int abs ();
   extern int atoi ();
   extern long strtol();
#define __stdlib_h
#define __STDLIB_H__
#define __STDLIB_H
#define _STDLIB_H_
#define _STDLIB_H
#define  _H_STDLIB
#define _STDLIB_INCLUDED

#endif /* __STDLIB_H__ */
#endif /* __STDLIB_H__ */
#endif /* _STDLIB_H_ */
#endif /* _STDLIB_H_ */
#endif /* _STDLIB_H_ */
#endif /* _STDLIB_H_ */
#endif /* _STDLIB_H_ */

#endif





$EOD
$!
$CREATE messages.h
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/*
 * $Header: /home/eeyore_data/keith/md/moldy/RCS/messages.h,v 2.14 1996/09/02 21:45:42 keith Exp $
 */

#ifndef SYSRD   /* Skip if already defined				      */

		/* Severities to be passed to 'message'.		      */
#define		INFO		0
#define		WARNING		1
#define		ERROR		2
#define		FATAL		3
#define	NULLP		(char*)0
#define	NULLI		(int*)0

#define	BACKUP	"Simulation restarted from backup file %s"
#define RNFAIL	"Rename to \"%s\" failed -- file left in \"%s\"\n    -  %s"
#define SYSRD	"reading system specification file"
#define TOMANY	"Too many species in system specification file"
#define NOSPEC	"failed to read any molecular species info"
#define NONUM	"number of molecules of %s not specified"
#define NOMOLS	"invalid number (%d) of molecules of %s"
#define NOSITE	"invalid number (%d) of sites on molecule %s"
#define INVSID	"invalid site id %d"
#define NCONF	"site %d - name conflicts with previously set value %s"
#define CCONF	"site %d - charge conflicts with previously set value %f"
#define MCONF	"site %d - mass conflicts with previously set value %f"
#define	INVMAS	"site %d - negative value (%g) supplied for mass"
#define MISSCO	"only %d site co-ordinates specified for site %d"
#define BROKEN	"internal error - sscanf returned %d items"
#define NOMASS	"no mass specified for site id %d"
#define NOCGRG	"no charge specified for site id %d"
#define NONAME	"no name specified for site id %d"
#define NOTUSD	"unused site identifier %d"
#define	UNKPOT	"unknown type of potential %s (lennard-jones, buckingham, mcy)"
#define NOPAIR	"site id pair is required"
#define NOPOTP	"insufficient potential parameters supplied (%d needed)"
#define IDOUTR	"site id %d out of range"
#define EXTPOT	"potentials specified for unused sites - ignored"
#define DUPPOT	"potentials already set for this site pair"
#define NOPOT	"no potential parameters given between sites %d and %d"
#define ERRS	"system specification file contains %d errors"
#define SUCCES	"system specification file successfully read in"
#define LATTIC	"system successfully initialised from lattice start"
#define	REFORM	"wrong length record in restart file at byte %ld\
 - found %lu, expected %lu"
#define	REREAD	"read error on restart file at byte %ld\n    -  %s"
#define	REEOF	"unexpected end of file reading restart file"
#define	REWRT	"write error on restart file\n    -  %s"
#define	NOVAL	"no value associated with name \"%s\""
#define	NOTFND	"keyword \"%s\" not found"
#define	BADVAL	"value \"%s\" is wrong type for keyword \"%s\""
#define	ERRCON	"control file contains %d error%c"
#define SUCCON  "control file read in successfully"
#define	BADUNI	"Overflow during conversion of units - ln(scale) = %f"
#define	ZMASS	"Mass of %s molecule (%f) is less than 1 amu"
#define	OCFAIL	"Failed to open file \"%s\" for reading control info"
#define ODFAIL	"Failed to open file \"%s\" for reading system specification\n    -  %s"
#define ORFAIL	"Failed to open file \"%s\" for reading restart configuration\n    -  %s"
#define OSFAIL	"Failed to open file \"%s\" for writing restart configuration\n    -  %s"
#define	OOFAIL	"Failed to open file \"%s\" as main output file\n    -  %s"
#define SEFAIL  "Rewind or seek failed on file  \"%s\"\n    -  %s"
#define	RESUCC	"restart file \"%s\" successfully read in"
#define	NEWTS	"Interpolating accelerations from old timestep %g to new %g"
#define ZEROTS  "Cannot interpolate from old timestep of 0 (to new of %gps)"
#define	NSPCON	"Number of species in sysdef file (%d) and restart file (%d)\
must be the same"
#define	NMLCON	"Number of molecules of %s in sysdef and restart files must\
be the same (%d vs %d)"
#define	NDFCON	"Molecules of %s in sysdef and restart files must have same \
rotational degrees of freedom (%d vs %d)"
#define CROWDED "Cells too large - multiple occupation (mol=%d, cell=%d)"
#define NABORS  "Neighbour list contains %d cells"
#define TONAB	"Too many sites (%d) for neighbour list arrays (%d slots) \n\
  -  Increase NMULT in \"force.c\""
#define CUTOFF  "Cutoff radius > %d * cell dimension.\n\
  -  Increase NSH in \"force.c\""
#define CUTRDF  "RDF limit > %d * cell dimension.\n\
  -  Increase NSH in \"force.c\""
#define TOOCLS	"Sites %d and %d closer than %fA."
#define TOODIM  "Arralloc request for %d dimensions - max %d"
#define INSIDE  "Array bounds [%d...%d] inside out (from ARRALLOC)"
#define UNKPTY  "KERNEL called with unkown potential type %d"
#define NOCELL  "Not enough values to specify unit cell\n\
     expected 3 lengths, 3 angles and number of unit cells in MD cell."
#define INVCEL  "Invalid unit cell parameters - must be +ve and angles < 180"
#define UNKSPE  "\"%s\" is not recognised as a molecular species"
#define	FEWCOO  "Too few co-ordinates for species \"%s\" - 3 needed"
#define	FEWQUA  "Too few quaternions for species \"%s\" - 4 needed"
#define FRACCO  "Fractional co-ordinates must be in range [0,1) - (%f,%f,%f)"
#define QNORM   "Quaternion (%f,%f,%f,%f) is not normalised"
#define QNORM2  "Quaternion %d (%f,%f,%f,%f) - normalisation error in beeman"
#define QCONST  "Quaternion %d - constraint error (%g)"
#define NIMOLS  "Wrong number of molecules of %s - given %d, expected %d"
#define INITER  "Initialisation file contains %d errors"
#define ROTLEN  "Length (%d) not a multiple of quaternion number (%d) in \
\"rotate\""
#define OVRLAP  "%s - Result matrix overlaps input"
#define OVRLP1  "mat_vec_mul - Input and output vectors overlap\
(in=%x, out=%x, len=%x)"
#define SNGMAT  "%s - matrix is singular"
#define AVNOC   "%s - init_averages has not been called"
#define AVBNDS  "add_average - offset (%d) out of bounds for type %s"
#define NEGVAR  "%s - variance < 0 (%f) for type %s"
#define NOMEM   "Memory allocation fails at line %d in \"%s\"\
(%d items of %lu bytes)"
#define	DUMPST	"Started dumping data to file \"%s\" at timestep %ld"
#define CONTIG	"Dump file \"%s\" and restart file do not match"
#define DUMPTS  "Dump file(\"%s\")'s timestep (%ld) does not match current timestep (%ld)."
#define DRESET  "Problems prevent continuing existing dump.  Starting new sequence."
#define	SHTDMP	"Records missing from dump file \"%s\" - found %d, expected %d"
#define LNGDMP	"Extra records in dump file \"%s\" - found %d, expected %d"
#define	CORUPT	"Dump file \"%s\" corrupt - expected %d bytes, found %d"
#define DOERRR	"Failed to open dump file \"%s\"\n    -  %s"
#define DRERR	"Read from dump file \"%s\" failed\n    -  %s"
#define DMPALT	"Dump-level altered, new dump run started"
#define DMPEXS	"File \"%s\" exists - Dumps will be written to \"%s\""
#define DOERRW	"Failed to open dump file \"%s\" for writing\n    -  %s"
#define DWERR	"Write to dump file \"%s\" failed\n    -  %s"
#define MUFAIL  "Unable to mutate dump file name \"%s\" (in %d attempts)\n"
#define UNKEY   "Unknown keyword \"%s\""
#define	SYSCHG	"System has net electric charge of %.2g - \
correction of %g kJmol(-1) added to self energy"
#define	FRACHG	"Framework has net electric charge of %.2g - \
correction of %g kJmol(-1) added to self energy"
#define WDPTR	"Arralloc called for object with size (%ul) not an integral\
number of words"
#define NCNVRG  "Do_step: velocities failed to converge after %d iterations \
- dist = %f"
#define INRVSN	"Values.c:init_averages: Invalid RCS version \"%s\"\n"
#define CPOTFL  "Not enough space to store potential parameters from restart\
 file.  Increase NPOTP in \"defs.h\" from %d to at least %d and recompile"
#define LOCKED   "Another instance of MOLDY is accessing the same %s files.\n\
     Delete lockfile \"%s\" if the previous run crashed."
#define LOCKFL  "Failed to create lockfile \"%s\""
#define XDRFL	"XDR call failed"
#define GPFAIL  "Get file position failed on file  \"%s\"\n    -  %s"
#endif
#define SYSEOF	"Unexpected end of file encountered while reading %s"
#define MAXCUT  "Optimum cutoff of %2.2f A is larger than allowed maximum \
of %2.2f.\n Maximum will be used but accuracy of Ewald sum is compromised."
#define GANDP   "Application of Parinello-Rahman algorithm along with \
Gaussian thermostat would interfere with each other."
#define	NOKEY	"no keyword found on line"
#define NOCUT   "No real-space cutoff specified or cutoff invalid"
#define BODGUP  "Reading bad 2.10 restart file - thermostat parameters invalid.\
\n     Setting ttmass and rtmass to %f and const-temp=0"
#define DESYNC  "Trajectories on parallel threads are diverging.\n     Thread %d: %20.17g != %20.17g"
$EOD
$!
$CREATE xdr.h
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */

/******************************************************************************
 * xdr  Moldy-specific xdr routines for storing binary data in machine-       *
 *      independent format.	For compatibility with existing binary 	      *
 *      formats, strings are stored as fixed-length opaque data.	      *
 ******************************************************************************
 *      Revision Log
 *       $Log: xdr.h,v $
 *       Revision 2.9  1996/09/25 16:29:12  keith
 *       Fixed restart structure correctly - broken in prev version.
 *       Thermostat parameters may not be properly read.
 *
 *       Revision 2.8  1996/03/07 15:01:57  keith
 *       Made "malloc in types.h" macro protection conditional on "ANSI_LIBS"
 *
 *       Revision 2.7  1994/06/08 13:22:31  keith
 *       Null update for version compatibility
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Revision 2.5.1.1  1994/02/03  18:36:12  keith
 * Tidied up and got rid of most of the global data items.
 *
 * Revision 2.5  94/01/18  17:35:45  keith
 * Incorporated all portability experience to multiple platforms since 2.2.
 * Including ports to VAX/VMS and Open VMS on Alpha AXP and Solaris.
 * 
 * Revision 2.5  94/01/18  13:33:08  keith
 * Null update for XDR portability release
 * 
 * Revision 2.4  94/01/18  13:23:19  keith
 * Incorporated all portability experience to multiple platforms since 2.2.
 * Including ports to VAX/VMS and Open VMS on Alpha AXP and Solaris.
 * 
 * Revision 2.3  93/10/14  18:18:16  keith
 * Fixed prortability problems to IBM RS6000
 * 
 * Revision 2.2  93/09/06  14:42:47  keith
 * Fixed portability problems/bugs in XDR code.
 * 
 * Revision 2.1  93/07/19  13:29:47  keith
 * Support for XDR backup/dump routines.
 * 
 */
#ifndef lint
static char *RCSidh = "$Header: /home/eeyore_data/keith/md/moldy/RCS/xdr.h,v 2.9 1996/09/25 16:29:12 keith Exp $";
#endif
/*========================== Library include files ===========================*/
#ifdef USE_XDR

/*
 * Some  descended from Sun's original include a declaration
 * of "malloc" in an unprotected fashion.  Try to define it out of the
 * way -- include "stdlib.h" if necessary to put it back.
 * In case an implementation (eg SGI) does it right by including 
 * ensure that any Moldy module includes "stdlib.h" *before* "xdr.h".
*/
#ifndef ANSI_LIBS
#define free xxfree
#define exit xxexit
#define malloc xxmalloc
#define calloc xxcalloc
#define realloc xxrealloc
#endif

#ifdef vms
#include	"rpc_types.h"
#include	"rpc_xdr.h"
#else
#include	"time.h"
#include	
#include	
#endif


#ifndef ANSI_LIBS
#undef free
#undef exit
#undef malloc
#undef calloc
#undef realloc
#endif

#else
typedef	char XDR;
typedef int bool_t;
typedef bool_t (*xdrproc_t)();
#endif
/*============================================================================*/

bool_t xdr_real();
bool_t xdr_contr();
bool_t xdr_system();
bool_t xdr_system_2();
bool_t xdr_species();
bool_t xdr_site();
void   xdr_set_npotpar();
bool_t xdr_pot();
bool_t xdr_restrt();
bool_t xdr_dump();
void   xdr_set_av_size_conv();
bool_t xdr_averages();

#ifndef USE_XDR
bool_t	xdr_int();
bool_t  xdr_bool();
#endif

#define XDR_INT_SIZE 4
#define XDR_4PTR_SIZE 4
#define XDR_ULONG_SIZE 4
#define XDR_FLOAT_SIZE 4
#define XDR_DOUBLE_SIZE 8
#define XDR_REAL_SIZE ( (sizeof(real)==sizeof(double))?XDR_DOUBLE_SIZE:XDR_FLOAT_SIZE)

#define XDR_RESTRT_SIZE  (2*XDR_ULONG_SIZE+(DLEN)+(L_name)+16+XDR_INT_SIZE)
#define XDR_DUMP_SIZE    ((L_name)+16+6*XDR_INT_SIZE+3*XDR_ULONG_SIZE)
$EOD
$!
$CREATE control.water
$DECK
#
# N.B. This file conians an error but is left for backward compatibility
# The tips2.in potentials are in units of kcal/mol so this file should
# set time-unit=4.8888213e-14 #Kcal mol-1 amu A
#
title=Water_test
surface-dipole=1
#out-file=out.water.list
temperature=300
subcell=2.5
density=1
scale-interval=10
scale-end=500
backup-interval=100
average-interval=1000
sys-spec-file=tips2.in
page-length=44
step=0.0005
nsteps=10
print-interval=10
begin-rdf=1000
rdf-interval=0
rdf-out=4000
rdf-limit=10
cutoff=6.25
k-cutoff = 3
alpha = 0.45
dump-file = 
dump-level=0
ndumps=3
dump-interval=0
begin-dump=10000
restart-file =
save-file=
rdf-limit=9
end
$EOD
$!
$CREATE control.tip4p
$DECK
title=Example TIP4P Simulation
surface-dipole=1
temperature=300
subcell=2.5
density=1
scale-interval=10
scale-end=500
average-interval=1000
sys-spec-file=tip4p.in
page-length=44
step=0.0005
nsteps=1000
print-interval=100
dump-level=1
dump-file=tip4p.dmp
dump-interval=10
ndumps=10000
save-file=tip4p.mds
time-unit=4.8888213e-14 #Kcal mol-1 amu A
end
$EOD
$!
$CREATE control.mgclh2o
$DECK
title=Magnesium Chloride Solution
sys-spec-file=mgclh2o.in
step=0
nsteps=1
print-interval=1
density=1
cutoff=6.25
k-cutoff = 3
alpha = 0.45
text-mode-save=1
save-file=mdsave.mgclh2o
#restart-file=mdsave.water.1
time-unit=4.8888213e-14
end
$EOD
$!
$CREATE control.clay
$DECK
title = 4 Mg 64 H2O Otay-montmorillonite run 1
nsteps = 01
step = 0.0
text-mode-save = 0
new-sys-spec = 0
surface-dipole = 0
lattice-start = 1
sys-spec-file = 
restart-file = 
save-file = 
strain-mask = 200
const-pressure = 0
reset-averages = 0
scale-end = 10000
begin-average = 10001
average-interval = 20000
begin-dump = 10001
dump-interval = 20
dump-level = 0
ndumps = 250
backup-interval = 500
roll-interval = 100
print-interval = 1
begin-rdf = 40000
rdf-interval = 20
rdf-out = 30000
temperature = 300
pressure = 0
w = 100
cutoff = 12.6
strict-cutoff=0
subcell = 1.8
density = 1
alpha = 0.35
k-cutoff = 2
rdf-limit = 10
cpu-limit = 1e+20
mass-unit = 1.660565e-27
length-unit = 1e-10
time-unit = 1e-12
charge-unit = 4.298401e-22
end
 water             64  
      1   0.75695         0 -0.520325    1.0079   267.435 H
      1  -0.75695         0 -0.520325    1.0079   267.435 H
      2         0         0 0.0655569   15.9994         0 O
      3         0         0 -0.202143         0  -534.871 Charge
 cation            4  
      4         0         0         0     24.31   745.481 Ni
 clay              1  framework
      5  -7.16606     -9.14      3.28   15.9994   -298.22 O
      5  -8.48606     -6.86      3.28   15.9994   -298.22 O
      5  -5.84606     -6.86      3.28   15.9994   -298.22 O
      6  -9.80606     -9.14      1.06   15.9994   -267.44 O
      7  -9.80606     -9.14    2.0176    1.0079    267.44 H
      8  -7.16606     -7.62      2.73    28.086    447.33 Si
      8  -9.80606     -6.09      2.73    28.086    447.33 Si
      9  -8.04606     -9.14         0         0   -372.74 Vacancy
      5  -9.80606     -4.57      3.28   15.9994   -298.22 O
      5  -5.84606     -2.29      3.28   15.9994   -298.22 O
      5  -8.48606     -2.29      3.28   15.9994   -298.22 O
      6  -7.16606     -4.57      1.06   15.9994   -267.44 O
      7  -7.16606     -4.57    2.0176    1.0079    267.44 H
      8  -9.80606     -3.05      2.73    28.086    447.33 Si
      8  -7.16606     -1.52      2.73    28.086    447.33 Si
     10  -5.40606     -4.57         0    24.305         0 Mg
      5  -8.92606 8.88178e-15     -3.28   15.9994   -298.22 O
      5  -7.60606     -2.28     -3.28   15.9994   -298.22 O
      5  -10.2461     -2.28     -3.28   15.9994   -298.22 O
      6  -6.28606 8.88178e-15     -1.06   15.9994   -267.44 O
      7  -6.28606 8.88178e-15   -2.0176    1.0079    267.44 H
      8  -8.92606     -1.52     -2.73    28.086    447.33 Si
      8  -6.28606     -3.05     -2.73    28.086    447.33 Si
      5  -6.28606     -4.57     -3.28   15.9994   -298.22 O
      5  -10.2461     -6.85     -3.28   15.9994   -298.22 O
      5  -7.60606     -6.85     -3.28   15.9994   -298.22 O
      6  -8.92606     -4.57     -1.06   15.9994   -267.44 O
      7  -8.92606     -4.57   -2.0176    1.0079    267.44 H
      8  -6.28606     -6.09     -2.73    28.086    447.33 Si
      8  -8.92606     -7.62     -2.73    28.086    447.33 Si
      5  -7.16606 8.88178e-15      3.28   15.9994   -298.22 O
      5  -8.48606      2.28      3.28   15.9994   -298.22 O
      5  -5.84606      2.28      3.28   15.9994   -298.22 O
      6  -9.80606 8.88178e-15      1.06   15.9994   -267.44 O
      7  -9.80606 8.88178e-15    2.0176    1.0079    267.44 H
      8  -7.16606      1.52      2.73    28.086    447.33 Si
      8  -9.80606      3.05      2.73    28.086    447.33 Si
      9  -8.04606 8.88178e-15         0         0   -372.74 Vacancy
      5  -9.80606      4.57      3.28   15.9994   -298.22 O
      5  -5.84606      6.85      3.28   15.9994   -298.22 O
      5  -8.48606      6.85      3.28   15.9994   -298.22 O
      6  -7.16606      4.57      1.06   15.9994   -267.44 O
      7  -7.16606      4.57    2.0176    1.0079    267.44 H
      8  -9.80606      6.09      2.73    28.086    447.33 Si
      8  -7.16606      7.62      2.73    28.086    447.33 Si
     10  -5.40606      4.57         0    24.305         0 Mg
      5  -8.92606      9.14     -3.28   15.9994   -298.22 O
      5  -7.60606      6.86     -3.28   15.9994   -298.22 O
      5  -10.2461      6.86     -3.28   15.9994   -298.22 O
      6  -6.28606      9.14     -1.06   15.9994   -267.44 O
      7  -6.28606      9.14   -2.0176    1.0079    267.44 H
      8  -8.92606      7.62     -2.73    28.086    447.33 Si
      8  -6.28606      6.09     -2.73    28.086    447.33 Si
      5  -6.28606      4.57     -3.28   15.9994   -298.22 O
      5  -10.2461      2.29     -3.28   15.9994   -298.22 O
      5  -7.60606      2.29     -3.28   15.9994   -298.22 O
      6  -8.92606      4.57     -1.06   15.9994   -267.44 O
      7  -8.92606      4.57   -2.0176    1.0079    267.44 H
      8  -6.28606      3.05     -2.73    28.086    447.33 Si
      8  -8.92606      1.52     -2.73    28.086    447.33 Si
      5  -1.88606     -9.14      3.28   15.9994   -298.22 O
      5  -3.20606     -6.86      3.28   15.9994   -298.22 O
      5 -0.566058     -6.86      3.28   15.9994   -298.22 O
      6  -4.52606     -9.14      1.06   15.9994   -267.44 O
      7  -4.52606     -9.14    2.0176    1.0079    267.44 H
      8  -1.88606     -7.62      2.73    28.086    447.33 Si
      8  -4.52606     -6.09      2.73    28.086    447.33 Si
      9  -2.76606     -9.14         0         0   -372.74 Vacancy
      5  -4.52606     -4.57      3.28   15.9994   -298.22 O
      5 -0.566058     -2.29      3.28   15.9994   -298.22 O
      5  -3.20606     -2.29      3.28   15.9994   -298.22 O
      6  -1.88606     -4.57      1.06   15.9994   -267.44 O
      7  -1.88606     -4.57    2.0176    1.0079    267.44 H
      8  -4.52606     -3.05      2.73    28.086    447.33 Si
      8  -1.88606     -1.52      2.73    28.086    447.33 Si
     10 -0.126058     -4.57         0    24.305         0 Mg
      5  -3.64606 8.88178e-15     -3.28   15.9994   -298.22 O
      5  -2.32606     -2.28     -3.28   15.9994   -298.22 O
      5  -4.96606     -2.28     -3.28   15.9994   -298.22 O
      6  -1.00606 8.88178e-15     -1.06   15.9994   -267.44 O
      7  -1.00606 8.88178e-15   -2.0176    1.0079    267.44 H
      8  -3.64606     -1.52     -2.73    28.086    447.33 Si
      8  -1.00606     -3.05     -2.73    28.086    447.33 Si
      5  -1.00606     -4.57     -3.28   15.9994   -298.22 O
      5  -4.96606     -6.85     -3.28   15.9994   -298.22 O
      5  -2.32606     -6.85     -3.28   15.9994   -298.22 O
      6  -3.64606     -4.57     -1.06   15.9994   -267.44 O
      7  -3.64606     -4.57   -2.0176    1.0079    267.44 H
      8  -1.00606     -6.09     -2.73    28.086    447.33 Si
      8  -3.64606     -7.62     -2.73    28.086    447.33 Si
      5  -1.88606 8.88178e-15      3.28   15.9994   -298.22 O
      5  -3.20606      2.28      3.28   15.9994   -298.22 O
      5 -0.566058      2.28      3.28   15.9994   -298.22 O
      6  -4.52606 8.88178e-15      1.06   15.9994   -267.44 O
      7  -4.52606 8.88178e-15    2.0176    1.0079    267.44 H
      8  -1.88606      1.52      2.73    28.086    447.33 Si
      8  -4.52606      3.05      2.73    28.086    447.33 Si
      9  -2.76606 8.88178e-15         0         0   -372.74 Vacancy
      5  -4.52606      4.57      3.28   15.9994   -298.22 O
      5 -0.566058      6.85      3.28   15.9994   -298.22 O
      5  -3.20606      6.85      3.28   15.9994   -298.22 O
      6  -1.88606      4.57      1.06   15.9994   -267.44 O
      7  -1.88606      4.57    2.0176    1.0079    267.44 H
      8  -4.52606      6.09      2.73    28.086    447.33 Si
      8  -1.88606      7.62      2.73    28.086    447.33 Si
     10 -0.126058      4.57         0    24.305         0 Mg
      5  -3.64606      9.14     -3.28   15.9994   -298.22 O
      5  -2.32606      6.86     -3.28   15.9994   -298.22 O
      5  -4.96606      6.86     -3.28   15.9994   -298.22 O
      6  -1.00606      9.14     -1.06   15.9994   -267.44 O
      7  -1.00606      9.14   -2.0176    1.0079    267.44 H
      8  -3.64606      7.62     -2.73    28.086    447.33 Si
      8  -1.00606      6.09     -2.73    28.086    447.33 Si
      5  -1.00606      4.57     -3.28   15.9994   -298.22 O
      5  -4.96606      2.29     -3.28   15.9994   -298.22 O
      5  -2.32606      2.29     -3.28   15.9994   -298.22 O
      6  -3.64606      4.57     -1.06   15.9994   -267.44 O
      7  -3.64606      4.57   -2.0176    1.0079    267.44 H
      8  -1.00606      3.05     -2.73    28.086    447.33 Si
      8  -3.64606      1.52     -2.73    28.086    447.33 Si
      5   3.39394     -9.14      3.28   15.9994   -298.22 O
      5   2.07394     -6.86      3.28   15.9994   -298.22 O
      5   4.71394     -6.86      3.28   15.9994   -298.22 O
      6  0.753942     -9.14      1.06   15.9994   -267.44 O
      7  0.753942     -9.14    2.0176    1.0079    267.44 H
      8   3.39394     -7.62      2.73    28.086    447.33 Si
      8  0.753942     -6.09      2.73    28.086    447.33 Si
      9   2.51394     -9.14         0         0   -372.74 Vacancy
      5  0.753942     -4.57      3.28   15.9994   -298.22 O
      5   4.71394     -2.29      3.28   15.9994   -298.22 O
      5   2.07394     -2.29      3.28   15.9994   -298.22 O
      6   3.39394     -4.57      1.06   15.9994   -267.44 O
      7   3.39394     -4.57    2.0176    1.0079    267.44 H
      8  0.753942     -3.05      2.73    28.086    447.33 Si
      8   3.39394     -1.52      2.73    28.086    447.33 Si
     10   5.15394     -4.57         0    24.305         0 Mg
      5   1.63394 8.88178e-15     -3.28   15.9994   -298.22 O
      5   2.95394     -2.28     -3.28   15.9994   -298.22 O
      5  0.313942     -2.28     -3.28   15.9994   -298.22 O
      6   4.27394 8.88178e-15     -1.06   15.9994   -267.44 O
      7   4.27394 8.88178e-15   -2.0176    1.0079    267.44 H
      8   1.63394     -1.52     -2.73    28.086    447.33 Si
      8   4.27394     -3.05     -2.73    28.086    447.33 Si
      5   4.27394     -4.57     -3.28   15.9994   -298.22 O
      5  0.313942     -6.85     -3.28   15.9994   -298.22 O
      5   2.95394     -6.85     -3.28   15.9994   -298.22 O
      6   1.63394     -4.57     -1.06   15.9994   -267.44 O
      7   1.63394     -4.57   -2.0176    1.0079    267.44 H
      8   4.27394     -6.09     -2.73    28.086    447.33 Si
      8   1.63394     -7.62     -2.73    28.086    447.33 Si
      5   3.39394 8.88178e-15      3.28   15.9994   -298.22 O
      5   2.07394      2.28      3.28   15.9994   -298.22 O
      5   4.71394      2.28      3.28   15.9994   -298.22 O
      6  0.753942 8.88178e-15      1.06   15.9994   -267.44 O
      7  0.753942 8.88178e-15    2.0176    1.0079    267.44 H
      8   3.39394      1.52      2.73    28.086    447.33 Si
      8  0.753942      3.05      2.73    28.086    447.33 Si
      9   2.51394 8.88178e-15         0         0   -372.74 Vacancy
      5  0.753942      4.57      3.28   15.9994   -298.22 O
      5   4.71394      6.85      3.28   15.9994   -298.22 O
      5   2.07394      6.85      3.28   15.9994   -298.22 O
      6   3.39394      4.57      1.06   15.9994   -267.44 O
      7   3.39394      4.57    2.0176    1.0079    267.44 H
      8  0.753942      6.09      2.73    28.086    447.33 Si
      8   3.39394      7.62      2.73    28.086    447.33 Si
     10   5.15394      4.57         0    24.305         0 Mg
      5   1.63394      9.14     -3.28   15.9994   -298.22 O
      5   2.95394      6.86     -3.28   15.9994   -298.22 O
      5  0.313942      6.86     -3.28   15.9994   -298.22 O
      6   4.27394      9.14     -1.06   15.9994   -267.44 O
      7   4.27394      9.14   -2.0176    1.0079    267.44 H
      8   1.63394      7.62     -2.73    28.086    447.33 Si
      8   4.27394      6.09     -2.73    28.086    447.33 Si
      5   4.27394      4.57     -3.28   15.9994   -298.22 O
      5  0.313942      2.29     -3.28   15.9994   -298.22 O
      5   2.95394      2.29     -3.28   15.9994   -298.22 O
      6   1.63394      4.57     -1.06   15.9994   -267.44 O
      7   1.63394      4.57   -2.0176    1.0079    267.44 H
      8   4.27394      3.05     -2.73    28.086    447.33 Si
      8   1.63394      1.52     -2.73    28.086    447.33 Si
      5   8.67394     -9.14      3.28   15.9994   -298.22 O
      5   7.35394     -6.86      3.28   15.9994   -298.22 O
      5   9.99394     -6.86      3.28   15.9994   -298.22 O
      6   6.03394     -9.14      1.06   15.9994   -267.44 O
      7   6.03394     -9.14    2.0176    1.0079    267.44 H
      8   8.67394     -7.62      2.73    28.086    447.33 Si
      8   6.03394     -6.09      2.73    28.086    447.33 Si
      9   7.79394     -9.14         0         0   -372.74 Vacancy
      5   6.03394     -4.57      3.28   15.9994   -298.22 O
      5   9.99394     -2.29      3.28   15.9994   -298.22 O
      5   7.35394     -2.29      3.28   15.9994   -298.22 O
      6   8.67394     -4.57      1.06   15.9994   -267.44 O
      7   8.67394     -4.57    2.0176    1.0079    267.44 H
      8   6.03394     -3.05      2.73    28.086    447.33 Si
      8   8.67394     -1.52      2.73    28.086    447.33 Si
     10   10.4339     -4.57         0    24.305         0 Mg
      5   6.91394 8.88178e-15     -3.28   15.9994   -298.22 O
      5   8.23394     -2.28     -3.28   15.9994   -298.22 O
      5   5.59394     -2.28     -3.28   15.9994   -298.22 O
      6   9.55394 8.88178e-15     -1.06   15.9994   -267.44 O
      7   9.55394 8.88178e-15   -2.0176    1.0079    267.44 H
      8   6.91394     -1.52     -2.73    28.086    447.33 Si
      8   9.55394     -3.05     -2.73    28.086    447.33 Si
      5   9.55394     -4.57     -3.28   15.9994   -298.22 O
      5   5.59394     -6.85     -3.28   15.9994   -298.22 O
      5   8.23394     -6.85     -3.28   15.9994   -298.22 O
      6   6.91394     -4.57     -1.06   15.9994   -267.44 O
      7   6.91394     -4.57   -2.0176    1.0079    267.44 H
      8   9.55394     -6.09     -2.73    28.086    447.33 Si
      8   6.91394     -7.62     -2.73    28.086    447.33 Si
      5   8.67394 8.88178e-15      3.28   15.9994   -298.22 O
      5   7.35394      2.28      3.28   15.9994   -298.22 O
      5   9.99394      2.28      3.28   15.9994   -298.22 O
      6   6.03394 8.88178e-15      1.06   15.9994   -267.44 O
      7   6.03394 8.88178e-15    2.0176    1.0079    267.44 H
      8   8.67394      1.52      2.73    28.086    447.33 Si
      8   6.03394      3.05      2.73    28.086    447.33 Si
      9   7.79394 8.88178e-15         0         0   -372.74 Vacancy
      5   6.03394      4.57      3.28   15.9994   -298.22 O
      5   9.99394      6.85      3.28   15.9994   -298.22 O
      5   7.35394      6.85      3.28   15.9994   -298.22 O
      6   8.67394      4.57      1.06   15.9994   -267.44 O
      7   8.67394      4.57    2.0176    1.0079    267.44 H
      8   6.03394      6.09      2.73    28.086    447.33 Si
      8   8.67394      7.62      2.73    28.086    447.33 Si
     10   10.4339      4.57         0    24.305         0 Mg
      5   6.91394      9.14     -3.28   15.9994   -298.22 O
      5   8.23394      6.86     -3.28   15.9994   -298.22 O
      5   5.59394      6.86     -3.28   15.9994   -298.22 O
      6   9.55394      9.14     -1.06   15.9994   -267.44 O
      7   9.55394      9.14   -2.0176    1.0079    267.44 H
      8   6.91394      7.62     -2.73    28.086    447.33 Si
      8   9.55394      6.09     -2.73    28.086    447.33 Si
      5   9.55394      4.57     -3.28   15.9994   -298.22 O
      5   5.59394      2.29     -3.28   15.9994   -298.22 O
      5   8.23394      2.29     -3.28   15.9994   -298.22 O
      6   6.91394      4.57     -1.06   15.9994   -267.44 O
      7   6.91394      4.57   -2.0176    1.0079    267.44 H
      8   9.55394      3.05     -2.73    28.086    447.33 Si
      8   6.91394      1.52     -2.73    28.086    447.33 Si
 end
 mcy potential parameters
      1      1    278795   2.76084         0         0
      1      2    608950    2.9619    114472   2.23326
      1      3         0         0         0         0
      1      4    310472    1.6807         0         2
      1      5    608950    2.9619    114472   2.23326
      1      6    608950    2.9619    114472   2.23326
      1      7    278795   2.76084         0         0
      1      8    241513   2.15646   894.111   1.21767
      1      9         0         0         0         0
      1     10         0         0         0         0
      2      2 4.55308e+08   5.15271         0         0
      2      3         0         0         0         0
      2      4 4.92339e+06   2.39325 2.84573e+06   2.10376
      2      5 4.55308e+08   5.15271         0         0
      2      6 4.55308e+08   5.15271         0         0
      2      7    608950    2.9619    114472   2.23326
      2      8 5.465e+06   3.20367    563073   2.26705
      2      9         0         0         0         0
      2     10         0         0         0         0
      3      3         0         0         0         0
      3      4         0         0         0         0
      3      5         0         0         0         0
      3      6         0         0         0         0
      3      7         0         0         0         0
      3      8         0         0         0         0
      3      9         0         0         0         0
      3     10         0         0         0         0
      4      4         0         0         0         0
      4      5 4.92339e+06   2.39325 2.84573e+06   2.10376
      4      6 4.92339e+06   2.39325 2.84573e+06   2.10376
      4      7    310472    1.6807         0         2
      4      8 2.90316e+06   2.00923 7.80092e+07    4.8272
      4      9         0         0         0         0
      4     10         0         0         0         0
      5      5 4.55308e+08   5.15271         0         0
      5      6 4.55308e+08   5.15271         0         0
      5      7    608950    2.9619    114472   2.23326
      5      8 5.465e+06   3.20367    563073   2.26705
      5      9         0         0         0         0
      5     10         0         0         0         0
      6      6 4.55308e+08   5.15271         0         0
      6      7    608950    2.9619    114472   2.23326
      6      8 5.465e+06   3.20367    563073   2.26705
      6      9         0         0         0         0
      6     10         0         0         0         0
      7      7    278795   2.76084         0         0
      7      8    241513   2.15646   894.111   1.21767
      7      9         0         0         0         0
      7     10         0         0         0         0
      8      8         0         0         0         0
      8      9         0         0         0         0
      8     10         0         0         0         0
      9      9         0         0         0         0
      9     10         0         0         0         0
     10     10         0         0         0         0
 end
21.12 18.28 14.6846 90 90 90 1 1 1
water 0.759832 0.322085 0.579316 0.553836 -0.245817 -0.695562 -0.38605
water 0.223053 0.4935 0.404179 0.0254173 0.28349 0.232883 -0.929921
water 0.889842 0.86648 0.581554 -0.388827 0.490987 0.754408 0.196502
water 0.745392 0.561496 0.436654 -0.796572 -0.335381 0.349554 -0.361668
water 0.319489 0.758783 0.418008 0.546379 -0.380393 0.25875 0.699871
water 0.624505 0.542736 0.390583 0.330871 0.32724 0.123698 0.876435
water 0.105252 0.216243 0.499404 0.403523 -0.205568 -0.602575 -0.657126
water 0.849922 0.0198829 0.407292 0.67912 0.0729964 -0.479315 0.551113
water 0.844278 0.25793 0.414067 -0.898672 -0.257052 0.0919572 -0.343302
water 0.34612 0.494503 0.418877 -0.750894 0.00243009 0.431425 -0.500024
water 0.488573 0.444088 0.427095 0.644461 -0.177315 -0.149875 0.728537
water 0.988289 0.710787 0.610797 -0.197612 0.847297 -0.477572 -0.12232
water 0.260721 0.111984 0.586134 0.0641008 0.919175 -0.180528 -0.344119
water 0.460344 0.13135 0.42775 -0.802078 -0.253715 -0.539999 -0.0264657
water 0.796052 0.97418 0.583112 0.111393 0.768994 -0.22146 -0.589233
water 0.738605 0.483402 0.612498 -0.126574 -0.631955 0.614351 0.455176
water 0.101785 0.431662 0.409435 0.803029 -0.0827806 0.267762 -0.525924
water 0.388823 0.0879565 0.582199 0.0173793 -0.68974 0.574983 0.439717
water 0.202808 0.939093 0.460056 -0.835197 -0.161335 0.496707 0.172334
water 0.57187 0.248668 0.754676 -0.409311 -0.13072 0.253034 0.866805
water 0.958542 0.466028 0.578196 -0.433213 0.587305 -0.668432 0.143523
water 0.683699 0.416439 0.442454 0.156154 -0.0157807 -0.504856 -0.848815
water 0.156825 0.0157323 0.602513 0.321394 0.23251 -0.858882 0.323987
water 0.107467 0.0319565 0.403613 -0.902008 -0.280924 0.189709 -0.267345
water 0.648611 0.620677 0.55868 0.424098 0.679881 0.528197 -0.280909
water 0.979669 0.764235 0.440702 -0.419644 0.510726 -0.498646 -0.560723
water 0.575486 0.0717962 0.395312 -0.102159 -0.315145 0.33499 -0.882059
water 0.454516 0.620378 0.578225 0.168697 0.601681 -0.779169 0.0491688
water 0.447083 0.976982 0.45556 -0.456518 0.356597 -0.03401 0.814416
water 0.55227 0.0263455 0.567041 -0.0847129 0.833366 0.437724 0.326683
water 0.127496 0.508683 0.594628 -0.0662231 -0.595624 0.60085 -0.528985
water 0.604049 0.480899 0.585807 -0.482056 0.112756 -0.860665 -0.119009
water 0.195631 0.770207 0.451215 -0.941964 -0.0405309 0.32119 0.0888647
water 0.250478 0.435973 0.589574 0.0530178 -0.894343 0.0310409 0.443143
water 0.687891 0.190913 0.402201 0.401279 0.133607 -0.316638 0.849038
water 0.587425 0.330902 0.406204 -0.260244 0.518073 0.349927 0.735816
water 0.668211 0.951081 0.59193 0.160572 -0.967481 -0.124172 0.150927
water 0.213446 0.624263 0.571595 0.391166 0.684839 -0.443336 0.425955
water 0.677464 0.762682 0.440927 0.0390529 0.495236 0.0862708 -0.863582
water 0.338145 0.892386 0.523161 0.346735 -0.611359 0.632383 0.325741
water 0.518301 0.254972 0.550605 -0.0138953 0.640156 0.514757 0.570116
water 0.509235 0.602066 0.406765 -0.62137 -0.158615 -0.765676 0.0498089
water 0.0211412 0.979153 0.543686 0.392641 -0.39296 0.555751 -0.618511
water 0.9645 0.339662 0.452773 -0.72078 -0.126765 0.546836 0.406666
water 0.665761 0.096088 0.5486 -0.511585 -0.550495 0.608281 -0.255403
water 0.0832491 0.877086 0.406179 -0.814705 0.00298576 -0.397789 -0.421913
water 0.210736 0.121134 0.409758 0.201056 0.613347 0.0048254 -0.763779
water 0.33091 0.631779 0.50305 0.0944773 -0.132146 0.986161 0.0331458
water 0.704021 0.998381 0.404576 -0.0615754 -0.377837 0.273204 0.882501
water 0.020632 0.127384 0.571868 -0.55586 -0.210702 -0.802132 0.0566425
water 0.327792 0.252616 0.515905 0.0620516 -0.870431 -0.461407 0.160007
water 0.923759 0.148728 0.445765 -0.28803 -0.292352 0.282701 -0.866977
water 0.134534 0.349143 0.581143 0.406134 -0.605335 0.673065 0.124932
water 0.38132 0.388073 0.564208 0.037677 0.748064 0.317653 -0.581444
water 0.832489 0.241757 0.719359 0.768678 -0.395081 -0.193884 0.464171
water 0.980405 0.546512 0.420296 0.645924 -0.155894 0.648178 0.371947
water 0.851108 0.638525 0.501449 -0.425952 0.244066 0.856044 -0.161818
water 0.497237 0.772974 0.408313 -0.348851 0.062697 0.150953 -0.922814
water 0.0945238 0.633043 0.456066 0.215811 -0.0922409 0.165256 -0.957918
water 0.606612 0.901825 0.427113 0.760682 -0.405813 -0.256159 -0.437106
water 0.772279 0.764202 0.590733 -0.131805 -0.432765 0.779731 -0.432852
water 0.115709 0.843612 0.576826 0.446792 -0.499523 -0.622361 -0.404378
water 0.53523 0.87816 0.602627 0.16992 -0.635633 -0.512522 -0.551742
water 0.232435 0.335546 0.447805 0.352898 -0.586391 0.120519 0.719086
cation 0.679092 0.515941 0.509385
cation 0.629674 0.00628125 0.484204
cation 0.175441 0.433911 0.510471
cation 0.111913 0.946498 0.521342
clay 0.5 0.5 0. 1 0 0 0
end
$EOD
$!
$CREATE control.argon
$DECK
#
# This sets up conditions at the triple point
#
sys-spec-file=argon.in
density=1.428
temperature=84
scale-interval=5
scale-end=2500
nsteps=5000
print-interval=500
roll-interval=500
begin-average=2501
average-interval=2500
step=0.01
subcell=2
strict-cutoff=1
cutoff=8.5125  #2.5 * sigma
begin-rdf=2501
rdf-interval=50
rdf-out=2500

$EOD
$!
$CREATE control.tips2
$DECK
title=Example TIPS2 Simulation
surface-dipole=1
temperature=300
subcell=2.5
density=1
scale-interval=10
scale-end=500
average-interval=1000
sys-spec-file=tips2.in
page-length=44
step=0.0005
nsteps=10
print-interval=10
dump-level=0
save-file=
time-unit=4.8888213e-14 #Kcal mol-1 amu A
rdf-limit=6
end
$EOD
$!
$CREATE control.quartz
$DECK
sys-spec-file=quartz-vbst.in
time-unit=1.0181e-14
nsteps=10
step=0.001
print-interval=10
scale-interval=1
temperature=300
lattice-start=1
cutoff=8.48
subcell=3
end
$EOD
$!
$CREATE tips2.in
$DECK
# Modified TIPS2 water
Water 64
1 	   0 		0 	   0 16    0 O
2  0.7569503		0 -0.5858822 1 0.535 H
2 -0.7569503		0 -0.5858822
3          0            0      -0.15 0 -1.07 M
end
lennard-jones
1 1 0.51799  3.2407
end
$EOD
$!
$CREATE tip4p.in
$DECK
# TIP4P water in KCal Mol-1
Water 256
1 	   0 		0 	   0 16    0 O
2  0.7569503		0 -0.5858822 1  0.52 H
2 -0.7569503		0 -0.5858822
3          0            0      -0.15 0 -1.04 M
end
lennard-jones
1 1 0.6201667  3.1536
end
$EOD
$!
$CREATE mgclh2o.in
$DECK
# MCY Water/ Mg2+ / Cl - solution
Water 200
1 	   0 		0 	   0 16    0 O
2  0.7569503		0 -0.5858822 1     0.717484 H
2 -0.7569503		0 -0.5858822
3          0            0    -0.2677 0 -1.434968 M
Magnesium 4
4	   0		0	   0 24.31 2 Mg2+
Chloride  8
5	   0		0	   0 35.45 -1 Cl-
end
MCY
1 1 1088213.2 	5.152712 	0 		0
1 2 1455.427 	2.961895 	273.5954 	2.233264
2 2 666.3373 	2.760844 	0 		0
1 4 47750.0	3.836		546.3		1.253 # New values of Mg potl
2 4 111.0	1.06		0		1.0   
1 5 198855.0	3.910		0		0
2 5 1857.0	2.408		77.94		1.369
4 5 28325.5	2.65		0		0
end
$EOD
$!
$CREATE argon.in
$DECK
# LJ Argon - about as simple as you can get
# Parameters from Allen and Tildesley Table 1.1
Argon 108
1          0           0          0    39.948 0 Ar
end
Lennard-Jones
1 1 3.984 3.41
$EOD
$!
$CREATE quartz-vbst.in
$DECK
# Quartz parameters from Van Beest, Kramer and Van Santen
# Physical Review Letters 64,(16) p1955 (1990)
# Units are eV, A, el chg. so time-unit=1.0181e-14
Oxygen 384
1	0	0	0	16	-1.2	O
Silicon 192
2	0	0	0	28.0855	2.4	Si
end
buckingham
1 1	175.0000	1388.7730	2.76000
1 2	133.5381	18003.7572	4.87318
2 2	0.0		0.0		0.0
end
4.903 4.903 5.393 90 90 120 4 4 4
Oxygen   0.415000     0.272000     0.120000
Oxygen   0.857000     0.5850000    0.453300
Oxygen   0.728000     0.143000     0.453300
Oxygen   0.143000     0.728000     0.880000
Oxygen   0.272000     0.415000     0.546700
Oxygen   0.5850000    0.857000     0.213300
Silicon	 0.465000     0            0
Silicon	 0.535000     0.535000     0.333300
Silicon	 0            0.465000     0.666700
end
$EOD
$!
$CREATE methane.in
$DECK
# Deutero-Methane test case
Methane 100
1 	    0 		0 	    0 12 0.0 C
2  0.88181631		0 -0.62353829 2  0.0 H
2 -0.88181631		0 -0.62353829
2	    0  0.88181631  0.62353829
2	    0 -0.88181631  0.62353829
end
buckingham
1 1 2376.5 349908.0 3.60   # William's parameters for carbon
1 2  523.0  36677.0 3.67
2 2  114.2  11104.0 3.74
end
$EOD
$!
$CREATE mcy.in
$DECK
# MCY Water
Water 64
1 	   0 		0 	   0 16    0 O
2  0.7569503		0 -0.5858822 1     0.717484 H
2 -0.7569503		0 -0.5858822
3          0            0    -0.2677 0 -1.434968 M
end
MCY
1 1 1088213.2 	5.152712 	0 		1.0
1 2 1455.427 	2.961895 	273.5954 	2.233264
2 2 666.3373 	2.760844 	0 		1.0
end
$EOD
$!
$CREATE water-example.out
$DECK
 *I* control file read in successfully
	Tue Mar 19 15:15:10 1996	Water_test	Page 1
 *I* reading system specification file
 *W* no potential parameters given between sites 1 and 2
 *W* no potential parameters given between sites 1 and 3
 *W* no potential parameters given between sites 2 and 2
 *W* no potential parameters given between sites 2 and 3
 *W* no potential parameters given between sites 3 and 3
 *I* system specification file successfully read in
	Tue Mar 19 15:15:10 1996	Water_test	Page 2


		#     # ####### #       ######  #     #
		##   ## #     # #       #     #  #   # 
		# # # # #     # #       #     #   # #  
		#  #  # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # ####### ####### ######     #   

		Version 2.10  (Exp ) 1996/03/06 18:16:21 

		Keith Refson
		Department of Earth Sciences
		Parks Road, Oxford OX1 3PR
		keith@earth.ox.ac.uk

		Moldy Copyright (C) Keith Refson 1988, 1992, 1993
		Moldy comes with ABSOLUTELY NO WARRANTY:
		This is free software and you are welcome to
		redistribute it under certain conditions.
		For details see file COPYING included with source.

 System specification read in from file tips2.in
 Water
	Number of molecules              = 64
	Number of sites                  = 4
	Mass                             = 18 amu
	Electric Charge                  = 0 Qe
	Dipole moment                    = 2.23869 D
	Moments of inertia               = 0.610236 1.75618 1.14595 amuA**2

 MD cell vectors
	a                                = 12.4137 0 0 A
	b                                = 0 12.4137 0 A
	c                                = 0 0 12.4137 A
 Run parameters
	Final step                       = 10
	Size of step                     = 0.0005 ps
	CPU limit                        = 1e+20 s
 Temperature will be scaled using instantaneous kinetic energy
	No. steps between scalings       = 10
	End scaling at step              = 500
	Applied Temperature              = 300 K
	Tue Mar 19 15:15:10 1996	Water_test	Page 3
	Interaction cut-off              = 6.25 A
	Alpha parameter for Ewald sum    = 0.45 A(-1)
	Reciprocal space cut-off         = 3 A(-1)
 New run entitled "Water_test" started Tue Mar 19 15:15:10 1996
 *I* Distant potential correction = -11.021253, Pressure correction = -18.948198
 *I* MD cell divided into 125 subcells (5x5x5)
 *I* Neighbour list contains 82 cells
 *I* Ewald self-energy = 794.119798 Kj/mol
 *I* 447 K-vectors included in reciprocal-space sum
	Tue Mar 19 15:15:11 1996	Water_test	Page 4
    Trans KE      Rot KE  Pot Energy  Tot Energy  TTemp  RTemp   Temp h(1,*) h(2,*) h(3,*)     Stress     Stress     Stress
======== Timestep 10      Current values ==================================================================================
      241.85      446.66     -47.024       393.4  303.0  559.6  431.3  12.41   0.00   0.00      -15.9        254       -172
                             -248.09                                    0.00  12.41   0.00        254        107      -84.6
                                                                        0.00   0.00  12.41       -172      -84.6      -33.9
-------- Rolling averages over last 10 timesteps --------------------------------------------------------------------------
      238.28      323.42       35.98      393.33  298.5  405.2  351.9  12.41   0.00   0.00        177        322      -97.7
                             -204.35                                    0.00  12.41   0.00        322        305      -41.8
                                                                        0.00   0.00  12.41      -97.7      -41.8        152
-------- Standard deviations ----------------------------------------------------------------------------------------------
        1.78      64.953      42.932    0.099594    2.2   81.4   41.8   0.00   0.00   0.00        256        124        186
                              23.783                                    0.00   0.00   0.00        124        173       70.6
                                                                        0.00   0.00   0.00        186       70.6        208
 *I* Run used 1.08s of CPU time and 1.39s elapsed
$EOD
$!
$CREATE tip4p-example.out
$DECK
 *I* control file read in successfully
	Tue Mar 19 15:15:39 1996	Example TIP4P Simulation	Page 1
 *I* reading system specification file
 *W* no potential parameters given between sites 1 and 2
 *W* no potential parameters given between sites 1 and 3
 *W* no potential parameters given between sites 2 and 2
 *W* no potential parameters given between sites 2 and 3
 *W* no potential parameters given between sites 3 and 3
 *I* system specification file successfully read in
	Tue Mar 19 15:15:39 1996	Example TIP4P Simulation	Page 2


		#     # ####### #       ######  #     #
		##   ## #     # #       #     #  #   # 
		# # # # #     # #       #     #   # #  
		#  #  # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # ####### ####### ######     #   

		Version 2.10  (Exp ) 1996/03/06 18:16:21 

		Keith Refson
		Department of Earth Sciences
		Parks Road, Oxford OX1 3PR
		keith@earth.ox.ac.uk

		Moldy Copyright (C) Keith Refson 1988, 1992, 1993
		Moldy comes with ABSOLUTELY NO WARRANTY:
		This is free software and you are welcome to
		redistribute it under certain conditions.
		For details see file COPYING included with source.

 System specification read in from file tip4p.in
 Water
	Number of molecules              = 256
	Number of sites                  = 4
	Mass                             = 18 amu
	Electric Charge                  = 0 Qe
	Dipole moment                    = 2.17592 D
	Moments of inertia               = 0.610236 1.75618 1.14595 amuA**2

 MD cell vectors
	a                                = 19.7055 0 0 A
	b                                = 0 19.7055 0 A
	c                                = 0 0 19.7055 A
 Run parameters
	Final step                       = 10
	Size of step                     = 0.0005 ps
	CPU limit                        = 1e+20 s
 Temperature will be scaled using instantaneous kinetic energy
	No. steps between scalings       = 10
	End scaling at step              = 500
	Applied Temperature              = 300 K
	Tue Mar 19 15:15:39 1996	Example TIP4P Simulation	Page 3
	Interaction cut-off              = 8.93822 A
	Alpha parameter for Ewald sum    = 0.379401 A(-1)
	Reciprocal space cut-off         = 2.57322 A(-1)
 Radial distribution functions will be calculated
	Starting at timestep             = 1000000
	No. steps between binnings       = 20
	Calculate and print after        = 5000
 New run entitled "Example TIP4P Simulation" started Tue Mar 19 15:15:39 1996
 *I* Distant potential correction = -64.115635, Pressure correction = -27.801126
 *I* MD cell divided into 512 subcells (8x8x8)
 *I* Neighbour list contains 204 cells
 *I* Ewald self-energy = 1722.888659 Kj/mol
 *I* 1102 K-vectors included in reciprocal-space sum
	Tue Mar 19 15:16:01 1996	Example TIP4P Simulation	Page 4
    Trans KE      Rot KE  Pot Energy  Tot Energy  TTemp  RTemp   Temp h(1,*) h(2,*) h(3,*)     Stress     Stress     Stress
======== Timestep 10      Current values ==================================================================================
      922.49      1589.3     -1410.9      1079.1  288.9  497.8  393.4  19.71   0.00   0.00        881       -485      -16.1
                              -21.87                                    0.00  19.71   0.00       -485        903        110
                                                                        0.00   0.00  19.71      -16.1        110       -452
-------- Rolling averages over last 10 timesteps --------------------------------------------------------------------------
      913.44      1187.8     -1088.8      1078.8  286.1  372.0  329.1  19.71   0.00   0.00        492       -221        441
                              66.438                                    0.00  19.71   0.00       -221   1.18e+03        236
                                                                        0.00   0.00  19.71        441        236       -364
-------- Standard deviations ----------------------------------------------------------------------------------------------
      4.6929      213.58      171.89     0.60279    1.5   66.9   34.2   0.00   0.00   0.00   1.61e+03        820   1.39e+03
                              46.168                                    0.00   0.00   0.00        820        455        317
                                                                        0.00   0.00   0.00   1.39e+03        317        114
 *I* Run used 10.88s of CPU time and 22.93s elapsed
$EOD
$!
$CREATE mgclh2o-example.out
$DECK
 *I* control file read in successfully
	Tue Mar 19 15:16:27 1996	Magnesium Chloride Solution	Page 1
 *I* reading system specification file
 *W* no potential parameters given between sites 1 and 3
 *W* no potential parameters given between sites 2 and 3
 *W* no potential parameters given between sites 3 and 3
 *W* no potential parameters given between sites 3 and 4
 *W* no potential parameters given between sites 3 and 5
 *W* no potential parameters given between sites 4 and 4
 *W* no potential parameters given between sites 5 and 5
 *I* system specification file successfully read in
	Tue Mar 19 15:16:27 1996	Magnesium Chloride Solution	Page 2


		#     # ####### #       ######  #     #
		##   ## #     # #       #     #  #   # 
		# # # # #     # #       #     #   # #  
		#  #  # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # ####### ####### ######     #   

		Version 2.10  (Exp ) 1996/03/06 18:16:21 

		Keith Refson
		Department of Earth Sciences
		Parks Road, Oxford OX1 3PR
		keith@earth.ox.ac.uk

		Moldy Copyright (C) Keith Refson 1988, 1992, 1993
		Moldy comes with ABSOLUTELY NO WARRANTY:
		This is free software and you are welcome to
		redistribute it under certain conditions.
		For details see file COPYING included with source.

 System specification read in from file mgclh2o.in
 Water
	Number of molecules              = 200
	Number of sites                  = 4
	Mass                             = 18 amu
	Electric Charge                  = 0 Qe
	Dipole moment                    = 2.19159 D
	Moments of inertia               = 0.610236 1.75618 1.14595 amuA**2
 Magnesium
	Number of molecules              = 4
	Number of sites                  = 1
	Mass                             = 24.31 amu
	Electric Charge                  = 2 Qe
	Magnesium molecule has no rotational degrees of freedom
 Chloride
	Number of molecules              = 8
	Number of sites                  = 1
	Mass                             = 35.45 amu
	Electric Charge                  = -1 Qe
	Chloride molecule has no rotational degrees of freedom

	Tue Mar 19 15:16:27 1996	Magnesium Chloride Solution	Page 3
 MD cell vectors
	a                                = 18.7676 0 0 A
	b                                = 0 18.7676 0 A
	c                                = 0 0 18.7676 A
 Run parameters
	Final step                       = 1
	Size of step                     = 0 ps
	CPU limit                        = 1e+20 s
 Temperature will be scaled using instantaneous kinetic energy
	No. steps between scalings       = 10
	End scaling at step              = 1000000
	Applied Temperature              = 0 K
	Interaction cut-off              = 6.25 A
	Alpha parameter for Ewald sum    = 0.45 A(-1)
	Reciprocal space cut-off         = 3 A(-1)
 Radial distribution functions will be calculated
	Starting at timestep             = 1000000
	No. steps between binnings       = 20
	Calculate and print after        = 5000
 New run entitled "Magnesium Chloride Solution" started Tue Mar 19 15:16:27 1996
 *I* Distant potential correction = -72.207003, Pressure correction = -18.188941
 *I* MD cell divided into 3375 subcells (15x15x15)
 *I* Neighbour list contains 486 cells
 *I* Ewald self-energy = 11332.778403 Kj/mol
 *I* 1484 K-vectors included in reciprocal-space sum
	Tue Mar 19 15:16:28 1996	Magnesium Chloride Solution	Page 4
    Trans KE      Rot KE  Pot Energy  Tot Energy  TTemp  RTemp   Temp h(1,*) h(2,*) h(3,*)     Stress     Stress     Stress
======== Timestep 1      Current values ===================================================================================
           0           0      3209.1      2423.7    0.0    0.0    0.0  18.77   0.00   0.00   3.36e+04   1.68e+04   7.49e+03
           0           0     -785.41                0.0    0.0          0.00  18.77   0.00   1.68e+04   5.84e+03   1.19e+03
           0           0                            0.0    0.0          0.00   0.00  18.77   7.49e+03   1.19e+03   3.65e+03
-------- Rolling averages over last 1 timesteps ---------------------------------------------------------------------------
           0           0      3209.1      2423.7    0.0    0.0    0.0  18.77   0.00   0.00   3.36e+04   1.68e+04   7.49e+03
           0           0     -785.41                0.0    0.0          0.00  18.77   0.00   1.68e+04   5.84e+03   1.19e+03
           0           0                            0.0    0.0          0.00   0.00  18.77   7.49e+03   1.19e+03   3.65e+03
-------- Standard deviations ----------------------------------------------------------------------------------------------
           0           0           0           0    0.0    0.0    0.0   0.00   0.00   0.00          0          0          0
           0           0           0                0.0    0.0          0.00   0.00   0.00          0          0          0
           0           0                            0.0    0.0          0.00   0.00   0.00          0          0          0
 *I* Run used 0.85s of CPU time and 1.21s elapsed
$EOD
$!
$CREATE clay-example.out
$DECK
 *I* control file read in successfully
	Tue Mar 19 15:16:43 1996	4 Mg 64 H2O Otay-montmorillonite run 1	Page 1
 *I* reading system specification file
 *I* system specification file successfully read in
 *I* system successfully initialised from lattice start
	Tue Mar 19 15:16:43 1996	4 Mg 64 H2O Otay-montmorillonite run 1	Page 2


		#     # ####### #       ######  #     #
		##   ## #     # #       #     #  #   # 
		# # # # #     # #       #     #   # #  
		#  #  # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # ####### ####### ######     #   

		Version 2.10  (Exp ) 1996/03/06 18:16:21 

		Keith Refson
		Department of Earth Sciences
		Parks Road, Oxford OX1 3PR
		keith@earth.ox.ac.uk

		Moldy Copyright (C) Keith Refson 1988, 1992, 1993
		Moldy comes with ABSOLUTELY NO WARRANTY:
		This is free software and you are welcome to
		redistribute it under certain conditions.
		For details see file COPYING included with source.

 System specification read in from file 
 water
	Number of molecules              = 64
	Number of sites                  = 4
	Mass                             = 18.0155 amu
	Electric Charge                  = -2.68285e-06 Qe
	Dipole moment                    = 2.1916 D
	Moments of inertia               = 0.614524 1.76954 1.15502 amuA**2
 cation
	Number of molecules              = 4
	Number of sites                  = 1
	Mass                             = 24.3104 amu
	Electric Charge                  = 2.00001 Qe
	cation molecule has no rotational degrees of freedom
 clay
	Number of molecules              = 1
	Number of sites                  = 240
	Mass                             = 4072.18 amu
	Electric Charge                  = -8.00004 Qe
	Dipole moment                    = 175.556 D
	clay molecule has no rotational degrees of freedom
	Tue Mar 19 15:16:43 1996	4 Mg 64 H2O Otay-montmorillonite run 1	Page 3

 MD cell vectors
	a                                = 21.12 0 0 A
	b                                = 1.11933e-15 18.28 0 A
	c                                = 8.99172e-16 8.99172e-16 14.6846 A
 Run parameters
	Final step                       = 1
	Size of step                     = 0 ps
	CPU limit                        = 1e+20 s
 Temperature will be scaled using instantaneous kinetic energy
	No. steps between scalings       = 10
	End scaling at step              = 10000
	Applied Temperature              = 300 K
	Interaction cut-off              = 12.6 A
	Alpha parameter for Ewald sum    = 0.35 A(-1)
	Reciprocal space cut-off         = 2 A(-1)
 Radial distribution functions will be calculated
	Starting at timestep             = 40000
	No. steps between binnings       = 20
	Calculate and print after        = 30000
 New run entitled "4 Mg 64 H2O Otay-montmorillonite run 1" started Tue Mar 19 15:16:43 1996
 *I* Distant potential correction = -0.006564, Pressure correction = -0.009982
 *I* MD cell divided into 960 subcells (12x10x8)
 *I* Neighbour list contains 1400 cells
 *I* Framework has net electric charge of -8 - correction of 201.117 kJmol(-1) added to self energy
 *W* System has net electric charge of -0.00016 - correction of 8.14254e-08 kJmol(-1) added to self energy
 *I* Ewald self-energy = 4773.730122 Kj/mol
 *I* 381 K-vectors included in reciprocal-space sum
	Tue Mar 19 15:16:44 1996	4 Mg 64 H2O Otay-montmorillonite run 1	Page 4
    Trans KE      Rot KE  Pot Energy  Tot Energy  TTemp  RTemp   Temp h(1,*) h(2,*) h(3,*)     Stress     Stress     Stress
======== Timestep 1      Current values ===================================================================================
      162.66      238.47     -5068.5     -9171.2  203.8  298.8  246.5  21.12   0.00   0.00      -11.5       3.53       -895
      4.6092           0     -4508.4               92.4    0.0          0.00  18.28   0.00       3.53       39.8        528
           0           0                            0.0    0.0          0.00   0.00  14.68       -895        528      -38.1
-------- Rolling averages over last 1 timesteps ---------------------------------------------------------------------------
      162.66      238.47     -5068.5     -9171.2  203.8  298.8  246.5  21.12   0.00   0.00      -11.5       3.53       -895
      4.6092           0     -4508.4               92.4    0.0          0.00  18.28   0.00       3.53       39.8        528
           0           0                            0.0    0.0          0.00   0.00  14.68       -895        528      -38.1
-------- Standard deviations ----------------------------------------------------------------------------------------------
           0           0           0           0    0.0    0.0    0.0   0.00   0.00   0.00          0          0          0
           0           0           0                0.0    0.0          0.00   0.00   0.00          0          0          0
           0           0                            0.0    0.0          0.00   0.00   0.00          0          0          0
 *I* Run used 0.90s of CPU time and 1.70s elapsed
$EOD
$!
$CREATE argon-example.out
$DECK
 *I* control file read in successfully
	Tue Mar 19 15:26:32 1996	Test Simulation	Page 1
 *I* reading system specification file
 *I* system specification file successfully read in
	Tue Mar 19 15:26:32 1996	Test Simulation	Page 2


		#     # ####### #       ######  #     #
		##   ## #     # #       #     #  #   # 
		# # # # #     # #       #     #   # #  
		#  #  # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # ####### ####### ######     #   

		Version 2.10  (Exp ) 1996/03/06 18:16:21 

		Keith Refson
		Department of Earth Sciences
		Parks Road, Oxford OX1 3PR
		keith@earth.ox.ac.uk

		Moldy Copyright (C) Keith Refson 1988, 1992, 1993
		Moldy comes with ABSOLUTELY NO WARRANTY:
		This is free software and you are welcome to
		redistribute it under certain conditions.
		For details see file COPYING included with source.

 System specification read in from file argon.in
 Argon
	Number of molecules              = 108
	Number of sites                  = 1
	Mass                             = 39.948 amu
	Electric Charge                  = 0 Qe
	Argon molecule has no rotational degrees of freedom

 MD cell vectors
	a                                = 17.1191 0 0 A
	b                                = 0 17.1191 0 A
	c                                = 0 0 17.1191 A
 Run parameters
	Final step                       = 5000
	Size of step                     = 0.01 ps
	CPU limit                        = 1e+20 s
 Temperature will be scaled using instantaneous kinetic energy
	No. steps between scalings       = 5
	End scaling at step              = 2500
	Applied Temperature              = 84 K
	Interaction cut-off              = 8.5125 A
	Tue Mar 19 15:26:32 1996	Test Simulation	Page 3
	Alpha parameter for Ewald sum    = -1 A(-1)
	Reciprocal space cut-off         = 0 A(-1)
 Radial distribution functions will be calculated
	Starting at timestep             = 2501
	No. steps between binnings       = 50
	Calculate and print after        = 2500
 New run entitled "Test Simulation" started Tue Mar 19 15:26:32 1996
 *I* Distant potential correction = -49.447049, Pressure correction = -32.664916
 *I* MD cell divided into 729 subcells (9x9x9)
 *I* Neighbour list contains 912 cells
	Tue Mar 19 15:26:54 1996	Test Simulation	Page 4
    Trans KE      Rot KE  Pot Energy  Tot Energy  TTemp  RTemp   Temp h(1,*) h(2,*) h(3,*)     Stress     Stress     Stress
======== Timestep 500      Current values =================================================================================
      114.61           0     -661.46     -546.85   85.1    0.0   85.1  17.12   0.00   0.00      -11.7       4.62      0.187
                                   0                                    0.00  17.12   0.00       4.62       6.83       0.59
                                                                        0.00   0.00  17.12      0.187       0.59       45.5
-------- Rolling averages over last 500 timesteps -------------------------------------------------------------------------
      112.62           0     -668.07     -555.46   83.6    0.0   83.6  17.12   0.00   0.00       2.22      -13.6       1.75
                                   0                                    0.00  17.12   0.00      -13.6      -1.64      -23.1
                                                                        0.00   0.00  17.12       1.75      -23.1      -10.7
-------- Standard deviations ----------------------------------------------------------------------------------------------
      3.6427           0      11.857      13.496    2.7    0.0    2.7   0.00   0.00   0.00       25.7       14.2       19.1
                                   0                                    0.00   0.00   0.00       14.2       41.4       22.3
                                                                        0.00   0.00   0.00       19.1       22.3       34.8
======== Timestep 1000      Current values ================================================================================
      118.41           0     -665.58     -547.17   87.9    0.0   87.9  17.12   0.00   0.00       12.8       18.8      -8.52
                                   0                                    0.00  17.12   0.00       18.8      -7.15      0.142
                                                                        0.00   0.00  17.12      -8.52      0.142        -16
-------- Rolling averages over last 500 timesteps -------------------------------------------------------------------------
      113.16           0     -665.22     -552.07   84.0    0.0   84.0  17.12   0.00   0.00      -1.06       1.14      -4.48
                                   0                                    0.00  17.12   0.00       1.14       7.94       -1.6
                                                                        0.00   0.00  17.12      -4.48       -1.6       -3.3
-------- Standard deviations ----------------------------------------------------------------------------------------------
      3.1419           0      5.9929      6.0009    2.3    0.0    2.3   0.00   0.00   0.00       21.8       17.2       13.2
                                   0                                    0.00   0.00   0.00       17.2       19.8       14.6
                                                                        0.00   0.00   0.00       13.2       14.6       22.2
======== Timestep 1500      Current values ================================================================================
      113.87           0     -666.44     -552.56   84.5    0.0   84.5  17.12   0.00   0.00       3.59       -9.6       17.7
                                   0                                    0.00  17.12   0.00       -9.6      -4.45      -27.1
                                                                        0.00   0.00  17.12       17.7      -27.1         11
-------- Rolling averages over last 500 timesteps -------------------------------------------------------------------------
      113.15           0     -668.95     -555.81   84.0    0.0   84.0  17.12   0.00   0.00        4.7       5.03      -3.66
                                   0                                    0.00  17.12   0.00       5.03      -3.09       5.91
                                                                        0.00   0.00  17.12      -3.66       5.91      -9.98
-------- Standard deviations ----------------------------------------------------------------------------------------------
      2.5191           0      7.0706      7.0682    1.9    0.0    1.9   0.00   0.00   0.00       26.8       13.3       15.1
                                   0                                    0.00   0.00   0.00       13.3       20.9       18.7
                                                                        0.00   0.00   0.00       15.1       18.7       21.9
======== Timestep 2000      Current values ================================================================================
      118.64           0     -674.94      -556.3   88.1    0.0   88.1  17.12   0.00   0.00       8.68       6.92       4.34
                                   0                                    0.00  17.12   0.00       6.92      -27.9      -9.61
                                                                        0.00   0.00  17.12       4.34      -9.61      -9.36
	Tue Mar 19 15:28:14 1996	Test Simulation	Page 5
    Trans KE      Rot KE  Pot Energy  Tot Energy  TTemp  RTemp   Temp h(1,*) h(2,*) h(3,*)     Stress     Stress     Stress
-------- Rolling averages over last 500 timesteps -------------------------------------------------------------------------
      113.18           0     -665.26     -552.08   84.0    0.0   84.0  17.12   0.00   0.00       2.83      0.862        7.4
                                   0                                    0.00  17.12   0.00      0.862       8.63      -2.94
                                                                        0.00   0.00  17.12        7.4      -2.94      0.988
-------- Standard deviations ----------------------------------------------------------------------------------------------
      2.7947           0      7.3998      7.3711    2.1    0.0    2.1   0.00   0.00   0.00       27.9       13.2       13.8
                                   0                                    0.00   0.00   0.00       13.2         19       10.3
                                                                        0.00   0.00   0.00       13.8       10.3       21.1
======== Timestep 2500      Current values ================================================================================
      113.58           0     -663.33     -549.74   84.3    0.0   84.3  17.12   0.00   0.00       18.5      -9.17       2.88
                                   0                                    0.00  17.12   0.00      -9.17        -14       18.4
                                                                        0.00   0.00  17.12       2.88       18.4       19.6
-------- Rolling averages over last 500 timesteps -------------------------------------------------------------------------
      113.07           0     -665.48     -552.41   83.9    0.0   83.9  17.12   0.00   0.00       2.04       11.1       4.86
                                   0                                    0.00  17.12   0.00       11.1      -5.15      -7.27
                                                                        0.00   0.00  17.12       4.86      -7.27       6.31
-------- Standard deviations ----------------------------------------------------------------------------------------------
      2.6673           0      6.8769      6.9119    2.0    0.0    2.0   0.00   0.00   0.00       21.3       12.7       13.4
                                   0                                    0.00   0.00   0.00       12.7       23.1       14.7
                                                                        0.00   0.00   0.00       13.4       14.7       21.1
 *I* Temperature scaling turned off after step 2500
 *I* started accumulating thermodynamic averages on timestep 2501
======== Timestep 3000      Current values ================================================================================
      121.78           0     -672.13     -550.35   90.4    0.0   90.4  17.12   0.00   0.00      -38.8       8.94       29.9
                                   0                                    0.00  17.12   0.00       8.94       5.59        -21
                                                                        0.00   0.00  17.12       29.9        -21      -4.84
-------- Rolling averages over last 500 timesteps -------------------------------------------------------------------------
      112.54           0     -662.69     -550.15   83.6    0.0   83.6  17.12   0.00   0.00       14.5       7.33      0.889
                                   0                                    0.00  17.12   0.00       7.33       0.51      -5.23
                                                                        0.00   0.00  17.12      0.889      -5.23        2.7
-------- Standard deviations ----------------------------------------------------------------------------------------------
      5.9692           0      5.9969     0.21025    4.4    0.0    4.4   0.00   0.00   0.00       19.9       16.1       14.5
                                   0                                    0.00   0.00   0.00       16.1       24.1       13.5
                                                                        0.00   0.00   0.00       14.5       13.5         21
======== Timestep 3500      Current values ================================================================================
      119.43           0     -668.94     -549.51   88.7    0.0   88.7  17.12   0.00   0.00       10.9      -15.1       -4.8
                                   0                                    0.00  17.12   0.00      -15.1       8.06      -8.65
                                                                        0.00   0.00  17.12       -4.8      -8.65      -32.5
-------- Rolling averages over last 500 timesteps -------------------------------------------------------------------------
      114.77           0     -664.72     -549.95   85.2    0.0   85.2  17.12   0.00   0.00      0.694       1.24       6.96
                                   0                                    0.00  17.12   0.00       1.24          5      -3.06
                                                                        0.00   0.00  17.12       6.96      -3.06       6.35
	Tue Mar 19 15:29:29 1996	Test Simulation	Page 6
    Trans KE      Rot KE  Pot Energy  Tot Energy  TTemp  RTemp   Temp h(1,*) h(2,*) h(3,*)     Stress     Stress     Stress
-------- Standard deviations ----------------------------------------------------------------------------------------------
       6.464           0       6.547      0.2507    4.8    0.0    4.8   0.00   0.00   0.00       21.7       13.9       15.9
                                   0                                    0.00   0.00   0.00       13.9       23.5       12.6
                                                                        0.00   0.00   0.00       15.9       12.6       18.3
======== Timestep 4000      Current values ================================================================================
      114.04           0      -663.9     -549.86   84.7    0.0   84.7  17.12   0.00   0.00      -10.3       21.2      -3.94
                                   0                                    0.00  17.12   0.00       21.2       6.79       6.49
                                                                        0.00   0.00  17.12      -3.94       6.49       13.9
-------- Rolling averages over last 500 timesteps -------------------------------------------------------------------------
      114.06           0      -664.2     -550.13   84.7    0.0   84.7  17.12   0.00   0.00       1.02      -9.18       5.13
                                   0                                    0.00  17.12   0.00      -9.18       7.94       1.31
                                                                        0.00   0.00  17.12       5.13       1.31        1.2
-------- Standard deviations ----------------------------------------------------------------------------------------------
      5.5247           0      5.5451     0.20845    4.1    0.0    4.1   0.00   0.00   0.00       18.9       13.2       12.3
                                   0                                    0.00   0.00   0.00       13.2       19.8       15.1
                                                                        0.00   0.00   0.00       12.3       15.1       20.1
======== Timestep 4500      Current values ================================================================================
      107.92           0     -657.56     -549.63   80.1    0.0   80.1  17.12   0.00   0.00       27.1       4.43      -8.28
                                   0                                    0.00  17.12   0.00       4.43       23.1      -10.2
                                                                        0.00   0.00  17.12      -8.28      -10.2       12.1
-------- Rolling averages over last 500 timesteps -------------------------------------------------------------------------
      115.03           0     -665.12     -550.09   85.4    0.0   85.4  17.12   0.00   0.00       7.29      0.831       1.87
                                   0                                    0.00  17.12   0.00      0.831       -3.7       1.42
                                                                        0.00   0.00  17.12       1.87       1.42       4.77
-------- Standard deviations ----------------------------------------------------------------------------------------------
      6.0505           0      6.0715     0.26149    4.5    0.0    4.5   0.00   0.00   0.00         17       18.4         14
                                   0                                    0.00   0.00   0.00       18.4       21.7       13.7
                                                                        0.00   0.00   0.00         14       13.7       23.1
======== Timestep 5000      Current values ================================================================================
      123.96           0     -674.22     -550.26   92.0    0.0   92.0  17.12   0.00   0.00      -30.3         10      -31.2
                                   0                                    0.00  17.12   0.00         10       1.83      -14.1
                                                                        0.00   0.00  17.12      -31.2      -14.1      -23.5
-------- Rolling averages over last 500 timesteps -------------------------------------------------------------------------
      114.61           0      -664.5     -549.89   85.1    0.0   85.1  17.12   0.00   0.00         16      -3.71       2.72
                                   0                                    0.00  17.12   0.00      -3.71     -0.173       1.82
                                                                        0.00   0.00  17.12       2.72       1.82       3.64
-------- Standard deviations ----------------------------------------------------------------------------------------------
      6.1853           0      6.2186     0.28451    4.6    0.0    4.6   0.00   0.00   0.00       20.8       15.5       13.7
                                   0                                    0.00   0.00   0.00       15.5       23.6       20.6
                                                                        0.00   0.00   0.00       13.7       20.6         16
	Tue Mar 19 15:30:44 1996	Test Simulation	Page 7
   Averages over last 2500 timesteps
   Trans KE        =       114.2 +/-      6.1113  kJ/mol
   Rot KE          =           0 +/-           0  kJ/mol
   Pot Energy      =     -664.25 +/-      6.1411,          0 +/-           0  kJ/mol
   Tot Energy      =     -550.04 +/-     0.26601  kJ/mol
   TTemp           =   84.8 +/-    4.5  K
   RTemp           =    0.0 +/-    0.0  K
   Temp            =   84.8 +/-    4.5  K
   h(1,*)          =  17.12 +/-   0.00,  0.00 +/-   0.00,  0.00 +/-   0.00  A
   h(2,*)          =   0.00 +/-   0.00, 17.12 +/-   0.00,  0.00 +/-   0.00  A
   h(3,*)          =   0.00 +/-   0.00,  0.00 +/-   0.00, 17.12 +/-   0.00  A
   Stress          =       7.91 +/-       20.8,    -0.698 +/-       16.5,      3.51 +/-       14.3  Mpa
   Stress          =     -0.698 +/-       16.5,      1.92 +/-       22.9,    -0.748 +/-       15.6  Mpa
   Stress          =       3.51 +/-       14.3,    -0.748 +/-       15.6,      3.73 +/-       19.9  Mpa
   Pressure        =       4.52 +/-       11.8  Mpa
   Virial          =     -62.481 +/-      39.258  kJ/mol
             =     2790.5 +/-     672.46,    2707.9 +/-     631.47,    2839.5 +/-     642.43  N**2/mol
             =          0 +/-          0,         0 +/-          0,         0 +/-          0  (Nm)**2/mol
   Dip Mom         =        0 +/-        0,       0 +/-        0,       0 +/-        0  D
____________________________________________________________________________________________________________________________________
	Radial Distribution Functions	Bin width=0.1
	Ar-Ar RDF
 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
 0.000000 0.000000 0.000000 0.008278 0.147756 0.731934 1.679294 2.500705 2.940002 2.935227 2.725624 2.326135 1.956447 1.669340
 1.402221 1.194516 1.029435 0.894747 0.791462 0.707519 0.660602 0.602891 0.614683 0.561621 0.578181 0.600775 0.556807 0.596482
 0.622303 0.678695 0.729301 0.760295 0.854689 0.872368 0.984521 1.030832 1.104755 1.176589 1.136206 1.212770 1.271280 1.273792
 1.294933 1.283603 1.297667 1.270707 1.257051 1.151918 1.100711 1.059952 1.016456 0.996895 0.926224 0.865295 0.848670 0.828072
 0.815875 0.811136 0.835673 0.839571 0.832592 0.870152 0.857046 0.880279 0.914954 0.947011 0.998518 1.006689 1.036509 1.066917
 1.068506 1.085858
____________________________________________________________________________________________________________________________________
 *I* Run used 175.40s of CPU time and 253.43s elapsed
$EOD
$!
$CREATE tips2-example.out
$DECK
 *I* control file read in successfully
	Tue Mar 19 15:24:49 1996	Example TIPS2 Simulation	Page 1
 *I* reading system specification file
 *W* no potential parameters given between sites 1 and 2
 *W* no potential parameters given between sites 1 and 3
 *W* no potential parameters given between sites 2 and 2
 *W* no potential parameters given between sites 2 and 3
 *W* no potential parameters given between sites 3 and 3
 *I* system specification file successfully read in
	Tue Mar 19 15:24:49 1996	Example TIPS2 Simulation	Page 2


		#     # ####### #       ######  #     #
		##   ## #     # #       #     #  #   # 
		# # # # #     # #       #     #   # #  
		#  #  # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # ####### ####### ######     #   

		Version 2.10  (Exp ) 1996/03/06 18:16:21 

		Keith Refson
		Department of Earth Sciences
		Parks Road, Oxford OX1 3PR
		keith@earth.ox.ac.uk

		Moldy Copyright (C) Keith Refson 1988, 1992, 1993
		Moldy comes with ABSOLUTELY NO WARRANTY:
		This is free software and you are welcome to
		redistribute it under certain conditions.
		For details see file COPYING included with source.

 System specification read in from file tips2.in
 Water
	Number of molecules              = 64
	Number of sites                  = 4
	Mass                             = 18 amu
	Electric Charge                  = 0 Qe
	Dipole moment                    = 2.23869 D
	Moments of inertia               = 0.610236 1.75618 1.14595 amuA**2

 MD cell vectors
	a                                = 12.4137 0 0 A
	b                                = 0 12.4137 0 A
	c                                = 0 0 12.4137 A
 Run parameters
	Final step                       = 10
	Size of step                     = 0.0005 ps
	CPU limit                        = 1e+20 s
 Temperature will be scaled using instantaneous kinetic energy
	No. steps between scalings       = 10
	End scaling at step              = 500
	Applied Temperature              = 300 K
	Tue Mar 19 15:24:49 1996	Example TIPS2 Simulation	Page 3
	Interaction cut-off              = 7.09427 A
	Alpha parameter for Ewald sum    = 0.478015 A(-1)
	Reciprocal space cut-off         = 3.24205 A(-1)
 Radial distribution functions will be calculated
	Starting at timestep             = 1000000
	No. steps between binnings       = 20
	Calculate and print after        = 5000
 New run entitled "Example TIPS2 Simulation" started Tue Mar 19 15:24:49 1996
 *I* Distant potential correction = -31.531180, Pressure correction = -54.492931
 *I* MD cell divided into 125 subcells (5x5x5)
 *I* Neighbour list contains 94 cells
 *I* Ewald self-energy = 968.356089 Kj/mol
 *I* 570 K-vectors included in reciprocal-space sum
	Tue Mar 19 15:24:51 1996	Example TIPS2 Simulation	Page 4
    Trans KE      Rot KE  Pot Energy  Tot Energy  TTemp  RTemp   Temp h(1,*) h(2,*) h(3,*)     Stress     Stress     Stress
======== Timestep 10      Current values ==================================================================================
      240.18      447.32      -65.41      325.56  300.9  560.4  430.7  12.41   0.00   0.00        455        437       -144
                             -296.54                                    0.00  12.41   0.00        437        654        116
                                                                        0.00   0.00  12.41       -144        116   1.03e+03
-------- Rolling averages over last 10 timesteps --------------------------------------------------------------------------
      237.63      323.63      11.435      325.49  297.7  405.5  351.6  12.41   0.00   0.00        651        532      -13.7
                             -247.21                                    0.00  12.41   0.00        532        864        178
                                                                        0.00   0.00  12.41      -13.7        178   1.21e+03
-------- Standard deviations ----------------------------------------------------------------------------------------------
      1.2436      65.177       39.65    0.098793    1.6   81.7   41.6   0.00   0.00   0.00        271        202        350
                               26.75                                    0.00   0.00   0.00        202        208        125
                                                                        0.00   0.00   0.00        350        125        214
 *I* Run used 1.30s of CPU time and 1.50s elapsed
$EOD
$!
$CREATE quartz-example.out
$DECK
 *I* control file read in successfully
	Tue Mar 19 15:25:39 1996	Test Simulation	Page 1
 *I* reading system specification file
 *I* system specification file successfully read in
 *I* system successfully initialised from lattice start
	Tue Mar 19 15:25:39 1996	Test Simulation	Page 2


		#     # ####### #       ######  #     #
		##   ## #     # #       #     #  #   # 
		# # # # #     # #       #     #   # #  
		#  #  # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # ####### ####### ######     #   

		Version 2.10  (Exp ) 1996/03/06 18:16:21 

		Keith Refson
		Department of Earth Sciences
		Parks Road, Oxford OX1 3PR
		keith@earth.ox.ac.uk

		Moldy Copyright (C) Keith Refson 1988, 1992, 1993
		Moldy comes with ABSOLUTELY NO WARRANTY:
		This is free software and you are welcome to
		redistribute it under certain conditions.
		For details see file COPYING included with source.

 System specification read in from file quartz-vbst.in
 oxygen
	Number of molecules              = 384
	Number of sites                  = 1
	Mass                             = 16 amu
	Electric Charge                  = -1.2 Qe
	oxygen molecule has no rotational degrees of freedom
 silicon
	Number of molecules              = 192
	Number of sites                  = 1
	Mass                             = 28.0855 amu
	Electric Charge                  = 2.4 Qe
	silicon molecule has no rotational degrees of freedom

 MD cell vectors
	a                                = 19.612 0 0 A
	b                                = -9.806 16.9845 0 A
	c                                = 1.3209e-15 2.28787e-15 21.572 A
 Run parameters
	Final step                       = 10
	Size of step                     = 0.001 ps
	Tue Mar 19 15:25:39 1996	Test Simulation	Page 3
	CPU limit                        = 1e+20 s
 Temperature will be scaled using instantaneous kinetic energy
	No. steps between scalings       = 1
	End scaling at step              = 1000000
	Applied Temperature              = 300 K
	Interaction cut-off              = 8.48 A
	Alpha parameter for Ewald sum    = 0.352007 A(-1)
	Reciprocal space cut-off         = 2.38743 A(-1)
 Radial distribution functions will be calculated
	Starting at timestep             = 1000000
	No. steps between binnings       = 20
	Calculate and print after        = 5000
 New run entitled "Test Simulation" started Tue Mar 19 15:25:39 1996
 *I* Distant potential correction = -2097.957193, Pressure correction = -969.585951
 *I* MD cell divided into 1560 subcells (12x10x13)
 *I* Neighbour list contains 552 cells
 *I* Ewald self-energy = 457724.399972 Kj/mol
 *I* 833 K-vectors included in reciprocal-space sum
	Tue Mar 19 15:25:51 1996	Test Simulation	Page 4
    Trans KE      Rot KE  Pot Energy  Tot Energy  TTemp  RTemp   Temp h(1,*) h(2,*) h(3,*)     Stress     Stress     Stress
======== Timestep 10      Current values ==================================================================================
      4420.6           0 -5.4959e+05 -1.0005e+06  923.0    0.0  733.4  19.61  -9.81   0.00   1.36e+03  -4.15e+03        114
       848.2           0 -4.5614e+05              354.2    0.0          0.00  16.98   0.00  -4.15e+03   1.45e+04       -176
                                                                        0.00   0.00  21.57        114       -176  -7.07e+03
-------- Rolling averages over last 10 timesteps --------------------------------------------------------------------------
      4401.2           0 -5.3414e+05  -9.844e+05  919.0    0.0  749.1  19.61  -9.81   0.00       -480  -2.62e+03   1.95e+03
      980.16           0 -4.5565e+05              409.3    0.0          0.00  16.98   0.00  -2.62e+03    1.8e+04   1.27e+03
                                                                        0.00   0.00  21.57   1.95e+03   1.27e+03  -9.95e+03
-------- Standard deviations ----------------------------------------------------------------------------------------------
      1053.4           0       10253       10141  220.0    0.0  153.2   0.00   0.00   0.00   2.49e+03   2.11e+03   8.08e+03
      125.59           0      325.13               52.4    0.0          0.00   0.00   0.00   2.11e+03   2.91e+03   2.76e+03
                                                                        0.00   0.00   0.00   8.08e+03   2.76e+03   1.08e+04
 *I* Run used 5.47s of CPU time and 12.35s elapsed
$EOD
$!
$CREATE moldyext.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
#include "defs.h"
#include "string.h"
#if defined(ANSI) || defined(__STDC__)
#include 
#else
#include 
#endif
#include "stdlib.h"
#include "stddef.h"
#include 

int	getopt();

#define NSIGNAL 8
#define buf_inc 128

#if defined(ANSI) || defined(__STDC__)
#undef  va_alist
#define	va_alist char *format, ...
#ifdef  va_dcl
#   undef  va_dcl
#endif
#define va_dcl /* */
#endif
/*VARARGS*/
void error(va_alist)
va_dcl
{
   va_list p;
#if defined(ANSI) || defined(__STDC__)
   va_start(p, format);
#else
   char	*format;

   va_start(p);
   format = va_arg(p, char *);
#endif
   vfprintf(stderr,format,p);
   fputc('\n',stderr);
   va_end(p);
   exit(3);
}
static char * mystrdup(s)
char *s;
{
   char * t=malloc(strlen(s)+1);
   return t?strcpy(t,s):0;
}
/******************************************************************************
 *  Tokenise().  Parse the string of fields to be returned and return a mask  *
 *  in a char array.  Format is 1,3,6-9,3 . . . ie comma-separated with cont- *
 *  iguous range specified with hyphen.  Numbering starts at 1.		      *
 ******************************************************************************/
int	tokenise(fields, mask, len)
char	*fields, *mask;
int	len;
{
   char	*s;
   int	lo, hi, i, n;

   for(i = 0; i < len; i++)
      mask[i] = 0;

   while( ( s = strtok(fields,",") ) != NULL )
   {
      n = sscanf(s, "%d-%d", &lo, &hi);
      if( n == 0 )
	 return 0;

      if( n == 1 )
	 hi = lo;

      if( lo < 1 || hi < lo || hi > len)
	 return 0;

      for( i = lo-1; i < hi; i++)
	 mask[i] = 1;
      fields = NULL;
   }
   return 1;
}
/******************************************************************************
 * read_record().    Read one 'record' of Moldy output into buffer.  A record *
 * starts on the line following 8 '=' characters and enbtd either at the first*
 * line containing 8 '-' chars or a linefeed. It takes a char* pointer to a   *
 * malloc'ed buffer area, and realloc's this if it needs more space.          *
 *****************************************************************************/
char	*read_record()
{
   static	char	*buf = NULL;
   static	int	buf_len = 132;
   int	c, nsymb, buf_cnt;

   if( buf == 0)
   {
      if( ( buf = malloc(buf_len) ) == 0)
	 error("Memory allocation fails: %d bytes requested", buf_len);
   }
      
   nsymb = 0;
   /*
    * Read input, discarding up to and including next 8 contiguous '='
    */
   while( nsymb < NSIGNAL && ( c = getchar()) != EOF)
   {
      if( c == '=' )
	 nsymb++;
      else
	 nsymb = 0;
   }
   /*
    *  Read up to and including next newline
    */
   while( (c = getchar()) != EOF && c != '\n' )
      ; /* Empty loop body */
   /*
    *  Now read input into buffer, extending it if necessary.
    *  Read up to (and including) next 8 '-' or formfeed
    */
   buf_cnt = 0;  nsymb = 0;
   while( nsymb < NSIGNAL && (c = getchar()) != EOF && c != '\f' )
   {
      if( buf_cnt >= buf_len )		/* Buffer too small */
      {
	 buf_len += buf_inc;		/* Make it bigger   */
	 if( ( buf = realloc(buf, buf_len) ) == 0)
	    error("Memory allocation fails: %d bytes requested", buf_len);
      }
      buf[buf_cnt++] = c;
      if( c == '-' )
	 nsymb++;
      else
	 nsymb = 0;
   }
   buf_cnt -= nsymb;			/* Don't return trailing '-'  */
   buf[buf_cnt] = '\0';			/* Terminate string	      */

   return( buf );
}
/******************************************************************************
 * main program								      *
 ******************************************************************************/
#define MAX_FIELDS 256
int
main(argc, argv)
int	argc;
char	*argv[];
{
   char *buf, *fields;
   char mask[MAX_FIELDS];
   char sfield[64];
   int field, cnt, end, inc;

   extern char *optarg;
   extern int optind;
 
   if( getopt(argc, argv, "f:") == -1)
      fields = "1-256";
   else
      fields = optarg;
      
   if( tokenise(mystrdup(fields), mask, MAX_FIELDS) == 0 )
      error("Invalid field specification \"%s\": usage eg 1,3,5-9,4",fields);

#ifdef DEBUG
   {
      int i;
      for(i = 0; i < MAX_FIELDS; i++)
         if(mask[i])
	    putchar('1');
         else
	    putchar('0');
      putchar('\n');
   }
#endif
      
   while(optind < argc)
     {
       if(strcmp(argv[optind],"-") && freopen(argv[optind], "r", stdin) == NULL)
	 error("Failed to open file \"%s\" for reading\n", argv[optind]);  
       optind++;     
       while( ! feof( stdin) )
	 {
	   buf = read_record();
	   end = strlen(buf);
	   cnt = 0;
	   field = 0;
	   while( cnt < end )
	     {
	       if(sscanf(buf+cnt, "%63s%n", sfield, &inc) != EOF)
		 {
		   if(mask[field])
		     {
		       putchar('\t');
		       fputs(sfield,stdout);
		     }
		   cnt += inc;
		   field++;
		 }
	       else
		 cnt = end;			/* Flag exit from inner loop	*/
	     }
	   if( field > 0 )
	     putchar('\n');
	 }
     }
   return 0;
}


$EOD
$!
$CREATE dumpanal.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/dumpanal.c,v 2.7 1994/06/08 13:12:11 keith stab $";
#endif

/*
 * $Log: dumpanal.c,v $
 * Revision 2.7  1994/06/08 13:12:11  keith
 * Declared xdr_dump().
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Revision 2.5  94/01/26  11:55:39  keith
 * Tidied up lint/gcc warnings.
 * Fixed def'n of main to "int" coz it failed on broken (?) VMS compiler.
 * 
 * Revision 2.4  94/01/18  13:23:12  keith
 * Incorporated all portability experience to multiple platforms since 2.2.
 * Including ports to VAX/VMS and Open VMS on Alpha AXP and Solaris.
 * 
 * Revision 2.3  93/10/14  18:18:36  keith
 * Fixed prortability problems to IBM RS6000
 * 
 * Revision 2.2  93/09/06  14:42:41  keith
 * Fixed portability problems/bugs in XDR code.
 * 
 * Revision 2.1  93/08/18  20:52:07  keith
 * Added support for dumps in XDR format.
 * 
 * Revision 2.0  93/03/15  14:49:43  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.3  93/03/09  15:59:49  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.2  89/05/15  16:51:27  keith
 * Modified to work with structs.h 1.3 or later
 * 
 * Revision 1.1  89/04/11  15:04:51  keith
 * Initial revision
 * 
 */

#include "defs.h"
#include 
#include "string.h"
#include "structs.h"
#include "time.h"
#ifdef USE_XDR
#include        "xdr.h"
#endif

#ifdef USE_XDR
bool_t xdr_dump();
#endif
/******************************************************************************
 * strstr replacement for pre-ANSI machines which don't have it.              *
 ******************************************************************************/
#ifndef ANSI_LIBS
char *strstr(cs, ct)
char *cs, *ct;
{
   char *end = cs+strlen(cs)-strlen(ct);
   for(; cs <= end; cs++)
      if( !strcmp(cs,ct) )
	 return cs;
   return 0;      
}
#endif

char	*ctime();
void	analyze();
void	print_header();
int
main(argc, argv)
int	argc;
char	*argv[];
{
   int	i;
   for(i = 1; i < argc; i++)
      analyze(argv[i]);
   return 0;
}

void analyze(file)
char *file;
{
   FILE *f;
   dump_mt header;
   int          errflg = 0, xdr = 0;
#ifdef USE_XDR
   XDR          xdrs;
#endif
   
   if( (f = fopen(file,"rb")) != NULL )
   {
      printf("\n***** %s *****\n",file);
#ifdef USE_XDR
      /*
       * Attempt to read dump header in XDR format
       */
      xdrstdio_create(&xdrs, f, XDR_DECODE);
      if( xdr_dump(&xdrs, &header) )
      {
         header.vsn[sizeof header.vsn - 1] = '\0';
         if( strstr(header.vsn,"(XDR)") )
         {
            errflg = 0;
            xdr = 1;
         }
      }
      else
         errflg = 1;
#endif
      /*
       * If we failed, try to read header as native struct image.
       */
      if( ! xdr )
      {
         if( fseek(f, 0L, 0) == 0 &&
             fread((char*)&header, sizeof(dump_mt), 1, f) == 1) 
            errflg = 0;
      }
      if( errflg == 0)
	 print_header(&header);
      else
         printf("Failed to read anything from %s",file);
#ifdef USE_XDR
      if( xdr )
	 xdr_destroy(&xdrs);
#endif
      (void)fclose(f);
   }
}
void	print_header(header)
dump_mt	*header;
{
   printf("Title\t\t\t= \"%s\"\n",header->title);
   printf("RCS Revision\t\t= %.*s\n", (int)strlen(header->vsn)-1, header->vsn);
   printf("Istep\t\t\t= %d\n",header->istep);
   printf("Dump_interval\t\t= %d\n", header->dump_interval);
   printf("Dump_level\t\t= %d\n", header->dump_level);
   printf("Max dumps\t\t= %d\n", header->maxdumps);
   printf("Dump Size\t\t= %d\n", header->dump_size);
   printf("Number of dumps\t\t= %d\n", header->ndumps);
   printf("Timestamp\t\t= %s", ctime((time_t*)&header->timestamp));
   printf("Restart Timestamp\t= %s", ctime((time_t*)&header->restart_timestamp));
   printf("Dump Start\t\t= %s", ctime((time_t*)&header->dump_init));
}
$EOD
$!
$CREATE dumpconv.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/dumpconv.c,v 2.8 1995/12/05 20:55:10 keith Exp $";
#endif

/*
 * $Log: dumpconv.c,v $
 * Revision 2.8  1995/12/05 20:55:10  keith
 * Separated ANSI replacement routines from Auxil.c into Ansi.c
 * Removed all COS functionality.
 *
 * Revision 2.7  1994/06/08 13:12:34  keith
 * Changed "%g" scan format to "%f" - should be identical but
 * some systems, particularly VAX/VMS didn't grok "%g".
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Revision 2.5  94/01/21  12:51:04  keith
 * Corrected declaration of main()
 * 
 * Revision 2.4  94/01/21  12:35:03  keith
 * Incorporated all portability experience to multiple platforms since 2.2.
 * Rewrote varargs functions to use stdargs conditionally on __STDC__
 * 
 * Revision 2.5  1994/01/18  16:26:29  keith
 * Incorporated all portability experience to multiple platforms since 2.2.
 * Rewrote varargs functions to use stdargs conditionally on __STDC__
 *
 * Revision 2.5  94/01/18  13:13:09  keith
 * Incorporated all portability experience to multiple platforms since 2.2.
 * Rewrote varargs functions to use stdargs conditionally on __STDC__
 * 
 * Revision 2.4  93/12/21  18:49:40  keith
 * Portability improvements:
 * 1. Moved malloc etc declarations into header files
 * 2. Rewrote varargs functions to use stdargs conditionally on __STDC__
 * 
 * Revision 2.3  93/10/28  10:28:50  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.2  93/09/06  14:42:43  keith
 * Fixed portability problems/bugs in XDR code.
 * 
 * Revision 2.1  93/08/18  20:52:05  keith
 * Added support for dumps in XDR format.
 * 
 * Revision 2.0  93/03/15  14:49:44  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.5  93/03/09  15:59:51  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.4  91/08/15  18:13:10  keith
 * 
 * 
 * Revision 1.3  90/09/28  10:51:27  keith
 * Amended #idfefs for unicos
 * 
 * Revision 1.2  89/09/07  18:15:54  keith
 * checked in with -k by keith at 89.09.08.15.48.37.
 * 
 * Revision 1.2  89/09/07  18:15:54  keith
 * Rationalised command-line parameters.  Now takes option '-d', + two
 * file names for input and output.  A file name of '-' means use stdin/out
 * as does an absent name.   
 * This should work better on non-unix machines.
 * 
 * Revision 1.1  89/06/27  12:10:34  keith
 * Initial revision
 * 
 */

#include "defs.h"
#include "stdlib.h"
#include "stddef.h"
#include "structs.h"
#include "string.h"
#if defined(ANSI) || defined(__STDC__)
#include 
#else
#include 
#endif
#include 
#ifdef USE_XDR
#include "xdr.h"
XDR	 xdrsr;
XDR	 xdrsw;
bool_t xdr_dump();
#endif

/******************************************************************************
 * strstr replacement for pre-ANSI machines which don't have it.              *
 ******************************************************************************/
#ifndef ANSI_LIBS
char *strstr(cs, ct)
char *cs, *ct;
{
   char *end = cs+strlen(cs)-strlen(ct);
   for(; cs <= end; cs++)
      if( !strcmp(cs,ct) )
	 return cs;
   return 0;      
}
#endif

#if defined(ANSI) || defined(__STDC__)
#undef  va_alist
#define	va_alist char *format, ...
#define va_dcl /* */
#endif
/*VARARGS*/
void error(va_alist)
va_dcl
{
   va_list p;
#if defined(ANSI) || defined(__STDC__)
   va_start(p, format);
#else
   char	*format;

   va_start(p);
   format = va_arg(p, char *);
#endif
   vfprintf(stderr,format,p);
   fputc('\n',stderr);
   va_end(p);
   exit(3);
}

void read_text(buf,buflen)
float  *buf;
int    buflen;
{
   int i;

   for( i = 0; i < buflen; i++ )
      scanf("%f", &buf[i]);
   if( ferror(stdin) )
      error("Read error on input file (Error code %d).",ferror(stdin));
}

void write_text(buf,buflen)
float  *buf;
int    buflen;
{
   int i;

   for( i = 0; i < buflen; i++ )
      printf("%.7g%c",buf[i],(i+1) % 5 ? ' ' : '\n');
   if( i % 5 ) fputc('\n', stdout);
   if( ferror(stdout) )
      error("Write error on output file (Error code %d).",ferror(stdout));
}

void read_binary(buf,buflen,xdr)
float  *buf;
int    buflen, xdr;
{
#ifdef USE_XDR
   if( xdr )
   {
      xdr_vector(&xdrsr, (char*)buf, buflen, XDR_FLOAT_SIZE, xdr_float);
   }
   else
#endif
   {
      fread(buf , sizeof(float), buflen, stdin);
   }
   if( ferror(stdin) )
      error("Read error on input file (Error code %d).",ferror(stdin));
}

void write_native(buf, buflen)
float  *buf;
int    buflen;
{
   fwrite(buf , sizeof(float), buflen, stdout);
   if( ferror(stdout) )
      error("Write error on output file (Error code %d).",ferror(stdout));
}

#ifdef USE_XDR
void write_xdr(buf, buflen)
float  *buf;
int    buflen;
{
   xdr_vector(&xdrsw, (char*)buf, buflen, XDR_FLOAT_SIZE, xdr_float);
   if( ferror(stdout) )
      error("Write error on output file (Error code %d).",ferror(stdout));
}
#endif

void read_bin_hdr(header, xdrw)
dump_mt *header;
int     *xdrw;
{
   int    xdr = 0, errflg = 0;

#ifdef USE_XDR
   /*
    * Attempt to read dump header in XDR format
    */
   xdrstdio_create(&xdrsr, stdin, XDR_DECODE);
   if( xdr_dump(&xdrsr, header) )
   {
      header->vsn[sizeof header->vsn - 1] = '\0';
      if( strstr(header->vsn,"(XDR)") )
      {
	 errflg = 0;
	 xdr = 1;
      }
   }
   else
      errflg = 1;
#endif
   /*
    * If we failed, try to read header as native struct image.
    */
   if( ! xdr )
   {
      if( fseek(stdin, 0L, 0) == 0 &&
	 fread((char*)header, sizeof(dump_mt), 1, stdin) == 1) 
	 errflg = 0;
   }
   if( errflg || ferror(stdin) || feof(stdin) )
   {
      error("Failed to read header record (Error code %d).",ferror(stdin));
      exit(2);
   }
   *xdrw = xdr;
}

int	read_header(header)
dump_mt	*header;
{
   int num;
   char *c;
   
   fgets(header->title, sizeof header->title, stdin);
   if((c = strchr(header->title, '\n')))
      *c = '\0';
   fgets(header->vsn, sizeof header->vsn, stdin);
   if((c = strchr(header->vsn, '\n')))
      *c = '\0';
   num  = 2;
   num += scanf("%d %d %d %d %d", &header->istep, &header->dump_interval,
	  &header->dump_level, &header->maxdumps, &header->ndumps);
   num += scanf("%ld %ld %ld %d",&header->timestamp,
		 &header->restart_timestamp,
		 &header->dump_init, &header->dump_size);
   if( num == 11 ) return 0;
   else            return -1;
}

void write_native_hdr(header)
dump_mt *header;
{
   char *s;
   if( (s = strstr(header->vsn,"(XDR)") ) != 0 )
      *s = 0;
   if( ! fwrite((char*)header, sizeof(dump_mt), 1, stdout) )
      error("Write error on output file (Error code %d).",ferror(stdout));
}

#ifdef USE_XDR
void write_xdr_hdr(header)
dump_mt *header;
{
   strncat(header->vsn,"(XDR)",sizeof header->vsn);
   xdrstdio_create(&xdrsw, stdout, XDR_ENCODE);
   if( ! xdr_dump(&xdrsw, header) )
      error("Write error on output file (Error code %d).",ferror(stdout));
}
#endif

void	print_header(header)
dump_mt	*header;
{
   char *s;
   if( (s = strstr(header->vsn,"(XDR)") ) != 0 )
      *s = 0;
   printf("%s\n%s\n",header->title, header->vsn);
   printf("%d %d %d %d %d\n", header->istep, header->dump_interval,
	  header->dump_level, header->maxdumps, header->ndumps);
   printf("%ld %ld %ld %d\n",header->timestamp, header->restart_timestamp,
	   header->dump_init, header->dump_size); 
}

int
main(argc, argv)
int	argc;
char	*argv[];
{
   static char *ity[2] = {"rb","r"}, *oty[2] = {"w","wb"};
   dump_mt header;
   float *buf;
   int   idump;
   int   textin = 0, xdrin, xdrout = 0;

   while( argc > 0 && argv[1][0] == '-' )
   {
      switch( argv[1][1] ) {
       case 'd':	
	 textin++;
	 break;
       case 'x':
	  xdrout++;
	 break;
       default:
	 fprintf(stderr,"Usage: dumpconvert [-d] [-x] infile outfile\n");
	 exit(2);
      }
      argc--; argv++;
   }

   if( argc > 0 )
   {
      if( strcmp(argv[1],"-") && freopen(argv[1], ity[textin], stdin) == NULL )
	 error("Failed to open file \"%s\" for input", argv[1]);
   }
   if( argc > 1 )
   {
      if( strcmp(argv[2],"-") && freopen(argv[2], oty[textin], stdout) == NULL )
	 error("Failed to open file \"%s\" for writing", argv[2]);
   }
	    
      
   if( textin )
   {
      read_header(&header);
#ifdef USE_XDR
      if( xdrout )
	 write_xdr_hdr(&header);
      else
#endif
	 write_native_hdr(&header);
   }
   else
   {
      read_bin_hdr(&header, &xdrin);
#ifdef USE_XDR
      if( xdrout )
	 write_xdr_hdr(&header);
      else
#endif
	 print_header(&header);
   }

   if( ! (buf = (float *)calloc(header.dump_size, sizeof(float))))
      error("Failed to allocate memory (%d words requested)\n",
	    header.dump_size);
   
   for( idump = 0; idump < header.ndumps; idump++)
   {
      if( textin )
      {
	 read_text(buf, header.dump_size);
#ifdef USE_XDR
	 if( xdrout )
	    write_xdr(buf, header.dump_size);
	 else
#endif
	    write_native(buf, header.dump_size);
      }
      else
      {
	 read_binary(buf, header.dump_size, xdrin);
#ifdef USE_XDR
	 if( xdrout )
	    write_xdr(buf, header.dump_size);
	 else
#endif
	    write_text(buf, header.dump_size);
      }
   }
return 0;
}
$EOD
$!
$CREATE dumpext.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
#include "defs.h"
#include	"stdlib.h"
#include	"stddef.h"
#include 	"string.h"
#include 	
#include	"structs.h"
#ifdef USE_XDR
#   include	"xdr.h"
#endif

int		getopt();

static char * mystrdup(s)
char *s;
{
   char * t=malloc(strlen(s)+1);
   return t?strcpy(t,s):0;
}
/******************************************************************************
 * strstr replacement for pre-ANSI machines which don't have it.              *
 ******************************************************************************/
#ifndef ANSI_LIBS
char *strstr(cs, ct)
char *cs, *ct;
{
   char *end = cs+strlen(cs)-strlen(ct);
   for(; cs <= end; cs++)
      if( !strcmp(cs,ct) )
	 return cs;
   return 0;      
}
#endif

int av_convert;
#undef MIN
#define MIN(x,y) ( (x) > (y) ? (y) : (x))

/*========================== Macros ==========================================*/
#define DUMP_SIZE(level, n, n_r)  \
   			 (( (level & 1) + (level>>1 & 1) + (level>>2 & 1) ) * \
                                    (3*n + 4*n_r + 9)+ \
                             (level>>3 & 1) * \
                                    (3*n + 3*n_r + 9) +\
                             (level & 1))
/*============================================================================*/
typedef struct list_mt
{
   struct list_mt	*next;
   int			i;
   char *p;
   int num;
} list_mt;

typedef struct cpt_mt
{
   int ncpt, offset, size, mols;
   char name[32];
} cpt_mt;

/******************************************************************************
 * get_int().  Read an integer from stdin, issuing a prompt and checking      *
 * validity and range.  Loop until satisfied, returning EOF if appropriate.   *
 ******************************************************************************/
int get_int(prompt, lo, hi)
char    *prompt;
int     lo, hi;
{
   char         ans_str[80];
   int          ans_i, ans_flag;

   ans_flag = 0;
   while( ! feof(stdin) && ! ans_flag )
   {
      fputs(prompt, stderr);
      fflush(stderr);
      fgets(ans_str, sizeof ans_str, stdin);
      if( sscanf(ans_str, "%d", &ans_i) == 1 && ans_i >= lo && ans_i <= hi)
         ans_flag++;
   }
   if( feof(stdin) )
   {
      fprintf(stderr,"\nExit requested\n");
      exit(3);
   }
   if( ans_flag )
      return(ans_i);
   else
      return(EOF);
}
/******************************************************************************
 * List manipulation procedures						      *
 ******************************************************************************/
void
insert(entry, head)
list_mt	*entry, *head;
{
   while( head->next != NULL && entry->i > head->next->i)
      head = head->next;

   entry->next = head->next;
   head->next  = entry;
}

void
print_list(head)
list_mt	*head;
{
   if(head == NULL)
      return;
   fprintf(stderr,"%-8d%s\n",head->i, head->p);
   print_list(head->next);
}

/******************************************************************************
 * forstr.  Parse string str of format s-f:n  (final 2 parts optional),       *
 *          returning integer values of s,f,n. f defaults to s and n to 1     *
 ******************************************************************************/
int
forstr(str, start, finish, inc)
char	*str;
int	*start, *finish, *inc;
{
   char	*p, *pp;
   
   if( (p = strchr(str,':')) != NULL)
   {
      *inc = strtol(p+1, &pp, 0);
      if( pp == p+1 )
	 goto limerr;
      *p = 0;
   }
   else
      *inc = 1;
   if( (p = strchr(str,'-')) != NULL)
   {
      *p = 0;
      *start = strtol(str, &pp, 0);
      if( pp == str )
	 goto limerr;
      *finish = strtol(p+1, &pp, 0);
      if( pp == p+1 )
	 goto limerr;
   }
   else
   {
      *start = *finish = strtol(str, &pp, 0);
      if( pp == str )
	 goto limerr;
   }
   return 0;
 limerr:
   return -1;
}
/******************************************************************************
 * Put.  Write data in text or binary form.				      *
 ******************************************************************************/
void
put(buf, n, bflg)
float *buf;
int   n;
int   bflg;
{
#ifdef DEBUG2
   fprintf(stderr,"Put: %d at %8x (%s)\n", n, buf, bflg? "binary":"text");
#endif
   if( bflg )
      fwrite((char*)buf, sizeof(float), n, stdout);
   else
      while(n-- > 0)
	 fprintf(stdout,"%7g ",*buf++);

}
/******************************************************************************
 * Extract.  Process one dump file, extracting and outputting data.	      *
 ******************************************************************************/
void
extract(dump_name, cpt_mask, molecules, cpt, ncpt, tslice, num, inc, 
	bflg, nmols, xdr)
char	*dump_name;
int	cpt_mask;
list_mt	*molecules;
cpt_mt	cpt[];
int	ncpt, tslice, num, inc;
int	bflg, nmols, xdr;
{
   FILE		*dump_file;
   dump_mt	header;
   float	*buf = (float*)calloc(4*nmols,sizeof(float));/* nmols > nmols_r */
   long		dump_base;
   int		icpt, start, nitems;
   list_mt	*mol;
   int		errflg = 0;
#ifdef USE_XDR
   XDR          xdrs;
#endif

   
   if( (dump_file = fopen(dump_name, "rb")) == NULL)
   {
      fprintf(stderr, "Failed to open dump file \"%s\"\n", dump_name);
      exit(2);
   }
#ifdef DEBUG
   fprintf(stderr,"Working on file \"%s\" (%d-%d)\n", dump_name, tslice, num);
#endif
#ifdef USE_XDR
   /*
    * Attempt to read dump header in XDR format
    */
   if( xdr )
   {
      xdrstdio_create(&xdrs, dump_file, XDR_DECODE);
      errflg = ! xdr_dump(&xdrs, &header);
   }
   else
#endif
   {
      if( fread((char*)&header, sizeof(dump_mt), 1, dump_file) == 0 )
	 errflg = false;
   }
         
   if( errflg || ferror(dump_file) || feof(dump_file) )
   {
      fprintf(stderr, "Failed to read dump header \"%s\"\n", dump_name);
      exit(2);
   }

#ifdef USE_XDR
   if( xdr )
      dump_base = XDR_DUMP_SIZE+tslice*header.dump_size*XDR_FLOAT_SIZE;
   else
#endif
      dump_base = sizeof(dump_mt)+tslice*header.dump_size*sizeof(float);
   while(tslice < num && tslice < header.ndumps)
   {
#ifdef DEBUG
      fprintf(stderr,"Timeslice %d of %d\n", tslice, num);
#endif
      for( icpt = 0; icpt < ncpt; icpt++)
      {
	 if( cpt_mask & (1 << icpt) )
	 {
#ifdef USE_XDR
	    if( xdr )
	    {
	       xdr_setpos(&xdrs, dump_base+cpt[icpt].offset*XDR_FLOAT_SIZE);
	       xdr_vector(&xdrs, (char*)buf, (u_int)cpt[icpt].size, XDR_FLOAT_SIZE, 
			  xdr_float);
	    }
	    else
#endif
	    {
	       fseek(dump_file, dump_base+cpt[icpt].offset*sizeof(float), 0);
	       fread((char*)buf, sizeof(float), cpt[icpt].size, dump_file);
	    }
	    if( cpt[icpt].mols )
	       for(mol = molecules; mol != 0; mol = mol->next)
	       {
		  start  = mol->i   * cpt[icpt].ncpt;
		  nitems = mol->num * cpt[icpt].ncpt;
		  if( start+nitems > cpt[icpt].size )
		     nitems = cpt[icpt].size - start;
		  put(buf+start, nitems, bflg);
	       }
	    else
	       put(buf, cpt[icpt].size, bflg);
	 }
      }
      if( ! bflg )
	 putchar('\n');
      tslice += inc;
#ifdef USE_XDR
      if( xdr )
	 dump_base += inc*XDR_FLOAT_SIZE * header.dump_size;
      else
#endif
	 dump_base += inc*sizeof(float) * header.dump_size;
   }
#ifdef USE_XDR
   if( xdr )
      xdr_destroy(&xdrs);
#endif
   (void)fclose(dump_file);
   (void)free((char*)buf);
}

int
main(argc, argv)
int	argc;
char	*argv[];
{
   int	c;
   extern int	optind;
   extern char	*optarg;
   int		errflg = 0, genflg = 0, tsflg = 0, bflg = 0;
   int          nmols=-1, nmols_r=-1;
   int		xcpt= -1;
   char		*dump_name=0, *dump_base=0, *out_name=0;
   char		cur_dump[256];
   char		*tsrange;
   FILE		*dump_file;
   int		nfiles = 0;
   int		start,finish,inc;
   int		tslice, numslice, maxslice;
   int		offset, icpt;
   int		idump0;
   int		xdr = 0;
#ifdef USE_XDR
   XDR          xdrs;
#endif
   
   static cpt_mt cpt[] = {{3, 0, 3, 1, "C of M positions"},
			 {4, 0, 4, 1, "quaternions"},
			 {9, 0, 1, 0, "unit cell matrix"},
			 {1, 0, 1, 0, "potential energy"},
			 {3, 0, 3, 1, "C of M velocities"},
			 {4, 0, 4, 1, "quaternion derivatives"},
			 {9, 0, 1, 0, "unit cell velocities"},
			 {3, 0, 3, 1, "C of M accelerations"},
			 {4, 0, 4, 1, "quaternion accelerations"},
			 {9, 0, 1, 0, "unit cell accelerations"},
			 {3, 0, 3, 1, "C of M forces"},
			 {3, 0, 3, 1, "torques"},
			 {9, 0, 1, 0, "stress tensor"} };
#define NCPT (int)(sizeof(cpt)/sizeof(cpt_mt))

   static int level_mask[16] = {  0x0000,0x000f,0x0070,0x007f,
				  0x0380,0x038f,0x03f0,0x03ff,
				  0x1c00,0x1c0f,0x1c70,0x1c7f,
				  0x1f80,0x1f8f,0x1ff0,0x1fff};

   dump_mt	proto_header, header;

   list_mt	f_head;
   list_mt	mol_head;
   list_mt	*cur;

   mol_head.next = NULL;
   f_head.next = NULL;

   while( (c = getopt(argc, argv, "c:br:R:q:Q:t:m:o:") ) != EOF )
      switch(c)
      {
       case 'c':
	 xcpt = strtol(optarg,(char**)0,0);
	 break;
       case 'b':
	 bflg++;
	 break;
       case 'r':
       case 'R':
	 nmols = strtol(optarg,(char**)0,0);
	 break;
       case 'q':
       case 'Q':
	 nmols_r = strtol(optarg,(char**)0,0);
	 break;
       case 'o':
	 out_name = optarg;
	 break;
       case 't':
	 if( tsflg++ == 0)
	    tsrange = optarg;
	 else
	    errflg++;
	 break;
       case 'm':
	 if( forstr(optarg, &start, &finish, &inc) )
	    errflg++;
	 else
	 {
	    cur = (list_mt *)calloc(1, sizeof(list_mt));
	    cur->i = start;  cur->num=finish-start+1;
	    insert(cur, &mol_head);
	 }	 
	 break;
       case '?': 
       case 'h':
	 errflg++;
      }


   if(optind >= argc)
   {
      fprintf(stderr,"%s: no dump files given\n",argv[0]);
      errflg++;
   }
   if( errflg )
   {
      fprintf(stderr,
	   "Usage: dumpext [-Rn] [-Qn] [-b] [-c cpt]\
 [-t timeslices] [-m molecules] [-o output-file] dumpfiles\n");
      exit(2);
   }
   /*
    *  Interactive input of parameters not supplied as argument
    */
   if( nmols <= 0)
      nmols = get_int("Number of molecules? ",1,1000000);
   if( nmols_r < 0)
      nmols_r = get_int("Number of polyatomic molecules? ",0,1000000);
   if( xcpt < 0 )
   {
      fprintf(stderr,"Which quantity do you require?\n");
      fprintf(stderr,"\t%-32s %d\n","All data components",0);
      for(icpt = 0; icpt < NCPT; icpt++)
	 fprintf(stderr,"\t%-32s %d\n",cpt[icpt].name,icpt+1);
      xcpt=get_int("Quantity index (0-13)? ",0,NCPT);
   }

   /*
    *  Molecule mask
    */
   if(mol_head.next == 0)
   {
      cur = (list_mt*)calloc(1,sizeof(list_mt));
      cur->i = 0;
      cur->num = nmols;
      mol_head.next = cur;
   }

   /*
    *  Generate list of dump files if required
    */
   if( strchr(argv[optind],'%') )
   {
      genflg++;
#define MAXTRY 500
      dump_base = argv[optind];
      idump0 = -1;
      do                      /* Search for a dump file matching pattern */
	 sprintf(cur_dump, dump_base, ++idump0);
      while( (dump_file = fopen(cur_dump, "rb")) == NULL && idump0 < MAXTRY);
      if( dump_file == NULL )        /* If we didn't find one . .               */
      {
	 fprintf(stderr,"I can't find any dump files to match \"%s\".\n",dump_base);
	 exit(2);
      }
      (void)fclose(dump_file);
   }
   else
      idump0 = optind;
   
   /*
    *  Check all dump files for correctness and build ordered list
    */
   while(1)
   {
      if( genflg )
      {
	 sprintf(cur_dump, dump_base, idump0++);
	 dump_name = cur_dump;
      }
      else
      {
	 dump_name = argv[idump0++];
	 if( dump_name == 0 )
	    break;
      }

      if( (dump_file = fopen(dump_name, "rb")) == NULL)
      {
	 if( genflg )
	    break;		/* Exit loop if at end of sequence */
	 fprintf(stderr, "Failed to open dump file \"%s\"\n", dump_name);
	 exit(2);
      }
#ifdef USE_XDR
      /*
       * Attempt to read dump header in XDR format
       */
      xdrstdio_create(&xdrs, dump_file, XDR_DECODE);
      if( xdr_dump(&xdrs, &header) )
      {
	 header.vsn[sizeof header.vsn - 1] = '\0';
	 if( strstr(header.vsn,"(XDR)") )
	 {
	    errflg = 0;
	    xdr = 1;
	 }
      }
      else
	 errflg = 1;
#endif
      /*
       * If we failed, try to read header as native struct image.
       */
      if( ! xdr )
      {
	 if( fseek(dump_file, 0L, 0) == 0 &&
             fread((char*)&header, sizeof(dump_mt), 1, dump_file) == 1) 
	    errflg = 0;
      }
      if( errflg || ferror(dump_file) || feof(dump_file) )
      {
	 fprintf(stderr, "Failed to read dump header \"%s\"\n", dump_name);
	 exit(2);
      }
      
      if( nfiles++ == 0 )
	 proto_header = header;
      else if( strncmp(header.title, proto_header.title, L_name) ||
	      strncmp(header.vsn, proto_header.vsn, sizeof header.vsn) ||
	    header.dump_interval != proto_header.dump_interval ||
	    header.dump_level != proto_header.dump_level       ||
	    header.dump_size != proto_header.dump_size         ||
	    header.dump_init != proto_header.dump_init )
      {
         fprintf(stderr,"Dump headers don't match: file\"%s\"\n", dump_name);
	 exit(2);
      };

#ifdef USE_XDR
      if( xdr )
	 xdr_destroy(&xdrs);
#endif
      (void)fclose(dump_file);
      cur = (list_mt *)calloc(1, sizeof(list_mt));
      cur->p = mystrdup(dump_name);
      cur->i = header.istep/header.dump_interval;
      cur->num = header.ndumps;
      insert(cur, &f_head);
#ifdef DEBUG
      fprintf(stderr,"File \"%s\" \nslice %5d length %5d\n",
	              dump_name, cur->i, cur->num);
#endif
   }

   if( xcpt > 0 && ! (1 << (xcpt-1) & level_mask[proto_header.dump_level]) )
   {
      fprintf(stderr,"Sorry the component requested (%s)",cpt[xcpt-1].name);
      fprintf(stderr," is not contained in a dump of level %d\n",
	      header.dump_level);
      exit(2);
   }

   tslice = start = f_head.next->i;
   for(cur = f_head.next; cur; cur = cur->next)
   {
      if( cur->i != tslice )
      {
	 fprintf(stderr,"Dump file \"%s\" out of sequence at slice %d\n",
		        cur->p, tslice);
	 exit(2);
      }
      cur->i -= start;
      tslice += cur->num;
#ifdef DEBUG
      fprintf(stderr,"File \"%s\" \nslice %5d length %5d\n",
	              cur->p, cur->i, cur->num);
#endif
   }
   maxslice = tslice - start;

   if(DUMP_SIZE(header.dump_level, nmols,nmols_r) != proto_header.dump_size)
   {
      fprintf(stderr, "Number of molecules (%d/%d) ",nmols,nmols_r);
      fprintf(stderr, "incompatible with dump size (%d) and level(%d)\n",
	      proto_header.dump_size, proto_header.dump_level);
      exit(2);
   }
   /*
    *  Set up timestep mask
    */
   if( ! tsflg )
   {
      numslice = maxslice;
      tslice = 0;
      inc = 1;
   }
   else if( forstr(tsrange, &tslice, &numslice, &inc) )
   {
      fprintf(stderr,"Incorrect time slice selection \"%s\"\n", tsrange);
      exit(2);
   }
   else
   {
      numslice += 1;
      if(tslice < 0 || numslice > maxslice)
      {
	 fprintf(stderr, "Error in dump sequence - step %d not found\n", 
		 numslice);
	 exit(2);
      }
   }
   /*
    *  Set up component size and offsets
    */
#ifdef DEBUG
   fprintf(stderr,"Size Offset\n");
#endif
   offset=0;
   cpt[0].size = cpt[4].size = cpt[7].size = cpt[10].size = nmols;
   cpt[1].size = cpt[5].size = cpt[8].size = cpt[11].size = nmols_r;
   for(icpt = 0; icpt < NCPT; icpt++)
   {
      cpt[icpt].size *= cpt[icpt].ncpt;
      cpt[icpt].offset = offset;
      if( (1<next)
   {
      if( cur->i <= tslice && tslice < MIN(cur->i + cur->num, numslice) )
      {
	 extract(cur->p, xcpt?1<<(xcpt-1):~0, mol_head.next, cpt, NCPT, 
		 tslice-cur->i,
		 MIN(cur->num,numslice-cur->i), inc, bflg, nmols, xdr);
	 tslice += (cur->i + cur->num - tslice - 1) / inc * inc + inc;
      }
   }

   return 0;
}
$EOD
$!
$CREATE manalyze.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/manalyze.c,v 2.8 1996/11/05 09:53:50 keith Exp $";
#endif

/*
 * $Log: manalyze.c,v $
 * Revision 2.8  1996/11/05 09:53:50  keith
 * Fixed bug which reported last record twice.
 * Now prints offsets too for ease of debugging.
 *
 * Revision 2.7  1994/06/08 13:22:31  keith
 * Null update for version compatibility
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Revision 2.5  1994/01/26  11:57:07  keith
 * Tidied up lint/gcc warnings.
 * Fixed def'n of main to "int" coz it failed on broken (?) VMS compiler.
 * Rewrote varargs functions to use stdargs conditionally on __STDC__
 *
 * Revision 2.5  1994/01/21  12:46:01  keith
 * Lint/gcc -Wall tidying
 *
 * Revision 2.4  94/01/18  13:23:14  keith
 * Incorporated all portability experience to multiple platforms since 2.2.
 * Including ports to VAX/VMS and Open VMS on Alpha AXP and Solaris.
 * 
 * Revision 2.3  93/10/28  10:28:54  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.0  93/03/15  14:49:46  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.5  93/03/09  15:59:54  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.4  91/08/15  18:13:13  keith
 * 
 * 
 * Revision 1.3  91/03/07  18:10:59  keith
 * Changed message
 * 
 * Revision 1.2  90/04/25  14:47:28  keith
 * Declared malloc().
 * 
 * Revision 1.1  90/02/22  17:46:11  keith
 * Initial revision
 * 
 */

#include	"defs.h"
#include	"stddef.h"
#include	"structs.h"
#include	"stdlib.h"
#include	


char	*ctime();
int
main(argc, argv)
int	argc;
char	*argv[];
{
   FILE		*f = stdin;
   restrt_mt	restart_header;
   unsigned		size, offset;
   char		*ptr;
   int		n, rec = 1;
   if(argc > 1)
   {
      f = fopen(argv[1],"r");
      if(f == NULL)
      {
         fprintf(stderr,"Couldn't open restart file \"%s\"\n",argv[1]);
         exit(1);
      }
   }
   (void)fread((gptr*)&size, sizeof size, 1, f);
   if(size != sizeof restart_header)
   {
   	fprintf(stderr,"This isn't a Moldy restart file\n");
   	exit(1);
   }
   fread(&restart_header, size, 1,f);
   offset = sizeof size;
   printf("Restart file was written at %s", ctime(&restart_header.timestamp));
   printf("This is restart No %d of run \"%s\" started %s\n",
            restart_header.seq, restart_header.title, restart_header.init_date);
   printf("It was written by version %s of write_restart\n",restart_header.vsn);
   printf("\n\tHeader record\t%d\tbytes %9X %9X\n",size, offset, size);
   offset += sizeof restart_header + sizeof size;
   do
   {
      n = fread((gptr*)&size, sizeof size, 1, f);
      if( n < 1 )
	 break;
      printf("\tRecord %d \t%d\tbytes %9X %9X\n",rec++,size, offset, size);
      offset += size + sizeof size;
      ptr = malloc(size);
      fread(ptr,size,1,f);
      free(ptr);
   } while(!feof(f));
   return 0;
}
$EOD
$!
$CREATE mdshak.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/mdshak.c,v 2.15 1998/01/28 09:55:37 keith Exp $";
#endif

#include "defs.h"
#if defined(ANSI) || defined(__STDC__)
#include 
#else
#include 
#endif
#include 
#include 
#include "stdlib.h"
#include "stddef.h"
#include "string.h"
#include 
#include "structs.h"
#include "messages.h"
#include "ReadDCD.h"
#if defined(ANSI) || defined(__STDC__)
gptr	*arralloc(size_mt,int,...); 	/* Array allocator		      */
#else
gptr	*arralloc();	        	/* Array allocator		      */
#endif

void	invert();
void	mat_vec_mul();
void	make_sites();
char	*strlower();
void	read_sysdef();
void	initialise_sysdef();
void	re_re_header();
void	re_re_sysdef();
void	allocate_dynamics();
void	lattice_start();
void	read_restart();
void	init_averages();
int	getopt();
gptr	*talloc();
FILE	*popen();
/*======================== Global vars =======================================*/
int ithread=0, nthreads=1;
static   char		*comm;
contr_mt		control;

#define OUTBIN 2
#define SHAK   0
#define XYZ 1
#define DCD 3
/******************************************************************************
 * Dummies of 'moldy' routines so that mdshak may be linked with moldy library*
 ******************************************************************************/
void 	init_rdf()
{}
gptr *rdf_ptr()
{return 0;}
void new_lins()
{}
int lines_left()
{return 0;}
void new_page()
{}
void	new_line()
{
   (void)putchar('\n');
}
void	banner_page()
{}
void	note()
{}
void	conv_potentials()
{}
void	conv_control()
{}
/******************************************************************************
 *  message.   Deliver error message to possibly exiting.  It can be called   *
 *             BEFORE output file is opened, in which case outt to stderr.    *
 ******************************************************************************/
#if defined(ANSI) || defined(__STDC__)
#   undef  va_alist
#   define      va_alist int *nerrs, ...
#   ifdef va_dcl
#      undef va_dcl
#   endif
#   define va_dcl /* */
#endif
/*VARARGS*/
void    message(va_alist)
va_dcl
{
   va_list      ap;
   char         *buff;
   int          sev;
   char         *format;
   static char  *sev_txt[] = {" *I* "," *W* "," *E* "," *F* "};
#if defined(ANSI) || defined(__STDC__)
   va_start(ap, nerrs);
#else
   int          *nerrs;

   va_start(ap);
   nerrs = va_arg(ap, int *);
#endif
   buff  = va_arg(ap, char *);
   sev   = va_arg(ap, int);
   format= va_arg(ap, char *);

   (void)fprintf(stderr, "%s: ",comm);
   (void)vfprintf(stderr, format, ap);
   va_end(ap);
   fputc('\n',stderr);

   if(buff != NULL)                     /* null ptr means don't print buffer  */
   {
      (void)fprintf(stderr,"     buffer contents=\"%s\"",buff);
      fputc('\n',stderr);
   }
   if(sev >= ERROR && nerrs != NULL)
      (*nerrs)++;
   if(sev == FATAL)
      exit(3);
}

/******************************************************************************
 *  message.   Deliver error message to possibly exiting. 		      *
 ******************************************************************************/
#if defined(ANSI) || defined(__STDC__)
#undef  va_alist
#define	va_alist char *format, ...
#ifdef  va_dcl
#   undef  va_dcl
#endif
#define va_dcl /* */
#endif
/*VARARGS*/
void	error(va_alist)
va_dcl
{
   va_list	ap;
#if defined(ANSI) || defined(__STDC__)
   va_start(ap, format);
#else
   char		*format;

   va_start(ap);
   format= va_arg(ap, char *);
#endif

   (void)fprintf(stderr, "%s: ",comm);
   (void)vfprintf(stderr, format, ap);
   fputc('\n',stderr);
   va_end(ap);

   exit(3);
}
static char * mystrdup(s)
char *s;
{
   char * t=malloc(strlen(s)+1);
   return t?strcpy(t,s):0;
}
/******************************************************************************
 * get_int().  Read an integer from stdin, issuing a prompt and checking      *
 * validity and range.  Loop until satisfied, returning EOF if appropriate.   *
 ******************************************************************************/
int get_int(prompt, lo, hi)
char	*prompt;
int	lo, hi;
{
   char		ans_str[80];
   int		ans_i, ans_flag;

   ans_flag = 0;
   while( ! feof(stdin) && ! ans_flag )
   {
      fputs(prompt, stderr);
      fflush(stderr);
      fgets(ans_str, sizeof ans_str, stdin);
      if( sscanf(ans_str, "%d", &ans_i) == 1 && ans_i >= lo && ans_i <= hi)
	 ans_flag++;
   }
   if( ans_flag )
      return(ans_i);
   else
      return(EOF);
}
/******************************************************************************
 * get_sym().  Read a character from stdin and match to suplied set	      *
 ******************************************************************************/
int get_sym(prompt, cset)
char	*prompt;
char	*cset;
{
   char		ans_c, ans_str[80];
   int		ans_flag;

   ans_flag = 0;
   while( ! feof(stdin) && ! ans_flag )
   {
      fputs(prompt, stderr);
      fflush(stderr);
      fgets(ans_str, sizeof ans_str, stdin);
      if( sscanf(ans_str, " %c", &ans_c) == 1 && strchr(cset, ans_c))
	 ans_flag++;
   }
   if( ans_flag )
      return(ans_c);
   else
      return(EOF);
}
/******************************************************************************
 * get_str().  Read an string from stdin, issuing a prompt.		      *
 ******************************************************************************/
char	*get_str(prompt)
char	*prompt;
{
   char		ans_str[80];
   char		*str = malloc(80);
   int		ans_flag;

   ans_flag = 0;
   while( ! feof(stdin) && ! ans_flag )
   {
      fputs(prompt, stderr);
      fflush(stderr);
      fgets(ans_str, sizeof ans_str, stdin);
      if( sscanf(ans_str, "%s", str) == 1)
	 ans_flag++;
   }
   if( ans_flag )
      return(str);
   else
      return(NULL);
}
/******************************************************************************
 ******************************************************************************/
/******************************************************************************
 * forstr.  Parse string str of format s-f:n  (final 2 parts optional),       *
 *          returning integer values of s,f,n. f defaults to s and n to 1     *
 ******************************************************************************/
int
forstr(instr, start, finish, inc)
char	*instr;
int	*start, *finish, *inc;
{
   char	*p, *pp, *str = mystrdup(instr);
   long strtol();
   
   if( (p = strchr(str,':')) != NULL)
   {
      *inc = strtol(p+1, &pp, 0);
      if( pp == p+1 )
	 goto limerr;
      *p = 0;
   }
   else
      *inc = 1;
   if( (p = strchr(str,'-')) != NULL)
   {
      *p = 0;
      *start = strtol(str, &pp, 0);
      if( pp == str )
	 goto limerr;
      *finish = strtol(p+1, &pp, 0);
      if( pp == p+1 )
	 goto limerr;
   }
   else
   {
      *start = *finish = strtol(str, &pp, 0);
      if( pp == str )
	 goto limerr;
   }
   return 0;
 limerr:
   return -1;
}
/******************************************************************************
 * dump_to_moldy.  Fill the 'system' arrays with the dump data in 'buf' (see  *
 * dump.c for format), expanding floats to doubles if necessary.              *
 ******************************************************************************/
#define DUMP_SIZE(level)  (( (level & 1) + (level>>1 & 1) + (level>>2 & 1) ) * \
			            (3*sys.nmols + 4*sys.nmols_r + 9)+ \
			     (level>>3 & 1) * \
			            (3*sys.nmols + 3*sys.nmols_r + 9) +\
			     (level & 1))
void
dump_to_moldy(buf, system)
float	*buf;
system_mt *system;
{
   int i;
   float	*c_of_m = buf;
   float	*quat   = buf+3*system->nmols;
   float	*h      = buf+3*system->nmols + 4*system->nmols_r;
   mat_mt hinv;

/* $dir no_recurrence */
   for(i = 0; i < system->nmols; i++)
   {
      system->c_of_m[i][0] = c_of_m[3*i];
      system->c_of_m[i][1] = c_of_m[3*i+1];
      system->c_of_m[i][2] = c_of_m[3*i+2];
   }
/* $dir no_recurrence */
   for(i = 0; i < system->nmols_r; i++)
   {
      system->quat[i][0] = quat[4*i];
      system->quat[i][1] = quat[4*i+1];
      system->quat[i][2] = quat[4*i+2];
      system->quat[i][3] = quat[4*i+3];
   }
/* $dir no_recurrence */
   for(i = 0; i < 3; i++)
   {
      system->h[i][0] = h[3*i];
      system->h[i][1] = h[3*i+1];
      system->h[i][2] = h[3*i+2];
   }

   invert(system->h, hinv);
   mat_vec_mul(hinv, system->c_of_m, system->c_of_m, system->nmols);
}
/******************************************************************************
 ******************************************************************************/
void mat_vec_mul3(m, vec, number)
int             number;         /* Number of vectors to be multiplied         */
real            m[3][3];        /* Matrix                                     */
real            **vec;          /* Output vector.  CAN BE SAME AS INPUT  (out)*/
{
   int i;
   register double        a0, a1, a2;
   
   for(i = 0; i < number; i++)
   {
      a0 = vec[0][i];  a1 = vec[1][i];  a2 = vec[2][i];
      
      vec[0][i] = m[0][0]*a0 + m[0][1]*a1 + m[0][2]*a2;
      vec[1][i] = m[1][0]*a0 + m[1][1]*a1 + m[1][2]*a2;
      vec[2][i] = m[2][0]*a0 + m[2][1]*a1 + m[2][2]*a2;
   }
}
/******************************************************************************
 * shakal_out().  Write a system configuration to stdout in the form of an    *
 * input data file for the graphics program SCHAKAL88.			      *
 ******************************************************************************/
void
schakal_out(n, system, species, site_info, insert)
int	n;
system_mt	*system;
spec_mt		species[];
site_mt		site_info[];
char		*insert;
{
   double	**site = (double**)arralloc(sizeof(double),2,
					    0,2,0,system->nsites-1);
   spec_mt	*spec;
   double	a, b, c, alpha, beta, gamma;
   mat_mp	h = system->h;
   mat_mt	hinv;
   int		imol, isite, is;

   invert(h,hinv);

   a = sqrt(SQR(h[0][0]) + SQR(h[1][0]) + SQR(h[2][0]));
   b = sqrt(SQR(h[0][1]) + SQR(h[1][1]) + SQR(h[2][1]));
   c = sqrt(SQR(h[0][2]) + SQR(h[1][2]) + SQR(h[2][2]));
   alpha = 180/PI*acos((h[0][1]*h[0][2]+h[1][1]*h[1][2]+h[2][1]*h[2][2])/b/c);
   beta  = 180/PI*acos((h[0][0]*h[0][2]+h[1][0]*h[1][2]+h[2][0]*h[2][2])/a/c);
   gamma = 180/PI*acos((h[0][0]*h[0][1]+h[1][0]*h[1][1]+h[2][0]*h[2][1])/a/b);

   printf("CELL %f %f %f %f %f %f\n", a, b, c, alpha, beta, gamma);
   for(spec = species; spec < species+system->nspecies; spec++)
   {
      make_sites(system->h, spec->c_of_m, spec->quat, spec->p_f_sites,
		 spec->framework, site, spec->nmols, spec->nsites);

      mat_vec_mul3(hinv, site, spec->nsites*spec->nmols);

      isite = 0;
      for(imol = 0; imol < spec->nmols; imol++)
      {
	 puts("MOL");
	 for(is = 0; is < spec->nsites; is++)
	 {
	    if(fabs(site_info[spec->site_id[is]].mass) != 0)
	       (void)printf("ATOM %-8s %7.4f %7.4f %7.4f\n",
			    site_info[spec->site_id[is]].name,
			    site[0][isite], site[1][isite], site[2][isite]);
	    isite++;
	 }
      }
   }

   if( insert != NULL)
      (void)printf("%s\n", insert);

   (void)printf("END %d\n", n);
   if( ferror(stdout) )
      error("Error writing output - \n%s\n", strerror(errno));
   afree((gptr*) site);
}
/******************************************************************************
 * xyz_out().  Write a system configuration to stdout in the form of an    *
 * input data file for the graphics program XYZ (rasmol -xyz file)	      *
 ******************************************************************************/
void
xyz_out(n, system, species, site_info, insert)
int	n;
system_mt	*system;
spec_mt		species[];
site_mt		site_info[];
char		*insert;
{
   double	**site = (double**)arralloc(sizeof(double),2,
					    0,2,0,system->nsites-1);
   spec_mt	*spec;
   int		imol, isite, is;

/* We count the number of atoms */
   isite=0;
   for(spec = species; spec < species+system->nspecies; spec++)
   {
      for(imol = 0; imol < spec->nmols; imol++)
      {
	 for(is = 0; is < spec->nsites; is++)
         {
	    if(fabs(site_info[spec->site_id[is]].mass) != 0)
              isite++;
         }
      }
   }
/* Now we write the Xyz header */
   (void)printf("%d\n",isite);
/* It would be nice to have here the real title */
   (void)printf("%s\n",control.title);
   
   for(spec = species; spec < species+system->nspecies; spec++)
   {
      make_sites(system->h, spec->c_of_m, spec->quat, spec->p_f_sites,
		 spec->framework, site, spec->nmols, spec->nsites);

      isite = 0;
      for(imol = 0; imol < spec->nmols; imol++)
      {
	 for(is = 0; is < spec->nsites; is++)
	 {
	    if(fabs(site_info[spec->site_id[is]].mass) != 0)
	       (void)printf("%-8s %7.4f %7.4f %7.4f\n",
			    site_info[spec->site_id[is]].name,
			    site[0][isite], site[1][isite], site[2][isite]);
	    isite++;
	 }
      }
   }

   if( insert != NULL)
      (void)printf("%s\n", insert);

   if( ferror(stdout) )
      error("Error writing output - \n%s\n", strerror(errno));
   afree((gptr*) site);
}
/******************************************************************************
 * dcd_out().  Write a system configuration to stdout in the form of an    *
 * DCD data file for the graphics program VMD                              *
 ******************************************************************************/
void
dcd_out(n, irec, inc, system, species, site_info, insert)
int	n, irec, inc;
system_mt	*system;
spec_mt		species[];
site_mt		site_info[];
char		*insert;
{
   double	**site = (double**)arralloc(sizeof(double),2,
					    0,2,0,system->nsites-1);
   float	**sitef = (float**)arralloc(sizeof(float),2,
					    0,2,0,system->nsites-1);
   spec_mt	*spec;
   int		isite, is, i, imol, isitem;

   isitem=0; 
   for(spec = species; spec < species+system->nspecies; spec++)
   {
      make_sites(system->h, spec->c_of_m, spec->quat, spec->p_f_sites,
		 spec->framework, site, spec->nmols, spec->nsites);

      isite = 0;
      for(imol = 0; imol < spec->nmols; imol++)
      {
	 for(is = 0; is < spec->nsites; is++)
	 {
	    if(fabs(site_info[spec->site_id[is]].mass) != 0)
	    {
	       for(i=0; i<3; i++)
		  sitef[i][isitem] = site[i][isite];
	       isitem++;
	    }
	    isite++;
	 }
      }
   }
/* On first call  write the DCD header.  Always write to stdout. */
   if( n == 0 )
      write_dcdheader(stdout, control.title, isitem, irec, 0, inc, control.step);
   
   write_dcdstep(stdout, isitem, sitef[0], sitef[1], sitef[2]);

   if( ferror(stdout) )
      error("Error writing output - \n%s\n", strerror(errno));
   afree((gptr*)site);
   afree((gptr*)sitef);
}
/******************************************************************************
 * atoms_out().  Write a system configuration to stdout in the form of an     *
 * binary atomic co-ordinates.						      *
 ******************************************************************************/
void
atoms_out(n, system, species, site_info, atom_sel)
int	n;
system_mt	*system;
spec_mt		species[];
site_mt		site_info[];
char		*atom_sel;
{
   double	**site = (double**)arralloc(sizeof(double),2,
					    0,2,0,system->nsites-1);
   spec_mt	*spec;
   float	fsite[3];
   mat_mp	h = system->h;
   mat_mt	hinv;
   int		imol, isite, is,i;

   invert(h,hinv);

   for(spec = species; spec < species+system->nspecies; spec++)
   {
      make_sites(system->h, spec->c_of_m, spec->quat, spec->p_f_sites,
		 spec->framework, site, spec->nmols, spec->nsites);

      mat_vec_mul3(hinv, site, spec->nsites*spec->nmols);

      isite = 0;
      for(imol = 0; imol < spec->nmols; imol++)
      {
	 for(is = 0; is < spec->nsites; is++)
	 {
	    for(i=0; i<3; i++)
	       fsite[i]=site[i][isite];
            fwrite((char*)fsite, sizeof fsite, 1, stdout);
	    isite++;
	 }
      }
   }
   if( ferror(stdout) )
      error("Error writing output - \n%s\n", strerror(errno));
   afree((gptr*) site);
}
 /******************************************************************************
 * Centre_mass.  Shift system centre of mass to origin (in discrete steps),   *
 ******************************************************************************/
void
centre_mass(species, nspecies, c_of_m)
spec_mt		species[];
int		nspecies;
vec_mt		c_of_m;
{
   double	mass;
   spec_mt	*spec;
   int		imol;
   vec_mt	*s_c_of_m;

   mass = c_of_m[0] = c_of_m[1] = c_of_m[2] = 0.0;
   for(spec = species; spec < species + nspecies; spec++ )
   {
      s_c_of_m = spec->c_of_m;
      for(imol = 0; imol < spec->nmols; imol++)
      {
	 c_of_m[0] += spec->mass*s_c_of_m[imol][0];
	 c_of_m[1] += spec->mass*s_c_of_m[imol][1];
	 c_of_m[2] += spec->mass*s_c_of_m[imol][2];
      }
      mass += spec->nmols*spec->mass;
   }

   c_of_m[0] /= mass;
   c_of_m[1] /= mass;
   c_of_m[2] /= mass;
   c_of_m[0] = floor(c_of_m[0]+0.5);
   c_of_m[1] = floor(c_of_m[1]+0.5);
   c_of_m[2] = floor(c_of_m[2]+0.5);
}
/******************************************************************************
 * Shift.  Translate all co-ordinates.					      *
 ******************************************************************************/
void	shift(r, nmols, s)
vec_mt	r[];
int	nmols;
vec_mt	s;
{
   int imol;
   for(imol = 0; imol < nmols; imol++)
   {
      r[imol][0] -= s[0];
      r[imol][1] -= s[1];
      r[imol][2] -= s[2];
   }
}
/******************************************************************************
 * moldy_out.  Select output routine and handle file open/close		      *
 * Translate system relative to either centre of mass of posn of framework.   *
 ******************************************************************************/
void
moldy_out(n, irec, inc, system, species, site_info, atom_sel, outsw, insert)
int	n, irec, inc;
system_mt	*system;
spec_mt		species[];
site_mt		site_info[];
char		*atom_sel;
int		outsw;
char		*insert;
{
   spec_mp	spec, frame_spec  = NULL;
   vec_mt	c_of_m;
   
   for(spec = species; spec < species+system->nspecies; spec++)
      if( spec->framework )
	 frame_spec = spec;

   if( frame_spec != NULL )
      shift(system->c_of_m, system->nmols, frame_spec->c_of_m[0]);
   else
   {
      centre_mass(species, system->nspecies, c_of_m);
      shift(system->c_of_m, system->nmols, c_of_m);
   }
   switch (outsw)
   {
   case DCD:
      dcd_out(n, irec, inc, system, species, site_info, insert);
      break;
    case SHAK:
      schakal_out(n, system, species, site_info, insert);
      break;
    case XYZ:
      xyz_out(n, system, species, site_info, insert);
      break;
    case OUTBIN:
      atoms_out(n, system, species, site_info, atom_sel);
      break;
   }
}
/******************************************************************************
 * main().   Driver program for generating SCHAKAL input files from MOLDY     *
 * files.    Acceptable inputs are sys-spec files, or restart files. Actual   *
 * configrational info can be read from dump files, lattice-start files or    *
 * restart files.  Call: mdshak [-s sys-spec-file] [-r restart-file].   If    *
 * neither specified on command line, user is interrogated.		      *
 ******************************************************************************/
int
main(argc, argv)
int	argc;
char	*argv[];
{
   int	c, cflg = 0, ans_i, sym, data_source = 0;
   char 	line[80];
   extern char	*optarg;
   int		errflg = 0;
   int		intyp = 0;
   int		start, finish, inc;
   int		rflag;
   int		irec;
   int		iout = 0;
   int		outsw=0;
   char		*filename = NULL, *dump_name = NULL;
   char		*dumplims = NULL, *atom_sel = NULL;
   char		*insert = NULL;
   char		*tempname;
   char		dumpcommand[256];
   int		dump_size;
   float	*dump_buf;
   FILE		*Fp, *Dp;
   restrt_mt	restart_header;
   system_mt	sys;
   spec_mt	*species;
   site_mt	*site_info;
   pot_mt	*potpar;
   quat_mt	*qpf;
   int		av_convert;
   
#define MAXTRY 100
   control.page_length=1000000;

   comm = argv[0];
   if( strstr(comm, "mdshak") )
     outsw = SHAK;
   else if (strstr(comm, "mdxyz") )
     outsw = XYZ;
   else if (strstr(comm, "mddcd") || strstr(comm, "mdvmd") )
     outsw = DCD;
   else
     outsw = OUTBIN;

   while( (c = getopt(argc, argv, "a:bo:cr:s:d:t:i:xhv") ) != EOF )
      switch(c)
      {
       case 'a': 
	 atom_sel = optarg;
	 break;
       case 'b':
	 outsw=OUTBIN;
	 break;
       case 'o':
	 if( freopen(optarg, "w", stdout) == NULL )
	    error("failed to open file \"%s\" for output", optarg);
	 break;
       case 'c':
	 cflg++;
	 break;
       case 'r':
	 if( intyp )
	    errflg++;
	 intyp = data_source = c;
	 filename = optarg;
	 break;
       case 's':
	 if( intyp )
	    errflg++;
	 intyp = data_source = c;
	 filename = optarg;
	 break;
       case 'd':
	 dump_name = optarg;
	 break;
       case 't':
	 dumplims = optarg;
	 break;
       case 'i':
	 insert = optarg;
	 break;
       case 'x':
	 outsw = XYZ;
	 break;
       case 'h':
	 outsw = SHAK;
	 break;
       case 'v':
	 outsw = DCD;
	 break;
       default:
       case '?':
	 errflg++;
      }

   if( errflg )
   {
      fprintf(stderr,
	      "Usage: %s [-x|-v] [-h] [-c] [-s sys-spec-file|-r restart-file] ",
	      comm);
      fputs("[-d dump-files] [-t s[-f[:n]]] [-o output-file]\n", stderr);
      exit(2);
   }

   if( dump_name )
      data_source = 'd';

   if(intyp == 0)
   {
      fputs("How do you want to  specify the simulated system?\n", stderr);
      fputs("Do you want to use a system specification file (1)", stderr);
      fputs(" or a restart file (2)\n", stderr);
      if( (ans_i = get_int("? ", 1, 2)) == EOF )
	 exit(2);
      intyp = ans_i-1 ? 'r': 's';
      if( intyp == 's' )
      {
	 fputs( "Do you need to skip 'control' information?\n", stderr);
	 if( (sym = get_sym("y or n? ","yYnN")) == 'y' || sym == 'Y')
	    cflg++;
      }

      if( (filename = get_str("File name? ")) == NULL )
	 exit(2);
   }

   switch(intyp)
   {
    case 's':
      if( (Fp = fopen(filename,"r")) == NULL)
	 error("Couldn't open sys-spec file \"%s\" for reading", filename);
      if( cflg )
      {
	 do
	 {
	    fscanf(Fp, "%s",line);
	    (void)strlower(line);
	 }
	 while(! feof(stdin) && strcmp(line,"end"));
      }
      read_sysdef(Fp, &sys, &species, &site_info, &potpar);
      qpf = qalloc(sys.nspecies);
      initialise_sysdef(&sys, species, site_info, qpf);
      break;
    case 'r':
      if( (Fp = fopen(filename,"rb")) == NULL)
	 error("Couldn't open restart file \"%s\" for reading -\n%s\n", 
	       filename, strerror(errno));
      re_re_header(Fp, &restart_header, &control);
      control.rdf_interval = 0;       /* Don't attempt to read RDF data */
      re_re_sysdef(Fp, restart_header.vsn, &sys, &species, &site_info, &potpar);
      break;
    default:
      error("Internal error - invalid input type", "");
   }
   allocate_dynamics(&sys, species);

   if( data_source == 0 )		/* If called interactively	      */
   {
      fputs( "Where is the configurational information kept?\n", stderr);
      if( intyp == 's' )
      {
	 fputs( "In a lattice start file(1) or a dump dataset(2)?\n", stderr);
	 if( (ans_i = get_int("? ", 1, 2)) == EOF)
	    exit(2);
	 data_source = ans_i-1 ? 'd' : 's';
      }
      else if( intyp == 'r' )
      {
	 fputs( "In a restart file(1) or a dump dataset(2)?\n", stderr);
	 if( (ans_i = get_int("? ", 1, 2)) == EOF)
	    exit(2);
	 data_source = ans_i-1 ? 'd' : 'r';
      }
   }

   switch(data_source)			/* To read configurational data	      */
   {
    case 's':				/* Lattice_start file		      */
	lattice_start(Fp, &sys, species, qpf);
	moldy_out(1, 1, 1, &sys, species, site_info, atom_sel, outsw, insert);
      break;
    case 'r':				/* Restart file			      */
	init_averages(sys.nspecies, restart_header.vsn,
		      control.roll_interval, control.roll_interval,
		      &av_convert);
	read_restart(Fp, restart_header.vsn, &sys, av_convert);
	moldy_out(1, 1, 1, &sys, species, site_info, atom_sel, outsw, insert);
      break;
    case 'd':				/* Dump dataset			      */
	if( dump_name == 0 )
	{
	   fputs("Enter canonical name of dump files (as in control)\n",stderr);
	   if( (dump_name = get_str("Dumps? ")) == NULL)
	      exit(2);
	}

	/*
	 *  Ensure that the dump limits start, finish, inc are set up,
	 *  either on command line or by user interaction.
	 */
	do
	{
	   rflag = 0;
	   if( dumplims == NULL )
	   {
	      fputs("Please specify range of dump records in form", stderr);
	      fputs(" in form start-finish:increment\n", stderr);
	      dumplims = get_str("s-f:n? ");
	   }
	   if( forstr(dumplims, &start, &finish, &inc) )
	   {
	      rflag++;
	      fputs("Invalid range for dump records \"", stderr);
	      fputs(dumplims, stderr);
	      fputs("\"\n", stderr);
	   }
	   if( start > finish || start < 0 || inc <= 0 )
	   {
	      rflag++;
	      fputs("Dump record limits must satisfy", stderr);
	      fputs(" finish >= start, start >= 0 and increment > 0\n", stderr);
	   }
	   if( rflag)
	   {
	      (void)free(dumplims);
	      dumplims = NULL;
	   }
	} while(rflag);   
		
	/*
	 * Allocate buffer for data
         */
	dump_size = DUMP_SIZE(~0)*sizeof(float);
	if( (dump_buf = (float*)malloc(dump_size)) == 0)
	   error("malloc failed to allocate dump record buffer (%d bytes)",
		 dump_size);
	/*
	 * Loop over dump records, ascertaining which file they are in
	 * and opening it if necessary.  Call output routine.
	 */
#if defined (HAVE_POPEN) 
	sprintf(dumpcommand,"dumpext -R%d -Q%d -b -c 0 -t %s %s",
		sys.nmols,sys.nmols_r, dumplims, dump_name);
	if( (Dp = popen(dumpcommand,"r")) == 0)
	   error("Failed to execute \'dumpext\" command - \n%s",
		 strerror(errno));
#else
	tempname = tmpnam((char*)0);
	sprintf(dumpcommand,"dumpext -R%d -Q%d -b -c 0 -t %s -o %s %s",
		sys.nmols,sys.nmols_r, dumplims, tempname, dump_name);
	system(dumpcommand);
	if( (Dp = fopen(tempname,"rb")) == 0)
	   error("Failed to open \"%s\"",tempname);
#endif
	for(irec = start; irec <= finish; irec+=inc)
	{
	   
	   if( fread(dump_buf, dump_size, 1, Dp) < 1 || ferror(Dp) )
              error("Error reading record %d in dump file - \n%s\n",
		    irec, strerror(errno));

	   dump_to_moldy(dump_buf, &sys);

	   moldy_out(iout++, irec, inc, &sys, species, site_info, atom_sel, outsw, insert);
#ifdef DEBUG
	   fprintf(stderr,"Sucessfully read dump record %d from file  \"%s\"\n",
		   irec%header.maxdumps, dump_name);
#endif
	}
#if defined (HAVE_POPEN) 
	pclose(Dp);
#else
	fclose(Dp);
	remove(tempname);
#endif
	break;
      default:
	break;
     }
   return 0;    
}
      

		   
			     
$EOD
$!
$CREATE msd.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1997 Craig Fisher
Copyright (C) 1988, 1992, 1993, 1997 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding! */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/msd.c,v 1.13 1998/01/28 09:55:05 keith Exp $";
#endif
/**************************************************************************************
 * msd    	Code for calculating mean square displacements of centres of mass     *
 *              of molecules from MolDy dump files.			              *
 *		Output in columnar form "x y z total" for successive time intervals.  *
 *		Selection of species using -g: 0 = species 1, 1 = species 2, etc.     *
 *		Default msd time intervals:			     	              *
 *                             1 to (total no. of dump slices-1)/2, step size 1       *
 *		Option -u outputs trajectory coordinates in columnar format           *
 *		"x y z" against time for each particle of selected species.           *
 *		nb. msd time intervals taken relative to extracted dump slices.       *
 ************************************************************************************** 
 *  Revision Log
 *  $Log: msd.c,v $
 *  Revision 1.13  1998/01/28 09:55:05  keith
 *  Changes HAS_POPEN to more natural HAVE_POPEN.
 *  Fixed minor portability problem with struct initialization.
 *
 *  Revision 1.13  1998/01/27 17:46:58  keith
 *  Fixed minor portability problem with struct initialization.
 *
 *  Revision 1.12  1998/01/09 11:34:14  keith
 *  Added casts to arralloc() calls for portability.
 *  Changed to "HAVE_POPEN" macro from system-specifics
 *
 *  Revision 1.11  1997/11/27 16:00:38  keith
 *  Lintified it to avoid compiler warnings and complaints from
 *  nervous users.
 *
 *  Revision 1.10  1997/11/26 10:06:00  keith
 *  Corrected usage message.
 *  Made -r and -s options mutually exclusive.
 *
 *  Revision 1.9  1997/10/15 13:13:09  keith
 *  Minor tirying up by CF.
 *
 *  Revision 1.8  1997/10/13  11:16:10  craig
 *  Removed unused variable declarations
 *
 *  Revision 1.8  1997/10/09  11:21:40  craig
 *  Option for renaming program "mdtraj" added for default trajectory calculation
 *  Option 'c' added to parameter list to skip control information
 *  Changed hmat allocation from arralloc to aalloc
 *  Removed freeing of dumplims which was causing crash
 *  Msd limits modified to cope with dump limits interval = 1 (special case)
 *
 *  Revision 1.7  1997/10/08 13:30:55  keith
 *  Fixed dump_buf mem free bug.
 *
 *  Revision 1.6  1997/08/12 14:03:05  keith
 *  Combined version to produce output for GNUPLOT or IDL
 *
 *  Revision 1.2  1997/07/16 14:20:47  craig
 *  Option for various output formats for trajectory coords
 *
 *  Revision 1.1  1997/07/14 15:36:24  keith
 *  Modified by KR.  MSD calc put into separate function and optimised
 *  for roughly 4x speedup.
 *
 *  Revision 1.0  1997/07/11 16:55:26  craig
 *  Initial revision
 *
 */
#include "defs.h"
#if defined(ANSI) || defined(__STDC__)
#include 
#else
#include 
#endif
#include 
#include 
#include "stdlib.h"
#include "stddef.h"
#include "string.h"
#include 
#include "structs.h"
#include "messages.h"
#if defined(ANSI) || defined(__STDC__)
gptr	*arralloc(size_mt,int,...); 	/* Array allocator		      */
#else
gptr	*arralloc();	        	/* Array allocator		      */
#endif

void	invert();
void	mat_vec_mul();
void	make_sites();
char	*strlower();
void	read_sysdef();
void	initialise_sysdef();
void	re_re_header();
void	re_re_sysdef();
void	allocate_dynamics();
void	lattice_start();
void	read_restart();
void	init_averages();
int	getopt();
gptr	*talloc();
FILE	*popen();
/*======================== Global vars =======================================*/
int ithread=0, nthreads=1;
static char  *comm;
#define MSD  0
#define TRAJ 1
#define GNUP 0
#define IDL  1
/******************************************************************************
 * Dummies of 'moldy' routines so that msd may be linked with moldy library   *
 ******************************************************************************/
void 	init_rdf()
{}
gptr *rdf_ptr()
{return 0;}
void new_lins()
{}
int lines_left()
{return 0;}
void new_page()
{}
void	new_line()
{
   (void)putchar('\n');
}
void	banner_page()
{}
void	note()
{}
void	conv_potentials()
{}
void	conv_control()
{}
/******************************************************************************
 *  message.   Deliver error message to possibly exiting.  It can be called   *
 *             BEFORE output file is opened, in which case outt to stderr.    *
 ******************************************************************************/
#if defined(ANSI) || defined(__STDC__)
#   undef  va_alist
#   define      va_alist int *nerrs, ...
#   ifdef va_dcl
#      undef va_dcl
#   endif
#   define va_dcl /* */
#endif
/*VARARGS*/
void    message(va_alist)
va_dcl
{
   va_list      ap;
   char         *buff;
   int          sev;
   char         *format;
   static char  *sev_txt[] = {" *I* "," *W* "," *E* "," *F* "};
#if defined(ANSI) || defined(__STDC__)
   va_start(ap, nerrs);
#else
   int          *nerrs;

   va_start(ap);
   nerrs = va_arg(ap, int *);
#endif
   buff  = va_arg(ap, char *);
   sev   = va_arg(ap, int);
   format= va_arg(ap, char *);

   (void)fprintf(stderr, "%s: ", comm);
   (void)vfprintf(stderr, format, ap);
   va_end(ap);
   fputc('\n',stderr);

   if(buff != NULL)                     /* null ptr means don't print buffer  */
   {
      (void)fprintf(stderr,"     buffer contents=\"%s\"",buff);
      fputc('\n',stderr);
   }
   if(sev >= ERROR && nerrs != NULL)
      (*nerrs)++;
   if(sev == FATAL)
      exit(3);
}

/******************************************************************************
 *  message.   Deliver error message to possibly exiting. 		      *
 ******************************************************************************/
#if defined(ANSI) || defined(__STDC__)
#undef  va_alist
#define	va_alist char *format, ...
#ifdef  va_dcl
#   undef  va_dcl
#endif
#define va_dcl /* */
#endif
/*VARARGS*/
void	error(va_alist)
va_dcl
{
   va_list	ap;
#if defined(ANSI) || defined(__STDC__)
   va_start(ap, format);
#else
   char		*format;

   va_start(ap);
   format= va_arg(ap, char *);
#endif

   (void)fprintf(stderr, "msd: ");
   (void)vfprintf(stderr, format, ap);
   fputc('\n',stderr);
   va_end(ap);

   exit(3);
}
static char * mystrdup(s)
char *s;
{
   char * t=malloc(strlen(s)+1);
   return t?strcpy(t,s):0;
}
/******************************************************************************
 * get_int().  Read an integer from stdin, issuing a prompt and checking      *
 * validity and range.  Loop until satisfied, returning EOF if appropriate.   *
 ******************************************************************************/
int get_int(prompt, lo, hi)
char	*prompt;
int	lo, hi;
{
   char		ans_str[80];
   int		ans_i, ans_flag;

   ans_flag = 0;
   while( ! feof(stdin) && ! ans_flag )
   {
      fputs(prompt, stderr);
      fflush(stderr);
      fgets(ans_str, sizeof ans_str, stdin);
      if( sscanf(ans_str, "%d", &ans_i) == 1 && ans_i >= lo && ans_i <= hi)
	 ans_flag++;
   }
   if( ans_flag )
      return(ans_i);
   else
      return(EOF);
}
/******************************************************************************
 * get_real().  Read an int from stdin, issuing a prompt and checking         *
 * validity and range.  Loop until satisfied, returning EOF if appropriate.   *
 ******************************************************************************/
int get_real(prompt, lo, hi)
char	*prompt;
real 	lo, hi;
{
   char		ans_str[80];
   int	ans_r; 
   int		ans_flag;
   ans_flag = 0;
   while( ! feof(stdin) && ! ans_flag )
   {
      fputs(prompt, stderr);
      fflush(stderr);
      fgets(ans_str, sizeof ans_str, stdin);
      if( sscanf(ans_str, "%d", &ans_r) == 1 && ans_r >= lo && ans_r <= hi)
	 ans_flag++;
   }
   if( ans_flag )
      return(ans_r);
   else
      return(EOF);
}
/******************************************************************************
 * get_sym().  Read a character from stdin and match to supplied set	      *
 ******************************************************************************/
int get_sym(prompt, cset)
char	*prompt;
char	*cset;
{
   char		ans_c, ans_str[80];
   int		ans_flag;

   ans_flag = 0;
   while( ! feof(stdin) && ! ans_flag )
   {
      fputs(prompt, stderr);
      fflush(stderr);
      fgets(ans_str, sizeof ans_str, stdin);
      if( sscanf(ans_str, " %c", &ans_c) == 1 && strchr(cset, ans_c))
	 ans_flag++;
   }
   if( ans_flag )
      return(ans_c);
   else
      return(EOF);
}
/******************************************************************************
 * get_str().  Read a string from stdin, issuing a prompt.		      *
 ******************************************************************************/
char	*get_str(prompt)
char	*prompt;
{
   char		ans_str[80];
   char		*str = malloc(80);
   int		ans_flag;

   ans_flag = 0;
   while( ! feof(stdin) && ! ans_flag )
   {
      fputs(prompt, stderr);
      fflush(stderr);
      fgets(ans_str, sizeof ans_str, stdin);
      if( sscanf(ans_str, "%s", str) == 1)
	 ans_flag++;
   }
   if( ans_flag )
      return(str);
   else
      return(NULL);
}
/******************************************************************************
 * forstr.  Parse string str of format s-f:n (final 2 parts optional),        *
 *          returning integer values of s,f,n. f defaults to s and n to 1     *
 ******************************************************************************/
int
forstr(instr, start, finish, inc)
char	*instr;
int	*start, *finish, *inc;
{
   char	*p, *pp, *str = mystrdup(instr);
   /* long strtol(); */
   
   if( (p = strchr(str,':')) != NULL)
   {
      *inc = strtol(p+1, &pp, 0);
      if( pp == p+1 )
	 goto limerr;
      *p = 0;
   }
   else
      *inc = 1;
   if( (p = strchr(str,'-')) != NULL)
   {
      *p = 0;
      *start = strtol(str, &pp, 0);
      if( pp == str )
	 goto limerr;
      *finish = strtol(p+1, &pp, 0);
      if( pp == p+1 )
	 goto limerr;
   }
   else
   {
      *start = *finish = strtol(str, &pp, 0);
      if( pp == str )
	 goto limerr;
   }
   if( *start > *finish || *start < 0 || *inc <= 0 )
   {
      fputs("Limits must satisfy", stderr);
      fputs(" finish >= start, start >= 0 and increment > 0\n", stderr);
      goto limerr;
   }
   return 0;
 limerr:
   return -1;
}
/******************************************************************************
 * range_in. Function for determining ranges of atom positions to include     *
 ******************************************************************************/
int
range_in(system, range, range_flag)
system_mt	*system;
real		range[3][2];
int		*range_flag;
{
   double	box[3];
   mat_mp	h = system->h;
   int		i;

   for( i=0; i<3; i++)
   {
       box[i] = sqrt(SQR(h[0][i]) + SQR(h[1][i]) + SQR(h[2][i]));

       if( range_flag[i])
          switch(i)
          {
           case 0:
             range[i][0] = get_real("Enter x minimum: ", -1*box[i],box[i]);
             range[i][1] = get_real("Enter x maximum: ", range[i][0],2*box[i]);
             break;
           case 1:
             range[i][0] = get_real("Enter y minimum: ", -1*box[i],box[i]);
             range[i][1] = get_real("Enter y maximum: ", range[i][0],2*box[i]);
             break;
           case 2:
             range[i][0] = get_real("Enter z minimum: ", -1*box[i],box[i]);
             range[i][1] = get_real("Enter z maximum: ", range[i][0],2*box[i]);
             break;
          }    
       else
       {
          range[i][0] = -1*box[i];
          range[i][1] = box[i];
       }
   }
   return 0;
}
/******************************************************************************
 * dump_to_moldy.  Fill the 'system' arrays with the dump data in 'buf' (see  *
 * dump.c for format), expanding floats to doubles if necessary.              *
 ******************************************************************************/
#define DUMP_SIZE(level)  (( (level & 1) + (level>>1 & 1) + (level>>2 & 1) ) * \
			            (3*sys.nmols + 4*sys.nmols_r + 9)+ \
			     (level>>3 & 1) * \
			            (3*sys.nmols + 3*sys.nmols_r + 9) +\
			     (level & 1))
void
dump_to_moldy(buf, system)
float	*buf;
system_mt *system;
{
   int i;
   float	*c_of_m = buf;
   float	*quat   = buf+3*system->nmols;
   float	*h      = buf+3*system->nmols + 4*system->nmols_r;
   mat_mt	hinv;

/* $dir no_recurrence */
   for(i = 0; i < system->nmols; i++)
   {
      system->c_of_m[i][0] = c_of_m[3*i];
      system->c_of_m[i][1] = c_of_m[3*i+1];
      system->c_of_m[i][2] = c_of_m[3*i+2]; 
   }
/* $dir no_recurrence */
   for(i = 0; i < system->nmols_r; i++)
   {
      system->quat[i][0] = quat[4*i];
      system->quat[i][1] = quat[4*i+1];
      system->quat[i][2] = quat[4*i+2];
      system->quat[i][3] = quat[4*i+3];
   }
/* $dir no_recurrence */
   for(i = 0; i < 3; i++)
   {
      system->h[i][0] = h[3*i];
      system->h[i][1] = h[3*i+1];
      system->h[i][2] = h[3*i+2];
   }
   invert(system->h,hinv);
   mat_vec_mul(hinv, system->c_of_m, system->c_of_m, system->nmols);
}
/******************************************************************************
 ******************************************************************************/
void mat_vec_mul3(m, vec, number)
int             number;         /* Number of vectors to be multiplied         */
real            m[3][3];        /* Matrix                                     */
vec_mt          *vec;           /* Output vector.  CAN BE SAME AS INPUT  (out)*/
{
   int i;
   register double        a0, a1, a2;

   for(i = 0; i < number; i++)
   {
      a0 = vec[i][0];  a1 = vec[i][1];  a2 = vec[i][2];

      vec[i][0] = m[0][0]*a0 + m[0][1]*a1 + m[0][2]*a2;
      vec[i][1] = m[1][0]*a0 + m[1][1]*a1 + m[1][2]*a2;
      vec[i][2] = m[2][0]*a0 + m[2][1]*a1 + m[2][2]*a2;
   }
}
/******************************************************************************
 * traj_con().  Connect molecular c_of_m`s into continuous trajectories       * 
 ******************************************************************************/
void
traj_con(species, prev_cofm, traj_cofm, sp_range)
spec_mt		species[];
vec_mt		*prev_cofm;
vec_mt		*traj_cofm;
int		sp_range[3];
{
   spec_mt	*spec;
   int		i, imol, ispec, totmol=0;
 
   for( ispec = sp_range[0], spec=species+sp_range[0]; ispec <= sp_range[1]; 
                                              ispec+=sp_range[2], spec+=sp_range[2])
     for( imol = 0; imol < spec->nmols; totmol++, imol++)
	if( prev_cofm == 0 ) 
	   for( i = 0; i < 3; i++)
	      traj_cofm[totmol][i] = spec->c_of_m[imol][i];
	else 
	   for( i = 0; i < 3; i++)
	      traj_cofm[totmol][i] = spec->c_of_m[imol][i] - 
		 floor(spec->c_of_m[imol][i]-prev_cofm[totmol][i]+0.5);
}
/******************************************************************************
 * traj_gnu().  Output routine for displaying trajectories                    *
 *		- coords vs time for each species/atom for GNUplot            * 
 ******************************************************************************/
void
traj_gnu(species, traj_cofm, nslices, range, sp_range)
spec_mt         species[];
vec_mt          **traj_cofm;
real            range[3][2];
int             nslices, sp_range[3];
{
   int          totmol=0, imol, i, itime;
   spec_mp      spec;

   for( spec = species+sp_range[0]; spec <= species+sp_range[1]; spec+=sp_range[2])
   {
     (void)printf("# %s\n",spec->name);
     for( imol = 0; imol < spec->nmols; totmol++, imol++)
     {
       if( traj_cofm[0][totmol][0] >= range[0][0] && traj_cofm[0][totmol][0] <= range[0][1] &&
          traj_cofm[0][totmol][1] >= range[1][0] && traj_cofm[0][totmol][1] <= range[1][1] &&
             traj_cofm[0][totmol][2] >= range[2][0] && traj_cofm[0][totmol][2] <= range[2][1])
          for( itime = 0; itime < nslices; itime++)
          {
             for( i = 0; i < 3; i++)
                 (void)printf("%f ",traj_cofm[itime][totmol][i]);
             (void)printf("\n");
          }
          (void)printf("\n\n");
     }
   }
   if( ferror(stdout) )
      error("Error writing output - \n%s\n", strerror(errno));
}
/******************************************************************************
 * traj_idl().  Output routine for displaying trajectories                    *
 *		- simple columnar format e.g. for IDL		              * 
 ******************************************************************************/
void
traj_idl(species, traj_cofm, nslices, range, sp_range)
spec_mt		species[];
vec_mt		**traj_cofm;
real		range[3][2];
int		nslices, sp_range[3];
{
   int		totmol, imol, i, itime;
   spec_mp	spec;

   for( itime = 0; itime < nslices; itime++)
   {
     totmol = 0;
     for( spec = species+sp_range[0]; spec <= species+sp_range[1]; spec+=sp_range[2])
     {
       for( imol = 0; imol < spec->nmols; totmol++, imol++)
         if( traj_cofm[0][totmol][0] >= range[0][0] && traj_cofm[0][totmol][0] <= range[0][1] &&
            traj_cofm[0][totmol][1] >= range[1][0] && traj_cofm[0][totmol][1] <= range[1][1] &&
               traj_cofm[0][totmol][2] >= range[2][0] && traj_cofm[0][totmol][2] <= range[2][1])
         {
           for( i = 0; i < 3; i++)
               (void)printf("%f ",traj_cofm[itime][totmol][i]);
         }
     }
     (void)printf("\n");
   }
   if( ferror(stdout) )
      error("Error writing output - \n%s\n", strerror(errno));
}
/***********************************************************************
 * msd_calc. Calculate msds from trajectory array		       *
 ***********************************************************************/    
void
msd_calc(species, sp_range, mstart, mfinish, minc, max_av, it_inc, traj_cofm, msd)
spec_mt		species[];
vec_mt		**traj_cofm;
real            ***msd;
int		sp_range[3];
int             mstart, mfinish, minc, max_av, it_inc;
{
   int it, irec, totmol, imsd, ispec, imol, nmols, i;
   spec_mp      spec;
   double       msdtmp, stmp;
   vec_mt	*tct0, *tct1;

   /* Outer loop for selecting initial time slice */
   for(it = 0; it <= (max_av-1)*it_inc; it+=it_inc)

      /* Inner loop for calculating displacement from initial slice */ 
      for(irec = mstart; irec <= mfinish; irec+=minc)
      {
	 imsd = (irec-mstart)/minc;
	 tct0 = traj_cofm[it];
	 tct1 = traj_cofm[it+irec];
	 for(i=0; i<3; i++)
	 {
	    totmol=0;
	    for( ispec = sp_range[0], spec = species; ispec <= sp_range[1];
		 spec += sp_range[2], ispec += sp_range[2])
	    {
	       nmols = spec->nmols;
	       msdtmp = 0.0;
	       for( imol = 0; imol < nmols; totmol++, imol++)
	       {
		  stmp = tct1[totmol][i] - tct0[totmol][i] ;
		  msdtmp += SQR(stmp);
	       }
	       msd[imsd][ispec][i] += msdtmp / nmols;
	    }
	 }
      }
}
/******************************************************************************
 * msd_out().  Output routine for displaying msd results                      *
 ******************************************************************************/
void
msd_out(species, msd, max_av, nmsd, sp_range)
spec_mt         *species;
real            ***msd;
int             max_av;
int             nmsd, sp_range[3];
{
   int          ispec, imsd, i;
   real         totmsd;
   spec_mp      spec;

   for( spec = species+sp_range[0], ispec = sp_range[0]; ispec <= sp_range[1];
                                   spec+=sp_range[2], ispec+=sp_range[2])
   {
       puts(spec->name);
       for( imsd = 0; imsd < nmsd; imsd++)
       {
         for( i=0, totmsd = 0; i<3; i++)
         {
           msd[imsd][ispec][i] /= max_av;
           totmsd += msd[imsd][ispec][i];
           (void)printf("%9.7f ", msd[imsd][ispec][i]);
         }
         (void)printf("%9.7f",totmsd);
         (void)printf("\n");
       }
   }
   if( ferror(stdout) )
      error("Error writing output - \n%s\n", strerror(errno));
}
/******************************************************************************
 * main().  Driver program for calculating trajectories/msds from MOLDY dumps *
 * Acceptable inputs are sys-spec files or restart files. Actual 	      *
 * configurational info must be read from dump files.			      *
 * Call: msd [-s sys-spec-file] [-r restart-file] [-d dump-file] 	      *
 * If not specified on command line, user is interrogated.		      *
 * Options [-x][-y][-z] prompt for limits in given direction to be applied    *
 *        when outputting trajectories. Molecules selected based on initial   *
 *	  positions.							      *
 ******************************************************************************/
contr_mt		control;

int
main(argc, argv)
int	argc;
char	*argv[];
{
   int	c, cflg = 0, ans_i, sym = 0;
   char 	line[80];
   extern char	*optarg;
   int		errflg = 0;
   int		intyp = 0;
   int		outsw = MSD, trajsw = GNUP;
   int		start, finish, inc;
   int		mstart, mfinish, minc;
   int		nslices;
   int		sp_range[3];
   int		dflag, iflag, sflag, mflag;
   int		irec, it_inc = 1;
   char		*filename = NULL, *dump_name = NULL;
   char		*dumplims = NULL, *speclims = NULL;
   char		*msdlims = NULL;
   char		*tempname;
   char		dumpcommand[256];
   int		dump_size;
   float	*dump_buf;
   FILE		*Fp, *Dp;
   restrt_mt	restart_header;
   system_mt	sys;
   spec_mt	*species;
   vec_mt 	**traj_cofm;
   mat_mt	*hmat;
   real		range[3][2];
   int		range_flag[3];
   site_mt	*site_info;
   pot_mt	*potpar;
   quat_mt	*qpf;
   contr_mt	control_junk;
   int          nmsd, max_av;
   real         ***msd;
   int		it;

#define MAXTRY 100
   control.page_length=1000000;
   range_flag[0] = range_flag[1] = range_flag[2] = 0;

   comm = argv[0];
   if( strstr(comm, "msd") )
      outsw = MSD;
   else if( strstr(comm, "mdtraj") )
      outsw = TRAJ;

   while( (c = getopt(argc, argv, "cr:s:d:t:m:i:g:o:w:uxyz") ) != EOF )
      switch(c)
      {
       case 'c':
         cflg++;
         break;
       case 'r':
	 if( intyp )
	    errflg++;
	 intyp = c;
	 filename = optarg;
	 break;
        case 's':
	 if( intyp )
	    errflg++;
	 intyp = c;
	 filename = optarg;
	 break;
       case 'd':
	 dump_name = optarg;
	 break;
       case 't':
	 dumplims = optarg;
         break;
       case 'm':
	 msdlims = optarg;
         break;
       case 'g':
	 speclims = optarg;
	 break;
       case 'i':
	 it_inc = atoi(optarg);
	 break;
       case 'o':
	 if( freopen(optarg, "w", stdout) == NULL )
	    error("failed to open file \"%s\" for output", optarg);
	 break;
       case 'u':
	 outsw = TRAJ;
	 break;
       case 'x':
         range_flag[0] = 1;
         break;
       case 'y':
	 range_flag[1] = 1;
         break;
       case 'z':
         range_flag[2] = 1;
         break;
       case 'w':
         trajsw = atoi(optarg);
         break;
       default:
       case '?':
	 errflg++;
      }

   if( errflg )
   {
      fprintf(stderr,
         "Usage: %s [-s sys-spec-file |-r restart-file] [-c] ",comm);
      fputs("[-d dump-files] [-t s[-f[:n]]] [-m s[-f[:n]]] ",stderr);
      fputs("[-g s[-f[:n]]] [-i init_inc] ",stderr);
      fputs("[-u] [-w] [-x] [-y] [-z] [-o output-file]\n",stderr);
      exit(2);
   }

   if(intyp == 0)
   {
      fputs("How do you want to specify the simulated system?\n", stderr);
      fputs("Do you want to use a system specification file (1)", stderr);
      fputs(" or a restart file (2)\n", stderr);
      if( (ans_i = get_int("? ", 1, 2)) == EOF )
	 exit(2);
      intyp = ans_i-1 ? 'r': 's';
      if( intyp == 's' )
      {
	 fputs( "Do you need to skip 'control' information?\n", stderr);
	 if( (sym = get_sym("y or n? ","yYnN")) == 'y' || sym == 'Y')
	    cflg++;
      }

      if( (filename = get_str("File name? ")) == NULL )
	 exit(2);
   }

   switch(intyp)
   {
    case 's':
      if( (Fp = fopen(filename,"r")) == NULL)
	 error("Couldn't open sys-spec file \"%s\" for reading", filename);
      if( cflg )
      {
	 do
	 {
	    fscanf(Fp, "%s",line);
	    (void)strlower(line);
	 }
	 while(! feof(stdin) && strcmp(line,"end"));
      }
      read_sysdef(Fp, &sys, &species, &site_info, &potpar);
      qpf = qalloc(sys.nspecies);
      initialise_sysdef(&sys, species, site_info, qpf);
      break;
    case 'r':
      if( (Fp = fopen(filename,"rb")) == NULL)
	 error("Couldn't open restart file \"%s\" for reading -\n%s\n", 
	       filename, strerror(errno));
      re_re_header(Fp, &restart_header, &control_junk);
      re_re_sysdef(Fp, restart_header.vsn, &sys, &species, &site_info, &potpar);
      break;
    default:
      error("Internal error - invalid input type", "");
   }
   allocate_dynamics(&sys, species);

  /* Dump dataset			      */
   if( dump_name == 0 )
   {
	fputs("Enter canonical name of dump files (as in control)\n",stderr);
	if( (dump_name = get_str("Dump file name? ")) == NULL)
	exit(2);
    }

  /*
   *  Ensure that the dump limits start, finish, inc are set up,
   *  either on command line or by user interaction.
   */
   do
   {
      dflag = 0;
      if( dumplims == NULL )
      {
          fputs("Please specify range of dump records in form", stderr);
          fputs(" start-finish:increment\n", stderr);
          dumplims = get_str("s-f:n? ");
      }
      if( forstr(dumplims, &start, &finish, &inc) )
      {
          dflag++;
          fputs("Invalid range for dump records \"", stderr);
          fputs(dumplims, stderr);
          fputs("\"\n", stderr);
      }
      if( dflag)
      {
          dumplims = NULL;
      } 
   } while(dflag);

   /* Ensure initial time slice increment is valid for given dump range */
   do
   {
      iflag = 0;
      if( it_inc <= 0)
      {
         fputs("Invalid initial time slice increment\n",stderr);
         fputs("Please specify initial time slice increment\n",stderr);
         it_inc=atoi(get_str("Increment? "));
         iflag++;
      }
   } while(iflag);

  /*
   * Ensure that the msd limits mstart, mfinish, minc are set up,
   * either on command line or by user interaction.
   */
   if( msdlims != NULL)
      do
      {
         mflag = 0;
         if( forstr(msdlims, &mstart, &mfinish, &minc) )
         {
           mflag++;
           fputs("Invalid range for msd intervals \"", stderr);
           fputs(msdlims, stderr);
           fputs("\"\n", stderr);
         }
         if( mstart*inc > finish-start || mfinish*inc > finish-start)
         {
            mflag++;
            fputs("MSD interval exceeds dump range\n",stderr);
         }
         if( mflag )
         {
            msdlims = NULL;
            fputs("Please specify msd intervals in form", stderr);
            fputs(" start-finish:increment\n", stderr);
            msdlims = get_str("s-f:n? ");
         }
      } while(mflag);
   else
   {
     /* Use default values for msd interval limits */
      mstart = 1;
      if( finish > start + 1)
          mfinish = (finish-start)/(2*inc); /* Midpoint of longest time span */
      else
          mfinish = 1;
      minc = 1;
   }

  /*
   * Ensure that the species selection limits sp_range are set up,
   * either on command line or by user interaction.
   */
   if( speclims != NULL)
   {
      do
      {
         sflag = 0;
         if( forstr(speclims, &(sp_range[0]), &(sp_range[1]), &(sp_range[2])))
         {  
	   sflag++;
           fputs("Invalid range for molecule selection \"", stderr);
	   fputs(speclims, stderr);
	   fputs("\"\n", stderr);
         }
         if( sp_range[1] > sys.nspecies-1)
         {
            sflag++;
            fputs("Molecule selection exceeds no. of species\n",stderr);
         }
         if( sflag )
         {
            speclims = NULL;
            fputs("Please specify molecule selection in form", stderr);
            fputs(" start-finish:increment\n", stderr);
            speclims = get_str("s-f:n? ");
         }
       } while(sflag);         
   }
   else
   {
      /* Use default values for molecule selection limits */
       sp_range[0] = 0;
       sp_range[1] = sys.nspecies-1;
       sp_range[2] = 1;
   } 
  /*
   * Allocate buffer for data
   */
   dump_size = DUMP_SIZE(~0)*sizeof(float);

   nslices = (finish-start)/inc+1; /* no. of time slices in traj_cofm */

  /* Allocate memory for trajectory data and zero */
   traj_cofm = (vec_mt**)arralloc(sizeof(vec_mt),2,0,nslices-1,0,sys.nmols-1);
   zero_real(traj_cofm[0], nslices*sys.nmols*3);

  /* Allocate array to store unit cell matrices */
   hmat = aalloc(nslices, mat_mt);

   if( (dump_buf = (float*)malloc(dump_size)) == 0)
      error("malloc failed to allocate dump record buffer (%d bytes)",
          dump_size);
#if defined (HAS_POPEN) 
   sprintf(dumpcommand,"dumpext -R%d -Q%d -b -c 0 -t %d-%d:%d %s",
        sys.nmols, sys.nmols_r, start, finish, inc, dump_name);
   
   if( (Dp = popen(dumpcommand,"r")) == 0)
        error("Failed to execute \'dumpext\" command - \n%s",
            strerror(errno));
#else
   tempname = tmpnam((char*)0);
   sprintf(dumpcommand,"dumpext -R%d -Q%d -b -c 0 -t %d-%d:%d -o %s %s",
         sys.nmols,sys.nmols_r, start, finish, inc, tempname, dump_name);
   system(dumpcommand);
   if( (Dp = fopen(tempname,"rb")) == 0)
        error("Failed to open \"%s\"",tempname);
#endif

/* Loop for calculating trajectories from current and previous time slices */ 
   for(irec = 0; irec <= finish-start; irec+=inc)
   {
        if( fread(dump_buf, dump_size, 1, Dp) < 1 || ferror(Dp) )
           error("Error reading record %d in dump file - \n%s\n",
              irec, strerror(errno));
        dump_to_moldy(dump_buf, &sys);  /*read dump data */

	memcpy(hmat[irec/inc], sys.h, sizeof(mat_mt));

        if( irec == 0)
	{
          range_in(&sys, range, range_flag);
          traj_con(species, (vec_mt*)0, traj_cofm[irec/inc], sp_range);
	}
	else
          traj_con(species, traj_cofm[irec/inc-1], traj_cofm[irec/inc], sp_range);

#ifdef DEBUG
        fprintf(stderr,"Sucessfully read dump record %d from file  \"%s\"\n",
	   irec, dump_name);
#endif
   }
   xfree(dump_buf);

#if defined (HAS_POPEN) 
   pclose(Dp);
#else
   fclose(Dp);
   remove(tempname);
#endif

/* Convert trajectories from frac coords to Cartesian coords */
   for( it = 0; it < nslices; it++)
      mat_vec_mul3(hmat[it], traj_cofm[it], sys.nmols);

/*
 * Output either msd values or trajectory coords
 */
   if( outsw == MSD)
   {
  /* Calculate msd parameters */
     nmsd = (mfinish-mstart)/minc+1; /* No of msd time intervals */
     max_av = (nslices - mfinish)/it_inc; /* Max no of msd calcs to average over */

  /* Allocate memory for msd array and zero */
     msd = (real***)arralloc(sizeof(real),3,0,nmsd-1,0,sys.nspecies-1,0,2);
     zero_real(msd[0][0],nmsd*sys.nspecies*3);

  /* Calculate and print msd values */
     msd_calc(species, sp_range, mstart, mfinish, minc, max_av, it_inc, traj_cofm, msd);
     msd_out(species, msd, max_av, nmsd, sp_range);
   }
   else /* Otherwise output trajectories in selected format */
     switch(trajsw)
     {
       case IDL:
          traj_idl(species, traj_cofm, nslices, range, sp_range);
          break;
       case GNUP:
       default:
	  traj_gnu(species, traj_cofm, nslices, range, sp_range);
     }
   return 0;    
}
$EOD
$!
$CREATE mdavpos.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1997 Craig Fisher
Copyright (C) 1988, 1992, 1993, 1997 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding! */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/mdavpos.c,v 2.4 1998/01/28 09:55:37 keith Exp $";
#endif
/**************************************************************************************
 * mdavpos    	code for calculating mean positions of                                *       
 *              molecules and average box dimensions from MolDy dump files            *
 ************************************************************************************** 
 *  Revision Log
 *  $Log: mdavpos.c,v $
 *  Revision 2.4  1998/01/28 09:55:37  keith
 *  Changed to "HAVE_POPEN" macro from system-specifics
 *
 *  Revision 2.4  1998/01/09 11:35:11  keith
 *  Changed to "HAVE_POPEN" macro from system-specifics.
 *
 *  Revision 2.3  1997/11/26 10:08:29  keith
 *  Corrected usage message.
 *  Made -r and -s options mutually exclusive.
 *
 *  Revision 2.2  1997/10/15 13:12:07  keith
 *  Fixed for polyatomics - CF
 *
 *  Revision 2.1  1997/10/13 10:55:13  craig
 *  Removed declarations of unused variables
 *
 *  Revision 2.1  1997/10/8 15:05:24  craig
 *  Correctly initialised p_f_sites for monatomic species
 *  Moved constant species quantities from copy_spec to init_spec
 *
 *  Revision 2.0  1997/10/7 16:41:48  craig
 *  Major corrections to polyatomic and framework calculations
 *
 *  Revision 1.5  1997/10/3 16:50:44  craig
 *  Schakal format set as default output
 *  Option 'c' added to parameter list to skip control information
 *  Removed freeing of dumplims which was causing crash
 *  Initialisation of p_f_sites and quaternion arrays for polyatomic species added
 *
 *  Revision 1.4  1997/08/15 15:20:10  craig
 *  Init_h function replaced with call to memcpy
 *  Calculation now performed entirely in scaled coords
 *  Error in shakal_out corrected - outputs scaled coords instead of real coords 
 *  Centre_mass and shift functions called correctly
 *
 *  Revision 1.3  1997/08/12 14:03:53  keith
 *  Fixed minor bugs in start/finish timeslice code
 *
 *  Revision 1.2  1997/07/10 11:15:23  craig
 *  Options for different output formats added
 *
 *  Revision 1.1  1997/01/27 19:06:12  craig 
 *  Initial revision
 *
 */
#include "defs.h"
#if defined(ANSI) || defined(__STDC__)
#include 
#else
#include 
#endif
#include 
#include 
#include "stdlib.h"
#include "stddef.h"
#include "string.h"
#include 
#include "structs.h"
#include "messages.h"
#if defined(ANSI) || defined(__STDC__)
gptr	*arralloc(size_mt,int,...); 	/* Array allocator */
#else
gptr	*arralloc();	        	/* Array allocator */
#endif

void	invert();
void	mat_vec_mul();
void	make_sites();
char	*strlower();
void	read_sysdef();
void	initialise_sysdef();
void	re_re_header();
void	re_re_sysdef();
void	allocate_dynamics();
void	lattice_start();
void	read_restart();
void	init_averages();
int	getopt();
gptr	*talloc();
FILE	*popen();
/*======================== Global vars =======================================*/
int ithread=0, nthreads=1;
contr_mt                control;
#define SHAK 0
#define PDB 1
#define XYZ 2
/******************************************************************************
 * Dummies of moldy routines so that mdavpos may be linked with moldy library *
 ******************************************************************************/
void 	init_rdf()
{}
gptr *rdf_ptr()
{return 0;}
void new_lins()
{}
int lines_left()
{return 0;}
void new_page()
{}
void	new_line()
{
   (void)putchar('\n');
}
void	banner_page()
{}
void	note()
{}
void	conv_potentials()
{}
void	conv_control()
{}
/******************************************************************************
 *  message.   Deliver error message to possibly exiting.  It can be called   *
 *             BEFORE output file is opened, in which case outt to stderr.    *
 ******************************************************************************/
#if defined(ANSI) || defined(__STDC__)
#   undef  va_alist
#   define      va_alist int *nerrs, ...
#   ifdef va_dcl
#      undef va_dcl
#   endif
#   define va_dcl /* */
#endif
/*VARARGS*/
void    message(va_alist)
va_dcl
{
   va_list      ap;
   char         *buff;
   int          sev;
   char         *format;
   static char  *sev_txt[] = {" *I* "," *W* "," *E* "," *F* "};
#if defined(ANSI) || defined(__STDC__)
   va_start(ap, nerrs);
#else
   int          *nerrs;

   va_start(ap);
   nerrs = va_arg(ap, int *);
#endif
   buff  = va_arg(ap, char *);
   sev   = va_arg(ap, int);
   format= va_arg(ap, char *);

   (void)fprintf(stderr, "mdavpos: ");
   (void)vfprintf(stderr, format, ap);
   va_end(ap);
   fputc('\n',stderr);

   if(buff != NULL)                     /* null ptr means don't print buffer  */
   {
      (void)fprintf(stderr,"     buffer contents=\"%s\"",buff);
      fputc('\n',stderr);
   }
   if(sev >= ERROR && nerrs != NULL)
      (*nerrs)++;
   if(sev == FATAL)
      exit(3);
}

/******************************************************************************
 *  message.   Deliver error message to possibly exiting.                     *
 ******************************************************************************/
#if defined(ANSI) || defined(__STDC__)
#undef  va_alist
#define	va_alist char *format, ...
#ifdef  va_dcl
#   undef  va_dcl
#endif
#define va_dcl /* */
#endif
/*VARARGS*/
void	error(va_alist)
va_dcl
{
   va_list	ap;
#if defined(ANSI) || defined(__STDC__)
   va_start(ap, format);
#else
   char		*format;

   va_start(ap);
   format= va_arg(ap, char *);
#endif

   (void)fprintf(stderr, "mdavpos: ");
   (void)vfprintf(stderr, format, ap);
   fputc('\n',stderr);
   va_end(ap);

   exit(3);
}
static char * mystrdup(s)
char *s;
{
   char * t=malloc(strlen(s)+1);
   return t?strcpy(t,s):0;
}
/******************************************************************************
 * get_int().  Read an integer from stdin, issuing a prompt and checking      *
 * validity and range.  Loop until satisfied, returning EOF if appropriate.   *
 ******************************************************************************/
int get_int(prompt, lo, hi)
char	*prompt;
int	lo, hi;
{
   char		ans_str[80];
   int		ans_i, ans_flag;

   ans_flag = 0;
   while( ! feof(stdin) && ! ans_flag )
   {
      fputs(prompt, stderr);
      fflush(stderr);
      fgets(ans_str, sizeof ans_str, stdin);
      if( sscanf(ans_str, "%d", &ans_i) == 1 && ans_i >= lo && ans_i <= hi)
	 ans_flag++;
   }
   if( ans_flag )
      return(ans_i);
   else
      return(EOF);
}
/******************************************************************************
 * get_sym().  Read a character from stdin and match to supplied set          *
 ******************************************************************************/
int get_sym(prompt, cset)
char	*prompt;
char	*cset;
{
   char		ans_c, ans_str[80];
   int		ans_flag;

   ans_flag = 0;
   while( ! feof(stdin) && ! ans_flag )
   {
      fputs(prompt, stderr);
      fflush(stderr);
      fgets(ans_str, sizeof ans_str, stdin);
      if( sscanf(ans_str, " %c", &ans_c) == 1 && strchr(cset, ans_c))
	 ans_flag++;
   }
   if( ans_flag )
      return(ans_c);
   else
      return(EOF);
}
/******************************************************************************
 * get_str().  Read a string from stdin, issuing a prompt                     *
 ******************************************************************************/
char	*get_str(prompt)
char	*prompt;
{
   char		ans_str[80];
   char		*str = malloc(80);
   int		ans_flag;

   ans_flag = 0;
   while( ! feof(stdin) && ! ans_flag )
   {
      fputs(prompt, stderr);
      fflush(stderr);
      fgets(ans_str, sizeof ans_str, stdin);
      if( sscanf(ans_str, "%s", str) == 1)
	 ans_flag++;
   }
   if( ans_flag )
      return(str);
   else
      return(NULL);
}
/******************************************************************************
 * forstr.  Parse string str of format s-f:n (final 2 parts optional),        *
 *          returning integer values of s,f,n. f defaults to s and n to 1     *
 ******************************************************************************/
int
forstr(instr, start, finish, inc)
char	*instr;
int	*start, *finish, *inc;
{
   char	*p, *pp, *str = mystrdup(instr);
   
   if( (p = strchr(str,':')) != NULL)
   {
      *inc = strtol(p+1, &pp, 0);
      if( pp == p+1 )
	 goto limerr;
      *p = 0;
   }
   else
      *inc = 1;
   if( (p = strchr(str,'-')) != NULL)
   {
      *p = 0;
      *start = strtol(str, &pp, 0);
      if( pp == str )
	 goto limerr;
      *finish = strtol(p+1, &pp, 0);
      if( pp == p+1 )
	 goto limerr;
   }
   else
   {
      *start = *finish = strtol(str, &pp, 0);
      if( pp == str )
	 goto limerr;
   }
   if( *start > *finish || *start < 0 || *inc <= 0 )
   {
      fputs("Limits must satisfy", stderr);
      fputs(" finish >= start, start >= 0 and increment > 0\n", stderr);
      goto limerr;
   }
   return 0;
 limerr:
   return -1;
}
/******************************************************************************
 * dump_to_moldy.  Fill the 'system' arrays with the dump data in 'buf' (see  *
 * dump.c for format), expanding floats to doubles if necessary.              *
 ******************************************************************************/
#define DUMP_SIZE(level)  (( (level & 1) + (level>>1 & 1) + (level>>2 & 1) ) * \
           (3*sys.nmols + 4*sys.nmols_r + 9)+ (level>>3 & 1) * \
           (3*sys.nmols + 3*sys.nmols_r + 9) + (level & 1))
void
dump_to_moldy(buf, system)
float	*buf;
system_mt *system;
{
   int i;
   float	*c_of_m = buf;
   float	*quat   = buf+3*system->nmols;
   float	*h      = buf+3*system->nmols + 4*system->nmols_r;
   mat_mt	hinv;

/* $dir no_recurrence */
   for(i = 0; i < system->nmols; i++)
   {
      system->c_of_m[i][0] = c_of_m[3*i];
      system->c_of_m[i][1] = c_of_m[3*i+1];
      system->c_of_m[i][2] = c_of_m[3*i+2]; 
   }
/* $dir no_recurrence */
   for(i = 0; i < system->nmols_r; i++)
   {
      system->quat[i][0] = quat[4*i];
      system->quat[i][1] = quat[4*i+1];
      system->quat[i][2] = quat[4*i+2];
      system->quat[i][3] = quat[4*i+3];
   }
/* $dir no_recurrence */
   for(i = 0; i < 3; i++)
   {
      system->h[i][0] = h[3*i];
      system->h[i][1] = h[3*i+1];
      system->h[i][2] = h[3*i+2];
   }
   invert(system->h, hinv);
   mat_vec_mul(hinv, system->c_of_m, system->c_of_m, system->nmols);
}
/******************************************************************************
 ******************************************************************************/
void mat_vec_mul3(m, vec, number)
int             number;         /* Number of vectors to be multiplied         */
real            m[3][3];        /* Matrix                                     */
real            **vec;          /* Output vector.  CAN BE SAME AS INPUT  (out)*/
{
   int i;
   register double        a0, a1, a2;
   
   for(i = 0; i < number; i++)
   {
      a0 = vec[0][i];  a1 = vec[1][i];  a2 = vec[2][i];
      
      vec[0][i] = m[0][0]*a0 + m[0][1]*a1 + m[0][2]*a2;
      vec[1][i] = m[1][0]*a0 + m[1][1]*a1 + m[1][2]*a2;
      vec[2][i] = m[2][0]*a0 + m[2][1]*a1 + m[2][2]*a2;
   }
}
/******************************************************************************
 * traj_con().  Connect molecular c_of_m's into continuous trajectories       * 
 ******************************************************************************/
void
traj_con(system, species, prev_slice)
system_mt	*system;
spec_mt		species[];
spec_mt		prev_slice[];
{
   spec_mt	*spec;
   int		i, imol;

   for(spec = species; spec < species+system->nspecies; prev_slice++, spec++) 
      for( imol=0; imolnmols; imol++)
        for (i = 0; i < 3; i++)
             spec->c_of_m[imol][i] = spec->c_of_m[imol][i] - floor(
                (spec->c_of_m[imol][i]-prev_slice->c_of_m[imol][i])+0.5);
}
/******************************************************************************
 * shakal_out().  Write a system configuration to stdout in the form of an    *
 * input data file for the graphics program SCHAKAL88.                        *
 ******************************************************************************/
void
schakal_out(system, site_info, insert, avpos, avh)
system_mt       *system;
spec_mt         avpos[];
site_mt         site_info[];
char            *insert;
mat_mp		avh;
{
   double       **site = (double**)arralloc(sizeof(double),2,
                                            0,2,0,system->nsites-1);
   spec_mt      *spec;
   double       a, b, c, alpha, beta, gamma;
   mat_mp	h = avh;
   mat_mt       hinv;
   int          imol, isite, is;

   invert(h,hinv);

   a = sqrt(SQR(h[0][0]) + SQR(h[1][0]) + SQR(h[2][0]));
   b = sqrt(SQR(h[0][1]) + SQR(h[1][1]) + SQR(h[2][1]));
   c = sqrt(SQR(h[0][2]) + SQR(h[1][2]) + SQR(h[2][2]));
   alpha = 180/PI*acos((h[0][1]*h[0][2]+h[1][1]*h[1][2]+h[2][1]*h[2][2])/b/c);
   beta  = 180/PI*acos((h[0][0]*h[0][2]+h[1][0]*h[1][2]+h[2][0]*h[2][2])/a/c);
   gamma = 180/PI*acos((h[0][0]*h[0][1]+h[1][0]*h[1][1]+h[2][0]*h[2][1])/a/b);

   printf("CELL %f %f %f %f %f %f\n", a, b, c, alpha, beta, gamma);
   for(spec = avpos; spec < avpos+system->nspecies; spec++)
   {
      make_sites(h, spec->c_of_m, spec->quat, spec->p_f_sites,
                 spec->framework, site, spec->nmols, spec->nsites);

      mat_vec_mul3(hinv, site, spec->nsites*spec->nmols);

      isite = 0;
      for(imol = 0; imol < spec->nmols; imol++)
      {
         puts("MOL");
         for(is = 0; is < spec->nsites; is++)
         {
            if(fabs(site_info[spec->site_id[is]].mass) != 0)
               (void)printf("ATOM %-8s %7.4f %7.4f %7.4f\n",
                            site_info[spec->site_id[is]].name,
                            site[0][isite], site[1][isite], site[2][isite]);
            isite++;
         }
      }
   }
   if( insert != NULL)
      (void)printf("%s\n", insert);

   (void)printf("END 1\n");
   if( ferror(stdout) )
      error("Error writing output - \n%s\n", strerror(errno));
}
/******************************************************************************
 * pdb_out().  Write a system configuration to stdout in the form of a        *
 * Brookhaven Protein Data Bank (pdb) file                                    *
 ******************************************************************************/
void
pdb_out(system, site_info, insert, avpos, avh)
system_mt	*system;
site_mt		site_info[];
char		*insert;
spec_mt		avpos[];
mat_mp		avh;
{
   double	**site = (double**)arralloc(sizeof(double),2,
                                            0,2,0,system->nsites-1);
   mat_mp       h = avh;
   spec_mt	*spec;
   double	a,b,c, alpha, beta, gamma;
   int		imol, isite, itot=1, ispec=1;
   int		is;
   
   a = sqrt(SQR(h[0][0]) + SQR(h[1][0]) + SQR(h[2][0]));
   b = sqrt(SQR(h[0][1]) + SQR(h[1][1]) + SQR(h[2][1]));
   c = sqrt(SQR(h[0][2]) + SQR(h[1][2]) + SQR(h[2][2]));
   alpha = 180/PI*acos((h[0][1]*h[0][2]+h[1][1]*h[1][2]+h[2][1]*h[2][2])/b/c);
   beta  = 180/PI*acos((h[0][0]*h[0][2]+h[1][0]*h[1][2]+h[2][0]*h[2][2])/a/c);
   gamma = 180/PI*acos((h[0][0]*h[0][1]+h[1][0]*h[1][1]+h[2][0]*h[2][1])/a/b);

/* Write the pdb header */
   (void)printf("CRYST1   %6.3f   %6.3f   %6.3f  %5.2f  %5.2f  %5.2f P 1\n",
          a,b,c,alpha,beta,gamma);

   for(spec = avpos; spec < avpos+system->nspecies; ispec++, spec++)
   {
     make_sites(h, spec->c_of_m, spec->quat, spec->p_f_sites,
           spec->framework, site, spec->nmols, spec->nsites);

     isite = 0;
     for(imol = 0; imol < spec->nmols; imol++)
     {
       for(is = 0; is < spec->nsites; is++)
       {
         if(fabs(site_info[spec->site_id[is]].mass) != 0)
            (void)printf("HETATM%5d %2s%d  NONE    1     %7.3f %7.3f %7.3f  1.00  0.00\n",
               itot, site_info[spec->site_id[is]].name, ispec,
                  site[0][isite], site[1][isite], site[2][isite]);
         isite++;
         itot++;
       }
     }
   }
   (void)printf("TER              %d     NONE    1\n",itot);
   (void)printf("END\n");
   if( insert != NULL)
      (void)printf("%s\n", insert);

   if( ferror(stdout) )
      error("Error writing output - \n%s\n", strerror(errno));
}
/******************************************************************************
 * xyz_out().  Write a system configuration to stdout in the form of an       *
 * input data file for the graphics program XYZ (rasmol -xyz file)            *
 ******************************************************************************/
void
xyz_out(system, site_info, insert, avpos, avh)
system_mt       *system;
spec_mt         avpos[];
mat_mp		avh;
site_mt         site_info[];
char            *insert;
{
   double       **site = (double**)arralloc(sizeof(double),2,
                                            0,2,0,system->nsites-1);
   spec_mt      *spec;
   mat_mt       hinv;
   int          imol, isite, is;

   invert(avh, hinv);

/* We count the number of atoms */
   isite=0;
   for(spec = avpos; spec < avpos+system->nspecies; spec++)
   {
      for(imol = 0; imol < spec->nmols; imol++)
      {
         for(is = 0; is < spec->nsites; is++)
         {
            if(fabs(site_info[spec->site_id[is]].mass) != 0)
              isite++;
         }
      }
   }
/* Now we write the xyz header */
   (void)printf("%d\n",isite);
/* It would be nice to have here the real title */
   (void)printf("%s\n",control.title);

   for(spec = avpos; spec < avpos+system->nspecies; spec++)
   {
      make_sites(avh, spec->c_of_m, spec->quat, spec->p_f_sites,
                 spec->framework, site, spec->nmols, spec->nsites);
      isite = 0;
      for(imol = 0; imol < spec->nmols; imol++)
      {
         for(is = 0; is < spec->nsites; is++)
         {
            if(fabs(site_info[spec->site_id[is]].mass) != 0)
               (void)printf("%-8s %7.4f %7.4f %7.4f\n",
                            site_info[spec->site_id[is]].name,
                            site[0][isite], site[1][isite], site[2][isite]);
            isite++;
         }
      }
   }

   if( insert != NULL)
      (void)printf("%s\n", insert);

   if( ferror(stdout) )
      error("Error writing output - \n%s\n", strerror(errno));
}
/******************************************************************************
 * Centre_mass.  Shift system centre of mass to origin (in discrete steps),   *
 ******************************************************************************/
void
centre_mass(species, nspecies, c_of_m)
spec_mt         species[];
int             nspecies;
vec_mt          c_of_m;
{
   double       mass;
   spec_mt      *spec;
   int          imol;
   vec_mt       *s_c_of_m;

   mass = c_of_m[0] = c_of_m[1] = c_of_m[2] = 0.0;
   for(spec = species; spec < species + nspecies; spec++ )
   {
      s_c_of_m = spec->c_of_m;
      for(imol = 0; imol < spec->nmols; imol++)
      {
         c_of_m[0] += spec->mass*s_c_of_m[imol][0];
         c_of_m[1] += spec->mass*s_c_of_m[imol][1];
         c_of_m[2] += spec->mass*s_c_of_m[imol][2];
      }
      mass += spec->nmols*spec->mass;
   }

   c_of_m[0] /= mass;
   c_of_m[1] /= mass;
   c_of_m[2] /= mass;
   c_of_m[0] = floor(c_of_m[0]+0.5);
   c_of_m[1] = floor(c_of_m[1]+0.5);
   c_of_m[2] = floor(c_of_m[2]+0.5);
}
/******************************************************************************
 * Shift.  Translate all co-ordinates.                                        *
 ******************************************************************************/
void    shift(r, nmols, s)
vec_mt  r[];
int     nmols;
vec_mt  s;
{
   int imol;
   for(imol = 0; imol < nmols; imol++)
   {
      r[imol][0] -= s[0];
      r[imol][1] -= s[1];
      r[imol][2] -= s[2];
   }
}
/******************************************************************************
 * moldy_out.  Select output routine and handle file open/close               *
 * Translate system relative to either centre of mass or posn of framework.   *
 ******************************************************************************/
void
moldy_out(system, site_info, insert, avpos, avh, outsw)
system_mt       *system;
spec_mt         avpos[];
site_mt         site_info[];
mat_mp		avh;
int             outsw;
char            *insert;
{
   spec_mp      spec, frame_spec  = NULL;
   vec_mt       c_of_m;

   for( spec = avpos; spec < avpos+system->nspecies; spec++)
      if( spec->framework )
         frame_spec = spec;

   if( frame_spec != NULL )
      for( spec = avpos; spec < avpos+system->nspecies; spec++)
         shift(spec->c_of_m, spec->nmols, frame_spec->c_of_m[0]);
   else
   {
      centre_mass(avpos, system->nspecies, c_of_m);
      for( spec = avpos; spec < avpos+system->nspecies; spec++) 
         shift(spec->c_of_m, spec->nmols, c_of_m);
   } 
   switch (outsw)
   {
    case PDB:
      pdb_out(system, site_info, insert, avpos, avh);
      break;
    case XYZ:
      xyz_out(system, site_info, insert, avpos, avh); 
      break;
    default:
    case SHAK:
      schakal_out(system, site_info, insert, avpos, avh);
      break;
   }
}
/******************************************************************************
 * copy_spec().  Duplicate species data in another array    	              *
 ******************************************************************************/
void
copy_spec(system, species, dupl_spec)
system_mt	*system;
spec_mt		species[];
spec_mt		dupl_spec[];
{
   spec_mt	*spec;
   int		imol, i, j;

   for(spec = species; spec < species+system->nspecies; dupl_spec++,spec++)
   {
      if( spec->rdof > 0)	/* polyatomic (non-framework) species */
      {
         for( imol=0; imol < spec->nmols; imol++)
             for( j=0; j<4; j++)
                 dupl_spec->quat[imol][j] = spec->quat[imol][j];
      } 
      for(imol=0; imol < spec->nmols; imol++)
         for( i=0; i<3; i++)
             dupl_spec->c_of_m[imol][i] = spec->c_of_m[imol][i];
   }
}
/******************************************************************************
 * init_species().  Create arrays of c_of_m`s for each molecule of species    *
 ******************************************************************************/
void
init_species(system, species, init_spec)
system_mt	*system;
spec_mt		species[];
spec_mt		init_spec[];
{
   spec_mt	*spec;
   int		i;
   for(spec = species; spec < species+system->nspecies; init_spec++,spec++)
   {
   /* Allocate space for data */
        init_spec->site_id = ialloc(spec->nsites);
        init_spec->p_f_sites = ralloc(spec->nsites);
        init_spec->c_of_m = ralloc(spec->nmols);
        if( spec->rdof > 0)
           init_spec->quat = qalloc(spec->nmols);
        else
           init_spec->quat = 0;

   /* Duplicate non-varying quantities */
        init_spec->nmols = spec->nmols;
        init_spec->nsites = spec->nsites;
        init_spec->framework = spec->framework;
        init_spec->mass = spec->mass;
        init_spec->rdof = spec->rdof;
        init_spec->site_id = spec->site_id;
        init_spec->p_f_sites = spec->p_f_sites;
        for( i=0; i<32; i++)
           init_spec->name[i] = spec->name[i];
   }
}
/******************************************************************************
 * summate().  Summate positions of each species                              *
 ******************************************************************************/
void
summate(system, species, avpos, avh)
system_mt	*system;
spec_mt		species[];
spec_mt		avpos[];
mat_mp		avh;

{
   spec_mt	*spec;
   int		i, j, imol;
 
   for( i =0; i<3; i++)
       for( j = 0; j < 3; j++)
   	   avh[i][j] += system->h[i][j];

   for(spec = species; spec < species+system->nspecies; avpos++, spec++)
   {         
      for( imol=0; imolnmols; imol++)
         for( i=0; i<3; i++)
            avpos->c_of_m[imol][i] += spec->c_of_m[imol][i];

      if( spec->rdof > 1)
         for(imol = 0; imol < spec->nmols; imol++)
            for( j = 0; j< 4; j++)
                avpos->quat[imol][j] += spec->quat[imol][j];
   }
}
/******************************************************************************
 * average().  Divide total values by no. of timesteps for each molecule      *
 ******************************************************************************/
void
average(system, avpos, avh, nav)
system_mt	*system;
spec_mt		avpos[];
mat_mp		avh;
int		nav;
{
   int		i, j, imol;

   for( i = 0; i < 3; i++)
      for( j = 0; j < 3; j++)
          avh[i][j] /= nav;
 
   for(i = 0; i< system->nspecies; avpos++, i++)
   {
      for(imol = 0; imol < avpos->nmols; imol++)
         for( j = 0; j < 3; j++) 
            avpos->c_of_m[imol][j] /= nav;

      if( avpos->rdof > 1)
         for(imol = 0; imol < avpos->nmols; imol++)
            for( j = 0; j< 4; j++)
                avpos->quat[imol][j] /= nav;
   }
}
/******************************************************************************
 * main().   Driver program for calculating mean pos. from MOLDY dump files   *
 * Acceptable inputs are sys-spec files or restart files. Actual              *
 * configurational info must be read from dump files.                         *
 * Call: mdavpos [-s sys-spec-file] [-r restart-file].                        *
 * If neither specified on command line, user is interrogated.                *
 ******************************************************************************/
int
main(argc, argv)
int	argc;
char	*argv[];
{
   int	c, cflg = 0, ans_i, sym = 0;
   char 	line[80];
   extern char	*optarg;
   int		errflg = 0;
   int		intyp = 0;
   int		outsw = SHAK;
   int		start, finish, inc;
   int		rflag, nav;
   int		irec;
   char		*filename = NULL, *dump_name = NULL;
   char		*dumplims = NULL;
   char		*insert = NULL;
   char		*tempname;
   char		dumpcommand[256];
   int		dump_size;
   float	*dump_buf;
   FILE		*Fp, *Dp;
   restrt_mt	restart_header;
   system_mt	sys;
   spec_mt	*species;
   spec_mt	*prev_slice;
   site_mt	*site_info;
   pot_mt	*potpar;
   quat_mt	*qpf;
   contr_mt	control_junk;
   spec_mt	*avpos;
   mat_mt	avh;

#define MAXTRY 100
   control.page_length=1000000;

   while( (c = getopt(argc, argv, "cr:s:d:t:o:hpx") ) != EOF )
      switch(c)
      {
       case 'c':
         cflg++;
         break;
       case 'r':
	 if( intyp )
	    errflg++;
	 intyp = c;
	 filename = optarg;
	 break;
       case 's':
	 if( intyp )
	    errflg++;
	 intyp = c;
	 filename = optarg;
	 break;
       case 'd':
	 dump_name = optarg;
	 break;
       case 't':
	 dumplims = optarg;
	 break;
       case 'h':
	 outsw = SHAK;
	 break;
       case 'p':
         outsw = PDB;
	 break;
       case 'x':
	 outsw = XYZ;
	 break;
       case 'o':
	 if( freopen(optarg, "w", stdout) == NULL )
	    error("failed to open file \"%s\" for output", optarg);
	 break;
       default:
       case '?': 
	 errflg++;
      }

   if( errflg )
   {
      fputs("Usage: mdavpos [-r restart-file | -s sys-spec-file] ",stderr);
      fputs("[-c] [-h] [-p] [-x] [-d dump-files] [-t s[-f[:n]]] ",stderr);
      fputs("[-o output-file]\n",stderr);
      exit(2);
   }

   if(intyp == 0)
   {
      fputs("How do you want to specify the simulated system?\n", stderr);
      fputs("Do you want to use a system specification file (1)", stderr);
      fputs(" or a restart file (2)\n", stderr);
      if( (ans_i = get_int("? ", 1, 2)) == EOF )
	 exit(2);
      intyp = ans_i-1 ? 'r': 's';
      if( intyp == 's' )
      {
	 fputs( "Do you need to skip 'control' information?\n", stderr);
	 if( (sym = get_sym("y or n? ","yYnN")) == 'y' || sym == 'Y')
	    cflg++;
      }

      if( (filename = get_str("File name? ")) == NULL )
	 exit(2);
   }

   switch(intyp)
   {
    case 's':
      if( (Fp = fopen(filename,"r")) == NULL)
	 error("Couldn't open sys-spec file \"%s\" for reading", filename);
      if( cflg )
      {
	 do
	 {
	    fscanf(Fp, "%s",line);
	    (void)strlower(line);
	 }
	 while(! feof(stdin) && strcmp(line,"end"));
      }
      read_sysdef(Fp, &sys, &species, &site_info, &potpar);
      qpf = qalloc(sys.nspecies);
      initialise_sysdef(&sys, species, site_info, qpf);
      break;
    case 'r':
      if( (Fp = fopen(filename,"rb")) == NULL)
	 error("Couldn't open restart file \"%s\" for reading -\n%s\n", 
	       filename, strerror(errno));
      re_re_header(Fp, &restart_header, &control_junk);
      re_re_sysdef(Fp, restart_header.vsn, &sys, &species, &site_info, &potpar);
      break;
    default:
      error("Internal error - invalid input type", "");
   }
   allocate_dynamics(&sys, species);

  /* Dump dataset */
   if( dump_name == 0 )
   {
	fputs("Enter canonical name of dump files (as in control)\n",stderr);
	if( (dump_name = get_str("Dump file name? ")) == NULL)
	exit(2);
    }

  /*
   *  Ensure that the dump limits start, finish, inc are set up,
   *  either on command line or by user interaction.
   */
   do
   {
      rflag = 0;
      if( dumplims == NULL )
      {
          fputs("Please specify range of dump records in form", stderr);
          fputs(" start-finish:increment\n", stderr);
          dumplims = get_str("s-f:n? ");
       }
       if( forstr(dumplims, &start, &finish, &inc) )
       {
          rflag++;
          fputs("Invalid range for dump records \"", stderr);
          fputs(dumplims, stderr);
          fputs("\"\n", stderr);
       }
       if( rflag)
       {
          dumplims = NULL;
       } 
   } while(rflag);
      
  /*
   * Allocate buffer for data
   */
     dump_size = DUMP_SIZE(~0)*sizeof(float);

  /* create arrays for previous c_of_m`s for each species */
     prev_slice = aalloc(sys.nspecies, spec_mt);
     avpos = aalloc(sys.nspecies, spec_mt);
  
     if( (dump_buf = (float*)malloc(dump_size)) == 0)
       error("malloc failed to allocate dump record buffer (%d bytes)",
           dump_size);
#if defined (HAVE_POPEN) 
     sprintf(dumpcommand,"dumpext -R%d -Q%d -b -c 0 -t %d-%d:%d %s",
        sys.nmols, sys.nmols_r, start, finish, inc, dump_name);
   
     if( (Dp = popen(dumpcommand,"r")) == 0)
        error("Failed to execute \'dumpext\" command - \n%s",
            strerror(errno));
#else
     tempname = tmpnam((char*)0);
     sprintf(dumpcommand,"dumpext -R%d -Q%d -b -c 0 -t %d-%d:%d -o %s %s",
         sys.nmols,sys.nmols_r, start, finish, inc, tempname, dump_name);
     system(dumpcommand);
     if( (Dp = fopen(tempname,"rb")) == 0)
        error("Failed to open \"%s\"",tempname);
#endif

 /* Loop for calculating trajectories from current and previous time slices */ 

     nav = floor((finish-start+1)/inc);  /* Number of time slices averaged over */

     for(irec = start; irec <= finish; irec+=inc)
     {
       if( fread(dump_buf, dump_size, 1, Dp) < 1 || ferror(Dp) )
           error("Error reading record %d in dump file - \n%s\n",
              irec, strerror(errno));
        dump_to_moldy(dump_buf, &sys);  /*read dump data */

        if( irec == start) /* Set up species arrays and h matrix */
        {
 	   init_species(&sys, species, prev_slice); 
 	   init_species(&sys, species, avpos);
 	   copy_spec(&sys, species, avpos);
	   memcpy(avh, sys.h, sizeof(mat_mt));
        }       
        else
        {
           traj_con(&sys, species, prev_slice);
           summate(&sys, species, avpos, avh);
        }
   /*   Make copy of c_of_m`s for joining trajectory of next time slice */
        copy_spec(&sys, species, prev_slice); 
#ifdef DEBUG
        fprintf(stderr,"Sucessfully read dump record %d from file  \"%s\"\n",
	   irec, dump_name);
#endif
      }

     /* Display species and calculated trajectories */
        average(&sys, avpos, avh, nav); 
        moldy_out(&sys, site_info, insert, avpos, avh, outsw);

#if defined (HAVE_POPEN) 
   pclose(Dp);
#else
   fclose(Dp);
   remove(tempname);
#endif

   return 0;    
}
$EOD
$!
$CREATE ReadDCD.c
$DECK
/***************************************************************************
 *cr                                                                       
 *cr            (C) Copyright 1995 The Board of Trustees of the           
 *cr                        University of Illinois                       
 *cr                         All Rights Reserved                        
 *cr                                                                   
 ***************************************************************************/

/***************************************************************************
 * RCS INFORMATION:
 *
 *      $RCSfile: ReadDCD.c,v $
 *      $Author: keith $        $Locker:  $                $State: Exp $
 *      $Revision: 1.7 $      $Date: 1997/11/27 15:58:19 $
 *
 ***************************************************************************
 * DESCRIPTION:
 *
 * C routines to read and write binary DCD files (which use the goofy
 * FORTRAN UNFORMATTED format).  These routines are courtesy of
 * Mark Nelson (autographs available upon request and $1 tip).
 *
 * Converted to ANSI C for use with Moldy - Keith Refson, Nov 1997         
 ***************************************************************************/

#include 
#include "ReadDCD.h"

/************************************************************************/
/*									*/
/*				MACRO CHECK_FREAD			*/
/*									*/
/*	CHECK_FREAD is used to check the status of a file read.  It     */
/*   is passed the return code from read and a string to print out if   */
/*   an error is detected.  If an error is found, an error message is   */
/*   printed out and the program terminates.  This was made into a      */
/*   macro because it had to be done over and over and over . . .	*/
/*									*/
/************************************************************************/

#define CHECK_FREAD(X, msg)  if (X<=0 && ferror(fp)) \
			     { \
				return(DCD_BADREAD); \
			     }

/************************************************************************/
/*									*/
/*			MACRO CHECK_FEOF				*/
/*									*/
/*	CHECK_FEOF checks for an abnormal EOF on a file read.  It is    */
/*   passed the return status from read and a message to print on error.*/
/*   if an EOF is detected, the error message is printed and the program*/
/*   terminates.  This is used for reads that should find something in  */
/*   the file.								*/
/*									*/
/************************************************************************/

#define CHECK_FEOF(X, msg)  if (X<=0 && feof(fp)) \
			     { \
				return(DCD_BADEOF); \
			     }


/*			GLOBAL VARIABLES				*/
extern int errno;

void pad(s, len)
     char *s;
     int len;
{
	int curlen;
	int i;

	curlen=strlen(s);

	if (curlen>len)
	{
		s[len]=0;
		return;
	}

	for (i=curlen; ifirst time called				*/
/*	indexes - indexes for free atoms				*/
/*									*/
/*   OUTPUTS:								*/
/*	read step populates the arrays X, Y, Z and returns a 0 if the   */
/*   read is successful.  If the read fails, a negative error code is   */
/*   returned.								*/
/*									*/
/*	read_step reads in the coordinates from one step.  It places    */
/*   these coordinates into the arrays X, Y, and Z.			*/
/*									*/
/************************************************************************/

int read_dcdstep(fp, N, X, Y, Z, num_fixed, first, indexes)
     FILE *fp;
     int N;
     float *X;
     float *Y;
     float *Z;
     int num_fixed;
     int first;
     int *indexes;

{
	int ret_val;		/*  Return value from read		*/
	int input_integer;	/*  Integer buffer space		*/
	int i;			/*  Loop counter			*/
	static float *tmpX;

	if (first && num_fixed)
	{
		tmpX = (float *) calloc(N-num_fixed, sizeof(float));

		if (tmpX==NULL)
		{
			return(DCD_BADMALLOC);
		}
	}

	/*  Get the first size from the file				*/
	ret_val = fread(&input_integer, sizeof(int), 1, fp);

	CHECK_FREAD(ret_val, "reading number of atoms at begining of step");

	/*  See if we've reached the end of the file			*/
	if (ret_val == 0)
	{
		free(tmpX);

		return(-1);
	}

	if ( (num_fixed==0) || first)
	{
		if (input_integer != 4*N)
		{
			return(DCD_BADFORMAT);
		}

		ret_val = fread(X, N, 4, fp);
	
		CHECK_FREAD(ret_val, "reading X array");
		CHECK_FEOF(ret_val, "reading X array");

		ret_val = fread(&input_integer, sizeof(int), 1, fp);

		CHECK_FREAD(ret_val, "reading number of atoms after X array");
		CHECK_FEOF(ret_val, "reading number of atoms after X array");

		if (input_integer != 4*N)
		{
			return(DCD_BADFORMAT);
		}

		ret_val = fread(&input_integer, sizeof(int), 1, fp);

		CHECK_FREAD(ret_val, "reading number of atoms after X array");
		CHECK_FEOF(ret_val, "reading number of atoms after X array");

		if (input_integer != 4*N)
		{
			return(DCD_BADFORMAT);
		}

		ret_val = fread(Y, N, 4, fp);

		CHECK_FREAD(ret_val, "reading Y array");
		CHECK_FEOF(ret_val, "reading Y array");

		ret_val = fread(&input_integer, sizeof(int), 1, fp);

		CHECK_FREAD(ret_val, "reading number of atoms after Y array");
		CHECK_FEOF(ret_val, "reading number of atoms after Y array");

		if (input_integer != 4*N)
		{
			return(DCD_BADFORMAT);
		}

		ret_val = fread(&input_integer, sizeof(int), 1, fp);

		CHECK_FREAD(ret_val, "reading number of atoms after Y array");
		CHECK_FEOF(ret_val, "reading number of atoms after Y array");

		if (input_integer != 4*N)
		{
			return(DCD_BADFORMAT);
		}

		ret_val = fread(Z, N, 4, fp);

		CHECK_FREAD(ret_val, "reading Z array");
		CHECK_FEOF(ret_val, "reading Z array");

		ret_val = fread(&input_integer, sizeof(int), 1, fp);

		CHECK_FREAD(ret_val, "reading number of atoms after Z array");
		CHECK_FEOF(ret_val, "reading number of atoms after Z array");

		if (input_integer != 4*N)
		{
			return(DCD_BADFORMAT);
		}
	}
	else
	{
		if (input_integer != 4*(N-num_fixed))
		{
			return(DCD_BADFORMAT);
		}

		ret_val = fread(tmpX, N-num_fixed, 4, fp);
	
		CHECK_FREAD(ret_val, "reading Xtmp array");
		CHECK_FEOF(ret_val, "reading Xtmp array");

		for (i=0; i
#include 
#include 
#include 

/*  DEFINE ERROR CODES THAT MAY BE RETURNED BY DCD ROUTINES		*/
#define DCD_DNE		-2	/*  DCD file does not exist		*/
#define DCD_OPENFAILED	-3	/*  Open of DCD file failed		*/
#define DCD_BADREAD 	-4	/*  read call on DCD file failed	*/
#define DCD_BADEOF	-5	/*  premature EOF found in DCD file	*/
#define DCD_BADFORMAT	-6	/*  format of DCD file is wrong		*/
#define DCD_FILEEXISTS  -7	/*  output file already exists		*/
#define DCD_BADMALLOC   -8	/*  malloc failed			*/


/*			FUNCTION ALLUSIONS				*/
int read_dcdheader();	
				/*  Read the DCD header			*/
int read_dcdstep();	
				/*  Read a timestep's values		*/
int write_dcdstep();
				/*  Write out a timesteps values	*/
int write_dcdheader();	
				/*  Write a dcd header			*/
void close_dcd_read();
				/*  Close a dcd file open for reading   */
void close_dcd_write();	/*  Close a dcd file open for writing   */

#endif /* ! DCDLIB_H */

$EOD
$!
$CREATE ewald_parallel.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * Ewald	The reciprocal-space part of the standard Ewald sum technique *
 ******************************************************************************
 *      Revision Log
 *       $Log: ewald_parallel.c,v $
 *       Revision 2.9  1994/12/30 11:58:42  keith
 *       Fixed bug hwhich caused core dump for small k-cutoff (hmax=0)
 *
 * Revision 2.8  1994/06/22  09:59:05  keith
 * Rearranged procedures
 * Minor optimization to "trig rules" loops.
 *
 * Revision 2.7  1994/06/08  13:13:59  keith
 * New version of array allocator which breaks up requests for DOS.
 * Now must use specific "afree()" paired with arralloc().
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Added CONST qualifier to (re-)declarations of ANSI library
 * emulation routines to give reliable compilation even
 * without ANSI_LIBS macro. (#define's away for K&R
 * compilers)
 *
 * Revision 2.5  1994/01/26  16:34:36  keith
 * Fixed non-ansi #endif.
 *
 * Revision 2.3  93/10/28  10:28:59  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.1  93/09/02  12:34:44  keith
 * Optimized qsincos() -- should give up to 25% speed improvement on
 * compilers without assert no aliasing options.
 * 
 * Revision 2.0  93/03/15  14:49:49  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.22  93/03/12  12:23:05  keith
 * Reorganized defines to recognise all ANSI (__type__) forms.
 * Moved spxpy() from aux.c to force.c and force_parallel.c
 * 
 * 
 * Revision 1.21  93/03/09  15:59:58  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.20  93/03/05  15:01:53  keith
 * Added CRAY parallelising directives
 * 
 * Revision 1.19  92/08/13  17:56:58  keith
 * Modified nprocessors to limit execution to 1 proc
 * unless env var THREADS explicitly set.
 * 
 * Revision 1.18  92/06/26  17:03:02  keith
 * Got rid of assumption that memory returned by talloc() or
 * arralloc() is zeroed.  This enhances ANSI compatibility.
 * Removed memory zeroing from alloc.c() in consequence.
 * 
 * Revision 1.17  92/02/26  14:33:48  keith
 * Got rid of pstrip pragmas for convex -- they just broke things
 * 
 * Revision 1.16  91/11/27  15:15:56  keith
 * Corrected calculation of sheet energy term for charged framework.
 * Split force loop so as to omit frame-frame force (and stress) terms.
 * Added spaces so that Stellix compiler doesn't choke on "=*"
 * Replaced main loop variable with pointer to work round Stellix 2.3 bug
 * 
 * Revision 1.16  91/11/26  12:48:43  keith
 * Put #ifdefs around machine-specific pragmas for portability.  Now
 * supports Stardent 1000,2000 & 3000 (titan) series and convex.
 * 
 * Revision 1.13  91/08/24  16:55:18  keith
 * Added pragmas for convex C240 parallelization
 * 
 * Revision 1.12  91/08/15  18:13:17  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_mt and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.11  91/05/29  17:02:00  keith
 * Modified for minor speed improvement on Stardent titan
 * 
 * Revision 1.10  91/03/12  16:30:08  keith
 * Stardent Titan (ST3000) version.
 * 
 * Revision 1.9  90/09/28  13:29:19  keith
 * Inserted braces around VECTORIZE directives and changed include files
 * for STARDtardent 3000 series (via cond. comp symbol "ardent").
 * 
 * Revision 1.8  90/08/29  11:00:49  keith
 * Speeded up loop at 231 to improve parallel efficiency.
 * 
 * Revision 1.7  90/08/01  19:11:40  keith
 * Modified to exclude framework-framework interactions.
 * N.B. Excluded from pe and stress but NOT forces (as they sum to 0).
 * 
 * Revision 1.6  90/05/16  18:40:57  keith
 * Renamed own freer from cfree to tfree.
 * 
 * Revision 1.5  90/05/16  14:20:47  keith
 * *** empty log message ***
 * 
 * Revision 1.4  90/05/02  15:37:31  keith
 * Removed references to size_mt and time_t typedefs, no longer in "defs.h"
 * 
 * Revision 1.3  90/04/26  15:29:48  keith
 * Changed declaration of arralloc back to char*
 * 
 * Revision 1.2  90/04/25  10:37:06  keith
 * Fixed bug which led to ihkl[] accessing outside bounds of chx[] etc arrays
 * when in const-pressure mode and MD cell expanded between steps.
 * 
 * Revision 1.1  90/01/31  13:18:59  keith
 * Initial revision
 * 
 * Revision 1.6  89/12/15  12:56:26  keith
 * Added conditional ionclusion of  for stellar
 * 
 * Revision 1.5  89/11/01  17:29:10  keith
 * Sin and cos loop vectorised - mat_vec_mul extracted from loop.
 * 'Uniform charge sheet' term added in case of electrically charged system.
 * 
 * Revision 1.5  89/10/26  11:29:26  keith
 * Sin and cos loop vectorised - mat_vec_mul extracted from loop.
 * 'Uniform charge sheet' term added in case of electrically charged layer.
 * 
 * Revision 1.5  89/10/26  11:27:31  keith
 * Sin and cos loop vectorised - mat_vec_mul extracted from loop.
 * 
 * Revision 1.4  89/10/02  11:39:14  keith
 * Fixed error in *star macros which assumed rlv's were cols of h(-1) r.t. rows.
 * 
 * Revision 1.3  89/06/09  13:38:17  keith
 * Older code for computation of q cos/sin k.r restored conditionally by
 * use of macro OLDEWALD.  This is for more primitive vectorising compilers.
 * 
 * Revision 1.2  89/06/08  10:42:51  keith
 * Modified to circumvent compiler bug in VMS/VAXC 2.4-026
 * 
 * Revision 1.1  89/04/20  16:00:39  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/ewald_parallel.c,v 2.9 1994/12/30 11:58:42 keith stab $";
#endif
/*========================== Program include files ===========================*/
#include 	"defs.h"
/*========================== Library include files ===========================*/
#ifdef stellar
#   include 	
#else
#ifdef titan
#   include 	
#else
#   include 	
#endif
#endif
#include	"stddef.h"
#include 	"stdlib.h"
/*========================== Program include files ===========================*/
#include 	"structs.h"
#include 	"messages.h"
/*========================== External function declarations ==================*/
gptr            *talloc();	       /* Interface to memory allocator       */
void            tfree();	       /* Free allocated memory	      	      */
void            afree();	       /* Free allocated array	      	      */
double	err_fn();			/* Error function		      */
double	det();				/* Determinant of 3x3 matrix	      */
void	invert();			/* Inverts a 3x3 matrix		      */
void	mat_vec_mul();			/* Multiplies a 3x3 matrix by 3xN vect*/
void	mat_sca_mul();			/* Multiplies a 3x3 matrix by scalar  */
void	transpose();			/* Transposes a 3x3 matrix	      */
void    zero_real();            	/* Initialiser                        */
void    zero_double();          	/* Initialiser                        */
double	sum();				/* Sum of elements of 'real' vector   */
void	ewald_inner();			/* Inner loop forward reference       */
int	nprocessors();			/* Return no. of procs to execute on. */
#if defined(ANSI) || defined(__STDC__)
gptr	*arralloc(size_mt,int,...); 	/* Array allocator		      */
void	note(char *, ...);		/* Write a message to the output file */
void	message(int *, ...);		/* Write a warning or error message   */
#else
gptr	*arralloc();	        	/* Array allocator		      */
void	note();				/* Write a message to the output file */
void	message();			/* Write a warning or error message   */
#endif
/*========================== External data references ========================*/
extern	contr_mt	control;       	/* Main simulation control record     */
/*========================== Macros ==========================================*/
#define astar hinvp[0]
#define bstar hinvp[1]
#define cstar hinvp[2]
#define moda(hmat) (hmat[0][0])
#define modb(hmat) sqrt(SQR(hmat[0][1]) + SQR(hmat[1][1]))
#define modc(hmat) sqrt(SQR(hmat[0][2]) + SQR(hmat[1][2]) + SQR(hmat[2][2]))
/*============================================================================*/
   struct _hkl {double kx, ky, kz; int h,k,l;};
/*****************************************************************************
 * qsincos().  Evaluate q sin(k.r) and q cos(k.r).  This is in a separate    *
 * function because some compilers (notably Stellar's) generate MUCH better  *
 * vector code this way. 						     *
 *****************************************************************************/
static
void      qsincos(coshx,sinhx,cosky,sinky,coslz,sinlz,chg,
		  qcoskr,qsinkr,k,l,nsites)
real coshx[], sinhx[], cosky[], sinky[], coslz[], sinlz[],
     chg[], qcoskr[], qsinkr[];
int  k,l,nsites;
{
   int is;
   real qckr;
   
   if( k >= 0 )
      if( l >= 0 )
      {
#ifdef __convexc__
#pragma _CNX no_parallel
#endif
VECTORIZE
	 for(is = 0; is < nsites; is++)
	 {
	    qckr = chg[is]*(
		  (coshx[is]*cosky[is] - sinhx[is]*sinky[is])*coslz[is] 
                - (sinhx[is]*cosky[is] + coshx[is]*sinky[is])*sinlz[is]);
	    qsinkr[is] = chg[is]*(
                  (sinhx[is]*cosky[is] + coshx[is]*sinky[is])*coslz[is] 
		+ (coshx[is]*cosky[is] - sinhx[is]*sinky[is])*sinlz[is]);
	    qcoskr[is] = qckr;
	 }
      }
      else
      {
#ifdef __convexc__
#pragma _CNX no_parallel
#endif
VECTORIZE
	 for(is = 0; is < nsites; is++)
	 {
	    qckr = chg[is]*(
		  (coshx[is]*cosky[is] - sinhx[is]*sinky[is])*coslz[is] 
                + (sinhx[is]*cosky[is] + coshx[is]*sinky[is])*sinlz[is]);
	    qsinkr[is] = chg[is]*(
                  (sinhx[is]*cosky[is] + coshx[is]*sinky[is])*coslz[is] 
		- (coshx[is]*cosky[is] - sinhx[is]*sinky[is])*sinlz[is]);
	    qcoskr[is] = qckr;
	 }
      }
   else
      if( l >= 0 )
      {
#ifdef __convexc__
#pragma _CNX no_parallel
#endif
VECTORIZE
	 for(is = 0; is < nsites; is++)
	 {
	    qckr = chg[is]*(
		  (coshx[is]*cosky[is] + sinhx[is]*sinky[is])*coslz[is] 
                - (sinhx[is]*cosky[is] - coshx[is]*sinky[is])*sinlz[is]);
	    qsinkr[is] = chg[is]*(
                  (sinhx[is]*cosky[is] - coshx[is]*sinky[is])*coslz[is] 
		+ (coshx[is]*cosky[is] + sinhx[is]*sinky[is])*sinlz[is]);
	    qcoskr[is] = qckr;
	 }
      }
      else
      {
#ifdef __convexc__
#pragma _CNX no_parallel
#endif
VECTORIZE
	 for(is = 0; is < nsites; is++)
	 {
	    qckr = chg[is]*(
		  (coshx[is]*cosky[is] + sinhx[is]*sinky[is])*coslz[is] 
                + (sinhx[is]*cosky[is] - coshx[is]*sinky[is])*sinlz[is]);
	    qsinkr[is] = chg[is]*(
                  (sinhx[is]*cosky[is] - coshx[is]*sinky[is])*coslz[is] 
		- (coshx[is]*cosky[is] + sinhx[is]*sinky[is])*sinlz[is]);
	    qcoskr[is] = qckr;
	 }
     }
}
/******************************************************************************
 *  Ewald  Calculate reciprocal-space part of coulombic forces		      *
 ******************************************************************************/
#ifdef titan
#ifdef PARALLEL
#pragma opt_level 3
#pragma pproc ewald_inner
#endif
#endif
void	ewald(site,site_force,system,species,chg,pe,stress)
real		**site,			/* Site co-ordinate arrays	 (in) */
		**site_force;		/* Site force arrays		(out) */
system_mp	system;			/* System record		 (in) */
spec_mt	species[];			/* Array of species records	 (in) */
real		chg[];			/* Array of site charges	 (in) */
double		*pe;			/* Potential energy		(out) */
mat_mt		stress;			/* Stress virial		(out) */
{
   mat_mt	hinvp;			/* Matrix of reciprocal lattice vects*/
   register	int	h, k, l;	/* Recip. lattice vector indices     */
		int	i, j, is, ssite;/* Counters.			     */
   		spec_mp	spec;		/* species[ispec]		     */
   register	int	nsites = system->nsites;
   		double	kx,ky,kz;
   		vec_mt	kv;		/* (Kx,Ky,Kz)  			     */
   	 	struct _hkl *hkl;
   		int	nhkl = 0;
   register	real	coss;
/*
 * Maximum values of h, k, l  s.t. |k| < k_cutoff
 */
		int	hmax = floor(control.k_cutoff/(2*PI)*moda(system->h)),
			kmax = floor(control.k_cutoff/(2*PI)*modb(system->h)),
			lmax = floor(control.k_cutoff/(2*PI)*modc(system->h));
/*
 * Arrays for cos & sin (h x(i)), (k y(i)) and (l z(i)) eg chx[h][isite]
 * and pointers to a particular h,k or l eg coshx[is] = chh[2][is]
 */
   real		**chx = (real**)arralloc((size_mt)sizeof(real),2,
					 0, hmax, 0, nsites-1),
		**cky = (real**)arralloc((size_mt)sizeof(real),2,
					 0, kmax, 0, nsites-1),
		**clz = (real**)arralloc((size_mt)sizeof(real),2,
					 0, lmax, 0, nsites-1),
		**shx = (real**)arralloc((size_mt)sizeof(real),2,
					 0, hmax, 0, nsites-1),
		**sky = (real**)arralloc((size_mt)sizeof(real),2,
					 0, kmax, 0, nsites-1),
		**slz = (real**)arralloc((size_mt)sizeof(real),2,
					 0, lmax, 0, nsites-1);
   real		*coshx, *cosky, *coslz, *sinhx, *sinky, *sinlz;
   real               *c1, *s1, *cm1, *sm1;
   real		*sf0, *sf1, *sf2, *ssf0, *ssf1, *ssf2;
   real		*site0, *site1, *site2;
   int		nthreads = nprocessors(),
   		ithread;
   double	*pe_n = aalloc(nthreads, double);
   mat_mt	*stress_n = aalloc(nthreads, mat_mt);
   real		***s_f_n;
   double	r_4_alpha = -1.0/(4.0 * control.alpha * control.alpha);
   double	vol = det(system->h);	/* Volume of MD cell		      */
   static	double	self_energy,	/* Constant self energy term	      */
   			sheet_energy;	/* Correction for non-neutral system. */
   static	boolean init = true;	/* Flag for the first call of function*/
   static	int	nsitesxf;	/* Number of non-framework sites.     */

   invert(system->h, hinvp);		/* Inverse of h is matrix of r.l.v.'s */
   mat_sca_mul(2*PI, hinvp, hinvp);
   s_f_n = aalloc(nthreads, real**);
   s_f_n[0] = site_force;
   for(ithread = 1; ithread < nthreads; ithread++)
   {
      s_f_n[ithread] = (real**)arralloc((size_mt)sizeof(real), 2, 
					0, 2, 0, nsites-1);
      zero_real(s_f_n[ithread][0],3*nsites);
   }
   zero_real(stress_n,9*nthreads);
   zero_double(pe_n, nthreads);

/*
 * First call only - evaluate self energy term and store for subsequent calls
 * Self energy includes terms for non-framework sites only.
 */
   if(init)
   {
      double	sqsq = 0, sq = 0, sqxf, intra, r;
      int	js, frame_flag;

      self_energy = sheet_energy = 0;
      ssite = 0;
      spec = species; 
      while( spec < species+system->nspecies && ! spec->framework)
      {
	 intra = 0.0;
	 for(is = 0; is < spec->nsites; is++)
	    for(js = is+1; js < spec->nsites; js++)
	    {
	       r = DISTANCE(spec->p_f_sites[is], spec->p_f_sites[js]);
	       intra += chg[ssite+is] * chg[ssite+js]
		       *err_fn(control.alpha * r) / r;
	    }
	 self_energy += spec->nmols * intra;
	 ssite += spec->nsites*spec->nmols;
	 spec++;
      }
      nsitesxf = ssite;
      frame_flag = (spec != species+system->nspecies);

#ifdef titan
#pragma no_parallel
#endif
      for(is = 0; is < nsitesxf; is++)
      {
	 sq += chg[is];
	 sqsq += SQR(chg[is]);
      }
      self_energy += control.alpha / sqrt(PI) * sqsq;
      /*
       * Sqxf is total non-framework charge.  Calculate grand total in sq.
       */
      sqxf = sq;
#ifdef titan
#pragma no_parallel
#endif
      for(; is < nsites; is++)
	 sq += chg[is];
      /*
       *  Charged-system/uniform sheet correction terms (really in direct
       *  space term but included here for convenience).
       *  1) For charged framework only.
       */
      if( frame_flag )
      {
	 sheet_energy = PI*SQR(sq-sqxf) / (2.0*SQR(control.alpha));
	 message(NULLI, NULLP, INFO, FRACHG, 
		 (sq-sqxf)*CONV_Q, sheet_energy/vol*CONV_E);
      }
      /* 
       *  2) Case of entire system non-neutral.
       */
      if( fabs(sq)*CONV_Q > 1.0e-5)
      {
	 sheet_energy -= intra = PI*SQR(sq) / (2.0*SQR(control.alpha));
	 message(NULLI, NULLP, WARNING, SYSCHG, sq*CONV_Q, intra/vol*CONV_E);
      }

      note("Ewald self-energy = %f Kj/mol",self_energy*CONV_E);
      init = false;
   }
   
   /*
    * Build array hkl[] of k vectors within cutoff
    */
   hkl = aalloc(4*(hmax+1)*(kmax+1)*(lmax+1), struct _hkl);
   for(h = 0; h <= hmax; h++)
      for(k = (h==0 ? 0 : -kmax); k <= kmax; k++)
      {
	 kv[0] = h*astar[0] + k*bstar[0];
	 kv[1] = h*astar[1] + k*bstar[1];
	 kz = h*astar[2] + k*bstar[2];
	 for(l = (h==0 && k==0 ? 1 : -lmax); l <= lmax; l++)
	 {
	  /*  kv[0] = kx + l*cstar[0]; 
	    kv[1] = ky + l*cstar[1]; */
	    kv[2] = kz + l*cstar[2];
	    if( SUMSQ(kv) < SQR(control.k_cutoff) )
	    {
	       hkl[nhkl].h = h; hkl[nhkl].k = k; hkl[nhkl].l = l;
	       hkl[nhkl].kx = kv[0];
	       hkl[nhkl].ky = kv[1];
	       hkl[nhkl].kz = kv[2];
	       nhkl++;
	    }
	 }
      }

   *pe -= self_energy;			/* Subtract self energy term	      */
   *pe += sheet_energy/vol;		/* Uniform charge correction	      */
   for(i=0; i<3; i++)
      stress[i][i] += sheet_energy/vol;
/*
 * Calculate cos and sin of astar*x, bstar*y & cstar*z for each charged site
 */
   coshx = chx[0]; cosky = cky[0]; coslz = clz[0];
   sinhx = shx[0]; sinky = sky[0]; sinlz = slz[0];
#ifdef titan
#pragma no_parallel
#endif
VECTORIZE
   for(is = 0; is < nsites; is++)
   {
      coshx[is] = cosky[is] = coslz[is] = 1.0;
      sinhx[is] = sinky[is] = sinlz[is] = 0.0;
   }      
   site0 = site[0]; site1 = site[1]; site2 = site[2];
   if( hmax >= 1 )
   {
      coshx = chx[1]; sinhx = shx[1];
#ifdef titan
#pragma no_parallel
#endif
VECTORIZE
      for(is = 0; is < nsites; is++)
      {
	 kx = astar[0]*site0[is]+astar[1]*site1[is]+astar[2]*site2[is];
	 coshx[is] = cos(kx); sinhx[is] = sin(kx);
      }
   }
   if( kmax >= 1 )
   {
      cosky = cky[1]; sinky = sky[1];
#ifdef titan
#pragma no_parallel
#endif
VECTORIZE
      for(is = 0; is < nsites; is++)
      {
	 ky = bstar[0]*site0[is]+bstar[1]*site1[is]+bstar[2]*site2[is];
	 cosky[is] = cos(ky); sinky[is] = sin(ky);
      }
   }
   if( lmax >= 1 )
   {
      coslz = clz[1]; sinlz = slz[1];
#ifdef titan
#pragma no_parallel
#endif
VECTORIZE
      for(is = 0; is < nsites; is++)
      {
	 kz = cstar[0]*site0[is]+cstar[1]*site1[is]+cstar[2]*site2[is];
	 coslz[is] = cos(kz); sinlz[is] = sin(kz);
      }
   }

/*
 * Use addition formulae to get sin(h*astar*x)=sin(Kx*x) etc for each site
 */
   for(h = 2; h <= hmax; h++)
   {
      coshx = chx[h];
      sinhx = shx[h];
      cm1 = chx[h-1]; sm1 = shx[h-1];
      c1  = chx[1];   s1  = shx[1];
#ifdef titan
#pragma no_parallel
#endif
VECTORIZE
      for(is = 0; is < nsites; is++)
      {
	 coss      = cm1[is]*c1[is] - sm1[is]*s1[is];
	 sinhx[is] = sm1[is]*c1[is] + cm1[is]*s1[is];
	 coshx[is] = coss;
      }
   }
   for(k = 2; k <= kmax; k++)
   {
      cosky = cky[k];
      sinky = sky[k];
      cm1 = cky[k-1]; sm1 = sky[k-1];
      c1  = cky[1];   s1  = sky[1];
#ifdef titan
#pragma no_parallel
#endif
VECTORIZE
      for(is = 0; is < nsites; is++)
      {
	 coss      = cm1[is]*c1[is] - sm1[is]*s1[is];
	 sinky[is] = sm1[is]*c1[is] + cm1[is]*s1[is];
	 cosky[is] = coss;
      }
   }
   for(l = 2; l <= lmax; l++)
   {
      coslz = clz[l];
      sinlz = slz[l];
      cm1 = clz[l-1]; sm1 = slz[l-1];
      c1  = clz[1];   s1  = slz[1];
#ifdef titan
#pragma no_parallel
#endif
VECTORIZE
      for(is = 0; is < nsites; is++)
      {
	 coss      = cm1[is]*c1[is] - sm1[is]*s1[is];
	 sinlz[is] = sm1[is]*c1[is] + cm1[is]*s1[is];
	 coslz[is] = coss;
      }
   }
/*
 * Start of main loops over K vector indices h, k, l between -*max, *max etc.
 * To avoid calculating K and -K, only half of the K-space box is covered. 
 * Points on the axes are included once and only once. (0,0,0) is omitted.
 */
#ifdef PARALLEL
#ifdef stellar
/*$dir parallel*/
#endif /* stellar */
#ifdef titan
#pragma ipdep
#pragma pproc ewald_inner
#endif /* titan */
#ifdef __convexc__
#pragma _CNX force_parallel
#endif /* __convexc__ */
#ifdef CRAY
#pragma _CRI taskloop private(ithread) shared(nthreads, nhkl, hkl, nsites, \
		  nsitesxf, chx, cky, clz, shx, sky, slz, chg, vol, r_4_alpha, \
		  stress_n, pe_n, s_f_n)
#endif /* CRAY */
#endif /*PARALLEL */
   for(ithread = 0; ithread < nthreads; ithread++)
      ewald_inner(ithread, nthreads, nhkl, hkl, nsites, nsitesxf, 
		  chx, cky, clz, shx, sky, slz, chg, &vol, &r_4_alpha,
		  stress_n[ithread], pe_n+ithread, s_f_n[ithread]);
/*
 *  Sum Pot, energies, forces and stress from each parallel invocation
 */
   for(ithread = 0; ithread < nthreads; ithread++)
   {
      *pe += pe_n[ithread];
#ifdef titan
#pragma asis
#endif
      for(i = 0; i < 3; i++)
	 for(j = 0; j < 3; j++)
	    stress[i][j] += stress_n[ithread][i][j];
   }
   sf0 = site_force[0]; sf1 = site_force[1]; sf2 = site_force[2];
   for(ithread = 1; ithread < nthreads; ithread++)
   {
      ssf0 = s_f_n[ithread][0];
      ssf1 = s_f_n[ithread][1];
      ssf2 = s_f_n[ithread][2];
#ifdef titan
#pragma ipdep
#endif
#ifdef __convexc__
#pragma _CNX vstrip (64)
#pragma _CNX force_vector
#pragma _CNX force_parallel_ext
#endif
#ifdef CRAY
#pragma _CRI ivdep
#endif
      for(is = 0; is < nsites; is++)
      {
	 sf0[is] += ssf0[is];
	 sf1[is] += ssf1[is];
	 sf2[is] += ssf2[is];
      }
   }
   
   afree((gptr*)chx); afree((gptr*)cky); afree((gptr*)clz); 
   afree((gptr*)shx); afree((gptr*)sky); afree((gptr*)slz);
   xfree(pe_n);   xfree(stress_n);
   xfree(hkl);
   for( ithread = 1; ithread < nthreads; ithread++)
      afree(s_f_n[ithread]);
   xfree(s_f_n);
}
#ifdef titan
#ifdef PARALLEL
#pragma opt_level 2
#endif
#endif
/*****************************************************************************
 *  Ewald_inner().  Part of Ewald sum to run in parallel on multi-stream or  *
 *  multi-processor computers.  It splits up the loop over k-vectors by using*
 *  a stride of the number of threads in use.  The loop starts from a value  *
 *  unique to the thread.    Summable quantities, pe, stress, site_force are *
 *  accumulated where specified by the arguments so it is the callers	     *
 *  responsibility to provide a separte area and accumulate the grand totals *
 *****************************************************************************/
void
ewald_inner(ithread, nthreads, nhkl, hkl, nsites, nsitesxf, 
            chx, cky, clz, shx, sky, slz,
	    chg, volp, r_4_alphap, stress, pe, site_force)
int ithread, nthreads, nhkl;
struct _hkl hkl[];
int nsites;
int nsitesxf;			/* N sites excluding framework sites.	      */
real **chx, **cky, **clz, **shx, **sky, **slz;
double *volp, *r_4_alphap;
mat_mt	stress;
double *pe;
real	chg[];
real	**site_force;
{
   vec_mt	kv;
   double ksq, coeff, coeff2, pe_k;
   double sqcoskr, sqsinkr, sqcoskrn, sqsinkrn, sqcoskrf, sqsinkrf;
   real *coshx, *cosky, *coslz, *sinhx, *sinky, *sinlz;
   int is, i, j, h, k, l;
   struct _hkl *phkl;
   double vol = *volp, r_4_alpha = *r_4_alphap;
   real		force_comp, kv0, kv1, kv2;
   real *qcoskr = dalloc(nsites), *qsinkr = dalloc(nsites);
   real		*site_fx = site_force[0],
   		*site_fy = site_force[1],
   		*site_fz = site_force[2];

   for(phkl = hkl+ithread; phkl < hkl+nhkl; phkl += nthreads)
   {
      h  = phkl->h;	    k     = phkl->k;  l     = phkl->l;
      kv0 = kv[0] = phkl->kx; 
      kv1 = kv[1] = phkl->ky; 
      kv2 = kv[2] = phkl->kz;
/*
 * Calculate pre-factors A(K) etc
 */
      ksq = SUMSQ(kv);
      coeff  = 2.0 / (EPS0 * vol) * exp(ksq * r_4_alpha) / ksq;
      coeff2 = 2.0 * (1.0 - ksq * r_4_alpha) / ksq;
      
/*
 * Set pointers to array of cos (h*astar*x) (for efficiency & vectorisation)
 */
      coshx = chx[h];  cosky = cky[abs(k)]; coslz = clz[abs(l)];
      sinhx = shx[h];  sinky = sky[abs(k)]; sinlz = slz[abs(l)];

/*
 * Calculate q(i)*cos/sin(K.R(i)) by addition formulae. Note handling of
 * negative k and l by using sin(-x) = -sin(x), cos(-x) = cos(x). For
 * efficiency & vectorisation there is a loop for each case.
 */
      qsincos(coshx,sinhx,cosky,sinky,coslz,sinlz,chg,
	      qcoskr,qsinkr,k,l,nsites);
      sqcoskrn = sum(nsitesxf, qcoskr, 1);
      sqsinkrn = sum(nsitesxf, qsinkr, 1);
      sqcoskrf = sum(nsites-nsitesxf, qcoskr+nsitesxf, 1);
      sqsinkrf = sum(nsites-nsitesxf, qsinkr+nsitesxf, 1);
      sqcoskr = sqcoskrn + sqcoskrf;
      sqsinkr = sqsinkrn + sqsinkrf;
      
/*
 * Evaluate potential energy contribution for this K and add to total.
 * Exclude frame-frame interaction terms.
 */
      pe_k = 0.5 * coeff * (sqcoskrn*(sqcoskrn+sqcoskrf+sqcoskrf) +
			    sqsinkrn*(sqsinkrn+sqsinkrf+sqsinkrf));
      *pe += pe_k;

      sqsinkr *= coeff; sqcoskr *= coeff;
      sqsinkrn *= coeff; sqcoskrn *= coeff;
/*
 * Calculate long-range coulombic contribution to stress tensor
 */
NOVECTOR
      for(i = 0; i < 3; i++)
      {
	 stress[i][i] += pe_k;
NOVECTOR
	 for(j = i; j < 3; j++)
	    stress[i][j] -= pe_k * coeff2 * kv[i] * kv[j];
      }

/*
 * Evaluation of site forces.   Non-framework sites interact with all others
 */
VECTORIZE
      for(is = 0; is < nsitesxf; is++)
      {
	 force_comp = qsinkr[is]*sqcoskr - qcoskr[is]*sqsinkr;
	 site_fx[is] += kv0 * force_comp;
	 site_fy[is] += kv1 * force_comp;
	 site_fz[is] += kv2 * force_comp;
      }
#if 1
/*
 *  Framework sites -- only interact with non-framework sites
 */
VECTORIZE
      for(is = nsitesxf; is < nsites; is++)
      {
	 force_comp = qsinkr[is]*sqcoskrn - qcoskr[is]*sqsinkrn;
	 site_fx[is] += kv0 * force_comp;
	 site_fy[is] += kv1 * force_comp;
	 site_fz[is] += kv2 * force_comp;
      }
#endif
/*
 * End of loop over K vectors.
 */
   }
xfree(qcoskr); xfree(qsinkr);
}

char *getenv();
#ifdef titan
int nprocessors()
{
   char *env;
   static int n=0;
   int    nphys;

   if( n <= 0 )
   {
      nphys = MT_NUMBER_OF_PROCS();
      if( ( env = getenv("THREADS") ) == NULL )
	 n = 1;
      else
      {
	 n = atoi(env);
	 if ( n <= 0 || n > nphys)
	    n = nphys;
      }
   }
   return n;
}
#else
#ifdef CRAY
int nprocessors()
{
   char *env;
   static int n = 0;
   int nphys = 8;

   if( n <= 0 )
   {
      if( ( env = getenv("NCPUS") ) == NULL )
	 n = 1;
      else
      {
	 n = atoi(env);
	 if ( n <= 0 || n > nphys)
	    n = nphys;
      }
   }
   return n;
}
#else			/* GS1000/2000 but should compile on any unix */
int nprocessors()
{
   char *env;
   static int n = 0;
   int nphys = 4;

   if( n <= 0 )
   {
      if( ( env = getenv("THREADS") ) == NULL )
	 n = 1;
      else
      {
	 n = atoi(env);
	 if ( n <= 0 || n > nphys)
	    n = nphys;
      }
   }
   return n;
}
#endif
#endif
$EOD
$!
$CREATE force_parallel.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * Force        This module contains functions to implement the 'link cell'   *
 *              interatomic force calculation (Hockney, R.W. & Eastwood J.W.  *
 *              "Computer Simulation Using Particles" McGraw-Hill (1981), 277)*
 *              It is vectorised and optimised for a CRAY XMP and Convex C1,  *
 *              but should run well on any vector machine with reasonably     *
 *              efficient scatter/gather. (And of course on scalar machines!) *
 *              The actual calculation of the potential is in a different     *
 *              module (kernel.c) for ease of modification.                   *
 ******************************************************************************
 *       $Log: force_parallel.c,v $
 *       Revision 2.9  1997/07/09 14:45:19  keith
 *       Brought up to daye with main-line developments in force.c.
 *       (Actually, re-introduced compiler parallelism into force.c 2.14)
 *
 *       Revision 2.14  1996/11/04 17:34:30  keith
 *       Moderate rewriting and code re-organization.
 *       1. Simplified PBC relocation calculation, got rid of large
 *          arrays reloc[] etc, saving 32 MB for 8192 waters on T3D.
 *          There is now NO LIMIT to cutoff or RDF limit and macro
 *          parameter NSH is removed.
 *       2. Rewrote site_neighbour_list to be more transparent and got
 *          rid of silly vector sort calls. There are now separate versions for
 *          scalar and vector machines.  Code is now optimized for usual non-
 *          framework case, but it's faster than 2.10 even for frameworks.
 *       3. Corrected misleading comments, reorganized code in force_calc()
 *          to be more transparent.  Commented local variables MUCH better.
 *       4. It's also a bit faster.
 *
 *       Revision 2.13  1996/08/14 16:46:04  keith
 *       Workaround for T3D Cray compiler bug real*int/int ==> int division.
 *       Got rid of unnccessary par_abort() calls - rplaced with exit().
 *       (message on thread 0 calls par_abort()).
 *
 *       Revision 2.12  1996/05/03 16:14:20  keith
 *       Fixed bug whereby reloc could overflow in strict cutoff mode by
 *       tightening up test condition. Also fixed the calculation of
 *       n_nab_sites to reflect the increased cutoff in strict mode.
 *
 *       Revision 2.11  1996/01/17 17:12:47  keith
 *       Incorporated rdf accumulation into forces and parallelized.
 *       New functions rdf_inner(), calls rdf_accum() from rdf.c
 *
 * Revision 1.8.1.8  89/11/01  17:34:15  keith
 * Modified to use SPAXPY vectorised scattered add.
 * 
 * Revision 1.8.1.6  89/10/12  16:28:39  keith
 * Added conditional code to produce histogram of interaction distances
 * and calculate 'minimum image' energy.
 * Added preprocessor constant NSH to fix size of relocation arrays.
 * Fixed mistake in metric G in neighbour_list() (G=h'h not hh').
 * 
 * Revision 1.8.1.5  89/10/02  17:12:59  keith
 * New version of neighbour_list() which works for arbitrary cutoff radii.
 * site_neighbour_list checks for overflow.
 * Main loop limits modified, (in conjunction with mods in site_neighbour_list)
 * to correctly include all molecule-framework interactions.
 * 
 * Revision 1.8.1.4  89/09/12  16:14:41  keith
 * Fixed bug in fill_cells() which didn't increment spec properly in imol loop
 * 
 * Revision 1.8.1.3  89/08/31  11:58:04  keith
 * Fixed bug in 'BIN' macro to correctly handle case of rc<0.
 * 
 * Revision 1.8.1.2  89/08/30  17:00:33  keith
 * Fixed memory overlap bug in site_neighbour_list
 * 
 * Revision 1.8.1.1  89/08/25  15:24:43  keith
 * Mods to add framework structures to simulation model
 * 
 * Revision 1.7  89/08/22  14:48:39  keith
 * Created new variable 'n_nab_sites' for max size of vector arrays.
 * 
 * Revision 1.6  89/07/04  18:43:14  keith
 * Fixed error in kernel and force which led to sites being allocated the
 * wrong potential parameters.  Needed extra parameter to kernel.
 * 
 * Revision 1.5  89/06/22  15:44:23  keith
 * Tidied up loops over species to use one pointer as counter.
 * 
 * Revision 1.4  89/06/14  14:18:49  keith
 * Fixed #ifdef s for CRAY to handle case of UNICOS
 * Fix mistake in VCALLS conditional code.
 * 
 * Revision 1.3  89/06/01  18:01:46  keith
 * Moved `vadd()' from aux.c to force.c for ease of vectorisation.
 * Now no need to compile aux.c with vectorisation.
 * 
 * Revision 1.2  89/05/17  13:53:49  keith
 * Reorganised neighbour list construction in preparation for framework.
 * (Also goes slighty faster)
 * 
 * Revision 1.1  89/04/20  16:00:40  keith
 * Initial revision
 * 
 * Revision 1.3  90/03/29  15:44:51  keith
 * Merged force.c revisions 1.8.1.11-1.8.1.13
 * 
 * Revision 1.2  90/03/09  17:30:29  keith
 * Modified FKERNEL ifdefs for UNICOS.
 * 
 * Revision 1.1  90/01/31  13:19:28  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/force_parallel.c,v 2.9 1997/07/09 14:45:19 keith Exp $";
#endif
/*========================== Program include files ===========================*/
#include        "defs.h"
/*========================== Library include files ===========================*/
#ifdef  stellar
#include        
#else
#include        
#endif
#include        "stddef.h"
#include        "string.h"
#include        
/*========================== Program include files ===========================*/
#include        "structs.h"
#include        "messages.h"
/*========================== External function declarations ==================*/
gptr            *talloc();             /* Interface to memory allocator       */
void            tfree();               /* Free allocated memory               */
void            afree();               /* Free allocated array                */
int             search_lt();            /* Search a vector for el. < scalar   */
void            gather();               /* Interface to CRAY gather routine   */
void            mat_mul();              /* Matrix multiplier                  */
double          det();                  /* Determinant of 3x3 matrix          */
void            invert();               /* 3x3 matrix inverter                */
void            mat_vec_mul();          /* Matrix by vector multiplier        */
void            transpose();            /* Generate 3x3 matrix transpose      */
void            zero_real();            /* Initialiser                        */
void    	zero_double();          /* Initialiser                        */
void            force_inner();          /* Inner loop forward reference       */
void            rdf_inner();            /* RDF calc forward reference         */
double          precision();            /* Floating pt precision.             */
void            kernel();               /* Force kernel routine               */
double          mol_radius();           /* Radius of largest molecule.        */
void            rdf_accum();            /* Bin distances for rdf evaluation.  */
#if defined(ANSI) || defined(__STDC__)
gptr            *arralloc(size_mt,int,...); /* Array allocator                */
void            note(char *, ...);      /* Write a message to the output file */
void            message(int *, ...);    /* Write a warning or error message   */
#else
gptr            *arralloc();            /* Array allocator                    */
void            note();                 /* Write a message to the output file */
void            message();              /* Write a warning or error message   */
#endif
int     	nprocessors();          /* Return no. of procs to execute on. */
/*========================== External data references ========================*/
extern  contr_mt control;                   /* Main simulation control parms. */
/*========================== Structs local to module =========================*/
typedef struct cell_s                   /* Prototype element of linked list of*/
{                                       /* molecules within interaction range */
   int          isite, num, frame_type;
   struct cell_s *next;
}               cell_mt;

typedef struct                          /* Prototype of neighbour cell list   */
{                                       /* element.                           */
   int          i, j, k;
}               ivec_mt;

typedef struct                          /* Prototype of neighbour cell list   */
{                                       /* element.                           */
   real         i, j, k;
}               rvec_mt;

typedef struct                          /* Prototype of neighbour cell list   */
{                                       /* element.                           */
   real         x, y, z;
   int          i, j, k;
}               irvec_mt;

/*========================== Global variables ================================*/
static irvec_mt *ifloor; /*Lookup tables for int "floor()"    */
/*========================== Macros ==========================================*/
/*
 * Multiplication factor for size of neighbour list arrays.  If you need
 * to increase this from 1, your system must be *highly* inhomogeneous
 * and may not make sense!
 */
#define         NMULT 3.0
#define         TOO_CLOSE       0.25    /* Error signalled if r**2 < this     */
#define         NCELL(ix,iy,iz) ((iz)+(nz)*((iy)+(ny)*(ix)))
#define         LOCATE(r,eps)   NCELL(cellbin(r[0], nx, eps), \
                                      cellbin(r[1], ny, eps), \
                                      cellbin(r[2], nz, eps))
#define         BIGINT 32768
#define         IFLOOR(i,n)     ((i+BIGINT*n)/n-BIGINT)
#define moda(hmat) sqrt(SQR(hmat[0][0]) + SQR(hmat[1][0]) + SQR(hmat[2][0]))
#define modb(hmat) sqrt(SQR(hmat[0][1]) + SQR(hmat[1][1]) + SQR(hmat[2][1]))
#define modc(hmat) sqrt(SQR(hmat[0][2]) + SQR(hmat[1][2]) + SQR(hmat[2][2]))
#ifdef __BOUNDS_CHECKING_ON /* The declaration of "array" potp has a lower */
#   define P0 0             /* bound of 1.  This is a violation of strict  */
#else                       /* ANSI, so turn it off when bounds checking.  */
#   define P0 1
#endif
/*============================================================================*/
/******************************************************************************
 * spxpy    Sparse add for force.c.  N.B.  MUST NOT BE VECTORIZED as ix may   *
 *          contain duplicate entries.  This occurs if a site interacts with  *
 *          more than one periodic copy of another site.                      *
 ******************************************************************************/
static void spxpy(n, sx, sy, ix)
int     n, ix[];
real    sx[], sy[];
{
   int i;
NOVECTOR
#ifdef __STDC__
#pragma novector
#endif
   for( i = 0; i < n; i++)
   {
      sy[ix[i]] += sx[i];
   }
}

/******************************************************************************
 *  cellbin.    Safe binning function for putting molecules/sites into cells. *
 *  Any error at the boundaries is disasterous and hard to detect.            *
 *  Results may depend on machine-dependant rounding etc.                     *
 ******************************************************************************/
int cellbin(rc, nc, eps)
double rc, eps;
int nc;
{
   int ibin;

   if(rc < -0.5+eps || rc >= 0.5-eps)
   {
      if(rc < -0.5+eps && rc >= -0.5-eps)
         rc = -0.5;
      else if(rc >= 0.5-eps && rc <= 0.5+eps)
         rc = 0.5-eps;
      else 
         message(NULLI, NULLP, ERROR, 
                 "Co-ordinate out of range in BIN (fill_cells) %.17g\n",rc);
   }
   if( (ibin = ((rc+0.5)*nc)) >= nc || ibin < 0)
         message(NULLI, NULLP, ERROR, 
                 "Rounding problem in BIN (fill_cells) %.17g\n",rc);
   return ibin;
}

/******************************************************************************
 ******************************************************************************/
#ifdef DEBUG2
#define NBINS 200
static int bins[NBINS];

hist(jmin, jmax, rr)
int jmin, jmax;
real rr[];
{
   double rbin = 10.0;
   int j;

   for(j = jmin; j < jmax; j++)
      if(rr[j] < SQR(NBINS/rbin))
         bins[(int)(rbin*sqrt(rr[j]))]++;
}
void histout()
{
   int j;
   for(j = 0; j < NBINS; j++)
   {
      printf("%d%c",bins[j],(j+1)%10?' ':'\n');
      bins[j] = 0;
   }
}
#endif
/******************************************************************************
 *  Neighbour_list.  Build the list of cells within cutoff radius of cell 0   *
 ******************************************************************************/
static ivec_mt   *neighbour_list(nnabor, h, cutoff, nx, ny, nz, icheck)
int     *nnabor;
mat_mt  h;
double  cutoff;
int     nx, ny, nz;
int     icheck;
{
   double               dist;
   int                  i, j, ix, iy, iz, mx, my, mz, inabor = 0, nnab;
   static int           onabor=0;
   ivec_mt              *nabor;
   vec_mt               s;
   mat_mt               G, htr, htrinv;

   transpose(h, htr);
   mat_mul(htr, h, G);
   invert(htr, htrinv);

   mx = ceil(cutoff*nx*moda(htrinv));
   my = ceil(cutoff*ny*modb(htrinv));
   mz = ceil(cutoff*nz*modc(htrinv));

   nnab = 4*mx*my*mz;
   nabor = aalloc(nnab, ivec_mt);
#ifdef DEBUG1
   printf("  Distance    ix    iy    iz      sx        sy          sz\n");
#endif
   for(ix = 0; ix < mx; ix++)
      for(iy = (ix == 0 ? 0 : -my); iy < my; iy++)
         for(iz = (ix == 0 && iy == 0 ? 0 : -mz); iz < mz; iz++)
         {
            s[0] = (double)ix/nx;
            s[1] = (double)iy/ny;
            s[2] = (double)iz/nz;
            dist = 0.0;
            for(i = 0; i < 3; i++)
               for(j = 0; j < 3; j++)
                  dist += s[i]*G[i][j]*s[j];
            if(dist < SQR(cutoff))
            {
               if( inabor >= nnab )
                  message(NULLI, NULLP, FATAL,
                          "Internal error in neighbour_list()");
               nabor[inabor].i = ix;
               nabor[inabor].j = iy;
               nabor[inabor].k = iz;
               inabor++;
#ifdef DEBUG1
               printf("%12f %4d %4d %4d %12f %12f %12f\n",
                      dist,ix,iy,iz,s[0],s[1],s[2]);
#endif
            }
         }
   if( icheck )
   {
      if( inabor != onabor )
         note(NABORS,2 * inabor);
      onabor = inabor;
   }
   *nnabor = inabor;
   return(nabor);
}
/******************************************************************************
 *  Strict_Neighbour_list.  Build the list of cells within cutoff radius      *
 *  This is the strict version and includes every cell which has an interior  *
 *  point at a distance less than the cutoff from any interior point of the   *
 *  reference cell.  In fact the distance criterion is cutoff+2*(maximum mol- *
 *  ecular radius).  This ensures that all *sites* which might be closer to-  *
 *  gether than the cutoff are included.                                      *
 *     The method used is based on the fact that the closest interior points  *
 *  of a pair of parallelopiped cells are either at corners of both cells or  *
 *  at the ends of a line perpendicular to the faces of both parallelopipeds. *
 *  The face-face distance is always the shortest, if the perpendicular       *
 *  projection of the faces onto a common plane intersect with each other     *
 *  The goal is therefore to build a list containing all cells which have a   *
 *  corner-corner or face-face distance to the reference cell which is less   *
 *  than the cutoff.                                                          *
 *  Cells may be admitted to the list by multiple corner-corner or face-face  *
 *  contact criteria but must only be recorded in the final list once.  The   *
 *  easiest way to do this is to use a "map" of all the cells potentially     *
 *  within the cutoff radius and to flag occupancy.                           *
 *  It is easier to loop over all grid vectors within the cutoff and assign   *
 *  cells which have that vector as some corner-corner vector with the        *
 *  reference cell, rather than to loop over cells and calculate all corner   *
 *  pair distances.  This method calculates each distance only once instead   *
 *  of 27 times (the number of distinct corner-corner vectors between 2       *
 *  cells).                                                                   *
 *  To exploit Newton's third law the list should contain only the positive   *
 *  hemisphere (in the x direction).                                          *
 *                                                                            *
 *  The algorithm is as follows.                                              *
 *  1) Set up an empty "map"                                                  *
 *  2) Loop over all points on a grid with points at the link-cell corners    * 
 *     choose only points which are closer to the origin than the cutoff.     *
 *     Set the occupancy flag for all cells which have that as a corner-pair  *
 *     vector to the reference cell.   This is a 3x3x3 block of cells centred *
 *     on the cell whose index is the same as the gridpoint being considered. *
 *     Because we only want the "positive x" cells 2x3x3 will suffice.        *
 *  3) Add cells which have a perpendicular face-face separation within the   *
 *     cutoff.  Only the outermost cells need be considered since inner ones  *
 *     are already admitted by corner-pair distance.  Thus                    *
 *     3a) project the facing corner points of the reference cell onto the    *
 *         plane just within the cutoff.                                      *
 *     3b) add the cells with faces which overlap the projection.  Zero, two  *
 *         or four cells are added depending on whether the projected points  *
 *         coincide with the corner points, the edges or none of the faces.   *
 *  4) The final list is built by scanning the map.                           *
 ******************************************************************************/
static ivec_mt   *strict_neighbour_list(nnabor, h, cutoff, nx, ny, nz, icheck)
int     *nnabor;
mat_mt  h;
double  cutoff;
int     nx, ny, nz;
int     icheck;
{
   double               dist;
   int                  i, j, k, ix, iy, iz, mx, my, mz, inabor = 0, nnab;
   static int           onabor=0;
   int                  ***cellmap;
   ivec_mt              *nabor;
   vec_mt               s;
   mat_mt               G, htr, htrinv;
   int                  face_cells[4][3],mxyz[3], nxyz[3], ixyz, jxyz, kxyz;
   double               proj[3], modabc;

   transpose(h, htr);
   mat_mul(htr, h, G);
   invert(htr, htrinv);

   mx = ceil(cutoff*nx*moda(htrinv));
   my = ceil(cutoff*ny*modb(htrinv));
   mz = ceil(cutoff*nz*modc(htrinv));

   /*
    * Allocate and clear array for map of cells
    */
   nnab = 4*(mx+1)*(my+1)*(mz+1);
   cellmap = (int***)arralloc((size_mt)sizeof ***cellmap, 3, 
                              0, mx, -my-1, my, -mz-1, mz);
   memst(cellmap[0][-my-1]-mz-1,0, nnab*sizeof ***cellmap);

   /*
    * Add cells with corner-pair distances < cutoff
    */
#ifdef DEBUG1
   printf("  Distance    ix    iy    iz      sx        sy          sz\n");
#endif
   for(ix = 0; ix < mx; ix++)
      for(iy = (ix == 0 ? 0 : -my); iy < my; iy++)
         for(iz = (ix == 0 && iy == 0 ? 0 : -mz); iz < mz; iz++)
         {
            s[0] = (double)ix/nx;
            s[1] = (double)iy/ny;
            s[2] = (double)iz/nz;
            dist = 0.0;
            for(i = 0; i < 3; i++)
               for(j = 0; j < 3; j++)
                  dist += s[i]*G[i][j]*s[j];
            if(dist < SQR(cutoff))
            {
               for(i=0; i<=1; i++)
                  for(j = -1; j <= 1; j++)
                     for(k = -1; k <= 1; k++)
                        cellmap[ix+i][iy+j][iz+k] = 1;

#ifdef DEBUG1
               printf("%12f %4d %4d %4d %12f %12f %12f\n",
                      dist,ix,iy,iz,s[0],s[1],s[2]);
#endif
            }
         }
   /*
    * Add cells with face-face distance < cutoff.  Cells along x,y,z axes
    * are added in +/- directions, but only +ve ix indices added to map.
    */
   nxyz[0] = nx; nxyz[1] = ny; nxyz[2] = nz;
   mxyz[0] = mx; mxyz[1] = my; mxyz[2] = mz;
   for( ixyz=0; ixyz < 3; ixyz++)       /* Loop over directions */
   {
      jxyz = (ixyz+1) % 3; kxyz = (jxyz+1) % 3;
      proj[0] = proj[1] = proj[2] = 0.0;
      modabc = 0.0;
      for( i=0; i<3; i++ )
      {
         modabc += htrinv[i][ixyz];
         proj[i] += htrinv[i][ixyz]*htrinv[i][(ixyz+i) % 3];
      }
      for( i=0; i<3; i++ )
         proj[i] *= (mxyz[ixyz]-1)*nxyz[i]/(nxyz[ixyz] * modabc);
      /*
       * proj now contains projection vector.  Construct 4 candidate
       * cells.
       */
      for( i=0; i<3; i++ )
         face_cells[0][i] = face_cells[1][i] = face_cells[2][i] = 
            face_cells[3][i] = floor(proj[i]);
   
      face_cells[0][ixyz] = face_cells[1][ixyz] = face_cells[2][ixyz] = 
         face_cells[3][ixyz] = mxyz[ixyz];

      face_cells[1][jxyz] = face_cells[3][jxyz] = ceil(proj[jxyz]);
      face_cells[2][kxyz] = face_cells[3][kxyz] = ceil(proj[kxyz]);
      /*
       *  Now make sure we add only cells with +ve x index.  Add the
       *  inverse cell if ix<0.
       */
      for( i=0; i < 4; i++)
         if( face_cells[i][0] < 0 )
            for( j=0; j<3; j++ )
               face_cells[i][j] = -face_cells[i][j];
      /*
       * Now add the cells to the map.
       */
      for(  i=0; i < 4; i++ )
      {
         cellmap[face_cells[i][0]][face_cells[i][1]][face_cells[i][2]] = 1;
#ifdef DEBUG1
               printf("%12f %4d %4d %4d %12f %12f %12f\n",
                      0.5,face_cells[i][0],face_cells[i][1],face_cells[i][2],
                      0.0,0.0,0.0);
#endif
      }
   }
   /*
    * Scan map and build list.  N.B.  Loop indices are 1 greater than when
    * list built since we added cells outside original loop limits.
    */   
   nabor = aalloc(nnab, ivec_mt);
   for(ix = 0; ix <= mx; ix++)
      for(iy = (ix == 0 ? 0 : -my-1); iy <= my; iy++)
         for(iz = (ix == 0 && iy == 0 ? 0 : -mz-1); iz <= mz; iz++)
         {
            if( cellmap[ix][iy][iz] )
            {
               if( inabor >= nnab )
                  message(NULLI, NULLP, FATAL,
                          "Internal error in neighbour_list()");
               nabor[inabor].i = ix;
               nabor[inabor].j = iy;
               nabor[inabor].k = iz;
               inabor++;
#ifdef DEBUG1
               printf("%12f %4d %4d %4d %12f %12f %12f\n",
                      1.0,ix,iy,iz,0.0,0.0,0.0);
#endif
            }
         }

   if( icheck )
   {
      if( inabor != onabor )
         note(NABORS,2 * inabor);
      onabor = inabor;
   }
   *nnabor = inabor;
   afree((gptr*)cellmap);
   return(nabor);
}
/******************************************************************************
 *  Fill_cells.  Allocate all the sites to cells depending on their centre of *
 *  mass co-ordinate by binning.                                              *
 ******************************************************************************/
static void    fill_cells(c_of_m, nmols, site, species, h, nx, ny, nz, 
                          lst, cell, frame_type)
vec_mt  c_of_m[];                       /* Centre of mass co-ords        (in) */
int     nmols;                          /* Number of molecules           (in) */
real    **site;                         /* Atomic site co-ordinates      (in) */
spec_mp species;                        /* Pointer to species array      (in) */
mat_mt  h;                              /* Unit cell matrix              (in) */
int     nx, ny, nz;
cell_mt *lst;                           /* Pile of cell structs          (in) */
cell_mt *cell[];                        /* Array of cells (assume zeroed)(out)*/
int     *frame_type;                    /* Framework type counter        (out)*/
{
   int icell, imol, im=0, is, isite = 0;
   double eps = 8.0*precision();
   spec_mp spec = species;
   cell_mt *list = lst;
   vec_mt ssite;
   mat_mt hinv;

   *frame_type=1;
   invert(h, hinv);

   for(imol = 0, im = 0; imol < nmols; imol++, im++)
   {
      if(im == spec->nmols)
      {
         im = 0;
         spec++;
      }

      if( spec->framework )
      {
         for( is = 0; is < spec->nsites; is++)
         {
            ssite[0] = site[0][isite];
            ssite[1] = site[1][isite];
            ssite[2] = site[2][isite];
            mat_vec_mul(hinv, (vec_mt*)ssite, (vec_mt*)ssite, 1);
            icell = LOCATE(ssite, eps);
            list->isite = isite++;
            list->num   = 1;
            list->frame_type = *frame_type;
            list->next = cell[icell];
            cell[icell] = list++;
            list->next = NULL;
         }
         (*frame_type)++;
      }
      else
      {
         icell = LOCATE(c_of_m[imol], eps);
         list->isite = isite;
         list->num   = spec->nsites;
         list->frame_type = 0;
         list->next = cell[icell];
         cell[icell] = list++;
         list->next = NULL;
         isite += spec->nsites;
      }
   }
}
/******************************************************************************
 *  site_neightbour list.  Build the list of sites withing interaction radius *
 *                         from the lists of sites in cells.                  *
 ******************************************************************************/
int     site_neighbour_list(nab, reloc, n_nab_sites, nfnab, n_frame_types,
                            n_nabors, ix, iy, iz, nx, ny, nz, nabor, cell)
int     *nab;                           /* Array of sites in list      (out) */
rvec_mt reloc[];                        /* Relocation indices for list (out) */
int     n_nab_sites;                    /* Size of above arrays         (in) */
int     nfnab[];                        /* N frame sites index by type (out) */
int     n_frame_types;                  /* Number of distinct frameworks(in) */
int     n_nabors;                       /* Number of neighbour cells    (in) */
int     ix, iy, iz, nx, ny, nz;         /* Labels of current cell       (in) */
ivec_mt *nabor;                         /* List of neighbour cells      (in) */
cell_mt **cell;                         /* Head of cell list            (in) */
{
   int  jx, jy, jz;                     /* Labels of cell in neighbour list  */
   int  j0, jnab, jsite;                /* Counters for cells etc            */
   int  nnab = 0;                       /* Counter for size of nab           */
   int  ftype;
   cell_mt      *cmol;                  /* Pointer to current cell element   */

#ifndef VECTOR
   /*
    * Usual version for scalar machines. 
    */
   irvec_mt *ifl = ifloor;
   real ri, rj, rk;
   int jcell;

   nnab = 0;
   for(ftype = 0; ftype < n_frame_types; ftype++) /* Do Framework types first */
   {
      j0 = (ftype == 0) ? 0: 1;
      for(jnab = j0; jnab < n_nabors; jnab++)    /* Loop over neighbour cells  */
      {
         jx = ix + nabor[jnab].i;       /* jx-jz are indices of neighbour cell*/
         jy = iy + nabor[jnab].j;
         jz = iz + nabor[jnab].k;
         ri  = ifl[jx].x;       /* Compute PBC relocation vector -            */
         jx -= ifl[jx].i;       /* Actually we use a lookup table for speed.  */
         rj  = ifl[jy].y;       /* Ifl[] contains integer and real versions   */
         jy -= ifl[jy].j;       /* of value since conversions are expensive.  */
         rk  = ifl[jz].z;       /* Float value is floor(jx/nx).               */
         jz -= ifl[jz].k;       /* Int value is nx*floor(jx/nx).              */
         jcell = NCELL(jx,jy,jz);
#ifdef DEBUG1
         if(jx<0 || jx>=nx || jy<0 || jy>=ny || jz<0 || jz>=nz)
            message(NULLI,NULLP,FATAL,"Bounds error on reloc (%d,%d,%d)",jx,jy,jz);
#endif
         /* Loop over molecules in this cell, filling 'nab' with its sites    */
         for(cmol = cell[jcell]; cmol != 0; cmol = cmol->next)
         {
            if( cmol->frame_type == ftype )
            {
               for(jsite = 0; jsite < cmol->num; jsite++)
               {
                  nab[nnab] = cmol->isite + jsite;
                  reloc[nnab].i = ri;
                  reloc[nnab].j = rj;
                  reloc[nnab].k = rk;
                  nnab++;
               }
            }
            if(nnab > n_nab_sites) 
               message(NULLI,NULLP,FATAL,TONAB,nnab,n_nab_sites);
         }
      }
      nfnab[ftype] = nnab;
   }
#else
   /* 
    * Version optimized for vector machines, particularly Cray PVP
    */
#ifdef PARALLEL
#ifdef CRAY
#pragma _CRI taskcommon(nnarray, ri,rj, rk, jcell)
#endif
#endif
   int ti,tj,tk;
   static int nnarray;
   static real *ri, *rj, *rk;
   static int  *jcell;

   if( n_nabors > nnarray )
   {
      /*
       * Malloc workspace arrays. Keep them around and only deallocate if
       * required size changes.
       */
      if (ri) 
      {
	 xfree(ri); xfree(jcell);
      }
      ri = dalloc(3*n_nabors);
      rj = ri + n_nabors;
      rk = rj + n_nabors;
      jcell = ialloc(n_nabors);
      nnarray = n_nabors;
   }
   
   nnab = 0;
   for(jnab = 0; jnab < n_nabors; jnab++)    /* Loop over neighbour cells  */
   {
      jx = ix + nabor[jnab].i;       /* jx-jz are indices of neighbour cell*/
      jy = iy + nabor[jnab].j;
      jz = iz + nabor[jnab].k;
      ri[jnab] = ti = IFLOOR(jx,nx);
      rj[jnab] = tj = IFLOOR(jy,ny);
      rk[jnab] = tk = IFLOOR(jz,nz);
      jcell[jnab] = NCELL(jx-ti*nx,jy-tj*ny,jz-tk*nz);
#ifdef DEBUG1
      if(jx<0 || jx>=nx || jy<0 || jy>=ny || jz<0 || jz>=nz)
	 message(NULLI,NULLP,FATAL,"Bounds error on reloc (%d,%d,%d)",jx,jy,jz);
#endif
   }
   for(ftype = 0; ftype < n_frame_types; ftype++) /* Do Framework types first */
   {
      j0 = (ftype == 0) ? 0: 1;
      for(jnab = j0 ; jnab < n_nabors; jnab++)    /* Loop over neighbour cells  */
      {
         /* Loop over molecules in this cell, filling 'nab' with its sites    */
         for(cmol = cell[jcell[jnab]]; cmol != 0; cmol = cmol->next)
         {
            if( cmol->frame_type == ftype)
            {
               for(jsite = 0; jsite < cmol->num; jsite++)
               {
                  nab[nnab] = cmol->isite + jsite;
                  reloc[nnab].i = ri[jnab];
                  reloc[nnab].j = rj[jnab];
                  reloc[nnab].k = rk[jnab];
                  nnab++;
               }
            }
         }
      }
      if(nnab > n_nab_sites) 
	 message(NULLI,NULLP,FATAL,TONAB,nnab,n_nab_sites);
      nfnab[ftype] = nnab;
   }
#endif

   return nnab;
}
/******************************************************************************
 * Force_calc.   This is the main intermolecular site force calculation       *
 * routine                                                                    *
 ******************************************************************************/
void force_calc(site, site_force, system, species, chg, potpar, pe, stress)
real            **site,                 /* Site co-ordinate arrays       (in) */
                **site_force;           /* Site force arrays            (out) */
system_mt       *system;                /* System struct                 (in) */
spec_mt         species[];              /* Array of species records      (in) */
real            chg[];                  /* Array of site charges         (in) */
pot_mt          potpar[];               /* Array of potential parameters (in) */
double          *pe;                    /* Potential energy             (out) */
mat_mt          stress;                 /* Stress virial                (out) */
{
   int          isite, imol, i,         /* Site counter i,j                   */
                i_id, ipot;             /* Miscellaneous                      */
   int          n_frame_types;          /* ==1 for no fw, 2 if fw present.    */
   int          nsites = system->nsites,/* Local copy to keep optimiser happy */
                n_potpar = system->n_potpar,
                max_id = system->max_id;
   double       mol_diam = 2.0*mol_radius(species, system->nspecies),
                cutoff = control.cutoff + (control.strict_cutoff?mol_diam:0);
   double       reloc_lim = MAX(cutoff, control.limit+mol_diam);
   double       subcell = control.subcell; /* Local copy. May change it.      */
   int          *id   = ialloc(nsites), /* Array of site_id[nsites]           */
                *id_ptr;                /* Pointer to 'id' array              */
   int          n_nab_sites = nsites*   /* Dimension of site n'bor list arrays*/
#ifdef DEBUG2 
                                MAX(1.0,NMULT*4.19*CUBE(cutoff)/det(system->h));
#else
                                NMULT*4.19*CUBE(cutoff)/det(system->h);
#endif
   ivec_mt      *nabor, *rdf_nabor;     /* Lists of neighbour cells           */
   int          n_nabors, n_rdf_nabors; /* Number of elements in lists.       */
   int          icell, ncells;          /* Subcell counter and total = nxnynz */
   int          n_cell_list;            /* Size of link-cells "heap"          */
   int          nx, ny, nz, nmax;       /* Number of subcells in MD cell      */
   static int   onx=0, ony=0, onz=0;    /* Saved values of nx, ny, nz.        */
   static int   mmax;                   /* Saved offset of ifloor for free(). */
   int          mx, my, mz;             /* Limits for ifloor array.           */
   real         ***potp                 /* Expanded potential parameter array */
                      = (real***)arralloc((size_mt)sizeof(real), 3, P0,max_id-1,
                                          0, n_potpar-1, 0, nsites-1);
   cell_mt      *c_ptr;                 /* Heap of link cell entries for list */
   cell_mt      **cell;                 /* Array of list heads for subcells   */
   spec_mt      *spec;                  /* Temp. loop pointer to species.     */
   mat_mt       htr, htrinv;            /* Transpos and inverse of h matrix   */
   int		nthreads = nprocessors(),
   		ithread;
   /*
    * Thread-local force and stress arrays For compiler-parallel version
    */
   int		j;
   real		*sf0, *sf1, *sf2, *ssf0, *ssf1, *ssf2;
   double	*pe_n = (double *)aalloc(nthreads, double);
   mat_mt	*stress_n = (mat_mt *)aalloc(nthreads, mat_mt);
   real		***s_f_n;
   
#ifdef DEBUG2
   double ppe, rr[3], ss[3];
   int im, is, i;
   mat_mp h = system->h;
   mat_mt hinv;
#endif

   /*
    * Initialise thread-local arrays
    */
   s_f_n = aalloc(nthreads, real**);
   s_f_n[0] = site_force;
   for(ithread = 1; ithread < nthreads; ithread++)
   {
      s_f_n[ithread] = (real**)arralloc((size_mt)sizeof(real),2,0,2,0,nsites-1);
      zero_real(s_f_n[ithread][0],3*nsites);
   }
   zero_real(stress_n[0][0],9*nthreads);
   zero_double(pe_n, nthreads);

   /*
    * Choose a partition into subcells if none specified.
    */
   if(subcell <= 0.0) subcell = control.cutoff/5.0;
   nx = system->h[0][0]/subcell+0.5;
   ny = system->h[1][1]/subcell+0.5;
   nz = system->h[2][2]/subcell+0.5;
   ncells = nx*ny*nz;
   if( nx != onx || ny != ony || nz != onz )
   {
      note("MD cell divided into %d subcells (%dx%dx%d)",ncells,nx,ny,nz);
      onx = nx; ony = ny; onz = nz;
      /*
       * Allocate and fill lookup tables for floor(jx/nx) etc.
       */
      if( ifloor )
         xfree(ifloor-mmax);
      
      transpose(system->h, htr);
      invert(htr, htrinv);
      mx = (int)ceil(reloc_lim*nx*moda(htrinv))+1;
      my = (int)ceil(reloc_lim*ny*modb(htrinv))+1;
      mz = (int)ceil(reloc_lim*nz*modc(htrinv))+1;
      mmax = MAX3(mx, my, mz);
      nmax = MAX3(nx, ny, nz);

      ifloor = aalloc(2*mmax+nmax, irvec_mt)+mmax;
      for(i = -mmax; i < mmax+nmax; i++)
      {
         ifloor[i].x = IFLOOR(i,nx);
         ifloor[i].y = IFLOOR(i,ny);
         ifloor[i].z = IFLOOR(i,nz);
         ifloor[i].i = nx*IFLOOR(i,nx);
         ifloor[i].j = ny*IFLOOR(i,ny);
         ifloor[i].k = nz*IFLOOR(i,nz);
      }
   }

   /*  
    * Construct and fill expanded site-identifier array, id   
    */
   id_ptr = id;
   for (spec = species; spec < species+system->nspecies; spec++)
      for(imol = 0; imol < spec->nmols; imol++)
      {
         memcp(id_ptr, spec->site_id, spec->nsites*sizeof(int));
         id_ptr += spec->nsites;
      }
   /*   
    * Build arrays of pot. pars [max_id][nsites] for access in vector loops
    */
   for(ipot = 0; ipot < n_potpar; ipot++)
      for(i_id = 1; i_id < max_id; i_id++)
      {
#ifdef titan
NOVECTOR
#endif
         for(isite = 0; isite < nsites; isite++)
            potp[i_id][ipot][isite] = potpar[i_id*max_id+id[isite]].p[ipot];
      }
   /*
    * Allocate "heap" of list entries to build linked lists from.
    */
   n_cell_list = 1;
   for(spec = species; spec < species+system->nspecies; spec++)
      if( spec->framework )
         n_cell_list += spec->nmols*spec->nsites;
      else 
         n_cell_list += spec->nmols;
   c_ptr = aalloc(n_cell_list, cell_mt); 
   /*
    * Build a linked list of molecules/sites for each subcell.
    * "cell" is the array of list heads NX x NY x NZ.
    */
   cell = aalloc(ncells, cell_mt *);     
#pragma _CRI novector
   for( icell=0; icell < ncells; icell++)
      cell[icell] = NULL;
   fill_cells(system->c_of_m, system->nmols, site, species, system->h,
              nx, ny, nz, c_ptr, cell, &n_frame_types);
   if( n_frame_types > 2 )
      message(NULLI, NULLP, FATAL,
              "Multiple framework molecules are not supported");
   /*
    * Build lists of cells within cutoff of reference cell.
    */
   if( control.strict_cutoff )
      nabor = strict_neighbour_list(&n_nabors, system->h, cutoff, nx, ny, nz, 1);
   else
      nabor = neighbour_list(&n_nabors, system->h, cutoff, nx, ny, nz, 1);
   
   
#ifdef DEBUG2
   ppe = 0;
   spec = species; isite = 0;
   invert(h, hinv);
   for(imol = 0, im = 0; imol < system->nmols; imol++, im++)
   {
      if(im == spec->nmols)
      {
         im = 0;
         spec++;
      }
      for(is = isite; is < isite+spec->nsites; is++)
      {
         for(jsite = 0; jsite < isite; jsite++)
         {
            for( i=0; i<3; i++)
               rr[i] = site[i][jsite] - site[i][is];
            mat_vec_mul(hinv, rr, ss, 1);
            for( i=0; i<3; i++)
               ss[i] -= floor(ss[i]+0.5);
            mat_vec_mul(h, ss, rr, 1);

            r_sqr[jsite] = SUMSQ(rr);
            if( control.strict_cutoff && r_sqr[jsite] > cutoffsq )
                  r_sqr[jsite] = cutoff100sq;
         }

         hist(0,isite,r_sqr);
         kernel(0,isite,forceij,&ppe,r_sqr,chg,chg[is],
                norm,control.alpha,system->ptype,potp[id[is]]);
      }
      isite += spec->nsites;
   }
   histout();
   note("Direct pot. energy = %g",ppe*CONV_E);
#endif

#ifdef PARALLEL
#ifdef stellar
/*$dir parallel*/
#endif /* stellar */
#ifdef titan
#pragma ipdep
#endif /* titan */
#ifdef __convexc__
#pragma _CNX force_parallel
#endif /* --convexc__ */
#ifdef CRAY
#pragma _CRI taskloop private(ithread) shared(nthreads,site,chg, potp, id,\
		  n_nab_sites, n_nabors, nabor, nx, ny, nz, cell, n_frame_types, \
		  system, stress_n, pe_n, s_f_n)
#endif /* CRAY */
#endif /*PARALLEL */
   for(ithread = 0; ithread < nthreads; ithread++)
      force_inner(ithread, nthreads, site, chg, potp, id, n_nab_sites, 
		  n_nabors, nabor, nx, ny, nz, cell, n_frame_types, system,
		  stress_n[ithread], pe_n+ithread, s_f_n[ithread]);
/*
 *  Sum Pot, energies, forces and stress from each parallel invocation
 */
   for(ithread = 0; ithread < nthreads; ithread++)
   {
      *pe += pe_n[ithread];
NOVECTOR
      for(i = 0; i < 3; i++)
	 for(j = 0; j < 3; j++)
	    stress[i][j] += stress_n[ithread][i][j];
   }
   /*
    * Sum thread's copies of the site forces.  s_f_n[ithread] points
    * to the force arrays for each thread.  s_f_n[0] is just
    * site_force and the others are independant arrays.
    */
   sf0 = site_force[0]; sf1 = site_force[1]; sf2 = site_force[2];
   for(ithread = 1; ithread < nthreads; ithread++)
   {
      ssf0 = s_f_n[ithread][0];
      ssf1 = s_f_n[ithread][1];
      ssf2 = s_f_n[ithread][2];
VECTORIZE
#ifdef titan
#pragma ipdep
#endif
#ifdef __convexc__
#pragma _CNX vstrip (32)
#pragma _CNX force_vector
#pragma _CNX force_parallel_ext
#endif
#ifdef CRAY
#pragma _CRI ivdep
#endif
      for(isite = 0; isite < nsites; isite++)
      {
	 sf0[isite] += ssf0[isite];
	 sf1[isite] += ssf1[isite];
	 sf2[isite] += ssf2[isite];
      }
   }
   /*
    * Accumulate radial distribution functions
    */
   if (control.rdf_interval > 0 && 
       control.istep >= control.begin_rdf &&
       control.istep % control.rdf_interval == 0)
   {
      n_nab_sites = nsites*
                    NMULT*4.19*CUBE(control.limit+mol_diam)/det(system->h);
      rdf_nabor = strict_neighbour_list(&n_rdf_nabors, system->h, 
                                        control.limit+mol_diam, nx, ny, nz, 0);
      rdf_inner(0, 1, site, id, n_nab_sites, n_rdf_nabors, 
                rdf_nabor, nx, ny, nz, cell, n_frame_types, system);
      xfree(rdf_nabor);
   }
#ifdef DEBUG2
   histout();
#endif
   afree((gptr*)(potp+P0));  xfree(c_ptr); 
   xfree(cell);        xfree(id); 
   xfree(nabor);
   xfree(pe_n);   xfree(stress_n);
   for( ithread = 1; ithread < nthreads; ithread++)
      afree((gptr*)s_f_n[ithread]);
   xfree(s_f_n);
}
#ifdef titan
#ifdef PARALLEL
#pragma opt_level 2
#endif
#endif
/******************************************************************************
 *  Force_inner() Paralellised inner loops of force_calc.  Loops over cells   *
 *  in MD cell with stride = nomber of processors available.  Should be       *
 *  called once for each parallel thread.                                     *
 ******************************************************************************/
void
force_inner(ithread, nthreads, site, chg, potp, id, n_nab_sites, n_nabors, 
            nabor, nx, ny, nz, cell, n_frame_types, system,
            stress, pe, site_force)
int             ithread, nthreads;      /* Parallel node variables.      (in) */
real            **site;                 /* Site co-ordinate arrays       (in) */
real            chg[];                  /* Array of site charges         (in) */
real            ***potp;                /* Expanded potential parameter array */
int             id[];                   /* Array of site_id[nsites]      (in) */
int             n_nab_sites;            /* Dimension of site n'bor list arrays*/
int             n_nabors;               /* Number of elements in lists.   (in)*/
ivec_mt         *nabor;                 /* Lists of neighbour cells       (in)*/
int             nx, ny, nz;             /* Number of subcells in MD cell  (in)*/
cell_mt         **cell;                 /* Array of list heads of subcells(in)*/
int             n_frame_types;          /* ==1 for no fw, 2 if fw present (in)*/
system_mt       *system;                /* System struct                 (in) */
mat_mt          stress;                 /* Stress virial                (out) */
double          *pe;                    /* Potential energy             (out) */
real            **site_force;           /* Site force arrays            (out) */
{
                /*
                 * The following arrays are for 'neighbour site list'
                 * quantities and should be dimensioned to the max value of
                 * 'nnab'.  A rough approx is the ratio of the volume of
                 * the "cutoff sphere" to that of the MD cell times nsites.
                 * This may be too small for inhomogeneous systems, but at
                 * least it scales with the cutoff radius.
                 */
   int          *nab  = ialloc(n_nab_sites);    /* Neigbour site gather vector*/
   rvec_mt      *reloc = aalloc(n_nab_sites, rvec_mt); /* Site PBC shifts     */
   real         *nab_sx  = dalloc(n_nab_sites), /* 'Gathered' list of         */
                *nab_sy  = dalloc(n_nab_sites), /*   neighbour site co-ords   */
                *nab_sz  = dalloc(n_nab_sites), /*   - x,y,z components.      */
                *forcejx = dalloc(n_nab_sites), /* List of neighbour site     */
                *forcejy = dalloc(n_nab_sites), /*  forces in gathered form   */
                *forcejz = dalloc(n_nab_sites), /*  - xyz components.         */
                *rx      = dalloc(n_nab_sites), /* Reference to neigbour site */
                *ry      = dalloc(n_nab_sites), /* - site vector adjusted for */
                *rz      = dalloc(n_nab_sites), /*  periodic boundaries. xyz. */
                *r_sqr   = dalloc(n_nab_sites), /* Squared site-site distance */
                *nab_chg = dalloc(n_nab_sites), /* Gathered neig. site charges*/
                *forceij = dalloc(n_nab_sites); /* -V'(r) / r                 */
   real         **nab_pot                       /* Gathered pot par array     */
                = (real**)arralloc((size_mt)sizeof(real), 2,
                                   0, system->n_potpar-1, 0, n_nab_sites-1);
   real         **pp, **ppp;            /* Loop pointer variables for potp.   */
   real         force_cpt, site0, site1, site2, s00, s01, s02, s11, s12, s22;
                                   /* Accumulators for forces and stresses.   */
   real         rrx, rry, rrz;                  /* Scalar loop temporaries    */
   real         h00, h01, h02, h11, h12, h22;   /* Temp copies of system->h   */
   double       norm = 2.0*control.alpha/sqrt(PI);      /* Coulombic prefactor*/
   double       cutoffsq = SQR(control.cutoff), /* Temporary copy for optim'n */
                cutoff100sq = 10000.0*cutoffsq;
   int          ix, iy, iz;             /* 3-d cell indices for ref and neig. */
   int          icell,                  /* Index for cells of molecule pair   */
                nnab, jbeg, jmin, jmax, /* Number of sites in neighbour list  */
                isite, jsite, ipot, lim;/* Counters.                          */
   int          nsites = system -> nsites;      /* Temporary copy for optim'n */
   int          nfnab[2];               /* Number of non-fw and fw neighbours */
   cell_mt      *cmol;                  /* Loop counter for link cells.       */

   s00 = s01 = s02 = s11 = s12 = s22 = 0.0;     /* Accumulators for stress    */

/******************************************************************************
 *  Start of main loop over all subcells.                                     *
 *  First build "site neighbour list" containing all sites belonging to       *
 *  molecules in this subcell and all others in the cell neighbour list.      *
 *  Use "gather" to construct corresponding arrays of co-ordinates, charges   *
 *  and potential parameters.                                                 *
 *  Then loop over all sites in THIS cell and calculate pair distances,       *
 *  potential forces and stress.                                              *
 ******************************************************************************/
   for( icell = ithread; icell < nx*ny*nz; icell += nthreads)
   {
      if(cell[icell] == NULL) continue;       /* Empty cell - go on to next */
      ix = icell/ (ny*nz);
      iy = icell/nz - ny*ix;
      iz = icell - nz*(iy + ny*ix);
      nnab = 0;
#ifdef DEBUG1
      printf("Working on cell %4d (%d,%d,%d) (sites %4d to %4d)\n", icell,
             ix,iy,iz,cell[icell]->isite,cell[icell]->isite+cell[icell]->num-1);
      printf("\n jcell\tjx jy jz\tNsites\n");
#endif
      /*
       * Build site neighbour list 'nab' from cell list.
       */ 
      nnab = site_neighbour_list(nab, reloc, n_nab_sites, nfnab, n_frame_types, 
                                 n_nabors, ix, iy, iz, nx, ny, nz, nabor, cell);
#ifdef DEBUG4
      for(jsite=0; jsiteh[0][0];   h01 = system->h[0][1];   h02 = system->h[0][2];
      h11 = system->h[1][1];   h12 = system->h[1][2];   h22 = system->h[2][2];
VECTORIZE
      for(jsite=0; jsitenext)
      {
         if( cmol->frame_type )
         {
            jmin = 0;
            jmax = nfnab[0];
         }
         else
         {
            jmin = jbeg += cmol->num;
            jmax = nnab;
         }
         lim = cmol->isite + cmol->num;
         for(isite = cmol->isite; isite < lim; isite++)
         {                                   /* Loop over sites in molecule */
            /*
             * Construct pot'l param arrays corresponding to neighbour sites.
             */
            pp = potp[id[isite]];
            ppp = nab_pot;
            for(ipot = 0; ipot < system->n_potpar; ipot++)
            {
               gather(jmax, *ppp++, *pp++, nab, nsites);
            }
#ifdef DEBUG1
            if(isite == 100)
#endif
#if defined(DEBUG1) || defined(DEBUG5)
            { int jnab;
            for(jnab = jmin; jnab < jmax; jnab++)
               printf("%4d %4d\n", jnab,nab[jnab]);
           }
#endif
            site0=site[0][isite]; site1=site[1][isite]; site2=site[2][isite];
VECTORIZE
            for(jsite=jmin; jsite < jmax; jsite++)
            {
               rrx = nab_sx[jsite] - site0;
               rry = nab_sy[jsite] - site1;
               rrz = nab_sz[jsite] - site2;
               r_sqr[jsite] = rrx*rrx+rry*rry+rrz*rrz;
               rx[jsite] = rrx;
               ry[jsite] = rry;
               rz[jsite] = rrz;
            }
            if( (jsite = jmin+search_lt(jmax-jmin, r_sqr+jmin, 1, TOO_CLOSE))
               < jmax )
               message(NULLI, NULLP, WARNING, TOOCLS,
                       isite, nab[jsite], sqrt(TOO_CLOSE));

            if( control.strict_cutoff )
               for(jsite = jmin; jsite < jmax; jsite++)
                  if( r_sqr[jsite] > cutoffsq )
                     r_sqr[jsite] = cutoff100sq;

#ifdef DEBUG2
            hist(jmin, jmax, r_sqr);
#endif
               
            /*  Call the potential function kernel                            */
            kernel(jmin, jmax, forceij, pe, r_sqr, nab_chg, chg[isite],
                   norm, control.alpha, system->ptype, nab_pot);
            site0 = site1 = site2 = 0.0;
VECTORIZE
            for(jsite=jmin; jsite < jmax; jsite++)
            {
               force_cpt  = forceij[jsite]*rx[jsite];
               s00         += force_cpt * rx[jsite];
               s02         += force_cpt * rz[jsite];
               s01         += force_cpt * ry[jsite];
               site0           -= force_cpt;
               forcejx[jsite]  += force_cpt;
            }
VECTORIZE
            for(jsite=jmin; jsite < jmax; jsite++)
            {
               force_cpt = forceij[jsite]*ry[jsite];
               s11         += force_cpt * ry[jsite];
               s12         += force_cpt * rz[jsite];
               site1           -= force_cpt;
               forcejy[jsite]  += force_cpt;
            }
VECTORIZE
            for(jsite=jmin; jsite < jmax; jsite++)
            {
               force_cpt = forceij[jsite]*rz[jsite];
               s22         += force_cpt * rz[jsite];
               site2           -= force_cpt;
               forcejz[jsite]  += force_cpt;
            }
            site_force[0][isite] += site0;
            site_force[1][isite] += site1;
            site_force[2][isite] += site2;
#ifdef DEBUG3
            printf("PE = %f\n",pe[0]);
#endif
         }
      }
      spxpy(nnab, forcejx, site_force[0], nab);
      spxpy(nnab, forcejy, site_force[1], nab);
      spxpy(nnab, forcejz, site_force[2], nab);
   }
   stress[0][0]  += s00;
   stress[0][1]  += s01;
   stress[0][2]  += s02;
   stress[1][1]  += s11;
   stress[1][2]  += s12;
   stress[2][2]  += s22;

   afree((gptr*)nab_pot);
   xfree(nab);     xfree(reloc);  xfree(nab_chg);
   xfree(r_sqr);   xfree(forceij);
   xfree(rx);      xfree(ry);      xfree(rz);
   xfree(forcejx); xfree(forcejy); xfree(forcejz);
   xfree(nab_sx);  xfree(nab_sy);  xfree(nab_sz);
}
/******************************************************************************
 *  Rdf_inner() Paralellised inner loops of force_calc.  Based on force_inner *
 *     but only calls rdf_accum().                                            *
 ******************************************************************************/
void
rdf_inner(ithread, nthreads, site, id, n_nab_sites, n_nabors, 
            nabor, nx, ny, nz, cell, n_frame_types, system)
int             ithread, nthreads;      /* Parallel node variables.      (in) */
real            **site;                 /* Site co-ordinate arrays       (in) */
int             id[];                   /* Array of site_id[nsites]      (in) */
int             n_nab_sites;            /* Dimension of site n'bor list arrays*/
int             n_nabors;               /* Number of elements in lists.   (in)*/
ivec_mt         *nabor;                 /* Lists of neighbour cells       (in)*/
int             nx, ny, nz;             /* Number of subcells in MD cell  (in)*/
cell_mt         **cell;                 /* Array of list heads of subcells(in)*/
int             n_frame_types;          /* ==1 for no fw, 2 if fw present (in)*/
system_mt       *system;                /* System struct                  (in)*/
{
   int          *nab  = ialloc(n_nab_sites);    /* Neigbour site gather vector*/
   rvec_mt      *reloc = aalloc(n_nab_sites, rvec_mt); /* Site PBC shifts     */
   real         *nab_sx  = dalloc(n_nab_sites), /* 'Gathered' list of         */
                *nab_sy  = dalloc(n_nab_sites), /*   neighbour site co-ords   */
                *nab_sz  = dalloc(n_nab_sites), /*   - x,y,z components.      */
                *r_sqr   = dalloc(n_nab_sites); /* Squared site-site distance */
   real         site0, site1, site2;
   real         rrx, rry, rrz;                  /* Scalar loop temporaries    */
   real         h00, h01, h02, h11, h12, h22;   /* Temp copies of system-> h  */
   int          ix, iy, iz;             /* 3-d cell indices for ref and neig. */
   int          icell,                  /* Index for cells of molecule pair   */
                nnab, jbeg, jmin, jmax, /* Number of sites in neighbour list  */
                isite, jsite, lim;      /* Counters.                          */
   int          nsites = system -> nsites;      /* Temporary copy for optim'n */
   int          nfnab[2];               /* Number of non-fw and fw neighbours */
   cell_mt      *cmol;                  /* Loop counter for link cells.       */

/******************************************************************************
 *  Start of main loop over subcells.                                         *
 ******************************************************************************/
   for( icell = ithread; icell < nx*ny*nz; icell += nthreads)
   {
      if(cell[icell] == NULL) continue;       /* Empty cell - go on to next */
      ix = icell/ (ny*nz);
      iy = icell/nz - ny*ix;
      iz = icell - nz*(iy + ny*ix);

      /*
       * Build site neighbour list 'nab' from cell list.
       */ 
      nnab = site_neighbour_list(nab, reloc, n_nab_sites, nfnab, n_frame_types, 
                                 n_nabors, ix, iy, iz, nx, ny, nz, nabor, cell);
      gather(nnab, nab_sx, site[0], nab, nsites); /* Construct list of site  */
      gather(nnab, nab_sy, site[1], nab, nsites); /* co-ordinates from nabo  */
      gather(nnab, nab_sz, site[2], nab, nsites); /* list.                   */

      /*
       * Apply periodic boundary conditions to neighbour site co-ords.
       * Assume h matrix is upper triangular.
       */
      h00 = system->h[0][0];   h01 = system->h[0][1];   h02 = system->h[0][2];
      h11 = system->h[1][1];   h12 = system->h[1][2];   h22 = system->h[2][2];
VECTORIZE
      for(jsite=0; jsitenext)
      {
         if( cmol->frame_type )
         {
            jmin = 0;
            jmax = nfnab[0];
         }
         else
         {
            jmin = jbeg += cmol->num;
            jmax = nnab;
         }
         lim = cmol->isite + cmol->num;
         for(isite = cmol->isite; isite < lim; isite++)
         {                                   /* Loop over sites in molecule */
            site0=site[0][isite]; site1=site[1][isite]; site2=site[2][isite];
VECTORIZE
            for(jsite=jmin; jsite < jmax; jsite++)
            {
               rrx = nab_sx[jsite] - site0;
               rry = nab_sy[jsite] - site1;
               rrz = nab_sz[jsite] - site2;
               r_sqr[jsite] = rrx*rrx+rry*rry+rrz*rrz;
            }
            /*
             * Accumulate radial distribution functions
             */
            rdf_accum(jmin, jmax, r_sqr, id[isite], id, nab);
         }
      }
   }
   xfree(nab);     xfree(reloc);
   xfree(r_sqr);
   xfree(nab_sx);  xfree(nab_sy);  xfree(nab_sz);
}
$EOD
$!
$CREATE ewald-RIL.c
$DECK
/* MOLecular DYnamics simulation code, Moldy.
Copyright (C) 1988, 1992, 1993 Keith Refson
 
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, or (at your option) any
later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */
/******************************************************************************
 * Ewald	The reciprocal-space part of the standard Ewald sum technique *
 ******************************************************************************
 *      Revision Log
 *       $Log: ewald.c,v $
 *       Revision 2.8.1.6  1996/11/12 15:33:52  keith
 *       Updated to match Ewald.c 2.13 with cache tuning etc.
 *
 *       Revision 2.8.1.5  1996/03/25 17:36:15  keith
 *       Fixed memory leak introduced in previous revision.
 *       Now prints # k-vectors just like RKL version
 *
 *       Revision 2.8.1.4  1996/02/07 20:51:44  keith
 *       Restructured for convergence of std and RIL versions
 *        - Implemented pre-computed k-vector set
 *        - Re-based trig arrays to start at zero for ANSI conformance.
 *
 *       Revision 2.8.1.3  1996/01/25 21:01:41  keith
 *       Fixed bug in allocation of sites to processors which caused
 *       crash on large # procs.
 *
 *       Revision 2.8.1.2  1995/12/06 15:07:50  keith
 *       Fixed bug which caused core dump for small k-cutoff (hmax=0)
 *
 *       Revision 2.8.1.2  1994/12/30 11:53:51  keith
 *       Finxed bug which caused core dump for small k-cutoff (hmax=0)
 *
 * Revision 2.8.1.1  1994/07/19  10:40:57  keith
 * Implementation of W. Smith's RIL parallelization strategy
 * (Comp Phys Commun, 67, (1992) 293-406
 * This involves distributing memory better but does communication
 * in the inner k-vector loop.  On most machines this seems to
 * defeat the parallelization altogether - it runs more slowly
 * in parallel on the Titan than in serial mode even for 32772 sites.
 *
 * Revision 2.8  1994/06/22  09:37:02  keith
 * Performance optimization of "trig rules" loops.
 *
 * Revision 2.7  1994/06/08  13:13:59  keith
 * New version of array allocator which breaks up requests for DOS.
 * Now must use specific "afree()" paired with arralloc().
 *
 * Revision 2.6  1994/02/17  16:38:16  keith
 * Significant restructuring for better portability and
 * data modularity.
 *
 * Got rid of all global (external) data items except for
 * "control" struct and constant data objects.  The latter
 * (pot_dim, potspec, prog_unit) are declared with CONST
 * qualifier macro which evaluates to "const" or nil
 * depending on ANSI/K&R environment.
 * Also moved as many "write" instantiations of "control"
 * members as possible to "startup", "main" leaving just
 * "dump".
 *
 * Declared as "static"  all functions which should be.
 *
 * Revision 2.5  1994/01/18  13:32:27  keith
 * Null update for XDR portability release
 *
 * Revision 2.3  93/10/28  10:27:48  keith
 * Corrected declarations of stdargs functions to be standard-conforming
 * 
 * Revision 2.1  93/09/02  12:31:55  keith
 * Optimized qsincos() -- should give up to 25% speed improvement on
 * compilers without assert no aliasing options.
 * 
 * Revision 2.1  93/05/17  10:42:22  keith
 * Optimized qsincos() -- should give up to 25% speed improvement on
 * compilers without assert no aliasing options.
 * 
 * Revision 2.0  93/03/15  14:49:02  keith
 * Added copyright notice and disclaimer to apply GPL
 * to all modules. (Previous versions licensed by explicit 
 * consent only).
 * 
 * Revision 1.23  93/03/12  12:22:38  keith
 * Reorganized defines to recognise all ANSI (__type__) forms.
 * Moved spxpy() from aux.c to force.c and force_parallel.c
 * 
 * 
 * Revision 1.23  93/03/12  12:21:50  keith
 * *** empty log message ***
 * 
 * Revision 1.22  93/03/09  15:58:28  keith
 * Changed all *_t types to *_mt for portability.
 * Reordered header files for GNU CC compatibility.
 * 
 * Revision 1.21  92/06/26  17:02:58  keith
 * Got rid of assumption that memory returned by talloc() or
 * arralloc() is zeroed.  This enhances ANSI compatibility.
 * Removed memory zeroing from alloc.c() in consequence.
 * 
 * Revision 1.20  91/11/26  10:26:34  keith
 * Corrected calculation of sheet energy term for charged framework.
 * Split force loop so as to omit frame-frame force (and stress) terms.
 * 
 * Revision 1.19  91/08/15  18:11:52  keith
 * Modifications for better ANSI/K&R compatibility and portability
 * --Changed sources to use "gptr" for generic pointer -- typedefed in "defs.h"
 * --Tidied up memcpy calls and used struct assignment.
 * --Moved defn of NULL to stddef.h and included that where necessary.
 * --Eliminated clashes with ANSI library names
 * --Modified defs.h to recognise CONVEX ANSI compiler
 * --Modified declaration of size_mt and inclusion of sys/types.h in aux.c
 *   for GNU compiler with and without fixed includes.
 * 
 * Revision 1.18  91/05/29  16:33:01  keith
 * Modified code for speed improvement in TITAN
 * 
 * Revision 1.17  91/03/12  15:42:31  keith
 * Tidied up typedefs size_mt and include file 
 * Added explicit function declarations.
 * 
 * Revision 1.16  91/02/07  16:52:18  keith
 * Rewrote trig identity loops for better vectorization on Titan.
 * Finally deleted ancient commented-out code (#if OLDEWALD and VCALLS).
 * 
 * Revision 1.15  90/09/28  13:29:15  keith
 * Inserted braces around VECTORIZE directives and changed include files
 * for STARDtardent 3000 series (via cond. comp symbol "ardent").
 * 
 * Revision 1.14  90/08/29  11:01:19  keith
 * Modified to keep consistency with ewald_parallel.c r1.8
 * 
 * Revision 1.13  90/08/02  15:50:17  keith
 * Modified to exclude framework-framework interactions.
 * N.B. Excluded from pe and stress but NOT forces (as they sum to 0).
 * 
 * Revision 1.12  90/05/16  18:40:04  keith
 * Renamed own freer from cfree to tfree.
 * 
 * Revision 1.11  90/05/02  15:33:27  keith
 * Make declaration of saxpy() conditional along with use.
 * 
 * Revision 1.10  90/01/15  12:24:05  keith
 * Corrected declaration of arralloc from void* to char* to keep lint happy.
 * 
 * Revision 1.9  90/01/01  20:07:20  keith
 * Parcelled up generation of qcoskr etc into separate function and
 * created temp's site_fx etc to point at site_force[0] etc.
 * - Generates substabtially better code on Stellar.
 * 
 * Revision 1.8  89/12/22  19:31:53  keith
 * New version of arralloc() orders memory so that pointers come FIRST.
 * This means you can simply free() the pointer returned (if l.b. = 0).
 * 
 * Revision 1.7  89/12/21  16:29:47  keith
 * Reversed indices in 'site' and 'site_force' to allow stride of 1 in ewald.
 * 
 * Revision 1.6  89/12/15  12:56:26  keith
 * Added conditional ionclusion of  for stellar
 * 
 * Revision 1.5  89/11/01  17:29:10  keith
 * Sin and cos loop vectorised - mat_vec_mul extracted from loop.
 * 'Uniform charge sheet' term added in case of electrically charged system.
 * 
 * Revision 1.5  89/10/26  11:29:26  keith
 * Sin and cos loop vectorised - mat_vec_mul extracted from loop.
 * 'Uniform charge sheet' term added in case of electrically charged layer.
 * 
 * Revision 1.5  89/10/26  11:27:31  keith
 * Sin and cos loop vectorised - mat_vec_mul extracted from loop.
 * 
 * Revision 1.4  89/10/02  11:39:14  keith
 * Fixed error in *star macros which assumed rlv's were cols of h(-1) r.t. rows.
 * 
 * Revision 1.3  89/06/09  13:38:17  keith
 * Older code for computation of q cos/sin k.r restored conditionally by
 * use of macro OLDEWALD.  This is for more primitive vectorising compilers.
 * 
 * Revision 1.2  89/06/08  10:42:51  keith
 * Modified to circumvent compiler bug in VMS/VAXC 2.4-026
 * 
 * Revision 1.1  89/04/20  16:00:39  keith
 * Initial revision
 * 
 */
#ifndef lint
static char *RCSid = "$Header: /home/eeyore_data/keith/md/moldy/RCS/ewald.c,v 2.8.1.6 1996/11/12 15:33:52 keith Exp $";
#endif
/*========================== Program include files ===========================*/
#include 	"defs.h"
/*========================== Library include files ===========================*/
#ifdef stellar
#   include 	
#else
#ifdef titan
#   include 	
#else
#   include 	
#endif
#endif
#include	"stddef.h"
#include 	"stdlib.h"
#include        "string.h"
/*========================== Program include files ===========================*/
#include 	"structs.h"
#include 	"messages.h"
/*========================== External function declarations ==================*/
gptr            *talloc();	       /* Interface to memory allocator       */
void            tfree();	       /* Free allocated memory	      	      */
void            afree();	       /* Free allocated array	      	      */
double	err_fn();			/* Error function		      */
double	det();				/* Determinant of 3x3 matrix	      */
void	invert();			/* Inverts a 3x3 matrix		      */
void	mat_vec_mul();			/* Multiplies a 3x3 matrix by 3xN vect*/
void	mat_sca_mul();			/* Multiplies a 3x3 matrix by scalar  */
void	transpose();			/* Transposes a 3x3 matrix	      */
void    zero_real();            	/* Initialiser                        */
void    zero_double();          	/* Initialiser                        */
double	sum();				/* Sum of elements of 'real' vector   */
#if defined(ANSI) || defined(__STDC__)
gptr	*arralloc(size_mt,int,...); 	/* Array allocator		      */
void	note(char *,...);		/* Write a message to the output file */
void	message(int *,...);		/* Write a warning or error message   */
#else
gptr	*arralloc();	        	/* Array allocator		      */
void	note();				/* Write a message to the output file */
void	message();			/* Write a warning or error message   */
#endif
/*========================== External data references ========================*/
extern	contr_mt	control;       	/* Main simulation control record     */
extern int		ithread, nthreads;
/*========================== Macros ==========================================*/
#define astar hinvp[0]
#define bstar hinvp[1]
#define cstar hinvp[2]
#define moda(hmat) (hmat[0][0])
#define modb(hmat) sqrt(SQR(hmat[0][1]) + SQR(hmat[1][1]))
#define modc(hmat) sqrt(SQR(hmat[0][2]) + SQR(hmat[1][2]) + SQR(hmat[2][2]))
/*========================== Cache Parameters=================================*/
/* The default values are for the Cray T3D but are probably good enough 
 *  for most other systems too. */
#ifndef NCACHE
#   define NCACHE (256*sizeof(double)/sizeof(real))
#endif
#ifndef NLINE
#   define NLINE  (4*sizeof(double)/sizeof(real))
#endif
/*============================================================================*/
   struct s_hkl {double kx, ky, kz; int h,k,l;};
/*****************************************************************************
 * qsincos().  Evaluate q sin(k.r) and q cos(k.r).  This is in a separate    *
 * function because some compilers (notably Stellar's) generate MUCH better  *
 * vector code this way. 						     *
 *****************************************************************************/
static
void      qsincos(coshx,sinhx,cosky,sinky,coslz,sinlz,chg,
		  qcoskr,qsinkr,k,l,nsites)
real coshx[], sinhx[], cosky[], sinky[], coslz[], sinlz[],
     chg[], qcoskr[], qsinkr[];
int  k,l,nsites;
{
   int is;
   real qckr;
   
   if( k >= 0 )
      if( l >= 0 )
      {
VECTORIZE
	 for(is = 0; is < nsites; is++)
	 {
	    qckr = chg[is]*(
		  (coshx[is]*cosky[is] - sinhx[is]*sinky[is])*coslz[is] 
                - (sinhx[is]*cosky[is] + coshx[is]*sinky[is])*sinlz[is]);
	    qsinkr[is] = chg[is]*(
                  (sinhx[is]*cosky[is] + coshx[is]*sinky[is])*coslz[is] 
		+ (coshx[is]*cosky[is] - sinhx[is]*sinky[is])*sinlz[is]);
	    qcoskr[is] = qckr;
	 }
      }
      else
      {
VECTORIZE
	 for(is = 0; is < nsites; is++)
	 {
	    qckr = chg[is]*(
		  (coshx[is]*cosky[is] - sinhx[is]*sinky[is])*coslz[is] 
                + (sinhx[is]*cosky[is] + coshx[is]*sinky[is])*sinlz[is]);
	    qsinkr[is] = chg[is]*(
                  (sinhx[is]*cosky[is] + coshx[is]*sinky[is])*coslz[is] 
		- (coshx[is]*cosky[is] - sinhx[is]*sinky[is])*sinlz[is]);
	    qcoskr[is] = qckr;
	 }
      }
   else
      if( l >= 0 )
      {
VECTORIZE
	 for(is = 0; is < nsites; is++)
	 {
	    qckr = chg[is]*(
		  (coshx[is]*cosky[is] + sinhx[is]*sinky[is])*coslz[is] 
                - (sinhx[is]*cosky[is] - coshx[is]*sinky[is])*sinlz[is]);
	    qsinkr[is] = chg[is]*(
                  (sinhx[is]*cosky[is] - coshx[is]*sinky[is])*coslz[is] 
		+ (coshx[is]*cosky[is] + sinhx[is]*sinky[is])*sinlz[is]);
	    qcoskr[is] = qckr;
	 }
      }
      else
      {
VECTORIZE
	 for(is = 0; is < nsites; is++)
	 {
	    qckr = chg[is]*(
		  (coshx[is]*cosky[is] + sinhx[is]*sinky[is])*coslz[is] 
                + (sinhx[is]*cosky[is] - coshx[is]*sinky[is])*sinlz[is]);
	    qsinkr[is] = chg[is]*(
                  (sinhx[is]*cosky[is] - coshx[is]*sinky[is])*coslz[is] 
		- (coshx[is]*cosky[is] + sinhx[is]*sinky[is])*sinlz[is]);
	    qcoskr[is] = qckr;
	 }
     }
}

/******************************************************************************
 * Calculate cos and sin of astar*x, bstar*y & cstar*z for each charged site  *
 * Use addition formulae to get sin(h*astar*x)=sin(Kx*x) etc for each site    *
 ******************************************************************************/
static
void trig_recur(chx,shx,sin1x,cos1x,site0,site1,site2,kstar,hmax,ns0,ns1)
real **chx, **shx, sin1x[],cos1x[], *site0, *site1, *site2, *kstar;
int ns0, ns1, hmax;
{
   int h, is;
   real *coshx, *sinhx, *cm1, *sm1, coss, kr;
   real ksx = kstar[0], ksy = kstar[1], ksz = kstar[2];

   sinhx = shx[0];    coshx = chx[0]; 
VECTORIZE
   for(is = ns0; is < ns1; is++)
   {
      coshx[is] = 1.0;
      sinhx[is] = 0.0;
   }      

   if( hmax >= 1 )
   {
      coshx = chx[1]; sinhx = shx[1];
VECTORIZE
      for(is = ns0; is < ns1; is++)
      {
	 kr = ksx*site0[is]+ksy*site1[is]+ksz*site2[is];
	 coshx[is] = cos(kr); sinhx[is] = sin(kr);
      }

      memcp(cos1x+ns0, coshx+ns0, (ns1-ns0)*sizeof(real));
      memcp(sin1x+ns0, sinhx+ns0, (ns1-ns0)*sizeof(real));

      for(h = 2; h <= hmax; h++)
      {
         coshx = chx[h]; sinhx = shx[h];
         cm1 = chx[h-1]; sm1 = shx[h-1];
VECTORIZE
         for(is = ns0; is < ns1; is++)
      	 {
      	    coss      = cm1[is]*cos1x[is] - sm1[is]*sin1x[is];
      	    sinhx[is] = sm1[is]*cos1x[is] + cm1[is]*sin1x[is];
      	    coshx[is] = coss;
      	 }
      }
   }
}

/******************************************************************************
 * allocate_arrays(). Memory allocation for Ewald's arrays.  We malloc one    *
 * large block and divide it out so as to precisely control the relative      *
 * offsets of the arrays.  This is to avoid cache conflicts.                  *
 * Revert to bog-standard method for MSDOS				      *
 ******************************************************************************/
static real*  allocate_arrays(nsarray, hmax, kmax, lmax,
			      chx, cky, clz, shx, sky, slz, chg,
			      cos1x, cos1y, cos1z, sin1x, sin1y, sin1z, 
			      qcoskr, qsinkr)
int	nsarray, hmax, kmax, lmax;
real	***chx, ***cky, ***clz, ***shx, ***sky, ***slz;
real	**cos1x, **cos1y, **cos1z, **sin1x, **sin1y, **sin1z;
real	**chg, **qcoskr, **qsinkr;
{
   int h, k,l; 
   real *csp, *base;

#ifndef ALLOC_SEPARATELY
   /*
    * Attempt to cache-align these arrays for optimum performance.
    * This requires NON-ANSI pointer conversion and arithmetic.
    * It should work with any UNIX address space, so make it conditional.
    */
   base = dalloc((2*(hmax+kmax+lmax)+15)*nsarray+10*NLINE+NCACHE);
#if defined(ALLOC_ALIGN) && !defined(__BOUNDS_CHECKING_ON)
   csp = (real*)0 + (((base - (real*)0) - 1 | NCACHE-1) + 1);
#else
   csp = base;
#endif

   *chx = (real**)arralloc(sizeof(real*),1,0,hmax);
   *cky = (real**)arralloc(sizeof(real*),1,0,kmax);
   *clz = (real**)arralloc(sizeof(real*),1,0,lmax);
   *shx = (real**)arralloc(sizeof(real*),1,0,hmax);
   *sky = (real**)arralloc(sizeof(real*),1,0,kmax);
   *slz = (real**)arralloc(sizeof(real*),1,0,lmax);
 
   *qcoskr = csp; csp += nsarray + NLINE;
   *qsinkr = csp; csp += nsarray + NLINE;
   *chg = csp;    csp += nsarray + NLINE;
   for(h = 0; h <= hmax; h++, csp += nsarray)
     (*chx)[h] = csp;
   csp += NLINE;
   for(h = 0; h <= hmax; h++, csp += nsarray)
     (*shx)[h] = csp;
   csp += NLINE;
   for(k = 0; k <= kmax; k++, csp += nsarray)
     (*cky)[k] = csp;
   csp += NLINE;
   for(k = 0; k <= kmax; k++, csp += nsarray)
     (*sky)[k] = csp;
   csp += NLINE;
   for(l = 0; l <= lmax; l++, csp += nsarray)
     (*clz)[l] = csp;
   csp += NLINE;
   for(l = 0; l <= lmax; l++, csp += nsarray)
     (*slz)[l] = csp;
   csp += NLINE;
   *cos1x = csp;  csp += nsarray;
   *cos1y = csp;  csp += nsarray;
   *cos1z = csp;  csp += nsarray + NLINE;
   *sin1x = csp;  csp += nsarray;
   *sin1y = csp;  csp += nsarray;
   *sin1z = csp;  csp += nsarray;
#else
   *chx = (real**)arralloc((size_mt)sizeof(real),2, 0, hmax, 0, nsarray-1);
   *cky = (real**)arralloc((size_mt)sizeof(real),2, 0, kmax, 0, nsarray-1);
   *clz = (real**)arralloc((size_mt)sizeof(real),2, 0, lmax, 0, nsarray-1);
   *shx = (real**)arralloc((size_mt)sizeof(real),2, 0, hmax, 0, nsarray-1);
   *sky = (real**)arralloc((size_mt)sizeof(real),2, 0, kmax, 0, nsarray-1);
   *slz = (real**)arralloc((size_mt)sizeof(real),2, 0, lmax, 0, nsarray-1);
   *cos1x = (*chx)[1]; *cos1y = (*cky)[1]; *cos1z = (*clz)[1];
   *sin1x = (*shx)[1]; *sin1y = (*sky)[1]; *sin1z = (*slz)[1];

   csp = base = dalloc(3*nsarray);
   *chg = csp;    csp += nsarray;
   *qcoskr = csp; csp += nsarray;
   *qsinkr = csp; csp += nsarray;
#endif

   return base;
}
/******************************************************************************
 *  Ewald  Calculate reciprocal-space part of coulombic forces		      *
 ******************************************************************************/
void	ewald(site,site_force,system,species,chgx,pe,stress)
real		**site,			/* Site co-ordinate arrays	 (in) */
		**site_force;		/* Site force arrays		(out) */
system_mp	system;			/* System record		 (in) */
spec_mt	species[];			/* Array of species records	 (in) */
real		chgx[];			/* Array of site charges	 (in) */
double		*pe;			/* Potential energy		(out) */
mat_mt		stress;			/* Stress virial		(out) */
{
   mat_mt	hinvp;			/* Matrix of reciprocal lattice vects*/
   int		h, k, l;		/* Recip. lattice vector indices     */
   int		i, j, is, ssite;	/* Counters.			     */
   spec_mp	spec;			/* species[ispec]		     */
   int		nsites = system->nsites;
   double	pe_k,			/* Pot'l energy for current K vector */
		coeff, coeff2;		/* 2/(e0V) * A(K) & similar	     */
   double	r_4_alpha = -1.0/(4.0 * control.alpha * control.alpha);
   double	sqcoskr,sqsinkr,	/* Sum q(i) sin/cos(K.r(i))          */
		sqcoskrn, sqsinkrn,
		sqcoskrf, sqsinkrf;
   double	ksq,			/* Squared magnitude of K vector     */
		kcsq = SQR(control.k_cutoff);
   double	kx,ky,kz,kzt;
   vec_mt	kv;			/* (Kx,Ky,Kz)  			     */
   real		force_comp, kv0, kv1, kv2, sfx, sfy;
   struct	s_hkl *hkl, *phkl;
   int		nhkl = 0;
/*
 * Maximum values of h, k, l  s.t. |k| < k_cutoff
 */
   int		hmax = floor(control.k_cutoff/(2*PI)*moda(system->h)),
		kmax = floor(control.k_cutoff/(2*PI)*modb(system->h)),
		lmax = floor(control.k_cutoff/(2*PI)*modc(system->h));
   real		sqexpkr[4];
   mat_mt	stress_ew;
/*
 * lower and upper limits for parallel loops.  This doles out the sites
 * in parcels of "nsnode0" sites on "nns" threads and "nsnode0+1" sites
 * on the rest. "ns0" marks the beginning of the sites for this thread
 * and "nsarr0" the number of sites allocated to the node.  
 * N. B. The parcelling algorithm in W. Smith's paper FAILS if
 *       nthreads**2 > nsites.
 */
   int		nsnode0 = nsites/nthreads;
   int		nns = nthreads*(nsnode0 + 1 ) - nsites;
   int		nsarr0 = nsnode0 + ((ithread < nns)?0:1);
   int		ns0 = MIN(ithread, nns)*nsnode0 + MAX(ithread-nns,0)*(nsnode0+1);
   int		ns1 = ns0 + nsarr0, ns0f, ns1f;
/*
 * Round up size of arrays to cache sub-multiple size.
 */
#if 1
   int		nsarray = (nsarr0 - 1 | NCACHE - 1) + 1;
#else
   int		nsarray = nsarr0;
#endif
/*
 * Arrays for cos & sin (h x(i)), (k y(i)) and (l z(i)) eg chx[h][isite]
 * and pointers to a particular h,k or l eg coshx[is] = chh[2][is]
 */
   real		**chx, **cky, **clz, **shx, **sky, **slz;
   real		*cshkl;
   real		*coshx, *cosky, *coslz, *sinhx, *sinky, *sinlz;
   real		*cos1x, *cos1y, *cos1z, *sin1x, *sin1y, *sin1z;
   real		*chg;
   real		*site_fx = site_force[0]+ns0,
   		*site_fy = site_force[1]+ns0,
   		*site_fz = site_force[2]+ns0;
   real		*qcoskr,		/* q(i) cos(K.R(i))	      */
		*qsinkr;		/* q(i) sin(K.R(i))	      */
   double	vol = det(system->h);	/* Volume of MD cell		      */
   static	double	self_energy,	/* Constant self energy term	      */
   			sheet_energy;	/* Correction for non-neutral system. */
   static	boolean init = true;	/* Flag for the first call of function*/
   static	int	nsitesxf;	/* Number of non-framework sites.     */
/*
 * Trig array set-up.  The precise storage layout is to avoid cache conflicts.
 */
   cshkl = allocate_arrays(nsarray, hmax, kmax, lmax,
			   &chx, &cky, &clz, &shx, &sky, &slz, &chg,
			   &cos1x, &cos1y, &cos1z, &sin1x, &sin1y, &sin1z, 
			   &qcoskr, &qsinkr);
   memcp(chg, chgx+ns0, nsarr0*lsizeof(real));
/*
 * First call only - evaluate self energy term and store for subsequent calls
 * Self energy includes terms for non-framework sites only.
 */
   if(init)
   {
      double	sqsq = 0, sq = 0, sqxf, intra, r;
      int	js, frame_flag;

      self_energy = sheet_energy = 0;
      ssite = 0;
      spec = species; 
      while( spec < species+system->nspecies && ! spec->framework)
      {
	 intra = 0.0;
	 for(is = 0; is < spec->nsites; is++)
	    for(js = is+1; js < spec->nsites; js++)
	    {
	       r = DISTANCE(spec->p_f_sites[is], spec->p_f_sites[js]);
	       intra += chgx[ssite+is] * chgx[ssite+js]
		       *err_fn(control.alpha * r) / r;
	    }
	 self_energy += spec->nmols * intra;
	 ssite += spec->nsites*spec->nmols;
	 spec++;
      }
      nsitesxf = ssite;
      frame_flag = (spec != species+system->nspecies);

      for(is = 0; is < nsitesxf; is++)
      {
	 sq += chgx[is];
	 sqsq += SQR(chgx[is]);
      }
      self_energy += control.alpha / sqrt(PI) * sqsq;
      /*
       * Sqxf is total non-framework charge.  Calculate grand total in sq.
       */
      sqxf = sq;
      for(; is < nsites; is++)
	 sq += chgx[is];
      /*
       *  Charged-system/uniform sheet correction terms (really in direct
       *  space term but included here for convenience).
       *  1) For charged framework only.
       */
      if( frame_flag )
      {
	 sheet_energy = PI*SQR(sq-sqxf) / (2.0*SQR(control.alpha));
	 message(NULLI, NULLP, INFO, FRACHG, 
		 (sq-sqxf)*CONV_Q, sheet_energy/vol*CONV_E);
      }
      /* 
       *  2) Case of entire system non-neutral.
       */
      if( fabs(sq)*CONV_Q > 1.0e-5)
      {
	 sheet_energy -= intra = PI*SQR(sq) / (2.0*SQR(control.alpha));
	 message(NULLI, NULLP, WARNING, SYSCHG, sq*CONV_Q, intra/vol*CONV_E);
      }

      note("Ewald self-energy = %f Kj/mol",self_energy*CONV_E);
   }

   *pe -= self_energy;			/* Subtract self energy term	      */
   *pe += sheet_energy/vol;		/* Uniform charge correction	      */
   zero_real(stress_ew, 9);
   for(i=0; i<3; i++)
      stress_ew[i][i] += sheet_energy/vol;

   invert(system->h, hinvp);		/* Inverse of h is matrix of r.l.v.'s */
   mat_sca_mul(2*PI, hinvp, hinvp);
   /*
    * Build array hkl[] of k vectors within cutoff
    */
   hkl = aalloc(4*(hmax+1)*(kmax+1)*(lmax+1), struct s_hkl);
   for(h = 0; h <= hmax; h++)
      for(k = (h==0 ? 0 : -kmax); k <= kmax; k++)
      {
	 kx = h*astar[0] + k*bstar[0];
	 ky = h*astar[1] + k*bstar[1];
	 kzt = h*astar[2] + k*bstar[2];
	 ksq = SQR(kx) + SQR(ky);
	 for(l = (h==0 && k==0 ? 1 : -lmax); l <= lmax; l++)
	 {
	    kz = kzt + l*cstar[2];
	    if( SQR(kz)+ksq < kcsq )
	    {
	       hkl[nhkl].h = h; hkl[nhkl].k = k; hkl[nhkl].l = l;
	       hkl[nhkl].kx = kx;
	       hkl[nhkl].ky = ky;
	       hkl[nhkl].kz = kz;
	       nhkl++;
	    }
	 }
      }
   if( init )
      note("%d K-vectors included in reciprocal-space sum",nhkl);
   init = false;
/*
 * Calculate cos and sin of astar*x, bstar*y & cstar*z for each charged site
 * Use addition formulae to get sin(h*astar*x)=sin(Kx*x) etc for each site
 */
   trig_recur(chx,shx,sin1x,cos1x,
	      site[0]+ns0,site[1]+ns0,site[2]+ns0,astar,hmax,0,nsarr0);
   trig_recur(cky,sky,sin1y,cos1y,
	      site[0]+ns0,site[1]+ns0,site[2]+ns0,bstar,kmax,0,nsarr0);
   trig_recur(clz,slz,sin1z,cos1z,
	      site[0]+ns0,site[1]+ns0,site[2]+ns0,cstar,lmax,0,nsarr0);
/*
 * Start of main loops over K vector indices h, k, l between -*max, *max etc.
 * To avoid calculating K and -K, only half of the K-space box is covered. 
 * Points on the axes are included once and only once. (0,0,0) is omitted.
 */
   for(phkl = hkl; phkl < hkl+nhkl; phkl++)
   {
/*
 * Calculate actual K vector and its squared magnitude.
 */
      h  = phkl->h;	    k     = phkl->k;  l     = phkl->l;
      kv0 = kv[0] = phkl->kx; 
      kv1 = kv[1] = phkl->ky; 
      kv2 = kv[2] = phkl->kz;

      ksq = SUMSQ(kv);

/*
 * Calculate pre-factors A(K) etc
 */
      coeff  = 2.0 / (EPS0 * vol) * exp(ksq * r_4_alpha) / ksq;
      coeff2 = 2.0 * (1.0 - ksq * r_4_alpha) / ksq;
      
/*
 * Set pointers to array of cos (h*astar*x) (for efficiency & vectorisation)
 */
      coshx = chx[h];  cosky = cky[abs(k)]; coslz = clz[abs(l)];
      sinhx = shx[h];  sinky = sky[abs(k)]; sinlz = slz[abs(l)];

/*
 * Calculate q(i)*cos/sin(K.R(i)) by addition formulae. Note handling of
 * negative k and l by using sin(-x) = -sin(x), cos(-x) = cos(x). For
 * efficiency & vectorisation there is a loop for each case.
 */
      qsincos(coshx,sinhx,cosky,sinky,coslz,sinlz,chg, 
	      qcoskr,qsinkr,k,l,nsarr0);
      ns1f = MIN(ns1, nsitesxf); ns0f = MAX(ns0, nsitesxf);
      sqexpkr[0] = sum(ns1f-ns0, qcoskr, 1);
      sqexpkr[1] = sum(ns1f-ns0, qsinkr, 1);
      sqexpkr[2] = sum(ns1-ns0f, qcoskr+(ns0f-ns0), 1);
      sqexpkr[3] = sum(ns1-ns0f, qsinkr+(ns0f-ns0), 1);
#ifdef SPMD
      par_rsum(sqexpkr, 4);
#endif
      sqcoskrn = sqexpkr[0]; 
      sqsinkrn = sqexpkr[1];
      sqcoskrf = sqexpkr[2];
      sqsinkrf = sqexpkr[3];
      sqcoskr = sqcoskrn + sqcoskrf;
      sqsinkr = sqsinkrn + sqsinkrf;
      
/*
 * Evaluate potential energy contribution for this K and add to total.
 * Exclude frame-frame interaction terms.
 */
      pe_k = 0.5 * coeff * (sqcoskrn*(sqcoskrn+sqcoskrf+sqcoskrf) +
			    sqsinkrn*(sqsinkrn+sqsinkrf+sqsinkrf));
      *pe += pe_k;

      sqsinkr *= coeff; sqcoskr *= coeff;
      sqsinkrn *= coeff; sqcoskrn *= coeff;
/*
 * Calculate long-range coulombic contribution to stress tensor
 */
NOVECTOR
      for(i = 0; i < 3; i++)
      {
	 stress_ew[i][i] += pe_k;
NOVECTOR
	 for(j = i; j < 3; j++)
	    stress_ew[i][j] -= pe_k * coeff2 * kv[i] * kv[j];
      }
/*
 * Evaluation of site forces.   Non-framework sites interact with all others
 */
VECTORIZE
      for(is = 0; is < ns1f-ns0; is++)
      {
	 force_comp = qsinkr[is]*sqcoskr - qcoskr[is]*sqsinkr;
	 sfx =  site_fx[is] + kv0 * force_comp;
	 sfy =  site_fy[is] + kv1 * force_comp;
	 site_fz[is] +=       kv2 * force_comp;
	 site_fx[is] = sfx;
	 site_fy[is] = sfy;
      }
/*
 *  Framework sites -- only interact with non-framework sites
 */
VECTORIZE
      for(is = ns0f-ns0; is < ns1-ns0; is++)
      {
	 force_comp = qsinkr[is]*sqcoskrn - qcoskr[is]*sqsinkrn;
	 sfx =  site_fx[is] + kv0 * force_comp;
	 sfy =  site_fy[is] + kv1 * force_comp;
	 site_fz[is] +=       kv2 * force_comp;
	 site_fx[is] = sfx;
	 site_fy[is] = sfy;
      }
/*
 * End of loop over K vectors.
 */
   }
   *pe /= nthreads;
   for(i=0; i<3; i++)
      for(j=0; j<3; j++)
	 stress[i][j] += stress_ew[i][j] / nthreads;

   afree((gptr*)chx); afree((gptr*)cky); afree((gptr*)clz); 
   afree((gptr*)shx); afree((gptr*)sky); afree((gptr*)slz);
   xfree(cshkl);
   xfree(hkl);
}
$EOD
$!
$CREATE getopt.c
$DECK
/*
	I got this off net.sources from Henry Spencer.
	It is a public domain getopt(3) like in System V.
	I have made the following modifications:

	index(s,c) was added because too many people could
	not compile getopt without it.

	A test main program was added, ifdeffed by GETOPT.
	This main program is a public domain implementation
	of the getopt(1) program like in System V.  The getopt
	program can be used to standardize shell option handling.
		e.g.  cc -DGETOPT getopt.c -o getopt
*/

#ifndef HAVE_GETOPT

#include "defs.h"

#include 

#ifndef lint
static	char	sccsfid[] = "@(#) getopt.c 5.0 (UTZoo) 1985";
#endif

#define	ARGCH    (int)':'
#define BADCH	 (int)'?'
#define EMSG	 ""
#define	ENDARGS  "--"

/* this is included because index is not on some UNIX systems */
static
char *
index (s, c)
register	char	*s;
register	int 	c;
	{
	while (*s)
		if (c == *s) return (s);
		else s++;
	return (NULL);
	}

/*
 * get option letter from argument vector
 */
int	opterr = 1,		/* useless, never set or used */
	optind = 1,		/* index into parent argv vector */
	optopt;			/* character checked for validity */
char	*optarg;		/* argument associated with option */

#define tell(s)	fputs(*nargv,stderr);fputs(s,stderr); \
		fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);


getopt(nargc,nargv,ostr)
int	nargc;
char	**nargv,
	*ostr;
{
	static char	*place = EMSG;	/* option letter processing */
	register char	*oli;		/* option letter list index */
	char	*index();

	if(!*place) {			/* update scanning pointer */
		if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF);
		if (*place == '-') {	/* found "--" */
			++optind;
			return(EOF);
		}
	}				/* option letter okay? */
	if ((optopt = (int)*place++) == ARGCH || !(oli = index(ostr,optopt))) {
		if(!*place) ++optind;
		tell(": illegal option -- ");
	}
	if (*++oli != ARGCH) {		/* don't need argument */
		optarg = NULL;
		if (!*place) ++optind;
	}
	else {				/* need an argument */
		if (*place) optarg = place;	/* no white space */
		else if (nargc <= ++optind) {	/* no arg */
			place = EMSG;
			tell(": option requires an argument -- ");
		}
	 	else optarg = nargv[optind];	/* white space */
		place = EMSG;
		++optind;
	}
	return(optopt);			/* dump back option letter */
}

#endif
$EOD
$!
$CREATE READ.ME
$DECK
         
		#     # ####### #       ######  #     #
		##   ## #     # #       #     #  #   # 
		# # # # #     # #       #     #   # #  
		#  #  # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # #     # #       #     #    #   
		#     # ####### ####### ######     #   

                Copyright Keith Refson January 1990
		All rights reserved


Moldy is a general-purpose molecular dynamics simulation program which
I wrote initially for my own research into aqueous solutions at
mineral surfaces.  However it is sufficiently flexible that it ought
to be useful for a wide range of simulation calculations of atomic,
ionic and molecular systems.

Moldy is available by anonymous file transfer from Oxford.  Connect to
"earth.ox.ac.uk" using "ftp", with an account name of "anonymous" and
your email address as password.  The relevant files are all in the
"/pub" directory and are

* moldy-2.yy.tar.gz   - The Unix distribution (also for MSDOS/Windoze)
* moldy-2.yy.com      - The VMS distribution
* moldy-2.yy.zip      - The binary distribution for Windows 95/NT
* moldy-manual.ps.Z   - The Manual in PostScript form.  Note that
                        the distribution files already contain 
                        the LaTeX source.

Please note that moldy is copyrighted and distributed under the GNU
public license which is designed to encourage its distribution and
modification.  This is to ensure that the source code of moldy and any
improvements made to it by anybody remains available to anyone who
wishes to use it.  If you change or improve Moldy, please tell me 
and if practical and appropriate I will incorporate your modifications
into a future release.  I hope that as time goes on Moldy will become
yet more comprehensive as a result of your input.

I am also keeping a list of email addresses of anyone who uses the
program for notification of updates, bugs and so forth.  Please notify
me if you would like to be added to this list, preferably by email to
Keith.Refson@earth.ox.ac.uk.


Contents of the distribution:

accel.c
algorith.c
alloc.c
ansi.c
auxil.c
beeman.c		These files are the source code for Moldy.
convert.c
dump.c
ewald.c
force.c
input.c
eigens.c
kernel.c
main.c
matrix.c
output.c
quaterns.c
rdf.c
restart.c
startup.c
values.c
xdr.c
parallel.c
   
defs.h
messages.h		'Include' files for above files
structs.h
xdr.h

stddef.h
time.h			replacement ANSI C include files for non-ANSI systems.
stdlib.h
string.h


dumpanal.c
dumpconv.c
dumpext.c		Source code for 'utility' programs.
mdshak.c
mextract.c

getopt.c		Support routine for utility progs.

Makefile		Make file for Moldy.

compile.com		Master compile file for VMS.  Calls all the others.
compile_moldy.com	Compile file for "moldy" itself.
compile_utils.com	Link file for "moldy" itself.       
link_moldy.com		Compile file for utilities.
link_utils.com		Link file for utilities.
defcomm.com		Defines comands - execute from your LOGIN.COM

moldy.tex		LaTeX source for manual
moldy.bbl
fig_*.ps		Encapsulated Postscript versions of figures.
fig_*.tex		LaTeX annotation for figures.
fig_*-eepic.tex		Alternative version of figures in eepic form.


tips2.in
tip4p.in
methane.in
mcy.in
mgclh2o.in		Example system specification files
control.clay
argon.in
quartz-vbst.in

control.mgclh2o
control.tip4p		Example control files
control.water
control.clay
control.argon
control.tips2
control.quartz
control.mgclh2o

water-example.out
argon-example.out	Output from example runs in manual
tips2-example.out
tip4p-example.out
mgclh2o-example.out
quartz-example.out
clay-example.out 

UNPACKING
---------
A. Unix distribution

This distribution of Moldy takes the form of a compressed tar archive.
The archive is unpacked with 
% gunzip moldy.tar (.gz)    or    % uncompress moldy.tar (.Z)
% tar xvf moldy.tar

B. VMS 

The VMS version of Moldy comes as a DCL archive, moldy.com. To unpack:
$ @moldy
This creates all the files needed in the current directory.

Alternatively, versions of "uncompress" and "tar" are available for
VMS, though they are not standard.  If you have them then you can
unpack the "moldy.tar.Z" archive (suitably renamed) in the same way.

C. Windows 95
The shareware program "winzip" available from good archive sites may
be used to unpack either the ".tar.gz" source distribution, or the
".zip" binary+source distribution.

COMPILING
---------

Here are some brief notes.  There are more detailed instructions in the 
manual.

A. UNIX

Stage 1.
You ought to be able to type "make moldy utilities" on just about any
flavour of unix and build a working version.  However if you want to
get the best performance you will have to work a bit harder with
compiler options.  You may also find that the compile fails for
obscure reasons.  Don't worry, some compile options will probably sort
that out too.

Stage 2.  
Edit "Makefile", choose a suitable set of compiler options for your
machine and uncomment them. Moldy has been test compiled on most
modern workstations as well as vector super and minisuper-computers,
so you only have to select the preset options.

Stage 3.  
This section describes how to "hand-tune" compiler options.
If you get this far you have probably got a different machine or
compiler system from any of the tested ones.  Moldy recognises a
number of C preprocessor macros which adjust its expectations of the
compiler and operating system.  These are best defined using the
"-DMACRO-NAME" option of most unix C compilers.

MACRO		Purpose
-----           -------
	The following two macros are used to select the ANSI C
	"stdarg" mechanism. Default is older "varargs" mechanism. 

__STDC__	Automatically defined by ANSI compilers in strict mode.
ANSI		Alternative for ANSI compiler in non-strict mode which
		does not define __STDC__.

ANSI_LIBS	Set this if your libraries and header files conform
		to those expected in the ANSI 89 Standard.  Otherwise
		extra replacement routines and header files to
		remedy the deficiencies of older systems are included.

		Set this if at all possible.  Only in an ANSI
		environment can you be sure that all needed headers and
		library routines will be present.  The alternative is
		a kludge which works most of the time on most machines.

		This is automatically set in "defs.h" for some
		machines/compilers vhich are known to have ANSI libraries.

USE_XDR		Turn on support for the portable binary dump and
		restart files using the XDR mechanism.  This is the
		only area where Moldy departs substantially from the
		ANSI C standard so it is optional.  Nevertheless it
		is so massively useful that it is on by default.

		As this is not part of the standard it may not compile
		correctly on some systems if the compiler is in
		"strict ANSI" mode.  Use the default  or the "relaxed"
		or "extended" ansi mode often provided. 

		You may well need to add a library to the link using
		the LDFLAGS variable in the Makefile.  For
		example, solaris 2 on Suns needs the "-lnsl"
		option and SGI's need the "-lsun" option.

		You may also need to define some other macro to get
		the headers correctly included viz:

_ALL_SOURCE 	(IBM RS6000)
_HPUX_SOURCE	(HPUX)
		These are needed to enable stuff needed for the XDR
		routines and headers on IBM and HP machines.  
		N.B. These are actually set automatically in the Moldy
		header "defs.h" However other systems may require something
		similar .

SPMD		Compile for SPMD (Single Program Multiple Data) parallel
		execution.  You must also specify one of the macros
		BSP, MPI or TCGMSG to select a message-passing library
		and have the appropriate include paths and libraries
		defined in the Makefile.

BSP/MPI/TCGMSG/SHMEM   (parallel.c only)
		These select the parallel interface library.  Portable
		implementations of the first three are available for
		most parallel architectures, and MPI should be available
		on most true parallel machines.  

MPPMANY		(Ewald.c only) Selects parallelization of some of the
		trig initialization code for the reciprocal-space sum.
		This is a loser on all but the highest-bandwidth
		parallel machines.  It increases run time on the IBM SP2.
		However it DOES give a speedup on the T3D when used in
		conjunction with the SHMEM or BSP library interface.

B. VMS (VAX/VMS and OpenVMS/AXP)

Just execute the "compile.com" DCL command file which will build Moldy
and the utilities.  All the required macros are set in "defs.h".  It
also executes the command file "defcomm.com" which defines the command
symbols to execute the programs.  It is a good idea to execute this
file from your LOGIN.COM to make them available every time you log in.

N.B. Depending on how your VMS system is set up you may need to take
additional steps to link moldy with the C runtime library.  Consult
your local documentation or systems staff.  If the C library isn't linked
by default the command

   $ assign sys$library:vaxcrtl lnk$library
   
before the executing the compile command file

   $ @compile

may well do the trick.

C. Windows 95/NT

i) Source distribution.
Moldy should compile using any good ANSI C compiler, but one of the
ports of the free GNU gcc compiler to Win95 is recommended.  The
simplest is the "Mingw32" port available from
"http://agnes.dida.physik.uni-essen.de/~janjaap/mingw32/" which
contains executables of the compiler, libraries and GNU Make.  Once
this is installed the compilation procedure is the same as that for
Unix.  There is a set of compiler options in the standard Makefile
which produce a reasonably fast and portable executable.  (N.B.  These
compilers etc can only be run from the MS-DOS command line window)

Moldy has also been compiled using WATCOM C and Borland Turbo C.
There is a Turbo C makefile supplied called "Makefile.Mak" (although
this has not been tested with a recent version).  The compilation and
installation procedure will vary according to the compiler.

ii) Binary distribution
The file moldy-2.yy.zip contains ".exe" files for moldy and the
utilities compiled with Mingw32/gcc as above which will run on
a 486 or above.  Just unpack the distribution into a convenient
directory.  The ".exe" files may either be left in place or moved to 
a suitable "bin" directory.   The PATH should be setin autoexec.bat to
include whichever directory the ".exe" files are kept in.


	RUNNING
	-------

Try it out by typing

	moldy control.water

to do a 10 timestep simulation of water.  (Under Windows 95, Moldy can
only be run from a MS-DOS command-line window)


PRINTING THE MANUAL
-------------------

The LaTeX source, "moldy.tex" and bibliography file, "moldy.bbl"
are supplied. If you have LaTeX, "latex moldy" a couple of times
to get the cross-references correct and print the dvi file using
dvips or dvi2ps, or whatever dvi output you normally use.

There is a "moldy.dvi" target in the make file so just "make
moldy.dvi" ought to do the trick.

PARALLEL VERSION
----------------

A.  Distributed Memory
    ------------------

    To build this version you must have one of the three supported
    message-passing libraries installed on the target system.  These
    are the Oxford BSP library, MPI (the new standardised
    message-passing library interface) and TCGMSG (the Theoretical
    Chamistry message-passing system).  Then define the macro PARLIBC
    in the Makefile to contain the "include" path for the library
    header files, and the preprocessor symbols SPMD and one of MPI,
    BSP and TCGMSG.  The macro PARLIBL should be defined to link with
    the appropriate libraries.

    For the Cray T3D, there is also an interface to the SHMEM libraries
    which provides the very fastest interprocessor communication.  This
    may be used in conjunction with the MPPMANY option in Ewald.c.

    To find out how to set up a parallel run, consult the documentation
    for your parallel system, as this varies.  

    The parallel performance increses with system size as the amount
    of work in the force and ewald sum loops increases as a proportion
    of the total work and with respect to the communication overhead.
    A speedup of nearly 7 on an 8-processor IBM SP1 has been
    demonstrated for the run "control.big", and in general the larger
    the system the better the parallel performance.  

    The parallel interface is contained within a single file
    "parallel.c" and versions for other MP libraries should be
    relatively easy to add with a few hours of programming effort.

    Alternative Ewald:
    ------------------

    There is an alternative version of Ewald.c which uses W. Smith's
    RIL paralellization strategy in ewald-RIL.c.  This uses far less
    memory but at the cost of doing parallel communication in the
    inner loops.  This works reasonably on parallel machines
    with very short latencies such as the Cray T3D, but on lesser
    beasts it serializes the whole code!

B.  Shared memory.
    -------------
    The sources contain separate versions of ewald.c and force.c with
    the appropriate code and compiler directives for compilation on
    certain shared-memory parallel machines including Stardent Titan,
    Convex and Cray architectures.  To use, you must REPLACE ewald.c
    and force.c with the file ewald_parallel.c and force_parallel.c
    respecively.  You must also define the preprocessor symbol
    PARALLEL during the compilation (eg with the compiler option
    -DPARALLEL).

    The code reads the environment to decide how many processors
    to execute on.  The name of the env variable is usually the
    same one as the manufacturers use for the same purpose
    NCPUS for the CRAY and THREADS on everything else.  Use
    the SETENV command (for c-chell) or the bourne-shell equivalent
    to before starting a run.

    Note. The Stardent Titan version does not work as shipped because
    the system supplied version of "malloc" can not be safely called
    from a parallel program.  Contact the author for a "thread-safe"
    version which can.
$EOD
$!
$CREATE BENCHMARK
$DECK
	Using MOLDY as a Benchmark
	__________________________

Moldy has been tested on a variety of machines and provides a
reasonable example of a scientific floating point program which is not
a heavy user of I/O or memory. However it does access memory quite
rapidly in the inner loops, and in some at 1 memory operation per
floating point operation.  It is well suited to vector machines but
for several reasons does not usually approach the "peak" figure.

There are two major parts to the computation.  1) The "Ewald sum"
consists mainly of linear passes through arrays doing multiplies and
adds, with a few sum reductions.  This is expensive on scalar machines
but very fast on vector machines with good memory bandwidth.  One
function, qsincos(), was measured by the PERFTRACE utility on the Cray
XMP to be achieving 230 Mflop/s -- exactly the peak!  2) The "real
space force calculation" which has some scalar overhead and a
vectorisable part which makes heavy use of gather/scatter operations
and math library functions, especially sqrt() and exp().  These
should all make use of vector hardware but the combination reduces
the MFlop rate substantially.   

It is thus reasonably demanding of both compilers and hardware and
tests mainly CPU rate and memory bandwidth.

To use:  compile and build the program (see READ.ME) and execute with
the test case control file "control.water", directing the output to
some other file.

% moldy control.water > bench.out

This run executes 10 timesteps of the standard benchmark test case,
and prints timing information as the bottom of the file.  The output
should be numerically identical to the sample in "water-canon.out",
which was run on a SparcStation II.  (Floating point roundoff should
*not* make any significant difference.)

For a reasonable supercomputer this run is probably too short, so try
the run in "control.100" (which performs 100 steps) instead, and divide
the times by 10.

	Rules of the Benchmark
	______________________

The idea is to allow minor machine-specific optimizations, such as
adding compiler directives, replacing the BLAS-like calls in "aux.c"
with calls to assembler libraries, but not to change the substance
of the algorithm.  Minor rewriting of loops to enable efficient
vectorization is allowed.  The results must, of course, be correct.

	Parallel Version
	________________

This is still experimental and not so portable.  It is designed for
compilers with the ability to parallelise loops.  Replace "ewald.c"
and "force.c" with their "x_parallel.c" variants.  The loops over
the calls to functions "ewald_inner()" and "force_inner()" are the
ones which must parallelize, and be executed one iteration per
processor.  The program reads the environment variable "THREADS" to
determine the number of processors to use.

This version has been tested on the Stardent 1000, 2000 and 3000
(titan) series machines and the Convex C2 (though it has not been
benchmarked on the latter).  For the Stardent Titan the macro
PARALLEL must be defined for the compilation of main.c and alloc.c to
set up correctly for a parallel run.  Good luck.

	Keith Refson, August 1991

$EOD
$!
$CREATE COPYING
$DECK
		    GNU GENERAL PUBLIC LICENSE
		       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
                          675 Mass Ave, Cambridge, MA 02139, USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

		    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

			    NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

		     END OF TERMS AND CONDITIONS

	Appendix: How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    
    Copyright (C) 19yy  

    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.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) 19yy name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  , 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Library General
Public License instead of this License.
$EOD
$!
$CREATE RELNOTES
$DECK
Release 2.12f
-------------
* Bugfixes and portability enhancements to utilities, msd, mdavpos, mdtraj.

Release 2.12c
-------------

* Bugfix to mdshak, which had a BAD memory leak.  Should run much faster
  now and not run out of memory.

* A writer for CHARMM/X-Plor DCD trajectory files added to mdshak.  This
  allows easy import of trajectories into other visualization programs,
  notable the excellent VMD (see http://www.ks.uiuc.edu/Research/vmd/)
  Use "mdshak -v" or link the executable to and invoke as "mddcd" or
  "mdvmd" to use.

* A few bugfixes to mdavpos and msd.

* Minor  corrections to the manual.

Release 2.12
------------

* New utilities msd and mdavpos.  Many thanks to Dr Craig Fisher for
  doing the work and adding these worthwhile utilites. 

  "msd" computes mean-square displacements from dump files, eg for
        calculation of diffusion constants.  It can also output
        "joined-up" trajectories for molecules to eliminate the
        discontinuities caused by the periodic boundary conditions.

  "mdavpos" computes the average co-ordinates of a molecule throughout
            the run.
    
* Minor bug fixes:

  - mdshak had an initialization problem which meant it couldn't read
           certain restart files.
 
  - makefile updated to work with GNU make (Linux users should be happy!)


Release 2.11
------------

* The manual is finished!  There is a new chapter on the organization and
  internals plus other minor rewrites and clarifications.  All the
  LaTeX sources are supplied. You need a modern LaTeX2e installation
  to format and print it.  A preformatted postscript version is also
  available but not included in the distribution file.

* Tuning and optimization for various architectures.  
  - Tuned ewald sum to avoid cache conflicts. Gains of 10-20%
    on T3D, Sun, DEC, 400% on IBM RS6000 for systems with
    powers-of-2 numbers of sites.
  - Tuned compiler options. Gain of 80% on SGI R8000 and
    Sun SPARC under Solaris 2.
  - Tuned kernel.c to improve pipelining on DEC Alpha.

* Rewrote parts of link-cell short-ranged force in force.c
  - eliminated huge tables of PBC relocation vectors.
    got rid of NSH macro and arbitrary limit to cutoff and
    RDF limit.  These are unlimited now.  The code is more
    transparent and easier to understand too.   It's also
    a little bit faster.

* Fixed bug in restart file format in 2.10 which meant that
  thermostat parameters were not stored retrievably.  2.11 can read
  2.10 XDR restart files, though you have to reset the thermostat
  mass parameters and turn thermostatting back on by hand in the
  control file.  For non XDR files there's nothing that can be done.

* Fixed initialization problem which caused FP error on Cray T3D (non-ieee)

* Corrected error in implementation of integration of thermostat
  equations of motion. This seems to make no practical diff at all.

* Included workaround for bug in Cray T3D compiler version 4.0.5 which
  caused crash.

* Added test for divergent trajectories when running in parallel.

* New back-end to "mdshak" program writes "XYZ" format files that
  can be read by RasMol, XMol, etc.  If mdshak is renamed to
  "mdxyz" this is invoked automatically.

Release 2.10
------------
* Added Nose-Hoover and Hoover (Gaussian) thermostats. (V. Murachov)

* Fixed bug in calculation of long-range-correction to pressure.

* Fixed bug whereby large rc used with strict-cutoff overran tables
  and caused crash.  Simply tighten up test to abort gracefully.

* Fixed bug in reciprocal-space sum where v. small k_cutoff caused
  crash.

* Fixed bug in input parser whereby faulty control file with
  missing "=" would apparently succeed but in fact return junk.

* Many updates an improvements to manual.

* Manual now requires LaTeX2e to process the source.

* IMPORTANT: Updated fundamental constants to more accurate values
  from CODATA 1986.  This will produce changes of order 1e-5 in
  some results.  If you want to continue using the old values
  compile with -DOLDCONSTS.

* RDF calculation parallelised. Now uses pair distances from link-cell
  force calculation, and is therefore limited only by "rdf-limit".

* Fixed problem whereby rescaling species separately led to net
  velocities of whole system.

* Added SHMEM library interface for Cray T3D.

Release 2.9
-----------
* Ewald sum parameter and cutoffs now automatically chosen from
  Fincham's formulae if no value specified in control file.

* Various minor bugs fixed.

NOTE 2.8c  24/11/94: The init code for the TCGMSG version of par_begin
got the arguments wrong. Now fixed.  Also fixed problem with dump
sequence continuity.

NOTE 2.8b 14/9/94:  The definition of bspstart in the bsp lib CHANGED
to include argc and argv as params.  New version of parallel.c incorporates
modified arg list.

Release 2.8
-----------
* Port to distributed-memory MIMD parallel machines.

  - This port is based on a message-passing paradigm, and contains
    interfaces to three different message-passing systems - the
    Oxford BSP library, MPI (the new standardised message-passing
    library interface) and TCGMSG (the Theoretical Chamistry
    message-passing system).  The parallel interface is contained
    within a single file "parallel.c" and versions for other MP
    libraries should be relatively easy to add.

  - To use, define the macro PARLIBC in the Makefile
    to contain the "include" path, -DSPMD and one of -DMPI, -DBSP
    and -DTCGMSG.  The macro PARLIBL should be defined to link
    with the appropriate libraries.

* Performance optimizations for IBM RS6000.  The 4-way associative
  cache on the high-end machines in the IBM RS6000 line leads to
  a severe inefficiency when the number of sites is a power of 2.
  This version declares the arrays larger than the system size to 
  avoid the problem.

Release 2.7
-----------
* Added casts for size_mt in arralloc() calls - for K&R/ANSI compat.

* New version of array allocator which breaks up requests for DOS.

* Changed all timestep-related parameters to type "long". This means
  that 16-bit DOS compilers can do more than 32767 timesteps.

  NOTE: This changes the size of "native" format dump and restart
        files on a machine where sizeof(long) > sizeof(int), in
        particular DEC Alpha machines and possibly SGI R4000 based
	ones.  Version 2.7 will not be able to read these "native"
	format files written by earlier versions, though the recommended
	XDR format will work.  Versions 2.6 of "dumpconv -x" should be  
	used to convert dump files to XDR format and moldy itself to
	convert restart files.

* Utilities renamed for systems with short filenames.
  dumpextract  ==> dumpext
  dumpconvert  ==> dumpconv
  dumpanalyze  ==> dumpanal
  moldyextract ==> moldyext
  moldyanalyze ==> manalyze

* Strengthened error checking in "mdshak" which previously just gave
  ridiculous answers if you specified wrong arguments.  It now calls
  "dumpext" via a pipe if it needs to read dump files.

* Arguments of "dumpext" simplified.  You can now specify the dump
  files as EITHER the complete set in any order (eg by unix shell
  wildcard expansion OR as a proforma exactly as in the control file.
  "dumpext" just tries plugging in numbers with "sprintf" to locate
  files. This eliminates the need for the "-n" option.  (However if
  you start your run with an index > 500 it will not find them.)

Release 2.6
-----------
* Port to PCs running MS-DOS
 
* Significant internal restructuring.  
  This consists of eliminating global data and improving data locality
  throughout the code.  No external impact, but a gain in code quality
  which should make improvements and porting, esp to parallel machines
  much easier.

* Yet more portability improvements

* Bugs corrected

- Fixed serious bug whereby linear molecules weren't recognised.
  Caused failure on RS6000 and possibly others, but not Sun or DEC
  Alpha.


Release 2.5
-----------
This is to announce the release of version 2.5 of Moldy.  This is mainly 
a bugfix and portability release with no major changes in functionality.
It incorporates all the experience gained from the "beta-test" releases of
2.2 and 2.3.

Major points are:

* Greatly improved portability.  Tested on Sun, SGI, HP, DEC, Convex
  IBM and Cray systems and should compile without problems on many more.

* VMS support consolidated.  Now compiles on VAX/VMS and OpenVMS/AXP
  systems without problems.  Distribution comes in form of "moldy.com"
  script for ftping and contains "compile.com" to compile everything.

* Better and more comprehensive install notes in READ.ME file.

* Bugs corrected/mods since 2.2.

- "Divide-by-zero" error on velocity rescale of atomic systems fixed.

- Relaxed "dump"s checks to allow dump run to continue after backup 
  restart without changing dump file names.

- Minor optimization in real-space force calculation.  A few percent faster.

- Added distant-correction for "generic" potential type GENPOT

- XDR mode for dump/restart files is now active by default.

- Various minor portability bugs in utility programs corrected.


$EOD
$!
$CREATE moldy.tex
$DECK
%MOLecular DYnamics simulation code, Moldy.
%Copyright (C) 1988, 1992, 1993 Keith Refson
% 
%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, or (at your option) any
%later version.
% 
%This program is distributed in the hope that it will be useful,
%but WITHOUT ANY WARRANTY; without even the implied warranty of
%MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%GNU General Public License for more details.
% 
%You should have received a copy of the GNU General Public License
%along with this program; if not, write to the Free Software
%Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
% 
%In other words, you are welcome to use, share and improve this program.
%You are forbidden to forbid anyone else to use, share and improve
%what you give them.   Help stamp out software-hoarding!  */
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%% This document requires LaTeX2e to format it and it will %%%%%%%%%
%%%%%% not run under obsolete (LaTeX 2.09) installations.      %%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\documentclass[a4paper,twoside]{report}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%% Use Times-Roman fonts. This works for PSNFSS Latex2e%%%%%%%%
%%%%%%%%%%% mode.  Just comment it out if it doesn't work       %%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%\usepackage{pslatex}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%% Not essential, except that it MUST be used with pslatex %%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%\usepackage[T1]{fontenc}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%% Use the "geometry" package to set page layout.      %%%%%%%%
%%%%%%%%%%% Hard coded params are given as comments below if nec. %%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%\usepackage{geometry}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%% Dimensions and settings                                %%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%\geometry{body={160mm,230mm},top=40mm,nohead}
%% This gives the following settings on A4 paper.  For printing
%% on US letter size, re-enable the \geometry and its \usepackage
\textwidth  455.24408pt
\textheight 654.41338pt
\oddsidemargin  18.86191pt
\evensidemargin -21.13809pt
\topmargin  41.54103pt
\headheight 0.0pt
\headsep    0.0pt
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%% Needed for tables 3.1 and 3.2.  If you get rid of it %%%%%%%
%%%%%%%%%%% edit the tabular header to excise the >{} descriptor.%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\usepackage{array}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%% Prettifies some script letters.  Omit if necessary. %%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\usepackage[mathcal]{eucal}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%% Postscript figure inclusion.  Epsfig is part of the %%%%%%%%
%%%%%%%%%%% "graphics" bundle.  Install it if necessary.        %%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\usepackage{graphicx}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%% Eepic graphics work well if you don't have PostScript
%%%%%%%%%%% but you DO have a graphics driver which supports the
%%%%%%%%%%% "tpic" \special's, (iptex,dviis).  Even if you don't
%%%%%%%%%%% you can use the "eepicemu" package at a pinch but the
%%%%%%%%%%% quality is poor.  
%%%%%%%%%%%   To use it, rename the eepic figures "fig_*-eepic.ftx"
%%%%%%%%%%% to "fig_*.ftx" and then run latex.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%\usepackage{epic}             % For eepic or eepicemu
%%\usepackage{eepic}            % Tpic specials *or*
%%%%\usepackage{eepicemu}       % No special support.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%% Bold math symbols.  The 'bm' package, new with teTeX 0.9
%%%%%%%%%%% does this  *right*. Otherwise use \mathbf, but you won't
%%%%%%%%%%% get bold greek or symbols. 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%begin{latexonly}
\typeout{^^JAttention - if the ``bm'' Package isn't installed you will
  get an error here.^^JJust hit  and continue. (Bold math won't
  work quite as well).}
\usepackage{bm}
%end{latexonly}
\providecommand{\bm}[1]{\mathbf{#1}}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%% This defines the \sideset{} macro as a replacement for
%%%%%%%%%%% the AMS version plus the superfig/partfigure environments.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\usepackage{amsmath}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%% Specific customized macros for abstracting physical   %%%%%%
%%%%%%%%%%%  font changes.                                        %%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\usepackage{moldy}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%% Hyperref is useful for including hypertext links.   %%%%%%%%
%%%%%%%%%%% Useful options to the package are                   %%%%%%%%
%%%%%%%%%%% [hypertex] - for xdvi.  (Need xhdvik)               %%%%%%%%
%%%%%%%%%%% [pdfmark]  - tp process ps file with distiller to pdf %%%%%%
%%%%%%%%%%% [pdftex] for direct conversion to pdf using pdftex  %%%%%%%%
%%%%%%%%%%% No option defaults to ``hypertex'' using LaTeX and  %%%%%%%%
%%%%%%%%%%% ``pdftex'' using pdflatex.                          %%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\usepackage{hyperref}
%
% Comment this out for LaTeX2html translation
%
\providecommand{\htmladdnormallink}[2]{#1}

\renewcommand{\topfraction}{0.9}
\renewcommand{\textfraction}{0.1}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%% Useful shorthand macros                               %%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newcommand{\moldy}{\emph{Moldy}}
\newcommand{\etc}{\emph{etc.}}
\newcommand{\eg}{\emph{e.g.}}
\newcommand{\ie}{\emph{i.e.}}
\newcommand{\erf}{\mbox{erf}}
\newcommand{\erfc}{\mbox{erfc}}
\newcommand{\insqrt}{\sqrt{\mathstrut}}
\newcommand{\saferagged}{\let\temp=\\\protect\raggedright\let\\=\temp}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%% Hyphenation                                         %%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\hyphenation{algo-rithm time-slice time-slices}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%% RCS Emulation stuff                                 %%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newcommand{\RCSrevision}{$ $Revision: 2.16 $ $}
\newcommand{\moldyversion}{2.13}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%% Document fromt matter/titlepage stuff               %%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\title{Moldy User's Manual}
\author{Keith Refson\\Department of Earth Sciences\\Parks Road
\\Oxford OX1 3PR\\keith@earth.ox.ac.uk\\}
\version{\RCSrevision for release \moldyversion}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{document}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\pagenumbering{roman}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\maketitle
\thispagestyle{empty}

Copyright \copyright\ 1988, 1993--1996 Keith Refson.

   Permission is granted to make and distribute verbatim copies of this
manual provided the copyright notice and this permission notice are
preserved on all copies.

   Permission is granted to copy and distribute modified versions of
this manual under the conditions for verbatim copying, provided also
that the entire resulting derived work is distributed under the terms
of a permission notice identical to this one.

   Permission is granted to copy and distribute translations of this
manual into another language, under the above conditions for modified
versions.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section*{Acknowledgements} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The initial implementation of the Nos\'{e}-Hoover and Gaussian
thermostats was by Vladimir Murashov at Dalhousie University.
Rafael R. Pappalardo contributed the modifications to ``mdshak'' to
write XYZ format molecular graphics files.

Thanks are due to Craig Fisher and Jos\'{e} Manuel Martinez Fernandez
for diligent proofreading of several versions of the manual and many
helpful suggestions on how to improve it.  Any mistakes that remain are
my own.

\cleardoublepage
\setcounter{page}{1}
\tableofcontents
\listoftables
\listoffigures

\cleardoublepage
\pagenumbering{arabic}
\setcounter{page}{1}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Introduction}   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\moldy\ is a computer program for performing molecular dynamics
simulations of condensed matter.  It can handle any assembly of rigid
polyatomic molecules, atoms or ions and any mixture thereof. It uses
the `link cell' method to calculate short-range forces and the Ewald
sum technique to handle long-range electrostatic forces.  Simulations
may be performed either in the usual $NVE$ ensemble or in $NVT$,
$N\sigma H$ or $N\sigma T$ ensembles using Nos\'e-Hoover thermostat and
Parrinello and Rahman constant-stress methods. As the MD cell need not
be cubic, the program is equally suitable for simulations of solids
and liquids.

Most existing MD programs are limited in their capabilities, for
example to one kind of potential function, or molecular symmetry, or
to some restricted number of molecules.  \moldy\  is (as far as
possible) free from such arbitrary constraints.  The system is
specified at the beginning of each run and its size is only limited by
the amount of memory available to the program: if a system is too
large to handle, the solution is to buy some more memory.  The system
may contain a mixture of an arbitrary number of molecular species,
each with an arbitrary number of atoms and an arbitrary number of
molecules of each. Molecules or ions may be monatomic or polyatomic,
linear or three dimensional in any combination.  The potential
functions may be of the Lennard-Jones, Buckingham (including
Born-Mayer) or MCY types, and other potential types may be easily
added.  Such flexibility is possible because \moldy\  is written in the
`C' language which permits dynamic memory allocation.

\moldy\  is written to be highly portable and has been tested on a wide
range of computers and operating systems, including VAX/VMS,
MS-DOS and Unix\footnote{Goodness knows who will own the Unix
registered trademark by the time you read this.}  (both BSD and system V
varieties).  It should be straightforward to move it to any other
machine with a good `C' compiler.

To be of real use a simulation  program must run efficiently on modern
high-speed computers, which  are  increasingly of vector   or parallel
architectures.  \moldy\  is written so  as to be highly vectorizable and
has been  tested on a   range of  vector machines  from  manufacturers
including Cray, Convex, Stardent and  Alliant.  On the cray XMP-48 its
performance can exceed 100 MFlop/sec (on a suitably large  system).
\moldy\ is also able to run on a parallel computer of either shared or
distributed memory architectures, and has been tested on
multiprocessors from Stardent, Convex, Cray Research, SGI and IBM SP1
and Cray Research T3D massively parallel machines.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Terms and Conditions}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Permission to compile and run \moldy\ is granted to any individual or
organization without restriction.  This program is distributed in the
hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE\@.  See the GNU General Public License for more details.

\moldy\ is free software; you may 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, or (at your option) any
later version.  The terms of the GNU General Public License are
described in the file \Fname{COPYING} which is included with the
source distribution, or from the Free Software Foundation, 675 Mass
Ave, Cambridge, MA 02139, USA.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Algorithms and Equations}  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

This chapter  describes the implementation  of the  molecular dynamics
technique as used in \moldy.  It is not intended as an introduction to
MD  simulations, and  does assume some    familiarity  with the  basic
concepts of microscopic models  and simulations thereof.  The book  by
Allen and Tildesley\cite{allen:87}  is a  very good    introductory text,
covering   both the  theory  and  the  practicalities and  is   highly
recommended.  It   also  contains   comprehensive  references  to  the
scientific literature of microscopic computer simulation.

\moldy\  is designed to simulate a common class of models of atomic or
molecular systems. The assumptions are: that the system is an assembly
of  \emph{rigid   molecules},  atoms   or  ions;  that  the  forces  of
interaction are derived   from \emph{continuous  potential  functions}
acting between (usually atomic) \emph{sites} on each molecule; that the
dynamics are governed by the \emph{classical} Newton-Euler equations of
motion.  A major aim of \moldy\ has been to allow the most  general of
models within that class and to impose as few restrictions as possible.
In particular arbitrary mixtures of different molecules are allowed
and several popular forms of potential functions are catered for.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{The Equations of Motion}  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
If we denote the force exerted by atom $\alpha$ of molecule $i$ on
atom $\beta$ of molecule $j$ by $\bm{f}_{i\alpha j\beta}$\footnote{A
comment on notation is appropriate here.  In this chapter, site
quantities are denoted by \emph{lowercase} letters, molecular
quantities by \emph{uppercase}, sites are indexed by \emph{greek}
letters and molecules by \emph{roman}. A missing index denotes a sum
over the corresponding sites or molecules so that, for example
$\bm{r}_{i\alpha j\beta}$ is a site-site vector and $\bm{F}_{ij}$ the
molecule-molecule force.}  then the total force acting on molecule $i$
is
\begin{equation}
\label{eqn:comf}
\bm{F}_i = \sum_j \sum_\beta \sum_\alpha \bm{f}_{i\alpha j\beta}
\end{equation}
and the torque is given by
\begin{equation}
\label{eqn:comt}
\bm{N}_i = \sum_\alpha (\bm{r}_{i \alpha} - \bm{R}_{i}) 
\times\bm{f}_{i\alpha} 
\end{equation}
where $\bm{R}_{i} = 1/M_i \sum_\alpha
m_{i\alpha} \bm{r}_{i\alpha}$ is the centre of mass of molecule $i$.

The motion is governed by the Newton-Euler equations
\begin{equation}
M_i\ddot{\bm{R}}_i = \bm{F}_i \label{eqn:newton}
\end{equation}
\begin{equation}
  \bm{I}_i \cdot \dot{\bm{\omega}}_i - \bm{\omega}_i \times \bm{I}_i
  \cdot \bm{\omega}_i = \bm{N}_i \label{eqn:euler}
\end{equation}
where  $\bm{\omega}_i$ is the angular velocity of the molecule, 
$ \bm{I}_i = \sum_{\alpha} m_{i\alpha} 
( p_{i\alpha}^2 \bm{1} - \bm{p}_{i\alpha}\bm{p}_{i\alpha} ) $ 
is the inertia tensor and 
$\bm{p}_{i\alpha} = \bm{r}_{i\alpha} - \bm{R}_i $
is the atomic site co-ordinate relative to the molecular centre of mass.

% Quaternions
\label{sec:quaternions}
The orientations of the molecules are represented by \emph{quaternions}
as  has now  become common  practice.   They are  preferred over Euler
angles for two reasons. Firstly they lead to equations of motion which
are  free  of singularities\cite{evans:77} which   means  that   no
special-case  code is required. This  leads to much improved numerical
stability      of  the  simulation\cite{evans:77b}.   Secondly,
molecular symmetry   operations   and  combinations of  rotations  are
elegantly expressed   in    terms  of     a     simple      quaternion
algebra\cite{evans:77b,pawley:85b}.

A quaternion  is an ordered number  quartet  which obeys  the  algebra
given  by   Pawley\cite{pawley:81}.  The multiplication   rule in that
reference may be restated as a matrix product treating each quaternion
as a  4-vector.  If  $\Quat{p} \equiv (p_0,p_1,p_2,p_3)$ and  $\Quat{q}
\equiv  (q_0,q_1,q_2,q_3)$  are quaternions then
\begin{equation}
\Quat{pq} = 
\left(
\begin{array}{rrrr}
p_0 & -p_1 & -p_2 & -p_3 \\
p_1 &  p_0 & -p_3 &  p_2 \\
p_2 &  p_3 &  p_0 & -p_1 \\
p_3 & -p_2 &  p_1 &  p_0 
\end{array}
\right) \left(
\begin{array}{r}
q_0 \\ q_1 \\ q_2 \\ q_3
\end{array}
\right)
\end{equation}
The quaternion $ \Quat{\tilde{q}} $ conjugate to $\Quat{q}$ is defined
as $\Quat{\tilde{q}} = (q_0,-q_1,-q_2,-q_3)$ so that
\begin{equation}
\Quat{q\tilde{q}} = (q_0^2+q_1^2+q_2^2+q_3^2,0,0,0).
\label{eqn:qnorm}
\end{equation}
The \emph{norm} is defined as $ |\Quat{q}| \equiv
\sqrt{q_0^2+q_1^2+q_2^2+q_3^2} $ and $\Quat{q}$ is called a
\emph{unit} quaternion if $ |\Quat{q}| = 1 $.  Any possible rotation
can be represented by a unit quaternion. Du Val shows\cite{duval:64}
that if $ \Quat{q} \! = \! (\cos \frac{\alpha}{2}, \bm{l} \sin
\frac{\alpha}{2}) $ (where we have combined the last three components
to form a 3-vector) and $\Quat{p} = (0,\bm{r})$ then the operation
\begin{equation}
\Quat{p'} \equiv (0,\bm{r'}) = \Quat{q p \tilde{q}}
\end{equation}
corresponds to a rotation of the vector $\bm{r}$ by an angle of
$\alpha$ about the axis $\bm{l}$.  The components may also be
expressed in terms of the Euler angles as\footnote{The definition of
  quaternions used here differs from that used in Evans'
  paper\cite[equation 21]{evans:77} in the sign of $q_2$ or $\xi$.
  This error has been compounded by subsequent
  authors\cite{sonnenschein:85,smith:82,laakonsen:85} who also managed
  to permute the components which means that the parameters do not
  form an ordered number quartet which obeys quaternion algebra. Like
  Allen and Tildesley\cite[page 88]{allen:87} we follow the definition
  of Goldstein\cite[pages 143 and 155]{goldstein:80}.}
\begin{eqnarray}
q_0 & = & \cos \frac{\phi+\psi}{2} \cos \frac{\theta}{2} \nonumber \\
q_1 & = & \sin \frac{\phi-\psi}{2} \sin \frac{\theta}{2} \nonumber \\
q_2 & = & \cos \frac{\phi-\psi}{2} \sin \frac{\theta}{2} \nonumber \\
q_3 & = & \sin \frac{\phi+\psi}{2} \cos \frac{\theta}{2}.
\end{eqnarray}

The relationship between the time derivative of a quaternion and the
principal frame angular velocity was given by Evans\cite[Equation
27]{evans:77} and rewritten using quaternion algebra by 
Refson\cite{refson:87a} as
\begin{equation}
2 \Quat{\dot{q}} = \Quat{q}(0,\bm{\omega}^p)
\label{eqn:qomega}
\end{equation}
The second derivative is given by
\begin{eqnarray}
\nonumber
2\Quat{\ddot{q}} & = & \Quat{q}(- 1/2 (\omega^p)^2,\dot{\bm{\omega}}^p) \\
 & = & \Quat{q}(-2 |\Quat{\dot{q}}|^2,\dot{\bm{\omega}}^p)
\label{eqn:qddot}
\end{eqnarray}
Equations~\ref{eqn:qddot} and~\ref{eqn:euler} allow the simulation to
be implemented using quaternions and their derivatives as the dynamic
variables for rotational motion, and this is the method employed in
\moldy.  This second order formulation was first used by Powles \emph{et
al}.\cite{powles:79} and Sonnenschein showed\cite{sonnenschein:85}
that it gives substantially better stability than if angular
velocities and accelerations are used as dynamic variables.
 
Using equations~\ref{eqn:qddot} to describe the dynamics means that
they are integrated as if all four components were independent.
Therefore the normalization condition $\Quat{q \tilde{q}} = \Quat{1}$
may not be exactly satisfied after performing an integration step.
\moldy\ adopts the usual practice of scaling all components of the
quaternion after each timestep to satisfy the normalization
condition\cite{evans:77b}.

It is less widely realized that the second order equations
(\ref{eqn:qddot}) introduce a \emph{second} unconstrained variable
into the procedure.  Differentiating equation~\ref{eqn:qnorm} gives a
constraint on the quaternion derivatives
\begin{equation}
q_0\dot{q_0} + q_1\dot{q_1} + q_2\dot{q_2} + q_3\dot{q_3} = 0
\label{eqn:qconst} 
\end{equation}
which is just the $q_0$ component of equation~\ref{eqn:qomega}.
Just as with the normalization condition, the integration algorithm
will not preserve this condition exactly unless explicit measures are
taken.  After each timestep the constraint may be re-established by
subtracting the discrepancy from the quaternion derivatives.  If
$\delta = q_0\dot{q_0} + q_1\dot{q_1} + q_2\dot{q_2} + q_3\dot{q_3}$
then the corrected quaternion derivatives are given by
\begin{equation}
\Quat{\dot{q}^\prime } =  \Quat{\dot{q}} - \delta \Quat{q}.
\label{eqn:qconcorr}
\end{equation}
Experiments conducted while developing \moldy\ show that enforcing
this constraint significantly decreases the fluctuations in the total
energy. 

Linear molecules are a slightly special case as the moment of inertia
about the molecular axis is zero.  Though there are unique methods to
represent this situation\cite[page 90]{allen:87} \moldy\ uses a minor
modification of the quaternion algorithm.  All that is necessary is a
little special-case code to avoid dividing by the zero component of
inertia in the solution of equation~\ref{eqn:euler} and to hold the
components of angular velocity and acceleration about the molecular
axis to zero.  This has the considerable advantage of uniform
treatment of all kinds of molecules which is convenient when dealing
with heterogeneous mixtures.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Integration Algorithms}   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\label{sec:beeman}

The dynamical equations~\ref{eqn:newton} and~\ref{eqn:euler} are
integrated using this author's modification\cite{refson:85} of the
Beeman algorithm\hspace{0pt}\cite{beeman:76}.  For atomic systems the
accuracy is of the same order as the commonly used Verlet
algorithm\cite{verlet:67}.

The accuracy of common integration algorithms was discussed by
Rodger\cite{rodger:89} who showed that the Beeman algorithm is the
most accurate of the supposedly ``Verlet-equivalent'' algorithms. The
Beeman algorithm is the only one accurate to $O(\delta t^4)$ in the
co-ordinates and $O(\delta t^3)$ in the velocities compared to the
velocity Verlet algorithm which is only $O(\delta t)$ in the
velocities. This is insufficient for an accurate determination of the
kinetic energy, pressure and other dynamic quantities. More seriously,
it fails badly in the case of polyatomic molecules and for
constant-pressure and constant-temperature methods where the
(generalized) velocities enter the dynamical equations themselves.

Velocity-dependent forces occur in equations~\ref{eqn:euler}
and~\ref{eqn:qddot}, in the Parrinello-Rahman constant-pressure
equations (section~\ref{sec:const-stress}) and in the Nos\'e-Hoover
heat bath algorithms (section~\ref{sec:const-temp}). These usually
present a problem to non ``predictor-corrector'' algorithms which are
based on the assumption that the forces depend only on the
co-ordinates.  Fincham has devised a scheme to allow integration of
the rotational equations using Verlet-like
algorithms\cite{fincham:81}, which is widely used despite the
potential problems caused by the low accuracy of the velocities being
propagated into the dynamics.

These cases are easily and accurately handled by the modification to
Beeman's equations proposed by the author\cite{refson:85}.  These may
be summarized using the symbol $x$ to represent any dynamic variable
(centre-of-mass co-ordinate, quaternion or MD cell edge),
$\dot{x}^{(p)}$ and $\dot{x}^{(c)}$ to represent ``predicted'' and
``corrected'' velocities respectively.

\begin{equation}
  \begin{array}[c]{llcl}
    \textup{i} \quad & x(t+\delta t) &=& x(t) + \delta t \, \dot{x}(t) + 
    \frac{\delta t^2}{6} 
    \left [ 4 \ddot{x}(t) - \ddot{x}(t-\delta t) \right ] \\
    \textup{ii}  & \dot{x}^{(p)}(t+\delta t) & = & \dot{x}(t) + 
    \frac{\delta t}{2} \left [ 3 \ddot{x}(t)- \ddot{x}(t-\delta t) \right ]\\
    \textup{iii}  & \ddot{x}(t+\delta t) & = & F(\{ x_i(t+\delta t),
    \dot{x}_i^{(p)}(t+\delta t)\}, i =  1\ldots n) \\
    \textup{iv} & \dot{x}^{(c)}(t+\delta t) & = & \dot{x}(t) + \frac{\delta t}{6} 
    \left [ 2 \ddot{x}(t+\delta t) + 5 \ddot{x}(t)- \ddot{x}(t-\delta t) \right ]\\
    \textup{v} & \multicolumn{3}{l}{
      \textnormal{Replace $ \dot{x}^{(p)}$ with $\dot{x}^{(c)}$ and
    goto \emph{iii}.  Iterate to convergence}} 
  \end{array}
  \begin{array}[c]{l}
  \begin{array}{l}
    \strut\\
    \strut
  \end{array}\\
  \left .
  \begin{array}{l}
    \strut\\
    \strut\\
    \strut
  \end{array}
  \right \}
  \end{array}
  \label{eqn:beeman}
\end{equation}

The predictor-corrector cycle of steps \emph{iii} to \emph{v} is
iterated until the predicted and corrected velocities have converged
to a relative precision of better than 1 part in $10^{-7}$, which in
practice takes 2 or 3 cycles.  This iteration is not as inefficient as
it might at first appear as it does \emph{not} include the expensive
part of the calculation --- the recalculation of the site forces.
Only the angular accelerations and quaternion second derivatives must
be evaluated at each iteration using equations~\ref{eqn:euler} 
and~\ref{eqn:qddot}, and this operation is relatively cheap.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Potentials and Short-Range Forces}%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

The forces determining the dynamics of the system are derived from the
potential function denoted by $\phi_{i\alpha j\beta}(\bm{r} _{i\alpha
  j\beta})$.  The indices $i$ and $j$ run over all molecules in the
system and $\alpha$ and $\beta$ over sites on the respective molecule.
The total potential energy of the system is
\begin{equation}
\label{eqn:toten}
U = \sum_i \sum_{j > i} \sum_\alpha \sum_\beta \phi_{i\alpha j\beta}(
\bm{r}_{i\alpha j\beta}).
\end{equation}
where $\bm{f}_{i\alpha j\beta} = - \bm{\nabla} \phi_{i\alpha  j\beta}(
\bm{r}_{i\alpha j\beta})$ is the force acting on site $\beta$ of molecule
$j$ from site $\alpha$ of molecule $i$.

The total force and torque acting on any particular molecule are
calculated using equations~\ref{eqn:comf} and~\ref{eqn:comt}.  Since
$\bm{f}_{i\alpha j\beta}$ and therefore $\bm{F}_{ij}$ are short-ranged
forces (\ie they decay faster than $r^{-3}$) one can define a
\emph{cutoff} radius, $r_c$.  Interactions between sites whose
separation $r_{ij}$ is greater than $r_c$ are assumed to be negligible
and need not be evaluated.  In the case of a polyatomic molecular
system it is usual to apply the cutoff according to the
\emph{intermolecular} separation $R_{ij}$.


\subsection{Pressure and Stress}
The internal stress of a system of interacting molecules is given by
Allen and Tildesley\cite[pp 46-49]{allen:87} in terms of the molecular
virial, but we may rewrite it more conveniently in terms of the atomic
site virial as

\begin{eqnarray}
\label{eqn:stress-sr}
V \bm{\pi}^{sr} & = & \left <  \sum_i M_i \bm{V}_i \bm{V}_i 
              + \sum_i \sum_{j>i} \bm{R}_{ij} \bm{F}_{ij} 
\right > \\ \nonumber
& = & \left < \sum_i M_i \bm{V}_i \bm{V}_i 
              + \sum_{i} \sum_{j>i} \sum_\alpha \sum_\beta 
                \bm{r}_{i\alpha j\beta}  \bm{f}_{i\alpha j\beta} 
              - \sum_i \sum_\alpha \bm{p}_{i\alpha} \bm{f}_{i\alpha}
\right >
\end{eqnarray}
The quantity $\bm{p}_{i\alpha} \equiv \bm{r}_{i\alpha} - \bm{R}_i$ is the
co-ordinate of each site relative to the molecular centre-of-mass.
The pressure is easily evaluated as one third of the trace of the
stress tensor. 

\subsection{Long Range Corrections}
The truncation of the interactions at the cut-off radius does
introduce some errors into the calculated potential energy and stress.
By neglecting density fluctuations on a scale longer than the cutoff
radius we may approximate the errors and calculate correction 
terms\cite[pp 64-65]{allen:87}.

\begin{eqnarray}
U_c & = & \frac{2\pi}{V} \sum_\alpha \sum_\beta N_\alpha N_\beta 
               \int_{r_c}^\infty r^2 \phi_{\alpha\beta}(r) \, \Calcd r \\
P_c V & = & \frac{2\pi}{3 V} \sum_\alpha \sum_\beta N_\alpha N_\beta 
               \int_{r_c}^\infty r^3 
               \frac{\Calcd\phi_{\alpha\beta}(r)}{\Calcd r}
                \, \Calcd r \nonumber \\ 
           & = & U_c + \frac{2\pi}{3 V} \sum_\alpha \sum_\beta
           N_\alpha N_\beta r_c^3 \phi_{\alpha\beta}(r_c) \\
\bm{\pi}_c & = & P_c \bm{\delta}
\end{eqnarray}
where the sums run over all the distinct kinds of sites,
$N_\alpha$ is the total number of sites of type $\alpha$ in the system
and $\bm{\delta}$ is the unit matrix.


\subsection{Potential Functions}
\label{sec:potentials}
% LJ, Buckingham (Born-Mayer) & MCY
\moldy\ supports most common forms of potential function.  In
particular, 
\begin{list}{}{%
\setlength{\parsep}{0in} 
\setlength{\labelwidth}{1in}
\setlength{\labelsep}{0.25in}
\setlength{\leftmargin}{1.5in}
\renewcommand{\makelabel}[1]{\hspace{\labelsep}\emph{#1} \hfil}}
\item[Lennard-Jones] $\phi(r) = \epsilon((\frac{\sigma}{r})^{12} -
(\frac{\sigma}{r})^{6})$
\item[6-exp] $\phi(r) = -\frac{A}{r^{6}} +
B\exp(-Cr)$\\
This is the most general 6-exp form and
includes potential of the \emph{Born-Mayer} and \emph{Buckingham} forms.
\item[MCY] $\phi(r) = A \exp(-Br) - C \exp(-Dr)$
\item[generic]  $\phi(r) = A \exp(-Br) + C/r^{12} - D/r^4 -E/r^6 -F/r^8$\\
This is a composite which contains terms of several inverse powers
which may be useful for ionic solution simulations.
\end{list}

In addition to the short-range potential electric charges may be
specified for each atomic site which interact according to Coulomb's
Law.  See the following section for details.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{The Ewald Sum}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\label{sec:ewald}
The long range Coulomb interactions are handled using the Ewald Sum
technique in three dimensions\cite[p.\ 156]{berthaut:52,allen:87}.
The electrostatic potential of a system of charges is expressed as a
sum of short-range and long-range contributions.  Each term is written
as a series, the first in real space and the second, obtained by
Fourier transformation using the periodicity of the MD cell, in
reciprocal space.  The expression for the Coulomb energy $U$ is
\begin{eqnarray}
\label{eqn:ewald}
U & = &  \underbrace{\frac{1}{4 \pi \epsilon_0}
\sideset{}{^\dag}\sum_{\bm{n}} \sum_{i=1}^{N} \sum_{j=i+1}^{N} q_iq_j
\frac{\erfc( \alpha | \bm{r}_{ij} +  \bm{n} |)}{| \bm{r}_{ij} +
\bm{n} | }}_{\textnormal{Real-space term}} 
\nonumber \\
 & + & \underbrace{\frac{1}{\epsilon_0 V} \sum_{\bm{k} > 0} \frac{1}{k^2}
       e^{-\frac{k^2}{4 \alpha^2}} 
\left \lbrace 
\left | \sum_{i=1}^{N} q_i \cos(\bm{k \cdot r}_i) \right |^2 + 
\left | \sum_{i=1}^{N} q_i \sin(\bm{k \cdot r}_i) \right |^2 
\right \rbrace}_{\textnormal{Reciprocal-space term}}\\
 & - & 
\underbrace{\frac{\alpha}{4 \pi^\frac{3}{2} \epsilon_0} 
\sum_{i=1}^{N} q_i^2}_{\textnormal{Point self-energy}} 
 - \underbrace{\frac{1}{4 \pi \epsilon_0} \sum_{n=1}^M
\sum_{\kappa=1}^{N_m} \sum_{\lambda=\kappa+1}^{N_m} q_{n\kappa} q_{n\lambda}
\frac{\erf( \alpha | \bm{r}_{\kappa\lambda} |)}{|
\bm{r}_{\kappa\lambda}| }}_{\textnormal{Intra-molecular self energy}}
\nonumber \\
& - & \underbrace{ \frac{1}{8 \epsilon_0 V \alpha^2}
                    \left | \sum_{i=1}^N q_i 
                    \right |^2}_{\textnormal{Charged system term}} +
\; \underbrace{\left [  \frac{1}{6 \epsilon_0 V} 
                    \left | \sum_{i=1}^N q_i \bm{r}_i 
                    \right |^2 \right ]}_{\textnormal{Surface dipole term}}
\nonumber
\end{eqnarray}
where the ``daggered'' summation indicates omission of site pairs $i,
j$ belonging to the same molecule if $\bm{n}=\bm{0}$.  The meaning of
the symbols is

\begin{tabbing}
\rule{2cm}{0cm} \= \rule{2cm}{0cm} \=\\
\> $\bm{n}$        \> lattice vector of periodic array of MD cell images \\
\> $\bm{k}$        \> reciprocal lattice vector of periodic array of MD cell images \\
\> $k$             \> modulus of $\bm{k}$ \\
\> $i,j$           \> absolute indices of all charged sites \\
\> $n$             \> index of molecules \\
\> $\kappa,\lambda$ \> indices of sites within a single molecule \\
\> $N$             \> total number of charged sites \\
\> $M$             \> total number of molecules \\
\> $N_m$           \> number of sites on molecule $m$ \\
\> $\bm{p}_i$      \> co-ord of site $i$ relative to molecular 
centre-of-mass, $\bm{r}_i - \bm{R}_i$ \\
\> $q_i$           \> charge on absolute site $i$ \\
\> $q_{m\kappa}$   \> charge on site $\kappa$ of molecule $m$ \\
\> $\bm{r}_i$      \> Cartesian co-ordinate of site $i$ \\
\> $\bm{r}_{ij}$   \> $\bm{r}_j - \bm{r}_i$ \\
\> $\alpha$        \> real/reciprocal space partition parameter \\
\> $\pi_{lm}$      \> instantaneous stress tensor \\
\> $\delta_{lm}$   \> Kronecker delta symbol \\
\> $l, m$          \> $xyz$ tensor indices \\
\> $V$             \> volume of MD cell
\end{tabbing}


and the force on charge $i$ is given by
\begin{eqnarray}
  \label{eqn:ewald-force}
  \bm{f}_i &=& - \nabla_{\bm{r}_i} U \nonumber \\
  & = & \underbrace{\frac{q_i}{4 \pi \epsilon_0}
    \sideset{}{^\dag}\sum_{\bm{n}} \sum_{\genfrac{}{}{0pt}{}{j=1}{j \neq
        i}}^{N} q_j \left \lbrace \frac{\erfc( \alpha | \bm{r}_{ij} +
        \bm{n} |)}{| \bm{r}_{ij} +\bm{n} | } + \frac{2
        \alpha}{\sqrt{\pi}}e^{- \alpha^2 | \bm{r}_{ij} + \bm{n} |^2}
    \right \rbrace \frac{\bm{r}_{ij} + \bm{n}}{| \bm{r}_{ij} + \bm{n}
      |^2}
    }_{\textnormal{Real-space term}} \nonumber \\
  & + & \underbrace{\frac{2}{\epsilon_0 V} \sum_{\bm{k} > 0} q_i
    \frac{\bm{k}}{k^2} e^{-\frac{k^2}{4 \alpha^2}} \left \lbrace
      \sin(\bm{k \cdot r}_i) \sum_{j=1}^{N} q_j \cos(\bm{k \cdot r}_j) -
      \cos(\bm{k \cdot r}_i) \sum_{j=1}^{N} q_j \sin(\bm{k \cdot r}_j)
    \right \rbrace}_{\textnormal{Reciprocal-space term}}\\
  & + & \underbrace{\left [ \frac{q_i}{6 \epsilon_0 V} \left (
        \sum_{j=1}^N q_j \bm{r}_j \right ) \right
    ]}_{\textnormal{Surface dipole term}} \nonumber
\end{eqnarray}

The molecular forces and torques $\bm{F}$ and $\bm{N}$ are evaluated
from the site forces $\bm{f}_i$ using equations~\ref{eqn:comf} 
and~\ref{eqn:comt}.

Notice that the equation~\ref{eqn:ewald} for the energy contains a
correction for the intra-molecular self-energy, whose derivative is
absent from the equation for the forces
(equation~\ref{eqn:ewald-force}).  This term corrects for interactions
between charges on the \emph{same} molecule which are implicitly
included in the reciprocal space sum, but are not required in the
rigid-molecule model.  Though the site forces $\bm{f}_i$ do therefore
include unwanted terms these sum to zero in the evaluation of the
molecular centre-of-mass forces and torques (equations~\ref{eqn:comf}
and~\ref{eqn:comt}) (by the conservation laws for linear and angular
momentum).

\subsection{Parameter Values}
\label{sec:ewald-auto}

Both the real- and reciprocal-space series (the sums over $\bm{n}$ and
$\bm{k}$) converge fairly rapidly so that only a few terms need be
evaluated.  We define the \emph{cut-off} distances $r_c$ and $k_c$ so
that only terms with $| \bm{r}_{ij} +\bm{n} | < r_c$ and $|\bm{k}| < k_c$
are included.  The parameter $\alpha$ determines how rapidly the terms
decrease and the values of $r_c$ and $k_c$ needed to achieve a given
accuracy. 

For a fixed $\alpha$ and accuracy the number of terms in the
real-space sum is proportional to the total number of sites, $N$ but
the cost of the reciprocal-space sum increases as $N^2$. An overall
scaling of $N^\frac{3}{2}$ may be achieved if $\alpha$ varies with
$N$. This is discussed in detail in an excellent article by David
Fincham\cite{fincham:94}.  The optimal value of $\alpha$ is
%
\begin{equation}
\alpha = \sqrt{\pi} \left ( \frac{t_R}{t_F} \frac{N}{V^2}\right )
^\frac{1}{6} 
\label{eqn:ewald-alpha}
\end{equation}
%
where $t_R$ and $t_F$ are the execution times needed to evaluate a
single term in the real- and reciprocal-space sums respectively.
If we require that the sums converge to an accuracy of $ \epsilon =
\exp ( -p )$ the cutoffs are then given by

\begin{eqnarray}
r_c  = \frac{\sqrt{p}}{\alpha} \\
k_c = 2 \alpha \sqrt{p}
\label{eqn:ewald-cut}
\end{eqnarray}

A representative value of $t_R/t_F$ specific to \moldy\ has been
established as 5.5.  Though this will vary on different processors
and for different potentials its value is not critical since it
enters the equations as a sixth root.  

It must be emphasized that the $r_c$ is used as a cutoff for the
short-ranged potentials as well as for the electrostatic part.  The
value chosen above \emph{does not} take the nature of the
non-electrostatic part of the potential into account.  It is therefore
the responsibility of the user to ensure that $r_c$ is adequate for
this part too.

\subsection{Uniform Sheet Correction}
The 5th term in equation~\ref{eqn:ewald} is necessary only if the system
has a non-zero net electric charge, and is useful in special cases such
as framework systems.  

In a periodic system the electrostatic energy is finite only if the
total electric charge of the MD cell is zero.  The reciprocal space
sum in equation~\ref{eqn:ewald} for $\bm{k}=0$ takes the form
\[\frac{1}{k^2}e^{-\frac{k^2}{4 \alpha^2}} \left | \sum_{i=1}^{N} q_i
 \right |^2\] which is zero in the case of electroneutrality but
infinite otherwise.  Its omission from the sum in
equation~\ref{eqn:ewald} is physically equivalent to adding a uniform
jelly of charge which exactly neutralizes the unbalanced point
charges.  But though the form of the reciprocal space sum is
unaffected by the uniform charge jelly the real-space sum is not.  The
real-space part of the interaction of the jelly with each point charge
as well as the self-energy of the jelly itself must be included giving
the fifth term in equation~\ref{eqn:ewald}.

\subsection{Surface Dipole Term}
The optional final term in equations~\ref{eqn:ewald} 
and~\ref{eqn:ewald-force} if used performs the calculations under
different periodic boundary conditions.  It was suggested by De Leeuw,
Perram and Smith\cite{deleeuw:80} in order to accurately model
dipolar systems and is necessary in any calculation of a dielectric
constant. 

The distinction arises from considerations of how the imaginary set of
infinite replicas is constructed from a single copy of the MD 
box\cite[pp 156-159]{allen:87}.  Consider a near-spherical cluster
of MD cells.  The ``infinite'' result for any property is the limit of
its ``cluster'' value as the size of the cluster tends to infinity.
However this value is non-unique and depends on the dielectric
constant, $\epsilon_s$ of the physical medium surrounding the cluster.
If this medium is conductive ($\epsilon_s=\infty$) the dipole
moment of the cluster is neutralized by image charges, whereas in a
vacuum ($\epsilon_s=1$) it remains.  It is trivial to show that
in that case the dipole moment per unit volume (or per MD cell) does
\emph{not} decrease with the size of the cluster.

The final term in equation~\ref{eqn:ewald} is just the dipole energy,
and ought to be used in any calculation of the dielectric constant of
a dipolar molecular system.  It is switched on by \moldy's control
parameter \Lit{surface-dipole}.  Note that as it represents the
dipole at the surface of the cluster the system is no longer truly
periodic.

Conversely it \emph{must not} be used if the simulated system contains
mobile ions.  Consider an ion crossing a periodic boundary and jumping
from one side of the MD cell to another.  In that case the dipole
moment of the MD cell changes discontinuously.   Because of the
surface dipole term the calculation would model a discontinuous
macroscopic change in the dipole moment of the whole system caused by
an infinite number of ions jumping an infinite distance.  This is
manifested in practice by a large and discontinuous change in the
energy of the system and on the force on each charge within it.

This situation is completely non-physical but is easily avoided.
However the problem may also arise more subtly even when there are no
mobile ions if a framework is being simulated
(section~\ref{sec:frameworks}).  The framework is treated as a set of
discrete, but fixed atoms rather than a molecular unit.  If the shape
of the unit cell is allowed to vary then ions constituting the
framework may indeed cross MD cell boundaries causing the
aforementioned problems.

\subsection{Stress}

The internal stress (and pressure) of an atomic system is given by the
volume-derivative of the internal energy.  The situation is slightly
more complicated for rigid molecules since molecules do not scale with
volume and only the inter-molecular distances vary.  The resulting
expression for the Coulombic part of the instantaneous stress
$\pi_{ik}^e$ is\cite[Appendix A]{nose:83}

\begin{eqnarray}
\label{eqn:ewald-stress}
V\pi_{lm}^e & = & \underbrace{\frac{1}{4 \pi \epsilon_0}
\sideset{}{^\dag}\sum_{\bm{n}}\sum_{i=1}^{N} \sum_{j=i+1}^{N} q_iq_j 
\left \lbrace 
\frac{\erfc( \alpha | \bm{r}_{ij} +  \bm{n} |)}{| \bm{r}_{ij} +\bm{n} | } +
\frac{2 \alpha}{\sqrt{\pi}}e^{- \alpha^2 | \bm{r}_{ij} +  \bm{n} |^2}
\right \rbrace \frac{(\bm{r}_{ij} + \bm{n})_l(\bm{r}_{ij} +
\bm{n})_m}{| \bm{r}_{ij} + \bm{n} |^2}}_{\textnormal{Real-space term}}
    \nonumber \\
 & + & \underbrace{\frac{1}{\epsilon_0 V} \sum_{\bm{k} > 0} \frac{1}{k^2}
       e^{-\frac{k^2}{4 \alpha^2}}
\left ( \delta_{lm}  \!- \! 2 \left [ \frac{1}{k^2} \! + \! \frac{1}{4 \alpha^2}
\right ] \bm{k}_l\bm{k}_m \right )\left \lbrace 
%
\left | \sum_{i=1}^{N} q_i \cos(\bm{k \cdot r}_i) \right |^2 + 
\left | \sum_{i=1}^{N} q_i \sin(\bm{k \cdot r}_i) \right |^2 
\right \rbrace}_{\textnormal{Reciprocal-space term}} \nonumber \\
& - & \underbrace{\sum_{i=1}^{N}  (\bm{F}_i)_l
  (\bm{p}_i)_m}_{\textnormal{Molecular Virial Correction}} 
-  \underbrace{ \frac{\delta_{lm}}{8 \epsilon_0 V \alpha^2}
                    \left | \sum_{i=1}^N q_i 
                    \right |^2}_{\textnormal{Charged system term}}
\end{eqnarray}

The true internal stress is the ensemble average, $\pi_{lm} = \left <
  \pi_{lm}^e + \pi_{lm}^{\textnormal{s.r.}} + \pi_{lm}^K \right >$,
where $\pi_{lm}^{\textnormal{s.r.}}$ and $\pi_{lm}^K$ are the
short-range force and kinetic contributions respectively.
$\pi_{lm}^e$ enters into the Parrinello-Rahman equations of motion
(see section~\ref{sec:const-stress}).

The term marked \emph{Molecular Virial Correction} in
equation~\ref{eqn:ewald-stress} is the difference between the
site-site virial $\sum_i \bm{f}_i \cdot \bm{r}_i$ and the molecular virial
$\sum_i \bm{F}_i \cdot \bm{R}_i$ and is subtracted after all of the site forces
and the molecular forces have been calculated including the
short-range potential components which are not included in the
equations above.  Though it is not apparent in reference\cite[Appendix
A]{nose:83} this term has exactly the same form for all parts of the
stress --- the short-range potential and the real- and reciprocal
space Coulombic parts.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Periodic Boundaries --- the Link Cell Method}%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\label{sec:link-cell}
The real space part of the Ewald (equation~\ref{eqn:ewald}) and
short-range potential energy is a sum of contributions over pairs of
sites.  In both cases the interaction decays rapidly with separation,
which means that only site pairs closer then some \emph{cutoff} distance
$r_c$ need be considered.  Several methods are available to enumerate
site pairs and choose those within the cutoff.

Most simple MD codes simply loop over all pairs of particles in the MD
box and compute the separation $r$ for each.  If $r < r_c$ the
interaction is computed.  This method suffers from several
disadvantages.  Since for any given site, the interaction with any
other site is considered only once, only the \emph{nearest} periodic
image of that site is included (the \emph{minimum-image} convention).
However this restricts the cutoff radius to less than half the MD cell
dimension $r_c < 2L$.  More serious is the way the computational
time scales with the number of sites.  If there are $N$ sites, there
are $O(N^2)$ separations to compute and the overall time therefore scales
as $O(N^2)$.

The Verlet \emph{Neighbour List} scheme\cite[pp 146-149]{allen:87} makes
use of the spatial locality of the interaction potential by
maintaining a list for each site of all the ``neighbouring'' sites
(\ie\ all those within the cutoff distance).  This can give
considerable gains for moderate numbers of sites, but it ultimately
requires $O(N^2)$ time (to build the lists) as well as $O(N)$ storage for
the lists.

\moldy\ uses an implementation of the link cell method of Quentrec
\emph{et al}.\cite{quentrec:75} described in Allen and Tildesley's
book\cite[pp 149-152]{allen:87} which is a true $O(N)$ algorithm.  The
fundamental idea is that the MD cell is partitioned into a number of
smaller cells, known as \emph{subcells}.  Every timestep a linked list
of all the particles contained in each subcell is constructed.  The
selection of all pairs of \emph{particles} within the cutoff is
achieved by looping over all pairs of \emph{subcells} within the
cutoff and particles within the subcells.  Because of their regular
arrangement, the list of neighbouring subcells is fixed and may be
precomputed.  Its construction takes only $O(N)$ operations and only
$O(N)$ pair interactions need be calculated.

% Molecular nature of cutoffs.

When the system consists of polyatomic molecules it is important that
all sites belonging to a particular molecule are assigned to the same
cell.  Otherwise it is impossible to calculate the stress and pressure
(equation~\ref{eqn:ewald-stress}) correctly as MD cell vectors are
added to some intra-molecular distances.  Therefore all sites of any
molecule are assigned to a cell if the molecular centre-of-mass lies
inside it.

One drawback of the link cell method has been the difficulty of
implementing it efficiently for vector processors.  The linked list of
``neighbour'' particles is not stored in the regular-stride array
which is required for vectorization.  Heyes and Smith\cite{heyes:87}
pointed out that \emph{gather} operations might be used to assemble a
temporary array of neighbour particle co-ordinates from which the
interaction potential and forces could be evaluated in vector mode.  A
\emph{scatter} operation is then used to add the resulting forces to
the total force array.  This is the technique used in \moldy.  Almost
all modern vector machines have scatter/gather hardware which means
these operations are fairly cheap.

\subsection{No minimum-image convention}
One notable feature of the implementation of the link cell method in
\moldy\ is that it does \emph{not} follow the \emph{minimum image}
convention used in most MD codes.  Instead the list of neighbouring
cells, and hence interactions considered, includes \emph{all} periodic
images of a particle which are within the cutoff.  This means that it
is quite safe to use a cutoff of more than half of the MD cell side in
any direction.  

% quick 'n dirty vs strict.
\subsection{Cell or Strict cutoff}
\label{sec:strict-cutoff}
There are two options for the way in which site-site interactions are
selected for inclusion in the total energy and forces.  These are
\emph{cell-based} cutoff and \emph{strict} cutoff and are selected
by the \Lit{strict-cutoff} control parameter.

In cell-based mode (\Lit{strict-cutoff=0}) the neighbour cell list is
built to include only those cells whose centre is within the cutoff
radius of the centre of the reference cell.  All interactions between
sites belonging to molecules in the neighbouring cell list are
computed.  This is a ``quick and dirty'' method as some interactions
between sites closer than the cutoff will inevitably be excluded
whereas some outside the cutoff range will be included.

In strict mode (\Lit{strict-cutoff=1}) all interactions between pairs
of \emph{sites} within the cutoff are included.  The neighbouring cell
list contains all pairs of cells with any parts closer than the cutoff
plus twice the greatest molecular radius.  This ensures that all
appropriate interactions are included.  Furthermore, all interactions
between sites further apart than the cutoff are excluded (by the
expedient of setting their separation to a large value in the
potential calculation).  This means that large and asymmetric
molecules are handled correctly.  

For a given cutoff radius the cell-based mode is rather quicker than
the strict mode since the neighbouring cell list is much smaller and
fewer interactions are computed.  However to ensure that significant
interactions are not omitted, the cutoff ought to be set to a
greater value than strictly required.    This tends to offset the
potential speed gain.   On the other hand, if strict isotropy is
required in a liquid simulation for example then the strict cutoff
option ought to be used.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Temperature Initialization and Control}%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
From the equipartition theorem, every degree of freedom in the system,
$f$ has the same kinetic energy, given by $\left < {\mathcal K} \right
>_f~=~\frac{1}{2}k_BT$.  The effective temperature of the system is
therefore given by the ensemble average of its kinetic energy.
\begin{equation}
\label{eqn:equipartition}
T = \left < {\mathcal T} \right > = \frac{2}{gk_B} \left <
  \sum_{f=1}^{g} {\mathcal K}_f \right > = \frac{1}{3Nk_B}\left <
  \sum_{i=1}^N m_i \bm{v}_i^2 + \bm{\omega}_i \cdot \bm{I} \cdot
  \bm{\omega}_i \right >
\end{equation}
Here ${\mathcal K}_f$ is the instantaneous kinetic energy of degree of
freedom $f$, $g$ is the number of degrees of freedom, $N$ is the
number of molecules, $\mathcal T$ is an instantaneous ``temperature''.

It is almost always desirable that a simulation be conducted so that
the temperature is the supplied parameter rather than the kinetic
energy.  This requires some mechanism to fix the \emph{average}
kinetic energy at thermal equilibrium.  The \emph{initial} kinetic
energy may be set approximately by choosing random velocities which
sample the Maxwell-Boltzmann distribution at the desired temperature,
and this is indeed what \moldy\ does on starting a new run (see
section~\ref{sec:velinit}).  But because the initial configuration is
usually far from equilibrium it will have too much potential energy.
As the run progresses this will be converted into kinetic energy,
raising the temperature above the desired value. It is therefore
necessary to have some mechanism for removing excess kinetic energy as
the run progresses.

\moldy\ offers several mechanisms to control the temperature. The
common technique of \emph{velocity scaling} is suitable for use
during the equilibration period but does not generate meaningful
particle trajectories. The Nos\'e-Hoover method couples the system to
a heat bath using a fictional dynamical variable and the Gaussian
thermostat replaces the Newton-Euler equations by variants of which
the kinetic energy is a conserved quantity.

\subsection{Rescaling}
\label{sec:rescaling}

At periodic intervals linear and
angular velocities are multiplied by a factor of
\begin{equation}
\label{eqn:scaling}
s = \sqrt{\frac{gk_BT}{2\mathcal K}}
\end{equation}
where $T$ is the desired temperature.  By repeatedly setting the
``instantaneous'' temperature to the correct value while the system
approaches its equilibrium state, the kinetic energy is made to
approach its desired value.  \emph{Scaling} may be performed every
timestep, or every few depending on the simulation conditions.

An MD run with scaling does not generate a valid statistical ensemble,
and it must therefore be switched off before any calculation of
thermodynamic averages is performed.

\moldy\ incorporates two refinements to the basic scaling algorithm
(which are selected by the control parameter \Lit{scale-options}).
Linear and angular velocities can be scaled independently, either for
the whole system or for each species individually.  In this way, one
does not rely on the interactions of these degrees of freedom for
convergence to equilibrium.  In many systems these degrees of freedom
are loosely coupled and the exchange of energy between them is slow.
In these cases individual scaling can speed up the approach to
equilibrium considerably.

The other refinement addresses the problem of setting the temperature
accurately.  At equilibrium the system's kinetic energy fluctuates
with mean-square amplitude\footnote{This formula actually applies to
the Canonical rather than the microcanonical ensemble, but it serves
for the purpose of this argument.} $\left < \delta {\mathcal K}^2\right > =
\frac{1}{2}g\left(k_BT\right)^2$, which corresponds to a
rms fluctuation in the instantaneous ``temperature'' 
$\sqrt{\left < \delta {\mathcal T}^2\right >} = 
\sqrt{2 /g} T$.   
The difficulty with applying equation~\ref{eqn:scaling}
is the instantaneous kinetic energy $\mathcal K$ in the denominator.
Strictly, scaling ought to use the \emph{average} kinetic energy
$\left<\mathcal K \right>$ as in equation~\ref{eqn:equipartition}, but
this quantity is not known until after the run is completed.  Because
of this equation~\ref{eqn:scaling} can only set the temperature to an
accuracy of $\sqrt{1/g}$.  This is often inadequate for purposes of
comparison with experiment.

In order to allow the temperature to be set with greater accuracy,
\moldy\ allows the use of a partial average in the denominator,
\begin{equation}
\label{eqn:rav-scaling}
s = \sqrt{\frac{gk_BT}{2\left < {\mathcal  K}\right >^\prime}}
\end{equation}
where $\left < {\mathcal K}\right >^\prime$ is the ``rolling'' average of
$\mathcal K$ over some number of preceding timesteps.  That number is
determined by the control parameter \Lit{roll-interval}.   

This option should be used cautiously.  The change in $\left <{\mathcal
K}\right >$ upon scaling only has a significant effect on the average
after many timesteps. If the subsequent scaling is performed before
this change is reflected in the value of $\left < {\mathcal K}\right
>^\prime$ it will use an out-of-date value of the average kinetic
energy.  It is therefore recommended that the number of timesteps
between scalings be greater than or equal to the number used to
compute the rolling average.  Otherwise it is possible to produce wild
overshoots and oscillations in the temperature.

Finally, there is a method for tackling really difficult cases when
even individual scaling is unable to keep the temperature under
control.  This might be a far-from-equilibrium configuration where the
potentials are so strong that velocities rapidly become very large,
or when a single molecule acquires a very large velocity.  In that
case the velocities can all be re-initialized randomly from the
Maxwell-Boltzmann distribution periodically.  This provides a kind of
pseudo Monte-Carlo equilibration for difficult cases.

\subsection{Nos\'e-Hoover Thermostat}
\label{sec:const-temp}
A more sophisticated approach than rescaling is to couple the system
to a heat bath.  Nos\'e\cite{nose:84} proposed an extended-system
Hamiltonian to represent the degrees of freedom of the thermal
reservoir: \moldy\ uses the simpler but equivalent formulation by
Hoover\cite{hoover:85,allen:87}.  The equations of
motion (equations~\ref{eqn:newton} and~\ref{eqn:euler}) are modified thus

\begin{eqnarray}
\label{eqn:nhtherm}
\ddot{\bm{R}}_i = \frac{\bm{F}_i}{M_i} - \zeta \dot{\bm{R}}_i
\nonumber \\
\bm{I}_i \cdot \dot{\bm{\omega}}_i - \bm{\omega}_i \times \bm{I}_i
\cdot
\bm{\omega}_i = \bm{N}_i - \zeta \bm{I}_i \cdot \bm{\omega}_i \\
\dot{\zeta} = \frac{g}{Q}\left ( k_B {\mathcal T} - k_B T \right )
\nonumber
\end{eqnarray}

\noindent
where most of the symbols have their previous meanings, $g$ is the
number of degrees of freedom in the system. $\zeta$ is a new ``heat
bath'' dynamic variable and $Q$ is the associated fictitious ``mass''
parameter.  With a suitable choice of $Q$ these equations generate
trajectories which sample the canonical ensemble\cite{nose:84}.  In
other words both averages and fluctuations calculated as time averages
from a simulation run tend to their canonical ensemble limits.%
\footnote{There has been some discussion in the literature of the
  validity of representing a heat bath by a single dynamical variable,
  which the more diligent reader may wish to
  explore\cite{cho:93,nose:91}.} This does not necessarily guarantee
the correctness of \emph{dynamical} quantities.

It is apparent from equations~\ref{eqn:nhtherm} that $\zeta$ has the
dimensions of a time derivative, and is analogous to the unit cell
velocities in equations~\ref{eqn:par}.  It is therefore treated as if
it was a velocity and updated using only steps \emph{ii--v} of the
Beeman integration procedure (equations~\ref{sec:beeman}).  Note that
the equation for the centre-of-mass acceleration depends
\emph{indirectly} on the velocities through $\mathcal T$ as well as
explicitly.  The iteration on steps \emph{iii--v} of
equations~\ref{sec:beeman} is therefore essential to integrate these
equations consistently.

There are two criteria to be considered when choosing the value of
$Q$.  The coupling to the heat bath introduces non-physical
oscillations of period $t_0 = 2 \pi \sqrt{Q / 2 g k_B T}$ which may be
easily detected in the total energy\cite{nose:91}.  Firstly it must be
arranged that there are sufficiently many oscillations during a
simulation run that the computed thermodynamic values represent
averages over many periods $t_0$. This ensures that the configurations
used to calculate the averages sample the whole of the phase space
generated by the fluctuations in $\zeta$ to represent the canonical
ensemble.  Secondly $Q$ should be chosen so that $t_0$ is large
compared to the characteristic decay time of dynamical correlation
functions. This is to ensure that the fictitious dynamics of the heat
bath are decoupled from the real molecular dynamics, and is
particularly important in a simulation of dynamic
properties\cite{cho:92} such as velocity correlation functions. Since
the first criteria favours a small $Q$ and the second a large one, it
may be necessary to increase the total simulation time in order to
satisfy them both.

\subsection{Gaussian Thermostat}
An alternative approach is known as ``constrained dynamics'' whereby
the equations of motion are modified to generate trajectories which
exactly satisfy ${\mathcal T} = T$ at all times.  One such is the Gaussian
constraint (see Allen and Tildesley\cite{allen:87} pp 230-231). The
equations of motion are 

\begin{eqnarray}
\label{eqn:gtherm}
\itemsep=0.5em
\ddot{\bm{R}}_i = \frac{\bm{F}_i}{M_i} - \zeta_T \dot{\bm{R}}_i \nonumber \\
\zeta_T = \frac{\sum_j\bm{v}_j \cdot \bm{F}_j}{\sum_j M_j\bm{v}_j^2} \\
\bm{I}_i \cdot \dot{\bm{\omega}}_i - \bm{\omega}_i \times \bm{I}_i
\cdot
\bm{\omega}_i = \bm{N}_i - \zeta_R \bm{I}_i \cdot \bm{\omega}_i \nonumber \\
\zeta_R = \frac{\sum_j\bm{\omega}_j \cdot \bm{N}_j}{\sum_j
  \bm{\omega}_j \cdot \bm{I}_j \cdot \bm{\omega}_j}\nonumber
\end{eqnarray}

These bear a superficial resemblance to equations~\ref{eqn:nhtherm}
but now $\zeta$ is not itself a dynamical variable but a function of the
other dynamical variables.  Note
that $T$ does not enter explicitly into  equations~\ref{eqn:gtherm}
since ${\mathcal T}$ is now a conserved quantity equal to its initial
value at all subsequent times. Perhaps surprisingly these equations of
motion generate configurational \emph{averages} from the canonical
ensemble, but this does \emph{not} hold true for the momenta and
therefore dynamics nor for fluctuations. 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Constant Stress}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\label{sec:const-stress}
It is frequently desirable to conduct simulations under conditions of
constant pressure or stress, rather than constant volume.  For
example, this allows the simulation of a solid-state phase transition
with a change of symmetry or unit cell size.  \moldy\ incorporates the
constant pressure method of Parrinello and Rahman\cite{parrinello:81}.

In a constant-stress simulation the MD cell changes in size and shape
in response to the imbalance between the internal and externally
applied pressure.  For an exposition of the method the reader should
refer to Parrinello and Rahman's paper\cite{parrinello:81} and to
Nos\'{e} and Klein's extension to rigid molecular
systems\cite{nose:83}.  The equation of motion for the reduced
centre-of-mass co-ordinates $\bm{S}_i =
\bm{h}^{-1}\bm{R}_i$ is
\begin{equation}
\label{eqn:par}
M_i\ddot{\bm{S}}_i = \bm{h}^{-1} \bm{F}_i - M_i \bm{G}^{-1}\dot{\bm{G}}\dot{\bm{S}}_i
\end{equation}
replacing the straightforward Newton equation~\ref{eqn:newton}.
$\bm{h}$ denotes the $3 \times 3$ MD cell matrix whose columns are
the MD cell vectors $\bm{a}, \bm{b}$ and $\bm{c}$, $\bm{F}_i$ is the
centre-of-mass force and $\bm{G} =\bm{h^\prime h}$.
Equation~\ref{eqn:euler}, the Euler equation, governs the angular
motion exactly as in the constant-volume case.

The dynamics of the unit cell matrix $\bm{h}$ are governed by the
equation
\begin{equation}
\label{eqn:rahman}
W\ddot{\bm{h}} = \left ( \bm{\pi} - p \right ) \bm{\sigma}
\end{equation}
where $W$ is a fictitious mass parameter, $\bm{\sigma} = V
\bm{h}^{\prime-1}$ and $p$ is the external pressure.  The
instantaneous internal stress $\bm{\pi}$ is given by 
\begin{equation}
\label{eqn:prstress}
\bm{\pi} = \frac{1}{V}\sum_{i=1}^N m_i (\bm{h}_i\dot{\bm{s}}_i)^2 +
\bm{\pi}^{\textnormal{s.r.}} + \bm{\pi}^e 
\end{equation}
with the short-ranged and electrostatic components given by
equations~\ref{eqn:stress-sr} and~\ref{eqn:ewald-stress} respectively.

The choice of fictitious mass, $W$ is governed by similar
considerations to those concerning the heat bath mass, $Q$
discussed in section~\ref{sec:const-temp}.

Nos\'{e} and Klein\cite{nose:83} describe and address the problem of
the whole MD cell rotating during the course of the simulation.  This
is because angular momentum is not conserved in a periodic system, and
because the $\bm{h}$ matrix has 9 degrees of freedom, three more than
needed to specify the position and orientation of the MD cell.  Their
solution is to constrain the $\bm{h}$ matrix to be symmetric, and
involves a modification of the Parrinello-Rahman equations.

\moldy\ incorporates a rather different constraint which is not only
simpler to implement (as it does not require modification of the
equations of motion) but which also has a more obvious physical
interpretation.  The lower three sub-diagonal elements of the $\bm{h}$
matrix are constrained to zero.  In other words the MD cell vector
$\bm{a}$ is constrained to lie along the $x$-axis and $\bm{b}$ is
constrained to lie in the $xy$-plane.  Physically this may be thought
of as implementing an MD box lying on a horizontal surface under the
influence of a weak gravitational field.  The implementation is
trivial; at each timestep the acceleration of those components,
$\ddot{\bm{h}}_{ij}$, is set to zero which is equivalent to adding a
fictitious opposing force.

This constraint technique is not restricted merely to eliminating
redundant degrees of freedom, but can also be used for other purposes.
For example it may be used to allow uniaxial expansion only.  The most
important use of $\bm{h}$ matrix constraints however is probably for
simulations of liquids.  Since a liquid can not support shear stress
there is no restoring force to keep the simulation cell nearly cubic,
and it will therefore drift to a parallelepiped shape.  To counter
this tendency $\bm{h}$ may be constrained so that only the diagonal
elements are non-zero and allowed to change.  This does not give a
completely isotropic MD cell expansion, but the time average should
tend towards a cubic cell.

MD cell constraints are selected using the control parameter
\Lit{strain-mask} (see section~\ref{sec:cp-constraints}).  A value of
238 will freeze the off-diagonal components of $\bm{h}$.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Radial Distribution Functions}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%`
\label{sec:rdf}
The \emph{radial distribution function} or RDF is one of the most
important structural quantities characterizing a system, particularly
for liquids.  For a one-component system, the RDF is defined
as\cite[p445]{hansen:86}
\begin{eqnarray}
g(r) & = & \frac{1}{\rho^2} \left < \sum_i \sum_{j\neq i}
\delta(\bm{r} + \bm{r}_i - \bm{r}_j) \right > \\ \nonumber
 &  \approx  & V \left < \delta(\bm{r} + \bm{r}_1 - \bm{r}_2) \right >
\end{eqnarray}
where we use the angle brackets to denote a spherical average as well
as the usual configurational average.  Allen and
Tildesley\cite[pp184,185]{allen:87} describe how to evaluate $g(r)$
from a histogram of pair distances accumulated during a simulation
run. With the notation that $N$ is the total number of particles, $b$
is the number of the histogram bins, $\delta r$ is the bin width (so
that $r = b \delta r$), $n_{\textnormal{his}}(b)$ is the accumulated
number per bin, $\tau$ is the number of steps when binning was carried
out
\begin{equation}
\label{eqn:rdf-atom}
g(r + \delta r/2) = \frac{3 n_{\textnormal{his}}(b)}{4 \pi \rho N \tau
  [(r + \delta r)^3 - r^3]} 
\end{equation}
In the case of a molecular system, the partial RDF for atoms
$\alpha$ and $\beta$ is defined as\cite[p 445]{hansen:86}
\begin{equation}
g_{\alpha \beta}(r) = \frac{1}{\rho^2 V} \left < N(N-1)\delta(\bm{r} +
  \bm{r}_{1\alpha} - \bm{r}_{2\beta}) \right > 
\end{equation}
which may be rewritten more usefully for an arbitrary multi-component
mixture by eliminating the molecular density $\rho$ and number $N$ as
\begin{equation}
g_{\alpha \beta}(r) = V \left < \delta(\bm{r} +  \bm{r}_{1\alpha} -
  \bm{r}_{2\beta}) \right > 
\end{equation}
In the simulation this is evaluated by an expression very similar to
equation~\ref{eqn:rdf-atom}
\begin{equation}
\label{eqn:rdf-site}
g_{\alpha \beta}(r + \delta r/2) = \frac{3 N n_{\textnormal{his}}(b)}{4 \pi
\rho N_\alpha N_\beta \tau [(r + \delta r)^3 - r^3]}
\end{equation}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{The Initial Configuration}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
One of the more trying aspects of initiating a molecular dynamics
simulation is getting it started in the first place.  It is not hard
to see the reason why.  An MD integration algorithm will only generate
a good approximation to the correct equations of motion if the forces
and velocities of the particles are less than some value.  The
timestep is chosen so that this criterion is satisfied for
near-equilibrium configurations.  But if the configuration is far from
equilibrium certain forces may be extremely large (due to atoms
approaching each other too closely).  And worse, the breakdown of the
integrator leads to breakdown of the conservation laws and the
system evolves to a state even further from equilibrium.

One way around this difficulty is to start the system from a
configuration known to be near-equilibrium, such as a known crystal
structure.   For a solid-state simulation this is the method of
choice, and \moldy\ allows any initial structure to be specified and
replicated to fill the MD cell.  In the case of a liquid, a
crystalline initial state is less desirable, and indeed, none may be
known.  Furthermore such a starting configuration restricts the
allowed number of molecules to a multiple of the number in the unit
cell and worse, may force the use of a non-cubic MD cell.

\moldy\ incorporates a novel method of generating an initial
configuration which, in the main, overcomes these problems.
\subsection{The Skew Start Method}
\label{sec:skewstart}
The essence of the \emph{Skew Start} method is to generate a
configuration which, though not periodic in 3 dimensions, nevertheless
is sufficiently regular to guarantee a minimum separation between
molecular centres of mass.  Figure~\ref{fig:skewstart} demonstrates
the principle in 2 dimensions.

\begin{figure}
\setlength{\unitlength}{0.012500in}%
\input{fig_skewstart.ftx}
\caption[The Skew Start method]{The Skew Start method.  $N$ molecules
  are placed at evenly-spaced intervals, $a$ on a line joining a
  corner of the MD cell to its image $k$ cells away (5 in this
  illustration).  When mapped back into the original cell this
  guarantees a minimum separation of $min(d,a)$.}
\label{fig:skewstart}
\end{figure}

The $N$ molecules are placed at some suitable interval $a$ on a line
drawn between one corner of the MD cell (of side $L$) and one of its
periodic images.  Clearly the index $(h,k)$ of the image cell corner
should be chosen so that the molecule spacing, $a$ is close to the
spacing of the line's images, $d$. For simplicity, choose $k=1$ which
leads to the condition:
\begin{eqnarray}
\nonumber
 &a = \frac{L \sqrt{h^2+1}}{N} \approx \frac{L}{\sqrt{h^2+1}} = d \\
& \Rightarrow h \approx \sqrt{N-1}
\end{eqnarray}
Therefore the optimum value of $h$ is the nearest integer to
$\sqrt{N-1}$.  

The formalism may be extended to three dimensions, and yields the
results for the molecular and ``inter-line'' spacings $a$, $d_y$ and
$d_z$ respectively
\begin{eqnarray}
\label{eqn:skew-hkl}
\nonumber
a & = & \frac{L}{N}\sqrt{h^2+k^2+l^2}\\
d_y  & = & L \frac{\sqrt{k^2+l^2}}{\sqrt{h^2+k^2+l^2}} \\
d_z & \approx & L\frac{l}{k} \qquad\textnormal{(assuming $h/k$ is an integer)}
\nonumber
\end{eqnarray}
The ``equal spacing'' requirements are satisfied approximately by
\begin{eqnarray}
  \nonumber
  h & \approx & N^{2/3} \\
  k & \approx & N^{1/3} \\
  \nonumber
  l & = & 1
\end{eqnarray}
which when substituted into equation~\ref{eqn:skew-hkl} yields
\begin{equation}
  a \approx d_y \approx d_z \approx LN^{-1/3}
\end{equation}

Using this method an arbitrary number of molecules may be packed into
a cubic cell with a guaranteed minimum centre-of-mass separation of
approximately $LN^{-1/3}$.  In contrast to other methods, such as
random placement with excluded volume, it will always yield a
configuration no matter how high the density.  It is also very simple
to implement.

It still remains to determine the initial orientations of the
molecules in the case of polyatomics.  In the current implementation
these are assigned randomly, which works well for small or
near-spherical molecules.  Further refinements which may help avoid
overlaps for elongated molecules are possible, such as a periodic
sequence of orientations along the line, but no investigation of this
possibility has yet been carried out.

In practice the method has proved to work well for water and aqueous
systems and always yields a configuration which may be evolved towards
equilibrium by the MD equations of motion.  Any feedback on its
performance in more difficult cases will be welcome.

\subsection{Initial Velocities}
\label{sec:velinit}
It remains to choose the initial velocities of the molecules to
complete the specification of the initial configuration.  The recipe
is the same irrespective of whether the molecules are started on a
lattice or from a skew start.  The initial centre-of-mass velocities
are chosen from the Maxwell-Boltzmann distribution at the temperature
specified for the simulation\cite[pp 170]{allen:87}.  That is, the
velocities are chosen from a set of random numbers with a Gaussian
distribution and normalized so that the probability density $p(v)$ of
the $xyz$ component of the velocity $v_{ik}$ of molecule $k$ is
\begin{equation}
p(v_{ik}) = \left ( \frac{m_k}{2 \pi k_B T}\right )^{1/2} 
\exp(-\frac{m_k v_{ik}^2}{2 k_B T})
\end{equation}
This is easily accomplished given a random number generator which
samples from a Gaussian distribution with unit variance.  Given a
random number $R_{ik}$, each component of velocity is set to
\begin{equation}
v_{ik} = \sqrt{\frac{k_B T}{m_k}} R_{ik}
\end{equation}
Each component of the angular velocity (expressed in the
principal frame) has a probability distribution
\begin{equation}
p(\omega^p_{ik}) = \left ( \frac{I_{ik}}{2 \pi k_B T}\right )^{1/2}
\exp(-\frac{I_{ik} (\omega^p_{ik})^2}{2 k_B T})
\end{equation}
which is ensured by assigning a random velocity
\begin{equation}
\label{eqn:omega-rand}
\omega^p_{ik} = \sqrt{\frac{k_B T}{I_{ik}}} R_{ik}
\end{equation}

Since the dynamical variables used by \moldy\ for the angular
co-ordinates are in fact the quaternions and their derivatives, we
must set the quaternion derivatives and accelerations to the corresponding
values. Using equations~\ref{eqn:qomega} and~\ref{eqn:qddot} we
have
\begin{equation}
\Quat{\dot{q}} = \Quat{q}(0,\bm{\omega}^p/2)
\end{equation}
and
\begin{equation}
\Quat{\ddot{q}} = -\frac{1}{4}(\omega^p)^2 \Quat{q}
\end{equation}

Finally, we note that if a molecule has less than three degrees of
freedom, that is $I_{ik}=0$ for some $i$ the corresponding angular
velocities \etc\ are simply set to zero.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Frameworks}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\label{sec:frameworks}
In addition to the bulk properties of solids and liquids, much
attention has been devoted in recent years to using MD simulation to
model atoms or molecules interacting with surfaces or other structures
such as zeolites.  The distinctive feature of such a situation is that
the 2- or 3-dimensional surface or structure is larger than the
interacting molecules by many orders of magnitude.  In fact the
idealization of this system makes the structure infinite in extent.
An atomistic model of this kind of a system necessitates choosing the
periodic boundary conditions of the MD cell to be commensurate with
the periodicity of the surface or structure.  This manual will refer
to an infinitely repeating crystalline surface or structure as a
\emph{framework}.

There are two possible formulations of a system of this kind for use
in a MD simulation.  Firstly the framework may be modelled as a set of
independent atoms interacting via pair potentials.  This merely
requires specifying the correct initial structure and MD cell plus a
suitable set of pair potentials. The latter model both the internal
forces which determine the crystal structure of the framework and its
interaction with the atoms or molecules of the fluid.  Conceptually
there is no distinction between this situation and a regular solid or
liquid system, and the mechanics of initiating a simulation are
handled in exactly the usual manner.

However there are situations in which this ``atomic'' approach is
impractical.  Because the system being modelled is essentially
``two-phase'' the atoms of the framework find themselves under the
influences of two distinct kinds of force.  There are the strong
forces, usually covalent or ionic, which bind the atoms to form the
framework itself and the weaker, non-bonded forces of the interaction
with the fluid.  Ideally these could all be modelled by a single
consistent set of interatomic potentials which are sufficiently
transferable to yield an accurate crystal structure for the framework
as well as the molecular structure of the fluid and its interaction
with the surface.  Regrettably such potentials are hard to find.

Furthermore the characteristic vibrational frequencies of the solid
framework will probably be much higher than those of the fluid.
Consequently the timestep must be chosen sufficiently small to
accurately model the crystalline vibrations.  This will usually be far
smaller than the value required for the fluid, necessitating very
lengthy MD runs to model both regimes properly.  This is, of course,
exactly the argument used to justify rigid-molecule models. 

\moldy\ implements a variation on the rigid-molecule model to simulate
a rigid framework structure periodically extended throughout space.
There are a few subtleties which must be correctly handled to achieve
a consistent implementation, which are described hereafter.  

\subsection{Implementation}
The framework is in many respects exactly like an ordinary molecule.
It should be defined to exactly fill the MD cell so that the
periodic repeat of the cell generates the periodicity of its
crystal structure.  The distinctive features of a framework molecule are:
\begin{itemize}
\item The framework is fixed in space and is not allowed to rotate. 
Any rotation would of course destroy the 3D structure.  For most
purposes it is convenient to regard the framework as being at rest, hence
translational motion is forbidden also.
\item No interactions between sites on a framework molecule and on itself
or any of its periodic images are evaluated.  That is,
framework-framework interactions, both potentials and forces, are
systematically excluded from the real-space and reciprocal-space parts
of the Ewald sum, including the point and intra-molecular self terms
of equation~\ref{eqn:ewald}. (The exact modifications to
equations~\ref{eqn:ewald},\ref{eqn:ewald-force} \etc\ are left as an
exercise for the reader.)
\item In the real-space force calculation, sites are treated as being
independent atoms rather than belonging to a molecule.  In particular
the cutoff is applied on the basis of the (framework) site to (fluid)
molecule distance.  By virtue of the ``all-image'' convention, all the
requisite molecule-framework interactions are correctly
included.  When assigning sites to sub-cells, each site is therefore
placed in the sub-cell which contains its co-ordinate.  (By contrast
sites belonging to an ordinary molecule are placed in the cell which
contains the molecular centre of mass.)
\end{itemize}

With these modifications, \moldy\ is able to successfully model a fluid
in contact with a 3D rigid framework. 2-dimensional systems such as a
fluid at a surface or between two surfaces may be represented as a 3D
system by adding an artificial periodicity in the third dimension. To
reduce the errors so introduced, the framework can be made ``large
with space inside'' to fill a MD cell with a large $c$-axis.  

In the case of a 3D framework it is clearly not sensible to allow the
MD cell to change shape or size, since this would destroy the internal
structure of the framework.  However if the framework represents a 2D
layer or surface, then the layer spacing may be allowed to vary using
the constant-stress equations (section~\ref{sec:const-stress}) and
applying constraints to allow only the $c$-axis to fluctuate.  In that
case, bear in mind that the dynamics are governed by the
Parrinello-Rahman equations of motion for the cell, rather than the
Newtonian equations for the layer.  In particular the mass is given by
the parameter $W$ rather than the mass of the framework molecule.
(These may, of course be set equal if required.)  Note also that no
layer-layer interactions are calculated, and any force is the result
of the externally applied pressure.\footnote{This is a restriction of
  the current implementation and may be lifted in future versions.}

Finally there are two subtle complications which arise from the
framework concept.

\subsection{Stress and Pressure Undefined}
There is no unique definition of the internal stress or pressure of a
framework system.    The pressure of a system in a space with periodic
boundary conditions  is defined in terms of the molecular virial
\begin{equation}
\label{eqn:virial}
{\mathcal W} = \frac{1}{3} \sum_{i=1}^N \sum_{j \neq i}^N \bm{R}_{ij}
\cdot \bm{F}_{ij}
\end{equation}
But the framework has no centre-of-mass, and so the quantity
$\bm{R}_{ij}$ can not be defined.  The site-virial formulation of
equation~\ref{eqn:stress-sr} is of no assistance as the definition of the
``internal'' co-ordinate $\bm{p}_{i\alpha}$ involves the centre of
mass co-ordinate $\bm{R}_i$.  Neither can one simply choose a
convenient reference $\bm{R}_i$.  Since the force exerted by the fluid
acting on the framework is in general non-zero, the term
\begin{equation}
\sum_i \sum_\alpha \bm{p}_{i\alpha} \bm{f}_{i\alpha}
= \sum_i \sum_\alpha \bm{r}_{i\alpha} \bm{f}_{i\alpha}
- \sum_i \bm{R}_i \bm{F}_i
\end{equation}
clearly depends on $\bm{R}_i$.  The situation may also be viewed
physically.  The definition of pressure of a system is the derivative
of the free energy with respect to volume.  But with an infinite rigid
framework the volume derivative can not be defined.

The useful quantity in this case is the partial pressure of the
fluid.  Though not currently implemented, this may be rectified in a
future version of \moldy.

Finally we note that in the case of a 2D layer structure, which is
\emph{not} rigid in the third dimension the perpendicular component if
the stress tensor \emph{does} have a physical meaning and is correctly
calculated. 
 
\subsection{Charged Frameworks}
A minor complication arises when using a framework which has a
non-zero net electric charge.  Although the system as a whole may be
electrically neutral, the omission of  framework-framework
interactions from the calculation also means that the
$\bm{k}=0$ term does not vanish.  To see this examine
equation~\ref{eqn:ewald}.  In the non-framework case the indices $i$
and $j$ in the terms
\begin{displaymath}
\left | \sum_{i=1}^{N} q_i \cos(\bm{k \cdot r}_i) \right |^2 
  = \sum_{i=1}^{N} \sum_{j=1}^{N} q_i q_j 
    \cos(\bm{k \cdot r}_i)\cos(\bm{k \cdot r}_j) 
\end{displaymath}
run over all site pairs.  If $\bm{k}=0$
the squared sum is 
\begin{displaymath}
\left | \sum_{i=1}^{N} q_i \right |^2 = 0
\end{displaymath}
If a framework is present the formulation becomes
\begin{displaymath}
\left | \sum_{i=1}^{N} q_i \cos(\bm{k \cdot r}_i) \right |^2 
-
\left | \sum_{i=M\!+\!1}^{N} q_i \cos(\bm{k \cdot r}_i) \right |^2 
\end{displaymath}
assuming sites $M+1 \ldots N$ are the framework sites.  On setting
$\bm{k}=0$ this reduces to
\begin{displaymath}
\left | \sum_{i=1}^N q_i \right |^2 - \left | \sum_{i=M\!+\!1}^N q_i \right |^2
\end{displaymath}
It is therefore necessary to modify the charged-system term of
equation~\ref{eqn:ewald} to
\begin{equation}
U_z = - \frac{1}{8 \epsilon_0 V \alpha^2}
\left \lbrace
\left | \sum_{i=1}^N q_i \right |^2 - \left | \sum_{i=M\!+\!1}^N q_i \right |^2
\right \rbrace
\end{equation}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Running Moldy}  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

The way \moldy\ is invoked depends to some extent on the operating
system, but usually by issuing the command \Lit{moldy}.\footnote{On
  VMS, \Lit{moldy} may be defined as a foreign command by \Lit{\$
    moldy :== \$mydisk:[mydir]moldy}} For Unix(tm) Windows 95 and NT
and MS-DOS the executable file \Fname{moldy} or \Fname{MOLDY.EXE}
should be placed in the shell search path (\eg\ in the current
directory). There is no GUI and \moldy\ should always be run from a
command line, from a terminal window under Unix or a MS-DOS window
under Windows 95/NT.  There are two optional arguments - the name of
the control file (see section~\ref{sec:control}) and the output file
(see section~\ref{sec:output}).  If either is omitted, control input
is read from the ``standard input'' which may be a terminal or a job
command file depending on the operating system and circumstances, and
the output is written to ``standard output'' which may be a terminal
or batch job logfile.\footnote{Some operating systems (Unix and
  MS-DOS) allow \emph{file redirection} whereby the standard input is
  associated with some file.  This may also be used to supply the
  control file, provided that no command line parameter is given.}
Here are examples for VAX/VMS and Unix (tm), which assume that in each
case the command has been set up to invoke \moldy.  Under VMS the
commands
\begin{verbatim}
        $ moldy control.dat output.lis
        $ moldy control.dat
\end{verbatim}
will start \moldy\  which will read its input from \Fname{control.dat}.
The output will be directed to the file \Fname{output.lis} in the first
case and written to the terminal or batch log in the second.  Under
UNIX any of
\begin{verbatim}
        % moldy < control > output.lis
        % moldy control output.lis
        % moldy control
\end{verbatim}
will cause moldy to read from the file called \Fname{control} and in
the first two examples to write its output to \Fname{output.lis}.  The
command-line interface for Windows and MS-DOS is very similar. 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{The Control File}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\label{sec:control}

The information needed to initiate and control a run of \moldy\ is
specified in a file known as the \emph{control file}. This contains
the parameters governing the run \eg\ the number of timesteps to be
executed or the frequency of output, and the names of files to be used
\eg\ for reading a restart configuration from or for writing the
output to. Parameters in the control file are specified by entries of
the form \Lit{keyword~=~value} which appear one to a line,
terminated by the special keyword \Lit{end}.  Spaces and blank
lines are ignored as are comments (\ie\ the remainder of a line
following a \Lit{\#}~symbol) and keywords may be entered in upper
or lower case. For example
\begin{verbatim}
   title= Moldy example      # This is a comment

   # The above blank line is ignored
   nsteps = 1000
   step=0.0005
   restart-file = RESTART.DAT
   end                       # The control file ends here
\end{verbatim}
sets the title of the simulation to ``Moldy example'', the number of
steps to execute to 1000, the timestep to 0.0005ps and supplies the
name of a restart file to read from.

It is not necessary to specify all of the parameters on each run.
Unless it is explicitly assigned a value in the control file, each
parameter has a default value.  This is either the value listed in
table~\ref{tab:parameters} or, in the case where the simulation is
continuing from a restart file, the value it had on the previous run
(see section~\ref{sec:restarting}). Parameters are read in sequence
from the control file and if one appears more than once only the final
instance is used.

The two most important parameters are \Lit{step} which sets the
size of the simulation timestep (in ps), and \Lit{nsteps} which
specifies the number of steps to perform.  Together these control the
length of time to be simulated.  It is also possible to specify that a
run should be terminated after a certain amount of computer time has
been used - given by parameter \Lit{cpu-limit}.  This will be
particularly useful in batch mode systems, where the run is killed
after some specified CPU time has elapsed.  Setting \Lit{cpu-limit}
to the maximum time allowed will cause \moldy\ to terminate the run
\emph{before} the limit is reached and write out a backup file (see
section~\ref{sec:backup}).

There are several kinds of parameters:
\begin{description}

\item[character strings] Apart from \Lit{title} these are just file
names \eg\ \Lit{sys-spec-file}.  No checks
are performed on the validity of the name (because \moldy\  has to work
on many different computer systems), so if you make a mistake you are
likely to get an error message to the effect that \moldy\  couldn't find
the file you asked for.  To remove a default value, just specify a
null string \eg\ \Lit{save-file = }.

\item[booleans] These are just switches which turn a feature off or
on. `0' means off or false and `1' means on or true. The parameters
\Lit{text-mode-save}, \Lit{new-sys-spec}, 
\Lit{surface-dipole} and \Lit{lattice-start} are booleans.

\item[real parameters] Several parameters are real numbers \eg\
\Lit{step} which specifies the timestep.  They may be entered in the
usual floating point or scientific notation \eg\ \Lit{step~=~0.0005}
or \Lit{step~=~.5e-3}, and are taken to be in the units given in
table~\ref{tab:parameters}.

\item[integer parameters] Parameters such as \Lit{dump-level} take a
numeric value, which should be an integer.

\item[timestep-related parameters] Several parameters govern when some
calculation begins and ends and how frequently it is performed in
between. These are known as ``begin'', ``end'' and ``interval''
parameters, but are really a special case of integer parameters.  For
example \Lit{begin-average}, \Lit{dump-interval} and
\Lit{scale-end}.  The calculation begins \emph{on} the timestep
specified on the \Lit{begin} parameter, occurs every \Lit{interval}
timesteps thereafter and ends \emph{after} the timestep specified by
the \Lit{end} parameter.  Setting the \Lit{interval} parameter to
zero is the usual method of turning that calculation off.

The \Lit{begin} and \Lit{end} parameters behave in a special fashion
when the simulation is continued from a restart file; they are
interpreted \emph{relative} to the current timestep.  Notice especially
that \Lit{nsteps}, the number of timesteps is treated in this way.

\end{description}

A complete list of the parameters,
their meanings and default values appears in
table~\ref{tab:parameters}. 

\begin{table}
\begin{minipage}{\textwidth}
\caption{Control Parameters}
\label{tab:parameters}
\setlength{\rightskip}{0pt plus 2cm}
\begin{tabular}{|l|l|l|>{\saferagged}p{2.9in}|}
\hline
\textbf{name} & \textbf{type}\footnote{See section~\ref{sec:control}} &
\textbf{default} & \textbf{function} \\ \hline \hline
\Lit{title} &                  character  & {\small Test Simulation} &
A title to be printed on all output. \\
\Lit{nsteps} &                 integer &                       0 &
Number of MD steps to execute. \\
\Lit{cpu-limit} &              real &                  1e20 &
Terminate run if excessive CPU time used. \\
\Lit{step} &                   real &                  0.005 &
Size of timestep \\ \hline
\Lit{sys-spec-file} &          character  &    null &
Name of system specification file.  Appended to control file if null. \\
\Lit{lattice-start} &          boolean &               false  &
Switch for crystalline initial configuration. \\
\Lit{save-file} &              character  &    null &
File to save restart configuration in. \\
\Lit{restart-file} &           character  &    null &
File to read restart configuration from. \\
\Lit{new-sys-spec} &           boolean &               false  &
Read restart configuration with changed system specification. \\
\Lit{text-mode-save} &         boolean &               false &
Write a portable ``restart'' file consisting of control, system
specification and lattice start files. \\ \hline
\Lit{density} &                real &                  1.0 &
Initial density in g\,cm$^{-3}$.  Used by \emph{skew start} only to
determine initial MD cell dimensions. \\ \hline
\Lit{scale-interval} &         integer &               10 &
Number of steps between velocity scalings. \\
\Lit{scale-end} &              integer &               1000000 &
When to stop scaling. \\
\Lit{const-temp} &             integer &               0 &
1 for Nos\'e-Hoover, 2 for Gaussian thermostat. \\
\Lit{ttmass} &                 real &                  100 &
Translational inertia parameter for Nos\'e-Hoover thermostat 
(kJ\,mol$^{-1}$\,ps$^2$).\\
\Lit{rtmass} &                 real &                  100 &
Rotational inertia parameter for Nos\'e-Hoover thermostat 
(kJ\,mol$^{-1}$\,ps$^2$).\\
\Lit{scale-options} &           integer &              0  &
Select variations on scaling or thermostat. \\
\Lit{temperature} &            real &                  0 &
Temperature of initial configuration for scaling and thermostat (K). \\ \hline
\Lit{const-pressure} &         boolean &               false  &
Whether to use Parrinello and Rahman constant stress. \\
\Lit{w} &                      real &                  100.0 &
Value of P \& R mass parameter (amu). \\
\Lit{pressure} &               real &                  0 &
External applied pressure (MPa). \\
\Lit{strain-mask} &            integer &               200 &
Bitmask controlling $\bm{h}$ matrix constraint. \\ \hline
\Lit{alpha} &                  real &                  \emph{auto} &
$\alpha$ parameter for Ewald sum. \\
\Lit{k-cutoff} &               real &                  \emph{auto} &
Reciprocal space cut off distance in \AA$^{-1}$. \\
\Lit{cutoff} &                 real &                  \emph{auto} &
Direct space cutoff distance in \AA. \\
\Lit{strict-cutoff} &          boolean &               false &
Flag to select rigorous or cheap but approximate cutoff algorithm. \\
\Lit{surface-dipole} & boolean &               false  &
Include De Leeuw \& Perram term in Ewald sum. \\ \hline
\Lit{roll-interval} &          integer &               10 &
Period over which to calculate rolling averages. \\
\Lit{print-interval} &         integer &               10 &
How frequently to print normal output. \\ \hline
\end{tabular}
\end{minipage}
\end{table}
\begin{table}
\begin{minipage}{\textwidth}
\caption{Control Parameters (continued)}
\setlength{\rightskip}{0pt plus 2cm}
\begin{tabular}{|l|l|l|>{\saferagged}p{2.9in}|}
\hline
\textbf{name} & \textbf{type}\footnote{See section~\ref{sec:control}} &
\textbf{default} & \textbf{function} \\ \hline \hline
\Lit{begin-average} &          integer &               1001 &
When to start accumulating the thermodynamic averages. \\
\Lit{average-interval} &       integer &               5000 &
How frequently to calculate and print averages. \\
\Lit{reset-averages} &         boolean &               false  &
Discard accumulated averages in restart file. \\ \hline
\Lit{begin-rdf} &              integer &               1000000 &
When to start accumulating radial distribution function information. \\
\Lit{rdf-interval} &           integer &               20 &
How frequently binning calculation is performed. \\
\Lit{rdf-out} &                integer &               5000 &
How frequently to calculate and print RDFs. \\
\Lit{rdf-limit} &              real &                  10 &
Calculate RDFs up to what distance? (\AA) \\
\Lit{nbins} &                  integer &               100 &
Number of binning intervals between 0 and rdf-limit. \\ \hline
\Lit{xdr} &                    boolean &               true &
Write restart,  backup and dump files in portable
binary format using Sun XDR. \\ \hline
\Lit{dump-file} &              character  &    null &
Template of file names used for data dumps. \\
\Lit{begin-dump} &             integer &               1 &
Timestep to begin dumping at. \\
\Lit{dump-interval} &          integer &               20 &
How frequently to perform dumps. \\
\Lit{dump-level} &             integer &               0 &
Amount of information to include in dump. \\
\Lit{ndumps} &         integer &               250 &
Number of dump records in each dump file. \\ \hline
\Lit{backup-interval} &        integer &               500 &
Frequency to write backup file. \\
\Lit{backup-file} &            character  &    \Fname{MDBACKUP} &
Name of backup file. \\ \hline
\Lit{temp-file} &              character  &    \Fname{MDTEMPX} &
Name of temporary file used for writing restart configurations. \\ \hline
\Lit{subcell} &                real &                  0 &
Size of sub-cell (in \AA) to divide MD cell into for link cell force
calculation. \\ \hline
\Lit{seed} &                   integer &               1234567 &
Seed for random number generator. \\ \hline
\Lit{page-width} &             integer &               132 & 
Number of columns on output paper. \\
\Lit{page-length} &    integer &               44 &
Number of lines on a page of output. \\ \hline
\Lit{mass-unit} &              real &                  1.6605402e-27 &
Unit of mass for system specification file. \\
\Lit{length-unit} &            real &                  1e-10 &
Unit of length for system specification file. \\
\Lit{time-unit} &              real &                  1e-13 &
Unit of time for system specification file. \\
\Lit{charge-unit} &            real &                  1.60217733e-19 &
Unit of charge for system specification file. \\ \hline
\end{tabular}
\end{minipage}
\end{table}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Setting up the System}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{System Specification}
\label{sec:sys-spec}

The information which describes to \moldy\  the system to be simulated
and the interaction potentials is contained in a file known as the
\emph{system specification file}.  This may be presented to \moldy\  in
either of two ways: If the control file parameter \Lit{sys-spec-file}
is null or absent, it is assumed to be appended to the end of the
control file.  Otherwise it is read from the file whose name is
the value of \Lit{sys-spec-file}.

This file is divided into two sections.  First is the description of
the molecules, atoms or ions, which is followed by the potential
functions.  As for the control file, the input is case independent and
free format, but line structured. Blank lines, spacing and comments
are ignored.

The physical description consists of a series of entries, one for each
molecular species, terminated by the keyword \Lit{end}. The entry
for species $i$ should have the form
\begin{displaymath}
\begin{array}{lllllll}
\multicolumn{7}{l}{\textit{species-name}_{i} \quad N_{i}}  \\
id_{1} & x_{1} & y_{1} & z_{1}&  m_{1} & q_{1} & name_{1} \\
id_{2} & x_{2} & y_{2} & z_{2}&  m_{2} & q_{2} & name_{2} \\
\vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots \\
id_{n_{i}} & x_{n_{i}} & y_{n_{i}} & z_{n_{i}}& 
m_{n_{i}} & q_{n_{i}} & name_{n_{i}} \\
\end{array}
\end{displaymath}
where \textit{species-name}$_{i}$ is the name of the molecule and $N_{i}$
is the number of molecules of that type in the system. Each molecule
has $n_{i}$ atoms, one for each line in that group and each kind of
atom is identified by a number $id_{i}$ (the site id) which will be
used to specify the appropriate potential parameters. Its co-ordinates
are $(x_{i},y_{i},z_{i})$, its mass is $m_{i}$, its charge is $q_{i}$
and its name is $name_{i}$.  See Appendix~\ref{sec:examples} for some
sample system specification files.

If there is more than one atom of any type (in the system - not just
the same molecule) it is sufficient to identify it by its $id$ (and
the site co-ordinates!).  If $m_{i}$, $q_{i}$ or $name_{i}$ \emph{are}
given they must agree exactly with the previous values or an error
will be signalled.

Site ids, masses and charges are all checked for `reasonableness'
and impossible values cause an error. The set of site ids does not
have to start at 1 or be contiguous, but since this may indicate a
mistake, a warning is issued.

Following the physical specification is the specification of the
potential functions. This takes the form
\begin{displaymath}
\begin{array}{llllll}
\multicolumn{4}{l}{\textit{potential-type}} \\
i & j & p^{1}_{ij} & p^{2}_{ij} & \ldots & p^{r}_{ij}  \\
k & l & p^{1}_{kl} & p^{2}_{kl} & \ldots & p^{r}_{kl}  \\
\vdots & \vdots & \vdots & \vdots & \vdots & \vdots \\
m & n & p^{1}_{mn} & p^{2}_{mn} & \ldots & p^{r}_{mn}  \\
\Lit{end} \\
\end{array}
\end{displaymath}
where \emph{potential-type} is one of the keywords
\Lit{Lennard-Jones}, \Lit{Buckingham} or \Lit{MCY} or
\Lit{generic} to identify the kind of potentials to be used, $i, j,
k, l, m, n$ are site ids and $p^{\alpha}_{ij}$ is the $\alpha^{th}$
potential parameter between sites $i$ and $j$.  There should be one
line for each distinct pair of site ids.  If any pair is omitted a
warning is issued and the parameter values are set to zero.

The meaning of the parameters for the currently defined potentials is
as follows:
\begin{Litdescription}
\item[Lennard-Jones] The potential is 
\[\phi(r_{ij}) = \epsilon((\sigma/r_{ij})^{12} -
(\sigma/r_{ij})^{6}),\] and has two parameters, $\epsilon ( \equiv p^{1}_{ij})$
and $\sigma ( \equiv p^{2}_{ij})$, which occur on each line in that
order.  Note that the definition of $\epsilon$ \emph{includes} the
factor of 4 more usually separated out.  The control parameter
\Lit{time-unit} may be divided by four to read potentials
specified in the standard form.

\item[Buckingham] This includes potentials of the Born-Mayer type and
has formula \[\phi(r_{ij}) = -A_{ij}/r^{6}_{ij} + B_{ij}\exp(-C_{ij}r_{ij}).\]
The three parameters appear on each line in the order $A, B, C$.
\item[MCY] This type supports potentials of the same form as the water
model of Matsuoka, Clementi and Yoshimine\cite{matsuoka:75}, 
\[\phi(r_{ij}) = A_{ij}\exp(-B_{ij}r_{ij}) - C_{ij}\exp(-D_{ij}r_{ij}),\]
and the four parameters appear on the line in the order $A, B, C, D$.
\item[generic] This contains a number of inverse power terms and
  an exponential repulsion in support of ionic solution models.
  It takes the form
\[\phi(r_{ij}) = A_{ij} \exp(-B_{ij}r_{ij}) + C_{ij}/r_{ij}^{12} -
  D_{ij}/r_{ij}^4 -E_{ij}/r_{ij}^6 -F_{ij}/r_{ij}^8\]
with the six parameters $A_{ij}$ -- $F_{ij}$.
\end{Litdescription}
\noindent
Other types of potential may be easily added: see section~\ref{sec:newpot}.

It is possible to specify the units in which these quantities are given
by means of the control file parameters \Lit{mass-unit},
\Lit{length-unit}, \Lit{time-unit} and \Lit{charge-unit} 
(which are themselves specified in SI units). All quantities read from
the system specification file (dimensions as well as potentials) are
taken to be in those units. Their default values are amu, \AA, 0.1ps
and $q_{e}$, which means that the unit of energy is kJ mol$^{-1}$.  So
to read in \AA, amu and kcal mol$^{-1}$, specify
\Lit{time-unit=4.8888213e-14}.

Once the system specification has been read in, all quantities are
converted to `internal' units: a.m.u., \AA, ps, and $\insqrt (
\textnormal{a.m.u. \AA}^{3} \textnormal{ps}^{-2}/(4 \pi \epsilon_{0}) )$.
The prototype molecule for each species is then shifted so that its
zero of coordinates lies on its centre of mass, and rotated into the
principal frame (polyatomics only).

\subsection{The Initial Configuration}

\moldy\  provides two methods of setting up an initial configuration.
By default the \emph{skew start} method of section~\ref{sec:skewstart}
is used to place the molecular centres of mass in a regular
arrangement which ensures molecular separation.  If there is more than
one species present, molecules of each are chosen randomly for each
site.  Molecular orientations are chosen randomly from a uniform
distribution.  This method has been found to work well for reasonably
small or fairly isotropic molecules and it is anticipated that it will
be the usual method of starting a simulation of the liquid state.   On
the other hand, if the constituent molecules are sufficiently large
and irregular, or if it is intended to simulate the solid state then
the \emph{lattice start} method will be more appropriate.

This method is activated by setting the control parameter
\Lit{lattice-start} to 1, and creates the initial configuration by
periodic replication of some crystalline unit cell.  In that case
\moldy\  expects to find, following the \Lit{end} which terminates the
system specification, an initial configuration specification of the
following form:
\begin{displaymath}
\begin{array}{llllllll}
\multicolumn{8}{l}{a \quad b \quad c \quad \alpha \quad \beta \quad
  \gamma \quad n_{x} \quad n_{y} \quad n_{z}} \\ 
\textit{species-name}_{1} &
X_{1} & Y_{1} & Z_{1} &  q_{10} & q_{11} & q_{12} & q_{13} \\
\vdots &  \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots \\
\textit{species-name}_{i} & X_{i} & Y_{i} & Z_{i} \\
\vdots &  \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots \\
\textit{species-name}_{n} &
X_{n} & Y_{n} & Z_{n} &  q_{n0} & q_{n1} & q_{n2} & q_{n3}  \\
\Lit{end}. \\
\end{array}
\end{displaymath}
Here $a, b, c$ and $\alpha, \beta, \gamma$ are the crystal unit cell
parameters, and $n_{x}, n_{y}, n_{z}$ are the number of unit cells in
each direction which comprise the MD cell.  The next $n$ lines
describe the $n$ molecules of the basis which will be replicated to
form the full configuration.  Molecules may appear in any order, but
of course the total number of each, multiplied by the number of unit
cells $n_{x} n_{y} n_{z}$ must agree with that given in the system
specification file.

Each molecule is identified by its name, as given in the system
specification file.  $X, Y$ and $Z$ are \emph{fractional}
co-ordinates, between 0 and 1 giving the location of the molecular
centres of mass in the crystal unit cell. The orientation is given by
the four quaternions $q_{0}, q_{1}, q_{2}, q_{3}$ which specify a
rotation about the centre-of-mass \emph{relative to the orientation of
  the prototype molecule in the system specification file}.  (Notice
the slight inconsistency with the positions, which are of the centres
of mass, \emph{not} the zeroes of co-ordinates in the system
specification file. This may be fixed in future releases.)
Quaternions need only be included for polyatomic species, that is
molecules $1$ and $n$ above, and omitted for the monatomic species
$i$.

After the molecular positions and orientations have been set up, their
velocities (and angular velocities if appropriate) are initialized.
Their values are sampled at random from the
Maxwell-Boltzmann distribution for the temperature $T$, as given by
the control parameter \Lit{temperature}.  This is done for both
starting methods.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Restarting from a Previous Run}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\label{sec:restarting}

At the end of a simulation run, it is often desirable to store the
configuration of the system in a file.  This \emph{restart file} may be
used at a later date to continue the simulation from that point rather
than from scratch.  To instruct \moldy\  to write a restart file, simply
set the control parameter \Lit{save-file} to a suitable filename; to
start from a restart file, set \Lit{restart-file} to be the name of
that file.

Each restart file is a binary file which contains enough information
to reconstruct the exact state of the system and of the program.  It
includes a copy of all the control parameters in force, the current
timestep number, a complete system specification, all the simulation
dynamic variables and the intermediate data used in the calculation of
averages and radial distribution functions.  Thus a run continued from
a restart file will proceed just as if there had been no interruption
and will generate identical results (provided the control parameters
are not changed).

When continuing a simulation, it is only necessary to explicitly
specify control parameters which are to be changed.  Their previous
values are read from the restart file and are used as defaults when
reading the control file. Consequently control files for restarting
tend to be rather short. \Emph{Caution}: always include a new
(possibly null) value for \Lit{save-file}. Otherwise when the new run
terminates, the new restart file may overwrite the old
one.\footnote{Whether the old file is lost depends on the operating
  system. Under systems such as VMS which have version numbers a new
  version is created and the old one remains. Under Unix, the old file
  is renamed by the addition of a ``\%'' character and thus is saved.
  On other systems it will be lost.}

Neither is it necessary to repeat the system specification since that
too is stored in the restart file.  However there are occasions
when it is desirable to do just that, for example if the value of one
of the potential parameters is to be modified.  In that case, set the
switch \Lit{new-sys-spec} to 1 (true) and provide a system
specification as per a new simulation.  This is checked for
consistency with the existing one and if correct replaces it.  The
following checks are applied, which only verify that it is sensible to
assign the old dynamic variables to the new system. \emph{1}. The
number of species must be the same. \emph{2}. Each species must have
the same number of rotational degrees of freedom as its predecessor.
It is not possible to replace a polyatomic by a monatomic or
linear molecule, for example.  \emph{3}. The number of molecules of
each species must not change.  This means that the order in the
specification file must be identical too.  It is however possible to
change the number of sites on a molecule, subject to \emph{2}.

\subsection{Periodic Backup}
\label{sec:backup}
Closely related to restarting is the backup mechanism.  This is
provided to guard against the complete loss of a simulation due to
computer failure. Periodically during a run, \moldy\ writes its state
to a \emph{backup file} -- which is in fact just a restart file.  In
the event of a crash, the simulation can be restarted from the point
the last backup was written rather than from the beginning. The
related control parameters are \Lit{backup-file} which specifies the
file name and \Lit{backup-interval} which gives the frequency of
backups.  It should not normally be necessary to change the name, but
the optimum interval will depend on the size of the simulated system
and the speed of the computer. By default it is 500.  At the
successful end of a run the backup file is deleted so that only if
there is an abnormal termination does one remain.\footnote{A backup
  file is also written if the run is terminated for exceeding its cpu
  limit.}

The restart from a backup is entirely automatic. If a backup file
exists when a run is started, it is read in and the run continues from
it.  In contrast to a normal restart all of the control parameters are
taken from the backup file and the control file (and a restart file if
one is specified) is ignored.\footnote{This is not quite true. \moldy\ 
  does read the control file and any restart file but only to
  determine the name of the backup file. Thus even if the backup has a
  non-standard name it can still be found.} In consequence, if a run
is aborted or stops abnormally for some reason, the backup file must
be removed manually otherwise next time a run starts, the unwanted
simulation will continue instead.

If a run terminates abnormally there may also be a \emph{lock file}
called \Fname{MDBACKUP.lck} which ought to be removed.  \moldy\
attempts to prevent two runs from overwriting each other's backup
files by creating a lock file whose name is formed from the backup
name by appending \Fname{.lck}.  A second run which attempts to use the
same backup file will test for the presence of the lock file and abort
the run if it finds it.

A restart or backup file is created by first writing the data to a
temporary file which is then renamed to the final name.  This ensures
that there is no possibility of a file being left incomplete or
corrupt if the computer crashes part-way through the write.  If the
file already exists either it is replaced (on systems which only keep
one version of a file) or a new version is created (on systems such as
VMS which retain multiple versions).  In the unlikely event of it
being necessary to change where the temporary file is
kept,\footnote{This may be necessary if the restart file is located on
  a different device or disk partition from the current directory. To
  rename the temporary file successfully, it must reside in the same
  partition or device as the restart file.} it may be specified with
the control parameter \Lit{temp-file}.

\subsection{Portable Restart Files}
\label{sec:xdr}
\moldy\ is able\footnote{From version 2.1 onwards} to read and write
restart and dump files in a portable binary format which is
transportable between computers of different architectures.  So a
restart file written on, for example, a Sun may be used to initiate a
new run on a Cray, and the dump files generated on the Cray may be
analyzed on the Sun.  This feature will also be of considerable use in
modern heterogeneous networks where diverse machines frequently share
a common file space.

The format is based on Sun Microsystems XDR protocol\cite{sunxdr}.
The XDR routines are available on almost every modern Unix machine,
and are simple enough to implement on any other
system.\footnote{Because the XDR calls are not part of ANSI standard
  C, however, the XDR code is conditionally compiled into \moldy\ only
  if the \Lit{USE\_XDR} preprocessor symbol is defined during
  compilation.} If the control parameter \Lit{xdr} is set to 1 then
all files will be written using this format. \moldy\ automatically
determines whether a restart file was written using XDR (by examining
the file header) and reads it in the appropriate fashion irrespective
of the value of \Lit{xdr}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Setting the Temperature}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\moldy\ implements three different methods to control the temperature
of the simulated system. These are the velocity rescaling technique
described in section~\ref{sec:rescaling}, the Nos\'e-Hoover thermostat
and constrained equations of motion (section~\ref{sec:const-temp}).
Scaling is selected by the parameters \Lit{scale-interval} \etc\ %
Every \Lit{scale-interval} timesteps until \Lit{scale-end}, the
velocities are adjusted so that the kinetic energy corresponds exactly
to the desired temperature (the value of control parameter
\Lit{temperature}).  The Nos\'e-Hoover and constrained thermostat
are selected by setting \Lit{const-temp} equal to \Lit{1} or
\Lit{2} respectively.

The control parameter \Lit{scale-options} selects refinements to
the basic scaling or thermostat algorithms. This is an integer
parameter interpreted as a set of bit flags with the following meanings.
\begin{description}
\item[bit 0]    perform scaling or thermostatting for each molecular species individually.
\item[bit 1]    scale/thermostat the rotational and translational components of
the kinetic energy separately.
\item[bit 2]    use the rolling averages of kinetic energy to
calculate the scale factor rather than the instantaneous values.
\item[bit 3]    discard all existing velocities and accelerations and
re-initialize from the Maxwell-Boltzmann distribution.
\end{description}
The bits may be set in any combination so, for example
\Lit{scale-options=6} sets bits 1 and 2 ($ 6 = 2^1 + 2^2$) and scales
separately for rotation/translation using the rolling averages.  If
bit 3 is set the others are ignored.  Only bits 0 and 1 have any
meaning in the case of a thermostat, and signify that each species, or
the translational and rotational degrees of freedom are isolated from
each other and coupled to their own, individual heat baths.

The options for scaling separately rotation and translation, and per
species may be useful for achieving equilibration in ``difficult''
systems where mode-coupling is ineffective.  In those situations it is
otherwise possible for all the energy to be transferred into the
rotational modes of a particular species, halting any progress to
equilibrium for other degrees of freedom.   These options ensure that
all degrees of freedom have some thermal energy.

The option controlled by bit 3, to discard all existing information
and start from a random set of velocities may be of use when starting
from far-from-equilibrium situations.  In such cases the forces are
frequently so large that the velocities and accelerations exceed the
limits of the integration algorithm and timestep, which results in
\moldy\ stopping with a \emph{quaternion normalization} or
\emph{quaternion constraint} error.  Judicious use of this option
every few timesteps (using \Lit{scale-interval}) ought to allow the
system to relax to a state sufficiently close to equilibrium for
normal scaling to take over.

Bit 2 is intended to deal with the problem of setting the temperature
accurately using scaling.  The \emph{ensemble average} kinetic energy
which characterizes the temperature of the system and the
instantaneous value fluctuates about this value.  However in the
traditional implementation of scaling, velocities are multiplied by a
factor of $\sqrt{\emph{desired KE} / {\emph{instantaneous KE}}}$.
Thus the scaling factor is ``wrong'' by the ratio of the instantaneous
to average KE's which means that the temperature can not be set more
accurately than the relative size of the fluctuations in the KE\@.
The option selected by bit 2 goes some way towards the ideal scaling
factor by using the rolling average KE instead of the instantaneous
value.  The fluctuations in this short-term average should be much
lower than in the instantaneous value, allowing more accurate
temperature control.  However it will almost always be easier to use a
true thermostat to achieve this goal.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Output}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\label{sec:output}
At the beginning of each run \moldy\ writes a \emph{banner page}
containing a summary of the system being simulated and details of the
important control parameters. The bulk of the output file is the
\emph{periodic output} which contains the instantaneous values of
various thermodynamic variables, their rolling averages and associated
standard deviations. The \emph{rolling average} is just the mean over
the preceding $n$ timesteps where $n$ is set by the control parameter
\Lit{roll-interval}.  An annotated example is given in
figure~\ref{fig:output}.  The frequency of periodic output may be
altered by setting the control parameter \Lit{print-interval} to
the interval required.  (This may be necessary to constrain the size
of the output file which can grow to be very large indeed with the
default interval of only 10.)

\begin{figure}
\tiny
%
\begin{tabular}{rrrrrrrr
@{\hspace{1em}}r@{\hspace{1em}}rr@{\hspace{1em}}r@{\hspace{1em}}r}
\multicolumn{6}{r}{\hfill Nov 17 15:13:06 1989 \hfill Water\_test
\hfill Page 4} & & & & & & &\\
Trans KE & Rot KE & Pot Energy & Tot Energy & TTemp & RTemp & Temp &
h(1,*) & h(2,*) & h(3,*) & Stress & Stress & Stress \\
\multicolumn{13}{l}{======== Timestep 10      Current values
======================================================== } \\
243.88 & 453.88  & -187.35  & 533.5  & 305.5  & 568.6  & 424.4  &
12.53  & 0.00  & 0.00  & 589  & 46.4  & 120 \\
22.053  & 0  & 1.0401 & & 221.0  & 0.0 & & 0.00  & 12.53  & 0.00  & 46.4
& 373  & 90.1 \\
 &  &  &  &  & & &  0.00  & 0.00  & 12.53 & 120  & 90.1  & -207 \\
\multicolumn{13}{l}{-------- Rolling averages over last 10 timesteps
--------------------------------------------------------------------------
------------------------------------------------
} \\
240.27 & 319.31 & -82.472 & 533.39 & 301.0 & 400.0 & 342.9 & 12.53 &
0.00 & 0.00 & 1.2e+03 & 296 & 127 \\
22.077 & 0 & 34.205  & & 221.3 & 0.0 & & 0.00 & 12.53 & 0.00 & 296 &
589 & 133 \\
& & & & & & & 0.00 & 0.00 & 12.53 & 127 & 133 & -132 \\
\multicolumn{13}{l}{-------- Standard deviations
---------------------------------------------------------------------------------------------------------------------------------------------------
} \\
1.8214 & 71.893 & 56.441 &.19942 & 2.3 & 90.1 & 43.4 & 0.00 & 0.00 &
0.00 & 1.32e+03 & 750 & 51 \\
0.013 & 0 & 17.173 & & 0.1 & 0.0 & & 0.00 & 0.00 & 0.00 & 750 & 119 & 55.2 \\
 & & &  &  &  &  & 0.00  & 0.00 & 0.00 & 51 & 55.2 & 49 \\
\end{tabular}
\caption[Sample \moldy\  output.]{Sample \moldy\  output from a simulation 
of a two component mixture.  The first component is a polyatomic
molecule and the second is atomic.  There are three \emph{frames}, for
the instantaneous values, the rolling averages and their associated
standard deviations.  Within a frame, each row has the following
meaning: for translational and rotational kinetic energies and
temperatures it is the per-species value; for the potential energy it
is the direct and reciprocal space components, and the MD cell matrix,
\Lit{h} and the stress are laid out as $3\times 3$ matrices.}
\label{fig:output}
\end{figure}

As well as the short term rolling averages, long term averages are
calculated and printed out at regular but usually infrequent
intervals.  Accumulation starts on the timestep given by the control
parameter \Lit{begin-average} and every \Lit{average-interval}
timesteps thereafter, the means and standard deviations are calculated
and printed.  This output is interspersed with the periodic output and
is formatted with one variable to a line in the form \emph{mean +/-
  s.d}.. Where a variable has more than one component (such as
multiple species for the translational temperature or Cartesian
components for the mean square forces) the components are printed
across the page.\footnote{Remember that the standard deviation is a
  measure of the \emph{fluctuations} about the mean, \Emph{not} the
  \emph{uncertainty} in the mean. For that the standard error in the
  mean is required, which is more difficult to evaluate.
  Theoretically it is the \emph{s.d.} divided by $\sqrt N$ where $N$
  is the number of independent observations.  But successive timesteps
  are highly correlated and do not count as independent.  See
  ref~\cite{allen:87} section 6.4, page 191 onwards for a full
  discussion.}  In addition to those variables printed as part of the
periodic output, the pressure, the virial, mean square forces, mean
square torques and total dipole moments are calculated.

\subsection{Output units}
All of the various forms of output use the same units, though for
brevity they are not explicitly mentioned on the periodic output.
Lengths are measured in \AA, energies are all expressed in kJ
mol$^{-1}$, temperatures in Kelvin, pressure and stress in MPa, mean
square forces and torques in N$^2$ mol$^{-1}$ and Nm$^2$
mol$^{-1}$, charge in electron charges and dipole moments in Debye.
Because energy is an extensive quantity the printed values refer to
the \emph{whole system}.  (There is no practical way of expressing
energies per mole of any particular constituent in a program capable
of simulating arbitrary mixtures.)

If these units do not suit, they can be changed in the configuration
file \Fname{defs.h}, where the conversion from internal to output units
is parameterized.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Radial Distribution Functions}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Radial distribution functions are calculated by binning site pair
distances periodically throughout the simulation (see
section~\ref{sec:rdf}).  As this process is expensive in computer time
the binning subroutine is invoked only every few timesteps, as set by
the control parameter \Lit{rdf-interval} (20 by default).  Since
the pair distances only change a little on each timestep, very little
statistical information is lost.  Collection of binning data may also
be turned off during an equilibration period: specify when binning is
to start by means of the parameter \Lit{begin-rdf}.  The parameters
\Lit{rdf-limit} and \Lit{nbins} control the details of binning,
giving respectively the largest distance counted and the number of
bins that interval is divided into. The calculation of the interatomic
distances is done separately from that used in the evaluation of the
forces, using the same link cell scheme.  This ensures that all site
pairs separated by less than \Lit{rdf-limit} are included.  This
parameter may be varied independently of the interaction
cutoff, thereby allowing RDFs to be evaluated out to large distances
without incurring the time penalty of increasing the cutoff.%
\footnote{In previous versions of \moldy\ the calculation of the
  interatomic distances was done on the basis of the ``minimum image''
  convention.  Consequently the calculated value of
  $g_{\alpha\beta}(r)$ tailed off for $r_c > L/2$.  This restriction
  is now lifted}

Every \Lit{rdf-out} timesteps (by default 5000) the RDFs are
calculated from the binned distances and printed out, and the counters
are reset to zero to begin accumulation again.  Distances are binned
and RDFs $g_{\alpha \beta}(r)$ calculated separately for each distinct
type of atom-atom (or site-site) pair.  An explanation of the output
format is given in figure~\ref{fig:rdf-output}.  Note that each number
should be considered as the value at the \emph{centre} of its bin, so
that entry $i$ in each list is the value of
$g_{\alpha\beta}((i+1/2)b)$ where $b$ is the bin width.

\begin{figure}
\begin{tiny}
\begin{verbatim}
        Radial Distribution Functions   Bin width=0.1
        O-O RDF
 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000481 0.035710 0.183334 0.442186 0.613992 1.024402
 1.046396 0.964906 0.830174 0.660035 0.693341 0.615902 0.593192 0.510595 0.530697 0.532030 0.535959 0.524457 0.523221 0.466219
 0.496028 0.438487 0.456500 0.410547 0.443861 0.457956 0.446822 0.452202 0.419768 0.439333 0.465509 0.486887 0.461970 0.475745
 0.478883 0.480854 0.509090 0.533728 0.552747 0.552555 0.575402 0.547278 0.544836 0.493597 0.488168 0.520727 0.508073 0.479948
 0.501159 0.484000 0.485378 0.489160 0.464448 0.466791 0.476508 0.446576 0.470948 0.474468 0.449340 0.462169 0.501220 0.519107
 0.513338 0.510192 0.499766 0.525963 0.504663 0.517673 0.498359 0.512156 0.507061 0.466390 0.464342 0.445886 0.417555 0.407778
 0.387220 0.374041
        O-H RDF
 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 26.976688 0.000000 0.000000 0.000000 0.000000
 0.000000 0.016214 0.061257 0.304082 0.647342 0.847404 0.757188 0.601222 0.478273 0.462682 0.449614 0.450424 0.518998 0.572242
 0.689704 0.914269 1.184674 1.441772 1.570390 1.609068 1.600392 1.430457 1.322722 1.183606 1.103701 1.061788 0.980018 0.960570
 0.924390 0.908883 0.877591 0.857668 0.890761 0.852463 0.815447 0.824963 0.841255 0.890416 0.929030 0.960589 0.984145 1.020650
 1.028199 1.047496 1.064600 1.099812 1.095715 1.073793 1.078131 1.049212 1.052160 1.052001 1.020737 1.010782 0.979748 0.983158
 0.988946 0.967620 0.955655 0.944384 0.952145 0.948509 0.946692 0.960097 0.959299 0.964074 0.969219 0.972704 0.998504 1.027791
 1.041576 1.037637 1.039961 1.016804 1.004726 1.026805 1.030903 1.006268 0.972421 0.948140 0.908959 0.877089 0.849855 0.817964
 0.776986 0.721485

\end{verbatim}
%%\hrulefill
\end{tiny}
\caption[Example output of radial distribution functions.]{Example
output of radial distribution functions. After the header line
consisting of underscores there is an indication of the bin width $b$
(that is the distance between points at which the RDF is tabulated).
Then for each site type pair $\alpha\beta$ there is a line listing
which pair (\eg\ \Lit{O-O RDF}) followed by \Lit{nbins} values of
$g_{\alpha\beta}((i+1/2)b)$.  }
\label{fig:rdf-output}
\end{figure}

There are a couple of tricks which may be played with the system
specification if the atomic pair RDFs do not give exactly the
functions required.  Firstly, it is possible to calculate RDFs about a
particular site, distinguishing it from otherwise identical atoms by
assigning it a new and unique site id in the system specification
file.  (This is the MD equivalent of the isotopic substitution method
used in neutron diffraction). Secondly, if the \emph{molecular} pair
correlation is required, this is identical to the RDF of an atom
located at the molecular centre-of-mass.  A ``ghost'' site without
charge, mass or potentials may be added if necessary.
\pagebreak[3]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Dumping}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\label{sec:dumping}
The dump facility is provided in order to allow the calculation of
dynamic properties, such as time correlation functions and additional
static averages not normally calculated by \moldy.  During a run, dump
files are produced which contain a record of the simulation dynamic
variables (positions, quaternions \etc) at varying degrees of
detail.  Any property of interest, dynamic or static, may then be
evaluated using the data in the dump.

A dump consists of a sequence of files since the amount of data
generated in a run can be very large indeed and it is usually more
convenient to manipulate a series of smaller files rather than one
large and unwieldy one. \moldy\  takes considerable pains to ensure that
a contiguous sequence of dump files is maintained and also ensures
that dumps from different runs are not accidentally intermixed.  There
is no requirement that a dump file be produced by a single run of
\moldy\ , which extends an existing file or starts a new one as appropriate.
A simulation may stop and be restarted many times without
disturbing the dump file sequence.  The  sequence
should (in most cases) even survive a system crash and a restart from
a backup file (see section~\ref{sec:backup}).

Each dump file in a sequence is a binary file consisting of a
\emph{dump header}, which contains information about the contents of
the file followed by a number of \emph{dump records} which contain the
actual data.

Several control parameters govern dumping. It starts at the timestep
specified by \Lit{begin-dump}, and a dump record is written every
\Lit{dump-interval} timesteps thereafter.  After \Lit{ndumps}
dump records have been written to a file, it is closed and another is
begun. Filenames are generated from a prototype (given by the
parameter \Lit{dump-file}) by appending a number, so that if the
prototype is \Fname{MDDUMP} then successive files will be named
\Fname{MDDUMP0}, \Fname{MDDUMP1}, \Fname{MDDUMP2} \etc\ If it is
not convenient for the sequence number to appear at the end of the
file, include the characters ``\%d'' at an appropriate
point.\footnote{This is actually the code \Lit{sprintf()}, the C
  library function uses to signify converting an integer to a decimal
  character string. This function is used to create the actual file
  name from the prototype and the integer dump sequence number. (See
  any C library manual for details.)}  For example under VMS,
specifying \Lit{dump-file=mddump\%d.dat} will name the files
\Fname{mddump0.dat}, \Fname{mddump1.dat} \etc

Each dump record is a sequence of single-precision floating point
binary numbers.  These are written either in native (\ie\ the
machine's own) format or XDR format (see section~\ref{sec:xdr})
depending on the value of the control parameter \Lit{xdr}.  The
record's exact contents are determined by the control parameter
\Lit{dump-level} which is a bit flag \ie\ a value of $2^{n}$
means that bit $n$ is set.  Four bits are used and any combination may
be specified but the cumulative values 1, 3, 7 and 15 are most
useful.  A value of 0 disables dumping. The data dumped for each bit
are as follows:
\begin{description}
\item[bit 0]    centre of mass co-ordinates, quaternions, unit cell matrix 
and potential energy.         
\item[bit 1]    centre of mass velocities, quaternion and unit cell
matrix derivatives.
\item[bit 2]    centre of mass accelerations, quaternion and unit cell
matrix second derivatives.
\item[bit 3]    forces, torques and stress tensor.
\end{description}
Items selected are written in the order laid out above.  Within each
set of variables, values are ordered primarily by species in the order
they appeared in the system specification.  Within a species ordering
is by molecule (or atom) and at the finest level by $x$, $y$ or $z$
component ($q_{0}, \ldots q_{3}$ for quaternions). Therefore if $n$ is
the total number of molecules and $n_{r}$ is the number with
rotational freedom the size of each record is
\begin{displaymath}
\begin{array}{cll}
   & 3n + 4n_{r} + 9 + 1 & \textnormal{(if bit 0 is set)} \\
 + & 3n + 4n_{r} + 9 & \textnormal{(if bit 1 is set)} \\
 + & 3n + 4n_{r} + 9 & \textnormal{(if bit 2 is set)} \\
 + & 3n + 3n_{r} + 9 & \textnormal{(if bit 3 is set)} \\
\end{array}
\end{displaymath}
single precision floating point numbers.

The header is a copy of a \Lit{struct dump\_t} (see
source file \Fname{structs.h} for the format). It contains the simulation
title and version number, the timestep at the beginning of the file,
the control parameters \Lit{dump-interval} and \Lit{dump-level}, the
maximum and actual number of dump records in the file, a unique marker
(actually a timestamp), common to all the files in a dump run, and the
timestamp\footnote{A timestamp is simply the number of seconds elapsed
  since midnight on January 1, 1970.} of any restart file used to
start the run.

It is not possible to dump directly to magnetic tape. \moldy\ must
rewind to the beginning of a file to keep the header up to date with
the number of dumps in the file, as well as extend existing files.
Neither operation is allowed on a tape drive.  Large disk stores are
now very cheap so this should not be a problem in practice.  If disk
store \emph{is} limited then the simulation may be divided into
multiple \moldy\ runs interspersed with copying of dump files to
tape.

Notice that \moldy\  must sometimes read an existing but complete dump
file to propagate the unique marker to all of the files in a sequence.
Therefore when continuing a simulation and a dump run, at least
the immediately preceding dump file must still be accessible.  This
should be borne in mind when copying dumps to tape!

\moldy\  is careful to ensure that existing files are not
overwritten - especially necessary since dump records are added to
the end of an existing dump file.  Whenever \moldy\  prepares to start a
new dump file it checks to see if one of that name is already present.
If so, a new name is chosen by ``mutating'' the old one, and a warning
message to that effect is written to the output file. On the other
hand, if the \emph{first} file of a new dump run (including one
initiated because of some error in continuing an old one) already
exists, the \emph{prototype} file name is mutated as above and the
whole dump run is written to files based on the mutated name.

When a run is restarted checks are made to ensure that the values of
the dump control parameters have not been altered.  If they
have, it is not possible to continue an existing dump sequence and a
new one will be started.  (If existing dump files are present the new
sequence will have mutated file names.)  This also happens if an
existing file does not appear to be a \moldy\  dump.  Existing dump
files are also tested to ensure that there is no corruption (due, for
example to a system crash) and that they contain the correct number of
records.  If the dump sequence can not be continued in these
circumstances, \moldy\  terminates with a fatal error rather than waste
computer time.

%To ensure that the header always records the correct number of dump
%records in a file, it is updated \emph{after} the actual record has
%been written.  It is possible that following s crash there may be one
%more record than recorded.  \moldy\  ignores it and recalculates and
%dumps the data.

Two utility programs included in the distribution are \emph{dumpanal}
which identifies dump files by printing out the headers and
\emph{dumpext} which extracts atomic or molecular trajectories.  The
latter should be useful as a prototype for writing programs to analyse
dump data.

It is frequently convenient to perform analysis of dump data, and
perhaps graphical output on a different computer to that which
generated the data.  In the past it has not usually been possible to
sensibly transfer binary data between computers of different
architectures.  However \moldy\ is able to write dump files in a
portable format using XDR (see section~\ref{sec:xdr}) which may be
read by \emph{dumpext} on any machine.  The control parameter
\Lit{xdr} enables XDR mode for dumps as well as restart files.
As yet, XDR is not available on every machine.  Therefore a program
called \emph{dumpconv} is provided which converts dump files to a
portable text file format (which may be easily moved between machines)
and back again. It is described in appendix~\ref{sec:dumpconv}.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Constant Stress Simulation}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Setting  the control parameter   \Lit{const-pressure} switches from a
constant-volume  simulation to a con\-stant-stress  simulation using the
method     of    Parrinello     and    Rahman\cite{parrinello:81}   (see
section~\ref{sec:const-stress}).   The value of  the MD cell mass
parameter,  $W$,  is given by the control  parameter \Lit{w}  and  the
external pressure by \Lit{pressure}. At present it is not possible to
specify an anisotropic external stress, though this  capability may be
added in future versions of the program.

\label{sec:cp-constraints}
The $\bm{h}$ matrix may be constrained so as to disable motion of any
or all of its components using the parameter \Lit{strain-mask}. 
\Lit{Strain-mask} is a bitmap: each ``bit'' of the integer
freezes one of the components of $\bm{h}$;
bit $i$ freezes $ \bm{h}_{kl}$ with 
$ i = 3 (k\!-\!1)\! +\! l\! -\!1$.
The bitmask is the sum of $2^i$ over the $i$'s to be set, so the
\Lit{strain-mask} values

\begin{displaymath}
\left (
\begin{array}{rrr}
1 & 2 & 4 \\ 8 & 16 & 32 \\ 64 & 128 & 256
\end{array}
\right )
\textnormal{constrain the corresponding components of }
h, 
\left (
\begin{array}{rrr}
h_{11} & h_{12} & h_{13} \\ h_{21} & h_{22} & h_{23} \\ h_{31} & h_{32} & h_{33}
\end{array}
\right ).
\end{displaymath}
\noindent
Thus the default constraint of $\bm{h}_{?1} = \bm{a} = (a_x,0,0),
\bm{h}_{?2} = \bm{b} = (b_x,b_y,0)$ is given by \Lit{strain-mask=200}
\linebreak[2]
(8+64+128).  Another useful value is 238 which freezes all the
off-diagonal components.  This is needed for a liquid simulation since
there are no shear restoring forces acting on those components.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Cutoffs and Adjustable Parameters}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

There are four parameters related to the Ewald sum method of force
evaluation (see section~\ref{sec:ewald}), $\alpha$, $r_c$, $k_c$, and
\Lit{subcell}.  In addition the two options \Lit{strict-cutoff}
and \Lit{surface-dipole} select how rigorously the real-space
cutoff is applied and whether to include the De Leuuw surface dipole
term.

By default $\alpha$, $r_c$ and $k_c$ are chosen automatically, using
equations~\ref{eqn:ewald-cut} to give a default accuracy of $\epsilon
= \exp(-p) = 10^{-5}$ (\ie\ $p = 11.5$) in the Coulombic potential energy
evaluation.  An empirically determined value of $t_R/t_F = 5.5$ is
used.  If a different accuracy is desired the cutoffs may be adjusted
using equations~\ref{eqn:ewald-cut}.  The $\alpha$ parameter is
specified by \Lit{alpha} in units of \AA$^{-1}$ and the direct and
reciprocal space cutoff distances $r_{c}$ and $k_{c}$ by
\Lit{cutoff} and \Lit{k-cutoff} in units of \AA\ and \AA$^{-1}$
respectively.  The value of $\alpha$ should only be changed after
careful timing tests if the system size is large.  The power-law given
in equation~\ref{eqn:ewald-alpha} gives a theoretical scaling of
execution time with number of ions of $T \propto N^{1.5}$. In practise $T
\propto N^{1.57}$ has been achieved over a range of $N$ from 72 to
7800, which is very close to optimal.

\Emph{Important note:} The automatically determined value of $r_c$
is chosen to converge the Coulombic part of the potential only.  Due
to the very general nature of the potentials it is not possible to
choose $r_c$ automatically so as to guarantee convergence of the
non-electrostatic part. Although in many cases the automatic value
will be adequate \Emph{it is the user's responsibility to ensure
  that it is large enough}. If there are no charges in the system
specification file then $r_c$ is not set and an error message is
issued.

As an example of manual determination of the parameters, for a
simulation of 512 MCY water molecules the values $\alpha =
0.3$\AA$^{-1}$, $r_{c} = 9$\AA\ and $k_{c} = 1.9$\AA$^{-1}$ give
potential energies correct to approximately 1 part in $10^{5}$.  For a
simulation including ions - 1.1 Molal Magnesium Chloride solution -
the same accuracy is attained with $\alpha = 0.45$\AA$^{-1}$, $r_{c} =
9$\AA\ and $k_{c} = 3$\AA$^{-1}$.

The other relevant parameter is the switch \Lit{surface-dipole} which
includes the dipole surface energy term of De Leeuw, Perram and
Smith\cite{deleeuw:80}.  See the note in section~\ref{sec:ewald} for an
explanation of why this term should \emph{never} be used for an ionic
(as opposed to dipolar) system.

The two adjustable parameters which control the link cell force
calculation (see section~\ref{sec:link-cell}) are \Lit{subcell} and
\Lit{strict-cutoff}. The former specifies the length (in \AA) of the side of
a link cell and determines the number of cells the MD cell is divided
into. In fact the MD cell is divided into a whole number of subcells
whose side in each of the three directions is nearest to the value of
\Lit{subcell}.  (The default of zero though, is special and sets
subcell to one fifth of the cutoff radius.)  In general the smaller
the link cell, the more accurately the cutoff radius is implemented,
but too many of them reduces the efficiency of the program.

In the default cutoff mode \Lit{strict-cutoff=false} the list of
neighbour cells is constructed to include all cells whose
centre-centre distance is less than the cutoff.  This means that some
molecule pairs separated by more than the cutoff will be included and
some by less will be omitted.  Setting \Lit{strict-cutoff} to true
generates a larger cell neighbour list which is guaranteed to include
all appropriate molecule pairs.  Furthermore, molecules separated by
more than the cutoff are excluded from the force calculation by
setting their separation to a large number, 100 times the cutoff, at
which distance it is assumed the potential is very close to zero.
This is therefore the mode of choice for liquid simulations where any
artificial anisotropy is undesirable.  See section~\ref{sec:link-cell}
for a full explanation.

It is worth noting that it is unnecessary to recompile the program or
change anything else when the cutoffs are modified. Unlike most MD
codes, \moldy\ employs dynamic array allocation and automatically sets
up arrays of the correct size (and no more!) for any given $k_{c}$.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Framework Simulations}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

There has recently been much interest in simulations of systems of
molecules interacting with some rigid framework such as zeolites,
clays and other surfaces.  \moldy\  has the capability to include such a
framework in a simulation by defining it as a special kind of molecule.

The system specification should contain an entry, similar to that for
a normal molecule, which describes the atomic sites belonging to one
MD cell's worth of the framework.  Its periodic images should fill
space to construct the required infinite framework.  This is notified
to the program by modifying the first line of the specification of
that molecule to read
\begin{displaymath}
\begin{array}{lllllll}
\multicolumn{7}{l}{\textit{species-name}_{i} %
                  \quad \Lit{1} \quad \Lit{Framework}}  \\
\vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots \\
\end{array}
\end{displaymath}
(compare with section~\ref{sec:sys-spec}).  The effect of the special
keyword \Lit{framework} is
\begin{enumerate}
\item to remove the rotational freedom of the molecule.  This
preserves the infinite structure over MD cell repeats by disallowing
relative motion of its parts. (Linear motion does not destroy the
structure and \emph{is} allowed.)
\item to modify the effect of the periodic boundary conditions.
Normally a molecule is assumed to be ``small'' and periodic relocations
are applied to \emph{all} of its atoms depending on its centre-of-mass
co-ordinates relative to some interacting molecule.  In contrast, the
atoms of a framework are independently relocated.  This ensures that
each molecule ``sees'' all framework atoms from any unit cell which
are within the cut-off distance.
\end{enumerate}

In the present version of the program, only one framework molecule is
allowed, though more may be permitted in future versions. Consequently
the configuration given as a lattice start must fill the entire MD
box. (A skew start is not sensible under these circumstances since the
orientation of the framework must be explicitly specified to construct
a good space-filling structure.)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Messages and Errors}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Apart from the periodic output, there are occasional once-off
messages which \moldy\ writes to the usual output file.  Such messages
begin with the characters \Lit{*I*}, \Lit{*W*}, \Lit{*E*} or
\Lit{*F*} denoting the classes \emph{information}, \emph{warning},
\emph{error} or \emph{fatal} respectively.  Their meanings are
\begin{Litdescription}
\item[*I*] information. These are often produced by subroutines
to give useful information on their particular calculations.  For
example when temperature scaling is turned off a message to that
effect is recorded in the output file.  Various routines which
calculate internal quantities such as the Ewald sum self energies and
distant potential corrections also record their values using an
information message.
\item[*W*] warning.  When the system specification is suspicious
but not clearly wrong, or some untoward condition is detected such as
two atoms approaching too closely, a warning message is issued.
\item[*E*] error. Error messages are issued when a mistake is
detected reading any of the input files. To make correction easier,
processing continues until the end of that file, so that all of the
errors are found.  The simulation is then stopped with a fatal error.
\item[*F*] fatal. The simulation is terminated immediately. Faulty 
input files generate fatal errors after they have been completely
processed.  There are many other conditions which also generate fatal
errors, for example if the simulation algorithms violate some
criterion such as quaternion normalization or constraints (see
section~\ref{sec:quaternions}), if the program runs out of memory or
if a restart file can not be correctly opened or is of the wrong
format.
\end{Litdescription}
Most of the messages are self-explanatory.  However there are two
fatal errors which occasionally arise and are somewhat cryptic:

\Lit{*F* Quaternion n (x,x,x,x) - normalization error in beeman}

and

\Lit{*F* Quaternion n - constraint error (x)}

\noindent
Technically these refer to violations of the conditions that the
quaternions representing the angular co-ordinates be normalized to 1
and that equation~\ref{eqn:qconst} be satisfied.  Either may occur if
the angular velocity of some molecule becomes too high for accurate
integration of the equations of motion. This may have a number of
causes.  First the timestep may simply be too large.  Second the
system may be in a state where atoms are so close as to generate large
torques which accelerate the molecule to a high angular velocity. This
commonly arises if the starting configuration is very far from
equilibrium, particularly in the case of molecules with small moments
of inertia, such as methane.  In most cases the simulation may be
restarted using strong rescaling or a Gaussian thermostat to limit
velocities during the approach to equilibrium.  Occasionally a smaller
timestep may help during equilibration.  The third cause of
normalization or constraint errors is an error in the potentials or
the units which allows for a state with high or infinite negative
binding energy.

Note that these messages only occur for polyatomic molecules.  If the
system is monatomic the errors mentioned above may still be present
but will not be detected and the simulation may fail in some less
predictable manner, for example particles may approach too closely
and/or acquire high velocities and fly off to infinity.  The message

\Lit{*W* Sites n and m closer than 0.5A.}

\noindent
gives some warning of this condition.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Compiling and Modifying Moldy} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

The \moldy\  distribution consists of numerous files of ``C'' source code
for \moldy\  and the utility programs, command or job files to compile
the source, \LaTeX\ input for the manual and example control and system
specification files.  For ease of transport these are packed into one
large archive file, whose format and method of unpacking depends
on the operating system of the target machine.  At present it is
available for:

\begin{description}
\item[unix] The archive is usually a tar archive called
  \Fname{moldy.tar} possibly compressed with the ``compress'' or
  ``gzip'' programs and named \Fname{moldy.tar.Z} or
  \Fname{moldy.tar.gz}. These files may be uncompressed using the
  ``gunzip''  or ``uncompress'' programs, \emph{viz.}
  \Lit{gunzip moldy.tar.gz} or \Lit{uncompress moldy.tar.Z} whereupon
  the archive is unpacked by the command \Lit{tar xvf moldy.tar}.
  
  An alternative form of archive for those unusual systems without a
  ``tar'' program is a shell archive --- a Bourne shell script called
  \Fname{moldy.shar}. This is unpacked by \Lit{/bin/sh moldy.shar}.  

\item[VMS]  The archive is a DCL command file called \Fname{moldy.com},
and is unpacked by the command \Lit{@moldy}.

\item[MS Windows 3/Windows 95/NT] The shareware program ``winzip'' may be
  used to unpack the compressed tar archive
  \Fname{moldy.tar.gz}. There is also a precompiled binary version for
  Windows 95/NT which is available as \Fname{moldy.zip}.  Winzip may
  also be used to uncompress this distribution.

\item[MS-DOS] The files must be unpacked from the tar archive on
another host and transferred to the PC by disk or ftp.

\end{description}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Compilation}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The source code of \moldy\  consists of 23 files of C programs (which have
suffix ``\Fname{.c}'') and 8 header files (suffix ``\Fname{.h}'').
To build \moldy, all of the \Fname{.c} files must be compiled and linked
together to form an executable.  The method of doing this depends on
the operating system of the target computer.
\begin{description}
\item[unix]  The ``make'' program is used and the make file is
supplied in the distribution.  The \Fname{Makefile} supplied will attempt
to compile using the command \Lit{cc -O -DUSE\_XDR} which will usually
be sufficient to build a working executable on most operating
systems.  However some systems may require extra libraries to
be specified and it will usually be possible to build a faster-running
executable by judicious use of optimization options. Options for many
common compilers/operating systems are listed in commented-out form.
To enable them, simply uncomment the appropriate lines in the makefile.
Then just type \Lit{make} to compile and link \moldy\  and
\Lit{make utilities} for the utility programs.

\item[VMS]  Simply type \Lit{@compile} to execute the command file
\Fname{compile.com}. This will build \moldy\  and the utilities.

\item[DOS/MS Windows 3] There is a makefile for Borland Turbo C called
  \Fname{Makefile.mak} in the standard distribution.  This must be
  edited to select the appropriate compiler options before executing
  \emph{make}.\footnote{Be sure to delete or rename the unix make file
    \Fname{Makefile} since Turbo C \emph{make} will attempt to
    execute this in preference to \Fname{Makefile.mak}}
  Alternatively the programs may be built within the interactive
  environment.  Consult the \Fname{makefile} to find out which source
  files to link to build \moldy\ and the utilities.  \moldy\ has also
  been built using Watcom C.
  
\item[Windows 95/NT] The most straightforward way to build \moldy\ is
  to install one of the ports of the GNU gcc compiler such as the
  Mingw32 or Cygnus versions.  The Mingw32 version is the more
  straightforward and has the advantage over Cygnus that the
  executable files do not depend on a DLL file and are therefore more
  portable. Both are available over the internet via the URL
  \htmladdnormallink{http://www.fu.is.saga-u.ac.jp/\%7Ecolin/gcc.html}%
{http://www.fu.is.saga-u.ac.jp/\%7Ecolin/gcc.html} which contains links
  to both distributions. (The mirror site in the USA
  \htmladdnormallink{http://www.geocities.com/Tokyo/Towers/6162/gcc.html}%
{http://www.geocities.com/Tokyo/Towers/6162/gcc.html} may be
  faster).  Both ports contain the GNU make program and will work with
  the supplied \Fname{Makefile}.  Simply edit \Fname{Makefile} and
  uncomment the appropriate set of compiler options before invoking
  \Fname{make}.
  
  \moldy\ has also been successfully compiled using Watcom C and the
  development environment.  Consult the Makefile to see which objects
  to link to build \moldy\ and the utilities. It is also possible that
  the Borland C \Fname{Makefile.mak} will work under Borland C for
  Windows 95 or NT\footnote{The author's experience of Windows 3 and
    Windows 95 platforms is somewhat limited.  I would very much
    welcome any reports from users on how to build \moldy\ in these
    environments for inclusion in future versions of this manual.}.


\end{description}

\subsection{XDR Dump and Restart}
As described in section~\ref{sec:xdr} binary restart and dump files
may be written and read in a portable way using the Sun XDR calls.
Since these are are not part of the ANSI C standard libraries, this
code is conditionally included by defining the C preprocessor macro
\Lit{USE\_XDR}.  Though non-standard, XDR is almost universally
available on unix operating systems, so the makefile defines this by
default, assuming the syntax of the compiler flag \Lit{-DUSE\_XDR}
to do so.  If the XDR headers and libraries are not present on your
system, then comment out the line reading \Lit{XDR=-DUSE\_XDR} at
the top of the makefile to deactivate this feature.  On certain
systems (\eg\ older versions of SGI IRIX and Sun Microsystems Solaris
2.x) it may be necessary to include some additional library in the
link, via the \Lit{LDFLAGS} make macro.

\subsection{Parallel Version (Message Passing)}
\label{sec:spmdpar}
The parallel version of \moldy\ relies on an interface with a suitable
message-passing library.  This is the recommended version and
supersedes the ``shared-memory'' parallel implementation described in
section~\ref{sec:shmpar}.  The current release contains interfaces to
the MPI library\cite{mpi:94}, the TCGMSG library and the Oxford BSP
library.  MPI is the most recommended interface since it is the new
standard for message-passing libraries, and should become ubiquitous.
If none of these are installed on your machine, some public-domain
implementations are available for workstation clusters,
multiprocessors and many distributed-memory parallel machines.

\begin{description}
\raggedright
\item[MPI] The MPICH implementation can be downloaded by anonymous ftp
  from the URL
  \htmladdnormallink{\Lit{ftp://info.mcs.anl.gov/pub/mpi/mpich.tar.gz}}{ftp://info.mcs.anl.gov/pub/mpi/mpich.tar.gz}\ and with other information at
  \htmladdnormallink{\Lit{http://www.mcs.anl.gov/mpi/index.html}}{http://www.mcs.anl.gov/mpi/index.html}.

  The CHIMP implementation can be downloaded by anonymous ftp
  from \htmladdnormallink{\Lit{ftp://ftp.epcc.ed.ac.uk/pub/chimp/release/chimp.tar.Z}}{ftp://ftp.epcc.ed.ac.uk/pub/chimp/release/chimp.tar.Z}.
\item[TCGMSG] This may be obtained by anonymous ftp from
  \htmladdnormallink{\Lit{ftp://ftp.tcg.anl.gov/pub/tcgmsg/tcgmsg.4.04.tar.Z}}{ftp://ftp.tcg.anl.gov/pub/tcgmsg/tcgmsg.4.04.tar.Z}.
\item[BSP] The Oxford BSP Library is available through Oxford
  Parallel's WWW server
  \htmladdnormallink{\Lit{http://www.BSP-Worldwide.org/implmnts/oxtool.htm}}{http://www.BSP-Worldwide.org/implmnts/oxtool.htm}\ or by anonymous ftp
  \htmladdnormallink{\Lit{ftp://ftp.comlab.ox.ac.uk/pub/Packages/BSP/}}{ftp://ftp.comlab.ox.ac.uk/pub/Packages/BSP/}.
%v1.3\_bsplib\_toolset.tar.gz}.
\item[SHMEM] The CRI native communications library for T3D and T3E
  systems.
\end{description}

\noindent
Alternatively a port to another parallel interface should be quite
straightforward, see section~\ref{sec:parport}.

Once a suitable message-passing library is installed the procedure for
building \moldy\ is quite simple.  The C preprocessor macro
\Lit{SPMD} must be defined as well as one of \Lit{MPI},
\Lit{TCGMSG}, \Lit{BSP} or \Lit{SHMEM}. This is usually done
in the makefile by setting the Make macro \Lit{PARLIBC=-DSPMD
  -DMPI}, for example.  This macro should also include a \Lit{-I}
directive specifying the directory for the library's header files if
these are not in the default path searched by the compiler.  The
similar make macro \Lit{PARLIBL} should contain the linker
directives necessary to link to the library itself. Examples are
provided at the top of the supplied \Fname{Makefile}.

This parallel implementation makes use of the \emph{replicated data}
approach\cite{smith:91} whereby every processor has a complete copy of
all the arrays containing dynamical variables and every site on every
molecule.  The computation of the real-space potential and forces is
distributed over processors on the basis of link cells.  For the
reciprocal-space part of the Ewald sum, the \emph{k}-vectors are
distributed among processors.  This is an extremely efficient way of
implementing parallelism since the forces \etc\ must be summed over
processors only once per timestep, thus minimizing interprocessor
communication costs.  It is therefore possible to get considerable
speedup for a small number of workstations coupled by a fast network
such as an Ethernet.

The biggest disadvantage of the replicated data strategy is that
every processor must maintain a copy of all of the data, and therefore
that the memory requirement per processor increases with the size of
the system.   In many cases this is not a severe problem, as MD
memory requirements are not large compared with memory sizes of modern
computers.  However the poor scaling will eventually limit the number
of processors which may be used.  On a shared-memory multiprocessor,
the alternative parallel version in section~\ref{sec:shmpar} may
provide a solution, if it can be ported to that machine.

The memory limitation will be most acute when the goal is to simulate
an extremely large system on a massively parallel distributed-memory
computer where it is desirable to scale the system size with the
number of processors.  In such architectures the available memory per
processor is usually a constant independent of the number of
processors. But the memory needed \emph{per processor} increases with
system size.  Mostly the scaling is linear, but the reciprocal-space
sum uses temporary arrays whose size scales with the product of the
number of sites and \emph{k}-vectors, and hence to the
{\small$\frac{3}{2}^{\textnormal{th}}$} power of the system size.

An alternative version of \Fname{ewald.c} which implements the
reciprocal-space term of the Ewald sum by distributing over
\emph{sites} rather than \emph{k}-vectors is included in the
distribution as \Fname{ewald-RIL.c}.  It is based on the RIL algorithm
of Smith~\cite{smith:92} and \emph{distributes} the temporary arrays
containing the $\cos(\bm{k} \cdot \bm{r}_i)$ and $\sin(\bm{k} \cdot
\bm{r}_i)$ over the nodes. In other words, each node only stores the
terms involving the $\bm{r}_i$'s to be considered on that node.  Since
these arrays are by far the largest users of memory there is a
substantial decrease in overall memory requirement.  Moreover the size
per node now scales \emph{linearly} with the number of
\emph{k}-vectors and therefore (assuming $\alpha$ is optimized), to
the two-thirds power of the number of sites.  These arrays will not
therefore dominate the memory requirement in the limit of large
numbers of processors and system size.  The disadvantage of the RIL
scheme is that the partial sums of $\cos(\bm{k} \cdot \bm{r}_i)$ and
$\sin(\bm{k} \cdot \bm{r}_i)$ must be summed over nodes separately for
each \emph{k}-vector. Though the amount of data transferred each time
is small, the communication and inter-processor synchronization is far
more frequent than for the RKL scheme and the parallelism becomes very
fine-grained.  The upshot is that only machines with very low
communications latency can run this version effectively.  Practical
tests show that the communications overhead completely negate any
parallel gain on systems of networked workstations and most
multiprocessors.  However a significant speedup is obtained on a Cray
T3D, which is exactly the case where this version is needed.

\subsection{Shared-Memory Parallel Version}
\label{sec:shmpar}
An alternative parallel version is available for shared-memory
multiprocessors with ``parallelizing'' compilers.  This relies on the
compiler handling the multi-threading, synchronization and allocation
of local memory stacks for inner function calls. It requires
compiler-specific directives to be inserted in the code and is
therefore less portable than the distributed-memory version of the
previous section. (Note that that version works on this class of
machines too under the message-passing interface.) Nevertheless, it
works and has been run on Stardent, Convex and Cray computers.  It
consists of replacements for files \Fname{force.c} and
\Fname{ewald.c} called \Fname{force\_parallel.c} and
\Fname{ewald\_parallel.c}. Then the program should be compiled with
the preprocessor macro \Lit{PARALLEL} defined (not \Lit{SPMD}).

The distributed-memory parallel version (section~\ref{sec:spmdpar}) is
generally recommended over this one.  However because the parallel
sections reference a single global copy of most of the arrays, the
shared-memory version uses much less memory.  This version may
therefore be of use if memory limits the size of the system on 
a multiprocessor machine.

% The approach taken is to retain the vectorized inner loops and
% parallelize the the outer loops, over cells for \Fname{force.c} and {\bf
% k} vectors for \Fname{ewald.c}.  The two parallel loops are structured by
% the method developed by the Kingston group\cite{wojcik:85}. That is, a
% loop of the form
% \begin{verbatim}
%         for(i = 0; i < N; i++)
%            
% \end{verbatim}
% is transformed into
% \begin{verbatim}
%         for(processor = 0; processor < NPROCESSORS; processor++)
%            for(i = processor; i < N; i += NPROCESSORS)
%               
% \end{verbatim}
% so that the outer loop is over the number of available processors,
% and the inner loop has an increment of that number.  Note that this
% transformation reorders the loop iterations.
% 
% If the outer loop is to execute concurrently, each sub-process or
% thread must have local copies of any temporary variables, notably
% \Lit{i}.  Therefore the contents of each loop are isolated into a
% separate function. Each thread executes a separate incarnation
% of the function.  A shared-memory model is assumed, in which each
% thread accesses a single copy of the parameters and global
% variables, but has a unique copy of all of its local variables.
% 
% The ``master'' thread maintains separate copies of result variables
% and arrays, (\ie\ the forces, potential energy and stress virial) so
% that each incarnation of the function can write to its own copy
% without interference from other threads.  After all of the threads
% recombine, the separate copies are summed to give the final result of
% the calculation.  This method avoids the need for explicit
% synchronization between threads, but will add to the serial overhead
% on highly parallel machines.
% 
% The method of actually parallelizing the outer loop will vary from
% machine to machine. The stellar compiler is able to generate parallel
% code, and the directive \Lit{/*\$dir}~\Lit{no\_recurrence*/}
% instructs it to parallelize the following loop.  The Ardent, Convex
% and Cray compilers use \Lit{\#pragma} directives, namely
% \Lit{\#pragma ipdep}, \Lit{\#pragma \_CNX force\_parallel} and
% \Lit{\#pragma \_CRI taskloop} respectively.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Portability}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
A major goal in writing \moldy\ was that it be as portable as possible
between different computers and operating systems.  It is written in
the Kernighan and Ritchie\cite{kernighan:78} compatible subset of ANSI
C and assumes the library calls and header files defined for a hosted
implementation of the standard.  It should therefore be possible to
compile and run \moldy\ on any computer which has a good C compiler.

There are two possible sources of difficulty in moving \moldy\ to a
new machine. Though hosted ANSI standard C environments are now
commonly available it may still be necessary to compile \moldy\ using
a pre-ANSI compiler.  In that case some library functions and header
files may not be present. Secondly, to make good use of vector or
parallel architectures, compiler directives or calls to specialized
library functions are usually required.  Replacement ANSI library
functions are supplied in \Fname{ansi.c} for the VMS and unix (both
Berkeley and AT\&T system V varieties) operating systems.  Different
versions of a function are selected by the C preprocessor and
conditionally compiled according to the pre-defined preprocessor
symbols (see the documentation for your compiler).  For ease of
portability \emph{all other system-dependent functions are in the
  module \Fname{auxil.c}} and \emph{all preprocessor conditionals are in
  the header file \Fname{defs.h}}.

If the target machine has ANSI conformant C libraries, all that must
be done is to define the preprocessor symbol \Lit{ANSI\_LIBS},
either in \Fname{defs.h} or by using a compiler option \eg\ 
\Lit{-DANSI\_LIBS}.  This is done automatically in \Fname{defs.h}
for several machines known to have conformant environments.  If the
target operating system is the system V variant of UNIX, the
preprocessor symbol \Lit{USG} is defined automatically in
\Fname{defs.h}.  It is possible that some environments may defeat the
selection making it necessary to define it by hand, either in
\Fname{defs.h} or by setting the compiler option \Lit{-DUSG} in
the makefile.

\subsection{System Dependencies}
In this section, details of system-dependent functions are described
for the major operating systems.
  \begin{description}
\item[Replacement ANSI header files]
 \begin{sloppypar}
   The ANSI header files \Fname{string.h}, \Fname{stdlib.h},
   \Fname{stddef.h} and \Fname{time.h} are missing from Berkeley
   unix, or incomplete.  Replacements are included which may be
   dispensed with on an ANSI conformant system - If the symbol
   \Lit{ANSI\_LIBS} is defined they simply include the system
   version.
 \end{sloppypar}

\item[Replacements for ANSI functions] \hspace*{1em}
\noindent 
  \begin{itemize}
  \item The ANSI function to delete a file, \Lit{remove()}, the
    signalling function \Lit{raise()} and the string functions
    \Lit{strstr()} and \Lit{strerror()} are missing from
    pre-ANSI libraries.  Replacements are supplied in \Fname{ansi.c}.
  \item Replacements are provided in \Fname{ansi.c} for functions
    \Lit{memset()}, \Lit{memcpy()} and \Lit{strchr()} which
    are missing from Berkeley UNIX.
  \item The function \Lit{vprintf()} is often absent from older
    libraries. Replacements are provided which \emph{a)} call the
    internal function \Lit{\_doprnt()} or \emph{b)} implements a
    portable \Lit{vprintf()}.  Use the preprocessor macros
    \Lit{HAVE\_VPRINTF} or \Lit{HAVE\_DOPRNT} to select which.
  \end{itemize}
\item[Timing routines] The supplied \Lit{clock()} function on
  32-bit UNIX systems resets to zero after 36 minutes.  Replacements,
  called \Lit{cpu()} for system V and Berkeley UNIXes and
  POSIX are supplied in \Fname{auxil.c}.  The function \Lit{rt\_clock()}
  is also defined and returns the elapsed time in seconds.  For a
  non-unix system \Lit{cpu()} and \Lit{rt\_clock()} simply call
  the ANSI functions \Lit{clock()} and \Lit{time()}.
\item[File manipulation routines]
 \Fname{Auxil.c} contains the functions \Lit{replace()} and
  \Lit{purge()}.  \Lit{replace()} renames a file, making a
  backup of any existing file of that name. \Lit{purge()} removes
  the previous or backup version of a file.  These functions make use
  of the file name syntax of the host operating system and are
  therefore system-dependent.  Unix file systems do not have explicit
  version numbers but \moldy\ keeps a single previous version by
  appending a ``\%'' character to the name.  The pure ANSI versions
  just interface to \Lit{rename()} and do nothing respectively.
\end{description}

\subsection{Optimization and Vectorization}
\moldy\ has been designed to run fast on a wide range of computers,
and in particular on those with vector, multiprocessor and parallel
architectures. This is a difficult problem, since the constructs which
run fast on different architectures may be quite distinct and
occasionally in conflict.  Nonetheless, it has been found that
following a few basic rules gives extremely good performance on a wide
range of computer architectures.  In a rough order of importance these
are:
\begin{enumerate}
\item Minimize the number of memory references to floating point data
  in critical loops.  Memory access is the major bottleneck on almost
  every modern computer, scalar, vector or parallel.
\item Minimize the number of memory references to floating point data
  in critical loops.  This cannot be emphasized enough.
\item Ensure that memory is accessed contiguously within critical
  loops. That is, arrays should be accessed with a stride of 1 and
  with the last index varying most rapidly.\footnote{C uses the
    opposite convention to FORTRAN in storage layout of
    multidimensional arrays}  This is absolutely critical on machines
  where memory is accessed via a cache, \ie\ all workstations and many
  parallel systems, and frequently very important on machines with
  interleaved memory (\ie\ most vector machines).
\item If the value of any array element is used more than once in a
  loop, write the loop using temporary scalars to store results and
  assign them to the arrays at the end of the loop. This allows the
  compiler to optimize memory references.\footnote{Technically, the C
    standard treats arrays passed as formal function parameters as
    pointers which are permitted to refer to overlapping areas of
    memory.  The compiler must therefore assume that if an array
    element is written in a loop then elements of any other arrays may
    also be changed.  It must therefore reload from memory even though
    it already has a copy of the value in a register. But if all loads
    are completed before any stores then the compiler is at liberty to
    re-use the values and save memory accesses.}
\item Minimize the floating-point operation count in critical loops.
\item Minimize integer arithmetic in critical code.  CRAY vector
  machines in particular have no integer multiplication hardware,
  and integer operations are slow as a result.
\end{enumerate}

The performance of \moldy\  has been carefully studied using profiling
tools, and all critical regions of code are written as efficiently
vectorizable loops.  

The most critical sections of code (\ie\ those which use the majority
of the computer time) are all to do with the site forces calculation.
Thus it is the inner loops in \Fname{force.c}, \Fname{ewald.c} and
\Fname{kernel.c} to which most attention should be paid.  The
pair-distance loop of \Lit{rdf\_calc()} in \Fname{rdf.c} should
vectorize for efficient radial distribution function evaluation.
Others which are of minor importance are in \Fname{beeman.c},
\Fname{matrix.c}, \Fname{quaterns.c} and \Fname{algorith.c}.
\Fname{Auxil.c} contains alternative versions of various sum, dot
product, scatter and gather routines \etc\ which are interfaces to
machine-specific libraries \eg\ Cray scilib, Convex veclib (which
usually have FORTRAN calling conventions). There are also default
versions coded in C which do vectorize, for machines lacking
specialist libraries as well as for scalar computers.

\subsection{Optimization for Vector Architectures}
The program should, of course, be compiled with options specifying
vectorization.   Since highly
optimizing and vectorizing compilers frequently contain bugs, and
since some options generate ``unsafe'' optimizations, it may be
necessary to restrict the highest optimization level to those modules
which contain critical code.

To allow the compiler to generate vector code, it must be instructed
to ignore apparent vector recurrences. The reason is that the run-time
dimensioned arrays necessary to implement such a flexible program must
use pointers as their base.  (See any C textbook, \eg\ Kernighan and
Ritchie\cite[Chapter 5]{kernighan:88} for an explanation of C pointers and
arrays.)  Unfortunately this means that the compiler can not determine
that each iteration of the loop is independent of the preceding
iterations.  In the jargon of vectorizing compilers, there may be a
\emph{vector dependency} or \emph{recurrence}. The compiler can be
notified that these are not genuine recurrences either globally by use
of a command-line directive or on a per-loop basis using
machine-specific compiler directives inserted into the source.

Most compilers also have an option which directs it to ignore
recurrences throughout the whole program, \eg\ \Lit{-va} on the
Convex, \Lit{-va} and \Lit{-h ivdep} on the Cray compilers.  It
should normally be safe to use these options. Each manufacturer's
compiler has its own peculiar set of inline directives.  For example
the CRAY compilers use a \Lit{\#pragma}~\Lit{ivdep} statement
whereas the convex and Stellar compilers use a ``significant comment''
\Lit{/*\$dir no\_recurrence*/}.\footnote{A mechanism is
  provided to insert appropriate directives using the C preprocessor.
  The text \Lit{VECTORIZE} has been placed before each loop which
  ought to be vectorized, and the file \Fname{defs.h} contains
  machine-conditional \Lit{\#define}s to replace it with the
  appropriate directive.  Currently directives for the CRAY, Stellar
  and Convex compilers are included, and null text is substituted for
  other machines.  Notice that in each case the substituted text is
  \emph{not} the directive described in the manual, but rather that
  directive \emph{after} it has been passed through the preprocessor.
  To determine what should be substituted on a new vector machine,
  create a small test containing the documented directive and use the
  C preprocessor on that file. The output will show the form that
  should be defined in \Fname{defs.h}.
  
  Unfortunately this method had been made obsolete by the ANSI C standard,
  which makes it impossible to insert pragmas using the
  preprocessor. } 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Modifying Moldy}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Adding a New Potential}
\label{sec:newpot}
By default \moldy\ supports potential functions of the Lennard-Jones,
six-exp and MCY forms.  However it should be very easy to add further
types. The program is written in a highly modular fashion so that
\emph{the only code which need be altered is in file \Fname{kernel.c}}
(and occasionally in \Fname{defs.h}).

The calculation of the potential and forces is performed entirely in
the function \Lit{kernel()}.  This function is called repeatedly with
a vector of (squared) distances between some reference site and its
neighbour sites.  Vectors of potential parameters and charges are
supplied which bear a one to one correspondence with the elements of
the distance vector.  It calculates the corresponding values of
$\frac{1}{r_{ij}}\frac{dU(r_{ij})}{dr_{ij}}$ which it stores in
\Lit{forceij[]}.  There are several variants of the force-calculation
loop, one for each kind of potential.  The potential type in use is
passed as a parameter to \Lit{kernel()} and is used in a \Lit{switch}
statement to select the appropriate code.

To add a new potential the array of structs called \Lit{potspec[]}
must be extended.  The new array element should contain the name of
the new potential (against which, the names given in system
specification files will be matched) and the number of potential
parameters for each site pair.\footnote{By default the arrays are sized
  for up to seven parameters. If this is not sufficient, the limit,
  set by the value of the constant \Lit{NPOTP} defined in
  \Fname{defs.h} may be increased.}  In parallel with
\Lit{potspec[]}, the array \Lit{pot\_dim[]} must also be updated
with a new entry which describes the dimensions of each parameter for
the new potential. It is used by the input routines to convert from
the input units into program units.  The entry consists of triplets
containing the powers of mass, length and time, one for each
parameter.  Then define a new preprocessor symbol to the index of the
new type in the array \Lit{potspec[]} (after the line
\Lit{\#define~MCYPOT~2}).  The value must correspond to the index
of the new entry in \Lit{potspec[]} starting from 0 in accordance
with the usual C convention.  This constant should be used to define a
new case in the \Lit{switch} statement of \Lit{kernel()}, and
this is where the code to evaluate the potential goes.

The existing cases may be used as a model, especially for the
evaluation of the electrostatic term $\erfc(\alpha r) / r$ which is
evaluated by the polynomial expansion of Abramowitz and
Stegun\cite[section 7.1.26]{abramowitz:70}. There are currently
\emph{two} versions of each loop, the second omitting this term for
efficiency when all the electric charges are zero (which case is
flagged by a negative value of $\alpha$).

Finally, the distant potential correction for the new potential should
be added as a new case to function \Lit{dist\_pot()}. The code
should evaluate
\[
- \int^{\infty}_{r_c} r^2 U(r) \, \Calcd r
\]
for the potential $U(r)$.

\subsection{Porting the Parallel Version}
\label{sec:parport}
It should be relatively straightforward to port the distributed-memory
parallel version to a new message-passing library.
Section~\ref{sec:parstrat} describes the parallel implementation. All
of the interface code is contained in \Fname{parallel.c} and it will
only be necessary to modify this file.  A new port should declare a
new preprocessor macro along the lines of \Lit{MPI} \etc\ which
should be used to conditionally compile its code only.  Any header
files may be included in the appropriate place in \Fname{parallel.c}.
Then the interface functions should be written to call the underlying
message passing library. These should again be conditionally compiled.
It should be obvious where to place them in the file and the existing
versions will provide a model.  Their specifications are:

\begin{Litdescription}
\sloppy
\item[par\_sigintreset(void)] Moldy sets a handler for SIGINT\@.  This
  function is called from the signal handler to restore the default.
\item[par\_begin(int *argc, char ***argv, int *ithread, int
  *nthreads)] Initialize the library and return the number of
  processes and the ID of this process.
\item[par\_finish(void)] Terminate the parallel run normally.
\item[par\_abort(int code)] Terminate the run abnormally.  Return code
  if possible.
\item[par\_broadcast(void *buf, int n, size\_mt size, int ifrom)]
  Broadcast the specified buffer from node \Lit{ifrom} to all nodes.
\item[par\_\{r,d,i\}sum(void *buf, int n)] Perform a global parallel
  sum reduction on the buffer containing n reals,\footnote{\Lit{real}
    is a typedef defined in \Fname{defs.h} which is set either to
    \Lit{float} or \Lit{double} (see section~\ref{sec:types}).  The
    code for \Lit{par\_rsum()} must handle either case, which may be
    tested using the \Lit{sizeof} operator. For example the
    preprocessor macro \Lit{\#define M\_REAL
      (sizeof(real)==sizeof(double)?MPI\_DOUBLE:MPI\_FLOAT)} is used
    to determine which constant to pass to the MPI sum function.}
  doubles or ints. 
\item[par\_imax(int *idat)] Perform a global maximum reduction on
  the single int argument.
\fussy
\end{Litdescription}

The SPMD parallel strategy updates the dynamic variables independently on
all processors (see section~\ref{sec:parstrat}).  To update the
separate copies of the co-ordinates and other dynamic variables
synchronously the results of the floating-point arithmetic must be
identical.  Therefore the result returned by \Lit{par\_rsum} and
\Lit{par\_dsum} must be identical to the last bit on all processors:
see the footnote on page~\pageref{sec:parstrat}.  Another consequence
is that execution on heterogeneous workstation networks is not
supported - the identity of floating-point operations in not
guaranteed even if all use IEEE arithmetic.  \moldy\ periodically
tests for divergence of trajectories and will exit with the message

\Lit{*F* Trajectories on parallel threads are diverging.}

\noindent if this condition is detected.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Program Structure} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
The source of \moldy\ consists of 31 different C source files
amounting to 167 functions and 9000 lines of code. A complete and
detailed description would be a compendious volume of questionable
value.  Instead, much of the detailed documentation is contained in
the source code in the form of comments. This chapter concentrates on
describing the organization of the calculation and the data structures
used throughout the code, describes some of the more complicated and
less obvious algorithms and provides call graphs and brief function
descriptions to act as a map of the program structure.

\moldy\ is written in modular fashion in adherence to the principles
of \emph{structured programming} to as great a degree as practical.
This does not mean merely the avoidance of \Lit{goto}s but, instead
the organization of the program into modules and functions which are
independent of each other and of global environmental variables.
Functions are kept to a limited size and as far as practical
serve a single, well defined purpose.\footnote{The term ``function''
  in C corresponds to both functions and subroutines in FORTRAN.} This
ensures that the internal workings of a function are unaffected by
changes elsewhere in the program, and do not have any influence on any
other part of it, except through the defined interface.  In \moldy\,
functions are grouped into different files according to a rough
classification of their purpose.  

The other primary consideration in the design of a large computer
program is the provision and organization of storage for the data.
Structured programming advocates that definitions of data objects be
restricted as closely as possible to the code that uses them, and that
the code be organized in a modular fashion to encourage data locality.
This minimizes the risk of a programming error in one part of the code
modifying a variable used in a completely different part and producing
difficult-to-locate side effects.  With two exceptions,%
\footnote{These are the struct \Lit{control}
  (section~\ref{sec:sysvars}) and the integers \Lit{ithread} and
  \Lit{nthreads} holding the parallelization parameters
  (section~\ref{sec:pario}).} global data is avoided in \moldy\ and
all arrays are passed as function arguments where necessary.  Heavy
use is made of C structures (or ``structs'') to further group related
data so that it may be manipulated \emph{in toto}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Data Structures}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
\subsection{Types and Typedefs}
\label{sec:types}

A number of derived types are defined in the header file
\Fname{defs.h} for reasons of economy of expression, portability and
ease of customization.  Most of these derived types are named using a
suffix \Lit{\_mt} to mark them as such.  These are
\begin{Litdescription}
\item[real] This is the ``standard precision'' floating-point type
  used for all the dynamic variables and most other internal
  variables. It is set to \Lit{double} by default. Changing this to
  \Lit{float} will give a single-precision version of \moldy\ with
  a consequent memory saving.  However additional work must be done to
  create a fully single-precision version, since the standard
  double-precision maths library will still be used.  The C standard
  does not require a single-precision maths library, but most systems
  do make the functions available. However there is no
  standardization of the interface so this can not be done portably.
\item[boolean] Used for storing logical values.  Typed to
  \Lit{int}.
\item[gptr] Generic pointer type, set to \Lit{void} in the case of ANSI C.
  Pre-ANSI compilers do not support \Lit{void} so \Lit{char} is
  used instead.
\item[time\_mt] This is used to store integer format times and dates as
  returned by the \Lit{time()} function.  It is declared as
  \Lit{unsigned long} and is used because pre-ANSI compiling
  systems may not define the \Lit{time\_t} type for this purpose.
\item[size\_mt] Used for storage of C object sizes as returned by
  \Lit{sizeof}.  Like \Lit{time\_mt} this would be unnecessary if
  we were guaranteed an ANSI C compilation system.
\item[vec\_mt] Array of 3 \Lit{real}s for holding a vector type.
\item[quat\_mt] Array of four reals for holding a quaternion type.
\item[mat\_mt] $3 \times 3$ array of reals for storing a matrix.
\end{Litdescription}

\subsection{Memory Management}

The allocation of memory for storage of the multitude of data required
in a molecular-dynamics simulation is one of the main design criteria
of the code. The general nature of the systems to be accepted by
\moldy, namely the arbitrary mixtures of molecules with different
numbers and kinds of atoms requires a number of large multidimensional
arrays with system-dependent bounds in more than one dimension.  It is
impractical to declare these statically with dimensions fixed at some
suitably large value because total memory use would then be infeasibly
large.  The availability of standard, portable, dynamic memory
allocation was one of the major reasons the author chose to write
\moldy\ in C.\footnote{At the time \moldy\ was being planned in 1988 C
  was the \emph{only} widely available language offering dynamic
  memory allocation.  Fortran 90, which also offers dynamically
  declared arrays was standardized in 1991 and compilers only became
  common in the mid-nineties.} \moldy\ uses C's capability of array
emulation by pointers and heap-based memory
allocation\cite{kernighan:88} rather than true C arrays which, like
FORTRAN's, are restricted to bounds fixed at compile-time.

Much of the dynamic memory used in \moldy\ is allocated on entry to a
function and deallocated before exit to emulate local,
variably-dimensioned arrays.  The main exceptions are the arrays of
dynamical variables which are allocated once during the startup phase
and not freed until program exit.  All dynamic memory is allocated
using the function \Lit{talloc()} which is a wrapper around the
standard library function \Lit{malloc()}.  Function \Lit{talloc()}
simply calls \Lit{malloc()}, tests the return value and calls an error
exit function if it failed to allocate the memory requested.  Its
interface takes advantage of the C preprocessor macros
\Lit{\_\_LINE\_\_} and \Lit{\_\_FILE\_\_} to print out the location of
a failing call, and a wrapping macro \Lit{aalloc()} is provided in
\Fname{defs.h} for this purpose. This also contains other
\Lit{\textsl{x}alloc()} macros customized for various data types.  The
complementary function \Lit{tfree()} calls the library function
\Lit{free()} but also allows tracing of allocation and freeing for
debugging purposes.  All of \moldy's memory-management functions are
in the source file \Fname{alloc.c}.

Like FORTRAN, C only permits the declaration of arrays of size fixed
at compile time. Unlike FORTRAN, C lacks any method of declaring
arrays with adjustable innermost dimensions as function formal
parameters.  However through the use of pointer --- array mapping and
dynamic memory allocation, variable-sized multidimensional arrays may
be emulated\cite[p107]{kernighan:88},\cite[pp 20--23]{press:92C}.
Multidimensional array emulation is done by the function
\Lit{arralloc()} (see Figure~\ref{fig:arralloc}). This takes the size
of the atomic data-type, the number of dimensions and the lower and
upper bounds for each dimension as arguments and returns a pointer to
the allocated pseudo-array. This is an array of \emph{pointers} (to an
array of pointers to \ldots) to the data area.  The C mapping of array
notation onto pointer syntax allows this construct to be referenced
exactly as if it was a true multidimensional array. For example\\
\parbox{\textwidth}{%
\begin{quote}
  \Litf
double **r;\\
r = arralloc(sizeof(double), 2, 0, m-1, 0, n-1);
\end{quote}}\\
\noindent defines a 2D array, $m \times n$ of \Lit{double} so that
\Lit{r[i][j]} is a reference to the $i, j$ th element in the usual
manner. This pseudo-array may be passed directly to a function, \eg\\
\parbox{\textwidth}{%
\begin{quote}
  \Litf
double doit(r, i, j)\\
double **r;\\
int    i, j;\\
\{\\
\hspace*{1cm} r[i][j] = 0.0;\\
\}
\end{quote}}\\
\noindent
since the underlying pointer arrays contain all the necessary shape
information to access it. Function \Lit{arralloc()} is implemented so
as to lay out the individual pointer and data arrays as part of a
single block of memory which is allocated by a single call to
\Lit{talloc()}. It then sets up the values of the pointers to emulate
the requested array. The memory can be freed with a single call to
\Lit{tfree().}

The \Lit{arralloc()} mechanism is unnecessary for 2D arrays where
the innermost dimensions are fixed, such as for an array of position
co-ordinates which has dimensions \Lit{[n][3]}.  In such cases one
of the \Lit{\textsl{x}alloc()} macros is used to allocate an array
with fixed innermost dimensions and whose type is a pointer to a
fixed-size array \Lit{double (*x)[3]} rather than a pointer to a
pointer \Lit{double **x}.

\begin{figure}[tb]
  \begin{center}
    \input{fig_arralloc.ftx}
  \end{center}
  \caption[Storage layout of a 2-dimensional pointer-based
    pseudo-array]{Storage layout of a 2-dimensional pointer-based
    pseudo-array. The base pointer \Lit{r} is of type ``pointer to
    pointer to double'' and declared \Lit{double **r}.  This points
    at a 1-dimensional array with length $m$ of pointers, which in
    turn point to the $m$ rows of data arrays.  This structure
    contains all the information needed to access element
    \Lit{r[i][j]} using pointer indirection rather than an indexing
    computation and therefore without any reference to the values of
    $m$ or $n$.  Higher dimensional arrays are set out in a similar
    fashion with more levels of pointer arrays, $n-1$ for an
    $n$-dimensional array}
    \label{fig:arralloc}
\end{figure}

There is one important instance of a more sophisticated use of arrays
of pointers; the allocation of the atomic-site, site-force,
centre-of-mass force and torque arrays in the main timestep loop
function \Lit{do\_step()}. These syntactically resemble 3D arrays
declared, \eg\ \Lit{site[nspecies][nsites][3]} but because the
number of sites differs between molecular species they do not map onto
``rectangular'' 3D arrays.  However the ``array of pointers''
construct does allow for rows of different lengths and this is easily
set up.  These pseudo-arrays can again be passed straightforwardly as
function arguments and used with the obvious and intuitive indexing
scheme at the cost of a little extra code to allocate and initialize
the pointers.

\subsection{System Variables and Data Structures}
\label{sec:sysvars}

\moldy\ makes consistent use of C language structures or ``structs''
to combine related pieces of data into a single variable. This may
then be passed as a single function argument avoiding long and
cumbersome argument lists and the consequent risk of programming
error.  All of the major struct types used in this way are defined in
the header file \Fname{structs.h} where each type is carefully annotated
with the meaning of its members.

The struct \Lit{control} is shared between almost all modules in the
program using external linkage.\footnote{This is analogous to a COMMON
  block in FORTRAN.} It contains the values of the parameters from the
control file --- the exact mapping is defined by the array (of
structs) \Lit{match[]} declared in source file \Fname{startup.c}.  The
values are read from the control file by function
\Lit{read\_control()} in source file \Fname{input.c} and adjusted by
\Lit{start\_up()} in \Fname{startup.c} where the timestep-related
parameters are updated and the floating-point values are transformed
into program units.  These are the only functions which alter values
in \Lit{control}.

Most of the information needed to describe the system is stored in the
struct \Lit{system} and the array of structs \Lit{species[]}.  Struct
\Lit{system} contains counters to record the total numbers of species
(\Lit{nspecies}), atomic sites (\Lit{nsites}), molecules
(\Lit{nmols}), polyatomic molecules\footnote{strictly speaking
  molecules with rotational degrees of freedom.} (\Lit{nmols\_r}) and
pointers to the arrays of dynamical variables.  Its counterpart
\Lit{species[]} is a dynamically allocated array, length
\Lit{nspecies}, of structs of type \Lit{spec\_mt} which
contains individual data for each molecular or atomic species.  This
includes the mass, moments of inertia, dipole moment, and charge, the
number of sites belonging to this species and the number of molecules.
It also contains pointers to the array of site identification numbers,
and Cartesian site co-ordinates (expressed in the molecular principal
frame) for this species and also to the arrays of dynamical variables.

The dynamical variables are stored in dynamically-allocated arrays of
total length \Lit{nmols} and \Lit{nmols\_r} which may be regarded as
the concatenation of individual arrays containing variables belonging
to each molecular species. The pointers in \Lit{species[i]}
locate the beginning of the array of variables belonging to species
\Lit{i} and those in \Lit{system} to the beginning of the entire
array.  The variables are manipulated by either route as is most
convenient (but never both within a single function).

The potential parameters are stored separately in the array
\Lit{potpar} since they refer to pairs of site types
(\emph{site-id's}).  This is an array of structs of type
\Lit{pot\_mt}, laid out as a 2-dimensional symmetric matrix of
dimension \Lit{system.max\_id} with rows and columns indexed by the
site identifiers as given in the system specification file.  In fact
it is stored in a one-dimensional array of length
(\Lit{system.max\_id})$^2$ and the index calculation is performed
explicitly when it is referenced.  Structure type \Lit{pot\_mt}
contains an array \Lit{p} for the actual parameter values.  This is
a fixed-length array with \Lit{NPOTP} members, where the constant
\Lit{NPOTP} is defined in the header file \Fname{defs.h}.  If a new
potential with more than 7 parameters is to be added to \moldy\ it
will be necessary to increase its value.  

The above primary storage of the potential parameters is convenient
for reading in and storing and rereading restart files.  However it
can not be indexed efficiently to retrieve the potential parameter
values in the critical innermost loops.  The data is therefore copied
into an expanded 3-dimensional array
\Lit{potp[max\_id][NPOTP][nsites]} in the force evaluation function
\Lit{forces()} before being used.  The masses and charges of the
various sites are stored in another array of structs
\Lit{site\_info[]}, simply indexed by the site identifier. As with the
potential parameters this is not convenient for access within the
inner force loop, so the data are expanded in \Lit{do\_step()} to fill
an array \Lit{chg[nsites]} to allow direct indexing by the loop
counter.

The two final data structures of importance are those containing
accumulated data for computing the usual averages and the radial
distribution functions.  Both databases are considered private to
their respective source modules \Fname{values.c} and \Fname{rdf.c}
and are only accessed using the function interfaces provided in those
modules.

The averages database provides a general and extensible scheme for
storing partial sums and computing rolling- and time- averages of
instantaneous values such as temperature, kinetic energy \etc\ It
consists of two parts, a linear array \Lit{av[]} of structs of type
\Lit{av\_mt} (defined in \Fname{values.c}) and an array of classified
types of data which contains pointers to \Lit{av[]}, the numbers of
components, and to format strings and units conversion factors for
output formatting and printing.  This is the compile-time struct array
\Lit{av\_info[]}, again defined in \Fname{values.c}. To compute
averages of a different quantity a new entry should be added to the
array \Lit{av\_info}, and another corresponding enum type to \Lit{enum
  av\_n} in \Fname{defs.h}.  The storage, retrieval and averages
computation functions will then recognize the new type and may be
called from within \Lit{values()} and \Lit{averages()}.

The array \Lit{av} itself is set up using the ``struct hack'' to
allocate space for the rolling average data.  The structure type
\Lit{av\_mt} contains as its final entry an array with one element
\Lit{roll[1]}.  When storage for \Lit{av} is allocated, the
amount requested is calculated \emph{as if} the member array was
declared \Lit{roll[n]} with $n$ equal to the rolling-average
interval.  The array member \Lit{roll[]} may then be used as if it
had $n$ members.  This does mean that the array \Lit{av} can not be
simply indexed using pointer arithmetic or array subscripting since
the compiler's notion of the size of a pointer of type
\Lit{av\_mt~*} is wrong.  It is instead accessed solely via the
pointers contained in array \Lit{av\_info}.  The general functions
\Lit{add\_average()} \etc\ in \Fname{values.c} provide the means to
store and retrieve values and compute averages.  The function
\Lit{av\_ptr()} is provided to make the size and base address of
the averages database available to the restart file reading and
writing functions in \Fname{restart.c}.

Radial distribution functions are calculated between all pairs of site
types (defined by the site identifier given in the system
specification file).  Storage for the accumulating binned pair
distances is provided by a 3D pseudo-array \Lit{rdf[idi][idj][bin]}
with two indices for the site identifiers of the pair and one for the
bin.  This does not have the geometry of a true 3D array but is set up
by function \Lit{init\_rdf()} to economise on memory by making
\Lit{rdf[idi][idj][bin]} an alias for \Lit{rdf[idj][idi][bin]}.
Both the pointers and the actual data area are of course dynamically
allocated.  The latter is in a single block of memory whose address is
stored in \Lit{rdf\_base} so that it may be easily stored in and
retrieved from a restart file.  The function \Lit{rdf\_ptr()} is
provided to make the size and base address of the RDF database
available to the restart file reading and writing functions in
\Fname{restart.c}.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Files and Functions}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\subsection{Source Files}
The source files consist of 8 ``\Fname{.h}'' header files and 23
``\Fname{.c}'' code files which contain functions grouped in a modular
fashion.  A detailed description of the purpose and interface to each
function is given in the source code in the form of comments, which
should be regarded as the primary documentation for the function.  An
annotated list of files follows.

\begin{Fndescription}
\item[structs.h] Definitions of structure types used throughout the
  program.
\item[defs.h] Main \moldy\ header file to be included in all
  ``\Fname{.c}'' files.  It contains machine and operating-system
  configuration macros., physical constants, unit definitions and
  global (non-structure) type definitions.
\item[string.h, time.h, stddef.h, stdlib.h] These files are
  replacements for the ANSI C library header files of the same name,
  included here for portability to pre-ANSI environments.
\item[messages.h] Error messages file containing messages in the form
  of preprocessor macros. This may allow for non-English language
  customization. 
\item[xdr.h] Includes the system headers for the usual External Data
  Representation (XDR) functions and prototypes of additional
  functions for XDR reading and writing of \moldy--specific structure
  types.
\item[accel.c] Contains \Lit{do\_step()}, which implements the
  major part of the main MD timestep procedure plus functions for
  velocity rescaling and thermostatting.
\item[algorith.c] Contains functions to implement algorithms and
  computations related to the dynamical equations of motion. This
  includes the computation of molecular centre-of-mass forces,
  torques, the Newton-Euler equations and the constant--stress and
  --temperature functions.  These functions all have an
  interface which makes no reference to the \Lit{system} or
  \Lit{species[]} structs.
\item[alloc.c] Contains functions for memory management; allocation,
  (\Lit{talloc()}) freeing (\Lit{tfree()}) and setting up of
  pointer-based multi-dimensional arrays (\Lit{arralloc()}).
\item[ansi.c] Replacements for certain functions required in any ANSI
  C standard library but missing from pre-ANSI environments.
\item[auxil.c] Machine- and OS-dependent support functions. Contains
  \emph{(a)} Fast vector arithmetic functions, including interface to
  various vector libraries as well as pure ``C'' versions. \emph{(b)}
  OS-dependent time and file manipulation functions.
\item[beeman.c] Functions implementing the separate stages of the
  modified Beeman algorithm, including basic steps and the updating of
  all the necessary dynamical variables.
\item[convert.c] Functions for converting the potential parameters and
  control file parameters from input units to program units and vice
  versa. 
\item[dump.c] Functions for managing periodic trajectory \etc\ dumps.
\item[ewald.c] Evaluates the reciprocal-space part of the Ewald sum
  for the long-ranged Coulombic forces.
\item[force.c] Implements the calculation of the short-ranged forces
  and the real-space part of the Ewald sum using the Link Cell
  algorithm. This excludes the actual evaluation of the potential
  which is contained in \Lit{kernel()} in \Fname{kernel.c}.
\item[input.c] Functions for reading the control and system
  specification files and allocating the structures to hold the data
  read in from them. 
\item[eigens.c] Matrix diagonalizer from the netlib archive.
\item[kernel.c] Contains \Lit{kernel()} which actually evaluates
  the potential given a vector of pair distances.
\item[main.c] Function \Lit{main()} is the main program which
  controls the set up and contains the main MD timestep loop that 
  calls \Lit{do\_step()} to do most of the work of a timestep.
\item[matrix.c] Functions for manipulating $3 \times 3$ matrices and
  applying to $3 \times n$ arrays of co-ordinates \etc
\item[output.c] Main output functions responsible for regular output
  sent to main output file.  Also contains error-handling function
  \Lit{message()} which may terminate simulation.
\item[parallel.c] Interface between \moldy\ and various parallel
  message-passing libraries.  Also contains functions to copy \moldy's
  data structures from the input/output node to other nodes using the
  parallel interface.
\item[quaterns.c] Functions for manipulating arrays of quaternions.
\item[rdf.c] Contains \Lit{init\_rdf()} which sets up RDF database,
  \Lit{rdf\_accum()} which periodically bins pair distances and
  \Lit{rdf\_out()} which calculates and prints all the radial
  distribution functions.
\item[restart.c] Functions for reading and writing the restart and
  backup files.
\item[startup.c] Primary initialization function \Lit{start\_up()}
  plus subsidiary functions \Lit{allocate\_dynamics()} to create
  dynamic variable arrays and \Lit{initialise\_sysdef} to compute
  molecular and whole-system properties.  Also contains functions to
  create initial configuration for system.
\item[values.c] Function \Lit{init\_averages()} allocates and sets
  up pointers to averages database.  Other functions store and
  retrieve instantaneous values of thermodynamic quantities and
  compute the averages thereof.
\item[xdr.c] Functions to read and write \moldy's own struct types
  using the External Data Representation (XDR) library.
\end{Fndescription}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Flow of Control}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Input and Initialization}
\label{sec:startup}

\begin{figure}[tb]
\noindent
\begin{minipage}[b]{260bp}
\input{fig_startup-a.ftx}
\end{minipage}
\begin{minipage}[b]{2.55in}
\raggedright
\textbf{Functions called by start\_up()}\\
\footnotesize
\setlength{\leftmargini}{1em}
\begin{Litdescription}
\item[default\_control()] Initialize struct \Lit{control} with
  default parameter values.
\item[read\_control()] Read parameters from control file and store in struct
  \Lit{control}.
\item[convert\_control()] Convert physical values in \Lit{control}
  between input and program units.
\item[read\_sysdef()] Read the system specification file. Allocate
  storage for arrays \Lit{species}, \Lit{site\_info} and
  \Lit{potpar} and copy in values from file.
\item[initialise\_sysdef()] Finish set up of \Lit{system} and
  \Lit{species} structs.
\item[allocate\_dynamics()] Allocate memory for the dynamic variable
  arrays and set up pointers in \Lit{system} and \Lit{species}
\item[skew\_start()] Set up skew-cyclic initial state.
\item[lattice\_start()] Read crystal structure and set up
  co-ordinates, quaternions and cell vectors.
\item[init\_cutoffs()] Determine optimum parameters for Ewald sum.
\item[re\_re\_header()] Read the \Lit{restart\_header} and \Lit{control}
  structs from the restart file.                                        
\item[re\_re\_sysdef()] Read the system specification (structs/arrays
  \Lit{system}, \Lit{species}, \Lit{site\_info} and
  \Lit{potpar}) from the restart file.
\item[read\_restart()] Read dynamic variables and the RDF and averages
  databases from the restart file.
\item[check\_sysdef()] Check new system specification is consistent
  with old.
\item[thermalise()] Set up Maxwell-Boltzmann velocity distribution
\item[init\_rdf()] Prepare to bin RDFs.  Allocate memory and pointers
\item[init\_averages()] Allocate space for and initialize the averages
  database.
\item[convert\_averages()] Update averages database if roll\_interval
  changed or if old-style restart file.
\item[conv\_potentials()] Convert potential params between ``input''
  and ``program'' units.
\end{Litdescription}
\end{minipage}
\renewcommand{\thefigure}{\arabic{chapter}.\arabic{figure}(a)}
\caption[Block diagram of initialization function \Lit{start\_up()}]{%
  Block diagram of the initialization function \Lit{start\_up()} and a
  list of the functions called. Continued in Figure~\ref{fig:startup-b}.}
\label{fig:startup-a}
\end{figure}

\addtocounter{figure}{-1}
\begin{figure}[tb]
\centering
\input{fig_startup-b.ftx}
\renewcommand{\thefigure}{\arabic{chapter}.\arabic{figure}(b)}
\caption[Block diagram of initialization function \Lit{start\_up()}]{%
  Block diagram of the initialization function
  \Lit{start\_up()} and a list of the functions called.  Continued
  from Figure~\ref{fig:startup-a}.  The paths beginning at \textbf{A},
  \textbf{B} and \textbf{C} are for a new run, a restart from a save
  file, and a restart from a backup file respectively.}
\label{fig:startup-b}
\end{figure}


The initialization and input file reading stage of \moldy\ is rather
more complicated than is usual in a molecular-dynamics simulation
program because
\begin{itemize}
\item of the use of dynamic data structures.  The size of the arrays
  required can only be determined after the input files have been
  partially read in, but they must be allocated before the read is
  complete. Thus reading of input files and array allocation must be
  interspersed.
\item of the general nature of the systems \moldy\ is able to simulate.
  Many structures that might otherwise be hard-coded must here be read
  from the input files and set up dynamically.
\item \moldy\ has three different modes of start-up; an initial run,
  a restart and a backup restart.  The use of the stored values of the
  control parameters as defaults on a restart run is an added
  complication. 
\item the ability to specify the input units the potential parameters
  are expressed in requires the appropriate conversion to be performed
  at start-up.
\end{itemize}

Initialization is controlled by function \Lit{start\_up()} which is
called directly from \Lit{main()}. It calls subsidiary functions to
read the control and system specification, restart or backup files.
It controls the allocation of dynamic memory for the system
description and potential and dynamic variable arrays, and the RDF and
averages databases. It oversees the computation of quantities derived
from the input parameters and system specification and generally
transforms the input data from a form useful for human preparation to
one useful for machine computation. Figures~\ref{fig:startup-a}
and~\ref{fig:startup-b} show a block diagram of \Lit{start\_up()} and
a brief description of the functions it calls.

Parameters are read from the control file by \Lit{read\_control()}
which assigns their values to the corresponding member of struct
\Lit{control}.  The default values were previously set by
\Lit{default\_control()}.  In the case of a restart the saved
parameters in the restart file are restored to \Lit{control} by
function \Lit{re\_re\_header()}, overwriting all of the current
values.  In this way the saved values become the defaults for a second
call of \Lit{read\_control()} which rereads the control file and
assigns any new values.  The repeated read is
necessary because the \emph{name} of the restart file is supplied by
the control file.  Note that those parameters representing physical
quantities must be converted to and fro between input and program
units for consistency since they are stored in program units in the
restart file.

The three alternative routes within \Lit{start\_up()} for reading the
system specification and initializing the dynamic variables \etc\ are
shown in Figure~\ref{fig:startup-b}.  The case of a new run is
reasonably straightforward.  Memory for the ``system specification''
arrays \Lit{species[]}, \Lit{potpar[]} and \Lit{site\_info[]} is
allocated as the system specification is read in by
\Lit{read\_sysdef()}.  The raw atomic site co-ordinates are then
shifted to the molecular centre-of-mass frame and rotated to the
principal frame of the inertia tensor by \Lit{initialise\_sysdef()},
which also computes a number of other molecular quantities and
completes the set up of the system specification. Next the
dynamic-variable arrays are allocated and initialized and finally the
RDF and averages databases are installed.

If the run is a restart then those control parameters whose value is
interpreted relative to the current timestep (see
section~\ref{sec:control}) must have its value added to theirs.  This
is only done if the parameter was explicitly specified in the new
control file, otherwise its saved value is untouched.  Function
\Lit{re\_re\_sysdef()} reads the system specification variables and
arrays \Lit{system}, \Lit{species[]}, \Lit{potpar[]} and
\Lit{site\_info[]} from the restart file.  These were stored in their
final form after being set up by the previous run so a call to
\Lit{initialise\_sysdef()} is unnecessary.  Alternatively a new system
specification file may be used in which case a similar sequence to
that for a new run is followed.  Memory for the dynamic variable
arrays and the RDF and averages databases must be allocated before
their saved values are restored by \Lit{read\_restart()}.

The simplest startup mode is that from a backup file.  Once it has
been determined that a backup file exists, setup proceeds much as in
the restart case except that the parameters and system specification
are restored unchanged.  Thus the run continues exactly from the
point the backup file was written. A restart file is still opened, but
only to obtain the name of a backup file to search for.

The final act of \Lit{start\_up()} is to call \Lit{banner\_page()}
to print out a nicely formatted page of information to record the
simulation details in the output file.  Control is then transferred
back to the main program where everything is now ready for the
molecular dynamics to begin.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Main timestep loop}

The initial entry point into \moldy\ is into function \Lit{main()},
which calls \Lit{start\_up()} (section~\ref{sec:startup}) and then
proceeds into the main loop over molecular-dynamics timesteps
(Figure~\ref{fig:main}).  This loop calls \Lit{do\_step()} which
computes the forces and advances the co-ordinates, velocities and
their angular counterparts by one timestep.  Most of the periodic
tasks not directly associated with progressing the simulation, such as
the accumulation of running averages, velocity rescaling and writing
the output files are called directly from \Lit{main()}.
The exceptions are the binning of site-pair distances for computing
the radial distribution functions which is integrated into the site
forces evaluation, and the writing of trajectory data to the dump file
which is called from \Lit{do\_step()} since the force arrays are
local to that function.

\begin{figure}[tb]
\input{fig_main.ftx}
\caption[The main timestep loop]{The main timestep loop in file
\Fname{main.c} showing the outermost control structures.  Periodic
analysis, trajectory dump or output tasks are all shown here including
the accumulation of the radial distribution function data which is
integrated into the link cell force calculation.}
\label{fig:main}
\end{figure}

\begin{figure}[tbp]
\centering
\input{fig_dostep-a.ftx}
\renewcommand{\thefigure}{\arabic{chapter}.\arabic{figure}(a)}
\caption[Flow  diagram of function \Lit{do\_step()} which performs
a single timestep.]{Flow diagram of function \Lit{do\_step()} which
  performs a single timestep.  (\emph{continued in
    Figure~\protect\ref{fig:dostep-b}})}
\label{fig:dostep}
\label{fig:dostep-a}
\end{figure}

\addtocounter{figure}{-1}
\begin{figure}[tbp]
\centering
\input{fig_dostep-b.ftx}
\renewcommand{\thefigure}{\arabic{chapter}.\arabic{figure}(b)}
\caption[Flow  diagram of function \Lit{do\_step()} which performs
a single timestep.]{Flow diagram of function \Lit{do\_step()} which
  performs a single timestep.  (\emph{continued from
    Figure~\protect\ref{fig:dostep-a}})}
\label{fig:dostep-b}
\end{figure}

The timestep loop continues until either the requested number of steps
have been completed, a \Lit{SIGTERM} or \Lit{SIGXCPU} signal is
received or one more step would exceed the CPU time limit set in
\Lit{control.cpu\_limit}.  Only the normal termination case is shown
in Figure~\ref{fig:main}.  In the case of an early termination
\Lit{write\_restart()} is called to write out a backup file so the
simulation may be restarted later.  The signal handling functions
\Lit{shutdown()} for \Lit{SIGTERM} and \Lit{SIGXCPU} and
\Lit{siglock()} for other signals are installed in \Lit{main()}
following the call to \Lit{start\_up()}.  Function \Lit{shutdown()}
simply sets a flag which is checked at the end of every iteration of
the timestep loop.  This allows for an orderly shutdown if the CPU
limit is exceeded or if the run must be interrupted for some other
reason. For conditions which require immediate program exit, function
\Lit{siglock()} is called to delete the lock files before exiting.

The tasks of calculating the forces, implementing the equations of
motion and updating the dynamic variables each timestep are managed by
function \Lit{do\_step()}.  The conceptually simple calculation of
forces and stepping of co-ordinates is made lengthy and less
transparent by the need to handle the rigid-molecule equations of
motion and the constant-pressure and -temperature extended system
equations.  The procedure is set out in figures~\ref{fig:dostep-a}
and~\ref{fig:dostep-b}.  The main stages of the calculation are
\begin{enumerate}
\renewcommand{\theenumi}{\emph{\alph{enumi}}}
\item update all of the centre-of-mass co-ordinates, quaternions and
  extended-system variables according to steps \emph{i} and
  \emph{ii} of equations~\ref{eqn:beeman}. This is done by function
  \Lit{step\_1()} which also applies the quaternion normalization
  and constraints of equations~\ref{eqn:qnorm} and~\ref{eqn:qconst}.
\item call \Lit{force\_calc()} and \Lit{ewald()} to
  evaluate the potential energy and the forces on all atomic sites.
  The site co-ordinates must themselves be computed from the centre-of
  mass co-ordinates and the quaternions by function
  \Lit{make\_sites()}. 
\item calculate the molecular centre-of-mass forces and torques using
  equations~\ref{eqn:comf} and~\ref{eqn:comt}  which are implemented
  in functions \Lit{mol\_force()} and \Lit{mol\_torque()}. At
  this point the accelerations from the previous timestep are
  ``shuffled down'' from the \Lit{acc}, \Lit{qddot} \etc\ arrays
  in \Lit{system} and \Lit{species[]} to \Lit{acco},
  \Lit{qddoto} to make way for the new accelerations computed in
  this timestep. 
\item calculate the internal stress (equation~\ref{eqn:prstress}) and
  the ``accelerations'' of the MD cell matrix from the
  Parrinello-Rahman equations of motion. Function \Lit{rahman()}
  implements equation~\ref{eqn:rahman}.
\item calculate the new centre-of-mass and extended-system
  variable accelerations including the velocity-dep\-end\-ent parts
  which arise in a constant-pressure or -temperature calculation.
  Iterate steps \emph{iii}--\emph{v} of equations~\ref{eqn:beeman}
  until the velocities have converged. \label{loop:vel}
\item calculate the new angular and rotational thermostat
  accelerations. The Euler equations (\ref{eqn:euler}) and
  equation~\ref{eqn:qddot} are used by function \Lit{euler()} to
  evaluate the second time-derivatives of the molecular quaternions.
  Iterate until the corresponding angular velocities have
  converged. \label{loop:quat} 
\item call \Lit{step\_2()} to apply the final step \emph{iv} of
  equations~\ref{eqn:beeman}.
\item write requested co-ordinates \etc\ to the dump file.
\end{enumerate}

The loops of steps~\ref{loop:vel} and~\ref{loop:quat} iterate until
the molecular centre-of-mass velocities and quaternion time
derivatives have adequately converged (in practice about 3 or 4
iterations).  No additional iteration is applied to converge the
extended-system dynamic variables, since in any practical system these
should be very slowly-varying compared to molecular motion and any
error introduced will be very small.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{The Site-Forces Calculation}

\begin{figure}[tb]
\noindent
\input{fig_link-cell.ftx}
\caption{The Link-Cell short-range force calculation}
\label{fig:link-cell}
\end{figure}

The evaluation of the forces on atomic sites comprises almost all of
the run-time of any reasonably sized simulation --- over 95\% for
systems of only a few hundred atoms.  Efficiency of execution is
therefore a primary consideration in this part of the code.  It is
frequently the case that a straightforward implementation of an
algorithm is not optimally fast, which regrettably means that
the optimized code is not as transparent to read as for other, less
critical parts of the program.

The link cell algorithm for the short-ranged part of the forces
(section~\ref{sec:link-cell}) is one which scales very well to large
systems.  In its original form~\cite{quentrec:75} the MD cell is
divided into a number of ``subcells'' and a linked-list
structure~\cite{knuth:73a} lists the molecules or atoms belonging to
each subcell.  The inner loops of the force calculation must therefore
use an \emph{indirect} indexing operation to obtain the co-ordinates
on every iteration: the index of a molecule on each iteration depends
on the index of the previous iteration.  This kind of loop is
inherently un-vectorizable which grossly limits the efficiency on
vector computers.  

\begin{figure}[tb]
\noindent
\input{fig_ewald.ftx}
\caption{Evaluation of the reciprocal-space parts of the ewald sum}
\label{fig:ewald}
\end{figure}

To overcome this limitation a further step is needed, as suggested by
Heyes and Smith~\cite{heyes:87}.  Inside the loop over subcells, the
linked list is pre-scanned and used to construct an array containing
the indices of the molecules in the list.  This is known as the
\emph{neighbour list} since it points to the molecules in the region
of the subcell under consideration. The co-ordinates are then
assembled into a contiguous array using a \emph{gather} operation.  The
inner force loop then becomes a straightforward DO loop over this
temporary array which \emph{is} vectorizable.  In addition to the
co-ordinates, a corresponding gather operation using the same neighbour
list operates on the electric charge and potential parameter arrays.
The corresponding temporary arrays are also accessed in the inner loop
over neighbour sites with a single array subscript.  Finally the
computed forces are added to the main site force arrays using a
\emph{scatter} operation, inverse to the gather.

Although this algorithm was designed for vector machines, it is also
highly efficient on modern RISC processors which universally employ a
cache-based memory architecture.  Since adjacent iterations of the
inner loop access adjacent locations in memory, loading a cache line
will bring the operands of the next few iterations into cache.
Otherwise a (very slow) cache load would be necessary on every loop
iteration. 

The link cell functions are all in \Fname{force.c}
(figure~\ref{fig:link-cell}).  The outer-level function is
\Lit{force\_calc()}.  Function \Lit{neighbour\_list()}
constructs a list of subcells within the cutoff radius of a reference
cell.  By adding an offset and applying periodic boundary conditions
this list yields the neighbour cells of \emph{any} subcell.  This
should not be confused with the neighbour \emph{sites} list above.  In
fact the list contains only a hemisphere of cells since Newton's third
law is exploited to halve the computational cost.  A more rigorous
list may be computed instead by \Lit{strict\_neighbour\_list()}
which is selected in strict cutoff mode (see
section~\ref{sec:strict-cutoff}).  Next an array of potential
parameters called \Lit{potp} is constructed.  This has an innermost
dimension of the number of sites, which maps one-to-one onto the
co-ordinates array. It will be used as the argument of the gather
operation using the site neighbour list.  Function
\Lit{fill\_cells()} constructs the linked list of molecules in each
subcell.

The loops over cells are all contained in \Lit{force\_inner()}.
Within the loop over subcells the neighbour list of \emph{sites} for
this cell is constructed from the list of neighbour \emph{cells} by
\Lit{site\_neighbour\_list()}.  This list is then used as the index
array to gather the co-ordinates, charges, potential parameters and
any periodic boundary vectors.  The innermost loops compute the site
pair distances, the potential and the forces on the neighbour list
sites.  The actual potential and scalar-force evaluation is delegated
to function \Lit{kernel()} for ease of comprehension and modification.
This takes as its arguments an array of squared pair distances and
corresponding arrays of potential parameters and site charges and
returns the potential energy and an array containing the scalar part
of the force, $\phi'(r_{ij})/r_{ij}$. The structure of \Lit{kernel()}
is designed to evaluate all the different kinds of potential functions
(see section~\ref{sec:potentials}) as efficiently as possible.  It
contains separate versions of a vector loop for each distinct
potential type, both with and without electrostatic charges.  For the
charged case, the complementary error function is approximated to an
accuracy of approximately 1 part in $10^{-6}$ by the formula given in
Abramowitz and Stegun\cite[section 7.1.26]{abramowitz:70}.

Pair distances for the calculation of radial distribution functions
are evaluated by \Lit{rdf\_inner()} whose structure
resembles a simplified \Lit{force\_inner()}. It does not call
\Lit{kernel()} or compute forces but instead calls
\Lit{rdf\_accum()} which bins the computed pair distances. The
separate function allows the use of a larger cutoff than for the force
and potential evaluation.

The reciprocal-space part of the Ewald sum is evaluated in function
\Lit{ewald()}, shown in figure~\ref{fig:ewald}.  This is a fairly
straightforward implementation of the \emph{k}-space terms of
equations~\ref{eqn:ewald}, \ref{eqn:ewald-force}
and~\ref{eqn:ewald-stress}, and differs from common implementations
only in the respect that it must correctly handle
parallelepiped-shaped MD cells and therefore a triclinic grid of
\emph{k}-vectors.  A list of vectors satisfying $\bm{k} > \bm{0}$ and
$|\bm{k}| < k_c$ is pre-computed to simplify the control condition of
the main \emph{k}-vector loop.

Again, computational efficiency dictates much of the structure of
\Lit{ewald()}.  The values of $\cos(\bm{k \cdot r}_i)$ and $\sin(\bm{k
  \cdot r}_i)$ must be computed for every site~$i$ and every vector
$\bm{k} = h \bm{a}^* + k \bm{b}^* + l \bm{c}^*$ with $|\bm{k}| < k_c$.
Since the cosine and sine functions are relatively expensive to
evaluate, the values of $\cos(h \bm{a}^*\bm{ \cdot r}_i)$, $\cos(k
\bm{b}^*\bm{ \cdot r}_i)$ \etc\ are precomputed for each site~$i$ and
$h = 0, 1, \ldots h_{\textnormal{max}}$, $k = 0, 1, \ldots
k_{\textnormal{max}}$ and stored in arrays of dimension
\Lit{[hmax][nsites]} \etc\ Then within the loop over \emph{k}-vectors
the trigonometric addition formulae are used to construct $q_i
\cos(\bm{k \cdot r}_i)$ and $q_i \sin(\bm{k \cdot r}_i)$ using only
arithmetic operations.  That task is delegated to function
\Lit{qsincos()} since certain compilers can optimize it better that
way.  The self-energy and charged-system terms of
equations~\ref{eqn:ewald} and~\ref{eqn:ewald-stress} are also
evaluated in \Lit{ewald()}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Parallelization}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
It has been predicted since the early 1980s that parallel computers
would offer the very highest performance for scientific computing.
That vision has been somewhat slow in coming about, in part due to the
difficulty of writing programs to explicitly exploit unique and
idiosyncratic architectures and the lack of portability or
re-usability of the resulting code.  It was often necessary to write
programs in machine-specific languages~\cite{pawley:82,bowler:87}, use
proprietary and obscure library calls~\cite{clementi:85} and be
designed around a specific communications topology.  Nevertheless
molecular-dynamics simulation was one of the earliest applications of
parallel computers and many simulations have been undertaken despite
the difficulties~\cite{pawley:82,clementi:85,rapaport:88}.

The emergence and widespread adoption of the single-program
multiple-data (SPMD) programming model and the standardization of
parallel communications libraries in the 1990s has much improved the
situation.  In this model a parallel program is treated as a set of
copies of a single program executing asynchronously and independently
on different processors and which communicate using library calls.
The program is written in a standard language and arranged to divide
the calculation among the processors in some way so that each handles
a subset of the operations. The model is completed by the addition of
a library of communications routines which allow the separate parts of
the calculation to be assembled to give the full, desired result. A
significant advance was the availability of parallel libraries such as
TCGMSG~\cite{tcgmsg:94}, PVM~\cite{pvm:94}, BSP~\cite{bsp:93} and the new
standard MPI~\cite{mpi:94}.  Together these developments allow a program
to be both parallel and sufficiently independent of architectural
details to be portable to a wide range of parallel environments.
These may include shared-memory and distributed-memory
multiprocessors, and even networks of workstations.  

Of course formal portability is no guarantee of good performance on
hardware with widely differing processor and communications
performance. That is a function of the granularity of the problem with
respect to communications latency and volume of data with respect to
communications bandwidth. Both of these depend on the nature of the
problem and the parallelization strategy.

\subsection{Parallel Strategy}
\label{sec:parstrat}
\moldy\ uses the ``replicated data'' approach to parallel molecular
dynamics~\cite{clementi:85,smith:91} whereby each processor maintains
a complete set of dynamical variable arrays for all particles.  The
short-ranged and reciprocal-space force calculations are divided among
the processors and a global sum operation is used to add the separate
contributions to the forces and propagate a copy of the complete force
array to every processor.  This ``parallel'' part of the calculation
dominates the execution time and shows a close to linear speed-up. It
takes over 95\% of the CPU time even for quite small systems of a few
hundred particles.  The remainder of the calculation, in particular
the integration of the equations of motion is not parallelized but is
performed redundantly on every processor simultaneously.\footnote{It
  is essential that the particle co-ordinates are identical on every
  processor and remain so throughout the simulation run.  Since the
  equations of motion are chaotic the tiniest numerical difference in
  the forces between processors will cause the trajectories to
  diverge.  It is not difficult to arrange that a global sum returns
  results identical to the last decimal place on all processors and
  this is guaranteed by the BSP, and cray SHMEM implementations and
  strongly recommended by the MPI standard.  \moldy\ relies on this
  behaviour to ensure that no divergence occurs.  If this were not the
  case it would be necessary to add a step to synchronize the
  co-ordinates periodically.  This may also be an issue for running on
  networks of workstations - it is assumed that the numerical
  properties of all the processors are identical.  The equality of the
  separate copies of the co-ordinates is checked periodically and the
  simulation exits if divergence is detected.} This ``serial'' part of
the code will therefore limit the parallel speedup on large numbers of
processors by Amdahl's law. However this part of the computational
work only grows as $N$ compared with $N^\frac{3}{2}$ for the forces
calculation (see section~\ref{sec:ewald-auto}).  Consequently the
``serial'' part becomes an increasingly insignificant fraction of the
computation as the system size increases.  Since one might reasonably
expect that in practice $N$ is scaled with the number of processors
the serial part does not seriously limit the parallel performance.

The advantages of the replicated data strategy are firstly simplicity
of programming.  Most of the code is identical to the serial program
which greatly eases maintenance.  Secondly load-balancing is much more
straightforward than with domain decomposition methods.  This will be
discussed below.  Thirdly the communications granularity is coarse,
consisting of one global summation per timestep.  This makes it very
suitable for loosely-coupled processors such as a workstation cluster
where the communications \emph{latency} is high.  Finally it is very
efficient for small and medium-sized systems.

The big disadvantage is that the scaling to very large numbers of
processors is poor, which will eventually limit the size of system
which can be simulated.  This is because both the amount of memory
used per processor and the total amount of data to be communicated per
processor increase linearly with $N$, the number of particles in the
system.  However in comparison with other supercomputing applications,
molecular dynamics simulation uses relatively little memory and most
supercomputers can easily accommodate simulations of many thousands of
particles.  Though scaling to multi-million particle systems
would be desirable, most practical simulations of solids or liquids
can be accomplished using systems of a few tens of thousands of
particles or fewer.

\subsection{Implementation}

Two of the design goals are that the serial and parallel versions of
\moldy\ can be built from the same code, and that the code is easily
portable to a number of communications libraries.  The C preprocessor
macro \Lit{SPMD} is used to conditionally include the parallel
code. All calls to the parallel library interface are protected by
conditional-compilation and if \Lit{SPMD} is undefined at compile
time then the serial version is built.  These calls are limited to a
very few modules --- \Fname{main.c} for the parallel set-up,
\Lit{do\_step()} in \Fname{accel.c} for the global summation of the
forces, virial and potential energy and \Lit{message()} in
\Fname{output.c} for error handling.  Furthermore the communications
library is not called directly, but through interface functions
defined in file \Fname{parallel.c} (these are listed in
section~\ref{sec:parport}).  This means that only \Fname{parallel.c} need
ever be modified if a port to a different communications library is
needed, as the rest of the code sees a common interface.  As supplied
\Fname{parallel.c} contains implementations for the MPI library, the
TCGMSG library, the Oxford BSP library and the Cray SHMEM library for
the T3D/T3E series machines (see section~\ref{sec:spmdpar}).  One of
these is selected by conditional compilation of \Fname{parallel.c} with
one of the preprocessor symbols \Lit{MPI}, \Lit{TCGMSG},
\Lit{BSP} or \Lit{SHMEM} defined.

\subsection{Input/Output and startup}
\label{sec:pario}

Since each processor runs an identical copy of the program executable,
steps must be taken to ensure that input and output are handled by a
single processor.  Otherwise a parallel run would print P copies of
every line if running on P processors.  Indeed there are some parallel
systems on which only one processor is allowed to perform input or
output. It follows that all parts of the code which perform I/O must
be aware of which processor this instance is running on.  This is done
using a global integer variable, \Lit{ithread} which contains
the index (or ``rank'') of the processor.  Another variable
\Lit{nthreads} contains the value of P.  These are set up in
function \Lit{par\_begin()} called from \Lit{main()} and passed
by external linkage to every module which needs them.  Processor 0
performs all input/output, which is arranged by testing
\Lit{ithread} prior to any I/O call, \eg\\
\parbox{\textwidth}{%
\begin{quote}
  \Litf
      if( ithread == 0 \&\& control.istep \% control.print\_interval == 0)\\
\hspace*{1cm}          output();
\end{quote}}\\
which is the code in \Lit{main()} used to print the regular output.
These variables are also present in the serial code, where they are
harmlessly redundant. In that case \Lit{ithread} is always set to 0
and \Lit{nthreads} to 1.

\Fname{Parallel.c} also contains the function \Lit{replicate()} to
pass the system specification and dynamical variables from processor 0
to all of the others.  This is called from \Lit{main()} after
\Lit{start\_up()} has returned.  It allocates memory for the system
specification and dynamical variables on all other processors using
the same functions as \Lit{start\_up()} itself and calls
\Lit{copy\_sysdef()} and \Lit{copy\_dynamics()} to broadcast the
data from processor 0 to all of the others.  The function
\Lit{par\_broadcast()} defined earlier in \Fname{parallel.c}
calls the underlying communications library to broadcast the
data globally.  On exit from \Lit{replicate()} \emph{every}
processor now contains a complete set of data and is ready to begin
the run.

Error or warning conditions are handled by function \Lit{message()}
in \Fname{output.c} if called with the \Lit{FATAL} or \Lit{WARNING}
flags set in its argument list.  In the serial case this prints out a
failure message and calls \Lit{exit()} to terminate the run.  In a
parallel run, an error condition may occur on just one processor or on
all simultaneously depending on precisely what caused the problem.
Furthermore, if just one processor is involved, it may not be the I/O
processor (rank 0).  The approach of printing the message only from
processor 0 is inadequate since there would be no indication that
something has gone wrong.  But neither is it appropriate to print on
all, since that would result in a multiplicity of messages in some
cases.  The approach taken is that \Lit{message()} always prints
the error irrespective of which processor is executing it, but that
calls to \Lit{message()} are made conditional on
\Lit{ithread~==~0} for those conditions known to occur on all
processors simultaneously. If the condition signalled was
\Lit{FATAL} then \Lit{message()} finally calls \Lit{par\_abort()}
which terminates the run on all processors.

\subsection{Distributed forces calculation}

The two parts of the forces calculation which must be parallelized are
the short-ranged forces calculation and the reciprocal-space part of
the Ewald sum.  The outermost loop of the link cell forces calculation
in function \Lit{force\_inner()} runs over all subcells within the
simulation cell (see figure~\ref{fig:link-cell}).  In the serial case, 
it has the form\\
\parbox{\textwidth}{%
\begin{quote}
  \Litf
  for( icell = 0; icell < nx*ny*nz; icell++)\\
  \{\\
  \hspace*{1cm}\textnormal{[\emph{Evaluate forces on particles in cell}
  \Lit{icell}]}\\ 
    \}.
\end{quote}}\\
The iterations of this loop are distributed over processors by
rewriting it as\\
\parbox{\textwidth}{%
\begin{quote}
  \Litf
  for( icell = ithread; icell < nx*ny*nz; icell += nthreads)\\
  \{\\
  \hspace*{1cm}\textnormal{[\emph{Evaluate forces on particles in cell}
  \Lit{icell}]}\\ 
    \}.
\end{quote}}\\
where the integer variables \Lit{ithread} and \Lit{nthreads} contain
the rank of the processor and the total number of processors
respectively. Since \Lit{ithread} has a different value on each
processor, the separate copies of the force arrays contain only the
contributions from the set of subcells assigned to that processor.
After completion of the forces calculation the individual arrays are
summed in function \Lit{do\_step()}.  Similar partial contributions to
the potential energy and the stress are also globally summed.
Provided that the number of subcells is rather greater than the number
of processors and the number of particles per subcell is fairly
constant, this algorithm automatically ensures that the computation is
divided evenly between the processors.

The reciprocal-space sum may be parallelized either by distributing
the loop over \emph{k}-vectors over
processors~\cite{clementi:85,smith:92} or by partitioning the
particles between processors. Smith~\cite{smith:92} called the former
strategy the ``reduced k-vector list'' (RKL) method and the latter the
``reduced ion list'' (RIL) method.  \moldy\ contains implementations
of both methods.  In the RKL method, it is necessary to pre-compute a
list of \emph{k}-vectors within the cutoff sphere (see
figure~\ref{fig:ewald}) which is stored in the array \Lit{hkl[]}.
(Each element of \Lit{hkl[]} is a struct containing the values of $h$,
$k$, $l$ and the vector components of $\bm{k}$.) The loop over
\emph{k}-vectors then takes the form of a single loop over this list,
which is easily sliced over processors exactly as the loop over
subcells above.\\
\parbox{\textwidth}{%
\begin{quote}
  \Litf
   for(phkl = hkl+ithread; phkl < hkl+nhkl; phkl += nthreads)\\
   \{\\
\hspace*{1cm}\textnormal{[\emph{Compute the contribution of
   \emph{k}-vector in} \Lit{phkl}]}\\
   \}.
\end{quote}}\\
At the end of this loop the force arrays contain the contributions to
the force on each site from the particular set of \emph{k}-vectors
handled by that processor.  The total forces are obtained by globally
summing these arrays over processors.  In fact since the same arrays are
used to store both the short-ranged and long-ranged contributions to
the force then only one global sum is needed for the forces.  Since
the contribution at each \emph{k}-point is a sum of fixed length and
takes exactly the same time to evaluate as any other, this algorithm
is always optimally load-balanced.

There is also an implementation of the alternative, RIL
parallelization strategy in file \Fname{ewald-RIL.c}, which is a direct
replacement for \Fname{ewald.c}.  In this case each processor handles a
subset of the particles and all \emph{k}-vectors.  This involves an
extra global sum within the loop over \emph{k}-vectors to evaluate
the total structure-factor for each $\bm{k}$.  

\subsection{Radial Distribution Functions}

The accumulation of distances for the radial distribution function
calculation is potentially costly since it is a site-pair property. It
is therefore handled using the parallel link cell method exactly as
for the site forces calculation by functions \Lit{rdf\_inner()} and
\Lit{rdf\_accum()}.  However the contributions from different
processors do \emph{not} need to be summed every time
\Lit{rdf\_inner()} is called, but may be left to accumulate
separately.  The data is globally summed by function
\Lit{par\_isum()} only when \Lit{rdf\_out()} is called to
calculate and print the RDFs or just before a restart file is written.

\appendix
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Example System Specifications}  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\label{sec:examples}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Argon}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{verbatim}
# LJ Argon - about as simple as you can get
# Parameters from Allen and Tildesley Table 1.1
Argon 108
1          0           0          0    39.948 0 Ar
end
Lennard-Jones
1 1 3.984 3.41
\end{verbatim}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{TIPS2 Water}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
This is the four-site water model of Jorgensen\cite{jorgensen:82}.  
Only the oxygen site interacts via the Lennard-Jones
potential, and the charge site, M, is displaced 0.15\AA\ from the Oxygen.
\begin{verbatim}
# Modified TIPS2 water
Water 64
1          0            0          0 16    0 O
2  0.7569503            0 -0.5858822 1 0.535 H
2 -0.7569503            0 -0.5858822
3          0            0      -0.15 0 -1.07 M
end
lennard-jones
1 1 0.51799  3.2407
end
\end{verbatim}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Aqueous MgCl$_2$ Solution}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
This is a three-component system consisting of MCY
water\cite{matsuoka:75}, magnesium and chloride ions.  The Mg$^{2+}$
potential was fitted to the SCF calculations of Dietz and
Heinzinger\cite{dietz:82} and the Cl$^-$ to the calculations of
Kistenmacher, Popkie and Clementi\cite{kistenmacher:73b}.  Note that
the potential parameters are expressed in kcal mol$^{-1}$, and the
control file must set the parameter \Lit{time-unit=4.8888213e-14}.
\begin{verbatim}
# MCY Water/ Mg2+ / Cl - solution
Water 200
1          0            0          0 16    0 O
2  0.7569503            0 -0.5858822 1     0.717484 H
2 -0.7569503            0 -0.5858822
3          0            0    -0.2677 0 -1.434968 M
Magnesium 4
4          0            0          0 24.31 2 Mg2+
Chloride  8
5          0            0          0 35.45 -1 Cl-
end
MCY
1 1 1088213.2   5.152712        0               0
1 2 1455.427    2.961895        273.5954        2.233264
2 2 666.3373    2.760844        0               0
1 4 47750.0     3.836           546.3           1.253 # New values of Mg potl
2 4 111.0       1.06            0               1.0   
1 5 198855.0    3.910           0               0
2 5 1857.0      2.408           77.94           1.369
4 5 28325.5     2.65            0               0
end
\end{verbatim}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Quartz}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{verbatim}
# Quartz parameters from Van Beest, Kramer and Van Santen
# Physical Review Letters 64,(16) p1955 (1990)
# Units are eV, A, el chg. so time-unit=1.0181e-14
Oxygen 384
1       0       0       0       16      -1.2    O
Silicon 192
2       0       0       0       28.0855 2.4     Si
end
buckingham
1 1     175.0000        1388.7730       2.76000
1 2     133.5381        18003.7572      4.87318
2 2     0.0             0.0             0.0
end
4.903 4.903 5.393 90 90 120 4 4 4
Oxygen   0.415000     0.272000     0.120000
Oxygen   0.857000     0.5850000    0.453300
Oxygen   0.728000     0.143000     0.453300
Oxygen   0.143000     0.728000     0.880000
Oxygen   0.272000     0.415000     0.546700
Oxygen   0.5850000    0.857000     0.213300
Silicon  0.465000     0            0
Silicon  0.535000     0.535000     0.333300
Silicon  0            0.465000     0.666700
end
\end{verbatim}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\chapter{Utility Programs} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Seven utility programs are included in the \moldy\ package, mostly for
manipulation and analysis of dump data (see section~\ref{sec:dumping}).
They are easily compiled on unix systems using the makefile: the
command is ``\Lit{make}~\emph{progname}'' for each program or
``\Lit{make utilities}'' to make the lot.  They are written with
unix systems in mind using unix-style option arguments, but ought to
compile and run under VMS if defined as foreign commands.

Several of them require you to specify lists of (integer) numbers, for
example selected molecules, timeslices \etc, which share a common
syntax for such options. The numbers 1, 5, 17 to 20 inclusive and
34,44,54\ldots94 are selected by the command-line argument
\mbox{\Lit{-t 1,5,17-20,34-100:10}}.  Selectors are separated by
commas and each one may be a range separated by a hyphen with an
optional increment following a colon.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Moldyext}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
It is usual to plot the instantaneous values of energy, temperature
\etc\ during the course of a simulation, for example to monitor the
approach to equilibrium.  \emph{Moldyext} processes the periodic
output produced by \moldy\ (see section~\ref{sec:output}) and extracts
this information from each timestep recorded.  It is presented in
tabular form for input into plotting programs.  The command is
\begin{center}
\Lit{moldyext -f} \textit{fields output-file1 output-file2} \ldots
\end{center}
where \emph{fields} is a list in the selector format above.  The
numbering runs from left to right, ignoring newlines and blank columns
so that the translational KE's in figure~\ref{fig:output} are fields 1
and 14 and the $\sigma_{zz}$ component of the stress tensor is 30.

\emph{Moldyext} can currently only extract information from the
``instantaneous'' section of the output, not the rolling averages or
standard deviations.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Dumpanal}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\emph{Dumpanal} examines the dump files given as arguments and
prints out the header information to help with identification. For
example
\begin{center}
\Lit{dumpanal} \textit{dump-file1 dump-file2} \ldots
\end{center}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Dumpconv}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\label{sec:dumpconv}
\emph{Dumpconv} is a tool for moving binary dump files between
computers of different architectures.  It has mostly been superseded
by the portable XDR format dump files introduced in version~2.1 (see
section~\ref{sec:xdr}) but is retained in case of machines for which
no XDR implementation is available.  The command
\begin{center}
\Lit{dumpconv} \textit{binary-dump-file text-dump-file} 
\end{center}
creates a straightforward ASCII text file with a representation of the
dump information in the binary file including the header.  The command
\begin{center}
\Lit{dumpconv -d} \textit{text-dump-file binary-dump-file} 
\end{center}
converts it back.  Seven significant decimals are used which ought to
retain almost all of the precision of the single precision binary
floating-point numbers.  You can also convert an old ``native'' format
dump into XDR format using the \Lit{-x} option, \emph{viz}
\begin{center}
\Lit{dumpconv -x} \textit{native-dump-file xdr-dump-file}. 
\end{center}
The \Lit{-x} and \Lit{-d} options may be combined if the input is a
text format dump.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Dumpext}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Dumps are designed so that \moldy\ can take care of all the
bookkeeping, perform data security checks and to divide a lot of data
into manageable portions.  It is therefore not in a convenient form
for reading by other programs, especially FORTRAN ones.
\emph{Dumpext} is a program which processes dump files, extracts a
subset of the information and outputs it in a form more suitable for
reading by other programs.  It is invoked with the command:
\begin{center}
\Lit{dumpext -R} \textit{nmols} \Lit{-Q} \textit{nquats} 
\Lit{-c} \textit{components}
\Lit{[-t} \textit{timeslices} \Lit{]} \\
\Lit{[-m} \textit{molecules} \Lit{]}
\Lit{[-o} \textit{output-file} \Lit{]}
\Lit{[-b]} 
\textit{dump-file1 dump-file2} \ldots
\end{center}
The meanings of its arguments are
\begin{Argdescription}
\item[-R] the total number of molecules.  This argument is compulsory.
\item[-Q] the number of polyatomic molecules.  This argument is compulsory.
\item[-c] which pieces (``components'') of the information in dump
record to extract.  This is a selector-format list and may list any
combination of
\begin{enumerate}
\itemsep=0pt
\parskip=0pt
\item C of M positions
\item quaternions
\item unit cell matrix
\item potential energy
\item C of M velocities
\item quaternion derivatives
\item unit cell velocities
\item C of M accelerations
\item quaternion accelerations
\item unit cell accelerations
\item C of M forces
\item torques
\item stress tensor
\end{enumerate}
This argument is compulsory.
\item[-t] which timeslices (or dump records) to extract.  This is a
selector format list and is the index in the whole dump sequence, not
just a single file.  Timeslices or dump records are sequentially
numbered in the dump files from 1.  Defaults to all timeslices.
\item[-m] extract information for selected molecules.  This is a
selector list specifying the molecule index.  Defaults to all
molecules.
\item[-o] name of optional output file.  Defaults to standard output.
\item[-b] selects binary output in single-precision floating point
numbers. Defaults to ASCII formatted numbers.
\end{Argdescription}
If any of the compulsory arguments are omitted, you will be prompted
to supply a value.  In particular you must \emph{always} supply the
number of molecules and polyatomic molecules.  (This information is
not recorded in the dump header and is needed to determine the number
of co-ordinates and quaternions in each dump record.)  You must
specify which pieces of information to extract using the \Lit{-c}
option.

The dump files must, of course, be part of the same dump sequence;
this is carefully checked.  They may be supplied as arguments in any
order; \emph{dumpext} automatically determines the sequence from
the information in the headers.  This is not as pointless as it sounds,
since the list may be generated using unix shell wild-cards which arrange
them in alphabetical rather than numeric order.

The output is arranged in columns, one line per time slice.  So if,
for example you wish to extract positions and quaternions there will
be 7 columns corresponding to $x,y,z,q_0,q_1,q_2,q_3$. Multiple
components are printed in the integer order of the component,
\emph{not} the order specified with \Lit{-c}. If binary output is
asked for with the \Lit{-b} option the order is the same. The numbers
are written sequentially as single-precision floating-point numbers
(in machine native format) without record delimiters.  The records may
be easily read in C with an \Lit{fread()} call with the appropriate
number of bytes or from FORTRAN77 using direct-access unformatted
read\footnote{The usual FORTRAN sequential unformatted read is not
  suitable as this expects record size information to be present.
  \emph{Dumpext} can not write this as its format is entirely
  determined by the whim of the compiler writer.  FORTRAN unformatted
  sequential files are not guaranteed to be portable even between
  different compilers on the same machine.} of the appropriate length
records\footnote{Be aware that the FORTRAN77 standard does not
  guarantee what units the record length is specified in.  Predictably
  some manufacturers' compilers use bytes and others words.  Consult
  the documentation!} \Lit{OPEN(\ldots,ACCESS=DIRECT,LRECL=nnn)}.  In
the above example there are $3 \times 4 + 4 \times 4 = 28$ bytes in
each record.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Mdshak/Mdxyz}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\emph{Mdshak} was written as an interface between \moldy\ 
configurations and a molecular graphics program called
SCHAKAL\cite{schakal:88}.  It will also, optionally, write the output
in XYZ form, suitable for input to the freely available viewers
\emph{Xmol} and \emph{RasMol}.  The output format is sufficiently
transparent that it should not be hard to modify it for any other
molecular graphics package, but note that the \emph{babel} package,
also freely available on the Internet will convert between the XYZ
file format and many others. There is also a SCHAKAL file viewer
called ``read\_shak'' written by the author for use with AVS, the
general purpose visualization program, and available free from the
International AVS Center.\footnote{It may be obtained using anonymous
  ftp from \Lit{avs.ncsc.org} or by writing to The International AVS
  Center, PO Box 12889, 3021 Cornwallis Road, RTP, NC 27709, USA.}

\emph{Mdshak} writes a SCHAKAL file containing the co-ordinates of a
single or of multiple time slices during a MD run.  It can read
configurations from (\emph{a}) a system specification file plus
lattice start, (\emph{b}) a system specification file plus dump file
(\emph{c}) a restart file or (\emph{d}) a restart file plus dump file.
It can be driven either interactively or using command-line arguments.
If the executable file is named \Fname{mdshak} then the default output
is in SCHAKAL file format. If it is renamed to \Fname{mdxyz} then the
default is XYZ file format.  

In interactive mode you are prompted for the source of the system
specification (sys-spec or restart file) and the configurational
information (restart or dump file).  But you must either redirect the
output (using \Lit{>} on unix) or specify an output file with \Lit{-o}
or the output will go to the screen!

Alternatively, the command line options are
\begin{center}
\Lit{mdshak}
\Lit{[-s} \textit{system-specification} \Lit{]|}% 
\Lit{[-r} \textit{restart-file} \Lit{]}
\Lit{[-d} \textit{dump-file-format} \Lit{]}
\Lit{[-t} \textit{dump-range} \Lit{]} \\
\Lit{[-c]}
\Lit{[-x]|[-b]}
\Lit{[-i} \textit{extra-text} \Lit{]}
\Lit{[-o} \textit{output-file} \Lit{]}
\end{center}
where the meanings of the options are
\begin{Argdescription}
\item[-s] read a system specification file
\item[-r] read a restart file.  Only one of \Lit{-s} or \Lit{-r} may
be given.
\item[-c] if reading a system specification file, skip any preceding
control parameter info.
\item[-d] read the configurational information from a dump file.  The
argument is a dump prototype name containing a \Lit{printf()} format
string -- see section~\ref{sec:dumping}.
\item[-t] range of records in dump file, specified in ``selector''
format.
\item[-i]   this inserts its argument into the output file and is used
to add extra SCHAKAL commands, such as BOX.
\item[-x] Write an output file in XYZ format suitable for \emph{Xmol}
  or \emph{RasMol}
\item[-b] Write the atom co-ordinates in raw binary (unformatted) format.
\end{Argdescription}

\emph{Mdshak} may be useful for more than just visualization purposes
as it extracts atomic co-ordinates from the system specification,
positions and quaternions.  It is written so that only the output routine
(called \Lit{schakal\_out()}) is SCHAKAL specific.  This is quite
short and can easily be replaced with one tailored to a different
purpose.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Msd}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\emph{Msd} is a utility for calculating the mean square displacements
of selected species from the dump files written during the course of a
Moldy run. A plot of msd against time enables the diffusion
coefficient of that species to be calculated from the gradient using
the Einstein relation
\begin{equation}
   <|\bm{r}(t)-\bm{r}(0)|^2> = 6 D t
\end{equation}
(see Allen and Tildesley\cite[p60]{allen:87}).

For a species of $N$ particles, the mean square displacement is
calculated as
\begin{equation}  
        <|\bm{r}(t)-\bm{r}(0)|^2> = \frac{1}{N N_t}
        \sum_{n=1}^{N} \sum_{t_0}^{N_t}
        |\bm{r}_n(t+t_0) - \bm{r}(t_0)|^2
\end{equation}

where $\bm{r}_n(t)$ is the position of particle $n$ at
time $t$.  In interactive mode you are prompted for the source of the
system specification (sys-spec or restart file) and the dump file for
reading the configurational information. You will also be prompted to
supply the dump range limits and msd time interval limits before the
calculation can commence. Be careful to either redirect the output
(using \Lit{>} on unix) or specify an output file with \Lit{-o},
otherwise the output will go to the screen!  More options can be
specified in command line mode:

\begin{center}
\Lit{msd}
\Lit{[-s} \textit{system-specification} \Lit{]|}% 
\Lit{[-r} \textit{restart-file} \Lit{]}
\Lit{[-d} \textit{dump-file-format} \Lit{]}
\Lit{[-t} \textit{dump-range} \Lit{]} \\

\Lit{[-m} \textit{msd-time-range} \Lit{]}
\Lit{[-i} \textit{initial-time-increment} \Lit{]}
\Lit{[-g} \textit{species-range} \Lit{]}
\Lit{[-u]}
\Lit{[-w} \textit{trajectory-output-format} \Lit{]}
\Lit{[-x]} \Lit{[-y]} \Lit{[-z]}
\Lit{[-o} \textit{output-file} \Lit{]}
\end{center}

where meanings of the options are
\begin{Argdescription}
\item[-s] read the species data from a system specification file.
\item[-r] read the species data from a restart file. Only one of
  \Lit{-s} or \Lit{-r} may be given.
\item[-d] read the centre of mass coordinates from a dump file given as a
prototype name containing a \Lit{printf()} format string -- see
section~\ref{sec:dumping}. 
\item[-t] range of records in dump file, specified in ``selector'' format.
\item[-m] time intervals to calculate the msd values for, specified in
  ``selector'' format.
\item[-i] the increment the initial time, $t_0$, is increased by during
calculation of msd for a given time interval. Defaults to 1.
\item[-g] range of species to be included, given in ``selector''
  format where \Lit{0} represents the \emph{first} species. Defaults
  to all species. 
\item[-u] option to by-pass msd calculation and output the connected particle
trajectories in the format selected by \Lit{-w}. 
\item[-w] selects output format for trajectory data. Set to 
        \Lit{0} for format readable by GNUPlot (default) or
        \Lit{1} for generic format (simple matrix)
      \item[-x, -y, -z] indicate that limits are to be specified in
        the given direction. Only particles whose positions in the
        first timeslice lie within these limits are included in the
        trajectory output. The user is prompted at run time for the
        limits. The default is to include all particles.
\item[-o] name of optional output file. Defaults to standard output.
\end{Argdescription}


The output format for msd calculations consists of four columns
corresponding to the $x, y, z$ components and total msd, with
increasing time intervals down the columns.

The time intervals specified by \Lit{-m} (and initial time increment
\Lit{-i}) are taken relative to the dump slices selected with \Lit{-t}
rather than the complete dump records stored in the dump files. For
example, if dump records 1,3,5,\ldots{}19 are extracted using \Lit{-t}, then
time intervals of \Lit{-m} 1-5 correspond to "real" intervals of
2,4,\ldots{}10 relative to the original dump file data. It should also be
pointed out that the msds for each time interval are only averaged
over as many initial time slices as the \emph{longest} time interval.
Therefore it is usual to specify the largest value of \Lit{-m} to be
half that of the total number of dump slices in order to optimise the
msd time interval and number of calculations over which each msd is
averaged.

Selecting option \Lit{-u} circumvents calculation of the msds and
instead sends the connected trajectory coordinates to the output. The
default format for trajectory data is "\# \textit{species-name}" followed by a
row of $x y z$ for a single particle for each increasing time interval.
Trajectory data for each particle are separated by two blank lines.
This is the format favoured by GNUPlot. Specifying \Lit{-w 1}  selects
the generic matrix format in which a single row contains consecutive $x
y z$ coordinates for all particles, with time increasing down the
columns.

The \Lit{-x}, \Lit{-y} and \Lit{-z} options only apply to the
trajectory data and are useful for reducing the number of particle
trajectories outputted by "cutting" the simulation box into more
manageable slices.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Mdavpos}%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Mdavpos is a utility for calculating the average positions of particles
from dump slices recorded during a Moldy run. The utility is similar to
Mdshak, with command line options

\begin{center}
\Lit{mdavpos}
\Lit{[-s} \textit{system-specification}\Lit{]}
\Lit{[-r} \textit{restart-file}\Lit{]}
\Lit{[-d} \textit{dump-file-format}\Lit{]}
\Lit{[-t} \textit{dump-range}\Lit{]} 
\Lit{[-h]}
\Lit{[-p]}
\Lit{[-x]}
\Lit{[-o} \textit{output-file}\Lit{]}
\end{center}

where the arguments have the following meanings

\begin{Argdescription}
\item[-s] read a system specification file.
\item[-r] read a restart file. Only one of \Lit{-s} or \Lit{-r} may be given.
\item[-d] read configurational data from a dump file given as a prototype name
containing a printf() format string - see section 3.7.
\item[-t] range of records in the dump file, specified in ``selector'' format, to
average positions over.
\item[-h] give the output in SCHAKAL format (default).
\item[-p] give the output in Brookhaven Protein Data Bank (PDB) format.
\item[-x] give the output in XYZ format suitable for Xmol or RasMol.
\item[-o] name of optional output file. Defaults to standard output.
\end{Argdescription}

\bibliography{simulation,mypubs}
\bibliographystyle{amsplain}

\end{document}
% LocalWords:  Fndescription Litdescription erf erfc subfig superfig Refson MCY
% LocalWords:  Parrinello Rahman Lennard Buckingham Stardent Alliant cray XMP
% LocalWords:  MFlop SGI SP Tildesley Pawley Powles Sonnenschein Rodger Nos co
% LocalWords:  Fincham Beeman's Perram Tildesley's subcells subcell Heyes 
% LocalWords:  Rahman's RDF dat const Matsuoka Clementi Yoshimine MDBACKUP KE
% LocalWords:  Microsystems RDFs Leuuw Coulombic Leeuw unix DUSE TCGMSG BSP MPI
% LocalWords:  SPMD SHMEM defs struct structs auxil beeman ewald eigens rdf xdr
% LocalWords:  quaterns sysdef init potpar thermalise conv Abramowitz Stegun kJ
% LocalWords:  PVM accel ithread nthreads istep icell icell Moldyext KE's AVS
% LocalWords:  Dumpanal Dumpconv dumpconv SCHAKAL Quentrec nsteps rtmass mol ps
% LocalWords:  amu MPa nbins ndumps Debye MDDUMP dumpanal stdlib stddef LIBS mt
% LocalWords:  aalloc alloc arralloc talloc tfree nspecies nsites nmols NPOTP
% LocalWords:  algorith ansi pre
$EOD
$!
$CREATE moldy.bbl
$DECK
\providecommand{\bysame}{\leavevmode\hbox to3em{\hrulefill}\thinspace}
\begin{thebibliography}{10}

\bibitem{abramowitz:70}
M.~Abramowitz and I.~A. Stegun, \emph{Handbook of mathematical functions},
  Dover, New York, 1970.

\bibitem{allen:87}
M.~P. Allen and D.~J. Tildesley, \emph{Computer simulation of liquids},
  Clarendon Press, Oxford, 1987.

\bibitem{beeman:76}
D.~Beeman, \emph{Some multistep methods for use in molecular dynamics
  calculations}, Journal of Computational Physics \textbf{20} (1976), 130--139.

\bibitem{berthaut:52}
F.~Berthaut, \emph{L'\'{e}nergie \'{e}lectrostatique de r\`{e}seaux ioniques},
  J. Phys. Radium \textbf{13} (1952), 499--505.

\bibitem{bowler:87}
K.~C. Bowler, R.~D. Kennaway, G.~S. Pawley, and D.~Roweth, \emph{An
  introduction to {OCCAM-2} programming}, Chartwell-Bratt, 1987.

\bibitem{cho:92}
K.~Cho and J.~D. Joannopoulos, \emph{Ergodicity and dynamical properties of
  constant-temperature molecular dynamics}, Physical Review A \textbf{45}
  (1992), no.~10, 7089--7097.

\bibitem{cho:93}
K.~Cho, J.~D. Joannopoulos, and L.~Kleinman, \emph{Constant-temperature
  molecular dynamics with momentum conservation}, Physical Review E \textbf{47}
  (1993), no.~5, 3145--3151.

\bibitem{clementi:85}
E.~Clementi, G.~Corongiu, and J.~H. Detrich, \emph{Parallelism in computations
  in quantum and statistical mechanics}, Computer Physics Communications
  \textbf{37} (1985), 287--294.

\bibitem{dietz:82}
W.~Deitz, W.~O. Riede, and K.~Heinzinger, \emph{Molecular dynamics simulation
  of an aqueous {M}g{C}l$_2$ solution}, Z. Naturforsch \textbf{37a} (1982),
  1038--1048.

\bibitem{evans:77}
D.~J. Evans, \emph{On the representation of orientation space}, Molecular
  Physics \textbf{34} (1977), no.~2, 317--325.

\bibitem{evans:77b}
D.~J. Evans and S.~Murad, \emph{Singularity free algorithm for molecular
  dynamics simulation of rigid polyatomics}, Molecular Physics \textbf{34}
  (1977), no.~2, 327--331.

\bibitem{fincham:81}
D.~Fincham, \emph{Rotational verlet}, Information Quarterly - {CCP5} \textbf{5}
  (1981), 6.

\bibitem{fincham:94}
D.~Fincham, \emph{Optimization of the {E}wald sum for large systems}, Molecular
  Simulation \textbf{13} (1994), no.~1, 1--9.

\bibitem{mpi:94}
Message Passing~Interface Forum, \emph{{MPI}: A message-passing interface
  standard}, International Journal of Supercomputer Applications \textbf{8}
  (1994), no.~3-4, 165.

\bibitem{pvm:94}
Al~Geist, Adam Beguelin, Jack Dongarra, Weicheng Jiang, Robert Manchek, and
  Vaidy Sunderam, \emph{{PVM}: Parallel virtual machines, a user's guide and
  tutorial for networked parallel computing}, MIT Press, Cambridge, {MA}, 1994.

\bibitem{goldstein:80}
H.~Goldstein, \emph{Classical mechanics}, 2nd ed., Addison--Wesley, Reading,
  MA, 1980.

\bibitem{hansen:86}
J.~P. Hansen and I.~R. McDonald, \emph{Theory of simple liquids}, 2nd ed.,
  Academic Press, London, 1986.

\bibitem{tcgmsg:94}
Robert~J. Harrison, \emph{Tcgmsg send/receive subroutines}, Battelle Pacific
  Northwest Laboratory, 1994, Available by internet ftp from url
  ftp://ftp.tcg.anl.gov/pub/tcgmst/tcgmsg-4.04.tar.Z.

\bibitem{heyes:87}
D.~M. Heyes, \emph{Large numbers of particles on the {C}ray-1}, Information
  Quarterly - {CCP5} \textbf{25} (1987), 57--61.

\bibitem{hoover:85}
W.~G. Hoover, \emph{Canonical dynamics: equilibrium phase-space distributions},
  Physical Review A \textbf{31} (1985), 1695--1697.

\bibitem{sunxdr}
Sun~Microsystems Inc, \emph{External data representation: Protocol
  specification}, 1987, RFC1014.

\bibitem{jorgensen:82}
W.~L. Jorgensen, \emph{Revised {TIPS} model for simulations of liquid water and
  aqueous solutions}, Journal of Chemical Physics \textbf{77} (1982), 4156--63.

\bibitem{schakal:88}
E.~Keller, \emph{Neues von {SCHAKAL}}, Chemie in unserer Zeit \textbf{20}
  (1986), no.~6, 178--181.

\bibitem{kernighan:78}
B.~W. Kernighan and D.~Ritchie, \emph{The {C} programming language}, 1st ed.,
  Prentice Hall, Cambridge, 1978.

\bibitem{kernighan:88}
\bysame, \emph{The {C} programming language}, second ed., Prentice Hall,
  Cambridge, 1988.

\bibitem{kistenmacher:73b}
H.~Kistenmacher, H.~Popkie, and E.~Clementi, \emph{Study of the structure of
  molecular complexes {III}. {E}nergy surface of a water molecule in the field
  of a fluorine or chlorine anion.}, Journal of Chemical Physics \textbf{58}
  (1973), no.~4, 5842.

\bibitem{knuth:73a}
Donald~E. Knuth, \emph{Fundamental algorithms}, second ed., The Art of Computer
  Programming, vol.~1, section 1.2, Addison-Wesley, 1973.

\bibitem{laakonsen:85}
A.~Laakonsen, \emph{Computer simulation package for liquids and solids with
  polar interactions. {I}. {McMOLDYN/H2O}: aqueous systems}, Internal Report
  KGN-41, IBM Corporation, Kingston, N.Y.12401, USA, 1985.

\bibitem{deleeuw:80}
S.~W.~De Leeuw, J.~W. Perram, and E.~R. Smith, \emph{Simulation or
  electrostatic systems in periodic boundary conditions. {I} {L}attice sums and
  dielectric constant}, Proceedings of the Royal Society \textbf{A373} (1980),
  27--56.

\bibitem{matsuoka:75}
O.~Matsuoka, E.~Clementi, and M.~Yoshimine, \emph{{CI} study of the water dimer
  potential surface}, Journal of Chemical Physics \textbf{64} (1976), no.~4,
  1351--1361.

\bibitem{bsp:93}
Richard Miller, \emph{A library for bulk-synchronous parallel computing},
  Parallel Processing Specialist Group workshop on General Purpose Parallel
  Computing, British Computer Society, 1993.

\bibitem{nose:84}
S.~Nos{\'e}, \emph{A molecular dynamics method for simulations in the canonical
  ensemble}, Molecular Physics \textbf{52} (1984), 255--268.

\bibitem{nose:91}
\bysame, \emph{Molecular dynamics simulations at constant temperature and
  pressure}, Computer Simulation in Materials Science (M.~Meyer and
  V.~Pontikis, eds.), vol. E205, Kluwer, Dordrecht, 1991, NATO ASI, pp.~21--42.

\bibitem{nose:83}
S.~Nos\'{e} and M.~L. Klein, \emph{Constant pressure molecular dynamics for
  molecular systems}, Molecular Physics \textbf{50(5)} (1983), 1055--1076.

\bibitem{parrinello:81}
M.~Parrinello and A.~Rahman, \emph{Polymorphic transitions in single crystals:
  {A} new molecular dynamics method}, Journal of Applied Physics \textbf{52}
  (1981), no.~12, 7182--7190.

\bibitem{pawley:81}
G.~S. Pawley, \emph{Molecular dynamics simulation of the plastic phase; a model
  for {SF}$_6$}, Molecular Physics \textbf{43} (1981), no.~6, 1321--1330.

\bibitem{pawley:82}
\bysame, \emph{Computer simulation of the plastic-to-crystalline phase
  transition in {SF}6}, Physical Review Letters \textbf{48} (1982), 410--413.

\bibitem{pawley:85b}
G.~S. Pawley and M.~T. Dove, \emph{Quaternion-based reorientation conditions
  for molecular dynamics analyses}, Molecular Physics \textbf{55} (1985),
  no.~5, 1147--1157.

\bibitem{powles:79}
J.~G. Powles, W.~A.~B. Evans, E.~McGrath, K.~E. Gubbins, and S.~Murad, \emph{A
  computer simulation for a simple model of liquid hydrogen chloride},
  Molecular Physics \textbf{38} (1979), 893--908.

\bibitem{press:92C}
William~H. Press, Saul~A. Teukolsky, William~T. Vetterling, and Brian~P.
  Flannery, \emph{Numerical recipes in {C} the art of scientific computing},
  2nd ed., Cambridge University Press, 1992.

\bibitem{quentrec:75}
B.~Quentrec and C.~Brot, \emph{New methods for searching for neighbours in
  molecular dynamics computations}, Journal of Computational Physics
  \textbf{13} (1975), 430--432.

\bibitem{rapaport:88}
D.C. Rapaport, \emph{Large-scale molecular-dynamics simulation using vector and
  parallel computers}, Computer Physics Reports \textbf{9} (1988), no.~1,
  1--53.

\bibitem{refson:85}
K.~Refson, \emph{Molecular dynamics simulation of solid {\em n}-butane},
  Physica \textbf{131B} (1985), 256--266.

\bibitem{refson:87a}
K.~Refson and G.~S. Pawley, \emph{Molecular dynamics studies of the condensed
  phases of {\em n}-butane and their transitions, {I} {T}echniques and model
  results}, Molecular Physics \textbf{61} (1987), no.~3, 669--692.

\bibitem{rodger:89}
P.~M. Rodger, \emph{On the accuracy of some common molecular dynamics
  algorithms}, Molecular Simulations \textbf{3} (1989), 263--269.

\bibitem{smith:91}
W.~Smith, \emph{Molecular dynamics on hypercube parallel computers}, Computer
  Physics Communications \textbf{62} (1991), 229--248.

\bibitem{smith:92}
\bysame, \emph{A replicated-data molecular dynamics strategy for the parallel
  {E}wald sum}, Computer Physics Communications \textbf{67} (1992), 392--406.

\bibitem{smith:82}
W.~Smith and D.~Fincham, \emph{The program {MDMPOL}}, SERC, Daresbury
  Laboratory, 1982, CCP5 Program Library.

\bibitem{sonnenschein:85}
R.~Sonnenschein, \emph{An improved algorithm for molecular dynamics simulation
  of rigid molecules}, Journal of Computational Physics \textbf{59} (1985),
  no.~2, 347--350.

\bibitem{duval:64}
P.~Du Val, \emph{Homographies, quaternions and rotations}, Oxford Mathematical
  Monograph, 1964.

\bibitem{verlet:67}
L.~Verlet, \emph{Computer `experiments' on classical fluids. {I}.
  thermodynamical properties of lennard-jones molecules}, Physical Review
  \textbf{165} (1967), 201--214.

\end{thebibliography}
$EOD
$!
$CREATE fig_arralloc.ftx
$DECK
\begin{picture}(0,0)%
\includegraphics{fig_arralloc}%
\end{picture}%
\setlength{\unitlength}{0.00057700in}%
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
\begin{picture}(8719,5131)(566,-4744)
\put(883,-4250){\makebox(0,0)[lb]{\smash{\SetFigFont{11}{13.2}{\ttdefault}{\mddefault}{\updefault}r}}}
\put(2526,-4210){\makebox(0,0)[lb]{\smash{\SetFigFont{11}{13.2}{\ttdefault}{\mddefault}{\updefault}r[i]}}}
\put(6545,-4189){\makebox(0,0)[lb]{\smash{\SetFigFont{11}{13.2}{\ttdefault}{\mddefault}{\updefault}r[i][j]}}}
\put(953,-4699){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\familydefault}{\mddefault}{\updefault}(pointer)}}}
\put(2770,-4699){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\familydefault}{\mddefault}{\updefault}(array of \textit{m} pointers)}}}
\put(6959,-4699){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\familydefault}{\mddefault}{\updefault}(\textit{m} arrays of \textit{n} data objects)}}}
\put(5023,255){\makebox(0,0)[lb]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}1}}}
\put(5570,255){\makebox(0,0)[lb]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}2}}}
\put(7945,255){\makebox(0,0)[lb]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}n-2}}}
\put(9285,-354){\makebox(0,0)[lb]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}0}}}
\put(9285,-902){\makebox(0,0)[lb]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}1}}}
\put(9224,-2606){\makebox(0,0)[lb]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}m-2}}}
\put(4414,255){\makebox(0,0)[lb]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}0}}}
\put(7336,255){\makebox(0,0)[lb]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}n-3}}}
\put(8615,255){\makebox(0,0)[lb]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}n-1}}}
\put(9224,-3215){\makebox(0,0)[lb]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}m-1}}}
\end{picture}
$EOD
$!
$CREATE fig_dostep-a.ftx
$DECK
\begin{picture}(0,0)%
\includegraphics{fig_dostep-a}%
\end{picture}%
\setlength{\unitlength}{0.00057700in}%
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
\begin{picture}(10599,10570)(214,-9944)
\put(8551,-106){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}Calculation of molecular}}}
\put(8551,-309){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}centre-of-mass forces}}}
\put(8551,-512){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}and torques.}}}
\put(1801,-691){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}first call?}}}
\put(1171,-1636){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Allocate arrays}}}
\put(1171,-1839){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}for sites and}}}
\put(1171,-2042){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}site forces}}}
\put(1171,-2806){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Set up array of}}}
\put(1171,-3009){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}site charges}}}
\put(1801,-3931){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}step\_1()}}}
\put(3286,-511){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Calc. distant}}}
\put(3286,-691){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}pot'l \& pressure}}}
\put(3286,-871){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}correction}}}
\put(1801,-5281){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}make\_sites()}}}
\put(1801,-5461){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Calc. site co-ords.}}}
\put(4051,-7531){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}ewald()}}}
\put(4051,-7756){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}recip-space forces}}}
\put(2701,-7486){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(1576,-8161){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(1576,-1186){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(2701,-511){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(1801,-6421){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}force\_calc()}}}
\put(1801,-7681){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}coulomb forces?}}}
\put(1801,-8716){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}surface-dipole}}}
\put(8329,-5913){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(7204,-6580){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(7429,-9647){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}A}}}
\put(9676,-1321){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}mol\_torque()}}}
\put(7471,-4606){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}newton()}}}
\put(7426,-181){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}mol\_force()}}}
\put(7436,-1261){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}any}}}
\put(7436,-1464){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}polyatomics?}}}
\put(6746,-2544){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}molecular virial}}}
\put(6746,-2747){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}correction term.}}}
\put(6751,-3639){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}last timestep in}}}
\put(6751,-3842){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}"old" arrays etc.}}}
\put(8326,-1186){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(7201,-1861){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(9676,-7066){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}rahman()}}}
\put(9676,-8206){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}beeman\_2()}}}
\put(1801,-4118){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Apply Beeman steps \textit{i, ii} to}}}
\put(2978,-5213){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}(b)}}}
\put(1801,-6624){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}real-space forces}}}
\put(1801,-7479){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}need}}}
\put(1801,-8882){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}switch on?}}}
\put(3241,-8859){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}forces \& energy.}}}
\put(3241,-8656){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get surf. dipole}}}
\put(8409,-52){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}(c)}}}
\put(7421,-368){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get C of M forces.}}}
\put(9679,-1516){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get torques.}}}
\put(6751,-3436){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Store acc's from}}}
\put(6746,-2341){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get atomic,}}}
\put(7421,-6093){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Const stress?}}}
\put(6311,-5700){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}(d)}}}
\put(7429,-4810){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}New C of M acc's}}}
\put(676,-3670){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}(a)}}}
\put(9676,-8405){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Step cell velocities}}}
\put(9671,-7254){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get unit cell acc's}}}
\put(3166,-5221){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}Calculation of atomic}}}
\put(3166,-5424){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}site forces and}}}
\put(3166,-5627){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}potential energy.}}}
\put(1801,-4284){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}update dynamic variables}}}
\put(1801,315){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}do\_step()}}}
\put(2701,-8626){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(1576,-9278){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(9676,-5971){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}energy\_dyad()}}}
\put(9661,-6166){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault} kinetic stress term}}}
\end{picture}
$EOD
$!
$CREATE fig_dostep-b.ftx
$DECK
\begin{picture}(0,0)%
\includegraphics{fig_dostep-b}%
\end{picture}%
\setlength{\unitlength}{0.00057700in}%
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
\begin{picture}(10449,10378)(364,-9752)
\put(7951,-4126){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}(f)}}}
\put(8201,-4303){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}derivatives using Beeman}}}
\put(8201,-4144){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}Iteratively update quatern}}}
\put(8201,-4454){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}step \emph{iv}.}}}
\put(2806,-5821){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}(e)}}}
\put(3006,-5816){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}Iteratative loop to step}}}
\put(3011,-5976){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}C of M velocities using}}}
\put(3011,-6120){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}Beeman step \emph{iv}.}}}
\put(2041,-9436){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(2731,-8821){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(7197,-278){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Thermostat ?}}}
\put(6050,-60){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}None}}}
\put(7415,-792){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Gaussian}}}
\put(8239, 37){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Nose-Hoover}}}
\put(7180,-1338){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}gtherm()}}}
\put(8155,-5840){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(7216,-4703){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}beeman\_2()}}}
\put(7193,-3608){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}euler()}}}
\put(7231,-2528){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}hoover\_rot()}}}
\put(7430,-6410){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(7216,-7100){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}step\_2()}}}
\put(7201,-9560){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}return}}}
\put(9440,-1336){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}beeman\_2()}}}
\put(9440,-88){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}nhtherm()}}}
\put(9451,-1576){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Predict N-H $\zeta$}}}
\put(9436,-331){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get N-H $\dot{\zeta}$}}}
\put(7186,-1591){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Calc.  gaussian $\zeta$}}}
\put(7195,-5803){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}quatern}}}
\put(7195,-6092){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}converged?}}}
\put(7195,-5945){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}derivatives}}}
\put(7206,-7271){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Apply Beeman step \textit{iv} to}}}
\put(7216,-7443){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}dynamic var derivatives}}}
\put(7201,-4881){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}step quatern derivs}}}
\put(7196,-2706){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get torque corr.}}}
\put(7201,-3801){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Calc angular accs}}}
\put(7186,-8596){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}dump\_interval}}}
\put(7185,-8429){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}every}}}
\put(7186,-8764){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}steps}}}
\put(8296,-6811){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}(g)}}}
\put(9455,-8422){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}dump()}}}
\put(9432,-8585){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}write trajectories,}}}
\put(9432,-8747){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}forces \emph{etc.}}}}
\put(1801,134){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}A}}}
\put(901,-646){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(2033,-1314){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(1801,-719){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}const-stress}}}
\put(1801,-883){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}or thermostat?}}}
\put(1801,-2116){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}simulation?}}}
\put(1808,-3209){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Thermostat ?}}}
\put(661,-2991){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}None}}}
\put(2026,-3723){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Gaussian}}}
\put(2850,-2894){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Nose-Hoover}}}
\put(4051,-3064){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}nhtherm()}}}
\put(1811,-5394){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}hoover\_tr()}}}
\put(1791,-4269){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}gtherm()}}}
\put(4051,-4317){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}beeman\_2()}}}
\put(1786,-6554){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}beeman\_2()}}}
\put(1816,-7858){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}converged?}}}
\put(1816,-7681){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}velocities}}}
\put(2716,-7621){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(1801,-1938){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Const stress}}}
\put(4061,-1858){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}parinello()}}}
\put(4051,-3286){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get N-H $\dot{\zeta}$}}}
\put(4066,-4524){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Step N-H $\zeta$}}}
\put(1801,-4546){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get  gaussian $\zeta$}}}
\put(4056,-2081){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Get P\&R acc corr.}}}
\put(1786,-6766){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}step C of M vels}}}
\put(1801,-5626){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Calc N-H acc corr.}}}
\put(1954,-8243){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(1801,-8851){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}any}}}
\put(1801,-9006){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}polyatomics?}}}
\end{picture}
$EOD
$!
$CREATE fig_ewald.ftx
$DECK
\begin{picture}(0,0)%
\includegraphics{fig_ewald}%
\end{picture}%
\setlength{\unitlength}{0.00057700in}%
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
\begin{picture}(4794,9250)(160,-8624)
\put(736,-2310){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Set up $\sin \mathbf{k \cdot r}_i$ and }}}
\put(736,-2477){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}$\cos\mathbf{k \cdot r}_i$ arrays for}}}
\put(736,-2664){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}$\mathbf{k}=\mathbf{0}, \mathbf{a}^*, \mathbf{b}^*, \mathbf{c}^*$}}}
\put(736,-3401){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}$\mathbf{k}= h\mathbf{a}^*, k\mathbf{b}^*, l\mathbf{c}^*$}}}
\put(736,-3212){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Get $\sin$, $\cos \mathbf{k \cdot r}_i $ for}}}
\put(736,-3568){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}using recursion}}}
\put(736,-4967){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Calc. pre-factors}}}
\put(736,-5134){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}for energy, force}}}
\put(1576,-6111){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}for this $\mathbf{k}$ and all sites $i$}}}
\put(1576,-5879){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Evaluate  $\sin$ and $\cos \mathbf{k \cdot r}_i$}}}
\put(1576,-5670){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}qsincos()}}}
\put(736,-6941){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}and energy term}}}
\put(736,-6767){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Eval. struct. factor}}}
\put(736,-7441){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Eval. forces on all}}}
\put(736,-7608){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}sites \& add to total}}}
\put(1561,329){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}ewald()}}}
\put(3277,-619){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}terms}}}
\put(3277,-278){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Calculate self-}}}
\put(3277,-453){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}and sheet-energy }}}
\put(1576,-457){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}first call?}}}
\put(1576,-4350){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}\emph{k}-vectors}}}
\put(1576,-4168){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}loop over}}}
\put(736,-1391){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Precompute list of}}}
\put(736,-1580){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}$h,k,l$ for \emph{k}-vectors}}}
\put(736,-1769){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}within cutoff}}}
\put(1561,-8431){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}return}}}
\end{picture}
$EOD
$!
$CREATE fig_link-cell.ftx
$DECK
\begin{picture}(0,0)%
\includegraphics{fig_link-cell}%
\end{picture}%
\setlength{\unitlength}{0.00057700in}%
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
\begin{picture}(9933,11049)(664,-10648)
\put(8320,-6348){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(7442,-6963){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(1486,-7966){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}force\_inner()}}}
\put(1486,-9121){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}return}}}
\put(7254,-10442){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}return}}}
\put(7254,-7307){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}kernel()}}}
\put(7314,-1538){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}site\_neighbour\_list()}}}
\put(9745,-6536){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Print warning.}}}
\put(7284,-7789){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}on neighbour list.}}}
\put(7329,-6586){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}site approaches}}}
\put(7314,-6421){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}test for close}}}
\put(7314,-3676){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}loop over}}}
\put(7329,-2026){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}range of current cell.}}}
\put(7329,-1778){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Build list of all sites within}}}
\put(7366,-661){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}loop- over all cells}}}
\put(6189,-9304){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Distribute neighbour list}}}
\put(6189,-8659){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}on sites in neighbour list.}}}
\put(6101,-3013){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}vectors into neighbour lists}}}
\put(6101,-2788){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Gather site co-ords reloc,}}}
\put(6123,-4588){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Gather potential params}}}
\put(6123,-4858){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}into neighbour list arrays.}}}
\put(6114,-5503){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Calculate squared pair site}}}
\put(6114,-5728){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}distances in neighbour list.}}}
\put(7284,-7549){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Evaluate potential  \& force}}}
\put(6189,-8434){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Calculate stress \& forces}}}
\put(6189,-9529){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}forces onto force array.}}}
\put(901,-2326){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Create list of neighbouring cells.}}}
\put(1441,104){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}force()}}}
\put(2926,-2881){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}neighbour\_list()}}}
\put(2251,-766){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(1621,-1756){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(3466,-721){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Set up arrays of}}}
\put(3496,-946){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}P.B.C. relocation}}}
\put(3181,-1171){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}vectors.}}}
\put(3001,-4045){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}site\_neighbour\_list()}}}
\put(1432,-4108){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(1456,-2764){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(751,-5041){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Allocate and fill}}}
\put(751,-5491){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}of pot'l params.}}}
\put(751,-5266){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}expanded array}}}
\put(1491,-6426){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}fill\_cells()}}}
\put(1491,-6626){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Build linked list }}}
\put(1486,-6816){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}of mols in cells}}}
\put(1577,-3481){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}strict cutoff?}}}
\put(7299,-4006){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}in cell}}}
\put(7299,-3833){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}molecules and sites}}}
\put(1464,-1316){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}changed.}}}
\put(1434,-1091){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault} cell size/shape}}}
\put(1464,-888){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}first call or}}}
\put(7329, 90){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}force\_inner()}}}
\end{picture}
$EOD
$!
$CREATE fig_main.ftx
$DECK
\begin{picture}(0,0)%
\includegraphics{fig_main}%
\end{picture}%
\setlength{\unitlength}{0.00057700in}%
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
\begin{picture}(8798,10824)(218,-10197)
\put(2702,-1176){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}loop}}}
\put(3608,-8384){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}on}}}
\put(1793,-8369){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}off}}}
\put(2695,-282){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}start\_up()}}}
\put(7027,-1085){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}rdf\_inner()}}}
\put(8122,-2741){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}dump()}}}
\put(2695,-2097){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}do\_step()}}}
\put(2700,-2987){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}values()}}}
\put(2710,-4065){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}print\_interval}}}
\put(2702,-4965){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}scale\_interval}}}
\put(2710,-8549){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}text\_mode\_save}}}
\put(5417,-2893){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}dump\_interval}}}
\put(5410,-3892){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}output()}}}
\put(5410,-4792){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}rescale()}}}
\put(5405,-5722){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}averages()}}}
\put(5390,-6622){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}print\_rdf()}}}
\put(4270,-8992){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}print\_config()}}}
\put(2700,-10002){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}exit()}}}
\put(1120,-9052){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}write\_restart()}}}
\put(2701,-4771){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}every}}}
\put(5416,-2716){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}every}}}
\put(2701,-7486){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}every}}}
\put(2701,-3896){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}every}}}
\put(2701,-4246){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}steps}}}
\put(2701,-6046){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}steps}}}
\put(2701,-5146){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}steps}}}
\put(2686,-5686){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}every}}}
\put(2701,-6591){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}every}}}
\put(2702,-6762){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}rdf\_out}}}
\put(2696,-6946){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}steps}}}
\put(5431,-3061){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}steps}}}
\put(5415,-6853){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}calc/print RDFs}}}
\put(5400,-5048){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}scale velocities}}}
\put(1125,-9234){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}write save file}}}
\put(5400,-5910){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}calc/print averages}}}
\put(7067,-1297){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}link-cell RDF calc}}}
\put(8116,-2961){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}write trajectories}}}
\put(4290,-9201){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}write text restart}}}
\put(5408,-7768){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}write backup file}}}
\put(2695,-3171){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}log thermo. values}}}
\put(2701,-2291){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Step co-ords by \textit{dt}}}}
\put(2702,-484){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}input/initialization}}}
\put(5402,-4108){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}periodic write}}}
\put(2700,330){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}main()}}}
\put(2702,-1521){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}istep}}}
\put(2702,-1355){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}over timesteps}}}
\put(4982,-1160){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}force\_calc()}}}
\put(8112,-2085){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}bin pair distances}}}
\put(8097,-1888){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}rdf\_accum()}}}
\put(2702,-5872){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}average\_interval}}}
\put(2701,-7846){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}steps}}}
\put(2702,-7687){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}backup\_interval}}}
\put(5410,-7604){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}write\_restart()}}}
\end{picture}
$EOD
$!
$CREATE fig_skewstart.ftx
$DECK
\begin{picture}(0,0)%
\includegraphics{fig_skewstart}%
\end{picture}%
\setlength{\unitlength}{0.00057700in}%
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
\begin{picture}(10761,5513)(52,-4721)
\put(5071,-4470){\makebox(0,0)[lb]{\smash{\SetFigFont{14}{16.8}{\rmdefault}{\mddefault}{\updefault}a}}}
\put(6901,-2553){\makebox(0,0)[lb]{\smash{\SetFigFont{14}{16.8}{\rmdefault}{\mddefault}{\updefault}d}}}
\end{picture}
$EOD
$!
$CREATE fig_startup-a.ftx
$DECK
\begin{picture}(0,0)%
\includegraphics{fig_startup-a}%
\end{picture}%
\setlength{\unitlength}{0.00057700in}%
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
\begin{picture}(6096,10374)(438,-9748)
\put(1351,-4046){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}restart?}}}
\put(2246,-3886){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(1131,-4686){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(1351,-5346){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}exist?}}}
\put(2246,-4991){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(3491,-5176){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Abort}}}
\put(1131,-5636){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(1346,-7081){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}backup}}}
\put(1346,-7261){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}file exists?}}}
\put(2251,-7026){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(2246,-8156){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(1581,-7711){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(1576,-8836){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(1356,-9446){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}A}}}
\put(3826,-8361){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}B}}}
\put(3826,-7236){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}C}}}
\put(1346,336){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}start\_up()}}}
\put(616,-6086){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Create backup \&}}}
\put(616,-6266){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}dump lock files}}}
\put(3856,-1433){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}get saved params}}}
\put(3856,-3218){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}get new params}}}
\put(3833,-2347){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}to "input" units}}}
\put(3818,-4163){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}to prog units}}}
\put(1351,-4986){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault} dump}}}
\put(1351,-5166){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}/backup lockfiles}}}
\put(3826,-1230){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}re\_re\_header()}}}
\put(3826,-2123){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}conv\_control()}}}
\put(3831,-3028){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}read\_control()}}}
\put(3826,-3924){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}conv\_control()}}}
\put(1356,-632){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}default\_control()}}}
\put(1351,-1741){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}read\_control()}}}
\put(1346,-2885){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}conv\_control()}}}
\put(5626,-7466){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}D}}}
\put(5631,-8438){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}banner\_page()}}}
\put(5626,-8645){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}write output file}}}
\put(5648,-9529){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}return}}}
\put(1351,-8309){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}restart?}}}
\end{picture}
$EOD
$!
$CREATE fig_startup-b.ftx
$DECK
\begin{picture}(0,0)%
\includegraphics{fig_startup-b}%
\end{picture}%
\setlength{\unitlength}{0.00057700in}%
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
\begin{picture}(10612,10829)(443,-10207)
\put(2483,-3209){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}allocate\_}}}
\put(2483,-3374){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}dynamics()}}}
\put(2476,-2353){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}initialise\_}}}
\put(2476,-2518){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}sysdef()}}}
\put(10134,-2543){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}allocate\_}}}
\put(10134,-2708){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}dynamics()}}}
\put(5626,-4128){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}allocate\_}}}
\put(5626,-4293){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}dynamics()}}}
\put(7919,-3230){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}initialise\_}}}
\put(7919,-3395){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}sysdef()}}}
\put(2491,164){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}A}}}
\put(5626,149){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}B}}}
\put(10156,164){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}C}}}
\put(2476,-9968){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}D}}}
\put(10126,-7493){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}D}}}
\put(2476,-569){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}read\_sysdef()}}}
\put(2476,-4281){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}lattice start?}}}
\put(3368,-4109){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(1576,-4116){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(10126,-3511){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}init\_averages()}}}
\put(2468,-1544){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}conv\_potentials()}}}
\put(2476,-7126){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}init\_averages()}}}
\put(2468,-6232){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}thermalise()}}}
\put(3593,-5093){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}lattice\_start()}}}
\put(1343,-5093){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}skew\_start()}}}
\put(2476,-8025){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}init\_rdf()}}}
\put(2483,-8932){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}init\_cutoffs()}}}
\put(10116,-817){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}re\_re\_header()}}}
\put(10131,-1725){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}re\_re\_sysdef()}}}
\put(10096,-4425){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}init\_rdf()}}}
\put(10126,-5326){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}read\_restart()}}}
\put(4831,-494){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Adjust timestep-}}}
\put(4831,-854){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}parameters}}}
\put(4831,-674){\makebox(0,0)[lb]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}related control}}}
\put(2476,-787){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}from sys-spec file}}}
\put(10129,-1927){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}from backup}}}
\put(10129,-5516){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}from backup}}}
\put(10148,-6291){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}convert\_averages()}}}
\put(10129,-1020){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault} from backup}}}
\put(5611,-9968){\makebox(0,0)[b]{\smash{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}D}}}
\put(5626,-1476){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}replace}}}
\put(5626,-1656){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}system spec?}}}
\put(6526,-1431){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(5858,-2099){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(6526,-8618){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}
\put(5408,-9308){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}
\put(7874,-8678){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}interpolate\_}}}
\put(7874,-8843){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}derivatives()}}}
\put(5626,-2631){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}re\_re\_sysdef()}}}
\put(5671,-5108){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}init\_averages()}}}
\put(5626,-6014){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}init\_rdf()}}}
\put(5633,-6907){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}read\_restart()}}}
\put(7874,-1502){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}read\_sysdef()}}}
\put(7881,-4210){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}check\_sysdef()}}}
\put(7875,-1717){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}from sys-spec file}}}
\put(5625,-2842){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}from restart file}}}
\put(7882,-4437){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}abort if failed}}}
\put(5625,-7107){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}dyamic vars etc.}}}
\put(7881,-2485){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}conv\_potentials()}}}
\put(5641,-8715){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}timestep}}}
\put(5641,-8895){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}changed?}}}
\put(5633,-7891){\makebox(0,0)[b]{\smash{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}convert\_averages()}}}
\end{picture}
$EOD
$!
$CREATE fig_arralloc.ps
$DECK
%!PS-Adobe-2.0 EPSF-2.0
%%Title: fig_arralloc.fig
%%Creator: fig2dev Version 3.1 Patchlevel 2
%%CreationDate: Tue Jan  6 10:00:26 1998
%%For: keith@metropolis (Keith Refson)
%Magnification: 0.69
%%Orientation: Portrait
%%BoundingBox: 0 0 378 215
%%Pages: 0
%%BeginSetup
%%IncludeFeature: *PageSize A4
%%EndSetup
%%EndComments
/$F2psDict 200 dict def
$F2psDict begin
$F2psDict /mtrx matrix put
/col-1 {0 setgray} bind def
/col0 {0.000 0.000 0.000 srgb} bind def
/col1 {0.000 0.000 1.000 srgb} bind def
/col2 {0.000 1.000 0.000 srgb} bind def
/col3 {0.000 1.000 1.000 srgb} bind def
/col4 {1.000 0.000 0.000 srgb} bind def
/col5 {1.000 0.000 1.000 srgb} bind def
/col6 {1.000 1.000 0.000 srgb} bind def
/col7 {1.000 1.000 1.000 srgb} bind def
/col8 {0.000 0.000 0.560 srgb} bind def
/col9 {0.000 0.000 0.690 srgb} bind def
/col10 {0.000 0.000 0.820 srgb} bind def
/col11 {0.530 0.810 1.000 srgb} bind def
/col12 {0.000 0.560 0.000 srgb} bind def
/col13 {0.000 0.690 0.000 srgb} bind def
/col14 {0.000 0.820 0.000 srgb} bind def
/col15 {0.000 0.560 0.560 srgb} bind def
/col16 {0.000 0.690 0.690 srgb} bind def
/col17 {0.000 0.820 0.820 srgb} bind def
/col18 {0.560 0.000 0.000 srgb} bind def
/col19 {0.690 0.000 0.000 srgb} bind def
/col20 {0.820 0.000 0.000 srgb} bind def
/col21 {0.560 0.000 0.560 srgb} bind def
/col22 {0.690 0.000 0.690 srgb} bind def
/col23 {0.820 0.000 0.820 srgb} bind def
/col24 {0.500 0.190 0.000 srgb} bind def
/col25 {0.630 0.250 0.000 srgb} bind def
/col26 {0.750 0.380 0.000 srgb} bind def
/col27 {1.000 0.500 0.500 srgb} bind def
/col28 {1.000 0.630 0.630 srgb} bind def
/col29 {1.000 0.750 0.750 srgb} bind def
/col30 {1.000 0.880 0.880 srgb} bind def
/col31 {1.000 0.840 0.000 srgb} bind def

end
save
-23.0 233.0 translate
1 -1 scale

/cp {closepath} bind def
/ef {eofill} bind def
/gr {grestore} bind def
/gs {gsave} bind def
/sa {save} bind def
/rs {restore} bind def
/l {lineto} bind def
/m {moveto} bind def
/rm {rmoveto} bind def
/n {newpath} bind def
/s {stroke} bind def
/sh {show} bind def
/slc {setlinecap} bind def
/slj {setlinejoin} bind def
/slw {setlinewidth} bind def
/srgb {setrgbcolor} bind def
/rot {rotate} bind def
/sc {scale} bind def
/sd {setdash} bind def
/ff {findfont} bind def
/sf {setfont} bind def
/scf {scalefont} bind def
/sw {stringwidth} bind def
/tr {translate} bind def
/tnt {dup dup currentrgbcolor
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
  bind def
/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
  4 -2 roll mul srgb} bind def
/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
/$F2psEnd {$F2psEnteredState restore end} def
%%EndProlog

$F2psBegin
10 setmiterlimit
n 0 842 m 0 0 l 595 0 l 595 842 l cp clip
 0.04157 0.04157 sc
7.500 slw
% Polyline
n 3013 767 m 2404 767 l 2404 1984 l 3013 1984 l gs col-1 s gr 
% Polyline
n 2404 1375 m 3013 1375 l gs col-1 s gr 
% Polyline
n 3013 3202 m 2404 3202 l 2404 4419 l 3013 4419 l gs col-1 s gr 
% Polyline
n 2404 3811 m 3013 3811 l gs col-1 s gr 
% Polyline
n 577 767 m 1186 767 l 1186 1375 l 577 1375 l cp gs col-1 s gr 
15.000 slw
% Polyline
gs  clippath
2091 1011 m 2331 1071 l 2091 1131 l 2415 1131 l 2415 1011 l  cp clip
n 882 1071 m 2385 1071 l gs col-1 s gr gr

% arrowhead
n 2091 1011 m 2331 1071 l 2091 1131 l 2091 1071 l 2091 1011 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
3936 1011 m 4176 1071 l 3936 1131 l 4260 1131 l 4260 1011 l  cp clip
n 2647 1071 m 4230 1071 l gs col-1 s gr gr

% arrowhead
n 3936 1011 m 4176 1071 l 3936 1131 l 3936 1071 l 3936 1011 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
3936 1620 m 4176 1680 l 3936 1740 l 4260 1740 l 4260 1620 l  cp clip
n 2647 1680 m 4230 1680 l gs col-1 s gr gr

% arrowhead
n 3936 1620 m 4176 1680 l 3936 1740 l 3936 1680 l 3936 1620 l  cp gs 0.00 setgray ef gr  col-1 s
7.500 slw
% Polyline
n 3013 767 m 3013 1984 l gs col-1 s gr 
% Polyline
n 3013 3202 m 3013 4419 l gs col-1 s gr 
15.000 slw
% Polyline
gs  clippath
3936 4055 m 4176 4115 l 3936 4175 l 4260 4175 l 4260 4055 l  cp clip
n 2647 4115 m 4230 4115 l gs col-1 s gr gr

% arrowhead
n 3936 4055 m 4176 4115 l 3936 4175 l 3936 4115 l 3936 4055 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
3936 3446 m 4176 3506 l 3936 3566 l 4260 3566 l 4260 3446 l  cp clip
n 2647 3506 m 4230 3506 l gs col-1 s gr gr

% arrowhead
n 3936 3446 m 4176 3506 l 3936 3566 l 3936 3506 l 3936 3446 l  cp gs 0.00 setgray ef gr  col-1 s
7.500 slw
% Polyline
n 6057 767 m 6057 1984 l gs col-1 s gr 
% Polyline
n 6057 3202 m 6057 4419 l gs col-1 s gr 
% Polyline
n 9101 767 m 9101 1984 l gs col-1 s gr 
% Polyline
n 9101 3202 m 9101 4419 l gs col-1 s gr 
% Polyline
n 4839 3202 m 4230 3202 l 4230 4419 l 4839 4419 l gs col-1 s gr 
% Polyline
n 4230 3811 m 4839 3811 l gs col-1 s gr 
% Polyline
n 4839 767 m 4230 767 l 4230 1984 l 4839 1984 l gs col-1 s gr 
% Polyline
n 4230 1375 m 4839 1375 l gs col-1 s gr 
% Polyline
n 6057 767 m 5448 767 l 5448 1984 l 6057 1984 l gs col-1 s gr 
% Polyline
n 5448 1375 m 6057 1375 l gs col-1 s gr 
% Polyline
n 6057 3202 m 5448 3202 l 5448 4419 l 6057 4419 l gs col-1 s gr 
% Polyline
n 5448 3811 m 6057 3811 l gs col-1 s gr 
% Polyline
n 7883 767 m 7274 767 l 7274 1984 l 7883 1984 l gs col-1 s gr 
% Polyline
n 7274 1375 m 7883 1375 l gs col-1 s gr 
% Polyline
n 9101 767 m 8492 767 l 8492 1984 l 9101 1984 l gs col-1 s gr 
% Polyline
n 8492 1375 m 9101 1375 l gs col-1 s gr 
% Polyline
n 9101 3202 m 8492 3202 l 8492 4419 l 9101 4419 l gs col-1 s gr 
% Polyline
n 8492 3811 m 9101 3811 l gs col-1 s gr 
% Polyline
n 7883 3202 m 7274 3202 l 7274 4419 l 7883 4419 l gs col-1 s gr 
% Polyline
n 7274 3811 m 7883 3811 l gs col-1 s gr 
% Polyline
n 4839 1375 m 5448 1375 l gs col-1 s gr 
% Polyline
n 5448 767 m 4839 767 l 4839 1984 l 5448 1984 l gs col-1 s gr 
% Polyline
n 7883 1375 m 8492 1375 l gs col-1 s gr 
% Polyline
n 8492 767 m 7883 767 l 7883 1984 l 8492 1984 l gs col-1 s gr 
% Polyline
n 7883 3811 m 8492 3811 l gs col-1 s gr 
% Polyline
n 8492 3202 m 7883 3202 l 7883 4419 l 8492 4419 l gs col-1 s gr 
% Polyline
n 4839 3811 m 5448 3811 l gs col-1 s gr 
% Polyline
n 5448 3202 m 4839 3202 l 4839 4419 l 5448 4419 l gs col-1 s gr 
% Polyline
 [15 133.3] 133.3 sd
n 2404 1984 m 2404 3202 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 3013 1984 m 3013 3202 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 4230 1984 m 4230 3202 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 4839 1984 m 4839 3202 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 5448 1984 m 5448 3202 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 6057 1984 m 6057 3202 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 7274 1984 m 7274 3202 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 7883 1984 m 7883 3202 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 8492 1984 m 8492 3202 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 9101 1984 m 9101 3202 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 6057 3202 m 7274 3202 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 6057 3811 m 7274 3811 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 6057 4419 m 7274 4419 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 6057 1984 m 7274 1984 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 6057 1375 m 7274 1375 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 133.3] 133.3 sd
n 6057 767 m 7274 767 l gs col-1 s gr  [] 0 sd
$F2psEnd
rs
$EOD
$!
$CREATE fig_dostep-a.ps
$DECK
%!PS-Adobe-2.0 EPSF-2.0
%%Title: fig_dostep-a.fig
%%Creator: fig2dev Version 3.1 Patchlevel 2
%%CreationDate: Tue Jan  6 10:00:27 1998
%%For: keith@metropolis (Keith Refson)
%Magnification: 0.69
%%Orientation: Portrait
%%BoundingBox: 0 0 442 441
%%Pages: 0
%%BeginSetup
%%IncludeFeature: *PageSize A4
%%EndSetup
%%EndComments
/$F2psDict 200 dict def
$F2psDict begin
$F2psDict /mtrx matrix put
/col-1 {0 setgray} bind def
/col0 {0.000 0.000 0.000 srgb} bind def
/col1 {0.000 0.000 1.000 srgb} bind def
/col2 {0.000 1.000 0.000 srgb} bind def
/col3 {0.000 1.000 1.000 srgb} bind def
/col4 {1.000 0.000 0.000 srgb} bind def
/col5 {1.000 0.000 1.000 srgb} bind def
/col6 {1.000 1.000 0.000 srgb} bind def
/col7 {1.000 1.000 1.000 srgb} bind def
/col8 {0.000 0.000 0.560 srgb} bind def
/col9 {0.000 0.000 0.690 srgb} bind def
/col10 {0.000 0.000 0.820 srgb} bind def
/col11 {0.530 0.810 1.000 srgb} bind def
/col12 {0.000 0.560 0.000 srgb} bind def
/col13 {0.000 0.690 0.000 srgb} bind def
/col14 {0.000 0.820 0.000 srgb} bind def
/col15 {0.000 0.560 0.560 srgb} bind def
/col16 {0.000 0.690 0.690 srgb} bind def
/col17 {0.000 0.820 0.820 srgb} bind def
/col18 {0.560 0.000 0.000 srgb} bind def
/col19 {0.690 0.000 0.000 srgb} bind def
/col20 {0.820 0.000 0.000 srgb} bind def
/col21 {0.560 0.000 0.560 srgb} bind def
/col22 {0.690 0.000 0.690 srgb} bind def
/col23 {0.820 0.000 0.820 srgb} bind def
/col24 {0.500 0.190 0.000 srgb} bind def
/col25 {0.630 0.250 0.000 srgb} bind def
/col26 {0.750 0.380 0.000 srgb} bind def
/col27 {1.000 0.500 0.500 srgb} bind def
/col28 {1.000 0.630 0.630 srgb} bind def
/col29 {1.000 0.750 0.750 srgb} bind def
/col30 {1.000 0.880 0.880 srgb} bind def
/col31 {1.000 0.840 0.000 srgb} bind def

end
save
-8.0 449.0 translate
1 -1 scale

/cp {closepath} bind def
/ef {eofill} bind def
/gr {grestore} bind def
/gs {gsave} bind def
/sa {save} bind def
/rs {restore} bind def
/l {lineto} bind def
/m {moveto} bind def
/rm {rmoveto} bind def
/n {newpath} bind def
/s {stroke} bind def
/sh {show} bind def
/slc {setlinecap} bind def
/slj {setlinejoin} bind def
/slw {setlinewidth} bind def
/srgb {setrgbcolor} bind def
/rot {rotate} bind def
/sc {scale} bind def
/sd {setdash} bind def
/ff {findfont} bind def
/sf {setfont} bind def
/scf {scalefont} bind def
/sw {stringwidth} bind def
/tr {translate} bind def
/tnt {dup dup currentrgbcolor
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
  bind def
/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
  4 -2 roll mul srgb} bind def
 /DrawEllipse {
	/endangle exch def
	/startangle exch def
	/yrad exch def
	/xrad exch def
	/y exch def
	/x exch def
	/savematrix mtrx currentmatrix def
	x y tr xrad yrad sc 0 0 1 startangle endangle arc
	closepath
	savematrix setmatrix
	} def

 /DrawSplineSection {
	/y3 exch def
	/x3 exch def
	/y2 exch def
	/x2 exch def
	/y1 exch def
	/x1 exch def
	/xa x1 x2 x1 sub 0.666667 mul add def
	/ya y1 y2 y1 sub 0.666667 mul add def
	/xb x3 x2 x3 sub 0.666667 mul add def
	/yb y3 y2 y3 sub 0.666667 mul add def
	x1 y1 lineto
	xa ya xb yb x3 y3 curveto
	} def

/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
/$F2psEnd {$F2psEnteredState restore end} def
%%EndProlog

$F2psBegin
10 setmiterlimit
n 0 842 m 0 0 l 595 0 l 595 842 l cp clip
 0.04157 0.04157 sc
7.500 slw
% Polyline
n 1480 225 m 2155 225 l gs col-1 s gr 
% Polyline
n 1480 675 m 2155 675 l gs col-1 s gr 
% Open spline
gs n 1480.0 225.0 m 1255.0 225.0 l
	1255.0 225.0 1030.0 225.0 1030.0 450.0 DrawSplineSection
	1030.0 450.0 1030.0 675.0 1255.0 675.0 DrawSplineSection
	1480.0 675.0 l  gs col-1 s gr
 gr

% Open spline
gs n 2155.0 225.0 m 2380.0 225.0 l
	2380.0 225.0 2605.0 225.0 2605.0 450.0 DrawSplineSection
	2605.0 450.0 2605.0 675.0 2380.0 675.0 DrawSplineSection
	2155.0 675.0 l  gs col-1 s gr
 gr

% Ellipse
n 1800 6180 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7425 10440 335 335 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 1800 7313 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7425 5535 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7425 1080 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 9682 2201 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 9682 8006 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 9682 6881 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 9682 9131 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 1795 4950 1260 450 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 4057 8456 900 315 0 360 DrawEllipse gs col-1 s gr

% Polyline
gs  clippath
1825 956 m 1795 1076 l 1765 956 l 1765 1118 l 1825 1118 l  cp clip
n 1795 1103 m 1795 675 l gs col-1 s gr gr

% arrowhead
n 1825 956 m 1795 1076 l 1765 956 l 1795 956 l 1825 956 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1830 6858 m 1800 6978 l 1770 6858 l 1770 7020 l 1830 7020 l  cp clip
n 1800 6498 m 1800 7005 l gs col-1 s gr gr

% arrowhead
n 1830 6858 m 1800 6978 l 1770 6858 l 1800 6858 l 1830 6858 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1830 4368 m 1800 4488 l 1770 4368 l 1770 4530 l 1830 4530 l  cp clip
n 1800 4050 m 1800 4515 l gs col-1 s gr gr

% arrowhead
n 1830 4368 m 1800 4488 l 1770 4368 l 1800 4368 l 1830 4368 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1830 3228 m 1800 3348 l 1770 3228 l 1770 3390 l 1830 3390 l  cp clip
n 1800 3375 m 1800 2925 l gs col-1 s gr gr

% arrowhead
n 1830 3228 m 1800 3348 l 1770 3228 l 1800 3228 l 1830 3228 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 1030 2250 m 2605 2250 l 2605 2925 l 1030 2925 l cp gs col-1 s gr 
% Polyline
n 1030 3375 m 2605 3375 l 2605 4050 l 1030 4050 l cp gs col-1 s gr 
% Polyline
n 3150 1118 m 4725 1118 l 4725 1793 l 3150 1793 l cp gs col-1 s gr 
% Polyline
gs  clippath
1830 5718 m 1800 5838 l 1770 5718 l 1770 5880 l 1830 5880 l  cp clip
n 1800 5400 m 1800 5865 l gs col-1 s gr gr

% arrowhead
n 1830 5718 m 1800 5838 l 1770 5718 l 1800 5718 l 1830 5718 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 1800 7835 m 1800 7835 l gs col-1 s gr
% Polyline
gs  clippath
1952 10161 m 1832 10131 l 1952 10101 l 1790 10101 l 1790 10161 l  cp clip
n 4055 9906 m 4055 10131 l 1805 10131 l gs col-1 s gr gr

% arrowhead
n 1952 10161 m 1832 10131 l 1952 10101 l 1952 10131 l 1952 10161 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 3157 9224 m 4732 9224 l 4732 9899 l 3157 9899 l cp gs col-1 s gr 
% Polyline
gs  clippath
3008 9541 m 3128 9571 l 3008 9601 l 3170 9601 l 3170 9541 l  cp clip
n 2700 9571 m 3155 9571 l gs col-1 s gr gr

% arrowhead
n 3008 9541 m 3128 9571 l 3008 9601 l 3008 9571 l 3008 9541 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7455 618 m 7425 738 l 7395 618 l 7395 780 l 7455 780 l  cp clip
n 1800 9930 m 1800 10575 l 5625 10575 l 5625 225 l 7425 225 l 7425 765 l gs col-1 s gr gr

% arrowhead
n 7455 618 m 7425 738 l 7395 618 l 7425 618 l 7455 618 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1830 7953 m 1800 8073 l 1770 7953 l 1770 8115 l 1830 8115 l  cp clip
n 1800 8100 m 1800 7632 l gs col-1 s gr gr

% arrowhead
n 1830 7953 m 1800 8073 l 1770 7953 l 1800 7953 l 1830 7953 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7460 6378 m 7430 6498 l 7400 6378 l 7400 6540 l 7460 6540 l  cp clip
n 7430 5850 m 7430 6525 l gs col-1 s gr gr

% arrowhead
n 7460 6378 m 7430 6498 l 7400 6378 l 7430 6378 l 7460 6378 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7460 9961 m 7430 10081 l 7400 9961 l 7400 10123 l 7460 10123 l  cp clip
n 7430 7252 m 7430 10108 l gs col-1 s gr gr

% arrowhead
n 7460 9961 m 7430 10081 l 7400 9961 l 7430 9961 l 7460 9961 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7577 9705 m 7457 9675 l 7577 9645 l 7415 9645 l 7415 9705 l  cp clip
n 9680 9451 m 9680 9675 l 7430 9675 l gs col-1 s gr gr

% arrowhead
n 7577 9705 m 7457 9675 l 7577 9645 l 7577 9675 l 7577 9705 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2995 1432 m 3115 1462 l 2995 1492 l 3157 1492 l 3157 1432 l  cp clip
n 2707 1462 m 3142 1462 l gs col-1 s gr gr

% arrowhead
n 2995 1432 m 3115 1462 l 2995 1492 l 2995 1462 l 2995 1432 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
 [100.0] 0 sd
n 6075 450 m 10800 450 l 10800 6075 l 6075 6075 l cp gs col-1 s gr  [] 0 sd
% Polyline
gs  clippath
7454 5074 m 7424 5194 l 7394 5074 l 7394 5236 l 7454 5236 l  cp clip
n 7424 4770 m 7424 5221 l gs col-1 s gr gr

% arrowhead
n 7454 5074 m 7424 5194 l 7394 5074 l 7424 5074 l 7454 5074 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 6640 2970 m 8215 2970 l 8215 3645 l 6640 3645 l cp gs col-1 s gr 
% Polyline
n 6640 4095 m 8215 4095 l 8215 4770 l 6640 4770 l cp gs col-1 s gr 
% Polyline
gs  clippath
7455 1698 m 7425 1818 l 7395 1698 l 7395 1860 l 7455 1860 l  cp clip
n 7425 1395 m 7425 1845 l gs col-1 s gr gr

% arrowhead
n 7455 1698 m 7425 1818 l 7395 1698 l 7425 1698 l 7455 1698 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 8330 2205 m 7430 1845 l 6530 2205 l 7430 2565 l cp gs col-1 s gr 
% Polyline
gs  clippath
7460 2823 m 7430 2943 l 7400 2823 l 7400 2985 l 7460 2985 l  cp clip
n 7430 2565 m 7430 2970 l gs col-1 s gr gr

% arrowhead
n 7460 2823 m 7430 2943 l 7400 2823 l 7430 2823 l 7460 2823 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7455 3948 m 7425 4068 l 7395 3948 l 7395 4110 l 7455 4110 l  cp clip
n 7425 3645 m 7425 4095 l gs col-1 s gr gr

% arrowhead
n 7455 3948 m 7425 4068 l 7395 3948 l 7425 3948 l 7455 3948 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7582 2775 m 7462 2745 l 7582 2715 l 7420 2715 l 7420 2775 l  cp clip
n 9675 2520 m 9675 2745 l 7435 2745 l gs col-1 s gr gr

% arrowhead
n 7582 2775 m 7462 2745 l 7582 2715 l 7582 2745 l 7582 2775 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
 [100.0] 0 sd
n 225 5625 m 5175 5625 l 5175 10350 l 225 10350 l cp gs col-1 s gr  [] 0 sd
% Polyline
gs  clippath
8633 6861 m 8753 6891 l 8633 6921 l 8795 6921 l 8795 6861 l  cp clip
n 8335 6891 m 8780 6891 l gs col-1 s gr gr

% arrowhead
n 8633 6861 m 8753 6891 l 8633 6921 l 8633 6891 l 8633 6861 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
9705 7548 m 9675 7668 l 9645 7548 l 9645 7710 l 9705 7710 l  cp clip
n 9675 7200 m 9675 7695 l gs col-1 s gr gr

% arrowhead
n 9705 7548 m 9675 7668 l 9645 7548 l 9675 7548 l 9705 7548 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
9705 8673 m 9675 8793 l 9645 8673 l 9645 8835 l 9705 8835 l  cp clip
n 9675 8325 m 9675 8820 l gs col-1 s gr gr

% arrowhead
n 9705 8673 m 9675 8793 l 9645 8673 l 9675 8673 l 9705 8673 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
 [100.0] 0 sd
n 6075 6300 m 6075 9900 l 8100 9900 l 10800 9893 l 10800 6300 l cp gs col-1 s gr  [] 0 sd
% Polyline
gs  clippath
1947 2055 m 1827 2025 l 1947 1995 l 1785 1995 l 1785 2055 l  cp clip
n 3960 1800 m 3960 2025 l 1800 2025 l gs col-1 s gr gr

% arrowhead
n 1947 2055 m 1827 2025 l 1947 1995 l 1947 2025 l 1947 2055 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 2695 1463 m 1795 1103 l 895 1463 l 1795 1823 l cp gs col-1 s gr 
% Polyline
gs  clippath
1824 2103 m 1794 2223 l 1764 2103 l 1764 2265 l 1824 2265 l  cp clip
n 1794 1823 m 1794 2250 l gs col-1 s gr gr

% arrowhead
n 1824 2103 m 1794 2223 l 1764 2103 l 1794 2103 l 1824 2103 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
3008 8436 m 3128 8466 l 3008 8496 l 3170 8496 l 3170 8436 l  cp clip
n 2710 8466 m 3155 8466 l gs col-1 s gr gr

% arrowhead
n 3008 8436 m 3128 8466 l 3008 8496 l 3008 8466 l 3008 8436 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1952 9036 m 1832 9006 l 1952 8976 l 1790 8976 l 1790 9036 l  cp clip
n 4055 8776 m 4055 9006 l 1805 9006 l gs col-1 s gr gr

% arrowhead
n 1952 9036 m 1832 9006 l 1952 8976 l 1952 9006 l 1952 9036 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1830 9064 m 1800 9184 l 1770 9064 l 1770 9226 l 1830 9226 l  cp clip
n 1800 8826 m 1800 9211 l gs col-1 s gr gr

% arrowhead
n 1830 9064 m 1800 9184 l 1770 9064 l 1800 9064 l 1830 9064 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 2700 8460 m 1800 8100 l 900 8460 l 1800 8820 l cp gs col-1 s gr 
% Polyline
n 2700 9569 m 1800 9209 l 900 9569 l 1800 9929 l cp gs col-1 s gr 
% Polyline
n 8330 6885 m 7430 6525 l 6530 6885 l 7430 7245 l cp gs col-1 s gr 
% Polyline
gs  clippath
8630 2174 m 8750 2204 l 8630 2234 l 8792 2234 l 8792 2174 l  cp clip
n 8332 2204 m 8777 2204 l gs col-1 s gr gr

% arrowhead
n 8630 2174 m 8750 2204 l 8630 2234 l 8630 2204 l 8630 2174 l  cp gs 0.00 setgray ef gr  col-1 s
$F2psEnd
rs
$EOD
$!
$CREATE fig_dostep-b.ps
$DECK
%!PS-Adobe-2.0 EPSF-2.0
%%Title: fig_dostep-b.fig
%%Creator: fig2dev Version 3.1 Patchlevel 2
%%CreationDate: Tue Jan  6 10:00:27 1998
%%For: keith@metropolis (Keith Refson)
%Magnification: 0.69
%%Orientation: Portrait
%%BoundingBox: 0 0 435 433
%%Pages: 0
%%BeginSetup
%%IncludeFeature: *PageSize A4
%%EndSetup
%%EndComments
/$F2psDict 200 dict def
$F2psDict begin
$F2psDict /mtrx matrix put
/col-1 {0 setgray} bind def
/col0 {0.000 0.000 0.000 srgb} bind def
/col1 {0.000 0.000 1.000 srgb} bind def
/col2 {0.000 1.000 0.000 srgb} bind def
/col3 {0.000 1.000 1.000 srgb} bind def
/col4 {1.000 0.000 0.000 srgb} bind def
/col5 {1.000 0.000 1.000 srgb} bind def
/col6 {1.000 1.000 0.000 srgb} bind def
/col7 {1.000 1.000 1.000 srgb} bind def
/col8 {0.000 0.000 0.560 srgb} bind def
/col9 {0.000 0.000 0.690 srgb} bind def
/col10 {0.000 0.000 0.820 srgb} bind def
/col11 {0.530 0.810 1.000 srgb} bind def
/col12 {0.000 0.560 0.000 srgb} bind def
/col13 {0.000 0.690 0.000 srgb} bind def
/col14 {0.000 0.820 0.000 srgb} bind def
/col15 {0.000 0.560 0.560 srgb} bind def
/col16 {0.000 0.690 0.690 srgb} bind def
/col17 {0.000 0.820 0.820 srgb} bind def
/col18 {0.560 0.000 0.000 srgb} bind def
/col19 {0.690 0.000 0.000 srgb} bind def
/col20 {0.820 0.000 0.000 srgb} bind def
/col21 {0.560 0.000 0.560 srgb} bind def
/col22 {0.690 0.000 0.690 srgb} bind def
/col23 {0.820 0.000 0.820 srgb} bind def
/col24 {0.500 0.190 0.000 srgb} bind def
/col25 {0.630 0.250 0.000 srgb} bind def
/col26 {0.750 0.380 0.000 srgb} bind def
/col27 {1.000 0.500 0.500 srgb} bind def
/col28 {1.000 0.630 0.630 srgb} bind def
/col29 {1.000 0.750 0.750 srgb} bind def
/col30 {1.000 0.880 0.880 srgb} bind def
/col31 {1.000 0.840 0.000 srgb} bind def

end
save
-15.0 441.0 translate
1 -1 scale

/cp {closepath} bind def
/ef {eofill} bind def
/gr {grestore} bind def
/gs {gsave} bind def
/sa {save} bind def
/rs {restore} bind def
/l {lineto} bind def
/m {moveto} bind def
/rm {rmoveto} bind def
/n {newpath} bind def
/s {stroke} bind def
/sh {show} bind def
/slc {setlinecap} bind def
/slj {setlinejoin} bind def
/slw {setlinewidth} bind def
/srgb {setrgbcolor} bind def
/rot {rotate} bind def
/sc {scale} bind def
/sd {setdash} bind def
/ff {findfont} bind def
/sf {setfont} bind def
/scf {scalefont} bind def
/sw {stringwidth} bind def
/tr {translate} bind def
/tnt {dup dup currentrgbcolor
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
  bind def
/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
  4 -2 roll mul srgb} bind def
 /DrawEllipse {
	/endangle exch def
	/startangle exch def
	/yrad exch def
	/xrad exch def
	/y exch def
	/x exch def
	/savematrix mtrx currentmatrix def
	x y tr xrad yrad sc 0 0 1 startangle endangle arc
	closepath
	savematrix setmatrix
	} def

 /DrawSplineSection {
	/y3 exch def
	/x3 exch def
	/y2 exch def
	/x2 exch def
	/y1 exch def
	/x1 exch def
	/xa x1 x2 x1 sub 0.666667 mul add def
	/ya y1 y2 y1 sub 0.666667 mul add def
	/xb x3 x2 x3 sub 0.666667 mul add def
	/yb y3 y2 y3 sub 0.666667 mul add def
	x1 y1 lineto
	xa ya xb yb x3 y3 curveto
	} def

/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
/$F2psEnd {$F2psEnteredState restore end} def
%%EndProlog

$F2psBegin
10 setmiterlimit
n 0 842 m 0 0 l 595 0 l 595 842 l cp clip
 0.04157 0.04157 sc
7.500 slw
% Polyline
n 6836 10129 m 7511 10129 l gs col-1 s gr 
% Polyline
n 6836 10579 m 7511 10579 l gs col-1 s gr 
% Open spline
gs n 6836.0 10129.0 m 6611.0 10129.0 l
	6611.0 10129.0 6386.0 10129.0 6386.0 10354.0 DrawSplineSection
	6386.0 10354.0 6386.0 10579.0 6611.0 10579.0 DrawSplineSection
	6836.0 10579.0 l  gs col-1 s gr
 gr

% Open spline
gs n 7511.0 10129.0 m 7736.0 10129.0 l
	7736.0 10129.0 7961.0 10129.0 7961.0 10354.0 DrawSplineSection
	7961.0 10354.0 7961.0 10579.0 7736.0 10579.0 DrawSplineSection
	7511.0 10579.0 l  gs col-1 s gr
 gr

% Ellipse
n 9439 2279 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 9446 1057 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7189 2279 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7189 3401 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7192 4523 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7200 8099 1260 450 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7196 5632 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 9434 9386 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 1800 605 335 335 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 4050 5210 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 4057 3988 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 1800 5210 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 1800 6332 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 1803 7454 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 4057 2786 900 338 0 360 DrawEllipse gs col-1 s gr

% Polyline
gs  clippath
1830 9258 m 1800 9378 l 1770 9258 l 1770 9420 l 1830 9420 l  cp clip
n 1800 8955 m 1800 9405 l gs col-1 s gr gr

% arrowhead
n 1830 9258 m 1800 9378 l 1770 9258 l 1800 9258 l 1830 9258 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7053 7395 m 7173 7425 l 7053 7455 l 7215 7455 l 7215 7395 l  cp clip
n 1804 10140 m 1804 10365 l 5865 10365 l 5865 7425 l 7200 7425 l gs col-1 s gr gr

% arrowhead
n 7053 7395 m 7173 7425 l 7053 7455 l 7053 7425 l 7053 7395 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1653 9194 m 1773 9224 l 1653 9254 l 1815 9254 l 1815 9194 l  cp clip
n 892 1620 m 375 1620 l 375 9224 l 1800 9224 l gs col-1 s gr gr

% arrowhead
n 1653 9194 m 1773 9224 l 1653 9254 l 1653 9224 l 1653 9194 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7224 1804 m 7194 1924 l 7164 1804 l 7164 1966 l 7224 1966 l  cp clip
n 7194 1439 m 7194 1951 l gs col-1 s gr gr

% arrowhead
n 7224 1804 m 7194 1924 l 7164 1804 l 7194 1804 l 7224 1804 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7219 2914 m 7189 3034 l 7159 2914 l 7159 3076 l 7219 3076 l  cp clip
n 7189 2617 m 7189 3061 l gs col-1 s gr gr

% arrowhead
n 7219 2914 m 7189 3034 l 7159 2914 l 7189 2914 l 7219 2914 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 9439 1389 m 9439 1968 l gs col-1 s gr 
% Polyline
gs  clippath
7341 2914 m 7221 2884 l 7341 2854 l 7179 2854 l 7179 2914 l  cp clip
n 9444 2611 m 9444 2884 l 7194 2884 l gs col-1 s gr gr

% arrowhead
n 7341 2914 m 7221 2884 l 7341 2854 l 7341 2884 l 7341 2914 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
8397 1023 m 8517 1053 l 8397 1083 l 8559 1083 l 8559 1023 l  cp clip
n 8099 1053 m 8544 1053 l gs col-1 s gr gr

% arrowhead
n 8397 1023 m 8517 1053 l 8397 1083 l 8397 1053 l 8397 1023 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7219 4039 m 7189 4159 l 7159 4039 l 7159 4201 l 7219 4201 l  cp clip
n 7189 3736 m 7189 4186 l gs col-1 s gr gr

% arrowhead
n 7219 4039 m 7189 4159 l 7159 4039 l 7189 4039 l 7219 4039 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7223 5170 m 7193 5290 l 7163 5170 l 7163 5332 l 7223 5332 l  cp clip
n 7193 4858 m 7193 5317 l gs col-1 s gr gr

% arrowhead
n 7223 5170 m 7193 5290 l 7163 5170 l 7193 5170 l 7223 5170 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7225 6245 m 7195 6365 l 7165 6245 l 7165 6407 l 7225 6407 l  cp clip
n 7195 5952 m 7195 6392 l gs col-1 s gr gr

% arrowhead
n 7225 6245 m 7195 6365 l 7165 6245 l 7195 6245 l 7225 6245 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7225 9982 m 7195 10102 l 7165 9982 l 7165 10144 l 7225 10144 l  cp clip
n 7195 9765 m 7195 10129 l gs col-1 s gr gr

% arrowhead
n 7225 9982 m 7195 10102 l 7165 9982 l 7195 9982 l 7225 9982 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7230 7502 m 7200 7622 l 7170 7502 l 7170 7664 l 7230 7664 l  cp clip
n 7200 7124 m 7200 7649 l gs col-1 s gr gr

% arrowhead
n 7230 7502 m 7200 7622 l 7170 7502 l 7200 7502 l 7230 7502 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7230 8853 m 7200 8973 l 7170 8853 l 7170 9015 l 7230 9015 l  cp clip
n 7200 8554 m 7200 9000 l gs col-1 s gr gr

% arrowhead
n 7230 8853 m 7200 8973 l 7170 8853 l 7200 8853 l 7230 8853 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7336 477 m 7216 447 l 7336 417 l 7174 417 l 7174 477 l  cp clip
n 8095 6760 m 10578 6760 l 10578 447 l 7189 447 l gs col-1 s gr gr

% arrowhead
n 7336 477 m 7216 447 l 7336 417 l 7336 447 l 7336 477 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7042 3910 m 7162 3940 l 7042 3970 l 7204 3970 l 7204 3910 l  cp clip
n 7189 3940 m 6060 3940 l 6060 1050 l 6289 1050 l gs col-1 s gr gr

% arrowhead
n 7042 3910 m 7162 3940 l 7042 3970 l 7042 3940 l 7042 3910 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
8382 9354 m 8502 9384 l 8382 9414 l 8544 9414 l 8544 9354 l  cp clip
n 8104 9384 m 8529 9384 l gs col-1 s gr gr

% arrowhead
n 8382 9354 m 8502 9384 l 8382 9414 l 8382 9384 l 8382 9354 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7341 9936 m 7221 9906 l 7341 9876 l 7179 9876 l 7179 9936 l  cp clip
n 9439 9706 m 9439 9906 l 7194 9906 l gs col-1 s gr gr

% arrowhead
n 7341 9936 m 7221 9906 l 7341 9876 l 7341 9906 l 7341 9936 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 2700 9780 m 1800 9420 l 900 9780 l 1800 10140 l cp gs col-1 s gr 
% Polyline
gs  clippath
7220 520 m 7190 640 l 7160 520 l 7160 682 l 7220 682 l  cp clip
n 2700 9780 m 5625 9780 l 5625 225 l 7190 225 l 7190 667 l gs col-1 s gr gr

% arrowhead
n 7220 520 m 7190 640 l 7160 520 l 7190 520 l 7220 520 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 8090 1053 m 7190 667 l 6290 1053 l 7190 1439 l cp gs col-1 s gr 
% Polyline
n 8095 6760 m 7195 6400 l 6295 6760 l 7195 7120 l cp gs col-1 s gr 
% Polyline
n 8100 9384 m 7200 8998 l 6300 9384 l 7200 9770 l cp gs col-1 s gr 
% Polyline
 [66.7] 0 sd
n 5865 330 m 10800 330 l 10800 7316 l 5865 7316 l cp gs col-1 s gr  [] 0 sd
% Polyline
gs  clippath
1947 2178 m 1826 2148 l 1947 2118 l 1785 2118 l 1785 2178 l  cp clip
n 2700 8589 m 5175 8589 l 5175 2150 l 1800 2148 l gs col-1 s gr gr

% arrowhead
n 1947 2178 m 1826 2148 l 1947 2118 l 1947 2148 l 1947 2178 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1835 3450 m 1805 3570 l 1775 3450 l 1775 3612 l 1835 3612 l  cp clip
n 1805 3179 m 1805 3597 l gs col-1 s gr gr

% arrowhead
n 1835 3450 m 1805 3570 l 1775 3450 l 1805 3450 l 1835 3450 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1952 3410 m 1832 3380 l 1952 3350 l 1790 3350 l 1790 3410 l  cp clip
n 4055 3122 m 4055 3380 l 1805 3380 l gs col-1 s gr gr

% arrowhead
n 1952 3410 m 1832 3380 l 1952 3350 l 1952 3380 l 1952 3410 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 2700 2785 m 1800 2399 l 900 2785 l 1800 3171 l cp gs col-1 s gr 
% Polyline
gs  clippath
3008 2755 m 3128 2785 l 3008 2815 l 3170 2815 l 3170 2755 l  cp clip
n 2710 2785 m 3155 2785 l gs col-1 s gr gr

% arrowhead
n 3008 2755 m 3128 2785 l 3008 2815 l 3008 2785 l 3008 2755 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1830 5845 m 1800 5965 l 1770 5845 l 1770 6007 l 1830 6007 l  cp clip
n 1800 5548 m 1800 5992 l gs col-1 s gr gr

% arrowhead
n 1830 5845 m 1800 5965 l 1770 5845 l 1800 5845 l 1830 5845 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 4050 4320 m 4050 4899 l gs col-1 s gr 
% Polyline
gs  clippath
1952 5845 m 1832 5815 l 1952 5785 l 1790 5785 l 1790 5845 l  cp clip
n 4055 5542 m 4055 5815 l 1805 5815 l gs col-1 s gr gr

% arrowhead
n 1952 5845 m 1832 5815 l 1952 5785 l 1952 5815 l 1952 5845 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1653 6857 m 1773 6887 l 1653 6917 l 1815 6917 l 1815 6857 l  cp clip
n 1800 6887 m 675 6887 l 675 3983 l 900 3991 l gs col-1 s gr gr

% arrowhead
n 1653 6857 m 1773 6887 l 1653 6917 l 1653 6887 l 1653 6857 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
3008 3954 m 3128 3984 l 3008 4014 l 3170 4014 l 3170 3954 l  cp clip
n 2710 3984 m 3155 3984 l gs col-1 s gr gr

% arrowhead
n 3008 3954 m 3128 3984 l 3008 4014 l 3008 3984 l 3008 3954 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 2700 3984 m 1800 3598 l 900 3984 l 1800 4370 l cp gs col-1 s gr 
% Polyline
gs  clippath
1830 6970 m 1800 7090 l 1770 6970 l 1770 7132 l 1830 7132 l  cp clip
n 1800 6667 m 1800 7117 l gs col-1 s gr gr

% arrowhead
n 1830 6970 m 1800 7090 l 1770 6970 l 1800 6970 l 1830 6970 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 2700 8589 m 1800 8229 l 900 8589 l 1800 8949 l cp gs col-1 s gr 
% Polyline
gs  clippath
1834 8077 m 1804 8197 l 1774 8077 l 1774 8239 l 1834 8239 l  cp clip
n 1804 7789 m 1804 8224 l gs col-1 s gr gr

% arrowhead
n 1834 8077 m 1804 8197 l 1774 8077 l 1804 8077 l 1834 8077 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 2700 1620 m 1800 1260 l 900 1620 l 1800 1980 l cp gs col-1 s gr 
% Polyline
gs  clippath
1830 1113 m 1800 1233 l 1770 1113 l 1770 1275 l 1830 1275 l  cp clip
n 1800 946 m 1800 1260 l gs col-1 s gr gr

% arrowhead
n 1830 1113 m 1800 1233 l 1770 1113 l 1800 1113 l 1830 1113 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1830 2246 m 1800 2366 l 1770 2246 l 1770 2408 l 1830 2408 l  cp clip
n 1800 1982 m 1800 2393 l gs col-1 s gr gr

% arrowhead
n 1830 2246 m 1800 2366 l 1770 2246 l 1800 2246 l 1830 2246 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1828 4717 m 1798 4837 l 1768 4717 l 1768 4879 l 1828 4879 l  cp clip
n 1798 4370 m 1798 4864 l gs col-1 s gr gr

% arrowhead
n 1828 4717 m 1798 4837 l 1768 4717 l 1798 4717 l 1828 4717 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
 [66.7] 0 sd
n 540 2040 m 5310 2040 l 5310 9135 l 540 9135 l cp gs col-1 s gr  [] 0 sd
$F2psEnd
rs
$EOD
$!
$CREATE fig_ewald.ps
$DECK
%!PS-Adobe-2.0 EPSF-2.0
%%Title: fig_ewald.fig
%%Creator: fig2dev Version 3.1 Patchlevel 2
%%CreationDate: Tue Jan  6 10:00:28 1998
%%For: keith@metropolis (Keith Refson)
%Magnification: 0.69
%%Orientation: Portrait
%%BoundingBox: 0 0 223 386
%%Pages: 0
%%BeginSetup
%%IncludeFeature: *PageSize A4
%%EndSetup
%%EndComments
/$F2psDict 200 dict def
$F2psDict begin
$F2psDict /mtrx matrix put
/col-1 {0 setgray} bind def
/col0 {0.000 0.000 0.000 srgb} bind def
/col1 {0.000 0.000 1.000 srgb} bind def
/col2 {0.000 1.000 0.000 srgb} bind def
/col3 {0.000 1.000 1.000 srgb} bind def
/col4 {1.000 0.000 0.000 srgb} bind def
/col5 {1.000 0.000 1.000 srgb} bind def
/col6 {1.000 1.000 0.000 srgb} bind def
/col7 {1.000 1.000 1.000 srgb} bind def
/col8 {0.000 0.000 0.560 srgb} bind def
/col9 {0.000 0.000 0.690 srgb} bind def
/col10 {0.000 0.000 0.820 srgb} bind def
/col11 {0.530 0.810 1.000 srgb} bind def
/col12 {0.000 0.560 0.000 srgb} bind def
/col13 {0.000 0.690 0.000 srgb} bind def
/col14 {0.000 0.820 0.000 srgb} bind def
/col15 {0.000 0.560 0.560 srgb} bind def
/col16 {0.000 0.690 0.690 srgb} bind def
/col17 {0.000 0.820 0.820 srgb} bind def
/col18 {0.560 0.000 0.000 srgb} bind def
/col19 {0.690 0.000 0.000 srgb} bind def
/col20 {0.820 0.000 0.000 srgb} bind def
/col21 {0.560 0.000 0.560 srgb} bind def
/col22 {0.690 0.000 0.690 srgb} bind def
/col23 {0.820 0.000 0.820 srgb} bind def
/col24 {0.500 0.190 0.000 srgb} bind def
/col25 {0.630 0.250 0.000 srgb} bind def
/col26 {0.750 0.380 0.000 srgb} bind def
/col27 {1.000 0.500 0.500 srgb} bind def
/col28 {1.000 0.630 0.630 srgb} bind def
/col29 {1.000 0.750 0.750 srgb} bind def
/col30 {1.000 0.880 0.880 srgb} bind def
/col31 {1.000 0.840 0.000 srgb} bind def

end
save
-6.0 394.0 translate
1 -1 scale

/cp {closepath} bind def
/ef {eofill} bind def
/gr {grestore} bind def
/gs {gsave} bind def
/sa {save} bind def
/rs {restore} bind def
/l {lineto} bind def
/m {moveto} bind def
/rm {rmoveto} bind def
/n {newpath} bind def
/s {stroke} bind def
/sh {show} bind def
/slc {setlinecap} bind def
/slj {setlinejoin} bind def
/slw {setlinewidth} bind def
/srgb {setrgbcolor} bind def
/rot {rotate} bind def
/sc {scale} bind def
/sd {setdash} bind def
/ff {findfont} bind def
/sf {setfont} bind def
/scf {scalefont} bind def
/sw {stringwidth} bind def
/tr {translate} bind def
/tnt {dup dup currentrgbcolor
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
  bind def
/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
  4 -2 roll mul srgb} bind def
 /DrawEllipse {
	/endangle exch def
	/startangle exch def
	/yrad exch def
	/xrad exch def
	/y exch def
	/x exch def
	/savematrix mtrx currentmatrix def
	x y tr xrad yrad sc 0 0 1 startangle endangle arc
	closepath
	savematrix setmatrix
	} def

 /DrawSplineSection {
	/y3 exch def
	/x3 exch def
	/y2 exch def
	/x2 exch def
	/y1 exch def
	/x1 exch def
	/xa x1 x2 x1 sub 0.666667 mul add def
	/ya y1 y2 y1 sub 0.666667 mul add def
	/xb x3 x2 x3 sub 0.666667 mul add def
	/yb y3 y2 y3 sub 0.666667 mul add def
	x1 y1 lineto
	xa ya xb yb x3 y3 curveto
	} def

/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
/$F2psEnd {$F2psEnteredState restore end} def
%%EndProlog

$F2psBegin
10 setmiterlimit
n 0 842 m 0 0 l 595 0 l 595 842 l cp clip
 0.04157 0.04157 sc
7.500 slw
% Polyline
n 675 2924 m 2475 2924 l 2475 3599 l 675 3599 l cp gs col-1 s gr 
% Polyline
n 675 3826 m 2475 3826 l 2475 4501 l 675 4501 l cp gs col-1 s gr 
% Polyline
n 675 5626 m 2475 5626 l 2475 6076 l 675 6076 l cp gs col-1 s gr 
% Ellipse
n 1575 6748 1348 447 0 360 DrawEllipse gs col-1 s gr

% Polyline
n 675 7426 m 2475 7426 l 2475 7876 l 675 7876 l cp gs col-1 s gr 
% Polyline
n 675 8100 m 2475 8100 l 2475 8550 l 675 8550 l cp gs col-1 s gr 
% Polyline
n 1245 9001 m 1920 9001 l gs col-1 s gr 
% Polyline
n 1245 9451 m 1920 9451 l gs col-1 s gr 
% Open spline
gs n 1245.0 9001.0 m 1020.0 9001.0 l
	1020.0 9001.0 795.0 9001.0 795.0 9226.0 DrawSplineSection
	795.0 9226.0 795.0 9451.0 1020.0 9451.0 DrawSplineSection
	1245.0 9451.0 l  gs col-1 s gr
 gr

% Open spline
gs n 1920.0 9001.0 m 2145.0 9001.0 l
	2145.0 9001.0 2370.0 9001.0 2370.0 9226.0 DrawSplineSection
	2370.0 9226.0 2370.0 9451.0 2145.0 9451.0 DrawSplineSection
	1920.0 9451.0 l  gs col-1 s gr
 gr

% Polyline
n 1245 225 m 1920 225 l gs col-1 s gr 
% Polyline
n 1245 675 m 1920 675 l gs col-1 s gr 
% Open spline
gs n 1245.0 225.0 m 1020.0 225.0 l
	1020.0 225.0 795.0 225.0 795.0 450.0 DrawSplineSection
	795.0 450.0 795.0 675.0 1020.0 675.0 DrawSplineSection
	1245.0 675.0 l  gs col-1 s gr
 gr

% Open spline
gs n 1920.0 225.0 m 2145.0 225.0 l
	2145.0 225.0 2370.0 225.0 2370.0 450.0 DrawSplineSection
	2370.0 450.0 2370.0 675.0 2145.0 675.0 DrawSplineSection
	1920.0 675.0 l  gs col-1 s gr
 gr

% Polyline
gs  clippath
1605 753 m 1575 873 l 1545 753 l 1545 915 l 1605 915 l  cp clip
n 1575 675 m 1575 900 l gs col-1 s gr gr

% arrowhead
n 1605 753 m 1575 873 l 1545 753 l 1575 753 l 1605 753 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 575 1245 m 1575 900 l 2600 1245 l 1575 1575 l cp gs col-1 s gr 
% Polyline
n 3141 900 m 4941 900 l 4941 1575 l 3141 1575 l cp gs col-1 s gr 
% Polyline
gs  clippath
2994 1214 m 3114 1244 l 2994 1274 l 3156 1274 l 3156 1214 l  cp clip
n 2601 1244 m 3141 1244 l gs col-1 s gr gr

% arrowhead
n 2994 1214 m 3114 1244 l 2994 1274 l 2994 1244 l 2994 1214 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1731 1830 m 1611 1800 l 1731 1770 l 1569 1770 l 1569 1830 l  cp clip
n 4041 1575 m 4041 1800 l 1584 1800 l gs col-1 s gr gr

% arrowhead
n 1731 1830 m 1611 1800 l 1731 1770 l 1731 1800 l 1731 1830 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1605 1873 m 1575 1993 l 1545 1873 l 1545 2035 l 1605 2035 l  cp clip
n 1575 1570 m 1575 2020 l gs col-1 s gr gr

% arrowhead
n 1605 1873 m 1575 1993 l 1545 1873 l 1575 1873 l 1605 1873 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2750 5094 m 2630 5064 l 2750 5034 l 2588 5034 l 2588 5094 l  cp clip
n 1575 8774 m 3150 8774 l 3150 5064 l 2603 5064 l gs col-1 s gr gr

% arrowhead
n 2750 5094 m 2630 5064 l 2750 5034 l 2750 5064 l 2750 5094 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1605 7953 m 1575 8073 l 1545 7953 l 1545 8115 l 1605 8115 l  cp clip
n 1575 7880 m 1575 8100 l gs col-1 s gr gr

% arrowhead
n 1605 7953 m 1575 8073 l 1545 7953 l 1575 7953 l 1605 7953 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1605 8853 m 1575 8973 l 1545 8853 l 1545 9015 l 1605 9015 l  cp clip
n 1575 8550 m 1575 9000 l gs col-1 s gr gr

% arrowhead
n 1605 8853 m 1575 8973 l 1545 8853 l 1575 8853 l 1605 8853 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1605 7278 m 1575 7398 l 1545 7278 l 1545 7440 l 1605 7440 l  cp clip
n 1575 7205 m 1575 7425 l gs col-1 s gr gr

% arrowhead
n 1605 7278 m 1575 7398 l 1545 7278 l 1575 7278 l 1605 7278 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1605 6156 m 1575 6276 l 1545 6156 l 1545 6318 l 1605 6318 l  cp clip
n 1575 6083 m 1575 6303 l gs col-1 s gr gr

% arrowhead
n 1605 6156 m 1575 6276 l 1545 6156 l 1575 6156 l 1605 6156 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1605 5475 m 1575 5595 l 1545 5475 l 1545 5637 l 1605 5637 l  cp clip
n 1575 5402 m 1575 5622 l gs col-1 s gr gr

% arrowhead
n 1605 5475 m 1575 5595 l 1545 5475 l 1575 5475 l 1605 5475 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1605 4575 m 1575 4695 l 1545 4575 l 1545 4737 l 1605 4737 l  cp clip
n 1575 4502 m 1575 4722 l gs col-1 s gr gr

% arrowhead
n 1605 4575 m 1575 4695 l 1545 4575 l 1575 4575 l 1605 4575 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 575 5067 m 1575 4722 l 2600 5067 l 1575 5400 l cp gs col-1 s gr 
% Polyline
gs  clippath
1605 3675 m 1575 3795 l 1545 3675 l 1545 3837 l 1605 3837 l  cp clip
n 1575 3602 m 1575 3822 l gs col-1 s gr gr

% arrowhead
n 1605 3675 m 1575 3795 l 1545 3675 l 1575 3675 l 1605 3675 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1605 2775 m 1575 2895 l 1545 2775 l 1545 2937 l 1605 2937 l  cp clip
n 1575 2702 m 1575 2922 l gs col-1 s gr gr

% arrowhead
n 1605 2775 m 1575 2895 l 1545 2775 l 1575 2775 l 1605 2775 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 675 2020 m 2475 2020 l 2475 2695 l 675 2695 l cp gs col-1 s gr 
$F2psEnd
rs
$EOD
$!
$CREATE fig_link-cell.ps
$DECK
%!PS-Adobe-2.0 EPSF-2.0
%%Title: fig_link-cell.fig
%%Creator: fig2dev Version 3.1 Patchlevel 2
%%CreationDate: Tue Jan  6 10:00:28 1998
%%For: keith@metropolis (Keith Refson)
%Magnification: 0.69
%%Orientation: Portrait
%%BoundingBox: 0 0 414 460
%%Pages: 0
%%BeginSetup
%%IncludeFeature: *PageSize A4
%%EndSetup
%%EndComments
/$F2psDict 200 dict def
$F2psDict begin
$F2psDict /mtrx matrix put
/col-1 {0 setgray} bind def
/col0 {0.000 0.000 0.000 srgb} bind def
/col1 {0.000 0.000 1.000 srgb} bind def
/col2 {0.000 1.000 0.000 srgb} bind def
/col3 {0.000 1.000 1.000 srgb} bind def
/col4 {1.000 0.000 0.000 srgb} bind def
/col5 {1.000 0.000 1.000 srgb} bind def
/col6 {1.000 1.000 0.000 srgb} bind def
/col7 {1.000 1.000 1.000 srgb} bind def
/col8 {0.000 0.000 0.560 srgb} bind def
/col9 {0.000 0.000 0.690 srgb} bind def
/col10 {0.000 0.000 0.820 srgb} bind def
/col11 {0.530 0.810 1.000 srgb} bind def
/col12 {0.000 0.560 0.000 srgb} bind def
/col13 {0.000 0.690 0.000 srgb} bind def
/col14 {0.000 0.820 0.000 srgb} bind def
/col15 {0.000 0.560 0.560 srgb} bind def
/col16 {0.000 0.690 0.690 srgb} bind def
/col17 {0.000 0.820 0.820 srgb} bind def
/col18 {0.560 0.000 0.000 srgb} bind def
/col19 {0.690 0.000 0.000 srgb} bind def
/col20 {0.820 0.000 0.000 srgb} bind def
/col21 {0.560 0.000 0.560 srgb} bind def
/col22 {0.690 0.000 0.690 srgb} bind def
/col23 {0.820 0.000 0.820 srgb} bind def
/col24 {0.500 0.190 0.000 srgb} bind def
/col25 {0.630 0.250 0.000 srgb} bind def
/col26 {0.750 0.380 0.000 srgb} bind def
/col27 {1.000 0.500 0.500 srgb} bind def
/col28 {1.000 0.630 0.630 srgb} bind def
/col29 {1.000 0.750 0.750 srgb} bind def
/col30 {1.000 0.880 0.880 srgb} bind def
/col31 {1.000 0.840 0.000 srgb} bind def

end
save
-27.0 478.0 translate
1 -1 scale

/cp {closepath} bind def
/ef {eofill} bind def
/gr {grestore} bind def
/gs {gsave} bind def
/sa {save} bind def
/rs {restore} bind def
/l {lineto} bind def
/m {moveto} bind def
/rm {rmoveto} bind def
/n {newpath} bind def
/s {stroke} bind def
/sh {show} bind def
/slc {setlinecap} bind def
/slj {setlinejoin} bind def
/slw {setlinewidth} bind def
/srgb {setrgbcolor} bind def
/rot {rotate} bind def
/sc {scale} bind def
/sd {setdash} bind def
/ff {findfont} bind def
/sf {setfont} bind def
/scf {scalefont} bind def
/sw {stringwidth} bind def
/tr {translate} bind def
/tnt {dup dup currentrgbcolor
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
  bind def
/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
  4 -2 roll mul srgb} bind def
 /DrawEllipse {
	/endangle exch def
	/startangle exch def
	/yrad exch def
	/xrad exch def
	/y exch def
	/x exch def
	/savematrix mtrx currentmatrix def
	x y tr xrad yrad sc 0 0 1 startangle endangle arc
	closepath
	savematrix setmatrix
	} def

 /DrawSplineSection {
	/y3 exch def
	/x3 exch def
	/y2 exch def
	/x2 exch def
	/y1 exch def
	/x1 exch def
	/xa x1 x2 x1 sub 0.666667 mul add def
	/ya y1 y2 y1 sub 0.666667 mul add def
	/xb x3 x2 x3 sub 0.666667 mul add def
	/yb y3 y2 y3 sub 0.666667 mul add def
	x1 y1 lineto
	xa ya xb yb x3 y3 curveto
	} def

/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
/$F2psEnd {$F2psEnteredState restore end} def
%%EndProlog

$F2psBegin
10 setmiterlimit
n 0 842 m 0 0 l 595 0 l 595 842 l cp clip
 0.04157 0.04157 sc
7.500 slw
% Polyline
n 1125 9675 m 1800 9675 l gs col-1 s gr 
% Polyline
n 1125 10125 m 1800 10125 l gs col-1 s gr 
% Open spline
gs n 1125.0 9675.0 m 900.0 9675.0 l
	900.0 9675.0 675.0 9675.0 675.0 9900.0 DrawSplineSection
	675.0 9900.0 675.0 10125.0 900.0 10125.0 DrawSplineSection
	1125.0 10125.0 l  gs col-1 s gr
 gr

% Open spline
gs n 1800.0 9675.0 m 2025.0 9675.0 l
	2025.0 9675.0 2250.0 9675.0 2250.0 9900.0 DrawSplineSection
	2250.0 9900.0 2250.0 10125.0 2025.0 10125.0 DrawSplineSection
	1800.0 10125.0 l  gs col-1 s gr
 gr

% Polyline
n 6975 11025 m 7650 11025 l gs col-1 s gr 
% Polyline
n 6975 11475 m 7650 11475 l gs col-1 s gr 
% Open spline
gs n 6975.0 11025.0 m 6750.0 11025.0 l
	6750.0 11025.0 6525.0 11025.0 6525.0 11250.0 DrawSplineSection
	6525.0 11250.0 6525.0 11475.0 6750.0 11475.0 DrawSplineSection
	6975.0 11475.0 l  gs col-1 s gr
 gr

% Open spline
gs n 7650.0 11025.0 m 7875.0 11025.0 l
	7875.0 11025.0 8100.0 11025.0 8100.0 11250.0 DrawSplineSection
	8100.0 11250.0 8100.0 11475.0 7875.0 11475.0 DrawSplineSection
	7650.0 11475.0 l  gs col-1 s gr
 gr

% Polyline
n 6975 450 m 7650 450 l gs col-1 s gr 
% Polyline
n 6975 900 m 7650 900 l gs col-1 s gr 
% Open spline
gs n 6975.0 450.0 m 6750.0 450.0 l
	6750.0 450.0 6525.0 450.0 6525.0 675.0 DrawSplineSection
	6525.0 675.0 6525.0 900.0 6750.0 900.0 DrawSplineSection
	6975.0 900.0 l  gs col-1 s gr
 gr

% Open spline
gs n 7650.0 450.0 m 7875.0 450.0 l
	7875.0 450.0 8100.0 450.0 8100.0 675.0 DrawSplineSection
	8100.0 675.0 8100.0 900.0 7875.0 900.0 DrawSplineSection
	7650.0 900.0 l  gs col-1 s gr
 gr

% Polyline
n 1125 450 m 1800 450 l gs col-1 s gr 
% Polyline
n 1125 900 m 1800 900 l gs col-1 s gr 
% Open spline
gs n 1125.0 450.0 m 900.0 450.0 l
	900.0 450.0 675.0 450.0 675.0 675.0 DrawSplineSection
	675.0 675.0 675.0 900.0 900.0 900.0 DrawSplineSection
	1125.0 900.0 l  gs col-1 s gr
 gr

% Open spline
gs n 1800.0 450.0 m 2025.0 450.0 l
	2025.0 450.0 2250.0 450.0 2250.0 675.0 DrawSplineSection
	2250.0 675.0 2250.0 900.0 2025.0 900.0 DrawSplineSection
	1800.0 900.0 l  gs col-1 s gr
 gr

% Ellipse
n 1485 8760 810 450 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7313 2504 1380 555 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7298 8328 1230 435 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 1485 7418 810 450 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 2925 3675 990 225 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 3015 4838 1215 270 0 360 DrawEllipse gs col-1 s gr

% Polyline
n 7302 4938 m 7302 4938 l gs col-1 s gr
% Polyline
n 10359 7697 m 10584 6887 l 9009 6887 l 8784 7697 l cp gs col-1 s gr 
% Polyline
n 7284 10563 m 7284 10563 l gs col-1 s gr
% Polyline
n 6068 8988 m 8543 8988 l 8543 9663 l 6068 9663 l cp gs col-1 s gr 
% Polyline
n 6053 9888 m 8528 9888 l 8528 10563 l 6053 10563 l cp gs col-1 s gr 
% Polyline
n 6285 7304 m 7283 6959 l 8310 7304 l 7298 7634 l cp gs col-1 s gr 
% Polyline
n 6053 6074 m 8528 6074 l 8528 6749 l 6053 6749 l cp gs col-1 s gr 
% Polyline
n 6068 5159 m 8543 5159 l 8543 5834 l 6068 5834 l cp gs col-1 s gr 
% Polyline
n 6285 4604 m 7283 4259 l 8310 4604 l 7298 4934 l cp gs col-1 s gr 
% Polyline
n 6053 3359 m 8528 3359 l 8528 4034 l 6053 4034 l cp gs col-1 s gr 
% Polyline
n 6285 1439 m 7283 1094 l 8310 1439 l 7298 1769 l cp gs col-1 s gr 
% Polyline
gs  clippath
1485 9513 m 1455 9633 l 1425 9513 l 1425 9675 l 1485 9675 l  cp clip
n 1455 9210 m 1455 9660 l gs col-1 s gr gr

% arrowhead
n 1485 9513 m 1455 9633 l 1425 9513 l 1455 9513 l 1485 9513 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7330 1803 m 7301 1923 l 7270 1804 l 7272 1965 l 7332 1964 l  cp clip
n 7299 1770 m 7302 1950 l gs col-1 s gr gr

% arrowhead
n 7330 1803 m 7301 1923 l 7270 1804 l 7300 1803 l 7330 1803 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7318 3212 m 7288 3332 l 7258 3212 l 7258 3374 l 7318 3374 l  cp clip
n 7288 3059 m 7288 3359 l gs col-1 s gr gr

% arrowhead
n 7318 3212 m 7288 3332 l 7258 3212 l 7288 3212 l 7318 3212 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7326 5008 m 7295 5128 l 7266 5008 l 7265 5170 l 7325 5170 l  cp clip
n 7296 4935 m 7295 5155 l gs col-1 s gr gr

% arrowhead
n 7326 5008 m 7295 5128 l 7266 5008 l 7296 5008 l 7326 5008 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7318 5923 m 7288 6043 l 7258 5923 l 7258 6085 l 7318 6085 l  cp clip
n 7288 5830 m 7288 6070 l gs col-1 s gr gr

% arrowhead
n 7318 5923 m 7288 6043 l 7258 5923 l 7288 5923 l 7318 5923 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7303 8833 m 7273 8953 l 7243 8833 l 7243 8995 l 7303 8995 l  cp clip
n 7273 8762 m 7273 8980 l gs col-1 s gr gr

% arrowhead
n 7303 8833 m 7273 8953 l 7243 8833 l 7273 8833 l 7303 8833 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7303 9744 m 7272 9864 l 7243 9744 l 7242 9906 l 7302 9906 l  cp clip
n 7273 9655 m 7272 9891 l gs col-1 s gr gr

% arrowhead
n 7303 9744 m 7272 9864 l 7243 9744 l 7273 9744 l 7303 9744 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7321 10881 m 7291 11001 l 7261 10881 l 7261 11043 l 7321 11043 l  cp clip
n 7291 10563 m 7291 11028 l gs col-1 s gr gr

% arrowhead
n 7321 10881 m 7291 11001 l 7261 10881 l 7291 10881 l 7321 10881 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
6139 1409 m 6259 1439 l 6139 1469 l 6301 1469 l 6301 1409 l  cp clip
n 7291 10796 m 5604 10796 l 5605 1440 l 6286 1439 l gs col-1 s gr gr

% arrowhead
n 6139 1409 m 6259 1439 l 6139 1469 l 6139 1439 l 6139 1409 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
6147 4574 m 6267 4604 l 6147 4634 l 6309 4634 l 6309 4574 l  cp clip
n 5830 10785 m 5830 4604 l 6294 4604 l gs col-1 s gr gr

% arrowhead
n 6147 4574 m 6267 4604 l 6147 4634 l 6147 4604 l 6147 4574 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7317 947 m 7287 1067 l 7257 947 l 7258 1109 l 7318 1109 l  cp clip
n 7288 1094 m 7287 909 l gs col-1 s gr gr

% arrowhead
n 7317 947 m 7287 1067 l 7257 947 l 7287 947 l 7317 947 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7316 4110 m 7284 4230 l 7256 4110 l 7254 4272 l 7314 4272 l  cp clip
n 7284 4257 m 7287 4038 l gs col-1 s gr gr

% arrowhead
n 7316 4110 m 7284 4230 l 7256 4110 l 7286 4110 l 7316 4110 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
 [100.0] 0 sd
gs  clippath
6370 636 m 6490 666 l 6370 696 l 6532 696 l 6532 636 l  cp clip
n 2288 8730 m 4958 8730 l 4950 666 l 6517 666 l gs col-1 s gr gr
 [] 0 sd
% arrowhead
n 6370 636 m 6490 666 l 6370 696 l 6370 666 l 6370 636 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
8748 7274 m 8868 7304 l 8748 7334 l 8910 7334 l 8910 7274 l  cp clip
n 8310 7304 m 8895 7304 l gs col-1 s gr gr

% arrowhead
n 8748 7274 m 8868 7304 l 8748 7334 l 8748 7304 l 8748 7274 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
 [100.0] 0 sd
gs  clippath
2442 8850 m 2322 8820 l 2442 8790 l 2280 8790 l 2280 8850 l  cp clip
n 2295 8820 m 4950 8820 l 4950 11250 l 6502 11250 l gs col-1 s gr gr
 [] 0 sd
% arrowhead
n 2442 8850 m 2322 8820 l 2442 8790 l 2442 8820 l 2442 8850 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7311 6810 m 7281 6930 l 7251 6810 l 7252 6972 l 7312 6972 l  cp clip
n 7282 6957 m 7281 6756 l gs col-1 s gr gr

% arrowhead
n 7311 6810 m 7281 6930 l 7251 6810 l 7281 6810 l 7311 6810 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7327 7749 m 7297 7869 l 7267 7749 l 7267 7911 l 7327 7911 l  cp clip
n 7297 7633 m 7297 7896 l gs col-1 s gr gr

% arrowhead
n 7327 7749 m 7297 7869 l 7267 7749 l 7297 7749 l 7327 7749 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 2700 1335 m 4275 1335 l 4275 2235 l 2700 2235 l cp gs col-1 s gr 
% Polyline
n 675 1830 m 1460 1155 l 2250 1830 l 1470 2505 l cp gs col-1 s gr 
% Polyline
n 675 2910 m 4275 2910 l 4275 5160 l 675 5160 l cp gs col-1 s gr 
% Polyline
n 675 5610 m 2250 5610 l 2250 6510 l 675 6510 l cp gs col-1 s gr 
% Polyline
n 787 4281 m 1572 3816 l 2362 4281 l 1582 4729 l cp gs col-1 s gr 
% Polyline
gs  clippath
2553 1800 m 2673 1830 l 2553 1860 l 2715 1860 l 2715 1800 l  cp clip
n 2250 1830 m 2700 1830 l gs col-1 s gr gr

% arrowhead
n 2553 1800 m 2673 1830 l 2553 1860 l 2553 1830 l 2553 1800 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1632 2730 m 1512 2700 l 1632 2670 l 1470 2670 l 1470 2730 l  cp clip
n 3465 2235 m 3465 2700 l 1485 2700 l gs col-1 s gr gr

% arrowhead
n 1632 2730 m 1512 2700 l 1632 2670 l 1632 2700 l 1632 2730 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1470 5463 m 1440 5583 l 1410 5463 l 1410 5625 l 1470 5625 l  cp clip
n 1440 5160 m 1440 5610 l gs col-1 s gr gr

% arrowhead
n 1470 5463 m 1440 5583 l 1410 5463 l 1440 5463 l 1470 5463 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1803 3644 m 1923 3674 l 1803 3704 l 1965 3705 l 1965 3645 l  cp clip
n 1572 3816 m 1572 3672 l 1950 3675 l gs col-1 s gr gr

% arrowhead
n 1803 3644 m 1923 3674 l 1803 3704 l 1803 3674 l 1803 3644 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1647 4816 m 1767 4847 l 1647 4876 l 1809 4878 l 1809 4818 l  cp clip
n 1581 4731 m 1581 4845 l 1794 4848 l gs col-1 s gr gr

% arrowhead
n 1647 4816 m 1767 4847 l 1647 4876 l 1647 4846 l 1647 4816 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1497 2763 m 1467 2883 l 1437 2763 l 1437 2925 l 1497 2925 l  cp clip
n 1467 2505 m 1467 2910 l gs col-1 s gr gr

% arrowhead
n 1497 2763 m 1467 2883 l 1437 2763 l 1467 2763 l 1497 2763 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1490 1008 m 1458 1128 l 1430 1008 l 1428 1170 l 1488 1170 l  cp clip
n 1458 1155 m 1461 900 l gs col-1 s gr gr

% arrowhead
n 1490 1008 m 1458 1128 l 1430 1008 l 1460 1008 l 1490 1008 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1490 8158 m 1460 8278 l 1430 8158 l 1430 8320 l 1490 8320 l  cp clip
n 1460 7870 m 1460 8305 l gs col-1 s gr gr

% arrowhead
n 1490 8158 m 1460 8278 l 1430 8158 l 1460 8158 l 1490 8158 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1485 6813 m 1455 6933 l 1425 6813 l 1425 6975 l 1485 6975 l  cp clip
n 1455 6510 m 1455 6960 l gs col-1 s gr gr

% arrowhead
n 1485 6813 m 1455 6933 l 1425 6813 l 1455 6813 l 1485 6813 l  cp gs 0.00 setgray ef gr  col-1 s
$F2psEnd
rs
$EOD
$!
$CREATE fig_main.ps
$DECK
%!PS-Adobe-2.0 EPSF-2.0
%%Title: fig_main.fig
%%Creator: fig2dev Version 3.1 Patchlevel 2
%%CreationDate: Tue Jan  6 10:00:28 1998
%%For: keith@metropolis (Keith Refson)
%Magnification: 0.69
%%Orientation: Portrait
%%BoundingBox: 0 0 366 451
%%Pages: 0
%%BeginSetup
%%IncludeFeature: *PageSize A4
%%EndSetup
%%EndComments
/$F2psDict 200 dict def
$F2psDict begin
$F2psDict /mtrx matrix put
/col-1 {0 setgray} bind def
/col0 {0.000 0.000 0.000 srgb} bind def
/col1 {0.000 0.000 1.000 srgb} bind def
/col2 {0.000 1.000 0.000 srgb} bind def
/col3 {0.000 1.000 1.000 srgb} bind def
/col4 {1.000 0.000 0.000 srgb} bind def
/col5 {1.000 0.000 1.000 srgb} bind def
/col6 {1.000 1.000 0.000 srgb} bind def
/col7 {1.000 1.000 1.000 srgb} bind def
/col8 {0.000 0.000 0.560 srgb} bind def
/col9 {0.000 0.000 0.690 srgb} bind def
/col10 {0.000 0.000 0.820 srgb} bind def
/col11 {0.530 0.810 1.000 srgb} bind def
/col12 {0.000 0.560 0.000 srgb} bind def
/col13 {0.000 0.690 0.000 srgb} bind def
/col14 {0.000 0.820 0.000 srgb} bind def
/col15 {0.000 0.560 0.560 srgb} bind def
/col16 {0.000 0.690 0.690 srgb} bind def
/col17 {0.000 0.820 0.820 srgb} bind def
/col18 {0.560 0.000 0.000 srgb} bind def
/col19 {0.690 0.000 0.000 srgb} bind def
/col20 {0.820 0.000 0.000 srgb} bind def
/col21 {0.560 0.000 0.560 srgb} bind def
/col22 {0.690 0.000 0.690 srgb} bind def
/col23 {0.820 0.000 0.820 srgb} bind def
/col24 {0.500 0.190 0.000 srgb} bind def
/col25 {0.630 0.250 0.000 srgb} bind def
/col26 {0.750 0.380 0.000 srgb} bind def
/col27 {1.000 0.500 0.500 srgb} bind def
/col28 {1.000 0.630 0.630 srgb} bind def
/col29 {1.000 0.750 0.750 srgb} bind def
/col30 {1.000 0.880 0.880 srgb} bind def
/col31 {1.000 0.840 0.000 srgb} bind def

end
save
-9.0 459.0 translate
1 -1 scale

/cp {closepath} bind def
/ef {eofill} bind def
/gr {grestore} bind def
/gs {gsave} bind def
/sa {save} bind def
/rs {restore} bind def
/l {lineto} bind def
/m {moveto} bind def
/rm {rmoveto} bind def
/n {newpath} bind def
/s {stroke} bind def
/sh {show} bind def
/slc {setlinecap} bind def
/slj {setlinejoin} bind def
/slw {setlinewidth} bind def
/srgb {setrgbcolor} bind def
/rot {rotate} bind def
/sc {scale} bind def
/sd {setdash} bind def
/ff {findfont} bind def
/sf {setfont} bind def
/scf {scalefont} bind def
/sw {stringwidth} bind def
/tr {translate} bind def
/tnt {dup dup currentrgbcolor
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
  bind def
/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
  4 -2 roll mul srgb} bind def
 /DrawEllipse {
	/endangle exch def
	/startangle exch def
	/yrad exch def
	/xrad exch def
	/y exch def
	/x exch def
	/savematrix mtrx currentmatrix def
	x y tr xrad yrad sc 0 0 1 startangle endangle arc
	closepath
	savematrix setmatrix
	} def

 /DrawSplineSection {
	/y3 exch def
	/x3 exch def
	/y2 exch def
	/x2 exch def
	/y1 exch def
	/x1 exch def
	/xa x1 x2 x1 sub 0.666667 mul add def
	/ya y1 y2 y1 sub 0.666667 mul add def
	/xb x3 x2 x3 sub 0.666667 mul add def
	/yb y3 y2 y3 sub 0.666667 mul add def
	x1 y1 lineto
	xa ya xb yb x3 y3 curveto
	} def

/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
/$F2psEnd {$F2psEnteredState restore end} def
%%EndProlog

$F2psBegin
10 setmiterlimit
n 0 842 m 0 0 l 595 0 l 595 842 l cp clip
 0.04157 0.04157 sc
7.500 slw
% Polyline
n 2340 224 m 3015 224 l gs col-1 s gr 
% Polyline
n 2340 674 m 3015 674 l gs col-1 s gr 
% Open spline
gs n 2340.0 224.0 m 2115.0 224.0 l
	2115.0 224.0 1890.0 224.0 1890.0 449.0 DrawSplineSection
	1890.0 449.0 1890.0 674.0 2115.0 674.0 DrawSplineSection
	2340.0 674.0 l  gs col-1 s gr
 gr

% Open spline
gs n 3015.0 224.0 m 3240.0 224.0 l
	3240.0 224.0 3465.0 224.0 3465.0 449.0 DrawSplineSection
	3465.0 449.0 3465.0 674.0 3240.0 674.0 DrawSplineSection
	3015.0 674.0 l  gs col-1 s gr
 gr

% Polyline
n 2385 10574 m 3060 10574 l gs col-1 s gr 
% Polyline
n 2385 11024 m 3060 11024 l gs col-1 s gr 
% Open spline
gs n 2385.0 10574.0 m 2160.0 10574.0 l
	2160.0 10574.0 1935.0 10574.0 1935.0 10799.0 DrawSplineSection
	1935.0 10799.0 1935.0 11024.0 2160.0 11024.0 DrawSplineSection
	2385.0 11024.0 l  gs col-1 s gr
 gr

% Open spline
gs n 3060.0 10574.0 m 3285.0 10574.0 l
	3285.0 10574.0 3510.0 10574.0 3510.0 10799.0 DrawSplineSection
	3510.0 10799.0 3510.0 11024.0 3285.0 11024.0 DrawSplineSection
	3060.0 11024.0 l  gs col-1 s gr
 gr

% Ellipse
n 1125 9899 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 5400 7559 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 5400 6659 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 2700 1214 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 2700 3014 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 2700 3914 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 4275 9899 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 5400 5737 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7052 2029 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 8087 2794 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 4937 2029 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 8107 3687 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 5400 4859 900 315 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 5400 8459 900 315 0 360 DrawEllipse gs col-1 s gr

% Polyline
n 3600 6659 m 2700 6299 l 1800 6659 l 2700 7019 l cp gs col-1 s gr 
% Polyline
n 3600 7559 m 2700 7199 l 1800 7559 l 2700 7919 l cp gs col-1 s gr 
% Polyline
n 3600 8460 m 2700 8100 l 1800 8460 l 2700 8820 l cp gs col-1 s gr 
% Polyline
n 3600 9360 m 2700 9000 l 1800 9360 l 2700 9720 l cp gs col-1 s gr 
% Polyline
gs  clippath
2730 752 m 2700 872 l 2670 752 l 2670 914 l 2730 914 l  cp clip
n 2700 674 m 2700 899 l gs col-1 s gr gr

% arrowhead
n 2730 752 m 2700 872 l 2670 752 l 2700 752 l 2730 752 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2730 2545 m 2700 2665 l 2670 2545 l 2670 2707 l 2730 2707 l  cp clip
n 2700 2503 m 2700 2692 l gs col-1 s gr gr

% arrowhead
n 2730 2545 m 2700 2665 l 2670 2545 l 2700 2545 l 2730 2545 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2730 3448 m 2700 3568 l 2670 3448 l 2670 3610 l 2730 3610 l  cp clip
n 2700 3329 m 2700 3595 l gs col-1 s gr gr

% arrowhead
n 2730 3448 m 2700 3568 l 2670 3448 l 2700 3448 l 2730 3448 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2730 4352 m 2700 4472 l 2670 4352 l 2670 4514 l 2730 4514 l  cp clip
n 2700 4229 m 2700 4499 l gs col-1 s gr gr

% arrowhead
n 2730 4352 m 2700 4472 l 2670 4352 l 2700 4352 l 2730 4352 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2730 6152 m 2700 6272 l 2670 6152 l 2670 6314 l 2730 6314 l  cp clip
n 2700 6119 m 2700 6299 l gs col-1 s gr gr

% arrowhead
n 2730 6152 m 2700 6272 l 2670 6152 l 2700 6152 l 2730 6152 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2730 7052 m 2700 7172 l 2670 7052 l 2670 7214 l 2730 7214 l  cp clip
n 2700 7019 m 2700 7199 l gs col-1 s gr gr

% arrowhead
n 2730 7052 m 2700 7172 l 2670 7052 l 2700 7052 l 2730 7052 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2730 7952 m 2700 8072 l 2670 7952 l 2670 8114 l 2730 8114 l  cp clip
n 2700 7919 m 2700 8099 l gs col-1 s gr gr

% arrowhead
n 2730 7952 m 2700 8072 l 2670 7952 l 2700 7952 l 2730 7952 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
4305 9437 m 4275 9557 l 4245 9437 l 4245 9599 l 4305 9599 l  cp clip
n 3599 9359 m 4275 9359 l 4275 9584 l gs col-1 s gr gr

% arrowhead
n 4305 9437 m 4275 9557 l 4245 9437 l 4275 9437 l 4305 9437 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1641 2119 m 1762 2146 l 1643 2179 l 1805 2176 l 1803 2116 l  cp clip
n 2700 8850 m 1575 8850 l 1575 2151 l 1789 2146 l gs col-1 s gr gr

% arrowhead
n 1641 2119 m 1762 2146 l 1643 2179 l 1642 2149 l 1641 2119 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
4353 5729 m 4473 5759 l 4353 5789 l 4515 5789 l 4515 5729 l  cp clip
n 3600 5759 m 4500 5759 l gs col-1 s gr gr

% arrowhead
n 4353 5729 m 4473 5759 l 4353 5789 l 4353 5759 l 4353 5729 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
4353 6629 m 4473 6659 l 4353 6689 l 4515 6689 l 4515 6629 l  cp clip
n 3600 6659 m 4500 6659 l gs col-1 s gr gr

% arrowhead
n 4353 6629 m 4473 6659 l 4353 6689 l 4353 6659 l 4353 6629 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
4353 7529 m 4473 7559 l 4353 7589 l 4515 7589 l 4515 7529 l  cp clip
n 3600 7559 m 4500 7559 l gs col-1 s gr gr

% arrowhead
n 4353 7529 m 4473 7559 l 4353 7589 l 4353 7559 l 4353 7529 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
4353 8429 m 4473 8459 l 4353 8489 l 4515 8489 l 4515 8429 l  cp clip
n 3600 8459 m 4500 8459 l gs col-1 s gr gr

% arrowhead
n 4353 8429 m 4473 8459 l 4353 8489 l 4353 8459 l 4353 8429 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2730 10433 m 2700 10553 l 2670 10433 l 2670 10595 l 2730 10595 l  cp clip
n 2700 10350 m 2700 10580 l gs col-1 s gr gr

% arrowhead
n 2730 10433 m 2700 10553 l 2670 10433 l 2700 10433 l 2730 10433 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2730 8853 m 2700 8973 l 2670 8853 l 2670 9015 l 2730 9015 l  cp clip
n 2700 8820 m 2700 9000 l gs col-1 s gr gr

% arrowhead
n 2730 8853 m 2700 8973 l 2670 8853 l 2700 8853 l 2730 8853 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 4275 10214 m 4275 10350 l 2700 10350 l gs col-1 s gr 
% Polyline
gs  clippath
2862 6262 m 2742 6232 l 2862 6202 l 2700 6202 l 2700 6262 l  cp clip
n 2715 6232 m 5400 6232 l 5400 6052 l gs col-1 s gr gr

% arrowhead
n 2862 6262 m 2742 6232 l 2862 6202 l 2862 6232 l 2862 6262 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2862 7162 m 2742 7132 l 2862 7102 l 2700 7102 l 2700 7162 l  cp clip
n 2715 7132 m 5400 7132 l 5400 6967 l gs col-1 s gr gr

% arrowhead
n 2862 7162 m 2742 7132 l 2862 7102 l 2862 7132 l 2862 7162 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2862 8047 m 2742 8017 l 2862 7987 l 2700 7987 l 2700 8047 l  cp clip
n 2715 8017 m 5415 8017 l 5415 7882 l gs col-1 s gr gr

% arrowhead
n 2862 8047 m 2742 8017 l 2862 7987 l 2862 8017 l 2862 8047 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2847 8880 m 2727 8850 l 2847 8820 l 2685 8820 l 2685 8880 l  cp clip
n 2700 8850 m 5400 8850 l 5400 8782 l gs col-1 s gr gr

% arrowhead
n 2847 8880 m 2727 8850 l 2847 8820 l 2847 8850 l 2847 8880 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1155 9437 m 1125 9557 l 1095 9437 l 1095 9599 l 1155 9599 l  cp clip
n 1125 9584 m 1125 9359 l 1800 9359 l gs col-1 s gr gr

% arrowhead
n 1155 9437 m 1125 9557 l 1095 9437 l 1125 9437 l 1155 9437 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
 [100.0] 0 sd
gs  clippath
3951 2144 m 4027 2046 l 4006 2169 l 4072 2020 l 4017 1996 l  cp clip
3687 2889 m 3610 2986 l 3632 2864 l 3566 3013 l 3621 3037 l  cp clip
n 3600 3011 m 4038 2022 l gs col-1 s gr gr
 [] 0 sd
% arrowhead
n 3687 2889 m 3610 2986 l 3632 2864 l 3660 2877 l 3687 2889 l  cp gs 0.00 setgray ef gr  col-1 s
% arrowhead
n 3951 2144 m 4027 2046 l 4006 2169 l 3978 2156 l 3951 2144 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
 [100.0] 0 sd
gs  clippath
7640 2365 m 7707 2468 l 7599 2409 l 7718 2519 l 7758 2475 l  cp clip
n 7517 2292 m 7727 2487 l gs col-1 s gr gr
 [] 0 sd
% arrowhead
n 7640 2365 m 7707 2468 l 7599 2409 l 7619 2387 l 7640 2365 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7060 3657 m 7180 3687 l 7060 3717 l 7222 3717 l 7222 3657 l  cp clip
n 6307 3687 m 7207 3687 l gs col-1 s gr gr

% arrowhead
n 7060 3657 m 7180 3687 l 7060 3717 l 7060 3687 l 7060 3657 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 3600 4859 m 2700 4499 l 1800 4859 l 2700 5219 l cp gs col-1 s gr 
% Polyline
gs  clippath
4353 4829 m 4473 4859 l 4353 4889 l 4515 4889 l 4515 4829 l  cp clip
n 3600 4859 m 4500 4859 l gs col-1 s gr gr

% arrowhead
n 4353 4829 m 4473 4859 l 4353 4889 l 4353 4859 l 4353 4829 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2847 5347 m 2727 5317 l 2847 5287 l 2685 5287 l 2685 5347 l  cp clip
n 2700 5317 m 5400 5317 l 5400 5182 l gs col-1 s gr gr

% arrowhead
n 2847 5347 m 2727 5317 l 2847 5287 l 2847 5317 l 2847 5347 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 3600 2144 m 2700 1784 l 1800 2144 l 2700 2504 l cp gs col-1 s gr 
% Polyline
gs  clippath
2730 1637 m 2700 1757 l 2670 1637 l 2670 1799 l 2730 1799 l  cp clip
n 2700 1529 m 2700 1784 l gs col-1 s gr gr

% arrowhead
n 2730 1637 m 2700 1757 l 2670 1637 l 2700 1637 l 2730 1637 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 3605 5760 m 2705 5400 l 1805 5760 l 2705 6120 l cp gs col-1 s gr 
% Polyline
gs  clippath
2730 5253 m 2700 5373 l 2670 5253 l 2670 5415 l 2730 5415 l  cp clip
n 2700 5219 m 2700 5400 l gs col-1 s gr gr

% arrowhead
n 2730 5253 m 2700 5373 l 2670 5253 l 2700 5253 l 2730 5253 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 1125 10214 m 1125 10350 l 2700 10350 l gs col-1 s gr 
% Polyline
 [100.0] 0 sd
gs  clippath
4406 3576 m 4485 3671 l 4371 3625 l 4501 3720 l 4537 3672 l  cp clip
3701 3137 m 3621 3041 l 3736 3088 l 3606 2993 l 3570 3041 l  cp clip
n 3600 3026 m 4507 3687 l gs col-1 s gr gr
 [] 0 sd
% arrowhead
n 3701 3137 m 3621 3041 l 3736 3088 l 3719 3113 l 3701 3137 l  cp gs 0.00 setgray ef gr  col-1 s
% arrowhead
n 4406 3576 m 4485 3671 l 4371 3625 l 4388 3600 l 4406 3576 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 6307 3687 m 5407 3327 l 4507 3687 l 5407 4047 l cp gs col-1 s gr 
% Polyline
 [100.0] 0 sd
gs  clippath
6005 2000 m 6125 2030 l 6005 2060 l 6167 2060 l 6167 2000 l  cp clip
n 5841 2030 m 6152 2030 l gs col-1 s gr gr
 [] 0 sd
% arrowhead
n 6005 2000 m 6125 2030 l 6005 2060 l 6005 2030 l 6005 2000 l  cp gs 0.00 setgray ef gr  col-1 s
$F2psEnd
rs
$EOD
$!
$CREATE fig_skewstart.ps
$DECK
%!PS-Adobe-2.0 EPSF-2.0
%%Title: fig_skewstart.fig
%%Creator: fig2dev Version 3.1 Patchlevel 2
%%CreationDate: Tue Jan  6 10:10:35 1998
%%For: keith@metropolis (Keith Refson)
%Magnification: 0.69
%%Orientation: Portrait
%%BoundingBox: 0 0 448 231
%%Pages: 0
%%BeginSetup
%%IncludeFeature: *PageSize A4
%%EndSetup
%%EndComments
/$F2psDict 200 dict def
$F2psDict begin
$F2psDict /mtrx matrix put
/col-1 {0 setgray} bind def
/col0 {0.000 0.000 0.000 srgb} bind def
/col1 {0.000 0.000 1.000 srgb} bind def
/col2 {0.000 1.000 0.000 srgb} bind def
/col3 {0.000 1.000 1.000 srgb} bind def
/col4 {1.000 0.000 0.000 srgb} bind def
/col5 {1.000 0.000 1.000 srgb} bind def
/col6 {1.000 1.000 0.000 srgb} bind def
/col7 {1.000 1.000 1.000 srgb} bind def
/col8 {0.000 0.000 0.560 srgb} bind def
/col9 {0.000 0.000 0.690 srgb} bind def
/col10 {0.000 0.000 0.820 srgb} bind def
/col11 {0.530 0.810 1.000 srgb} bind def
/col12 {0.000 0.560 0.000 srgb} bind def
/col13 {0.000 0.690 0.000 srgb} bind def
/col14 {0.000 0.820 0.000 srgb} bind def
/col15 {0.000 0.560 0.560 srgb} bind def
/col16 {0.000 0.690 0.690 srgb} bind def
/col17 {0.000 0.820 0.820 srgb} bind def
/col18 {0.560 0.000 0.000 srgb} bind def
/col19 {0.690 0.000 0.000 srgb} bind def
/col20 {0.820 0.000 0.000 srgb} bind def
/col21 {0.560 0.000 0.560 srgb} bind def
/col22 {0.690 0.000 0.690 srgb} bind def
/col23 {0.820 0.000 0.820 srgb} bind def
/col24 {0.500 0.190 0.000 srgb} bind def
/col25 {0.630 0.250 0.000 srgb} bind def
/col26 {0.750 0.380 0.000 srgb} bind def
/col27 {1.000 0.500 0.500 srgb} bind def
/col28 {1.000 0.630 0.630 srgb} bind def
/col29 {1.000 0.750 0.750 srgb} bind def
/col30 {1.000 0.880 0.880 srgb} bind def
/col31 {1.000 0.840 0.000 srgb} bind def

end
save
-2.0 232.0 translate
1 -1 scale

/cp {closepath} bind def
/ef {eofill} bind def
/gr {grestore} bind def
/gs {gsave} bind def
/sa {save} bind def
/rs {restore} bind def
/l {lineto} bind def
/m {moveto} bind def
/rm {rmoveto} bind def
/n {newpath} bind def
/s {stroke} bind def
/sh {show} bind def
/slc {setlinecap} bind def
/slj {setlinejoin} bind def
/slw {setlinewidth} bind def
/srgb {setrgbcolor} bind def
/rot {rotate} bind def
/sc {scale} bind def
/sd {setdash} bind def
/ff {findfont} bind def
/sf {setfont} bind def
/scf {scalefont} bind def
/sw {stringwidth} bind def
/tr {translate} bind def
/tnt {dup dup currentrgbcolor
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
  bind def
/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
  4 -2 roll mul srgb} bind def
 /DrawEllipse {
	/endangle exch def
	/startangle exch def
	/yrad exch def
	/xrad exch def
	/y exch def
	/x exch def
	/savematrix mtrx currentmatrix def
	x y tr xrad yrad sc 0 0 1 startangle endangle arc
	closepath
	savematrix setmatrix
	} def

/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
/$F2psEnd {$F2psEnteredState restore end} def
%%EndProlog

$F2psBegin
10 setmiterlimit
n 0 842 m 0 0 l 595 0 l 595 842 l cp clip
 0.04157 0.04157 sc
7.500 slw
% Ellipse
n 4373 1366 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4525 1366 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 1017 2063 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 1170 2063 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 1867 1888 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 2041 1888 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5222 1235 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5375 1235 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5658 1061 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5658 1235 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6072 1061 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6246 1061 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6573 865 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6573 1039 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6878 865 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 7031 865 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 7728 690 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 7880 690 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 8578 516 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 8730 516 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 9406 342 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 9558 342 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 146 2259 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 298 2259 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 647 2085 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 647 2259 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 1475 1932 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 1475 2085 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 2346 1736 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 2346 1910 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 2695 1736 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 2847 1736 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 3152 1583 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 3152 1736 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 3523 1562 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 3675 1562 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 3959 1409 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 3959 1562 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 7358 690 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 7358 865 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 8185 538 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 8185 690 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 9035 364 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 9035 516 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 9863 189 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 9863 342 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 10277 189 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 10430 189 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4373 3196 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4525 3196 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4830 3043 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4830 3196 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5222 3043 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5375 3043 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5658 2891 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5658 3043 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6072 2869 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6246 2869 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6203 3196 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6203 3370 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5723 3370 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5876 3370 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5288 3392 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5288 3544 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4852 3544 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5004 3544 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4460 4372 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4460 4546 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4852 4372 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5004 4372 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5288 4219 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5288 4372 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5723 4198 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5876 4198 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6203 4023 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6203 4198 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4830 4742 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4830 4895 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5222 4742 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5375 4742 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5658 4568 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5658 4742 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6072 4568 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6246 4568 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4373 4895 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4525 4895 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4460 3544 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4460 3718 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4373 4045 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4525 4045 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4830 3893 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4830 4045 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5222 3893 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5375 3893 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6072 3718 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 6246 3718 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5658 3740 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 5658 3893 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4830 1235 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Ellipse
n 4830 1387 87 87 0 360 DrawEllipse gs 0.00 setgray ef gr gs col-1 s gr

% Polyline
n 2346 80 m 2346 2259 l gs col-1 s gr 
% Polyline
n 4460 80 m 4460 2259 l gs col-1 s gr 
% Polyline
n 6573 80 m 6573 2259 l gs col-1 s gr 
% Polyline
n 8687 80 m 8687 2259 l gs col-1 s gr 
% Polyline
n 4460 4459 m 4460 4459 l gs col-1 s gr
% Polyline
 [15 25.0] 25.0 sd
n 4460 4895 m 6573 4459 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 25.0] 25.0 sd
n 4460 4459 m 6573 4045 l gs col-1 s gr  [] 0 sd
% Polyline
 [15 25.0] 25.0 sd
n 4460 4045 m 6573 3631 l gs col-1 s gr  [] 0 sd
% Polyline
n 6573 3196 m 7292 3043 l gs col-1 s gr 
% Polyline
n 6573 3631 m 7379 3457 l gs col-1 s gr 
% Polyline
n 4830 4808 m 4939 5548 l gs col-1 s gr 
% Polyline
 [15 25.0] 25.0 sd
n 4460 3631 m 6573 3196 l gs 0.00 setgray ef gr gs col-1 s gr  [] 0 sd
% Polyline
n 10800 2259 m 10800 80 l 233 80 l 233 2259 l cp gs col-1 s gr 
% Polyline
 [15 25.0] 25.0 sd
n 233 2259 m 10800 59 l gs col-1 s gr  [] 0 sd
% Polyline
n 5309 4699 m 5418 5439 l gs col-1 s gr 
% Polyline
n 5506 5287 m 5397 5221 l 5462 5091 l gs col-1 s gr 
% Polyline
n 4765 5221 m 4895 5309 l 4808 5461 l gs col-1 s gr 
% Polyline
n 7009 2978 m 6922 3108 l 6813 3043 l gs col-1 s gr 
% Polyline
n 6900 3675 m 6987 3544 l 7118 3631 l gs col-1 s gr 
% Polyline
 [15 25.0] 25.0 sd
n 4460 3196 m 6573 2782 l gs 0.00 setgray ef gr gs col-1 s gr  [] 0 sd
% Polyline
n 6573 4895 m 6573 2782 l 4460 2782 l 4460 4895 l cp gs col-1 s gr 
/Times-Roman ff 345.00 scf sf
5070 5309 m
gs 1 -1 sc (a) col-1 sh gr
/Times-Roman ff 345.00 scf sf
6900 3392 m
gs 1 -1 sc (d) col-1 sh gr
$F2psEnd
rs
$EOD
$!
$CREATE fig_startup-a.ps
$DECK
%!PS-Adobe-2.0 EPSF-2.0
%%Title: fig_startup-a.fig
%%Creator: fig2dev Version 3.1 Patchlevel 2
%%CreationDate: Tue Jan  6 10:00:29 1998
%%For: keith@metropolis (Keith Refson)
%Magnification: 0.69
%%Orientation: Portrait
%%BoundingBox: 0 0 254 433
%%Pages: 0
%%BeginSetup
%%IncludeFeature: *PageSize A4
%%EndSetup
%%EndComments
/$F2psDict 200 dict def
$F2psDict begin
$F2psDict /mtrx matrix put
/col-1 {0 setgray} bind def
/col0 {0.000 0.000 0.000 srgb} bind def
/col1 {0.000 0.000 1.000 srgb} bind def
/col2 {0.000 1.000 0.000 srgb} bind def
/col3 {0.000 1.000 1.000 srgb} bind def
/col4 {1.000 0.000 0.000 srgb} bind def
/col5 {1.000 0.000 1.000 srgb} bind def
/col6 {1.000 1.000 0.000 srgb} bind def
/col7 {1.000 1.000 1.000 srgb} bind def
/col8 {0.000 0.000 0.560 srgb} bind def
/col9 {0.000 0.000 0.690 srgb} bind def
/col10 {0.000 0.000 0.820 srgb} bind def
/col11 {0.530 0.810 1.000 srgb} bind def
/col12 {0.000 0.560 0.000 srgb} bind def
/col13 {0.000 0.690 0.000 srgb} bind def
/col14 {0.000 0.820 0.000 srgb} bind def
/col15 {0.000 0.560 0.560 srgb} bind def
/col16 {0.000 0.690 0.690 srgb} bind def
/col17 {0.000 0.820 0.820 srgb} bind def
/col18 {0.560 0.000 0.000 srgb} bind def
/col19 {0.690 0.000 0.000 srgb} bind def
/col20 {0.820 0.000 0.000 srgb} bind def
/col21 {0.560 0.000 0.560 srgb} bind def
/col22 {0.690 0.000 0.690 srgb} bind def
/col23 {0.820 0.000 0.820 srgb} bind def
/col24 {0.500 0.190 0.000 srgb} bind def
/col25 {0.630 0.250 0.000 srgb} bind def
/col26 {0.750 0.380 0.000 srgb} bind def
/col27 {1.000 0.500 0.500 srgb} bind def
/col28 {1.000 0.630 0.630 srgb} bind def
/col29 {1.000 0.750 0.750 srgb} bind def
/col30 {1.000 0.880 0.880 srgb} bind def
/col31 {1.000 0.840 0.000 srgb} bind def

end
save
-18.0 441.0 translate
1 -1 scale

/cp {closepath} bind def
/ef {eofill} bind def
/gr {grestore} bind def
/gs {gsave} bind def
/sa {save} bind def
/rs {restore} bind def
/l {lineto} bind def
/m {moveto} bind def
/rm {rmoveto} bind def
/n {newpath} bind def
/s {stroke} bind def
/sh {show} bind def
/slc {setlinecap} bind def
/slj {setlinejoin} bind def
/slw {setlinewidth} bind def
/srgb {setrgbcolor} bind def
/rot {rotate} bind def
/sc {scale} bind def
/sd {setdash} bind def
/ff {findfont} bind def
/sf {setfont} bind def
/scf {scalefont} bind def
/sw {stringwidth} bind def
/tr {translate} bind def
/tnt {dup dup currentrgbcolor
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
  bind def
/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
  4 -2 roll mul srgb} bind def
 /DrawEllipse {
	/endangle exch def
	/startangle exch def
	/yrad exch def
	/xrad exch def
	/y exch def
	/x exch def
	/savematrix mtrx currentmatrix def
	x y tr xrad yrad sc 0 0 1 startangle endangle arc
	closepath
	savematrix setmatrix
	} def

 /DrawSplineSection {
	/y3 exch def
	/x3 exch def
	/y2 exch def
	/x2 exch def
	/y1 exch def
	/x1 exch def
	/xa x1 x2 x1 sub 0.666667 mul add def
	/ya y1 y2 y1 sub 0.666667 mul add def
	/xb x3 x2 x3 sub 0.666667 mul add def
	/yb y3 y2 y3 sub 0.666667 mul add def
	x1 y1 lineto
	xa ya xb yb x3 y3 curveto
	} def

/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
/$F2psEnd {$F2psEnteredState restore end} def
%%EndProlog

$F2psBegin
10 setmiterlimit
n 0 842 m 0 0 l 595 0 l 595 842 l cp clip
 0.04157 0.04157 sc
7.500 slw
% Polyline
n 1010 225 m 1685 225 l gs col-1 s gr 
% Polyline
n 1010 675 m 1685 675 l gs col-1 s gr 
% Open spline
gs n 1010.0 225.0 m 785.0 225.0 l
	785.0 225.0 560.0 225.0 560.0 450.0 DrawSplineSection
	560.0 450.0 560.0 675.0 785.0 675.0 DrawSplineSection
	1010.0 675.0 l  gs col-1 s gr
 gr

% Open spline
gs n 1685.0 225.0 m 1910.0 225.0 l
	1910.0 225.0 2135.0 225.0 2135.0 450.0 DrawSplineSection
	2135.0 450.0 2135.0 675.0 1910.0 675.0 DrawSplineSection
	1685.0 675.0 l  gs col-1 s gr
 gr

% Polyline
n 3155 5740 m 3830 5740 l gs col-1 s gr 
% Polyline
n 3155 6190 m 3830 6190 l gs col-1 s gr 
% Open spline
gs n 3155.0 5740.0 m 2930.0 5740.0 l
	2930.0 5740.0 2705.0 5740.0 2705.0 5965.0 DrawSplineSection
	2705.0 5965.0 2705.0 6190.0 2930.0 6190.0 DrawSplineSection
	3155.0 6190.0 l  gs col-1 s gr
 gr

% Open spline
gs n 3830.0 5740.0 m 4055.0 5740.0 l
	4055.0 5740.0 4280.0 5740.0 4280.0 5965.0 DrawSplineSection
	4280.0 5965.0 4280.0 6190.0 4055.0 6190.0 DrawSplineSection
	3830.0 6190.0 l  gs col-1 s gr
 gr

% Ellipse
n 3826 2139 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 3831 3038 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 3831 3938 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 3831 4843 900 338 0 360 DrawEllipse gs col-1 s gr

% Polyline
gs  clippath
3861 2553 m 3831 2673 l 3801 2553 l 3801 2715 l 3861 2715 l  cp clip
n 3831 2474 m 3831 2700 l gs col-1 s gr gr

% arrowhead
n 3861 2553 m 3831 2673 l 3801 2553 l 3831 2553 l 3861 2553 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
3861 3453 m 3831 3573 l 3801 3453 l 3801 3615 l 3861 3615 l  cp clip
n 3831 3374 m 3831 3600 l gs col-1 s gr gr

% arrowhead
n 3861 3453 m 3831 3573 l 3801 3453 l 3831 3453 l 3861 3453 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
3861 4353 m 3831 4473 l 3801 4353 l 3801 4515 l 3861 4515 l  cp clip
n 3831 4274 m 3831 4500 l gs col-1 s gr gr

% arrowhead
n 3861 4353 m 3831 4473 l 3801 4353 l 3831 4353 l 3861 4353 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 5310 10125 m 5985 10125 l gs col-1 s gr 
% Polyline
n 5310 10575 m 5985 10575 l gs col-1 s gr 
% Open spline
gs n 5310.0 10125.0 m 5085.0 10125.0 l
	5085.0 10125.0 4860.0 10125.0 4860.0 10350.0 DrawSplineSection
	4860.0 10350.0 4860.0 10575.0 5085.0 10575.0 DrawSplineSection
	5310.0 10575.0 l  gs col-1 s gr
 gr

% Open spline
gs n 5985.0 10125.0 m 6210.0 10125.0 l
	6210.0 10125.0 6435.0 10125.0 6435.0 10350.0 DrawSplineSection
	6435.0 10350.0 6435.0 10575.0 6210.0 10575.0 DrawSplineSection
	5985.0 10575.0 l  gs col-1 s gr
 gr

% Ellipse
n 1350 2590 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 3825 9110 335 335 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 1350 10215 335 335 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 3825 7990 335 335 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 1350 3715 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 1345 1465 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 5625 8215 335 335 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 5625 9340 900 338 0 360 DrawEllipse gs col-1 s gr

% Polyline
gs  clippath
1380 978 m 1350 1098 l 1320 978 l 1320 1140 l 1380 1140 l  cp clip
n 1350 675 m 1350 1125 l gs col-1 s gr gr

% arrowhead
n 1380 978 m 1350 1098 l 1320 978 l 1350 978 l 1380 978 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1380 2103 m 1350 2223 l 1320 2103 l 1320 2265 l 1380 2265 l  cp clip
n 1350 1800 m 1350 2250 l gs col-1 s gr gr

% arrowhead
n 1380 2103 m 1350 2223 l 1320 2103 l 1350 2103 l 1380 2103 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1380 3228 m 1350 3348 l 1320 3228 l 1320 3390 l 1380 3390 l  cp clip
n 1350 2925 m 1350 3375 l gs col-1 s gr gr

% arrowhead
n 1380 3228 m 1350 3348 l 1320 3228 l 1350 3228 l 1380 3228 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 2250 7980 m 1350 7620 l 450 7980 l 1350 8340 l cp gs col-1 s gr 
% Polyline
n 2250 9110 m 1350 8750 l 450 9110 l 1350 9470 l cp gs col-1 s gr 
% Polyline
gs  clippath
1385 7473 m 1355 7593 l 1325 7473 l 1325 7635 l 1385 7635 l  cp clip
n 1355 7205 m 1355 7620 l gs col-1 s gr gr

% arrowhead
n 1385 7473 m 1355 7593 l 1325 7473 l 1355 7473 l 1385 7473 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1380 8613 m 1350 8733 l 1320 8613 l 1320 8775 l 1380 8775 l  cp clip
n 1350 8340 m 1350 8760 l gs col-1 s gr gr

% arrowhead
n 1380 8613 m 1350 8733 l 1320 8613 l 1350 8613 l 1380 8613 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1380 9723 m 1350 9843 l 1320 9723 l 1320 9885 l 1380 9885 l  cp clip
n 1350 9470 m 1350 9870 l gs col-1 s gr gr

% arrowhead
n 1380 9723 m 1350 9843 l 1320 9723 l 1350 9723 l 1380 9723 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
3855 1653 m 3825 1773 l 3795 1653 l 3795 1815 l 3855 1815 l  cp clip
n 2260 4845 m 2700 4845 l 2700 1575 l 3825 1575 l 3825 1800 l gs col-1 s gr gr

% arrowhead
n 3855 1653 m 3825 1773 l 3795 1653 l 3825 1653 l 3855 1653 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2558 5935 m 2678 5965 l 2558 5995 l 2720 5995 l 2720 5935 l  cp clip
n 2250 5965 m 2705 5965 l gs col-1 s gr gr

% arrowhead
n 2558 5935 m 2678 5965 l 2558 5995 l 2558 5965 l 2558 5935 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
3343 7955 m 3463 7985 l 3343 8015 l 3505 8015 l 3505 7955 l  cp clip
n 2245 7985 m 3490 7985 l gs col-1 s gr gr

% arrowhead
n 3343 7955 m 3463 7985 l 3343 8015 l 3343 7985 l 3343 7955 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
3343 9085 m 3463 9115 l 3343 9145 l 3505 9145 l 3505 9085 l  cp clip
n 2255 9115 m 3490 9115 l gs col-1 s gr gr

% arrowhead
n 3343 9085 m 3463 9115 l 3343 9145 l 3343 9115 l 3343 9085 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 2255 4845 m 1355 4485 l 455 4845 l 1355 5205 l cp gs col-1 s gr 
% Polyline
gs  clippath
1385 4338 m 1355 4458 l 1325 4338 l 1325 4500 l 1385 4500 l  cp clip
n 1355 4050 m 1355 4485 l gs col-1 s gr gr

% arrowhead
n 1385 4338 m 1355 4458 l 1325 4338 l 1355 4338 l 1385 4338 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 2250 5960 m 1350 5600 l 450 5960 l 1350 6320 l cp gs col-1 s gr 
% Polyline
gs  clippath
1380 5453 m 1350 5573 l 1320 5453 l 1320 5615 l 1380 5615 l  cp clip
n 1350 5205 m 1350 5600 l gs col-1 s gr gr

% arrowhead
n 1380 5453 m 1350 5573 l 1320 5453 l 1350 5453 l 1380 5453 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1497 5430 m 1377 5400 l 1497 5370 l 1335 5370 l 1335 5430 l  cp clip
n 3825 5170 m 3825 5400 l 1350 5400 l gs col-1 s gr gr

% arrowhead
n 1497 5430 m 1377 5400 l 1497 5370 l 1497 5400 l 1497 5430 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 560 6750 m 2135 6750 l 2135 7200 l 560 7200 l cp gs col-1 s gr 
% Polyline
gs  clippath
1380 6603 m 1350 6723 l 1320 6603 l 1320 6765 l 1380 6765 l  cp clip
n 1350 6325 m 1350 6750 l gs col-1 s gr gr

% arrowhead
n 1380 6603 m 1350 6723 l 1320 6603 l 1350 6603 l 1380 6603 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
5655 8858 m 5625 8978 l 5595 8858 l 5595 9020 l 5655 9020 l  cp clip
n 5625 8550 m 5625 9005 l gs col-1 s gr gr

% arrowhead
n 5655 8858 m 5625 8978 l 5595 8858 l 5625 8858 l 5655 8858 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
5655 9978 m 5625 10098 l 5595 9978 l 5595 10140 l 5655 10140 l  cp clip
n 5625 9675 m 5625 10125 l gs col-1 s gr gr

% arrowhead
n 5655 9978 m 5625 10098 l 5595 9978 l 5625 9978 l 5655 9978 l  cp gs 0.00 setgray ef gr  col-1 s
$F2psEnd
rs
$EOD
$!
$CREATE fig_startup-b.ps
$DECK
%!PS-Adobe-2.0 EPSF-2.0
%%Title: fig_startup-b.fig
%%Creator: fig2dev Version 3.1 Patchlevel 2
%%CreationDate: Tue Jan  6 10:00:30 1998
%%For: keith@metropolis (Keith Refson)
%Magnification: 0.69
%%Orientation: Portrait
%%BoundingBox: 0 0 442 451
%%Pages: 0
%%BeginSetup
%%IncludeFeature: *PageSize A4
%%EndSetup
%%EndComments
/$F2psDict 200 dict def
$F2psDict begin
$F2psDict /mtrx matrix put
/col-1 {0 setgray} bind def
/col0 {0.000 0.000 0.000 srgb} bind def
/col1 {0.000 0.000 1.000 srgb} bind def
/col2 {0.000 1.000 0.000 srgb} bind def
/col3 {0.000 1.000 1.000 srgb} bind def
/col4 {1.000 0.000 0.000 srgb} bind def
/col5 {1.000 0.000 1.000 srgb} bind def
/col6 {1.000 1.000 0.000 srgb} bind def
/col7 {1.000 1.000 1.000 srgb} bind def
/col8 {0.000 0.000 0.560 srgb} bind def
/col9 {0.000 0.000 0.690 srgb} bind def
/col10 {0.000 0.000 0.820 srgb} bind def
/col11 {0.530 0.810 1.000 srgb} bind def
/col12 {0.000 0.560 0.000 srgb} bind def
/col13 {0.000 0.690 0.000 srgb} bind def
/col14 {0.000 0.820 0.000 srgb} bind def
/col15 {0.000 0.560 0.560 srgb} bind def
/col16 {0.000 0.690 0.690 srgb} bind def
/col17 {0.000 0.820 0.820 srgb} bind def
/col18 {0.560 0.000 0.000 srgb} bind def
/col19 {0.690 0.000 0.000 srgb} bind def
/col20 {0.820 0.000 0.000 srgb} bind def
/col21 {0.560 0.000 0.560 srgb} bind def
/col22 {0.690 0.000 0.690 srgb} bind def
/col23 {0.820 0.000 0.820 srgb} bind def
/col24 {0.500 0.190 0.000 srgb} bind def
/col25 {0.630 0.250 0.000 srgb} bind def
/col26 {0.750 0.380 0.000 srgb} bind def
/col27 {1.000 0.500 0.500 srgb} bind def
/col28 {1.000 0.630 0.630 srgb} bind def
/col29 {1.000 0.750 0.750 srgb} bind def
/col30 {1.000 0.880 0.880 srgb} bind def
/col31 {1.000 0.840 0.000 srgb} bind def

end
save
-18.0 460.0 translate
1 -1 scale

/cp {closepath} bind def
/ef {eofill} bind def
/gr {grestore} bind def
/gs {gsave} bind def
/sa {save} bind def
/rs {restore} bind def
/l {lineto} bind def
/m {moveto} bind def
/rm {rmoveto} bind def
/n {newpath} bind def
/s {stroke} bind def
/sh {show} bind def
/slc {setlinecap} bind def
/slj {setlinejoin} bind def
/slw {setlinewidth} bind def
/srgb {setrgbcolor} bind def
/rot {rotate} bind def
/sc {scale} bind def
/sd {setdash} bind def
/ff {findfont} bind def
/sf {setfont} bind def
/scf {scalefont} bind def
/sw {stringwidth} bind def
/tr {translate} bind def
/tnt {dup dup currentrgbcolor
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add
  4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
  bind def
/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
  4 -2 roll mul srgb} bind def
 /DrawEllipse {
	/endangle exch def
	/startangle exch def
	/yrad exch def
	/xrad exch def
	/y exch def
	/x exch def
	/savematrix mtrx currentmatrix def
	x y tr xrad yrad sc 0 0 1 startangle endangle arc
	closepath
	savematrix setmatrix
	} def

/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
/$F2psEnd {$F2psEnteredState restore end} def
%%EndProlog

$F2psBegin
10 setmiterlimit
n 0 842 m 0 0 l 595 0 l 595 842 l cp clip
 0.04157 0.04157 sc
7.500 slw
% Ellipse
n 2476 7089 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 2481 7988 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 2481 8888 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 2481 9793 900 338 0 360 DrawEllipse gs col-1 s gr

% Polyline
gs  clippath
2511 7503 m 2481 7623 l 2451 7503 l 2451 7665 l 2511 7665 l  cp clip
n 2481 7424 m 2481 7650 l gs col-1 s gr gr

% arrowhead
n 2511 7503 m 2481 7623 l 2451 7503 l 2481 7503 l 2511 7503 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2511 8403 m 2481 8523 l 2451 8403 l 2451 8565 l 2511 8565 l  cp clip
n 2481 8324 m 2481 8550 l gs col-1 s gr gr

% arrowhead
n 2511 8403 m 2481 8523 l 2451 8403 l 2481 8403 l 2511 8403 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2511 9303 m 2481 9423 l 2451 9303 l 2451 9465 l 2511 9465 l  cp clip
n 2481 9224 m 2481 9450 l gs col-1 s gr gr

% arrowhead
n 2511 9303 m 2481 9423 l 2451 9303 l 2481 9303 l 2511 9303 l  cp gs 0.00 setgray ef gr  col-1 s
% Ellipse
n 2476 1464 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 2481 2363 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 2481 3263 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 2481 4168 900 338 0 360 DrawEllipse gs col-1 s gr

% Polyline
gs  clippath
2511 1878 m 2481 1998 l 2451 1878 l 2451 2040 l 2511 2040 l  cp clip
n 2481 1799 m 2481 2025 l gs col-1 s gr gr

% arrowhead
n 2511 1878 m 2481 1998 l 2451 1878 l 2481 1878 l 2511 1878 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2511 2778 m 2481 2898 l 2451 2778 l 2451 2940 l 2511 2940 l  cp clip
n 2481 2699 m 2481 2925 l gs col-1 s gr gr

% arrowhead
n 2511 2778 m 2481 2898 l 2451 2778 l 2481 2778 l 2511 2778 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2511 3678 m 2481 3798 l 2451 3678 l 2451 3840 l 2511 3840 l  cp clip
n 2481 3599 m 2481 3825 l gs col-1 s gr gr

% arrowhead
n 2511 3678 m 2481 3798 l 2451 3678 l 2481 3678 l 2511 3678 l  cp gs 0.00 setgray ef gr  col-1 s
% Ellipse
n 10141 1689 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 10146 2588 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 10146 3488 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 10146 4393 900 338 0 360 DrawEllipse gs col-1 s gr

% Polyline
gs  clippath
10176 2103 m 10146 2223 l 10116 2103 l 10116 2265 l 10176 2265 l  cp clip
n 10146 2024 m 10146 2250 l gs col-1 s gr gr

% arrowhead
n 10176 2103 m 10146 2223 l 10116 2103 l 10146 2103 l 10176 2103 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
10176 3003 m 10146 3123 l 10116 3003 l 10116 3165 l 10176 3165 l  cp clip
n 10146 2924 m 10146 3150 l gs col-1 s gr gr

% arrowhead
n 10176 3003 m 10146 3123 l 10116 3003 l 10146 3003 l 10176 3003 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
10176 3903 m 10146 4023 l 10116 3903 l 10116 4065 l 10176 4065 l  cp clip
n 10146 3824 m 10146 4050 l gs col-1 s gr gr

% arrowhead
n 10176 3903 m 10146 4023 l 10116 3903 l 10146 3903 l 10176 3903 l  cp gs 0.00 setgray ef gr  col-1 s
% Ellipse
n 10140 8205 335 335 0 360 DrawEllipse gs col-1 s gr

% Polyline
gs  clippath
10170 7728 m 10140 7848 l 10110 7728 l 10110 7890 l 10170 7890 l  cp clip
n 10140 7425 m 10140 7875 l gs col-1 s gr gr

% arrowhead
n 10170 7728 m 10140 7848 l 10110 7728 l 10140 7728 l 10170 7728 l  cp gs 0.00 setgray ef gr  col-1 s
% Ellipse
n 10140 560 335 335 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 10141 5289 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 10146 6188 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 10146 7088 900 338 0 360 DrawEllipse gs col-1 s gr

% Polyline
gs  clippath
10170 1203 m 10140 1323 l 10110 1203 l 10110 1365 l 10170 1365 l  cp clip
n 10140 900 m 10140 1350 l gs col-1 s gr gr

% arrowhead
n 10170 1203 m 10140 1323 l 10110 1203 l 10140 1203 l 10170 1203 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
10176 5703 m 10146 5823 l 10116 5703 l 10116 5865 l 10176 5865 l  cp clip
n 10146 5624 m 10146 5850 l gs col-1 s gr gr

% arrowhead
n 10176 5703 m 10146 5823 l 10116 5703 l 10146 5703 l 10176 5703 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
10176 6603 m 10146 6723 l 10116 6603 l 10116 6765 l 10176 6765 l  cp clip
n 10146 6524 m 10146 6750 l gs col-1 s gr gr

% arrowhead
n 10176 6603 m 10146 6723 l 10116 6603 l 10146 6603 l 10176 6603 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
10176 4803 m 10146 4923 l 10116 4803 l 10116 4965 l 10176 4965 l  cp clip
n 10146 4724 m 10146 4950 l gs col-1 s gr gr

% arrowhead
n 10176 4803 m 10146 4923 l 10116 4803 l 10146 4803 l 10176 4803 l  cp gs 0.00 setgray ef gr  col-1 s
% Ellipse
n 5625 565 335 335 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 2471 10690 335 335 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 1350 5962 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 3598 5962 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 2475 570 335 335 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 5625 3505 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 5626 5082 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 5631 5981 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 5631 6881 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 5631 7786 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 5625 10703 335 335 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7870 9593 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7876 2381 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7881 3280 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7881 5085 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 7881 4180 900 338 0 360 DrawEllipse gs col-1 s gr

% Ellipse
n 5631 8680 900 338 0 360 DrawEllipse gs col-1 s gr

% Polyline
n 4725 1125 m 6525 1125 l 6525 1800 l 4725 1800 l cp gs col-1 s gr 
% Polyline
gs  clippath
5655 978 m 5625 1098 l 5595 978 l 5595 1140 l 5655 1140 l  cp clip
n 5625 900 m 5625 1125 l gs col-1 s gr gr

% arrowhead
n 5655 978 m 5625 1098 l 5595 978 l 5625 978 l 5655 978 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
5655 1878 m 5625 1998 l 5595 1878 l 5595 2040 l 5655 2040 l  cp clip
n 5625 1800 m 5625 2025 l gs col-1 s gr gr

% arrowhead
n 5655 1878 m 5625 1998 l 5595 1878 l 5625 1878 l 5655 1878 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2505 978 m 2475 1098 l 2445 978 l 2445 1140 l 2505 1140 l  cp clip
n 2475 900 m 2475 1125 l gs col-1 s gr gr

% arrowhead
n 2505 978 m 2475 1098 l 2445 978 l 2475 978 l 2505 978 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2505 4573 m 2475 4693 l 2445 4573 l 2445 4735 l 2505 4735 l  cp clip
n 2475 4500 m 2475 4720 l gs col-1 s gr gr

% arrowhead
n 2505 4573 m 2475 4693 l 2445 4573 l 2475 4573 l 2505 4573 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
1380 5481 m 1350 5601 l 1320 5481 l 1320 5643 l 1380 5643 l  cp clip
n 1575 5085 m 1350 5085 l 1350 5628 l gs col-1 s gr gr

% arrowhead
n 1380 5481 m 1350 5601 l 1320 5481 l 1350 5481 l 1380 5481 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
3630 5483 m 3600 5603 l 3570 5483 l 3570 5645 l 3630 5645 l  cp clip
n 3375 5085 m 3600 5085 l 3600 5630 l gs col-1 s gr gr

% arrowhead
n 3630 5483 m 3600 5603 l 3570 5483 l 3600 5483 l 3630 5483 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2328 6495 m 2448 6525 l 2328 6555 l 2490 6555 l 2490 6495 l  cp clip
n 1350 6300 m 1350 6525 l 2475 6525 l gs col-1 s gr gr

% arrowhead
n 2328 6495 m 2448 6525 l 2328 6555 l 2328 6525 l 2328 6495 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2622 6555 m 2502 6525 l 2622 6495 l 2460 6495 l 2460 6555 l  cp clip
n 3600 6300 m 3600 6525 l 2475 6525 l gs col-1 s gr gr

% arrowhead
n 2622 6555 m 2502 6525 l 2622 6495 l 2622 6525 l 2622 6555 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2505 6603 m 2475 6723 l 2445 6603 l 2445 6765 l 2505 6765 l  cp clip
n 2475 6525 m 2475 6750 l gs col-1 s gr gr

% arrowhead
n 2505 6603 m 2475 6723 l 2445 6603 l 2475 6603 l 2505 6603 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
2510 10213 m 2480 10333 l 2450 10213 l 2450 10375 l 2510 10375 l  cp clip
n 2480 10135 m 2480 10360 l gs col-1 s gr gr

% arrowhead
n 2510 10213 m 2480 10333 l 2450 10213 l 2480 10213 l 2510 10213 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
5655 3018 m 5625 3138 l 5595 3018 l 5595 3180 l 5655 3180 l  cp clip
n 5625 2740 m 5625 3165 l gs col-1 s gr gr

% arrowhead
n 5655 3018 m 5625 3138 l 5595 3018 l 5625 3018 l 5655 3018 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 6525 2385 m 5625 2025 l 4725 2385 l 5625 2745 l cp gs col-1 s gr 
% Polyline
gs  clippath
5655 4601 m 5625 4721 l 5595 4601 l 5595 4763 l 5655 4763 l  cp clip
n 5625 3840 m 5625 4748 l gs col-1 s gr gr

% arrowhead
n 5655 4601 m 5625 4721 l 5595 4601 l 5625 4601 l 5655 4601 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
5661 5496 m 5631 5616 l 5601 5496 l 5601 5658 l 5661 5658 l  cp clip
n 5631 5417 m 5631 5643 l gs col-1 s gr gr

% arrowhead
n 5661 5496 m 5631 5616 l 5601 5496 l 5631 5496 l 5661 5496 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
5661 6396 m 5631 6516 l 5601 6396 l 5601 6558 l 5661 6558 l  cp clip
n 5631 6317 m 5631 6543 l gs col-1 s gr gr

% arrowhead
n 5661 6396 m 5631 6516 l 5601 6396 l 5631 6396 l 5661 6396 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
5661 7296 m 5631 7416 l 5601 7296 l 5601 7458 l 5661 7458 l  cp clip
n 5631 7217 m 5631 7443 l gs col-1 s gr gr

% arrowhead
n 5661 7296 m 5631 7416 l 5601 7296 l 5631 7296 l 5661 7296 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
5661 8197 m 5631 8317 l 5601 8197 l 5601 8359 l 5661 8359 l  cp clip
n 5631 8118 m 5631 8344 l gs col-1 s gr gr

% arrowhead
n 5661 8197 m 5631 8317 l 5601 8197 l 5631 8197 l 5661 8197 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 6525 9586 m 5625 9226 l 4725 9586 l 5625 9946 l cp gs col-1 s gr 
% Polyline
gs  clippath
6828 9558 m 6948 9588 l 6828 9618 l 6990 9618 l 6990 9558 l  cp clip
n 6525 9588 m 6975 9588 l gs col-1 s gr gr

% arrowhead
n 6828 9558 m 6948 9588 l 6828 9618 l 6828 9588 l 6828 9558 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
5772 10178 m 5652 10148 l 5772 10118 l 5610 10118 l 5610 10178 l  cp clip
n 7880 9933 m 7880 10148 l 5625 10148 l gs col-1 s gr gr

% arrowhead
n 5772 10178 m 5652 10148 l 5772 10118 l 5772 10148 l 5772 10178 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
5655 10214 m 5625 10334 l 5595 10214 l 5595 10376 l 5655 10376 l  cp clip
n 5625 9948 m 5625 10361 l gs col-1 s gr gr

% arrowhead
n 5655 10214 m 5625 10334 l 5595 10214 l 5625 10214 l 5655 10214 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
5655 9080 m 5625 9200 l 5595 9080 l 5595 9242 l 5655 9242 l  cp clip
n 5625 9015 m 5625 9227 l gs col-1 s gr gr

% arrowhead
n 5655 9080 m 5625 9200 l 5595 9080 l 5625 9080 l 5655 9080 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7911 2795 m 7881 2915 l 7851 2795 l 7851 2957 l 7911 2957 l  cp clip
n 7881 2716 m 7881 2942 l gs col-1 s gr gr

% arrowhead
n 7911 2795 m 7881 2915 l 7851 2795 l 7881 2795 l 7911 2795 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7911 3695 m 7881 3815 l 7851 3695 l 7851 3857 l 7911 3857 l  cp clip
n 7881 3616 m 7881 3842 l gs col-1 s gr gr

% arrowhead
n 7911 3695 m 7881 3815 l 7851 3695 l 7881 3695 l 7911 3695 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
7911 4595 m 7881 4715 l 7851 4595 l 7851 4757 l 7911 4757 l  cp clip
n 7881 4516 m 7881 4742 l gs col-1 s gr gr

% arrowhead
n 7911 4595 m 7881 4715 l 7851 4595 l 7881 4595 l 7911 4595 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
5772 4320 m 5652 4290 l 5772 4260 l 5610 4260 l 5610 4320 l  cp clip
n 7875 5420 m 7875 5645 l 6750 5640 l 6750 4290 l 5625 4290 l gs col-1 s gr gr

% arrowhead
n 5772 4320 m 5652 4290 l 5772 4260 l 5772 4290 l 5772 4320 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
gs  clippath
6834 2354 m 6954 2384 l 6834 2414 l 6996 2414 l 6996 2354 l  cp clip
n 6531 2384 m 6981 2384 l gs col-1 s gr gr

% arrowhead
n 6834 2354 m 6954 2384 l 6834 2414 l 6834 2384 l 6834 2354 l  cp gs 0.00 setgray ef gr  col-1 s
% Polyline
n 3375 5080 m 2475 4720 l 1575 5080 l 2475 5440 l cp gs col-1 s gr 
$F2psEnd
rs
$EOD
$!
$CREATE fig_arralloc-eepic.ftx
$DECK
\setlength{\unitlength}{0.00057743in}
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
{\renewcommand{\dashlinestretch}{30}
\begin{picture}(9073,5146)(0,-10)
\put(317,494){\makebox(0,0)[lb]{\smash{{{\SetFigFont{11}{13.2}{\ttdefault}{\mddefault}{\updefault}r}}}}}
\put(1960,534){\makebox(0,0)[lb]{\smash{{{\SetFigFont{11}{13.2}{\ttdefault}{\mddefault}{\updefault}r[i]}}}}}
\put(5979,555){\makebox(0,0)[lb]{\smash{{{\SetFigFont{11}{13.2}{\ttdefault}{\mddefault}{\updefault}r[i][j]}}}}}
\put(387,45){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\familydefault}{\mddefault}{\updefault}(pointer)}}}}}
\put(2204,45){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\familydefault}{\mddefault}{\updefault}(array of \textit{m} pointers)}}}}}
\put(6393,45){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\familydefault}{\mddefault}{\updefault}(\textit{m} arrays of \textit{n} data objects)}}}}}
\path(2448,4816)(1839,4816)(1839,3599)(2448,3599)
\path(1839,4208)(2448,4208)
\path(2448,2381)(1839,2381)(1839,1164)(2448,1164)
\path(1839,1772)(2448,1772)
\path(12,4816)(621,4816)(621,4208)
	(12,4208)(12,4816)
\path(317,4512)(1820,4512)
\blacken\path(1580.000,4452.000)(1820.000,4512.000)(1580.000,4572.000)(1580.000,4452.000)
\path(2082,4512)(3665,4512)
\blacken\path(3425.000,4452.000)(3665.000,4512.000)(3425.000,4572.000)(3425.000,4452.000)
\path(2082,3903)(3665,3903)
\blacken\path(3425.000,3843.000)(3665.000,3903.000)(3425.000,3963.000)(3425.000,3843.000)
\path(2448,4816)(2448,3599)
\path(2448,2381)(2448,1164)
\path(2082,1468)(3665,1468)
\blacken\path(3425.000,1408.000)(3665.000,1468.000)(3425.000,1528.000)(3425.000,1408.000)
\path(2082,2077)(3665,2077)
\blacken\path(3425.000,2017.000)(3665.000,2077.000)(3425.000,2137.000)(3425.000,2017.000)
\path(5492,4816)(5492,3599)
\path(5492,2381)(5492,1164)
\path(8536,4816)(8536,3599)
\path(8536,2381)(8536,1164)
\path(4274,2381)(3665,2381)(3665,1164)(4274,1164)
\path(3665,1772)(4274,1772)
\path(4274,4816)(3665,4816)(3665,3599)(4274,3599)
\path(3665,4208)(4274,4208)
\path(5492,4816)(4883,4816)(4883,3599)(5492,3599)
\path(4883,4208)(5492,4208)
\path(5492,2381)(4883,2381)(4883,1164)(5492,1164)
\path(4883,1772)(5492,1772)
\path(7318,4816)(6709,4816)(6709,3599)(7318,3599)
\path(6709,4208)(7318,4208)
\path(8536,4816)(7927,4816)(7927,3599)(8536,3599)
\path(7927,4208)(8536,4208)
\path(8536,2381)(7927,2381)(7927,1164)(8536,1164)
\path(7927,1772)(8536,1772)
\path(7318,2381)(6709,2381)(6709,1164)(7318,1164)
\path(6709,1772)(7318,1772)
\path(4274,4208)(4883,4208)
\path(4883,4816)(4274,4816)(4274,3599)(4883,3599)
\path(7318,4208)(7927,4208)
\path(7927,4816)(7318,4816)(7318,3599)(7927,3599)
\path(7318,1772)(7927,1772)
\path(7927,2381)(7318,2381)(7318,1164)(7927,1164)
\path(4274,1772)(4883,1772)
\path(4883,2381)(4274,2381)(4274,1164)(4883,1164)
\dottedline{120}(1839,3599)(1839,2381)
\dottedline{120}(2448,3599)(2448,2381)
\dottedline{120}(3665,3599)(3665,2381)
\dottedline{120}(4274,3599)(4274,2381)
\dottedline{120}(4883,3599)(4883,2381)
\dottedline{120}(5492,3599)(5492,2381)
\dottedline{120}(6709,3599)(6709,2381)
\dottedline{120}(7318,3599)(7318,2381)
\dottedline{120}(7927,3599)(7927,2381)
\dottedline{120}(8536,3599)(8536,2381)
\dottedline{120}(5492,2381)(6709,2381)
\dottedline{120}(5492,1772)(6709,1772)
\dottedline{120}(5492,1164)(6709,1164)
\dottedline{120}(5492,3599)(6709,3599)
\dottedline{120}(5492,4208)(6709,4208)
\dottedline{120}(5492,4816)(6709,4816)
\put(4457,4999){\makebox(0,0)[lb]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}1}}}}}
\put(5004,4999){\makebox(0,0)[lb]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}2}}}}}
\put(7379,4999){\makebox(0,0)[lb]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}n-2}}}}}
\put(8719,4390){\makebox(0,0)[lb]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}0}}}}}
\put(8719,3842){\makebox(0,0)[lb]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}1}}}}}
\put(8658,2138){\makebox(0,0)[lb]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}m-2}}}}}
\put(3848,4999){\makebox(0,0)[lb]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}0}}}}}
\put(6770,4999){\makebox(0,0)[lb]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}n-3}}}}}
\put(8049,4999){\makebox(0,0)[lb]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}n-1}}}}}
\put(8658,1529){\makebox(0,0)[lb]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\mddefault}{\itdefault}m-1}}}}}
\end{picture}
}
$EOD
$!
$CREATE fig_dostep-a-eepic.ftx
$DECK
\setlength{\unitlength}{0.00057743in}
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
{\renewcommand{\dashlinestretch}{30}
\begin{picture}(10599,10585)(0,-10)
\path(1267,10558)(1942,10558)
\path(1267,10108)(1942,10108)
\spline(1267,10558)
(817,10558)(817,10108)(1267,10108)
\spline(1942,10558)
(2392,10558)(2392,10108)(1942,10108)
\put(8337,9838){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}Calculation of molecular}}}}}
\put(8337,9635){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}centre-of-mass forces}}}}}
\put(8337,9432){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}and torques.}}}}}
\put(1587,4603){\ellipse{1800}{630}}
\put(7212,343){\ellipse{670}{670}}
\put(1587,3470){\ellipse{1800}{630}}
\put(7212,5248){\ellipse{1800}{630}}
\put(7212,9703){\ellipse{1800}{630}}
\put(9469,8582){\ellipse{1800}{630}}
\put(9469,2777){\ellipse{1800}{630}}
\put(9469,3902){\ellipse{1800}{630}}
\put(9469,1652){\ellipse{1800}{630}}
\put(1582,5833){\ellipse{2520}{900}}
\put(3844,2327){\ellipse{1800}{630}}
\path(1582,9680)(1582,10108)
\blacken\path(1552.000,9800.000)(1582.000,9680.000)(1612.000,9800.000)(1552.000,9800.000)
\path(1587,4285)(1587,3778)
\blacken\path(1557.000,3898.000)(1587.000,3778.000)(1617.000,3898.000)(1557.000,3898.000)
\path(1587,6733)(1587,6268)
\blacken\path(1557.000,6388.000)(1587.000,6268.000)(1617.000,6388.000)(1557.000,6388.000)
\path(1587,7408)(1587,7858)
\blacken\path(1557.000,7528.000)(1587.000,7408.000)(1617.000,7528.000)(1557.000,7528.000)
\path(817,8533)(2392,8533)(2392,7858)
	(817,7858)(817,8533)
\path(817,7408)(2392,7408)(2392,6733)
	(817,6733)(817,7408)
\path(2937,9665)(4512,9665)(4512,8990)
	(2937,8990)(2937,9665)
\path(1587,5383)(1587,4918)
\blacken\path(1557.000,5038.000)(1587.000,4918.000)(1617.000,5038.000)(1557.000,5038.000)
\drawline(1587,2948)(1587,2948)
\path(3842,877)(3842,652)(1592,652)
\blacken\path(1712.000,682.000)(1592.000,652.000)(1712.000,622.000)(1712.000,682.000)
\path(2944,1559)(4519,1559)(4519,884)
	(2944,884)(2944,1559)
\path(2487,1212)(2942,1212)
\blacken\path(2822.000,1182.000)(2942.000,1212.000)(2822.000,1242.000)(2822.000,1182.000)
\path(1587,853)(1587,208)(5412,208)
	(5412,10558)(7212,10558)(7212,10018)
\blacken\path(7182.000,10138.000)(7212.000,10018.000)(7242.000,10138.000)(7182.000,10138.000)
\path(1587,2683)(1587,3151)
\blacken\path(1557.000,2803.000)(1587.000,2683.000)(1617.000,2803.000)(1557.000,2803.000)
\path(7217,4933)(7217,4258)
\blacken\path(7187.000,4378.000)(7217.000,4258.000)(7247.000,4378.000)(7187.000,4378.000)
\path(7217,3531)(7217,675)
\blacken\path(7187.000,795.000)(7217.000,675.000)(7247.000,795.000)(7187.000,795.000)
\path(9467,1332)(9467,1108)(7217,1108)
\blacken\path(7337.000,1138.000)(7217.000,1108.000)(7337.000,1078.000)(7337.000,1138.000)
\path(2494,9321)(2929,9321)
\blacken\path(2809.000,9291.000)(2929.000,9321.000)(2809.000,9351.000)(2809.000,9291.000)
\dashline{90.000}(5862,10333)(10587,10333)(10587,4708)
	(5862,4708)(5862,10333)
\path(7211,6013)(7211,5562)
\blacken\path(7181.000,5682.000)(7211.000,5562.000)(7241.000,5682.000)(7181.000,5682.000)
\path(6427,7813)(8002,7813)(8002,7138)
	(6427,7138)(6427,7813)
\path(6427,6688)(8002,6688)(8002,6013)
	(6427,6013)(6427,6688)
\path(7212,9388)(7212,8938)
\blacken\path(7182.000,9058.000)(7212.000,8938.000)(7242.000,9058.000)(7182.000,9058.000)
\path(8117,8578)(7217,8938)(6317,8578)
	(7217,8218)(8117,8578)
\path(7217,8218)(7217,7813)
\blacken\path(7187.000,7933.000)(7217.000,7813.000)(7247.000,7933.000)(7187.000,7933.000)
\path(7212,7138)(7212,6688)
\blacken\path(7182.000,6808.000)(7212.000,6688.000)(7242.000,6808.000)(7182.000,6808.000)
\path(9462,8263)(9462,8038)(7222,8038)
\blacken\path(7342.000,8068.000)(7222.000,8038.000)(7342.000,8008.000)(7342.000,8068.000)
\dashline{90.000}(12,5158)(4962,5158)(4962,433)
	(12,433)(12,5158)
\path(8122,3892)(8567,3892)
\blacken\path(8447.000,3862.000)(8567.000,3892.000)(8447.000,3922.000)(8447.000,3862.000)
\path(9462,3583)(9462,3088)
\blacken\path(9432.000,3208.000)(9462.000,3088.000)(9492.000,3208.000)(9432.000,3208.000)
\path(9462,2458)(9462,1963)
\blacken\path(9432.000,2083.000)(9462.000,1963.000)(9492.000,2083.000)(9432.000,2083.000)
\dashline{90.000}(5862,4483)(5862,883)(7887,883)
	(10587,890)(10587,4483)(5862,4483)
\path(3747,8983)(3747,8758)(1587,8758)
\blacken\path(1707.000,8788.000)(1587.000,8758.000)(1707.000,8728.000)(1707.000,8788.000)
\path(2482,9320)(1582,9680)(682,9320)
	(1582,8960)(2482,9320)
\path(1581,8960)(1581,8533)
\blacken\path(1551.000,8653.000)(1581.000,8533.000)(1611.000,8653.000)(1551.000,8653.000)
\path(2497,2317)(2942,2317)
\blacken\path(2822.000,2287.000)(2942.000,2317.000)(2822.000,2347.000)(2822.000,2287.000)
\path(3842,2007)(3842,1777)(1592,1777)
\blacken\path(1712.000,1807.000)(1592.000,1777.000)(1712.000,1747.000)(1712.000,1807.000)
\path(1587,1957)(1587,1572)
\blacken\path(1557.000,1692.000)(1587.000,1572.000)(1617.000,1692.000)(1557.000,1692.000)
\path(2487,2323)(1587,2683)(687,2323)
	(1587,1963)(2487,2323)
\path(2487,1214)(1587,1574)(687,1214)
	(1587,854)(2487,1214)
\path(8117,3898)(7217,4258)(6317,3898)
	(7217,3538)(8117,3898)
\path(8119,8579)(8564,8579)
\blacken\path(8444.000,8549.000)(8564.000,8579.000)(8444.000,8609.000)(8444.000,8549.000)
\put(1587,9253){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}first call?}}}}}
\put(957,8308){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Allocate arrays}}}}}
\put(957,8105){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}for sites and}}}}}
\put(957,7902){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}site forces}}}}}
\put(957,7138){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Set up array of}}}}}
\put(957,6935){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}site charges}}}}}
\put(1587,6013){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}step\_1()}}}}}
\put(3072,9433){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Calc. distant}}}}}
\put(3072,9253){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}pot'l \& pressure}}}}}
\put(3072,9073){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}correction}}}}}
\put(1587,4663){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}make\_sites()}}}}}
\put(1587,4483){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Calc. site co-ords.}}}}}
\put(3837,2413){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}ewald()}}}}}
\put(3837,2188){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}recip-space forces}}}}}
\put(2487,2458){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(1362,1783){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(1362,8758){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(2487,9433){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(1587,3523){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}force\_calc()}}}}}
\put(1587,2263){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}coulomb forces?}}}}}
\put(1587,1228){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}surface-dipole}}}}}
\put(8115,4031){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(6990,3364){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(7215,297){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}A}}}}}
\put(9462,8623){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}mol\_torque()}}}}}
\put(7257,5338){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}newton()}}}}}
\put(7212,9763){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}mol\_force()}}}}}
\put(7222,8683){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}any}}}}}
\put(7222,8480){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}polyatomics?}}}}}
\put(6532,7400){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}molecular virial}}}}}
\put(6532,7197){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}correction term.}}}}}
\put(6537,6305){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}last timestep in}}}}}
\put(6537,6102){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}"old" arrays etc.}}}}}
\put(8112,8758){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(6987,8083){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(9462,2878){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}rahman()}}}}}
\put(9462,1738){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}beeman\_2()}}}}}
\put(1587,5826){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Apply Beeman steps \textit{i, ii} to}}}}}
\put(2764,4731){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}(b)}}}}}
\put(1587,3320){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}real-space forces}}}}}
\put(1587,2465){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}need}}}}}
\put(1587,1062){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}switch on?}}}}}
\put(3027,1085){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}forces \& energy.}}}}}
\put(3027,1288){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get surf. dipole}}}}}
\put(8195,9892){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}(c)}}}}}
\put(7207,9576){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get C of M forces.}}}}}
\put(9465,8428){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get torques.}}}}}
\put(6537,6508){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Store acc's from}}}}}
\put(6532,7603){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get atomic,}}}}}
\put(7207,3851){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Const stress?}}}}}
\put(6097,4244){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}(d)}}}}}
\put(7215,5134){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}New C of M acc's}}}}}
\put(462,6274){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}(a)}}}}}
\put(9462,1539){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Step cell velocities}}}}}
\put(9457,2690){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get unit cell acc's}}}}}
\put(2952,4723){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}Calculation of atomic}}}}}
\put(2952,4520){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}site forces and}}}}}
\put(2952,4317){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}potential energy.}}}}}
\put(1587,5660){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}update dynamic variables}}}}}
\put(1587,10259){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}do\_step()}}}}}
\put(2487,1318){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(1362,666){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(9462,3973){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}energy\_dyad()}}}}}
\put(9447,3778){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault} kinetic stress term}}}}}
\end{picture}
}
$EOD
$!
$CREATE fig_dostep-b-eepic.ftx
$DECK
\setlength{\unitlength}{0.00057743in}
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
{\renewcommand{\dashlinestretch}{30}
\begin{picture}(10449,10393)(0,-10)
\path(6473,462)(7148,462)
\path(6473,12)(7148,12)
\spline(6473,462)
(6023,462)(6023,12)(6473,12)
\spline(7148,462)
(7598,462)(7598,12)(7148,12)
\put(7587,5626){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}(f)}}}}}
\put(7837,5449){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}derivatives using Beeman}}}}}
\put(7837,5608){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}Iteratively update quatern}}}}}
\put(7837,5298){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}step \emph{iv}.}}}}}
\put(2442,3931){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}(e)}}}}}
\put(2642,3936){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}Iteratative loop to step}}}}}
\put(2647,3776){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}C of M velocities using}}}}}
\put(2647,3632){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}Beeman step \emph{iv}.}}}}}
\put(9076,8312){\ellipse{1800}{676}}
\put(9083,9534){\ellipse{1800}{676}}
\put(6826,8312){\ellipse{1800}{676}}
\put(6826,7190){\ellipse{1800}{676}}
\put(6829,6068){\ellipse{1800}{676}}
\put(6837,2492){\ellipse{2520}{900}}
\put(6833,4959){\ellipse{1800}{630}}
\put(9071,1205){\ellipse{1800}{630}}
\put(1437,9986){\ellipse{670}{670}}
\put(3687,5381){\ellipse{1800}{676}}
\put(3694,6603){\ellipse{1800}{676}}
\put(1437,5381){\ellipse{1800}{676}}
\put(1437,4259){\ellipse{1800}{676}}
\put(1440,3137){\ellipse{1800}{676}}
\put(3694,7805){\ellipse{1800}{676}}
\path(1437,1636)(1437,1186)
\blacken\path(1407.000,1306.000)(1437.000,1186.000)(1467.000,1306.000)(1407.000,1306.000)
\path(1441,451)(1441,226)(5502,226)
	(5502,3166)(6837,3166)
\blacken\path(6717.000,3136.000)(6837.000,3166.000)(6717.000,3196.000)(6717.000,3136.000)
\path(529,8971)(12,8971)(12,1367)(1437,1367)
\blacken\path(1317.000,1337.000)(1437.000,1367.000)(1317.000,1397.000)(1317.000,1337.000)
\path(6831,9152)(6831,8640)
\blacken\path(6801.000,8760.000)(6831.000,8640.000)(6861.000,8760.000)(6801.000,8760.000)
\path(6826,7974)(6826,7530)
\blacken\path(6796.000,7650.000)(6826.000,7530.000)(6856.000,7650.000)(6796.000,7650.000)
\path(9076,9202)(9076,8623)
\path(9081,7980)(9081,7707)(6831,7707)
\blacken\path(6951.000,7737.000)(6831.000,7707.000)(6951.000,7677.000)(6951.000,7737.000)
\path(7736,9538)(8181,9538)
\blacken\path(8061.000,9508.000)(8181.000,9538.000)(8061.000,9568.000)(8061.000,9508.000)
\path(6826,6855)(6826,6405)
\blacken\path(6796.000,6525.000)(6826.000,6405.000)(6856.000,6525.000)(6796.000,6525.000)
\path(6830,5733)(6830,5274)
\blacken\path(6800.000,5394.000)(6830.000,5274.000)(6860.000,5394.000)(6800.000,5394.000)
\path(6832,4639)(6832,4199)
\blacken\path(6802.000,4319.000)(6832.000,4199.000)(6862.000,4319.000)(6802.000,4319.000)
\path(6832,826)(6832,462)
\blacken\path(6802.000,582.000)(6832.000,462.000)(6862.000,582.000)(6802.000,582.000)
\path(6837,3467)(6837,2942)
\blacken\path(6807.000,3062.000)(6837.000,2942.000)(6867.000,3062.000)(6807.000,3062.000)
\path(6837,2037)(6837,1591)
\blacken\path(6807.000,1711.000)(6837.000,1591.000)(6867.000,1711.000)(6807.000,1711.000)
\path(7732,3831)(10215,3831)(10215,10144)(6826,10144)
\blacken\path(6946.000,10174.000)(6826.000,10144.000)(6946.000,10114.000)(6946.000,10174.000)
\path(6826,6651)(5697,6651)(5697,9541)(5926,9541)
\blacken\path(5817.000,9571.000)(5697.000,9541.000)(5817.000,9511.000)(5817.000,9571.000)
\path(7741,1207)(8166,1207)
\blacken\path(8046.000,1177.000)(8166.000,1207.000)(8046.000,1237.000)(8046.000,1177.000)
\path(9076,885)(9076,685)(6831,685)
\blacken\path(6951.000,715.000)(6831.000,685.000)(6951.000,655.000)(6951.000,715.000)
\path(2337,811)(1437,1171)(537,811)
	(1437,451)(2337,811)
\path(2337,811)(5262,811)(5262,10366)
	(6827,10366)(6827,9924)
\blacken\path(6797.000,10044.000)(6827.000,9924.000)(6857.000,10044.000)(6797.000,10044.000)
\path(7727,9538)(6827,9924)(5927,9538)
	(6827,9152)(7727,9538)
\path(7732,3831)(6832,4191)(5932,3831)
	(6832,3471)(7732,3831)
\path(7737,1207)(6837,1593)(5937,1207)
	(6837,821)(7737,1207)
\dashline{60.000}(5502,10261)(10437,10261)(10437,3275)
	(5502,3275)(5502,10261)
\path(2337,2002)(4812,2002)(4812,8441)(1437,8443)
\blacken\path(1557.018,8472.929)(1437.000,8443.000)(1556.982,8412.929)(1557.018,8472.929)
\path(1442,7412)(1442,6994)
\blacken\path(1412.000,7114.000)(1442.000,6994.000)(1472.000,7114.000)(1412.000,7114.000)
\path(3692,7469)(3692,7211)(1442,7211)
\blacken\path(1562.000,7241.000)(1442.000,7211.000)(1562.000,7181.000)(1562.000,7241.000)
\path(2337,7806)(1437,8192)(537,7806)
	(1437,7420)(2337,7806)
\path(2347,7806)(2792,7806)
\blacken\path(2672.000,7776.000)(2792.000,7806.000)(2672.000,7836.000)(2672.000,7776.000)
\path(1437,5043)(1437,4599)
\blacken\path(1407.000,4719.000)(1437.000,4599.000)(1467.000,4719.000)(1407.000,4719.000)
\path(3687,6271)(3687,5692)
\path(3692,5049)(3692,4776)(1442,4776)
\blacken\path(1562.000,4806.000)(1442.000,4776.000)(1562.000,4746.000)(1562.000,4806.000)
\path(1437,3704)(312,3704)(312,6608)(537,6600)
\blacken\path(432.990,6633.717)(312.000,6608.000)(430.858,6573.755)(432.990,6633.717)
\path(2347,6607)(2792,6607)
\blacken\path(2672.000,6577.000)(2792.000,6607.000)(2672.000,6637.000)(2672.000,6577.000)
\path(2337,6607)(1437,6993)(537,6607)
	(1437,6221)(2337,6607)
\path(1437,3924)(1437,3474)
\blacken\path(1407.000,3594.000)(1437.000,3474.000)(1467.000,3594.000)(1407.000,3594.000)
\path(2337,2002)(1437,2362)(537,2002)
	(1437,1642)(2337,2002)
\path(1441,2802)(1441,2367)
\blacken\path(1411.000,2487.000)(1441.000,2367.000)(1471.000,2487.000)(1411.000,2487.000)
\path(2337,8971)(1437,9331)(537,8971)
	(1437,8611)(2337,8971)
\path(1437,9645)(1437,9331)
\blacken\path(1407.000,9451.000)(1437.000,9331.000)(1467.000,9451.000)(1407.000,9451.000)
\path(1437,8609)(1437,8198)
\blacken\path(1407.000,8318.000)(1437.000,8198.000)(1467.000,8318.000)(1407.000,8318.000)
\path(1435,6221)(1435,5727)
\blacken\path(1405.000,5847.000)(1435.000,5727.000)(1465.000,5847.000)(1405.000,5847.000)
\dashline{60.000}(177,8551)(4947,8551)(4947,1456)
	(177,1456)(177,8551)
\put(1677,316){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(2367,931){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(6833,9474){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Thermostat ?}}}}}
\put(5686,9692){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}None}}}}}
\put(7051,8960){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Gaussian}}}}}
\put(7875,9789){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Nose-Hoover}}}}}
\put(6816,8414){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}gtherm()}}}}}
\put(7791,3912){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(6852,5049){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}beeman\_2()}}}}}
\put(6829,6144){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}euler()}}}}}
\put(6867,7224){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}hoover\_rot()}}}}}
\put(7066,3342){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(6852,2652){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}step\_2()}}}}}
\put(6837,192){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}return}}}}}
\put(9076,8416){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}beeman\_2()}}}}}
\put(9076,9664){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}nhtherm()}}}}}
\put(9087,8176){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Predict N-H $\zeta$}}}}}
\put(9072,9421){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get N-H $\dot{\zeta}$}}}}}
\put(6822,8161){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Calc.  gaussian $\zeta$}}}}}
\put(6831,3949){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}quatern}}}}}
\put(6831,3660){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}converged?}}}}}
\put(6831,3807){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}derivatives}}}}}
\put(6842,2481){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Apply Beeman step \textit{iv} to}}}}}
\put(6852,2309){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}dynamic var derivatives}}}}}
\put(6837,4871){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}step quatern derivs}}}}}
\put(6832,7046){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get torque corr.}}}}}
\put(6837,5951){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Calc angular accs}}}}}
\put(6822,1156){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}dump\_interval}}}}}
\put(6821,1323){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}every}}}}}
\put(6822,988){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}steps}}}}}
\put(7932,2941){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\itdefault}(g)}}}}}
\put(9091,1330){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}dump()}}}}}
\put(9068,1167){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}write trajectories,}}}}}
\put(9068,1005){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}forces \emph{etc.}}}}}}
\put(1437,9886){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}A}}}}}
\put(537,9106){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(1669,8438){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(1437,9033){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}const-stress}}}}}
\put(1437,8869){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}or thermostat?}}}}}
\put(1437,7636){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}simulation?}}}}}
\put(1444,6543){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Thermostat ?}}}}}
\put(297,6761){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}None}}}}}
\put(1662,6029){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Gaussian}}}}}
\put(2486,6858){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Nose-Hoover}}}}}
\put(3687,6688){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}nhtherm()}}}}}
\put(1447,4358){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}hoover\_tr()}}}}}
\put(1427,5483){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}gtherm()}}}}}
\put(3687,5435){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}beeman\_2()}}}}}
\put(1422,3198){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}beeman\_2()}}}}}
\put(1452,1894){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}converged?}}}}}
\put(1452,2071){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}velocities}}}}}
\put(2352,2131){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(1437,7814){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Const stress}}}}}
\put(3697,7894){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}parinello()}}}}}
\put(3687,6466){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get N-H $\dot{\zeta}$}}}}}
\put(3702,5228){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Step N-H $\zeta$}}}}}
\put(1437,5206){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Get  gaussian $\zeta$}}}}}
\put(3692,7671){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Get P\&R acc corr.}}}}}
\put(1422,2986){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}step C of M vels}}}}}
\put(1437,4126){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Calc N-H acc corr.}}}}}
\put(1590,1509){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(1437,901){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}any}}}}}
\put(1437,746){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}polyatomics?}}}}}
\end{picture}
}
$EOD
$!
$CREATE fig_ewald-eepic.ftx
$DECK
\setlength{\unitlength}{0.00057743in}
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
{\renewcommand{\dashlinestretch}{30}
\begin{picture}(5329,9265)(0,-10)
\path(516,6539)(2316,6539)(2316,5864)
	(516,5864)(516,6539)
\put(576,6314){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Set up $\sin \mathbf{k \cdot r}_i$ and }}}}}
\put(576,6147){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}$\cos\mathbf{k \cdot r}_i$ arrays for}}}}}
\put(576,5960){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}$\mathbf{k}=\mathbf{0}, \mathbf{a}^*, \mathbf{b}^*, \mathbf{c}^*$}}}}}
\put(576,5223){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}$\mathbf{k}= h\mathbf{a}^*, k\mathbf{b}^*, l\mathbf{c}^*$}}}}}
\put(576,5412){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Get $\sin$, $\cos \mathbf{k \cdot r}_i $ for}}}}}
\put(576,5056){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}using recursion}}}}}
\path(516,5637)(2316,5637)(2316,4962)
	(516,4962)(516,5637)
\path(516,3837)(2316,3837)(2316,3387)
	(516,3387)(516,3837)
\put(576,3657){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Calc. pre-factors}}}}}
\put(576,3490){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}for energy, force}}}}}
\put(1416,2715){\ellipse{2696}{894}}
\put(1416,2513){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}for this $\mathbf{k}$ and all sites $i$}}}}}
\put(1416,2745){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Evaluate  $\sin$ and $\cos \mathbf{k \cdot r}_i$}}}}}
\put(1416,2954){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}qsincos()}}}}}
\path(516,2037)(2316,2037)(2316,1587)
	(516,1587)(516,2037)
\put(576,1683){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}and energy term}}}}}
\put(576,1857){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Eval. struct. factor}}}}}
\path(516,1363)(2316,1363)(2316,913)
	(516,913)(516,1363)
\put(576,1183){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Eval. forces on all}}}}}
\put(576,1016){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}sites \& add to total}}}}}
\path(1086,462)(1761,462)
\path(1086,12)(1761,12)
\spline(1086,462)
(636,462)(636,12)(1086,12)
\spline(1761,462)
(2211,462)(2211,12)(1761,12)
\path(1086,9238)(1761,9238)
\path(1086,8788)(1761,8788)
\spline(1086,9238)
(636,9238)(636,8788)(1086,8788)
\spline(1761,9238)
(2211,9238)(2211,8788)(1761,8788)
\put(1401,8953){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}ewald()}}}}}
\path(1416,8788)(1416,8563)
\blacken\path(1386.000,8683.000)(1416.000,8563.000)(1446.000,8683.000)(1386.000,8683.000)
\path(416,8218)(1416,8563)(2441,8218)
	(1416,7888)(416,8218)
\path(2982,8563)(4782,8563)(4782,7888)
	(2982,7888)(2982,8563)
\path(2442,8219)(2982,8219)
\blacken\path(2862.000,8189.000)(2982.000,8219.000)(2862.000,8249.000)(2862.000,8189.000)
\path(3882,7888)(3882,7663)(1425,7663)
\blacken\path(1545.000,7693.000)(1425.000,7663.000)(1545.000,7633.000)(1545.000,7693.000)
\path(1416,7893)(1416,7443)
\blacken\path(1386.000,7563.000)(1416.000,7443.000)(1446.000,7563.000)(1386.000,7563.000)
\path(1416,689)(2991,689)(2991,4399)(2444,4399)
\blacken\path(2564.000,4429.000)(2444.000,4399.000)(2564.000,4369.000)(2564.000,4429.000)
\path(1416,1583)(1416,1363)
\blacken\path(1386.000,1483.000)(1416.000,1363.000)(1446.000,1483.000)(1386.000,1483.000)
\path(1416,913)(1416,463)
\blacken\path(1386.000,583.000)(1416.000,463.000)(1446.000,583.000)(1386.000,583.000)
\path(1416,2258)(1416,2038)
\blacken\path(1386.000,2158.000)(1416.000,2038.000)(1446.000,2158.000)(1386.000,2158.000)
\path(1416,3380)(1416,3160)
\blacken\path(1386.000,3280.000)(1416.000,3160.000)(1446.000,3280.000)(1386.000,3280.000)
\path(1416,4061)(1416,3841)
\blacken\path(1386.000,3961.000)(1416.000,3841.000)(1446.000,3961.000)(1386.000,3961.000)
\path(1416,4961)(1416,4741)
\blacken\path(1386.000,4861.000)(1416.000,4741.000)(1446.000,4861.000)(1386.000,4861.000)
\path(416,4396)(1416,4741)(2441,4396)
	(1416,4063)(416,4396)
\path(1416,5861)(1416,5641)
\blacken\path(1386.000,5761.000)(1416.000,5641.000)(1446.000,5761.000)(1386.000,5761.000)
\path(1416,6761)(1416,6541)
\blacken\path(1386.000,6661.000)(1416.000,6541.000)(1446.000,6661.000)(1386.000,6661.000)
\path(516,7443)(2316,7443)(2316,6768)
	(516,6768)(516,7443)
\put(3117,8005){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}terms}}}}}
\put(3117,8346){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Calculate self-}}}}}
\put(3117,8171){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}and sheet-energy }}}}}
\put(1416,8167){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}first call?}}}}}
\put(1416,4274){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}\emph{k}-vectors}}}}}
\put(1416,4456){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}loop over}}}}}
\put(576,7233){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Precompute list of}}}}}
\put(576,7044){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}$h,k,l$ for \emph{k}-vectors}}}}}
\put(576,6855){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}within cutoff}}}}}
\put(1401,193){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}return}}}}}
\end{picture}
}
$EOD
$!
$CREATE fig_link-cell-eepic.ftx
$DECK
\setlength{\unitlength}{0.00057743in}
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
{\renewcommand{\dashlinestretch}{30}
\begin{picture}(9933,11064)(0,-10)
\path(462,1812)(1137,1812)
\path(462,1362)(1137,1362)
\spline(462,1812)
(12,1812)(12,1362)(462,1362)
\spline(1137,1812)
(1587,1812)(1587,1362)(1137,1362)
\path(6312,462)(6987,462)
\path(6312,12)(6987,12)
\spline(6312,462)
(5862,462)(5862,12)(6312,12)
\spline(6987,462)
(7437,462)(7437,12)(6987,12)
\path(6312,11037)(6987,11037)
\path(6312,10587)(6987,10587)
\spline(6312,11037)
(5862,11037)(5862,10587)(6312,10587)
\spline(6987,11037)
(7437,11037)(7437,10587)(6987,10587)
\path(462,11037)(1137,11037)
\path(462,10587)(1137,10587)
\spline(462,11037)
(12,11037)(12,10587)(462,10587)
\spline(1137,11037)
(1587,11037)(1587,10587)(1137,10587)
\put(822,2727){\ellipse{1620}{900}}
\put(6650,8983){\ellipse{2760}{1110}}
\put(6635,3159){\ellipse{2460}{870}}
\put(822,4069){\ellipse{1620}{900}}
\put(2262,7812){\ellipse{1980}{450}}
\put(2352,6649){\ellipse{2430}{540}}
\drawline(6639,6549)(6639,6549)
\path(9696,3790)(9921,4600)(8346,4600)
	(8121,3790)(9696,3790)
\drawline(6621,924)(6621,924)
\path(5405,2499)(7880,2499)(7880,1824)
	(5405,1824)(5405,2499)
\path(5390,1599)(7865,1599)(7865,924)
	(5390,924)(5390,1599)
\path(5622,4183)(6620,4528)(7647,4183)
	(6635,3853)(5622,4183)
\path(5390,5413)(7865,5413)(7865,4738)
	(5390,4738)(5390,5413)
\path(5405,6328)(7880,6328)(7880,5653)
	(5405,5653)(5405,6328)
\path(5622,6883)(6620,7228)(7647,6883)
	(6635,6553)(5622,6883)
\path(5390,8128)(7865,8128)(7865,7453)
	(5390,7453)(5390,8128)
\path(5622,10048)(6620,10393)(7647,10048)
	(6635,9718)(5622,10048)
\path(792,2277)(792,1827)
\blacken\path(762.000,1947.000)(792.000,1827.000)(822.000,1947.000)(762.000,1947.000)
\path(6636,9717)(6639,9537)
\blacken\path(6607.004,9656.483)(6639.000,9537.000)(6666.996,9657.483)(6607.004,9656.483)
\path(6625,8428)(6625,8128)
\blacken\path(6595.000,8248.000)(6625.000,8128.000)(6655.000,8248.000)(6595.000,8248.000)
\path(6633,6552)(6632,6332)
\blacken\path(6602.546,6452.135)(6632.000,6332.000)(6662.545,6451.862)(6602.546,6452.135)
\path(6625,5657)(6625,5417)
\blacken\path(6595.000,5537.000)(6625.000,5417.000)(6655.000,5537.000)(6595.000,5537.000)
\path(6610,2725)(6610,2507)
\blacken\path(6580.000,2627.000)(6610.000,2507.000)(6640.000,2627.000)(6580.000,2627.000)
\path(6610,1832)(6609,1596)
\blacken\path(6579.509,1716.126)(6609.000,1596.000)(6639.508,1715.872)(6579.509,1716.126)
\path(6628,924)(6628,459)
\blacken\path(6598.000,579.000)(6628.000,459.000)(6658.000,579.000)(6598.000,579.000)
\path(6628,691)(4941,691)(4942,10047)(5623,10048)
\blacken\path(5503.044,10017.824)(5623.000,10048.000)(5502.956,10077.824)(5503.044,10017.824)
\path(5167,702)(5167,6883)(5631,6883)
\blacken\path(5511.000,6853.000)(5631.000,6883.000)(5511.000,6913.000)(5511.000,6853.000)
\path(6625,10393)(6624,10578)
\blacken\path(6594.352,10512.836)(6625.000,10393.000)(6654.351,10513.160)(6594.352,10512.836)
\path(6621,7230)(6624,7449)
\blacken\path(6592.646,7350.400)(6621.000,7230.000)(6652.641,7349.578)(6592.646,7350.400)
\dashline{90.000}(1625,2757)(4295,2757)(4287,10821)(5854,10821)
\blacken\path(5734.000,10791.000)(5854.000,10821.000)(5734.000,10851.000)(5734.000,10791.000)
\path(7647,4183)(8232,4183)
\blacken\path(8112.000,4153.000)(8232.000,4183.000)(8112.000,4213.000)(8112.000,4153.000)
\dashline{90.000}(1632,2667)(4287,2667)(4287,237)(5839,237)
\blacken\path(4407.000,267.000)(4287.000,237.000)(4407.000,207.000)(4407.000,267.000)
\path(6619,4530)(6618,4731)
\blacken\path(6588.403,4649.849)(6619.000,4530.000)(6648.403,4650.148)(6588.403,4649.849)
\path(6634,3854)(6634,3591)
\blacken\path(6604.000,3711.000)(6634.000,3591.000)(6664.000,3711.000)(6604.000,3711.000)
\path(2037,10152)(3612,10152)(3612,9252)
	(2037,9252)(2037,10152)
\path(12,9657)(797,10332)(1587,9657)
	(807,8982)(12,9657)
\path(12,8577)(3612,8577)(3612,6327)
	(12,6327)(12,8577)
\path(12,5877)(1587,5877)(1587,4977)
	(12,4977)(12,5877)
\path(124,7206)(909,7671)(1699,7206)
	(919,6758)(124,7206)
\path(1587,9657)(2037,9657)
\blacken\path(1917.000,9627.000)(2037.000,9657.000)(1917.000,9687.000)(1917.000,9627.000)
\path(2802,9252)(2802,8787)(822,8787)
\blacken\path(942.000,8817.000)(822.000,8787.000)(942.000,8757.000)(942.000,8817.000)
\path(777,6327)(777,5877)
\blacken\path(747.000,5997.000)(777.000,5877.000)(807.000,5997.000)(747.000,5997.000)
\path(909,7671)(909,7815)(1287,7812)
\blacken\path(1166.766,7782.953)(1287.000,7812.000)(1167.242,7842.951)(1166.766,7782.953)
\path(918,6756)(918,6642)(1131,6639)
\blacken\path(1010.589,6610.693)(1131.000,6639.000)(1011.434,6670.687)(1010.589,6610.693)
\path(804,8982)(804,8577)
\blacken\path(774.000,8697.000)(804.000,8577.000)(834.000,8697.000)(774.000,8697.000)
\path(795,10332)(798,10587)
\blacken\path(766.414,10452.345)(795.000,10332.000)(826.410,10451.639)(766.414,10452.345)
\path(797,3617)(797,3182)
\blacken\path(767.000,3302.000)(797.000,3182.000)(827.000,3302.000)(767.000,3302.000)
\path(792,4977)(792,4527)
\blacken\path(762.000,4647.000)(792.000,4527.000)(822.000,4647.000)(762.000,4647.000)
\put(7656,4300){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(6778,3685){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(822,2682){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}force\_inner()}}}}}
\put(822,1527){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}return}}}}}
\put(6590,206){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}return}}}}}
\put(6590,3341){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}kernel()}}}}}
\put(6650,9110){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}site\_neighbour\_list()}}}}}
\put(9081,4112){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Print warning.}}}}}
\put(6620,2859){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}on neighbour list.}}}}}
\put(6665,4062){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}site approaches}}}}}
\put(6650,4227){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}test for close}}}}}
\put(6650,6972){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}loop over}}}}}
\put(6665,8622){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}range of current cell.}}}}}
\put(6665,8870){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Build list of all sites within}}}}}
\put(6702,9987){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}loop- over all cells}}}}}
\put(5525,1344){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Distribute neighbour list}}}}}
\put(5525,1989){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}on sites in neighbour list.}}}}}
\put(5437,7635){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}vectors into neighbour lists}}}}}
\put(5437,7860){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Gather site co-ords reloc,}}}}}
\put(5459,6060){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Gather potential params}}}}}
\put(5459,5790){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}into neighbour list arrays.}}}}}
\put(5450,5145){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Calculate squared pair site}}}}}
\put(5450,4920){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}distances in neighbour list.}}}}}
\put(6620,3099){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Evaluate potential  \& force}}}}}
\put(5525,2214){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Calculate stress \& forces}}}}}
\put(5525,1119){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}forces onto force array.}}}}}
\put(237,8322){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Create list of neighbouring cells.}}}}}
\put(777,10752){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}force()}}}}}
\put(2262,7767){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}neighbour\_list()}}}}}
\put(1587,9882){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(957,8892){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(2802,9927){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Set up arrays of}}}}}
\put(2832,9702){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}P.B.C. relocation}}}}}
\put(2517,9477){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}vectors.}}}}}
\put(2337,6603){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}site\_neighbour\_list()}}}}}
\put(768,6540){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(792,7884){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(87,5607){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Allocate and fill}}}}}
\put(87,5157){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}of pot'l params.}}}}}
\put(87,5382){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}expanded array}}}}}
\put(827,4222){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}fill\_cells()}}}}}
\put(827,4022){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Build linked list }}}}}
\put(822,3832){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}of mols in cells}}}}}
\put(913,7167){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}strict cutoff?}}}}}
\put(6635,6642){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}in cell}}}}}
\put(6635,6815){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}molecules and sites}}}}}
\put(800,9332){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}changed.}}}}}
\put(770,9557){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault} cell size/shape}}}}}
\put(800,9760){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}first call or}}}}}
\put(6665,10738){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}force\_inner()}}}}}
\end{picture}
}
$EOD
$!
$CREATE fig_main-eepic.ftx
$DECK
\setlength{\unitlength}{0.00057743in}
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
{\renewcommand{\dashlinestretch}{30}
\begin{picture}(8798,10839)(0,-10)
\path(2123,10812)(2798,10812)
\path(2123,10362)(2798,10362)
\spline(2123,10812)
(1673,10812)(1673,10362)(2123,10362)
\spline(2798,10812)
(3248,10812)(3248,10362)(2798,10362)
\path(2168,462)(2843,462)
\path(2168,12)(2843,12)
\spline(2168,462)
(1718,462)(1718,12)(2168,12)
\spline(2843,462)
(3293,462)(3293,12)(2843,12)
\put(908,1137){\ellipse{1800}{630}}
\put(5183,3477){\ellipse{1800}{630}}
\put(5183,4377){\ellipse{1800}{630}}
\put(2483,9822){\ellipse{1800}{630}}
\put(2483,8022){\ellipse{1800}{630}}
\put(2483,7122){\ellipse{1800}{630}}
\put(4058,1137){\ellipse{1800}{630}}
\put(5183,5299){\ellipse{1800}{630}}
\put(6835,9007){\ellipse{1800}{630}}
\put(7870,8242){\ellipse{1800}{630}}
\put(4720,9007){\ellipse{1800}{630}}
\put(7890,7349){\ellipse{1800}{630}}
\put(5183,6177){\ellipse{1800}{630}}
\put(5183,2577){\ellipse{1800}{630}}
\path(3383,4377)(2483,4737)(1583,4377)
	(2483,4017)(3383,4377)
\path(3383,3477)(2483,3837)(1583,3477)
	(2483,3117)(3383,3477)
\path(3383,2576)(2483,2936)(1583,2576)
	(2483,2216)(3383,2576)
\path(3383,1676)(2483,2036)(1583,1676)
	(2483,1316)(3383,1676)
\path(2483,10362)(2483,10137)
\blacken\path(2453.000,10257.000)(2483.000,10137.000)(2513.000,10257.000)(2453.000,10257.000)
\path(2483,8533)(2483,8344)
\blacken\path(2453.000,8464.000)(2483.000,8344.000)(2513.000,8464.000)(2453.000,8464.000)
\path(2483,7707)(2483,7441)
\blacken\path(2453.000,7561.000)(2483.000,7441.000)(2513.000,7561.000)(2453.000,7561.000)
\path(2483,6807)(2483,6537)
\blacken\path(2453.000,6657.000)(2483.000,6537.000)(2513.000,6657.000)(2453.000,6657.000)
\path(2483,4917)(2483,4737)
\blacken\path(2453.000,4857.000)(2483.000,4737.000)(2513.000,4857.000)(2453.000,4857.000)
\path(2483,4017)(2483,3837)
\blacken\path(2453.000,3957.000)(2483.000,3837.000)(2513.000,3957.000)(2453.000,3957.000)
\path(2483,3117)(2483,2937)
\blacken\path(2453.000,3057.000)(2483.000,2937.000)(2513.000,3057.000)(2453.000,3057.000)
\path(3382,1677)(4058,1677)(4058,1452)
\blacken\path(4028.000,1572.000)(4058.000,1452.000)(4088.000,1572.000)(4028.000,1572.000)
\path(2483,2186)(1358,2186)(1358,8885)(1572,8890)
\blacken\path(1452.733,8857.205)(1572.000,8890.000)(1451.332,8917.189)(1452.733,8857.205)
\path(3383,5277)(4283,5277)
\blacken\path(4163.000,5247.000)(4283.000,5277.000)(4163.000,5307.000)(4163.000,5247.000)
\path(3383,4377)(4283,4377)
\blacken\path(4163.000,4347.000)(4283.000,4377.000)(4163.000,4407.000)(4163.000,4347.000)
\path(3383,3477)(4283,3477)
\blacken\path(4163.000,3447.000)(4283.000,3477.000)(4163.000,3507.000)(4163.000,3447.000)
\path(3383,2577)(4283,2577)
\blacken\path(4163.000,2547.000)(4283.000,2577.000)(4163.000,2607.000)(4163.000,2547.000)
\path(2483,686)(2483,456)
\blacken\path(2453.000,576.000)(2483.000,456.000)(2513.000,576.000)(2453.000,576.000)
\path(2483,2216)(2483,2036)
\blacken\path(2453.000,2156.000)(2483.000,2036.000)(2513.000,2156.000)(2453.000,2156.000)
\path(4058,822)(4058,686)(2483,686)
\path(2498,4804)(5183,4804)(5183,4984)
\blacken\path(5153.000,4924.000)(5183.000,4804.000)(5213.000,4924.000)(5153.000,4924.000)
\path(2498,3904)(5183,3904)(5183,4069)
\blacken\path(5153.000,4024.000)(5183.000,3904.000)(5213.000,4024.000)(5153.000,4024.000)
\path(2498,3019)(5198,3019)(5198,3154)
\blacken\path(5168.000,3139.000)(5198.000,3019.000)(5228.000,3139.000)(5168.000,3139.000)
\path(2483,2186)(5183,2186)(5183,2254)
\blacken\path(5153.000,2306.000)(5183.000,2186.000)(5213.000,2306.000)(5153.000,2306.000)
\path(908,1452)(908,1677)(1583,1677)
\blacken\path(1028.000,1707.000)(908.000,1677.000)(1028.000,1647.000)(1028.000,1707.000)
\dashline{90.000}(3383,8025)(3821,9014)
\blacken\path(3404.162,8146.869)(3383.000,8025.000)(3459.023,8122.573)(3404.162,8146.869)
\blacken\path(3799.838,8892.131)(3821.000,9014.000)(3744.977,8916.427)(3799.838,8892.131)
\dashline{90.000}(7300,8744)(7510,8549)
\blacken\path(7401.651,8608.670)(7510.000,8549.000)(7442.478,8652.638)(7401.651,8608.670)
\path(6090,7349)(6990,7349)
\blacken\path(6870.000,7319.000)(6990.000,7349.000)(6870.000,7379.000)(6870.000,7319.000)
\path(3383,6177)(2483,6537)(1583,6177)
	(2483,5817)(3383,6177)
\path(3383,6177)(4283,6177)
\blacken\path(4163.000,6147.000)(4283.000,6177.000)(4163.000,6207.000)(4163.000,6147.000)
\path(2483,5719)(5183,5719)(5183,5854)
\blacken\path(5153.000,5839.000)(5183.000,5719.000)(5213.000,5839.000)(5153.000,5839.000)
\path(3383,8892)(2483,9252)(1583,8892)
	(2483,8532)(3383,8892)
\path(2483,9507)(2483,9252)
\blacken\path(2453.000,9372.000)(2483.000,9252.000)(2513.000,9372.000)(2453.000,9372.000)
\path(3388,5276)(2488,5636)(1588,5276)
	(2488,4916)(3388,5276)
\path(2483,5817)(2483,5636)
\blacken\path(2453.000,5756.000)(2483.000,5636.000)(2513.000,5756.000)(2453.000,5756.000)
\path(908,822)(908,686)(2483,686)
\dashline{90.000}(3383,8010)(4290,7349)
\blacken\path(3497.648,7963.569)(3383.000,8010.000)(3462.310,7915.079)(3497.648,7963.569)
\blacken\path(4175.352,7395.431)(4290.000,7349.000)(4210.690,7443.921)(4175.352,7395.431)
\path(6090,7349)(5190,7709)(4290,7349)
	(5190,6989)(6090,7349)
\dashline{90.000}(5624,9006)(5935,9006)
\blacken\path(5815.000,8976.000)(5935.000,9006.000)(5815.000,9036.000)(5815.000,8976.000)
\put(2484,9021){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}loop}}}}}
\put(3390,1813){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}on}}}}}
\put(1575,1828){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}off}}}}}
\put(2477,9915){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}start\_up()}}}}}
\put(6809,9112){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}rdf\_inner()}}}}}
\put(7904,7456){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}dump()}}}}}
\put(2477,8100){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}do\_step()}}}}}
\put(2482,7210){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}values()}}}}}
\put(2492,6132){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}print\_interval}}}}}
\put(2484,5232){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}scale\_interval}}}}}
\put(2492,1648){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}text\_mode\_save}}}}}
\put(5199,7304){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}dump\_interval}}}}}
\put(5192,6305){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}output()}}}}}
\put(5192,5405){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}rescale()}}}}}
\put(5187,4475){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}averages()}}}}}
\put(5172,3575){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}print\_rdf()}}}}}
\put(4052,1205){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}print\_config()}}}}}
\put(2482,195){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}exit()}}}}}
\put(902,1145){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}write\_restart()}}}}}
\put(2483,5426){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}every}}}}}
\put(5198,7481){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}every}}}}}
\put(2483,2711){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}every}}}}}
\put(2483,6301){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}every}}}}}
\put(2483,5951){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}steps}}}}}
\put(2483,4151){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}steps}}}}}
\put(2483,5051){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}steps}}}}}
\put(2468,4511){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}every}}}}}
\put(2483,3606){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}every}}}}}
\put(2484,3435){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}rdf\_out}}}}}
\put(2478,3251){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}steps}}}}}
\put(5213,7136){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}steps}}}}}
\put(5197,3344){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}calc/print RDFs}}}}}
\put(5182,5149){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}scale velocities}}}}}
\put(907,963){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}write save file}}}}}
\put(5182,4287){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}calc/print averages}}}}}
\put(6849,8900){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}link-cell RDF calc}}}}}
\put(7898,7236){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}write trajectories}}}}}
\put(4072,996){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}write text restart}}}}}
\put(5190,2429){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}write backup file}}}}}
\put(2477,7026){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}log thermo. values}}}}}
\put(2483,7906){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}Step co-ords by \textit{dt}}}}}}
\put(2484,9713){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}input/initialization}}}}}
\put(5184,6089){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}periodic write}}}}}
\put(2482,10527){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}main()}}}}}
\put(2484,8676){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}istep}}}}}
\put(2484,8842){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}over timesteps}}}}}
\put(4764,9037){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}force\_calc()}}}}}
\put(7894,8112){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}bin pair distances}}}}}
\put(7879,8309){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}rdf\_accum()}}}}}
\put(2484,4325){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}average\_interval}}}}}
\put(2483,2351){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\rmdefault}{\mddefault}{\updefault}steps}}}}}
\put(2484,2510){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}backup\_interval}}}}}
\put(5192,2593){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}write\_restart()}}}}}
\end{picture}
}
$EOD
$!
$CREATE fig_skewstart-eepic.ftx
$DECK
\setlength{\unitlength}{0.00057743in}
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
{\renewcommand{\dashlinestretch}{30}
\begin{picture}(10761,5528)(0,-10)
\put(4322,4194){\blacken\ellipse{174}{174}}
\put(4322,4194){\ellipse{174}{174}}
\put(4474,4194){\blacken\ellipse{174}{174}}
\put(4474,4194){\ellipse{174}{174}}
\put(966,3497){\blacken\ellipse{174}{174}}
\put(966,3497){\ellipse{174}{174}}
\put(1119,3497){\blacken\ellipse{174}{174}}
\put(1119,3497){\ellipse{174}{174}}
\put(1816,3672){\blacken\ellipse{174}{174}}
\put(1816,3672){\ellipse{174}{174}}
\put(1990,3672){\blacken\ellipse{174}{174}}
\put(1990,3672){\ellipse{174}{174}}
\put(5171,4325){\blacken\ellipse{174}{174}}
\put(5171,4325){\ellipse{174}{174}}
\put(5324,4325){\blacken\ellipse{174}{174}}
\put(5324,4325){\ellipse{174}{174}}
\put(5607,4499){\blacken\ellipse{174}{174}}
\put(5607,4499){\ellipse{174}{174}}
\put(5607,4325){\blacken\ellipse{174}{174}}
\put(5607,4325){\ellipse{174}{174}}
\put(6021,4499){\blacken\ellipse{174}{174}}
\put(6021,4499){\ellipse{174}{174}}
\put(6195,4499){\blacken\ellipse{174}{174}}
\put(6195,4499){\ellipse{174}{174}}
\put(6522,4695){\blacken\ellipse{174}{174}}
\put(6522,4695){\ellipse{174}{174}}
\put(6522,4521){\blacken\ellipse{174}{174}}
\put(6522,4521){\ellipse{174}{174}}
\put(6827,4695){\blacken\ellipse{174}{174}}
\put(6827,4695){\ellipse{174}{174}}
\put(6980,4695){\blacken\ellipse{174}{174}}
\put(6980,4695){\ellipse{174}{174}}
\put(7677,4870){\blacken\ellipse{174}{174}}
\put(7677,4870){\ellipse{174}{174}}
\put(7829,4870){\blacken\ellipse{174}{174}}
\put(7829,4870){\ellipse{174}{174}}
\put(8527,5044){\blacken\ellipse{174}{174}}
\put(8527,5044){\ellipse{174}{174}}
\put(8679,5044){\blacken\ellipse{174}{174}}
\put(8679,5044){\ellipse{174}{174}}
\put(9355,5218){\blacken\ellipse{174}{174}}
\put(9355,5218){\ellipse{174}{174}}
\put(9507,5218){\blacken\ellipse{174}{174}}
\put(9507,5218){\ellipse{174}{174}}
\put(95,3301){\blacken\ellipse{174}{174}}
\put(95,3301){\ellipse{174}{174}}
\put(247,3301){\blacken\ellipse{174}{174}}
\put(247,3301){\ellipse{174}{174}}
\put(596,3475){\blacken\ellipse{174}{174}}
\put(596,3475){\ellipse{174}{174}}
\put(596,3301){\blacken\ellipse{174}{174}}
\put(596,3301){\ellipse{174}{174}}
\put(1424,3628){\blacken\ellipse{174}{174}}
\put(1424,3628){\ellipse{174}{174}}
\put(1424,3475){\blacken\ellipse{174}{174}}
\put(1424,3475){\ellipse{174}{174}}
\put(2295,3824){\blacken\ellipse{174}{174}}
\put(2295,3824){\ellipse{174}{174}}
\put(2295,3650){\blacken\ellipse{174}{174}}
\put(2295,3650){\ellipse{174}{174}}
\put(2644,3824){\blacken\ellipse{174}{174}}
\put(2644,3824){\ellipse{174}{174}}
\put(2796,3824){\blacken\ellipse{174}{174}}
\put(2796,3824){\ellipse{174}{174}}
\put(3101,3977){\blacken\ellipse{174}{174}}
\put(3101,3977){\ellipse{174}{174}}
\put(3101,3824){\blacken\ellipse{174}{174}}
\put(3101,3824){\ellipse{174}{174}}
\put(3472,3998){\blacken\ellipse{174}{174}}
\put(3472,3998){\ellipse{174}{174}}
\put(3624,3998){\blacken\ellipse{174}{174}}
\put(3624,3998){\ellipse{174}{174}}
\put(3908,4151){\blacken\ellipse{174}{174}}
\put(3908,4151){\ellipse{174}{174}}
\put(3908,3998){\blacken\ellipse{174}{174}}
\put(3908,3998){\ellipse{174}{174}}
\put(7307,4870){\blacken\ellipse{174}{174}}
\put(7307,4870){\ellipse{174}{174}}
\put(7307,4695){\blacken\ellipse{174}{174}}
\put(7307,4695){\ellipse{174}{174}}
\put(8134,5022){\blacken\ellipse{174}{174}}
\put(8134,5022){\ellipse{174}{174}}
\put(8134,4870){\blacken\ellipse{174}{174}}
\put(8134,4870){\ellipse{174}{174}}
\put(8984,5196){\blacken\ellipse{174}{174}}
\put(8984,5196){\ellipse{174}{174}}
\put(8984,5044){\blacken\ellipse{174}{174}}
\put(8984,5044){\ellipse{174}{174}}
\put(9812,5371){\blacken\ellipse{174}{174}}
\put(9812,5371){\ellipse{174}{174}}
\put(9812,5218){\blacken\ellipse{174}{174}}
\put(9812,5218){\ellipse{174}{174}}
\put(10226,5371){\blacken\ellipse{174}{174}}
\put(10226,5371){\ellipse{174}{174}}
\put(10379,5371){\blacken\ellipse{174}{174}}
\put(10379,5371){\ellipse{174}{174}}
\put(4322,2364){\blacken\ellipse{174}{174}}
\put(4322,2364){\ellipse{174}{174}}
\put(4474,2364){\blacken\ellipse{174}{174}}
\put(4474,2364){\ellipse{174}{174}}
\put(4779,2517){\blacken\ellipse{174}{174}}
\put(4779,2517){\ellipse{174}{174}}
\put(4779,2364){\blacken\ellipse{174}{174}}
\put(4779,2364){\ellipse{174}{174}}
\put(5171,2517){\blacken\ellipse{174}{174}}
\put(5171,2517){\ellipse{174}{174}}
\put(5324,2517){\blacken\ellipse{174}{174}}
\put(5324,2517){\ellipse{174}{174}}
\put(5607,2669){\blacken\ellipse{174}{174}}
\put(5607,2669){\ellipse{174}{174}}
\put(5607,2517){\blacken\ellipse{174}{174}}
\put(5607,2517){\ellipse{174}{174}}
\put(6021,2691){\blacken\ellipse{174}{174}}
\put(6021,2691){\ellipse{174}{174}}
\put(6195,2691){\blacken\ellipse{174}{174}}
\put(6195,2691){\ellipse{174}{174}}
\put(6152,2364){\blacken\ellipse{174}{174}}
\put(6152,2364){\ellipse{174}{174}}
\put(6152,2190){\blacken\ellipse{174}{174}}
\put(6152,2190){\ellipse{174}{174}}
\put(5672,2190){\blacken\ellipse{174}{174}}
\put(5672,2190){\ellipse{174}{174}}
\put(5825,2190){\blacken\ellipse{174}{174}}
\put(5825,2190){\ellipse{174}{174}}
\put(5237,2168){\blacken\ellipse{174}{174}}
\put(5237,2168){\ellipse{174}{174}}
\put(5237,2016){\blacken\ellipse{174}{174}}
\put(5237,2016){\ellipse{174}{174}}
\put(4801,2016){\blacken\ellipse{174}{174}}
\put(4801,2016){\ellipse{174}{174}}
\put(4953,2016){\blacken\ellipse{174}{174}}
\put(4953,2016){\ellipse{174}{174}}
\put(4409,1188){\blacken\ellipse{174}{174}}
\put(4409,1188){\ellipse{174}{174}}
\put(4409,1014){\blacken\ellipse{174}{174}}
\put(4409,1014){\ellipse{174}{174}}
\put(4801,1188){\blacken\ellipse{174}{174}}
\put(4801,1188){\ellipse{174}{174}}
\put(4953,1188){\blacken\ellipse{174}{174}}
\put(4953,1188){\ellipse{174}{174}}
\put(5237,1341){\blacken\ellipse{174}{174}}
\put(5237,1341){\ellipse{174}{174}}
\put(5237,1188){\blacken\ellipse{174}{174}}
\put(5237,1188){\ellipse{174}{174}}
\put(5672,1362){\blacken\ellipse{174}{174}}
\put(5672,1362){\ellipse{174}{174}}
\put(5825,1362){\blacken\ellipse{174}{174}}
\put(5825,1362){\ellipse{174}{174}}
\put(6152,1537){\blacken\ellipse{174}{174}}
\put(6152,1537){\ellipse{174}{174}}
\put(6152,1362){\blacken\ellipse{174}{174}}
\put(6152,1362){\ellipse{174}{174}}
\put(4779,818){\blacken\ellipse{174}{174}}
\put(4779,818){\ellipse{174}{174}}
\put(4779,665){\blacken\ellipse{174}{174}}
\put(4779,665){\ellipse{174}{174}}
\put(5171,818){\blacken\ellipse{174}{174}}
\put(5171,818){\ellipse{174}{174}}
\put(5324,818){\blacken\ellipse{174}{174}}
\put(5324,818){\ellipse{174}{174}}
\put(5607,992){\blacken\ellipse{174}{174}}
\put(5607,992){\ellipse{174}{174}}
\put(5607,818){\blacken\ellipse{174}{174}}
\put(5607,818){\ellipse{174}{174}}
\put(6021,992){\blacken\ellipse{174}{174}}
\put(6021,992){\ellipse{174}{174}}
\put(6195,992){\blacken\ellipse{174}{174}}
\put(6195,992){\ellipse{174}{174}}
\put(4322,665){\blacken\ellipse{174}{174}}
\put(4322,665){\ellipse{174}{174}}
\put(4474,665){\blacken\ellipse{174}{174}}
\put(4474,665){\ellipse{174}{174}}
\put(4409,2016){\blacken\ellipse{174}{174}}
\put(4409,2016){\ellipse{174}{174}}
\put(4409,1842){\blacken\ellipse{174}{174}}
\put(4409,1842){\ellipse{174}{174}}
\put(4322,1515){\blacken\ellipse{174}{174}}
\put(4322,1515){\ellipse{174}{174}}
\put(4474,1515){\blacken\ellipse{174}{174}}
\put(4474,1515){\ellipse{174}{174}}
\put(4779,1667){\blacken\ellipse{174}{174}}
\put(4779,1667){\ellipse{174}{174}}
\put(4779,1515){\blacken\ellipse{174}{174}}
\put(4779,1515){\ellipse{174}{174}}
\put(5171,1667){\blacken\ellipse{174}{174}}
\put(5171,1667){\ellipse{174}{174}}
\put(5324,1667){\blacken\ellipse{174}{174}}
\put(5324,1667){\ellipse{174}{174}}
\put(6021,1842){\blacken\ellipse{174}{174}}
\put(6021,1842){\ellipse{174}{174}}
\put(6195,1842){\blacken\ellipse{174}{174}}
\put(6195,1842){\ellipse{174}{174}}
\put(5607,1820){\blacken\ellipse{174}{174}}
\put(5607,1820){\ellipse{174}{174}}
\put(5607,1667){\blacken\ellipse{174}{174}}
\put(5607,1667){\ellipse{174}{174}}
\put(4779,4325){\blacken\ellipse{174}{174}}
\put(4779,4325){\ellipse{174}{174}}
\put(4779,4173){\blacken\ellipse{174}{174}}
\put(4779,4173){\ellipse{174}{174}}
\path(2295,5480)(2295,3301)
\path(4409,5480)(4409,3301)
\path(6522,5480)(6522,3301)
\path(8636,5480)(8636,3301)
\drawline(4409,1101)(4409,1101)
\dottedline{22}(4409,665)(6522,1101)
\dottedline{22}(4409,1101)(6522,1515)
\dottedline{22}(4409,1515)(6522,1929)
\path(6522,2364)(7241,2517)
\path(6522,1929)(7328,2103)
\path(4779,752)(4888,12)
\dottedline{22}(4409,1929)(6522,2364)
\path(10749,3301)(10749,5480)(182,5480)
	(182,3301)(10749,3301)
\dottedline{22}(182,3301)(10749,5501)
\path(5258,861)(5367,121)
\path(5455,273)(5346,339)(5411,469)
\path(4714,339)(4844,251)(4757,99)
\path(6958,2582)(6871,2452)(6762,2517)
\path(6849,1885)(6936,2016)(7067,1929)
\dottedline{22}(4409,2364)(6522,2778)
\path(6522,665)(6522,2778)(4409,2778)
	(4409,665)(6522,665)
\put(6849,2168){\makebox(0,0)[lb]{\smash{{{\SetFigFont{14}{16.8}{\rmdefault}{\mddefault}{\updefault}d}}}}}
\put(5019,251){\makebox(0,0)[lb]{\smash{{{\SetFigFont{14}{16.8}{\rmdefault}{\mddefault}{\updefault}a}}}}}
\end{picture}
}
$EOD
$!
$CREATE fig_startup-a-eepic.ftx
$DECK
\setlength{\unitlength}{0.00057743in}
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
{\renewcommand{\dashlinestretch}{30}
\begin{picture}(6096,10389)(0,-10)
\path(573,10362)(1248,10362)
\path(573,9912)(1248,9912)
\spline(573,10362)
(123,10362)(123,9912)(573,9912)
\spline(1248,10362)
(1698,10362)(1698,9912)(1248,9912)
\path(2718,4847)(3393,4847)
\path(2718,4397)(3393,4397)
\spline(2718,4847)
(2268,4847)(2268,4397)(2718,4397)
\spline(3393,4847)
(3843,4847)(3843,4397)(3393,4397)
\put(3389,8448){\ellipse{1800}{676}}
\put(3394,7549){\ellipse{1800}{676}}
\put(3394,6649){\ellipse{1800}{676}}
\put(3394,5744){\ellipse{1800}{676}}
\path(3394,8113)(3394,7887)
\blacken\path(3364.000,8007.000)(3394.000,7887.000)(3424.000,8007.000)(3364.000,8007.000)
\path(3394,7213)(3394,6987)
\blacken\path(3364.000,7107.000)(3394.000,6987.000)(3424.000,7107.000)(3364.000,7107.000)
\path(3394,6313)(3394,6087)
\blacken\path(3364.000,6207.000)(3394.000,6087.000)(3424.000,6207.000)(3364.000,6207.000)
\path(4873,462)(5548,462)
\path(4873,12)(5548,12)
\spline(4873,462)
(4423,462)(4423,12)(4873,12)
\spline(5548,462)
(5998,462)(5998,12)(5548,12)
\put(913,7997){\ellipse{1800}{676}}
\put(3388,1477){\ellipse{670}{670}}
\put(913,372){\ellipse{670}{670}}
\put(3388,2597){\ellipse{670}{670}}
\put(913,6872){\ellipse{1800}{676}}
\put(908,9122){\ellipse{1800}{676}}
\put(5188,2372){\ellipse{670}{670}}
\put(5188,1247){\ellipse{1800}{676}}
\path(913,9912)(913,9462)
\blacken\path(883.000,9582.000)(913.000,9462.000)(943.000,9582.000)(883.000,9582.000)
\path(913,8787)(913,8337)
\blacken\path(883.000,8457.000)(913.000,8337.000)(943.000,8457.000)(883.000,8457.000)
\path(913,7662)(913,7212)
\blacken\path(883.000,7332.000)(913.000,7212.000)(943.000,7332.000)(883.000,7332.000)
\path(1813,2607)(913,2967)(13,2607)
	(913,2247)(1813,2607)
\path(1813,1477)(913,1837)(13,1477)
	(913,1117)(1813,1477)
\path(918,3382)(918,2967)
\blacken\path(888.000,3087.000)(918.000,2967.000)(948.000,3087.000)(888.000,3087.000)
\path(913,2247)(913,1827)
\blacken\path(883.000,1947.000)(913.000,1827.000)(943.000,1947.000)(883.000,1947.000)
\path(913,1117)(913,717)
\blacken\path(883.000,837.000)(913.000,717.000)(943.000,837.000)(883.000,837.000)
\path(1823,5742)(2263,5742)(2263,9012)
	(3388,9012)(3388,8787)
\blacken\path(3358.000,8907.000)(3388.000,8787.000)(3418.000,8907.000)(3358.000,8907.000)
\path(1813,4622)(2268,4622)
\blacken\path(2148.000,4592.000)(2268.000,4622.000)(2148.000,4652.000)(2148.000,4592.000)
\path(1808,2602)(3053,2602)
\blacken\path(2933.000,2572.000)(3053.000,2602.000)(2933.000,2632.000)(2933.000,2572.000)
\path(1818,1472)(3053,1472)
\blacken\path(2933.000,1442.000)(3053.000,1472.000)(2933.000,1502.000)(2933.000,1442.000)
\path(1818,5742)(918,6102)(18,5742)
	(918,5382)(1818,5742)
\path(918,6537)(918,6102)
\blacken\path(888.000,6222.000)(918.000,6102.000)(948.000,6222.000)(888.000,6222.000)
\path(1813,4627)(913,4987)(13,4627)
	(913,4267)(1813,4627)
\path(913,5382)(913,4987)
\blacken\path(883.000,5107.000)(913.000,4987.000)(943.000,5107.000)(883.000,5107.000)
\path(3388,5417)(3388,5187)(913,5187)
\blacken\path(1033.000,5217.000)(913.000,5187.000)(1033.000,5157.000)(1033.000,5217.000)
\path(123,3837)(1698,3837)(1698,3387)
	(123,3387)(123,3837)
\path(913,4262)(913,3837)
\blacken\path(883.000,3957.000)(913.000,3837.000)(943.000,3957.000)(883.000,3957.000)
\path(5188,2037)(5188,1582)
\blacken\path(5158.000,1702.000)(5188.000,1582.000)(5218.000,1702.000)(5158.000,1702.000)
\path(5188,912)(5188,462)
\blacken\path(5158.000,582.000)(5188.000,462.000)(5218.000,582.000)(5158.000,582.000)
\put(913,5702){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}restart?}}}}}
\put(1808,5862){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(693,5062){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(913,4402){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}exist?}}}}}
\put(1808,4757){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(3053,4572){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Abort}}}}}
\put(693,4112){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(908,2667){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}backup}}}}}
\put(908,2487){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}file exists?}}}}}
\put(1813,2722){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(1808,1592){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(1143,2037){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(1138,912){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(918,302){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}A}}}}}
\put(3388,1387){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}B}}}}}
\put(3388,2512){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}C}}}}}
\put(908,10084){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}start\_up()}}}}}
\put(178,3662){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Create backup \&}}}}}
\put(178,3482){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}dump lock files}}}}}
\put(3418,8315){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}get saved params}}}}}
\put(3418,6530){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}get new params}}}}}
\put(3395,7401){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}to "input" units}}}}}
\put(3380,5585){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}to prog units}}}}}
\put(913,4762){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault} dump}}}}}
\put(913,4582){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}/backup lockfiles}}}}}
\put(3388,8518){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}re\_re\_header()}}}}}
\put(3388,7625){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}conv\_control()}}}}}
\put(3393,6720){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}read\_control()}}}}}
\put(3388,5824){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}conv\_control()}}}}}
\put(918,9116){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}default\_control()}}}}}
\put(913,8007){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}read\_control()}}}}}
\put(908,6863){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}conv\_control()}}}}}
\put(5188,2282){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}D}}}}}
\put(5193,1310){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}banner\_page()}}}}}
\put(5188,1103){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}write output file}}}}}
\put(5210,219){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}return}}}}}
\put(913,1439){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}restart?}}}}}
\end{picture}
}
$EOD
$!
$CREATE fig_startup-b-eepic.ftx
$DECK
\setlength{\unitlength}{0.00057743in}
%
\begingroup\makeatletter\ifx\SetFigFont\undefined%
\gdef\SetFigFont#1#2#3#4#5{%
  \reset@font\fontsize{#1}{#2pt}%
  \fontfamily{#3}\fontseries{#4}\fontshape{#5}%
  \selectfont}%
\fi\endgroup%
{\renewcommand{\dashlinestretch}{30}
\begin{picture}(10612,10844)(0,-10)
\put(2034,3957){\ellipse{1800}{676}}
\put(2039,3058){\ellipse{1800}{676}}
\put(2039,2158){\ellipse{1800}{676}}
\put(2039,1253){\ellipse{1800}{676}}
\path(2039,3622)(2039,3396)
\blacken\path(2009.000,3516.000)(2039.000,3396.000)(2069.000,3516.000)(2009.000,3516.000)
\path(2039,2722)(2039,2496)
\blacken\path(2009.000,2616.000)(2039.000,2496.000)(2069.000,2616.000)(2009.000,2616.000)
\path(2039,1822)(2039,1596)
\blacken\path(2009.000,1716.000)(2039.000,1596.000)(2069.000,1716.000)(2009.000,1716.000)
\put(2034,9582){\ellipse{1800}{676}}
\put(2039,8683){\ellipse{1800}{676}}
\put(2039,7783){\ellipse{1800}{676}}
\put(2039,6878){\ellipse{1800}{676}}
\path(2039,9247)(2039,9021)
\blacken\path(2009.000,9141.000)(2039.000,9021.000)(2069.000,9141.000)(2009.000,9141.000)
\path(2039,8347)(2039,8121)
\blacken\path(2009.000,8241.000)(2039.000,8121.000)(2069.000,8241.000)(2009.000,8241.000)
\path(2039,7447)(2039,7221)
\blacken\path(2009.000,7341.000)(2039.000,7221.000)(2069.000,7341.000)(2009.000,7341.000)
\put(9699,9357){\ellipse{1800}{676}}
\put(9704,8458){\ellipse{1800}{676}}
\put(9704,7558){\ellipse{1800}{676}}
\put(9704,6653){\ellipse{1800}{676}}
\path(9704,9022)(9704,8796)
\blacken\path(9674.000,8916.000)(9704.000,8796.000)(9734.000,8916.000)(9674.000,8916.000)
\path(9704,8122)(9704,7896)
\blacken\path(9674.000,8016.000)(9704.000,7896.000)(9734.000,8016.000)(9674.000,8016.000)
\path(9704,7222)(9704,6996)
\blacken\path(9674.000,7116.000)(9704.000,6996.000)(9734.000,7116.000)(9674.000,7116.000)
\put(9698,2841){\ellipse{670}{670}}
\path(9698,3621)(9698,3171)
\blacken\path(9668.000,3291.000)(9698.000,3171.000)(9728.000,3291.000)(9668.000,3291.000)
\put(9698,10486){\ellipse{670}{670}}
\put(9699,5757){\ellipse{1800}{676}}
\put(9704,4858){\ellipse{1800}{676}}
\put(9704,3958){\ellipse{1800}{676}}
\path(9698,10146)(9698,9696)
\blacken\path(9668.000,9816.000)(9698.000,9696.000)(9728.000,9816.000)(9668.000,9816.000)
\path(9704,5422)(9704,5196)
\blacken\path(9674.000,5316.000)(9704.000,5196.000)(9734.000,5316.000)(9674.000,5316.000)
\path(9704,4522)(9704,4296)
\blacken\path(9674.000,4416.000)(9704.000,4296.000)(9734.000,4416.000)(9674.000,4416.000)
\path(9704,6322)(9704,6096)
\blacken\path(9674.000,6216.000)(9704.000,6096.000)(9734.000,6216.000)(9674.000,6216.000)
\put(2040,6998){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}allocate\_}}}}}
\put(2040,6833){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}dynamics()}}}}}
\put(2033,7854){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}initialise\_}}}}}
\put(2033,7689){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}sysdef()}}}}}
\put(9691,7664){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}allocate\_}}}}}
\put(9691,7499){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}dynamics()}}}}}
\put(5183,6079){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}allocate\_}}}}}
\put(5183,5914){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}dynamics()}}}}}
\put(7476,6977){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}initialise\_}}}}}
\put(7476,6812){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}sysdef()}}}}}
\put(5183,10481){\ellipse{670}{670}}
\put(2029,356){\ellipse{670}{670}}
\put(908,5084){\ellipse{1800}{676}}
\put(3156,5084){\ellipse{1800}{676}}
\put(2033,10476){\ellipse{670}{670}}
\put(5183,7541){\ellipse{1800}{676}}
\put(5184,5964){\ellipse{1800}{676}}
\put(5189,5065){\ellipse{1800}{676}}
\put(5189,4165){\ellipse{1800}{676}}
\put(5189,3260){\ellipse{1800}{676}}
\put(5183,343){\ellipse{670}{670}}
\put(7428,1453){\ellipse{1800}{676}}
\put(7434,8665){\ellipse{1800}{676}}
\put(7439,7766){\ellipse{1800}{676}}
\put(7439,5961){\ellipse{1800}{676}}
\put(7439,6866){\ellipse{1800}{676}}
\put(5189,2366){\ellipse{1800}{676}}
\path(4283,9921)(6083,9921)(6083,9246)
	(4283,9246)(4283,9921)
\path(5183,10146)(5183,9921)
\blacken\path(5153.000,10041.000)(5183.000,9921.000)(5213.000,10041.000)(5153.000,10041.000)
\path(5183,9246)(5183,9021)
\blacken\path(5153.000,9141.000)(5183.000,9021.000)(5213.000,9141.000)(5153.000,9141.000)
\path(2033,10146)(2033,9921)
\blacken\path(2003.000,10041.000)(2033.000,9921.000)(2063.000,10041.000)(2003.000,10041.000)
\path(2033,6546)(2033,6326)
\blacken\path(2003.000,6446.000)(2033.000,6326.000)(2063.000,6446.000)(2003.000,6446.000)
\path(1133,5961)(908,5961)(908,5418)
\blacken\path(878.000,5538.000)(908.000,5418.000)(938.000,5538.000)(878.000,5538.000)
\path(2933,5961)(3158,5961)(3158,5416)
\blacken\path(3128.000,5536.000)(3158.000,5416.000)(3188.000,5536.000)(3128.000,5536.000)
\path(908,4746)(908,4521)(2033,4521)
\blacken\path(1913.000,4491.000)(2033.000,4521.000)(1913.000,4551.000)(1913.000,4491.000)
\path(3158,4746)(3158,4521)(2033,4521)
\blacken\path(2153.000,4551.000)(2033.000,4521.000)(2153.000,4491.000)(2153.000,4551.000)
\path(2033,4521)(2033,4296)
\blacken\path(2003.000,4416.000)(2033.000,4296.000)(2063.000,4416.000)(2003.000,4416.000)
\path(2038,911)(2038,686)
\blacken\path(2008.000,806.000)(2038.000,686.000)(2068.000,806.000)(2008.000,806.000)
\path(5183,8306)(5183,7881)
\blacken\path(5153.000,8001.000)(5183.000,7881.000)(5213.000,8001.000)(5153.000,8001.000)
\path(6083,8661)(5183,9021)(4283,8661)
	(5183,8301)(6083,8661)
\path(5183,7206)(5183,6298)
\blacken\path(5153.000,6418.000)(5183.000,6298.000)(5213.000,6418.000)(5153.000,6418.000)
\path(5189,5629)(5189,5403)
\blacken\path(5159.000,5523.000)(5189.000,5403.000)(5219.000,5523.000)(5159.000,5523.000)
\path(5189,4729)(5189,4503)
\blacken\path(5159.000,4623.000)(5189.000,4503.000)(5219.000,4623.000)(5159.000,4623.000)
\path(5189,3829)(5189,3603)
\blacken\path(5159.000,3723.000)(5189.000,3603.000)(5219.000,3723.000)(5159.000,3723.000)
\path(5189,2928)(5189,2702)
\blacken\path(5159.000,2822.000)(5189.000,2702.000)(5219.000,2822.000)(5159.000,2822.000)
\path(6083,1460)(5183,1820)(4283,1460)
	(5183,1100)(6083,1460)
\path(6083,1458)(6533,1458)
\blacken\path(6413.000,1428.000)(6533.000,1458.000)(6413.000,1488.000)(6413.000,1428.000)
\path(7438,1113)(7438,898)(5183,898)
\blacken\path(5303.000,928.000)(5183.000,898.000)(5303.000,868.000)(5303.000,928.000)
\path(5183,1098)(5183,685)
\blacken\path(5153.000,805.000)(5183.000,685.000)(5213.000,805.000)(5153.000,805.000)
\path(5183,2031)(5183,1819)
\blacken\path(5153.000,1939.000)(5183.000,1819.000)(5213.000,1939.000)(5153.000,1939.000)
\path(7439,8330)(7439,8104)
\blacken\path(7409.000,8224.000)(7439.000,8104.000)(7469.000,8224.000)(7409.000,8224.000)
\path(7439,7430)(7439,7204)
\blacken\path(7409.000,7324.000)(7439.000,7204.000)(7469.000,7324.000)(7409.000,7324.000)
\path(7439,6530)(7439,6304)
\blacken\path(7409.000,6424.000)(7439.000,6304.000)(7469.000,6424.000)(7409.000,6424.000)
\path(7433,5626)(7433,5401)(6308,5406)
	(6308,6756)(5183,6756)
\blacken\path(5303.000,6786.000)(5183.000,6756.000)(5303.000,6726.000)(5303.000,6786.000)
\path(6089,8662)(6539,8662)
\blacken\path(6419.000,8632.000)(6539.000,8662.000)(6419.000,8692.000)(6419.000,8632.000)
\path(2933,5966)(2033,6326)(1133,5966)
	(2033,5606)(2933,5966)
\put(2048,10371){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}A}}}}}
\put(5183,10356){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}B}}}}}
\put(9713,10371){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}C}}}}}
\put(2033,239){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}D}}}}}
\put(9683,2714){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}D}}}}}
\put(2033,9638){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}read\_sysdef()}}}}}
\put(2033,5926){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}lattice start?}}}}}
\put(2925,6098){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(1133,6091){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(9683,6696){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}init\_averages()}}}}}
\put(2025,8663){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}conv\_potentials()}}}}}
\put(2033,3081){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}init\_averages()}}}}}
\put(2025,3975){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}thermalise()}}}}}
\put(3150,5114){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}lattice\_start()}}}}}
\put(900,5114){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}skew\_start()}}}}}
\put(2033,2182){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}init\_rdf()}}}}}
\put(2040,1275){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}init\_cutoffs()}}}}}
\put(9673,9390){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}re\_re\_header()}}}}}
\put(9688,8482){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}re\_re\_sysdef()}}}}}
\put(9653,5782){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}init\_rdf()}}}}}
\put(9683,4881){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}read\_restart()}}}}}
\put(4388,9713){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Adjust timestep-}}}}}
\put(4388,9353){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}parameters}}}}}
\put(4388,9533){\makebox(0,0)[lb]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}related control}}}}}
\put(2033,9420){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}from sys-spec file}}}}}
\put(9686,8280){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}from backup}}}}}
\put(9686,4691){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}from backup}}}}}
\put(9705,3916){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}convert\_averages()}}}}}
\put(9686,9187){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault} from backup}}}}}
\put(5168,239){\makebox(0,0)[b]{\smash{{{\SetFigFont{11}{13.2}{\rmdefault}{\bfdefault}{\updefault}D}}}}}
\put(5183,8731){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}replace}}}}}
\put(5183,8551){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}system spec?}}}}}
\put(6083,8776){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(5415,8108){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(6083,1589){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}Y}}}}}
\put(4965,899){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}N}}}}}
\put(7431,1529){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}interpolate\_}}}}}
\put(7431,1364){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}derivatives()}}}}}
\put(5183,7576){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}re\_re\_sysdef()}}}}}
\put(5228,5099){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}init\_averages()}}}}}
\put(5183,4193){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}init\_rdf()}}}}}
\put(5190,3300){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}read\_restart()}}}}}
\put(7431,8705){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}read\_sysdef()}}}}}
\put(7438,5997){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}check\_sysdef()}}}}}
\put(7432,8490){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}from sys-spec file}}}}}
\put(5182,7365){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}from restart file}}}}}
\put(7439,5770){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}abort if failed}}}}}
\put(5182,3100){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}dyamic vars etc.}}}}}
\put(7438,7722){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}conv\_potentials()}}}}}
\put(5198,1492){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}timestep}}}}}
\put(5198,1312){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\familydefault}{\mddefault}{\updefault}changed?}}}}}
\put(5190,2316){\makebox(0,0)[b]{\smash{{{\SetFigFont{8}{9.6}{\ttdefault}{\mddefault}{\updefault}convert\_averages()}}}}}
\end{picture}
}
$EOD
$!
$CREATE moldy.sty
$DECK
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%% Customize title page %%%%
\let\@title\@empty
\def\version#1{\gdef\@version{#1}}

\renewcommand\maketitle{\begin{titlepage}%
  \let\footnotesize\small
  \let\footnoterule\relax
  \let \footnote \thanks
  \null\vfil
  \vskip 60\p@
  \begin{center}%
    {\Huge \@title \par}%
    \vskip 1em%
    {\large \@version \par}%
    \vskip 3em%
    {\large
     \lineskip .75em%
      \begin{tabular}[t]{c}%
        \@author
      \end{tabular}\par}%
      \vskip 1.5em%
    {\large \@date \par}%       % Set date in \large size.
  \end{center}\par
  \@thanks
  \vfil\null
  \end{titlepage}%
  \setcounter{footnote}{0}%
  \global\let\thanks\relax
  \global\let\maketitle\relax
  \global\let\@thanks\@empty
  \global\let\@author\@empty
  \global\let\@date\@empty
  \global\let\@title\@empty
  \global\let\title\relax
  \global\let\author\relax
  \global\let\date\relax
  \global\let\and\relax
}


\renewcommand{\l@figure}{\@dottedtocline{1}{1.5em}{3em}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%% Font selection macros to parameterize document %%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Extra emphasis - use bold.
%
\newcommand{\Emph}[1]{\textbf{#1}}
%
% Set font for filename display - sans-serif is nice.
%
\newcommand{\Fname}[1]{{\upshape\mdseries\sffamily{}#1}}

\newenvironment{Fndescription}{\begingroup%
     \renewcommand{\descriptionlabel}[1]{\Fname{##1}}\begin{description}}%
     {\end{description}\endgroup}

%
% Set font for literal text display (typed commands, file contents
%     input/output and code fragments.
%
\newcommand{\Litf}{\ttfamily\upshape\mdseries}

\DeclareTextFontCommand{\Lit}{\Litf}

%% LaTeX2HTML doesn't recognise \DeclareTextFontCommand.  The
%% \providecommand  should define it conditionally for LaTeX2html at
%% the expense of our font.parameterization.
\providecommand{\Lit}[1]{\texttt{#1}}

\newcommand{\ttlabel}[1]{\Lit{#1}\hfil}

\newenvironment{Litdescription}{\begingroup%
     \renewcommand{\descriptionlabel}[1]{\Lit{##1}}\begin{description}}%
     {\end{description}\endgroup}
\newenvironment{Argdescription}{%
      \begin{list}{}{\let\makelabel\ttlabel \itemsep=0pt%
          \parsep=3pt \leftmargin=1.5cm}}%
      {\end{list}}

%
% Calculus infinitesimal - upright ``d''
%
\newcommand{\Calcd}{\mathrm{d}}
%
% Quaternion - bold sans-serif.
%
\newcommand{\Quat}[1]{\mbox{\mathversion{bold}$\mathsf{#1}$}}

$EOD
$!
$CREATE moldy.perl
$DECK

#
# Extension to LaTeX2HTML supply support for the "superfig"
# LaTeX style, used in Moldy.
# Change Log:
# ===========
 
package main;
#
#  Make the floatingfigure environment be translated as
#  an ordinary figure, ignoring the mandatory width.
#
#
 
sub do_env_partfigure {
    local($_) = @_;
 
    $contents =~ s/$optional_arg_rx//o;    # ditch [tbp]
    &process_environment("figure", $global{'max_id'}++);
    }

sub do_env_Argdescription {
    local($_) = @_;
    #RRM - catch nested lists
    $_ = &translate_environments($_);
    $* = 1;                     # Multiline matching ON
    s/$item_description_rx/
$1<\/TT>\n
/g; $* = 0; # Multiline matching OFF &do_env_description($_, " COMPACT"); } sub do_env_Litdescription { local($_) = @_; #RRM - catch nested lists $_ = &translate_environments($_); $* = 1; # Multiline matching ON s/$item_description_rx/
$1<\/TT>\n
/g; $* = 0; # Multiline matching OFF &do_env_description($_, " COMPACT"); } sub do_env_Fndescription { local($_) = @_; #RRM - catch nested lists $_ = &translate_environments($_); $* = 1; # Multiline matching ON s/$item_description_rx/
$1<\/I>\n
/g; $* = 0; # Multiline matching OFF &do_env_description($_, " COMPACT"); } &process_commands_in_tex (<<_RAW_ARG_CMDS_); partfigure # [] _RAW_ARG_CMDS_ &ignore_commands( <<_IGNORED_CMDS_); superfig saferagged _IGNORED_CMDS_ 1; # This must be the last line $EOD $! $CREATE compile_moldy.com $DECK $ write sys$output "Compiling accel" $ CC accel $ write sys$output "Compiling algorith" $ CC algorith $ write sys$output "Compiling alloc" $ CC alloc $ write sys$output "Compiling ansi" $ CC ansi $ write sys$output "Compiling auxil" $ CC auxil $ write sys$output "Compiling beeman" $ CC beeman $ write sys$output "Compiling convert" $ CC convert $ write sys$output "Compiling dump" $ CC dump $ write sys$output "Compiling ewald" $ CC ewald $ write sys$output "Compiling force" $ CC force $ write sys$output "Compiling input" $ CC input $ write sys$output "Compiling eigens" $ CC eigens $ write sys$output "Compiling kernel" $ CC kernel $ write sys$output "Compiling main" $ CC main $ write sys$output "Compiling matrix" $ CC matrix $ write sys$output "Compiling output" $ CC output $ write sys$output "Compiling quaterns" $ CC quaterns $ write sys$output "Compiling rdf" $ CC rdf $ write sys$output "Compiling restart" $ CC restart $ write sys$output "Compiling startup" $ CC startup $ write sys$output "Compiling values" $ CC values $ write sys$output "Compiling xdr" $ CC xdr $ write sys$output "Compiling parallel" $ CC parallel $EOD $! $CREATE link_moldy.com $DECK $ write sys$output "Linking moldy" $ Link/exe=moldy accel,algorith,alloc,ansi,auxil,beeman,convert,dump,ewald,force,input,eigens,kernel,main,matrix,output,quaterns,rdf,restart,startup,values,xdr,parallel $EOD $! $CREATE defcomm.com $DECK $ dir = F$TRNLNM("SYS$DISK")+F$DIRECTORY() $ moldy :== $'dir'moldy $ mdshak :== $'dir'mdshak $ moldyext :== $'dir'moldyext $ dumpanal :== $'dir'dumpanal $ dumpconv :== $'dir'dumpconv $ dumpext :== $'dir'dumpext $ manalyze :== $'dir'manalyze $ msd :== $'dir'msd $ mdavpos :== $'dir'mdavpos $EOD $! $CREATE compile_utils.com $DECK $ write sys$output "Compiling getopt" $ CC getopt $ write sys$output "Compiling ReadDCD" $ CC ReadDCD $ write sys$output "Compiling moldyext" $ CC moldyext $ write sys$output "Compiling dumpanal" $ CC dumpanal $ write sys$output "Compiling dumpconv" $ CC dumpconv $ write sys$output "Compiling dumpext" $ CC dumpext $ write sys$output "Compiling manalyze" $ CC manalyze $ write sys$output "Compiling mdshak" $ CC mdshak $ write sys$output "Compiling msd" $ CC msd $ write sys$output "Compiling mdavpos" $ CC mdavpos $EOD $! $CREATE link_utils.com $DECK $ write sys$output "Linking moldyext" $ Link/exe=moldyext moldyext,getopt $ write sys$output "Linking dumpanal" $ Link/exe=dumpanal dumpanal,getopt $ write sys$output "Linking dumpconv" $ Link/exe=dumpconv dumpconv,getopt $ write sys$output "Linking dumpext" $ Link/exe=dumpext dumpext,getopt $ write sys$output "Linking manalyze" $ Link/exe=manalyze manalyze,getopt $ write sys$output "Linking mdshak" $ Link/exe=mdshak mdshak,getopt,algorith,alloc,auxil,ansi,eigens,kernel,input,matrix,quaterns,restart,startup,values,xdr,ReadDCD $ write sys$output "Linking msd" $ Link/exe=msd msd,getopt,algorith,alloc,auxil,ansi,eigens,kernel,input,matrix,quaterns,restart,startup,values,xdr,ReadDCD $ write sys$output "Linking mdavpos" $ Link/exe=mdavpos mdavpos,getopt,algorith,alloc,auxil,ansi,eigens,kernel,input,matrix,quaterns,restart,startup,values,xdr,ReadDCD $EOD $! $CREATE compile.com $DECK $ write sys$output "===========Building Moldy============" $ @compile_moldy $ @link_moldy $ write sys$output "===========Building utilities============" $ @compile_utils $ @link_utils $ @defcomm $ write sys$output "===========Done============" $EOD $! $CREATE Makefile.mak $DECK SHELL=/bin/sh # # Compilation Options - choose a suitable set for your machine # # # USE_XDR. Uncomment this line to enable XDR support. #XDR=-DUSE_XDR # # Parallel library support. Uncomment as needed, and set paths to # include directories and library paths. # # TCGMSG library #PARLIBC=-DSPMD -DTCGMSG -I/usr/local/include/tcgmsg #PARLIBL=-L/usr/local/lib -ltcgmsg # Oxford BSP library #CC=bspcc -overridecc cc #PARLIBC=-DSPMD -DBSP #PARLIBL=-flibrary-level 2 # MPI library #CC=mpicc #PARLIBC=-DSPMD -DMPI -I/usr/local/include/mpi #PARLIBL=-L/usr/local/lib -lmpi # MPI library on Edinburgh T3D #PARLIBC=-DSPMD -DMPI #PARLIBL=-lmpi # SHMEM library on Edinburgh T3D #PARLIBC=-DSPMD -DSHMEM #-DMPPMANY #PARLIBL= # **** MSDOS with Turbo C **** CC=tcc CFLAGS= -A -DANSI_LIBS -f287 -mh -O -Z -G $(CFLAGS0) GETOPT=getopt.obj LDFLAGS1= -mh $(LDFLAGS0) # **** Scalar machines using default C compiler **** #CFLAGS= -O $(XDR) $(PARLIBC) $(CFLAGS0) LDFLAGS= $(LDFLAGS0) $(PARLIBL) -lm #INLINE= # **** Scalar machines using gcc - can compile with -g **** #CC= gcc #CFLAGS= -O2 -ffast-math $(XDR) $(PARLIBC) $(CFLAGS0) #CFLAGSAUX=-funroll-loops #LDFLAGS= $(PARLIBL) $(LDFLAGS0) -lm #INLINE= # **** gcc under Windows 95/NT using mingw32 port. **** # Options for exe which will run under 486, optimum under pentium pro. # This doesn't have a "getopt" so include getopt.o. Nor a separate # maths library, so no "-lm". #CC= gcc ##XDR= #GETOPT=getopt.obj #CFLAGS= -O2 -ffast-math -mcpu=pentiumpro -march=i486 -malign-double $(XDR) $(PARLIBC) $(CFLAGS0) #CFLAGSAUX=-funroll-loops #LDFLAGS= $(PARLIBL) $(LDFLAGS0) #INLINE= # **** IBM RS6000 **** #CC=cc #CFLAGS= -DRS6000 -qansialias -O3 $(XDR) $(PARLIBC) $(CFLAGS0) #LDFLAGS= $(PARLIBL) $(LDFLAGS0) -lm #INLINE= # **** MIPS for SGI, IRIX 5 **** #CFLAGS= -O2 -mips2 $(XDR) $(PARLIBC) $(CFLAGS0) #LDFLAGS= $(PARLIBL) $(LDFLAGS0) -lsun -lmalloc -lfastm -lm #INLINE= # **** MIPS for SGI, IRIX 6/R8000 **** #CFLAGS= -DANSI_LIBS -O3 -mips4 -woff 1174 $(XDR) $(PARLIBC) $(CFLAGS0) #LDFLAGS= $(PARLIBL) $(LDFLAGS0) -lm #CFLAGS2=-OPT:alias=restrict:fast_sqrt=on #INLINE= # **** Sun C 3.0.1 #CC=acc #CFLAGS= -fast -xO4 $(PARLIBC) $(XDR) $(CFLAGS0) #CFLAGS2= -fsimple #LDFLAGS= $(PARLIBL) $(LDFLAGS0) -fast -lm #INLINE= # **** Sun Solaris 2 #CFLAGS= -fast -xO4 $(PARLIBC) $(XDR) $(CFLAGS0) #CFLAGS2= -fsimple -xrestrict=%all #LDFLAGS= $(PARLIBL) $(LDFLAGS0) -fast -lnsl -lm #INLINE= # **** Solaris 2 using gcc **** #CC= gcc #CFLAGS= -O2 -ffast-math $(PARLIBC) $(XDR) $(CFLAGS0) #CFLAGSAUX=-funroll-loops #LDFLAGS= $(PARLIBL) $(LDFLAGS0) -lnsl -lm #INLINE= # **** DEC Alpha **** #CC=cc -migrate #CFLAGS= -tune host -O4 $(XDR) $(PARLIBC) $(CFLAGS0) -D_FASTMATH #CFLAGS2=-ansi_alias -O5 #LDFLAGS= $(PARLIBL) $(LDFLAGS0) -lm # **** HP 700 and Convex SPP **** #CC=c89 ##CC=mpicc #CFLAGS= +O3 +Olibcalls $(XDR) $(PARLIBC) $(CFLAGS0) #CFLAGS2= +Onoparmsoverlap #LDFLAGS= $(PARLIBL) $(LDFLAGS0) -lm # **** Stellar **** #CFLAGS= -g -O2 -va -na -nv $(XDR) $(CFLAGS0) #CFLAGS2= +CaliasFreePointers #LDFLAGS= $(LDFLAGS0) -g -z -lm #INLINE= # **** Titan **** #CFLAGS= -O2 -vector_c -vreport $(XDR) $(PARLIBC) $(CFLAGS0) #CFLAGS2= -safe=ptrs #LDFLAGS= $(PARLIBL) $(LDFLAGS0) -lm #CFLAGSAUX=-catalog=auxil.in #INLINE= -inline -Npaths=auxil.in # **** convex **** #CFLAGS= -O2 -na -nv -fi $(XDR) $(PARLIBC) $(CFLAGS0) #CFLAGS2= -alias standard -alias ptr_args -alias array_args ##CFLAGSP= -O3 -re # force,ewald_parallel versions only #LDFLAGS= $(PARLIBL) $(LDFLAGS0) -fi -lveclib -lm #INLINE= # **** Cray **** (SCC 3.0) #CFLAGS= -h nostdc,vector3,scalar3,ivdep,fastaddr $(XDR) $(PARLIBC) $(CFLAGS0) #LDFLAGS= $(LDFLAGS0) -Wl,-D,"HEAP=50000+50000;STACK=10000+10000" -lsci -lm #INLINE= # **** Cray T3D/T3E **** #CC=TARGET=cray-t3d cc #CC=TARGET=cray-t3e cc #CFLAGS= -O2 $(XDR) $(PARLIBC) $(CFLAGS0) #CFLAGS2=-hrestrict=a # # The benchmark lib contains fast trig and sqrt functions. # It's not essential but it speeds the program up slightly. #LDFLAGS=$(PARLIBL) $(LDFLAGS0) -L/usr/local/lib/bnchlib -lbnch -lsci -lm # ***** End ***** W32BIN=w32bin PRFLAGS=-c "-DUSE_XDR -DSPMD -D__MSDOS__" LINT= lint TAR= tar DCLAR= dclar ZIP= zip PFLAGS= -P EPFLAGS= -2r RM= rm -f A2FLAGS= SHFILE= $$file FILES = accel algorith alloc ansi auxil beeman convert dump ewald force input \ eigens kernel main matrix output quaterns rdf restart startup \ values xdr parallel CFILES= accel.c algorith.c alloc.c ansi.c auxil.c beeman.c convert.c dump.c ewald.c force.c input.c eigens.c kernel.c main.c matrix.c output.c quaterns.c rdf.c restart.c startup.c values.c xdr.c parallel.c OFILES= accel.o algorith.o alloc.o ansi.o auxil.o beeman.o convert.o dump.o ewald.o force.o input.o eigens.o kernel.o main.o matrix.o output.o quaterns.o rdf.o restart.o startup.o values.o xdr.o parallel.o LFILES= accel.ln algorith.ln alloc.ln ansi.ln auxil.ln beeman.ln convert.ln dump.ln ewald.ln force.ln input.ln eigens.ln kernel.ln main.ln matrix.ln output.ln quaterns.ln rdf.ln restart.ln startup.ln values.ln xdr.ln parallel.ln INFILES = control.water control.tip4p control.mgclh2o control.clay \ control.argon control.tips2 control.quartz \ tips2.in tip4p.in mgclh2o.in \ argon.in quartz-vbst.in methane.in mcy.in \ water-example.out tip4p-example.out \ mgclh2o-example.out clay-example.out \ argon-example.out tips2-example.out \ quartz-example.out SHAKS= algorith alloc auxil ansi eigens kernel input matrix\ quaterns restart startup values xdr ReadDCD SHAKC= algorith.c alloc.c auxil.c ansi.c eigens.c kernel.c input.c matrix.c quaterns.c restart.c startup.c values.c xdr.c ReadDCD.c SHAKOBJS=algorith.o alloc.o auxil.o ansi.o eigens.o kernel.o input.o matrix.o quaterns.o restart.o startup.o values.o xdr.o ReadDCD.o UTILS = moldyext dumpanal dumpconv dumpext manalyze UTILS2= mdshak msd mdavpos EXTRA0= moldyext.c dumpanal.c dumpconv.c dumpext.c manalyze.c mdshak.c msd.c mdavpos.c ReadDCD.c ReadDCD.h ewald_parallel.c force_parallel.c EXTRAS= moldyext.c dumpanal.c dumpconv.c dumpext.c manalyze.c mdshak.c msd.c mdavpos.c ReadDCD.c ReadDCD.h ewald_parallel.c force_parallel.c ewald-RIL.c HFILES= structs.h defs.h string.h time.h stddef.h stdlib.h messages.h xdr.h FIGTEXPS= fig_arralloc.ftx fig_dostep-a.ftx fig_dostep-b.ftx \ fig_ewald.ftx fig_link-cell.ftx fig_main.ftx \ fig_skewstart.ftx fig_startup-a.ftx fig_startup-b.ftx \ fig_arralloc.ps fig_dostep-a.ps fig_dostep-b.ps \ fig_ewald.ps fig_link-cell.ps fig_main.ps \ fig_skewstart.ps fig_startup-a.ps fig_startup-b.ps \ fig_arralloc-eepic.ftx fig_dostep-a-eepic.ftx \ fig_dostep-b-eepic.ftx fig_ewald-eepic.ftx \ fig_link-cell-eepic.ftx fig_main-eepic.ftx \ fig_skewstart-eepic.ftx fig_startup-a-eepic.ftx \ fig_startup-b-eepic.ftx DOC= READ.ME BENCHMARK COPYING RELNOTES moldy.tex moldy.bbl \ $(FIGTEXPS) moldy.sty moldy.perl VMSBUILD= compile_moldy.com link_moldy.com defcomm.com\ compile_utils.com link_utils.com compile.com DOSBUILD= Makefile.mak OFILES.RSP SHAKOBJS.RSP moldy: $(OFILES) $(EXTRA_OBJ) $(HFILES) $(CC) -o moldy $(LDFLAGS1) @OFILES.RSP $(EXTRA_OBJ) $(LDFLAGS) moldyext: moldyext.c $(GETOPT) $(HFILES) $(CC) $(CFLAGS) -o moldyext $(LDFLAGS1) moldyext.c $(GETOPT) $(LDFLAGS) manalyze: manalyze.c $(HFILES) $(CC) $(CFLAGS) -o manalyze $(LDFLAGS1) manalyze.c $(LDFLAGS) dumpanal: dumpanal.c xdr.o $(HFILES) $(CC) $(CFLAGS) -o dumpanal $(LDFLAGS1) dumpanal.c xdr.o $(LDFLAGS) dumpext: dumpext.c xdr.o $(GETOPT) $(HFILES) $(CC) $(CFLAGS) -o dumpext $(LDFLAGS1) dumpext.c xdr.o $(GETOPT) $(LDFLAGS) dumpconv: dumpconv.c xdr.o $(HFILES) $(CC) $(CFLAGS) -o dumpconv $(LDFLAGS1) dumpconv.c xdr.o $(LDFLAGS) mdshak: mdshak.o $(SHAKOBJS) $(GETOPT) $(CC) -o mdshak $(LDFLAGS1) mdshak.o @SHAKOBJS.RSP $(GETOPT) $(LDFLAGS) msd: msd.o $(SHAKOBJS) $(GETOPT) $(CC) -o msd $(LDFLAGS1) msd.o @SHAKOBJS.RSP $(GETOPT) $(LDFLAGS) mdavpos: mdavpos.o $(SHAKOBJS) $(GETOPT) $(CC) -o mdavpos $(LDFLAGS1) mdavpos.o @SHAKOBJS.RSP $(GETOPT) $(LDFLAGS) utilities: $(UTILS) $(UTILS2) $(FIGTEXPS): (set -x;cd figures; \ transfig -L pstex -m 0.66; \ $(MAKE); \ mv *.ps ..;\ for file in *.tex; do mv $$file ../`basename $$file .tex`.ftx; done;\ transfig -L eepic -m 0.66;\ $(MAKE); \ for file in *.tex; do mv $$file ../`basename $$file .tex`-eepic.ftx; done;) protoize: $(CFILES) $(HFILES) protoize -k $(PRFLAGS) $(CFILES) proto-mdshak: mdshak.c $(SHAKC) protoize mdshak.c $(SHAKC) install: moldy $(UTILS) $(UTILS2) for file in $?; do install $$file $$HOME/bin/; done install2: moldy $(UTILS) $(UTILS2) for file in $?; do install $$file $$HOME/bin.$$HOSTTYPE/; done Makefile: xmakefile @PATH=/usr/5bin:${PATH};\ echo '{{{{{{{\n/^CFILES *=/ c\\\nCFILES=' $(CFILES)\ '\n}\n/^OFILES *=/ c\\\nOFILES=' @OFILES.RSP\ '\n}\n/^LFILES *=/ c\\\nLFILES=' $(LFILES)\ '\n}\n/^EXTRA0 *=/ c\\\nEXTRA0=' $(EXTRA0)\ '\n}\n/^EXTRAS *=/ c\\\nEXTRAS=' $(EXTRAS)\ '\n}\n/^SHAKC *=/ c\\\nSHAKC=' $(SHAKC)\ '\n}\n/^SHAKOBJS *=/ c\\\nSHAKOBJS='@SHAKOBJS.RSP\ '\n}\n/^%\.[a-z][a-z]*: /,/^$$/ d\n'\ '/^moldy.bbl: /,/^$$/ d\n' \ '/^moldy.aux: /,/^$$/ d\n' \ > make.sed sed -f make.sed ./xmakefile > Makefile Makefile.mak: Makefile @PATH=/usr/5bin:${PATH};\ echo 's/^\([ ].*\)$@OFILES.RSP/\1@OFILES.RSP/g\n'\ 's/^\([ ].*\)$@SHAKOBJS.RSP/\1@SHAKOBJS.RSP/g\n'\ 's/\.\/.obj/g\n'\ 's/\.o$$/.obj/g\n'\ 's/ -\ *\(\<[a-z]*\>\)/ -e\1 /\n'\ 's/ -\//\n'\ '/^# \*\*\*\* MSDOS/,/^# \*/ s/^#\([A-Z]\)/\1/\n'\ 's/#XDR=/#XDR=/\n'\ 's/^\(CFLAGS=.*$$(XDR)\)/#\1/\n'\ moldy.tar.gz: moldy.tar gzip -c moldy.tar > moldy.tar.gz moldy.shar: $(CFILES) $(HFILES) Makefile xmakefile \ $(EXTRAS) $(INFILES) $(DOC) getopt.c $(VMSBUILD) $(DOSBUILD) shar $(CFILES) $(HFILES) Makefile xmakefile \ $(EXTRAS) $(INFILES) $(DOC) getopt.c $(VMSBUILD) $(DOSBUILD)\ > moldy.shar moldy.shar.Z: moldy.shar compress moldy.shar moldy.zip: $(CFILES) $(HFILES) Makefile xmakefile \ $(EXTRAS) $(INFILES) $(DOC) getopt.c $(VMSBUILD) $(DOSBUILD) $(ZIP) -l -q moldy.zip\ $(CFILES) $(HFILES) Makefile xmakefile \ $(EXTRAS) $(INFILES) $(DOC) getopt.c $(VMSBUILD) $(DOSBUILD);\ $(ZIP) -r -j -q moldy.zip $(W32BIN) cray: $(CFILES) $(HFILES) ./cray_make $(CFILES) $(HFILES) > cray.job moldy.job: $(CFILES) $(HFILES) ./make_cray $(CFILES) $(HFILES) > moldy.job compile_moldy.com: Makefile ./make_vms_compile $(FILES) > compile_moldy.com link_moldy.com: Makefile ./make_vms_link moldy $(FILES) > link_moldy.com compile_utils.com: Makefile ./make_vms_compile getopt ReadDCD $(UTILS) $(UTILS2) > compile_utils.com link_utils.com: Makefile $(RM) link_utils.com; \ for file in $(UTILS); do ./make_vms_link $$file $$file getopt>> \ link_utils.com; done for file in $(UTILS2); do \ ./make_vms_link $$file $$file getopt $(SHAKS) >> \ link_utils.com; done moldy.com: $(CFILES) $(HFILES) $(EXTRAS)\ $(INFILES) Aaaa_Read.Me $(DOC) $(VMSBUILD) $(DOSBUILD) $(DCLAR) Aaaa_Read.Me compile.com $(CFILES) $(HFILES)\ $(INFILES) $(EXTRAS) getopt.c $(DOC) $(VMSBUILD) $(DOSBUILD)\ > moldy.com moldy.com.Z: moldy.com compress moldy.com moldy.ps: moldy.dvi dvips -t a4 moldy -o moldy.ps moldy.dvi: moldy.tex moldy.bbl $(FIGTEXPS) latex moldy latex moldy @touch moldy.bbl $(LFILES): $(HFILES) # # Dependencies of objects on headers. # eigens.o quaterns.o: defs.h accel.o beeman.o convert.o input.o main.o output.o startup.o values.o parallel.o: \ structs.h defs.h messages.h algorith.o alloc.o matrix.o: defs.h messages.h xdr.o restart.o dump.o: structs.h defs.h messages.h xdr.h ReadDCD.o: ReadDCD.h # # Special options for performance-critical modules # auxil.o: auxil.c defs.h $(CC) $(CFLAGS) $(CFLAGS2) $(CFLAGSAUX) -c $< ewald.o: ewald.c structs.h defs.h $(CC) $(CFLAGS) $(CFLAGS2) $(CFLAGSP) -c $(INLINE) $< rdf.o: rdf.c structs.h defs.h $(CC) $(CFLAGS) $(CFLAGS2) -c $< kernel.o: kernel.c structs.h defs.h $(CC) $(CFLAGS) $(CFLAGS2) $(CFLAGSP) -c $< force.o: force.c structs.h defs.h $(CC) $(CFLAGS) $(CFLAGS2) $(CFLAGSP) $(INLINE) -c $< # # Rule for checking out source under RCS # ewald-RIL.c: co -pRIL ewald.c > ewald-RIL.c .c.o: $(CC) $(CFLAGS) -c $< $EOD $! $CREATE OFILES.RSP $DECK accel.o algorith.o alloc.o ansi.o auxil.o beeman.o convert.o dump.o ewald.o force.o input.o eigens.o kernel.o main.o matrix.o output.o quaterns.o rdf.o restart.o startup.o values.o xdr.o parallel.o $EOD $! $CREATE SHAKOBJS.RSP $DECK algorith.o alloc.o auxil.o ansi.o eigens.o kernel.o input.o matrix.o quaterns.o restart.o startup.o values.o xdr.o ReadDCD.o $EOD
Modified: Wed Feb 4 17:00:00 1998 GMT
Page accessed 3172 times since Sat Apr 17 21:33:00 1999 GMT