xref: /honee/src/mat-ceed.c (revision c63b910fa7a793fc9e5914451e74158ea05ce0bb)
158600ac3SJames Wright /// @file
240d80af1SJames Wright /// MatCEED implementation
358600ac3SJames Wright 
458600ac3SJames Wright #include <ceed.h>
558600ac3SJames Wright #include <ceed/backend.h>
658600ac3SJames Wright #include <mat-ceed-impl.h>
758600ac3SJames Wright #include <mat-ceed.h>
840d80af1SJames Wright #include <petsc-ceed-utils.h>
940d80af1SJames Wright #include <petsc-ceed.h>
1058600ac3SJames Wright #include <petscdmplex.h>
1158600ac3SJames Wright #include <stdlib.h>
1258600ac3SJames Wright #include <string.h>
1358600ac3SJames Wright 
1458600ac3SJames Wright PetscClassId  MATCEED_CLASSID;
15*c63b910fSJames Wright PetscLogEvent MATCEED_MULT, MATCEED_MULT_CEEDOP, MATCEED_MULT_TRANSPOSE, MATCEED_MULT_TRANSPOSE_CEEDOP, MATCEED_ASSEMBLE_DIAGONAL,
16*c63b910fSJames Wright     MATCEED_ASSEMBLE_DIAGONAL_CEEDOP, MATCEED_SETUP_PBDIAGONAL, MATCEED_SETUP_PBDIAGONAL_CEEDOP, MATCEED_ASSEMBLE_PBDIAGONAL,
17*c63b910fSJames Wright     MATCEED_ASSEMBLE_PBDIAGONAL_CEEDOP, MATCEED_SETUP_FULL, MATCEED_SETUP_FULL_CEEDOP, MATCEED_ASSEMBLE_FULL, MATCEED_ASSEMBLE_FULL_CEEDOP;
1858600ac3SJames Wright 
1958600ac3SJames Wright /**
2058600ac3SJames Wright   @brief Register MATCEED log events.
2158600ac3SJames Wright 
2258600ac3SJames Wright   Not collective across MPI processes.
2358600ac3SJames Wright 
2458600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
2558600ac3SJames Wright **/
2658600ac3SJames Wright static PetscErrorCode MatCeedRegisterLogEvents() {
2740d80af1SJames Wright   static PetscBool registered = PETSC_FALSE;
2858600ac3SJames Wright 
2958600ac3SJames Wright   PetscFunctionBeginUser;
3058600ac3SJames Wright   if (registered) PetscFunctionReturn(PETSC_SUCCESS);
31*c63b910fSJames Wright   PetscCall(PetscClassIdRegister("MatCEED", &MATCEED_CLASSID));
32*c63b910fSJames Wright   PetscCall(PetscLogEventRegister("MatCEEDMul", MATCEED_CLASSID, &MATCEED_MULT));
33*c63b910fSJames Wright   PetscCall(PetscLogEventRegister("MatCEEDMulCeed", MATCEED_CLASSID, &MATCEED_MULT_CEEDOP));
34*c63b910fSJames Wright   PetscCall(PetscLogEventRegister("MatCEEDMulT", MATCEED_CLASSID, &MATCEED_MULT_TRANSPOSE));
35*c63b910fSJames Wright   PetscCall(PetscLogEventRegister("MatCEEDMulTCeed", MATCEED_CLASSID, &MATCEED_MULT_TRANSPOSE_CEEDOP));
36*c63b910fSJames Wright   PetscCall(PetscLogEventRegister("MatCEEDAsmDiag", MATCEED_CLASSID, &MATCEED_ASSEMBLE_DIAGONAL));
37*c63b910fSJames Wright   PetscCall(PetscLogEventRegister("MatCEEDAsmPBDSU", MATCEED_CLASSID, &MATCEED_SETUP_PBDIAGONAL));
38*c63b910fSJames Wright   PetscCall(PetscLogEventRegister("MatCEEDAsmPBDSUCeed", MATCEED_CLASSID, &MATCEED_SETUP_PBDIAGONAL_CEEDOP));
39*c63b910fSJames Wright   PetscCall(PetscLogEventRegister("MatCEEDAsmPBD", MATCEED_CLASSID, &MATCEED_ASSEMBLE_PBDIAGONAL));
40*c63b910fSJames Wright   PetscCall(PetscLogEventRegister("MatCEEDAsmPBDCeed", MATCEED_CLASSID, &MATCEED_ASSEMBLE_PBDIAGONAL_CEEDOP));
41*c63b910fSJames Wright   PetscCall(PetscLogEventRegister("MatCEEDAsmSU", MATCEED_CLASSID, &MATCEED_SETUP_FULL));
42*c63b910fSJames Wright   PetscCall(PetscLogEventRegister("MatCEEDAsmSUCeed", MATCEED_CLASSID, &MATCEED_SETUP_FULL_CEEDOP));
43*c63b910fSJames Wright   PetscCall(PetscLogEventRegister("MatCEEDAsm", MATCEED_CLASSID, &MATCEED_ASSEMBLE_FULL));
44*c63b910fSJames Wright   PetscCall(PetscLogEventRegister("MatCEEDAsmCeed", MATCEED_CLASSID, &MATCEED_ASSEMBLE_FULL_CEEDOP));
4540d80af1SJames Wright   registered = PETSC_TRUE;
4658600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
4758600ac3SJames Wright }
4858600ac3SJames Wright 
4958600ac3SJames Wright /**
5058600ac3SJames Wright   @brief Assemble the point block diagonal of a `MATCEED` into a `MATAIJ` or similar.
5158600ac3SJames Wright          The `mat_coo` preallocation is set to match the sparsity pattern of `mat_ceed`.
5258600ac3SJames Wright          The caller is responsible for assuring the global and local sizes are compatible, otherwise this function will fail.
5358600ac3SJames Wright 
5458600ac3SJames Wright   Collective across MPI processes.
5558600ac3SJames Wright 
5658600ac3SJames Wright   @param[in]      mat_ceed  `MATCEED` to assemble
5758600ac3SJames Wright   @param[in,out]  mat_coo   `MATAIJ` or similar to assemble into
5858600ac3SJames Wright 
5958600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
6058600ac3SJames Wright **/
6158600ac3SJames Wright static PetscErrorCode MatCeedAssemblePointBlockDiagonalCOO(Mat mat_ceed, Mat mat_coo) {
6258600ac3SJames Wright   MatCeedContext ctx;
6358600ac3SJames Wright 
6458600ac3SJames Wright   PetscFunctionBeginUser;
6558600ac3SJames Wright   PetscCall(MatShellGetContext(mat_ceed, &ctx));
6658600ac3SJames Wright 
6758600ac3SJames Wright   // Check if COO pattern set
6858600ac3SJames Wright   {
6958600ac3SJames Wright     PetscInt index = -1;
7058600ac3SJames Wright 
7158600ac3SJames Wright     for (PetscInt i = 0; i < ctx->num_mats_assembled_pbd; i++) {
7258600ac3SJames Wright       if (ctx->mats_assembled_pbd[i] == mat_coo) index = i;
7358600ac3SJames Wright     }
7458600ac3SJames Wright     if (index == -1) {
7558600ac3SJames Wright       PetscInt     *rows_petsc = NULL, *cols_petsc = NULL;
7658600ac3SJames Wright       CeedInt      *rows_ceed, *cols_ceed;
7758600ac3SJames Wright       PetscCount    num_entries;
7858600ac3SJames Wright       PetscLogStage stage_amg_setup;
7958600ac3SJames Wright 
8058600ac3SJames Wright       // -- Assemble sparsity pattern if mat hasn't been assembled before
81*c63b910fSJames Wright       PetscCall(PetscLogStageGetId("MatCEED Asm Setup", &stage_amg_setup));
8258600ac3SJames Wright       if (stage_amg_setup == -1) {
83*c63b910fSJames Wright         PetscCall(PetscLogStageRegister("MatCEED Asm Setup", &stage_amg_setup));
8458600ac3SJames Wright       }
8558600ac3SJames Wright       PetscCall(PetscLogStagePush(stage_amg_setup));
86*c63b910fSJames Wright       PetscCall(PetscLogEventBegin(MATCEED_SETUP_PBDIAGONAL, mat_ceed, mat_coo, NULL, NULL));
87*c63b910fSJames Wright       PetscCall(PetscLogEventBegin(MATCEED_SETUP_PBDIAGONAL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
8850f50432SJames Wright       PetscCallCeed(ctx->ceed, CeedOperatorLinearAssemblePointBlockDiagonalSymbolic(ctx->op_mult, &num_entries, &rows_ceed, &cols_ceed));
89*c63b910fSJames Wright       PetscCall(PetscLogEventEnd(MATCEED_SETUP_PBDIAGONAL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
90a7dac1d5SJames Wright       PetscCall(IntArrayCeedToPetsc(num_entries, &rows_ceed, &rows_petsc));
91a7dac1d5SJames Wright       PetscCall(IntArrayCeedToPetsc(num_entries, &cols_ceed, &cols_petsc));
9258600ac3SJames Wright       PetscCall(MatSetPreallocationCOOLocal(mat_coo, num_entries, rows_petsc, cols_petsc));
9358600ac3SJames Wright       free(rows_petsc);
9458600ac3SJames Wright       free(cols_petsc);
9550f50432SJames Wright       if (!ctx->coo_values_pbd) PetscCallCeed(ctx->ceed, CeedVectorCreate(ctx->ceed, num_entries, &ctx->coo_values_pbd));
9658600ac3SJames Wright       PetscCall(PetscRealloc(++ctx->num_mats_assembled_pbd * sizeof(Mat), &ctx->mats_assembled_pbd));
9758600ac3SJames Wright       ctx->mats_assembled_pbd[ctx->num_mats_assembled_pbd - 1] = mat_coo;
98*c63b910fSJames Wright       PetscCall(PetscLogEventEnd(MATCEED_SETUP_PBDIAGONAL, mat_ceed, mat_coo, NULL, NULL));
9958600ac3SJames Wright       PetscCall(PetscLogStagePop());
10058600ac3SJames Wright     }
10158600ac3SJames Wright   }
10258600ac3SJames Wright 
10358600ac3SJames Wright   // Assemble mat_ceed
104*c63b910fSJames Wright   PetscCall(PetscLogEventBegin(MATCEED_ASSEMBLE_PBDIAGONAL, mat_ceed, mat_coo, NULL, NULL));
10558600ac3SJames Wright   PetscCall(MatAssemblyBegin(mat_coo, MAT_FINAL_ASSEMBLY));
10658600ac3SJames Wright   {
10758600ac3SJames Wright     const CeedScalar *values;
10858600ac3SJames Wright     MatType           mat_type;
10958600ac3SJames Wright     CeedMemType       mem_type = CEED_MEM_HOST;
11058600ac3SJames Wright     PetscBool         is_spd, is_spd_known;
11158600ac3SJames Wright 
11258600ac3SJames Wright     PetscCall(MatGetType(mat_coo, &mat_type));
11358600ac3SJames Wright     if (strstr(mat_type, "cusparse")) mem_type = CEED_MEM_DEVICE;
11458600ac3SJames Wright     else if (strstr(mat_type, "kokkos")) mem_type = CEED_MEM_DEVICE;
11558600ac3SJames Wright     else mem_type = CEED_MEM_HOST;
11658600ac3SJames Wright 
117*c63b910fSJames Wright     PetscCall(PetscLogEventBegin(MATCEED_ASSEMBLE_PBDIAGONAL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
11850f50432SJames Wright     PetscCallCeed(ctx->ceed, CeedOperatorLinearAssemblePointBlockDiagonal(ctx->op_mult, ctx->coo_values_pbd, CEED_REQUEST_IMMEDIATE));
119*c63b910fSJames Wright     PetscCall(PetscLogEventEnd(MATCEED_ASSEMBLE_PBDIAGONAL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
12050f50432SJames Wright     PetscCallCeed(ctx->ceed, CeedVectorGetArrayRead(ctx->coo_values_pbd, mem_type, &values));
12158600ac3SJames Wright     PetscCall(MatSetValuesCOO(mat_coo, values, INSERT_VALUES));
12258600ac3SJames Wright     PetscCall(MatIsSPDKnown(mat_ceed, &is_spd_known, &is_spd));
12358600ac3SJames Wright     if (is_spd_known) PetscCall(MatSetOption(mat_coo, MAT_SPD, is_spd));
12450f50432SJames Wright     PetscCallCeed(ctx->ceed, CeedVectorRestoreArrayRead(ctx->coo_values_pbd, &values));
12558600ac3SJames Wright   }
12658600ac3SJames Wright   PetscCall(MatAssemblyEnd(mat_coo, MAT_FINAL_ASSEMBLY));
127*c63b910fSJames Wright   PetscCall(PetscLogEventEnd(MATCEED_ASSEMBLE_PBDIAGONAL, mat_ceed, mat_coo, NULL, NULL));
12858600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
12958600ac3SJames Wright }
13058600ac3SJames Wright 
13158600ac3SJames Wright /**
13258600ac3SJames Wright   @brief Assemble inner `Mat` for diagonal `PC` operations
13358600ac3SJames Wright 
13458600ac3SJames Wright   Collective across MPI processes.
13558600ac3SJames Wright 
13658600ac3SJames Wright   @param[in]   mat_ceed      `MATCEED` to invert
13758600ac3SJames Wright   @param[in]   use_ceed_pbd  Boolean flag to use libCEED PBD assembly
13858600ac3SJames Wright   @param[out]  mat_inner     Inner `Mat` for diagonal operations
13958600ac3SJames Wright 
14058600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
14158600ac3SJames Wright **/
14258600ac3SJames Wright static PetscErrorCode MatCeedAssembleInnerBlockDiagonalMat(Mat mat_ceed, PetscBool use_ceed_pbd, Mat *mat_inner) {
14358600ac3SJames Wright   MatCeedContext ctx;
14458600ac3SJames Wright 
14558600ac3SJames Wright   PetscFunctionBeginUser;
14658600ac3SJames Wright   PetscCall(MatShellGetContext(mat_ceed, &ctx));
14758600ac3SJames Wright   if (use_ceed_pbd) {
14858600ac3SJames Wright     // Check if COO pattern set
14940d80af1SJames Wright     if (!ctx->mat_assembled_pbd_internal) PetscCall(MatCeedCreateMatCOO(mat_ceed, &ctx->mat_assembled_pbd_internal));
15058600ac3SJames Wright 
15158600ac3SJames Wright     // Assemble mat_assembled_full_internal
15258600ac3SJames Wright     PetscCall(MatCeedAssemblePointBlockDiagonalCOO(mat_ceed, ctx->mat_assembled_pbd_internal));
15358600ac3SJames Wright     if (mat_inner) *mat_inner = ctx->mat_assembled_pbd_internal;
15458600ac3SJames Wright   } else {
15558600ac3SJames Wright     // Check if COO pattern set
15640d80af1SJames Wright     if (!ctx->mat_assembled_full_internal) PetscCall(MatCeedCreateMatCOO(mat_ceed, &ctx->mat_assembled_full_internal));
15758600ac3SJames Wright 
15858600ac3SJames Wright     // Assemble mat_assembled_full_internal
15958600ac3SJames Wright     PetscCall(MatCeedAssembleCOO(mat_ceed, ctx->mat_assembled_full_internal));
16058600ac3SJames Wright     if (mat_inner) *mat_inner = ctx->mat_assembled_full_internal;
16158600ac3SJames Wright   }
16258600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
16358600ac3SJames Wright }
16458600ac3SJames Wright 
16558600ac3SJames Wright /**
16658600ac3SJames Wright   @brief Get `MATCEED` diagonal block for Jacobi.
16758600ac3SJames Wright 
16858600ac3SJames Wright   Collective across MPI processes.
16958600ac3SJames Wright 
17058600ac3SJames Wright   @param[in]   mat_ceed   `MATCEED` to invert
17158600ac3SJames Wright   @param[out]  mat_block  The diagonal block matrix
17258600ac3SJames Wright 
17358600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
17458600ac3SJames Wright **/
17558600ac3SJames Wright static PetscErrorCode MatGetDiagonalBlock_Ceed(Mat mat_ceed, Mat *mat_block) {
17658600ac3SJames Wright   Mat            mat_inner = NULL;
17758600ac3SJames Wright   MatCeedContext ctx;
17858600ac3SJames Wright 
17958600ac3SJames Wright   PetscFunctionBeginUser;
18058600ac3SJames Wright   PetscCall(MatShellGetContext(mat_ceed, &ctx));
18158600ac3SJames Wright 
18258600ac3SJames Wright   // Assemble inner mat if needed
18358600ac3SJames Wright   PetscCall(MatCeedAssembleInnerBlockDiagonalMat(mat_ceed, ctx->is_ceed_pbd_valid, &mat_inner));
18458600ac3SJames Wright 
18558600ac3SJames Wright   // Get block diagonal
18658600ac3SJames Wright   PetscCall(MatGetDiagonalBlock(mat_inner, mat_block));
18758600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
18858600ac3SJames Wright }
18958600ac3SJames Wright 
19058600ac3SJames Wright /**
19158600ac3SJames Wright   @brief Invert `MATCEED` diagonal block for Jacobi.
19258600ac3SJames Wright 
19358600ac3SJames Wright   Collective across MPI processes.
19458600ac3SJames Wright 
19558600ac3SJames Wright   @param[in]   mat_ceed  `MATCEED` to invert
19658600ac3SJames Wright   @param[out]  values    The block inverses in column major order
19758600ac3SJames Wright 
19858600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
19958600ac3SJames Wright **/
20058600ac3SJames Wright static PetscErrorCode MatInvertBlockDiagonal_Ceed(Mat mat_ceed, const PetscScalar **values) {
20158600ac3SJames Wright   Mat            mat_inner = NULL;
20258600ac3SJames Wright   MatCeedContext ctx;
20358600ac3SJames Wright 
20458600ac3SJames Wright   PetscFunctionBeginUser;
20558600ac3SJames Wright   PetscCall(MatShellGetContext(mat_ceed, &ctx));
20658600ac3SJames Wright 
20758600ac3SJames Wright   // Assemble inner mat if needed
20858600ac3SJames Wright   PetscCall(MatCeedAssembleInnerBlockDiagonalMat(mat_ceed, ctx->is_ceed_pbd_valid, &mat_inner));
20958600ac3SJames Wright 
21058600ac3SJames Wright   // Invert PB diagonal
21158600ac3SJames Wright   PetscCall(MatInvertBlockDiagonal(mat_inner, values));
21258600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
21358600ac3SJames Wright }
21458600ac3SJames Wright 
21558600ac3SJames Wright /**
21658600ac3SJames Wright   @brief Invert `MATCEED` variable diagonal block for Jacobi.
21758600ac3SJames Wright 
21858600ac3SJames Wright   Collective across MPI processes.
21958600ac3SJames Wright 
22058600ac3SJames Wright   @param[in]   mat_ceed     `MATCEED` to invert
22158600ac3SJames Wright   @param[in]   num_blocks   The number of blocks on the process
22258600ac3SJames Wright   @param[in]   block_sizes  The size of each block on the process
22358600ac3SJames Wright   @param[out]  values       The block inverses in column major order
22458600ac3SJames Wright 
22558600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
22658600ac3SJames Wright **/
22758600ac3SJames Wright static PetscErrorCode MatInvertVariableBlockDiagonal_Ceed(Mat mat_ceed, PetscInt num_blocks, const PetscInt *block_sizes, PetscScalar *values) {
22858600ac3SJames Wright   Mat            mat_inner = NULL;
22958600ac3SJames Wright   MatCeedContext ctx;
23058600ac3SJames Wright 
23158600ac3SJames Wright   PetscFunctionBeginUser;
23258600ac3SJames Wright   PetscCall(MatShellGetContext(mat_ceed, &ctx));
23358600ac3SJames Wright 
23458600ac3SJames Wright   // Assemble inner mat if needed
23558600ac3SJames Wright   PetscCall(MatCeedAssembleInnerBlockDiagonalMat(mat_ceed, ctx->is_ceed_vpbd_valid, &mat_inner));
23658600ac3SJames Wright 
23758600ac3SJames Wright   // Invert PB diagonal
23858600ac3SJames Wright   PetscCall(MatInvertVariableBlockDiagonal(mat_inner, num_blocks, block_sizes, values));
23958600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
24058600ac3SJames Wright }
24158600ac3SJames Wright 
242e90c2ceeSJames Wright /**
243e90c2ceeSJames Wright   @brief View `MATCEED`.
244e90c2ceeSJames Wright 
245e90c2ceeSJames Wright   Collective across MPI processes.
246e90c2ceeSJames Wright 
247e90c2ceeSJames Wright   @param[in]   mat_ceed  `MATCEED` to view
248e90c2ceeSJames Wright   @param[in]   viewer    The visualization context
249e90c2ceeSJames Wright 
250e90c2ceeSJames Wright   @return An error code: 0 - success, otherwise - failure
251e90c2ceeSJames Wright **/
252e90c2ceeSJames Wright static PetscErrorCode MatView_Ceed(Mat mat_ceed, PetscViewer viewer) {
253e90c2ceeSJames Wright   PetscBool         is_ascii;
254e90c2ceeSJames Wright   PetscViewerFormat format;
255e90c2ceeSJames Wright   PetscMPIInt       size;
256e90c2ceeSJames Wright   MatCeedContext    ctx;
257e90c2ceeSJames Wright 
258e90c2ceeSJames Wright   PetscFunctionBeginUser;
259e90c2ceeSJames Wright   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
260e90c2ceeSJames Wright   PetscCall(MatShellGetContext(mat_ceed, &ctx));
261e90c2ceeSJames Wright   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)mat_ceed), &viewer));
262e90c2ceeSJames Wright 
263e90c2ceeSJames Wright   PetscCall(PetscViewerGetFormat(viewer, &format));
264e90c2ceeSJames Wright   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)mat_ceed), &size));
265e90c2ceeSJames Wright   if (size == 1 && format == PETSC_VIEWER_LOAD_BALANCE) PetscFunctionReturn(PETSC_SUCCESS);
266e90c2ceeSJames Wright 
267e90c2ceeSJames Wright   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &is_ascii));
268e90c2ceeSJames Wright   {
269e90c2ceeSJames Wright     FILE *file;
270e90c2ceeSJames Wright 
27140d80af1SJames Wright     PetscCall(PetscViewerASCIIPrintf(viewer, "MatCEED:\n  Default COO MatType:%s\n", ctx->coo_mat_type));
272e90c2ceeSJames Wright     PetscCall(PetscViewerASCIIGetPointer(viewer, &file));
273e90c2ceeSJames Wright     PetscCall(PetscViewerASCIIPrintf(viewer, " libCEED Operator:\n"));
274e90c2ceeSJames Wright     PetscCallCeed(ctx->ceed, CeedOperatorView(ctx->op_mult, file));
275e90c2ceeSJames Wright     if (ctx->op_mult_transpose) {
276e90c2ceeSJames Wright       PetscCall(PetscViewerASCIIPrintf(viewer, "  libCEED Transpose Operator:\n"));
277e90c2ceeSJames Wright       PetscCallCeed(ctx->ceed, CeedOperatorView(ctx->op_mult_transpose, file));
278e90c2ceeSJames Wright     }
279e90c2ceeSJames Wright   }
280e90c2ceeSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
281e90c2ceeSJames Wright }
282e90c2ceeSJames Wright 
28358600ac3SJames Wright // -----------------------------------------------------------------------------
28458600ac3SJames Wright // MatCeed
28558600ac3SJames Wright // -----------------------------------------------------------------------------
28658600ac3SJames Wright 
28758600ac3SJames Wright /**
28858600ac3SJames Wright   @brief Create PETSc `Mat` from libCEED operators.
28958600ac3SJames Wright 
29058600ac3SJames Wright   Collective across MPI processes.
29158600ac3SJames Wright 
29258600ac3SJames Wright   @param[in]   dm_x                      Input `DM`
29358600ac3SJames Wright   @param[in]   dm_y                      Output `DM`
29458600ac3SJames Wright   @param[in]   op_mult                   `CeedOperator` for forward evaluation
29558600ac3SJames Wright   @param[in]   op_mult_transpose         `CeedOperator` for transpose evaluation
29658600ac3SJames Wright   @param[out]  mat                        New MatCeed
29758600ac3SJames Wright 
29858600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
29958600ac3SJames Wright **/
30058600ac3SJames Wright PetscErrorCode MatCeedCreate(DM dm_x, DM dm_y, CeedOperator op_mult, CeedOperator op_mult_transpose, Mat *mat) {
30158600ac3SJames Wright   PetscInt       X_l_size, X_g_size, Y_l_size, Y_g_size;
30258600ac3SJames Wright   VecType        vec_type;
30358600ac3SJames Wright   MatCeedContext ctx;
30458600ac3SJames Wright 
30558600ac3SJames Wright   PetscFunctionBeginUser;
30658600ac3SJames Wright   PetscCall(MatCeedRegisterLogEvents());
30758600ac3SJames Wright 
30858600ac3SJames Wright   // Collect context data
30958600ac3SJames Wright   PetscCall(DMGetVecType(dm_x, &vec_type));
31058600ac3SJames Wright   {
31158600ac3SJames Wright     Vec X;
31258600ac3SJames Wright 
31358600ac3SJames Wright     PetscCall(DMGetGlobalVector(dm_x, &X));
31458600ac3SJames Wright     PetscCall(VecGetSize(X, &X_g_size));
31558600ac3SJames Wright     PetscCall(VecGetLocalSize(X, &X_l_size));
31658600ac3SJames Wright     PetscCall(DMRestoreGlobalVector(dm_x, &X));
31758600ac3SJames Wright   }
31858600ac3SJames Wright   if (dm_y) {
31958600ac3SJames Wright     Vec Y;
32058600ac3SJames Wright 
32158600ac3SJames Wright     PetscCall(DMGetGlobalVector(dm_y, &Y));
32258600ac3SJames Wright     PetscCall(VecGetSize(Y, &Y_g_size));
32358600ac3SJames Wright     PetscCall(VecGetLocalSize(Y, &Y_l_size));
32458600ac3SJames Wright     PetscCall(DMRestoreGlobalVector(dm_y, &Y));
32558600ac3SJames Wright   } else {
32658600ac3SJames Wright     dm_y     = dm_x;
32758600ac3SJames Wright     Y_g_size = X_g_size;
32858600ac3SJames Wright     Y_l_size = X_l_size;
32958600ac3SJames Wright   }
33040d80af1SJames Wright 
33158600ac3SJames Wright   // Create context
33258600ac3SJames Wright   {
33358600ac3SJames Wright     Vec X_loc, Y_loc_transpose = NULL;
33458600ac3SJames Wright 
33558600ac3SJames Wright     PetscCall(DMCreateLocalVector(dm_x, &X_loc));
33658600ac3SJames Wright     PetscCall(VecZeroEntries(X_loc));
33758600ac3SJames Wright     if (op_mult_transpose) {
33858600ac3SJames Wright       PetscCall(DMCreateLocalVector(dm_y, &Y_loc_transpose));
33958600ac3SJames Wright       PetscCall(VecZeroEntries(Y_loc_transpose));
34058600ac3SJames Wright     }
341*c63b910fSJames Wright     PetscCall(MatCeedContextCreate(dm_x, dm_y, X_loc, Y_loc_transpose, op_mult, op_mult_transpose, MATCEED_MULT, MATCEED_MULT_TRANSPOSE,
342*c63b910fSJames Wright                                    MATCEED_MULT_CEEDOP, MATCEED_MULT_TRANSPOSE_CEEDOP, &ctx));
34358600ac3SJames Wright     PetscCall(VecDestroy(&X_loc));
34458600ac3SJames Wright     PetscCall(VecDestroy(&Y_loc_transpose));
34558600ac3SJames Wright   }
34658600ac3SJames Wright 
34758600ac3SJames Wright   // Create mat
34858600ac3SJames Wright   PetscCall(MatCreateShell(PetscObjectComm((PetscObject)dm_x), Y_l_size, X_l_size, Y_g_size, X_g_size, ctx, mat));
34958600ac3SJames Wright   PetscCall(PetscObjectChangeTypeName((PetscObject)*mat, MATCEED));
35058600ac3SJames Wright   // -- Set block and variable block sizes
35158600ac3SJames Wright   if (dm_x == dm_y) {
35258600ac3SJames Wright     MatType dm_mat_type, dm_mat_type_copy;
35358600ac3SJames Wright     Mat     temp_mat;
35458600ac3SJames Wright 
35558600ac3SJames Wright     PetscCall(DMGetMatType(dm_x, &dm_mat_type));
35658600ac3SJames Wright     PetscCall(PetscStrallocpy(dm_mat_type, (char **)&dm_mat_type_copy));
35758600ac3SJames Wright     PetscCall(DMSetMatType(dm_x, MATAIJ));
35858600ac3SJames Wright     PetscCall(DMCreateMatrix(dm_x, &temp_mat));
35958600ac3SJames Wright     PetscCall(DMSetMatType(dm_x, dm_mat_type_copy));
36058600ac3SJames Wright     PetscCall(PetscFree(dm_mat_type_copy));
36158600ac3SJames Wright 
36258600ac3SJames Wright     {
36358600ac3SJames Wright       PetscInt        block_size, num_blocks, max_vblock_size = PETSC_INT_MAX;
36458600ac3SJames Wright       const PetscInt *vblock_sizes;
36558600ac3SJames Wright 
36658600ac3SJames Wright       // -- Get block sizes
36758600ac3SJames Wright       PetscCall(MatGetBlockSize(temp_mat, &block_size));
36858600ac3SJames Wright       PetscCall(MatGetVariableBlockSizes(temp_mat, &num_blocks, &vblock_sizes));
36958600ac3SJames Wright       {
37058600ac3SJames Wright         PetscInt local_min_max[2] = {0}, global_min_max[2] = {0, PETSC_INT_MAX};
37158600ac3SJames Wright 
37258600ac3SJames Wright         for (PetscInt i = 0; i < num_blocks; i++) local_min_max[1] = PetscMax(local_min_max[1], vblock_sizes[i]);
37358600ac3SJames Wright         PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm_x), local_min_max, global_min_max));
37458600ac3SJames Wright         max_vblock_size = global_min_max[1];
37558600ac3SJames Wright       }
37658600ac3SJames Wright 
37758600ac3SJames Wright       // -- Copy block sizes
37858600ac3SJames Wright       if (block_size > 1) PetscCall(MatSetBlockSize(*mat, block_size));
37958600ac3SJames Wright       if (num_blocks) PetscCall(MatSetVariableBlockSizes(*mat, num_blocks, (PetscInt *)vblock_sizes));
38058600ac3SJames Wright 
38158600ac3SJames Wright       // -- Check libCEED compatibility
38258600ac3SJames Wright       {
38358600ac3SJames Wright         bool is_composite;
38458600ac3SJames Wright 
38558600ac3SJames Wright         ctx->is_ceed_pbd_valid  = PETSC_TRUE;
38658600ac3SJames Wright         ctx->is_ceed_vpbd_valid = PETSC_TRUE;
38750f50432SJames Wright         PetscCallCeed(ctx->ceed, CeedOperatorIsComposite(op_mult, &is_composite));
38858600ac3SJames Wright         if (is_composite) {
38958600ac3SJames Wright           CeedInt       num_sub_operators;
39058600ac3SJames Wright           CeedOperator *sub_operators;
39158600ac3SJames Wright 
39250f50432SJames Wright           PetscCallCeed(ctx->ceed, CeedCompositeOperatorGetNumSub(op_mult, &num_sub_operators));
39350f50432SJames Wright           PetscCallCeed(ctx->ceed, CeedCompositeOperatorGetSubList(op_mult, &sub_operators));
39458600ac3SJames Wright           for (CeedInt i = 0; i < num_sub_operators; i++) {
39558600ac3SJames Wright             CeedInt                  num_bases, num_comp;
39658600ac3SJames Wright             CeedBasis               *active_bases;
39758600ac3SJames Wright             CeedOperatorAssemblyData assembly_data;
39858600ac3SJames Wright 
39950f50432SJames Wright             PetscCallCeed(ctx->ceed, CeedOperatorGetOperatorAssemblyData(sub_operators[i], &assembly_data));
40050f50432SJames Wright             PetscCallCeed(ctx->ceed, CeedOperatorAssemblyDataGetBases(assembly_data, &num_bases, &active_bases, NULL, NULL, NULL, NULL));
40150f50432SJames Wright             PetscCallCeed(ctx->ceed, CeedBasisGetNumComponents(active_bases[0], &num_comp));
40258600ac3SJames Wright             if (num_bases > 1) {
40358600ac3SJames Wright               ctx->is_ceed_pbd_valid  = PETSC_FALSE;
40458600ac3SJames Wright               ctx->is_ceed_vpbd_valid = PETSC_FALSE;
40558600ac3SJames Wright             }
40658600ac3SJames Wright             if (num_comp != block_size) ctx->is_ceed_pbd_valid = PETSC_FALSE;
40758600ac3SJames Wright             if (num_comp < max_vblock_size) ctx->is_ceed_vpbd_valid = PETSC_FALSE;
40858600ac3SJames Wright           }
40958600ac3SJames Wright         } else {
41058600ac3SJames Wright           // LCOV_EXCL_START
41158600ac3SJames Wright           CeedInt                  num_bases, num_comp;
41258600ac3SJames Wright           CeedBasis               *active_bases;
41358600ac3SJames Wright           CeedOperatorAssemblyData assembly_data;
41458600ac3SJames Wright 
41550f50432SJames Wright           PetscCallCeed(ctx->ceed, CeedOperatorGetOperatorAssemblyData(op_mult, &assembly_data));
41650f50432SJames Wright           PetscCallCeed(ctx->ceed, CeedOperatorAssemblyDataGetBases(assembly_data, &num_bases, &active_bases, NULL, NULL, NULL, NULL));
41750f50432SJames Wright           PetscCallCeed(ctx->ceed, CeedBasisGetNumComponents(active_bases[0], &num_comp));
41858600ac3SJames Wright           if (num_bases > 1) {
41958600ac3SJames Wright             ctx->is_ceed_pbd_valid  = PETSC_FALSE;
42058600ac3SJames Wright             ctx->is_ceed_vpbd_valid = PETSC_FALSE;
42158600ac3SJames Wright           }
42258600ac3SJames Wright           if (num_comp != block_size) ctx->is_ceed_pbd_valid = PETSC_FALSE;
42358600ac3SJames Wright           if (num_comp < max_vblock_size) ctx->is_ceed_vpbd_valid = PETSC_FALSE;
42458600ac3SJames Wright           // LCOV_EXCL_STOP
42558600ac3SJames Wright         }
42658600ac3SJames Wright         {
42758600ac3SJames Wright           PetscInt local_is_valid[2], global_is_valid[2];
42858600ac3SJames Wright 
42958600ac3SJames Wright           local_is_valid[0] = local_is_valid[1] = ctx->is_ceed_pbd_valid;
43058600ac3SJames Wright           PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm_x), local_is_valid, global_is_valid));
43158600ac3SJames Wright           ctx->is_ceed_pbd_valid = global_is_valid[0];
43258600ac3SJames Wright           local_is_valid[0] = local_is_valid[1] = ctx->is_ceed_vpbd_valid;
43358600ac3SJames Wright           PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm_x), local_is_valid, global_is_valid));
43458600ac3SJames Wright           ctx->is_ceed_vpbd_valid = global_is_valid[0];
43558600ac3SJames Wright         }
43658600ac3SJames Wright       }
43758600ac3SJames Wright     }
43858600ac3SJames Wright     PetscCall(MatDestroy(&temp_mat));
43958600ac3SJames Wright   }
44058600ac3SJames Wright   // -- Set internal mat type
44158600ac3SJames Wright   {
44258600ac3SJames Wright     VecType vec_type;
44340d80af1SJames Wright     MatType coo_mat_type;
44458600ac3SJames Wright 
44558600ac3SJames Wright     PetscCall(VecGetType(ctx->X_loc, &vec_type));
44640d80af1SJames Wright     if (strstr(vec_type, VECCUDA)) coo_mat_type = MATAIJCUSPARSE;
44740d80af1SJames Wright     else if (strstr(vec_type, VECKOKKOS)) coo_mat_type = MATAIJKOKKOS;
44840d80af1SJames Wright     else coo_mat_type = MATAIJ;
44940d80af1SJames Wright     PetscCall(PetscStrallocpy(coo_mat_type, &ctx->coo_mat_type));
45058600ac3SJames Wright   }
45158600ac3SJames Wright   // -- Set mat operations
45258600ac3SJames Wright   PetscCall(MatShellSetContextDestroy(*mat, (PetscErrorCode(*)(void *))MatCeedContextDestroy));
453e90c2ceeSJames Wright   PetscCall(MatShellSetOperation(*mat, MATOP_VIEW, (void (*)(void))MatView_Ceed));
45458600ac3SJames Wright   PetscCall(MatShellSetOperation(*mat, MATOP_MULT, (void (*)(void))MatMult_Ceed));
45558600ac3SJames Wright   if (op_mult_transpose) PetscCall(MatShellSetOperation(*mat, MATOP_MULT_TRANSPOSE, (void (*)(void))MatMultTranspose_Ceed));
45658600ac3SJames Wright   PetscCall(MatShellSetOperation(*mat, MATOP_GET_DIAGONAL, (void (*)(void))MatGetDiagonal_Ceed));
45758600ac3SJames Wright   PetscCall(MatShellSetOperation(*mat, MATOP_GET_DIAGONAL_BLOCK, (void (*)(void))MatGetDiagonalBlock_Ceed));
45858600ac3SJames Wright   PetscCall(MatShellSetOperation(*mat, MATOP_INVERT_BLOCK_DIAGONAL, (void (*)(void))MatInvertBlockDiagonal_Ceed));
45958600ac3SJames Wright   PetscCall(MatShellSetOperation(*mat, MATOP_INVERT_VBLOCK_DIAGONAL, (void (*)(void))MatInvertVariableBlockDiagonal_Ceed));
46058600ac3SJames Wright   PetscCall(MatShellSetVecType(*mat, vec_type));
46158600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
46258600ac3SJames Wright }
46358600ac3SJames Wright 
46458600ac3SJames Wright /**
46558600ac3SJames Wright   @brief Copy `MATCEED` into a compatible `Mat` with type `MatShell` or `MATCEED`.
46658600ac3SJames Wright 
46758600ac3SJames Wright   Collective across MPI processes.
46858600ac3SJames Wright 
46958600ac3SJames Wright   @param[in]   mat_ceed   `MATCEED` to copy from
47058600ac3SJames Wright   @param[out]  mat_other  `MatShell` or `MATCEED` to copy into
47158600ac3SJames Wright 
47258600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
47358600ac3SJames Wright **/
47458600ac3SJames Wright PetscErrorCode MatCeedCopy(Mat mat_ceed, Mat mat_other) {
47558600ac3SJames Wright   PetscFunctionBeginUser;
47658600ac3SJames Wright   PetscCall(MatCeedRegisterLogEvents());
47758600ac3SJames Wright 
47858600ac3SJames Wright   // Check type compatibility
47958600ac3SJames Wright   {
48040d80af1SJames Wright     PetscBool is_matceed = PETSC_FALSE, is_matshell = PETSC_FALSE;
48158600ac3SJames Wright     MatType   mat_type_ceed, mat_type_other;
48258600ac3SJames Wright 
48358600ac3SJames Wright     PetscCall(MatGetType(mat_ceed, &mat_type_ceed));
48440d80af1SJames Wright     PetscCall(PetscStrcmp(mat_type_ceed, MATCEED, &is_matceed));
48540d80af1SJames Wright     PetscCheck(is_matceed, PETSC_COMM_SELF, PETSC_ERR_LIB, "mat_ceed must have type " MATCEED);
48640d80af1SJames Wright     PetscCall(MatGetType(mat_other, &mat_type_other));
48740d80af1SJames Wright     PetscCall(PetscStrcmp(mat_type_other, MATCEED, &is_matceed));
48840d80af1SJames Wright     PetscCall(PetscStrcmp(mat_type_other, MATSHELL, &is_matceed));
48940d80af1SJames Wright     PetscCheck(is_matceed || is_matshell, PETSC_COMM_SELF, PETSC_ERR_LIB, "mat_other must have type " MATCEED " or " MATSHELL);
49058600ac3SJames Wright   }
49158600ac3SJames Wright 
49258600ac3SJames Wright   // Check dimension compatibility
49358600ac3SJames Wright   {
49458600ac3SJames Wright     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;
49558600ac3SJames Wright 
49658600ac3SJames Wright     PetscCall(MatGetSize(mat_ceed, &Y_g_ceed_size, &X_g_ceed_size));
49758600ac3SJames Wright     PetscCall(MatGetLocalSize(mat_ceed, &Y_l_ceed_size, &X_l_ceed_size));
49858600ac3SJames Wright     PetscCall(MatGetSize(mat_ceed, &Y_g_other_size, &X_g_other_size));
49958600ac3SJames Wright     PetscCall(MatGetLocalSize(mat_ceed, &Y_l_other_size, &X_l_other_size));
50058600ac3SJames Wright     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) &&
50158600ac3SJames Wright                    (X_l_ceed_size == X_l_other_size),
50258600ac3SJames Wright                PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
50358600ac3SJames Wright                "mat_ceed and mat_other must have compatible sizes; found mat_ceed (Global: %" PetscInt_FMT ", %" PetscInt_FMT
50458600ac3SJames Wright                "; Local: %" PetscInt_FMT ", %" PetscInt_FMT ") mat_other (Global: %" PetscInt_FMT ", %" PetscInt_FMT "; Local: %" PetscInt_FMT
50558600ac3SJames Wright                ", %" PetscInt_FMT ")",
50658600ac3SJames Wright                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);
50758600ac3SJames Wright   }
50858600ac3SJames Wright 
50958600ac3SJames Wright   // Convert
51058600ac3SJames Wright   {
51158600ac3SJames Wright     VecType        vec_type;
51258600ac3SJames Wright     MatCeedContext ctx;
51358600ac3SJames Wright 
51458600ac3SJames Wright     PetscCall(PetscObjectChangeTypeName((PetscObject)mat_other, MATCEED));
51558600ac3SJames Wright     PetscCall(MatShellGetContext(mat_ceed, &ctx));
51658600ac3SJames Wright     PetscCall(MatCeedContextReference(ctx));
51758600ac3SJames Wright     PetscCall(MatShellSetContext(mat_other, ctx));
51858600ac3SJames Wright     PetscCall(MatShellSetContextDestroy(mat_other, (PetscErrorCode(*)(void *))MatCeedContextDestroy));
519e90c2ceeSJames Wright     PetscCall(MatShellSetOperation(mat_other, MATOP_VIEW, (void (*)(void))MatView_Ceed));
52058600ac3SJames Wright     PetscCall(MatShellSetOperation(mat_other, MATOP_MULT, (void (*)(void))MatMult_Ceed));
52158600ac3SJames Wright     if (ctx->op_mult_transpose) PetscCall(MatShellSetOperation(mat_other, MATOP_MULT_TRANSPOSE, (void (*)(void))MatMultTranspose_Ceed));
52258600ac3SJames Wright     PetscCall(MatShellSetOperation(mat_other, MATOP_GET_DIAGONAL, (void (*)(void))MatGetDiagonal_Ceed));
52358600ac3SJames Wright     PetscCall(MatShellSetOperation(mat_other, MATOP_GET_DIAGONAL_BLOCK, (void (*)(void))MatGetDiagonalBlock_Ceed));
52458600ac3SJames Wright     PetscCall(MatShellSetOperation(mat_other, MATOP_INVERT_BLOCK_DIAGONAL, (void (*)(void))MatInvertBlockDiagonal_Ceed));
52558600ac3SJames Wright     PetscCall(MatShellSetOperation(mat_other, MATOP_INVERT_VBLOCK_DIAGONAL, (void (*)(void))MatInvertVariableBlockDiagonal_Ceed));
52658600ac3SJames Wright     {
52758600ac3SJames Wright       PetscInt block_size;
52858600ac3SJames Wright 
52958600ac3SJames Wright       PetscCall(MatGetBlockSize(mat_ceed, &block_size));
53058600ac3SJames Wright       if (block_size > 1) PetscCall(MatSetBlockSize(mat_other, block_size));
53158600ac3SJames Wright     }
53258600ac3SJames Wright     {
53358600ac3SJames Wright       PetscInt        num_blocks;
53458600ac3SJames Wright       const PetscInt *block_sizes;
53558600ac3SJames Wright 
53658600ac3SJames Wright       PetscCall(MatGetVariableBlockSizes(mat_ceed, &num_blocks, &block_sizes));
53758600ac3SJames Wright       if (num_blocks) PetscCall(MatSetVariableBlockSizes(mat_other, num_blocks, (PetscInt *)block_sizes));
53858600ac3SJames Wright     }
53958600ac3SJames Wright     PetscCall(DMGetVecType(ctx->dm_x, &vec_type));
54058600ac3SJames Wright     PetscCall(MatShellSetVecType(mat_other, vec_type));
54158600ac3SJames Wright   }
54258600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
54358600ac3SJames Wright }
54458600ac3SJames Wright 
54558600ac3SJames Wright /**
54640d80af1SJames Wright   @brief Setup a `Mat` with the same COO pattern as a `MatCEED`.
54740d80af1SJames Wright 
54840d80af1SJames Wright   Collective across MPI processes.
54940d80af1SJames Wright 
55040d80af1SJames Wright   @param[in]   mat_ceed  `MATCEED`
55140d80af1SJames Wright   @param[out]  mat_coo   Sparse `Mat` with same COO pattern
55240d80af1SJames Wright 
55340d80af1SJames Wright   @return An error code: 0 - success, otherwise - failure
55440d80af1SJames Wright **/
55540d80af1SJames Wright PetscErrorCode MatCeedCreateMatCOO(Mat mat_ceed, Mat *mat_coo) {
55640d80af1SJames Wright   MatCeedContext ctx;
55740d80af1SJames Wright 
55840d80af1SJames Wright   PetscFunctionBeginUser;
55940d80af1SJames Wright   PetscCall(MatShellGetContext(mat_ceed, &ctx));
56040d80af1SJames Wright 
56140d80af1SJames Wright   PetscCheck(ctx->dm_x == ctx->dm_y, PetscObjectComm((PetscObject)mat_ceed), PETSC_ERR_SUP, "COO assembly only supported for MATCEED on a single DM");
56240d80af1SJames Wright 
56340d80af1SJames Wright   // Check cl mat type
56440d80af1SJames Wright   {
56540d80af1SJames Wright     PetscBool is_coo_mat_type_cl = PETSC_FALSE;
56640d80af1SJames Wright     char      coo_mat_type_cl[64];
56740d80af1SJames Wright 
56840d80af1SJames Wright     // Check for specific CL coo mat type for this Mat
56940d80af1SJames Wright     {
57040d80af1SJames Wright       const char *mat_ceed_prefix = NULL;
57140d80af1SJames Wright 
57240d80af1SJames Wright       PetscCall(MatGetOptionsPrefix(mat_ceed, &mat_ceed_prefix));
57340d80af1SJames Wright       PetscOptionsBegin(PetscObjectComm((PetscObject)mat_ceed), mat_ceed_prefix, "", NULL);
57440d80af1SJames Wright       PetscCall(PetscOptionsFList("-ceed_coo_mat_type", "Default MATCEED COO assembly MatType", NULL, MatList, coo_mat_type_cl, coo_mat_type_cl,
57540d80af1SJames Wright                                   sizeof(coo_mat_type_cl), &is_coo_mat_type_cl));
57640d80af1SJames Wright       PetscOptionsEnd();
57740d80af1SJames Wright       if (is_coo_mat_type_cl) {
57840d80af1SJames Wright         PetscCall(PetscFree(ctx->coo_mat_type));
57940d80af1SJames Wright         PetscCall(PetscStrallocpy(coo_mat_type_cl, &ctx->coo_mat_type));
58040d80af1SJames Wright       }
58140d80af1SJames Wright     }
58240d80af1SJames Wright   }
58340d80af1SJames Wright 
58440d80af1SJames Wright   // Create sparse matrix
58540d80af1SJames Wright   {
58640d80af1SJames Wright     MatType dm_mat_type, dm_mat_type_copy;
58740d80af1SJames Wright 
58840d80af1SJames Wright     PetscCall(DMGetMatType(ctx->dm_x, &dm_mat_type));
58940d80af1SJames Wright     PetscCall(PetscStrallocpy(dm_mat_type, (char **)&dm_mat_type_copy));
59040d80af1SJames Wright     PetscCall(DMSetMatType(ctx->dm_x, ctx->coo_mat_type));
59140d80af1SJames Wright     PetscCall(DMCreateMatrix(ctx->dm_x, mat_coo));
59240d80af1SJames Wright     PetscCall(DMSetMatType(ctx->dm_x, dm_mat_type_copy));
59340d80af1SJames Wright     PetscCall(PetscFree(dm_mat_type_copy));
59440d80af1SJames Wright   }
59540d80af1SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
59640d80af1SJames Wright }
59740d80af1SJames Wright 
59840d80af1SJames Wright /**
59940d80af1SJames Wright   @brief Setup the COO preallocation `MATCEED` into a `MATAIJ` or similar.
60058600ac3SJames Wright          The caller is responsible for assuring the global and local sizes are compatible, otherwise this function will fail.
60158600ac3SJames Wright 
60258600ac3SJames Wright   Collective across MPI processes.
60358600ac3SJames Wright 
60458600ac3SJames Wright   @param[in]      mat_ceed  `MATCEED` to assemble
60558600ac3SJames Wright   @param[in,out]  mat_coo   `MATAIJ` or similar to assemble into
60658600ac3SJames Wright 
60758600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
60858600ac3SJames Wright **/
60940d80af1SJames Wright PetscErrorCode MatCeedSetPreallocationCOO(Mat mat_ceed, Mat mat_coo) {
61058600ac3SJames Wright   MatCeedContext ctx;
61158600ac3SJames Wright 
61258600ac3SJames Wright   PetscFunctionBeginUser;
61358600ac3SJames Wright   PetscCall(MatShellGetContext(mat_ceed, &ctx));
61458600ac3SJames Wright 
61558600ac3SJames Wright   {
61658600ac3SJames Wright     PetscInt     *rows_petsc = NULL, *cols_petsc = NULL;
61758600ac3SJames Wright     CeedInt      *rows_ceed, *cols_ceed;
61858600ac3SJames Wright     PetscCount    num_entries;
61958600ac3SJames Wright     PetscLogStage stage_amg_setup;
62058600ac3SJames Wright 
62158600ac3SJames Wright     // -- Assemble sparsity pattern if mat hasn't been assembled before
622*c63b910fSJames Wright     PetscCall(PetscLogStageGetId("MatCEED Asm Setup", &stage_amg_setup));
62358600ac3SJames Wright     if (stage_amg_setup == -1) {
624*c63b910fSJames Wright       PetscCall(PetscLogStageRegister("MatCEED Asm Setup", &stage_amg_setup));
62558600ac3SJames Wright     }
62658600ac3SJames Wright     PetscCall(PetscLogStagePush(stage_amg_setup));
627*c63b910fSJames Wright     PetscCall(PetscLogEventBegin(MATCEED_SETUP_FULL, mat_ceed, mat_coo, NULL, NULL));
628*c63b910fSJames Wright     PetscCall(PetscLogEventBegin(MATCEED_SETUP_FULL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
62950f50432SJames Wright     PetscCallCeed(ctx->ceed, CeedOperatorLinearAssembleSymbolic(ctx->op_mult, &num_entries, &rows_ceed, &cols_ceed));
630*c63b910fSJames Wright     PetscCall(PetscLogEventEnd(MATCEED_SETUP_FULL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
631a7dac1d5SJames Wright     PetscCall(IntArrayCeedToPetsc(num_entries, &rows_ceed, &rows_petsc));
632a7dac1d5SJames Wright     PetscCall(IntArrayCeedToPetsc(num_entries, &cols_ceed, &cols_petsc));
63358600ac3SJames Wright     PetscCall(MatSetPreallocationCOOLocal(mat_coo, num_entries, rows_petsc, cols_petsc));
63458600ac3SJames Wright     free(rows_petsc);
63558600ac3SJames Wright     free(cols_petsc);
63650f50432SJames Wright     if (!ctx->coo_values_full) PetscCallCeed(ctx->ceed, CeedVectorCreate(ctx->ceed, num_entries, &ctx->coo_values_full));
63758600ac3SJames Wright     PetscCall(PetscRealloc(++ctx->num_mats_assembled_full * sizeof(Mat), &ctx->mats_assembled_full));
63858600ac3SJames Wright     ctx->mats_assembled_full[ctx->num_mats_assembled_full - 1] = mat_coo;
639*c63b910fSJames Wright     PetscCall(PetscLogEventEnd(MATCEED_SETUP_FULL, mat_ceed, mat_coo, NULL, NULL));
64058600ac3SJames Wright     PetscCall(PetscLogStagePop());
64158600ac3SJames Wright   }
64240d80af1SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
64340d80af1SJames Wright }
64440d80af1SJames Wright 
64540d80af1SJames Wright /**
64640d80af1SJames Wright   @brief Assemble a `MATCEED` into a `MATAIJ` or similar.
64740d80af1SJames Wright          The `mat_coo` preallocation is set to match the sparsity pattern of `mat_ceed`.
64840d80af1SJames Wright          The caller is responsible for assuring the global and local sizes are compatible, otherwise this function will fail.
64940d80af1SJames Wright 
65040d80af1SJames Wright   Collective across MPI processes.
65140d80af1SJames Wright 
65240d80af1SJames Wright   @param[in]      mat_ceed  `MATCEED` to assemble
65340d80af1SJames Wright   @param[in,out]  mat_coo   `MATAIJ` or similar to assemble into
65440d80af1SJames Wright 
65540d80af1SJames Wright   @return An error code: 0 - success, otherwise - failure
65640d80af1SJames Wright **/
65740d80af1SJames Wright PetscErrorCode MatCeedAssembleCOO(Mat mat_ceed, Mat mat_coo) {
65840d80af1SJames Wright   MatCeedContext ctx;
65940d80af1SJames Wright 
66040d80af1SJames Wright   PetscFunctionBeginUser;
66140d80af1SJames Wright   PetscCall(MatShellGetContext(mat_ceed, &ctx));
66240d80af1SJames Wright 
66340d80af1SJames Wright   // Set COO pattern if needed
66440d80af1SJames Wright   {
66540d80af1SJames Wright     CeedInt index = -1;
66640d80af1SJames Wright 
66740d80af1SJames Wright     for (PetscInt i = 0; i < ctx->num_mats_assembled_full; i++) {
66840d80af1SJames Wright       if (ctx->mats_assembled_full[i] == mat_coo) index = i;
66940d80af1SJames Wright     }
67040d80af1SJames Wright     if (index == -1) PetscCall(MatCeedSetPreallocationCOO(mat_ceed, mat_coo));
67158600ac3SJames Wright   }
67258600ac3SJames Wright 
67358600ac3SJames Wright   // Assemble mat_ceed
674*c63b910fSJames Wright   PetscCall(PetscLogEventBegin(MATCEED_ASSEMBLE_FULL, mat_ceed, mat_coo, NULL, NULL));
67558600ac3SJames Wright   PetscCall(MatAssemblyBegin(mat_coo, MAT_FINAL_ASSEMBLY));
67658600ac3SJames Wright   {
67758600ac3SJames Wright     const CeedScalar *values;
67858600ac3SJames Wright     MatType           mat_type;
67958600ac3SJames Wright     CeedMemType       mem_type = CEED_MEM_HOST;
68058600ac3SJames Wright     PetscBool         is_spd, is_spd_known;
68158600ac3SJames Wright 
68258600ac3SJames Wright     PetscCall(MatGetType(mat_coo, &mat_type));
68358600ac3SJames Wright     if (strstr(mat_type, "cusparse")) mem_type = CEED_MEM_DEVICE;
68458600ac3SJames Wright     else if (strstr(mat_type, "kokkos")) mem_type = CEED_MEM_DEVICE;
68558600ac3SJames Wright     else mem_type = CEED_MEM_HOST;
68658600ac3SJames Wright 
687*c63b910fSJames Wright     PetscCall(PetscLogEventBegin(MATCEED_ASSEMBLE_FULL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
68850f50432SJames Wright     PetscCallCeed(ctx->ceed, CeedOperatorLinearAssemble(ctx->op_mult, ctx->coo_values_full));
689*c63b910fSJames Wright     PetscCall(PetscLogEventEnd(MATCEED_ASSEMBLE_FULL_CEEDOP, mat_ceed, mat_coo, NULL, NULL));
69050f50432SJames Wright     PetscCallCeed(ctx->ceed, CeedVectorGetArrayRead(ctx->coo_values_full, mem_type, &values));
69158600ac3SJames Wright     PetscCall(MatSetValuesCOO(mat_coo, values, INSERT_VALUES));
69258600ac3SJames Wright     PetscCall(MatIsSPDKnown(mat_ceed, &is_spd_known, &is_spd));
69358600ac3SJames Wright     if (is_spd_known) PetscCall(MatSetOption(mat_coo, MAT_SPD, is_spd));
69450f50432SJames Wright     PetscCallCeed(ctx->ceed, CeedVectorRestoreArrayRead(ctx->coo_values_full, &values));
69558600ac3SJames Wright   }
69658600ac3SJames Wright   PetscCall(MatAssemblyEnd(mat_coo, MAT_FINAL_ASSEMBLY));
697*c63b910fSJames Wright   PetscCall(PetscLogEventEnd(MATCEED_ASSEMBLE_FULL, mat_ceed, mat_coo, NULL, NULL));
69858600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
69958600ac3SJames Wright }
70058600ac3SJames Wright 
70158600ac3SJames Wright /**
70240d80af1SJames Wright   @brief Set the current value of a context field for a `MatCEED`.
70340d80af1SJames Wright 
70440d80af1SJames Wright   Not collective across MPI processes.
70540d80af1SJames Wright 
70640d80af1SJames Wright   @param[in,out]  mat    `MatCEED`
70740d80af1SJames Wright   @param[in]      name   Name of the context field
70840d80af1SJames Wright   @param[in]      value  New context field value
70940d80af1SJames Wright 
71040d80af1SJames Wright   @return An error code: 0 - success, otherwise - failure
71140d80af1SJames Wright **/
71240d80af1SJames Wright PetscErrorCode MatCeedSetContextDouble(Mat mat, const char *name, double value) {
71340d80af1SJames Wright   PetscBool      was_updated = PETSC_FALSE;
71440d80af1SJames Wright   MatCeedContext ctx;
71540d80af1SJames Wright 
71640d80af1SJames Wright   PetscFunctionBeginUser;
71740d80af1SJames Wright   PetscCall(MatShellGetContext(mat, &ctx));
71840d80af1SJames Wright   {
71940d80af1SJames Wright     CeedContextFieldLabel label = NULL;
72040d80af1SJames Wright 
72140d80af1SJames Wright     PetscCallCeed(ctx->ceed, CeedOperatorGetContextFieldLabel(ctx->op_mult, name, &label));
72240d80af1SJames Wright     if (label) {
72340d80af1SJames Wright       PetscCallCeed(ctx->ceed, CeedOperatorSetContextDouble(ctx->op_mult, label, &value));
72440d80af1SJames Wright       was_updated = PETSC_TRUE;
72540d80af1SJames Wright     }
72640d80af1SJames Wright     if (ctx->op_mult_transpose) {
72740d80af1SJames Wright       label = NULL;
72840d80af1SJames Wright       PetscCallCeed(ctx->ceed, CeedOperatorGetContextFieldLabel(ctx->op_mult_transpose, name, &label));
72940d80af1SJames Wright       if (label) {
73040d80af1SJames Wright         PetscCallCeed(ctx->ceed, CeedOperatorSetContextDouble(ctx->op_mult_transpose, label, &value));
73140d80af1SJames Wright         was_updated = PETSC_TRUE;
73240d80af1SJames Wright       }
73340d80af1SJames Wright     }
73440d80af1SJames Wright   }
73540d80af1SJames Wright   if (was_updated) {
73640d80af1SJames Wright     PetscCall(MatAssemblyBegin(mat, MAT_FINAL_ASSEMBLY));
73740d80af1SJames Wright     PetscCall(MatAssemblyEnd(mat, MAT_FINAL_ASSEMBLY));
73840d80af1SJames Wright   }
73940d80af1SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
74040d80af1SJames Wright }
74140d80af1SJames Wright 
74240d80af1SJames Wright /**
74351bb547fSJames Wright   @brief Set the current `PetscReal` value of a context field for a `MatCEED`.
74451bb547fSJames Wright 
74551bb547fSJames Wright   Not collective across MPI processes.
74651bb547fSJames Wright 
74751bb547fSJames Wright   @param[in,out]  mat    `MatCEED`
74851bb547fSJames Wright   @param[in]      name   Name of the context field
74951bb547fSJames Wright   @param[in]      value  New context field value
75051bb547fSJames Wright 
75151bb547fSJames Wright   @return An error code: 0 - success, otherwise - failure
75251bb547fSJames Wright **/
75351bb547fSJames Wright PetscErrorCode MatCeedSetContextReal(Mat mat, const char *name, PetscReal value) {
75451bb547fSJames Wright   double value_double = value;
75551bb547fSJames Wright 
75651bb547fSJames Wright   PetscFunctionBeginUser;
75751bb547fSJames Wright   PetscCall(MatCeedSetContextDouble(mat, name, value_double));
75851bb547fSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
75951bb547fSJames Wright }
76051bb547fSJames Wright 
76151bb547fSJames Wright /**
76240d80af1SJames Wright   @brief Get the current value of a context field for a `MatCEED`.
76340d80af1SJames Wright 
76440d80af1SJames Wright   Not collective across MPI processes.
76540d80af1SJames Wright 
76640d80af1SJames Wright   @param[in]   mat    `MatCEED`
76740d80af1SJames Wright   @param[in]   name   Name of the context field
76840d80af1SJames Wright   @param[out]  value  Current context field value
76940d80af1SJames Wright 
77040d80af1SJames Wright   @return An error code: 0 - success, otherwise - failure
77140d80af1SJames Wright **/
77240d80af1SJames Wright PetscErrorCode MatCeedGetContextDouble(Mat mat, const char *name, double *value) {
77340d80af1SJames Wright   MatCeedContext ctx;
77440d80af1SJames Wright 
77540d80af1SJames Wright   PetscFunctionBeginUser;
77640d80af1SJames Wright   PetscCall(MatShellGetContext(mat, &ctx));
77740d80af1SJames Wright   {
77840d80af1SJames Wright     CeedContextFieldLabel label = NULL;
77940d80af1SJames Wright     CeedOperator          op    = ctx->op_mult;
78040d80af1SJames Wright 
78140d80af1SJames Wright     PetscCallCeed(ctx->ceed, CeedOperatorGetContextFieldLabel(op, name, &label));
78240d80af1SJames Wright     if (!label && ctx->op_mult_transpose) {
78340d80af1SJames Wright       op = ctx->op_mult_transpose;
78440d80af1SJames Wright       PetscCallCeed(ctx->ceed, CeedOperatorGetContextFieldLabel(op, name, &label));
78540d80af1SJames Wright     }
78640d80af1SJames Wright     if (label) {
78740d80af1SJames Wright       PetscSizeT    num_values;
78840d80af1SJames Wright       const double *values_ceed;
78940d80af1SJames Wright 
79040d80af1SJames Wright       PetscCallCeed(ctx->ceed, CeedOperatorGetContextDoubleRead(op, label, &num_values, &values_ceed));
79140d80af1SJames Wright       *value = values_ceed[0];
79240d80af1SJames Wright       PetscCallCeed(ctx->ceed, CeedOperatorRestoreContextDoubleRead(op, label, &values_ceed));
79340d80af1SJames Wright     }
79440d80af1SJames Wright   }
79540d80af1SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
79640d80af1SJames Wright }
79740d80af1SJames Wright 
79840d80af1SJames Wright /**
79951bb547fSJames Wright   @brief Get the current `PetscReal` value of a context field for a `MatCEED`.
80051bb547fSJames Wright 
80151bb547fSJames Wright   Not collective across MPI processes.
80251bb547fSJames Wright 
80351bb547fSJames Wright   @param[in]   mat    `MatCEED`
80451bb547fSJames Wright   @param[in]   name   Name of the context field
80551bb547fSJames Wright   @param[out]  value  Current context field value
80651bb547fSJames Wright 
80751bb547fSJames Wright   @return An error code: 0 - success, otherwise - failure
80851bb547fSJames Wright **/
80951bb547fSJames Wright PetscErrorCode MatCeedGetContextReal(Mat mat, const char *name, PetscReal *value) {
81051bb547fSJames Wright   double value_double;
81151bb547fSJames Wright 
81251bb547fSJames Wright   PetscFunctionBeginUser;
81351bb547fSJames Wright   PetscCall(MatCeedGetContextDouble(mat, name, &value_double));
81451bb547fSJames Wright   *value = value_double;
81551bb547fSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
81651bb547fSJames Wright }
81751bb547fSJames Wright 
81851bb547fSJames Wright /**
81958600ac3SJames Wright   @brief Set user context for a `MATCEED`.
82058600ac3SJames Wright 
82158600ac3SJames Wright   Collective across MPI processes.
82258600ac3SJames Wright 
82358600ac3SJames Wright   @param[in,out]  mat  `MATCEED`
82458600ac3SJames Wright   @param[in]      f    The context destroy function, or NULL
82558600ac3SJames Wright   @param[in]      ctx  User context, or NULL to unset
82658600ac3SJames Wright 
82758600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
82858600ac3SJames Wright **/
82958600ac3SJames Wright PetscErrorCode MatCeedSetContext(Mat mat, PetscErrorCode (*f)(void *), void *ctx) {
83058600ac3SJames Wright   PetscContainer user_ctx = NULL;
83158600ac3SJames Wright 
83258600ac3SJames Wright   PetscFunctionBeginUser;
83358600ac3SJames Wright   if (ctx) {
83458600ac3SJames Wright     PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)mat), &user_ctx));
83558600ac3SJames Wright     PetscCall(PetscContainerSetPointer(user_ctx, ctx));
83658600ac3SJames Wright     PetscCall(PetscContainerSetUserDestroy(user_ctx, f));
83758600ac3SJames Wright   }
83858600ac3SJames Wright   PetscCall(PetscObjectCompose((PetscObject)mat, "MatCeed user context", (PetscObject)user_ctx));
83958600ac3SJames Wright   PetscCall(PetscContainerDestroy(&user_ctx));
84058600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
84158600ac3SJames Wright }
84258600ac3SJames Wright 
84358600ac3SJames Wright /**
84458600ac3SJames Wright   @brief Retrieve the user context for a `MATCEED`.
84558600ac3SJames Wright 
84658600ac3SJames Wright   Collective across MPI processes.
84758600ac3SJames Wright 
84858600ac3SJames Wright   @param[in,out]  mat  `MATCEED`
84958600ac3SJames Wright   @param[in]      ctx  User context
85058600ac3SJames Wright 
85158600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
85258600ac3SJames Wright **/
85358600ac3SJames Wright PetscErrorCode MatCeedGetContext(Mat mat, void *ctx) {
85458600ac3SJames Wright   PetscContainer user_ctx;
85558600ac3SJames Wright 
85658600ac3SJames Wright   PetscFunctionBeginUser;
85758600ac3SJames Wright   PetscCall(PetscObjectQuery((PetscObject)mat, "MatCeed user context", (PetscObject *)&user_ctx));
85858600ac3SJames Wright   if (user_ctx) PetscCall(PetscContainerGetPointer(user_ctx, (void **)ctx));
85958600ac3SJames Wright   else *(void **)ctx = NULL;
86058600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
86158600ac3SJames Wright }
86258600ac3SJames Wright /**
86340d80af1SJames Wright   @brief Set a user defined matrix operation for a `MATCEED` matrix.
86440d80af1SJames Wright 
86540d80af1SJames Wright   Within each user-defined routine, the user should call `MatCeedGetContext()` to obtain the user-defined context that was set by
86640d80af1SJames Wright `MatCeedSetContext()`.
86758600ac3SJames Wright 
86858600ac3SJames Wright   Collective across MPI processes.
86958600ac3SJames Wright 
87058600ac3SJames Wright   @param[in,out]  mat  `MATCEED`
87140d80af1SJames Wright   @param[in]      op   Name of the `MatOperation`
87240d80af1SJames Wright   @param[in]      g    Function that provides the operation
87358600ac3SJames Wright 
87458600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
87558600ac3SJames Wright **/
87640d80af1SJames Wright PetscErrorCode MatCeedSetOperation(Mat mat, MatOperation op, void (*g)(void)) {
87740d80af1SJames Wright   PetscFunctionBeginUser;
87840d80af1SJames Wright   PetscCall(MatShellSetOperation(mat, op, g));
87940d80af1SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
88040d80af1SJames Wright }
88140d80af1SJames Wright 
88240d80af1SJames Wright /**
88340d80af1SJames Wright   @brief Sets the default COO matrix type as a string from the `MATCEED`.
88440d80af1SJames Wright 
88540d80af1SJames Wright   Collective across MPI processes.
88640d80af1SJames Wright 
88740d80af1SJames Wright   @param[in,out]  mat   `MATCEED`
88840d80af1SJames Wright   @param[in]      type  COO `MatType` to set
88940d80af1SJames Wright 
89040d80af1SJames Wright   @return An error code: 0 - success, otherwise - failure
89140d80af1SJames Wright **/
89240d80af1SJames Wright PetscErrorCode MatCeedSetCOOMatType(Mat mat, MatType type) {
89358600ac3SJames Wright   MatCeedContext ctx;
89458600ac3SJames Wright 
89558600ac3SJames Wright   PetscFunctionBeginUser;
89658600ac3SJames Wright   PetscCall(MatShellGetContext(mat, &ctx));
89758600ac3SJames Wright   // Check if same
89858600ac3SJames Wright   {
89958600ac3SJames Wright     size_t    len_old, len_new;
90058600ac3SJames Wright     PetscBool is_same = PETSC_FALSE;
90158600ac3SJames Wright 
90240d80af1SJames Wright     PetscCall(PetscStrlen(ctx->coo_mat_type, &len_old));
90358600ac3SJames Wright     PetscCall(PetscStrlen(type, &len_new));
90440d80af1SJames Wright     if (len_old == len_new) PetscCall(PetscStrncmp(ctx->coo_mat_type, type, len_old, &is_same));
90558600ac3SJames Wright     if (is_same) PetscFunctionReturn(PETSC_SUCCESS);
90658600ac3SJames Wright   }
90758600ac3SJames Wright   // Clean up old mats in different format
90858600ac3SJames Wright   // LCOV_EXCL_START
90958600ac3SJames Wright   if (ctx->mat_assembled_full_internal) {
91058600ac3SJames Wright     for (PetscInt i = 0; i < ctx->num_mats_assembled_full; i++) {
91158600ac3SJames Wright       if (ctx->mats_assembled_full[i] == ctx->mat_assembled_full_internal) {
91258600ac3SJames Wright         for (PetscInt j = i + 1; j < ctx->num_mats_assembled_full; j++) {
91358600ac3SJames Wright           ctx->mats_assembled_full[j - 1] = ctx->mats_assembled_full[j];
91458600ac3SJames Wright         }
91558600ac3SJames Wright         ctx->num_mats_assembled_full--;
91658600ac3SJames Wright         // Note: we'll realloc this array again, so no need to shrink the allocation
91758600ac3SJames Wright         PetscCall(MatDestroy(&ctx->mat_assembled_full_internal));
91858600ac3SJames Wright       }
91958600ac3SJames Wright     }
92058600ac3SJames Wright   }
92158600ac3SJames Wright   if (ctx->mat_assembled_pbd_internal) {
92258600ac3SJames Wright     for (PetscInt i = 0; i < ctx->num_mats_assembled_pbd; i++) {
92358600ac3SJames Wright       if (ctx->mats_assembled_pbd[i] == ctx->mat_assembled_pbd_internal) {
92458600ac3SJames Wright         for (PetscInt j = i + 1; j < ctx->num_mats_assembled_pbd; j++) {
92558600ac3SJames Wright           ctx->mats_assembled_pbd[j - 1] = ctx->mats_assembled_pbd[j];
92658600ac3SJames Wright         }
92758600ac3SJames Wright         // Note: we'll realloc this array again, so no need to shrink the allocation
92858600ac3SJames Wright         ctx->num_mats_assembled_pbd--;
92958600ac3SJames Wright         PetscCall(MatDestroy(&ctx->mat_assembled_pbd_internal));
93058600ac3SJames Wright       }
93158600ac3SJames Wright     }
93258600ac3SJames Wright   }
93340d80af1SJames Wright   PetscCall(PetscFree(ctx->coo_mat_type));
93440d80af1SJames Wright   PetscCall(PetscStrallocpy(type, &ctx->coo_mat_type));
93558600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
93658600ac3SJames Wright   // LCOV_EXCL_STOP
93758600ac3SJames Wright }
93858600ac3SJames Wright 
93958600ac3SJames Wright /**
94040d80af1SJames Wright   @brief Gets the default COO matrix type as a string from the `MATCEED`.
94158600ac3SJames Wright 
94258600ac3SJames Wright   Collective across MPI processes.
94358600ac3SJames Wright 
94458600ac3SJames Wright   @param[in,out]  mat   `MATCEED`
94540d80af1SJames Wright   @param[in]      type  COO `MatType`
94658600ac3SJames Wright 
94758600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
94858600ac3SJames Wright **/
94940d80af1SJames Wright PetscErrorCode MatCeedGetCOOMatType(Mat mat, MatType *type) {
95058600ac3SJames Wright   MatCeedContext ctx;
95158600ac3SJames Wright 
95258600ac3SJames Wright   PetscFunctionBeginUser;
95358600ac3SJames Wright   PetscCall(MatShellGetContext(mat, &ctx));
95440d80af1SJames Wright   *type = ctx->coo_mat_type;
95558600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
95658600ac3SJames Wright }
95758600ac3SJames Wright 
95858600ac3SJames Wright /**
95958600ac3SJames Wright   @brief Set input local vectors for `MATCEED` `MatMult()` and `MatMultTranspose()` operations.
96058600ac3SJames Wright 
96158600ac3SJames Wright   Not collective across MPI processes.
96258600ac3SJames Wright 
96358600ac3SJames Wright   @param[in,out]  mat              `MATCEED`
96458600ac3SJames Wright   @param[in]      X_loc            Input PETSc local vector, or NULL
96558600ac3SJames Wright   @param[in]      Y_loc_transpose  Input PETSc local vector for transpose operation, or NULL
96658600ac3SJames Wright 
96758600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
96858600ac3SJames Wright **/
96958600ac3SJames Wright PetscErrorCode MatCeedSetLocalVectors(Mat mat, Vec X_loc, Vec Y_loc_transpose) {
97058600ac3SJames Wright   MatCeedContext ctx;
97158600ac3SJames Wright 
97258600ac3SJames Wright   PetscFunctionBeginUser;
97358600ac3SJames Wright   PetscCall(MatShellGetContext(mat, &ctx));
97458600ac3SJames Wright   if (X_loc) {
97558600ac3SJames Wright     PetscInt len_old, len_new;
97658600ac3SJames Wright 
97758600ac3SJames Wright     PetscCall(VecGetSize(ctx->X_loc, &len_old));
97858600ac3SJames Wright     PetscCall(VecGetSize(X_loc, &len_new));
97958600ac3SJames Wright     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,
98058600ac3SJames Wright                len_new, len_old);
98140d80af1SJames Wright     PetscCall(VecReferenceCopy(X_loc, &ctx->X_loc));
98258600ac3SJames Wright   }
98358600ac3SJames Wright   if (Y_loc_transpose) {
98458600ac3SJames Wright     PetscInt len_old, len_new;
98558600ac3SJames Wright 
98658600ac3SJames Wright     PetscCall(VecGetSize(ctx->Y_loc_transpose, &len_old));
98758600ac3SJames Wright     PetscCall(VecGetSize(Y_loc_transpose, &len_new));
98858600ac3SJames Wright     PetscCheck(len_old == len_new, PETSC_COMM_SELF, PETSC_ERR_LIB,
98958600ac3SJames Wright                "new Y_loc_transpose length %" PetscInt_FMT " should match old Y_loc_transpose length %" PetscInt_FMT, len_new, len_old);
99040d80af1SJames Wright     PetscCall(VecReferenceCopy(Y_loc_transpose, &ctx->Y_loc_transpose));
99158600ac3SJames Wright   }
99258600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
99358600ac3SJames Wright }
99458600ac3SJames Wright 
99558600ac3SJames Wright /**
99658600ac3SJames Wright   @brief Get input local vectors for `MATCEED` `MatMult()` and `MatMultTranspose()` operations.
99758600ac3SJames Wright 
99858600ac3SJames Wright   Not collective across MPI processes.
99958600ac3SJames Wright 
100058600ac3SJames Wright   @param[in,out]  mat              `MATCEED`
100158600ac3SJames Wright   @param[out]     X_loc            Input PETSc local vector, or NULL
100258600ac3SJames Wright   @param[out]     Y_loc_transpose  Input PETSc local vector for transpose operation, or NULL
100358600ac3SJames Wright 
100458600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
100558600ac3SJames Wright **/
100658600ac3SJames Wright PetscErrorCode MatCeedGetLocalVectors(Mat mat, Vec *X_loc, Vec *Y_loc_transpose) {
100758600ac3SJames Wright   MatCeedContext ctx;
100858600ac3SJames Wright 
100958600ac3SJames Wright   PetscFunctionBeginUser;
101058600ac3SJames Wright   PetscCall(MatShellGetContext(mat, &ctx));
101158600ac3SJames Wright   if (X_loc) {
101240d80af1SJames Wright     *X_loc = NULL;
101340d80af1SJames Wright     PetscCall(VecReferenceCopy(ctx->X_loc, X_loc));
101458600ac3SJames Wright   }
101558600ac3SJames Wright   if (Y_loc_transpose) {
101640d80af1SJames Wright     *Y_loc_transpose = NULL;
101740d80af1SJames Wright     PetscCall(VecReferenceCopy(ctx->Y_loc_transpose, Y_loc_transpose));
101858600ac3SJames Wright   }
101958600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
102058600ac3SJames Wright }
102158600ac3SJames Wright 
102258600ac3SJames Wright /**
102358600ac3SJames Wright   @brief Restore input local vectors for `MATCEED` `MatMult()` and `MatMultTranspose()` operations.
102458600ac3SJames Wright 
102558600ac3SJames Wright   Not collective across MPI processes.
102658600ac3SJames Wright 
102758600ac3SJames Wright   @param[in,out]  mat              MatCeed
102858600ac3SJames Wright   @param[out]     X_loc            Input PETSc local vector, or NULL
102958600ac3SJames Wright   @param[out]     Y_loc_transpose  Input PETSc local vector for transpose operation, or NULL
103058600ac3SJames Wright 
103158600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
103258600ac3SJames Wright **/
103358600ac3SJames Wright PetscErrorCode MatCeedRestoreLocalVectors(Mat mat, Vec *X_loc, Vec *Y_loc_transpose) {
103458600ac3SJames Wright   PetscFunctionBeginUser;
103558600ac3SJames Wright   if (X_loc) PetscCall(VecDestroy(X_loc));
103658600ac3SJames Wright   if (Y_loc_transpose) PetscCall(VecDestroy(Y_loc_transpose));
103758600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
103858600ac3SJames Wright }
103958600ac3SJames Wright 
104058600ac3SJames Wright /**
104158600ac3SJames Wright   @brief Get libCEED `CeedOperator` for `MATCEED` `MatMult()` and `MatMultTranspose()` operations.
104258600ac3SJames Wright 
104358600ac3SJames Wright   Not collective across MPI processes.
104458600ac3SJames Wright 
104558600ac3SJames Wright   @param[in,out]  mat                MatCeed
104658600ac3SJames Wright   @param[out]     op_mult            libCEED `CeedOperator` for `MatMult()`, or NULL
104758600ac3SJames Wright   @param[out]     op_mult_transpose  libCEED `CeedOperator` for `MatMultTranspose()`, or NULL
104858600ac3SJames Wright 
104958600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
105058600ac3SJames Wright **/
105158600ac3SJames Wright PetscErrorCode MatCeedGetCeedOperators(Mat mat, CeedOperator *op_mult, CeedOperator *op_mult_transpose) {
105258600ac3SJames Wright   MatCeedContext ctx;
105358600ac3SJames Wright 
105458600ac3SJames Wright   PetscFunctionBeginUser;
105558600ac3SJames Wright   PetscCall(MatShellGetContext(mat, &ctx));
105658600ac3SJames Wright   if (op_mult) {
105758600ac3SJames Wright     *op_mult = NULL;
105850f50432SJames Wright     PetscCallCeed(ctx->ceed, CeedOperatorReferenceCopy(ctx->op_mult, op_mult));
105958600ac3SJames Wright   }
106058600ac3SJames Wright   if (op_mult_transpose) {
106158600ac3SJames Wright     *op_mult_transpose = NULL;
106250f50432SJames Wright     PetscCallCeed(ctx->ceed, CeedOperatorReferenceCopy(ctx->op_mult_transpose, op_mult_transpose));
106358600ac3SJames Wright   }
106458600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
106558600ac3SJames Wright }
106658600ac3SJames Wright 
106758600ac3SJames Wright /**
106858600ac3SJames Wright   @brief Restore libCEED `CeedOperator` for `MATCEED` `MatMult()` and `MatMultTranspose()` operations.
106958600ac3SJames Wright 
107058600ac3SJames Wright   Not collective across MPI processes.
107158600ac3SJames Wright 
107258600ac3SJames Wright   @param[in,out]  mat                MatCeed
107358600ac3SJames Wright   @param[out]     op_mult            libCEED `CeedOperator` for `MatMult()`, or NULL
107458600ac3SJames Wright   @param[out]     op_mult_transpose  libCEED `CeedOperator` for `MatMultTranspose()`, or NULL
107558600ac3SJames Wright 
107658600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
107758600ac3SJames Wright **/
107858600ac3SJames Wright PetscErrorCode MatCeedRestoreCeedOperators(Mat mat, CeedOperator *op_mult, CeedOperator *op_mult_transpose) {
107958600ac3SJames Wright   MatCeedContext ctx;
108058600ac3SJames Wright 
108158600ac3SJames Wright   PetscFunctionBeginUser;
108258600ac3SJames Wright   PetscCall(MatShellGetContext(mat, &ctx));
108350f50432SJames Wright   if (op_mult) PetscCallCeed(ctx->ceed, CeedOperatorDestroy(op_mult));
108450f50432SJames Wright   if (op_mult_transpose) PetscCallCeed(ctx->ceed, CeedOperatorDestroy(op_mult_transpose));
108558600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
108658600ac3SJames Wright }
108758600ac3SJames Wright 
108858600ac3SJames Wright /**
108958600ac3SJames Wright   @brief Set `PetscLogEvent` for `MATCEED` `MatMult()` and `MatMultTranspose()` operators.
109058600ac3SJames Wright 
109158600ac3SJames Wright   Not collective across MPI processes.
109258600ac3SJames Wright 
109358600ac3SJames Wright   @param[in,out]  mat                       MatCeed
109458600ac3SJames Wright   @param[out]     log_event_mult            `PetscLogEvent` for forward evaluation, or NULL
109558600ac3SJames Wright   @param[out]     log_event_mult_transpose  `PetscLogEvent` for transpose evaluation, or NULL
109658600ac3SJames Wright 
109758600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
109858600ac3SJames Wright **/
109958600ac3SJames Wright PetscErrorCode MatCeedSetLogEvents(Mat mat, PetscLogEvent log_event_mult, PetscLogEvent log_event_mult_transpose) {
110058600ac3SJames Wright   MatCeedContext ctx;
110158600ac3SJames Wright 
110258600ac3SJames Wright   PetscFunctionBeginUser;
110358600ac3SJames Wright   PetscCall(MatShellGetContext(mat, &ctx));
110458600ac3SJames Wright   if (log_event_mult) ctx->log_event_mult = log_event_mult;
110558600ac3SJames Wright   if (log_event_mult_transpose) ctx->log_event_mult_transpose = log_event_mult_transpose;
110658600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
110758600ac3SJames Wright }
110858600ac3SJames Wright 
110958600ac3SJames Wright /**
111058600ac3SJames Wright   @brief Get `PetscLogEvent` for `MATCEED` `MatMult()` and `MatMultTranspose()` operators.
111158600ac3SJames Wright 
111258600ac3SJames Wright   Not collective across MPI processes.
111358600ac3SJames Wright 
111458600ac3SJames Wright   @param[in,out]  mat                       MatCeed
111558600ac3SJames Wright   @param[out]     log_event_mult            `PetscLogEvent` for forward evaluation, or NULL
111658600ac3SJames Wright   @param[out]     log_event_mult_transpose  `PetscLogEvent` for transpose evaluation, or NULL
111758600ac3SJames Wright 
111858600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
111958600ac3SJames Wright **/
112058600ac3SJames Wright PetscErrorCode MatCeedGetLogEvents(Mat mat, PetscLogEvent *log_event_mult, PetscLogEvent *log_event_mult_transpose) {
112158600ac3SJames Wright   MatCeedContext ctx;
112258600ac3SJames Wright 
112358600ac3SJames Wright   PetscFunctionBeginUser;
112458600ac3SJames Wright   PetscCall(MatShellGetContext(mat, &ctx));
112558600ac3SJames Wright   if (log_event_mult) *log_event_mult = ctx->log_event_mult;
112658600ac3SJames Wright   if (log_event_mult_transpose) *log_event_mult_transpose = ctx->log_event_mult_transpose;
112758600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
112858600ac3SJames Wright }
112958600ac3SJames Wright 
1130*c63b910fSJames Wright /**
1131*c63b910fSJames Wright   @brief Set `CeedOperator` `PetscLogEvent` for `MATCEED` `MatMult()` and `MatMultTranspose()` operators.
1132*c63b910fSJames Wright 
1133*c63b910fSJames Wright   Not collective across MPI processes.
1134*c63b910fSJames Wright 
1135*c63b910fSJames Wright   @param[in,out]  mat                       MatCeed
1136*c63b910fSJames Wright   @param[out]     log_event_mult            `PetscLogEvent` for forward `CeedOperator` evaluation, or NULL
1137*c63b910fSJames Wright   @param[out]     log_event_mult_transpose  `PetscLogEvent` for transpose `CeedOperator` evaluation, or NULL
1138*c63b910fSJames Wright 
1139*c63b910fSJames Wright   @return An error code: 0 - success, otherwise - failure
1140*c63b910fSJames Wright **/
1141*c63b910fSJames Wright PetscErrorCode MatCeedSetCeedOperatorLogEvents(Mat mat, PetscLogEvent log_event_mult, PetscLogEvent log_event_mult_transpose) {
1142*c63b910fSJames Wright   MatCeedContext ctx;
1143*c63b910fSJames Wright 
1144*c63b910fSJames Wright   PetscFunctionBeginUser;
1145*c63b910fSJames Wright   PetscCall(MatShellGetContext(mat, &ctx));
1146*c63b910fSJames Wright   if (log_event_mult) ctx->log_event_ceed_mult = log_event_mult;
1147*c63b910fSJames Wright   if (log_event_mult_transpose) ctx->log_event_ceed_mult_transpose = log_event_mult_transpose;
1148*c63b910fSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
1149*c63b910fSJames Wright }
1150*c63b910fSJames Wright 
1151*c63b910fSJames Wright /**
1152*c63b910fSJames Wright   @brief Get `CeedOperator` `PetscLogEvent` for `MATCEED` `MatMult()` and `MatMultTranspose()` operators.
1153*c63b910fSJames Wright 
1154*c63b910fSJames Wright   Not collective across MPI processes.
1155*c63b910fSJames Wright 
1156*c63b910fSJames Wright   @param[in,out]  mat                       MatCeed
1157*c63b910fSJames Wright   @param[out]     log_event_mult            `PetscLogEvent` for forward `CeedOperator` evaluation, or NULL
1158*c63b910fSJames Wright   @param[out]     log_event_mult_transpose  `PetscLogEvent` for transpose `CeedOperator` evaluation, or NULL
1159*c63b910fSJames Wright 
1160*c63b910fSJames Wright   @return An error code: 0 - success, otherwise - failure
1161*c63b910fSJames Wright **/
1162*c63b910fSJames Wright PetscErrorCode MatCeedGetCeedOperatorLogEvents(Mat mat, PetscLogEvent *log_event_mult, PetscLogEvent *log_event_mult_transpose) {
1163*c63b910fSJames Wright   MatCeedContext ctx;
1164*c63b910fSJames Wright 
1165*c63b910fSJames Wright   PetscFunctionBeginUser;
1166*c63b910fSJames Wright   PetscCall(MatShellGetContext(mat, &ctx));
1167*c63b910fSJames Wright   if (log_event_mult) *log_event_mult = ctx->log_event_ceed_mult;
1168*c63b910fSJames Wright   if (log_event_mult_transpose) *log_event_mult_transpose = ctx->log_event_ceed_mult_transpose;
1169*c63b910fSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
1170*c63b910fSJames Wright }
1171*c63b910fSJames Wright 
117258600ac3SJames Wright // -----------------------------------------------------------------------------
117358600ac3SJames Wright // Operator context data
117458600ac3SJames Wright // -----------------------------------------------------------------------------
117558600ac3SJames Wright 
117658600ac3SJames Wright /**
117758600ac3SJames Wright   @brief Setup context data for operator application.
117858600ac3SJames Wright 
117958600ac3SJames Wright   Collective across MPI processes.
118058600ac3SJames Wright 
118158600ac3SJames Wright   @param[in]   dm_x                           Input `DM`
118258600ac3SJames Wright   @param[in]   dm_y                           Output `DM`
118358600ac3SJames Wright   @param[in]   X_loc                          Input PETSc local vector, or NULL
118458600ac3SJames Wright   @param[in]   Y_loc_transpose                Input PETSc local vector for transpose operation, or NULL
118558600ac3SJames Wright   @param[in]   op_mult                        `CeedOperator` for forward evaluation
118658600ac3SJames Wright   @param[in]   op_mult_transpose              `CeedOperator` for transpose evaluation
118758600ac3SJames Wright   @param[in]   log_event_mult                 `PetscLogEvent` for forward evaluation
118858600ac3SJames Wright   @param[in]   log_event_mult_transpose       `PetscLogEvent` for transpose evaluation
1189*c63b910fSJames Wright   @param[in]   log_event_ceed_mult            `PetscLogEvent` for forward `CeedOperator` evaluation
1190*c63b910fSJames Wright   @param[in]   log_event_ceed_mult_transpose  `PetscLogEvent` for transpose `CeedOperator` evaluation
119158600ac3SJames Wright   @param[out]  ctx                            Context data for operator evaluation
119258600ac3SJames Wright 
119358600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
119458600ac3SJames Wright **/
119558600ac3SJames Wright PetscErrorCode MatCeedContextCreate(DM dm_x, DM dm_y, Vec X_loc, Vec Y_loc_transpose, CeedOperator op_mult, CeedOperator op_mult_transpose,
1196*c63b910fSJames Wright                                     PetscLogEvent log_event_mult, PetscLogEvent log_event_mult_transpose, PetscLogEvent log_event_ceed_mult,
1197*c63b910fSJames Wright                                     PetscLogEvent log_event_ceed_mult_transpose, MatCeedContext *ctx) {
119858600ac3SJames Wright   CeedSize x_loc_len, y_loc_len;
119958600ac3SJames Wright 
120058600ac3SJames Wright   PetscFunctionBeginUser;
120158600ac3SJames Wright 
120258600ac3SJames Wright   // Allocate
120358600ac3SJames Wright   PetscCall(PetscNew(ctx));
120458600ac3SJames Wright   (*ctx)->ref_count = 1;
120558600ac3SJames Wright 
120658600ac3SJames Wright   // Logging
120758600ac3SJames Wright   (*ctx)->log_event_mult                = log_event_mult;
120858600ac3SJames Wright   (*ctx)->log_event_mult_transpose      = log_event_mult_transpose;
1209*c63b910fSJames Wright   (*ctx)->log_event_ceed_mult           = log_event_ceed_mult;
1210*c63b910fSJames Wright   (*ctx)->log_event_ceed_mult_transpose = log_event_ceed_mult_transpose;
121158600ac3SJames Wright 
121258600ac3SJames Wright   // PETSc objects
121340d80af1SJames Wright   PetscCall(DMReferenceCopy(dm_x, &(*ctx)->dm_x));
121440d80af1SJames Wright   PetscCall(DMReferenceCopy(dm_y, &(*ctx)->dm_y));
121540d80af1SJames Wright   if (X_loc) PetscCall(VecReferenceCopy(X_loc, &(*ctx)->X_loc));
121640d80af1SJames Wright   if (Y_loc_transpose) PetscCall(VecReferenceCopy(Y_loc_transpose, &(*ctx)->Y_loc_transpose));
121758600ac3SJames Wright 
121858600ac3SJames Wright   // Memtype
121958600ac3SJames Wright   {
122058600ac3SJames Wright     const PetscScalar *x;
122158600ac3SJames Wright     Vec                X;
122258600ac3SJames Wright 
122358600ac3SJames Wright     PetscCall(DMGetLocalVector(dm_x, &X));
122458600ac3SJames Wright     PetscCall(VecGetArrayReadAndMemType(X, &x, &(*ctx)->mem_type));
122558600ac3SJames Wright     PetscCall(VecRestoreArrayReadAndMemType(X, &x));
122658600ac3SJames Wright     PetscCall(DMRestoreLocalVector(dm_x, &X));
122758600ac3SJames Wright   }
122858600ac3SJames Wright 
122958600ac3SJames Wright   // libCEED objects
123058600ac3SJames Wright   PetscCheck(CeedOperatorGetCeed(op_mult, &(*ctx)->ceed) == CEED_ERROR_SUCCESS, PETSC_COMM_SELF, PETSC_ERR_LIB,
123158600ac3SJames Wright              "retrieving Ceed context object failed");
123250f50432SJames Wright   PetscCallCeed((*ctx)->ceed, CeedReference((*ctx)->ceed));
123350f50432SJames Wright   PetscCallCeed((*ctx)->ceed, CeedOperatorGetActiveVectorLengths(op_mult, &x_loc_len, &y_loc_len));
123450f50432SJames Wright   PetscCallCeed((*ctx)->ceed, CeedOperatorReferenceCopy(op_mult, &(*ctx)->op_mult));
123550f50432SJames Wright   if (op_mult_transpose) PetscCallCeed((*ctx)->ceed, CeedOperatorReferenceCopy(op_mult_transpose, &(*ctx)->op_mult_transpose));
123650f50432SJames Wright   PetscCallCeed((*ctx)->ceed, CeedVectorCreate((*ctx)->ceed, x_loc_len, &(*ctx)->x_loc));
123750f50432SJames Wright   PetscCallCeed((*ctx)->ceed, CeedVectorCreate((*ctx)->ceed, y_loc_len, &(*ctx)->y_loc));
123858600ac3SJames Wright 
123958600ac3SJames Wright   // Flop counting
124058600ac3SJames Wright   {
124158600ac3SJames Wright     CeedSize ceed_flops_estimate = 0;
124258600ac3SJames Wright 
124350f50432SJames Wright     PetscCallCeed((*ctx)->ceed, CeedOperatorGetFlopsEstimate(op_mult, &ceed_flops_estimate));
124458600ac3SJames Wright     (*ctx)->flops_mult = ceed_flops_estimate;
124558600ac3SJames Wright     if (op_mult_transpose) {
124650f50432SJames Wright       PetscCallCeed((*ctx)->ceed, CeedOperatorGetFlopsEstimate(op_mult_transpose, &ceed_flops_estimate));
124758600ac3SJames Wright       (*ctx)->flops_mult_transpose = ceed_flops_estimate;
124858600ac3SJames Wright     }
124958600ac3SJames Wright   }
125058600ac3SJames Wright 
125158600ac3SJames Wright   // Check sizes
125258600ac3SJames Wright   if (x_loc_len > 0 || y_loc_len > 0) {
125358600ac3SJames Wright     CeedSize ctx_x_loc_len, ctx_y_loc_len;
125458600ac3SJames Wright     PetscInt X_loc_len, dm_x_loc_len, Y_loc_len, dm_y_loc_len;
125558600ac3SJames Wright     Vec      dm_X_loc, dm_Y_loc;
125658600ac3SJames Wright 
125758600ac3SJames Wright     // -- Input
125858600ac3SJames Wright     PetscCall(DMGetLocalVector(dm_x, &dm_X_loc));
125958600ac3SJames Wright     PetscCall(VecGetLocalSize(dm_X_loc, &dm_x_loc_len));
126058600ac3SJames Wright     PetscCall(DMRestoreLocalVector(dm_x, &dm_X_loc));
126150f50432SJames Wright     PetscCallCeed((*ctx)->ceed, CeedVectorGetLength((*ctx)->x_loc, &ctx_x_loc_len));
12624c17272bSJames Wright     if (X_loc) {
12634c17272bSJames Wright       PetscCall(VecGetLocalSize(X_loc, &X_loc_len));
12644c17272bSJames Wright       PetscCheck(X_loc_len == dm_x_loc_len, PETSC_COMM_SELF, PETSC_ERR_LIB,
12654c17272bSJames Wright                  "X_loc (%" PetscInt_FMT ") must match dm_x (%" PetscInt_FMT ") dimensions", X_loc_len, dm_x_loc_len);
12664c17272bSJames Wright     }
12674c17272bSJames Wright     PetscCheck(x_loc_len == dm_x_loc_len, PETSC_COMM_SELF, PETSC_ERR_LIB, "op (%" CeedSize_FMT ") must match dm_x (%" PetscInt_FMT ") dimensions",
12684c17272bSJames Wright                x_loc_len, dm_x_loc_len);
12694c17272bSJames Wright     PetscCheck(x_loc_len == ctx_x_loc_len, PETSC_COMM_SELF, PETSC_ERR_LIB, "x_loc (%" CeedSize_FMT ") must match op dimensions (%" CeedSize_FMT ")",
12704c17272bSJames Wright                x_loc_len, ctx_x_loc_len);
127158600ac3SJames Wright 
127258600ac3SJames Wright     // -- Output
127358600ac3SJames Wright     PetscCall(DMGetLocalVector(dm_y, &dm_Y_loc));
127458600ac3SJames Wright     PetscCall(VecGetLocalSize(dm_Y_loc, &dm_y_loc_len));
127558600ac3SJames Wright     PetscCall(DMRestoreLocalVector(dm_y, &dm_Y_loc));
127650f50432SJames Wright     PetscCallCeed((*ctx)->ceed, CeedVectorGetLength((*ctx)->y_loc, &ctx_y_loc_len));
12774c17272bSJames Wright     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",
12784c17272bSJames Wright                ctx_y_loc_len, dm_y_loc_len);
127958600ac3SJames Wright 
128058600ac3SJames Wright     // -- Transpose
128158600ac3SJames Wright     if (Y_loc_transpose) {
128258600ac3SJames Wright       PetscCall(VecGetLocalSize(Y_loc_transpose, &Y_loc_len));
12834c17272bSJames Wright       PetscCheck(Y_loc_len == dm_y_loc_len, PETSC_COMM_SELF, PETSC_ERR_LIB,
12844c17272bSJames Wright                  "Y_loc_transpose (%" PetscInt_FMT ") must match dm_y (%" PetscInt_FMT ") dimensions", Y_loc_len, dm_y_loc_len);
128558600ac3SJames Wright     }
128658600ac3SJames Wright   }
128758600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
128858600ac3SJames Wright }
128958600ac3SJames Wright 
129058600ac3SJames Wright /**
129158600ac3SJames Wright   @brief Increment reference counter for `MATCEED` context.
129258600ac3SJames Wright 
129358600ac3SJames Wright   Not collective across MPI processes.
129458600ac3SJames Wright 
129558600ac3SJames Wright   @param[in,out]  ctx  Context data
129658600ac3SJames Wright 
129758600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
129858600ac3SJames Wright **/
129958600ac3SJames Wright PetscErrorCode MatCeedContextReference(MatCeedContext ctx) {
130058600ac3SJames Wright   PetscFunctionBeginUser;
130158600ac3SJames Wright   ctx->ref_count++;
130258600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
130358600ac3SJames Wright }
130458600ac3SJames Wright 
130558600ac3SJames Wright /**
130658600ac3SJames Wright   @brief Copy reference for `MATCEED`.
130758600ac3SJames Wright          Note: If `ctx_copy` is non-null, it is assumed to be a valid pointer to a `MatCeedContext`.
130858600ac3SJames Wright 
130958600ac3SJames Wright   Not collective across MPI processes.
131058600ac3SJames Wright 
131158600ac3SJames Wright   @param[in]   ctx       Context data
131258600ac3SJames Wright   @param[out]  ctx_copy  Copy of pointer to context data
131358600ac3SJames Wright 
131458600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
131558600ac3SJames Wright **/
131658600ac3SJames Wright PetscErrorCode MatCeedContextReferenceCopy(MatCeedContext ctx, MatCeedContext *ctx_copy) {
131758600ac3SJames Wright   PetscFunctionBeginUser;
131858600ac3SJames Wright   PetscCall(MatCeedContextReference(ctx));
131958600ac3SJames Wright   PetscCall(MatCeedContextDestroy(*ctx_copy));
132058600ac3SJames Wright   *ctx_copy = ctx;
132158600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
132258600ac3SJames Wright }
132358600ac3SJames Wright 
132458600ac3SJames Wright /**
132558600ac3SJames Wright   @brief Destroy context data for operator application.
132658600ac3SJames Wright 
132758600ac3SJames Wright   Collective across MPI processes.
132858600ac3SJames Wright 
132958600ac3SJames Wright   @param[in,out]  ctx  Context data for operator evaluation
133058600ac3SJames Wright 
133158600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
133258600ac3SJames Wright **/
133358600ac3SJames Wright PetscErrorCode MatCeedContextDestroy(MatCeedContext ctx) {
133458600ac3SJames Wright   PetscFunctionBeginUser;
133558600ac3SJames Wright   if (!ctx || --ctx->ref_count > 0) PetscFunctionReturn(PETSC_SUCCESS);
133658600ac3SJames Wright 
133758600ac3SJames Wright   // PETSc objects
133858600ac3SJames Wright   PetscCall(DMDestroy(&ctx->dm_x));
133958600ac3SJames Wright   PetscCall(DMDestroy(&ctx->dm_y));
134058600ac3SJames Wright   PetscCall(VecDestroy(&ctx->X_loc));
134158600ac3SJames Wright   PetscCall(VecDestroy(&ctx->Y_loc_transpose));
134258600ac3SJames Wright   PetscCall(MatDestroy(&ctx->mat_assembled_full_internal));
134358600ac3SJames Wright   PetscCall(MatDestroy(&ctx->mat_assembled_pbd_internal));
134440d80af1SJames Wright   PetscCall(PetscFree(ctx->coo_mat_type));
134558600ac3SJames Wright   PetscCall(PetscFree(ctx->mats_assembled_full));
134658600ac3SJames Wright   PetscCall(PetscFree(ctx->mats_assembled_pbd));
134758600ac3SJames Wright 
134858600ac3SJames Wright   // libCEED objects
134950f50432SJames Wright   PetscCallCeed(ctx->ceed, CeedVectorDestroy(&ctx->x_loc));
135050f50432SJames Wright   PetscCallCeed(ctx->ceed, CeedVectorDestroy(&ctx->y_loc));
135150f50432SJames Wright   PetscCallCeed(ctx->ceed, CeedVectorDestroy(&ctx->coo_values_full));
135250f50432SJames Wright   PetscCallCeed(ctx->ceed, CeedVectorDestroy(&ctx->coo_values_pbd));
135350f50432SJames Wright   PetscCallCeed(ctx->ceed, CeedOperatorDestroy(&ctx->op_mult));
135450f50432SJames Wright   PetscCallCeed(ctx->ceed, CeedOperatorDestroy(&ctx->op_mult_transpose));
135558600ac3SJames Wright   PetscCheck(CeedDestroy(&ctx->ceed) == CEED_ERROR_SUCCESS, PETSC_COMM_SELF, PETSC_ERR_LIB, "destroying libCEED context object failed");
135658600ac3SJames Wright 
135758600ac3SJames Wright   // Deallocate
135858600ac3SJames Wright   ctx->is_destroyed = PETSC_TRUE;  // Flag as destroyed in case someone has stale ref
135958600ac3SJames Wright   PetscCall(PetscFree(ctx));
136058600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
136158600ac3SJames Wright }
136258600ac3SJames Wright 
136358600ac3SJames Wright /**
136458600ac3SJames Wright   @brief Compute the diagonal of an operator via libCEED.
136558600ac3SJames Wright 
136658600ac3SJames Wright   Collective across MPI processes.
136758600ac3SJames Wright 
136858600ac3SJames Wright   @param[in]   A  `MATCEED`
136958600ac3SJames Wright   @param[out]  D  Vector holding operator diagonal
137058600ac3SJames Wright 
137158600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
137258600ac3SJames Wright **/
137358600ac3SJames Wright PetscErrorCode MatGetDiagonal_Ceed(Mat A, Vec D) {
137458600ac3SJames Wright   PetscMemType   mem_type;
137558600ac3SJames Wright   Vec            D_loc;
137658600ac3SJames Wright   MatCeedContext ctx;
137758600ac3SJames Wright 
137858600ac3SJames Wright   PetscFunctionBeginUser;
137958600ac3SJames Wright   PetscCall(MatShellGetContext(A, &ctx));
138058600ac3SJames Wright 
138158600ac3SJames Wright   // Place PETSc vector in libCEED vector
1382*c63b910fSJames Wright   PetscCall(PetscLogEventBegin(MATCEED_ASSEMBLE_DIAGONAL, A, D, NULL, NULL));
138358600ac3SJames Wright   PetscCall(DMGetLocalVector(ctx->dm_x, &D_loc));
1384a7dac1d5SJames Wright   PetscCall(VecPetscToCeed(D_loc, &mem_type, ctx->x_loc));
138558600ac3SJames Wright 
138658600ac3SJames Wright   // Compute Diagonal
1387*c63b910fSJames Wright   PetscCall(PetscLogEventBegin(MATCEED_ASSEMBLE_DIAGONAL_CEEDOP, A, D, NULL, NULL));
138850f50432SJames Wright   PetscCallCeed(ctx->ceed, CeedOperatorLinearAssembleDiagonal(ctx->op_mult, ctx->x_loc, CEED_REQUEST_IMMEDIATE));
1389*c63b910fSJames Wright   PetscCall(PetscLogEventEnd(MATCEED_ASSEMBLE_DIAGONAL_CEEDOP, A, D, NULL, NULL));
139058600ac3SJames Wright 
139158600ac3SJames Wright   // Restore PETSc vector
1392a7dac1d5SJames Wright   PetscCall(VecCeedToPetsc(ctx->x_loc, mem_type, D_loc));
139358600ac3SJames Wright 
139458600ac3SJames Wright   // Local-to-Global
139558600ac3SJames Wright   PetscCall(VecZeroEntries(D));
139658600ac3SJames Wright   PetscCall(DMLocalToGlobal(ctx->dm_x, D_loc, ADD_VALUES, D));
139758600ac3SJames Wright   PetscCall(DMRestoreLocalVector(ctx->dm_x, &D_loc));
1398*c63b910fSJames Wright   PetscCall(PetscLogEventEnd(MATCEED_ASSEMBLE_DIAGONAL, A, D, NULL, NULL));
139958600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
140058600ac3SJames Wright }
140158600ac3SJames Wright 
140258600ac3SJames Wright /**
140358600ac3SJames Wright   @brief Compute `A X = Y` for a `MATCEED`.
140458600ac3SJames Wright 
140558600ac3SJames Wright   Collective across MPI processes.
140658600ac3SJames Wright 
140758600ac3SJames Wright   @param[in]   A  `MATCEED`
140858600ac3SJames Wright   @param[in]   X  Input PETSc vector
140958600ac3SJames Wright   @param[out]  Y  Output PETSc vector
141058600ac3SJames Wright 
141158600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
141258600ac3SJames Wright **/
141358600ac3SJames Wright PetscErrorCode MatMult_Ceed(Mat A, Vec X, Vec Y) {
141458600ac3SJames Wright   MatCeedContext ctx;
141558600ac3SJames Wright 
141658600ac3SJames Wright   PetscFunctionBeginUser;
141758600ac3SJames Wright   PetscCall(MatShellGetContext(A, &ctx));
1418*c63b910fSJames Wright   PetscCall(PetscLogEventBegin(ctx->log_event_mult, A, X, Y, NULL));
141958600ac3SJames Wright 
142058600ac3SJames Wright   {
142158600ac3SJames Wright     PetscMemType x_mem_type, y_mem_type;
142258600ac3SJames Wright     Vec          X_loc = ctx->X_loc, Y_loc;
142358600ac3SJames Wright 
142458600ac3SJames Wright     // Get local vectors
142558600ac3SJames Wright     if (!ctx->X_loc) PetscCall(DMGetLocalVector(ctx->dm_x, &X_loc));
142658600ac3SJames Wright     PetscCall(DMGetLocalVector(ctx->dm_y, &Y_loc));
142758600ac3SJames Wright 
142858600ac3SJames Wright     // Global-to-local
142958600ac3SJames Wright     PetscCall(DMGlobalToLocal(ctx->dm_x, X, INSERT_VALUES, X_loc));
143058600ac3SJames Wright 
143158600ac3SJames Wright     // Setup libCEED vectors
1432a7dac1d5SJames Wright     PetscCall(VecReadPetscToCeed(X_loc, &x_mem_type, ctx->x_loc));
143358600ac3SJames Wright     PetscCall(VecZeroEntries(Y_loc));
1434a7dac1d5SJames Wright     PetscCall(VecPetscToCeed(Y_loc, &y_mem_type, ctx->y_loc));
143558600ac3SJames Wright 
143658600ac3SJames Wright     // Apply libCEED operator
143758600ac3SJames Wright     PetscCall(PetscLogGpuTimeBegin());
1438*c63b910fSJames Wright     PetscCall(PetscLogEventBegin(ctx->log_event_ceed_mult, A, X, Y, NULL));
143950f50432SJames Wright     PetscCallCeed(ctx->ceed, CeedOperatorApplyAdd(ctx->op_mult, ctx->x_loc, ctx->y_loc, CEED_REQUEST_IMMEDIATE));
1440*c63b910fSJames Wright     PetscCall(PetscLogEventEnd(ctx->log_event_ceed_mult, A, X, Y, NULL));
144158600ac3SJames Wright     PetscCall(PetscLogGpuTimeEnd());
144258600ac3SJames Wright 
144358600ac3SJames Wright     // Restore PETSc vectors
1444a7dac1d5SJames Wright     PetscCall(VecReadCeedToPetsc(ctx->x_loc, x_mem_type, X_loc));
1445a7dac1d5SJames Wright     PetscCall(VecCeedToPetsc(ctx->y_loc, y_mem_type, Y_loc));
144658600ac3SJames Wright 
144758600ac3SJames Wright     // Local-to-global
144858600ac3SJames Wright     PetscCall(VecZeroEntries(Y));
144958600ac3SJames Wright     PetscCall(DMLocalToGlobal(ctx->dm_y, Y_loc, ADD_VALUES, Y));
145058600ac3SJames Wright 
145158600ac3SJames Wright     // Restore local vectors, as needed
145258600ac3SJames Wright     if (!ctx->X_loc) PetscCall(DMRestoreLocalVector(ctx->dm_x, &X_loc));
145358600ac3SJames Wright     PetscCall(DMRestoreLocalVector(ctx->dm_y, &Y_loc));
145458600ac3SJames Wright   }
145558600ac3SJames Wright 
145658600ac3SJames Wright   // Log flops
145758600ac3SJames Wright   if (PetscMemTypeDevice(ctx->mem_type)) PetscCall(PetscLogGpuFlops(ctx->flops_mult));
145858600ac3SJames Wright   else PetscCall(PetscLogFlops(ctx->flops_mult));
1459*c63b910fSJames Wright   PetscCall(PetscLogEventEnd(ctx->log_event_mult, A, X, Y, NULL));
146058600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
146158600ac3SJames Wright }
146258600ac3SJames Wright 
146358600ac3SJames Wright /**
146458600ac3SJames Wright   @brief Compute `A^T Y = X` for a `MATCEED`.
146558600ac3SJames Wright 
146658600ac3SJames Wright   Collective across MPI processes.
146758600ac3SJames Wright 
146858600ac3SJames Wright   @param[in]   A  `MATCEED`
146958600ac3SJames Wright   @param[in]   Y  Input PETSc vector
147058600ac3SJames Wright   @param[out]  X  Output PETSc vector
147158600ac3SJames Wright 
147258600ac3SJames Wright   @return An error code: 0 - success, otherwise - failure
147358600ac3SJames Wright **/
147458600ac3SJames Wright PetscErrorCode MatMultTranspose_Ceed(Mat A, Vec Y, Vec X) {
147558600ac3SJames Wright   MatCeedContext ctx;
147658600ac3SJames Wright 
147758600ac3SJames Wright   PetscFunctionBeginUser;
147858600ac3SJames Wright   PetscCall(MatShellGetContext(A, &ctx));
1479*c63b910fSJames Wright   PetscCall(PetscLogEventBegin(ctx->log_event_mult_transpose, A, Y, X, NULL));
148058600ac3SJames Wright 
148158600ac3SJames Wright   {
148258600ac3SJames Wright     PetscMemType x_mem_type, y_mem_type;
148358600ac3SJames Wright     Vec          X_loc, Y_loc = ctx->Y_loc_transpose;
148458600ac3SJames Wright 
148558600ac3SJames Wright     // Get local vectors
148658600ac3SJames Wright     if (!ctx->Y_loc_transpose) PetscCall(DMGetLocalVector(ctx->dm_y, &Y_loc));
148758600ac3SJames Wright     PetscCall(DMGetLocalVector(ctx->dm_x, &X_loc));
148858600ac3SJames Wright 
148958600ac3SJames Wright     // Global-to-local
149058600ac3SJames Wright     PetscCall(DMGlobalToLocal(ctx->dm_y, Y, INSERT_VALUES, Y_loc));
149158600ac3SJames Wright 
149258600ac3SJames Wright     // Setup libCEED vectors
1493a7dac1d5SJames Wright     PetscCall(VecReadPetscToCeed(Y_loc, &y_mem_type, ctx->y_loc));
149458600ac3SJames Wright     PetscCall(VecZeroEntries(X_loc));
1495a7dac1d5SJames Wright     PetscCall(VecPetscToCeed(X_loc, &x_mem_type, ctx->x_loc));
149658600ac3SJames Wright 
149758600ac3SJames Wright     // Apply libCEED operator
149858600ac3SJames Wright     PetscCall(PetscLogGpuTimeBegin());
1499*c63b910fSJames Wright     PetscCall(PetscLogEventBegin(ctx->log_event_ceed_mult_transpose, A, Y, X, NULL));
150050f50432SJames Wright     PetscCallCeed(ctx->ceed, CeedOperatorApplyAdd(ctx->op_mult_transpose, ctx->y_loc, ctx->x_loc, CEED_REQUEST_IMMEDIATE));
1501*c63b910fSJames Wright     PetscCall(PetscLogEventEnd(ctx->log_event_ceed_mult_transpose, A, Y, X, NULL));
150258600ac3SJames Wright     PetscCall(PetscLogGpuTimeEnd());
150358600ac3SJames Wright 
150458600ac3SJames Wright     // Restore PETSc vectors
1505a7dac1d5SJames Wright     PetscCall(VecReadCeedToPetsc(ctx->y_loc, y_mem_type, Y_loc));
1506a7dac1d5SJames Wright     PetscCall(VecCeedToPetsc(ctx->x_loc, x_mem_type, X_loc));
150758600ac3SJames Wright 
150858600ac3SJames Wright     // Local-to-global
150958600ac3SJames Wright     PetscCall(VecZeroEntries(X));
151058600ac3SJames Wright     PetscCall(DMLocalToGlobal(ctx->dm_x, X_loc, ADD_VALUES, X));
151158600ac3SJames Wright 
151258600ac3SJames Wright     // Restore local vectors, as needed
151358600ac3SJames Wright     if (!ctx->Y_loc_transpose) PetscCall(DMRestoreLocalVector(ctx->dm_y, &Y_loc));
151458600ac3SJames Wright     PetscCall(DMRestoreLocalVector(ctx->dm_x, &X_loc));
151558600ac3SJames Wright   }
151658600ac3SJames Wright 
151758600ac3SJames Wright   // Log flops
151858600ac3SJames Wright   if (PetscMemTypeDevice(ctx->mem_type)) PetscCall(PetscLogGpuFlops(ctx->flops_mult_transpose));
151958600ac3SJames Wright   else PetscCall(PetscLogFlops(ctx->flops_mult_transpose));
1520*c63b910fSJames Wright   PetscCall(PetscLogEventEnd(ctx->log_event_mult_transpose, A, Y, X, NULL));
152158600ac3SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
152258600ac3SJames Wright }
1523