source: anuga_core/source/anuga/shallow_water/urs_ext.c @ 5469

Last change on this file since 5469 was 5469, checked in by ole, 14 years ago

John and Ole updated urs_ext.c to allow multiple source files to be read in sequence using only the memory to read one file

File size: 16.0 KB
Line 
1/*
2gcc -fPIC -c urs_ext.c -I/usr/include/python2.5 -o urs_ext.o -Wall -O
3gcc -shared urs_ext.o  -o urs_ext.so
4*/
5#include "Python.h"
6#include "Numeric/arrayobject.h"
7#include "math.h"
8#include <stdio.h>
9#include <float.h>
10#include <time.h>
11#include "structure.h"
12
13#define MAX_FILE_NAME_LENGTH 128
14#define NODATA 99.0
15#define EPSILON  0.00001
16
17#define DEBUG 0
18
19#define POFFSET 5 //Number of site_params
20
21void fillDataArray(int, int, int, int, int *, int *, float *, int *, int *, float *);
22long getNumData(int*, int*, int);
23char isdata(float);
24void wrttxt(char *, float, int, float *, float, float, float, float, float, int, int, int);
25float** _read_mux2(int, char **, float *, double *, int);
26
27PyObject *read_mux2(PyObject *self, PyObject *args){
28/*Read in mux 2 file
29   
30    Python call:
31    read_mux2(numSrc,filenames,weights,file_params,write)
32
33    NOTE:
34    A Python int is equivalent to a C long
35    A Python double corresponds to a C double
36*/
37  PyObject *filenames;
38  PyArrayObject *pyweights,*file_params;
39  PyArrayObject *pydata;
40  PyObject *fname;
41
42  char **muxFileNameArray;
43  float *weights;
44  long numSrc;
45  long write;
46
47  float **cdata;
48  int dimensions[2];
49  int nsta0;
50  int nt;
51  double dt;
52  int i,j;
53  int start_tstep;
54  int finish_tstep;
55  int it,time;
56  int num_ts;
57 
58  // Convert Python arguments to C
59  if (!PyArg_ParseTuple(args, "iOOOi",
60                        &numSrc,&filenames,&pyweights,&file_params,&write)) {                   
61                       
62    PyErr_SetString(PyExc_RuntimeError, 
63                    "Input arguments to read_mux2 failed");
64    return NULL;
65  }
66
67  if(!PyList_Check(filenames)) {
68    PyErr_SetString(PyExc_TypeError, "get_first_elem expects a list");
69    return NULL;
70  }
71
72  if(PyList_Size(filenames) == 0){
73    PyErr_SetString(PyExc_ValueError, "empty lists not allowed");
74    return NULL;
75  }
76
77  if (pyweights->nd != 1 || pyweights->descr->type_num != PyArray_DOUBLE) {
78    PyErr_SetString(PyExc_ValueError,
79                    "pyweights must be one-dimensional and of type double");
80    return NULL; 
81  }
82
83  if(PyList_Size(filenames) != pyweights->dimensions[0]){
84      PyErr_SetString(PyExc_ValueError, "Must specify one weight for each filename");
85      return NULL;
86  }
87
88  muxFileNameArray = (char **) malloc((int)numSrc*sizeof(char *));
89  if (muxFileNameArray == NULL) {
90     printf("ERROR: Memory for muxFileNameArray could not be allocated.\n");
91     exit(-1);
92  }
93  for (i=0;i<PyList_Size(filenames);i++){
94    muxFileNameArray[i] = (char *) malloc((MAX_FILE_NAME_LENGTH+1)*sizeof(char));
95    if (muxFileNameArray[i] == NULL) {
96      printf("ERROR: Memory for muxFileNameArray could not be allocated.\n");
97      exit(-1);
98    }
99    fname=PyList_GetItem(filenames, i);
100    if (!PyString_Check(fname)) {
101      PyErr_SetString(PyExc_ValueError, "filename not a string");
102    }
103    muxFileNameArray[i]=PyString_AsString(fname);
104  }
105
106  if (file_params->nd != 1 || file_params->descr->type_num != PyArray_DOUBLE) {
107    PyErr_SetString(PyExc_ValueError,
108                    "file_params must be one-dimensional and of type double");
109    return NULL; 
110  }
111
112  // Create array for weights which are passed to read_mux2
113  weights = (float *) malloc((int)numSrc*sizeof(float));
114  for (i=0;i<(int)numSrc;i++){
115    weights[i]=(float)(*(double *)(pyweights->data+i*pyweights->strides[0]));
116  }
117
118  // Read in mux2 data from file
119  cdata=_read_mux2((int)numSrc,muxFileNameArray,weights,(double*)file_params->data,(int)write);
120
121
122  // Allocate space for return vector
123  nsta0=(int)*(double *) (file_params -> data+0*file_params->strides[0]);
124  dt=*(double *) (file_params -> data+1*file_params->strides[0]);
125  nt=(int)*(double *) (file_params -> data+2*file_params->strides[0]);
126
127 
128  // Find min and max start times of all gauges
129  start_tstep=nt+1;
130  finish_tstep=-1;
131  for (i=0;i<nsta0;i++){
132    if ((int)cdata[i][nt+3] < start_tstep){
133      start_tstep=(int)cdata[i][nt+3];
134    }
135    if ((int)cdata[i][nt+4] > finish_tstep){
136      finish_tstep=(int)cdata[i][nt+4]; 
137    }
138  }
139
140  if ((start_tstep>nt) | (finish_tstep < 0)){
141    printf("ERROR: Gauge data has incorrect start and finsh times\n");
142    return NULL;
143  }
144
145  if (start_tstep>=finish_tstep){
146    printf("ERROR: Gauge data has non-postive_length\n");
147    return NULL;
148  }
149
150  num_ts=finish_tstep-start_tstep+1;
151  dimensions[0]=nsta0;
152  dimensions[1]=num_ts+POFFSET;
153  pydata = (PyArrayObject *) PyArray_FromDims(2, dimensions, PyArray_DOUBLE);
154  if(pydata == NULL){
155    printf("ERROR: Memory for pydata array could not be allocated.\n");
156    return NULL;
157  }
158
159  // Each gauge begins and ends recording at different times. When a gauge is
160  // not recording but at least one other gauge is. Pad the non-recording gauge
161  // array with zeros.
162  for (i=0;i<nsta0;i++){
163    time=0;
164    for (it=0;it<finish_tstep;it++){
165      if((it+1>=start_tstep)&&(it+1<=finish_tstep)){
166        if (it+1>(int)cdata[i][nt+4]){
167          //This gauge has stopped recording but others are still recording
168        *(double *) (pydata -> data+i*pydata->strides[0]+time*pydata->strides[1])=0.0;
169        }else{
170          *(double *) (pydata -> data+i*pydata->strides[0]+time*pydata->strides[1])=cdata[i][it];
171        }
172        time++;
173      }
174    }
175    // Pass back lat,lon,elevation
176    for (j=0; j<POFFSET; j++){
177      *(double *) (pydata -> data+i*pydata->strides[0]+(num_ts+j)*pydata->strides[1])=cdata[i][nt+j];
178     }
179  }
180
181  free(weights);
182  free(muxFileNameArray);
183  return  PyArray_Return(pydata);
184
185}
186
187float** _read_mux2(int numSrc, char **muxFileNameArray, float *weights, double *params, int write)
188{
189   FILE *fp;
190   int nsta, nsta0, i, isrc, ista;
191   struct tgsrwg *mytgs, *mytgs0;
192   char *muxFileName;                                                                 
193   int istart, istop;
194   int *fros, *lros;
195   char susMuxFileName;
196   float *muxData;
197   long numData;
198
199
200   int len_sts_data;
201   float **sts_data;
202   float *temp_sts_data;
203   
204   long int offset;
205
206   /* Allocate space for the names and the weights and pointers to the data*/   
207   
208   /* Check that the input files have mux2 extension*/
209   susMuxFileName=0;
210   for(isrc=0;isrc<numSrc;isrc++)
211     { 
212       muxFileName=muxFileNameArray[isrc];
213       if(!susMuxFileName && strcmp(muxFileName+strlen(muxFileName)-4,"mux2")!=0){
214         susMuxFileName=1;
215       }
216     }
217   
218   if(susMuxFileName)
219   {
220      printf("\n**************************************************************************\n");
221      printf("   WARNING: This program operates only on multiplexed files in mux2 format\n"); 
222      printf("   At least one input file name does not end with mux2\n");
223      printf("   Check your results carefully!\n");
224      printf("**************************************************************************\n\n");
225   }   
226                     
227   
228   /* open the first muxfile */
229   if((fp=fopen(muxFileNameArray[0],"r"))==NULL)
230   {
231      fprintf(stderr, "cannot open file %s\n", muxFileNameArray[0]);
232      exit(-1); 
233   }
234 
235   /* read in the header */
236   /*first read the number of stations*/   
237   fread(&nsta0,sizeof(int),1,fp);
238   
239   /*now allocate space for, and read in, the structures for each station*/
240   mytgs0 = (struct tgsrwg *) malloc(nsta0*sizeof(struct tgsrwg));
241   fread(&mytgs0[0], nsta0*sizeof(struct tgsrwg), 1, fp);
242
243   /*make an array to hold the start and stop steps for each station for each
244     source*/   
245   //FIXME: Should only store start and stop times for one source
246   fros = (int *) malloc(nsta0*numSrc*sizeof(int));
247   lros = (int *) malloc(nsta0*numSrc*sizeof(int));
248   
249   /* read the start and stop steps for source 0 into the array */   
250   fread(fros,nsta0*sizeof(int),1,fp);
251   fread(lros,nsta0*sizeof(int),1,fp);
252
253   /* compute the size of the data block for source 0 */   
254   numData = getNumData(fros, lros, nsta0);
255
256   /* Burbidge: Added a sanity check here */
257   if (numData < 0) {
258     fprintf(stderr,"Size of data block appears to be negative!\n");
259     exit(-1);
260   }
261   fclose(fp); 
262
263   // Allocate header array for each remaining source file.
264   // FIXME: only need to store for one source and which is compared to all subsequent
265   // source headers
266   if(numSrc > 1){
267      /* allocate space for tgsrwg for the other sources */
268      mytgs = (struct tgsrwg *)malloc( nsta0*sizeof(struct tgsrwg) );
269   } else {
270     /* FIXME (JJ): What should happen in case the are no source files?*/
271     /* If we exit here, tests will break */
272     // fprintf(stderr, "No source file specified\n");
273     // exit(-1);       
274   }
275   
276   /* loop over remaining sources, check compatibility, and read them into
277    *muxData */
278   for(isrc=1; isrc<numSrc; isrc++){
279     muxFileName = muxFileNameArray[isrc];
280     
281     /* open the mux file */
282     if((fp=fopen(muxFileName,"r"))==NULL)
283       {
284         fprintf(stderr, "cannot open file %s\n", muxFileName);
285         exit(-1); 
286       }
287     
288     /* check that the mux files are compatible */     
289     fread(&nsta,sizeof(int),1,fp);
290     if(nsta != nsta0){
291       fprintf(stderr,"%s has different number of stations to %s\n", muxFileName, muxFileNameArray[0]);
292       fclose(fp);
293       exit(-1);   
294     }
295     fread(&mytgs[0], nsta*sizeof(struct tgsrwg), 1, fp);
296     for(ista=0; ista < nsta; ista++){
297       if(mytgs[ista].dt != mytgs0[ista].dt){
298         fprintf(stderr,"%s has different sampling rate to %s\n", muxFileName, muxFileNameArray[0]);
299         fclose(fp);
300         exit(-1);                 
301       }   
302       if(mytgs[ista].nt != mytgs0[ista].nt){
303         fprintf(stderr,"%s has different series length to %s\n", muxFileName, muxFileNameArray[0]);
304         fclose(fp);
305         exit(-1);                 
306       }
307     }
308
309      /* read the start and stop times for this source */
310      fread(fros+isrc*nsta0,nsta0*sizeof(int),1,fp);
311      fread(lros+isrc*nsta0,nsta0*sizeof(int),1,fp);
312     
313      /* compute the size of the data block for this source */
314      numData = getNumData(fros+isrc*nsta0, lros+isrc*nsta0, nsta0);
315
316      /* Burbidge: Added a sanity check here */
317      if (numData < 0){
318          fprintf(stderr,"Size of data block appears to be negative!\n");
319          exit(-1);
320      }
321      fclose(fp);             
322   }
323   params[0]=(double)nsta0;
324   params[1]=(double)mytgs0[0].dt;
325   params[2]=(double)mytgs0[0].nt;
326   
327   /* make array(s) to hold the demuxed data */
328   //FIXME: This can be reduced to only contain stations in order file
329   sts_data = (float **)malloc (nsta0 * sizeof(float *));
330   
331   if (sts_data == NULL){
332     printf("ERROR: Memory for sts_data could not be allocated.\n");
333     exit(-1);
334   }
335   
336   len_sts_data=mytgs0[0].nt + POFFSET;
337   for (ista=0; ista<nsta0; ista++){
338     // Initialise sts_data to zero
339     sts_data[ista] = (float *)calloc(len_sts_data, sizeof(float) );
340     if (sts_data[ista] == NULL){
341       printf("ERROR: Memory for sts_data could not be allocated.\n");
342       exit(-1);
343     }
344     sts_data[ista][mytgs0[0].nt]=(float)mytgs0[ista].geolat;
345     sts_data[ista][mytgs0[0].nt+1]=(float)mytgs0[ista].geolon;
346     sts_data[ista][mytgs0[0].nt+2]=(float)mytgs0[ista].z;
347     sts_data[ista][mytgs0[0].nt+3]=(float)fros[ista];
348     sts_data[ista][mytgs0[0].nt+4]=(float)lros[ista];
349   }
350
351   temp_sts_data = (float *)calloc(len_sts_data, sizeof(float) );
352     
353   /* Loop over all sources */
354   //FIXME: remove istart and istop they are not used.
355   istart = -1;
356   istop = -1;
357   for (isrc=0;isrc<numSrc;isrc++){
358     /* Read in data block from mux2 file */
359     muxFileName = muxFileNameArray[isrc];
360     if((fp=fopen(muxFileName,"r"))==NULL)
361       {
362         //fprintf(stderr, "%s: cannot open file %s\n", av[0], muxFileName);
363         fprintf(stderr, "cannot open file %s\n", muxFileName);
364         exit(-1); 
365       }
366
367     offset=sizeof(int)+nsta0*(sizeof(struct tgsrwg)+2*sizeof(int));
368     fseek(fp,offset,0);
369     
370     numData = getNumData(fros+isrc*nsta0, lros+isrc*nsta0, nsta0);
371     muxData = (float*) malloc(numData*sizeof(float));
372     fread(muxData, numData*sizeof(float),1,fp); 
373     fclose(fp);         
374
375     /* loop over stations */
376     for(ista=0; ista<nsta0; ista++){               
377       
378       /* fill the data0 array from the mux file, and weight it */
379       fillDataArray(ista, nsta0, mytgs0[ista].nt, mytgs0[ista].ig, fros+isrc*nsta0, lros+isrc*nsta0, temp_sts_data, &istart, &istop, muxData);
380       
381       /* weight appropriately and add */
382       for(i=0; i<mytgs0[ista].nt; i++){
383         if((isdata(sts_data[ista][i])) && isdata(temp_sts_data[i])){
384           sts_data[ista][i] += temp_sts_data[i] * weights[isrc];
385           //printf("%d,%d,%f\n",ista,i,sts_data[ista][i]);
386         }else{
387           sts_data[ista][i] = NODATA;
388         }
389       }
390     }
391   }
392   free(muxData);
393   free(temp_sts_data);
394   return sts_data;
395}   
396
397
398/* thomas */
399void fillDataArray(int ista, int nsta, int nt, int ig, int *nst, int *nft, float *data, int *istart_p, int *istop_p, float *muxData)
400{
401   int it, last_it, jsta;
402   long int offset=0;
403
404
405   last_it=-1;
406   /* make arrays of starting and finishing time steps for the tide gauges */
407   /* and fill them from the file */
408     
409   /* update start and stop timesteps for this gauge */
410   if(nst[ista]!=-1){
411     if(*istart_p==-1){
412         *istart_p=nst[ista];
413     }else{
414         *istart_p=((nst[ista]<*istart_p)?nst[ista]:*istart_p);
415     }
416   }
417   if(nft[ista]!=-1){
418     if(*istop_p==-1){
419         *istop_p=nft[ista];
420     }else{
421         *istop_p=((nft[ista]<*istop_p)?nft[ista]:*istop_p);
422     }
423   }     
424   if(ig==-1 || nst[ista] == -1) /* currently ig==-1 => nst[ista]==-1 */
425   {
426      /* gauge never started recording, or was outside of all grids, fill array with 0 */
427      for(it=0; it<nt; it++)
428         data[it] = 0.0;
429   }   
430   else
431   {
432      for(it=0; it<nt; it++)
433      {
434         last_it = it;
435         /* skip t record of data block */
436         offset++;
437         /* skip records from earlier tide gauges */
438         for(jsta=0; jsta<ista; jsta++)
439            if(it+1>=nst[jsta]&&it+1<=nft[jsta])
440               offset++;
441               
442         /* deal with the tide gauge at hand */
443         if(it+1>=nst[ista]&&it+1<=nft[ista])
444         /* gauge is recording at this time */
445         {
446            memcpy(data+it,muxData+offset,sizeof(float));
447            offset++;
448         }
449         else if (it+1<nst[ista])
450         {
451            /* gauge has not yet started recording */
452            data[it] = 0.0;
453         }   
454         else
455         /* gauge has finished recording */                                           
456         {
457            data[it] = NODATA;
458            break;
459         }
460   
461         /* skip records from later tide gauges */
462         for(jsta=ista+1; jsta<nsta; jsta++)
463            if(it+1>=nst[jsta]&&it+1<=nft[jsta])
464               offset++;
465      }
466   
467      if(last_it < nt - 1)
468         /* the loop was exited early because the gauge had finished recording */
469         for(it=last_it+1; it < nt; it++)
470            data[it] = NODATA;
471   }
472} 
473
474/* Burbidge: No "offset" is sent. Replace with max. Added grid_id */
475void wrttxt( fname, dt, nt, x, beg, lat, lon, max, depth, grid_id, istart, istop )
476char *fname;
477float dt, *x, beg, max, lat, lon, depth;
478int grid_id;
479int nt;
480int istart, istop;
481{
482   int it;
483   float t;
484   FILE *fp;
485
486   fp = fopen(fname,"w");
487   fprintf(fp,"> lat %g lon %g depth %g max %g start_time %g stop_time %g grid_id %d\n", lat, lon, depth, max, 
488            istart*dt, istop*dt, grid_id );
489   for(it=0;it<nt;it++)
490   {
491      t=beg+it*dt;
492      fprintf(fp,"%9.3f %g\n", t, x[it]);
493   }
494   fclose(fp);
495}
496 
497char isdata(float x)
498{
499  //char value;
500   if(x < NODATA + EPSILON && NODATA < x + EPSILON)
501      return 0;
502   else
503      return 1; 
504}
505
506long getNumData(int *fros, int *lros, int nsta)
507/* calculates the number of data in the data block of a mux file */
508/* based on the first and last recorded output steps for each gauge */ 
509{
510   int ista, last_output_step;
511   long numData = 0;
512
513   last_output_step = 0;   
514   for(ista=0; ista < nsta; ista++)
515      if(*(fros + ista) != -1)
516      {
517         numData += *(lros + ista) - *(fros + ista) + 1;
518         last_output_step = (last_output_step < *(lros+ista) ? *(lros+ista):last_output_step);
519      }   
520   numData += last_output_step*nsta; /* these are the t records */
521   return numData;
522}   
523
524
525
526//-------------------------------
527// Method table for python module
528//-------------------------------
529static struct PyMethodDef MethodTable[] = {
530  {"read_mux2", read_mux2, METH_VARARGS, "Print out"},
531  {NULL, NULL}
532};
533
534// Module initialisation
535void initurs_ext(void){
536  Py_InitModule("urs_ext", MethodTable);
537
538  import_array(); // Necessary for handling of NumPY structures
539}
Note: See TracBrowser for help on using the repository browser.