Particles
In the PIC method, the simulation grid is filled with macro-particles, which respresent a large number of real particles. They are described primarily by a position on the grid, and a momentum, charge, mass and weight. In EPOCH, the first few variables describe a single real particle within the macro-particle, and the weight describes how many real particles the macro-particle represents.
Macro-particles are split between different species, set by the species blocks
in the input deck. All macro-particles present on a single rank are contained
within the species_list
variable, which contains lists of particles for all
species present, along with information about each species.
In short, the data is distributed among types as follows:
- Particle type: contains information about a single macro-particle.
- Particle list: a linked list object, containing a list of macro-particles.
- Particle species: contains information about a set of particles as a whole, and a corresponding particle list.
- Species list: an array containing all the particle species.
The particle data-type
Particles are represented as linked lists of Fortran TYPES. The definition can
be found in shared_data.F90
, which is shared globally among all modules.
Hence, any module can use a particle data-type.
In this data-type, you will notice that many parameters are locked behind pre-processor flags, and must be manually switched on through the Makefile. This is because as particles move around the grid, they must be transferred from processor to processor as they move in and out of cells controlled by each rank. This MPI transfer is a major bottleneck for the code, so by default, EPOCH assigns the minimum amount of information to each particle, to minimise the rank-to-rank data transfer.
The datatype is shown here:
TYPE particle
REAL(num), DIMENSION(3) :: part_p
REAL(num), DIMENSION(c_ndims) :: part_pos
#if !defined(PER_SPECIES_WEIGHT) || defined(PHOTONS)
REAL(num) :: weight
#endif
#ifdef DELTAF_METHOD
REAL(num) :: pvol
#endif
#ifdef PER_PARTICLE_CHARGE_MASS
REAL(num) :: charge
REAL(num) :: mass
#endif
TYPE(particle), POINTER :: next, prev
#ifdef PARTICLE_DEBUG
INTEGER :: processor
INTEGER :: processor_at_t0
#endif
#ifdef PARTICLE_ID4
INTEGER :: id
#elif PARTICLE_ID
INTEGER(i8) :: id
#endif
#ifdef COLLISIONS_TEST
INTEGER :: coll_count
#endif
#ifdef WORK_DONE_INTEGRATED
REAL(num) :: work_x
REAL(num) :: work_y
REAL(num) :: work_z
REAL(num) :: work_x_total
REAL(num) :: work_y_total
REAL(num) :: work_z_total
#endif
#ifdef PHOTONS
REAL(num) :: optical_depth
#endif
#if defined(PHOTONS) || defined(BREMSSTRAHLUNG)
REAL(num) :: particle_energy
#endif
#if defined(PHOTONS) && defined(TRIDENT_PHOTONS)
REAL(num) :: optical_depth_tri
#endif
#ifdef BREMSSTRAHLUNG
REAL(num) :: optical_depth_bremsstrahlung
#endif
#if defined(PROBE_TIME)
REAL(num) :: probe_time
#endif
END TYPE particle
And the descriptions are
REAL(num) :: part_p(3)
- The particle momentum. Always dimension 3 even in 1D and 2D codes. Describes the momentum of a single particle within the macro-particle.REAL(num) :: part_pos(ndims)
- The particle position. Has the same dimensions as that of the code.REAL(num) :: weight
- The particle weight if the code is running with per particle weighting (otherwise weight is a species parameter).REAL(num) :: charge
- The particle charge in Coulombs if the code is running with per particle charge (otherwise charge is a species parameter).REAL(nun) :: mass
- The particle mass in kilograms if the code is running with per particle mass (otherwise mass is a species parameter).TYPE(particle), POINTER :: next, prev
- The pointers to the next and previous elements of the linked list.INTEGER :: processor
- The rank of the processor that the particle thinks it is on. Used for debugging.INTEGER :: processor_at_t0
- The rank of the processor that the particle started on. Used for debugging.INTEGER :: id
- A unique integer assigned to each particle, only set for species which output the id, and only when the code is compiled with ID support.INTEGER :: coll_count
- An integer present for debugging the number of collisions a particle undergoes (this is not used in the current version of EPOCH).REAL(num) :: work_{x,y,z}
- Work done by the electric field on the particle during the last particle push, in each direction. Describes work done to a single particle within the macro-particle.REAL(num) :: work_{x,y,z}_total
- Work done by the electric fields on the particle over the full simulation, in each direction. Describes work done to a single particle within the macro-particle.REAL(num) :: optical_depth
- For some secondary-particle emission processes, a particle is assigned an “optical depth of emission”, related to how far the particle has moved, and the cross section of emission. The total optical depth is tracked, and an emission is sampled once the optical_depth goes negative. The originaloptical_depth
here describes the remaining optical depth to travel before photon emission through the QED non-linear Compton scatter process for electrons. For photons, this describes the remaining optical depth before Breit-Wheeler pair production.REAL(num) :: particle_energy
- Contains the relativistic particle energy, primarily for use in the QED routines.REAL(num) :: optical_depth_tri
- Remaining optical depth before emission of a pair through the electron trident process.REAL(num) :: optical_depth_bremsstrahlung
- Remaining optical depth before emission of a bremsstrahlung photon for electrons, or a Bethe-Heitler pair for photons.REAL(num) :: probe_time
- If a particle passes a probe, a copy of the particle is stored in a new list. This parameter is set to the time the particle passes the probe, for the particle copy.
WARNING: Simply adding a new parameter to the definition of the particle type is NOT sufficient to extend the particle type, since the communications when the particle crosses a processor boundary do not know about the new parameter and it will not be transmitted with the particle. How to add new properties to the particle communication layer is described later.
Particle list type
The entire linked list of particles is encapsulated in another Fortran TYPE,
called particle_list
, which is defined as:
TYPE particle_list
TYPE(particle), POINTER :: head
TYPE(particle), POINTER :: tail
INTEGER(KIND=8) :: count
! Pointer is safe if the particles in it are all unambiguously linked
LOGICAL :: safe
TYPE(particle_list), POINTER :: next, prev
END TYPE particle_list
And its properties are:
TYPE(particle), POINTER :: head
- The first particle in the linked list.TYPE(particle), POINTER :: tail
- The last particle in the linked list. New particles added to the end of the list are added onto the end of the tail element, and the new last particle becomes the new tail element.INTEGER(KIND=8) :: count
- The number of particles in this particle list. Note that theparticle_list
type is not directly MPI aware, so this is literally the number of particles in this particle list, not the number of particles of this species on all processors.LOGICAL :: safe
- A particle list safe if the particles in it are unambiguously linked. That is that thecount
th particle is guaranteed to have itsnext
property be null. Most particle lists within EPOCH are safe, but sometimes it is useful to be able to have particle lists which are subsets of longer particles lists, and these particle lists are not safe.TYPE(particle_list), POINTER :: next, prev
- At present, EPOCH does not use these pointers, which are intended to allow multiple particle lists to be attached together. Certain parts of EPOCH, such as the I/O system are aware of these pointers and will automatically use them if they are ever set. They are reserved for future use.
The particle_list
objects are used to abstract all the functions
of the linked list, including adding and removing particles and transporting
particles between processors.
Particle species type
The particle species are represented by yet another Fortran TYPE, this time
called particle_species
, which in 2D is defined as:
TYPE particle_species
! Core properties
CHARACTER(string_length) :: name
TYPE(particle_species), POINTER :: next, prev
INTEGER :: id
INTEGER :: dumpmask
INTEGER :: count_update_step
REAL(num) :: charge
REAL(num) :: mass
REAL(num) :: weight
INTEGER(i8) :: count
TYPE(particle_list) :: attached_list
TYPE(particle_pointer_list), POINTER :: boundary_particles => NULL()
LOGICAL :: immobile
LOGICAL :: fill_ghosts
! Parameters for relativistic and arbitrary particle loader
INTEGER :: ic_df_type
REAL(num) :: fractional_tail_cutoff
TYPE(primitive_stack) :: dist_fn
TYPE(primitive_stack) :: dist_fn_range(3)
#ifndef NO_TRACER_PARTICLES
LOGICAL :: zero_current
#endif
INTEGER :: atomic_no
LOGICAL :: atomic_no_set = .FALSE.
! Specify if species is background species or not
LOGICAL :: background_species = .FALSE.
! Background density
REAL(num), DIMENSION(:,:), POINTER :: background_density
! Do we need to make secondary lists for this species?
LOGICAL :: make_secondary_list = .FALSE.
! Has species list been randomised in order?
LOGICAL :: is_shuffled
! Specifiy if species is background for collisions
LOGICAL :: coll_background = .FALSE.
LOGICAL :: coll_fast = .FALSE.
LOGICAL :: coll_pairwise = .FALSE.
! ID code which identifies if a species is of a special type
INTEGER :: species_type
! particle cell division
INTEGER(i8) :: global_count
LOGICAL :: split
INTEGER(i8) :: npart_max
! Secondary list
TYPE(particle_list), DIMENSION(:,:), POINTER :: secondary_list
! Loading of particles
REAL(num) :: npart_per_cell
TYPE(primitive_stack) :: density_function, temperature_function(3)
TYPE(primitive_stack) :: drift_function(3)
! Thermal boundaries
REAL(num), DIMENSION(:,:), POINTER :: ext_temp_x_min, ext_temp_x_max
REAL(num), DIMENSION(:,:), POINTER :: ext_temp_y_min, ext_temp_y_max
! Species_ionisation
LOGICAL :: electron
LOGICAL :: ionise
INTEGER :: ionise_to_species
INTEGER :: release_species
INTEGER :: n
INTEGER :: l
REAL(num) :: ionisation_energy
REAL(num), ALLOCATABLE :: coll_ion_incident_ke(:)
REAL(num), ALLOCATABLE :: coll_ion_cross_sec(:)
REAL(num), ALLOCATABLE :: coll_ion_mean_bind(:,:)
REAL(num), ALLOCATABLE :: coll_ion_secondary_ke(:,:)
REAL(num), ALLOCATABLE :: coll_ion_secondary_cdf(:,:)
! Attached probes for this species
#ifndef NO_PARTICLE_PROBES
TYPE(particle_probe), POINTER :: attached_probes
#endif
! Particle migration
TYPE(particle_species_migration) :: migrate
! Initial conditions
TYPE(initial_condition_block) :: initial_conditions
! Per-species boundary conditions
INTEGER, DIMENSION(2*c_ndims) :: bc_particle
END TYPE particle_species
Again, most of these properties are self explanatory, but they are detailed below.
CHARACTER(LEN=entry_length) :: name
- The name of the particle species. Used when constructing things like “ekbar_electron” and similar names.TYPE(particle_species), POINTER :: next, prev
- Particle species are connected to each other as a linked list using pointers as well as being available through a simple array. These pointers are used behind the scenes in the I/O.INTEGER :: id
- The number of the species, so for the speciesspecies_list(1)
, the id field would be 1. Forspecies_list(2)
, the id field would be 2 etc.INTEGER :: dumpmask
- Bitmask to determine when this species should be dumped in diagnostic output.INTEGER :: count_update_step
- The last step where thecount
parameter was updated.REAL(num) :: charge
- The charge on a single particle of the species in Coulombs.REAL(num) :: mass
- The mass of a single particle of the species in kilograms.REAL(num) :: weight
- The per-species particle weight.INTEGER(KIND=8) :: count
- The number of particles of this species on all processors. NOTE that this is only accurate if the code is compiled with the correct preprocessor options. Without the correct preprocessor options, this will be accurate at the start of the code runtime, but will not be if any particles enter or leave the domain. This is mainly a debugging parameter.TYPE(particle_list) :: attached_list
- This is theparticle_list
object which holds the particles assigned to this species on this processor. Particles are attached to this list except between the calls toreorder_particles_to_grid
andreattach_particles_to_mainlist
inepoch{1,2,3}d.F90
where the particles are instead attached tosecondary_list
. This is explained later.TYPE(particle_pointer_list), POINTER :: boundary_particles
- A list of particles which have been pushed out of the simulation boundaries.LOGICAL :: immobile
- These particles skip the particle push if true.LOGICAL :: fill_ghosts
- Loads particles into ghost cells surrounding the simulation if true.INTEGER :: ic_df_type
- Sets temperature calculation for the delta-f loader.REAL(num) :: fractional_tail_cutoff
- Sets a cut-off for the relativistic temperature to momentum calculation insrc/user_interaction/particle_temperature.F90
TYPE(primitive_stack) :: dist_fn
- Arbitrary momentum distribution function for particle loading.TYPE(primitive_stack) :: dist_fn_range(3)
- Set upper and lower ranges to the momentum distribution functions.LOGICAL :: zero_current
- Whether or not this species is a tracer particle. If a species is a tracer species then it moves under the fields as normal for a particle with its mass and charge but contributes no current.INTEGER :: atomic_no
- Set atomic number of species. Used for bremsstrahlung and ionisation.LOGICAL :: atomic_no_set
- Identifies is an atomic number has been set for the species.LOGICAL :: background_species
- If true, the species is not to load any particles. Instead, this is represented with a density value in each cell, and it can only act as a target species for bremsstrahlung radiation.REAL(num), DIMENSION(:,:,:), POINTER :: background_density
- The number density of a background species (for bremsstrahlung radiation).LOGICAL :: make_secondary_list
- Checks if this species will ever need to create a secondary list (seesecondary_list
).LOGICAL :: is_shuffled
- Checks if the order of particles in secondary lists has been randomisedLOGICAL :: coll_background
- Checks if this species forms the background species in a fast-background pair for background particle collisions.LOGICAL :: coll_fast
- Checks if this species describes the fast species in a fast-background pair for background particle collisions.LOGICAL :: coll_pairwise
- Checks if this species undergoes binary collisions with another species.INTEGER :: species_type
- Tag the species as a particular type of particle, using constants likec_species_id_electron
. This is required for the physics packages.INTEGER(i8) :: global_count
- The number of particles from this species summed over all ranks.LOGICAL :: split
- EPOCH includes a very early version of a particle splitting operator. It works mechanically but has undesirable properties at present. If this flag is true then the code attempts to split the particles when the pseudoparticle number density drops too low.INTEGER(KIND=8) :: npart_max
- Used with the particle splitting operator. When the total number of particles equals this number, further particle splitting is suppressed.TYPE(particle_list), DIMENSION(:,:,:), POINTER :: secondary_list
- This describes an array of particle lists. The subroutinereorder_particles_to_grid
allocatessecondary_list(0:nx+1,0:ny+1,0:nz+1)
and then loops over all particles. It calculates the cell in which each particle is and moves the particle fromattached_list
to the correct element ofsecondary_list
for that cell. This means the particles which are nearby in space are now linked together in an array of linked lists. This allows things such as collision operators which require direct interaction between nearby particles.INTEGER(KIND=8) :: npart_per_cell
- The number of pseudoparticles per cell in the initial conditions. This is used with the moving window function to ensure that the same number of particles per cell are used for the new material introduced at the leading edge of the window.REAL(num), DIMENSION(:,:), POINTER :: density
- The density of the plasma at the leading edge of the window at the start of the simulation. This is used to structure the density of the new material introduced at the leading edge of the plasma.REAL(num), DIMENSION(:,:,:), POINTER :: temperature
- The temperature in of the plasma at the leading edge of a moving window at the start of the simulation. The final index of the array is the direction in which the temperature is set (1=x, 2=y, 3=z).REAL(num), DIMENSION(:,:,:), POINTER :: ext_temp_{x,y,z}_{min,max}
- Sets the temperature on the boundary for this particle species ifthermal
boundaries have been used.LOGICAL :: electron
- Species is tagged as an electron for the ionisation routines.LOGICAL :: ionise
- If the ionisation model is activated then this species should ionise.INTEGER :: ionise_to_species
- The species number for the next ionised state of this species.INTEGER :: release_species
- Specifies what type of particle should be released when this species ionises (i.e. which species is the electron).REAL(num) :: ionisation_energy
- The ionisation energy for the next ionisation of this species.INTEGER :: n, l
- The principle and angular quantum numbers of the outermost electron for this species, assuming a ground-state electron configuration. This is used for field ionisation.REAL(num), ALLOCATABLE :: coll_ion_incident_ke(:)
- A table of logarithmically spaced kinetic energy values for incident electrons in collisional ionisation.REAL(num), ALLOCATABLE :: coll_ion_cross_sec(:)
- The collisional ionisation cross sections corresponding to incident electrons with kinetic energies given bycoll_ion_incident_ke(:)
.REAL(num), ALLOCATABLE :: coll_ion_mean_bind(:,:)
- For each incident/ejected electron energy pair for collisional ionisation, this gives the mean bound electron energy, weighted by the ionisation cross section of each bound shell.REAL(num), ALLOCATABLE :: coll_ion_secondary_ke(:,:)
- An array of possible ejected electron energies for each incident electron kinetic energy in thecoll_ion_incident_ke(:)
table.REAL(num), ALLOCATABLE :: coll_ion_secondary_cdf(:,:)
- The cumulative distribution function for the probability of emission of an ejected electron energy for a given incident energy during collisional ionisation, corresponding to the energies incoll_ion_secondary_ke
.TYPE(particle_probe), POINTER :: attached_probes
- A pointer pointing to the head of an attached linked list of particle probe diagnostics.TYPE(particle_species_migration) :: migrate
- Determines the criteria for particles being moved to other species, using the particle migration routines.TYPE(initial_condition_block) :: initial_conditions
- Stores parameters read from the species block, describing the initial conditions of the species.INTEGER, DIMENSION(2*c_ndims) :: bc_particle
- Boundary conditions for particles in this species, which override the global boundaries set in the input deck boundary block.