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

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