/* * Copyright (c) 1994 Sandia Corporation. Under the terms of Contract * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Governement * retains certain rights in this software. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * * Neither the name of Sandia Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /***************************************************************************** * * exutils - exodus utilities * * author - James A. Schutt - 8 byte float and standard C definitions * Vic Yarberry - Added headers and error logging * * environment - UNIX * * entry conditions - * * exit conditions - * * revision history - * * $Id: ex_conv.c,v 1.1 2005/07/17 15:43:59 andy Exp $ * *****************************************************************************/ #include #include "exodusII.h" #include "exodusII_int.h" typedef int convert_task; /* this file contains code needed to support the various floating point word * size combinations for computation and i/o that applications might want to * use. the following discussion uses the C type names "float" and "double". * * netCDF supports two floating point word sizes for its files: * NC_FLOAT - 32 bit IEEE (in XDR parlance, XDR_FLOAT) * NC_DOUBLE - 64 bit IEEE (in XDR parlance, XDR_DOUBLE) * now, if you want to write an array of NC_FLOATs, netCDF expects as input * an array of native floats; NC_DOUBLEs require an input array of native * doubles. * * so, suppose you're computing using variables declared double, but you want * to write a netCDF file using NC_FLOATs. you need to copy your array into * a buffer array declared as float, which truncates your data from double to * float (type conversion). then you can pass the buffer array to netCDF * routines for output as NC_FLOATs, and everything will work OK. similarly, * if you are computing in floats but want to write NC_DOUBLEs, you need to * copy your data into a buffer array declared as double, which promotes it * from float to double, and then call the netCDF routine with the buffer array. * * these routines are designed to do this type conversion, based on information * given in the ex_open or ex_create calls. thus, except for when the file is * opened, the user is relieved of the burden of caring about compute word size * (the size of floating point variables used in the application program, and * passed into the EXODUS II calls) and i/o word size (the size of floating * point data as written in the netCDF file). * * this code is supposed to be general enough to handle weird cases like the * cray, where in C (and C++) both floats and doubles are 8 byte quantities. * thus the same array can be passed into a netCDF routine to write either * NC_FLOATs or NC_DOUBLEs. * * note: 16 byte floating point values, such as might be obtained from an ANSI C * "long double", are specifically not handled. Also, I don't know how * the vanilla netCDF interface handles double precision on a CRAY, which * gives 16 byte values, but these routines as written won't be able to * handle it. * * author: j. a. schutt, sandia national laboratories, department 1425 */ #define NC_FLOAT_WORDSIZE 4 #define NC_DOUBLE_WORDSIZE 8 enum conv_action { NO_CONVERSION, CONVERT_UP, CONVERT_DOWN }; typedef int conv_action; struct file_item { int file_id; conv_action rd_conv_action; conv_action wr_conv_action; nc_type netcdf_type_code; int user_compute_wordsize; struct file_item* next; }; struct file_item* file_list = NULL; /* * Now recognized at more locations worldwide in this file... */ static int cur_len = 0; /* in bytes! */ static void* buffer_array = NULL; static int do_conversion = 0; /* Do any files do a conversion? */ #define FIND_FILE(ptr,id) { ptr = file_list; \ while(ptr) { \ if( ptr->file_id == id ) break; \ ptr = ptr->next; \ } \ } /*............................................................................*/ /*............................................................................*/ int ex_conv_ini( int exoid, int* comp_wordsize, int* io_wordsize, int file_wordsize ) { char errmsg[MAX_ERR_LENGTH]; struct file_item* new_file; /* ex_conv_ini() initializes the floating point conversion process. * * exoid an integer uniquely identifying the file of interest. * * word size parameters are specified in bytes. valid values are 0, 4, and 8: * * comp_wordsize compute floating point word size in the user's code. * a zero value indicates that the user is requesting the * default float size for the machine. The appropriate * value is chosen and returned in comp_wordsize, and used * in subsequent conversions. a valid but inappropriate * for this parameter cannot be detected. * * io_wordsize the desired floating point word size for a netCDF file. * for an existing file, if this parameter doesn't match * the word size of data already stored in the file, a * fatal error is generated. a value of 0 for an existing * file indicates that the word size of the file was not * known a priori, so use whatever is in the file. a value * of 0 for a new file means to use the default size, an * NC_FLOAT (4 bytes). when a value of 0 is specified the * actual value used is returned in io_wordsize. * * file_wordsize floating point word size in an existing netCDF file. * a value of 0 should be passed in for a new netCDF file. */ /* check to make sure machine word sizes aren't weird (I'm paranoid) */ if ((sizeof(float) != 4 && sizeof(float) != 8) || (sizeof(double) != 4 && sizeof(double) != 8 ) ) { sprintf(errmsg,"Error: unsupported compute word size for file id: %d", exoid); ex_err("ex_conv_ini",errmsg,EX_FATAL); return(EX_FATAL); } /* check to see if requested word sizes are valid */ if (!*io_wordsize ) { if (!file_wordsize ) *io_wordsize = NC_FLOAT_WORDSIZE; else *io_wordsize = file_wordsize; } else if (*io_wordsize != 4 && *io_wordsize != 8 ) { sprintf(errmsg,"Error: unsupported I/O word size for file id: %d",exoid); ex_err("ex_conv_ini",errmsg,EX_FATAL); return(EX_FATAL); } else if (file_wordsize && *io_wordsize != file_wordsize ) { *io_wordsize = file_wordsize; sprintf(errmsg, "Error: invalid I/O word size specified for existing file id: %d", exoid); ex_err("ex_conv_ini",errmsg,EX_MSG); ex_err("ex_conv_ini", " Requested I/O word size overridden.", EX_MSG); } if (!*comp_wordsize ) { *comp_wordsize = sizeof(float); } else if (*comp_wordsize != 4 && *comp_wordsize != 8 ) { ex_err("ex_conv_ini","Error: invalid compute wordsize specified",EX_FATAL); return(EX_FATAL); } /* finally, set up conversion action */ new_file = malloc(sizeof(struct file_item)); new_file->file_id = exoid; new_file->user_compute_wordsize = *comp_wordsize; new_file->next = file_list; file_list = new_file; /* crays writing NC_FLOATs always hit this case first since on a cray * sizeof(float) = sizeof(double) */ if( *comp_wordsize == sizeof(float) && *io_wordsize == NC_FLOAT_WORDSIZE ) { new_file->rd_conv_action = NO_CONVERSION; new_file->wr_conv_action = NO_CONVERSION; new_file->netcdf_type_code = NC_FLOAT; } /* crays writing NC_DOUBLEs always hit this case first since on a cray * sizeof(float) = sizeof(double) */ else if( *comp_wordsize == sizeof(double) && *io_wordsize == NC_DOUBLE_WORDSIZE ) { new_file->rd_conv_action = NO_CONVERSION; new_file->wr_conv_action = NO_CONVERSION; new_file->netcdf_type_code = NC_DOUBLE; } else if( *comp_wordsize == sizeof(double) && *io_wordsize == NC_FLOAT_WORDSIZE ) { new_file->rd_conv_action = CONVERT_UP; new_file->wr_conv_action = CONVERT_DOWN; new_file->netcdf_type_code = NC_FLOAT; do_conversion = 1; } else if( *comp_wordsize == sizeof(float) && *io_wordsize == NC_DOUBLE_WORDSIZE ) { new_file->rd_conv_action = CONVERT_DOWN; new_file->wr_conv_action = CONVERT_UP; new_file->netcdf_type_code = NC_DOUBLE; do_conversion = 1; } else { /* Invalid compute or io wordsize: i.e. 4 byte compute word on Cray */ sprintf(errmsg,"Error: invalid compute (%d) or io (%d) wordsize specified", *comp_wordsize, *io_wordsize); ex_err("ex_conv_ini", errmsg, EX_FATAL); return(EX_FATAL); } return(EX_NOERR); } /*............................................................................*/ /*............................................................................*/ void ex_conv_exit( int exoid ) { /* ex_conv_exit() takes the structure identified by "exoid" out of the linked * list which describes the files that ex_conv_array() knows how to convert. * * NOTE: it is absolutely necessary for ex_conv_array() to be called after * ncclose(), if the parameter used as "exoid" is the id returned from * an ncopen() or nccreate() call, as netCDF reuses file ids! * the best place to do this is ex_close(), which is where I did it. * * "exoid" is some integer which uniquely identifies the file of interest. */ char errmsg[MAX_ERR_LENGTH]; struct file_item* file = file_list; struct file_item* prev = NULL; exerrval = 0; /* clear error code */ while( file ) { if (file->file_id == exoid ) break; prev = file; file = file->next; } if (!file ) { sprintf(errmsg,"Warning: failure to clear file id %d - not in list.",exoid); ex_err("ex_conv_exit",errmsg,EX_MSG); exerrval = EX_BADFILEID; return; } if (prev ) prev->next = file->next; else file_list = file->next; free( file ); /* * If no other files are opened, any buffer arrays for float/double * conversion ought to be cleaned up. */ if ( !file_list ) { if ( cur_len > 0 ) { free(buffer_array); /* Better not be null if condition true! */ buffer_array = NULL; cur_len = 0; } do_conversion = 0; } } /*............................................................................*/ /*............................................................................*/ nc_type nc_flt_code( int exoid ) { /* nc_flt_code() returns either NC_FLOAT or NC_DOUBLE, based on the parameters * with which ex_conv_ini() was called. nc_flt_code() is used as the nc_type * parameter on ncvardef() calls that define floating point variables. * * "exoid" is some integer which uniquely identifies the file of interest. */ char errmsg[MAX_ERR_LENGTH]; struct file_item* file; exerrval = 0; /* clear error code */ FIND_FILE( file, exoid ); if (!file ) { exerrval = EX_BADFILEID; sprintf(errmsg,"Error: unknown file id %d for nc_flt_code().",exoid); ex_err("nc_flt_code",errmsg,exerrval); return (nc_type) -1; } return file->netcdf_type_code; } /*............................................................................*/ /*............................................................................*/ int ex_comp_ws( int exoid ) { /* "exoid" is some integer which uniquely identifies the file of interest. * * ex_comp_ws() returns 4 (i.e. sizeof(float)) or 8 (i.e. sizeof(double)), * depending on the value of floating point word size used to initialize * the conversion facility for this file id (exoid). */ char errmsg[MAX_ERR_LENGTH]; struct file_item* file; exerrval = 0; /* clear error code */ FIND_FILE( file, exoid ); if (!file ) { exerrval = EX_BADFILEID; sprintf(errmsg,"Error: unknown file id %d",exoid); ex_err("ex_comp_ws",errmsg,exerrval); return(EX_FATAL); } return file->user_compute_wordsize; } /*............................................................................*/ /*............................................................................*/ /* some utility routines for use only by ex_conv_array() */ #define BUFFER_SIZE_UNIT 8192 /* should be even multiple of sizeof(double) */ void* resize_buffer( void* buffer, int new_len ) /* in bytes! */ { /* * Broaden the scope of this puppy to aid cleanup in ex_conv_exit(). */ /* static int cur_len = 0; in bytes! */ exerrval = 0; /* clear error code */ if( new_len > cur_len ) { cur_len = BUFFER_SIZE_UNIT * ( new_len/BUFFER_SIZE_UNIT + 1 ); if( buffer ) free( buffer ); buffer = malloc( cur_len ); if (!buffer ) { exerrval = EX_MEMFAIL; ex_err("ex_conv_array","couldn't allocate buffer space",exerrval); return (NULL); } } return buffer; } void flt_to_dbl( float* in_vec, int len, double* out_vec ) { int i; for( i=0; ird_conv_action ) { case NO_CONVERSION: return (void*)usr_array; case CONVERT_UP: /* file ws: 4 byte, CPU ws: 8 byte */ len_bytes = usr_length * sizeof(float); buffer_array = resize_buffer( buffer_array, len_bytes ); return buffer_array; case CONVERT_DOWN: /* file ws: 8 byte, CPU ws: 4 byte */ len_bytes = usr_length * sizeof(double); buffer_array = resize_buffer( buffer_array, len_bytes ); return buffer_array; } break; case READ_CONVERT: switch( file->rd_conv_action ) { case NO_CONVERSION: break; case CONVERT_UP: flt_to_dbl( buffer_array, usr_length, (void*)usr_array ); break; case CONVERT_DOWN: dbl_to_flt( buffer_array, usr_length, (void*)usr_array ); break; } return NULL; case WRITE_CONVERT: switch( file->wr_conv_action ) { case NO_CONVERSION: return (void*)usr_array; case CONVERT_UP: len_bytes = usr_length * sizeof(double); buffer_array = resize_buffer( buffer_array, len_bytes ); flt_to_dbl( (void*)usr_array, usr_length, buffer_array ); return buffer_array; case CONVERT_DOWN: len_bytes = usr_length * sizeof(float); buffer_array = resize_buffer( buffer_array, len_bytes ); dbl_to_flt( (void*)usr_array, usr_length, buffer_array ); return buffer_array; } break; case WRITE_CONVERT_DOWN: len_bytes = usr_length * sizeof(float); buffer_array = resize_buffer( buffer_array, len_bytes ); dbl_to_flt( (void*)usr_array, usr_length, buffer_array ); return buffer_array; case WRITE_CONVERT_UP: len_bytes = usr_length * sizeof(double); buffer_array = resize_buffer( buffer_array, len_bytes ); flt_to_dbl( (void*)usr_array, usr_length, buffer_array ); return buffer_array; } exerrval = EX_FATAL; sprintf(errmsg, "Error: unknown task code %d specified for converting float array",task); ex_err("ex_conv_array",errmsg,exerrval); return NULL; }