diff --git a/docs/notebooks/08-composable-solvers.ipynb b/docs/notebooks/08-composable-solvers.ipynb index d13d798bb0..af0fb4edc8 100644 --- a/docs/notebooks/08-composable-solvers.ipynb +++ b/docs/notebooks/08-composable-solvers.ipynb @@ -114,7 +114,7 @@ "bc_value = as_vector([0.25 * x**2 * (2-x)**2 *y**2, 0])\n", "\n", "bcs = [DirichletBC(W.sub(0), bc_value, 4),\n", - " DirichletBC(W.sub(0), zero(mesh.geometric_dimension), (1, 2, 3))]" + " DirichletBC(W.sub(0), 0, (1, 2, 3))]" ] }, { @@ -222,7 +222,7 @@ } ], "source": [ - "w.assign(0)\n", + "w.zero()\n", "solver = create_solver(solver_parameters)\n", "solver.solve()\n", "convergence(solver)" @@ -319,7 +319,7 @@ } ], "source": [ - "w.assign(0)\n", + "w.zero()\n", "solver = create_solver(solver_parameters)\n", "solver.solve()\n", "convergence(solver)" @@ -409,7 +409,7 @@ } ], "source": [ - "w.assign(0)\n", + "w.zero()\n", "solver = create_solver(exact_inverse_parameters)\n", "solver.solve()\n", "convergence(solver)" @@ -446,10 +446,11 @@ "metadata": {}, "outputs": [], "source": [ - "w_t = TrialFunction(W)\n", - "_, p_t = split(w_t)\n", + "trial = TrialFunction(W)\n", + "_, p_t = split(trial)\n", "\n", - "pmat = lhs(derivative(F, w, w_t)) - 1/nu * p_t * q*dx" + "amat = lhs(derivative(F, w, trial))\n", + "pmat = amat - 1/nu * p_t * q*dx" ] }, { @@ -638,7 +639,7 @@ } ], "source": [ - "w.assign(0)\n", + "w.zero()\n", "solver = create_solver(pmat_parameters, pmat=pmat)\n", "solver.solve()\n", "convergence(solver)" @@ -667,9 +668,11 @@ "class MassMatrix(AuxiliaryOperatorPC):\n", " _prefix = \"mass_\"\n", " def form(self, pc, test, trial):\n", + " # Extract the original form and bcs\n", + " a, bcs = super().form(pc, test, trial)\n", " # Grab the definition of nu from the user application context (a dict)\n", " nu = self.get_appctx(pc)[\"nu\"]\n", - " return (-1/nu * test*trial*dx, None)" + " return (-1/nu * test*trial*dx, bcs)" ] }, { @@ -871,7 +874,7 @@ ], "source": [ "appctx = {\"nu\": nu} # arbitrary user data that is available inside the user PC object\n", - "w.assign(0)\n", + "w.zero()\n", "solver = create_solver(mass_parameters, appctx=appctx)\n", "solver.solve()\n", "convergence(solver)" @@ -918,7 +921,7 @@ " \"ksp_type\": \"chebyshev\",\n", " \"ksp_max_it\": 2,\n", " \"pc_type\": \"python\",\n", - " \"pc_python_type\": \"__main__.MassMatrix\",\n", + " \"pc_python_type\": f\"{__name__}.MassMatrix\",\n", " \"mass_pc_type\": \"sor\",\n", " }\n", "}" @@ -1211,7 +1214,7 @@ ], "source": [ "appctx = {\"nu\": nu} # arbitrary user data that is available inside the user PC object\n", - "w.assign(0)\n", + "w.zero()\n", "solver = create_solver(fieldsplit_mg_parameters, appctx=appctx)\n", "solver.solve()\n", "convergence(solver)" @@ -1259,13 +1262,10 @@ " }\n", " },\n", " \"mg_coarse\": {\n", + " \"mat_type\": \"aij\",\n", " \"ksp_type\": \"preonly\",\n", - " \"pc_type\": \"python\",\n", - " \"pc_python_type\": \"firedrake.AssembledPC\",\n", - " \"assembled\": {\n", - " \"pc_type\": \"lu\",\n", - " \"pc_factor_mat_solver_type\": \"mumps\",\n", - " }\n", + " \"pc_type\": \"lu\",\n", + " \"pc_factor_mat_solver_type\": \"mumps\",\n", " }\n", "}" ] @@ -1283,7 +1283,7 @@ "metadata": {}, "outputs": [], "source": [ - "#w.assign(0)\n", + "#w.zero()\n", "#solver = create_solver(vanka_parameters)\n", "#solver.solve()\n", "#convergence(solver)" @@ -1313,7 +1313,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.10" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/docs/source/firedrake_26.rst b/docs/source/firedrake_26.rst index 2f66c83228..360141787e 100644 --- a/docs/source/firedrake_26.rst +++ b/docs/source/firedrake_26.rst @@ -33,6 +33,20 @@ Programme `The programme is now available `__. +Firedrake Tutorials +------------------- + +.. toctree:: + :maxdepth: 1 + + Introduction to Firedrake and PETSc solvers + Solver Composition + Using Irksome for the heat equation + Composable solvers for the heat equation + Exploring the TimeStepper options + Using Irksome for the Navier Stokes equations + Implicit-explicit steppers for the monodomain equation + Variational data assimilation in Firedrake Venue & registration -------------------- @@ -53,7 +67,7 @@ Event Dates Full Student Full (no ================= =================================== ====== ======= ======================= ========================== PETSc & Firedrake 1 June (10:00) - 5 June (16:00) £1000 £400 £420 £170 PETSc only 1 June (10:00) - 3 June (lunchtime) £550 £220 £250 £100 -Firedrake only 3 June (lunchtime) - 5 June (16:00) £550 £220 £250 £100 +Firedrake only 3 June (lunchtime) - 5 June (16:50) £550 £220 £250 £100 ================= =================================== ====== ======= ======================= ========================== International attendees will likely need to book a hotel for the night before diff --git a/firedrake/assemble.py b/firedrake/assemble.py index 6340502cc4..8013b4d94f 100644 --- a/firedrake/assemble.py +++ b/firedrake/assemble.py @@ -1863,7 +1863,7 @@ def _as_global_kernel_arg_coefficient(_, self): # Interior facet integrals double Real coefficients for the # two sides of the facet, matching the TSFC-generated kernel. return op2.GlobalKernelArg( - (V.value_size,), double=self._integral_type.startswith("interior_facet") + (V.block_size,), double=self._integral_type.startswith("interior_facet") ) else: return self._make_dat_global_kernel_arg(V, index=index) diff --git a/firedrake/preconditioners/patch.py b/firedrake/preconditioners/patch.py index ee4329a243..9e938f88ab 100644 --- a/firedrake/preconditioners/patch.py +++ b/firedrake/preconditioners/patch.py @@ -190,7 +190,7 @@ def matrix_funptr(form, state): mat = LocalMat(dofset) arg = mat(op2.INC, (entity_node_map, entity_node_map)) - args.append(arg) + args.append(arg.global_kernel_arg) statedat = LocalDat(dofset) state_entity_node_map = op2.Map(iterset, toset, arity, @@ -200,42 +200,48 @@ def matrix_funptr(form, state): for i in kinfo.active_domain_numbers.coordinates: c = all_meshes[i].coordinates arg = c.dat(op2.READ, get_map(c.function_space(), mesh, integral_type)) - args.append(arg) + args.append(arg.global_kernel_arg) for i in kinfo.active_domain_numbers.cell_orientations: c = all_meshes[i].cell_orientations() arg = c.dat(op2.READ, get_map(c.function_space(), mesh, integral_type)) - args.append(arg) + args.append(arg.global_kernel_arg) for i in kinfo.active_domain_numbers.cell_sizes: c = all_meshes[i].cell_sizes arg = c.dat(op2.READ, get_map(c.function_space(), mesh, integral_type)) - args.append(arg) + args.append(arg.global_kernel_arg) for n, indices in kinfo.coefficient_numbers: c = form.coefficients()[n] if c is state: if indices != (0, ): raise ValueError(f"Active indices of state (dont_split) function must be (0, ), not {indices}") - args.append(statearg) + args.append(statearg.global_kernel_arg) continue for ind in indices: c_ = c.subfunctions[ind] map_ = get_map(c_.function_space(), mesh, integral_type) - arg = c_.dat(op2.READ, map_) + if c_.function_space().ufl_element().family() == "Real": + # Interior facet integrals double Real coefficients for the + # two sides of the facet, matching the TSFC-generated kernel. + arg = op2.GlobalKernelArg( + (c_.function_space().block_size,), double=integral_type.startswith("interior_facet") + ) + else: + arg = c_.dat(op2.READ, map_).global_kernel_arg args.append(arg) all_constants = extract_firedrake_constants(form) for constant_index in kinfo.constant_numbers: - args.append(all_constants[constant_index].dat(op2.READ)) + args.append(all_constants[constant_index].dat(op2.READ).global_kernel_arg) if integral_type == "interior_facet": arg = mesh.interior_facets.local_facet_dat(op2.READ) - args.append(arg) + args.append(arg.global_kernel_arg) elif integral_type == "exterior_facet": arg = mesh.exterior_facets.local_facet_dat(op2.READ) - args.append(arg) + args.append(arg.global_kernel_arg) iterset = op2.Subset(iterset, []) - wrapper_knl_args = tuple(a.global_kernel_arg for a in args) - mod = op2.GlobalKernel(kinfo.kernel, wrapper_knl_args, subset=True) + mod = op2.GlobalKernel(kinfo.kernel, args, subset=True) kernels.append(CompiledKernel(compile_global_kernel(mod, iterset.comm), kinfo)) return cell_kernels, int_facet_kernels, ext_facet_kernels @@ -294,47 +300,53 @@ def residual_funptr(form, state): statearg = statedat(op2.READ, state_entity_node_map) arg = dat(op2.INC, entity_node_map) - args.append(arg) + args.append(arg.global_kernel_arg) for i in kinfo.active_domain_numbers.coordinates: c = all_meshes[i].coordinates arg = c.dat(op2.READ, get_map(c.function_space(), mesh, integral_type)) - args.append(arg) + args.append(arg.global_kernel_arg) for i in kinfo.active_domain_numbers.cell_orientations: c = all_meshes[i].cell_orientations() arg = c.dat(op2.READ, get_map(c.function_space(), mesh, integral_type)) - args.append(arg) + args.append(arg.global_kernel_arg) for i in kinfo.active_domain_numbers.cell_sizes: c = all_meshes[i].cell_sizes arg = c.dat(op2.READ, get_map(c.function_space(), mesh, integral_type)) - args.append(arg) + args.append(arg.global_kernel_arg) for n, indices in kinfo.coefficient_numbers: c = form.coefficients()[n] if c is state: if indices != (0, ): raise ValueError(f"Active indices of state (dont_split) function must be (0, ), not {indices}") - args.append(statearg) + args.append(statearg.global_kernel_arg) continue for ind in indices: c_ = c.subfunctions[ind] map_ = get_map(c_.function_space(), mesh, integral_type) - arg = c_.dat(op2.READ, map_) + if c_.function_space().ufl_element().family() == "Real": + # Interior facet integrals double Real coefficients for the + # two sides of the facet, matching the TSFC-generated kernel. + arg = op2.GlobalKernelArg( + (c_.function_space().block_size,), double=integral_type.startswith("interior_facet") + ) + else: + arg = c_.dat(op2.READ, map_).global_kernel_arg args.append(arg) all_constants = extract_firedrake_constants(form) for constant_index in kinfo.constant_numbers: - args.append(all_constants[constant_index].dat(op2.READ)) + args.append(all_constants[constant_index].dat(op2.READ).global_kernel_arg) if kinfo.integral_type == "interior_facet": arg = extract_unique_domain(test).interior_facets.local_facet_dat(op2.READ) - args.append(arg) + args.append(arg.global_kernel_arg) elif kinfo.integral_type == "exterior_facet": arg = extract_unique_domain(test).exterior_facets.local_facet_dat(op2.READ) - args.append(arg) + args.append(arg.global_kernel_arg) iterset = op2.Subset(iterset, []) - wrapper_knl_args = tuple(a.global_kernel_arg for a in args) - mod = op2.GlobalKernel(kinfo.kernel, wrapper_knl_args, subset=True) + mod = op2.GlobalKernel(kinfo.kernel, args, subset=True) kernels.append(CompiledKernel(compile_global_kernel(mod, iterset.comm), kinfo)) return cell_kernels, int_facet_kernels, ext_facet_kernels diff --git a/tests/firedrake/regression/test_patch_pc.py b/tests/firedrake/regression/test_patch_pc.py index 039e7940e4..875b423b0c 100644 --- a/tests/firedrake/regression/test_patch_pc.py +++ b/tests/firedrake/regression/test_patch_pc.py @@ -174,3 +174,43 @@ def test_patch_pc_exterior_facets_dx_dS_ds(): L = inner(Constant(1.0), v) * dx star_its, patch_its = _patch_pc_exterior_facets_problem(a, L) assert star_its == patch_its + + +def test_patch_pc_real(): + distribution = {"overlap_type": (DistributedMeshOverlapType.VERTEX, 1)} + mesh = UnitSquareMesh(4, 4, distribution_parameters=distribution) + V = FunctionSpace(mesh, "DG", 1) + R = FunctionSpace(mesh, "R", 0) + u = TrialFunction(V) + v = TestFunction(V) + r = Function(R).assign(3) + # test a form with all types of integral + a = ( + r * inner(u, v) * dx + + avg(r) * inner(avg(u), avg(v)) * dS + + r * inner(u, v) * ds + ) + L = inner(Constant(1.0), v) * dx + + patch_solver_parameters = { + "ksp_type": "preonly", + "ksp_max_it": 1, + "pc_type": "python", + "pc_python_type": "firedrake.PatchPC", + "patch_pc_patch_construct_type": "star", + "patch_pc_patch_construct_dim": 0, + } + patch_solution = Function(V) + solve(a == L, patch_solution, solver_parameters=patch_solver_parameters) + + star_solver_parameters = { + "ksp_type": "preonly", + "ksp_max_it": 1, + "pc_type": "python", + "pc_python_type": "firedrake.ASMStarPC", + "pc_star_construct_dim": 0, + } + star_solution = Function(V) + solve(a == L, star_solution, solver_parameters=star_solver_parameters) + + assert errornorm(patch_solution, star_solution) < 1e-8 diff --git a/tsfc/ufl_utils.py b/tsfc/ufl_utils.py index d16b5b93f5..cf47fb8974 100644 --- a/tsfc/ufl_utils.py +++ b/tsfc/ufl_utils.py @@ -25,7 +25,7 @@ Product, ScalarValue, Sqrt, Zero, CellVolume, FacetArea) from ufl.utils.sorting import sorted_by_count -from ufl.domain import extract_unique_domain +from ufl.domain import extract_domains, extract_unique_domain from gem.node import MemoizerArg @@ -55,7 +55,7 @@ def compute_form_data(form, """ # Multidomain problems require further index simplifications to ensure # that unwanted quantities do not appear inside single-domain integrals. - do_remove_component_tensors = len(form.ufl_domains()) > 1 + do_remove_component_tensors = len(extract_domains(form)) > 1 fd = ufl_compute_form_data( form, do_apply_function_pullbacks=do_apply_function_pullbacks,