|  | 
|  | 1 | +# --- | 
|  | 2 | +# jupyter: | 
|  | 3 | +#   jupytext: | 
|  | 4 | +#     text_representation: | 
|  | 5 | +#       extension: .py | 
|  | 6 | +#       format_name: light | 
|  | 7 | +#       format_version: '1.5' | 
|  | 8 | +#       jupytext_version: 1.14.1 | 
|  | 9 | +# --- | 
|  | 10 | + | 
|  | 11 | +# # Mixed formulation of the Poisson equation solved with BDDC preconditioner # noqa | 
|  | 12 | +# | 
|  | 13 | +# This demo illustrates how to solve the Poisson equation using a mixed | 
|  | 14 | +# (two-field) formulation and a monolithic Balancing Domain Decomposition by | 
|  | 15 | +# Constraits (BDDC) preconditioner for the Krylov solver. For details, see | 
|  | 16 | +# the [paper](https://doi.org/10.1137/16M1080653). For details on the | 
|  | 17 | +# equations, see demo_mixed-poisson.py | 
|  | 18 | +# | 
|  | 19 | +# ```{admonition} Download sources | 
|  | 20 | +# :class: download | 
|  | 21 | +# * {download}`Python script <./demo_mixed-poisson-bddc.py>` | 
|  | 22 | +# * {download}`Jupyter notebook <./demo_mixed-poisson-bddc.ipynb>` | 
|  | 23 | +# ``` | 
|  | 24 | + | 
|  | 25 | +# | 
|  | 26 | +# ## Implementation | 
|  | 27 | +# | 
|  | 28 | +# Import the required modules: | 
|  | 29 | + | 
|  | 30 | +# + | 
|  | 31 | +from mpi4py import MPI | 
|  | 32 | +from petsc4py import PETSc | 
|  | 33 | + | 
|  | 34 | +import numpy as np | 
|  | 35 | + | 
|  | 36 | +import dolfinx | 
|  | 37 | +import ufl | 
|  | 38 | +from basix.ufl import element | 
|  | 39 | +from dolfinx import fem, mesh | 
|  | 40 | +from dolfinx.fem.petsc import discrete_gradient, interpolation_matrix | 
|  | 41 | +from dolfinx.mesh import CellType, create_unit_square, GhostMode | 
|  | 42 | + | 
|  | 43 | +# Solution scalar (e.g., float32, complex128) and geometry (float32/64) | 
|  | 44 | +# types | 
|  | 45 | +dtype = dolfinx.default_scalar_type | 
|  | 46 | +xdtype = dolfinx.default_real_type | 
|  | 47 | +# - | 
|  | 48 | + | 
|  | 49 | +# Create a two-dimensional mesh. The iterative solver supports three- | 
|  | 50 | +# dimensional meshes too. BDDC methods needs the linear operator | 
|  | 51 | +# to be stored in unassembled format, i.e. assembled on each MPI process | 
|  | 52 | +# associated with the subdomain part of the mesh, but not across processes | 
|  | 53 | +# To this end, we require the mesh to not be distributed with overlap | 
|  | 54 | + | 
|  | 55 | +# + | 
|  | 56 | +msh = create_unit_square(MPI.COMM_WORLD, 96, 96, CellType.triangle, ghost_mode=GhostMode.none, dtype=xdtype) | 
|  | 57 | +# - | 
|  | 58 | + | 
|  | 59 | +# From now on, the script is the same as demo_mixed-poisson.py. | 
|  | 60 | + | 
|  | 61 | +# + | 
|  | 62 | +k = 1 | 
|  | 63 | +V = fem.functionspace(msh, element("RT", msh.basix_cell(), k, dtype=xdtype)) | 
|  | 64 | +W = fem.functionspace(msh, element("Discontinuous Lagrange", msh.basix_cell(), k - 1, dtype=xdtype)) | 
|  | 65 | +Q = ufl.MixedFunctionSpace(V, W) | 
|  | 66 | + | 
|  | 67 | +sigma, u = ufl.TrialFunctions(Q) | 
|  | 68 | +tau, v = ufl.TestFunctions(Q) | 
|  | 69 | + | 
|  | 70 | +x = ufl.SpatialCoordinate(msh) | 
|  | 71 | +f = 10 * ufl.exp(-((x[0] - 0.5) * (x[0] - 0.5) + (x[1] - 0.5) * (x[1] - 0.5)) / 0.02) | 
|  | 72 | + | 
|  | 73 | +dx = ufl.Measure("dx", msh) | 
|  | 74 | + | 
|  | 75 | +a = ufl.extract_blocks( | 
|  | 76 | +    ufl.inner(sigma, tau) * dx + ufl.inner(u, ufl.div(tau)) * dx + ufl.inner(ufl.div(sigma), v) * dx | 
|  | 77 | +) | 
|  | 78 | +L = [ufl.ZeroBaseForm((tau,)), -ufl.inner(f, v) * dx] | 
|  | 79 | + | 
|  | 80 | +fdim = msh.topology.dim - 1 | 
|  | 81 | +facets_top = mesh.locate_entities_boundary(msh, fdim, lambda x: np.isclose(x[1], 1.0)) | 
|  | 82 | +facets_bottom = mesh.locate_entities_boundary(msh, fdim, lambda x: np.isclose(x[1], 0.0)) | 
|  | 83 | +dofs_top = fem.locate_dofs_topological(V, fdim, facets_top) | 
|  | 84 | +dofs_bottom = fem.locate_dofs_topological(V, fdim, facets_bottom) | 
|  | 85 | + | 
|  | 86 | +cells_top_ = mesh.compute_incident_entities(msh.topology, facets_top, fdim, fdim + 1) | 
|  | 87 | +cells_bottom = mesh.compute_incident_entities(msh.topology, facets_bottom, fdim, fdim + 1) | 
|  | 88 | +g = fem.Function(V, dtype=dtype) | 
|  | 89 | +g.interpolate(lambda x: np.vstack((np.zeros_like(x[0]), np.sin(5 * x[0]))), cells0=cells_top_) | 
|  | 90 | +g.interpolate(lambda x: np.vstack((np.zeros_like(x[0]), -np.sin(5 * x[0]))), cells0=cells_bottom) | 
|  | 91 | +bcs = [fem.dirichletbc(g, dofs_top), fem.dirichletbc(g, dofs_bottom)] | 
|  | 92 | + | 
|  | 93 | +sigma, u = fem.Function(V, name="sigma", dtype=dtype), fem.Function(W, name="u", dtype=dtype) | 
|  | 94 | +# - | 
|  | 95 | + | 
|  | 96 | +# We now create a linear problem solver for the mixed problem. | 
|  | 97 | +# As we will use BDDC, we specify the matrix kind to be "is". | 
|  | 98 | +# We also show a minimal customization for the method to be | 
|  | 99 | +# applied monolithically on the symmetric indefinite linear | 
|  | 100 | +# system arising from the mixed problem. | 
|  | 101 | +# In particular, we need solvers for the subproblems that can | 
|  | 102 | +# support the saddle point formulation. | 
|  | 103 | + | 
|  | 104 | +# + | 
|  | 105 | +if PETSc.Sys().hasExternalPackage("umfpack"): | 
|  | 106 | +    solver = "lu" | 
|  | 107 | +    solver_type = "umfpack" | 
|  | 108 | +elif PETSc.Sys().hasExternalPackage("mumps"): | 
|  | 109 | +    solver = "cholesky" | 
|  | 110 | +    solver_type = "mumps" | 
|  | 111 | +elif PETSc.Sys().hasExternalPackage("superlu"): | 
|  | 112 | +    solver = "lu" | 
|  | 113 | +    solver_type = "superlu" | 
|  | 114 | +elif PETSc.Sys().hasExternalPackage("umfpack"): | 
|  | 115 | +    solver = "lu" | 
|  | 116 | +    solver_type = "umfpack" | 
|  | 117 | +else: | 
|  | 118 | +    solver = "svd" | 
|  | 119 | +    solver_type = "dummy" | 
|  | 120 | + | 
|  | 121 | +problem = fem.petsc.LinearProblem( | 
|  | 122 | +    a, | 
|  | 123 | +    L, | 
|  | 124 | +    u=[sigma, u], | 
|  | 125 | +    kind="is", | 
|  | 126 | +    bcs=bcs, | 
|  | 127 | +    petsc_options_prefix="demo_mixed_poisson_", | 
|  | 128 | +    petsc_options={ | 
|  | 129 | +        "ksp_rtol": 1e-8, | 
|  | 130 | +        "ksp_view": None, | 
|  | 131 | +        "pc_type": "bddc", | 
|  | 132 | +        "pc_bddc_use_local_mat_graph" : False, | 
|  | 133 | +        "pc_bddc_benign_trick": None, | 
|  | 134 | +        "pc_bddc_nonetflux": None, | 
|  | 135 | +        "pc_bddc_detect_disconnected": None, | 
|  | 136 | +        "pc_bddc_dirichlet_pc_type": solver, | 
|  | 137 | +        "pc_bddc_dirichlet_pc_factor_mat_solver_type": solver_type, | 
|  | 138 | +        "pc_bddc_neumann_pc_factor_mat_solver_type": solver_type, | 
|  | 139 | +        "pc_bddc_neumann_pc_type": solver, | 
|  | 140 | +        "pc_bddc_coarse_redundant_pc_factor_mat_solver_type": solver_type, | 
|  | 141 | +        "pc_bddc_coarse_redundant_pc_type": solver, | 
|  | 142 | +    }, | 
|  | 143 | +) | 
|  | 144 | +# - | 
|  | 145 | + | 
|  | 146 | +# Solve and save the solution `u` in VTX format: | 
|  | 147 | + | 
|  | 148 | +# + | 
|  | 149 | +ksp = problem.solver | 
|  | 150 | +ksp.setMonitor( | 
|  | 151 | +    lambda _, its, rnorm: PETSc.Sys.Print(f"Iteration: {its:>4d}, residual: {rnorm:.3e}") | 
|  | 152 | +) | 
|  | 153 | + | 
|  | 154 | +problem.solve() | 
|  | 155 | +converged_reason = problem.solver.getConvergedReason() | 
|  | 156 | +assert converged_reason > 0, f"Krylov solver has not converged, reason: {converged_reason}." | 
|  | 157 | + | 
|  | 158 | +if dolfinx.has_adios2: | 
|  | 159 | +    from dolfinx.io import VTXWriter | 
|  | 160 | + | 
|  | 161 | +    u.name = "u" | 
|  | 162 | +    with VTXWriter(msh.comm, "output_mixed_poisson_bddc.bp", u, "bp4") as f: | 
|  | 163 | +        f.write(0.0) | 
|  | 164 | +else: | 
|  | 165 | +    print("ADIOS2 required for VTX output.") | 
|  | 166 | +# - | 
0 commit comments