sandbox/geoffroy/testcase/morerivers.c
A more real case for imposing flow rates
In this example, we use the discharge() function in order to impose different flow rates on different rivers situated on the same boundary of a Saint-Venant simulation. Wa also use adaptative quadtrees.
#include "saint-venant.h"
#include "discharge.h"
The domain is 400 metres squared, centered on the origin. Time is in seconds. The minimum level of refinement is 2^4 and the maximum one is 2^8. We fix the maximum error on the water elevation to a low value (1e^{-3}). Therefore, the grid is refined to its maximum value where it has presence of fluid.
#define MINLEVEL 4
#define MAXLEVEL 8
#define HMAXE 1e-3
int main(){
L0 = 20.;
X0 = - L0/2.;
Y0 = - L0/2.;
G = 9.81;
N = 1 << MAXLEVEL;
run();
}
Initial conditions
We chose a complicated river bed with height “valleys” flowing in four lakes (see figure below). We set the field river[] with the values 1 and 2 in order to distinguish the rivers on the same boundary.
We start with a dry riverbed, so that the problem does not have a natural timescale the Saint-Venant solver can use. We set a maximum timestep to set this timescale.
DT = 1e-2;
foreach() {
We define a topography with two valleys at each side of the domain and one lake at each quarter of the map. See the map below.
zb[] = 5e-4*(x*y*x*y - x*x - y*y + 2000*(cos(x/10.*2*3.14)+cos(y/10.*2*3.14)));
reset
set isosample 20,20
set pm3d depthorder
f(x,y) = 5e-4*(x*y*x*y - x*x - y*y + 2000*(cos(x/10.*2*3.14)+cos(y/10.*2*3.14)))
splot [-10:10][-10:10] f(x,y) with pm3d t'topography'
The scalar river[] is defined by default in the discharge.h library. We use it to store the location of each rivers. Its value is 1 for the river at north-east and south-west of the map and 2 for the rivers at north-west and south-east.
expected expression before ‘]’ token
river[] = x*y > 0 ? 1 : 2;
reset
set isosample 20,20
set pm3d map
f(x,y) = (x*y < 0)+1
splot [-10:10][-10:10] f(x,y) t'river[]'
}
boundary ({zb, river});
}
Adaptation
We use the adap_wavelet function to adapt the grid only on the wet cells. You can find a full explaination of this process in the tsunami example
void adapt_H() {
#if QUADTREE
scalar h1[];
foreach(){
h1[] = h[];
}
boundary ({h1});
adapt_wavelet ({h1}, (double[]){HMAXE} ,MAXLEVEL,MINLEVEL);
#endif
}
We refine at each time step.
Boundary conditions
We impose inflow on all boundaries. In addition, on each boundary, the tangential velocity is set to zero and a neumann condition is fixed on the normal velocity.
u.n[top] = neumann(0);
u.t[top] = dirichlet(0);
u.n[bottom] = neumann(0);
u.t[bottom] = dirichlet(0);
u.n[right] = neumann(0);
u.t[right] = dirichlet(0);
u.n[left] = neumann(0);
u.t[left] = dirichlet(0);
We impose different flow rates : 1 m^3.s^{-1} for the rivers in the north-east quarter, 1.5 m^3.s^{-1} for the ones at north-west, 2 m^3.s^{-1} for the ones at south-west, and 0 at south-east. The process is more detailed in the multi-river test case.
eta1 = discharge (0.5, top, 1); // N-E
expected expression before ‘]’ token
eta2 = discharge (1, top, 2); // N-W
expected expression before ‘]’ token
h[top] = max ( (river[] == 1 ? eta1 : eta2) - zb[] , 0) ;
eta[top] = max ((river[] == 1 ? eta1 : eta2), zb[]);
// Left boundary
eta4 = discharge (1.5, left, 1); // S-W
expected expression before ‘]’ token
eta3 = discharge (1, left, 2); // N-W
expected expression before ‘]’ token
h[left] = max ((river[] == 1 ? eta4 : eta3) - zb[] , 0);
eta[left] = max ((river[] == 1 ? eta4 : eta3), zb[]);
// Bottom boundary
expected expression before ‘]’ token
eta5 = discharge (1.5, bottom, 1); //S-W
expected expression before ‘]’ token
h[bottom] = max ((river[] == 1 ? eta5 : zb[]) - zb[] , 0);
eta[bottom] = max ((river[] == 1 ? eta5 : zb[]), zb[]);
// Right boundary
expected expression before ‘]’ token
eta6 = discharge (0.5, right, 1); //N-E
expected expression before ‘]’ token
h[right] = max ((river[] == 1 ? eta6 : zb[]) - zb[] , 0);
eta[right] = max ((river[] == 1 ? eta6 : zb[]), zb[]);
}
Outputs
We compute the evolution of the water volumes in each quarter of the map.
event volume (i += 10) {
double volume1 = 0, volume2 = 0, volume3 = 0, volume4 = 0;
foreach() {
double dv = h[]*sq(Delta);
if (y > 0 && x > 0)
volume1 += dv; // N-E
else if(y > 0 && x < 0 )
volume2 += dv; // N-W
else if( y < 0 && x < 0 )
volume3 += dv; // S-W
else volume4 += dv; // S-E-
}
fprintf (stderr, "%g %g %g %g %g\n",
t, volume1, volume2, volume3, volume4);
}
We use gnuplot to produce an animation of the water surface.
event animation (t <= 1; t += 0.01) {
double dx = 2.*L0/(1 << MAXLEVEL), dy = dx;
printf ("set title 't = %.3f'\n"
"sp [%g:%g][%g:%g][-5:5] '-'"
" u 1:2:($3+$4-.05) t 'free surface' w l lt 3,"
" '' u 1:2:4 t 'topography' w l lt 2\n",
t, X0, -X0, Y0, -Y0);
for (double x = X0; x <= X0 + L0; x += dx) {
for (double y = Y0; y <= Y0 + L0; y += dy)
printf ("%g %g %g %g\n",
x, y, interpolate (h, x, y), interpolate (zb, x, y));
putchar ('\n');
}
printf ("e\n"
"pause %.5lf \n\n", 0.);
}
We record a movie for the field level which is storing the level of refinement.
event movies (t+=0.01) {
static FILE * fp1 = popen ("ppm2mpeg > level.mpg", "w");
scalar l[];
foreach()
l[] = level;
output_ppm (l, fp1, min = MINLEVEL, max = MAXLEVEL, n = 512);
}
Results
reset
set key top left
set xlabel 'Time'
set ylabel 'Volume'
f(x)=a*x + b
fit [0.1:0.8] f(x) './log' u 1:2 via a,b
g(x)=c*x + d
fit [0.1:0.8] g(x) './log' u 1:3 via c,d
h(x)=k*x + l
fit [0.1:0.8] h(x) './log' u 1:4 via k,l
i(x)=m*x
fit [0.1:0.8] i(x) './log' u 1:5 via m
title1=sprintf("f(x) = %1.3f*t + %1.3f", a,b)
title2=sprintf("g(x) = %1.3f*t + %1.3f", c,d)
title3=sprintf("h(x) = %1.3f*t + %1.3f", k,l)
title4=sprintf("i(x) = %1.3f*t", m)
plot [0:0.8] './log' u 1:2 t 'North-east (q=1)', \
f(x) t title1, \
'./log' u 1:3 t 'North-west (q=2)', \
g(x) t title2, \
'./log' u 1:4 t 'South-west (q=3)', \
h(x) t title3, \
'./log' u 1:5 t 'South-east (q=0)', \
i(x) t title4
reset
set view 40,15
set xlabel 'x'
set ylabel 'y'
set hidden3d
set term gif animate
set output 'movie.gif'
load './out'