1c4762a1bSJed Brown static const char help[] = "Solves PDE optimization problem using full-space method, interlaces state and adjoint variables.\n\n";
2c4762a1bSJed Brown
3c4762a1bSJed Brown #include <petscdm.h>
4c4762a1bSJed Brown #include <petscdmda.h>
5c4762a1bSJed Brown #include <petscdmredundant.h>
6c4762a1bSJed Brown #include <petscdmcomposite.h>
7c4762a1bSJed Brown #include <petscpf.h>
8c4762a1bSJed Brown #include <petscsnes.h>
9c4762a1bSJed Brown #include <petsc/private/dmimpl.h>
10c4762a1bSJed Brown
11c4762a1bSJed Brown /*
12c4762a1bSJed Brown
13c4762a1bSJed Brown w - design variables (what we change to get an optimal solution)
14c4762a1bSJed Brown u - state variables (i.e. the PDE solution)
15c4762a1bSJed Brown lambda - the Lagrange multipliers
16c4762a1bSJed Brown
17c4762a1bSJed Brown U = (w [u_0 lambda_0 u_1 lambda_1 .....])
18c4762a1bSJed Brown
19c4762a1bSJed Brown fu, fw, flambda contain the gradient of L(w,u,lambda)
20c4762a1bSJed Brown
21c4762a1bSJed Brown FU = (fw [fu_0 flambda_0 .....])
22c4762a1bSJed Brown
23c4762a1bSJed Brown In this example the PDE is
24c4762a1bSJed Brown Uxx = 2,
25c4762a1bSJed Brown u(0) = w(0), thus this is the free parameter
26c4762a1bSJed Brown u(1) = 0
27c4762a1bSJed Brown the function we wish to minimize is
28c4762a1bSJed Brown \integral u^{2}
29c4762a1bSJed Brown
30c4762a1bSJed Brown The exact solution for u is given by u(x) = x*x - 1.25*x + .25
31c4762a1bSJed Brown
32c4762a1bSJed Brown Use the usual centered finite differences.
33c4762a1bSJed Brown
34c4762a1bSJed Brown Note we treat the problem as non-linear though it happens to be linear
35c4762a1bSJed Brown
36c4762a1bSJed Brown See ex21.c for the same code, but that does NOT interlaces the u and the lambda
37c4762a1bSJed Brown
38c4762a1bSJed Brown The vectors u_lambda and fu_lambda contain the u and the lambda interlaced
39c4762a1bSJed Brown */
40c4762a1bSJed Brown
41c4762a1bSJed Brown typedef struct {
42c4762a1bSJed Brown PetscViewer u_lambda_viewer;
43c4762a1bSJed Brown PetscViewer fu_lambda_viewer;
44c4762a1bSJed Brown } UserCtx;
45c4762a1bSJed Brown
46c4762a1bSJed Brown extern PetscErrorCode ComputeFunction(SNES, Vec, Vec, void *);
47c4762a1bSJed Brown extern PetscErrorCode ComputeJacobian_MF(SNES, Vec, Mat, Mat, void *);
48c4762a1bSJed Brown extern PetscErrorCode Monitor(SNES, PetscInt, PetscReal, void *);
49c4762a1bSJed Brown
50c4762a1bSJed Brown /*
51c4762a1bSJed Brown Uses full multigrid preconditioner with GMRES (with no preconditioner inside the GMRES) as the
5201c1178eSBarry Smith smoother on all levels. This is because (1) in the matrix-free case no matrix entries are
53c4762a1bSJed Brown available for doing Jacobi or SOR preconditioning and (2) the explicit matrix case the diagonal
54c4762a1bSJed Brown entry for the control variable is zero which means default SOR will not work.
55c4762a1bSJed Brown
56c4762a1bSJed Brown */
57c4762a1bSJed Brown char common_options[] = "-ksp_type fgmres\
58c4762a1bSJed Brown -snes_grid_sequence 2 \
59c4762a1bSJed Brown -pc_type mg\
60c4762a1bSJed Brown -mg_levels_pc_type none \
61c4762a1bSJed Brown -mg_coarse_pc_type none \
62c4762a1bSJed Brown -pc_mg_type full \
63c4762a1bSJed Brown -mg_coarse_ksp_type gmres \
64c4762a1bSJed Brown -mg_levels_ksp_type gmres \
65c4762a1bSJed Brown -mg_coarse_ksp_max_it 6 \
66c4762a1bSJed Brown -mg_levels_ksp_max_it 3";
67c4762a1bSJed Brown
68c4762a1bSJed Brown char matrix_free_options[] = "-mat_mffd_compute_normu no \
69c4762a1bSJed Brown -mat_mffd_type wp";
70c4762a1bSJed Brown
71c4762a1bSJed Brown extern PetscErrorCode DMCreateMatrix_MF(DM, Mat *);
72c4762a1bSJed Brown
main(int argc,char ** argv)73d71ae5a4SJacob Faibussowitsch int main(int argc, char **argv)
74d71ae5a4SJacob Faibussowitsch {
75c4762a1bSJed Brown UserCtx user;
76c4762a1bSJed Brown DM red, da;
77c4762a1bSJed Brown SNES snes;
78c4762a1bSJed Brown DM packer;
79c4762a1bSJed Brown PetscBool use_monitor = PETSC_FALSE;
80c4762a1bSJed Brown
81327415f7SBarry Smith PetscFunctionBeginUser;
829566063dSJacob Faibussowitsch PetscCall(PetscInitialize(&argc, &argv, NULL, help));
83c4762a1bSJed Brown
84c4762a1bSJed Brown /* Hardwire several options; can be changed at command line */
859566063dSJacob Faibussowitsch PetscCall(PetscOptionsInsertString(NULL, common_options));
869566063dSJacob Faibussowitsch PetscCall(PetscOptionsInsertString(NULL, matrix_free_options));
879566063dSJacob Faibussowitsch PetscCall(PetscOptionsInsert(NULL, &argc, &argv, NULL));
889566063dSJacob Faibussowitsch PetscCall(PetscOptionsGetBool(NULL, NULL, "-use_monitor", &use_monitor, PETSC_IGNORE));
89c4762a1bSJed Brown
90c4762a1bSJed Brown /* Create a global vector that includes a single redundant array and two da arrays */
919566063dSJacob Faibussowitsch PetscCall(DMCompositeCreate(PETSC_COMM_WORLD, &packer));
929566063dSJacob Faibussowitsch PetscCall(DMRedundantCreate(PETSC_COMM_WORLD, 0, 1, &red));
939566063dSJacob Faibussowitsch PetscCall(DMSetOptionsPrefix(red, "red_"));
949566063dSJacob Faibussowitsch PetscCall(DMCompositeAddDM(packer, red));
959566063dSJacob Faibussowitsch PetscCall(DMDACreate1d(PETSC_COMM_WORLD, DM_BOUNDARY_NONE, 5, 2, 1, NULL, &da));
969566063dSJacob Faibussowitsch PetscCall(DMSetOptionsPrefix(red, "da_"));
979566063dSJacob Faibussowitsch PetscCall(DMSetFromOptions(da));
989566063dSJacob Faibussowitsch PetscCall(DMSetUp(da));
999566063dSJacob Faibussowitsch PetscCall(DMDASetFieldName(da, 0, "u"));
1009566063dSJacob Faibussowitsch PetscCall(DMDASetFieldName(da, 1, "lambda"));
1019566063dSJacob Faibussowitsch PetscCall(DMCompositeAddDM(packer, (DM)da));
1029566063dSJacob Faibussowitsch PetscCall(DMSetApplicationContext(packer, &user));
103c4762a1bSJed Brown
104c4762a1bSJed Brown packer->ops->creatematrix = DMCreateMatrix_MF;
105c4762a1bSJed Brown
106c4762a1bSJed Brown /* create nonlinear multi-level solver */
1079566063dSJacob Faibussowitsch PetscCall(SNESCreate(PETSC_COMM_WORLD, &snes));
1089566063dSJacob Faibussowitsch PetscCall(SNESSetDM(snes, packer));
1099566063dSJacob Faibussowitsch PetscCall(SNESSetFunction(snes, NULL, ComputeFunction, NULL));
1109566063dSJacob Faibussowitsch PetscCall(SNESSetJacobian(snes, NULL, NULL, ComputeJacobian_MF, NULL));
111c4762a1bSJed Brown
1129566063dSJacob Faibussowitsch PetscCall(SNESSetFromOptions(snes));
113c4762a1bSJed Brown
114c4762a1bSJed Brown if (use_monitor) {
115c4762a1bSJed Brown /* create graphics windows */
1169566063dSJacob Faibussowitsch PetscCall(PetscViewerDrawOpen(PETSC_COMM_WORLD, 0, "u_lambda - state variables and Lagrange multipliers", -1, -1, -1, -1, &user.u_lambda_viewer));
1179d2ffcd0SPierre Jolivet PetscCall(PetscViewerDrawOpen(PETSC_COMM_WORLD, 0, "fu_lambda - derivative w.r.t. state variables and Lagrange multipliers", -1, -1, -1, -1, &user.fu_lambda_viewer));
1189566063dSJacob Faibussowitsch PetscCall(SNESMonitorSet(snes, Monitor, 0, 0));
119c4762a1bSJed Brown }
120c4762a1bSJed Brown
1219566063dSJacob Faibussowitsch PetscCall(SNESSolve(snes, NULL, NULL));
1229566063dSJacob Faibussowitsch PetscCall(SNESDestroy(&snes));
123c4762a1bSJed Brown
1249566063dSJacob Faibussowitsch PetscCall(DMDestroy(&red));
1259566063dSJacob Faibussowitsch PetscCall(DMDestroy(&da));
1269566063dSJacob Faibussowitsch PetscCall(DMDestroy(&packer));
127c4762a1bSJed Brown if (use_monitor) {
1289566063dSJacob Faibussowitsch PetscCall(PetscViewerDestroy(&user.u_lambda_viewer));
1299566063dSJacob Faibussowitsch PetscCall(PetscViewerDestroy(&user.fu_lambda_viewer));
130c4762a1bSJed Brown }
1319566063dSJacob Faibussowitsch PetscCall(PetscFinalize());
132b122ec5aSJacob Faibussowitsch return 0;
133c4762a1bSJed Brown }
134c4762a1bSJed Brown
135c4762a1bSJed Brown typedef struct {
136c4762a1bSJed Brown PetscScalar u;
137c4762a1bSJed Brown PetscScalar lambda;
138c4762a1bSJed Brown } ULambda;
139c4762a1bSJed Brown
140c4762a1bSJed Brown /*
141d5b43468SJose E. Roman Evaluates FU = Gradient(L(w,u,lambda))
142c4762a1bSJed Brown
143c4762a1bSJed Brown This local function acts on the ghosted version of U (accessed via DMCompositeGetLocalVectors() and
144c4762a1bSJed Brown DMCompositeScatter()) BUT the global, nonghosted version of FU (via DMCompositeGetAccess()).
145c4762a1bSJed Brown
146c4762a1bSJed Brown */
ComputeFunction(SNES snes,Vec U,Vec FU,PetscCtx ctx)147*2a8381b2SBarry Smith PetscErrorCode ComputeFunction(SNES snes, Vec U, Vec FU, PetscCtx ctx)
148d71ae5a4SJacob Faibussowitsch {
149c4762a1bSJed Brown PetscInt xs, xm, i, N;
150c4762a1bSJed Brown ULambda *u_lambda, *fu_lambda;
151c4762a1bSJed Brown PetscScalar d, h, *w, *fw;
152c4762a1bSJed Brown Vec vw, vfw, vu_lambda, vfu_lambda;
153c4762a1bSJed Brown DM packer, red, da;
154c4762a1bSJed Brown
155c4762a1bSJed Brown PetscFunctionBeginUser;
1569566063dSJacob Faibussowitsch PetscCall(VecGetDM(U, &packer));
1579566063dSJacob Faibussowitsch PetscCall(DMCompositeGetEntries(packer, &red, &da));
1589566063dSJacob Faibussowitsch PetscCall(DMCompositeGetLocalVectors(packer, &vw, &vu_lambda));
1599566063dSJacob Faibussowitsch PetscCall(DMCompositeScatter(packer, U, vw, vu_lambda));
1609566063dSJacob Faibussowitsch PetscCall(DMCompositeGetAccess(packer, FU, &vfw, &vfu_lambda));
161c4762a1bSJed Brown
1629566063dSJacob Faibussowitsch PetscCall(DMDAGetCorners(da, &xs, NULL, NULL, &xm, NULL, NULL));
1639566063dSJacob Faibussowitsch PetscCall(DMDAGetInfo(da, 0, &N, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
1649566063dSJacob Faibussowitsch PetscCall(VecGetArray(vw, &w));
1659566063dSJacob Faibussowitsch PetscCall(VecGetArray(vfw, &fw));
1669566063dSJacob Faibussowitsch PetscCall(DMDAVecGetArray(da, vu_lambda, &u_lambda));
1679566063dSJacob Faibussowitsch PetscCall(DMDAVecGetArray(da, vfu_lambda, &fu_lambda));
168c4762a1bSJed Brown d = N - 1.0;
169c4762a1bSJed Brown h = 1.0 / d;
170c4762a1bSJed Brown
171c4762a1bSJed Brown /* derivative of L() w.r.t. w */
172c4762a1bSJed Brown if (xs == 0) { /* only first processor computes this */
173c4762a1bSJed Brown fw[0] = -2.0 * d * u_lambda[0].lambda;
174c4762a1bSJed Brown }
175c4762a1bSJed Brown
176c4762a1bSJed Brown /* derivative of L() w.r.t. u */
177c4762a1bSJed Brown for (i = xs; i < xs + xm; i++) {
178c4762a1bSJed Brown if (i == 0) fu_lambda[0].lambda = h * u_lambda[0].u + 2. * d * u_lambda[0].lambda - d * u_lambda[1].lambda;
179c4762a1bSJed Brown else if (i == 1) fu_lambda[1].lambda = 2. * h * u_lambda[1].u + 2. * d * u_lambda[1].lambda - d * u_lambda[2].lambda;
180c4762a1bSJed Brown else if (i == N - 1) fu_lambda[N - 1].lambda = h * u_lambda[N - 1].u + 2. * d * u_lambda[N - 1].lambda - d * u_lambda[N - 2].lambda;
181c4762a1bSJed Brown else if (i == N - 2) fu_lambda[N - 2].lambda = 2. * h * u_lambda[N - 2].u + 2. * d * u_lambda[N - 2].lambda - d * u_lambda[N - 3].lambda;
182c4762a1bSJed Brown else fu_lambda[i].lambda = 2. * h * u_lambda[i].u - d * (u_lambda[i + 1].lambda - 2.0 * u_lambda[i].lambda + u_lambda[i - 1].lambda);
183c4762a1bSJed Brown }
184c4762a1bSJed Brown
185c4762a1bSJed Brown /* derivative of L() w.r.t. lambda */
186c4762a1bSJed Brown for (i = xs; i < xs + xm; i++) {
187c4762a1bSJed Brown if (i == 0) fu_lambda[0].u = 2.0 * d * (u_lambda[0].u - w[0]);
188c4762a1bSJed Brown else if (i == N - 1) fu_lambda[N - 1].u = 2.0 * d * u_lambda[N - 1].u;
189c4762a1bSJed Brown else fu_lambda[i].u = -(d * (u_lambda[i + 1].u - 2.0 * u_lambda[i].u + u_lambda[i - 1].u) - 2.0 * h);
190c4762a1bSJed Brown }
191c4762a1bSJed Brown
1929566063dSJacob Faibussowitsch PetscCall(VecRestoreArray(vw, &w));
1939566063dSJacob Faibussowitsch PetscCall(VecRestoreArray(vfw, &fw));
1949566063dSJacob Faibussowitsch PetscCall(DMDAVecRestoreArray(da, vu_lambda, &u_lambda));
1959566063dSJacob Faibussowitsch PetscCall(DMDAVecRestoreArray(da, vfu_lambda, &fu_lambda));
1969566063dSJacob Faibussowitsch PetscCall(DMCompositeRestoreLocalVectors(packer, &vw, &vu_lambda));
1979566063dSJacob Faibussowitsch PetscCall(DMCompositeRestoreAccess(packer, FU, &vfw, &vfu_lambda));
1989566063dSJacob Faibussowitsch PetscCall(PetscLogFlops(13.0 * N));
1993ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
200c4762a1bSJed Brown }
201c4762a1bSJed Brown
202c4762a1bSJed Brown /*
203c4762a1bSJed Brown Computes the exact solution
204c4762a1bSJed Brown */
u_solution(void * dummy,PetscInt n,const PetscScalar * x,PetscScalar * u)205d71ae5a4SJacob Faibussowitsch PetscErrorCode u_solution(void *dummy, PetscInt n, const PetscScalar *x, PetscScalar *u)
206d71ae5a4SJacob Faibussowitsch {
207c4762a1bSJed Brown PetscInt i;
208c4762a1bSJed Brown
209c4762a1bSJed Brown PetscFunctionBeginUser;
210c4762a1bSJed Brown for (i = 0; i < n; i++) u[2 * i] = x[i] * x[i] - 1.25 * x[i] + .25;
2113ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
212c4762a1bSJed Brown }
213c4762a1bSJed Brown
ExactSolution(DM packer,Vec U)214d71ae5a4SJacob Faibussowitsch PetscErrorCode ExactSolution(DM packer, Vec U)
215d71ae5a4SJacob Faibussowitsch {
216c4762a1bSJed Brown PF pf;
217c4762a1bSJed Brown Vec x, u_global;
218c4762a1bSJed Brown PetscScalar *w;
219c4762a1bSJed Brown DM da;
220c4762a1bSJed Brown PetscInt m;
221c4762a1bSJed Brown
222c4762a1bSJed Brown PetscFunctionBeginUser;
2239566063dSJacob Faibussowitsch PetscCall(DMCompositeGetEntries(packer, &m, &da));
224c4762a1bSJed Brown
2259566063dSJacob Faibussowitsch PetscCall(PFCreate(PETSC_COMM_WORLD, 1, 2, &pf));
226c4762a1bSJed Brown /* The cast through PETSC_UINTPTR_T is so that compilers will warn about casting to void * from void(*)(void) */
2279566063dSJacob Faibussowitsch PetscCall(PFSetType(pf, PFQUICK, (void *)(PETSC_UINTPTR_T)u_solution));
2289566063dSJacob Faibussowitsch PetscCall(DMGetCoordinates(da, &x));
229c4762a1bSJed Brown if (!x) {
2309566063dSJacob Faibussowitsch PetscCall(DMDASetUniformCoordinates(da, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0));
2319566063dSJacob Faibussowitsch PetscCall(DMGetCoordinates(da, &x));
232c4762a1bSJed Brown }
2339566063dSJacob Faibussowitsch PetscCall(DMCompositeGetAccess(packer, U, &w, &u_global, 0));
234c4762a1bSJed Brown if (w) w[0] = .25;
2359566063dSJacob Faibussowitsch PetscCall(PFApplyVec(pf, x, u_global));
2369566063dSJacob Faibussowitsch PetscCall(PFDestroy(&pf));
2379566063dSJacob Faibussowitsch PetscCall(DMCompositeRestoreAccess(packer, U, &w, &u_global, 0));
2383ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
239c4762a1bSJed Brown }
240c4762a1bSJed Brown
Monitor(SNES snes,PetscInt its,PetscReal rnorm,void * dummy)241d71ae5a4SJacob Faibussowitsch PetscErrorCode Monitor(SNES snes, PetscInt its, PetscReal rnorm, void *dummy)
242d71ae5a4SJacob Faibussowitsch {
243c4762a1bSJed Brown UserCtx *user;
244c4762a1bSJed Brown PetscInt m, N;
245c4762a1bSJed Brown PetscScalar *w, *dw;
246c4762a1bSJed Brown Vec u_lambda, U, F, Uexact;
247c4762a1bSJed Brown DM packer;
248c4762a1bSJed Brown PetscReal norm;
249c4762a1bSJed Brown DM da;
250c4762a1bSJed Brown
251c4762a1bSJed Brown PetscFunctionBeginUser;
2529566063dSJacob Faibussowitsch PetscCall(SNESGetDM(snes, &packer));
2539566063dSJacob Faibussowitsch PetscCall(DMGetApplicationContext(packer, &user));
2549566063dSJacob Faibussowitsch PetscCall(SNESGetSolution(snes, &U));
2559566063dSJacob Faibussowitsch PetscCall(DMCompositeGetAccess(packer, U, &w, &u_lambda));
2569566063dSJacob Faibussowitsch PetscCall(VecView(u_lambda, user->u_lambda_viewer));
2579566063dSJacob Faibussowitsch PetscCall(DMCompositeRestoreAccess(packer, U, &w, &u_lambda));
258c4762a1bSJed Brown
2599566063dSJacob Faibussowitsch PetscCall(SNESGetFunction(snes, &F, 0, 0));
2609566063dSJacob Faibussowitsch PetscCall(DMCompositeGetAccess(packer, F, &w, &u_lambda));
261c4762a1bSJed Brown /* ierr = VecView(u_lambda,user->fu_lambda_viewer); */
2629566063dSJacob Faibussowitsch PetscCall(DMCompositeRestoreAccess(packer, U, &w, &u_lambda));
263c4762a1bSJed Brown
2649566063dSJacob Faibussowitsch PetscCall(DMCompositeGetEntries(packer, &m, &da));
2659566063dSJacob Faibussowitsch PetscCall(DMDAGetInfo(da, 0, &N, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
2669566063dSJacob Faibussowitsch PetscCall(VecDuplicate(U, &Uexact));
2679566063dSJacob Faibussowitsch PetscCall(ExactSolution(packer, Uexact));
2689566063dSJacob Faibussowitsch PetscCall(VecAXPY(Uexact, -1.0, U));
2699566063dSJacob Faibussowitsch PetscCall(DMCompositeGetAccess(packer, Uexact, &dw, &u_lambda));
2709566063dSJacob Faibussowitsch PetscCall(VecStrideNorm(u_lambda, 0, NORM_2, &norm));
271c4762a1bSJed Brown norm = norm / PetscSqrtReal((PetscReal)N - 1.);
2729566063dSJacob Faibussowitsch if (dw) PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Norm of error %g Error at x = 0 %g\n", (double)norm, (double)PetscRealPart(dw[0])));
2739566063dSJacob Faibussowitsch PetscCall(VecView(u_lambda, user->fu_lambda_viewer));
2749566063dSJacob Faibussowitsch PetscCall(DMCompositeRestoreAccess(packer, Uexact, &dw, &u_lambda));
2759566063dSJacob Faibussowitsch PetscCall(VecDestroy(&Uexact));
2763ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
277c4762a1bSJed Brown }
278c4762a1bSJed Brown
DMCreateMatrix_MF(DM packer,Mat * A)279d71ae5a4SJacob Faibussowitsch PetscErrorCode DMCreateMatrix_MF(DM packer, Mat *A)
280d71ae5a4SJacob Faibussowitsch {
281c4762a1bSJed Brown Vec t;
282c4762a1bSJed Brown PetscInt m;
283c4762a1bSJed Brown
284c4762a1bSJed Brown PetscFunctionBeginUser;
2859566063dSJacob Faibussowitsch PetscCall(DMGetGlobalVector(packer, &t));
2869566063dSJacob Faibussowitsch PetscCall(VecGetLocalSize(t, &m));
2879566063dSJacob Faibussowitsch PetscCall(DMRestoreGlobalVector(packer, &t));
2889566063dSJacob Faibussowitsch PetscCall(MatCreateMFFD(PETSC_COMM_WORLD, m, m, PETSC_DETERMINE, PETSC_DETERMINE, A));
2899566063dSJacob Faibussowitsch PetscCall(MatSetUp(*A));
2909566063dSJacob Faibussowitsch PetscCall(MatSetDM(*A, packer));
2913ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
292c4762a1bSJed Brown }
293c4762a1bSJed Brown
ComputeJacobian_MF(SNES snes,Vec x,Mat A,Mat B,PetscCtx ctx)294*2a8381b2SBarry Smith PetscErrorCode ComputeJacobian_MF(SNES snes, Vec x, Mat A, Mat B, PetscCtx ctx)
295d71ae5a4SJacob Faibussowitsch {
296c4762a1bSJed Brown PetscFunctionBeginUser;
2979566063dSJacob Faibussowitsch PetscCall(MatMFFDSetFunction(A, (PetscErrorCode (*)(void *, Vec, Vec))SNESComputeFunction, snes));
2989566063dSJacob Faibussowitsch PetscCall(MatMFFDSetBase(A, x, NULL));
2993ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
300c4762a1bSJed Brown }
301c4762a1bSJed Brown
302c4762a1bSJed Brown /*TEST
303c4762a1bSJed Brown
304c4762a1bSJed Brown test:
305c4762a1bSJed Brown nsize: 2
306c4762a1bSJed Brown args: -da_grid_x 10 -snes_converged_reason -ksp_converged_reason -snes_view
307c4762a1bSJed Brown requires: !single
308c4762a1bSJed Brown
309c4762a1bSJed Brown TEST*/
310