Report Typos and Errors    
Semi-Lagrangian Library
Modular library for kinetic and gyrokinetic simulations of plasmas in fusion energy devices.
sll_m_time_propagator_pic_vm_1d2v_boris.F90
Go to the documentation of this file.
1 
7 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 #include "sll_assert.h"
9 #include "sll_memory.h"
10 #include "sll_working_precision.h"
11 
12  use sll_m_collective, only: &
15 
16  use sll_m_time_propagator_base, only: &
18 
21 
22  use sll_m_maxwell_1d_base, only: &
24 
25  use sll_m_particle_group_base, only: &
27 
28  use mpi, only: &
29  mpi_sum
30 
31  implicit none
32 
33  public :: &
35 
36  private
37 
38 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
39 
42  class(sll_c_maxwell_1d_base), pointer :: maxwell_solver
43  class(sll_c_particle_mesh_coupling_1d), pointer :: kernel_smoother_0
44  class(sll_c_particle_mesh_coupling_1d), pointer :: kernel_smoother_1
45  class(sll_t_particle_array), pointer :: particle_group
46  sll_int32 :: spline_degree
47  sll_real64 :: lx
48  sll_real64 :: x_min
49  sll_real64 :: delta_x
50 
51  sll_real64 :: cell_integrals_0(4)
52  sll_real64 :: cell_integrals_1(3)
53 
54 
55  sll_real64, pointer :: efield_dofs(:,:)
56  sll_real64, pointer :: efield_dofs_mid(:,:)
57  sll_real64, pointer :: bfield_dofs(:)
58  sll_real64, allocatable :: bfield_dofs_mid(:)
59  sll_real64, allocatable :: j_dofs(:,:)
60  sll_real64, allocatable :: j_dofs_local(:,:)
61 
62  contains
63  procedure :: lie_splitting => lie_boris
64  procedure :: lie_splitting_back => lie_boris
65  procedure :: strang_splitting => operator_boris
66 
67  procedure :: init => initialize_pic_vm_1d2v_boris
68  procedure :: free => delete_pic_vm_1d2v_boris
69 
70  procedure :: staggering => staggering_pic_vm_1d2v_boris
71 
73 
74 contains
75 
77  subroutine lie_boris(self, dt, number_steps)
78  class(sll_t_time_propagator_pic_vm_1d2v_boris), intent(inout) :: self
79  sll_real64, intent(in) :: dt
80  sll_int32, intent(in) :: number_steps
81 
82 
83  print*, 'Lie splitting not implemented in sll_m_time_propagator_pic_vm_1d2v_boris.'
84 
85  end subroutine lie_boris
86 
88  subroutine operator_boris(self, dt, number_steps)
89  class(sll_t_time_propagator_pic_vm_1d2v_boris), intent(inout) :: self
90  sll_real64, intent(in) :: dt
91  sll_int32, intent(in) :: number_steps
92 
93  sll_int32 :: i_step
94 
95  do i_step = 1, number_steps
96  ! (1) Compute B_{n+1/2} from B_n
97  self%bfield_dofs_mid = self%bfield_dofs
98  call self%maxwell_solver%compute_B_from_E( &
99  dt, self%efield_dofs_mid(:,2), self%bfield_dofs)
100  self%bfield_dofs_mid = (self%bfield_dofs_mid + self%bfield_dofs)*0.5_f64
101 
102  ! (2) Propagate v: v_{n-1/2} -> v_{n+1/2}
103  ! (2a) Half time step with E-part
104  call push_v_epart( self, dt*0.5_f64 )
105  ! (2b) Full time step with B-part
106  call push_v_bpart( self, dt )
107  ! (2c) Half time step with E-part
108  call push_v_epart( self, dt*0.5_f64 )
109 
110  ! (3) Propagate x: x_n -> x_{n+1}. Includes also accumulation of j_x, j_y
111  call push_x_accumulate_j ( self, dt )
112 
113  ! (4) Compute E_{n+1}
114  self%efield_dofs = self%efield_dofs_mid
115  ! Ex part:
116  call self%maxwell_solver%compute_E_from_j(self%j_dofs(:,1), 1, self%efield_dofs_mid(:,1))
117  ! TODO: Combine the two steps for efficiency
118  ! Ey part:
119  call self%maxwell_solver%compute_E_from_B(&
120  dt, self%bfield_dofs, self%efield_dofs_mid(:,2))
121  call self%maxwell_solver%compute_E_from_j(self%j_dofs(:,2), 2, self%efield_dofs_mid(:,2))
122 
123  end do
124 
125  end subroutine operator_boris
126 
128  subroutine push_v_epart (self, dt)
129  class(sll_t_time_propagator_pic_vm_1d2v_boris), intent(inout) :: self
130  sll_real64, intent(in) :: dt
131 
132  !local variables
133  sll_int32 :: i_part, i_sp
134  sll_real64 :: v_new(3), xi(3)
135  sll_real64 :: efield(2)
136  sll_real64 :: qm
137 
138 
139  do i_sp =1, self%particle_group%n_species
140  qm = self%particle_group%group(i_sp)%species%q_over_m();
141  ! V_new = V_old + dt * E
142  do i_part=1,self%particle_group%group(i_sp)%n_particles
143  ! Evaluate efields at particle position
144  xi = self%particle_group%group(i_sp)%get_x(i_part)
145  call self%kernel_smoother_1%evaluate &
146  (xi(1), self%efield_dofs_mid(:,1), efield(1))
147  call self%kernel_smoother_0%evaluate &
148  (xi(1), self%efield_dofs_mid(:,2), efield(2))
149  v_new = self%particle_group%group(i_sp)%get_v(i_part)
150  v_new(1:2) = v_new(1:2) + dt* qm * efield
151  call self%particle_group%group(i_sp)%set_v(i_part, v_new)
152  end do
153  end do
154 
155 
156  end subroutine push_v_epart
157 
159  subroutine push_v_bpart (self, dt)
160  class(sll_t_time_propagator_pic_vm_1d2v_boris), intent(inout) :: self
161  sll_real64, intent(in) :: dt
162 
163  !local variables
164  sll_int32 :: i_part, i_sp
165  sll_real64 :: vi(3), v_new(3), xi(3)
166  sll_real64 :: bfield, m11, m12
167  sll_real64 :: qmdt
168 
169  do i_sp =1, self%particle_group%n_species
170  qmdt = self%particle_group%group(i_sp)%species%q_over_m()*0.5_f64*dt;
171 
172  do i_part=1,self%particle_group%group(i_sp)%n_particles
173  vi= self%particle_group%group(i_sp)%get_v(i_part)
174  xi = self%particle_group%group(i_sp)%get_x(i_part)
175  call self%kernel_smoother_1%evaluate &
176  (xi(1), self%bfield_dofs_mid, bfield)
177 
178  bfield = qmdt*bfield
179  m11 = 1.0_f64/(1.0_f64 + bfield**2)
180  m12 = m11*bfield*2.0_f64
181  m11 = m11*(1-bfield**2)
182 
183  v_new(1) = m11 * vi(1) + m12 * vi(2)
184  v_new(2) = - m12 * vi(1) + m11 * vi(2)
185  v_new(3) = 0.0_f64
186 
187  call self%particle_group%group(i_sp)%set_v(i_part, v_new)
188 
189  end do
190  end do
191 
192  end subroutine push_v_bpart
193 
195  subroutine push_x_accumulate_j (self, dt)
196  class(sll_t_time_propagator_pic_vm_1d2v_boris), intent(inout) :: self
197  sll_real64, intent(in) :: dt
198 
199  !local variables
200  sll_int32 :: i_part, i_sp
201  sll_real64 :: x_new(3), vi(3), wi(1), x_old(3)
202  sll_int32 :: n_cells
203  sll_real64 :: qoverm
204 
205 
206  n_cells = self%kernel_smoother_0%n_dofs
207 
208 
209  self%j_dofs_local = 0.0_f64
210 
211  ! For each particle compute the index of the first DoF on the grid it contributes to and its position (normalized to cell size one). Note: j_dofs(_local) does not hold the values for j itself but for the integrated j.
212  ! Then update particle position: X_new = X_old + dt * V
213 
214 
215  do i_sp =1, self%particle_group%n_species
216  qoverm = self%particle_group%group(i_sp)%species%q_over_m();
217  do i_part=1,self%particle_group%group(i_sp)%n_particles
218  ! Read out particle position and velocity
219  x_old = self%particle_group%group(i_sp)%get_x(i_part)
220  vi = self%particle_group%group(i_sp)%get_v(i_part)
221 
222  ! Then update particle position: X_new = X_old + dt * V
223  x_new = x_old + dt * vi
224 
225  ! Get charge for accumulation of j
226  wi = self%particle_group%group(i_sp)%get_charge(i_part)
227 
228  ! TODO: Check here also second posibility with sum of two accumulations
229 !!$ ! Accumulate jx
230 !!$ call self%kernel_smoother_1%add_charge( [(x_old(1)+x_new(1))*0.5_f64], wi(1)*vi(1)*dt, &
231 !!$ self%j_dofs_local(:,1))
232 !!$ ! Accumulate jy
233 !!$ call self%kernel_smoother_0%add_charge( [(x_old(1)+x_new(1))*0.5_f64], wi(1)*vi(2)*dt, &
234 !!$ self%j_dofs_local(:,2))
235  call self%kernel_smoother_1%add_current( x_old(1), x_new(1), wi(1), self%j_dofs_local(:,1) )
236  if ( abs(vi(1)) > 1e-15_f64 ) then
237  call self%kernel_smoother_0%add_current( x_old(1), x_new(1), wi(1)*vi(2)/vi(1), self%j_dofs_local(:,2) )
238  else
239  call self%kernel_smoother_0%add_charge( [(x_old(1)+x_new(1))*0.5_f64], wi(1)*vi(2)*dt , &
240  self%j_dofs_local(:,2))
241  end if
242 
243  x_new(1) = modulo(x_new(1), self%Lx)
244  call self%particle_group%group(i_sp)%set_x(i_part, x_new)
245 
246  end do
247  end do
248 
249  self%j_dofs = 0.0_f64
250  ! MPI to sum up contributions from each processor
251  call sll_o_collective_allreduce( sll_v_world_collective, self%j_dofs_local(:,1), &
252  n_cells, mpi_sum, self%j_dofs(:,1))
253  call sll_o_collective_allreduce( sll_v_world_collective, self%j_dofs_local(:,2), &
254  n_cells, mpi_sum, self%j_dofs(:,2))
255 
256 
257  end subroutine push_x_accumulate_j
258 
259  !---------------------------------------------------------------------------!
262  self, &
263  maxwell_solver, &
264  kernel_smoother_0, &
265  kernel_smoother_1, &
266  particle_group, &
267  efield_dofs, &
268  bfield_dofs, &
269  x_min, &
270  Lx)
271  class(sll_t_time_propagator_pic_vm_1d2v_boris), intent(out) :: self
272  class(sll_c_maxwell_1d_base), target, intent(in) :: maxwell_solver
273  class(sll_c_particle_mesh_coupling_1d), target, intent(in) :: kernel_smoother_0
274  class(sll_c_particle_mesh_coupling_1d), target, intent(in) :: kernel_smoother_1
275  class(sll_t_particle_array), target, intent(in) :: particle_group
276  sll_real64, target, intent(in) :: efield_dofs(:,:)
277  sll_real64, target, intent(in) :: bfield_dofs(:)
278  sll_real64, intent(in) :: x_min
279  sll_real64, intent(in) :: lx
280 
281  !local variables
282  sll_int32 :: ierr
283 
284  self%maxwell_solver => maxwell_solver
285  self%kernel_smoother_0 => kernel_smoother_0
286  self%kernel_smoother_1 => kernel_smoother_1
287  self%particle_group => particle_group
288  self%efield_dofs => efield_dofs
289  self%bfield_dofs => bfield_dofs
290 
291 #ifndef __PGI
292  ! Check that n_dofs is the same for both kernel smoothers.
293  sll_assert( self%kernel_smoother_0%n_dofs == self%kernel_smoother_1%n_dofs )
294 #endif
295  sll_allocate(self%j_dofs(self%kernel_smoother_0%n_dofs,2), ierr)
296  sll_allocate(self%j_dofs_local(self%kernel_smoother_0%n_dofs,2), ierr)
297  sll_allocate(self%bfield_dofs_mid(self%kernel_smoother_1%n_dofs), ierr)
298  sll_allocate(self%efield_dofs_mid(self%kernel_smoother_1%n_dofs,2), ierr)
299 
300  self%spline_degree = self%kernel_smoother_0%spline_degree
301  self%x_min = x_min
302  self%Lx = lx
303  self%delta_x = self%Lx/real(self%kernel_smoother_1%n_dofs, f64)
304 
305  self%cell_integrals_1 = [0.5_f64, 2.0_f64, 0.5_f64]
306  self%cell_integrals_1 = self%cell_integrals_1 / 3.0_f64
307 
308  self%cell_integrals_0 = [1.0_f64,11.0_f64,11.0_f64,1.0_f64]
309  self%cell_integrals_0 = self%cell_integrals_0 / 24.0_f64
310 
311 
312  end subroutine initialize_pic_vm_1d2v_boris
313 
314  !---------------------------------------------------------------------------!
316  subroutine delete_pic_vm_1d2v_boris(self)
317  class(sll_t_time_propagator_pic_vm_1d2v_boris), intent( inout ) :: self
318 
319  deallocate(self%j_dofs)
320  deallocate(self%j_dofs_local)
321  deallocate(self%bfield_dofs_mid)
322  deallocate(self%efield_dofs_mid)
323  self%maxwell_solver => null()
324  self%kernel_smoother_0 => null()
325  self%kernel_smoother_1 => null()
326  self%particle_group => null()
327  self%efield_dofs => null()
328  self%bfield_dofs => null()
329 
330  end subroutine delete_pic_vm_1d2v_boris
331 
332 
333  !---------------------------------------------------------------------------!
335  subroutine staggering_pic_vm_1d2v_boris(self, dt)
336  class(sll_t_time_propagator_pic_vm_1d2v_boris), intent( inout ) :: self
337  sll_real64, intent(in) :: dt
338 
339  call push_x_accumulate_j (self, dt*0.5_f64)
340 
341  ! (4) Compute E_{n+1}
342  self%efield_dofs_mid = self%efield_dofs
343  ! self%j_dofs = dt*0.5_f64*self%j_dofs
344  ! Ex part:
345  call self%maxwell_solver%compute_E_from_j(self%j_dofs(:,1), 1, self%efield_dofs_mid(:,1))
346  ! TODO: Combine the two steps for efficiency
347  ! Ey part:
348  call self%maxwell_solver%compute_E_from_B(&
349  dt*0.5_f64, self%bfield_dofs, self%efield_dofs_mid(:,2))
350  call self%maxwell_solver%compute_E_from_j(self%j_dofs(:,2), 2, self%efield_dofs_mid(:,2))
351 
352  end subroutine staggering_pic_vm_1d2v_boris
353 
354 
Combines values from all processes and distributes the result back to all processes.
Parallelizing facility.
type(sll_t_collective_t), pointer, public sll_v_world_collective
Control of subset assignment. Processes with the same color are in the same new communicator.
Module interface to solve Maxwell's equations in 1D.
Base class for kernel smoothers for accumulation and field evaluation in PIC.
Base class for Hamiltonian splittings.
Boris pusher in GEMPIC framework (spline finite elements)
subroutine push_x_accumulate_j(self, dt)
Pusher for x and accumulate current densities.
subroutine push_v_epart(self, dt)
Pusher for E \nabla_v part.
subroutine operator_boris(self, dt, number_steps)
Second order Boris pusher using staggered grid.
subroutine lie_boris(self, dt, number_steps)
Initialize the staggering.
subroutine staggering_pic_vm_1d2v_boris(self, dt)
Propagate E_0 to E_{1/2} and x_0 to x_{1/2} to initialize the staggering.
subroutine initialize_pic_vm_1d2v_boris(self, maxwell_solver, kernel_smoother_0, kernel_smoother_1, particle_group, efield_dofs, bfield_dofs, x_min, Lx)
Constructor.
Solves 1d2v Vlasov-Maxwell with PIC and spline finite elements with Boris pusher.
    Report Typos and Errors