/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* This file is distributed subject to a Software License Agreement found
* in the file LICENSE that is included with this distribution. 
\*************************************************************************/

/* 
 * $Log: not supported by cvs2svn $
 * Revision 1.56 2011/05/19 23:43:29 lemery
 * Doubled the number of regions to 200k
 *
 * Revision 1.55  2011/05/19 23:07:05  lemery
 * Added leadingParticleBias option. Added necessary common block from egs to pass this flag.
 *
 * Revision 1.54  2010/05/05 21:12:35  lemery
 * Moved a debugging print statement passed an assignment statement so that correctt value is printed.
 *
 * Revision 1.53  2009/12/08 17:06:18  shang
 * added -macro option for geometry file.
 *
 * Revision 1.52  2009/11/24 05:04:00  lemery
 * Added seed number and IsOutputVolumn to summary file.
 * Changed int32_t to long, which is ok as long as the accompanying fortran
 * code will be compiled to use integer*8 as default integer for 64-bit machines.
 *
 * Revision 1.51  2009/08/13 17:46:25  soliday
 * Fixed a problem on Windows. Now that Windows uses the egs.c file the
 * options being passed to ausgab are pointers on Windows. This was causing
 * the problem because the other operating systems are not expecting pointers.
 *
 * Revision 1.50  2009/06/15 21:48:03  emery
 * Changed the declaration of most longs to int32_t. Some of the integers must
 * keep the long declaration for compatibility with get_long calls and fortran
 * subroutine arguments.
 *
 * Revision 1.49  2009/03/24 20:53:13  emery
 * Forgot a break inside a switch statement in ausgab, which caused electron
 * time-of-flight to be the sum of its normal value and that of a photon. Oops!
 *
 * Revision 1.48  2008/09/18 21:34:44  emery
 * Now uses the time column in the beam input file to start particles.
 *
 * Revision 1.47  2008/09/15 18:54:49  emery
 * Added double variable beta to epcont_. Changed order of
 * fields in epcont to conform to changes to egs4.
 * Use beta instead of beta_sqr for time calculation.
 *
 * Revision 1.46  2008/09/12 22:50:23  emery
 * Added time column in output file. For now initial
 * times are all 0, but could in the future be set from
 * input beam file.
 * Added extra time parameter in egs4 shower call.
 * Time is calculated in ausgab.
 * (Time inheritance is handled in egs4 routines.)
 * Added to list of ausgab flags.
 *
 * Revision 1.45  2008/04/18 16:16:37  emery
 * Include energy cut-off as columns of the summary file.
 *
 * Revision 1.44  2008/04/18 15:42:01  emery
 * Added capability to sum path length of the three types of particles.
 * A low-energy cut-off can be user-provided in the geometry definition file.
 * Non-zero default values are assumed.
 *
 * Revision 1.43  2008/02/15 02:40:45  emery
 * Moved debug statement in ausgab outside of an if statement
 * so that printing is done for all conditions.
 *
 * Revision 1.42  2008/02/15 01:56:46  emery
 * Turned on the PHOTOELECTRIC_INTERACTION flag which was important
 * to get all the energy deposited from photo-electrons.
 * The missing energy problem was discovered by J. Dooling.
 * Added more DEBUG printouts.
 *
 * Revision 1.41  2008/02/08 17:34:07  emery
 * Renamed variable RadiationDose as RadiationDoseRate since it is really a dose
 * rate.
 *
 * Revision 1.40  2008/01/15 15:59:59  emery
 * Added check to initial particle energy is greater than rest mass.
 * Removed verbose condition for printing out that initial particles
 * are in undefined regions or out of bounds.
 *
 * Revision 1.39  2007/02/12 21:06:30  soliday
 * Reverted some of my change to shower_ and fixtmx_ because there is some
 * hidden middle layer on Linux and Solaris where the elements are converted
 * to pointers. So if I pass pointers they turn out to be pointers to pointers
 * and thus will not work. It will stay the same for Windows.
 *
 * Revision 1.38  2007/02/12 20:52:53  soliday
 * Updated so that shower_ and fixtmx_ calls properly use pointers. It still
 * worked somehow on Linux and Solaris but Windows was having a problem. Also
 * updated the code so that Windows builds will probably call the functions
 * in the new egs4.c file.
 *
 * Revision 1.37  2006/08/24 19:23:16  soliday
 * Updated to not use cfortran.h on Windows.
 *
 * Revision 1.36  2006/08/24 15:11:21  soliday
 * Updated to work on Windows using Intel Visual Fortran Compiler. This compiler
 * is required if using Microsoft Visual Studio 2003 or 2005.
 *
 * Revision 1.35  2005/03/15 20:16:11  emery
 * Fixed an identation.
 *
 * Revision 1.34  2005/03/03 16:04:38  emery
 * Added test of charge before computing angle of arc segment in howfar.
 * Added more debugging print statements.
 *
 * Revision 1.33  2004/12/11 02:25:50  emery
 * Because of the use of strcpy, the character array axialDirectionName
 * in extended by one (a total of 2 bytes) to protect variable
 * TotalEnergyOfInitialRun or other variables in the declaration section.
 *
 * Revision 1.32  2004/12/04 20:24:33  emery
 * Changed short to long for the region value of the 3D or 2D grid elements.
 * This allows for a number of regions larger than 2^15 = 32k.
 * Added axialDirection variable for the cylindrical geometry.
 * Now the cconcentric ylinders can be defined along one of
 * the three axes instead of just z. Mixing of directions in one
 * run is not allowed. Increased INCREMENT_ARRAY_SIZE to 100.
 *
 * Revision 1.31  2004/04/26 04:14:13  emery
 * Increased MXREG 10-fold to 100000
 *
 * Revision 1.30  2003/11/10 02:16:28  emery
 * In debug block, added printout of whole stack instead
 * of just the top.
 *
 * Revision 1.29  2003/07/08 18:25:37  soliday
 * Added missing return statement.
 *
 * Revision 1.28  2002/08/14 20:34:34  soliday
 * Added Open License
 *
 * Revision 1.27  2002/05/31 22:08:21  emery
 * Removed the more sophisticated tracking installed in howfar last version
 * because it produced slightly different result from basic egs
 * tracking, which results in many warning messages.
 *
 * Revision 1.26  2002/05/31 21:22:26  emery
 * Fixed bug when magnetic field is used with slices. The deflection
 * angle (distance/rho) must be recalculated after the distance
 * is shortened.
 *
 * Revision 1.25  2001/05/30 23:08:21  emery
 * Fixed problem with missing parameter definitions in
 * trajectory and summary files. Increased array size for padded labels
 * shared with fortran. Use more accurate cos and sin coefficients
 * for arc trajectories in B fields.
 *
 * Revision 1.24  2001/04/12 17:32:00  emery
 * Added function compose_filename by M. Borland which
 * allows using %s string in the geometry file to define
 * rootname-based filenames.
 *
 * Revision 1.23  2000/05/19 19:23:53  soliday
 * Now defines _WINDOWS_ when running on Windows because this will stop
 * mdb.h from including windows.h
 *
 * Revision 1.22  2000/03/10 14:39:49  emery
 * Moved some define parameters to the function defineColumnsAndParameters.
 * Calculate the total energy of the input file before doing any
 * tracking so that the output file all have th evalue as a parameter.
 * Removed some if(summary) structures since summary==1 is enforced
 * at the start of the program.
 *
 * Revision 1.21  2000/03/10 07:29:03  emery
 * Added overlooked writing of some string parameter values to the summary file.
 * Indented some comments and added some comments.
 *
 * Revision 1.20  2000/03/08 02:05:55  emery
 * Removed some old DEBUG printing statements in howfar which
 * can no longer be reached when DEBUG is defined.
 * Aligned some comment lines.
 *
 * Revision 1.19  2000/03/06 23:38:06  emery
 * Removed superfluous \\ characters in SDDS file
 * descriptions.
 *
 * Revision 1.18  2000/03/01 20:56:41  soliday
 * Now works in msdos shell, not just cygnus shell.
 *
 * Revision 1.17  2000/02/21 16:49:26  soliday
 * Added support for Linux and WIN32
 *
 * Revision 1.16  1999/12/06 19:22:31  emery
 * Fixed error in formula for radiationDose which
 * was introduced in last few updates.
 *
 * Revision 1.15  1999/12/03 22:50:03  emery
 * Changed units of RadiationDose from mrem/hr to rad/s.
 *
 * Revision 1.14  1999/12/03 20:52:25  emery
 * Fixed a symbol definition for Edep column.
 *
 * Revision 1.13  1999/12/02 21:27:53  emery
 * Change option name from vacuumStepDefault to vacuumStepSize.
 *
 * Revision 1.12  1999/12/02 17:56:36  emery
 * Indented the usage message with less wasted space.
 * Changed format of a warning message printout.
 *
 * Revision 1.11  1998/05/21 18:44:00  emery
 * Renamed column descriptions of output data file.
 *
 * Revision 1.10  1997/08/15 21:18:22  emery
 * Bug found: i_y_slice where there should have been i_r_slice
 *
 * Revision 1.9  1997/02/13 21:25:07  emery
 * changed extern structure random_ to randomm_ because
 * the new egs4 version (3.0) changed the name of the random
 * common block.
 *
 * Revision 1.8  1996/11/20 22:08:50  emery
 * Added #ifdef EXCLUDE_FORTRAN statements
 * for compilation without the fortran calls or fortran
 * common blocks. Of course the program won't run as expected,
 * but as least it will be "purifiable".
 * Use an include statement which provides structure definitions.
 *
 * Revision 1.7  1996/07/13  21:26:24  emery
 * Changed names of variables in structure that links to
 * FORTRAN common block thresh
 *
 * Revision 1.6  1996/06/28  13:27:18  emery
 * Changed the way the regions are sliced up in order
 * to make the upper boundary coordinate on one slice
 * exactly (i.e. to all digits) equal to the lower
 * boundary of the next slice.
 *
 * Revision 1.5  1996/06/27  19:13:17  emery
 * Added calls to fixtmx which allows the user to
 * specify a maximum relative energy loss per tracking step
 * for a charged particle. The calls can be turned off at the command line.
 * Corrected a bug in the summary file column definitions
 * for the energy depositions.
 *
 * Revision 1.4  1996/06/13  14:16:42  emery
 * Added SDDS_register program call. Added one more
 * element to array EDepParticle to fix bug.
 *
 * Revision 1.3  1996/02/14  20:02:48  emery
 * Added -describeGeometryInput option,
 * which printouts namelist commands and fields for the geometry input file
 * */
/* shower3.c shower version 3
 * improvements over version 2:
 *     particle splitting
 * 11/01/95 11:54:21: cleansing of associate definitions
 *
 */
/* #define DEBUG */
#if defined(_WIN32)
#define _WINDOWS_
#endif
#include <stdio.h>
#include "mdb.h"
#include "scan.h"
#include "match_string.h"
#include "SDDS.h"
#include "constants.h"
#include "namelist.h"
#include "rpn.h"
#include "shower.h"
#include "showerTypes.h"
#if !defined(_WIN32)
#include "cfortran.h"
#endif

#if defined(_WIN32)
#define strcasecmp(s1, s2) _stricmp(s1, s2)
#endif

static char *USAGE1 = "shower <SDDSinputfile> -geometry=<GeometryDefinitionFile> -root=<string>\n\
 -summary[=<file>] [-defaultParticleType=<particleType>] [-seedNumber=<integer>] [-samples=<integer>]\n\
 [-trajectories[=<file>]] [-vacuumStepSize=<distance-in-cm>] [-plotGeometry[=<file>]]\n\
 [-outOfBounds[=<file>]] [-undefinedRegion[=<file>]]\n\
 [-useInInput=<particleType>[,...]] [-keepInOutput=<particle-type>[,...]] [-verbose]\n\
 [-splitting] [-leadingParticleBias] [-describeGeometryInput] [-fixLowEnergyTransport=[{0|1}]] \n\
 [-sumPathLengths] [-addTimeColumn] [-useTimeColumn] [-macro=<tag>=<value>,[...]]\n\n\
SDDSinputfile         input file of initial coordinates of incident particles;\n\
                      output files produced by previous shower runs are valid input files;\n\
-geometry             specifies file with geometry namelist commands defining materials;\n\
-root                 string with which to construct default filenames;\n\
-summary              SDDS file containing summary data for each region; default name\n\
                      is <root>.summary where <root> is the value of the -root option;\n\
-defaultParticleType  default particle type if not defined in input beam file;\n\
<particleType>        valid values are \"electrons\", \"positrons\", or \"photons\";\n\
-seedNumber           integer to initialize the random number generator;\n\
-samples              number of times to repeat the tracking of input file particles;\n\
-trajectories         file for storing shower trajectories; default name \n\
                      is <root>.traj; \n";

static char *USAGE2 = "-vacuumStepSize       maximum step size in vacuum;\n\
-plotGeometry         SDDS file containing line segments\n\
                      defining solid regions edges;\n\
-outOfBounds          SDDS file containing coordinates of out-of-bounds particles;\n\
                      default name is <root>.outOfBounds;\n\
-undefinedRegion      SDDS file containing coordinates of particles entering\n\
                      undefined space; default name is <root>.undefinedRegion;\n\
-useInInput           select which particle type or types use (read) in particle\n\
                      coordinate input file;\n\
-keepInOutput         select which particle type or types to keep (write) in particle\n\
                      coordinate output files;\n\
-splitting            enables splitting of particle for improved statistics\n\
                      as defined in the geometry definitions;\n\
-leadingParticleBias  Enables leading particle bias. Can be used to shorten simulation time\n\
                      though variance in small volumes may increase.\n\
-describeGeometryInput prints out the namelist commands for the geometry input file\n\
-fixLowEnergyTransport flag for enabling(1)/disabling(0) the maximum limit on the fractional\n\
                      energy loss per step defined for each material in the geometry\n\
                      input file. The default value is 1\n\
-sumPathLengths       calculated path lengths for all particles in all regions.\n\
                      Energy cut-offs are specified in region definitions.\n\
                      Results are in the summary file.\n\
-addTimeColumn        Output coordinate files contains time elapsed\n\
                      relative to initial particle. Initial particle has t=0.\n\
-useTimeColumn        Use the column \"t\" in the beam input file as a starting\n\
                      time value for the shower.\n\
-macro                replace the tags in the geometry input file by provided values.\n\
-verbose              spews verbiage\n";

#define MXSTACK 500
/* **warning ** these common blocks are subject to change
   because all common blocks in egs are expanded macros */
#ifdef EXCLUDE_FORTRAN
struct stack {
#else
extern struct stack {
#endif
  double energyMeV[MXSTACK];
  double x[MXSTACK], y[MXSTACK], z[MXSTACK];
  double u[MXSTACK], v[MXSTACK], w[MXSTACK];
  double t[MXSTACK];
  double distanceNextRegion[MXSTACK];
  double weight[MXSTACK];
  /* We switched from int32_t to long, now that shower will be
     compiled with integer*8 as default on 64-bit machines
   */
  long integerCharge[MXSTACK];
  long region[MXSTACK];
  long latch[MXSTACK];   /* not used */
  long latchi;
  long stackPointer;
} stack_;
#define MXAUS 25

#ifdef EXCLUDE_FORTRAN
struct epcont {
#else
extern struct epcont {
#endif
  double energy_deposited_MeV;
  double dist_next_interaction_cm;  /* tstep */
  double curved_step_cm;     /* tustep */
  double straight_step_cm;   /* ustep */
  double actual_curved_step_cm;  /* tvstep */
  double actual_straight_step_cm;  /* vstep */
  double tscat;  /* maximum path length divided by two */
  double beta_sqr;           /* (v/c)^2 */
  double beta;               /* v/c     */
  float density_correction;    /* rhof */
  float initial_energy_MeV;   /* eold */
  float final_energy_MeV;    /* enew */
  float kin_energy_MeV;      /* eke */
  float ln_kin_energy_MeV;   /* elke */
  float ln_photon_energy_MeV;  /* gle */
  /* We switched from int32_t to long, now that shower will be
     compiled with integer*8 as default on 64-bit machines
   */
  long discard_flag;         /* idisc */
  long oldRegion;     /* irold */
  long newRegion;     /* irnew */
  long ausgab_flag[MXAUS];  /*iausfl($MXAUS) */
} epcont_;
#define MXMED 10
#ifdef EXCLUDE_FORTRAN
struct media {
#else
extern struct media {
#endif
  float rad_length_cm[MXMED];
  float rad_length_user_units[MXMED];
  float density_cgs[MXMED];
  /* We switched from int32_t to long, now that shower will be
     compiled with integer*8 as default on 64-bit machines
   */
  long msge[MXMED];
  long mge[MXMED];
  long mseke[MXMED];
  long meke[MXMED];
  long mleke[MXMED];
  long mcmfp[MXMED];
  long mrange[MXMED];
  long rayleigh_flags[MXMED];
  long no_media;
} media_;
/* need 96 characters for the 24 characters stored in egs common because
   only the first character in the character*4 variable is used in egs */
#ifdef EXCLUDE_FORTRAN
struct mediac {
#else
extern struct mediac {
#endif
  char media_labels[MXMED][96];
} mediac_;
#define MXREG 200000
#ifdef EXCLUDE_FORTRAN
struct bounds {
#else
extern struct bounds {
#endif
  float leptonEnergyCutoff[MXREG];
  float photonEnergyCutoff[MXREG];
  float vacuum_step_default;
} bounds_;
#ifdef EXCLUDE_FORTRAN
struct misc {
#else
extern struct misc {
#endif
  /* We switched from int32_t to long, now that shower will be
     compiled with integer*8 as default on 64-bit machines
   */
  long fortran_read_unit;
  long fortran_echo_unit;
  float user_units;
  long no_bypasses_scattering;
  long medium_index[MXREG];
  float density_cgs[MXREG];
  long Rayleigh_flags[MXREG];
} misc_;
#ifdef EXCLUDE_FORTRAN
struct random {
#else
extern struct random {
#endif
  /* We switched from int32_t to long, now that shower will be
     compiled with integer*8 as default on 64-bit machines
   */
  long seed_number;     /* ixx, if zero, then it is initialized in hatch with value 987654321 */
} randomm_;

#ifdef EXCLUDE_FORTRAN
struct thresh {
#else
extern struct thresh {
#endif
  float eRestMass2;
  float eRestMassSquare;
  float escd2;
  float photonLowerEnergyCutoff[MXMED];
  float leptonLowerEnergyCutoff[MXMED];
  float photonUpperEnergyCutoff[MXMED];
  float leptonUpperEnergyCutoff[MXMED];
  float leptonLowerKineticEnergyCutoff[MXMED];
  float mollerThreshEnergy[MXMED];
} thresh_;

#ifdef EXCLUDE_FORTRAN
struct useful {
#else
extern struct useful {
#endif
  double precisionZero;
  double precisionRestMass;
  double precisionRestMass2;
  float restMass;
  /* We switched from int32_t to long, now that shower will be
     compiled with integer*8 as default on 64-bit machines
   */
  long medium;
  long medold;
  long iblobe;
} useful_;

