Approach for using mixed elements with Petsc Non linear solver

If I take the Mixed Elements code from the Workshop and want to solve it with the Petsc Nonlinear problem Solver fem.petsc.NonlinearProblem ( I know it’s linear, just as an MWE), what is the right approach for doing that?

This MWE from that problem:

from mpi4py import MPI

import numpy as np

import basix.ufl
import dolfinx
import ufl

from petsc4py import PETSc
import warnings
import dolfinx.fem.petsc
M = 6
mesh = dolfinx.mesh.create_unit_square(MPI.COMM_WORLD, M, M)

el_u = basix.ufl.element(“Lagrange”, mesh.basix_cell(), 3, shape=(mesh.geometry.dim,))
el_p = basix.ufl.element(“Lagrange”, mesh.basix_cell(), 2)
el_mixed = basix.ufl.mixed_element([el_u, el_p])

W = dolfinx.fem.functionspace(mesh, el_mixed)
u, p = ufl.TrialFunctions(W)
v, q = ufl.TestFunctions(W)

wh = dolfinx.fem.Function(W)
uh, ph = ufl.split(wh)

ds = ufl.Measure(“ds”, domain=mesh)
g = dolfinx.fem.Constant(mesh, dolfinx.default_scalar_type((0, 0)))

f = dolfinx.fem.Constant(mesh, dolfinx.default_scalar_type((0, 0)))

F = ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx
F += ufl.inner(p, ufl.div(v)) * ufl.dx
F += ufl.inner(ufl.div(u), q) * ufl.dx
F -= ufl.inner(f, v) * ufl.dx

def boundary_marker(x):
return np.isclose(x[0], 0.0)

mesh.topology.create_connectivity(mesh.topology.dim - 1, mesh.topology.dim)
left_facets = dolfinx.mesh.locate_entities_boundary(mesh, mesh.topology.dim - 1, boundary_marker)

def top_bottom_marker(x):
return np.isclose(x[1], 1.0) | np.isclose(x[1], 0.0)

tb_facets = dolfinx.mesh.locate_entities_boundary(mesh, mesh.topology.dim - 1, top_bottom_marker)

all_boundary_facets = dolfinx.mesh.exterior_facet_indices(mesh.topology)
remaining_facets = np.setdiff1d(all_boundary_facets, np.union1d(tb_facets, left_facets))

W0 = W.sub(0)
V, V_to_W0 = W0.collapse()
u_inlet_x = dolfinx.fem.Constant(mesh, dolfinx.default_scalar_type(1))
u_inlet_y = dolfinx.fem.Constant(mesh, dolfinx.default_scalar_type(0))
dofs_inlet_x = dolfinx.fem.locate_dofs_topological(W.sub(0).sub(0), mesh.topology.dim - 1, left_facets)
dofs_inlet_y = dolfinx.fem.locate_dofs_topological(W.sub(0).sub(1), mesh.topology.dim - 1, left_facets)
bc_inlet_x = dolfinx.fem.dirichletbc(u_inlet_x, dofs_inlet_x, W.sub(0).sub(0))
bc_inlet_y = dolfinx.fem.dirichletbc(u_inlet_y, dofs_inlet_y, W.sub(0).sub(1))

u_wall = dolfinx.fem.Function(V)
u_wall.x.array[:] = 0
dofs_wall = dolfinx.fem.locate_dofs_topological((W0, V), mesh.topology.dim - 1, tb_facets)
bc_wall = dolfinx.fem.dirichletbc(u_wall, dofs_wall, W0)

bcs = [bc_inlet_x, bc_inlet_y, bc_wall]

Throws me an arity error when I try to naively squeeze it into the nonlinear problem

ptsc_opts_begin = {
“snes_max_it”:100,
“snes_linesearch_monitor”: None,
“snes_linesearch_type”: “None”,
“ksp_type”: “preonly”,
“pc_type”: “lu”,
“pc_factor_mat_solver_type”: “mumps”,
“mat_mumps_icntl_14”: 120,
“ksp_error_if_not_converged”: True,
“snes_atol”: 1e-12,
“snes_rtol”: 1e-10,
“snes_stol”: 1e-10,}

