xref: /petsc/src/ts/tutorials/hamiltonian/ex2.c (revision 8214e71cafbcb127ae10b482372edc608d7428e6)
1*8214e71cSJoe static char help[] = "Landau Damping/Two Stream instability test using Vlasov-Poisson equations\n";
2b80bf5b1SJoe Pusztay 
3*8214e71cSJoe /*
4*8214e71cSJoe   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"
5*8214e71cSJoe   According to Lukas, good damping results come at ~16k particles per cell
6*8214e71cSJoe 
7*8214e71cSJoe   To visualize the efield use
8*8214e71cSJoe 
9*8214e71cSJoe     -monitor_efield
10*8214e71cSJoe 
11*8214e71cSJoe   To visualize the swarm distribution use
12*8214e71cSJoe 
13*8214e71cSJoe     -ts_monitor_hg_swarm
14*8214e71cSJoe 
15*8214e71cSJoe   To visualize the particles, we can use
16*8214e71cSJoe 
17*8214e71cSJoe     -ts_monitor_sp_swarm -ts_monitor_sp_swarm_retain 0 -ts_monitor_sp_swarm_phase 1 -draw_size 500,500
18*8214e71cSJoe 
19*8214e71cSJoe */
20b80bf5b1SJoe Pusztay #include <petscts.h>
21*8214e71cSJoe #include <petscdmplex.h>
22*8214e71cSJoe #include <petscdmswarm.h>
23*8214e71cSJoe #include <petscfe.h>
24*8214e71cSJoe #include <petscds.h>
25*8214e71cSJoe #include <petscbag.h>
26*8214e71cSJoe #include <petscdraw.h>
27*8214e71cSJoe #include <petsc/private/dmpleximpl.h>  /* For norm and dot */
28*8214e71cSJoe #include <petsc/private/petscfeimpl.h> /* For interpolation */
29*8214e71cSJoe #include "petscdm.h"
30*8214e71cSJoe #include "petscdmlabel.h"
31*8214e71cSJoe 
32*8214e71cSJoe PETSC_EXTERN PetscErrorCode stream(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar[], void *);
33*8214e71cSJoe PETSC_EXTERN PetscErrorCode line(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar[], void *);
34*8214e71cSJoe 
35*8214e71cSJoe const char *EMTypes[] = {"primal", "mixed", "coulomb", "none", "EMType", "EM_", NULL};
36*8214e71cSJoe typedef enum {
37*8214e71cSJoe   EM_PRIMAL,
38*8214e71cSJoe   EM_MIXED,
39*8214e71cSJoe   EM_COULOMB,
40*8214e71cSJoe   EM_NONE
41*8214e71cSJoe } EMType;
42*8214e71cSJoe 
43*8214e71cSJoe typedef enum {
44*8214e71cSJoe   V0,
45*8214e71cSJoe   X0,
46*8214e71cSJoe   T0,
47*8214e71cSJoe   M0,
48*8214e71cSJoe   Q0,
49*8214e71cSJoe   PHI0,
50*8214e71cSJoe   POISSON,
51*8214e71cSJoe   VLASOV,
52*8214e71cSJoe   SIGMA,
53*8214e71cSJoe   NUM_CONSTANTS
54*8214e71cSJoe } ConstantType;
55*8214e71cSJoe typedef struct {
56*8214e71cSJoe   PetscScalar v0; /* Velocity scale, often the thermal velocity */
57*8214e71cSJoe   PetscScalar t0; /* Time scale */
58*8214e71cSJoe   PetscScalar x0; /* Space scale */
59*8214e71cSJoe   PetscScalar m0; /* Mass scale */
60*8214e71cSJoe   PetscScalar q0; /* Charge scale */
61*8214e71cSJoe   PetscScalar kb;
62*8214e71cSJoe   PetscScalar epsi0;
63*8214e71cSJoe   PetscScalar phi0;          /* Potential scale */
64*8214e71cSJoe   PetscScalar poissonNumber; /* Non-Dimensional Poisson Number */
65*8214e71cSJoe   PetscScalar vlasovNumber;  /* Non-Dimensional Vlasov Number */
66*8214e71cSJoe   PetscReal   sigma;         /* Nondimensional charge per length in x */
67*8214e71cSJoe } Parameter;
68b80bf5b1SJoe Pusztay 
69b80bf5b1SJoe Pusztay typedef struct {
70*8214e71cSJoe   PetscBag    bag;            /* Problem parameters */
71*8214e71cSJoe   PetscBool   error;          /* Flag for printing the error */
72*8214e71cSJoe   PetscBool   efield_monitor; /* Flag to show electric field monitor */
73*8214e71cSJoe   PetscBool   initial_monitor;
74*8214e71cSJoe   PetscBool   fake_1D;           /* Run simulation in 2D but zeroing second dimension */
75*8214e71cSJoe   PetscBool   perturbed_weights; /* Uniformly sample x,v space with gaussian weights */
76*8214e71cSJoe   PetscBool   poisson_monitor;
77*8214e71cSJoe   PetscInt    ostep; /* print the energy at each ostep time steps */
78*8214e71cSJoe   PetscInt    numParticles;
79*8214e71cSJoe   PetscReal   timeScale;              /* Nondimensionalizing time scale */
80*8214e71cSJoe   PetscReal   charges[2];             /* The charges of each species */
81*8214e71cSJoe   PetscReal   masses[2];              /* The masses of each species */
82*8214e71cSJoe   PetscReal   thermal_energy[2];      /* Thermal Energy (used to get other constants)*/
83*8214e71cSJoe   PetscReal   cosine_coefficients[2]; /*(alpha, k)*/
84*8214e71cSJoe   PetscReal   totalWeight;
85*8214e71cSJoe   PetscReal   stepSize;
86*8214e71cSJoe   PetscInt    steps;
87*8214e71cSJoe   PetscReal   initVel;
88*8214e71cSJoe   EMType      em; /* Type of electrostatic model */
89*8214e71cSJoe   SNES        snes;
90*8214e71cSJoe   PetscDraw   drawef;
91*8214e71cSJoe   PetscDrawLG drawlg_ef;
92*8214e71cSJoe   PetscDraw   drawic_x;
93*8214e71cSJoe   PetscDraw   drawic_v;
94*8214e71cSJoe   PetscDraw   drawic_w;
95*8214e71cSJoe   PetscDrawHG drawhgic_x;
96*8214e71cSJoe   PetscDrawHG drawhgic_v;
97*8214e71cSJoe   PetscDrawHG drawhgic_w;
98*8214e71cSJoe   PetscDraw   EDraw;
99*8214e71cSJoe   PetscDraw   RhoDraw;
100*8214e71cSJoe   PetscDraw   PotDraw;
101*8214e71cSJoe   PetscDrawSP EDrawSP;
102*8214e71cSJoe   PetscDrawSP RhoDrawSP;
103*8214e71cSJoe   PetscDrawSP PotDrawSP;
104*8214e71cSJoe   PetscBool   monitor_positions; /* Flag to show particle positins at each time step */
105*8214e71cSJoe   PetscDraw   positionDraw;
106*8214e71cSJoe   PetscDrawSP positionDrawSP;
107*8214e71cSJoe   DM          swarm;
108*8214e71cSJoe   PetscRandom random;
109*8214e71cSJoe   PetscBool   twostream;
110*8214e71cSJoe   PetscBool   checkweights;
111*8214e71cSJoe   PetscInt    checkVRes; /* Flag to check/output velocity residuals for nightly tests */
112b80bf5b1SJoe Pusztay } AppCtx;
113b80bf5b1SJoe Pusztay 
114d71ae5a4SJacob Faibussowitsch static PetscErrorCode ProcessOptions(MPI_Comm comm, AppCtx *options)
115d71ae5a4SJacob Faibussowitsch {
116b80bf5b1SJoe Pusztay   PetscFunctionBeginUser;
117*8214e71cSJoe   PetscInt d                      = 2;
118*8214e71cSJoe   PetscInt maxSpecies             = 2;
119*8214e71cSJoe   options->error                  = PETSC_FALSE;
120*8214e71cSJoe   options->efield_monitor         = PETSC_FALSE;
121*8214e71cSJoe   options->initial_monitor        = PETSC_FALSE;
122*8214e71cSJoe   options->fake_1D                = PETSC_FALSE;
123*8214e71cSJoe   options->perturbed_weights      = PETSC_FALSE;
124*8214e71cSJoe   options->poisson_monitor        = PETSC_FALSE;
125*8214e71cSJoe   options->ostep                  = 100;
126*8214e71cSJoe   options->timeScale              = 2.0e-14;
127*8214e71cSJoe   options->charges[0]             = -1.0;
128*8214e71cSJoe   options->charges[1]             = 1.0;
129*8214e71cSJoe   options->masses[0]              = 1.0;
130*8214e71cSJoe   options->masses[1]              = 1000.0;
131*8214e71cSJoe   options->thermal_energy[0]      = 1.0;
132*8214e71cSJoe   options->thermal_energy[1]      = 1.0;
133*8214e71cSJoe   options->cosine_coefficients[0] = 0.01;
134*8214e71cSJoe   options->cosine_coefficients[1] = 0.5;
135*8214e71cSJoe   options->initVel                = 1;
136*8214e71cSJoe   options->totalWeight            = 1.0;
137*8214e71cSJoe   options->drawef                 = NULL;
138*8214e71cSJoe   options->drawlg_ef              = NULL;
139*8214e71cSJoe   options->drawic_x               = NULL;
140*8214e71cSJoe   options->drawic_v               = NULL;
141*8214e71cSJoe   options->drawic_w               = NULL;
142*8214e71cSJoe   options->drawhgic_x             = NULL;
143*8214e71cSJoe   options->drawhgic_v             = NULL;
144*8214e71cSJoe   options->drawhgic_w             = NULL;
145*8214e71cSJoe   options->EDraw                  = NULL;
146*8214e71cSJoe   options->RhoDraw                = NULL;
147*8214e71cSJoe   options->PotDraw                = NULL;
148*8214e71cSJoe   options->EDrawSP                = NULL;
149*8214e71cSJoe   options->RhoDrawSP              = NULL;
150*8214e71cSJoe   options->PotDrawSP              = NULL;
151*8214e71cSJoe   options->em                     = EM_COULOMB;
152*8214e71cSJoe   options->numParticles           = 32768;
153*8214e71cSJoe   options->monitor_positions      = PETSC_FALSE;
154*8214e71cSJoe   options->positionDraw           = NULL;
155*8214e71cSJoe   options->positionDrawSP         = NULL;
156*8214e71cSJoe   options->twostream              = PETSC_FALSE;
157*8214e71cSJoe   options->checkweights           = PETSC_FALSE;
158*8214e71cSJoe   options->checkVRes              = 0;
159b80bf5b1SJoe Pusztay 
160*8214e71cSJoe   PetscOptionsBegin(comm, "", "Landau Damping and Two Stream options", "DMSWARM");
161*8214e71cSJoe   PetscCall(PetscOptionsBool("-error", "Flag to print the error", "ex2.c", options->error, &options->error, NULL));
162*8214e71cSJoe   PetscCall(PetscOptionsBool("-monitor_efield", "Flag to show efield plot", "ex2.c", options->efield_monitor, &options->efield_monitor, NULL));
163*8214e71cSJoe   PetscCall(PetscOptionsBool("-monitor_ics", "Flag to show initial condition histograms", "ex2.c", options->initial_monitor, &options->initial_monitor, NULL));
164*8214e71cSJoe   PetscCall(PetscOptionsBool("-monitor_positions", "The flag to show particle positions", "ex2.c", options->monitor_positions, &options->monitor_positions, NULL));
165*8214e71cSJoe   PetscCall(PetscOptionsBool("-monitor_poisson", "The flag to show charges, Efield and potential solve", "ex2.c", options->poisson_monitor, &options->poisson_monitor, NULL));
166*8214e71cSJoe   PetscCall(PetscOptionsBool("-fake_1D", "Flag to run a 1D simulation (but really in 2D)", "ex2.c", options->fake_1D, &options->fake_1D, NULL));
167*8214e71cSJoe   PetscCall(PetscOptionsBool("-twostream", "Run two stream instability", "ex2.c", options->twostream, &options->twostream, NULL));
168*8214e71cSJoe   PetscCall(PetscOptionsBool("-perturbed_weights", "Flag to run uniform sampling with perturbed weights", "ex2.c", options->perturbed_weights, &options->perturbed_weights, NULL));
169*8214e71cSJoe   PetscCall(PetscOptionsBool("-check_weights", "Ensure all particle weights are positive", "ex2.c", options->checkweights, &options->checkweights, NULL));
170*8214e71cSJoe   PetscCall(PetscOptionsInt("-check_vel_res", "Check particle velocity residuals for nightly tests", "ex2.c", options->checkVRes, &options->checkVRes, NULL));
171*8214e71cSJoe   PetscCall(PetscOptionsInt("-output_step", "Number of time steps between output", "ex2.c", options->ostep, &options->ostep, NULL));
172*8214e71cSJoe   PetscCall(PetscOptionsReal("-timeScale", "Nondimensionalizing time scale", "ex2.c", options->timeScale, &options->timeScale, NULL));
173*8214e71cSJoe   PetscCall(PetscOptionsReal("-initial_velocity", "Initial velocity of perturbed particle", "ex2.c", options->initVel, &options->initVel, NULL));
174*8214e71cSJoe   PetscCall(PetscOptionsReal("-total_weight", "Total weight of all particles", "ex2.c", options->totalWeight, &options->totalWeight, NULL));
175*8214e71cSJoe   PetscCall(PetscOptionsRealArray("-cosine_coefficients", "Amplitude and frequency of cosine equation used in initialization", "ex2.c", options->cosine_coefficients, &d, NULL));
176*8214e71cSJoe   PetscCall(PetscOptionsRealArray("-charges", "Species charges", "ex2.c", options->charges, &maxSpecies, NULL));
177*8214e71cSJoe   PetscCall(PetscOptionsEnum("-em_type", "Type of electrostatic solver", "ex2.c", EMTypes, (PetscEnum)options->em, (PetscEnum *)&options->em, NULL));
178d0609cedSBarry Smith   PetscOptionsEnd();
1793ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
180b80bf5b1SJoe Pusztay }
181b80bf5b1SJoe Pusztay 
182*8214e71cSJoe static PetscErrorCode SetupContext(DM dm, DM sw, AppCtx *user)
183*8214e71cSJoe {
184*8214e71cSJoe   PetscFunctionBeginUser;
185*8214e71cSJoe   if (user->efield_monitor) {
186*8214e71cSJoe     PetscDrawAxis axis_ef;
187*8214e71cSJoe     PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "monitor_efield", 0, 300, 400, 300, &user->drawef));
188*8214e71cSJoe     PetscCall(PetscDrawSetSave(user->drawef, "ex9_Efield.png"));
189*8214e71cSJoe     PetscCall(PetscDrawSetFromOptions(user->drawef));
190*8214e71cSJoe     PetscCall(PetscDrawLGCreate(user->drawef, 1, &user->drawlg_ef));
191*8214e71cSJoe     PetscCall(PetscDrawLGGetAxis(user->drawlg_ef, &axis_ef));
192*8214e71cSJoe     PetscCall(PetscDrawAxisSetLabels(axis_ef, "Electron Electric Field", "time", "E_max"));
193*8214e71cSJoe     PetscCall(PetscDrawLGSetLimits(user->drawlg_ef, 0., user->steps * user->stepSize, -10., 0.));
194*8214e71cSJoe     PetscCall(PetscDrawAxisSetLimits(axis_ef, 0., user->steps * user->stepSize, -10., 0.));
195*8214e71cSJoe   }
196*8214e71cSJoe   if (user->initial_monitor) {
197*8214e71cSJoe     PetscDrawAxis axis1, axis2, axis3;
198*8214e71cSJoe     PetscReal     dmboxlower[2], dmboxupper[2];
199*8214e71cSJoe     PetscInt      dim, cStart, cEnd;
200*8214e71cSJoe     PetscCall(DMGetDimension(sw, &dim));
201*8214e71cSJoe     PetscCall(DMGetBoundingBox(dm, dmboxlower, dmboxupper));
202*8214e71cSJoe     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
203*8214e71cSJoe 
204*8214e71cSJoe     PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "monitor_initial_conditions_x", 0, 300, 400, 300, &user->drawic_x));
205*8214e71cSJoe     PetscCall(PetscDrawSetSave(user->drawic_x, "ex9_ic_x.png"));
206*8214e71cSJoe     PetscCall(PetscDrawSetFromOptions(user->drawic_x));
207*8214e71cSJoe     PetscCall(PetscDrawHGCreate(user->drawic_x, dim, &user->drawhgic_x));
208*8214e71cSJoe     PetscCall(PetscDrawHGGetAxis(user->drawhgic_x, &axis1));
209*8214e71cSJoe     PetscCall(PetscDrawHGSetNumberBins(user->drawhgic_x, cEnd - cStart));
210*8214e71cSJoe     PetscCall(PetscDrawAxisSetLabels(axis1, "Initial X Distribution", "X", "counts"));
211*8214e71cSJoe     PetscCall(PetscDrawAxisSetLimits(axis1, dmboxlower[0], dmboxupper[0], 0, 1500));
212*8214e71cSJoe 
213*8214e71cSJoe     PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "monitor_initial_conditions_v", 400, 300, 400, 300, &user->drawic_v));
214*8214e71cSJoe     PetscCall(PetscDrawSetSave(user->drawic_v, "ex9_ic_v.png"));
215*8214e71cSJoe     PetscCall(PetscDrawSetFromOptions(user->drawic_v));
216*8214e71cSJoe     PetscCall(PetscDrawHGCreate(user->drawic_v, dim, &user->drawhgic_v));
217*8214e71cSJoe     PetscCall(PetscDrawHGGetAxis(user->drawhgic_v, &axis2));
218*8214e71cSJoe     PetscCall(PetscDrawHGSetNumberBins(user->drawhgic_v, 1000));
219*8214e71cSJoe     PetscCall(PetscDrawAxisSetLabels(axis2, "Initial V_x Distribution", "V", "counts"));
220*8214e71cSJoe     PetscCall(PetscDrawAxisSetLimits(axis2, -1, 1, 0, 1500));
221*8214e71cSJoe 
222*8214e71cSJoe     PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "monitor_initial_conditions_w", 800, 300, 400, 300, &user->drawic_w));
223*8214e71cSJoe     PetscCall(PetscDrawSetSave(user->drawic_w, "ex9_ic_w.png"));
224*8214e71cSJoe     PetscCall(PetscDrawSetFromOptions(user->drawic_w));
225*8214e71cSJoe     PetscCall(PetscDrawHGCreate(user->drawic_w, dim, &user->drawhgic_w));
226*8214e71cSJoe     PetscCall(PetscDrawHGGetAxis(user->drawhgic_w, &axis3));
227*8214e71cSJoe     PetscCall(PetscDrawHGSetNumberBins(user->drawhgic_w, 10));
228*8214e71cSJoe     PetscCall(PetscDrawAxisSetLabels(axis3, "Initial W Distribution", "weight", "counts"));
229*8214e71cSJoe     PetscCall(PetscDrawAxisSetLimits(axis3, 0, 0.01, 0, 5000));
230*8214e71cSJoe   }
231*8214e71cSJoe   if (user->monitor_positions) {
232*8214e71cSJoe     PetscDrawAxis axis;
233*8214e71cSJoe 
234*8214e71cSJoe     PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "position_monitor_species1", 0, 0, 400, 300, &user->positionDraw));
235*8214e71cSJoe     PetscCall(PetscDrawSetFromOptions(user->positionDraw));
236*8214e71cSJoe     PetscCall(PetscDrawSPCreate(user->positionDraw, 10, &user->positionDrawSP));
237*8214e71cSJoe     PetscCall(PetscDrawSPSetDimension(user->positionDrawSP, 1));
238*8214e71cSJoe     PetscCall(PetscDrawSPGetAxis(user->positionDrawSP, &axis));
239*8214e71cSJoe     PetscCall(PetscDrawSPReset(user->positionDrawSP));
240*8214e71cSJoe     PetscCall(PetscDrawAxisSetLabels(axis, "Particles", "x", "v"));
241*8214e71cSJoe     PetscCall(PetscDrawSetSave(user->positionDraw, "ex9_pos.png"));
242*8214e71cSJoe   }
243*8214e71cSJoe   if (user->poisson_monitor) {
244*8214e71cSJoe     PetscDrawAxis axis_E, axis_Rho, axis_Pot;
245*8214e71cSJoe 
246*8214e71cSJoe     PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "Efield_monitor", 0, 0, 400, 300, &user->EDraw));
247*8214e71cSJoe     PetscCall(PetscDrawSetFromOptions(user->EDraw));
248*8214e71cSJoe     PetscCall(PetscDrawSPCreate(user->EDraw, 10, &user->EDrawSP));
249*8214e71cSJoe     PetscCall(PetscDrawSPSetDimension(user->EDrawSP, 1));
250*8214e71cSJoe     PetscCall(PetscDrawSPGetAxis(user->EDrawSP, &axis_E));
251*8214e71cSJoe     PetscCall(PetscDrawSPReset(user->EDrawSP));
252*8214e71cSJoe     PetscCall(PetscDrawAxisSetLabels(axis_E, "Particles", "x", "E"));
253*8214e71cSJoe     PetscCall(PetscDrawSetSave(user->EDraw, "ex9_E_spatial.png"));
254*8214e71cSJoe 
255*8214e71cSJoe     PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "rho_monitor", 0, 0, 400, 300, &user->RhoDraw));
256*8214e71cSJoe     PetscCall(PetscDrawSetFromOptions(user->RhoDraw));
257*8214e71cSJoe     PetscCall(PetscDrawSPCreate(user->RhoDraw, 10, &user->RhoDrawSP));
258*8214e71cSJoe     PetscCall(PetscDrawSPSetDimension(user->RhoDrawSP, 1));
259*8214e71cSJoe     PetscCall(PetscDrawSPGetAxis(user->RhoDrawSP, &axis_Rho));
260*8214e71cSJoe     PetscCall(PetscDrawSPReset(user->RhoDrawSP));
261*8214e71cSJoe     PetscCall(PetscDrawAxisSetLabels(axis_Rho, "Particles", "x", "rho"));
262*8214e71cSJoe     PetscCall(PetscDrawSetSave(user->RhoDraw, "ex9_rho_spatial.png"));
263*8214e71cSJoe 
264*8214e71cSJoe     PetscCall(PetscDrawCreate(PETSC_COMM_WORLD, NULL, "potential_monitor", 0, 0, 400, 300, &user->PotDraw));
265*8214e71cSJoe     PetscCall(PetscDrawSetFromOptions(user->PotDraw));
266*8214e71cSJoe     PetscCall(PetscDrawSPCreate(user->PotDraw, 10, &user->PotDrawSP));
267*8214e71cSJoe     PetscCall(PetscDrawSPSetDimension(user->PotDrawSP, 1));
268*8214e71cSJoe     PetscCall(PetscDrawSPGetAxis(user->PotDrawSP, &axis_Pot));
269*8214e71cSJoe     PetscCall(PetscDrawSPReset(user->PotDrawSP));
270*8214e71cSJoe     PetscCall(PetscDrawAxisSetLabels(axis_Pot, "Particles", "x", "potential"));
271*8214e71cSJoe     PetscCall(PetscDrawSetSave(user->PotDraw, "ex9_phi_spatial.png"));
272*8214e71cSJoe   }
273*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
274*8214e71cSJoe }
275*8214e71cSJoe 
276*8214e71cSJoe static PetscErrorCode DestroyContext(AppCtx *user)
277*8214e71cSJoe {
278*8214e71cSJoe   PetscFunctionBeginUser;
279*8214e71cSJoe   PetscCall(PetscDrawLGDestroy(&user->drawlg_ef));
280*8214e71cSJoe   PetscCall(PetscDrawDestroy(&user->drawef));
281*8214e71cSJoe   PetscCall(PetscDrawHGDestroy(&user->drawhgic_x));
282*8214e71cSJoe   PetscCall(PetscDrawDestroy(&user->drawic_x));
283*8214e71cSJoe   PetscCall(PetscDrawHGDestroy(&user->drawhgic_v));
284*8214e71cSJoe   PetscCall(PetscDrawDestroy(&user->drawic_v));
285*8214e71cSJoe   PetscCall(PetscDrawHGDestroy(&user->drawhgic_w));
286*8214e71cSJoe   PetscCall(PetscDrawDestroy(&user->drawic_w));
287*8214e71cSJoe   PetscCall(PetscDrawSPDestroy(&user->positionDrawSP));
288*8214e71cSJoe   PetscCall(PetscDrawDestroy(&user->positionDraw));
289*8214e71cSJoe 
290*8214e71cSJoe   PetscCall(PetscDrawSPDestroy(&user->EDrawSP));
291*8214e71cSJoe   PetscCall(PetscDrawDestroy(&user->EDraw));
292*8214e71cSJoe   PetscCall(PetscDrawSPDestroy(&user->RhoDrawSP));
293*8214e71cSJoe   PetscCall(PetscDrawDestroy(&user->RhoDraw));
294*8214e71cSJoe   PetscCall(PetscDrawSPDestroy(&user->PotDrawSP));
295*8214e71cSJoe   PetscCall(PetscDrawDestroy(&user->PotDraw));
296*8214e71cSJoe 
297*8214e71cSJoe   PetscCall(PetscBagDestroy(&user->bag));
298*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
299*8214e71cSJoe }
300*8214e71cSJoe 
301*8214e71cSJoe static PetscErrorCode CheckNonNegativeWeights(DM sw, AppCtx *user)
302*8214e71cSJoe {
303*8214e71cSJoe   const PetscScalar *w;
304*8214e71cSJoe   PetscInt           Np;
305*8214e71cSJoe 
306*8214e71cSJoe   PetscFunctionBeginUser;
307*8214e71cSJoe   if (!user->checkweights) PetscFunctionReturn(PETSC_SUCCESS);
308*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&w));
309*8214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
310*8214e71cSJoe   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]);
311*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&w));
312*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
313*8214e71cSJoe }
314*8214e71cSJoe 
315*8214e71cSJoe static PetscErrorCode computeParticleMoments(DM sw, PetscReal moments[3], AppCtx *user)
316*8214e71cSJoe {
317*8214e71cSJoe   DM                 dm;
318*8214e71cSJoe   const PetscReal   *coords;
319*8214e71cSJoe   const PetscScalar *w;
320*8214e71cSJoe   PetscReal          mom[3] = {0.0, 0.0, 0.0};
321*8214e71cSJoe   PetscInt           cell, cStart, cEnd, dim;
322*8214e71cSJoe 
323*8214e71cSJoe   PetscFunctionBeginUser;
324*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
325*8214e71cSJoe   PetscCall(DMSwarmGetCellDM(sw, &dm));
326*8214e71cSJoe   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
327*8214e71cSJoe   PetscCall(DMSwarmSortGetAccess(sw));
328*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "velocity", NULL, NULL, (void **)&coords));
329*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&w));
330*8214e71cSJoe   for (cell = cStart; cell < cEnd; ++cell) {
331*8214e71cSJoe     PetscInt *pidx;
332*8214e71cSJoe     PetscInt  Np, p, d;
333*8214e71cSJoe 
334*8214e71cSJoe     PetscCall(DMSwarmSortGetPointsPerCell(sw, cell, &Np, &pidx));
335*8214e71cSJoe     for (p = 0; p < Np; ++p) {
336*8214e71cSJoe       const PetscInt   idx = pidx[p];
337*8214e71cSJoe       const PetscReal *c   = &coords[idx * dim];
338*8214e71cSJoe 
339*8214e71cSJoe       mom[0] += PetscRealPart(w[idx]);
340*8214e71cSJoe       mom[1] += PetscRealPart(w[idx]) * c[0];
341*8214e71cSJoe       for (d = 0; d < dim; ++d) mom[2] += PetscRealPart(w[idx]) * c[d] * c[d];
342*8214e71cSJoe       //if (w[idx] < 0. ) PetscPrintf(PETSC_COMM_WORLD, "error, negative weight %" PetscInt_FMT " \n", idx);
343*8214e71cSJoe     }
344*8214e71cSJoe     PetscCall(PetscFree(pidx));
345*8214e71cSJoe   }
346*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "velocity", NULL, NULL, (void **)&coords));
347*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&w));
348*8214e71cSJoe   PetscCall(DMSwarmSortRestoreAccess(sw));
349*8214e71cSJoe   PetscCallMPI(MPI_Allreduce(mom, moments, 3, MPIU_REAL, MPI_SUM, PetscObjectComm((PetscObject)sw)));
350*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
351*8214e71cSJoe }
352*8214e71cSJoe 
353*8214e71cSJoe static void f0_1(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[])
354*8214e71cSJoe {
355*8214e71cSJoe   f0[0] = u[0];
356*8214e71cSJoe }
357*8214e71cSJoe 
358*8214e71cSJoe static void f0_x(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[])
359*8214e71cSJoe {
360*8214e71cSJoe   f0[0] = x[0] * u[0];
361*8214e71cSJoe }
362*8214e71cSJoe 
363*8214e71cSJoe static void f0_r2(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[])
364*8214e71cSJoe {
365*8214e71cSJoe   PetscInt d;
366*8214e71cSJoe 
367*8214e71cSJoe   f0[0] = 0.0;
368*8214e71cSJoe   for (d = 0; d < dim; ++d) f0[0] += PetscSqr(x[d]) * u[0];
369*8214e71cSJoe }
370*8214e71cSJoe 
371*8214e71cSJoe static PetscErrorCode computeFEMMoments(DM dm, Vec u, PetscReal moments[3], AppCtx *user)
372*8214e71cSJoe {
373*8214e71cSJoe   PetscDS     prob;
374*8214e71cSJoe   PetscScalar mom;
375*8214e71cSJoe   PetscInt    field = 0;
376*8214e71cSJoe 
377*8214e71cSJoe   PetscFunctionBeginUser;
378*8214e71cSJoe   PetscCall(DMGetDS(dm, &prob));
379*8214e71cSJoe   PetscCall(PetscDSSetObjective(prob, field, &f0_1));
380*8214e71cSJoe   PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, user));
381*8214e71cSJoe   moments[0] = PetscRealPart(mom);
382*8214e71cSJoe   PetscCall(PetscDSSetObjective(prob, field, &f0_x));
383*8214e71cSJoe   PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, user));
384*8214e71cSJoe   moments[1] = PetscRealPart(mom);
385*8214e71cSJoe   PetscCall(PetscDSSetObjective(prob, field, &f0_r2));
386*8214e71cSJoe   PetscCall(DMPlexComputeIntegralFEM(dm, u, &mom, user));
387*8214e71cSJoe   moments[2] = PetscRealPart(mom);
388*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
389*8214e71cSJoe }
390*8214e71cSJoe 
391*8214e71cSJoe static PetscErrorCode MonitorEField(TS ts, PetscInt step, PetscReal t, Vec U, void *ctx)
392*8214e71cSJoe {
393*8214e71cSJoe   AppCtx    *user = (AppCtx *)ctx;
394*8214e71cSJoe   DM         dm, sw;
395*8214e71cSJoe   PetscReal *E;
396*8214e71cSJoe   PetscReal  Enorm = 0., lgEnorm, lgEmax, sum = 0., Emax = 0., temp = 0., *weight, chargesum = 0.;
397*8214e71cSJoe   PetscReal *x, *v;
398*8214e71cSJoe   PetscInt  *species, dim, p, d, Np, cStart, cEnd;
399*8214e71cSJoe   PetscReal  pmoments[3]; /* \int f, \int x f, \int r^2 f */
400*8214e71cSJoe   PetscReal  fmoments[3]; /* \int \hat f, \int x \hat f, \int r^2 \hat f */
401*8214e71cSJoe   Vec        rho;
402*8214e71cSJoe 
403*8214e71cSJoe   PetscFunctionBeginUser;
404*8214e71cSJoe   if (step < 0) PetscFunctionReturn(PETSC_SUCCESS);
405*8214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
406*8214e71cSJoe   PetscCall(DMSwarmGetCellDM(sw, &dm));
407*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
408*8214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
409*8214e71cSJoe   PetscCall(DMSwarmSortGetAccess(sw));
410*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
411*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "velocity", NULL, NULL, (void **)&v));
412*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "E_field", NULL, NULL, (void **)&E));
413*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **)&species));
414*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&weight));
415*8214e71cSJoe   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
416*8214e71cSJoe 
417*8214e71cSJoe   for (p = 0; p < Np; ++p) {
418*8214e71cSJoe     for (d = 0; d < 1; ++d) {
419*8214e71cSJoe       temp = PetscAbsReal(E[p * dim + d]);
420*8214e71cSJoe       if (temp > Emax) Emax = temp;
421*8214e71cSJoe     }
422*8214e71cSJoe     Enorm += PetscSqrtReal(E[p * dim] * E[p * dim]);
423*8214e71cSJoe     sum += E[p * dim];
424*8214e71cSJoe     chargesum += user->charges[0] * weight[p];
425*8214e71cSJoe   }
426*8214e71cSJoe   lgEnorm = Enorm != 0 ? PetscLog10Real(Enorm) : -16.;
427*8214e71cSJoe   lgEmax  = Emax != 0 ? PetscLog10Real(Emax) : -16.;
428*8214e71cSJoe 
429*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
430*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "velocity", NULL, NULL, (void **)&v));
431*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&weight));
432*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "E_field", NULL, NULL, (void **)&E));
433*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **)&species));
434*8214e71cSJoe 
435*8214e71cSJoe   Parameter *param;
436*8214e71cSJoe   PetscCall(PetscBagGetData(user->bag, (void **)&param));
437*8214e71cSJoe   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "charges", &rho));
438*8214e71cSJoe   if (user->em == EM_PRIMAL) {
439*8214e71cSJoe     PetscCall(computeParticleMoments(sw, pmoments, user));
440*8214e71cSJoe     PetscCall(computeFEMMoments(dm, rho, fmoments, user));
441*8214e71cSJoe   } else if (user->em == EM_MIXED) {
442*8214e71cSJoe     DM       potential_dm;
443*8214e71cSJoe     IS       potential_IS;
444*8214e71cSJoe     PetscInt fields = 1;
445*8214e71cSJoe     PetscCall(DMCreateSubDM(dm, 1, &fields, &potential_IS, &potential_dm));
446*8214e71cSJoe 
447*8214e71cSJoe     PetscCall(computeParticleMoments(sw, pmoments, user));
448*8214e71cSJoe     PetscCall(computeFEMMoments(potential_dm, rho, fmoments, user));
449*8214e71cSJoe     PetscCall(DMDestroy(&potential_dm));
450*8214e71cSJoe     PetscCall(ISDestroy(&potential_IS));
451*8214e71cSJoe   }
452*8214e71cSJoe   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "charges", &rho));
453*8214e71cSJoe 
454*8214e71cSJoe   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "%f\t%+e\t%e\t%f\t%f\t%f\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[2], (double)fmoments[0], (double)fmoments[1], (double)fmoments[2]));
455*8214e71cSJoe   PetscCall(PetscDrawLGAddPoint(user->drawlg_ef, &t, &lgEmax));
456*8214e71cSJoe   PetscCall(PetscDrawLGDraw(user->drawlg_ef));
457*8214e71cSJoe   PetscCall(PetscDrawSave(user->drawef));
458*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
459*8214e71cSJoe }
460*8214e71cSJoe 
461*8214e71cSJoe PetscErrorCode MonitorInitialConditions(TS ts, PetscInt step, PetscReal t, Vec U, void *ctx)
462*8214e71cSJoe {
463*8214e71cSJoe   AppCtx            *user = (AppCtx *)ctx;
464*8214e71cSJoe   DM                 dm, sw;
465*8214e71cSJoe   const PetscScalar *u;
466*8214e71cSJoe   PetscReal         *weight, *pos, *vel;
467*8214e71cSJoe   PetscInt           dim, p, Np, cStart, cEnd;
468*8214e71cSJoe 
469*8214e71cSJoe   PetscFunctionBegin;
470*8214e71cSJoe   if (step < 0) PetscFunctionReturn(PETSC_SUCCESS); /* -1 indicates interpolated solution */
471*8214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
472*8214e71cSJoe   PetscCall(DMSwarmGetCellDM(sw, &dm));
473*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
474*8214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
475*8214e71cSJoe   PetscCall(DMSwarmSortGetAccess(sw));
476*8214e71cSJoe   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
477*8214e71cSJoe 
478*8214e71cSJoe   if (step == 0) {
479*8214e71cSJoe     PetscCall(PetscDrawHGReset(user->drawhgic_x));
480*8214e71cSJoe     PetscCall(PetscDrawHGGetDraw(user->drawhgic_x, &user->drawic_x));
481*8214e71cSJoe     PetscCall(PetscDrawClear(user->drawic_x));
482*8214e71cSJoe     PetscCall(PetscDrawFlush(user->drawic_x));
483*8214e71cSJoe 
484*8214e71cSJoe     PetscCall(PetscDrawHGReset(user->drawhgic_v));
485*8214e71cSJoe     PetscCall(PetscDrawHGGetDraw(user->drawhgic_v, &user->drawic_v));
486*8214e71cSJoe     PetscCall(PetscDrawClear(user->drawic_v));
487*8214e71cSJoe     PetscCall(PetscDrawFlush(user->drawic_v));
488*8214e71cSJoe 
489*8214e71cSJoe     PetscCall(PetscDrawHGReset(user->drawhgic_w));
490*8214e71cSJoe     PetscCall(PetscDrawHGGetDraw(user->drawhgic_w, &user->drawic_w));
491*8214e71cSJoe     PetscCall(PetscDrawClear(user->drawic_w));
492*8214e71cSJoe     PetscCall(PetscDrawFlush(user->drawic_w));
493*8214e71cSJoe 
494*8214e71cSJoe     PetscCall(VecGetArrayRead(U, &u));
495*8214e71cSJoe     PetscCall(DMSwarmGetField(sw, "velocity", NULL, NULL, (void **)&vel));
496*8214e71cSJoe     PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&weight));
497*8214e71cSJoe     PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&pos));
498*8214e71cSJoe 
499*8214e71cSJoe     PetscCall(VecGetLocalSize(U, &Np));
500*8214e71cSJoe     Np /= dim * 2;
501*8214e71cSJoe     for (p = 0; p < Np; ++p) {
502*8214e71cSJoe       PetscCall(PetscDrawHGAddValue(user->drawhgic_x, pos[p * dim]));
503*8214e71cSJoe       PetscCall(PetscDrawHGAddValue(user->drawhgic_v, vel[p * dim]));
504*8214e71cSJoe       PetscCall(PetscDrawHGAddValue(user->drawhgic_w, weight[p]));
505*8214e71cSJoe     }
506*8214e71cSJoe 
507*8214e71cSJoe     PetscCall(VecRestoreArrayRead(U, &u));
508*8214e71cSJoe     PetscCall(PetscDrawHGDraw(user->drawhgic_x));
509*8214e71cSJoe     PetscCall(PetscDrawHGSave(user->drawhgic_x));
510*8214e71cSJoe 
511*8214e71cSJoe     PetscCall(PetscDrawHGDraw(user->drawhgic_v));
512*8214e71cSJoe     PetscCall(PetscDrawHGSave(user->drawhgic_v));
513*8214e71cSJoe 
514*8214e71cSJoe     PetscCall(PetscDrawHGDraw(user->drawhgic_w));
515*8214e71cSJoe     PetscCall(PetscDrawHGSave(user->drawhgic_w));
516*8214e71cSJoe 
517*8214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&pos));
518*8214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, "velocity", NULL, NULL, (void **)&vel));
519*8214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&weight));
520*8214e71cSJoe   }
521*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
522*8214e71cSJoe }
523*8214e71cSJoe 
524*8214e71cSJoe static PetscErrorCode MonitorPositions_2D(TS ts, PetscInt step, PetscReal t, Vec U, void *ctx)
525*8214e71cSJoe {
526*8214e71cSJoe   AppCtx         *user = (AppCtx *)ctx;
527*8214e71cSJoe   DM              dm, sw;
528*8214e71cSJoe   PetscScalar    *x, *v, *weight;
529*8214e71cSJoe   PetscReal       lower[3], upper[3], speed;
530*8214e71cSJoe   const PetscInt *s;
531*8214e71cSJoe   PetscInt        dim, cStart, cEnd, c;
532*8214e71cSJoe 
533*8214e71cSJoe   PetscFunctionBeginUser;
534*8214e71cSJoe   if (step > 0 && step % user->ostep == 0) {
535*8214e71cSJoe     PetscCall(TSGetDM(ts, &sw));
536*8214e71cSJoe     PetscCall(DMSwarmGetCellDM(sw, &dm));
537*8214e71cSJoe     PetscCall(DMGetDimension(dm, &dim));
538*8214e71cSJoe     PetscCall(DMGetBoundingBox(dm, lower, upper));
539*8214e71cSJoe     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
540*8214e71cSJoe     PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
541*8214e71cSJoe     PetscCall(DMSwarmGetField(sw, "velocity", NULL, NULL, (void **)&v));
542*8214e71cSJoe     PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&weight));
543*8214e71cSJoe     PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **)&s));
544*8214e71cSJoe     PetscCall(DMSwarmSortGetAccess(sw));
545*8214e71cSJoe     PetscCall(PetscDrawSPReset(user->positionDrawSP));
546*8214e71cSJoe     PetscCall(PetscDrawSPSetLimits(user->positionDrawSP, lower[0], upper[0], lower[1], upper[1]));
547*8214e71cSJoe     PetscCall(PetscDrawSPSetLimits(user->positionDrawSP, lower[0], upper[0], -12, 12));
548*8214e71cSJoe     for (c = 0; c < cEnd - cStart; ++c) {
549*8214e71cSJoe       PetscInt *pidx, Npc, q;
550*8214e71cSJoe       PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Npc, &pidx));
551*8214e71cSJoe       for (q = 0; q < Npc; ++q) {
552*8214e71cSJoe         const PetscInt p = pidx[q];
553*8214e71cSJoe         if (s[p] == 0) {
554*8214e71cSJoe           speed = PetscSqrtReal(PetscSqr(v[p * dim]) + PetscSqr(v[p * dim + 1]));
555*8214e71cSJoe           if (dim == 1 || user->fake_1D) {
556*8214e71cSJoe             PetscCall(PetscDrawSPAddPointColorized(user->positionDrawSP, &x[p * dim], &v[p * dim], &speed));
557*8214e71cSJoe           } else {
558*8214e71cSJoe             PetscCall(PetscDrawSPAddPointColorized(user->positionDrawSP, &x[p * dim], &x[p * dim + 1], &speed));
559*8214e71cSJoe           }
560*8214e71cSJoe         } else if (s[p] == 1) {
561*8214e71cSJoe           PetscCall(PetscDrawSPAddPoint(user->positionDrawSP, &x[p * dim], &v[p * dim]));
562*8214e71cSJoe         }
563*8214e71cSJoe       }
564*8214e71cSJoe       PetscCall(PetscFree(pidx));
565*8214e71cSJoe     }
566*8214e71cSJoe     PetscCall(PetscDrawSPDraw(user->positionDrawSP, PETSC_TRUE));
567*8214e71cSJoe     PetscCall(PetscDrawSave(user->positionDraw));
568*8214e71cSJoe     PetscCall(DMSwarmSortRestoreAccess(sw));
569*8214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
570*8214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&weight));
571*8214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, "velocity", NULL, NULL, (void **)&v));
572*8214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **)&s));
573*8214e71cSJoe   }
574*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
575*8214e71cSJoe }
576*8214e71cSJoe 
577*8214e71cSJoe static PetscErrorCode MonitorPoisson(TS ts, PetscInt step, PetscReal t, Vec U, void *ctx)
578*8214e71cSJoe {
579*8214e71cSJoe   AppCtx      *user = (AppCtx *)ctx;
580*8214e71cSJoe   DM           dm, sw;
581*8214e71cSJoe   PetscScalar *x, *E, *weight, *pot, *charges;
582*8214e71cSJoe   PetscReal    lower[3], upper[3], xval;
583*8214e71cSJoe   PetscInt     dim, cStart, cEnd, c;
584*8214e71cSJoe 
585*8214e71cSJoe   PetscFunctionBeginUser;
586*8214e71cSJoe   if (step > 0 && step % user->ostep == 0) {
587*8214e71cSJoe     PetscCall(TSGetDM(ts, &sw));
588*8214e71cSJoe     PetscCall(DMSwarmGetCellDM(sw, &dm));
589*8214e71cSJoe     PetscCall(DMGetDimension(dm, &dim));
590*8214e71cSJoe     PetscCall(DMGetBoundingBox(dm, lower, upper));
591*8214e71cSJoe     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
592*8214e71cSJoe 
593*8214e71cSJoe     PetscCall(PetscDrawSPReset(user->RhoDrawSP));
594*8214e71cSJoe     PetscCall(PetscDrawSPReset(user->EDrawSP));
595*8214e71cSJoe     PetscCall(PetscDrawSPReset(user->PotDrawSP));
596*8214e71cSJoe     PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
597*8214e71cSJoe     PetscCall(DMSwarmGetField(sw, "E_field", NULL, NULL, (void **)&E));
598*8214e71cSJoe     PetscCall(DMSwarmGetField(sw, "potential", NULL, NULL, (void **)&pot));
599*8214e71cSJoe     PetscCall(DMSwarmGetField(sw, "charges", NULL, NULL, (void **)&charges));
600*8214e71cSJoe     PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&weight));
601*8214e71cSJoe 
602*8214e71cSJoe     PetscCall(DMSwarmSortGetAccess(sw));
603*8214e71cSJoe     for (c = 0; c < cEnd - cStart; ++c) {
604*8214e71cSJoe       PetscReal Esum = 0.0;
605*8214e71cSJoe       PetscInt *pidx, Npc, q;
606*8214e71cSJoe       PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Npc, &pidx));
607*8214e71cSJoe       for (q = 0; q < Npc; ++q) {
608*8214e71cSJoe         const PetscInt p = pidx[q];
609*8214e71cSJoe         Esum += E[p * dim];
610*8214e71cSJoe       }
611*8214e71cSJoe       xval = (c + 0.5) * ((upper[0] - lower[0]) / (cEnd - cStart));
612*8214e71cSJoe       PetscCall(PetscDrawSPAddPoint(user->EDrawSP, &xval, &Esum));
613*8214e71cSJoe       PetscCall(PetscFree(pidx));
614*8214e71cSJoe     }
615*8214e71cSJoe     for (c = 0; c < (cEnd - cStart); ++c) {
616*8214e71cSJoe       xval = (c + 0.5) * ((upper[0] - lower[0]) / (cEnd - cStart));
617*8214e71cSJoe       PetscCall(PetscDrawSPAddPoint(user->RhoDrawSP, &xval, &charges[c]));
618*8214e71cSJoe       PetscCall(PetscDrawSPAddPoint(user->PotDrawSP, &xval, &pot[c]));
619*8214e71cSJoe     }
620*8214e71cSJoe     PetscCall(PetscDrawSPDraw(user->RhoDrawSP, PETSC_TRUE));
621*8214e71cSJoe     PetscCall(PetscDrawSave(user->RhoDraw));
622*8214e71cSJoe     PetscCall(PetscDrawSPDraw(user->EDrawSP, PETSC_TRUE));
623*8214e71cSJoe     PetscCall(PetscDrawSave(user->EDraw));
624*8214e71cSJoe     PetscCall(PetscDrawSPDraw(user->PotDrawSP, PETSC_TRUE));
625*8214e71cSJoe     PetscCall(PetscDrawSave(user->PotDraw));
626*8214e71cSJoe     PetscCall(DMSwarmSortRestoreAccess(sw));
627*8214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
628*8214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, "potential", NULL, NULL, (void **)&pot));
629*8214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, "charges", NULL, NULL, (void **)&charges));
630*8214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&weight));
631*8214e71cSJoe     PetscCall(DMSwarmRestoreField(sw, "E_field", NULL, NULL, (void **)&E));
632*8214e71cSJoe   }
633*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
634*8214e71cSJoe }
635*8214e71cSJoe 
636*8214e71cSJoe static PetscErrorCode SetupParameters(MPI_Comm comm, AppCtx *ctx)
637*8214e71cSJoe {
638*8214e71cSJoe   PetscBag   bag;
639*8214e71cSJoe   Parameter *p;
640*8214e71cSJoe 
641*8214e71cSJoe   PetscFunctionBeginUser;
642*8214e71cSJoe   /* setup PETSc parameter bag */
643*8214e71cSJoe   PetscCall(PetscBagGetData(ctx->bag, (void **)&p));
644*8214e71cSJoe   PetscCall(PetscBagSetName(ctx->bag, "par", "Vlasov-Poisson Parameters"));
645*8214e71cSJoe   bag = ctx->bag;
646*8214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->v0, 1.0, "v0", "Velocity scale, m/s"));
647*8214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->t0, 1.0, "t0", "Time scale, s"));
648*8214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->x0, 1.0, "x0", "Space scale, m"));
649*8214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->v0, 1.0, "phi0", "Potential scale, kg*m^2/A*s^3"));
650*8214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->q0, 1.0, "q0", "Charge Scale, A*s"));
651*8214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->m0, 1.0, "m0", "Mass Scale, kg"));
652*8214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->epsi0, 1.0, "epsi0", "Permittivity of Free Space, kg"));
653*8214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->kb, 1.0, "kb", "Boltzmann Constant, m^2 kg/s^2 K^1"));
654*8214e71cSJoe 
655*8214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->sigma, 1.0, "sigma", "Charge per unit area, C/m^3"));
656*8214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->poissonNumber, 1.0, "poissonNumber", "Non-Dimensional Poisson Number"));
657*8214e71cSJoe   PetscCall(PetscBagRegisterScalar(bag, &p->vlasovNumber, 1.0, "vlasovNumber", "Non-Dimensional Vlasov Number"));
658*8214e71cSJoe   PetscCall(PetscBagSetFromOptions(bag));
659*8214e71cSJoe   {
660*8214e71cSJoe     PetscViewer       viewer;
661*8214e71cSJoe     PetscViewerFormat format;
662*8214e71cSJoe     PetscBool         flg;
663*8214e71cSJoe 
664*8214e71cSJoe     PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-param_view", &viewer, &format, &flg));
665*8214e71cSJoe     if (flg) {
666*8214e71cSJoe       PetscCall(PetscViewerPushFormat(viewer, format));
667*8214e71cSJoe       PetscCall(PetscBagView(bag, viewer));
668*8214e71cSJoe       PetscCall(PetscViewerFlush(viewer));
669*8214e71cSJoe       PetscCall(PetscViewerPopFormat(viewer));
670*8214e71cSJoe       PetscCall(PetscViewerDestroy(&viewer));
671*8214e71cSJoe     }
672*8214e71cSJoe   }
673*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
674*8214e71cSJoe }
675*8214e71cSJoe 
676*8214e71cSJoe static PetscErrorCode CreateMesh(MPI_Comm comm, AppCtx *user, DM *dm)
677d71ae5a4SJacob Faibussowitsch {
678b80bf5b1SJoe Pusztay   PetscFunctionBeginUser;
6799566063dSJacob Faibussowitsch   PetscCall(DMCreate(comm, dm));
6809566063dSJacob Faibussowitsch   PetscCall(DMSetType(*dm, DMPLEX));
6819566063dSJacob Faibussowitsch   PetscCall(DMSetFromOptions(*dm));
6829566063dSJacob Faibussowitsch   PetscCall(DMViewFromOptions(*dm, NULL, "-dm_view"));
6833ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
684b80bf5b1SJoe Pusztay }
685b80bf5b1SJoe Pusztay 
686*8214e71cSJoe 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[])
687*8214e71cSJoe {
688*8214e71cSJoe   f0[0] = -constants[SIGMA];
689*8214e71cSJoe }
690*8214e71cSJoe 
691d71ae5a4SJacob 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[])
692d71ae5a4SJacob Faibussowitsch {
693b80bf5b1SJoe Pusztay   PetscInt d;
694ad540459SPierre Jolivet   for (d = 0; d < dim; ++d) f1[d] = u_x[d];
695b80bf5b1SJoe Pusztay }
696b80bf5b1SJoe Pusztay 
697*8214e71cSJoe 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[])
698d71ae5a4SJacob Faibussowitsch {
699b80bf5b1SJoe Pusztay   PetscInt d;
700ad540459SPierre Jolivet   for (d = 0; d < dim; ++d) g3[d * dim + d] = 1.0;
701b80bf5b1SJoe Pusztay }
702b80bf5b1SJoe Pusztay 
703*8214e71cSJoe static PetscErrorCode zero(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx)
704d71ae5a4SJacob Faibussowitsch {
705*8214e71cSJoe   *u = 0.0;
706*8214e71cSJoe   return PETSC_SUCCESS;
707b80bf5b1SJoe Pusztay }
708b80bf5b1SJoe Pusztay 
709b80bf5b1SJoe Pusztay /*
710*8214e71cSJoe    /  I   -grad\ / q \ = /0\
711*8214e71cSJoe    \-div    0  / \phi/   \f/
712b80bf5b1SJoe Pusztay */
713*8214e71cSJoe 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[])
714d71ae5a4SJacob Faibussowitsch {
715*8214e71cSJoe   for (PetscInt d = 0; d < dim; ++d) f0[d] += u[uOff[0] + d];
716*8214e71cSJoe }
717*8214e71cSJoe 
718*8214e71cSJoe 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[])
719*8214e71cSJoe {
720*8214e71cSJoe   for (PetscInt d = 0; d < dim; ++d) f1[d * dim + d] = u[uOff[1]];
721*8214e71cSJoe }
722*8214e71cSJoe 
723*8214e71cSJoe 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[])
724*8214e71cSJoe {
725*8214e71cSJoe   f0[0] += constants[SIGMA];
726*8214e71cSJoe   for (PetscInt d = 0; d < dim; ++d) f0[0] += u_x[uOff_x[0] + d * dim + d];
727*8214e71cSJoe }
728*8214e71cSJoe 
729*8214e71cSJoe /* Boundary residual. Dirichlet boundary for u means u_bdy=p*n */
730*8214e71cSJoe 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[])
731*8214e71cSJoe {
732*8214e71cSJoe   for (PetscInt d = 0; d < dim; ++d) g0[d * dim + d] = 1.0;
733*8214e71cSJoe }
734*8214e71cSJoe 
735*8214e71cSJoe 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[])
736*8214e71cSJoe {
737*8214e71cSJoe   for (PetscInt d = 0; d < dim; ++d) g2[d * dim + d] = 1.0;
738*8214e71cSJoe }
739*8214e71cSJoe 
740*8214e71cSJoe 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[])
741*8214e71cSJoe {
742*8214e71cSJoe   for (PetscInt d = 0; d < dim; ++d) g1[d * dim + d] = 1.0;
743*8214e71cSJoe }
744*8214e71cSJoe 
745*8214e71cSJoe static PetscErrorCode CreateFEM(DM dm, AppCtx *user)
746*8214e71cSJoe {
747*8214e71cSJoe   PetscFE   fephi, feq;
748*8214e71cSJoe   PetscDS   ds;
749*8214e71cSJoe   PetscBool simplex;
750*8214e71cSJoe   PetscInt  dim;
751*8214e71cSJoe 
752*8214e71cSJoe   PetscFunctionBeginUser;
753*8214e71cSJoe   PetscCall(DMGetDimension(dm, &dim));
754*8214e71cSJoe   PetscCall(DMPlexIsSimplex(dm, &simplex));
755*8214e71cSJoe   if (user->em == EM_MIXED) {
756*8214e71cSJoe     DMLabel        label;
757*8214e71cSJoe     const PetscInt id = 1;
758*8214e71cSJoe 
759*8214e71cSJoe     PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, dim, simplex, "field_", PETSC_DETERMINE, &feq));
760*8214e71cSJoe     PetscCall(PetscObjectSetName((PetscObject)feq, "field"));
761*8214e71cSJoe     PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "potential_", PETSC_DETERMINE, &fephi));
762*8214e71cSJoe     PetscCall(PetscObjectSetName((PetscObject)fephi, "potential"));
763*8214e71cSJoe     PetscCall(PetscFECopyQuadrature(feq, fephi));
764*8214e71cSJoe     PetscCall(DMSetField(dm, 0, NULL, (PetscObject)feq));
765*8214e71cSJoe     PetscCall(DMSetField(dm, 1, NULL, (PetscObject)fephi));
766*8214e71cSJoe     PetscCall(DMCreateDS(dm));
767*8214e71cSJoe     PetscCall(PetscFEDestroy(&fephi));
768*8214e71cSJoe     PetscCall(PetscFEDestroy(&feq));
769*8214e71cSJoe 
770*8214e71cSJoe     PetscCall(DMGetLabel(dm, "marker", &label));
771*8214e71cSJoe     PetscCall(DMGetDS(dm, &ds));
772*8214e71cSJoe 
773*8214e71cSJoe     PetscCall(PetscDSSetResidual(ds, 0, f0_q, f1_q));
774*8214e71cSJoe     PetscCall(PetscDSSetResidual(ds, 1, f0_phi_backgroundCharge, NULL));
775*8214e71cSJoe     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_qq, NULL, NULL, NULL));
776*8214e71cSJoe     PetscCall(PetscDSSetJacobian(ds, 0, 1, NULL, NULL, g2_qphi, NULL));
777*8214e71cSJoe     PetscCall(PetscDSSetJacobian(ds, 1, 0, NULL, g1_phiq, NULL, NULL));
778*8214e71cSJoe 
779*8214e71cSJoe     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "wall", label, 1, &id, 0, 0, NULL, (void (*)(void))zero, NULL, NULL, NULL));
780*8214e71cSJoe 
781*8214e71cSJoe   } else if (user->em == EM_PRIMAL) {
782*8214e71cSJoe     MatNullSpace nullsp;
783*8214e71cSJoe     PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, NULL, PETSC_DETERMINE, &fephi));
784*8214e71cSJoe     PetscCall(PetscObjectSetName((PetscObject)fephi, "potential"));
785*8214e71cSJoe     PetscCall(DMSetField(dm, 0, NULL, (PetscObject)fephi));
786*8214e71cSJoe     PetscCall(DMCreateDS(dm));
787*8214e71cSJoe     PetscCall(DMGetDS(dm, &ds));
788*8214e71cSJoe     PetscCall(PetscDSSetResidual(ds, 0, ion_f0, laplacian_f1));
789*8214e71cSJoe     PetscCall(PetscDSSetJacobian(ds, 0, 0, NULL, NULL, NULL, laplacian_g3));
790*8214e71cSJoe     PetscCall(MatNullSpaceCreate(PetscObjectComm((PetscObject)dm), PETSC_TRUE, 0, NULL, &nullsp));
791*8214e71cSJoe     PetscCall(PetscObjectCompose((PetscObject)fephi, "nullspace", (PetscObject)nullsp));
792*8214e71cSJoe     PetscCall(MatNullSpaceDestroy(&nullsp));
793*8214e71cSJoe     PetscCall(PetscFEDestroy(&fephi));
794*8214e71cSJoe   }
795*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
796*8214e71cSJoe }
797*8214e71cSJoe 
798*8214e71cSJoe static PetscErrorCode CreatePoisson(DM dm, AppCtx *user)
799*8214e71cSJoe {
800*8214e71cSJoe   SNES         snes;
801*8214e71cSJoe   Mat          J;
802*8214e71cSJoe   MatNullSpace nullSpace;
803*8214e71cSJoe 
804*8214e71cSJoe   PetscFunctionBeginUser;
805*8214e71cSJoe   PetscCall(CreateFEM(dm, user));
806*8214e71cSJoe   PetscCall(SNESCreate(PetscObjectComm((PetscObject)dm), &snes));
807*8214e71cSJoe   PetscCall(SNESSetOptionsPrefix(snes, "em_"));
808*8214e71cSJoe   PetscCall(SNESSetDM(snes, dm));
809*8214e71cSJoe   PetscCall(DMPlexSetSNESLocalFEM(dm, PETSC_FALSE, user));
810*8214e71cSJoe   PetscCall(SNESSetFromOptions(snes));
811*8214e71cSJoe 
812*8214e71cSJoe   PetscCall(DMCreateMatrix(dm, &J));
813*8214e71cSJoe   PetscCall(MatNullSpaceCreate(PetscObjectComm((PetscObject)dm), PETSC_TRUE, 0, NULL, &nullSpace));
814*8214e71cSJoe   PetscCall(MatSetNullSpace(J, nullSpace));
815*8214e71cSJoe   PetscCall(MatNullSpaceDestroy(&nullSpace));
816*8214e71cSJoe   PetscCall(SNESSetJacobian(snes, J, J, NULL, NULL));
817*8214e71cSJoe   PetscCall(MatDestroy(&J));
818*8214e71cSJoe   user->snes = snes;
819*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
820*8214e71cSJoe }
821*8214e71cSJoe 
822*8214e71cSJoe PetscErrorCode PetscPDFPertubedConstant2D(const PetscReal x[], const PetscReal dummy[], PetscReal p[])
823*8214e71cSJoe {
824*8214e71cSJoe   p[0] = (1 + 0.01 * PetscCosReal(0.5 * x[0])) / (2 * PETSC_PI);
825*8214e71cSJoe   p[1] = (1 + 0.01 * PetscCosReal(0.5 * x[1])) / (2 * PETSC_PI);
826*8214e71cSJoe   return PETSC_SUCCESS;
827*8214e71cSJoe }
828*8214e71cSJoe PetscErrorCode PetscPDFPertubedConstant1D(const PetscReal x[], const PetscReal dummy[], PetscReal p[])
829*8214e71cSJoe {
830*8214e71cSJoe   p[0] = (1. + 0.01 * PetscCosReal(0.5 * x[0])) / (2 * PETSC_PI);
831*8214e71cSJoe   return PETSC_SUCCESS;
832*8214e71cSJoe }
833*8214e71cSJoe 
834*8214e71cSJoe PetscErrorCode PetscPDFCosine1D(const PetscReal x[], const PetscReal scale[], PetscReal p[])
835*8214e71cSJoe {
836*8214e71cSJoe   const PetscReal alpha = scale ? scale[0] : 0.0;
837*8214e71cSJoe   const PetscReal k     = scale ? scale[1] : 1.;
838*8214e71cSJoe   p[0]                  = (1 + alpha * PetscCosReal(k * x[0]));
839*8214e71cSJoe   return PETSC_SUCCESS;
840*8214e71cSJoe }
841*8214e71cSJoe 
842*8214e71cSJoe PetscErrorCode PetscPDFCosine2D(const PetscReal x[], const PetscReal scale[], PetscReal p[])
843*8214e71cSJoe {
844*8214e71cSJoe   const PetscReal alpha = scale ? scale[0] : 0.;
845*8214e71cSJoe   const PetscReal k     = scale ? scale[0] : 1.;
846*8214e71cSJoe   p[0]                  = (1 + alpha * PetscCosReal(k * (x[0] + x[1])));
847*8214e71cSJoe   return PETSC_SUCCESS;
848*8214e71cSJoe }
849*8214e71cSJoe 
850*8214e71cSJoe PetscErrorCode PetscPDFCosine1D_TwoStream(const PetscReal x[], const PetscReal scale[], PetscReal p[])
851*8214e71cSJoe {
852*8214e71cSJoe   const PetscReal alpha = scale ? scale[0] : 0.0;
853*8214e71cSJoe   const PetscReal k     = scale ? scale[1] : 1.;
854*8214e71cSJoe   p[0]                  = (1. + alpha * PetscCosReal(k * x[0]));
855*8214e71cSJoe   return PETSC_SUCCESS;
856*8214e71cSJoe }
857*8214e71cSJoe 
858*8214e71cSJoe static PetscErrorCode InitializeParticles_PerturbedWeights(DM sw, AppCtx *user)
859*8214e71cSJoe {
860*8214e71cSJoe   DM           vdm, dm;
861*8214e71cSJoe   PetscScalar *weight;
862*8214e71cSJoe   PetscReal   *x, *v, vmin[3], vmax[3], gmin[3], gmax[3], xi0[3];
863*8214e71cSJoe   PetscInt    *N, Ns, dim, *cellid, *species, Np, cStart, cEnd, Npc, n;
864*8214e71cSJoe   PetscInt     Np_global, p, q, s, c, d, cv;
865*8214e71cSJoe   PetscBool    flg;
866*8214e71cSJoe   PetscMPIInt  size, rank;
867*8214e71cSJoe   Parameter   *param;
868*8214e71cSJoe 
869*8214e71cSJoe   PetscFunctionBegin;
870*8214e71cSJoe   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)sw), &size));
871*8214e71cSJoe   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)sw), &rank));
872*8214e71cSJoe   PetscOptionsBegin(PetscObjectComm((PetscObject)sw), "", "DMSwarm Options", "DMSWARM");
873*8214e71cSJoe   PetscCall(DMSwarmGetNumSpecies(sw, &Ns));
874*8214e71cSJoe   PetscCall(PetscOptionsInt("-dm_swarm_num_species", "The number of species", "DMSwarmSetNumSpecies", Ns, &Ns, &flg));
875*8214e71cSJoe   if (flg) PetscCall(DMSwarmSetNumSpecies(sw, Ns));
876*8214e71cSJoe   PetscCall(PetscCalloc1(Ns, &N));
877*8214e71cSJoe   n = Ns;
878*8214e71cSJoe   PetscCall(PetscOptionsIntArray("-dm_swarm_num_particles", "The target number of particles", "", N, &n, NULL));
879*8214e71cSJoe   PetscOptionsEnd();
880*8214e71cSJoe 
881*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
882*8214e71cSJoe   PetscCall(DMSwarmGetCellDM(sw, &dm));
883*8214e71cSJoe   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
884*8214e71cSJoe 
885*8214e71cSJoe   PetscCall(DMCreate(PETSC_COMM_SELF, &vdm));
886*8214e71cSJoe   PetscCall(DMSetType(vdm, DMPLEX));
887*8214e71cSJoe   PetscCall(DMPlexSetOptionsPrefix(vdm, "v"));
888*8214e71cSJoe   PetscCall(DMSetFromOptions(vdm));
889*8214e71cSJoe   PetscCall(DMViewFromOptions(vdm, NULL, "-vdm_view"));
890*8214e71cSJoe 
891*8214e71cSJoe   PetscInt vStart, vEnd;
892*8214e71cSJoe   PetscCall(DMPlexGetHeightStratum(vdm, 0, &vStart, &vEnd));
893*8214e71cSJoe   PetscCall(DMGetBoundingBox(vdm, vmin, vmax));
894*8214e71cSJoe 
895*8214e71cSJoe   PetscCall(DMGetBoundingBox(dm, gmin, gmax));
896*8214e71cSJoe   PetscCall(PetscBagGetData(user->bag, (void **)&param));
897*8214e71cSJoe   Np = (cEnd - cStart) * (vEnd - vStart);
898*8214e71cSJoe   PetscCall(MPIU_Allreduce(&Np, &Np_global, 1, MPIU_INT, MPIU_SUM, PETSC_COMM_WORLD));
899*8214e71cSJoe   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Global Np = %" PetscInt_FMT "\n", Np_global));
900*8214e71cSJoe   PetscCall(DMSwarmSetLocalSizes(sw, Np, 0));
901*8214e71cSJoe   Npc = Np / (cEnd - cStart);
902*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, DMSwarmPICField_cellid, NULL, NULL, (void **)&cellid));
903*8214e71cSJoe   for (c = 0, p = 0; c < cEnd - cStart; ++c) {
904*8214e71cSJoe     for (s = 0; s < Ns; ++s) {
905*8214e71cSJoe       for (q = 0; q < Npc; ++q, ++p) cellid[p] = c;
906*8214e71cSJoe     }
907*8214e71cSJoe   }
908*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_cellid, NULL, NULL, (void **)&cellid));
909*8214e71cSJoe   PetscCall(PetscFree(N));
910*8214e71cSJoe 
911*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
912*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "velocity", NULL, NULL, (void **)&v));
913*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&weight));
914*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **)&species));
915*8214e71cSJoe 
916*8214e71cSJoe   PetscCall(DMSwarmSortGetAccess(sw));
917*8214e71cSJoe   for (c = 0; c < cEnd - cStart; ++c) {
918*8214e71cSJoe     const PetscInt cell = c + cStart;
919*8214e71cSJoe     PetscInt      *pidx, Npc;
920*8214e71cSJoe     PetscReal      centroid[3], volume;
921*8214e71cSJoe 
922*8214e71cSJoe     PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Npc, &pidx));
923*8214e71cSJoe     PetscCall(DMPlexComputeCellGeometryFVM(dm, cell, &volume, centroid, NULL));
924*8214e71cSJoe     for (q = 0; q < Npc; ++q) {
925*8214e71cSJoe       const PetscInt p = pidx[q];
926*8214e71cSJoe 
927*8214e71cSJoe       for (d = 0; d < dim; ++d) {
928*8214e71cSJoe         x[p * dim + d] = centroid[d];
929*8214e71cSJoe         v[p * dim + d] = vmin[0] + (q + 0.5) * (vmax[0] - vmin[0]) / Npc;
930*8214e71cSJoe         if (user->fake_1D && d > 0) v[p * dim + d] = 0;
931*8214e71cSJoe       }
932*8214e71cSJoe     }
933*8214e71cSJoe     PetscCall(PetscFree(pidx));
934*8214e71cSJoe   }
935*8214e71cSJoe   PetscCall(DMGetCoordinatesLocalSetUp(vdm));
936*8214e71cSJoe 
937*8214e71cSJoe   /* Setup Quadrature for spatial and velocity weight calculations*/
938*8214e71cSJoe   PetscQuadrature  quad_x;
939*8214e71cSJoe   PetscInt         Nq_x;
940*8214e71cSJoe   const PetscReal *wq_x, *xq_x;
941*8214e71cSJoe   PetscReal       *xq_x_extended;
942*8214e71cSJoe   PetscReal        weightsum = 0., totalcellweight = 0., *weight_x, *weight_v;
943*8214e71cSJoe   PetscReal        scale[2] = {user->cosine_coefficients[0], user->cosine_coefficients[1]};
944*8214e71cSJoe 
945*8214e71cSJoe   PetscCall(PetscCalloc2(cEnd - cStart, &weight_x, Np, &weight_v));
946*8214e71cSJoe   if (user->fake_1D) PetscCall(PetscDTGaussTensorQuadrature(1, 1, 5, -1.0, 1.0, &quad_x));
947*8214e71cSJoe   else PetscCall(PetscDTGaussTensorQuadrature(dim, 1, 5, -1.0, 1.0, &quad_x));
948*8214e71cSJoe   PetscCall(PetscQuadratureGetData(quad_x, NULL, NULL, &Nq_x, &xq_x, &wq_x));
949*8214e71cSJoe   if (user->fake_1D) {
950*8214e71cSJoe     PetscCall(PetscCalloc1(Nq_x * dim, &xq_x_extended));
951*8214e71cSJoe     for (PetscInt i = 0; i < Nq_x; ++i) xq_x_extended[i * dim] = xq_x[i];
952*8214e71cSJoe   }
953*8214e71cSJoe   /* Integrate the density function to get the weights of particles in each cell */
954*8214e71cSJoe   for (d = 0; d < dim; ++d) xi0[d] = -1.0;
955*8214e71cSJoe   for (c = cStart; c < cEnd; ++c) {
956*8214e71cSJoe     PetscReal          v0_x[3], J_x[9], invJ_x[9], detJ_x, xr_x[3], den_x;
957*8214e71cSJoe     PetscInt          *pidx, Npc, q;
958*8214e71cSJoe     PetscInt           Ncx;
959*8214e71cSJoe     const PetscScalar *array_x;
960*8214e71cSJoe     PetscScalar       *coords_x = NULL;
961*8214e71cSJoe     PetscBool          isDGx;
962*8214e71cSJoe     weight_x[c] = 0.;
963*8214e71cSJoe 
964*8214e71cSJoe     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDGx, &Ncx, &array_x, &coords_x));
965*8214e71cSJoe     PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Npc, &pidx));
966*8214e71cSJoe     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, v0_x, J_x, invJ_x, &detJ_x));
967*8214e71cSJoe     for (q = 0; q < Nq_x; ++q) {
968*8214e71cSJoe       /*Transform quadrature points from ref space to real space (0,12.5664)*/
969*8214e71cSJoe       if (user->fake_1D) CoordinatesRefToReal(dim, dim, xi0, v0_x, J_x, &xq_x_extended[q * dim], xr_x);
970*8214e71cSJoe       else CoordinatesRefToReal(dim, dim, xi0, v0_x, J_x, &xq_x[q * dim], xr_x);
971*8214e71cSJoe 
972*8214e71cSJoe       /*Transform quadrature points from real space to ideal real space (0, 2PI/k)*/
973*8214e71cSJoe       if (user->fake_1D) {
974*8214e71cSJoe         if (user->twostream) PetscCall(PetscPDFCosine1D_TwoStream(xr_x, scale, &den_x));
975*8214e71cSJoe         else PetscCall(PetscPDFCosine1D(xr_x, scale, &den_x));
976*8214e71cSJoe         detJ_x = J_x[0];
977*8214e71cSJoe       } else PetscCall(PetscPDFCosine2D(xr_x, scale, &den_x));
978*8214e71cSJoe       /*We have to transform the quadrature weights as well*/
979*8214e71cSJoe       weight_x[c] += den_x * (wq_x[q] * detJ_x);
980*8214e71cSJoe     }
981*8214e71cSJoe     // Get the cell numbering for consistent output between sequential and distributed tests
982*8214e71cSJoe     IS              globalOrdering;
983*8214e71cSJoe     const PetscInt *ordering;
984*8214e71cSJoe     PetscCall(DMPlexGetCellNumbering(dm, &globalOrdering));
985*8214e71cSJoe     PetscCall(ISGetIndices(globalOrdering, &ordering));
986*8214e71cSJoe     PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "c:%" PetscInt_FMT " [x_a,x_b] = %1.15f,%1.15f -> cell weight = %1.15f\n", ordering[c], (double)PetscRealPart(coords_x[0]), (double)PetscRealPart(coords_x[2]), (double)weight_x[c]));
987*8214e71cSJoe     PetscCall(ISRestoreIndices(globalOrdering, &ordering));
988*8214e71cSJoe     totalcellweight += weight_x[c];
989*8214e71cSJoe     // Confirm the number of particles per spatial cell conforms to the size of the velocity grid
990*8214e71cSJoe     PetscCheck(Npc == vEnd - vStart, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of particles %" PetscInt_FMT " in cell (rank %d/%d) != %" PetscInt_FMT " number of velocity vertices", Npc, rank, size, vEnd - vStart);
991*8214e71cSJoe 
992*8214e71cSJoe     /* Set weights to be gaussian in velocity cells (using exact solution) */
993*8214e71cSJoe     for (cv = 0; cv < vEnd - vStart; ++cv) {
994*8214e71cSJoe       PetscInt           Nc;
995*8214e71cSJoe       const PetscScalar *array_v;
996*8214e71cSJoe       PetscScalar       *coords_v = NULL;
997*8214e71cSJoe       PetscBool          isDG;
998*8214e71cSJoe       PetscCall(DMPlexGetCellCoordinates(vdm, cv, &isDG, &Nc, &array_v, &coords_v));
999*8214e71cSJoe 
1000*8214e71cSJoe       const PetscInt p = pidx[cv];
1001*8214e71cSJoe       // Two stream function from 1/2pi v^2 e^(-v^2/2)
1002*8214e71cSJoe       if (user->twostream)
1003*8214e71cSJoe         weight_v[p] = 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.)));
1004*8214e71cSJoe       else weight_v[p] = 0.5 * (PetscErfReal(coords_v[1] / PetscSqrtReal(2.)) - PetscErfReal(coords_v[0] / PetscSqrtReal(2.)));
1005*8214e71cSJoe 
1006*8214e71cSJoe       weight[p] = user->totalWeight * weight_v[p] * weight_x[c];
1007*8214e71cSJoe       if (weight[p] > 1.) PetscCall(PetscPrintf(PETSC_COMM_WORLD, "weights: %g, %g, %g\n", user->totalWeight, weight_v[p], weight_x[c]));
1008*8214e71cSJoe       //PetscPrintf(PETSC_COMM_WORLD, "particle %"PetscInt_FMT": %g, weight_v: %g weight_x: %g\n", p, weight[p], weight_v[p], weight_x[p]);
1009*8214e71cSJoe       weightsum += weight[p];
1010*8214e71cSJoe 
1011*8214e71cSJoe       PetscCall(DMPlexRestoreCellCoordinates(vdm, cv, &isDG, &Nc, &array_v, &coords_v));
1012*8214e71cSJoe     }
1013*8214e71cSJoe     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDGx, &Ncx, &array_x, &coords_x));
1014*8214e71cSJoe     PetscCall(PetscFree(pidx));
1015*8214e71cSJoe   }
1016*8214e71cSJoe   PetscCall(PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT));
1017*8214e71cSJoe   PetscReal global_cellweight, global_weightsum;
1018*8214e71cSJoe   PetscCall(MPIU_Allreduce(&totalcellweight, &global_cellweight, 1, MPIU_REAL, MPIU_SUM, PETSC_COMM_WORLD));
1019*8214e71cSJoe   PetscCall(MPIU_Allreduce(&weightsum, &global_weightsum, 1, MPIU_REAL, MPIU_SUM, PETSC_COMM_WORLD));
1020*8214e71cSJoe   PetscCall(PetscPrintf(PETSC_COMM_WORLD, "particle weight sum = %1.10f cell weight sum = %1.10f\n", (double)global_cellweight, (double)global_weightsum));
1021*8214e71cSJoe   if (user->fake_1D) PetscCall(PetscFree(xq_x_extended));
1022*8214e71cSJoe   PetscCall(PetscFree2(weight_x, weight_v));
1023*8214e71cSJoe   PetscCall(PetscQuadratureDestroy(&quad_x));
1024*8214e71cSJoe   PetscCall(DMSwarmSortRestoreAccess(sw));
1025*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&x));
1026*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&weight));
1027*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **)&species));
1028*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "velocity", NULL, NULL, (void **)&v));
1029*8214e71cSJoe   PetscCall(DMDestroy(&vdm));
1030*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1031*8214e71cSJoe }
1032*8214e71cSJoe 
1033*8214e71cSJoe static PetscErrorCode InitializeConstants(DM sw, AppCtx *user)
1034*8214e71cSJoe {
1035*8214e71cSJoe   DM         dm;
1036*8214e71cSJoe   PetscInt  *species;
1037*8214e71cSJoe   PetscReal *weight, totalCharge = 0., totalWeight = 0., gmin[3], gmax[3], global_charge, global_weight;
1038*8214e71cSJoe   PetscInt   Np, dim;
1039*8214e71cSJoe 
1040*8214e71cSJoe   PetscFunctionBegin;
1041*8214e71cSJoe   PetscCall(DMSwarmGetCellDM(sw, &dm));
1042*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
1043*8214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
1044*8214e71cSJoe   PetscCall(DMGetBoundingBox(dm, gmin, gmax));
1045*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "w_q", NULL, NULL, (void **)&weight));
1046*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **)&species));
1047*8214e71cSJoe   for (PetscInt p = 0; p < Np; ++p) {
1048*8214e71cSJoe     totalWeight += weight[p];
1049*8214e71cSJoe     totalCharge += user->charges[species[p]] * weight[p];
1050*8214e71cSJoe   }
1051*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "w_q", NULL, NULL, (void **)&weight));
1052*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **)&species));
1053*8214e71cSJoe   {
1054*8214e71cSJoe     Parameter *param;
1055*8214e71cSJoe     PetscReal  Area;
1056*8214e71cSJoe 
1057*8214e71cSJoe     PetscCall(PetscBagGetData(user->bag, (void **)&param));
1058*8214e71cSJoe     switch (dim) {
1059*8214e71cSJoe     case 1:
1060*8214e71cSJoe       Area = (gmax[0] - gmin[0]);
1061*8214e71cSJoe       break;
1062*8214e71cSJoe     case 2:
1063*8214e71cSJoe       if (user->fake_1D) {
1064*8214e71cSJoe         Area = (gmax[0] - gmin[0]);
1065*8214e71cSJoe       } else {
1066*8214e71cSJoe         Area = (gmax[0] - gmin[0]) * (gmax[1] - gmin[1]);
1067*8214e71cSJoe       }
1068*8214e71cSJoe       break;
1069*8214e71cSJoe     case 3:
1070*8214e71cSJoe       Area = (gmax[0] - gmin[0]) * (gmax[1] - gmin[1]) * (gmax[2] - gmin[2]);
1071*8214e71cSJoe       break;
1072*8214e71cSJoe     default:
1073*8214e71cSJoe       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Dimension %" PetscInt_FMT " not supported", dim);
1074*8214e71cSJoe     }
1075*8214e71cSJoe     PetscCall(MPIU_Allreduce(&totalWeight, &global_weight, 1, MPIU_REAL, MPIU_SUM, PETSC_COMM_WORLD));
1076*8214e71cSJoe     PetscCall(MPIU_Allreduce(&totalCharge, &global_charge, 1, MPIU_REAL, MPIU_SUM, PETSC_COMM_WORLD));
1077*8214e71cSJoe     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));
1078*8214e71cSJoe     param->sigma = PetscAbsReal(global_charge / (Area));
1079*8214e71cSJoe 
1080*8214e71cSJoe     PetscCall(PetscPrintf(PETSC_COMM_WORLD, "sigma: %g\n", (double)param->sigma));
1081*8214e71cSJoe     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,
1082*8214e71cSJoe                           (double)param->vlasovNumber));
1083*8214e71cSJoe   }
1084*8214e71cSJoe   /* Setup Constants */
1085*8214e71cSJoe   {
1086*8214e71cSJoe     PetscDS    ds;
1087*8214e71cSJoe     Parameter *param;
1088*8214e71cSJoe     PetscCall(PetscBagGetData(user->bag, (void **)&param));
1089*8214e71cSJoe     PetscScalar constants[NUM_CONSTANTS];
1090*8214e71cSJoe     constants[SIGMA]   = param->sigma;
1091*8214e71cSJoe     constants[V0]      = param->v0;
1092*8214e71cSJoe     constants[T0]      = param->t0;
1093*8214e71cSJoe     constants[X0]      = param->x0;
1094*8214e71cSJoe     constants[M0]      = param->m0;
1095*8214e71cSJoe     constants[Q0]      = param->q0;
1096*8214e71cSJoe     constants[PHI0]    = param->phi0;
1097*8214e71cSJoe     constants[POISSON] = param->poissonNumber;
1098*8214e71cSJoe     constants[VLASOV]  = param->vlasovNumber;
1099*8214e71cSJoe     PetscCall(DMGetDS(dm, &ds));
1100*8214e71cSJoe     PetscCall(PetscDSSetConstants(ds, NUM_CONSTANTS, constants));
1101*8214e71cSJoe   }
1102*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1103*8214e71cSJoe }
1104*8214e71cSJoe 
1105*8214e71cSJoe static PetscErrorCode InitializeVelocities_Fake1D(DM sw, AppCtx *user)
1106*8214e71cSJoe {
1107*8214e71cSJoe   DM         dm;
1108*8214e71cSJoe   PetscReal *v;
1109*8214e71cSJoe   PetscInt  *species, cStart, cEnd;
1110*8214e71cSJoe   PetscInt   dim, Np;
1111*8214e71cSJoe 
1112*8214e71cSJoe   PetscFunctionBegin;
1113*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
1114*8214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
1115*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "velocity", NULL, NULL, (void **)&v));
1116*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **)&species));
1117*8214e71cSJoe   PetscCall(DMSwarmGetCellDM(sw, &dm));
1118*8214e71cSJoe   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1119*8214e71cSJoe   PetscRandom rnd;
1120*8214e71cSJoe   PetscCall(PetscRandomCreate(PetscObjectComm((PetscObject)sw), &rnd));
1121*8214e71cSJoe   PetscCall(PetscRandomSetInterval(rnd, 0, 1.));
1122*8214e71cSJoe   PetscCall(PetscRandomSetFromOptions(rnd));
1123*8214e71cSJoe 
1124*8214e71cSJoe   for (PetscInt p = 0; p < Np; ++p) {
1125*8214e71cSJoe     PetscReal a[3] = {0., 0., 0.}, vel[3] = {0., 0., 0.};
1126*8214e71cSJoe 
1127*8214e71cSJoe     PetscCall(PetscRandomGetValueReal(rnd, &a[0]));
1128*8214e71cSJoe     if (user->perturbed_weights) {
1129*8214e71cSJoe       PetscCall(PetscPDFSampleConstant1D(a, NULL, vel));
1130*8214e71cSJoe     } else {
1131*8214e71cSJoe       PetscCall(PetscPDFSampleGaussian1D(a, NULL, vel));
1132*8214e71cSJoe     }
1133*8214e71cSJoe     v[p * dim] = vel[0];
1134*8214e71cSJoe   }
1135*8214e71cSJoe   PetscCall(PetscRandomDestroy(&rnd));
1136*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "velocity", NULL, NULL, (void **)&v));
1137*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **)&species));
1138*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1139*8214e71cSJoe }
1140*8214e71cSJoe 
1141*8214e71cSJoe static PetscErrorCode CreateSwarm(DM dm, AppCtx *user, DM *sw)
1142*8214e71cSJoe {
1143*8214e71cSJoe   PetscReal v0[2] = {1., 0.};
1144*8214e71cSJoe   PetscInt  dim;
1145b80bf5b1SJoe Pusztay 
1146b80bf5b1SJoe Pusztay   PetscFunctionBeginUser;
11479566063dSJacob Faibussowitsch   PetscCall(DMGetDimension(dm, &dim));
11489566063dSJacob Faibussowitsch   PetscCall(DMCreate(PetscObjectComm((PetscObject)dm), sw));
11499566063dSJacob Faibussowitsch   PetscCall(DMSetType(*sw, DMSWARM));
11509566063dSJacob Faibussowitsch   PetscCall(DMSetDimension(*sw, dim));
11519566063dSJacob Faibussowitsch   PetscCall(DMSwarmSetType(*sw, DMSWARM_PIC));
11529566063dSJacob Faibussowitsch   PetscCall(DMSwarmSetCellDM(*sw, dm));
11539566063dSJacob Faibussowitsch   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "w_q", 1, PETSC_SCALAR));
1154*8214e71cSJoe   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "velocity", dim, PETSC_REAL));
1155*8214e71cSJoe   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "species", 1, PETSC_INT));
1156*8214e71cSJoe   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "initCoordinates", dim, PETSC_REAL));
1157*8214e71cSJoe   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "initVelocity", dim, PETSC_REAL));
1158*8214e71cSJoe   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "E_field", dim, PETSC_REAL));
1159*8214e71cSJoe   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "potential", dim, PETSC_REAL));
1160*8214e71cSJoe   PetscCall(DMSwarmRegisterPetscDatatypeField(*sw, "charges", dim, PETSC_REAL));
11619566063dSJacob Faibussowitsch   PetscCall(DMSwarmFinalizeFieldRegister(*sw));
1162*8214e71cSJoe   PetscCall(DMSetApplicationContext(*sw, user));
11639566063dSJacob Faibussowitsch   PetscCall(DMSetFromOptions(*sw));
1164*8214e71cSJoe   user->swarm = *sw;
1165*8214e71cSJoe   if (user->perturbed_weights) {
1166*8214e71cSJoe     PetscCall(InitializeParticles_PerturbedWeights(*sw, user));
1167b80bf5b1SJoe Pusztay   } else {
1168*8214e71cSJoe     PetscCall(DMSwarmComputeLocalSizeFromOptions(*sw));
1169*8214e71cSJoe     PetscCall(DMSwarmInitializeCoordinates(*sw));
1170*8214e71cSJoe     if (user->fake_1D) {
1171*8214e71cSJoe       PetscCall(InitializeVelocities_Fake1D(*sw, user));
11729371c9d4SSatish Balay     } else {
1173*8214e71cSJoe       PetscCall(DMSwarmInitializeVelocitiesFromOptions(*sw, v0));
1174b80bf5b1SJoe Pusztay     }
1175b80bf5b1SJoe Pusztay   }
11769566063dSJacob Faibussowitsch   PetscCall(PetscObjectSetName((PetscObject)*sw, "Particles"));
11779566063dSJacob Faibussowitsch   PetscCall(DMViewFromOptions(*sw, NULL, "-sw_view"));
1178*8214e71cSJoe   {
1179*8214e71cSJoe     Vec gc, gc0, gv, gv0;
1180*8214e71cSJoe 
1181*8214e71cSJoe     PetscCall(DMSwarmCreateGlobalVectorFromField(*sw, DMSwarmPICField_coor, &gc));
1182*8214e71cSJoe     PetscCall(DMSwarmCreateGlobalVectorFromField(*sw, "initCoordinates", &gc0));
1183*8214e71cSJoe     PetscCall(VecCopy(gc, gc0));
1184*8214e71cSJoe     PetscCall(VecViewFromOptions(gc, NULL, "-ic_x_view"));
1185*8214e71cSJoe     PetscCall(DMSwarmDestroyGlobalVectorFromField(*sw, DMSwarmPICField_coor, &gc));
1186*8214e71cSJoe     PetscCall(DMSwarmDestroyGlobalVectorFromField(*sw, "initCoordinates", &gc0));
1187*8214e71cSJoe     PetscCall(DMSwarmCreateGlobalVectorFromField(*sw, "velocity", &gv));
1188*8214e71cSJoe     PetscCall(DMSwarmCreateGlobalVectorFromField(*sw, "initVelocity", &gv0));
1189*8214e71cSJoe     PetscCall(VecCopy(gv, gv0));
1190*8214e71cSJoe     PetscCall(VecViewFromOptions(gv, NULL, "-ic_v_view"));
1191*8214e71cSJoe     PetscCall(DMSwarmDestroyGlobalVectorFromField(*sw, "velocity", &gv));
1192*8214e71cSJoe     PetscCall(DMSwarmDestroyGlobalVectorFromField(*sw, "initVelocity", &gv0));
1193*8214e71cSJoe   }
11943ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1195b80bf5b1SJoe Pusztay }
1196b80bf5b1SJoe Pusztay 
1197*8214e71cSJoe static PetscErrorCode ComputeFieldAtParticles_Coulomb(SNES snes, DM sw, PetscReal E[])
1198d71ae5a4SJacob Faibussowitsch {
1199*8214e71cSJoe   AppCtx     *user;
1200*8214e71cSJoe   PetscReal  *coords;
1201*8214e71cSJoe   PetscInt   *species, dim, Np, Ns;
1202*8214e71cSJoe   PetscMPIInt size;
1203*8214e71cSJoe 
1204*8214e71cSJoe   PetscFunctionBegin;
1205*8214e71cSJoe   PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)snes), &size));
1206*8214e71cSJoe   PetscCheck(size == 1, PetscObjectComm((PetscObject)snes), PETSC_ERR_SUP, "Coulomb code only works in serial");
1207*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
1208*8214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
1209*8214e71cSJoe   PetscCall(DMSwarmGetNumSpecies(sw, &Ns));
1210*8214e71cSJoe   PetscCall(DMGetApplicationContext(sw, (void *)&user));
1211*8214e71cSJoe 
1212*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords));
1213*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "species", NULL, NULL, (void **)&species));
1214*8214e71cSJoe   for (PetscInt p = 0; p < Np; ++p) {
1215*8214e71cSJoe     PetscReal *pcoord = &coords[p * dim];
1216*8214e71cSJoe     PetscReal  pE[3]  = {0., 0., 0.};
1217*8214e71cSJoe 
1218*8214e71cSJoe     /* Calculate field at particle p due to particle q */
1219*8214e71cSJoe     for (PetscInt q = 0; q < Np; ++q) {
1220*8214e71cSJoe       PetscReal *qcoord = &coords[q * dim];
1221*8214e71cSJoe       PetscReal  rpq[3], r, r3, q_q;
1222*8214e71cSJoe 
1223*8214e71cSJoe       if (p == q) continue;
1224*8214e71cSJoe       q_q = user->charges[species[q]] * 1.;
1225*8214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) rpq[d] = pcoord[d] - qcoord[d];
1226*8214e71cSJoe       r = DMPlex_NormD_Internal(dim, rpq);
1227*8214e71cSJoe       if (r < PETSC_SQRT_MACHINE_EPSILON) continue;
1228*8214e71cSJoe       r3 = PetscPowRealInt(r, 3);
1229*8214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) pE[d] += q_q * rpq[d] / r3;
1230*8214e71cSJoe     }
1231*8214e71cSJoe     for (PetscInt d = 0; d < dim; ++d) E[p * dim + d] = pE[d];
1232*8214e71cSJoe   }
1233*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "species", NULL, NULL, (void **)&species));
1234*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords));
1235*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1236*8214e71cSJoe }
1237*8214e71cSJoe 
1238*8214e71cSJoe static PetscErrorCode ComputeFieldAtParticles_Primal(SNES snes, DM sw, PetscReal E[])
1239*8214e71cSJoe {
1240b80bf5b1SJoe Pusztay   DM              dm;
1241*8214e71cSJoe   AppCtx         *user;
1242*8214e71cSJoe   PetscDS         ds;
1243*8214e71cSJoe   PetscFE         fe;
1244*8214e71cSJoe   Mat             M_p, M;
1245*8214e71cSJoe   Vec             phi, locPhi, rho, f;
1246*8214e71cSJoe   PetscReal      *coords;
1247*8214e71cSJoe   PetscInt        dim, cStart, cEnd, Np;
1248*8214e71cSJoe   PetscQuadrature q;
1249*8214e71cSJoe 
1250*8214e71cSJoe   PetscFunctionBegin;
1251*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
1252*8214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
1253*8214e71cSJoe   PetscCall(DMGetApplicationContext(sw, (void *)&user));
1254*8214e71cSJoe 
1255*8214e71cSJoe   KSP         ksp;
1256*8214e71cSJoe   Vec         rho0;
1257*8214e71cSJoe   char        oldField[PETSC_MAX_PATH_LEN];
1258*8214e71cSJoe   const char *tmp;
1259*8214e71cSJoe 
1260*8214e71cSJoe   /* Create the charges rho */
1261*8214e71cSJoe   PetscCall(SNESGetDM(snes, &dm));
1262*8214e71cSJoe   PetscCall(DMSwarmVectorGetField(sw, &tmp));
1263*8214e71cSJoe   PetscCall(PetscStrncpy(oldField, tmp, PETSC_MAX_PATH_LEN));
1264*8214e71cSJoe   PetscCall(DMSwarmVectorDefineField(sw, "w_q"));
1265*8214e71cSJoe   PetscCall(DMCreateMassMatrix(sw, dm, &M_p));
1266*8214e71cSJoe   PetscCall(DMSwarmVectorDefineField(sw, oldField));
1267*8214e71cSJoe 
1268*8214e71cSJoe   PetscCall(DMCreateMassMatrix(dm, dm, &M));
1269*8214e71cSJoe   PetscCall(DMGetGlobalVector(dm, &rho0));
1270*8214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)rho0, "Charge density (rho0) from Primal Compute"));
1271*8214e71cSJoe   PetscCall(DMGetGlobalVector(dm, &rho));
1272*8214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)rho, "rho"));
1273*8214e71cSJoe   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "w_q", &f));
1274*8214e71cSJoe 
1275*8214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)f, "particle weight"));
1276*8214e71cSJoe   PetscCall(MatMultTranspose(M_p, f, rho));
1277*8214e71cSJoe   PetscCall(MatViewFromOptions(M_p, NULL, "-mp_view"));
1278*8214e71cSJoe   PetscCall(MatViewFromOptions(M, NULL, "-m_view"));
1279*8214e71cSJoe   PetscCall(VecViewFromOptions(f, NULL, "-weights_view"));
1280*8214e71cSJoe   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "w_q", &f));
1281*8214e71cSJoe 
1282*8214e71cSJoe   PetscCall(KSPCreate(PetscObjectComm((PetscObject)dm), &ksp));
1283*8214e71cSJoe   PetscCall(KSPSetOptionsPrefix(ksp, "em_proj_"));
1284*8214e71cSJoe   PetscCall(KSPSetOperators(ksp, M, M));
1285*8214e71cSJoe   PetscCall(KSPSetFromOptions(ksp));
1286*8214e71cSJoe   PetscCall(KSPSolve(ksp, rho, rho0));
1287*8214e71cSJoe   PetscCall(VecViewFromOptions(rho0, NULL, "-rho0_view"));
1288*8214e71cSJoe 
1289*8214e71cSJoe   PetscInt           rhosize;
1290*8214e71cSJoe   PetscReal         *charges;
1291*8214e71cSJoe   const PetscScalar *rho_vals;
1292*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "charges", NULL, NULL, (void **)&charges));
1293*8214e71cSJoe   PetscCall(VecGetLocalSize(rho0, &rhosize));
1294*8214e71cSJoe   PetscCall(VecGetArrayRead(rho0, &rho_vals));
1295*8214e71cSJoe   for (PetscInt c = 0; c < rhosize; ++c) charges[c] = rho_vals[c];
1296*8214e71cSJoe   PetscCall(VecRestoreArrayRead(rho0, &rho_vals));
1297*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "charges", NULL, NULL, (void **)&charges));
1298*8214e71cSJoe 
1299*8214e71cSJoe   PetscCall(VecScale(rho, -1.0));
1300*8214e71cSJoe 
1301*8214e71cSJoe   PetscCall(VecViewFromOptions(rho0, NULL, "-rho0_view"));
1302*8214e71cSJoe   PetscCall(VecViewFromOptions(rho, NULL, "-rho_view"));
1303*8214e71cSJoe   PetscCall(DMRestoreGlobalVector(dm, &rho0));
1304*8214e71cSJoe   PetscCall(KSPDestroy(&ksp));
1305*8214e71cSJoe   PetscCall(MatDestroy(&M_p));
1306*8214e71cSJoe   PetscCall(MatDestroy(&M));
1307*8214e71cSJoe 
1308*8214e71cSJoe   PetscCall(DMGetGlobalVector(dm, &phi));
1309*8214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)phi, "potential"));
1310*8214e71cSJoe   PetscCall(VecSet(phi, 0.0));
1311*8214e71cSJoe   PetscCall(SNESSolve(snes, rho, phi));
1312*8214e71cSJoe   PetscCall(DMRestoreGlobalVector(dm, &rho));
1313*8214e71cSJoe   PetscCall(VecViewFromOptions(phi, NULL, "-phi_view"));
1314*8214e71cSJoe 
1315*8214e71cSJoe   PetscInt           phisize;
1316*8214e71cSJoe   PetscReal         *pot;
1317*8214e71cSJoe   const PetscScalar *phi_vals;
1318*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "potential", NULL, NULL, (void **)&pot));
1319*8214e71cSJoe   PetscCall(VecGetLocalSize(phi, &phisize));
1320*8214e71cSJoe   PetscCall(VecGetArrayRead(phi, &phi_vals));
1321*8214e71cSJoe   for (PetscInt c = 0; c < phisize; ++c) pot[c] = phi_vals[c];
1322*8214e71cSJoe   PetscCall(VecRestoreArrayRead(phi, &phi_vals));
1323*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "potential", NULL, NULL, (void **)&pot));
1324*8214e71cSJoe 
1325*8214e71cSJoe   PetscCall(DMGetLocalVector(dm, &locPhi));
1326*8214e71cSJoe   PetscCall(DMGlobalToLocalBegin(dm, phi, INSERT_VALUES, locPhi));
1327*8214e71cSJoe   PetscCall(DMGlobalToLocalEnd(dm, phi, INSERT_VALUES, locPhi));
1328*8214e71cSJoe   PetscCall(DMRestoreGlobalVector(dm, &phi));
1329*8214e71cSJoe 
1330*8214e71cSJoe   PetscCall(DMGetDS(dm, &ds));
1331*8214e71cSJoe   PetscCall(PetscDSGetDiscretization(ds, 0, (PetscObject *)&fe));
1332*8214e71cSJoe   PetscCall(DMSwarmSortGetAccess(sw));
1333*8214e71cSJoe   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1334*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords));
1335*8214e71cSJoe 
1336*8214e71cSJoe   for (PetscInt c = cStart; c < cEnd; ++c) {
1337*8214e71cSJoe     PetscTabulation tab;
1338*8214e71cSJoe     PetscScalar    *clPhi = NULL;
1339*8214e71cSJoe     PetscReal      *pcoord, *refcoord;
1340*8214e71cSJoe     PetscReal       v[3], J[9], invJ[9], detJ;
1341*8214e71cSJoe     PetscInt       *points;
1342*8214e71cSJoe     PetscInt        Ncp;
1343*8214e71cSJoe 
1344*8214e71cSJoe     PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Ncp, &points));
1345*8214e71cSJoe     PetscCall(DMGetWorkArray(dm, Ncp * dim, MPIU_REAL, &pcoord));
1346*8214e71cSJoe     PetscCall(DMGetWorkArray(dm, Ncp * dim, MPIU_REAL, &refcoord));
1347*8214e71cSJoe     for (PetscInt cp = 0; cp < Ncp; ++cp)
1348*8214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) pcoord[cp * dim + d] = coords[points[cp] * dim + d];
1349*8214e71cSJoe     PetscCall(DMPlexCoordinatesToReference(dm, c, Ncp, pcoord, refcoord));
1350*8214e71cSJoe     PetscCall(PetscFECreateTabulation(fe, 1, Ncp, refcoord, 1, &tab));
1351*8214e71cSJoe     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, v, J, invJ, &detJ));
1352*8214e71cSJoe     PetscCall(DMPlexVecGetClosure(dm, NULL, locPhi, c, NULL, &clPhi));
1353*8214e71cSJoe     for (PetscInt cp = 0; cp < Ncp; ++cp) {
1354*8214e71cSJoe       const PetscReal *basisDer = tab->T[1];
1355*8214e71cSJoe       const PetscInt   p        = points[cp];
1356*8214e71cSJoe 
1357*8214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) E[p * dim + d] = 0.;
1358*8214e71cSJoe       PetscCall(PetscFEGetQuadrature(fe, &q));
1359*8214e71cSJoe       PetscCall(PetscFEFreeInterpolateGradient_Static(fe, basisDer, clPhi, dim, invJ, NULL, cp, &E[p * dim]));
1360*8214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) {
1361*8214e71cSJoe         E[p * dim + d] *= -1.0;
1362*8214e71cSJoe         if (user->fake_1D && d > 0) E[p * dim + d] = 0;
1363*8214e71cSJoe       }
1364*8214e71cSJoe     }
1365*8214e71cSJoe     PetscCall(DMPlexVecRestoreClosure(dm, NULL, locPhi, c, NULL, &clPhi));
1366*8214e71cSJoe     PetscCall(DMRestoreWorkArray(dm, Ncp * dim, MPIU_REAL, &pcoord));
1367*8214e71cSJoe     PetscCall(DMRestoreWorkArray(dm, Ncp * dim, MPIU_REAL, &refcoord));
1368*8214e71cSJoe     PetscCall(PetscTabulationDestroy(&tab));
1369*8214e71cSJoe     PetscCall(PetscFree(points));
1370*8214e71cSJoe   }
1371*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords));
1372*8214e71cSJoe   PetscCall(DMSwarmSortRestoreAccess(sw));
1373*8214e71cSJoe   PetscCall(DMRestoreLocalVector(dm, &locPhi));
1374*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1375*8214e71cSJoe }
1376*8214e71cSJoe 
1377*8214e71cSJoe static PetscErrorCode ComputeFieldAtParticles_Mixed(SNES snes, DM sw, PetscReal E[])
1378*8214e71cSJoe {
1379*8214e71cSJoe   AppCtx         *user;
1380*8214e71cSJoe   DM              dm, potential_dm;
1381*8214e71cSJoe   KSP             ksp;
1382*8214e71cSJoe   IS              potential_IS;
1383*8214e71cSJoe   PetscDS         ds;
1384*8214e71cSJoe   PetscFE         fe;
1385*8214e71cSJoe   PetscFEGeom     feGeometry;
1386*8214e71cSJoe   Mat             M_p, M;
1387*8214e71cSJoe   Vec             phi, locPhi, rho, f, temp_rho, rho0;
1388*8214e71cSJoe   PetscQuadrature q;
1389*8214e71cSJoe   PetscReal      *coords, *pot;
1390*8214e71cSJoe   PetscInt        dim, cStart, cEnd, Np, fields = 1;
1391*8214e71cSJoe   char            oldField[PETSC_MAX_PATH_LEN];
1392*8214e71cSJoe   const char     *tmp;
1393*8214e71cSJoe 
1394*8214e71cSJoe   PetscFunctionBegin;
1395*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
1396*8214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
1397*8214e71cSJoe   PetscCall(DMGetApplicationContext(sw, &user));
1398*8214e71cSJoe 
1399*8214e71cSJoe   /* Create the charges rho */
1400*8214e71cSJoe   PetscCall(SNESGetDM(snes, &dm));
1401*8214e71cSJoe   PetscCall(DMGetGlobalVector(dm, &rho));
1402*8214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)rho, "rho"));
1403*8214e71cSJoe 
1404*8214e71cSJoe   PetscCall(DMCreateSubDM(dm, 1, &fields, &potential_IS, &potential_dm));
1405*8214e71cSJoe 
1406*8214e71cSJoe   PetscCall(DMSwarmVectorGetField(sw, &tmp));
1407*8214e71cSJoe   PetscCall(PetscStrncpy(oldField, tmp, PETSC_MAX_PATH_LEN));
1408*8214e71cSJoe   PetscCall(DMSwarmVectorDefineField(sw, "w_q"));
1409*8214e71cSJoe   PetscCall(DMCreateMassMatrix(sw, potential_dm, &M_p));
1410*8214e71cSJoe   PetscCall(DMSwarmVectorDefineField(sw, oldField));
1411*8214e71cSJoe 
1412*8214e71cSJoe   PetscCall(DMCreateMassMatrix(potential_dm, potential_dm, &M));
1413*8214e71cSJoe   PetscCall(MatViewFromOptions(M_p, NULL, "-mp_view"));
1414*8214e71cSJoe   PetscCall(MatViewFromOptions(M, NULL, "-m_view"));
1415*8214e71cSJoe   PetscCall(DMGetGlobalVector(potential_dm, &temp_rho));
1416*8214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)temp_rho, "Mf"));
1417*8214e71cSJoe   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "w_q", &f));
1418*8214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)f, "particle weight"));
1419*8214e71cSJoe   PetscCall(VecViewFromOptions(f, NULL, "-weights_view"));
1420*8214e71cSJoe   PetscCall(MatMultTranspose(M_p, f, temp_rho));
1421*8214e71cSJoe   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "w_q", &f));
1422*8214e71cSJoe   PetscCall(DMGetGlobalVector(potential_dm, &rho0));
1423*8214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)rho0, "Charge density (rho0) from Mixed Compute"));
1424*8214e71cSJoe 
1425*8214e71cSJoe   PetscCall(KSPCreate(PetscObjectComm((PetscObject)dm), &ksp));
1426*8214e71cSJoe   PetscCall(KSPSetOptionsPrefix(ksp, "em_proj"));
1427*8214e71cSJoe   PetscCall(KSPSetOperators(ksp, M, M));
1428*8214e71cSJoe   PetscCall(KSPSetFromOptions(ksp));
1429*8214e71cSJoe   PetscCall(KSPSolve(ksp, temp_rho, rho0));
1430*8214e71cSJoe   PetscCall(VecViewFromOptions(rho0, NULL, "-rho0_view"));
1431*8214e71cSJoe 
1432*8214e71cSJoe   PetscInt           rhosize;
1433*8214e71cSJoe   PetscReal         *charges;
1434*8214e71cSJoe   const PetscScalar *rho_vals;
1435*8214e71cSJoe   Parameter         *param;
1436*8214e71cSJoe   PetscCall(PetscBagGetData(user->bag, (void **)&param));
1437*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "charges", NULL, NULL, (void **)&charges));
1438*8214e71cSJoe   PetscCall(VecGetLocalSize(rho0, &rhosize));
1439*8214e71cSJoe 
1440*8214e71cSJoe   /* Integral over reference element is size 1.  Reference element area is 4.  Scale rho0 by 1/4 because the basis function is 1/4 */
1441*8214e71cSJoe   PetscCall(VecScale(rho0, 0.25));
1442*8214e71cSJoe   PetscCall(VecGetArrayRead(rho0, &rho_vals));
1443*8214e71cSJoe   for (PetscInt c = 0; c < rhosize; ++c) charges[c] = rho_vals[c];
1444*8214e71cSJoe   PetscCall(VecRestoreArrayRead(rho0, &rho_vals));
1445*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "charges", NULL, NULL, (void **)&charges));
1446*8214e71cSJoe 
1447*8214e71cSJoe   PetscCall(VecISCopy(rho, potential_IS, SCATTER_FORWARD, temp_rho));
1448*8214e71cSJoe   PetscCall(VecScale(rho, 0.25));
1449*8214e71cSJoe   PetscCall(VecViewFromOptions(rho0, NULL, "-rho0_view"));
1450*8214e71cSJoe   PetscCall(VecViewFromOptions(temp_rho, NULL, "-temprho_view"));
1451*8214e71cSJoe   PetscCall(VecViewFromOptions(rho, NULL, "-rho_view"));
1452*8214e71cSJoe   PetscCall(DMRestoreGlobalVector(potential_dm, &temp_rho));
1453*8214e71cSJoe   PetscCall(DMRestoreGlobalVector(potential_dm, &rho0));
1454*8214e71cSJoe 
1455*8214e71cSJoe   PetscCall(MatDestroy(&M_p));
1456*8214e71cSJoe   PetscCall(MatDestroy(&M));
1457*8214e71cSJoe   PetscCall(KSPDestroy(&ksp));
1458*8214e71cSJoe   PetscCall(DMDestroy(&potential_dm));
1459*8214e71cSJoe   PetscCall(ISDestroy(&potential_IS));
1460*8214e71cSJoe 
1461*8214e71cSJoe   PetscCall(DMGetGlobalVector(dm, &phi));
1462*8214e71cSJoe   PetscCall(PetscObjectSetName((PetscObject)phi, "potential"));
1463*8214e71cSJoe   PetscCall(VecSet(phi, 0.0));
1464*8214e71cSJoe   PetscCall(SNESSolve(snes, rho, phi));
1465*8214e71cSJoe   PetscCall(DMRestoreGlobalVector(dm, &rho));
1466*8214e71cSJoe 
1467*8214e71cSJoe   PetscInt           phisize;
1468*8214e71cSJoe   const PetscScalar *phi_vals;
1469*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "potential", NULL, NULL, (void **)&pot));
1470*8214e71cSJoe   PetscCall(VecGetLocalSize(phi, &phisize));
1471*8214e71cSJoe   PetscCall(VecViewFromOptions(phi, NULL, "-phi_view"));
1472*8214e71cSJoe   PetscCall(VecGetArrayRead(phi, &phi_vals));
1473*8214e71cSJoe   for (PetscInt c = 0; c < phisize; ++c) pot[c] = phi_vals[c];
1474*8214e71cSJoe   PetscCall(VecRestoreArrayRead(phi, &phi_vals));
1475*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "potential", NULL, NULL, (void **)&pot));
1476*8214e71cSJoe 
1477*8214e71cSJoe   PetscCall(DMGetLocalVector(dm, &locPhi));
1478*8214e71cSJoe   PetscCall(DMGlobalToLocalBegin(dm, phi, INSERT_VALUES, locPhi));
1479*8214e71cSJoe   PetscCall(DMGlobalToLocalEnd(dm, phi, INSERT_VALUES, locPhi));
1480*8214e71cSJoe   PetscCall(DMRestoreGlobalVector(dm, &phi));
1481*8214e71cSJoe 
1482*8214e71cSJoe   PetscCall(DMGetDS(dm, &ds));
1483*8214e71cSJoe   PetscCall(PetscDSGetDiscretization(ds, 0, (PetscObject *)&fe));
1484*8214e71cSJoe   PetscCall(DMSwarmSortGetAccess(sw));
1485*8214e71cSJoe   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1486*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords));
1487*8214e71cSJoe   PetscCall(PetscFEGetQuadrature(fe, &q));
1488*8214e71cSJoe   PetscCall(PetscFECreateCellGeometry(fe, q, &feGeometry));
1489*8214e71cSJoe   for (PetscInt c = cStart; c < cEnd; ++c) {
1490*8214e71cSJoe     PetscTabulation tab;
1491*8214e71cSJoe     PetscScalar    *clPhi = NULL;
1492*8214e71cSJoe     PetscReal      *pcoord, *refcoord;
1493*8214e71cSJoe     PetscInt       *points;
1494*8214e71cSJoe     PetscInt        Ncp;
1495*8214e71cSJoe 
1496*8214e71cSJoe     PetscCall(DMSwarmSortGetPointsPerCell(sw, c, &Ncp, &points));
1497*8214e71cSJoe     PetscCall(DMGetWorkArray(dm, Ncp * dim, MPIU_REAL, &pcoord));
1498*8214e71cSJoe     PetscCall(DMGetWorkArray(dm, Ncp * dim, MPIU_REAL, &refcoord));
1499*8214e71cSJoe     for (PetscInt cp = 0; cp < Ncp; ++cp)
1500*8214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) pcoord[cp * dim + d] = coords[points[cp] * dim + d];
1501*8214e71cSJoe     PetscCall(DMPlexCoordinatesToReference(dm, c, Ncp, pcoord, refcoord));
1502*8214e71cSJoe     PetscCall(PetscFECreateTabulation(fe, 1, Ncp, refcoord, 1, &tab));
1503*8214e71cSJoe     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, q, feGeometry.v, feGeometry.J, feGeometry.invJ, feGeometry.detJ));
1504*8214e71cSJoe     PetscCall(DMPlexVecGetClosure(dm, NULL, locPhi, c, NULL, &clPhi));
1505*8214e71cSJoe 
1506*8214e71cSJoe     for (PetscInt cp = 0; cp < Ncp; ++cp) {
1507*8214e71cSJoe       const PetscInt p = points[cp];
1508*8214e71cSJoe 
1509*8214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) E[p * dim + d] = 0.;
1510*8214e71cSJoe       PetscCall(PetscFEInterpolateAtPoints_Static(fe, tab, clPhi, &feGeometry, cp, &E[p * dim]));
1511*8214e71cSJoe       PetscCall(PetscFEPushforward(fe, &feGeometry, 1, &E[p * dim]));
1512*8214e71cSJoe       for (PetscInt d = 0; d < dim; ++d) {
1513*8214e71cSJoe         E[p * dim + d] *= -2.0;
1514*8214e71cSJoe         if (user->fake_1D && d > 0) E[p * dim + d] = 0;
1515*8214e71cSJoe       }
1516*8214e71cSJoe     }
1517*8214e71cSJoe     PetscCall(DMPlexVecRestoreClosure(dm, NULL, locPhi, c, NULL, &clPhi));
1518*8214e71cSJoe     PetscCall(DMRestoreWorkArray(dm, Ncp * dim, MPIU_REAL, &pcoord));
1519*8214e71cSJoe     PetscCall(DMRestoreWorkArray(dm, Ncp * dim, MPIU_REAL, &refcoord));
1520*8214e71cSJoe     PetscCall(PetscTabulationDestroy(&tab));
1521*8214e71cSJoe     PetscCall(PetscFree(points));
1522*8214e71cSJoe   }
1523*8214e71cSJoe   PetscCall(PetscFEDestroyCellGeometry(fe, &feGeometry));
1524*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, DMSwarmPICField_coor, NULL, NULL, (void **)&coords));
1525*8214e71cSJoe   PetscCall(DMSwarmSortRestoreAccess(sw));
1526*8214e71cSJoe   PetscCall(DMRestoreLocalVector(dm, &locPhi));
1527*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1528*8214e71cSJoe }
1529*8214e71cSJoe 
1530*8214e71cSJoe static PetscErrorCode ComputeFieldAtParticles(SNES snes, DM sw, PetscReal E[])
1531*8214e71cSJoe {
1532*8214e71cSJoe   AppCtx  *ctx;
1533*8214e71cSJoe   PetscInt dim, Np;
1534*8214e71cSJoe 
1535*8214e71cSJoe   PetscFunctionBegin;
1536*8214e71cSJoe   PetscValidHeaderSpecific(snes, SNES_CLASSID, 1);
1537*8214e71cSJoe   PetscValidHeaderSpecific(sw, DM_CLASSID, 2);
1538*8214e71cSJoe   PetscAssertPointer(E, 3);
1539*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
1540*8214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
1541*8214e71cSJoe   PetscCall(DMGetApplicationContext(sw, &ctx));
1542*8214e71cSJoe   PetscCall(PetscArrayzero(E, Np * dim));
1543*8214e71cSJoe 
1544*8214e71cSJoe   switch (ctx->em) {
1545*8214e71cSJoe   case EM_PRIMAL:
1546*8214e71cSJoe     PetscCall(ComputeFieldAtParticles_Primal(snes, sw, E));
1547*8214e71cSJoe     break;
1548*8214e71cSJoe   case EM_COULOMB:
1549*8214e71cSJoe     PetscCall(ComputeFieldAtParticles_Coulomb(snes, sw, E));
1550*8214e71cSJoe     break;
1551*8214e71cSJoe   case EM_MIXED:
1552*8214e71cSJoe     PetscCall(ComputeFieldAtParticles_Mixed(snes, sw, E));
1553*8214e71cSJoe     break;
1554*8214e71cSJoe   case EM_NONE:
1555*8214e71cSJoe     break;
1556*8214e71cSJoe   default:
1557*8214e71cSJoe     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "No solver for electrostatic model %s", EMTypes[ctx->em]);
1558*8214e71cSJoe   }
1559*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1560*8214e71cSJoe }
1561*8214e71cSJoe 
1562*8214e71cSJoe static PetscErrorCode RHSFunction(TS ts, PetscReal t, Vec U, Vec G, void *ctx)
1563*8214e71cSJoe {
1564*8214e71cSJoe   DM                 sw;
1565*8214e71cSJoe   SNES               snes = ((AppCtx *)ctx)->snes;
1566*8214e71cSJoe   const PetscReal   *coords, *vel;
1567*8214e71cSJoe   const PetscScalar *u;
1568*8214e71cSJoe   PetscScalar       *g;
1569*8214e71cSJoe   PetscReal         *E, m_p = 1., q_p = -1.;
1570*8214e71cSJoe   PetscInt           dim, d, Np, p;
1571b80bf5b1SJoe Pusztay 
1572b80bf5b1SJoe Pusztay   PetscFunctionBeginUser;
1573*8214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
1574*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
1575*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
1576*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initVelocity", NULL, NULL, (void **)&vel));
1577*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "E_field", NULL, NULL, (void **)&E));
1578*8214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
1579*8214e71cSJoe   PetscCall(VecGetArrayRead(U, &u));
1580*8214e71cSJoe   PetscCall(VecGetArray(G, &g));
1581*8214e71cSJoe 
1582*8214e71cSJoe   PetscCall(ComputeFieldAtParticles(snes, sw, E));
1583*8214e71cSJoe 
1584*8214e71cSJoe   Np /= 2 * dim;
1585*8214e71cSJoe   for (p = 0; p < Np; ++p) {
1586*8214e71cSJoe     for (d = 0; d < dim; ++d) {
1587*8214e71cSJoe       g[(p * 2 + 0) * dim + d] = u[(p * 2 + 1) * dim + d];
1588*8214e71cSJoe       g[(p * 2 + 1) * dim + d] = q_p * E[p * dim + d] / m_p;
1589*8214e71cSJoe     }
1590*8214e71cSJoe   }
1591*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
1592*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initVelocity", NULL, NULL, (void **)&vel));
1593*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "E_field", NULL, NULL, (void **)&E));
1594*8214e71cSJoe   PetscCall(VecRestoreArrayRead(U, &u));
1595*8214e71cSJoe   PetscCall(VecRestoreArray(G, &g));
1596*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1597*8214e71cSJoe }
1598*8214e71cSJoe 
1599*8214e71cSJoe /* J_{ij} = dF_i/dx_j
1600*8214e71cSJoe    J_p = (  0   1)
1601*8214e71cSJoe          (-w^2  0)
1602*8214e71cSJoe    TODO Now there is another term with w^2 from the electric field. I think we will need to invert the operator.
1603*8214e71cSJoe         Perhaps we can approximate the Jacobian using only the cellwise P-P gradient from Coulomb
1604*8214e71cSJoe */
1605*8214e71cSJoe static PetscErrorCode RHSJacobian(TS ts, PetscReal t, Vec U, Mat J, Mat P, void *ctx)
1606*8214e71cSJoe {
1607*8214e71cSJoe   DM               sw;
1608*8214e71cSJoe   const PetscReal *coords, *vel;
1609*8214e71cSJoe   PetscInt         dim, d, Np, p, rStart;
1610*8214e71cSJoe 
1611*8214e71cSJoe   PetscFunctionBeginUser;
1612*8214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
1613*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
1614*8214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
1615*8214e71cSJoe   PetscCall(MatGetOwnershipRange(J, &rStart, NULL));
1616*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
1617*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initVelocity", NULL, NULL, (void **)&vel));
1618*8214e71cSJoe   Np /= 2 * dim;
1619*8214e71cSJoe   for (p = 0; p < Np; ++p) {
1620*8214e71cSJoe     const PetscReal x0      = coords[p * dim + 0];
1621*8214e71cSJoe     const PetscReal vy0     = vel[p * dim + 1];
1622*8214e71cSJoe     const PetscReal omega   = vy0 / x0;
1623*8214e71cSJoe     PetscScalar     vals[4] = {0., 1., -PetscSqr(omega), 0.};
1624*8214e71cSJoe 
1625*8214e71cSJoe     for (d = 0; d < dim; ++d) {
1626*8214e71cSJoe       const PetscInt rows[2] = {(p * 2 + 0) * dim + d + rStart, (p * 2 + 1) * dim + d + rStart};
1627*8214e71cSJoe       PetscCall(MatSetValues(J, 2, rows, 2, rows, vals, INSERT_VALUES));
1628*8214e71cSJoe     }
1629*8214e71cSJoe   }
1630*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
1631*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initVelocity", NULL, NULL, (void **)&vel));
1632*8214e71cSJoe   PetscCall(MatAssemblyBegin(J, MAT_FINAL_ASSEMBLY));
1633*8214e71cSJoe   PetscCall(MatAssemblyEnd(J, MAT_FINAL_ASSEMBLY));
1634*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1635*8214e71cSJoe }
1636*8214e71cSJoe 
1637*8214e71cSJoe static PetscErrorCode RHSFunctionX(TS ts, PetscReal t, Vec V, Vec Xres, void *ctx)
1638*8214e71cSJoe {
1639*8214e71cSJoe   AppCtx            *user = (AppCtx *)ctx;
1640*8214e71cSJoe   DM                 sw;
1641*8214e71cSJoe   const PetscScalar *v;
1642*8214e71cSJoe   PetscScalar       *xres;
1643*8214e71cSJoe   PetscInt           Np, p, d, dim;
1644*8214e71cSJoe 
1645*8214e71cSJoe   PetscFunctionBeginUser;
1646*8214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
1647*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
1648*8214e71cSJoe   PetscCall(VecGetLocalSize(Xres, &Np));
16499566063dSJacob Faibussowitsch   PetscCall(VecGetArrayRead(V, &v));
1650*8214e71cSJoe   PetscCall(VecGetArray(Xres, &xres));
1651b80bf5b1SJoe Pusztay   Np /= dim;
1652b80bf5b1SJoe Pusztay   for (p = 0; p < Np; ++p) {
1653*8214e71cSJoe     for (d = 0; d < dim; ++d) {
1654*8214e71cSJoe       xres[p * dim + d] = v[p * dim + d];
1655*8214e71cSJoe       if (user->fake_1D && d > 0) xres[p * dim + d] = 0;
1656*8214e71cSJoe     }
1657b80bf5b1SJoe Pusztay   }
16589566063dSJacob Faibussowitsch   PetscCall(VecRestoreArrayRead(V, &v));
1659*8214e71cSJoe   PetscCall(VecRestoreArray(Xres, &xres));
16603ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1661b80bf5b1SJoe Pusztay }
1662b80bf5b1SJoe Pusztay 
1663*8214e71cSJoe static PetscErrorCode RHSFunctionV(TS ts, PetscReal t, Vec X, Vec Vres, void *ctx)
1664*8214e71cSJoe {
1665*8214e71cSJoe   DM                 sw;
1666*8214e71cSJoe   AppCtx            *user = (AppCtx *)ctx;
1667*8214e71cSJoe   SNES               snes = ((AppCtx *)ctx)->snes;
1668*8214e71cSJoe   const PetscScalar *x;
1669*8214e71cSJoe   const PetscReal   *coords, *vel;
1670*8214e71cSJoe   PetscReal         *E, m_p, q_p;
1671*8214e71cSJoe   PetscScalar       *vres;
1672*8214e71cSJoe   PetscInt           Np, p, dim, d;
1673*8214e71cSJoe   Parameter         *param;
1674*8214e71cSJoe 
1675*8214e71cSJoe   PetscFunctionBeginUser;
1676*8214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
1677*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
1678*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
1679*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initVelocity", NULL, NULL, (void **)&vel));
1680*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "E_field", NULL, NULL, (void **)&E));
1681*8214e71cSJoe   PetscCall(PetscBagGetData(user->bag, (void **)&param));
1682*8214e71cSJoe   m_p = user->masses[0] * param->m0;
1683*8214e71cSJoe   q_p = user->charges[0] * param->q0;
1684*8214e71cSJoe   PetscCall(VecGetLocalSize(Vres, &Np));
1685*8214e71cSJoe   PetscCall(VecGetArrayRead(X, &x));
1686*8214e71cSJoe   PetscCall(VecGetArray(Vres, &vres));
1687*8214e71cSJoe   PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Dimension must be 2");
1688*8214e71cSJoe   PetscCall(ComputeFieldAtParticles(snes, sw, E));
1689*8214e71cSJoe 
1690*8214e71cSJoe   Np /= dim;
1691*8214e71cSJoe   for (p = 0; p < Np; ++p) {
1692*8214e71cSJoe     for (d = 0; d < dim; ++d) {
1693*8214e71cSJoe       vres[p * dim + d] = q_p * E[p * dim + d] / m_p;
1694*8214e71cSJoe       if (user->fake_1D && d > 0) vres[p * dim + d] = 0.;
1695*8214e71cSJoe     }
1696*8214e71cSJoe   }
1697*8214e71cSJoe   PetscCall(VecRestoreArrayRead(X, &x));
1698*8214e71cSJoe   /*
1699*8214e71cSJoe     Syncrhonized, ordered output for parallel/sequential test cases.
1700*8214e71cSJoe     In the 1D (on the 2D mesh) case, every y component should be zero.
1701*8214e71cSJoe   */
1702*8214e71cSJoe   if (user->checkVRes) {
1703*8214e71cSJoe     PetscBool pr = user->checkVRes > 1 ? PETSC_TRUE : PETSC_FALSE;
1704*8214e71cSJoe     PetscInt  step;
1705*8214e71cSJoe 
1706*8214e71cSJoe     PetscCall(TSGetStepNumber(ts, &step));
1707*8214e71cSJoe     if (pr) PetscCall(PetscPrintf(PETSC_COMM_WORLD, "step: %" PetscInt_FMT "\n", step));
1708*8214e71cSJoe     for (PetscInt p = 0; p < Np; ++p) {
1709*8214e71cSJoe       if (pr) PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "Residual: %.12g %.12g\n", (double)PetscRealPart(vres[p * dim + 0]), (double)PetscRealPart(vres[p * dim + 1])));
1710*8214e71cSJoe       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]));
1711*8214e71cSJoe     }
1712*8214e71cSJoe     if (pr) PetscCall(PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT));
1713*8214e71cSJoe   }
1714*8214e71cSJoe   PetscCall(VecRestoreArray(Vres, &vres));
1715*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
1716*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initVelocity", NULL, NULL, (void **)&vel));
1717*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "E_field", NULL, NULL, (void **)&E));
1718*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1719*8214e71cSJoe }
1720*8214e71cSJoe 
1721*8214e71cSJoe static PetscErrorCode CreateSolution(TS ts)
1722*8214e71cSJoe {
1723*8214e71cSJoe   DM       sw;
1724*8214e71cSJoe   Vec      u;
1725*8214e71cSJoe   PetscInt dim, Np;
1726*8214e71cSJoe 
1727*8214e71cSJoe   PetscFunctionBegin;
1728*8214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
1729*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
1730*8214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
1731*8214e71cSJoe   PetscCall(VecCreate(PETSC_COMM_WORLD, &u));
1732*8214e71cSJoe   PetscCall(VecSetBlockSize(u, dim));
1733*8214e71cSJoe   PetscCall(VecSetSizes(u, 2 * Np * dim, PETSC_DECIDE));
1734*8214e71cSJoe   PetscCall(VecSetUp(u));
1735*8214e71cSJoe   PetscCall(TSSetSolution(ts, u));
1736*8214e71cSJoe   PetscCall(VecDestroy(&u));
1737*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1738*8214e71cSJoe }
1739*8214e71cSJoe 
1740*8214e71cSJoe static PetscErrorCode SetProblem(TS ts)
1741*8214e71cSJoe {
1742*8214e71cSJoe   AppCtx *user;
1743*8214e71cSJoe   DM      sw;
1744*8214e71cSJoe 
1745*8214e71cSJoe   PetscFunctionBegin;
1746*8214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
1747*8214e71cSJoe   PetscCall(DMGetApplicationContext(sw, (void **)&user));
1748*8214e71cSJoe   // Define unified system for (X, V)
1749*8214e71cSJoe   {
1750*8214e71cSJoe     Mat      J;
1751*8214e71cSJoe     PetscInt dim, Np;
1752*8214e71cSJoe 
1753*8214e71cSJoe     PetscCall(DMGetDimension(sw, &dim));
1754*8214e71cSJoe     PetscCall(DMSwarmGetLocalSize(sw, &Np));
1755*8214e71cSJoe     PetscCall(MatCreate(PETSC_COMM_WORLD, &J));
1756*8214e71cSJoe     PetscCall(MatSetSizes(J, 2 * Np * dim, 2 * Np * dim, PETSC_DECIDE, PETSC_DECIDE));
1757*8214e71cSJoe     PetscCall(MatSetBlockSize(J, 2 * dim));
1758*8214e71cSJoe     PetscCall(MatSetFromOptions(J));
1759*8214e71cSJoe     PetscCall(MatSetUp(J));
1760*8214e71cSJoe     PetscCall(TSSetRHSFunction(ts, NULL, RHSFunction, user));
1761*8214e71cSJoe     PetscCall(TSSetRHSJacobian(ts, J, J, RHSJacobian, user));
1762*8214e71cSJoe     PetscCall(MatDestroy(&J));
1763*8214e71cSJoe   }
1764*8214e71cSJoe   /* Define split system for X and V */
1765*8214e71cSJoe   {
1766*8214e71cSJoe     Vec             u;
1767*8214e71cSJoe     IS              isx, isv, istmp;
1768*8214e71cSJoe     const PetscInt *idx;
1769*8214e71cSJoe     PetscInt        dim, Np, rstart;
1770*8214e71cSJoe 
1771*8214e71cSJoe     PetscCall(TSGetSolution(ts, &u));
1772*8214e71cSJoe     PetscCall(DMGetDimension(sw, &dim));
1773*8214e71cSJoe     PetscCall(DMSwarmGetLocalSize(sw, &Np));
1774*8214e71cSJoe     PetscCall(VecGetOwnershipRange(u, &rstart, NULL));
1775*8214e71cSJoe     PetscCall(ISCreateStride(PETSC_COMM_WORLD, Np, (rstart / dim) + 0, 2, &istmp));
1776*8214e71cSJoe     PetscCall(ISGetIndices(istmp, &idx));
1777*8214e71cSJoe     PetscCall(ISCreateBlock(PETSC_COMM_WORLD, dim, Np, idx, PETSC_COPY_VALUES, &isx));
1778*8214e71cSJoe     PetscCall(ISRestoreIndices(istmp, &idx));
1779*8214e71cSJoe     PetscCall(ISDestroy(&istmp));
1780*8214e71cSJoe     PetscCall(ISCreateStride(PETSC_COMM_WORLD, Np, (rstart / dim) + 1, 2, &istmp));
1781*8214e71cSJoe     PetscCall(ISGetIndices(istmp, &idx));
1782*8214e71cSJoe     PetscCall(ISCreateBlock(PETSC_COMM_WORLD, dim, Np, idx, PETSC_COPY_VALUES, &isv));
1783*8214e71cSJoe     PetscCall(ISRestoreIndices(istmp, &idx));
1784*8214e71cSJoe     PetscCall(ISDestroy(&istmp));
1785*8214e71cSJoe     PetscCall(TSRHSSplitSetIS(ts, "position", isx));
1786*8214e71cSJoe     PetscCall(TSRHSSplitSetIS(ts, "momentum", isv));
1787*8214e71cSJoe     PetscCall(ISDestroy(&isx));
1788*8214e71cSJoe     PetscCall(ISDestroy(&isv));
1789*8214e71cSJoe     PetscCall(TSRHSSplitSetRHSFunction(ts, "position", NULL, RHSFunctionX, user));
1790*8214e71cSJoe     PetscCall(TSRHSSplitSetRHSFunction(ts, "momentum", NULL, RHSFunctionV, user));
1791*8214e71cSJoe   }
1792*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1793*8214e71cSJoe }
1794*8214e71cSJoe 
1795*8214e71cSJoe static PetscErrorCode DMSwarmTSRedistribute(TS ts)
1796*8214e71cSJoe {
1797*8214e71cSJoe   DM        sw;
1798*8214e71cSJoe   Vec       u;
1799*8214e71cSJoe   PetscReal t, maxt, dt;
1800*8214e71cSJoe   PetscInt  n, maxn;
1801*8214e71cSJoe 
1802*8214e71cSJoe   PetscFunctionBegin;
1803*8214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
1804*8214e71cSJoe   PetscCall(TSGetTime(ts, &t));
1805*8214e71cSJoe   PetscCall(TSGetMaxTime(ts, &maxt));
1806*8214e71cSJoe   PetscCall(TSGetTimeStep(ts, &dt));
1807*8214e71cSJoe   PetscCall(TSGetStepNumber(ts, &n));
1808*8214e71cSJoe   PetscCall(TSGetMaxSteps(ts, &maxn));
1809*8214e71cSJoe 
1810*8214e71cSJoe   PetscCall(TSReset(ts));
1811*8214e71cSJoe   PetscCall(TSSetDM(ts, sw));
1812*8214e71cSJoe   PetscCall(TSSetFromOptions(ts));
1813*8214e71cSJoe   PetscCall(TSSetTime(ts, t));
1814*8214e71cSJoe   PetscCall(TSSetMaxTime(ts, maxt));
1815*8214e71cSJoe   PetscCall(TSSetTimeStep(ts, dt));
1816*8214e71cSJoe   PetscCall(TSSetStepNumber(ts, n));
1817*8214e71cSJoe   PetscCall(TSSetMaxSteps(ts, maxn));
1818*8214e71cSJoe 
1819*8214e71cSJoe   PetscCall(CreateSolution(ts));
1820*8214e71cSJoe   PetscCall(SetProblem(ts));
1821*8214e71cSJoe   PetscCall(TSGetSolution(ts, &u));
1822*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1823*8214e71cSJoe }
1824*8214e71cSJoe 
1825*8214e71cSJoe PetscErrorCode line(PetscInt dim, PetscReal time, const PetscReal dummy[], PetscInt p, PetscScalar x[], void *ctx)
1826*8214e71cSJoe {
1827*8214e71cSJoe   DM        sw, cdm;
1828*8214e71cSJoe   PetscInt  Np;
1829*8214e71cSJoe   PetscReal low[2], high[2];
1830*8214e71cSJoe   AppCtx   *user = (AppCtx *)ctx;
1831*8214e71cSJoe 
1832*8214e71cSJoe   sw = user->swarm;
1833*8214e71cSJoe   PetscCall(DMSwarmGetCellDM(sw, &cdm));
1834*8214e71cSJoe   // Get the bounding box so we can equally space the particles
1835*8214e71cSJoe   PetscCall(DMGetLocalBoundingBox(cdm, low, high));
1836*8214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
1837*8214e71cSJoe   // shift it by h/2 so nothing is initialized directly on a boundary
1838*8214e71cSJoe   x[0] = ((high[0] - low[0]) / Np) * (p + 0.5);
1839*8214e71cSJoe   x[1] = 0.;
1840*8214e71cSJoe   return PETSC_SUCCESS;
1841*8214e71cSJoe }
1842*8214e71cSJoe 
1843b80bf5b1SJoe Pusztay /*
1844*8214e71cSJoe   InitializeSolveAndSwarm - Set the solution values to the swarm coordinates and velocities, and also possibly set the initial values.
1845*8214e71cSJoe 
1846*8214e71cSJoe   Input Parameters:
1847*8214e71cSJoe + ts         - The TS
1848*8214e71cSJoe - useInitial - Flag to also set the initial conditions to the current coodinates and velocities and setup the problem
1849*8214e71cSJoe 
1850*8214e71cSJoe   Output Parameters:
1851*8214e71cSJoe . u - The initialized solution vector
1852*8214e71cSJoe 
1853*8214e71cSJoe   Level: advanced
1854*8214e71cSJoe 
1855*8214e71cSJoe .seealso: InitializeSolve()
1856b80bf5b1SJoe Pusztay */
1857*8214e71cSJoe static PetscErrorCode InitializeSolveAndSwarm(TS ts, PetscBool useInitial)
1858d71ae5a4SJacob Faibussowitsch {
1859*8214e71cSJoe   DM       sw;
1860*8214e71cSJoe   Vec      u, gc, gv, gc0, gv0;
1861*8214e71cSJoe   IS       isx, isv;
1862*8214e71cSJoe   PetscInt dim;
1863*8214e71cSJoe   AppCtx  *user;
1864b80bf5b1SJoe Pusztay 
1865b80bf5b1SJoe Pusztay   PetscFunctionBeginUser;
1866*8214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
1867*8214e71cSJoe   PetscCall(DMGetApplicationContext(sw, &user));
1868*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
1869*8214e71cSJoe   if (useInitial) {
1870*8214e71cSJoe     PetscReal v0[2] = {1., 0.};
1871*8214e71cSJoe     if (user->perturbed_weights) {
1872*8214e71cSJoe       PetscCall(InitializeParticles_PerturbedWeights(sw, user));
1873*8214e71cSJoe     } else {
1874*8214e71cSJoe       PetscCall(DMSwarmComputeLocalSizeFromOptions(sw));
1875*8214e71cSJoe       PetscCall(DMSwarmInitializeCoordinates(sw));
1876*8214e71cSJoe       if (user->fake_1D) {
1877*8214e71cSJoe         PetscCall(InitializeVelocities_Fake1D(sw, user));
1878*8214e71cSJoe       } else {
1879*8214e71cSJoe         PetscCall(DMSwarmInitializeVelocitiesFromOptions(sw, v0));
1880*8214e71cSJoe       }
1881*8214e71cSJoe     }
1882*8214e71cSJoe     PetscCall(DMSwarmMigrate(sw, PETSC_TRUE));
1883*8214e71cSJoe     PetscCall(DMSwarmTSRedistribute(ts));
1884*8214e71cSJoe   }
1885*8214e71cSJoe   PetscCall(TSGetSolution(ts, &u));
1886*8214e71cSJoe   PetscCall(TSRHSSplitGetIS(ts, "position", &isx));
1887*8214e71cSJoe   PetscCall(TSRHSSplitGetIS(ts, "momentum", &isv));
1888*8214e71cSJoe   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, DMSwarmPICField_coor, &gc));
1889*8214e71cSJoe   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "initCoordinates", &gc0));
1890*8214e71cSJoe   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "velocity", &gv));
1891*8214e71cSJoe   PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "initVelocity", &gv0));
1892*8214e71cSJoe   if (useInitial) {
1893*8214e71cSJoe     PetscCall(VecCopy(gc, gc0));
1894*8214e71cSJoe     PetscCall(VecCopy(gv, gv0));
1895*8214e71cSJoe   }
1896*8214e71cSJoe   PetscCall(VecISCopy(u, isx, SCATTER_FORWARD, gc));
1897*8214e71cSJoe   PetscCall(VecISCopy(u, isv, SCATTER_FORWARD, gv));
1898*8214e71cSJoe   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, DMSwarmPICField_coor, &gc));
1899*8214e71cSJoe   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "initCoordinates", &gc0));
1900*8214e71cSJoe   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "velocity", &gv));
1901*8214e71cSJoe   PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "initVelocity", &gv0));
1902*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1903*8214e71cSJoe }
1904*8214e71cSJoe 
1905*8214e71cSJoe static PetscErrorCode InitializeSolve(TS ts, Vec u)
1906b80bf5b1SJoe Pusztay {
1907*8214e71cSJoe   PetscFunctionBegin;
1908*8214e71cSJoe   PetscCall(TSSetSolution(ts, u));
1909*8214e71cSJoe   PetscCall(InitializeSolveAndSwarm(ts, PETSC_TRUE));
1910*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1911b80bf5b1SJoe Pusztay }
1912b80bf5b1SJoe Pusztay 
1913*8214e71cSJoe static PetscErrorCode ComputeError(TS ts, Vec U, Vec E)
1914*8214e71cSJoe {
1915*8214e71cSJoe   MPI_Comm           comm;
1916*8214e71cSJoe   DM                 sw;
1917*8214e71cSJoe   AppCtx            *user;
1918*8214e71cSJoe   const PetscScalar *u;
1919*8214e71cSJoe   const PetscReal   *coords, *vel;
1920*8214e71cSJoe   PetscScalar       *e;
1921*8214e71cSJoe   PetscReal          t;
1922*8214e71cSJoe   PetscInt           dim, Np, p;
1923b80bf5b1SJoe Pusztay 
1924*8214e71cSJoe   PetscFunctionBeginUser;
1925*8214e71cSJoe   PetscCall(PetscObjectGetComm((PetscObject)ts, &comm));
1926*8214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
1927*8214e71cSJoe   PetscCall(DMGetApplicationContext(sw, &user));
1928*8214e71cSJoe   PetscCall(DMGetDimension(sw, &dim));
1929*8214e71cSJoe   PetscCall(TSGetSolveTime(ts, &t));
1930*8214e71cSJoe   PetscCall(VecGetArray(E, &e));
1931*8214e71cSJoe   PetscCall(VecGetArrayRead(U, &u));
1932*8214e71cSJoe   PetscCall(DMSwarmGetLocalSize(sw, &Np));
1933*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
1934*8214e71cSJoe   PetscCall(DMSwarmGetField(sw, "initVelocity", NULL, NULL, (void **)&vel));
1935*8214e71cSJoe   Np /= 2 * dim;
1936*8214e71cSJoe   for (p = 0; p < Np; ++p) {
1937*8214e71cSJoe     /* TODO generalize initial conditions and project into plane instead of assuming x-y */
1938*8214e71cSJoe     const PetscReal    r0    = DMPlex_NormD_Internal(dim, &coords[p * dim]);
1939*8214e71cSJoe     const PetscReal    th0   = PetscAtan2Real(coords[p * dim + 1], coords[p * dim + 0]);
1940*8214e71cSJoe     const PetscReal    v0    = DMPlex_NormD_Internal(dim, &vel[p * dim]);
1941*8214e71cSJoe     const PetscReal    omega = v0 / r0;
1942*8214e71cSJoe     const PetscReal    ct    = PetscCosReal(omega * t + th0);
1943*8214e71cSJoe     const PetscReal    st    = PetscSinReal(omega * t + th0);
1944*8214e71cSJoe     const PetscScalar *x     = &u[(p * 2 + 0) * dim];
1945*8214e71cSJoe     const PetscScalar *v     = &u[(p * 2 + 1) * dim];
1946*8214e71cSJoe     const PetscReal    xe[3] = {r0 * ct, r0 * st, 0.0};
1947*8214e71cSJoe     const PetscReal    ve[3] = {-v0 * st, v0 * ct, 0.0};
1948*8214e71cSJoe     PetscInt           d;
1949*8214e71cSJoe 
1950*8214e71cSJoe     for (d = 0; d < dim; ++d) {
1951*8214e71cSJoe       e[(p * 2 + 0) * dim + d] = x[d] - xe[d];
1952*8214e71cSJoe       e[(p * 2 + 1) * dim + d] = v[d] - ve[d];
1953b80bf5b1SJoe Pusztay     }
1954*8214e71cSJoe     if (user->error) {
1955*8214e71cSJoe       const PetscReal en   = 0.5 * DMPlex_DotRealD_Internal(dim, v, v);
1956*8214e71cSJoe       const PetscReal exen = 0.5 * PetscSqr(v0);
1957*8214e71cSJoe       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)));
1958b80bf5b1SJoe Pusztay     }
1959*8214e71cSJoe   }
1960*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initCoordinates", NULL, NULL, (void **)&coords));
1961*8214e71cSJoe   PetscCall(DMSwarmRestoreField(sw, "initVelocity", NULL, NULL, (void **)&vel));
1962*8214e71cSJoe   PetscCall(VecRestoreArrayRead(U, &u));
1963*8214e71cSJoe   PetscCall(VecRestoreArray(E, &e));
1964*8214e71cSJoe   PetscFunctionReturn(PETSC_SUCCESS);
1965*8214e71cSJoe }
1966*8214e71cSJoe 
1967*8214e71cSJoe static PetscErrorCode MigrateParticles(TS ts)
1968*8214e71cSJoe {
1969*8214e71cSJoe   DM               sw, cdm;
1970*8214e71cSJoe   const PetscReal *L;
1971*8214e71cSJoe 
1972*8214e71cSJoe   PetscFunctionBeginUser;
1973*8214e71cSJoe   PetscCall(TSGetDM(ts, &sw));
1974*8214e71cSJoe   PetscCall(DMViewFromOptions(sw, NULL, "-migrate_view_pre"));
1975*8214e71cSJoe   {
1976*8214e71cSJoe     Vec        u, gc, gv, position, momentum;
1977*8214e71cSJoe     IS         isx, isv;
1978*8214e71cSJoe     PetscReal *pos, *mom;
1979*8214e71cSJoe 
1980*8214e71cSJoe     PetscCall(TSGetSolution(ts, &u));
1981*8214e71cSJoe     PetscCall(TSRHSSplitGetIS(ts, "position", &isx));
1982*8214e71cSJoe     PetscCall(TSRHSSplitGetIS(ts, "momentum", &isv));
1983*8214e71cSJoe     PetscCall(VecGetSubVector(u, isx, &position));
1984*8214e71cSJoe     PetscCall(VecGetSubVector(u, isv, &momentum));
1985*8214e71cSJoe     PetscCall(VecGetArray(position, &pos));
1986*8214e71cSJoe     PetscCall(VecGetArray(momentum, &mom));
1987*8214e71cSJoe     PetscCall(DMSwarmCreateGlobalVectorFromField(sw, DMSwarmPICField_coor, &gc));
1988*8214e71cSJoe     PetscCall(DMSwarmCreateGlobalVectorFromField(sw, "velocity", &gv));
1989*8214e71cSJoe     PetscCall(VecISCopy(u, isx, SCATTER_REVERSE, gc));
1990*8214e71cSJoe     PetscCall(VecISCopy(u, isv, SCATTER_REVERSE, gv));
1991*8214e71cSJoe 
1992*8214e71cSJoe     PetscCall(DMSwarmGetCellDM(sw, &cdm));
1993*8214e71cSJoe     PetscCall(DMGetPeriodicity(cdm, NULL, NULL, &L));
1994*8214e71cSJoe     if ((L[0] || L[1]) >= 0.) {
1995*8214e71cSJoe       PetscReal *x, *v, upper[3], lower[3];
1996*8214e71cSJoe       PetscInt   Np, dim;
1997*8214e71cSJoe 
1998*8214e71cSJoe       PetscCall(DMSwarmGetLocalSize(sw, &Np));
1999*8214e71cSJoe       PetscCall(DMGetDimension(cdm, &dim));
2000*8214e71cSJoe       PetscCall(DMGetBoundingBox(cdm, lower, upper));
2001*8214e71cSJoe       PetscCall(VecGetArray(gc, &x));
2002*8214e71cSJoe       PetscCall(VecGetArray(gv, &v));
2003*8214e71cSJoe       for (PetscInt p = 0; p < Np; ++p) {
2004*8214e71cSJoe         for (PetscInt d = 0; d < dim; ++d) {
2005*8214e71cSJoe           if (pos[p * dim + d] < lower[d]) {
2006*8214e71cSJoe             x[p * dim + d] = pos[p * dim + d] + (upper[d] - lower[d]);
2007*8214e71cSJoe           } else if (pos[p * dim + d] > upper[d]) {
2008*8214e71cSJoe             x[p * dim + d] = pos[p * dim + d] - (upper[d] - lower[d]);
2009*8214e71cSJoe           } else {
2010*8214e71cSJoe             x[p * dim + d] = pos[p * dim + d];
2011*8214e71cSJoe           }
2012*8214e71cSJoe           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]);
2013*8214e71cSJoe           v[p * dim + d] = mom[p * dim + d];
2014*8214e71cSJoe         }
2015*8214e71cSJoe       }
2016*8214e71cSJoe       PetscCall(VecRestoreArray(gc, &x));
2017*8214e71cSJoe       PetscCall(VecRestoreArray(gv, &v));
2018*8214e71cSJoe     }
2019*8214e71cSJoe     PetscCall(VecRestoreArray(position, &pos));
2020*8214e71cSJoe     PetscCall(VecRestoreArray(momentum, &mom));
2021*8214e71cSJoe     PetscCall(VecRestoreSubVector(u, isx, &position));
2022*8214e71cSJoe     PetscCall(VecRestoreSubVector(u, isv, &momentum));
2023*8214e71cSJoe     PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, "velocity", &gv));
2024*8214e71cSJoe     PetscCall(DMSwarmDestroyGlobalVectorFromField(sw, DMSwarmPICField_coor, &gc));
2025*8214e71cSJoe   }
2026*8214e71cSJoe   PetscCall(DMSwarmMigrate(sw, PETSC_TRUE));
2027*8214e71cSJoe   PetscCall(DMSwarmTSRedistribute(ts));
2028*8214e71cSJoe   PetscCall(InitializeSolveAndSwarm(ts, PETSC_FALSE));
20293ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
2030b80bf5b1SJoe Pusztay }
2031b80bf5b1SJoe Pusztay 
2032d71ae5a4SJacob Faibussowitsch int main(int argc, char **argv)
2033d71ae5a4SJacob Faibussowitsch {
2034b80bf5b1SJoe Pusztay   DM        dm, sw;
2035*8214e71cSJoe   TS        ts;
2036*8214e71cSJoe   Vec       u;
2037*8214e71cSJoe   PetscReal dt;
2038*8214e71cSJoe   PetscInt  maxn;
2039b80bf5b1SJoe Pusztay   AppCtx    user;
2040b80bf5b1SJoe Pusztay 
20419566063dSJacob Faibussowitsch   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
2042*8214e71cSJoe   PetscCall(ProcessOptions(PETSC_COMM_WORLD, &user));
2043*8214e71cSJoe   PetscCall(PetscBagCreate(PETSC_COMM_SELF, sizeof(Parameter), &user.bag));
2044*8214e71cSJoe   PetscCall(CreateMesh(PETSC_COMM_WORLD, &user, &dm));
2045*8214e71cSJoe   PetscCall(CreatePoisson(dm, &user));
2046*8214e71cSJoe   PetscCall(CreateSwarm(dm, &user, &sw));
2047*8214e71cSJoe   PetscCall(SetupParameters(PETSC_COMM_WORLD, &user));
2048*8214e71cSJoe   PetscCall(InitializeConstants(sw, &user));
2049*8214e71cSJoe   PetscCall(DMSetApplicationContext(sw, &user));
2050b80bf5b1SJoe Pusztay 
2051*8214e71cSJoe   PetscCall(TSCreate(PETSC_COMM_WORLD, &ts));
2052*8214e71cSJoe   PetscCall(TSSetProblemType(ts, TS_NONLINEAR));
20539566063dSJacob Faibussowitsch   PetscCall(TSSetDM(ts, sw));
2054*8214e71cSJoe   PetscCall(TSSetMaxTime(ts, 0.1));
2055*8214e71cSJoe   PetscCall(TSSetTimeStep(ts, 0.00001));
2056*8214e71cSJoe   PetscCall(TSSetMaxSteps(ts, 100));
2057*8214e71cSJoe   PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_MATCHSTEP));
2058*8214e71cSJoe 
2059*8214e71cSJoe   if (user.efield_monitor) PetscCall(TSMonitorSet(ts, MonitorEField, &user, NULL));
2060*8214e71cSJoe   if (user.initial_monitor) PetscCall(TSMonitorSet(ts, MonitorInitialConditions, &user, NULL));
2061*8214e71cSJoe   if (user.monitor_positions) PetscCall(TSMonitorSet(ts, MonitorPositions_2D, &user, NULL));
2062*8214e71cSJoe   if (user.poisson_monitor) PetscCall(TSMonitorSet(ts, MonitorPoisson, &user, NULL));
2063*8214e71cSJoe 
2064*8214e71cSJoe   PetscCall(TSSetFromOptions(ts));
2065*8214e71cSJoe   PetscCall(TSGetTimeStep(ts, &dt));
2066*8214e71cSJoe   PetscCall(TSGetMaxSteps(ts, &maxn));
2067*8214e71cSJoe   user.steps    = maxn;
2068*8214e71cSJoe   user.stepSize = dt;
2069*8214e71cSJoe   PetscCall(SetupContext(dm, sw, &user));
2070*8214e71cSJoe   PetscCall(DMSwarmVectorDefineField(sw, "velocity"));
2071*8214e71cSJoe   PetscCall(TSSetComputeInitialCondition(ts, InitializeSolve));
2072*8214e71cSJoe   PetscCall(TSSetComputeExactError(ts, ComputeError));
2073*8214e71cSJoe   PetscCall(TSSetPostStep(ts, MigrateParticles));
2074*8214e71cSJoe   PetscCall(CreateSolution(ts));
2075*8214e71cSJoe   PetscCall(TSGetSolution(ts, &u));
2076*8214e71cSJoe   PetscCall(TSComputeInitialCondition(ts, u));
2077*8214e71cSJoe   PetscCall(CheckNonNegativeWeights(sw, &user));
2078*8214e71cSJoe   PetscCall(TSSolve(ts, NULL));
2079*8214e71cSJoe 
20809566063dSJacob Faibussowitsch   PetscCall(SNESDestroy(&user.snes));
20819566063dSJacob Faibussowitsch   PetscCall(TSDestroy(&ts));
20829566063dSJacob Faibussowitsch   PetscCall(DMDestroy(&sw));
20839566063dSJacob Faibussowitsch   PetscCall(DMDestroy(&dm));
2084*8214e71cSJoe   PetscCall(DestroyContext(&user));
20859566063dSJacob Faibussowitsch   PetscCall(PetscFinalize());
2086b122ec5aSJacob Faibussowitsch   return 0;
2087b80bf5b1SJoe Pusztay }
2088b80bf5b1SJoe Pusztay 
2089b80bf5b1SJoe Pusztay /*TEST
2090b80bf5b1SJoe Pusztay 
2091b80bf5b1SJoe Pusztay    build:
2092*8214e71cSJoe     requires: !complex double
2093*8214e71cSJoe 
2094*8214e71cSJoe   # This tests that we can put particles in a box and compute the Coulomb force
2095*8214e71cSJoe   # Recommend -draw_size 500,500
2096*8214e71cSJoe    testset:
2097*8214e71cSJoe      requires: defined(PETSC_HAVE_EXECUTABLE_EXPORT)
2098*8214e71cSJoe      args: -dm_plex_dim 2 -fake_1D -dm_plex_simplex 0 -dm_plex_box_faces 20,1 \
2099*8214e71cSJoe              -dm_plex_box_lower 0,-1 -dm_plex_box_upper 12.5664,1 \
2100b80bf5b1SJoe Pusztay              -dm_plex_box_bd periodic,none \
2101*8214e71cSJoe            -dm_swarm_coordinate_density constant -dm_swarm_num_particles 100 \
2102*8214e71cSJoe            -sigma 1.0e-8 -timeScale 2.0e-14 \
2103*8214e71cSJoe            -ts_type basicsymplectic -ts_basicsymplectic_type 1 \
2104*8214e71cSJoe              -ts_monitor_sp_swarm -ts_monitor_sp_swarm_retain 0 -ts_monitor_sp_swarm_phase 0 \
2105*8214e71cSJoe            -output_step 50 -check_vel_res
2106*8214e71cSJoe      test:
2107*8214e71cSJoe        suffix: none_1d
2108*8214e71cSJoe        args: -em_type none -error
2109*8214e71cSJoe      test:
2110*8214e71cSJoe        suffix: coulomb_1d
2111*8214e71cSJoe        args: -em_type coulomb
2112*8214e71cSJoe 
2113*8214e71cSJoe    # for viewers
2114*8214e71cSJoe    #-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
2115*8214e71cSJoe    testset:
2116*8214e71cSJoe      nsize: {{1 2}}
2117*8214e71cSJoe      requires: defined(PETSC_HAVE_EXECUTABLE_EXPORT)
2118*8214e71cSJoe      args: -dm_plex_dim 2 -fake_1D -dm_plex_simplex 0 -dm_plex_box_faces 36,1 \
2119*8214e71cSJoe              -dm_plex_box_lower 0.,-0.5 -dm_plex_box_upper 12.5664,0.5 \
2120*8214e71cSJoe              -dm_plex_box_bd periodic,none \
2121*8214e71cSJoe            -vdm_plex_dim 1 -vdm_plex_simplex 0 -vdm_plex_box_faces 10 \
2122*8214e71cSJoe              -vdm_plex_box_lower -3 -vdm_plex_box_upper 3 \
2123*8214e71cSJoe            -dm_swarm_num_species 1 -dm_swarm_num_particles 360 \
2124*8214e71cSJoe            -twostream -charges -1.,1. -sigma 1.0e-8 \
2125*8214e71cSJoe              -cosine_coefficients 0.01,0.5 -perturbed_weights -total_weight 1. \
2126*8214e71cSJoe            -ts_type basicsymplectic -ts_basicsymplectic_type 2 \
2127*8214e71cSJoe              -ts_dt 0.01 -ts_max_time 5 -ts_max_steps 10 \
2128*8214e71cSJoe            -em_snes_atol 1.e-15 -em_snes_error_if_not_converged -em_ksp_error_if_not_converged \
2129*8214e71cSJoe            -output_step 1 -check_vel_res
2130*8214e71cSJoe      test:
2131*8214e71cSJoe        suffix: two_stream_c0
2132*8214e71cSJoe        args: -em_type primal -petscfe_default_quadrature_order 2 -petscspace_degree 2 -em_pc_type svd
2133*8214e71cSJoe      test:
2134*8214e71cSJoe        suffix: two_stream_rt
2135*8214e71cSJoe        requires: superlu_dist
2136*8214e71cSJoe        args: -em_type mixed \
2137*8214e71cSJoe                -potential_petscspace_degree 0 \
2138*8214e71cSJoe                -potential_petscdualspace_lagrange_use_moments \
2139*8214e71cSJoe                -potential_petscdualspace_lagrange_moment_order 2 \
2140*8214e71cSJoe                -field_petscspace_degree 2 -field_petscfe_default_quadrature_order 1 \
2141*8214e71cSJoe                -field_petscspace_type sum \
2142*8214e71cSJoe                  -field_petscspace_variables 2 \
2143*8214e71cSJoe                  -field_petscspace_components 2 \
2144*8214e71cSJoe                  -field_petscspace_sum_spaces 2 \
2145*8214e71cSJoe                  -field_petscspace_sum_concatenate true \
2146*8214e71cSJoe                  -field_sumcomp_0_petscspace_variables 2 \
2147*8214e71cSJoe                  -field_sumcomp_0_petscspace_type tensor \
2148*8214e71cSJoe                  -field_sumcomp_0_petscspace_tensor_spaces 2 \
2149*8214e71cSJoe                  -field_sumcomp_0_petscspace_tensor_uniform false \
2150*8214e71cSJoe                  -field_sumcomp_0_tensorcomp_0_petscspace_degree 1 \
2151*8214e71cSJoe                  -field_sumcomp_0_tensorcomp_1_petscspace_degree 0 \
2152*8214e71cSJoe                  -field_sumcomp_1_petscspace_variables 2 \
2153*8214e71cSJoe                  -field_sumcomp_1_petscspace_type tensor \
2154*8214e71cSJoe                  -field_sumcomp_1_petscspace_tensor_spaces 2 \
2155*8214e71cSJoe                  -field_sumcomp_1_petscspace_tensor_uniform false \
2156*8214e71cSJoe                  -field_sumcomp_1_tensorcomp_0_petscspace_degree 0 \
2157*8214e71cSJoe                  -field_sumcomp_1_tensorcomp_1_petscspace_degree 1 \
2158*8214e71cSJoe                -field_petscdualspace_form_degree -1 \
2159*8214e71cSJoe                -field_petscdualspace_order 1 \
2160*8214e71cSJoe                -field_petscdualspace_lagrange_trimmed true \
2161*8214e71cSJoe              -em_snes_error_if_not_converged \
2162*8214e71cSJoe              -em_ksp_type preonly -em_ksp_error_if_not_converged \
2163*8214e71cSJoe              -em_pc_type fieldsplit -em_pc_fieldsplit_type schur \
2164*8214e71cSJoe                -em_pc_fieldsplit_schur_fact_type full -em_pc_fieldsplit_schur_precondition full \
2165*8214e71cSJoe                -em_fieldsplit_field_pc_type lu \
2166*8214e71cSJoe                  -em_fieldsplit_field_pc_factor_mat_solver_type superlu_dist \
2167*8214e71cSJoe                -em_fieldsplit_potential_pc_type svd
2168*8214e71cSJoe 
2169*8214e71cSJoe    # For verification, we use
2170*8214e71cSJoe    # -dm_plex_box_faces 100,1 -vdm_plex_box_faces 8000 -dm_swarm_num_particles 800000
2171*8214e71cSJoe    # -ts_monitor_sp_swarm_multi_species 0 -ts_monitor_sp_swarm_retain 0 -ts_monitor_sp_swarm_phase 1 -draw_size 500,500
2172*8214e71cSJoe    testset:
2173*8214e71cSJoe      nsize: {{1 2}}
2174*8214e71cSJoe      requires: defined(PETSC_HAVE_EXECUTABLE_EXPORT)
2175*8214e71cSJoe      args: -dm_plex_dim 2 -fake_1D -dm_plex_simplex 0 -dm_plex_box_faces 10,1 \
2176*8214e71cSJoe              -dm_plex_box_lower 0.,-0.5 -dm_plex_box_upper 12.5664,0.5 \
2177*8214e71cSJoe              -dm_plex_box_bd periodic,none \
2178*8214e71cSJoe            -vdm_plex_dim 1 -vdm_plex_simplex 0 -vdm_plex_box_faces 10 \
2179*8214e71cSJoe              -vdm_plex_box_lower -10 -vdm_plex_box_upper 10 \
2180*8214e71cSJoe            -dm_swarm_num_species 1 -dm_swarm_num_particles 100 \
2181*8214e71cSJoe            -charges -1.,1. \
2182*8214e71cSJoe              -cosine_coefficients 0.01,0.5 -perturbed_weights -total_weight 1. \
2183*8214e71cSJoe            -ts_type basicsymplectic -ts_basicsymplectic_type 1 \
2184*8214e71cSJoe              -ts_dt 0.03 -ts_max_time 500 -ts_max_steps 1 \
2185*8214e71cSJoe            -em_snes_atol 1.e-12 -em_snes_error_if_not_converged -em_ksp_error_if_not_converged \
2186*8214e71cSJoe            -output_step 1 -check_vel_res
2187*8214e71cSJoe 
2188*8214e71cSJoe      test:
2189*8214e71cSJoe        suffix: uniform_equilibrium_1d
2190*8214e71cSJoe        args: -cosine_coefficients 0.0,0.5 -em_type primal -petscspace_degree 1 -em_pc_type svd
2191*8214e71cSJoe      test:
2192*8214e71cSJoe        suffix: uniform_primal_1d
2193*8214e71cSJoe        args: -em_type primal -petscspace_degree 1 -em_pc_type svd
2194*8214e71cSJoe      test:
2195*8214e71cSJoe        requires: superlu_dist
2196*8214e71cSJoe        suffix: uniform_mixed_1d
2197*8214e71cSJoe        args: -em_type mixed \
2198*8214e71cSJoe                -potential_petscspace_degree 0 \
2199*8214e71cSJoe                -potential_petscdualspace_lagrange_use_moments \
2200*8214e71cSJoe                -potential_petscdualspace_lagrange_moment_order 2 \
2201*8214e71cSJoe                -field_petscspace_degree 2 -field_petscfe_default_quadrature_order 1 \
2202*8214e71cSJoe                -field_petscspace_type sum \
2203*8214e71cSJoe                  -field_petscspace_variables 2 \
2204*8214e71cSJoe                  -field_petscspace_components 2 \
2205*8214e71cSJoe                  -field_petscspace_sum_spaces 2 \
2206*8214e71cSJoe                  -field_petscspace_sum_concatenate true \
2207*8214e71cSJoe                  -field_sumcomp_0_petscspace_variables 2 \
2208*8214e71cSJoe                  -field_sumcomp_0_petscspace_type tensor \
2209*8214e71cSJoe                  -field_sumcomp_0_petscspace_tensor_spaces 2 \
2210*8214e71cSJoe                  -field_sumcomp_0_petscspace_tensor_uniform false \
2211*8214e71cSJoe                  -field_sumcomp_0_tensorcomp_0_petscspace_degree 1 \
2212*8214e71cSJoe                  -field_sumcomp_0_tensorcomp_1_petscspace_degree 0 \
2213*8214e71cSJoe                  -field_sumcomp_1_petscspace_variables 2 \
2214*8214e71cSJoe                  -field_sumcomp_1_petscspace_type tensor \
2215*8214e71cSJoe                  -field_sumcomp_1_petscspace_tensor_spaces 2 \
2216*8214e71cSJoe                  -field_sumcomp_1_petscspace_tensor_uniform false \
2217*8214e71cSJoe                  -field_sumcomp_1_tensorcomp_0_petscspace_degree 0 \
2218*8214e71cSJoe                  -field_sumcomp_1_tensorcomp_1_petscspace_degree 1 \
2219*8214e71cSJoe                -field_petscdualspace_form_degree -1 \
2220*8214e71cSJoe                -field_petscdualspace_order 1 \
2221*8214e71cSJoe                -field_petscdualspace_lagrange_trimmed true \
2222*8214e71cSJoe              -em_snes_error_if_not_converged \
2223*8214e71cSJoe              -em_ksp_type preonly -em_ksp_error_if_not_converged \
2224*8214e71cSJoe              -em_pc_type fieldsplit -em_pc_fieldsplit_type schur \
2225*8214e71cSJoe                -em_pc_fieldsplit_schur_fact_type full -em_pc_fieldsplit_schur_precondition full \
2226*8214e71cSJoe                -em_fieldsplit_field_pc_type lu \
2227*8214e71cSJoe                  -em_fieldsplit_field_pc_factor_mat_solver_type superlu_dist \
2228*8214e71cSJoe                -em_fieldsplit_potential_pc_type svd
2229*8214e71cSJoe 
2230b80bf5b1SJoe Pusztay TEST*/
2231