What is the right way to evaluate the value of a function when using MPI?
Normally (single process) you just call the function. e.g. adapting demo_poisson.py from the standard demos, to evaluate the solution at the centre point (0.5,0.5):
# Compute solution
u = Function(V)
solve(a == L, u, bc)
print "value at centre = " + str(u(0.5,0.5)) + " [" + str(MPI.rank(mpi_comm_world())) + "]"
On a single process, that returns "value at centre = 0.251894786516 [0]".
But if I run it as "mpirun -np 4 python demo_poisson.py", it gives an error:
value at centre = 0.251894786516 [3]
Traceback (most recent call last):
File "demo_poisson_MPI.py", line 63, in <module>
Traceback (most recent call last):
File "demo_poisson_MPI.py", line 63, in <module>
Traceback (most recent call last):
File "demo_poisson_MPI.py", line 63, in <module>
print "value at centre = " + str(u(0.5,0.5)) + " [" + str(MPI.rank(mpi_comm_world())) + "]"
File "/usr/lib/python2.7/dist-packages/dolfin/functions/function.py", line 601, in __call__
print "value at centre = " + str(u(0.5,0.5)) + " [" + str(MPI.rank(mpi_comm_world())) + "]"
File "/usr/lib/python2.7/dist-packages/dolfin/functions/function.py", line 601, in __call__
print "value at centre = " + str(u(0.5,0.5)) + " [" + str(MPI.rank(mpi_comm_world())) + "]"
File "/usr/lib/python2.7/dist-packages/dolfin/functions/function.py", line 601, in __call__
self.eval(values, x)
RuntimeError:
*** -------------------------------------------------------------------------
*** DOLFIN encountered an error. If you are not able to resolve this issue
*** using the information listed below, you can ask for help at
***
*** fenics@fenicsproject.org
***
*** Remember to include the error message listed below and, if possible,
*** include a *minimal* running example to reproduce the error.
***
*** -------------------------------------------------------------------------
*** Error: Unable to evaluate function at point.
*** Reason: The point is not inside the domain. Consider calling "Function::set_allow_extrapolation(true)" on this Function to allow extrapolation.
*** Where: This error was encountered inside Function.cpp.
*** Process: 2
***
*** DOLFIN version: 1.6.0
*** Git changeset: unknown
*** -------------------------------------------------------------------------
In one sense this makes sense: each process handles a subdomain, so the origin lies only within the domain of one process (process 3 here). For the other processes the origin point is outside their subdomain.
But suppose I need to inspect the value of the solution at a particular point. I don't necessarily know beforehand which process handles the subdomain that the point lies in. So how can I get the value without triggering the error messages on the wrong processes?
The error message mentions "set_allow_extrapolation(true)". But that doesn't help. It just extrapolates out of each subdomain to a point which might in general be far away, so the evaluation will be badly wrong. If I do try it:
solve(a == L, u, bc)
u.set_allow_extrapolation(True)
print "value at centre = " + str(u(0.5,0.5)) + " [" + str(MPI.rank(mpi_comm_world())) + "]"
then I confirm that the extrapolations do not match the exact value (except for process 3, which contains the point) and are different for each process:
Process 1: Building point search tree to accelerate distance queries.
Process 1: Computed bounding box tree with 979 nodes for 490 points.
value at centre = 0.265262040993 [1]
value at centre = 0.251894786516 [3]
Process 0: Building point search tree to accelerate distance queries.
Process 0: Computed bounding box tree with 985 nodes for 493 points.
value at centre = 0.251835898676 [0]
Process 2: Building point search tree to accelerate distance queries.
Process 2: Computed bounding box tree with 1063 nodes for 532 points.
value at centre = 0.261791908549 [2]
I could gather all the values of the solution into a single Vector, e.g.
x = Vector()
u.vector().gather(x, numpy.array(range(V.dim()), "intc"))
print "value gathered at centre = " + str(x.array()[V.dim()/2]) + " [" + str(MPI.rank(mpi_comm_world())) + "]"
But that doesn't seem the best approach. It only returns discrete array points and therefore makes interpolation to the point of interest unclear. For instance, the value of x.array()[V.dim()/2] is 0.227221210641 rather than the correct value 0.251894786516.
What is the proper way to evaluate functions under MPI?