Resid = ufl.extract_blocks(F)
problem = dolfinx.fem.petsc.NonlinearProblem(Resid, [wh], bcs=bcs, petsc_options_prefix=“eulelastb”,
petsc_options=ptsc_opts_begin
)
Error Output ArityMismatch

---------------------------------------------------------------------------
ArityMismatch                             Traceback (most recent call last)
Cell In[4], line 2
      1 Resid = ufl.extract_blocks(F)
----> 2 problem = dolfinx.fem.petsc.NonlinearProblem(Resid, [wh], bcs=bcs, petsc_options_prefix="eulelastb",
      3                                             petsc_options=ptsc_opts_begin
      4                                             )

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/petsc.py:1239, in NonlinearProblem.__init__(self, F, u, petsc_options_prefix, bcs, J, P, kind, petsc_options, form_compiler_options, jit_options, entity_maps)
   1176 """
   1177 Initialize solver for a nonlinear variational problem.
   1178 
   (...)   1236         `EntityMap<dolfinx.mesh.EntityMap>` must be provided.
   1237 """
   1238 # Compile residual and Jacobian forms
-> 1239 self._F = _create_form(
   1240     F,
   1241     form_compiler_options=form_compiler_options,
   1242     jit_options=jit_options,
   1243     entity_maps=entity_maps,
   1244 )
   1246 if J is None:
   1247     J = derivative_block(F, u)

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/forms.py:449, in form(form, dtype, form_compiler_options, jit_options, entity_maps)
    446     else:
    447         return form
--> 449 return _create_form(form)

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/forms.py:445, in form.<locals>._create_form(form)
    443     return _zero_form(form)
    444 elif isinstance(form, collections.abc.Iterable):
--> 445     return list(map(lambda sub_form: _create_form(sub_form), form))
    446 else:
    447     return form

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/forms.py:445, in form.<locals>._create_form.<locals>.<lambda>(sub_form)
    443     return _zero_form(form)
    444 elif isinstance(form, collections.abc.Iterable):
--> 445     return list(map(lambda sub_form: _create_form(sub_form), form))
    446 else:
    447     return form

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/forms.py:445, in form.<locals>._create_form(form)
    443     return _zero_form(form)
    444 elif isinstance(form, collections.abc.Iterable):
--> 445     return list(map(lambda sub_form: _create_form(sub_form), form))
    446 else:
    447     return form

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/forms.py:445, in form.<locals>._create_form.<locals>.<lambda>(sub_form)
    443     return _zero_form(form)
    444 elif isinstance(form, collections.abc.Iterable):
--> 445     return list(map(lambda sub_form: _create_form(sub_form), form))
    446 else:
    447     return form

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/forms.py:441, in form.<locals>._create_form(form)
    431 """Recursively convert ufl.Forms to dolfinx.fem.Form.
    432 
    433 Args:
   (...)    438     A ``dolfinx.fem.Form`` or a list of ``dolfinx.fem.Form``.
    439 """
    440 if isinstance(form, ufl.Form):
--> 441     return _form(form)
    442 elif isinstance(form, ufl.ZeroBaseForm):
    443     return _zero_form(form)

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/forms.py:361, in form.<locals>._form(form)
    359 if msh is None:
    360     raise RuntimeError("Expecting to find a Mesh in the form.")
--> 361 ufcx_form, module, code = jit.ffcx_jit(
    362     msh.comm, form, form_compiler_options=form_compiler_options, jit_options=jit_options
    363 )
    365 # For each argument in form extract its function space
    366 V = [arg.ufl_function_space()._cpp_object for arg in form.arguments()]

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/jit.py:60, in mpi_jit_decorator.<locals>.mpi_jit(comm, *args, **kwargs)
     56 @functools.wraps(local_jit)
     57 def mpi_jit(comm, *args, **kwargs):
     58     # Just call JIT compiler when running in serial
     59     if comm.size == 1:
---> 60         return local_jit(*args, **kwargs)
     62     # Default status (0 == ok, 1 == fail)
     63     status = 0

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/jit.py:215, in ffcx_jit(ufl_object, form_compiler_options, jit_options)
    213 # Switch on type and compile, returning cffi object
    214 if isinstance(ufl_object, ufl.Form):
