Sorry to continue a thread already marked as solved, but I am not sure I have figured out the right way of doing this and need some more help.
Here is a MWE adapted from the unit test for nonlinear pde snes.
# Copyright (C) 2018 Garth N. Wells
#
# This file is part of DOLFINx (https://www.fenicsproject.org)
#
# SPDX-License-Identifier: LGPL-3.0-or-later
"""Unit tests for Newton solver assembly"""
import numpy as np
import ufl
from dolfinx import cpp as _cpp
from dolfinx import la
from dolfinx.fem import (Function, FunctionSpace, dirichletbc, form,
locate_dofs_geometrical)
from dolfinx.fem.petsc import (apply_lifting, assemble_matrix, assemble_vector,
create_matrix, create_vector, set_bc)
from dolfinx.mesh import create_unit_square
from ufl import TestFunction, TrialFunction, derivative, dx, grad, inner
from mpi4py import MPI
from petsc4py import PETSc
class NonlinearPDE_SNESProblem:
def __init__(self, F, u, bc):
V = u.function_space
du = TrialFunction(V)
self.L = form(F)
self.a = form(derivative(F, u, du))
self.bc = bc
self._F, self._J = None, None
self.u = u
def F(self, snes, x, F):
"""Assemble residual vector."""
x.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD)
x.copy(self.u.vector)
self.u.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD)
with F.localForm() as f_local:
f_local.set(0.0)
assemble_vector(F, self.L)
apply_lifting(F, [self.a], bcs=[[self.bc]], x0=[x], scale=-1.0)
F.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)
set_bc(F, [self.bc], x, -1.0)
def J(self, snes, x, J, P):
"""Assemble Jacobian matrix."""
J.zeroEntries()
assemble_matrix(J, self.a, bcs=[self.bc])
J.assemble()
def test_nonlinear_pde_snes(bound):
"""Test Newton solver for a simple nonlinear PDE"""
# Create mesh and function space
mesh = create_unit_square(MPI.COMM_WORLD, 12, 15)
V = FunctionSpace(mesh, ("Lagrange", 1))
u = Function(V)
u.x.array[:] = 15
u.name = 'solution'
from dolfinx import io
with io.XDMFFile(MPI.COMM_WORLD,'u.xdmf','w') as xdmf:
xdmf.write_mesh(mesh)
xdmf.write_function(u,0)
v = TestFunction(V)
F = inner(5.0, v) * dx - ufl.sqrt(u * u) * inner(
grad(u), grad(v)) * dx - inner(u, v) * dx
u_bc = Function(V)
u_bc.x.array[:] = 1.0
bc = dirichletbc(u_bc, locate_dofs_geometrical(V, lambda x: np.logical_or(np.isclose(x[0], 0.0),
np.isclose(x[0], 1.0))))
# Create nonlinear problem
problem = NonlinearPDE_SNESProblem(F, u, bc)
b = la.create_petsc_vector(V.dofmap.index_map, V.dofmap.index_map_bs)
J = create_matrix(problem.a)
# Create Newton solver and solve
snes = PETSc.SNES().create()
opts = PETSc.Options()
opts['snes_monitor'] = None
snes.setFromOptions()
snes.setType('vinewtonssls')
snes.setFunction(problem.F, b)
snes.setJacobian(problem.J, J)
snes.setTolerances(atol=1e-10,rtol=1.0e-9, max_it=10)
snes.getKSP().setType("preonly")
snes.getKSP().setTolerances(rtol=1.0e-9)
snes.getKSP().getPC().setType("lu")
if bound:
u_min, u_max = Function(V), Function(V)
u_min.x.array[:] = 1.0
u_max.x.array[:] = 1.5
snes.setVariableBounds(u_min.vector, u_max.vector)
snes.solve(None, u.vector)
print('Minimum in u array:',np.min(u.x.array),'\nMaximum in u array:', np.max(u.x.array))
from dolfinx import io
with io.XDMFFile(MPI.COMM_WORLD,'u.xdmf','a') as xdmf:
xdmf.write_function(u,1)
print('No bounds')
test_nonlinear_pde_snes(bound=False)
print('\nBounds set between 1.0 and 1.5')
test_nonlinear_pde_snes(bound=True)
When I run this code, I get the following results,
No bounds
0 SNES Function norm 9.083961017374e+02
1 SNES Function norm 2.603033612549e-01
2 SNES Function norm 6.870718004914e-02
3 SNES Function norm 1.678228243832e-03
4 SNES Function norm 8.126048345290e-07
Minimum in u array: 1.0
Maximum in u array: 1.3851364823572738
Bounds set between 1.0 and 1.5
0 SNES Function norm 2.202572332184e+00
1 SNES Function norm 4.139002904298e-01
Minimum in u array: -0.012085010497842469
Maximum in u array: 0.03760750671222456
Considering that the final solution without bounds lies between 1.0 and 1.5, I expected the solver to converge to that solution without any issues even with those bounds set. But this doesn’t seem to be the case. Is there something wrong with the way I am setting the bounds on the problem?
Also, the two solvers that PETSc provides for problems with bounds, vinewtonssls
and vinewtonrsls
give different results.
Could someone please share their expertise in this matter?
Thank you
Krishna Kenja