Skip to content

Commit 5c56df3

Browse files
authored
Merge pull request #4656 from firedrakeproject/pbrubeck/merge-release-into-main
DO NOT SQUASH merge release into main
2 parents feb6f94 + 7a4c264 commit 5c56df3

File tree

15 files changed

+334
-156
lines changed

15 files changed

+334
-156
lines changed

docs/source/conf.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@
172172
r'https://www.hilton.com/en/hotels/leehnhn-hilton-leeds-city/',
173173
r'https://www.radissonhotels.com/en-us/hotels/park-plaza-leeds',
174174
r'https://www.radissonhotels.com/en-us/hotels/radisson-blu-leeds'
175+
r'https://www.radissonhotels.com/en-us/hotels/radisson-blu-leeds',
176+
r'https://all.accor.com/hotel/*',
175177
]
176178
linkcheck_timeout = 30
177179

@@ -413,10 +415,8 @@
413415

414416
intersphinx_mapping = {
415417
'pyop2': ('https://op2.github.io/PyOP2', None),
416-
'ufl': ('https://fenics.readthedocs.io/projects/ufl/en/latest/', None),
417418
'ufl': ('https://docs.fenicsproject.org/ufl/main/', None),
418-
'FIAT': ('https://fenics.readthedocs.io/projects/fiat/en/latest/', None),
419-
'FInAT': ('https://finat.github.io/FInAT/', None),
419+
'FIAT': ('https://firedrakeproject.org/fiat', None),
420420
'petsctools': ('https://firedrakeproject.org/petsctools/', None),
421421
'mpi4py': ('https://mpi4py.readthedocs.io/en/stable/', None),
422422
'h5py': ('http://docs.h5py.org/en/latest/', None),

docs/source/install.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ should be followed:
590590
591591
#. Install Firedrake in editable mode without build isolation::
592592

593-
$ pip install --no-build-isolation --no-binary h5py --editable './firedrake[check]'
593+
$ pip install --no-build-isolation --no-binary h5py --editable './firedrake[check,docs]'
594594

595595

596596
Editing subpackages

firedrake/adjoint_utils/blocks/solving.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,13 @@ def _should_compute_boundary_adjoint(self, relevant_dependencies):
163163
def adj_sol(self):
164164
return self.adj_state
165165

166+
@adj_sol.setter
167+
def adj_sol(self, value):
168+
if self.adj_state is None:
169+
self.adj_state = value.copy(deepcopy=True)
170+
else:
171+
self.adj_state.assign(value)
172+
166173
def prepare_evaluate_adj(self, inputs, adj_inputs, relevant_dependencies):
167174
fwd_block_variable = self.get_outputs()[0]
168175
u = fwd_block_variable.output
@@ -187,7 +194,7 @@ def prepare_evaluate_adj(self, inputs, adj_inputs, relevant_dependencies):
187194
adj_sol, adj_sol_bdy = self._assemble_and_solve_adj_eq(
188195
dFdu_form, dJdu, compute_bdy
189196
)
190-
self.adj_state = adj_sol
197+
self.adj_sol = adj_sol
191198
if self.adj_cb is not None:
192199
self.adj_cb(adj_sol)
193200
if self.adj_bdy_cb is not None and compute_bdy:
@@ -408,7 +415,7 @@ def prepare_evaluate_hessian(self, inputs, hessian_inputs, adj_inputs,
408415
firedrake.derivative(dFdu_form, fwd_block_variable.saved_output,
409416
tlm_output))
410417

411-
adj_sol = self.adj_state
418+
adj_sol = self.adj_sol
412419
if adj_sol is None:
413420
raise RuntimeError("Hessian computation was run before adjoint.")
414421
bdy = self._should_compute_boundary_adjoint(relevant_dependencies)
@@ -726,7 +733,7 @@ def prepare_evaluate_adj(self, inputs, adj_inputs, relevant_dependencies):
726733
relevant_dependencies
727734
)
728735
adj_sol, adj_sol_bdy = self._adjoint_solve(adj_inputs[0], compute_bdy)
729-
self.adj_state = adj_sol
736+
self.adj_sol = adj_sol
730737
if self.adj_cb is not None:
731738
self.adj_cb(adj_sol)
732739
if self.adj_bdy_cb is not None and compute_bdy:

firedrake/adjoint_utils/variational_solver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def wrapper(self, *args, **kwargs):
2727
# Try again without expanding derivatives,
2828
# as dFdu might have been simplied to an empty Form
2929
self._ad_adj_F = adjoint(dFdu, derivatives_expanded=True)
30-
except (TypeError, NotImplementedError):
30+
except (ValueError, TypeError, NotImplementedError):
3131
self._ad_adj_F = None
3232
self._ad_kwargs = {'Jp': self.Jp, 'form_compiler_parameters': self.form_compiler_parameters, 'is_linear': self.is_linear}
3333
self._ad_count_map = {}

firedrake/assemble.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,23 @@ def assemble(expr, *args, **kwargs):
6060
`ufl.classes.Measure` in the form. For example, if a
6161
``quadrature_degree`` of 4 is specified in this argument, but a degree of
6262
3 is requested in the measure, the latter will be used.
63-
mat_type : str
63+
mat_type : str | None
6464
String indicating how a 2-form (matrix) should be
6565
assembled -- either as a monolithic matrix (``"aij"`` or ``"baij"``),
66-
a block matrix (``"nest"``), or left as a `matrix.ImplicitMatrix` giving
66+
a block matrix (``"nest"``), or left as a :class:`firedrake.matrix.ImplicitMatrix` giving
6767
matrix-free actions (``'matfree'``). If not supplied, the default value in
68-
``parameters["default_matrix_type"]`` is used. BAIJ differs
69-
from AIJ in that only the block sparsity rather than the dof
68+
``parameters["default_matrix_type"]`` is used. ``"baij"``` differs
69+
from ``"aij"`` in that only the block sparsity rather than the DoF
7070
sparsity is constructed. This can result in some memory
7171
savings, but does not work with all PETSc preconditioners.
72-
BAIJ matrices only make sense for non-mixed matrices.
73-
sub_mat_type : str
72+
``"baij"`` matrices only make sense for non-mixed matrices with arguments
73+
on a :func:`firedrake.functionspace.VectorFunctionSpace`.
74+
75+
NOTE
76+
----
77+
For the assembly of a 0-form or 1-form arising from the action of a 2-form,
78+
the default matrix type is ``"matfree"``.
79+
sub_mat_type : str | None
7480
String indicating the matrix type to
7581
use *inside* a nested block matrix. Only makes sense if
7682
``mat_type`` is ``nest``. May be one of ``"aij"`` or ``"baij"``. If
@@ -154,7 +160,10 @@ def get_assembler(form, *args, **kwargs):
154160
is_base_form_preprocessed = kwargs.pop('is_base_form_preprocessed', False)
155161
fc_params = kwargs.get('form_compiler_parameters', None)
156162
if isinstance(form, ufl.form.BaseForm) and not is_base_form_preprocessed:
157-
mat_type = kwargs.get('mat_type', None)
163+
# If not assembling a matrix, internal BaseForm nodes are matfree by default
164+
# Otherwise, the default matrix type is firedrake.parameters["default_matrix_type"]
165+
default_mat_type = "matfree" if len(form.arguments()) < 2 else None
166+
mat_type = kwargs.get('mat_type', default_mat_type)
158167
# Preprocess the DAG and restructure the DAG
159168
# Only pre-process `form` once beforehand to avoid pre-processing for each assembly call
160169
form = BaseFormAssembler.preprocess_base_form(form, mat_type=mat_type, form_compiler_parameters=fc_params)

firedrake/interpolation.py

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -930,8 +930,8 @@ def callable():
930930
return callable
931931
else:
932932
loops = []
933-
934-
if access == op2.INC:
933+
# Initialise to zero if needed
934+
if access is op2.INC:
935935
loops.append(tensor.zero)
936936

937937
# Arguments in the operand are allowed to be from a MixedFunctionSpace
@@ -957,7 +957,7 @@ def callable():
957957
for indices, sub_expr in expressions.items():
958958
sub_tensor = tensor[indices[0]] if rank == 1 else tensor
959959
loops.extend(_interpolator(sub_tensor, sub_expr, subset, access, bcs=bcs))
960-
960+
# Apply bcs
961961
if bcs and rank == 1:
962962
loops.extend(partial(bc.apply, f) for bc in bcs)
963963

@@ -1038,32 +1038,36 @@ def _interpolator(tensor, expr, subset, access, bcs=None):
10381038
parameters = {}
10391039
parameters['scalar_type'] = utils.ScalarType
10401040

1041-
callables = ()
1041+
copyin = ()
1042+
copyout = ()
10421043

10431044
# For the matfree adjoint 1-form and the 0-form, the cellwise kernel will add multiple
10441045
# contributions from the facet DOFs of the dual argument.
10451046
# The incoming Cofunction needs to be weighted by the reciprocal of the DOF multiplicity.
10461047
needs_weight = isinstance(dual_arg, ufl.Cofunction) and not to_element.is_dg()
10471048
if needs_weight:
1048-
# Compute the reciprocal of the DOF multiplicity
1049+
# Create a buffer for the weighted Cofunction
10491050
W = dual_arg.function_space()
1051+
v = firedrake.Function(W)
1052+
expr = expr._ufl_expr_reconstruct_(operand, v=v)
1053+
copyin += (partial(dual_arg.dat.copy, v.dat),)
1054+
1055+
# Compute the reciprocal of the DOF multiplicity
1056+
wdat = W.make_dat()
1057+
m_ = get_interp_node_map(source_mesh, target_mesh, W)
10501058
wsize = W.finat_element.space_dimension() * W.block_size
10511059
kernel_code = f"""
10521060
void multiplicity(PetscScalar *restrict w) {{
10531061
for (PetscInt i=0; i<{wsize}; i++) w[i] += 1;
10541062
}}"""
1055-
kernel = op2.Kernel(kernel_code, "multiplicity", requires_zeroed_output_arguments=False)
1056-
weight = firedrake.Function(W)
1057-
m_ = get_interp_node_map(source_mesh, target_mesh, W)
1058-
op2.par_loop(kernel, cell_set, weight.dat(op2.INC, m_))
1059-
with weight.dat.vec as w:
1063+
kernel = op2.Kernel(kernel_code, "multiplicity")
1064+
op2.par_loop(kernel, cell_set, wdat(op2.INC, m_))
1065+
with wdat.vec as w:
10601066
w.reciprocal()
10611067

1062-
# Create a buffer for the weighted Cofunction and a callable to apply the weight
1063-
v = firedrake.Function(W)
1064-
expr = expr._ufl_expr_reconstruct_(operand, v=v)
1065-
with weight.dat.vec_ro as w, dual_arg.dat.vec_ro as x, v.dat.vec_wo as y:
1066-
callables += (partial(y.pointwiseMult, x, w),)
1068+
# Create a callable to apply the weight
1069+
with wdat.vec_ro as w, v.dat.vec as y:
1070+
copyin += (partial(y.pointwiseMult, y, w),)
10671071

10681072
# We need to pass both the ufl element and the finat element
10691073
# because the finat elements might not have the right mapping
@@ -1079,7 +1083,7 @@ def _interpolator(tensor, expr, subset, access, bcs=None):
10791083
coefficient_numbers = kernel.coefficient_numbers
10801084
needs_external_coords = kernel.needs_external_coords
10811085
name = kernel.name
1082-
kernel = op2.Kernel(ast, name, requires_zeroed_output_arguments=True,
1086+
kernel = op2.Kernel(ast, name, requires_zeroed_output_arguments=(access is not op2.INC),
10831087
flop_count=kernel.flop_count, events=(kernel.event,))
10841088

10851089
parloop_args = [kernel, cell_set]
@@ -1092,17 +1096,12 @@ def _interpolator(tensor, expr, subset, access, bcs=None):
10921096
output = tensor
10931097
tensor = op2.Dat(tensor.dataset)
10941098
if access is not op2.WRITE:
1095-
copyin = (partial(output.copy, tensor), )
1096-
else:
1097-
copyin = ()
1098-
copyout = (partial(tensor.copy, output), )
1099-
else:
1100-
copyin = ()
1101-
copyout = ()
1099+
copyin += (partial(output.copy, tensor), )
1100+
copyout += (partial(tensor.copy, output), )
11021101
if isinstance(tensor, op2.Global):
11031102
parloop_args.append(tensor(access))
11041103
elif isinstance(tensor, op2.Dat):
1105-
V_dest = arguments[-1].function_space() if isinstance(dual_arg, ufl.Cofunction) else V
1104+
V_dest = arguments[-1].function_space()
11061105
m_ = get_interp_node_map(source_mesh, target_mesh, V_dest)
11071106
parloop_args.append(tensor(access, m_))
11081107
else:
@@ -1162,11 +1161,10 @@ def _interpolator(tensor, expr, subset, access, bcs=None):
11621161
parloop_args.append(target_ref_coords.dat(op2.READ, m_))
11631162

11641163
parloop = op2.ParLoop(*parloop_args)
1165-
parloop_compute_callable = parloop.compute
11661164
if isinstance(tensor, op2.Mat):
1167-
return parloop_compute_callable, tensor.assemble
1165+
return parloop, tensor.assemble
11681166
else:
1169-
return copyin + callables + (parloop_compute_callable, ) + copyout
1167+
return copyin + (parloop, ) + copyout
11701168

11711169

11721170
def get_interp_node_map(source_mesh, target_mesh, fs):

firedrake/mg/kernels.py

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
from tsfc.driver import TSFCIntegralDataInfo
3131
from tsfc.kernel_interface.common import lower_integral_type
3232
from tsfc.parameters import default_parameters
33+
from tsfc.ufl_utils import apply_mapping, simplify_abs
34+
3335
from finat.element_factory import create_element
3436
from finat.quadrature import make_quadrature
3537
from firedrake.pointquery_utils import dX_norm_square, X_isub_dX, init_X, inside_check, is_affine, celldist_l1_c_expr
@@ -113,28 +115,34 @@ def compile_element(expression, dual_space=None, parameters=None,
113115
# # Collect required coefficients
114116

115117
try:
118+
# Forward interpolation: expression has a coefficient
116119
arg, = extract_coefficients(expression)
117120
argument_multiindices = ()
118121
coefficient = True
119-
if expression.ufl_shape:
120-
tensor_indices = tuple(gem.Index() for s in expression.ufl_shape)
121-
else:
122-
tensor_indices = ()
123122
except ValueError:
123+
# Adjoint interpolation: expression has an argument
124124
arg, = extract_arguments(expression)
125125
finat_elem = create_element(arg.ufl_element())
126-
argument_multiindices = (finat_elem.get_indices(), )
127-
argument_multiindex, = argument_multiindices
128-
value_shape = finat_elem.value_shape
126+
argument_multiindex = finat_elem.get_indices()
127+
argument_multiindices = (argument_multiindex, )
128+
coefficient = False
129+
130+
# Map into reference values
131+
domain = extract_unique_domain(expression)
132+
expression = apply_mapping(expression, arg.ufl_element(), domain)
133+
value_shape = expression.ufl_shape
134+
135+
# Get indices for the output tensor
136+
if coefficient:
137+
tensor_indices = tuple(gem.Index() for s in value_shape)
138+
else:
129139
if value_shape:
130140
tensor_indices = argument_multiindex[-len(value_shape):]
131141
else:
132142
tensor_indices = ()
133-
coefficient = False
134143

135144
# Replace coordinates (if any)
136145
builder = firedrake_interface.KernelBuilderBase(scalar_type=ScalarType)
137-
domain = extract_unique_domain(expression)
138146
builder._domain_integral_type_map = {domain: "cell"}
139147
# Translate to GEM
140148
cell = domain.ufl_cell()
@@ -151,7 +159,7 @@ def compile_element(expression, dual_space=None, parameters=None,
151159
context = tsfc.fem.GemPointContext(**config)
152160

153161
# Abs-simplification
154-
expression = tsfc.ufl_utils.simplify_abs(expression, complex_mode)
162+
expression = simplify_abs(expression, complex_mode)
155163

156164
# Translate UFL -> GEM
157165
if coefficient:
@@ -177,7 +185,6 @@ def compile_element(expression, dual_space=None, parameters=None,
177185
return_variable = gem.Indexed(gem.Variable('R', finat_elem.index_shape), argument_multiindex)
178186
result = gem.Indexed(result, tensor_indices)
179187
if dual_space:
180-
value_shape = dual_space.value_shape
181188
if value_shape:
182189
var = gem.Indexed(gem.Variable("b", value_shape), tensor_indices)
183190
b_arg = [lp.GlobalArg("b", dtype=ScalarType, shape=value_shape)]
@@ -219,7 +226,7 @@ def prolong_kernel(expression):
219226
assert hierarchy._meshes[int(idx)].cell_set._extruded
220227
V = expression.function_space()
221228
key = (("prolong",)
222-
+ V.value_shape
229+
+ (V.block_size,)
223230
+ entity_dofs_key(V.finat_element.complex.get_topology())
224231
+ entity_dofs_key(V.finat_element.entity_dofs())
225232
+ entity_dofs_key(coordinates.function_space().finat_element.entity_dofs()))
@@ -283,7 +290,7 @@ def prolong_kernel(expression):
283290
"evaluate": eval_code,
284291
"spacedim": element.cell.get_spatial_dimension(),
285292
"ncandidate": hierarchy.fine_to_coarse_cells[levelf].shape[1],
286-
"Rdim": V.value_size,
293+
"Rdim": V.block_size,
287294
"inside_cell": inside_check(element.cell, eps=1e-8, X="Xref"),
288295
"celldist_l1_c_expr": celldist_l1_c_expr(element.cell, X="Xref"),
289296
"Xc_cell_inc": coords_element.space_dimension(),
@@ -301,7 +308,7 @@ def restrict_kernel(Vf, Vc):
301308
if Vf.extruded:
302309
assert Vc.extruded
303310
key = (("restrict",)
304-
+ Vf.value_shape
311+
+ (Vf.block_size,)
305312
+ entity_dofs_key(Vf.finat_element.complex.get_topology())
306313
+ entity_dofs_key(Vc.finat_element.complex.get_topology())
307314
+ entity_dofs_key(Vf.finat_element.entity_dofs())
@@ -389,7 +396,7 @@ def inject_kernel(Vf, Vc):
389396
else:
390397
level_ratio = 1
391398
key = (("inject", level_ratio)
392-
+ Vf.value_shape
399+
+ (Vf.block_size,)
393400
+ entity_dofs_key(Vc.finat_element.complex.get_topology())
394401
+ entity_dofs_key(Vf.finat_element.complex.get_topology())
395402
+ entity_dofs_key(Vc.finat_element.entity_dofs())
@@ -464,7 +471,7 @@ def inject_kernel(Vf, Vc):
464471
"celldist_l1_c_expr": celldist_l1_c_expr(Vc.finat_element.cell, X="Xref"),
465472
"tdim": Vc.mesh().topological_dimension(),
466473
"ncandidate": ncandidate,
467-
"Rdim": numpy.prod(Vf.value_shape),
474+
"Rdim": Vf.block_size,
468475
"Xf_cell_inc": coords_element.space_dimension(),
469476
"f_cell_inc": Vf_element.space_dimension()
470477
}
@@ -608,7 +615,8 @@ def dg_injection_kernel(Vf, Vc, ncell):
608615
# Coarse basis function evaluated at fine quadrature points
609616
phi_c = fem.fiat_to_ufl(Vce.point_evaluation(0, X_a, (Vce.cell.get_dimension(), 0)), 0)
610617

611-
tensor_indices = tuple(gem.Index(extent=d) for d in f.ufl_shape)
618+
index_shape = f.ufl_element().reference_value_shape
619+
tensor_indices = tuple(gem.Index(extent=d) for d in index_shape)
612620

613621
phi_c = gem.Indexed(phi_c, argument_multiindex + tensor_indices)
614622
fexpr = gem.Indexed(fexpr, tensor_indices)

0 commit comments

Comments
 (0)