Hello,
I'm struggling to understand the singular-poisson example.
As I understand, specifying only Neumann boundary conditions means that the solution is only determined up to a constant term. In other words, I can find two solutions u1 and u2 so that a(u1, v) = a(u2, v) = L(v) for all v.
I see that this implies that A has a non-zero null space. However, I do not understand why this causes a problem that needs to be handled in practice.
It seems to me that if b is calculated by evaluating L(v) for all v, it automatically lies in the range of A, and there is no need to subtract anything. As a matter of fact, when I coded some simple examples ignoring the singularity of A, FEniCS calculated the proper solution just fine. If we can find a b not in the range of A, wouldn't this mean that there is no solution to the original PDE with the given boundary conditions?
In addition to that, the example code...
# Create vector that spans the null space
null_vec = Vector(u.vector())
V.dofmap().set(null_vec, 1.0)
null_vec *= 1.0/null_vec.norm("l2")
[...]
# Orthogonalize b with respect to the null space (this gurantees that
# a solution exists)
null_space.orthogonalize(b);
...seems to use the null space of A to orthogonalize b. Shouldn't this be the nullspace of transpose(A)? (cf. issue 186)
Finally, I also do not understand how ensuring that b is in the range of A fixes the constant term of the solution. Even with the new b, there will still be two solutions u1 and u2 such that A.u1 = A.u2 = b.
Can someone explain what I'm missing here?