sandbox/Antoonvh/integrator2.h
Solving for a line-integral field
The line integral of b from \mathbf{x_o} in the (constant) direction \mathbf{n} is defined as a,
\displaystyle a(\mathbf{x_o}) = \int_{\mathbf{x_o}} b\ \mathrm{d}\mathbf{n},
using some intuitive vector calculus, a statisfies
\displaystyle \frac{\partial a}{\partial \mathbf{n}} = -b,
Because solving stragies for both first and second-order accurate discretizations of this problem have specific issues, we derive the equation again,
\displaystyle \frac{\partial^2 a}{\partial \mathbf{n}^2} = -\frac{\partial b}{\partial \mathbf{n}},
or, the so-called “implicit integral equation”
\displaystyle \mathbf{n}\cdot\left(\nabla\left(\mathbf{n}\cdot \nabla a\right) \right) = -\mathbf{n} \cdot \nabla b.
i.e. in 2 dimensions,
\displaystyle n_x^2a_{xx} + n_y^2a_{yy} + 2n_xn_ya_{xy} = -n_xb_x - n_yb_y,
or in 3,
\displaystyle n_x^2a_{xx} + n_y^2a_{yy} + n_z^2a_{zz} + 2n_xn_ya_{xy} + 2n_xn_za_{xz}+ 2n_yn_za_{yz} = -n_xb_x - n_yb_y - n_zb_z,
where the subscripts indicate components or derivatives where sensible. Such an equation maybe solved iteratively, using multigrid acceleration.
#include "poisson.h"
struct Integrate_dn {
scalar a, b;
coord n;
double tolerance;
int nrelax, minlevel;
scalar * res;
};
static double residual_int (scalar * al, scalar * rhsl, scalar * resl, void * data) {
scalar a = al[0], rhs = rhsl[0], res = resl[0];
struct Integrate_dn * p = (struct Integrate_dn *) data;
coord n = p->n;
double maxres = 0;
boundary(al);
foreach () {
res[] = rhs[];
foreach_dimension() {
res[] -= sq(n.x)*(a[1] + a[-1] - 2.*a[])/(sq(Delta));
}
#if (dimension == 2)
res[] -= 2.*n.x*n.y*((a[1, 1] - a[1, -1]) - (a[-1, 1] - a[-1, -1]))/(4*sq(Delta));
#elif (dimension == 3)
res[] -= 2.*(n.x*n.y*((a[1, 1, 0] - a[1, -1, 0]) - (a[-1, 1, 0] - a[-1, -1, 0])) +
n.x*n.z*((a[1, 0, 1] - a[1, 0, -1]) - (a[-1, 0, 1] - a[-1, 0, -1])) +
n.y*n.z*((a[0, 1, 1] - a[0, 1, -1]) - (a[0, -1, 1] - a[0, -1, -1]))
)/(4*sq(Delta));
#endif
if (fabs (res[]) > maxres)
maxres = fabs (res[]);
}
boundary (resl);
return maxres;
}
static void relax_int (scalar * al, scalar * rhsl, int l, void * data) {
scalar a = al[0], rhs = rhsl[0];;
struct Integrate_dn * p = (struct Integrate_dn *) data;
coord n = p->n;
#if JACOBI
scalar c[];
#else
scalar c = a;
#endif
foreach_level (l) {
double nu = -rhs[]*sq(Delta), d = 0;
foreach_dimension() {
d += 2.*sq(n.x);
nu += sq(n.x)*(a[1] + a[-1]);
}
#if (dimension == 2)
nu += 2.*n.x*n.y*((a[1, 1] - a[1, -1]) - (a[-1, 1] - a[-1, -1]))/4.;
#elif (dimension == 3)
nu += 2.*(n.x*n.y*((a[1, 1, 0] - a[1, -1, 0]) - (a[-1, 1, 0] - a[-1, -1, 0])) +
n.x*n.z*((a[1, 0, 1] - a[1, 0, -1]) - (a[-1, 0, 1] - a[-1, 0, -1])) +
n.y*n.z*((a[0, 1, 1] - a[0, 1, -1]) - (a[0, -1, 1] - a[0, -1, -1]))
)/(4.);
#endif
c[] = nu/d;
}
#if JACOBI
foreach_level (l) {
a[] = (a[] + 2.*c[])/3.;
}
#endif
}
Boundary conditions
The solution to the explicit equation is only relevant when subjected to the proper boundary conditions.
#include "bwatch.h"
trace
double BC_int (coord O, coord n, scalar s) {
double integral = 0;
ray r;
r.O = O;
r.dir = n;
foreach_ray_cell_intersection(r reduction(+:integral)) {
double l = 0;
foreach_dimension()
l += sq(_a[1].x - _a[0].x);
l = l > 0 ? sqrt (l) : 0;
integral += l*interpolate (s, (_a[0].x + _a[1].x)/2.,
(_a[0].y + _a[1].y)/2., (_a[0].z + _a[1].z)/2.);
}
return integral;
}
User interface
integrate_dn()
forms the user interface. For the moment, boundary conditions are not computed properly when using MPI.
face vector _BC;
trace
mgstats integrate_dn (struct Integrate_dn p) {
scalar a = p.a, b = p.b, temp[];
_BC = new_face_vector ("_BC");
foreach()
temp[] = a[];
boundary ({temp});
normalize (&p.n);
double tol = L0*1e-4;
//We compute the boundary values
temp[left] = dirichlet (_BC.x[]);
temp[right] = dirichlet (_BC.x[]);
foreach_boundary (left) {
_BC.x[] = BC_int ((coord){x + tol, y, z}, p.n, b);
}
foreach_boundary (right) {
_BC.x[] = BC_int ((coord){x - tol, y, z}, p.n, b);
}
#if (dimension >= 2)
temp[bottom] = dirichlet (_BC.y[]);
temp[top] = dirichlet (_BC.y[]);
foreach_boundary (bottom) {
_BC.y[] = BC_int ((coord){x, y + tol, z}, p.n, b);
}
foreach_boundary (top) {
_BC.y[] = BC_int ((coord){x, y - tol, z}, p.n, b);
}
#endif
#if (dimension == 3)
temp[back] = dirichlet (_BC.z[]);
temp[front] = dirichlet (_BC.z[]);
foreach_boundary (back) {
_BC.z[] = BC_int ((coord){x, y, z + tol}, p.n, b);
}
foreach_boundary (front) {
_BC.z[] = BC_int ((coord){x, y, z - tol}, p.n, b);
}
#endif
boundary_flux ({_BC}); //Coarse level boundary values.
scalar rhs[];
foreach() {
rhs[] = 0;
foreach_dimension()
rhs[] += -p.n.x*(b[1] - b[-1])/(2.*Delta);
}
mgstats mgint = mg_solve ({temp}, {rhs}, residual_int, relax_int, &p,
p.nrelax, p.res, minlevel = max(1, p.minlevel));
delete ((scalar*){_BC});
foreach()
a[] = temp[];
boundary ({a});
return mgint;
}