1 #include <petscdm.h> 2 #include <petscdmda.h> 3 #include <petscdmplex.h> 4 #include <petscdmswarm.h> 5 #include <petsc/private/dmswarmimpl.h> 6 7 int sort_CompareSwarmPoint(const void *dataA, const void *dataB) { 8 SwarmPoint *pointA = (SwarmPoint *)dataA; 9 SwarmPoint *pointB = (SwarmPoint *)dataB; 10 11 if (pointA->cell_index < pointB->cell_index) { 12 return -1; 13 } else if (pointA->cell_index > pointB->cell_index) { 14 return 1; 15 } else { 16 return 0; 17 } 18 } 19 20 PetscErrorCode DMSwarmSortApplyCellIndexSort(DMSwarmSort ctx) { 21 PetscFunctionBegin; 22 qsort(ctx->list, ctx->npoints, sizeof(SwarmPoint), sort_CompareSwarmPoint); 23 PetscFunctionReturn(0); 24 } 25 26 PetscErrorCode DMSwarmSortCreate(DMSwarmSort *_ctx) { 27 DMSwarmSort ctx; 28 29 PetscFunctionBegin; 30 PetscCall(PetscNew(&ctx)); 31 ctx->isvalid = PETSC_FALSE; 32 ctx->ncells = 0; 33 ctx->npoints = 0; 34 PetscCall(PetscMalloc1(1, &ctx->pcell_offsets)); 35 PetscCall(PetscMalloc1(1, &ctx->list)); 36 *_ctx = ctx; 37 PetscFunctionReturn(0); 38 } 39 40 PetscErrorCode DMSwarmSortSetup(DMSwarmSort ctx, DM dm, PetscInt ncells) { 41 PetscInt *swarm_cellid; 42 PetscInt p, npoints; 43 PetscInt tmp, c, count; 44 45 PetscFunctionBegin; 46 if (!ctx) PetscFunctionReturn(0); 47 if (ctx->isvalid) PetscFunctionReturn(0); 48 49 PetscCall(PetscLogEventBegin(DMSWARM_Sort, 0, 0, 0, 0)); 50 /* check the number of cells */ 51 if (ncells != ctx->ncells) { 52 PetscCall(PetscRealloc(sizeof(PetscInt) * (ncells + 1), &ctx->pcell_offsets)); 53 ctx->ncells = ncells; 54 } 55 PetscCall(PetscArrayzero(ctx->pcell_offsets, ctx->ncells + 1)); 56 57 /* get the number of points */ 58 PetscCall(DMSwarmGetLocalSize(dm, &npoints)); 59 if (npoints != ctx->npoints) { 60 PetscCall(PetscRealloc(sizeof(SwarmPoint) * npoints, &ctx->list)); 61 ctx->npoints = npoints; 62 } 63 PetscCall(PetscArrayzero(ctx->list, npoints)); 64 65 PetscCall(DMSwarmGetField(dm, DMSwarmPICField_cellid, NULL, NULL, (void **)&swarm_cellid)); 66 for (p = 0; p < ctx->npoints; p++) { 67 ctx->list[p].point_index = p; 68 ctx->list[p].cell_index = swarm_cellid[p]; 69 } 70 PetscCall(DMSwarmRestoreField(dm, DMSwarmPICField_cellid, NULL, NULL, (void **)&swarm_cellid)); 71 PetscCall(DMSwarmSortApplyCellIndexSort(ctx)); 72 73 /* sum points per cell */ 74 for (p = 0; p < ctx->npoints; p++) { ctx->pcell_offsets[ctx->list[p].cell_index]++; } 75 76 /* create offset list */ 77 count = 0; 78 for (c = 0; c < ctx->ncells; c++) { 79 tmp = ctx->pcell_offsets[c]; 80 ctx->pcell_offsets[c] = count; 81 count = count + tmp; 82 } 83 ctx->pcell_offsets[c] = count; 84 85 ctx->isvalid = PETSC_TRUE; 86 PetscCall(PetscLogEventEnd(DMSWARM_Sort, 0, 0, 0, 0)); 87 PetscFunctionReturn(0); 88 } 89 90 PetscErrorCode DMSwarmSortDestroy(DMSwarmSort *_ctx) { 91 DMSwarmSort ctx; 92 93 PetscFunctionBegin; 94 if (!_ctx) PetscFunctionReturn(0); 95 if (!*_ctx) PetscFunctionReturn(0); 96 ctx = *_ctx; 97 if (ctx->list) PetscCall(PetscFree(ctx->list)); 98 if (ctx->pcell_offsets) PetscCall(PetscFree(ctx->pcell_offsets)); 99 PetscCall(PetscFree(ctx)); 100 *_ctx = NULL; 101 PetscFunctionReturn(0); 102 } 103 104 /*@C 105 DMSwarmSortGetNumberOfPointsPerCell - Returns the number of points in a cell 106 107 Not collective 108 109 Input parameters: 110 + dm - a DMSwarm objects 111 . e - the index of the cell 112 - npoints - the number of points in the cell 113 114 Level: advanced 115 116 Notes: 117 You must call DMSwarmSortGetAccess() before you can call DMSwarmSortGetNumberOfPointsPerCell() 118 119 .seealso: `DMSwarmSetType()`, `DMSwarmSortGetAccess()`, `DMSwarmSortGetPointsPerCell()` 120 @*/ 121 PetscErrorCode DMSwarmSortGetNumberOfPointsPerCell(DM dm, PetscInt e, PetscInt *npoints) { 122 DM_Swarm *swarm = (DM_Swarm *)dm->data; 123 PetscInt points_per_cell; 124 DMSwarmSort ctx; 125 126 PetscFunctionBegin; 127 ctx = swarm->sort_context; 128 PetscCheck(ctx, PetscObjectComm((PetscObject)dm), PETSC_ERR_USER, "The DMSwarmSort context has not been created. Must call DMSwarmSortGetAccess() first"); 129 PetscCheck(ctx->isvalid, PETSC_COMM_SELF, PETSC_ERR_USER, "SwarmPointSort container is not valid. Must call DMSwarmSortGetAccess() first"); 130 PetscCheck(e < ctx->ncells, PETSC_COMM_SELF, PETSC_ERR_USER, "Cell index (%" PetscInt_FMT ") is greater than max number of local cells (%" PetscInt_FMT ")", e, ctx->ncells); 131 PetscCheck(e >= 0, PETSC_COMM_SELF, PETSC_ERR_USER, "Cell index (%" PetscInt_FMT ") cannot be negative", e); 132 points_per_cell = ctx->pcell_offsets[e + 1] - ctx->pcell_offsets[e]; 133 *npoints = points_per_cell; 134 PetscFunctionReturn(0); 135 } 136 137 /*@C 138 DMSwarmSortGetPointsPerCell - Creates an array of point indices for all points in a cell 139 140 Not collective 141 142 Input parameters: 143 + dm - a DMSwarm object 144 . e - the index of the cell 145 . npoints - the number of points in the cell 146 - pidlist - array of the indices indentifying all points in cell e 147 148 Level: advanced 149 150 Notes: 151 You must call DMSwarmSortGetAccess() before you can call DMSwarmSortGetPointsPerCell() 152 153 The array pidlist is internally created and must be free'd by the user 154 155 .seealso: `DMSwarmSetType()`, `DMSwarmSortGetAccess()`, `DMSwarmSortGetNumberOfPointsPerCell()` 156 @*/ 157 PETSC_EXTERN PetscErrorCode DMSwarmSortGetPointsPerCell(DM dm, PetscInt e, PetscInt *npoints, PetscInt **pidlist) { 158 DM_Swarm *swarm = (DM_Swarm *)dm->data; 159 PetscInt points_per_cell; 160 PetscInt p, pid, pid_unsorted; 161 PetscInt *plist; 162 DMSwarmSort ctx; 163 164 PetscFunctionBegin; 165 ctx = swarm->sort_context; 166 PetscCheck(ctx, PetscObjectComm((PetscObject)dm), PETSC_ERR_USER, "The DMSwarmSort context has not been created. Must call DMSwarmSortGetAccess() first"); 167 PetscCall(DMSwarmSortGetNumberOfPointsPerCell(dm, e, &points_per_cell)); 168 PetscCall(PetscMalloc1(points_per_cell, &plist)); 169 for (p = 0; p < points_per_cell; p++) { 170 pid = ctx->pcell_offsets[e] + p; 171 pid_unsorted = ctx->list[pid].point_index; 172 plist[p] = pid_unsorted; 173 } 174 *npoints = points_per_cell; 175 *pidlist = plist; 176 177 PetscFunctionReturn(0); 178 } 179 180 /*@C 181 DMSwarmSortGetAccess - Setups up a DMSwarm point sort context for efficient traversal of points within a cell 182 183 Not collective 184 185 Input parameter: 186 . dm - a DMSwarm object 187 188 Calling DMSwarmSortGetAccess() creates a list which enables easy identification of all points contained in a 189 given cell. This method does not explicitly sort the data within the DMSwarm based on the cell index associated 190 with a DMSwarm point. 191 192 The sort context is valid only for the DMSwarm points defined at the time when DMSwarmSortGetAccess() was called. 193 For example, suppose the swarm contained NP points when DMSwarmSortGetAccess() was called. If the user subsequently 194 adds 10 additional points to the swarm, the sort context is still valid, but only for the first NP points. 195 The indices associated with the 10 new additional points will not be contained within the sort context. 196 This means that the user can still safely perform queries via DMSwarmSortGetPointsPerCell() and 197 DMSwarmSortGetPointsPerCell(), however the results return will be based on the first NP points. 198 199 If any DMSwam re-sizing method is called after DMSwarmSortGetAccess() which modifies any of the first NP entries 200 in the DMSwarm, the sort context will become invalid. Currently there are no guards to prevent the user from 201 invalidating the sort context. For this reason, we highly recommend you do not use DMSwarmRemovePointAtIndex() in 202 between calls to DMSwarmSortGetAccess() and DMSwarmSortRestoreAccess(). 203 204 To facilitate safe removal of points using the sort context, we suggest a "two pass" strategy in which the 205 first pass "marks" points for removal, and the second pass actually removes the points from the DMSwarm. 206 207 Notes: 208 You must call DMSwarmSortGetAccess() before you can call DMSwarmSortGetPointsPerCell() or DMSwarmSortGetNumberOfPointsPerCell() 209 210 The sort context may become invalid if any re-sizing methods are applied which alter the first NP points 211 within swarm at the time DMSwarmSortGetAccess() was called. 212 213 You must call DMSwarmSortRestoreAccess() when you no longer need access to the sort context 214 215 Level: advanced 216 217 .seealso: `DMSwarmSetType()`, `DMSwarmSortRestoreAccess()` 218 @*/ 219 PETSC_EXTERN PetscErrorCode DMSwarmSortGetAccess(DM dm) { 220 DM_Swarm *swarm = (DM_Swarm *)dm->data; 221 PetscInt ncells; 222 DM celldm; 223 PetscBool isda, isplex, isshell; 224 225 PetscFunctionBegin; 226 if (!swarm->sort_context) { PetscCall(DMSwarmSortCreate(&swarm->sort_context)); } 227 228 /* get the number of cells */ 229 PetscCall(DMSwarmGetCellDM(dm, &celldm)); 230 PetscCall(PetscObjectTypeCompare((PetscObject)celldm, DMDA, &isda)); 231 PetscCall(PetscObjectTypeCompare((PetscObject)celldm, DMPLEX, &isplex)); 232 PetscCall(PetscObjectTypeCompare((PetscObject)celldm, DMSHELL, &isshell)); 233 ncells = 0; 234 if (isda) { 235 PetscInt nel, npe; 236 const PetscInt *element; 237 238 PetscCall(DMDAGetElements(celldm, &nel, &npe, &element)); 239 ncells = nel; 240 PetscCall(DMDARestoreElements(celldm, &nel, &npe, &element)); 241 } else if (isplex) { 242 PetscInt ps, pe; 243 244 PetscCall(DMPlexGetHeightStratum(celldm, 0, &ps, &pe)); 245 ncells = pe - ps; 246 } else if (isshell) { 247 PetscErrorCode (*method_DMShellGetNumberOfCells)(DM, PetscInt *); 248 249 PetscCall(PetscObjectQueryFunction((PetscObject)celldm, "DMGetNumberOfCells_C", &method_DMShellGetNumberOfCells)); 250 if (method_DMShellGetNumberOfCells) { 251 PetscCall(method_DMShellGetNumberOfCells(celldm, &ncells)); 252 } else 253 SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot determine the number of cells for the DMSHELL object. User must provide a method via PetscObjectComposeFunction( (PetscObject)shelldm, \"DMGetNumberOfCells_C\", your_function_to_compute_number_of_cells);"); 254 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot determine the number of cells for a DM not of type DA, PLEX or SHELL"); 255 256 /* setup */ 257 PetscCall(DMSwarmSortSetup(swarm->sort_context, dm, ncells)); 258 PetscFunctionReturn(0); 259 } 260 261 /*@C 262 DMSwarmSortRestoreAccess - Invalidates the DMSwarm point sorting context 263 264 Not collective 265 266 Input parameter: 267 . dm - a DMSwarm object 268 269 Level: advanced 270 271 Note: 272 You must call DMSwarmSortGetAccess() before calling DMSwarmSortRestoreAccess() 273 274 .seealso: `DMSwarmSetType()`, `DMSwarmSortGetAccess()` 275 @*/ 276 PETSC_EXTERN PetscErrorCode DMSwarmSortRestoreAccess(DM dm) { 277 DM_Swarm *swarm = (DM_Swarm *)dm->data; 278 279 PetscFunctionBegin; 280 if (!swarm->sort_context) PetscFunctionReturn(0); 281 PetscCheck(swarm->sort_context->isvalid, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "You must call DMSwarmSortGetAccess() before calling DMSwarmSortRestoreAccess()"); 282 swarm->sort_context->isvalid = PETSC_FALSE; 283 PetscFunctionReturn(0); 284 } 285 286 /*@C 287 DMSwarmSortGetIsValid - Gets the isvalid flag associated with a DMSwarm point sorting context 288 289 Not collective 290 291 Input parameter: 292 . dm - a DMSwarm object 293 294 Output parameter: 295 . isvalid - flag indicating whether the sort context is up-to-date 296 297 Level: advanced 298 299 .seealso: `DMSwarmSetType()`, `DMSwarmSortGetAccess()` 300 @*/ 301 PETSC_EXTERN PetscErrorCode DMSwarmSortGetIsValid(DM dm, PetscBool *isvalid) { 302 DM_Swarm *swarm = (DM_Swarm *)dm->data; 303 304 PetscFunctionBegin; 305 if (!swarm->sort_context) { 306 *isvalid = PETSC_FALSE; 307 PetscFunctionReturn(0); 308 } 309 *isvalid = swarm->sort_context->isvalid; 310 PetscFunctionReturn(0); 311 } 312 313 /*@C 314 DMSwarmSortGetSizes - Gets the sizes associated with a DMSwarm point sorting context 315 316 Not collective 317 318 Input parameter: 319 . dm - a DMSwarm object 320 321 Output parameters: 322 + ncells - number of cells within the sort context (pass NULL to ignore) 323 - npoints - number of points used to create the sort context (pass NULL to ignore) 324 325 Level: advanced 326 327 .seealso: `DMSwarmSetType()`, `DMSwarmSortGetAccess()` 328 @*/ 329 PETSC_EXTERN PetscErrorCode DMSwarmSortGetSizes(DM dm, PetscInt *ncells, PetscInt *npoints) { 330 DM_Swarm *swarm = (DM_Swarm *)dm->data; 331 332 PetscFunctionBegin; 333 if (!swarm->sort_context) { 334 if (ncells) *ncells = 0; 335 if (npoints) *npoints = 0; 336 PetscFunctionReturn(0); 337 } 338 if (ncells) *ncells = swarm->sort_context->ncells; 339 if (npoints) *npoints = swarm->sort_context->npoints; 340 PetscFunctionReturn(0); 341 } 342