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

Last change on this file since 5224 was 5224, checked in by ole, 15 years ago

Work done during Water Down Under 2008.
Tried alternative CFL conditions as suggested by Ted Rigby.
They are kept commented out.

File size: 73.4 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
23
24const double pi = 3.14159265358979;
25
26// Computational function for rotation
27int _rotate(double *q, double n1, double n2) {
28  /*Rotate the momentum component q (q[1], q[2])
29    from x,y coordinates to coordinates based on normal vector (n1, n2).
30
31    Result is returned in array 3x1 r
32    To rotate in opposite direction, call rotate with (q, n1, -n2)
33
34    Contents of q are changed by this function */
35
36
37  double q1, q2;
38
39  // Shorthands
40  q1 = q[1];  // uh momentum
41  q2 = q[2];  // vh momentum
42
43  // Rotate
44  q[1] =  n1*q1 + n2*q2;
45  q[2] = -n2*q1 + n1*q2;
46
47  return 0;
48}
49
50int find_qmin_and_qmax(double dq0, double dq1, double dq2, 
51                       double *qmin, double *qmax){
52  // Considering the centroid of an FV triangle and the vertices of its
53  // auxiliary triangle, find
54  // qmin=min(q)-qc and qmax=max(q)-qc,
55  // where min(q) and max(q) are respectively min and max over the
56  // four values (at the centroid of the FV triangle and the auxiliary
57  // triangle vertices),
58  // and qc is the centroid
59  // dq0=q(vertex0)-q(centroid of FV triangle)
60  // dq1=q(vertex1)-q(vertex0)
61  // dq2=q(vertex2)-q(vertex0)
62 
63  if (dq0>=0.0){
64    if (dq1>=dq2){
65      if (dq1>=0.0)
66        *qmax=dq0+dq1;
67      else
68        *qmax=dq0;
69      if ((*qmin=dq0+dq2)<0)
70        ;// qmin is already set to correct value
71      else
72        *qmin=0.0;
73    }
74    else{// dq1<dq2
75      if (dq2>0)
76        *qmax=dq0+dq2;
77      else
78        *qmax=dq0;
79      if ((*qmin=dq0+dq1)<0)
80        ;// qmin is the correct value
81      else
82        *qmin=0.0;
83    }
84  }
85  else{//dq0<0
86    if (dq1<=dq2){
87      if (dq1<0.0)
88        *qmin=dq0+dq1;
89      else
90        *qmin=dq0;
91      if ((*qmax=dq0+dq2)>0.0)
92        ;// qmax is already set to the correct value
93      else
94        *qmax=0.0;
95    }
96    else{// dq1>dq2
97      if (dq2<0.0)
98        *qmin=dq0+dq2;
99      else
100        *qmin=dq0;
101      if ((*qmax=dq0+dq1)>0.0)
102        ;// qmax is already set to the correct value
103      else
104        *qmax=0.0;
105    }
106  }
107  return 0;
108}
109
110int limit_gradient(double *dqv, double qmin, double qmax, double beta_w){
111  // Given provisional jumps dqv from the FV triangle centroid to its
112  // vertices and jumps qmin (qmax) between the centroid of the FV
113  // triangle and the minimum (maximum) of the values at the centroid of
114  // the FV triangle and the auxiliary triangle vertices,
115  // calculate a multiplicative factor phi by which the provisional
116  // vertex jumps are to be limited
117 
118  int i;
119  double r=1000.0, r0=1.0, phi=1.0;
120  static double TINY = 1.0e-100; // to avoid machine accuracy problems.
121  // FIXME: Perhaps use the epsilon used elsewhere.
122 
123  // Any provisional jump with magnitude < TINY does not contribute to
124  // the limiting process.
125  for (i=0;i<3;i++){
126    if (dqv[i]<-TINY)
127      r0=qmin/dqv[i];
128     
129    if (dqv[i]>TINY)
130      r0=qmax/dqv[i];
131     
132    r=min(r0,r);
133  }
134 
135  phi=min(r*beta_w,1.0);
136  for (i=0;i<3;i++)
137    dqv[i]=dqv[i]*phi;
138   
139  return 0;
140}
141
142
143double compute_froude_number(double uh,
144                             double h, 
145                             double g,
146                             double epsilon) {
147                         
148  // Compute Froude number; v/sqrt(gh)
149 
150  double froude_number;
151 
152  //Compute Froude number (stability diagnostics)
153  if (h > epsilon) { 
154    froude_number = uh/sqrt(g*h)/h;
155  } else {
156    froude_number = 0.0;
157    // FIXME (Ole): What should it be when dry??
158  }
159 
160  return froude_number;
161}
162
163
164
165// Function to obtain speed from momentum and depth.
166// This is used by flux functions
167// Input parameters uh and h may be modified by this function.
168double _compute_speed(double *uh, 
169                      double *h, 
170                      double epsilon, 
171                      double h0) {
172 
173  double u;
174
175 
176  if (*h < epsilon) {
177    *h = 0.0;  //Could have been negative
178    u = 0.0;
179  } else {
180    u = *uh/(*h + h0/ *h);   
181  }
182 
183
184  // Adjust momentum to be consistent with speed
185  *uh = u * *h;
186 
187  return u;
188}
189
190// Innermost flux function (using stage w=z+h)
191int _flux_function_central(double *q_left, double *q_right,
192                           double z_left, double z_right,
193                           double n1, double n2,
194                           double epsilon, double H0, double g,
195                           double *edgeflux, double *max_speed) {
196
197  /*Compute fluxes between volumes for the shallow water wave equation
198    cast in terms of the 'stage', w = h+z using
199    the 'central scheme' as described in
200
201    Kurganov, Noelle, Petrova. 'Semidiscrete Central-Upwind Schemes For
202    Hyperbolic Conservation Laws and Hamilton-Jacobi Equations'.
203    Siam J. Sci. Comput. Vol. 23, No. 3, pp. 707-740.
204
205    The implemented formula is given in equation (3.15) on page 714
206  */
207
208  int i;
209
210  double w_left, h_left, uh_left, vh_left, u_left;
211  double w_right, h_right, uh_right, vh_right, u_right;
212  double v_left, v_right; 
213  double s_min, s_max, soundspeed_left, soundspeed_right;
214  double denom, z;
215
216  // Workspace (allocate once, use many)
217  static double q_left_rotated[3], q_right_rotated[3], flux_right[3], flux_left[3];
218
219  double h0 = H0*H0; // This ensures a good balance when h approaches H0.
220                     // But evidence suggests that h0 can be as little as
221                     // epsilon!
222 
223  // Copy conserved quantities to protect from modification
224  for (i=0; i<3; i++) {
225    q_left_rotated[i] = q_left[i];
226    q_right_rotated[i] = q_right[i];
227  }
228
229  // Align x- and y-momentum with x-axis
230  _rotate(q_left_rotated, n1, n2);
231  _rotate(q_right_rotated, n1, n2);
232
233  z = (z_left+z_right)/2; // Average elevation values.
234                          // Even though this will nominally allow for discontinuities
235                          // in the elevation data, there is currently no numerical
236                          // support for this so results may be strange near jumps in the bed.
237
238  // Compute speeds in x-direction
239  w_left = q_left_rotated[0];         
240  h_left = w_left-z;
241  uh_left = q_left_rotated[1];
242  u_left = _compute_speed(&uh_left, &h_left, epsilon, h0);
243
244  w_right = q_right_rotated[0];
245  h_right = w_right-z;
246  uh_right = q_right_rotated[1];
247  u_right = _compute_speed(&uh_right, &h_right, epsilon, h0); 
248
249  // Momentum in y-direction
250  vh_left  = q_left_rotated[2];
251  vh_right = q_right_rotated[2];
252
253  // Limit y-momentum if necessary 
254  v_left = _compute_speed(&vh_left, &h_left, epsilon, h0);
255  v_right = _compute_speed(&vh_right, &h_right, epsilon, h0);
256
257  // Maximal and minimal wave speeds
258  soundspeed_left  = sqrt(g*h_left);
259  soundspeed_right = sqrt(g*h_right);
260
261  s_max = max(u_left+soundspeed_left, u_right+soundspeed_right);
262  if (s_max < 0.0) s_max = 0.0;
263
264  s_min = min(u_left-soundspeed_left, u_right-soundspeed_right);
265  if (s_min > 0.0) s_min = 0.0;
266
267 
268  // Flux formulas
269  flux_left[0] = u_left*h_left;
270  flux_left[1] = u_left*uh_left + 0.5*g*h_left*h_left;
271  flux_left[2] = u_left*vh_left;
272
273  flux_right[0] = u_right*h_right;
274  flux_right[1] = u_right*uh_right + 0.5*g*h_right*h_right;
275  flux_right[2] = u_right*vh_right;
276
277 
278  // Flux computation
279  denom = s_max-s_min;
280  if (denom < epsilon) { // FIXME (Ole): Try using H0 here
281    for (i=0; i<3; i++) edgeflux[i] = 0.0;
282    *max_speed = 0.0;
283  } else {
284    for (i=0; i<3; i++) {
285      edgeflux[i] = s_max*flux_left[i] - s_min*flux_right[i];
286      edgeflux[i] += s_max*s_min*(q_right_rotated[i]-q_left_rotated[i]);
287      edgeflux[i] /= denom;
288    }
289
290    // Maximal wavespeed
291    *max_speed = max(fabs(s_max), fabs(s_min));
292
293    // Rotate back
294    _rotate(edgeflux, n1, -n2);
295  }
296 
297  return 0;
298}
299
300double erfcc(double x){
301    double z,t,result;
302
303    z=fabs(x);
304    t=1.0/(1.0+0.5*z);
305    result=t*exp(-z*z-1.26551223+t*(1.00002368+t*(.37409196+
306         t*(.09678418+t*(-.18628806+t*(.27886807+t*(-1.13520398+
307         t*(1.48851587+t*(-.82215223+t*.17087277)))))))));
308    if (x < 0.0) result = 2.0-result;
309
310    return result;
311    }
312
313
314
315// Computational function for flux computation (using stage w=z+h)
316int flux_function_kinetic(double *q_left, double *q_right,
317                  double z_left, double z_right,
318                  double n1, double n2,
319                  double epsilon, double H0, double g,
320                  double *edgeflux, double *max_speed) {
321
322  /*Compute fluxes between volumes for the shallow water wave equation
323    cast in terms of the 'stage', w = h+z using
324    the 'central scheme' as described in
325
326    Zhang et. al., Advances in Water Resources, 26(6), 2003, 635-647.
327  */
328
329  int i;
330
331  double w_left, h_left, uh_left, vh_left, u_left, F_left;
332  double w_right, h_right, uh_right, vh_right, u_right, F_right;
333  double s_min, s_max, soundspeed_left, soundspeed_right;
334  double z;
335  double q_left_rotated[3], q_right_rotated[3];
336
337  double h0 = H0*H0; //This ensures a good balance when h approaches H0.
338
339  //Copy conserved quantities to protect from modification
340  for (i=0; i<3; i++) {
341    q_left_rotated[i] = q_left[i];
342    q_right_rotated[i] = q_right[i];
343  }
344
345  //Align x- and y-momentum with x-axis
346  _rotate(q_left_rotated, n1, n2);
347  _rotate(q_right_rotated, n1, n2);
348
349  z = (z_left+z_right)/2; //Take average of field values
350
351  //Compute speeds in x-direction
352  w_left = q_left_rotated[0];         
353  h_left = w_left-z;
354  uh_left = q_left_rotated[1];
355  u_left =_compute_speed(&uh_left, &h_left, epsilon, h0);
356
357  w_right = q_right_rotated[0];
358  h_right = w_right-z;
359  uh_right = q_right_rotated[1];
360  u_right =_compute_speed(&uh_right, &h_right, epsilon, h0); 
361
362
363  //Momentum in y-direction
364  vh_left  = q_left_rotated[2];
365  vh_right = q_right_rotated[2];
366
367
368  //Maximal and minimal wave speeds
369  soundspeed_left  = sqrt(g*h_left);
370  soundspeed_right = sqrt(g*h_right);
371
372  s_max = max(u_left+soundspeed_left, u_right+soundspeed_right);
373  if (s_max < 0.0) s_max = 0.0;
374
375  s_min = min(u_left-soundspeed_left, u_right-soundspeed_right);
376  if (s_min > 0.0) s_min = 0.0;
377
378
379  F_left  = 0.0;
380  F_right = 0.0;
381  if (h_left > 0.0) F_left = u_left/sqrt(g*h_left);
382  if (h_right > 0.0) F_right = u_right/sqrt(g*h_right);
383
384  for (i=0; i<3; i++) edgeflux[i] = 0.0;
385  *max_speed = 0.0;
386
387  edgeflux[0] = h_left*u_left/2.0*erfcc(-F_left) +  \
388          h_left*sqrt(g*h_left)/2.0/sqrt(pi)*exp(-(F_left*F_left)) + \
389          h_right*u_right/2.0*erfcc(F_right) -  \
390          h_right*sqrt(g*h_right)/2.0/sqrt(pi)*exp(-(F_right*F_right));
391
392  edgeflux[1] = (h_left*u_left*u_left + g/2.0*h_left*h_left)/2.0*erfcc(-F_left) + \
393          u_left*h_left*sqrt(g*h_left)/2.0/sqrt(pi)*exp(-(F_left*F_left)) + \
394          (h_right*u_right*u_right + g/2.0*h_right*h_right)/2.0*erfcc(F_right) -  \
395          u_right*h_right*sqrt(g*h_right)/2.0/sqrt(pi)*exp(-(F_right*F_right));
396
397  edgeflux[2] = vh_left*u_left/2.0*erfcc(-F_left) + \
398          vh_left*sqrt(g*h_left)/2.0/sqrt(pi)*exp(-(F_left*F_left)) + \
399          vh_right*u_right/2.0*erfcc(F_right) - \
400          vh_right*sqrt(g*h_right)/2.0/sqrt(pi)*exp(-(F_right*F_right));
401
402  //Maximal wavespeed
403  *max_speed = max(fabs(s_max), fabs(s_min));
404
405  //Rotate back
406  _rotate(edgeflux, n1, -n2);
407
408  return 0;
409}
410
411
412
413
414void _manning_friction(double g, double eps, int N,
415                       double* w, double* z,
416                       double* uh, double* vh,
417                       double* eta, double* xmom, double* ymom) {
418
419  int k;
420  double S, h;
421
422  for (k=0; k<N; k++) {
423    if (eta[k] > eps) {
424      h = w[k]-z[k];
425      if (h >= eps) {
426        S = -g * eta[k]*eta[k] * sqrt((uh[k]*uh[k] + vh[k]*vh[k]));
427        S /= pow(h, 7.0/3);      //Expensive (on Ole's home computer)
428        //S /= exp(7.0/3.0*log(h));      //seems to save about 15% over manning_friction
429        //S /= h*h*(1 + h/3.0 - h*h/9.0); //FIXME: Could use a Taylor expansion
430
431
432        //Update momentum
433        xmom[k] += S*uh[k];
434        ymom[k] += S*vh[k];
435      }
436    }
437  }
438}
439
440
441/*
442void _manning_friction_explicit(double g, double eps, int N,
443                       double* w, double* z,
444                       double* uh, double* vh,
445                       double* eta, double* xmom, double* ymom) {
446
447  int k;
448  double S, h;
449
450  for (k=0; k<N; k++) {
451    if (eta[k] > eps) {
452      h = w[k]-z[k];
453      if (h >= eps) {
454        S = -g * eta[k]*eta[k] * sqrt((uh[k]*uh[k] + vh[k]*vh[k]));
455        S /= pow(h, 7.0/3);      //Expensive (on Ole's home computer)
456        //S /= exp(7.0/3.0*log(h));      //seems to save about 15% over manning_friction
457        //S /= h*h*(1 + h/3.0 - h*h/9.0); //FIXME: Could use a Taylor expansion
458
459
460        //Update momentum
461        xmom[k] += S*uh[k];
462        ymom[k] += S*vh[k];
463      }
464    }
465  }
466}
467*/
468
469
470
471
472double velocity_balance(double uh_i, 
473                        double uh,
474                        double h_i, 
475                        double h, 
476                        double alpha,
477                        double epsilon) {
478  // Find alpha such that speed at the vertex is within one
479  // order of magnitude of the centroid speed   
480
481  // FIXME(Ole): Work in progress
482 
483  double a, b, estimate;
484  double m=10; // One order of magnitude - allow for velocity deviations at vertices
485
486 
487  printf("alpha = %f, uh_i=%f, uh=%f, h_i=%f, h=%f\n",
488         alpha, uh_i, uh, h_i, h);
489     
490 
491 
492   
493  // Shorthands and determine inequality
494  if (fabs(uh) < epsilon) {
495    a = 1.0e10; // Limit
496  } else {
497    a = fabs(uh_i - uh)/fabs(uh);
498  }
499     
500  if (h < epsilon) {
501    b = 1.0e10; // Limit
502  } else {       
503    b = m*fabs(h_i - h)/h;
504  }
505
506  printf("a %f, b %f\n", a, b); 
507
508  if (a > b) {
509    estimate = (m-1)/(a-b);             
510   
511    printf("Alpha %f, estimate %f\n", 
512           alpha, estimate);   
513           
514    if (alpha < estimate) {
515      printf("Adjusting alpha from %f to %f\n", 
516             alpha, estimate);
517      alpha = estimate;
518    }
519  } else {
520 
521    if (h < h_i) {
522      estimate = (m-1)/(a-b);                 
523   
524      printf("Alpha %f, estimate %f\n", 
525             alpha, estimate);   
526           
527      if (alpha < estimate) {
528        printf("Adjusting alpha from %f to %f\n", 
529               alpha, estimate);
530        alpha = estimate;
531      }   
532    }
533    // Always fulfilled as alpha and m-1 are non negative
534  }
535 
536 
537  return alpha;
538}
539
540
541int _balance_deep_and_shallow(int N,
542                              double beta_h,
543                              double* wc,
544                              double* zc,
545                              double* wv,
546                              double* zv,
547                              double* hvbar, // Retire this
548                              double* xmomc,
549                              double* ymomc,
550                              double* xmomv,
551                              double* ymomv,
552                              double H0,
553                              int tight_slope_limiters,
554                              double alpha_balance) {
555
556  int k, k3, i, excessive_froude_number=0;
557
558  double dz, hmin, alpha, h_diff, hc_k;
559  double epsilon = 1.0e-6; // FIXME: Temporary measure
560  double g = 9.81; // FIXME: Temporary measure
561  double hv[3], h; // Depths at vertices
562  double Fx, Fy; // Froude numbers
563  double uc, vc; // Centroid speeds
564
565  // Compute linear combination between w-limited stages and
566  // h-limited stages close to the bed elevation.
567
568  for (k=0; k<N; k++) {
569    // Compute maximal variation in bed elevation
570    // This quantitiy is
571    //     dz = max_i abs(z_i - z_c)
572    // and it is independent of dimension
573    // In the 1d case zc = (z0+z1)/2
574    // In the 2d case zc = (z0+z1+z2)/3
575
576    k3 = 3*k;
577    hc_k = wc[k] - zc[k]; // Centroid value at triangle k
578   
579    dz = 0.0;
580    if (tight_slope_limiters == 0) {     
581      // FIXME: Try with this one precomputed
582      for (i=0; i<3; i++) {
583        dz = max(dz, fabs(zv[k3+i]-zc[k]));
584      }
585    }
586
587    // Calculate depth at vertices (possibly negative here!)
588    hv[0] = wv[k3] -   zv[k3];
589    hv[1] = wv[k3+1] - zv[k3+1];
590    hv[2] = wv[k3+2] - zv[k3+2];       
591   
592    // Calculate minimal depth across all three vertices
593    hmin = min(hv[0], min(hv[1], hv[2]));
594
595    //if (hmin < 0.0 ) {
596    //  printf("hmin = %f\n",hmin);
597    //}
598   
599
600    // Create alpha in [0,1], where alpha==0 means using the h-limited
601    // stage and alpha==1 means using the w-limited stage as
602    // computed by the gradient limiter (both 1st or 2nd order)
603    if (tight_slope_limiters == 0) {     
604      // If hmin > dz/alpha_balance then alpha = 1 and the bed will have no
605      // effect
606      // If hmin < 0 then alpha = 0 reverting to constant height above bed.
607      // The parameter alpha_balance==2 by default
608
609     
610      if (dz > 0.0) {
611        alpha = max( min( alpha_balance*hmin/dz, 1.0), 0.0 );     
612      } else {
613        alpha = 1.0;  // Flat bed
614      }
615      //printf("Using old style limiter\n");
616     
617    } else {
618
619      // Tight Slope Limiter (2007)
620   
621      // Make alpha as large as possible but still ensure that
622      // final depth is positive and that velocities at vertices
623      // are controlled
624   
625      if (hmin < H0) {
626        alpha = 1.0;
627        for (i=0; i<3; i++) {
628       
629          // FIXME (Ole): Simplify (remove) when (if) hvbar gets retired
630          if (beta_h > epsilon) {
631            hc_k = hvbar[k3+i]; // Depth to be used at vertices
632          }
633         
634          h_diff = hc_k - hv[i];         
635          if (h_diff <= 0) {
636            // Deep water triangle is further away from bed than
637            // shallow water (hbar < h). Any alpha will do
638         
639          } else { 
640            // Denominator is positive which means that we need some of the
641            // h-limited stage.
642           
643            alpha = min(alpha, (hc_k - H0)/h_diff);         
644          }
645        }
646
647        // Ensure alpha in [0,1]
648        if (alpha>1.0) alpha=1.0;
649        if (alpha<0.0) alpha=0.0;
650       
651      } else {
652        // Use w-limited stage exclusively in deeper water.
653        alpha = 1.0;       
654      }
655
656      //printf("alpha=%f, tight_slope_limiters=%d\n", alpha, tight_slope_limiters);
657
658      /*     
659      // Experimental code for controlling velocities at vertices.
660      // Adjust alpha (down towards first order) such that
661      // velocities at vertices remain within one order of magnitude
662      // of those at the centroid.
663     
664      for (i=0; i<3; i++) {     
665     
666        // FIXME (Ole): Simplify (remove) when (if) hvbar gets retired
667        if (beta_h > epsilon) {
668          hc_k = hvbar[k3+i]; // Depth to be used at vertices
669        }
670       
671       
672        alpha = velocity_balance(xmomv[k3+i], xmomc[k],
673                                 hv[i], hc_k, alpha, epsilon);
674                                 
675        alpha = velocity_balance(ymomv[k3+i], ymomc[k],
676                                 hv[i], hc_k, alpha, epsilon);                           
677      }
678      */
679    }
680           
681    //printf("k=%d, hmin=%.2f, dz=%.2f, alpha=%.2f, alpha_balance=%.2f\n",
682    //     k, hmin, dz, alpha, alpha_balance);
683
684    //printf("dz = %.3f, alpha = %.8f\n", dz, alpha);
685
686    //  Let
687    //
688    //    wvi be the w-limited stage (wvi = zvi + hvi)
689    //    wvi- be the h-limited state (wvi- = zvi + hvi-)
690    //
691    //
692    //  where i=0,1,2 denotes the vertex ids
693    //
694    //  Weighted balance between w-limited and h-limited stage is
695    //
696    //    wvi := (1-alpha)*(zvi+hvi-) + alpha*(zvi+hvi)
697    //
698    //  It follows that the updated wvi is
699    //    wvi := zvi + (1-alpha)*hvi- + alpha*hvi
700    //
701    //   Momentum is balanced between constant and limited
702
703
704    if (alpha < 1) {     
705      for (i=0; i<3; i++) {
706        // FIXME (Ole): Simplify when (if) hvbar gets retired       
707        if (beta_h > epsilon) {   
708          wv[k3+i] = zv[k3+i] + (1-alpha)*hvbar[k3+i] + alpha*hv[i];
709        } else {
710          wv[k3+i] = zv[k3+i] + (1-alpha)*hc_k + alpha*hv[i];   
711        }
712
713        // Update momentum at vertices
714
715
716        // Update momentum at vertices
717        // Update momentum as a linear combination of
718        // xmomc and ymomc (shallow) and momentum
719        // from extrapolator xmomv and ymomv (deep).
720
721
722        //xmomv[k3+i] = (1-alpha)*xmomc[k] + alpha*xmomv[k3+i];
723        //ymomv[k3+i] = (1-alpha)*ymomc[k] + alpha*ymomv[k3+i];
724
725        if (tight_slope_limiters == 1) {
726          // FIXME(Ole): Here's what I think (as of 17 Nov 2007)
727          // we need to do. Simple and efficient:
728       
729          // Speeds at centroids
730          if (hc_k > epsilon) {
731            uc = xmomc[k]/hc_k;
732            vc = ymomc[k]/hc_k;
733          } else {
734            uc = 0.0;
735            vc = 0.0;
736          }
737         
738          // Vertex momenta guaranteed to be consistent with depth guaranteeing
739          // controlled speed
740          hv[i] = wv[k3+i] - zv[k3+i]; // Recompute (balanced) vertex depth
741          xmomv[k3+i] = uc*hv[i];
742          ymomv[k3+i] = vc*hv[i];
743        } else {
744          // Update momentum as a linear combination of
745          // xmomc and ymomc (shallow) and momentum
746          // from extrapolator xmomv and ymomv (deep).
747          // FIXME (Ole): Is this really needed? Could we use the above
748          // instead?
749         
750          xmomv[k3+i] = (1-alpha)*xmomc[k] + alpha*xmomv[k3+i];
751          ymomv[k3+i] = (1-alpha)*ymomc[k] + alpha*ymomv[k3+i];
752       
753        }
754      }
755    }
756       
757
758       
759    if (0) {  // FIXME(Ole): Disabled while testing balancing of velocities above
760      //if (tight_slope_limiters == 1) {               
761   
762      // Ensure that the Froude number is kept realistic at vertices
763      // FIXME (Ole): I think it could be used to adjust alpha down
764      // whenever Fr is too large. Possible make sure it doesn't deviate
765      // too much from the value at the centroid. I like this idea!
766     
767      // FIXME (Ole): currently only used with tights_SL
768     
769      // FIXME (Ole): may not be necessary now
770
771      excessive_froude_number=0;   
772      for (i=0; i<3; i++) {   
773     
774        // Recalculate depth at vertex i
775        h = wv[k3+i] - zv[k3+i];
776       
777        Fx = compute_froude_number(xmomv[k3+i], h, g, epsilon);
778        Fy = compute_froude_number(ymomv[k3+i], h, g, epsilon);
779       
780        if ( (fabs(Fx) > 100.0) || (fabs(Fy) > 100.0)) {
781          // FIXME: use max_froude - or base it on centroid value of F
782          // printf("Excessive Froude number detected: %f or %f\n", Fx, Fy);
783          excessive_froude_number=1;         
784        }
785      }
786     
787      if (excessive_froude_number) { 
788   
789        // printf("Adjusting momentum to first order.\n");
790        // Go back to first order (alpha = 0) for this triangle
791        for (i=0; i<3; i++) {         
792          xmomv[k3+i] = xmomc[k];
793          ymomv[k3+i] = ymomc[k];
794         
795          if (beta_h > epsilon) {         
796            wv[k3+i] = zv[k3+i] + hvbar[k3+i];
797          } else {
798            wv[k3+i] = zv[k3+i] + hc_k; 
799          }
800        }       
801      }
802    }
803  }
804  return 0;
805}
806
807
808
809int _protect(int N,
810             double minimum_allowed_height,
811             double maximum_allowed_speed,
812             double epsilon,
813             double* wc,
814             double* zc,
815             double* xmomc,
816             double* ymomc) {
817
818  int k;
819  double hc;
820  double u, v, reduced_speed;
821
822
823  // Protect against initesimal and negative heights 
824  if (maximum_allowed_speed < epsilon) {
825    for (k=0; k<N; k++) {
826      hc = wc[k] - zc[k];
827
828      if (hc < minimum_allowed_height) {
829       
830        // Set momentum to zero and ensure h is non negative
831        xmomc[k] = 0.0;
832        ymomc[k] = 0.0;
833        if (hc <= 0.0) wc[k] = zc[k];
834      }
835    }
836  } else {
837   
838    // Protect against initesimal and negative heights
839    for (k=0; k<N; k++) {
840      hc = wc[k] - zc[k];
841
842      if (hc < minimum_allowed_height) {
843
844        //New code: Adjust momentum to guarantee speeds are physical
845        //          ensure h is non negative
846             
847        if (hc <= 0.0) {
848                wc[k] = zc[k];
849        xmomc[k] = 0.0;
850        ymomc[k] = 0.0;
851        } else {
852          //Reduce excessive speeds derived from division by small hc
853        //FIXME (Ole): This may be unnecessary with new slope limiters
854        //in effect.
855         
856          u = xmomc[k]/hc;
857          if (fabs(u) > maximum_allowed_speed) {
858            reduced_speed = maximum_allowed_speed * u/fabs(u);
859            //printf("Speed (u) has been reduced from %.3f to %.3f\n",
860            //   u, reduced_speed);
861            xmomc[k] = reduced_speed * hc;
862          }
863
864          v = ymomc[k]/hc;
865          if (fabs(v) > maximum_allowed_speed) {
866            reduced_speed = maximum_allowed_speed * v/fabs(v);
867            //printf("Speed (v) has been reduced from %.3f to %.3f\n",
868            //   v, reduced_speed);
869            ymomc[k] = reduced_speed * hc;
870          }
871        }
872      }
873    }
874  }
875  return 0;
876}
877
878
879
880int _assign_wind_field_values(int N,
881                              double* xmom_update,
882                              double* ymom_update,
883                              double* s_vec,
884                              double* phi_vec,
885                              double cw) {
886
887  // Assign windfield values to momentum updates
888
889  int k;
890  double S, s, phi, u, v;
891
892  for (k=0; k<N; k++) {
893
894    s = s_vec[k];
895    phi = phi_vec[k];
896
897    //Convert to radians
898    phi = phi*pi/180;
899
900    //Compute velocity vector (u, v)
901    u = s*cos(phi);
902    v = s*sin(phi);
903
904    //Compute wind stress
905    S = cw * sqrt(u*u + v*v);
906    xmom_update[k] += S*u;
907    ymom_update[k] += S*v;
908  }
909  return 0;
910}
911
912
913
914///////////////////////////////////////////////////////////////////
915// Gateways to Python
916
917
918
919PyObject *flux_function_central(PyObject *self, PyObject *args) {
920  //
921  // Gateway to innermost flux function.
922  // This is only used by the unit tests as the c implementation is
923  // normally called by compute_fluxes in this module.
924
925
926  PyArrayObject *normal, *ql, *qr,  *edgeflux;
927  double g, epsilon, max_speed, H0, zl, zr;
928
929  if (!PyArg_ParseTuple(args, "OOOddOddd",
930                        &normal, &ql, &qr, &zl, &zr, &edgeflux,
931                        &epsilon, &g, &H0)) {
932    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: flux_function_central could not parse input arguments");
933    return NULL;
934  }
935
936 
937  _flux_function_central((double*) ql -> data, 
938                         (double*) qr -> data, 
939                         zl, 
940                         zr,                                             
941                         ((double*) normal -> data)[0],                                                                         
942                         ((double*) normal -> data)[1],                 
943                         epsilon, H0, g,
944                         (double*) edgeflux -> data, 
945                         &max_speed);
946 
947  return Py_BuildValue("d", max_speed); 
948}
949
950
951
952PyObject *gravity(PyObject *self, PyObject *args) {
953  //
954  //  gravity(g, h, v, x, xmom, ymom)
955  //
956
957
958  PyArrayObject *h, *v, *x, *xmom, *ymom;
959  int k, N, k3, k6;
960  double g, avg_h, zx, zy;
961  double x0, y0, x1, y1, x2, y2, z0, z1, z2;
962  //double epsilon;
963
964  if (!PyArg_ParseTuple(args, "dOOOOO",
965                        &g, &h, &v, &x,
966                        &xmom, &ymom)) {
967    //&epsilon)) {
968    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: gravity could not parse input arguments");
969    return NULL;
970  }
971
972  N = h -> dimensions[0];
973  for (k=0; k<N; k++) {
974    k3 = 3*k;  // base index
975
976    // Get bathymetry
977    z0 = ((double*) v -> data)[k3 + 0];
978    z1 = ((double*) v -> data)[k3 + 1];
979    z2 = ((double*) v -> data)[k3 + 2];
980
981    // Optimise for flat bed
982    // Note (Ole): This didn't produce measurable speed up.
983    // Revisit later
984    //if (fabs(z0-z1)<epsilon && fabs(z1-z2)<epsilon) {
985    //  continue;
986    //}
987
988    // Get average depth from centroid values
989    avg_h = ((double *) h -> data)[k];
990
991    // Compute bed slope
992    k6 = 6*k;  // base index
993 
994    x0 = ((double*) x -> data)[k6 + 0];
995    y0 = ((double*) x -> data)[k6 + 1];
996    x1 = ((double*) x -> data)[k6 + 2];
997    y1 = ((double*) x -> data)[k6 + 3];
998    x2 = ((double*) x -> data)[k6 + 4];
999    y2 = ((double*) x -> data)[k6 + 5];
1000
1001
1002    _gradient(x0, y0, x1, y1, x2, y2, z0, z1, z2, &zx, &zy);
1003
1004    // Update momentum
1005    ((double*) xmom -> data)[k] += -g*zx*avg_h;
1006    ((double*) ymom -> data)[k] += -g*zy*avg_h;
1007  }
1008
1009  return Py_BuildValue("");
1010}
1011
1012
1013PyObject *manning_friction(PyObject *self, PyObject *args) {
1014  //
1015  // manning_friction(g, eps, h, uh, vh, eta, xmom_update, ymom_update)
1016  //
1017
1018
1019  PyArrayObject *w, *z, *uh, *vh, *eta, *xmom, *ymom;
1020  int N;
1021  double g, eps;
1022
1023  if (!PyArg_ParseTuple(args, "ddOOOOOOO",
1024                        &g, &eps, &w, &z, &uh, &vh, &eta,
1025                        &xmom, &ymom)) {
1026    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: manning_friction could not parse input arguments");
1027    return NULL;
1028  }
1029
1030
1031  N = w -> dimensions[0];
1032  _manning_friction(g, eps, N,
1033                    (double*) w -> data,
1034                    (double*) z -> data,
1035                    (double*) uh -> data,
1036                    (double*) vh -> data,
1037                    (double*) eta -> data,
1038                    (double*) xmom -> data,
1039                    (double*) ymom -> data);
1040
1041  return Py_BuildValue("");
1042}
1043
1044
1045/*
1046PyObject *manning_friction_explicit(PyObject *self, PyObject *args) {
1047  //
1048  // manning_friction_explicit(g, eps, h, uh, vh, eta, xmom_update, ymom_update)
1049  //
1050
1051
1052  PyArrayObject *w, *z, *uh, *vh, *eta, *xmom, *ymom;
1053  int N;
1054  double g, eps;
1055
1056  if (!PyArg_ParseTuple(args, "ddOOOOOOO",
1057                        &g, &eps, &w, &z, &uh, &vh, &eta,
1058                        &xmom, &ymom))
1059    return NULL;
1060
1061  N = w -> dimensions[0];
1062  _manning_friction_explicit(g, eps, N,
1063                    (double*) w -> data,
1064                    (double*) z -> data,
1065                    (double*) uh -> data,
1066                    (double*) vh -> data,
1067                    (double*) eta -> data,
1068                    (double*) xmom -> data,
1069                    (double*) ymom -> data);
1070
1071  return Py_BuildValue("");
1072}
1073*/
1074
1075
1076
1077// Computational routine
1078int _extrapolate_second_order_sw(int number_of_elements,
1079                                  double epsilon,
1080                                  double minimum_allowed_height,
1081                                  double beta_w,
1082                                  double beta_w_dry,
1083                                  double beta_uh,
1084                                  double beta_uh_dry,
1085                                  double beta_vh,
1086                                  double beta_vh_dry,
1087                                  long* surrogate_neighbours,
1088                                  long* number_of_boundaries,
1089                                  double* centroid_coordinates,
1090                                  double* stage_centroid_values,
1091                                  double* xmom_centroid_values,
1092                                  double* ymom_centroid_values,
1093                                  double* elevation_centroid_values,
1094                                  double* vertex_coordinates,
1095                                  double* stage_vertex_values,
1096                                  double* xmom_vertex_values,
1097                                  double* ymom_vertex_values,
1098                                  double* elevation_vertex_values,
1099                                  int optimise_dry_cells) {
1100                                 
1101                                 
1102
1103  // Local variables
1104  double a, b; // Gradient vector used to calculate vertex values from centroids
1105  int k,k0,k1,k2,k3,k6,coord_index,i;
1106  double x,y,x0,y0,x1,y1,x2,y2,xv0,yv0,xv1,yv1,xv2,yv2; // Vertices of the auxiliary triangle
1107  double dx1,dx2,dy1,dy2,dxv0,dxv1,dxv2,dyv0,dyv1,dyv2,dq0,dq1,dq2,area2;
1108  double dqv[3], qmin, qmax, hmin, hmax;
1109  double hc, h0, h1, h2, beta_tmp, hfactor;
1110
1111       
1112  for (k=0; k<number_of_elements; k++) {
1113    k3=k*3;
1114    k6=k*6;
1115
1116   
1117    if (number_of_boundaries[k]==3){
1118      // No neighbours, set gradient on the triangle to zero
1119     
1120      stage_vertex_values[k3]   = stage_centroid_values[k];
1121      stage_vertex_values[k3+1] = stage_centroid_values[k];
1122      stage_vertex_values[k3+2] = stage_centroid_values[k];
1123      xmom_vertex_values[k3]    = xmom_centroid_values[k];
1124      xmom_vertex_values[k3+1]  = xmom_centroid_values[k];
1125      xmom_vertex_values[k3+2]  = xmom_centroid_values[k];
1126      ymom_vertex_values[k3]    = ymom_centroid_values[k];
1127      ymom_vertex_values[k3+1]  = ymom_centroid_values[k];
1128      ymom_vertex_values[k3+2]  = ymom_centroid_values[k];
1129     
1130      continue;
1131    }
1132    else {
1133      // Triangle k has one or more neighbours.
1134      // Get centroid and vertex coordinates of the triangle
1135     
1136      // Get the vertex coordinates
1137      xv0 = vertex_coordinates[k6];   yv0=vertex_coordinates[k6+1];
1138      xv1 = vertex_coordinates[k6+2]; yv1=vertex_coordinates[k6+3];
1139      xv2 = vertex_coordinates[k6+4]; yv2=vertex_coordinates[k6+5];
1140     
1141      // Get the centroid coordinates
1142      coord_index=2*k;
1143      x=centroid_coordinates[coord_index];
1144      y=centroid_coordinates[coord_index+1];
1145     
1146      // Store x- and y- differentials for the vertices of
1147      // triangle k relative to the centroid
1148      dxv0=xv0-x; dxv1=xv1-x; dxv2=xv2-x;
1149      dyv0=yv0-y; dyv1=yv1-y; dyv2=yv2-y;
1150    }
1151
1152
1153           
1154    if (number_of_boundaries[k]<=1){
1155   
1156      //==============================================
1157      // Number of boundaries <= 1
1158      //==============================================   
1159   
1160   
1161      // If no boundaries, auxiliary triangle is formed
1162      // from the centroids of the three neighbours
1163      // If one boundary, auxiliary triangle is formed
1164      // from this centroid and its two neighbours
1165     
1166      k0=surrogate_neighbours[k3];
1167      k1=surrogate_neighbours[k3+1];
1168      k2=surrogate_neighbours[k3+2];
1169     
1170      // Get the auxiliary triangle's vertex coordinates
1171      // (really the centroids of neighbouring triangles)
1172      coord_index=2*k0;
1173      x0=centroid_coordinates[coord_index];
1174      y0=centroid_coordinates[coord_index+1];
1175     
1176      coord_index=2*k1;
1177      x1=centroid_coordinates[coord_index];
1178      y1=centroid_coordinates[coord_index+1];
1179     
1180      coord_index=2*k2;
1181      x2=centroid_coordinates[coord_index];
1182      y2=centroid_coordinates[coord_index+1];
1183     
1184      // Store x- and y- differentials for the vertices
1185      // of the auxiliary triangle
1186      dx1=x1-x0; dx2=x2-x0;
1187      dy1=y1-y0; dy2=y2-y0;
1188     
1189      // Calculate 2*area of the auxiliary triangle
1190      // The triangle is guaranteed to be counter-clockwise     
1191      area2 = dy2*dx1 - dy1*dx2; 
1192     
1193      // If the mesh is 'weird' near the boundary,
1194      // the triangle might be flat or clockwise:
1195      if (area2<=0) {
1196        PyErr_SetString(PyExc_RuntimeError, 
1197                        "shallow_water_ext.c: negative triangle area encountered");
1198        return -1;
1199      } 
1200     
1201      // Calculate heights of neighbouring cells
1202      hc = stage_centroid_values[k]  - elevation_centroid_values[k];
1203      h0 = stage_centroid_values[k0] - elevation_centroid_values[k0];
1204      h1 = stage_centroid_values[k1] - elevation_centroid_values[k1];
1205      h2 = stage_centroid_values[k2] - elevation_centroid_values[k2];
1206      hmin = min(min(h0,min(h1,h2)),hc);
1207      //hfactor = hc/(hc + 1.0);
1208
1209      hfactor = 0.0;
1210      if (hmin > 0.001 ) {
1211          hfactor = (hmin-0.001)/(hmin+0.004);
1212      }
1213     
1214      if (optimise_dry_cells) {     
1215        // Check if linear reconstruction is necessary for triangle k
1216        // This check will exclude dry cells.
1217
1218        hmax = max(h0,max(h1,h2));     
1219        if (hmax < epsilon) {
1220          continue;
1221        }
1222      }
1223
1224           
1225      //-----------------------------------
1226      // stage
1227      //-----------------------------------     
1228     
1229      // Calculate the difference between vertex 0 of the auxiliary
1230      // triangle and the centroid of triangle k
1231      dq0=stage_centroid_values[k0]-stage_centroid_values[k];
1232     
1233      // Calculate differentials between the vertices
1234      // of the auxiliary triangle (centroids of neighbouring triangles)
1235      dq1=stage_centroid_values[k1]-stage_centroid_values[k0];
1236      dq2=stage_centroid_values[k2]-stage_centroid_values[k0];
1237     
1238      // Calculate the gradient of stage on the auxiliary triangle
1239      a = dy2*dq1 - dy1*dq2;
1240      a /= area2;
1241      b = dx1*dq2 - dx2*dq1;
1242      b /= area2;
1243     
1244      // Calculate provisional jumps in stage from the centroid
1245      // of triangle k to its vertices, to be limited
1246      dqv[0]=a*dxv0+b*dyv0;
1247      dqv[1]=a*dxv1+b*dyv1;
1248      dqv[2]=a*dxv2+b*dyv2;
1249     
1250      // Now we want to find min and max of the centroid and the
1251      // vertices of the auxiliary triangle and compute jumps
1252      // from the centroid to the min and max
1253      find_qmin_and_qmax(dq0,dq1,dq2,&qmin,&qmax);
1254     
1255      // Playing with dry wet interface
1256      //hmin = qmin;
1257      //beta_tmp = beta_w_dry;
1258      //if (hmin>minimum_allowed_height)
1259      beta_tmp = beta_w_dry + (beta_w - beta_w_dry) * hfactor;
1260       
1261      //printf("min_alled_height = %f\n",minimum_allowed_height);
1262      //printf("hmin = %f\n",hmin);
1263      //printf("beta_w = %f\n",beta_w);
1264      //printf("beta_tmp = %f\n",beta_tmp);
1265      // Limit the gradient
1266      limit_gradient(dqv,qmin,qmax,beta_tmp); 
1267     
1268      for (i=0;i<3;i++)
1269        stage_vertex_values[k3+i]=stage_centroid_values[k]+dqv[i];
1270     
1271     
1272      //-----------------------------------
1273      // xmomentum
1274      //-----------------------------------           
1275
1276      // Calculate the difference between vertex 0 of the auxiliary
1277      // triangle and the centroid of triangle k     
1278      dq0=xmom_centroid_values[k0]-xmom_centroid_values[k];
1279     
1280      // Calculate differentials between the vertices
1281      // of the auxiliary triangle
1282      dq1=xmom_centroid_values[k1]-xmom_centroid_values[k0];
1283      dq2=xmom_centroid_values[k2]-xmom_centroid_values[k0];
1284     
1285      // Calculate the gradient of xmom on the auxiliary triangle
1286      a = dy2*dq1 - dy1*dq2;
1287      a /= area2;
1288      b = dx1*dq2 - dx2*dq1;
1289      b /= area2;
1290     
1291      // Calculate provisional jumps in stage from the centroid
1292      // of triangle k to its vertices, to be limited     
1293      dqv[0]=a*dxv0+b*dyv0;
1294      dqv[1]=a*dxv1+b*dyv1;
1295      dqv[2]=a*dxv2+b*dyv2;
1296     
1297      // Now we want to find min and max of the centroid and the
1298      // vertices of the auxiliary triangle and compute jumps
1299      // from the centroid to the min and max
1300      find_qmin_and_qmax(dq0,dq1,dq2,&qmin,&qmax);
1301      //beta_tmp = beta_uh;
1302      //if (hmin<minimum_allowed_height)
1303      //beta_tmp = beta_uh_dry;
1304      beta_tmp = beta_uh_dry + (beta_uh - beta_uh_dry) * hfactor;
1305
1306      // Limit the gradient
1307      limit_gradient(dqv,qmin,qmax,beta_tmp);
1308
1309      for (i=0;i<3;i++)
1310        xmom_vertex_values[k3+i]=xmom_centroid_values[k]+dqv[i];
1311     
1312     
1313      //-----------------------------------
1314      // ymomentum
1315      //-----------------------------------                 
1316
1317      // Calculate the difference between vertex 0 of the auxiliary
1318      // triangle and the centroid of triangle k
1319      dq0=ymom_centroid_values[k0]-ymom_centroid_values[k];
1320     
1321      // Calculate differentials between the vertices
1322      // of the auxiliary triangle
1323      dq1=ymom_centroid_values[k1]-ymom_centroid_values[k0];
1324      dq2=ymom_centroid_values[k2]-ymom_centroid_values[k0];
1325     
1326      // Calculate the gradient of xmom on the auxiliary triangle
1327      a = dy2*dq1 - dy1*dq2;
1328      a /= area2;
1329      b = dx1*dq2 - dx2*dq1;
1330      b /= area2;
1331     
1332      // Calculate provisional jumps in stage from the centroid
1333      // of triangle k to its vertices, to be limited
1334      dqv[0]=a*dxv0+b*dyv0;
1335      dqv[1]=a*dxv1+b*dyv1;
1336      dqv[2]=a*dxv2+b*dyv2;
1337     
1338      // Now we want to find min and max of the centroid and the
1339      // vertices of the auxiliary triangle and compute jumps
1340      // from the centroid to the min and max
1341      find_qmin_and_qmax(dq0,dq1,dq2,&qmin,&qmax);
1342     
1343      //beta_tmp = beta_vh;
1344      //
1345      //if (hmin<minimum_allowed_height)
1346      //beta_tmp = beta_vh_dry;
1347      beta_tmp = beta_vh_dry + (beta_vh - beta_vh_dry) * hfactor;       
1348
1349      // Limit the gradient
1350      limit_gradient(dqv,qmin,qmax,beta_tmp);
1351     
1352      for (i=0;i<3;i++)
1353        ymom_vertex_values[k3+i]=ymom_centroid_values[k]+dqv[i];
1354       
1355    } // End number_of_boundaries <=1
1356    else{
1357
1358      //==============================================
1359      // Number of boundaries == 2
1360      //==============================================       
1361       
1362      // One internal neighbour and gradient is in direction of the neighbour's centroid
1363     
1364      // Find the only internal neighbour
1365      for (k2=k3;k2<k3+3;k2++){
1366        // Find internal neighbour of triangle k     
1367        // k2 indexes the edges of triangle k   
1368     
1369        if (surrogate_neighbours[k2]!=k)
1370          break;
1371      }
1372      if ((k2==k3+3)) {
1373        // If we didn't find an internal neighbour
1374        PyErr_SetString(PyExc_RuntimeError, 
1375                        "shallow_water_ext.c: Internal neighbour not found");     
1376        return -1;
1377      }
1378     
1379      k1=surrogate_neighbours[k2];
1380     
1381      // The coordinates of the triangle are already (x,y).
1382      // Get centroid of the neighbour (x1,y1)
1383      coord_index=2*k1;
1384      x1=centroid_coordinates[coord_index];
1385      y1=centroid_coordinates[coord_index+1];
1386     
1387      // Compute x- and y- distances between the centroid of
1388      // triangle k and that of its neighbour
1389      dx1=x1-x; dy1=y1-y;
1390     
1391      // Set area2 as the square of the distance
1392      area2=dx1*dx1+dy1*dy1;
1393     
1394      // Set dx2=(x1-x0)/((x1-x0)^2+(y1-y0)^2)
1395      // and dy2=(y1-y0)/((x1-x0)^2+(y1-y0)^2) which
1396      // respectively correspond to the x- and y- gradients
1397      // of the conserved quantities
1398      dx2=1.0/area2;
1399      dy2=dx2*dy1;
1400      dx2*=dx1;
1401     
1402     
1403     
1404      //-----------------------------------
1405      // stage
1406      //-----------------------------------           
1407
1408      // Compute differentials
1409      dq1=stage_centroid_values[k1]-stage_centroid_values[k];
1410     
1411      // Calculate the gradient between the centroid of triangle k
1412      // and that of its neighbour
1413      a=dq1*dx2;
1414      b=dq1*dy2;
1415     
1416      // Calculate provisional vertex jumps, to be limited
1417      dqv[0]=a*dxv0+b*dyv0;
1418      dqv[1]=a*dxv1+b*dyv1;
1419      dqv[2]=a*dxv2+b*dyv2;
1420     
1421      // Now limit the jumps
1422      if (dq1>=0.0){
1423        qmin=0.0;
1424        qmax=dq1;
1425      }
1426      else{
1427        qmin=dq1;
1428        qmax=0.0;
1429      }
1430     
1431      // Limit the gradient
1432      limit_gradient(dqv,qmin,qmax,beta_w);
1433     
1434      for (i=0;i<3;i++)
1435        stage_vertex_values[k3+i]=stage_centroid_values[k]+dqv[i];
1436     
1437     
1438      //-----------------------------------
1439      // xmomentum
1440      //-----------------------------------                       
1441     
1442      // Compute differentials
1443      dq1=xmom_centroid_values[k1]-xmom_centroid_values[k];
1444     
1445      // Calculate the gradient between the centroid of triangle k
1446      // and that of its neighbour
1447      a=dq1*dx2;
1448      b=dq1*dy2;
1449     
1450      // Calculate provisional vertex jumps, to be limited
1451      dqv[0]=a*dxv0+b*dyv0;
1452      dqv[1]=a*dxv1+b*dyv1;
1453      dqv[2]=a*dxv2+b*dyv2;
1454     
1455      // Now limit the jumps
1456      if (dq1>=0.0){
1457        qmin=0.0;
1458        qmax=dq1;
1459      }
1460      else{
1461        qmin=dq1;
1462        qmax=0.0;
1463      }
1464     
1465      // Limit the gradient     
1466      limit_gradient(dqv,qmin,qmax,beta_w);
1467     
1468      for (i=0;i<3;i++)
1469        xmom_vertex_values[k3+i]=xmom_centroid_values[k]+dqv[i];
1470     
1471     
1472      //-----------------------------------
1473      // ymomentum
1474      //-----------------------------------                       
1475
1476      // Compute differentials
1477      dq1=ymom_centroid_values[k1]-ymom_centroid_values[k];
1478     
1479      // Calculate the gradient between the centroid of triangle k
1480      // and that of its neighbour
1481      a=dq1*dx2;
1482      b=dq1*dy2;
1483     
1484      // Calculate provisional vertex jumps, to be limited
1485      dqv[0]=a*dxv0+b*dyv0;
1486      dqv[1]=a*dxv1+b*dyv1;
1487      dqv[2]=a*dxv2+b*dyv2;
1488     
1489      // Now limit the jumps
1490      if (dq1>=0.0){
1491        qmin=0.0;
1492        qmax=dq1;
1493      }
1494      else{
1495        qmin=dq1;
1496        qmax=0.0;
1497      }
1498     
1499      // Limit the gradient
1500      limit_gradient(dqv,qmin,qmax,beta_w);
1501     
1502      for (i=0;i<3;i++)
1503        ymom_vertex_values[k3+i]=ymom_centroid_values[k]+dqv[i];
1504       
1505    } // else [number_of_boundaries==2]
1506  } // for k=0 to number_of_elements-1
1507 
1508  return 0;
1509}                       
1510
1511
1512PyObject *extrapolate_second_order_sw(PyObject *self, PyObject *args) {
1513  /*Compute the vertex values based on a linear reconstruction
1514    on each triangle
1515   
1516    These values are calculated as follows:
1517    1) For each triangle not adjacent to a boundary, we consider the
1518       auxiliary triangle formed by the centroids of its three
1519       neighbours.
1520    2) For each conserved quantity, we integrate around the auxiliary
1521       triangle's boundary the product of the quantity and the outward
1522       normal vector. Dividing by the triangle area gives (a,b), the
1523       average of the vector (q_x,q_y) on the auxiliary triangle.
1524       We suppose that the linear reconstruction on the original
1525       triangle has gradient (a,b).
1526    3) Provisional vertex jumps dqv[0,1,2] are computed and these are
1527       then limited by calling the functions find_qmin_and_qmax and
1528       limit_gradient
1529
1530    Python call:
1531    extrapolate_second_order_sw(domain.surrogate_neighbours,
1532                                domain.number_of_boundaries
1533                                domain.centroid_coordinates,
1534                                Stage.centroid_values
1535                                Xmom.centroid_values
1536                                Ymom.centroid_values
1537                                domain.vertex_coordinates,
1538                                Stage.vertex_values,
1539                                Xmom.vertex_values,
1540                                Ymom.vertex_values)
1541
1542    Post conditions:
1543            The vertices of each triangle have values from a
1544            limited linear reconstruction
1545            based on centroid values
1546
1547  */
1548  PyArrayObject *surrogate_neighbours,
1549    *number_of_boundaries,
1550    *centroid_coordinates,
1551    *stage_centroid_values,
1552    *xmom_centroid_values,
1553    *ymom_centroid_values,
1554    *elevation_centroid_values,
1555    *vertex_coordinates,
1556    *stage_vertex_values,
1557    *xmom_vertex_values,
1558    *ymom_vertex_values,
1559    *elevation_vertex_values;
1560 
1561  PyObject *domain;
1562
1563 
1564  double beta_w, beta_w_dry, beta_uh, beta_uh_dry, beta_vh, beta_vh_dry;   
1565  double minimum_allowed_height, epsilon;
1566  int optimise_dry_cells, number_of_elements, e;
1567 
1568  // Provisional jumps from centroids to v'tices and safety factor re limiting
1569  // by which these jumps are limited
1570  // Convert Python arguments to C
1571  if (!PyArg_ParseTuple(args, "OOOOOOOOOOOOOi",
1572                        &domain,
1573                        &surrogate_neighbours,
1574                        &number_of_boundaries,
1575                        &centroid_coordinates,
1576                        &stage_centroid_values,
1577                        &xmom_centroid_values,
1578                        &ymom_centroid_values,
1579                        &elevation_centroid_values,
1580                        &vertex_coordinates,
1581                        &stage_vertex_values,
1582                        &xmom_vertex_values,
1583                        &ymom_vertex_values,
1584                        &elevation_vertex_values,
1585                        &optimise_dry_cells)) {                 
1586                       
1587    PyErr_SetString(PyExc_RuntimeError, 
1588                    "Input arguments to extrapolate_second_order_sw failed");
1589    return NULL;
1590  }
1591
1592  // Get the safety factor beta_w, set in the config.py file.
1593  // This is used in the limiting process
1594 
1595
1596  beta_w                 = get_python_double(domain,"beta_w");
1597  beta_w_dry             = get_python_double(domain,"beta_w_dry");
1598  beta_uh                = get_python_double(domain,"beta_uh");
1599  beta_uh_dry            = get_python_double(domain,"beta_uh_dry");
1600  beta_vh                = get_python_double(domain,"beta_vh");
1601  beta_vh_dry            = get_python_double(domain,"beta_vh_dry"); 
1602
1603  minimum_allowed_height = get_python_double(domain,"minimum_allowed_height");
1604  epsilon                = get_python_double(domain,"epsilon");
1605
1606  number_of_elements = stage_centroid_values -> dimensions[0]; 
1607
1608  // Call underlying computational routine
1609  e = _extrapolate_second_order_sw(number_of_elements,
1610                                   epsilon,
1611                                   minimum_allowed_height,
1612                                   beta_w,
1613                                   beta_w_dry,
1614                                   beta_uh,
1615                                   beta_uh_dry,
1616                                   beta_vh,
1617                                   beta_vh_dry,
1618                                   (long*) surrogate_neighbours -> data,
1619                                   (long*) number_of_boundaries -> data,
1620                                   (double*) centroid_coordinates -> data,
1621                                   (double*) stage_centroid_values -> data,
1622                                   (double*) xmom_centroid_values -> data,
1623                                   (double*) ymom_centroid_values -> data,
1624                                   (double*) elevation_centroid_values -> data,
1625                                   (double*) vertex_coordinates -> data,
1626                                   (double*) stage_vertex_values -> data,
1627                                   (double*) xmom_vertex_values -> data,
1628                                   (double*) ymom_vertex_values -> data,
1629                                   (double*) elevation_vertex_values -> data,
1630                                   optimise_dry_cells);
1631  if (e == -1) {
1632    // Use error string set inside computational routine
1633    return NULL;
1634  }                               
1635 
1636 
1637  return Py_BuildValue("");
1638 
1639}// extrapolate_second-order_sw
1640
1641
1642
1643
1644PyObject *rotate(PyObject *self, PyObject *args, PyObject *kwargs) {
1645  //
1646  // r = rotate(q, normal, direction=1)
1647  //
1648  // Where q is assumed to be a Float numeric array of length 3 and
1649  // normal a Float numeric array of length 2.
1650
1651  // FIXME(Ole): I don't think this is used anymore
1652
1653  PyObject *Q, *Normal;
1654  PyArrayObject *q, *r, *normal;
1655
1656  static char *argnames[] = {"q", "normal", "direction", NULL};
1657  int dimensions[1], i, direction=1;
1658  double n1, n2;
1659
1660  // Convert Python arguments to C
1661  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|i", argnames,
1662                                   &Q, &Normal, &direction)) {
1663    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: rotate could not parse input arguments");
1664    return NULL;
1665  } 
1666
1667  // Input checks (convert sequences into numeric arrays)
1668  q = (PyArrayObject *)
1669    PyArray_ContiguousFromObject(Q, PyArray_DOUBLE, 0, 0);
1670  normal = (PyArrayObject *)
1671    PyArray_ContiguousFromObject(Normal, PyArray_DOUBLE, 0, 0);
1672
1673
1674  if (normal -> dimensions[0] != 2) {
1675    PyErr_SetString(PyExc_RuntimeError, "Normal vector must have 2 components");
1676    return NULL;
1677  }
1678
1679  // Allocate space for return vector r (don't DECREF)
1680  dimensions[0] = 3;
1681  r = (PyArrayObject *) PyArray_FromDims(1, dimensions, PyArray_DOUBLE);
1682
1683  // Copy
1684  for (i=0; i<3; i++) {
1685    ((double *) (r -> data))[i] = ((double *) (q -> data))[i];
1686  }
1687
1688  // Get normal and direction
1689  n1 = ((double *) normal -> data)[0];
1690  n2 = ((double *) normal -> data)[1];
1691  if (direction == -1) n2 = -n2;
1692
1693  // Rotate
1694  _rotate((double *) r -> data, n1, n2);
1695
1696  // Release numeric arrays
1697  Py_DECREF(q);
1698  Py_DECREF(normal);
1699
1700  // Return result using PyArray to avoid memory leak
1701  return PyArray_Return(r);
1702}
1703
1704
1705// Computational function for flux computation
1706double _compute_fluxes_central(int number_of_elements,
1707                               double timestep,
1708                               double epsilon,
1709                               double H0,
1710                               double g,
1711                               long* neighbours,
1712                               long* neighbour_edges,
1713                               double* normals,
1714                               double* edgelengths, 
1715                               double* radii, 
1716                               double* areas,
1717                               long* tri_full_flag,
1718                               double* stage_edge_values,
1719                               double* xmom_edge_values,
1720                               double* ymom_edge_values,
1721                               double* bed_edge_values,
1722                               double* stage_boundary_values,
1723                               double* xmom_boundary_values,
1724                               double* ymom_boundary_values,
1725                               double* stage_explicit_update,
1726                               double* xmom_explicit_update,
1727                               double* ymom_explicit_update,
1728                               long* already_computed_flux,
1729                               double* max_speed_array,
1730                               int optimise_dry_cells) {
1731                               
1732  // Local variables
1733  double max_speed, length, area, zl, zr;
1734  int k, i, m, n;
1735  int ki, nm=0, ki2; // Index shorthands
1736 
1737  // Workspace (making them static actually made function slightly slower (Ole)) 
1738  double ql[3], qr[3], edgeflux[3]; // Work array for summing up fluxes
1739
1740  static long call=1; // Static local variable flagging already computed flux
1741                               
1742       
1743  // Start computation
1744  call++; // Flag 'id' of flux calculation for this timestep
1745 
1746  // Set explicit_update to zero for all conserved_quantities.
1747  // This assumes compute_fluxes called before forcing terms
1748  for (k=0; k<number_of_elements; k++) {
1749    stage_explicit_update[k]=0.0;
1750    xmom_explicit_update[k]=0.0;
1751    ymom_explicit_update[k]=0.0; 
1752  }
1753
1754  // For all triangles
1755  for (k=0; k<number_of_elements; k++) {
1756   
1757    // Loop through neighbours and compute edge flux for each 
1758    for (i=0; i<3; i++) {
1759      ki = k*3+i; // Linear index (triangle k, edge i)
1760     
1761      if (already_computed_flux[ki] == call)
1762        // We've already computed the flux across this edge
1763        continue;
1764       
1765       
1766      ql[0] = stage_edge_values[ki];
1767      ql[1] = xmom_edge_values[ki];
1768      ql[2] = ymom_edge_values[ki];
1769      zl = bed_edge_values[ki];
1770
1771      // Quantities at neighbour on nearest face
1772      n = neighbours[ki];
1773      if (n < 0) {
1774        // Neighbour is a boundary condition
1775        m = -n-1; // Convert negative flag to boundary index
1776       
1777        qr[0] = stage_boundary_values[m];
1778        qr[1] = xmom_boundary_values[m];
1779        qr[2] = ymom_boundary_values[m];
1780        zr = zl; // Extend bed elevation to boundary
1781      } else {
1782        // Neighbour is a real element
1783        m = neighbour_edges[ki];
1784        nm = n*3+m; // Linear index (triangle n, edge m)
1785       
1786        qr[0] = stage_edge_values[nm];
1787        qr[1] = xmom_edge_values[nm];
1788        qr[2] = ymom_edge_values[nm];
1789        zr = bed_edge_values[nm];
1790      }
1791     
1792     
1793      if (optimise_dry_cells) {     
1794        // Check if flux calculation is necessary across this edge
1795        // This check will exclude dry cells.
1796        // This will also optimise cases where zl != zr as
1797        // long as both are dry
1798
1799        if ( fabs(ql[0] - zl) < epsilon && 
1800             fabs(qr[0] - zr) < epsilon ) {
1801          // Cell boundary is dry
1802         
1803          already_computed_flux[ki] = call; // #k Done 
1804          if (n>=0)
1805            already_computed_flux[nm] = call; // #n Done
1806       
1807          max_speed = 0.0;
1808          continue;
1809        }
1810      }
1811   
1812     
1813      // Outward pointing normal vector (domain.normals[k, 2*i:2*i+2])
1814      ki2 = 2*ki; //k*6 + i*2
1815
1816      // Edge flux computation (triangle k, edge i)
1817      _flux_function_central(ql, qr, zl, zr,
1818                             normals[ki2], normals[ki2+1],
1819                             epsilon, H0, g,
1820                             edgeflux, &max_speed);
1821     
1822     
1823      // Multiply edgeflux by edgelength
1824      length = edgelengths[ki];
1825      edgeflux[0] *= length;           
1826      edgeflux[1] *= length;           
1827      edgeflux[2] *= length;                       
1828     
1829     
1830      // Update triangle k with flux from edge i
1831      stage_explicit_update[k] -= edgeflux[0];
1832      xmom_explicit_update[k] -= edgeflux[1];
1833      ymom_explicit_update[k] -= edgeflux[2];
1834     
1835      already_computed_flux[ki] = call; // #k Done
1836     
1837     
1838      // Update neighbour n with same flux but reversed sign
1839      if (n>=0) {
1840        stage_explicit_update[n] += edgeflux[0];
1841        xmom_explicit_update[n] += edgeflux[1];
1842        ymom_explicit_update[n] += edgeflux[2];
1843       
1844        already_computed_flux[nm] = call; // #n Done
1845      }
1846
1847
1848      // Update timestep based on edge i and possibly neighbour n
1849      if (tri_full_flag[k] == 1) {
1850        if (max_speed > epsilon) {
1851          timestep = min(timestep, radii[k]/max_speed);
1852          if (n>=0) 
1853            timestep = min(timestep, radii[n]/max_speed);
1854         
1855          // Ted Rigby's suggestion
1856          //if (n>=0) {             
1857          //  timestep = min(timestep, 0.8*(radii[k]+radii[n])/max_speed);
1858          //} else {
1859          //  timestep = min(timestep, radii[k]/max_speed);
1860          // }
1861         
1862         
1863          // Ole's modification
1864          //if (n>=0) {             
1865          //  timestep = min(timestep, max(radii[k],radii[n])/max_speed);
1866          //} else {
1867          //  timestep = min(timestep, radii[k]/max_speed);
1868          //}
1869         
1870         
1871        }
1872      }
1873     
1874    } // End edge i (and neighbour n)
1875   
1876   
1877    // Normalise triangle k by area and store for when all conserved
1878    // quantities get updated
1879    area = areas[k];
1880    stage_explicit_update[k] /= area;
1881    xmom_explicit_update[k] /= area;
1882    ymom_explicit_update[k] /= area;
1883   
1884   
1885    // Keep track of maximal speeds
1886    max_speed_array[k] = max_speed;
1887   
1888  } // End triangle k
1889       
1890                               
1891                               
1892  return timestep;
1893}
1894
1895
1896PyObject *compute_fluxes_ext_central(PyObject *self, PyObject *args) {
1897  /*Compute all fluxes and the timestep suitable for all volumes
1898    in domain.
1899
1900    Compute total flux for each conserved quantity using "flux_function_central"
1901
1902    Fluxes across each edge are scaled by edgelengths and summed up
1903    Resulting flux is then scaled by area and stored in
1904    explicit_update for each of the three conserved quantities
1905    stage, xmomentum and ymomentum
1906
1907    The maximal allowable speed computed by the flux_function for each volume
1908    is converted to a timestep that must not be exceeded. The minimum of
1909    those is computed as the next overall timestep.
1910
1911    Python call:
1912    domain.timestep = compute_fluxes(timestep,
1913                                     domain.epsilon,
1914                                     domain.H0,
1915                                     domain.g,
1916                                     domain.neighbours,
1917                                     domain.neighbour_edges,
1918                                     domain.normals,
1919                                     domain.edgelengths,
1920                                     domain.radii,
1921                                     domain.areas,
1922                                     tri_full_flag,
1923                                     Stage.edge_values,
1924                                     Xmom.edge_values,
1925                                     Ymom.edge_values,
1926                                     Bed.edge_values,
1927                                     Stage.boundary_values,
1928                                     Xmom.boundary_values,
1929                                     Ymom.boundary_values,
1930                                     Stage.explicit_update,
1931                                     Xmom.explicit_update,
1932                                     Ymom.explicit_update,
1933                                     already_computed_flux,
1934                                     optimise_dry_cells)                                     
1935
1936
1937    Post conditions:
1938      domain.explicit_update is reset to computed flux values
1939      domain.timestep is set to the largest step satisfying all volumes.
1940
1941
1942  */
1943
1944
1945  PyArrayObject *neighbours, *neighbour_edges,
1946    *normals, *edgelengths, *radii, *areas,
1947    *tri_full_flag,
1948    *stage_edge_values,
1949    *xmom_edge_values,
1950    *ymom_edge_values,
1951    *bed_edge_values,
1952    *stage_boundary_values,
1953    *xmom_boundary_values,
1954    *ymom_boundary_values,
1955    *stage_explicit_update,
1956    *xmom_explicit_update,
1957    *ymom_explicit_update,
1958    *already_computed_flux, //Tracks whether the flux across an edge has already been computed
1959    *max_speed_array; //Keeps track of max speeds for each triangle
1960
1961   
1962  double timestep, epsilon, H0, g;
1963  int optimise_dry_cells;
1964   
1965  // Convert Python arguments to C
1966  if (!PyArg_ParseTuple(args, "ddddOOOOOOOOOOOOOOOOOOOi",
1967                        &timestep,
1968                        &epsilon,
1969                        &H0,
1970                        &g,
1971                        &neighbours,
1972                        &neighbour_edges,
1973                        &normals,
1974                        &edgelengths, &radii, &areas,
1975                        &tri_full_flag,
1976                        &stage_edge_values,
1977                        &xmom_edge_values,
1978                        &ymom_edge_values,
1979                        &bed_edge_values,
1980                        &stage_boundary_values,
1981                        &xmom_boundary_values,
1982                        &ymom_boundary_values,
1983                        &stage_explicit_update,
1984                        &xmom_explicit_update,
1985                        &ymom_explicit_update,
1986                        &already_computed_flux,
1987                        &max_speed_array,
1988                        &optimise_dry_cells)) {
1989    PyErr_SetString(PyExc_RuntimeError, "Input arguments failed");
1990    return NULL;
1991  }
1992
1993 
1994  int number_of_elements = stage_edge_values -> dimensions[0];
1995
1996  // Call underlying flux computation routine and update
1997  // the explicit update arrays
1998  timestep = _compute_fluxes_central(number_of_elements,
1999                                     timestep,
2000                                     epsilon,
2001                                     H0,
2002                                     g,
2003                                     (long*) neighbours -> data,
2004                                     (long*) neighbour_edges -> data,
2005                                     (double*) normals -> data,
2006                                     (double*) edgelengths -> data, 
2007                                     (double*) radii -> data, 
2008                                     (double*) areas -> data,
2009                                     (long*) tri_full_flag -> data,
2010                                     (double*) stage_edge_values -> data,
2011                                     (double*) xmom_edge_values -> data,
2012                                     (double*) ymom_edge_values -> data,
2013                                     (double*) bed_edge_values -> data,
2014                                     (double*) stage_boundary_values -> data,
2015                                     (double*) xmom_boundary_values -> data,
2016                                     (double*) ymom_boundary_values -> data,
2017                                     (double*) stage_explicit_update -> data,
2018                                     (double*) xmom_explicit_update -> data,
2019                                     (double*) ymom_explicit_update -> data,
2020                                     (long*) already_computed_flux -> data,
2021                                     (double*) max_speed_array -> data,
2022                                     optimise_dry_cells);
2023 
2024  // Return updated flux timestep
2025  return Py_BuildValue("d", timestep);
2026}
2027
2028
2029
2030PyObject *compute_fluxes_ext_kinetic(PyObject *self, PyObject *args) {
2031  /*Compute all fluxes and the timestep suitable for all volumes
2032    in domain.
2033
2034    THIS IS AN EXPERIMENTAL FUNCTION - NORMALLY flux_function_central IS USED.
2035   
2036    Compute total flux for each conserved quantity using "flux_function_kinetic"
2037
2038    Fluxes across each edge are scaled by edgelengths and summed up
2039    Resulting flux is then scaled by area and stored in
2040    explicit_update for each of the three conserved quantities
2041    stage, xmomentum and ymomentum
2042
2043    The maximal allowable speed computed by the flux_function for each volume
2044    is converted to a timestep that must not be exceeded. The minimum of
2045    those is computed as the next overall timestep.
2046
2047    Python call:
2048    domain.timestep = compute_fluxes(timestep,
2049                                     domain.epsilon,
2050                                     domain.H0,
2051                                     domain.g,
2052                                     domain.neighbours,
2053                                     domain.neighbour_edges,
2054                                     domain.normals,
2055                                     domain.edgelengths,
2056                                     domain.radii,
2057                                     domain.areas,
2058                                     Stage.edge_values,
2059                                     Xmom.edge_values,
2060                                     Ymom.edge_values,
2061                                     Bed.edge_values,
2062                                     Stage.boundary_values,
2063                                     Xmom.boundary_values,
2064                                     Ymom.boundary_values,
2065                                     Stage.explicit_update,
2066                                     Xmom.explicit_update,
2067                                     Ymom.explicit_update,
2068                                     already_computed_flux)
2069
2070
2071    Post conditions:
2072      domain.explicit_update is reset to computed flux values
2073      domain.timestep is set to the largest step satisfying all volumes.
2074
2075
2076  */
2077
2078
2079  PyArrayObject *neighbours, *neighbour_edges,
2080    *normals, *edgelengths, *radii, *areas,
2081    *tri_full_flag,
2082    *stage_edge_values,
2083    *xmom_edge_values,
2084    *ymom_edge_values,
2085    *bed_edge_values,
2086    *stage_boundary_values,
2087    *xmom_boundary_values,
2088    *ymom_boundary_values,
2089    *stage_explicit_update,
2090    *xmom_explicit_update,
2091    *ymom_explicit_update,
2092    *already_computed_flux; // Tracks whether the flux across an edge has already been computed
2093
2094
2095  // Local variables
2096  double timestep, max_speed, epsilon, g, H0;
2097  double normal[2], ql[3], qr[3], zl, zr;
2098  double edgeflux[3]; //Work arrays for summing up fluxes
2099
2100  int number_of_elements, k, i, m, n;
2101  int ki, nm=0, ki2; //Index shorthands
2102  static long call=1;
2103
2104
2105  // Convert Python arguments to C
2106  if (!PyArg_ParseTuple(args, "ddddOOOOOOOOOOOOOOOOOO",
2107                        &timestep,
2108                        &epsilon,
2109                        &H0,
2110                        &g,
2111                        &neighbours,
2112                        &neighbour_edges,
2113                        &normals,
2114                        &edgelengths, &radii, &areas,
2115                        &tri_full_flag,
2116                        &stage_edge_values,
2117                        &xmom_edge_values,
2118                        &ymom_edge_values,
2119                        &bed_edge_values,
2120                        &stage_boundary_values,
2121                        &xmom_boundary_values,
2122                        &ymom_boundary_values,
2123                        &stage_explicit_update,
2124                        &xmom_explicit_update,
2125                        &ymom_explicit_update,
2126                        &already_computed_flux)) {
2127    PyErr_SetString(PyExc_RuntimeError, "Input arguments failed");
2128    return NULL;
2129  }
2130  number_of_elements = stage_edge_values -> dimensions[0];
2131  call++;//a static local variable to which already_computed_flux is compared
2132  //set explicit_update to zero for all conserved_quantities.
2133  //This assumes compute_fluxes called before forcing terms
2134  for (k=0; k<number_of_elements; k++) {
2135    ((double *) stage_explicit_update -> data)[k]=0.0;
2136    ((double *) xmom_explicit_update -> data)[k]=0.0;
2137    ((double *) ymom_explicit_update -> data)[k]=0.0;
2138  }
2139  //Loop through neighbours and compute edge flux for each
2140  for (k=0; k<number_of_elements; k++) {
2141    for (i=0; i<3; i++) {
2142      ki = k*3+i;
2143      if (((long *) already_computed_flux->data)[ki]==call)//we've already computed the flux across this edge
2144        continue;
2145      ql[0] = ((double *) stage_edge_values -> data)[ki];
2146      ql[1] = ((double *) xmom_edge_values -> data)[ki];
2147      ql[2] = ((double *) ymom_edge_values -> data)[ki];
2148      zl =    ((double *) bed_edge_values -> data)[ki];
2149
2150      //Quantities at neighbour on nearest face
2151      n = ((long *) neighbours -> data)[ki];
2152      if (n < 0) {
2153        m = -n-1; //Convert negative flag to index
2154        qr[0] = ((double *) stage_boundary_values -> data)[m];
2155        qr[1] = ((double *) xmom_boundary_values -> data)[m];
2156        qr[2] = ((double *) ymom_boundary_values -> data)[m];
2157        zr = zl; //Extend bed elevation to boundary
2158      } else {
2159        m = ((long *) neighbour_edges -> data)[ki];
2160        nm = n*3+m;
2161        qr[0] = ((double *) stage_edge_values -> data)[nm];
2162        qr[1] = ((double *) xmom_edge_values -> data)[nm];
2163        qr[2] = ((double *) ymom_edge_values -> data)[nm];
2164        zr =    ((double *) bed_edge_values -> data)[nm];
2165      }
2166      // Outward pointing normal vector
2167      // normal = domain.normals[k, 2*i:2*i+2]
2168      ki2 = 2*ki; //k*6 + i*2
2169      normal[0] = ((double *) normals -> data)[ki2];
2170      normal[1] = ((double *) normals -> data)[ki2+1];
2171      //Edge flux computation
2172      flux_function_kinetic(ql, qr, zl, zr,
2173                    normal[0], normal[1],
2174                    epsilon, H0, g,
2175                    edgeflux, &max_speed);
2176      //update triangle k
2177      ((long *) already_computed_flux->data)[ki]=call;
2178      ((double *) stage_explicit_update -> data)[k] -= edgeflux[0]*((double *) edgelengths -> data)[ki];
2179      ((double *) xmom_explicit_update -> data)[k] -= edgeflux[1]*((double *) edgelengths -> data)[ki];
2180      ((double *) ymom_explicit_update -> data)[k] -= edgeflux[2]*((double *) edgelengths -> data)[ki];
2181      //update the neighbour n
2182      if (n>=0){
2183        ((long *) already_computed_flux->data)[nm]=call;
2184        ((double *) stage_explicit_update -> data)[n] += edgeflux[0]*((double *) edgelengths -> data)[nm];
2185        ((double *) xmom_explicit_update -> data)[n] += edgeflux[1]*((double *) edgelengths -> data)[nm];
2186        ((double *) ymom_explicit_update -> data)[n] += edgeflux[2]*((double *) edgelengths -> data)[nm];
2187      }
2188      ///for (j=0; j<3; j++) {
2189        ///flux[j] -= edgeflux[j]*((double *) edgelengths -> data)[ki];
2190        ///}
2191        //Update timestep
2192        //timestep = min(timestep, domain.radii[k]/max_speed)
2193        //FIXME: SR Add parameter for CFL condition
2194    if ( ((long *) tri_full_flag -> data)[k] == 1) {
2195            if (max_speed > epsilon) {
2196                timestep = min(timestep, ((double *) radii -> data)[k]/max_speed);
2197                //maxspeed in flux_function is calculated as max(|u+a|,|u-a|)
2198                if (n>=0)
2199                    timestep = min(timestep, ((double *) radii -> data)[n]/max_speed);
2200            }
2201    }
2202    } // end for i
2203    //Normalise by area and store for when all conserved
2204    //quantities get updated
2205    ((double *) stage_explicit_update -> data)[k] /= ((double *) areas -> data)[k];
2206    ((double *) xmom_explicit_update -> data)[k] /= ((double *) areas -> data)[k];
2207    ((double *) ymom_explicit_update -> data)[k] /= ((double *) areas -> data)[k];
2208  } //end for k
2209  return Py_BuildValue("d", timestep);
2210}
2211
2212PyObject *protect(PyObject *self, PyObject *args) {
2213  //
2214  //    protect(minimum_allowed_height, maximum_allowed_speed, wc, zc, xmomc, ymomc)
2215
2216
2217  PyArrayObject
2218  *wc,            //Stage at centroids
2219  *zc,            //Elevation at centroids
2220  *xmomc,         //Momentums at centroids
2221  *ymomc;
2222
2223
2224  int N;
2225  double minimum_allowed_height, maximum_allowed_speed, epsilon;
2226
2227  // Convert Python arguments to C
2228  if (!PyArg_ParseTuple(args, "dddOOOO",
2229                        &minimum_allowed_height,
2230                        &maximum_allowed_speed,
2231                        &epsilon,
2232                        &wc, &zc, &xmomc, &ymomc)) {
2233    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: protect could not parse input arguments");
2234    return NULL;
2235  } 
2236
2237  N = wc -> dimensions[0];
2238
2239  _protect(N,
2240           minimum_allowed_height,
2241           maximum_allowed_speed,
2242           epsilon,
2243           (double*) wc -> data,
2244           (double*) zc -> data,
2245           (double*) xmomc -> data,
2246           (double*) ymomc -> data);
2247
2248  return Py_BuildValue("");
2249}
2250
2251
2252
2253PyObject *balance_deep_and_shallow(PyObject *self, PyObject *args) {
2254  // Compute linear combination between stage as computed by
2255  // gradient-limiters limiting using w, and stage computed by
2256  // gradient-limiters limiting using h (h-limiter).
2257  // The former takes precedence when heights are large compared to the
2258  // bed slope while the latter takes precedence when heights are
2259  // relatively small.  Anything in between is computed as a balanced
2260  // linear combination in order to avoid numerical disturbances which
2261  // would otherwise appear as a result of hard switching between
2262  // modes.
2263  //
2264  //    balance_deep_and_shallow(beta_h, wc, zc, wv, zv,
2265  //                             xmomc, ymomc, xmomv, ymomv)
2266
2267
2268  PyArrayObject
2269    *wc,            //Stage at centroids
2270    *zc,            //Elevation at centroids
2271    *wv,            //Stage at vertices
2272    *zv,            //Elevation at vertices
2273    *hvbar,         //h-Limited depths at vertices
2274    *xmomc,         //Momentums at centroids and vertices
2275    *ymomc,
2276    *xmomv,
2277    *ymomv;
2278 
2279  PyObject *domain, *Tmp;
2280   
2281  double alpha_balance = 2.0;
2282  double H0, beta_h;
2283
2284  int N, tight_slope_limiters; //, err;
2285
2286  // Convert Python arguments to C
2287  if (!PyArg_ParseTuple(args, "OdOOOOOOOOO",
2288                        &domain,
2289                        &beta_h,
2290                        &wc, &zc, 
2291                        &wv, &zv, &hvbar,
2292                        &xmomc, &ymomc, &xmomv, &ymomv)) {
2293    PyErr_SetString(PyExc_RuntimeError, 
2294                    "shallow_water_ext.c: balance_deep_and_shallow could not parse input arguments");
2295    return NULL;
2296  } 
2297         
2298         
2299  // FIXME (Ole): I tested this without GetAttrString and got time down
2300  // marginally from 4.0s to 3.8s. Consider passing everything in
2301  // through ParseTuple and profile.
2302 
2303  // Pull out parameters
2304  Tmp = PyObject_GetAttrString(domain, "alpha_balance");
2305  if (!Tmp) {
2306    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: balance_deep_and_shallow could not obtain object alpha_balance from domain");
2307    return NULL;
2308  } 
2309  alpha_balance = PyFloat_AsDouble(Tmp);
2310  Py_DECREF(Tmp);
2311
2312 
2313  Tmp = PyObject_GetAttrString(domain, "H0");
2314  if (!Tmp) {
2315    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: balance_deep_and_shallow could not obtain object H0 from domain");
2316    return NULL;
2317  } 
2318  H0 = PyFloat_AsDouble(Tmp);
2319  Py_DECREF(Tmp);
2320
2321 
2322  Tmp = PyObject_GetAttrString(domain, "tight_slope_limiters");
2323  if (!Tmp) {
2324    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: balance_deep_and_shallow could not obtain object tight_slope_limiters from domain");
2325    return NULL;
2326  } 
2327  tight_slope_limiters = PyInt_AsLong(Tmp);
2328  Py_DECREF(Tmp);
2329 
2330 
2331  N = wc -> dimensions[0];
2332  _balance_deep_and_shallow(N,
2333                            beta_h,
2334                            (double*) wc -> data,
2335                            (double*) zc -> data,
2336                            (double*) wv -> data,
2337                            (double*) zv -> data,
2338                            (double*) hvbar -> data,
2339                            (double*) xmomc -> data,
2340                            (double*) ymomc -> data,
2341                            (double*) xmomv -> data,
2342                            (double*) ymomv -> data,
2343                            H0,
2344                            (int) tight_slope_limiters,
2345                            alpha_balance);
2346
2347
2348  return Py_BuildValue("");
2349}
2350
2351
2352
2353PyObject *h_limiter(PyObject *self, PyObject *args) {
2354
2355  PyObject *domain, *Tmp;
2356  PyArrayObject
2357    *hv, *hc, //Depth at vertices and centroids
2358    *hvbar,   //Limited depth at vertices (return values)
2359    *neighbours;
2360
2361  int k, i, n, N, k3;
2362  int dimensions[2];
2363  double beta_h; // Safety factor (see config.py)
2364  double *hmin, *hmax, hn;
2365
2366  // Convert Python arguments to C
2367  if (!PyArg_ParseTuple(args, "OOO", &domain, &hc, &hv)) {
2368    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: h_limiter could not parse input arguments");
2369    return NULL;
2370  } 
2371 
2372  neighbours = get_consecutive_array(domain, "neighbours");
2373
2374  //Get safety factor beta_h
2375  Tmp = PyObject_GetAttrString(domain, "beta_h");
2376  if (!Tmp) {
2377    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: h_limiter could not obtain object beta_h from domain");
2378    return NULL;
2379  } 
2380  beta_h = PyFloat_AsDouble(Tmp);
2381
2382  Py_DECREF(Tmp);
2383
2384  N = hc -> dimensions[0];
2385
2386  //Create hvbar
2387  dimensions[0] = N;
2388  dimensions[1] = 3;
2389  hvbar = (PyArrayObject *) PyArray_FromDims(2, dimensions, PyArray_DOUBLE);
2390
2391
2392  //Find min and max of this and neighbour's centroid values
2393  hmin = malloc(N * sizeof(double));
2394  hmax = malloc(N * sizeof(double));
2395  for (k=0; k<N; k++) {
2396    k3=k*3;
2397
2398    hmin[k] = ((double*) hc -> data)[k];
2399    hmax[k] = hmin[k];
2400
2401    for (i=0; i<3; i++) {
2402      n = ((long*) neighbours -> data)[k3+i];
2403
2404      // Initialise hvbar with values from hv
2405      ((double*) hvbar -> data)[k3+i] = ((double*) hv -> data)[k3+i];
2406
2407      if (n >= 0) {
2408        hn = ((double*) hc -> data)[n]; // Neighbour's centroid value
2409
2410        hmin[k] = min(hmin[k], hn);
2411        hmax[k] = max(hmax[k], hn);
2412      }
2413    }
2414  }
2415
2416  // Call underlying standard routine
2417  _limit_old(N, beta_h, (double*) hc -> data, (double*) hvbar -> data, hmin, hmax);
2418
2419  // // //Py_DECREF(domain); //FIXME: Necessary?
2420  free(hmin);
2421  free(hmax);
2422
2423  // Return result using PyArray to avoid memory leak
2424  return PyArray_Return(hvbar);
2425}
2426
2427PyObject *h_limiter_sw(PyObject *self, PyObject *args) {
2428  //a faster version of h_limiter above
2429  PyObject *domain, *Tmp;
2430  PyArrayObject
2431    *hv, *hc, //Depth at vertices and centroids
2432    *hvbar,   //Limited depth at vertices (return values)
2433    *neighbours;
2434
2435  int k, i, N, k3,k0,k1,k2;
2436  int dimensions[2];
2437  double beta_h; //Safety factor (see config.py)
2438  double hmin, hmax, dh[3];
2439// Convert Python arguments to C
2440  if (!PyArg_ParseTuple(args, "OOO", &domain, &hc, &hv)) {
2441    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: h_limiter_sw could not parse input arguments");
2442    return NULL;
2443  } 
2444  neighbours = get_consecutive_array(domain, "neighbours");
2445
2446  //Get safety factor beta_h
2447  Tmp = PyObject_GetAttrString(domain, "beta_h");
2448  if (!Tmp) {
2449    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: h_limiter_sw could not obtain object beta_h from domain");
2450    return NULL;
2451  } 
2452  beta_h = PyFloat_AsDouble(Tmp);
2453
2454  Py_DECREF(Tmp);
2455
2456  N = hc -> dimensions[0];
2457
2458  //Create hvbar
2459  dimensions[0] = N;
2460  dimensions[1] = 3;
2461  hvbar = (PyArrayObject *) PyArray_FromDims(2, dimensions, PyArray_DOUBLE);
2462  for (k=0;k<N;k++){
2463    k3=k*3;
2464    //get the ids of the neighbours
2465    k0 = ((long*) neighbours -> data)[k3];
2466    k1 = ((long*) neighbours -> data)[k3+1];
2467    k2 = ((long*) neighbours -> data)[k3+2];
2468    //set hvbar provisionally
2469    for (i=0;i<3;i++){
2470      ((double*) hvbar -> data)[k3+i] = ((double*) hv -> data)[k3+i];
2471      dh[i]=((double*) hvbar -> data)[k3+i]-((double*) hc -> data)[k];
2472    }
2473    hmin=((double*) hc -> data)[k];
2474    hmax=hmin;
2475    if (k0>=0){
2476      hmin=min(hmin,((double*) hc -> data)[k0]);
2477      hmax=max(hmax,((double*) hc -> data)[k0]);
2478    }
2479    if (k1>=0){
2480      hmin=min(hmin,((double*) hc -> data)[k1]);
2481      hmax=max(hmax,((double*) hc -> data)[k1]);
2482    }
2483    if (k2>=0){
2484      hmin=min(hmin,((double*) hc -> data)[k2]);
2485      hmax=max(hmax,((double*) hc -> data)[k2]);
2486    }
2487    hmin-=((double*) hc -> data)[k];
2488    hmax-=((double*) hc -> data)[k];
2489    limit_gradient(dh,hmin,hmax,beta_h);
2490    for (i=0;i<3;i++)
2491      ((double*) hvbar -> data)[k3+i] = ((double*) hc -> data)[k]+dh[i];
2492  }
2493  return PyArray_Return(hvbar);
2494}
2495
2496PyObject *assign_windfield_values(PyObject *self, PyObject *args) {
2497  //
2498  //      assign_windfield_values(xmom_update, ymom_update,
2499  //                              s_vec, phi_vec, self.const)
2500
2501
2502
2503  PyArrayObject   //(one element per triangle)
2504  *s_vec,         //Speeds
2505  *phi_vec,       //Bearings
2506  *xmom_update,   //Momentum updates
2507  *ymom_update;
2508
2509
2510  int N;
2511  double cw;
2512
2513  // Convert Python arguments to C
2514  if (!PyArg_ParseTuple(args, "OOOOd",
2515                        &xmom_update,
2516                        &ymom_update,
2517                        &s_vec, &phi_vec,
2518                        &cw)) {
2519    PyErr_SetString(PyExc_RuntimeError, "shallow_water_ext.c: assign_windfield_values could not parse input arguments");
2520    return NULL;
2521  } 
2522                       
2523
2524  N = xmom_update -> dimensions[0];
2525
2526  _assign_wind_field_values(N,
2527           (double*) xmom_update -> data,
2528           (double*) ymom_update -> data,
2529           (double*) s_vec -> data,
2530           (double*) phi_vec -> data,
2531           cw);
2532
2533  return Py_BuildValue("");
2534}
2535
2536
2537
2538
2539//-------------------------------
2540// Method table for python module
2541//-------------------------------
2542static struct PyMethodDef MethodTable[] = {
2543  /* The cast of the function is necessary since PyCFunction values
2544   * only take two PyObject* parameters, and rotate() takes
2545   * three.
2546   */
2547
2548  {"rotate", (PyCFunction)rotate, METH_VARARGS | METH_KEYWORDS, "Print out"},
2549  {"extrapolate_second_order_sw", extrapolate_second_order_sw, METH_VARARGS, "Print out"},
2550  {"compute_fluxes_ext_central", compute_fluxes_ext_central, METH_VARARGS, "Print out"},
2551  {"compute_fluxes_ext_kinetic", compute_fluxes_ext_kinetic, METH_VARARGS, "Print out"},
2552  {"gravity", gravity, METH_VARARGS, "Print out"},
2553  {"manning_friction", manning_friction, METH_VARARGS, "Print out"},
2554  {"flux_function_central", flux_function_central, METH_VARARGS, "Print out"}, 
2555  {"balance_deep_and_shallow", balance_deep_and_shallow,
2556   METH_VARARGS, "Print out"},
2557  {"h_limiter", h_limiter,
2558   METH_VARARGS, "Print out"},
2559  {"h_limiter_sw", h_limiter_sw,
2560   METH_VARARGS, "Print out"},
2561  {"protect", protect, METH_VARARGS | METH_KEYWORDS, "Print out"},
2562  {"assign_windfield_values", assign_windfield_values,
2563   METH_VARARGS | METH_KEYWORDS, "Print out"},
2564  {NULL, NULL}
2565};
2566
2567// Module initialisation
2568void initshallow_water_ext(void){
2569  Py_InitModule("shallow_water_ext", MethodTable);
2570
2571  import_array(); // Necessary for handling of NumPY structures
2572}
Note: See TracBrowser for help on using the repository browser.