Looping in xi

Finally, here’s the function that binds it all together, and currently makes up half of LCODE 3D API.

In short it: moves, deposits, estimates fields, moves, deposits, recalculates fields, moves and deposits.

config_example.xi_step_size = 0.005

Step size in time-space coordinate xi

config_example.xi_steps = 599999

Amount of xi steps

lcode.step(config, const, virt_params, prev, beam_ro)[source]

Calculate the next iteration of plasma evolution and response. Returns the new state with the following attributes: x_offt, y_offt, px, py, pz, Ex, Ey, Ez, Bx, By, Bz, ro, jx, jy, jz. Pass the returned value as prev for the next iteration. Wrap it in GPUArraysView if you want transparent conversion to numpy arrays.

Input parameters

Beam density array rho_b (beam_ro) is copied to the GPU with cupy.asarray, as it is calculated with numpy in config-residing beam().

All the other arrays come packed in GPUArrays objects [GPU array conversion], which ensures that they reside in the GPU memory. These objects are:

  • const and virt_params, which are constant at least for the \(\xi\)-step duration and defined during the initialization, and
  • prev, which is usually obtained as the return value of the previous step() invocation, except for the very first step.

Initial half-step estimation

  1. The particles are advanced according to their current momenta only (lcode.move_estimate_wo_fields()).

Field prediction

While we don’t know the fields on the next step:

  1. The particles are advanced with the fields from the previous step using the coordinates estimated at 1. to calculate the half-step positions where the previous step fields should be interpolated at (lcode.move_smart()).
  2. The particles from 2. are deposited onto the charge/current density grids (lcode.deposit()).
  3. The fields at the next step are calculated using densities from 3. (lcode.calculate_Ez(), lcode.calculate_Ex_Ey_Bx_By(), lcode.calculate_Bz()) and averaged with the previous fields.

This phase gives us an estimation of the fields at half-step, and the coordinate estimation at next step, while all other intermediate results are ultimately ignored.

Field correction

  1. The particles are advanced with the averaged fields from 4., using the coordinates from 2. to calculate the half-step positions where the averaged fields from 4. should be interpolated at (lcode.move_smart()).
  2. The particles from 5. are deposited onto the charge/current density grids (lcode.deposit()).
  3. The fields at the next step are calculated using densities from 6. (lcode.calculate_Ez(), lcode.calculate_Ex_Ey_Bx_By(), lcode.calculate_Bz()) and averaged with the previous fields.

The resulting fields are far more precise than the ones from the prediction phase, but the coordinates and momenta are still pretty low-quality until we recalculate them using the new fields. Iterating the algorithm more times improves the stability, but it currently doesn’t bring much to the table as the transverse noise dominates.

Final plasma evolution and deposition

  1. The particles are advanced with the averaged fields from 7., using the coordinates from 5. to calculate the half-step positions where the averaged fields from 7. should be interpolated at (lcode.move_smart()).
  2. The particles from 8. are deposited onto the charge/current density grids (lcode.deposit()).

The result, or the ‘new prev’

The fields from 7., coordinates and momenta from 8., and densities from 9. make up the new GPUArrays collection that would be passed as prev to the next iteration of step().