--> 215     r = ffcx.codegeneration.jit.compile_forms([ufl_object], options=p_ffcx, **p_jit)
    216 elif isinstance(ufl_object, tuple) and isinstance(ufl_object[0], ufl.core.expr.Expr):
    217     r = ffcx.codegeneration.jit.compile_expressions([ufl_object], options=p_ffcx, **p_jit)

File /dolfinx-env/lib/python3.12/site-packages/ffcx/codegeneration/jit.py:224, in compile_forms(forms, options, cache_dir, timeout, cffi_extra_compile_args, cffi_verbose, cffi_debug, cffi_libraries, visualise)
    221     for name in form_names:
    222         decl += form_template.format(name=name)
--> 224     impl = _compile_objects(
    225         decl,
    226         forms,
    227         form_names,
    228         module_name,
    229         p,
    230         cache_dir,
    231         cffi_extra_compile_args,
    232         cffi_verbose,
    233         cffi_debug,
    234         cffi_libraries,
    235         visualise=visualise,
    236     )
    237 except Exception as e:
    238     try:
    239         # remove c file so that it will not timeout next time

File /dolfinx-env/lib/python3.12/site-packages/ffcx/codegeneration/jit.py:349, in _compile_objects(decl, ufl_objects, object_names, module_name, options, cache_dir, cffi_extra_compile_args, cffi_verbose, cffi_debug, cffi_libraries, visualise)
    345 libraries = _libraries + cffi_libraries if cffi_libraries is not None else _libraries
    347 # JIT uses module_name as prefix, which is needed to make names of all struct/function
    348 # unique across modules
--> 349 _, code_body = ffcx.compiler.compile_ufl_objects(
    350     ufl_objects, prefix=module_name, options=options, visualise=visualise
    351 )
    353 # Raise error immediately prior to compilation if no support for C99
    354 # _Complex. Doing this here allows FFCx to be used for complex codegen on
    355 # Windows.
    356 if sys.platform.startswith("win32"):

File /dolfinx-env/lib/python3.12/site-packages/ffcx/compiler.py:108, in compile_ufl_objects(ufl_objects, options, object_names, prefix, visualise)
    106 # Stage 1: analysis
    107 cpu_time = time()
--> 108 analysis = analyze_ufl_objects(ufl_objects, options["scalar_type"])  # type: ignore
    109 _print_timing(1, time() - cpu_time)
    111 # Stage 2: intermediate representation

File /dolfinx-env/lib/python3.12/site-packages/ffcx/analysis.py:94, in analyze_ufl_objects(ufl_objects, scalar_type)
     91     else:
     92         raise TypeError("UFL objects not recognised.")
---> 94 form_data = tuple(_analyze_form(form, scalar_type) for form in forms)
     95 for data in form_data:
     96     elements += data.unique_sub_elements  # type: ignore

File /dolfinx-env/lib/python3.12/site-packages/ffcx/analysis.py:94, in <genexpr>(.0)
     91     else:
     92         raise TypeError("UFL objects not recognised.")
---> 94 form_data = tuple(_analyze_form(form, scalar_type) for form in forms)
     95 for data in form_data:
     96     elements += data.unique_sub_elements  # type: ignore

File /dolfinx-env/lib/python3.12/site-packages/ffcx/analysis.py:180, in _analyze_form(form, scalar_type)
    177 complex_mode = np.issubdtype(scalar_type, np.complexfloating)
    179 # Compute form metadata
--> 180 form_data: ufl.algorithms.formdata.FormData = ufl.algorithms.compute_form_data(
    181     form,
    182     do_apply_function_pullbacks=True,
    183     do_apply_integral_scaling=True,
    184     do_apply_geometry_lowering=True,
    185     preserve_geometry_types=(ufl.classes.Jacobian,),
    186     do_apply_restrictions=True,
    187     do_append_everywhere_integrals=False,  # do not add dx integrals to dx(i) in UFL
    188     complex_mode=complex_mode,
    189 )
    191 # Determine unique quadrature degree and quadrature scheme
    192 # per each integral data
    193 for id, integral_data in enumerate(form_data.integral_data):  # type: ignore
    194     # Iterate through groups of integral data. There is one integral
    195     # data for all integrals with same domain, itype, subdomain_id
   (...)    199     # all integrals in this integral data group, i.e. must be the
    200     # same for for the same (domain, itype, subdomain_id)

