1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at 2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights 3 // reserved. See files LICENSE and NOTICE for details. 4 // 5 // This file is part of CEED, a collection of benchmarks, miniapps, software 6 // libraries and APIs for efficient high-order finite element and spectral 7 // element discretizations for exascale applications. For more information and 8 // source code availability see http://github.com/ceed. 9 // 10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC, 11 // a collaborative effort of two U.S. Department of Energy organizations (Office 12 // of Science and the National Nuclear Security Administration) responsible for 13 // the planning and preparation of a capable exascale ecosystem, including 14 // software, applications, hardware, advanced system engineering and early 15 // testbed platforms, in support of the nation's exascale computing imperative. 16 17 // libCEED + PETSc Example: Surface Area 18 // 19 // This example demonstrates a simple usage of libCEED with PETSc to calculate 20 // the surface area of a simple closed surface, such as the one of a cube 21 // via the mass operator. 22 // 23 // The code uses higher level communication protocols in DMPlex. 24 // 25 // Build with: 26 // 27 // make area [PETSC_DIR=</path/to/petsc>] [CEED_DIR=</path/to/libceed>] 28 // 29 // Sample runs: 30 // Sequential: 31 // 32 // area -petscspace_degree 3 33 // 34 // In parallel: 35 // 36 // mpiexec -n 4 area -petscspace_degree 3 37 // 38 //TESTARGS -ceed {ceed_resource} -test -petscspace_degree 3 39 40 /// @file 41 /// libCEED example using the mass operator to compute surface area using PETSc with DMPlex 42 static const char help[] = 43 "Compute surface area of a cube using DMPlex in PETSc\n"; 44 45 #include <string.h> 46 #include <petscdmplex.h> 47 #include <ceed.h> 48 #include "qfunctions/area/area.h" 49 50 // Auxiliary function to define CEED restrictions from DMPlex data 51 static int CreateRestrictionPlex(Ceed ceed, CeedInt P, CeedInt ncomp, 52 CeedElemRestriction *Erestrict, DM dm) { 53 PetscInt ierr; 54 PetscInt c, cStart, cEnd, nelem, nnodes, *erestrict, eoffset; 55 PetscSection section; 56 Vec Uloc; 57 58 PetscFunctionBegin; 59 60 // Get Nelem 61 ierr = DMGetSection(dm, §ion); CHKERRQ(ierr); 62 ierr = DMPlexGetHeightStratum(dm, 0, &cStart,& cEnd); CHKERRQ(ierr); 63 nelem = cEnd - cStart; 64 65 // Get indices 66 ierr = PetscMalloc1(nelem*P*P, &erestrict); CHKERRQ(ierr); 67 for (c=cStart, eoffset = 0; c<cEnd; c++) { 68 PetscInt numindices, *indices, i; 69 ierr = DMPlexGetClosureIndices(dm, section, section, c, &numindices, 70 &indices, NULL); CHKERRQ(ierr); 71 for (i=0; i<numindices; i+=ncomp) { 72 for (PetscInt j=0; j<ncomp; j++) { 73 if (indices[i+j] != indices[i] + (PetscInt)(copysign(j, indices[i]))) 74 SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, 75 "Cell %D closure indices not interlaced", c); 76 } 77 // NO BC on closed surfaces 78 PetscInt loc = indices[i]; 79 erestrict[eoffset++] = loc/ncomp; 80 } 81 ierr = DMPlexRestoreClosureIndices(dm, section, section, c, &numindices, 82 &indices, NULL); CHKERRQ(ierr); 83 } 84 85 // Setup CEED restriction 86 ierr = DMGetLocalVector(dm, &Uloc); CHKERRQ(ierr); 87 ierr = VecGetLocalSize(Uloc, &nnodes); CHKERRQ(ierr); 88 89 ierr = DMRestoreLocalVector(dm, &Uloc); CHKERRQ(ierr); 90 CeedElemRestrictionCreate(ceed, nelem, P*P, nnodes/ncomp, ncomp, 91 CEED_MEM_HOST, CEED_COPY_VALUES, erestrict, 92 Erestrict); 93 ierr = PetscFree(erestrict); CHKERRQ(ierr); 94 95 PetscFunctionReturn(0); 96 } 97 98 int main(int argc, char **argv) { 99 PetscInt ierr; 100 MPI_Comm comm; 101 char filename[PETSC_MAX_PATH_LEN], 102 ceedresource[PETSC_MAX_PATH_LEN] = "/cpu/self"; 103 PetscInt lsize, gsize, xlsize, 104 qextra = 1, // default number of extra quadrature points 105 ncompx = 3, // number of components of 3D physical coordinates 106 ncompu = 1, // dimension of field to which apply mass operator 107 topodim = 2, // topological dimension of manifold 108 degree = 3; // default degree for finite element bases 109 PetscBool read_mesh = PETSC_FALSE, 110 test_mode = PETSC_FALSE; 111 PetscSpace sp; 112 PetscFE fe; 113 Vec X, Xloc, V, Vloc; 114 DM dm, dmcoord; 115 Ceed ceed; 116 CeedInt P, Q; 117 118 ierr = PetscInitialize(&argc, &argv, NULL, help); 119 if (ierr) return ierr; 120 comm = PETSC_COMM_WORLD; 121 122 // Read CL options 123 ierr = PetscOptionsBegin(comm, NULL, "CEED surface area problem with PETSc", 124 NULL); 125 CHKERRQ(ierr); 126 ierr = PetscOptionsInt("-qextra", "Number of extra quadrature points", 127 NULL, qextra, &qextra, NULL); CHKERRQ(ierr); 128 ierr = PetscOptionsString("-ceed", "CEED resource specifier", 129 NULL, ceedresource, ceedresource, 130 sizeof(ceedresource), NULL); CHKERRQ(ierr); 131 ierr = PetscOptionsBool("-test", 132 "Testing mode (do not print unless error is large)", 133 NULL, test_mode, &test_mode, NULL); CHKERRQ(ierr); 134 ierr = PetscOptionsString("-mesh", "Read mesh from file", NULL, 135 filename, filename, sizeof(filename), &read_mesh); 136 CHKERRQ(ierr); 137 ierr = PetscOptionsEnd(); CHKERRQ(ierr); 138 139 // Setup DM 140 PetscScalar l = 1.0/PetscSqrtReal(3.0); // half edge of the cube 141 if (read_mesh) { 142 ierr = DMPlexCreateFromFile(PETSC_COMM_WORLD, filename, PETSC_TRUE, &dm); 143 CHKERRQ(ierr); 144 } else { 145 // Create the mesh as a 0-refined sphere. This will create a cubic surface, not a box. 146 PetscBool simplex = PETSC_FALSE; 147 ierr = DMPlexCreateSphereMesh(PETSC_COMM_WORLD, topodim, simplex, &dm); 148 CHKERRQ(ierr); 149 // Set the object name 150 ierr = PetscObjectSetName((PetscObject) dm, "Cube"); CHKERRQ(ierr); 151 // Distribute mesh over processes 152 { 153 DM dmDist = NULL; 154 PetscPartitioner part; 155 156 ierr = DMPlexGetPartitioner(dm, &part); CHKERRQ(ierr); 157 ierr = PetscPartitionerSetFromOptions(part); CHKERRQ(ierr); 158 ierr = DMPlexDistribute(dm, 0, NULL, &dmDist); CHKERRQ(ierr); 159 if (dmDist) { 160 ierr = DMDestroy(&dm); CHKERRQ(ierr); 161 dm = dmDist; 162 } 163 } 164 // View DMPlex via runtime option 165 ierr = DMViewFromOptions(dm, NULL, "-dm_view"); CHKERRQ(ierr); 166 } 167 168 // Create FE 169 ierr = PetscFECreateDefault(PETSC_COMM_SELF, topodim, ncompu, PETSC_FALSE, NULL, 170 PETSC_DETERMINE, &fe); 171 CHKERRQ(ierr); 172 ierr = DMSetFromOptions(dm); CHKERRQ(ierr); 173 ierr = DMAddField(dm, NULL, (PetscObject)fe); CHKERRQ(ierr); 174 ierr = DMCreateDS(dm); CHKERRQ(ierr); 175 ierr = DMPlexSetClosurePermutationTensor(dm, PETSC_DETERMINE, NULL); 176 CHKERRQ(ierr); 177 178 // Get basis space degree 179 ierr = PetscFEGetBasisSpace(fe, &sp); CHKERRQ(ierr); 180 ierr = PetscSpaceGetDegree(sp, °ree, NULL); CHKERRQ(ierr); 181 ierr = PetscFEDestroy(&fe); CHKERRQ(ierr); 182 if (degree < 1) SETERRQ1(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, 183 "-petscspace_degree %D must be at least 1", degree); 184 185 // Create vectors 186 ierr = DMCreateGlobalVector(dm, &X); CHKERRQ(ierr); 187 ierr = VecGetLocalSize(X, &lsize); CHKERRQ(ierr); 188 ierr = VecGetSize(X, &gsize); CHKERRQ(ierr); 189 ierr = DMCreateLocalVector(dm, &Xloc); CHKERRQ(ierr); 190 ierr = VecGetSize(Xloc, &xlsize); CHKERRQ(ierr); 191 ierr = VecDuplicate(X, &V); CHKERRQ(ierr); 192 ierr = VecDuplicate(Xloc, &Vloc); CHKERRQ(ierr); 193 194 // Set up libCEED 195 CeedInit(ceedresource, &ceed); 196 197 // Print summary 198 P = degree + 1; 199 Q = P + qextra; 200 const char *usedresource; 201 CeedGetResource(ceed, &usedresource); 202 if (!test_mode) { 203 ierr = PetscPrintf(comm, 204 "\n-- libCEED + PETSc Surface Area problem --\n" 205 " libCEED:\n" 206 " libCEED Backend : %s\n" 207 " Mesh:\n" 208 " Number of 1D Basis Nodes (p) : %d\n" 209 " Number of 1D Quadrature Points (q) : %d\n" 210 " Global nodes : %D\n", 211 usedresource, P, Q, gsize/ncompu); 212 CHKERRQ(ierr); 213 } 214 215 // Setup libCEED's objects 216 // Create CEED operators and API objects they need 217 CeedOperator op_setupgeo, op_apply; 218 CeedQFunction qf_setupgeo, qf_apply; 219 CeedBasis basisx, basisu; 220 CeedElemRestriction Erestrictx, Erestrictu, Erestrictxi, 221 Erestrictqdi; 222 223 // Create bases 224 CeedBasisCreateTensorH1Lagrange(ceed, topodim, ncompu, P, Q, 225 CEED_GAUSS, &basisu); 226 CeedBasisCreateTensorH1Lagrange(ceed, topodim, ncompx, 2, Q, 227 CEED_GAUSS, &basisx); 228 229 // CEED restrictions 230 ierr = DMGetCoordinateDM(dm, &dmcoord); CHKERRQ(ierr); 231 ierr = DMPlexSetClosurePermutationTensor(dmcoord, PETSC_DETERMINE, NULL); 232 CHKERRQ(ierr); 233 234 CreateRestrictionPlex(ceed, 2, ncompx, &Erestrictx, dmcoord); CHKERRQ(ierr); 235 CreateRestrictionPlex(ceed, P, ncompu, &Erestrictu, dm); CHKERRQ(ierr); 236 237 CeedInt cStart, cEnd; 238 ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd); CHKERRQ(ierr); 239 const CeedInt nelem = cEnd - cStart; 240 241 // CEED identity restrictions 242 const CeedInt qdatasize = 1; 243 CeedElemRestrictionCreateIdentity(ceed, nelem, Q*Q, nelem*Q*Q, 244 qdatasize, &Erestrictqdi); 245 CeedElemRestrictionCreateIdentity(ceed, nelem, Q*Q, nelem*Q*Q, 1, 246 &Erestrictxi); 247 248 // Element coordinates 249 Vec coords; 250 const PetscScalar *coordArray; 251 PetscSection section; 252 ierr = DMGetCoordinatesLocal(dm, &coords); CHKERRQ(ierr); 253 ierr = VecGetArrayRead(coords, &coordArray); CHKERRQ(ierr); 254 ierr = DMGetSection(dmcoord, §ion); CHKERRQ(ierr); 255 256 CeedVector xcoord; 257 CeedElemRestrictionCreateVector(Erestrictx, &xcoord, NULL); 258 CeedVectorSetArray(xcoord, CEED_MEM_HOST, CEED_COPY_VALUES, 259 (PetscScalar *)coordArray); 260 ierr = VecRestoreArrayRead(coords, &coordArray); 261 262 // Create the vectors that will be needed in setup and apply 263 CeedVector uceed, vceed, qdata; 264 CeedInt nqpts; 265 CeedBasisGetNumQuadraturePoints(basisu, &nqpts); 266 CeedVectorCreate(ceed, qdatasize*nelem*nqpts, &qdata); 267 CeedVectorCreate(ceed, xlsize, &uceed); 268 CeedVectorCreate(ceed, xlsize, &vceed); 269 270 /* Create the Q-function that builds the operator for the geomteric factors 271 (i.e., the quadrature data) */ 272 CeedQFunctionCreateInterior(ceed, 1, SetupMassGeo, 273 SetupMassGeo_loc, &qf_setupgeo); 274 CeedQFunctionAddInput(qf_setupgeo, "dx", ncompx*topodim, CEED_EVAL_GRAD); 275 CeedQFunctionAddInput(qf_setupgeo, "weight", 1, CEED_EVAL_WEIGHT); 276 CeedQFunctionAddOutput(qf_setupgeo, "qdata", qdatasize, CEED_EVAL_NONE); 277 278 // Set up the mass operator 279 CeedQFunctionCreateInterior(ceed, 1, Mass, Mass_loc, &qf_apply); 280 CeedQFunctionAddInput(qf_apply, "u", ncompu, CEED_EVAL_INTERP); 281 CeedQFunctionAddInput(qf_apply, "qdata", qdatasize, CEED_EVAL_NONE); 282 CeedQFunctionAddOutput(qf_apply, "v", ncompu, CEED_EVAL_INTERP); 283 284 // Create the operator that builds the quadrature data for the operator 285 CeedOperatorCreate(ceed, qf_setupgeo, NULL, NULL, &op_setupgeo); 286 CeedOperatorSetField(op_setupgeo, "dx", Erestrictx, CEED_TRANSPOSE, 287 basisx, CEED_VECTOR_ACTIVE); 288 CeedOperatorSetField(op_setupgeo, "weight", Erestrictxi, CEED_NOTRANSPOSE, 289 basisx, CEED_VECTOR_NONE); 290 CeedOperatorSetField(op_setupgeo, "qdata", Erestrictqdi, CEED_NOTRANSPOSE, 291 CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE); 292 293 // Create the mass operator 294 CeedOperatorCreate(ceed, qf_apply, NULL, NULL, &op_apply); 295 CeedOperatorSetField(op_apply, "u", Erestrictu, CEED_TRANSPOSE, 296 basisu, CEED_VECTOR_ACTIVE); 297 CeedOperatorSetField(op_apply, "qdata", Erestrictqdi, CEED_NOTRANSPOSE, 298 CEED_BASIS_COLLOCATED, qdata); 299 CeedOperatorSetField(op_apply, "v", Erestrictu, CEED_TRANSPOSE, 300 basisu, CEED_VECTOR_ACTIVE); 301 302 // Compute the quadrature data for the mass operator 303 CeedOperatorApply(op_setupgeo, xcoord, qdata, CEED_REQUEST_IMMEDIATE); 304 305 PetscScalar *v; 306 ierr = VecZeroEntries(Vloc); CHKERRQ(ierr); 307 ierr = VecGetArray(Vloc, &v); 308 CeedVectorSetArray(vceed, CEED_MEM_HOST, CEED_USE_POINTER, v); 309 310 // Compute the mesh volume using the mass operator: vol = 1^T \cdot M \cdot 1 311 if (!test_mode) { 312 ierr = PetscPrintf(comm, 313 "Computing the mesh volume using the formula: vol = 1^T M 1\n"); 314 CHKERRQ(ierr); 315 } 316 317 // Initialize u and v with ones 318 CeedVectorSetValue(uceed, 1.0); 319 CeedVectorSetValue(vceed, 1.0); 320 321 // Apply the mass operator: 'u' -> 'v' 322 CeedOperatorApply(op_apply, uceed, vceed, CEED_REQUEST_IMMEDIATE); 323 CeedVectorSyncArray(vceed, CEED_MEM_HOST); 324 325 // Gather output vector 326 ierr = VecRestoreArray(Vloc, &v); CHKERRQ(ierr); 327 ierr = VecZeroEntries(V); CHKERRQ(ierr); 328 ierr = DMLocalToGlobalBegin(dm, Vloc, ADD_VALUES, V); CHKERRQ(ierr); 329 ierr = DMLocalToGlobalEnd(dm, Vloc, ADD_VALUES, V); CHKERRQ(ierr); 330 331 // Compute and print the sum of the entries of 'v' giving the mesh surface area 332 PetscScalar area; 333 ierr = VecSum(V, &area); CHKERRQ(ierr); 334 335 // Compute the exact surface area and print the result 336 CeedScalar exact_surfarea = 6 * (2*l) * (2*l); 337 if (!test_mode) { 338 ierr = PetscPrintf(comm, "Exact mesh surface area : % .14g\n", 339 exact_surfarea); CHKERRQ(ierr); 340 ierr = PetscPrintf(comm, "Computed mesh surface area : % .14g\n", area); 341 CHKERRQ(ierr); 342 ierr = PetscPrintf(comm, "Area error : % .14g\n", 343 fabs(area - exact_surfarea)); CHKERRQ(ierr); 344 } 345 346 // PETSc cleanup 347 ierr = DMDestroy(&dm); CHKERRQ(ierr); 348 ierr = VecDestroy(&X); CHKERRQ(ierr); 349 ierr = VecDestroy(&Xloc); CHKERRQ(ierr); 350 ierr = VecDestroy(&V); CHKERRQ(ierr); 351 ierr = VecDestroy(&Vloc); CHKERRQ(ierr); 352 353 // libCEED cleanup 354 CeedQFunctionDestroy(&qf_setupgeo); 355 CeedOperatorDestroy(&op_setupgeo); 356 CeedVectorDestroy(&xcoord); 357 CeedVectorDestroy(&uceed); 358 CeedVectorDestroy(&vceed); 359 CeedVectorDestroy(&qdata); 360 CeedBasisDestroy(&basisx); 361 CeedBasisDestroy(&basisu); 362 CeedElemRestrictionDestroy(&Erestrictu); 363 CeedElemRestrictionDestroy(&Erestrictx); 364 CeedElemRestrictionDestroy(&Erestrictxi); 365 CeedElemRestrictionDestroy(&Erestrictqdi); 366 CeedQFunctionDestroy(&qf_apply); 367 CeedOperatorDestroy(&op_apply); 368 CeedDestroy(&ceed); 369 return PetscFinalize(); 370 } 371