xref: /honee/src/mat-ceed.c (revision 397c018740ff2982e3a40a731b4e72707218072e)
1 /// @file
2 /// MatCEED implementation
3 
4 #include <ceed.h>
5 #include <ceed/backend.h>
6 #include <mat-ceed-impl.h>
7 #include <mat-ceed.h>
8 #include <petsc-ceed-utils.h>
9 #include <petsc-ceed.h>
10 #include <petscdm.h>
11 #include <petscmat.h>
12 #include <stdbool.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 PetscClassId  MATCEED_CLASSID;
18 PetscLogEvent MATCEED_MULT, MATCEED_MULT_CEEDOP, MATCEED_MULT_TRANSPOSE, MATCEED_MULT_TRANSPOSE_CEEDOP, MATCEED_ASSEMBLE_DIAGONAL,
19     MATCEED_ASSEMBLE_DIAGONAL_CEEDOP, MATCEED_SETUP_PBDIAGONAL, MATCEED_SETUP_PBDIAGONAL_CEEDOP, MATCEED_ASSEMBLE_PBDIAGONAL,
20     MATCEED_ASSEMBLE_PBDIAGONAL_CEEDOP, MATCEED_SETUP_FULL, MATCEED_SETUP_FULL_CEEDOP, MATCEED_ASSEMBLE_FULL, MATCEED_ASSEMBLE_FULL_CEEDOP;
21 
22 /**
23   @brief Register MATCEED log events.
24 
25   Not collective across MPI processes.
26 
27   @return An error code: 0 - success, otherwise - failure
28 **/
29 static PetscErrorCode MatCeedRegisterLogEvents() {
30   static PetscBool registered = PETSC_FALSE;
31 
32   PetscFunctionBeginUser;
33   if (registered) PetscFunctionReturn(PETSC_SUCCESS);
34   PetscCall(PetscClassIdRegister("MatCEED", &MATCEED_CLASSID));
35   PetscCall(PetscLogEventRegister("MatCEEDMul", MATCEED_CLASSID, &MATCEED_MULT));
36   PetscCall(PetscLogEventRegister("MatCEEDMulCeed", MATCEED_CLASSID, &MATCEED_MULT_CEEDOP));
37   PetscCall(PetscLogEventRegister("MatCEEDMulT", MATCEED_CLASSID, &MATCEED_MULT_TRANSPOSE));
38   PetscCall(PetscLogEventRegister("MatCEEDMulTCeed", MATCEED_CLASSID, &MATCEED_MULT_TRANSPOSE_CEEDOP));
39   PetscCall(PetscLogEventRegister("MatCEEDAsmDiag", MATCEED_CLASSID, &MATCEED_ASSEMBLE_DIAGONAL));
40   PetscCall(PetscLogEventRegister("MatCEEDAsmPBDSU", MATCEED_CLASSID, &MATCEED_SETUP_PBDIAGONAL));
41   PetscCall(PetscLogEventRegister("MatCEEDAsmPBDSUCeed", MATCEED_CLASSID, &MATCEED_SETUP_PBDIAGONAL_CEEDOP));
42   PetscCall(PetscLogEventRegister("MatCEEDAsmPBD", MATCEED_CLASSID, &MATCEED_ASSEMBLE_PBDIAGONAL));
43   PetscCall(PetscLogEventRegister("MatCEEDAsmPBDCeed", MATCEED_CLASSID, &MATCEED_ASSEMBLE_PBDIAGONAL_CEEDOP));
44   PetscCall(PetscLogEventRegister("MatCEEDAsmSU", MATCEED_CLASSID, &MATCEED_SETUP_FULL));
45   PetscCall(PetscLogEventRegister("MatCEEDAsmSUCeed", MATCEED_CLASSID, &MATCEED_SETUP_FULL_CEEDOP));
46   PetscCall(PetscLogEventRegister("MatCEEDAsm", MATCEED_CLASSID, &MATCEED_ASSEMBLE_FULL));
47   PetscCall(PetscLogEventRegister("MatCEEDAsmCeed", MATCEED_CLASSID, &MATCEED_ASSEMBLE_FULL_CEEDOP));
48   registered = PETSC_TRUE;
49   PetscFunctionReturn(PETSC_SUCCESS);
50 }
51 
52 /**
53   @brief Assemble the point block diagonal of a `MATCEED` into a `MATAIJ` or similar.
54          The `mat_coo` preallocation is set to match the sparsity pattern of `mat_ceed`.
55          The caller is responsible for assuring the global and local sizes are compatible, otherwise this function will fail.
56 
57   Collective across MPI processes.
58 
59   @param[in]      mat_ceed  `MATCEED` to assemble
60   @param[in,out]  mat_coo   `MATAIJ` or similar to assemble into
61 
62   @return An error code: 0 - success, otherwise - failure
63 **/
64 static PetscErrorCode MatCeedAssemblePointBlockDiagonalCOO(Mat mat_ceed, Mat mat_coo) {
65   MatCeedContext ctx;
66 
67   PetscFunctionBeginUser;
68   PetscCall(MatShellGetContext(mat_ceed, &ctx));
69 
70   // Check if COO pattern set
71   {
72     PetscInt index = -1;
73 
74     for (PetscInt i = 0; i < ctx->num_mats_assembled_pbd; i++) {
75       if (ctx->mats_assembled_pbd[i] == mat_coo) index = i;
76     }
77     if (index == -1) {
78       PetscInt     *rows_petsc = NULL, *cols_petsc = NULL;
79       CeedInt      *rows_ceed, *cols_ceed;
80       PetscCount    num_entries;
81       PetscLogStage stage_amg_setup;
82 
83       // -- Assemble sparsity pattern if mat hasn't been assembled before
84       PetscCall(PetscLogStageGetId("MatCEED Asm Setup", &stage_amg_setup));
85       if (stage_amg_setup == -1) {
86         PetscCall(PetscLogStageRegister("MatCEED Asm Setup", &stage_amg_setup));
87       }
88       PetscCall(PetscLogStagePush(stage_amg_setup));
89       PetscCall(PetscLogEventBegin(MATCEED_SETUP_PBDIAGONAL, mat_ceed, mat_coo, NULL, NULL));
90       PetscCall(PetscLogEventBegin(MATCEED_SETUP_PBDIAGONAL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
91       PetscCallCeed(ctx->ceed, CeedOperatorLinearAssemblePointBlockDiagonalSymbolic(ctx->op_mult, &num_entries, &rows_ceed, &cols_ceed));
92       PetscCall(PetscLogEventEnd(MATCEED_SETUP_PBDIAGONAL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
93       PetscCall(IntArrayCeedToPetsc(num_entries, &rows_ceed, &rows_petsc));
94       PetscCall(IntArrayCeedToPetsc(num_entries, &cols_ceed, &cols_petsc));
95       PetscCall(MatSetPreallocationCOOLocal(mat_coo, num_entries, rows_petsc, cols_petsc));
96       free(rows_petsc);
97       free(cols_petsc);
98       if (!ctx->coo_values_pbd) PetscCallCeed(ctx->ceed, CeedVectorCreate(ctx->ceed, num_entries, &ctx->coo_values_pbd));
99       PetscCall(PetscRealloc(++ctx->num_mats_assembled_pbd * sizeof(Mat), &ctx->mats_assembled_pbd));
100       ctx->mats_assembled_pbd[ctx->num_mats_assembled_pbd - 1] = mat_coo;
101       PetscCall(PetscLogEventEnd(MATCEED_SETUP_PBDIAGONAL, mat_ceed, mat_coo, NULL, NULL));
102       PetscCall(PetscLogStagePop());
103     }
104   }
105 
106   // Assemble mat_ceed
107   PetscCall(PetscLogEventBegin(MATCEED_ASSEMBLE_PBDIAGONAL, mat_ceed, mat_coo, NULL, NULL));
108   PetscCall(MatAssemblyBegin(mat_coo, MAT_FINAL_ASSEMBLY));
109   {
110     const CeedScalar *values;
111     MatType           mat_type;
112     CeedMemType       mem_type = CEED_MEM_HOST;
113     PetscBool         is_spd, is_spd_known;
114 
115     PetscCall(MatGetType(mat_coo, &mat_type));
116     if (strstr(mat_type, "cusparse")) mem_type = CEED_MEM_DEVICE;
117     else if (strstr(mat_type, "kokkos")) mem_type = CEED_MEM_DEVICE;
118     else mem_type = CEED_MEM_HOST;
119 
120     PetscCall(PetscLogEventBegin(MATCEED_ASSEMBLE_PBDIAGONAL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
121     PetscCallCeed(ctx->ceed, CeedOperatorLinearAssemblePointBlockDiagonal(ctx->op_mult, ctx->coo_values_pbd, CEED_REQUEST_IMMEDIATE));
122     PetscCall(PetscLogEventEnd(MATCEED_ASSEMBLE_PBDIAGONAL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
123     PetscCallCeed(ctx->ceed, CeedVectorGetArrayRead(ctx->coo_values_pbd, mem_type, &values));
124     PetscCall(MatSetValuesCOO(mat_coo, values, INSERT_VALUES));
125     PetscCall(MatIsSPDKnown(mat_ceed, &is_spd_known, &is_spd));
126     if (is_spd_known) PetscCall(MatSetOption(mat_coo, MAT_SPD, is_spd));
127     PetscCallCeed(ctx->ceed, CeedVectorRestoreArrayRead(ctx->coo_values_pbd, &values));
128   }
129   PetscCall(MatAssemblyEnd(mat_coo, MAT_FINAL_ASSEMBLY));
130   PetscCall(PetscLogEventEnd(MATCEED_ASSEMBLE_PBDIAGONAL, mat_ceed, mat_coo, NULL, NULL));
131   PetscFunctionReturn(PETSC_SUCCESS);
132 }
133 
134 /**
135   @brief Assemble inner `Mat` for diagonal `PC` operations
136 
137   Collective across MPI processes.
138 
139   @param[in]   mat_ceed      `MATCEED` to invert
140   @param[in]   use_ceed_pbd  Boolean flag to use libCEED PBD assembly
141   @param[out]  mat_inner     Inner `Mat` for diagonal operations
142 
143   @return An error code: 0 - success, otherwise - failure
144 **/
145 static PetscErrorCode MatCeedAssembleInnerBlockDiagonalMat(Mat mat_ceed, PetscBool use_ceed_pbd, Mat *mat_inner) {
146   MatCeedContext ctx;
147 
148   PetscFunctionBeginUser;
149   PetscCall(MatShellGetContext(mat_ceed, &ctx));
150   if (use_ceed_pbd) {
151     // Check if COO pattern set
152     if (!ctx->mat_assembled_pbd_internal) PetscCall(MatCeedCreateMatCOO(mat_ceed, &ctx->mat_assembled_pbd_internal));
153 
154     // Assemble mat_assembled_full_internal
155     PetscCall(MatCeedAssemblePointBlockDiagonalCOO(mat_ceed, ctx->mat_assembled_pbd_internal));
156     if (mat_inner) *mat_inner = ctx->mat_assembled_pbd_internal;
157   } else {
158     // Check if COO pattern set
159     if (!ctx->mat_assembled_full_internal) PetscCall(MatCeedCreateMatCOO(mat_ceed, &ctx->mat_assembled_full_internal));
160 
161     // Assemble mat_assembled_full_internal
162     PetscCall(MatCeedAssembleCOO(mat_ceed, ctx->mat_assembled_full_internal));
163     if (mat_inner) *mat_inner = ctx->mat_assembled_full_internal;
164   }
165   PetscFunctionReturn(PETSC_SUCCESS);
166 }
167 
168 /**
169   @brief Get `MATCEED` diagonal block for Jacobi.
170 
171   Collective across MPI processes.
172 
173   @param[in]   mat_ceed   `MATCEED` to invert
174   @param[out]  mat_block  The diagonal block matrix
175 
176   @return An error code: 0 - success, otherwise - failure
177 **/
178 static PetscErrorCode MatGetDiagonalBlock_Ceed(Mat mat_ceed, Mat *mat_block) {
179   Mat            mat_inner = NULL;
180   MatCeedContext ctx;
181 
182   PetscFunctionBeginUser;
183   PetscCall(MatShellGetContext(mat_ceed, &ctx));
184 
185   // Assemble inner mat if needed
186   PetscCall(MatCeedAssembleInnerBlockDiagonalMat(mat_ceed, ctx->is_ceed_pbd_valid, &mat_inner));
187 
188   // Get block diagonal
189   PetscCall(MatGetDiagonalBlock(mat_inner, mat_block));
190   PetscFunctionReturn(PETSC_SUCCESS);
191 }
192 
193 /**
194   @brief Invert `MATCEED` diagonal block for Jacobi.
195 
196   Collective across MPI processes.
197 
198   @param[in]   mat_ceed  `MATCEED` to invert
199   @param[out]  values    The block inverses in column major order
200 
201   @return An error code: 0 - success, otherwise - failure
202 **/
203 static PetscErrorCode MatInvertBlockDiagonal_Ceed(Mat mat_ceed, const PetscScalar **values) {
204   Mat            mat_inner = NULL;
205   MatCeedContext ctx;
206 
207   PetscFunctionBeginUser;
208   PetscCall(MatShellGetContext(mat_ceed, &ctx));
209 
210   // Assemble inner mat if needed
211   PetscCall(MatCeedAssembleInnerBlockDiagonalMat(mat_ceed, ctx->is_ceed_pbd_valid, &mat_inner));
212 
213   // Invert PB diagonal
214   PetscCall(MatInvertBlockDiagonal(mat_inner, values));
215   PetscFunctionReturn(PETSC_SUCCESS);
216 }
217 
218 /**
219   @brief Invert `MATCEED` variable diagonal block for Jacobi.
220 
221   Collective across MPI processes.
222 
223   @param[in]   mat_ceed     `MATCEED` to invert
224   @param[in]   num_blocks   The number of blocks on the process
225   @param[in]   block_sizes  The size of each block on the process
226   @param[out]  values       The block inverses in column major order
227 
228   @return An error code: 0 - success, otherwise - failure
229 **/
230 static PetscErrorCode MatInvertVariableBlockDiagonal_Ceed(Mat mat_ceed, PetscInt num_blocks, const PetscInt *block_sizes, PetscScalar *values) {
231   Mat            mat_inner = NULL;
232   MatCeedContext ctx;
233 
234   PetscFunctionBeginUser;
235   PetscCall(MatShellGetContext(mat_ceed, &ctx));
236 
237   // Assemble inner mat if needed
238   PetscCall(MatCeedAssembleInnerBlockDiagonalMat(mat_ceed, ctx->is_ceed_vpbd_valid, &mat_inner));
239 
240   // Invert PB diagonal
241   PetscCall(MatInvertVariableBlockDiagonal(mat_inner, num_blocks, block_sizes, values));
242   PetscFunctionReturn(PETSC_SUCCESS);
243 }
244 
245 /**
246   @brief View `MATCEED`.
247 
248   Collective across MPI processes.
249 
250   @param[in]   mat_ceed  `MATCEED` to view
251   @param[in]   viewer    The visualization context
252 
253   @return An error code: 0 - success, otherwise - failure
254 **/
255 static PetscErrorCode MatView_Ceed(Mat mat_ceed, PetscViewer viewer) {
256   PetscBool         is_ascii;
257   PetscViewerFormat format;
258   PetscMPIInt       size, rank;
259   MatCeedContext    ctx;
260 
261   PetscFunctionBeginUser;
262   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
263   PetscCall(MatShellGetContext(mat_ceed, &ctx));
264   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)mat_ceed), &viewer));
265 
266   PetscCall(PetscViewerGetFormat(viewer, &format));
267   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)mat_ceed), &size));
268   if (size == 1 && format == PETSC_VIEWER_LOAD_BALANCE) PetscFunctionReturn(PETSC_SUCCESS);
269 
270   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)mat_ceed), &rank));
271   if (rank != 0) PetscFunctionReturn(PETSC_SUCCESS);
272 
273   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &is_ascii));
274   {
275     PetscBool is_detailed     = format == PETSC_VIEWER_ASCII_INFO_DETAIL;
276     char      rank_string[16] = {'\0'};
277     FILE     *file;
278 
279     PetscCall(PetscViewerASCIIPrintf(viewer, "MatCEED:\n  Default COO MatType:%s\n", ctx->coo_mat_type));
280     PetscCall(PetscViewerASCIIGetPointer(viewer, &file));
281     PetscCall(PetscSNPrintf(rank_string, 16, "on Rank %d", rank));
282     PetscCall(PetscViewerASCIIPrintf(viewer, " CeedOperator Apply %s:\n", is_detailed ? rank_string : "Summary"));
283     if (is_detailed) PetscCallCeed(ctx->ceed, CeedOperatorView(ctx->op_mult, file));
284     else PetscCallCeed(ctx->ceed, CeedOperatorViewTerse(ctx->op_mult, file));
285     if (ctx->op_mult_transpose) {
286       PetscCall(PetscViewerASCIIPrintf(viewer, "  CeedOperator ApplyTranspose %s:\n", is_detailed ? rank_string : "Summary"));
287       if (is_detailed) PetscCallCeed(ctx->ceed, CeedOperatorView(ctx->op_mult_transpose, file));
288       else PetscCallCeed(ctx->ceed, CeedOperatorViewTerse(ctx->op_mult_transpose, file));
289     }
290   }
291   PetscFunctionReturn(PETSC_SUCCESS);
292 }
293 
294 // -----------------------------------------------------------------------------
295 // MatCeed
296 // -----------------------------------------------------------------------------
297 
298 /**
299   @brief Create PETSc `Mat` from libCEED operators.
300 
301   Collective across MPI processes.
302 
303   @param[in]   dm_x                      Input `DM`
304   @param[in]   dm_y                      Output `DM`
305   @param[in]   op_mult                   `CeedOperator` for forward evaluation
306   @param[in]   op_mult_transpose         `CeedOperator` for transpose evaluation
307   @param[out]  mat                        New MatCeed
308 
309   @return An error code: 0 - success, otherwise - failure
310 **/
311 PetscErrorCode MatCreateCeed(DM dm_x, DM dm_y, CeedOperator op_mult, CeedOperator op_mult_transpose, Mat *mat) {
312   PetscInt       X_l_size, X_g_size, Y_l_size, Y_g_size;
313   VecType        vec_type;
314   MatCeedContext ctx;
315 
316   PetscFunctionBeginUser;
317   PetscCall(MatCeedRegisterLogEvents());
318 
319   // Collect context data
320   PetscCall(DMGetVecType(dm_x, &vec_type));
321   {
322     Vec X;
323 
324     PetscCall(DMGetGlobalVector(dm_x, &X));
325     PetscCall(VecGetSize(X, &X_g_size));
326     PetscCall(VecGetLocalSize(X, &X_l_size));
327     PetscCall(DMRestoreGlobalVector(dm_x, &X));
328   }
329   if (dm_y) {
330     Vec Y;
331 
332     PetscCall(DMGetGlobalVector(dm_y, &Y));
333     PetscCall(VecGetSize(Y, &Y_g_size));
334     PetscCall(VecGetLocalSize(Y, &Y_l_size));
335     PetscCall(DMRestoreGlobalVector(dm_y, &Y));
336   } else {
337     dm_y     = dm_x;
338     Y_g_size = X_g_size;
339     Y_l_size = X_l_size;
340   }
341 
342   // Create context
343   {
344     Vec X_loc, Y_loc_transpose = NULL;
345 
346     PetscCall(DMCreateLocalVector(dm_x, &X_loc));
347     PetscCall(VecZeroEntries(X_loc));
348     if (op_mult_transpose) {
349       PetscCall(DMCreateLocalVector(dm_y, &Y_loc_transpose));
350       PetscCall(VecZeroEntries(Y_loc_transpose));
351     }
352     PetscCall(MatCeedContextCreate(dm_x, dm_y, X_loc, Y_loc_transpose, op_mult, op_mult_transpose, MATCEED_MULT, MATCEED_MULT_TRANSPOSE,
353                                    MATCEED_MULT_CEEDOP, MATCEED_MULT_TRANSPOSE_CEEDOP, &ctx));
354     PetscCall(VecDestroy(&X_loc));
355     PetscCall(VecDestroy(&Y_loc_transpose));
356   }
357 
358   // Create mat
359   PetscCall(MatCreateShell(PetscObjectComm((PetscObject)dm_x), Y_l_size, X_l_size, Y_g_size, X_g_size, ctx, mat));
360   PetscCall(PetscObjectChangeTypeName((PetscObject)*mat, MATCEED));
361   // -- Set block and variable block sizes
362   if (dm_x == dm_y) {
363     MatType dm_mat_type, dm_mat_type_copy;
364     Mat     temp_mat;
365 
366     PetscCall(DMGetMatType(dm_x, &dm_mat_type));
367     PetscCall(PetscStrallocpy(dm_mat_type, (char **)&dm_mat_type_copy));
368     PetscCall(DMSetMatType(dm_x, MATAIJ));
369     PetscCall(DMCreateMatrix(dm_x, &temp_mat));
370     PetscCall(DMSetMatType(dm_x, dm_mat_type_copy));
371     PetscCall(PetscFree(dm_mat_type_copy));
372 
373     {
374       PetscInt        block_size, num_blocks, max_vblock_size = PETSC_INT_MAX;
375       const PetscInt *vblock_sizes;
376 
377       // -- Get block sizes
378       PetscCall(MatGetBlockSize(temp_mat, &block_size));
379       PetscCall(MatGetVariableBlockSizes(temp_mat, &num_blocks, &vblock_sizes));
380       {
381         PetscInt local_min_max[2] = {0}, global_min_max[2] = {0, PETSC_INT_MAX};
382 
383         for (PetscInt i = 0; i < num_blocks; i++) local_min_max[1] = PetscMax(local_min_max[1], vblock_sizes[i]);
384         PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm_x), local_min_max, global_min_max));
385         max_vblock_size = global_min_max[1];
386       }
387 
388       // -- Copy block sizes
389       if (block_size > 1) PetscCall(MatSetBlockSize(*mat, block_size));
390       if (num_blocks) PetscCall(MatSetVariableBlockSizes(*mat, num_blocks, (PetscInt *)vblock_sizes));
391 
392       // -- Check libCEED compatibility
393       {
394         bool is_composite;
395 
396         ctx->is_ceed_pbd_valid  = PETSC_TRUE;
397         ctx->is_ceed_vpbd_valid = PETSC_TRUE;
398         PetscCallCeed(ctx->ceed, CeedOperatorIsComposite(op_mult, &is_composite));
399         if (is_composite) {
400           CeedInt       num_sub_operators;
401           CeedOperator *sub_operators;
402 
403           PetscCallCeed(ctx->ceed, CeedCompositeOperatorGetNumSub(op_mult, &num_sub_operators));
404           PetscCallCeed(ctx->ceed, CeedCompositeOperatorGetSubList(op_mult, &sub_operators));
405           for (CeedInt i = 0; i < num_sub_operators; i++) {
406             CeedInt                  num_bases, num_comp;
407             CeedBasis               *active_bases;
408             CeedOperatorAssemblyData assembly_data;
409 
410             PetscCallCeed(ctx->ceed, CeedOperatorGetOperatorAssemblyData(sub_operators[i], &assembly_data));
411             PetscCallCeed(ctx->ceed, CeedOperatorAssemblyDataGetBases(assembly_data, &num_bases, &active_bases, NULL, NULL, NULL, NULL));
412             PetscCallCeed(ctx->ceed, CeedBasisGetNumComponents(active_bases[0], &num_comp));
413             if (num_bases > 1) {
414               ctx->is_ceed_pbd_valid  = PETSC_FALSE;
415               ctx->is_ceed_vpbd_valid = PETSC_FALSE;
416             }
417             if (num_comp != block_size) ctx->is_ceed_pbd_valid = PETSC_FALSE;
418             if (num_comp < max_vblock_size) ctx->is_ceed_vpbd_valid = PETSC_FALSE;
419           }
420         } else {
421           // LCOV_EXCL_START
422           CeedInt                  num_bases, num_comp;
423           CeedBasis               *active_bases;
424           CeedOperatorAssemblyData assembly_data;
425 
426           PetscCallCeed(ctx->ceed, CeedOperatorGetOperatorAssemblyData(op_mult, &assembly_data));
427           PetscCallCeed(ctx->ceed, CeedOperatorAssemblyDataGetBases(assembly_data, &num_bases, &active_bases, NULL, NULL, NULL, NULL));
428           PetscCallCeed(ctx->ceed, CeedBasisGetNumComponents(active_bases[0], &num_comp));
429           if (num_bases > 1) {
430             ctx->is_ceed_pbd_valid  = PETSC_FALSE;
431             ctx->is_ceed_vpbd_valid = PETSC_FALSE;
432           }
433           if (num_comp != block_size) ctx->is_ceed_pbd_valid = PETSC_FALSE;
434           if (num_comp < max_vblock_size) ctx->is_ceed_vpbd_valid = PETSC_FALSE;
435           // LCOV_EXCL_STOP
436         }
437         {
438           PetscInt local_is_valid[2], global_is_valid[2];
439 
440           local_is_valid[0] = local_is_valid[1] = ctx->is_ceed_pbd_valid;
441           PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm_x), local_is_valid, global_is_valid));
442           ctx->is_ceed_pbd_valid = global_is_valid[0];
443           local_is_valid[0] = local_is_valid[1] = ctx->is_ceed_vpbd_valid;
444           PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm_x), local_is_valid, global_is_valid));
445           ctx->is_ceed_vpbd_valid = global_is_valid[0];
446         }
447       }
448     }
449     PetscCall(MatDestroy(&temp_mat));
450   }
451   // -- Set internal mat type
452   {
453     VecType vec_type;
454     MatType coo_mat_type;
455 
456     PetscCall(VecGetType(ctx->X_loc, &vec_type));
457     if (strstr(vec_type, VECCUDA)) coo_mat_type = MATAIJCUSPARSE;
458     else if (strstr(vec_type, VECKOKKOS)) coo_mat_type = MATAIJKOKKOS;
459     else coo_mat_type = MATAIJ;
460     PetscCall(PetscStrallocpy(coo_mat_type, &ctx->coo_mat_type));
461   }
462   // -- Set mat operations
463   PetscCall(MatShellSetContextDestroy(*mat, (PetscErrorCode(*)(void *))MatCeedContextDestroy));
464   PetscCall(MatShellSetOperation(*mat, MATOP_VIEW, (void (*)(void))MatView_Ceed));
465   PetscCall(MatShellSetOperation(*mat, MATOP_MULT, (void (*)(void))MatMult_Ceed));
466   if (op_mult_transpose) PetscCall(MatShellSetOperation(*mat, MATOP_MULT_TRANSPOSE, (void (*)(void))MatMultTranspose_Ceed));
467   PetscCall(MatShellSetOperation(*mat, MATOP_GET_DIAGONAL, (void (*)(void))MatGetDiagonal_Ceed));
468   PetscCall(MatShellSetOperation(*mat, MATOP_GET_DIAGONAL_BLOCK, (void (*)(void))MatGetDiagonalBlock_Ceed));
469   PetscCall(MatShellSetOperation(*mat, MATOP_INVERT_BLOCK_DIAGONAL, (void (*)(void))MatInvertBlockDiagonal_Ceed));
470   PetscCall(MatShellSetOperation(*mat, MATOP_INVERT_VBLOCK_DIAGONAL, (void (*)(void))MatInvertVariableBlockDiagonal_Ceed));
471   PetscCall(MatShellSetVecType(*mat, vec_type));
472   PetscFunctionReturn(PETSC_SUCCESS);
473 }
474 
475 /**
476   @brief Copy `MATCEED` into a compatible `Mat` with type `MatShell` or `MATCEED`.
477 
478   Collective across MPI processes.
479 
480   @param[in]   mat_ceed   `MATCEED` to copy from
481   @param[out]  mat_other  `MatShell` or `MATCEED` to copy into
482 
483   @return An error code: 0 - success, otherwise - failure
484 **/
485 PetscErrorCode MatCeedCopy(Mat mat_ceed, Mat mat_other) {
486   PetscFunctionBeginUser;
487   PetscCall(MatCeedRegisterLogEvents());
488 
489   // Check type compatibility
490   {
491     PetscBool is_matceed = PETSC_FALSE, is_matshell = PETSC_FALSE;
492     MatType   mat_type_ceed, mat_type_other;
493 
494     PetscCall(MatGetType(mat_ceed, &mat_type_ceed));
495     PetscCall(PetscStrcmp(mat_type_ceed, MATCEED, &is_matceed));
496     PetscCheck(is_matceed, PETSC_COMM_SELF, PETSC_ERR_LIB, "mat_ceed must have type " MATCEED);
497     PetscCall(MatGetType(mat_other, &mat_type_other));
498     PetscCall(PetscStrcmp(mat_type_other, MATCEED, &is_matceed));
499     PetscCall(PetscStrcmp(mat_type_other, MATSHELL, &is_matceed));
500     PetscCheck(is_matceed || is_matshell, PETSC_COMM_SELF, PETSC_ERR_LIB, "mat_other must have type " MATCEED " or " MATSHELL);
501   }
502 
503   // Check dimension compatibility
504   {
505     PetscInt X_l_ceed_size, X_g_ceed_size, Y_l_ceed_size, Y_g_ceed_size, X_l_other_size, X_g_other_size, Y_l_other_size, Y_g_other_size;
506 
507     PetscCall(MatGetSize(mat_ceed, &Y_g_ceed_size, &X_g_ceed_size));
508     PetscCall(MatGetLocalSize(mat_ceed, &Y_l_ceed_size, &X_l_ceed_size));
509     PetscCall(MatGetSize(mat_ceed, &Y_g_other_size, &X_g_other_size));
510     PetscCall(MatGetLocalSize(mat_ceed, &Y_l_other_size, &X_l_other_size));
511     PetscCheck((Y_g_ceed_size == Y_g_other_size) && (X_g_ceed_size == X_g_other_size) && (Y_l_ceed_size == Y_l_other_size) &&
512                    (X_l_ceed_size == X_l_other_size),
513                PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
514                "mat_ceed and mat_other must have compatible sizes; found mat_ceed (Global: %" PetscInt_FMT ", %" PetscInt_FMT
515                "; Local: %" PetscInt_FMT ", %" PetscInt_FMT ") mat_other (Global: %" PetscInt_FMT ", %" PetscInt_FMT "; Local: %" PetscInt_FMT
516                ", %" PetscInt_FMT ")",
517                Y_g_ceed_size, X_g_ceed_size, Y_l_ceed_size, X_l_ceed_size, Y_g_other_size, X_g_other_size, Y_l_other_size, X_l_other_size);
518   }
519 
520   // Convert
521   {
522     VecType        vec_type;
523     MatCeedContext ctx;
524 
525     PetscCall(PetscObjectChangeTypeName((PetscObject)mat_other, MATCEED));
526     PetscCall(MatShellGetContext(mat_ceed, &ctx));
527     PetscCall(MatCeedContextReference(ctx));
528     PetscCall(MatShellSetContext(mat_other, ctx));
529     PetscCall(MatShellSetContextDestroy(mat_other, (PetscErrorCode(*)(void *))MatCeedContextDestroy));
530     PetscCall(MatShellSetOperation(mat_other, MATOP_VIEW, (void (*)(void))MatView_Ceed));
531     PetscCall(MatShellSetOperation(mat_other, MATOP_MULT, (void (*)(void))MatMult_Ceed));
532     if (ctx->op_mult_transpose) PetscCall(MatShellSetOperation(mat_other, MATOP_MULT_TRANSPOSE, (void (*)(void))MatMultTranspose_Ceed));
533     PetscCall(MatShellSetOperation(mat_other, MATOP_GET_DIAGONAL, (void (*)(void))MatGetDiagonal_Ceed));
534     PetscCall(MatShellSetOperation(mat_other, MATOP_GET_DIAGONAL_BLOCK, (void (*)(void))MatGetDiagonalBlock_Ceed));
535     PetscCall(MatShellSetOperation(mat_other, MATOP_INVERT_BLOCK_DIAGONAL, (void (*)(void))MatInvertBlockDiagonal_Ceed));
536     PetscCall(MatShellSetOperation(mat_other, MATOP_INVERT_VBLOCK_DIAGONAL, (void (*)(void))MatInvertVariableBlockDiagonal_Ceed));
537     {
538       PetscInt block_size;
539 
540       PetscCall(MatGetBlockSize(mat_ceed, &block_size));
541       if (block_size > 1) PetscCall(MatSetBlockSize(mat_other, block_size));
542     }
543     {
544       PetscInt        num_blocks;
545       const PetscInt *block_sizes;
546 
547       PetscCall(MatGetVariableBlockSizes(mat_ceed, &num_blocks, &block_sizes));
548       if (num_blocks) PetscCall(MatSetVariableBlockSizes(mat_other, num_blocks, (PetscInt *)block_sizes));
549     }
550     PetscCall(DMGetVecType(ctx->dm_x, &vec_type));
551     PetscCall(MatShellSetVecType(mat_other, vec_type));
552   }
553   PetscFunctionReturn(PETSC_SUCCESS);
554 }
555 
556 /**
557   @brief Mark Mark `CeedQFunction` data as updated and the `CeedQFunction` as requiring re-assembly for a `MatCEED`.
558 
559   Collective across MPI processes.
560 
561   @param[in]   mat_ceed       `MATCEED`
562   @param[out]  update_needed  Boolean flag indicating `CeedQFunction` update needed
563 
564   @return An error code: 0 - success, otherwise - failure
565 **/
566 PetscErrorCode MatCeedSetAssemblyDataUpdateNeeded(Mat mat_ceed, PetscBool update_needed) {
567   MatCeedContext ctx;
568 
569   PetscFunctionBeginUser;
570   PetscCall(MatShellGetContext(mat_ceed, &ctx));
571   PetscCallCeed(ctx->ceed, CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(ctx->op_mult, update_needed));
572   if (ctx->op_mult_transpose) {
573     PetscCallCeed(ctx->ceed, CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(ctx->op_mult_transpose, update_needed));
574   }
575   if (update_needed) {
576     PetscCall(MatAssemblyBegin(mat_ceed, MAT_FINAL_ASSEMBLY));
577     PetscCall(MatAssemblyEnd(mat_ceed, MAT_FINAL_ASSEMBLY));
578   }
579   PetscFunctionReturn(PETSC_SUCCESS);
580 }
581 
582 /**
583   @brief Setup a `Mat` with the same COO pattern as a `MatCEED`.
584 
585   Collective across MPI processes.
586 
587   @param[in]   mat_ceed  `MATCEED`
588   @param[out]  mat_coo   Sparse `Mat` with same COO pattern
589 
590   @return An error code: 0 - success, otherwise - failure
591 **/
592 PetscErrorCode MatCeedCreateMatCOO(Mat mat_ceed, Mat *mat_coo) {
593   MatCeedContext ctx;
594 
595   PetscFunctionBeginUser;
596   PetscCall(MatShellGetContext(mat_ceed, &ctx));
597 
598   PetscCheck(ctx->dm_x == ctx->dm_y, PetscObjectComm((PetscObject)mat_ceed), PETSC_ERR_SUP, "COO assembly only supported for MATCEED on a single DM");
599 
600   // Check cl mat type
601   {
602     PetscBool is_coo_mat_type_cl = PETSC_FALSE;
603     char      coo_mat_type_cl[64];
604 
605     // Check for specific CL coo mat type for this Mat
606     {
607       const char *mat_ceed_prefix = NULL;
608 
609       PetscCall(MatGetOptionsPrefix(mat_ceed, &mat_ceed_prefix));
610       PetscOptionsBegin(PetscObjectComm((PetscObject)mat_ceed), mat_ceed_prefix, "", NULL);
611       PetscCall(PetscOptionsFList("-ceed_coo_mat_type", "Default MATCEED COO assembly MatType", NULL, MatList, coo_mat_type_cl, coo_mat_type_cl,
612                                   sizeof(coo_mat_type_cl), &is_coo_mat_type_cl));
613       PetscOptionsEnd();
614       if (is_coo_mat_type_cl) {
615         PetscCall(PetscFree(ctx->coo_mat_type));
616         PetscCall(PetscStrallocpy(coo_mat_type_cl, &ctx->coo_mat_type));
617       }
618     }
619   }
620 
621   // Create sparse matrix
622   {
623     MatType dm_mat_type, dm_mat_type_copy;
624 
625     PetscCall(DMGetMatType(ctx->dm_x, &dm_mat_type));
626     PetscCall(PetscStrallocpy(dm_mat_type, (char **)&dm_mat_type_copy));
627     PetscCall(DMSetMatType(ctx->dm_x, ctx->coo_mat_type));
628     PetscCall(DMCreateMatrix(ctx->dm_x, mat_coo));
629     PetscCall(DMSetMatType(ctx->dm_x, dm_mat_type_copy));
630     PetscCall(PetscFree(dm_mat_type_copy));
631   }
632   PetscFunctionReturn(PETSC_SUCCESS);
633 }
634 
635 /**
636   @brief Setup the COO preallocation `MATCEED` into a `MATAIJ` or similar.
637          The caller is responsible for assuring the global and local sizes are compatible, otherwise this function will fail.
638 
639   Collective across MPI processes.
640 
641   @param[in]      mat_ceed  `MATCEED` to assemble
642   @param[in,out]  mat_coo   `MATAIJ` or similar to assemble into
643 
644   @return An error code: 0 - success, otherwise - failure
645 **/
646 PetscErrorCode MatCeedSetPreallocationCOO(Mat mat_ceed, Mat mat_coo) {
647   MatCeedContext ctx;
648 
649   PetscFunctionBeginUser;
650   PetscCall(MatShellGetContext(mat_ceed, &ctx));
651 
652   {
653     PetscInt     *rows_petsc = NULL, *cols_petsc = NULL;
654     CeedInt      *rows_ceed, *cols_ceed;
655     PetscCount    num_entries;
656     PetscLogStage stage_amg_setup;
657 
658     // -- Assemble sparsity pattern if mat hasn't been assembled before
659     PetscCall(PetscLogStageGetId("MatCEED Asm Setup", &stage_amg_setup));
660     if (stage_amg_setup == -1) {
661       PetscCall(PetscLogStageRegister("MatCEED Asm Setup", &stage_amg_setup));
662     }
663     PetscCall(PetscLogStagePush(stage_amg_setup));
664     PetscCall(PetscLogEventBegin(MATCEED_SETUP_FULL, mat_ceed, mat_coo, NULL, NULL));
665     PetscCall(PetscLogEventBegin(MATCEED_SETUP_FULL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
666     PetscCallCeed(ctx->ceed, CeedOperatorLinearAssembleSymbolic(ctx->op_mult, &num_entries, &rows_ceed, &cols_ceed));
667     PetscCall(PetscLogEventEnd(MATCEED_SETUP_FULL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
668     PetscCall(IntArrayCeedToPetsc(num_entries, &rows_ceed, &rows_petsc));
669     PetscCall(IntArrayCeedToPetsc(num_entries, &cols_ceed, &cols_petsc));
670     PetscCall(MatSetPreallocationCOOLocal(mat_coo, num_entries, rows_petsc, cols_petsc));
671     free(rows_petsc);
672     free(cols_petsc);
673     if (!ctx->coo_values_full) PetscCallCeed(ctx->ceed, CeedVectorCreate(ctx->ceed, num_entries, &ctx->coo_values_full));
674     PetscCall(PetscRealloc(++ctx->num_mats_assembled_full * sizeof(Mat), &ctx->mats_assembled_full));
675     ctx->mats_assembled_full[ctx->num_mats_assembled_full - 1] = mat_coo;
676     PetscCall(PetscLogEventEnd(MATCEED_SETUP_FULL, mat_ceed, mat_coo, NULL, NULL));
677     PetscCall(PetscLogStagePop());
678   }
679   PetscFunctionReturn(PETSC_SUCCESS);
680 }
681 
682 /**
683   @brief Assemble a `MATCEED` into a `MATAIJ` or similar.
684          The `mat_coo` preallocation is set to match the sparsity pattern of `mat_ceed`.
685          The caller is responsible for assuring the global and local sizes are compatible, otherwise this function will fail.
686 
687   Collective across MPI processes.
688 
689   @param[in]      mat_ceed  `MATCEED` to assemble
690   @param[in,out]  mat_coo   `MATAIJ` or similar to assemble into
691 
692   @return An error code: 0 - success, otherwise - failure
693 **/
694 PetscErrorCode MatCeedAssembleCOO(Mat mat_ceed, Mat mat_coo) {
695   MatCeedContext ctx;
696 
697   PetscFunctionBeginUser;
698   PetscCall(MatShellGetContext(mat_ceed, &ctx));
699 
700   // Set COO pattern if needed
701   {
702     CeedInt index = -1;
703 
704     for (PetscInt i = 0; i < ctx->num_mats_assembled_full; i++) {
705       if (ctx->mats_assembled_full[i] == mat_coo) index = i;
706     }
707     if (index == -1) PetscCall(MatCeedSetPreallocationCOO(mat_ceed, mat_coo));
708   }
709 
710   // Assemble mat_ceed
711   PetscCall(PetscLogEventBegin(MATCEED_ASSEMBLE_FULL, mat_ceed, mat_coo, NULL, NULL));
712   PetscCall(MatAssemblyBegin(mat_coo, MAT_FINAL_ASSEMBLY));
713   {
714     const CeedScalar *values;
715     MatType           mat_type;
716     CeedMemType       mem_type = CEED_MEM_HOST;
717     PetscBool         is_spd, is_spd_known;
718 
719     PetscCall(MatGetType(mat_coo, &mat_type));
720     if (strstr(mat_type, "cusparse")) mem_type = CEED_MEM_DEVICE;
721     else if (strstr(mat_type, "kokkos")) mem_type = CEED_MEM_DEVICE;
722     else mem_type = CEED_MEM_HOST;
723 
724     PetscCall(PetscLogEventBegin(MATCEED_ASSEMBLE_FULL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
725     PetscCallCeed(ctx->ceed, CeedOperatorLinearAssemble(ctx->op_mult, ctx->coo_values_full));
726     PetscCall(PetscLogEventEnd(MATCEED_ASSEMBLE_FULL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
727     PetscCallCeed(ctx->ceed, CeedVectorGetArrayRead(ctx->coo_values_full, mem_type, &values));
728     PetscCall(MatSetValuesCOO(mat_coo, values, INSERT_VALUES));
729     PetscCall(MatIsSPDKnown(mat_ceed, &is_spd_known, &is_spd));
730     if (is_spd_known) PetscCall(MatSetOption(mat_coo, MAT_SPD, is_spd));
731     PetscCallCeed(ctx->ceed, CeedVectorRestoreArrayRead(ctx->coo_values_full, &values));
732   }
733   PetscCall(MatAssemblyEnd(mat_coo, MAT_FINAL_ASSEMBLY));
734   PetscCall(PetscLogEventEnd(MATCEED_ASSEMBLE_FULL, mat_ceed, mat_coo, NULL, NULL));
735   PetscFunctionReturn(PETSC_SUCCESS);
736 }
737 
738 /**
739   @brief Set the current value of a context field for a `MatCEED`.
740 
741   Not collective across MPI processes.
742 
743   @param[in,out]  mat    `MatCEED`
744   @param[in]      name   Name of the context field
745   @param[in]      value  New context field value
746 
747   @return An error code: 0 - success, otherwise - failure
748 **/
749 PetscErrorCode MatCeedSetContextDouble(Mat mat, const char *name, double value) {
750   PetscBool      was_updated = PETSC_FALSE;
751   MatCeedContext ctx;
752 
753   PetscFunctionBeginUser;
754   PetscCall(MatShellGetContext(mat, &ctx));
755   {
756     CeedContextFieldLabel label = NULL;
757 
758     PetscCallCeed(ctx->ceed, CeedOperatorGetContextFieldLabel(ctx->op_mult, name, &label));
759     if (label) {
760       double set_value = 2 * value + 1.0;
761 
762       PetscCall(MatCeedGetContextDouble(mat, name, &set_value));
763       if (set_value != value) {
764         PetscCallCeed(ctx->ceed, CeedOperatorSetContextDouble(ctx->op_mult, label, &value));
765         was_updated = PETSC_TRUE;
766       }
767     }
768     if (ctx->op_mult_transpose) {
769       label = NULL;
770       PetscCallCeed(ctx->ceed, CeedOperatorGetContextFieldLabel(ctx->op_mult_transpose, name, &label));
771       if (label) {
772         double set_value = 2 * value + 1.0;
773 
774         PetscCall(MatCeedGetContextDouble(mat, name, &set_value));
775         if (set_value != value) {
776           PetscCallCeed(ctx->ceed, CeedOperatorSetContextDouble(ctx->op_mult_transpose, label, &value));
777           was_updated = PETSC_TRUE;
778         }
779       }
780     }
781   }
782   if (was_updated) {
783     PetscCall(MatAssemblyBegin(mat, MAT_FINAL_ASSEMBLY));
784     PetscCall(MatAssemblyEnd(mat, MAT_FINAL_ASSEMBLY));
785   }
786   PetscFunctionReturn(PETSC_SUCCESS);
787 }
788 
789 /**
790   @brief Get the current value of a context field for a `MatCEED`.
791 
792   Not collective across MPI processes.
793 
794   @param[in]   mat    `MatCEED`
795   @param[in]   name   Name of the context field
796   @param[out]  value  Current context field value
797 
798   @return An error code: 0 - success, otherwise - failure
799 **/
800 PetscErrorCode MatCeedGetContextDouble(Mat mat, const char *name, double *value) {
801   MatCeedContext ctx;
802 
803   PetscFunctionBeginUser;
804   PetscCall(MatShellGetContext(mat, &ctx));
805   {
806     CeedContextFieldLabel label = NULL;
807     CeedOperator          op    = ctx->op_mult;
808 
809     PetscCallCeed(ctx->ceed, CeedOperatorGetContextFieldLabel(op, name, &label));
810     if (!label && ctx->op_mult_transpose) {
811       op = ctx->op_mult_transpose;
812       PetscCallCeed(ctx->ceed, CeedOperatorGetContextFieldLabel(op, name, &label));
813     }
814     if (label) {
815       PetscSizeT    num_values;
816       const double *values_ceed;
817 
818       PetscCallCeed(ctx->ceed, CeedOperatorGetContextDoubleRead(op, label, &num_values, &values_ceed));
819       *value = values_ceed[0];
820       PetscCallCeed(ctx->ceed, CeedOperatorRestoreContextDoubleRead(op, label, &values_ceed));
821     }
822   }
823   PetscFunctionReturn(PETSC_SUCCESS);
824 }
825 
826 /**
827   @brief Set the current `PetscReal` value of a context field for a `MatCEED`.
828 
829   Not collective across MPI processes.
830 
831   @param[in,out]  mat    `MatCEED`
832   @param[in]      name   Name of the context field
833   @param[in]      value  New context field value
834 
835   @return An error code: 0 - success, otherwise - failure
836 **/
837 PetscErrorCode MatCeedSetContextReal(Mat mat, const char *name, PetscReal value) {
838   double value_double = value;
839 
840   PetscFunctionBeginUser;
841   PetscCall(MatCeedSetContextDouble(mat, name, value_double));
842   PetscFunctionReturn(PETSC_SUCCESS);
843 }
844 
845 /**
846   @brief Get the current `PetscReal` value of a context field for a `MatCEED`.
847 
848   Not collective across MPI processes.
849 
850   @param[in]   mat    `MatCEED`
851   @param[in]   name   Name of the context field
852   @param[out]  value  Current context field value
853 
854   @return An error code: 0 - success, otherwise - failure
855 **/
856 PetscErrorCode MatCeedGetContextReal(Mat mat, const char *name, PetscReal *value) {
857   double value_double = 0.0;
858 
859   PetscFunctionBeginUser;
860   PetscCall(MatCeedGetContextDouble(mat, name, &value_double));
861   *value = value_double;
862   PetscFunctionReturn(PETSC_SUCCESS);
863 }
864 
865 /**
866   @brief Set the current time for a `MatCEED`.
867 
868   Not collective across MPI processes.
869 
870   @param[in,out]  mat   `MatCEED`
871   @param[in]      time  Current time
872 
873   @return An error code: 0 - success, otherwise - failure
874 **/
875 PetscErrorCode MatCeedSetTime(Mat mat, PetscReal time) {
876   PetscFunctionBeginUser;
877   {
878     double time_ceed = time;
879 
880     PetscCall(MatCeedSetContextDouble(mat, "time", time_ceed));
881   }
882   PetscFunctionReturn(PETSC_SUCCESS);
883 }
884 
885 /**
886   @brief Get the current time for a `MatCEED`.
887 
888   Not collective across MPI processes.
889 
890   @param[in]   mat   `MatCEED`
891   @param[out]  time  Current time, or -1.0 if the boundary evaluator has no time field
892 
893   @return An error code: 0 - success, otherwise - failure
894 **/
895 PetscErrorCode MatCeedGetTime(Mat mat, PetscReal *time) {
896   PetscFunctionBeginUser;
897   *time = -1.0;
898   {
899     double time_ceed = -1.0;
900 
901     PetscCall(MatCeedGetContextDouble(mat, "time", &time_ceed));
902     *time = time_ceed;
903   }
904   PetscFunctionReturn(PETSC_SUCCESS);
905 }
906 
907 /**
908   @brief Set the current time step for a `MatCEED`.
909 
910   Not collective across MPI processes.
911 
912   @param[in,out]  mat  `MatCEED`
913   @param[in]      dt   Current time step
914 
915   @return An error code: 0 - success, otherwise - failure
916 **/
917 PetscErrorCode MatCeedSetDt(Mat mat, PetscReal dt) {
918   PetscFunctionBeginUser;
919   {
920     double dt_ceed = dt;
921 
922     PetscCall(MatCeedSetContextDouble(mat, "dt", dt_ceed));
923   }
924   PetscFunctionReturn(PETSC_SUCCESS);
925 }
926 
927 /**
928   @brief Set the Jacobian shifts for a `MatCEED`.
929 
930   Not collective across MPI processes.
931 
932   @param[in,out]  mat      `MatCEED`
933   @param[in]      shift_v  Velocity shift
934   @param[in]      shift_a  Acceleration shift
935 
936   @return An error code: 0 - success, otherwise - failure
937 **/
938 PetscErrorCode MatCeedSetShifts(Mat mat, PetscReal shift_v, PetscReal shift_a) {
939   PetscFunctionBeginUser;
940   {
941     double shift_v_ceed = shift_v;
942 
943     PetscCall(MatCeedSetContextDouble(mat, "shift v", shift_v_ceed));
944   }
945   if (shift_a) {
946     double shift_a_ceed = shift_a;
947 
948     PetscCall(MatCeedSetContextDouble(mat, "shift a", shift_a_ceed));
949   }
950   PetscFunctionReturn(PETSC_SUCCESS);
951 }
952 
953 /**
954   @brief Set user context for a `MATCEED`.
955 
956   Collective across MPI processes.
957 
958   @param[in,out]  mat  `MATCEED`
959   @param[in]      f    The context destroy function, or NULL
960   @param[in]      ctx  User context, or NULL to unset
961 
962   @return An error code: 0 - success, otherwise - failure
963 **/
964 PetscErrorCode MatCeedSetContext(Mat mat, PetscErrorCode (*f)(void *), void *ctx) {
965   PetscContainer user_ctx = NULL;
966 
967   PetscFunctionBeginUser;
968   if (ctx) {
969     PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)mat), &user_ctx));
970     PetscCall(PetscContainerSetPointer(user_ctx, ctx));
971     PetscCall(PetscContainerSetUserDestroy(user_ctx, f));
972   }
973   PetscCall(PetscObjectCompose((PetscObject)mat, "MatCeed user context", (PetscObject)user_ctx));
974   PetscCall(PetscContainerDestroy(&user_ctx));
975   PetscFunctionReturn(PETSC_SUCCESS);
976 }
977 
978 /**
979   @brief Retrieve the user context for a `MATCEED`.
980 
981   Collective across MPI processes.
982 
983   @param[in,out]  mat  `MATCEED`
984   @param[in]      ctx  User context
985 
986   @return An error code: 0 - success, otherwise - failure
987 **/
988 PetscErrorCode MatCeedGetContext(Mat mat, void *ctx) {
989   PetscContainer user_ctx;
990 
991   PetscFunctionBeginUser;
992   PetscCall(PetscObjectQuery((PetscObject)mat, "MatCeed user context", (PetscObject *)&user_ctx));
993   if (user_ctx) PetscCall(PetscContainerGetPointer(user_ctx, (void **)ctx));
994   else *(void **)ctx = NULL;
995   PetscFunctionReturn(PETSC_SUCCESS);
996 }
997 /**
998   @brief Set a user defined matrix operation for a `MATCEED` matrix.
999 
1000   Within each user-defined routine, the user should call `MatCeedGetContext()` to obtain the user-defined context that was set by
1001 `MatCeedSetContext()`.
1002 
1003   Collective across MPI processes.
1004 
1005   @param[in,out]  mat  `MATCEED`
1006   @param[in]      op   Name of the `MatOperation`
1007   @param[in]      g    Function that provides the operation
1008 
1009   @return An error code: 0 - success, otherwise - failure
1010 **/
1011 PetscErrorCode MatCeedSetOperation(Mat mat, MatOperation op, void (*g)(void)) {
1012   PetscFunctionBeginUser;
1013   PetscCall(MatShellSetOperation(mat, op, g));
1014   PetscFunctionReturn(PETSC_SUCCESS);
1015 }
1016 
1017 /**
1018   @brief Sets the default COO matrix type as a string from the `MATCEED`.
1019 
1020   Collective across MPI processes.
1021 
1022   @param[in,out]  mat   `MATCEED`
1023   @param[in]      type  COO `MatType` to set
1024 
1025   @return An error code: 0 - success, otherwise - failure
1026 **/
1027 PetscErrorCode MatCeedSetCOOMatType(Mat mat, MatType type) {
1028   MatCeedContext ctx;
1029 
1030   PetscFunctionBeginUser;
1031   PetscCall(MatShellGetContext(mat, &ctx));
1032   // Check if same
1033   {
1034     size_t    len_old, len_new;
1035     PetscBool is_same = PETSC_FALSE;
1036 
1037     PetscCall(PetscStrlen(ctx->coo_mat_type, &len_old));
1038     PetscCall(PetscStrlen(type, &len_new));
1039     if (len_old == len_new) PetscCall(PetscStrncmp(ctx->coo_mat_type, type, len_old, &is_same));
1040     if (is_same) PetscFunctionReturn(PETSC_SUCCESS);
1041   }
1042   // Clean up old mats in different format
1043   // LCOV_EXCL_START
1044   if (ctx->mat_assembled_full_internal) {
1045     for (PetscInt i = 0; i < ctx->num_mats_assembled_full; i++) {
1046       if (ctx->mats_assembled_full[i] == ctx->mat_assembled_full_internal) {
1047         for (PetscInt j = i + 1; j < ctx->num_mats_assembled_full; j++) {
1048           ctx->mats_assembled_full[j - 1] = ctx->mats_assembled_full[j];
1049         }
1050         ctx->num_mats_assembled_full--;
1051         // Note: we'll realloc this array again, so no need to shrink the allocation
1052         PetscCall(MatDestroy(&ctx->mat_assembled_full_internal));
1053       }
1054     }
1055   }
1056   if (ctx->mat_assembled_pbd_internal) {
1057     for (PetscInt i = 0; i < ctx->num_mats_assembled_pbd; i++) {
1058       if (ctx->mats_assembled_pbd[i] == ctx->mat_assembled_pbd_internal) {
1059         for (PetscInt j = i + 1; j < ctx->num_mats_assembled_pbd; j++) {
1060           ctx->mats_assembled_pbd[j - 1] = ctx->mats_assembled_pbd[j];
1061         }
1062         // Note: we'll realloc this array again, so no need to shrink the allocation
1063         ctx->num_mats_assembled_pbd--;
1064         PetscCall(MatDestroy(&ctx->mat_assembled_pbd_internal));
1065       }
1066     }
1067   }
1068   PetscCall(PetscFree(ctx->coo_mat_type));
1069   PetscCall(PetscStrallocpy(type, &ctx->coo_mat_type));
1070   PetscFunctionReturn(PETSC_SUCCESS);
1071   // LCOV_EXCL_STOP
1072 }
1073 
1074 /**
1075   @brief Gets the default COO matrix type as a string from the `MATCEED`.
1076 
1077   Collective across MPI processes.
1078 
1079   @param[in,out]  mat   `MATCEED`
1080   @param[in]      type  COO `MatType`
1081 
1082   @return An error code: 0 - success, otherwise - failure
1083 **/
1084 PetscErrorCode MatCeedGetCOOMatType(Mat mat, MatType *type) {
1085   MatCeedContext ctx;
1086 
1087   PetscFunctionBeginUser;
1088   PetscCall(MatShellGetContext(mat, &ctx));
1089   *type = ctx->coo_mat_type;
1090   PetscFunctionReturn(PETSC_SUCCESS);
1091 }
1092 
1093 /**
1094   @brief Set input local vectors for `MATCEED` `MatMult()` and `MatMultTranspose()` operations.
1095 
1096   Not collective across MPI processes.
1097 
1098   @param[in,out]  mat              `MATCEED`
1099   @param[in]      X_loc            Input PETSc local vector, or NULL
1100   @param[in]      Y_loc_transpose  Input PETSc local vector for transpose operation, or NULL
1101 
1102   @return An error code: 0 - success, otherwise - failure
1103 **/
1104 PetscErrorCode MatCeedSetLocalVectors(Mat mat, Vec X_loc, Vec Y_loc_transpose) {
1105   MatCeedContext ctx;
1106 
1107   PetscFunctionBeginUser;
1108   PetscCall(MatShellGetContext(mat, &ctx));
1109   if (X_loc) {
1110     PetscInt len_old, len_new;
1111 
1112     PetscCall(VecGetSize(ctx->X_loc, &len_old));
1113     PetscCall(VecGetSize(X_loc, &len_new));
1114     PetscCheck(len_old == len_new, PETSC_COMM_SELF, PETSC_ERR_LIB, "new X_loc length %" PetscInt_FMT " should match old X_loc length %" PetscInt_FMT,
1115                len_new, len_old);
1116     PetscCall(VecReferenceCopy(X_loc, &ctx->X_loc));
1117   }
1118   if (Y_loc_transpose) {
1119     PetscInt len_old, len_new;
1120 
1121     PetscCall(VecGetSize(ctx->Y_loc_transpose, &len_old));
1122     PetscCall(VecGetSize(Y_loc_transpose, &len_new));
1123     PetscCheck(len_old == len_new, PETSC_COMM_SELF, PETSC_ERR_LIB,
1124                "new Y_loc_transpose length %" PetscInt_FMT " should match old Y_loc_transpose length %" PetscInt_FMT, len_new, len_old);
1125     PetscCall(VecReferenceCopy(Y_loc_transpose, &ctx->Y_loc_transpose));
1126   }
1127   PetscFunctionReturn(PETSC_SUCCESS);
1128 }
1129 
1130 /**
1131   @brief Get input local vectors for `MATCEED` `MatMult()` and `MatMultTranspose()` operations.
1132 
1133   Not collective across MPI processes.
1134 
1135   @param[in,out]  mat              `MATCEED`
1136   @param[out]     X_loc            Input PETSc local vector, or NULL
1137   @param[out]     Y_loc_transpose  Input PETSc local vector for transpose operation, or NULL
1138 
1139   @return An error code: 0 - success, otherwise - failure
1140 **/
1141 PetscErrorCode MatCeedGetLocalVectors(Mat mat, Vec *X_loc, Vec *Y_loc_transpose) {
1142   MatCeedContext ctx;
1143 
1144   PetscFunctionBeginUser;
1145   PetscCall(MatShellGetContext(mat, &ctx));
1146   if (X_loc) {
1147     *X_loc = NULL;
1148     PetscCall(VecReferenceCopy(ctx->X_loc, X_loc));
1149   }
1150   if (Y_loc_transpose) {
1151     *Y_loc_transpose = NULL;
1152     PetscCall(VecReferenceCopy(ctx->Y_loc_transpose, Y_loc_transpose));
1153   }
1154   PetscFunctionReturn(PETSC_SUCCESS);
1155 }
1156 
1157 /**
1158   @brief Restore input local vectors for `MATCEED` `MatMult()` and `MatMultTranspose()` operations.
1159 
1160   Not collective across MPI processes.
1161 
1162   @param[in,out]  mat              MatCeed
1163   @param[out]     X_loc            Input PETSc local vector, or NULL
1164   @param[out]     Y_loc_transpose  Input PETSc local vector for transpose operation, or NULL
1165 
1166   @return An error code: 0 - success, otherwise - failure
1167 **/
1168 PetscErrorCode MatCeedRestoreLocalVectors(Mat mat, Vec *X_loc, Vec *Y_loc_transpose) {
1169   PetscFunctionBeginUser;
1170   if (X_loc) PetscCall(VecDestroy(X_loc));
1171   if (Y_loc_transpose) PetscCall(VecDestroy(Y_loc_transpose));
1172   PetscFunctionReturn(PETSC_SUCCESS);
1173 }
1174 
1175 /**
1176   @brief Get libCEED `CeedOperator` for `MATCEED` `MatMult()` and `MatMultTranspose()` operations.
1177 
1178   Not collective across MPI processes.
1179 
1180   @param[in,out]  mat                MatCeed
1181   @param[out]     op_mult            libCEED `CeedOperator` for `MatMult()`, or NULL
1182   @param[out]     op_mult_transpose  libCEED `CeedOperator` for `MatMultTranspose()`, or NULL
1183 
1184   @return An error code: 0 - success, otherwise - failure
1185 **/
1186 PetscErrorCode MatCeedGetCeedOperators(Mat mat, CeedOperator *op_mult, CeedOperator *op_mult_transpose) {
1187   MatCeedContext ctx;
1188 
1189   PetscFunctionBeginUser;
1190   PetscCall(MatShellGetContext(mat, &ctx));
1191   if (op_mult) {
1192     *op_mult = NULL;
1193     PetscCallCeed(ctx->ceed, CeedOperatorReferenceCopy(ctx->op_mult, op_mult));
1194   }
1195   if (op_mult_transpose) {
1196     *op_mult_transpose = NULL;
1197     PetscCallCeed(ctx->ceed, CeedOperatorReferenceCopy(ctx->op_mult_transpose, op_mult_transpose));
1198   }
1199   PetscFunctionReturn(PETSC_SUCCESS);
1200 }
1201 
1202 /**
1203   @brief Restore libCEED `CeedOperator` for `MATCEED` `MatMult()` and `MatMultTranspose()` operations.
1204 
1205   Not collective across MPI processes.
1206 
1207   @param[in,out]  mat                MatCeed
1208   @param[out]     op_mult            libCEED `CeedOperator` for `MatMult()`, or NULL
1209   @param[out]     op_mult_transpose  libCEED `CeedOperator` for `MatMultTranspose()`, or NULL
1210 
1211   @return An error code: 0 - success, otherwise - failure
1212 **/
1213 PetscErrorCode MatCeedRestoreCeedOperators(Mat mat, CeedOperator *op_mult, CeedOperator *op_mult_transpose) {
1214   MatCeedContext ctx;
1215 
1216   PetscFunctionBeginUser;
1217   PetscCall(MatShellGetContext(mat, &ctx));
1218   if (op_mult) PetscCallCeed(ctx->ceed, CeedOperatorDestroy(op_mult));
1219   if (op_mult_transpose) PetscCallCeed(ctx->ceed, CeedOperatorDestroy(op_mult_transpose));
1220   PetscFunctionReturn(PETSC_SUCCESS);
1221 }
1222 
1223 /**
1224   @brief Set `PetscLogEvent` for `MATCEED` `MatMult()` and `MatMultTranspose()` operators.
1225 
1226   Not collective across MPI processes.
1227 
1228   @param[in,out]  mat                       MatCeed
1229   @param[out]     log_event_mult            `PetscLogEvent` for forward evaluation, or NULL
1230   @param[out]     log_event_mult_transpose  `PetscLogEvent` for transpose evaluation, or NULL
1231 
1232   @return An error code: 0 - success, otherwise - failure
1233 **/
1234 PetscErrorCode MatCeedSetLogEvents(Mat mat, PetscLogEvent log_event_mult, PetscLogEvent log_event_mult_transpose) {
1235   MatCeedContext ctx;
1236 
1237   PetscFunctionBeginUser;
1238   PetscCall(MatShellGetContext(mat, &ctx));
1239   if (log_event_mult) ctx->log_event_mult = log_event_mult;
1240   if (log_event_mult_transpose) ctx->log_event_mult_transpose = log_event_mult_transpose;
1241   PetscFunctionReturn(PETSC_SUCCESS);
1242 }
1243 
1244 /**
1245   @brief Get `PetscLogEvent` for `MATCEED` `MatMult()` and `MatMultTranspose()` operators.
1246 
1247   Not collective across MPI processes.
1248 
1249   @param[in,out]  mat                       MatCeed
1250   @param[out]     log_event_mult            `PetscLogEvent` for forward evaluation, or NULL
1251   @param[out]     log_event_mult_transpose  `PetscLogEvent` for transpose evaluation, or NULL
1252 
1253   @return An error code: 0 - success, otherwise - failure
1254 **/
1255 PetscErrorCode MatCeedGetLogEvents(Mat mat, PetscLogEvent *log_event_mult, PetscLogEvent *log_event_mult_transpose) {
1256   MatCeedContext ctx;
1257 
1258   PetscFunctionBeginUser;
1259   PetscCall(MatShellGetContext(mat, &ctx));
1260   if (log_event_mult) *log_event_mult = ctx->log_event_mult;
1261   if (log_event_mult_transpose) *log_event_mult_transpose = ctx->log_event_mult_transpose;
1262   PetscFunctionReturn(PETSC_SUCCESS);
1263 }
1264 
1265 /**
1266   @brief Set `CeedOperator` `PetscLogEvent` for `MATCEED` `MatMult()` and `MatMultTranspose()` operators.
1267 
1268   Not collective across MPI processes.
1269 
1270   @param[in,out]  mat                       MatCeed
1271   @param[out]     log_event_mult            `PetscLogEvent` for forward `CeedOperator` evaluation, or NULL
1272   @param[out]     log_event_mult_transpose  `PetscLogEvent` for transpose `CeedOperator` evaluation, or NULL
1273 
1274   @return An error code: 0 - success, otherwise - failure
1275 **/
1276 PetscErrorCode MatCeedSetCeedOperatorLogEvents(Mat mat, PetscLogEvent log_event_mult, PetscLogEvent log_event_mult_transpose) {
1277   MatCeedContext ctx;
1278 
1279   PetscFunctionBeginUser;
1280   PetscCall(MatShellGetContext(mat, &ctx));
1281   if (log_event_mult) ctx->log_event_ceed_mult = log_event_mult;
1282   if (log_event_mult_transpose) ctx->log_event_ceed_mult_transpose = log_event_mult_transpose;
1283   PetscFunctionReturn(PETSC_SUCCESS);
1284 }
1285 
1286 /**
1287   @brief Get `CeedOperator` `PetscLogEvent` for `MATCEED` `MatMult()` and `MatMultTranspose()` operators.
1288 
1289   Not collective across MPI processes.
1290 
1291   @param[in,out]  mat                       MatCeed
1292   @param[out]     log_event_mult            `PetscLogEvent` for forward `CeedOperator` evaluation, or NULL
1293   @param[out]     log_event_mult_transpose  `PetscLogEvent` for transpose `CeedOperator` evaluation, or NULL
1294 
1295   @return An error code: 0 - success, otherwise - failure
1296 **/
1297 PetscErrorCode MatCeedGetCeedOperatorLogEvents(Mat mat, PetscLogEvent *log_event_mult, PetscLogEvent *log_event_mult_transpose) {
1298   MatCeedContext ctx;
1299 
1300   PetscFunctionBeginUser;
1301   PetscCall(MatShellGetContext(mat, &ctx));
1302   if (log_event_mult) *log_event_mult = ctx->log_event_ceed_mult;
1303   if (log_event_mult_transpose) *log_event_mult_transpose = ctx->log_event_ceed_mult_transpose;
1304   PetscFunctionReturn(PETSC_SUCCESS);
1305 }
1306 
1307 // -----------------------------------------------------------------------------
1308 // Operator context data
1309 // -----------------------------------------------------------------------------
1310 
1311 /**
1312   @brief Setup context data for operator application.
1313 
1314   Collective across MPI processes.
1315 
1316   @param[in]   dm_x                           Input `DM`
1317   @param[in]   dm_y                           Output `DM`
1318   @param[in]   X_loc                          Input PETSc local vector, or NULL
1319   @param[in]   Y_loc_transpose                Input PETSc local vector for transpose operation, or NULL
1320   @param[in]   op_mult                        `CeedOperator` for forward evaluation
1321   @param[in]   op_mult_transpose              `CeedOperator` for transpose evaluation
1322   @param[in]   log_event_mult                 `PetscLogEvent` for forward evaluation
1323   @param[in]   log_event_mult_transpose       `PetscLogEvent` for transpose evaluation
1324   @param[in]   log_event_ceed_mult            `PetscLogEvent` for forward `CeedOperator` evaluation
1325   @param[in]   log_event_ceed_mult_transpose  `PetscLogEvent` for transpose `CeedOperator` evaluation
1326   @param[out]  ctx                            Context data for operator evaluation
1327 
1328   @return An error code: 0 - success, otherwise - failure
1329 **/
1330 PetscErrorCode MatCeedContextCreate(DM dm_x, DM dm_y, Vec X_loc, Vec Y_loc_transpose, CeedOperator op_mult, CeedOperator op_mult_transpose,
1331                                     PetscLogEvent log_event_mult, PetscLogEvent log_event_mult_transpose, PetscLogEvent log_event_ceed_mult,
1332                                     PetscLogEvent log_event_ceed_mult_transpose, MatCeedContext *ctx) {
1333   CeedSize x_loc_len, y_loc_len;
1334 
1335   PetscFunctionBeginUser;
1336 
1337   // Allocate
1338   PetscCall(PetscNew(ctx));
1339   (*ctx)->ref_count = 1;
1340 
1341   // Logging
1342   (*ctx)->log_event_mult                = log_event_mult;
1343   (*ctx)->log_event_mult_transpose      = log_event_mult_transpose;
1344   (*ctx)->log_event_ceed_mult           = log_event_ceed_mult;
1345   (*ctx)->log_event_ceed_mult_transpose = log_event_ceed_mult_transpose;
1346 
1347   // PETSc objects
1348   PetscCall(DMReferenceCopy(dm_x, &(*ctx)->dm_x));
1349   PetscCall(DMReferenceCopy(dm_y, &(*ctx)->dm_y));
1350   if (X_loc) PetscCall(VecReferenceCopy(X_loc, &(*ctx)->X_loc));
1351   if (Y_loc_transpose) PetscCall(VecReferenceCopy(Y_loc_transpose, &(*ctx)->Y_loc_transpose));
1352 
1353   // Memtype
1354   {
1355     const PetscScalar *x;
1356     Vec                X;
1357 
1358     PetscCall(DMGetLocalVector(dm_x, &X));
1359     PetscCall(VecGetArrayReadAndMemType(X, &x, &(*ctx)->mem_type));
1360     PetscCall(VecRestoreArrayReadAndMemType(X, &x));
1361     PetscCall(DMRestoreLocalVector(dm_x, &X));
1362   }
1363 
1364   // libCEED objects
1365   PetscCheck(CeedOperatorGetCeed(op_mult, &(*ctx)->ceed) == CEED_ERROR_SUCCESS, PETSC_COMM_SELF, PETSC_ERR_LIB,
1366              "retrieving Ceed context object failed");
1367   PetscCallCeed((*ctx)->ceed, CeedReference((*ctx)->ceed));
1368   PetscCallCeed((*ctx)->ceed, CeedOperatorGetActiveVectorLengths(op_mult, &x_loc_len, &y_loc_len));
1369   PetscCallCeed((*ctx)->ceed, CeedOperatorReferenceCopy(op_mult, &(*ctx)->op_mult));
1370   if (op_mult_transpose) PetscCallCeed((*ctx)->ceed, CeedOperatorReferenceCopy(op_mult_transpose, &(*ctx)->op_mult_transpose));
1371   PetscCallCeed((*ctx)->ceed, CeedVectorCreate((*ctx)->ceed, x_loc_len, &(*ctx)->x_loc));
1372   PetscCallCeed((*ctx)->ceed, CeedVectorCreate((*ctx)->ceed, y_loc_len, &(*ctx)->y_loc));
1373 
1374   // Flop counting
1375   {
1376     CeedSize ceed_flops_estimate = 0;
1377 
1378     PetscCallCeed((*ctx)->ceed, CeedOperatorGetFlopsEstimate(op_mult, &ceed_flops_estimate));
1379     (*ctx)->flops_mult = ceed_flops_estimate;
1380     if (op_mult_transpose) {
1381       PetscCallCeed((*ctx)->ceed, CeedOperatorGetFlopsEstimate(op_mult_transpose, &ceed_flops_estimate));
1382       (*ctx)->flops_mult_transpose = ceed_flops_estimate;
1383     }
1384   }
1385 
1386   // Check sizes
1387   if (x_loc_len > 0 || y_loc_len > 0) {
1388     CeedSize ctx_x_loc_len, ctx_y_loc_len;
1389     PetscInt X_loc_len, dm_x_loc_len, Y_loc_len, dm_y_loc_len;
1390     Vec      dm_X_loc, dm_Y_loc;
1391 
1392     // -- Input
1393     PetscCall(DMGetLocalVector(dm_x, &dm_X_loc));
1394     PetscCall(VecGetLocalSize(dm_X_loc, &dm_x_loc_len));
1395     PetscCall(DMRestoreLocalVector(dm_x, &dm_X_loc));
1396     PetscCallCeed((*ctx)->ceed, CeedVectorGetLength((*ctx)->x_loc, &ctx_x_loc_len));
1397     if (X_loc) {
1398       PetscCall(VecGetLocalSize(X_loc, &X_loc_len));
1399       PetscCheck(X_loc_len == dm_x_loc_len, PETSC_COMM_SELF, PETSC_ERR_LIB,
1400                  "X_loc (%" PetscInt_FMT ") must match dm_x (%" PetscInt_FMT ") dimensions", X_loc_len, dm_x_loc_len);
1401     }
1402     PetscCheck(x_loc_len == dm_x_loc_len, PETSC_COMM_SELF, PETSC_ERR_LIB, "op (%" CeedSize_FMT ") must match dm_x (%" PetscInt_FMT ") dimensions",
1403                x_loc_len, dm_x_loc_len);
1404     PetscCheck(x_loc_len == ctx_x_loc_len, PETSC_COMM_SELF, PETSC_ERR_LIB, "x_loc (%" CeedSize_FMT ") must match op dimensions (%" CeedSize_FMT ")",
1405                x_loc_len, ctx_x_loc_len);
1406 
1407     // -- Output
1408     PetscCall(DMGetLocalVector(dm_y, &dm_Y_loc));
1409     PetscCall(VecGetLocalSize(dm_Y_loc, &dm_y_loc_len));
1410     PetscCall(DMRestoreLocalVector(dm_y, &dm_Y_loc));
1411     PetscCallCeed((*ctx)->ceed, CeedVectorGetLength((*ctx)->y_loc, &ctx_y_loc_len));
1412     PetscCheck(ctx_y_loc_len == dm_y_loc_len, PETSC_COMM_SELF, PETSC_ERR_LIB, "op (%" CeedSize_FMT ") must match dm_y (%" PetscInt_FMT ") dimensions",
1413                ctx_y_loc_len, dm_y_loc_len);
1414 
1415     // -- Transpose
1416     if (Y_loc_transpose) {
1417       PetscCall(VecGetLocalSize(Y_loc_transpose, &Y_loc_len));
1418       PetscCheck(Y_loc_len == dm_y_loc_len, PETSC_COMM_SELF, PETSC_ERR_LIB,
1419                  "Y_loc_transpose (%" PetscInt_FMT ") must match dm_y (%" PetscInt_FMT ") dimensions", Y_loc_len, dm_y_loc_len);
1420     }
1421   }
1422   PetscFunctionReturn(PETSC_SUCCESS);
1423 }
1424 
1425 /**
1426   @brief Increment reference counter for `MATCEED` context.
1427 
1428   Not collective across MPI processes.
1429 
1430   @param[in,out]  ctx  Context data
1431 
1432   @return An error code: 0 - success, otherwise - failure
1433 **/
1434 PetscErrorCode MatCeedContextReference(MatCeedContext ctx) {
1435   PetscFunctionBeginUser;
1436   ctx->ref_count++;
1437   PetscFunctionReturn(PETSC_SUCCESS);
1438 }
1439 
1440 /**
1441   @brief Copy reference for `MATCEED`.
1442          Note: If `ctx_copy` is non-null, it is assumed to be a valid pointer to a `MatCeedContext`.
1443 
1444   Not collective across MPI processes.
1445 
1446   @param[in]   ctx       Context data
1447   @param[out]  ctx_copy  Copy of pointer to context data
1448 
1449   @return An error code: 0 - success, otherwise - failure
1450 **/
1451 PetscErrorCode MatCeedContextReferenceCopy(MatCeedContext ctx, MatCeedContext *ctx_copy) {
1452   PetscFunctionBeginUser;
1453   PetscCall(MatCeedContextReference(ctx));
1454   PetscCall(MatCeedContextDestroy(*ctx_copy));
1455   *ctx_copy = ctx;
1456   PetscFunctionReturn(PETSC_SUCCESS);
1457 }
1458 
1459 /**
1460   @brief Destroy context data for operator application.
1461 
1462   Collective across MPI processes.
1463 
1464   @param[in,out]  ctx  Context data for operator evaluation
1465 
1466   @return An error code: 0 - success, otherwise - failure
1467 **/
1468 PetscErrorCode MatCeedContextDestroy(MatCeedContext ctx) {
1469   PetscFunctionBeginUser;
1470   if (!ctx || --ctx->ref_count > 0) PetscFunctionReturn(PETSC_SUCCESS);
1471 
1472   // PETSc objects
1473   PetscCall(DMDestroy(&ctx->dm_x));
1474   PetscCall(DMDestroy(&ctx->dm_y));
1475   PetscCall(VecDestroy(&ctx->X_loc));
1476   PetscCall(VecDestroy(&ctx->Y_loc_transpose));
1477   PetscCall(MatDestroy(&ctx->mat_assembled_full_internal));
1478   PetscCall(MatDestroy(&ctx->mat_assembled_pbd_internal));
1479   PetscCall(PetscFree(ctx->coo_mat_type));
1480   PetscCall(PetscFree(ctx->mats_assembled_full));
1481   PetscCall(PetscFree(ctx->mats_assembled_pbd));
1482 
1483   // libCEED objects
1484   PetscCallCeed(ctx->ceed, CeedVectorDestroy(&ctx->x_loc));
1485   PetscCallCeed(ctx->ceed, CeedVectorDestroy(&ctx->y_loc));
1486   PetscCallCeed(ctx->ceed, CeedVectorDestroy(&ctx->coo_values_full));
1487   PetscCallCeed(ctx->ceed, CeedVectorDestroy(&ctx->coo_values_pbd));
1488   PetscCallCeed(ctx->ceed, CeedOperatorDestroy(&ctx->op_mult));
1489   PetscCallCeed(ctx->ceed, CeedOperatorDestroy(&ctx->op_mult_transpose));
1490   PetscCheck(CeedDestroy(&ctx->ceed) == CEED_ERROR_SUCCESS, PETSC_COMM_SELF, PETSC_ERR_LIB, "destroying libCEED context object failed");
1491 
1492   // Deallocate
1493   ctx->is_destroyed = PETSC_TRUE;  // Flag as destroyed in case someone has stale ref
1494   PetscCall(PetscFree(ctx));
1495   PetscFunctionReturn(PETSC_SUCCESS);
1496 }
1497 
1498 /**
1499   @brief Compute the diagonal of an operator via libCEED.
1500 
1501   Collective across MPI processes.
1502 
1503   @param[in]   A  `MATCEED`
1504   @param[out]  D  Vector holding operator diagonal
1505 
1506   @return An error code: 0 - success, otherwise - failure
1507 **/
1508 PetscErrorCode MatGetDiagonal_Ceed(Mat A, Vec D) {
1509   PetscMemType   mem_type;
1510   Vec            D_loc;
1511   MatCeedContext ctx;
1512 
1513   PetscFunctionBeginUser;
1514   PetscCall(MatShellGetContext(A, &ctx));
1515 
1516   // Place PETSc vector in libCEED vector
1517   PetscCall(PetscLogEventBegin(MATCEED_ASSEMBLE_DIAGONAL, A, D, NULL, NULL));
1518   PetscCall(DMGetLocalVector(ctx->dm_x, &D_loc));
1519   PetscCall(VecPetscToCeed(D_loc, &mem_type, ctx->x_loc));
1520 
1521   // Compute Diagonal
1522   PetscCall(PetscLogEventBegin(MATCEED_ASSEMBLE_DIAGONAL_CEEDOP, A, D, NULL, NULL));
1523   PetscCallCeed(ctx->ceed, CeedOperatorLinearAssembleDiagonal(ctx->op_mult, ctx->x_loc, CEED_REQUEST_IMMEDIATE));
1524   PetscCall(PetscLogEventEnd(MATCEED_ASSEMBLE_DIAGONAL_CEEDOP, A, D, NULL, NULL));
1525 
1526   // Restore PETSc vector
1527   PetscCall(VecCeedToPetsc(ctx->x_loc, mem_type, D_loc));
1528 
1529   // Local-to-Global
1530   PetscCall(VecZeroEntries(D));
1531   PetscCall(DMLocalToGlobal(ctx->dm_x, D_loc, ADD_VALUES, D));
1532   PetscCall(DMRestoreLocalVector(ctx->dm_x, &D_loc));
1533   PetscCall(PetscLogEventEnd(MATCEED_ASSEMBLE_DIAGONAL, A, D, NULL, NULL));
1534   PetscFunctionReturn(PETSC_SUCCESS);
1535 }
1536 
1537 /**
1538   @brief Compute `A X = Y` for a `MATCEED`.
1539 
1540   Collective across MPI processes.
1541 
1542   @param[in]   A  `MATCEED`
1543   @param[in]   X  Input PETSc vector
1544   @param[out]  Y  Output PETSc vector
1545 
1546   @return An error code: 0 - success, otherwise - failure
1547 **/
1548 PetscErrorCode MatMult_Ceed(Mat A, Vec X, Vec Y) {
1549   MatCeedContext ctx;
1550 
1551   PetscFunctionBeginUser;
1552   PetscCall(MatShellGetContext(A, &ctx));
1553   PetscCall(PetscLogEventBegin(ctx->log_event_mult, A, X, Y, NULL));
1554 
1555   {
1556     PetscMemType x_mem_type, y_mem_type;
1557     Vec          X_loc = ctx->X_loc, Y_loc;
1558 
1559     // Get local vectors
1560     if (!ctx->X_loc) PetscCall(DMGetLocalVector(ctx->dm_x, &X_loc));
1561     PetscCall(DMGetLocalVector(ctx->dm_y, &Y_loc));
1562 
1563     // Global-to-local
1564     PetscCall(DMGlobalToLocal(ctx->dm_x, X, INSERT_VALUES, X_loc));
1565 
1566     // Setup libCEED vectors
1567     PetscCall(VecReadPetscToCeed(X_loc, &x_mem_type, ctx->x_loc));
1568     PetscCall(VecZeroEntries(Y_loc));
1569     PetscCall(VecPetscToCeed(Y_loc, &y_mem_type, ctx->y_loc));
1570 
1571     // Apply libCEED operator
1572     PetscCall(PetscLogGpuTimeBegin());
1573     PetscCall(PetscLogEventBegin(ctx->log_event_ceed_mult, A, X, Y, NULL));
1574     PetscCallCeed(ctx->ceed, CeedOperatorApplyAdd(ctx->op_mult, ctx->x_loc, ctx->y_loc, CEED_REQUEST_IMMEDIATE));
1575     PetscCall(PetscLogEventEnd(ctx->log_event_ceed_mult, A, X, Y, NULL));
1576     PetscCall(PetscLogGpuTimeEnd());
1577 
1578     // Restore PETSc vectors
1579     PetscCall(VecReadCeedToPetsc(ctx->x_loc, x_mem_type, X_loc));
1580     PetscCall(VecCeedToPetsc(ctx->y_loc, y_mem_type, Y_loc));
1581 
1582     // Local-to-global
1583     PetscCall(VecZeroEntries(Y));
1584     PetscCall(DMLocalToGlobal(ctx->dm_y, Y_loc, ADD_VALUES, Y));
1585 
1586     // Restore local vectors, as needed
1587     if (!ctx->X_loc) PetscCall(DMRestoreLocalVector(ctx->dm_x, &X_loc));
1588     PetscCall(DMRestoreLocalVector(ctx->dm_y, &Y_loc));
1589   }
1590 
1591   // Log flops
1592   if (PetscMemTypeDevice(ctx->mem_type)) PetscCall(PetscLogGpuFlops(ctx->flops_mult));
1593   else PetscCall(PetscLogFlops(ctx->flops_mult));
1594   PetscCall(PetscLogEventEnd(ctx->log_event_mult, A, X, Y, NULL));
1595   PetscFunctionReturn(PETSC_SUCCESS);
1596 }
1597 
1598 /**
1599   @brief Compute `A^T Y = X` for a `MATCEED`.
1600 
1601   Collective across MPI processes.
1602 
1603   @param[in]   A  `MATCEED`
1604   @param[in]   Y  Input PETSc vector
1605   @param[out]  X  Output PETSc vector
1606 
1607   @return An error code: 0 - success, otherwise - failure
1608 **/
1609 PetscErrorCode MatMultTranspose_Ceed(Mat A, Vec Y, Vec X) {
1610   MatCeedContext ctx;
1611 
1612   PetscFunctionBeginUser;
1613   PetscCall(MatShellGetContext(A, &ctx));
1614   PetscCall(PetscLogEventBegin(ctx->log_event_mult_transpose, A, Y, X, NULL));
1615 
1616   {
1617     PetscMemType x_mem_type, y_mem_type;
1618     Vec          X_loc, Y_loc = ctx->Y_loc_transpose;
1619 
1620     // Get local vectors
1621     if (!ctx->Y_loc_transpose) PetscCall(DMGetLocalVector(ctx->dm_y, &Y_loc));
1622     PetscCall(DMGetLocalVector(ctx->dm_x, &X_loc));
1623 
1624     // Global-to-local
1625     PetscCall(DMGlobalToLocal(ctx->dm_y, Y, INSERT_VALUES, Y_loc));
1626 
1627     // Setup libCEED vectors
1628     PetscCall(VecReadPetscToCeed(Y_loc, &y_mem_type, ctx->y_loc));
1629     PetscCall(VecZeroEntries(X_loc));
1630     PetscCall(VecPetscToCeed(X_loc, &x_mem_type, ctx->x_loc));
1631 
1632     // Apply libCEED operator
1633     PetscCall(PetscLogGpuTimeBegin());
1634     PetscCall(PetscLogEventBegin(ctx->log_event_ceed_mult_transpose, A, Y, X, NULL));
1635     PetscCallCeed(ctx->ceed, CeedOperatorApplyAdd(ctx->op_mult_transpose, ctx->y_loc, ctx->x_loc, CEED_REQUEST_IMMEDIATE));
1636     PetscCall(PetscLogEventEnd(ctx->log_event_ceed_mult_transpose, A, Y, X, NULL));
1637     PetscCall(PetscLogGpuTimeEnd());
1638 
1639     // Restore PETSc vectors
1640     PetscCall(VecReadCeedToPetsc(ctx->y_loc, y_mem_type, Y_loc));
1641     PetscCall(VecCeedToPetsc(ctx->x_loc, x_mem_type, X_loc));
1642 
1643     // Local-to-global
1644     PetscCall(VecZeroEntries(X));
1645     PetscCall(DMLocalToGlobal(ctx->dm_x, X_loc, ADD_VALUES, X));
1646 
1647     // Restore local vectors, as needed
1648     if (!ctx->Y_loc_transpose) PetscCall(DMRestoreLocalVector(ctx->dm_y, &Y_loc));
1649     PetscCall(DMRestoreLocalVector(ctx->dm_x, &X_loc));
1650   }
1651 
1652   // Log flops
1653   if (PetscMemTypeDevice(ctx->mem_type)) PetscCall(PetscLogGpuFlops(ctx->flops_mult_transpose));
1654   else PetscCall(PetscLogFlops(ctx->flops_mult_transpose));
1655   PetscCall(PetscLogEventEnd(ctx->log_event_mult_transpose, A, Y, X, NULL));
1656   PetscFunctionReturn(PETSC_SUCCESS);
1657 }
1658