/* This file is part of Cloudy and is copyright (C)1978-2006 by Gary J. Ferland
 * For conditions of distribution and use see copyright notice in license.txt */
/*cdInit routine to initialize variables, called at start of calculation */
/*cdEXIT exit handler to terminate calculation, called at end of calculation */
#include "cddefines.h"
/* NB - following must appear after cddefines.h since code there
 * ensures that __unix is defined on any unix box */
#ifdef __unix
#	include <unistd.h>
#	include <stdlib.h>
#endif
#include "cddrive.h"
#include "assertresults.h" 
#include "called.h"
#include "mole.h"
#include "h2.h"
#include "hmi.h"
#include "version.h"
#include "input.h"
#include "parse.h"
#include "punch.h"
#include "atmdat.h"
#include "zero.h"
#include "date.h"
#include "optimize.h"
#include "grid.h"
#include "cpu.h"
/* include mpi header if mpi true */
/* This flag indicates whether we are multi-processing.
 * if this is true then all output happens at the end,
 * if false then each set of results is printed and flushed when it happens.
 * must be set true on parallel machines, false on pc */
#ifdef MPI
#	include <mpi.h>
#endif

static char chMonth[12][4] ={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
static char chMonthInt[12][3] ={"01","02","03","04","05","06","07","08","09","10","11","12" };

#include <stdio.h>

/* the intel compiler */
/* this needs to be before gcc since icc also sets __GNUC__ */
#if defined __INTEL_COMPILER
#	define	__COMP	"icc"
#	define	__COMP_VER	__INTEL_COMPILER 

/* gcc */
#elif defined __GNUC__
#	define	__COMP	"gcc"
#	if defined(__GNUC_PATCHLEVEL__)
#		define __COMP_VER (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#	else
#		define __COMP_VER (__GNUC__ * 10000 + __GNUC_MINOR__ * 100)
#	endif

/* SGI MIPSpro */
/* this needs to be after gcc, since gcc under IRIX also sets _COMPILER_VERSION */
#elif defined(__sgi) && defined(_COMPILER_VERSION)
#	define	__COMP	"MIPSpro"
#	define	__COMP_VER	_COMPILER_VERSION

/* HP */
#elif defined __HP_aCC 
#	define	__COMP	"HP acc"
#	define	__COMP_VER	__HP_aCC  

/* DEC */
#elif defined __DECC 
#	define	__COMP	"DEC cc"
#	define	__COMP_VER	__DECC_VER   

/* MS VS */
#elif defined	_MSC_VER
#	define	__COMP	"vs"
#	define	__COMP_VER	_MSC_VER  

/* Sun */
#elif defined	__SUNPRO_C
#	define	__COMP	"Sun Workshop"
#	define	__COMP_VER	__SUNPRO_C  

/* unknown */
#else
#define	__COMP	"unknown"
#define	__COMP_VER	0 
#endif

/* ----------------------------  OS ---------------------------- */
/* linux */
#if defined	__linux
#	define	__OS	"linux"

/* macintosh */
#elif defined	macintosh
#	define	__OS	"Mac OS 9"

/* macintosh */
#elif defined	__MACOSX__
#	define	__OS	"Mac OS X"

/* HP */
#elif defined	hpux
#	define	__OS	"HP-UX"

/* Sun */
#elif defined	__sun
#	define	__OS	"Solaris"

/* IBM AIX */
#elif defined	_AIX
#	define	__OS	"AIX"

/* Compaq alpha */
#elif defined	ultrix
#	define	__OS	"Ultrix"

/* the BSD variants */
#elif defined	__FreeBSD__
#	define	__OS	"FreeBSD"

#elif defined	__NetBSD__
#	define	__OS	"NetBSD"

#elif defined	__OpenBSD__
#	define	__OS	"OpenBSD"

/* Windows */
#elif defined	_WIN32
#	define	__OS	"Win32"

/* Cygwin */
#elif defined	__CYGWIN__
#	define	__OS	"Cygwin"

/* SGI */
#elif defined	__sgi
#	define	__OS	"IRIX"

/* unknown */
#else
#	define	__OS	"unknown"
#endif

/*************************************************************************
 *
 * cdMPI sets flag to tell exit handler to call MPI_Finalize 
 *
 ************************************************************************/

/* flag set true when cdMPI called, tells exit handler to clean up mpi */
/* lint error saying never used for anything is fine -- 
 * only is used if mpi flags are set,
 * this is so that we do not call MPI_Finalize when on serial queue on
 * parallel machine - that causes machine to drop coe */
static int lgMPI;

/*cdMPI sets flag telling exit handler to call MPI_Finalize, must
 * call after cdInit when MPI is used */
void cdMPI(void) /* set flag so that exit handler will clean up MPI */
{
	lgMPI = TRUE;
	return;
}

static void SetFPEnv( void )
{
	/* >>chng 01 aug 07, added code to circumvent math library bug with gcc on
	 * alpha-linux machines, see bug report 51072 on http://bugzilla.redhat.com, PvH */
	/* >>chng 01 apr 17, added code for Solaris and SGI operating systems, PvH */
	/* this routine contains no code for alphas or crays, they do not need
	 * special code to enable FP exceptions since they are enabled by default */

	/* there is no command line option on MS Visual Studio to force crash */
#	if defined(_MSC_VER)
	unsigned int NewMask;

	/* | is a bitwise inclusive or, turns on bits
	 * 0|0 = 0
	 * 0|1 = 1|0 = 1|1 = 1 */
	NewMask = _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_INVALID;
	/* ~ is the unary bitwise complement - all bits flip */ 
	NewMask = ~NewMask;
	/*lint -e534 ignore value returned by function (nothing to do with it)*/
	_controlfp( NewMask , _MCW_EM );
	/*lint +e534 ignore value returned by function (nothing to do with it)*/

	/* this is the code for Linux PC (but not Linux alpha) to force crash */
	/* >>chng 04 apr 26, added support for AMD64, enable FPE traps for SSE/SSE2, PvH */
#	elif defined(__linux) && ( defined(__i386) || defined(__amd64) )
#	include <fpu_control.h>
	fpu_control_t Old_Mask, New_Mask;
#	if defined(__SSE__) || defined(__SSE2__)
	unsigned int SSE_Mask;
#	endif

	/*  | is a bitwise inclusive or, turns on bits     */
	/* 0|0 = 0                                         */
	/* 0|1 = 1|0 = 1|1 = 1                             */

	/*   _FPU_MASK_IM  0x01 Invalid          */
	/*   _FPU_MASK_DM  0x02 Denormalized     */
	/*   _FPU_MASK_ZM  0x04 Division-by-zero */
	/*   _FPU_MASK_OM  0x08 Overflow         */
	/*   _FPU_MASK_UM  0x10 Underflow        */
	/*   _FPU_MASK_PM  0x20 Inexact          */

	/*  ~ is the unary bitwise complement - all bits flip */

	/* this enables FPE traps for regular i387 FP instructions */

	fpu_control_t UnMask = ~((fpu_control_t)( _FPU_MASK_ZM | _FPU_MASK_IM  | _FPU_MASK_OM ));

	/* the following two macros are not defined on a Linux alpha ! */
	_FPU_GETCW(Old_Mask);

	New_Mask = Old_Mask & UnMask;

	_FPU_SETCW(New_Mask);

#	if defined(__SSE__) || defined(__SSE2__)

#	if defined(FLUSH_DENORM_TO_ZERO)
	/* using this causes denormalized numbers to be flushed to zero,
	 * which will speed up the code on Pentium 4 processors */
	SSE_Mask = 0x9900;
#	else
	/* this version allows denormalized numbers to be retained */
	SSE_Mask = 0x1900;
#	endif

	/* this enables FPE traps for SSE/SSE2 instructions */

	__asm__ volatile( "ldmxcsr %0" : : "m" (*&SSE_Mask) );

#	endif

	/* this is for IA64 systems running gcc or icc (e.g. SGI, HP, ...) */
#	elif defined(__ia64)

#	define FPSR_TRAP_VD     (1 << 0)        /* invalid op trap disabled */
#	define FPSR_TRAP_DD     (1 << 1)        /* denormal trap disabled */
#	define FPSR_TRAP_ZD     (1 << 2)        /* zero-divide trap disabled */
#	define FPSR_TRAP_OD     (1 << 3)        /* overflow trap disabled */
#	define FPSR_TRAP_UD     (1 << 4)        /* underflow trap disabled */
#	define FPSR_TRAP_ID     (1 << 5)        /* inexact trap disabled */

#	define FPSR_SF0_FTZ     (1 << 6)        /* flush denormalized numbers to zero */

#	if defined(__GNUC_EXCL__)
	/* __asm__ instructions are not supported by icc as of v9.0 */
#	define _IA64_REG_AR_FPSR    40

#	define ia64_getreg( regnum )       __asm__ volatile( "mov %0=ar%1" : "=r" (fpsr) : "i"(regnum) )
#	define ia64_setreg( regnum, val )  __asm__ volatile( "mov ar%0=%1" :: "i" (regnum), "r"(val): "memory" )
#	define ia64_serialize              __asm__ volatile( "srlz.i" );

	unsigned long fpsr, flags = FPSR_TRAP_VD | FPSR_TRAP_ZD | FPSR_TRAP_OD;

	ia64_getreg( _IA64_REG_AR_FPSR );
	fpsr &= ~flags;
#	if defined(FLUSH_DENORM_TO_ZERO)
	fpsr |= FPSR_SF0_FTZ;
#	endif
	ia64_setreg( _IA64_REG_AR_FPSR, fpsr );
	/* this prevents RAW and WAW dependency violations in case this ever gets inlined... */
	ia64_serialize;

#	elif defined(__INTEL_COMPILER) || defined(__HP_aCC)
	/* this is for icc on IA64 SGI machines and the HP compiler on the sdx */
	unsigned long fpgetmask();
	void fpsetmask(unsigned long);

	unsigned long fpsr = fpgetmask();
	fpsr |= FPSR_TRAP_VD | FPSR_TRAP_ZD | FPSR_TRAP_OD;
	fpsetmask( fpsr );
#	endif /* defined(__GNUC_EXCL__) */

	/* this is for Solaris and SGI to force crash */
#	elif defined(__sun) || defined(__sgi)
#	include <ieeefp.h>

	fp_except mask;

	/* >>chng 05 dec 30, accept FLUSH_DENORM_TO_ZERO as a synonym for HAVE_SUNMATH, PvH */
#	if defined(HAVE_SUNMATH) || defined(FLUSH_DENORM_TO_ZERO)
#	include <sunmath.h>

	/* >>chng 01 oct 09, disable gradual underflow on ultrasparc whith gcc
	 * (only needed for versions prior to 3.1, see Note 1).
	 *
	 * compile this module with:
	 *     gcc [ other options... ] -I<include-dir> -DHAVE_SUNMATH -c setfpenv.c
	 * link the program with:
	 *     gcc -L<library-dir> -o cloudy.exe *.o -lsunmath -lm
	 *
	 * you probably need to use -I<include-dir> and -L<library-dir> to point the
	 * compiler/linker to the location of the sunmath.h header file and libsunmath.so
	 * library (e.g., -I/opt/SUNWspro/prod/include/cc -L/opt/SUNWspro/lib ; note that
	 * the actual location may vary from one installation to another).
	 * See also bug report 4487 on http://gcc.gnu.org/cgi-bin/gnatsweb.pl
	 *
	 * Note 1: Starting with gcc 3.1, bug 4487 has been solved: -funsafe-math-optimizations
	 * will automatically disable gradual underflow. Hence using nonstandard_arithmetic()
	 * is no longer necessary. The option -funsafe-math-optimizations should be included
	 * both when compiling and linking:
	 *
	 * gcc [ other options... ] -funsafe-math-optimizations -c *.c
	 * gcc [ other options... ] -funsafe-math-optimizations -o cloudy.exe *.o -lm
	 *
	 * Note 2: Don't use nonstandard_arithmetic() with cc (the SunWorks/Forte compiler);
	 * use the -fast commandline option instead to disable gradual underflow (or use
	 * -fnonstd if you don't want all the other options enabled by -fast). The option
	 * -fast (or -fnonstd) should be included both when compiling and linking:
	 *
	 * cc [ other options... ] -fast -c *.c
	 * cc -fast -o cloudy.exe *.o -lm
	 *
	 * PvH */
	nonstandard_arithmetic();
#	endif

	/* enable floating point exceptions on sun and sgi */
	mask = fpgetmask();
	mask = mask | FP_X_INV | FP_X_OFL | FP_X_DZ;
	fpsetmask(mask);

#	elif defined(__alpha) && defined(__linux) && defined(__GNUC__)
#	define __USE_GNU
#	include <fenv.h>
	/* the following is not supported on all hardware platforms, but certainly for EV56
	 * and later. earlier versions may work as well, but that has not been tested.
	 * for details see https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=51072 */
#	ifdef FE_NONIEEE_ENV
	/* this prevents the infamous math library bug when compiling with gcc on alpha-linux
	 * machines. if this doesn't work on your system, the only alternative is to link
	 * against the Compaq math library: gcc *.o -lcpml -lm, or use ccc itself, PvH */
	fesetenv(FE_NONIEEE_ENV);
#	endif

#	endif
	return ;
}

/* =================================================================== */
void cdInit()
{
	long i;
	double vtest;
	double cs;
#	ifdef __unix
	char *ptr;
#	endif
	static int lgDateSet=FALSE;

	/* >>chng 05 dec 14, add test of endianness of the CPU, PvH */
	/* NB NB - this needs to be done before the first MALLOC !! */
	/* in C++ this should become an initializer of the cpu class */
	cpu.endian.c[0] = 0x12;
	cpu.endian.c[1] = 0x34;
	cpu.endian.c[2] = 0x56;
	cpu.endian.c[3] = 0x78;

	/* >>chng 05 dec 15, add signaling NaN for float and double to cpu struct, PvH */
	/* in C++ this should be replaced by numeric_limits<TYPE>::signaling_NaN() */
	if( sizeof(float) == 4 )
	{
#		ifdef __mips
		/* definition of signaling and quiet NaN is reversed on MIPS */
		cpu.Float_SNaN_Value = 0xffffffff;
#		else
		if( CPU_IS_BIG_ENDIAN || CPU_IS_LITTLE_ENDIAN )
		{
			/* this should work on most modern CPU's */
			cpu.Float_SNaN_Value = 0xffbfffff;
		}
		else
		{
			/* this is an unusual CPU -> bit pattern for SNaN is unknown */
			cpu.Float_SNaN_Value = -1;
		}
#		endif
	}
	else
	{
		/* this is an unusual CPU -> bit pattern for SNaN is unknown */
		cpu.Float_SNaN_Value = -1;
	}

	if( sizeof(double) == 8 )
	{
#		ifdef __mips
		/* definition of signaling and quiet NaN is reversed on MIPS */
		cpu.Double_SNaN_Value[0] = 0xffffffff;
		cpu.Double_SNaN_Value[1] = 0xffffffff;
#		else
		if( CPU_IS_BIG_ENDIAN )
		{
			/* this should work on most modern CPU's */
			cpu.Double_SNaN_Value[0] = 0xfff7ffff;
			cpu.Double_SNaN_Value[1] = 0xffbfffff;
		}
		else if( CPU_IS_LITTLE_ENDIAN )
		{
			/* this should work on most modern CPU's */
			cpu.Double_SNaN_Value[0] = 0xffbfffff;
			cpu.Double_SNaN_Value[1] = 0xfff7ffff;
		}
		else
		{
			/* this is an unusual CPU -> bit pattern for SNaN is unknown */
			cpu.Double_SNaN_Value[0] = -1;
			cpu.Double_SNaN_Value[1] = -1;
		}
#		endif
	}
	else
	{
		/* this is an unusual CPU -> bit pattern for SNaN is unknown */
		cpu.Double_SNaN_Value[0] = -1;
		cpu.Double_SNaN_Value[1] = -1;
	}

	/* set FP environment to trap FP exceptions, this should be before the first FP operation! */
	SetFPEnv();

	/* set the date saved in date.h into version.chDate if it has not already been sone */
	if( !lgDateSet )
	{
		/* this is the decade part of the year counted after 2000 */
		int nDecade = (YEAR-100)/10;
		char chDecade[2];

		/* create space for these strings */
		if( (version.chDate = (char *)MALLOC( sizeof(char)*INPUT_LINE_LENGTH )) == NULL )
			BadMalloc();
		if( (version.chVersion = (char *)MALLOC( sizeof(char)*INPUT_LINE_LENGTH )) == NULL )
			BadMalloc();

		lgDateSet = TRUE;
		sprintf(chDecade,"%i", nDecade );
		/* set date and version - this code will need to be modified in the year 2110 */
		if( nDecade > 9 )
		{
			fprintf(ioQQQ,"must update formation of chDate in cdInit \n");
			TotalInsanity();
		}
		if( DAY < 10 )
		{
			sprintf( version.chDate , "%s%1i%s0%1i", 
				chDecade,
				YEAR-100-nDecade*10,
				chMonth[MONTH],
				DAY );
			sprintf( version.chVersion , "%s%1i.%s.0%1i", 
				chDecade,
				YEAR-100-nDecade*10,
				chMonthInt[MONTH],
				DAY );
		}
		else
		{
			sprintf( version.chDate , "%s%1i%s%2i", 
				chDecade,
				YEAR-100-nDecade*10,
				chMonth[MONTH],
				DAY );
			sprintf( version.chVersion , "%s%1i.%s.%2i", 
				chDecade,
				YEAR-100-nDecade*10,
				chMonthInt[MONTH],
				DAY );
		}
		/* >>chng 06 mar 06, this is subversion a of 06.02 */
		strcat( version.chVersion , "a" );
		strcat( version.chDate , "a" );

		/* now generate info on how we were compiled */
		sprintf(version.chInfo , 
			"cdInit compiled on %s in OS %s using the %s %i compiler." , __DATE__  ,__OS , __COMP , __COMP_VER);

	}
	/*strcpy( version.chDate, "04Oct103" );
	 * >>chng 05 jan 01, version is now the date in format yy.mm.dd 
	strcpy( version.chVersion, " 96.01 " );*/

	/* set ioQQQ to standard output */
	ioQQQ = stdout;

	/* set ioStdin to standard input, will get line images from here */
	ioStdin = stdin;

	/* set flag saying that we have been called */
	lgcdInitCalled = TRUE;

	/* set ioPrnErr to stderr */
	ioPrnErr = stderr;

	/* but don't what to usually do this */
	lgPrnErr = FALSE;

	/* set MPI flag false, set true with cdMPI()*/
	lgMPI = FALSE;

	/* initialize lots of constants */
	cdDefines();

	/* >>chng 00 mar 10, moved call for grain block data init to Cloudy, PvH */

	/*need a fake call to atmdat_phfit so that ionization potentials are stored in 
	 * proper structures */
	cs = atmdat_phfit(1,1,1,13.7,0);
	if( cs<=0. ) 
		TotalInsanity();

	/*test if the following integer types have the correct width*/
	if( sizeof(int16) != 2 || sizeof(uint16) != 2 || sizeof(int32) != 4 || sizeof(uint32) != 4 )
		TotalInsanity();

	/*********************************************************
	 *  on a VAX compile with /G_FLOATING option on FORTRAN; *
	 *  following makes sure this happened.                  *
	 *********************************************************/
	vtest = 1e-35;
	vtest /= 1e35;
	if( vtest == 0. )
	{
		fprintf( ioQQQ, " Something is wrong with the double precision.  Use /g_floating on a VAX\n" );
	}

	/*********************************************************
	 *  check that we are being compiled with ANSI C or C++  *
	 *********************************************************/
	/* >>chng 02 feb 15, this test disabled - K&R compilers probably
	 * no longer exist, and this slows down execution of optimized code 
	 * on a P4 by roughtly 30% */
#	if !defined(__STDC__) && !defined(__cplusplus)
	{
		/* following should be set true to allow this test,
		 * set false to have non-ANSI compilers work */
		/*@-redef@*/
		enum {CHECKIT=FALSE};
		/*@+redef@*/
		if( CHECKIT )
		{
			fprintf(ioQQQ," I must be compiled using ANSI C options.  This was not enabled.\n");
			fprintf(ioQQQ," Visual Studio use the /Za flag.\n");
			fprintf(ioQQQ," GNU gcc use the -ansi flag.\n");
			fprintf(ioQQQ," Dec alpha use the -std1 flag.\n");
			fprintf(ioQQQ," Sparc use the -Xc flag.\n");
			puts( "[Stop in cdInit]" );
			cdEXIT(EXIT_FAILURE);
		}
	}
#	endif

	/* >>chng 02 feb 27, include this test, provided by Peter van Hoof */
	/* check that we ARE NOT being compiled by gcc version 2.96 */
#	if defined __GNUC__
#	if __GNUC__ == 2 && __GNUC_MINOR__ == 96
	fprintf(ioQQQ,"HELP!!! You compiled me with gcc 2.96!\n");
	fprintf(ioQQQ,"This version of gcc cannot compile Cloudy and must not be used!\n");
	fprintf(ioQQQ,"Use kgcc or update gcc to a functional version.\n");
	puts( "[Stop in cdInit]" );
	cdEXIT(EXIT_FAILURE);
#	endif
#	endif


	/* initialize some variables dealing with cloudy's interaction with machine environment */
	/* if TALK is TRUE then do standard printout
	 * if FALSE then never say anything */
	called.lgTalk = TRUE;
	/* this flag is needed to turn print on to ahave effect */
	called.lgTalkIsOK = TRUE;
	/* means talk not forced off by call to cdTalk*/
	called.lgTalkForcedOff = FALSE;

	/* these tell the molecular solver what zone and iteration it have
	 * been evaluated on */
	CO_Init();
	hmole_init();
	H2_Init();

	optimize.lgNoVary = FALSE;
	optimize.lgVaryOn = FALSE;
	optimize.lgOptimr = FALSE;
	grid.lgGrid = FALSE;

	/* this is a global variable in assertresults.h, and can be checked by
	 * other routines to see if asserts are ok - (most calculations will not use asserts,
	 * and this will be the only place values are set, although they will be checked in maincl) */
	lgAssertsOK = TRUE;
	lgBigBotch = FALSE;

#	ifdef __unix
	/* determine hostname; used by PHYMIR */
        if( (ptr = getenv("HOST")) != NULL )
		strcpy(optimize.HostName,ptr);
	else
		strcpy(optimize.HostName,"unknown");
	/* determine the no. of CPUs on this machine; used by PHYMIR */
#	if defined(_SC_NPROCESSORS_ONLN)  /* Sun Sparc, DEC Alpha */
	optimize.maxCPU = sysconf(_SC_NPROCESSORS_ONLN);
#	elif defined(_SC_NPROC_ONLN)      /* SGI Iris */
	optimize.maxCPU = sysconf(_SC_NPROC_ONLN);
#	elif defined(_SC_CRAY_NCPU)       /* Cray */
	optimize.maxCPU = sysconf(_SC_CRAY_NCPU);
#	else                              
	/* Other systems, supply no. of CPUs on OPTIMIZE PHYMIR command line */
	optimize.maxCPU = 1;
#	endif
#	else
	strcpy(optimize.HostName,"unknown");
	optimize.maxCPU = 1;
#	endif

	/* number of lines entered with cdLine
	 * both check that number less than NKRD, the limit
	 * the line save array is devined from 0 through input.nSave */
	input.nSave = -1;

	/* nRead is the number of the command in the input stream - many optimize options
	 * point to it to refer to the original command.  it is incremented before
	 * it is used, so will become 0.  it is the array element within the stack
	 * of emission lines */
	input.nRead = -1;

	/* this is number of init lines read in */
	input.nSaveIni = 0;
	input.lgUnderscoreFound = FALSE;
	input.lgBracketFound = FALSE;

	/* this is sanity check that lines are read in ok */
	for( i=0; i < NKRD; i++ )
	{
		strcpy( input.chCardSav[i], "error! - no line image input" );
	}

	/* initialize stuff for opening punch files, should not be done in zero() */
	punch.lgOpenUnits = TRUE;
	PunchFilesInit();

	/* start the timer to log execution time */
	cdSetExecTime();

	/* zero out lots of variables */
	zero();

#	ifdef DEBUG_FUN
	fputs( " <->cdInit()\n", debug_fp );
#	endif
	return;
}


/* =================================================================== */
/*cdEXIT the routine that should be called to exit cloudy */
#ifdef _MSC_VER
/* MS compiler directive saying that cdEXIT does not return */
__declspec(noreturn) 
#elif defined(__GNUC__) || ( defined(__INTEL_COMPILER) && defined(__linux) )
 __attribute__ ((noreturn))
#endif
void cdEXIT(int iexit)
{
	/*@-redef@*/
	enum {DEBUG_LOC=FALSE};
	/*@+redef@*/
	if( DEBUG_LOC )
	{
		fprintf(ioQQQ," cdEXIT called\n");
	}

	/* close any open units */
	/* flush ioQQQ */
	fflush( ioQQQ );

	ClosePunchFiles();

	/* this routine must be called upon exit or machine will appear to "hang",
	 * but must not be called on mpi machine when used in serial mode,
	 * or it will drop core */
	if( lgMPI )
	{
		fprintf(ioQQQ," MPI_Finalize called.\n");
#		ifdef MPI
		MPI_Finalize();
#		endif
	}
	exit( iexit );
}