  /* this links to common userlpd (containing only leading partile bias flag) */
#ifdef EXCLUDE_FORTRAN
struct user {
#else
extern struct user {
#endif
  long leadingParticleBias;
} userlpb_ ;

/* these macros are used in determining the particle type according to the charge 
   returned from egs */
#define PHOTON_CHARGE 0
#define ELECTRON_CHARGE -1
#define POSITRON_CHARGE 1

/* These macros are used when matching a particle type to elements of an array
 */
#define ELECTRON 0
#define POSITRON 1
#define PHOTON 2
#define PARTICLE_TYPES 3
char *particleNames[PARTICLE_TYPES] = {
  "electrons", "positrons", "photons"};
int32_t particleCharge[PARTICLE_TYPES] = {
  ELECTRON_CHARGE, POSITRON_CHARGE, PHOTON_CHARGE};

/* ausgab flags */
#define BEFORE_TRANSPORT 0
#define USER_ENERGY_CUTOFF_DISCARD 1
#define PEGS_ENERGY_CUTOFF_DISCARD 2
#define USER_REQUESTED_DISCARD 3
#define PHOTOELECTRIC_INTERACTION 4
#define AFTER_TRANSPORT 5
#define BEFORE_BREMS_CALL 6
#define AFTER_BREMS_CALL 7
#define AFTER_MOLLER_CALL 9
#define AFTER_BHABHA_CALL 11
#define BEFORE_ANNIH_CALL 12
#define AFTER_ANNIH_CALL 13
#define ANNIH_AT_REST 14
#define BEFORE_PAIR_CALL 15
#define AFTER_PAIR_CALL 16
#define BEFORE_COMPTON_CALL 17
#define AFTER_COMPTON_CALL 18
#define BEFORE_PHOTO_CALL 19
#define AFTER_PHOTO_CALL 20
#define BEFORE_UPHI_CALL 21
#define AFTER_UPHI_CALL 22

char *ausgab_flag_messages[MXAUS] = {
  "is going to be transported by distance epcont_.actual_curved_step_cm (TVSTEP)  = ", 
  "is going to be discarded because AE < energy < ECUT (or for photons, AP < energy < PCUT)", 
  "is going to be discarded because energy < AE and energy < ECUT (or for photons, energy < AP and energy < PCUT)", 
  "is going to be discarded because the user requested it (in HOWFAR usually)", 
  "A photoelectric interaction has occurred and either: a) energy < K-edge and it is going to \nbe discarded, or b) a (fluorescent) photon is going to be discarded with the K-edge binding energy.", 
  "has been transported by distance epcont_.actual_curved_step_cm (TVSTEP)  = ", 
  "A bremsstrahlung interaction is to occur and a call to BREMS is about to be made in ELECTR.", 
  "Returned to ELECTR after a call to BREMS was made.", 
  "A Moller interaction is to occur and a call to MOLLER is about to be made in ELECTR.", 
  "Returned to ELECTR after a call to MOLLER was made.", 
  "A Bhabha interaction is to occur and a call to BHABHA is about to be made in ELECTR.", 
  "Returned to ELECTR after a call to BHABHA was made.", 
  "An in-flight annihilation of the positron is to occur and a call to ANNIH is about to be made in ELECTR", 
  "Returned to ELECTR after a call to AHHIH was made.", 
  "A positron has annihilated at rest.", 
  "A pair production interaction is to occur and a call to PAIR is about to be made in PHOTON.", 
  "Returned to PHOTON after a call to PAIR was made.", 
  "A Compton interaction is to occur and a call to COMPT is about to be made in PHOTON.", 
  "Returned to PHOTON after a call to COMPT was made.", 
  "A photoelectric interaction is to occur and a call to PHOTO is about to be made in PHOTON.", 
  "Returned to PHOTON after a call to PHOTO was made (assuming stackPointer (NP) is non-zero).", 
  "Subroutine UPHI was just entered.", 
  "Subroutine UPHI was just exited.", 
  "A coherent (Rayleigh) interaction is about to occur.", 
  "A coherent (Rayleigh) interaction has just occurred."
  };

/*geometry command */
#define INITIAL_COORDINATES 0
#define CYLINDER 1
#define BLOCK 2
#define N_COMMANDS 3
char *command_names[N_COMMANDS] = {
  "initial_coordinates", "cylinder", "block"
  };

char *GeomCommDescription[N_COMMANDS]  =  {
  "initial_coordinates          define initial coordinate for tracking", 
  "cylinder                     define a cylinder of material", 
  "block                        define a block of material"     };

#define BLOCK_MODE 0
#define CYLINDER_MODE 1
#define MODES 2
char *modeNames[MODES] = {
  "block", 
  "cylinder"
  };

int32_t mode;

#define X_DIRECTION 0
#define Y_DIRECTION 1
#define Z_DIRECTION 2
#define DIRECTIONS 3
char *directionNames[DIRECTIONS] = {
  "x", 
  "y",
  "z"
  };

char padded_label[25];     /* used to write to egs labels. need 24+1 bytes*/
int32_t materials;

#define INFINITY 1e38
#define MINIMUM_ANGLE 0.05
#define TRAJECTORY_COORDINATES 0x0001UL
#define FULL_COORDINATES       0x0002UL

REGION *region;
REGION *undefinedRegion;
REGION *outOfBoundsRegion;
SDDS_DATASET trajectoryPage;
SDDS_DATASET outOfBoundsPage;
SDDS_DATASET undefinedRegionPage;
BUFFER_ARRAY_GROUP *showerBufferArrayGroup;
BUFFER_ARRAY_GROUP *outOfBoundsBufferArrayGroup;
BUFFER_ARRAY_GROUP *undefinedBufferArrayGroup;

int32_t regions;

double *EDepParticle[PARTICLE_TYPES+1];
char *EDepParticleNames[PARTICLE_TYPES+1] = {
  "EDepElectrons", "EDepPositrons", "EDepPhotons", "EDepTotal"};
char *EDepParticleUnits[PARTICLE_TYPES+1] = {
  "MeV", "MeV", "MeV", "MeV"};
char *EDepParticleSymbols[PARTICLE_TYPES+1] = {
  "E$bdep,e-$n", "E$bdep,e+$n", "E$bdep,$gg$r$n", "E$bdep$n"};
char *EDepParticleDescriptions[PARTICLE_TYPES+1] = {
  "Energy deposited by electrons", "Energy deposited by positrons", "Energy deposited by photons", "Energy deposited"};
char *EDepParticleFormat[PARTICLE_TYPES+1] = {
  "%14.6e", "%14.6e", "%14.6e", "%14.6e"};


#define SET_SAMPLE_NO 0
#define SAVE_SHOWERS 1
#define SET_VACUUM_STEP_DEFAULT 2
#define GEOMETRY 3
#define SUMMARY 4
#define PLOT_GEOMETRY 5
#define SEED_NUMBER 6
#define OUT_OF_BOUNDS 7
#define UNDEFINED_REGION 8
#define ROOT_FILENAME 9
#define USE_IN_INPUT 10
#define KEEP_IN_OUTPUT 11
#define DEFAULT_PARTICLE_TYPE 12
#define VERBOSE 13
#define SPLITTING 14
#define DESCRIBEGEOM 15
#define FIXTMXOPTION 16
#define SUMPATHLENGTH 17
#define ADDTIMECOLUMN 18
#define USETIMECOLUMN 19
#define SET_MACROS 20
#define LEADINGPARTICLEBIAS 21
#define N_OPTIONS 22
char *option[N_OPTIONS] = {
  "samples", 
  "trajectories", 
  "vacuumStepSize", 
  "geometry", 
  "summary", 
  "plotGeometry", 
  "seedNumber", 
  "outOfBounds", 
  "undefinedRegion", 
  "root", 
  "useInInput", 
  "keepInOutput", 
  "defaultParticle", 
  "verbose", 
  "splitting",
  "describeGeometryInput",
  "fixLowEnergyTransport",
  "sumPathLengths",
  "addTimeColumn",
  "useTimeColumn",
  "macro",
  "leadingParticleBias",
  };

int32_t outputRegions;

int32_t iParticle;
int32_t iSample;
#define POINTS_PER_PAGE 500
int32_t trajectoryPage_set;
#define DEFAULT_VACUUM_STEP 1000

#define KEEP_PARTICLE    0x0001UL
#define USE_PARTICLE     KEEP_PARTICLE
#define DISCARD_PARTICLE 0x0002UL
#define IGNORE_PARTICLE  DISCARD_PARTICLE
uint32_t outputFlags[PARTICLE_TYPES];
uint32_t inputFlags[PARTICLE_TYPES];

int32_t summary = 0;
int32_t outOfBoundsPage_set = 0;
int32_t undefinedRegionPage_set = 0;
int32_t allowsplitting = 0;
int32_t sumPathLengths = 0;
int32_t TotalEnergyOfInitialRunDefined;
int32_t addTimeColumn = 0;

#define INCREMENT_ARRAY_SIZE 100
#define IS_ZERO(x) ((x)==0?1:0)
#define IS_NONZERO(x) ((x)!=0?1:0)
/* prototypes */
#ifndef EXCLUDE_FORTRAN
#if defined(_WIN32) /*Currently you must use Intel Visual Fortran on Windows*/
void shower_(int32_t *a, double *b, double *c, double *d, double *e, double *f, double *g, double *h, double *k, int32_t *i, float *j);
void hatch_(void);
void fixtmx_(float *a, int32_t *b);
#define FIXTMX fixtmx_
#define HATCH hatch_
#define SHOWER shower_
#else
PROTOCCALLSFSUB11(SHOWER,shower,PLONG,PDOUBLE,PDOUBLE,PDOUBLE,PDOUBLE,PDOUBLE,PDOUBLE,PDOUBLE,PDOUBLE,PLONG,PFLOAT)
#define SHOWER(A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11) CCALLSFSUB11(SHOWER,shower,PLONG,PDOUBLE,PDOUBLE,PDOUBLE,PDOUBLE,PDOUBLE,PDOUBLE,PDOUBLE,PDOUBLE,PLONG,PFLOAT,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11)
PROTOCCALLSFSUB0(HATCH,hatch)
#define HATCH() CCALLSFSUB0(HATCH,hatch)
PROTOCCALLSFSUB2(FIXTMX,fixtmx,PFLOAT,PLONG)
#define FIXTMX(A1,A2) CCALLSFSUB2(FIXTMX,fixtmx,PFLOAT,PLONG,A1,A2)
#endif
#endif

#if defined(_WIN32) /*Currently you must use Intel Visual Fortran on Windows*/
void howfar_();  
void ausgab_(int32_t *situationIndex);
#else
void howfar();  
void ausgab(int32_t situationIndex);
#endif

void defineColumnsAndParameters(SDDS_DATASET *page, uint32_t coordinateflag);
void flushBufferArrayGroup( SDDS_DATASET *page, BUFFER_ARRAY_GROUP *bufferArrayGroup, uint32_t coordinateflag);
void flushBufferArray( SDDS_DATASET *page, BUFFER_ARRAY *buffer_array, uint32_t coordinateflag);
BUFFER_ARRAY_GROUP *allocateBufferArrayGroup(uint32_t coordinateflag);
BUFFER_ARRAY *allocateBufferArrays(uint32_t coordinateflag);
int32_t getParticleType(int32_t charge);
char *compose_filename(char *template, char *root_name);

int32_t blockFindRegion(double x, double y, double z);
int32_t cylFindRegion(double x, double y, double z);
int32_t (*findRegion[MODES])(double x, double y, double z) = {
  blockFindRegion, cylFindRegion
  };

double blockFindDistanceToBoundary(double x, double y, double z, double u, double v, double w);
double cylFindDistanceToBoundary(double x, double y, double z, double u, double v, double w);
double (*findDistanceToBoundary[MODES])(double x, double y, double z, double u, double v, double w) = {
  blockFindDistanceToBoundary, cylFindDistanceToBoundary
  };

void setup3DblocksGrid( REGION *region, int32_t regions);
void setup2DcylindersGrid( REGION *region, int32_t regions);
void (*setupGrid[MODES])(REGION *region, int32_t regions) = {
  setup3DblocksGrid, setup2DcylindersGrid
  };

void plot3Dgeometry(REGION *region, char *filename);
void plot2Dgeometry(REGION *region, char *filename);
void (*plotGeometry[MODES])(REGION *region, char *filename) = {
  plot3Dgeometry, plot2Dgeometry
  };


int main( int argc, char **argv)
{
  SCANNED_ARG *scanned;

  char *inputfile;
  int32_t rootFilename_set = 0;
  int32_t rootFilename_required = 0;
  char *rootFilename=NULL;
  char *trajectoryFile;
  char *outOfBoundsFile;
  char *undefinedRegionFile;
  int32_t trajectoryFilePending = 0;
  int32_t useTimeColumn = 0 ;
  
  long local_seed; /* for get_long, which is only garanteed for long, not int32_t */
  int32_t seedNumber=0;
  int32_t i, j, ipage;
  long samples;
  int32_t iMaterial;
  double vacuum_step_default;
  
  char **material;
  char *char_ptr1, *char_ptr2;
  
  long integerCharge;
  double energyMeV;
  double TotalEnergy;  /* units of MeV */
  double x, y, z, u, v, w, t;
  float weight;  /* quirk of egs subroutine shower forces this declaration for argument */
  float maximumDeltaE;  /* variable to pass to fixtmx */
  char *Type;
  int32_t iRegion, initialRegion;
  long region_egs;
  int32_t i_flag;
  double denom;
  int32_t type, particleType;
  int32_t regionsMax = 0;
  int32_t i_r_slice, i_x_slice, i_y_slice, i_z_slice;
  int32_t geometry_set = 0;
  char s[1024];
  char *geometryFile;
  FILE *fp_geometry;
  int32_t materialCodeFound;
  int32_t plotGeometry_set;
  char *plotGeometryFile;
  int32_t plotGeometryFilePending = 0;
  char *summaryFile;
  int32_t summaryFilePending = 0;
  SDDS_DATASET summaryPage;
  int32_t outOfBoundsFilePending = 0;
  int32_t undefinedRegionFilePending = 0;
  SDDS_DATASET inputBeamPage;
  int32_t useDefaultParticleType  =  0;
  int32_t default_particle_type_set;
  int32_t default_particle_type=0, input_particle_type;
  char *RegionFileDescription;
  
  double PowerOfInitialBeam;
  double CumulativeSamples;
  int32_t Generation;
  int32_t iRow, rows;
  int32_t xDefined, yDefined, zDefined, uDefined, vDefined, wDefined, weightDefined, tDefined=0;
  double *xColumn=NULL, *yColumn=NULL, *zColumn=NULL, *uColumn=NULL, *vColumn=NULL, *wColumn=NULL, *weightColumn=NULL, *EColumn, *tColumn=NULL;
  double TotalEnergyOfInitialRun = 0;
  char shell_command[256];
  char *egs4_crosssectionFile;
  FILE *fort12_fp, *egs4_crosssection_fp;
  int32_t verbose;
  int32_t describeGeometry;
  long fixtmxFlag;
  int32_t axialSlices=0, i_axial_slice, axialDirection;
  char axialDirectionName[2]="";
  double axialCoordinateLowerCM=0, axialCoordinateUpperCM=0;

  double FractionEDep, RadiationDoseRate, Mass;

  long macros;
  char **macroTag, **macroValue;
#if defined(VAX_VMS) || defined(UNIX) || defined(_WIN32)
  init_stats();
#endif
  SDDS_RegisterProgramName(argv[0]);
  argc  =  scanargs(&scanned, argc, argv);
  if (argc == 1) {
    fprintf(stderr, "%s%s", USAGE1, USAGE2);
    exit(1);
  }

  if (getenv("RPN_DEFNS")) {
    rpn(getenv("RPN_DEFNS"));
    if (rpn_check_error()) {
      bomb("RPN_DEFNS environment variable invalid", NULL);
    }
  } else {
    printf("Warning: RPN_DEFNS environment variable undefined. Must define if rpn expressions are used in geometry files.");
  }

  inputfile  =  NULL;
  trajectoryFile  =  NULL;
  outOfBoundsFile  =  NULL;
  undefinedRegionFile  =  NULL;
  geometryFile  =  NULL;
  plotGeometryFile  =  NULL;
  summaryFile  =  NULL;
  samples = 1;
  trajectoryPage_set = 0;
  vacuum_step_default = DEFAULT_VACUUM_STEP;
  plotGeometry_set = 0;
  verbose = 0;
  allowsplitting = 0;
  sumPathLengths = 0;
  default_particle_type_set = 0;
  describeGeometry = 0;
  fixtmxFlag = 1;
  axialDirection = -1;
  userlpb_.leadingParticleBias = 0;

  macros = 0;
  macroTag = macroValue = NULL;
  for (type = 0; type<PARTICLE_TYPES; type++) {
    inputFlags[type] = USE_PARTICLE;
    outputFlags[type] = KEEP_PARTICLE;
  }    
  for (i = 1; i<argc; i++) {
    if (scanned[i].arg_type == OPTION) {
      delete_chars(scanned[i].list[0], "_");
      switch(match_string(scanned[i].list[0], option, N_OPTIONS, UNIQUE_MATCH)) {
      case SET_SAMPLE_NO:
        get_long(&samples, scanned[i].list[1]);
        break;
      case SAVE_SHOWERS:
        if (!trajectoryPage_set) {
          if (scanned[i].n_items > 1) {
            trajectoryFile = scanned[i].list[1];
          }
          else {
            trajectoryFilePending = 1;
            rootFilename_required = 1;
          }
          trajectoryPage_set = 1;
        }
        else {
          printf("-trajectories option following the first one is ignored.");
        }               
        break;
      case SET_VACUUM_STEP_DEFAULT:
        get_double(&vacuum_step_default, scanned[i].list[1]);
        if (!vacuum_step_default)
          bomb("Vacuum step default can't be set to zero", NULL);
        break;
      case GEOMETRY:
        if (!geometry_set) {
          if (scanned[i].n_items > 1) {
            geometryFile = scanned[i].list[1];
          }
          else {
            bomb("No geometry file given", NULL);
          }
          geometry_set = 1;
        }
        else {
          printf("-geometry command following the first one is ignored.\n");
        }
        break;
      case SUMMARY:
        if (!summary) {
          if (scanned[i].n_items > 1) {
            summaryFile = scanned[i].list[1];
          }
          else {
            summaryFilePending = 1;
            rootFilename_required = 1;
          }
          summary = 1;
        }
        else {
          printf("-summary option following the first one is ignored.");
        }
        break;
      case PLOT_GEOMETRY:
        if (!plotGeometry_set) {
          if (scanned[i].n_items > 1) {
            plotGeometryFile = scanned[i].list[1];
          }
          else {
            plotGeometryFilePending = 1;
            rootFilename_required = 1;
          }
          plotGeometry_set = 1;
        }
        else {
          printf("-plotGeometry option following the first one is ignored.");
        }
        break;
      case OUT_OF_BOUNDS:
        if (!outOfBoundsPage_set) {
          if (scanned[i].n_items > 1) {
            outOfBoundsFile = scanned[i].list[1];
          }
          else {
            outOfBoundsFilePending = 1;
            rootFilename_required = 1;
          }
          outOfBoundsPage_set = 1;
        }
        else {
          printf("-outOfBounds option following the first one is ignored.");
        }
        break;
      case UNDEFINED_REGION:
        if (!undefinedRegionPage_set) {
          if (scanned[i].n_items > 1) {
            undefinedRegionFile = scanned[i].list[1];
          }
          else {
            undefinedRegionFilePending = 1;
            rootFilename_required = 1;
          }
          undefinedRegionPage_set = 1;
        }
        else {
          printf("-undefinedRegion option following the first one is ignored.");
        }
        break;
      case SEED_NUMBER:
        get_long(&local_seed, scanned[i].list[1]);
	randomm_.seed_number = local_seed;
        if (!randomm_.seed_number)
          printf("Seed number of 0 will be changed to the EGS4 internal default "
                 "which isn't guaranteed\n to be known in advance.\n");
        break;
      case ROOT_FILENAME:
        if (!rootFilename_set) {
          if (scanned[i].n_items > 1) {
            rootFilename = scanned[i].list[1];
          }
          else {
            bomb("rootFilename must be specified.", NULL);
          }
          rootFilename_set = 1;
        }
        else {
          printf("-rootFilename option following the first one is ignored.");
        }
        break;
      case USE_IN_INPUT:
        if (scanned[i].n_items == 1) {
          printf("Warning: no particle type was specified in option -useininput.\n");
          break;
        }
        else {
          for (type = 0; type<PARTICLE_TYPES; type++)
            inputFlags[type] = IGNORE_PARTICLE;
          for (j = 1; j<scanned[i].n_items; j++) {
            if (0>(particleType = match_string(scanned[i].list[j], particleNames, PARTICLE_TYPES, UNIQUE_MATCH))) {
              printf("Particle type %s isn't valid.\n", scanned[i].list[j]);
              exit(1);
            }
            inputFlags[particleType] = USE_PARTICLE;
          }
        }
        break;
      case KEEP_IN_OUTPUT:
        if (scanned[i].n_items == 1) {
          printf("Warning: no particle type was specified in option -keepinoutput.\n");
          break;
        }
        else {
          for (type = 0; type<PARTICLE_TYPES; type++)
            outputFlags[type] = DISCARD_PARTICLE;
          for (j = 1; j<scanned[i].n_items; j++) {
            if (0>(particleType = match_string(scanned[i].list[j], particleNames, PARTICLE_TYPES, UNIQUE_MATCH))) {
              printf("Particle type %s isn't valid.\n", scanned[i].list[j]);
              exit(1);
            }
            outputFlags[particleType] = KEEP_PARTICLE;
          }
        }
        break;
      case DEFAULT_PARTICLE_TYPE:
        if (scanned[i].n_items!= 2) {
          printf("bad -defaultparticletype syntax.\n");
          exit(1);
        }
        else {
          if (0>(default_particle_type = 
                 match_string(scanned[i].list[1], particleNames, PARTICLE_TYPES, UNIQUE_MATCH))) {
            printf("Particle type %s isn't valid.\n", scanned[i].list[1]);
            exit(1);
          }
          default_particle_type_set = 1;
        }
        break;
      case VERBOSE:
        verbose = 1;
        break;
      case SPLITTING:
        allowsplitting = 1;
        break;
      case LEADINGPARTICLEBIAS:
        userlpb_.leadingParticleBias = 1;
        break;
      case DESCRIBEGEOM:
        show_namelists_fields(stdout, namelist_pointer, namelist_name, n_namelists);
        if (argc==2)
          exit(0);
        break;
      case FIXTMXOPTION:
        if (scanned[i].n_items != 2 || 
            !get_long(&fixtmxFlag, scanned[i].list[1])) {
          printf("bad -fixLowEnergyTransport syntax.\n");
          exit(1);
        }
        break;
      case SUMPATHLENGTH:
        sumPathLengths = 1;
        break;
      case ADDTIMECOLUMN:
        addTimeColumn = 1;
        break;
      case USETIMECOLUMN:
        useTimeColumn = 1;
        break;
      case SET_MACROS:
        if ((scanned[i].n_items-=1)<0) {
          fprintf(stdout, "Invalid -macro syntax\n");
          exit(1);
        }
        if (!(macroTag=SDDS_Realloc(macroTag, sizeof(*macroTag)*(macros+scanned[i].n_items))) ||
            !(macroValue=SDDS_Realloc(macroValue, sizeof(*macroValue)*(macros+scanned[i].n_items))))
          bomb("memory allocation failure (-macro)", NULL);
        else {
          for (j=0; j<scanned[i].n_items; j++) {
            macroTag[macros] = scanned[i].list[j+1];
            if (!(macroValue[macros] = strchr(macroTag[macros], '='))) {
              fprintf(stdout, "Invalid -macro syntax\n");
              exit(1);
            }
            macroValue[macros][0] = 0;
            macroValue[macros] += 1;
            macros++;
          }
        }
        scanned[i].n_items +=1;
        break;
     default:
        bomb("unknown option given.", NULL);  
      }
    }
    else {
      if (!inputfile)
        inputfile  =  scanned[i].list[0];
      else
        bomb("too many filenames given", NULL);
    }
  }

  if (!rootFilename_set && rootFilename_required) {
    bomb("option -root was not specified", NULL);
  }
  
  if (!geometry_set)
    bomb("Geometry file (option -geometry) not specified.", NULL);

  if (!summary)
    bomb("Summary file (option -summary) not specified.", NULL);



  /***************************************************\
   * get parameter information from first input file  *
   \***************************************************/
  if (!SDDS_InitializeInput(&inputBeamPage, inputfile))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  /* read first page of input file to get parameters 
     Type
     Generation
     PowerOfInitialBeam
     TotalEnergyOfInitialRun
     CumulativeSamples
     */
  while((ipage=SDDS_ReadPage(&inputBeamPage))>0) {
    rows = SDDS_CountRowsOfInterest(&inputBeamPage);
    if (ipage==1) {
      /* parameter Type */
      switch(SDDS_CheckParameter(&inputBeamPage, "Type", NULL, SDDS_STRING, verbose?stderr:NULL)) {
      case SDDS_CHECK_NONEXISTENT:
        if (verbose)
          fprintf( stderr, "\tParameter Type not found in input file.\n");
        if (!default_particle_type_set) {
          fprintf( stderr, "Option -defaultparticletype not found.\n");
          exit(1);
        }
        useDefaultParticleType = 1;
        break;
      case SDDS_CHECK_WRONGTYPE:
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        exit(1);
        break;
      case SDDS_CHECK_OKAY:
        break;
      default:
        fprintf( stderr, "Unexpected result from SDDS_CheckParameter routine while checking parameter Type.\n");
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        exit(1);
        break;
      }
      /* parameter Generation */
      switch(SDDS_CheckParameter(&inputBeamPage, "Generation", NULL, SDDS_LONG, verbose?stderr:NULL)) {
      case SDDS_CHECK_NONEXISTENT:
        /* generate parameter */
        Generation = 0;
        if (verbose)
          fprintf( stderr, "\tParameter Generation not found in input file. Using value of 0.\n");
        break;
      case SDDS_CHECK_WRONGTYPE:
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        exit(1);
        break;
      case SDDS_CHECK_OKAY:
        SDDS_GetParameter(&inputBeamPage, "Generation", &Generation);
        break;
      default:
        fprintf( stderr, "Unexpected result from SDDS_CheckParameter routine while checking parameter Generation.\n");
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        exit(1);
        break;
      }
      /* Parameter PowerOfInitialBeam */
      switch(SDDS_CheckParameter(&inputBeamPage, "PowerOfInitialBeam", "W", SDDS_DOUBLE, verbose?stderr:NULL)) {
      case SDDS_CHECK_NONEXISTENT:
        /* generate parameter */
        PowerOfInitialBeam = 1;
        if (verbose)
          fprintf( stderr, "\tParameter PowerOfInitialBeam not found in the input file. Assume value of 1 W.\n");
        break;
      case SDDS_CHECK_WRONGUNITS:
      case SDDS_CHECK_WRONGTYPE:
        fprintf( stderr, "Something wrong with parameter PowerOfInitialBeam.\n");
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        exit(1);
        break;
      case SDDS_CHECK_OKAY:
        SDDS_GetParameter(&inputBeamPage, "PowerOfInitialBeam", &PowerOfInitialBeam);
        break;
      default:
        fprintf( stderr, "Unexpected result from SDDS_CheckParameter routine while checking parameter PowerOfInitialBeam.\n");
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        exit(1);
        break;
      }
      /* parameter TotalEnergyOfInitialRun */
      TotalEnergyOfInitialRunDefined = 0;
      switch(SDDS_CheckParameter(&inputBeamPage, "TotalEnergyOfInitialRun", "MeV", SDDS_DOUBLE, verbose?stderr:NULL)) {
      case SDDS_CHECK_NONEXISTENT:
        if (verbose) {
          if (Generation>0)
            fprintf( stderr, "\tParameter TotalEnergyOfInitialRun not found in input file.\n");
        }
        break;
      case SDDS_CHECK_WRONGUNITS:
      case SDDS_CHECK_WRONGTYPE:
        fprintf( stderr, "Something wrong with parameter TotalEnergyOfInitialRun.\n");
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        exit(1);
        break;
      case SDDS_CHECK_OKAY:
        TotalEnergyOfInitialRunDefined = 1;
        SDDS_GetParameter(&inputBeamPage, "TotalEnergyOfInitialRun", &TotalEnergyOfInitialRun);
        break;
      default:
        fprintf( stderr, "Unexpected result from SDDS_CheckParameter routine while checking parameter TotalEnergyOfInitialRun.\n");
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        exit(1);
        break;
      }
      /* parameter CumulativeSamples */
      switch(SDDS_CheckParameter(&inputBeamPage, "CumulativeSamples", NULL, SDDS_DOUBLE, verbose?stderr:NULL)) {
      case SDDS_CHECK_NONEXISTENT:
        /* generate parameter */
        CumulativeSamples = 1;
        if (verbose&&Generation)
          fprintf( stderr, "\tParameter CumulativeSamples not found in input file. Assume value of 1.\n");
        break;
      case SDDS_CHECK_WRONGUNITS:
      case SDDS_CHECK_WRONGTYPE:
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        exit(1);
        break;
      case SDDS_CHECK_OKAY:
        SDDS_GetParameter(&inputBeamPage, "CumulativeSamples", &CumulativeSamples);
        break;
      default:
        fprintf( stderr, "Unexpected result from SDDS_CheckParameter routine while checking parameter CumulativeSamples.\n");
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        exit(1);
        break;
      }
    }
    if (Generation==0) {
      /* If first generation (=0), then calculate the total energy in the input file.
       * The output files must have this quantity as a parameter */
      EColumn  =  SDDS_GetColumnInDoubles(&inputBeamPage, "Energy");
      switch(SDDS_CheckColumn(&inputBeamPage, "Weight", NULL, SDDS_DOUBLE, verbose?stderr:NULL)) {
      case SDDS_CHECK_NONEXISTENT:
        weightDefined = 0;
        if (verbose)
          fprintf( stderr, "\tAssume ones for column Weight.\n");
        break;
      case SDDS_CHECK_WRONGTYPE:
      case SDDS_CHECK_WRONGUNITS:
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
        exit(1);
        break;
      }
      if (weightDefined)
        weightColumn  =  SDDS_GetColumnInDoubles(&inputBeamPage, "Weight");
      for (iRow = 0; iRow<rows; iRow++) {
        if (weightDefined)
          TotalEnergyOfInitialRun +=  EColumn[iRow] * weightColumn[iRow];
        else
          TotalEnergyOfInitialRun +=  EColumn[iRow];
      }
      TotalEnergyOfInitialRunDefined = 1; 
    }
  }
  SDDS_Terminate(&inputBeamPage);

  /**********************************************************\
   * update the CumulativeSamples and Generation parameters *
   \**********************************************************/
  CumulativeSamples *=  samples;
  Generation++;

  /*****************************************\
   * set up various pages and output files *
   \*****************************************/
  if (trajectoryPage_set) {
    if (trajectoryFilePending) {
      trajectoryFile  =  (char *) malloc((strlen(rootFilename)+strlen(".traj")+1)*sizeof(char));
      strcat(strcpy(trajectoryFile, rootFilename), ".traj");
    }
    if (verbose)
      fprintf( stderr, "Opening SDDS file \"%s\"\n", trajectoryFile);
    if (!SDDS_InitializeOutput(&trajectoryPage, SDDS_BINARY, 1, "Shower particle trajectories",
                               "Shower particle trajectories", trajectoryFile))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    defineColumnsAndParameters(&trajectoryPage, TRAJECTORY_COORDINATES);
    /* allocate coordinate arrays for buffering */
    showerBufferArrayGroup = (BUFFER_ARRAY_GROUP *)allocateBufferArrayGroup(TRAJECTORY_COORDINATES);
  }
  
  if (outOfBoundsPage_set) {
    if (outOfBoundsFilePending) {
      outOfBoundsFile  =  malloc((strlen(rootFilename)+strlen(".outOfBounds")+1)*sizeof(char));
      strcat(strcpy(outOfBoundsFile, rootFilename), ".outOfBounds");
    }
    if (verbose)
      fprintf( stderr, "Opening SDDS file \"%s\"\n", outOfBoundsFile);
    if (!SDDS_InitializeOutput(&outOfBoundsPage, SDDS_BINARY, 1, "Out of bounds particle trajectories", 
                               "Out of bounds particle trajectories", outOfBoundsFile))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    defineColumnsAndParameters(&outOfBoundsPage, FULL_COORDINATES);
    /* allocate coordinate arrays for buffering */
    outOfBoundsBufferArrayGroup = (BUFFER_ARRAY_GROUP *)allocateBufferArrayGroup(FULL_COORDINATES);
  }
  
  if (undefinedRegionPage_set) {
    if (undefinedRegionFilePending) {
      undefinedRegionFile  =  (char *) malloc((strlen(rootFilename)+strlen(".undefinedRegion")+1)*sizeof(char));
      strcat(strcpy(undefinedRegionFile, rootFilename), ".undefinedRegion");
    }
    if (verbose)
      fprintf( stderr, "Opening SDDS file \"%s\"\n", undefinedRegionFile);
    if (!SDDS_InitializeOutput(&undefinedRegionPage, SDDS_BINARY, 1, "Undefined region particle trajectories", 
                               "Undefined region particle trajectories", undefinedRegionFile))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    defineColumnsAndParameters(&undefinedRegionPage, FULL_COORDINATES);
    /* allocate coordinate arrays for buffering */
    undefinedBufferArrayGroup = (BUFFER_ARRAY_GROUP *)allocateBufferArrayGroup(FULL_COORDINATES);
  }

  /* read information contained in geometry file */
  iRegion = 0;
  fp_geometry  =  fopen_e(geometryFile, "r", 0);
  region  =  (REGION *) tmalloc( regionsMax * sizeof(REGION) );
  mode  = -1;
  
  while( get_namelist(s, 1024, fp_geometry) ) {
    substituteTagValue(s, 1024, macroTag, macroValue, macros);
    scan_namelist(&namelist_text, s);
    switch( match_string(namelist_text.group_name, command_names, N_COMMANDS, EXACT_MATCH) ) {
    case INITIAL_COORDINATES:    
      process_namelist(&initial_coordinates, &namelist_text);
      break;
    case CYLINDER:
      /* re-initialize some variables, leaving the rest sticky. */
      if (mode<0) {
        mode = match_string(namelist_text.group_name, modeNames, MODES, EXACT_MATCH);
      }
      else if (mode!= match_string(namelist_text.group_name, modeNames, MODES, EXACT_MATCH)) {
        bomb("Cannot mix geometry modes", NULL);
      }
      set_namelist_processing_flags(STICKY_NAMELIST_DEFAULTS);
      process_namelist(&cylinder, &namelist_text);
      /* check sign of radius variable */
      if ( cylinder_struct.r_lower_cm < 0 ) {
        fprintf( stderr, "** error ** r lower limit of %f cm is negative in cylinder definition \"%s\".\n", cylinder_struct.r_lower_cm, 
                cylinder_struct.label);
        print_namelist(stderr, &cylinder);
        exit(1);
      }
      if ( cylinder_struct.r_upper_cm < 0 ) {
        fprintf( stderr, "** error ** r upper limit of %f cm is negative in cylinder definition \"%s\".\n", cylinder_struct.r_upper_cm, 
                cylinder_struct.label);
        print_namelist(stderr, &cylinder);
        exit(1);
      }
      /* check that lower limits is really less than upper limits */
      if ( cylinder_struct.r_lower_cm >=  cylinder_struct.r_upper_cm ) {
        fprintf( stderr, "** error ** r lower limit is greater than the r upper limit in cylinder definition"
                " \"%s\".\n", cylinder_struct.label);
        print_namelist(stderr, &cylinder);
        exit(1);
      }
      /* determine axis of revolution for cylinder */
      /* intially the value of direction is -1 */
      if (axialDirection < 0) {
        if ( 0 > (axialDirection = match_string(cylinder_struct.axialDirection, directionNames, DIRECTIONS, EXACT_MATCH))) {
          fprintf( stderr, "** error ** Invalid axial direction given \"%s\".\n", 
                  cylinder_struct.axialDirection);
          print_namelist(stderr, &cylinder);
          exit(1);
        }
      }
      if (axialDirection >= 0 && axialDirection != match_string(cylinder_struct.axialDirection, directionNames, DIRECTIONS, EXACT_MATCH)) {
        fprintf( stderr, "** error ** Cannot mix axial directions.\n");
        print_namelist(stderr, &cylinder);
        exit(1);
      }

      switch( axialDirection ) {
      case X_DIRECTION:
        if ( cylinder_struct.x_lower_cm >=  cylinder_struct.x_upper_cm ) {
          fprintf( stderr, "** error ** x lower limit is greater than the x upper limit in cylinder definition"
                  " \"%s\".\n", cylinder_struct.label);
          print_namelist(stderr, &cylinder);
          exit(1);
        }
        axialSlices = cylinder_struct.x_slices;
        axialCoordinateLowerCM  = cylinder_struct.x_lower_cm;
        axialCoordinateUpperCM  = cylinder_struct.x_upper_cm;
        strcpy( axialDirectionName, "x");
        break;
      case Y_DIRECTION:
        if ( cylinder_struct.y_lower_cm >=  cylinder_struct.y_upper_cm ) {
          fprintf( stderr, "** error ** y lower limit is greater than the y upper limit in cylinder definition"
                  " \"%s\".\n", cylinder_struct.label);
          print_namelist(stderr, &cylinder);
          exit(1);
        }
        axialSlices = cylinder_struct.y_slices;
        axialCoordinateLowerCM  = cylinder_struct.y_lower_cm;
        axialCoordinateUpperCM  = cylinder_struct.y_upper_cm;
        strcpy( axialDirectionName, "y");
        break;
      case Z_DIRECTION:
        if ( cylinder_struct.z_lower_cm >=  cylinder_struct.z_upper_cm ) {
          fprintf( stderr, "** error ** z lower limit is greater than the z upper limit in cylinder definition"
                  " \"%s\".\n", cylinder_struct.label);
          print_namelist(stderr, &cylinder);
          exit(1);
        }
        axialSlices = cylinder_struct.z_slices;
        axialCoordinateLowerCM  = cylinder_struct.z_lower_cm;
        axialCoordinateUpperCM  = cylinder_struct.z_upper_cm;
        strcpy( axialDirectionName, "z");
        break;
      }
      
      /* copy cylinder information into REGION array regions */
      for (i_r_slice = 0; i_r_slice<cylinder_struct.r_slices; i_r_slice++) {
        for (i_axial_slice = 0; i_axial_slice<axialSlices; i_axial_slice++) {
          if ( iRegion >=  regionsMax ) {
            regionsMax +=  INCREMENT_ARRAY_SIZE;
            region  =  (REGION *) trealloc( region, regionsMax * sizeof(REGION) );
          }
          if (!cylinder_struct.label) {
            fprintf( stderr, "label not found.\n");
            print_namelist(stderr, &cylinder);
            exit(1);
          }
          if (!cylinder_struct.material) {
            fprintf( stderr, "material not found.\n");
            print_namelist(stderr, &cylinder);
            exit(1);
          }
          region[iRegion].shape  =  (char *) malloc((strlen("cylinder")+1)*sizeof(char));
          strcpy(region[iRegion].shape, "cylinder");
          region[iRegion].axialDirection = axialDirection;
          region[iRegion].axialDirectionName  =  (char *) malloc((strlen("z")+1)*sizeof(char));
          strcpy(region[iRegion].axialDirectionName, axialDirectionName);
          region[iRegion].label  =  (char *) malloc((strlen(cylinder_struct.label)+1)*sizeof(char));
          strcpy(region[iRegion].label, cylinder_struct.label);
          region[iRegion].material  =  (char *) malloc((strlen(cylinder_struct.material)+1)*sizeof(char));
          strcpy(region[iRegion].material, trim_spaces(cylinder_struct.material));
          if (cylinder_struct.r_slices == 1) {
            region[iRegion].r_lo  =  cylinder_struct.r_lower_cm;
            region[iRegion].r_up  =  cylinder_struct.r_upper_cm;
          }
          else {
            region[iRegion].r_lo  =  cylinder_struct.r_lower_cm 
              + i_r_slice*(cylinder_struct.r_upper_cm-cylinder_struct.r_lower_cm)/cylinder_struct.r_slices;
            region[iRegion].r_up  =  cylinder_struct.r_lower_cm
              + (i_r_slice + 1) * (cylinder_struct.r_upper_cm-cylinder_struct.r_lower_cm)/cylinder_struct.r_slices;
          }
          switch( axialDirection ) {
          case X_DIRECTION:
            if (axialSlices == 1) {
              region[iRegion].x_lo  =  axialCoordinateLowerCM;
              region[iRegion].x_up  =  axialCoordinateUpperCM;
            }
            else {
              region[iRegion].x_lo  =  axialCoordinateLowerCM 
                + i_axial_slice * (axialCoordinateUpperCM - axialCoordinateLowerCM)/ axialSlices;
              region[iRegion].x_up  =  axialCoordinateLowerCM
                + (i_axial_slice + 1) * (axialCoordinateUpperCM - axialCoordinateLowerCM)/ axialSlices;
            }
            region[iRegion].xMidpoint  =  (region[iRegion].x_lo + region[iRegion].x_up )/200;
            break;
          case Y_DIRECTION:
            if (axialSlices == 1) {
              region[iRegion].y_lo  =  axialCoordinateLowerCM;
              region[iRegion].y_up  =  axialCoordinateUpperCM;
            }
            else {
              region[iRegion].y_lo  =  axialCoordinateLowerCM 
                + i_axial_slice * (axialCoordinateUpperCM - axialCoordinateLowerCM)/ axialSlices;
              region[iRegion].y_up  =  axialCoordinateLowerCM
                + (i_axial_slice + 1) * (axialCoordinateUpperCM - axialCoordinateLowerCM)/ axialSlices;
            }
            region[iRegion].yMidpoint  =  (region[iRegion].y_lo + region[iRegion].y_up )/200;
            break;
          case Z_DIRECTION:
            if (axialSlices == 1) {
              region[iRegion].z_lo  =  axialCoordinateLowerCM;
              region[iRegion].z_up  =  axialCoordinateUpperCM;
            }
            else {
              region[iRegion].z_lo  =  axialCoordinateLowerCM 
                + i_axial_slice * (axialCoordinateUpperCM - axialCoordinateLowerCM)/ axialSlices;
              region[iRegion].z_up  =  axialCoordinateLowerCM
                + (i_axial_slice + 1) * (axialCoordinateUpperCM - axialCoordinateLowerCM)/ axialSlices;
            }
            region[iRegion].zMidpoint  =  (region[iRegion].z_lo + region[iRegion].z_up )/200;
            break;
          }            
          /* midpoints in MKS units */
          region[iRegion].rMidpoint  =  (region[iRegion].r_lo + region[iRegion].r_up )/200;
          region[iRegion].Bx  =  cylinder_struct.Bx;
          region[iRegion].By  =  cylinder_struct.By;
          region[iRegion].Bz  =  cylinder_struct.Bz;
          region[iRegion].Ex  =  cylinder_struct.Ex;
          region[iRegion].Ey  =  cylinder_struct.Ey;
          region[iRegion].Ez  =  cylinder_struct.Ez;
          if( (IS_NONZERO(region[iRegion].Bx) + IS_NONZERO(region[iRegion].By) + 
               IS_NONZERO(region[iRegion].Bz) + IS_NONZERO(region[iRegion].Ex) + 
               IS_NONZERO(region[iRegion].Ey) + IS_NONZERO(region[iRegion].Ez)) > 1) {
            fprintf( stderr, "Only one of Bx, By, Bz, Ex, Ey, or Ez may be non-zero.\nOffending region is %s.\n", region[iRegion].label);
          }
          region[iRegion].photonEnergyCutoff  =  cylinder_struct.photonEnergyCutoff;
          region[iRegion].leptonEnergyCutoff  =  cylinder_struct.leptonEnergyCutoff;
          region[iRegion].split  =  cylinder_struct.split;
          region[iRegion].minimumWeight  =  cylinder_struct.minimumWeight;
          region[iRegion].maximumDeltaE  =  cylinder_struct.maximumDeltaE;
          region[iRegion].electronPathLengthEnergyCutoff  =  cylinder_struct.electronPathLengthEnergyCutoff;
          region[iRegion].positronPathLengthEnergyCutoff  =  cylinder_struct.positronPathLengthEnergyCutoff;
          region[iRegion].photonPathLengthEnergyCutoff  =  cylinder_struct.photonPathLengthEnergyCutoff;
          /* open an output file, initialize SDDS_DATASET, and allocate buffer arrays */
          if (cylinder_struct.outputfile) {
            if ( i_r_slice || i_axial_slice ) {
              fprintf( stderr, "Sliced up region %s can't be made an output region. May be implemented in the future.\n", 
                      cylinder_struct.label);
            }
            region[iRegion].page  =  (SDDS_DATASET *) malloc(sizeof(SDDS_DATASET));
            RegionFileDescription  =  (char*)malloc((strlen("Coordinates of particles entering the region ")
                                                     +strlen(cylinder_struct.label)+6)*sizeof(char));
            sprintf( RegionFileDescription, "Coordinates of particles entering the \"%s\" region", 
                    cylinder_struct.label);
            cylinder_struct.outputfile = compose_filename( cylinder_struct.outputfile, rootFilename);
            if (!SDDS_InitializeOutput(region[iRegion].page, SDDS_BINARY, 1, RegionFileDescription, 
                                       "Particle coordinates", cylinder_struct.outputfile))
              SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
            free(RegionFileDescription);
            fprintf( stderr, "Opening SDDS file \"%s\" for region \"%s\".\n", cylinder_struct.outputfile, cylinder_struct.label);
            defineColumnsAndParameters(region[iRegion].page, FULL_COORDINATES);
            /* allocate coordinate arrays for buffering */
            region[iRegion].bufferArrayGroup = (BUFFER_ARRAY_GROUP *)allocateBufferArrayGroup(FULL_COORDINATES);
          }
          else {
            region[iRegion].page  =  NULL;
            region[iRegion].bufferArrayGroup  =  NULL;
          }
          iRegion ++;
        }
      }
      break;
    case BLOCK:
      if (mode<0) {
        mode = match_string(namelist_text.group_name, modeNames, MODES, EXACT_MATCH);
      }
      else if (mode!= match_string(namelist_text.group_name, modeNames, MODES, EXACT_MATCH)) {
        bomb("Cannot mix geometry modes", NULL);
      }
      set_namelist_processing_flags(STICKY_NAMELIST_DEFAULTS);
      process_namelist(&block, &namelist_text);
      /* check that lower limits is really less than upper limits */
      if ( block_struct.x_lower_cm >=  block_struct.x_upper_cm ) {
        fprintf( stderr, "** error ** x lower limit is greater than the x upper limit in block definition"
                " \"%s\".\n", block_struct.label);
        print_namelist(stderr, &block);
        exit(1);
      }
      if ( block_struct.y_lower_cm >=  block_struct.y_upper_cm ) {
        fprintf( stderr, "** error ** y lower limit is greater than the y upper limit in block definition"
                " \"%s\".\n", block_struct.label);
        print_namelist(stderr, &block);
        exit(1);
      }
      if ( block_struct.z_lower_cm >=  block_struct.z_upper_cm ) {
        fprintf( stderr, "** error ** z lower limit is greater than the z upper limit in block definition"
                " \"%s\".\n", block_struct.label);
        print_namelist(stderr, &block);
        exit(1);
      }
      /* copy block information into REGION array regions */
      for (i_x_slice = 0; i_x_slice<block_struct.x_slices; i_x_slice++) {
        for (i_y_slice = 0; i_y_slice<block_struct.y_slices; i_y_slice++) {
          for (i_z_slice = 0; i_z_slice<block_struct.z_slices; i_z_slice++) {
            if ( iRegion >=  regionsMax ) {
              regionsMax +=  INCREMENT_ARRAY_SIZE;
              region  =  (REGION *) trealloc( region, regionsMax * sizeof(REGION) );
            }
            if (!block_struct.label) {
              fprintf( stderr, "label not found.\n");
              print_namelist(stderr, &block);
              exit(1);
            }
            if (!block_struct.material) {
              fprintf( stderr, "material not found.\n");
              print_namelist(stderr, &block);
              exit(1);
            }
            region[iRegion].label  =  (char *) malloc((strlen(block_struct.label)+1)*sizeof(char));
            strcpy(region[iRegion].label, block_struct.label);
            region[iRegion].material  =  (char *) malloc((strlen(block_struct.material)+1)*sizeof(char));
            strcpy(region[iRegion].material, trim_spaces(block_struct.material));
            if (block_struct.x_slices == 1) {
              region[iRegion].x_lo  =  block_struct.x_lower_cm;
              region[iRegion].x_up  =  block_struct.x_upper_cm;
            }
            else {
              region[iRegion].x_lo  =  block_struct.x_lower_cm 
                + i_x_slice*(block_struct.x_upper_cm-block_struct.x_lower_cm)/block_struct.x_slices;
              region[iRegion].x_up  =  block_struct.x_lower_cm
                + (i_x_slice + 1) * (block_struct.x_upper_cm-block_struct.x_lower_cm)/block_struct.x_slices;
            }
            if (block_struct.y_slices == 1) {
              region[iRegion].y_lo  =  block_struct.y_lower_cm;
              region[iRegion].y_up  =  block_struct.y_upper_cm;
            }
            else {
              region[iRegion].y_lo  =  block_struct.y_lower_cm 
                + i_y_slice*(block_struct.y_upper_cm-block_struct.y_lower_cm)/block_struct.y_slices;
              region[iRegion].y_up  =  block_struct.y_lower_cm
                + (i_y_slice + 1) * (block_struct.y_upper_cm-block_struct.y_lower_cm)/block_struct.y_slices;
            }
            if (block_struct.z_slices == 1) {
              region[iRegion].z_lo  =  block_struct.z_lower_cm;
              region[iRegion].z_up  =  block_struct.z_upper_cm;
            }
            else {
              region[iRegion].z_lo  =  block_struct.z_lower_cm 
                + i_z_slice*(block_struct.z_upper_cm-block_struct.z_lower_cm)/block_struct.z_slices;
              region[iRegion].z_up  =  block_struct.z_lower_cm
                + (i_z_slice + 1) * (block_struct.z_upper_cm-block_struct.z_lower_cm)/block_struct.z_slices;
            }
            /* midpoint in MKS units */
            region[iRegion].xMidpoint  =  (region[iRegion].x_lo + region[iRegion].x_up )/200;
            region[iRegion].yMidpoint  =  (region[iRegion].y_lo + region[iRegion].y_up )/200;
            region[iRegion].zMidpoint  =  (region[iRegion].z_lo + region[iRegion].z_up )/200;
            region[iRegion].Bx  =  block_struct.Bx;
            region[iRegion].By  =  block_struct.By;
            region[iRegion].Bz  =  block_struct.Bz;
            region[iRegion].Ex  =  block_struct.Ex;
            region[iRegion].Ey  =  block_struct.Ey;
            region[iRegion].Ez  =  block_struct.Ez;
            if( (IS_NONZERO(region[iRegion].Bx) + IS_NONZERO(region[iRegion].By) + 
                 IS_NONZERO(region[iRegion].Bz) + IS_NONZERO(region[iRegion].Ex) + 
                 IS_NONZERO(region[iRegion].Ey) + IS_NONZERO(region[iRegion].Ez)) > 1) {
              fprintf( stderr, "Only one of Bx, By, Bz, Ex, Ey, or Ez may be non-zero.\nOffending region is %s.\n", region[iRegion].label);
            }
            region[iRegion].photonEnergyCutoff  =  block_struct.photonEnergyCutoff;
            region[iRegion].leptonEnergyCutoff  =  block_struct.leptonEnergyCutoff;
            region[iRegion].split  =  block_struct.split;
            region[iRegion].minimumWeight  =  block_struct.minimumWeight;
            region[iRegion].maximumDeltaE  =  block_struct.maximumDeltaE;
            region[iRegion].electronPathLengthEnergyCutoff  =  block_struct.electronPathLengthEnergyCutoff;
            region[iRegion].positronPathLengthEnergyCutoff  =  block_struct.positronPathLengthEnergyCutoff;
            region[iRegion].photonPathLengthEnergyCutoff  =  block_struct.photonPathLengthEnergyCutoff;

            /* open an output file, initialize SDDS_DATASET, and allocate buffer arrays */
            if (block_struct.outputfile) {
              if ( i_x_slice || i_y_slice || i_z_slice ) {
                fprintf( stderr, "Sliced up region %s can't be made an output region. May be implemented in the future.\n", 
                        block_struct.label);
              }
              region[iRegion].page  =  (SDDS_DATASET *) malloc(sizeof(SDDS_DATASET));
              RegionFileDescription  =  (char*)malloc((strlen("Coordinates of particles entering the region ")
                                                       +strlen(block_struct.label)+6)*sizeof(char));
              sprintf( RegionFileDescription, "Coordinates of particles entering the \"%s\" region", 
                      block_struct.label);
              block_struct.outputfile = compose_filename( block_struct.outputfile, rootFilename);
              if (!SDDS_InitializeOutput(region[iRegion].page, SDDS_BINARY, 1, RegionFileDescription, 
                                         "Particle coordinates", block_struct.outputfile))
                SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
              free(RegionFileDescription);
              fprintf( stderr, "Opening SDDS file \"%s\" for region \"%s\".\n", block_struct.outputfile, block_struct.label);
              defineColumnsAndParameters(region[iRegion].page, FULL_COORDINATES);
              /* allocate coordinate arrays for buffering */
              region[iRegion].bufferArrayGroup = (BUFFER_ARRAY_GROUP *)allocateBufferArrayGroup(FULL_COORDINATES);
            }
            else {
              region[iRegion].page  =  NULL;
              region[iRegion].bufferArrayGroup  =  NULL;
            }
            iRegion ++;
          } 
        }
      }
      break;
    default:
      fprintf( stderr, "Default:\n");
      fprintf( stderr, "Unknown namelist %s given in geometry file %s. Known namelists are:\n", namelist_text.group_name, 
              geometryFile);
      for (i = 0; i<N_COMMANDS; i++) {
        fprintf( stderr, "%s\n", GeomCommDescription[i]);
      }
      exit(1);
    }
  }

  regions = iRegion;
  /****************************************\
   * Define two extra regions:
   * one for undefined regions within the plane boundaries, and
   * one for out of bounds
   * very few structure elements for these regions are allocated.
   \****************************************/
  if ( (regions+2) > MXREG ) {
    fprintf( stderr, "Too many regions defined. The maximum is %d while %"PRId32" was requested. Recompiling egs4 is recommended\n", 
            MXREG, (regions+2));
    exit(1);
  }
  if ( (regions+1) >=  regionsMax ) {
    regionsMax +=  INCREMENT_ARRAY_SIZE;
    region  =  (REGION *) trealloc( region, regionsMax  * sizeof(REGION) );
  }
  undefinedRegion  =  &region[regions];
  undefinedRegion->label  =  (char *) malloc((strlen("Undefined region within boundaries")+1)*sizeof(char));
  strcpy(undefinedRegion->label, "Undefined region within boundaries");
  undefinedRegion->materialCode = 0; /* undefined taken as vacuum in some cases */
  undefinedRegion->bufferArrayGroup  =  undefinedBufferArrayGroup;
  undefinedRegion->page  =  &undefinedRegionPage;
  if ( (regions+2) >=  regionsMax ) {
    regionsMax +=  INCREMENT_ARRAY_SIZE;
    region  =  (REGION *) trealloc( region, regionsMax  * sizeof(REGION) );
  }
  outOfBoundsRegion  =  &region[regions+1];
  outOfBoundsRegion->label  =  (char *) malloc((strlen("Out of bounds region")+1)*sizeof(char));
  strcpy(outOfBoundsRegion->label, "Out of bounds region");
  outOfBoundsRegion->materialCode = 0; /* out-of-bounds taken as vacuum in some cases */
  outOfBoundsRegion->bufferArrayGroup  =  outOfBoundsBufferArrayGroup;
  outOfBoundsRegion->page  =  &outOfBoundsPage;
  
  /* setup grid  */
  if (verbose)
    fprintf( stderr, "Setting up grid...\n");
  (void) setupGrid[mode](region, regions);
  
  /* energy deposition file setup needs to follow the geometry processing because some geometry information is used. */    
  if (summaryFilePending) {
    summaryFile  =  (char *) malloc((strlen(rootFilename)+strlen(".summary")+1)*sizeof(char));
    strcat(strcpy(summaryFile, rootFilename), ".summary");
  }
  if (verbose)
    fprintf( stderr, "Opening SDDS file  \"%s\"\n", summaryFile);
  SDDS_InitializeOutput(&summaryPage, SDDS_ASCII, 1, "Summary file", 
                        "Summary file", summaryFile);
  for (i=0;  i<=PARTICLE_TYPES;  i++) {
    EDepParticle[i]  =  (double *) malloc(regions*sizeof(double));
    for (iRegion = 0; iRegion<regions; iRegion++)
      EDepParticle[i][iRegion]  =  0.0;
  }

  /* fill some fortran common block variables */
  bounds_.vacuum_step_default  =  (float)vacuum_step_default;
  if (verbose)
    fprintf( stderr, "Defining egs materials...\n");
  /* define material = 0 as vacuum. Vacuum isn't considered a material in egs */
  /* so the indices are shifted */
  materials = 1;
  material  =  (char **) malloc( sizeof(char *));   /* start by allocating only one character array */
  material[0]  =  (char *) malloc( (strlen("Vacuum")+1)*sizeof(char));
  strcpy(material[0], "Vacuum");
  /* find number of materials used in normal regions 
     (out-of-bound and undefined of course are not included. */
  for (iRegion = 0; iRegion < regions; iRegion++) {
    /* for each region find the material code */
    materialCodeFound = 0;
    for (iMaterial = 0; iMaterial < materials; iMaterial++) {
      if ( !strcmp(str_toupper(region[iRegion].material), str_toupper(material[iMaterial])) ) {
        region[iRegion].materialCode  =  iMaterial;
        misc_.medium_index[iRegion]  =  iMaterial;
        bounds_.leptonEnergyCutoff[iRegion]  =  (float)region[iRegion].leptonEnergyCutoff;
        bounds_.photonEnergyCutoff[iRegion]  =  (float)region[iRegion].photonEnergyCutoff;
        materialCodeFound  =  1;
        break;
      }
    }
    if (!materialCodeFound) {
      /* new material */
      if ( materials > MXMED ) {
        fprintf( stderr, "**Error** The number of materials defined in file %s exceeds the maximum (MXMED) allowed\nby the current"
                " compiled version of egs4.", geometryFile);      
        abort();
      }
      region[iRegion].materialCode  =  materials;
      misc_.medium_index[iRegion]  =  materials;
      material  =  (char **) trealloc( material, (materials+1)*sizeof(char *));
      material[materials]  =  (char *) malloc( (strlen(region[iRegion].material)+1) * sizeof(char));
      strcpy(material[materials], str_toupper(region[iRegion].material));
      char_ptr1 =  &mediac_.media_labels[materials-1][0];
      /* padded_labal has room for 24 characters */
      strcpy(padded_label, material[materials]);
      char_ptr2 = pad_with_spaces(padded_label, 24-strlen(padded_label));
      /* I had to do this because egs system uses only the first character */
      /* of the character*4 variable */
      while((*char_ptr1 =  *char_ptr2)) {
        char_ptr1++;
        /* add three spaces */
        strcpy(char_ptr1, "   ");
        char_ptr1++; char_ptr1++; char_ptr1++;
        char_ptr2++;
      }
      materials++;
    }
  }

  /* include undefined and out-of-bounds regions (the last two regions) as vacuum media */
  misc_.medium_index[regions]  =  0;
  misc_.medium_index[regions+1]  =  0;
  /* HATCH doesn't work if NMED = 0. So I have to make a default medium
     using the string "AL". Aluminum must be defined in fort.12 too. */
  if (materials == 1) {
    char_ptr1 =  &mediac_.media_labels[0][0];
    /* padded_label has room for 25 characters */
    strcpy(padded_label, "AL");
    char_ptr2 = pad_with_spaces(padded_label, 24-strlen(padded_label));
    /* I had to do this because egs system uses only the first character */
    /* of the character*4 variable */
    while((*char_ptr1 =  *char_ptr2)) {
      char_ptr1++;
      /* add three spaces */
      strcpy(char_ptr1, "   ");
      char_ptr1++; char_ptr1++; char_ptr1++;
      char_ptr2++;
    }
    media_.no_media  =  1;
  }
  else
    media_.no_media = materials-1;   
  if ((egs4_crosssectionFile  =  getenv("EGS4_CROSSSECTION"))) {
#ifdef _WIN32
    sprintf(shell_command, "copy %s fort.12", egs4_crosssectionFile);
    if (system(shell_command)) {
      sprintf(shell_command, "cp %s fort.12", egs4_crosssectionFile);
      if (system(shell_command)) {
       fprintf( stderr, "Error: Problem in copying file %s to fort.12.\n", egs4_crosssectionFile);
	exit(1);
      }
    }
#else
    sprintf(shell_command, "ln -s %s fort.12", egs4_crosssectionFile);
    if (system(shell_command)) {
      fprintf( stderr, "Error: Problem in linking file %s to fort.12.\n", egs4_crosssectionFile);
      exit(1);
    }
#endif
    else {
      if ((egs4_crosssection_fp = fopen(egs4_crosssectionFile, "r")))
        fclose(egs4_crosssection_fp);  /* file opens with no error, therefore it exists */
      else
      {
        fprintf( stderr, "Error: Can't find cross-section file %s.\n", egs4_crosssectionFile);
        exit(1);
      }
    }
  }
  else /* environment variable not defined */
    /* check to see if file fort.12 exists */
  {
    if ((fort12_fp = fopen("fort.12", "r"))) {
      fclose(fort12_fp);  /* file opens with no error, therefore it exists */
    }
    else
    {
      bomb("Variable EGS4_CROSSSECTION not defined and cross-section file fort.12 can't be found.\n", NULL);
    }
  }
#ifndef EXCLUDE_FORTRAN
  HATCH();
#endif

  /*  close(egs4_crosssection_fp);*/ /* I have a question about this */
  if ((egs4_crosssectionFile  =  getenv("EGS4_CROSSSECTION"))) {
#ifdef _WIN32
    remove("fort.12");
#else
    system("rm fort.12");
#endif
  }
  if (fixtmxFlag) {
    for (iRegion = 0; iRegion <= regions; iRegion++) {
      if (region[iRegion].materialCode) {
        maximumDeltaE = (float)region[iRegion].maximumDeltaE;
#ifndef EXCLUDE_FORTRAN
#ifdef _WIN32
        FIXTMX(&maximumDeltaE, &(region[iRegion].materialCode));
#else
        FIXTMX(maximumDeltaE, (region[iRegion].materialCode));
#endif
#endif
      }
    }
  }
  /* fill Density member of region structure array */
  /* Convert from cgs to mks */
  for (iRegion = 0; iRegion <= regions; iRegion++) {
    if (region[iRegion].materialCode) {
      region[iRegion].Density = misc_.density_cgs[iRegion]*1e3;
      if (verbose) {
        fprintf( stderr, "material[%"PRId32"] \"%s\" %g kg/m^3.\n", iRegion, region[iRegion].label, region[iRegion].Density);
      }
    }
    else {
      region[iRegion].Density = 0;
    }
  }
  
  /* check geometry after material numbers have been assigned */
  if (plotGeometry_set) {
    if (plotGeometryFilePending) {
      plotGeometryFile = (char *) malloc((strlen(rootFilename)+strlen(".line_segments")+1)*sizeof(char));
      strcat(strcpy(plotGeometryFile, rootFilename), ".line_segments");
    }
    if (verbose)
      fprintf( stderr, "Opening SDDS file  \"%s\"\n", plotGeometryFile);

    /* function plotGeometry is disabled for now in the cylindrical version of shower */
    (void) plotGeometry[mode](region, plotGeometryFile);
  }

  /* write layouts and constant values of SDDS parameters */
  if (outOfBoundsFile) {
    if (!SDDS_WriteLayout(&outOfBoundsPage) || 0>SDDS_StartPage(&outOfBoundsPage, 0) ||
        !SDDS_SetParameters(&outOfBoundsPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 
                            "Generation", Generation, 
                            "TotalEnergy", (double)0, 
                            "PowerOfInitialBeam", PowerOfInitialBeam, 
                            "CumulativeSamples", CumulativeSamples, 
                            "InputBeamFile", inputfile,
                            "GeometryDefinitionFile", geometryFile,
                            "SummaryFile", summaryFile,
                            NULL))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    if (TotalEnergyOfInitialRunDefined) {
      /* pass on this parameter to next generation */
      if (!SDDS_SetParameters(&outOfBoundsPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 
                              "TotalEnergyOfInitialRun", TotalEnergyOfInitialRun, 
                              NULL))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    }
  }
  
  if (undefinedRegionFile) {
    if (!SDDS_WriteLayout(&undefinedRegionPage) || 0>SDDS_StartPage(&undefinedRegionPage, 0) ||
        !SDDS_SetParameters(&undefinedRegionPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 
                            "Generation", Generation, 
                            "TotalEnergy", (double)0, 
                            "PowerOfInitialBeam", PowerOfInitialBeam, 
                            "CumulativeSamples", CumulativeSamples, 
                            "InputBeamFile", inputfile,
                            "GeometryDefinitionFile", geometryFile,
                            "SummaryFile", summaryFile,
                            NULL))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    if (TotalEnergyOfInitialRunDefined) {
      /* pass on this parameter to next generation */
      if (!SDDS_SetParameters(&undefinedRegionPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 
                              "TotalEnergyOfInitialRun", TotalEnergyOfInitialRun, 
                              NULL))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    }
  }
    
  for (iRegion = 0; iRegion<regions; iRegion++) {
    if (region[iRegion].page) {
      if (!SDDS_WriteLayout(region[iRegion].page) || 0>SDDS_StartPage(region[iRegion].page, 0))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
      if (!SDDS_SetParameters(region[iRegion].page, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 
                              "Generation", Generation, 
                              "TotalEnergy", (double)0, 
                              "PowerOfInitialBeam", PowerOfInitialBeam, 
                              "CumulativeSamples", CumulativeSamples, 
                              "InputBeamFile", inputfile,
                              "GeometryDefinitionFile", geometryFile,
                              "SummaryFile", summaryFile,
                              NULL))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
      if (TotalEnergyOfInitialRunDefined) {
        /* pass on this parameter to next generation */
        if (!SDDS_SetParameters(region[iRegion].page, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 
                                "TotalEnergyOfInitialRun", TotalEnergyOfInitialRun, 
                                NULL))
          SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
      }
    }
  }
  if (trajectoryFile) {
    if (!SDDS_WriteLayout(&trajectoryPage) || 0>SDDS_StartPage(&trajectoryPage, 0) ||
        !SDDS_SetParameters(&trajectoryPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 
                            "Generation", Generation, 
                            "TotalEnergy", (double)0, 
                            "PowerOfInitialBeam", PowerOfInitialBeam, 
                            "CumulativeSamples", CumulativeSamples, 
                            "InputBeamFile", inputfile,
                            "GeometryDefinitionFile", geometryFile,
                            "SummaryFile", summaryFile,
                            NULL))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    if (TotalEnergyOfInitialRunDefined) {
      /* pass on this parameter to next generation */
      if (!SDDS_SetParameters(&trajectoryPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 
                              "TotalEnergyOfInitialRun", TotalEnergyOfInitialRun, 
                              NULL))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    }
  }
  
  /* set ausgab flags */
  for (i_flag = 0; i_flag<MXAUS; i_flag++) {
    epcont_.ausgab_flag[i_flag] = 0;
  }

/* for debugging, all ausgab flags are set */
#ifdef DEBUG
  for (i_flag = 0; i_flag<MXAUS; i_flag++) {
    epcont_.ausgab_flag[i_flag] = 1;
  }
#endif

/* I don't remember why some data is recorded in summary file with 
BEFORE_TRANSPORT rather than AFTER_TRANSPORT. Is it because
the region may already be changed in the AFTER_TRANSPORT phase? */
  if (sumPathLengths) 
    epcont_.ausgab_flag[BEFORE_TRANSPORT] = 1;
  if (addTimeColumn) {
    epcont_.ausgab_flag[AFTER_TRANSPORT] = 1;
  }
  
  if (trajectoryPage_set) {
    epcont_.ausgab_flag[USER_ENERGY_CUTOFF_DISCARD] = 1;
    epcont_.ausgab_flag[PEGS_ENERGY_CUTOFF_DISCARD] = 1;
    epcont_.ausgab_flag[USER_REQUESTED_DISCARD] = 1;
    epcont_.ausgab_flag[AFTER_TRANSPORT] = 1;
  }
  if (summary) {
    epcont_.ausgab_flag[BEFORE_TRANSPORT] = 1;
    epcont_.ausgab_flag[USER_ENERGY_CUTOFF_DISCARD] = 1;
    epcont_.ausgab_flag[PEGS_ENERGY_CUTOFF_DISCARD] = 1;
    epcont_.ausgab_flag[PHOTOELECTRIC_INTERACTION] = 1;
    epcont_.ausgab_flag[PEGS_ENERGY_CUTOFF_DISCARD] = 1;
  }
  /* default for output file */
  epcont_.ausgab_flag[USER_REQUESTED_DISCARD] = 1;
  
  /* iParticle keeps track of the total number of particles in the input beam files */
  iParticle = 0;  
  TotalEnergy  =  0.0;  
  /* read in particle from SDDS input file */
  SDDS_InitializeInput(&inputBeamPage, inputfile);
  /* check columns in the input file */
  xDefined = yDefined = zDefined = uDefined = vDefined = wDefined = weightDefined = 1;
  switch(SDDS_CheckColumn(&inputBeamPage, "x", "m", SDDS_DOUBLE, verbose?stderr:NULL)) {
  case SDDS_CHECK_NONEXISTENT:
    xDefined = 0;
    if (verbose)
      fprintf( stderr, "\tAssume zero values for column x.\n");
    break;
  case SDDS_CHECK_WRONGTYPE:
  case SDDS_CHECK_WRONGUNITS:
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    exit(1);
    break;
  }
  switch(SDDS_CheckColumn(&inputBeamPage, "y", "m", SDDS_DOUBLE, verbose?stderr:NULL)) {
  case SDDS_CHECK_NONEXISTENT:
    yDefined = 0;
    if (verbose)
      fprintf( stderr, "\tAssume zero values for column y.\n");
    break;
  case SDDS_CHECK_WRONGTYPE:
  case SDDS_CHECK_WRONGUNITS:
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    exit(1);
    break;
  }
  switch(SDDS_CheckColumn(&inputBeamPage, "z", "m", SDDS_DOUBLE, verbose?stderr:NULL)) {
  case SDDS_CHECK_NONEXISTENT:
    zDefined = 0;
    if (verbose)
      fprintf( stderr, "\tAssume zero values for column z.\n");
    break;
  case SDDS_CHECK_WRONGTYPE:
  case SDDS_CHECK_WRONGUNITS:
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    exit(1);
    break;
  }
  switch(SDDS_CheckColumn(&inputBeamPage, "u", NULL, SDDS_DOUBLE, verbose?stderr:NULL)) {
  case SDDS_CHECK_NONEXISTENT:
    uDefined = 0;
    if (verbose)
      fprintf( stderr, "\tAssume zero values for column u.\n");
    break;
  case SDDS_CHECK_WRONGTYPE:
  case SDDS_CHECK_WRONGUNITS:
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    exit(1);
    break;
  }
  switch(SDDS_CheckColumn(&inputBeamPage, "v", NULL, SDDS_DOUBLE, verbose?stderr:NULL)) {
  case SDDS_CHECK_NONEXISTENT:
    vDefined = 0;
    if (verbose)
      fprintf( stderr, "\tAssume zero values for column v.\n");
    break;
  case SDDS_CHECK_WRONGTYPE:
  case SDDS_CHECK_WRONGUNITS:
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    exit(1);
    break;
  }
  switch(SDDS_CheckColumn(&inputBeamPage, "w", NULL, SDDS_DOUBLE, verbose?stderr:NULL)) {
  case SDDS_CHECK_NONEXISTENT:
    wDefined = 0;
    if (verbose)
      fprintf( stderr, "\tAssume zero values for column w.\n");
    break;
  case SDDS_CHECK_WRONGTYPE:
  case SDDS_CHECK_WRONGUNITS:
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    exit(1);
    break;
  }
  switch(SDDS_CheckColumn(&inputBeamPage, "Weight", NULL, SDDS_DOUBLE, verbose?stderr:NULL)) {
  case SDDS_CHECK_NONEXISTENT:
    weightDefined = 0;
    if (verbose)
      fprintf( stderr, "\tAssume ones for column Weight.\n");
    break;
  case SDDS_CHECK_WRONGTYPE:
  case SDDS_CHECK_WRONGUNITS:
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    exit(1);
    break;
  }
  if (useTimeColumn) {
    tDefined = 1;
    switch(SDDS_CheckColumn(&inputBeamPage, "t", "s", SDDS_DOUBLE, verbose?stderr:NULL)) {
    case SDDS_CHECK_NONEXISTENT:
      tDefined = 0;
      if (verbose)
      fprintf( stderr, "\tAssume zero values for column t.\n");
      break;
    case SDDS_CHECK_WRONGTYPE:
    case SDDS_CHECK_WRONGUNITS:
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
      exit(1);
      break;
    }
  }
  switch(SDDS_CheckColumn(&inputBeamPage, "Energy", "MeV", SDDS_DOUBLE, stderr)) {
  case SDDS_CHECK_NONEXISTENT:
  case SDDS_CHECK_WRONGTYPE:
  case SDDS_CHECK_WRONGUNITS:
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    exit(1);
    break;
  }
  
  /* shower loop */
  /* loop over pages in the input beam file */
  while(SDDS_ReadPage(&inputBeamPage)>0) {
    if (useDefaultParticleType) {
      input_particle_type = default_particle_type;
    }
    else {
      if (!(SDDS_GetParameter(&inputBeamPage, "Type", &Type))) {
        fprintf( stderr, "Problem with reading parameter Type in inputfile.\n");
        exit(1);
      }
      if (0>(input_particle_type = match_string(Type, particleNames, PARTICLE_TYPES, UNIQUE_MATCH))) {
        fprintf( stderr, "Invalid parameter Type value: %s.\n", Type);
      }
    }
    integerCharge  =  particleCharge[input_particle_type];
    /* if any of the columns x, y, z, u, v, w are undefined, then assume a value of 0 */
    /* copy columns into arrays first, then assign initial coordinates from these arrays */
    rows = SDDS_CountRowsOfInterest(&inputBeamPage);
    if (xDefined)
      xColumn  =  SDDS_GetColumnInDoubles(&inputBeamPage, "x");
    if (yDefined)
      yColumn  =  SDDS_GetColumnInDoubles(&inputBeamPage, "y");
    if (zDefined)
      zColumn  =  SDDS_GetColumnInDoubles(&inputBeamPage, "z");
    if (uDefined)
      uColumn  =  SDDS_GetColumnInDoubles(&inputBeamPage, "u");
    if (vDefined)
      vColumn  =  SDDS_GetColumnInDoubles(&inputBeamPage, "v");
    if (wDefined)
      wColumn  =  SDDS_GetColumnInDoubles(&inputBeamPage, "w");
    if (weightDefined) 
      weightColumn  =  SDDS_GetColumnInDoubles(&inputBeamPage, "Weight");
    if (useTimeColumn && tDefined)
      tColumn  =  SDDS_GetColumnInDoubles(&inputBeamPage, "t");
    EColumn  =  SDDS_GetColumnInDoubles(&inputBeamPage, "Energy");
    
    /* loop over particles in each input beam page */
    for (iRow = 0; iRow<rows; iRow++) {
      if (xDefined)
        x = xColumn[iRow]*100 + initial_coordinates_struct.x_cm;
      else
        x = initial_coordinates_struct.x_cm;
      if (yDefined)
        y = yColumn[iRow]*100 + initial_coordinates_struct.y_cm;
      else
        y = initial_coordinates_struct.y_cm;
      if (zDefined)
        z = zColumn[iRow]*100 + initial_coordinates_struct.z_cm;
      else
        z = initial_coordinates_struct.z_cm;
      if (uDefined)
        u = uColumn[iRow] + initial_coordinates_struct.xp;
      else
        u = initial_coordinates_struct.xp;
      if (vDefined)
        v = vColumn[iRow] + initial_coordinates_struct.yp;
      else
        v = initial_coordinates_struct.yp;
      if (wDefined)
        w = wColumn[iRow];
      else
        w = 1;
      if (useTimeColumn && tDefined)
        t = tColumn[iRow];
      else
        t = 0;
      if (weightDefined)
        weight = (float)weightColumn[iRow];
      else
        weight = 1;
      /* normalize direction cosines */
      denom  =  sqrt(sqr(u)+sqr(v)+sqr(w));
      u /=  denom;
      v /=  denom;
      w /=  denom;
      energyMeV  =  EColumn[iRow];
      TotalEnergy +=  EColumn[iRow] * weight;
      initialRegion = (int32_t)findRegion[mode](x, y, z);
      /* test if particle is initially in an undefined region or out of bounds */
      if ( initialRegion == regions ||  initialRegion<0 ) {
        /* do nothing, particle is skipped. The particle will not appear
         * in undefinedRegion or outOfBounds file 
         */
        fprintf( stderr, "Particle %"PRId32" of energy %f MeV starting at x=%f, y=%f, z=%f is skipped because it is in an indefined region or out of bounds.\n", iRow, energyMeV, x, y, z);
      }
      else if ((integerCharge == ELECTRON_CHARGE) && (energyMeV < useful_.precisionRestMass) ) {
        fprintf( stderr, "Electron (particle %"PRId32") has insufficient energy %f MeV. Needs to have rest mass included. Particle skipped.\n", iRow, energyMeV);
      }
      else if ((integerCharge == POSITRON_CHARGE) && (energyMeV < useful_.precisionRestMass) ) {
        fprintf( stderr, "Positron (particle %"PRId32") has insufficient energy %f MeV. Needs to have rest mass included. Particle skipped.\n", iRow, energyMeV);
      }
      else
      {
        /* in egs, arrays start at 1 */
        region_egs  =  initialRegion + 1;
        for (iSample = 0; iSample < samples; iSample++) {
#ifndef EXCLUDE_FORTRAN
#ifdef _WIN32
          SHOWER(&integerCharge, &energyMeV, &x, &y, &z, &u, &v, &w, &t, &region_egs, &weight);
#else
          SHOWER(integerCharge, energyMeV, x, y, z, u, v, w, t, region_egs, weight);
#endif
#endif
        }
      }
      iParticle++;
    }
  }
  SDDS_Terminate(&inputBeamPage);
  /*********************************\
   * End of Monte Carlo simulation * 
   \*********************************/
  if (Generation == 1 && !TotalEnergyOfInitialRunDefined) { 
    TotalEnergyOfInitialRun  =  TotalEnergy;
    TotalEnergyOfInitialRunDefined = 1;
  }
  
  /* dump last batch of particle if they haven't been written yet */
  for (iRegion = 0; iRegion < regions; iRegion++) {
    if (region[iRegion].page) {
      flushBufferArrayGroup(region[iRegion].page, region[iRegion].bufferArrayGroup, FULL_COORDINATES);
      if (!SDDS_Terminate(region[iRegion].page))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    }
  }
  if (showerBufferArrayGroup) {
    flushBufferArrayGroup(&trajectoryPage, showerBufferArrayGroup, TRAJECTORY_COORDINATES);
    if (!SDDS_Terminate(&trajectoryPage))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  }
  if (outOfBoundsBufferArrayGroup) {
    flushBufferArrayGroup(&outOfBoundsPage, outOfBoundsBufferArrayGroup, FULL_COORDINATES);
    if (!SDDS_Terminate(&outOfBoundsPage))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  }
  if (undefinedBufferArrayGroup) {
    flushBufferArrayGroup(&undefinedRegionPage, undefinedBufferArrayGroup, FULL_COORDINATES);
    if (!SDDS_Terminate(&undefinedRegionPage))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  }
  /***********************\
   * create summary file *    
   \***********************/
  /* define columns and parameters */
  if( 0>SDDS_DefineParameter(&summaryPage, "InputBeamFile", NULL, NULL, 
                             NULL, NULL, SDDS_STRING, NULL) ||
     0>SDDS_DefineParameter(&summaryPage, "GeometryDefinitionFile", NULL, NULL, 
                            NULL, NULL, SDDS_STRING, NULL) ||
     0>SDDS_DefineParameter(&summaryPage, "SummaryFile", NULL, NULL, 
                            NULL, NULL, SDDS_STRING, NULL) ||
     0>SDDS_DefineParameter(&summaryPage, "Seed", NULL, NULL, 
                            NULL, NULL, SDDS_LONG, NULL))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  
  if (0>SDDS_DefineColumn(&summaryPage, "Region", "Region", NULL, NULL, "%ld", SDDS_LONG, 0))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  switch(mode) {
  case CYLINDER_MODE:
    if (0>SDDS_DefineColumn(&summaryPage, "rMidpoint", "r$bmid$n", "m", 
                            "region r-midpoint", "%14.6e", SDDS_DOUBLE, 0))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    break;
  case BLOCK_MODE:
    if (0>SDDS_DefineColumn(&summaryPage, "xMidpoint", "x$bmid$n", "m", 
                            "region x-midpoint", "%14.6e", SDDS_DOUBLE, 0) ||
        0>SDDS_DefineColumn(&summaryPage, "yMidpoint", "y$bmid$n", "m", 
                            "region y-midpoint", "%14.6e", SDDS_DOUBLE, 0))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    break;
  }
  if (0>SDDS_DefineColumn(&summaryPage, "zMidpoint", "z$bmid$n", "m", 
                          "region z-midpoint", "%14.6e", SDDS_DOUBLE, 0)||
      0>SDDS_DefineColumn(&summaryPage, "Volume", "V", "m$a3$n", "region volume", 
                          "%14.6e", SDDS_DOUBLE, 0)||
      0>SDDS_DefineColumn(&summaryPage, "IsOutputVolume", NULL, NULL, "output volume?", 
                          NULL, SDDS_SHORT, 0)||
      0>SDDS_DefineColumn(&summaryPage, "Fraction", "Fraction", NULL, 
                          "Fraction of energy deposited", "%14.6e", SDDS_DOUBLE, 0))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  for (i=0;  i <= PARTICLE_TYPES; i++) { /* this includes the total energy deposits column */
    if (0>SDDS_DefineColumn(&summaryPage, EDepParticleNames[i], EDepParticleSymbols[i], 
                            EDepParticleUnits[i], EDepParticleDescriptions[i], 
                            EDepParticleFormat[i], SDDS_DOUBLE, 0))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  }            
  if (sumPathLengths) {
    if (0>SDDS_DefineColumn(&summaryPage, "ElectronPathLength", "$gl$r$be-$n", "m", 
                            "Electron path length for energies above a specified cut-off", 
                            "%14.6e", SDDS_DOUBLE, 0)||
        0>SDDS_DefineColumn(&summaryPage, "PositronPathLength", "$gl$r$be+$n", "m", 
                            "Positron path length for energies above a specified cut-off", 
                            "%14.6e", SDDS_DOUBLE, 0)||
        0>SDDS_DefineColumn(&summaryPage, "PhotonPathLength", "$gl$r$bp$n", "m", 
                            "Photon path length for energies above a specified cut-off", 
                            "%14.6e", SDDS_DOUBLE, 0)||
        0>SDDS_DefineColumn(&summaryPage, "ElectronPathLengthEnergyCutoff", "E$bc,$gl$re-$n", "MeV", 
                            "Energy cut-off for electron path length summation", 
                            "%14.6e", SDDS_DOUBLE, 0)||
        0>SDDS_DefineColumn(&summaryPage, "PositronPathLengthEnergyCutoff", "E$bc,$gl$re+$n", "MeV", 
                            "Energy cut-off for positron path length summation", 
                            "%14.6e", SDDS_DOUBLE, 0)||
        0>SDDS_DefineColumn(&summaryPage, "PhotonPathLengthEnergyCutoff", "E$bc,$gl$rp$n", "MeV", 
                            "Energy cut-off for photon path length summation", 
                            "%14.6e", SDDS_DOUBLE, 0))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  }
  
  if (0>SDDS_DefineParameter(&summaryPage, "Generation", NULL, NULL, "Generation", 
                             "%ld", SDDS_LONG, NULL)||
      0>SDDS_DefineParameter(&summaryPage, "TotalEnergy", "E$btot$n", "MeV", 
                             "Total energy of this run", "%14.6e", SDDS_DOUBLE, 0))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  /* define total energy of the first run in the summary file,
   * so that it is available for future runs 
   * OR determine whether the RadiationDoseRate column should be defined or not */
  if (Generation == 1 || TotalEnergyOfInitialRunDefined) {
    if (0>SDDS_DefineParameter(&summaryPage, "TotalEnergyOfInitialRun", "E$btot,0$n", "MeV", 
                               "Total energy of first run", "%14.6e", SDDS_DOUBLE, 0))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  }
  if (TotalEnergyOfInitialRunDefined )
    if (0>SDDS_DefineColumn(&summaryPage, "RadiationDoseRate", "Dose Rate", "rad/s", 
                            "Radiation dose rate calculated from beam power", "%14.6e", SDDS_DOUBLE, 0))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  
  if (0>SDDS_DefineColumn(&summaryPage, "Label", "Label", NULL, "Region label", 
                          "%s", SDDS_STRING, 0)||
      0>SDDS_DefineColumn(&summaryPage, "Material", "Material", NULL, 
                          "Region material", "%s", SDDS_STRING, 0)||
      0>SDDS_DefineColumn(&summaryPage, "Density", "$gr$r", "kg/m$a3$n", 
                          "Region density", "%14.6e", SDDS_DOUBLE, 0)||
      0>SDDS_DefineColumn(&summaryPage, "LeptonEnergyLowerCutoff", "E$bc,e$n", 
                          "MeV", "Lepton energy lower cutoff actually used in the simulation", "%14.6e", SDDS_DOUBLE, 0)||
      0>SDDS_DefineColumn(&summaryPage, "LeptonEnergyUserDefinedLowerCutoff", 
                          "E$bc,e$n", "MeV", 
"Lepton energy lower cutoff defined by the user geometry file", "%14.6e", SDDS_DOUBLE, 0)||
      0>SDDS_DefineColumn(&summaryPage, "LeptonEnergyAbsoluteLowerCutoff", 
                          "E$bc,e$n", "MeV", "Absolute Lepton energy lower cutoff defined by cross-section data file", "%14.6e", SDDS_DOUBLE, 0)||
      0>SDDS_DefineColumn(&summaryPage, "PhotonEnergyLowerCutoff", "E$bc,$gg$r$n", 
                          "MeV", "Photon energy lower cutoff actually used in the simulation", "%14.6e", SDDS_DOUBLE, 0)||
      0>SDDS_DefineColumn(&summaryPage, "PhotonEnergyUserDefinedLowerCutoff", 
                          "E$bc,$gg$r$n", "MeV", "Photon energy lower cutoff defined by the geometry file", "%14.6e", SDDS_DOUBLE, 0)||
      0>SDDS_DefineColumn(&summaryPage, "PhotonEnergyAbsoluteLowerCutoff", 
                          "E$bc,$gg$r$n", "MeV", "Absolute Photon energy lower cutoff defined by cross-section data file", "%14.6e", SDDS_DOUBLE, 0)||
      0>SDDS_DefineParameter(&summaryPage, "PowerOfInitialBeam", "I$b0$n", "W", 
                             "Initial beam power", "%14.6e", SDDS_DOUBLE, NULL)||
      0>SDDS_DefineParameter(&summaryPage, "CumulativeSamples", "N$bsamples$n", 
                             NULL, "Cumulative samples", "%f", 
                             SDDS_DOUBLE, NULL))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  
  /* start page */
  if (!SDDS_WriteLayout(&summaryPage)|| !SDDS_StartPage(&summaryPage, regions))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  if (!SDDS_SetParameters(&summaryPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 
                          "Generation", Generation, 
                          "TotalEnergy", TotalEnergy, 
                          "PowerOfInitialBeam", PowerOfInitialBeam, 
                          "CumulativeSamples", CumulativeSamples, 
                          "InputBeamFile", inputfile,
                          "GeometryDefinitionFile", geometryFile,
                          "SummaryFile", summaryFile,
                          "Seed", seedNumber,
                          NULL))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  if (Generation == 1) {
    if (!SDDS_SetParameters(&summaryPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 
                            "TotalEnergyOfInitialRun", TotalEnergy, 
                            NULL))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  }
  else if (TotalEnergyOfInitialRunDefined) {
    /* pass on this parameter to next generation */
    if (!SDDS_SetParameters(&summaryPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 
                            "TotalEnergyOfInitialRun", TotalEnergyOfInitialRun, 
                            NULL))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  }
  for (iRegion = 0; iRegion<regions; iRegion++) {
    switch(mode) {
    case CYLINDER_MODE:
      if (!SDDS_SetRowValues(&summaryPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, iRegion, 
                             "Region", iRegion, 
                             "Label", region[iRegion].label, 
                             "Material", region[iRegion].material, 
                             "Density", region[iRegion].Density, 
                             "rMidpoint", region[iRegion].rMidpoint, 
                             "zMidpoint", region[iRegion].zMidpoint, 
                             "Volume", region[iRegion].Volume, 
                             "IsOutputVolume", region[iRegion].page?1:0, 
			     NULL))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
      break;
    case BLOCK_MODE:
      if (!SDDS_SetRowValues(&summaryPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, iRegion, 
                             "Region", iRegion, 
                             "Label", region[iRegion].label, 
                             "Material", region[iRegion].material, 
                             "Density", region[iRegion].Density, 
                             "xMidpoint", region[iRegion].xMidpoint, 
                             "yMidpoint", region[iRegion].yMidpoint, 
                             "zMidpoint", region[iRegion].zMidpoint, 
                             "Volume", region[iRegion].Volume,  
                             "IsOutputVolume", region[iRegion].page?1:0, 
			     NULL))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
      break;
    }
  }
  for (i = 0; i < PARTICLE_TYPES; i++) {
    if (!SDDS_SetColumn(&summaryPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, EDepParticle[i], regions, EDepParticleNames[i]))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  }
  
  for (iRegion = 0; iRegion < regions; iRegion++) {
    for (i = 0; i < PARTICLE_TYPES; i++)
      EDepParticle[PARTICLE_TYPES][iRegion] +=  EDepParticle[i][iRegion];
    if (TotalEnergy)
      FractionEDep  =  EDepParticle[PARTICLE_TYPES][iRegion]/ TotalEnergy/ samples;
    else
      FractionEDep  =  0.0;
    if (!SDDS_SetRowValues(&summaryPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, iRegion, 
                           "EDepTotal", EDepParticle[PARTICLE_TYPES][iRegion], 
                           "Fraction", FractionEDep, 
                           "LeptonEnergyUserDefinedLowerCutoff", region[iRegion].leptonEnergyCutoff,
                           "PhotonEnergyUserDefinedLowerCutoff", region[iRegion].photonEnergyCutoff,
                           "LeptonEnergyAbsoluteLowerCutoff", 
                           region[iRegion].materialCode ? thresh_.leptonLowerEnergyCutoff[region[iRegion].materialCode-1] : 0,
                           "PhotonEnergyAbsoluteLowerCutoff", 
                           region[iRegion].materialCode ? thresh_.photonLowerEnergyCutoff[region[iRegion].materialCode-1] : 0,
                           "LeptonEnergyLowerCutoff", MAX( region[iRegion].leptonEnergyCutoff,
                                                          region[iRegion].materialCode ? thresh_.leptonLowerEnergyCutoff[region[iRegion].materialCode-1] : 0),
                           "PhotonEnergyLowerCutoff", MAX( region[iRegion].photonEnergyCutoff,
                                                          region[iRegion].materialCode ? thresh_.photonLowerEnergyCutoff[region[iRegion].materialCode-1] : 0),
                           NULL))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);

    if (sumPathLengths) {
      if (!SDDS_SetRowValues(&summaryPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, iRegion, 
                             "ElectronPathLength", region[iRegion].electronPathLength,
                             "PositronPathLength", region[iRegion].positronPathLength,
                             "PhotonPathLength", region[iRegion].photonPathLength,
                             "ElectronPathLengthEnergyCutoff", region[iRegion].electronPathLengthEnergyCutoff,
                             "PositronPathLengthEnergyCutoff", region[iRegion].positronPathLengthEnergyCutoff,
                             "PhotonPathLengthEnergyCutoff", region[iRegion].photonPathLengthEnergyCutoff,
                             NULL))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    }
    
    if (TotalEnergyOfInitialRunDefined) {
      /* Factors to convert W/Kg to rad/s. 1 J/kg  =  100 rad;  */
      /* mass in kg and volume in m^3 */
      Mass  =  region[iRegion].Volume * region[iRegion].Density;
      /* if there is no mass or the region is defined as an 
       * output region, then the radiation dose isn't calculated 
       */
      if (Mass && !region[iRegion].page)
        RadiationDoseRate  =  PowerOfInitialBeam * EDepParticle[PARTICLE_TYPES][iRegion]/ 
          TotalEnergyOfInitialRun/ CumulativeSamples * 100 / Mass;
      else 
        RadiationDoseRate  =  0;
      if (!SDDS_SetRowValues(&summaryPage, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, iRegion, 
                             "RadiationDoseRate", RadiationDoseRate, NULL))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    }
  }
  if (!SDDS_WritePage(&summaryPage)||SDDS_Terminate(&summaryPage))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  
#if defined(VAX_VMS) || defined(UNIX) || defined(_WIN32)
  report_stats(stderr, "statistics:");
  fflush(stderr);
#endif
  return(0);
}

#if defined(_WIN32) /*Currently you must use Intel Visual Fortran on Windows*/
void howfar_()
#else
void howfar()
#endif
{
  /* howfar_: given a particle with x, y, z and direction 
   *  u, v, w, can the particle
   * go a distance ustep without crossing a boundary?
   */

  /*  The following external variables may be assigned new values:
   *  epcont_.discard_flag
   *  stack_.distanceNextRegion[stack_.stackPointer-1]  always use stackPointer-1 because C arrays start with 0
   *  epcont_.straight_step_cm
   *  epcont_.newRegion   danger! when returning a value for this variable 
   *                      add 1 because it is used to reference a fortran array 
   *                      when using epcont_.newRegion in this subroutine, subtract 1.
   */

  /*  EPSILON is the distance by which one extends the trajectory just
   *  so that the region on the other side of a boundary can be determined 
   *  by findRegion[mode](x, y, z) 
   */
#define EPSILON 1e-6 /* units of cm */ 
  /* A safe stack limit has to be used because egs doesn't check the size of the stack */
#define SAFE_STACK_LIMIT (MXSTACK-10)
  double x, y, z;
  double u, v, w, u_orig, v_orig, w_orig, utmp, vtmp, wtmp, denom;
  int32_t stackPointer;
  int32_t currentRegion, newRegion;
  double bending_radius_cm, angle;
  double suggested_distance;
  double incremental_distance_to_next_boundary;
  double distance_to_next_boundary;
  int32_t i;
  
#ifdef DEBUG
  double xnew, ynew, znew;
#endif

  /* the splitting should occur when a particle just enters a new region */
  stackPointer = stack_.stackPointer-1;
  if (stack_.stackPointer+1>MXSTACK) {
    fprintf( stderr, "error found when calling howfar: EGS particle stack overflowed.\n");
    exit(1);
  }
/* splitting in shower occurs here; not in egs4 */
  if ( allowsplitting && (epcont_.oldRegion !=  stack_.region[stackPointer]) &&
      (region[(currentRegion = stack_.region[stackPointer]-1)].split>1) &&
      (stack_.weight[stackPointer]>region[currentRegion].minimumWeight) ) { 
    stack_.weight[stackPointer]/= (double)region[currentRegion].split;
    for (i = 1; i<region[currentRegion].split; i++) {
      stack_.energyMeV[stackPointer+i] = stack_.energyMeV[stackPointer];
      stack_.x[stackPointer+i] = stack_.x[stackPointer];
      stack_.y[stackPointer+i] = stack_.y[stackPointer];
      stack_.z[stackPointer+i] = stack_.z[stackPointer];
      stack_.u[stackPointer+i] = stack_.u[stackPointer];
      stack_.v[stackPointer+i] = stack_.v[stackPointer];
      stack_.w[stackPointer+i] = stack_.w[stackPointer];
      stack_.t[stackPointer+i] = stack_.t[stackPointer];
      stack_.distanceNextRegion[stackPointer+i] = stack_.distanceNextRegion[stackPointer];
      stack_.weight[stackPointer+i] = stack_.weight[stackPointer];
      stack_.integerCharge[stackPointer+i] = stack_.integerCharge[stackPointer];
      stack_.region[stackPointer+i] = (currentRegion+1);  /* egs region array is offset */
      /* latch is not used now, but it could be used in the future */
      stack_.latch[stackPointer+i] = stack_.latch[stackPointer];
    }
    stack_.stackPointer += (region[currentRegion].split-1);

    stackPointer        += (region[currentRegion].split-1);
    if (stack_.stackPointer+1>SAFE_STACK_LIMIT) {
      fprintf( stderr, "error in howfar: EGS particle stack overflowed while splitting particles.\n");
      exit(1);
    }
  }
  x  =  stack_.x[stackPointer];
  y  =  stack_.y[stackPointer];
  z  =  stack_.z[stackPointer];
  u = stack_.u[stackPointer];
  v = stack_.v[stackPointer];
  w = stack_.w[stackPointer];
  /* subroutine mscat and uphi sometimes gives values of direction cosines greater than 1 */
  denom  =  sqrt(u*u+v*v+w*w);
  stack_.u[stackPointer] /=  denom;
  stack_.v[stackPointer] /=  denom;
  stack_.w[stackPointer] /=  denom;
  u = stack_.u[stackPointer];
  v = stack_.v[stackPointer];
  w = stack_.w[stackPointer];
  u_orig = u; /* required if there is a magnetic field */
  v_orig = v; /* and distance is shortened. */
  w_orig = w;

  currentRegion = stack_.region[stackPointer]-1;
  
#ifdef DEBUG
  fprintf( stderr, "\nStart of howfar_\n");
  for (i=0; i<=stackPointer; i++) {
    fprintf( stderr, "np: %"PRId32" x: %12.5le y: %12.5le z: %12.5le\n      u: %12.5le v: %12.5le w: %12.5le E: %12.5e t: %12.5le W: %12.5le q: %"PRId32" region: %s\n", i, stack_.x[i], stack_.y[i], stack_.z[i], stack_.u[i], stack_.v[i], stack_.w[i], stack_.energyMeV[i], stack_.t[i], stack_.weight[i], stack_.integerCharge[i], region[stack_.region[i]-1].label);
  }
  xnew  =  x + (epcont_.straight_step_cm)*u;
  ynew  =  y + (epcont_.straight_step_cm)*v;
  znew  =  z + (epcont_.straight_step_cm)*w;
  fprintf( stderr, "Suggested trajectory: x: %12.5le y: %12.5le z: %12.5le and ustep: %12.5le\n", xnew, ynew, znew, epcont_.straight_step_cm);
#endif
  
  /* check region of x, y, z coordinates with value stored in egs subroutines */
  /* At some point, I should remove this check. It costs one call to findRegion[mode] */
#ifdef DEBUG
  fprintf( stderr, "Current region: %"PRId32"  findRegion(x,y,z): %"PRId32"\n", currentRegion, findRegion[mode](x, y, z));
#endif
  if (currentRegion != findRegion[mode](x, y, z)) {
    fprintf( stderr, "Particle at\nx: %12.5e y: %12.5e z: %12.5e\n"
            "u: %12.5e v: %12.5e w: %12.5e\n"
            "d: %12.5e E: %12.5e q: %ld stack: %"PRId32"\n"
            "can't possibly be in region \"%s.\"\n", 
            x, y, z, u, v, w, 
            epcont_.straight_step_cm, stack_.energyMeV[stackPointer], 
            stack_.integerCharge[stackPointer], stackPointer, region[currentRegion].label);
  }
  /* If a magnetic field is present then reduce step size 
     if necessary and change direction cosine */
  /* for one small segment of arc, the trajectory is considered rectilinear */
  bending_radius_cm = 0;
  if (stack_.integerCharge[stackPointer] &&
      (region[currentRegion].Bx || region[currentRegion].By ||
       region[currentRegion].Bz) ) {
    if (region[currentRegion].Bx) {
      bending_radius_cm = stack_.integerCharge[stackPointer]*
        sqrt( sqr( stack_.energyMeV[stackPointer] ) - sqr(me_mev) )
          /region[epcont_.newRegion-1].Bx/0.299792458/10.0;
    } 
    else if (region[currentRegion].By) {
      bending_radius_cm = stack_.integerCharge[stackPointer]*
        sqrt( sqr( stack_.energyMeV[stackPointer] ) - sqr(me_mev) )
          /region[epcont_.newRegion-1].By/0.299792458/10.0;
    }
    else if (region[currentRegion].Bz) {
      bending_radius_cm = stack_.integerCharge[stackPointer]*
        sqrt( sqr( stack_.energyMeV[stackPointer] ) - sqr(me_mev) )
          /region[epcont_.newRegion-1].Bz/0.299792458/10.0;
    }
    angle  =  epcont_.straight_step_cm / bending_radius_cm;
    if (FABS(angle) > MINIMUM_ANGLE ) {
      angle  =  SIGN(angle) * MINIMUM_ANGLE;
      epcont_.straight_step_cm  =  angle * bending_radius_cm;
    }
    /* change direction cosines */
    if (region[currentRegion].Bx) {
      stack_.v[stackPointer]  =  ( v * cos(angle) + w * sin(angle));
      stack_.w[stackPointer]  =  (-v * sin(angle) + w * cos(angle));
      v  =  stack_.v[stackPointer];
      w  =  stack_.w[stackPointer];
    } 
    else if (region[currentRegion].By) {
      stack_.w[stackPointer]  =  ( w * cos(angle) + u * sin(angle));
      stack_.u[stackPointer]  =  (-w * sin(angle) + u * cos(angle));
      w  =  stack_.w[stackPointer];
      u  =  stack_.u[stackPointer];
    } 
    else if (region[currentRegion].Bz) {
      stack_.u[stackPointer]  =  ( u * cos(angle) + v * sin(angle));
      stack_.v[stackPointer]  =  (-u * sin(angle) + v * cos(angle));
      u  =  stack_.u[stackPointer];
      v  =  stack_.v[stackPointer];
    }
#ifdef DEBUG
    fprintf( stderr, "After influence of magnetic field, u: %12.5le v: %12.5le w: %12.5le and ustep: %12.5le\n", u, v, w, epcont_.straight_step_cm);
    xnew  =  x + (epcont_.straight_step_cm)*u;
    ynew  =  y + (epcont_.straight_step_cm)*v;
    znew  =  z + (epcont_.straight_step_cm)*w;
    fprintf( stderr, "suggested trajectory: x: %12.5le y: %12.5le z: %12.5le \n", xnew, ynew, znew);
#endif
  }
  epcont_.straight_step_cm +=  EPSILON;
  /* epcont_.straight_step_cm may have been modified by 
     the magnetic field */
  suggested_distance = epcont_.straight_step_cm;   
  distance_to_next_boundary  =  0.0;
  incremental_distance_to_next_boundary  =  0.0;
  /* multiple pass of the loop occurs if the suggested trajectory
   * passes through more than one
   * brick that are defined to be the same region. 
   * This occurs often when transporting in vacuum
   * with a long initial trajectory. */
  u = u_orig;
  v = v_orig;
  w = w_orig;
  while(1) {
    incremental_distance_to_next_boundary  =  findDistanceToBoundary[mode](x, y, z, u, v, w);
    distance_to_next_boundary +=  incremental_distance_to_next_boundary;
#ifdef DEBUG
    fprintf( stderr, "suggested_distance %lf distance_to_next_boundary %lf.\n", suggested_distance, distance_to_next_boundary);
#endif
    if ( suggested_distance < distance_to_next_boundary )
      return;
    /* This makes sure that a point on the boundary is determined 
     * to be in the adjacent region.
     * It doesn't matter here that the region boundary 
     * is closed at the negative end and open at the positive end */
    incremental_distance_to_next_boundary +=  EPSILON;     
    distance_to_next_boundary +=  EPSILON;     
    /* On each pass through the loop the coordinates x, y, z  
     * are updated at each boundary. These variables are not used outside this loop. */
    /* Need to recalculate the u, v, w if the distance travelled is
       shortened and going through a magnetic field. 
       These values of u, v, w are not used outside this loop either. */
    if (stack_.integerCharge[stackPointer]) {
      angle  =  incremental_distance_to_next_boundary / bending_radius_cm;
      if (region[currentRegion].Bx) {
        vtmp  =  (v * cos(angle) + w * sin(angle));
        wtmp  =  (-v * sin(angle) + w * cos(angle));
        v = vtmp;
        w = wtmp;
      } 
      else if (region[currentRegion].By) {
        wtmp  =  ( w * cos(angle) + u * sin(angle));
        utmp  =  (-w * sin(angle) + u * cos(angle));
        w = wtmp;
        u = utmp;
      } 
      else if (region[currentRegion].Bz) {
        utmp  =  ( u * cos(angle) + v * sin(angle));
        vtmp  =  (-u * sin(angle) + v * cos(angle));
        u = utmp;
        v = vtmp;
      }
    }
    x +=  incremental_distance_to_next_boundary*u;
    y +=  incremental_distance_to_next_boundary*v;
    z +=  incremental_distance_to_next_boundary*w;
    stack_.u[stackPointer]  =  u;
    stack_.v[stackPointer]  =  v;
    stack_.w[stackPointer]  =  w;

#ifdef DEBUG
    fprintf( stderr, "After checking the distance to the next boundary, suggested coordinates: x: %12.5le y: %12.5le z: %12.5le u: %12.5le v: %12.5le w: %12.5le and distance_to_next_boundary: %12.5le\n", x, y, z, u, v, w, distance_to_next_boundary);
#endif

    if ( currentRegion !=  (newRegion = findRegion[mode](x, y, z))) {
      epcont_.straight_step_cm  =  distance_to_next_boundary;
      /* "out of bounds" is defined by setting the region number to (regions+1).
       * undefined regions are already defined with index value 
       * regions in function findRegion[mode] */
      if (newRegion ==  -1 )
        epcont_.newRegion = (regions+1)+1;  /* convert to egs4 array numbering */
      else
        epcont_.newRegion = newRegion+1;  /* convert to egs4 array numbering */
      /* A positive discard flag means ausgab is called once 
       * with flag USER_REQUESTED_DISCARD set. We don't use these here.
       * A negative discard flag means ausgab is called twice, 
       * first with flag BEFORE_TRANSPORT and then with flag USER_REQUESTED_DISCARD set.
       */
      if (newRegion  ==  regions || newRegion ==  -1 || region[newRegion].page) {
        epcont_.discard_flag =  -1;
#ifdef DEBUG
        if (newRegion  ==  regions)
          fprintf( stderr, "Particle is out of bounds: x: %12.5le y: %12.5le z: %12.5le u: %12.5le v: %12.5le w: %12.5le and distance_to_next_boundary: %12.5le\n", x, y, z, u, v, w, distance_to_next_boundary);
        if (newRegion ==  -1 )
          fprintf( stderr, "Particle is in undefined region: x: %12.5le y: %12.5le z: %12.5le u: %12.5le v: %12.5le w: %12.5le and distance_to_next_boundary: %12.5le\n", x, y, z, u, v, w, distance_to_next_boundary);
        if (region[newRegion].page)
          fprintf( stderr, "Particle is being written to a file: x: %12.5le y: %12.5le z: %12.5le u: %12.5le v: %12.5le w: %12.5le and distance_to_next_boundary: %12.5le\n", x, y, z, u, v, w, distance_to_next_boundary);
#endif
      }
      return;
    }
  }
}

#if !defined(_WIN32) /*Currently you must use Intel Visual Fortran on Windows*/
FCALLSCSUB0(howfar,HOWFAR,howfar)
#endif

#if defined(_WIN32) /*Currently you must use Intel Visual Fortran on Windows*/
void ausgab_(int32_t *situationIndexPtr)
#else
void ausgab(int32_t situationIndex)
#endif
{
  int32_t stackPointer;
  int32_t particleType, currentRegion, newRegion;
  double x, y, z, u, v, w, t, weight, energy;
  int32_t RegionCredited=0;
  BUFFER_ARRAY *arrayPtr;
  int32_t i;
#if defined(_WIN32)
  int32_t situationIndex = *situationIndexPtr;
#endif
  stackPointer = stack_.stackPointer-1;

  /* write coordinates in meters */
  x  =  stack_.x[stackPointer]/100;
  y  =  stack_.y[stackPointer]/100;
  z  =  stack_.z[stackPointer]/100;
  u  =  stack_.u[stackPointer];
  v  =  stack_.v[stackPointer];
  w  =  stack_.w[stackPointer];
  t  =  stack_.t[stackPointer];
  
  weight  =  stack_.weight[stackPointer];
  energy  =  stack_.energyMeV[stackPointer];
  if (0>(particleType = getParticleType(stack_.integerCharge[stackPointer]))) {
    fprintf( stderr, "error code returned from getParticleType in ausgab.\n");
    exit(1);
  }
  currentRegion =  stack_.region[stackPointer] - 1;
  newRegion = epcont_.newRegion - 1;
#ifdef DEBUG
  fprintf( stderr, "\nstart of ausgab_ situation #%"PRId32"\n", situationIndex);
  fprintf( stderr, "%s, stackPointer: %"PRId32", stack_.region[stackPointer] %"PRId32", currentRegion %"PRId32".\n",
          particleNames[particleType], stackPointer, stack_.region[stackPointer], currentRegion);
  fprintf( stderr, "stack_.region: %s\n", region[currentRegion].label);
  fprintf( stderr, "epcont_.oldRegion: %s\n", region[epcont_.oldRegion-1].label);
  fprintf( stderr, "epcont_.newRegion: %s\n", region[epcont_.newRegion-1].label);
  if ((t > 1e-14) && z*0.01/t > c_mks) {
    /* check causality */
    fprintf( stderr, "Time %12.5e appears to be too short for z of %12.5e \n", t, z);
  }
#endif

  /*******************************\
   * summary
   * find the region which will be credited for absorbed energy
   \*******************************/
  if (summary && currentRegion < regions && currentRegion >= 0 && energy != 0) { 
    /* the second test eliminates the undefined region */
    /* the third test eliminates region = -1 (out of bounds) */
    switch(situationIndex) {
      /* deposit in current region for the three cases */
    case USER_ENERGY_CUTOFF_DISCARD:
    case PEGS_ENERGY_CUTOFF_DISCARD:
      /* sometimes, a particle energy falls below cut-off 
       * in the last step of the transport
       * in material */
      if (!strcasecmp(region[currentRegion].material, "vacuum")) {
        RegionCredited = epcont_.oldRegion-1;
	/*	  fprintf( stderr, "Old region [%ld] is used to register energy loss. "
	  " (Particle energy falls below cut-off in the last step of"
	  " the transport.)\n", RegionCredited);
	*/
      }
      else
        RegionCredited = currentRegion;
      break;
    case PHOTOELECTRIC_INTERACTION:
      RegionCredited = currentRegion;
      break;
    case BEFORE_TRANSPORT: 
    case USER_REQUESTED_DISCARD:  
      /* with this case, particles going into an output region 
       * gets recorded as an absorbed particle */
      RegionCredited = currentRegion;
      break;
    }

    switch(situationIndex) {
    case USER_ENERGY_CUTOFF_DISCARD:
    case PEGS_ENERGY_CUTOFF_DISCARD:
    case PHOTOELECTRIC_INTERACTION:
    case BEFORE_TRANSPORT: 
      /* An egs subroutine puts the value of a charged 
       * particle's kinetic energy
       * or a photons energy into epcont_.energy_deposited_MeV */ 
      EDepParticle[particleType][RegionCredited] +=  epcont_.energy_deposited_MeV * weight;
      break;
    case USER_REQUESTED_DISCARD:  
      /* with this case, particles going into an output region 
       * gets its kinetic energy absorbed as an absorbed particle */
      EDepParticle[particleType][RegionCredited] +=  epcont_.energy_deposited_MeV * weight;
      break;
    }
  }

#ifdef DEBUG
  switch(situationIndex) {
  case BEFORE_TRANSPORT: 
  case AFTER_TRANSPORT: 
    /* write distance to be traversed */
    fprintf( stderr, "Sample %"PRId32" %s %f cm.\nEnergy deposited in region %s: %lg MeV.\n", 
            iSample, ausgab_flag_messages[situationIndex], 
            epcont_.actual_curved_step_cm,
            region[currentRegion].label, epcont_.energy_deposited_MeV);
    break;
  default:
    fprintf( stderr, "Sample %"PRId32" %s\nEnergy deposited in region %s: %lg MeV.\n", 
            iSample, ausgab_flag_messages[situationIndex], 
            region[currentRegion].label, epcont_.energy_deposited_MeV);
    break;
  }     
#endif

if (addTimeColumn)
  switch(situationIndex) {
  case AFTER_TRANSPORT:
#ifdef DEBUG
    fprintf( stderr, "Time before step %12.5e\n", t);
#endif
    switch(particleType) {
    case ELECTRON:
    case POSITRON:
      t += epcont_.actual_curved_step_cm * 1e-2 / (c_mks * epcont_.beta);
      break;
    case PHOTON:
      t += epcont_.actual_curved_step_cm * 1e-2 / c_mks;
    }
#ifdef DEBUG
    fprintf( stderr, "Time after step %12.5e\n", t);
#endif
    stack_.t[stackPointer] = t;
  }
  
  /***************************\
   * shower coordinates
   *
   \***************************/
  if (trajectoryPage_set) {
    switch(situationIndex) {
    case AFTER_TRANSPORT:
    case USER_ENERGY_CUTOFF_DISCARD:
    case PEGS_ENERGY_CUTOFF_DISCARD:
    case USER_REQUESTED_DISCARD:
      if (outputFlags[particleType]&KEEP_PARTICLE) {
        arrayPtr  =  showerBufferArrayGroup->particleType[particleType];
        i  =  arrayPtr->i;
        arrayPtr->x[i] = x;
        arrayPtr->y[i] = y;
        arrayPtr->z[i] = z;
        if (addTimeColumn)
          arrayPtr->t[i] = t;
        arrayPtr->weight[i] = weight;
        arrayPtr->en[i] = energy;
        arrayPtr->particle[i] = iParticle;
        arrayPtr->sample[i] = iSample;
#ifdef DEBUG
        fprintf( stderr, "Written to trajectory buffer arrays: index %"PRId32" %lf MeV %s\n",
            arrayPtr->i, arrayPtr->en[i], arrayPtr->particleName);
#endif
        arrayPtr->i++;
        if (arrayPtr->i == arrayPtr->length) {
#ifdef DEBUG
          fprintf( stderr, "Flushing trajectory buffer array: %s\n",
                  arrayPtr->particleName);
#endif
          flushBufferArray(&trajectoryPage, arrayPtr, TRAJECTORY_COORDINATES);
          arrayPtr->i  =  0;
        }
      }
    }
  }

  /*******************\
   * output  regions *
   *                 *
   \*******************/
  if (region[newRegion].bufferArrayGroup &&
      situationIndex == USER_REQUESTED_DISCARD &&
      outputFlags[particleType]&KEEP_PARTICLE) {
    arrayPtr  =  region[newRegion].bufferArrayGroup->particleType[particleType];
    i  =  arrayPtr->i;
    arrayPtr->x[i] = x;
    arrayPtr->y[i] = y;
    arrayPtr->z[i] = z;
    arrayPtr->u[i] = u;
    arrayPtr->v[i] = v;
    arrayPtr->w[i] = w;
    if (addTimeColumn)
      arrayPtr->t[i] = t;
    arrayPtr->weight[i] = weight;
    arrayPtr->en[i] = energy;
    arrayPtr->particle[i] = iParticle;
    arrayPtr->sample[i] = iSample;
    arrayPtr->i++;
    if (arrayPtr->i == arrayPtr->length) {
      flushBufferArray(region[newRegion].page, arrayPtr, FULL_COORDINATES);
      arrayPtr->i  =  0;
    }
  }
  
  /*******************************************************\
   * if particle goes out of bounds,                     *
   * then write the coordinates if the user requested it *
   \*******************************************************/
  else if (newRegion == regions+1 && outOfBoundsBufferArrayGroup &&
           situationIndex == USER_REQUESTED_DISCARD &&
           outputFlags[particleType]&KEEP_PARTICLE) {
    arrayPtr  =  outOfBoundsBufferArrayGroup->particleType[particleType];
    i  =  arrayPtr->i;
    arrayPtr->x[i] = x;
    arrayPtr->y[i] = y;
    arrayPtr->z[i] = z;
    arrayPtr->u[i] = u;
    arrayPtr->v[i] = v;
    arrayPtr->w[i] = w; 
    if (addTimeColumn)
      arrayPtr->t[i] = t;
    arrayPtr->weight[i] = weight;
    arrayPtr->en[i] = energy;
    arrayPtr->particle[i] = iParticle;
    arrayPtr->sample[i] = iSample;
    arrayPtr->i++;
    if (arrayPtr->i == arrayPtr->length) {
      flushBufferArray(&outOfBoundsPage, arrayPtr, FULL_COORDINATES);
      arrayPtr->i  =  0;
    }
  }    
  /********************************************************\
   * if particle goes into an undefined region,           *
   * then record the coordinates if the user requested it *
   \********************************************************/
  else if (newRegion == regions && undefinedBufferArrayGroup && 
           situationIndex == USER_REQUESTED_DISCARD &&
           outputFlags[particleType]&KEEP_PARTICLE) {
    arrayPtr  =  undefinedBufferArrayGroup->particleType[particleType];
    i  =  arrayPtr->i;
    arrayPtr->x[i] = x;
    arrayPtr->y[i] = y;
    arrayPtr->z[i] = z;
    arrayPtr->u[i] = u;
    arrayPtr->v[i] = v;
    arrayPtr->w[i] = w;
    if (addTimeColumn)
      arrayPtr->t[i] = t;
    arrayPtr->weight[i] = weight;
    arrayPtr->en[i] = energy;
    arrayPtr->particle[i] = iParticle;
    arrayPtr->sample[i] = iSample;
    arrayPtr->i++;
    if (arrayPtr->i == arrayPtr->length) {
      flushBufferArray(&undefinedRegionPage, arrayPtr, FULL_COORDINATES);
      arrayPtr->i  =  0;
    }
  }

/* record path lengths. Why BEFORE_TRANSPORT and not AFTER_TRANSPORT? */
  switch(situationIndex) {
  case BEFORE_TRANSPORT:
    /* sum the distance travelled for a charged particle */
    switch (particleType) {
    case ELECTRON:
      if (energy> region[currentRegion].electronPathLengthEnergyCutoff) {
        region[currentRegion].electronPathLength += epcont_.actual_curved_step_cm;
      }
      break;
    case POSITRON:
      if (energy> region[currentRegion].positronPathLengthEnergyCutoff) {
        region[currentRegion].positronPathLength += epcont_.actual_curved_step_cm;
      }
      break;
    case PHOTON:
      if (energy> region[currentRegion].photonPathLengthEnergyCutoff) {
        region[currentRegion].photonPathLength += epcont_.actual_curved_step_cm;
      }
      break;
    }
  }
}

#if !defined(_WIN32) /*Currently you must use Intel Visual Fortran on Windows*/
FCALLSCSUB1(ausgab,AUSGAB,ausgab,LONG)
#endif

/* dump last batch of particle if they haven't been written yet and close files */
void flushBufferArrayGroup(SDDS_DATASET *page, BUFFER_ARRAY_GROUP *bufferArrayGroup, uint32_t coordinateflag) {
  BUFFER_ARRAY *arrayPtr;
  int32_t type;
  
  for (type = 0; type<PARTICLE_TYPES; type++) {
    arrayPtr  =  bufferArrayGroup->particleType[type];
#ifdef DEBUG
    fprintf( stderr, "Flushing trajectory buffer array: %s\n",
            arrayPtr->particleName);
#endif
    flushBufferArray(page, arrayPtr, coordinateflag);
  }
}

/* called for regions output files 
   undefined, out-of-bounds region file, and
   trajectory file, but not the summary file  */
void defineColumnsAndParameters(SDDS_DATASET *page, uint32_t coordinateflag) {
  
  if (0>SDDS_DefineColumn(page, "x", "x", "m", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(page, "y", "y", "m", NULL, "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(page, "z", "z", "m", NULL, "%14.6e", SDDS_DOUBLE, 0))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  if (coordinateflag&FULL_COORDINATES) {
    if (0>SDDS_DefineColumn(page, "u", "u", NULL, NULL, "%14.6e", SDDS_DOUBLE, 0) ||
        0>SDDS_DefineColumn(page, "v", "v", NULL, NULL, "%14.6e", SDDS_DOUBLE, 0) ||
        0>SDDS_DefineColumn(page, "w", "w", NULL, NULL, "%14.6e", SDDS_DOUBLE, 0))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  }
  if (addTimeColumn)
    if (0>SDDS_DefineColumn(page, "t", "t", "s", NULL, "%14.6e", SDDS_DOUBLE, 0))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);


  if (0>SDDS_DefineColumn(page, "Weight", "Weight", NULL, "Statistical weight", 
                          "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(page, "Energy", "E", "MeV", "Total energy of particle", 
                          "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineColumn(page, "Particle", "Particle", NULL, "Particle number", 
                          "%ld", SDDS_LONG, 0) ||
      0>SDDS_DefineColumn(page, "Sample", "Sample", NULL, "Sample number", "%ld",
                          SDDS_LONG, 0) ||
      0>SDDS_DefineParameter(page, "Generation", NULL, NULL, "Generation", 
                             "%ld", SDDS_LONG, 0) ||
      0>SDDS_DefineParameter(page, "TotalEnergy", "E$btot$n", "MeV", 
                             "Sum of total energies of all particles", "%14.6e", SDDS_DOUBLE, 0))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);

  if (0>SDDS_DefineParameter(page, "TotalEnergyOfInitialRun", "E$btot$n", "MeV",
                             "Total energy of first run", "%14.6e", SDDS_DOUBLE, 0))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);

