17b8c5038SJames Wright // SPDX-FileCopyrightText: Copyright (c) 2017-2024, HONEE contributors. 27b8c5038SJames Wright // SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause 37b8c5038SJames Wright 47b8c5038SJames Wright /// @file 57b8c5038SJames Wright /// Utilities for setting up DM and PetscFE 67b8c5038SJames Wright 77b8c5038SJames Wright #include <ceed.h> 87b8c5038SJames Wright #include <dm-utils.h> 97b8c5038SJames Wright #include <petsc-ceed-utils.h> 107b8c5038SJames Wright #include <petscdmplex.h> 117b8c5038SJames Wright #include <petscds.h> 127b8c5038SJames Wright 137b8c5038SJames Wright /** 147b8c5038SJames Wright @brief Convert `DM` field to `DS` field. 157b8c5038SJames Wright 167b8c5038SJames Wright @param[in] dm `DM` holding mesh 177b8c5038SJames Wright @param[in] domain_label Label for `DM` domain 187b8c5038SJames Wright @param[in] dm_field Index of `DM` field 197b8c5038SJames Wright @param[out] ds_field Index of `DS` field 207b8c5038SJames Wright 217b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 227b8c5038SJames Wright **/ 237b8c5038SJames Wright PetscErrorCode DMFieldToDSField(DM dm, DMLabel domain_label, PetscInt dm_field, PetscInt *ds_field) { 247b8c5038SJames Wright PetscDS ds; 257b8c5038SJames Wright IS field_is; 267b8c5038SJames Wright const PetscInt *fields; 277b8c5038SJames Wright PetscInt num_fields; 287b8c5038SJames Wright 297b8c5038SJames Wright PetscFunctionBeginUser; 307b8c5038SJames Wright PetscCall(DMGetRegionDS(dm, domain_label, &field_is, &ds, NULL)); 317b8c5038SJames Wright PetscCall(ISGetIndices(field_is, &fields)); 327b8c5038SJames Wright PetscCall(ISGetSize(field_is, &num_fields)); 337b8c5038SJames Wright for (PetscInt i = 0; i < num_fields; i++) { 347b8c5038SJames Wright if (dm_field == fields[i]) { 357b8c5038SJames Wright *ds_field = i; 367b8c5038SJames Wright break; 377b8c5038SJames Wright } 387b8c5038SJames Wright } 397b8c5038SJames Wright PetscCall(ISRestoreIndices(field_is, &fields)); 407b8c5038SJames Wright 417b8c5038SJames Wright PetscCheck(*ds_field != -1, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Could not find dm_field %" PetscInt_FMT " in DS", dm_field); 427b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 437b8c5038SJames Wright } 447b8c5038SJames Wright 457b8c5038SJames Wright /** 467b8c5038SJames Wright @brief Destroy `ElemRestriction` in a `PetscContainer`. 477b8c5038SJames Wright 487b8c5038SJames Wright Not collective across MPI processes. 497b8c5038SJames Wright 507b8c5038SJames Wright @param[in,out] ctx `CeedElemRestriction` 517b8c5038SJames Wright 527b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 537b8c5038SJames Wright **/ 547b8c5038SJames Wright static PetscErrorCode DMPlexCeedElemRestrictionDestroy(void **ctx) { 557b8c5038SJames Wright CeedElemRestriction rstr = *(CeedElemRestriction *)ctx; 567b8c5038SJames Wright 577b8c5038SJames Wright PetscFunctionBegin; 587b8c5038SJames Wright PetscCallCeed(CeedElemRestrictionReturnCeed(rstr), CeedElemRestrictionDestroy(&rstr)); 597b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 607b8c5038SJames Wright } 617b8c5038SJames Wright 627b8c5038SJames Wright /** 637b8c5038SJames Wright @brief Create `CeedElemRestriction` from `DMPlex`. 647b8c5038SJames Wright 657b8c5038SJames Wright The `CeedElemRestriction` is stored in the `DMPlex` object for reuse; 667b8c5038SJames Wright if the routine is called again with the same arguments, the same `CeedElemRestriction` is returned. 677b8c5038SJames Wright 687b8c5038SJames Wright Not collective across MPI processes. 697b8c5038SJames Wright 707b8c5038SJames Wright @param[in] ceed `Ceed` context 717b8c5038SJames Wright @param[in] dm `DMPlex` holding mesh 727b8c5038SJames Wright @param[in] domain_label `DMLabel` for `DMPlex` domain 737b8c5038SJames Wright @param[in] label_value Stratum value 747b8c5038SJames Wright @param[in] height Height of `DMPlex` topology 757b8c5038SJames Wright @param[in] dm_field Index of `DMPlex` field 767b8c5038SJames Wright @param[out] restriction `CeedElemRestriction` for `DMPlex` 777b8c5038SJames Wright 787b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 797b8c5038SJames Wright **/ 807b8c5038SJames Wright PetscErrorCode DMPlexCeedElemRestrictionCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, PetscInt dm_field, 817b8c5038SJames Wright CeedElemRestriction *restriction) { 827b8c5038SJames Wright char container_name[1024]; 837b8c5038SJames Wright CeedElemRestriction container_restriction; 847b8c5038SJames Wright 857b8c5038SJames Wright PetscFunctionBeginUser; 867b8c5038SJames Wright { 877b8c5038SJames Wright const char *label_name = NULL; 887b8c5038SJames Wright 897b8c5038SJames Wright if (domain_label) PetscCall(PetscObjectGetName((PetscObject)domain_label, &label_name)); 907b8c5038SJames Wright PetscCall(PetscSNPrintf(container_name, sizeof(container_name), 917b8c5038SJames Wright "DM CeedElemRestriction - label: %s label value: %" PetscInt_FMT " height: %" PetscInt_FMT " DM field: %" PetscInt_FMT, 927b8c5038SJames Wright label_name ? label_name : "NONE", label_value, height, dm_field)); 937b8c5038SJames Wright } 947b8c5038SJames Wright PetscCall(PetscObjectContainerQuery((PetscObject)dm, container_name, (void **)&container_restriction)); 957b8c5038SJames Wright 967b8c5038SJames Wright if (container_restriction) { 977b8c5038SJames Wright // Copy existing restriction 987b8c5038SJames Wright *restriction = NULL; 997b8c5038SJames Wright PetscCallCeed(ceed, CeedElemRestrictionReferenceCopy(container_restriction, restriction)); 1007b8c5038SJames Wright } else { 1017b8c5038SJames Wright PetscInt num_elem, elem_size, num_dof, num_comp, *restriction_offsets_petsc; 1027b8c5038SJames Wright CeedInt *restriction_offsets_ceed = NULL; 1037b8c5038SJames Wright 1047b8c5038SJames Wright // Build restriction 1057b8c5038SJames Wright PetscCall(DMPlexGetLocalOffsets(dm, domain_label, label_value, height, dm_field, &num_elem, &elem_size, &num_comp, &num_dof, 1067b8c5038SJames Wright &restriction_offsets_petsc)); 1077b8c5038SJames Wright PetscCall(IntArrayPetscToCeed(num_elem * elem_size, &restriction_offsets_petsc, &restriction_offsets_ceed)); 1087b8c5038SJames Wright PetscCallCeed(ceed, CeedElemRestrictionCreate(ceed, num_elem, elem_size, num_comp, 1, num_dof, CEED_MEM_HOST, CEED_COPY_VALUES, 1097b8c5038SJames Wright restriction_offsets_ceed, restriction)); 1107b8c5038SJames Wright PetscCall(PetscFree(restriction_offsets_ceed)); 1117b8c5038SJames Wright 1127b8c5038SJames Wright // Set in container 1137b8c5038SJames Wright PetscCallCeed(ceed, CeedElemRestrictionReferenceCopy(*restriction, &container_restriction)); 114*26d401f3SJames Wright PetscCall(PetscObjectContainerCompose((PetscObject)dm, container_name, container_restriction, 115*26d401f3SJames Wright (PetscCtxDestroyFn *)DMPlexCeedElemRestrictionDestroy)); 1167b8c5038SJames Wright } 1177b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 1187b8c5038SJames Wright } 1197b8c5038SJames Wright 1207b8c5038SJames Wright /** 1217b8c5038SJames Wright @brief Create `CeedElemRestriction` from `DMPlex` domain for mesh coordinates. 1227b8c5038SJames Wright 1237b8c5038SJames Wright The `CeedElemRestriction` is stored in the coordinate `DMPlex` object for reuse; 1247b8c5038SJames Wright if the routine is called again with the same arguments, the same `CeedElemRestriction` is returned. 1257b8c5038SJames Wright 1267b8c5038SJames Wright Not collective across MPI processes. 1277b8c5038SJames Wright 1287b8c5038SJames Wright @param[in] ceed `Ceed` context 1297b8c5038SJames Wright @param[in] dm `DMPlex` holding mesh 1307b8c5038SJames Wright @param[in] domain_label Label for `DMPlex` domain 1317b8c5038SJames Wright @param[in] label_value Stratum value 1327b8c5038SJames Wright @param[in] height Height of `DMPlex` topology 1337b8c5038SJames Wright @param[out] restriction `CeedElemRestriction` for mesh 1347b8c5038SJames Wright 1357b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 1367b8c5038SJames Wright **/ 1377b8c5038SJames Wright PetscErrorCode DMPlexCeedElemRestrictionCoordinateCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, 1387b8c5038SJames Wright CeedElemRestriction *restriction) { 1397b8c5038SJames Wright DM dm_coord; 1407b8c5038SJames Wright 1417b8c5038SJames Wright PetscFunctionBeginUser; 1427b8c5038SJames Wright PetscCall(DMGetCellCoordinateDM(dm, &dm_coord)); 1437b8c5038SJames Wright if (!dm_coord) { 1447b8c5038SJames Wright PetscCall(DMGetCoordinateDM(dm, &dm_coord)); 1457b8c5038SJames Wright } 1467b8c5038SJames Wright PetscCall(DMPlexCeedElemRestrictionCreate(ceed, dm_coord, domain_label, label_value, height, 0, restriction)); 1477b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 1487b8c5038SJames Wright } 1497b8c5038SJames Wright 1507b8c5038SJames Wright /** 1517b8c5038SJames Wright @brief Create `CeedElemRestriction` from `DMPlex` domain for auxilury `QFunction` data. 1527b8c5038SJames Wright 1537b8c5038SJames Wright Not collective across MPI processes. 1547b8c5038SJames Wright 1557b8c5038SJames Wright @param[in] ceed `Ceed` context 1567b8c5038SJames Wright @param[in] dm `DMPlex` holding mesh 1577b8c5038SJames Wright @param[in] domain_label Label for `DMPlex` domain 1587b8c5038SJames Wright @param[in] label_value Stratum value 1597b8c5038SJames Wright @param[in] height Height of `DMPlex` topology 1607b8c5038SJames Wright @param[in] q_data_size Number of components for `QFunction` data 1617b8c5038SJames Wright @param[in] is_collocated Boolean flag indicating if the data is collocated on the nodes (`PETSC_TRUE`) or on quadrature points (`PETSC_FALSE`) 1627b8c5038SJames Wright @param[out] restriction Strided `CeedElemRestriction` for `QFunction` data 1637b8c5038SJames Wright 1647b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 1657b8c5038SJames Wright **/ 1667b8c5038SJames Wright static PetscErrorCode DMPlexCeedElemRestrictionStridedCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, 1677b8c5038SJames Wright PetscInt q_data_size, PetscBool is_collocated, CeedElemRestriction *restriction) { 1687b8c5038SJames Wright PetscInt num_elem, num_qpts, dm_field = 0; 1697b8c5038SJames Wright 1707b8c5038SJames Wright PetscFunctionBeginUser; 1717b8c5038SJames Wright { // Get number of elements 1727b8c5038SJames Wright PetscInt depth; 1737b8c5038SJames Wright DMLabel depth_label; 1747b8c5038SJames Wright IS point_is, depth_is; 1757b8c5038SJames Wright 1767b8c5038SJames Wright PetscCall(DMPlexGetDepth(dm, &depth)); 1777b8c5038SJames Wright PetscCall(DMPlexGetDepthLabel(dm, &depth_label)); 1787b8c5038SJames Wright PetscCall(DMLabelGetStratumIS(depth_label, depth - height, &depth_is)); 1797b8c5038SJames Wright if (domain_label) { 1807b8c5038SJames Wright IS domain_is; 1817b8c5038SJames Wright 1827b8c5038SJames Wright PetscCall(DMLabelGetStratumIS(domain_label, label_value, &domain_is)); 1837b8c5038SJames Wright if (domain_is) { 1847b8c5038SJames Wright PetscCall(ISIntersect(depth_is, domain_is, &point_is)); 1857b8c5038SJames Wright PetscCall(ISDestroy(&domain_is)); 1867b8c5038SJames Wright } else { 1877b8c5038SJames Wright point_is = NULL; 1887b8c5038SJames Wright } 1897b8c5038SJames Wright PetscCall(ISDestroy(&depth_is)); 1907b8c5038SJames Wright } else { 1917b8c5038SJames Wright point_is = depth_is; 1927b8c5038SJames Wright } 1937b8c5038SJames Wright if (point_is) { 1947b8c5038SJames Wright PetscCall(ISGetLocalSize(point_is, &num_elem)); 1957b8c5038SJames Wright } else { 1967b8c5038SJames Wright num_elem = 0; 1977b8c5038SJames Wright } 1987b8c5038SJames Wright PetscCall(ISDestroy(&point_is)); 1997b8c5038SJames Wright } 2007b8c5038SJames Wright 2017b8c5038SJames Wright { // Get number of quadrature points 2027b8c5038SJames Wright PetscDS ds; 2037b8c5038SJames Wright PetscFE fe; 2047b8c5038SJames Wright PetscInt ds_field = -1; 2057b8c5038SJames Wright 2067b8c5038SJames Wright PetscCall(DMGetRegionDS(dm, domain_label, NULL, &ds, NULL)); 2077b8c5038SJames Wright PetscCall(DMFieldToDSField(dm, domain_label, dm_field, &ds_field)); 2087b8c5038SJames Wright PetscCall(PetscDSGetDiscretization(ds, ds_field, (PetscObject *)&fe)); 2097b8c5038SJames Wright PetscCall(PetscFEGetHeightSubspace(fe, height, &fe)); 2107b8c5038SJames Wright if (is_collocated) { 2117b8c5038SJames Wright PetscDualSpace dual_space; 2127b8c5038SJames Wright PetscInt num_dual_basis_vectors, dim, num_comp; 2137b8c5038SJames Wright 2147b8c5038SJames Wright PetscCall(PetscFEGetSpatialDimension(fe, &dim)); 2157b8c5038SJames Wright PetscCall(PetscFEGetNumComponents(fe, &num_comp)); 2167b8c5038SJames Wright PetscCall(PetscFEGetDualSpace(fe, &dual_space)); 2177b8c5038SJames Wright PetscCall(PetscDualSpaceGetDimension(dual_space, &num_dual_basis_vectors)); 2187b8c5038SJames Wright num_qpts = num_dual_basis_vectors / num_comp; 2197b8c5038SJames Wright } else { 2207b8c5038SJames Wright PetscQuadrature quadrature; 2217b8c5038SJames Wright 2227b8c5038SJames Wright PetscCall(DMGetRegionDS(dm, domain_label, NULL, &ds, NULL)); 2237b8c5038SJames Wright PetscCall(DMFieldToDSField(dm, domain_label, dm_field, &ds_field)); 2247b8c5038SJames Wright PetscCall(PetscDSGetDiscretization(ds, ds_field, (PetscObject *)&fe)); 2257b8c5038SJames Wright PetscCall(PetscFEGetHeightSubspace(fe, height, &fe)); 2267b8c5038SJames Wright PetscCall(PetscFEGetQuadrature(fe, &quadrature)); 2277b8c5038SJames Wright PetscCall(PetscQuadratureGetData(quadrature, NULL, NULL, &num_qpts, NULL, NULL)); 2287b8c5038SJames Wright } 2297b8c5038SJames Wright } 2307b8c5038SJames Wright 2317b8c5038SJames Wright // Create the restriction 2327b8c5038SJames Wright PetscCallCeed(ceed, CeedElemRestrictionCreateStrided(ceed, num_elem, num_qpts, q_data_size, num_elem * num_qpts * q_data_size, CEED_STRIDES_BACKEND, 2337b8c5038SJames Wright restriction)); 2347b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 2357b8c5038SJames Wright } 2367b8c5038SJames Wright 2377b8c5038SJames Wright /** 2387b8c5038SJames Wright @brief Create `CeedElemRestriction` from `DMPlex` domain for auxilury `QFunction` data. 2397b8c5038SJames Wright 2407b8c5038SJames Wright Not collective across MPI processes. 2417b8c5038SJames Wright 2427b8c5038SJames Wright @param[in] ceed `Ceed` context 2437b8c5038SJames Wright @param[in] dm `DMPlex` holding mesh 2447b8c5038SJames Wright @param[in] domain_label Label for `DMPlex` domain 2457b8c5038SJames Wright @param[in] label_value Stratum value 2467b8c5038SJames Wright @param[in] height Height of `DMPlex` topology 2477b8c5038SJames Wright @param[in] q_data_size Number of components for `QFunction` data 2487b8c5038SJames Wright @param[out] restriction Strided `CeedElemRestriction` for `QFunction` data 2497b8c5038SJames Wright 2507b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 2517b8c5038SJames Wright **/ 2527b8c5038SJames Wright PetscErrorCode DMPlexCeedElemRestrictionQDataCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, 2537b8c5038SJames Wright PetscInt q_data_size, CeedElemRestriction *restriction) { 2547b8c5038SJames Wright PetscFunctionBeginUser; 2557b8c5038SJames Wright PetscCall(DMPlexCeedElemRestrictionStridedCreate(ceed, dm, domain_label, label_value, height, q_data_size, PETSC_FALSE, restriction)); 2567b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 2577b8c5038SJames Wright } 2587b8c5038SJames Wright 2597b8c5038SJames Wright /** 2607b8c5038SJames Wright @brief Create `CeedElemRestriction` from `DMPlex` domain for nodally collocated auxilury `QFunction` data. 2617b8c5038SJames Wright 2627b8c5038SJames Wright Not collective across MPI processes. 2637b8c5038SJames Wright 2647b8c5038SJames Wright @param[in] ceed `Ceed` context 2657b8c5038SJames Wright @param[in] dm `DMPlex` holding mesh 2667b8c5038SJames Wright @param[in] domain_label Label for `DMPlex` domain 2677b8c5038SJames Wright @param[in] label_value Stratum value 2687b8c5038SJames Wright @param[in] height Height of `DMPlex` topology 2697b8c5038SJames Wright @param[in] q_data_size Number of components for `QFunction` data 2707b8c5038SJames Wright @param[out] restriction Strided `CeedElemRestriction` for `QFunction` data 2717b8c5038SJames Wright 2727b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 2737b8c5038SJames Wright **/ 2747b8c5038SJames Wright PetscErrorCode DMPlexCeedElemRestrictionCollocatedCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, 2757b8c5038SJames Wright PetscInt q_data_size, CeedElemRestriction *restriction) { 2767b8c5038SJames Wright PetscFunctionBeginUser; 2777b8c5038SJames Wright PetscCall(DMPlexCeedElemRestrictionStridedCreate(ceed, dm, domain_label, label_value, height, q_data_size, PETSC_TRUE, restriction)); 2787b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 2797b8c5038SJames Wright } 2807b8c5038SJames Wright 2817b8c5038SJames Wright /** 2827b8c5038SJames Wright @brief Creates a tensor-product uniform quadrature. 2837b8c5038SJames Wright This is only for comparison studies and generally should not be used. 2847b8c5038SJames Wright 2857b8c5038SJames Wright @param[in] dim Spatial dimension 2867b8c5038SJames Wright @param[in] num_comp Number of components 2877b8c5038SJames Wright @param[in] num_points Number of points in one dimension 2887b8c5038SJames Wright @param[in] a Left end of interval (often -1) 2897b8c5038SJames Wright @param[in] b Right end of interval (often +1) 2907b8c5038SJames Wright @param[out] q `PetscQuadrature` object 2917b8c5038SJames Wright 2927b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 2937b8c5038SJames Wright **/ 2947b8c5038SJames Wright static PetscErrorCode PetscDTUniformTensorQuadrature(PetscInt dim, PetscInt num_comp, PetscInt num_points, PetscReal a, PetscReal b, 2957b8c5038SJames Wright PetscQuadrature *q) { 2967b8c5038SJames Wright DMPolytopeType ct; 2977b8c5038SJames Wright PetscInt num_total_points = dim > 1 ? (dim > 2 ? (num_points * num_points * num_points) : (num_points * num_points)) : num_points; 2987b8c5038SJames Wright PetscReal *coords, *weights, *coords_1d; 2997b8c5038SJames Wright 3007b8c5038SJames Wright PetscFunctionBeginUser; 3017b8c5038SJames Wright PetscCall(PetscMalloc1(num_total_points * dim, &coords)); 3027b8c5038SJames Wright PetscCall(PetscMalloc1(num_total_points * num_comp, &weights)); 3037b8c5038SJames Wright // Compute weights, points 3047b8c5038SJames Wright switch (dim) { 3057b8c5038SJames Wright case 0: 3067b8c5038SJames Wright ct = DM_POLYTOPE_POINT; 3077b8c5038SJames Wright PetscCall(PetscFree(coords)); 3087b8c5038SJames Wright PetscCall(PetscFree(weights)); 3097b8c5038SJames Wright PetscCall(PetscMalloc1(1, &coords)); 3107b8c5038SJames Wright PetscCall(PetscMalloc1(num_comp, &weights)); 3117b8c5038SJames Wright num_total_points = 1; 3127b8c5038SJames Wright coords[0] = 0.0; 3137b8c5038SJames Wright for (PetscInt c = 0; c < num_comp; c++) weights[c] = 1.0; 3147b8c5038SJames Wright break; 3157b8c5038SJames Wright case 1: { 3167b8c5038SJames Wright ct = DM_POLYTOPE_SEGMENT; 3177b8c5038SJames Wright PetscReal step = (b - a) / num_points; 3187b8c5038SJames Wright 3197b8c5038SJames Wright for (PetscInt i = 0; i < num_points; i++) { 3207b8c5038SJames Wright coords[i] = step * (i + 0.5) + a; 3217b8c5038SJames Wright for (PetscInt c = 0; c < num_comp; c++) weights[i * num_comp + c] = 1.0; 3227b8c5038SJames Wright } 3237b8c5038SJames Wright } break; 3247b8c5038SJames Wright case 2: { 3257b8c5038SJames Wright ct = DM_POLYTOPE_QUADRILATERAL; 3267b8c5038SJames Wright PetscCall(PetscMalloc1(num_points, &coords_1d)); 3277b8c5038SJames Wright PetscReal step = (b - a) / num_points; 3287b8c5038SJames Wright 3297b8c5038SJames Wright for (PetscInt i = 0; i < num_points; i++) coords_1d[i] = step * (i + 0.5) + a; 3307b8c5038SJames Wright for (PetscInt i = 0; i < num_points; i++) { 3317b8c5038SJames Wright for (PetscInt j = 0; j < num_points; j++) { 3327b8c5038SJames Wright coords[(i * num_points + j) * dim + 0] = coords_1d[i]; 3337b8c5038SJames Wright coords[(i * num_points + j) * dim + 1] = coords_1d[j]; 3347b8c5038SJames Wright for (PetscInt c = 0; c < num_comp; c++) weights[(i * num_points + j) * num_comp + c] = 1.0; 3357b8c5038SJames Wright } 3367b8c5038SJames Wright } 3377b8c5038SJames Wright PetscCall(PetscFree(coords_1d)); 3387b8c5038SJames Wright } break; 3397b8c5038SJames Wright case 3: { 3407b8c5038SJames Wright ct = DM_POLYTOPE_HEXAHEDRON; 3417b8c5038SJames Wright PetscCall(PetscMalloc1(num_points, &coords_1d)); 3427b8c5038SJames Wright PetscReal step = (b - a) / num_points; 3437b8c5038SJames Wright 3447b8c5038SJames Wright for (PetscInt i = 0; i < num_points; i++) coords_1d[i] = step * (i + 0.5) + a; 3457b8c5038SJames Wright for (PetscInt i = 0; i < num_points; i++) { 3467b8c5038SJames Wright for (PetscInt j = 0; j < num_points; j++) { 3477b8c5038SJames Wright for (PetscInt k = 0; k < num_points; k++) { 3487b8c5038SJames Wright coords[((i * num_points + j) * num_points + k) * dim + 0] = coords_1d[i]; 3497b8c5038SJames Wright coords[((i * num_points + j) * num_points + k) * dim + 1] = coords_1d[j]; 3507b8c5038SJames Wright coords[((i * num_points + j) * num_points + k) * dim + 2] = coords_1d[k]; 3517b8c5038SJames Wright for (PetscInt c = 0; c < num_comp; c++) weights[((i * num_points + j) * num_points + k) * num_comp + c] = 1.0; 3527b8c5038SJames Wright } 3537b8c5038SJames Wright } 3547b8c5038SJames Wright } 3557b8c5038SJames Wright PetscCall(PetscFree(coords_1d)); 3567b8c5038SJames Wright } break; 3577b8c5038SJames Wright // LCOV_EXCL_START 3587b8c5038SJames Wright default: 3597b8c5038SJames Wright SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %" PetscInt_FMT, dim); 3607b8c5038SJames Wright // LCOV_EXCL_STOP 3617b8c5038SJames Wright } 3627b8c5038SJames Wright PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q)); 3637b8c5038SJames Wright PetscCall(PetscQuadratureSetCellType(*q, ct)); 3647b8c5038SJames Wright PetscCall(PetscQuadratureSetOrder(*q, num_points)); 3657b8c5038SJames Wright PetscCall(PetscQuadratureSetData(*q, dim, num_comp, num_total_points, coords, weights)); 3667b8c5038SJames Wright PetscCall(PetscObjectChangeTypeName((PetscObject)*q, "UniformTensor")); 3677b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 3687b8c5038SJames Wright } 3697b8c5038SJames Wright 3707b8c5038SJames Wright /** 3717b8c5038SJames Wright @brief Setup `DM` with FE space of appropriate degree 3727b8c5038SJames Wright 3737b8c5038SJames Wright @param[in] comm MPI communicator 3747b8c5038SJames Wright @param[in] dim Spatial dimension 3757b8c5038SJames Wright @param[in] num_comp Number of components 3767b8c5038SJames Wright @param[in] is_simplex Flag for simplex or tensor product element 3777b8c5038SJames Wright @param[in] order Polynomial order of space 3787b8c5038SJames Wright @param[in] q_order Quadrature order 3797b8c5038SJames Wright @param[in] prefix The options prefix, or `NULL` 3807b8c5038SJames Wright @param[out] fem `PetscFE` object 3817b8c5038SJames Wright 3827b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 3837b8c5038SJames Wright **/ 3847b8c5038SJames Wright PetscErrorCode PetscFECreateLagrangeFromOptions(MPI_Comm comm, PetscInt dim, PetscInt num_comp, PetscBool is_simplex, PetscInt order, 3857b8c5038SJames Wright PetscInt q_order, const char prefix[], PetscFE *fem) { 3867b8c5038SJames Wright PetscBool is_tensor = !is_simplex; 3877b8c5038SJames Wright DMPolytopeType polytope_type = DMPolytopeTypeSimpleShape(dim, is_simplex); 3887b8c5038SJames Wright PetscSpace fe_space; 3897b8c5038SJames Wright PetscDualSpace fe_dual_space; 3907b8c5038SJames Wright PetscQuadrature quadrature, face_quadrature; 3917b8c5038SJames Wright 3927b8c5038SJames Wright PetscFunctionBeginUser; 3937b8c5038SJames Wright // Create space 3947b8c5038SJames Wright PetscCall(PetscSpaceCreate(comm, &fe_space)); 3957b8c5038SJames Wright PetscCall(PetscSpaceSetType(fe_space, PETSCSPACEPOLYNOMIAL)); 3967b8c5038SJames Wright PetscCall(PetscObjectSetOptionsPrefix((PetscObject)fe_space, prefix)); 3977b8c5038SJames Wright PetscCall(PetscSpacePolynomialSetTensor(fe_space, is_tensor)); 3987b8c5038SJames Wright PetscCall(PetscSpaceSetNumComponents(fe_space, num_comp)); 3997b8c5038SJames Wright PetscCall(PetscSpaceSetNumVariables(fe_space, dim)); 4007b8c5038SJames Wright if (order >= 0) { 4017b8c5038SJames Wright PetscCall(PetscSpaceSetDegree(fe_space, order, PETSC_DETERMINE)); 4027b8c5038SJames Wright if (polytope_type == DM_POLYTOPE_TRI_PRISM || polytope_type == DM_POLYTOPE_TRI_PRISM_TENSOR) { 4037b8c5038SJames Wright PetscSpace fe_space_end, fe_space_side; 4047b8c5038SJames Wright 4057b8c5038SJames Wright PetscCall(PetscSpaceSetNumComponents(fe_space, 1)); 4067b8c5038SJames Wright PetscCall(PetscSpaceCreate(comm, &fe_space_end)); 4077b8c5038SJames Wright PetscCall(PetscSpaceSetType(fe_space_end, PETSCSPACEPOLYNOMIAL)); 4087b8c5038SJames Wright PetscCall(PetscSpacePolynomialSetTensor(fe_space_end, PETSC_FALSE)); 4097b8c5038SJames Wright PetscCall(PetscSpaceSetNumComponents(fe_space_end, 1)); 4107b8c5038SJames Wright PetscCall(PetscSpaceSetNumVariables(fe_space_end, dim - 1)); 4117b8c5038SJames Wright PetscCall(PetscSpaceSetDegree(fe_space_end, order, PETSC_DETERMINE)); 4127b8c5038SJames Wright PetscCall(PetscSpaceCreate(comm, &fe_space_side)); 4137b8c5038SJames Wright PetscCall(PetscSpaceSetType(fe_space_side, PETSCSPACEPOLYNOMIAL)); 4147b8c5038SJames Wright PetscCall(PetscSpacePolynomialSetTensor(fe_space_side, PETSC_FALSE)); 4157b8c5038SJames Wright PetscCall(PetscSpaceSetNumComponents(fe_space_side, 1)); 4167b8c5038SJames Wright PetscCall(PetscSpaceSetNumVariables(fe_space_side, 1)); 4177b8c5038SJames Wright PetscCall(PetscSpaceSetDegree(fe_space_side, order, PETSC_DETERMINE)); 4187b8c5038SJames Wright PetscCall(PetscSpaceSetType(fe_space, PETSCSPACETENSOR)); 4197b8c5038SJames Wright PetscCall(PetscSpaceTensorSetNumSubspaces(fe_space, 2)); 4207b8c5038SJames Wright PetscCall(PetscSpaceTensorSetSubspace(fe_space, 0, fe_space_end)); 4217b8c5038SJames Wright PetscCall(PetscSpaceTensorSetSubspace(fe_space, 1, fe_space_side)); 4227b8c5038SJames Wright PetscCall(PetscSpaceDestroy(&fe_space_end)); 4237b8c5038SJames Wright PetscCall(PetscSpaceDestroy(&fe_space_side)); 4247b8c5038SJames Wright 4257b8c5038SJames Wright if (num_comp > 1) { 4267b8c5038SJames Wright PetscSpace scalar_fe_space = fe_space; 4277b8c5038SJames Wright 4287b8c5038SJames Wright PetscCall(PetscSpaceCreate(comm, &fe_space)); 4297b8c5038SJames Wright PetscCall(PetscSpaceSetNumVariables(fe_space, dim)); 4307b8c5038SJames Wright PetscCall(PetscSpaceSetNumComponents(fe_space, num_comp)); 4317b8c5038SJames Wright PetscCall(PetscSpaceSetType(fe_space, PETSCSPACESUM)); 4327b8c5038SJames Wright PetscCall(PetscSpaceSumSetNumSubspaces(fe_space, num_comp)); 4337b8c5038SJames Wright PetscCall(PetscSpaceSumSetConcatenate(fe_space, PETSC_TRUE)); 4347b8c5038SJames Wright PetscCall(PetscSpaceSumSetInterleave(fe_space, PETSC_TRUE, PETSC_FALSE)); 4357b8c5038SJames Wright for (PetscInt i = 0; i < num_comp; i++) PetscCall(PetscSpaceSumSetSubspace(fe_space, i, scalar_fe_space)); 4367b8c5038SJames Wright PetscCall(PetscSpaceDestroy(&scalar_fe_space)); 4377b8c5038SJames Wright } 4387b8c5038SJames Wright } 4397b8c5038SJames Wright } 4407b8c5038SJames Wright PetscCall(PetscSpaceSetFromOptions(fe_space)); 4417b8c5038SJames Wright PetscCall(PetscSpaceSetUp(fe_space)); 4427b8c5038SJames Wright PetscCall(PetscSpaceGetDegree(fe_space, &order, NULL)); 4437b8c5038SJames Wright PetscCall(PetscSpacePolynomialGetTensor(fe_space, &is_tensor)); 4447b8c5038SJames Wright PetscCall(PetscSpaceGetNumComponents(fe_space, &num_comp)); 4457b8c5038SJames Wright 4467b8c5038SJames Wright // Create dual space 4477b8c5038SJames Wright PetscCall(PetscDualSpaceCreate(comm, &fe_dual_space)); 4487b8c5038SJames Wright PetscCall(PetscDualSpaceSetType(fe_dual_space, PETSCDUALSPACELAGRANGE)); 4497b8c5038SJames Wright PetscCall(PetscObjectSetOptionsPrefix((PetscObject)fe_dual_space, prefix)); 4507b8c5038SJames Wright { 4517b8c5038SJames Wright DM dual_space_dm; 4527b8c5038SJames Wright 4537b8c5038SJames Wright PetscCall(DMPlexCreateReferenceCell(PETSC_COMM_SELF, polytope_type, &dual_space_dm)); 4547b8c5038SJames Wright PetscCall(PetscDualSpaceSetDM(fe_dual_space, dual_space_dm)); 4557b8c5038SJames Wright PetscCall(DMDestroy(&dual_space_dm)); 4567b8c5038SJames Wright } 4577b8c5038SJames Wright PetscCall(PetscDualSpaceSetNumComponents(fe_dual_space, num_comp)); 4587b8c5038SJames Wright PetscCall(PetscDualSpaceSetOrder(fe_dual_space, order)); 4597b8c5038SJames Wright PetscCall(PetscDualSpaceLagrangeSetTensor(fe_dual_space, (is_tensor || (polytope_type == DM_POLYTOPE_TRI_PRISM)) ? PETSC_TRUE : PETSC_FALSE)); 4607b8c5038SJames Wright PetscCall(PetscDualSpaceSetFromOptions(fe_dual_space)); 4617b8c5038SJames Wright PetscCall(PetscDualSpaceSetUp(fe_dual_space)); 4627b8c5038SJames Wright 4637b8c5038SJames Wright // Create quadrature 4647b8c5038SJames Wright q_order = q_order >= 0 ? q_order : order; 4657b8c5038SJames Wright PetscBool use_uniform = PETSC_FALSE; 4667b8c5038SJames Wright 4677b8c5038SJames Wright PetscOptionsBegin(comm, NULL, "Uniform quadrature check", NULL); 4687b8c5038SJames Wright PetscCall(PetscOptionsBool("-petscdt_uniform_tensor_quadrature", "", NULL, use_uniform, &use_uniform, NULL)); 4697b8c5038SJames Wright PetscOptionsEnd(); 4707b8c5038SJames Wright PetscCheck(!use_uniform || (is_tensor || dim == 1), comm, PETSC_ERR_SUP, "Can only use uniform quadrature on tensor product elements"); 4717b8c5038SJames Wright if (use_uniform) { 4727b8c5038SJames Wright PetscCall(PetscDTUniformTensorQuadrature(dim, 1, q_order + 1, -1.0, 1.0, &quadrature)); 4737b8c5038SJames Wright PetscCall(PetscDTUniformTensorQuadrature(dim - 1, 1, q_order + 1, -1.0, 1.0, &face_quadrature)); 4747b8c5038SJames Wright } else { 4757b8c5038SJames Wright PetscCall(PetscDTCreateDefaultQuadrature(polytope_type, q_order, &quadrature, &face_quadrature)); 4767b8c5038SJames Wright } 4777b8c5038SJames Wright 4787b8c5038SJames Wright // Create finite element 4797b8c5038SJames Wright PetscCall(PetscFECreateFromSpaces(fe_space, fe_dual_space, quadrature, face_quadrature, fem)); 4807b8c5038SJames Wright PetscCall(PetscFESetFromOptions(*fem)); 4817b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 4827b8c5038SJames Wright } 4837b8c5038SJames Wright 4847b8c5038SJames Wright /** 4857b8c5038SJames Wright @brief Get global `DMPlex` topology type. 4867b8c5038SJames Wright 4877b8c5038SJames Wright Collective across MPI processes. 4887b8c5038SJames Wright 4897b8c5038SJames Wright @param[in] dm `DMPlex` holding mesh 4907b8c5038SJames Wright @param[in] domain_label `DMLabel` for `DMPlex` domain 4917b8c5038SJames Wright @param[in] label_value Stratum value 4927b8c5038SJames Wright @param[in] height Height of `DMPlex` topology 4937b8c5038SJames Wright @param[out] cell_type `DMPlex` topology type 4947b8c5038SJames Wright **/ 4957b8c5038SJames Wright static inline PetscErrorCode GetGlobalDMPlexPolytopeType(DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, 4967b8c5038SJames Wright DMPolytopeType *cell_type) { 4977b8c5038SJames Wright PetscInt first_point; 4987b8c5038SJames Wright PetscInt ids[1] = {label_value}; 4997b8c5038SJames Wright DMLabel depth_label; 5007b8c5038SJames Wright 5017b8c5038SJames Wright PetscFunctionBeginUser; 5027b8c5038SJames Wright // Use depth label if no domain label present 5037b8c5038SJames Wright if (!domain_label) { 5047b8c5038SJames Wright PetscInt depth; 5057b8c5038SJames Wright 5067b8c5038SJames Wright PetscCall(DMPlexGetDepth(dm, &depth)); 5077b8c5038SJames Wright PetscCall(DMPlexGetDepthLabel(dm, &depth_label)); 5087b8c5038SJames Wright ids[0] = depth - height; 5097b8c5038SJames Wright } 5107b8c5038SJames Wright 5117b8c5038SJames Wright // Get cell interp, grad, and quadrature data 5127b8c5038SJames Wright PetscCall(DMGetFirstLabeledPoint(dm, dm, domain_label ? domain_label : depth_label, 1, ids, height, &first_point, NULL)); 5137b8c5038SJames Wright if (first_point != -1) PetscCall(DMPlexGetCellType(dm, first_point, cell_type)); 5147b8c5038SJames Wright 5157b8c5038SJames Wright { // Form agreement about CellType 5167b8c5038SJames Wright PetscInt cell_type_local = -1, cell_type_global = -1; 5177b8c5038SJames Wright 5187b8c5038SJames Wright if (first_point != -1) cell_type_local = (PetscInt)*cell_type; 5197b8c5038SJames Wright PetscCallMPI(MPIU_Allreduce(&cell_type_local, &cell_type_global, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm))); 5207b8c5038SJames Wright *cell_type = (DMPolytopeType)cell_type_global; 5217b8c5038SJames Wright } 5227b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 5237b8c5038SJames Wright } 5247b8c5038SJames Wright 5257b8c5038SJames Wright /** 5267b8c5038SJames Wright @brief Get the permutation and field offset for a given depth. 5277b8c5038SJames Wright 5287b8c5038SJames Wright Not collective across MPI processes. 5297b8c5038SJames Wright 5307b8c5038SJames Wright @param[in] dm `DMPlex` holding mesh 5317b8c5038SJames Wright @param[in] depth Depth of `DMPlex` topology 5327b8c5038SJames Wright @param[in] field Index of `DMPlex` field 5337b8c5038SJames Wright @param[out] permutation Permutation for `DMPlex` field 5347b8c5038SJames Wright @param[out] field_offset Offset to `DMPlex` field 5357b8c5038SJames Wright **/ 5367b8c5038SJames Wright static inline PetscErrorCode GetClosurePermutationAndFieldOffsetAtDepth(DM dm, PetscInt depth, PetscInt field, IS *permutation, 5377b8c5038SJames Wright PetscInt *field_offset) { 5387b8c5038SJames Wright PetscBool is_field_continuous = PETSC_TRUE; 5397b8c5038SJames Wright PetscInt dim, num_fields, offset = 0, size = 0; 5407b8c5038SJames Wright PetscSection section; 5417b8c5038SJames Wright 5427b8c5038SJames Wright PetscFunctionBeginUser; 5437b8c5038SJames Wright *field_offset = 0; 5447b8c5038SJames Wright 5457b8c5038SJames Wright PetscCall(DMGetDimension(dm, &dim)); 5467b8c5038SJames Wright PetscCall(DMGetLocalSection(dm, §ion)); 5477b8c5038SJames Wright PetscCall(PetscSectionGetNumFields(section, &num_fields)); 5487b8c5038SJames Wright // ---- Permutation size and offset to current field 5497b8c5038SJames Wright for (PetscInt f = 0; f < num_fields; f++) { 5507b8c5038SJames Wright PetscBool is_continuous; 5517b8c5038SJames Wright PetscInt num_components, num_dof_1d, dual_space_size, field_size; 5527b8c5038SJames Wright PetscObject obj; 5537b8c5038SJames Wright PetscFE fe = NULL; 5547b8c5038SJames Wright PetscDualSpace dual_space; 5557b8c5038SJames Wright 5567b8c5038SJames Wright PetscCall(PetscSectionGetFieldComponents(section, f, &num_components)); 5577b8c5038SJames Wright PetscCall(DMGetField(dm, f, NULL, &obj)); 5587b8c5038SJames Wright fe = (PetscFE)obj; 5597b8c5038SJames Wright PetscCall(PetscFEGetDualSpace(fe, &dual_space)); 5607b8c5038SJames Wright PetscCall(PetscDualSpaceLagrangeGetContinuity(dual_space, &is_continuous)); 5617b8c5038SJames Wright if (f == field) is_field_continuous = is_continuous; 5627b8c5038SJames Wright if (!is_continuous) continue; 5637b8c5038SJames Wright PetscCall(PetscDualSpaceGetDimension(dual_space, &dual_space_size)); 5647b8c5038SJames Wright num_dof_1d = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / num_components, 1.0 / dim)); 5657b8c5038SJames Wright field_size = PetscPowInt(num_dof_1d, depth) * num_components; 5667b8c5038SJames Wright if (f < field) offset += field_size; 5677b8c5038SJames Wright size += field_size; 5687b8c5038SJames Wright } 5697b8c5038SJames Wright 5707b8c5038SJames Wright if (is_field_continuous) { 5717b8c5038SJames Wright *field_offset = offset; 5727b8c5038SJames Wright PetscCall(PetscSectionGetClosurePermutation(section, (PetscObject)dm, depth, size, permutation)); 5737b8c5038SJames Wright } 5747b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 5757b8c5038SJames Wright } 5767b8c5038SJames Wright 5777b8c5038SJames Wright /** 5787b8c5038SJames Wright @brief Compute field tabulation from `PetscTabulation`. 5797b8c5038SJames Wright 5807b8c5038SJames Wright Not collective across MPI processes. 5817b8c5038SJames Wright 5827b8c5038SJames Wright @param[in] dm `DMPlex` holding mesh 5837b8c5038SJames Wright @param[in] field Index of `DMPlex` field 5847b8c5038SJames Wright @param[in] face Index of basis face, or 0 5857b8c5038SJames Wright @param[in] tabulation PETSc basis tabulation 5867b8c5038SJames Wright @param[out] interp Interpolation matrix in libCEED orientation 5877b8c5038SJames Wright @param[out] grad Gradient matrix in libCEED orientation 5887b8c5038SJames Wright 5897b8c5038SJames Wright @note `interp` and `grad` are allocated using `PetscCalloc1` and must be freed by the user. 5907b8c5038SJames Wright **/ 5917b8c5038SJames Wright static inline PetscErrorCode ComputeFieldTabulationP2C(DM dm, PetscInt field, PetscInt face, PetscTabulation tabulation, CeedScalar **interp, 5927b8c5038SJames Wright CeedScalar **grad) { 5937b8c5038SJames Wright PetscBool is_simplex, has_permutation = PETSC_FALSE; 5947b8c5038SJames Wright PetscInt field_offset = 0, num_comp, P, Q, dim; 5957b8c5038SJames Wright const PetscInt *permutation_indices; 5967b8c5038SJames Wright IS permutation = NULL; 5977b8c5038SJames Wright 5987b8c5038SJames Wright PetscFunctionBeginUser; 5997b8c5038SJames Wright num_comp = tabulation->Nc; 6007b8c5038SJames Wright P = tabulation->Nb / num_comp; 6017b8c5038SJames Wright Q = tabulation->Np; 6027b8c5038SJames Wright dim = tabulation->cdim; 6037b8c5038SJames Wright 6047b8c5038SJames Wright PetscCall(PetscCalloc1(P * Q, interp)); 6057b8c5038SJames Wright PetscCall(PetscCalloc1(P * Q * dim, grad)); 6067b8c5038SJames Wright 6077b8c5038SJames Wright PetscCall(DMPlexIsSimplex(dm, &is_simplex)); 6087b8c5038SJames Wright if (!is_simplex) { 6097b8c5038SJames Wright PetscCall(GetClosurePermutationAndFieldOffsetAtDepth(dm, dim, field, &permutation, &field_offset)); 6107b8c5038SJames Wright has_permutation = !!permutation; 6117b8c5038SJames Wright if (has_permutation) PetscCall(ISGetIndices(permutation, &permutation_indices)); 6127b8c5038SJames Wright } 6137b8c5038SJames Wright 6147b8c5038SJames Wright const CeedInt c = 0; 6157b8c5038SJames Wright for (CeedInt q = 0; q < Q; q++) { 6167b8c5038SJames Wright for (CeedInt p_ceed = 0; p_ceed < P; p_ceed++) { 6177b8c5038SJames Wright CeedInt p_petsc = has_permutation ? (permutation_indices[field_offset + p_ceed * num_comp] - field_offset) : (p_ceed * num_comp); 6187b8c5038SJames Wright (*interp)[q * P + p_ceed] = tabulation->T[0][((face * Q + q) * P * num_comp + p_petsc) * num_comp + c]; 6197b8c5038SJames Wright for (CeedInt d = 0; d < dim; d++) { 6207b8c5038SJames Wright (*grad)[(d * Q + q) * P + p_ceed] = tabulation->T[1][(((face * Q + q) * P * num_comp + p_petsc) * num_comp + c) * dim + d]; 6217b8c5038SJames Wright } 6227b8c5038SJames Wright } 6237b8c5038SJames Wright } 6247b8c5038SJames Wright 6257b8c5038SJames Wright if (has_permutation) PetscCall(ISRestoreIndices(permutation, &permutation_indices)); 6267b8c5038SJames Wright PetscCall(ISDestroy(&permutation)); 6277b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 6287b8c5038SJames Wright } 6297b8c5038SJames Wright 6307b8c5038SJames Wright /** 6317b8c5038SJames Wright @brief Get quadrature data from `PetscQuadrature` for use with libCEED. 6327b8c5038SJames Wright 6337b8c5038SJames Wright Not collective across MPI processes. 6347b8c5038SJames Wright 6357b8c5038SJames Wright @param[in] fe `PetscFE` object 6367b8c5038SJames Wright @param[in] quadrature PETSc basis quadrature 6377b8c5038SJames Wright @param[out] q_dim Quadrature dimension, or NULL if not needed 6387b8c5038SJames Wright @param[out] num_comp Number of components, or NULL if not needed 6397b8c5038SJames Wright @param[out] Q Number of quadrature points, or NULL if not needed 6407b8c5038SJames Wright @param[out] q_points Quadrature points in libCEED orientation, or NULL if not needed 6417b8c5038SJames Wright @param[out] q_weights Quadrature weights in libCEED orientation, or NULL if not needed 6427b8c5038SJames Wright 6437b8c5038SJames Wright @note `q_weights` and `q_points` are allocated using `PetscCalloc1` and must be freed by the user. 6447b8c5038SJames Wright **/ 6457b8c5038SJames Wright static inline PetscErrorCode GetQuadratureDataP2C(PetscFE fe, PetscQuadrature quadrature, PetscInt *q_dim, PetscInt *num_comp, PetscInt *Q, 6467b8c5038SJames Wright CeedScalar **q_points, CeedScalar **q_weights) { 6477b8c5038SJames Wright PetscInt spatial_dim, dim, num_comp_quadrature, num_q_points; 6487b8c5038SJames Wright const PetscReal *q_points_petsc, *q_weights_petsc; 6497b8c5038SJames Wright 6507b8c5038SJames Wright PetscFunctionBeginUser; 6517b8c5038SJames Wright PetscCall(PetscFEGetSpatialDimension(fe, &spatial_dim)); 6527b8c5038SJames Wright PetscCall(PetscQuadratureGetData(quadrature, &dim, &num_comp_quadrature, &num_q_points, &q_points_petsc, &q_weights_petsc)); 6537b8c5038SJames Wright if (q_dim) *q_dim = dim; 6547b8c5038SJames Wright if (num_comp) *num_comp = num_comp_quadrature; 6557b8c5038SJames Wright if (Q) *Q = num_q_points; 6567b8c5038SJames Wright if (q_weights) { 6577b8c5038SJames Wright PetscSizeT q_weights_size = num_q_points; 6587b8c5038SJames Wright 6597b8c5038SJames Wright PetscCall(PetscCalloc1(q_weights_size, q_weights)); 6607b8c5038SJames Wright for (CeedInt i = 0; i < num_q_points; i++) (*q_weights)[i] = q_weights_petsc[i]; 6617b8c5038SJames Wright } 6627b8c5038SJames Wright if (q_points) { 6637b8c5038SJames Wright PetscSizeT q_points_size = num_q_points * spatial_dim; 6647b8c5038SJames Wright 6657b8c5038SJames Wright PetscCall(PetscCalloc1(q_points_size, q_points)); 6667b8c5038SJames Wright for (CeedInt i = 0; i < num_q_points; i++) { 6677b8c5038SJames Wright for (CeedInt d = 0; d < dim; d++) (*q_points)[i * spatial_dim + d] = q_points_petsc[i * dim + d]; 6687b8c5038SJames Wright } 6697b8c5038SJames Wright } 6707b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 6717b8c5038SJames Wright } 6727b8c5038SJames Wright 6737b8c5038SJames Wright /** 6747b8c5038SJames Wright @brief Create 1D tabulation from `PetscFE`. 6757b8c5038SJames Wright 6767b8c5038SJames Wright Collective across MPI processes. 6777b8c5038SJames Wright 6787b8c5038SJames Wright @note `q_weights` and `q_points` are allocated using `PetscCalloc1` and must be freed by the user. 6797b8c5038SJames Wright 6807b8c5038SJames Wright @param[in] comm `MPI_Comm` for creating `FE` object from options 6817b8c5038SJames Wright @param[in] fe PETSc basis `FE` object 6827b8c5038SJames Wright @param[out] tabulation PETSc basis tabulation 6837b8c5038SJames Wright @param[out] q_points_1d Quadrature points in libCEED orientation 6847b8c5038SJames Wright @param[out] q_weights_1d Quadrature weights in libCEED orientation 6857b8c5038SJames Wright 6867b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 6877b8c5038SJames Wright **/ 6887b8c5038SJames Wright static inline PetscErrorCode Create1DTabulation_Tensor(MPI_Comm comm, PetscFE fe, PetscTabulation *tabulation, PetscReal **q_points_1d, 6897b8c5038SJames Wright CeedScalar **q_weights_1d) { 6907b8c5038SJames Wright PetscFE fe_1d; 6917b8c5038SJames Wright PetscInt dim, num_comp, Q = -1, q_dim = -1, num_derivatives = 1; 6927b8c5038SJames Wright PetscQuadrature quadrature; 6937b8c5038SJames Wright 6947b8c5038SJames Wright PetscFunctionBeginUser; 6957b8c5038SJames Wright // Create 1D FE 6967b8c5038SJames Wright PetscCall(PetscFEGetNumComponents(fe, &num_comp)); 6977b8c5038SJames Wright PetscCall(PetscFEGetSpatialDimension(fe, &dim)); 6987b8c5038SJames Wright { 6997b8c5038SJames Wright const char *prefix; 7007b8c5038SJames Wright PetscBool is_tensor; 7017b8c5038SJames Wright PetscInt num_comp, order, q_order; 7027b8c5038SJames Wright PetscDualSpace dual_space; 7037b8c5038SJames Wright PetscQuadrature quadrature; 7047b8c5038SJames Wright 7057b8c5038SJames Wright PetscCall(PetscObjectGetOptionsPrefix((PetscObject)fe, &prefix)); 7067b8c5038SJames Wright PetscCall(PetscFEGetDualSpace(fe, &dual_space)); 7077b8c5038SJames Wright PetscCall(PetscFEGetNumComponents(fe, &num_comp)); 7087b8c5038SJames Wright PetscCall(PetscDualSpaceLagrangeGetTensor(dual_space, &is_tensor)); 7097b8c5038SJames Wright PetscCall(PetscDualSpaceGetOrder(dual_space, &order)); 7107b8c5038SJames Wright PetscCall(PetscFEGetQuadrature(fe, &quadrature)); 7117b8c5038SJames Wright PetscCall(PetscQuadratureGetData(quadrature, NULL, NULL, &q_order, NULL, NULL)); 7127b8c5038SJames Wright PetscCall(PetscFECreateLagrangeFromOptions(comm, 1, num_comp, !is_tensor, order, 7137b8c5038SJames Wright (PetscInt)PetscCeilReal(PetscPowReal(1.0 * q_order, 1.0 / dim)) - 1, prefix, &fe_1d)); 7147b8c5038SJames Wright } 7157b8c5038SJames Wright 7167b8c5038SJames Wright // Get quadrature data 7177b8c5038SJames Wright PetscCall(PetscFEGetQuadrature(fe_1d, &quadrature)); 7187b8c5038SJames Wright PetscCall(GetQuadratureDataP2C(fe_1d, quadrature, &q_dim, NULL, &Q, q_points_1d, q_weights_1d)); 7197b8c5038SJames Wright 7207b8c5038SJames Wright // Compute 1D tabulation 7217b8c5038SJames Wright PetscCall(PetscFECreateTabulation(fe_1d, 1, Q, *q_points_1d, num_derivatives, tabulation)); 7227b8c5038SJames Wright 7237b8c5038SJames Wright // Cleanup 7247b8c5038SJames Wright PetscCall(PetscFEDestroy(&fe_1d)); 7257b8c5038SJames Wright 7267b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 7277b8c5038SJames Wright } 7287b8c5038SJames Wright 7297b8c5038SJames Wright /** 7307b8c5038SJames Wright @brief Destroy `CeedBasis` in a `PetscContainer`. 7317b8c5038SJames Wright 7327b8c5038SJames Wright Not collective across MPI processes. 7337b8c5038SJames Wright 7347b8c5038SJames Wright @param[in,out] ctx `CeedBasis` 7357b8c5038SJames Wright 7367b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 7377b8c5038SJames Wright **/ 7387b8c5038SJames Wright static PetscErrorCode DMPlexCeedBasisDestroy(void **ctx) { 7397b8c5038SJames Wright CeedBasis basis = *(CeedBasis *)ctx; 7407b8c5038SJames Wright 7417b8c5038SJames Wright PetscFunctionBegin; 7427b8c5038SJames Wright PetscCallCeed(CeedBasisReturnCeed(basis), CeedBasisDestroy(&basis)); 7437b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 7447b8c5038SJames Wright } 7457b8c5038SJames Wright 7467b8c5038SJames Wright /** 7477b8c5038SJames Wright @brief Create `CeedBasis` from `DMPlex`. 7487b8c5038SJames Wright 7497b8c5038SJames Wright The `CeedBasis` is stored in the `DMPlex` object for reuse; 7507b8c5038SJames Wright if the routine is called again with the same arguments, the same `CeedBasis` is returned. 7517b8c5038SJames Wright 7527b8c5038SJames Wright Collective across MPI processes. 7537b8c5038SJames Wright 7547b8c5038SJames Wright @param[in] ceed `Ceed` context 7557b8c5038SJames Wright @param[in] dm `DMPlex` holding mesh 7567b8c5038SJames Wright @param[in] domain_label `DMLabel` for `DMPlex` domain 7577b8c5038SJames Wright @param[in] label_value Stratum value 7587b8c5038SJames Wright @param[in] height Height of `DMPlex` topology 7597b8c5038SJames Wright @param[in] dm_field Index of `DMPlex` field 7607b8c5038SJames Wright @param[out] basis `CeedBasis` for `DMPlex` 7617b8c5038SJames Wright 7627b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 7637b8c5038SJames Wright **/ 7647b8c5038SJames Wright PetscErrorCode DMPlexCeedBasisCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, PetscInt dm_field, 7657b8c5038SJames Wright CeedBasis *basis) { 7667b8c5038SJames Wright MPI_Comm comm = PetscObjectComm((PetscObject)dm); 7677b8c5038SJames Wright char container_name[1024]; 7687b8c5038SJames Wright CeedBasis container_basis; 7697b8c5038SJames Wright 7707b8c5038SJames Wright PetscFunctionBeginUser; 7717b8c5038SJames Wright { 7727b8c5038SJames Wright const char *label_name = NULL; 7737b8c5038SJames Wright 7747b8c5038SJames Wright if (domain_label) PetscCall(PetscObjectGetName((PetscObject)domain_label, &label_name)); 7757b8c5038SJames Wright PetscCall(PetscSNPrintf(container_name, sizeof(container_name), 7767b8c5038SJames Wright "DM CeedBasis - label: %s label value: %" PetscInt_FMT " height: %" PetscInt_FMT " DM field: %" PetscInt_FMT, 7777b8c5038SJames Wright label_name ? label_name : "NONE", label_value, height, dm_field)); 7787b8c5038SJames Wright } 7797b8c5038SJames Wright PetscCall(PetscObjectContainerQuery((PetscObject)dm, container_name, (void **)&container_basis)); 7807b8c5038SJames Wright 7817b8c5038SJames Wright if (container_basis) { 7827b8c5038SJames Wright // Copy existing basis 7837b8c5038SJames Wright *basis = NULL; 7847b8c5038SJames Wright PetscCallCeed(ceed, CeedBasisReferenceCopy(container_basis, basis)); 7857b8c5038SJames Wright } else { 7867b8c5038SJames Wright PetscDS ds; 7877b8c5038SJames Wright PetscFE fe; 7887b8c5038SJames Wright PetscDualSpace dual_space; 7897b8c5038SJames Wright PetscQuadrature quadrature; 7907b8c5038SJames Wright PetscBool is_tensor = PETSC_TRUE; 7917b8c5038SJames Wright PetscInt ds_field = -1; 7927b8c5038SJames Wright 7937b8c5038SJames Wright // Get element information 7947b8c5038SJames Wright PetscCall(DMGetRegionDS(dm, domain_label, NULL, &ds, NULL)); 7957b8c5038SJames Wright PetscCall(DMFieldToDSField(dm, domain_label, dm_field, &ds_field)); 7967b8c5038SJames Wright PetscCall(PetscDSGetDiscretization(ds, ds_field, (PetscObject *)&fe)); 7977b8c5038SJames Wright PetscCall(PetscFEGetHeightSubspace(fe, height, &fe)); 7987b8c5038SJames Wright PetscCall(PetscFEGetQuadrature(fe, &quadrature)); 7997b8c5038SJames Wright PetscCall(PetscFEGetDualSpace(fe, &dual_space)); 8007b8c5038SJames Wright PetscCall(PetscDualSpaceLagrangeGetTensor(dual_space, &is_tensor)); 8017b8c5038SJames Wright 8027b8c5038SJames Wright // Build libCEED basis 8037b8c5038SJames Wright PetscBool use_nontensor = PETSC_FALSE; 8047b8c5038SJames Wright 8057b8c5038SJames Wright PetscOptionsBegin(comm, NULL, "Tensor Basis as Nontensor Check", NULL); 8067b8c5038SJames Wright PetscCall(PetscOptionsBool("-ceed_basis_as_nontensor", "", NULL, use_nontensor, &use_nontensor, NULL)); 8077b8c5038SJames Wright PetscOptionsEnd(); 8087b8c5038SJames Wright 8097b8c5038SJames Wright if (!is_tensor || use_nontensor) { 8107b8c5038SJames Wright PetscTabulation basis_tabulation; 8117b8c5038SJames Wright PetscInt num_derivatives = 1, face = 0; 8127b8c5038SJames Wright CeedScalar *q_points, *q_weights, *interp, *grad; 8137b8c5038SJames Wright CeedElemTopology elem_topo; 8147b8c5038SJames Wright 8157b8c5038SJames Wright { 8167b8c5038SJames Wright DMPolytopeType cell_type; 8177b8c5038SJames Wright 8187b8c5038SJames Wright PetscCall(GetGlobalDMPlexPolytopeType(dm, domain_label, label_value, height, &cell_type)); 8197b8c5038SJames Wright elem_topo = PolytopeTypePetscToCeed(cell_type); 8207b8c5038SJames Wright PetscCheck(elem_topo, comm, PETSC_ERR_SUP, "DMPlex topology not supported: %s", DMPolytopeTypes[cell_type]); 8217b8c5038SJames Wright } 8227b8c5038SJames Wright 8237b8c5038SJames Wright // Compute basis tabulation 8247b8c5038SJames Wright PetscCall(GetQuadratureDataP2C(fe, quadrature, NULL, NULL, NULL, &q_points, &q_weights)); 8257b8c5038SJames Wright PetscCall(PetscFEGetCellTabulation(fe, num_derivatives, &basis_tabulation)); 8267b8c5038SJames Wright PetscCall(ComputeFieldTabulationP2C(dm, dm_field, face, basis_tabulation, &interp, &grad)); 8277b8c5038SJames Wright { 8287b8c5038SJames Wright PetscInt num_comp = basis_tabulation->Nc, P = basis_tabulation->Nb / num_comp, Q = basis_tabulation->Np; 8297b8c5038SJames Wright 8307b8c5038SJames Wright PetscCallCeed(ceed, CeedBasisCreateH1(ceed, elem_topo, num_comp, P, Q, interp, grad, q_points, q_weights, basis)); 8317b8c5038SJames Wright } 8327b8c5038SJames Wright 8337b8c5038SJames Wright PetscCall(PetscFree(q_points)); 8347b8c5038SJames Wright PetscCall(PetscFree(q_weights)); 8357b8c5038SJames Wright PetscCall(PetscFree(interp)); 8367b8c5038SJames Wright PetscCall(PetscFree(grad)); 8377b8c5038SJames Wright } else { 8387b8c5038SJames Wright PetscInt P_1d, Q_1d, num_comp, dim; 8397b8c5038SJames Wright PetscTabulation basis_tabulation; 8407b8c5038SJames Wright CeedScalar *q_points_1d, *q_weights_1d, *interp_1d, *grad_1d; 8417b8c5038SJames Wright 8427b8c5038SJames Wright PetscCall(PetscFEGetSpatialDimension(fe, &dim)); 8437b8c5038SJames Wright PetscCall(Create1DTabulation_Tensor(comm, fe, &basis_tabulation, &q_points_1d, &q_weights_1d)); 8447b8c5038SJames Wright num_comp = basis_tabulation->Nc; 8457b8c5038SJames Wright P_1d = basis_tabulation->Nb / num_comp; 8467b8c5038SJames Wright Q_1d = basis_tabulation->Np; 8477b8c5038SJames Wright PetscCall(ComputeFieldTabulationP2C(dm, dm_field, 0, basis_tabulation, &interp_1d, &grad_1d)); 8487b8c5038SJames Wright PetscCallCeed(ceed, CeedBasisCreateTensorH1(ceed, dim, num_comp, P_1d, Q_1d, interp_1d, grad_1d, q_points_1d, q_weights_1d, basis)); 8497b8c5038SJames Wright 8507b8c5038SJames Wright // Cleanup 8517b8c5038SJames Wright PetscCall(PetscFree(q_points_1d)); 8527b8c5038SJames Wright PetscCall(PetscFree(q_weights_1d)); 8537b8c5038SJames Wright PetscCall(PetscFree(interp_1d)); 8547b8c5038SJames Wright PetscCall(PetscFree(grad_1d)); 8557b8c5038SJames Wright PetscCall(PetscTabulationDestroy(&basis_tabulation)); 8567b8c5038SJames Wright } 8577b8c5038SJames Wright // Set in container 8587b8c5038SJames Wright PetscCallCeed(ceed, CeedBasisReferenceCopy(*basis, &container_basis)); 859*26d401f3SJames Wright PetscCall(PetscObjectContainerCompose((PetscObject)dm, container_name, container_basis, (PetscCtxDestroyFn *)DMPlexCeedBasisDestroy)); 8607b8c5038SJames Wright } 8617b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 8627b8c5038SJames Wright } 8637b8c5038SJames Wright 8647b8c5038SJames Wright /** 8657b8c5038SJames Wright @brief Create `CeedBasis` for coordinate space of `DMPlex`. 8667b8c5038SJames Wright 8677b8c5038SJames Wright The `CeedBasis` is stored in the coordinate `DMPlex` object for reuse; 8687b8c5038SJames Wright if the routine is called again with the same arguments, the same `CeedBasis` is returned. 8697b8c5038SJames Wright 8707b8c5038SJames Wright Collective across MPI processes. 8717b8c5038SJames Wright 8727b8c5038SJames Wright @param[in] ceed `Ceed` context 8737b8c5038SJames Wright @param[in] dm `DMPlex` holding mesh 8747b8c5038SJames Wright @param[in] domain_label `DMLabel` for `DMPlex` domain 8757b8c5038SJames Wright @param[in] label_value Stratum value 8767b8c5038SJames Wright @param[in] height Height of `DMPlex` topology 8777b8c5038SJames Wright @param[out] basis `CeedBasis` for coordinate space of `DMPlex` 8787b8c5038SJames Wright **/ 8797b8c5038SJames Wright PetscErrorCode DMPlexCeedBasisCoordinateCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, CeedBasis *basis) { 8807b8c5038SJames Wright DM dm_coord = NULL; 8817b8c5038SJames Wright 8827b8c5038SJames Wright PetscFunctionBeginUser; 88377efe802SJames Wright PetscCall(DMGetCoordinateDM(dm, &dm_coord)); 8847b8c5038SJames Wright PetscCall(DMPlexCeedBasisCreate(ceed, dm_coord, domain_label, label_value, height, 0, basis)); 8857b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 8867b8c5038SJames Wright } 8877b8c5038SJames Wright 88877efe802SJames Wright typedef struct CeedVectorAndState_ *CeedVectorAndState; 88977efe802SJames Wright struct CeedVectorAndState_ { 89077efe802SJames Wright CeedVector vector; // !< `CeedVector` containing copy of data from a `Vec` 89177efe802SJames Wright PetscObjectState state; // !< Object state of the `Vec` when the data was copied 89277efe802SJames Wright }; 89377efe802SJames Wright 89477efe802SJames Wright /** 89577efe802SJames Wright @brief Destroy `CeedVectorAndState` in a `PetscContainer`. 89677efe802SJames Wright 89777efe802SJames Wright Not collective across MPI processes. 89877efe802SJames Wright 89977efe802SJames Wright @param[in,out] ctx `CeedVectorAndState` 90077efe802SJames Wright 90177efe802SJames Wright @return An error code: 0 - success, otherwise - failure 90277efe802SJames Wright **/ 90377efe802SJames Wright static PetscErrorCode CeedVectorAndStateDestroy(void **ctx) { 90477efe802SJames Wright CeedVectorAndState vec_and_state = *(CeedVectorAndState *)ctx; 90577efe802SJames Wright 90677efe802SJames Wright PetscFunctionBegin; 90777efe802SJames Wright PetscCallCeed(CeedVectorReturnCeed(vec_and_state->vector), CeedVectorDestroy(&vec_and_state->vector)); 90877efe802SJames Wright PetscCall(PetscFree(vec_and_state)); 90977efe802SJames Wright *ctx = NULL; 91077efe802SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 91177efe802SJames Wright } 91277efe802SJames Wright 91377efe802SJames Wright /** 91477efe802SJames Wright @brief Get Ceed objects for coordinate field of DM 91577efe802SJames Wright 91677efe802SJames Wright @param[in] ceed `Ceed` context 91777efe802SJames Wright @param[in] dm `DMPlex` holding mesh 91877efe802SJames Wright @param[in] domain_label `DMLabel` for `DMPlex` domain 91977efe802SJames Wright @param[in] label_value Stratum value 92077efe802SJames Wright @param[in] height Height of `DMPlex` topology 92177efe802SJames Wright @param[out] elem_restr `CeedElemRestriction` for coodinates of `DMPlex`, or `NULL` 92277efe802SJames Wright @param[out] basis `CeedBasis` for coordinate space of `DMPlex`, or `NULL` 92377efe802SJames Wright @param[out] vector `CeedVector` for coordinates of `DMPlex`, or `NULL` 92477efe802SJames Wright **/ 92577efe802SJames Wright PetscErrorCode DMPlexCeedCoordinateCreateField(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, 92677efe802SJames Wright CeedElemRestriction *elem_restr, CeedBasis *basis, CeedVector *vector) { 92777efe802SJames Wright CeedElemRestriction elem_restr_ = NULL; 92877efe802SJames Wright 92977efe802SJames Wright PetscFunctionBeginUser; 93077efe802SJames Wright PetscCall(DMPlexCeedElemRestrictionCoordinateCreate(ceed, dm, domain_label, label_value, height, &elem_restr_)); 93177efe802SJames Wright if (elem_restr) { 93277efe802SJames Wright *elem_restr = NULL; 93377efe802SJames Wright PetscCallCeed(ceed, CeedElemRestrictionReferenceCopy(elem_restr_, elem_restr)); 93477efe802SJames Wright } 93577efe802SJames Wright if (basis) PetscCall(DMPlexCeedBasisCoordinateCreate(ceed, dm, domain_label, label_value, height, basis)); 93677efe802SJames Wright if (vector) { 93777efe802SJames Wright DM cdm; 93877efe802SJames Wright char container_name[] = "DM Coordinate CeedVectorAndState"; 93977efe802SJames Wright CeedVectorAndState vec_and_state; 94077efe802SJames Wright Vec X_loc; 94177efe802SJames Wright PetscObjectState X_loc_state; 94277efe802SJames Wright 94377efe802SJames Wright PetscCall(DMGetCellCoordinateDM(dm, &cdm)); 94477efe802SJames Wright if (cdm) { 94577efe802SJames Wright PetscCall(DMGetCellCoordinatesLocal(dm, &X_loc)); 94677efe802SJames Wright } else { 94777efe802SJames Wright PetscCall(DMGetCoordinatesLocal(dm, &X_loc)); 94877efe802SJames Wright } 94977efe802SJames Wright PetscCall(VecGetState(X_loc, &X_loc_state)); 95077efe802SJames Wright PetscCall(PetscObjectContainerQuery((PetscObject)X_loc, container_name, (void **)&vec_and_state)); 95177efe802SJames Wright 95277efe802SJames Wright if (vec_and_state && vec_and_state->state == X_loc_state) { 95377efe802SJames Wright // Copy existing vector 95477efe802SJames Wright *vector = NULL; 95577efe802SJames Wright PetscCallCeed(ceed, CeedVectorReferenceCopy(vec_and_state->vector, vector)); 95677efe802SJames Wright } else { 95777efe802SJames Wright PetscCall(CeedElemRestrictionCreateVector(elem_restr_, vector, NULL)); 95877efe802SJames Wright PetscCall(VecCopyPetscToCeed(X_loc, *vector)); 95977efe802SJames Wright 96077efe802SJames Wright // Set in container 96177efe802SJames Wright PetscCall(PetscNew(&vec_and_state)); 96277efe802SJames Wright PetscCallCeed(ceed, CeedVectorReferenceCopy(*vector, &vec_and_state->vector)); 96377efe802SJames Wright vec_and_state->state = X_loc_state; 964*26d401f3SJames Wright PetscCall(PetscObjectContainerCompose((PetscObject)X_loc, container_name, vec_and_state, (PetscCtxDestroyFn *)CeedVectorAndStateDestroy)); 96577efe802SJames Wright } 96677efe802SJames Wright } 96777efe802SJames Wright PetscCallCeed(ceed, CeedElemRestrictionDestroy(&elem_restr_)); 96877efe802SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 96977efe802SJames Wright } 97077efe802SJames Wright 9717b8c5038SJames Wright /** 9727b8c5038SJames Wright @brief Create `CeedBasis` for cell to face quadrature space evaluation from `DMPlex`. 9737b8c5038SJames Wright 9747b8c5038SJames Wright Collective across MPI processes. 9757b8c5038SJames Wright 9767b8c5038SJames Wright @param[in] ceed `Ceed` context 9777b8c5038SJames Wright @param[in] dm `DMPlex` holding mesh 9787b8c5038SJames Wright @param[in] domain_label `DMLabel` for `DMPlex` domain 9797b8c5038SJames Wright @param[in] label_value Stratum value 9807b8c5038SJames Wright @param[in] face Index of face 9817b8c5038SJames Wright @param[in] dm_field Index of `DMPlex` field 9827b8c5038SJames Wright @param[out] basis `CeedBasis` for cell to face evaluation 9837b8c5038SJames Wright **/ 9847b8c5038SJames Wright PetscErrorCode DMPlexCeedBasisCellToFaceCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt face, PetscInt dm_field, 9857b8c5038SJames Wright CeedBasis *basis) { 9867b8c5038SJames Wright PetscInt ds_field = -1, height = 0; 9877b8c5038SJames Wright PetscDS ds; 9887b8c5038SJames Wright PetscFE fe; 9897b8c5038SJames Wright PetscQuadrature face_quadrature; 9907b8c5038SJames Wright 9917b8c5038SJames Wright PetscFunctionBeginUser; 9927b8c5038SJames Wright // Get element information 9937b8c5038SJames Wright PetscCall(DMGetRegionDS(dm, domain_label, NULL, &ds, NULL)); 9947b8c5038SJames Wright PetscCall(DMFieldToDSField(dm, domain_label, dm_field, &ds_field)); 9957b8c5038SJames Wright PetscCall(PetscDSGetDiscretization(ds, ds_field, (PetscObject *)&fe)); 9967b8c5038SJames Wright PetscCall(PetscFEGetFaceQuadrature(fe, &face_quadrature)); 9977b8c5038SJames Wright 9987b8c5038SJames Wright { // Build libCEED basis 9997b8c5038SJames Wright PetscInt num_derivatives = 1, num_comp, P, Q = -1; 10007b8c5038SJames Wright PetscTabulation basis_tabulation; 10017b8c5038SJames Wright CeedScalar *q_points, *q_weights, *interp, *grad; 10027b8c5038SJames Wright CeedElemTopology elem_topo; 10037b8c5038SJames Wright 10047b8c5038SJames Wright { // Verify compatible element topology 10057b8c5038SJames Wright DMPolytopeType cell_type; 10067b8c5038SJames Wright 10077b8c5038SJames Wright PetscCall(GetGlobalDMPlexPolytopeType(dm, domain_label, label_value, height, &cell_type)); 10087b8c5038SJames Wright elem_topo = PolytopeTypePetscToCeed(cell_type); 10097b8c5038SJames Wright PetscCheck(elem_topo, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "DMPlex topology not supported: %s", DMPolytopeTypes[cell_type]); 10107b8c5038SJames Wright } 10117b8c5038SJames Wright 10127b8c5038SJames Wright // Compute basis tabulation 10137b8c5038SJames Wright PetscCall(GetQuadratureDataP2C(fe, face_quadrature, NULL, NULL, &Q, &q_points, &q_weights)); 10147b8c5038SJames Wright PetscCall(PetscFEGetFaceTabulation(fe, num_derivatives, &basis_tabulation)); 10157b8c5038SJames Wright num_comp = basis_tabulation->Nc; 10167b8c5038SJames Wright P = basis_tabulation->Nb / num_comp; 10177b8c5038SJames Wright PetscCall(ComputeFieldTabulationP2C(dm, dm_field, face, basis_tabulation, &interp, &grad)); 10187b8c5038SJames Wright PetscCallCeed(ceed, CeedBasisCreateH1(ceed, elem_topo, num_comp, P, Q, interp, grad, q_points, q_weights, basis)); 10197b8c5038SJames Wright 10207b8c5038SJames Wright PetscCall(PetscFree(q_points)); 10217b8c5038SJames Wright PetscCall(PetscFree(q_weights)); 10227b8c5038SJames Wright PetscCall(PetscFree(interp)); 10237b8c5038SJames Wright PetscCall(PetscFree(grad)); 10247b8c5038SJames Wright } 10257b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 10267b8c5038SJames Wright } 10277b8c5038SJames Wright 10287b8c5038SJames Wright /** 10297b8c5038SJames Wright @brief Create `CeedBasis` for cell to face quadrature space evaluation on coordinate space of `DMPlex`. 10307b8c5038SJames Wright 10317b8c5038SJames Wright Collective across MPI processes. 10327b8c5038SJames Wright 10337b8c5038SJames Wright @param[in] ceed `Ceed` context 10347b8c5038SJames Wright @param[in] dm `DMPlex` holding mesh 10357b8c5038SJames Wright @param[in] domain_label `DMLabel` for `DMPlex` domain 10367b8c5038SJames Wright @param[in] label_value Stratum value 10377b8c5038SJames Wright @param[in] face Index of face 10387b8c5038SJames Wright @param[out] basis `CeedBasis` for cell to face evaluation on coordinate space of `DMPlex` 10397b8c5038SJames Wright **/ 10407b8c5038SJames Wright PetscErrorCode DMPlexCeedBasisCellToFaceCoordinateCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt face, 10417b8c5038SJames Wright CeedBasis *basis) { 10427b8c5038SJames Wright DM dm_coord; 10437b8c5038SJames Wright 10447b8c5038SJames Wright PetscFunctionBeginUser; 10457b8c5038SJames Wright PetscCall(DMGetCellCoordinateDM(dm, &dm_coord)); 10467b8c5038SJames Wright if (!dm_coord) { 10477b8c5038SJames Wright PetscCall(DMGetCoordinateDM(dm, &dm_coord)); 10487b8c5038SJames Wright } 10497b8c5038SJames Wright PetscCall(DMPlexCeedBasisCellToFaceCreate(ceed, dm_coord, domain_label, label_value, face, 0, basis)); 10507b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 10517b8c5038SJames Wright } 10527b8c5038SJames Wright 10537b8c5038SJames Wright /** 10547b8c5038SJames Wright @brief Setup `DM` with FE space of appropriate degree 10557b8c5038SJames Wright 10567b8c5038SJames Wright Must be followed by `DMSetupByOrderEnd_FEM` 10577b8c5038SJames Wright 10587b8c5038SJames Wright @param[in] setup_faces Flag to setup face geometry 10597b8c5038SJames Wright @param[in] setup_coords Flag to setup coordinate spaces 10607b8c5038SJames Wright @param[in] degree Polynomial orders of field 10617b8c5038SJames Wright @param[in] coord_order Polynomial order of coordinate basis, or `PETSC_DECIDE` for default 10627b8c5038SJames Wright @param[in] q_extra Additional quadrature order 10637b8c5038SJames Wright @param[in] num_fields Number of fields in solution vector 10647b8c5038SJames Wright @param[in] field_sizes Array of number of components for each field 10657b8c5038SJames Wright @param[out] dm `DM` to setup 10667b8c5038SJames Wright 10677b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 10687b8c5038SJames Wright **/ 10697b8c5038SJames Wright PetscErrorCode DMSetupByOrderBegin_FEM(PetscBool setup_faces, PetscBool setup_coords, PetscInt degree, PetscInt coord_order, PetscInt q_extra, 10707b8c5038SJames Wright PetscInt num_fields, const PetscInt *field_sizes, DM dm) { 10717b8c5038SJames Wright PetscInt dim, q_order = degree + q_extra; 10727b8c5038SJames Wright PetscBool is_simplex = PETSC_TRUE; 10737b8c5038SJames Wright PetscFE fe; 10747b8c5038SJames Wright MPI_Comm comm = PetscObjectComm((PetscObject)dm); 10757b8c5038SJames Wright 10767b8c5038SJames Wright PetscFunctionBeginUser; 10777b8c5038SJames Wright PetscCall(DMPlexIsSimplex(dm, &is_simplex)); 10787b8c5038SJames Wright 10797b8c5038SJames Wright // Setup DM 10807b8c5038SJames Wright PetscCall(DMGetDimension(dm, &dim)); 10817b8c5038SJames Wright for (PetscInt i = 0; i < num_fields; i++) { 10827b8c5038SJames Wright PetscFE fe_face; 10837b8c5038SJames Wright PetscInt q_order = degree + q_extra; 10847b8c5038SJames Wright 10857b8c5038SJames Wright PetscCall(PetscFECreateLagrange(comm, dim, field_sizes[i], is_simplex, degree, q_order, &fe)); 10867b8c5038SJames Wright if (setup_faces) PetscCall(PetscFEGetHeightSubspace(fe, 1, &fe_face)); 10877b8c5038SJames Wright PetscCall(DMAddField(dm, NULL, (PetscObject)fe)); 10887b8c5038SJames Wright PetscCall(PetscFEDestroy(&fe)); 10897b8c5038SJames Wright } 10907b8c5038SJames Wright PetscCall(DMCreateDS(dm)); 10917b8c5038SJames Wright 10927b8c5038SJames Wright // Project coordinates to enrich quadrature space 10937b8c5038SJames Wright if (setup_coords) { 10947b8c5038SJames Wright DM dm_coord; 10957b8c5038SJames Wright PetscDS ds_coord; 10967b8c5038SJames Wright PetscFE fe_coord_current, fe_coord_new, fe_coord_face_new; 10977b8c5038SJames Wright PetscDualSpace fe_coord_dual_space; 10987b8c5038SJames Wright PetscInt fe_coord_order, num_comp_coord; 10997b8c5038SJames Wright 11007b8c5038SJames Wright PetscCall(DMGetCoordinateDM(dm, &dm_coord)); 11017b8c5038SJames Wright PetscCall(DMGetCoordinateDim(dm, &num_comp_coord)); 11027b8c5038SJames Wright PetscCall(DMGetRegionDS(dm_coord, NULL, NULL, &ds_coord, NULL)); 11037b8c5038SJames Wright PetscCall(PetscDSGetDiscretization(ds_coord, 0, (PetscObject *)&fe_coord_current)); 11047b8c5038SJames Wright PetscCall(PetscFEGetDualSpace(fe_coord_current, &fe_coord_dual_space)); 11057b8c5038SJames Wright PetscCall(PetscDualSpaceGetOrder(fe_coord_dual_space, &fe_coord_order)); 11067b8c5038SJames Wright 11077b8c5038SJames Wright // Create FE for coordinates 11087b8c5038SJames Wright if (coord_order != PETSC_DECIDE) fe_coord_order = coord_order; 11097b8c5038SJames Wright PetscCall(PetscFECreateLagrange(comm, dim, num_comp_coord, is_simplex, fe_coord_order, q_order, &fe_coord_new)); 11107b8c5038SJames Wright if (setup_faces) PetscCall(PetscFEGetHeightSubspace(fe_coord_new, 1, &fe_coord_face_new)); 11117b8c5038SJames Wright PetscCall(DMSetCoordinateDisc(dm, fe_coord_new, PETSC_FALSE, PETSC_TRUE)); 11127b8c5038SJames Wright PetscCall(PetscFEDestroy(&fe_coord_new)); 11137b8c5038SJames Wright } 11147b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 11157b8c5038SJames Wright } 11167b8c5038SJames Wright 11177b8c5038SJames Wright /** 11187b8c5038SJames Wright @brief Finish setting up `DM` 11197b8c5038SJames Wright 11207b8c5038SJames Wright Must be called after `DMSetupByOrderBegin_FEM` and all strong BCs have be declared via `DMAddBoundaries`. 11217b8c5038SJames Wright 11227b8c5038SJames Wright @param[in] setup_coords Flag to setup coordinate spaces 11237b8c5038SJames Wright @param[out] dm `DM` to setup 11247b8c5038SJames Wright 11257b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 11267b8c5038SJames Wright **/ 11277b8c5038SJames Wright PetscErrorCode DMSetupByOrderEnd_FEM(PetscBool setup_coords, DM dm) { 11287b8c5038SJames Wright PetscBool is_simplex; 11297b8c5038SJames Wright 11307b8c5038SJames Wright PetscFunctionBeginUser; 11317b8c5038SJames Wright PetscCall(DMPlexIsSimplex(dm, &is_simplex)); 11327b8c5038SJames Wright // Set tensor permutation if needed 11337b8c5038SJames Wright if (!is_simplex) { 11347b8c5038SJames Wright PetscCall(DMPlexSetClosurePermutationTensor(dm, PETSC_DETERMINE, NULL)); 11357b8c5038SJames Wright if (setup_coords) { 11367b8c5038SJames Wright DM dm_coord; 11377b8c5038SJames Wright 11387b8c5038SJames Wright PetscCall(DMGetCoordinateDM(dm, &dm_coord)); 11397b8c5038SJames Wright PetscCall(DMPlexSetClosurePermutationTensor(dm_coord, PETSC_DETERMINE, NULL)); 11407b8c5038SJames Wright } 11417b8c5038SJames Wright } 11427b8c5038SJames Wright PetscCall(DMLocalizeCoordinates(dm)); // Must localize after tensor closure setting 11437b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 11447b8c5038SJames Wright } 11457b8c5038SJames Wright 11467b8c5038SJames Wright /** 11477b8c5038SJames Wright @brief Setup `DM` with FE space of appropriate degree with no boundary conditions 11487b8c5038SJames Wright 11497b8c5038SJames Wright Calls `DMSetupByOrderBegin_FEM` and `DMSetupByOrderEnd_FEM` successively 11507b8c5038SJames Wright 11517b8c5038SJames Wright @param[in] setup_faces Flag to setup face geometry 11527b8c5038SJames Wright @param[in] setup_coords Flag to setup coordinate spaces 11537b8c5038SJames Wright @param[in] degree Polynomial orders of field 11547b8c5038SJames Wright @param[in] coord_order Polynomial order of coordinate basis, or `PETSC_DECIDE` for default 11557b8c5038SJames Wright @param[in] q_extra Additional quadrature order 11567b8c5038SJames Wright @param[in] num_fields Number of fields in solution vector 11577b8c5038SJames Wright @param[in] field_sizes Array of number of components for each field 11587b8c5038SJames Wright @param[out] dm `DM` to setup 11597b8c5038SJames Wright 11607b8c5038SJames Wright @return An error code: 0 - success, otherwise - failure 11617b8c5038SJames Wright **/ 11627b8c5038SJames Wright PetscErrorCode DMSetupByOrder_FEM(PetscBool setup_faces, PetscBool setup_coords, PetscInt degree, PetscInt coord_order, PetscInt q_extra, 11637b8c5038SJames Wright PetscInt num_fields, const PetscInt *field_sizes, DM dm) { 11647b8c5038SJames Wright PetscFunctionBeginUser; 11657b8c5038SJames Wright PetscCall(DMSetupByOrderBegin_FEM(setup_faces, setup_coords, degree, coord_order, q_extra, num_fields, field_sizes, dm)); 11667b8c5038SJames Wright PetscCall(DMSetupByOrderEnd_FEM(setup_coords, dm)); 11677b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 11687b8c5038SJames Wright } 11697b8c5038SJames Wright 11707b8c5038SJames Wright /** 11717b8c5038SJames Wright @brief Get the points in a label stratum which have requested height 11727b8c5038SJames Wright 11737b8c5038SJames Wright Example, from the "Face Sets" label (which contains face, edge, and vertex points), return points that correspond to face points (height = 1) and have a label value of 3: 11747b8c5038SJames Wright ``` 11757b8c5038SJames Wright PetscCall(DMGetStratumISAtHeight(dm, "Face Sets", 3, 1, &is_face_points)); 11767b8c5038SJames Wright ``` 11777b8c5038SJames Wright 11787b8c5038SJames Wright @param[in] dm DM which has the label 11797b8c5038SJames Wright @param[in] name Name fo the label 11807b8c5038SJames Wright @param[in] value Label value of points to return 11817b8c5038SJames Wright @param[in] height Height of points to return 11827b8c5038SJames Wright @param[out] points `IS` of points, or `NULL` is there are no points in that stratum at height 11837b8c5038SJames Wright **/ 11847b8c5038SJames Wright static PetscErrorCode DMGetStratumISAtHeight(DM dm, const char name[], PetscInt value, PetscInt height, IS *points) { 11857b8c5038SJames Wright PetscInt depth; 11867b8c5038SJames Wright DMLabel depth_label; 11877b8c5038SJames Wright IS label_is, depth_is; 11887b8c5038SJames Wright 11897b8c5038SJames Wright PetscFunctionBeginUser; 11907b8c5038SJames Wright PetscCall(DMPlexGetDepth(dm, &depth)); 11917b8c5038SJames Wright PetscCall(DMPlexGetDepthLabel(dm, &depth_label)); 11927b8c5038SJames Wright PetscCall(DMLabelGetStratumIS(depth_label, depth - height, &depth_is)); 11937b8c5038SJames Wright PetscCall(DMGetStratumIS(dm, name, value, &label_is)); 11947b8c5038SJames Wright if (label_is == NULL || depth_is == NULL) *points = NULL; 11957b8c5038SJames Wright else PetscCall(ISIntersect(depth_is, label_is, points)); 11967b8c5038SJames Wright PetscCall(ISDestroy(&depth_is)); 11977b8c5038SJames Wright PetscCall(ISDestroy(&label_is)); 11987b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 11997b8c5038SJames Wright } 12007b8c5038SJames Wright 12017b8c5038SJames Wright /** 12027b8c5038SJames Wright @brief Create a label with separate values for the `FE` orientations for all cells with this material for the `DMPlex` face. 12037b8c5038SJames Wright 12047b8c5038SJames Wright Collective across MPI processes. 12057b8c5038SJames Wright 12067b8c5038SJames Wright @param[in,out] dm `DMPlex` holding mesh 12077b8c5038SJames Wright @param[in] dm_face Face number on `DMPlex` 12087b8c5038SJames Wright @param[out] face_label_name Label name for newly created label, or `NULL` if no elements match the `material` and `dm_face` 12097b8c5038SJames Wright **/ 12107b8c5038SJames Wright PetscErrorCode DMPlexCreateFaceLabel(DM dm, PetscInt dm_face, char **face_label_name) { 12117b8c5038SJames Wright PetscInt num_face_points = 0, face_height = 1; 12127b8c5038SJames Wright const PetscInt *face_points; 12137b8c5038SJames Wright IS is_face_points; 12147b8c5038SJames Wright DMLabel face_label = NULL; 12157b8c5038SJames Wright MPI_Comm comm = PetscObjectComm((PetscObject)dm); 12167b8c5038SJames Wright 12177b8c5038SJames Wright PetscFunctionBeginUser; 12187b8c5038SJames Wright { // Create label name and label 12197b8c5038SJames Wright PetscSizeT label_name_len = 64; 12207b8c5038SJames Wright PetscBool has_label_already; 12217b8c5038SJames Wright 12227b8c5038SJames Wright // Face label 12237b8c5038SJames Wright PetscCall(PetscCalloc1(label_name_len, face_label_name)); 12247b8c5038SJames Wright PetscCall(PetscSNPrintf(*face_label_name, label_name_len, "Orientation for DM face %" PetscInt_FMT, dm_face)); 12257b8c5038SJames Wright PetscCall(DMHasLabel(dm, *face_label_name, &has_label_already)); 12267b8c5038SJames Wright if (has_label_already) PetscFunctionReturn(PETSC_SUCCESS); 12277b8c5038SJames Wright 12287b8c5038SJames Wright PetscCall(DMCreateLabel(dm, *face_label_name)); 12297b8c5038SJames Wright PetscCall(DMGetLabel(dm, *face_label_name, &face_label)); 12307b8c5038SJames Wright } 12317b8c5038SJames Wright 12327b8c5038SJames Wright PetscCall(DMGetStratumISAtHeight(dm, "Face Sets", dm_face, face_height, &is_face_points)); 12337b8c5038SJames Wright if (is_face_points) { 12347b8c5038SJames Wright PetscCall(ISGetLocalSize(is_face_points, &num_face_points)); 12357b8c5038SJames Wright PetscCall(ISGetIndices(is_face_points, &face_points)); 12367b8c5038SJames Wright } 12377b8c5038SJames Wright 12387b8c5038SJames Wright for (PetscInt face_index = 0; face_index < num_face_points; face_index++) { 12397b8c5038SJames Wright const PetscInt *face_support, *cell_cone; 12407b8c5038SJames Wright PetscInt face_support_size = 0, cell_cone_size = 0, fe_face_on_cell = -1; 12417b8c5038SJames Wright PetscInt face_point = face_points[face_index]; 12427b8c5038SJames Wright 12437b8c5038SJames Wright // Get cell supporting face 12447b8c5038SJames Wright PetscCall(DMPlexGetSupport(dm, face_point, &face_support)); 12457b8c5038SJames Wright PetscCall(DMPlexGetSupportSize(dm, face_point, &face_support_size)); 12467b8c5038SJames Wright PetscCheck(face_support_size == 1, comm, PETSC_ERR_SUP, "Expected one cell in support of exterior face, but got %" PetscInt_FMT " cells", 12477b8c5038SJames Wright face_support_size); 12487b8c5038SJames Wright PetscInt cell_point = face_support[0]; 12497b8c5038SJames Wright 12507b8c5038SJames Wright // Find face number 12517b8c5038SJames Wright PetscCall(DMPlexGetCone(dm, cell_point, &cell_cone)); 12527b8c5038SJames Wright PetscCall(DMPlexGetConeSize(dm, cell_point, &cell_cone_size)); 12537b8c5038SJames Wright for (PetscInt i = 0; i < cell_cone_size; i++) { 12547b8c5038SJames Wright if (cell_cone[i] == face_point) fe_face_on_cell = i; 12557b8c5038SJames Wright } 12567b8c5038SJames Wright PetscCheck(fe_face_on_cell != -1, comm, PETSC_ERR_SUP, "Could not find face %" PetscInt_FMT " in cone of its support", face_point); 12577b8c5038SJames Wright 12587b8c5038SJames Wright // Add to label 12597b8c5038SJames Wright PetscCall(DMLabelSetValue(face_label, face_point, fe_face_on_cell)); 12607b8c5038SJames Wright } 12617b8c5038SJames Wright PetscCall(DMPlexLabelAddFaceCells(dm, face_label)); 12627b8c5038SJames Wright PetscCall(ISDestroy(&is_face_points)); 12637b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 12647b8c5038SJames Wright } 12657b8c5038SJames Wright 12667b8c5038SJames Wright /** 12677b8c5038SJames Wright @brief Creates an array of all the values set for a `DMLabel` 12687b8c5038SJames Wright 12697b8c5038SJames Wright Because `DMLabelGetValueIS` returns label values locally, not globally. 12707b8c5038SJames Wright 12717b8c5038SJames Wright Collective across MPI processes. 12727b8c5038SJames Wright 12737b8c5038SJames Wright @param[in] dm `DM` with the label 12747b8c5038SJames Wright @param[in] label `DMLabel` to get values of 12757b8c5038SJames Wright @param[out] num_values Total number of values 12767b8c5038SJames Wright @param[out] value_array Array of label values, must be freed by user 12777b8c5038SJames Wright **/ 12787b8c5038SJames Wright PetscErrorCode DMLabelCreateGlobalValueArray(DM dm, DMLabel label, PetscInt *num_values, PetscInt **value_array) { 12797b8c5038SJames Wright PetscInt num_values_local, minmax_values[2], minmax_values_loc[2]; 12807b8c5038SJames Wright PetscInt *values_local = NULL; 12817b8c5038SJames Wright MPI_Comm comm = PetscObjectComm((PetscObject)dm); 12827b8c5038SJames Wright PetscSegBuffer seg; 12837b8c5038SJames Wright PetscCount seg_size; 12847b8c5038SJames Wright 12857b8c5038SJames Wright PetscFunctionBeginUser; 12867b8c5038SJames Wright { // Extract array of local DMLabel values so it can be sorted 12877b8c5038SJames Wright IS is_values; 12887b8c5038SJames Wright const PetscInt *is_values_local = NULL; 12897b8c5038SJames Wright 12907b8c5038SJames Wright PetscCall(DMLabelGetValueIS(label, &is_values)); 12917b8c5038SJames Wright PetscCall(ISGetLocalSize(is_values, &num_values_local)); 12927b8c5038SJames Wright 12937b8c5038SJames Wright PetscCall(ISGetIndices(is_values, &is_values_local)); 12947b8c5038SJames Wright PetscCall(PetscMalloc1(num_values_local, &values_local)); 12957b8c5038SJames Wright PetscCall(PetscArraycpy(values_local, is_values_local, num_values_local)); 12967b8c5038SJames Wright PetscCall(ISRestoreIndices(is_values, &is_values_local)); 12977b8c5038SJames Wright PetscCall(ISDestroy(&is_values)); 12987b8c5038SJames Wright } 12997b8c5038SJames Wright 13007b8c5038SJames Wright if (num_values_local) { 13017b8c5038SJames Wright PetscCall(PetscSortInt(num_values_local, values_local)); 13027b8c5038SJames Wright minmax_values_loc[0] = values_local[0]; 13037b8c5038SJames Wright minmax_values_loc[1] = values_local[num_values_local - 1]; 13047b8c5038SJames Wright } else { 13057b8c5038SJames Wright // Rank has no set label values, therefore set local min/max to have no effect on global min/max 13067b8c5038SJames Wright minmax_values_loc[0] = PETSC_INT_MAX; 13077b8c5038SJames Wright minmax_values_loc[1] = PETSC_INT_MIN; 13087b8c5038SJames Wright } 13097b8c5038SJames Wright PetscCall(PetscGlobalMinMaxInt(comm, minmax_values_loc, minmax_values)); 13107b8c5038SJames Wright 13117b8c5038SJames Wright PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 16, &seg)); 13127b8c5038SJames Wright for (PetscInt value = minmax_values[0]; value <= minmax_values[1]; value++) { 13137b8c5038SJames Wright PetscInt location_local = -1, location_global = -1; 13147b8c5038SJames Wright 13157b8c5038SJames Wright PetscCall(PetscFindInt(value, num_values_local, values_local, &location_local)); 13167b8c5038SJames Wright PetscCallMPI(MPIU_Allreduce(&location_local, &location_global, 1, MPIU_INT, MPI_MAX, comm)); 13177b8c5038SJames Wright if (location_global < 0) continue; 13187b8c5038SJames Wright else { 13197b8c5038SJames Wright PetscInt *seg_slot; 13207b8c5038SJames Wright 13217b8c5038SJames Wright PetscCall(PetscSegBufferGetInts(seg, 1, &seg_slot)); 13227b8c5038SJames Wright *seg_slot = value; 13237b8c5038SJames Wright } 13247b8c5038SJames Wright } 13257b8c5038SJames Wright PetscCall(PetscSegBufferGetSize(seg, &seg_size)); 13267b8c5038SJames Wright *num_values = (PetscInt)seg_size; 13277b8c5038SJames Wright PetscCall(PetscSegBufferExtractAlloc(seg, value_array)); 13287b8c5038SJames Wright PetscCall(PetscSegBufferDestroy(&seg)); 13297b8c5038SJames Wright if (values_local) PetscCall(PetscFree(values_local)); 13307b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 13317b8c5038SJames Wright } 13327b8c5038SJames Wright 13337b8c5038SJames Wright /** 13347b8c5038SJames Wright @brief Get the number of components for `dm`'s coordinate field 13357b8c5038SJames Wright 13367b8c5038SJames Wright @param[in] dm DM 13377b8c5038SJames Wright @param[out] num_comp Number of components for the coordinate field 13387b8c5038SJames Wright **/ 13397b8c5038SJames Wright PetscErrorCode DMGetCoordinateNumComps(DM dm, PetscInt *num_comp) { 13407b8c5038SJames Wright DM dm_coord; 13417b8c5038SJames Wright PetscSection section_coord; 13427b8c5038SJames Wright PetscInt field = 0; // Default field has the coordinates 13437b8c5038SJames Wright 13447b8c5038SJames Wright PetscFunctionBeginUser; 13457b8c5038SJames Wright PetscCall(DMGetCoordinateDM(dm, &dm_coord)); 13467b8c5038SJames Wright PetscCall(DMGetLocalSection(dm_coord, §ion_coord)); 13477b8c5038SJames Wright PetscCall(PetscSectionGetFieldComponents(section_coord, field, num_comp)); 13487b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 13497b8c5038SJames Wright } 13507b8c5038SJames Wright 13517b8c5038SJames Wright /** 13527b8c5038SJames Wright @brief Get the number of components for `dm`'s field 13537b8c5038SJames Wright 13547b8c5038SJames Wright @param[in] dm DM 13557b8c5038SJames Wright @param[in] field The field ID to get the number of components for 13567b8c5038SJames Wright @param[out] num_comp Number of components for the coordinate field 13577b8c5038SJames Wright **/ 13587b8c5038SJames Wright PetscErrorCode DMGetFieldNumComps(DM dm, PetscInt field, PetscInt *num_comp) { 13597b8c5038SJames Wright PetscSection section; 13607b8c5038SJames Wright 13617b8c5038SJames Wright PetscFunctionBeginUser; 13627b8c5038SJames Wright PetscCall(DMGetLocalSection(dm, §ion)); 13637b8c5038SJames Wright PetscCall(PetscSectionGetFieldComponents(section, field, num_comp)); 13647b8c5038SJames Wright PetscFunctionReturn(PETSC_SUCCESS); 13657b8c5038SJames Wright } 1366