sandbox/geoffroy/dev/discharge.h
Discharge.h
We define several functions in order to impose a flux at boundaries. The discharge function can work with adaptative refinement. We first define the scalar river[] as a global scalar.
scalar river[];
Adaptation
When using an adaptive discretisation, we define the refinement laws for the scalar river[]. The refine_injection function give to each child the parent value. The coarsen_max function fix the value of the parent as the maximum value of all the children.
#if QUADTREE
static void coarsen_max (Point point, scalar s){
s[] = max(max(fine(s,0,0),fine(s,1,0)),max(fine(s,0,1),fine(s,1,1)));
}
event defaults( i = 0 ){
river.refine = river.prolongation = refine_injection;
river.coarsen = coarsen_max;
}
#endif
Boundary structure “defb”
This structure stores all we need in order to define the boundary : a double value which test the location of the scalar river[], two doubles “xb” and “yb” to store the x and y coordinates of the boundary, “dx” equal to the minimum spatial resolution, “zmin” which store the minimum topography elevation on the boundary, “h0” the water elevation on zmin, “lim” the limit of the boundary and an integer “kw” which stores the relative location of the boundary (top, bottom, left and right).
typedef struct {
double value, xb, yb, dx, zmin, h0, lim;
int kw;
} defb;
Structure definition
We allocate the right values to the “defb” structure
static defb defbf (int keyw, double value){
defb b;
b.dx = L0/N;
b.value = value;
b.kw = keyw;
b.xb = X0 + 0.5 * b.dx;
b.yb = Y0 + 0.5 * b.dx;
b.zmin= 1e40;
b.h0 = 0;
if( keyw == right ) b.xb = X0 + L0 - 0.5 * b.dx;
else if( keyw == top ) b.yb = X0 + L0 - 0.5 * b.dx;
else if( keyw != left && keyw != bottom ){
fprintf(stderr,"wrong keyword in discharge function\n");
assert (false);
}
if (b.kw == bottom || b.kw == top)
b.lim = X0 + L0;
else
b.lim = Y0 + L0;
return b;
}
Altitude
The altitude function find the minimum value of the topography and the water depth at this location.
static void altitude (defb * b) {
double ztemp = b->zmin, htemp = 0, x = b->xb, y = b->yb;
# if _OPENMP
# pragma omp parallel for reduction(min:ztemp)
# endif
for (int j = 0; j < N-1; j++) {
if( b->kw == top || b->kw == bottom ) x = b->xb + j * b->dx;
else y = b->yb + j * b->dx;
Point point = locate (x,y);
if (zb[] < ztemp && river[] == b->value) {
ztemp = zb[];
htemp = h[];
}
}
b->zmin = ztemp;
b->h0 = htemp;
}
Real flux
This function return the exact incoming flow on the boundary for an imposed water depth.
static double realflux (defb b, double eta_imp) {
double q = 0, deltax = b.dx , xp = b.xb, yp = b.yb, dtmax = 1;
double bx;
do {
Point point = locate (xp, yp);
if (river[] == b.value) {
# if QUADTREE
// Relocate at the center of the element only on the sliding coordinate
if(b.kw == bottom || b.kw == top ) xp = x;
else yp = y;
// To not miss refined elements
deltax = 0.75*Delta;
# endif
// Take care of the sign of velocity
double ub;
if (b.kw == bottom || b.kw == top)
ub = b.kw == bottom ? u.y[] : - u.y[];
else
ub = b.kw == left ? u.x[] : - u.x[];
// computing the local numerical flux
double hn = max (eta_imp - zb[], 0);
if (h[] > dry || hn > dry) {
double fh, fu;
kurganov (hn, max(h[],0), ub, ub, b.dx, &fh, &fu, &dtmax);
q += fh*b.dx;
}
}
// Testing the boundary limit and advancing the local point
if (b.kw == bottom || b.kw == top) {
xp += deltax;
bx = xp;
}
else {
yp += deltax;
bx = yp;
}
} while( bx < b.lim );
return q;
}
Double false position method
Here, we find the water depth corresponding to the imposed inflow by the false position method. This method is a root-finding method of type “guess and check”. In our case, we want to solve the equation :
\displaystyle flux(\eta) - q_{imp}=0
From wikipedia : “The method proceeds by producing a sequence of shrinking intervals [ak, bk] that all contain a root of f.(…) At each step, the method compute ck which is the root of the secant line through (ak, f(ak)) and (bk, f(bk)). If f(ak) and f(ck) have the same sign, then we set ak+1 = ck and bk+1 = bk, otherwise we set ak+1 = ak and bk+1 = ck. This process is repeated until the root is approximated sufficiently well.”
static double metfalsepos (defb bo, double prec, double q_imp) {
double binf, bsup, qinf, newb, qsup;
double eta0 = bo.zmin + bo.h0;
First, we test the inflow at the precedent time step.
double newq = realflux (bo, eta0);
if (fabs((newq - q_imp)/q_imp) <= prec)
return eta0;
Then, we find an inferior and a superior limit around the solution.
// Find inferior limit
if (newq > q_imp) {
bsup = eta0;
qsup = newq;
binf = bo.zmin;
qinf = 0;
}
else {
// We must take care of the case h0=0
if (bo.h0 <= dry) {
bo.h0 = 1;
bsup = bo.zmin;
}
else bsup = eta0;
do {
// Find a "good" superior limit
binf = bsup;
bsup = bsup + bo.h0;
qsup = realflux (bo, bsup);
} while (qsup <= q_imp);
qinf = realflux (bo, binf);
}
// The false position method
do {
double alpha = (qsup - qinf)/(bsup - binf); // Define the slope
newb = binf + (q_imp - qinf)/alpha; // new bound
newq = realflux (bo, newb); // new total inflow
if (newq > q_imp) { //test the new solution & replacing the good limit
bsup = newb;
qsup = newq;
}
else {
binf = newb;
qinf = newq;
}
} while (fabs((newq - q_imp)/q_imp) >= prec);
return newb;
}
The discharge routine
double discharge (double q_imp, int keyw, double value)
{
// Inialisation of the Defb structure
defb bo = defbf(keyw, value);
// Fix zmin et h
altitude (&bo);
// If imposed inflow <= 0
if (q_imp <= 0) return bo.zmin - 0.0001;
// Return eta found by the false position method
return metfalsepos (bo, 0.001, q_imp);
}
Hydrograph
This function return the inflow by linearly interpolating the data.
double hydrograph (const char * name, double mult)
{
FILE * fp;
if ((fp = fopen (name , "r")) == NULL) {
fprintf (stderr,"cannot open hydrograph data file.\n name = %s ",name);
assert (false);
}
double time = 0, timea, q = 0, qa, alpha = 0;
// We read the data at each call => Can definitively be optimised !
do {
qa = q;
timea = time;
if (fscanf (fp, "%lf \t %lf \n", &time, &q) == EOF) break;
time *= mult;
if (time - timea != 0) alpha = (q - qa)/(time - timea);
else alpha = 0;
} while (time < t);
fclose (fp);
/*
If the solver time is sup to the larger time of the data, return the last value */
if (timea >= t) return q;
// Else, return the interpolation
else return alpha*(t - timea) + qa;
}