1ee25b4d0Sjosephpu static const char help[] = "Test initialization and migration with swarm.\n"; 2461fcd32Sjosephpu 3461fcd32Sjosephpu #include <petscdmplex.h> 4461fcd32Sjosephpu #include <petscdmswarm.h> 5461fcd32Sjosephpu 6461fcd32Sjosephpu typedef struct { 7461fcd32Sjosephpu PetscReal L[3]; /* Dimensions of the mesh bounding box */ 88ea0bac9SZach Atkins PetscBool setClosurePermutation; 9461fcd32Sjosephpu } AppCtx; 10461fcd32Sjosephpu 11461fcd32Sjosephpu static PetscErrorCode ProcessOptions(MPI_Comm comm, AppCtx *options) 12461fcd32Sjosephpu { 13461fcd32Sjosephpu PetscFunctionBeginUser; 14461fcd32Sjosephpu PetscOptionsBegin(comm, "", "Swarm configuration options", "DMSWARM"); 158ea0bac9SZach Atkins options->setClosurePermutation = PETSC_FALSE; 168ea0bac9SZach Atkins PetscCall(PetscOptionsBool("-set_closure_permutation", "Set the closure permutation to tensor ordering", NULL, options->setClosurePermutation, &options->setClosurePermutation, NULL)); 17461fcd32Sjosephpu PetscOptionsEnd(); 18461fcd32Sjosephpu PetscFunctionReturn(PETSC_SUCCESS); 19461fcd32Sjosephpu } 20461fcd32Sjosephpu 21461fcd32Sjosephpu static PetscErrorCode CreateMesh(MPI_Comm comm, DM *dm, AppCtx *user) 22461fcd32Sjosephpu { 23461fcd32Sjosephpu PetscReal low[3], high[3]; 24461fcd32Sjosephpu PetscInt cdim, d; 25461fcd32Sjosephpu 26461fcd32Sjosephpu PetscFunctionBeginUser; 27461fcd32Sjosephpu PetscCall(DMCreate(comm, dm)); 28461fcd32Sjosephpu PetscCall(DMSetType(*dm, DMPLEX)); 29461fcd32Sjosephpu PetscCall(DMSetFromOptions(*dm)); 30461fcd32Sjosephpu PetscCall(DMViewFromOptions(*dm, NULL, "-dm_view")); 318ea0bac9SZach Atkins if (user->setClosurePermutation) { 328ea0bac9SZach Atkins DM cdm; 338ea0bac9SZach Atkins 348ea0bac9SZach Atkins // -- Set tensor permutation 358ea0bac9SZach Atkins PetscCall(DMGetCoordinateDM(*dm, &cdm)); 368ea0bac9SZach Atkins PetscCall(DMPlexSetClosurePermutationTensor(*dm, PETSC_DETERMINE, NULL)); 378ea0bac9SZach Atkins PetscCall(DMPlexSetClosurePermutationTensor(cdm, PETSC_DETERMINE, NULL)); 388ea0bac9SZach Atkins } 39461fcd32Sjosephpu PetscCall(DMGetCoordinateDim(*dm, &cdim)); 40461fcd32Sjosephpu PetscCall(DMGetBoundingBox(*dm, low, high)); 41ee25b4d0Sjosephpu PetscCall(PetscPrintf(PETSC_COMM_WORLD, "dim: %" PetscInt_FMT "\n", cdim)); 42461fcd32Sjosephpu for (d = 0; d < cdim; ++d) user->L[d] = high[d] - low[d]; 43461fcd32Sjosephpu PetscFunctionReturn(PETSC_SUCCESS); 44461fcd32Sjosephpu } 45461fcd32Sjosephpu 46461fcd32Sjosephpu /* 47461fcd32Sjosephpu This function initializes all particles on rank 0. 48461fcd32Sjosephpu They are sent to other ranks to test migration across non nearest neighbors 49461fcd32Sjosephpu */ 50461fcd32Sjosephpu static PetscErrorCode CreateSwarm(DM dm, DM *sw, AppCtx *user) 51461fcd32Sjosephpu { 5219307e5cSMatthew G. Knepley DMSwarmCellDM celldm; 53461fcd32Sjosephpu PetscInt particleInitSize = 10; 54461fcd32Sjosephpu PetscReal *coords, upper[3], lower[3]; 5519307e5cSMatthew G. Knepley PetscInt *swarm_cellid, Np, dim, Nfc; 56ee25b4d0Sjosephpu PetscMPIInt rank, size; 57461fcd32Sjosephpu MPI_Comm comm; 5819307e5cSMatthew G. Knepley const char **coordFields, *cellid; 59461fcd32Sjosephpu 60461fcd32Sjosephpu PetscFunctionBegin; 61461fcd32Sjosephpu comm = PETSC_COMM_WORLD; 62ee25b4d0Sjosephpu PetscCallMPI(MPI_Comm_rank(comm, &rank)); 63ee25b4d0Sjosephpu PetscCallMPI(MPI_Comm_size(comm, &size)); 64461fcd32Sjosephpu PetscCall(DMGetBoundingBox(dm, lower, upper)); 65461fcd32Sjosephpu PetscCall(DMCreate(PETSC_COMM_WORLD, sw)); 66461fcd32Sjosephpu PetscCall(DMGetDimension(dm, &dim)); 67461fcd32Sjosephpu PetscCall(DMSetType(*sw, DMSWARM)); 68461fcd32Sjosephpu PetscCall(DMSetDimension(*sw, dim)); 69461fcd32Sjosephpu PetscCall(DMSwarmSetType(*sw, DMSWARM_PIC)); 70461fcd32Sjosephpu PetscCall(DMSwarmSetCellDM(*sw, dm)); 71461fcd32Sjosephpu PetscCall(DMSwarmFinalizeFieldRegister(*sw)); 72461fcd32Sjosephpu PetscCall(DMSwarmSetLocalSizes(*sw, rank == 0 ? particleInitSize : 0, 0)); 73461fcd32Sjosephpu PetscCall(DMSetFromOptions(*sw)); 7419307e5cSMatthew G. Knepley PetscCall(DMSwarmGetCellDMActive(*sw, &celldm)); 7519307e5cSMatthew G. Knepley PetscCall(DMSwarmCellDMGetCellID(celldm, &cellid)); 7619307e5cSMatthew G. Knepley PetscCall(DMSwarmCellDMGetCoordinateFields(celldm, &Nfc, &coordFields)); 7719307e5cSMatthew G. Knepley PetscCheck(Nfc == 1, PetscObjectComm((PetscObject)sw), PETSC_ERR_SUP, "We only support a single coordinate field right now, not %" PetscInt_FMT, Nfc); 7819307e5cSMatthew G. Knepley PetscCall(DMSwarmGetField(*sw, coordFields[0], NULL, NULL, (void **)&coords)); 7919307e5cSMatthew G. Knepley PetscCall(DMSwarmGetField(*sw, cellid, NULL, NULL, (void **)&swarm_cellid)); 80461fcd32Sjosephpu PetscCall(DMSwarmGetLocalSize(*sw, &Np)); 81461fcd32Sjosephpu for (PetscInt p = 0; p < Np; ++p) { 82*ac530a7eSPierre Jolivet for (PetscInt d = 0; d < dim; ++d) coords[p * dim + d] = 0.5 * (upper[d] - lower[d]); 83461fcd32Sjosephpu coords[p * dim + 1] = (upper[1] - lower[1]) / particleInitSize * p + lower[1]; 8419307e5cSMatthew G. Knepley swarm_cellid[p] = 0; 85461fcd32Sjosephpu } 8619307e5cSMatthew G. Knepley PetscCall(DMSwarmRestoreField(*sw, coordFields[0], NULL, NULL, (void **)&coords)); 8719307e5cSMatthew G. Knepley PetscCall(DMSwarmRestoreField(*sw, cellid, NULL, NULL, (void **)&swarm_cellid)); 88461fcd32Sjosephpu PetscCall(DMViewFromOptions(*sw, NULL, "-sw_view")); 89461fcd32Sjosephpu PetscFunctionReturn(PETSC_SUCCESS); 90461fcd32Sjosephpu } 91461fcd32Sjosephpu 92461fcd32Sjosephpu /* 93461fcd32Sjosephpu Configure the swarm on rank 0 with all particles 94461fcd32Sjosephpu located there, then migrate where they need to be 95461fcd32Sjosephpu */ 96461fcd32Sjosephpu static PetscErrorCode CheckMigrate(DM sw) 97461fcd32Sjosephpu { 98461fcd32Sjosephpu Vec preMigrate, postMigrate, tmp; 99461fcd32Sjosephpu PetscInt preSize, postSize; 100461fcd32Sjosephpu PetscReal prenorm, postnorm; 101461fcd32Sjosephpu 102461fcd32Sjosephpu PetscFunctionBeginUser; 103461fcd32Sjosephpu PetscCall(DMSwarmCreateGlobalVectorFromField(sw, DMSwarmPICField_coor, &tmp)); 104461fcd32Sjosephpu PetscCall(VecDuplicate(tmp, &preMigrate)); 105461fcd32Sjosephpu PetscCall(VecCopy(tmp, preMigrate)); 106461fcd32Sjosephpu PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, DMSwarmPICField_coor, &tmp)); 107461fcd32Sjosephpu PetscCall(DMSwarmMigrate(sw, PETSC_TRUE)); 108461fcd32Sjosephpu PetscCall(DMSwarmCreateGlobalVectorFromField(sw, DMSwarmPICField_coor, &tmp)); 109461fcd32Sjosephpu PetscCall(VecDuplicate(tmp, &postMigrate)); 110461fcd32Sjosephpu PetscCall(VecCopy(tmp, postMigrate)); 111461fcd32Sjosephpu PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, DMSwarmPICField_coor, &tmp)); 112461fcd32Sjosephpu PetscCall(VecGetSize(preMigrate, &preSize)); 113461fcd32Sjosephpu PetscCall(VecGetSize(postMigrate, &postSize)); 114e978a55eSPierre Jolivet 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); 115461fcd32Sjosephpu PetscCall(VecNorm(preMigrate, NORM_2, &prenorm)); 116461fcd32Sjosephpu PetscCall(VecNorm(postMigrate, NORM_2, &postnorm)); 117ee25b4d0Sjosephpu PetscCheck(PetscAbsReal(prenorm - postnorm) < 100. * PETSC_MACHINE_EPSILON, PETSC_COMM_SELF, PETSC_ERR_COR, "Particle coordinates corrupted in migrate with abs(norm(pre) - norm(post)) = %.16g", PetscAbsReal(prenorm - postnorm)); 118461fcd32Sjosephpu PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Migrate check passes.\n")); 119461fcd32Sjosephpu PetscCall(VecDestroy(&preMigrate)); 120461fcd32Sjosephpu PetscCall(VecDestroy(&postMigrate)); 121461fcd32Sjosephpu PetscFunctionReturn(PETSC_SUCCESS); 122461fcd32Sjosephpu } 123461fcd32Sjosephpu 124461fcd32Sjosephpu /* 125461fcd32Sjosephpu Checks added points persist on migrate 126461fcd32Sjosephpu */ 127461fcd32Sjosephpu static PetscErrorCode CheckPointInsertion(DM sw) 128461fcd32Sjosephpu { 129ee25b4d0Sjosephpu PetscInt Np_pre, Np_post; 130ee25b4d0Sjosephpu PetscMPIInt rank, size; 131461fcd32Sjosephpu MPI_Comm comm; 132461fcd32Sjosephpu 133461fcd32Sjosephpu PetscFunctionBeginUser; 134461fcd32Sjosephpu comm = PETSC_COMM_WORLD; 135ee25b4d0Sjosephpu PetscCallMPI(MPI_Comm_rank(comm, &rank)); 136ee25b4d0Sjosephpu PetscCallMPI(MPI_Comm_size(comm, &size)); 137461fcd32Sjosephpu PetscCall(PetscPrintf(comm, "Basic point insertion check...\n")); 138461fcd32Sjosephpu PetscCall(DMSwarmGetSize(sw, &Np_pre)); 139461fcd32Sjosephpu if (rank == 0) PetscCall(DMSwarmAddPoint(sw)); 140461fcd32Sjosephpu PetscCall(DMSwarmGetSize(sw, &Np_post)); 141461fcd32Sjosephpu 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); 142461fcd32Sjosephpu PetscCall(CheckMigrate(sw)); 143461fcd32Sjosephpu PetscCall(PetscPrintf(comm, "Basic point insertion check passes.\n")); 144461fcd32Sjosephpu PetscFunctionReturn(PETSC_SUCCESS); 145461fcd32Sjosephpu } 146461fcd32Sjosephpu 147461fcd32Sjosephpu /* 148461fcd32Sjosephpu Checks tie breaking works properly when a particle 149461fcd32Sjosephpu is located at a shared boundary. The higher rank should 150baca6076SPierre Jolivet receive the particle while the lower rank deletes it. 151461fcd32Sjosephpu 152461fcd32Sjosephpu TODO: Currently only works for 2 procs. 153461fcd32Sjosephpu */ 154461fcd32Sjosephpu static PetscErrorCode CheckPointInsertion_Boundary(DM sw) 155461fcd32Sjosephpu { 156ee25b4d0Sjosephpu PetscInt Np_loc_pre, Np_loc_post, dim; 157ee25b4d0Sjosephpu PetscMPIInt rank, size; 158461fcd32Sjosephpu PetscReal lbox_low[3], lbox_high[3], gbox_low[3], gbox_high[3]; 159461fcd32Sjosephpu MPI_Comm comm; 160461fcd32Sjosephpu DM cdm; 161461fcd32Sjosephpu 162461fcd32Sjosephpu PetscFunctionBeginUser; 163461fcd32Sjosephpu comm = PETSC_COMM_WORLD; 164ee25b4d0Sjosephpu PetscCallMPI(MPI_Comm_rank(comm, &rank)); 165ee25b4d0Sjosephpu PetscCallMPI(MPI_Comm_size(comm, &size)); 166461fcd32Sjosephpu PetscCall(PetscPrintf(comm, "Rank boundary point insertion check...\n")); 167461fcd32Sjosephpu PetscCall(DMSwarmGetCellDM(sw, &cdm)); 168461fcd32Sjosephpu PetscCall(DMGetDimension(cdm, &dim)); 169461fcd32Sjosephpu PetscCall(DMGetBoundingBox(cdm, gbox_low, gbox_high)); 170461fcd32Sjosephpu if (rank == 0) { 171461fcd32Sjosephpu PetscReal *coords; 172ee25b4d0Sjosephpu PetscInt adjacentdim = 0, Np; 173461fcd32Sjosephpu 174461fcd32Sjosephpu PetscCall(DMGetLocalBoundingBox(cdm, lbox_low, lbox_high)); 175461fcd32Sjosephpu // find a face that belongs to the neighbor. 176461fcd32Sjosephpu for (PetscInt d = 0; d < dim; ++d) { 177461fcd32Sjosephpu if ((gbox_high[d] - lbox_high[d]) != 0.) adjacentdim = d; 178461fcd32Sjosephpu } 179461fcd32Sjosephpu PetscCall(DMSwarmAddPoint(sw)); 180461fcd32Sjosephpu PetscCall(DMSwarmGetLocalSize(sw, &Np)); 181461fcd32Sjosephpu PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords)); 182461fcd32Sjosephpu for (PetscInt d = 0; d < dim; ++d) coords[(Np - 1) * dim + d] = 0.5 * (lbox_high[d] - lbox_low[d]); 183461fcd32Sjosephpu coords[(Np - 1) * dim + adjacentdim] = lbox_high[adjacentdim]; 184461fcd32Sjosephpu PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords)); 185461fcd32Sjosephpu } 186461fcd32Sjosephpu PetscCall(DMSwarmGetLocalSize(sw, &Np_loc_pre)); 187461fcd32Sjosephpu PetscCall(CheckMigrate(sw)); 188461fcd32Sjosephpu PetscCall(DMSwarmGetLocalSize(sw, &Np_loc_post)); 189ee25b4d0Sjosephpu if (rank == 0) PetscCheck(Np_loc_pre == (Np_loc_post + 1), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Migration tie breaking failed on rank %d. Particle on boundary not sent.", rank); 190baca6076SPierre Jolivet if (rank == 1) PetscCheck(Np_loc_pre == (Np_loc_post - 1), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Migration tie breaking failed on rank %d. Particle on boundary not received.", rank); 191461fcd32Sjosephpu PetscCall(PetscPrintf(comm, "Rank boundary point insertion check passes.\n")); 192461fcd32Sjosephpu PetscFunctionReturn(PETSC_SUCCESS); 193461fcd32Sjosephpu } 194461fcd32Sjosephpu 195461fcd32Sjosephpu int main(int argc, char **argv) 196461fcd32Sjosephpu { 197461fcd32Sjosephpu DM dm, sw; 198461fcd32Sjosephpu MPI_Comm comm; 199461fcd32Sjosephpu AppCtx user; 200461fcd32Sjosephpu 201461fcd32Sjosephpu PetscCall(PetscInitialize(&argc, &argv, NULL, help)); 202461fcd32Sjosephpu comm = PETSC_COMM_WORLD; 203461fcd32Sjosephpu PetscCall(ProcessOptions(comm, &user)); 204461fcd32Sjosephpu PetscCall(CreateMesh(comm, &dm, &user)); 205461fcd32Sjosephpu PetscCall(CreateSwarm(dm, &sw, &user)); 206461fcd32Sjosephpu PetscCall(CheckMigrate(sw)); 207461fcd32Sjosephpu PetscCall(CheckPointInsertion(sw)); 208461fcd32Sjosephpu PetscCall(CheckPointInsertion_Boundary(sw)); 209461fcd32Sjosephpu PetscCall(DMDestroy(&sw)); 210461fcd32Sjosephpu PetscCall(DMDestroy(&dm)); 211ee25b4d0Sjosephpu PetscCall(PetscFinalize()); 212461fcd32Sjosephpu return 0; 213461fcd32Sjosephpu } 214461fcd32Sjosephpu 215461fcd32Sjosephpu /*TEST 216ee25b4d0Sjosephpu 217461fcd32Sjosephpu # Swarm does not handle complex or quad 218461fcd32Sjosephpu build: 219461fcd32Sjosephpu requires: !complex double 220ee25b4d0Sjosephpu # swarm_migrate_hash and swarm_migrate_scan test swarm migration against point location types 221ee25b4d0Sjosephpu # with a distributed mesh where ranks overlap by 1. Points in the shared boundary should 222ee25b4d0Sjosephpu # be sent to the process which has the highest rank that has that portion of the domain. 223461fcd32Sjosephpu test: 224953fc092SRylanor suffix: swarm_migrate_vec_hip_scan 225953fc092SRylanor nsize: 2 226953fc092SRylanor requires: hip 227953fc092SRylanor args: -dm_plex_dim 3 -dm_plex_simplex 0 -dm_distribute_overlap 1 -dm_plex_box_faces 10,10,10\ 228953fc092SRylanor -dm_plex_box_lower 0.,0.,0. -dm_plex_box_upper 1.,1.,10. -dm_plex_box_bd none,none,none\ 229953fc092SRylanor -dm_plex_hash_location false -dm_vec_type hip -dm_plex_hash_location false 230953fc092SRylanor test: 231953fc092SRylanor suffix: swarm_migrate_vec_hip_hash 232953fc092SRylanor nsize: 2 233953fc092SRylanor requires: hip 234953fc092SRylanor args: -dm_plex_dim 3 -dm_plex_simplex 0 -dm_distribute_overlap 1 -dm_plex_box_faces 10,10,10\ 235953fc092SRylanor -dm_plex_box_lower 0.,0.,0. -dm_plex_box_upper 1.,1.,10. -dm_plex_box_bd none,none,none\ 236953fc092SRylanor -dm_plex_hash_location false -dm_vec_type hip -dm_plex_hash_location true 237953fc092SRylanor test: 238953fc092SRylanor suffix: swarm_migrate_vec_hip_hash_tensor_permutation 239953fc092SRylanor nsize: 2 240953fc092SRylanor requires: hip 241953fc092SRylanor args: -dm_plex_dim 3 -dm_plex_simplex 0 -dm_distribute_overlap 1 -dm_plex_box_faces 10,10,10\ 242953fc092SRylanor -dm_plex_box_lower 0.,0.,0. -dm_plex_box_upper 1.,1.,10. -dm_plex_box_bd none,none,none\ 243953fc092SRylanor -dm_plex_hash_location false -dm_vec_type hip -dm_plex_hash_location true\ 244953fc092SRylanor -set_closure_permutation 245953fc092SRylanor test: 246461fcd32Sjosephpu suffix: swarm_migrate_hash 247461fcd32Sjosephpu nsize: 2 248ee25b4d0Sjosephpu args: -dm_plex_dim 3 -dm_plex_simplex 0 -dm_distribute_overlap 1 -dm_plex_box_faces 10,10,10\ 249ee25b4d0Sjosephpu -dm_plex_box_lower 0.,0.,0. -dm_plex_box_upper 1.,1.,10. -dm_plex_box_bd none,none,none\ 250ee25b4d0Sjosephpu -dm_plex_hash_location true 251461fcd32Sjosephpu filter: grep -v marker | grep -v atomic | grep -v usage 252461fcd32Sjosephpu test: 2538ea0bac9SZach Atkins suffix: swarm_migrate_hash_tensor_permutation 2548ea0bac9SZach Atkins nsize: 2 2558ea0bac9SZach Atkins args: -dm_plex_dim 3 -dm_plex_simplex 0 -dm_distribute_overlap 1 -dm_plex_box_faces 10,10,10\ 2568ea0bac9SZach Atkins -dm_plex_box_lower 0.,0.,0. -dm_plex_box_upper 1.,1.,10. -dm_plex_box_bd none,none,none\ 2578ea0bac9SZach Atkins -dm_plex_hash_location true -set_closure_permutation 2588ea0bac9SZach Atkins filter: grep -v marker | grep -v atomic | grep -v usage 2598ea0bac9SZach Atkins test: 260461fcd32Sjosephpu suffix: swarm_migrate_scan 261461fcd32Sjosephpu nsize: 2 262ee25b4d0Sjosephpu args: -dm_plex_dim 3 -dm_plex_simplex 0 -dm_distribute_overlap 1 -dm_plex_box_faces 10,10,10\ 263ee25b4d0Sjosephpu -dm_plex_box_lower 0.,0.,0. -dm_plex_box_upper 1.,1.,10. -dm_plex_box_bd none,none,none\ 264ee25b4d0Sjosephpu -dm_plex_hash_location false 265461fcd32Sjosephpu filter: grep -v marker | grep -v atomic | grep -v usage 2668ea0bac9SZach Atkins test: 2678ea0bac9SZach Atkins suffix: swarm_migrate_scan_tensor_permutation 2688ea0bac9SZach Atkins nsize: 2 2698ea0bac9SZach Atkins args: -dm_plex_dim 3 -dm_plex_simplex 0 -dm_distribute_overlap 1 -dm_plex_box_faces 10,10,10\ 2708ea0bac9SZach Atkins -dm_plex_box_lower 0.,0.,0. -dm_plex_box_upper 1.,1.,10. -dm_plex_box_bd none,none,none\ 2718ea0bac9SZach Atkins -dm_plex_hash_location false -set_closure_permutation 2728ea0bac9SZach Atkins filter: grep -v marker | grep -v atomic | grep -v usage 273461fcd32Sjosephpu TEST*/ 274