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_subcyc.F90
Go to the documentation of this file.
1 
6 
9 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
10 #include "sll_assert.h"
11 #include "sll_memory.h"
12 #include "sll_working_precision.h"
13 
14  use sll_m_collective, only: &
17 
18  use sll_m_binomial_filter, only: &
20 
21  use sll_m_control_variate, only: &
23 
24  use sll_m_time_propagator_base, only: &
26 
29 
32 
33  use sll_m_maxwell_1d_base, only: &
35 
36  use sll_m_particle_group_base, only: &
38 
39  use mpi, only: &
40  mpi_sum
41 
42  implicit none
43 
44  public :: &
48 
49  private
50 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
51 
54  class(sll_c_maxwell_1d_base), pointer :: maxwell_solver
55  class(sll_c_particle_mesh_coupling_1d), pointer :: kernel_smoother_0
56  class(sll_c_particle_mesh_coupling_1d), pointer :: kernel_smoother_1
57  class(sll_t_particle_array), pointer :: particle_group
58 
59  sll_int32 :: spline_degree
60  sll_real64 :: lx
61  sll_real64 :: x_min
62  sll_real64 :: delta_x
63 
64  sll_real64 :: cell_integrals_0(4)
65  sll_real64 :: cell_integrals_1(3)
66 
67 
68  sll_real64, pointer :: efield_dofs(:,:)
69  sll_real64, pointer :: bfield_dofs(:)
70  sll_real64, allocatable :: j_dofs(:,:)
71  sll_real64, allocatable :: j_dofs_local(:,:)
72  sll_real64, allocatable :: rho_dofs_local(:)
73  sll_int32 :: n_species
74 
75  logical :: jmean = .false.
76  sll_real64, allocatable :: efield_filter(:,:)
77  sll_real64, allocatable :: bfield_filter(:)
78  sll_real64, allocatable :: bfield_dofs_old(:)
79 
80  type(sll_t_binomial_filter), pointer :: filter
81 
82  ! For version with control variate
84  sll_int32 :: i_weight
85 
86  sll_int32 :: n_sub_iter = 4
87  sll_int32 :: n_max_iter = 50
88  sll_real64 :: tolerance = 1d-10
89  sll_int32 :: file_id
90  sll_int32 :: file_id_rho
91 
92  contains
93  procedure :: operatorall => operatorhp_pic_vm_1d2v_newton
94  procedure :: lie_splitting => lie_splitting_pic_vm_1d2v
95  procedure :: lie_splitting_back => lie_splitting_back_pic_vm_1d2v
96  procedure :: strang_splitting => strang_splitting_pic_vm_1d2v
97  procedure :: reinit_fields
98 
99  procedure :: init => initialize_pic_vm_1d2v
100  procedure :: free => delete_pic_vm_1d2v
101 
103 
104 contains
105 
107  subroutine reinit_fields( self )
108  class(sll_t_time_propagator_pic_vm_1d2v_subcyc), intent(inout) :: self
109 
110  call self%filter%apply( self%efield_dofs(:,1), self%efield_filter(:,1) )
111  call self%filter%apply( self%efield_dofs(:,2), self%efield_filter(:,2) )
112  call self%filter%apply( self%bfield_dofs, self%bfield_filter )
113 
114  end subroutine reinit_fields
115 
117  subroutine strang_splitting_pic_vm_1d2v(self,dt, number_steps)
118  class(sll_t_time_propagator_pic_vm_1d2v_subcyc), intent(inout) :: self
119  sll_real64, intent(in) :: dt
120  sll_int32, intent(in) :: number_steps
121 
122  sll_int32 :: i_step
123 
124 
125  do i_step = 1, number_steps
126  call self%operatorall( dt )
127  end do
128 
129  end subroutine strang_splitting_pic_vm_1d2v
130 
132  subroutine lie_splitting_pic_vm_1d2v(self,dt, number_steps)
133  class(sll_t_time_propagator_pic_vm_1d2v_subcyc), intent(inout) :: self
134  sll_real64, intent(in) :: dt
135  sll_int32, intent(in) :: number_steps
136 
137  sll_int32 :: i_step
138 
139  do i_step = 1,number_steps
140  call self%operatorall( dt )
141  end do
142 
143 
144  end subroutine lie_splitting_pic_vm_1d2v
145 
147  subroutine lie_splitting_back_pic_vm_1d2v(self,dt, number_steps)
148  class(sll_t_time_propagator_pic_vm_1d2v_subcyc), intent(inout) :: self
149  sll_real64, intent(in) :: dt
150  sll_int32, intent(in) :: number_steps
151 
152  sll_int32 :: i_step
153 
154  do i_step = 1,number_steps
155  call self%operatorall( dt )
156  end do
157 
158  end subroutine lie_splitting_back_pic_vm_1d2v
159 
160 
161 
162  ! Implementation of the full operator with Picard iteration
163  !---------------------------------------------------------------------------!
164  subroutine operatorhp_pic_vm_1d2v(self, dt)
165  class(sll_t_time_propagator_pic_vm_1d2v_subcyc), intent(inout) :: self
166  sll_real64, intent(in) :: dt
167 
168  !local variables
169  sll_int32 :: i_part
170  sll_real64 :: x_new(3), vi(3), wi(1), x_old(3), wp(3), x_future(3), v_new(3), v_nnew(3)
171  sll_int32 :: n_cells, i_sp
172  sll_real64 :: qoverm
173  sll_real64 :: efield(2), bfield_old, bfield_new
174  sll_real64 :: residual
175  sll_int32 :: n_iter, n_iter_mean
176  sll_int32 :: iter
177  sll_real64 :: dtau
178 
179  dtau = dt/real(self%n_sub_iter,f64)
180 
181  n_iter_mean = 0
182 
183  n_cells = self%kernel_smoother_0%n_dofs
184 
185  self%j_dofs_local = 0.0_f64
186 
187  ! Update the magnetic field
188  self%bfield_dofs_old = self%bfield_dofs
189  call self%maxwell_solver%compute_B_from_E( &
190  dt, self%efield_dofs(:,2), self%bfield_dofs)
191  call self%filter%apply( self%bfield_dofs, self%bfield_filter )
192 
193  call self%filter%apply( self%efield_dofs(:,1), self%efield_filter(:,1) )
194  call self%filter%apply( self%efield_dofs(:,2), self%efield_filter(:,2) )
195 
196  ! 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.
197  ! Then update particle position: X_new = X_old + dt * V
198  do i_sp = 1,self%n_species
199  qoverm = self%particle_group%group(i_sp)%species%q_over_m();
200  do i_part=1,self%particle_group%group(i_sp)%n_particles
201  ! Read out particle position and velocity
202  x_new = self%particle_group%group(i_sp)%get_x(i_part)
203  vi = self%particle_group%group(i_sp)%get_v(i_part)
204 
205  ! Get charge for accumulation of j
206  wi = self%particle_group%group(i_sp)%get_charge(i_part, self%i_weight)
207 
208  ! Then update particle position (first part): X_new = X_old + dt * 0.5 * V
209  x_old = x_new - dtau * vi
210 
211 
212  ! Now the first half of the velocity update
213  ! First we evaluate the electric field
214  call self%kernel_smoother_1%evaluate &
215  (x_new(1), self%efield_filter(:,1), efield(1))
216  call self%kernel_smoother_0%evaluate &
217  (x_new(1), self%efield_filter(:,2), efield(2))
218  select type ( q=> self%kernel_smoother_1 )
220  call q%evaluate_int_quad( x_old(1), x_new(1), self%bfield_dofs_old, bfield_old )
221  end select
222  ! Now we add up the fixed update into efield
223  efield(1) = qoverm * ( dt * efield(1) + dtau * vi(2) * bfield_old )
224  efield(2) = qoverm * ( dt * efield(2) - dtau * vi(1) * bfield_old )
225 
226  ! First guess for v_new = v_old
227  v_new(1) = vi(1) + efield(1) + qoverm * dtau * vi(2) * bfield_old
228  v_new(2) = vi(2) + efield(2) - qoverm * dtau * vi(1) * bfield_old
229 
230  x_future = x_new + dtau * v_new
231 
232  ! Loop here for convergence
233  residual = 1.0_f64
234  n_iter = 0
235  do while( residual > self%tolerance .and. n_iter < self%n_max_iter )
236  n_iter = n_iter + 1
237  select type ( q=> self%kernel_smoother_1 )
239  call q%evaluate_int_quad( x_future(1), x_new(1), self%bfield_filter, bfield_new )
240  end select
241 
242  v_nnew(1) = vi(1) + efield(1) + qoverm * dtau * v_new(2) * bfield_new
243  v_nnew(2) = vi(2) + efield(2) - qoverm * dtau * v_new(1) * bfield_new
244  x_future = x_new + dtau * v_nnew
245  residual = max( abs( v_new(1)-v_nnew(1) ), abs( v_new(2) - v_nnew(2) ) )
246  v_new = v_nnew
247 
248  end do
249  if ( n_iter .eq. self%n_max_iter ) then
250  print*, 'First iteration did not converge. Residual:', residual
251  end if
252  n_iter_mean = n_iter_mean + n_iter
253  ! End of the loop
254 
255  if (abs(v_new(1))> 1e-16_f64) then
256  ! Now the charge deposition
257  call self%kernel_smoother_1%add_current( x_new, x_future, wi(1), self%j_dofs_local(:,1 ) )
258  end if
259 
260  select type ( q=> self%kernel_smoother_0 )
262  call q%add_charge_int( x_new, x_future, wi(1)*v_new(2)*dtau, self%j_dofs_local(:,2 ) )
263  end select
264 
265  do iter=2,self%n_sub_iter
266  ! Then update particle position (second part): X_new = X_old + dt * 0.5 * V
267  x_old = x_new
268  x_new = x_future
269  vi = v_new
270 
271  ! Then the second half of the velocity update
272  ! First the fixed old part again
273  select type ( q=> self%kernel_smoother_1 )
275  call q%evaluate_int_quad( x_old(1), x_new(1), self%bfield_filter, bfield_old )
276  end select
277  ! Now we add up the fixed update into efield
278  efield(1) = qoverm * dtau * ( vi(2) * bfield_old )
279  efield(2) = qoverm * dtau * (- vi(1) * bfield_old )
280 
281  ! First guess for v_new = v_old (already set)
282  v_new(1) = vi(1) + efield(1) + qoverm * dtau * vi(2) * bfield_old
283  v_new(2) = vi(2) + efield(2) - qoverm * dtau * vi(1) * bfield_old
284  x_future = x_new + dtau * v_new
285 
286  ! Loop here for convergence
287  residual = 1.0_f64
288  n_iter = 0
289  do while( residual > self%tolerance .and. n_iter < self%n_max_iter )
290  n_iter = n_iter + 1
291  select type ( q=> self%kernel_smoother_1 )
293  call q%evaluate_int_quad( x_future(1), x_new(1), self%bfield_filter, bfield_new )
294  end select
295  v_nnew(1) = vi(1) + efield(1) + qoverm * dtau * v_new(2) * bfield_new
296  v_nnew(2) = vi(2) + efield(2) - qoverm * dtau * v_new(1) * bfield_new
297  x_future = x_new + dtau * v_nnew
298  residual = max( abs(v_new(1)-v_nnew(1)), abs(v_new(2)-v_nnew(2)))
299  v_new = v_nnew
300  end do
301  if ( n_iter .eq. self%n_max_iter ) then
302  print*, 'Second iteration did not converge. Residual:', residual, self%tolerance
303  end if
304  n_iter_mean = n_iter_mean + n_iter
305  ! End of the loop
306 
307  if (abs(v_new(1))> 1e-16_f64) then
308  ! Now the charge deposition
309  call self%kernel_smoother_1%add_current( x_new, x_future, wi(1), self%j_dofs_local(:,1 ) )
310  end if
311  select type ( q=> self%kernel_smoother_0 )
313  call q%add_charge_int( x_new, x_future, wi(1)*v_new(2)*dtau, self%j_dofs_local(:,2 ) )
314  end select
315  end do
316 
317  x_new(1) = modulo(x_future(1), self%Lx)
318  call self%particle_group%group(i_sp)%set_x(i_part, x_new)
319  call self%particle_group%group(i_sp)%set_v(i_part, v_new)
320 
321  if (self%particle_group%group(i_sp)%n_weights == 3) then
322  ! Update weights if control variate
323  wp = self%particle_group%group(i_sp)%get_weights(i_part)
324  wp(3) = self%control_variate%cv(i_sp)%update_df_weight( x_new(1:1), vi(1:2), 0.0_f64, wp(1), wp(2))
325  call self%particle_group%group(i_sp)%set_weights(i_part, wp)
326  end if
327 
328  end do
329  end do
330 
331  write(self%file_id,*) real(n_iter_mean, f64)/real(self%particle_group%group(1)%n_particles,f64)/real(self%n_sub_iter,f64)
332 
333  self%j_dofs = 0.0_f64
334  ! MPI to sum up contributions from each processor
335  call sll_o_collective_allreduce( sll_v_world_collective, self%j_dofs_local(:,1), &
336  n_cells, mpi_sum, self%j_dofs(:,1))
337  call sll_o_collective_allreduce( sll_v_world_collective, self%j_dofs_local(:,2), &
338  n_cells, mpi_sum, self%j_dofs(:,2))
339 
340  call self%filter%apply_inplace( self%j_dofs(:,1) )
341  call self%filter%apply_inplace( self%j_dofs(:,2) )
342 
343  if ( self%jmean .eqv. .true. ) then
344  self%j_dofs(:,1) = self%j_dofs(:,1) - sum(self%j_dofs(:,1))/real(self%kernel_smoother_0%n_dofs, f64)
345  self%j_dofs(:,2) = self%j_dofs(:,2) - sum(self%j_dofs(:,2))/real(self%kernel_smoother_1%n_dofs, f64)
346  end if
347 
348  ! Update the electric field.
349  call self%maxwell_solver%compute_E_from_j(self%j_dofs(:,1), 1, self%efield_dofs(:,1))
350  call self%maxwell_solver%compute_E_from_j(self%j_dofs(:,2), 2, self%efield_dofs(:,2))
351 
352  call self%maxwell_solver%compute_E_from_B(&
353  dt, self%bfield_dofs, self%efield_dofs(:,2))
354 
355  end subroutine operatorhp_pic_vm_1d2v
356 
357 
359  !---------------------------------------------------------------------------!
360  subroutine operatorhp_pic_vm_1d2v_newton(self, dt)
361  class(sll_t_time_propagator_pic_vm_1d2v_subcyc), intent(inout) :: self
362  sll_real64, intent(in) :: dt
363 
364  !local variables
365  sll_int32 :: i_part
366  sll_real64 :: x_new(3), vi(3), wi(1), x_old(3), wp(3), x_future(3), v_new(3), v_nnew(3)
367  sll_int32 :: n_cells, i_sp
368  sll_real64 :: qoverm
369  sll_real64 :: efield(2), bfield_old, bfield_new
370  sll_real64 :: residual
371  sll_int32 :: n_iter, n_iter_mean
372  sll_int32 :: iter
373  sll_real64 :: dtau
374  sll_real64 :: gamma, residuum(2)
375  sll_real64 :: df11, df12, df21, df22, det
376 
377  dtau = dt/real(self%n_sub_iter,f64)
378 
379  n_iter_mean = 0
380 
381  n_cells = self%kernel_smoother_0%n_dofs
382 
383  self%j_dofs_local = 0.0_f64
384  self%rho_dofs_local = 0.0_f64
385 
386  ! Update the magnetic field
387  self%bfield_dofs_old = self%bfield_dofs
388  call self%maxwell_solver%compute_B_from_E( &
389  dt, self%efield_dofs(:,2), self%bfield_dofs)
390  call self%filter%apply( self%bfield_dofs, self%bfield_filter )
391 
392  call self%filter%apply( self%efield_dofs(:,1), self%efield_filter(:,1) )
393  call self%filter%apply( self%efield_dofs(:,2), self%efield_filter(:,2) )
394 
395  ! 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.
396  ! Then update particle position: X_new = X_old + dt * V
397  do i_sp = 1,self%n_species
398  qoverm = self%particle_group%group(i_sp)%species%q_over_m();
399  do i_part=1,self%particle_group%group(i_sp)%n_particles
400  ! Read out particle position and velocity
401  x_new = self%particle_group%group(i_sp)%get_x(i_part)
402  vi = self%particle_group%group(i_sp)%get_v(i_part)
403 
404  ! Get charge for accumulation of j
405  wi = self%particle_group%group(i_sp)%get_charge(i_part, self%i_weight)
406 
407  ! Then update particle position (first part): X_new = X_old + dt * 0.5 * V
408  x_old = x_new - dtau * vi
409 
410 
411  ! Now the first half of the velocity update
412  ! First we evaluate the electric field
413  call self%kernel_smoother_1%evaluate &
414  (x_new(1), self%efield_filter(:,1), efield(1))
415  call self%kernel_smoother_0%evaluate &
416  (x_new(1), self%efield_filter(:,2), efield(2))
417  select type ( q=> self%kernel_smoother_1 )
419  call q%evaluate_int_quad( x_old(1), x_new(1), self%bfield_dofs_old, bfield_old )
420  end select
421  ! Now we add up the fixed update into efield
422  ! Note: Use dtau also for efield for the variant with orbit-averaged electric field
423  efield(1) = qoverm * ( dt * efield(1) + dtau * vi(2) * bfield_old )
424  efield(2) = qoverm * ( dt * efield(2) - dtau * vi(1) * bfield_old )
425 
426  ! First guess for v_new = v_old
427  v_new(1) = vi(1) + efield(1) + qoverm * dtau * vi(2) * bfield_old
428  v_new(2) = vi(2) + efield(2) - qoverm * dtau * vi(1) * bfield_old
429  x_future = x_new + dtau * v_new
430 
431  select type ( q=> self%kernel_smoother_1 )
433  call q%evaluate_int_quad( x_future(1), x_new(1), self%bfield_filter, bfield_new )
434  end select
435 
436  residuum(1) = v_new(1) - ( vi(1) + efield(1) + qoverm * dtau * v_new(2) * bfield_new )
437  residuum(2) = v_new(2) - ( vi(2) + efield(2) - qoverm * dtau * v_new(1) * bfield_new )
438  residual = 1.0_f64!maxval( abs(residuum) )
439 
440  n_iter = 0
441  do while( residual > self%tolerance .and. n_iter < self%n_max_iter )
442  n_iter = n_iter + 1
443  select type ( q=> self%kernel_smoother_1 )
445  call q%evaluate_int_subnewton( x_new(1), x_future(1), self%bfield_filter, gamma )
446  gamma = gamma * qoverm
447  end select
448 
449  df11 = 1.0_f64 - gamma * v_new(2)
450  df12 = - qoverm * dtau * bfield_new
451  df21 = ( qoverm * dtau * bfield_new + gamma * v_new(1) )
452  df22 = 1.0_f64
453  det = df11*df22- df12*df21
454 
455  v_nnew(1) = v_new(1) - ( df22 * residuum(1) - df12 * residuum(2) )/det
456  v_nnew(2) = v_new(2) - ( - df21 * residuum(1) + df11 * residuum(2) )/det
457 
458  x_future = x_new + dtau * v_nnew
459  v_new = v_nnew
460  select type ( q=> self%kernel_smoother_1 )
462  call q%evaluate_int_quad( x_future(1), x_new(1), self%bfield_filter, bfield_new )
463  end select
464 
465  residuum(1) = v_new(1) - ( vi(1) + efield(1) + qoverm * dtau * v_new(2) * bfield_new )
466  residuum(2) = v_new(2) - ( vi(2) + efield(2) - qoverm * dtau * v_new(1) * bfield_new )
467  residual = maxval( abs(residuum) )
468  end do
469  v_nnew(1) = vi(1) + efield(1) + qoverm * dtau * v_new(2) * bfield_new
470  v_nnew(2) = vi(2) + efield(2) - qoverm * dtau * v_new(1) * bfield_new
471  v_new = v_nnew
472  x_future = x_new + dtau * v_nnew
473 
474  if ( n_iter .eq. self%n_max_iter ) then
475  print*, 'First iteration did not converge. Residual:', residual
476  end if
477  n_iter_mean = n_iter_mean + n_iter
478  ! End of the loop
479 
480  if (abs(v_new(1))> 1e-16_f64) then
481  ! Now the charge deposition
482  call self%kernel_smoother_1%add_current( x_new, x_future, wi(1), self%j_dofs_local(:,1 ) )
483  call self%kernel_smoother_0%add_current( x_new, x_future, wi(1)*v_new(2)/v_new(1), self%j_dofs_local(:,2 ) )
484  ! For rho check
485  ! call self%kernel_smoother_0%add_charge( x_new, wi(1), self%rho_dofs_local )
486  end if
487 
488  do iter=2,self%n_sub_iter
489  ! Then update particle position (second part): X_new = X_old + dt * 0.5 * V
490  x_old = x_new
491  x_new = x_future
492  vi = v_new
493 
494  ! Then the second half of the velocity update
495  ! First the fixed old part again
496  select type ( q=> self%kernel_smoother_1 )
498  call q%evaluate_int_quad( x_old(1), x_new(1), self%bfield_filter, bfield_old )
499  end select
500  !call self%kernel_smoother_1%evaluate( x_new(1), self%bfield_filter, bfield_old )
501  ! This is for the variant with orbit-averaged electric field
502 !!$ call self%kernel_smoother_1%evaluate &
503 !!$ (x_new(1), self%efield_filter(:,1), efield(1))
504 !!$ call self%kernel_smoother_0%evaluate &
505 !!$ (x_new(1), self%efield_filter(:,2), efield(2))
506 !!$ ! Now we add up the fixed update into efield
507 !!$ efield(1) = qoverm * ( dtau * efield(1) + dtau * vi(2) * bfield_old )
508 !!$ efield(2) = qoverm * ( dtau * efield(2) - dtau * vi(1) * bfield_old )
509  ! Now we add up the fixed update into efield
510  efield(1) = qoverm * dtau * ( vi(2) * bfield_old )
511  efield(2) = qoverm * dtau * (- vi(1) * bfield_old )
512 
513  ! First guess for v_new = v_old (already set)
514  v_new(1) = vi(1) + efield(1) + qoverm * dtau * vi(2) * bfield_old
515  v_new(2) = vi(2) + efield(2) - qoverm * dtau * vi(1) * bfield_old
516  x_future = x_new + dtau * v_new
517 
518  ! Loop here for convergence
519  select type ( q=> self%kernel_smoother_1 )
521  call q%evaluate_int_quad( x_future(1), x_new(1), self%bfield_filter, bfield_new )
522  end select
523 
524  residuum(1) = v_new(1) - ( vi(1) + efield(1) + qoverm * dtau * v_new(2) * bfield_new)
525  residuum(2) = v_new(2) - ( vi(2) + efield(2) - qoverm * dtau * v_new(1) * bfield_new )
526  residual = 1.0_f64!maxval( abs(residuum) )
527  n_iter = 0
528  do while( residual > self%tolerance .and. n_iter < self%n_max_iter )
529  n_iter = n_iter + 1
530  select type ( q=> self%kernel_smoother_1 )
532  call q%evaluate_int_subnewton( x_new(1), x_future(1), self%bfield_filter, gamma )
533  gamma = gamma * qoverm
534  end select
535 
536  df11 = 1.0_f64 - gamma * v_new(2)
537  df12 = - qoverm * dtau * bfield_new
538  df21 = ( qoverm * dtau * bfield_new + gamma * v_new(1) )
539  df22 = 1.0_f64
540  det = df11*df22- df12*df21
541 
542  v_nnew(1) = v_new(1) - ( df22 * residuum(1) - df12 * residuum(2) )/det
543  v_nnew(2) = v_new(2) - ( - df21 * residuum(1) + df11 * residuum(2) )/det
544 
545  x_future = x_new + dtau * v_nnew
546  v_new = v_nnew
547  select type ( q=> self%kernel_smoother_1 )
549  call q%evaluate_int_quad( x_future(1), x_new(1), self%bfield_filter, bfield_new )
550  end select
551 
552  residuum(1) = v_new(1) - ( vi(1) + efield(1) + qoverm * dtau * v_new(2) * bfield_new )
553  residuum(2) = v_new(2) - ( vi(2) + efield(2) - qoverm * dtau * v_new(1) * bfield_new )
554  residual = maxval( abs(residuum) )
555  end do
556  v_nnew(1) = vi(1) + efield(1) + qoverm * dtau * v_new(2) * bfield_new
557  v_nnew(2) = vi(2) + efield(2) - qoverm * dtau * v_new(1) * bfield_new
558  v_new = v_nnew
559  x_future = x_new + dtau * v_nnew
560  if ( n_iter .eq. self%n_max_iter ) then
561  print*, 'Second iteration did not converge. Residual:', residual, self%tolerance
562  end if
563  n_iter_mean = n_iter_mean + n_iter
564  ! End of the loop
565 
566  if (abs(v_new(1))> 1e-16_f64) then
567  ! Now the charge deposition
568  call self%kernel_smoother_1%add_current( x_new, x_future, wi(1), self%j_dofs_local(:,1 ) )
569  call self%kernel_smoother_0%add_current( x_new, x_future, wi(1)*v_new(2)/v_new(1), self%j_dofs_local(:,2 ) )
570  ! For rho check
571  !call self%kernel_smoother_0%add_charge( x_new, wi(1), self%rho_dofs_local )
572  end if
573  end do
574 
575  x_new(1) = modulo(x_future(1), self%Lx)
576  call self%particle_group%group(i_sp)%set_x(i_part, x_new)
577  call self%particle_group%group(i_sp)%set_v(i_part, v_new)
578 
579  if (self%particle_group%group(i_sp)%n_weights == 3) then
580  ! Update weights if control variate
581  wp = self%particle_group%group(i_sp)%get_weights(i_part)
582  wp(3) = self%control_variate%cv(i_sp)%update_df_weight( x_new(1:1), vi(1:2), 0.0_f64, wp(1), wp(2))
583  call self%particle_group%group(i_sp)%set_weights(i_part, wp)
584  end if
585 
586  end do
587  end do
588 
589  write(self%file_id,*) real(n_iter_mean, f64)/real(self%particle_group%group(1)%n_particles,f64)/real(self%n_sub_iter,f64)
590 
591  ! Temporary check of Gauss law for the case with substepping in the electric field
592  ! self%j_dofs = 0.0_f64
593  ! call sll_o_collective_allreduce( sll_v_world_collective, self%rho_dofs_local, n_cells, &
594  ! MPI_SUM, self%j_dofs(:,1) )
595  ! call self%maxwell_solver%compute_rho_from_e( self%efield_dofs(:,1), self%j_dofs(:,2) )
596 
597  ! self%j_dofs(:,1) = self%j_dofs(:,1)/real(self%n_sub_iter,f64)
598  ! self%j_dofs(:,1) = self%j_dofs(:,1) - sum(self%j_dofs(:,1))/real(n_cells, f64)
599  ! write(self%file_id_rho,*) maxval( abs(self%j_dofs(:,1) - self%j_dofs(:,2) ) )
600 
601 
602  self%j_dofs = 0.0_f64
603  ! MPI to sum up contributions from each processor
604  call sll_o_collective_allreduce( sll_v_world_collective, self%j_dofs_local(:,1), &
605  n_cells, mpi_sum, self%j_dofs(:,1))
606  call sll_o_collective_allreduce( sll_v_world_collective, self%j_dofs_local(:,2), &
607  n_cells, mpi_sum, self%j_dofs(:,2))
608 
609  call self%filter%apply_inplace( self%j_dofs(:,1) )
610  call self%filter%apply_inplace( self%j_dofs(:,2) )
611 
612 
613  if ( self%jmean .eqv. .true. ) then
614  self%j_dofs(:,1) = self%j_dofs(:,1) - sum(self%j_dofs(:,1))/real(self%kernel_smoother_0%n_dofs, f64)
615  self%j_dofs(:,2) = self%j_dofs(:,2) - sum(self%j_dofs(:,2))/real(self%kernel_smoother_1%n_dofs, f64)
616  end if
617  ! Update the electric field.
618  call self%maxwell_solver%compute_E_from_j(self%j_dofs(:,1), 1, self%efield_dofs(:,1))
619  call self%maxwell_solver%compute_E_from_j(self%j_dofs(:,2), 2, self%efield_dofs(:,2))
620 
621  call self%maxwell_solver%compute_E_from_B(&
622  dt, self%bfield_dofs, self%efield_dofs(:,2))
623 
624 
625  end subroutine operatorhp_pic_vm_1d2v_newton
626 
627 
628 
629 
630  !---------------------------------------------------------------------------!
633  self, &
634  maxwell_solver, &
635  kernel_smoother_0, &
636  kernel_smoother_1, &
637  particle_group, &
638  efield_dofs, &
639  bfield_dofs, &
640  x_min, &
641  Lx, &
642  n_sub_iter, &
643  file_prefix, &
644  filter, &
645  jmean, &
646  control_variate, &
647  i_weight)
648  class(sll_t_time_propagator_pic_vm_1d2v_subcyc), intent(out) :: self
649  class(sll_c_maxwell_1d_base), target, intent(in) :: maxwell_solver
650  class(sll_c_particle_mesh_coupling_1d), target, intent(in) :: kernel_smoother_0
651  class(sll_c_particle_mesh_coupling_1d), target, intent(in) :: kernel_smoother_1
652  class(sll_t_particle_array), target, intent(in) :: particle_group
653  sll_real64, target, intent(in) :: efield_dofs(:,:)
654  sll_real64, target, intent(in) :: bfield_dofs(:)
655  sll_real64, intent(in) :: x_min
656  sll_real64, intent(in) :: lx
657  sll_int32, intent(in) :: n_sub_iter
658  character(*), intent(in) :: file_prefix
659  type( sll_t_binomial_filter ), intent( in ), target :: filter
660  logical, optional, intent(in) :: jmean
661  class(sll_t_control_variates), optional, target, intent(in) :: control_variate
662  sll_int32, optional, intent(in) :: i_weight
663 
664  !local variables
665  sll_int32 :: ierr
666 
667  print*, 'Subcycling propagator with efield'
668 
669  self%maxwell_solver => maxwell_solver
670  self%kernel_smoother_0 => kernel_smoother_0
671  self%kernel_smoother_1 => kernel_smoother_1
672 
673  self%n_species = particle_group%n_species
674 
675  self%particle_group => particle_group
676  self%efield_dofs => efield_dofs
677  self%bfield_dofs => bfield_dofs
678 
679  self%n_sub_iter = n_sub_iter
680 
681  ! Check that n_dofs is the same for both kernel smoothers.
682  sll_assert( self%kernel_smoother_0%n_dofs == self%kernel_smoother_1%n_dofs )
683 
684  sll_allocate(self%j_dofs(self%kernel_smoother_0%n_dofs,2), ierr)
685  sll_allocate(self%j_dofs_local(self%kernel_smoother_0%n_dofs,2), ierr)
686  sll_allocate(self%rho_dofs_local(self%kernel_smoother_0%n_dofs), ierr)
687  sll_allocate(self%efield_filter(self%kernel_smoother_1%n_dofs,2), ierr)
688  sll_allocate(self%bfield_filter(self%kernel_smoother_0%n_dofs), ierr)
689  sll_allocate(self%bfield_dofs_old(self%kernel_smoother_0%n_dofs), ierr)
690 
691  self%spline_degree = self%kernel_smoother_0%spline_degree
692  self%x_min = x_min
693  self%Lx = lx
694  self%delta_x = self%Lx/real(self%kernel_smoother_1%n_dofs, f64)
695 
696  self%cell_integrals_1 = [0.5_f64, 2.0_f64, 0.5_f64]
697  self%cell_integrals_1 = self%cell_integrals_1 / 3.0_f64
698 
699  self%cell_integrals_0 = [1.0_f64,11.0_f64,11.0_f64,1.0_f64]
700  self%cell_integrals_0 = self%cell_integrals_0 / 24.0_f64
701 
702  self%filter => filter
703 
704  call self%filter%apply( self%efield_dofs(:,1), self%efield_filter(:,1) )
705  call self%filter%apply( self%efield_dofs(:,2), self%efield_filter(:,2) )
706  call self%filter%apply( self%bfield_dofs, self%bfield_filter )
707 
708  if (present(jmean)) then
709  self%jmean = jmean
710  end if
711 
712  self%i_weight = 1
713  if (present(i_weight)) self%i_weight = i_weight
714  if(present(control_variate)) then
715  allocate(self%control_variate )
716  allocate(self%control_variate%cv(self%n_species) )
717  self%control_variate => control_variate
718  end if
719 
720  ! File to write out no. of iterations
721  open(newunit=self%file_id, file=trim(file_prefix)//"_subcycling_iterations.dat",action='write')
722 
723  open(newunit=self%file_id_rho, file=trim(file_prefix)//"_rho.dat",action='write')
724 
725 
726  end subroutine initialize_pic_vm_1d2v
727 
728  !---------------------------------------------------------------------------!
730  subroutine delete_pic_vm_1d2v(self)
731  class(sll_t_time_propagator_pic_vm_1d2v_subcyc), intent( inout ) :: self
732 
733  deallocate(self%j_dofs)
734  deallocate(self%j_dofs_local)
735  self%maxwell_solver => null()
736  self%kernel_smoother_0 => null()
737  self%kernel_smoother_1 => null()
738  self%particle_group => null()
739  self%efield_dofs => null()
740  self%bfield_dofs => null()
741 
742  end subroutine delete_pic_vm_1d2v
743 
744 
745  !---------------------------------------------------------------------------!
748  splitting, &
749  maxwell_solver, &
750  kernel_smoother_0, &
751  kernel_smoother_1, &
752  particle_group, &
753  efield_dofs, &
754  bfield_dofs, &
755  x_min, &
756  Lx, &
757  n_sub_iter, &
758  file_prefix, &
759  filter, &
760  jmean, &
761  control_variate, &
762  i_weight)
763  class(sll_c_time_propagator_base), allocatable, intent(out) :: splitting
764  class(sll_c_maxwell_1d_base), target, intent(in) :: maxwell_solver
765  class(sll_c_particle_mesh_coupling_1d), target, intent(in) :: kernel_smoother_0
766  class(sll_c_particle_mesh_coupling_1d), target, intent(in) :: kernel_smoother_1
767  class(sll_t_particle_array), target, intent(in) :: particle_group
768  !class(sll_c_particle_group_base),target, intent(in) :: particle_group(:) !< Particle group
769  sll_real64, target, intent(in) :: efield_dofs(:,:)
770  sll_real64, target, intent(in) :: bfield_dofs(:)
771  sll_real64, intent(in) :: x_min
772  sll_real64, intent(in) :: lx
773  sll_int32, intent(in) :: n_sub_iter
774  character(*), intent(in) :: file_prefix
775  type( sll_t_binomial_filter ), intent( in ), target :: filter
776  logical, optional, intent(in) :: jmean
777  class(sll_t_control_variates), optional, target, intent(in) :: control_variate
778  sll_int32, optional, intent(in) :: i_weight
779 
780  !local variables
781  sll_int32 :: ierr
782  logical :: jmean_val
783 
784  sll_allocate(sll_t_time_propagator_pic_vm_1d2v_subcyc :: splitting, ierr)
785 
786  if (present(jmean) ) then
787  jmean_val = jmean
788  else
789  jmean_val = .false.
790  end if
791 
792  select type (splitting)
794  if (present(control_variate) ) then
795  call splitting%init(&
796  maxwell_solver, &
797  kernel_smoother_0, &
798  kernel_smoother_1, &
799  particle_group, &
800  efield_dofs, &
801  bfield_dofs, &
802  x_min, &
803  lx, &
804  n_sub_iter, &
805  file_prefix, &
806  filter, &
807  jmean_val, &
808  control_variate, &
809  i_weight)
810  else
811  call splitting%init(&
812  maxwell_solver, &
813  kernel_smoother_0, &
814  kernel_smoother_1, &
815  particle_group, &
816  efield_dofs, &
817  bfield_dofs, &
818  x_min, &
819  lx, &
820  n_sub_iter, &
821  file_prefix, &
822  filter, &
823  jmean_val)
824  end if
825  end select
826 
828 
829  !---------------------------------------------------------------------------!
832  splitting, &
833  maxwell_solver, &
834  kernel_smoother_0, &
835  kernel_smoother_1, &
836  particle_group, &
837  efield_dofs, &
838  bfield_dofs, &
839  x_min, &
840  Lx, &
841  n_sub_iter, &
842  file_prefix, &
843  filter, &
844  jmean)
845  class(sll_c_time_propagator_base), pointer, intent(out) :: splitting
846  class(sll_c_maxwell_1d_base), target, intent(in) :: maxwell_solver
847  class(sll_c_particle_mesh_coupling_1d), target, intent(in) :: kernel_smoother_0
848  class(sll_c_particle_mesh_coupling_1d), target, intent(in) :: kernel_smoother_1
849  !class(sll_c_particle_group_base),target, intent(in) :: particle_group(:) !< Particle group
850  class(sll_t_particle_array), target, intent(in) :: particle_group
851  sll_real64, target, intent(in) :: efield_dofs(:,:)
852  sll_real64, target, intent(in) :: bfield_dofs(:)
853  sll_real64, intent(in) :: x_min
854  sll_real64, intent(in) :: lx
855  sll_int32, intent(in) :: n_sub_iter
856  character(*), intent(in) :: file_prefix
857  type( sll_t_binomial_filter ), intent( in ), target :: filter
858  logical, optional, intent(in) :: jmean
859 
860 
861 
862  !local variables
863  sll_int32 :: ierr
864  logical :: jmean_val
865 
866  sll_allocate(sll_t_time_propagator_pic_vm_1d2v_subcyc :: splitting, ierr)
867 
868 
869  if (present(jmean) ) then
870  jmean_val = jmean
871  else
872  jmean_val = .false.
873  end if
874 
875  select type (splitting)
877  call splitting%init(&
878  maxwell_solver, &
879  kernel_smoother_0, &
880  kernel_smoother_1, &
881  particle_group, &
882  efield_dofs, &
883  bfield_dofs, &
884  x_min, &
885  lx, &
886  n_sub_iter, &
887  file_prefix, &
888  filter, &
889  jmean_val)
890  end select
891 
893 
Combines values from all processes and distributes the result back to all processes.
Binomial filter for smooting of fields.
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.
Kernel smoother for 1d with splines of arbitrary degree placed on a uniform mesh.
Base class for Hamiltonian splittings.
Particle pusher based on the subcycling algorithm for the 1d2v Vlasov-Maxwell equation.
subroutine lie_splitting_pic_vm_1d2v(self, dt, number_steps)
Lie splitting.
subroutine, public sll_s_new_time_propagator_pic_vm_1d2v_subcyc_ptr(splitting, maxwell_solver, kernel_smoother_0, kernel_smoother_1, particle_group, efield_dofs, bfield_dofs, x_min, Lx, n_sub_iter, file_prefix, filter, jmean)
Constructor for pointer abstract type.
subroutine lie_splitting_back_pic_vm_1d2v(self, dt, number_steps)
Lie splitting (oposite ordering)
subroutine strang_splitting_pic_vm_1d2v(self, dt, number_steps)
Strang splitting.
subroutine operatorhp_pic_vm_1d2v_newton(self, dt)
Implementation of the full time step with Newton iteration for the nonlinear part.
subroutine, public sll_s_new_time_propagator_pic_vm_1d2v_subcyc(splitting, maxwell_solver, kernel_smoother_0, kernel_smoother_1, particle_group, efield_dofs, bfield_dofs, x_min, Lx, n_sub_iter, file_prefix, filter, jmean, control_variate, i_weight)
Constructor for allocatable abstract type.
subroutine initialize_pic_vm_1d2v(self, maxwell_solver, kernel_smoother_0, kernel_smoother_1, particle_group, efield_dofs, bfield_dofs, x_min, Lx, n_sub_iter, file_prefix, filter, jmean, control_variate, i_weight)
Constructor.
real(kind=f64) function, dimension(size(particle, 2)) control_variate(particle)
    Report Typos and Errors