File /dolfinx-env/lib/python3.12/site-packages/ufl/algorithms/compute_form_data.py:482, in compute_form_data(form, do_apply_function_pullbacks, do_apply_integral_scaling, do_apply_geometry_lowering, preserve_geometry_types, do_apply_default_restrictions, do_apply_restrictions, do_estimate_degrees, do_append_everywhere_integrals, do_replace_functions, coefficients_to_split, complex_mode, do_remove_component_tensors)
    479 preprocessed_form = reconstruct_form_from_integral_data(self.integral_data)
    481 # TODO: Test how fast this is
--> 482 check_form_arity(preprocessed_form, self.original_form.arguments(), complex_mode)
    484 # TODO: This member is used by unit tests, change the tests to
    485 # remove this!
    486 self.preprocessed_form = preprocessed_form

File /dolfinx-env/lib/python3.12/site-packages/ufl/algorithms/check_arities.py:213, in check_form_arity(form, arguments, complex_mode)
    211 """Check the arity of a form."""
    212 for itg in form.integrals():
--> 213     check_integrand_arity(itg.integrand(), arguments, complex_mode)

File /dolfinx-env/lib/python3.12/site-packages/ufl/algorithms/check_arities.py:194, in check_integrand_arity(expr, arguments, complex_mode)
    192 arguments = tuple(sorted(set(arguments), key=lambda x: (x.number(), x.part())))
    193 rules = ArityChecker(arguments)
--> 194 arg_tuples = map_expr_dag(rules, expr, compress=False)
    195 args = tuple(a[0] for a in arg_tuples)
    196 if args != arguments:

File /dolfinx-env/lib/python3.12/site-packages/ufl/corealg/map_dag.py:37, in map_expr_dag(function, expression, compress, vcache, rcache)
     15 def map_expr_dag(function, expression, compress=True, vcache=None, rcache=None):
     16     """Apply a function to each subexpression node in an expression DAG.
     17 
     18     If the same function is called multiple times in a transformation
   (...)     35         The result of the final function call
     36     """
---> 37     (result,) = map_expr_dags(
     38         function, [expression], compress=compress, vcache=vcache, rcache=rcache
     39     )
     40     return result

File /dolfinx-env/lib/python3.12/site-packages/ufl/corealg/map_dag.py:109, in map_expr_dags(function, expressions, compress, vcache, rcache)
    107     r = handlers[v._ufl_typecode_](v)
    108 else:
--> 109     r = handlers[v._ufl_typecode_](v, *(vcache[u] for u in v.ufl_operands))
    111 # Optionally check if r is in rcache, a memory optimization
    112 # to be able to keep representation of result compact
    113 if compress:

File /dolfinx-env/lib/python3.12/site-packages/ufl/algorithms/check_arities.py:58, in ArityChecker.sum(self, o, a, b)
     56 """Apply to sum."""
     57 if a != b:
---> 58     raise ArityMismatch(
     59         f"Adding expressions with non-matching form arguments "
     60         f"{tuple(map(_afmt, a))} vs {tuple(map(_afmt, b))}."
     61     )
     62 return a

ArityMismatch: Adding expressions with non-matching form arguments ('v_0',) vs ('v_0', 'v_1').

even though it solves linearly with

a, L = ufl.system(F) 
a_compiled = dolfinx.fem.form(a)
L_compiled = dolfinx.fem.form(L)
A = dolfinx.fem.create_matrix(a_compiled)
b = dolfinx.fem.create_vector(L_compiled.function_spaces[0])
A_scipy = A.to_scipy()
bcs = [bc_inlet_x, bc_inlet_y, bc_wall]
dolfinx.fem.assemble_matrix(A, a_compiled, bcs=bcs)

dolfinx.fem.assemble_vector(b.array, L_compiled)
dolfinx.fem.apply_lifting(b.array, [a_compiled], [bcs])
b.scatter_reverse(dolfinx.la.InsertMode.add)
[bc.set(b.array) for bc in bcs]

import scipy.sparse

A_inv = scipy.sparse.linalg.splu(A_scipy)

wh = dolfinx.fem.Function(W)
wh.x.array[:] = A_inv.solve(b.array)

Is it related to the use of TrialFunctions, which are for linear problems? If I try:

