source: anuga_work/development/sudi/sw_2d/swb_domain_ext.c @ 7837

Last change on this file since 7837 was 7738, checked in by steve, 15 years ago

Adding a directory for Sudi to work on.

File size: 21.3 KB
Line 
1// Python - C extension module for shallow_water.py
2//
3// To compile (Python2.3):
4//  gcc -c swb_domain_ext.c -I/usr/include/python2.3 -o domain_ext.o -Wall -O
5//  gcc -shared swb_domain_ext.o  -o swb_domain_ext.so
6//
7// or use python compile.py
8//
9// See the module swb_domain.py for more documentation on
10// how to use this module
11//
12//
13// Stephen Roberts, ANU 2009
14// Ole Nielsen, GA 2004
15
16
17#include "Python.h"
18#include "numpy/arrayobject.h"
19#include "math.h"
20#include <stdio.h>
21//#include "numpy_shim.h"
22
23// Shared code snippets
24#include "util_ext.h"
25
26
27const double pi = 3.14159265358979;
28
29// Computational function for rotation
30int _rotate(double *q, double n1, double n2) {
31  /*Rotate the last  2 coordinates of q (q[1], q[2])
32    from x,y coordinates to coordinates based on normal vector (n1, n2).
33
34    Result is returned in array 2x1 r
35    To rotate in opposite direction, call rotate with (q, n1, -n2)
36
37    Contents of q are changed by this function */
38
39
40  double q1, q2;
41
42  // Shorthands
43  q1 = q[1];  // x coordinate
44  q2 = q[2];  // y coordinate
45
46  // Rotate
47  q[1] =  n1*q1 + n2*q2;
48  q[2] = -n2*q1 + n1*q2;
49
50  return 0;
51}
52
53// Innermost flux function
54int _flux_function(double *q_inside, double *q_outside,
55                   double n1, double n2, 
56                   double g,
57                   double *edgeflux, double *local_max_speed) 
58{
59
60  /*Compute fluxes between volumes for the shallow water wave equation
61    cast in terms of the 'stage', w = h+z using
62    the 'central scheme' as described in
63
64    Kurganov, Noelle, Petrova. 'Semidiscrete Central-Upwind Schemes For
65    Hyperbolic Conservation Laws and Hamilton-Jacobi Equations'.
66    Siam J. Sci. Comput. Vol. 23, No. 3, pp. 707-740.
67
68    The implemented formula is given in equation (3.15) on page 714
69  */
70
71  int i;
72
73  double z_star, h_inside_star, h_outside_star;
74  double w_inside,  h_inside,  u_inside,  v_inside,  z_inside;
75  double w_outside, h_outside, u_outside, v_outside, z_outside;
76  double s_min, s_max, soundspeed_inside, soundspeed_outside;
77  double epsilon, denom, inverse_denominator;
78
79  // Workspace
80  double cons_inside[3], cons_outside[3], flux_outside[3], flux_inside[3];
81
82  epsilon = 1.0e-15;
83
84  // Setup aliases
85  w_inside = q_inside[0]; 
86  h_inside = q_inside[3];
87  z_inside = q_inside[4];
88  u_inside = q_inside[5];
89  v_inside = q_inside[6];
90
91  w_outside = q_outside[0]; 
92  h_outside = q_outside[3];
93  z_outside = q_outside[4];
94  u_outside = q_outside[5];
95  v_outside = q_outside[6];
96
97
98  // Rotate the velocity terms and update. Setup cons_ to hold conservative rotated
99  // variables
100  cons_inside[0] = w_inside;
101  cons_inside[1] = u_inside;
102  cons_inside[2] = v_inside;
103
104  _rotate(cons_inside, n1, n2);
105
106  u_inside = cons_inside[1];
107  v_inside = cons_inside[2];
108  cons_inside[1] = u_inside*h_inside;
109  cons_inside[2] = v_inside*h_inside;
110
111  cons_outside[0] = w_outside;
112  cons_outside[1] = u_outside;
113  cons_outside[2] = v_outside;
114
115  _rotate(cons_outside, n1, n2);
116
117  u_outside = cons_outside[1];
118  v_outside = cons_outside[2];
119  cons_outside[1] = u_outside*h_outside;
120  cons_outside[2] = v_outside*h_outside;
121
122
123
124  // Deal with discontinuous z
125  z_star = max(z_inside, z_outside);
126  h_inside_star  = max(0.0, w_inside-z_star);
127  h_outside_star = max(0.0, w_outside-z_star);
128                   
129
130  // Maximal and minimal wave speeds
131  soundspeed_inside  = sqrt(g*h_inside_star);
132  soundspeed_outside = sqrt(g*h_outside_star); 
133 
134  s_max = max(u_inside + soundspeed_inside, u_outside + soundspeed_outside);
135  s_max = max(0.0, s_max);
136
137  s_min = min(u_inside - soundspeed_inside, u_outside - soundspeed_outside);
138  s_min = min(0.0, s_min);
139 
140  // Flux formulas
141  flux_inside[0] = u_inside*h_inside_star;
142  flux_inside[1] = u_inside*u_inside*h_inside_star + 0.5*g*h_inside_star*h_inside_star;
143  flux_inside[2] = u_inside*v_inside*h_inside_star;
144
145  flux_outside[0] = u_outside*h_outside_star;
146  flux_outside[1] = u_outside*u_outside*h_outside_star + 0.5*g*h_outside_star*h_outside_star;
147  flux_outside[2] = u_outside*v_outside*h_outside_star;
148
149  // Flux computation
150  denom = s_max - s_min;
151
152  memset(edgeflux, 0, 3*sizeof(double));
153 
154  if (denom < epsilon) 
155    {     
156      *local_max_speed = 0.0;
157      //printf("here %g\n",h_inside);
158      // Add in edge pressure term due to discontinuous bed
159      if ( h_inside > 0.0 ) edgeflux[1] = 0.5*g*h_inside*h_inside ;
160    } 
161  else 
162    {
163      inverse_denominator = 1.0/denom;
164      for (i = 0; i < 3; i++) 
165        {
166          edgeflux[i] = s_max*flux_inside[i] - s_min*flux_outside[i];
167          edgeflux[i] += s_max*s_min*(cons_outside[i] - cons_inside[i]);
168          edgeflux[i] *= inverse_denominator;
169        }
170
171      // Balance the pressure term
172      edgeflux[1] += 0.5*g*h_inside*h_inside - 0.5*g*h_inside_star*h_inside_star;
173     
174    }
175 
176// Maximal wavespeed
177  *local_max_speed = max(fabs(s_max), fabs(s_min));
178  //printf("local speed % g  h_inside %g \n",*local_max_speed, h_inside);
179  // Rotate back
180  _rotate(edgeflux, n1, -n2);
181 
182  return 0;
183}
184
185
186
187//=========================================================================
188// Python Glue
189//=========================================================================
190
191
192//========================================================================
193// Compute fluxes
194//========================================================================
195
196PyObject *compute_fluxes(PyObject *self, PyObject *args) {
197  /*Compute all fluxes and the timestep suitable for all volumes
198    in domain.
199
200    Compute total flux for each conserved quantity using "flux_function_central"
201
202    Fluxes across each edge are scaled by edgelengths and summed up
203    Resulting flux is then scaled by area and stored in
204    explicit_update for each of the three conserved quantities
205    stage, xmomentum and ymomentum
206
207    The maximal allowable speed computed by the flux_function for each volume
208    is converted to a timestep that must not be exceeded. The minimum of
209    those is computed as the next overall timestep.
210
211    Python call:
212    timestep = compute_fluxes(timestep, domain, stage, xmom, ymom, height, bed, xvel, yvel)
213
214
215    Post conditions:
216      domain.explicit_update is reset to computed flux values
217      returns timestep which is the largest step satisfying all volumes.
218
219
220  */
221
222  PyObject
223    *domain,
224    *stage, 
225    *xmom, 
226    *ymom,
227    *height,
228    *bed,
229    *xvel,
230    *yvel;
231
232    PyArrayObject
233      *num_neighbours          ,
234      *num_neighbour_edges     ,
235      *num_normals             ,
236      *num_edgelengths         ,     
237      *num_radii               ,   
238      *num_areas               ,
239      *num_tri_full_flag       ,
240      *num_max_speed           ,
241      *num_stage_edge_values   ,
242      *num_xmom_edge_values    ,
243      *num_ymom_edge_values    ,
244      *num_height_edge_values  ,
245      *num_bed_edge_values     ,
246      *num_xvel_edge_values    ,
247      *num_yvel_edge_values    ,
248      *num_stage_boundary_values ,
249      *num_xmom_boundary_values  ,
250      *num_ymom_boundary_values  ,
251      *num_height_boundary_values,
252      *num_bed_boundary_values   ,
253      *num_xvel_boundary_values  ,
254      *num_yvel_boundary_values  ,
255      *num_stage_explicit_update ,
256      *num_xmom_explicit_update  ,
257      *num_ymom_explicit_update ;
258
259    long
260      *neighbours          ,
261      *neighbour_edges     ,
262      *tri_full_flag       ; 
263
264    double
265      *normals             ,
266      *edgelengths         ,     
267      *radii               ,   
268      *areas               ,
269      *max_speed           ,
270      *stage_edge_values   ,
271      *xmom_edge_values    ,
272      *ymom_edge_values    ,
273      *height_edge_values  ,
274      *bed_edge_values     ,
275      *xvel_edge_values    ,
276      *yvel_edge_values    ,
277      *stage_boundary_values ,
278      *xmom_boundary_values  ,
279      *ymom_boundary_values  ,
280      *height_boundary_values,
281      *bed_boundary_values   ,
282      *xvel_boundary_values  ,
283      *yvel_boundary_values  ,
284      *stage_explicit_update ,
285      *xmom_explicit_update  ,
286      *ymom_explicit_update ;
287
288   
289
290    double flux_timestep, g;
291    double local_max_speed, length, inv_area;
292
293    int k, i, m, n;
294    int ki, nm=0, ki2; // Index shorthands
295   
296    double ql[7], qr[7]; // Work arrays for storing local evolving quantities
297    double edgeflux[3];  // Work array for summing up fluxes
298
299   
300    //======================================================================
301    // Convert Python arguments to C
302    //======================================================================
303    if (!PyArg_ParseTuple(args, "dOOOOOOOO", 
304                          &flux_timestep, 
305                          &domain, 
306                          &stage, 
307                          &xmom, 
308                          &ymom, 
309                          &height,
310                          &bed,
311                          &xvel, 
312                          &yvel)) {
313      PyErr_SetString(PyExc_RuntimeError, "Input arguments failed");
314      return NULL;
315    }
316   
317    //======================================================================
318    // Extract data from python objects
319    //======================================================================
320    g = get_python_double(domain,"g");
321   
322    num_neighbours             = get_consecutive_array(domain, "neighbours");
323    num_neighbour_edges        = get_consecutive_array(domain, "neighbour_edges"); 
324    num_normals                = get_consecutive_array(domain, "normals");
325    num_edgelengths            = get_consecutive_array(domain, "edgelengths");   
326    num_radii                  = get_consecutive_array(domain, "radii");   
327    num_areas                  = get_consecutive_array(domain, "areas");   
328    num_tri_full_flag          = get_consecutive_array(domain, "tri_full_flag");
329    num_max_speed              = get_consecutive_array(domain, "max_speed");
330   
331    num_stage_edge_values      = get_consecutive_array(stage, "edge_values");   
332    num_xmom_edge_values       = get_consecutive_array(xmom, "edge_values");   
333    num_ymom_edge_values       = get_consecutive_array(ymom, "edge_values"); 
334    num_height_edge_values     = get_consecutive_array(height, "edge_values"); 
335    num_bed_edge_values        = get_consecutive_array(bed, "edge_values");       
336    num_xvel_edge_values       = get_consecutive_array(xvel, "edge_values");   
337    num_yvel_edge_values       = get_consecutive_array(yvel, "edge_values");   
338   
339    num_stage_boundary_values  = get_consecutive_array(stage, "boundary_values");   
340    num_xmom_boundary_values   = get_consecutive_array(xmom, "boundary_values");   
341    num_ymom_boundary_values   = get_consecutive_array(ymom, "boundary_values"); 
342    num_height_boundary_values = get_consecutive_array(height, "boundary_values"); 
343    num_bed_boundary_values    = get_consecutive_array(bed, "boundary_values"); 
344    num_xvel_boundary_values   = get_consecutive_array(xvel, "boundary_values"); 
345    num_yvel_boundary_values   = get_consecutive_array(yvel, "boundary_values"); 
346   
347    num_stage_explicit_update  = get_consecutive_array(stage, "explicit_update");   
348    num_xmom_explicit_update   = get_consecutive_array(xmom, "explicit_update");   
349    num_ymom_explicit_update   = get_consecutive_array(ymom, "explicit_update"); 
350   
351    //---------------------------------------------------------------------------
352   
353    neighbours         = (long *) num_neighbours-> data;
354    neighbour_edges    = (long *) num_neighbour_edges-> data;
355    normals            = (double *) num_normals -> data;
356    edgelengths        = (double *) num_edgelengths -> data;
357    radii              = (double *) num_radii -> data;
358    areas              = (double *) num_areas -> data; 
359    tri_full_flag      = (long *) num_tri_full_flag -> data; 
360    max_speed          = (double *) num_max_speed -> data;
361   
362    stage_edge_values       = (double *) num_stage_edge_values -> data;   
363    xmom_edge_values        = (double *) num_xmom_edge_values -> data;
364    ymom_edge_values        = (double *) num_ymom_edge_values -> data;
365    height_edge_values      = (double *) num_height_edge_values -> data;
366    bed_edge_values         = (double *) num_bed_edge_values -> data;
367    xvel_edge_values        = (double *) num_xvel_edge_values -> data;
368    yvel_edge_values        = (double *) num_yvel_edge_values -> data;
369   
370    stage_boundary_values      = (double *) num_stage_boundary_values -> data; 
371    xmom_boundary_values       = (double *) num_xmom_boundary_values -> data;
372    ymom_boundary_values       = (double *) num_ymom_boundary_values -> data;
373    height_boundary_values     = (double *) num_height_boundary_values -> data;
374    bed_boundary_values        = (double *) num_bed_boundary_values -> data;
375    xvel_boundary_values       = (double *) num_xvel_boundary_values -> data;
376    yvel_boundary_values       = (double *) num_yvel_boundary_values -> data;
377   
378    stage_explicit_update      = (double *) num_stage_explicit_update -> data;
379    xmom_explicit_update       = (double *) num_xmom_explicit_update -> data;
380    ymom_explicit_update       = (double *) num_ymom_explicit_update -> data;
381   
382   
383    int number_of_elements = num_stage_edge_values -> dimensions[0];
384
385    //======================================================================
386    // Flux computation routine and update the explicit update arrays
387    //======================================================================
388
389    // Set explicit_update to zero for all conserved_quantities.
390    // This assumes compute_fluxes called before forcing terms
391
392    memset((char*) stage_explicit_update, 0, number_of_elements*sizeof(double));
393    memset((char*) xmom_explicit_update, 0, number_of_elements*sizeof(double));
394    memset((char*) ymom_explicit_update, 0, number_of_elements*sizeof(double));   
395 
396    // For all triangles
397    for (k = 0; k < number_of_elements; k++) 
398      { 
399        max_speed[k] = 0.0;
400
401        // Loop through neighbours and compute edge flux for each  epsilon
402        for (i = 0; i < 3; i++) 
403          {
404            ki = k*3 + i; // Linear index to edge i of triangle k
405     
406            // Get left hand side values from triangle k, edge i
407            ql[0] = stage_edge_values[ki];
408            ql[1] = xmom_edge_values[ki];
409            ql[2] = ymom_edge_values[ki];
410            ql[3] = height_edge_values[ki];
411            ql[4] = bed_edge_values[ki];
412            ql[5] = xvel_edge_values[ki];
413            ql[6] = yvel_edge_values[ki];
414
415            // Get right hand side values either from neighbouring triangle
416            // or from boundary array (Quantities at neighbour on nearest face).
417            n = neighbours[ki];
418            if (n < 0) 
419              {
420                // Neighbour is a boundary condition
421                m = -n - 1; // Convert negative flag to boundary index
422               
423                qr[0] = stage_boundary_values[m];
424                qr[1] = xmom_boundary_values[m];
425                qr[2] = ymom_boundary_values[m];
426                qr[3] = height_boundary_values[m];
427                qr[4] = bed_boundary_values[m];
428                qr[5] = xvel_boundary_values[m];
429                qr[6] = yvel_boundary_values[m];
430               
431              } 
432            else 
433              {
434                // Neighbour is a real triangle
435                m = neighbour_edges[ki];
436                nm = n*3 + m; // Linear index (triangle n, edge m)
437               
438                qr[0] = stage_edge_values[nm];
439                qr[1] = xmom_edge_values[nm];
440                qr[2] = ymom_edge_values[nm];
441                qr[3] = height_edge_values[nm];
442                qr[4] = bed_edge_values[nm];
443                qr[5] = xvel_edge_values[nm];
444                qr[6] = yvel_edge_values[nm];
445               
446              }
447           
448            // Now we have values for this edge - both from left and right side.
449
450            // Outward pointing normal vector (domain.normals[k, 2*i:2*i+2])
451            ki2 = 2*ki; //k*6 + i*2
452
453            // Edge flux computation (triangle k, edge i)
454            _flux_function(ql,
455                           qr,
456                           normals[ki2], 
457                           normals[ki2+1],
458                           g,
459                           edgeflux, 
460                           &local_max_speed);
461           
462           
463            // Multiply edgeflux by edgelength
464            length = edgelengths[ki];
465            edgeflux[0] *= length;           
466            edgeflux[1] *= length;           
467            edgeflux[2] *= length;                       
468     
469     
470            // Update triangle k with flux from edge i
471            stage_explicit_update[k] -= edgeflux[0];
472            xmom_explicit_update[k]  -= edgeflux[1];
473            ymom_explicit_update[k]  -= edgeflux[2];
474     
475
476            // Update flux_timestep based on edge i and possibly neighbour n
477            if (tri_full_flag[k] == 1) 
478              {
479                if (local_max_speed > 1.0e-15) 
480                  {
481                    // Calculate safe timestep based on local_max_speed and size of triangle k
482                    // A reduction of timestep based on domain.CFL is applied in update_timestep
483                    flux_timestep = min(flux_timestep, radii[k]/local_max_speed);
484                   
485                  }
486              }
487           
488          } // End edge i
489       
490       
491        // Normalise triangle k by area and store for when all conserved
492        // quantities get updated
493        inv_area = 1.0/areas[k];
494        stage_explicit_update[k] *= inv_area;
495        xmom_explicit_update[k]  *= inv_area;
496        ymom_explicit_update[k]  *= inv_area;
497       
498       
499        // Keep track of maximal speeds
500        max_speed[k] = max(max_speed[k],local_max_speed);
501       
502      } // End triangle k
503   
504   
505    //======================================================================
506    // Cleanup
507    //======================================================================
508
509    Py_DECREF(num_neighbours            );
510    Py_DECREF(num_neighbour_edges       );
511    Py_DECREF(num_normals               );
512    Py_DECREF(num_edgelengths           );     
513    Py_DECREF(num_radii                 );   
514    Py_DECREF(num_areas                 );
515    Py_DECREF(num_tri_full_flag         );
516    Py_DECREF(num_max_speed             );
517    Py_DECREF(num_stage_edge_values     );
518    Py_DECREF(num_xmom_edge_values      );
519    Py_DECREF(num_ymom_edge_values      );
520    Py_DECREF(num_height_edge_values    );
521    Py_DECREF(num_bed_edge_values       );
522    Py_DECREF(num_xvel_edge_values      );
523    Py_DECREF(num_yvel_edge_values      );
524    Py_DECREF(num_stage_boundary_values );
525    Py_DECREF(num_xmom_boundary_values  );
526    Py_DECREF(num_ymom_boundary_values  );
527    Py_DECREF(num_height_boundary_values);
528    Py_DECREF(num_bed_boundary_values   );
529    Py_DECREF(num_xvel_boundary_values  );
530    Py_DECREF(num_yvel_boundary_values  );
531    Py_DECREF(num_stage_explicit_update );
532    Py_DECREF(num_xmom_explicit_update  );
533    Py_DECREF(num_ymom_explicit_update  );
534
535    //======================================================================
536    // Return updated flux flux_timestep
537    //======================================================================
538    return Py_BuildValue("d", flux_timestep);
539}
540
541//========================================================================
542// Flux Function
543//========================================================================
544
545PyObject *flux_function(PyObject *self, PyObject *args) {
546  //
547  // Gateway to innermost flux function.
548  // This is only used by the unit tests as the c implementation is
549  // normally called by compute_fluxes in this module.
550
551
552  PyArrayObject *normal, *ql, *qr,  *edgeflux;
553  double g, local_max_speed;
554
555  if (!PyArg_ParseTuple(args, "OOOOd",
556            &normal, &ql, &qr, &edgeflux, &g)) {
557    PyErr_SetString(PyExc_RuntimeError, 
558                    "swb_domain_ext.c: flux_function could not parse input arguments");
559    return NULL;
560  }
561
562 
563  _flux_function((double*) ql -> data, 
564                 (double*) qr -> data, 
565                 ((double*) normal -> data)[0],
566                 ((double*) normal -> data)[1],         
567                 g,
568                 (double*) edgeflux -> data, 
569                 &local_max_speed);
570 
571  return Py_BuildValue("d", local_max_speed); 
572}
573
574
575//========================================================================
576// Gravity
577//========================================================================
578
579PyObject *gravity(PyObject *self, PyObject *args) {
580  //
581  //  gravity(g, h, v, x, xmom, ymom)
582  //
583 
584 
585  PyArrayObject *h, *v, *x, *xmom, *ymom;
586  int k, N, k3, k6;
587  double g, avg_h, zx, zy;
588  double x0, y0, x1, y1, x2, y2, z0, z1, z2;
589  //double epsilon;
590 
591  if (!PyArg_ParseTuple(args, "dOOOOO",
592                        &g, &h, &v, &x,
593                        &xmom, &ymom)) {
594    //&epsilon)) {
595    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: gravity could not parse input arguments");
596    return NULL;
597  }
598 
599  // check that numpy array objects arrays are C contiguous memory
600  CHECK_C_CONTIG(h);
601  CHECK_C_CONTIG(v);
602  CHECK_C_CONTIG(x);
603  CHECK_C_CONTIG(xmom);
604  CHECK_C_CONTIG(ymom);
605 
606  N = h -> dimensions[0];
607  for (k=0; k<N; k++) {
608    k3 = 3*k;  // base index
609   
610    // Get bathymetry
611    z0 = ((double*) v -> data)[k3 + 0];
612    z1 = ((double*) v -> data)[k3 + 1];
613    z2 = ((double*) v -> data)[k3 + 2];
614   
615    // Optimise for flat bed
616    // Note (Ole): This didn't produce measurable speed up.
617    // Revisit later
618    //if (fabs(z0-z1)<epsilon && fabs(z1-z2)<epsilon) {
619    //  continue;
620    //}
621   
622    // Get average depth from centroid values
623    avg_h = ((double *) h -> data)[k];
624   
625    // Compute bed slope
626    k6 = 6*k;  // base index
627   
628    x0 = ((double*) x -> data)[k6 + 0];
629    y0 = ((double*) x -> data)[k6 + 1];
630    x1 = ((double*) x -> data)[k6 + 2];
631    y1 = ((double*) x -> data)[k6 + 3];
632    x2 = ((double*) x -> data)[k6 + 4];
633    y2 = ((double*) x -> data)[k6 + 5];
634   
635   
636    _gradient(x0, y0, x1, y1, x2, y2, z0, z1, z2, &zx, &zy);
637   
638    // Update momentum
639    ((double*) xmom -> data)[k] += -g*zx*avg_h;
640    ((double*) ymom -> data)[k] += -g*zy*avg_h;
641  }
642 
643  return Py_BuildValue("");
644}
645
646
647
648//========================================================================
649// Method table for python module
650//========================================================================
651
652static struct PyMethodDef MethodTable[] = {
653  /* The cast of the function is necessary since PyCFunction values
654   * only take two PyObject* parameters, and rotate() takes
655   * three.
656   */
657  {"compute_fluxes_c", compute_fluxes,     METH_VARARGS, "Print out"},
658  {"gravity_c",        gravity,            METH_VARARGS, "Print out"},
659  {"flux_function_c",  flux_function,      METH_VARARGS, "Print out"},
660  {NULL, NULL, 0, NULL}
661};
662
663// Module initialisation
664void initswb_domain_ext(void){
665  Py_InitModule("swb_domain_ext", MethodTable);
666
667  import_array(); // Necessary for handling of NumPY structures
668}
Note: See TracBrowser for help on using the repository browser.