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