F = ufl.replace(F, {u: uh, p:ph})
Resid = ufl.extract_blocks(F)
jac = ufl.derivative(F, uh, v) + ufl.derivative(F, ph, q)
J = ufl.extract_blocks(jac)

problem = dolfinx.fem.petsc.NonlinearProblem(Resid, wh, bcs=bcs,  J=J,petsc_options_prefix=“eulelastb”,
petsc_options=ptsc_opts_begin
)

I get:

Attribute Error: 'FunctionSpace' object has no attribute '\_cpp_object'
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[7], line 4
      2 jac = ufl.derivative(F, uh, v) + ufl.derivative(F, ph, q)
      3 J = ufl.extract_blocks(jac)
----> 4 problem = dolfinx.fem.petsc.NonlinearProblem(Resid, wh, bcs=bcs, J=J,petsc_options_prefix="eulelastb",
      5                                             petsc_options=ptsc_opts_begin
      6                                             )

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/petsc.py:1239, in NonlinearProblem.__init__(self, F, u, petsc_options_prefix, bcs, J, P, kind, petsc_options, form_compiler_options, jit_options, entity_maps)
   1176 """
   1177 Initialize solver for a nonlinear variational problem.
   1178 
   (...)   1236         `EntityMap<dolfinx.mesh.EntityMap>` must be provided.
   1237 """
   1238 # Compile residual and Jacobian forms
-> 1239 self._F = _create_form(
   1240     F,
   1241     form_compiler_options=form_compiler_options,
   1242     jit_options=jit_options,
   1243     entity_maps=entity_maps,
   1244 )
   1246 if J is None:
   1247     J = derivative_block(F, u)

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/forms.py:449, in form(form, dtype, form_compiler_options, jit_options, entity_maps)
    446     else:
    447         return form
--> 449 return _create_form(form)

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/forms.py:445, in form.<locals>._create_form(form)
    443     return _zero_form(form)
    444 elif isinstance(form, collections.abc.Iterable):
--> 445     return list(map(lambda sub_form: _create_form(sub_form), form))
    446 else:
    447     return form

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/forms.py:445, in form.<locals>._create_form.<locals>.<lambda>(sub_form)
    443     return _zero_form(form)
    444 elif isinstance(form, collections.abc.Iterable):
--> 445     return list(map(lambda sub_form: _create_form(sub_form), form))
    446 else:
    447     return form

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/forms.py:445, in form.<locals>._create_form(form)
    443     return _zero_form(form)
    444 elif isinstance(form, collections.abc.Iterable):
--> 445     return list(map(lambda sub_form: _create_form(sub_form), form))
    446 else:
    447     return form

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/forms.py:445, in form.<locals>._create_form.<locals>.<lambda>(sub_form)
    443     return _zero_form(form)
    444 elif isinstance(form, collections.abc.Iterable):
--> 445     return list(map(lambda sub_form: _create_form(sub_form), form))
    446 else:
    447     return form

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/forms.py:441, in form.<locals>._create_form(form)
    431 """Recursively convert ufl.Forms to dolfinx.fem.Form.
    432 
    433 Args:
   (...)    438     A ``dolfinx.fem.Form`` or a list of ``dolfinx.fem.Form``.
    439 """
    440 if isinstance(form, ufl.Form):
--> 441     return _form(form)
    442 elif isinstance(form, ufl.ZeroBaseForm):
    443     return _zero_form(form)

File /usr/local/dolfinx-real/lib/python3.12/dist-packages/dolfinx/fem/forms.py:366, in form.<locals>._form(form)
    361 ufcx_form, module, code = jit.ffcx_jit(
    362     msh.comm, form, form_compiler_options=form_compiler_options, jit_options=jit_options
    363 )
    365 # For each argument in form extract its function space
--> 366 V = [arg.ufl_function_space()._cpp_object for arg in form.arguments()]
    367 part = form_compiler_options.get("part", "full")
    368 if part == "diagonal":

AttributeError: 'FunctionSpace' object has no attribute '_cpp_object'

Should one avoid the use of Mixed Elements in that case, perhaps follow instead Coupling PDEs instead? In that case do we set bcs on each function space separately?

Here is an example of what you could change from your existing definition:

F = ufl.replace(F, {u: uh, p:ph})

