Hi Community,
I have a problem solving nonlinear problems with periodic boundary conditions using dolfinx_mpc. I solved a 2D linear variational problem with a linear and a nonlinear solver and the results are not equal. How is that possible?
I am solving the following Poisson equation: \kappa \nabla^2 (\overline{\nabla T} \cdot \vec{x} + \tilde{T} ) = 0
The weak formulation is
\kappa \nabla (\overline{\nabla T} \cdot \vec{x} + \tilde{T} ) \cdot \nabla \delta v = 0 with zero Neumann boundary conditions.
As you can see in the following picture, the results between linear and nonlinear solver are only qualitatively the same, but not quantitatively.
Do you have any advice what is wrong?
I am not sure, but since I am using Docker and version 0.5.0 of dolfinx and dolfinx_mpc, I can only assume that something is wrong in this version. I appreciate any help!
Here is my MWE:
# Load required libraries
#########################
import gmsh
import dolfinx
from dolfinx import fem, io
from dolfinx.io.gmshio import model_to_mesh
import numpy as np
from mpi4py import MPI
import ufl
from petsc4py import PETSc
from typing import List
import dolfinx_mpc
from Nonlinear_assembly import NonlinearMPCProblem, NewtonSolverMPC
# from: https://github.com/jorgensd/dolfinx_mpc/blob/main/python/tests/test_nonlinear_assembly.py
##############################################################################
# Generate mesh #
##############################################################################
gmsh.initialize()
comm = MPI.COMM_WORLD
rank = MPI.COMM_WORLD.rank
L = 1 # length of rectangle
f = 0.3 # volume fraction of inclusion
r = np.sqrt(f*L**2/np.pi) # radius of inclusion
gdim = 2 # dimension
if rank == 0:
inclusion = gmsh.model.occ.addDisk(L / 2, L / 2, 0, r, r)
gmsh.model.occ.synchronize()
matrix = gmsh.model.occ.addRectangle(0,0,0, L, L)
gmsh.model.occ.synchronize()
whole_domain = gmsh.model.occ.fragment([(2, matrix)], [(2, inclusion)])
gmsh.model.occ.synchronize()
gmsh.model.addPhysicalGroup(gdim, [matrix], tag = 1)
gmsh.model.addPhysicalGroup(gdim, [inclusion], tag = 2)
background_surfaces = []
inclusion_surfaces = []
for domain in whole_domain[0]:
com = gmsh.model.occ.getCenterOfMass(domain[0], domain[1])
mass = gmsh.model.occ.getMass(domain[0], domain[1])
if np.isclose(mass, np.pi * (r ** 2)): # identify inclusion by its mass
inclusion_surfaces.append(domain)
else:
background_surfaces.append(domain)
gmsh.model.mesh.field.add("Distance", 1)
edges = gmsh.model.getBoundary(inclusion_surfaces, oriented=False)
gmsh.model.mesh.field.setNumbers(1, "EdgesList", [e[1] for e in edges])
gmsh.model.mesh.field.add("Threshold", 2)
gmsh.model.mesh.field.setNumber(2, "IField", 1)
gmsh.model.mesh.field.setNumber(2, "LcMin", 0.01)
gmsh.model.mesh.field.setNumber(2, "LcMax", 0.05)
gmsh.model.mesh.field.setNumber(2, "DistMin", 0.025)
gmsh.model.mesh.field.setNumber(2, "DistMax", 0.1)
gmsh.model.mesh.field.setAsBackgroundMesh(2)
gmsh.option.setNumber("Mesh.Algorithm", 6)
gmsh.model.mesh.generate(gdim)
domain, ct, _ = model_to_mesh(gmsh.model, comm, rank, gdim=gdim)
gmsh.finalize()
with io.XDMFFile(domain.comm, "MeshWithGmsh_v2.xdmf", "w") as xdmf:
xdmf.write_mesh(domain)
xdmf.write_meshtags(ct)
dx = ufl.Measure('dx', domain=domain, subdomain_data=ct)
x_coor = ufl.SpatialCoordinate(domain) # coordinates
num_cells = domain.topology.index_map(domain.topology.dim).size_local
##############################################################################
# Function space and elements #
##############################################################################
P1 = ufl.FiniteElement("CG", domain.ufl_cell(), 1)
V = fem.FunctionSpace(domain, P1)
### for linear problem:
T_lin = ufl.TrialFunction(V) # fluctuation: primary variable linear problem
### for nonlinear problem:
T_nonl = fem.Function(V) # fluctuation: primary variable nonlinear problem
dT_nonl = ufl.TrialFunction(V)
# testfunction
v = ufl.TestFunction(V)
##############################################################################
# periodic boundary conditions #
##############################################################################
boundary_condition: List[str] = ["periodic", "periodic"]
fdim = domain.topology.dim - 1
bcs = []
pbc_directions = []
pbc_slave_tags = []
pbc_is_slave = []
pbc_is_master = []
pbc_meshtags = []
pbc_slave_to_master_maps = []
# Create MultiPointConstraint object
mpc = dolfinx_mpc.MultiPointConstraint(V)
def generate_pbc_slave_to_master_map(i):
def pbc_slave_to_master_map(x):
out_x = x.copy()
out_x[i] = x[i] - domain.geometry.x[:, i].max()
return out_x
return pbc_slave_to_master_map
def generate_pbc_is_slave(i):
return lambda x: np.isclose(x[i], domain.geometry.x[:, i].max())
def generate_pbc_is_master(i):
return lambda x: np.isclose(x[i], domain.geometry.x[:, i].min())
for i, bc_type in enumerate(boundary_condition):
pbc_directions.append(i)
pbc_slave_tags.append(i + 2)
pbc_is_slave.append(generate_pbc_is_slave(i))
pbc_is_master.append(generate_pbc_is_master(i))
pbc_slave_to_master_maps.append(generate_pbc_slave_to_master_map(i))
facets = dolfinx.mesh.locate_entities_boundary(domain, fdim, pbc_is_slave[-1])
arg_sort = np.argsort(facets)
pbc_meshtags.append(dolfinx.mesh.meshtags(domain,
fdim,
facets[arg_sort],
np.full(len(facets), pbc_slave_tags[-1], dtype=np.int32)))
N_pbc = len(pbc_directions)
for i in range(N_pbc):
if N_pbc > 1:
def pbc_slave_to_master_map(x):
out_x = pbc_slave_to_master_maps[i](x)
idx = pbc_is_slave[(i + 1) % N_pbc](x)
out_x[pbc_directions[i]][idx] = np.nan
return out_x
else:
pbc_slave_to_master_map = pbc_slave_to_master_maps[i]
mpc.create_periodic_constraint_topological(V, pbc_meshtags[i],
pbc_slave_tags[i],
pbc_slave_to_master_map,
bcs)
# Map intersection(slaves_x, slaves_y) to intersection(masters_x, masters_y),
# i.e. map the slave dof at (1, 1) to the master dof at (0, 0)
if len(pbc_directions) > 1:
def pbc_slave_to_master_map(x):
out_x = x.copy()
out_x[0] = x[0] - domain.geometry.x[:, 0].max()
out_x[1] = x[1] - domain.geometry.x[:, 1].max()
idx = np.logical_and(pbc_is_slave[0](x), pbc_is_slave[1](x))
out_x[0][~idx] = np.nan
out_x[1][~idx] = np.nan
return out_x
mpc.create_periodic_constraint_topological(V, pbc_meshtags[1],
pbc_slave_tags[1],
pbc_slave_to_master_map,
bcs)
mpc.finalize()
##############################################################################
# Definition of parameters #
##############################################################################
### material parameters
S = fem.FunctionSpace(domain, ("DG", 0))
kappa = fem.Function(S)
for i in range(num_cells):
if ct.values[i] == 1: # matrix
kappa.x.array[i] = 1
elif ct.values[i] == 2: # inclusions
kappa.x.array[i] = 5
### prescribed Gradient \bar{\nabla T}
T_gradient = fem.Constant(domain, PETSc.ScalarType((10,0)))
##############################################################################
# variational problem #
##############################################################################
# linear form
eqn_lin = kappa * ufl.dot(ufl.grad(ufl.dot(T_gradient , x_coor) + T_lin), ufl.grad(v)) * dx
# nonlinear form
eqn_nonl = kappa * ufl.dot(ufl.grad(ufl.dot(T_gradient , x_coor) + T_nonl), ufl.grad(v)) * dx
Jac = ufl.derivative(eqn_nonl, T_nonl, dT_nonl)
##############################################################################
# Solve the problem #
##############################################################################
### linear solver
a_form = ufl.lhs(eqn_lin)
L_form = ufl.rhs(eqn_lin)
problem_lin = dolfinx_mpc.LinearProblem(a_form, L_form, mpc, bcs=bcs)
T_lin = problem_lin.solve()
### nonlinear solver
problem_nonl = NonlinearMPCProblem(eqn_nonl, T_nonl, mpc, bcs=bcs, J = Jac)
solver_nonl = NewtonSolverMPC(comm, problem_nonl, mpc)
solver_nonl.solve(T_nonl)
with io.XDMFFile(domain.comm, "results.xdmf", "w") as xdmf:
xdmf.write_mesh(domain)
xdmf.write_function(T_lin)
with io.XDMFFile(domain.comm, "results_nonl.xdmf", "w") as xdmf:
xdmf.write_mesh(domain)
xdmf.write_function(T_nonl)