source: inundation/ga/storm_surge/pyvolution/quantity_ext.c @ 278

Last change on this file since 278 was 272, checked in by ole, 21 years ago

Coded update as C implementation and verified improvement.
Removed oldstyle obsolete balanced limiter

File size: 14.9 KB
Line 
1// Python - C extension for quantity module.
2//
3// To compile (Python2.3):
4//  gcc -c util_ext.c -I/usr/include/python2.3 -o util_ext.o -Wall -O
5//  gcc -shared util_ext.o  -o util_ext.so     
6//
7// See the module quantity.py
8//
9//
10// Ole Nielsen, GA 2004
11       
12#include "Python.h"
13#include "Numeric/arrayobject.h"
14#include "math.h"
15
16//Shared code snippets
17#include "util_ext.h"
18#include "quantity_ext.h"
19
20
21
22int _compute_gradients(int N,
23                        double* centroids, 
24                        double* centroid_values,
25                        int* number_of_boundaries,
26                        int* surrogate_neighbours,
27                        double* a,
28                        double* b) {
29                       
30  int i, k, k0, k1, k2, index3;
31  double x0, x1, x2, y0, y1, y2, q0, q1, q2, det;
32 
33 
34  for (k=0; k<N; k++) {
35    index3 = 3*k;
36   
37    if (number_of_boundaries[k] < 2) {
38      //Two or three true neighbours
39
40      //Get indices of neighbours (or self when used as surrogate)         
41      //k0, k1, k2 = surrogate_neighbours[k,:]
42     
43      k0 = surrogate_neighbours[index3 + 0];
44      k1 = surrogate_neighbours[index3 + 1];
45      k2 = surrogate_neighbours[index3 + 2];           
46      if (k0 == k1 || k1 == k2) return -1;     
47
48      //Get data
49      q0 = centroid_values[k0];
50      q1 = centroid_values[k1];
51      q2 = centroid_values[k2];                   
52
53      x0 = centroids[k0*2]; y0 = centroids[k0*2+1]; 
54      x1 = centroids[k1*2]; y1 = centroids[k1*2+1]; 
55      x2 = centroids[k2*2]; y2 = centroids[k2*2+1];             
56
57      //Gradient
58      _gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2, &a[k], &b[k]);
59     
60    } else if (number_of_boundaries[k] == 2) {
61      //One true neighbour
62
63      //#Get index of the one neighbour
64      i=0; k0 = k;
65      while (i<3 && k0==k) {
66        k0 = surrogate_neighbours[index3 + i];
67        i++;
68      }
69      if (k0 == k) return -1;
70     
71      k1 = k; //self
72
73      //Get data
74      q0 = centroid_values[k0];
75      q1 = centroid_values[k1];
76           
77      x0 = centroids[k0*2]; y0 = centroids[k0*2+1]; 
78      x1 = centroids[k1*2]; y1 = centroids[k1*2+1]; 
79
80      //Gradient
81      det = x0*y1 - x1*y0;
82      if (det != 0.0) {
83        a[k] = (y1*q0 - y0*q1)/det;
84        b[k] = (x0*q1 - x1*q0)/det;
85      }
86    } 
87    //    else:
88    //        #No true neighbours -     
89    //        #Fall back to first order scheme
90    //        pass
91  }
92  return 0;
93}           
94
95
96int _extrapolate(int N,
97                 double* centroids, 
98                 double* centroid_values,               
99                 double* vertex_coordinates,
100                 double* vertex_values,
101                 double* a,
102                 double* b) {
103 
104  int k, k2, k3, k6;
105  double x, y, x0, y0, x1, y1, x2, y2;
106                     
107  for (k=0; k<N; k++) {
108    k6 = 6*k;
109    k3 = 3*k;   
110    k2 = 2*k;
111       
112    //Centroid coordinates           
113    x = centroids[k2]; y = centroids[k2+1]; 
114
115    //vertex coordinates
116    //x0, y0, x1, y1, x2, y2 = X[k,:]
117    x0 = vertex_coordinates[k6 + 0];
118    y0 = vertex_coordinates[k6 + 1];   
119    x1 = vertex_coordinates[k6 + 2];
120    y1 = vertex_coordinates[k6 + 3];       
121    x2 = vertex_coordinates[k6 + 4];
122    y2 = vertex_coordinates[k6 + 5];           
123
124    //Extrapolate
125    vertex_values[k3+0] = centroid_values[k] + a[k]*(x0-x) + b[k]*(y0-y);
126    vertex_values[k3+1] = centroid_values[k] + a[k]*(x1-x) + b[k]*(y1-y);
127    vertex_values[k3+2] = centroid_values[k] + a[k]*(x2-x) + b[k]*(y2-y);
128   
129  }
130  return 0;
131}
132
133
134
135
136int _interpolate(int N,
137                 double* vertex_values,
138                 double* edge_values) {
139 
140  int k, k3;
141  double q0, q1, q2;
142
143                     
144  for (k=0; k<N; k++) {
145    k3 = 3*k;   
146   
147    q0 = vertex_values[k3 + 0];
148    q1 = vertex_values[k3 + 1];
149    q2 = vertex_values[k3 + 2];
150           
151    //printf("%f, %f, %f\n", q0, q1, q2);
152    edge_values[k3 + 0] = 0.5*(q1+q2);
153    edge_values[k3 + 1] = 0.5*(q0+q2); 
154    edge_values[k3 + 2] = 0.5*(q0+q1);
155  } 
156  return 0;
157}
158
159int _update(int N, 
160            double timestep,
161            double* centroid_values,
162            double* explicit_update,
163            double* semi_implicit_update) {
164  //Update centroid values based on values stored in
165  //explicit_update and semi_implicit_update as well as given timestep
166 
167 
168  int k;
169  double denominator;
170 
171  //Explicit updates
172  for (k=0; k<N; k++) {
173    centroid_values[k] += timestep*explicit_update[k];
174  }
175           
176  //Semi implicit updates
177  for (k=0; k<N; k++) { 
178    denominator = 1.0 - timestep*semi_implicit_update[k]; 
179   
180    if (denominator == 0.0) {
181      return -1;
182    } else {
183      //Update conserved_quantities from semi implicit updates
184      centroid_values[k] /= denominator;
185    }
186  }
187  return 0;
188}
189                     
190 
191/////////////////////////////////////////////////
192// Gateways to Python
193PyObject *update(PyObject *self, PyObject *args) {
194 
195  PyObject *quantity;
196  PyArrayObject *centroid_values, *explicit_update, *semi_implicit_update; 
197 
198  double timestep; 
199  int N, err;
200 
201  // Convert Python arguments to C 
202  if (!PyArg_ParseTuple(args, "Od", &quantity, &timestep)) 
203    return NULL;
204
205  centroid_values = (PyArrayObject*) 
206    PyObject_GetAttrString(quantity, "centroid_values");           
207  if (!centroid_values) return NULL;               
208
209  explicit_update = (PyArrayObject*) 
210    PyObject_GetAttrString(quantity, "explicit_update");   
211  if (!explicit_update) return NULL;         
212 
213  semi_implicit_update = (PyArrayObject*) 
214    PyObject_GetAttrString(quantity, "semi_implicit_update");   
215  if (!semi_implicit_update) return NULL;           
216
217  N = centroid_values -> dimensions[0]; 
218 
219 
220  err = _update(N, timestep,
221                (double*) centroid_values -> data,
222                (double*) explicit_update -> data,
223                (double*) semi_implicit_update -> data);               
224               
225                     
226  if (err != 0) {
227    PyErr_SetString(PyExc_RuntimeError, 
228                    "Zero division in semi implicit update - call Stephen :)");
229    return NULL;
230  }                 
231 
232  //Release and return               
233  Py_DECREF(centroid_values);   
234  Py_DECREF(explicit_update); 
235  Py_DECREF(semi_implicit_update);   
236
237  return Py_BuildValue("");
238}
239
240
241PyObject *interpolate_from_vertices_to_edges(PyObject *self, PyObject *args) {
242 
243  PyObject *quantity;
244  PyArrayObject *vertex_values, *edge_values;
245   
246  int N, err;
247 
248  // Convert Python arguments to C 
249  if (!PyArg_ParseTuple(args, "O", &quantity)) 
250    return NULL;
251
252  vertex_values = (PyArrayObject*) 
253    PyObject_GetAttrString(quantity, "vertex_values");           
254  if (!vertex_values) return NULL;               
255
256  edge_values = (PyArrayObject*) 
257    PyObject_GetAttrString(quantity, "edge_values");   
258  if (!edge_values) return NULL;         
259
260  N = vertex_values -> dimensions[0]; 
261 
262  err = _interpolate(N,
263                     (double*) vertex_values -> data,
264                     (double*) edge_values -> data);
265                     
266  if (err != 0) {
267    PyErr_SetString(PyExc_RuntimeError, "Interpolate could not be computed");
268    return NULL;
269  }                 
270 
271  //Release and return               
272  Py_DECREF(vertex_values);   
273  Py_DECREF(edge_values); 
274
275  return Py_BuildValue("");
276}
277
278
279PyObject *compute_gradients(PyObject *self, PyObject *args) {
280 
281  PyObject *quantity, *domain, *R;
282  PyArrayObject
283    *centroids,            //Coordinates at centroids
284    *centroid_values,      //Values at centroids   
285    *number_of_boundaries, //Number of boundaries for each triangle
286    *surrogate_neighbours, //True neighbours or - if one missing - self
287    *a, *b;                //Return values
288   
289  int dimensions[1], N, err;
290 
291  // Convert Python arguments to C 
292  if (!PyArg_ParseTuple(args, "O", &quantity)) 
293    return NULL;
294
295  domain = PyObject_GetAttrString(quantity, "domain");     
296  if (!domain) 
297    return NULL; 
298
299  //Get pertinent variables
300  centroids = (PyArrayObject*) PyObject_GetAttrString(domain, "centroids"); 
301  if (!centroids) return NULL;   
302 
303  centroid_values = (PyArrayObject*) 
304    PyObject_GetAttrString(quantity, "centroid_values");   
305  if (!centroid_values) return NULL;       
306 
307  surrogate_neighbours = (PyArrayObject*) 
308    PyObject_GetAttrString(domain, "surrogate_neighbours");
309  if (!surrogate_neighbours) return NULL;       
310 
311  number_of_boundaries = (PyArrayObject*) 
312    PyObject_GetAttrString(domain, "number_of_boundaries");           
313  if (!number_of_boundaries) return NULL;           
314 
315  N = centroid_values -> dimensions[0];
316   
317  //Release
318  Py_DECREF(domain);     
319         
320  //Allocate space for return vectors a and b (don't DECREF)
321  dimensions[0] = N;
322  a = (PyArrayObject *) PyArray_FromDims(1, dimensions, PyArray_DOUBLE);
323  b = (PyArrayObject *) PyArray_FromDims(1, dimensions, PyArray_DOUBLE); 
324       
325
326 
327  err = _compute_gradients(N,
328                           (double*) centroids -> data, 
329                           (double*) centroid_values -> data,
330                           (int*) number_of_boundaries -> data,
331                           (int*) surrogate_neighbours -> data,
332                           (double*) a -> data,
333                           (double*) b -> data);     
334                           
335  if (err != 0) {
336    PyErr_SetString(PyExc_RuntimeError, "Gradient could not be computed");
337    return NULL;
338  }
339                     
340  //Release                 
341  Py_DECREF(centroids);   
342  Py_DECREF(centroid_values);   
343  Py_DECREF(number_of_boundaries); 
344  Py_DECREF(surrogate_neighbours);           
345         
346  //Build result, release and return
347  R = Py_BuildValue("OO", PyArray_Return(a), PyArray_Return(b)); 
348  Py_DECREF(a);     
349  Py_DECREF(b);       
350  return R;
351}
352
353
354
355PyObject *extrapolate_second_order(PyObject *self, PyObject *args) {
356 
357  PyObject *quantity, *domain;
358  PyArrayObject
359    *centroids,            //Coordinates at centroids
360    *centroid_values,      //Values at centroids   
361    *vertex_coordinates,   //Coordinates at vertices           
362    *vertex_values,        //Values at vertices       
363    *number_of_boundaries, //Number of boundaries for each triangle
364    *surrogate_neighbours, //True neighbours or - if one missing - self
365    *a, *b;                //Gradients
366   
367  //int N, err;
368  int dimensions[1], N, err; 
369  //double *a, *b;  //Gradients
370 
371  // Convert Python arguments to C 
372  if (!PyArg_ParseTuple(args, "O", &quantity)) 
373    return NULL;
374
375  domain = PyObject_GetAttrString(quantity, "domain");     
376  if (!domain) 
377    return NULL; 
378
379  //Get pertinent variables
380  centroids = (PyArrayObject*) PyObject_GetAttrString(domain, "centroids"); 
381  if (!centroids) return NULL;   
382 
383  centroid_values = (PyArrayObject*) 
384    PyObject_GetAttrString(quantity, "centroid_values");   
385  if (!centroid_values) return NULL;       
386 
387  surrogate_neighbours = (PyArrayObject*) 
388    PyObject_GetAttrString(domain, "surrogate_neighbours");
389  if (!surrogate_neighbours) return NULL;       
390 
391  number_of_boundaries = (PyArrayObject*) 
392    PyObject_GetAttrString(domain, "number_of_boundaries");           
393  if (!number_of_boundaries) return NULL;           
394 
395  vertex_coordinates = (PyArrayObject*) 
396    PyObject_GetAttrString(domain, "vertex_coordinates");           
397  if (!vertex_coordinates) return NULL;             
398 
399  vertex_values = (PyArrayObject*) 
400    PyObject_GetAttrString(quantity, "vertex_values");           
401  if (!vertex_values) return NULL;               
402
403 
404  /*
405  printf("In extrapolate C routine\n"); 
406  printf("d0=%d, d1=%d\n",
407         vertex_values -> dimensions[0],
408         vertex_values -> dimensions[1]);
409  */
410 
411  vertex_values = (PyArrayObject*) 
412    PyObject_GetAttrString(quantity, "vertex_values");           
413  if (!vertex_values) return NULL;               
414 
415  N = centroid_values -> dimensions[0];
416   
417  //Release
418  Py_DECREF(domain);     
419         
420  //Allocate space for return vectors a and b (don't DECREF)
421  dimensions[0] = N;
422  a = (PyArrayObject *) PyArray_FromDims(1, dimensions, PyArray_DOUBLE);
423  b = (PyArrayObject *) PyArray_FromDims(1, dimensions, PyArray_DOUBLE); 
424
425  //FIXME: Odd that I couldn't use normal arrays         
426  //Allocate space for return vectors a and b (don't DECREF)
427  //a = (double*) malloc(N * sizeof(double));
428  //if (!a) return NULL; 
429  //b = (double*) malloc(N * sizeof(double)); 
430  //if (!b) return NULL;
431
432 
433  err = _compute_gradients(N,
434                           (double*) centroids -> data, 
435                           (double*) centroid_values -> data,
436                           (int*) number_of_boundaries -> data,
437                           (int*) surrogate_neighbours -> data,
438                           (double*) a -> data,
439                           (double*) b -> data);
440                           
441  if (err != 0) {
442    PyErr_SetString(PyExc_RuntimeError, "Gradient could not be computed");
443    return NULL;
444  }
445 
446  err = _extrapolate(N, 
447                     (double*) centroids -> data, 
448                     (double*) centroid_values -> data,             
449                     (double*) vertex_coordinates -> data,
450                     (double*) vertex_values -> data,
451                     (double*) a -> data,
452                     (double*) b -> data);                   
453  //a, b);
454                     
455                     
456  if (err != 0) {
457    PyErr_SetString(PyExc_RuntimeError, 
458                    "Internal function _extrapolate failed");
459    return NULL;
460  }                 
461               
462 
463
464  //Release 
465  Py_DECREF(centroids);   
466  Py_DECREF(centroid_values);   
467  Py_DECREF(number_of_boundaries); 
468  Py_DECREF(surrogate_neighbours);
469  Py_DECREF(vertex_coordinates);                   
470  Py_DECREF(vertex_values);                 
471  Py_DECREF(a);                   
472  Py_DECREF(b);                     
473  //free(a);     
474  //free(b);       
475 
476  return Py_BuildValue("");   
477}   
478
479
480
481PyObject *limit(PyObject *self, PyObject *args) {
482 
483  PyObject *quantity, *domain, *Tmp;
484  PyArrayObject
485    *qv, //Conserved quantities at vertices
486    *qc, //Conserved quantities at centroids
487    *neighbours;
488   
489  int k, i, n, N, k3;
490  double beta; //Safety factor
491  double *qmin, *qmax, qn;
492 
493  // Convert Python arguments to C 
494  if (!PyArg_ParseTuple(args, "O", &quantity)) 
495    return NULL;
496
497  domain = PyObject_GetAttrString(quantity, "domain");     
498  if (!domain) 
499    return NULL; 
500   
501  neighbours = (PyArrayObject*) PyObject_GetAttrString(domain, "neighbours");
502
503  //Get safety factor beta
504  Tmp = PyObject_GetAttrString(domain, "beta"); 
505  if (!Tmp) 
506    return NULL;
507     
508  beta = PyFloat_AsDouble(Tmp); 
509 
510  Py_DECREF(Tmp);   
511  Py_DECREF(domain);     
512
513  qc = (PyArrayObject*) PyObject_GetAttrString(quantity, "centroid_values"); 
514  qv = (PyArrayObject*) PyObject_GetAttrString(quantity, "vertex_values");   
515  N = qc -> dimensions[0];
516   
517  //Find min and max of this and neighbour's centroid values
518  qmin = malloc(N * sizeof(double));
519  qmax = malloc(N * sizeof(double)); 
520  for (k=0; k<N; k++) {
521    k3=k*3;
522   
523    qmin[k] = ((double*) qc -> data)[k];
524    qmax[k] = qmin[k];
525   
526    for (i=0; i<3; i++) {
527      n = ((int*) neighbours -> data)[k3+i];
528      if (n >= 0) {
529        qn = ((double*) qc -> data)[n]; //Neighbour's centroid value
530         
531        qmin[k] = min(qmin[k], qn);
532        qmax[k] = max(qmax[k], qn);
533      }
534    }
535  }
536 
537  // Call underlying routine
538  _limit(N, beta, (double*) qc -> data, (double*) qv -> data, qmin, qmax);
539         
540  free(qmin);
541  free(qmax); 
542  return Py_BuildValue(""); 
543}
544
545
546
547// Method table for python module
548static struct PyMethodDef MethodTable[] = {
549  {"limit", limit, METH_VARARGS, "Print out"},   
550  {"update", update, METH_VARARGS, "Print out"},     
551  {"compute_gradients", compute_gradients, METH_VARARGS, "Print out"},     
552  {"extrapolate_second_order", extrapolate_second_order, 
553   METH_VARARGS, "Print out"},       
554  {"interpolate_from_vertices_to_edges", 
555   interpolate_from_vertices_to_edges,
556   METH_VARARGS, "Print out"},           
557  {NULL, NULL, 0, NULL}   /* sentinel */
558};
559
560// Module initialisation   
561void initquantity_ext(void){
562  Py_InitModule("quantity_ext", MethodTable);
563 
564  import_array();     //Necessary for handling of NumPY structures 
565}
566
Note: See TracBrowser for help on using the repository browser.