solver.sensitivity(mu, m) (and so solver.gradient(...)) integrates mu . dR/dm over the volume and adds the facet part for a parameter in a natural condition. A parameter that appears only in an essential datum, u|Γ = g(m), contributes nothing, and nothing says so: the gradient comes back zero for a lid velocity or a wall value used as a control.
The missing term is the reaction one. With our convention Kᵀμ = -∂J/∂u,
dJ/dm |_BC = μ_Iᵀ K_IΓ ∂g/∂m = (Kᵀμ)_Γ · ∂g/∂m
since μ_Γ = 0. K_IΓ is not available: PETSc's global vector holds only the unconstrained dofs, and its local residual assembly skips the constrained rows too (ADD_VALUES drops them), so neither the assembled matrix nor a residual evaluation carries them.
Design: assemble Kᵀμ on every node, constrained ones included, as the FEM load ∫ φ f0_adj(μ) + ∇φ · f1_adj(μ) of the adjoint templates on an unconstrained copy of the unknown's space (the load assembler uw.adjoint.dual_on already does this for misfit duals), then dot the constrained rows with ∂g/∂m evaluated at the boundary nodes, honouring the order in which PETSc inserts boundary values where boundaries meet. For Stokes the pressure adjoint enters through the transposed divergence block. Until that lands, a parameter found in an essential datum should be refused loudly rather than dropped.
Found while validating the adjoint against a run with a prescribed wall velocity as the control (branch feature/discrete-adjoint, PR #744).
solver.sensitivity(mu, m)(and sosolver.gradient(...)) integratesmu . dR/dmover the volume and adds the facet part for a parameter in a natural condition. A parameter that appears only in an essential datum,u|Γ = g(m), contributes nothing, and nothing says so: the gradient comes back zero for a lid velocity or a wall value used as a control.The missing term is the reaction one. With our convention
Kᵀμ = -∂J/∂u,since
μ_Γ = 0.K_IΓis not available: PETSc's global vector holds only the unconstrained dofs, and its local residual assembly skips the constrained rows too (ADD_VALUES drops them), so neither the assembled matrix nor a residual evaluation carries them.Design: assemble
Kᵀμon every node, constrained ones included, as the FEM load∫ φ f0_adj(μ) + ∇φ · f1_adj(μ)of the adjoint templates on an unconstrained copy of the unknown's space (the load assembleruw.adjoint.dual_onalready does this for misfit duals), then dot the constrained rows with∂g/∂mevaluated at the boundary nodes, honouring the order in which PETSc inserts boundary values where boundaries meet. For Stokes the pressure adjoint enters through the transposed divergence block. Until that lands, a parameter found in an essential datum should be refused loudly rather than dropped.Found while validating the adjoint against a run with a prescribed wall velocity as the control (branch
feature/discrete-adjoint, PR #744).