source: inundation/ga/storm_surge/pyvolution/shallow_water_ext.c @ 467

Last change on this file since 467 was 449, checked in by ole, 20 years ago

Bug huntin'

File size: 18.7 KB
Line 
1// Python - C extension module for shallow_water.py
2//
3// To compile (Python2.3):
4//  gcc -c domain_ext.c -I/usr/include/python2.3 -o domain_ext.o -Wall -O
5//  gcc -shared domain_ext.o  -o domain_ext.so 
6//
7// or use python compile.py
8//
9// See the module shallow_water.py
10//
11//
12// Ole Nielsen, GA 2004
13   
14   
15#include "Python.h"
16#include "Numeric/arrayobject.h"
17#include "math.h"
18
19//Shared code snippets
20#include "util_ext.h"
21
22
23// Computational function for rotation
24int _rotate(double *q, double n1, double n2) {
25  /*Rotate the momentum component q (q[1], q[2])
26    from x,y coordinates to coordinates based on normal vector (n1, n2).
27   
28    Result is returned in array 3x1 r     
29    To rotate in opposite direction, call rotate with (q, n1, -n2)
30   
31    Contents of q are changed by this function */   
32
33
34  double q1, q2;
35 
36  //Shorthands
37  q1 = q[1];  //uh momentum
38  q2 = q[2];  //vh momentum
39
40  //Rotate
41  q[1] =  n1*q1 + n2*q2;
42  q[2] = -n2*q1 + n1*q2; 
43
44  return 0;
45} 
46
47
48       
49// Computational function for flux computation (using stage w=z+h)
50int flux_function(double *q_left, double *q_right, 
51                  double z_left, double z_right, 
52                  double n1, double n2, 
53                  double epsilon, double g, 
54                  double *edgeflux, double *max_speed) {
55 
56  /*Compute fluxes between volumes for the shallow water wave equation
57    cast in terms of the 'stage', w = h+z using
58    the 'central scheme' as described in
59   
60    Kurganov, Noelle, Petrova. 'Semidiscrete Central-Upwind Schemes For
61    Hyperbolic Conservation Laws and Hamilton-Jacobi Equations'.
62    Siam J. Sci. Comput. Vol. 23, No. 3, pp. 707-740.
63   
64    The implemented formula is given in equation (3.15) on page 714
65  */
66       
67  int i;
68       
69  double w_left, h_left, uh_left, vh_left, u_left;
70  double w_right, h_right, uh_right, vh_right, u_right;
71  double s_min, s_max, soundspeed_left, soundspeed_right;
72  double denom, z;
73  double q_left_copy[3], q_right_copy[3];   
74  double flux_right[3], flux_left[3];
75 
76  //Copy conserved quantities to protect from modification
77  for (i=0; i<3; i++) {
78    q_left_copy[i] = q_left[i];
79    q_right_copy[i] = q_right[i];
80  } 
81   
82  //Align x- and y-momentum with x-axis
83  _rotate(q_left_copy, n1, n2);
84  _rotate(q_right_copy, n1, n2);   
85
86  z = (z_left+z_right)/2; //Take average of field values
87
88  //Compute speeds in x-direction
89  w_left = q_left_copy[0];              // h+z
90  h_left = w_left-z;
91  uh_left = q_left_copy[1];
92
93  if (h_left < epsilon) {
94    h_left = 0.0;  //Could have been negative
95    u_left = 0.0;
96  } else { 
97    u_left = uh_left/h_left;
98  }
99 
100  w_right = q_right_copy[0];
101  h_right = w_right-z;
102  uh_right = q_right_copy[1];
103
104  if (h_right < epsilon) {
105    h_right = 0.0; //Could have been negative
106    u_right = 0.0;
107  } else { 
108    u_right = uh_right/h_right;
109  }
110
111  //Momentum in y-direction             
112  vh_left  = q_left_copy[2];
113  vh_right = q_right_copy[2];   
114       
115
116  //Maximal and minimal wave speeds
117  soundspeed_left  = sqrt(g*h_left); 
118  soundspeed_right = sqrt(g*h_right);
119   
120  s_max = max(u_left+soundspeed_left, u_right+soundspeed_right);
121  if (s_max < 0.0) s_max = 0.0; 
122 
123  s_min = min(u_left-soundspeed_left, u_right-soundspeed_right);
124  if (s_min > 0.0) s_min = 0.0;   
125 
126  //Flux formulas 
127  flux_left[0] = u_left*h_left;
128  flux_left[1] = u_left*uh_left + 0.5*g*h_left*h_left;
129  flux_left[2] = u_left*vh_left;
130 
131  flux_right[0] = u_right*h_right;
132  flux_right[1] = u_right*uh_right + 0.5*g*h_right*h_right;
133  flux_right[2] = u_right*vh_right;
134   
135
136  //Flux computation   
137  denom = s_max-s_min;
138  if (denom == 0.0) {
139    for (i=0; i<3; i++) edgeflux[i] = 0.0;
140    *max_speed = 0.0;
141  } else {   
142    for (i=0; i<3; i++) {
143      edgeflux[i] = s_max*flux_left[i] - s_min*flux_right[i];
144      edgeflux[i] += s_max*s_min*(q_right_copy[i]-q_left_copy[i]);
145      edgeflux[i] /= denom;
146    } 
147       
148    //Maximal wavespeed
149    *max_speed = max(fabs(s_max), fabs(s_min));
150   
151    //Rotate back       
152    _rotate(edgeflux, n1, -n2);
153  }
154  return 0;
155}
156       
157void _manning_friction(double g, double eps, int N, 
158                       double* w, double* uh, double* vh, 
159                       double* eta, double* xmom, double* ymom) {     
160
161  int k;
162  double S;
163 
164  for (k=0; k<N; k++) {
165    if (w[k] >= eps) {
166      S = -g * eta[k]*eta[k] * sqrt((uh[k]*uh[k] + vh[k]*vh[k]));
167      S /= pow(w[k], 7.0/3);     
168
169      //Update momentum
170      xmom[k] += S*uh[k];
171      ymom[k] += S*vh[k];
172    }
173  }
174 
175}
176           
177
178
179int _balance_deep_and_shallow(int N,
180                              double* wc,
181                              double* zc, 
182                              double* hc,                             
183                              double* wv, 
184                              double* zv, 
185                              double* hv,
186                              double* xmomc, 
187                              double* ymomc, 
188                              double* xmomv, 
189                              double* ymomv) { 
190 
191  int k, k3, i;
192  double dz, hmin, alpha;
193 
194  //Compute linear combination between constant levels and and
195  //levels parallel to the bed elevation.     
196 
197  for (k=0; k<N; k++) {
198    // Compute maximal variation in bed elevation
199    // This quantitiy is
200    //     dz = max_i abs(z_i - z_c)
201    // and it is independent of dimension
202    // In the 1d case zc = (z0+z1)/2
203    // In the 2d case zc = (z0+z1+z2)/3
204
205    k3 = 3*k;
206   
207    //FIXME: Try with this one precomputed
208    dz = 0.0;
209    hmin = hv[k3];
210    for (i=0; i<3; i++) {
211      dz = max(dz, fabs(zv[k3+i]-zc[k]));
212      hmin = min(hmin, hv[k3+i]);
213    }
214
215   
216    //Create alpha in [0,1], where alpha==0 means using shallow
217    //first order scheme and alpha==1 means using the stage w as
218    //computed by the gradient limiter (1st or 2nd order)
219    //
220    //If hmin > dz/2 then alpha = 1 and the bed will have no effect
221    //If hmin < 0 then alpha = 0 reverting to constant height above bed.
222   
223    if (dz > 0.0) 
224      alpha = max( min( 2*hmin/dz, 1.0), 0.0 );
225    else
226      alpha = 1.0;  //Flat bed
227
228     
229    //Weighted balance between stage parallel to bed elevation
230    //(wvi = zvi + hc) and stage as computed by 1st or 2nd
231    //order gradient limiter
232    //(wvi = zvi + hvi) where i=0,1,2 denotes the vertex ids
233    //
234    //It follows that the updated wvi is
235    //  wvi := (1-alpha)*(zvi+hc) + alpha*(zvi+hvi) =
236    //  zvi + hc + alpha*(hvi - hc)
237    //
238    //Note that hvi = zc+hc-zvi in the first order case (constant).
239
240    if (alpha < 1) {         
241      for (i=0; i<3; i++) {
242        wv[k3+i] = zv[k3+i] + hc[k] + alpha*(hv[k3+i]-hc[k]);
243           
244     
245        //Update momentum as a linear combination of
246        //xmomc and ymomc (shallow) and momentum
247        //from extrapolator xmomv and ymomv (deep).
248        xmomv[k3+i] = (1-alpha)*xmomc[k] + alpha*xmomv[k3+i];           
249        ymomv[k3+i] = (1-alpha)*ymomc[k] + alpha*ymomv[k3+i];
250      } 
251    }
252  }         
253  return 0;
254}
255
256
257
258int _protect(int N, 
259             double minimum_allowed_height,       
260             double* wc,
261             double* zc, 
262             double* xmomc, 
263             double* ymomc) {
264 
265  int k; 
266  double hc;
267 
268  //Protect against initesimal and negative heights
269 
270  for (k=0; k<N; k++) {
271    hc = wc[k] - zc[k];
272   
273    if (hc < minimum_allowed_height) {   
274      wc[k] = zc[k];       
275      xmomc[k] = 0.0;
276      ymomc[k] = 0.0;     
277    }
278   
279  }
280  return 0;
281}
282
283
284
285///////////////////////////////////////////////////////////////////
286// Gateways to Python
287
288PyObject *gravity(PyObject *self, PyObject *args) {
289  //
290  //  gravity(g, h, v, x, xmom, ymom)
291  //
292 
293 
294  PyArrayObject *h, *v, *x, *xmom, *ymom;
295  int k, i, N, k3, k6;
296  double g, avg_h, zx, zy;
297  double x0, y0, x1, y1, x2, y2, z0, z1, z2;
298   
299  if (!PyArg_ParseTuple(args, "dOOOOO",
300                        &g, &h, &v, &x, 
301                        &xmom, &ymom)) 
302    return NULL; 
303
304  N = h -> dimensions[0];
305  for (k=0; k<N; k++) {
306    k3 = 3*k;  // base index
307    k6 = 6*k;  // base index   
308   
309    avg_h = 0.0;
310    for (i=0; i<3; i++) {
311      avg_h += ((double *) h -> data)[k3+i];
312    }   
313    avg_h /= 3;
314       
315   
316    //Compute bed slope
317    x0 = ((double*) x -> data)[k6 + 0];
318    y0 = ((double*) x -> data)[k6 + 1];   
319    x1 = ((double*) x -> data)[k6 + 2];
320    y1 = ((double*) x -> data)[k6 + 3];       
321    x2 = ((double*) x -> data)[k6 + 4];
322    y2 = ((double*) x -> data)[k6 + 5];           
323
324
325    z0 = ((double*) v -> data)[k3 + 0];
326    z1 = ((double*) v -> data)[k3 + 1];
327    z2 = ((double*) v -> data)[k3 + 2];       
328
329    _gradient(x0, y0, x1, y1, x2, y2, z0, z1, z2, &zx, &zy);
330
331    //Update momentum
332    ((double*) xmom -> data)[k] += -g*zx*avg_h;
333    ((double*) ymom -> data)[k] += -g*zy*avg_h;       
334  }
335   
336  return Py_BuildValue("");
337}
338
339
340PyObject *manning_friction(PyObject *self, PyObject *args) {
341  //
342  // manning_friction(g, eps, w, uh, vh, eta, xmom_update, ymom_update)
343  //
344 
345 
346  PyArrayObject *w, *uh, *vh, *eta, *xmom, *ymom;
347  int N;
348  double g, eps;
349   
350  if (!PyArg_ParseTuple(args, "ddOOOOOO",
351                        &g, &eps, &w, &uh, &vh, &eta, 
352                        &xmom, &ymom)) 
353    return NULL; 
354
355  N = w -> dimensions[0];   
356  _manning_friction(g, eps, N,
357                    (double*) w -> data,
358                    (double*) uh -> data, 
359                    (double*) vh -> data, 
360                    (double*) eta -> data,
361                    (double*) xmom -> data, 
362                    (double*) ymom -> data);
363
364  return Py_BuildValue("");
365}                   
366
367PyObject *rotate(PyObject *self, PyObject *args, PyObject *kwargs) {
368  //
369  // r = rotate(q, normal, direction=1)
370  //
371  // Where q is assumed to be a Float numeric array of length 3 and
372  // normal a Float numeric array of length 2.
373
374 
375  PyObject *Q, *Normal;
376  PyArrayObject *q, *r, *normal;
377 
378  static char *argnames[] = {"q", "normal", "direction", NULL};
379  int dimensions[1], i, direction=1;
380  double n1, n2;
381
382  // Convert Python arguments to C 
383  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|i", argnames, 
384                                   &Q, &Normal, &direction)) 
385    return NULL; 
386
387  //Input checks (convert sequences into numeric arrays)
388  q = (PyArrayObject *) 
389    PyArray_ContiguousFromObject(Q, PyArray_DOUBLE, 0, 0);
390  normal = (PyArrayObject *) 
391    PyArray_ContiguousFromObject(Normal, PyArray_DOUBLE, 0, 0);
392 
393  //Allocate space for return vector r (don't DECREF)
394  dimensions[0] = 3;
395  r = (PyArrayObject *) PyArray_FromDims(1, dimensions, PyArray_DOUBLE);
396
397  //Copy
398  for (i=0; i<3; i++) {
399    ((double *) (r -> data))[i] = ((double *) (q -> data))[i]; 
400  }
401 
402  //Get normal and direction
403  n1 = ((double *) normal -> data)[0]; 
404  n2 = ((double *) normal -> data)[1];   
405  if (direction == -1) n2 = -n2;
406
407  //Rotate
408  _rotate((double *) r -> data, n1, n2);
409
410  //Release numeric arrays
411  Py_DECREF(q);   
412  Py_DECREF(normal);
413       
414  //return result using PyArray to avoid memory leak
415  return PyArray_Return(r);
416}   
417
418
419PyObject *compute_fluxes(PyObject *self, PyObject *args) {
420  /*Compute all fluxes and the timestep suitable for all volumes
421    in domain.
422
423    Compute total flux for each conserved quantity using "flux_function"
424
425    Fluxes across each edge are scaled by edgelengths and summed up
426    Resulting flux is then scaled by area and stored in
427    explicit_update for each of the three conserved quantities
428    level, xmomentum and ymomentum
429
430    The maximal allowable speed computed by the flux_function for each volume
431    is converted to a timestep that must not be exceeded. The minimum of
432    those is computed as the next overall timestep.
433   
434    Python call:
435    domain.timestep = compute_fluxes(timestep,
436                                     domain.epsilon,
437                                     domain.g,
438                                     domain.neighbours,
439                                     domain.neighbour_edges,
440                                     domain.normals,
441                                     domain.edgelengths,                       
442                                     domain.radii,
443                                     domain.areas,
444                                     Level.edge_values,
445                                     Xmom.edge_values,
446                                     Ymom.edge_values, 
447                                     Bed.edge_values,   
448                                     Level.boundary_values,
449                                     Xmom.boundary_values,
450                                     Ymom.boundary_values,
451                                     Level.explicit_update,
452                                     Xmom.explicit_update,
453                                     Ymom.explicit_update)
454       
455
456    Post conditions:
457      domain.explicit_update is reset to computed flux values
458      domain.timestep is set to the largest step satisfying all volumes.
459
460           
461  */
462
463 
464  PyArrayObject *neighbours, *neighbour_edges,
465    *normals, *edgelengths, *radii, *areas,
466    *level_edge_values, 
467    *xmom_edge_values, 
468    *ymom_edge_values, 
469    *bed_edge_values,   
470    *level_boundary_values,
471    *xmom_boundary_values,
472    *ymom_boundary_values,
473    *level_explicit_update,
474    *xmom_explicit_update,
475    *ymom_explicit_update;
476
477   
478  //Local variables 
479  double timestep, max_speed, epsilon, g;
480  double normal[2], ql[3], qr[3], zl, zr;
481  double flux[3], edgeflux[3]; //Work arrays for summing up fluxes
482
483  int number_of_elements, k, i, j, m, n;
484  int ki, nm, ki2; //Index shorthands
485 
486 
487  // Convert Python arguments to C 
488  if (!PyArg_ParseTuple(args, "dddOOOOOOOOOOOOOOOO",
489                        &timestep,
490                        &epsilon,
491                        &g,
492                        &neighbours, 
493                        &neighbour_edges,
494                        &normals, 
495                        &edgelengths, &radii, &areas,
496                        &level_edge_values, 
497                        &xmom_edge_values, 
498                        &ymom_edge_values, 
499                        &bed_edge_values,   
500                        &level_boundary_values,
501                        &xmom_boundary_values,
502                        &ymom_boundary_values,
503                        &level_explicit_update,
504                        &xmom_explicit_update,
505                        &ymom_explicit_update)) {
506    PyErr_SetString(PyExc_RuntimeError, "Input arguments failed");
507    return NULL;
508  }
509
510  number_of_elements = level_edge_values -> dimensions[0];
511 
512   
513  for (k=0; k<number_of_elements; k++) {
514
515    //Reset work array
516    for (j=0; j<3; j++) flux[j] = 0.0;
517                         
518    //Loop through neighbours and compute edge flux for each
519    for (i=0; i<3; i++) {
520      ki = k*3+i;
521      ql[0] = ((double *) level_edge_values -> data)[ki];
522      ql[1] = ((double *) xmom_edge_values -> data)[ki];
523      ql[2] = ((double *) ymom_edge_values -> data)[ki];           
524      zl =    ((double *) bed_edge_values -> data)[ki];                 
525     
526      //Quantities at neighbour on nearest face
527      n = ((int *) neighbours -> data)[ki];
528      if (n < 0) {
529        m = -n-1; //Convert negative flag to index
530        qr[0] = ((double *) level_boundary_values -> data)[m]; 
531        qr[1] = ((double *) xmom_boundary_values -> data)[m];   
532        qr[2] = ((double *) ymom_boundary_values -> data)[m];   
533        zr = zl; //Extend bed elevation to boundary
534      } else {   
535        m = ((int *) neighbour_edges -> data)[ki];
536       
537        nm = n*3+m;     
538        qr[0] = ((double *) level_edge_values -> data)[nm];
539        qr[1] = ((double *) xmom_edge_values -> data)[nm];
540        qr[2] = ((double *) ymom_edge_values -> data)[nm];           
541        zr =    ((double *) bed_edge_values -> data)[nm];                 
542      }
543     
544      // Outward pointing normal vector   
545      // normal = domain.normals[k, 2*i:2*i+2]
546      ki2 = 2*ki; //k*6 + i*2
547      normal[0] = ((double *) normals -> data)[ki2];
548      normal[1] = ((double *) normals -> data)[ki2+1];     
549
550      //Edge flux computation
551      flux_function(ql, qr, zl, zr, 
552                    normal[0], normal[1],
553                    epsilon, g, 
554                    edgeflux, &max_speed);
555
556                   
557      //flux -= edgeflux * edgelengths[k,i]
558      for (j=0; j<3; j++) { 
559        flux[j] -= edgeflux[j]*((double *) edgelengths -> data)[ki];
560      }
561     
562      //Update timestep
563      //timestep = min(timestep, domain.radii[k]/max_speed)
564      if (max_speed > epsilon) {
565        timestep = min(timestep, ((double *) radii -> data)[k]/max_speed);
566      }   
567    } // end for i
568   
569    //Normalise by area and store for when all conserved
570    //quantities get updated
571    // flux /= areas[k]
572    for (j=0; j<3; j++) { 
573      flux[j] /= ((double *) areas -> data)[k];
574    }
575
576    ((double *) level_explicit_update -> data)[k] = flux[0];
577    ((double *) xmom_explicit_update -> data)[k] = flux[1];
578    ((double *) ymom_explicit_update -> data)[k] = flux[2];       
579
580  } //end for k
581
582  return Py_BuildValue("d", timestep);
583}   
584
585
586
587PyObject *protect(PyObject *self, PyObject *args) {
588  //
589  //    protect(minimum_allowed_height, wc, zc, xmomc, ymomc)
590 
591
592  PyArrayObject
593  *wc,            //Level at centroids
594  *zc,            //Elevation at centroids   
595  *xmomc,         //Momentums at centroids
596  *ymomc; 
597
598   
599  int N; 
600  double minimum_allowed_height;
601 
602  // Convert Python arguments to C 
603  if (!PyArg_ParseTuple(args, "dOOOO", 
604                        &minimum_allowed_height,
605                        &wc, &zc, &xmomc, &ymomc))
606    return NULL;
607
608  N = wc -> dimensions[0];
609   
610  _protect(N,
611           minimum_allowed_height,
612           (double*) wc -> data,
613           (double*) zc -> data, 
614           (double*) xmomc -> data, 
615           (double*) ymomc -> data);
616 
617  return Py_BuildValue(""); 
618}
619
620
621
622PyObject *balance_deep_and_shallow(PyObject *self, PyObject *args) {
623  //
624  //    balance_deep_and_shallow(wc, zc, hc, wv, zv, hv,
625  //                             xmomc, ymomc, xmomv, ymomv)
626 
627
628  PyArrayObject
629    *wc,            //Level at centroids
630    *zc,            //Elevation at centroids   
631    *hc,            //Height at centroids       
632    *wv,            //Level at vertices
633    *zv,            //Elevation at vertices
634    *hv,            //Heights at vertices   
635    *xmomc,         //Momentums at centroids and vertices
636    *ymomc, 
637    *xmomv, 
638    *ymomv;   
639   
640  int N; //, err;
641 
642  // Convert Python arguments to C 
643  if (!PyArg_ParseTuple(args, "OOOOOOOOOO", 
644                        &wc, &zc, &hc, 
645                        &wv, &zv, &hv,
646                        &xmomc, &ymomc, &xmomv, &ymomv))
647    return NULL;
648
649  N = wc -> dimensions[0];
650   
651  _balance_deep_and_shallow(N,
652                            (double*) wc -> data,
653                            (double*) zc -> data, 
654                            (double*) hc -> data,                           
655                            (double*) wv -> data, 
656                            (double*) zv -> data, 
657                            (double*) hv -> data,
658                            (double*) xmomc -> data, 
659                            (double*) ymomc -> data, 
660                            (double*) xmomv -> data, 
661                            (double*) ymomv -> data); 
662 
663 
664  return Py_BuildValue(""); 
665}
666
667
668
669//////////////////////////////////////////     
670// Method table for python module
671static struct PyMethodDef MethodTable[] = {
672  /* The cast of the function is necessary since PyCFunction values
673   * only take two PyObject* parameters, and rotate() takes
674   * three.
675   */
676 
677  {"rotate", (PyCFunction)rotate, METH_VARARGS | METH_KEYWORDS, "Print out"},
678  {"compute_fluxes", compute_fluxes, METH_VARARGS, "Print out"},   
679  {"gravity", gravity, METH_VARARGS, "Print out"},     
680  {"manning_friction", manning_friction, METH_VARARGS, "Print out"},       
681  {"balance_deep_and_shallow", balance_deep_and_shallow, 
682   METH_VARARGS, "Print out"},         
683  {"protect", protect, METH_VARARGS | METH_KEYWORDS, "Print out"},   
684  //{"distribute_to_vertices_and_edges",
685  // distribute_to_vertices_and_edges, METH_VARARGS},   
686  //{"update_conserved_quantities",
687  // update_conserved_quantities, METH_VARARGS},       
688  //{"set_initialcondition",
689  // set_initialcondition, METH_VARARGS},   
690  {NULL, NULL}
691};
692       
693// Module initialisation   
694void initshallow_water_ext(void){
695  Py_InitModule("shallow_water_ext", MethodTable);
696 
697  import_array();     //Necessary for handling of NumPY structures 
698}
Note: See TracBrowser for help on using the repository browser.