Description
If I understand correctly, since two weeks there appears to be a (total?) ban on using Constants without a domain in the adjoint. This breaks the adjoint of Thetis in a way that is quite hard to deal with. The Thetis code is littered with Constants, most of them just arbitrary parameters, rescaling factors, physical constants, etc., and mostly these are created in a context where no domain is available, e.g. options classes. The only way I can think of fixing this is to have some utility function that attaches a missing domain (i.e. re-creating Constants but now with a domain) before it's being used or modified, but figuring this out proves to be a little involved. I also have to make sure I don't recreate Constants that have been user-provided, as these may be modified by the user later on. This basically means the Thetis user also has to set a domain in any constant they use in their setup, even if they don't care about the adjoint.
I also have to carefully make sure that constants that are used on different meshes, e.g. on a normal FEM mesh and on a VertexOnlyMesh, get different copies of each. This appears to also not always be handled correctly by the adjoint, e.g. in the following code with a functional on the vertex-only mesh:
from firedrake import *
from firedrake_adjoint import *
mesh = UnitSquareMesh(10,10)
mesh0 = VertexOnlyMesh(mesh, [[.5,.5]])
c = Constant(1.0, domain=mesh)
d = Constant(2.0, domain=mesh0)
d.assign(c)
J = assemble(d*dx(domain=mesh0))
rf = ReducedFunctional(J, Control(c))
rf(Constant(3.0, domain=mesh))
The initial forward model works as expected, copying the value of c
on mesh
into a constant d
on mesh0
which is then integrated over mesh0
. In the replay however, the checkpointed value of d
after assignment appears to be on mesh
which then causes the assembly of the functional to fail.
My questions are:
- any suggestions how to best fix Thetis to deal with this
- is there any chance domain-less Constants could be supported again in the adjoint? I feel this change kind of breaks with the philosophy that the adjoint should be automatic regardless of the forward model. If it is too hard to do so, why support domain-less Constants at all? Not that I would be very happy if they got dropped altogether, but at least it would tell me that the forward model I develop including all its functionality that I've tested, should still run in principle with the adjoint/tlm/etc.
- currently I'm just adding a
domain=...
argument when creating Constants, but that now gives a user warning "FutureWarning: Giving Constants a domain is no longer supported. Instead please create a Function in the Real space" - so should I be doing that instead? This seems even more intrusive - and I don't really understand why it couldn't just wrap "Constant(value, domain=mesh)" to return a Function on the Real space. Of course I would have to change myisinstance(..., Constant)
instances in that case.