xref: /petsc/src/dm/impls/swarm/swarmpic.c (revision ef46b1a67e276116c83b5d4ce8efc2932ea4fc0a)
1 #define PETSCDM_DLL
2 #include <petsc/private/dmswarmimpl.h>    /*I   "petscdmswarm.h"   I*/
3 #include <petscsf.h>
4 #include <petscdmda.h>
5 #include <petscdmplex.h>
6 #include <petscdt.h>
7 #include "../src/dm/impls/swarm/data_bucket.h"
8 
9 #include <petsc/private/petscfeimpl.h> /* For CoordinatesRefToReal() */
10 
11 /*
12  Error checking to ensure the swarm type is correct and that a cell DM has been set
13 */
14 #define DMSWARMPICVALID(dm) \
15 { \
16   DM_Swarm *_swarm = (DM_Swarm*)(dm)->data; \
17   PetscCheck(_swarm->swarm_type == DMSWARM_PIC,PetscObjectComm((PetscObject)(dm)),PETSC_ERR_SUP,"Valid only for DMSwarm-PIC. You must call DMSwarmSetType(dm,DMSWARM_PIC)"); \
18   else \
19     PetscCheck(_swarm->dmcell,PetscObjectComm((PetscObject)(dm)),PETSC_ERR_SUP,"Valid only for DMSwarmPIC if the cell DM is set. You must call DMSwarmSetCellDM(dm,celldm)"); \
20 }
21 
22 /* Coordinate insertition/addition API */
23 /*@C
24    DMSwarmSetPointsUniformCoordinates - Set point coordinates in a DMSwarm on a regular (ijk) grid
25 
26    Collective on dm
27 
28    Input parameters:
29 +  dm - the DMSwarm
30 .  min - minimum coordinate values in the x, y, z directions (array of length dim)
31 .  max - maximum coordinate values in the x, y, z directions (array of length dim)
32 .  npoints - number of points in each spatial direction (array of length dim)
33 -  mode - indicates whether to append points to the swarm (ADD_VALUES), or over-ride existing points (INSERT_VALUES)
34 
35    Level: beginner
36 
37    Notes:
38    When using mode = INSERT_VALUES, this method will reset the number of particles in the DMSwarm
39    to be npoints[0]*npoints[1] (2D) or npoints[0]*npoints[1]*npoints[2] (3D). When using mode = ADD_VALUES,
40    new points will be appended to any already existing in the DMSwarm
41 
42 .seealso: DMSwarmSetType(), DMSwarmSetCellDM(), DMSwarmType
43 @*/
44 PETSC_EXTERN PetscErrorCode DMSwarmSetPointsUniformCoordinates(DM dm,PetscReal min[],PetscReal max[],PetscInt npoints[],InsertMode mode)
45 {
46   PetscReal         gmin[] = {PETSC_MAX_REAL ,PETSC_MAX_REAL, PETSC_MAX_REAL};
47   PetscReal         gmax[] = {PETSC_MIN_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
48   PetscInt          i,j,k,N,bs,b,n_estimate,n_curr,n_new_est,p,n_found;
49   Vec               coorlocal;
50   const PetscScalar *_coor;
51   DM                celldm;
52   PetscReal         dx[3];
53   PetscInt          _npoints[] = { 0, 0, 1 };
54   Vec               pos;
55   PetscScalar       *_pos;
56   PetscReal         *swarm_coor;
57   PetscInt          *swarm_cellid;
58   PetscSF           sfcell = NULL;
59   const PetscSFNode *LA_sfcell;
60 
61   PetscFunctionBegin;
62   DMSWARMPICVALID(dm);
63   PetscCall(DMSwarmGetCellDM(dm,&celldm));
64   PetscCall(DMGetCoordinatesLocal(celldm,&coorlocal));
65   PetscCall(VecGetSize(coorlocal,&N));
66   PetscCall(VecGetBlockSize(coorlocal,&bs));
67   N = N / bs;
68   PetscCall(VecGetArrayRead(coorlocal,&_coor));
69   for (i=0; i<N; i++) {
70     for (b=0; b<bs; b++) {
71       gmin[b] = PetscMin(gmin[b],PetscRealPart(_coor[bs*i+b]));
72       gmax[b] = PetscMax(gmax[b],PetscRealPart(_coor[bs*i+b]));
73     }
74   }
75   PetscCall(VecRestoreArrayRead(coorlocal,&_coor));
76 
77   for (b=0; b<bs; b++) {
78     if (npoints[b] > 1) {
79       dx[b] = (max[b] - min[b])/((PetscReal)(npoints[b]-1));
80     } else {
81       dx[b] = 0.0;
82     }
83     _npoints[b] = npoints[b];
84   }
85 
86   /* determine number of points living in the bounding box */
87   n_estimate = 0;
88   for (k=0; k<_npoints[2]; k++) {
89     for (j=0; j<_npoints[1]; j++) {
90       for (i=0; i<_npoints[0]; i++) {
91         PetscReal xp[] = {0.0,0.0,0.0};
92         PetscInt ijk[3];
93         PetscBool point_inside = PETSC_TRUE;
94 
95         ijk[0] = i;
96         ijk[1] = j;
97         ijk[2] = k;
98         for (b=0; b<bs; b++) {
99           xp[b] = min[b] + ijk[b] * dx[b];
100         }
101         for (b=0; b<bs; b++) {
102           if (xp[b] < gmin[b]) { point_inside = PETSC_FALSE; }
103           if (xp[b] > gmax[b]) { point_inside = PETSC_FALSE; }
104         }
105         if (point_inside) { n_estimate++; }
106       }
107     }
108   }
109 
110   /* create candidate list */
111   PetscCall(VecCreate(PETSC_COMM_SELF,&pos));
112   PetscCall(VecSetSizes(pos,bs*n_estimate,PETSC_DECIDE));
113   PetscCall(VecSetBlockSize(pos,bs));
114   PetscCall(VecSetFromOptions(pos));
115   PetscCall(VecGetArray(pos,&_pos));
116 
117   n_estimate = 0;
118   for (k=0; k<_npoints[2]; k++) {
119     for (j=0; j<_npoints[1]; j++) {
120       for (i=0; i<_npoints[0]; i++) {
121         PetscReal xp[] = {0.0,0.0,0.0};
122         PetscInt  ijk[3];
123         PetscBool point_inside = PETSC_TRUE;
124 
125         ijk[0] = i;
126         ijk[1] = j;
127         ijk[2] = k;
128         for (b=0; b<bs; b++) {
129           xp[b] = min[b] + ijk[b] * dx[b];
130         }
131         for (b=0; b<bs; b++) {
132           if (xp[b] < gmin[b]) { point_inside = PETSC_FALSE; }
133           if (xp[b] > gmax[b]) { point_inside = PETSC_FALSE; }
134         }
135         if (point_inside) {
136           for (b=0; b<bs; b++) {
137             _pos[bs*n_estimate+b] = xp[b];
138           }
139           n_estimate++;
140         }
141       }
142     }
143   }
144   PetscCall(VecRestoreArray(pos,&_pos));
145 
146   /* locate points */
147   PetscCall(DMLocatePoints(celldm,pos,DM_POINTLOCATION_NONE,&sfcell));
148   PetscCall(PetscSFGetGraph(sfcell, NULL, NULL, NULL, &LA_sfcell));
149   n_found = 0;
150   for (p=0; p<n_estimate; p++) {
151     if (LA_sfcell[p].index != DMLOCATEPOINT_POINT_NOT_FOUND) {
152       n_found++;
153     }
154   }
155 
156   /* adjust size */
157   if (mode == ADD_VALUES) {
158     PetscCall(DMSwarmGetLocalSize(dm,&n_curr));
159     n_new_est = n_curr + n_found;
160     PetscCall(DMSwarmSetLocalSizes(dm,n_new_est,-1));
161   }
162   if (mode == INSERT_VALUES) {
163     n_curr = 0;
164     n_new_est = n_found;
165     PetscCall(DMSwarmSetLocalSizes(dm,n_new_est,-1));
166   }
167 
168   /* initialize new coords, cell owners, pid */
169   PetscCall(VecGetArrayRead(pos,&_coor));
170   PetscCall(DMSwarmGetField(dm,DMSwarmPICField_coor,NULL,NULL,(void**)&swarm_coor));
171   PetscCall(DMSwarmGetField(dm,DMSwarmPICField_cellid,NULL,NULL,(void**)&swarm_cellid));
172   n_found = 0;
173   for (p=0; p<n_estimate; p++) {
174     if (LA_sfcell[p].index != DMLOCATEPOINT_POINT_NOT_FOUND) {
175       for (b=0; b<bs; b++) {
176         swarm_coor[bs*(n_curr + n_found) + b] = PetscRealPart(_coor[bs*p+b]);
177       }
178       swarm_cellid[n_curr + n_found] = LA_sfcell[p].index;
179       n_found++;
180     }
181   }
182   PetscCall(DMSwarmRestoreField(dm,DMSwarmPICField_cellid,NULL,NULL,(void**)&swarm_cellid));
183   PetscCall(DMSwarmRestoreField(dm,DMSwarmPICField_coor,NULL,NULL,(void**)&swarm_coor));
184   PetscCall(VecRestoreArrayRead(pos,&_coor));
185 
186   PetscCall(PetscSFDestroy(&sfcell));
187   PetscCall(VecDestroy(&pos));
188   PetscFunctionReturn(0);
189 }
190 
191 /*@C
192    DMSwarmSetPointCoordinates - Set point coordinates in a DMSwarm from a user defined list
193 
194    Collective on dm
195 
196    Input parameters:
197 +  dm - the DMSwarm
198 .  npoints - the number of points to insert
199 .  coor - the coordinate values
200 .  redundant - if set to PETSC_TRUE, it is assumed that npoints and coor[] are only valid on rank 0 and should be broadcast to other ranks
201 -  mode - indicates whether to append points to the swarm (ADD_VALUES), or over-ride existing points (INSERT_VALUES)
202 
203    Level: beginner
204 
205    Notes:
206    If the user has specified redundant = PETSC_FALSE, the cell DM will attempt to locate the coordinates provided by coor[] within
207    its sub-domain. If they any values within coor[] are not located in the sub-domain, they will be ignored and will not get
208    added to the DMSwarm.
209 
210 .seealso: DMSwarmSetType(), DMSwarmSetCellDM(), DMSwarmType, DMSwarmSetPointsUniformCoordinates()
211 @*/
212 PETSC_EXTERN PetscErrorCode DMSwarmSetPointCoordinates(DM dm,PetscInt npoints,PetscReal coor[],PetscBool redundant,InsertMode mode)
213 {
214   PetscReal         gmin[] = {PETSC_MAX_REAL ,PETSC_MAX_REAL, PETSC_MAX_REAL};
215   PetscReal         gmax[] = {PETSC_MIN_REAL, PETSC_MIN_REAL, PETSC_MIN_REAL};
216   PetscInt          i,N,bs,b,n_estimate,n_curr,n_new_est,p,n_found;
217   Vec               coorlocal;
218   const PetscScalar *_coor;
219   DM                celldm;
220   Vec               pos;
221   PetscScalar       *_pos;
222   PetscReal         *swarm_coor;
223   PetscInt          *swarm_cellid;
224   PetscSF           sfcell = NULL;
225   const PetscSFNode *LA_sfcell;
226   PetscReal         *my_coor;
227   PetscInt          my_npoints;
228   PetscMPIInt       rank;
229   MPI_Comm          comm;
230 
231   PetscFunctionBegin;
232   DMSWARMPICVALID(dm);
233   PetscCall(PetscObjectGetComm((PetscObject)dm,&comm));
234   PetscCallMPI(MPI_Comm_rank(comm,&rank));
235 
236   PetscCall(DMSwarmGetCellDM(dm,&celldm));
237   PetscCall(DMGetCoordinatesLocal(celldm,&coorlocal));
238   PetscCall(VecGetSize(coorlocal,&N));
239   PetscCall(VecGetBlockSize(coorlocal,&bs));
240   N = N / bs;
241   PetscCall(VecGetArrayRead(coorlocal,&_coor));
242   for (i=0; i<N; i++) {
243     for (b=0; b<bs; b++) {
244       gmin[b] = PetscMin(gmin[b],PetscRealPart(_coor[bs*i+b]));
245       gmax[b] = PetscMax(gmax[b],PetscRealPart(_coor[bs*i+b]));
246     }
247   }
248   PetscCall(VecRestoreArrayRead(coorlocal,&_coor));
249 
250   /* broadcast points from rank 0 if requested */
251   if (redundant) {
252     my_npoints = npoints;
253     PetscCallMPI(MPI_Bcast(&my_npoints,1,MPIU_INT,0,comm));
254 
255     if (rank > 0) { /* allocate space */
256       PetscCall(PetscMalloc1(bs*my_npoints,&my_coor));
257     } else {
258       my_coor = coor;
259     }
260     PetscCallMPI(MPI_Bcast(my_coor,bs*my_npoints,MPIU_REAL,0,comm));
261   } else {
262     my_npoints = npoints;
263     my_coor = coor;
264   }
265 
266   /* determine the number of points living in the bounding box */
267   n_estimate = 0;
268   for (i=0; i<my_npoints; i++) {
269     PetscBool point_inside = PETSC_TRUE;
270 
271     for (b=0; b<bs; b++) {
272       if (my_coor[bs*i+b] < gmin[b]) { point_inside = PETSC_FALSE; }
273       if (my_coor[bs*i+b] > gmax[b]) { point_inside = PETSC_FALSE; }
274     }
275     if (point_inside) { n_estimate++; }
276   }
277 
278   /* create candidate list */
279   PetscCall(VecCreate(PETSC_COMM_SELF,&pos));
280   PetscCall(VecSetSizes(pos,bs*n_estimate,PETSC_DECIDE));
281   PetscCall(VecSetBlockSize(pos,bs));
282   PetscCall(VecSetFromOptions(pos));
283   PetscCall(VecGetArray(pos,&_pos));
284 
285   n_estimate = 0;
286   for (i=0; i<my_npoints; i++) {
287     PetscBool point_inside = PETSC_TRUE;
288 
289     for (b=0; b<bs; b++) {
290       if (my_coor[bs*i+b] < gmin[b]) { point_inside = PETSC_FALSE; }
291       if (my_coor[bs*i+b] > gmax[b]) { point_inside = PETSC_FALSE; }
292     }
293     if (point_inside) {
294       for (b=0; b<bs; b++) {
295         _pos[bs*n_estimate+b] = my_coor[bs*i+b];
296       }
297       n_estimate++;
298     }
299   }
300   PetscCall(VecRestoreArray(pos,&_pos));
301 
302   /* locate points */
303   PetscCall(DMLocatePoints(celldm,pos,DM_POINTLOCATION_NONE,&sfcell));
304 
305   PetscCall(PetscSFGetGraph(sfcell, NULL, NULL, NULL, &LA_sfcell));
306   n_found = 0;
307   for (p=0; p<n_estimate; p++) {
308     if (LA_sfcell[p].index != DMLOCATEPOINT_POINT_NOT_FOUND) {
309       n_found++;
310     }
311   }
312 
313   /* adjust size */
314   if (mode == ADD_VALUES) {
315     PetscCall(DMSwarmGetLocalSize(dm,&n_curr));
316     n_new_est = n_curr + n_found;
317     PetscCall(DMSwarmSetLocalSizes(dm,n_new_est,-1));
318   }
319   if (mode == INSERT_VALUES) {
320     n_curr = 0;
321     n_new_est = n_found;
322     PetscCall(DMSwarmSetLocalSizes(dm,n_new_est,-1));
323   }
324 
325   /* initialize new coords, cell owners, pid */
326   PetscCall(VecGetArrayRead(pos,&_coor));
327   PetscCall(DMSwarmGetField(dm,DMSwarmPICField_coor,NULL,NULL,(void**)&swarm_coor));
328   PetscCall(DMSwarmGetField(dm,DMSwarmPICField_cellid,NULL,NULL,(void**)&swarm_cellid));
329   n_found = 0;
330   for (p=0; p<n_estimate; p++) {
331     if (LA_sfcell[p].index != DMLOCATEPOINT_POINT_NOT_FOUND) {
332       for (b=0; b<bs; b++) {
333         swarm_coor[bs*(n_curr + n_found) + b] = PetscRealPart(_coor[bs*p+b]);
334       }
335       swarm_cellid[n_curr + n_found] = LA_sfcell[p].index;
336       n_found++;
337     }
338   }
339   PetscCall(DMSwarmRestoreField(dm,DMSwarmPICField_cellid,NULL,NULL,(void**)&swarm_cellid));
340   PetscCall(DMSwarmRestoreField(dm,DMSwarmPICField_coor,NULL,NULL,(void**)&swarm_coor));
341   PetscCall(VecRestoreArrayRead(pos,&_coor));
342 
343   if (redundant) {
344     if (rank > 0) {
345       PetscCall(PetscFree(my_coor));
346     }
347   }
348   PetscCall(PetscSFDestroy(&sfcell));
349   PetscCall(VecDestroy(&pos));
350   PetscFunctionReturn(0);
351 }
352 
353 extern PetscErrorCode private_DMSwarmInsertPointsUsingCellDM_DA(DM,DM,DMSwarmPICLayoutType,PetscInt);
354 extern PetscErrorCode private_DMSwarmInsertPointsUsingCellDM_PLEX(DM,DM,DMSwarmPICLayoutType,PetscInt);
355 
356 /*@C
357    DMSwarmInsertPointsUsingCellDM - Insert point coordinates within each cell
358 
359    Not collective
360 
361    Input parameters:
362 +  dm - the DMSwarm
363 .  layout_type - method used to fill each cell with the cell DM
364 -  fill_param - parameter controlling how many points per cell are added (the meaning of this parameter is dependent on the layout type)
365 
366    Level: beginner
367 
368    Notes:
369 
370    The insert method will reset any previous defined points within the DMSwarm.
371 
372    When using a DMDA both 2D and 3D are supported for all layout types provided you are using DMDA_ELEMENT_Q1.
373 
374    When using a DMPLEX the following case are supported:
375    (i) DMSWARMPIC_LAYOUT_REGULAR: 2D (triangle),
376    (ii) DMSWARMPIC_LAYOUT_GAUSS: 2D and 3D provided the cell is a tri/tet or a quad/hex,
377    (iii) DMSWARMPIC_LAYOUT_SUBDIVISION: 2D and 3D for quad/hex and 2D tri.
378 
379 .seealso: DMSwarmPICLayoutType, DMSwarmSetType(), DMSwarmSetCellDM(), DMSwarmType
380 @*/
381 PETSC_EXTERN PetscErrorCode DMSwarmInsertPointsUsingCellDM(DM dm,DMSwarmPICLayoutType layout_type,PetscInt fill_param)
382 {
383   DM             celldm;
384   PetscBool      isDA,isPLEX;
385 
386   PetscFunctionBegin;
387   DMSWARMPICVALID(dm);
388   PetscCall(DMSwarmGetCellDM(dm,&celldm));
389   PetscCall(PetscObjectTypeCompare((PetscObject)celldm,DMDA,&isDA));
390   PetscCall(PetscObjectTypeCompare((PetscObject)celldm,DMPLEX,&isPLEX));
391   if (isDA) {
392     PetscCall(private_DMSwarmInsertPointsUsingCellDM_DA(dm,celldm,layout_type,fill_param));
393   } else if (isPLEX) {
394     PetscCall(private_DMSwarmInsertPointsUsingCellDM_PLEX(dm,celldm,layout_type,fill_param));
395   } else SETERRQ(PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Only supported for cell DMs of type DMDA and DMPLEX");
396   PetscFunctionReturn(0);
397 }
398 
399 extern PetscErrorCode private_DMSwarmSetPointCoordinatesCellwise_PLEX(DM,DM,PetscInt,PetscReal*);
400 
401 /*@C
402    DMSwarmSetPointCoordinatesCellwise - Insert point coordinates (defined over the reference cell) within each cell
403 
404    Not collective
405 
406    Input parameters:
407 +  dm - the DMSwarm
408 .  celldm - the cell DM
409 .  npoints - the number of points to insert in each cell
410 -  xi - the coordinates (defined in the local coordinate system for each cell) to insert
411 
412  Level: beginner
413 
414  Notes:
415  The method will reset any previous defined points within the DMSwarm.
416  Only supported for DMPLEX. If you are using a DMDA it is recommended to either use
417  DMSwarmInsertPointsUsingCellDM(), or extract and set the coordinates yourself the following code
418 
419 $    PetscReal *coor;
420 $    DMSwarmGetField(dm,DMSwarmPICField_coor,NULL,NULL,(void**)&coor);
421 $    // user code to define the coordinates here
422 $    DMSwarmRestoreField(dm,DMSwarmPICField_coor,NULL,NULL,(void**)&coor);
423 
424 .seealso: DMSwarmSetCellDM(), DMSwarmInsertPointsUsingCellDM()
425 @*/
426 PETSC_EXTERN PetscErrorCode DMSwarmSetPointCoordinatesCellwise(DM dm,PetscInt npoints,PetscReal xi[])
427 {
428   DM             celldm;
429   PetscBool      isDA,isPLEX;
430 
431   PetscFunctionBegin;
432   DMSWARMPICVALID(dm);
433   PetscCall(DMSwarmGetCellDM(dm,&celldm));
434   PetscCall(PetscObjectTypeCompare((PetscObject)celldm,DMDA,&isDA));
435   PetscCall(PetscObjectTypeCompare((PetscObject)celldm,DMPLEX,&isPLEX));
436   PetscCheck(!isDA,PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Only supported for cell DMs of type DMPLEX. Recommended you use DMSwarmInsertPointsUsingCellDM()");
437   else if (isPLEX) {
438     PetscCall(private_DMSwarmSetPointCoordinatesCellwise_PLEX(dm,celldm,npoints,xi));
439   } else SETERRQ(PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Only supported for cell DMs of type DMDA and DMPLEX");
440   PetscFunctionReturn(0);
441 }
442 
443 /* Field projection API */
444 extern PetscErrorCode private_DMSwarmProjectFields_DA(DM swarm,DM celldm,PetscInt project_type,PetscInt nfields,DMSwarmDataField dfield[],Vec vecs[]);
445 extern PetscErrorCode private_DMSwarmProjectFields_PLEX(DM swarm,DM celldm,PetscInt project_type,PetscInt nfields,DMSwarmDataField dfield[],Vec vecs[]);
446 
447 /*@C
448    DMSwarmProjectFields - Project a set of swarm fields onto the cell DM
449 
450    Collective on dm
451 
452    Input parameters:
453 +  dm - the DMSwarm
454 .  nfields - the number of swarm fields to project
455 .  fieldnames - the textual names of the swarm fields to project
456 .  fields - an array of Vec's of length nfields
457 -  reuse - flag indicating whether the array and contents of fields should be re-used or internally allocated
458 
459    Currently, the only available projection method consists of
460      phi_i = \sum_{p=0}^{np} N_i(x_p) phi_p dJ / \sum_{p=0}^{np} N_i(x_p) dJ
461    where phi_p is the swarm field at point p,
462      N_i() is the cell DM basis function at vertex i,
463      dJ is the determinant of the cell Jacobian and
464      phi_i is the projected vertex value of the field phi.
465 
466    Level: beginner
467 
468    Notes:
469 
470    If reuse = PETSC_FALSE, this function will allocate the array of Vec's, and each individual Vec.
471      The user is responsible for destroying both the array and the individual Vec objects.
472 
473    Only swarm fields registered with data type = PETSC_REAL can be projected onto the cell DM.
474 
475    Only swarm fields of block size = 1 can currently be projected.
476 
477    The only projection methods currently only support the DA (2D) and PLEX (triangles 2D).
478 
479 .seealso: DMSwarmSetType(), DMSwarmSetCellDM(), DMSwarmType
480 @*/
481 PETSC_EXTERN PetscErrorCode DMSwarmProjectFields(DM dm,PetscInt nfields,const char *fieldnames[],Vec **fields,PetscBool reuse)
482 {
483   DM_Swarm         *swarm = (DM_Swarm*)dm->data;
484   DMSwarmDataField *gfield;
485   DM               celldm;
486   PetscBool        isDA,isPLEX;
487   Vec              *vecs;
488   PetscInt         f,nvecs;
489   PetscInt         project_type = 0;
490 
491   PetscFunctionBegin;
492   DMSWARMPICVALID(dm);
493   PetscCall(DMSwarmGetCellDM(dm,&celldm));
494   PetscCall(PetscMalloc1(nfields,&gfield));
495   nvecs = 0;
496   for (f=0; f<nfields; f++) {
497     PetscCall(DMSwarmDataBucketGetDMSwarmDataFieldByName(swarm->db,fieldnames[f],&gfield[f]));
498     PetscCheck(gfield[f]->petsc_type == PETSC_REAL,PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Projection only valid for fields using a data type = PETSC_REAL");
499     PetscCheck(gfield[f]->bs == 1,PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Projection only valid for fields with block size = 1");
500     nvecs += gfield[f]->bs;
501   }
502   if (!reuse) {
503     PetscCall(PetscMalloc1(nvecs,&vecs));
504     for (f=0; f<nvecs; f++) {
505       PetscCall(DMCreateGlobalVector(celldm,&vecs[f]));
506       PetscCall(PetscObjectSetName((PetscObject)vecs[f],gfield[f]->name));
507     }
508   } else {
509     vecs = *fields;
510   }
511 
512   PetscCall(PetscObjectTypeCompare((PetscObject)celldm,DMDA,&isDA));
513   PetscCall(PetscObjectTypeCompare((PetscObject)celldm,DMPLEX,&isPLEX));
514   if (isDA) {
515     PetscCall(private_DMSwarmProjectFields_DA(dm,celldm,project_type,nfields,gfield,vecs));
516   } else if (isPLEX) {
517     PetscCall(private_DMSwarmProjectFields_PLEX(dm,celldm,project_type,nfields,gfield,vecs));
518   } else SETERRQ(PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Only supported for cell DMs of type DMDA and DMPLEX");
519 
520   PetscCall(PetscFree(gfield));
521   if (!reuse) *fields = vecs;
522   PetscFunctionReturn(0);
523 }
524 
525 /*@C
526    DMSwarmCreatePointPerCellCount - Count the number of points within all cells in the cell DM
527 
528    Not collective
529 
530    Input parameter:
531 .  dm - the DMSwarm
532 
533    Output parameters:
534 +  ncells - the number of cells in the cell DM (optional argument, pass NULL to ignore)
535 -  count - array of length ncells containing the number of points per cell
536 
537    Level: beginner
538 
539    Notes:
540    The array count is allocated internally and must be free'd by the user.
541 
542 .seealso: DMSwarmSetType(), DMSwarmSetCellDM(), DMSwarmType
543 @*/
544 PETSC_EXTERN PetscErrorCode DMSwarmCreatePointPerCellCount(DM dm,PetscInt *ncells,PetscInt **count)
545 {
546   PetscBool      isvalid;
547   PetscInt       nel;
548   PetscInt       *sum;
549 
550   PetscFunctionBegin;
551   PetscCall(DMSwarmSortGetIsValid(dm,&isvalid));
552   nel = 0;
553   if (isvalid) {
554     PetscInt e;
555 
556     PetscCall(DMSwarmSortGetSizes(dm,&nel,NULL));
557 
558     PetscCall(PetscMalloc1(nel,&sum));
559     for (e=0; e<nel; e++) {
560       PetscCall(DMSwarmSortGetNumberOfPointsPerCell(dm,e,&sum[e]));
561     }
562   } else {
563     DM        celldm;
564     PetscBool isda,isplex,isshell;
565     PetscInt  p,npoints;
566     PetscInt *swarm_cellid;
567 
568     /* get the number of cells */
569     PetscCall(DMSwarmGetCellDM(dm,&celldm));
570     PetscCall(PetscObjectTypeCompare((PetscObject)celldm,DMDA,&isda));
571     PetscCall(PetscObjectTypeCompare((PetscObject)celldm,DMPLEX,&isplex));
572     PetscCall(PetscObjectTypeCompare((PetscObject)celldm,DMSHELL,&isshell));
573     if (isda) {
574       PetscInt       _nel,_npe;
575       const PetscInt *_element;
576 
577       PetscCall(DMDAGetElements(celldm,&_nel,&_npe,&_element));
578       nel = _nel;
579       PetscCall(DMDARestoreElements(celldm,&_nel,&_npe,&_element));
580     } else if (isplex) {
581       PetscInt ps,pe;
582 
583       PetscCall(DMPlexGetHeightStratum(celldm,0,&ps,&pe));
584       nel = pe - ps;
585     } else if (isshell) {
586       PetscErrorCode (*method_DMShellGetNumberOfCells)(DM,PetscInt*);
587 
588       PetscCall(PetscObjectQueryFunction((PetscObject)celldm,"DMGetNumberOfCells_C",&method_DMShellGetNumberOfCells));
589       if (method_DMShellGetNumberOfCells) {
590         PetscCall(method_DMShellGetNumberOfCells(celldm,&nel));
591       } else 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);");
592     } else SETERRQ(PetscObjectComm((PetscObject)dm),PETSC_ERR_SUP,"Cannot determine the number of cells for a DM not of type DA, PLEX or SHELL");
593 
594     PetscCall(PetscMalloc1(nel,&sum));
595     PetscCall(PetscArrayzero(sum,nel));
596     PetscCall(DMSwarmGetLocalSize(dm,&npoints));
597     PetscCall(DMSwarmGetField(dm,DMSwarmPICField_cellid,NULL,NULL,(void**)&swarm_cellid));
598     for (p=0; p<npoints; p++) {
599       if (swarm_cellid[p] != DMLOCATEPOINT_POINT_NOT_FOUND) {
600         sum[ swarm_cellid[p] ]++;
601       }
602     }
603     PetscCall(DMSwarmRestoreField(dm,DMSwarmPICField_cellid,NULL,NULL,(void**)&swarm_cellid));
604   }
605   if (ncells) { *ncells = nel; }
606   *count  = sum;
607   PetscFunctionReturn(0);
608 }
609 
610 /*@
611   DMSwarmGetNumSpecies - Get the number of particle species
612 
613   Not collective
614 
615   Input parameter:
616 . dm - the DMSwarm
617 
618   Output parameters:
619 . Ns - the number of species
620 
621   Level: intermediate
622 
623 .seealso: DMSwarmSetNumSpecies(), DMSwarmSetType(), DMSwarmType
624 @*/
625 PetscErrorCode DMSwarmGetNumSpecies(DM sw, PetscInt *Ns)
626 {
627   DM_Swarm *swarm = (DM_Swarm *) sw->data;
628 
629   PetscFunctionBegin;
630   *Ns = swarm->Ns;
631   PetscFunctionReturn(0);
632 }
633 
634 /*@
635   DMSwarmSetNumSpecies - Set the number of particle species
636 
637   Not collective
638 
639   Input parameter:
640 + dm - the DMSwarm
641 - Ns - the number of species
642 
643   Level: intermediate
644 
645 .seealso: DMSwarmGetNumSpecies(), DMSwarmSetType(), DMSwarmType
646 @*/
647 PetscErrorCode DMSwarmSetNumSpecies(DM sw, PetscInt Ns)
648 {
649   DM_Swarm *swarm = (DM_Swarm *) sw->data;
650 
651   PetscFunctionBegin;
652   swarm->Ns =  Ns;
653   PetscFunctionReturn(0);
654 }
655 
656 /*@C
657   DMSwarmGetCoordinateFunction - Get the function setting initial particle positions, if it exists
658 
659   Not collective
660 
661   Input parameter:
662 . dm - the DMSwarm
663 
664   Output Parameter:
665 . coordFunc - the function setting initial particle positions, or NULL
666 
667   Level: intermediate
668 
669 .seealso: DMSwarmSetCoordinateFunction(), DMSwarmGetVelocityFunction(), DMSwarmInitializeCoordinates()
670 @*/
671 PetscErrorCode DMSwarmGetCoordinateFunction(DM sw, PetscSimplePointFunc *coordFunc)
672 {
673   DM_Swarm *swarm = (DM_Swarm *) sw->data;
674 
675   PetscFunctionBegin;
676   PetscValidHeaderSpecific(sw, DM_CLASSID, 1);
677   PetscValidPointer(coordFunc, 2);
678   *coordFunc = swarm->coordFunc;
679   PetscFunctionReturn(0);
680 }
681 
682 /*@C
683   DMSwarmSetCoordinateFunction - Set the function setting initial particle positions
684 
685   Not collective
686 
687   Input parameters:
688 + dm - the DMSwarm
689 - coordFunc - the function setting initial particle positions
690 
691   Level: intermediate
692 
693 .seealso: DMSwarmGetCoordinateFunction(), DMSwarmSetVelocityFunction(), DMSwarmInitializeCoordinates()
694 @*/
695 PetscErrorCode DMSwarmSetCoordinateFunction(DM sw, PetscSimplePointFunc coordFunc)
696 {
697   DM_Swarm *swarm = (DM_Swarm *) sw->data;
698 
699   PetscFunctionBegin;
700   PetscValidHeaderSpecific(sw, DM_CLASSID, 1);
701   PetscValidFunction(coordFunc, 2);
702   swarm->coordFunc = coordFunc;
703   PetscFunctionReturn(0);
704 }
705 
706 /*@C
707   DMSwarmGetCoordinateFunction - Get the function setting initial particle velocities, if it exists
708 
709   Not collective
710 
711   Input parameter:
712 . dm - the DMSwarm
713 
714   Output Parameter:
715 . velFunc - the function setting initial particle velocities, or NULL
716 
717   Level: intermediate
718 
719 .seealso: DMSwarmSetVelocityFunction(), DMSwarmGetCoordinateFunction(), DMSwarmInitializeVelocities()
720 @*/
721 PetscErrorCode DMSwarmGetVelocityFunction(DM sw, PetscSimplePointFunc *velFunc)
722 {
723   DM_Swarm *swarm = (DM_Swarm *) sw->data;
724 
725   PetscFunctionBegin;
726   PetscValidHeaderSpecific(sw, DM_CLASSID, 1);
727   PetscValidPointer(velFunc, 2);
728   *velFunc = swarm->velFunc;
729   PetscFunctionReturn(0);
730 }
731 
732 /*@C
733   DMSwarmSetVelocityFunction - Set the function setting initial particle velocities
734 
735   Not collective
736 
737   Input parameters:
738 + dm - the DMSwarm
739 - coordFunc - the function setting initial particle velocities
740 
741   Level: intermediate
742 
743 .seealso: DMSwarmGetVelocityFunction(), DMSwarmSetCoordinateFunction(), DMSwarmInitializeVelocities()
744 @*/
745 PetscErrorCode DMSwarmSetVelocityFunction(DM sw, PetscSimplePointFunc velFunc)
746 {
747   DM_Swarm *swarm = (DM_Swarm *) sw->data;
748 
749   PetscFunctionBegin;
750   PetscValidHeaderSpecific(sw, DM_CLASSID, 1);
751   PetscValidFunction(velFunc, 2);
752   swarm->velFunc = velFunc;
753   PetscFunctionReturn(0);
754 }
755 
756 /*@C
757   DMSwarmComputeLocalSize - Compute the local number and distribution of particles based upon a density function
758 
759   Not collective
760 
761   Input Parameters:
762 + sw      - The DMSwarm
763 . N       - The target number of particles
764 - density - The density field for the particle layout, normalized to unity
765 
766   Note: One particle will be created for each species.
767 
768   Level: advanced
769 
770 .seealso: DMSwarmComputeLocalSizeFromOptions()
771 @*/
772 PetscErrorCode DMSwarmComputeLocalSize(DM sw, PetscInt N, PetscProbFunc density)
773 {
774   DM               dm;
775   PetscQuadrature  quad;
776   const PetscReal *xq, *wq;
777   PetscInt        *npc, *cellid;
778   PetscReal        xi0[3];
779   PetscInt         Ns, cStart, cEnd, c, dim, d, Nq, q, Np = 0, p;
780   PetscBool        simplex;
781 
782   PetscFunctionBegin;
783   PetscCall(DMSwarmGetNumSpecies(sw, &Ns));
784   PetscCall(DMSwarmGetCellDM(sw, &dm));
785   PetscCall(DMGetDimension(dm, &dim));
786   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
787   PetscCall(DMPlexIsSimplex(dm, &simplex));
788   if (simplex) PetscCall(PetscDTStroudConicalQuadrature(dim, 1, 5, -1.0, 1.0, &quad));
789   else         PetscCall(PetscDTGaussTensorQuadrature(dim, 1, 5, -1.0, 1.0, &quad));
790   PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, &xq, &wq));
791   PetscCall(PetscMalloc1(cEnd-cStart, &npc));
792   /* Integrate the density function to get the number of particles in each cell */
793   for (d = 0; d < dim; ++d) xi0[d] = -1.0;
794   for (c = 0; c < cEnd-cStart; ++c) {
795     const PetscInt cell = c + cStart;
796     PetscReal v0[3], J[9], invJ[9], detJ;
797     PetscReal n_int = 0.;
798 
799     PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, NULL, v0, J, invJ, &detJ));
800     for (q = 0; q < Nq; ++q) {
801       PetscReal xr[3], den[3];
802 
803       CoordinatesRefToReal(dim, dim, xi0, v0, J, &xq[q*dim], xr);
804       PetscCall(density(xr, NULL, den));
805       n_int += den[0]*wq[q];
806     }
807     npc[c]  = (PetscInt) (N*n_int);
808     npc[c] *= Ns;
809     Np     += npc[c];
810   }
811   PetscCall(PetscQuadratureDestroy(&quad));
812   PetscCall(DMSwarmSetLocalSizes(sw, Np, 0));
813 
814   PetscCall(DMSwarmGetField(sw, DMSwarmPICField_cellid, NULL, NULL, (void **) &cellid));
815   for (c = 0, p = 0; c < cEnd-cStart; ++c) {
816     for (q = 0; q < npc[c]; ++q, ++p) cellid[p] = c;
817   }
818   PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_cellid, NULL, NULL, (void **) &cellid));
819   PetscCall(PetscFree(npc));
820   PetscFunctionReturn(0);
821 }
822 
823 /*@
824   DMSwarmComputeLocalSizeFromOptions - Compute the local number and distribution of particles based upon a density function determined by options
825 
826   Not collective
827 
828   Input Parameters:
829 , sw - The DMSwarm
830 
831   Level: advanced
832 
833 .seealso: DMSwarmComputeLocalSize()
834 @*/
835 PetscErrorCode DMSwarmComputeLocalSizeFromOptions(DM sw)
836 {
837   PetscProbFunc  pdf;
838   const char    *prefix;
839   char           funcname[PETSC_MAX_PATH_LEN];
840   PetscInt      *N, Ns, dim, n;
841   PetscBool      flg;
842   PetscMPIInt    size, rank;
843 
844   PetscFunctionBegin;
845   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject) sw), &size));
846   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject) sw), &rank));
847   PetscCall(PetscCalloc1(size, &N));
848   PetscOptionsBegin(PetscObjectComm((PetscObject) sw), "", "DMSwarm Options", "DMSWARM");
849   n = size;
850   PetscCall(PetscOptionsIntArray("-dm_swarm_num_particles", "The target number of particles", "", N, &n, NULL));
851   PetscCall(DMSwarmGetNumSpecies(sw, &Ns));
852   PetscCall(PetscOptionsInt("-dm_swarm_num_species", "The number of species", "DMSwarmSetNumSpecies", Ns, &Ns, &flg));
853   if (flg) PetscCall(DMSwarmSetNumSpecies(sw, Ns));
854   PetscCall(PetscOptionsString("-dm_swarm_coordinate_function", "Function to determine particle coordinates", "DMSwarmSetCoordinateFunction", funcname, funcname, sizeof(funcname), &flg));
855   PetscOptionsEnd();
856   if (flg) {
857     PetscSimplePointFunc coordFunc;
858 
859     PetscCall(DMSwarmGetNumSpecies(sw, &Ns));
860     PetscCall(PetscDLSym(NULL, funcname, (void **) &coordFunc));
861     PetscCheck(coordFunc, PetscObjectComm((PetscObject) sw), PETSC_ERR_ARG_WRONG, "Could not locate function %s", funcname);
862     PetscCall(DMSwarmGetNumSpecies(sw, &Ns));
863     PetscCall(DMSwarmSetLocalSizes(sw, N[rank]*Ns, 0));
864     PetscCall(DMSwarmSetCoordinateFunction(sw, coordFunc));
865   } else {
866     PetscCall(DMGetDimension(sw, &dim));
867     PetscCall(PetscObjectGetOptionsPrefix((PetscObject) sw, &prefix));
868     PetscCall(PetscProbCreateFromOptions(dim, prefix, "-dm_swarm_coordinate_density", &pdf, NULL, NULL));
869     PetscCall(DMSwarmComputeLocalSize(sw, N[rank], pdf));
870   }
871   PetscCall(PetscFree(N));
872   PetscFunctionReturn(0);
873 }
874 
875 /*@
876   DMSwarmInitializeCoordinates - Determine the initial coordinates of particles for a PIC method
877 
878   Not collective
879 
880   Input Parameters:
881 , sw - The DMSwarm
882 
883   Note: Currently, we randomly place particles in their assigned cell
884 
885   Level: advanced
886 
887 .seealso: DMSwarmComputeLocalSize(), DMSwarmInitializeVelocities()
888 @*/
889 PetscErrorCode DMSwarmInitializeCoordinates(DM sw)
890 {
891   PetscSimplePointFunc coordFunc;
892   PetscScalar         *weight;
893   PetscReal           *x;
894   PetscInt            *species;
895   void                *ctx;
896   PetscBool            removePoints = PETSC_TRUE;
897   PetscDataType        dtype;
898   PetscInt             Np, p, Ns, dim, d, bs;
899 
900   PetscFunctionBeginUser;
901   PetscCall(DMGetDimension(sw, &dim));
902   PetscCall(DMSwarmGetLocalSize(sw, &Np));
903   PetscCall(DMSwarmGetNumSpecies(sw, &Ns));
904   PetscCall(DMSwarmGetCoordinateFunction(sw, &coordFunc));
905 
906   PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, &bs, &dtype, (void **) &x));
907   PetscCall(DMSwarmGetField(sw, "w_q", &bs, &dtype, (void **) &weight));
908   PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **) &species));
909   if (coordFunc) {
910     PetscCall(DMGetApplicationContext(sw, &ctx));
911     for (p = 0; p < Np; ++p) {
912       PetscScalar X[3];
913 
914       PetscCall((*coordFunc)(dim, 0., NULL, p, X, ctx));
915       for (d = 0; d < dim; ++d) x[p*dim+d] = PetscRealPart(X[d]);
916       weight[p]  = 1.0;
917       species[p] = p % Ns;
918     }
919   } else {
920     DM          dm;
921     PetscRandom rnd;
922     PetscReal   xi0[3];
923     PetscInt    cStart, cEnd, c;
924 
925     PetscCall(DMSwarmGetCellDM(sw, &dm));
926     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
927 
928     /* Set particle position randomly in cell, set weights to 1 */
929     PetscCall(PetscRandomCreate(PetscObjectComm((PetscObject) dm), &rnd));
930     PetscCall(PetscRandomSetInterval(rnd, -1.0, 1.0));
931     PetscCall(PetscRandomSetFromOptions(rnd));
932     PetscCall(DMSwarmSortGetAccess(sw));
933     for (d = 0; d < dim; ++d) xi0[d] = -1.0;
934     for (c = cStart; c < cEnd; ++c) {
935       PetscReal v0[3], J[9], invJ[9], detJ;
936       PetscInt *pidx, Npc, q;
937 
938       PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Npc, &pidx));
939       PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, v0, J, invJ, &detJ));
940       for (q = 0; q < Npc; ++q) {
941         const PetscInt p = pidx[q];
942         PetscReal      xref[3];
943 
944         for (d = 0; d < dim; ++d) PetscCall(PetscRandomGetValueReal(rnd, &xref[d]));
945         CoordinatesRefToReal(dim, dim, xi0, v0, J, xref, &x[p*dim]);
946 
947         weight[p]  = 1.0;
948         species[p] = p % Ns;
949       }
950       PetscCall(PetscFree(pidx));
951     }
952     PetscCall(PetscRandomDestroy(&rnd));
953     PetscCall(DMSwarmSortRestoreAccess(sw));
954   }
955   PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **) &x));
956   PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **) &weight));
957   PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **) &species));
958 
959   PetscCall(DMSwarmMigrate(sw, removePoints));
960   PetscCall(DMLocalizeCoordinates(sw));
961   PetscFunctionReturn(0);
962 }
963 
964 /*@C
965   DMSwarmInitializeVelocities - Set the initial velocities of particles using a distribution.
966 
967   Collective on dm
968 
969   Input Parameters:
970 + sw      - The DMSwarm object
971 . sampler - A function which uniformly samples the velocity PDF
972 - v0      - The velocity scale for nondimensionalization for each species
973 
974   Note: If v0 is zero for the first species, all velocities are set to zero. If it is zero for any other species, the effect will be to give that species zero velocity.
975 
976   Level: advanced
977 
978 .seealso: DMSwarmComputeLocalSize(), DMSwarmInitializeCoordinates(), DMSwarmInitializeVelocitiesFromOptions()
979 @*/
980 PetscErrorCode DMSwarmInitializeVelocities(DM sw, PetscProbFunc sampler, const PetscReal v0[])
981 {
982   PetscSimplePointFunc velFunc;
983   PetscReal           *v;
984   PetscInt            *species;
985   void                *ctx;
986   PetscInt             dim, Np, p;
987 
988   PetscFunctionBegin;
989   PetscCall(DMSwarmGetVelocityFunction(sw, &velFunc));
990 
991   PetscCall(DMGetDimension(sw, &dim));
992   PetscCall(DMSwarmGetLocalSize(sw, &Np));
993   PetscCall(DMSwarmGetField(sw, "velocity", NULL, NULL, (void **) &v));
994   PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **) &species));
995   if (v0[0] == 0.) {
996     PetscCall(PetscArrayzero(v, Np*dim));
997   } else if (velFunc) {
998     PetscCall(DMGetApplicationContext(sw, &ctx));
999     for (p = 0; p < Np; ++p) {
1000       PetscInt    s = species[p], d;
1001       PetscScalar vel[3];
1002 
1003       PetscCall((*velFunc)(dim, 0., NULL, p, vel, ctx));
1004       for (d = 0; d < dim; ++d) v[p*dim+d] = (v0[s] / v0[0]) * PetscRealPart(vel[d]);
1005     }
1006   } else {
1007     PetscRandom  rnd;
1008 
1009     PetscCall(PetscRandomCreate(PetscObjectComm((PetscObject) sw), &rnd));
1010     PetscCall(PetscRandomSetInterval(rnd, 0, 1.));
1011     PetscCall(PetscRandomSetFromOptions(rnd));
1012 
1013     for (p = 0; p < Np; ++p) {
1014       PetscInt  s = species[p], d;
1015       PetscReal a[3], vel[3];
1016 
1017       for (d = 0; d < dim; ++d) PetscCall(PetscRandomGetValueReal(rnd, &a[d]));
1018       PetscCall(sampler(a, NULL, vel));
1019       for (d = 0; d < dim; ++d) {v[p*dim+d] = (v0[s] / v0[0]) * vel[d];}
1020     }
1021     PetscCall(PetscRandomDestroy(&rnd));
1022   }
1023   PetscCall(DMSwarmRestoreField(sw, "velocity", NULL, NULL, (void **) &v));
1024   PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **) &species));
1025   PetscFunctionReturn(0);
1026 }
1027 
1028 /*@
1029   DMSwarmInitializeVelocitiesFromOptions - Set the initial velocities of particles using a distribution determined from options.
1030 
1031   Collective on dm
1032 
1033   Input Parameters:
1034 + sw      - The DMSwarm object
1035 - v0      - The velocity scale for nondimensionalization for each species
1036 
1037   Level: advanced
1038 
1039 .seealso: DMSwarmComputeLocalSize(), DMSwarmInitializeCoordinates(), DMSwarmInitializeVelocities()
1040 @*/
1041 PetscErrorCode DMSwarmInitializeVelocitiesFromOptions(DM sw, const PetscReal v0[])
1042 {
1043   PetscProbFunc  sampler;
1044   PetscInt       dim;
1045   const char    *prefix;
1046   char           funcname[PETSC_MAX_PATH_LEN];
1047   PetscBool      flg;
1048 
1049   PetscFunctionBegin;
1050   PetscOptionsBegin(PetscObjectComm((PetscObject) sw), "", "DMSwarm Options", "DMSWARM");
1051   PetscCall(PetscOptionsString("-dm_swarm_velocity_function", "Function to determine particle velocities", "DMSwarmSetVelocityFunction", funcname, funcname, sizeof(funcname), &flg));
1052   PetscOptionsEnd();
1053   if (flg) {
1054     PetscSimplePointFunc velFunc;
1055 
1056     PetscCall(PetscDLSym(NULL, funcname, (void **) &velFunc));
1057     PetscCheck(velFunc, PetscObjectComm((PetscObject) sw), PETSC_ERR_ARG_WRONG, "Could not locate function %s", funcname);
1058     PetscCall(DMSwarmSetVelocityFunction(sw, velFunc));
1059   }
1060   PetscCall(DMGetDimension(sw, &dim));
1061   PetscCall(PetscObjectGetOptionsPrefix((PetscObject) sw, &prefix));
1062   PetscCall(PetscProbCreateFromOptions(dim, prefix, "-dm_swarm_velocity_density", NULL, NULL, &sampler));
1063   PetscCall(DMSwarmInitializeVelocities(sw, sampler, v0));
1064   PetscFunctionReturn(0);
1065 }
1066