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,