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

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

Allow urs_ext to store only a subset of gauges in mux file according to a list of ordered points

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