1*461fcd32Sjosephpu static const char help[] = "Test initialization and migration with swarm."; 2*461fcd32Sjosephpu 3*461fcd32Sjosephpu #include <petscdmplex.h> 4*461fcd32Sjosephpu #include <petscdmswarm.h> 5*461fcd32Sjosephpu 6*461fcd32Sjosephpu typedef struct { 7*461fcd32Sjosephpu PetscReal L[3]; /* Dimensions of the mesh bounding box */ 8*461fcd32Sjosephpu } AppCtx; 9*461fcd32Sjosephpu 10*461fcd32Sjosephpu static PetscErrorCode ProcessOptions(MPI_Comm comm, AppCtx *options) 11*461fcd32Sjosephpu { 12*461fcd32Sjosephpu PetscFunctionBeginUser; 13*461fcd32Sjosephpu PetscOptionsBegin(comm, "", "Swarm configuration options", "DMSWARM"); 14*461fcd32Sjosephpu PetscOptionsEnd(); 15*461fcd32Sjosephpu PetscFunctionReturn(PETSC_SUCCESS); 16*461fcd32Sjosephpu } 17*461fcd32Sjosephpu 18*461fcd32Sjosephpu static PetscErrorCode CreateMesh(MPI_Comm comm, DM *dm, AppCtx *user) 19*461fcd32Sjosephpu { 20*461fcd32Sjosephpu PetscReal low[3], high[3]; 21*461fcd32Sjosephpu PetscInt cdim, d; 22*461fcd32Sjosephpu 23*461fcd32Sjosephpu PetscFunctionBeginUser; 24*461fcd32Sjosephpu PetscCall(DMCreate(comm, dm)); 25*461fcd32Sjosephpu PetscCall(DMSetType(*dm, DMPLEX)); 26*461fcd32Sjosephpu PetscCall(DMSetFromOptions(*dm)); 27*461fcd32Sjosephpu PetscCall(DMViewFromOptions(*dm, NULL, "-dm_view")); 28*461fcd32Sjosephpu PetscCall(DMGetCoordinateDim(*dm, &cdim)); 29*461fcd32Sjosephpu PetscCall(DMGetBoundingBox(*dm, low, high)); 30*461fcd32Sjosephpu PetscPrintf(PETSC_COMM_WORLD, "dim: %" PetscInt_FMT "\n", cdim); 31*461fcd32Sjosephpu for (d = 0; d < cdim; ++d) user->L[d] = high[d] - low[d]; 32*461fcd32Sjosephpu PetscFunctionReturn(PETSC_SUCCESS); 33*461fcd32Sjosephpu } 34*461fcd32Sjosephpu 35*461fcd32Sjosephpu /* 36*461fcd32Sjosephpu This function initializes all particles on rank 0. 37*461fcd32Sjosephpu They are sent to other ranks to test migration across non nearest neighbors 38*461fcd32Sjosephpu */ 39*461fcd32Sjosephpu static PetscErrorCode CreateSwarm(DM dm, DM *sw, AppCtx *user) 40*461fcd32Sjosephpu { 41*461fcd32Sjosephpu PetscInt particleInitSize = 10; 42*461fcd32Sjosephpu PetscReal *coords, upper[3], lower[3]; 43*461fcd32Sjosephpu PetscInt *cellid, rank, size, Np, d, dim; 44*461fcd32Sjosephpu MPI_Comm comm; 45*461fcd32Sjosephpu 46*461fcd32Sjosephpu PetscFunctionBegin; 47*461fcd32Sjosephpu comm = PETSC_COMM_WORLD; 48*461fcd32Sjosephpu MPI_Comm_rank(comm, &rank); 49*461fcd32Sjosephpu MPI_Comm_size(comm, &size); 50*461fcd32Sjosephpu PetscCall(DMGetBoundingBox(dm, lower, upper)); 51*461fcd32Sjosephpu PetscCall(DMCreate(PETSC_COMM_WORLD, sw)); 52*461fcd32Sjosephpu PetscCall(DMGetDimension(dm, &dim)); 53*461fcd32Sjosephpu PetscCall(DMSetType(*sw, DMSWARM)); 54*461fcd32Sjosephpu PetscCall(DMSetDimension(*sw, dim)); 55*461fcd32Sjosephpu PetscCall(DMSwarmSetType(*sw, DMSWARM_PIC)); 56*461fcd32Sjosephpu PetscCall(DMSwarmSetCellDM(*sw, dm)); 57*461fcd32Sjosephpu PetscCall(DMSwarmFinalizeFieldRegister(*sw)); 58*461fcd32Sjosephpu PetscCall(DMSwarmSetLocalSizes(*sw, rank == 0 ? particleInitSize : 0, 0)); 59*461fcd32Sjosephpu PetscCall(DMSetFromOptions(*sw)); 60*461fcd32Sjosephpu PetscCall(DMSwarmGetField(*sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords)); 61*461fcd32Sjosephpu PetscCall(DMSwarmGetField(*sw, DMSwarmPICField_cellid, NULL, NULL, (void **)&cellid)); 62*461fcd32Sjosephpu PetscCall(DMSwarmGetLocalSize(*sw, &Np)); 63*461fcd32Sjosephpu for (PetscInt p = 0; p < Np; ++p) { 64*461fcd32Sjosephpu for (PetscInt d = 0; d < dim; ++d) { coords[p * dim + d] = 0.5 * (upper[d] - lower[d]); } 65*461fcd32Sjosephpu coords[p * dim + 1] = (upper[1] - lower[1]) / particleInitSize * p + lower[1]; 66*461fcd32Sjosephpu cellid[p] = 0; 67*461fcd32Sjosephpu } 68*461fcd32Sjosephpu PetscCall(DMSwarmRestoreField(*sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords)); 69*461fcd32Sjosephpu PetscCall(DMSwarmRestoreField(*sw, DMSwarmPICField_cellid, NULL, NULL, (void **)&cellid)); 70*461fcd32Sjosephpu PetscCall(DMViewFromOptions(*sw, NULL, "-sw_view")); 71*461fcd32Sjosephpu PetscFunctionReturn(PETSC_SUCCESS); 72*461fcd32Sjosephpu } 73*461fcd32Sjosephpu 74*461fcd32Sjosephpu /* 75*461fcd32Sjosephpu Configure the swarm on rank 0 with all particles 76*461fcd32Sjosephpu located there, then migrate where they need to be 77*461fcd32Sjosephpu */ 78*461fcd32Sjosephpu static PetscErrorCode CheckMigrate(DM sw) 79*461fcd32Sjosephpu { 80*461fcd32Sjosephpu Vec preMigrate, postMigrate, tmp; 81*461fcd32Sjosephpu PetscInt preSize, postSize; 82*461fcd32Sjosephpu PetscReal prenorm, postnorm; 83*461fcd32Sjosephpu 84*461fcd32Sjosephpu PetscFunctionBeginUser; 85*461fcd32Sjosephpu PetscCall(DMSwarmCreateGlobalVectorFromField(sw, DMSwarmPICField_coor, &tmp)); 86*461fcd32Sjosephpu PetscCall(VecDuplicate(tmp, &preMigrate)); 87*461fcd32Sjosephpu PetscCall(VecCopy(tmp, preMigrate)); 88*461fcd32Sjosephpu PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, DMSwarmPICField_coor, &tmp)); 89*461fcd32Sjosephpu PetscCall(DMSwarmMigrate(sw, PETSC_TRUE)); 90*461fcd32Sjosephpu PetscCall(DMSwarmCreateGlobalVectorFromField(sw, DMSwarmPICField_coor, &tmp)); 91*461fcd32Sjosephpu PetscCall(VecDuplicate(tmp, &postMigrate)); 92*461fcd32Sjosephpu PetscCall(VecCopy(tmp, postMigrate)); 93*461fcd32Sjosephpu PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, DMSwarmPICField_coor, &tmp)); 94*461fcd32Sjosephpu PetscCall(VecGetSize(preMigrate, &preSize)); 95*461fcd32Sjosephpu PetscCall(VecGetSize(postMigrate, &postSize)); 96*461fcd32Sjosephpu PetscCheck(preSize == postSize, PETSC_COMM_WORLD, PETSC_ERR_ARG_SIZ, "Particles either lost or duplicated. Pre migrate global size %" PetscInt_FMT " != Post migrate size %" PetscInt_FMT "", preSize, postSize); 97*461fcd32Sjosephpu PetscCall(VecNorm(preMigrate, NORM_2, &prenorm)); 98*461fcd32Sjosephpu PetscCall(VecNorm(postMigrate, NORM_2, &postnorm)); 99*461fcd32Sjosephpu PetscCheck(PetscAbsReal(prenorm - postnorm) < PETSC_MACHINE_EPSILON, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Particle coordinates corrupted in migrate with abs(norm(pre) - norm(post)) = %.16g", PetscAbsReal(prenorm - postnorm)); 100*461fcd32Sjosephpu PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Migrate check passes.\n")); 101*461fcd32Sjosephpu PetscCall(VecDestroy(&preMigrate)); 102*461fcd32Sjosephpu PetscCall(VecDestroy(&postMigrate)); 103*461fcd32Sjosephpu PetscFunctionReturn(PETSC_SUCCESS); 104*461fcd32Sjosephpu } 105*461fcd32Sjosephpu 106*461fcd32Sjosephpu /* 107*461fcd32Sjosephpu Checks added points persist on migrate 108*461fcd32Sjosephpu */ 109*461fcd32Sjosephpu static PetscErrorCode CheckPointInsertion(DM sw) 110*461fcd32Sjosephpu { 111*461fcd32Sjosephpu PetscInt rank, size, Np_pre, Np_post; 112*461fcd32Sjosephpu MPI_Comm comm; 113*461fcd32Sjosephpu 114*461fcd32Sjosephpu PetscFunctionBeginUser; 115*461fcd32Sjosephpu comm = PETSC_COMM_WORLD; 116*461fcd32Sjosephpu MPI_Comm_rank(comm, &rank); 117*461fcd32Sjosephpu MPI_Comm_size(comm, &size); 118*461fcd32Sjosephpu PetscCall(PetscPrintf(comm, "Basic point insertion check...\n")); 119*461fcd32Sjosephpu PetscCall(DMSwarmGetSize(sw, &Np_pre)); 120*461fcd32Sjosephpu if (rank == 0) PetscCall(DMSwarmAddPoint(sw)); 121*461fcd32Sjosephpu PetscCall(DMSwarmGetSize(sw, &Np_post)); 122*461fcd32Sjosephpu PetscCheck(Np_post == (Np_pre + 1), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Particle insertion failed. Global size pre insertion: %" PetscInt_FMT " global size post insertion: %" PetscInt_FMT, Np_pre, Np_post); 123*461fcd32Sjosephpu PetscCall(CheckMigrate(sw)); 124*461fcd32Sjosephpu PetscCall(PetscPrintf(comm, "Basic point insertion check passes.\n")); 125*461fcd32Sjosephpu PetscFunctionReturn(PETSC_SUCCESS); 126*461fcd32Sjosephpu } 127*461fcd32Sjosephpu 128*461fcd32Sjosephpu /* 129*461fcd32Sjosephpu Checks tie breaking works properly when a particle 130*461fcd32Sjosephpu is located at a shared boundary. The higher rank should 131*461fcd32Sjosephpu recieve the particle while the lower rank deletes it. 132*461fcd32Sjosephpu 133*461fcd32Sjosephpu TODO: Currently only works for 2 procs. 134*461fcd32Sjosephpu */ 135*461fcd32Sjosephpu static PetscErrorCode CheckPointInsertion_Boundary(DM sw) 136*461fcd32Sjosephpu { 137*461fcd32Sjosephpu PetscInt rank, size, Np_loc_pre, Np_loc_post, dim; 138*461fcd32Sjosephpu PetscReal lbox_low[3], lbox_high[3], gbox_low[3], gbox_high[3]; 139*461fcd32Sjosephpu MPI_Comm comm; 140*461fcd32Sjosephpu DM cdm; 141*461fcd32Sjosephpu 142*461fcd32Sjosephpu PetscFunctionBeginUser; 143*461fcd32Sjosephpu comm = PETSC_COMM_WORLD; 144*461fcd32Sjosephpu MPI_Comm_rank(comm, &rank); 145*461fcd32Sjosephpu MPI_Comm_size(comm, &size); 146*461fcd32Sjosephpu PetscCall(PetscPrintf(comm, "Rank boundary point insertion check...\n")); 147*461fcd32Sjosephpu PetscCall(DMSwarmGetCellDM(sw, &cdm)); 148*461fcd32Sjosephpu PetscCall(DMGetDimension(cdm, &dim)); 149*461fcd32Sjosephpu PetscCall(DMGetBoundingBox(cdm, gbox_low, gbox_high)); 150*461fcd32Sjosephpu if (rank == 0) { 151*461fcd32Sjosephpu PetscReal *coords; 152*461fcd32Sjosephpu PetscInt adjacentdim, Np; 153*461fcd32Sjosephpu 154*461fcd32Sjosephpu PetscCall(DMGetLocalBoundingBox(cdm, lbox_low, lbox_high)); 155*461fcd32Sjosephpu // find a face that belongs to the neighbor. 156*461fcd32Sjosephpu for (PetscInt d = 0; d < dim; ++d) { 157*461fcd32Sjosephpu if ((gbox_high[d] - lbox_high[d]) != 0.) adjacentdim = d; 158*461fcd32Sjosephpu } 159*461fcd32Sjosephpu PetscCall(DMSwarmAddPoint(sw)); 160*461fcd32Sjosephpu PetscCall(DMSwarmGetLocalSize(sw, &Np)); 161*461fcd32Sjosephpu PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords)); 162*461fcd32Sjosephpu for (PetscInt d = 0; d < dim; ++d) coords[(Np - 1) * dim + d] = 0.5 * (lbox_high[d] - lbox_low[d]); 163*461fcd32Sjosephpu coords[(Np - 1) * dim + adjacentdim] = lbox_high[adjacentdim]; 164*461fcd32Sjosephpu PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords)); 165*461fcd32Sjosephpu } 166*461fcd32Sjosephpu PetscCall(DMSwarmGetLocalSize(sw, &Np_loc_pre)); 167*461fcd32Sjosephpu PetscCall(CheckMigrate(sw)); 168*461fcd32Sjosephpu PetscCall(DMSwarmGetLocalSize(sw, &Np_loc_post)); 169*461fcd32Sjosephpu if (rank == 0) PetscCheck(Np_loc_pre == (Np_loc_post + 1), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Migration tie breaking failed on rank %" PetscInt_FMT ". Particle on boundary not sent.", rank); 170*461fcd32Sjosephpu if (rank == 1) PetscCheck(Np_loc_pre == (Np_loc_post - 1), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Migration tie breaking failed on rank %" PetscInt_FMT ". Particle on boundary not recieved.", rank); 171*461fcd32Sjosephpu PetscCall(PetscPrintf(comm, "Rank boundary point insertion check passes.\n")); 172*461fcd32Sjosephpu PetscFunctionReturn(PETSC_SUCCESS); 173*461fcd32Sjosephpu } 174*461fcd32Sjosephpu 175*461fcd32Sjosephpu int main(int argc, char **argv) 176*461fcd32Sjosephpu { 177*461fcd32Sjosephpu DM dm, sw; 178*461fcd32Sjosephpu PetscInt rank, size; 179*461fcd32Sjosephpu MPI_Comm comm; 180*461fcd32Sjosephpu AppCtx user; 181*461fcd32Sjosephpu 182*461fcd32Sjosephpu PetscCall(PetscInitialize(&argc, &argv, NULL, help)); 183*461fcd32Sjosephpu comm = PETSC_COMM_WORLD; 184*461fcd32Sjosephpu PetscCall(ProcessOptions(comm, &user)); 185*461fcd32Sjosephpu PetscCall(CreateMesh(comm, &dm, &user)); 186*461fcd32Sjosephpu PetscCall(CreateSwarm(dm, &sw, &user)); 187*461fcd32Sjosephpu PetscCall(CheckMigrate(sw)); 188*461fcd32Sjosephpu PetscCall(CheckPointInsertion(sw)); 189*461fcd32Sjosephpu PetscCall(CheckPointInsertion_Boundary(sw)); 190*461fcd32Sjosephpu PetscCall(DMDestroy(&sw)); 191*461fcd32Sjosephpu PetscCall(DMDestroy(&dm)); 192*461fcd32Sjosephpu PetscFinalize(); 193*461fcd32Sjosephpu return 0; 194*461fcd32Sjosephpu } 195*461fcd32Sjosephpu 196*461fcd32Sjosephpu /*TEST 197*461fcd32Sjosephpu # Swarm does not handle complex or quad 198*461fcd32Sjosephpu build: 199*461fcd32Sjosephpu requires: !complex double 200*461fcd32Sjosephpu 201*461fcd32Sjosephpu test: 202*461fcd32Sjosephpu suffix: swarm_migrate_hash 203*461fcd32Sjosephpu requires: ctetgen 204*461fcd32Sjosephpu nsize: 2 205*461fcd32Sjosephpu args: -dm_plex_simplex 0 -dm_distribute_overlap 1 -dm_plex_hash_location true -dm_plex_box_faces 10,10,10 -dm_plex_box_lower 0.,0.,0. -dm_plex_box_upper 1.,1.,10. -dm_plex_box_bd none,none,none -dm_plex_dim 3 206*461fcd32Sjosephpu filter: grep -v marker | grep -v atomic | grep -v usage 207*461fcd32Sjosephpu test: 208*461fcd32Sjosephpu suffix: swarm_migrate_scan 209*461fcd32Sjosephpu requires: ctetgen 210*461fcd32Sjosephpu nsize: 2 211*461fcd32Sjosephpu args: -dm_plex_simplex 0 -dm_distribute_overlap 1 -dm_plex_hash_location false -dm_plex_box_faces 10,10,10 -dm_plex_box_lower 0.,0.,0. -dm_plex_box_upper 1.,1.,10. -dm_plex_box_bd none,none,none -dm_plex_dim 3 212*461fcd32Sjosephpu filter: grep -v marker | grep -v atomic | grep -v usage 213*461fcd32Sjosephpu TEST*/ 214