You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
584 lines
19 KiB
584 lines
19 KiB
/*
|
|
* 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 <stdlib.h>
|
|
#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; i<len; i++ ) out_vec[i] = (double)(in_vec[i]);
|
|
}
|
|
|
|
void dbl_to_flt( double* in_vec,
|
|
int len,
|
|
float* out_vec )
|
|
{
|
|
int i;
|
|
|
|
for( i=0; i<len; i++ ) out_vec[i] = (float)(in_vec[i]);
|
|
}
|
|
|
|
|
|
/*............................................................................*/
|
|
/*............................................................................*/
|
|
|
|
void* ex_conv_array( int exoid,
|
|
convert_task task,
|
|
const void* usr_array,
|
|
int usr_length )
|
|
{
|
|
/* ex_conv_array() actually performs the floating point size conversion.
|
|
*
|
|
* "exoid" is some integer which uniquely identifies the file of interest.
|
|
*
|
|
* for reads, in conjunction with ncvarget()/ncvarget1(), ex_conv_array() must
|
|
* be called twice per read. the first call must be before ncvarget(), and
|
|
* should be something like ex_conv_array( id, RTN_ADDRESS, usr_array, len ),
|
|
* where "usr_array" is the address of the user's data array, and "len" is
|
|
* the number of floating point values to convert. this call returns an
|
|
* address which should be passed as a parameter in the subsequent ncvarget()
|
|
* call. after ncvarget(), call ex_conv_array() again with something like
|
|
* ex_conv_array( ID, READ_CONVERT, usr_array, len ). here ex_conv_array()
|
|
* should return NULL.
|
|
*
|
|
* for writes, in conjunction with ncvarput()/ncvarput1(), ex_conv_array() need
|
|
* only be called once, before the call to ncvarput(). the call should be
|
|
* something like ex_conv_array( id, WRITE_CONVERT, usr_array, len ), and
|
|
* returns an address that should be passed in the subsequent ncvarput() call.
|
|
*/
|
|
|
|
|
|
char errmsg[MAX_ERR_LENGTH];
|
|
/* static void* buffer_array = NULL; -- now global! */
|
|
struct file_item* file;
|
|
int len_bytes;
|
|
|
|
exerrval = 0; /* clear error code */
|
|
if (do_conversion == 0) {
|
|
switch( task ) {
|
|
|
|
case RTN_ADDRESS:
|
|
return (void*)usr_array;
|
|
break;
|
|
case READ_CONVERT:
|
|
return NULL;
|
|
break;
|
|
case WRITE_CONVERT:
|
|
return (void*)usr_array;
|
|
break;
|
|
default:
|
|
/* Fall through if other task is specified */
|
|
;
|
|
}
|
|
}
|
|
|
|
FIND_FILE( file, exoid );
|
|
|
|
if( !file )
|
|
{
|
|
exerrval = EX_BADFILEID;
|
|
sprintf(errmsg,"Error: unknown file id %d",exoid);
|
|
ex_err("ex_conv_array",errmsg,exerrval);
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
switch( task ) {
|
|
|
|
case RTN_ADDRESS:
|
|
|
|
switch( file->rd_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;
|
|
}
|
|
|