  if (0>SDDS_DefineParameter(page, "PowerOfInitialBeam", "I$b0$n", "W", 
                             "Initial beam power", "%14.6e", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineParameter(page, "CumulativeSamples", "N$bsamples$n", NULL, 
                             "Cumulative samples", "%f", SDDS_DOUBLE, 0) ||
      0>SDDS_DefineParameter(page, "Type", "Type", NULL, "Particle type", "%s", 
                             SDDS_STRING, NULL))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);

  if( 0>SDDS_DefineParameter(page, "InputBeamFile", NULL, NULL, 
                             NULL, NULL, SDDS_STRING, NULL) ||
     0>SDDS_DefineParameter(page, "GeometryDefinitionFile", NULL, 
                            NULL, NULL, NULL, SDDS_STRING, NULL) ||
     0>SDDS_DefineParameter(page, "SummaryFile", NULL, NULL, 
                            NULL, NULL, SDDS_STRING, NULL))
    SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);

}

void flushBufferArray(SDDS_DATASET *page, BUFFER_ARRAY *arrayPtr, uint32_t coordinateflag) {
  int32_t i;
  double TotalEnergy;

  if (arrayPtr->i) {
    if (!SDDS_StartPage(page, arrayPtr->i))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    if (!SDDS_SetParameters(page, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, "Type", arrayPtr->particleName, NULL)||
        !SDDS_SetColumn(page, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, arrayPtr->x, arrayPtr->i, "x")||
        !SDDS_SetColumn(page, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, arrayPtr->y, arrayPtr->i, "y")||
        !SDDS_SetColumn(page, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, arrayPtr->z, arrayPtr->i, "z")||
        !SDDS_SetColumn(page, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, arrayPtr->weight, arrayPtr->i, "Weight")||
        !SDDS_SetColumn(page, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, arrayPtr->en, arrayPtr->i, "Energy")||
        !SDDS_SetColumn(page, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, arrayPtr->particle, arrayPtr->i, "Particle")||
        !SDDS_SetColumn(page, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, arrayPtr->sample, arrayPtr->i, "Sample"))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    if (coordinateflag&FULL_COORDINATES) {
      if (!SDDS_SetColumn(page, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, arrayPtr->u, arrayPtr->i, "u")||
          !SDDS_SetColumn(page, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, arrayPtr->v, arrayPtr->i, "v")||
          !SDDS_SetColumn(page, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, arrayPtr->w, arrayPtr->i, "w"))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    }

    if (addTimeColumn)
      if (!SDDS_SetColumn(page, SDDS_SET_BY_NAME|SDDS_PASS_BY_REFERENCE, arrayPtr->t, arrayPtr->i, "t"))
        SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);

    if (!SDDS_GetParameter(page, "TotalEnergy", &TotalEnergy))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
    for (i = 0; i<arrayPtr->i; i++)
      TotalEnergy += arrayPtr->en[i]*arrayPtr->weight[i];
    if (!SDDS_SetParameters(page, SDDS_SET_BY_NAME|SDDS_PASS_BY_VALUE, 
                            "TotalEnergy", TotalEnergy, 
                            NULL) ||
        !SDDS_WritePage(page))
      SDDS_PrintErrors(stderr, SDDS_VERBOSE_PrintErrors|SDDS_EXIT_PrintErrors);
  }
}

BUFFER_ARRAY_GROUP *allocateBufferArrayGroup(uint32_t coordinateflag) {
  int32_t type;
  BUFFER_ARRAY_GROUP *bufferArrayGroup;

  bufferArrayGroup  =  (BUFFER_ARRAY_GROUP *)malloc(sizeof(BUFFER_ARRAY_GROUP));
  bufferArrayGroup->particleType  =  (BUFFER_ARRAY **)malloc(sizeof(BUFFER_ARRAY*)*PARTICLE_TYPES);
  for (type=0; type<PARTICLE_TYPES; type++) {
    bufferArrayGroup->particleType[type]  =  (BUFFER_ARRAY *)allocateBufferArrays(coordinateflag);
    bufferArrayGroup->particleType[type]->particleName  =  particleNames[type];
  }
  return(bufferArrayGroup);
}

BUFFER_ARRAY *allocateBufferArrays(uint32_t coordinateflag) {
  BUFFER_ARRAY *buffer_array;
  
  buffer_array  =  (BUFFER_ARRAY *)malloc(sizeof(BUFFER_ARRAY));
  buffer_array->length = POINTS_PER_PAGE;
  buffer_array->x = (double *)malloc(buffer_array->length*sizeof(double));
  buffer_array->y = (double *)malloc(buffer_array->length*sizeof(double));
  buffer_array->z = (double *)malloc(buffer_array->length*sizeof(double));
  buffer_array->weight = (double *)malloc(buffer_array->length*sizeof(double));
  buffer_array->en = (double *)malloc(buffer_array->length*sizeof(double));
  buffer_array->particle = (int32_t *)malloc(buffer_array->length*sizeof(int32_t));
  buffer_array->sample = (int32_t *)malloc(buffer_array->length*sizeof(int32_t));
  if ( coordinateflag&FULL_COORDINATES ) {
    buffer_array->u = (double *)malloc(buffer_array->length*sizeof(double));
    buffer_array->v = (double *)malloc(buffer_array->length*sizeof(double));
    buffer_array->w = (double *)malloc(buffer_array->length*sizeof(double));
  }
  if (addTimeColumn)
    buffer_array->t = (double *)malloc(buffer_array->length*sizeof(double));
  buffer_array->i = 0;
  return(buffer_array);
}

int32_t getParticleType(int32_t charge) {
  switch(charge) {
  case ELECTRON_CHARGE:
    return(ELECTRON);
  case POSITRON_CHARGE:
    return(POSITRON);
  case PHOTON_CHARGE:
    return(PHOTON);
  default:
    return(-1);
  }
}
/* Copyright 1994 by Michael Borland and Argonne National Laboratory,
 * all rights reserved.
 */
/* file: compose_fn.c
 *
 * Michael Borland, 1994.
 */
char *compose_filename(char *template, char *root_name)
{
  char *ptr;
  
  if (str_in(template, "%s")) {
    ptr = tmalloc(sizeof(char)*(strlen(template)+strlen(root_name)+1));
    sprintf(ptr, template, root_name);
    return(ptr);
  }
  else
    return(template);
}
