/* grid.c */ /* * Vis5D system for visualizing five dimensional gridded data sets. * Copyright (C) 1990 - 2000 Bill Hibbard, Johan Kellum, Brian Paul, * Dave Santek, and Andre Battaiola. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * As a special exception to the terms of the GNU General Public * License, you are permitted to link Vis5D with (and distribute the * resulting source and executables) the LUI library (copyright by * Stellar Computer Inc. and licensed for distribution with Vis5D), * the McIDAS library, and/or the NetCDF library, where those * libraries are governed by the terms of their own licenses. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "../config.h" #include #include #include #include #include #include #include "api.h" #include "binio.h" #include "grid.h" #include "graphics.h" #include "globals.h" #include "memory.h" #include "proj.h" #include "sync.h" /* MJK 12.02.98 */ #include "user_data.h" #include "v5d.h" #ifndef M_PI # define M_PI 3.14159265 #endif #define DEG2RAD (M_PI/180.0) #define RAD2DEG (180.0/M_PI) /* MJK 12.02.98 begin */ static int read_user_header( char filename[], v5dstruct *v ) { int iret = 0; /* * Call the user's function to get the user's header information. * It is not necessary for this function to actually read a file -- * it only needs to put the user's header information into the Vis5D * data structure. * * An example for this function can be found in user_data.c. */ iret = user_data_get_header (filename, v); return iret; } static int read_user_grid( v5dstruct *v, int time, int var, void *griddata ) { int iret = 0; /* * Call the user's function to get the user's grid data for the * specified variable and time. * It is not necessary for this function to actually read a file -- * it only needs to put the user's grid data in the place provided. * * An example for this function can be found in user_data.c. */ iret = user_data_get_grid (v, time, var, griddata); return iret; } /* * Open a user grid file, read the header, and initialize a bunch of * global variables. * Input: filename - name of user grid file. * Return: 1 = success, 0 = error, -1 = try Vis5D. */ int open_userfile( char filename[], v5dstruct *v ) { int iret, var; if ((iret = read_user_header (filename, v)) != 1) { return iret; } v5dVerifyStruct( v ); /* compute grid sizes */ v->SumGridSizes = 0; for (var=0;varNumVars;var++) { v->GridSize[var] = 8 * v->Nl[var] + v5dSizeofGrid( v, 0, var ); v->SumGridSizes += v->GridSize[var]; } return 1; } /* * Read a grid from a user file. * Input: v - pointer to v5dstruct describing the file * time, var - which timestep and variable * griddata - address of where to store grid data. * Return: 1 = ok, 0 = error, -1 = try Vis5D. */ int read_userfile( v5dstruct *v, int time, int var, void *griddata ) { if (time<0 || time>=v->NumTimes) { printf("Error in v5dReadCompressedGrid: bad timestep argument (%d)\n", time); return 0; } if (var<0 || var>=v->NumVars) { printf("Error in v5dReadCompressedGrid: bad var argument (%d)\n", var); return 0; } return read_user_grid (v, time, var, griddata); } static void *get_compressed_grid( Context ctx, int time, int var, float **ga, float **gb ); int write_gridfile( Context ctx, char filename[] ) { int i, time, var; void *compdata; float *ga, *gb; v5dstruct *v; v = v5dNewStruct(); v->NumTimes = ctx->NumTimes; v->NumVars = ctx->NumVars; v->Nr = ctx->Nr; v->Nc = ctx->Nc; for (var=0;varNumVars;var++) { v->Nl[var] = ctx->Nl[var]; v->LowLev[var] = ctx->Variable[var]->LowLev; strncpy( v->VarName[var], ctx->Variable[var]->VarName, 8); strncpy( v->Units[var], ctx->Variable[var]->Units, 19 ); v->MinVal[var] = ctx->Variable[var]->MinVal; v->MaxVal[var] = ctx->Variable[var]->MaxVal; } for (time=0;timeNumTimes;time++) { v->TimeStamp[time] = v5dSecondsToHHMMSS( ctx->TimeStamp[time] ); v->DateStamp[time] = v5dDaysToYYDDD( ctx->DayStamp[time] ); } v->CompressMode = ctx->CompressMode; /* do the projection and vert coord sys */ v->Projection = ctx->Projection; switch (ctx->Projection) { case PROJ_GENERIC: case PROJ_LINEAR: case PROJ_CYLINDRICAL: case PROJ_SPHERICAL: v->ProjArgs[0] = ctx->NorthBound; v->ProjArgs[1] = ctx->WestBound; v->ProjArgs[2] = ctx->RowInc; v->ProjArgs[3] = ctx->ColInc; break; case PROJ_MERCATOR: v->ProjArgs[0] = ctx->CentralLat; v->ProjArgs[1] = ctx->CentralLon; v->ProjArgs[2] = ctx->RowIncKm; v->ProjArgs[3] = ctx->ColIncKm; break; case PROJ_ROTATED: v->ProjArgs[0] = ctx->NorthBound; v->ProjArgs[1] = ctx->WestBound; v->ProjArgs[2] = ctx->RowInc; v->ProjArgs[3] = ctx->ColInc; v->ProjArgs[4] = ctx->CentralLat/DEG2RAD; v->ProjArgs[5] = ctx->CentralLon/DEG2RAD; v->ProjArgs[6] = ctx->Rotation/DEG2RAD; break; case PROJ_LAMBERT: v->ProjArgs[0] = ctx->Lat1; v->ProjArgs[1] = ctx->Lat2; v->ProjArgs[2] = ctx->PoleRow; v->ProjArgs[3] = ctx->PoleCol; v->ProjArgs[4] = ctx->CentralLon; v->ProjArgs[5] = ctx->ColInc; break; case PROJ_STEREO: v->ProjArgs[0] = ctx->CentralLat; v->ProjArgs[1] = ctx->CentralLon; v->ProjArgs[2] = ctx->CentralRow; v->ProjArgs[3] = ctx->CentralCol; v->ProjArgs[4] = ctx->ColInc; break; default: printf("Error: unknown projection type in grid.c\n"); } v->VerticalSystem = ctx->VerticalSystem; switch (ctx->VerticalSystem) { case VERT_GENERIC: case VERT_EQUAL_KM: v->VertArgs[0] = ctx->BottomBound; v->VertArgs[1] = ctx->LevInc; break; case VERT_NONEQUAL_MB: case VERT_NONEQUAL_KM: for (i=0;iMaxNl;i++) { v->VertArgs[i] = ctx->Height[i]; } break; default: printf("Error in grid.c, unknown vertical coord system\n"); } v5dCreateFile( filename, v); for (time=0;timeNumTimes;time++) { for (var=0;varNumVars;var++) { printf("Writing grid to file. Time = %d Var = %d\n", time, var); compdata = get_compressed_grid( ctx, time, var, &ga, &gb ); if (!v5dWriteCompressedGrid( v, time, var, ga, gb, compdata )){ printf("Error in write_gridfile: cannot write compressed grid to file\n"); exit(0); } } } v5dCloseFile( v ); v5dFreeStruct( v ); return 1; } /* * Open a compressed grid file, read the header, and initialize a bunch of * global variables. * Input: filename - name of compressed grid file. * Return: 1 = success, 0 = error. */ int open_gridfile( Context ctx, char filename[] ) { int ok; /* MJK 12.02.98 begin */ ok = -1; if (ctx->UserDataFlag) { ok = open_userfile (filename, &ctx->G); if (ok == 0) return 0; } if (ok == -1) { if (!initially_open_gridfile( filename, &ctx->G)) { return 0; } } /* MJK 12.02.98 end */ return set_ctx_from_internalv5d(ctx); } int set_ctx_from_internalv5d(Context ctx) { int i, time, var, first; /* Initalize parameter type table */ for (i=0;i < ctx->G.NumVars;i++) { ctx->Variable[i] = (vis5d_variable *) calloc(1,sizeof(vis5d_variable)); } /* Copy header info from G to global variables */ ctx->NumTimes = ctx->G.NumTimes; ctx->NumVars = ctx->G.NumVars; ctx->Nr = ctx->G.Nr; ctx->Nc = ctx->G.Nc; ctx->MaxNl = 0; for (var=0;varNumVars;var++) { ctx->Nl[var] = ctx->G.Nl[var]; ctx->Variable[var]->LowLev = ctx->G.LowLev[var]; if (ctx->Nl[var]+ctx->Variable[var]->LowLev>ctx->MaxNl) { ctx->MaxNl = ctx->Nl[var]+ctx->Variable[var]->LowLev; ctx->MaxNlVar = var; } strncpy( ctx->Variable[var]->VarName, ctx->G.VarName[var], 8 ); strncpy( ctx->Variable[var]->Units, ctx->G.Units[var], 19 ); ctx->Variable[var]->MinVal = ctx->G.MinVal[var]; ctx->Variable[var]->MaxVal = ctx->G.MaxVal[var]; ctx->Variable[var]->VarType = VIS5D_REGULAR; ctx->Variable[var]->CloneTable = var; } /* MJK 4-13-98 if (ctx->NumVars == 1 && ctx->MaxNl == 1){ ctx->MaxNl = 2; ctx->Nl[0] = 2; } end MJK*/ /* Check that grid isn't too big */ if (ctx->NumTimes>MAXTIMES) { printf("Error: Too many time steps (%d) limit is %d\n", ctx->NumTimes, MAXTIMES ); return 0; } if (ctx->NumVars>MAXVARS) { printf("Error: Too many variables (%d) limit is %d\n", ctx->NumVars, MAXVARS ); return 0; } if (ctx->Nr>MAXROWS) { printf("Error: Number of grid rows (%d) too large, %d is limit.\n", ctx->Nr,MAXROWS); printf("Edit src/v5d.h and increase MAXROWS\n"); return 0; } if (ctx->Nc>MAXCOLUMNS) { printf("Error: Number of grid columns (%d) too large, %d is limit.\n", ctx->Nc, MAXCOLUMNS ); printf("Edit src/v5d.h and increase MAXCOLUMNS\n"); return 0; } if (ctx->MaxNl>MAXLEVELS) { printf("Error: Number of grid levels (%d) too large, %d is limit.\n", ctx->MaxNl, MAXLEVELS ); printf("Edit src/v5d.h and increase MAXLEVELS\n"); return 0; } /* convert time from HHMMSS to seconds since midnight */ /* convert date from YYDDD to days since Jan, 1900 */ for (time=0;timeNumTimes;time++) { ctx->TimeStamp[time] = v5dHHMMSStoSeconds( ctx->G.TimeStamp[time] ); ctx->DayStamp[time] = v5dYYDDDtoDays( ctx->G.DateStamp[time] ); } ctx->CompressMode = ctx->G.CompressMode; /* calculate elapsed time (in seconds) for each time since initial time */ first = ctx->DayStamp[0]* 24*60*60 + ctx->TimeStamp[0]; for (time=0;timeNumTimes;time++) { ctx->Elapsed[time] = ctx->DayStamp[time] * 24*60*60 + ctx->TimeStamp[time] - first; } return 1; } void free_grid_cache( Context ctx ) { int it, iv; for (it=0; itGa[it][iv]){ deallocate(ctx, ctx->Ga[it][iv], -1); ctx->Ga[it][iv] = NULL; } if (ctx->Gb[it][iv]){ deallocate( ctx, ctx->Gb[it][iv], -1); ctx->Gb[it][iv] = NULL; } } } for(it=0;itMaxCachedGrids;it++) deallocate(ctx,ctx->GridCache[it].Data,0); deallocate(ctx,ctx->GridCache,ctx->MaxCachedGrids * sizeof(struct cache_rec)); ctx->GridCache=NULL; } /* * Initialize the grid caching system. This *must* be called *after* * the data file's header information has been read. * Input: maxbytes - maximum number of bytes to use for caching grids. * Return: 1 = ok, 0 = error (out of memory). */ int init_grid_cache( Context ctx, int maxbytes, float *ratio ) { int i, it, iv; int maxnl, gridsize; free_grid_cache( ctx ); /* First allocate space for ga/gb compression values */ for (it=0;itNumTimes;it++) { for (iv=0;ivNumVars;iv++) { ctx->Ga[it][iv] = (float *) allocate( ctx, ctx->Nl[iv] * sizeof(float) ); ctx->Gb[it][iv] = (float *) allocate( ctx, ctx->Nl[iv] * sizeof(float) ); } } ALLOC_LOCK( ctx->Mutex ); /* Allocate the mutex lock */ /* Determine the maximum number of grids to cache given the maximum */ /* number of bytes of memory to use. */ maxnl = 0; for (iv=0; ivNumVars; iv++) { if (ctx->Nl[iv] > maxnl) { maxnl = ctx->Nl[iv]; } } gridsize = ctx->Nr * ctx->Nc * maxnl * ctx->CompressMode; ctx->MaxCachedGrids = (int) (maxbytes / gridsize); if (ctx->MaxCachedGrids >= ctx->NumTimes*ctx->NumVars) { /* the whole file can be cached */ ctx->MaxCachedGrids = ctx->NumTimes*ctx->NumVars; *ratio = 1.0; } else { *ratio = ((float) ctx->MaxCachedGrids) / ((float) (ctx->NumTimes*ctx->NumVars)); } ctx->NumCachedGrids = 0; printf("Cache size: %d grids %d %d\n", ctx->MaxCachedGrids, ctx->NumTimes,ctx->NumVars); if (ctx->MaxCachedGrids != ctx->NumTimes*ctx->NumVars){ int needed; needed = (((gridsize * ctx->NumTimes * ctx->NumVars) * 5 / 2) / (1024*1024)); needed = (int) ( (float) needed * 1.25) + 2; printf(" Hint... To run Vis5D more efficiently try setting %s to '-mbs %d'\n", ctx->DataFile, needed); } /* Allocate the ctx->GridCache array */ ctx->GridCache = (struct cache_rec *) allocate( ctx, ctx->MaxCachedGrids * sizeof(struct cache_rec) ); if (!ctx->GridCache) { printf("Error: out of memory. Couldn't allocate cache table.\n"); return 0; } ctx->CacheClock = 1; /* Initialize tables */ for (i=0;iMaxCachedGrids;i++) { ctx->GridCache[i].Data = (void *) allocate( ctx, gridsize ); if (!ctx->GridCache[i].Data) { printf("Error: out of memory. Couldn't allocate cache space.\n"); return 0; } ctx->GridCache[i].Locked = 0; ctx->GridCache[i].Timestep = 0; ctx->GridCache[i].Var = 0; } for (it=0;itNumTimes;it++) { for (iv=0;ivGridTable[it][iv].CachePos = -1; ctx->GridTable[it][iv].Data = NULL; } } return 1; } /* * Open a compressed grid file and read the header; * if lowecase name fails, try uppercase. * Input: filename -name of compressed grid file * v - pointer to struct to hold return values * Return: 1 = success, 0 = error */ int initially_open_gridfile( char filename[], v5dstruct *v ) { char name[1000]; int i; strcpy( name, filename); /* Open the v5d file */ if (!v5dOpenFile( name, v)) { /* try uppercase name */ for ( i = strlen(name)-1; i >= 0; i--) { if (name[i] == '/') break; else if (islower(name[i])) name [i] -= 'a'-'A'; } if (!v5dOpenFile( name, v)) { printf("Error: datafile %s not found \n", filename); return 0; } else { /* success, change filename to uppercase */ strcpy (filename, name); } } return 1; } /* * Open a compressed grid file,, read the header, and then close the grid file. * Input: filename - name of compressed grid file. * v - pointer to struct to hold return values * Return: 1 = success, 0 = error. */ int query_gridfile( char filename[], v5dstruct *v ) { if (!initially_open_gridfile( filename, v)) { return 0; } v5dCloseFile( v ); return 1; } /* * Return an index into the ctx->GridCache array which corresponds to an empty * position. If the cache is full, we'll discard something. */ int get_empty_cache_pos( Context ctx ) { int g; /* find g */ if (ctx->NumCachedGridsMaxCachedGrids) { /* There's an unused position. */ g = ctx->NumCachedGrids; ctx->NumCachedGrids++; } else { int time, var; #ifdef RANDOM /* pick a cache position at random for replacement */ g = rand() % ctx->MaxCachedGrids; while (ctx->GridCache[g].Locked) { g++; if (g>=ctx->MaxCachedGrids) g = 0; } printf("Random discard %d\n", g ); #else /* LRU */ int minage, i, mini; minage = ctx->CacheClock; for (i=0;iMaxCachedGrids;i++) { if (ctx->GridCache[i].AgeGridCache[i].Locked==0) { minage = ctx->GridCache[i].Age; mini = i; } } g = mini; /* remove references to data being discarded */ time = ctx->GridCache[g].Timestep; var = ctx->GridCache[g].Var; ctx->GridTable[time][var].Data = NULL; ctx->GridTable[time][var].CachePos = -1; #endif } ctx->GridCache[g].Locked = 1; return g; } /*** get_compressed_grid ********************************************** Return a pointer to the compressed data for a 3-D grid. Input: time, var - time and variable of grid wanted. ga, gb - pointer to pointer to float. Output: ga, gb - array of values to use for decompressing. Return: pointer to 1, 2 or 4-byte data values **********************************************************************/ static void *get_compressed_grid( Context ctx, int time, int var, float **ga, float **gb ) { int p, ok; var = ctx->Variable[var]->CloneTable; LOCK_ON( ctx->Mutex ); if (ctx->GridTable[time][var].Data) { /* already in the cache */ p = ctx->GridTable[time][var].CachePos; if (p>=0) { ctx->GridCache[p].Locked = 1; ctx->GridCache[p].Age = ctx->CacheClock++; } LOCK_OFF( ctx->Mutex ); *ga = ctx->Ga[time][var]; *gb = ctx->Gb[time][var]; return ctx->GridTable[time][var].Data; } else { /* not in the cache */ int g; g = get_empty_cache_pos(ctx); /*printf("Reading grid into pos %d\n", g );*/ ok = -1; if (ctx->UserDataFlag) { ok = read_userfile (&ctx->G, time, var, ctx->GridCache[g].Data); } if (ok == -1) { ok = v5dReadCompressedGrid( &ctx->G, time, var, ctx->Ga[time][var], ctx->Gb[time][var], ctx->GridCache[g].Data ); } /* MJK 12.02.98 end */ /* MJK 3.3.99 */ if (!ok){ printf("Error: unable to read grid (time=%d, var=%d)\n", time, var ); LOCK_OFF( ctx->Mutex ); return NULL; } ctx->GridTable[time][var].Data = ctx->GridCache[g].Data; ctx->GridTable[time][var].CachePos = g; ctx->GridCache[g].Locked = 1; ctx->GridCache[g].Timestep = time; ctx->GridCache[g].Var = var; ctx->GridCache[g].Age = ctx->CacheClock++; LOCK_OFF( ctx->Mutex ); *ga = ctx->Ga[time][var]; *gb = ctx->Gb[time][var]; return ctx->GridTable[time][var].Data; } } /*** release_compressed_grid ****************************************** Release a compressed grid. Input: time, var - the timestep and variable of grid to release. **********************************************************************/ void release_compressed_grid( Context ctx, int time, int var ) { int p; /* just unlock */ LOCK_ON( ctx->Mutex ); p = ctx->GridTable[time][var].CachePos; if (p>=0) { ctx->GridCache[ p ].Locked = 0; } LOCK_OFF( ctx->Mutex ); } /* * Load some or all of the grid data into main memory. */ void preload_cache( Context ctx ) { if (ctx->NumTimes*ctx->NumVars <= ctx->MaxCachedGrids) { /* All grids will fit in the cache. Read whole file now. */ int time, var; printf("Reading all grids.\n"); for (time=0;timeNumTimes;time++) { for (var=0;varNumVars;var++) { float *ga, *gb; void *d; d = get_compressed_grid( ctx, time, var, &ga, &gb ); if (d) release_compressed_grid( ctx, time, var ); } } } } /*** get_grid ********************************************************* Return a pointer to the uncompressed data for a 3-D grid. Input: time, var - time and variable of grid wanted. Return: pointer to float data **********************************************************************/ float *get_grid( Context ctx, int time, int var ) { float *data, *ga, *gb; void *compdata; int nrncnl; var = ctx->Variable[var]->CloneTable; nrncnl = ctx->Nr * ctx->Nc * ctx->Nl[var]; data = (float *) allocate_type( ctx, nrncnl*sizeof(float), GRID_TYPE ); if (!data) { return NULL; } compdata = get_compressed_grid( ctx, time, var, &ga, &gb ); if (compdata) { v5dDecompressGrid( ctx->Nr, ctx->Nc, ctx->Nl[var], ctx->CompressMode, compdata, ga, gb, data ); release_compressed_grid( ctx, time, var ); } return data; } /* time = fromctx time */ /* var = fromctx var */ float *get_grid2( Context toctx, Context fromctx, int time, int var, int numlevs ) { float *data, fdata; int nrncnl, nr, nc, nl; float yonrr, yoncc, yonll, nnr, nnc, nnl, lat, lon, hgt; var = fromctx->Variable[var]->CloneTable; nrncnl = toctx->Nr * toctx->Nc * numlevs; data = (float *) allocate_type( toctx, nrncnl*sizeof(float), GRID_TYPE ); if (!data) { return NULL; } for (nr = 0; nr < toctx->Nr; nr++){ for (nc = 0; nc < toctx->Nc; nc++){ for (nl = 0; nl < numlevs; nl++){ yonrr = (float) (nr); yoncc = (float) (nc); yonll = (float) (nl); grid_to_geo(toctx, 0, 0, 1, &yonrr, &yoncc, &yonll, &lat, &lon, &hgt); geo_to_grid(fromctx, time, var, 1, &lat, &lon, &hgt, &nnr, &nnc, &nnl); fdata = interpolate_grid_value( fromctx, time, var, nnr, nnc, nnl); data[nr+toctx->Nr*(nc+toctx->Nc*nl)] = fdata; } } } return data; } /*** put_grid ********************************************************* Store a new 3-D grid of data. Input: time, var - time and variable of the new grid griddata - the new grid data Return: 1=ok, 0=error **********************************************************************/ int put_grid( Context ctx, int time, int var, float *griddata ) { if (ctx->Variable[var]->VarType == VIS5D_REGULAR || ctx->Variable[var]->VarType == VIS5D_PUT) { return install_new_grid( ctx, time, var, griddata, ctx->Nl[var], ctx->Variable[var]->LowLev); } else { return 0; } } /*** release_grid ***************************************************** Release an uncompressed grid. Input: time, var - the timestep and parameter of the grid. data - the grid data returned by get_grid(). **********************************************************************/ void release_grid( Context ctx, int time, int var, float *data ) { assert( time>=0 && timeNumTimes ); assert( var>=0 && varNumVars ); deallocate( ctx, data, ctx->Nr*ctx->Nc*ctx->Nl[var]*sizeof(float) ); } void release_grid2( Context ctx, int time, int var, int nl, float *data ) { deallocate( ctx, data, ctx->Nr*ctx->Nc*nl*sizeof(float) ); } /* * Get a discrete grid value. * Input: time - which timestep * var - which variable * row, col, lev - location * Return: grid value or MISSING if missing. */ float get_grid_value( Context ctx, int time, int var, int row, int col, int lev ) { void *data; float *gavec, *gbvec; float value; /* WLH 6-30-95 */ lev -= ctx->Variable[var]->LowLev; if (lev < 0 || lev >= ctx->Nl[var]) return MISSING; var = ctx->Variable[var]->CloneTable; data = get_compressed_grid( ctx, time, var, &gavec, &gbvec ); if (!data) return MISSING; if (ctx->CompressMode == 1) { V5Dubyte *data1 = (V5Dubyte *) data; V5Dubyte c1 = data1[ (lev * ctx->Nc + col) * ctx->Nr + row ]; if (c1==255) { value = MISSING; } else { value = (float) (int) c1 * gavec[lev] + gbvec[lev]; } } else if (ctx->CompressMode == 2) { V5Dushort *data2 = (V5Dushort *) data; V5Dushort c2 = data2[ (lev * ctx->Nc + col) * ctx->Nr + row ]; if (c2==65535) { value = MISSING; } else { value = (float) (int) c2 * gavec[lev] + gbvec[lev]; } } else { float *data4 = (float *) data; value = data4[ (lev * ctx->Nc + col) * ctx->Nr + row ]; } release_compressed_grid( ctx, time, var ); return value; } /* * Return a grid value at an arbitrary grid position. Values will be * interpolated between neighboring values. * Input: time - timestep in [0..NumTimes-1] * var - variable in [0..NumVars-1] * row, col, lev - location in [0..Nr-1],[0..Nc-1],[0..Nl[var]-1] * Return: data value or MISSING if missing. */ float interpolate_grid_value( Context ctx, int time, int var, float row, float col, float lev ) { void *data; int i0, j0, k0, i1, j1, k1; float d0,d1,d2,d3,d4,d5,d6,d7, d; float ei, ej, ek; float *gavec, *gbvec, ga, gb; /* WLH 6-30-95 */ lev -= ctx->Variable[var]->LowLev; if (lev < 0 || lev >= ctx->Nl[var] || col < 0 || col >= ctx->Nc || row < 0 || row >= ctx->Nr){ return MISSING; } var = ctx->Variable[var]->CloneTable; data = get_compressed_grid( ctx, time, var, &gavec, &gbvec ); if (!data) return MISSING; i0 = (int) row; i1 = i0 + 1; j0 = (int) col; j1 = j0 + 1; k0 = (int) lev; k1 = k0 + 1; /* check for grid boundaries */ if (i0==ctx->Nr-1) { i1 = i0; } if (j0==ctx->Nc-1) { j1 = j0; } if (k0==ctx->Nl[var]-1) { k1 = k0; } /* error terms */ ei = row - (float) i0; ej = col - (float) j0; ek = lev - (float) k0; /* check if exactly on a row, column, or level */ if (ei==0.0) i1 = i0; if (ej==0.0) j1 = j0; if (ek==0.0) k1 = k0; if (ctx->CompressMode == 1) { /* get eight values at corners of a cube around (r,c,l) */ V5Dubyte c0,c1,c2,c3,c4,c5,c6,c7; V5Dubyte *data1 = (V5Dubyte *) data; c0 = data1[ (k0 * ctx->Nc + j0) * ctx->Nr + i0 ]; /* d0 @ (i0,j0,k0) */ c1 = data1[ (k0 * ctx->Nc + j0) * ctx->Nr + i1 ]; /* d1 @ (i1,j0,k0) */ c2 = data1[ (k0 * ctx->Nc + j1) * ctx->Nr + i0 ]; /* d2 @ (i0,j1,k0) */ c3 = data1[ (k0 * ctx->Nc + j1) * ctx->Nr + i1 ]; /* d3 @ (i1,j1,k0) */ c4 = data1[ (k1 * ctx->Nc + j0) * ctx->Nr + i0 ]; /* d4 @ (i0,j0,k1) */ c5 = data1[ (k1 * ctx->Nc + j0) * ctx->Nr + i1 ]; /* d5 @ (i1,j0,k1) */ c6 = data1[ (k1 * ctx->Nc + j1) * ctx->Nr + i0 ]; /* d6 @ (i0,j1,k1) */ c7 = data1[ (k1 * ctx->Nc + j1) * ctx->Nr + i1 ]; /* d7 @ (i1,j1,k1) */ release_compressed_grid( ctx, time, var ); /* check for missing data */ if (c0==255 || c1==255 || c2==255 || c3==255 || c4==255 || c5==255 || c6==255 || c7==255) { return MISSING; } /* decompress the values */ ga = gavec[k0]; gb = gbvec[k0]; d0 = (float) (int) c0 * ga + gb; d1 = (float) (int) c1 * ga + gb; d2 = (float) (int) c2 * ga + gb; d3 = (float) (int) c3 * ga + gb; ga = gavec[k1]; gb = gbvec[k1]; d4 = (float) (int) c4 * ga + gb; d5 = (float) (int) c5 * ga + gb; d6 = (float) (int) c6 * ga + gb; d7 = (float) (int) c7 * ga + gb; } else if (ctx->CompressMode == 2) { V5Dushort c0,c1,c2,c3,c4,c5,c6,c7; V5Dushort *data2 = (V5Dushort *) data; /* get eight values at corners of a cube around (r,c,l) */ c0 = data2[ (k0 * ctx->Nc + j0) * ctx->Nr + i0 ]; /* d0 @ (i0,j0,k0) */ c1 = data2[ (k0 * ctx->Nc + j0) * ctx->Nr + i1 ]; /* d1 @ (i1,j0,k0) */ c2 = data2[ (k0 * ctx->Nc + j1) * ctx->Nr + i0 ]; /* d2 @ (i0,j1,k0) */ c3 = data2[ (k0 * ctx->Nc + j1) * ctx->Nr + i1 ]; /* d3 @ (i1,j1,k0) */ c4 = data2[ (k1 * ctx->Nc + j0) * ctx->Nr + i0 ]; /* d4 @ (i0,j0,k1) */ c5 = data2[ (k1 * ctx->Nc + j0) * ctx->Nr + i1 ]; /* d5 @ (i1,j0,k1) */ c6 = data2[ (k1 * ctx->Nc + j1) * ctx->Nr + i0 ]; /* d6 @ (i0,j1,k1) */ c7 = data2[ (k1 * ctx->Nc + j1) * ctx->Nr + i1 ]; /* d7 @ (i1,j1,k1) */ release_compressed_grid( ctx, time, var ); /* check for missing data */ if (c0==65535 || c1==65535 || c2==65535 || c3==65535 || c4==65535 || c5==65535 || c6==65535 || c7==65535) { return MISSING; } /* decompress the values */ ga = gavec[k0]; gb = gbvec[k0]; d0 = (float) (int) c0 * ga + gb; d1 = (float) (int) c1 * ga + gb; d2 = (float) (int) c2 * ga + gb; d3 = (float) (int) c3 * ga + gb; ga = gavec[k1]; gb = gbvec[k1]; d4 = (float) (int) c4 * ga + gb; d5 = (float) (int) c5 * ga + gb; d6 = (float) (int) c6 * ga + gb; d7 = (float) (int) c7 * ga + gb; } else { float *data4 = (float *) data; /* get eight values at corners of a cube around (r,c,l) */ d0 = data4[ (k0 * ctx->Nc + j0) * ctx->Nr + i0 ]; /* d0 @ (i0,j0,k0) */ d1 = data4[ (k0 * ctx->Nc + j0) * ctx->Nr + i1 ]; /* d1 @ (i1,j0,k0) */ d2 = data4[ (k0 * ctx->Nc + j1) * ctx->Nr + i0 ]; /* d2 @ (i0,j1,k0) */ d3 = data4[ (k0 * ctx->Nc + j1) * ctx->Nr + i1 ]; /* d3 @ (i1,j1,k0) */ d4 = data4[ (k1 * ctx->Nc + j0) * ctx->Nr + i0 ]; /* d4 @ (i0,j0,k1) */ d5 = data4[ (k1 * ctx->Nc + j0) * ctx->Nr + i1 ]; /* d5 @ (i1,j0,k1) */ d6 = data4[ (k1 * ctx->Nc + j1) * ctx->Nr + i0 ]; /* d6 @ (i0,j1,k1) */ d7 = data4[ (k1 * ctx->Nc + j1) * ctx->Nr + i1 ]; /* d7 @ (i1,j1,k1) */ release_compressed_grid( ctx, time, var ); /* check for missing data */ if (IS_MISSING(d0) || IS_MISSING(d1) || IS_MISSING(d2) || IS_MISSING(d3) || IS_MISSING(d4) || IS_MISSING(d5) || IS_MISSING(d6) || IS_MISSING(d7)) { return MISSING; } } /* compute weighted value */ d = ( d0 * (1.0-ei) * (1.0-ej) + d1 * ei * (1.0-ej) + d2 * (1.0-ei) * ej + d3 * ei * ej ) * (1.0-ek) + ( d4 * (1.0-ei) * (1.0-ej) + d5 * ei * (1.0-ej) + d6 * (1.0-ei) * ej + d7 * ei * ej ) * ek; return d; } /*** allocate_clone_variable ***************************************** Allocate a new variable which is to be a clone of an existing one. This function takes care of all the grid data structure housekeeping. Input: name - name of the new variable. var_to_clone - number of the existing variable which we will clone (in [0..ctx->NumVars-1]). Return: -1 = error otherwise, return the number of the new variable. **********************************************************************/ int allocate_clone_variable( Context ctx, char name[], int var_to_clone ) { int newvar; if (ctx->NumVars==MAXVARS) { /* no space for a new variable */ return -1; } newvar = ctx->NumVars; ctx->Variable[newvar] = (vis5d_variable *) calloc(1,sizeof(vis5d_variable)); ctx->Variable[newvar]->VarType = VIS5D_CLONE; ctx->Variable[newvar]->CloneTable = var_to_clone; ctx->NumVars++; /* copy variable/parameter name */ strncpy( ctx->Variable[newvar]->VarName, name, 8 ); ctx->Nl[newvar] = ctx->Nl[var_to_clone]; ctx->Variable[newvar]->LowLev = ctx->Variable[var_to_clone]->LowLev; /* copy min, max values */ ctx->Variable[newvar]->MinVal = ctx->Variable[var_to_clone]->MinVal; ctx->Variable[newvar]->MaxVal = ctx->Variable[var_to_clone]->MaxVal; /* MJK 12.14.98 */ strcpy (ctx->Variable[newvar]->Units, ctx->Variable[var_to_clone]->Units); return newvar; } /* * Initialize ctx->MinVal and ctx->MaxVal for install_new_grid. * Input: newvar - index of variable to initialize */ void min_max_init( Context ctx, int newvar ) { ctx->Variable[newvar]->MinVal = MISSING; ctx->Variable[newvar]->MaxVal = -MISSING; } /* * Allocate a new variable which is computed by an external Fortran program. * Input: name - name of the new variable. * Return: -1 = error * otherwise, return the number of the new variable. */ int allocate_extfunc_variable( Context ctx, char name[] ) { int newvar; for (newvar=0;newvarVariable[newvar]->VarType==0) break; } if (newvar==MAXVARS) { /* no space for a new variable */ return -1; } ctx->Variable[newvar]->VarType = VIS5D_EXT_FUNC; ctx->Variable[newvar]->CloneTable = newvar; ctx->NumVars++; strncpy( ctx->Variable[newvar]->VarName, name, 8 ); min_max_init(ctx, newvar); return newvar; } /* * Allocate a new variable which is computed as a function of the other * variables by a simple typed-in expression. * Input: name - the name of the new variable * Return: -1 = error (MAXVARS exceeded) * otherwise, return the number of the new variable. */ int allocate_computed_variable( Context ctx, char *name ) { int newvar; for (newvar=0;newvarVariable[newvar]->VarType==0) break; } if (newvar==MAXVARS) { /* no space for a new variable */ return -1; } ctx->Variable[newvar]->VarType = VIS5D_EXPRESSION; ctx->Variable[newvar]->CloneTable = newvar; ctx->NumVars++; strncpy( ctx->Variable[newvar]->VarName, name, 8 ); min_max_init(ctx, newvar); return newvar; } /* * Allocate a new variable which is initialized to MISSING to be * filled in by calls to vis5d_put_grid * Input: name - the name of the new variable * Return: -1 = error (MAXVARS exceeded) * otherwise, return the number of the new variable. */ int allocate_new_variable( Context ctx, char *name, int nl, int lowlev ) { int newvar, time, gridsize, i; float *griddata; for (newvar=0;newvarVariable[newvar]->VarType==0) break; } if (newvar==MAXVARS) { /* no space for a new variable */ return -1; } ctx->Variable[newvar]->VarType = VIS5D_PUT; ctx->Variable[newvar]->CloneTable = newvar; ctx->NumVars++; ctx->Nl[newvar] = nl; ctx->Variable[newvar]->LowLev = lowlev; strncpy( ctx->Variable[newvar]->VarName, name, 8 ); min_max_init(ctx, newvar); gridsize = ctx->Nr * ctx->Nc * nl * 4; griddata = (float *) allocate ( ctx, gridsize ); for (i=0; iNumTimes; time++) { put_grid( ctx, time, newvar, griddata ); } deallocate( ctx, griddata, gridsize ); return newvar; } /*** deallocate_variable ********************************************** deallocate the last variable made with one of the 2 previous functions. This should only be used in case of an error when cloning/computing a new variable. **********************************************************************/ int deallocate_variable( Context ctx, int var ) { /* This function isn't completely implemented nor is it used yet */ assert( var == ctx->NumVars-1 ); ctx->Variable[var]->VarType = 0; ctx->NumVars--; return 0; } /*** install_new_grid ************************************************* When new grids are computed by an external analysis function, we call this function to install the data into Vis5D's globals data structures. Input: it, var - times step and variable number of the grid which we're installing. griddata - array of grid data nl - number of levels in new grid (we know Nr and Nc) lowlev - lowest level in new grid Return: 1=ok, 0=error **********************************************************************/ int install_new_grid( Context ctx, int time, int var, float *griddata, int nl, int lowlev) { float min, max; ctx->Nl[var] = nl; ctx->Variable[var]->LowLev = lowlev; if (!ctx->GridTable[time][var].Data) { int bytes = ctx->Nr * ctx->Nc * nl * ctx->CompressMode; ctx->GridTable[time][var].Data = (void *) allocate( ctx, bytes ); if (ctx->Ga[time][var]){ deallocate( ctx, ctx->Ga[time][var], -1); ctx->Ga[time][var] = NULL; } if (ctx->Gb[time][var]){ deallocate( ctx, ctx->Gb[time][var], -1); ctx->Gb[time][var] = NULL; } ctx->Ga[time][var] = (float *) allocate( ctx, nl * sizeof(float) ); ctx->Gb[time][var] = (float *) allocate( ctx, nl * sizeof(float) ); if (!ctx->GridTable[time][var].Data || !ctx->Ga[time][var] || !ctx->Gb[time][var]) { printf("Out of memory, couldn't save results of external "); printf("function computation.\n"); return 0; } } /* compress the data */ v5dCompressGrid( ctx->Nr, ctx->Nc, nl, ctx->CompressMode, griddata, ctx->GridTable[time][var].Data, ctx->Ga[time][var], ctx->Gb[time][var], &min, &max ); ctx->GridTable[time][var].CachePos = -1; /* update min and max values */ if (minVariable[var]->MinVal) { ctx->Variable[var]->MinVal = min; ctx->Variable[var]->RealMinVal = min; } if (max>ctx->Variable[var]->MaxVal) { ctx->Variable[var]->MaxVal = max; ctx->Variable[var]->RealMaxVal = max; } return 1; }