Hi Antoon,
> ... setting such explicit boundary conditions
> for a field that is the solution to a Poisson problem will
> not always work, because the homogeneous equivalent is not
> defined. Meaning that the corrected fields after a MG cycle
> may not satisfy boundary conditions.
This is what I suspected, especially after perusing the work
here:
http://basilisk.fr/sandbox/tfullana/patch_robinBC and
here:
http://basilisk.fr/sandbox/yonghui/smalltest/robin.c
So I rolled up my sleeves and added a Robin (mixed) boundary
condition and its accompanying homogeneous boundary condition
like in the articles above to see if it works. I will summarize
my steps below, show that it works by comparing with an
analytical solution, and then pose a question about how this can
be better done.
1) First, I edit the lines in the qcc.lex source code
(
http://basilisk.fr/src/qcc.lex):
> char * cond[2] = {"dirichlet", "neumann"};
is replaced by
> char * cond[3] = {"dirichlet", "neumann", "robin"};
and the iterator five lines below this is appropriately changed
from
> for (i = 0; i < 2; i++)
to
> for (i = 0; i < 3; i++)
After this, I simply recompile qcc.
2) To test if the Robin boundary conditions and their
homogeneous counterparts work, I used an example problem from
Professor Fitzpatrick's notes
(
http://farside.ph.utexas.edu/teaching/329/lectures/node66.html)
where he codifies the Robin boundary condition as αu+β∇u=γ where
α, β, and γ are prescribed constants and u is the variable being
solved for. The "robin" boundary conditions are codified for the
lexer as
> double alpha = 0.0e0;
> double beta = 0.0e0;
> double _gamma = 0.0e0;
> @define robin(alpha,beta,_gamma) ((2.*_gamma*Delta/(2.*beta+alpha*Delta)) + ((2.*beta-alpha*Delta)/(2.*beta+alpha*Delta))*val(_s,0,0,0))
> @define robin_homogeneous() (((2.*beta-alpha*Delta)/(2.*beta+alpha*Delta))*val(_s,0,0,0))
I believe the two @define statements ensure the correct boundary
conditions are communicated to the Poisson solver, specifically
the correction fields.
Here is the full code that tests Robin boundary conditions for a
Poisson problem:
> /* This program tests the correctness of the Robin boundary condition: αu+β∇u=γ
> * where α, β, and γ are given constants.*/
>
> double alpha = 0.0e0;
> double beta = 0.0e0;
> double _gamma = 0.0e0;
> @define robin(alpha,beta,_gamma) ((2.*_gamma*Delta/(2.*beta+alpha*Delta)) + ((2.*beta-alpha*Delta)/(2.*beta+alpha*Delta))*val(_s,0,0,0))
> @define robin_homogeneous() (((2.*beta-alpha*Delta)/(2.*beta+alpha*Delta))*val(_s,0,0,0))
>
> #define LEVEL 7
> #include "grid/bitree.h"
> #include "poisson.h"
>
> double alphaL=1.0e0;
> double betaL=-1.0e0;
> double gammaL=1.0e0;
>
> double alphaR=1.0e0;
> double betaR=1.0e0;
> double gammaR=1.0e0;
>
> /* Here is the analaytical solution taken from Professor
> Fitzpatrick's notes
> (
http://farside.ph.utexas.edu/teaching/329/lectures/node66.html):*/
>
> double analyticalU(double x){
> double d = alphaL*alphaR+alphaL*betaR-betaL*alphaR;
> double g = (gammaL*(alphaR+betaR)-betaL*(gammaR-(alphaR+betaR)/3.))/d;
> double h = (alphaL*(gammaR-(alphaR+betaR)/3.0e0)-gammaL*alphaR)/d;
> double sqx=x*x;
> double qx=sqx*sqx;
> return(g +h*x +sqx/2.0 -qx/6.0);
> }
>
>
> int main(){
> FILE* output=fopen("output.dat","w");
> size(1.0);
> init_grid(1<<LEVEL);
> scalar u[],rhs[];
>
> foreach(){
> rhs[]=1.0-2.0*sq(x);
> u[]=0.0e0;
> }
>
> alpha=alphaL;
> beta=betaL;
> _gamma=gammaL;
> u[left]=robin(alpha,beta,_gamma);
>
> alpha=alphaR;
> beta=betaR;
> _gamma=gammaR;
> u[right]=robin(alpha,beta,_gamma);
>
> boundary({u});
>
> mgstats mg = poisson(a=u,b=rhs,tolerance=1e-09);
> printf("resbefore: %15.6e resafter: %15.6e minlevel: %d, iters: %d, nrelax: %d\n",
> mg.resb, mg.resa,mg.minlevel,mg.i,mg.nrelax);
>
> foreach(){
> fprintf(output,"%15.6e\t%15.6e\t%15.6e\n",x,u[],analyticalU(x));
> }
>
> free_grid();
> fclose(output);
> exit(0);
> }
One can compile and run the above via "qcc robin.c -lm;./a.out"
(only after the appropriate modifications are made to qcc.lex of
course!).
3) See the attached numerical solution compared to the
analytical one. It looks satisfactory to me, especially because
there is a non-zero gradient at both ends (unlike the
aforementioned sag.c test case).
So all this is fine and it looks like for the simplest problems
one can proceed in this fashion.
However, the approach seems messy as it involves entering the
source code and making a modification. I'd much rather use a
user defined subroutine to handle homogeneous boundary
conditions (like the one used in the complex domain Poisson
example here:
http://basilisk.fr/src/test/neumann.c ). The thing
is I don't know what the mechanism of passing data is for such a
subroutine and how it ought to be linked correctly to the
scalar. Can someone share a minimal example of how to do this
(if it is even possible)? Is there another way to accomplish the
above without interfering with the source code?
I'll also take this opportunity to congratulate the Basilisk
team on maintaining such a powerful and readily usable code:
there is an invaluable educational emphasis here that I haven't
found much in other numerical code groups and I'm extremely
grateful for that! Thank you Stéphane, Antoon, and everyone
else!
Regards,
Vyaas