The copy lines are necessary because you have no guarantee that the solution vector used by PETSc (x in def F(self, snes, x, F)) shares the same memory as the dolfinx.fem.Function (self.u in that snippet). In practice, they probably won’t, especially if line search is enabled.