diff options
author | tradke <tradke@ebee0441-1374-4afa-a3b5-247f3ba15b9a> | 1999-06-25 11:40:06 +0000 |
---|---|---|
committer | tradke <tradke@ebee0441-1374-4afa-a3b5-247f3ba15b9a> | 1999-06-25 11:40:06 +0000 |
commit | de89a5c6b08b5ad9eb7044dc8b2c04a965b19982 (patch) | |
tree | 339e8646892a1638fc0d9b81c38cfed38acb238c | |
parent | f9c5c1bc939250652e057dcd06f5ce9701b8a0bb (diff) |
The hopefully final renaming of the FlexIO thorn.
git-svn-id: http://svn.cactuscode.org/arrangements/CactusPUGHIO/IOFlexIO/trunk@2 ebee0441-1374-4afa-a3b5-247f3ba15b9a
-rw-r--r-- | README | 20 | ||||
-rw-r--r-- | interface.ccl | 3 | ||||
-rw-r--r-- | param.ccl | 160 | ||||
-rw-r--r-- | schedule.ccl | 31 | ||||
-rw-r--r-- | src/DumpGH.c | 379 | ||||
-rw-r--r-- | src/DumpVar.c | 941 | ||||
-rw-r--r-- | src/GHExtension.c | 91 | ||||
-rw-r--r-- | src/IEEEIO/Arch.h | 72 | ||||
-rw-r--r-- | src/IEEEIO/FlexArrayTmpl.H | 180 | ||||
-rw-r--r-- | src/IEEEIO/IEEEIO.cc | 1363 | ||||
-rw-r--r-- | src/IEEEIO/IEEEIO.h | 13 | ||||
-rw-r--r-- | src/IEEEIO/IEEEIO.hh | 598 | ||||
-rw-r--r-- | src/IEEEIO/IO.cc | 371 | ||||
-rw-r--r-- | src/IEEEIO/IO.h | 63 | ||||
-rw-r--r-- | src/IEEEIO/IO.hh | 168 | ||||
-rw-r--r-- | src/IEEEIO/make.code.defn | 84 | ||||
-rw-r--r-- | src/Output2D.c | 283 | ||||
-rw-r--r-- | src/Output3D.c | 285 | ||||
-rw-r--r-- | src/RecoverGH.c | 343 | ||||
-rw-r--r-- | src/RestoreFile.c | 520 | ||||
-rw-r--r-- | src/Startup.c | 78 | ||||
-rw-r--r-- | src/Write2D.c | 479 | ||||
-rw-r--r-- | src/Write3D.c | 462 | ||||
-rw-r--r-- | src/ioFlexGH.h | 85 | ||||
-rw-r--r-- | src/make.code.defn | 3 |
25 files changed, 7075 insertions, 0 deletions
@@ -0,0 +1,20 @@ +Cactus Code Thorn IOFlexIO +Authors : John Shalf +Managed by : Thomas Radke <tradke@aei-potsdam.mpg.de> +Version : ... +CVS info : $Header$ +-------------------------------------------------------------------------- + +1. Purpose of the thorn + +This thorn provides John Shalf's IEEEIO (FlexIO) library as a thorn to Cactus. + +2. Dependencies of the thorn + +no dependencies + +3. Thorn distribution + +This thorn is available to ... + +4. Additional information diff --git a/interface.ccl b/interface.ccl new file mode 100644 index 0000000..b552ebd --- /dev/null +++ b/interface.ccl @@ -0,0 +1,3 @@ +# Interface definition for thorn IOFlexIO + +implements: IOFlexIO diff --git a/param.ccl b/param.ccl new file mode 100644 index 0000000..b9f6d66 --- /dev/null +++ b/param.ccl @@ -0,0 +1,160 @@ +# Parameter definitions for thorn IOFlexIO + + +############################################################################# +### declare IOFlexIO parameters +############################################################################# +private: + +########################## +# What variables to output +########################## +STRING output2D "Variables to output in 2D FlexIO file format" +{ + :: "default is to output nothing" +} "" +STRING output3D "Variables to output in 3D FlexIO file format" +{ + :: "default is to output nothing" +} "" +################ +# Various things +################ +LOGICAL checkpoint_FlexIO "Do checkpointing with IOFlexIO" +{ +} "no" +LOGICAL reuse_fh "Reuse file handles by closing files after each write operation." +{ +} "no" + + +############################################################################# +### import IOUtil parameters +############################################################################# +friend: IO + +#################### +# Output directories +#################### +EXTEND STRING IO_outdir "Name of IO output directory" +{ +} "." +EXTEND STRING IO_outdir2D "Name of IO 2D output directory, overrides IO_outdir" +{ +} "IO_outdir" +EXTEND STRING IO_outdir3D "Name of IO 3D output directory, overrides IO_outdir" +{ +} "IO_outdir" + + +######################## +# How often to do output +######################## +EXTEND INTEGER IO_every "How often to do IO output" +{ + -1:* :: +} -1 +EXTEND INTEGER IO_2Devery "How often to do 2D output, overrides IO_every" +{ + -1:* :: +} -1 +EXTEND INTEGER IO_3Devery "How often to do 3D output, overrides IO_every" +{ + -1:* :: +} -1 + + +################ +# various things +################ +EXTEND LOGICAL IO_verbose "Give extended screen output in IO?" +{ +} "no" +EXTEND LOGICAL IO_datestamp "Write date as attribute to IO 3D output file?" +{ +} "yes" +EXTEND LOGICAL IO_parameters "Write parameters to IO 3D output file?" +{ +} "yes" +EXTEND LOGICAL IO_structures "Write structures to IO 3D output file?" +{ +} "yes" + + +####################### +# Specific to 3D output +####################### +EXTEND KEYWORD iomode "Which mode for 3D IO" +{ + "proc" :: "every processor writes its share of data into a separate file" + "np" :: "data is collected and written by every N'th processor into a separate file, where N is specified by the parameter ioproc_every" + "onefile" :: "all output is written into a single file by processor 0" +} "proc" + +EXTEND INTEGER ioproc_every "Do IO on every N processors." +{ + 1:* :: "Must be a positive integer" +} 8 +EXTEND LOGICAL onefileperslice "Write one file per time slice, as opposed to all data in one file" +{ +} "no" +EXTEND LOGICAL unchunked "Don't write data in chunks." +{ +} "no" + + +############################################## +# Downsampling parameters (only for 3D output) +############################################## +EXTEND INTEGER downsample_x "Factor by which to downsample output in x direction. Point (0,0,0) is always included." +{ + 1:* :: "Must be a positive integer" +} 1 +EXTEND INTEGER downsample_y "Factor by which to downsample output in y direction. Point (0,0,0) is always included." +{ + 1:* :: "Must be a positive integer" +} 1 +EXTEND INTEGER downsample_z "Factor by which to downsample output in z direction. Point (0,0,0) is always included." +{ + 1:* :: "Must be a positive integer" +} 1 +EXTEND LOGICAL out_single "Output 3D data in single precision ? This parameter is ignored for Cactus compiled with SINGLE_PRECISION." +{ +} "no" + + +################################### +# Checkpointing/recovery parameters +################################### +EXTEND LOGICAL checkpoint_ID "Checkpoint initial data ?" +{ +} "no" +EXTEND LOGICAL checkpoint_keep_all "Keep all checkpoint files ?" +{ +} "no" +EXTEND LOGICAL recover "Recover from a checkpoint file ?" +{ +} "no" +EXTEND INTEGER checkpoint_every "Checkpoint every x iterations" +{ + -1:* :: "negative values disable checkpointing" +} -1 +EXTEND INTEGER checkpoint_keep "How many checkpoint files to keep" +{ + 1:* :: "1 overwrites the latest checkpoint file" +} 1 +EXTEND STRING checkpoint_file "File name for regular checkpoint" +{ +} "checkpoint" +EXTEND STRING checkpoint_ID_file "File name for initial data checkpoint" +{ +} "checkpointID" +EXTEND STRING recover_file "File name of recovery file" +{ +} "checkpoint" +EXTEND STRING checkpoint_dir "Output directory for checkpoint files" +{ +} "." +EXTEND STRING recovery_dir "Directory to look for the recovery file" +{ +} "." diff --git a/schedule.ccl b/schedule.ccl new file mode 100644 index 0000000..ddf2d19 --- /dev/null +++ b/schedule.ccl @@ -0,0 +1,31 @@ +# Schedule definitions for thorn IOFlexIO + +######################################################################## +### register IOFlexIO routines +######################################################################## +schedule IOFlexIO_Startup at STARTUP +{ + LANG:C +} "IOFlexIO startup routine" + + +######################################################################## +### register checkpointing routines +######################################################################## +if (checkpoint_FlexIO && checkpoint_ID) { + schedule IOFlexIO_InitialDataDumpGH at CCTK_CPINITIAL { + LANG:C + } "Initial data checkpoint routine" +} + +if (checkpoint_FlexIO && checkpoint_every > 0) { + schedule IOFlexIO_ConditionallyDumpGH at CCTK_CHECKPOINT { + LANG:C + } "Regular checkpoint routine" +} + +if (checkpoint_FlexIO) { + schedule IOFlexIO_TerminationDumpGH at CCTK_TERMINATE { + LANG:C + } "Termination checkpoint routine" +} diff --git a/src/DumpGH.c b/src/DumpGH.c new file mode 100644 index 0000000..fd366ba --- /dev/null +++ b/src/DumpGH.c @@ -0,0 +1,379 @@ + /*@@ + @file DumpGH.c + @date Wed Jun 10 14:13:35 1998 + @author Paul Walker + @desc + DumpGH dumps an entire grid hierarchy (except for 1D arrays) to a + checkpoint file. This file also contains the different wrappers for + DumpGH: in case of initial data, termination or regular checkpointing + @enddesc + @version $Id$ + @@*/ + +static char *rcsid = "$Id$"; + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "cctk.h" +#include "flesh.h" +#include "declare_parameters.h" +#include "Groups.h" +#include "Comm.h" +#include "GHExtensions.h" +#include "CactusBase/IOUtil/src/ioGH.h" +#include "ioFlexGH.h" + + +/* we define the values for cp_type; cp_type will be set by the + the wrappers indicating what type of checkpointiung is going to happen; + That way we allow for more flex. treatment, eg. directory/filenames + If you put more than 5 CP_*s here, enlarge the made_cpdir array. */ + +#define CP_RECOVER_DATA 0 +#define CP_EVOLUTION_DATA 1 +#define CP_INITIAL_DATA 2 + +/* these have to be consistent with the defaults in param.ccl */ +#define CP_DEFAULT_FNAME "checkpoint" +#define CP_DEFAULT_IDFNAME "checkpointID" + +/* cp_type indicates the type of checkpoint (cp); default: recovering data + The reason why RECOVER is default is, that RecoverGH doesn't share the scope + of the cp_type variable the way the routines in this file do. Fix for this ? */ +static int cp_type=CP_RECOVER_DATA; + +void IOFlexIO_DumpGH (cGH *GH, int called_from); + + + + /*@@ + @routine IOFlexIO_ConditionallyDumpGH + @date Fri Aug 21 14:38:25 1998 + @author Gabrielle Allen + @desc + registered as CCTK_CHECKPOINT, checks if it's time for + checkpointing, sets the checkpoint type and calls DumpGH + @enddesc + @calls IOFlexIO_DumpGH + @calledby rfrTraverse + @history + + @endhistory + +@@*/ + +void IOFlexIO_ConditionallyDumpGH (cGH *GH) +{ + DECLARE_PARAMETERS + + if (checkpoint_every > 0 && + GH->iteration % checkpoint_every == 0) { + if (IO_verbose) { + printf ("------------------------------------------------------------\n"); + printf ("Dumping periodic checkpoint file at iteration %d\n", + GH->iteration); + } + IOFlexIO_DumpGH (GH, CP_EVOLUTION_DATA); + } +} + + + /*@@ + @routine IOFlexIO_TerminationDumpGH + @date Fri Aug 21 14:40:21 1998 + @author Gabrielle Allen + @desc + This routine is registered as CCTK_TERMINATE, it checks if a + termination signal is raised (that is: global var cactus_terminate + set to TERMINATION_RAISED_BRDCAST by ./steppers/TerminationStepper.c) + and checkpoints all available grid hierarchies. + @enddesc + @calls IOFlexIO_DumpGH + @calledby rfrTraverse + @history + + @endhistory + +@@*/ + +void IOFlexIO_TerminationDumpGH (cGH *GH) +{ +/*** FIXME: no cactus_terminate flag anymore ? ***/ +#if 0 +#ifdef CACTUSBASE_PUGH + if (cactus_terminate == TERMINATION_RAISED_BRDCAST) { + IOFlexIO_DumpGH (GH, CP_EVOLUTION_DATA); + } +#endif /* CACTUSBASE_PUGH */ +#else + CCTK_WARN (1, "TerminationDumpGH() not yet implemented !"); +#endif +} + + /*@@ + @routine IOFlexIO_InitialDataDumpGH + @date Fri Aug 21 14:46:28 1998 + @author Gerd Lanfermann + @desc + This routine dumps the initial data of the GHs, registered + as CCTK_CPINITIAL; sets cp_type to CP_INITIAL_DATA and calls + DumpGH; BoxInBox needs special treatment since for now. + @enddesc + @calls IOFlexIO_DumpGH + @calledby rfrTraverse + @history + + @endhistory + +@@*/ + +void IOFlexIO_InitialDataDumpGH (cGH *GH) +{ + cGH *helperGH; + static int maxlev = 0; + int l; + +/*** FIXME ***/ +#if 0 +#ifdef THORN_BOXINBOX + /* This will go once we reorganize the CINitial calling structure! */ + /* or have a function to deregister a rfr routine */ + /* BoxinBox does its own: get maxlevel (once!) and do dumpGH for all GH*/ + /* when this is called by the highest box */ + if (Contains("boxinbox","yes")) { + if (maxlev==0) while (GetGHbyLevel(0,maxlev+1,0)) maxlev++; + if (maxlev==0) return; + + if (GH->level==maxlev) + for (l=0;l<=maxlev;l++) { + printf("Dumping BoxinBox level %d of %d \n",l,maxlev); + IOFlexIO_DumpGH(GetGHbyLevel(0,l,0), CP_INITIAL_DATA); + } + return; + } +#endif +#endif + IOFlexIO_DumpGH (GH, CP_INITIAL_DATA); +} + + +void IOFlexIO_IEEEIOStructDump (cGH *GH, IOFile iof) +{ + CCTK_INT4 i_temp; + CCTK_REAL d_temp; + ioGH *ioUtilGH; + + /* Get the handle for IOUtil extensions */ + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + + i_temp = GH->iteration; + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "GH$iteration", FLEXIO_INT4, + 1, &i_temp)); + + i_temp = ioUtilGH->ioproc_every; + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "GH$ioproc_every", FLEXIO_INT4, + 1, &i_temp)); + + i_temp = CCTK_GetnProcs (GH); + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "GH$nprocs", FLEXIO_INT4, + 1, &i_temp)); + + d_temp = GH->time; + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "GH$time", FLEXIO_REAL, + 1, &d_temp)); +} + + + /*@@ + @routine IOFlexIO_DumpGH + @date Fri Aug 21 15:13:13 1998 + @author Paul Walker + @desc + The heart of checkpointing. Called by the different wrappers, this + routines get the appropriate filename by IOUtil_PrepareFilename() and + then dumps away using the dump routines from IO ... + @enddesc + @calls IO_DumpGF IO_DumpArray IO_DumpScalar + @history + @hdate Oct 4 1998 @hauthor Gabrielle Allen + @hdesc Removed code which checked and reset unchunked, assume that this + is done when the variable is first set. + @hdate Nov 4 1998 @hauthor Gabrielle Allen + @hdesc Do a forced synchronization before checkpointing + @hdate Apr 16 1999 @hauthor Thomas Radke + @hdesc Removed forced sync before checkpointing (just do a normal sync) + Introduced a ring buffer for keeping last <checkpoint_keep> files + @endhistory + +@@*/ + +void IOFlexIO_DumpGH (cGH *GH, int called_from) +{ + DECLARE_PARAMETERS + char dumpfname [1024], tmpfname [1024]; + IOFile iof; + int index, wrotech; + int timelevel, current_timelevel; + int forceSync; + int old_downsample_x, old_downsample_y, old_downsample_z; + int old_out_single; + ioGH *ioUtilGH; + cTimer total_time, write_time; + static char **dumpfnames = NULL; /* dump filename ring buffer */ + static int findex = 0; /* index into ring buffer */ + + + /* allocate the ring buffer for filenames */ + if (! dumpfnames) + dumpfnames = (char **) calloc (checkpoint_keep, sizeof (char *)); + + /* initialize timers */ + CactusResetTimer (&total_time); + CactusResetTimer (&write_time); + + CactusStartTimer (&total_time); + + /* Get the handle for IOUtil xtensions */ + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + + /* disable downsampling after saving original downsampling params */ + old_downsample_x = ioUtilGH->downsample_x; + old_downsample_y = ioUtilGH->downsample_y; + old_downsample_z = ioUtilGH->downsample_z; + ioUtilGH->downsample_x = ioUtilGH->downsample_y = ioUtilGH->downsample_z = 1; + + /* disable output in single precision */ + old_out_single = ioUtilGH->out_single; + ioUtilGH->out_single = 0; + + /* sync all groups */ + for (index = 0; index < CCTK_GetNumGroups (); index++) { + char *gname = CCTK_GetGroupName (index); + + CCTK_SyncGroup (GH, gname); + free (gname); + } + + /* get the base filename ... */ + IOUtil_PrepareFilename (GH, NULL, dumpfname, called_from, + CCTK_GetMyProc (GH) / ioUtilGH->ioproc_every, + ioUtilGH->unchunked); + + /* ... and append the extension */ + sprintf (tmpfname, "%s.chkpt_tmp.ieee", dumpfname); + sprintf (dumpfname, "%s.chkpt.ieee", dumpfname); + + /* Now open the file */ + if (CCTK_GetMyProc (GH) == ioUtilGH->ioproc) { + if (IO_verbose) + printf ("Creating file %s\n", tmpfname); + iof = IEEEopen (tmpfname, "w"); + if (! IOisValid (iof)) { + CCTK_WARN (1, "Can't open checkpoint file. Checkpointing is skipped"); + return; + } + } else + iof = (IOFile) NULL; + + /* Great; Now start dumping away! */ + CactusResetTimer (&write_time); + CactusStartTimer (&write_time); + + if (IO_verbose) + printf ("Dumping Params ...\n -- "); + + /* first the parameters ... */ + if (iof) { +/*** FIXME ***/ +#if 0 + if (IO_parameters) + IOFlexIO_IEEEIOparamDump (iof); +#endif + if (IO_structures) + IOFlexIO_IEEEIOStructDump (GH, iof); + } + + wrotech = 0; + + /* ... now the variables */ + for (index = 0; index < CCTK_GetNumVars (); index++) { + + /* let only variables pass which have storage assigned */ + if (! CCTK_QueryGroupStorage_ByIndex (GH, + CCTK_GetGroupIndexFromVar_ByIndex (index))) + continue; + + if (IO_verbose) { + char *varname = CCTK_GetVarName (index); + + printf (" %s", varname); + wrotech += strlen (varname) + 1; + if (wrotech > 65) { + printf ("\n "); + wrotech = 0; + } + } + + /* get the current timelevel */ + current_timelevel = CCTK_GetNumTimeLevels (index) - 1; + if (current_timelevel > 0) + current_timelevel--; + + /* now dump all timelevels up to the current */ + for (timelevel = 0; timelevel <= current_timelevel; timelevel++) + IOFlexIO_DumpVar (GH, index, timelevel, iof); + } /* end of loop over all variables */ + + CactusStopTimer (&write_time); + + CACTUS_IEEEIO_ERROR (IOclose (iof)); + + if (IO_verbose) + printf ("\nTime to write: %lf sec (%lf sec per Grid Func)\n", +/*** FIXME: choose right component of basic[] ***/ + write_time.total.basic [0], + write_time.total.basic [0] / CCTK_GetNumVars ()); + +#ifndef WIN32 + /* PW: This should work on WIN32 also, since it + is in stdio.h. Someone should check this + if/when we go into WIN32 production. + */ + if (CCTK_GetMyProc (GH) == ioUtilGH->ioproc) { + if (rename (tmpfname, dumpfname)) { + char msg [512]; + + sprintf (msg, "Could not rename temporary checkpoint file %s to %s", tmpfname, dumpfname); + CCTK_WARN (0, msg); + /* should not return here */ + } + } +#endif + + /* delete the oldest dumpfile if checkpoint_keep_all isn't set + and put the new filename into the ring buffer */ + if (dumpfnames [findex]) { + if (! checkpoint_keep_all) + remove (dumpfnames [findex]); + free (dumpfnames [findex]); + } + dumpfnames [findex] = strdup (dumpfname); + findex = (findex + 1) % checkpoint_keep; + + /* restore output precision flag */ + ioUtilGH->out_single = old_out_single; + + /* restore original downsampling params */ + ioUtilGH->downsample_x = old_downsample_x; + ioUtilGH->downsample_y = old_downsample_y; + ioUtilGH->downsample_z = old_downsample_z; + + CactusStopTimer (&total_time); + if (IO_verbose) { +/*** FIXME: choose right component of basic[] ***/ + printf ("Time to checkpoint: %lf sec\n", total_time.total.basic [0]); + printf ("------------------------------------------------------------\n"); + } +} diff --git a/src/DumpVar.c b/src/DumpVar.c new file mode 100644 index 0000000..77325b8 --- /dev/null +++ b/src/DumpVar.c @@ -0,0 +1,941 @@ +/*@@ + @file DumpVar.c + @desc + Do the actual writing of a 3D grid function, for output or for checkpointing + @enddesc + @history + @hauthor Gabrielle Allen @hdate Oct 17 1998 + @hdesc Split into subroutines and tidied. + @hauthor Gabrielle Allen @hdate 19 Oct 1998 + @hdesc Changed names ready for thorn_IO + @hauthor Thomas Radke @hdate 16 Mar 1999 + @hdesc Converted to Cactus 4.0 + introduced separate downsample values for each dimension + @hauthor Thomas Radke @hdate 17 Mar 1999 + @hdesc Changed naming: IEEEIO -> IOFlexIO + @hauthor Thomas Radke @hdate 17 Mar 1999 + @hdesc fixed errors for MPI + @hauthor Thomas Radke @hdate 12 Apr 1999 + @hdesc Store full variable name in IOFlexIO_AddCommonAttributes() + @hauthor Thomas Radke @hdate 16 Apr 1999 + @hdesc Added groupname, grouptype, ntimelevels, and current timelevel + as common attributes + @hendhistory + @version $Id$ + @@*/ + +static char *rcsid = "$Id$"; + +#include <stdio.h> +#include <stdlib.h> +#ifdef SGI +#include <time.h> +#endif + +#include "cctk.h" +#include "flesh.h" +#include "Groups.h" +#include "GroupsOnGH.h" +#include "Comm.h" +#include "WarnLevel.h" +#include "GHExtensions.h" +#include "declare_parameters.h" +#ifdef CACTUSBASE_PUGH +#include "CactusBase/pugh/src/include/pugh.h" +#endif +#include "CactusBase/IOUtil/src/ioGH.h" +#include "ioFlexGH.h" + + +#define IOTAGBASE 20000 /* This may break on more than 2000 processors */ + + +static char *char_time_date = NULL; + +/* local function prototypes */ +void IOFlexIO_DumpScalar (cGH *GH, int index, int timelevel, IOFile iof, + int ioflex_type); +void IOFlexIO_DumpGF (cGH *GH, int index, int timelevel, IOFile iof, + int ioflex_type, int mpi_type, int element_size); +void IOFlexIO_DumpArray (cGH *GH, int index, int timelevel, IOFile iof, + int ioflex_type, int mpi_type, int element_size); +void IOFlexIO_getDumpData (cGH *GH, int index, int timelevel, void **outme, + int *free_outme, CCTK_INT4 bnd [9], int element_size); +void IOFlexIO_eachProcDump (cGH *GH, int index, int timelevel, void *outme, + CCTK_INT4 bnd [9], IOFile iof, int ioflex_type); +void IOFlexIO_procDump (IOFile iof, cGH *GH, int index, void *outme, + CCTK_INT4 bnd [9], int ioflex_type); +void IOFlexIO_AddChunkAttributes (cGH *GH, int index, CCTK_INT4 chunk_origin [3], + IOFile iof); +void IOFlexIO_AddCommonAttributes (cGH *GH, int index, int timelevel, + CCTK_INT4 gsz [3], IOFile iof); + + + +/*@@ + @routine IOFlexIO_DumpVar + @date 16 Apr 1999 + @author Thomas Radke + @desc + Generic dump routine, just calls the appropriate dump routine + @enddesc + @calls + @calledby IOFlexIO_DumpGH IOFlexIO_Write3D + + @var GH + @vdesc Pointer to CCTK grid hierarchy + @vtype cGH + @vio in + @endvar + @var index + @vdesc global index of the variable to be dumped + @vtype int + @vio in + @endvar + @var timelevel + @vdesc the timelevel to store + @vtype int + @vio in + @endvar + @var iof + @vdesc the IEEEIO file to dump to + @vtype IOFile + @vio in + @endvar + @history + @endhistory + +@@*/ +void IOFlexIO_DumpVar (cGH *GH, int index, int timelevel, IOFile iof) +{ + char msg [80]; + ioGH *ioUtilGH; + int variable_type; + int ioflex_type, mpi_type = 0, element_size; + + /* Get the handle for IOUtil extensions */ + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + + variable_type = CCTK_GetVarVType (index); + + switch (variable_type) { + case CCTK_VARIABLE_REAL: + ioflex_type = ioUtilGH->out_single ? FLEXIO_REAL4 : FLEXIO_REAL; + element_size = ioUtilGH->out_single ? + sizeof (CCTK_REAL4) : sizeof (CCTK_REAL); +#ifdef MPI + mpi_type = ioUtilGH->out_single ? PUGH_MPI_REAL4 : PUGH_MPI_REAL; +#endif + break; + + case CCTK_VARIABLE_INTEGER: + ioflex_type = FLEXIO_INT; + element_size = sizeof (CCTK_INT); +#ifdef MPI + mpi_type = PUGH_MPI_INT; +#endif + break; + + case CCTK_VARIABLE_CHAR: + ioflex_type = FLEXIO_CHAR; + element_size = sizeof (CCTK_CHAR); +#ifdef MPI + mpi_type = PUGH_MPI_CHAR; +#endif + break; + + case CCTK_VARIABLE_COMPLEX: + CCTK_WARN (1, + "Don't know how to dump complex datatype in IOFlexIO_DumpScalar"); + return; + + default: + sprintf (msg, "Unknown variable type %d in IOFlexIO_DumpVar", variable_type); + CCTK_WARN (1, msg); + return; + } + + switch (CCTK_GetVarGType (index)) { + case GROUP_SCALAR: + IOFlexIO_DumpScalar (GH, index, timelevel, iof, ioflex_type); + break; + + case GROUP_ARRAY: + IOFlexIO_DumpArray (GH, index, timelevel, iof, ioflex_type, mpi_type, + element_size); + break; + + case GROUP_GF: + IOFlexIO_DumpGF (GH, index, timelevel, iof, ioflex_type, mpi_type, + element_size); + break; + + default: + sprintf (msg, "Invalid group type %d in IOFlexIO_DumpVar", + CCTK_GetVarGType (index)); + CCTK_WARN (1, msg); + return; + } +} + + +/*@@ + @routine IOFlexIO_DumpScalar + @date 16 Apr 1999 + @author Thomas Radke + @desc + Dumps a SCALAR type variable into a IEEEIO file + @enddesc + @calls + @calledby IOFlexIO_DumpVar + + @var GH + @vdesc Pointer to CCTK grid hierarchy + @vtype cGH + @vio in + @endvar + @var index + @vdesc global index of the variable to be dumped + @vtype int + @vio in + @endvar + @var timelevel + @vdesc the timelevel to store + @vtype int + @vio in + @endvar + @var iof + @vdesc the IEEEIO file to dump to + @vtype IOFile + @vio in + @endvar + @var ioflex_type + @vdesc the IOFlexIO datatype to store + @vtype int + @vio in + @endvar + @history + @endhistory + +@@*/ +void IOFlexIO_DumpScalar (cGH *GH, int index, int timelevel, IOFile iof, + int ioflex_type) +{ + ioGH *ioUtilGH; + CCTK_INT gsz [] = {0, 0, 0}; /* not needed here ? */ + int dim [] = {1}; /* size of dimensions for scalar */ + + + /* Get the handle for IOUtil extensions */ + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + + if (CCTK_GetMyProc (GH) != ioUtilGH->ioproc) + return; + + /* first dump the data then add the attributes */ + CACTUS_IEEEIO_ERROR (IOwrite (iof, ioflex_type, 1, dim, + CCTK_GetVarDataPtr_ByIndex (GH, timelevel, index))); + IOFlexIO_AddCommonAttributes (GH, index, timelevel, gsz, iof); +} + + +/*@@ + @routine IOFlexIO_DumpArray + @date 16 Apr 1999 + @author Thomas Radke + @desc + Dumps a SCALAR type variable into a IEEEIO file + @enddesc + @calls + @calledby IOFlexIO_DumpVar + + @var GH + @vdesc Pointer to CCTK grid hierarchy + @vtype cGH + @vio in + @endvar + @var index + @vdesc global index of the variable to be dumped + @vtype int + @vio in + @endvar + @var timelevel + @vdesc the timelevel to store + @vtype int + @vio in + @endvar + @var iof + @vdesc the IEEEIO file to dump to + @vtype IOFile + @vio in + @endvar + @var ioflex_type + @vdesc the IOFlexIO datatype to store + @vtype int + @vio in + @endvar + @var mpi_type + @vdesc the MPI datatype to communicate + @vtype int + @vio in + @endvar + @var element_size + @vdesc the size of an element of variable + @vtype int + @vio in + @endvar + @history + @endhistory + +@@*/ +void IOFlexIO_DumpArray (cGH *GH, int index, int timelevel, IOFile iof, + int hdf5io_type, int mpi_type, int element_size) +{ + CCTK_WARN (1, "Dumping of arrays not yet supported"); +} + + +/*@@ + @routine IOFlexIO_DumpGF + @date July 1998 + @author Paul Walker + @desc + + @enddesc + @calls + @calledby IOFlexIO_DumpVar + + @var GH + @vdesc Pointer to CCTK grid hierarchy + @vtype cGH + @vio in + @endvar + @var index + @vdesc global index of the variable to be dumped + @vtype int + @vio in + @endvar + @var timelevel + @vdesc the timelevel to store + @vtype int + @vio in + @endvar + @var iof + @vdesc the IEEEIO file to dump to + @vtype IOFile + @vio in + @endvar + @var ioflex_type + @vdesc the IOFlexIO datatype to store + @vtype int + @vio in + @endvar + @var mpi_type + @vdesc the MPI datatype to communicate + @vtype int + @vio in + @endvar + @var element_size + @vdesc the size of an element of variable + @vtype int + @vio in + @endvar + + @history + @hdate Wed Sep 2 10:14:17 1998 @hauthor Tom Goodale + @hdesc Merged in Szu-Wen's Panda changes + @hdate 26 Sep 1998 + @hauthor Gabrielle Allen + @hdesc Moved into separate file, tidied up, changed parameter names. + Removed zoom_active flag + @hauthor Thomas Radke + @hdesc New parameter timelevel specifies the timelevel to dump + @endhistory + +@@*/ +void IOFlexIO_DumpGF (cGH *GH, int index, int timelevel, IOFile iof, + int ioflex_type, int mpi_type, int element_size) +{ + DECLARE_PARAMETERS + ioGH *ioUtilGH; + pGH *pughGH; + int myproc; + int nprocs; + CCTK_INT4 bnd [9]; /* Lower bounds, size and global size of data we send */ + void *outme; /* pointer to the data to be dumped */ + int free_outme; /* indicates whether data buffer needs freeing */ +#ifdef SGI + /* Get the date if we can */ + time_t t = time (NULL); + char_time_date = asctime (localtime (&t)); +#endif +#ifdef MPI + int i; + MPI_Status ms; +#endif + + /* Get the handles for pugh and IO extensions */ + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + pughGH = (pGH *) GH->extensions [CCTK_GetGHExtensionHandle ("PUGH")]; + + myproc = CCTK_GetMyProc (GH); + nprocs = CCTK_GetnProcs (GH); + + /* If I'm not vertex centered, bail (for now) */ + if (pughGH->stagger != PUGH_VERTEXCTR) { + CCTK_WARN (1, "3D Output for staggered variables not available"); + return; + } + + /* Get the pointer to the data we want to output (includes downsampling) */ + IOFlexIO_getDumpData (GH, index, timelevel, &outme, &free_outme, bnd, + element_size); + + if (ioUtilGH->ioproc_every == 1) { + + /* + * OUTPUT ON EVERY PROCESSOR + */ + + IOFlexIO_eachProcDump (GH, index, timelevel, outme, bnd, iof, ioflex_type); + + } + +#ifdef MPI + else if (myproc != ioUtilGH->ioproc) { + + int outgoing; + + outgoing = bnd [3] * bnd [4] * bnd [5]; + + /* + * OUTPUT ON A SUBSET OF PROCESSORS + */ + + /* Send the geometry and data from the processors which aren't + * outputing to the ones that are + */ + + /* GEOMETRY SEND */ + CACTUS_MPI_ERROR (MPI_Send (bnd, 6, PUGH_MPI_INT4, ioUtilGH->ioproc, + 2*myproc+IOTAGBASE+1, pughGH->PUGH_COMM_WORLD)); + /* DATA SEND */ + CACTUS_MPI_ERROR (MPI_Send (outme, outgoing, mpi_type, ioUtilGH->ioproc, + 2*myproc+IOTAGBASE, pughGH->PUGH_COMM_WORLD)); + if (IO_verbose) { + printf ("Sent %d data points\n", outgoing); + fflush (stdout); + } + + } else if (myproc == ioUtilGH->ioproc) { + + int lsz [3]; + + /* First calculate the local size and reserve the chunk */ + if (ioUtilGH->ioproc_every >= nprocs) { + + /* One output file ... the local chunk size is the global size */ + lsz [0] = bnd [6]; lsz [1] = bnd [7]; lsz [2] = bnd [8]; + } + + /* Dump data held on output processor */ + + if (ioUtilGH->unchunked) + IOreserveChunk (iof, ioflex_type, 3, lsz); + + /* first the data then the attributes */ + IOFlexIO_procDump (iof, GH, index, outme, bnd, ioflex_type); + IOFlexIO_AddCommonAttributes (GH, index, timelevel, &bnd [6], iof); + + /* Dump data from all other processors */ + + for (i = myproc+1; i < myproc+ioUtilGH->ioproc_every && i < nprocs; i++) { + + /* Allocate temp to the right size (based on GH->ub [i] et..) */ + void *tmpd; + int incoming; + + /* Receive bounds information */ + CACTUS_MPI_ERROR (MPI_Recv (bnd, 6, PUGH_MPI_INT4, i, 2*i+IOTAGBASE+1, + pughGH->PUGH_COMM_WORLD, &ms)); + + incoming = bnd [3] * bnd [4] * bnd [5]; + + tmpd = malloc (incoming * element_size); + CACTUS_MPI_ERROR (MPI_Recv (tmpd, incoming, mpi_type, i, + 2*i+IOTAGBASE, pughGH->PUGH_COMM_WORLD, &ms)); + + IOFlexIO_procDump (iof, GH, index, tmpd, bnd, ioflex_type); + free (tmpd); + + } /* End loop over processors */ + } /* End myproc = 0 */ + + CACTUS_MPI_ERROR (MPI_Barrier (pughGH->PUGH_COMM_WORLD)); +#endif /* MPI */ + + if (free_outme) + free (outme); +} + + + /*@@ + @routine IOFlexIO_AddCommonAttributes + @date July 1998 + @author Paul Walker + @desc + Add "Common" attributes, these are the GF name, the current date, + simulation time, origin, bounding box, gridspacings (both downsampled + and evolution), global grid size, number of processors and iteration + number. Note that the datestamp should be turned of if you are byte + comparing two output files. + @enddesc + @calls + @calledby + @history + @hdate Wed Sep 2 10:15:22 1998 @hauthor Tom Goodale + @hdesc Abstracted WRITE_ATTRIBUTE to merge in Szu-Wen's Panda changes. + @hdate Apr 16 1999 @hauthor Thomas Radke + @hdesc Added attributes groupname, grouptype, ntimelevels, + and current timelevel to be stored + @hdate May 02 1999 @hauthor Thomas Radke + @hdesc Made chunked attribute nioprocs common so that it can be found by + the recombiner even for unchunked datasets (eg. SCALARs) + @hdate May 05 1999 @hauthor Thomas Radke + @hdesc Added "unchunked" attribute to distinguish between chunked/unchunked + output files + @endhistory + +@@*/ +void IOFlexIO_AddCommonAttributes (cGH *GH, int index, int timelevel, + CCTK_INT4 gsz [3], IOFile iof) +{ + DECLARE_PARAMETERS + CCTK_REAL d3_to_IO [3]; /* buffer for writing doubles to IEEEIO */ + CCTK_INT4 i_to_IO; /* buffer for writing an int to IEEEIO */ + char *name, *gname; + pGH *pughGH; + ioGH *ioUtilGH; + + /* Get the handles for pugh and IO extensions */ + pughGH = (pGH *) GH->extensions [CCTK_GetGHExtensionHandle ("PUGH")]; + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + + name = CCTK_GetFullName (index); + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "name", BYTE, + strlen (name) + 1, name)); + free (name); + + gname = CCTK_GetGroupNameFromVar_ByIndex (index); + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "groupname", BYTE, + strlen (gname) + 1, gname)); + + i_to_IO = CCTK_GetVarGType (index); + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "grouptype", FLEXIO_INT4, + 1, &i_to_IO)); + + i_to_IO = CCTK_GetNumTimeLevels (index); + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "ntimelevels", FLEXIO_INT4, + 1, &i_to_IO)); + + i_to_IO = timelevel; + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "timelevel", FLEXIO_INT4, + 1, &i_to_IO)); + + if (char_time_date && IO_datestamp) + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "date", BYTE, + strlen (char_time_date) + 1, char_time_date)); + + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "time", FLEXIO_REAL, 1,&GH->time)); + + /* FIXME : Origin (d3_to_IO) */ + d3_to_IO [0] = 0.0; + d3_to_IO [1] = 0.0; + d3_to_IO [2] = 0.0; + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "origin", FLEXIO_REAL,3,d3_to_IO)); + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "min_ext",FLEXIO_REAL,3,d3_to_IO)); + + d3_to_IO [0] = GH->delta_space [0] * ioUtilGH->downsample_x; + d3_to_IO [1] = GH->delta_space [1] * ioUtilGH->downsample_y; + d3_to_IO [2] = GH->delta_space [2] * ioUtilGH->downsample_z; + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "delta", FLEXIO_REAL, 3,d3_to_IO)); + + if (ioUtilGH->downsample_x > 1 || + ioUtilGH->downsample_y > 1 || + ioUtilGH->downsample_z > 1) { + d3_to_IO [0] = GH->delta_space [0]; + d3_to_IO [1] = GH->delta_space [1]; + d3_to_IO [2] = GH->delta_space [2]; + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "evolution_delta", FLEXIO_REAL, + 3, d3_to_IO)); + } + + /* FIXME : Origin (d3_to_IO) */ + d3_to_IO [0] = 0.0 + gsz [0] * GH->delta_space [0] * ioUtilGH->downsample_x; + d3_to_IO [1] = 0.0 + gsz [1] * GH->delta_space [1] * ioUtilGH->downsample_y; + d3_to_IO [2] = 0.0 + gsz [2] * GH->delta_space [2] * ioUtilGH->downsample_z; + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "max_ext",FLEXIO_REAL,3,d3_to_IO)); + + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "global_size", FLEXIO_INT4, + 3, gsz)); + + i_to_IO = CCTK_GetnProcs (GH); + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "nprocs", FLEXIO_INT4, + 1, &i_to_IO)); + + i_to_IO = ioUtilGH->ioproc_every; + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "ioproc_every", FLEXIO_INT4, + 1, &i_to_IO)); + + i_to_IO = ioUtilGH->unchunked; + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "unchunked", FLEXIO_INT4, + 1, &i_to_IO)); + + i_to_IO = GH->iteration; + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "iteration", FLEXIO_INT4, + 1, &i_to_IO)); +} + +/*@@ + @routine IOFlexIO_AddChunkAttributes + @author Paul Walker + @date Feb 1997 + @desc + This routine adds chunk attributes to a data set. That is, + is adds attributes to the chunks of data passed to the + io processors. + @enddesc + @history + @hauthor Gabrielle Allen + @hdate Jul 10 1998 + @hdesc Added the name of the grid function + @hdate Wed Sep 2 10:08:31 1998 @hauthor Tom Goodale + @hdesc Abstracted WRITE_ATTRIBUTE to merge in Szu-Wen's Panda calls. + @endhistory + @@*/ + +void IOFlexIO_AddChunkAttributes (cGH *GH, int index, CCTK_INT4 chunk_origin [3], + IOFile iof) +{ + char *name; + CCTK_INT4 i_to_IO; + + /* there is nothing to do for a serial run */ + if (CCTK_GetnProcs (GH) == 1) + return; + + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "chunk_origin", FLEXIO_INT4, + 3, chunk_origin)); + + i_to_IO = GH->iteration; + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "chunk_dataset", FLEXIO_INT4, + 1, &i_to_IO)); + + name = CCTK_GetFullName (index); + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "name", BYTE, + strlen (name)+1, name)); +} + + +/*@@ + @routine IOFlexIO_getDumpData + @author Paul Walker + @date Feb 1997 + @desc + Bounds and data to be output, takes into account downsampling + @enddesc + @history + @hauthor Gabrielle Allen @hdate Oct 5 1998 + @hdesc Made into subroutine + @endhistory + @var GH + @vdesc Identifies grid hierachy + @vtype pGH * + @vio in + @vcomment + @endvar + @var GF + @vdesc Identifies grid function + @vtype pGF * + @vio in + @vcomment + @endvar + @var outme + @vdesc data segment to output + @vtype void ** + @vio out + @vcomment This is just GF->data if there is no downsampling + @endvar + @var free_outme + @vdesc Specifies whether or not to free the storage associated with outme + @vtype int * + @vio out + @vcomment 1: free storage; 0: don't free storage + @endvar + @var bnd + @vdesc bounds, local size and global shape of the output + @vtype CCTK_INT4 [9] + @vio out + @vcomment bnd [0..2] lower bounds and bnd[3],bnd[4],bnd[5] + bnd [3..5] local size of data to send + bnd [6..8] global shape + @endvar + @var element_size + @vdesc the size of an element of variable + @vtype int + @vio in + @endvar + @@*/ + +void IOFlexIO_getDumpData (cGH *GH, int index, int timelevel, void **outme, + int *free_outme, CCTK_INT4 bnd [9], int element_size) +{ + DECLARE_PARAMETERS + int i; + int myproc; + ioGH *ioUtilGH; + pGH *pughGH; + CCTK_REAL4 *single_ptr; + CCTK_REAL *real_ptr; + CCTK_CHAR *char_ptr; + CCTK_INT *int_ptr; + void *data = CCTK_GetVarDataPtr_ByIndex (GH, timelevel, index); + + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + pughGH = (pGH *) GH->extensions [CCTK_GetGHExtensionHandle ("PUGH")]; + + myproc = CCTK_GetMyProc (GH); + + if (ioUtilGH->downsample_x == 1 && + ioUtilGH->downsample_y == 1 && + ioUtilGH->downsample_z == 1) { + + if (ioUtilGH->out_single) { + single_ptr = (CCTK_REAL4 *) malloc (pughGH->npoints*sizeof (CCTK_REAL4)); + + for (i = 0; i < pughGH->npoints; i++) + single_ptr [i] = (CCTK_REAL4) ((CCTK_REAL *) data) [i]; + + *outme = single_ptr; + *free_outme = 1; + } else { + *outme = data; + *free_outme = 0; + } + + for (i = 0; i < 3; i++) { + bnd [i] = GH->lower_bound[i]; /* the bounds */ + bnd [i+3] = GH->local_shape[i]; /* the sizes */ + bnd [i+6] = GH->global_shape[i]; /* the global space */ + } + + } else { + + int start [3], end [3]; + int i, j, k, l; + + /* Downsampling code ... */ + bnd [6] = GH->global_shape[0] / ioUtilGH->downsample_x; + if (GH->global_shape[0] % ioUtilGH->downsample_x) + bnd [6]++; + bnd [7] = GH->global_shape[1] / ioUtilGH->downsample_y; + if (GH->global_shape[1] % ioUtilGH->downsample_y) + bnd [7]++; + bnd [8] = GH->global_shape[2] / ioUtilGH->downsample_z; + if (GH->global_shape[2] % ioUtilGH->downsample_z) + bnd [8]++; + + if (IO_verbose) + printf ("Downsampled sizes (%d, %d, %d) -> (%d, %d, %d)\n", + GH->global_shape[0], GH->global_shape[1], GH->global_shape[2], + (int) bnd [6], (int) bnd [7], (int) bnd [8]); + + /* Now figure out the local downsampling */ + /* The local starts are the lb modded into the downsample */ + for (i = 0; i < 3; i++) { + int downsample; + + if (i == 0) + downsample = ioUtilGH->downsample_x; + else if (i == 1) + downsample = ioUtilGH->downsample_y; + else + downsample = ioUtilGH->downsample_z; + + bnd [i] = GH->lower_bound[i] / downsample; + start [i] = bnd [i] * downsample; + if (start [i] < + GH->lower_bound[i] + pughGH->ownership [PUGH_VERTEXCTR][i][0]) { + start [i] += downsample; + bnd [i] ++; + } + end [i] = ((GH->lower_bound [i] + + pughGH->ownership [PUGH_VERTEXCTR][i][1] - 1) / downsample) + * downsample; + bnd [i+3] = (end [i] - start [i]) / downsample + 1; + } + + if (IO_verbose) { + printf ("Downsample ranges (%d, %d, %d) -> (%d, %d, %d)\n", + start [0], start [1], start [2], + end [0], end [1], end [2]); + printf ("Local size/bound (%d, %d, %d) (%d, %d, %d)\n", + (int) bnd [3], (int) bnd [4], (int) bnd [5], + (int) bnd [0], (int) bnd [1], (int) bnd [2]); + } + + /* compute local ranges */ + for (i = 0; i < 3; i++) { + start [i] -= GH->lower_bound [i]; + end [i] -= GH->lower_bound [i]; + } + + *outme = malloc (bnd [3] * bnd [4] * bnd [5] * element_size); + *free_outme = 1; + + /* I hate it to repeat the loops for each case label + but that way produces much more efficient code */ + l = 0; + switch (CCTK_GetVarVType (index)) { + case CCTK_VARIABLE_CHAR: + char_ptr = (CCTK_CHAR *) *outme; + for (k = start [2]; k <= end [2]; k += ioUtilGH->downsample_z) + for (j = start [1]; j <= end [1]; j += ioUtilGH->downsample_y) + for (i = start [0]; i <= end [0]; i += ioUtilGH->downsample_x) + char_ptr [l++] = ((CCTK_CHAR *) data) [DI (pughGH, i, j, k)]; + break; + + case CCTK_VARIABLE_INTEGER: + int_ptr = (CCTK_INT *) *outme; + for (k = start [2]; k <= end [2]; k += ioUtilGH->downsample_z) + for (j = start [1]; j <= end [1]; j += ioUtilGH->downsample_y) + for (i = start [0]; i <= end [0]; i += ioUtilGH->downsample_x) + int_ptr [l++] = ((CCTK_INT *) data) [DI (pughGH, i, j, k)]; + break; + + case CCTK_VARIABLE_REAL: + if (ioUtilGH->out_single) + single_ptr = (CCTK_REAL4 *) *outme; + else + real_ptr = (CCTK_REAL *) *outme; + for (k = start [2]; k <= end [2]; k += ioUtilGH->downsample_z) + for (j = start [1]; j <= end [1]; j += ioUtilGH->downsample_y) + for (i = start [0]; i <= end [0]; i += ioUtilGH->downsample_x) + if (ioUtilGH->out_single) + single_ptr [l++] = (CCTK_REAL4) + (((CCTK_REAL *) data) [DI (pughGH, i, j, k)]); + else + real_ptr [l++] = ((CCTK_REAL *) data) [DI (pughGH, i, j, k)]; + break; + + default: + CCTK_WARN (1, "Unsupported variable type in IOFlexIO_getDumpData"); + return; + } + } + + if (IO_verbose) { + printf ("Global size: %d %d %d\n", + (int) bnd [6], (int) bnd [7], (int) bnd [8]); + printf ("Lower bound: %d %d %d\n", + (int) bnd [0], (int) bnd [1], (int) bnd [2]); + printf ("Chunk size : %d %d %d\n", + (int) bnd [3], (int) bnd [4], (int) bnd [5]); + } +} + + +/*@@ + @routine IOFlexIO_eachProcDump + @author Paul Walker + @date Feb 1997 + @desc + Dump data from each processor + @enddesc + @history + @hauthor Gabrielle Allen @hdate Oct 5 1998 + @hdesc Made into subroutine + @endhistory + @@*/ + +void IOFlexIO_eachProcDump (cGH *GH, int index, int timelevel, void *outme, + CCTK_INT4 bnd [9], IOFile iof, int ioflex_type) +{ + int chunk_dims [3]; + ioGH *ioUtilGH; + + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + + /* So set up the local shape. */ + chunk_dims [0] = bnd [3]; + chunk_dims [1] = bnd [4]; + chunk_dims [2] = bnd [5]; + + /* Dump the data */ + CACTUS_IEEEIO_ERROR (IOwrite (iof, ioflex_type, 3, chunk_dims, outme)); + + /* Add attributes for global space */ + IOFlexIO_AddCommonAttributes (GH, index, timelevel, &bnd [6], iof); + + /* Add chunk attributes ... lower bound of this chunk and + * number of processors participating. + */ + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "subchunk_lb", FLEXIO_INT4, + 3, &bnd [0])); + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "global_size", FLEXIO_INT4, + 3, &bnd [6])); + + IOFlexIO_AddChunkAttributes (GH, index, bnd, iof); +} + + +/*@@ + @routine IOFlexIO_procDump + @author Paul Walker + @date Feb 1997 + @desc + Dump data + @enddesc + @history + @hauthor Gabrielle Allen @hdate Oct 5 1998 + @hdesc Made into subroutine + @endhistory + @@*/ + +void IOFlexIO_procDump (IOFile iof, cGH *GH, int index, void *outme, + CCTK_INT4 bnd [9], int ioflex_type) +{ + int chunk_dims [3], chunk_origin [3]; /* Chunk dim and origin */ + ioGH *ioUtilGH; + + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + + chunk_origin [0] = bnd [0]; + chunk_origin [1] = bnd [1]; + chunk_origin [2] = bnd [2]; + chunk_dims [0] = bnd [3]; + chunk_dims [1] = bnd [4]; + chunk_dims [2] = bnd [5]; + + if (ioUtilGH->unchunked) { + + /* Unchunked data */ + CACTUS_IEEEIO_ERROR (IOwriteChunk (iof, chunk_dims, chunk_origin, outme)); + + } else { + + /* Chunked data */ + CACTUS_IEEEIO_ERROR (IOwrite (iof, ioflex_type, 3, chunk_dims, outme)); + + /* Write chunk attributes */ + IOFlexIO_AddChunkAttributes (GH, index, bnd, iof); + + /* Add chunk attributes ... lower bound of this chunk and + * number of processors participating. + */ + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "subchunk_lb", FLEXIO_INT4, + 3, &bnd [0])); + CACTUS_IEEEIO_ERROR (IOwriteAttribute (iof, "global_size", FLEXIO_INT4, + 3, &bnd [6])); + } +} diff --git a/src/GHExtension.c b/src/GHExtension.c new file mode 100644 index 0000000..824a7a2 --- /dev/null +++ b/src/GHExtension.c @@ -0,0 +1,91 @@ + /*@@ + @file GHExtension.c + @date Fri May 21 1999 + @author Thomas Radke + @desc + IOFlexIO GH extension stuff. + @enddesc + @history + @hauthor Thomas Radke @hdate May 21 1999 + @hdesc Just copied from thorn IO. + @endhistory + @@*/ + +/*#define DEBUG_IO*/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "flesh.h" +#include "Groups.h" +#include "Comm.h" +#include "Misc.h" +#include "GHExtensions.h" +#include "declare_parameters.h" +#ifdef CACTUSBASE_PUGH +#include "CactusBase/pugh/src/include/pugh.h" +#endif +#include "CactusBase/IOUtil/src/ioGH.h" +#include "ioFlexGH.h" + + +void *IOFlexIO_SetupGH (tFleshConfig *config, int convergence_level, cGH *GH) +{ + int i, numvars; + flexioGH *newGH; + + numvars = CCTK_GetNumVars (); + + newGH = (flexioGH *) malloc (sizeof (flexioGH)); + newGH->IO_2Dnum = (int *) malloc (numvars * sizeof (int)); + newGH->IO_3Dnum = (int *) malloc (numvars * sizeof (int)); + newGH->IO_2Dlast = (int *) malloc (numvars * sizeof (int)); + newGH->IO_3Dlast = (int *) malloc (numvars * sizeof (int)); + + newGH->IEEEfile_2D = (IOFile **) malloc (numvars * sizeof (IOFile *)); + for (i = 0; i < numvars; i++) + newGH->IEEEfile_2D [i] = (IOFile *) malloc (3 * sizeof (IOFile)); + + newGH->IEEEfname_3D = (char **) malloc (numvars * sizeof (char **)); + + for (i = 0; i < numvars; i++) + newGH->IEEEfname_3D [i] = (char *) malloc (512 * sizeof (char)); + newGH->IEEEfile_3D = (IOFile *) malloc (numvars * sizeof (IOFile)); + + return (newGH); +} + +int IOFlexIO_InitGH (cGH *GH) +{ + DECLARE_PARAMETERS + int i; + ioGH *ioUtilGH; + flexioGH *myGH; + + /* get the handles for IOUtil and IOFlexIO extensions */ + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + myGH = (flexioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IOFlexIO")]; + + InitIONum (myGH->IO_2Dnum, output2D); + InitIONum (myGH->IO_3Dnum, output3D); + + for (i=0; i<CCTK_GetNumVars(); i++) + myGH->IO_2Dlast [i] = myGH->IO_3Dlast [i] = -1; + + myGH->reuse_fh = reuse_fh; + + /* Only have reuse for chunked data */ + if (myGH->reuse_fh && ! ioUtilGH->unchunked) { + CCTK_Warn (2, "Cannot reuse handles with unchunked data. " + "Ignoring parameter 'reuse_fh'"); + myGH->reuse_fh = 0; + } + + return (0); +} + +int IOFlexIO_rfrTraverseGH (cGH *GH, int rfrpoint) +{ + return 0; +} diff --git a/src/IEEEIO/Arch.h b/src/IEEEIO/Arch.h new file mode 100644 index 0000000..e96147c --- /dev/null +++ b/src/IEEEIO/Arch.h @@ -0,0 +1,72 @@ +#ifndef _MACHDEFS_H_ +#define _MACHDEFS_H_ + +#ifdef ANSI +#define PROTO(x) x +#else +#define PROTO(x) () +#endif + +#if defined(SGI) || defined(CM5) || defined (DEC) +#define F77NAME(a,b,c) a +#else +#ifdef HP +#define F77NAME(a,b,c) b +#else +#ifdef CRAY +#define F77NAME(a,b,c) c +#endif +#endif +#endif + +#ifndef F77NAME +#define F77NAME(a,b,c) a +#endif + + +#ifndef SGI +typedef long Long8; +#else /* default SGI behavior. long longs=8 bytes long=4 bytes*/ +typedef long long Long8; +#endif + +#ifdef _CONFIG_H_ +/* config.h gives us INTEGER4 */ +typedef CCTK_INT4 Long; /* for now Long *MUST* be 32bit integer for PC compatability */ +#else +#ifdef T3E +typedef short Int; /* T3E uses 8-byte integers as default */ +typedef short Long; /* this is wierd, but its a T3E... */ +#else +typedef int Int; /* every other sane design uses 4-byte ints */ +typedef int Long; /* for now Long *MUST* be 32bit integer for PC compatability */ +#endif +/* if we run into problems later we'll just need have a separate rev of the file format for PC's */ +#endif + +#ifdef WIN32 /* this aint a happenin thang yet... */ +/* #include <sys/types.h> ... bahh!!! Win32 doesn't have this! */ +typedef unsigned int IOFile; /* cast to integer for pointers :( */ +union Integer8 { + long l; + int i[2]; + char c[8]; +}; /* what can be said about the byte order though? */ +#else /* its a Mac or Unix box probably */ +#include <sys/types.h> +typedef caddr_t IOFile; /* use caddr_t to store pointers */ +#endif + +#ifdef HP +#define NO_RECURSIVE_INLINE +#endif + +#ifndef CONST_BROKEN +#define CONST const +#else +#ifdef CONST +#undef CONST +#endif +#endif + +#endif diff --git a/src/IEEEIO/FlexArrayTmpl.H b/src/IEEEIO/FlexArrayTmpl.H new file mode 100644 index 0000000..b965e19 --- /dev/null +++ b/src/IEEEIO/FlexArrayTmpl.H @@ -0,0 +1,180 @@ +// FlexArrayTmpl.hh +#ifndef __FLEXARRAY_TMPL_HH_ +#define __FLEXARRAY_TMPL_HH_ + +#ifdef SGI +#include <new.h> +#endif + +#include "Arch.h" +//#define DOUBLEWORD 8 // sgi doubleword == -align64 at a minimum +#define DOUBLEWORD 1 +/*------------- + Template: FlexArray + Purpose: Simplifies dynamically allocated array management since it contains + array size information and manages array resizing. However, since speed-critical + operations like indexing (the [] operator) are inlined, it adds no speed + penalty for the most common speed-critical operations. + Method Summary: + constructor: The initial "size" is 0 (the size of the array that is accessible + using the indexing operator []. However, the internally allocated size is + a minimum of 8 elements. + [] : Indexing operator. Acts just like [] would for a generic array. Since + it is inlined, there is actually no penalty in performance compared to + using a generic array. + append : Add an element to the end of the array, resizing the array if necessary. + The arrays are allocated in blocks so it doesn't need to realloc on every append. + For arrays < 1024 elements long, it doubles the array size on each realloc. When + larger than 1024 elements, it adds 1024 elements to the array on every realloc. + setSize(): Sets the internal and externally availible array sizes to the + specified size; shrinking a larger internal array if necessary. + getSize(): Return the current externally availible array size. The internal + size may be larger. +---------------*/ +template<class T> +class FlexArray { + int size,maxsize; + T *data; + // Internal reallocation procedure. + void grow(int targetSize=1); +public: + // constructor + FlexArray(int sz=0); + // copy constructor + FlexArray(FlexArray &src); + FlexArray &operator=(FlexArray<T> &src); + // destructor + ~FlexArray(); + inline int getSize(){return size;} + int setSize(int s); + int append(T value); + int append(T *values,int nvals); +#ifdef FLEXARRAYDEBUG + T &operator[](int index){ + if(index>=size || index<0) { // range check + printf("FlexArray:: Bad index. Arraysize=%u Index=%u\n", + size,index); + index=0; + } + return (data[index]); + } +#else + inline T &operator[](int index){ return (data[index]); } +#endif + // purge? which forces array delete + inline void purge(){ delete[] data; data=0; maxsize=size=0; } + //inline operator T*() { return data; } // typecasting operator + inline T *getData() { return data; } // sometimes you need direct access to the contained array. +}; + +template<class T> +FlexArray<T>::FlexArray(int sz):size(sz){ + /* + if(sz>DOUBLEWORD) maxsize=sz; + else maxsize=DOUBLEWORD; always allocate in doubleword chunks + data = new T[maxsize]; + */ + maxsize=sz; // problem here is that virtually guarauntees reallocation + if(maxsize>0) data = new T[maxsize]; + else data = 0; +} + +template<class T> +FlexArray<T>::~FlexArray(){ + if(maxsize>0 && data) + delete[] data; + size=maxsize=0; + data=0; +} + + +template<class T> +void FlexArray<T>::grow(int targetSize){ + do{ + if(!maxsize) maxsize=1; + //if(maxsize<1024) + maxsize<<=1; // double in size + //else + // maxsize+=1024; // increase by 1k element block + }while(maxsize<targetSize); + //printf("FlexArray::grow(%u)->%u\n",targetSize,maxsize); + T *newdata=new T[maxsize]; + // has to do its own copy because realloc is not always compatible with + // new/delete on all machines + if(data){ + for(int i=0;i<size;i++) + newdata[i]=data[i]; + delete[] data; + } + data=newdata; +} + +template<class T> +FlexArray<T>::FlexArray(FlexArray<T> &src):data(0),size(0),maxsize(0){ + setSize(src.getSize()); + //puts("\tFlexArray<T>copy constructor***********"); + for(int i=0;i<src.getSize();i++) + data[i]=src.data[i]; + //this->data[i]=src->data[i]; + + // (*this)[i]=src[i]; // a full stupid copy!! +} + +template<class T> +FlexArray<T> &FlexArray<T>::operator=(FlexArray<T> &src){ + if(this == &src) return *this; + setSize(src.getSize()); + //puts("\t\tFlexArray<T>copy operator"); + for(int i=0;i<src.getSize();i++) + data[i]=src[i]; // a full stupid copy!! + return *this; +} + +template<class T> +int FlexArray<T>::setSize(int s){ + if(s<size){ + size=s; + return size; // can't allow shrinkage below current size + } + if(s==maxsize){ + size=maxsize; + return size; + } + // otherwise if maxsize>s>size> + maxsize=s; + T *newdata=new T[maxsize]; + if(data){ + for(int i=0;i<size && i<maxsize;i++) + newdata[i]=data[i]; + delete[] data; + } + data=newdata; + size=maxsize; + return size; +} + +template<class T> +int FlexArray<T>::append(T value){ + if(size>=maxsize) // failsafe check + grow(size+1); // this array only increases in size + data[size++]=value; + return size; +} + +template<class T> +int FlexArray<T>::append(T *values,int nvals){ + if(size+nvals >= maxsize) + grow(size+nvals);//this array only increases in size + for(int i=0;i<nvals;i++) + data[size++]=values[i]; + return size; +} + +/* + 1) Refcount must be fixed + 2) Need to have more flexibility in how the block allocation + is defined (eg. memory allocation blocksize) Use obstacks? + This would require a more global memory pool +*/ + +#endif // __FLEXARRAY_TMPL_HH_ diff --git a/src/IEEEIO/IEEEIO.cc b/src/IEEEIO/IEEEIO.cc new file mode 100644 index 0000000..5bcbbbc --- /dev/null +++ b/src/IEEEIO/IEEEIO.cc @@ -0,0 +1,1363 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "IEEEIO.hh" + +#ifdef WIN32 +// Are we Microsoft VC++ 5.0 or 6.0? +#if defined(_MSC_VER) && ( (_MSC_VER == 1100) || (_MSC_VER == 1200) ) // yes we are +#include <fcntl.h> +#define O_RDONLY _O_RDONLY|_O_BINARY +#define O_RDWR _O_RDWR|_O_BINARY +#define O_WRONLY _O_WRONLY|_O_BINARY +#define O_CREAT _O_CREAT|_O_BINARY +#define O_TRUNC _O_TRUNC|_O_BINARY +#define O_APPEND _O_APPEND|_O_BINARY +#else // not an MSC compiler (use the old defines) +#define O_RDONLY _O_RDONLY +#define O_RDWR _O_RDWR +#define O_WRONLY _O_WRONLY +#define O_CREAT _O_CREAT +#define O_TRUNC _O_TRUNC +#define O_APPEND _O_APPEND +#endif // not an MSC compiler +#endif // WIN32 + +#ifdef FFIO +#include <ffio.h> +#define open(x,y,z) ffopens(x,y,z, 0, 0, NULL, "bufa.bufsize=256.num_buffers=4") +#define close(x) ffclose(x) +#endif + +#ifdef T3E +int IEEEIO::ffioIsTrue(){ +#ifdef FFIO + return 1; +#else + return 0; +#endif +} +#endif + + +// int IEEEIO::nextRecord() (stores state in current_record) +// also garauntees integrity of datasetnumber and +// the current_rec and current_dat + +IEEEIO::AttribRef::AttribRef(IEEEIO::AttribRef &src){ + //puts("AttribRef::copy constructor"); + rec=src.rec; + offset=src.offset; + strcpy(name,src.name); +} + +IEEEIO::AttribRef::AttribRef(){ + //puts("empty attribref constructor"); +} +/* +IEEEIO::AttribRef::~AttribRef(){ + //puts("delete an attrib ref"); +} +IEEEIO::RecRef::~RecRef(){ + //puts("delete a recref"); +} +*/ + +IEEEIO::AttribRef &IEEEIO::AttribRef::operator=(IEEEIO::AttribRef &src){ + if(this != &src) { + //puts("\tAttribRef::copy operator"); + rec=src.rec; + offset=src.offset; + //name=src.name; // uses the flexarray copy constructor + strcpy(name,src.name); + } + return *this; +} + +IEEEIO::DataRef::DataRef():end_offset(0){ + //puts("Empty DataRef Constructor!!"); +} + + +IEEEIO::DataRef::DataRef(IEEEIO::DataRef &src){ + //puts("DataRef:: copy constructor"); + rec=src.rec; + offset=src.offset; + end_offset=src.end_offset; + annotations = src.annotations; + attributes = src.attributes; + //puts("DataRef:: copy constructor DONE"); +} +/* +IEEEIO::DataRef::~DataRef(){ + // puts("dataref destructor"); +}*/ + +IEEEIO::DataRef &IEEEIO::DataRef::operator=(IEEEIO::DataRef &src){ + if(this != &src){ + rec=src.rec; + offset=src.offset; + end_offset=src.end_offset; + annotations = src.annotations; + attributes = src.attributes; + } + return *this; +} + +void IEEEIO::initPosition(){ + // init +#ifdef FFIO + actual_position = ::ffseek(fid,0,L_INCR); +#else + actual_position = ::lseek(fid,0,L_INCR); +#endif + virtual_position = actual_position + writebuffercount; + // get actual filesize + +#ifdef FFIO + file_length = ::ffseek(fid,0,L_XTND); + ::ffseek(fid,actual_position,L_SET); // seek back to original pos +#else + file_length = ::lseek(fid,0,L_XTND); + ::lseek(fid,actual_position,L_SET); // seek back to original pos +#endif + if(virtual_position > file_length) + file_length = virtual_position; + // printf("Init position: a=%ld v=%ld f=%ld\n", + // actual_position,virtual_position,file_length); +} + + +//long IEEEIO::getPosition(int immediate) { +//#ifdef FFIO +// if(!writebuffer || immediate) +// return ::ffseek(fid,0,L_INCR); +// else +// return ::ffseek(fid,0,L_INCR)+writebuffercount; +//#else +// if(!writebuffer || immediate) +// return ::lseek(fid,0,L_INCR); +// else +// return ::lseek(fid,0,L_INCR)+writebuffercount; +//#endif +//} + +void IEEEIO::restart(){ + // this->initPosition(); + //puts("restart seek"); + IEEEIO::lseek(FileHdrSize,L_SET); // T3E Kludge + //puts("restart"); + current_rec_offset=current_dat_offset=FileHdrSize; // T3E Kludge + current_rec.recordsize=0; + current_dat.datasize=0; + current_dat.rank=0; + datasetnumber=0; // initial + hasread=0; // and it hasn't been read yet... +} + +int IEEEIO::nextRecord(){ + RecordHdr rec; + // should have it check the datasetnumber before seeking + // this will require consistancy checks for the datasetnumber + // elsewhere in the code + long src=current_rec_offset; + long dst=src+current_rec.recordsize; + current_rec_offset=IEEEIO::lseek(dst,L_SET); // seek from start + // or use rec.read(rec,fid) + // or IEEEIO::RecordHdr::read(rec,fid) + if(RecordHdr::read(this,rec) <= 0) + return 0; + current_rec_offset=dst+RecordHdrSize; + current_rec=rec; + return 1; +} + +int IEEEIO::nextDataRecord(){ + hasread=0; // reset the hasread field + if(current_rec.recordtype==DataRecord && + datasetnumber!=current_rec.sequenceID){ + IEEEIO::lseek(current_rec_offset,L_SET); // seek to record start + } + else { + do { // scan through records for the next DataRecord + if(!nextRecord()) + return 0; // reached end of file + } while(current_rec.recordtype!=DataRecord); + } + // dat_offset is same as current_rec_offset for the data record + // so nextRecord() can be used for scanning for annotations + current_dat_offset=current_rec_offset; + DataRecordHdr::read(this,current_dat); + datasetnumber=current_rec.sequenceID; + return 1; +} + +//long IEEEIO::getLength(int immediate){ +//#ifdef FFIO +// long currentpos=::ffseek(fid,0,L_INCR); // find currentposition +// long auspos = ::ffseek(fid,0,L_XTND); // seek to end to find length +//#else +// long currentpos=::lseek(fid,0,L_INCR); // find currentposition +// long auspos = ::lseek(fid,0,L_XTND); // seek to end to find length +//#endif +// if(writebuffer && writebuffercount && !immediate){ +// // must compute real end of file +// register long pos1=currentpos+writebuffercount; +// if(pos1>auspos) auspos=pos1; +// } +//#ifdef FFIO +// ::ffseek(fid,currentpos,L_SET); // seek back to original position +//#else +// ::lseek(fid,currentpos,L_SET); // seek back to original position +//#endif +// return auspos; // return end position +//} + +void IEEEIO::byteswapBuffer(void *buf,long nelements,int elementsize){ + char *buffer=(char *)buf; // treat as a character buffer + if(elementsize<=1) return; + for(long i=0;i<nelements;i++,buffer+=elementsize){ + register int s,d; + register char c; + // do the swap thang on each element + for(s=0,d=elementsize-1;s<d;s++,d--){ + c=buffer[s]; + buffer[s]=buffer[d]; + buffer[d]=c; + } + } +} + +void IEEEIO::byteswapBuffer(void *source,void *dest,long nelements,int elementsize){ + if(elementsize<=1) return; + // Lets optimize for integers + switch(elementsize){ + case 4: + { + Int *src=(Int*)source; + Int *dst=(Int*)dest; + for(long i=0;i<nelements;i++){ + union IntChar { + int i; + char a[4]; + }; + register IntChar ts,td; + // do the swap thang on each element + td.i=ts.i=src[i]; + td.a[0]=ts.a[3]; + td.a[1]=ts.a[2]; + td.a[2]=ts.a[1]; + td.a[3]=ts.a[0]; + dst[i]=td.i; + } + } + break; +#ifndef WIN32 // Win32 can't have 8byte integers + case 8: + { + Long *src=(Long*)source; + Long *dst=(Long*)dest; + for(long i=0;i<nelements;i++){ + union LongChar { + Long l; + char a[8]; + }; + register LongChar ts,td; + // do the swap thang on each element + td.l=ts.l=src[i]; + td.a[0]=ts.a[7]; + td.a[1]=ts.a[6]; + td.a[2]=ts.a[5]; + td.a[3]=ts.a[4]; + td.a[4]=ts.a[3]; + td.a[5]=ts.a[2]; + td.a[6]=ts.a[1]; + td.a[7]=ts.a[0]; + dst[i]=td.l; + } + } + break; +#endif + default: + { + char *src=(char *)source; // treat as a character buffer + char *dst=(char *)dest; + for(long i=0;i<nelements;i++,src+=elementsize,dst+=elementsize){ + register int s,d; + // do the swap thang on each element + for(s=0,d=elementsize-1;s<d;s++,d--){ + dst[d]=src[s]; + dst[s]=src[d]; + } + } + } + break; + } +} + +int IEEEIO::writeFileHeader(){ + // long pos=getPosition(); // use virtual_position + // this->flush(); + //puts("WriteHeader"); + for(char c=0;c<4;c++) // make sure magic is correct + file_header.magic.c[c]=c+1; + if(swapbytes) + file_header.byteorder.i=IEEE_REVERSE_MAGIC; + else + file_header.byteorder.i=IEEE_NATIVE_MAGIC; + file_header.majorversion = IEEE_MAJORVERSION; + file_header.minorversion = IEEE_MINORVERSION; + IEEEIO::lseek(0,L_SET); // will force a flush (if needed) + //puts("done writeheader seek"); + if(swapbytes) file_header.byteswap(); + // T3E Kludge + int sz=FileHdr::write(this,file_header); + if(swapbytes) file_header.byteswap(); + // if(pos>=FileHdrSize) // T3E Kludge + // IEEEIO::lseek(pos,L_SET); + return sz; +} + +// reads the start of the file (including the magic number) +// and determines if this is +// 1) A valid IEEEIO file +// 2) what the byte order is +// 3) How many datasets are contained +int IEEEIO::readFileHeader(){ + long pos=getPosition(); // use virtual_position + IEEEIO::lseek(0,L_SET); + // T3E Kludge + int sz=FileHdr::read(this,file_header); + IEEEIO::lseek(pos,L_SET); // return to original position + for(char c=0;c<4;c++){ + if((c+1)!=file_header.magic.c[c]){ + file_header.ndatasets=0; + fprintf(stderr,"IEEEIO: File %s has the wrong magic number\n",filename); + return -1; // bad magic + } + } + // now compare byte order + if(file_header.byteorder.i==IEEE_NATIVE_MAGIC) + swapbytes=0; + else{ + swapbytes=1; + //puts("byteswapping is On !!!!!!!!!!!!!!!"); + } + if(swapbytes) file_header.byteswap(); + ndatasets=file_header.ndatasets; + return 1; +} + +void IEEEIO::rebuildFileHeader(){ + int lndatasets; + long pos=getPosition(); // save file position (use virtual position + restart(); + // steal seek loop from the nextDataRecord() + for(lndatasets=0; + nextDataRecord()>0; + lndatasets++){ + // now check the length of the record + // for each record. + } + file_header.ndatasets=lndatasets; + writeFileHeader(); + IEEEIO::lseek(pos,L_SET); +} + +void IEEEIO::appendRecordTable(){ + //printf("appending records==================\n"); + while(nextRecord()>0){ + //printf("appending record\n"); + switch(current_rec.recordtype){ + case DataRecord: + //fprintf(stderr,"DataRecord"); + (rec_table[current_rec.sequenceID]).rec=current_rec; + // use virtual_position + (rec_table[current_rec.sequenceID]).offset=getPosition()-RecordHdrSize; + break; + case AnnotationRecord:{ + //fprintf(stderr,"\tAnnotationRecord\n"); + RecRef ref; + ref.rec=current_rec; + ref.offset=getPosition()- RecordHdrSize; // T3E Kludge + (rec_table[current_rec.sequenceID]).annotations.append(ref); + } + break; + case AttributeRecord:{ + //fprintf(stderr,"\tAttributeRecord\n"); + AttribRef ref,*refp; + AttributeRecordHdr attribhdr; + ref.rec=current_rec; + ref.offset=getPosition()- RecordHdrSize; // T3E Kludge + (rec_table[current_rec.sequenceID]).attributes.append(ref); + int idx=(rec_table[current_rec.sequenceID]).attributes.getSize()-1; + refp = &((rec_table[current_rec.sequenceID]).attributes[idx]); + AttributeRecordHdr::read(this,attribhdr); + IEEEIO::read(refp->name,attribhdr.namesize); + } + break; + default: + fprintf(stderr,"\tIEEEIO::Error UNKNOWN RECORD TYPE [%d]... recovering.", + (int)(current_rec.recordtype)); + return; + } + (rec_table[current_rec.sequenceID]).end_offset = + current_rec_offset + current_rec.recordsize; + } +} + +void IEEEIO::buildRecordTable(){ + if(file_header.ndatasets<0) + return; // failure + restart(); + rec_table.setSize(file_header.ndatasets); + appendRecordTable(); +} + +void IEEEIO::openFile(CONST char *fname,IObase::AccessMode access,int swbytes){ + switch(access){ + case IObase::Read: + fid=open(fname,O_RDONLY,0644); + if(fid<0){ + fprintf(stderr,"IEEEIO: Failed to open %s for reading\n",fname); + return; + } + initPosition(); + if(readFileHeader()<=0){ + close(fid); + fprintf(stderr,"IEEEIO: File %s is empty (opened for reading)\n",fname); + fid=-1; // invalid file + return; + } + if(file_header.ndatasets<0){ + // must recover from crash + close(fid); + fid=open(fname,O_RDWR,0644); + rebuildFileHeader(); + close(fid); + fid=open(fname,O_RDONLY,0644); + readFileHeader(); // reread.. + } + buildRecordTable(); + restart(); // go to start of file + ndatasets = file_header.ndatasets; + nextRecord(); // prime the recordnumber + break; + case IObase::Write: // truncates + fid=open(fname,O_WRONLY|O_CREAT|O_TRUNC,0644); + if(fid<0){ + fprintf(stderr,"IEEEIO: Failed to create %s for writing\n",fname); + return; + } + initPosition(); + file_header.ndatasets=-1; + ndatasets=0; + writeFileHeader(); + restart(); + break; + case IObase::Append: + default: + fid=open(fname,O_RDWR,0644); // PW: We *don't* want O_APPEND here. + // O_Append moves the file ptr to end + // of file before a write; this means + // when we do the writeHeader() below + // this is stuck at the end of this file + // which makes it puke. + if(fid<0){ + fprintf(stderr,"IEEEIO: Failed to open %s for append\n",fname); + return; + } + initPosition(); + if(readFileHeader()<=0){ + close(fid); + fprintf(stderr,"IEEEIO: File %s is empty (opened for append)\n",fname); + fid=-1; // invalid file + return; + } + if(file_header.ndatasets<0){ + // must recover from crash + close(fid); + fid=open(fname,O_RDWR,0644); + initPosition(); + rebuildFileHeader(); + close(fid); + fid=open(fname,O_RDWR,0644); // PW: We want to open RDWR again + initPosition(); + readFileHeader(); // reread.. + } + buildRecordTable(); + restart(); // go to start of file + ndatasets = file_header.ndatasets; + // now we sync up in the way we would for a write + file_header.ndatasets=-1; + writeFileHeader(); // write header with -1 to indicate we are writing + // could refcount by increasing the - of numbers in header + // (needs extra flag for synced) + restart(); + nextRecord(); // prime the recordnumber + //seek(ndatasets); + break; + } +} + +IEEEIO::IEEEIO(IEEEIO *file): // read only dup + IObase(file->filename,IObase::Read),fid(dup(file->fid)), + swapbytes(file->swapbytes),datasetnumber(0), + ndatasets(file->ndatasets),hasread(0),writebuffer(0), + writebuffersize(0),writebuffercount(0),savedposition(-1), + file_length(-1),actual_position(-1),virtual_position(-1) +{ + if(file->masterfile) masterfile=file->masterfile; + else masterfile=file; + initPosition(); // initial positional pointers + ndatasets = file_header.ndatasets = file->ndatasets; + buildRecordTable(); + restart(); // go to start of file + nextRecord(); // prime the recordnumber +} + +// IEEEIO::AccesMode has scope commented out to satisfy Microsoft compiler +IEEEIO::IEEEIO(CONST char *fname,/*IEEEIO::*/AccessMode access,int swbytes): + IObase(fname,mode(access)),fid(-1),swapbytes(swbytes),datasetnumber(0), + ndatasets(0),hasread(0),masterfile(0),writebuffer(0), + writebuffersize(0),writebuffercount(0),savedposition(-1), + file_length(-1),actual_position(-1),virtual_position(-1) +{ + if(access==IEEEIO::SharedRead){ + masterfile=this; // we are multi-reading + fid=open(fname,O_RDONLY,0644); + if(fid<0){ + fprintf(stderr,"IEEEIO: Failed to open %s for reading\n",fname); + return; + } + this->initPosition(); + if(readFileHeader()<=0){ + close(fid); + fprintf(stderr,"IEEEIO: File %s is empty (opened for reading)\n",fname); + fid=-1; // invalid file + return; + } + buildRecordTable(); + restart(); // go to start of file + ndatasets = file_header.ndatasets; + nextRecord(); // prime the recordnumber + } + else + openFile(fname,mode(access),swbytes); +} + +IEEEIO::IEEEIO(CONST char *fname,IObase::AccessMode access,int swbytes): + IObase(fname,access),fid(-1),swapbytes(swbytes),datasetnumber(0), + ndatasets(0),hasread(0),masterfile(0),writebuffer(0), + writebuffersize(0),writebuffercount(0),savedposition(-1) +{ + // long fpos; + openFile(fname,access,swbytes); +} + +IEEEIO::~IEEEIO(){ + if(fid<0 && savedposition>=0) resume(); + // resume IO only if it is paused so that + // we can do the final writes to the file before + // closing + //puts("do bufferoff"); + if(writebuffer) bufferOff(); // automatically flushes buffer + //puts("now rewrite header"); + if(fid>=0){ + if(accessmode!=IObase::Read){ + file_header.ndatasets=ndatasets; + writeFileHeader(); + } + close(fid); + } + fid=-1; +} + +int IEEEIO::isValid(){ + if(fid>=0) return 1; + else return 0; +} + +int IEEEIO::write(IObase::DataType typeID,int rank,CONST int *dims,void *data){ + int i; + RecordHdr rec; + DataRecordHdr hdr; + // make sure its the EOF + if(accessmode==IObase::Read) return 0; + hasread=0; // reset hasread; (JMS: changed from local Thu Mar 12 13:53:13 CST 1998) + if(rec_table.getSize()>0){ + long endpos = rec_table[rec_table.getSize()-1].end_offset; + IEEEIO::lseek(endpos,L_SET); // don't know if this is costly + } + else + IEEEIO::lseek(0,L_XTND); // seek to end of file + // if 0-length seeks are costly, then alternative + // logic can be constructed to ensure writes to end of file + for(i=0,hdr.datasize=1;i<rank;i++) + hdr.datasize*=dims[i]; + + if(chunkdims.getSize()<rank) + chunkdims.setSize(rank); + for(i=0;i<rank;i++) chunkdims[i]=dims[i]; + + hdr.datasize*=sizeOf(typeID); + hdr.numbertype=typeID; + hdr.rank=rank; + // if last annotation slot is filled, it is a pointer + // to another block of 8 annotation pointers. + rec.recordtype = DataRecord; + rec.recordsize = hdr.datasize + + DataRecordHdrSize + + sizeof(Int) * rank; + rec.sequenceID = datasetnumber = ndatasets++; + // need to byteswap the header and records if swapbytes==True + current_dat=hdr; // first copy native info to current + current_rec=rec; + RecordHdr::write(this,rec); + current_dat_offset=current_rec_offset=getPosition(); + DataRecordHdr::write(this,hdr); + // write the dims.... (byteswap if necessary) + if(swapbytes) byteswapBuffer(chunkdims.getData(),rank,sizeof(Int)); + IEEEIO::write(chunkdims.getData(),sizeof(Int)*rank); + if(swapbytes) byteswapBuffer(chunkdims.getData(),rank,sizeof(Int)); // swap back + if(swapbytes) byteswapBuffer(data,hdr.datasize/sizeOf(typeID),sizeOf(typeID)); + int sz = IEEEIO::write(data,hdr.datasize); + // yeah! double bytswap seem stupid, but consider the alternatives + // write one byte at a time (swapping as we go) == systemcall overhead + // copy to a temporary buffer and swap bytes == malloc, copy, and free! + // strangely its faster to swap twice + if(swapbytes) byteswapBuffer(data,hdr.datasize/sizeOf(typeID),sizeOf(typeID)); + DataRef dref; + dref.rec=current_rec; + // Must find exact size!!! + dref.offset = current_rec_offset - RecordHdrSize; + dref.end_offset = current_rec_offset + current_rec.recordsize; + if(accessmode!=IObase::Write) // do not store if in write-only mode + rec_table.append(dref); + return sz; +} + +//setCurrentRec(offset); +// storing in 8*1024 byte blocks (always aligned to chunk boundaries) +// will result in more efficient seeking behavior due to +// disk block size... thus file is always aligned to unix +// filesystem blocks. (for now, we'll use die method maximo-stupido... +int IEEEIO::readInfo(IObase::DataType &typeID,int &rank,int *dims,int maxdims){ + if(accessmode!=IObase::Read && accessmode!=IObase::Append) + return 0; + //int sz; + if(hasread) datasetnumber++; // increment to the next one if this has been read + if(datasetnumber>=rec_table.getSize()) return 0; // end of file + // read the record + header + hasread=1; // we've read this, so next time we hit it, we'll increment again + IEEEIO::lseek(rec_table[datasetnumber].offset,L_SET); + // T3E Kludge + RecordHdr::read(this,current_rec); + DataRecordHdr::read(this,current_dat); + rank=current_dat.rank; + if(chunkdims.getSize()<rank) chunkdims.setSize(rank); + typeID=Int2DataType(current_dat.numbertype); + IEEEIO::read(chunkdims.getData(),sizeof(Int)*rank); + if(swapbytes) byteswapBuffer(chunkdims.getData(),(rank>maxdims)?maxdims:rank,sizeof(Int)); + + for(int i=0;i<maxdims && i<rank;i++) dims[i]=chunkdims[i]; + streaming=0; + return 1; +} + +/* + It does not appear that the bytswapping is really configured here... + */ +int IEEEIO::read(void *data){ + if(accessmode!=IObase::Read && accessmode!=IObase::Append) + return 0; // can't readif write only + if(datasetnumber>=rec_table.getSize()) return 0; // seek past end. + long datapos=rec_table[datasetnumber].offset+RecordHdrSize+DataRecordHdrSize+ + sizeof(Int)*current_dat.rank; + // should make certain current file position is correct + // if(getPosition() != datapos) (redundant call to lseek()) + IEEEIO::lseek(datapos,L_SET); // seek to position + int sz=IEEEIO::read(data,current_dat.datasize); // read nbytes + int typelen = sizeOf(Int2DataType(current_dat.numbertype));// compute elem sz + if(swapbytes) byteswapBuffer(data,current_dat.datasize/typelen,typelen); + return sz; +} + +int IEEEIO::seek(int idx){ + if(accessmode!=IObase::Read && + accessmode!=IObase::Append) // can't seek unless readable + return -1; // failed to seek + if (idx >= ndatasets || idx < 0) { + return -1; // do a bounds check (probably should just clip) + } + // bound the index + if((1+idx)>rec_table.getSize()) idx=rec_table.getSize(); + if(idx<0) idx=0; + index = idx; + IEEEIO::lseek(rec_table[index].offset,L_SET); + // T3E Kludge + RecordHdr::read(this,current_rec); + current_rec_offset=getPosition(); + datasetnumber=current_rec.sequenceID; // Changed: had -1 + hasread=0; // new file position + return current_rec.sequenceID; +} + +int IEEEIO::nDatasets() { + // can work across threads or across processes since + // it gathers information directly from the file. + // it does not need to share info with masterfile + // in fact you can delete the masterfile + if(masterfile){ // we are passive read-only. update nDatasets + // seek-scan to find datasets? + int oldlength = (rec_table.getSize())?(rec_table[rec_table.getSize()-1].end_offset):0; + //printf("oldlength=%u newlength=%u\n",oldlength,(unsigned int)(getLength())); + if(getLength()>oldlength){ + // lets scan to find the end + int lndatasets; + puts("We should clearly not be here!!!"); + restart(); // go to beginning + // steal seek loop from the nextDataRecord() + for(lndatasets=0; + nextDataRecord()>0; + lndatasets++){ + //printf("scan dataset[%u]\n",lndatasets); + } + file_header.ndatasets = lndatasets; + rec_table.setSize(lndatasets); + // printf("counted %u datasets. old was %u datasets\n",lndatasets,ndatasets); + seek(ndatasets); // seek to current last + ndatasets = lndatasets; // then reset the last + appendRecordTable(); // append new stuff to record table + } + } + return ndatasets; +} + +int IEEEIO::writeAnnotation(CONST char *annotation){ + int stringlen; + RecRef aref; + RecordHdr rec; + if(accessmode==IObase::Read || !annotation) return 0; + stringlen=strlen(annotation)+1; + + if(rec_table.getSize()>0){ + long endpos = rec_table[rec_table.getSize()-1].end_offset; + IEEEIO::lseek(endpos,L_SET); // don't know if this is costly + } + else + IEEEIO::lseek(0,L_XTND); // seek to end of file + + rec.recordtype=AnnotationRecord; + rec.recordsize=stringlen; + if(datasetnumber>=0) rec.sequenceID=datasetnumber; + else rec.sequenceID=current_rec.sequenceID; // a kludge for error immunity + current_rec = rec; + // T3E Kludge + current_rec_offset = getPosition() + RecordHdrSize; + // T3E Kludge + RecordHdr::write(this,rec); + // if(swapbytes) byteswap(rec); + int sz=IEEEIO::write(annotation,stringlen); + + aref.rec=current_rec; + // T3E Kludge + aref.offset=current_rec_offset-RecordHdrSize; + if(accessmode!=IObase::Write){ // don't write to index cache if in write-only mode + rec_table[datasetnumber].annotations.append(aref); + rec_table[datasetnumber].end_offset=getPosition(); + } + return sz; +} +/* + If objects could have all attributes implicitly availible for querying + when passed to subroutines. Basic object methods for data movement like + linearize() or object.linearized[index] object.linearized.size. + Then for objects that a receiver can't deal with either + 1: have compiletime "gatekeepers" that restrict allowable types + that it can receive + 2: Do a treesearch through object space to find a convertor sequence + that can convert the current type into the one that the object + accepts. If no such object can be found, indicate runtime + object adaptor failure. +*/ +int IEEEIO::readAnnotationInfo(int number,int &length){ + if(datasetnumber<0 || + number>=rec_table[datasetnumber].annotations.getSize()){ + length=0; + return -1; + } + length=rec_table[datasetnumber].annotations[number].rec.recordsize; + return length; +} + +int IEEEIO::readAnnotation(int number,char *annotation,int maxsize){ + // returns actual size of annotation or -1 if error + if(datasetnumber<0 || accessmode==IObase::Write || + number>=rec_table[datasetnumber].annotations.getSize()){ + return -1; + } + IEEEIO::lseek(rec_table[datasetnumber].annotations[number].offset,L_SET); + // T3E Kludge + RecordHdr::read(this,current_rec); + IEEEIO::read(annotation,(maxsize<rec_table[datasetnumber].annotations[number].rec.recordsize)? + maxsize:(rec_table[datasetnumber].annotations[number].rec.recordsize)); + annotation[maxsize-1]='\0'; + return rec_table[datasetnumber].annotations[number].rec.recordsize; +} + +int IEEEIO::nAnnotations(){ + if(datasetnumber<0 || accessmode==IObase::Write) + return -1; + return rec_table[datasetnumber].annotations.getSize(); +} + +// for attributes +int IEEEIO::writeAttribute(CONST char *name,IObase::DataType typeID,Long length,void *data){ + int stringlen; + AttribRef aref; + RecordHdr rec; + AttributeRecordHdr attrib; + + if(datasetnumber<0){ + fprintf(stderr,"IEEEIO::writeAttribute(): Error, cannot write attribute before any datasets have been written!\n"); + return 0; + } + if(accessmode==IObase::Read) return 0; + stringlen=strlen(name)+1; + + if(rec_table.getSize()>0){ + long endpos = rec_table[rec_table.getSize()-1].end_offset; + IEEEIO::lseek(endpos,L_SET); // don't know if this is costly + } + else + IEEEIO::lseek(0,L_XTND); // seek to end of file + attrib.datasize=length*sizeOf(typeID); + attrib.namesize=stringlen; + attrib.numbertype=typeID; + rec.recordtype=AttributeRecord; + rec.recordsize=attrib.datasize+attrib.namesize+AttributeRecordHdrSize; + if(datasetnumber>=0) rec.sequenceID=datasetnumber; + else rec.sequenceID=current_rec.sequenceID; // a kludge for error immunity + current_rec=rec; + current_rec_offset=getPosition() + RecordHdrSize; + aref.rec=current_rec; + aref.offset=current_rec_offset-RecordHdrSize; + // no copy constructor for aref, so must do manually + if(accessmode != IObase::Write){ + int lastindex; + rec_table[datasetnumber].attributes.append(aref); + lastindex=rec_table[datasetnumber].attributes.getSize()-1; + strcpy(rec_table[datasetnumber].attributes[lastindex].name,name); + } + // T3E Kludge + RecordHdr::write(this,rec); + AttributeRecordHdr::write(this,attrib); + //printf("\tnow write the string\n"); + IEEEIO::write(name,stringlen); + //printf("\tdone IEEEIO::stringlen=%u typeid=%u\n",attrib.namesize,attrib.numbertype); + // data is not byte-reversed... + int sz=0; + if(swapbytes) byteswapBuffer(data,length,sizeOf(typeID)); + IEEEIO::write(data,length*sizeOf(typeID)); + if(swapbytes) byteswapBuffer(data,length,sizeOf(typeID)); // swap back + if(accessmode != IObase::Write) + rec_table[datasetnumber].end_offset=getPosition(); + // doesn't appear to store attributes properly + return sz; +} + +int IEEEIO::readAttributeInfo(int number,char *name,IObase::DataType &typeID, + Long &nelem,int maxnamelen){ + if(accessmode==IObase::Write) return -1; + FlexArray<AttribRef> *attribs= &(rec_table[datasetnumber].attributes); + if(number>=(*attribs).getSize()) return -1; // > number of attributes + AttribRef *attrib=&((*attribs)[number]); + if(strlen(attrib->name)>maxnamelen){ + strncpy(name,attrib->name,maxnamelen); // don't we want to copy the other way? + name[maxnamelen-1]='\0'; + } + else strcpy(name,attrib->name); + name[maxnamelen-1]='\0'; // make certain it is null capped + AttributeRecordHdr attribhdr; + IEEEIO::lseek((*attribs)[number].offset,L_SET); + RecordHdr::read(this,current_rec); + AttributeRecordHdr::read(this,attribhdr); +//printf("IEEEIO:attribrechdr = ds,nt,ns %d,%d,%d\n", +// attribhdr.datasize, +// attribhdr.numbertype, +// attribhdr.namesize); + typeID = Int2DataType(attribhdr.numbertype); + nelem = attribhdr.datasize/sizeOf(typeID); + return -1; +} + +int IEEEIO::readAttributeInfo(CONST char *name,IObase::DataType &typeID,Long &nelem){ + // by name + if(accessmode==IObase::Write) return -1; + FlexArray<AttribRef> *attribs= &(rec_table[datasetnumber].attributes); + for(int i=0;i<attribs->getSize();i++){ + if(!strcmp(name,(*attribs)[i].name)){ + // must read the record to get this info + AttributeRecordHdr attribhdr; + IEEEIO::lseek((*attribs)[i].offset,L_SET); + RecordHdr::read(this,current_rec); + AttributeRecordHdr::read(this,attribhdr); + typeID = Int2DataType(attribhdr.numbertype); + nelem = attribhdr.datasize/sizeOf(typeID); + return i; + } + } + return -1; // Attribute not found +} + +int IEEEIO::readAttribute(int number,void *data){ + if(accessmode==IObase::Write) return -1; + FlexArray<AttribRef> *attribs= &(rec_table[datasetnumber].attributes); + if(number>=(*attribs).getSize()) return -1; // > number of attributes + AttributeRecordHdr attribhdr; + IEEEIO::lseek((*attribs)[number].offset,L_SET); + RecordHdr::read(this,current_rec); + AttributeRecordHdr::read(this,attribhdr); + IObase::DataType typeID = Int2DataType(attribhdr.numbertype); + if(typeID==IObase::Error) return -1; // read failed due to bad datatype + long nelem = attribhdr.datasize/sizeOf(typeID); + IEEEIO::lseek(attribhdr.namesize,L_INCR); + int sz=IEEEIO::read(data,attribhdr.datasize)/sizeOf(typeID); + if(swapbytes) byteswapBuffer(data,sz,sizeOf(typeID)); + if(typeID==IObase::String){ + char *cdata=(char *)data; + cdata[sz]='\0'; /* Null Terminate String data */ + } + return sz; // returns number of elements read +} + +int IEEEIO::nAttributes(){ + if(datasetnumber<0 || accessmode == IObase::Write) return -1; + return rec_table[datasetnumber].attributes.getSize(); +} + +//================Chunking Interface----------------------- +void IEEEIO::clearChunk(int nbytes){ +#ifdef T3E +#define DISKBLOCKSIZE 128*1024 +#else +#define DISKBLOCKSIZE 8192 +#endif +// This is a wasteful feature that will be eliminated in later releases +// This is currently done because I needed the disk space to be zero'ed +// prior to writing for debugging purposes... Could have a writeStream +// class. + int nwritten; + char dummy[DISKBLOCKSIZE]; + for(int i=0;i<DISKBLOCKSIZE;i++) + dummy[i]=0; // bzero it (reserve space with zero'ed data) + while(nbytes>DISKBLOCKSIZE){ + nwritten=IEEEIO::write(dummy,DISKBLOCKSIZE); + if(nwritten<=0) return; // this aint working + nbytes-=nwritten; + } + if(nbytes>0) IEEEIO::write(dummy,nbytes); // write the remaining bytes +#undef DISKBLOCKSIZE +} + +int IEEEIO::reserveChunk(IObase::DataType typeID,int rank,CONST int *dims){ + reserveStream(typeID,rank,dims); // sets up the chunking state machine + // clearing does not appear to be needed... just need header + //clearChunk(IObase::nBytes(typeID,rank,dims)); // this pre-clears the reserved area to 0. (inefficient) + return 1; +} + +// Chunking should probably include striding.... +// But thats a pain since you'll need to read + write to +// Accomplish that (yuck!). Lots of seeking. +// Lets leave striding out for now. + +// should also check for contiguous data (eg. slicing) and optimize for that layout +// for now, the streaming interface best serves contiguous data. +int IEEEIO::writeChunk(CONST int *dims,CONST int *origin,void *data){ + int i,sz; + // make sure its the EOF + if(accessmode==IObase::Read) { + fprintf(stderr,"IEEEIO::writeChunk(): Error! Access is ReadOnly\n"); + return 0; + } + // should have a "chunk reserved" flag set. + if(current_reserved_chunk!=datasetnumber){ + fprintf(stderr,"IEEEIO::writeChunk(): Error! You forgot to reserve space for the chunk using IO::reserveChunk()\n"); + return 0; + } + + // now we need to seek to the position of the data + int rank = current_dat.rank; + IObase::DataType typeID = IObase::Int2DataType(current_dat.numbertype); + int typesize=sizeOf(typeID); + // long basefileoffset = rec_table[datasetnumber].offset+ + long basefileoffset = stream_offset + + RecordHdrSize+DataRecordHdrSize+sizeof(Int)*current_dat.rank; // T3E Kludge + long chunkcolumnsize=dims[0]*typesize; // stride between columns in chunk + long filecolumnsize=chunkdims[0]*typesize; // stride between columns on disk (full array size) + long originoffset; + long accumdims; + for(originoffset=0,accumdims=typesize,i=0;i<rank;i++){ + originoffset += (origin[i]*accumdims); + accumdims *= chunkdims[i]; + } + for(i=0;i<rank;i++){ + if((origin[i]+dims[i])>chunkdims[i]){ + fprintf(stderr,"IEEEIO::writeChunk(): ERROR!! specified dims and origin exceed reserved block size\n"); + fprintf(stderr,"\tfor dimension %u origin=%u and dims=%u\n",i,origin[i], + dims[i]); + fprintf(stderr,"\torigin+dims=%u whereas the maximum must be less than %u\n",origin[i]+dims[i],chunkdims[i]); + } + } + originoffset+=basefileoffset; // for absolute seeking + long maxindex,minindex; + minindex=basefileoffset; + maxindex=basefileoffset+current_dat.datasize; + int ncolumns; // computed in loop below + for(ncolumns=1,i=1;i<rank;i++) ncolumns*=dims[i]; + if(swapbytes) + byteswapBuffer(data,IObase::nElements(rank,dims),typesize); + for(i=0;i<ncolumns;i++){ // read the columns + if(originoffset<minindex){ + fprintf(stderr,"WriteChunk() inconsistency. Writing less than min index\n"); + fprintf(stderr,"\tCol[%u]: Requested %u, but minidex= %u\n", + i,(unsigned int)originoffset,(unsigned int)minindex); + } + if(originoffset>maxindex){ + fprintf(stderr,"WriteChunk() inconsistency. Writing greater than maximum index\n"); + fprintf(stderr,"\tCol[%u]: Requested %u, but maxindex= %u\n", + i,(unsigned int)originoffset,(unsigned int)maxindex); + } + if((originoffset+chunkcolumnsize)>maxindex){ + fprintf(stderr,"WriteChunk() inconsistency. This write will overrun the reserved data\n"); + fprintf(stderr,"\tCol[%u]: Requested %u, maxindex %u, and the write of %u will run to %u\n", + i,(unsigned int)originoffset,(unsigned int)maxindex,(unsigned int)chunkcolumnsize,(unsigned int)(originoffset+chunkcolumnsize)); + } + IEEEIO::lseek(originoffset,L_SET); + sz=IEEEIO::write(((char*)data)+i*chunkcolumnsize,(int)chunkcolumnsize); + originoffset+=filecolumnsize; + for(long j=1,idx=dims[1],planesize=filecolumnsize; + j<(rank-1) && !((i+1)%idx); + idx*=dims[++j]){ + long extraoffset=planesize*(chunkdims.getData()[j]-dims[j]); + originoffset+=extraoffset; + planesize*=dims[j]; + } + } + // now swap the data back to native order... + if(swapbytes) + byteswapBuffer(data,IObase::nElements(rank,dims),typesize); + + return sz; +} +int IEEEIO::readChunk(CONST int *dims,CONST int *origin,void *data){ + int sz,i; + if(accessmode!=IObase::Read && accessmode!=IObase::Append) + return 0; + // gonna have to stride through this sucker... (yuck) + // use the same chunkdims for this. Must set during readinfo... + // now we need to seek to the position of the data + int rank = current_dat.rank; + IObase::DataType typeID = IObase::Int2DataType(current_dat.numbertype); + int typesize=sizeOf(typeID); + long basefileoffset = rec_table[datasetnumber].offset+ + // sizeof(RecordHdr)+sizeof(DataRecordHdr)+sizeof(Int)*current_dat.rank; + RecordHdrSize+DataRecordHdrSize+sizeof(Int)*current_dat.rank; // T3E Kludge + long chunkcolumnsize=dims[0]*typesize; // stride between columns in chunk + long filecolumnsize=chunkdims[0]*typesize; // stride between columns on disk (full array size) + long originoffset; + long accumdims; + // compute the offset into the data on disk required by the chunk origin (initial offset) + for(originoffset=0,accumdims=typesize,i=0;i<rank;i++){ + originoffset += (origin[i]*accumdims); + accumdims *= chunkdims[i]; + } + originoffset+=basefileoffset; // for absolute seeking + long ncolumns; // computed in loop below + for(ncolumns=1,i=1;i<rank;i++) ncolumns*=dims[i]; + for(i=0;i<ncolumns;i++){ // read the columns + IEEEIO::lseek(originoffset,L_SET); + sz=IEEEIO::read(((char*)data)+i*chunkcolumnsize,(int)chunkcolumnsize); + originoffset+=filecolumnsize; + for(long j=1,idx=dims[1],planesize=filecolumnsize; + j<(rank-1) && !((i+1)%idx); + idx*=dims[++j]){ + long extraoffset=planesize*(chunkdims.getData()[j]-dims[j]); + originoffset+=extraoffset; + planesize*=dims[j]; + } + } + // now swap the data back to native order... + if(swapbytes) + byteswapBuffer(data,IObase::nElements(rank,dims),typesize); + return sz; +} +// Nearly identical to reserveChunk except that we don't need to pre-clear +// the space to 0 for streamed data. +int IEEEIO::reserveStream(IObase::DataType typeID,int rank,CONST int *dims){ + int i; + RecordHdr rec; + DataRecordHdr hdr; + // make sure its the EOF + if(accessmode==IObase::Read) return 0; + + if(rec_table.getSize()>0){ + long endpos = rec_table[rec_table.getSize()-1].end_offset; + IEEEIO::lseek(endpos,L_SET); // don't know if this is costly + } + else { + IEEEIO::lseek(0,L_XTND); // seek to end of file + } + // if 0-length seeks are costly, then alternative + // logic can be constructed to ensure writes to end of file + if(chunkdims.getSize() != rank) + chunkdims.setSize(rank); // KLUDGE! + for(i=0,hdr.datasize=1;i<rank;i++) { + hdr.datasize*=dims[i]; + chunkdims[i]=dims[i]; + } + cur_type_size=sizeOf(typeID); // KLUDGE: for writeStream() + hdr.datasize*=sizeOf(typeID); + hdr.numbertype=typeID; + hdr.rank=rank; + // if last annotation slot is filled, it is a pointer + // to another block of 8 annotation pointers. + rec.recordtype = DataRecord; + rec.recordsize = hdr.datasize + + DataRecordHdrSize + // T3E Kludge + sizeof(Int) * rank; + rec.sequenceID = datasetnumber = ndatasets++; + // need to byteswap the header and records if swapbytes==True + current_dat=hdr; // first copy native info to current + current_rec=rec; + RecordHdr::write(this,rec); + current_dat_offset=current_rec_offset=getPosition(); + DataRecordHdr::write(this,hdr); + if(swapbytes) byteswapBuffer(chunkdims.getData(),rank,sizeof(Int)); + IEEEIO::write(chunkdims.getData(),sizeof(Int)*rank); // for better for worse... + if(swapbytes) byteswapBuffer(chunkdims.getData(),rank,sizeof(Int)); // swap back + // OK, now writing 8k at a time, reserve the space with 0's + current_reserved_chunk=datasetnumber; + DataRef dref; + dref.rec=current_rec; + dref.offset=current_rec_offset-RecordHdrSize; + dref.end_offset=current_rec_offset+current_rec.recordsize; + if(accessmode!=IObase::Write) + rec_table.append(dref); + // else (JMS debugging change 5/12/98) + stream_offset = current_rec_offset-RecordHdrSize; + streaming=0; + return 1; +} + +int IEEEIO::writeStream(void *data,int length){ + int len; + if(!streaming){// seek to starting offset + long basefileoffset = stream_offset + + RecordHdrSize+DataRecordHdrSize+sizeof(Int)*current_dat.rank; + IEEEIO::lseek(basefileoffset,L_SET); + streaming=1; + } + // need to do a bounds check on the stream... for now it'll play dumb... + long nbytes=cur_type_size * length; + len = IEEEIO::write((char *)data,nbytes); // record file position?? + if(len<0) + return -1; // write failure + return len; +} + +int IEEEIO::readStream(void *data,int length){ + int len; + if(!streaming){// seek to starting offset + long basefileoffset = rec_table[datasetnumber].offset+ + RecordHdrSize+DataRecordHdrSize+sizeof(Int)*current_dat.rank; + IEEEIO::lseek(basefileoffset,L_SET); + streaming=1; + } + // need to do a bounds check on the stream... for now it'll play dumb... + int typesize= sizeOf(Int2DataType(current_dat.numbertype)); + long nbytes = typesize * length; + len = IEEEIO::read((char *)data,nbytes); + if(len<0) + return -1; // read failure + else + if(swapbytes) byteswapBuffer(data,length,typesize); + return len; +} + +void IEEEIO::bufferOn(long size){ + if(writebuffer) { + if(size==0) + bufferOff(); + return; + } + else { +#if !defined(WIN32) && !defined(T3E)// if not Windows, use fstat() to choose optimal blocksiz + if(size<0){ + struct stat mystat; + fstat(fid,&mystat); + size=mystat.st_blksize; + } +#endif + // if windows then take a wild guess +#ifdef T3E + size=(size<=64)?8*1024*1024:size; // 8Meg is a good guess for T3E +#else + size=(size<=64)?8*1024:size; //(8k is a good guess for workstations/WinTel) +#endif + writebuffer = new char[size]; + } + writebuffersize=size; + writebuffercount=0; +} + +void IEEEIO::bufferOff(){ + if(!writebuffer) return; + if(fid<0 && writebuffercount>0){ + fprintf(stderr,"IEEEIO::bufferOff() ERROR!!!!"); + fprintf(stderr,"\tFile %s is either paused or invalid, so I can't deallocate the write buffer safely\n",filename); + return; + } + //printf("bufferoff on a=%ld v=%ld f=%ld b=%d\n", + // actual_position,virtual_position,file_length,writebuffercount); + this->flush(); + delete writebuffer; + writebuffer=0; +} + +int IEEEIO::pause(){ + if(fid<0) return 0; // fail + this->flush(); // flush the buffer to pause + savedposition=getPosition(); + close(fid); + fid=-1; + return 1; // success +} + +int IEEEIO::resume(){ + if(fid>=0 || savedposition<0) return 0; // fail + switch(IObase::accessmode){ + case IObase::Read: + fid=open(filename,O_RDONLY,0644); + break; + case IObase::Write: + fid=open(filename,O_WRONLY,0644); + break; + case IObase::Append: + fid=open(filename,O_RDWR,0644); + break; + } + if(fid<0){ + printf("IEEEIO::resume failed for file %s\n",filename); + return -1; + } + ::lseek(fid,savedposition,L_SET); // this is OK.. + savedposition=-1; + return 1; // success +} + +//*********************Fortran Interface********************** +Long8 f_ieee_open (char *file,char *accessname,int flen,int alen){ + // would have used tolower(), but it doesn't exist everywhere.... :( + IObase::AccessMode mode; + if(*accessname=='R' || *accessname=='r') + mode=IObase::Read; + else if(*accessname=='W' || *accessname=='w' || + *accessname=='C' || *accessname=='c') + mode=IObase::Write; + else if(*accessname=='A' || *accessname=='a') + mode=IObase::Append; + else { + fprintf(stderr,"IEEEopen(): Error unknown option [%s] to open file %s\n", + accessname,file); + return 0; + } + IObase *fid=new IEEEIO(file,mode); + if(fid->isValid()) + return (Long8)fid; + else + delete fid; // file open failed + return 0; +} + +Long8 f_ieee_openr(char *filename,int namelen){ + filename[namelen]='\0'; + return (Long8)(new IEEEIO(filename,IObase::Read)); +} + +Long8 f_ieee_openw(char *filename,int namelen){ + filename[namelen]='\0'; + return (Long8)(new IEEEIO(filename,IObase::Write)); +} + +Long8 f_ieee_opena(char *filename,int namelen){ + filename[namelen]='\0'; + return (Long8)(new IEEEIO(filename,IObase::Append)); +} + +void ieee_bufon(Long8 *fileID,int bufsize){ + IEEEIO *f=(IEEEIO*)(*fileID); + if(bufsize<0) f->bufferOn(); // use default size + else f->bufferOn(bufsize); // or specify +} + +void ieee_bufoff(Long8 *fileID){ + IEEEIO *f=(IEEEIO*)(*fileID); + f->bufferOff(); +} + +IOFile IEEEopen(char *filename,char *accessname){ + // Parse all of the ansi stdio access option strings + IObase::AccessMode mode; + if(!strcmp(accessname,"read") || + !strcmp(accessname,"r") || + !strcmp(accessname,"rb")) + mode=IObase::Read; + else if(*accessname=='a') + mode=IObase::Append; + else if(!strcmp(accessname,"write") || + !strcmp(accessname,"create") || + !strcmp(accessname,"w") || + !strcmp(accessname,"wb") || + !strcmp(accessname,"w+") || + !strcmp(accessname,"w+b") || + !strcmp(accessname,"wb+")) + mode=IObase::Write; + else{ + fprintf(stderr,"IEEEopen(): Error unknown option [%s] to open file %s\n", + accessname,filename); + return 0; + } + IObase *fid=new IEEEIO(filename,mode); + if(fid->isValid()) + return (IOFile)fid; + else + delete fid; // file open failed + return 0; // unknown option +} + +IOFile IEEEopenRead(char *filename){ + return (IOFile)(new IEEEIO(filename,IObase::Read)); +} + +IOFile IEEEopenWrite(char *filename){ + return (IOFile)(new IEEEIO(filename,IObase::Create)); +} + +IOFile IEEEopenAppend(char *filename){ + return (IOFile)(new IEEEIO(filename,IObase::Append)); +} + +void IEEEbufferOn(IOFile fileID,int bufsize){ + IEEEIO *f=(IEEEIO*)fileID; + if(bufsize<0) f->bufferOn(); // use default size + else f->bufferOn(bufsize); // or specify +} + +void IEEEbufferOff(IOFile fileID){ + IEEEIO *f=(IEEEIO*)fileID; + f->bufferOff(); +} + diff --git a/src/IEEEIO/IEEEIO.h b/src/IEEEIO/IEEEIO.h new file mode 100644 index 0000000..e17da17 --- /dev/null +++ b/src/IEEEIO/IEEEIO.h @@ -0,0 +1,13 @@ +#ifndef __IEEEIO_H_ +#define __IEEEIO_H_ + +#include "IO.h" +#include "Arch.h" + +IOFile IEEEopen PROTO((char *filename,char *accessname)); +IOFile IEEEopenRead PROTO((char *filename)); +IOFile IEEEopenWrite PROTO((char *filename)); +IOFile IEEEopenAppend PROTO((char *filename)); +void IEEEbufferOn PROTO((IOFile fileID,int bufsize)); +void IEEEbufferOff PROTO((IOFile fileID)); +#endif diff --git a/src/IEEEIO/IEEEIO.hh b/src/IEEEIO/IEEEIO.hh new file mode 100644 index 0000000..4e79647 --- /dev/null +++ b/src/IEEEIO/IEEEIO.hh @@ -0,0 +1,598 @@ +#ifndef __IEEEIO_HH_ +#define __IEEEIO_HH_ +#ifndef WIN32 +#include <stdio.h> +#include <unistd.h> +#include <sys/file.h> +#else +#include <io.h> +#endif + +#include "IO.hh" +#include "FlexArrayTmpl.H" +#include "Arch.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> +#if defined(WIN32) || defined(SOLARIS) +// I'm a WIN32 box and I ignore standards +// or I'm a Sun box and I hate BSD. +#define bcopy(src,dest,len) memcpy(dest,src,len) +#else +#include <strings.h> // available non-ANSI/Posix compliant +#endif + +#ifdef FFIO +#include <ffio.h> +#endif + +// Win32/Solaris lseek crap... +#if defined(WIN32) || defined(SOLARIS) +#define L_INCR SEEK_CUR +#ifndef L_SET // for that wanky Solaris compiler +#define L_SET SEEK_SET +#endif +#define L_XTND SEEK_END +#endif +// T3E lseek crap... +#ifdef T3E // I'm a T3E and I ignore ANSI/Posix standards +#define L_INCR SEEK_CUR +#define L_SET SEEK_SET +#define L_XTND SEEK_END +#endif + +#define IEEE_NATIVE_MAGIC 0x01020304 +#define IEEE_REVERSE_MAGIC 0x04030201 +#define IEEE_ALTERNATING_MAGIC 0x02010403 + +#define IEEE_BIG_ENDIAN IEEE_NATIVE_MAGIC +#define IEEE_LITTLE_ENDIAN IEEE_REVERSE_MAGIC +#define IEEE_VAX_ENDIAN IEEE_ALTERNATING_MAGIC + +#define IEEE_MAJORVERSION 1 +#define IEEE_MINORVERSION 0 + +class IEEEIO : public IObase { +public: + enum RecordType {DataRecord=1,AnnotationRecord,AttributeRecord}; + union btorder { + char c[4]; // write as Int, read as Int + Int i; + }; +#define FileHdrSize (4+4+4+2+2) // T3E Kludge + struct FileHdr { + // do magic twice so we can figure out which byte-order + // the current machine is in addition (without hardcoding) in + // addition to which byte order the file is in + btorder magic; + btorder byteorder; + Long ndatasets; + // T3E will be stupid for this, but thats OK + short majorversion; + short minorversion; + // ** Inline these because they are + // ** actually kludges for the T3E + static int write(IEEEIO *ieeeio,FileHdr &rec){ + int r=0; +#ifdef T3E // data structures are not packed contiguously in memory + r+=ieeeio->write((rec.magic.c),4); + r+=ieeeio->write((rec.byteorder.c),4); + r+=ieeeio->write(&(rec.ndatasets),4); + // this may be incorrect, but it will work for now + // It assumes the low order bytes will be first to + // be written. + r+=ieeeio->write(&(rec.majorversion),2); + r+=ieeeio->write(&(rec.minorversion),2); +#else + // problem if this includes pointers to static methods + // will require an extra degree of inheritance. (inherit + // from a base structure. + r=ieeeio->write(&rec,FileHdrSize); +#endif + return r; + } + static int read(IEEEIO *ieeeio,FileHdr &rec){ + int r=0; +#ifdef T3E // data structures are not packed contiguously in memory + char buffer[FileHdrSize]; + r=ieeeio->read(buffer,FileHdrSize); // read full buffer in and then copy to struct + ::bcopy(buffer,(rec.magic.c),4); + ::bcopy(buffer+4,(rec.byteorder.c),4); + ::bcopy(buffer+8,&(rec.ndatasets),4); + // May be invalid if low order bytes are not the + // proper ones to load. (eg. may fail on big-endian systems) + ::bcopy(buffer+12,&(rec.majorversion),2); + ::bcopy(buffer+14,&(rec.minorversion),2); +#else + r=ieeeio->read(&rec,FileHdrSize); +#endif + return r; + } + static void byteswap(FileHdr &hdr){ + IEEEIO::byteswapBuffer(&(hdr.ndatasets),1,sizeof(hdr.ndatasets)); + // because the t3e doesn't have a 2-byte datatype, must do this manually + IEEEIO::byteswapBuffer(&(hdr.majorversion),1,2); + IEEEIO::byteswapBuffer(&(hdr.minorversion),1,2); + } + // May be a problem to have a nonstatic class member here because + // this will create a vtable entry. + void byteswap(){ + IEEEIO::byteswapBuffer(&ndatasets,1,sizeof(ndatasets)); + // because the t3e doesn't have a 2-byte datatype, must do this manually + IEEEIO::byteswapBuffer(&majorversion,1,2); + IEEEIO::byteswapBuffer(&minorversion,1,2); + } + }; +#define RecordHdrSize (4+4+4) // T3E Kludge + struct RecordHdr { + Int recordtype; + Long recordsize; + Int sequenceID; + // ** Inline these because they are + // ** actually kludges for the T3E + static int write(IEEEIO *ieeeio,RecordHdr &rec){ + int r=0; + // copy into buffer + if(ieeeio->swapbytes){ + char buffer[RecordHdrSize]; + IEEEIO::byteswapBuffer(&(rec.recordtype),buffer,1,4); + IEEEIO::byteswapBuffer(&(rec.recordsize),buffer+4,1,4); + IEEEIO::byteswapBuffer(&(rec.sequenceID),buffer+8,1,4); + r=ieeeio->write(buffer,RecordHdrSize); + } + else { +#ifdef T3E // data structures are not packed contiguously in memory + r+=ieeeio->write(&(rec.recordtype),4); + r+=ieeeio->write(&(rec.recordsize),4); + r+=ieeeio->write(&(rec.sequenceID),4); +#else + r=ieeeio->write(&rec,RecordHdrSize); + //printf("Wrote rechdr %u,%u,%u\n",rec.recordtype,rec.recordsize,rec.sequenceID); +#endif + } + return r; + } + static int read(IEEEIO *ieeeio,RecordHdr &rec){ + char buffer[RecordHdrSize]; + int r=ieeeio->read(buffer,RecordHdrSize); + if(ieeeio->swapbytes) + IEEEIO::byteswapBuffer(buffer,3,4);//3 elements of 4 bytes +#ifdef T3E // data structures are not packed contiguously in memory + // do it the stupid way first + // would be smarter to pack buffers really + ::bcopy(buffer,&(rec.recordtype),4); + ::bcopy(buffer+4,&(rec.recordsize),4); + ::bcopy(buffer+8,&(rec.sequenceID),4); +#else + ::bcopy(buffer,(char*)&rec,RecordHdrSize); +#endif + return r; + } + }; + +#define DataRecordHdrSize (4+4+4) // T3E Kludge + struct DataRecordHdr { + Long datasize; + Int numbertype; + Int rank; + // ** Inline these because they are + // ** actually kludges for the T3E + static int write(IEEEIO *ieeeio,DataRecordHdr &rec){ + int r=0; + // copy into buffer + if(ieeeio->swapbytes){ + char buffer[DataRecordHdrSize]; + IEEEIO::byteswapBuffer(&(rec.datasize),buffer,1,4); + IEEEIO::byteswapBuffer(&(rec.numbertype),buffer+4,1,4); + IEEEIO::byteswapBuffer(&(rec.rank),buffer+8,1,4); + r=ieeeio->write(buffer,DataRecordHdrSize); + } + else { +#ifdef T3E + r+=ieeeio->write(&(rec.datasize),4); + r+=ieeeio->write(&(rec.numbertype),4); + r+=ieeeio->write(&(rec.rank),4); +#else + r=ieeeio->write(&rec,DataRecordHdrSize); +#endif + } + return r; + } + static int read(IEEEIO *ieeeio,DataRecordHdr &rec){ + char buffer[DataRecordHdrSize]; + int r=ieeeio->read(buffer,DataRecordHdrSize); + if(ieeeio->swapbytes) + IEEEIO::byteswapBuffer(buffer,3,4);//3 elements of 4 bytes +#ifdef T3E + // do it the stupid way first + // would be smarter to pack buffers really + ::bcopy(buffer,&(rec.datasize),4); + ::bcopy(buffer+4,&(rec.numbertype),4); + ::bcopy(buffer+8,&(rec.rank),4); +#else + ::bcopy(buffer,(char*)&rec,DataRecordHdrSize); +#endif + return r; + } + }; +#define AttributeRecordHdrSize (4+4+4) // T3E Kludge + struct AttributeRecordHdr { + Long datasize; + Int numbertype; + Int namesize; // attributes are named + // ** Inline these because they are + // ** actually kludges for the T3E + static int write(IEEEIO *ieeeio,AttributeRecordHdr &rec){ + int r=0; + // copy into buffer + if(ieeeio->swapbytes){ // pack a buffer + char buffer[AttributeRecordHdrSize]; + IEEEIO::byteswapBuffer(&(rec.datasize),buffer,1,4); + IEEEIO::byteswapBuffer(&(rec.numbertype),buffer+4,1,4); + IEEEIO::byteswapBuffer(&(rec.namesize),buffer+8,1,4); + r=ieeeio->write(buffer,RecordHdrSize); + } + else { +#ifdef T3E + r+=ieeeio->write(&(rec.datasize),4); + r+=ieeeio->write(&(rec.numbertype),4); + r+=ieeeio->write(&(rec.namesize),4); +#else + //printf("AttribRec::writing ds,nt,ns %d,%d,%d\n",rec.datasize,rec.numbertype,rec.namesize); + r = ieeeio->write(&rec,AttributeRecordHdrSize); + //printf("Wrote attribhdr %d,%d,%d\n",rec.datasize,rec.numbertype,rec.namesize); + //printf("\tr=%u\n",r); +#endif + } + return r; + } + static int read(IEEEIO *ieeeio,AttributeRecordHdr &rec){ + int r=0; + char buffer[AttributeRecordHdrSize]; + r=ieeeio->read(buffer,AttributeRecordHdrSize); + if(ieeeio->swapbytes) + IEEEIO::byteswapBuffer(buffer,3,4); +#ifdef T3E + ::bcopy(buffer,&(rec.datasize),4); + ::bcopy(buffer+4,&(rec.numbertype),4); + ::bcopy(buffer+8,&(rec.namesize),4); +#else + ::bcopy(buffer,(char*)&rec,AttributeRecordHdrSize); + //printf("AttribRec::reading ds,nt,ns %d,%d,%d\n",rec.datasize,rec.numbertype,rec.namesize); +#endif + return r; + } + }; + + struct RecRef { + RecordHdr rec; + long offset; + //virtual ~RecRef(); + // ** Inline these because they are + // ** actually kludges for the T3E + }; + + struct AttribRef : public RecRef { + //FlexArray<char> name; + char name[64]; + AttribRef(); + AttribRef(AttribRef &src); + //virtual ~AttribRef(); + AttribRef &operator=(AttribRef &src); + // ** Inline these because they are + // ** actually kludges for the T3E + }; + + struct DataRef : public RecRef { + FlexArray<RecRef> annotations; + FlexArray<AttribRef> attributes; + long end_offset; + DataRef(); + //virtual ~DataRef(); + DataRef(DataRef &src); + DataRef &operator=(DataRef &src); + }; +private: + friend class AttribRef; + friend class DataRef; + friend class FileHdr; + friend class RecordHdr; + friend class DataRecordHdr; + friend class AttributeRecordHdr; + // convenience utilities +#ifdef T3E + int ffioIsTrue(); +#endif + void initPosition(); + inline long getPosition(){return this->lseek(0,L_INCR);} + void restart(); + int nextRecord(); + int nextDataRecord(); + inline long getLength(){ return file_length; } + static void byteswapBuffer(void *buf,long nelements,int elementsize); + static void byteswapBuffer(void *source,void *dest,long nelements,int elementsize); + int writeFileHeader(); + int readFileHeader(); + void rebuildFileHeader(); + void appendRecordTable(); + void buildRecordTable(); + void clearChunk(int nbytes); + void openFile(CONST char *fname,IObase::AccessMode access,int swbytes); + // annotations don't need a header + // for c, size includes null terminator. + // must decrement size for f77 + //inline int write(int fid,void *data,int size); +protected: + FlexArray<DataRef> rec_table; + FlexArray<Int> chunkdims; + FileHdr file_header; + RecordHdr current_rec,current_data_rec; + DataRecordHdr current_dat; + long current_rec_offset; // offset past the record header + long stream_offset; // kludge to allow index cache to be turned off + long current_dat_offset; // to datarec + int fid,datasetnumber,ndatasets,swapbytes,current_reserved_chunk; + int hasread,streaming,cur_type_size; + IEEEIO *masterfile; + int writebuffersize,writebuffercount; + char *writebuffer; + long savedposition; + long virtual_position,actual_position; + long file_length; +protected: + inline int write(const void *data,size_t size); + inline void flush(); + inline int lseek(long len, int direction); + inline int read(void *data,size_t size); +public: + //------------------------core stuff..................... + enum AccessMode {Read=0,Write=1,Create=1,Append=2,SharedRead=4}; + IObase::AccessMode mode(IEEEIO::AccessMode amode){ + switch(amode){ + case Read: + return IObase::Read; + case Write: + return IObase::Write; + case Append: + return IObase::Append; + default: + return IObase::Read; + } + } + // the IEEEIO:: commented out to satisfy Microsoft Compiler + IEEEIO(CONST char *fname, /*IEEEIO::*/AccessMode access,int swbytes=0); + IEEEIO(CONST char *fname,IObase::AccessMode access,int swbytes=0); + IEEEIO(IEEEIO *file); // read-only dup an existing open file + virtual ~IEEEIO(); + virtual int isValid(); + // could use overloading to differentiate type here... (but I'm going simple) + virtual int write(IObase::DataType typeID,int rank,CONST int *dims,void *data); + virtual int readInfo(IObase::DataType &typeID,int &rank,int *dims,int maxdims=3); + virtual int read(void *data); + //virtual int readChunk(int dims,int origin,int stride,void *data)=0; + virtual int seek(int dataset_index); + virtual int nDatasets(); + + virtual int writeAnnotation(CONST char *annotation); + virtual int readAnnotationInfo(int number,int &length); // returns length (-1 if none left) + virtual int readAnnotation(int number,char *annotation,int maxsize=128); + virtual int nAnnotations(); + + virtual int writeAttribute(CONST char *name,IObase::DataType typeID,Long length,void *data); + // returns number + virtual int readAttributeInfo(int number,char *name,IObase::DataType &typeID,Long &nelem,int maxnamelen=128); + virtual int readAttributeInfo(CONST char *name,IObase::DataType &typeID,Long &nelem); // returns number + virtual int readAttribute(int number,void *data); + // virtual Long readAttribute(CONST char *name,void *data); + virtual int nAttributes(); + + //-----------------Chunking Utilities.................. + virtual int reserveChunk(IObase::DataType typeID,int rank,CONST int *dims); + virtual int writeChunk(CONST int *chunkdims,CONST int *chunkorigin,void *data); + virtual int readChunk(CONST int *chunkdims,CONST int *chunkorigin,void *data); + // Streaming interface is for support of PANDA, Sockets etc.. + virtual int reserveStream(IObase::DataType typeID,int rank,CONST int *dims); + virtual int writeStream(void *data,int length); + virtual int readStream(void *data,int length); + virtual int pause(); + virtual int resume(); + void bufferOn(long size=-1); + void bufferOff(); +}; +/* +int bcopy(char *src,char *dst,int len){ + // copying bytes on the DEC Alpha processor can be costly due + // to the penalty of non-aligned memory accesses. This tries + // to align the copys. + + You can use the program copyperf to see how bcopy() improves performance + You must build copyperf separately using + gmake copyperf + + The following will test copy a 64meg buffer 1 time. + copyperf 64m + The following will test copy a 32k buffer 1000 times + copyperf 32k 1000 + The performance differences are 4-to-1 or greater in favor of bcopy(). +} +*/ +int IEEEIO::write(const void *data,size_t size){ + //puts("***********write()"); +#ifdef T3E + static int ffio_inline_true=0; + if(ffio_inline_true >= 0){ + // do this test only once on first write + // this just gives someone a clue that they've done the + // wrong thing when they compiled +#ifdef FFIO + if(!ffioIsTrue()){ + fprintf(stderr,"Error!!! Inconsistent FFIO flags on a T3E!\n"); + fprintf(stderr,"Your libraries have been compiled without -DFFIO, but your executable has been compled and linked with them with -DFFIO.\n"); + fprintf(stderr,"\t*****Please either rebuild the IEEEIO library with -DFFIO or *remove* -DFFIO from your own build.******\n"); + } +#else + if(ffioIsTrue()){ + fprintf(stderr,"Error!!! Inconsistent FFIO flags on a T3E!\n"); + fprintf(stderr,"Your libraries have been compiled with -DFFIO, but your executable has been compled and linked with them without -DFFIO\n"); + fprintf(stderr,"\t*****Please recompile with -DFFIO******\n"); + } +#endif + ffio_inline_true = -1; + } +#endif + if(!writebuffer){ + register long r=0; +#ifdef FFIO + r = ::ffwrite(fid,data,size); +#else + r = ::write(fid,data,size); +#endif + if(r>0) actual_position += r; + virtual_position = actual_position; + // if(r>0) virtual_position = actual_position = (actual_position+r); + if(virtual_position>file_length) file_length = virtual_position; + return r; + } + // otherwise, do a buffered write + int retval=0; + char *datap = (char *)data; + if(writebuffercount>0){ + // copy as much as you can to the writebuffer + // should use bcopy... its a whole lot faster!!! + long copysize = (size>(writebuffersize-writebuffercount))?(writebuffersize-writebuffercount):size; + bcopy(datap,writebuffer+writebuffercount,copysize); + writebuffercount += copysize; + virtual_position += copysize; + retval += copysize; + if(writebuffercount>=writebuffersize) + this->flush(); // dump immediately (updates both virtual and actual pos) + size-=copysize; datap+=copysize; + if(size<=0) { + // virtual_position = actual_position + writebuffercount; + if(virtual_position>file_length) file_length=virtual_position; + return retval; + } + } + // at this point size>0 and writebuffercount=0 + if(size<=0 || writebuffercount!=0){ + printf("***Write Failed: size=%u writebuffercount=%u\n", + (unsigned int)size,writebuffercount); + } + if(size<writebuffersize){// store to buffer (size>0 is implicit in loop) + bcopy(datap,writebuffer+writebuffercount,size); + writebuffercount += size; + virtual_position += size; + retval += size; + } + else { + register long r=0; + // its larger than buffer, so bypass it and + // write it directly to the disk. +#ifdef FFIO + r = ::ffwrite(fid,datap,size); +#else + r = ::write(fid,datap,size); +#endif + actual_position += r; + virtual_position = actual_position; // essentially equiv to flush + if(r>0) retval+=r; + } + // double-check virtual position + // virtual_position = actual_position + writebuffercount; + if(virtual_position>file_length) file_length = virtual_position; + return retval; +} +void IEEEIO::flush(){ + register long r=0; + if(writebuffercount){ +#ifdef FFIO + r = ::ffwrite(fid,writebuffer,writebuffercount); +#else + r = ::write(fid,writebuffer,writebuffercount); +#endif + } + writebuffercount=0; + if((r+actual_position)!=virtual_position){ + fprintf(stderr,"IEEEIO::flush() : inconsistent file positions! r=%ld v=%ld a=%ld\n", + r,virtual_position,actual_position); + } + actual_position = virtual_position; + if(virtual_position>file_length) file_length=virtual_position; +} +int IEEEIO::lseek(long len, int direction){ + // compute new pos + register long npos=0; + // can seek beyond end of file + switch(direction){ + case L_XTND: /* should do special case for 0 len */ + npos = file_length + len; + break; + case L_SET: + npos = len; + break; + case L_INCR:; + npos = virtual_position + len; + break; + } + if(npos>file_length) + file_length = npos; + if(virtual_position != npos) { + if(writebuffer) + this->flush(); // must flush current buffer contents + // printf("Actually seeking from %ld to %ld\n",virtual_position,npos); + virtual_position = actual_position = npos; +#ifdef FFIO + // if(!writebuffer) + return ::ffseek(fid,npos,L_SET); +#else + // if(!writebuffer) + return ::lseek(fid,npos,L_SET); +#endif + } + return npos; // otherwise no change required +} +/* Will eventually want to buffer the reads as well... */ +int IEEEIO::read(void *data,size_t size){ + register int r=0; + if(writebuffercount>0) + flush(); +#ifdef FFIO + r = ::ffread(fid,data,size); +#else + r = ::read(fid,data,size); +#endif + if(r>0) virtual_position += r; + actual_position=virtual_position; + // can't read past end of file of course so no need to update file_length; + return r; +} + + +//==========F77 Interface============= +#define f_ieee_open F77NAME(ieee_open_,ieee_open,IEEE_OPEN) +#define f_ieee_openr F77NAME(ieee_openr_,ieee_openr,IEEE_OPENR) +#define f_ieee_openw F77NAME(ieee_openw_,ieee_openw,IEEE_OPENW) +#define f_ieee_opena F77NAME(ieee_opena_,ieee_opena,IEEE_OPENA) +#define f_ieee_bufon F77NAME(ieee_bufon_,ieee_bufon,IEEE_BUFON) +#define f_ieee_bufoff F77NAME(ieee_bufoff_,ieee_bufoff,IEEE_BUFOFF) +extern "C"{ +#ifdef CRAY // Note: This isn't really implemented yet... +#include <fortran.h> + Long8 f_ieee_open (_fcd fcfilename,_fcd fcaccessmode); + Long8 f_ieee_openr (_fcd fcfilename); + Long8 f_ieee_openw (_fcd fcfilename); + Long8 f_ieee_opena (_fcd fcfilename); +#else + Long8 f_ieee_open (char *filename,char *accessmode,int namelen,int accesslength); + Long8 f_ieee_openr (char *filename,int namelen); + Long8 f_ieee_openw (char *filename,int namelen); // actually IObase::Create + Long8 f_ieee_opena (char *filename,int namelen); +#endif + void f_ieee_bufon (Long8 *fileID,int *bufsize); + void f_ieee_bufoff (Long8 *fileID); + //=====================ANSI C interface +#include "IEEEIO.h" // ANSI C interface + } + +#endif diff --git a/src/IEEEIO/IO.cc b/src/IEEEIO/IO.cc new file mode 100644 index 0000000..e347f2a --- /dev/null +++ b/src/IEEEIO/IO.cc @@ -0,0 +1,371 @@ + +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include "IO.hh" + +IObase::IObase(CONST char *fname,AccessMode access):index(0),nitems(0),accessmode(access){ + strcpy(filename,fname); +} + +IObase::DataType IObase::Int2DataType(int dt){ + if(dt==Byte) + return Byte; + else if(dt==Int16) + return Int16; + else if(dt==Int32) + return Int32; + else if(dt==Int64) + return Int64; + else if(dt==Float32) + return Float32; + else if(dt==Float64) + return Float64; + else if(dt==uInt8) + return uInt8; + else if(dt==uInt16) + return uInt16; + else if(dt==uInt32) + return uInt32; + else if(dt==uInt64) + return uInt64; + else if(dt==Char8) + return Char8; + else if(dt==Char16) + return Char16; + else return Error; +} + +int IObase::sizeOf(IObase::DataType dt){ + switch(dt){ + case Int8: + case uInt8: + case Char8: + return 1; + case Int16: + case uInt16: + case Char16: + return 2; + case Int32: + case uInt32: + case Float32: + return 4; + case Int64: + case uInt64: + case Float64: + return 8; + default: + return 0; + } +} + +int IObase::nElements(int rank,CONST int *dims){ + int nelem,i; + for(i=0,nelem=1;i<rank;i++) nelem*=dims[i]; + return nelem; +} + +int IObase::nBytes(IObase::DataType dt, int rank,CONST int *dims){ + return nElements(rank,dims)*sizeOf(dt); +} + +int f_io_close(Long8 *deviceID){ + IObase *dev=(IObase*)(*deviceID); + delete dev; + *deviceID=0; + return 1; +} + +int f_io_isvalid(Long8 *deviceID){ + if(!deviceID || !*deviceID) return 0; + IObase *dev=(IObase*)(*deviceID); + return dev->isValid(); +} + +int f_io_sizeof(int *type){ + return IObase::sizeOf(IObase::Int2DataType(*type)); +} + +int f_io_nelements(int *rank,int *dims){ + return IObase::nElements(*rank,dims); +} + +int f_io_nbytes(int *type,int *rank,int *dims){ + return IObase::nBytes(IObase::Int2DataType(*type),*rank,dims); +} + +int f_io_write(Long8 *deviceID,int *typeID,int *rank,int *dims,void *data){ + IObase *dev=(IObase*)(*deviceID); + dev->write(dev->Int2DataType(*typeID),*rank,dims,data); + return 1; +} + +int f_io_readinfo(Long8 *deviceID,int *typeID,int *rank,int *dims,int *maxdims){ + IObase *dev=(IObase*)(*deviceID); + IObase::DataType tid; + if(*maxdims<0) *maxdims=0; + int r=dev->readInfo(tid,*rank,dims,*maxdims); + //int r=dev->readInfo(tid,*rank,dims); + *typeID=tid; + return r; +} + +int f_io_numdata(Long8 *deviceID){ + IObase *dev=(IObase*)(*deviceID); + return dev->nDatasets(); +} +int f_io_read(Long8 *deviceID,void *data){ + IObase *dev=(IObase*)(*deviceID); + return dev->read(data); +} + +int f_io_seek(Long8 *deviceID,int *dataset_index){ + IObase *dev=(IObase*)(*deviceID); + return dev->seek(*dataset_index); +} +//--------------Annotations +int f_io_writenote(Long8 *deviceID,char *annotation,int size){ + IObase *dev=(IObase*)(*deviceID); + annotation[size]='\0'; // Yeah! its unsafe I know..!! (but it works!) + return dev->writeAnnotation(annotation); +} + +int f_io_readnote(Long8 *deviceID,int *index,char *annotation,int maxsize){ + IObase *dev=(IObase*)(*deviceID); + return dev->readAnnotation(*index,annotation,maxsize); +} + +int f_io_noteinfo(Long8 *deviceID,int *index,int *length){ + IObase *dev=(IObase*)(*deviceID); + return dev->readAnnotationInfo(*index,*length); +} + +int f_io_numnote(Long8 *deviceID){ + IObase *dev=(IObase*)(*deviceID); + return dev->nAnnotations(); +} + +//---------------Attributes +int f_io_writeatt(Long8 *deviceID,char *name, + int *datatype,Long *nelements,void *data,int namesize){ + IObase *dev=(IObase*)(*deviceID); + name[namesize]='\0'; // cap name (to be certain).. unsafe but it works + // should copy into a flexarray which will be destroyed on return + IObase::DataType typeID = IObase::Int2DataType(*datatype); + return dev->writeAttribute(name,typeID,*nelements,data); +} + +int f_io_attinfo(Long8 *deviceID,char *name, + int *datatype,Long *nelements,int namesize){ + IObase *dev=(IObase*)(*deviceID); + IObase::DataType typeID; + if(namesize>0) + name[namesize]='\0'; + int i=dev->readAttributeInfo(name,typeID,*nelements); + *datatype=typeID; + return i; +} + +int f_io_iattinfo(Long8 *deviceID,int *index,char *name, + int *datatype,Long *nelements,int namesize){ + int i; + IObase *dev=(IObase*)(*deviceID); + IObase::DataType typeID; + for(i=0;i<namesize;i++) name[i]='\0'; + //printf("io_iattinfo(): Namesize=%u Name=[%s]\n",namesize,name); + i=dev->readAttributeInfo(*index,name,typeID,*nelements,namesize); + // need to zero the array + //printf("io_iattinfo(): Newname=[%s]\n",name); + *datatype=typeID; + //printf("io_iattinfo(): attribs are index=%u type=%u nelements=%u namesize=%u\n", + // *index,*datatype,*nelements,namesize); + return i; +} + +int f_io_readatt(Long8 *deviceID,int *number,void *data){ + IObase *dev=(IObase*)(*deviceID); + return dev->readAttribute(*number,data); +} + +int f_io_numatt(Long8 *deviceID){ + IObase *dev=(IObase*)(*deviceID); + return dev->nAttributes(); +} + +//==========F77 Chunking interface-------------------- +int f_io_reserveck(Long8 *deviceID,int *typeID,int *rank,int *dims){ + IObase *dev=(IObase*)(*deviceID); + return dev->reserveChunk(dev->Int2DataType(*typeID),*rank,dims); +} + +int f_io_writeck(Long8 *deviceID,int *chunkdims,int *chunkorigin,void *data){ + IObase *dev=(IObase*)(*deviceID); + return dev->writeChunk(chunkdims,chunkorigin,data); +} + +int f_io_readck(Long8 *deviceID,int *chunkdims,int *chunkorigin,void *data){ + IObase *dev=(IObase*)(*deviceID); + return dev->readChunk(chunkdims,chunkorigin,data); +} +int f_io_writestrm(IOFile deviceID,void *data,int *length){ + IObase *dev=(IObase*)(deviceID); + return dev->writeStream(data,*length); +} + +int f_io_readstrm(IOFile deviceID,void *data,int *length){ + IObase *dev=(IObase*)(deviceID); + return dev->readStream(data,*length); +} + +int f_io_pause(Long8 *deviceID){ + IObase *dev=(IObase*)(deviceID); + return dev->pause(); +} + +int f_io_resume(Long8 *deviceID){ + IObase *dev=(IObase*)(deviceID); + return dev->resume(); +} + +//====================C Interface======================== +int IOclose(IOFile deviceID){ + IObase *dev=(IObase*)(deviceID); + delete dev; + deviceID=0; + return 1; +} + +int IOisValid(IOFile deviceID){ + if(!deviceID) return 0; + IObase *dev=(IObase*)(deviceID); + return dev->isValid(); +} + +int IOsizeOf(int type){ + return IObase::sizeOf(IObase::Int2DataType(type)); +} + +int IOnElements(int rank,int *dims){ + return IObase::nElements(rank,dims); +} + +int IOnBytes(int type,int rank,int *dims){ + return IObase::nBytes(IObase::Int2DataType(type),rank,dims); +} + +int IOwrite(IOFile deviceID,int typeID,int rank,int *dims,void *data){ + IObase *dev=(IObase*)(deviceID); + return dev->write(dev->Int2DataType(typeID),rank,dims,data); +} + +int IOreadInfo(IOFile deviceID,int *typeID,int *rank,int *dims,int maxdims){ + IObase *dev=(IObase*)(deviceID); + IObase::DataType tid; + int r=dev->readInfo(tid,*rank,dims,maxdims); + *typeID=tid; + return r; +} + +int IOread(IOFile deviceID,void *data){ + IObase *dev=(IObase*)(deviceID); + return dev->read(data); +} + +int IOseek(IOFile deviceID,int dataset_index){ + IObase *dev=(IObase*)(deviceID); + return dev->seek(dataset_index); +} + +int IOnDatasets(IOFile deviceID){ + IObase *dev=(IObase*)(deviceID); + return dev->nDatasets(); +} + +int IOwriteAnnotation(IOFile deviceID,char *annotation){ + IObase *dev=(IObase*)(deviceID); + //annotation[size]='\0'; // Yeah! its unsafe I know..!! (but it works!) + return dev->writeAnnotation(annotation); +} + +int IOreadAnnotation(IOFile deviceID,int index,char *annotation,int maxsize){ + IObase *dev=(IObase*)(deviceID); + return dev->readAnnotation(index,annotation,maxsize); +} + +int IOreadAnnotationInfo(IOFile deviceID,int index,int *size){ + IObase *dev=(IObase*)(deviceID); + return dev->readAnnotationInfo(index,*size); +} + +int IOnAnnotations(IOFile deviceID){ + IObase *dev=(IObase*)(deviceID); + return dev->nAnnotations(); +} + +int IOwriteAttribute(IOFile deviceID,char *name,int type,Long length,void *data){ + IObase *dev=(IObase*)(deviceID); + IObase::DataType typeID = IObase::Int2DataType(type); + return dev->writeAttribute(name,typeID,length,data); +} + +int IOreadIndexedAttributeInfo(IOFile deviceID,int number,char *name, + int *type,Long *nelem,int maxnamelen){ + IObase *dev=(IObase*)(deviceID); + IObase::DataType typeID; + int i=dev->readAttributeInfo(number,name,typeID,*nelem,maxnamelen); + *type=typeID; // convert from enum + return i; +} + +int IOreadAttributeInfo(IOFile deviceID,char *name,int *type,Long *nelem){ + IObase *dev=(IObase*)(deviceID); + IObase::DataType typeID; + int i=dev->readAttributeInfo(name,typeID,*nelem); + *type=typeID; // convert from enum + return i; +} + +int IOreadAttribute(IOFile deviceID,int number,void *data){ + IObase *dev=(IObase*)(deviceID); + return dev->readAttribute(number,data); +} + +int IOnAttributes(IOFile deviceID){ + IObase *dev=(IObase*)(deviceID); + return dev->nAttributes(); +} + +//=============C Chunking interface--------------- +int IOreserveChunk(IOFile deviceID,int typeID,int rank,int *dims){ + IObase *dev=(IObase*)(deviceID); + return dev->reserveChunk(dev->Int2DataType(typeID),rank,dims); +} + +int IOwriteChunk(IOFile deviceID,int *chunkdims,int *chunkorigin,void *data){ + IObase *dev=(IObase*)(deviceID); + return dev->writeChunk(chunkdims,chunkorigin,data); +} + +int IOreadChunk(IOFile deviceID,int *chunkdims,int *chunkorigin,void *data){ + IObase *dev=(IObase*)(deviceID); + return dev->readChunk(chunkdims,chunkorigin,data); +} + +int IOwriteStream(IOFile deviceID,void *data,int length){ + IObase *dev=(IObase*)(deviceID); + return dev->writeStream(data,length); +} + +int IOreadStream(IOFile deviceID,void *data,int length){ + IObase *dev=(IObase*)(deviceID); + return dev->readStream(data,length); +} +int IOpause(IOFile deviceID){ + IObase *dev=(IObase*)(deviceID); + return dev->pause(); +} + +int IOresume(IOFile deviceID){ + IObase *dev=(IObase*)(deviceID); + return dev->resume(); +} diff --git a/src/IEEEIO/IO.h b/src/IEEEIO/IO.h new file mode 100644 index 0000000..1a17cd3 --- /dev/null +++ b/src/IEEEIO/IO.h @@ -0,0 +1,63 @@ +/* IO.h C header */ +#ifndef __IO_H_ +#define __IO_H_ +#ifndef UNITYPE +#define UNITYPE +#define BYTE 0 +#define INT8 0 +#define INT16 1 +#define INT32 2 +#define INT64 3 +#define FLOAT32 4 +#define FLOAT64 5 +#define UCHAR 6 +#define UINT8 6 +#define UINT16 7 +#define UINT32 8 +#define UINT64 9 +/* special string types */ +#define CHAR 10 +#define CHAR8 10 +#define STRING 10 +#define UNICODE 11 +#define CHAR16 11 +#endif +/* error */ +#define TYPEERROR -1 + +#define IOREAD 0 +#define IOWRITE 1 +#define IOAPPEND 2 +#define IOCREATE 1 /* Same as IOWRITE */ + +#include "Arch.h" + +int IOclose PROTO((IOFile deviceID)); +int IOisValid PROTO((IOFile deviceID)); +int IOsizeOf PROTO((int datatype)); +int IOnElements PROTO((int rank,int *dims)); +int IOnBytes PROTO((int datatype,int rank,int *dims)); +int IOwrite PROTO((IOFile deviceID,int typeID,int rank,int *dims,void *data)); +int IOreadInfo PROTO((IOFile deviceID,int *typeID,int *rank,int *dims,int maxdims)); +int IOread PROTO((IOFile deviceID,void *data)); +int IOseek PROTO((IOFile deviceID,int dataset_index)); +int IOnDatasets PROTO((IOFile deviceID)); +int IOwriteAnnotation PROTO((IOFile deviceID,char *annotation)); +int IOreadAnnotation PROTO((IOFile deviceID,int index,char *annotation,int maxsize)); +int IOreadAnnotationInfo PROTO((IOFile deviceID,int index,int *size)); +int IOnAnnotations PROTO((IOFile deviceID)); + +int IOwriteAttribute PROTO((IOFile deviceID,char *name,int type,Long length,void *data)); +int IOreadIndexedAttributeInfo PROTO((IOFile deviceID,int number, + char *name,int *type,Long *nelem,int maxnamelen)); +int IOreadAttributeInfo PROTO((IOFile deviceID,char *name,int *type,Long *nelem)); +int IOreadAttribute PROTO((IOFile deviceID,int number,void *data)); +int IOnAttributes PROTO((IOFile deviceID)); +int IOreserveChunk PROTO((IOFile deviceID,int typeID,int rank,int *dims)); +int IOwriteChunk PROTO((IOFile deviceID,int *chunkdims,int *chunkorigin,void *data)); +int IOreadChunk PROTO((IOFile deviceID,int *chunkdims,int *chunkorigin,void *data)); +int IOwriteStream PROTO((IOFile deviceID,void *data,int length)); +int IOreadStream PROTO((IOFile deviceID,void *data,int length)); +int IOpause PROTO((IOFile deviceID)); +int IOresume PROTO((IOFile deviceID)); +#endif diff --git a/src/IEEEIO/IO.hh b/src/IEEEIO/IO.hh new file mode 100644 index 0000000..443f2c9 --- /dev/null +++ b/src/IEEEIO/IO.hh @@ -0,0 +1,168 @@ +#ifndef __IO_HH_ +#define __IO_HH_ +#include "Arch.h" + +class IObase { +public: + //-----------------------Public Enums.................... + enum AccessMode {Read=0,Write=1,Create=1,Append=2}; + enum DataType {Byte=0,Int8=0,Int16=1,Int32=2,Int64=3, + Float32=4,Float64=5, + uInt8=6,uChar=6,uInt16=7,uInt32=8,uInt64=9, + Char=10,Char8=10,String=10,Unicode=11,Char16=11, // special string types + Error=-1}; +protected: + //-----------------------State variables................. + int index,nitems; + AccessMode accessmode; + char filename[128]; +public: + //------------------------core stuff..................... + IObase(CONST char *fname,AccessMode access); + virtual ~IObase() {} + virtual int isValid() { return 0; } + // could use overloading to differentiate type here... (but I'm going simple) + virtual int write(DataType typeID,int rank,CONST int *dims,void *data)=0; + virtual int readInfo(DataType &typeID,int &rank,int *dims,int maxdims=3)=0; + virtual int read(void *data)=0; + virtual int seek(int dataset_index)=0; + virtual int nDatasets()=0; + virtual int writeAnnotation(CONST char *annotation)=0; + virtual int readAnnotationInfo(int number,int &length)=0; // returns length (-1 if none left) + virtual int readAnnotation(int number,char *annotation,int maxsize=128)=0; + virtual int nAnnotations()=0; + + virtual int writeAttribute(CONST char *name,IObase::DataType typeID,Long length,void *data)=0; + // returns number + virtual int readAttributeInfo(int number,char *name,IObase::DataType &typeID,Long &nelem,int maxnamelen=128)=0; + virtual int readAttributeInfo(CONST char *name,IObase::DataType &typeID,Long &nelem)=0; // returns number + virtual int readAttribute(int number,void *data)=0; + // virtual Long readAttribute(char *name,void *data); + virtual int nAttributes()=0; + + //-----------------Chunking Features (for MPI/HPF)................ + virtual int reserveChunk(IObase::DataType typeID,int rank,CONST int *dims)=0; + + virtual int writeChunk(CONST int *chunkdims,CONST int *chunkorigin,void *data)=0; + // virtual int writeStridedChunk()=0; + // virtual int writeHPF(int processorID,int *proclayout,IObase::HPFlayout *arraylayout,void *data)=0; + virtual int readChunk(CONST int *chunkdims,CONST int *chunkorigin,void *data)=0; + // virtual int readStrided()=0; + // virtual int readHPF(int processorID,int *proclayout,IObase::HPFlayout *arraylayout,void *data)=0; + //-----------------Streaming interface (for PANDA, Sockets & MPIO)......... + virtual int reserveStream(IObase::DataType typeID,int rank,CONST int *dims)=0; + virtual int writeStream(void *data,int length)=0; + virtual int readStream(void *data,int length)=0; + //-----------------------Utilities........................ + // unfortunately you can cast enums to int but not the reverse + static DataType Int2DataType(int dt); + static int sizeOf(DataType dt); + static int nElements(int rank,CONST int *dims); // returns number of elements in dataset + static int nBytes(DataType dt,int rank,CONST int *dims); // returns number of bytes (good for malloc) + CONST char *name() { return filename; } + virtual int pause() { return 0; } + virtual int resume() { return 0; } +}; + +//===================Fortran77 Interface +#define f_io_close F77NAME(io_close_,io_close,IO_CLOSE) +#define f_io_isvalid F77NAME(io_isvalid_,io_isvalid,IO_ISVALID) +#define f_io_sizeof F77NAME(io_sizeof_,io_sizeof,IO_SIZEOF) +#define f_io_nbytes F77NAME(io_nbytes_,io_nbytes,IO_NBYTES) +#define f_io_nelements F77NAME(io_nelements_,io_nelements,IO_NELEMENTS) +#define f_io_write F77NAME(io_write_,io_write,IO_WRITE) +#define f_io_readinfo F77NAME(io_readinfo_,io_readinfo,IO_READINFO) +#define f_io_read F77NAME(io_read_,io_read,IO_READ) +#define f_io_seek F77NAME(io_seek_,io_seek,IO_SEEK) +#define f_io_numdata F77NAME(io_numdata_,io_numdata,IO_NUMDATA) +#define f_io_writenote F77NAME(io_writenote_,io_writenote,IO_WRITENOTE) +#define f_io_readnote F77NAME(io_readnote_,io_readnote,IO_READNOTE) +#define f_io_numnote F77NAME(io_numnote_,io_numnote,IO_NUMNOTE) +#define f_io_writeatt F77NAME(io_writeatt_,io_writeatt,IO_WRITEATT) +#define f_io_attinfo F77NAME(io_attinfo_,io_attinfo,IO_ATTINFO) +#define f_io_iattinfo F77NAME(io_iattinfo_,io_iattinfo,IO_IATTINFO) +#define f_io_readatt F77NAME(io_readatt_,io_readatt,IO_READATT) +#define f_io_numatt F77NAME(io_numatt_,io_numatt,IO_NUMATT) +#define f_io_reserveck F77NAME(io_reserveck_,io_reserveck,IO_RESERVECK) +#define f_io_writeck F77NAME(io_writeck_,io_writeck,IO_WRITECK) +#define f_io_readck F77NAME(io_readck_,io_readck,IO_READCK) +#define f_io_writestrm F77NAME(io_writestrm_,io_writestrm,IO_WRITESTRM) +#define f_io_readstrm F77NAME(io_readstrm_,io_readstrm,IO_READSTRM) +#define f_io_pause F77NAME(io_pause_,io_pause,IO_PAUSE) +#define f_io_resume F77NAME(io_resume_,io_resume,IO_RESUME) +extern "C"{ +//================Ansi C interface +#include "IO.h" +//==== f77 interface +#ifdef CRAY + // Cray fortran uses an _fcd data structure to pass strings. This is in preparation + // for using this on Crays, but since nobody is using them yet, I've left the code for + // these versions of the routines blank. It'll be filled in later if the need arises +#include <fortran.h> + int f_io_close (Long8 *deviceID); + int f_io_isvalid (Long8 *deviceID); + int f_io_sizeof (int *datatype); + int f_io_nelements (int *rank,int *dims); + int f_io_nbytes (int *datatype,int *rank,int *dims); + int f_io_write (Long8 *deviceID,int *typeID,int *rank,int *dims,void *data); + int f_io_readinfo (Long8 *deviceID,int *typeID,int *rank,int *dims,int *maxdims); + int f_io_read (Long8 *deviceID,void *data); + int f_io_seek (Long8 *deviceID,int *dataset_index); + int f_io_numdata (Long8 *deviceID); + int f_io_writenote (Long8 *deviceID,_fcd fcannotation); + int f_io_noteinfo (Long8 *deviceID,int *number,int *length); + int f_io_readnote (Long8 *deviceID,int *number,_fcd fcannotation); + int f_io_numnote (Long8 *deviceID); + + int f_io_writeatt (Long8 *deviceID,_fcd fcname, + int *datatype,Long *nelements,void *data); + int f_io_attinfo (Long8 *deviceID,_fcd fcname, + int *datatype,Long *nelements); + int f_io_iattinfo (Long8 *deviceID,int *index,_fcd fcname, + int *datatype,Long *nelements); + int f_io_readatt (Long8 *deviceID,int *number,void *data); + int f_io_numatt (Long8 *deviceID); + int f_io_reserveck(Long8 *deviceID,int *typeID,int *rank,int *dims); + int f_io_writeck(Long8 *deviceID,int *chunkdims,int *chunkorigin,void *data); + int f_io_readck(Long8 *deviceID,int *chunkdims,int *chunkorigin,void *data); + int f_io_pause(Long8 *deviceID); + int f_io_resume(Long8 *deviceID); +#else + int f_io_close (Long8 *deviceID); + int f_io_isvalid (Long8 *deviceID); + int f_io_sizeof (int *datatype); + int f_io_nelements (int *rank,int *dims); + int f_io_nbytes (int *datatype,int *rank,int *dims); + int f_io_write (Long8 *deviceID,int *typeID,int *rank,int *dims,void *data); + int f_io_readinfo (Long8 *deviceID,int *typeID,int *rank,int *dims,int *maxdims); + int f_io_read (Long8 *deviceID,void *data); + int f_io_seek (Long8 *deviceID,int *dataset_index); + int f_io_numdata (Long8 *deviceID); + + int f_io_writenote (Long8 *deviceID,char *annotation,int size); + int f_io_noteinfo (Long8 *deviceID,int *number,int length); + int f_io_readnote (Long8 *deviceID,int *number,char *annotation,int maxsize); + int f_io_numnote (Long8 *deviceID); + + int f_io_writeatt (Long8 *deviceID,char *name, + int *datatype,Long *nelements,void *data,int namesize); + int f_io_attinfo (Long8 *deviceID,char *name, + int *datatype,Long *nelements,int namesize); + int f_io_iattinfo (Long8 *deviceID,int *index,char *name, + int *datatype,Long *nelements,int namesize); + int f_io_readatt (Long8 *deviceID,int *number,void *data); + int f_io_numatt (Long8 *deviceID); + int f_io_reserveck(Long8 *deviceID,int *typeID,int *rank,int *dims); + int f_io_writeck(Long8 *deviceID,int *chunkdims,int *chunkorigin,void *data); + int f_io_readck(Long8 *deviceID,int *chunkdims,int *chunkorigin,void *data); + int f_io_writestrm(Long8 *deviceID,void *data,int *length); + int f_io_readstrm(Long8 *deviceID,void *data,int *length); + int f_io_pause(Long8 *deviceID); + int f_io_resume(Long8 *deviceID); +#endif +} + +#endif + + + diff --git a/src/IEEEIO/make.code.defn b/src/IEEEIO/make.code.defn new file mode 100644 index 0000000..c945b9e --- /dev/null +++ b/src/IEEEIO/make.code.defn @@ -0,0 +1,84 @@ +# Main make.code.defn file for thorn FlexIO +# $Header$ + +# Source files in this directory +SRCS = IO.cc IEEEIO.cc + +# Subdirectories containing source files +SUBDIRS = + +# Work out some thorn specific compilation flags +# These are stripped down lines from the original IEEEIO makefile + +# The 9000 names of the cygwin tools and T3E... +TMPUN := $(shell uname) +ifeq ($(TMPUN), CYGWIN32_95) +UNAME = CYGWIN +else +ifeq ($(TMPUN), CYGWIN32_NT) +UNAME = CYGWIN +else +ifeq ($(TMPUN), CYGWIN_NT-4.0) +UNAME = CYGWIN +else +UNAME := $(shell uname | perl -pe 's/(sn\d\d\d\d|jsimpson)/UNICOS\/mk/') +endif +endif +endif + +# 64 Bit Irix +ifeq ($(UNAME), IRIX64) + +CXXFLAGS += -DANSI -ptused + +endif + +# 32 Bit Irix +ifeq ($(UNAME), IRIX) + +CXXFLAGS += -DANSI -ptused + +endif + +# HP +ifeq ($(UNAME), HP-UX) + +CXXFLAGS += -DANSI -DHP +a1 + +endif + +# Alpha +ifeq ($(UNAME), OSF1) + +CXXFLAGS += -DANSI + +endif + +# Linux +ifeq ($(UNAME), Linux) + +CXXFLAGS += -DANSI + +endif + +# Macintosh /PowerMach-MachTen +ifeq ($(UNAME), machten) + +CXXFLAGS += -DANSI + +endif + +# Cygwin / Win32 +ifeq ($(UNAME), CYGWIN) + +CXXFLAGS += -DANSI -DWIN32 + +endif + +# T3E +ifeq ($(UNAME), UNICOS/mk) + +CXXFLAGS += -DANSI -DT3E -hinstantiate=used + +endif + diff --git a/src/Output2D.c b/src/Output2D.c new file mode 100644 index 0000000..23b1298 --- /dev/null +++ b/src/Output2D.c @@ -0,0 +1,283 @@ + /*@@ + @file Output2D.c + @date Tue Jan 9 1999 + @author Gabrielle Allen + @desc + Functions to deal 2D output of GFs + @enddesc + @history + @hauthor Thomas Radke @hdate 16 Mar 1999 + @hdesc Converted to Cactus 4.0 + @hendhistory + @@*/ + +#include <stdio.h> +#include <string.h> + +#include "flesh.h" +#include "Groups.h" +#include "declare_parameters.h" +#include "CactusBase/IOUtil/src/ioGH.h" +#include "ioFlexGH.h" + + +int IOFlexIO_Output2DVarAs (cGH *GH, const char *var, const char *alias); +int IOFlexIO_TimeFor2D (cGH *GH, int index); + +/*@@ + @routine IOFlexIO_Output2DGH + @date Sat March 6 1999 + @author Gabrielle Allen + @desc + Loops over all variables and outputs them if necessary + @enddesc + @calls CCTK_GetGHExtensionHandle + CCTK_GetNumVars + CCTK_GetImplementationFromVar + CCTK_GetVarName + IOFlexIO_TimeFor2D + IOFlexIO_Output2DVarAs + @calledby + @history + + @endhistory + @var GH + @vdesc Pointer to CCTK GH + @vtype cGH + @vio in + @vcomment + @endvar +@@*/ + +int IOFlexIO_Output2DGH (cGH *GH) +{ + DECLARE_PARAMETERS + int i; + flexioGH *myGH; + char *name, *fullname; + + /* Get the GH extension for IOFlexIO */ + myGH = (flexioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IOFlexIO")]; + + /* Loop over all variables */ + for (i = 0; i < CCTK_GetNumVars (); i++) { + + if (IOFlexIO_TimeFor2D (GH, i)) { + + name = CCTK_GetVarName (i); + fullname = CCTK_GetFullName (i); + + if (IO_verbose) { + printf("IOFlexIO Output2DGH : \n"); + printf(" fullname/name = %s/%s\n", fullname, name); + } + + IOFlexIO_Output2DVarAs (GH, fullname, name); + + free (fullname); + + /* Register another 2D output for this GF */ + myGH->IO_2Dnum[i]++; + + /* Register GF as having 2D output this iteration */ + myGH->IO_2Dlast[i] = GH->iteration; + } + } + + return (0); +} + + +/*@@ + @routine IOFlexIO_Output2DVarAs + @date Sat March 6 1999 + @author Gabrielle Allen + @desc + unconditional output of a variable using the IO 2D output method + @enddesc + @calls CCTK_DecomposeName + CCTK_GetVarIndex + CCTK_GetGHExtensionHandle + IOFlexIO_Write2D + @calledby IOFlexIO_Output2DGH + @history + + @endhistory + @var GH + @vdesc Pointer to CCTK GH + @vtype cGH + @vio in + @vcomment + @endvar + @var fullname + @vdesc complete name of variable to output + @vtype const char * + @vio in + @vcomment + @endvar + @var alias + @vdesc alias name of variable to output (used to generate output filename) + @vtype const char * + @vio in + @vcomment + @endvar +@@*/ + +int IOFlexIO_Output2DVarAs (cGH *GH, const char *fullname, const char *alias) +{ + DECLARE_PARAMETERS + int index, first; + flexioGH *myGH; + + index = CCTK_GetVarIndex (fullname); + + /* get the handle for IOFlexIO extensions */ + myGH = (flexioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IOFlexIO")]; + + /* Output with correct file opening behaviour */ + first = (myGH->IO_2Dnum [index] == 1); + + if (IO_verbose) { + printf ("\nIn IOFlexIO Output2DVarAs\n-------------------\n"); + printf (" Fullname = -%s-\n", fullname); + printf (" Alias = -%s-\n", alias); + printf (" Index = %d\n", index); + } + + /* Do the 2D output */ + IOFlexIO_Write2D (GH, index, alias, first); + + return (0); +} + + + /*@@ + @routine IOFlexIO_TimeFor2D + @date Sat March 6 1999 + @author Gabrielle Allen + @desc + Decides if it is time to output a variable using the IO 2D output + method + @enddesc + @calls CCTK_GetGHExtensionHandle + CCTK_GetVarGType + CCTK_WARN + CCTK_QueryGroupStorage_ByIndex + CCTK_GetGroupNameFromVar_ByIndex + @calledby IOFlexIO_Output2DGH + @history + + @endhistory + @var GH + @vdesc Pointer to CCTK GH + @vtype cGH + @vio in + @vcomment + @endvar + @var index + @vdesc index of variable + @vtype int + @vio in + @vcomment + @endvar +@@*/ + + +int IOFlexIO_TimeFor2D (cGH *GH, int index) +{ + ioGH *ioUtilGH; + flexioGH *myGH; + + /* Get the GH extension for IOUtil and IOFlexIO */ + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + myGH = (flexioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IOFlexIO")]; + + /* Check this GF should be output */ + if (! (CCTK_GetVarGType (index) == GROUP_GF && myGH->IO_2Dnum [index] != 0 + && GH->iteration % ioUtilGH->IO_2Devery == 0)) + return (0); + + /* Check GF not already output this iteration */ + if (myGH->IO_2Dlast [index] == GH->iteration) { + CCTK_WARN (2, "Already done 2D output in IOFlexIO"); + return (0); + } + + /* Check GF has storage */ + if (! CCTK_QueryGroupStorage_ByIndex (GH, + CCTK_GetGroupIndexFromVar_ByIndex(index))) { + char *fullname = CCTK_GetFullName (index); + char *msg = (char *) malloc (80 + strlen (fullname)); + + sprintf (msg, "No IOFlexIO 2D output for '%s' (no storage)", fullname); + CCTK_WARN (2, msg); + free (fullname); + free (msg); + return (0); + } + + return (1); +} + + +/*@@ + @routine IOFlexIO_TriggerOutput2D + @date Sat March 6 1999 + @author Gabrielle Allen + @desc + Triggers the output a variable using the IO 2D output + method + @enddesc + @calls CCTK_GetGHExtensionHandle + CCTK_GetVarName + IOFlexIO_Write2D + @calledby + @history + + @endhistory + @var GH + @vdesc Pointer to CCTK GH + @vtype cGH + @vio in + @vcomment + @endvar + @var index + @vdesc index of variable to output + @vtype int + @vio in + @vcomment + @endvar +@@*/ + +int IOFlexIO_TriggerOutput2D (cGH *GH, int index) +{ + DECLARE_PARAMETERS + int first; + flexioGH *myGH; + char *varname; + + varname = CCTK_GetVarName (index); + + /* get the handle for IOFlexIO extensions */ + myGH = (flexioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IOFlexIO")]; + + /* Output with correct file opening behaviour */ + first = (myGH->IO_2Dnum [index] == 1); + + if (IO_verbose) { + printf("\nIn IOFlexIO TriggerOutput2D\n---------------------\n"); + printf(" Index = %d\n", index); + printf(" Variable = -%s-\n", varname); + } + + /* Do the 2D output */ + IOFlexIO_Write2D (GH, index, varname, first); + + /* Register another 2D output for this GF */ + myGH->IO_2Dnum [index]++; + + /* Register GF as having 2D output this iteration */ + myGH->IO_2Dlast [index] = GH->iteration; + + return (0); +} diff --git a/src/Output3D.c b/src/Output3D.c new file mode 100644 index 0000000..64f0b1a --- /dev/null +++ b/src/Output3D.c @@ -0,0 +1,285 @@ + /*@@ + @file Output3D.c + @date Tue Jan 9 1999 + @author Gabrielle Allen + @desc + Functions to deal 3D output of GFs + @enddesc + @history + @hauthor Thomas Radke @hdate 16 Mar 1999 + @hdesc Converted to Cactus 4.0 + @hendhistory + @@*/ + +#include <stdio.h> +#include <assert.h> + +#include "flesh.h" +#include "Groups.h" +#include "declare_parameters.h" +#include "CactusBase/IOUtil/src/ioGH.h" +#include "ioFlexGH.h" + +int IOFlexIO_Output3DVarAs (cGH *GH, const char *var, const char *alias); +int IOFlexIO_TimeFor3D (cGH *GH, int index); + +/*@@ + @routine IOFlexIO_Output3DGH + @date Sat March 6 1999 + @author Gabrielle Allen + @desc + Loops over all variables and outputs them if necessary + @enddesc + @calls CCTK_GetGHExtensionHandle + CCTK_GetNumVars + CCTK_GetImplementationFromVar + CCTK_GetVarName + IOFlexIO_TimeFor3D + IOFlexIO_Output3DVarAs + @calledby + @history + + @endhistory + @var GH + @vdesc Pointer to CCTK GH + @vtype cGH + @vio in + @vcomment + @endvar +@@*/ + +int IOFlexIO_Output3DGH (cGH *GH) +{ + int i; + flexioGH *myGH; + char *implementation; + char *name; + char *fullname; + DECLARE_PARAMETERS + + /* Get the GH extension for IOFlexIO */ + myGH = (flexioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IOFlexIO")]; + + /* Loop over all variables */ + for (i = 0; i < CCTK_GetNumVars (); i++) { + if (IOFlexIO_TimeFor3D (GH, i)) { + implementation = CCTK_GetImplementationFromVar (i); + name = CCTK_GetVarName (i); + fullname = (char *) malloc (strlen (implementation) + + strlen (name) + 3); + assert (fullname); + sprintf (fullname, "%s::%s", implementation, name); + + if (IO_verbose) { + printf ("IOFlexIO Output3DGH : \n"); + printf (" fullname/name = %s/%s\n", fullname, name); + } + + IOFlexIO_Output3DVarAs (GH, fullname, name); + + free (fullname); + + /* Register another 3D output for this GF */ + myGH->IO_3Dnum [i]++; + + /* Register GF as having 3D output this iteration */ + myGH->IO_3Dlast [i] = GH->iteration; + } + } + + return (0); +} + + +/*@@ + @routine IOFlexIO_Output3DVarAs + @date Sat March 6 1999 + @author Gabrielle Allen + @desc + unconditional output of a variable using the IOFlexIO 3D output method + @enddesc + @calls CCTK_DecomposeName + CCTK_GetVarIndex + CCTK_GetGHExtensionHandle + IOFlexIO_Write3D + @calledby IOFlexIO_Output3DGH + @history + + @endhistory + @var GH + @vdesc Pointer to CCTK GH + @vtype cGH + @vio in + @vcomment + @endvar + @var fullname + @vdesc complete name of variable to output + @vtype const char * + @vio in + @vcomment + @endvar + @var alias + @vdesc alias name of variable to output (used to generate output filename) + @vtype const char * + @vio in + @vcomment + @endvar +@@*/ + +int IOFlexIO_Output3DVarAs (cGH *GH, const char *fullname, const char *alias) +{ + DECLARE_PARAMETERS + int index, first; + flexioGH *myGH; + + index = CCTK_GetVarIndex(fullname); + + /* Get the GH extension for IOFlexIO */ + myGH = (flexioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IOFlexIO")]; + + /* Output with correct file opening behaviour */ + first = (myGH->IO_3Dnum [index] == 1); + + if (IO_verbose) { + printf ("\nIn IOFlexIO Output3DVarAs\n-------------------\n"); + printf (" Fullname = -%s-\n", fullname); + printf (" Alias = -%s-\n", alias); + printf (" Index = %d\n", index); + } + + /* Do the 3D output */ + IOFlexIO_Write3D (GH, index, alias, first); + + return (0); +} + + +/*@@ + @routine IOFlexIO_TimeFor3D + @date Sat March 6 1999 + @author Gabrielle Allen + @desc + Decides if it is time to output a variable using the IOFlexIO 3D output + method + @enddesc + @calls CCTK_GetGHExtensionHandle + CCTK_GetVarGType + CCTK_WARN + CCTK_QueryGroupStorage_ByIndex + CCTK_GetGroupNameFromVar_ByIndex + @calledby IOFlexIO_Output3DGH + @history + + @endhistory + @var GH + @vdesc Pointer to CCTK GH + @vtype cGH + @vio in + @vcomment + @endvar + @var index + @vdesc index of variable + @vtype int + @vio in + @vcomment + @endvar +@@*/ + +int IOFlexIO_TimeFor3D (cGH *GH, int index) +{ + ioGH *ioUtilGH; + flexioGH *myGH; + + /* Get the GH extension for IOUtil and IOFlexIO */ + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + myGH = (flexioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IOFlexIO")]; + + /* Check this GF should be output */ + if (! (myGH->IO_3Dnum [index] != 0 && + GH->iteration % ioUtilGH->IO_3Devery == 0)) + return (0); + + /* Check GF not already output this iteration */ + if (myGH->IO_3Dlast [index] == GH->iteration) { + CCTK_WARN (2, "Already done 3D output in IOFlexIO"); + return (0); + } + + /* Check GF has storage */ + if (! CCTK_QueryGroupStorage_ByIndex (GH, + CCTK_GetGroupIndexFromVar_ByIndex(index))) { + char *fullname = CCTK_GetFullName (index); + char *msg = (char *) malloc (80 + strlen (fullname)); + + sprintf (msg, "No IOFlexIO 3D output for '%s' (no storage)", fullname); + CCTK_WARN (2, msg); + free (fullname); + free (msg); + return (0); + } + + return (1); +} + + +/*@@ + @routine IOFlexIO_TriggerOutput3D + @date Sat March 6 1999 + @author Gabrielle Allen + @desc + Triggers the output a variable using the IOFlexIO 3D output + method + @enddesc + @calls CCTK_GetGHExtensionHandle + CCTK_GetVarName + IOFlexIO_Write3D + @calledby + @history + + @endhistory + @var GH + @vdesc Pointer to CCTK GH + @vtype cGH + @vio in + @vcomment + @endvar + @var index + @vdesc index of variable to output + @vtype int + @vio in + @vcomment + @endvar +@@*/ + +int IOFlexIO_TriggerOutput3D (cGH *GH, int index) +{ + DECLARE_PARAMETERS + int first; + flexioGH *myGH; + char *varname; + + varname = CCTK_GetVarName (index); + + /* Get the GH extension for IOFlexIO */ + myGH = (flexioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IOFlexIO")]; + + /* Output with correct file opening behaviour */ + first = (myGH->IO_3Dnum [index] == 1); + + if (IO_verbose) { + printf("\nIn IOFlexIO TriggerOutput3D\n---------------------\n"); + printf(" Index = %d\n", index); + printf(" Variable = -%s-\n", varname); + } + + /* Do the 3D output */ + IOFlexIO_Write3D (GH, index, varname, first); + + /* Register another 3D output for this GF */ + myGH->IO_3Dnum [index]++; + + /* Register GF as having 3D output this iteration */ + myGH->IO_3Dlast [index] = GH->iteration; + + return (0); +} diff --git a/src/RecoverGH.c b/src/RecoverGH.c new file mode 100644 index 0000000..0391b30 --- /dev/null +++ b/src/RecoverGH.c @@ -0,0 +1,343 @@ + /*@@ + @file RecoverGH.c + @date Fri Jun 19 09:14:22 1998 + @author Tom Goodale + @desc + Contains the routines to do the internal checkpoint recovery. + + Currently can recover from: + (1) One file containing recombined data + (2) Multiple unrecombined files, where the current + number of processors and outputing processors + match those used to write the data. + @enddesc + @history + @hauthor Gabrielle Allen @hdate 19 Oct 1998 + @hdesc Changed names ready for thorn_IO + @endhistory + @version $Id$ + @@*/ + +static char *rcsid = "$Id$"; + +#include <stdio.h> + +#include "cctk.h" +#include "flesh.h" +#include "declare_parameters.h" +#include "GHExtensions.h" +#include "WarnLevel.h" +#include "Comm.h" +#ifdef CACTUSBASE_PUGH +#include "CactusBase/pugh/src/include/pugh.h" +#endif +#include "CactusBase/IOUtil/src/ioGH.h" +#include "ioFlexGH.h" + + +/* this one comes from RestoreFile.c */ +int IOFlexIO_RestoreIEEEIOfile (cGH *GH, IOFile ifp, + int IOrecover_ioproc, + int IOrecover_ioproc_every, + int IOrecover_unchunked); + + + /*@@ + @routine IOFlexIO_RecoverGH + @date Fri Jun 19 09:22:52 1998 + @author Tom Goodale + @desc + Recovers a GH. + @enddesc + @calls IOUtil_PrepareFilename IOFlexIO_RestoreIEEEIOfile + @calledby + @history + @hauthor Gabrielle Allen + @hdate Thu Jul 2 18:17:59 1998 + @hdesc Restore the physical time and iteration count, pass myproc to + IEEEIOparamRestore + Derives the filename from IOUtil_PrepareFilename (in thorn IOUtil) + @hauthor Gabrielle Allen @hdate Oct 17 1998 + @hdesc Added input of (some) GH structure variables + @endhistory + +@@*/ + +int IOFlexIO_RecoverGH (cGH *GH, const char *basename, int called_from) +{ +#ifdef CACTUSBASE_PUGH + + DECLARE_PARAMETERS + IOFile ifp; + char ftmp [1024], fname [1024]; + int proc, nprocs, myproc; + int index; + int is_IEEEIO_file; + Long nels_stored; + int nt_stored; + int iteration_stored; + int file_ioproc, file_ioproc_every; + int file_nprocs, file_unchunked; + CCTK_INT4 tmpInt; + char msg [512]; + pGH *pughGH; + ioGH *ioUtilGH; + cTimer total_time, dataset_time, param_time; +#ifdef MPI + CCTK_INT info [3]; +#endif + + + /* Get the handles for PUGH and IOUtil extensions */ + pughGH = (pGH *) GH->extensions [CCTK_GetGHExtensionHandle ("PUGH")]; + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + + /* identify myself */ + nprocs = CCTK_GetnProcs (GH); + myproc = CCTK_GetMyProc (GH); + + /* initialize timers */ + CactusResetTimer (&total_time); + CactusResetTimer (&dataset_time); + CactusResetTimer (¶m_time); + + CactusStartTimer (&total_time); + + /* Examine base file to find whether recovering from + * one (recombined) file or from multiple files + */ + + if (myproc == 0) { + + /* Determine name of base file + NOTE: As we don't know whether the file is chunked or not + we need to try both file names. */ + /* at first try with current chunking mode */ + file_unchunked = ioUtilGH->unchunked; + IOUtil_PrepareFilename(GH, basename, fname, called_from, 0, file_unchunked); + if (called_from == CP_RECOVER_DATA) + strcat (fname, ".chkpt"); + strcat (fname, ".ieee"); + + if (IO_verbose) + printf ("Opening file %s\n", fname); + + /* Open file, make sure the file is valid */ + ifp = IEEEopen (fname, "r"); + if (IOisValid (ifp)) + is_IEEEIO_file = 1; + else { + if (IO_verbose) + printf ("Cannot open file '%s'\n", fname); + + /* now try with the other chunking mode */ + file_unchunked = ! ioUtilGH->unchunked; + IOUtil_PrepareFilename (GH, basename, fname, called_from, + 0, file_unchunked); + if (called_from == CP_RECOVER_DATA) + strcat (fname, ".chkpt"); + strcat (fname, ".ieee"); + if (IO_verbose) + printf ("Trying now file '%s'...\n", fname); + + /* Open file, make sure the file is valid */ + ifp = IEEEopen (fname, "r"); + is_IEEEIO_file = IOisValid (ifp); + } + } + + if (myproc == 0 && is_IEEEIO_file) { + /* Now determine how the data was written */ + + /* Read nioprocs used to write data */ + index = IOreadAttributeInfo (ifp, "GH$ioproc_every", &nt_stored, &nels_stored); + if (index >= 0 && nt_stored == FLEXIO_INT4 && nels_stored == 1) { + IOreadAttribute (ifp, index, &tmpInt); + file_ioproc_every = tmpInt; + } else { + CCTK_WARN (1, "Unable to restore GH$ioproc_every. " + "Assuming it is nprocs and continuing"); + file_ioproc_every = nprocs; + } + + /* Read nprocs used to write data */ + index = IOreadAttributeInfo (ifp, "GH$nprocs", &nt_stored, &nels_stored); + if (index >= 0 && nt_stored == FLEXIO_INT4 && nels_stored == 1) { + IOreadAttribute (ifp, index, &tmpInt); + file_nprocs = tmpInt; + } else { + CCTK_WARN (1, "Unable to restore GH$nprocs. " + "Assuming it is 1 and continuing"); + file_nprocs = 1; + } + + /* Determine whether data is chunked or unchunked + We could derive this from the filename itself but just to be sure ... */ + index = IOreadAttributeInfo (ifp, "unchunked", &nt_stored, &nels_stored); + if (index >= 0 && nt_stored == FLEXIO_INT4 && nels_stored == 1) { + IOreadAttribute (ifp, index, &tmpInt); + file_unchunked = tmpInt; + } else { + sprintf (msg, "Unable to restore 'unchunked' attribute. " + "Assuming it is %s and continuing", + file_unchunked ? "true" : "false"); + CCTK_WARN (1, msg); + } + + /* If we restore from multiple files + * the number of processors must match. + */ + + if (file_ioproc_every == nprocs || file_unchunked) { + if (IO_verbose) + printf ("Recovering from one %s file\n", + file_unchunked ? "unchunked" : "chunked"); + } else { + if (file_nprocs != nprocs) { + sprintf (msg, "Must restart on %d processors with multiple files " + "or recombine them", file_nprocs); + CCTK_WARN (0, msg); + } + if (IO_verbose) + printf ("Recovering from %d chunked files\n", + nprocs / file_ioproc_every + (nprocs % file_ioproc_every?1:0)); + } + } + +#ifdef MPI + /* Broadcast chunking mode to all processors from processor zero */ + info [0] = is_IEEEIO_file; + info [1] = unchunked; + info [2] = file_ioproc_every; + CACTUS_MPI_ERROR (MPI_Bcast (info, 3, PUGH_MPI_INT, 0, + pughGH->PUGH_COMM_WORLD)); + is_IEEEIO_file = info [0]; + unchunked = info [1]; + file_ioproc_every = info [2]; +#endif + + /* return here to IOUtil if no valid file could be found */ + if (! is_IEEEIO_file) { + if (myproc == 0) { + sprintf (msg, "No valid IEEEIO file '%s' found !", fname); + CCTK_WARN (2, msg); + } + return (-1); + } + + /* Determine the IO processors for each node and the corresponding + checkpoint file */ + file_ioproc = myproc - (myproc % file_ioproc_every); + IOUtil_PrepareFilename (GH, basename, fname, called_from, + file_ioproc/file_ioproc_every, file_unchunked); + if (called_from == CP_RECOVER_DATA) + strcat (fname, ".chkpt"); + strcat (fname, ".ieee"); + + /* Open chunked files on other IO processors */ + if (myproc != 0 && myproc == file_ioproc) { + + if (IO_verbose) + printf ("Opening chunked file '%s' on processor %d.\n", + fname, myproc); + + /* Open file, make sure the file is valid */ + ifp = IEEEopen (fname, "r"); + if (! IOisValid (ifp)) { + sprintf (msg, "Cannot open file '%s' on processor %d", + fname, myproc); + CCTK_WARN (0, msg); + } + } + + /* Restore the data */ + if (IO_verbose && myproc == 0) + printf ("Recovering %schunked data with ioproc %d, ioproc_every %d.\n", + file_unchunked ? "un" : "", file_ioproc, file_ioproc_every); + + CactusStartTimer (&dataset_time); + IOFlexIO_RestoreIEEEIOfile (GH, ifp, file_ioproc, file_ioproc_every, + file_unchunked); + CactusStopTimer (&dataset_time); + + /* Close the file. */ + if (myproc == file_ioproc) { + if (IO_verbose) + printf ("Closing file '%s' after recovery.\n", fname); + IOclose (ifp); + } + + if (called_from == CP_RECOVER_DATA) { + /* Must read in parameters and scalars on all processors. */ + CactusStartTimer (¶m_time); + for (proc = file_ioproc; + proc < file_ioproc+file_ioproc_every && proc < nprocs; + proc++) { + + /* Only have the file open by one proc at any time. */ + if (proc == myproc) { + + /* Open file, make sure the file is valid */ + ifp = IEEEopen (fname, "r"); + if (! IOisValid (ifp)) { + sprintf (msg, "Cannot open checkpoint file '%s' on processor %d", + fname, myproc); + CCTK_WARN (0, msg); + } + + /* Restore the parameters. */ + if (IO_verbose) + printf ("Recovering parameters on processor %d.\n", myproc); +/*** FIXME ***/ +#if 0 + IO_IEEEIOparamRestore (ifp, myproc); + + /* Restore the structure variables. */ + if (IO_verbose) + printf ("Recovering GH variables.\n"); + IO_IEEEIOStructRestore (GH, ifp); +#endif + + /* Restore global variables */ + + /* Get the iteration number. */ + if (IO_verbose) + printf ("Recovering iteration number.\n"); + index = IOreadAttributeInfo (ifp, "iteration", &nt_stored, &nels_stored); + + if (index >= 0 && nt_stored == FLEXIO_INT4 && nels_stored == 1) { + IOreadAttribute (ifp, index, &iteration_stored); + if (IO_verbose) + printf ("Iteration number is %d\n", (int) iteration_stored); + GH->iteration = iteration_stored; + } else + printf ("*Warning* Unable to restore iteration number\n"); + + /* Close the file. */ + if (IO_verbose) + printf ("Closing '%s' after recovery.\n", fname); + IOclose (ifp); + } + + /* Synchronise all processors */ + CCTK_Barrier (GH); + } + CactusStopTimer (¶m_time); + } + + /* print timing output */ + if (IO_verbose && called_from == CP_RECOVER_DATA && myproc == 0) { + printf ( + "----------------------------------------------------------------\n"); +/*** FIXME: choose right component of basic[] ***/ + printf ("Time to restore data: %10.3lf sec\n", + dataset_time.total.basic [0]); + printf ("Time to restore parameters: %10.3lf sec\n", + param_time.total.basic [0]); + printf ("Time to recover from checkpoint: %10.3lf sec\n", + total_time.total.basic [0]); + } + + return (0); +#endif /* CACTUSBASE_PUGH */ +} diff --git a/src/RestoreFile.c b/src/RestoreFile.c new file mode 100644 index 0000000..60fb56a --- /dev/null +++ b/src/RestoreFile.c @@ -0,0 +1,520 @@ + /*@@ + @file RestoreFile.c + @date Thu Jun 18 16:34:59 1998 + @author Tom Goodale + @desc + Routines to restore GFs from a given file + @enddesc + @history + @hauthor Gabrielle Allen @hdate 19 Oct 1998 + @hdesc Changed names ready for thorn_IO + @endhistory + @version $Id$ + @@*/ + +static char *rcsid = "$Id$"; + +#include <stdio.h> +#include <stdlib.h> + +#include "cctk.h" +#include "flesh.h" +#include "Groups.h" +#include "Misc.h" +#include "Comm.h" +#include "GHExtensions.h" +#include "WarnLevel.h" +#include "declare_parameters.h" +#ifdef CACTUSBASE_PUGH +#include "CactusBase/pugh/src/include/pugh.h" +#endif +#include "CactusBase/IOUtil/src/ioGH.h" +#include "ioFlexGH.h" + + +/* MPI tag base */ +#define STARTUPBASE 1001 +/* the maximum number of dimensions we can deal with (up to now :-) */ +#define MAXDIM 3 + + +/* local routine getDatasetAttributes() reads in the next dataset's attributes + and verifies them: + + * checks if there is a variable with the name given by the name attribute + * verifies that this variable still belongs to the same group + * checks the group data info: + - group type + - variable type + - ntimelevels + - sizes (rank, dimensions) according to chunking mode + + If there is a mismatch a warning (warning level 2) is printed and value of 1 + is returned to indicate that this dataset should be ignored. + If successful, the global variable index, the group type and the timelevel + to restore are stored in {*index, *gtype, *timelevel}, and 0 is returned. + + Currently only restoring of CCTK_VARIABLE_REAL variables is supported. +*/ + +int GetCommonAttributes (cGH *GH, IOFile ifp, int unchunked, int *index, + int *gtype, int *timelevel) +{ + int i, flag; + int atype; + Long asize; + int vtype_stored, rank_stored, dims_stored [MAXDIM]; + CCTK_INT4 gtype_stored, ntimelevels_stored, timelevel_stored; + int vtype, rank, ntimelevels; + int result; + char fullname [512], groupname [512]; + char msg [160]; + + + /* read the next dataset's info from the file */ + result = IOreadInfo (ifp, &vtype_stored, &rank_stored, dims_stored, MAXDIM); + CACTUS_IEEEIO_ERROR (result); + if (result == 0) { + CCTK_WARN (1, "Can't read dataset info"); + return (1); + } + + /* retrieve the name attribute */ + i = IOreadAttributeInfo (ifp, "name", &atype, &asize); + if (i < 0 || (atype != BYTE && atype != CHAR) || asize >= sizeof (fullname)) { + CCTK_WARN (2, "Can't read name attribute"); + return (1); + } + CACTUS_IEEEIO_ERROR (IOreadAttribute (ifp, i, fullname)); + + /* check if there is a matching variable */ + *index = CCTK_GetVarIndex (fullname); + if (*index < 0) { + sprintf (msg, "No matching variable found for '%s'", fullname); + CCTK_WARN (2, msg); + return (1); + } + + /* read and verify the group name */ + i = IOreadAttributeInfo (ifp, "groupname", &atype, &asize); + if (i < 0 || (atype != BYTE && atype != CHAR) || asize >= sizeof (groupname)) { + sprintf (msg, "Can't read groupname attribute of '%s'", fullname); + CCTK_WARN (2, msg); + return (1); + } + CACTUS_IEEEIO_ERROR (IOreadAttribute (ifp, i, groupname)); + if (! CCTK_Equals (groupname, CCTK_GetGroupNameFromVar_ByIndex (*index))) { + sprintf (msg, "Groupnames don't match for '%s'", fullname); + CCTK_WARN (2, msg); + return (1); + } + + /* read the group type */ + i = IOreadAttributeInfo (ifp, "grouptype", &atype, &asize); + if (i < 0 || atype != FLEXIO_INT4 || asize != 1) { + sprintf (msg, "Can't read grouptype attribute for '%s'", fullname); + CCTK_WARN (2, msg); + return (1); + } + CACTUS_IEEEIO_ERROR (IOreadAttribute (ifp, i, >ype_stored)); + + /* read the number of timelevels */ + i = IOreadAttributeInfo (ifp, "ntimelevels", &atype, &asize); + if (i < 0 || atype != FLEXIO_INT4 || asize != 1) { + sprintf (msg, "Can't read ntimelevels attribute for '%s'", fullname); + CCTK_WARN (2, msg); + return (1); + } + CACTUS_IEEEIO_ERROR (IOreadAttribute (ifp, i, &ntimelevels_stored)); + + /* read the timelevel to restore */ + i = IOreadAttributeInfo (ifp, "timelevel", &atype, &asize); + if (i < 0 || atype != FLEXIO_INT4 || asize != 1) { + sprintf (msg, "Can't read timelevel attribute for '%s'", fullname); + CCTK_WARN (2, msg); + return (1); + } + CACTUS_IEEEIO_ERROR (IOreadAttribute (ifp, i, &timelevel_stored)); + *timelevel = timelevel_stored; + + /* verify group type, variable type, dims, sizes and ntimelevels */ + CCTK_GetGroupData (CCTK_GetGroupIndex (groupname), gtype, &vtype, &rank, + &i, &ntimelevels); + if (*gtype != gtype_stored) { + sprintf (msg, "Group types don't match for '%s'", fullname); + CCTK_WARN (2, msg); + return (1); + } + if (ntimelevels != ntimelevels_stored) { + sprintf (msg, "Number of timelevels don't match for '%s'", fullname); + CCTK_WARN (2, msg); + return (1); + } + /* The CCTK variable type defines do not correlate with the IEEEIO defines + so compare them explicitely here. + Up to now CCTK knows only REALs, but COMPLEX types aren't supported + anyway by IOFlexIO. */ + if ((vtype_stored == FLEXIO_REAL && vtype == CCTK_VARIABLE_REAL) || + (vtype_stored == FLEXIO_INT && vtype == CCTK_VARIABLE_INTEGER) || + (vtype_stored == FLEXIO_CHAR && vtype == CCTK_VARIABLE_CHAR)) { +#if 0 + || (vtype_stored == FLEXIO_COMPLEX && vtype == CCTK_VARIABLE_COMPLEX)) { +#endif + /* everything is okay */ + } else { + sprintf (msg, "Variable types don't match for '%s'", fullname); + CCTK_WARN (2, msg); + return (1); + } + + /* verify the dims and sizes */ + flag = 0; + if (rank != rank_stored) + flag = 1; + switch (*gtype) { + case GROUP_SCALAR: + if (dims_stored [0] != 1) + flag = 1; + break; + case GROUP_ARRAY: + /*** FIXME: what's the local size of the array ?? ***/ + /* for (i = 0; i < rank; i++) + if (*(int *) CCTK_ArrayGroupSize (GH, groupname, i) != dims_stored [i]) + flag = 1; + */ + break; + case GROUP_GF: + if (unchunked) { + for (i = 0; i < rank; i++) + if (GH->global_shape [i] != dims_stored [i]) + flag = 1; + } else { + for (i = 0; i < rank; i++) + if (GH->local_shape [i] != dims_stored [i]) + flag = 1; + } + break; + } + if (flag) { + sprintf (msg, "Variable sizes don't match for '%s'", fullname); + CCTK_WARN (2, msg); + return (1); + } + + if (! CCTK_QueryGroupStorage_ByIndex (GH, + CCTK_GetGroupIndexFromVar_ByIndex (*index))) { + CCTK_WARN (2, "Can't read into variable with no storage"); + return (1); + } + + return (0); +} + + +/* local routine GetChunkAttributes() reads in the info of the next dataset + that should be a chunk. + It verifies via the name attribute that this chunk belongs to the + current variable given by its index. */ + +int GetChunkAttributes (cGH *GH, IOFile ifp, int index) +{ + int i; + int atype; + Long asize; + int result; + int vtype_stored, rank_stored, dims_stored [MAXDIM]; + char fullname [512]; + char msg [160]; + pGH *pughGH; /* PUGH extension handle */ + + + /* Get the handle for PUGH extensions */ + pughGH = (pGH *) GH->extensions [CCTK_GetGHExtensionHandle ("PUGH")]; + + /* read the next dataset's info from the file */ + result = IOreadInfo (ifp, &vtype_stored, &rank_stored, dims_stored, MAXDIM); + CACTUS_IEEEIO_ERROR (result); + if (result == 0) { + CCTK_WARN (1, "Can't read dataset info"); + return (1); + } + + /* retrieve the name attribute */ + i = IOreadAttributeInfo (ifp, "name", &atype, &asize); + if (i < 0 || (atype != BYTE && atype != CHAR) || asize >= sizeof (fullname)) { + CCTK_WARN (2, "Can't read name attribute"); + return (1); + } + CACTUS_IEEEIO_ERROR (IOreadAttribute (ifp, i, fullname)); + + /* check if there is a matching variable */ + if (index != CCTK_GetVarIndex (fullname)) { + sprintf (msg, "No matching variable found for '%s'", fullname); + CCTK_WARN (2, msg); + return (1); + } + + return (0); +} + + /*@@ + @routine IOFlexIO_RestoreIEEEIOfile + @date Fri Jun 19 09:19:48 1998 + @author Tom Goodale + @desc + Reads in data from an open IEEEIO file. + Code was originally in StartupReader. + @enddesc + @calls + @calledby + @history + @hauthor Gabrielle Allen @hdate Oct 17 1998 + @hdesc Changed logic so that cactus stops if any of the dimensions of the + input file and the current cactus run differ. + @hauthor Thomas Radke @hdate May 05 1999 + @hdesc Added parameter unchunked + @endhistory + +@@*/ +int IOFlexIO_RestoreIEEEIOfile (cGH *GH, IOFile ifp, int file_ioproc, + int file_ioproc_every, int file_unchunked) +{ +#ifdef CACTUSBASE_PUGH + + DECLARE_PARAMETERS + int index, gtype; + int myproc, nprocs; + int nDatasets, currentDataset; + int timelevel; /* current timelevel to be restored */ + pGH *pughGH; /* PUGH extension handle */ + char msg [512]; /* just a message buffer */ +#ifdef MPI + int proc; + CCTK_INT info [2]; /* communication buffer for MPI */ +#endif + + + /* Get the handles for PUGH and IO extensions */ + pughGH = (pGH *) GH->extensions [CCTK_GetGHExtensionHandle ("PUGH")]; + + myproc = CCTK_GetMyProc (GH); + nprocs = CCTK_GetnProcs (GH); + + /* all IO procs determine the number of datasets in their checkpoint files */ + if (myproc == file_ioproc) { + + /* Get the number of sets */ + nDatasets = IOnDatasets (ifp); + if (IO_verbose) + printf (" Input file has %d datasets\n", nDatasets); + + } + + /* In Cactus 3.x we had only datasets containing grid function data. + This distributed data was stored as one dataset per processor within + the group belonging to one IO processor. So there should be + nGF*ioproc_every datasets within each checkpoint file. + + This consistency condition is no longer true + because now there might be datasets containing a SCALAR grouptype. + These datasets are stored only from the IO processors, + and they are just distributed to the non-IO processors again + during recovery. + + if (nDatasets % file_ioproc_every != 0) + CCTK_WARN (0, "Number of datasets isn't a multiple of nioprocs"); + */ + + /* Now process the datasets. + All IO processors read the datasets from their checkpoint file + verify their contents and communicate them to the non-IO processors. */ + + /* At first the code for the IO processors ... */ + if (myproc == file_ioproc) { + + /* Seek here once to the beginning of the file, the file pointer + is advanced then implicitely by subsequent calls to IOreadInfo() */ + CACTUS_IEEEIO_ERROR (IOseek (ifp, 0)); + + /* Each IO processor loops over all available datasets, checks their + consistency and broadcasts them to the non-IO processors. */ + for (currentDataset = 0; currentDataset < nDatasets; currentDataset++) { + + /* read in the next dataset's attributes and verify them */ + if (GetCommonAttributes (GH, ifp, file_unchunked, &index, >ype, &timelevel)) { + sprintf (msg, "Ignoring dataset %d", currentDataset); + CCTK_WARN (1, msg); + } + + /*** FIXME: process all group types ! ***/ + else if (gtype != GROUP_GF && gtype != GROUP_SCALAR) { + CCTK_WARN (1, "Currently only restoring of GF and SCALAR datasets is supported"); + } + + /* Read in the data */ + else { + + if (IO_verbose) { + char *varname = CCTK_GetFullName (index); + + printf (" dataset %d: %s (timelevel %d)\n", currentDataset, + varname, timelevel); + free (varname); + } + + if (file_ioproc_every == 1) + CACTUS_IEEEIO_ERROR (IOread (ifp, GH->data [index][timelevel])); +#ifdef MPI + else { + int npoints; + void *buffer; + int chunkdims [3], *chunkorigin; + int element_size, mpi_type; + + switch (CCTK_GetVarVType (index)) { + case CCTK_VARIABLE_CHAR: + element_size = sizeof (CCTK_CHAR); mpi_type = PUGH_MPI_CHAR; + break; + case CCTK_VARIABLE_INTEGER: + element_size = sizeof (CCTK_INT); mpi_type = PUGH_MPI_INT; + break; + case CCTK_VARIABLE_REAL: + element_size = sizeof (CCTK_REAL); mpi_type = PUGH_MPI_REAL; + break; + case CCTK_VARIABLE_COMPLEX: + CCTK_WARN (1, "Restoring of complex datatypes not yet supported"); + continue; + default: + CCTK_WARN (1, "Unknown datatype in IOFlexIO_RestoreIEEEIOfile"); + continue; + } + + /* read my own data directly into data, + read others data into buffer and communicate it */ + if (! file_unchunked || gtype == GROUP_SCALAR) + CACTUS_IEEEIO_ERROR (IOread (ifp, GH->data [index][timelevel])); + else { + chunkdims [0] = pughGH->rnx [file_ioproc]; + chunkdims [1] = pughGH->rny [file_ioproc]; + chunkdims [2] = pughGH->rnz [file_ioproc]; + chunkorigin = pughGH->lb [file_ioproc]; + + CACTUS_IEEEIO_ERROR (IOreadChunk (ifp, chunkdims, chunkorigin, + GH->data [index][timelevel])); + } + + /* read data for non-IO processors */ + if (gtype == GROUP_SCALAR) { + npoints = 1; + buffer = GH->data [index][timelevel]; + } else { + /* allocate memory for the biggest chunk */ + npoints = pughGH->rnpoints [file_ioproc + 1]; + for (proc = 2; proc < file_ioproc_every; proc++) + if (npoints < pughGH->rnpoints [file_ioproc + proc]) + npoints = pughGH->rnpoints [file_ioproc + proc]; + buffer = malloc (npoints * element_size); + } + + for (proc = file_ioproc + 1; + proc < file_ioproc + file_ioproc_every && proc < nprocs; + proc++) { + + if (gtype != GROUP_SCALAR) { + + if (! unchunked) { + /* Also increment dataset counter here !!! */ + currentDataset++; + if (GetChunkAttributes (GH, ifp, index)) { + sprintf (msg, "Ignoring chunk in dataset %d", + currentDataset+1); + CCTK_WARN (1, msg); + continue; + } + + CACTUS_IEEEIO_ERROR (IOread (ifp, buffer)); + } else { + chunkdims [0] = pughGH->rnx [proc]; + chunkdims [1] = pughGH->rny [proc]; + chunkdims [2] = pughGH->rnz [proc]; + chunkorigin = pughGH->lb [proc]; + + CACTUS_IEEEIO_ERROR (IOreadChunk (ifp, chunkdims, chunkorigin, + buffer)); + } + + npoints = pughGH->rnpoints [proc]; + } + + /* and finally send the index and the data */ + info [0] = index; info [1] = timelevel; + CACTUS_MPI_ERROR (MPI_Send (info, 2, PUGH_MPI_INT, proc, + STARTUPBASE, pughGH->PUGH_COMM_WORLD)); + CACTUS_MPI_ERROR (MPI_Send (buffer, npoints, mpi_type, proc, + STARTUPBASE, pughGH->PUGH_COMM_WORLD)); + + } + + if (gtype != GROUP_GF) + free (buffer); + } +#endif + } /* reading data for file_ioproc_every processors */ + + } /* end of loop over all datasets */ + +#ifdef MPI + /* Finally an invalid variable index is communicated + to indicate completion to the non-IO processors. */ + info [0] = -1; + for (proc = 1; proc < file_ioproc_every; proc++) + CACTUS_MPI_ERROR (MPI_Send (info, 2, PUGH_MPI_INT, proc + file_ioproc, + STARTUPBASE, pughGH->PUGH_COMM_WORLD)); +#endif + + } else { + + /* And here the code for non-IO processors: */ +#ifdef MPI + int mpi_type; + MPI_Status ms; + + /* They don't know how many datasets there are, because the IO processors + could skip some on the fly during their consistency checks. + The IO Processor sends the index of the variable to be processed next. + So, all non-IO processors execute a loop where the termination condition + is when an invalid index was received. + */ + while (1) { + /* receive the next variable index from the IO processor */ + CACTUS_MPI_ERROR (MPI_Recv (info, 2, PUGH_MPI_INT, file_ioproc, + STARTUPBASE, pughGH->PUGH_COMM_WORLD, &ms)); + index = info [0]; timelevel = info [1]; + + /* check for termination condition */ + if (index < 0) + break; + + switch (CCTK_GetVarVType (index)) { + case CCTK_VARIABLE_CHAR: mpi_type = PUGH_MPI_CHAR; break; + case CCTK_VARIABLE_INTEGER: mpi_type = PUGH_MPI_INT; break; + case CCTK_VARIABLE_REAL: mpi_type = PUGH_MPI_REAL; break; + case CCTK_VARIABLE_COMPLEX: + CCTK_WARN (1, "Restoring of complex datatypes not yet supported"); + continue; + default: + CCTK_WARN (1, "Unknown datatype in IOFlexIO_RestoreIEEEIOfile"); + continue; + } + + /* receive following data from my IO processor */ + CACTUS_MPI_ERROR (MPI_Recv (GH->data [index][timelevel], pughGH->npoints, + mpi_type, file_ioproc, + STARTUPBASE, pughGH->PUGH_COMM_WORLD, &ms)); + } +#endif + } + + return (0); +#endif /* CACTUSBASE_PUGH */ +} diff --git a/src/Startup.c b/src/Startup.c new file mode 100644 index 0000000..a9d5113 --- /dev/null +++ b/src/Startup.c @@ -0,0 +1,78 @@ + /*@@ + @file Startup.c + @date Fri May 21 1999 + @author Thomas Radke + @desc + Startup routines for IOFlexIO. + @enddesc + @history + @hauthor Thomas Radke @hdate May 21 1999 + @hdesc Just copied from thorn IOFlexIO. + @endhistory + @@*/ + +#include <stdio.h> + +#include "flesh.h" +#include "GHExtensions.h" +#include "declare_parameters.h" + +/* prototypes of functions to be registered */ +int IOFlexIO_Output2DGH (cGH *GH); +int IOFlexIO_TriggerOutput2D (cGH *GH, int); +int IOFlexIO_TimeFor2D (cGH *GH, int); +int IOFlexIO_Output2DVarAs (cGH *GH, const char *var, const char *alias); +int IOFlexIO_Output3DGH (cGH *GH); +int IOFlexIO_TriggerOutput3D (cGH *GH, int); +int IOFlexIO_TimeFor3D (cGH *GH, int); +int IOFlexIO_Output3DVarAs (cGH *GH, const char *var, const char *alias); +void *IOFlexIO_SetupGH (tFleshConfig *config, int convergence_level, cGH *GH); +int IOFlexIO_InitGH (cGH *GH); +int IOFlexIO_rfrTraverseGH (cGH *GH, int rfrpoint); + + + + /*@@ + @routine IOFlexIO_Startup + @date Fri May 21 1999 + @author Thomas Radke + @desc + The startup registration routine for IOFlexIO. + Registers the GH extensions needed for IOFlexIO and + the registerable routines used for each method of IOFlexIO. + IOFlexIO does not overload any functions. + @enddesc + @calls + @calledby + @history + + @endhistory + +@@*/ +void IOFlexIO_Startup (void) +{ + DECLARE_PARAMETERS + int IO_GHExtension; + int IOMethod; + + IO_GHExtension = CCTK_RegisterGHExtension ("IOFlexIO"); + CCTK_RegisterGHExtensionSetupGH (IO_GHExtension, IOFlexIO_SetupGH); + CCTK_RegisterGHExtensionInitGH (IO_GHExtension, IOFlexIO_InitGH); + CCTK_RegisterGHExtensionrfrTraverseGH (IO_GHExtension, IOFlexIO_rfrTraverseGH); + + /* Register the 2D and 3D IOFlexIO routines as output methods */ + if (strlen (output2D) > 0) { + IOMethod = CCTK_RegisterIOMethod ("IOFlexIO_2D"); + CCTK_RegisterIOMethodOutputGH (IOMethod, IOFlexIO_Output2DGH); + CCTK_RegisterIOMethodOutputVarAs (IOMethod, IOFlexIO_Output2DVarAs); + CCTK_RegisterIOMethodTimeToOutput (IOMethod, IOFlexIO_TimeFor2D); + CCTK_RegisterIOMethodTriggerOutput (IOMethod, IOFlexIO_TriggerOutput2D); + } + if (strlen (output3D) > 0) { + IOMethod = CCTK_RegisterIOMethod ("IOFlexIO_3D"); + CCTK_RegisterIOMethodOutputGH (IOMethod, IOFlexIO_Output3DGH); + CCTK_RegisterIOMethodOutputVarAs (IOMethod, IOFlexIO_Output3DVarAs); + CCTK_RegisterIOMethodTimeToOutput (IOMethod, IOFlexIO_TimeFor3D); + CCTK_RegisterIOMethodTriggerOutput (IOMethod, IOFlexIO_TriggerOutput3D); + } +} diff --git a/src/Write2D.c b/src/Write2D.c new file mode 100644 index 0000000..c708440 --- /dev/null +++ b/src/Write2D.c @@ -0,0 +1,479 @@ +/*@@ + @file Write2D.c + @date Tue Feb 3 19:06:12 1998 + @author Paul Walker + @desc + Two dimensional slices into IEEEIO files on MPI distributed cactus data. + @enddesc + @history + @hauthor Gabrielle Allen @hdate 26 Sep 1998 + @hdesc Changed subroutine name (SliceTwoD -> Write2D), renamed IO parameters + @hauthor Gabrielle Allen @hdate Oct 17 1998 + @hdesc Added the IO_verbose parameter instead of an ifdef + @hauthor Gabrielle Allen @hdate 19 Oct 1998 + @hdesc Changed names ready for thorn_IO + @hauthor Thomas Radke @hdate 16 Mar 1999 + @hdesc Converted to Cactus 4.0 + @hauthor Thomas Radke @hdate 17 Mar 1999 + @hdesc Changed naming: IEEEIO -> IOFlexIO + @hendhistory + @version $Id$ + @@*/ + +static char *rcsid = "$Id$"; + +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> + +#include "cctk.h" +#include "flesh.h" +#include "Comm.h" +#include "declare_parameters.h" +#include "GroupsOnGH.h" +#ifdef CACTUSBASE_PUGH +#include "CactusBase/pugh/src/include/pugh.h" +#endif +#include "CactusBase/IOUtil/src/ioGH.h" +#include "ioFlexGH.h" + +/* MPI message tag */ +#define BASE 1234 + + +/*@@ + @routine IOFlexIO_Write2D + @date Sat March 6 1999 + @author Gabrielle Allen + @desc Writes the 2D data of a variable into separate IEEEIO files + @enddesc + @calls CCTK_GetGHExtensionHandle + CCTK_GetMyProc + CCTK_GetnProcs + CCTK_WARN + @calledby IOFlexIO_Output2DVarAs + IOFlexIO_TriggerOutput2D + @history + @endhistory + @var GH + @vdesc Pointer to CCTK GH + @vtype cGH + @vio in + @vcomment + @endvar + @var index + @vdesc index of variable to output + @vtype int + @vio in + @vcomment + @endvar + @var alias + @vdesc alias name of variable to output + @vtype const char * + @vio in + @vcomment + @endvar + @var first + @vdesc flag to indicate whether file should be opened in "w" or "a" mode + @vtype int (boolean) + @vio in + @vcomment + @endvar +@@*/ + +void IOFlexIO_Write2D (cGH *GH, int index, const char *alias, int first) +{ + DECLARE_PARAMETERS + int i, j, k, l; + int myproc, nprocs; + pGH *pughGH; + ioGH *ioUtilGH; + flexioGH *myGH; + int dir, ngpoints; + CCTK_INT npoints, *nrempoints, bnd [4]; + void *data; + int ni, nj; /* "x" and "y" direction ns */ + /* Attributes */ + CCTK_REAL origin [2], delta [2]; + int maxrempt; + int dims [2]; + void *locdat, *alldat, *recd; + int hi, lo; + int locbnd [4]; + int timelevel, variable_type, element_size, ioflex_type; +#ifdef MPI + MPI_Status ms; + int mpi_type; +#endif + + + /* Get the handles for pugh, IOFlexIO and IOUtil extensions */ + pughGH = (pGH *) GH->extensions [CCTK_GetGHExtensionHandle ("PUGH")]; + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + myGH = (flexioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IOFlexIO")]; + + /* What processor are we on? */ + myproc = CCTK_GetMyProc (GH); + nprocs = CCTK_GetnProcs (GH); + + if (IO_verbose) { + printf ("<><><><><><><><><><><><><><><><><>\n"); + printf ("2D Slice for [%s]\n", alias); +#if 0 + cactus_memtrace (); +#endif + } + + /* Open the files on the first trip through if we are proc. 0 */ + if (myproc == 0) { + if (first) { + int len; + char *fname; + char *msg; + + len = strlen (ioUtilGH->outpfx_2D) + strlen (alias); + fname = (char *) malloc (len + 20); + msg = (char *) malloc (len + 20 + 40); + assert (fname && msg); + + /* Create files (open mode "w") */ + sprintf (fname, "%s/%s_2d_yz.ieee", ioUtilGH->outpfx_2D, alias); + myGH->IEEEfile_2D [index][0] = IEEEopen (fname, "w"); + if (! IOisValid (myGH->IEEEfile_2D [index][0])) { + sprintf (msg, "Cannot open output file %s", fname); + CCTK_WARN (1, msg); + return; + } + + sprintf (fname, "%s/%s_2d_xz.ieee", ioUtilGH->outpfx_2D, alias); + myGH->IEEEfile_2D [index][1] = IEEEopen (fname, "w"); + if (! IOisValid (myGH->IEEEfile_2D [index][1])) { + sprintf (msg, "Cannot open output file %s", fname); + CCTK_WARN (1, msg); + return; + } + + sprintf (fname, "%s/%s_2d_xy.ieee", ioUtilGH->outpfx_2D, alias); + myGH->IEEEfile_2D [index][2] = IEEEopen (fname, "w"); + if (! IOisValid (myGH->IEEEfile_2D [index][2])) { + sprintf (msg, "Cannot open output file %s", fname); + CCTK_WARN (1, msg); + return; + } + + free (msg); + free (fname); + } + } + + nrempoints = (CCTK_INT *) malloc (nprocs * sizeof (CCTK_INT)); + assert (nrempoints); + + timelevel = CCTK_GetNumTimeLevels (index) - 1; + if (timelevel > 0) + timelevel--; + data = CCTK_GetVarDataPtr_ByIndex (GH, timelevel, index); + + variable_type = CCTK_GetVarVType (index); + switch (variable_type) { + case CCTK_VARIABLE_CHAR: + ioflex_type = FLEXIO_CHAR; + element_size = sizeof (CCTK_CHAR); +#ifdef MPI + mpi_type = PUGH_MPI_CHAR; +#endif + break; + + case CCTK_VARIABLE_INTEGER: + ioflex_type = FLEXIO_INT; + element_size = sizeof (CCTK_INT); +#ifdef MPI + mpi_type = PUGH_MPI_INT; +#endif + break; + + case CCTK_VARIABLE_REAL: + ioflex_type = FLEXIO_REAL; + element_size = ioUtilGH->out_single ? + sizeof (CCTK_REAL4) : sizeof (CCTK_REAL); +#ifdef MPI + mpi_type = ioUtilGH->out_single ? PUGH_MPI_REAL4 : PUGH_MPI_REAL; +#endif + break; + + default: + CCTK_WARN (1, "Unsupported variable type in IOFlexIO_Write2D"); + return; + } + + for (dir = 0; dir < 3; dir++) { + npoints = 0; + /* Figure out the number of points */ + if ((pughGH->ownership[pughGH->stagger][dir][0] <= pughGH->sp2dxyz[dir]) && + (pughGH->sp2dxyz[dir] < pughGH->ownership[pughGH->stagger][dir][1])) + npoints = (pughGH->ownership[pughGH->stagger][(dir+1)%3][1] - + pughGH->ownership[pughGH->stagger][(dir+1)%3][0]) * + (pughGH->ownership[pughGH->stagger][(dir+2)%3][1] - + pughGH->ownership[pughGH->stagger][(dir+2)%3][0]); + + /* Set up the number of global points and the points in the "i" + and "j" directions */ + if (dir == 0) { + ngpoints = GH->global_shape[1] * GH->global_shape[2]; + /* FIXME : origin */ + ni = GH->global_shape[1]; origin[0] = 0.0; delta[0] = pughGH->dy0; + nj = GH->global_shape[2]; origin[1] = 0.0; delta[1] = pughGH->dz0; + } else if (dir == 1) { + ngpoints = GH->global_shape[0] * GH->global_shape[2]; + /* FIXME : Origin */ + ni = GH->global_shape[0]; origin[0] = 0.0; delta[0] = pughGH->dx0; + nj = GH->global_shape[2]; origin[1] = 0.0; delta[1] = pughGH->dz0; + } else { + ngpoints = GH->global_shape[0] * GH->global_shape[1]; + /* FIXME : Origin */ + ni = GH->global_shape[0]; origin[0] = 0.0; delta[0] = pughGH->dx0; + nj = GH->global_shape[1]; origin[1] = 0.0; delta[1] = pughGH->dy0; + } + + if (IO_verbose) { + printf ("Npoints in dir %d is %d -> ",dir,npoints); + printf ("Global npoints is %d\n",ngpoints); + } + +#ifdef MPI + /* Send the number of points */ + CACTUS_MPI_ERROR (MPI_Gather (&npoints, 1, PUGH_MPI_INT, + nrempoints, 1, PUGH_MPI_INT, 0, pughGH->PUGH_COMM_WORLD)); + + if (IO_verbose && myproc == 0) + for (i = 0; i < nprocs; i++) + printf ("From proc %d: %d\n", i, (int) nrempoints [i]); + +#else + nrempoints [0] = npoints; +#endif + + maxrempt = 0; + if (myproc == 0) + for (i = 0; i < nprocs; i++) + maxrempt = (nrempoints [i] > maxrempt ? nrempoints [i] : maxrempt); + + if (IO_verbose) + printf ("Maximum remote points: %d\n", maxrempt); + + /* OK so we want the ordering in bnd to be yz xz or xy but the + mod naturally gives is yz zx xy so we need to flip the order. + thus put the dir+1 % 3 in at lo, the dir+2 % 3 in at hi, and + set lo and hi directionally as follows ... */ + if (dir == 0) {lo = 0; hi = 2;} + if (dir == 1) {lo = 2; hi = 0;} + if (dir == 2) {lo = 0; hi = 2;} + if (npoints > 0) { + /* See above comment ... */ + bnd [lo] = GH->lower_bound[(dir+1)%3] + + pughGH->ownership[pughGH->stagger][(dir+1)%3][0]; + bnd [lo+1] = GH->lower_bound[(dir+1)%3] + + pughGH->ownership[pughGH->stagger][(dir+1)%3][1]; + + bnd [hi] = GH->lower_bound[(dir+2)%3] + + pughGH->ownership[pughGH->stagger][(dir+2)%3][0]; + bnd [hi+1] = GH->lower_bound[(dir+2)%3] + + pughGH->ownership[pughGH->stagger][(dir+2)%3][1]; + + locbnd [lo] = pughGH->ownership[pughGH->stagger][(dir+1)%3][0]; + locbnd [lo+1] = pughGH->ownership[pughGH->stagger][(dir+1)%3][1]; + + locbnd [hi] = pughGH->ownership[pughGH->stagger][(dir+2)%3][0]; + locbnd [hi+1] = pughGH->ownership[pughGH->stagger][(dir+2)%3][1]; + + } else { + bnd [lo] = 0; + bnd [lo+1] = 0; + bnd [hi] = 0; + bnd [hi+1] = 0; + } + + if (IO_verbose) + printf ("Local bounds %d: j -> %d %d k -> %d %d\n", + dir, locbnd [0], locbnd [1], locbnd [2], locbnd [3]); + + /* Subsample the data */ + if (npoints > 0) { + locdat = malloc (npoints * element_size); + assert (locdat); + l = 0; + if (variable_type == CCTK_VARIABLE_CHAR) { + CCTK_CHAR *char_locdat = (CCTK_CHAR *) locdat; + + for (k = locbnd [2]; k < locbnd [3]; k++) + for (j = locbnd [0]; j < locbnd [1]; j++) { + int pt; + if (dir==0) pt = DI (pughGH, pughGH->sp2dxyz [dir], j, k); + if (dir==1) pt = DI (pughGH, j, pughGH->sp2dxyz [dir], k); + if (dir==2) pt = DI (pughGH, j, k, pughGH->sp2dxyz [dir]); + assert (l <= npoints && pt <= pughGH->npoints); + char_locdat [l++] = ((CCTK_CHAR *) data) [pt]; + } + } else if (variable_type == CCTK_VARIABLE_INTEGER) { + CCTK_INT *int_locdat = (CCTK_INT *) locdat; + + for (k = locbnd [2]; k < locbnd [3]; k++) + for (j = locbnd [0]; j < locbnd [1]; j++) { + int pt; + if (dir==0) pt = DI (pughGH, pughGH->sp2dxyz [dir], j, k); + if (dir==1) pt = DI (pughGH, j, pughGH->sp2dxyz [dir], k); + if (dir==2) pt = DI (pughGH, j, k, pughGH->sp2dxyz [dir]); + assert (l <= npoints && pt <= pughGH->npoints); + int_locdat [l++] = ((CCTK_INT *) data) [pt]; + } + } else if (variable_type == CCTK_VARIABLE_REAL) { + CCTK_REAL4 *real4_locdat = (CCTK_REAL4 *) locdat; + CCTK_REAL *real_locdat = (CCTK_REAL *) locdat; + + for (k = locbnd [2]; k < locbnd [3]; k++) + for (j = locbnd [0]; j < locbnd [1]; j++) { + int pt; + if (dir==0) pt = DI (pughGH, pughGH->sp2dxyz [dir], j, k); + if (dir==1) pt = DI (pughGH, j, pughGH->sp2dxyz [dir], k); + if (dir==2) pt = DI (pughGH, j, k, pughGH->sp2dxyz [dir]); + assert (l <= npoints && pt <= pughGH->npoints); + if (ioUtilGH->out_single) + real4_locdat [l++] = (CCTK_REAL4) (((CCTK_REAL *) data) [pt]); + else + real_locdat [l++] = ((CCTK_REAL *) data) [pt]; + } + } + } + + /* Send the data */ +#ifdef MPI + /* Only send if we have points and we are not on proc 0 */ + if (npoints > 0 && myproc != 0) { + CACTUS_MPI_ERROR (MPI_Send (bnd, 4, PUGH_MPI_INT, 0, 2*myproc+1+BASE, + pughGH->PUGH_COMM_WORLD)); + CACTUS_MPI_ERROR (MPI_Send (locdat, (int) npoints, mpi_type, 0, + 2*myproc+BASE, pughGH->PUGH_COMM_WORLD)); + } + + /* Receive the data on processor 0 */ + if (myproc == 0) { + alldat = malloc (ngpoints * element_size); + recd = malloc (maxrempt * element_size); + assert (alldat && recd); + + for (i = 0; i < nprocs; i++) { + void *ud; + + if (IO_verbose) + printf ("Proceesing proc %d\n",i);fflush(stdout); + + if (nrempoints [i] > 0) { + if (i == 0) + ud = locdat; + else { + + if (IO_verbose) { + printf ("Receiving %d from %d\n", nrempoints [i], i); + fflush (stdout); + } + + CACTUS_MPI_ERROR (MPI_Recv (bnd, 4, PUGH_MPI_INT, + i, 2*i+1+BASE, pughGH->PUGH_COMM_WORLD, &ms)); + CACTUS_MPI_ERROR (MPI_Recv (recd, (int) nrempoints [i], mpi_type, + i, 2*i+BASE, pughGH->PUGH_COMM_WORLD, &ms)); + ud = recd; + + if (IO_verbose) { + printf (" Received %d points from %d\n", + (int) nrempoints [i], i); + printf (" Data lives at [%d %d] -> [%d %d]\n", + (int) bnd [0], (int) bnd [2], (int) bnd [1], (int)bnd[3]); + } + + } /* End proc != 0 */ + + if (IO_verbose) + printf ("Remote Bounds %d: j -> %d %d k -> %d %d\n", + dir, (int) bnd [0], (int) bnd[1], (int)bnd[2], (int)bnd[3]); + + l = 0; + if (variable_type == CCTK_VARIABLE_CHAR) + for (k = bnd [2]; k < bnd [3]; k++) + for (j = bnd [0]; j < bnd [1]; j++) + ((CCTK_CHAR *) alldat) [j + k*ni] = ((CCTK_CHAR *) ud) [l++]; + else if (variable_type == CCTK_VARIABLE_INTEGER) + for (k = bnd [2]; k < bnd [3]; k++) + for (j = bnd [0]; j < bnd [1]; j++) + ((CCTK_INT *) alldat) [j + k*ni] = ((CCTK_INT *) ud) [l++]; + else if (variable_type == CCTK_VARIABLE_REAL && ioUtilGH->out_single) + for (k = bnd [2]; k < bnd [3]; k++) + for (j = bnd [0]; j < bnd [1]; j++) + ((CCTK_REAL4 *) alldat) [j + k*ni] = ((CCTK_REAL4 *) ud) [l++]; + else if (variable_type == CCTK_VARIABLE_REAL && !ioUtilGH->out_single) + for (k = bnd [2]; k < bnd [3]; k++) + for (j = bnd [0]; j < bnd [1]; j++) + ((CCTK_REAL *) alldat) [j + k*ni] = ((CCTK_REAL *) ud) [l++]; + } /* End if > 0 */ + } /* End processor loop */ + free (recd); + } +#else + alldat = locdat; +#endif + + /* Proc 0 writes */ + if (myproc == 0) { + dims [0] = ni; + dims [1] = nj; + + /*** FIXME: since slice center is not yet set up, npoints might be 0 ***/ + if (npoints <= 0) + CCTK_WARN (1, "2D output doesn't work since slice center is not set up"); + else { + /* the data itself */ + CACTUS_IEEEIO_ERROR (IOwrite (myGH->IEEEfile_2D [index][dir], + ioflex_type, 2, dims, alldat)); + + /* Time and space attributes */ + CACTUS_IEEEIO_ERROR (IOwriteAttribute (myGH->IEEEfile_2D [index][dir], + "time", FLEXIO_REAL, 1, &GH->time)); + CACTUS_IEEEIO_ERROR (IOwriteAttribute (myGH->IEEEfile_2D [index][dir], + "origin", FLEXIO_REAL, 2, origin)); + CACTUS_IEEEIO_ERROR (IOwriteAttribute (myGH->IEEEfile_2D [index][dir], + "delta", FLEXIO_REAL, 2, delta)); + } + } + + /* Free memory for this time through */ + if (npoints > 0) + free (locdat); +#ifdef MPI + if (myproc == 0) + free (alldat); +#endif /* If not MPI then alldat and locdat are not distinct */ + + } + + free (nrempoints); + +#if 0 + if (IO_verbose) + cactus_memtrace(); +#endif +} + + + +#if 0 +void FMODIFIER FORTRAN_NAME(io_write2d_, + IO_WRITE2D, + io_write2d) + (CCTK_REAL *x) +{ + pGF *GF; + + GF = locateGFbyDataPtr(x); + IO_Write2D(GF->parentGH,GF); +} + +#endif diff --git a/src/Write3D.c b/src/Write3D.c new file mode 100644 index 0000000..6d72a93 --- /dev/null +++ b/src/Write3D.c @@ -0,0 +1,462 @@ +/*@@ + @file Write3D.c + @date Fri Feb 21 15:27:03 1997 + @author + @desc + A horrible blocking write and not-quite-so-horrible + slicer for xgraph output. Basically MPI based IO + bites the big one, and this is MPI based IO written + quickly and in the obvious inefficient fashion. + <p> + Oh, not it also contains zero-d I/O and a closer also. + <p> + This routine can write DFSD style HDF (not the default) + or Johns IEEEIO 3D files (the default). For 0 and 1D + I/O it writes xgraph files. + @enddesc + @history + @hauthor Gabrielle Allen @hdate 26 Sep 1998 + @hdesc Changed routine name (WriteGF->Write3D), Lots of tidying up and + renaming of parameters + @hauthor Gabrielle Allen @hdate 17 Oct 1998 + @hdesc Implemented new parameters IO_verbose,IO_parameter,IO_structures,IO_scalars + @hauthor Gabrielle Allen @hdate 19 Oct 1998 + @hdesc Changed names ready for thorn_IO + @hauthor Thomas Radke @hdate 16 Mar 1999 + @hdesc Converted to Cactus 4.0 + @hauthor Thomas Radke @hdate 17 Mar 1999 + @hdesc Changed naming: IEEEIO -> IOFlexIO + @hendhistory + @version $Id$ + @@*/ + +static char *rcsid = "$Id$"; + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "cctk.h" +#include "flesh.h" +#include "Comm.h" +#include "Groups.h" +#include "GHExtensions.h" +#include "WarnLevel.h" +#include "declare_parameters.h" +#ifdef CACTUSBASE_PUGH +#include "CactusBase/pugh/src/include/pugh.h" +#endif +#include "CactusBase/IOUtil/src/ioGH.h" +#include "ioFlexGH.h" + + +/* local function prototypes */ +void IOFlexIO_Write3D_filename (cGH *GH, char *filename, const char *name); +void IOFlexIO_Write3D_openFile (cGH *GH, const char *filename, IOFile *iofile, int first); +void IOFlexIO_Write3D_closeFile (cGH *GH, const char *filename, IOFile *iofile); +/* external function prototypes */ +void IOFlexIO_DumpVar (cGH *GH, int index, int timelevel, IOFile iof); + + +/*@@ + @routine IOFlexIO_Write3D + @author Paul Walker + @date Feb 1997 + @desc + This routine does 3D IO for a grid function which has been + distributed with MPI. + <p> + 3D MPI IO! Wow, you may say, that sounds pretty clever. Well it isn't + because <b><i>The method I use sucks rocks</i></b>. That is. + I spam the data from all processors onto processor 0. Luckily + it doesn't just put it all in one memory chunk, but recieves it + bit by bit and writes it out chunkwise, using IEEEIO. Alas, + while this happens the other processors <i>sit in a barrier + with their metaphoric thumbs up their asses</i>. + <p> + <b>Even Worse</b> it is a blocking send which must come in order + from the other processors. + <p> + This is inefficient to say the least. I/O costs can go up to 70-80% + using this technique on the T3E. So there are also a couple of ways to + get around it. + <p> + The easiest option is one-file-per-processor which is + good on some machines, (eg T3E) and bad on others (eg O2K, I think). + So you can activate this mode (or de-activate it on the T3E) + by setting the parameter onefileperproc to "yes" or "no". + This will create a bunch of files in their own subdirectory, + and they can be smooshed together using cactus/lib/etc/Recombiner.C + <p> + Also, you can downsample output. You can also output one file per + slice. Various combinations of these work or don't, depending. + <p> + Finally notice that @seeroutine main guarantees that this is never + called on a 1- or 2-D grid. + @enddesc + @calls CCTK_GetGHExtensionHandle + CCTK_GetMyProc + CCTK_GetnProcs + CCTK_WARN + @calledby IOFlexIO_Output3DVarAs + IOFlexIO_TriggerOutput3D + @history + @hdate Wed Sep 2 10:11:41 1998 @hauthor Tom Goodale + @hdesc Merged in Szu-Wen's Panda calls. + @hdate Sep 25 1998 1998 @hauthor Gabrielle Allen + @hdesc Split into subroutines + @endhistory + @var GH + @vdesc Pointer to CCTK GH + @vtype cGH + @vio in + @vcomment + @endvar + @var index + @vdesc index of variable to output + @vtype int + @vio in + @vcomment + @endvar + @var alias + @vdesc alias name of variable to output + @vtype const char * + @vio in + @vcomment + @endvar + @var first + @vdesc flag to indicate whether file should be opened in "w" or "a" mode + @vtype int (boolean) + @vio in + @vcomment + @endvar + @@*/ + + +void IOFlexIO_Write3D (cGH *GH, int index, const char *alias, int first) +{ + DECLARE_PARAMETERS + int myproc, nprocs; + int timelevel; + ioGH *ioUtilGH; + flexioGH *myGH; + IOFile *iofile; + char *filename; + + + /* Get the handle for IOUtil and IOFlexIO extensions */ + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + myGH = (flexioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IOFlexIO")]; + + /* Get filename and reference to file handle */ + filename = myGH->IEEEfname_3D [index]; + iofile = &myGH->IEEEfile_3D [index]; + + if (IO_verbose) { + printf ("-------------------------------------------------------\n"); + printf ("3D I/O on Grid Function %s\n", alias); + printf (" downsample (x,y,z): (%d,%d,%d) ioproc : %d\n", + ioUtilGH->downsample_x, ioUtilGH->downsample_y, + ioUtilGH->downsample_z, ioUtilGH->ioproc); + fflush (stdout); + } + + /* What processor are we on? */ + myproc = CCTK_GetMyProc (GH); + nprocs = CCTK_GetnProcs (GH); + + + /* build the filename for output */ + /* This code is semi-repeated in DumpGH.c */ + /* The output directory is also created. */ + if (first || onefileperslice) + IOFlexIO_Write3D_filename (GH, filename, alias); + + /* Close the file if it is already open (if IEEEfile_3D is set). + * This shouldn't be needed, since we do it below, + * but better safe than sorry. + * Also I have some vague recollection of needing it in some + * very strange case ... So leave it here for now (PW 11.5.98) + */ + if (*iofile && onefileperslice) { + if (IO_verbose) + printf ("Closing IEEEfile from previous iteration.\n"); + CACTUS_IEEEIO_ERROR (IOclose (*iofile)); + *iofile = NULL; /* prevent attempts to close it twice! */ + } + + + /* open the output file (only if we are an output processor) */ + if (myproc == ioUtilGH->ioproc) + IOFlexIO_Write3D_openFile (GH, filename, iofile, first); + + /* get the current timelevel */ + timelevel = CCTK_GetNumTimeLevels (index) - 1; + if (timelevel > 0) + timelevel--; + + /* output the data */ + IOFlexIO_DumpVar (GH, index, timelevel, *iofile); + + /* output the scalars and parameters */ + /* Don't do this if the output is being byte compared, as the + * thorn lists might be different. + */ + if (myproc == ioUtilGH->ioproc) { + /* output parameters necessary for filereader datafiles */ + if (first) + IOFlexIO_IEEEIOStructDump (GH, *iofile); +#if 0 + if (ioUtilGH->parameters) + IOFlexIO_IEEEIOparamDump (*iofile); +#endif + /* close the file */ + IOFlexIO_Write3D_closeFile (GH, filename, iofile); + } + + if (IO_verbose) + printf ("-------------------\n"); +} + + +/*@@ + @routine IOFlexIO_Write3D_closeFile + @author Paul Walker + @date Feb 1997 + @desc + Closes or pauses the IEEEIO output file. + @enddesc + @history + @hauthor Gabrielle Allen + @hdate Sep 1998 + @hdesc Split into subroutine + @endhistory + @@*/ + + +void IOFlexIO_Write3D_closeFile (cGH *GH, const char *filename, IOFile *iofile) +{ + DECLARE_PARAMETERS + ioGH *ioUtilGH; + flexioGH *myGH; + + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + myGH = (flexioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IOFlexIO")]; + + if (*iofile == NULL) + return; + + if (onefileperslice) { + if (IO_verbose) + printf ("Closing IEEEfile from this iteration\n"); + CACTUS_IEEEIO_ERROR (IOclose (*iofile)); + *iofile = NULL; + return; + } + + if (myGH->reuse_fh) { + if (IO_verbose) + printf ("Pausing file %s\n", filename); + IEEEbufferOff (*iofile); + CACTUS_IEEEIO_ERROR (IOpause (*iofile)); + } else { + if (IO_verbose) + printf ("Closing file %s\n", filename); + CACTUS_IEEEIO_ERROR (IOclose (*iofile)); + *iofile = NULL; + } +} + + +/*@@ + @routine IOFlexIO_Write3D_openFile + @author Paul Walker + @date Feb 1997 + @desc + Opens or resumes the IEEEIO output file + @enddesc + @history + @hauthor Gabrielle Allen + @hdate Sep 1998 + @hdesc Split into subroutine + @endhistory + @@*/ + +void IOFlexIO_Write3D_openFile (cGH *GH, const char *filename, IOFile *iofile, int first) +{ + DECLARE_PARAMETERS + ioGH *ioUtilGH; + flexioGH *myGH; + + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + myGH = (flexioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IOFlexIO")]; + + /* If we are the first time through, or writing one file per slice, we need + to open a file in write mode. Or, if we are reusing file handles we will + need to resume the file */ + + if (first || onefileperslice) { + + /* First time through or one file per slice; open anew */ + if (IO_verbose) + printf ("Opening file %s\n", filename); + + *iofile = IEEEopen (filename, "w"); + if (! IOisValid (*iofile)) { + char *msg = (char *) malloc (strlen (filename) + 80); + + sprintf (msg, "The file %s could not be opened for writing.", filename); + CCTK_WARN (1, msg); + free (msg); + } + } + + else if (myGH->reuse_fh) { + + /* resume the file (reopen descriptor) + This allows descriptor reuse without requiring expensive destruction + and reallocation of the associated datastructures */ + + if (*iofile) { + + if (IO_verbose) + printf ("Resuming file %s\n", filename); + + CACTUS_IEEEIO_ERROR (IOresume (*iofile)); + } + } else { + /* File is closed, not onefileperslice and not first time. + Therefore we must be conserving file handles and failed + to use the pause()/resume methods(), So append. + */ + if (IO_verbose) + printf ("Re-opening file %s in append mode\n", filename); + + *iofile = IEEEopen (filename, "a"); + if (! IOisValid (*iofile)) { + char *msg = (char *) malloc (strlen (filename) + 80); + + sprintf (msg, "The file %s could not be opened for appending.\n", + filename); + CCTK_WARN (1, msg); + free (msg); + } + } + + /* Turn on buffer cache (with default size chosen by IEEEIO lib) */ + IEEEbufferOn (*iofile, 0); +} + + +/*@@ + @routine IOFlexIO_Write3D_filename + @author Paul Walker + @date Feb 1997 + @desc + Generates the filename for output + @enddesc + @history + @hauthor Gabrielle Allen + @hdate Sep 1998 + @hdesc Split into subroutine + @endhistory + @@*/ + +void IOFlexIO_Write3D_filename (cGH *GH, char *filename, const char *name) +{ + DECLARE_PARAMETERS + ioGH *ioUtilGH; /* handle for IOUtil extensions */ + pGH *pughGH; /* handle for PUGH extensions */ + int nprocs; + int myproc; + char extra [256]; /* Extra stuff in fname based on mode */ + char extradir [256]; /* Extra stuff for an output dir */ + char createdir [256]; /* Text for system call to create output directory */ + + ioUtilGH = (ioGH *) GH->extensions [CCTK_GetGHExtensionHandle ("IO")]; + pughGH = (pGH *) GH->extensions [CCTK_GetGHExtensionHandle ("PUGH")]; + + extra [0] = '\0'; + extradir [0] = '\0'; + + nprocs = CCTK_GetnProcs (GH); + myproc = CCTK_GetMyProc (GH); + +#if 0 + /* Say if we have a chunked data in the output file(s) */ + if (! ioUtilGH->unchunked && nprocs > 1) + sprintf (extra, "%s_chunked", extra); +#endif + + if (onefileperslice) + sprintf (extra, "%s.time_%7.3f", extra, GH->time); + + + /* OUTPUT ONE FILE FOR EACH N PROCESSORS + * ------------------------------------- + * + * If only one output file, the single file for each GF is written + * in the normal output dir (that is extradir = ""). Otherwise + * a directory is created for each output GF to hold the multiple + * file + */ + + if (ioUtilGH->ioproc_every < nprocs) { + + /* Add the output processor number to the extra string */ + sprintf (extra, "%s.file_%d", extra, myproc / ioUtilGH->ioproc_every); + + /* If necessary create the output directory */ + if (myproc == 0) { + + sprintf (createdir, "mkdir -p %s/%s_3d", ioUtilGH->outpfx_3D, name); + + if (IO_verbose) { + printf ("Creating output directory with command\n %s\n", createdir); + fflush (stdout); + } + + if (system (createdir) != 0) + printf ("Could not create directory\n"); + } + +#ifdef MPI + /* Wait for all processors to catch up */ + CACTUS_MPI_ERROR (MPI_Barrier (pughGH->PUGH_COMM_WORLD)); +#endif + + /* extradir is the relative output directory */ + sprintf (extradir, "%s_3d/", name); + + } + + + /* CREATE THE COMPLETE OUTPUT FILENAME + ----------------------------------- */ + + sprintf (filename, "%s/%s%s_3d%s.ieee", ioUtilGH->outpfx_3D, + extradir, name, extra); + + /* And be sure to replace any spaces in the filename with an _ */ + do + if (*filename == ' ') + *filename = '_'; + while (*++filename); + +} + + +#if 0 +void FMODIFIER FORTRAN_NAME(io_write3d_, + IO_WRITE3D, + io_write3d) + (Double *x) +{ + pGF *GF; + + GF = locateGFbyDataPtr(x); + IO_Write3D(GF->parentGH,GF); + +} + +#endif diff --git a/src/ioFlexGH.h b/src/ioFlexGH.h new file mode 100644 index 0000000..573acbf --- /dev/null +++ b/src/ioFlexGH.h @@ -0,0 +1,85 @@ + /*@@ + @header IOFlexIOGH.h + @date Tue 9th Jan 1999 + @author Gabrielle Allen + @desc + The extensions to the GH structure from IO. + @history + @hauthor Thomas Radke @hdate 16 Mar 1999 + @hdesc Added parameters for 2D and 3D output + @hauthor Thomas Radke @hdate 17 Mar 1999 + @hdesc Changed naming: IEEEIO -> IOFlexIO + @hauthor Thomas Radke @hdate 30 Mar 1999 + @hdesc Undefined DI macro + @endhistory + @version $Header$ + @@*/ + +#include <string.h> + +#include "IEEEIO/IO.h" +#include "IEEEIO/IEEEIO.h" + + +/* define the IOFlexIO datatypes according to CCTK_??? datatypes */ +#define FLEXIO_REAL4 FLOAT32 + +#ifdef CCTK_REAL_PRECISION_16 +#error "128-bit precision floating point types are not supported by IOFlexIO !" +#elif CCTK_REAL_PRECISION_8 +#define FLEXIO_REAL FLOAT64 +#elif CCTK_REAL_PRECISION_4 +#define FLEXIO_REAL FLOAT32 +#endif + +#ifdef CCTK_INTEGER_PRECISION_8 +#define FLEXIO_INT INT64 +#elif CCTK_INTEGER_PRECISION_4 +#define FLEXIO_INT INT32 +#elif CCTK_INTEGER_PRECISION_2 +#define FLEXIO_INT INT16 +#endif + +#define FLEXIO_INT4 INT32 +#define FLEXIO_CHAR CHAR + + +/* Check error flags from IOFlexIO */ +#define CACTUS_IEEEIO_ERROR(fn_call) \ + do { \ + \ + int error_code = fn_call; \ + \ + if (error_code < 0) { \ + char *msg = (char*) malloc(strlen(__FILE__) + strlen(#fn_call) + 160);\ + \ + sprintf (msg, \ + "IEEEIO call %s returned error code %d, file %s, line %d\n", \ + #fn_call, error_code, __FILE__, __LINE__); \ + CCTK_WARN (1, msg); \ + free (msg); \ + } \ + } while (0) + + +typedef struct IOFlexIOGH { + + /* The number of times output */ + int *IO_2Dnum; + int *IO_3Dnum; + + /* The last iteration output */ + int *IO_2Dlast; + int *IO_3Dlast; + + /* IEEEIO file pointers for 2D output (3 for each variable) */ + IOFile **IEEEfile_2D; + + /* IEEEIO file name and pointer for 3D output */ + char **IEEEfname_3D; + IOFile *IEEEfile_3D; + + /* reuse filehandles during IEEEIO */ + int reuse_fh; + +} flexioGH; diff --git a/src/make.code.defn b/src/make.code.defn new file mode 100644 index 0000000..2bbe405 --- /dev/null +++ b/src/make.code.defn @@ -0,0 +1,3 @@ +SRCS = Startup.c GHExtension.c Output2D.c Write2D.c Output3D.c Write3D.c DumpVar.c DumpGH.c RecoverGH.c RestoreFile.c + +SUBDIRS = IEEEIO |