xref: /petsc/src/dm/impls/swarm/tests/ex1.c (revision 461fcd32ce65a6fe0be6c08577c1de963aca7d28)
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