source: anuga_core/source/anuga/shallow_water/shallow_water_ext.c @ 3719

Last change on this file since 3719 was 3719, checked in by steve, 17 years ago

Adding holes to create_mesh_from_regions

File size: 57.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#include <stdio.h>
19
20//Shared code snippets
21#include "util_ext.h"
22
23const double pi = 3.14159265358979;
24
25// Computational function for rotation
26int _rotate(double *q, double n1, double n2) {
27  /*Rotate the momentum component q (q[1], q[2])
28    from x,y coordinates to coordinates based on normal vector (n1, n2).
29
30    Result is returned in array 3x1 r
31    To rotate in opposite direction, call rotate with (q, n1, -n2)
32
33    Contents of q are changed by this function */
34
35
36  double q1, q2;
37
38  //Shorthands
39  q1 = q[1];  //uh momentum
40  q2 = q[2];  //vh momentum
41
42  //Rotate
43  q[1] =  n1*q1 + n2*q2;
44  q[2] = -n2*q1 + n1*q2;
45
46  return 0;
47}
48
49int find_qmin_and_qmax(double dq0, double dq1, double dq2, double *qmin, double *qmax){
50  //Considering the centroid of an FV triangle and the vertices of its auxiliary triangle, find
51  //qmin=min(q)-qc and qmax=max(q)-qc, where min(q) and max(q) are respectively min and max over the
52  //four values (at the centroid of the FV triangle and the auxiliary triangle vertices),
53  //and qc is the centroid
54  //dq0=q(vertex0)-q(centroid of FV triangle)
55  //dq1=q(vertex1)-q(vertex0)
56  //dq2=q(vertex2)-q(vertex0)
57  if (dq0>=0.0){
58    if (dq1>=dq2){
59      if (dq1>=0.0)
60        *qmax=dq0+dq1;
61      else
62        *qmax=dq0;
63      if ((*qmin=dq0+dq2)<0)
64        ;//qmin is already set to correct value
65      else
66        *qmin=0.0;
67    }
68    else{//dq1<dq2
69      if (dq2>0)
70        *qmax=dq0+dq2;
71      else
72        *qmax=dq0;
73      if ((*qmin=dq0+dq1)<0)
74        ;//qmin is the correct value
75      else
76        *qmin=0.0;
77    }
78  }
79  else{//dq0<0
80    if (dq1<=dq2){
81      if (dq1<0.0)
82        *qmin=dq0+dq1;
83      else
84        *qmin=dq0;
85      if ((*qmax=dq0+dq2)>0.0)
86        ;//qmax is already set to the correct value
87      else
88        *qmax=0.0;
89    }
90    else{//dq1>dq2
91      if (dq2<0.0)
92        *qmin=dq0+dq2;
93      else
94        *qmin=dq0;
95      if ((*qmax=dq0+dq1)>0.0)
96        ;//qmax is already set to the correct value
97      else
98        *qmax=0.0;
99    }
100  }
101  return 0;
102}
103
104int limit_gradient(double *dqv, double qmin, double qmax, double beta_w){
105  //given provisional jumps dqv from the FV triangle centroid to its vertices and
106  //jumps qmin (qmax) between the centroid of the FV triangle and the
107  //minimum (maximum) of the values at the centroid of the FV triangle and the auxiliary triangle vertices,
108  //calculate a multiplicative factor phi by which the provisional vertex jumps are to be limited
109  int i;
110  double r=1000.0, r0=1.0, phi=1.0;
111  static double TINY = 1.0e-100;//to avoid machine accuracy problems.
112  //Any provisional jump with magnitude < TINY does not contribute to the limiting process.
113  for (i=0;i<3;i++){
114    if (dqv[i]<-TINY)
115      r0=qmin/dqv[i];
116    if (dqv[i]>TINY)
117      r0=qmax/dqv[i];
118    r=min(r0,r);
119    //
120  }
121  phi=min(r*beta_w,1.0);
122  for (i=0;i<3;i++)
123    dqv[i]=dqv[i]*phi;
124  return 0;
125}
126
127// Computational function for flux computation (using stage w=z+h)
128int flux_function_central(double *q_left, double *q_right,
129                  double z_left, double z_right,
130                  double n1, double n2,
131                  double epsilon, double g,
132                  double *edgeflux, double *max_speed) {
133
134  /*Compute fluxes between volumes for the shallow water wave equation
135    cast in terms of the 'stage', w = h+z using
136    the 'central scheme' as described in
137
138    Kurganov, Noelle, Petrova. 'Semidiscrete Central-Upwind Schemes For
139    Hyperbolic Conservation Laws and Hamilton-Jacobi Equations'.
140    Siam J. Sci. Comput. Vol. 23, No. 3, pp. 707-740.
141
142    The implemented formula is given in equation (3.15) on page 714
143  */
144
145  int i;
146
147  double w_left, h_left, uh_left, vh_left, u_left;
148  double w_right, h_right, uh_right, vh_right, u_right;
149  double s_min, s_max, soundspeed_left, soundspeed_right;
150  double denom, z;
151  double q_left_copy[3], q_right_copy[3];
152  double flux_right[3], flux_left[3];
153
154  //Copy conserved quantities to protect from modification
155  for (i=0; i<3; i++) {
156    q_left_copy[i] = q_left[i];
157    q_right_copy[i] = q_right[i];
158  }
159
160  //Align x- and y-momentum with x-axis
161  _rotate(q_left_copy, n1, n2);
162  _rotate(q_right_copy, n1, n2);
163
164  z = (z_left+z_right)/2; //Take average of field values
165
166  //Compute speeds in x-direction
167  w_left = q_left_copy[0];              // h+z
168  h_left = w_left-z;
169  uh_left = q_left_copy[1];
170
171  if (h_left < epsilon) {
172    h_left = 0.0;  //Could have been negative
173    u_left = 0.0;
174  } else {
175    u_left = uh_left/h_left;
176  }
177
178  w_right = q_right_copy[0];
179  h_right = w_right-z;
180  uh_right = q_right_copy[1];
181
182  if (h_right < epsilon) {
183    h_right = 0.0; //Could have been negative
184    u_right = 0.0;
185  } else {
186    u_right = uh_right/h_right;
187  }
188
189  //Momentum in y-direction
190  vh_left  = q_left_copy[2];
191  vh_right = q_right_copy[2];
192
193
194  //Maximal and minimal wave speeds
195  soundspeed_left  = sqrt(g*h_left);
196  soundspeed_right = sqrt(g*h_right);
197
198  s_max = max(u_left+soundspeed_left, u_right+soundspeed_right);
199  if (s_max < 0.0) s_max = 0.0;
200
201  s_min = min(u_left-soundspeed_left, u_right-soundspeed_right);
202  if (s_min > 0.0) s_min = 0.0;
203
204  //Flux formulas
205  flux_left[0] = u_left*h_left;
206  flux_left[1] = u_left*uh_left + 0.5*g*h_left*h_left;
207  flux_left[2] = u_left*vh_left;
208
209  flux_right[0] = u_right*h_right;
210  flux_right[1] = u_right*uh_right + 0.5*g*h_right*h_right;
211  flux_right[2] = u_right*vh_right;
212
213
214  //Flux computation
215  denom = s_max-s_min;
216  if (denom == 0.0) {
217    for (i=0; i<3; i++) edgeflux[i] = 0.0;
218    *max_speed = 0.0;
219  } else {
220    for (i=0; i<3; i++) {
221      edgeflux[i] = s_max*flux_left[i] - s_min*flux_right[i];
222      edgeflux[i] += s_max*s_min*(q_right_copy[i]-q_left_copy[i]);
223      edgeflux[i] /= denom;
224    }
225
226    //Maximal wavespeed
227    *max_speed = max(fabs(s_max), fabs(s_min));
228
229    //Rotate back
230    _rotate(edgeflux, n1, -n2);
231  }
232  return 0;
233}
234
235double erfcc(double x){
236    double z,t,result;
237
238    z=fabs(x);
239    t=1.0/(1.0+0.5*z);
240    result=t*exp(-z*z-1.26551223+t*(1.00002368+t*(.37409196+
241         t*(.09678418+t*(-.18628806+t*(.27886807+t*(-1.13520398+
242         t*(1.48851587+t*(-.82215223+t*.17087277)))))))));
243    if (x < 0.0) result = 2.0-result;
244
245    return result;
246    }
247
248
249
250// Computational function for flux computation (using stage w=z+h)
251int flux_function_kinetic(double *q_left, double *q_right,
252                  double z_left, double z_right,
253                  double n1, double n2,
254                  double epsilon, double g,
255                  double *edgeflux, double *max_speed) {
256
257  /*Compute fluxes between volumes for the shallow water wave equation
258    cast in terms of the 'stage', w = h+z using
259    the 'central scheme' as described in
260
261    Zhang et. al., Advances in Water Resources, 26(6), 2003, 635-647.
262  */
263
264  int i;
265
266  double w_left, h_left, uh_left, vh_left, u_left, F_left;
267  double w_right, h_right, uh_right, vh_right, u_right, F_right;
268  double s_min, s_max, soundspeed_left, soundspeed_right;
269  double z;
270  double q_left_copy[3], q_right_copy[3];
271
272
273  //Copy conserved quantities to protect from modification
274  for (i=0; i<3; i++) {
275    q_left_copy[i] = q_left[i];
276    q_right_copy[i] = q_right[i];
277  }
278
279  //Align x- and y-momentum with x-axis
280  _rotate(q_left_copy, n1, n2);
281  _rotate(q_right_copy, n1, n2);
282
283  z = (z_left+z_right)/2; //Take average of field values
284
285  //Compute speeds in x-direction
286  w_left = q_left_copy[0];              // h+z
287  h_left = w_left-z;
288  uh_left = q_left_copy[1];
289
290  if (h_left < epsilon) {
291    h_left = 0.0;  //Could have been negative
292    u_left = 0.0;
293  } else {
294    u_left = uh_left/h_left;
295  }
296
297  w_right = q_right_copy[0];
298  h_right = w_right-z;
299  uh_right = q_right_copy[1];
300
301  if (h_right < epsilon) {
302    h_right = 0.0; //Could have been negative
303    u_right = 0.0;
304  } else {
305    u_right = uh_right/h_right;
306  }
307
308  //Momentum in y-direction
309  vh_left  = q_left_copy[2];
310  vh_right = q_right_copy[2];
311
312
313  //Maximal and minimal wave speeds
314  soundspeed_left  = sqrt(g*h_left);
315  soundspeed_right = sqrt(g*h_right);
316
317  s_max = max(u_left+soundspeed_left, u_right+soundspeed_right);
318  if (s_max < 0.0) s_max = 0.0;
319
320  s_min = min(u_left-soundspeed_left, u_right-soundspeed_right);
321  if (s_min > 0.0) s_min = 0.0;
322
323
324  F_left  = 0.0;
325  F_right = 0.0;
326  if (h_left > 0.0) F_left = u_left/sqrt(g*h_left);
327  if (h_right > 0.0) F_right = u_right/sqrt(g*h_right);
328
329  for (i=0; i<3; i++) edgeflux[i] = 0.0;
330  *max_speed = 0.0;
331
332  edgeflux[0] = h_left*u_left/2.0*erfcc(-F_left) +  \
333          h_left*sqrt(g*h_left)/2.0/sqrt(pi)*exp(-(F_left*F_left)) + \
334          h_right*u_right/2.0*erfcc(F_right) -  \
335          h_right*sqrt(g*h_right)/2.0/sqrt(pi)*exp(-(F_right*F_right));
336
337  edgeflux[1] = (h_left*u_left*u_left + g/2.0*h_left*h_left)/2.0*erfcc(-F_left) + \
338          u_left*h_left*sqrt(g*h_left)/2.0/sqrt(pi)*exp(-(F_left*F_left)) + \
339          (h_right*u_right*u_right + g/2.0*h_right*h_right)/2.0*erfcc(F_right) -  \
340          u_right*h_right*sqrt(g*h_right)/2.0/sqrt(pi)*exp(-(F_right*F_right));
341
342  edgeflux[2] = vh_left*u_left/2.0*erfcc(-F_left) + \
343          vh_left*sqrt(g*h_left)/2.0/sqrt(pi)*exp(-(F_left*F_left)) + \
344          vh_right*u_right/2.0*erfcc(F_right) - \
345          vh_right*sqrt(g*h_right)/2.0/sqrt(pi)*exp(-(F_right*F_right));
346
347  //Maximal wavespeed
348  *max_speed = max(fabs(s_max), fabs(s_min));
349
350  //Rotate back
351  _rotate(edgeflux, n1, -n2);
352
353  return 0;
354}
355
356
357
358
359void _manning_friction(double g, double eps, int N,
360                       double* w, double* z,
361                       double* uh, double* vh,
362                       double* eta, double* xmom, double* ymom) {
363
364  int k;
365  double S, h;
366
367  for (k=0; k<N; k++) {
368    if (eta[k] > eps) {
369      h = w[k]-z[k];
370      if (h >= eps) {
371        S = -g * eta[k]*eta[k] * sqrt((uh[k]*uh[k] + vh[k]*vh[k]));
372        S /= pow(h, 7.0/3);      //Expensive (on Ole's home computer)
373        //S /= exp(7.0/3.0*log(h));      //seems to save about 15% over manning_friction
374        //S /= h*h*(1 + h/3.0 - h*h/9.0); //FIXME: Could use a Taylor expansion
375
376
377        //Update momentum
378        xmom[k] += S*uh[k];
379        ymom[k] += S*vh[k];
380      }
381    }
382  }
383}
384
385
386/*
387void _manning_friction_explicit(double g, double eps, int N,
388                       double* w, double* z,
389                       double* uh, double* vh,
390                       double* eta, double* xmom, double* ymom) {
391
392  int k;
393  double S, h;
394
395  for (k=0; k<N; k++) {
396    if (eta[k] > eps) {
397      h = w[k]-z[k];
398      if (h >= eps) {
399        S = -g * eta[k]*eta[k] * sqrt((uh[k]*uh[k] + vh[k]*vh[k]));
400        S /= pow(h, 7.0/3);      //Expensive (on Ole's home computer)
401        //S /= exp(7.0/3.0*log(h));      //seems to save about 15% over manning_friction
402        //S /= h*h*(1 + h/3.0 - h*h/9.0); //FIXME: Could use a Taylor expansion
403
404
405        //Update momentum
406        xmom[k] += S*uh[k];
407        ymom[k] += S*vh[k];
408      }
409    }
410  }
411}
412*/
413
414int _balance_deep_and_shallow(int N,
415                              double* wc,
416                              double* zc,
417                              double* hc,
418                              double* wv,
419                              double* zv,
420                              double* hv,
421                              double* hvbar,
422                              double* xmomc,
423                              double* ymomc,
424                              double* xmomv,
425                              double* ymomv) {
426
427  int k, k3, i;
428  double dz, hmin, alpha;
429
430  //Compute linear combination between w-limited stages and
431  //h-limited stages close to the bed elevation.
432
433  for (k=0; k<N; k++) {
434    // Compute maximal variation in bed elevation
435    // This quantitiy is
436    //     dz = max_i abs(z_i - z_c)
437    // and it is independent of dimension
438    // In the 1d case zc = (z0+z1)/2
439    // In the 2d case zc = (z0+z1+z2)/3
440
441    k3 = 3*k;
442
443    //FIXME: Try with this one precomputed
444    dz = 0.0;
445    hmin = hv[k3];
446    for (i=0; i<3; i++) {
447      dz = max(dz, fabs(zv[k3+i]-zc[k]));
448      hmin = min(hmin, hv[k3+i]);
449    }
450
451
452    //Create alpha in [0,1], where alpha==0 means using the h-limited
453    //stage and alpha==1 means using the w-limited stage as
454    //computed by the gradient limiter (both 1st or 2nd order)
455    //
456    //If hmin > dz/2 then alpha = 1 and the bed will have no effect
457    //If hmin < 0 then alpha = 0 reverting to constant height above bed.
458
459
460    if (dz > 0.0)
461      //if (hmin<0.0)
462      //        alpha = 0.0;
463      //else
464      //  alpha = max( min( hc[k]/dz, 1.0), 0.0 );
465      alpha = max( min( 2.0*hmin/dz, 1.0), 0.0 );
466    else
467      alpha = 1.0;  //Flat bed
468     
469     
470    //alpha = 1.0;  //Always deep FIXME: This actually looks good now
471
472    //printf("dz = %.3f, alpha = %.8f\n", dz, alpha);
473
474    //  Let
475    //
476    //    wvi be the w-limited stage (wvi = zvi + hvi)
477    //    wvi- be the h-limited state (wvi- = zvi + hvi-)
478    //
479    //
480    //  where i=0,1,2 denotes the vertex ids
481    //
482    //  Weighted balance between w-limited and h-limited stage is
483    //
484    //    wvi := (1-alpha)*(zvi+hvi-) + alpha*(zvi+hvi)
485    //
486    //  It follows that the updated wvi is
487    //    wvi := zvi + (1-alpha)*hvi- + alpha*hvi
488    //
489    //   Momentum is balanced between constant and limited
490
491    if (alpha < 1) {
492      for (i=0; i<3; i++) {
493         wv[k3+i] = zv[k3+i] + (1-alpha)*hvbar[k3+i] + alpha*hv[k3+i];
494
495        //Update momentum as a linear combination of
496        //xmomc and ymomc (shallow) and momentum
497        //from extrapolator xmomv and ymomv (deep).
498        xmomv[k3+i] = (1-alpha)*xmomc[k] + alpha*xmomv[k3+i];
499        ymomv[k3+i] = (1-alpha)*ymomc[k] + alpha*ymomv[k3+i];
500      }
501    }
502  }
503  return 0;
504}
505
506
507
508int _protect(int N,
509             double minimum_allowed_height,
510             double maximum_allowed_speed,
511             double epsilon,
512             double* wc,
513             double* zc,
514             double* xmomc,
515             double* ymomc) {
516
517  int k;
518  double hc;
519  double u, v, reduced_speed;
520
521  //Protect against initesimal and negative heights
522  for (k=0; k<N; k++) {
523    hc = wc[k] - zc[k];
524
525    if (hc < minimum_allowed_height) {
526       
527      //Old code: Set momentum to zero and ensure h is non negative
528      //xmomc[k] = 0.0;
529      //ymomc[k] = 0.0;
530      //if (hc <= 0.0) wc[k] = zc[k];
531
532
533      //New code: Adjust momentum to guarantee speeds are physical
534      //          ensure h is non negative
535      //FIXME (Ole): This is only implemented in this C extension and
536      //             has no Python equivalent
537      if (hc <= 0.0) {
538        wc[k] = zc[k];
539                xmomc[k] = 0.0;
540                ymomc[k] = 0.0;
541      } else {
542        //Reduce excessive speeds derived from division by small hc
543       
544        u = xmomc[k]/hc;
545                if (fabs(u) > maximum_allowed_speed) {
546                        reduced_speed = maximum_allowed_speed * u/fabs(u);
547                        //printf("Speed (u) has been reduced from %.3f to %.3f\n",
548                        //       u, reduced_speed);
549                        xmomc[k] = reduced_speed * hc;
550                }
551
552        v = ymomc[k]/hc;
553                if (fabs(v) > maximum_allowed_speed) {
554                        reduced_speed = maximum_allowed_speed * v/fabs(v);
555                        //printf("Speed (v) has been reduced from %.3f to %.3f\n",
556                        //       v, reduced_speed);
557                        ymomc[k] = reduced_speed * hc;
558                }
559      }
560    }
561  }
562  return 0;
563}
564
565
566
567
568int _assign_wind_field_values(int N,
569                              double* xmom_update,
570                              double* ymom_update,
571                              double* s_vec,
572                              double* phi_vec,
573                              double cw) {
574
575  //Assign windfield values to momentum updates
576
577  int k;
578  double S, s, phi, u, v;
579
580  for (k=0; k<N; k++) {
581
582    s = s_vec[k];
583    phi = phi_vec[k];
584
585    //Convert to radians
586    phi = phi*pi/180;
587
588    //Compute velocity vector (u, v)
589    u = s*cos(phi);
590    v = s*sin(phi);
591
592    //Compute wind stress
593    S = cw * sqrt(u*u + v*v);
594    xmom_update[k] += S*u;
595    ymom_update[k] += S*v;
596  }
597  return 0;
598}
599
600
601
602///////////////////////////////////////////////////////////////////
603// Gateways to Python
604
605PyObject *gravity(PyObject *self, PyObject *args) {
606  //
607  //  gravity(g, h, v, x, xmom, ymom)
608  //
609
610
611  PyArrayObject *h, *v, *x, *xmom, *ymom;
612  int k, i, N, k3, k6;
613  double g, avg_h, zx, zy;
614  double x0, y0, x1, y1, x2, y2, z0, z1, z2;
615
616  if (!PyArg_ParseTuple(args, "dOOOOO",
617                        &g, &h, &v, &x,
618                        &xmom, &ymom))
619    return NULL;
620
621  N = h -> dimensions[0];
622  for (k=0; k<N; k++) {
623    k3 = 3*k;  // base index
624    k6 = 6*k;  // base index
625
626    avg_h = 0.0;
627    for (i=0; i<3; i++) {
628      avg_h += ((double *) h -> data)[k3+i];
629    }
630    avg_h /= 3;
631
632
633    //Compute bed slope
634    x0 = ((double*) x -> data)[k6 + 0];
635    y0 = ((double*) x -> data)[k6 + 1];
636    x1 = ((double*) x -> data)[k6 + 2];
637    y1 = ((double*) x -> data)[k6 + 3];
638    x2 = ((double*) x -> data)[k6 + 4];
639    y2 = ((double*) x -> data)[k6 + 5];
640
641
642    z0 = ((double*) v -> data)[k3 + 0];
643    z1 = ((double*) v -> data)[k3 + 1];
644    z2 = ((double*) v -> data)[k3 + 2];
645
646    _gradient(x0, y0, x1, y1, x2, y2, z0, z1, z2, &zx, &zy);
647
648    //Update momentum
649    ((double*) xmom -> data)[k] += -g*zx*avg_h;
650    ((double*) ymom -> data)[k] += -g*zy*avg_h;
651  }
652
653  return Py_BuildValue("");
654}
655
656
657PyObject *manning_friction(PyObject *self, PyObject *args) {
658  //
659  // manning_friction(g, eps, h, uh, vh, eta, xmom_update, ymom_update)
660  //
661
662
663  PyArrayObject *w, *z, *uh, *vh, *eta, *xmom, *ymom;
664  int N;
665  double g, eps;
666
667  if (!PyArg_ParseTuple(args, "ddOOOOOOO",
668                        &g, &eps, &w, &z, &uh, &vh, &eta,
669                        &xmom, &ymom))
670    return NULL;
671
672  N = w -> dimensions[0];
673  _manning_friction(g, eps, N,
674                    (double*) w -> data,
675                    (double*) z -> data,
676                    (double*) uh -> data,
677                    (double*) vh -> data,
678                    (double*) eta -> data,
679                    (double*) xmom -> data,
680                    (double*) ymom -> data);
681
682  return Py_BuildValue("");
683}
684
685
686/*
687PyObject *manning_friction_explicit(PyObject *self, PyObject *args) {
688  //
689  // manning_friction_explicit(g, eps, h, uh, vh, eta, xmom_update, ymom_update)
690  //
691
692
693  PyArrayObject *w, *z, *uh, *vh, *eta, *xmom, *ymom;
694  int N;
695  double g, eps;
696
697  if (!PyArg_ParseTuple(args, "ddOOOOOOO",
698                        &g, &eps, &w, &z, &uh, &vh, &eta,
699                        &xmom, &ymom))
700    return NULL;
701
702  N = w -> dimensions[0];
703  _manning_friction_explicit(g, eps, N,
704                    (double*) w -> data,
705                    (double*) z -> data,
706                    (double*) uh -> data,
707                    (double*) vh -> data,
708                    (double*) eta -> data,
709                    (double*) xmom -> data,
710                    (double*) ymom -> data);
711
712  return Py_BuildValue("");
713}
714*/
715
716PyObject *extrapolate_second_order_sw(PyObject *self, PyObject *args) {
717  /*Compute the vertex values based on a linear reconstruction on each triangle
718    These values are calculated as follows:
719    1) For each triangle not adjacent to a boundary, we consider the auxiliary triangle
720    formed by the centroids of its three neighbours.
721    2) For each conserved quantity, we integrate around the auxiliary triangle's boundary the product
722    of the quantity and the outward normal vector. Dividing by the triangle area gives (a,b), the average
723    of the vector (q_x,q_y) on the auxiliary triangle. We suppose that the linear reconstruction on the
724    original triangle has gradient (a,b).
725    3) Provisional vertex junmps dqv[0,1,2] are computed and these are then limited by calling the functions
726    find_qmin_and_qmax and limit_gradient
727
728    Python call:
729    extrapolate_second_order_sw(domain.surrogate_neighbours,
730                                domain.number_of_boundaries
731                                domain.centroid_coordinates,
732                                Stage.centroid_values
733                                Xmom.centroid_values
734                                Ymom.centroid_values
735                                domain.vertex_coordinates,
736                                Stage.vertex_values,
737                                Xmom.vertex_values,
738                                Ymom.vertex_values)
739
740    Post conditions:
741            The vertices of each triangle have values from a limited linear reconstruction
742            based on centroid values
743
744  */
745  PyArrayObject *surrogate_neighbours,
746    *number_of_boundaries,
747    *centroid_coordinates,
748    *stage_centroid_values,
749    *xmom_centroid_values,
750    *ymom_centroid_values,
751        *elevation_centroid_values,
752    *vertex_coordinates,
753    *stage_vertex_values,
754    *xmom_vertex_values,
755    *ymom_vertex_values,
756        *elevation_vertex_values;
757  PyObject *domain, *Tmp;
758  //Local variables
759  double a, b;//gradient vector, not stored but used to calculate vertex values from centroids
760  int number_of_elements,k,k0,k1,k2,k3,k6,coord_index,i;
761  double x,y,x0,y0,x1,y1,x2,y2,xv0,yv0,xv1,yv1,xv2,yv2;//vertices of the auxiliary triangle
762  double dx1,dx2,dy1,dy2,dxv0,dxv1,dxv2,dyv0,dyv1,dyv2,dq0,dq1,dq2,area2;
763  double dqv[3], qmin, qmax, hmin;
764  double hc, h0, h1, h2;
765  double beta_w, beta_w_dry, beta_uh, beta_uh_dry, beta_vh, beta_vh_dry, beta_tmp;
766  double minimum_allowed_height;
767  //provisional jumps from centroids to v'tices and safety factor re limiting
768  //by which these jumps are limited
769  // Convert Python arguments to C
770  if (!PyArg_ParseTuple(args, "OOOOOOOOOOOOO",
771                        &domain,
772                        &surrogate_neighbours,
773                        &number_of_boundaries,
774                        &centroid_coordinates,
775                        &stage_centroid_values,
776                        &xmom_centroid_values,
777                        &ymom_centroid_values,
778                        &elevation_centroid_values,
779                        &vertex_coordinates,
780                        &stage_vertex_values,
781                        &xmom_vertex_values,
782                        &ymom_vertex_values,
783                        &elevation_vertex_values)) {
784    PyErr_SetString(PyExc_RuntimeError, "Input arguments failed");
785    return NULL;
786  }
787
788  //get the safety factor beta_w, set in the config.py file. This is used in the limiting process
789  Tmp = PyObject_GetAttrString(domain, "beta_w");
790  if (!Tmp)
791    return NULL;
792  beta_w = PyFloat_AsDouble(Tmp);
793  Py_DECREF(Tmp);
794 
795  Tmp = PyObject_GetAttrString(domain, "beta_w_dry");
796  if (!Tmp)
797    return NULL;
798  beta_w_dry = PyFloat_AsDouble(Tmp);
799  Py_DECREF(Tmp);
800 
801  Tmp = PyObject_GetAttrString(domain, "beta_uh");
802  if (!Tmp)
803    return NULL;
804  beta_uh = PyFloat_AsDouble(Tmp);
805  Py_DECREF(Tmp);
806 
807  Tmp = PyObject_GetAttrString(domain, "beta_uh_dry");
808  if (!Tmp)
809    return NULL;
810  beta_uh_dry = PyFloat_AsDouble(Tmp);
811  Py_DECREF(Tmp); 
812
813  Tmp = PyObject_GetAttrString(domain, "beta_vh");
814  if (!Tmp)
815    return NULL;
816  beta_vh = PyFloat_AsDouble(Tmp);
817  Py_DECREF(Tmp);
818 
819  Tmp = PyObject_GetAttrString(domain, "beta_vh_dry");
820  if (!Tmp)
821    return NULL;
822  beta_vh_dry = PyFloat_AsDouble(Tmp);
823  Py_DECREF(Tmp);
824 
825  Tmp = PyObject_GetAttrString(domain, "minimum_allowed_height");
826  if (!Tmp)
827    return NULL;
828  minimum_allowed_height = PyFloat_AsDouble(Tmp);
829  Py_DECREF(Tmp); 
830 
831  number_of_elements = stage_centroid_values -> dimensions[0];
832  for (k=0; k<number_of_elements; k++) {
833    k3=k*3;
834    k6=k*6;
835
836    if (((long *) number_of_boundaries->data)[k]==3){/*no neighbours, set gradient on the triangle to zero*/
837      ((double *) stage_vertex_values->data)[k3]=((double *)stage_centroid_values->data)[k];
838      ((double *) stage_vertex_values->data)[k3+1]=((double *)stage_centroid_values->data)[k];
839      ((double *) stage_vertex_values->data)[k3+2]=((double *)stage_centroid_values->data)[k];
840      ((double *) xmom_vertex_values->data)[k3]=((double *)xmom_centroid_values->data)[k];
841      ((double *) xmom_vertex_values->data)[k3+1]=((double *)xmom_centroid_values->data)[k];
842      ((double *) xmom_vertex_values->data)[k3+2]=((double *)xmom_centroid_values->data)[k];
843      ((double *) ymom_vertex_values->data)[k3]=((double *)ymom_centroid_values->data)[k];
844      ((double *) ymom_vertex_values->data)[k3+1]=((double *)ymom_centroid_values->data)[k];
845      ((double *) ymom_vertex_values->data)[k3+2]=((double *)ymom_centroid_values->data)[k];
846      continue;
847    }
848    else {//we will need centroid coordinates and vertex coordinates of the triangle
849      //get the vertex coordinates of the FV triangle
850      xv0=((double *)vertex_coordinates->data)[k6]; yv0=((double *)vertex_coordinates->data)[k6+1];
851      xv1=((double *)vertex_coordinates->data)[k6+2]; yv1=((double *)vertex_coordinates->data)[k6+3];
852      xv2=((double *)vertex_coordinates->data)[k6+4]; yv2=((double *)vertex_coordinates->data)[k6+5];
853      //get the centroid coordinates of the FV triangle
854      coord_index=2*k;
855      x=((double *)centroid_coordinates->data)[coord_index];
856      y=((double *)centroid_coordinates->data)[coord_index+1];
857      //store x- and y- differentials for the vertices of the FV triangle relative to the centroid
858      dxv0=xv0-x; dxv1=xv1-x; dxv2=xv2-x;
859      dyv0=yv0-y; dyv1=yv1-y; dyv2=yv2-y;
860    }
861    if (((long *)number_of_boundaries->data)[k]<=1){
862      //if no boundaries, auxiliary triangle is formed from the centroids of the three neighbours
863      //if one boundary, auxiliary triangle is formed from this centroid and its two neighbours
864      k0=((long *)surrogate_neighbours->data)[k3];
865      k1=((long *)surrogate_neighbours->data)[k3+1];
866      k2=((long *)surrogate_neighbours->data)[k3+2];
867      //get the auxiliary triangle's vertex coordinates (really the centroids of neighbouring triangles)
868      coord_index=2*k0;
869      x0=((double *)centroid_coordinates->data)[coord_index];
870      y0=((double *)centroid_coordinates->data)[coord_index+1];
871      coord_index=2*k1;
872      x1=((double *)centroid_coordinates->data)[coord_index];
873      y1=((double *)centroid_coordinates->data)[coord_index+1];
874      coord_index=2*k2;
875      x2=((double *)centroid_coordinates->data)[coord_index];
876      y2=((double *)centroid_coordinates->data)[coord_index+1];
877      //store x- and y- differentials for the vertices of the auxiliary triangle
878      dx1=x1-x0; dx2=x2-x0;
879      dy1=y1-y0; dy2=y2-y0;
880      //calculate 2*area of the auxiliary triangle
881      area2 = dy2*dx1 - dy1*dx2;//the triangle is guaranteed to be counter-clockwise
882      //If the mesh is 'weird' near the boundary, the trianlge might be flat or clockwise:
883      if (area2<=0)
884                return NULL;
885
886          //### Calculate heights of neighbouring cells
887          hc = ((double *)stage_centroid_values->data)[k]  - ((double *)elevation_centroid_values->data)[k];
888          h0 = ((double *)stage_centroid_values->data)[k0] - ((double *)elevation_centroid_values->data)[k0];
889          h1 = ((double *)stage_centroid_values->data)[k1] - ((double *)elevation_centroid_values->data)[k1];
890          h2 = ((double *)stage_centroid_values->data)[k2] - ((double *)elevation_centroid_values->data)[k2];
891          hmin = min(hc,min(h0,min(h1,h2)));
892      //### stage ###
893      //calculate the difference between vertex 0 of the auxiliary triangle and the FV triangle centroid
894      dq0=((double *)stage_centroid_values->data)[k0]-((double *)stage_centroid_values->data)[k];
895      //calculate differentials between the vertices of the auxiliary triangle
896      dq1=((double *)stage_centroid_values->data)[k1]-((double *)stage_centroid_values->data)[k0];
897      dq2=((double *)stage_centroid_values->data)[k2]-((double *)stage_centroid_values->data)[k0];
898      //calculate the gradient of stage on the auxiliary triangle
899      a = dy2*dq1 - dy1*dq2;
900      a /= area2;
901      b = dx1*dq2 - dx2*dq1;
902      b /= area2;
903      //calculate provisional jumps in stage from the centroid of the FV tri to its vertices, to be limited
904      dqv[0]=a*dxv0+b*dyv0;
905      dqv[1]=a*dxv1+b*dyv1;
906      dqv[2]=a*dxv2+b*dyv2;
907      //now we want to find min and max of the centroid and the vertices of the auxiliary triangle
908      //and compute jumps from the centroid to the min and max
909      find_qmin_and_qmax(dq0,dq1,dq2,&qmin,&qmax);
910          // Playing with dry wet interface
911          hmin = qmin;
912          beta_tmp = beta_w;
913          if (hmin<minimum_allowed_height)
914                beta_tmp = beta_w_dry;
915      limit_gradient(dqv,qmin,qmax,beta_tmp);//the gradient will be limited
916      for (i=0;i<3;i++)
917        ((double *)stage_vertex_values->data)[k3+i]=((double *)stage_centroid_values->data)[k]+dqv[i];
918
919      //### xmom ###
920      //calculate the difference between vertex 0 of the auxiliary triangle and the FV triangle centroid
921      dq0=((double *)xmom_centroid_values->data)[k0]-((double *)xmom_centroid_values->data)[k];
922      //calculate differentials between the vertices of the auxiliary triangle
923      dq1=((double *)xmom_centroid_values->data)[k1]-((double *)xmom_centroid_values->data)[k0];
924      dq2=((double *)xmom_centroid_values->data)[k2]-((double *)xmom_centroid_values->data)[k0];
925      //calculate the gradient of xmom on the auxiliary triangle
926      a = dy2*dq1 - dy1*dq2;
927      a /= area2;
928      b = dx1*dq2 - dx2*dq1;
929      b /= area2;
930      //calculate provisional jumps in stage from the centroid of the FV tri to its vertices, to be limited
931      dqv[0]=a*dxv0+b*dyv0;
932      dqv[1]=a*dxv1+b*dyv1;
933      dqv[2]=a*dxv2+b*dyv2;
934      //now we want to find min and max of the centroid and the vertices of the auxiliary triangle
935      //and compute jumps from the centroid to the min and max
936      find_qmin_and_qmax(dq0,dq1,dq2,&qmin,&qmax);
937          beta_tmp = beta_uh;
938          if (hmin<minimum_allowed_height)
939                beta_tmp = beta_uh_dry;
940      limit_gradient(dqv,qmin,qmax,beta_tmp);//the gradient will be limited
941      for (i=0;i<3;i++)
942        ((double *)xmom_vertex_values->data)[k3+i]=((double *)xmom_centroid_values->data)[k]+dqv[i];
943
944      //### ymom ###
945      //calculate the difference between vertex 0 of the auxiliary triangle and the FV triangle centroid
946      dq0=((double *)ymom_centroid_values->data)[k0]-((double *)ymom_centroid_values->data)[k];
947      //calculate differentials between the vertices of the auxiliary triangle
948      dq1=((double *)ymom_centroid_values->data)[k1]-((double *)ymom_centroid_values->data)[k0];
949      dq2=((double *)ymom_centroid_values->data)[k2]-((double *)ymom_centroid_values->data)[k0];
950      //calculate the gradient of xmom on the auxiliary triangle
951      a = dy2*dq1 - dy1*dq2;
952      a /= area2;
953      b = dx1*dq2 - dx2*dq1;
954      b /= area2;
955      //calculate provisional jumps in stage from the centroid of the FV tri to its vertices, to be limited
956      dqv[0]=a*dxv0+b*dyv0;
957      dqv[1]=a*dxv1+b*dyv1;
958      dqv[2]=a*dxv2+b*dyv2;
959      //now we want to find min and max of the centroid and the vertices of the auxiliary triangle
960      //and compute jumps from the centroid to the min and max
961      find_qmin_and_qmax(dq0,dq1,dq2,&qmin,&qmax);
962          beta_tmp = beta_vh;
963          if (hmin<minimum_allowed_height)
964                beta_tmp = beta_vh_dry;
965      limit_gradient(dqv,qmin,qmax,beta_tmp);//the gradient will be limited
966      for (i=0;i<3;i++)
967        ((double *)ymom_vertex_values->data)[k3+i]=((double *)ymom_centroid_values->data)[k]+dqv[i];
968    }//if (number_of_boundaries[k]<=1)
969    else{//number_of_boundaries==2
970      //one internal neighbour and gradient is in direction of the neighbour's centroid
971      //find the only internal neighbour
972      for (k2=k3;k2<k3+3;k2++){//k2 just indexes the edges of triangle k
973        if (((long *)surrogate_neighbours->data)[k2]!=k)//find internal neighbour of triabngle k
974          break;
975      }
976      if ((k2==k3+3))//if we didn't find an internal neighbour
977        return NULL;//error
978      k1=((long *)surrogate_neighbours->data)[k2];
979      //the coordinates of the triangle are already (x,y). Get centroid of the neighbour (x1,y1)
980      coord_index=2*k1;
981      x1=((double *)centroid_coordinates->data)[coord_index];
982      y1=((double *)centroid_coordinates->data)[coord_index+1];
983      //compute x- and y- distances between the centroid of the FV triangle and that of its neighbour
984      dx1=x1-x; dy1=y1-y;
985      //set area2 as the square of the distance
986      area2=dx1*dx1+dy1*dy1;
987      //set dx2=(x1-x0)/((x1-x0)^2+(y1-y0)^2) and dy2=(y1-y0)/((x1-x0)^2+(y1-y0)^2) which
988      //respectively correspond to the x- and y- gradients of the conserved quantities
989      dx2=1.0/area2;
990      dy2=dx2*dy1;
991      dx2*=dx1;
992
993      //## stage ###
994      //compute differentials
995      dq1=((double *)stage_centroid_values->data)[k1]-((double *)stage_centroid_values->data)[k];
996      //calculate the gradient between the centroid of the FV triangle and that of its neighbour
997      a=dq1*dx2;
998      b=dq1*dy2;
999      //calculate provisional vertex jumps, to be limited
1000      dqv[0]=a*dxv0+b*dyv0;
1001      dqv[1]=a*dxv1+b*dyv1;
1002      dqv[2]=a*dxv2+b*dyv2;
1003      //now limit the jumps
1004      if (dq1>=0.0){
1005        qmin=0.0;
1006        qmax=dq1;
1007      }
1008      else{
1009        qmin=dq1;
1010        qmax=0.0;
1011      }
1012      limit_gradient(dqv,qmin,qmax,beta_w);//the gradient will be limited
1013      for (i=0;i<3;i++)
1014        ((double *)stage_vertex_values->data)[k3+i]=((double *)stage_centroid_values->data)[k]+dqv[i];
1015
1016      //## xmom ###
1017      //compute differentials
1018      dq1=((double *)xmom_centroid_values->data)[k1]-((double *)xmom_centroid_values->data)[k];
1019      //calculate the gradient between the centroid of the FV triangle and that of its neighbour
1020      a=dq1*dx2;
1021      b=dq1*dy2;
1022      //calculate provisional vertex jumps, to be limited
1023      dqv[0]=a*dxv0+b*dyv0;
1024      dqv[1]=a*dxv1+b*dyv1;
1025      dqv[2]=a*dxv2+b*dyv2;
1026      //now limit the jumps
1027      if (dq1>=0.0){
1028        qmin=0.0;
1029        qmax=dq1;
1030      }
1031      else{
1032        qmin=dq1;
1033        qmax=0.0;
1034      }
1035      limit_gradient(dqv,qmin,qmax,beta_w);//the gradient will be limited
1036      for (i=0;i<3;i++)
1037        ((double *)xmom_vertex_values->data)[k3+i]=((double *)xmom_centroid_values->data)[k]+dqv[i];
1038
1039      //## ymom ###
1040      //compute differentials
1041      dq1=((double *)ymom_centroid_values->data)[k1]-((double *)ymom_centroid_values->data)[k];
1042      //calculate the gradient between the centroid of the FV triangle and that of its neighbour
1043      a=dq1*dx2;
1044      b=dq1*dy2;
1045      //calculate provisional vertex jumps, to be limited
1046      dqv[0]=a*dxv0+b*dyv0;
1047      dqv[1]=a*dxv1+b*dyv1;
1048      dqv[2]=a*dxv2+b*dyv2;
1049      //now limit the jumps
1050      if (dq1>=0.0){
1051        qmin=0.0;
1052        qmax=dq1;
1053      }
1054      else{
1055        qmin=dq1;
1056        qmax=0.0;
1057      }
1058      limit_gradient(dqv,qmin,qmax,beta_w);//the gradient will be limited
1059      for (i=0;i<3;i++)
1060        ((double *)ymom_vertex_values->data)[k3+i]=((double *)ymom_centroid_values->data)[k]+dqv[i];
1061    }//else [number_of_boudaries==2]
1062  }//for k=0 to number_of_elements-1
1063  return Py_BuildValue("");
1064}//extrapolate_second-order_sw
1065
1066PyObject *rotate(PyObject *self, PyObject *args, PyObject *kwargs) {
1067  //
1068  // r = rotate(q, normal, direction=1)
1069  //
1070  // Where q is assumed to be a Float numeric array of length 3 and
1071  // normal a Float numeric array of length 2.
1072
1073
1074  PyObject *Q, *Normal;
1075  PyArrayObject *q, *r, *normal;
1076
1077  static char *argnames[] = {"q", "normal", "direction", NULL};
1078  int dimensions[1], i, direction=1;
1079  double n1, n2;
1080
1081  // Convert Python arguments to C
1082  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|i", argnames,
1083                                   &Q, &Normal, &direction))
1084    return NULL;
1085
1086  //Input checks (convert sequences into numeric arrays)
1087  q = (PyArrayObject *)
1088    PyArray_ContiguousFromObject(Q, PyArray_DOUBLE, 0, 0);
1089  normal = (PyArrayObject *)
1090    PyArray_ContiguousFromObject(Normal, PyArray_DOUBLE, 0, 0);
1091
1092
1093  if (normal -> dimensions[0] != 2) {
1094    PyErr_SetString(PyExc_RuntimeError, "Normal vector must have 2 components");
1095    return NULL;
1096  }
1097
1098  //Allocate space for return vector r (don't DECREF)
1099  dimensions[0] = 3;
1100  r = (PyArrayObject *) PyArray_FromDims(1, dimensions, PyArray_DOUBLE);
1101
1102  //Copy
1103  for (i=0; i<3; i++) {
1104    ((double *) (r -> data))[i] = ((double *) (q -> data))[i];
1105  }
1106
1107  //Get normal and direction
1108  n1 = ((double *) normal -> data)[0];
1109  n2 = ((double *) normal -> data)[1];
1110  if (direction == -1) n2 = -n2;
1111
1112  //Rotate
1113  _rotate((double *) r -> data, n1, n2);
1114
1115  //Release numeric arrays
1116  Py_DECREF(q);
1117  Py_DECREF(normal);
1118
1119  //return result using PyArray to avoid memory leak
1120  return PyArray_Return(r);
1121}
1122
1123PyObject *compute_fluxes_ext_central(PyObject *self, PyObject *args) {
1124  /*Compute all fluxes and the timestep suitable for all volumes
1125    in domain.
1126
1127    Compute total flux for each conserved quantity using "flux_function_central"
1128
1129    Fluxes across each edge are scaled by edgelengths and summed up
1130    Resulting flux is then scaled by area and stored in
1131    explicit_update for each of the three conserved quantities
1132    stage, xmomentum and ymomentum
1133
1134    The maximal allowable speed computed by the flux_function for each volume
1135    is converted to a timestep that must not be exceeded. The minimum of
1136    those is computed as the next overall timestep.
1137
1138    Python call:
1139    domain.timestep = compute_fluxes(timestep,
1140                                     domain.epsilon,
1141                                     domain.g,
1142                                     domain.neighbours,
1143                                     domain.neighbour_edges,
1144                                     domain.normals,
1145                                     domain.edgelengths,
1146                                     domain.radii,
1147                                     domain.areas,
1148                                     Stage.edge_values,
1149                                     Xmom.edge_values,
1150                                     Ymom.edge_values,
1151                                     Bed.edge_values,
1152                                     Stage.boundary_values,
1153                                     Xmom.boundary_values,
1154                                     Ymom.boundary_values,
1155                                     Stage.explicit_update,
1156                                     Xmom.explicit_update,
1157                                     Ymom.explicit_update,
1158                                     already_computed_flux)
1159
1160
1161    Post conditions:
1162      domain.explicit_update is reset to computed flux values
1163      domain.timestep is set to the largest step satisfying all volumes.
1164
1165
1166  */
1167
1168
1169  PyArrayObject *neighbours, *neighbour_edges,
1170    *normals, *edgelengths, *radii, *areas,
1171    *tri_full_flag,
1172    *stage_edge_values,
1173    *xmom_edge_values,
1174    *ymom_edge_values,
1175    *bed_edge_values,
1176    *stage_boundary_values,
1177    *xmom_boundary_values,
1178    *ymom_boundary_values,
1179    *stage_explicit_update,
1180    *xmom_explicit_update,
1181    *ymom_explicit_update,
1182    *already_computed_flux;//tracks whether the flux across an edge has already been computed
1183
1184
1185  //Local variables
1186  double timestep, max_speed, epsilon, g;
1187  double normal[2], ql[3], qr[3], zl, zr;
1188  double edgeflux[3]; //Work arrays for summing up fluxes
1189
1190  int number_of_elements, k, i, m, n;
1191  int ki, nm=0, ki2; //Index shorthands
1192  static long call=1;
1193
1194
1195  // Convert Python arguments to C
1196  if (!PyArg_ParseTuple(args, "dddOOOOOOOOOOOOOOOOOO",
1197                        &timestep,
1198                        &epsilon,
1199                        &g,
1200                        &neighbours,
1201                        &neighbour_edges,
1202                        &normals,
1203                        &edgelengths, &radii, &areas,
1204                        &tri_full_flag,
1205                        &stage_edge_values,
1206                        &xmom_edge_values,
1207                        &ymom_edge_values,
1208                        &bed_edge_values,
1209                        &stage_boundary_values,
1210                        &xmom_boundary_values,
1211                        &ymom_boundary_values,
1212                        &stage_explicit_update,
1213                        &xmom_explicit_update,
1214                        &ymom_explicit_update,
1215                        &already_computed_flux)) {
1216    PyErr_SetString(PyExc_RuntimeError, "Input arguments failed");
1217    return NULL;
1218  }
1219  number_of_elements = stage_edge_values -> dimensions[0];
1220  call++;//a static local variable to which already_computed_flux is compared
1221  //set explicit_update to zero for all conserved_quantities.
1222  //This assumes compute_fluxes called before forcing terms
1223  for (k=0; k<number_of_elements; k++) {
1224    ((double *) stage_explicit_update -> data)[k]=0.0;
1225    ((double *) xmom_explicit_update -> data)[k]=0.0;
1226    ((double *) ymom_explicit_update -> data)[k]=0.0;
1227  }
1228  //Loop through neighbours and compute edge flux for each
1229  for (k=0; k<number_of_elements; k++) {
1230    for (i=0; i<3; i++) {
1231      ki = k*3+i;
1232      if (((long *) already_computed_flux->data)[ki]==call)//we've already computed the flux across this edge
1233        continue;
1234      ql[0] = ((double *) stage_edge_values -> data)[ki];
1235      ql[1] = ((double *) xmom_edge_values -> data)[ki];
1236      ql[2] = ((double *) ymom_edge_values -> data)[ki];
1237      zl =    ((double *) bed_edge_values -> data)[ki];
1238
1239      //Quantities at neighbour on nearest face
1240      n = ((long *) neighbours -> data)[ki];
1241      if (n < 0) {
1242        m = -n-1; //Convert negative flag to index
1243        qr[0] = ((double *) stage_boundary_values -> data)[m];
1244        qr[1] = ((double *) xmom_boundary_values -> data)[m];
1245        qr[2] = ((double *) ymom_boundary_values -> data)[m];
1246        zr = zl; //Extend bed elevation to boundary
1247      } else {
1248        m = ((long *) neighbour_edges -> data)[ki];
1249        nm = n*3+m;
1250        qr[0] = ((double *) stage_edge_values -> data)[nm];
1251        qr[1] = ((double *) xmom_edge_values -> data)[nm];
1252        qr[2] = ((double *) ymom_edge_values -> data)[nm];
1253        zr =    ((double *) bed_edge_values -> data)[nm];
1254      }
1255      // Outward pointing normal vector
1256      // normal = domain.normals[k, 2*i:2*i+2]
1257      ki2 = 2*ki; //k*6 + i*2
1258      normal[0] = ((double *) normals -> data)[ki2];
1259      normal[1] = ((double *) normals -> data)[ki2+1];
1260      //Edge flux computation
1261      flux_function_central(ql, qr, zl, zr,
1262                    normal[0], normal[1],
1263                    epsilon, g,
1264                    edgeflux, &max_speed);
1265      //update triangle k
1266      ((long *) already_computed_flux->data)[ki]=call;
1267      ((double *) stage_explicit_update -> data)[k] -= edgeflux[0]*((double *) edgelengths -> data)[ki];
1268      ((double *) xmom_explicit_update -> data)[k] -= edgeflux[1]*((double *) edgelengths -> data)[ki];
1269      ((double *) ymom_explicit_update -> data)[k] -= edgeflux[2]*((double *) edgelengths -> data)[ki];
1270      //update the neighbour n
1271      if (n>=0){
1272        ((long *) already_computed_flux->data)[nm]=call;
1273        ((double *) stage_explicit_update -> data)[n] += edgeflux[0]*((double *) edgelengths -> data)[nm];
1274        ((double *) xmom_explicit_update -> data)[n] += edgeflux[1]*((double *) edgelengths -> data)[nm];
1275        ((double *) ymom_explicit_update -> data)[n] += edgeflux[2]*((double *) edgelengths -> data)[nm];
1276      }
1277      ///for (j=0; j<3; j++) {
1278        ///flux[j] -= edgeflux[j]*((double *) edgelengths -> data)[ki];
1279        ///}
1280        //Update timestep
1281        //timestep = min(timestep, domain.radii[k]/max_speed)
1282        //FIXME: SR Add parameter for CFL condition
1283    if ( ((long *) tri_full_flag -> data)[k] == 1) {
1284            if (max_speed > epsilon) {
1285                timestep = min(timestep, ((double *) radii -> data)[k]/max_speed);
1286                //maxspeed in flux_function is calculated as max(|u+a|,|u-a|)
1287                if (n>=0)
1288                    timestep = min(timestep, ((double *) radii -> data)[n]/max_speed);
1289            }
1290    }
1291    } // end for i
1292    //Normalise by area and store for when all conserved
1293    //quantities get updated
1294    ((double *) stage_explicit_update -> data)[k] /= ((double *) areas -> data)[k];
1295    ((double *) xmom_explicit_update -> data)[k] /= ((double *) areas -> data)[k];
1296    ((double *) ymom_explicit_update -> data)[k] /= ((double *) areas -> data)[k];
1297  } //end for k
1298  return Py_BuildValue("d", timestep);
1299}
1300
1301
1302PyObject *compute_fluxes_ext_kinetic(PyObject *self, PyObject *args) {
1303  /*Compute all fluxes and the timestep suitable for all volumes
1304    in domain.
1305
1306    Compute total flux for each conserved quantity using "flux_function_central"
1307
1308    Fluxes across each edge are scaled by edgelengths and summed up
1309    Resulting flux is then scaled by area and stored in
1310    explicit_update for each of the three conserved quantities
1311    stage, xmomentum and ymomentum
1312
1313    The maximal allowable speed computed by the flux_function for each volume
1314    is converted to a timestep that must not be exceeded. The minimum of
1315    those is computed as the next overall timestep.
1316
1317    Python call:
1318    domain.timestep = compute_fluxes(timestep,
1319                                     domain.epsilon,
1320                                     domain.g,
1321                                     domain.neighbours,
1322                                     domain.neighbour_edges,
1323                                     domain.normals,
1324                                     domain.edgelengths,
1325                                     domain.radii,
1326                                     domain.areas,
1327                                     Stage.edge_values,
1328                                     Xmom.edge_values,
1329                                     Ymom.edge_values,
1330                                     Bed.edge_values,
1331                                     Stage.boundary_values,
1332                                     Xmom.boundary_values,
1333                                     Ymom.boundary_values,
1334                                     Stage.explicit_update,
1335                                     Xmom.explicit_update,
1336                                     Ymom.explicit_update,
1337                                     already_computed_flux)
1338
1339
1340    Post conditions:
1341      domain.explicit_update is reset to computed flux values
1342      domain.timestep is set to the largest step satisfying all volumes.
1343
1344
1345  */
1346
1347
1348  PyArrayObject *neighbours, *neighbour_edges,
1349    *normals, *edgelengths, *radii, *areas,
1350    *tri_full_flag,
1351    *stage_edge_values,
1352    *xmom_edge_values,
1353    *ymom_edge_values,
1354    *bed_edge_values,
1355    *stage_boundary_values,
1356    *xmom_boundary_values,
1357    *ymom_boundary_values,
1358    *stage_explicit_update,
1359    *xmom_explicit_update,
1360    *ymom_explicit_update,
1361    *already_computed_flux;//tracks whether the flux across an edge has already been computed
1362
1363
1364  //Local variables
1365  double timestep, max_speed, epsilon, g;
1366  double normal[2], ql[3], qr[3], zl, zr;
1367  double edgeflux[3]; //Work arrays for summing up fluxes
1368
1369  int number_of_elements, k, i, m, n;
1370  int ki, nm=0, ki2; //Index shorthands
1371  static long call=1;
1372
1373
1374  // Convert Python arguments to C
1375  if (!PyArg_ParseTuple(args, "dddOOOOOOOOOOOOOOOOOO",
1376                        &timestep,
1377                        &epsilon,
1378                        &g,
1379                        &neighbours,
1380                        &neighbour_edges,
1381                        &normals,
1382                        &edgelengths, &radii, &areas,
1383                        &tri_full_flag,
1384                        &stage_edge_values,
1385                        &xmom_edge_values,
1386                        &ymom_edge_values,
1387                        &bed_edge_values,
1388                        &stage_boundary_values,
1389                        &xmom_boundary_values,
1390                        &ymom_boundary_values,
1391                        &stage_explicit_update,
1392                        &xmom_explicit_update,
1393                        &ymom_explicit_update,
1394                        &already_computed_flux)) {
1395    PyErr_SetString(PyExc_RuntimeError, "Input arguments failed");
1396    return NULL;
1397  }
1398  number_of_elements = stage_edge_values -> dimensions[0];
1399  call++;//a static local variable to which already_computed_flux is compared
1400  //set explicit_update to zero for all conserved_quantities.
1401  //This assumes compute_fluxes called before forcing terms
1402  for (k=0; k<number_of_elements; k++) {
1403    ((double *) stage_explicit_update -> data)[k]=0.0;
1404    ((double *) xmom_explicit_update -> data)[k]=0.0;
1405    ((double *) ymom_explicit_update -> data)[k]=0.0;
1406  }
1407  //Loop through neighbours and compute edge flux for each
1408  for (k=0; k<number_of_elements; k++) {
1409    for (i=0; i<3; i++) {
1410      ki = k*3+i;
1411      if (((long *) already_computed_flux->data)[ki]==call)//we've already computed the flux across this edge
1412        continue;
1413      ql[0] = ((double *) stage_edge_values -> data)[ki];
1414      ql[1] = ((double *) xmom_edge_values -> data)[ki];
1415      ql[2] = ((double *) ymom_edge_values -> data)[ki];
1416      zl =    ((double *) bed_edge_values -> data)[ki];
1417
1418      //Quantities at neighbour on nearest face
1419      n = ((long *) neighbours -> data)[ki];
1420      if (n < 0) {
1421        m = -n-1; //Convert negative flag to index
1422        qr[0] = ((double *) stage_boundary_values -> data)[m];
1423        qr[1] = ((double *) xmom_boundary_values -> data)[m];
1424        qr[2] = ((double *) ymom_boundary_values -> data)[m];
1425        zr = zl; //Extend bed elevation to boundary
1426      } else {
1427        m = ((long *) neighbour_edges -> data)[ki];
1428        nm = n*3+m;
1429        qr[0] = ((double *) stage_edge_values -> data)[nm];
1430        qr[1] = ((double *) xmom_edge_values -> data)[nm];
1431        qr[2] = ((double *) ymom_edge_values -> data)[nm];
1432        zr =    ((double *) bed_edge_values -> data)[nm];
1433      }
1434      // Outward pointing normal vector
1435      // normal = domain.normals[k, 2*i:2*i+2]
1436      ki2 = 2*ki; //k*6 + i*2
1437      normal[0] = ((double *) normals -> data)[ki2];
1438      normal[1] = ((double *) normals -> data)[ki2+1];
1439      //Edge flux computation
1440      flux_function_kinetic(ql, qr, zl, zr,
1441                    normal[0], normal[1],
1442                    epsilon, g,
1443                    edgeflux, &max_speed);
1444      //update triangle k
1445      ((long *) already_computed_flux->data)[ki]=call;
1446      ((double *) stage_explicit_update -> data)[k] -= edgeflux[0]*((double *) edgelengths -> data)[ki];
1447      ((double *) xmom_explicit_update -> data)[k] -= edgeflux[1]*((double *) edgelengths -> data)[ki];
1448      ((double *) ymom_explicit_update -> data)[k] -= edgeflux[2]*((double *) edgelengths -> data)[ki];
1449      //update the neighbour n
1450      if (n>=0){
1451        ((long *) already_computed_flux->data)[nm]=call;
1452        ((double *) stage_explicit_update -> data)[n] += edgeflux[0]*((double *) edgelengths -> data)[nm];
1453        ((double *) xmom_explicit_update -> data)[n] += edgeflux[1]*((double *) edgelengths -> data)[nm];
1454        ((double *) ymom_explicit_update -> data)[n] += edgeflux[2]*((double *) edgelengths -> data)[nm];
1455      }
1456      ///for (j=0; j<3; j++) {
1457        ///flux[j] -= edgeflux[j]*((double *) edgelengths -> data)[ki];
1458        ///}
1459        //Update timestep
1460        //timestep = min(timestep, domain.radii[k]/max_speed)
1461        //FIXME: SR Add parameter for CFL condition
1462    if ( ((long *) tri_full_flag -> data)[k] == 1) {
1463            if (max_speed > epsilon) {
1464                timestep = min(timestep, ((double *) radii -> data)[k]/max_speed);
1465                //maxspeed in flux_function is calculated as max(|u+a|,|u-a|)
1466                if (n>=0)
1467                    timestep = min(timestep, ((double *) radii -> data)[n]/max_speed);
1468            }
1469    }
1470    } // end for i
1471    //Normalise by area and store for when all conserved
1472    //quantities get updated
1473    ((double *) stage_explicit_update -> data)[k] /= ((double *) areas -> data)[k];
1474    ((double *) xmom_explicit_update -> data)[k] /= ((double *) areas -> data)[k];
1475    ((double *) ymom_explicit_update -> data)[k] /= ((double *) areas -> data)[k];
1476  } //end for k
1477  return Py_BuildValue("d", timestep);
1478}
1479
1480PyObject *protect(PyObject *self, PyObject *args) {
1481  //
1482  //    protect(minimum_allowed_height, maximum_allowed_speed, wc, zc, xmomc, ymomc)
1483
1484
1485  PyArrayObject
1486  *wc,            //Stage at centroids
1487  *zc,            //Elevation at centroids
1488  *xmomc,         //Momentums at centroids
1489  *ymomc;
1490
1491
1492  int N;
1493  double minimum_allowed_height, maximum_allowed_speed, epsilon;
1494
1495  // Convert Python arguments to C
1496  if (!PyArg_ParseTuple(args, "dddOOOO",
1497                        &minimum_allowed_height,
1498                        &maximum_allowed_speed,
1499                        &epsilon,
1500                        &wc, &zc, &xmomc, &ymomc))
1501    return NULL;
1502
1503  N = wc -> dimensions[0];
1504
1505  _protect(N,
1506           minimum_allowed_height,
1507           maximum_allowed_speed,
1508           epsilon,
1509           (double*) wc -> data,
1510           (double*) zc -> data,
1511           (double*) xmomc -> data,
1512           (double*) ymomc -> data);
1513
1514  return Py_BuildValue("");
1515}
1516
1517
1518
1519PyObject *balance_deep_and_shallow(PyObject *self, PyObject *args) {
1520  //
1521  //    balance_deep_and_shallow(wc, zc, hc, wv, zv, hv,
1522  //                             xmomc, ymomc, xmomv, ymomv)
1523
1524
1525  PyArrayObject
1526    *wc,            //Stage at centroids
1527    *zc,            //Elevation at centroids
1528    *hc,            //Height at centroids
1529    *wv,            //Stage at vertices
1530    *zv,            //Elevation at vertices
1531    *hv,            //Depths at vertices
1532    *hvbar,         //h-Limited depths at vertices
1533    *xmomc,         //Momentums at centroids and vertices
1534    *ymomc,
1535    *xmomv,
1536    *ymomv;
1537
1538  int N; //, err;
1539
1540  // Convert Python arguments to C
1541  if (!PyArg_ParseTuple(args, "OOOOOOOOOOO",
1542                        &wc, &zc, &hc,
1543                        &wv, &zv, &hv, &hvbar,
1544                        &xmomc, &ymomc, &xmomv, &ymomv))
1545    return NULL;
1546
1547  N = wc -> dimensions[0];
1548
1549  _balance_deep_and_shallow(N,
1550                            (double*) wc -> data,
1551                            (double*) zc -> data,
1552                            (double*) hc -> data,
1553                            (double*) wv -> data,
1554                            (double*) zv -> data,
1555                            (double*) hv -> data,
1556                            (double*) hvbar -> data,
1557                            (double*) xmomc -> data,
1558                            (double*) ymomc -> data,
1559                            (double*) xmomv -> data,
1560                            (double*) ymomv -> data);
1561
1562
1563  return Py_BuildValue("");
1564}
1565
1566
1567
1568PyObject *h_limiter(PyObject *self, PyObject *args) {
1569
1570  PyObject *domain, *Tmp;
1571  PyArrayObject
1572    *hv, *hc, //Depth at vertices and centroids
1573    *hvbar,   //Limited depth at vertices (return values)
1574    *neighbours;
1575
1576  int k, i, n, N, k3;
1577  int dimensions[2];
1578  double beta_h; //Safety factor (see config.py)
1579  double *hmin, *hmax, hn;
1580
1581  // Convert Python arguments to C
1582  if (!PyArg_ParseTuple(args, "OOO", &domain, &hc, &hv))
1583    return NULL;
1584
1585  neighbours = get_consecutive_array(domain, "neighbours");
1586
1587  //Get safety factor beta_h
1588  Tmp = PyObject_GetAttrString(domain, "beta_h");
1589  if (!Tmp)
1590    return NULL;
1591
1592  beta_h = PyFloat_AsDouble(Tmp);
1593
1594  Py_DECREF(Tmp);
1595
1596  N = hc -> dimensions[0];
1597
1598  //Create hvbar
1599  dimensions[0] = N;
1600  dimensions[1] = 3;
1601  hvbar = (PyArrayObject *) PyArray_FromDims(2, dimensions, PyArray_DOUBLE);
1602
1603
1604  //Find min and max of this and neighbour's centroid values
1605  hmin = malloc(N * sizeof(double));
1606  hmax = malloc(N * sizeof(double));
1607  for (k=0; k<N; k++) {
1608    k3=k*3;
1609
1610    hmin[k] = ((double*) hc -> data)[k];
1611    hmax[k] = hmin[k];
1612
1613    for (i=0; i<3; i++) {
1614      n = ((long*) neighbours -> data)[k3+i];
1615
1616      //Initialise hvbar with values from hv
1617      ((double*) hvbar -> data)[k3+i] = ((double*) hv -> data)[k3+i];
1618
1619      if (n >= 0) {
1620        hn = ((double*) hc -> data)[n]; //Neighbour's centroid value
1621
1622        hmin[k] = min(hmin[k], hn);
1623        hmax[k] = max(hmax[k], hn);
1624      }
1625    }
1626  }
1627
1628  // Call underlying standard routine
1629  _limit(N, beta_h, (double*) hc -> data, (double*) hvbar -> data, hmin, hmax);
1630
1631  // // //Py_DECREF(domain); //FIXME: NEcessary?
1632  free(hmin);
1633  free(hmax);
1634
1635  //return result using PyArray to avoid memory leak
1636  return PyArray_Return(hvbar);
1637  //return Py_BuildValue("");
1638}
1639
1640PyObject *h_limiter_sw(PyObject *self, PyObject *args) {
1641  //a faster version of h_limiter above
1642  PyObject *domain, *Tmp;
1643  PyArrayObject
1644    *hv, *hc, //Depth at vertices and centroids
1645    *hvbar,   //Limited depth at vertices (return values)
1646    *neighbours;
1647
1648  int k, i, N, k3,k0,k1,k2;
1649  int dimensions[2];
1650  double beta_h; //Safety factor (see config.py)
1651  double hmin, hmax, dh[3];
1652  // Convert Python arguments to C
1653  if (!PyArg_ParseTuple(args, "OOO", &domain, &hc, &hv))
1654    return NULL;
1655  neighbours = get_consecutive_array(domain, "neighbours");
1656
1657  //Get safety factor beta_h
1658  Tmp = PyObject_GetAttrString(domain, "beta_h");
1659  if (!Tmp)
1660    return NULL;
1661  beta_h = PyFloat_AsDouble(Tmp);
1662
1663  Py_DECREF(Tmp);
1664
1665  N = hc -> dimensions[0];
1666
1667  //Create hvbar
1668  dimensions[0] = N;
1669  dimensions[1] = 3;
1670  hvbar = (PyArrayObject *) PyArray_FromDims(2, dimensions, PyArray_DOUBLE);
1671  for (k=0;k<N;k++){
1672    k3=k*3;
1673    //get the ids of the neighbours
1674    k0 = ((long*) neighbours -> data)[k3];
1675    k1 = ((long*) neighbours -> data)[k3+1];
1676    k2 = ((long*) neighbours -> data)[k3+2];
1677    //set hvbar provisionally
1678    for (i=0;i<3;i++){
1679      ((double*) hvbar -> data)[k3+i] = ((double*) hv -> data)[k3+i];
1680      dh[i]=((double*) hvbar -> data)[k3+i]-((double*) hc -> data)[k];
1681    }
1682    hmin=((double*) hc -> data)[k];
1683    hmax=hmin;
1684    if (k0>=0){
1685      hmin=min(hmin,((double*) hc -> data)[k0]);
1686      hmax=max(hmax,((double*) hc -> data)[k0]);
1687    }
1688    if (k1>=0){
1689      hmin=min(hmin,((double*) hc -> data)[k1]);
1690      hmax=max(hmax,((double*) hc -> data)[k1]);
1691    }
1692    if (k2>=0){
1693      hmin=min(hmin,((double*) hc -> data)[k2]);
1694      hmax=max(hmax,((double*) hc -> data)[k2]);
1695    }
1696    hmin-=((double*) hc -> data)[k];
1697    hmax-=((double*) hc -> data)[k];
1698    limit_gradient(dh,hmin,hmax,beta_h);
1699    for (i=0;i<3;i++)
1700      ((double*) hvbar -> data)[k3+i] = ((double*) hc -> data)[k]+dh[i];
1701  }
1702  return PyArray_Return(hvbar);
1703}
1704
1705PyObject *assign_windfield_values(PyObject *self, PyObject *args) {
1706  //
1707  //      assign_windfield_values(xmom_update, ymom_update,
1708  //                              s_vec, phi_vec, self.const)
1709
1710
1711
1712  PyArrayObject   //(one element per triangle)
1713  *s_vec,         //Speeds
1714  *phi_vec,       //Bearings
1715  *xmom_update,   //Momentum updates
1716  *ymom_update;
1717
1718
1719  int N;
1720  double cw;
1721
1722  // Convert Python arguments to C
1723  if (!PyArg_ParseTuple(args, "OOOOd",
1724                        &xmom_update,
1725                        &ymom_update,
1726                        &s_vec, &phi_vec,
1727                        &cw))
1728    return NULL;
1729
1730  N = xmom_update -> dimensions[0];
1731
1732  _assign_wind_field_values(N,
1733           (double*) xmom_update -> data,
1734           (double*) ymom_update -> data,
1735           (double*) s_vec -> data,
1736           (double*) phi_vec -> data,
1737           cw);
1738
1739  return Py_BuildValue("");
1740}
1741
1742
1743
1744
1745//////////////////////////////////////////
1746// Method table for python module
1747static struct PyMethodDef MethodTable[] = {
1748  /* The cast of the function is necessary since PyCFunction values
1749   * only take two PyObject* parameters, and rotate() takes
1750   * three.
1751   */
1752
1753  {"rotate", (PyCFunction)rotate, METH_VARARGS | METH_KEYWORDS, "Print out"},
1754  {"extrapolate_second_order_sw", extrapolate_second_order_sw, METH_VARARGS, "Print out"},
1755  {"compute_fluxes_ext_central", compute_fluxes_ext_central, METH_VARARGS, "Print out"},
1756  {"compute_fluxes_ext_kinetic", compute_fluxes_ext_kinetic, METH_VARARGS, "Print out"},
1757  {"gravity", gravity, METH_VARARGS, "Print out"},
1758  {"manning_friction", manning_friction, METH_VARARGS, "Print out"},
1759  {"balance_deep_and_shallow", balance_deep_and_shallow,
1760   METH_VARARGS, "Print out"},
1761  {"h_limiter", h_limiter,
1762   METH_VARARGS, "Print out"},
1763  {"h_limiter_sw", h_limiter_sw,
1764   METH_VARARGS, "Print out"},
1765  {"protect", protect, METH_VARARGS | METH_KEYWORDS, "Print out"},
1766  {"assign_windfield_values", assign_windfield_values,
1767   METH_VARARGS | METH_KEYWORDS, "Print out"},
1768  //{"distribute_to_vertices_and_edges",
1769  // distribute_to_vertices_and_edges, METH_VARARGS},
1770  //{"update_conserved_quantities",
1771  // update_conserved_quantities, METH_VARARGS},
1772  //{"set_initialcondition",
1773  // set_initialcondition, METH_VARARGS},
1774  {NULL, NULL}
1775};
1776
1777// Module initialisation
1778void initshallow_water_ext(void){
1779  Py_InitModule("shallow_water_ext", MethodTable);
1780
1781  import_array();     //Necessary for handling of NumPY structures
1782}
Note: See TracBrowser for help on using the repository browser.