Dear all,
I am trying to solve a Laplace problem in a square domain with a hole inside and I want to impose a Dirichlet boundary condition on the border of the hole using a Lagrange multiplier.
To do that, I construct a KSP solver (with petsc4py), from a shell matrix for which I give a matrix-vec multiplication rule.
The code below works well in sequential but fails in parallel (with mpirun -np 2 python
).
This the error that I get:
Traceback (most recent call last):
File "test.py", line 140, in <module>
l.solve(B,X)
File "test.py", line 120, in solve
self.ksp.solve(B, X)
File "PETSc/KSP.pyx", line 393, in petsc4py.PETSc.KSP.solve
petsc4py.PETSc.Error: error code 73
I have found very few reference to this error 73
and none of them was usefull in my case.
Do you have any idea on how to fix this error ? Or may be it is a bad idea to use petsc4py within FEniCS and you want to suggest a better solution ?
Cheers,
Fabien
Here is the mwe:
from dolfin import *
import petsc4py.PETSc as petsc
import mshr
xc = .5
yc = .5
rc = .2
f = Constant(1.)
mu = Constant(1.)
density = 50
comm = MPI.comm_world
rank = comm.rank
# Generate mesh
domain = mshr.Rectangle(Point(0,0), Point(1,1)) - mshr.Circle(Point(xc,yc),rc)
mesh = mshr.generate_mesh(domain,density)
with XDMFFile(comm,"laplace_mesh.xdmf") as outfile:
outfile.write(mesh)
# Boundaries
class Dirichlet(SubDomain):
def inside(self, x, on_boundary):
return on_boundary and ( near(x[0], 0., 1e-14) or near(x[0], 1., 1e-14) or near(x[1], 0., 1e-14) or near(x[1], 1., 1e-14) )
class Interface(SubDomain):
def inside(self, x, on_boundary):
return near( (x[0]-xc)*(x[0]-xc) + (x[1]-yc)*(x[1]-yc), rc*rc, mesh.hmin()/10.)
facets = MeshFunction("size_t", mesh, mesh.geometry().dim()-1, 0)
Interface().mark(facets,1)
# Interface mesh
mesh_i = MeshView.create(facets, 1)
# Define Problem
class Laplace:
def __init__(self, mesh, mesh_i):
self.mesh = mesh
self.mesh_i = mesh_i
Vi = FunctionSpace(self.mesh_i, "CG", 1)
V = FunctionSpace(self.mesh, "CG", 1)
self.W = MixedFunctionSpace(V,Vi)
self.dim = self.W.sub_space(1).dim()
self.first_dof, self.last_dof = self.W.sub_space(1).dofmap().ownership_range()
def setup_problem(self, coeff, force, boundary, boundary_condition):
# Define coefficient
self.mu = coeff
# Define external volumique force
self.f = force
# Define boundary condition
ff = MeshFunction("size_t", mesh, mesh.geometry().dim()-1, 0)
boundary().mark(ff, 1)
Interface().mark(ff,2)
self.bc = DirichletBC(self.W.sub_space(0), boundary_condition, ff, 1)
self.ds = Measure("ds", domain=self.W.sub_space(0).mesh(), subdomain_data=ff)
self.dsi = Measure("dx", domain=self.W.sub_space(1).mesh())
# Define problem
u = TrialFunction(self.W.sub_space(0))
self.v = TestFunction(self.W.sub_space(0))
self.sol = Function(self.W.sub_space(0))
a = self.mu*inner(grad(u), grad(self.v))*dx
self.A = PETScMatrix()
self.b = PETScVector()
assemble(a, tensor=self.A)
self.bc.apply(self.A)
# Define Lagrange multiplier
self.lmbda = Function(self.W.sub_space(1))
def setup_rhs(self, B):
L = self.f*self.v*dx
assemble(L, tensor=self.b)
self.bc.apply(self.b)
solve(self.A,self.sol.vector(),self.b)
proj = Function(self.W.sub_space(1))
LagrangeInterpolator.interpolate(proj,self.sol)
B[self.first_dof:self.last_dof] = proj.vector().vec()[self.first_dof:self.last_dof]
def setup_ksp(self, solver='gmres', prec='none'):
M = petsc.Mat().createPython([self.dim,self.dim])
M.setPythonContext(self)
M.setFromOptions()
M.setUp()
PETScOptions.set("ksp_monitor_true_residual")
self.ksp = petsc.KSP().create()
self.ksp.setOperators(M)
self.ksp.setType(solver)
pc = self.ksp.getPC()
pc.setType(prec)
self.ksp.setTolerances(rtol=1e-6)
self.ksp.setFromOptions()
def mult(self, mat, X, Y):
self.lmbda.vector().vec()[self.first_dof:self.last_dof] = X[self.first_dof:self.last_dof]
proj = Function(self.W.sub_space(0))
LagrangeInterpolator.interpolate(proj, self.lmbda)
L = proj*self.v*self.ds(2)
assemble(L, tensor=self.b)
self.bc.apply(self.b)
solve(self.A,self.sol.vector(),self.b)
proj = Function(self.W.sub_space(1))
LagrangeInterpolator.interpolate(proj,self.sol)
Y[self.first_dof:self.last_dof] = proj.vector().vec()[self.first_dof:self.last_dof]
def solve(self, B, X):
self.ksp.solve(B, X)
self.lmbda.vector().vec()[self.first_dof:self.last_dof] = X[self.first_dof:self.last_dof]
proj = Function(self.W.sub_space(0))
LagrangeInterpolator.interpolate(proj, self.lmbda)
L = self.f*self.v*dx - proj*self.v*self.ds(2)
assemble(L, tensor=self.b)
self.bc.apply(self.b)
solve(self.A,self.sol.vector(),self.b)
# Define Problem
l = Laplace(mesh,mesh_i)
B = Function(l.W.sub_space(1)).vector().vec()
X = B.copy()
# Setup and solve problem
zeros = Constant(0.)
l.setup_problem(mu, f, Dirichlet, zeros)
l.setup_rhs(B)
l.setup_ksp()
l.solve(B,X)
with XDMFFile(comm,"laplace.xdmf") as infile:
infile.write(l.sol)
PS: The code makes use of the MeshView function coming from the mixed-dimensional branch of FEniCS. It has be tested in the docker mixed-dimensional branch container