source: branches/source_numpy_conversion/anuga/shallow_water/urs_ext.c @ 6768

Last change on this file since 6768 was 5904, checked in by rwilson, 16 years ago

NumPy? conversion.

File size: 23.2 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
6#include "Python.h"
7#include "numpy/arrayobject.h"
8#include "structure.h"
9#include "math.h"
10#include <stdio.h>
11#include <float.h>
12#include <time.h>
13
14#define MAX_FILE_NAME_LENGTH 128
15#define NODATA 99.0
16#define EPSILON  0.00001
17
18#define DEBUG 0
19
20#define POFFSET 5 //Number of site_params
21
22static int *fros=NULL; 
23static int *lros=NULL;
24static struct tgsrwg* mytgs0=NULL;
25
26static long numDataMax=0;
27
28
29/*The MUX file format
30
31
32 */
33
34
35
36/////////////////////////////////////////////////////////////////////////
37//Auxiliary functions
38void fillDataArray(int ista, int total_number_of_stations, int nt, int ig, int *nst, 
39                   int *nft, float *data, int *istart_p, 
40                   int *istop_p, float *muxData)
41{
42    int it, last_it, jsta;
43    long int offset=0;
44
45
46    last_it = -1;
47    /* Make arrays of starting and finishing time steps for the tide gauges */
48    /* and fill them from the file */
49
50    /* Update start and stop timesteps for this gauge */
51    if (nst[ista]!= -1)
52    {
53        if(*istart_p == -1)
54        {
55            *istart_p = nst[ista];
56        }
57        else
58        {
59            *istart_p = ((nst[ista] < *istart_p) ? nst[ista] : *istart_p);
60        }
61    }
62   
63    if (nft[ista] != -1)
64    {
65        if (*istop_p == -1)
66        {
67            *istop_p = nft[ista];
68        }
69        else
70        {
71            *istop_p = ((nft[ista] < *istop_p) ? nft[ista] : *istop_p);
72        }
73    }     
74   
75    if (ig == -1 || nst[ista] == -1) /* currently ig==-1 => nst[ista]==-1 */
76    {
77        /* gauge never started recording, or was outside of all grids,
78        fill array with 0 */
79        for(it = 0; it < nt; it++)
80        {
81            data[it] = 0.0;
82        }
83    }   
84    else
85    {
86        for(it = 0; it < nt; it++)
87        {
88            last_it = it;
89            /* skip t record of data block */
90            offset++;
91            /* skip records from earlier tide gauges */
92            for(jsta = 0; jsta < ista; jsta++)
93                if(it + 1 >= nst[jsta] && it + 1 <= nft[jsta])
94                    offset++;
95
96            /* deal with the tide gauge at hand */
97            if(it + 1 >= nst[ista] && it + 1 <= nft[ista])
98            {
99                /* gauge is recording at this time */
100                memcpy(data + it, muxData + offset, sizeof(float));
101                offset++;
102            }
103            else if (it + 1 < nst[ista])
104            {
105                /* gauge has not yet started recording */
106                data[it] = 0.0;
107            }   
108            else
109                /* gauge has finished recording */
110            {
111                data[it] = NODATA;
112                break;
113            }
114
115            /* skip records from later tide gauges */
116            for(jsta = ista + 1; jsta < total_number_of_stations; jsta++)
117                if(it + 1 >= nst[jsta] && it+1 <= nft[jsta])
118                    offset++;
119        }
120
121        if(last_it < nt - 1)
122            /* the loop was exited early because the gauge had
123            finished recording */
124            for(it = last_it+1; it < nt; it++)
125                data[it] = NODATA;
126    }
127} 
128
129
130char isdata(float x)
131{
132    if(x < NODATA + EPSILON && NODATA < x + EPSILON)
133    {
134      return 0;
135    }
136    else
137    {
138      return 1; 
139    }
140}
141
142
143long getNumData(const int *fros, const int *lros, const int total_number_of_stations)
144/* calculates the number of data in the data block of a mux file */
145/* based on the first and last recorded output steps for each gauge */ 
146{
147    int ista, last_output_step;
148    long numData = 0;
149
150    last_output_step = 0;   
151    for(ista = 0; ista < total_number_of_stations; ista++)
152        if(*(fros + ista) != -1)
153        {
154            numData += *(lros + ista) - *(fros + ista) + 1;
155            last_output_step = (last_output_step < *(lros+ista) ? 
156                            *(lros+ista):last_output_step);
157        }   
158        numData += last_output_step*total_number_of_stations; /* these are the t records */
159        return numData;
160}
161
162/////////////////////////////////////////////////////////////////////////
163//Internal Functions
164int _read_mux2_headers(int numSrc, 
165                       char **muxFileNameArray, 
166                       int* total_number_of_stations,
167                       int* number_of_time_steps,
168                       double* delta_t,
169                       //long* numDataMax,
170                       int verbose)
171{
172    FILE *fp;
173    int numsta, i, j;
174    struct tgsrwg *mytgs=0;
175    char *muxFileName;                                                                 
176    char susMuxFileName;
177    long numData;
178
179    /* Allocate space for the names and the weights and pointers to the data*/
180
181    /* Check that the input files have mux2 extension*/
182    susMuxFileName = 0;
183    for(i = 0; i < numSrc; i++)
184    { 
185        muxFileName = muxFileNameArray[i];
186        if(!susMuxFileName && strcmp(muxFileName + strlen(muxFileName) - 4, 
187                     "mux2") != 0)
188        {
189            susMuxFileName = 1;
190            break;
191        }
192    }
193
194    if(susMuxFileName)
195    {
196        printf("\n**************************************************************************\n");
197        printf("   WARNING: This program operates only on multiplexed files in mux2 format\n"); 
198        printf("   At least one input file name does not end with mux2\n");
199        printf("   Check your results carefully!\n");
200        printf("**************************************************************************\n\n");
201    }   
202
203    if (verbose)
204    {
205        printf("Reading mux header information\n");
206    }
207
208    // Loop over all sources, read headers and check compatibility
209    for (i = 0; i < numSrc; i++)
210    {
211        muxFileName = muxFileNameArray[i];
212
213        // Open the mux file
214        if((fp = fopen(muxFileName, "r")) == NULL)
215        {
216            fprintf(stderr, "cannot open file %s\n", muxFileName);
217            return 0; 
218        }
219       
220        if (!i)
221        {
222            fread(total_number_of_stations, sizeof(int), 1, fp);
223       
224            fros = (int*) malloc(*total_number_of_stations*numSrc*sizeof(int)); 
225            lros = (int*) malloc(*total_number_of_stations*numSrc*sizeof(int));
226     
227            mytgs0 = (struct tgsrwg*) malloc(*total_number_of_stations*sizeof(struct tgsrwg));
228            mytgs = (struct tgsrwg*) malloc(*total_number_of_stations*sizeof(struct tgsrwg));
229
230            fread(mytgs0, *total_number_of_stations*sizeof(struct tgsrwg), 1, fp);
231        }
232        else
233        {
234            // Check that the mux files are compatible
235            fread(&numsta, sizeof(int), 1, fp);
236            if(numsta != *total_number_of_stations)
237            {
238                fprintf(stderr,"%s has different number of stations to %s\n", 
239                muxFileName, 
240                muxFileNameArray[0]);
241                fclose(fp);
242                return 0;   
243            }
244
245            fread(mytgs, numsta*sizeof(struct tgsrwg), 1, fp); 
246           
247            for (j = 0; j < numsta; j++)
248            {
249                if (mytgs[j].dt != mytgs0[j].dt)
250                {
251                    fprintf(stderr, "%s has different sampling rate to %s\n", 
252                    muxFileName, 
253                    muxFileNameArray[0]);
254                    fclose(fp);
255                    return 0;           
256                }   
257                if (mytgs[j].nt != mytgs0[j].nt)
258                {
259                    fprintf(stderr, "%s has different series length to %s\n", 
260                    muxFileName, 
261                    muxFileNameArray[0]);
262                    fclose(fp);
263                    return 0;           
264                }
265
266                if (mytgs[j].nt != mytgs0[0].nt)
267                {
268                    printf("Station 0 has different series length to Station %d\n", j); 
269                }
270            }
271        }
272
273        /* Read the start and stop times for this source */
274        fread(fros + i*(*total_number_of_stations), 
275              *total_number_of_stations*sizeof(int), 1, fp);
276        fread(lros + i*(*total_number_of_stations), 
277              *total_number_of_stations*sizeof(int), 1, fp);
278
279        /* Compute the size of the data block for this source */
280        numData = getNumData(fros + i*(*total_number_of_stations), 
281                             lros + i*(*total_number_of_stations), 
282                             (*total_number_of_stations));
283
284        /* Sanity check */
285        if (numData < 0)
286        {
287            fprintf(stderr,"Size of data block appears to be negative!\n");
288            return 0;       
289        }
290
291        if (numDataMax < numData)
292        {
293            numDataMax = numData;
294        }
295
296        fclose(fp);         
297    }
298
299   
300    // Store time resolution and number of timesteps   
301    // These are the same for all stations as tested above, so
302    // we take the first one.
303    *delta_t = (double)mytgs0[0].dt;
304    *number_of_time_steps = mytgs0[0].nt;
305
306    free(mytgs);
307
308    return 1;
309}
310
311
312float** _read_mux2(int numSrc, 
313                   char **muxFileNameArray, 
314                   float *weights, 
315                   double *params, 
316                   int *number_of_stations,
317                   long *permutation,
318                   int verbose)
319{
320    FILE *fp;
321    int total_number_of_stations, i, isrc, ista, k;
322    char *muxFileName;
323    int istart=-1, istop=-1;
324    int number_of_selected_stations;
325    float *muxData=NULL; // Suppress warning
326    long numData;
327
328    int len_sts_data;
329    float **sts_data;
330    float *temp_sts_data;
331
332    long int offset;
333
334    int number_of_time_steps, N;
335    double delta_t;
336   
337    // Shorthands pointing to memory blocks for each source
338    int *fros_per_source=NULL;     
339    int *lros_per_source=NULL;         
340   
341    _read_mux2_headers(numSrc, 
342                       muxFileNameArray, 
343                       &total_number_of_stations,
344                       &number_of_time_steps,
345                       &delta_t,
346                       verbose);
347
348    // Apply rule that an empty permutation file means 'take all stations'
349    // We could change this later by passing in None instead of the empty
350    // permutation.
351    number_of_selected_stations = *number_of_stations; 
352    if (number_of_selected_stations == 0)
353    {
354        number_of_selected_stations = total_number_of_stations; 
355   
356        // Return possibly updated number of stations
357        *number_of_stations = total_number_of_stations;     
358     
359        // Create the Identity permutation vector
360        permutation = (long *) malloc(number_of_selected_stations*sizeof(long));
361        for (i = 0; i < number_of_selected_stations; i++)
362        {
363            permutation[i] = (long) i; 
364        }
365    }
366   
367    // The params array is used only for passing data back to Python.
368    params[0] = (double) number_of_selected_stations;
369    params[1] = (double) delta_t;
370    params[2] = (double) number_of_time_steps;
371   
372   
373   
374    // Make array(s) to hold demuxed data for stations given in the
375    // permutation file
376    sts_data = (float**) malloc(number_of_selected_stations*sizeof(float*));
377    if (sts_data == NULL)
378    {
379        printf("ERROR: Memory for sts_data could not be allocated.\n");
380        return NULL;
381    }
382
383    // For each selected station, allocate space for its data
384    len_sts_data = number_of_time_steps + POFFSET; // Max length of each timeseries?
385    for (i = 0; i < number_of_selected_stations; i++)
386    {
387        // Initialise sts_data to zero
388        sts_data[i] = (float*) calloc(len_sts_data, sizeof(float));
389        if (sts_data[i] == NULL)
390        {
391            printf("ERROR: Memory for sts_data could not be allocated.\n");
392            return NULL;
393        }
394    }
395
396    temp_sts_data = (float*) calloc(len_sts_data, sizeof(float));
397
398    muxData = (float*) malloc(numDataMax*sizeof(float));
399   
400    // Loop over all sources
401    for (isrc = 0; isrc < numSrc; isrc++)
402    {
403   
404        // Shorthands to local memory
405        fros_per_source = (int*) fros + isrc*total_number_of_stations; 
406        lros_per_source = (int*) lros + isrc*total_number_of_stations;     
407           
408   
409        // Read in data block from mux2 file
410        muxFileName = muxFileNameArray[isrc];
411        if((fp = fopen(muxFileName, "r")) == NULL)
412        {
413            fprintf(stderr, "cannot open file %s\n", muxFileName);
414            return NULL;                   
415        }
416
417        if (verbose){
418            printf("Reading mux file %s\n", muxFileName);
419        }
420
421        offset = sizeof(int) + total_number_of_stations*(sizeof(struct tgsrwg) + 2*sizeof(int));
422        fseek(fp, offset, 0);
423
424        numData = getNumData(fros_per_source, 
425                             lros_per_source, 
426                             total_number_of_stations);
427                             
428        fread(muxData, numData*sizeof(float), 1, fp); 
429        fclose(fp);
430
431        // loop over stations present in the permutation array
432        //     use ista with mux data
433        //     use i with the processed data to be returned         
434        for(i = 0; i < number_of_selected_stations; i++)
435        {               
436   
437            ista = (int) permutation[i]; // Get global index into mux data 
438       
439            // fill the data0 array from the mux file, and weight it
440            fillDataArray(ista, 
441                          total_number_of_stations, 
442                          number_of_time_steps,
443                          mytgs0[ista].ig, // Grid number (if -1 fill with zeros)
444                          fros_per_source, 
445                          lros_per_source, 
446                          temp_sts_data, 
447                          &istart, 
448                          &istop, 
449                          muxData);
450
451            // Weight appropriately and add
452            for(k = 0; k < mytgs0[ista].nt; k++)
453            {
454                if((isdata(sts_data[i][k])) && isdata(temp_sts_data[k]))
455                {
456                    sts_data[i][k] += temp_sts_data[k] * weights[isrc];
457                }
458                else
459                {
460                    sts_data[i][k] = NODATA;
461                }
462            }
463           
464            // Update metadata (e.g. start time and end time)
465            N = number_of_time_steps;
466           
467            if (isrc == 0) {
468                // Assign values for first source
469                sts_data[i][N] = (float)mytgs0[ista].geolat;
470                sts_data[i][N+1] = (float)mytgs0[ista].geolon;
471                sts_data[i][N+2] = (float)mytgs0[ista].z;
472                sts_data[i][N+3] = (float)fros_per_source[ista];
473                sts_data[i][N+4] = (float)lros_per_source[ista];
474            } else {
475                // Update first and last timesteps for subsequent sources
476                if (sts_data[i][N+3] > (float)fros_per_source[ista]) {         
477                    if (verbose) {
478                        printf("Adjusting start time for station %d and source %d",
479                               ista, isrc);
480                        printf(" from %f to %f\n", 
481                               sts_data[i][N+3], 
482                               (float) fros_per_source[ista]); 
483                    }
484                    sts_data[i][N+3] = (float) fros_per_source[ista];
485                }
486               
487                if (sts_data[i][N+4] < (float) lros_per_source[ista]) {         
488                    if (verbose) {
489                        printf("Adjusting end time for station %d and source %d",
490                               ista, isrc);
491                        printf(" from %f to %f\n", 
492                               sts_data[i][N+4], 
493                               (float) lros_per_source[ista]); 
494                    }
495                    sts_data[i][N+4] = (float) lros_per_source[ista];
496                }               
497            }
498        }
499    }
500
501    free(muxData);
502    free(temp_sts_data);
503    free(fros);
504    free(lros);
505    free(mytgs0);
506
507    return sts_data;
508}
509
510/////////////////////////////////////////////////////////////////////////
511//Python gateways
512PyObject *read_mux2(PyObject *self, PyObject *args)
513{
514    /*Read in mux 2 file
515
516    Python call:
517    read_mux2(numSrc,filenames,weights,file_params,permutation,verbose)
518
519    NOTE:
520    A Python int is equivalent to a C long
521    (this becomes really important on 64 bit architectures)
522   
523    A Python double corresponds to a C double
524    */
525   
526    PyObject *filenames;
527    PyArrayObject *pyweights;
528    PyArrayObject *file_params;
529    PyArrayObject *permutation;  // Ordering of selected stations   
530    PyArrayObject *pydata;
531    PyObject *fname;
532
533    char **muxFileNameArray;
534    float **cdata;
535    float *weights;
536    int dimensions[2];
537    int numSrc;
538    int verbose;
539    int total_number_of_stations;
540    int number_of_selected_stations;   
541    int nt;
542    double dt;
543    int i;
544    int j;
545    int start_tstep;
546    int finish_tstep;
547    int it;
548    int time;
549    int num_ts;
550   
551    // Convert Python arguments to C
552    if (!PyArg_ParseTuple(args, "iOOOOi",
553              &numSrc, &filenames, &pyweights, &file_params, 
554              &permutation, &verbose)) 
555    {
556            PyErr_SetString(PyExc_RuntimeError, 
557                "Input arguments to read_mux2 failed");
558            return NULL;
559    }
560
561    if(!PyList_Check(filenames)) 
562    {
563        PyErr_SetString(PyExc_TypeError, "get_first_elem expects a list");
564        return NULL;
565    }
566
567    if(PyList_Size(filenames) == 0)
568    {
569        PyErr_SetString(PyExc_ValueError, "empty lists not allowed");
570        return NULL;
571    }
572
573    if (pyweights->nd != 1 || pyweights->descr->type_num != PyArray_DOUBLE) 
574    {
575        PyErr_SetString(PyExc_ValueError,
576            "pyweights must be one-dimensional and of type double");
577        return NULL; 
578    }
579
580    if(PyList_Size(filenames) != pyweights->dimensions[0])
581    {
582        PyErr_SetString(PyExc_ValueError, 
583            "Must specify one weight for each filename");
584        return NULL;
585    }
586
587    muxFileNameArray = (char**)malloc(numSrc*sizeof(char*));
588    if (muxFileNameArray == NULL) 
589    {
590        PyErr_SetString(PyExc_ValueError, 
591                        "ERROR: Memory for muxFileNameArray could not be allocated.");
592        return NULL;
593    }
594
595    for (i = 0; i < numSrc; i++)
596    {
597
598        fname = PyList_GetItem(filenames, i);
599        if (!fname) 
600        {
601            PyErr_SetString(PyExc_ValueError, "filename not a string");
602            return NULL;
603        }       
604
605        muxFileNameArray[i] = PyString_AsString(fname);
606        if (muxFileNameArray[i] == NULL) 
607        {
608            PyErr_SetString(PyExc_ValueError, 
609          "ERROR: Memory for muxFileNameArray could not be allocated.\n");
610            return NULL;
611        }
612    }
613
614    if (file_params->nd != 1 || file_params->descr->type_num != PyArray_DOUBLE) 
615    {
616        PyErr_SetString(PyExc_ValueError,
617          "file_params must be one-dimensional and of type double");
618        return NULL; 
619    }
620
621
622    // Create array for weights which are passed to read_mux2
623    weights = (float*) malloc(numSrc*sizeof(float));
624    for (i = 0; i < numSrc; i++)
625    {
626        weights[i] = (float)(*(double*)(pyweights->data + i*pyweights->strides[0]));
627    }
628   
629    // Desired number of stations
630    number_of_selected_stations = (int) permutation->dimensions[0];
631
632    // Read in mux2 data from file
633    cdata = _read_mux2(numSrc, 
634                       muxFileNameArray, 
635                       weights, 
636                       (double*)file_params->data,
637                       &number_of_selected_stations, 
638                       (long*) permutation->data,
639                       verbose);
640
641    if (!cdata) 
642    {
643        PyErr_SetString(PyExc_ValueError, "No STS_DATA returned");
644        return NULL;
645    }       
646               
647               
648    // Allocate space for return vector
649    total_number_of_stations = (int)*(double*)(file_params->data + 0*file_params->strides[0]);
650    dt = *(double*)(file_params->data + 1*file_params->strides[0]);
651    nt = (int)*(double*)(file_params->data + 2*file_params->strides[0]);
652
653   
654    // Find min and max start times of all gauges
655    start_tstep = nt + 1;
656    finish_tstep = -1;
657    for (i = 0; i < number_of_selected_stations; i++)
658    {
659        //printf("cdata[%d] start = %f\n", i, (double) cdata[i][nt+3]);
660        // printf("cdata[%d] finish = %f\n", i, (double) cdata[i][nt+4]);   
661   
662        if ((int)cdata[i][nt + 3] < start_tstep)
663        {
664            start_tstep = (int)cdata[i][nt + 3];
665        }
666        if ((int)cdata[i][nt + 4] > finish_tstep)
667        {
668            finish_tstep = (int)cdata[i][nt + 4]; 
669        }
670    }
671
672    if ((start_tstep > nt) | (finish_tstep < 0))
673    {
674        printf("ERROR: Gauge data has incorrect start and finish times:\n");
675        printf("   start_tstep = %d, max_number_of_steps = %d\n", 
676               start_tstep, nt);
677        printf("   finish_tstep = %d, min_number_of_steps = %d\n", 
678               finish_tstep, 0);   
679           
680        PyErr_SetString(PyExc_ValueError, "Incorrect start and finish times"); 
681        return NULL;
682    }
683
684    if (start_tstep >= finish_tstep)
685    {
686        PyErr_SetString(PyExc_ValueError,
687                    "ERROR: Gauge data has non-postive_length");
688        return NULL;
689    }
690
691    num_ts = finish_tstep - start_tstep + 1;
692    dimensions[0] = number_of_selected_stations;
693    dimensions[1] = num_ts + POFFSET;
694   
695    pydata = (PyArrayObject*) PyArray_FromDims(2, dimensions, PyArray_DOUBLE);
696    if(pydata == NULL)
697    {
698        PyErr_SetString(PyExc_ValueError, 
699          "ERROR: Memory for pydata array could not be allocated.");
700        return NULL;
701    }
702
703   
704    // Each gauge begins and ends recording at different times. When a gauge is
705    // not recording but at least one other gauge is.
706    // Pad the non-recording gauge array with zeros.
707    for (i = 0; i < number_of_selected_stations; i++)
708    {
709        time = 0;
710        for (it = 0; it < finish_tstep; it++)
711        {
712            if ((it + 1 >= start_tstep) && (it + 1 <= finish_tstep))
713            {
714                if (it + 1 > (int)cdata[i][nt + 4])
715                {
716                    // This gauge has stopped recording but others are
717                    // still recording
718                    *(double*)(pydata->data + i*pydata->strides[0] 
719                               + time*pydata->strides[1]) = 
720                      0.0;
721                }
722                else
723                {
724                    *(double*)(pydata->data + i*pydata->strides[0] 
725                                    + time*pydata->strides[1]) = 
726                        cdata[i][it];
727                }
728                time++;
729            }
730        }
731        // Pass back lat,lon,elevation
732        for (j = 0; j < POFFSET; j++)
733        {
734            *(double*)(pydata->data + i*pydata->strides[0] 
735                                + (num_ts + j)*pydata->strides[1]) = 
736                    cdata[i][nt + j];
737        }
738    }
739
740    free(weights);
741   
742    // Free filename array, but not independent Python strings
743    // FIXME(Ole): Do we need to update a reference counter in this case?
744    free(muxFileNameArray);
745   
746    for (i = 0; i < number_of_selected_stations; ++i)
747    {
748        free(cdata[i]);
749    }
750    free(cdata);
751
752    return  PyArray_Return(pydata);
753}
754
755//-------------------------------
756// Method table for python module
757//-------------------------------
758static struct PyMethodDef MethodTable[] = {
759    {"read_mux2", read_mux2, METH_VARARGS, "Print out"},
760    {NULL, NULL}
761};
762
763// Module initialisation
764void initurs_ext(void){
765    Py_InitModule("urs_ext", MethodTable);
766
767    import_array(); // Necessary for handling of NumPY structures
768}
Note: See TracBrowser for help on using the repository browser.