Hello,
I’d like to define a nonlinear function which is function of the trial function, per physical region.
For instance, I have c_a(u) = 1+u^2 and c_b = 0.1+u^4 for respectively physical regions \Omega_a and \Omega_b s.t. \Omega = \Omega_a \cup \Omega_b.
I tried to use fem.Function interpolating fem.Expression’s over their respective domain, however it does not yield the expected behavior
DG0 = fem.functionspace(domain, ("DG", 0))
c = fem.Function(DG0)
q = lambda u: 1+u*u
c.interpolate(
fem.Expression( q(uh), DG0.element.interpolation_points ),
mesh_data.cell_tags.find(one) #Omega_a, one is a 2d physical tag
)
c.interpolate(
fem.Expression( q(uh), DG0.element.interpolation_points ),
mesh_data.cell_tags.find(two) #Omega_b, two is a 2d physical tag
)
# F = c * ufl.dot(ufl.grad(uh), ufl.grad(v)) * ufl.dx - f * v * ufl.dx
I give below an adapted example from Jørgen Dokken’s Nonlinear Poisson tutorial.
Running the below code with --single yields
while without
Since here the below code interpolates a single nonlinear function over the whole domain, both runs shall give the same result.
I guess the interpolation projects once the field, removing the nonlinearity of the weak formulation.
How can I define a nonlinear weak formulation whose nonlinear law (function) is defined by physical region?
import gmsh
import numpy as np
import ufl
from dolfinx import fem, io, default_scalar_type, mesh, plot
from dolfinx.fem.petsc import NonlinearProblem, LinearProblem
from mpi4py import MPI
import pyvista
import argparse
def main(single: bool):
gmsh.initialize()
rectangle = gmsh.model.occ.addRectangle(-1, -1, 0, 2, 2)
disk = gmsh.model.occ.addDisk(0, 0, 0, .5, .5)
out, _ = gmsh.model.occ.fragment([(2, rectangle)], [(2, disk)])
dim2 = [ o[1] for o in out if o[0]==2 ]
gmsh.model.occ.synchronize()
one = gmsh.model.add_physical_group(2, [dim2[0]], name="one")
two = gmsh.model.add_physical_group(2, [dim2[1]], name="two")
gmsh.model.mesh.generate(2)
# What following is from
# Author: Jørgen S. Dokken, "Nonlinear poisson tutorial"
def q(u):
return 1 + u**2
mesh_data = io.gmsh.model_to_mesh(gmsh.model, MPI.COMM_WORLD, 0)
domain = mesh_data.mesh
x = ufl.SpatialCoordinate(domain)
u_ufl = 1 + x[0] + 2 * x[1]
f = -ufl.div(q(u_ufl) * ufl.grad(u_ufl))
V = fem.functionspace(domain, ("Lagrange", 1))
def u_exact(x):
return eval(str(u_ufl))
u_D = fem.Function(V)
u_D.interpolate(u_exact)
fdim = domain.topology.dim - 1
boundary_facets = mesh.locate_entities_boundary(
domain, fdim, lambda x: np.full(x.shape[1], True, dtype=bool)
)
bc = fem.dirichletbc(u_D, fem.locate_dofs_topological(V, fdim, boundary_facets))
uh = fem.Function(V)
v = ufl.TestFunction(V)
DG0 = fem.functionspace(domain, ("DG", 0))
c = fem.Function(DG0)
if single:
c = q(uh)
else: # the comments shows what is intended, the run code shows what is wrong
qbis = lambda u: u**4+0.1
c.interpolate(
fem.Expression( q(uh), DG0.element.interpolation_points ),
None#mesh_data.cell_tags.find(one)
)
#c.interpolate(
# fem.Expression( qbis(uh), DG0.element.interpolation_points ),
# mesh_data.cell_tags.find(two)
#)
#_end if-else
F = c * ufl.dot(ufl.grad(uh), ufl.grad(v)) * ufl.dx - f * v * ufl.dx
petsc_options = {
"snes_type": "newtonls",
"snes_linesearch_type": "none",
"snes_atol": 1e-6,
"snes_rtol": 1e-6,
"snes_monitor": None,
"ksp_error_if_not_converged": True,
"ksp_type": "gmres",
"ksp_rtol": 1e-8,
"ksp_monitor": None,
"pc_type": "hypre",
"pc_hypre_type": "boomeramg",
"pc_hypre_boomeramg_max_iter": 1,
"pc_hypre_boomeramg_cycle_type": "v",
}
problem = NonlinearProblem(
F,
uh,
bcs=[bc],
petsc_options=petsc_options,
petsc_options_prefix="nonlinpoisson",
)
problem.solve()
converged = problem.solver.getConvergedReason()
num_iter = problem.solver.getIterationNumber()
assert converged > 0, f"Solver did not converge, got {converged}."
print(
f"Solver converged after {num_iter} iterations with converged reason {converged}."
)
u_topology, u_cell_types, u_geometry = plot.vtk_mesh(V)
u_grid = pyvista.UnstructuredGrid(u_topology, u_cell_types, u_geometry)
u_grid.point_data["u"] = uh.x.array.real
u_grid.set_active_scalars("u")
u_plotter = pyvista.Plotter()
u_plotter.add_mesh(u_grid, show_edges=True)
u_plotter.view_xy()
u_plotter.show()
gmsh.finalize()
exit()
#_end def main()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--single", help="If true, run code with a single nonlinear function, otherwise tries to run the code with two nonlinear function", action="store_true")
args = parser.parse_args()
main(args.single)

