xref: /petsc/src/ts/tests/ex28.c (revision ebead697dbf761eb322f829370bbe90b3bd93fa3)
1 static char help[] = "Example application of the Bhatnagar-Gross-Krook (BGK) collision operator.\n\
2 This example is a 0D-1V setting for the kinetic equation\n\
3 https://en.wikipedia.org/wiki/Bhatnagar%E2%80%93Gross%E2%80%93Krook_operator\n";
4 
5 #include <petscdmplex.h>
6 #include <petscdmswarm.h>
7 #include <petscts.h>
8 #include <petscdraw.h>
9 #include <petscviewer.h>
10 
11 typedef struct {
12   PetscInt    particlesPerCell; /* The number of partices per cell */
13   PetscReal   momentTol;        /* Tolerance for checking moment conservation */
14   PetscBool   monitorhg;        /* Flag for using the TS histogram monitor */
15   PetscBool   monitorsp;        /* Flag for using the TS scatter monitor */
16   PetscBool   monitorks;        /* Monitor to perform KS test to the maxwellian */
17   PetscBool   error;            /* Flag for printing the error */
18   PetscInt    ostep;            /* print the energy at each ostep time steps */
19   PetscDraw   draw;             /* The draw object for histogram monitoring */
20   PetscDrawHG drawhg;           /* The histogram draw context for monitoring */
21   PetscDrawSP drawsp;           /* The scatter plot draw context for the monitor */
22   PetscDrawSP drawks;           /* Scatterplot draw context for KS test */
23 } AppCtx;
24 
25 static PetscErrorCode ProcessOptions(MPI_Comm comm, AppCtx *options)
26 {
27   PetscFunctionBeginUser;
28   options->monitorhg        = PETSC_FALSE;
29   options->monitorsp        = PETSC_FALSE;
30   options->monitorks        = PETSC_FALSE;
31   options->particlesPerCell = 1;
32   options->momentTol        = 100.0*PETSC_MACHINE_EPSILON;
33   options->ostep            = 100;
34 
35   PetscOptionsBegin(comm, "", "Collision Options", "DMPLEX");
36   PetscCall(PetscOptionsBool("-monitorhg", "Flag to use the TS histogram monitor", "ex28.c", options->monitorhg, &options->monitorhg, NULL));
37   PetscCall(PetscOptionsBool("-monitorsp", "Flag to use the TS scatter plot monitor", "ex28.c", options->monitorsp, &options->monitorsp, NULL));
38   PetscCall(PetscOptionsBool("-monitorks", "Flag to plot KS test results", "ex28.c", options->monitorks, &options->monitorks, NULL));
39   PetscCall(PetscOptionsInt("-particles_per_cell", "Number of particles per cell", "ex28.c", options->particlesPerCell, &options->particlesPerCell, NULL));
40   PetscCall(PetscOptionsInt("-output_step", "Number of time steps between output", "ex28.c", options->ostep, &options->ostep, PETSC_NULL));
41   PetscOptionsEnd();
42 
43   PetscFunctionReturn(0);
44 }
45 
46 /* Create the mesh for velocity space */
47 static PetscErrorCode CreateMesh(MPI_Comm comm, DM *dm, AppCtx *user)
48 {
49   PetscFunctionBeginUser;
50   PetscCall(DMCreate(comm, dm));
51   PetscCall(DMSetType(*dm, DMPLEX));
52   PetscCall(DMSetFromOptions(*dm));
53   PetscCall(DMViewFromOptions(*dm, NULL, "-dm_view"));
54   PetscFunctionReturn(0);
55 }
56 
57 /* Since we are putting the same number of particles in each cell, this amounts to a uniform distribution of v */
58 static PetscErrorCode SetInitialCoordinates(DM sw)
59 {
60   AppCtx        *user;
61   PetscRandom    rnd;
62   DM             dm;
63   DMPolytopeType ct;
64   PetscBool      simplex;
65   PetscReal     *centroid, *coords, *xi0, *v0, *J, *invJ, detJ, *vals;
66   PetscInt       dim, d, cStart, cEnd, c, Np, p;
67 
68   PetscFunctionBeginUser;
69   PetscCall(PetscRandomCreate(PetscObjectComm((PetscObject) sw), &rnd));
70   PetscCall(PetscRandomSetInterval(rnd, -1.0, 1.0));
71   PetscCall(PetscRandomSetFromOptions(rnd));
72 
73   PetscCall(DMGetApplicationContext(sw, &user));
74   Np   = user->particlesPerCell;
75   PetscCall(DMGetDimension(sw, &dim));
76   PetscCall(DMSwarmGetCellDM(sw, &dm));
77   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
78   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
79   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct)+1 ? PETSC_TRUE : PETSC_FALSE;
80   PetscCall(PetscMalloc5(dim, &centroid, dim, &xi0, dim, &v0, dim*dim, &J, dim*dim, &invJ));
81   for (d = 0; d < dim; ++d) xi0[d] = -1.0;
82   PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **) &coords));
83   PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **) &vals));
84   for (c = cStart; c < cEnd; ++c) {
85     if (Np == 1) {
86       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, NULL, centroid, NULL));
87       for (d = 0; d < dim; ++d) {
88         coords[c*dim+d] = centroid[d];
89         if ((coords[c*dim+d] >= -1) && (coords[c*dim+d] <= 1)) {
90           vals[c] = 1.0;
91         } else {
92           vals[c] = 0.;
93         }
94       }
95     } else {
96       PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, v0, J, invJ, &detJ)); /* affine */
97       for (p = 0; p < Np; ++p) {
98         const PetscInt n   = c*Np + p;
99         PetscReal      sum = 0.0, refcoords[3];
100 
101         for (d = 0; d < dim; ++d) {
102           PetscCall(PetscRandomGetValueReal(rnd, &refcoords[d]));
103           sum += refcoords[d];
104         }
105         if (simplex && sum > 0.0) for (d = 0; d < dim; ++d) refcoords[d] -= PetscSqrtReal(dim)*sum;
106         vals[n] = 1.0;
107         PetscCall(DMPlexReferenceToCoordinates(dm, c, 1, refcoords, &coords[n*dim]));
108       }
109     }
110   }
111   PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **) &coords));
112   PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **) &vals));
113   PetscCall(PetscFree5(centroid, xi0, v0, J, invJ));
114   PetscCall(PetscRandomDestroy(&rnd));
115   PetscFunctionReturn(0);
116 }
117 
118 /* The intiial conditions are just the initial particle weights */
119 static PetscErrorCode SetInitialConditions(DM dmSw, Vec u)
120 {
121   DM             dm;
122   AppCtx        *user;
123   PetscReal     *vals;
124   PetscScalar   *initialConditions;
125   PetscInt       dim, d, cStart, cEnd, c, Np, p, n;
126 
127   PetscFunctionBeginUser;
128   PetscCall(VecGetLocalSize(u, &n));
129   PetscCall(DMGetApplicationContext(dmSw, &user));
130   Np   = user->particlesPerCell;
131   PetscCall(DMSwarmGetCellDM(dmSw, &dm));
132   PetscCall(DMGetDimension(dm, &dim));
133   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
134   PetscCheck(n == (cEnd-cStart)*Np,PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "TS solution local size %" PetscInt_FMT " != %" PetscInt_FMT " nm particles", n, (cEnd-cStart)*Np);
135   PetscCall(DMSwarmGetField(dmSw, "w_q", NULL, NULL, (void **) &vals));
136   PetscCall(VecGetArray(u, &initialConditions));
137   for (c = cStart; c < cEnd; ++c) {
138     for (p = 0; p < Np; ++p) {
139       const PetscInt n = c*Np + p;
140       for (d = 0; d < dim; d++) {
141         initialConditions[n] = vals[n];
142       }
143     }
144   }
145   PetscCall(VecRestoreArray(u, &initialConditions));
146   PetscCall(DMSwarmRestoreField(dmSw, "w_q", NULL, NULL, (void **) &vals));
147   PetscFunctionReturn(0);
148 }
149 
150 static PetscErrorCode CreateParticles(DM dm, DM *sw, AppCtx *user)
151 {
152   PetscInt      *cellid;
153   PetscInt       dim, cStart, cEnd, c, Np = user->particlesPerCell, p;
154 
155   PetscFunctionBeginUser;
156   PetscCall(DMGetDimension(dm, &dim));
157   PetscCall(DMCreate(PetscObjectComm((PetscObject) dm), sw));
158   PetscCall(DMSetType(*sw, DMSWARM));
159   PetscCall(DMSetDimension(*sw, dim));
160   PetscCall(DMSwarmSetType(*sw, DMSWARM_PIC));
161   PetscCall(DMSwarmSetCellDM(*sw, dm));
162   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "kinematics", dim, PETSC_REAL));
163   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "w_q", 1, PETSC_SCALAR));
164   PetscCall(DMSwarmFinalizeFieldRegister(*sw));
165   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
166   PetscCall(DMSwarmSetLocalSizes(*sw, (cEnd - cStart) * Np, 0));
167   PetscCall(DMSetFromOptions(*sw));
168   PetscCall(DMSwarmGetField(*sw, DMSwarmPICField_cellid, NULL, NULL, (void **) &cellid));
169   for (c = cStart; c < cEnd; ++c) {
170     for (p = 0; p < Np; ++p) {
171       const PetscInt n = c*Np + p;
172       cellid[n] = c;
173     }
174   }
175   PetscCall(DMSwarmRestoreField(*sw, DMSwarmPICField_cellid, NULL, NULL, (void **) &cellid));
176   PetscCall(PetscObjectSetName((PetscObject) *sw, "Particles"));
177   PetscCall(DMViewFromOptions(*sw, NULL, "-sw_view"));
178   PetscFunctionReturn(0);
179 }
180 
181 /*
182   f_t = 1/\tau \left( f_eq - f \right)
183   n_t = 1/\tau \left( \int f_eq - \int f \right) = 1/\tau (n - n) = 0
184   v_t = 1/\tau \left( \int v f_eq - \int v f \right) = 1/\tau (v - v) = 0
185   E_t = 1/\tau \left( \int v^2 f_eq - \int v^2 f \right) = 1/\tau (T - T) = 0
186 
187   Let's look at a single cell:
188 
189     \int_C f_t             = 1/\tau \left( \int_C f_eq - \int_C f \right)
190     \sum_{x_i \in C} w^i_t = 1/\tau \left( F_eq(C) - \sum_{x_i \in C} w_i \right)
191 */
192 
193 /* This computes the 1D Maxwellian distribution for given mass n, velocity v, and temperature T */
194 static PetscReal ComputePDF(PetscReal m, PetscReal n, PetscReal T, PetscReal v[])
195 {
196   return (n/PetscSqrtReal(2.0*PETSC_PI*T/m)) * PetscExpReal(-0.5*m*PetscSqr(v[0])/T);
197 }
198 
199 /*
200   erf z = \frac{2}{\sqrt\pi} \int^z_0 dt e^{-t^2} and erf \infty = 1
201 
202   We begin with our distribution
203 
204     \sqrt{\frac{m}{2 \pi T}} e^{-m v^2/2T}
205 
206   Let t = \sqrt{m/2T} v, z = \sqrt{m/2T} w, so that we now have
207 
208       \sqrt{\frac{m}{2 \pi T}} \int^w_0 dv e^{-m v^2/2T}
209     = \sqrt{\frac{m}{2 \pi T}} \int^{\sqrt{m/2T} w}_0 \sqrt{2T/m} dt e^{-t^2}
210     = 1/\sqrt{\pi} \int^{\sqrt{m/2T} w}_0 dt e^{-t^2}
211     = 1/2 erf(\sqrt{m/2T} w)
212 */
213 static PetscReal ComputeCDF(PetscReal m, PetscReal n, PetscReal T, PetscReal va, PetscReal vb)
214 {
215   PetscReal alpha = PetscSqrtReal(0.5*m/T);
216   PetscReal za    = alpha*va;
217   PetscReal zb    = alpha*vb;
218   PetscReal sum   = 0.0;
219 
220   sum += zb >= 0 ? erf(zb) : -erf(-zb);
221   sum -= za >= 0 ? erf(za) : -erf(-za);
222   return 0.5 * n * sum;
223 }
224 
225 static PetscErrorCode CheckDistribution(DM dm, PetscReal m, PetscReal n, PetscReal T, PetscReal v[])
226 {
227   PetscSection   coordSection;
228   Vec            coordsLocal;
229   PetscReal     *xq, *wq;
230   PetscReal      vmin, vmax, neq, veq, Teq;
231   PetscInt       Nq = 100, q, cStart, cEnd, c;
232 
233   PetscFunctionBegin;
234   PetscCall(DMGetBoundingBox(dm, &vmin, &vmax));
235   /* Check analytic over entire line */
236   neq  = ComputeCDF(m, n, T, vmin, vmax);
237   PetscCheck(PetscAbsReal(neq - n) <= PETSC_SMALL,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Int f %g != %g mass (%g)", (double)neq, (double)n, (double)(neq-n));
238   /* Check analytic over cells */
239   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
240   PetscCall(DMGetCoordinateSection(dm, &coordSection));
241   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
242   neq  = 0.0;
243   for (c = cStart; c < cEnd; ++c) {
244     PetscScalar *vcoords = NULL;
245 
246     PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, NULL, &vcoords));
247     neq += ComputeCDF(m, n, T, vcoords[0], vcoords[1]);
248     PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, NULL, &vcoords));
249   }
250   PetscCheck(PetscAbsReal(neq - n) <= PETSC_SMALL,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell Int f %g != %g mass (%g)", (double)neq, (double)n, (double)(neq-n));
251   /* Check quadrature over entire line */
252   PetscCall(PetscMalloc2(Nq, &xq, Nq, &wq));
253   PetscCall(PetscDTGaussQuadrature(100, vmin, vmax, xq, wq));
254   neq  = 0.0;
255   for (q = 0; q < Nq; ++q) {
256     neq += ComputePDF(m, n, T, &xq[q])*wq[q];
257   }
258   PetscCheck(PetscAbsReal(neq - n) <= PETSC_SMALL,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Int f %g != %g mass (%g)", (double)neq, (double)n, (double)(neq-n));
259   /* Check omemnts with quadrature */
260   veq  = 0.0;
261   for (q = 0; q < Nq; ++q) {
262     veq += xq[q]*ComputePDF(m, n, T, &xq[q])*wq[q];
263   }
264   veq /= neq;
265   PetscCheck(PetscAbsReal(veq - v[0]) <= PETSC_SMALL,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Int v f %g != %g velocity (%g)", (double)veq, (double)v[0], (double)(veq-v[0]));
266   Teq  = 0.0;
267   for (q = 0; q < Nq; ++q) {
268     Teq += PetscSqr(xq[q])*ComputePDF(m, n, T, &xq[q])*wq[q];
269   }
270   Teq = Teq * m/neq - PetscSqr(veq);
271   PetscCheck(PetscAbsReal(Teq - T) <= PETSC_SMALL,PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Int v^2 f %g != %g temperature (%g)", (double)Teq, (double)T, (double)(Teq-T));
272   PetscCall(PetscFree2(xq, wq));
273   PetscFunctionReturn(0);
274 }
275 
276 static PetscErrorCode RHSFunctionParticles(TS ts, PetscReal t, Vec U, Vec R, void *ctx)
277 {
278   const PetscScalar *u;
279   PetscSection       coordSection;
280   Vec                coordsLocal;
281   PetscScalar       *r, *coords;
282   PetscReal          n = 0.0, v = 0.0, E = 0.0, T = 0.0, m = 1.0, cn = 0.0, cv = 0.0, cE = 0.0, pE = 0.0, eqE = 0.0;
283   PetscInt           dim, d, Np, Ncp, p, cStart, cEnd, c;
284   DM                 dmSw, plex;
285 
286   PetscFunctionBeginUser;
287   PetscCall(VecGetLocalSize(U, &Np));
288   PetscCall(VecGetArrayRead(U, &u));
289   PetscCall(VecGetArray(R, &r));
290   PetscCall(TSGetDM(ts, &dmSw));
291   PetscCall(DMSwarmGetCellDM(dmSw, &plex));
292   PetscCall(DMGetDimension(dmSw, &dim));
293   PetscCall(DMGetCoordinatesLocal(plex, &coordsLocal));
294   PetscCall(DMGetCoordinateSection(plex, &coordSection));
295   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, &cEnd));
296   Np  /= dim;
297   Ncp  = Np / (cEnd - cStart);
298   /* Calculate moments of particle distribution, note that velocity is in the coordinate */
299   PetscCall(DMSwarmGetField(dmSw, DMSwarmPICField_coor, NULL, NULL, (void **) &coords));
300   for (p = 0; p < Np; ++p) {
301     PetscReal m1 = 0.0, m2 = 0.0;
302 
303     for (d = 0; d < dim; ++d) {m1 += PetscRealPart(coords[p*dim+d]); m2 += PetscSqr(PetscRealPart(coords[p*dim+d]));}
304     n += u[p];
305     v += u[p]*m1;
306     E += u[p]*m2;
307   }
308   v /= n;
309   T  = E*m/n - v*v;
310   PetscCall(PetscInfo(ts, "Time %.2f: mass %.4f velocity: %+.4f temperature: %.4f\n", (double)t, (double)n, (double)v, (double)T));
311   PetscCall(CheckDistribution(plex, m, n, T, &v));
312   /*
313      Begin cellwise evaluation of the collision operator. Essentially, penalize the weights of the particles
314      in that cell against the maxwellian for the number of particles expected to be in that cell
315   */
316   for (c = cStart; c < cEnd; ++c) {
317     PetscScalar *vcoords = NULL;
318     PetscReal    relaxation = 1.0, neq;
319     PetscInt     sp      = c*Ncp, q;
320 
321     /* Calculate equilibrium occupation for this velocity cell */
322     PetscCall(DMPlexVecGetClosure(plex, coordSection, coordsLocal, c, NULL, &vcoords));
323     neq  = ComputeCDF(m, n, T, vcoords[0], vcoords[1]);
324     PetscCall(DMPlexVecRestoreClosure(plex, coordSection, coordsLocal, c, NULL, &vcoords));
325     for (q = 0; q < Ncp; ++q) r[sp+q] = (1.0/relaxation)*(neq - u[sp+q]);
326   }
327   /* Check update */
328   for (p = 0; p < Np; ++p) {
329     PetscReal m1 = 0.0, m2 = 0.0;
330     PetscScalar *vcoords = NULL;
331 
332     for (d = 0; d < dim; ++d) {m1 += PetscRealPart(coords[p*dim+d]); m2 += PetscSqr(PetscRealPart(coords[p*dim+d]));}
333     cn += r[p];
334     cv += r[p]*m1;
335     cE += r[p]*m2;
336     pE  += u[p]*m2;
337     PetscCall(DMPlexVecGetClosure(plex, coordSection, coordsLocal, p, NULL, &vcoords));
338     eqE += ComputeCDF(m, n, T, vcoords[0], vcoords[1])*m2;
339     PetscCall(DMPlexVecRestoreClosure(plex, coordSection, coordsLocal, p, NULL, &vcoords));
340   }
341   PetscCall(PetscInfo(ts, "Time %.2f: mass update %.8f velocity update: %+.8f energy update: %.8f (%.8f, %.8f)\n", (double)t, (double)cn, (double)cv, (double)cE, (double)pE, (double)eqE));
342   PetscCall(DMSwarmRestoreField(dmSw, DMSwarmPICField_coor, NULL, NULL, (void **) &coords));
343   PetscCall(VecRestoreArrayRead(U, &u));
344   PetscCall(VecRestoreArray(R, &r));
345   PetscFunctionReturn(0);
346 }
347 
348 static PetscErrorCode HGMonitor(TS ts, PetscInt step, PetscReal t, Vec U, void *ctx)
349 {
350   AppCtx            *user  = (AppCtx *) ctx;
351   const PetscScalar *u;
352   DM                 sw, dm;
353   PetscInt           dim, Np, p;
354 
355   PetscFunctionBeginUser;
356   if (step < 0) PetscFunctionReturn(0);
357   if (((user->ostep > 0) && (!(step % user->ostep)))) {
358     PetscDrawAxis axis;
359 
360     PetscCall(TSGetDM(ts, &sw));
361     PetscCall(DMSwarmGetCellDM(sw, &dm));
362     PetscCall(DMGetDimension(dm, &dim));
363     PetscCall(PetscDrawHGReset(user->drawhg));
364     PetscCall(PetscDrawHGGetAxis(user->drawhg,&axis));
365     PetscCall(PetscDrawAxisSetLabels(axis,"Particles","V","f(V)"));
366     PetscCall(PetscDrawAxisSetLimits(axis, -3, 3, 0, 100));
367     PetscCall(PetscDrawHGSetLimits(user->drawhg,-3.0, 3.0, 0, 10));
368     PetscCall(VecGetLocalSize(U, &Np));
369     Np  /= dim;
370     PetscCall(VecGetArrayRead(U,&u));
371     /* get points from solution vector */
372     for (p = 0; p < Np; ++p) PetscCall(PetscDrawHGAddValue(user->drawhg,u[p]));
373     PetscCall(PetscDrawHGDraw(user->drawhg));
374     PetscCall(VecRestoreArrayRead(U,&u));
375   }
376   PetscFunctionReturn(0);
377 }
378 
379 static PetscErrorCode SPMonitor(TS ts, PetscInt step, PetscReal t, Vec U, void *ctx)
380 {
381   AppCtx            *user  = (AppCtx *) ctx;
382   const PetscScalar *u;
383   PetscReal         *v, *coords;
384   PetscInt           Np, p;
385   DM                 dmSw;
386 
387   PetscFunctionBeginUser;
388 
389   if (step < 0) PetscFunctionReturn(0);
390   if (((user->ostep > 0) && (!(step % user->ostep)))) {
391     PetscDrawAxis axis;
392 
393     PetscCall(TSGetDM(ts, &dmSw));
394     PetscCall(PetscDrawSPReset(user->drawsp));
395     PetscCall(PetscDrawSPGetAxis(user->drawsp,&axis));
396     PetscCall(PetscDrawAxisSetLabels(axis,"Particles","V","w"));
397     PetscCall(VecGetLocalSize(U, &Np));
398     PetscCall(VecGetArrayRead(U,&u));
399     /* get points from solution vector */
400     PetscCall(PetscMalloc1(Np, &v));
401     for (p = 0; p < Np; ++p) v[p] = PetscRealPart(u[p]);
402     PetscCall(DMSwarmGetField(dmSw, DMSwarmPICField_coor, NULL, NULL, (void **) &coords));
403     for (p = 0; p < Np-1; ++p) PetscCall(PetscDrawSPAddPoint(user->drawsp, &coords[p], &v[p]));
404     PetscCall(PetscDrawSPDraw(user->drawsp, PETSC_TRUE));
405     PetscCall(VecRestoreArrayRead(U,&u));
406     PetscCall(DMSwarmRestoreField(dmSw, DMSwarmPICField_coor, NULL, NULL, (void **) &coords));
407     PetscCall(PetscFree(v));
408   }
409   PetscFunctionReturn(0);
410 }
411 
412 static PetscErrorCode KSConv(TS ts, PetscInt step, PetscReal t, Vec U, void *ctx)
413 {
414   AppCtx            *user  = (AppCtx *) ctx;
415   const PetscScalar *u;
416   PetscScalar       sup;
417   PetscReal         *v, *coords, T=0., vel=0., step_cast, w_sum;
418   PetscInt           dim, Np, p, cStart, cEnd;
419   DM                 sw, plex;
420 
421   PetscFunctionBeginUser;
422   if (step < 0) PetscFunctionReturn(0);
423   if (((user->ostep > 0) && (!(step % user->ostep)))) {
424     PetscDrawAxis axis;
425     PetscCall(PetscDrawSPGetAxis(user->drawks,&axis));
426     PetscCall(PetscDrawAxisSetLabels(axis,"Particles","t","D_n"));
427     PetscCall(PetscDrawSPSetLimits(user->drawks,0.,100,0.,3.5));
428     PetscCall(TSGetDM(ts, &sw));
429     PetscCall(VecGetLocalSize(U, &Np));
430     PetscCall(VecGetArrayRead(U,&u));
431     /* get points from solution vector */
432     PetscCall(PetscMalloc1(Np, &v));
433     PetscCall(DMSwarmGetCellDM(sw, &plex));
434     PetscCall(DMGetDimension(sw, &dim));
435     PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, &cEnd));
436     for (p = 0; p < Np; ++p) v[p] = PetscRealPart(u[p]);
437     PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **) &coords));
438     w_sum = 0.;
439     for (p = 0; p < Np; ++p) {
440       w_sum += u[p];
441       T += u[p]*coords[p]*coords[p];
442       vel += u[p]*coords[p];
443     }
444     vel /= w_sum;
445     T = T/w_sum - vel*vel;
446     sup = 0.0;
447     for (p = 0; p < Np; ++p) {
448         PetscReal tmp = 0.;
449         tmp = PetscAbs(u[p]-ComputePDF(1.0, w_sum, T, &coords[p*dim]));
450         if (tmp > sup) sup = tmp;
451     }
452     step_cast = (PetscReal)step;
453     PetscCall(PetscDrawSPAddPoint(user->drawks, &step_cast, &sup));
454     PetscCall(PetscDrawSPDraw(user->drawks, PETSC_TRUE));
455     PetscCall(VecRestoreArrayRead(U,&u));
456     PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **) &coords));
457     PetscCall(PetscFree(v));
458   }
459   PetscFunctionReturn(0);
460 }
461 
462 static PetscErrorCode InitializeSolve(TS ts, Vec u)
463 {
464   DM             dm;
465   AppCtx        *user;
466 
467   PetscFunctionBeginUser;
468   PetscCall(TSGetDM(ts, &dm));
469   PetscCall(DMGetApplicationContext(dm, &user));
470   PetscCall(SetInitialCoordinates(dm));
471   PetscCall(SetInitialConditions(dm, u));
472   PetscFunctionReturn(0);
473 }
474   /*
475      A single particle is generated for each velocity space cell using the dmswarmpicfield_coor and is used to evaluate collisions in that cell.
476      0 weight ghost particles are initialized outside of a small velocity domain to ensure the tails of the amxwellian are resolved.
477    */
478 int main(int argc,char **argv)
479 {
480   TS             ts;     /* nonlinear solver */
481   DM             dm, sw; /* Velocity space mesh and Particle Swarm */
482   Vec            u, w;   /* swarm vector */
483   MPI_Comm       comm;
484   AppCtx         user;
485 
486   PetscFunctionBeginUser;
487   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
488   comm = PETSC_COMM_WORLD;
489   PetscCall(ProcessOptions(comm, &user));
490   PetscCall(CreateMesh(comm, &dm, &user));
491   PetscCall(CreateParticles(dm, &sw, &user));
492   PetscCall(DMSetApplicationContext(sw, &user));
493   PetscCall(TSCreate(comm, &ts));
494   PetscCall(TSSetDM(ts, sw));
495   PetscCall(TSSetMaxTime(ts, 10.0));
496   PetscCall(TSSetTimeStep(ts, 0.01));
497   PetscCall(TSSetMaxSteps(ts, 100000));
498   PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_MATCHSTEP));
499   if (user.monitorhg) {
500     PetscCall(PetscDrawCreate(comm, NULL, "monitor", 0,0,400,300, &user.draw));
501     PetscCall(PetscDrawSetFromOptions(user.draw));
502     PetscCall(PetscDrawHGCreate(user.draw, 20, &user.drawhg));
503     PetscCall(PetscDrawHGSetColor(user.drawhg,3));
504     PetscCall(TSMonitorSet(ts, HGMonitor, &user, NULL));
505   }
506   else if (user.monitorsp) {
507     PetscCall(PetscDrawCreate(comm, NULL, "monitor", 0,0,400,300, &user.draw));
508     PetscCall(PetscDrawSetFromOptions(user.draw));
509     PetscCall(PetscDrawSPCreate(user.draw, 1, &user.drawsp));
510     PetscCall(TSMonitorSet(ts, SPMonitor, &user, NULL));
511   }
512   else if (user.monitorks) {
513     PetscCall(PetscDrawCreate(comm, NULL, "monitor", 0,0,400,300, &user.draw));
514     PetscCall(PetscDrawSetFromOptions(user.draw));
515     PetscCall(PetscDrawSPCreate(user.draw, 1, &user.drawks));
516     PetscCall(TSMonitorSet(ts, KSConv, &user, NULL));
517   }
518   PetscCall(TSSetRHSFunction(ts, NULL, RHSFunctionParticles, &user));
519   PetscCall(TSSetFromOptions(ts));
520   PetscCall(TSSetComputeInitialCondition(ts, InitializeSolve));
521   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "w_q", &w));
522   PetscCall(VecDuplicate(w, &u));
523   PetscCall(VecCopy(w, u));
524   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "w_q", &w));
525   PetscCall(TSComputeInitialCondition(ts, u));
526   PetscCall(TSSolve(ts, u));
527   if (user.monitorhg) {
528     PetscCall(PetscDrawSave(user.draw));
529     PetscCall(PetscDrawHGDestroy(&user.drawhg));
530     PetscCall(PetscDrawDestroy(&user.draw));
531   }
532   if (user.monitorsp) {
533     PetscCall(PetscDrawSave(user.draw));
534     PetscCall(PetscDrawSPDestroy(&user.drawsp));
535     PetscCall(PetscDrawDestroy(&user.draw));
536   }
537   if (user.monitorks) {
538     PetscCall(PetscDrawSave(user.draw));
539     PetscCall(PetscDrawSPDestroy(&user.drawks));
540     PetscCall(PetscDrawDestroy(&user.draw));
541   }
542   PetscCall(VecDestroy(&u));
543   PetscCall(TSDestroy(&ts));
544   PetscCall(DMDestroy(&sw));
545   PetscCall(DMDestroy(&dm));
546   PetscCall(PetscFinalize());
547   return 0;
548 }
549 
550 /*TEST
551    build:
552      requires: double !complex
553    test:
554      suffix: 1
555      args: -particles_per_cell 1 -output_step 10 -ts_type euler -dm_plex_dim 1 -dm_plex_box_faces 200 -dm_plex_box_lower -10 -dm_plex_box_upper 10 -dm_view -monitorsp
556    test:
557      suffix: 2
558      args: -particles_per_cell 1 -output_step 50 -ts_type euler -dm_plex_dim 1 -dm_plex_box_faces 200 -dm_plex_box_lower -10 -dm_plex_box_upper 10 -dm_view -monitorks
559 TEST*/
560