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

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

Looked at friction

File size: 18.9 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* z, 
159                       double* uh, double* vh, 
160                       double* eta, double* xmom, double* ymom) {     
161
162  int k;
163  double S, h;
164 
165  for (k=0; k<N; k++) {
166    if (eta[k] > eps) {
167      h = w[k]-z[k];
168      if (h >= eps) {
169        S = -g * eta[k]*eta[k] * sqrt((uh[k]*uh[k] + vh[k]*vh[k]));
170        S /= pow(h, 7.0/3);      //Expensive (on Ole's home computer)
171        //S /= h*h*(1 + h/3.0 - h*h/9.0); //FIXME: Could use a Taylor expansion
172
173
174        //Update momentum
175        xmom[k] += S*uh[k];
176        ymom[k] += S*vh[k];
177      }
178    }
179  }
180}
181           
182
183
184int _balance_deep_and_shallow(int N,
185                              double* wc,
186                              double* zc, 
187                              double* hc,                             
188                              double* wv, 
189                              double* zv, 
190                              double* hv,
191                              double* xmomc, 
192                              double* ymomc, 
193                              double* xmomv, 
194                              double* ymomv) { 
195 
196  int k, k3, i;
197  double dz, hmin, alpha;
198 
199  //Compute linear combination between constant levels and and
200  //levels parallel to the bed elevation.     
201 
202  for (k=0; k<N; k++) {
203    // Compute maximal variation in bed elevation
204    // This quantitiy is
205    //     dz = max_i abs(z_i - z_c)
206    // and it is independent of dimension
207    // In the 1d case zc = (z0+z1)/2
208    // In the 2d case zc = (z0+z1+z2)/3
209
210    k3 = 3*k;
211   
212    //FIXME: Try with this one precomputed
213    dz = 0.0;
214    hmin = hv[k3];
215    for (i=0; i<3; i++) {
216      dz = max(dz, fabs(zv[k3+i]-zc[k]));
217      hmin = min(hmin, hv[k3+i]);
218    }
219
220   
221    //Create alpha in [0,1], where alpha==0 means using shallow
222    //first order scheme and alpha==1 means using the stage w as
223    //computed by the gradient limiter (1st or 2nd order)
224    //
225    //If hmin > dz/2 then alpha = 1 and the bed will have no effect
226    //If hmin < 0 then alpha = 0 reverting to constant height above bed.
227   
228    if (dz > 0.0) 
229      alpha = max( min( 2*hmin/dz, 1.0), 0.0 );
230    else
231      alpha = 1.0;  //Flat bed
232
233     
234    //Weighted balance between stage parallel to bed elevation
235    //(wvi = zvi + hc) and stage as computed by 1st or 2nd
236    //order gradient limiter
237    //(wvi = zvi + hvi) where i=0,1,2 denotes the vertex ids
238    //
239    //It follows that the updated wvi is
240    //  wvi := (1-alpha)*(zvi+hc) + alpha*(zvi+hvi) =
241    //  zvi + hc + alpha*(hvi - hc)
242    //
243    //Note that hvi = zc+hc-zvi in the first order case (constant).
244
245    if (alpha < 1) {         
246      for (i=0; i<3; i++) {
247        wv[k3+i] = zv[k3+i] + hc[k] + alpha*(hv[k3+i]-hc[k]);
248           
249     
250        //Update momentum as a linear combination of
251        //xmomc and ymomc (shallow) and momentum
252        //from extrapolator xmomv and ymomv (deep).
253        xmomv[k3+i] = (1-alpha)*xmomc[k] + alpha*xmomv[k3+i];           
254        ymomv[k3+i] = (1-alpha)*ymomc[k] + alpha*ymomv[k3+i];
255      } 
256    }
257  }         
258  return 0;
259}
260
261
262
263int _protect(int N, 
264             double minimum_allowed_height,       
265             double* wc,
266             double* zc, 
267             double* xmomc, 
268             double* ymomc) {
269 
270  int k; 
271  double hc;
272 
273  //Protect against initesimal and negative heights
274 
275  for (k=0; k<N; k++) {
276    hc = wc[k] - zc[k];
277   
278    if (hc < minimum_allowed_height) {   
279      wc[k] = zc[k];       
280      xmomc[k] = 0.0;
281      ymomc[k] = 0.0;     
282    }
283   
284  }
285  return 0;
286}
287
288
289
290///////////////////////////////////////////////////////////////////
291// Gateways to Python
292
293PyObject *gravity(PyObject *self, PyObject *args) {
294  //
295  //  gravity(g, h, v, x, xmom, ymom)
296  //
297 
298 
299  PyArrayObject *h, *v, *x, *xmom, *ymom;
300  int k, i, N, k3, k6;
301  double g, avg_h, zx, zy;
302  double x0, y0, x1, y1, x2, y2, z0, z1, z2;
303   
304  if (!PyArg_ParseTuple(args, "dOOOOO",
305                        &g, &h, &v, &x, 
306                        &xmom, &ymom)) 
307    return NULL; 
308
309  N = h -> dimensions[0];
310  for (k=0; k<N; k++) {
311    k3 = 3*k;  // base index
312    k6 = 6*k;  // base index   
313   
314    avg_h = 0.0;
315    for (i=0; i<3; i++) {
316      avg_h += ((double *) h -> data)[k3+i];
317    }   
318    avg_h /= 3;
319       
320   
321    //Compute bed slope
322    x0 = ((double*) x -> data)[k6 + 0];
323    y0 = ((double*) x -> data)[k6 + 1];   
324    x1 = ((double*) x -> data)[k6 + 2];
325    y1 = ((double*) x -> data)[k6 + 3];       
326    x2 = ((double*) x -> data)[k6 + 4];
327    y2 = ((double*) x -> data)[k6 + 5];           
328
329
330    z0 = ((double*) v -> data)[k3 + 0];
331    z1 = ((double*) v -> data)[k3 + 1];
332    z2 = ((double*) v -> data)[k3 + 2];       
333
334    _gradient(x0, y0, x1, y1, x2, y2, z0, z1, z2, &zx, &zy);
335
336    //Update momentum
337    ((double*) xmom -> data)[k] += -g*zx*avg_h;
338    ((double*) ymom -> data)[k] += -g*zy*avg_h;       
339  }
340   
341  return Py_BuildValue("");
342}
343
344
345PyObject *manning_friction(PyObject *self, PyObject *args) {
346  //
347  // manning_friction(g, eps, h, uh, vh, eta, xmom_update, ymom_update)
348  //
349 
350 
351  PyArrayObject *w, *z, *uh, *vh, *eta, *xmom, *ymom;
352  int N;
353  double g, eps;
354   
355  if (!PyArg_ParseTuple(args, "ddOOOOOOO",
356                        &g, &eps, &w, &z, &uh, &vh, &eta, 
357                        &xmom, &ymom)) 
358    return NULL; 
359
360  N = w -> dimensions[0];   
361  _manning_friction(g, eps, N,
362                    (double*) w -> data,
363                    (double*) z -> data,                   
364                    (double*) uh -> data, 
365                    (double*) vh -> data, 
366                    (double*) eta -> data,
367                    (double*) xmom -> data, 
368                    (double*) ymom -> data);
369
370  return Py_BuildValue("");
371}                   
372
373PyObject *rotate(PyObject *self, PyObject *args, PyObject *kwargs) {
374  //
375  // r = rotate(q, normal, direction=1)
376  //
377  // Where q is assumed to be a Float numeric array of length 3 and
378  // normal a Float numeric array of length 2.
379
380 
381  PyObject *Q, *Normal;
382  PyArrayObject *q, *r, *normal;
383 
384  static char *argnames[] = {"q", "normal", "direction", NULL};
385  int dimensions[1], i, direction=1;
386  double n1, n2;
387
388  // Convert Python arguments to C 
389  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|i", argnames, 
390                                   &Q, &Normal, &direction)) 
391    return NULL; 
392
393  //Input checks (convert sequences into numeric arrays)
394  q = (PyArrayObject *) 
395    PyArray_ContiguousFromObject(Q, PyArray_DOUBLE, 0, 0);
396  normal = (PyArrayObject *) 
397    PyArray_ContiguousFromObject(Normal, PyArray_DOUBLE, 0, 0);
398 
399  //Allocate space for return vector r (don't DECREF)
400  dimensions[0] = 3;
401  r = (PyArrayObject *) PyArray_FromDims(1, dimensions, PyArray_DOUBLE);
402
403  //Copy
404  for (i=0; i<3; i++) {
405    ((double *) (r -> data))[i] = ((double *) (q -> data))[i]; 
406  }
407 
408  //Get normal and direction
409  n1 = ((double *) normal -> data)[0]; 
410  n2 = ((double *) normal -> data)[1];   
411  if (direction == -1) n2 = -n2;
412
413  //Rotate
414  _rotate((double *) r -> data, n1, n2);
415
416  //Release numeric arrays
417  Py_DECREF(q);   
418  Py_DECREF(normal);
419       
420  //return result using PyArray to avoid memory leak
421  return PyArray_Return(r);
422}   
423
424
425PyObject *compute_fluxes(PyObject *self, PyObject *args) {
426  /*Compute all fluxes and the timestep suitable for all volumes
427    in domain.
428
429    Compute total flux for each conserved quantity using "flux_function"
430
431    Fluxes across each edge are scaled by edgelengths and summed up
432    Resulting flux is then scaled by area and stored in
433    explicit_update for each of the three conserved quantities
434    level, xmomentum and ymomentum
435
436    The maximal allowable speed computed by the flux_function for each volume
437    is converted to a timestep that must not be exceeded. The minimum of
438    those is computed as the next overall timestep.
439   
440    Python call:
441    domain.timestep = compute_fluxes(timestep,
442                                     domain.epsilon,
443                                     domain.g,
444                                     domain.neighbours,
445                                     domain.neighbour_edges,
446                                     domain.normals,
447                                     domain.edgelengths,                       
448                                     domain.radii,
449                                     domain.areas,
450                                     Level.edge_values,
451                                     Xmom.edge_values,
452                                     Ymom.edge_values, 
453                                     Bed.edge_values,   
454                                     Level.boundary_values,
455                                     Xmom.boundary_values,
456                                     Ymom.boundary_values,
457                                     Level.explicit_update,
458                                     Xmom.explicit_update,
459                                     Ymom.explicit_update)
460       
461
462    Post conditions:
463      domain.explicit_update is reset to computed flux values
464      domain.timestep is set to the largest step satisfying all volumes.
465
466           
467  */
468
469 
470  PyArrayObject *neighbours, *neighbour_edges,
471    *normals, *edgelengths, *radii, *areas,
472    *level_edge_values, 
473    *xmom_edge_values, 
474    *ymom_edge_values, 
475    *bed_edge_values,   
476    *level_boundary_values,
477    *xmom_boundary_values,
478    *ymom_boundary_values,
479    *level_explicit_update,
480    *xmom_explicit_update,
481    *ymom_explicit_update;
482
483   
484  //Local variables 
485  double timestep, max_speed, epsilon, g;
486  double normal[2], ql[3], qr[3], zl, zr;
487  double flux[3], edgeflux[3]; //Work arrays for summing up fluxes
488
489  int number_of_elements, k, i, j, m, n;
490  int ki, nm, ki2; //Index shorthands
491 
492 
493  // Convert Python arguments to C 
494  if (!PyArg_ParseTuple(args, "dddOOOOOOOOOOOOOOOO",
495                        &timestep,
496                        &epsilon,
497                        &g,
498                        &neighbours, 
499                        &neighbour_edges,
500                        &normals, 
501                        &edgelengths, &radii, &areas,
502                        &level_edge_values, 
503                        &xmom_edge_values, 
504                        &ymom_edge_values, 
505                        &bed_edge_values,   
506                        &level_boundary_values,
507                        &xmom_boundary_values,
508                        &ymom_boundary_values,
509                        &level_explicit_update,
510                        &xmom_explicit_update,
511                        &ymom_explicit_update)) {
512    PyErr_SetString(PyExc_RuntimeError, "Input arguments failed");
513    return NULL;
514  }
515
516  number_of_elements = level_edge_values -> dimensions[0];
517 
518   
519  for (k=0; k<number_of_elements; k++) {
520
521    //Reset work array
522    for (j=0; j<3; j++) flux[j] = 0.0;
523                         
524    //Loop through neighbours and compute edge flux for each
525    for (i=0; i<3; i++) {
526      ki = k*3+i;
527      ql[0] = ((double *) level_edge_values -> data)[ki];
528      ql[1] = ((double *) xmom_edge_values -> data)[ki];
529      ql[2] = ((double *) ymom_edge_values -> data)[ki];           
530      zl =    ((double *) bed_edge_values -> data)[ki];                 
531     
532      //Quantities at neighbour on nearest face
533      n = ((int *) neighbours -> data)[ki];
534      if (n < 0) {
535        m = -n-1; //Convert negative flag to index
536        qr[0] = ((double *) level_boundary_values -> data)[m]; 
537        qr[1] = ((double *) xmom_boundary_values -> data)[m];   
538        qr[2] = ((double *) ymom_boundary_values -> data)[m];   
539        zr = zl; //Extend bed elevation to boundary
540      } else {   
541        m = ((int *) neighbour_edges -> data)[ki];
542       
543        nm = n*3+m;     
544        qr[0] = ((double *) level_edge_values -> data)[nm];
545        qr[1] = ((double *) xmom_edge_values -> data)[nm];
546        qr[2] = ((double *) ymom_edge_values -> data)[nm];           
547        zr =    ((double *) bed_edge_values -> data)[nm];                 
548      }
549     
550      // Outward pointing normal vector   
551      // normal = domain.normals[k, 2*i:2*i+2]
552      ki2 = 2*ki; //k*6 + i*2
553      normal[0] = ((double *) normals -> data)[ki2];
554      normal[1] = ((double *) normals -> data)[ki2+1];     
555
556      //Edge flux computation
557      flux_function(ql, qr, zl, zr, 
558                    normal[0], normal[1],
559                    epsilon, g, 
560                    edgeflux, &max_speed);
561
562                   
563      //flux -= edgeflux * edgelengths[k,i]
564      for (j=0; j<3; j++) { 
565        flux[j] -= edgeflux[j]*((double *) edgelengths -> data)[ki];
566      }
567     
568      //Update timestep
569      //timestep = min(timestep, domain.radii[k]/max_speed)
570      if (max_speed > epsilon) {
571        timestep = min(timestep, ((double *) radii -> data)[k]/max_speed);
572      }   
573    } // end for i
574   
575    //Normalise by area and store for when all conserved
576    //quantities get updated
577    // flux /= areas[k]
578    for (j=0; j<3; j++) { 
579      flux[j] /= ((double *) areas -> data)[k];
580    }
581
582    ((double *) level_explicit_update -> data)[k] = flux[0];
583    ((double *) xmom_explicit_update -> data)[k] = flux[1];
584    ((double *) ymom_explicit_update -> data)[k] = flux[2];       
585
586  } //end for k
587
588  return Py_BuildValue("d", timestep);
589}   
590
591
592
593PyObject *protect(PyObject *self, PyObject *args) {
594  //
595  //    protect(minimum_allowed_height, wc, zc, xmomc, ymomc)
596 
597
598  PyArrayObject
599  *wc,            //Level at centroids
600  *zc,            //Elevation at centroids   
601  *xmomc,         //Momentums at centroids
602  *ymomc; 
603
604   
605  int N; 
606  double minimum_allowed_height;
607 
608  // Convert Python arguments to C 
609  if (!PyArg_ParseTuple(args, "dOOOO", 
610                        &minimum_allowed_height,
611                        &wc, &zc, &xmomc, &ymomc))
612    return NULL;
613
614  N = wc -> dimensions[0];
615   
616  _protect(N,
617           minimum_allowed_height,
618           (double*) wc -> data,
619           (double*) zc -> data, 
620           (double*) xmomc -> data, 
621           (double*) ymomc -> data);
622 
623  return Py_BuildValue(""); 
624}
625
626
627
628PyObject *balance_deep_and_shallow(PyObject *self, PyObject *args) {
629  //
630  //    balance_deep_and_shallow(wc, zc, hc, wv, zv, hv,
631  //                             xmomc, ymomc, xmomv, ymomv)
632 
633
634  PyArrayObject
635    *wc,            //Level at centroids
636    *zc,            //Elevation at centroids   
637    *hc,            //Height at centroids       
638    *wv,            //Level at vertices
639    *zv,            //Elevation at vertices
640    *hv,            //Heights at vertices   
641    *xmomc,         //Momentums at centroids and vertices
642    *ymomc, 
643    *xmomv, 
644    *ymomv;   
645   
646  int N; //, err;
647 
648  // Convert Python arguments to C 
649  if (!PyArg_ParseTuple(args, "OOOOOOOOOO", 
650                        &wc, &zc, &hc, 
651                        &wv, &zv, &hv,
652                        &xmomc, &ymomc, &xmomv, &ymomv))
653    return NULL;
654
655  N = wc -> dimensions[0];
656   
657  _balance_deep_and_shallow(N,
658                            (double*) wc -> data,
659                            (double*) zc -> data, 
660                            (double*) hc -> data,                           
661                            (double*) wv -> data, 
662                            (double*) zv -> data, 
663                            (double*) hv -> data,
664                            (double*) xmomc -> data, 
665                            (double*) ymomc -> data, 
666                            (double*) xmomv -> data, 
667                            (double*) ymomv -> data); 
668 
669 
670  return Py_BuildValue(""); 
671}
672
673
674
675//////////////////////////////////////////     
676// Method table for python module
677static struct PyMethodDef MethodTable[] = {
678  /* The cast of the function is necessary since PyCFunction values
679   * only take two PyObject* parameters, and rotate() takes
680   * three.
681   */
682 
683  {"rotate", (PyCFunction)rotate, METH_VARARGS | METH_KEYWORDS, "Print out"},
684  {"compute_fluxes", compute_fluxes, METH_VARARGS, "Print out"},   
685  {"gravity", gravity, METH_VARARGS, "Print out"},     
686  {"manning_friction", manning_friction, METH_VARARGS, "Print out"},       
687  {"balance_deep_and_shallow", balance_deep_and_shallow, 
688   METH_VARARGS, "Print out"},         
689  {"protect", protect, METH_VARARGS | METH_KEYWORDS, "Print out"},   
690  //{"distribute_to_vertices_and_edges",
691  // distribute_to_vertices_and_edges, METH_VARARGS},   
692  //{"update_conserved_quantities",
693  // update_conserved_quantities, METH_VARARGS},       
694  //{"set_initialcondition",
695  // set_initialcondition, METH_VARARGS},   
696  {NULL, NULL}
697};
698       
699// Module initialisation   
700void initshallow_water_ext(void){
701  Py_InitModule("shallow_water_ext", MethodTable);
702 
703  import_array();     //Necessary for handling of NumPY structures 
704}
Note: See TracBrowser for help on using the repository browser.