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 asprev
for the next iteration. Wrap it inGPUArraysView
if you want transparent conversion tonumpy
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
andvirt_params
, which are constant at least for the \(\xi\)-step duration and defined during the initialization, andprev
, which is usually obtained as the return value of the previousstep()
invocation, except for the very first step.
Initial half-step estimation¶
- 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:
- 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()
). - The particles from 2. are deposited onto the charge/current density grids
(
lcode.deposit()
). - 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¶
- 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()
). - The particles from 5. are deposited onto the charge/current density grids
(
lcode.deposit()
). - 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¶
- 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()
). - 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()
.