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

Last change on this file since 7097 was 7097, checked in by ole, 14 years ago

Played with inlining and profiled, but no speedup was achieved, so I rolled it back and commented.

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