problem = dolfinx.fem.petsc.NonlinearProblem(F, wh, bcs=bcs, petsc_options_prefix="nls", petsc_options={"ksp_type":"preonly", "pc_type": "lu", "pc_factor_mat_solver_type": "mumps", "ksp_error_if_not_converged": True,
                                                                                                        "snes_error_if_not_converged": True, "snes_monitor": None})
problem.solve()

which in turn will give you:

  0 SNES Function norm 7.962531076789e+00
  1 SNES Function norm 5.101800648781e-14

Full script:

from mpi4py import MPI

import numpy as np

import basix.ufl
import dolfinx
import ufl

import dolfinx.fem.petsc
M = 6
mesh = dolfinx.mesh.create_unit_square(MPI.COMM_WORLD, M, M)

el_u = basix.ufl.element("Lagrange", mesh.basix_cell(), 3, shape=(mesh.geometry.dim,))
el_p = basix.ufl.element("Lagrange", mesh.basix_cell(), 2)
el_mixed = basix.ufl.mixed_element([el_u, el_p])

W = dolfinx.fem.functionspace(mesh, el_mixed)
u, p = ufl.TrialFunctions(W)
v, q = ufl.TestFunctions(W)

wh = dolfinx.fem.Function(W)
uh, ph = ufl.split(wh)

ds = ufl.Measure("ds", domain=mesh)
g = dolfinx.fem.Constant(mesh, dolfinx.default_scalar_type((0, 0)))

f = dolfinx.fem.Constant(mesh, dolfinx.default_scalar_type((0, 0)))

F = ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx
F += ufl.inner(p, ufl.div(v)) * ufl.dx
F += ufl.inner(ufl.div(u), q) * ufl.dx
F -= ufl.inner(f, v) * ufl.dx

def boundary_marker(x):
    return np.isclose(x[0], 0.0)

mesh.topology.create_connectivity(mesh.topology.dim - 1, mesh.topology.dim)
left_facets = dolfinx.mesh.locate_entities_boundary(mesh, mesh.topology.dim - 1, boundary_marker)

def top_bottom_marker(x):
    return np.isclose(x[1], 1.0) | np.isclose(x[1], 0.0)

tb_facets = dolfinx.mesh.locate_entities_boundary(mesh, mesh.topology.dim - 1, top_bottom_marker)

all_boundary_facets = dolfinx.mesh.exterior_facet_indices(mesh.topology)
remaining_facets = np.setdiff1d(all_boundary_facets, np.union1d(tb_facets, left_facets))

W0 = W.sub(0)
V, V_to_W0 = W0.collapse()
u_inlet_x = dolfinx.fem.Constant(mesh, dolfinx.default_scalar_type(1))
u_inlet_y = dolfinx.fem.Constant(mesh, dolfinx.default_scalar_type(0))
dofs_inlet_x = dolfinx.fem.locate_dofs_topological(W.sub(0).sub(0), mesh.topology.dim - 1, left_facets)
dofs_inlet_y = dolfinx.fem.locate_dofs_topological(W.sub(0).sub(1), mesh.topology.dim - 1, left_facets)
bc_inlet_x = dolfinx.fem.dirichletbc(u_inlet_x, dofs_inlet_x, W.sub(0).sub(0))
bc_inlet_y = dolfinx.fem.dirichletbc(u_inlet_y, dofs_inlet_y, W.sub(0).sub(1))

u_wall = dolfinx.fem.Function(V)
u_wall.x.array[:] = 0
dofs_wall = dolfinx.fem.locate_dofs_topological((W0, V), mesh.topology.dim - 1, tb_facets)
bc_wall = dolfinx.fem.dirichletbc(u_wall, dofs_wall, W0)

bcs = [bc_inlet_x, bc_inlet_y, bc_wall]


F = ufl.replace(F, {u: uh, p:ph})

problem = dolfinx.fem.petsc.NonlinearProblem(F, wh, bcs=bcs, petsc_options_prefix="nls", petsc_options={"ksp_type":"preonly", "pc_type": "lu", "pc_factor_mat_solver_type": "mumps", "ksp_error_if_not_converged": True,
                                                                                                        "snes_error_if_not_converged": True, "snes_monitor": None})
problem.solve()
1 Like

that’s it!? This is a good example of a simple solution which is also dissatisfying. Thanks :slight_smile: