sandbox/Antoonvh/lumpedrob.c
A Simple surface energy budget boundary condition
For an atmospheric medium with a viscosity \nu, the buoyancy budget at the surface may read,
\displaystyle G + B = Q_n,
With G, the soil flux, expressed using a lumped-parameter (i.e. a linear feedback) model:
\displaystyle G = \Lambda (b_{\mathrm{surf}} - b_0).
with b_0 a reference buoyancy value and \Lambda the lumped soil conduction parameter. For the sensible buoyancy flux B, per definition,
\displaystyle B = \kappa \frac{\partial b}{\partial \mathbf{n}},
with \kappa the heat conductivity of the atmospheric medium. Finally, for a simple diurnal cycle with period T, Q_n is written as,
\displaystyle Q_n = \mathrm{max} \left[ B_0\ \mathrm{sin}\left( \frac{2\pi t}{T} \right),\ B_1 \right],
with B_0 and B_1 buoyancy flux scales. Giving a Robin boundary condtion for b at the bottom surface,
\displaystyle \Lambda b_{\mathrm{surf}} + \kappa \frac{\partial b}{\partial \mathbf{n}} = Q_n + \Lambda b_0.
Initially, the atmospheric fluid is at rest with a constant (vertical y) stable stratification:
\displaystyle b(t=0) = b_0 + N^2y,
From the 7 system parameters (in order of appearance: \{ \nu, \Lambda, \kappa, B_0, T, B_1, N\}), we can construct 5 dimensionless parameters:
\displaystyle \Pi_1 = \frac{B_0}{B_1} \approx -6, \displaystyle \Pi_2 = TN \approx 2000 \rightarrow 500, \displaystyle \Pi_4 = \frac{\sqrt{B_0T}}{\Lambda} \approx 5000 \rightarrow 2500, \displaystyle Pr = \frac{\nu}{\kappa} \approx 1, \displaystyle Re = \frac{B_0}{\nu} \left( \frac{2T}{\pi N^2} \right) ^{2/3} \approx 10^8 \rightarrow 5000.
See Van Hooft et al (2019) for details.
Results
#include "navier-stokes/centered.h"
#include "tracer.h"
#include "diffusion.h"
#define robin(a,b,c) ((dirichlet ((c)*Delta/(2*(b) + (a)*Delta))) \
+ ((neumann (0))* \
((2*(b) - (a)*Delta)/(2*(b) + (a)*Delta) + 1.)))
#define QN (max(B0*sin(2*pi*t/T), B1))
double T = 24*60, Lc = 1;
double Pi1 = -6, Pi2 = 500, Pi4 = 2500, Re = 5000;
double LAMBDA, B0, B1, sqN, NU;
int maxlevel = 8;
scalar b[], * tracers = {b};
b[bottom] = robin (LAMBDA, NU, QN);
b[top] = dirichlet (sqN*y);
FILE * gp;
face vector av[];
int main() {
sqN = sq(Pi2/T);
B0 = sq(Lc)*sqN*pi/(2.*T);
B1 = B0/Pi1;
LAMBDA = sqrt(B0*T)/Pi4;
NU = B0*pow(2*T/(pi*sqN), 2./3.)/Re;
L0 = 3*Lc;
X0 = -L0/2;
periodic (left);
u.t[bottom] = dirichlet (0.);
#if (dimension == 3)
u.r[bottom] = dirichlet (0.);
periodic (back);
#endif
const face vector muc[] = {NU, NU, NU};
mu = muc;
a = av;
run();
}
event init (t = 0) {
DT = 1./(sqrt(sqN)*10.);
foreach() {
b[] = sqN*y;
foreach_dimension()
u.x[] = noise()/1000.;
}
boundary ({b, u});
gp = popen ("gnuplot", "w");
fprintf(gp,
"set term pngcairo size 500, 500\n"
"set xr [%g: %g]\n"
"set yr [0 : %g]\n"
"set grid\n"
"set size square"
"set key topleft"
"set xlabel 'b'\n"
"set ylabel 'y/L_c'\n",
B1/LAMBDA, sqN*Lc*2., Lc*2.);
}
event acceleration (i++) {
coord grav = {0, 1, 0};
boundary({b});
foreach_face()
av.x[] = grav.x*face_value(b, 0);
}
event tracer_diffusion(i++)
diffusion (b, dt, mu);
event adapt (i++) {
double be = sqN*Lc/40., ue = 0.05;
adapt_wavelet ({b, u}, (double[]){be, ue, ue, ue}, maxlevel, 4);
}
event damp (i++) {
foreach() {
if (y > 2.*L0/3.) {
foreach_dimension()
u.x[] *= exp (-dt*(y - 2*L0/3));
b[] -= (b[] - sqN*y)*(1. - exp (-dt*(y - 2*L0/3)));
}
}
boundary ({b, u});
}
#include "profile5c.h"
int frame = 0;
event movies (t += T/1000.) {
scalar db[];
foreach() {
db[] = 0;
foreach_dimension()
db[] += sq((b[1] - b[-1])/(2*Delta));
db[] = db[] > 0 ? log(sqrt(db[]) + 1.) : 0;
}
boundary ({db});
output_ppm (b, file = "b.mp4", linear = true,
n = 500, min = 0, max = sqN*Lc);
output_ppm (db, file = "db.mp4", linear = true,
n = 500, min = 0, max = 3*log(sqN + 1));
double by, yp = 1e-6;
fprintf (gp,
"set output 'plot%d.png'\n"
"set title 'Time since sunrise: %02d:%02d (HH:MM)'\n"
"plot '-' w l lw 3 t 'buoyancy'\n",
frame, (int)(t/60.), (int)floor(fmod(t, 60.)));
while (yp < L0) {
by = 0;
double dy = average_over_yp ({b}, &by, yp);
fprintf (gp,"%g %g\n", by, yp);
yp += dy;
}
fprintf (gp,"e\n");
frame++;
}
event stop (t = 2.*T) {
pclose (gp);
system ("rm mov.mp4");
system ("ffmpeg -f image2 -i plot%d.png -c:v libx264 -vf format=yuv420p -y mov.mp4");
system ("rm plot*");
sleep (10);
system ("ffmpeg -y -i b.mp4 -i mov.mp4 -filter_complex hstack output.mp4");
return 1;
}