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

Last change on this file since 6840 was 6840, checked in by nariman, 15 years ago

Optimisation of _extrapolate_second_order_sw and limit_gradient.

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