xref: /libCEED/examples/petsc/src/matops.c (revision 6c88e6a2c47a72cd5c3efacc26a364b9cb7d40db)
1e83e87a5Sjeremylt #include "../include/matops.h"
2e83e87a5Sjeremylt #include "../include/petscutils.h"
3e83e87a5Sjeremylt 
4e83e87a5Sjeremylt // -----------------------------------------------------------------------------
5*6c88e6a2Srezgarshakeri // Setup apply operator context data
6*6c88e6a2Srezgarshakeri // -----------------------------------------------------------------------------
7*6c88e6a2Srezgarshakeri PetscErrorCode SetupApplyOperatorCtx(MPI_Comm comm, DM dm, Ceed ceed,
8*6c88e6a2Srezgarshakeri                                      CeedData ceed_data, Vec X_loc,
9*6c88e6a2Srezgarshakeri                                      OperatorApplyContext op_apply_ctx) {
10*6c88e6a2Srezgarshakeri   PetscErrorCode ierr;
11*6c88e6a2Srezgarshakeri   PetscFunctionBeginUser;
12*6c88e6a2Srezgarshakeri 
13*6c88e6a2Srezgarshakeri   op_apply_ctx->comm = comm;
14*6c88e6a2Srezgarshakeri   op_apply_ctx->dm = dm;
15*6c88e6a2Srezgarshakeri   op_apply_ctx->X_loc = X_loc;
16*6c88e6a2Srezgarshakeri   ierr = VecDuplicate(X_loc, &op_apply_ctx->Y_loc); CHKERRQ(ierr);
17*6c88e6a2Srezgarshakeri   op_apply_ctx->x_ceed = ceed_data->x_ceed;
18*6c88e6a2Srezgarshakeri   op_apply_ctx->y_ceed = ceed_data->y_ceed;
19*6c88e6a2Srezgarshakeri   op_apply_ctx->op = ceed_data->op_apply;
20*6c88e6a2Srezgarshakeri   op_apply_ctx->ceed = ceed;
21*6c88e6a2Srezgarshakeri   PetscFunctionReturn(0);
22*6c88e6a2Srezgarshakeri }
23*6c88e6a2Srezgarshakeri 
24*6c88e6a2Srezgarshakeri // -----------------------------------------------------------------------------
25*6c88e6a2Srezgarshakeri // Setup error operator context data
26*6c88e6a2Srezgarshakeri // -----------------------------------------------------------------------------
27*6c88e6a2Srezgarshakeri PetscErrorCode SetupErrorOperatorCtx(MPI_Comm comm, DM dm, Ceed ceed,
28*6c88e6a2Srezgarshakeri                                      CeedData ceed_data, Vec X_loc, CeedOperator op_error,
29*6c88e6a2Srezgarshakeri                                      OperatorApplyContext op_error_ctx) {
30*6c88e6a2Srezgarshakeri   PetscErrorCode ierr;
31*6c88e6a2Srezgarshakeri   PetscFunctionBeginUser;
32*6c88e6a2Srezgarshakeri 
33*6c88e6a2Srezgarshakeri   op_error_ctx->comm = comm;
34*6c88e6a2Srezgarshakeri   op_error_ctx->dm = dm;
35*6c88e6a2Srezgarshakeri   op_error_ctx->X_loc = X_loc;
36*6c88e6a2Srezgarshakeri   ierr = VecDuplicate(X_loc, &op_error_ctx->Y_loc); CHKERRQ(ierr);
37*6c88e6a2Srezgarshakeri   op_error_ctx->x_ceed = ceed_data->x_ceed;
38*6c88e6a2Srezgarshakeri   op_error_ctx->y_ceed = ceed_data->y_ceed;
39*6c88e6a2Srezgarshakeri   op_error_ctx->op = op_error;
40*6c88e6a2Srezgarshakeri   op_error_ctx->ceed = ceed;
41*6c88e6a2Srezgarshakeri   PetscFunctionReturn(0);
42*6c88e6a2Srezgarshakeri }
43*6c88e6a2Srezgarshakeri 
44*6c88e6a2Srezgarshakeri // -----------------------------------------------------------------------------
45e83e87a5Sjeremylt // This function returns the computed diagonal of the operator
46e83e87a5Sjeremylt // -----------------------------------------------------------------------------
47e83e87a5Sjeremylt PetscErrorCode MatGetDiag(Mat A, Vec D) {
48e83e87a5Sjeremylt   PetscErrorCode ierr;
49d4d45553Srezgarshakeri   OperatorApplyContext op_apply_ctx;
50e83e87a5Sjeremylt 
51e83e87a5Sjeremylt   PetscFunctionBeginUser;
52e83e87a5Sjeremylt 
53d4d45553Srezgarshakeri   ierr = MatShellGetContext(A, &op_apply_ctx); CHKERRQ(ierr);
54e83e87a5Sjeremylt 
55e83e87a5Sjeremylt   // Compute Diagonal via libCEED
56f1c40530SJeremy L Thompson   PetscScalar *y;
579b072555Sjeremylt   PetscMemType mem_type;
58e83e87a5Sjeremylt 
59e83e87a5Sjeremylt   // -- Place PETSc vector in libCEED vector
60d4d45553Srezgarshakeri   ierr = VecGetArrayAndMemType(op_apply_ctx->Y_loc, &y, &mem_type); CHKERRQ(ierr);
61d4d45553Srezgarshakeri   CeedVectorSetArray(op_apply_ctx->y_ceed, MemTypeP2C(mem_type),
62d4d45553Srezgarshakeri                      CEED_USE_POINTER, y);
63e83e87a5Sjeremylt 
64e83e87a5Sjeremylt   // -- Compute Diagonal
65d4d45553Srezgarshakeri   CeedOperatorLinearAssembleDiagonal(op_apply_ctx->op, op_apply_ctx->y_ceed,
66e83e87a5Sjeremylt                                      CEED_REQUEST_IMMEDIATE);
67e83e87a5Sjeremylt 
68e83e87a5Sjeremylt   // -- Local-to-Global
69d4d45553Srezgarshakeri   CeedVectorTakeArray(op_apply_ctx->y_ceed, MemTypeP2C(mem_type), NULL);
70d4d45553Srezgarshakeri   ierr = VecRestoreArrayAndMemType(op_apply_ctx->Y_loc, &y); CHKERRQ(ierr);
71e83e87a5Sjeremylt   ierr = VecZeroEntries(D); CHKERRQ(ierr);
72d4d45553Srezgarshakeri   ierr = DMLocalToGlobal(op_apply_ctx->dm, op_apply_ctx->Y_loc, ADD_VALUES, D);
73d4d45553Srezgarshakeri   CHKERRQ(ierr);
74e83e87a5Sjeremylt 
75e83e87a5Sjeremylt   PetscFunctionReturn(0);
76e83e87a5Sjeremylt };
77e83e87a5Sjeremylt 
78e83e87a5Sjeremylt // -----------------------------------------------------------------------------
79e83e87a5Sjeremylt // This function uses libCEED to compute the action of the Laplacian with
80e83e87a5Sjeremylt // Dirichlet boundary conditions
81e83e87a5Sjeremylt // -----------------------------------------------------------------------------
82d4d45553Srezgarshakeri PetscErrorCode ApplyLocal_Ceed(Vec X, Vec Y,
83d4d45553Srezgarshakeri                                OperatorApplyContext op_apply_ctx) {
84e83e87a5Sjeremylt   PetscErrorCode ierr;
85e83e87a5Sjeremylt   PetscScalar *x, *y;
869b072555Sjeremylt   PetscMemType x_mem_type, y_mem_type;
87e83e87a5Sjeremylt 
88e83e87a5Sjeremylt   PetscFunctionBeginUser;
89e83e87a5Sjeremylt 
90e83e87a5Sjeremylt   // Global-to-local
91d4d45553Srezgarshakeri   ierr = DMGlobalToLocal(op_apply_ctx->dm, X, INSERT_VALUES, op_apply_ctx->X_loc);
92d4d45553Srezgarshakeri   CHKERRQ(ierr);
93e83e87a5Sjeremylt 
94e83e87a5Sjeremylt   // Setup libCEED vectors
95d4d45553Srezgarshakeri   ierr = VecGetArrayReadAndMemType(op_apply_ctx->X_loc, (const PetscScalar **)&x,
969b072555Sjeremylt                                    &x_mem_type); CHKERRQ(ierr);
97d4d45553Srezgarshakeri   ierr = VecGetArrayAndMemType(op_apply_ctx->Y_loc, &y, &y_mem_type);
98d4d45553Srezgarshakeri   CHKERRQ(ierr);
99d4d45553Srezgarshakeri   CeedVectorSetArray(op_apply_ctx->x_ceed, MemTypeP2C(x_mem_type),
100d4d45553Srezgarshakeri                      CEED_USE_POINTER, x);
101d4d45553Srezgarshakeri   CeedVectorSetArray(op_apply_ctx->y_ceed, MemTypeP2C(y_mem_type),
102d4d45553Srezgarshakeri                      CEED_USE_POINTER, y);
103e83e87a5Sjeremylt 
104e83e87a5Sjeremylt   // Apply libCEED operator
105d4d45553Srezgarshakeri   CeedOperatorApply(op_apply_ctx->op, op_apply_ctx->x_ceed, op_apply_ctx->y_ceed,
106d4d45553Srezgarshakeri                     CEED_REQUEST_IMMEDIATE);
107e83e87a5Sjeremylt 
108e83e87a5Sjeremylt   // Restore PETSc vectors
109d4d45553Srezgarshakeri   CeedVectorTakeArray(op_apply_ctx->x_ceed, MemTypeP2C(x_mem_type), NULL);
110d4d45553Srezgarshakeri   CeedVectorTakeArray(op_apply_ctx->y_ceed, MemTypeP2C(y_mem_type), NULL);
111d4d45553Srezgarshakeri   ierr = VecRestoreArrayReadAndMemType(op_apply_ctx->X_loc,
112d4d45553Srezgarshakeri                                        (const PetscScalar **)&x);
113e83e87a5Sjeremylt   CHKERRQ(ierr);
114d4d45553Srezgarshakeri   ierr = VecRestoreArrayAndMemType(op_apply_ctx->Y_loc, &y); CHKERRQ(ierr);
115e83e87a5Sjeremylt 
116e83e87a5Sjeremylt   // Local-to-global
117e83e87a5Sjeremylt   ierr = VecZeroEntries(Y); CHKERRQ(ierr);
118d4d45553Srezgarshakeri   ierr = DMLocalToGlobal(op_apply_ctx->dm, op_apply_ctx->Y_loc, ADD_VALUES, Y);
119d4d45553Srezgarshakeri   CHKERRQ(ierr);
120e83e87a5Sjeremylt 
121e83e87a5Sjeremylt   PetscFunctionReturn(0);
122e83e87a5Sjeremylt };
123e83e87a5Sjeremylt 
124e83e87a5Sjeremylt // -----------------------------------------------------------------------------
125e83e87a5Sjeremylt // This function wraps the libCEED operator for a MatShell
126e83e87a5Sjeremylt // -----------------------------------------------------------------------------
127e83e87a5Sjeremylt PetscErrorCode MatMult_Ceed(Mat A, Vec X, Vec Y) {
128e83e87a5Sjeremylt   PetscErrorCode ierr;
129d4d45553Srezgarshakeri   OperatorApplyContext op_apply_ctx;
130e83e87a5Sjeremylt 
131e83e87a5Sjeremylt   PetscFunctionBeginUser;
132e83e87a5Sjeremylt 
133d4d45553Srezgarshakeri   ierr = MatShellGetContext(A, &op_apply_ctx); CHKERRQ(ierr);
134e83e87a5Sjeremylt 
135e83e87a5Sjeremylt   // libCEED for local action of residual evaluator
136d4d45553Srezgarshakeri   ierr = ApplyLocal_Ceed(X, Y, op_apply_ctx); CHKERRQ(ierr);
137e83e87a5Sjeremylt 
138e83e87a5Sjeremylt   PetscFunctionReturn(0);
139e83e87a5Sjeremylt };
140e83e87a5Sjeremylt 
141e83e87a5Sjeremylt // -----------------------------------------------------------------------------
142e83e87a5Sjeremylt // This function wraps the libCEED operator for a SNES residual evaluation
143e83e87a5Sjeremylt // -----------------------------------------------------------------------------
144e83e87a5Sjeremylt PetscErrorCode FormResidual_Ceed(SNES snes, Vec X, Vec Y, void *ctx) {
145e83e87a5Sjeremylt   PetscErrorCode ierr;
146d4d45553Srezgarshakeri   OperatorApplyContext op_apply_ctx = (OperatorApplyContext)ctx;
147e83e87a5Sjeremylt 
148e83e87a5Sjeremylt   PetscFunctionBeginUser;
149e83e87a5Sjeremylt 
150e83e87a5Sjeremylt   // libCEED for local action of residual evaluator
151d4d45553Srezgarshakeri   ierr = ApplyLocal_Ceed(X, Y, op_apply_ctx); CHKERRQ(ierr);
152e83e87a5Sjeremylt 
153e83e87a5Sjeremylt   PetscFunctionReturn(0);
154e83e87a5Sjeremylt };
155e83e87a5Sjeremylt 
156e83e87a5Sjeremylt // -----------------------------------------------------------------------------
157e83e87a5Sjeremylt // This function uses libCEED to compute the action of the prolongation operator
158e83e87a5Sjeremylt // -----------------------------------------------------------------------------
159e83e87a5Sjeremylt PetscErrorCode MatMult_Prolong(Mat A, Vec X, Vec Y) {
160e83e87a5Sjeremylt   PetscErrorCode ierr;
161d4d45553Srezgarshakeri   ProlongRestrContext pr_restr_ctx;
162e83e87a5Sjeremylt   PetscScalar *c, *f;
1639b072555Sjeremylt   PetscMemType c_mem_type, f_mem_type;
164e83e87a5Sjeremylt 
165e83e87a5Sjeremylt   PetscFunctionBeginUser;
166e83e87a5Sjeremylt 
167d4d45553Srezgarshakeri   ierr = MatShellGetContext(A, &pr_restr_ctx); CHKERRQ(ierr);
168e83e87a5Sjeremylt 
169e83e87a5Sjeremylt   // Global-to-local
170d4d45553Srezgarshakeri   ierr = VecZeroEntries(pr_restr_ctx->loc_vec_c); CHKERRQ(ierr);
171d4d45553Srezgarshakeri   ierr = DMGlobalToLocal(pr_restr_ctx->dmc, X, INSERT_VALUES,
172d4d45553Srezgarshakeri                          pr_restr_ctx->loc_vec_c);
173e83e87a5Sjeremylt   CHKERRQ(ierr);
174e83e87a5Sjeremylt 
175e83e87a5Sjeremylt   // Setup libCEED vectors
176d4d45553Srezgarshakeri   ierr = VecGetArrayReadAndMemType(pr_restr_ctx->loc_vec_c,
177d4d45553Srezgarshakeri                                    (const PetscScalar **)&c,
1789b072555Sjeremylt                                    &c_mem_type); CHKERRQ(ierr);
179d4d45553Srezgarshakeri   ierr = VecGetArrayAndMemType(pr_restr_ctx->loc_vec_f, &f, &f_mem_type);
180d4d45553Srezgarshakeri   CHKERRQ(ierr);
181d4d45553Srezgarshakeri   CeedVectorSetArray(pr_restr_ctx->ceed_vec_c, MemTypeP2C(c_mem_type),
182d4d45553Srezgarshakeri                      CEED_USE_POINTER, c);
183d4d45553Srezgarshakeri   CeedVectorSetArray(pr_restr_ctx->ceed_vec_f, MemTypeP2C(f_mem_type),
184d4d45553Srezgarshakeri                      CEED_USE_POINTER, f);
185e83e87a5Sjeremylt 
186e83e87a5Sjeremylt   // Apply libCEED operator
187d4d45553Srezgarshakeri   CeedOperatorApply(pr_restr_ctx->op_prolong, pr_restr_ctx->ceed_vec_c,
188d4d45553Srezgarshakeri                     pr_restr_ctx->ceed_vec_f, CEED_REQUEST_IMMEDIATE);
189e83e87a5Sjeremylt 
190e83e87a5Sjeremylt   // Restore PETSc vectors
191d4d45553Srezgarshakeri   CeedVectorTakeArray(pr_restr_ctx->ceed_vec_c, MemTypeP2C(c_mem_type), NULL);
192d4d45553Srezgarshakeri   CeedVectorTakeArray(pr_restr_ctx->ceed_vec_f, MemTypeP2C(f_mem_type), NULL);
193d4d45553Srezgarshakeri   ierr = VecRestoreArrayReadAndMemType(pr_restr_ctx->loc_vec_c,
194d4d45553Srezgarshakeri                                        (const PetscScalar **)&c);
195e83e87a5Sjeremylt   CHKERRQ(ierr);
196d4d45553Srezgarshakeri   ierr = VecRestoreArrayAndMemType(pr_restr_ctx->loc_vec_f, &f); CHKERRQ(ierr);
197e83e87a5Sjeremylt 
198e83e87a5Sjeremylt   // Multiplicity
199d4d45553Srezgarshakeri   ierr = VecPointwiseMult(pr_restr_ctx->loc_vec_f, pr_restr_ctx->loc_vec_f,
200d4d45553Srezgarshakeri                           pr_restr_ctx->mult_vec);
201e83e87a5Sjeremylt 
202e83e87a5Sjeremylt   // Local-to-global
203e83e87a5Sjeremylt   ierr = VecZeroEntries(Y); CHKERRQ(ierr);
204d4d45553Srezgarshakeri   ierr = DMLocalToGlobal(pr_restr_ctx->dmf, pr_restr_ctx->loc_vec_f, ADD_VALUES,
205d4d45553Srezgarshakeri                          Y); CHKERRQ(ierr);
206e83e87a5Sjeremylt 
207e83e87a5Sjeremylt   PetscFunctionReturn(0);
208e83e87a5Sjeremylt };
209e83e87a5Sjeremylt 
210e83e87a5Sjeremylt // -----------------------------------------------------------------------------
211e83e87a5Sjeremylt // This function uses libCEED to compute the action of the restriction operator
212e83e87a5Sjeremylt // -----------------------------------------------------------------------------
213e83e87a5Sjeremylt PetscErrorCode MatMult_Restrict(Mat A, Vec X, Vec Y) {
214e83e87a5Sjeremylt   PetscErrorCode ierr;
215d4d45553Srezgarshakeri   ProlongRestrContext pr_restr_ctx;
216e83e87a5Sjeremylt   PetscScalar *c, *f;
2179b072555Sjeremylt   PetscMemType c_mem_type, f_mem_type;
218e83e87a5Sjeremylt 
219e83e87a5Sjeremylt   PetscFunctionBeginUser;
220e83e87a5Sjeremylt 
221d4d45553Srezgarshakeri   ierr = MatShellGetContext(A, &pr_restr_ctx); CHKERRQ(ierr);
222e83e87a5Sjeremylt 
223e83e87a5Sjeremylt   // Global-to-local
224d4d45553Srezgarshakeri   ierr = VecZeroEntries(pr_restr_ctx->loc_vec_f); CHKERRQ(ierr);
225d4d45553Srezgarshakeri   ierr = DMGlobalToLocal(pr_restr_ctx->dmf, X, INSERT_VALUES,
226d4d45553Srezgarshakeri                          pr_restr_ctx->loc_vec_f);
227e83e87a5Sjeremylt   CHKERRQ(ierr);
228e83e87a5Sjeremylt 
229e83e87a5Sjeremylt   // Multiplicity
230d4d45553Srezgarshakeri   ierr = VecPointwiseMult(pr_restr_ctx->loc_vec_f, pr_restr_ctx->loc_vec_f,
231d4d45553Srezgarshakeri                           pr_restr_ctx->mult_vec);
232e83e87a5Sjeremylt   CHKERRQ(ierr);
233e83e87a5Sjeremylt 
234e83e87a5Sjeremylt   // Setup libCEED vectors
235d4d45553Srezgarshakeri   ierr = VecGetArrayReadAndMemType(pr_restr_ctx->loc_vec_f,
236d4d45553Srezgarshakeri                                    (const PetscScalar **)&f,
2379b072555Sjeremylt                                    &f_mem_type); CHKERRQ(ierr);
238d4d45553Srezgarshakeri   ierr = VecGetArrayAndMemType(pr_restr_ctx->loc_vec_c, &c, &c_mem_type);
239d4d45553Srezgarshakeri   CHKERRQ(ierr);
240d4d45553Srezgarshakeri   CeedVectorSetArray(pr_restr_ctx->ceed_vec_f, MemTypeP2C(f_mem_type),
241d4d45553Srezgarshakeri                      CEED_USE_POINTER, f);
242d4d45553Srezgarshakeri   CeedVectorSetArray(pr_restr_ctx->ceed_vec_c, MemTypeP2C(c_mem_type),
243d4d45553Srezgarshakeri                      CEED_USE_POINTER, c);
244e83e87a5Sjeremylt 
245e83e87a5Sjeremylt   // Apply CEED operator
246d4d45553Srezgarshakeri   CeedOperatorApply(pr_restr_ctx->op_restrict, pr_restr_ctx->ceed_vec_f,
247d4d45553Srezgarshakeri                     pr_restr_ctx->ceed_vec_c, CEED_REQUEST_IMMEDIATE);
248e83e87a5Sjeremylt 
249e83e87a5Sjeremylt   // Restore PETSc vectors
250d4d45553Srezgarshakeri   CeedVectorTakeArray(pr_restr_ctx->ceed_vec_c, MemTypeP2C(c_mem_type), NULL);
251d4d45553Srezgarshakeri   CeedVectorTakeArray(pr_restr_ctx->ceed_vec_f, MemTypeP2C(f_mem_type), NULL);
252d4d45553Srezgarshakeri   ierr = VecRestoreArrayReadAndMemType(pr_restr_ctx->loc_vec_f,
253d4d45553Srezgarshakeri                                        (const PetscScalar **)&f); CHKERRQ(ierr);
254d4d45553Srezgarshakeri   ierr = VecRestoreArrayAndMemType(pr_restr_ctx->loc_vec_c, &c); CHKERRQ(ierr);
255e83e87a5Sjeremylt 
256e83e87a5Sjeremylt   // Local-to-global
257e83e87a5Sjeremylt   ierr = VecZeroEntries(Y); CHKERRQ(ierr);
258d4d45553Srezgarshakeri   ierr = DMLocalToGlobal(pr_restr_ctx->dmc, pr_restr_ctx->loc_vec_c, ADD_VALUES,
259d4d45553Srezgarshakeri                          Y);
260e83e87a5Sjeremylt   CHKERRQ(ierr);
261e83e87a5Sjeremylt 
262e83e87a5Sjeremylt   PetscFunctionReturn(0);
263e83e87a5Sjeremylt };
264e83e87a5Sjeremylt 
265e83e87a5Sjeremylt // -----------------------------------------------------------------------------
266e83e87a5Sjeremylt // This function calculates the error in the final solution
267e83e87a5Sjeremylt // -----------------------------------------------------------------------------
268*6c88e6a2Srezgarshakeri PetscErrorCode ComputeErrorMax(OperatorApplyContext op_error_ctx,
269e83e87a5Sjeremylt                                Vec X, CeedVector target,
2709b072555Sjeremylt                                PetscScalar *max_error) {
271e83e87a5Sjeremylt   PetscErrorCode ierr;
272e83e87a5Sjeremylt   PetscScalar *x;
2739b072555Sjeremylt   PetscMemType mem_type;
274e83e87a5Sjeremylt   CeedVector collocated_error;
2751f9221feSJeremy L Thompson   CeedSize length;
276e83e87a5Sjeremylt 
277e83e87a5Sjeremylt   PetscFunctionBeginUser;
278e83e87a5Sjeremylt   CeedVectorGetLength(target, &length);
279*6c88e6a2Srezgarshakeri   CeedVectorCreate(op_error_ctx->ceed, length, &collocated_error);
280e83e87a5Sjeremylt 
281e83e87a5Sjeremylt   // Global-to-local
282*6c88e6a2Srezgarshakeri   ierr = DMGlobalToLocal(op_error_ctx->dm, X, INSERT_VALUES, op_error_ctx->X_loc);
283d4d45553Srezgarshakeri   CHKERRQ(ierr);
284e83e87a5Sjeremylt 
285e83e87a5Sjeremylt   // Setup CEED vector
286*6c88e6a2Srezgarshakeri   ierr = VecGetArrayAndMemType(op_error_ctx->X_loc, &x, &mem_type); CHKERRQ(ierr);
287*6c88e6a2Srezgarshakeri   CeedVectorSetArray(op_error_ctx->x_ceed, MemTypeP2C(mem_type),
288d4d45553Srezgarshakeri                      CEED_USE_POINTER, x);
289e83e87a5Sjeremylt 
290e83e87a5Sjeremylt   // Apply CEED operator
291*6c88e6a2Srezgarshakeri   CeedOperatorApply(op_error_ctx->op, op_error_ctx->x_ceed, collocated_error,
292e83e87a5Sjeremylt                     CEED_REQUEST_IMMEDIATE);
293e83e87a5Sjeremylt 
294e83e87a5Sjeremylt   // Restore PETSc vector
295*6c88e6a2Srezgarshakeri   CeedVectorTakeArray(op_error_ctx->x_ceed, MemTypeP2C(mem_type), NULL);
296*6c88e6a2Srezgarshakeri   ierr = VecRestoreArrayReadAndMemType(op_error_ctx->X_loc,
297d4d45553Srezgarshakeri                                        (const PetscScalar **)&x);
298e83e87a5Sjeremylt   CHKERRQ(ierr);
299e83e87a5Sjeremylt 
300e83e87a5Sjeremylt   // Reduce max error
3019b072555Sjeremylt   *max_error = 0;
302e83e87a5Sjeremylt   const CeedScalar *e;
303e83e87a5Sjeremylt   CeedVectorGetArrayRead(collocated_error, CEED_MEM_HOST, &e);
304e83e87a5Sjeremylt   for (CeedInt i=0; i<length; i++) {
3059b072555Sjeremylt     *max_error = PetscMax(*max_error, PetscAbsScalar(e[i]));
306e83e87a5Sjeremylt   }
307e83e87a5Sjeremylt   CeedVectorRestoreArrayRead(collocated_error, &e);
3089b072555Sjeremylt   ierr = MPI_Allreduce(MPI_IN_PLACE, max_error, 1, MPIU_REAL, MPIU_MAX,
309*6c88e6a2Srezgarshakeri                        op_error_ctx->comm);
310e83e87a5Sjeremylt   CHKERRQ(ierr);
311e83e87a5Sjeremylt 
312e83e87a5Sjeremylt   // Cleanup
313e83e87a5Sjeremylt   CeedVectorDestroy(&collocated_error);
314e83e87a5Sjeremylt 
315e83e87a5Sjeremylt   PetscFunctionReturn(0);
316e83e87a5Sjeremylt };
317e83e87a5Sjeremylt 
318e83e87a5Sjeremylt // -----------------------------------------------------------------------------
319