xref: /petsc/src/ts/tutorials/hamiltonian/ex2.c (revision 9072cb8bb45fb6e4a71960af0064634e8a2371f4)
18214e71cSJoe static char help[] = "Landau Damping/Two Stream instability test using Vlasov-Poisson equations\n";
2b80bf5b1SJoe Pusztay 
38214e71cSJoe /*
4*9072cb8bSMatthew G. Knepley   TODO:
5*9072cb8bSMatthew G. Knepley   - Cache mesh geometry
6*9072cb8bSMatthew G. Knepley   - Move electrostatic solver to MG+SVD
7*9072cb8bSMatthew G. Knepley 
88214e71cSJoe   To run the code with particles sinusoidally perturbed in x space use the test "pp_poisson_bsi_1d_4" or "pp_poisson_bsi_2d_4"
98214e71cSJoe   According to Lukas, good damping results come at ~16k particles per cell
108214e71cSJoe 
11*9072cb8bSMatthew G. Knepley   To visualize the maximum electric field use
128214e71cSJoe 
13*9072cb8bSMatthew G. Knepley     -efield_monitor
148214e71cSJoe 
15*9072cb8bSMatthew G. Knepley   To monitor velocity moments of the distribution use
16f1e6e573SMatthew G. Knepley 
17*9072cb8bSMatthew G. Knepley     -ptof_pc_type lu -moments_monitor
18*9072cb8bSMatthew G. Knepley 
19*9072cb8bSMatthew G. Knepley   To monitor the particle positions in phase space use
20*9072cb8bSMatthew G. Knepley 
21*9072cb8bSMatthew G. Knepley     -positions_monitor
22*9072cb8bSMatthew G. Knepley 
23*9072cb8bSMatthew G. Knepley   To monitor the charge density, E field, and potential use
24*9072cb8bSMatthew G. Knepley 
25*9072cb8bSMatthew G. Knepley     -poisson_monitor
26*9072cb8bSMatthew G. Knepley 
27*9072cb8bSMatthew G. Knepley   To monitor the remapping field use
28*9072cb8bSMatthew G. Knepley 
29*9072cb8bSMatthew G. Knepley     -remap_uf_view draw
30f1e6e573SMatthew G. Knepley 
318214e71cSJoe   To visualize the swarm distribution use
328214e71cSJoe 
338214e71cSJoe     -ts_monitor_hg_swarm
348214e71cSJoe 
358214e71cSJoe   To visualize the particles, we can use
368214e71cSJoe 
378214e71cSJoe     -ts_monitor_sp_swarm -ts_monitor_sp_swarm_retain 0 -ts_monitor_sp_swarm_phase 1 -draw_size 500,500
388214e71cSJoe 
3984467f86SMatthew G. Knepley For a Landau Damping verification run, we use
4084467f86SMatthew G. Knepley 
4184467f86SMatthew G. Knepley     -dm_plex_dim 2 -fake_1D -dm_plex_simplex 0 -dm_plex_box_faces 10,1 \
4284467f86SMatthew G. Knepley       -dm_plex_box_lower 0.,-0.5 -dm_plex_box_upper 12.5664,0.5 -dm_plex_box_bd periodic,none \
4384467f86SMatthew G. Knepley     -vdm_plex_dim 1 -vdm_plex_simplex 0 -vdm_plex_box_faces 2000 -vdm_plex_box_lower -10 -vdm_plex_box_upper 10 \
4484467f86SMatthew G. Knepley     -dm_swarm_num_species 1 -charges -1.,1. \
4584467f86SMatthew G. Knepley     -cosine_coefficients 0.01,0.5 -perturbed_weights -total_weight 1. \
4684467f86SMatthew G. Knepley     -ts_type basicsymplectic -ts_basicsymplectic_type 1 -ts_dt 0.03 -ts_max_time 500 -ts_max_steps 500 \
4784467f86SMatthew G. Knepley     -em_type primal -petscspace_degree 1 -em_snes_atol 1.e-12 -em_snes_error_if_not_converged -em_ksp_error_if_not_converged -em_pc_type svd \
48*9072cb8bSMatthew G. Knepley     -output_step 100 -check_vel_res -efield_monitor -ts_monitor -log_view
4984467f86SMatthew G. Knepley 
508214e71cSJoe */
51b80bf5b1SJoe Pusztay #include <petscts.h>
528214e71cSJoe #include <petscdmplex.h>
538214e71cSJoe #include <petscdmswarm.h>
548214e71cSJoe #include <petscfe.h>
558214e71cSJoe #include <petscds.h>
568214e71cSJoe #include <petscbag.h>
578214e71cSJoe #include <petscdraw.h>
588214e71cSJoe #include <petsc/private/dmpleximpl.h>  /* For norm and dot */
598214e71cSJoe #include <petsc/private/petscfeimpl.h> /* For interpolation */
6084467f86SMatthew G. Knepley #include <petsc/private/dmswarmimpl.h> /* For swarm debugging */
618214e71cSJoe #include "petscdm.h"
628214e71cSJoe #include "petscdmlabel.h"
638214e71cSJoe 
648214e71cSJoe PETSC_EXTERN PetscErrorCode stream(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar[], void *);
658214e71cSJoe PETSC_EXTERN PetscErrorCode line(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar[], void *);
668214e71cSJoe 
678214e71cSJoe const char *EMTypes[] = {"primal", "mixed", "coulomb", "none", "EMType", "EM_", NULL};
688214e71cSJoe typedef enum {
698214e71cSJoe   EM_PRIMAL,
708214e71cSJoe   EM_MIXED,
718214e71cSJoe   EM_COULOMB,
728214e71cSJoe   EM_NONE
738214e71cSJoe } EMType;
748214e71cSJoe 
75*9072cb8bSMatthew G. Knepley const char *RemapTypes[] = {"colella", "pfak", "RemapType", "RM_", NULL};
76*9072cb8bSMatthew G. Knepley typedef enum {
77*9072cb8bSMatthew G. Knepley   RM_COLELLA,
78*9072cb8bSMatthew G. Knepley   RM_PFAK,
79*9072cb8bSMatthew G. Knepley } RemapType;
80*9072cb8bSMatthew G. Knepley 
818214e71cSJoe typedef enum {
828214e71cSJoe   V0,
838214e71cSJoe   X0,
848214e71cSJoe   T0,
858214e71cSJoe   M0,
868214e71cSJoe   Q0,
878214e71cSJoe   PHI0,
888214e71cSJoe   POISSON,
898214e71cSJoe   VLASOV,
908214e71cSJoe   SIGMA,
918214e71cSJoe   NUM_CONSTANTS
928214e71cSJoe } ConstantType;
938214e71cSJoe typedef struct {
948214e71cSJoe   PetscScalar v0; /* Velocity scale, often the thermal velocity */
958214e71cSJoe   PetscScalar t0; /* Time scale */
968214e71cSJoe   PetscScalar x0; /* Space scale */
978214e71cSJoe   PetscScalar m0; /* Mass scale */
988214e71cSJoe   PetscScalar q0; /* Charge scale */
998214e71cSJoe   PetscScalar kb;
1008214e71cSJoe   PetscScalar epsi0;
1018214e71cSJoe   PetscScalar phi0;          /* Potential scale */
1028214e71cSJoe   PetscScalar poissonNumber; /* Non-Dimensional Poisson Number */
1038214e71cSJoe   PetscScalar vlasovNumber;  /* Non-Dimensional Vlasov Number */
1048214e71cSJoe   PetscReal   sigma;         /* Nondimensional charge per length in x */
1058214e71cSJoe } Parameter;
106b80bf5b1SJoe Pusztay 
107b80bf5b1SJoe Pusztay typedef struct {
108*9072cb8bSMatthew G. Knepley   PetscBag     bag;               // Problem parameters
109*9072cb8bSMatthew G. Knepley   PetscBool    error;             // Flag for printing the error
110*9072cb8bSMatthew G. Knepley   PetscBool    remap;             // Flag to remap particles
111*9072cb8bSMatthew G. Knepley   RemapType    remapType;         // Remap algorithm
112*9072cb8bSMatthew G. Knepley   PetscInt     remapFreq;         // Number of timesteps between remapping
113*9072cb8bSMatthew G. Knepley   PetscBool    efield_monitor;    // Flag to show electric field monitor
114*9072cb8bSMatthew G. Knepley   PetscBool    moment_monitor;    // Flag to show distribution moment monitor
115*9072cb8bSMatthew G. Knepley   PetscBool    weight_monitor;    // Flag to show weight monitor
116*9072cb8bSMatthew G. Knepley   PetscBool    positions_monitor; // Flag to show particle positins at each time step
117*9072cb8bSMatthew G. Knepley   PetscBool    poisson_monitor;   // Flag to display charge, E field, and potential at each solve
118*9072cb8bSMatthew G. Knepley   PetscBool    initial_monitor;   // Flag to monitor the initial conditions
119*9072cb8bSMatthew G. Knepley   PetscBool    fake_1D;           // Run simulation in 2D but zeroing second dimension
120*9072cb8bSMatthew G. Knepley   PetscBool    perturbed_weights; // Uniformly sample x,v space with gaussian weights
121*9072cb8bSMatthew G. Knepley   PetscInt     ostep;             // Print the energy at each ostep time steps
1228214e71cSJoe   PetscInt     numParticles;
1238214e71cSJoe   PetscReal    timeScale;              /* Nondimensionalizing time scale */
1248214e71cSJoe   PetscReal    charges[2];             /* The charges of each species */
1258214e71cSJoe   PetscReal    masses[2];              /* The masses of each species */
1268214e71cSJoe   PetscReal    thermal_energy[2];      /* Thermal Energy (used to get other constants)*/
1278214e71cSJoe   PetscReal    cosine_coefficients[2]; /*(alpha, k)*/
1288214e71cSJoe   PetscReal    totalWeight;
1298214e71cSJoe   PetscReal    stepSize;
1308214e71cSJoe   PetscInt     steps;
1318214e71cSJoe   PetscReal    initVel;
132f1e6e573SMatthew G. Knepley   EMType       em;     // Type of electrostatic model
133f1e6e573SMatthew G. Knepley   SNES         snes;   // EM solver
134f1e6e573SMatthew G. Knepley   Mat          M;      // The finite element mass matrix
135f1e6e573SMatthew G. Knepley   PetscFEGeom *fegeom; // Geometric information for the DM cells
1368214e71cSJoe   PetscDraw    drawic_x;
1378214e71cSJoe   PetscDraw    drawic_v;
1388214e71cSJoe   PetscDraw    drawic_w;
1398214e71cSJoe   PetscDrawHG  drawhgic_x;
1408214e71cSJoe   PetscDrawHG  drawhgic_v;
1418214e71cSJoe   PetscDrawHG  drawhgic_w;
142*9072cb8bSMatthew G. Knepley   PetscBool    validE;     // Flag to indicate E-field in swarm is valid
14375155f48SMatthew G. Knepley   PetscReal    drawlgEmin; // The minimum lg(E) to plot
14475155f48SMatthew G. Knepley   PetscDrawLG  drawlgE;    // Logarithm of maximum electric field
14575155f48SMatthew G. Knepley   PetscDrawSP  drawspE;    // Electric field at particle positions
14675155f48SMatthew G. Knepley   PetscDrawSP  drawspX;    // Particle positions
14775155f48SMatthew G. Knepley   PetscViewer  viewerRho;  // Charge density viewer
14875155f48SMatthew G. Knepley   PetscViewer  viewerPhi;  // Potential viewer
1498214e71cSJoe   DM           swarm;
1508214e71cSJoe   PetscRandom  random;
1518214e71cSJoe   PetscBool    twostream;
1528214e71cSJoe   PetscBool    checkweights;
1538214e71cSJoe   PetscInt     checkVRes; /* Flag to check/output velocity residuals for nightly tests */
15484467f86SMatthew G. Knepley 
15584467f86SMatthew G. Knepley   PetscLogEvent RhsXEvent, RhsVEvent, ESolveEvent, ETabEvent;
156b80bf5b1SJoe Pusztay } AppCtx;
157b80bf5b1SJoe Pusztay 
158d71ae5a4SJacob Faibussowitsch static PetscErrorCode ProcessOptions(MPI_Comm comm, AppCtx *options)
159d71ae5a4SJacob Faibussowitsch {
160b80bf5b1SJoe Pusztay   PetscFunctionBeginUser;
1618214e71cSJoe   PetscInt d                      = 2;
1628214e71cSJoe   PetscInt maxSpecies             = 2;
1638214e71cSJoe   options->error                  = PETSC_FALSE;
164*9072cb8bSMatthew G. Knepley   options->remap                  = PETSC_FALSE;
165*9072cb8bSMatthew G. Knepley   options->remapType              = RM_COLELLA;
166*9072cb8bSMatthew G. Knepley   options->remapFreq              = 1;
1678214e71cSJoe   options->efield_monitor         = PETSC_FALSE;
168f1e6e573SMatthew G. Knepley   options->moment_monitor         = PETSC_FALSE;
169*9072cb8bSMatthew G. Knepley   options->weight_monitor         = PETSC_FALSE;
1708214e71cSJoe   options->initial_monitor        = PETSC_FALSE;
1718214e71cSJoe   options->fake_1D                = PETSC_FALSE;
1728214e71cSJoe   options->perturbed_weights      = PETSC_FALSE;
1738214e71cSJoe   options->poisson_monitor        = PETSC_FALSE;
174*9072cb8bSMatthew G. Knepley   options->positions_monitor      = PETSC_FALSE;
1758214e71cSJoe   options->ostep                  = 100;
1768214e71cSJoe   options->timeScale              = 2.0e-14;
1778214e71cSJoe   options->charges[0]             = -1.0;
1788214e71cSJoe   options->charges[1]             = 1.0;
1798214e71cSJoe   options->masses[0]              = 1.0;
1808214e71cSJoe   options->masses[1]              = 1000.0;
1818214e71cSJoe   options->thermal_energy[0]      = 1.0;
1828214e71cSJoe   options->thermal_energy[1]      = 1.0;
1838214e71cSJoe   options->cosine_coefficients[0] = 0.01;
1848214e71cSJoe   options->cosine_coefficients[1] = 0.5;
1858214e71cSJoe   options->initVel                = 1;
1868214e71cSJoe   options->totalWeight            = 1.0;
1878214e71cSJoe   options->drawic_x               = NULL;
1888214e71cSJoe   options->drawic_v               = NULL;
1898214e71cSJoe   options->drawic_w               = NULL;
1908214e71cSJoe   options->drawhgic_x             = NULL;
1918214e71cSJoe   options->drawhgic_v             = NULL;
1928214e71cSJoe   options->drawhgic_w             = NULL;
19375155f48SMatthew G. Knepley   options->drawlgEmin             = -6;
19475155f48SMatthew G. Knepley   options->drawlgE                = NULL;
19575155f48SMatthew G. Knepley   options->drawspE                = NULL;
19675155f48SMatthew G. Knepley   options->drawspX                = NULL;
19775155f48SMatthew G. Knepley   options->viewerRho              = NULL;
19875155f48SMatthew G. Knepley   options->viewerPhi              = NULL;
1998214e71cSJoe   options->em                     = EM_COULOMB;
2008214e71cSJoe   options->numParticles           = 32768;
2018214e71cSJoe   options->twostream              = PETSC_FALSE;
2028214e71cSJoe   options->checkweights           = PETSC_FALSE;
2038214e71cSJoe   options->checkVRes              = 0;
204b80bf5b1SJoe Pusztay 
2058214e71cSJoe   PetscOptionsBegin(comm, "", "Landau Damping and Two Stream options", "DMSWARM");
2068214e71cSJoe   PetscCall(PetscOptionsBool("-error", "Flag to print the error", "ex2.c", options->error, &options->error, NULL));
207*9072cb8bSMatthew G. Knepley   PetscCall(PetscOptionsBool("-remap", "Flag to remap the particles", "ex2.c", options->remap, &options->remap, NULL));
208*9072cb8bSMatthew G. Knepley   PetscCall(PetscOptionsInt("-remap_freq", "Number", "ex2.c", options->remapFreq, &options->remapFreq, NULL));
209*9072cb8bSMatthew G. Knepley   PetscCall(PetscOptionsEnum("-remap_type", "Remap algorithm", "ex2.c", RemapTypes, (PetscEnum)options->remapType, (PetscEnum *)&options->remapType, NULL));
210*9072cb8bSMatthew G. Knepley   PetscCall(PetscOptionsBool("-efield_monitor", "Flag to plot log(max E) over time", "ex2.c", options->efield_monitor, &options->efield_monitor, NULL));
211*9072cb8bSMatthew G. Knepley   PetscCall(PetscOptionsReal("-efield_min_monitor", "Minimum E field to plot", "ex2.c", options->drawlgEmin, &options->drawlgEmin, NULL));
212*9072cb8bSMatthew G. Knepley   PetscCall(PetscOptionsBool("-moments_monitor", "Flag to show moments table", "ex2.c", options->moment_monitor, &options->moment_monitor, NULL));
213*9072cb8bSMatthew G. Knepley   PetscCall(PetscOptionsBool("-weight_monitor", "The flag to show particle weights", "ex2.c", options->weight_monitor, &options->weight_monitor, NULL));
214*9072cb8bSMatthew G. Knepley   PetscCall(PetscOptionsBool("-ics_monitor", "Flag to show initial condition histograms", "ex2.c", options->initial_monitor, &options->initial_monitor, NULL));
215*9072cb8bSMatthew G. Knepley   PetscCall(PetscOptionsBool("-positions_monitor", "The flag to show particle positions", "ex2.c", options->positions_monitor, &options->positions_monitor, NULL));
216*9072cb8bSMatthew G. Knepley   PetscCall(PetscOptionsBool("-poisson_monitor", "The flag to show charges, Efield and potential solve", "ex2.c", options->poisson_monitor, &options->poisson_monitor, NULL));
2178214e71cSJoe   PetscCall(PetscOptionsBool("-fake_1D", "Flag to run a 1D simulation (but really in 2D)", "ex2.c", options->fake_1D, &options->fake_1D, NULL));
2188214e71cSJoe   PetscCall(PetscOptionsBool("-twostream", "Run two stream instability", "ex2.c", options->twostream, &options->twostream, NULL));
2198214e71cSJoe   PetscCall(PetscOptionsBool("-perturbed_weights", "Flag to run uniform sampling with perturbed weights", "ex2.c", options->perturbed_weights, &options->perturbed_weights, NULL));
2208214e71cSJoe   PetscCall(PetscOptionsBool("-check_weights", "Ensure all particle weights are positive", "ex2.c", options->checkweights, &options->checkweights, NULL));
2218214e71cSJoe   PetscCall(PetscOptionsInt("-check_vel_res", "Check particle velocity residuals for nightly tests", "ex2.c", options->checkVRes, &options->checkVRes, NULL));
2228214e71cSJoe   PetscCall(PetscOptionsInt("-output_step", "Number of time steps between output", "ex2.c", options->ostep, &options->ostep, NULL));
2238214e71cSJoe   PetscCall(PetscOptionsReal("-timeScale", "Nondimensionalizing time scale", "ex2.c", options->timeScale, &options->timeScale, NULL));
2248214e71cSJoe   PetscCall(PetscOptionsReal("-initial_velocity", "Initial velocity of perturbed particle", "ex2.c", options->initVel, &options->initVel, NULL));
2258214e71cSJoe   PetscCall(PetscOptionsReal("-total_weight", "Total weight of all particles", "ex2.c", options->totalWeight, &options->totalWeight, NULL));
2268214e71cSJoe   PetscCall(PetscOptionsRealArray("-cosine_coefficients", "Amplitude and frequency of cosine equation used in initialization", "ex2.c", options->cosine_coefficients, &d, NULL));
2278214e71cSJoe   PetscCall(PetscOptionsRealArray("-charges", "Species charges", "ex2.c", options->charges, &maxSpecies, NULL));
2288214e71cSJoe   PetscCall(PetscOptionsEnum("-em_type", "Type of electrostatic solver", "ex2.c", EMTypes, (PetscEnum)options->em, (PetscEnum *)&options->em, NULL));
229d0609cedSBarry Smith   PetscOptionsEnd();
23084467f86SMatthew G. Knepley 
23184467f86SMatthew G. Knepley   PetscCall(PetscLogEventRegister("RhsX", TS_CLASSID, &options->RhsXEvent));
23284467f86SMatthew G. Knepley   PetscCall(PetscLogEventRegister("RhsV", TS_CLASSID, &options->RhsVEvent));
23384467f86SMatthew G. Knepley   PetscCall(PetscLogEventRegister("ESolve", TS_CLASSID, &options->ESolveEvent));
23484467f86SMatthew G. Knepley   PetscCall(PetscLogEventRegister("ETab", TS_CLASSID, &options->ETabEvent));
2353ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
236b80bf5b1SJoe Pusztay }
237b80bf5b1SJoe Pusztay 
2388214e71cSJoe static PetscErrorCode SetupContext(DM dm, DM sw, AppCtx *user)
2398214e71cSJoe {
2408214e71cSJoe   PetscFunctionBeginUser;
2418214e71cSJoe   if (user->efield_monitor) {
24275155f48SMatthew G. Knepley     PetscDraw     draw;
24375155f48SMatthew G. Knepley     PetscDrawAxis axis;
24475155f48SMatthew G. Knepley 
24575155f48SMatthew G. Knepley     PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "Max Electric Field", 0, 300, 400, 300, &draw));
24675155f48SMatthew G. Knepley     PetscCall(PetscDrawSetSave(draw, "ex9_Efield"));
24775155f48SMatthew G. Knepley     PetscCall(PetscDrawSetFromOptions(draw));
24875155f48SMatthew G. Knepley     PetscCall(PetscDrawLGCreate(draw, 1, &user->drawlgE));
24975155f48SMatthew G. Knepley     PetscCall(PetscDrawDestroy(&draw));
25075155f48SMatthew G. Knepley     PetscCall(PetscDrawLGGetAxis(user->drawlgE, &axis));
25175155f48SMatthew G. Knepley     PetscCall(PetscDrawAxisSetLabels(axis, "Electron Electric Field", "time", "E_max"));
25275155f48SMatthew G. Knepley     PetscCall(PetscDrawLGSetLimits(user->drawlgE, 0., user->steps * user->stepSize, user->drawlgEmin, 0.));
2538214e71cSJoe   }
2548214e71cSJoe   if (user->initial_monitor) {
2558214e71cSJoe     PetscDrawAxis axis1, axis2, axis3;
2568214e71cSJoe     PetscReal     dmboxlower[2], dmboxupper[2];
2578214e71cSJoe     PetscInt      dim, cStart, cEnd;
2588214e71cSJoe     PetscCall(DMGetDimension(sw, &dim));
2598214e71cSJoe     PetscCall(DMGetBoundingBox(dm, dmboxlower, dmboxupper));
2608214e71cSJoe     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2618214e71cSJoe 
2628214e71cSJoe     PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "monitor_initial_conditions_x", 0, 300, 400, 300, &user->drawic_x));
26375155f48SMatthew G. Knepley     PetscCall(PetscDrawSetSave(user->drawic_x, "ex9_ic_x"));
2648214e71cSJoe     PetscCall(PetscDrawSetFromOptions(user->drawic_x));
2656497c311SBarry Smith     PetscCall(PetscDrawHGCreate(user->drawic_x, (int)dim, &user->drawhgic_x));
2668214e71cSJoe     PetscCall(PetscDrawHGGetAxis(user->drawhgic_x, &axis1));
2676497c311SBarry Smith     PetscCall(PetscDrawHGSetNumberBins(user->drawhgic_x, (int)(cEnd - cStart)));
2688214e71cSJoe     PetscCall(PetscDrawAxisSetLabels(axis1, "Initial X Distribution", "X", "counts"));
2698214e71cSJoe     PetscCall(PetscDrawAxisSetLimits(axis1, dmboxlower[0], dmboxupper[0], 0, 1500));
2708214e71cSJoe 
2718214e71cSJoe     PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "monitor_initial_conditions_v", 400, 300, 400, 300, &user->drawic_v));
27275155f48SMatthew G. Knepley     PetscCall(PetscDrawSetSave(user->drawic_v, "ex9_ic_v"));
2738214e71cSJoe     PetscCall(PetscDrawSetFromOptions(user->drawic_v));
2746497c311SBarry Smith     PetscCall(PetscDrawHGCreate(user->drawic_v, (int)dim, &user->drawhgic_v));
2758214e71cSJoe     PetscCall(PetscDrawHGGetAxis(user->drawhgic_v, &axis2));
2768214e71cSJoe     PetscCall(PetscDrawHGSetNumberBins(user->drawhgic_v, 1000));
2778214e71cSJoe     PetscCall(PetscDrawAxisSetLabels(axis2, "Initial V_x Distribution", "V", "counts"));
2788214e71cSJoe     PetscCall(PetscDrawAxisSetLimits(axis2, -1, 1, 0, 1500));
2798214e71cSJoe 
2808214e71cSJoe     PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "monitor_initial_conditions_w", 800, 300, 400, 300, &user->drawic_w));
28175155f48SMatthew G. Knepley     PetscCall(PetscDrawSetSave(user->drawic_w, "ex9_ic_w"));
2828214e71cSJoe     PetscCall(PetscDrawSetFromOptions(user->drawic_w));
2836497c311SBarry Smith     PetscCall(PetscDrawHGCreate(user->drawic_w, (int)dim, &user->drawhgic_w));
2848214e71cSJoe     PetscCall(PetscDrawHGGetAxis(user->drawhgic_w, &axis3));
2858214e71cSJoe     PetscCall(PetscDrawHGSetNumberBins(user->drawhgic_w, 10));
2868214e71cSJoe     PetscCall(PetscDrawAxisSetLabels(axis3, "Initial W Distribution", "weight", "counts"));
2878214e71cSJoe     PetscCall(PetscDrawAxisSetLimits(axis3, 0, 0.01, 0, 5000));
2888214e71cSJoe   }
289*9072cb8bSMatthew G. Knepley   if (user->positions_monitor) {
29075155f48SMatthew G. Knepley     PetscDraw     draw;
2918214e71cSJoe     PetscDrawAxis axis;
2928214e71cSJoe 
29375155f48SMatthew G. Knepley     PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "Particle Position", 0, 0, 400, 300, &draw));
29475155f48SMatthew G. Knepley     PetscCall(PetscDrawSetSave(draw, "ex9_pos"));
29575155f48SMatthew G. Knepley     PetscCall(PetscDrawSetFromOptions(draw));
29675155f48SMatthew G. Knepley     PetscCall(PetscDrawSPCreate(draw, 10, &user->drawspX));
29775155f48SMatthew G. Knepley     PetscCall(PetscDrawDestroy(&draw));
29875155f48SMatthew G. Knepley     PetscCall(PetscDrawSPSetDimension(user->drawspX, 1));
29975155f48SMatthew G. Knepley     PetscCall(PetscDrawSPGetAxis(user->drawspX, &axis));
3008214e71cSJoe     PetscCall(PetscDrawAxisSetLabels(axis, "Particles", "x", "v"));
30175155f48SMatthew G. Knepley     PetscCall(PetscDrawSPReset(user->drawspX));
3028214e71cSJoe   }
3038214e71cSJoe   if (user->poisson_monitor) {
30475155f48SMatthew G. Knepley     Vec           rho, phi;
30575155f48SMatthew G. Knepley     PetscDraw     draw;
30675155f48SMatthew G. Knepley     PetscDrawAxis axis;
3078214e71cSJoe 
30875155f48SMatthew G. Knepley     PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "Electric_Field", 0, 0, 400, 300, &draw));
30975155f48SMatthew G. Knepley     PetscCall(PetscDrawSetFromOptions(draw));
31075155f48SMatthew G. Knepley     PetscCall(PetscDrawSetSave(draw, "ex9_E_spatial"));
31175155f48SMatthew G. Knepley     PetscCall(PetscDrawSPCreate(draw, 10, &user->drawspE));
31275155f48SMatthew G. Knepley     PetscCall(PetscDrawDestroy(&draw));
31375155f48SMatthew G. Knepley     PetscCall(PetscDrawSPSetDimension(user->drawspE, 1));
31475155f48SMatthew G. Knepley     PetscCall(PetscDrawSPGetAxis(user->drawspE, &axis));
31575155f48SMatthew G. Knepley     PetscCall(PetscDrawAxisSetLabels(axis, "Particles", "x", "E"));
31675155f48SMatthew G. Knepley     PetscCall(PetscDrawSPReset(user->drawspE));
3178214e71cSJoe 
31875155f48SMatthew G. Knepley     PetscCall(PetscViewerDrawOpen(PETSC_COMM_WORLD, NULL, "Charge Density", 0, 0, 400, 300, &user->viewerRho));
31975155f48SMatthew G. Knepley     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)user->viewerRho, "rho_"));
32075155f48SMatthew G. Knepley     PetscCall(PetscViewerDrawGetDraw(user->viewerRho, 0, &draw));
32175155f48SMatthew G. Knepley     PetscCall(PetscDrawSetSave(draw, "ex9_rho_spatial"));
32275155f48SMatthew G. Knepley     PetscCall(PetscViewerSetFromOptions(user->viewerRho));
32375155f48SMatthew G. Knepley     PetscCall(DMGetNamedGlobalVector(dm, "rho", &rho));
32475155f48SMatthew G. Knepley     PetscCall(PetscObjectSetName((PetscObject)rho, "charge_density"));
32575155f48SMatthew G. Knepley     PetscCall(DMRestoreNamedGlobalVector(dm, "rho", &rho));
3268214e71cSJoe 
32775155f48SMatthew G. Knepley     PetscCall(PetscViewerDrawOpen(PETSC_COMM_WORLD, NULL, "Potential", 400, 0, 400, 300, &user->viewerPhi));
32875155f48SMatthew G. Knepley     PetscCall(PetscObjectSetOptionsPrefix((PetscObject)user->viewerPhi, "phi_"));
32975155f48SMatthew G. Knepley     PetscCall(PetscViewerDrawGetDraw(user->viewerPhi, 0, &draw));
33075155f48SMatthew G. Knepley     PetscCall(PetscDrawSetSave(draw, "ex9_phi_spatial"));
33175155f48SMatthew G. Knepley     PetscCall(PetscViewerSetFromOptions(user->viewerPhi));
33275155f48SMatthew G. Knepley     PetscCall(DMGetNamedGlobalVector(dm, "phi", &phi));
33375155f48SMatthew G. Knepley     PetscCall(PetscObjectSetName((PetscObject)phi, "potential"));
33475155f48SMatthew G. Knepley     PetscCall(DMRestoreNamedGlobalVector(dm, "phi", &phi));
3358214e71cSJoe   }
3368214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
3378214e71cSJoe }
3388214e71cSJoe 
3398214e71cSJoe static PetscErrorCode DestroyContext(AppCtx *user)
3408214e71cSJoe {
3418214e71cSJoe   PetscFunctionBeginUser;
3428214e71cSJoe   PetscCall(PetscDrawHGDestroy(&user->drawhgic_x));
3438214e71cSJoe   PetscCall(PetscDrawDestroy(&user->drawic_x));
3448214e71cSJoe   PetscCall(PetscDrawHGDestroy(&user->drawhgic_v));
3458214e71cSJoe   PetscCall(PetscDrawDestroy(&user->drawic_v));
3468214e71cSJoe   PetscCall(PetscDrawHGDestroy(&user->drawhgic_w));
3478214e71cSJoe   PetscCall(PetscDrawDestroy(&user->drawic_w));
3488214e71cSJoe 
34975155f48SMatthew G. Knepley   PetscCall(PetscDrawLGDestroy(&user->drawlgE));
35075155f48SMatthew G. Knepley   PetscCall(PetscDrawSPDestroy(&user->drawspE));
35175155f48SMatthew G. Knepley   PetscCall(PetscDrawSPDestroy(&user->drawspX));
35275155f48SMatthew G. Knepley   PetscCall(PetscViewerDestroy(&user->viewerRho));
35375155f48SMatthew G. Knepley   PetscCall(PetscViewerDestroy(&user->viewerPhi));
3548214e71cSJoe 
3558214e71cSJoe   PetscCall(PetscBagDestroy(&user->bag));
3568214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
3578214e71cSJoe }
3588214e71cSJoe 
3598214e71cSJoe static PetscErrorCode CheckNonNegativeWeights(DM sw, AppCtx *user)
3608214e71cSJoe {
3618214e71cSJoe   const PetscScalar *w;
3628214e71cSJoe   PetscInt           Np;
3638214e71cSJoe 
3648214e71cSJoe   PetscFunctionBeginUser;
3658214e71cSJoe   if (!user->checkweights) PetscFunctionReturn(PETSC_SUCCESS);
3668214e71cSJoe   PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&w));
3678214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
3688214e71cSJoe   for (PetscInt p = 0; p < Np; ++p) PetscCheck(w[p] >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Particle %" PetscInt_FMT " has negative weight %g", p, w[p]);
3698214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&w));
3708214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
3718214e71cSJoe }
3728214e71cSJoe 
373*9072cb8bSMatthew G. Knepley static PetscErrorCode computeVelocityFEMMoments(DM sw, PetscReal moments[], AppCtx *user)
3748214e71cSJoe {
375*9072cb8bSMatthew G. Knepley   DMSwarmCellDM celldm;
376f1e6e573SMatthew G. Knepley   DM            vdm;
377f1e6e573SMatthew G. Knepley   Vec           u[1];
378f1e6e573SMatthew G. Knepley   const char   *fields[1] = {"w_q"};
3798214e71cSJoe 
380f1e6e573SMatthew G. Knepley   PetscFunctionBegin;
381*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmSetCellDMActive(sw, "velocity"));
382*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetCellDMActive(sw, &celldm));
383*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmCellDMGetDM(celldm, &vdm));
384f1e6e573SMatthew G. Knepley   PetscCall(DMGetGlobalVector(vdm, &u[0]));
385f1e6e573SMatthew G. Knepley   PetscCall(DMSwarmProjectFields(sw, vdm, 1, fields, u, SCATTER_FORWARD));
386f1e6e573SMatthew G. Knepley   PetscCall(DMPlexComputeMoments(vdm, u[0], moments));
387f1e6e573SMatthew G. Knepley   PetscCall(DMRestoreGlobalVector(vdm, &u[0]));
388*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmSetCellDMActive(sw, "space"));
3898214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
3908214e71cSJoe }
3918214e71cSJoe 
3928214e71cSJoe static PetscErrorCode MonitorEField(TS ts, PetscInt step, PetscReal t, Vec U, void *ctx)
3938214e71cSJoe {
3948214e71cSJoe   AppCtx    *user = (AppCtx *)ctx;
395f1e6e573SMatthew G. Knepley   DM         sw;
396f1e6e573SMatthew G. Knepley   PetscReal *E, *x, *weight;
397f1e6e573SMatthew G. Knepley   PetscReal  Enorm = 0., lgEnorm, lgEmax, sum = 0., Emax = 0., chargesum = 0.;
398f1e6e573SMatthew G. Knepley   PetscReal  pmoments[4]; /* \int f, \int v f, \int v^2 f */
399f1e6e573SMatthew G. Knepley   PetscInt  *species, dim, Np;
4008214e71cSJoe 
4018214e71cSJoe   PetscFunctionBeginUser;
402*9072cb8bSMatthew G. Knepley   if (step < 0 || !user->validE) PetscFunctionReturn(PETSC_SUCCESS);
4038214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
4048214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
4058214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
4068214e71cSJoe   PetscCall(DMSwarmSortGetAccess(sw));
4078214e71cSJoe   PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
4088214e71cSJoe   PetscCall(DMSwarmGetField(sw, "E_field", NULL, NULL, (void **)&E));
4098214e71cSJoe   PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **)&species));
4108214e71cSJoe   PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&weight));
4118214e71cSJoe 
412f1e6e573SMatthew G. Knepley   for (PetscInt p = 0; p < Np; ++p) {
413f1e6e573SMatthew G. Knepley     for (PetscInt d = 0; d < 1; ++d) {
414f1e6e573SMatthew G. Knepley       PetscReal temp = PetscAbsReal(E[p * dim + d]);
4158214e71cSJoe       if (temp > Emax) Emax = temp;
4168214e71cSJoe     }
4178214e71cSJoe     Enorm += PetscSqrtReal(E[p * dim] * E[p * dim]);
4188214e71cSJoe     sum += E[p * dim];
4198214e71cSJoe     chargesum += user->charges[0] * weight[p];
4208214e71cSJoe   }
4218214e71cSJoe   lgEnorm = Enorm != 0 ? PetscLog10Real(Enorm) : -16.;
42275155f48SMatthew G. Knepley   lgEmax  = Emax != 0 ? PetscLog10Real(Emax) : user->drawlgEmin;
4238214e71cSJoe 
4248214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
4258214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&weight));
4268214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "E_field", NULL, NULL, (void **)&E));
4278214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **)&species));
42875155f48SMatthew G. Knepley   PetscCall(PetscDrawLGAddPoint(user->drawlgE, &t, &lgEmax));
42975155f48SMatthew G. Knepley   PetscCall(PetscDrawLGDraw(user->drawlgE));
43075155f48SMatthew G. Knepley   PetscDraw draw;
43175155f48SMatthew G. Knepley   PetscCall(PetscDrawLGGetDraw(user->drawlgE, &draw));
43275155f48SMatthew G. Knepley   PetscCall(PetscDrawSave(draw));
433f1e6e573SMatthew G. Knepley 
434f1e6e573SMatthew G. Knepley   PetscCall(DMSwarmComputeMoments(sw, "velocity", "w_q", pmoments));
43575155f48SMatthew G. Knepley   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "%f\t%+e\t%e\t%f\t%f\t%f\t%f\t%f\t%f\t%f\n", (double)t, (double)sum, (double)Enorm, (double)lgEnorm, (double)Emax, (double)lgEmax, (double)chargesum, (double)pmoments[0], (double)pmoments[1], (double)pmoments[1 + dim]));
43675155f48SMatthew G. Knepley   PetscCall(DMViewFromOptions(sw, NULL, "-sw_efield_view"));
437f1e6e573SMatthew G. Knepley   PetscFunctionReturn(PETSC_SUCCESS);
438f1e6e573SMatthew G. Knepley }
439f1e6e573SMatthew G. Knepley 
440f1e6e573SMatthew G. Knepley static PetscErrorCode MonitorMoments(TS ts, PetscInt step, PetscReal t, Vec U, void *ctx)
441f1e6e573SMatthew G. Knepley {
442f1e6e573SMatthew G. Knepley   AppCtx   *user = (AppCtx *)ctx;
443f1e6e573SMatthew G. Knepley   DM        sw;
444f1e6e573SMatthew G. Knepley   PetscReal pmoments[4], fmoments[4]; /* \int f, \int v f, \int v^2 f */
445f1e6e573SMatthew G. Knepley 
446f1e6e573SMatthew G. Knepley   PetscFunctionBeginUser;
447f1e6e573SMatthew G. Knepley   if (step < 0) PetscFunctionReturn(PETSC_SUCCESS);
448f1e6e573SMatthew G. Knepley   PetscCall(TSGetDM(ts, &sw));
449f1e6e573SMatthew G. Knepley 
450f1e6e573SMatthew G. Knepley   PetscCall(DMSwarmComputeMoments(sw, "velocity", "w_q", pmoments));
451f1e6e573SMatthew G. Knepley   PetscCall(computeVelocityFEMMoments(sw, fmoments, user));
452f1e6e573SMatthew G. Knepley 
453f1e6e573SMatthew G. Knepley   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "%f\t%f\t%f\t%f\t%f\t%f\t%f\n", (double)t, (double)pmoments[0], (double)pmoments[1], (double)pmoments[3], (double)fmoments[0], (double)fmoments[1], (double)fmoments[2]));
4548214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
4558214e71cSJoe }
4568214e71cSJoe 
4578214e71cSJoe PetscErrorCode MonitorInitialConditions(TS ts, PetscInt step, PetscReal t, Vec U, void *ctx)
4588214e71cSJoe {
4598214e71cSJoe   AppCtx            *user = (AppCtx *)ctx;
4608214e71cSJoe   DM                 dm, sw;
4618214e71cSJoe   const PetscScalar *u;
4628214e71cSJoe   PetscReal         *weight, *pos, *vel;
4638214e71cSJoe   PetscInt           dim, p, Np, cStart, cEnd;
4648214e71cSJoe 
4658214e71cSJoe   PetscFunctionBegin;
4668214e71cSJoe   if (step < 0) PetscFunctionReturn(PETSC_SUCCESS); /* -1 indicates interpolated solution */
4678214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
4688214e71cSJoe   PetscCall(DMSwarmGetCellDM(sw, &dm));
4698214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
4708214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
4718214e71cSJoe   PetscCall(DMSwarmSortGetAccess(sw));
4728214e71cSJoe   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
4738214e71cSJoe 
4748214e71cSJoe   if (step == 0) {
4758214e71cSJoe     PetscCall(PetscDrawHGReset(user->drawhgic_x));
4768214e71cSJoe     PetscCall(PetscDrawHGGetDraw(user->drawhgic_x, &user->drawic_x));
4778214e71cSJoe     PetscCall(PetscDrawClear(user->drawic_x));
4788214e71cSJoe     PetscCall(PetscDrawFlush(user->drawic_x));
4798214e71cSJoe 
4808214e71cSJoe     PetscCall(PetscDrawHGReset(user->drawhgic_v));
4818214e71cSJoe     PetscCall(PetscDrawHGGetDraw(user->drawhgic_v, &user->drawic_v));
4828214e71cSJoe     PetscCall(PetscDrawClear(user->drawic_v));
4838214e71cSJoe     PetscCall(PetscDrawFlush(user->drawic_v));
4848214e71cSJoe 
4858214e71cSJoe     PetscCall(PetscDrawHGReset(user->drawhgic_w));
4868214e71cSJoe     PetscCall(PetscDrawHGGetDraw(user->drawhgic_w, &user->drawic_w));
4878214e71cSJoe     PetscCall(PetscDrawClear(user->drawic_w));
4888214e71cSJoe     PetscCall(PetscDrawFlush(user->drawic_w));
4898214e71cSJoe 
4908214e71cSJoe     PetscCall(VecGetArrayRead(U, &u));
4918214e71cSJoe     PetscCall(DMSwarmGetField(sw, "velocity", NULL, NULL, (void **)&vel));
4928214e71cSJoe     PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&weight));
4938214e71cSJoe     PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&pos));
4948214e71cSJoe 
4958214e71cSJoe     PetscCall(VecGetLocalSize(U, &Np));
4968214e71cSJoe     Np /= dim * 2;
4978214e71cSJoe     for (p = 0; p < Np; ++p) {
4988214e71cSJoe       PetscCall(PetscDrawHGAddValue(user->drawhgic_x, pos[p * dim]));
4998214e71cSJoe       PetscCall(PetscDrawHGAddValue(user->drawhgic_v, vel[p * dim]));
5008214e71cSJoe       PetscCall(PetscDrawHGAddValue(user->drawhgic_w, weight[p]));
5018214e71cSJoe     }
5028214e71cSJoe 
5038214e71cSJoe     PetscCall(VecRestoreArrayRead(U, &u));
5048214e71cSJoe     PetscCall(PetscDrawHGDraw(user->drawhgic_x));
5058214e71cSJoe     PetscCall(PetscDrawHGSave(user->drawhgic_x));
5068214e71cSJoe 
5078214e71cSJoe     PetscCall(PetscDrawHGDraw(user->drawhgic_v));
5088214e71cSJoe     PetscCall(PetscDrawHGSave(user->drawhgic_v));
5098214e71cSJoe 
5108214e71cSJoe     PetscCall(PetscDrawHGDraw(user->drawhgic_w));
5118214e71cSJoe     PetscCall(PetscDrawHGSave(user->drawhgic_w));
5128214e71cSJoe 
5138214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&pos));
5148214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, "velocity", NULL, NULL, (void **)&vel));
5158214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&weight));
5168214e71cSJoe   }
5178214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
5188214e71cSJoe }
5198214e71cSJoe 
5208214e71cSJoe static PetscErrorCode MonitorPositions_2D(TS ts, PetscInt step, PetscReal t, Vec U, void *ctx)
5218214e71cSJoe {
5228214e71cSJoe   AppCtx         *user = (AppCtx *)ctx;
5238214e71cSJoe   DM              dm, sw;
5248214e71cSJoe   PetscScalar    *x, *v, *weight;
5258214e71cSJoe   PetscReal       lower[3], upper[3], speed;
5268214e71cSJoe   const PetscInt *s;
5278214e71cSJoe   PetscInt        dim, cStart, cEnd, c;
5288214e71cSJoe 
5298214e71cSJoe   PetscFunctionBeginUser;
5308214e71cSJoe   if (step > 0 && step % user->ostep == 0) {
5318214e71cSJoe     PetscCall(TSGetDM(ts, &sw));
5328214e71cSJoe     PetscCall(DMSwarmGetCellDM(sw, &dm));
5338214e71cSJoe     PetscCall(DMGetDimension(dm, &dim));
5348214e71cSJoe     PetscCall(DMGetBoundingBox(dm, lower, upper));
5358214e71cSJoe     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
5368214e71cSJoe     PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
5378214e71cSJoe     PetscCall(DMSwarmGetField(sw, "velocity", NULL, NULL, (void **)&v));
5388214e71cSJoe     PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&weight));
5398214e71cSJoe     PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **)&s));
5408214e71cSJoe     PetscCall(DMSwarmSortGetAccess(sw));
54175155f48SMatthew G. Knepley     PetscCall(PetscDrawSPReset(user->drawspX));
54275155f48SMatthew G. Knepley     PetscCall(PetscDrawSPSetLimits(user->drawspX, lower[0], upper[0], lower[1], upper[1]));
54375155f48SMatthew G. Knepley     PetscCall(PetscDrawSPSetLimits(user->drawspX, lower[0], upper[0], -12, 12));
5448214e71cSJoe     for (c = 0; c < cEnd - cStart; ++c) {
5458214e71cSJoe       PetscInt *pidx, Npc, q;
5468214e71cSJoe       PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Npc, &pidx));
5478214e71cSJoe       for (q = 0; q < Npc; ++q) {
5488214e71cSJoe         const PetscInt p = pidx[q];
5498214e71cSJoe         if (s[p] == 0) {
550*9072cb8bSMatthew G. Knepley           speed = 0.;
551*9072cb8bSMatthew G. Knepley           for (PetscInt d = 0; d < dim; ++d) speed += PetscSqr(v[p * dim + d]);
552*9072cb8bSMatthew G. Knepley           speed = PetscSqrtReal(speed);
5538214e71cSJoe           if (dim == 1 || user->fake_1D) {
55475155f48SMatthew G. Knepley             PetscCall(PetscDrawSPAddPointColorized(user->drawspX, &x[p * dim], &v[p * dim], &speed));
5558214e71cSJoe           } else {
55675155f48SMatthew G. Knepley             PetscCall(PetscDrawSPAddPointColorized(user->drawspX, &x[p * dim], &x[p * dim + 1], &speed));
5578214e71cSJoe           }
5588214e71cSJoe         } else if (s[p] == 1) {
55975155f48SMatthew G. Knepley           PetscCall(PetscDrawSPAddPoint(user->drawspX, &x[p * dim], &v[p * dim]));
5608214e71cSJoe         }
5618214e71cSJoe       }
56284467f86SMatthew G. Knepley       PetscCall(DMSwarmSortRestorePointsPerCell(sw, c, &Npc, &pidx));
5638214e71cSJoe     }
56475155f48SMatthew G. Knepley     PetscCall(PetscDrawSPDraw(user->drawspX, PETSC_TRUE));
56575155f48SMatthew G. Knepley     PetscDraw draw;
56675155f48SMatthew G. Knepley     PetscCall(PetscDrawSPGetDraw(user->drawspX, &draw));
56775155f48SMatthew G. Knepley     PetscCall(PetscDrawSave(draw));
5688214e71cSJoe     PetscCall(DMSwarmSortRestoreAccess(sw));
5698214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
5708214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&weight));
5718214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, "velocity", NULL, NULL, (void **)&v));
5728214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **)&s));
5738214e71cSJoe   }
5748214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
5758214e71cSJoe }
5768214e71cSJoe 
5778214e71cSJoe static PetscErrorCode MonitorPoisson(TS ts, PetscInt step, PetscReal t, Vec U, void *ctx)
5788214e71cSJoe {
5798214e71cSJoe   AppCtx *user = (AppCtx *)ctx;
5808214e71cSJoe   DM      dm, sw;
5818214e71cSJoe 
5828214e71cSJoe   PetscFunctionBeginUser;
5838214e71cSJoe   if (step > 0 && step % user->ostep == 0) {
5848214e71cSJoe     PetscCall(TSGetDM(ts, &sw));
5858214e71cSJoe     PetscCall(DMSwarmGetCellDM(sw, &dm));
586*9072cb8bSMatthew G. Knepley 
587*9072cb8bSMatthew G. Knepley     if (user->validE) {
588*9072cb8bSMatthew G. Knepley       PetscScalar *x, *E, *weight;
589*9072cb8bSMatthew G. Knepley       PetscReal    lower[3], upper[3], xval;
590*9072cb8bSMatthew G. Knepley       PetscDraw    draw;
591*9072cb8bSMatthew G. Knepley       PetscInt     dim, cStart, cEnd;
592*9072cb8bSMatthew G. Knepley 
5938214e71cSJoe       PetscCall(DMGetDimension(dm, &dim));
5948214e71cSJoe       PetscCall(DMGetBoundingBox(dm, lower, upper));
5958214e71cSJoe       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
5968214e71cSJoe 
59775155f48SMatthew G. Knepley       PetscCall(PetscDrawSPReset(user->drawspE));
5988214e71cSJoe       PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
5998214e71cSJoe       PetscCall(DMSwarmGetField(sw, "E_field", NULL, NULL, (void **)&E));
6008214e71cSJoe       PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&weight));
6018214e71cSJoe 
6028214e71cSJoe       PetscCall(DMSwarmSortGetAccess(sw));
603*9072cb8bSMatthew G. Knepley       for (PetscInt c = 0; c < cEnd - cStart; ++c) {
60475155f48SMatthew G. Knepley         PetscReal Eavg = 0.0;
605*9072cb8bSMatthew G. Knepley         PetscInt *pidx, Npc;
606*9072cb8bSMatthew G. Knepley 
6078214e71cSJoe         PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Npc, &pidx));
608*9072cb8bSMatthew G. Knepley         for (PetscInt q = 0; q < Npc; ++q) {
6098214e71cSJoe           const PetscInt p = pidx[q];
61075155f48SMatthew G. Knepley           Eavg += E[p * dim];
6118214e71cSJoe         }
61275155f48SMatthew G. Knepley         Eavg /= Npc;
6138214e71cSJoe         xval = (c + 0.5) * ((upper[0] - lower[0]) / (cEnd - cStart));
61475155f48SMatthew G. Knepley         PetscCall(PetscDrawSPAddPoint(user->drawspE, &xval, &Eavg));
61584467f86SMatthew G. Knepley         PetscCall(DMSwarmSortRestorePointsPerCell(sw, c, &Npc, &pidx));
6168214e71cSJoe       }
61775155f48SMatthew G. Knepley       PetscCall(PetscDrawSPDraw(user->drawspE, PETSC_TRUE));
61875155f48SMatthew G. Knepley       PetscCall(PetscDrawSPGetDraw(user->drawspE, &draw));
61975155f48SMatthew G. Knepley       PetscCall(PetscDrawSave(draw));
6208214e71cSJoe       PetscCall(DMSwarmSortRestoreAccess(sw));
6218214e71cSJoe       PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
6228214e71cSJoe       PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&weight));
6238214e71cSJoe       PetscCall(DMSwarmRestoreField(sw, "E_field", NULL, NULL, (void **)&E));
624*9072cb8bSMatthew G. Knepley     }
62575155f48SMatthew G. Knepley 
62675155f48SMatthew G. Knepley     Vec rho, phi;
62775155f48SMatthew G. Knepley 
62875155f48SMatthew G. Knepley     PetscCall(DMGetNamedGlobalVector(dm, "rho", &rho));
62975155f48SMatthew G. Knepley     PetscCall(VecView(rho, user->viewerRho));
63075155f48SMatthew G. Knepley     PetscCall(DMRestoreNamedGlobalVector(dm, "rho", &rho));
63175155f48SMatthew G. Knepley 
63275155f48SMatthew G. Knepley     PetscCall(DMGetNamedGlobalVector(dm, "phi", &phi));
63375155f48SMatthew G. Knepley     PetscCall(VecView(phi, user->viewerPhi));
63475155f48SMatthew G. Knepley     PetscCall(DMRestoreNamedGlobalVector(dm, "phi", &phi));
6358214e71cSJoe   }
6368214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
6378214e71cSJoe }
6388214e71cSJoe 
639*9072cb8bSMatthew G. Knepley static PetscErrorCode MonitorSwarmWeights(DM sw, const char field[])
640*9072cb8bSMatthew G. Knepley {
641*9072cb8bSMatthew G. Knepley   PetscReal *w;
642*9072cb8bSMatthew G. Knepley   PetscInt   bs;
643*9072cb8bSMatthew G. Knepley 
644*9072cb8bSMatthew G. Knepley   PetscFunctionBegin;
645*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetField(sw, field, &bs, NULL, (void **)&w));
646*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmRestoreField(sw, field, &bs, NULL, (void **)&w));
647*9072cb8bSMatthew G. Knepley   PetscFunctionReturn(PETSC_SUCCESS);
648*9072cb8bSMatthew G. Knepley }
649*9072cb8bSMatthew G. Knepley 
6508214e71cSJoe static PetscErrorCode SetupParameters(MPI_Comm comm, AppCtx *ctx)
6518214e71cSJoe {
6528214e71cSJoe   PetscBag   bag;
6538214e71cSJoe   Parameter *p;
6548214e71cSJoe 
6558214e71cSJoe   PetscFunctionBeginUser;
6568214e71cSJoe   /* setup PETSc parameter bag */
6578214e71cSJoe   PetscCall(PetscBagGetData(ctx->bag, (void **)&p));
6588214e71cSJoe   PetscCall(PetscBagSetName(ctx->bag, "par", "Vlasov-Poisson Parameters"));
6598214e71cSJoe   bag = ctx->bag;
6608214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->v0, 1.0, "v0", "Velocity scale, m/s"));
6618214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->t0, 1.0, "t0", "Time scale, s"));
6628214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->x0, 1.0, "x0", "Space scale, m"));
6638214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->v0, 1.0, "phi0", "Potential scale, kg*m^2/A*s^3"));
6648214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->q0, 1.0, "q0", "Charge Scale, A*s"));
6658214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->m0, 1.0, "m0", "Mass Scale, kg"));
6668214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->epsi0, 1.0, "epsi0", "Permittivity of Free Space, kg"));
6678214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->kb, 1.0, "kb", "Boltzmann Constant, m^2 kg/s^2 K^1"));
6688214e71cSJoe 
6698214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->sigma, 1.0, "sigma", "Charge per unit area, C/m^3"));
6708214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->poissonNumber, 1.0, "poissonNumber", "Non-Dimensional Poisson Number"));
6718214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->vlasovNumber, 1.0, "vlasovNumber", "Non-Dimensional Vlasov Number"));
6728214e71cSJoe   PetscCall(PetscBagSetFromOptions(bag));
6738214e71cSJoe   {
6748214e71cSJoe     PetscViewer       viewer;
6758214e71cSJoe     PetscViewerFormat format;
6768214e71cSJoe     PetscBool         flg;
6778214e71cSJoe 
678648c30bcSBarry Smith     PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-param_view", &viewer, &format, &flg));
6798214e71cSJoe     if (flg) {
6808214e71cSJoe       PetscCall(PetscViewerPushFormat(viewer, format));
6818214e71cSJoe       PetscCall(PetscBagView(bag, viewer));
6828214e71cSJoe       PetscCall(PetscViewerFlush(viewer));
6838214e71cSJoe       PetscCall(PetscViewerPopFormat(viewer));
6848214e71cSJoe       PetscCall(PetscViewerDestroy(&viewer));
6858214e71cSJoe     }
6868214e71cSJoe   }
6878214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
6888214e71cSJoe }
6898214e71cSJoe 
6908214e71cSJoe static PetscErrorCode CreateMesh(MPI_Comm comm, AppCtx *user, DM *dm)
691d71ae5a4SJacob Faibussowitsch {
692b80bf5b1SJoe Pusztay   PetscFunctionBeginUser;
6939566063dSJacob Faibussowitsch   PetscCall(DMCreate(comm, dm));
6949566063dSJacob Faibussowitsch   PetscCall(DMSetType(*dm, DMPLEX));
6959566063dSJacob Faibussowitsch   PetscCall(DMSetFromOptions(*dm));
696*9072cb8bSMatthew G. Knepley   PetscCall(PetscObjectSetName((PetscObject)*dm, "space"));
6979566063dSJacob Faibussowitsch   PetscCall(DMViewFromOptions(*dm, NULL, "-dm_view"));
698f1e6e573SMatthew G. Knepley 
699f1e6e573SMatthew G. Knepley   // Cache the mesh geometry
700f1e6e573SMatthew G. Knepley   DMField         coordField;
701f1e6e573SMatthew G. Knepley   IS              cellIS;
702f1e6e573SMatthew G. Knepley   PetscQuadrature quad;
703f1e6e573SMatthew G. Knepley   PetscReal      *wt, *pt;
704f1e6e573SMatthew G. Knepley   PetscInt        cdim, cStart, cEnd;
705f1e6e573SMatthew G. Knepley 
706f1e6e573SMatthew G. Knepley   PetscCall(DMGetCoordinateField(*dm, &coordField));
707f1e6e573SMatthew G. Knepley   PetscCheck(coordField, comm, PETSC_ERR_USER, "DM must have a coordinate field");
708f1e6e573SMatthew G. Knepley   PetscCall(DMGetCoordinateDim(*dm, &cdim));
709f1e6e573SMatthew G. Knepley   PetscCall(DMPlexGetHeightStratum(*dm, 0, &cStart, &cEnd));
710f1e6e573SMatthew G. Knepley   PetscCall(ISCreateStride(PETSC_COMM_SELF, cEnd - cStart, cStart, 1, &cellIS));
711f1e6e573SMatthew G. Knepley   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, &quad));
712f1e6e573SMatthew G. Knepley   PetscCall(PetscMalloc1(1, &wt));
713f1e6e573SMatthew G. Knepley   PetscCall(PetscMalloc1(2, &pt));
714f1e6e573SMatthew G. Knepley   wt[0] = 1.;
715f1e6e573SMatthew G. Knepley   pt[0] = -1.;
716f1e6e573SMatthew G. Knepley   pt[1] = -1.;
717f1e6e573SMatthew G. Knepley   PetscCall(PetscQuadratureSetData(quad, cdim, 1, 1, pt, wt));
718f1e6e573SMatthew G. Knepley   PetscCall(DMFieldCreateFEGeom(coordField, cellIS, quad, PETSC_FALSE, &user->fegeom));
719f1e6e573SMatthew G. Knepley   PetscCall(PetscQuadratureDestroy(&quad));
720f1e6e573SMatthew G. Knepley   PetscCall(ISDestroy(&cellIS));
7213ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
722b80bf5b1SJoe Pusztay }
723b80bf5b1SJoe Pusztay 
7248214e71cSJoe static void ion_f0(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[])
7258214e71cSJoe {
7268214e71cSJoe   f0[0] = -constants[SIGMA];
7278214e71cSJoe }
7288214e71cSJoe 
729d71ae5a4SJacob Faibussowitsch 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[])
730d71ae5a4SJacob Faibussowitsch {
731b80bf5b1SJoe Pusztay   PetscInt d;
732ad540459SPierre Jolivet   for (d = 0; d < dim; ++d) f1[d] = u_x[d];
733b80bf5b1SJoe Pusztay }
734b80bf5b1SJoe Pusztay 
7358214e71cSJoe 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[])
736d71ae5a4SJacob Faibussowitsch {
737b80bf5b1SJoe Pusztay   PetscInt d;
738ad540459SPierre Jolivet   for (d = 0; d < dim; ++d) g3[d * dim + d] = 1.0;
739b80bf5b1SJoe Pusztay }
740b80bf5b1SJoe Pusztay 
7418214e71cSJoe static PetscErrorCode zero(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
742d71ae5a4SJacob Faibussowitsch {
7438214e71cSJoe   *u = 0.0;
7448214e71cSJoe   return PETSC_SUCCESS;
745b80bf5b1SJoe Pusztay }
746b80bf5b1SJoe Pusztay 
747b80bf5b1SJoe Pusztay /*
7488214e71cSJoe    /  I   -grad\ / q \ = /0\
7498214e71cSJoe    \-div    0  / \phi/   \f/
750b80bf5b1SJoe Pusztay */
7518214e71cSJoe 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[])
752d71ae5a4SJacob Faibussowitsch {
7538214e71cSJoe   for (PetscInt d = 0; d < dim; ++d) f0[d] += u[uOff[0] + d];
7548214e71cSJoe }
7558214e71cSJoe 
7568214e71cSJoe 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[])
7578214e71cSJoe {
7588214e71cSJoe   for (PetscInt d = 0; d < dim; ++d) f1[d * dim + d] = u[uOff[1]];
7598214e71cSJoe }
7608214e71cSJoe 
7618214e71cSJoe static void f0_phi_backgroundCharge(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[])
7628214e71cSJoe {
7638214e71cSJoe   f0[0] += constants[SIGMA];
7648214e71cSJoe   for (PetscInt d = 0; d < dim; ++d) f0[0] += u_x[uOff_x[0] + d * dim + d];
7658214e71cSJoe }
7668214e71cSJoe 
7678214e71cSJoe /* Boundary residual. Dirichlet boundary for u means u_bdy=p*n */
7688214e71cSJoe 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[])
7698214e71cSJoe {
7708214e71cSJoe   for (PetscInt d = 0; d < dim; ++d) g0[d * dim + d] = 1.0;
7718214e71cSJoe }
7728214e71cSJoe 
7738214e71cSJoe static void g2_qphi(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[])
7748214e71cSJoe {
7758214e71cSJoe   for (PetscInt d = 0; d < dim; ++d) g2[d * dim + d] = 1.0;
7768214e71cSJoe }
7778214e71cSJoe 
7788214e71cSJoe static void g1_phiq(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[])
7798214e71cSJoe {
7808214e71cSJoe   for (PetscInt d = 0; d < dim; ++d) g1[d * dim + d] = 1.0;
7818214e71cSJoe }
7828214e71cSJoe 
7838214e71cSJoe static PetscErrorCode CreateFEM(DM dm, AppCtx *user)
7848214e71cSJoe {
7858214e71cSJoe   PetscFE   fephi, feq;
7868214e71cSJoe   PetscDS   ds;
7878214e71cSJoe   PetscBool simplex;
7888214e71cSJoe   PetscInt  dim;
7898214e71cSJoe 
7908214e71cSJoe   PetscFunctionBeginUser;
7918214e71cSJoe   PetscCall(DMGetDimension(dm, &dim));
7928214e71cSJoe   PetscCall(DMPlexIsSimplex(dm, &simplex));
7938214e71cSJoe   if (user->em == EM_MIXED) {
7948214e71cSJoe     DMLabel        label;
7958214e71cSJoe     const PetscInt id = 1;
7968214e71cSJoe 
7978214e71cSJoe     PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, dim, simplex, "field_", PETSC_DETERMINE, &feq));
7988214e71cSJoe     PetscCall(PetscObjectSetName((PetscObject)feq, "field"));
7998214e71cSJoe     PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "potential_", PETSC_DETERMINE, &fephi));
8008214e71cSJoe     PetscCall(PetscObjectSetName((PetscObject)fephi, "potential"));
8018214e71cSJoe     PetscCall(PetscFECopyQuadrature(feq, fephi));
8028214e71cSJoe     PetscCall(DMSetField(dm, 0, NULL, (PetscObject)feq));
8038214e71cSJoe     PetscCall(DMSetField(dm, 1, NULL, (PetscObject)fephi));
8048214e71cSJoe     PetscCall(DMCreateDS(dm));
8058214e71cSJoe     PetscCall(PetscFEDestroy(&fephi));
8068214e71cSJoe     PetscCall(PetscFEDestroy(&feq));
8078214e71cSJoe 
8088214e71cSJoe     PetscCall(DMGetLabel(dm, "marker", &label));
8098214e71cSJoe     PetscCall(DMGetDS(dm, &ds));
8108214e71cSJoe 
8118214e71cSJoe     PetscCall(PetscDSSetResidual(ds, 0, f0_q, f1_q));
8128214e71cSJoe     PetscCall(PetscDSSetResidual(ds, 1, f0_phi_backgroundCharge, NULL));
8138214e71cSJoe     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_qq, NULL, NULL, NULL));
8148214e71cSJoe     PetscCall(PetscDSSetJacobian(ds, 0, 1, NULL, NULL, g2_qphi, NULL));
8158214e71cSJoe     PetscCall(PetscDSSetJacobian(ds, 1, 0, NULL, g1_phiq, NULL, NULL));
8168214e71cSJoe 
8178214e71cSJoe     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "wall", label, 1, &id, 0, 0, NULL, (void (*)(void))zero, NULL, NULL, NULL));
8188214e71cSJoe 
819f1e6e573SMatthew G. Knepley   } else {
8208214e71cSJoe     MatNullSpace nullsp;
8218214e71cSJoe     PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, NULL, PETSC_DETERMINE, &fephi));
8228214e71cSJoe     PetscCall(PetscObjectSetName((PetscObject)fephi, "potential"));
8238214e71cSJoe     PetscCall(DMSetField(dm, 0, NULL, (PetscObject)fephi));
8248214e71cSJoe     PetscCall(DMCreateDS(dm));
8258214e71cSJoe     PetscCall(DMGetDS(dm, &ds));
8268214e71cSJoe     PetscCall(PetscDSSetResidual(ds, 0, ion_f0, laplacian_f1));
8278214e71cSJoe     PetscCall(PetscDSSetJacobian(ds, 0, 0, NULL, NULL, NULL, laplacian_g3));
8288214e71cSJoe     PetscCall(MatNullSpaceCreate(PetscObjectComm((PetscObject)dm), PETSC_TRUE, 0, NULL, &nullsp));
8298214e71cSJoe     PetscCall(PetscObjectCompose((PetscObject)fephi, "nullspace", (PetscObject)nullsp));
8308214e71cSJoe     PetscCall(MatNullSpaceDestroy(&nullsp));
8318214e71cSJoe     PetscCall(PetscFEDestroy(&fephi));
8328214e71cSJoe   }
8338214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
8348214e71cSJoe }
8358214e71cSJoe 
8368214e71cSJoe static PetscErrorCode CreatePoisson(DM dm, AppCtx *user)
8378214e71cSJoe {
8388214e71cSJoe   SNES         snes;
8398214e71cSJoe   Mat          J;
8408214e71cSJoe   MatNullSpace nullSpace;
8418214e71cSJoe 
8428214e71cSJoe   PetscFunctionBeginUser;
8438214e71cSJoe   PetscCall(CreateFEM(dm, user));
8448214e71cSJoe   PetscCall(SNESCreate(PetscObjectComm((PetscObject)dm), &snes));
8458214e71cSJoe   PetscCall(SNESSetOptionsPrefix(snes, "em_"));
8468214e71cSJoe   PetscCall(SNESSetDM(snes, dm));
8478214e71cSJoe   PetscCall(DMPlexSetSNESLocalFEM(dm, PETSC_FALSE, user));
8488214e71cSJoe   PetscCall(SNESSetFromOptions(snes));
8498214e71cSJoe 
8508214e71cSJoe   PetscCall(DMCreateMatrix(dm, &J));
8518214e71cSJoe   PetscCall(MatNullSpaceCreate(PetscObjectComm((PetscObject)dm), PETSC_TRUE, 0, NULL, &nullSpace));
8528214e71cSJoe   PetscCall(MatSetNullSpace(J, nullSpace));
8538214e71cSJoe   PetscCall(MatNullSpaceDestroy(&nullSpace));
8548214e71cSJoe   PetscCall(SNESSetJacobian(snes, J, J, NULL, NULL));
8558214e71cSJoe   PetscCall(MatDestroy(&J));
856f1e6e573SMatthew G. Knepley   PetscCall(DMCreateMassMatrix(dm, dm, &user->M));
857f1e6e573SMatthew G. Knepley   PetscCall(DMPlexCreateClosureIndex(dm, NULL));
8588214e71cSJoe   user->snes = snes;
8598214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
8608214e71cSJoe }
8618214e71cSJoe 
8628214e71cSJoe PetscErrorCode PetscPDFPertubedConstant2D(const PetscReal x[], const PetscReal dummy[], PetscReal p[])
8638214e71cSJoe {
8648214e71cSJoe   p[0] = (1 + 0.01 * PetscCosReal(0.5 * x[0])) / (2 * PETSC_PI);
8658214e71cSJoe   p[1] = (1 + 0.01 * PetscCosReal(0.5 * x[1])) / (2 * PETSC_PI);
8668214e71cSJoe   return PETSC_SUCCESS;
8678214e71cSJoe }
8688214e71cSJoe PetscErrorCode PetscPDFPertubedConstant1D(const PetscReal x[], const PetscReal dummy[], PetscReal p[])
8698214e71cSJoe {
8708214e71cSJoe   p[0] = (1. + 0.01 * PetscCosReal(0.5 * x[0])) / (2 * PETSC_PI);
8718214e71cSJoe   return PETSC_SUCCESS;
8728214e71cSJoe }
8738214e71cSJoe 
8748214e71cSJoe PetscErrorCode PetscPDFCosine1D(const PetscReal x[], const PetscReal scale[], PetscReal p[])
8758214e71cSJoe {
8768214e71cSJoe   const PetscReal alpha = scale ? scale[0] : 0.0;
8778214e71cSJoe   const PetscReal k     = scale ? scale[1] : 1.;
8788214e71cSJoe   p[0]                  = (1 + alpha * PetscCosReal(k * x[0]));
8798214e71cSJoe   return PETSC_SUCCESS;
8808214e71cSJoe }
8818214e71cSJoe 
8828214e71cSJoe PetscErrorCode PetscPDFCosine2D(const PetscReal x[], const PetscReal scale[], PetscReal p[])
8838214e71cSJoe {
8848214e71cSJoe   const PetscReal alpha = scale ? scale[0] : 0.;
8858214e71cSJoe   const PetscReal k     = scale ? scale[0] : 1.;
8868214e71cSJoe   p[0]                  = (1 + alpha * PetscCosReal(k * (x[0] + x[1])));
8878214e71cSJoe   return PETSC_SUCCESS;
8888214e71cSJoe }
8898214e71cSJoe 
89084467f86SMatthew G. Knepley static PetscErrorCode CreateVelocityDM(DM sw, DM *vdm)
8918214e71cSJoe {
892f1e6e573SMatthew G. Knepley   PetscFE        fe;
893f1e6e573SMatthew G. Knepley   DMPolytopeType ct;
894f1e6e573SMatthew G. Knepley   PetscInt       dim, cStart;
895f1e6e573SMatthew G. Knepley   const char    *prefix = "v";
896f1e6e573SMatthew G. Knepley 
89784467f86SMatthew G. Knepley   PetscFunctionBegin;
89884467f86SMatthew G. Knepley   PetscCall(DMCreate(PETSC_COMM_SELF, vdm));
89984467f86SMatthew G. Knepley   PetscCall(DMSetType(*vdm, DMPLEX));
900f1e6e573SMatthew G. Knepley   PetscCall(DMPlexSetOptionsPrefix(*vdm, prefix));
90184467f86SMatthew G. Knepley   PetscCall(DMSetFromOptions(*vdm));
902*9072cb8bSMatthew G. Knepley   PetscCall(PetscObjectSetName((PetscObject)*vdm, "velocity"));
90384467f86SMatthew G. Knepley   PetscCall(DMViewFromOptions(*vdm, NULL, "-dm_view"));
904f1e6e573SMatthew G. Knepley 
905f1e6e573SMatthew G. Knepley   PetscCall(DMGetDimension(*vdm, &dim));
906f1e6e573SMatthew G. Knepley   PetscCall(DMPlexGetHeightStratum(*vdm, 0, &cStart, NULL));
907f1e6e573SMatthew G. Knepley   PetscCall(DMPlexGetCellType(*vdm, cStart, &ct));
908f1e6e573SMatthew G. Knepley   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, prefix, PETSC_DETERMINE, &fe));
909f1e6e573SMatthew G. Knepley   PetscCall(PetscObjectSetName((PetscObject)fe, "distribution"));
910f1e6e573SMatthew G. Knepley   PetscCall(DMSetField(*vdm, 0, NULL, (PetscObject)fe));
911f1e6e573SMatthew G. Knepley   PetscCall(DMCreateDS(*vdm));
912f1e6e573SMatthew G. Knepley   PetscCall(PetscFEDestroy(&fe));
91384467f86SMatthew G. Knepley   PetscFunctionReturn(PETSC_SUCCESS);
9148214e71cSJoe }
9158214e71cSJoe 
916*9072cb8bSMatthew G. Knepley static PetscErrorCode CreateRemapDM(DM sw, DM *rdm)
917*9072cb8bSMatthew G. Knepley {
918*9072cb8bSMatthew G. Knepley   PetscFE        fe;
919*9072cb8bSMatthew G. Knepley   DMPolytopeType ct;
920*9072cb8bSMatthew G. Knepley   PetscInt       dim, cStart;
921*9072cb8bSMatthew G. Knepley   const char    *prefix = "remap_";
922*9072cb8bSMatthew G. Knepley 
923*9072cb8bSMatthew G. Knepley   PetscFunctionBegin;
924*9072cb8bSMatthew G. Knepley   PetscCall(DMCreate(PetscObjectComm((PetscObject)sw), rdm));
925*9072cb8bSMatthew G. Knepley   PetscCall(DMSetType(*rdm, DMPLEX));
926*9072cb8bSMatthew G. Knepley   PetscCall(DMPlexSetOptionsPrefix(*rdm, prefix));
927*9072cb8bSMatthew G. Knepley   PetscCall(DMSetFromOptions(*rdm));
928*9072cb8bSMatthew G. Knepley   PetscCall(PetscObjectSetName((PetscObject)*rdm, "remap"));
929*9072cb8bSMatthew G. Knepley   PetscCall(DMViewFromOptions(*rdm, NULL, "-dm_view"));
930*9072cb8bSMatthew G. Knepley 
931*9072cb8bSMatthew G. Knepley   PetscCall(DMGetDimension(*rdm, &dim));
932*9072cb8bSMatthew G. Knepley   PetscCall(DMPlexGetHeightStratum(*rdm, 0, &cStart, NULL));
933*9072cb8bSMatthew G. Knepley   PetscCall(DMPlexGetCellType(*rdm, cStart, &ct));
934*9072cb8bSMatthew G. Knepley   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, prefix, PETSC_DETERMINE, &fe));
935*9072cb8bSMatthew G. Knepley   PetscCall(PetscObjectSetName((PetscObject)fe, "distribution"));
936*9072cb8bSMatthew G. Knepley   PetscCall(DMSetField(*rdm, 0, NULL, (PetscObject)fe));
937*9072cb8bSMatthew G. Knepley   PetscCall(DMCreateDS(*rdm));
938*9072cb8bSMatthew G. Knepley   PetscCall(PetscFEDestroy(&fe));
939*9072cb8bSMatthew G. Knepley   PetscFunctionReturn(PETSC_SUCCESS);
940*9072cb8bSMatthew G. Knepley }
941*9072cb8bSMatthew G. Knepley 
942*9072cb8bSMatthew G. Knepley /*
943*9072cb8bSMatthew G. Knepley   InitializeParticles_Regular - Initialize a regular grid of particles in each cell
944*9072cb8bSMatthew G. Knepley 
945*9072cb8bSMatthew G. Knepley   Input Parameters:
946*9072cb8bSMatthew G. Knepley + sw - The `DMSWARM`
947*9072cb8bSMatthew G. Knepley - n  - The number of particles per dimension per species
948*9072cb8bSMatthew G. Knepley 
949*9072cb8bSMatthew G. Knepley Notes:
950*9072cb8bSMatthew G. Knepley   This functions sets the species, cellid, and cell DM coordinates.
951*9072cb8bSMatthew G. Knepley 
952*9072cb8bSMatthew G. Knepley   It places n^d particles per species in each cell of the cell DM.
953*9072cb8bSMatthew G. Knepley */
954*9072cb8bSMatthew G. Knepley static PetscErrorCode InitializeParticles_Regular(DM sw, PetscInt n)
955*9072cb8bSMatthew G. Knepley {
956*9072cb8bSMatthew G. Knepley   DM_Swarm     *swarm = (DM_Swarm *)sw->data;
957*9072cb8bSMatthew G. Knepley   DM            dm;
958*9072cb8bSMatthew G. Knepley   DMSwarmCellDM celldm;
959*9072cb8bSMatthew G. Knepley   PetscInt      dim, Ns, Npc, Np, cStart, cEnd, debug;
960*9072cb8bSMatthew G. Knepley   PetscBool     flg;
961*9072cb8bSMatthew G. Knepley   MPI_Comm      comm;
962*9072cb8bSMatthew G. Knepley 
963*9072cb8bSMatthew G. Knepley   PetscFunctionBegin;
964*9072cb8bSMatthew G. Knepley   PetscCall(PetscObjectGetComm((PetscObject)sw, &comm));
965*9072cb8bSMatthew G. Knepley 
966*9072cb8bSMatthew G. Knepley   PetscOptionsBegin(comm, "", "DMSwarm Options", "DMSWARM");
967*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetNumSpecies(sw, &Ns));
968*9072cb8bSMatthew G. Knepley   PetscCall(PetscOptionsInt("-dm_swarm_num_species", "The number of species", "DMSwarmSetNumSpecies", Ns, &Ns, &flg));
969*9072cb8bSMatthew G. Knepley   if (flg) PetscCall(DMSwarmSetNumSpecies(sw, Ns));
970*9072cb8bSMatthew G. Knepley   PetscCall(PetscOptionsBoundedInt("-dm_swarm_print_coords", "Debug output level for particle coordinate computations", "InitializeParticles", 0, &swarm->printCoords, NULL, 0));
971*9072cb8bSMatthew G. Knepley   PetscCall(PetscOptionsBoundedInt("-dm_swarm_print_weights", "Debug output level for particle weight computations", "InitializeWeights", 0, &swarm->printWeights, NULL, 0));
972*9072cb8bSMatthew G. Knepley   PetscOptionsEnd();
973*9072cb8bSMatthew G. Knepley   debug = swarm->printCoords;
974*9072cb8bSMatthew G. Knepley 
975*9072cb8bSMatthew G. Knepley   // n^d particle per cell on the grid
976*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetCellDM(sw, &dm));
977*9072cb8bSMatthew G. Knepley   PetscCall(DMGetDimension(dm, &dim));
978*9072cb8bSMatthew G. Knepley   PetscCheck(!(dim % 2), comm, PETSC_ERR_SUP, "We only support even dimension, not %" PetscInt_FMT, dim);
979*9072cb8bSMatthew G. Knepley   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
980*9072cb8bSMatthew G. Knepley   Npc = Ns * PetscPowInt(n, dim);
981*9072cb8bSMatthew G. Knepley   Np  = (cEnd - cStart) * Npc;
982*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmSetLocalSizes(sw, Np, 0));
983*9072cb8bSMatthew G. Knepley   if (debug) {
984*9072cb8bSMatthew G. Knepley     PetscInt gNp;
985*9072cb8bSMatthew G. Knepley     PetscCallMPI(MPIU_Allreduce(&Np, &gNp, 1, MPIU_INT, MPIU_SUM, comm));
986*9072cb8bSMatthew G. Knepley     PetscCall(PetscPrintf(comm, "Global Np = %" PetscInt_FMT "\n", gNp));
987*9072cb8bSMatthew G. Knepley   }
988*9072cb8bSMatthew G. Knepley   PetscCall(PetscPrintf(comm, "Regular layout using %" PetscInt_FMT " particles per cell\n", Npc));
989*9072cb8bSMatthew G. Knepley 
990*9072cb8bSMatthew G. Knepley   // Set species and cellid
991*9072cb8bSMatthew G. Knepley   {
992*9072cb8bSMatthew G. Knepley     const char *cellidName;
993*9072cb8bSMatthew G. Knepley     PetscInt   *species, *cellid;
994*9072cb8bSMatthew G. Knepley 
995*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmGetCellDMActive(sw, &celldm));
996*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmCellDMGetCellID(celldm, &cellidName));
997*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **)&species));
998*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmGetField(sw, cellidName, NULL, NULL, (void **)&cellid));
999*9072cb8bSMatthew G. Knepley     for (PetscInt c = 0, p = 0; c < cEnd - cStart; ++c) {
1000*9072cb8bSMatthew G. Knepley       for (PetscInt s = 0; s < Ns; ++s) {
1001*9072cb8bSMatthew G. Knepley         for (PetscInt q = 0; q < Npc / Ns; ++q, ++p) {
1002*9072cb8bSMatthew G. Knepley           species[p] = s;
1003*9072cb8bSMatthew G. Knepley           cellid[p]  = c;
1004*9072cb8bSMatthew G. Knepley         }
1005*9072cb8bSMatthew G. Knepley       }
1006*9072cb8bSMatthew G. Knepley     }
1007*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **)&species));
1008*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmRestoreField(sw, cellidName, NULL, NULL, (void **)&cellid));
1009*9072cb8bSMatthew G. Knepley   }
1010*9072cb8bSMatthew G. Knepley 
1011*9072cb8bSMatthew G. Knepley   // Set particle coordinates
1012*9072cb8bSMatthew G. Knepley   {
1013*9072cb8bSMatthew G. Knepley     PetscReal     *x, *v;
1014*9072cb8bSMatthew G. Knepley     const char   **coordNames;
1015*9072cb8bSMatthew G. Knepley     PetscInt       Ncoord;
1016*9072cb8bSMatthew G. Knepley     const PetscInt xdim = dim / 2, vdim = dim / 2;
1017*9072cb8bSMatthew G. Knepley 
1018*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmCellDMGetCoordinateFields(celldm, &Ncoord, &coordNames));
1019*9072cb8bSMatthew G. Knepley     PetscCheck(Ncoord == 2, comm, PETSC_ERR_SUP, "We only support regular layout for 2 coordinate fields, not %" PetscInt_FMT, Ncoord);
1020*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmGetField(sw, coordNames[0], NULL, NULL, (void **)&x));
1021*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmGetField(sw, coordNames[1], NULL, NULL, (void **)&v));
1022*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmSortGetAccess(sw));
1023*9072cb8bSMatthew G. Knepley     PetscCall(DMGetCoordinatesLocalSetUp(dm));
1024*9072cb8bSMatthew G. Knepley     for (PetscInt c = 0; c < cEnd - cStart; ++c) {
1025*9072cb8bSMatthew G. Knepley       const PetscInt     cell = c + cStart;
1026*9072cb8bSMatthew G. Knepley       const PetscScalar *a;
1027*9072cb8bSMatthew G. Knepley       PetscScalar       *coords;
1028*9072cb8bSMatthew G. Knepley       PetscReal          lower[6], upper[6];
1029*9072cb8bSMatthew G. Knepley       PetscBool          isDG;
1030*9072cb8bSMatthew G. Knepley       PetscInt          *pidx, npc, Nc;
1031*9072cb8bSMatthew G. Knepley 
1032*9072cb8bSMatthew G. Knepley       PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &npc, &pidx));
1033*9072cb8bSMatthew G. Knepley       PetscCheck(Npc == npc, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid number of points per cell %" PetscInt_FMT " != %" PetscInt_FMT, npc, Npc);
1034*9072cb8bSMatthew G. Knepley       PetscCall(DMPlexGetCellCoordinates(dm, cell, &isDG, &Nc, &a, &coords));
1035*9072cb8bSMatthew G. Knepley       for (PetscInt d = 0; d < dim; ++d) {
1036*9072cb8bSMatthew G. Knepley         lower[d] = coords[0 * dim + d];
1037*9072cb8bSMatthew G. Knepley         upper[d] = coords[0 * dim + d];
1038*9072cb8bSMatthew G. Knepley       }
1039*9072cb8bSMatthew G. Knepley       for (PetscInt i = 1; i < Nc / dim; ++i) {
1040*9072cb8bSMatthew G. Knepley         for (PetscInt d = 0; d < dim; ++d) {
1041*9072cb8bSMatthew G. Knepley           lower[d] = PetscMin(lower[d], coords[i * dim + d]);
1042*9072cb8bSMatthew G. Knepley           upper[d] = PetscMax(upper[d], coords[i * dim + d]);
1043*9072cb8bSMatthew G. Knepley         }
1044*9072cb8bSMatthew G. Knepley       }
1045*9072cb8bSMatthew G. Knepley       for (PetscInt s = 0; s < Ns; ++s) {
1046*9072cb8bSMatthew G. Knepley         for (PetscInt q = 0; q < Npc / Ns; ++q) {
1047*9072cb8bSMatthew G. Knepley           const PetscInt p = pidx[q * Ns + s];
1048*9072cb8bSMatthew G. Knepley           PetscInt       xi[3], vi[3];
1049*9072cb8bSMatthew G. Knepley 
1050*9072cb8bSMatthew G. Knepley           xi[0] = q % n;
1051*9072cb8bSMatthew G. Knepley           xi[1] = (q / n) % n;
1052*9072cb8bSMatthew G. Knepley           xi[2] = (q / PetscSqr(n)) % n;
1053*9072cb8bSMatthew G. Knepley           for (PetscInt d = 0; d < xdim; ++d) x[p * xdim + d] = lower[d] + (xi[d] + 0.5) * (upper[d] - lower[d]) / n;
1054*9072cb8bSMatthew G. Knepley           vi[0] = (q / PetscPowInt(n, xdim)) % n;
1055*9072cb8bSMatthew G. Knepley           vi[1] = (q / PetscPowInt(n, xdim + 1)) % n;
1056*9072cb8bSMatthew G. Knepley           vi[2] = (q / PetscPowInt(n, xdim + 2));
1057*9072cb8bSMatthew G. Knepley           for (PetscInt d = 0; d < vdim; ++d) v[p * vdim + d] = lower[xdim + d] + (vi[d] + 0.5) * (upper[xdim + d] - lower[xdim + d]) / n;
1058*9072cb8bSMatthew G. Knepley           if (debug > 1) {
1059*9072cb8bSMatthew G. Knepley             PetscCall(PetscPrintf(PETSC_COMM_SELF, "Particle %4" PetscInt_FMT " ", p));
1060*9072cb8bSMatthew G. Knepley             PetscCall(PetscPrintf(PETSC_COMM_SELF, "  x: ("));
1061*9072cb8bSMatthew G. Knepley             for (PetscInt d = 0; d < xdim; ++d) {
1062*9072cb8bSMatthew G. Knepley               if (d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
1063*9072cb8bSMatthew G. Knepley               PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", x[p * xdim + d]));
1064*9072cb8bSMatthew G. Knepley             }
1065*9072cb8bSMatthew G. Knepley             PetscCall(PetscPrintf(PETSC_COMM_SELF, ") v:("));
1066*9072cb8bSMatthew G. Knepley             for (PetscInt d = 0; d < vdim; ++d) {
1067*9072cb8bSMatthew G. Knepley               if (d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
1068*9072cb8bSMatthew G. Knepley               PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", v[p * vdim + d]));
1069*9072cb8bSMatthew G. Knepley             }
1070*9072cb8bSMatthew G. Knepley             PetscCall(PetscPrintf(PETSC_COMM_SELF, ")\n"));
1071*9072cb8bSMatthew G. Knepley           }
1072*9072cb8bSMatthew G. Knepley         }
1073*9072cb8bSMatthew G. Knepley       }
1074*9072cb8bSMatthew G. Knepley       PetscCall(DMPlexRestoreCellCoordinates(dm, cell, &isDG, &Nc, &a, &coords));
1075*9072cb8bSMatthew G. Knepley       PetscCall(DMSwarmSortRestorePointsPerCell(sw, c, &Npc, &pidx));
1076*9072cb8bSMatthew G. Knepley     }
1077*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmSortRestoreAccess(sw));
1078*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmRestoreField(sw, coordNames[0], NULL, NULL, (void **)&x));
1079*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmRestoreField(sw, coordNames[1], NULL, NULL, (void **)&v));
1080*9072cb8bSMatthew G. Knepley   }
1081*9072cb8bSMatthew G. Knepley   PetscFunctionReturn(PETSC_SUCCESS);
1082*9072cb8bSMatthew G. Knepley }
1083*9072cb8bSMatthew G. Knepley 
1084*9072cb8bSMatthew G. Knepley /*
1085*9072cb8bSMatthew G. Knepley   InitializeParticles_Centroid - Initialize a regular grid of particles.
1086*9072cb8bSMatthew G. Knepley 
1087*9072cb8bSMatthew G. Knepley   Input Parameters:
1088*9072cb8bSMatthew G. Knepley + sw      - The `DMSWARM`
1089*9072cb8bSMatthew G. Knepley - force1D - Treat the spatial domain as 1D
1090*9072cb8bSMatthew G. Knepley 
1091*9072cb8bSMatthew G. Knepley   Notes:
1092*9072cb8bSMatthew G. Knepley   This functions sets the species, cellid, spatial coordinate, and velocity fields for all particles.
1093*9072cb8bSMatthew G. Knepley 
1094*9072cb8bSMatthew G. Knepley   It places one particle in the centroid of each cell in the implicit tensor product of the spatial
1095*9072cb8bSMatthew G. Knepley   and velocity meshes.
1096*9072cb8bSMatthew G. Knepley */
109784467f86SMatthew G. Knepley static PetscErrorCode InitializeParticles_Centroid(DM sw, PetscBool force1D)
10988214e71cSJoe {
109984467f86SMatthew G. Knepley   DM_Swarm     *swarm = (DM_Swarm *)sw->data;
1100*9072cb8bSMatthew G. Knepley   DMSwarmCellDM celldm;
110184467f86SMatthew G. Knepley   DM            xdm, vdm;
110284467f86SMatthew G. Knepley   PetscReal     vmin[3], vmax[3];
110384467f86SMatthew G. Knepley   PetscReal    *x, *v;
110484467f86SMatthew G. Knepley   PetscInt     *species, *cellid;
110584467f86SMatthew G. Knepley   PetscInt      dim, xcStart, xcEnd, vcStart, vcEnd, Ns, Np, Npc, debug;
11068214e71cSJoe   PetscBool     flg;
110784467f86SMatthew G. Knepley   MPI_Comm      comm;
1108*9072cb8bSMatthew G. Knepley   const char   *cellidname;
11098214e71cSJoe 
11108214e71cSJoe   PetscFunctionBegin;
111184467f86SMatthew G. Knepley   PetscCall(PetscObjectGetComm((PetscObject)sw, &comm));
111284467f86SMatthew G. Knepley 
111384467f86SMatthew G. Knepley   PetscOptionsBegin(comm, "", "DMSwarm Options", "DMSWARM");
11148214e71cSJoe   PetscCall(DMSwarmGetNumSpecies(sw, &Ns));
11158214e71cSJoe   PetscCall(PetscOptionsInt("-dm_swarm_num_species", "The number of species", "DMSwarmSetNumSpecies", Ns, &Ns, &flg));
11168214e71cSJoe   if (flg) PetscCall(DMSwarmSetNumSpecies(sw, Ns));
111784467f86SMatthew G. Knepley   PetscCall(PetscOptionsBoundedInt("-dm_swarm_print_coords", "Debug output level for particle coordinate computations", "InitializeParticles", 0, &swarm->printCoords, NULL, 0));
111884467f86SMatthew G. Knepley   PetscCall(PetscOptionsBoundedInt("-dm_swarm_print_weights", "Debug output level for particle weight computations", "InitializeWeights", 0, &swarm->printWeights, NULL, 0));
11198214e71cSJoe   PetscOptionsEnd();
112084467f86SMatthew G. Knepley   debug = swarm->printCoords;
11218214e71cSJoe 
11228214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
112384467f86SMatthew G. Knepley   PetscCall(DMSwarmGetCellDM(sw, &xdm));
112484467f86SMatthew G. Knepley   PetscCall(DMPlexGetHeightStratum(xdm, 0, &xcStart, &xcEnd));
11258214e71cSJoe 
112684467f86SMatthew G. Knepley   PetscCall(PetscObjectQuery((PetscObject)sw, "__vdm__", (PetscObject *)&vdm));
112784467f86SMatthew G. Knepley   PetscCall(DMPlexGetHeightStratum(vdm, 0, &vcStart, &vcEnd));
11288214e71cSJoe 
112984467f86SMatthew G. Knepley   // One particle per centroid on the tensor product grid
113084467f86SMatthew G. Knepley   Npc = (vcEnd - vcStart) * Ns;
113184467f86SMatthew G. Knepley   Np  = (xcEnd - xcStart) * Npc;
11328214e71cSJoe   PetscCall(DMSwarmSetLocalSizes(sw, Np, 0));
113384467f86SMatthew G. Knepley   if (debug) {
113484467f86SMatthew G. Knepley     PetscInt gNp;
113584467f86SMatthew G. Knepley     PetscCallMPI(MPIU_Allreduce(&Np, &gNp, 1, MPIU_INT, MPIU_SUM, comm));
113684467f86SMatthew G. Knepley     PetscCall(PetscPrintf(comm, "Global Np = %" PetscInt_FMT "\n", gNp));
11378214e71cSJoe   }
11388214e71cSJoe 
113984467f86SMatthew G. Knepley   // Set species and cellid
1140*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetCellDMActive(sw, &celldm));
1141*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmCellDMGetCellID(celldm, &cellidname));
114284467f86SMatthew G. Knepley   PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **)&species));
1143*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetField(sw, cellidname, NULL, NULL, (void **)&cellid));
114484467f86SMatthew G. Knepley   for (PetscInt c = 0, p = 0; c < xcEnd - xcStart; ++c) {
114584467f86SMatthew G. Knepley     for (PetscInt s = 0; s < Ns; ++s) {
114684467f86SMatthew G. Knepley       for (PetscInt q = 0; q < Npc / Ns; ++q, ++p) {
114784467f86SMatthew G. Knepley         species[p] = s;
114884467f86SMatthew G. Knepley         cellid[p]  = c;
114984467f86SMatthew G. Knepley       }
115084467f86SMatthew G. Knepley     }
115184467f86SMatthew G. Knepley   }
115284467f86SMatthew G. Knepley   PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **)&species));
1153*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmRestoreField(sw, cellidname, NULL, NULL, (void **)&cellid));
115484467f86SMatthew G. Knepley 
115584467f86SMatthew G. Knepley   // Set particle coordinates
11568214e71cSJoe   PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
11578214e71cSJoe   PetscCall(DMSwarmGetField(sw, "velocity", NULL, NULL, (void **)&v));
11588214e71cSJoe   PetscCall(DMSwarmSortGetAccess(sw));
115984467f86SMatthew G. Knepley   PetscCall(DMGetBoundingBox(vdm, vmin, vmax));
116084467f86SMatthew G. Knepley   PetscCall(DMGetCoordinatesLocalSetUp(xdm));
116184467f86SMatthew G. Knepley   for (PetscInt c = 0; c < xcEnd - xcStart; ++c) {
116284467f86SMatthew G. Knepley     const PetscInt cell = c + xcStart;
11638214e71cSJoe     PetscInt      *pidx, Npc;
11648214e71cSJoe     PetscReal      centroid[3], volume;
11658214e71cSJoe 
11668214e71cSJoe     PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Npc, &pidx));
116784467f86SMatthew G. Knepley     PetscCall(DMPlexComputeCellGeometryFVM(xdm, cell, &volume, centroid, NULL));
116884467f86SMatthew G. Knepley     for (PetscInt s = 0; s < Ns; ++s) {
116984467f86SMatthew G. Knepley       for (PetscInt q = 0; q < Npc / Ns; ++q) {
117084467f86SMatthew G. Knepley         const PetscInt p = pidx[q * Ns + s];
11718214e71cSJoe 
117284467f86SMatthew G. Knepley         for (PetscInt d = 0; d < dim; ++d) {
11738214e71cSJoe           x[p * dim + d] = centroid[d];
117484467f86SMatthew G. Knepley           v[p * dim + d] = vmin[0] + (q + 0.5) * ((vmax[0] - vmin[0]) / (Npc / Ns));
117584467f86SMatthew G. Knepley           if (force1D && d > 0) v[p * dim + d] = 0.;
11768214e71cSJoe         }
1177*9072cb8bSMatthew G. Knepley         if (debug > 1) {
1178*9072cb8bSMatthew G. Knepley           PetscCall(PetscPrintf(PETSC_COMM_SELF, "Particle %4" PetscInt_FMT " ", p));
1179*9072cb8bSMatthew G. Knepley           PetscCall(PetscPrintf(PETSC_COMM_SELF, "  x: ("));
1180*9072cb8bSMatthew G. Knepley           for (PetscInt d = 0; d < dim; ++d) {
1181*9072cb8bSMatthew G. Knepley             if (d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
1182*9072cb8bSMatthew G. Knepley             PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", x[p * dim + d]));
1183*9072cb8bSMatthew G. Knepley           }
1184*9072cb8bSMatthew G. Knepley           PetscCall(PetscPrintf(PETSC_COMM_SELF, ") v:("));
1185*9072cb8bSMatthew G. Knepley           for (PetscInt d = 0; d < dim; ++d) {
1186*9072cb8bSMatthew G. Knepley             if (d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", "));
1187*9072cb8bSMatthew G. Knepley             PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", v[p * dim + d]));
1188*9072cb8bSMatthew G. Knepley           }
1189*9072cb8bSMatthew G. Knepley           PetscCall(PetscPrintf(PETSC_COMM_SELF, ")\n"));
1190*9072cb8bSMatthew G. Knepley         }
11918214e71cSJoe       }
11928214e71cSJoe     }
119384467f86SMatthew G. Knepley     PetscCall(DMSwarmSortRestorePointsPerCell(sw, c, &Npc, &pidx));
11948214e71cSJoe   }
11958214e71cSJoe   PetscCall(DMSwarmSortRestoreAccess(sw));
11968214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
11978214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "velocity", NULL, NULL, (void **)&v));
119884467f86SMatthew G. Knepley   PetscFunctionReturn(PETSC_SUCCESS);
119984467f86SMatthew G. Knepley }
120084467f86SMatthew G. Knepley 
120184467f86SMatthew G. Knepley /*
120284467f86SMatthew G. Knepley   InitializeWeights - Compute weight for each local particle
120384467f86SMatthew G. Knepley 
120484467f86SMatthew G. Knepley   Input Parameters:
120584467f86SMatthew G. Knepley + sw          - The `DMSwarm`
120684467f86SMatthew G. Knepley . totalWeight - The sum of all particle weights
120784467f86SMatthew G. Knepley . force1D     - Flag to treat the problem as 1D
120884467f86SMatthew G. Knepley . func        - The PDF for the particle spatial distribution
120984467f86SMatthew G. Knepley - param       - The PDF parameters
121084467f86SMatthew G. Knepley 
121184467f86SMatthew G. Knepley   Notes:
121284467f86SMatthew G. Knepley   The PDF for velocity is assumed to be a Gaussian
121384467f86SMatthew G. Knepley 
121484467f86SMatthew G. Knepley   The particle weights are returned in the `w_q` field of `sw`.
121584467f86SMatthew G. Knepley */
121684467f86SMatthew G. Knepley static PetscErrorCode InitializeWeights(DM sw, PetscReal totalWeight, PetscBool force1D, PetscProbFunc func, const PetscReal param[])
121784467f86SMatthew G. Knepley {
121884467f86SMatthew G. Knepley   DM               xdm, vdm;
121984467f86SMatthew G. Knepley   PetscScalar     *weight;
122084467f86SMatthew G. Knepley   PetscQuadrature  xquad;
122184467f86SMatthew G. Knepley   const PetscReal *xq, *xwq;
122284467f86SMatthew G. Knepley   const PetscInt   order = 5;
122384467f86SMatthew G. Knepley   PetscReal       *xqd   = NULL, xi0[3];
122484467f86SMatthew G. Knepley   PetscReal        xwtot = 0., pwtot = 0.;
122584467f86SMatthew G. Knepley   PetscInt         xNq;
122684467f86SMatthew G. Knepley   PetscInt         dim, Ns, xcStart, xcEnd, vcStart, vcEnd, debug = ((DM_Swarm *)sw->data)->printWeights;
122784467f86SMatthew G. Knepley   MPI_Comm         comm;
122884467f86SMatthew G. Knepley   PetscMPIInt      rank;
122984467f86SMatthew G. Knepley 
123084467f86SMatthew G. Knepley   PetscFunctionBegin;
123184467f86SMatthew G. Knepley   PetscCall(PetscObjectGetComm((PetscObject)sw, &comm));
123284467f86SMatthew G. Knepley   PetscCallMPI(MPI_Comm_rank(comm, &rank));
123384467f86SMatthew G. Knepley   PetscCall(DMGetDimension(sw, &dim));
123484467f86SMatthew G. Knepley   PetscCall(DMSwarmGetCellDM(sw, &xdm));
123584467f86SMatthew G. Knepley   PetscCall(DMSwarmGetNumSpecies(sw, &Ns));
123684467f86SMatthew G. Knepley   PetscCall(DMPlexGetHeightStratum(xdm, 0, &xcStart, &xcEnd));
123784467f86SMatthew G. Knepley   PetscCall(PetscObjectQuery((PetscObject)sw, "__vdm__", (PetscObject *)&vdm));
123884467f86SMatthew G. Knepley   PetscCall(DMPlexGetHeightStratum(vdm, 0, &vcStart, &vcEnd));
123984467f86SMatthew G. Knepley 
124084467f86SMatthew G. Knepley   // Setup Quadrature for spatial and velocity weight calculations
124184467f86SMatthew G. Knepley   if (force1D) PetscCall(PetscDTGaussTensorQuadrature(1, 1, order, -1.0, 1.0, &xquad));
124284467f86SMatthew G. Knepley   else PetscCall(PetscDTGaussTensorQuadrature(dim, 1, order, -1.0, 1.0, &xquad));
124384467f86SMatthew G. Knepley   PetscCall(PetscQuadratureGetData(xquad, NULL, NULL, &xNq, &xq, &xwq));
124484467f86SMatthew G. Knepley   if (force1D) {
124584467f86SMatthew G. Knepley     PetscCall(PetscCalloc1(xNq * dim, &xqd));
124684467f86SMatthew G. Knepley     for (PetscInt q = 0; q < xNq; ++q) xqd[q * dim] = xq[q];
124784467f86SMatthew G. Knepley   }
124884467f86SMatthew G. Knepley   for (PetscInt d = 0; d < dim; ++d) xi0[d] = -1.0;
124984467f86SMatthew G. Knepley 
125084467f86SMatthew G. Knepley   // Integrate the density function to get the weights of particles in each cell
125184467f86SMatthew G. Knepley   PetscCall(DMGetCoordinatesLocalSetUp(vdm));
125284467f86SMatthew G. Knepley   PetscCall(DMSwarmSortGetAccess(sw));
125384467f86SMatthew G. Knepley   PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&weight));
125484467f86SMatthew G. Knepley   for (PetscInt c = xcStart; c < xcEnd; ++c) {
125584467f86SMatthew G. Knepley     PetscReal          xv0[3], xJ[9], xinvJ[9], xdetJ, xqr[3], xden, xw = 0.;
125684467f86SMatthew G. Knepley     PetscInt          *pidx, Npc;
125784467f86SMatthew G. Knepley     PetscInt           xNc;
125884467f86SMatthew G. Knepley     const PetscScalar *xarray;
125984467f86SMatthew G. Knepley     PetscScalar       *xcoords = NULL;
126084467f86SMatthew G. Knepley     PetscBool          xisDG;
126184467f86SMatthew G. Knepley 
126284467f86SMatthew G. Knepley     PetscCall(DMPlexGetCellCoordinates(xdm, c, &xisDG, &xNc, &xarray, &xcoords));
126384467f86SMatthew G. Knepley     PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Npc, &pidx));
126484467f86SMatthew G. Knepley     PetscCheck(Npc == (vcEnd - vcStart) * Ns, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of particles %" PetscInt_FMT " in cell (rank %d) != %" PetscInt_FMT " number of velocity vertices", Npc, rank, (vcEnd - vcStart) * Ns);
126584467f86SMatthew G. Knepley     PetscCall(DMPlexComputeCellGeometryFEM(xdm, c, NULL, xv0, xJ, xinvJ, &xdetJ));
126684467f86SMatthew G. Knepley     for (PetscInt q = 0; q < xNq; ++q) {
126784467f86SMatthew G. Knepley       // Transform quadrature points from ref space to real space
126884467f86SMatthew G. Knepley       if (force1D) CoordinatesRefToReal(dim, dim, xi0, xv0, xJ, &xqd[q * dim], xqr);
126984467f86SMatthew G. Knepley       else CoordinatesRefToReal(dim, dim, xi0, xv0, xJ, &xq[q * dim], xqr);
127084467f86SMatthew G. Knepley       // Get probability density at quad point
127184467f86SMatthew G. Knepley       //   No need to scale xqr since PDF will be periodic
127284467f86SMatthew G. Knepley       PetscCall((*func)(xqr, param, &xden));
127384467f86SMatthew G. Knepley       if (force1D) xdetJ = xJ[0]; // Only want x contribution
127484467f86SMatthew G. Knepley       xw += xden * (xwq[q] * xdetJ);
127584467f86SMatthew G. Knepley     }
127684467f86SMatthew G. Knepley     xwtot += xw;
127784467f86SMatthew G. Knepley     if (debug) {
127884467f86SMatthew G. Knepley       IS              globalOrdering;
127984467f86SMatthew G. Knepley       const PetscInt *ordering;
128084467f86SMatthew G. Knepley 
128184467f86SMatthew G. Knepley       PetscCall(DMPlexGetCellNumbering(xdm, &globalOrdering));
128284467f86SMatthew G. Knepley       PetscCall(ISGetIndices(globalOrdering, &ordering));
128375155f48SMatthew G. Knepley       PetscCall(PetscSynchronizedPrintf(comm, "c:%" PetscInt_FMT " [x_a,x_b] = %1.15f,%1.15f -> cell weight = %1.15f\n", ordering[c], (double)PetscRealPart(xcoords[0]), (double)PetscRealPart(xcoords[0 + dim]), (double)xw));
128484467f86SMatthew G. Knepley       PetscCall(ISRestoreIndices(globalOrdering, &ordering));
128584467f86SMatthew G. Knepley     }
128684467f86SMatthew G. Knepley     // Set weights to be Gaussian in velocity cells
128784467f86SMatthew G. Knepley     for (PetscInt vc = vcStart; vc < vcEnd; ++vc) {
128884467f86SMatthew G. Knepley       const PetscInt     p  = pidx[vc * Ns + 0];
128984467f86SMatthew G. Knepley       PetscReal          vw = 0.;
129084467f86SMatthew G. Knepley       PetscInt           vNc;
129184467f86SMatthew G. Knepley       const PetscScalar *varray;
129284467f86SMatthew G. Knepley       PetscScalar       *vcoords = NULL;
129384467f86SMatthew G. Knepley       PetscBool          visDG;
129484467f86SMatthew G. Knepley 
129584467f86SMatthew G. Knepley       PetscCall(DMPlexGetCellCoordinates(vdm, vc, &visDG, &vNc, &varray, &vcoords));
129684467f86SMatthew G. Knepley       // TODO: Fix 2 stream Ask Joe
129784467f86SMatthew G. Knepley       //   Two stream function from 1/2pi v^2 e^(-v^2/2)
129884467f86SMatthew G. Knepley       //   vw = 1. / (PetscSqrtReal(2 * PETSC_PI)) * (((coords_v[0] * PetscExpReal(-PetscSqr(coords_v[0]) / 2.)) - (coords_v[1] * PetscExpReal(-PetscSqr(coords_v[1]) / 2.)))) - 0.5 * PetscErfReal(coords_v[0] / PetscSqrtReal(2.)) + 0.5 * (PetscErfReal(coords_v[1] / PetscSqrtReal(2.)));
129984467f86SMatthew G. Knepley       vw = 0.5 * (PetscErfReal(vcoords[1] / PetscSqrtReal(2.)) - PetscErfReal(vcoords[0] / PetscSqrtReal(2.)));
130084467f86SMatthew G. Knepley 
130184467f86SMatthew G. Knepley       weight[p] = totalWeight * vw * xw;
130284467f86SMatthew G. Knepley       pwtot += weight[p];
1303*9072cb8bSMatthew G. Knepley       PetscCheck(weight[p] <= 10., PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Particle %" PetscInt_FMT " weight exceeeded 1: %g, %g, %g", p, xw, vw, totalWeight);
130484467f86SMatthew G. Knepley       PetscCall(DMPlexRestoreCellCoordinates(vdm, vc, &visDG, &vNc, &varray, &vcoords));
130584467f86SMatthew G. Knepley       if (debug > 1) PetscCall(PetscPrintf(comm, "particle %" PetscInt_FMT ": %g, vw: %g xw: %g\n", p, weight[p], vw, xw));
130684467f86SMatthew G. Knepley     }
130784467f86SMatthew G. Knepley     PetscCall(DMPlexRestoreCellCoordinates(xdm, c, &xisDG, &xNc, &xarray, &xcoords));
130884467f86SMatthew G. Knepley     PetscCall(DMSwarmSortRestorePointsPerCell(sw, c, &Npc, &pidx));
130984467f86SMatthew G. Knepley   }
131084467f86SMatthew G. Knepley   PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&weight));
131184467f86SMatthew G. Knepley   PetscCall(DMSwarmSortRestoreAccess(sw));
131284467f86SMatthew G. Knepley   PetscCall(PetscQuadratureDestroy(&xquad));
131384467f86SMatthew G. Knepley   if (force1D) PetscCall(PetscFree(xqd));
131484467f86SMatthew G. Knepley 
131584467f86SMatthew G. Knepley   if (debug) {
131684467f86SMatthew G. Knepley     PetscReal wtot[2] = {pwtot, xwtot}, gwtot[2];
131784467f86SMatthew G. Knepley 
131884467f86SMatthew G. Knepley     PetscCall(PetscSynchronizedFlush(comm, NULL));
131984467f86SMatthew G. Knepley     PetscCallMPI(MPIU_Allreduce(wtot, gwtot, 2, MPIU_REAL, MPIU_SUM, PETSC_COMM_WORLD));
132084467f86SMatthew G. Knepley     PetscCall(PetscPrintf(comm, "particle weight sum = %1.10f cell weight sum = %1.10f\n", (double)gwtot[0], (double)gwtot[1]));
132184467f86SMatthew G. Knepley   }
132284467f86SMatthew G. Knepley   PetscFunctionReturn(PETSC_SUCCESS);
132384467f86SMatthew G. Knepley }
132484467f86SMatthew G. Knepley 
132584467f86SMatthew G. Knepley static PetscErrorCode InitializeParticles_PerturbedWeights(DM sw, AppCtx *user)
132684467f86SMatthew G. Knepley {
132784467f86SMatthew G. Knepley   PetscReal scale[2] = {user->cosine_coefficients[0], user->cosine_coefficients[1]};
132875155f48SMatthew G. Knepley   PetscInt  dim;
132984467f86SMatthew G. Knepley 
133084467f86SMatthew G. Knepley   PetscFunctionBegin;
133175155f48SMatthew G. Knepley   PetscCall(DMGetDimension(sw, &dim));
133284467f86SMatthew G. Knepley   PetscCall(InitializeParticles_Centroid(sw, user->fake_1D));
133375155f48SMatthew G. Knepley   PetscCall(InitializeWeights(sw, user->totalWeight, user->fake_1D, dim == 1 || user->fake_1D ? PetscPDFCosine1D : PetscPDFCosine2D, scale));
13348214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
13358214e71cSJoe }
13368214e71cSJoe 
13378214e71cSJoe static PetscErrorCode InitializeConstants(DM sw, AppCtx *user)
13388214e71cSJoe {
13398214e71cSJoe   DM         dm;
13408214e71cSJoe   PetscInt  *species;
13418214e71cSJoe   PetscReal *weight, totalCharge = 0., totalWeight = 0., gmin[3], gmax[3], global_charge, global_weight;
13428214e71cSJoe   PetscInt   Np, dim;
13438214e71cSJoe 
13448214e71cSJoe   PetscFunctionBegin;
13458214e71cSJoe   PetscCall(DMSwarmGetCellDM(sw, &dm));
13468214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
13478214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
13488214e71cSJoe   PetscCall(DMGetBoundingBox(dm, gmin, gmax));
13498214e71cSJoe   PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&weight));
13508214e71cSJoe   PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **)&species));
13518214e71cSJoe   for (PetscInt p = 0; p < Np; ++p) {
13528214e71cSJoe     totalWeight += weight[p];
13538214e71cSJoe     totalCharge += user->charges[species[p]] * weight[p];
13548214e71cSJoe   }
13558214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&weight));
13568214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **)&species));
13578214e71cSJoe   {
13588214e71cSJoe     Parameter *param;
13598214e71cSJoe     PetscReal  Area;
13608214e71cSJoe 
13618214e71cSJoe     PetscCall(PetscBagGetData(user->bag, (void **)&param));
13628214e71cSJoe     switch (dim) {
13638214e71cSJoe     case 1:
13648214e71cSJoe       Area = (gmax[0] - gmin[0]);
13658214e71cSJoe       break;
13668214e71cSJoe     case 2:
13678214e71cSJoe       if (user->fake_1D) {
13688214e71cSJoe         Area = (gmax[0] - gmin[0]);
13698214e71cSJoe       } else {
13708214e71cSJoe         Area = (gmax[0] - gmin[0]) * (gmax[1] - gmin[1]);
13718214e71cSJoe       }
13728214e71cSJoe       break;
13738214e71cSJoe     case 3:
13748214e71cSJoe       Area = (gmax[0] - gmin[0]) * (gmax[1] - gmin[1]) * (gmax[2] - gmin[2]);
13758214e71cSJoe       break;
13768214e71cSJoe     default:
13778214e71cSJoe       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Dimension %" PetscInt_FMT " not supported", dim);
13788214e71cSJoe     }
1379462c564dSBarry Smith     PetscCallMPI(MPIU_Allreduce(&totalWeight, &global_weight, 1, MPIU_REAL, MPIU_SUM, PETSC_COMM_WORLD));
1380462c564dSBarry Smith     PetscCallMPI(MPIU_Allreduce(&totalCharge, &global_charge, 1, MPIU_REAL, MPIU_SUM, PETSC_COMM_WORLD));
13818214e71cSJoe     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "dim = %" PetscInt_FMT "\ttotalWeight = %f, user->charges[species[0]] = %f\ttotalCharge = %f, Total Area = %f\n", dim, (double)global_weight, (double)user->charges[0], (double)global_charge, (double)Area));
13828214e71cSJoe     param->sigma = PetscAbsReal(global_charge / (Area));
13838214e71cSJoe 
13848214e71cSJoe     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "sigma: %g\n", (double)param->sigma));
13858214e71cSJoe     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "(x0,v0,t0,m0,q0,phi0): (%e, %e, %e, %e, %e, %e) - (P, V) = (%e, %e)\n", (double)param->x0, (double)param->v0, (double)param->t0, (double)param->m0, (double)param->q0, (double)param->phi0, (double)param->poissonNumber,
13868214e71cSJoe                           (double)param->vlasovNumber));
13878214e71cSJoe   }
13888214e71cSJoe   /* Setup Constants */
13898214e71cSJoe   {
13908214e71cSJoe     PetscDS    ds;
13918214e71cSJoe     Parameter *param;
13928214e71cSJoe     PetscCall(PetscBagGetData(user->bag, (void **)&param));
13938214e71cSJoe     PetscScalar constants[NUM_CONSTANTS];
13948214e71cSJoe     constants[SIGMA]   = param->sigma;
13958214e71cSJoe     constants[V0]      = param->v0;
13968214e71cSJoe     constants[T0]      = param->t0;
13978214e71cSJoe     constants[X0]      = param->x0;
13988214e71cSJoe     constants[M0]      = param->m0;
13998214e71cSJoe     constants[Q0]      = param->q0;
14008214e71cSJoe     constants[PHI0]    = param->phi0;
14018214e71cSJoe     constants[POISSON] = param->poissonNumber;
14028214e71cSJoe     constants[VLASOV]  = param->vlasovNumber;
14038214e71cSJoe     PetscCall(DMGetDS(dm, &ds));
14048214e71cSJoe     PetscCall(PetscDSSetConstants(ds, NUM_CONSTANTS, constants));
14058214e71cSJoe   }
14068214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
14078214e71cSJoe }
14088214e71cSJoe 
14098214e71cSJoe static PetscErrorCode InitializeVelocities_Fake1D(DM sw, AppCtx *user)
14108214e71cSJoe {
14118214e71cSJoe   DM         dm;
14128214e71cSJoe   PetscReal *v;
14138214e71cSJoe   PetscInt  *species, cStart, cEnd;
14148214e71cSJoe   PetscInt   dim, Np;
14158214e71cSJoe 
14168214e71cSJoe   PetscFunctionBegin;
14178214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
14188214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
14198214e71cSJoe   PetscCall(DMSwarmGetField(sw, "velocity", NULL, NULL, (void **)&v));
14208214e71cSJoe   PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **)&species));
14218214e71cSJoe   PetscCall(DMSwarmGetCellDM(sw, &dm));
14228214e71cSJoe   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
14238214e71cSJoe   PetscRandom rnd;
14248214e71cSJoe   PetscCall(PetscRandomCreate(PetscObjectComm((PetscObject)sw), &rnd));
14258214e71cSJoe   PetscCall(PetscRandomSetInterval(rnd, 0, 1.));
14268214e71cSJoe   PetscCall(PetscRandomSetFromOptions(rnd));
14278214e71cSJoe 
14288214e71cSJoe   for (PetscInt p = 0; p < Np; ++p) {
14298214e71cSJoe     PetscReal a[3] = {0., 0., 0.}, vel[3] = {0., 0., 0.};
14308214e71cSJoe 
14318214e71cSJoe     PetscCall(PetscRandomGetValueReal(rnd, &a[0]));
14328214e71cSJoe     if (user->perturbed_weights) {
14338214e71cSJoe       PetscCall(PetscPDFSampleConstant1D(a, NULL, vel));
14348214e71cSJoe     } else {
14358214e71cSJoe       PetscCall(PetscPDFSampleGaussian1D(a, NULL, vel));
14368214e71cSJoe     }
14378214e71cSJoe     v[p * dim] = vel[0];
14388214e71cSJoe   }
14398214e71cSJoe   PetscCall(PetscRandomDestroy(&rnd));
14408214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "velocity", NULL, NULL, (void **)&v));
14418214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **)&species));
14428214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
14438214e71cSJoe }
14448214e71cSJoe 
14458214e71cSJoe static PetscErrorCode CreateSwarm(DM dm, AppCtx *user, DM *sw)
14468214e71cSJoe {
1447*9072cb8bSMatthew G. Knepley   DMSwarmCellDM celldm;
1448*9072cb8bSMatthew G. Knepley   DM            vdm;
14498214e71cSJoe   PetscReal     v0[2] = {1., 0.};
14508214e71cSJoe   PetscInt      dim;
1451b80bf5b1SJoe Pusztay 
1452b80bf5b1SJoe Pusztay   PetscFunctionBeginUser;
14539566063dSJacob Faibussowitsch   PetscCall(DMGetDimension(dm, &dim));
14549566063dSJacob Faibussowitsch   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), sw));
14559566063dSJacob Faibussowitsch   PetscCall(DMSetType(*sw, DMSWARM));
14569566063dSJacob Faibussowitsch   PetscCall(DMSetDimension(*sw, dim));
14579566063dSJacob Faibussowitsch   PetscCall(DMSwarmSetType(*sw, DMSWARM_PIC));
1458*9072cb8bSMatthew G. Knepley 
14599566063dSJacob Faibussowitsch   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "w_q", 1, PETSC_SCALAR));
14608214e71cSJoe   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "velocity", dim, PETSC_REAL));
14618214e71cSJoe   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "species", 1, PETSC_INT));
14628214e71cSJoe   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "initCoordinates", dim, PETSC_REAL));
14638214e71cSJoe   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "initVelocity", dim, PETSC_REAL));
14648214e71cSJoe   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "E_field", dim, PETSC_REAL));
1465*9072cb8bSMatthew G. Knepley 
1466*9072cb8bSMatthew G. Knepley   const char *fieldnames[2] = {DMSwarmPICField_coor, "velocity"};
1467*9072cb8bSMatthew G. Knepley 
1468*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmCellDMCreate(dm, 2, fieldnames, 1, fieldnames, &celldm));
1469*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmAddCellDM(*sw, celldm));
1470*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmCellDMDestroy(&celldm));
1471*9072cb8bSMatthew G. Knepley 
1472*9072cb8bSMatthew G. Knepley   const char *vfieldnames[1] = {"w_q"};
1473*9072cb8bSMatthew G. Knepley 
1474*9072cb8bSMatthew G. Knepley   PetscCall(CreateVelocityDM(*sw, &vdm));
1475*9072cb8bSMatthew G. Knepley   PetscCall(PetscObjectCompose((PetscObject)*sw, "__vdm__", (PetscObject)vdm));
1476*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmCellDMCreate(vdm, 1, vfieldnames, 1, &fieldnames[1], &celldm));
1477*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmAddCellDM(*sw, celldm));
1478*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmCellDMDestroy(&celldm));
1479*9072cb8bSMatthew G. Knepley   PetscCall(DMDestroy(&vdm));
1480*9072cb8bSMatthew G. Knepley 
1481*9072cb8bSMatthew G. Knepley   if (user->remap && user->remapType == RM_PFAK) {
1482*9072cb8bSMatthew G. Knepley     DM rdm;
1483*9072cb8bSMatthew G. Knepley 
1484*9072cb8bSMatthew G. Knepley     PetscCall(CreateRemapDM(*sw, &rdm));
1485*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmCellDMCreate(rdm, 1, vfieldnames, 2, fieldnames, &celldm));
1486*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmAddCellDM(*sw, celldm));
1487*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmCellDMDestroy(&celldm));
1488*9072cb8bSMatthew G. Knepley     PetscCall(DMDestroy(&rdm));
1489*9072cb8bSMatthew G. Knepley   }
1490*9072cb8bSMatthew G. Knepley 
14919566063dSJacob Faibussowitsch   PetscCall(DMSwarmFinalizeFieldRegister(*sw));
14928214e71cSJoe   PetscCall(DMSetApplicationContext(*sw, user));
14939566063dSJacob Faibussowitsch   PetscCall(DMSetFromOptions(*sw));
1494*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmSetCellDMActive(*sw, "space"));
14958214e71cSJoe   user->swarm = *sw;
1496*9072cb8bSMatthew G. Knepley   // TODO: This is redundant init since it is done in InitializeSolveAndSwarm. To remove it, we need to move the
1497*9072cb8bSMatthew G. Knepley   // creation of initCoordinates and initVelocity
14988214e71cSJoe   if (user->perturbed_weights) {
14998214e71cSJoe     PetscCall(InitializeParticles_PerturbedWeights(*sw, user));
1500b80bf5b1SJoe Pusztay   } else {
15018214e71cSJoe     PetscCall(DMSwarmComputeLocalSizeFromOptions(*sw));
15028214e71cSJoe     PetscCall(DMSwarmInitializeCoordinates(*sw));
15038214e71cSJoe     if (user->fake_1D) {
15048214e71cSJoe       PetscCall(InitializeVelocities_Fake1D(*sw, user));
15059371c9d4SSatish Balay     } else {
15068214e71cSJoe       PetscCall(DMSwarmInitializeVelocitiesFromOptions(*sw, v0));
1507b80bf5b1SJoe Pusztay     }
1508b80bf5b1SJoe Pusztay   }
15099566063dSJacob Faibussowitsch   PetscCall(PetscObjectSetName((PetscObject)*sw, "Particles"));
15109566063dSJacob Faibussowitsch   PetscCall(DMViewFromOptions(*sw, NULL, "-sw_view"));
15118214e71cSJoe   {
15128214e71cSJoe     Vec gc, gc0, gv, gv0;
15138214e71cSJoe 
15148214e71cSJoe     PetscCall(DMSwarmCreateGlobalVectorFromField(*sw, DMSwarmPICField_coor, &gc));
15158214e71cSJoe     PetscCall(DMSwarmCreateGlobalVectorFromField(*sw, "initCoordinates", &gc0));
15168214e71cSJoe     PetscCall(VecCopy(gc, gc0));
15178214e71cSJoe     PetscCall(VecViewFromOptions(gc, NULL, "-ic_x_view"));
15188214e71cSJoe     PetscCall(DMSwarmDestroyGlobalVectorFromField(*sw, DMSwarmPICField_coor, &gc));
15198214e71cSJoe     PetscCall(DMSwarmDestroyGlobalVectorFromField(*sw, "initCoordinates", &gc0));
15208214e71cSJoe     PetscCall(DMSwarmCreateGlobalVectorFromField(*sw, "velocity", &gv));
15218214e71cSJoe     PetscCall(DMSwarmCreateGlobalVectorFromField(*sw, "initVelocity", &gv0));
15228214e71cSJoe     PetscCall(VecCopy(gv, gv0));
15238214e71cSJoe     PetscCall(VecViewFromOptions(gv, NULL, "-ic_v_view"));
15248214e71cSJoe     PetscCall(DMSwarmDestroyGlobalVectorFromField(*sw, "velocity", &gv));
15258214e71cSJoe     PetscCall(DMSwarmDestroyGlobalVectorFromField(*sw, "initVelocity", &gv0));
15268214e71cSJoe   }
1527*9072cb8bSMatthew G. Knepley   PetscFunctionReturn(PETSC_SUCCESS);
152884467f86SMatthew G. Knepley }
1529*9072cb8bSMatthew G. Knepley 
1530*9072cb8bSMatthew G. Knepley /*
1531*9072cb8bSMatthew G. Knepley @article{MyersColellaVanStraalen2017,
1532*9072cb8bSMatthew G. Knepley    title   = {A 4th-order particle-in-cell method with phase-space remapping for the {Vlasov-Poisson} equation},
1533*9072cb8bSMatthew G. Knepley    author  = {Andrew Myers and Phillip Colella and Brian Van Straalen},
1534*9072cb8bSMatthew G. Knepley    journal = {SIAM Journal on Scientific Computing},
1535*9072cb8bSMatthew G. Knepley    volume  = {39},
1536*9072cb8bSMatthew G. Knepley    issue   = {3},
1537*9072cb8bSMatthew G. Knepley    pages   = {B467-B485},
1538*9072cb8bSMatthew G. Knepley    doi     = {10.1137/16M105962X},
1539*9072cb8bSMatthew G. Knepley    issn    = {10957197},
1540*9072cb8bSMatthew G. Knepley    year    = {2017},
1541*9072cb8bSMatthew G. Knepley }
1542*9072cb8bSMatthew G. Knepley */
1543*9072cb8bSMatthew G. Knepley static PetscErrorCode W_3_Interpolation_Private(PetscReal x, PetscReal *w)
1544*9072cb8bSMatthew G. Knepley {
1545*9072cb8bSMatthew G. Knepley   const PetscReal ax = PetscAbsReal(x);
1546*9072cb8bSMatthew G. Knepley 
1547*9072cb8bSMatthew G. Knepley   PetscFunctionBegin;
1548*9072cb8bSMatthew G. Knepley   *w = 0.;
1549*9072cb8bSMatthew G. Knepley   // W_3(x) = 1 - 5/2 |x|^2 + 3/2 |x|^3   0 \le |x| \e 1
1550*9072cb8bSMatthew G. Knepley   if (ax <= 1.) *w = 1. - 2.5 * PetscSqr(ax) + 1.5 * PetscSqr(ax) * ax;
1551*9072cb8bSMatthew G. Knepley   //          1/2 (2 - |x|)^2 (1 - |x|)   1 \le |x| \le 2
1552*9072cb8bSMatthew G. Knepley   else if (ax <= 2.) *w = 0.5 * PetscSqr(2. - ax) * (1. - ax);
1553*9072cb8bSMatthew G. Knepley   //PetscCall(PetscPrintf(PETSC_COMM_SELF, "    W_3 %g --> %g\n", x, *w));
1554*9072cb8bSMatthew G. Knepley   PetscFunctionReturn(PETSC_SUCCESS);
1555*9072cb8bSMatthew G. Knepley }
1556*9072cb8bSMatthew G. Knepley 
1557*9072cb8bSMatthew G. Knepley // Right now, we will assume that the spatial and velocity grids are regular, which will speed up point location immensely
1558*9072cb8bSMatthew G. Knepley static PetscErrorCode DMSwarmRemap_Colella(DM sw, DM *rsw)
1559*9072cb8bSMatthew G. Knepley {
1560*9072cb8bSMatthew G. Knepley   DM            xdm, vdm;
1561*9072cb8bSMatthew G. Knepley   DMSwarmCellDM celldm;
1562*9072cb8bSMatthew G. Knepley   AppCtx       *user;
1563*9072cb8bSMatthew G. Knepley   PetscReal     xmin[3], xmax[3], vmin[3], vmax[3];
1564*9072cb8bSMatthew G. Knepley   PetscInt      xend[3], vend[3];
1565*9072cb8bSMatthew G. Knepley   PetscReal    *x, *v, *w, *rw;
1566*9072cb8bSMatthew G. Knepley   PetscReal     hx[3], hv[3];
1567*9072cb8bSMatthew G. Knepley   PetscInt      dim, xcdim, vcdim, xcStart, xcEnd, vcStart, vcEnd, Np, Nfc;
1568*9072cb8bSMatthew G. Knepley   PetscInt      debug = ((DM_Swarm *)sw->data)->printWeights;
1569*9072cb8bSMatthew G. Knepley   const char  **coordFields;
1570*9072cb8bSMatthew G. Knepley 
1571*9072cb8bSMatthew G. Knepley   PetscFunctionBegin;
1572*9072cb8bSMatthew G. Knepley   PetscCall(DMGetApplicationContext(sw, (void **)&user));
1573*9072cb8bSMatthew G. Knepley   PetscCall(DMGetDimension(sw, &dim));
1574*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetCellDM(sw, &xdm));
1575*9072cb8bSMatthew G. Knepley   PetscCall(DMGetCoordinateDim(xdm, &xcdim));
1576*9072cb8bSMatthew G. Knepley   // Create a new centroid swarm without weights
1577*9072cb8bSMatthew G. Knepley   PetscCall(CreateSwarm(xdm, user, rsw));
1578*9072cb8bSMatthew G. Knepley   PetscCall(InitializeParticles_Centroid(*rsw, user->fake_1D));
1579*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetLocalSize(*rsw, &Np));
1580*9072cb8bSMatthew G. Knepley   // Assume quad mesh and calculate cell diameters (TODO this could be more robust)
1581*9072cb8bSMatthew G. Knepley   {
1582*9072cb8bSMatthew G. Knepley     const PetscScalar *array;
1583*9072cb8bSMatthew G. Knepley     PetscScalar       *coords;
1584*9072cb8bSMatthew G. Knepley     PetscBool          isDG;
1585*9072cb8bSMatthew G. Knepley     PetscInt           Nc;
1586*9072cb8bSMatthew G. Knepley 
1587*9072cb8bSMatthew G. Knepley     PetscCall(DMGetBoundingBox(xdm, xmin, xmax));
1588*9072cb8bSMatthew G. Knepley     PetscCall(DMPlexGetHeightStratum(xdm, 0, &xcStart, &xcEnd));
1589*9072cb8bSMatthew G. Knepley     PetscCall(DMPlexGetCellCoordinates(xdm, xcStart, &isDG, &Nc, &array, &coords));
1590*9072cb8bSMatthew G. Knepley     hx[0] = coords[1 * xcdim + 0] - coords[0 * xcdim + 0];
1591*9072cb8bSMatthew G. Knepley     hx[1] = xcdim > 1 ? coords[2 * xcdim + 1] - coords[1 * xcdim + 1] : 1.;
1592*9072cb8bSMatthew G. Knepley     PetscCall(DMPlexRestoreCellCoordinates(xdm, xcStart, &isDG, &Nc, &array, &coords));
1593*9072cb8bSMatthew G. Knepley     PetscCall(PetscObjectQuery((PetscObject)sw, "__vdm__", (PetscObject *)&vdm));
1594*9072cb8bSMatthew G. Knepley     PetscCall(DMGetCoordinateDim(vdm, &vcdim));
1595*9072cb8bSMatthew G. Knepley     PetscCall(DMGetBoundingBox(vdm, vmin, vmax));
1596*9072cb8bSMatthew G. Knepley     PetscCall(DMPlexGetHeightStratum(vdm, 0, &vcStart, &vcEnd));
1597*9072cb8bSMatthew G. Knepley     PetscCall(DMPlexGetCellCoordinates(vdm, vcStart, &isDG, &Nc, &array, &coords));
1598*9072cb8bSMatthew G. Knepley     hv[0] = coords[1 * vcdim + 0] - coords[0 * vcdim + 0];
1599*9072cb8bSMatthew G. Knepley     hv[1] = vcdim > 1 ? coords[2 * vcdim + 1] - coords[1 * vcdim + 1] : 1.;
1600*9072cb8bSMatthew G. Knepley     PetscCall(DMPlexRestoreCellCoordinates(vdm, vcStart, &isDG, &Nc, &array, &coords));
1601*9072cb8bSMatthew G. Knepley 
1602*9072cb8bSMatthew G. Knepley     PetscCheck(dim == 1 || user->fake_1D, PetscObjectComm((PetscObject)sw), PETSC_ERR_ARG_WRONG, "Only support 1D distributions at this time");
1603*9072cb8bSMatthew G. Knepley     xend[0] = xcEnd - xcStart;
1604*9072cb8bSMatthew G. Knepley     xend[1] = 1;
1605*9072cb8bSMatthew G. Knepley     vend[0] = vcEnd - vcStart;
1606*9072cb8bSMatthew G. Knepley     vend[1] = 1;
1607*9072cb8bSMatthew G. Knepley     if (debug > 1) PetscCall(PetscPrintf(PETSC_COMM_SELF, "Phase Grid (%g, %g, %g, %g) (%" PetscInt_FMT ", %" PetscInt_FMT ", %" PetscInt_FMT ", %" PetscInt_FMT ")\n", hx[0], hx[1], hv[0], hv[1], xend[0], xend[1], vend[0], vend[1]));
1608*9072cb8bSMatthew G. Knepley   }
1609*9072cb8bSMatthew G. Knepley   // Iterate over particles in the original swarm
1610*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetCellDMActive(sw, &celldm));
1611*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmCellDMGetCoordinateFields(celldm, &Nfc, &coordFields));
1612*9072cb8bSMatthew G. Knepley   PetscCheck(Nfc == 1, PetscObjectComm((PetscObject)sw), PETSC_ERR_SUP, "We only support a single coordinate field right now, not %" PetscInt_FMT, Nfc);
1613*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetField(sw, coordFields[0], NULL, NULL, (void **)&x));
1614*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetField(sw, "velocity", NULL, NULL, (void **)&v));
1615*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&w));
1616*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetField(*rsw, "w_q", NULL, NULL, (void **)&rw));
1617*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmSortGetAccess(sw));
1618*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmSortGetAccess(*rsw));
1619*9072cb8bSMatthew G. Knepley   PetscCall(DMGetBoundingBox(vdm, vmin, vmax));
1620*9072cb8bSMatthew G. Knepley   PetscCall(DMGetCoordinatesLocalSetUp(xdm));
1621*9072cb8bSMatthew G. Knepley   for (PetscInt i = 0; i < Np; ++i) rw[i] = 0.;
1622*9072cb8bSMatthew G. Knepley   for (PetscInt c = 0; c < xcEnd - xcStart; ++c) {
1623*9072cb8bSMatthew G. Knepley     PetscInt *pidx, Npc;
1624*9072cb8bSMatthew G. Knepley     PetscInt *rpidx, rNpc;
1625*9072cb8bSMatthew G. Knepley 
1626*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Npc, &pidx));
1627*9072cb8bSMatthew G. Knepley     for (PetscInt q = 0; q < Npc; ++q) {
1628*9072cb8bSMatthew G. Knepley       const PetscInt  p  = pidx[q];
1629*9072cb8bSMatthew G. Knepley       const PetscReal wp = w[p];
1630*9072cb8bSMatthew G. Knepley       PetscReal       Wx[3], Wv[3];
1631*9072cb8bSMatthew G. Knepley       PetscInt        xs[3], vs[3];
1632*9072cb8bSMatthew G. Knepley 
1633*9072cb8bSMatthew G. Knepley       // Determine the containing cell
1634*9072cb8bSMatthew G. Knepley       for (PetscInt d = 0; d < dim; ++d) {
1635*9072cb8bSMatthew G. Knepley         const PetscReal xp = x[p * dim + d];
1636*9072cb8bSMatthew G. Knepley         const PetscReal vp = v[p * dim + d];
1637*9072cb8bSMatthew G. Knepley 
1638*9072cb8bSMatthew G. Knepley         xs[d] = PetscFloorReal((xp - xmin[d]) / hx[d]);
1639*9072cb8bSMatthew G. Knepley         vs[d] = PetscFloorReal((vp - vmin[d]) / hv[d]);
1640*9072cb8bSMatthew G. Knepley       }
1641*9072cb8bSMatthew G. Knepley       // Loop over all grid points within 2 spacings of the particle
1642*9072cb8bSMatthew G. Knepley       if (debug > 2) {
1643*9072cb8bSMatthew G. Knepley         PetscCall(PetscPrintf(PETSC_COMM_SELF, "Interpolating particle %" PetscInt_FMT " wt %g (%g, %g, %g, %g) (%" PetscInt_FMT ", %" PetscInt_FMT ", %" PetscInt_FMT ", %" PetscInt_FMT ")\n", p, wp, x[p * dim + 0], xcdim > 1 ? x[p * xcdim + 1] : 0., v[p * vcdim + 0], vcdim > 1 ? v[p * vcdim + 1] : 0., xs[0], xs[1], vs[0], vs[1]));
1644*9072cb8bSMatthew G. Knepley       }
1645*9072cb8bSMatthew G. Knepley       for (PetscInt xi = xs[0] - 1; xi < xs[0] + 3; ++xi) {
1646*9072cb8bSMatthew G. Knepley         // Treat xi as periodic
1647*9072cb8bSMatthew G. Knepley         const PetscInt xip = xi < 0 ? xi + xend[0] : (xi >= xend[0] ? xi - xend[0] : xi);
1648*9072cb8bSMatthew G. Knepley         PetscCall(W_3_Interpolation_Private((xmin[0] + (xi + 0.5) * hx[0] - x[p * dim + 0]) / hx[0], &Wx[0]));
1649*9072cb8bSMatthew G. Knepley         for (PetscInt xj = PetscMax(xs[1] - 1, 0); xj < PetscMin(xs[1] + 3, xend[1]); ++xj) {
1650*9072cb8bSMatthew G. Knepley           if (xcdim > 1) PetscCall(W_3_Interpolation_Private((xmin[1] + (xj + 0.5) * hx[1] - x[p * dim + 1]) / hx[1], &Wx[1]));
1651*9072cb8bSMatthew G. Knepley           else Wx[1] = 1.;
1652*9072cb8bSMatthew G. Knepley           for (PetscInt vi = PetscMax(vs[0] - 1, 0); vi < PetscMin(vs[0] + 3, vend[0]); ++vi) {
1653*9072cb8bSMatthew G. Knepley             PetscCall(W_3_Interpolation_Private((vmin[0] + (vi + 0.5) * hv[0] - v[p * dim + 0]) / hv[0], &Wv[0]));
1654*9072cb8bSMatthew G. Knepley             for (PetscInt vj = PetscMax(vs[1] - 1, 0); vj < PetscMin(vs[1] + 3, vend[1]); ++vj) {
1655*9072cb8bSMatthew G. Knepley               const PetscInt rc = xip * xend[1] + xj;
1656*9072cb8bSMatthew G. Knepley               const PetscInt rv = vi * vend[1] + vj;
1657*9072cb8bSMatthew G. Knepley 
1658*9072cb8bSMatthew G. Knepley               PetscCall(DMSwarmSortGetPointsPerCell(*rsw, rc, &rNpc, &rpidx));
1659*9072cb8bSMatthew G. Knepley               if (vcdim > 1) PetscCall(W_3_Interpolation_Private((vmin[1] + (vj + 0.5) * hv[1] - v[p * dim + 1]) / hv[1], &Wv[1]));
1660*9072cb8bSMatthew G. Knepley               else Wv[1] = 1.;
1661*9072cb8bSMatthew G. Knepley               if (debug > 2)
1662*9072cb8bSMatthew G. Knepley                 PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Depositing on particle (%" PetscInt_FMT ", %" PetscInt_FMT ", %" PetscInt_FMT ", %" PetscInt_FMT ") w = %g (%g, %g, %g, %g)\n", xi, xj, vi, vj, wp * Wx[0] * Wx[1] * Wv[0] * Wv[1], Wx[0], Wx[1], Wv[0], Wv[1]));
1663*9072cb8bSMatthew G. Knepley               // Add weight to new particles from original particle using interpolation function
1664*9072cb8bSMatthew G. Knepley               PetscCheck(rNpc == vend[0] * vend[1], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid particle velocity binning");
1665*9072cb8bSMatthew G. Knepley               const PetscInt rp = rpidx[rv];
1666*9072cb8bSMatthew G. Knepley               PetscCheck(rp >= 0 && rp < Np, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Particle index %" PetscInt_FMT " not in [0, %" PetscInt_FMT ")", rp, Np);
1667*9072cb8bSMatthew G. Knepley               rw[rp] += wp * Wx[0] * Wx[1] * Wv[0] * Wv[1];
1668*9072cb8bSMatthew G. Knepley               if (debug > 2) PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Adding weight %g (%g) to particle %" PetscInt_FMT "\n", wp * Wx[0] * Wx[1] * Wv[0] * Wv[1], rw[rp], rp));
1669*9072cb8bSMatthew G. Knepley               PetscCall(DMSwarmSortRestorePointsPerCell(*rsw, rc, &rNpc, &rpidx));
1670*9072cb8bSMatthew G. Knepley             }
1671*9072cb8bSMatthew G. Knepley           }
1672*9072cb8bSMatthew G. Knepley         }
1673*9072cb8bSMatthew G. Knepley       }
1674*9072cb8bSMatthew G. Knepley     }
1675*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmSortRestorePointsPerCell(sw, c, &Npc, &pidx));
1676*9072cb8bSMatthew G. Knepley   }
1677*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmSortRestoreAccess(sw));
1678*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmSortRestoreAccess(*rsw));
1679*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmRestoreField(sw, coordFields[0], NULL, NULL, (void **)&x));
1680*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmRestoreField(sw, "velocity", NULL, NULL, (void **)&v));
1681*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&w));
1682*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmRestoreField(*rsw, "w_q", NULL, NULL, (void **)&rw));
1683*9072cb8bSMatthew G. Knepley 
1684*9072cb8bSMatthew G. Knepley   if (debug) {
1685*9072cb8bSMatthew G. Knepley     Vec w;
1686*9072cb8bSMatthew G. Knepley 
1687*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmCreateGlobalVectorFromField(sw, coordFields[0], &w));
1688*9072cb8bSMatthew G. Knepley     PetscCall(VecViewFromOptions(w, NULL, "-remap_view"));
1689*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, coordFields[0], &w));
1690*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "velocity", &w));
1691*9072cb8bSMatthew G. Knepley     PetscCall(VecViewFromOptions(w, NULL, "-remap_view"));
1692*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "velocity", &w));
1693*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "w_q", &w));
1694*9072cb8bSMatthew G. Knepley     PetscCall(VecViewFromOptions(w, NULL, "-remap_view"));
1695*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "w_q", &w));
1696*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmCreateGlobalVectorFromField(*rsw, coordFields[0], &w));
1697*9072cb8bSMatthew G. Knepley     PetscCall(VecViewFromOptions(w, NULL, "-remap_view"));
1698*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmDestroyGlobalVectorFromField(*rsw, coordFields[0], &w));
1699*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmCreateGlobalVectorFromField(*rsw, "velocity", &w));
1700*9072cb8bSMatthew G. Knepley     PetscCall(VecViewFromOptions(w, NULL, "-remap_view"));
1701*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmDestroyGlobalVectorFromField(*rsw, "velocity", &w));
1702*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmCreateGlobalVectorFromField(*rsw, "w_q", &w));
1703*9072cb8bSMatthew G. Knepley     PetscCall(VecViewFromOptions(w, NULL, "-remap_view"));
1704*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmDestroyGlobalVectorFromField(*rsw, "w_q", &w));
1705*9072cb8bSMatthew G. Knepley   }
1706*9072cb8bSMatthew G. Knepley   PetscFunctionReturn(PETSC_SUCCESS);
1707*9072cb8bSMatthew G. Knepley }
1708*9072cb8bSMatthew G. Knepley 
1709*9072cb8bSMatthew G. Knepley static void f0_v2(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[])
1710*9072cb8bSMatthew G. Knepley {
1711*9072cb8bSMatthew G. Knepley   PetscInt d;
1712*9072cb8bSMatthew G. Knepley 
1713*9072cb8bSMatthew G. Knepley   f0[0] = 0.0;
1714*9072cb8bSMatthew G. Knepley   for (d = dim / 2; d < dim; ++d) f0[0] += PetscSqr(x[d]) * u[0];
1715*9072cb8bSMatthew G. Knepley }
1716*9072cb8bSMatthew G. Knepley 
1717*9072cb8bSMatthew G. Knepley static PetscErrorCode DMSwarmRemap_PFAK(DM sw, DM *rsw)
1718*9072cb8bSMatthew G. Knepley {
1719*9072cb8bSMatthew G. Knepley   DM            xdm, vdm, rdm;
1720*9072cb8bSMatthew G. Knepley   DMSwarmCellDM rcelldm;
1721*9072cb8bSMatthew G. Knepley   Mat           M_p, rM_p, rPM_p;
1722*9072cb8bSMatthew G. Knepley   Vec           w, rw, rhs;
1723*9072cb8bSMatthew G. Knepley   PetscInt      Nf;
1724*9072cb8bSMatthew G. Knepley   const char  **fields;
1725*9072cb8bSMatthew G. Knepley   AppCtx       *user;
1726*9072cb8bSMatthew G. Knepley 
1727*9072cb8bSMatthew G. Knepley   PetscFunctionBegin;
1728*9072cb8bSMatthew G. Knepley   // Create a new centroid swarm without weights
1729*9072cb8bSMatthew G. Knepley   PetscCall(DMGetApplicationContext(sw, (void **)&user));
1730*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetCellDM(sw, &xdm));
1731*9072cb8bSMatthew G. Knepley   PetscCall(PetscObjectQuery((PetscObject)sw, "__vdm__", (PetscObject *)&vdm));
1732*9072cb8bSMatthew G. Knepley   PetscCall(CreateSwarm(xdm, user, rsw));
1733*9072cb8bSMatthew G. Knepley   // Set remap cell DM
1734*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmSetCellDMActive(sw, "remap"));
1735*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetCellDMActive(sw, &rcelldm));
1736*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmCellDMGetFields(rcelldm, &Nf, &fields));
1737*9072cb8bSMatthew G. Knepley   PetscCheck(Nf == 1, PetscObjectComm((PetscObject)sw), PETSC_ERR_ARG_WRONG, "We only allow a single weight field, not %" PetscInt_FMT, Nf);
1738*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmGetCellDM(sw, &rdm));
1739*9072cb8bSMatthew G. Knepley   PetscCall(DMGetGlobalVector(rdm, &rhs));
1740*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmMigrate(sw, PETSC_FALSE)); // Bin particles in remap mesh
1741*9072cb8bSMatthew G. Knepley   // Compute rhs = M_p w_p
1742*9072cb8bSMatthew G. Knepley   PetscCall(DMCreateMassMatrix(sw, rdm, &M_p));
1743*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, fields[0], &w));
1744*9072cb8bSMatthew G. Knepley   PetscCall(VecViewFromOptions(w, NULL, "-remap_w_view"));
1745*9072cb8bSMatthew G. Knepley   PetscCall(MatMultTranspose(M_p, w, rhs));
1746*9072cb8bSMatthew G. Knepley   PetscCall(VecViewFromOptions(rhs, NULL, "-remap_rhs_view"));
1747*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, fields[0], &w));
1748*9072cb8bSMatthew G. Knepley   PetscCall(MatDestroy(&M_p));
1749*9072cb8bSMatthew G. Knepley   {
1750*9072cb8bSMatthew G. Knepley     KSP         ksp;
1751*9072cb8bSMatthew G. Knepley     Mat         M_f;
1752*9072cb8bSMatthew G. Knepley     Vec         u_f;
1753*9072cb8bSMatthew G. Knepley     PetscReal   mom[4];
1754*9072cb8bSMatthew G. Knepley     PetscInt    cdim;
1755*9072cb8bSMatthew G. Knepley     const char *prefix;
1756*9072cb8bSMatthew G. Knepley 
1757*9072cb8bSMatthew G. Knepley     PetscCall(DMGetCoordinateDim(rdm, &cdim));
1758*9072cb8bSMatthew G. Knepley     PetscCall(DMCreateMassMatrix(rdm, rdm, &M_f));
1759*9072cb8bSMatthew G. Knepley     PetscCall(DMGetGlobalVector(rdm, &u_f));
1760*9072cb8bSMatthew G. Knepley 
1761*9072cb8bSMatthew G. Knepley     PetscCall(KSPCreate(PetscObjectComm((PetscObject)sw), &ksp));
1762*9072cb8bSMatthew G. Knepley     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)sw, &prefix));
1763*9072cb8bSMatthew G. Knepley     PetscCall(KSPSetOptionsPrefix(ksp, prefix));
1764*9072cb8bSMatthew G. Knepley     PetscCall(KSPAppendOptionsPrefix(ksp, "ptof_"));
1765*9072cb8bSMatthew G. Knepley     PetscCall(KSPSetFromOptions(ksp));
1766*9072cb8bSMatthew G. Knepley 
1767*9072cb8bSMatthew G. Knepley     PetscCall(KSPSetOperators(ksp, M_f, M_f));
1768*9072cb8bSMatthew G. Knepley     PetscCall(KSPSolve(ksp, rhs, u_f));
1769*9072cb8bSMatthew G. Knepley     PetscCall(KSPDestroy(&ksp));
1770*9072cb8bSMatthew G. Knepley     PetscCall(VecViewFromOptions(u_f, NULL, "-remap_uf_view"));
1771*9072cb8bSMatthew G. Knepley 
1772*9072cb8bSMatthew G. Knepley     PetscCall(DMPlexComputeMoments(rdm, u_f, mom));
1773*9072cb8bSMatthew G. Knepley     // Energy is not correct since it uses (x^2 + v^2)
1774*9072cb8bSMatthew G. Knepley     PetscDS     rds;
1775*9072cb8bSMatthew G. Knepley     PetscScalar rmom;
1776*9072cb8bSMatthew G. Knepley     void       *ctx;
1777*9072cb8bSMatthew G. Knepley 
1778*9072cb8bSMatthew G. Knepley     PetscCall(DMGetDS(rdm, &rds));
1779*9072cb8bSMatthew G. Knepley     PetscCall(DMGetApplicationContext(rdm, &ctx));
1780*9072cb8bSMatthew G. Knepley     PetscCall(PetscDSSetObjective(rds, 0, &f0_v2));
1781*9072cb8bSMatthew G. Knepley     PetscCall(DMPlexComputeIntegralFEM(rdm, u_f, &rmom, ctx));
1782*9072cb8bSMatthew G. Knepley     mom[1 + cdim] = PetscRealPart(rmom);
1783*9072cb8bSMatthew G. Knepley 
1784*9072cb8bSMatthew G. Knepley     PetscCall(DMRestoreGlobalVector(rdm, &u_f));
1785*9072cb8bSMatthew G. Knepley     PetscCall(PetscPrintf(PETSC_COMM_SELF, "========== PFAK u_f ==========\n"));
1786*9072cb8bSMatthew G. Knepley     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Mom 0: %g\n", mom[0]));
1787*9072cb8bSMatthew G. Knepley     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Mom x: %g\n", mom[1 + 0]));
1788*9072cb8bSMatthew G. Knepley     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Mom v: %g\n", mom[1 + 1]));
1789*9072cb8bSMatthew G. Knepley     PetscCall(PetscPrintf(PETSC_COMM_SELF, "Mom 2: %g\n", mom[1 + cdim]));
1790*9072cb8bSMatthew G. Knepley     PetscCall(MatDestroy(&M_f));
1791*9072cb8bSMatthew G. Knepley   }
1792*9072cb8bSMatthew G. Knepley   // Create Remap particle mass matrix M_p
1793*9072cb8bSMatthew G. Knepley   PetscInt xcStart, xcEnd, vcStart, vcEnd, cStart, cEnd, r;
1794*9072cb8bSMatthew G. Knepley 
1795*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmSetCellDMActive(*rsw, "remap"));
1796*9072cb8bSMatthew G. Knepley   PetscCall(DMPlexGetHeightStratum(xdm, 0, &xcStart, &xcEnd));
1797*9072cb8bSMatthew G. Knepley   PetscCall(DMPlexGetHeightStratum(vdm, 0, &vcStart, &vcEnd));
1798*9072cb8bSMatthew G. Knepley   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
1799*9072cb8bSMatthew G. Knepley   r = (PetscInt)PetscSqrtReal(((xcEnd - xcStart) * (vcEnd - vcStart)) / (cEnd - cStart));
1800*9072cb8bSMatthew G. Knepley   PetscCall(InitializeParticles_Regular(*rsw, r));
1801*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmMigrate(*rsw, PETSC_FALSE)); // Bin particles in remap mesh
1802*9072cb8bSMatthew G. Knepley   PetscCall(DMCreateMassMatrix(*rsw, rdm, &rM_p));
1803*9072cb8bSMatthew G. Knepley   PetscCall(MatViewFromOptions(rM_p, NULL, "-rM_p_view"));
1804*9072cb8bSMatthew G. Knepley   // Solve M_p
1805*9072cb8bSMatthew G. Knepley   {
1806*9072cb8bSMatthew G. Knepley     KSP         ksp;
1807*9072cb8bSMatthew G. Knepley     PC          pc;
1808*9072cb8bSMatthew G. Knepley     const char *prefix;
1809*9072cb8bSMatthew G. Knepley     PetscBool   isBjacobi;
1810*9072cb8bSMatthew G. Knepley 
1811*9072cb8bSMatthew G. Knepley     PetscCall(KSPCreate(PetscObjectComm((PetscObject)sw), &ksp));
1812*9072cb8bSMatthew G. Knepley     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)sw, &prefix));
1813*9072cb8bSMatthew G. Knepley     PetscCall(KSPSetOptionsPrefix(ksp, prefix));
1814*9072cb8bSMatthew G. Knepley     PetscCall(KSPAppendOptionsPrefix(ksp, "ftop_"));
1815*9072cb8bSMatthew G. Knepley     PetscCall(KSPSetFromOptions(ksp));
1816*9072cb8bSMatthew G. Knepley 
1817*9072cb8bSMatthew G. Knepley     PetscCall(KSPGetPC(ksp, &pc));
1818*9072cb8bSMatthew G. Knepley     PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCBJACOBI, &isBjacobi));
1819*9072cb8bSMatthew G. Knepley     if (isBjacobi) {
1820*9072cb8bSMatthew G. Knepley       PetscCall(DMSwarmCreateMassMatrixSquare(sw, rdm, &rPM_p));
1821*9072cb8bSMatthew G. Knepley     } else {
1822*9072cb8bSMatthew G. Knepley       rPM_p = rM_p;
1823*9072cb8bSMatthew G. Knepley       PetscCall(PetscObjectReference((PetscObject)rPM_p));
1824*9072cb8bSMatthew G. Knepley     }
1825*9072cb8bSMatthew G. Knepley     PetscCall(KSPSetOperators(ksp, rM_p, rPM_p));
1826*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmCreateGlobalVectorFromField(*rsw, fields[0], &rw));
1827*9072cb8bSMatthew G. Knepley     PetscCall(KSPSolveTranspose(ksp, rhs, rw));
1828*9072cb8bSMatthew G. Knepley     PetscCall(VecViewFromOptions(rw, NULL, "-remap_rw_view"));
1829*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmDestroyGlobalVectorFromField(*rsw, fields[0], &rw));
1830*9072cb8bSMatthew G. Knepley     PetscCall(KSPDestroy(&ksp));
1831*9072cb8bSMatthew G. Knepley     PetscCall(MatDestroy(&rPM_p));
1832*9072cb8bSMatthew G. Knepley     PetscCall(MatDestroy(&rM_p));
1833*9072cb8bSMatthew G. Knepley   }
1834*9072cb8bSMatthew G. Knepley   PetscCall(DMRestoreGlobalVector(rdm, &rhs));
1835*9072cb8bSMatthew G. Knepley 
1836*9072cb8bSMatthew G. Knepley   // Restore original cell DM
1837*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmSetCellDMActive(sw, "space"));
1838*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmSetCellDMActive(*rsw, "space"));
1839*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmMigrate(*rsw, PETSC_FALSE)); // Bin particles in spatial mesh
1840*9072cb8bSMatthew G. Knepley   PetscFunctionReturn(PETSC_SUCCESS);
1841*9072cb8bSMatthew G. Knepley }
1842*9072cb8bSMatthew G. Knepley 
1843*9072cb8bSMatthew G. Knepley static PetscErrorCode DMSwarmRemap(DM sw)
1844*9072cb8bSMatthew G. Knepley {
1845*9072cb8bSMatthew G. Knepley   DM      rsw;
1846*9072cb8bSMatthew G. Knepley   AppCtx *user;
1847*9072cb8bSMatthew G. Knepley 
1848*9072cb8bSMatthew G. Knepley   PetscFunctionBegin;
1849*9072cb8bSMatthew G. Knepley   PetscCall(DMGetApplicationContext(sw, (void **)&user));
1850*9072cb8bSMatthew G. Knepley   switch (user->remapType) {
1851*9072cb8bSMatthew G. Knepley   case RM_COLELLA:
1852*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmRemap_Colella(sw, &rsw));
1853*9072cb8bSMatthew G. Knepley     break;
1854*9072cb8bSMatthew G. Knepley   case RM_PFAK:
1855*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmRemap_PFAK(sw, &rsw));
1856*9072cb8bSMatthew G. Knepley     break;
1857*9072cb8bSMatthew G. Knepley   default:
1858*9072cb8bSMatthew G. Knepley     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "No remap algorithmic %s", RemapTypes[user->remapType]);
1859*9072cb8bSMatthew G. Knepley   }
1860*9072cb8bSMatthew G. Knepley 
1861*9072cb8bSMatthew G. Knepley   PetscReal mom[4], rmom[4];
1862*9072cb8bSMatthew G. Knepley   PetscInt  cdim;
1863*9072cb8bSMatthew G. Knepley 
1864*9072cb8bSMatthew G. Knepley   PetscCall(DMGetCoordinateDim(sw, &cdim));
1865*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmComputeMoments(sw, "velocity", "w_q", mom));
1866*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmComputeMoments(rsw, "velocity", "w_q", rmom));
1867*9072cb8bSMatthew G. Knepley   PetscCall(PetscPrintf(PETSC_COMM_SELF, "========== Remapped ==========\n"));
1868*9072cb8bSMatthew G. Knepley   PetscCall(PetscPrintf(PETSC_COMM_SELF, "Mom 0: %g --> %g\n", mom[0], rmom[0]));
1869*9072cb8bSMatthew G. Knepley   PetscCall(PetscPrintf(PETSC_COMM_SELF, "Mom 1: %g --> %g\n", mom[1], rmom[1]));
1870*9072cb8bSMatthew G. Knepley   PetscCall(PetscPrintf(PETSC_COMM_SELF, "Mom 2: %g --> %g\n", mom[1 + cdim], rmom[1 + cdim]));
1871*9072cb8bSMatthew G. Knepley   if (user->weight_monitor) {
1872*9072cb8bSMatthew G. Knepley     PetscCall(MonitorSwarmWeights(sw, "w_q"));
1873*9072cb8bSMatthew G. Knepley     PetscCall(MonitorSwarmWeights(rsw, "w_q"));
1874*9072cb8bSMatthew G. Knepley   }
1875*9072cb8bSMatthew G. Knepley   PetscCall(DMSwarmReplace(sw, &rsw));
18763ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1877b80bf5b1SJoe Pusztay }
1878b80bf5b1SJoe Pusztay 
18798214e71cSJoe static PetscErrorCode ComputeFieldAtParticles_Coulomb(SNES snes, DM sw, PetscReal E[])
1880d71ae5a4SJacob Faibussowitsch {
18818214e71cSJoe   AppCtx     *user;
18828214e71cSJoe   PetscReal  *coords;
18838214e71cSJoe   PetscInt   *species, dim, Np, Ns;
18848214e71cSJoe   PetscMPIInt size;
18858214e71cSJoe 
18868214e71cSJoe   PetscFunctionBegin;
18878214e71cSJoe   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)snes), &size));
18888214e71cSJoe   PetscCheck(size == 1, PetscObjectComm((PetscObject)snes), PETSC_ERR_SUP, "Coulomb code only works in serial");
18898214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
18908214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
18918214e71cSJoe   PetscCall(DMSwarmGetNumSpecies(sw, &Ns));
18928214e71cSJoe   PetscCall(DMGetApplicationContext(sw, (void *)&user));
18938214e71cSJoe 
18948214e71cSJoe   PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords));
18958214e71cSJoe   PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **)&species));
18968214e71cSJoe   for (PetscInt p = 0; p < Np; ++p) {
18978214e71cSJoe     PetscReal *pcoord = &coords[p * dim];
18988214e71cSJoe     PetscReal  pE[3]  = {0., 0., 0.};
18998214e71cSJoe 
19008214e71cSJoe     /* Calculate field at particle p due to particle q */
19018214e71cSJoe     for (PetscInt q = 0; q < Np; ++q) {
19028214e71cSJoe       PetscReal *qcoord = &coords[q * dim];
19038214e71cSJoe       PetscReal  rpq[3], r, r3, q_q;
19048214e71cSJoe 
19058214e71cSJoe       if (p == q) continue;
19068214e71cSJoe       q_q = user->charges[species[q]] * 1.;
19078214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) rpq[d] = pcoord[d] - qcoord[d];
19088214e71cSJoe       r = DMPlex_NormD_Internal(dim, rpq);
19098214e71cSJoe       if (r < PETSC_SQRT_MACHINE_EPSILON) continue;
19108214e71cSJoe       r3 = PetscPowRealInt(r, 3);
19118214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) pE[d] += q_q * rpq[d] / r3;
19128214e71cSJoe     }
19138214e71cSJoe     for (PetscInt d = 0; d < dim; ++d) E[p * dim + d] = pE[d];
19148214e71cSJoe   }
19158214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **)&species));
19168214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords));
19178214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
19188214e71cSJoe }
19198214e71cSJoe 
19208214e71cSJoe static PetscErrorCode ComputeFieldAtParticles_Primal(SNES snes, DM sw, PetscReal E[])
19218214e71cSJoe {
1922b80bf5b1SJoe Pusztay   DM         dm;
19238214e71cSJoe   AppCtx    *user;
19248214e71cSJoe   PetscDS    ds;
19258214e71cSJoe   PetscFE    fe;
1926f1e6e573SMatthew G. Knepley   Mat        M_p;
192775155f48SMatthew G. Knepley   Vec        rhoRhs;      // Weak charge density, \int phi_i rho
192875155f48SMatthew G. Knepley   Vec        rho;         // Charge density, M^{-1} rhoRhs
192975155f48SMatthew G. Knepley   Vec        phi, locPhi; // Potential
193075155f48SMatthew G. Knepley   Vec        f;           // Particle weights
19318214e71cSJoe   PetscReal *coords;
19328214e71cSJoe   PetscInt   dim, cStart, cEnd, Np;
19338214e71cSJoe 
19348214e71cSJoe   PetscFunctionBegin;
193584467f86SMatthew G. Knepley   PetscCall(DMGetApplicationContext(sw, (void *)&user));
193684467f86SMatthew G. Knepley   PetscCall(PetscLogEventBegin(user->ESolveEvent, snes, sw, 0, 0));
19378214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
19388214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
19398214e71cSJoe 
19408214e71cSJoe   KSP          ksp;
194184467f86SMatthew G. Knepley   const char **oldFields;
194284467f86SMatthew G. Knepley   PetscInt     Nf;
194384467f86SMatthew G. Knepley   const char **tmp;
19448214e71cSJoe 
19458214e71cSJoe   PetscCall(SNESGetDM(snes, &dm));
194684467f86SMatthew G. Knepley   PetscCall(DMSwarmVectorGetField(sw, &Nf, &tmp));
194784467f86SMatthew G. Knepley   PetscCall(PetscMalloc1(Nf, &oldFields));
194884467f86SMatthew G. Knepley   for (PetscInt f = 0; f < Nf; ++f) PetscCall(PetscStrallocpy(tmp[f], (char **)&oldFields[f]));
1949f1e6e573SMatthew G. Knepley   PetscCall(DMSwarmVectorDefineField(sw, "w_q"));
19508214e71cSJoe   PetscCall(DMCreateMassMatrix(sw, dm, &M_p));
1951f1e6e573SMatthew G. Knepley   PetscCall(DMSwarmVectorDefineFields(sw, Nf, oldFields));
195284467f86SMatthew G. Knepley   for (PetscInt f = 0; f < Nf; ++f) PetscCall(PetscFree(oldFields[f]));
195384467f86SMatthew G. Knepley   PetscCall(PetscFree(oldFields));
19548214e71cSJoe 
195575155f48SMatthew G. Knepley   PetscCall(DMGetGlobalVector(dm, &rhoRhs));
195675155f48SMatthew G. Knepley   PetscCall(PetscObjectSetName((PetscObject)rhoRhs, "Weak charge density"));
195775155f48SMatthew G. Knepley   PetscCall(DMGetNamedGlobalVector(dm, "rho", &rho));
19588214e71cSJoe   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "w_q", &f));
19598214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)f, "particle weight"));
196075155f48SMatthew G. Knepley 
19618214e71cSJoe   PetscCall(MatViewFromOptions(M_p, NULL, "-mp_view"));
1962f1e6e573SMatthew G. Knepley   PetscCall(MatViewFromOptions(user->M, NULL, "-m_view"));
19638214e71cSJoe   PetscCall(VecViewFromOptions(f, NULL, "-weights_view"));
196475155f48SMatthew G. Knepley 
196575155f48SMatthew G. Knepley   PetscCall(MatMultTranspose(M_p, f, rhoRhs));
19668214e71cSJoe   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "w_q", &f));
19678214e71cSJoe 
19688214e71cSJoe   PetscCall(KSPCreate(PetscObjectComm((PetscObject)dm), &ksp));
19698214e71cSJoe   PetscCall(KSPSetOptionsPrefix(ksp, "em_proj_"));
1970f1e6e573SMatthew G. Knepley   PetscCall(KSPSetOperators(ksp, user->M, user->M));
19718214e71cSJoe   PetscCall(KSPSetFromOptions(ksp));
197275155f48SMatthew G. Knepley   PetscCall(KSPSolve(ksp, rhoRhs, rho));
19738214e71cSJoe 
197475155f48SMatthew G. Knepley   PetscCall(VecScale(rhoRhs, -1.0));
19758214e71cSJoe 
197675155f48SMatthew G. Knepley   PetscCall(VecViewFromOptions(rhoRhs, NULL, "-rho_view"));
197775155f48SMatthew G. Knepley   PetscCall(DMRestoreNamedGlobalVector(dm, "rho", &rho));
19788214e71cSJoe   PetscCall(KSPDestroy(&ksp));
19798214e71cSJoe   PetscCall(MatDestroy(&M_p));
19808214e71cSJoe 
198175155f48SMatthew G. Knepley   PetscCall(DMGetNamedGlobalVector(dm, "phi", &phi));
19828214e71cSJoe   PetscCall(VecSet(phi, 0.0));
198375155f48SMatthew G. Knepley   PetscCall(SNESSolve(snes, rhoRhs, phi));
198475155f48SMatthew G. Knepley   PetscCall(DMRestoreGlobalVector(dm, &rhoRhs));
19858214e71cSJoe   PetscCall(VecViewFromOptions(phi, NULL, "-phi_view"));
19868214e71cSJoe 
19878214e71cSJoe   PetscCall(DMGetLocalVector(dm, &locPhi));
19888214e71cSJoe   PetscCall(DMGlobalToLocalBegin(dm, phi, INSERT_VALUES, locPhi));
19898214e71cSJoe   PetscCall(DMGlobalToLocalEnd(dm, phi, INSERT_VALUES, locPhi));
199075155f48SMatthew G. Knepley   PetscCall(DMRestoreNamedGlobalVector(dm, "phi", &phi));
199184467f86SMatthew G. Knepley   PetscCall(PetscLogEventEnd(user->ESolveEvent, snes, sw, 0, 0));
19928214e71cSJoe 
19938214e71cSJoe   PetscCall(DMGetDS(dm, &ds));
19948214e71cSJoe   PetscCall(PetscDSGetDiscretization(ds, 0, (PetscObject *)&fe));
19958214e71cSJoe   PetscCall(DMSwarmSortGetAccess(sw));
19968214e71cSJoe   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
19978214e71cSJoe   PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords));
19988214e71cSJoe 
199984467f86SMatthew G. Knepley   PetscCall(PetscLogEventBegin(user->ETabEvent, snes, sw, 0, 0));
2000f1e6e573SMatthew G. Knepley   PetscFEGeom *chunkgeom = NULL;
20018214e71cSJoe   for (PetscInt c = cStart; c < cEnd; ++c) {
20028214e71cSJoe     PetscTabulation tab;
20038214e71cSJoe     PetscScalar    *clPhi = NULL;
20048214e71cSJoe     PetscReal      *pcoord, *refcoord;
20058214e71cSJoe     PetscInt       *points;
20068214e71cSJoe     PetscInt        Ncp;
20078214e71cSJoe 
20088214e71cSJoe     PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Ncp, &points));
20098214e71cSJoe     PetscCall(DMGetWorkArray(dm, Ncp * dim, MPIU_REAL, &pcoord));
20108214e71cSJoe     PetscCall(DMGetWorkArray(dm, Ncp * dim, MPIU_REAL, &refcoord));
20118214e71cSJoe     for (PetscInt cp = 0; cp < Ncp; ++cp)
20128214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) pcoord[cp * dim + d] = coords[points[cp] * dim + d];
2013f1e6e573SMatthew G. Knepley     {
2014f1e6e573SMatthew G. Knepley       PetscCall(PetscFEGeomGetChunk(user->fegeom, c - cStart, c - cStart + 1, &chunkgeom));
2015f1e6e573SMatthew G. Knepley       for (PetscInt i = 0; i < Ncp; ++i) {
2016f1e6e573SMatthew G. Knepley         const PetscReal x0[3] = {-1., -1., -1.};
2017f1e6e573SMatthew G. Knepley         CoordinatesRealToRef(dim, dim, x0, chunkgeom->v, chunkgeom->invJ, &pcoord[dim * i], &refcoord[dim * i]);
2018f1e6e573SMatthew G. Knepley       }
2019f1e6e573SMatthew G. Knepley     }
20208214e71cSJoe     PetscCall(PetscFECreateTabulation(fe, 1, Ncp, refcoord, 1, &tab));
20218214e71cSJoe     PetscCall(DMPlexVecGetClosure(dm, NULL, locPhi, c, NULL, &clPhi));
20228214e71cSJoe     for (PetscInt cp = 0; cp < Ncp; ++cp) {
20238214e71cSJoe       const PetscReal *basisDer = tab->T[1];
20248214e71cSJoe       const PetscInt   p        = points[cp];
20258214e71cSJoe 
20268214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) E[p * dim + d] = 0.;
2027f1e6e573SMatthew G. Knepley       PetscCall(PetscFEFreeInterpolateGradient_Static(fe, basisDer, clPhi, dim, chunkgeom->invJ, NULL, cp, &E[p * dim]));
20288214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) {
20298214e71cSJoe         E[p * dim + d] *= -1.0;
20308214e71cSJoe         if (user->fake_1D && d > 0) E[p * dim + d] = 0;
20318214e71cSJoe       }
20328214e71cSJoe     }
20338214e71cSJoe     PetscCall(DMPlexVecRestoreClosure(dm, NULL, locPhi, c, NULL, &clPhi));
2034f1e6e573SMatthew G. Knepley     PetscCall(PetscTabulationDestroy(&tab));
20358214e71cSJoe     PetscCall(DMRestoreWorkArray(dm, Ncp * dim, MPIU_REAL, &pcoord));
20368214e71cSJoe     PetscCall(DMRestoreWorkArray(dm, Ncp * dim, MPIU_REAL, &refcoord));
203784467f86SMatthew G. Knepley     PetscCall(DMSwarmSortRestorePointsPerCell(sw, c, &Ncp, &points));
20388214e71cSJoe   }
20398214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords));
20408214e71cSJoe   PetscCall(DMSwarmSortRestoreAccess(sw));
20418214e71cSJoe   PetscCall(DMRestoreLocalVector(dm, &locPhi));
2042f1e6e573SMatthew G. Knepley   PetscCall(PetscFEGeomRestoreChunk(user->fegeom, 0, 1, &chunkgeom));
204384467f86SMatthew G. Knepley   PetscCall(PetscLogEventEnd(user->ETabEvent, snes, sw, 0, 0));
20448214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
20458214e71cSJoe }
20468214e71cSJoe 
20478214e71cSJoe static PetscErrorCode ComputeFieldAtParticles_Mixed(SNES snes, DM sw, PetscReal E[])
20488214e71cSJoe {
20498214e71cSJoe   AppCtx         *user;
20508214e71cSJoe   DM              dm, potential_dm;
20518214e71cSJoe   KSP             ksp;
20528214e71cSJoe   IS              potential_IS;
20538214e71cSJoe   PetscDS         ds;
20548214e71cSJoe   PetscFE         fe;
20558214e71cSJoe   Mat             M_p, M;
20568214e71cSJoe   Vec             phi, locPhi, rho, f, temp_rho, rho0;
20578214e71cSJoe   PetscQuadrature q;
205875155f48SMatthew G. Knepley   PetscReal      *coords;
205984467f86SMatthew G. Knepley   PetscInt        dim, cStart, cEnd, Np, pot_field = 1;
206084467f86SMatthew G. Knepley   const char    **oldFields;
206184467f86SMatthew G. Knepley   PetscInt        Nf;
206284467f86SMatthew G. Knepley   const char    **tmp;
20638214e71cSJoe 
20648214e71cSJoe   PetscFunctionBegin;
206584467f86SMatthew G. Knepley   PetscCall(DMGetApplicationContext(sw, &user));
206684467f86SMatthew G. Knepley   PetscCall(PetscLogEventBegin(user->ESolveEvent, snes, sw, 0, 0));
20678214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
20688214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
20698214e71cSJoe 
20708214e71cSJoe   /* Create the charges rho */
20718214e71cSJoe   PetscCall(SNESGetDM(snes, &dm));
20728214e71cSJoe   PetscCall(DMGetGlobalVector(dm, &rho));
20738214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)rho, "rho"));
20748214e71cSJoe 
207584467f86SMatthew G. Knepley   PetscCall(DMCreateSubDM(dm, 1, &pot_field, &potential_IS, &potential_dm));
20768214e71cSJoe 
207784467f86SMatthew G. Knepley   PetscCall(DMSwarmVectorGetField(sw, &Nf, &tmp));
207884467f86SMatthew G. Knepley   PetscCall(PetscMalloc1(Nf, &oldFields));
207984467f86SMatthew G. Knepley   for (PetscInt f = 0; f < Nf; ++f) PetscCall(PetscStrallocpy(tmp[f], (char **)&oldFields[f]));
2080f1e6e573SMatthew G. Knepley   PetscCall(DMSwarmVectorDefineField(sw, "w_q"));
20818214e71cSJoe   PetscCall(DMCreateMassMatrix(sw, potential_dm, &M_p));
2082f1e6e573SMatthew G. Knepley   PetscCall(DMSwarmVectorDefineFields(sw, Nf, oldFields));
208384467f86SMatthew G. Knepley   for (PetscInt f = 0; f < Nf; ++f) PetscCall(PetscFree(oldFields[f]));
208484467f86SMatthew G. Knepley   PetscCall(PetscFree(oldFields));
20858214e71cSJoe 
20868214e71cSJoe   PetscCall(DMCreateMassMatrix(potential_dm, potential_dm, &M));
20878214e71cSJoe   PetscCall(MatViewFromOptions(M_p, NULL, "-mp_view"));
20888214e71cSJoe   PetscCall(MatViewFromOptions(M, NULL, "-m_view"));
20898214e71cSJoe   PetscCall(DMGetGlobalVector(potential_dm, &temp_rho));
20908214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)temp_rho, "Mf"));
20918214e71cSJoe   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "w_q", &f));
20928214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)f, "particle weight"));
20938214e71cSJoe   PetscCall(VecViewFromOptions(f, NULL, "-weights_view"));
20948214e71cSJoe   PetscCall(MatMultTranspose(M_p, f, temp_rho));
20958214e71cSJoe   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "w_q", &f));
20968214e71cSJoe   PetscCall(DMGetGlobalVector(potential_dm, &rho0));
20978214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)rho0, "Charge density (rho0) from Mixed Compute"));
20988214e71cSJoe 
20998214e71cSJoe   PetscCall(KSPCreate(PetscObjectComm((PetscObject)dm), &ksp));
21008214e71cSJoe   PetscCall(KSPSetOptionsPrefix(ksp, "em_proj"));
21018214e71cSJoe   PetscCall(KSPSetOperators(ksp, M, M));
21028214e71cSJoe   PetscCall(KSPSetFromOptions(ksp));
21038214e71cSJoe   PetscCall(KSPSolve(ksp, temp_rho, rho0));
21048214e71cSJoe   PetscCall(VecViewFromOptions(rho0, NULL, "-rho0_view"));
21058214e71cSJoe 
21068214e71cSJoe   PetscCall(VecISCopy(rho, potential_IS, SCATTER_FORWARD, temp_rho));
21078214e71cSJoe   PetscCall(VecScale(rho, 0.25));
21088214e71cSJoe   PetscCall(VecViewFromOptions(rho0, NULL, "-rho0_view"));
21098214e71cSJoe   PetscCall(VecViewFromOptions(temp_rho, NULL, "-temprho_view"));
21108214e71cSJoe   PetscCall(VecViewFromOptions(rho, NULL, "-rho_view"));
21118214e71cSJoe   PetscCall(DMRestoreGlobalVector(potential_dm, &temp_rho));
21128214e71cSJoe   PetscCall(DMRestoreGlobalVector(potential_dm, &rho0));
21138214e71cSJoe 
21148214e71cSJoe   PetscCall(MatDestroy(&M_p));
21158214e71cSJoe   PetscCall(MatDestroy(&M));
21168214e71cSJoe   PetscCall(KSPDestroy(&ksp));
21178214e71cSJoe   PetscCall(DMDestroy(&potential_dm));
21188214e71cSJoe   PetscCall(ISDestroy(&potential_IS));
21198214e71cSJoe 
21208214e71cSJoe   PetscCall(DMGetGlobalVector(dm, &phi));
21218214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)phi, "potential"));
21228214e71cSJoe   PetscCall(VecSet(phi, 0.0));
21238214e71cSJoe   PetscCall(SNESSolve(snes, rho, phi));
21248214e71cSJoe   PetscCall(DMRestoreGlobalVector(dm, &rho));
21258214e71cSJoe 
21268214e71cSJoe   PetscCall(VecViewFromOptions(phi, NULL, "-phi_view"));
21278214e71cSJoe 
21288214e71cSJoe   PetscCall(DMGetLocalVector(dm, &locPhi));
21298214e71cSJoe   PetscCall(DMGlobalToLocalBegin(dm, phi, INSERT_VALUES, locPhi));
21308214e71cSJoe   PetscCall(DMGlobalToLocalEnd(dm, phi, INSERT_VALUES, locPhi));
21318214e71cSJoe   PetscCall(DMRestoreGlobalVector(dm, &phi));
213284467f86SMatthew G. Knepley   PetscCall(PetscLogEventEnd(user->ESolveEvent, snes, sw, 0, 0));
21338214e71cSJoe 
213484467f86SMatthew G. Knepley   PetscCall(PetscLogEventBegin(user->ETabEvent, snes, sw, 0, 0));
21358214e71cSJoe   PetscCall(DMGetDS(dm, &ds));
21368214e71cSJoe   PetscCall(PetscDSGetDiscretization(ds, 0, (PetscObject *)&fe));
21378214e71cSJoe   PetscCall(DMSwarmSortGetAccess(sw));
21388214e71cSJoe   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
21398214e71cSJoe   PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords));
21408214e71cSJoe   PetscCall(PetscFEGetQuadrature(fe, &q));
2141f1e6e573SMatthew G. Knepley   PetscFEGeom *chunkgeom = NULL;
21428214e71cSJoe   for (PetscInt c = cStart; c < cEnd; ++c) {
21438214e71cSJoe     PetscTabulation tab;
21448214e71cSJoe     PetscScalar    *clPhi = NULL;
21458214e71cSJoe     PetscReal      *pcoord, *refcoord;
21468214e71cSJoe     PetscInt       *points;
21478214e71cSJoe     PetscInt        Ncp;
21488214e71cSJoe 
21498214e71cSJoe     PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Ncp, &points));
21508214e71cSJoe     PetscCall(DMGetWorkArray(dm, Ncp * dim, MPIU_REAL, &pcoord));
21518214e71cSJoe     PetscCall(DMGetWorkArray(dm, Ncp * dim, MPIU_REAL, &refcoord));
21528214e71cSJoe     for (PetscInt cp = 0; cp < Ncp; ++cp)
21538214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) pcoord[cp * dim + d] = coords[points[cp] * dim + d];
2154f1e6e573SMatthew G. Knepley     {
2155f1e6e573SMatthew G. Knepley       PetscCall(PetscFEGeomGetChunk(user->fegeom, c - cStart, c - cStart + 1, &chunkgeom));
2156f1e6e573SMatthew G. Knepley       for (PetscInt i = 0; i < Ncp; ++i) {
2157f1e6e573SMatthew G. Knepley         // Apply the inverse affine transformation for each point
2158f1e6e573SMatthew G. Knepley         const PetscReal x0[3] = {-1., -1., -1.};
2159f1e6e573SMatthew G. Knepley         CoordinatesRealToRef(dim, dim, x0, chunkgeom->v, chunkgeom->invJ, &pcoord[dim * i], &refcoord[dim * i]);
2160f1e6e573SMatthew G. Knepley       }
2161f1e6e573SMatthew G. Knepley     }
21628214e71cSJoe     PetscCall(PetscFECreateTabulation(fe, 1, Ncp, refcoord, 1, &tab));
21638214e71cSJoe     PetscCall(DMPlexVecGetClosure(dm, NULL, locPhi, c, NULL, &clPhi));
21648214e71cSJoe 
21658214e71cSJoe     for (PetscInt cp = 0; cp < Ncp; ++cp) {
21668214e71cSJoe       const PetscInt p = points[cp];
21678214e71cSJoe 
21688214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) E[p * dim + d] = 0.;
2169f1e6e573SMatthew G. Knepley       PetscCall(PetscFEInterpolateAtPoints_Static(fe, tab, clPhi, chunkgeom, cp, &E[p * dim]));
2170f1e6e573SMatthew G. Knepley       PetscCall(PetscFEPushforward(fe, chunkgeom, 1, &E[p * dim]));
21718214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) {
21728214e71cSJoe         E[p * dim + d] *= -2.0;
21738214e71cSJoe         if (user->fake_1D && d > 0) E[p * dim + d] = 0;
21748214e71cSJoe       }
21758214e71cSJoe     }
21768214e71cSJoe     PetscCall(DMPlexVecRestoreClosure(dm, NULL, locPhi, c, NULL, &clPhi));
2177f1e6e573SMatthew G. Knepley     PetscCall(PetscTabulationDestroy(&tab));
21788214e71cSJoe     PetscCall(DMRestoreWorkArray(dm, Ncp * dim, MPIU_REAL, &pcoord));
21798214e71cSJoe     PetscCall(DMRestoreWorkArray(dm, Ncp * dim, MPIU_REAL, &refcoord));
218084467f86SMatthew G. Knepley     PetscCall(DMSwarmSortRestorePointsPerCell(sw, c, &Ncp, &points));
21818214e71cSJoe   }
21828214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords));
21838214e71cSJoe   PetscCall(DMSwarmSortRestoreAccess(sw));
21848214e71cSJoe   PetscCall(DMRestoreLocalVector(dm, &locPhi));
2185f1e6e573SMatthew G. Knepley   PetscCall(PetscFEGeomRestoreChunk(user->fegeom, 0, 1, &chunkgeom));
218684467f86SMatthew G. Knepley   PetscCall(PetscLogEventEnd(user->ETabEvent, snes, sw, 0, 0));
21878214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
21888214e71cSJoe }
21898214e71cSJoe 
21908214e71cSJoe static PetscErrorCode ComputeFieldAtParticles(SNES snes, DM sw, PetscReal E[])
21918214e71cSJoe {
21928214e71cSJoe   AppCtx  *ctx;
21938214e71cSJoe   PetscInt dim, Np;
21948214e71cSJoe 
21958214e71cSJoe   PetscFunctionBegin;
21968214e71cSJoe   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
21978214e71cSJoe   PetscValidHeaderSpecific(sw, DM_CLASSID, 2);
21988214e71cSJoe   PetscAssertPointer(E, 3);
21998214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
22008214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
22018214e71cSJoe   PetscCall(DMGetApplicationContext(sw, &ctx));
22028214e71cSJoe   PetscCall(PetscArrayzero(E, Np * dim));
2203*9072cb8bSMatthew G. Knepley   ctx->validE = PETSC_TRUE;
22048214e71cSJoe 
22058214e71cSJoe   switch (ctx->em) {
22068214e71cSJoe   case EM_PRIMAL:
22078214e71cSJoe     PetscCall(ComputeFieldAtParticles_Primal(snes, sw, E));
22088214e71cSJoe     break;
22098214e71cSJoe   case EM_COULOMB:
22108214e71cSJoe     PetscCall(ComputeFieldAtParticles_Coulomb(snes, sw, E));
22118214e71cSJoe     break;
22128214e71cSJoe   case EM_MIXED:
22138214e71cSJoe     PetscCall(ComputeFieldAtParticles_Mixed(snes, sw, E));
22148214e71cSJoe     break;
22158214e71cSJoe   case EM_NONE:
22168214e71cSJoe     break;
22178214e71cSJoe   default:
22188214e71cSJoe     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "No solver for electrostatic model %s", EMTypes[ctx->em]);
22198214e71cSJoe   }
22208214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
22218214e71cSJoe }
22228214e71cSJoe 
22238214e71cSJoe static PetscErrorCode RHSFunction(TS ts, PetscReal t, Vec U, Vec G, void *ctx)
22248214e71cSJoe {
22258214e71cSJoe   DM                 sw;
22268214e71cSJoe   SNES               snes = ((AppCtx *)ctx)->snes;
22278214e71cSJoe   const PetscReal   *coords, *vel;
22288214e71cSJoe   const PetscScalar *u;
22298214e71cSJoe   PetscScalar       *g;
22308214e71cSJoe   PetscReal         *E, m_p = 1., q_p = -1.;
22318214e71cSJoe   PetscInt           dim, d, Np, p;
2232b80bf5b1SJoe Pusztay 
2233b80bf5b1SJoe Pusztay   PetscFunctionBeginUser;
22348214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
22358214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
22368214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
22378214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initVelocity", NULL, NULL, (void **)&vel));
22388214e71cSJoe   PetscCall(DMSwarmGetField(sw, "E_field", NULL, NULL, (void **)&E));
22398214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
22408214e71cSJoe   PetscCall(VecGetArrayRead(U, &u));
22418214e71cSJoe   PetscCall(VecGetArray(G, &g));
22428214e71cSJoe 
22438214e71cSJoe   PetscCall(ComputeFieldAtParticles(snes, sw, E));
22448214e71cSJoe 
22458214e71cSJoe   Np /= 2 * dim;
22468214e71cSJoe   for (p = 0; p < Np; ++p) {
22478214e71cSJoe     for (d = 0; d < dim; ++d) {
22488214e71cSJoe       g[(p * 2 + 0) * dim + d] = u[(p * 2 + 1) * dim + d];
22498214e71cSJoe       g[(p * 2 + 1) * dim + d] = q_p * E[p * dim + d] / m_p;
22508214e71cSJoe     }
22518214e71cSJoe   }
22528214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
22538214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initVelocity", NULL, NULL, (void **)&vel));
22548214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "E_field", NULL, NULL, (void **)&E));
22558214e71cSJoe   PetscCall(VecRestoreArrayRead(U, &u));
22568214e71cSJoe   PetscCall(VecRestoreArray(G, &g));
22578214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
22588214e71cSJoe }
22598214e71cSJoe 
22608214e71cSJoe /* J_{ij} = dF_i/dx_j
22618214e71cSJoe    J_p = (  0   1)
22628214e71cSJoe          (-w^2  0)
22638214e71cSJoe    TODO Now there is another term with w^2 from the electric field. I think we will need to invert the operator.
22648214e71cSJoe         Perhaps we can approximate the Jacobian using only the cellwise P-P gradient from Coulomb
22658214e71cSJoe */
22668214e71cSJoe static PetscErrorCode RHSJacobian(TS ts, PetscReal t, Vec U, Mat J, Mat P, void *ctx)
22678214e71cSJoe {
22688214e71cSJoe   DM               sw;
22698214e71cSJoe   const PetscReal *coords, *vel;
22708214e71cSJoe   PetscInt         dim, d, Np, p, rStart;
22718214e71cSJoe 
22728214e71cSJoe   PetscFunctionBeginUser;
22738214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
22748214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
22758214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
22768214e71cSJoe   PetscCall(MatGetOwnershipRange(J, &rStart, NULL));
22778214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
22788214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initVelocity", NULL, NULL, (void **)&vel));
22798214e71cSJoe   Np /= 2 * dim;
22808214e71cSJoe   for (p = 0; p < Np; ++p) {
22818214e71cSJoe     const PetscReal x0      = coords[p * dim + 0];
22828214e71cSJoe     const PetscReal vy0     = vel[p * dim + 1];
22838214e71cSJoe     const PetscReal omega   = vy0 / x0;
22848214e71cSJoe     PetscScalar     vals[4] = {0., 1., -PetscSqr(omega), 0.};
22858214e71cSJoe 
22868214e71cSJoe     for (d = 0; d < dim; ++d) {
22878214e71cSJoe       const PetscInt rows[2] = {(p * 2 + 0) * dim + d + rStart, (p * 2 + 1) * dim + d + rStart};
22888214e71cSJoe       PetscCall(MatSetValues(J, 2, rows, 2, rows, vals, INSERT_VALUES));
22898214e71cSJoe     }
22908214e71cSJoe   }
22918214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
22928214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initVelocity", NULL, NULL, (void **)&vel));
22938214e71cSJoe   PetscCall(MatAssemblyBegin(J, MAT_FINAL_ASSEMBLY));
22948214e71cSJoe   PetscCall(MatAssemblyEnd(J, MAT_FINAL_ASSEMBLY));
22958214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
22968214e71cSJoe }
22978214e71cSJoe 
22988214e71cSJoe static PetscErrorCode RHSFunctionX(TS ts, PetscReal t, Vec V, Vec Xres, void *ctx)
22998214e71cSJoe {
23008214e71cSJoe   AppCtx            *user = (AppCtx *)ctx;
23018214e71cSJoe   DM                 sw;
23028214e71cSJoe   const PetscScalar *v;
23038214e71cSJoe   PetscScalar       *xres;
23048214e71cSJoe   PetscInt           Np, p, d, dim;
23058214e71cSJoe 
23068214e71cSJoe   PetscFunctionBeginUser;
230784467f86SMatthew G. Knepley   PetscCall(PetscLogEventBegin(user->RhsXEvent, ts, 0, 0, 0));
23088214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
23098214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
23108214e71cSJoe   PetscCall(VecGetLocalSize(Xres, &Np));
23119566063dSJacob Faibussowitsch   PetscCall(VecGetArrayRead(V, &v));
23128214e71cSJoe   PetscCall(VecGetArray(Xres, &xres));
2313b80bf5b1SJoe Pusztay   Np /= dim;
2314b80bf5b1SJoe Pusztay   for (p = 0; p < Np; ++p) {
23158214e71cSJoe     for (d = 0; d < dim; ++d) {
23168214e71cSJoe       xres[p * dim + d] = v[p * dim + d];
23178214e71cSJoe       if (user->fake_1D && d > 0) xres[p * dim + d] = 0;
23188214e71cSJoe     }
2319b80bf5b1SJoe Pusztay   }
23209566063dSJacob Faibussowitsch   PetscCall(VecRestoreArrayRead(V, &v));
23218214e71cSJoe   PetscCall(VecRestoreArray(Xres, &xres));
232284467f86SMatthew G. Knepley   PetscCall(PetscLogEventEnd(user->RhsXEvent, ts, 0, 0, 0));
23233ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2324b80bf5b1SJoe Pusztay }
2325b80bf5b1SJoe Pusztay 
23268214e71cSJoe static PetscErrorCode RHSFunctionV(TS ts, PetscReal t, Vec X, Vec Vres, void *ctx)
23278214e71cSJoe {
23288214e71cSJoe   DM                 sw;
23298214e71cSJoe   AppCtx            *user = (AppCtx *)ctx;
23308214e71cSJoe   SNES               snes = ((AppCtx *)ctx)->snes;
23318214e71cSJoe   const PetscScalar *x;
23328214e71cSJoe   const PetscReal   *coords, *vel;
23338214e71cSJoe   PetscReal         *E, m_p, q_p;
23348214e71cSJoe   PetscScalar       *vres;
23358214e71cSJoe   PetscInt           Np, p, dim, d;
23368214e71cSJoe   Parameter         *param;
23378214e71cSJoe 
23388214e71cSJoe   PetscFunctionBeginUser;
233984467f86SMatthew G. Knepley   PetscCall(PetscLogEventBegin(user->RhsVEvent, ts, 0, 0, 0));
23408214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
23418214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
23428214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
23438214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initVelocity", NULL, NULL, (void **)&vel));
23448214e71cSJoe   PetscCall(DMSwarmGetField(sw, "E_field", NULL, NULL, (void **)&E));
23458214e71cSJoe   PetscCall(PetscBagGetData(user->bag, (void **)&param));
23468214e71cSJoe   m_p = user->masses[0] * param->m0;
23478214e71cSJoe   q_p = user->charges[0] * param->q0;
23488214e71cSJoe   PetscCall(VecGetLocalSize(Vres, &Np));
23498214e71cSJoe   PetscCall(VecGetArrayRead(X, &x));
23508214e71cSJoe   PetscCall(VecGetArray(Vres, &vres));
23518214e71cSJoe   PetscCall(ComputeFieldAtParticles(snes, sw, E));
23528214e71cSJoe 
23538214e71cSJoe   Np /= dim;
23548214e71cSJoe   for (p = 0; p < Np; ++p) {
23558214e71cSJoe     for (d = 0; d < dim; ++d) {
23568214e71cSJoe       vres[p * dim + d] = q_p * E[p * dim + d] / m_p;
23578214e71cSJoe       if (user->fake_1D && d > 0) vres[p * dim + d] = 0.;
23588214e71cSJoe     }
23598214e71cSJoe   }
23608214e71cSJoe   PetscCall(VecRestoreArrayRead(X, &x));
23618214e71cSJoe   /*
2362d7c1f440SPierre Jolivet     Synchronized, ordered output for parallel/sequential test cases.
23638214e71cSJoe     In the 1D (on the 2D mesh) case, every y component should be zero.
23648214e71cSJoe   */
23658214e71cSJoe   if (user->checkVRes) {
23668214e71cSJoe     PetscBool pr = user->checkVRes > 1 ? PETSC_TRUE : PETSC_FALSE;
23678214e71cSJoe     PetscInt  step;
23688214e71cSJoe 
23698214e71cSJoe     PetscCall(TSGetStepNumber(ts, &step));
23708214e71cSJoe     if (pr) PetscCall(PetscPrintf(PETSC_COMM_WORLD, "step: %" PetscInt_FMT "\n", step));
23718214e71cSJoe     for (PetscInt p = 0; p < Np; ++p) {
23728214e71cSJoe       if (pr) PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "Residual: %.12g %.12g\n", (double)PetscRealPart(vres[p * dim + 0]), (double)PetscRealPart(vres[p * dim + 1])));
23738214e71cSJoe       PetscCheck(PetscAbsScalar(vres[p * dim + 1]) < PETSC_SMALL, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Y velocity should be 0., not %g", (double)PetscRealPart(vres[p * dim + 1]));
23748214e71cSJoe     }
23758214e71cSJoe     if (pr) PetscCall(PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT));
23768214e71cSJoe   }
23778214e71cSJoe   PetscCall(VecRestoreArray(Vres, &vres));
23788214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
23798214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initVelocity", NULL, NULL, (void **)&vel));
23808214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "E_field", NULL, NULL, (void **)&E));
238184467f86SMatthew G. Knepley   PetscCall(PetscLogEventEnd(user->RhsVEvent, ts, 0, 0, 0));
23828214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
23838214e71cSJoe }
23848214e71cSJoe 
23858214e71cSJoe static PetscErrorCode CreateSolution(TS ts)
23868214e71cSJoe {
23878214e71cSJoe   DM       sw;
23888214e71cSJoe   Vec      u;
23898214e71cSJoe   PetscInt dim, Np;
23908214e71cSJoe 
23918214e71cSJoe   PetscFunctionBegin;
23928214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
23938214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
23948214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
23958214e71cSJoe   PetscCall(VecCreate(PETSC_COMM_WORLD, &u));
23968214e71cSJoe   PetscCall(VecSetBlockSize(u, dim));
23978214e71cSJoe   PetscCall(VecSetSizes(u, 2 * Np * dim, PETSC_DECIDE));
23988214e71cSJoe   PetscCall(VecSetUp(u));
23998214e71cSJoe   PetscCall(TSSetSolution(ts, u));
24008214e71cSJoe   PetscCall(VecDestroy(&u));
24018214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
24028214e71cSJoe }
24038214e71cSJoe 
24048214e71cSJoe static PetscErrorCode SetProblem(TS ts)
24058214e71cSJoe {
24068214e71cSJoe   AppCtx *user;
24078214e71cSJoe   DM      sw;
24088214e71cSJoe 
24098214e71cSJoe   PetscFunctionBegin;
24108214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
24118214e71cSJoe   PetscCall(DMGetApplicationContext(sw, (void **)&user));
24128214e71cSJoe   // Define unified system for (X, V)
24138214e71cSJoe   {
24148214e71cSJoe     Mat      J;
24158214e71cSJoe     PetscInt dim, Np;
24168214e71cSJoe 
24178214e71cSJoe     PetscCall(DMGetDimension(sw, &dim));
24188214e71cSJoe     PetscCall(DMSwarmGetLocalSize(sw, &Np));
24198214e71cSJoe     PetscCall(MatCreate(PETSC_COMM_WORLD, &J));
24208214e71cSJoe     PetscCall(MatSetSizes(J, 2 * Np * dim, 2 * Np * dim, PETSC_DECIDE, PETSC_DECIDE));
24218214e71cSJoe     PetscCall(MatSetBlockSize(J, 2 * dim));
24228214e71cSJoe     PetscCall(MatSetFromOptions(J));
24238214e71cSJoe     PetscCall(MatSetUp(J));
24248214e71cSJoe     PetscCall(TSSetRHSFunction(ts, NULL, RHSFunction, user));
24258214e71cSJoe     PetscCall(TSSetRHSJacobian(ts, J, J, RHSJacobian, user));
24268214e71cSJoe     PetscCall(MatDestroy(&J));
24278214e71cSJoe   }
24288214e71cSJoe   /* Define split system for X and V */
24298214e71cSJoe   {
24308214e71cSJoe     Vec             u;
24318214e71cSJoe     IS              isx, isv, istmp;
24328214e71cSJoe     const PetscInt *idx;
24338214e71cSJoe     PetscInt        dim, Np, rstart;
24348214e71cSJoe 
24358214e71cSJoe     PetscCall(TSGetSolution(ts, &u));
24368214e71cSJoe     PetscCall(DMGetDimension(sw, &dim));
24378214e71cSJoe     PetscCall(DMSwarmGetLocalSize(sw, &Np));
24388214e71cSJoe     PetscCall(VecGetOwnershipRange(u, &rstart, NULL));
24398214e71cSJoe     PetscCall(ISCreateStride(PETSC_COMM_WORLD, Np, (rstart / dim) + 0, 2, &istmp));
24408214e71cSJoe     PetscCall(ISGetIndices(istmp, &idx));
24418214e71cSJoe     PetscCall(ISCreateBlock(PETSC_COMM_WORLD, dim, Np, idx, PETSC_COPY_VALUES, &isx));
24428214e71cSJoe     PetscCall(ISRestoreIndices(istmp, &idx));
24438214e71cSJoe     PetscCall(ISDestroy(&istmp));
24448214e71cSJoe     PetscCall(ISCreateStride(PETSC_COMM_WORLD, Np, (rstart / dim) + 1, 2, &istmp));
24458214e71cSJoe     PetscCall(ISGetIndices(istmp, &idx));
24468214e71cSJoe     PetscCall(ISCreateBlock(PETSC_COMM_WORLD, dim, Np, idx, PETSC_COPY_VALUES, &isv));
24478214e71cSJoe     PetscCall(ISRestoreIndices(istmp, &idx));
24488214e71cSJoe     PetscCall(ISDestroy(&istmp));
24498214e71cSJoe     PetscCall(TSRHSSplitSetIS(ts, "position", isx));
24508214e71cSJoe     PetscCall(TSRHSSplitSetIS(ts, "momentum", isv));
24518214e71cSJoe     PetscCall(ISDestroy(&isx));
24528214e71cSJoe     PetscCall(ISDestroy(&isv));
24538214e71cSJoe     PetscCall(TSRHSSplitSetRHSFunction(ts, "position", NULL, RHSFunctionX, user));
24548214e71cSJoe     PetscCall(TSRHSSplitSetRHSFunction(ts, "momentum", NULL, RHSFunctionV, user));
24558214e71cSJoe   }
24568214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
24578214e71cSJoe }
24588214e71cSJoe 
24598214e71cSJoe static PetscErrorCode DMSwarmTSRedistribute(TS ts)
24608214e71cSJoe {
24618214e71cSJoe   DM        sw;
24628214e71cSJoe   Vec       u;
24638214e71cSJoe   PetscReal t, maxt, dt;
24648214e71cSJoe   PetscInt  n, maxn;
24658214e71cSJoe 
24668214e71cSJoe   PetscFunctionBegin;
24678214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
24688214e71cSJoe   PetscCall(TSGetTime(ts, &t));
24698214e71cSJoe   PetscCall(TSGetMaxTime(ts, &maxt));
24708214e71cSJoe   PetscCall(TSGetTimeStep(ts, &dt));
24718214e71cSJoe   PetscCall(TSGetStepNumber(ts, &n));
24728214e71cSJoe   PetscCall(TSGetMaxSteps(ts, &maxn));
24738214e71cSJoe 
24748214e71cSJoe   PetscCall(TSReset(ts));
24758214e71cSJoe   PetscCall(TSSetDM(ts, sw));
24768214e71cSJoe   PetscCall(TSSetFromOptions(ts));
24778214e71cSJoe   PetscCall(TSSetTime(ts, t));
24788214e71cSJoe   PetscCall(TSSetMaxTime(ts, maxt));
24798214e71cSJoe   PetscCall(TSSetTimeStep(ts, dt));
24808214e71cSJoe   PetscCall(TSSetStepNumber(ts, n));
24818214e71cSJoe   PetscCall(TSSetMaxSteps(ts, maxn));
24828214e71cSJoe 
24838214e71cSJoe   PetscCall(CreateSolution(ts));
24848214e71cSJoe   PetscCall(SetProblem(ts));
24858214e71cSJoe   PetscCall(TSGetSolution(ts, &u));
24868214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
24878214e71cSJoe }
24888214e71cSJoe 
24898214e71cSJoe PetscErrorCode line(PetscInt dim, PetscReal time, const PetscReal dummy[], PetscInt p, PetscScalar x[], void *ctx)
24908214e71cSJoe {
24918214e71cSJoe   DM        sw, cdm;
24928214e71cSJoe   PetscInt  Np;
24938214e71cSJoe   PetscReal low[2], high[2];
24948214e71cSJoe   AppCtx   *user = (AppCtx *)ctx;
24958214e71cSJoe 
24968214e71cSJoe   sw = user->swarm;
24978214e71cSJoe   PetscCall(DMSwarmGetCellDM(sw, &cdm));
24988214e71cSJoe   // Get the bounding box so we can equally space the particles
24998214e71cSJoe   PetscCall(DMGetLocalBoundingBox(cdm, low, high));
25008214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
25018214e71cSJoe   // shift it by h/2 so nothing is initialized directly on a boundary
25028214e71cSJoe   x[0] = ((high[0] - low[0]) / Np) * (p + 0.5);
25038214e71cSJoe   x[1] = 0.;
25048214e71cSJoe   return PETSC_SUCCESS;
25058214e71cSJoe }
25068214e71cSJoe 
2507b80bf5b1SJoe Pusztay /*
25088214e71cSJoe   InitializeSolveAndSwarm - Set the solution values to the swarm coordinates and velocities, and also possibly set the initial values.
25098214e71cSJoe 
25108214e71cSJoe   Input Parameters:
25118214e71cSJoe + ts         - The TS
2512d7c1f440SPierre Jolivet - useInitial - Flag to also set the initial conditions to the current coordinates and velocities and setup the problem
25138214e71cSJoe 
25148214e71cSJoe   Output Parameters:
25158214e71cSJoe . u - The initialized solution vector
25168214e71cSJoe 
25178214e71cSJoe   Level: advanced
25188214e71cSJoe 
25198214e71cSJoe .seealso: InitializeSolve()
2520b80bf5b1SJoe Pusztay */
25218214e71cSJoe static PetscErrorCode InitializeSolveAndSwarm(TS ts, PetscBool useInitial)
2522d71ae5a4SJacob Faibussowitsch {
25238214e71cSJoe   DM       sw;
25248214e71cSJoe   Vec      u, gc, gv, gc0, gv0;
25258214e71cSJoe   IS       isx, isv;
25268214e71cSJoe   PetscInt dim;
25278214e71cSJoe   AppCtx  *user;
2528b80bf5b1SJoe Pusztay 
2529b80bf5b1SJoe Pusztay   PetscFunctionBeginUser;
25308214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
25318214e71cSJoe   PetscCall(DMGetApplicationContext(sw, &user));
25328214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
25338214e71cSJoe   if (useInitial) {
25348214e71cSJoe     PetscReal v0[2] = {1., 0.};
25358214e71cSJoe     if (user->perturbed_weights) {
25368214e71cSJoe       PetscCall(InitializeParticles_PerturbedWeights(sw, user));
25378214e71cSJoe     } else {
25388214e71cSJoe       PetscCall(DMSwarmComputeLocalSizeFromOptions(sw));
25398214e71cSJoe       PetscCall(DMSwarmInitializeCoordinates(sw));
25408214e71cSJoe       if (user->fake_1D) {
25418214e71cSJoe         PetscCall(InitializeVelocities_Fake1D(sw, user));
25428214e71cSJoe       } else {
25438214e71cSJoe         PetscCall(DMSwarmInitializeVelocitiesFromOptions(sw, v0));
25448214e71cSJoe       }
25458214e71cSJoe     }
25468214e71cSJoe     PetscCall(DMSwarmMigrate(sw, PETSC_TRUE));
25478214e71cSJoe     PetscCall(DMSwarmTSRedistribute(ts));
25488214e71cSJoe   }
25498214e71cSJoe   PetscCall(TSGetSolution(ts, &u));
25508214e71cSJoe   PetscCall(TSRHSSplitGetIS(ts, "position", &isx));
25518214e71cSJoe   PetscCall(TSRHSSplitGetIS(ts, "momentum", &isv));
25528214e71cSJoe   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, DMSwarmPICField_coor, &gc));
25538214e71cSJoe   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "initCoordinates", &gc0));
25548214e71cSJoe   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "velocity", &gv));
25558214e71cSJoe   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "initVelocity", &gv0));
25568214e71cSJoe   if (useInitial) {
25578214e71cSJoe     PetscCall(VecCopy(gc, gc0));
25588214e71cSJoe     PetscCall(VecCopy(gv, gv0));
25598214e71cSJoe   }
25608214e71cSJoe   PetscCall(VecISCopy(u, isx, SCATTER_FORWARD, gc));
25618214e71cSJoe   PetscCall(VecISCopy(u, isv, SCATTER_FORWARD, gv));
25628214e71cSJoe   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, DMSwarmPICField_coor, &gc));
25638214e71cSJoe   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "initCoordinates", &gc0));
25648214e71cSJoe   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "velocity", &gv));
25658214e71cSJoe   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "initVelocity", &gv0));
25668214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
25678214e71cSJoe }
25688214e71cSJoe 
25698214e71cSJoe static PetscErrorCode InitializeSolve(TS ts, Vec u)
2570b80bf5b1SJoe Pusztay {
25718214e71cSJoe   PetscFunctionBegin;
25728214e71cSJoe   PetscCall(TSSetSolution(ts, u));
25738214e71cSJoe   PetscCall(InitializeSolveAndSwarm(ts, PETSC_TRUE));
25748214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
2575b80bf5b1SJoe Pusztay }
2576b80bf5b1SJoe Pusztay 
25778214e71cSJoe static PetscErrorCode ComputeError(TS ts, Vec U, Vec E)
25788214e71cSJoe {
25798214e71cSJoe   MPI_Comm           comm;
25808214e71cSJoe   DM                 sw;
25818214e71cSJoe   AppCtx            *user;
25828214e71cSJoe   const PetscScalar *u;
25838214e71cSJoe   const PetscReal   *coords, *vel;
25848214e71cSJoe   PetscScalar       *e;
25858214e71cSJoe   PetscReal          t;
25868214e71cSJoe   PetscInt           dim, Np, p;
2587b80bf5b1SJoe Pusztay 
25888214e71cSJoe   PetscFunctionBeginUser;
25898214e71cSJoe   PetscCall(PetscObjectGetComm((PetscObject)ts, &comm));
25908214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
25918214e71cSJoe   PetscCall(DMGetApplicationContext(sw, &user));
25928214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
25938214e71cSJoe   PetscCall(TSGetSolveTime(ts, &t));
25948214e71cSJoe   PetscCall(VecGetArray(E, &e));
25958214e71cSJoe   PetscCall(VecGetArrayRead(U, &u));
25968214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
25978214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
25988214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initVelocity", NULL, NULL, (void **)&vel));
25998214e71cSJoe   Np /= 2 * dim;
26008214e71cSJoe   for (p = 0; p < Np; ++p) {
26018214e71cSJoe     /* TODO generalize initial conditions and project into plane instead of assuming x-y */
26028214e71cSJoe     const PetscReal    r0    = DMPlex_NormD_Internal(dim, &coords[p * dim]);
26038214e71cSJoe     const PetscReal    th0   = PetscAtan2Real(coords[p * dim + 1], coords[p * dim + 0]);
26048214e71cSJoe     const PetscReal    v0    = DMPlex_NormD_Internal(dim, &vel[p * dim]);
26058214e71cSJoe     const PetscReal    omega = v0 / r0;
26068214e71cSJoe     const PetscReal    ct    = PetscCosReal(omega * t + th0);
26078214e71cSJoe     const PetscReal    st    = PetscSinReal(omega * t + th0);
26088214e71cSJoe     const PetscScalar *x     = &u[(p * 2 + 0) * dim];
26098214e71cSJoe     const PetscScalar *v     = &u[(p * 2 + 1) * dim];
26108214e71cSJoe     const PetscReal    xe[3] = {r0 * ct, r0 * st, 0.0};
26118214e71cSJoe     const PetscReal    ve[3] = {-v0 * st, v0 * ct, 0.0};
26128214e71cSJoe     PetscInt           d;
26138214e71cSJoe 
26148214e71cSJoe     for (d = 0; d < dim; ++d) {
26158214e71cSJoe       e[(p * 2 + 0) * dim + d] = x[d] - xe[d];
26168214e71cSJoe       e[(p * 2 + 1) * dim + d] = v[d] - ve[d];
2617b80bf5b1SJoe Pusztay     }
26188214e71cSJoe     if (user->error) {
26198214e71cSJoe       const PetscReal en   = 0.5 * DMPlex_DotRealD_Internal(dim, v, v);
26208214e71cSJoe       const PetscReal exen = 0.5 * PetscSqr(v0);
26218214e71cSJoe       PetscCall(PetscPrintf(comm, "t %.4g: p%" PetscInt_FMT " error [%.2g %.2g] 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)));
2622b80bf5b1SJoe Pusztay     }
26238214e71cSJoe   }
26248214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
26258214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initVelocity", NULL, NULL, (void **)&vel));
26268214e71cSJoe   PetscCall(VecRestoreArrayRead(U, &u));
26278214e71cSJoe   PetscCall(VecRestoreArray(E, &e));
26288214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
26298214e71cSJoe }
26308214e71cSJoe 
26318214e71cSJoe static PetscErrorCode MigrateParticles(TS ts)
26328214e71cSJoe {
26338214e71cSJoe   DM               sw, cdm;
26348214e71cSJoe   const PetscReal *L;
2635*9072cb8bSMatthew G. Knepley   AppCtx          *ctx;
26368214e71cSJoe 
26378214e71cSJoe   PetscFunctionBeginUser;
26388214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
2639*9072cb8bSMatthew G. Knepley   PetscCall(DMGetApplicationContext(sw, &ctx));
26408214e71cSJoe   PetscCall(DMViewFromOptions(sw, NULL, "-migrate_view_pre"));
26418214e71cSJoe   {
26428214e71cSJoe     Vec        u, gc, gv, position, momentum;
26438214e71cSJoe     IS         isx, isv;
26448214e71cSJoe     PetscReal *pos, *mom;
26458214e71cSJoe 
26468214e71cSJoe     PetscCall(TSGetSolution(ts, &u));
26478214e71cSJoe     PetscCall(TSRHSSplitGetIS(ts, "position", &isx));
26488214e71cSJoe     PetscCall(TSRHSSplitGetIS(ts, "momentum", &isv));
26498214e71cSJoe     PetscCall(VecGetSubVector(u, isx, &position));
26508214e71cSJoe     PetscCall(VecGetSubVector(u, isv, &momentum));
26518214e71cSJoe     PetscCall(VecGetArray(position, &pos));
26528214e71cSJoe     PetscCall(VecGetArray(momentum, &mom));
26538214e71cSJoe     PetscCall(DMSwarmCreateGlobalVectorFromField(sw, DMSwarmPICField_coor, &gc));
26548214e71cSJoe     PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "velocity", &gv));
26558214e71cSJoe     PetscCall(VecISCopy(u, isx, SCATTER_REVERSE, gc));
26568214e71cSJoe     PetscCall(VecISCopy(u, isv, SCATTER_REVERSE, gv));
26578214e71cSJoe 
26588214e71cSJoe     PetscCall(DMSwarmGetCellDM(sw, &cdm));
26598214e71cSJoe     PetscCall(DMGetPeriodicity(cdm, NULL, NULL, &L));
26608214e71cSJoe     if ((L[0] || L[1]) >= 0.) {
26618214e71cSJoe       PetscReal *x, *v, upper[3], lower[3];
26628214e71cSJoe       PetscInt   Np, dim;
26638214e71cSJoe 
26648214e71cSJoe       PetscCall(DMSwarmGetLocalSize(sw, &Np));
26658214e71cSJoe       PetscCall(DMGetDimension(cdm, &dim));
26668214e71cSJoe       PetscCall(DMGetBoundingBox(cdm, lower, upper));
26678214e71cSJoe       PetscCall(VecGetArray(gc, &x));
26688214e71cSJoe       PetscCall(VecGetArray(gv, &v));
26698214e71cSJoe       for (PetscInt p = 0; p < Np; ++p) {
26708214e71cSJoe         for (PetscInt d = 0; d < dim; ++d) {
26718214e71cSJoe           if (pos[p * dim + d] < lower[d]) {
26728214e71cSJoe             x[p * dim + d] = pos[p * dim + d] + (upper[d] - lower[d]);
26738214e71cSJoe           } else if (pos[p * dim + d] > upper[d]) {
26748214e71cSJoe             x[p * dim + d] = pos[p * dim + d] - (upper[d] - lower[d]);
26758214e71cSJoe           } else {
26768214e71cSJoe             x[p * dim + d] = pos[p * dim + d];
26778214e71cSJoe           }
26788214e71cSJoe           PetscCheck(x[p * dim + d] >= lower[d] && x[p * dim + d] <= upper[d], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "p: %" PetscInt_FMT "x[%" PetscInt_FMT "] %g", p, d, (double)x[p * dim + d]);
26798214e71cSJoe           v[p * dim + d] = mom[p * dim + d];
26808214e71cSJoe         }
26818214e71cSJoe       }
26828214e71cSJoe       PetscCall(VecRestoreArray(gc, &x));
26838214e71cSJoe       PetscCall(VecRestoreArray(gv, &v));
26848214e71cSJoe     }
26858214e71cSJoe     PetscCall(VecRestoreArray(position, &pos));
26868214e71cSJoe     PetscCall(VecRestoreArray(momentum, &mom));
26878214e71cSJoe     PetscCall(VecRestoreSubVector(u, isx, &position));
26888214e71cSJoe     PetscCall(VecRestoreSubVector(u, isv, &momentum));
26898214e71cSJoe     PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "velocity", &gv));
26908214e71cSJoe     PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, DMSwarmPICField_coor, &gc));
26918214e71cSJoe   }
26928214e71cSJoe   PetscCall(DMSwarmMigrate(sw, PETSC_TRUE));
2693*9072cb8bSMatthew G. Knepley   PetscInt step;
2694*9072cb8bSMatthew G. Knepley 
2695*9072cb8bSMatthew G. Knepley   PetscCall(TSGetStepNumber(ts, &step));
2696*9072cb8bSMatthew G. Knepley   if (ctx->remap && !(step % ctx->remapFreq)) {
2697*9072cb8bSMatthew G. Knepley     // Monitor electric field before we destroy it
2698*9072cb8bSMatthew G. Knepley     PetscReal ptime;
2699*9072cb8bSMatthew G. Knepley     PetscInt  step;
2700*9072cb8bSMatthew G. Knepley 
2701*9072cb8bSMatthew G. Knepley     PetscCall(TSGetStepNumber(ts, &step));
2702*9072cb8bSMatthew G. Knepley     PetscCall(TSGetTime(ts, &ptime));
2703*9072cb8bSMatthew G. Knepley     if (ctx->efield_monitor) PetscCall(MonitorEField(ts, step, ptime, NULL, ctx));
2704*9072cb8bSMatthew G. Knepley     if (ctx->poisson_monitor) PetscCall(MonitorPoisson(ts, step, ptime, NULL, ctx));
2705*9072cb8bSMatthew G. Knepley     PetscCall(DMSwarmRemap(sw));
2706*9072cb8bSMatthew G. Knepley     ctx->validE = PETSC_FALSE;
2707*9072cb8bSMatthew G. Knepley   }
2708*9072cb8bSMatthew G. Knepley   // This MUST come last, since it recreates the subswarms and they must DMClone() the new swarm
27098214e71cSJoe   PetscCall(DMSwarmTSRedistribute(ts));
27108214e71cSJoe   PetscCall(InitializeSolveAndSwarm(ts, PETSC_FALSE));
27113ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2712b80bf5b1SJoe Pusztay }
2713b80bf5b1SJoe Pusztay 
2714d71ae5a4SJacob Faibussowitsch int main(int argc, char **argv)
2715d71ae5a4SJacob Faibussowitsch {
2716b80bf5b1SJoe Pusztay   DM        dm, sw;
27178214e71cSJoe   TS        ts;
27188214e71cSJoe   Vec       u;
27198214e71cSJoe   PetscReal dt;
27208214e71cSJoe   PetscInt  maxn;
2721b80bf5b1SJoe Pusztay   AppCtx    user;
2722b80bf5b1SJoe Pusztay 
27239566063dSJacob Faibussowitsch   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
27248214e71cSJoe   PetscCall(ProcessOptions(PETSC_COMM_WORLD, &user));
27258214e71cSJoe   PetscCall(PetscBagCreate(PETSC_COMM_SELF, sizeof(Parameter), &user.bag));
27268214e71cSJoe   PetscCall(CreateMesh(PETSC_COMM_WORLD, &user, &dm));
27278214e71cSJoe   PetscCall(CreatePoisson(dm, &user));
27288214e71cSJoe   PetscCall(CreateSwarm(dm, &user, &sw));
27298214e71cSJoe   PetscCall(SetupParameters(PETSC_COMM_WORLD, &user));
27308214e71cSJoe   PetscCall(InitializeConstants(sw, &user));
27318214e71cSJoe   PetscCall(DMSetApplicationContext(sw, &user));
2732b80bf5b1SJoe Pusztay 
27338214e71cSJoe   PetscCall(TSCreate(PETSC_COMM_WORLD, &ts));
27348214e71cSJoe   PetscCall(TSSetProblemType(ts, TS_NONLINEAR));
27359566063dSJacob Faibussowitsch   PetscCall(TSSetDM(ts, sw));
27368214e71cSJoe   PetscCall(TSSetMaxTime(ts, 0.1));
27378214e71cSJoe   PetscCall(TSSetTimeStep(ts, 0.00001));
27388214e71cSJoe   PetscCall(TSSetMaxSteps(ts, 100));
27398214e71cSJoe   PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_MATCHSTEP));
27408214e71cSJoe 
27418214e71cSJoe   if (user.efield_monitor) PetscCall(TSMonitorSet(ts, MonitorEField, &user, NULL));
2742f1e6e573SMatthew G. Knepley   if (user.moment_monitor) PetscCall(TSMonitorSet(ts, MonitorMoments, &user, NULL));
27438214e71cSJoe   if (user.initial_monitor) PetscCall(TSMonitorSet(ts, MonitorInitialConditions, &user, NULL));
2744*9072cb8bSMatthew G. Knepley   if (user.positions_monitor) PetscCall(TSMonitorSet(ts, MonitorPositions_2D, &user, NULL));
27458214e71cSJoe   if (user.poisson_monitor) PetscCall(TSMonitorSet(ts, MonitorPoisson, &user, NULL));
27468214e71cSJoe 
27478214e71cSJoe   PetscCall(TSSetFromOptions(ts));
27488214e71cSJoe   PetscCall(TSGetTimeStep(ts, &dt));
27498214e71cSJoe   PetscCall(TSGetMaxSteps(ts, &maxn));
27508214e71cSJoe   user.steps    = maxn;
27518214e71cSJoe   user.stepSize = dt;
27528214e71cSJoe   PetscCall(SetupContext(dm, sw, &user));
27538214e71cSJoe   PetscCall(TSSetComputeInitialCondition(ts, InitializeSolve));
27548214e71cSJoe   PetscCall(TSSetComputeExactError(ts, ComputeError));
27558214e71cSJoe   PetscCall(TSSetPostStep(ts, MigrateParticles));
27568214e71cSJoe   PetscCall(CreateSolution(ts));
27578214e71cSJoe   PetscCall(TSGetSolution(ts, &u));
27588214e71cSJoe   PetscCall(TSComputeInitialCondition(ts, u));
27598214e71cSJoe   PetscCall(CheckNonNegativeWeights(sw, &user));
27608214e71cSJoe   PetscCall(TSSolve(ts, NULL));
27618214e71cSJoe 
27629566063dSJacob Faibussowitsch   PetscCall(SNESDestroy(&user.snes));
2763f1e6e573SMatthew G. Knepley   PetscCall(MatDestroy(&user.M));
2764f1e6e573SMatthew G. Knepley   PetscCall(PetscFEGeomDestroy(&user.fegeom));
27659566063dSJacob Faibussowitsch   PetscCall(TSDestroy(&ts));
27669566063dSJacob Faibussowitsch   PetscCall(DMDestroy(&sw));
27679566063dSJacob Faibussowitsch   PetscCall(DMDestroy(&dm));
27688214e71cSJoe   PetscCall(DestroyContext(&user));
27699566063dSJacob Faibussowitsch   PetscCall(PetscFinalize());
2770b122ec5aSJacob Faibussowitsch   return 0;
2771b80bf5b1SJoe Pusztay }
2772b80bf5b1SJoe Pusztay 
2773b80bf5b1SJoe Pusztay /*TEST
2774b80bf5b1SJoe Pusztay 
2775b80bf5b1SJoe Pusztay    build:
27768214e71cSJoe     requires: !complex double
27778214e71cSJoe 
27788214e71cSJoe   # This tests that we can put particles in a box and compute the Coulomb force
27798214e71cSJoe   # Recommend -draw_size 500,500
27808214e71cSJoe    testset:
27818214e71cSJoe      requires: defined(PETSC_HAVE_EXECUTABLE_EXPORT)
27828214e71cSJoe      args: -dm_plex_dim 2 -fake_1D -dm_plex_simplex 0 -dm_plex_box_faces 20,1 \
27838214e71cSJoe              -dm_plex_box_lower 0,-1 -dm_plex_box_upper 12.5664,1 \
2784b80bf5b1SJoe Pusztay              -dm_plex_box_bd periodic,none \
2785*9072cb8bSMatthew G. Knepley            -vdm_plex_simplex 0 \
27868214e71cSJoe            -dm_swarm_coordinate_density constant -dm_swarm_num_particles 100 \
27878214e71cSJoe            -sigma 1.0e-8 -timeScale 2.0e-14 \
27888214e71cSJoe            -ts_type basicsymplectic -ts_basicsymplectic_type 1 \
27898214e71cSJoe              -ts_monitor_sp_swarm -ts_monitor_sp_swarm_retain 0 -ts_monitor_sp_swarm_phase 0 \
27908214e71cSJoe            -output_step 50 -check_vel_res
27918214e71cSJoe      test:
27928214e71cSJoe        suffix: none_1d
2793*9072cb8bSMatthew G. Knepley        requires:
27948214e71cSJoe        args: -em_type none -error
27958214e71cSJoe      test:
27968214e71cSJoe        suffix: coulomb_1d
27978214e71cSJoe        args: -em_type coulomb
27988214e71cSJoe 
27998214e71cSJoe    # for viewers
28008214e71cSJoe    #-ts_monitor_sp_swarm_phase -ts_monitor_sp_swarm -em_snes_monitor -ts_monitor_sp_swarm_multi_species 0 -ts_monitor_sp_swarm_retain 0
28018214e71cSJoe    testset:
28028214e71cSJoe      nsize: {{1 2}}
28038214e71cSJoe      requires: defined(PETSC_HAVE_EXECUTABLE_EXPORT)
28048214e71cSJoe      args: -dm_plex_dim 2 -fake_1D -dm_plex_simplex 0 -dm_plex_box_faces 36,1 \
28058214e71cSJoe              -dm_plex_box_lower 0.,-0.5 -dm_plex_box_upper 12.5664,0.5 \
28068214e71cSJoe              -dm_plex_box_bd periodic,none \
28078214e71cSJoe            -vdm_plex_dim 1 -vdm_plex_simplex 0 -vdm_plex_box_faces 10 \
28088214e71cSJoe              -vdm_plex_box_lower -3 -vdm_plex_box_upper 3 \
280984467f86SMatthew G. Knepley            -dm_swarm_num_species 1 -twostream -charges -1.,1. -sigma 1.0e-8 \
28108214e71cSJoe              -cosine_coefficients 0.01,0.5 -perturbed_weights -total_weight 1. \
28118214e71cSJoe            -ts_type basicsymplectic -ts_basicsymplectic_type 2 \
28128214e71cSJoe              -ts_dt 0.01 -ts_max_time 5 -ts_max_steps 10 \
28138214e71cSJoe            -em_snes_atol 1.e-15 -em_snes_error_if_not_converged -em_ksp_error_if_not_converged \
281484467f86SMatthew G. Knepley            -output_step 1 -check_vel_res -dm_swarm_print_coords 1 -dm_swarm_print_weights 1
28158214e71cSJoe      test:
28168214e71cSJoe        suffix: two_stream_c0
28178214e71cSJoe        args: -em_type primal -petscfe_default_quadrature_order 2 -petscspace_degree 2 -em_pc_type svd
28188214e71cSJoe      test:
28198214e71cSJoe        suffix: two_stream_rt
28208214e71cSJoe        requires: superlu_dist
28218214e71cSJoe        args: -em_type mixed \
28228214e71cSJoe                -potential_petscspace_degree 0 \
28238214e71cSJoe                -potential_petscdualspace_lagrange_use_moments \
28248214e71cSJoe                -potential_petscdualspace_lagrange_moment_order 2 \
28258214e71cSJoe                -field_petscspace_degree 2 -field_petscfe_default_quadrature_order 1 \
28268214e71cSJoe                -field_petscspace_type sum \
28278214e71cSJoe                  -field_petscspace_variables 2 \
28288214e71cSJoe                  -field_petscspace_components 2 \
28298214e71cSJoe                  -field_petscspace_sum_spaces 2 \
28308214e71cSJoe                  -field_petscspace_sum_concatenate true \
28318214e71cSJoe                  -field_sumcomp_0_petscspace_variables 2 \
28328214e71cSJoe                  -field_sumcomp_0_petscspace_type tensor \
28338214e71cSJoe                  -field_sumcomp_0_petscspace_tensor_spaces 2 \
28348214e71cSJoe                  -field_sumcomp_0_petscspace_tensor_uniform false \
28358214e71cSJoe                  -field_sumcomp_0_tensorcomp_0_petscspace_degree 1 \
28368214e71cSJoe                  -field_sumcomp_0_tensorcomp_1_petscspace_degree 0 \
28378214e71cSJoe                  -field_sumcomp_1_petscspace_variables 2 \
28388214e71cSJoe                  -field_sumcomp_1_petscspace_type tensor \
28398214e71cSJoe                  -field_sumcomp_1_petscspace_tensor_spaces 2 \
28408214e71cSJoe                  -field_sumcomp_1_petscspace_tensor_uniform false \
28418214e71cSJoe                  -field_sumcomp_1_tensorcomp_0_petscspace_degree 0 \
28428214e71cSJoe                  -field_sumcomp_1_tensorcomp_1_petscspace_degree 1 \
28438214e71cSJoe                -field_petscdualspace_form_degree -1 \
28448214e71cSJoe                -field_petscdualspace_order 1 \
28458214e71cSJoe                -field_petscdualspace_lagrange_trimmed true \
28468214e71cSJoe              -em_snes_error_if_not_converged \
28478214e71cSJoe              -em_ksp_type preonly -em_ksp_error_if_not_converged \
28488214e71cSJoe              -em_pc_type fieldsplit -em_pc_fieldsplit_type schur \
28498214e71cSJoe                -em_pc_fieldsplit_schur_fact_type full -em_pc_fieldsplit_schur_precondition full \
28508214e71cSJoe                -em_fieldsplit_field_pc_type lu \
28518214e71cSJoe                  -em_fieldsplit_field_pc_factor_mat_solver_type superlu_dist \
28528214e71cSJoe                -em_fieldsplit_potential_pc_type svd
28538214e71cSJoe 
285484467f86SMatthew G. Knepley    # For an eyeball check, we use
2855*9072cb8bSMatthew G. Knepley    # -ts_max_steps 1000 -dm_plex_box_faces 10,1 -vdm_plex_box_faces 2000 -efield_monitor
28568214e71cSJoe    # For verification, we use
285784467f86SMatthew G. Knepley    # -ts_max_steps 1000 -dm_plex_box_faces 100,1 -vdm_plex_box_faces 8000 -dm_swarm_num_particles 800000 -monitor_efield
28588214e71cSJoe    # -ts_monitor_sp_swarm_multi_species 0 -ts_monitor_sp_swarm_retain 0 -ts_monitor_sp_swarm_phase 1 -draw_size 500,500
28598214e71cSJoe    testset:
28608214e71cSJoe      nsize: {{1 2}}
28618214e71cSJoe      requires: defined(PETSC_HAVE_EXECUTABLE_EXPORT)
28628214e71cSJoe      args: -dm_plex_dim 2 -fake_1D -dm_plex_simplex 0 -dm_plex_box_faces 10,1 \
28638214e71cSJoe              -dm_plex_box_lower 0.,-0.5 -dm_plex_box_upper 12.5664,0.5 \
28648214e71cSJoe              -dm_plex_box_bd periodic,none \
28658214e71cSJoe            -vdm_plex_dim 1 -vdm_plex_simplex 0 -vdm_plex_box_faces 10 \
28668214e71cSJoe              -vdm_plex_box_lower -10 -vdm_plex_box_upper 10 \
2867f1e6e573SMatthew G. Knepley              -vpetscspace_degree 2 -vdm_plex_hash_location \
286884467f86SMatthew G. Knepley            -dm_swarm_num_species 1 -charges -1.,1. \
28698214e71cSJoe              -cosine_coefficients 0.01,0.5 -perturbed_weights -total_weight 1. \
28708214e71cSJoe            -ts_type basicsymplectic -ts_basicsymplectic_type 1 \
28718214e71cSJoe              -ts_dt 0.03 -ts_max_time 500 -ts_max_steps 1 \
28728214e71cSJoe            -em_snes_atol 1.e-12 -em_snes_error_if_not_converged -em_ksp_error_if_not_converged \
287384467f86SMatthew G. Knepley            -output_step 1 -check_vel_res -dm_swarm_print_coords 1 -dm_swarm_print_weights 1
28748214e71cSJoe 
28758214e71cSJoe      test:
28768214e71cSJoe        suffix: uniform_equilibrium_1d
28778214e71cSJoe        args: -cosine_coefficients 0.0,0.5 -em_type primal -petscspace_degree 1 -em_pc_type svd
28788214e71cSJoe      test:
287975155f48SMatthew G. Knepley        suffix: uniform_equilibrium_1d_real
288075155f48SMatthew G. Knepley        args: -dm_plex_dim 1 -dm_plex_simplex 1 -fake_1D 0 -dm_plex_box_faces 10 \
288175155f48SMatthew G. Knepley                -dm_plex_box_lower 0. -dm_plex_box_upper 12.5664 -dm_plex_box_bd periodic \
288275155f48SMatthew G. Knepley              -cosine_coefficients 0.0 -em_type primal -petscspace_degree 1 -em_pc_type svd
288375155f48SMatthew G. Knepley      test:
28848214e71cSJoe        suffix: uniform_primal_1d
28858214e71cSJoe        args: -em_type primal -petscspace_degree 1 -em_pc_type svd
28868214e71cSJoe      test:
288775155f48SMatthew G. Knepley        suffix: uniform_primal_1d_real
288875155f48SMatthew G. Knepley        args: -dm_plex_dim 1 -dm_plex_simplex 1 -fake_1D 0 -dm_plex_box_faces 10 \
288975155f48SMatthew G. Knepley                -dm_plex_box_lower 0. -dm_plex_box_upper 12.5664 -dm_plex_box_bd periodic \
289075155f48SMatthew G. Knepley              -cosine_coefficients 0.01 -em_type primal -petscspace_degree 1 -em_pc_type svd
2891*9072cb8bSMatthew G. Knepley      # NOT WORKING -ftop_pc_type bjacobi -ftop_sub_pc_type lu -ftop_sub_pc_factor_shift_type nonzero
2892*9072cb8bSMatthew G. Knepley      test:
2893*9072cb8bSMatthew G. Knepley        suffix: uniform_primal_1d_real_pfak
2894*9072cb8bSMatthew G. Knepley        nsize: 1
2895*9072cb8bSMatthew G. Knepley        args: -dm_plex_dim 1 -dm_plex_simplex 1 -fake_1D 0 -dm_plex_box_faces 10 \
2896*9072cb8bSMatthew G. Knepley                -dm_plex_box_lower 0. -dm_plex_box_upper 12.5664 -dm_plex_box_bd periodic \
2897*9072cb8bSMatthew G. Knepley              -remap_dm_plex_dim 2 -remap_dm_plex_simplex 0 -remap_dm_plex_box_faces 10,10 -remap_dm_plex_box_bd periodic,none \
2898*9072cb8bSMatthew G. Knepley                -remap_dm_plex_box_lower 0.,-10. -remap_dm_plex_box_upper 12.5664,10. \
2899*9072cb8bSMatthew G. Knepley                -remap_petscspace_degree 2 -remap_dm_plex_hash_location \
2900*9072cb8bSMatthew G. Knepley              -remap -remap_freq 1 -remap_type pfak \
2901*9072cb8bSMatthew G. Knepley                -ftop_ksp_type lsqr -ftop_pc_type none -ftop_ksp_rtol 1.e-14 \
2902*9072cb8bSMatthew G. Knepley                -ptof_pc_type lu \
2903*9072cb8bSMatthew G. Knepley              -cosine_coefficients 0.01 -em_type primal -petscspace_degree 1 -em_pc_type svd -em_proj_pc_type lu
290475155f48SMatthew G. Knepley      test:
29058214e71cSJoe        requires: superlu_dist
29068214e71cSJoe        suffix: uniform_mixed_1d
29078214e71cSJoe        args: -em_type mixed \
29088214e71cSJoe                -potential_petscspace_degree 0 \
29098214e71cSJoe                -potential_petscdualspace_lagrange_use_moments \
29108214e71cSJoe                -potential_petscdualspace_lagrange_moment_order 2 \
29118214e71cSJoe                -field_petscspace_degree 2 -field_petscfe_default_quadrature_order 1 \
29128214e71cSJoe                -field_petscspace_type sum \
29138214e71cSJoe                  -field_petscspace_variables 2 \
29148214e71cSJoe                  -field_petscspace_components 2 \
29158214e71cSJoe                  -field_petscspace_sum_spaces 2 \
29168214e71cSJoe                  -field_petscspace_sum_concatenate true \
29178214e71cSJoe                  -field_sumcomp_0_petscspace_variables 2 \
29188214e71cSJoe                  -field_sumcomp_0_petscspace_type tensor \
29198214e71cSJoe                  -field_sumcomp_0_petscspace_tensor_spaces 2 \
29208214e71cSJoe                  -field_sumcomp_0_petscspace_tensor_uniform false \
29218214e71cSJoe                  -field_sumcomp_0_tensorcomp_0_petscspace_degree 1 \
29228214e71cSJoe                  -field_sumcomp_0_tensorcomp_1_petscspace_degree 0 \
29238214e71cSJoe                  -field_sumcomp_1_petscspace_variables 2 \
29248214e71cSJoe                  -field_sumcomp_1_petscspace_type tensor \
29258214e71cSJoe                  -field_sumcomp_1_petscspace_tensor_spaces 2 \
29268214e71cSJoe                  -field_sumcomp_1_petscspace_tensor_uniform false \
29278214e71cSJoe                  -field_sumcomp_1_tensorcomp_0_petscspace_degree 0 \
29288214e71cSJoe                  -field_sumcomp_1_tensorcomp_1_petscspace_degree 1 \
29298214e71cSJoe                -field_petscdualspace_form_degree -1 \
29308214e71cSJoe                -field_petscdualspace_order 1 \
29318214e71cSJoe                -field_petscdualspace_lagrange_trimmed true \
29328214e71cSJoe              -em_snes_error_if_not_converged \
29338214e71cSJoe              -em_ksp_type preonly -em_ksp_error_if_not_converged \
29348214e71cSJoe              -em_pc_type fieldsplit -em_pc_fieldsplit_type schur \
29358214e71cSJoe                -em_pc_fieldsplit_schur_fact_type full -em_pc_fieldsplit_schur_precondition full \
29368214e71cSJoe                -em_fieldsplit_field_pc_type lu \
29378214e71cSJoe                  -em_fieldsplit_field_pc_factor_mat_solver_type superlu_dist \
29388214e71cSJoe                -em_fieldsplit_potential_pc_type svd
29398214e71cSJoe 
2940b80bf5b1SJoe Pusztay TEST*/
2941