1 static char help[] = "Vlasov-Poisson example of central orbits\n"; 2 3 /* 4 To visualize the orbit, we can used 5 6 -ts_monitor_sp_swarm -ts_monitor_sp_swarm_retain -1 -ts_monitor_sp_swarm_phase 0 -draw_size 500,500 7 8 and we probably want it to run fast and not check convergence 9 10 -convest_num_refine 0 -ts_dt 0.01 -ts_max_steps 100 -ts_max_time 100 -output_step 10 11 */ 12 13 #include <petscts.h> 14 #include <petscdmplex.h> 15 #include <petscdmswarm.h> 16 #include <petsc/private/dmpleximpl.h> /* For norm and dot */ 17 #include <petscfe.h> 18 #include <petscds.h> 19 #include <petsc/private/petscfeimpl.h> /* For interpolation */ 20 #include <petscksp.h> 21 #include <petscsnes.h> 22 23 PETSC_EXTERN PetscErrorCode circleSingleX(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar[], void *); 24 PETSC_EXTERN PetscErrorCode circleSingleV(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar[], void *); 25 PETSC_EXTERN PetscErrorCode circleMultipleX(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar[], void *); 26 PETSC_EXTERN PetscErrorCode circleMultipleV(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar[], void *); 27 28 const char *EMTypes[] = {"primal", "mixed", "coulomb", "none", "EMType", "EM_", NULL}; 29 typedef enum { 30 EM_PRIMAL, 31 EM_MIXED, 32 EM_COULOMB, 33 EM_NONE 34 } EMType; 35 36 typedef struct { 37 PetscBool error; /* Flag for printing the error */ 38 PetscInt ostep; /* print the energy at each ostep time steps */ 39 PetscReal timeScale; /* Nondimensionalizing time scale */ 40 PetscReal sigma; /* Linear charge per box length */ 41 EMType em; /* Type of electrostatic model */ 42 SNES snes; 43 } AppCtx; 44 45 static PetscErrorCode ProcessOptions(MPI_Comm comm, AppCtx *options) 46 { 47 PetscFunctionBeginUser; 48 options->error = PETSC_FALSE; 49 options->ostep = 100; 50 options->timeScale = 1.0e-6; 51 options->sigma = 1.; 52 options->em = EM_COULOMB; 53 54 PetscOptionsBegin(comm, "", "Central Orbit Options", "DMSWARM"); 55 PetscCall(PetscOptionsBool("-error", "Flag to print the error", "ex6.c", options->error, &options->error, NULL)); 56 PetscCall(PetscOptionsInt("-output_step", "Number of time steps between output", "ex6.c", options->ostep, &options->ostep, NULL)); 57 PetscCall(PetscOptionsReal("-sigma", "Linear charge per box length", "ex6.c", options->sigma, &options->sigma, NULL)); 58 PetscCall(PetscOptionsReal("-timeScale", "Nondimensionalizing time scale", "ex6.c", options->timeScale, &options->timeScale, NULL)); 59 PetscCall(PetscOptionsEnum("-em_type", "Type of electrostatic solver", "ex6.c", EMTypes, (PetscEnum)options->em, (PetscEnum *)&options->em, NULL)); 60 PetscOptionsEnd(); 61 PetscFunctionReturn(PETSC_SUCCESS); 62 } 63 64 static PetscErrorCode CreateMesh(MPI_Comm comm, AppCtx *user, DM *dm) 65 { 66 PetscFunctionBeginUser; 67 PetscCall(DMCreate(comm, dm)); 68 PetscCall(DMSetType(*dm, DMPLEX)); 69 PetscCall(DMSetFromOptions(*dm)); 70 PetscCall(DMViewFromOptions(*dm, NULL, "-dm_view")); 71 PetscFunctionReturn(PETSC_SUCCESS); 72 } 73 74 static void laplacian_f1(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f1[]) 75 { 76 PetscInt d; 77 for (d = 0; d < dim; ++d) f1[d] = u_x[d]; 78 } 79 80 static void laplacian_g3(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g3[]) 81 { 82 PetscInt d; 83 for (d = 0; d < dim; ++d) g3[d * dim + d] = 1.0; 84 } 85 86 /* 87 / I grad\ /q\ = /0\ 88 \-div 0 / \u/ \f/ 89 */ 90 static void f0_q(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f0[]) 91 { 92 for (PetscInt c = 0; c < dim; ++c) f0[c] += u[uOff[0] + c]; 93 } 94 95 static void f1_q(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar f1[]) 96 { 97 for (PetscInt d = 0; d < dim; ++d) f1[d * dim + d] += u[uOff[1]]; 98 } 99 100 /* <t, q> */ 101 static void g0_qq(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g0[]) 102 { 103 PetscInt c; 104 for (c = 0; c < dim; ++c) g0[c * dim + c] += 1.0; 105 } 106 107 static void g2_qu(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g2[]) 108 { 109 for (PetscInt d = 0; d < dim; ++d) g2[d * dim + d] += 1.0; 110 } 111 112 static void g1_uq(PetscInt dim, PetscInt Nf, PetscInt NfAux, const PetscInt uOff[], const PetscInt uOff_x[], const PetscScalar u[], const PetscScalar u_t[], const PetscScalar u_x[], const PetscInt aOff[], const PetscInt aOff_x[], const PetscScalar a[], const PetscScalar a_t[], const PetscScalar a_x[], PetscReal t, PetscReal u_tShift, const PetscReal x[], PetscInt numConstants, const PetscScalar constants[], PetscScalar g1[]) 113 { 114 for (PetscInt d = 0; d < dim; ++d) g1[d * dim + d] += 1.0; 115 } 116 117 static PetscErrorCode CreateFEM(DM dm, AppCtx *user) 118 { 119 PetscFE feu, feq; 120 PetscDS ds; 121 PetscBool simplex; 122 PetscInt dim; 123 124 PetscFunctionBeginUser; 125 PetscCall(DMGetDimension(dm, &dim)); 126 PetscCall(DMPlexIsSimplex(dm, &simplex)); 127 if (user->em == EM_MIXED) { 128 DMLabel label; 129 130 PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, dim, simplex, "field_", PETSC_DETERMINE, &feq)); 131 PetscCall(PetscObjectSetName((PetscObject)feq, "field")); 132 PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "potential_", PETSC_DETERMINE, &feu)); 133 PetscCall(PetscObjectSetName((PetscObject)feu, "potential")); 134 PetscCall(PetscFECopyQuadrature(feq, feu)); 135 PetscCall(DMSetField(dm, 0, NULL, (PetscObject)feq)); 136 PetscCall(DMSetField(dm, 1, NULL, (PetscObject)feu)); 137 PetscCall(DMCreateDS(dm)); 138 PetscCall(PetscFEDestroy(&feu)); 139 PetscCall(PetscFEDestroy(&feq)); 140 141 PetscCall(DMGetLabel(dm, "marker", &label)); 142 PetscCall(DMGetDS(dm, &ds)); 143 PetscCall(PetscDSSetResidual(ds, 0, f0_q, f1_q)); 144 PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_qq, NULL, NULL, NULL)); 145 PetscCall(PetscDSSetJacobian(ds, 0, 1, NULL, NULL, g2_qu, NULL)); 146 PetscCall(PetscDSSetJacobian(ds, 1, 0, NULL, g1_uq, NULL, NULL)); 147 } else if (user->em == EM_PRIMAL) { 148 PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, NULL, PETSC_DETERMINE, &feu)); 149 PetscCall(PetscObjectSetName((PetscObject)feu, "potential")); 150 PetscCall(DMSetField(dm, 0, NULL, (PetscObject)feu)); 151 PetscCall(DMCreateDS(dm)); 152 PetscCall(PetscFEDestroy(&feu)); 153 PetscCall(DMGetDS(dm, &ds)); 154 PetscCall(PetscDSSetResidual(ds, 0, NULL, laplacian_f1)); 155 PetscCall(PetscDSSetJacobian(ds, 0, 0, NULL, NULL, NULL, laplacian_g3)); 156 } 157 PetscFunctionReturn(PETSC_SUCCESS); 158 } 159 160 static PetscErrorCode CreatePoisson(DM dm, AppCtx *user) 161 { 162 SNES snes; 163 Mat J; 164 MatNullSpace nullSpace; 165 166 PetscFunctionBeginUser; 167 PetscCall(CreateFEM(dm, user)); 168 PetscCall(SNESCreate(PetscObjectComm((PetscObject)dm), &snes)); 169 PetscCall(SNESSetOptionsPrefix(snes, "em_")); 170 PetscCall(SNESSetDM(snes, dm)); 171 PetscCall(DMPlexSetSNESLocalFEM(dm, PETSC_FALSE, user)); 172 PetscCall(SNESSetFromOptions(snes)); 173 174 PetscCall(DMCreateMatrix(dm, &J)); 175 PetscCall(MatNullSpaceCreate(PetscObjectComm((PetscObject)dm), PETSC_TRUE, 0, NULL, &nullSpace)); 176 PetscCall(MatSetNullSpace(J, nullSpace)); 177 PetscCall(MatNullSpaceDestroy(&nullSpace)); 178 PetscCall(SNESSetJacobian(snes, J, J, NULL, NULL)); 179 PetscCall(MatDestroy(&J)); 180 user->snes = snes; 181 PetscFunctionReturn(PETSC_SUCCESS); 182 } 183 184 static PetscErrorCode CreateSwarm(DM dm, AppCtx *user, DM *sw) 185 { 186 PetscReal v0[1] = {1.}; 187 PetscInt dim; 188 189 PetscFunctionBeginUser; 190 PetscCall(DMGetDimension(dm, &dim)); 191 PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), sw)); 192 PetscCall(DMSetType(*sw, DMSWARM)); 193 PetscCall(DMSetDimension(*sw, dim)); 194 PetscCall(DMSwarmSetType(*sw, DMSWARM_PIC)); 195 PetscCall(DMSwarmSetCellDM(*sw, dm)); 196 PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "w_q", 1, PETSC_SCALAR)); 197 PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "velocity", dim, PETSC_REAL)); 198 PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "species", 1, PETSC_INT)); 199 PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "initCoordinates", dim, PETSC_REAL)); 200 PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "initVelocity", dim, PETSC_REAL)); 201 PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "E_field", dim, PETSC_REAL)); 202 PetscCall(DMSwarmFinalizeFieldRegister(*sw)); 203 PetscCall(DMSwarmComputeLocalSizeFromOptions(*sw)); 204 PetscCall(DMSwarmInitializeCoordinates(*sw)); 205 PetscCall(DMSwarmInitializeVelocitiesFromOptions(*sw, v0)); 206 PetscCall(DMSetFromOptions(*sw)); 207 PetscCall(DMSetApplicationContext(*sw, user)); 208 PetscCall(PetscObjectSetName((PetscObject)*sw, "Particles")); 209 PetscCall(DMViewFromOptions(*sw, NULL, "-sw_view")); 210 { 211 Vec gc, gc0, gv, gv0; 212 213 PetscCall(DMSwarmCreateGlobalVectorFromField(*sw, DMSwarmPICField_coor, &gc)); 214 PetscCall(DMSwarmCreateGlobalVectorFromField(*sw, "initCoordinates", &gc0)); 215 PetscCall(VecCopy(gc, gc0)); 216 PetscCall(DMSwarmDestroyGlobalVectorFromField(*sw, DMSwarmPICField_coor, &gc)); 217 PetscCall(DMSwarmDestroyGlobalVectorFromField(*sw, "initCoordinates", &gc0)); 218 PetscCall(DMSwarmCreateGlobalVectorFromField(*sw, "velocity", &gv)); 219 PetscCall(DMSwarmCreateGlobalVectorFromField(*sw, "initVelocity", &gv0)); 220 PetscCall(VecCopy(gv, gv0)); 221 PetscCall(DMSwarmDestroyGlobalVectorFromField(*sw, "velocity", &gv)); 222 PetscCall(DMSwarmDestroyGlobalVectorFromField(*sw, "initVelocity", &gv0)); 223 } 224 PetscFunctionReturn(PETSC_SUCCESS); 225 } 226 227 static PetscErrorCode ComputeFieldAtParticles_Coulomb(SNES snes, DM sw, PetscReal E[]) 228 { 229 PetscReal *coords; 230 PetscInt dim, d, Np, p, q; 231 PetscMPIInt size; 232 233 PetscFunctionBegin; 234 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)snes), &size)); 235 PetscCheck(size == 1, PetscObjectComm((PetscObject)snes), PETSC_ERR_SUP, "Coulomb code only works in serial"); 236 PetscCall(DMGetDimension(sw, &dim)); 237 PetscCall(DMSwarmGetLocalSize(sw, &Np)); 238 239 PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords)); 240 for (p = 0; p < Np; ++p) { 241 PetscReal *pcoord = &coords[p * dim]; 242 PetscReal *pE = &E[p * dim]; 243 /* Calculate field at particle p due to particle q */ 244 for (q = 0; q < Np; ++q) { 245 PetscReal *qcoord = &coords[q * dim]; 246 PetscReal rpq[3], r; 247 248 if (p == q) continue; 249 for (d = 0; d < dim; ++d) rpq[d] = pcoord[d] - qcoord[d]; 250 r = DMPlex_NormD_Internal(dim, rpq); 251 for (d = 0; d < dim; ++d) pE[d] += rpq[d] / PetscPowRealInt(r, 3); 252 } 253 } 254 PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords)); 255 PetscFunctionReturn(PETSC_SUCCESS); 256 } 257 258 static PetscErrorCode ComputeFieldAtParticles_Primal(SNES snes, DM sw, PetscReal E[]) 259 { 260 DM dm; 261 PetscDS ds; 262 PetscFE fe; 263 Mat M_p; 264 Vec phi, locPhi, rho, f; 265 PetscReal *coords, chargeTol = 1e-13; 266 PetscInt dim, d, cStart, cEnd, c, Np; 267 char oldField[PETSC_MAX_PATH_LEN]; 268 const char *tmp; 269 270 PetscFunctionBegin; 271 PetscCall(DMGetDimension(sw, &dim)); 272 PetscCall(DMSwarmGetLocalSize(sw, &Np)); 273 PetscCall(SNESGetDM(snes, &dm)); 274 275 PetscCall(DMSwarmVectorGetField(sw, &tmp)); 276 PetscCall(PetscStrncpy(oldField, tmp, PETSC_MAX_PATH_LEN)); 277 PetscCall(DMSwarmVectorDefineField(sw, "w_q")); 278 PetscCall(DMCreateMassMatrix(sw, dm, &M_p)); 279 PetscCall(DMSwarmVectorDefineField(sw, oldField)); 280 281 /* Create the charges rho */ 282 PetscCall(DMGetGlobalVector(dm, &rho)); 283 PetscCall(PetscObjectSetName((PetscObject)rho, "rho")); 284 PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "w_q", &f)); 285 PetscCall(PetscObjectSetName((PetscObject)f, "particle weight")); 286 PetscCall(MatMultTranspose(M_p, f, rho)); 287 PetscCall(MatViewFromOptions(M_p, NULL, "-mp_view")); 288 PetscCall(VecViewFromOptions(f, NULL, "-weights_view")); 289 PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "w_q", &f)); 290 PetscCall(MatDestroy(&M_p)); 291 { 292 PetscScalar sum; 293 PetscInt n; 294 PetscReal phi_0 = 1.; /* (sigma*sigma*sigma)*(timeScale*timeScale)/(m_e*q_e*epsi_0)*/ 295 296 /* Remove constant from rho */ 297 PetscCall(VecGetSize(rho, &n)); 298 PetscCall(VecSum(rho, &sum)); 299 PetscCall(VecShift(rho, -sum / n)); 300 PetscCall(VecSum(rho, &sum)); 301 PetscCheck(PetscAbsScalar(sum) < chargeTol, PetscObjectComm((PetscObject)sw), PETSC_ERR_PLIB, "Charge should have no DC component: %g", (double)PetscRealPart(sum)); 302 /* Nondimensionalize rho */ 303 PetscCall(VecScale(rho, phi_0)); 304 } 305 PetscCall(VecViewFromOptions(rho, NULL, "-poisson_rho_view")); 306 307 PetscCall(DMGetGlobalVector(dm, &phi)); 308 PetscCall(PetscObjectSetName((PetscObject)phi, "potential")); 309 PetscCall(VecSet(phi, 0.0)); 310 PetscCall(SNESSolve(snes, rho, phi)); 311 PetscCall(DMRestoreGlobalVector(dm, &rho)); 312 PetscCall(VecViewFromOptions(phi, NULL, "-phi_view")); 313 314 PetscCall(DMGetLocalVector(dm, &locPhi)); 315 PetscCall(DMGlobalToLocalBegin(dm, phi, INSERT_VALUES, locPhi)); 316 PetscCall(DMGlobalToLocalEnd(dm, phi, INSERT_VALUES, locPhi)); 317 PetscCall(DMRestoreGlobalVector(dm, &phi)); 318 319 PetscCall(DMGetDS(dm, &ds)); 320 PetscCall(PetscDSGetDiscretization(ds, 0, (PetscObject *)&fe)); 321 PetscCall(DMSwarmSortGetAccess(sw)); 322 PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd)); 323 PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords)); 324 for (c = cStart; c < cEnd; ++c) { 325 PetscTabulation tab; 326 PetscScalar *clPhi = NULL; 327 PetscReal *pcoord, *refcoord; 328 PetscReal v[3], J[9], invJ[9], detJ; 329 PetscInt *points; 330 PetscInt Ncp, cp; 331 332 PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Ncp, &points)); 333 PetscCall(DMGetWorkArray(dm, Ncp * dim, MPIU_REAL, &pcoord)); 334 PetscCall(DMGetWorkArray(dm, Ncp * dim, MPIU_REAL, &refcoord)); 335 for (cp = 0; cp < Ncp; ++cp) 336 for (d = 0; d < dim; ++d) pcoord[cp * dim + d] = coords[points[cp] * dim + d]; 337 PetscCall(DMPlexCoordinatesToReference(dm, c, Ncp, pcoord, refcoord)); 338 PetscCall(PetscFECreateTabulation(fe, 1, Ncp, refcoord, 1, &tab)); 339 PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, v, J, invJ, &detJ)); 340 PetscCall(DMPlexVecGetClosure(dm, NULL, locPhi, c, NULL, &clPhi)); 341 for (cp = 0; cp < Ncp; ++cp) { 342 const PetscReal *basisDer = tab->T[1]; 343 const PetscInt p = points[cp]; 344 345 for (d = 0; d < dim; ++d) E[p * dim + d] = 0.; 346 PetscCall(PetscFEFreeInterpolateGradient_Static(fe, basisDer, clPhi, dim, invJ, NULL, cp, &E[p * dim])); 347 for (d = 0; d < dim; ++d) E[p * dim + d] *= -1.0; 348 } 349 PetscCall(DMPlexVecRestoreClosure(dm, NULL, locPhi, c, NULL, &clPhi)); 350 PetscCall(DMRestoreWorkArray(dm, Ncp * dim, MPIU_REAL, &pcoord)); 351 PetscCall(DMRestoreWorkArray(dm, Ncp * dim, MPIU_REAL, &refcoord)); 352 PetscCall(PetscTabulationDestroy(&tab)); 353 PetscCall(PetscFree(points)); 354 } 355 PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords)); 356 PetscCall(DMSwarmSortRestoreAccess(sw)); 357 PetscCall(DMRestoreLocalVector(dm, &locPhi)); 358 PetscFunctionReturn(PETSC_SUCCESS); 359 } 360 361 static PetscErrorCode ComputeFieldAtParticles_Mixed(SNES snes, DM sw, PetscReal E[]) 362 { 363 DM dm, potential_dm; 364 IS potential_IS; 365 PetscDS ds; 366 PetscFE fe; 367 PetscFEGeom feGeometry; 368 Mat M_p; 369 Vec phi, locPhi, rho, f, temp_rho; 370 PetscQuadrature q; 371 PetscReal *coords, chargeTol = 1e-13; 372 PetscInt dim, d, cStart, cEnd, c, Np, fields = 1; 373 char oldField[PETSC_MAX_PATH_LEN]; 374 const char *tmp; 375 376 PetscFunctionBegin; 377 PetscCall(DMGetDimension(sw, &dim)); 378 PetscCall(DMSwarmGetLocalSize(sw, &Np)); 379 380 /* Create the charges rho */ 381 PetscCall(SNESGetDM(snes, &dm)); 382 PetscCall(DMGetGlobalVector(dm, &rho)); 383 PetscCall(PetscObjectSetName((PetscObject)rho, "rho")); 384 PetscCall(DMCreateSubDM(dm, 1, &fields, &potential_IS, &potential_dm)); 385 386 PetscCall(DMSwarmVectorGetField(sw, &tmp)); 387 PetscCall(PetscStrncpy(oldField, tmp, PETSC_MAX_PATH_LEN)); 388 PetscCall(DMSwarmVectorDefineField(sw, "w_q")); 389 PetscCall(DMCreateMassMatrix(sw, potential_dm, &M_p)); 390 PetscCall(DMSwarmVectorDefineField(sw, oldField)); 391 392 PetscCall(MatViewFromOptions(M_p, NULL, "-mp_view")); 393 PetscCall(DMGetGlobalVector(potential_dm, &temp_rho)); 394 PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "w_q", &f)); 395 PetscCall(PetscObjectSetName((PetscObject)f, "particle weight")); 396 PetscCall(VecViewFromOptions(f, NULL, "-weights_view")); 397 PetscCall(MatMultTranspose(M_p, f, temp_rho)); 398 PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "w_q", &f)); 399 PetscCall(MatDestroy(&M_p)); 400 PetscCall(PetscObjectSetName((PetscObject)rho, "rho")); 401 PetscCall(VecViewFromOptions(rho, NULL, "-poisson_rho_view")); 402 PetscCall(VecISCopy(rho, potential_IS, SCATTER_FORWARD, temp_rho)); 403 PetscCall(DMRestoreGlobalVector(potential_dm, &temp_rho)); 404 PetscCall(DMDestroy(&potential_dm)); 405 PetscCall(ISDestroy(&potential_IS)); 406 { 407 PetscScalar sum; 408 PetscInt n; 409 PetscReal phi_0 = 1.; /*(sigma*sigma*sigma)*(timeScale*timeScale)/(m_e*q_e*epsi_0);*/ 410 411 /*Remove constant from rho*/ 412 PetscCall(VecGetSize(rho, &n)); 413 PetscCall(VecSum(rho, &sum)); 414 PetscCall(VecShift(rho, -sum / n)); 415 PetscCall(VecSum(rho, &sum)); 416 PetscCheck(PetscAbsScalar(sum) < chargeTol, PetscObjectComm((PetscObject)sw), PETSC_ERR_PLIB, "Charge should have no DC component: %g", (double)PetscRealPart(sum)); 417 /* Nondimensionalize rho */ 418 PetscCall(VecScale(rho, phi_0)); 419 } 420 PetscCall(DMGetGlobalVector(dm, &phi)); 421 PetscCall(PetscObjectSetName((PetscObject)phi, "potential")); 422 PetscCall(VecSet(phi, 0.0)); 423 PetscCall(SNESSolve(snes, NULL, phi)); 424 PetscCall(DMRestoreGlobalVector(dm, &rho)); 425 PetscCall(VecViewFromOptions(phi, NULL, "-phi_view")); 426 427 PetscCall(DMGetLocalVector(dm, &locPhi)); 428 PetscCall(DMGlobalToLocalBegin(dm, phi, INSERT_VALUES, locPhi)); 429 PetscCall(DMGlobalToLocalEnd(dm, phi, INSERT_VALUES, locPhi)); 430 PetscCall(DMRestoreGlobalVector(dm, &phi)); 431 432 PetscCall(DMGetDS(dm, &ds)); 433 PetscCall(PetscDSGetDiscretization(ds, 0, (PetscObject *)&fe)); 434 PetscCall(DMSwarmSortGetAccess(sw)); 435 PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd)); 436 PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords)); 437 for (c = cStart; c < cEnd; ++c) { 438 PetscTabulation tab; 439 PetscScalar *clPhi = NULL; 440 PetscReal *pcoord, *refcoord; 441 PetscReal v[3], J[9], invJ[9], detJ; 442 PetscInt *points; 443 PetscInt Ncp, cp; 444 445 PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Ncp, &points)); 446 PetscCall(DMGetWorkArray(dm, Ncp * dim, MPIU_REAL, &pcoord)); 447 PetscCall(DMGetWorkArray(dm, Ncp * dim, MPIU_REAL, &refcoord)); 448 for (cp = 0; cp < Ncp; ++cp) 449 for (d = 0; d < dim; ++d) pcoord[cp * dim + d] = coords[points[cp] * dim + d]; 450 PetscCall(DMPlexCoordinatesToReference(dm, c, Ncp, pcoord, refcoord)); 451 PetscCall(PetscFECreateTabulation(fe, 1, Ncp, refcoord, 1, &tab)); 452 PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, v, J, invJ, &detJ)); 453 PetscCall(DMPlexVecGetClosure(dm, NULL, locPhi, c, NULL, &clPhi)); 454 for (cp = 0; cp < Ncp; ++cp) { 455 const PetscInt p = points[cp]; 456 457 for (d = 0; d < dim; ++d) E[p * dim + d] = 0.; 458 PetscCall(PetscFEGetQuadrature(fe, &q)); 459 PetscCall(PetscFECreateCellGeometry(fe, q, &feGeometry)); 460 PetscCall(PetscFEInterpolateAtPoints_Static(fe, tab, clPhi, &feGeometry, cp, &E[p * dim])); 461 PetscCall(PetscFEDestroyCellGeometry(fe, &feGeometry)); 462 } 463 PetscCall(DMPlexVecRestoreClosure(dm, NULL, locPhi, c, NULL, &clPhi)); 464 PetscCall(DMRestoreWorkArray(dm, Ncp * dim, MPIU_REAL, &pcoord)); 465 PetscCall(DMRestoreWorkArray(dm, Ncp * dim, MPIU_REAL, &refcoord)); 466 PetscCall(PetscTabulationDestroy(&tab)); 467 PetscCall(PetscFree(points)); 468 } 469 PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords)); 470 PetscCall(DMSwarmSortRestoreAccess(sw)); 471 PetscCall(DMRestoreLocalVector(dm, &locPhi)); 472 PetscFunctionReturn(PETSC_SUCCESS); 473 } 474 475 static PetscErrorCode ComputeFieldAtParticles(SNES snes, DM sw, PetscReal E[]) 476 { 477 AppCtx *ctx; 478 PetscInt dim, Np; 479 480 PetscFunctionBegin; 481 PetscValidHeaderSpecific(snes, SNES_CLASSID, 1); 482 PetscValidHeaderSpecific(sw, DM_CLASSID, 2); 483 PetscAssertPointer(E, 3); 484 PetscCall(DMGetDimension(sw, &dim)); 485 PetscCall(DMSwarmGetLocalSize(sw, &Np)); 486 PetscCall(DMGetApplicationContext(sw, &ctx)); 487 PetscCall(PetscArrayzero(E, Np * dim)); 488 489 switch (ctx->em) { 490 case EM_PRIMAL: 491 PetscCall(ComputeFieldAtParticles_Primal(snes, sw, E)); 492 break; 493 case EM_COULOMB: 494 PetscCall(ComputeFieldAtParticles_Coulomb(snes, sw, E)); 495 break; 496 case EM_MIXED: 497 PetscCall(ComputeFieldAtParticles_Mixed(snes, sw, E)); 498 break; 499 case EM_NONE: 500 break; 501 default: 502 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "No solver for electrostatic model %s", EMTypes[ctx->em]); 503 } 504 505 PetscFunctionReturn(PETSC_SUCCESS); 506 } 507 508 static PetscErrorCode RHSFunction(TS ts, PetscReal t, Vec U, Vec G, void *ctx) 509 { 510 DM sw; 511 SNES snes = ((AppCtx *)ctx)->snes; 512 const PetscReal *coords, *vel; 513 const PetscScalar *u; 514 PetscScalar *g; 515 PetscReal *E; 516 PetscInt dim, d, Np, p; 517 518 PetscFunctionBeginUser; 519 PetscCall(TSGetDM(ts, &sw)); 520 PetscCall(DMGetDimension(sw, &dim)); 521 PetscCall(DMSwarmGetField(sw, "initCoordinates", NULL, NULL, (void **)&coords)); 522 PetscCall(DMSwarmGetField(sw, "initVelocity", NULL, NULL, (void **)&vel)); 523 PetscCall(DMSwarmGetField(sw, "E_field", NULL, NULL, (void **)&E)); 524 PetscCall(VecGetLocalSize(U, &Np)); 525 PetscCall(VecGetArrayRead(U, &u)); 526 PetscCall(VecGetArray(G, &g)); 527 528 PetscCall(ComputeFieldAtParticles(snes, sw, E)); 529 530 Np /= 2 * dim; 531 for (p = 0; p < Np; ++p) { 532 const PetscReal x0 = coords[p * dim + 0]; 533 const PetscReal vy0 = vel[p * dim + 1]; 534 const PetscReal omega = vy0 / x0; 535 536 for (d = 0; d < dim; ++d) { 537 g[(p * 2 + 0) * dim + d] = u[(p * 2 + 1) * dim + d]; 538 g[(p * 2 + 1) * dim + d] = E[p * dim + d] - PetscSqr(omega) * u[(p * 2 + 0) * dim + d]; 539 } 540 } 541 PetscCall(DMSwarmRestoreField(sw, "initCoordinates", NULL, NULL, (void **)&coords)); 542 PetscCall(DMSwarmRestoreField(sw, "initVelocity", NULL, NULL, (void **)&vel)); 543 PetscCall(DMSwarmRestoreField(sw, "E_field", NULL, NULL, (void **)&E)); 544 PetscCall(VecRestoreArrayRead(U, &u)); 545 PetscCall(VecRestoreArray(G, &g)); 546 PetscFunctionReturn(PETSC_SUCCESS); 547 } 548 549 /* J_{ij} = dF_i/dx_j 550 J_p = ( 0 1) 551 (-w^2 0) 552 TODO Now there is another term with w^2 from the electric field. I think we will need to invert the operator. 553 Perhaps we can approximate the Jacobian using only the cellwise P-P gradient from Coulomb 554 */ 555 static PetscErrorCode RHSJacobian(TS ts, PetscReal t, Vec U, Mat J, Mat P, void *ctx) 556 { 557 DM sw; 558 const PetscReal *coords, *vel; 559 PetscInt dim, d, Np, p, rStart; 560 561 PetscFunctionBeginUser; 562 PetscCall(TSGetDM(ts, &sw)); 563 PetscCall(DMGetDimension(sw, &dim)); 564 PetscCall(VecGetLocalSize(U, &Np)); 565 PetscCall(MatGetOwnershipRange(J, &rStart, NULL)); 566 PetscCall(DMSwarmGetField(sw, "initCoordinates", NULL, NULL, (void **)&coords)); 567 PetscCall(DMSwarmGetField(sw, "initVelocity", NULL, NULL, (void **)&vel)); 568 Np /= 2 * dim; 569 for (p = 0; p < Np; ++p) { 570 const PetscReal x0 = coords[p * dim + 0]; 571 const PetscReal vy0 = vel[p * dim + 1]; 572 const PetscReal omega = vy0 / x0; 573 PetscScalar vals[4] = {0., 1., -PetscSqr(omega), 0.}; 574 575 for (d = 0; d < dim; ++d) { 576 const PetscInt rows[2] = {(p * 2 + 0) * dim + d + rStart, (p * 2 + 1) * dim + d + rStart}; 577 PetscCall(MatSetValues(J, 2, rows, 2, rows, vals, INSERT_VALUES)); 578 } 579 } 580 PetscCall(DMSwarmRestoreField(sw, "initCoordinates", NULL, NULL, (void **)&coords)); 581 PetscCall(DMSwarmRestoreField(sw, "initVelocity", NULL, NULL, (void **)&vel)); 582 PetscCall(MatAssemblyBegin(J, MAT_FINAL_ASSEMBLY)); 583 PetscCall(MatAssemblyEnd(J, MAT_FINAL_ASSEMBLY)); 584 PetscFunctionReturn(PETSC_SUCCESS); 585 } 586 587 static PetscErrorCode RHSFunctionX(TS ts, PetscReal t, Vec V, Vec Xres, void *ctx) 588 { 589 DM sw; 590 const PetscScalar *v; 591 PetscScalar *xres; 592 PetscInt Np, p, dim, d; 593 594 PetscFunctionBeginUser; 595 PetscCall(TSGetDM(ts, &sw)); 596 PetscCall(DMGetDimension(sw, &dim)); 597 PetscCall(VecGetLocalSize(Xres, &Np)); 598 Np /= dim; 599 PetscCall(VecGetArrayRead(V, &v)); 600 PetscCall(VecGetArray(Xres, &xres)); 601 for (p = 0; p < Np; ++p) { 602 for (d = 0; d < dim; ++d) xres[p * dim + d] = v[p * dim + d]; 603 } 604 PetscCall(VecRestoreArrayRead(V, &v)); 605 PetscCall(VecRestoreArray(Xres, &xres)); 606 PetscFunctionReturn(PETSC_SUCCESS); 607 } 608 609 static PetscErrorCode RHSFunctionV(TS ts, PetscReal t, Vec X, Vec Vres, void *ctx) 610 { 611 DM sw; 612 SNES snes = ((AppCtx *)ctx)->snes; 613 const PetscScalar *x; 614 const PetscReal *coords, *vel; 615 PetscScalar *vres; 616 PetscReal *E; 617 PetscInt Np, p, dim, d; 618 619 PetscFunctionBeginUser; 620 PetscCall(TSGetDM(ts, &sw)); 621 PetscCall(DMGetDimension(sw, &dim)); 622 PetscCall(DMSwarmGetField(sw, "initCoordinates", NULL, NULL, (void **)&coords)); 623 PetscCall(DMSwarmGetField(sw, "initVelocity", NULL, NULL, (void **)&vel)); 624 PetscCall(DMSwarmGetField(sw, "E_field", NULL, NULL, (void **)&E)); 625 PetscCall(VecGetLocalSize(Vres, &Np)); 626 PetscCall(VecGetArrayRead(X, &x)); 627 PetscCall(VecGetArray(Vres, &vres)); 628 PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Dimension must be 2"); 629 630 PetscCall(ComputeFieldAtParticles(snes, sw, E)); 631 632 Np /= dim; 633 for (p = 0; p < Np; ++p) { 634 const PetscReal x0 = coords[p * dim + 0]; 635 const PetscReal vy0 = vel[p * dim + 1]; 636 const PetscReal omega = vy0 / x0; 637 638 for (d = 0; d < dim; ++d) vres[p * dim + d] = E[p * dim + d] - PetscSqr(omega) * x[p * dim + d]; 639 } 640 PetscCall(VecRestoreArrayRead(X, &x)); 641 PetscCall(VecRestoreArray(Vres, &vres)); 642 PetscCall(DMSwarmRestoreField(sw, "initCoordinates", NULL, NULL, (void **)&coords)); 643 PetscCall(DMSwarmRestoreField(sw, "initVelocity", NULL, NULL, (void **)&vel)); 644 PetscCall(DMSwarmRestoreField(sw, "E_field", NULL, NULL, (void **)&E)); 645 PetscFunctionReturn(PETSC_SUCCESS); 646 } 647 648 static PetscErrorCode CreateSolution(TS ts) 649 { 650 DM sw; 651 Vec u; 652 PetscInt dim, Np; 653 654 PetscFunctionBegin; 655 PetscCall(TSGetDM(ts, &sw)); 656 PetscCall(DMGetDimension(sw, &dim)); 657 PetscCall(DMSwarmGetLocalSize(sw, &Np)); 658 PetscCall(VecCreate(PETSC_COMM_WORLD, &u)); 659 PetscCall(VecSetBlockSize(u, dim)); 660 PetscCall(VecSetSizes(u, 2 * Np * dim, PETSC_DECIDE)); 661 PetscCall(VecSetUp(u)); 662 PetscCall(TSSetSolution(ts, u)); 663 PetscCall(VecDestroy(&u)); 664 PetscFunctionReturn(PETSC_SUCCESS); 665 } 666 667 static PetscErrorCode SetProblem(TS ts) 668 { 669 AppCtx *user; 670 DM sw; 671 672 PetscFunctionBegin; 673 PetscCall(TSGetDM(ts, &sw)); 674 PetscCall(DMGetApplicationContext(sw, (void **)&user)); 675 // Define unified system for (X, V) 676 { 677 Mat J; 678 PetscInt dim, Np; 679 680 PetscCall(DMGetDimension(sw, &dim)); 681 PetscCall(DMSwarmGetLocalSize(sw, &Np)); 682 PetscCall(MatCreate(PETSC_COMM_WORLD, &J)); 683 PetscCall(MatSetSizes(J, 2 * Np * dim, 2 * Np * dim, PETSC_DECIDE, PETSC_DECIDE)); 684 PetscCall(MatSetBlockSize(J, 2 * dim)); 685 PetscCall(MatSetFromOptions(J)); 686 PetscCall(MatSetUp(J)); 687 PetscCall(TSSetRHSFunction(ts, NULL, RHSFunction, user)); 688 PetscCall(TSSetRHSJacobian(ts, J, J, RHSJacobian, user)); 689 PetscCall(MatDestroy(&J)); 690 } 691 /* Define split system for X and V */ 692 { 693 Vec u; 694 IS isx, isv, istmp; 695 const PetscInt *idx; 696 PetscInt dim, Np, rstart; 697 698 PetscCall(TSGetSolution(ts, &u)); 699 PetscCall(DMGetDimension(sw, &dim)); 700 PetscCall(DMSwarmGetLocalSize(sw, &Np)); 701 PetscCall(VecGetOwnershipRange(u, &rstart, NULL)); 702 PetscCall(ISCreateStride(PETSC_COMM_WORLD, Np, (rstart / dim) + 0, 2, &istmp)); 703 PetscCall(ISGetIndices(istmp, &idx)); 704 PetscCall(ISCreateBlock(PETSC_COMM_WORLD, dim, Np, idx, PETSC_COPY_VALUES, &isx)); 705 PetscCall(ISRestoreIndices(istmp, &idx)); 706 PetscCall(ISDestroy(&istmp)); 707 PetscCall(ISCreateStride(PETSC_COMM_WORLD, Np, (rstart / dim) + 1, 2, &istmp)); 708 PetscCall(ISGetIndices(istmp, &idx)); 709 PetscCall(ISCreateBlock(PETSC_COMM_WORLD, dim, Np, idx, PETSC_COPY_VALUES, &isv)); 710 PetscCall(ISRestoreIndices(istmp, &idx)); 711 PetscCall(ISDestroy(&istmp)); 712 PetscCall(TSRHSSplitSetIS(ts, "position", isx)); 713 PetscCall(TSRHSSplitSetIS(ts, "momentum", isv)); 714 PetscCall(ISDestroy(&isx)); 715 PetscCall(ISDestroy(&isv)); 716 PetscCall(TSRHSSplitSetRHSFunction(ts, "position", NULL, RHSFunctionX, user)); 717 PetscCall(TSRHSSplitSetRHSFunction(ts, "momentum", NULL, RHSFunctionV, user)); 718 } 719 PetscFunctionReturn(PETSC_SUCCESS); 720 } 721 722 PetscErrorCode circleSingleX(PetscInt dim, PetscReal time, const PetscReal dummy[], PetscInt p, PetscScalar x[], void *ctx) 723 { 724 x[0] = p + 1.; 725 x[1] = 0.; 726 return PETSC_SUCCESS; 727 } 728 729 PetscErrorCode circleSingleV(PetscInt dim, PetscReal time, const PetscReal dummy[], PetscInt p, PetscScalar v[], void *ctx) 730 { 731 v[0] = 0.; 732 v[1] = PetscSqrtReal(1000. / (p + 1.)); 733 return PETSC_SUCCESS; 734 } 735 736 /* Put 5 particles into each circle */ 737 PetscErrorCode circleMultipleX(PetscInt dim, PetscReal time, const PetscReal dummy[], PetscInt p, PetscScalar x[], void *ctx) 738 { 739 const PetscInt n = 5; 740 const PetscReal r0 = (p / n) + 1.; 741 const PetscReal th0 = (2. * PETSC_PI * (p % n)) / n; 742 743 x[0] = r0 * PetscCosReal(th0); 744 x[1] = r0 * PetscSinReal(th0); 745 return PETSC_SUCCESS; 746 } 747 748 /* Put 5 particles into each circle */ 749 PetscErrorCode circleMultipleV(PetscInt dim, PetscReal time, const PetscReal dummy[], PetscInt p, PetscScalar v[], void *ctx) 750 { 751 const PetscInt n = 5; 752 const PetscReal r0 = (p / n) + 1.; 753 const PetscReal th0 = (2. * PETSC_PI * (p % n)) / n; 754 const PetscReal omega = PetscSqrtReal(1000. / r0) / r0; 755 756 v[0] = -r0 * omega * PetscSinReal(th0); 757 v[1] = r0 * omega * PetscCosReal(th0); 758 return PETSC_SUCCESS; 759 } 760 761 /* 762 InitializeSolveAndSwarm - Set the solution values to the swarm coordinates and velocities, and also possibly set the initial values. 763 764 Input Parameters: 765 + ts - The TS 766 - useInitial - Flag to also set the initial conditions to the current coordinates and velocities and setup the problem 767 768 Output Parameter: 769 . u - The initialized solution vector 770 771 Level: advanced 772 773 .seealso: InitializeSolve() 774 */ 775 static PetscErrorCode InitializeSolveAndSwarm(TS ts, PetscBool useInitial) 776 { 777 DM sw; 778 Vec u, gc, gv, gc0, gv0; 779 IS isx, isv; 780 AppCtx *user; 781 782 PetscFunctionBeginUser; 783 PetscCall(TSGetDM(ts, &sw)); 784 PetscCall(DMGetApplicationContext(sw, &user)); 785 if (useInitial) { 786 PetscReal v0[1] = {1.}; 787 788 PetscCall(DMSwarmInitializeCoordinates(sw)); 789 PetscCall(DMSwarmInitializeVelocitiesFromOptions(sw, v0)); 790 PetscCall(DMSwarmMigrate(sw, PETSC_TRUE)); 791 PetscCall(TSReset(ts)); 792 PetscCall(CreateSolution(ts)); 793 PetscCall(SetProblem(ts)); 794 } 795 PetscCall(TSGetSolution(ts, &u)); 796 PetscCall(TSRHSSplitGetIS(ts, "position", &isx)); 797 PetscCall(TSRHSSplitGetIS(ts, "momentum", &isv)); 798 PetscCall(DMSwarmCreateGlobalVectorFromField(sw, DMSwarmPICField_coor, &gc)); 799 PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "initCoordinates", &gc0)); 800 if (useInitial) PetscCall(VecCopy(gc, gc0)); 801 PetscCall(VecISCopy(u, isx, SCATTER_FORWARD, gc)); 802 PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, DMSwarmPICField_coor, &gc)); 803 PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "initCoordinates", &gc0)); 804 PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "velocity", &gv)); 805 PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "initVelocity", &gv0)); 806 if (useInitial) PetscCall(VecCopy(gv, gv0)); 807 PetscCall(VecISCopy(u, isv, SCATTER_FORWARD, gv)); 808 PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "velocity", &gv)); 809 PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "initVelocity", &gv0)); 810 PetscFunctionReturn(PETSC_SUCCESS); 811 } 812 813 static PetscErrorCode InitializeSolve(TS ts, Vec u) 814 { 815 PetscFunctionBegin; 816 PetscCall(TSSetSolution(ts, u)); 817 PetscCall(InitializeSolveAndSwarm(ts, PETSC_TRUE)); 818 PetscFunctionReturn(PETSC_SUCCESS); 819 } 820 821 static PetscErrorCode ComputeError(TS ts, Vec U, Vec E) 822 { 823 MPI_Comm comm; 824 DM sw; 825 AppCtx *user; 826 const PetscScalar *u; 827 const PetscReal *coords, *vel; 828 PetscScalar *e; 829 PetscReal t; 830 PetscInt dim, Np, p; 831 832 PetscFunctionBeginUser; 833 PetscCall(PetscObjectGetComm((PetscObject)ts, &comm)); 834 PetscCall(TSGetDM(ts, &sw)); 835 PetscCall(DMGetApplicationContext(sw, &user)); 836 PetscCall(DMGetDimension(sw, &dim)); 837 PetscCall(TSGetSolveTime(ts, &t)); 838 PetscCall(VecGetArray(E, &e)); 839 PetscCall(VecGetArrayRead(U, &u)); 840 PetscCall(VecGetLocalSize(U, &Np)); 841 PetscCall(DMSwarmGetField(sw, "initCoordinates", NULL, NULL, (void **)&coords)); 842 PetscCall(DMSwarmGetField(sw, "initVelocity", NULL, NULL, (void **)&vel)); 843 Np /= 2 * dim; 844 for (p = 0; p < Np; ++p) { 845 /* TODO generalize initial conditions and project into plane instead of assuming x-y */ 846 const PetscReal r0 = DMPlex_NormD_Internal(dim, &coords[p * dim]); 847 const PetscReal th0 = PetscAtan2Real(coords[p * dim + 1], coords[p * dim + 0]); 848 const PetscReal v0 = DMPlex_NormD_Internal(dim, &vel[p * dim]); 849 const PetscReal omega = v0 / r0; 850 const PetscReal ct = PetscCosReal(omega * t + th0); 851 const PetscReal st = PetscSinReal(omega * t + th0); 852 const PetscScalar *x = &u[(p * 2 + 0) * dim]; 853 const PetscScalar *v = &u[(p * 2 + 1) * dim]; 854 const PetscReal xe[3] = {r0 * ct, r0 * st, 0.0}; 855 const PetscReal ve[3] = {-v0 * st, v0 * ct, 0.0}; 856 PetscInt d; 857 858 for (d = 0; d < dim; ++d) { 859 e[(p * 2 + 0) * dim + d] = x[d] - xe[d]; 860 e[(p * 2 + 1) * dim + d] = v[d] - ve[d]; 861 } 862 if (user->error) { 863 const PetscReal en = 0.5 * DMPlex_DotRealD_Internal(dim, v, v); 864 const PetscReal exen = 0.5 * PetscSqr(v0); 865 PetscCall(PetscPrintf(comm, "t %.4g: p%" PetscInt_FMT " error [%.2f %.2f] sol [(%.6lf %.6lf) (%.6lf %.6lf)] exact [(%.6lf %.6lf) (%.6lf %.6lf)] energy/exact energy %g / %g (%.10lf%%)\n", (double)t, p, (double)DMPlex_NormD_Internal(dim, &e[(p * 2 + 0) * dim]), (double)DMPlex_NormD_Internal(dim, &e[(p * 2 + 1) * dim]), (double)x[0], (double)x[1], (double)v[0], (double)v[1], (double)xe[0], (double)xe[1], (double)ve[0], (double)ve[1], (double)en, (double)exen, (double)(PetscAbsReal(exen - en) * 100. / exen))); 866 } 867 } 868 PetscCall(DMSwarmRestoreField(sw, "initCoordinates", NULL, NULL, (void **)&coords)); 869 PetscCall(DMSwarmRestoreField(sw, "initVelocity", NULL, NULL, (void **)&vel)); 870 PetscCall(VecRestoreArrayRead(U, &u)); 871 PetscCall(VecRestoreArray(E, &e)); 872 PetscFunctionReturn(PETSC_SUCCESS); 873 } 874 875 static PetscErrorCode EnergyMonitor(TS ts, PetscInt step, PetscReal t, Vec U, void *ctx) 876 { 877 const PetscInt ostep = ((AppCtx *)ctx)->ostep; 878 const EMType em = ((AppCtx *)ctx)->em; 879 DM sw; 880 const PetscScalar *u; 881 PetscReal *coords, *E; 882 PetscReal enKin = 0., enEM = 0.; 883 PetscInt dim, d, Np, p, q; 884 885 PetscFunctionBeginUser; 886 if (step % ostep == 0) { 887 PetscCall(TSGetDM(ts, &sw)); 888 PetscCall(DMGetDimension(sw, &dim)); 889 PetscCall(VecGetArrayRead(U, &u)); 890 PetscCall(VecGetLocalSize(U, &Np)); 891 Np /= 2 * dim; 892 PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords)); 893 PetscCall(DMSwarmGetField(sw, "E_field", NULL, NULL, (void **)&E)); 894 if (!step) PetscCall(PetscPrintf(PetscObjectComm((PetscObject)ts), "Time Step Part Energy\n")); 895 for (p = 0; p < Np; ++p) { 896 const PetscReal v2 = DMPlex_DotRealD_Internal(dim, &u[(p * 2 + 1) * dim], &u[(p * 2 + 1) * dim]); 897 PetscReal *pcoord = &coords[p * dim]; 898 899 PetscCall(PetscSynchronizedPrintf(PetscObjectComm((PetscObject)ts), "%.6lf %4" PetscInt_FMT " %5" PetscInt_FMT " %10.4lf\n", (double)t, step, p, (double)(0.5 * v2))); 900 enKin += 0.5 * v2; 901 if (em == EM_NONE) { 902 continue; 903 } else if (em == EM_COULOMB) { 904 for (q = p + 1; q < Np; ++q) { 905 PetscReal *qcoord = &coords[q * dim]; 906 PetscReal rpq[3], r; 907 for (d = 0; d < dim; ++d) rpq[d] = pcoord[d] - qcoord[d]; 908 r = DMPlex_NormD_Internal(dim, rpq); 909 enEM += 1. / r; 910 } 911 } else if (em == EM_PRIMAL || em == EM_MIXED) { 912 for (d = 0; d < dim; ++d) enEM += E[p * dim + d]; 913 } 914 } 915 PetscCall(PetscSynchronizedPrintf(PetscObjectComm((PetscObject)ts), "%.6lf %4" PetscInt_FMT " KE\t %10.4lf\n", (double)t, step, (double)enKin)); 916 PetscCall(PetscSynchronizedPrintf(PetscObjectComm((PetscObject)ts), "%.6lf %4" PetscInt_FMT " PE\t %1.10g\n", (double)t, step, (double)enEM)); 917 PetscCall(PetscSynchronizedPrintf(PetscObjectComm((PetscObject)ts), "%.6lf %4" PetscInt_FMT " E\t %10.4lf\n", (double)t, step, (double)(enKin + enEM))); 918 PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords)); 919 PetscCall(DMSwarmRestoreField(sw, "E_field", NULL, NULL, (void **)&E)); 920 PetscCall(PetscSynchronizedFlush(PetscObjectComm((PetscObject)ts), NULL)); 921 PetscCall(VecRestoreArrayRead(U, &u)); 922 } 923 PetscFunctionReturn(PETSC_SUCCESS); 924 } 925 926 static PetscErrorCode SetUpMigrateParticles(TS ts, PetscInt n, PetscReal t, Vec x, PetscBool *flg, void *ctx) 927 { 928 DM sw; 929 930 PetscFunctionBeginUser; 931 *flg = PETSC_TRUE; 932 PetscCall(TSGetDM(ts, &sw)); 933 PetscCall(DMViewFromOptions(sw, NULL, "-migrate_view_pre")); 934 { 935 Vec u, gc, gv; 936 IS isx, isv; 937 938 PetscCall(TSGetSolution(ts, &u)); 939 PetscCall(TSRHSSplitGetIS(ts, "position", &isx)); 940 PetscCall(TSRHSSplitGetIS(ts, "momentum", &isv)); 941 PetscCall(DMSwarmCreateGlobalVectorFromField(sw, DMSwarmPICField_coor, &gc)); 942 PetscCall(VecISCopy(u, isx, SCATTER_REVERSE, gc)); 943 PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, DMSwarmPICField_coor, &gc)); 944 PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "velocity", &gv)); 945 PetscCall(VecISCopy(u, isv, SCATTER_REVERSE, gv)); 946 PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "velocity", &gv)); 947 } 948 PetscFunctionReturn(PETSC_SUCCESS); 949 } 950 951 static PetscErrorCode MigrateParticles(TS ts, PetscInt nv, Vec vecsin[], Vec vecsout[], void *ctx) 952 { 953 DM sw; 954 955 PetscFunctionBeginUser; 956 PetscCall(TSGetDM(ts, &sw)); 957 PetscCall(DMSwarmMigrate(sw, PETSC_TRUE)); 958 PetscCall(CreateSolution(ts)); 959 PetscCall(SetProblem(ts)); 960 PetscCall(InitializeSolveAndSwarm(ts, PETSC_FALSE)); 961 PetscFunctionReturn(PETSC_SUCCESS); 962 } 963 964 int main(int argc, char **argv) 965 { 966 DM dm, sw; 967 TS ts; 968 Vec u; 969 AppCtx user; 970 971 PetscCall(PetscInitialize(&argc, &argv, NULL, help)); 972 PetscCall(ProcessOptions(PETSC_COMM_WORLD, &user)); 973 PetscCall(CreateMesh(PETSC_COMM_WORLD, &user, &dm)); 974 PetscCall(CreatePoisson(dm, &user)); 975 PetscCall(CreateSwarm(dm, &user, &sw)); 976 PetscCall(DMSetApplicationContext(sw, &user)); 977 978 PetscCall(TSCreate(PETSC_COMM_WORLD, &ts)); 979 PetscCall(TSSetProblemType(ts, TS_NONLINEAR)); 980 PetscCall(TSSetDM(ts, sw)); 981 PetscCall(TSSetMaxTime(ts, 0.1)); 982 PetscCall(TSSetTimeStep(ts, 0.00001)); 983 PetscCall(TSSetMaxSteps(ts, 100)); 984 PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_MATCHSTEP)); 985 PetscCall(TSMonitorSet(ts, EnergyMonitor, &user, NULL)); 986 PetscCall(TSSetFromOptions(ts)); 987 PetscCall(DMSwarmVectorDefineField(sw, "velocity")); 988 PetscCall(TSSetComputeInitialCondition(ts, InitializeSolve)); 989 PetscCall(TSSetComputeExactError(ts, ComputeError)); 990 PetscCall(TSSetResize(ts, SetUpMigrateParticles, MigrateParticles, NULL)); 991 992 PetscCall(CreateSolution(ts)); 993 PetscCall(TSGetSolution(ts, &u)); 994 PetscCall(TSComputeInitialCondition(ts, u)); 995 PetscCall(TSSolve(ts, NULL)); 996 997 PetscCall(SNESDestroy(&user.snes)); 998 PetscCall(TSDestroy(&ts)); 999 PetscCall(DMDestroy(&sw)); 1000 PetscCall(DMDestroy(&dm)); 1001 PetscCall(PetscFinalize()); 1002 return 0; 1003 } 1004 1005 /*TEST 1006 1007 build: 1008 requires: double !complex 1009 1010 testset: 1011 requires: defined(PETSC_HAVE_EXECUTABLE_EXPORT) 1012 args: -dm_plex_dim 2 -dm_plex_simplex 0 -dm_plex_box_faces 1,1 -dm_plex_box_lower -5,-5 -dm_plex_box_upper 5,5 \ 1013 -dm_swarm_num_particles 2 -dm_swarm_coordinate_function circleSingleX -dm_swarm_velocity_function circleSingleV \ 1014 -ts_type basicsymplectic\ 1015 -dm_view -output_step 50 -ts_dt 0.01 -ts_max_time 10.0 -ts_max_steps 10 1016 test: 1017 suffix: none_bsi_2d_1 1018 args: -ts_basicsymplectic_type 1 -em_type none -error 1019 test: 1020 suffix: none_bsi_2d_2 1021 args: -ts_basicsymplectic_type 2 -em_type none -error 1022 test: 1023 suffix: none_bsi_2d_3 1024 args: -ts_basicsymplectic_type 3 -em_type none -error 1025 test: 1026 suffix: none_bsi_2d_4 1027 args: -ts_basicsymplectic_type 4 -em_type none -error 1028 test: 1029 suffix: coulomb_bsi_2d_1 1030 args: -ts_basicsymplectic_type 1 1031 test: 1032 suffix: coulomb_bsi_2d_2 1033 args: -ts_basicsymplectic_type 2 1034 test: 1035 suffix: coulomb_bsi_2d_3 1036 args: -ts_basicsymplectic_type 3 1037 test: 1038 suffix: coulomb_bsi_2d_4 1039 args: -ts_basicsymplectic_type 4 1040 1041 testset: 1042 requires: defined(PETSC_HAVE_EXECUTABLE_EXPORT) 1043 args: -dm_plex_dim 2 -dm_plex_simplex 0 -dm_plex_box_faces 1,1 -dm_plex_box_lower -5,-5 -dm_plex_box_upper 5,5 \ 1044 -dm_swarm_num_particles 2 -dm_swarm_coordinate_function circleSingleX -dm_swarm_velocity_function circleSingleV \ 1045 -ts_type basicsymplectic\ 1046 -em_type primal -em_pc_type svd\ 1047 -dm_view -output_step 50 -error -ts_dt 0.01 -ts_max_time 10.0 -ts_max_steps 10\ 1048 -petscspace_degree 2 -petscfe_default_quadrature_order 3 -sigma 1.0e-8 -timeScale 2.0e-14 1049 test: 1050 suffix: poisson_bsi_2d_1 1051 args: -ts_basicsymplectic_type 1 1052 test: 1053 suffix: poisson_bsi_2d_2 1054 args: -ts_basicsymplectic_type 2 1055 test: 1056 suffix: poisson_bsi_2d_3 1057 args: -ts_basicsymplectic_type 3 1058 test: 1059 suffix: poisson_bsi_2d_4 1060 args: -ts_basicsymplectic_type 4 1061 1062 testset: 1063 requires: defined(PETSC_HAVE_EXECUTABLE_EXPORT) 1064 args: -dm_swarm_num_particles 2 -dm_swarm_coordinate_function circleSingleX -dm_swarm_velocity_function circleSingleV \ 1065 -ts_type theta -ts_theta_theta 0.5 -ts_convergence_estimate -convest_num_refine 2 \ 1066 -mat_type baij -ksp_error_if_not_converged -em_pc_type svd\ 1067 -dm_view -output_step 50 -error -ts_dt 0.01 -ts_max_time 10.0 -ts_max_steps 10\ 1068 -pc_type svd -sigma 1.0e-8 -timeScale 2.0e-14 1069 test: 1070 suffix: im_2d_0 1071 args: -dm_plex_dim 2 -dm_plex_simplex 0 -dm_plex_box_faces 1,1 -dm_plex_box_lower -5,-5 -dm_plex_box_upper 5,5 1072 1073 testset: 1074 requires: defined(PETSC_HAVE_EXECUTABLE_EXPORT) 1075 args: -dm_plex_dim 2 -dm_plex_simplex 0 -dm_plex_box_faces 10,10 -dm_plex_box_lower -5,-5 -dm_plex_box_upper 5,5 -petscpartitioner_type simple \ 1076 -dm_swarm_num_particles 2 -dm_swarm_coordinate_function circleSingleX -dm_swarm_velocity_function circleSingleV -dm_swarm_num_species 1\ 1077 -ts_type basicsymplectic -ts_convergence_estimate -convest_num_refine 2 \ 1078 -em_snes_type ksponly -em_pc_type svd -em_type primal -petscspace_degree 1\ 1079 -dm_view -output_step 50\ 1080 -pc_type svd -sigma 1.0e-8 -timeScale 2.0e-14 -ts_dt 0.01 -ts_max_time 1.0 -ts_max_steps 10 1081 test: 1082 suffix: bsi_2d_mesh_1 1083 args: -ts_basicsymplectic_type 4 1084 test: 1085 suffix: bsi_2d_mesh_1_par_2 1086 nsize: 2 1087 args: -ts_basicsymplectic_type 4 1088 test: 1089 suffix: bsi_2d_mesh_1_par_3 1090 nsize: 3 1091 args: -ts_basicsymplectic_type 4 1092 test: 1093 suffix: bsi_2d_mesh_1_par_4 1094 nsize: 4 1095 args: -ts_basicsymplectic_type 4 -dm_swarm_num_particles 0,0,2,0 1096 1097 testset: 1098 requires: defined(PETSC_HAVE_EXECUTABLE_EXPORT) 1099 args: -dm_plex_dim 2 -dm_plex_simplex 0 -dm_plex_box_faces 10,10 -dm_plex_box_lower -5,-5 -dm_plex_box_upper 5,5 \ 1100 -dm_swarm_num_particles 10 -dm_swarm_coordinate_function circleMultipleX -dm_swarm_velocity_function circleMultipleV \ 1101 -ts_convergence_estimate -convest_num_refine 2 \ 1102 -em_pc_type lu\ 1103 -dm_view -output_step 50 -error\ 1104 -pc_type svd -sigma 1.0e-8 -timeScale 2.0e-14 -ts_dt 0.01 -ts_max_time 10.0 -ts_max_steps 10 1105 test: 1106 suffix: bsi_2d_multiple_1 1107 args: -ts_type basicsymplectic -ts_basicsymplectic_type 1 1108 test: 1109 suffix: bsi_2d_multiple_2 1110 args: -ts_type basicsymplectic -ts_basicsymplectic_type 2 1111 test: 1112 suffix: bsi_2d_multiple_3 1113 args: -ts_type basicsymplectic -ts_basicsymplectic_type 3 -ts_dt 0.001 1114 test: 1115 suffix: im_2d_multiple_0 1116 args: -ts_type theta -ts_theta_theta 0.5 \ 1117 -mat_type baij -ksp_error_if_not_converged -em_pc_type lu 1118 1119 testset: 1120 requires: defined(PETSC_HAVE_EXECUTABLE_EXPORT) 1121 args: -dm_plex_dim 2 -dm_plex_simplex 0 -dm_plex_box_faces 2,2 -dm_plex_box_lower -5,-5 -dm_plex_box_upper 5,5 \ 1122 -dm_swarm_num_particles 2 -dm_swarm_coordinate_function circleSingleX -dm_swarm_velocity_function circleSingleV \ 1123 -em_pc_type fieldsplit -ksp_rtol 1e-10 -em_ksp_type preonly -em_type mixed -em_ksp_error_if_not_converged\ 1124 -dm_view -output_step 50 -error -dm_refine 0\ 1125 -pc_type svd -sigma 1.0e-8 -timeScale 2.0e-14 -ts_dt 0.01 -ts_max_time 10.0 -ts_max_steps 10 1126 test: 1127 suffix: bsi_4_rt_1 1128 args: -ts_type basicsymplectic -ts_basicsymplectic_type 4\ 1129 -pc_fieldsplit_detect_saddle_point\ 1130 -pc_type fieldsplit\ 1131 -pc_fieldsplit_type schur\ 1132 -pc_fieldsplit_schur_precondition full \ 1133 -field_petscspace_degree 2\ 1134 -field_petscfe_default_quadrature_order 1\ 1135 -field_petscspace_type sum \ 1136 -field_petscspace_variables 2 \ 1137 -field_petscspace_components 2 \ 1138 -field_petscspace_sum_spaces 2 \ 1139 -field_petscspace_sum_concatenate true \ 1140 -field_sumcomp_0_petscspace_variables 2 \ 1141 -field_sumcomp_0_petscspace_type tensor \ 1142 -field_sumcomp_0_petscspace_tensor_spaces 2 \ 1143 -field_sumcomp_0_petscspace_tensor_uniform false \ 1144 -field_sumcomp_0_tensorcomp_0_petscspace_degree 1 \ 1145 -field_sumcomp_0_tensorcomp_1_petscspace_degree 0 \ 1146 -field_sumcomp_1_petscspace_variables 2 \ 1147 -field_sumcomp_1_petscspace_type tensor \ 1148 -field_sumcomp_1_petscspace_tensor_spaces 2 \ 1149 -field_sumcomp_1_petscspace_tensor_uniform false \ 1150 -field_sumcomp_1_tensorcomp_0_petscspace_degree 0 \ 1151 -field_sumcomp_1_tensorcomp_1_petscspace_degree 1 \ 1152 -field_petscdualspace_form_degree -1 \ 1153 -field_petscdualspace_order 1 \ 1154 -field_petscdualspace_lagrange_trimmed true\ 1155 -ksp_gmres_restart 500 1156 1157 TEST*/ 1158