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