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

Last change on this file since 1003 was 907, checked in by ole, 20 years ago

Addressed problem with artificial momentum generated by discontinuous water depths in the presence of steep slopes.
Now very shallow water is limited with a separate h-limiter controlled by beta_h
(see config.py) for details.

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