Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions firedrake/cofunction.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,13 @@ def __init__(self, function_space_or_inner_product=None,
sobolev_space, u, v
)

if bcs is None:
bcs = ()
else:
bcs = tuple(bcs)
if len(bcs) > 0 and inner_product == "l2":
raise ValueError("Cannot supply boundary conditions with an l2 Riesz map")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious here. Does this lead to the wrong results in optimization?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does "l2" here means that we are imposing the wrong inner-product between two primal objects, or does it mean that we are pairing a primal with a dual?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious here. Does this lead to the wrong results in optimization?

For $l_2$ the bcs are ignored. They could probably be applied, but I'm not sure it would be that useful.

Does "l2" here means that we are imposing the wrong inner-product between two primal objects, or does it mean that we are pairing a primal with a dual?

Yes, it's the basis dependent map defined by dof assignment. I'm not sure if there's a case to remove this now (Firedrake code at least doesn't seem to use it much?) but maybe not for this PR?

Copy link
Member

@JHopeCollins JHopeCollins Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if there's a case to remove this now (Firedrake code at least doesn't seem to use it much?) but maybe not for this PR?

This close to a major release I'd say lets keep this PR to fixes, then we can think about API changes after.


self._function_space = function_space
self._inner_product = inner_product
self._bcs = bcs
Expand Down Expand Up @@ -478,8 +485,8 @@ def __call__(self, value):
else:
solve, rhs, soln = self._solver
rhs.assign(value)
soln.zero()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't PETSc always use a zero initial guess unless you pass -ksp_initial_guess_nonzero?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be ignored due to #4142. I'd need to check for this use case, but I think it's still safer to zero here.

Copy link
Contributor

@pbrubeck pbrubeck Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LinearSolver inherits from NonlinearVariationalSolver, which means that we are running a SNES, which gives a different behaivor from the plain KSP. Currently -ksp_initial_guess_nonzero False zeros the update to whatever is prescribed as the initial guess.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting the initial guess explicitly outside PETSc is the only way with SNES.

solve()
output = Function(self._function_space)
output.assign(soln)
elif ufl.duals.is_primal(value):
if value.function_space() != self._function_space:
Expand All @@ -490,8 +497,13 @@ def __call__(self, value):
for o, c in zip(output.subfunctions, value.subfunctions):
o.dat.data[:] = c.dat.data_ro[:]
else:
if len(self._bcs) > 0:
value = value.copy(deepcopy=True)
for bc in self._bcs:
bc.apply(value)
output = firedrake.assemble(
firedrake.action(self._inner_product, value)
firedrake.action(self._inner_product, value),
bcs=self._bcs, zero_bc_nodes=True,
)
else:
raise ValueError(
Expand Down
Loading