Particle species initialization
Introduction
Adding particle data to a ZPIC simulation is done using the species and n_species parameters of the sim_new() function. The species parameter is an array of t_species structure. Before calling sim_new() each item in this array must be initialized using the spec_new() function:
void spec_new( t_species* spec, char name[], float m_q, int ppc[],
float ufl[], float uth[], int nx[], float box[], float dt,
t_density* density );
The parameters for this function are as follows:
spec- Pointer tot_speciesvariable being initialized.name- Name for the species. This is used for diagnostic information only.m_q- Mass over charge ratio for particles in the species in simulation units (e.g. -1 for electrons)ppc- Reference number of particles per cell in each direction (scalarintin 1D codes)ufl- Initial fluid velocity for particle species (float[3]), set to NULL to use a 0 fluid velocityuth- Initial thermal velocity for particle species (float[3]), set to NULL to use a 0 thermal velocitynx- Simulation grid sizebox- Simulation physical box size in simulation unitsdt- Simulation time step in simulation unitsdensity- Pointer tot_densityvariable describing initial charge density, set toNULLto use a uniform density.
The following example adds a particle species with “step like” density profile:
void sim_init( t_simulation* sim ){
// Simulation box
int nx[2] = { 1500, 128 };
float box[2] = { 30.0, 25.6 };
// Initialize particles
const int n_species = 1;
// Use 4x2 particles per cell
int ppc[] = {4,2};
// Density profile
t_density density = { .type = STEP, .start = 30.0 };
t_species* species = (t_species *) malloc( n_species * sizeof( t_species ));
spec_new( &species[0], "electrons", -1.0, ppc, NULL, NULL, nx, box, dt, &density );
//...
// Initialize Simulation data
sim_new( sim, nx, box, dt, tmax, ndump, species, n_species );
}
All particles in the same species share these parameters; if you require any of these parameters to change (e.g. initial fluid velocity) you will need to use additional species.
Particle injection - 1D
In ZPIC all particles in a species have the same charge. Initializing a given density profile is done by varying the number of particles placed inside each simulation cell. The ppc parameter of the spec_new() function discussed above sets the reference number of particles for a density of 1 (in simulation units). The individual particle charge $q_p$ will respect:
with $\Delta x$ being the cell size, meaning that a cell with $ppc$ particles will have a charge density of $n$.
To choose a different initial density profile we need to reate a t_density variable specifying the density profile parameters and use it in the density parameter of our spec_new() call. The t_density structure has the following fields:
| Density parameters | |
|---|---|
| n | Reference density (default 1.0) |
| type | Density profile type: UNIFORM (default), STEP, SLAB, RAMP, or CUSTOM |
| start | Start of the particle injection region (STEP, SLAB, RAMP) |
| end | End of the particle injection region (SLAB,RAMP) |
| ramp[2] | Density at the start (ramp[0]) and end (ramp[1]) of the particle injection region for type RAMP, normalized to $n$ |
| custom | Pointer to a function defining an arbitrary density profile, normalized to $n$ |
After defining a t_density variable, this must be supplied (by reference) as the last parameter of the spec_new() function:
// Uniform density example
t_density density = { .type = UNIFORM };
// (...)
spec_new( &species[0], "electrons", -1.0, ppc, NULL, NULL, nx, box, dt, &density );
Density profile types
Uniform
Injects a uniform density n.
// Uniform density example
t_density density = { .n = 1, .type = UNIFORM };
Step
Injects a step like density profile starting at position start, such that the density before this position is set to 0, and after this position it is set to n.
// Step density example
t_density density = { .type = STEP, .start = 17.5 };
Slab
Injects a density profile that has a value of n between start and end and 0 otherwise.
// Slab density example
t_density density = { .type = SLAB, .start = 17.5, .end = 22.5 };
Ramp
Injects a density profile that varies linearly from ramp[0] at position start to ramp[1] at position end. The values of the ramp parameter are normalized to the reference density n, meaning that ramp = 1.0, corresponds to a density n.
// Ramp density example
t_density density = {
.type = RAMP,
.start = 17.5,
.end = 22.5,
.ramp = {1.0, 2.0}
};
Custom
The custom type injects a density profile set by a user defined function. The function must accept two parameters: one parameter of type float (the position at which the density is to be evaluated in simulation units) and a void * pointer (which can be used to send additional data to the function). It must return the density value as a value of type float. Note that density values are normalized to n.
// Custom density example
// The density will oscillate between 0 and 0.1
// note the n parameter below
float custom_n0( float x, void *data ) {
return sin(x/M_PI)*sin(x/M_PI);
}
//(...)
t_density density = {
.type = CUSTOM,
.n = 0.1,
.custom = &custom_n0,
.custom_data = NULL,
};
Particle Injection - 2D
Particle injection in 2D is similar to 1D, with a couple of differences:
- The
ppcparameter is a two element integer array specifying the number of particles per cell in both directions. The total number of particles per cell will be the product of these 2 numbers. - The
RAMPdensity profile is not available (use theCUSTOMprofile instead) - The
CUSTOMdensity profile requires that the density can be described by a separable function in the $x$ and $y$ coordinates, i.e., $n(x,y) = n_x(x) \times n_y(y)$, and these two functions are defined separably.
The t_density structure in 2D has the following fields:
| Density parameters | |
|---|---|
| n | Reference density (default 1.0) |
| type | Density profile type: UNIFORM (default), STEP, SLAB, or CUSTOM |
| start | Start of the particle injection region (STEP, SLAB) |
| end | End of the particle injection region (SLAB) |
| custom_x | Pointer to a function defining the $n_x(x)$ function of a custom density profile $n(x,y) = n_x(x) \times n_y(y)$, normalized to n |
| custom_y | Pointer to a function defining the $n_y(y)$ function of a custom density profile $n(x,y) = n_x(x) \times n_y(y)$, normalized to n |
Density profile types
The types UNIFORM, STEP, and SLAB work exactly the same way as in the 1D version.
Custom
The custom type injects a density profile set by two user defined functions. The functions must accept two parameters: one parameter of type float (the position $x$ or $y$ at which the density is to be evaluated in simulation units) and a void * pointer (which can be used to send additional data to the function). They must return the density value as a value of type float. Note that density values are normalized to n.
// 2D Custom density example
// The 2D density will be the product of nx * ny
// It will oscillate between 0 and 0.1 - note the n parameter below
float nx( float x, void *data ) {
return sin(x/M_PI)*sin(x/M_PI);
}
float ny( float y, void *data ) {
return cos(y/M_PI)*cos(y/M_PI);
}
(...)
t_density density = {
.type = CUSTOM,
.n = 0.1,
.custom_x = &nx,
.custom_y = &ny,
};