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_3d3v_helper.F90
Go to the documentation of this file.
1 
6  !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 #include "sll_assert.h"
8 #include "sll_errors.h"
9 #include "sll_memory.h"
10 #include "sll_working_precision.h"
11 
12  use sll_m_collective, only: &
16 
17  use sll_m_control_variate, only: &
19 
20  use sll_m_filter_base_3d, only: &
22 
28 
29  use sll_m_initial_distribution, only: &
31 
32  use sll_m_linear_operator_block, only : &
34 
35  use sll_m_particle_mass_3d_base, only: &
37 
40 
43 
46 
49 
50  use sll_m_linear_solver_cg, only : &
52 
53  use sll_m_maxwell_3d_base, only: &
55 
56  use mpi, only: &
57  mpi_sum, &
58  mpi_max
59 
60  use sll_m_particle_group_base, only: &
62 
65 
66  use sll_m_preconditioner_fft, only : &
68 
69  use sll_m_preconditioner_singular, only : &
71 
72  use sll_m_profile_functions, only: &
74 
75  implicit none
76 
77  public :: &
79 
80  private
81  !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
84  class(sll_c_maxwell_3d_base), pointer :: maxwell_solver
85  class(sll_c_particle_mesh_coupling_3d), pointer :: particle_mesh_coupling
86  class(sll_t_particle_array), pointer :: particle_group
87 
88  type( sll_t_linear_operator_schur_phiv_3d ) :: linear_op_schur_phiv
89  type( sll_t_linear_solver_cg ) :: linear_solver_schur_phiv
90  type(sll_t_preconditioner_fft) :: preconditioner_fft
91  type(sll_t_preconditioner_singular) :: preconditioner1
92  type( sll_t_linear_operator_schur_ev_3d ) :: linear_op_schur_ev
93  type( sll_t_linear_solver_cg ) :: linear_solver_schur_ev
94  class(sll_c_particle_mass_3d_base), allocatable :: particle_mass_1
95  class(sll_c_particle_mass_3d_base), allocatable :: particle_mass_2
96  class(sll_c_particle_mass_3d_base), allocatable :: particle_mass_3
97  type( sll_t_linear_operator_block ) :: particle_mass_op
98 
99 
100  sll_int32 :: spline_degree(3)
101  sll_real64 :: lx(3)
102  sll_real64 :: x_min(3)
103  sll_real64 :: x_max(3)
104  sll_int32 :: n_total0
105  sll_int32 :: n_total1
106  sll_int32 :: nspan(3)
107  sll_real64 :: betar(2) = 1._f64
108 
109  sll_real64, pointer :: phi_dofs(:)
110  sll_real64, pointer :: efield_dofs(:)
111  sll_real64, pointer :: bfield_dofs(:)
112  sll_real64, allocatable :: j_dofs(:)
113  sll_real64, allocatable :: j_dofs_local(:)
114  sll_real64, allocatable :: particle_mass_1_local(:,:)
115  sll_real64, allocatable :: particle_mass_2_local(:,:)
116  sll_real64, allocatable :: particle_mass_3_local(:,:)
117 
118  sll_real64 :: solver_tolerance = 1d-12
119  sll_real64 :: iter_tolerance = 1d-10
120  sll_int32 :: max_iter = 10
121 
122  sll_int32 :: boundary_particles = 100
123  sll_int32 :: counter_left = 0
124  sll_int32 :: counter_right = 0
125  sll_real64, pointer :: rhob(:) => null()
126  sll_real64 :: force_sign = 1._f64
127  logical :: adiabatic_electrons = .false.
128  logical :: jmean = .false.
129  logical :: boundary = .false.
130 
131  sll_int32 :: n_particles_max ! Helper variable for size of temporary particle arrays
132  sll_real64, allocatable :: xnew(:,:,:)
133  sll_real64, allocatable :: vnew(:,:,:)
134  sll_real64, allocatable :: efield_dofs_new(:)
135  sll_real64, allocatable :: efield_dofs_work(:)
136  sll_real64, allocatable :: phi_dofs_new(:)
137  sll_real64, allocatable :: phi_dofs_work(:)
138 
139  sll_int32 :: n_failed = 0
140  sll_int32 :: iter_counter = 0
141  sll_int32 :: niter(100000)
142 
143  ! For control variate
144  class(sll_t_control_variates), pointer :: control_variate => null()
145  sll_int32 :: i_weight = 1
146 
147  !filtering
148  sll_real64, allocatable :: efield_filter(:)
149  sll_real64, allocatable :: bfield_filter(:)
150  class(sll_c_filter_base_3d), pointer :: filter
151 
152  contains
153  procedure :: advect_x => advect_x_pic_vm_3d3v
154  procedure :: advect_eb => advect_eb_schur_pic_vm_3d3v
155  procedure :: advect_vb => advect_vb_pic_vm_3d3v
156  procedure :: advect_e => advect_e_pic_vm_3d3v
157  procedure :: advect_e_trunc => advect_e_pic_vm_3d3v_trunc
158  procedure :: advect_ex => advect_ex_pic_vm_3d3v
159 
160  procedure :: init => initialize_pic_vm_3d3v
161  procedure :: init_from_file => initialize_file_pic_vm_3d3v
162  procedure :: free => delete_pic_vm_3d3v
163 
165 
166 contains
167 
168 
169  !---------------------------------------------------------------------------!
172  subroutine advect_x_pic_vm_3d3v ( self, dt )
173  class(sll_t_time_propagator_pic_vm_3d3v_helper), intent(inout) :: self
174  sll_real64, intent(in) :: dt
175  !local variables
176  sll_int32 :: i_part, i_sp
177  sll_real64 :: xi(3), xnew(3), vi(3), wall(3)
178 
179  do i_sp = 1, self%particle_group%n_species
180  do i_part=1,self%particle_group%group(i_sp)%n_particles
181  ! Read out particle position and velocity
182  xi = self%particle_group%group(i_sp)%get_x(i_part)
183  vi = self%particle_group%group(i_sp)%get_v(i_part)
184 
185  xnew = xi + dt * vi
186  call compute_particle_boundary( self, xi, xnew, vi )
187 
188 
189  call self%particle_group%group(i_sp)%set_x ( i_part, xnew )
190  call self%particle_group%group(i_sp)%set_v ( i_part, vi )
191  ! Update particle weights
192  if (self%particle_group%group(i_sp)%n_weights == 3 ) then
193  wall = self%particle_group%group(i_sp)%get_weights(i_part)
194  wall(3) = self%control_variate%cv(i_sp)%update_df_weight( xnew, vi, 0.0_f64, wall(1), wall(2) )
195  call self%particle_group%group(i_sp)%set_weights( i_part, wall )
196  end if
197  end do
198  end do
199 
200  end subroutine advect_x_pic_vm_3d3v
201 
202 
204  subroutine compute_particle_boundary( self, xold, xnew, vi )
205  class(sll_t_time_propagator_pic_vm_3d3v_helper), intent( inout ) :: self
206  sll_real64, intent( inout ) :: xold(3)
207  sll_real64, intent( inout ) :: xnew(3)
208  sll_real64, intent( inout ) :: vi(3)
209  !local variables
210  sll_real64 ::xbar
211 
212  if(xnew(1) < -self%x_max(1) .or. xnew(1) > 2._f64*self%x_max(1) ) then
213  print*, xnew
214  sll_error("particle boundary", "particle out of bound")
215  else if(xnew(1) < self%x_min(1) .or. xnew(1) > self%x_max(1) )then
216  if(xnew(1) < self%x_min(1) )then
217  xbar = self%x_min(1)
218  self%counter_left = self%counter_left+1
219  else if(xnew(1) > self%x_max(1))then
220  xbar = self%x_max(1)
221  self%counter_right = self%counter_right+1
222  end if
223 
224  select case(self%boundary_particles)
226  xnew(1) = 2._f64*xbar-xnew(1)
227  vi(1) = -vi(1)
230  xnew(1) = self%x_min(1) + modulo(xnew(1)-self%x_min(1), self%Lx(1))
231  case default
232  xnew(1) = self%x_min(1) + modulo(xnew(1)-self%x_min(1), self%Lx(1))
233  end select
234  end if
235  xnew(2:3) = self%x_min(2:3) + modulo(xnew(2:3)-self%x_min(2:3), self%Lx(2:3))
236 
237  end subroutine compute_particle_boundary
238 
239 
240  !---------------------------------------------------------------------------!
243  subroutine advect_vb_pic_vm_3d3v ( self, dt )
244  class(sll_t_time_propagator_pic_vm_3d3v_helper), intent(inout) :: self
245  sll_real64, intent(in) :: dt
246  !local variables
247  sll_int32 :: i_part, i_sp
248  sll_real64 :: qmdt
249  sll_real64 :: vi(3), vnew(3), xi(3), wall(3)
250  sll_real64 :: bfield(3), bsq(3), denom
251 
252  self%bfield_filter = self%bfield_dofs
253  call self%filter%apply_inplace(self%bfield_filter(1:self%n_total0))
254  call self%filter%apply_inplace(self%bfield_filter(self%n_total0+1:self%n_total0+self%n_total1))
255  call self%filter%apply_inplace(self%bfield_filter(self%n_total0+self%n_total1+1:self%n_total0+2*self%n_total1))
256 
257  do i_sp = 1, self%particle_group%n_species
258  qmdt = self%particle_group%group(i_sp)%species%q_over_m()*dt*0.5_f64;
259  do i_part = 1, self%particle_group%group(i_sp)%n_particles
260  vi = self%particle_group%group(i_sp)%get_v(i_part)
261  xi = self%particle_group%group(i_sp)%get_x(i_part)
262  call self%particle_mesh_coupling%evaluate &
263  (xi, [self%spline_degree(1), self%spline_degree(2)-1, self%spline_degree(3)-1], self%bfield_filter(1:self%n_total0), bfield(1))
264  call self%particle_mesh_coupling%evaluate &
265  (xi, [self%spline_degree(1)-1, self%spline_degree(2), self%spline_degree(3)-1],self%bfield_filter(self%n_total0+1:self%n_total0+self%n_total1), bfield(2))
266  call self%particle_mesh_coupling%evaluate &
267  (xi, [self%spline_degree(1)-1, self%spline_degree(2)-1, self%spline_degree(3)],self%bfield_filter(self%n_total0+self%n_total1+1:self%n_total0+2*self%n_total1), bfield(3))
268 
269 
270  bfield = qmdt*bfield
271  bsq = bfield**2
272  denom = sum(bsq)+1.0_f64
273 
274  vnew(1) = ( (bsq(1)-bsq(2)-bsq(3)+1.0_f64)*vi(1) + &
275  2.0_f64*(bfield(1)*bfield(2)+bfield(3))*vi(2) + &
276  2.0_f64*(bfield(1)*bfield(3)-bfield(2))*vi(3) )/denom
277  vnew(2) = ( 2.0_f64*(bfield(1)*bfield(2)-bfield(3))*vi(1) + &
278  (-bsq(1)+bsq(2)-bsq(3)+1.0_f64)*vi(2) + &
279  2.0_f64*(bfield(2)*bfield(3)+bfield(1))*vi(3) )/denom
280  vnew(3) = ( 2.0_f64*(bfield(1)*bfield(3)+bfield(2))*vi(1) + &
281  2.0_f64*(bfield(2)*bfield(3)-bfield(1))*vi(2) + &
282  (-bsq(1)-bsq(2)+bsq(3)+1.0_f64)*vi(3) )/denom
283 
284 
285  call self%particle_group%group(i_sp)%set_v( i_part, vnew )
286 
287  ! Update particle weights
288  if (self%particle_group%group(i_sp)%n_weights == 3 ) then
289  wall = self%particle_group%group(i_sp)%get_weights(i_part)
290  wall(3) = self%control_variate%cv(i_sp)%update_df_weight( xi, vnew, 0.0_f64, wall(1), wall(2) )
291  call self%particle_group%group(i_sp)%set_weights( i_part, wall )
292  end if
293  end do
294  end do
295 
296  end subroutine advect_vb_pic_vm_3d3v
297 
298 
299  !---------------------------------------------------------------------------!
304  subroutine advect_eb_schur_pic_vm_3d3v ( self, dt )
305  class(sll_t_time_propagator_pic_vm_3d3v_helper), intent(inout) :: self
306  sll_real64, intent(in) :: dt
307 
308  call self%maxwell_solver%compute_curl_part( dt, self%efield_dofs, self%bfield_dofs, self%betar(1) )
309 
310  end subroutine advect_eb_schur_pic_vm_3d3v
311 
312 
313  !---------------------------------------------------------------------------!
318  ! TODO: Here we need to merge at least the steps for the particle mass to improve the scaling
319  subroutine advect_e_pic_vm_3d3v( self, dt )
320  class(sll_t_time_propagator_pic_vm_3d3v_helper), intent(inout) :: self
321  sll_real64, intent(in) :: dt
322  ! local variables
323  sll_int32 :: i_part, i_sp, j
324  sll_real64 :: vi(3), xi(3), wi(1), wall(3)
325  sll_real64 :: qoverm, factor
326  sll_real64 :: efield(3)
327  sll_real64 :: rhs(self%n_total1+2*self%n_total0)
328 
329  ! Set to zero
330  self%j_dofs_local = 0.0_f64
331  self%particle_mass_1_local = 0.0_f64
332  self%particle_mass_2_local = 0.0_f64
333  self%particle_mass_3_local = 0.0_f64
334 
335  self%efield_filter = self%efield_dofs
336  call self%filter%apply_inplace(self%efield_filter(1:self%n_total1))
337  call self%filter%apply_inplace(self%efield_filter(self%n_total1+1:self%n_total1+self%n_total0))
338  call self%filter%apply_inplace(self%efield_filter(self%n_total1+self%n_total0+1:self%n_total1+2*self%n_total0))
339 
340 
341  ! First particle loop
342  do i_sp = 1, self%particle_group%n_species
343  qoverm = self%particle_group%group(i_sp)%species%q_over_m();
344  if( self%adiabatic_electrons ) then
345  factor = dt**2 * 0.25_f64* qoverm
346  else
347  factor = dt**2 * 0.25_f64* qoverm*self%betar(2)
348  end if
349  do i_part = 1, self%particle_group%group(i_sp)%n_particles
350  vi = self%particle_group%group(i_sp)%get_v(i_part)
351  xi = self%particle_group%group(i_sp)%get_x(i_part)
352 
353  ! Get charge for accumulation of j
354  wi = self%particle_group%group(i_sp)%get_charge(i_part, self%i_weight)
355 
356  ! Accumulate the particle mass matrix diagonals
357  call self%particle_mesh_coupling%add_particle_mass( xi, wi(1) * factor, &
358  [self%spline_degree(1)-1, self%spline_degree(2), self%spline_degree(3)], &
359  self%particle_mass_1_local )
360  call self%particle_mesh_coupling%add_particle_mass( xi, wi(1) * factor, &
361  [self%spline_degree(1), self%spline_degree(2)-1, self%spline_degree(3)], &
362  self%particle_mass_2_local )
363  call self%particle_mesh_coupling%add_particle_mass( xi, wi(1) * factor, &
364  [self%spline_degree(1), self%spline_degree(2), self%spline_degree(3)-1], &
365  self%particle_mass_3_local )
366 
367  ! Accumulate j
368  call self%particle_mesh_coupling%add_charge( xi, wi(1)*vi(1), &
369  [self%spline_degree(1)-1, self%spline_degree(2), self%spline_degree(3)], &
370  self%j_dofs_local(1:self%n_total1) )
371  call self%particle_mesh_coupling%add_charge( xi, wi(1)*vi(2), &
372  [self%spline_degree(1), self%spline_degree(2)-1, self%spline_degree(3)], &
373  self%j_dofs_local(1+self%n_total1:self%n_total1+self%n_total0) )
374  call self%particle_mesh_coupling%add_charge( xi, wi(1)*vi(3), &
375  [self%spline_degree(1), self%spline_degree(2), self%spline_degree(3)-1], &
376  self%j_dofs_local(1+self%n_total1+self%n_total0:self%n_total1+2*self%n_total0) )
377 
378  ! Evaulate E at particle position and propagate v a half step
379  call self%particle_mesh_coupling%evaluate &
380  (xi, [self%spline_degree(1)-1, self%spline_degree(2), self%spline_degree(3)], &
381  self%efield_filter(1:self%n_total1), efield(1))
382  call self%particle_mesh_coupling%evaluate &
383  (xi, [self%spline_degree(1), self%spline_degree(2)-1, self%spline_degree(3)], &
384  self%efield_filter(self%n_total1+1:self%n_total1+self%n_total0), efield(2))
385  call self%particle_mesh_coupling%evaluate &
386  (xi, [self%spline_degree(1), self%spline_degree(2), self%spline_degree(3)-1], &
387  self%efield_filter(self%n_total1+self%n_total0+1:self%n_total1+2*self%n_total0), efield(3))
388 
389  ! velocity update
390  vi = vi + dt* 0.5_f64* qoverm * efield
391  call self%particle_group%group(i_sp)%set_v( i_part, vi )
392  ! Update particle weights
393  if (self%particle_group%group(i_sp)%n_weights == 3 ) then
394  wall = self%particle_group%group(i_sp)%get_weights(i_part)
395  wall(3) = self%control_variate%cv(i_sp)%update_df_weight( xi, vi, 0.0_f64, wall(1), wall(2) )
396  call self%particle_group%group(i_sp)%set_weights( i_part, wall )
397  end if
398 
399  end do
400  end do
401  self%j_dofs = 0.0_f64
402  self%particle_mass_1%particle_mass = 0.0_f64
403  self%particle_mass_2%particle_mass = 0.0_f64
404  self%particle_mass_3%particle_mass = 0.0_f64
405  ! MPI to sum up contributions from each processor
406  call sll_o_collective_allreduce( sll_v_world_collective, self%j_dofs_local, &
407  self%n_total1+self%n_total0*2, mpi_sum, self%j_dofs )
408 
409  if ( self%jmean ) then
410  self%j_dofs(1:self%n_total1) = self%j_dofs(1:self%n_total1) - sum(self%j_dofs(1:self%n_total1))/real(self%n_total1, f64)
411  self%j_dofs(1+self%n_total1:self%n_total1+self%n_total0) = self%j_dofs(1+self%n_total1:self%n_total1+self%n_total0) - sum(self%j_dofs(1+self%n_total1:self%n_total1+self%n_total0))/real(self%n_total0, f64)
412  self%j_dofs(1+self%n_total1+self%n_total0:self%n_total1+2*self%n_total0) = self%j_dofs(1+self%n_total1+self%n_total0:self%n_total1+2*self%n_total0) - sum(self%j_dofs(1+self%n_total1+self%n_total0:self%n_total1+2*self%n_total0))/real(self%n_total0, f64)
413  end if
414 
415  call self%filter%apply_inplace(self%j_dofs(1:self%n_total1))
416  call self%filter%apply_inplace(self%j_dofs(self%n_total1+1:self%n_total1+self%n_total0))
417  call self%filter%apply_inplace(self%j_dofs(self%n_total1+self%n_total0+1:self%n_total1+2*self%n_total0))
418 
419 
420 
421  call sll_o_collective_allreduce( sll_v_world_collective, self%particle_mass_1_local, &
422  self%n_total1*self%nspan(1) , mpi_sum, self%particle_mass_1%particle_mass)
423  call sll_o_collective_allreduce( sll_v_world_collective, self%particle_mass_2_local, &
424  self%n_total0*self%nspan(2) , mpi_sum, self%particle_mass_2%particle_mass)
425  call sll_o_collective_allreduce( sll_v_world_collective, self%particle_mass_3_local, &
426  self%n_total0*self%nspan(3) , mpi_sum, self%particle_mass_3%particle_mass)
427 
428 
429  call self%particle_mass_op%set( 1, 1, self%particle_mass_1 )
430  call self%particle_mass_op%set( 2, 2, self%particle_mass_2 )
431  call self%particle_mass_op%set( 3, 3, self%particle_mass_3 )
432 
433  if( self%adiabatic_electrons ) then
434  ! Compute rhs
435  self%linear_op_schur_phiv%sign = -1._f64
436  call self%linear_op_schur_phiv%dot(self%phi_dofs, rhs(1: self%n_total0 ))
437  call self%maxwell_solver%multiply_gt( self%j_dofs, rhs(self%n_total0+1: 2*self%n_total0 ) )
438  rhs(1: self%n_total0 ) = rhs(1: self%n_total0 ) + dt * rhs(self%n_total0+1: 2*self%n_total0 )
439 
440  ! Solve the Schur complement
441  self%linear_op_schur_phiv%sign = 1._f64
442  call self%linear_solver_schur_phiv%set_guess(self%phi_dofs)
443  call self%linear_solver_schur_phiv%solve( rhs(1: self%n_total0 ), self%phi_dofs )
444  if( self%boundary ) then !perfect conductor boundary
445  do j = 1, self%maxwell_solver%n_dofs(2)*self%maxwell_solver%n_dofs(3)
446  self%phi_dofs(1+(j-1)*self%maxwell_solver%n_dofs(1)) = 0._f64
447  self%phi_dofs(j*self%maxwell_solver%n_dofs(1)) = 0._f64
448  end do
449  end if
450  call self%maxwell_solver%multiply_g( self%phi_dofs, self%efield_dofs )
451  self%efield_dofs = -self%efield_dofs
452  else
453  ! Compute rhs
454  self%linear_op_schur_ev%sign = -self%force_sign
455  call self%linear_op_schur_ev%dot(self%efield_dofs, rhs)
456  rhs = rhs - self%force_sign *dt * self%betar(2) * self%j_dofs
457 
458  ! Solve the Schur complement
459  self%linear_op_schur_ev%sign = self%force_sign
460  call self%linear_solver_schur_ev%set_guess(self%efield_dofs)
461  call self%linear_solver_schur_ev%solve(rhs, self%efield_dofs)
462 
463  end if
464 
465  if( self%boundary ) then !perfect conductor boundary
466  do j = 1, self%maxwell_solver%n_dofs(2)*self%maxwell_solver%n_dofs(3)
467  self%efield_dofs(self%n_total1+1+(j-1)*self%maxwell_solver%n_dofs(1)) = 0._f64
468  self%efield_dofs(self%n_total1+j*self%maxwell_solver%n_dofs(1)) = 0._f64
469  self%efield_dofs(self%n_total1+self%n_total0+1+(j-1)*self%maxwell_solver%n_dofs(1)) = 0._f64
470  self%efield_dofs(self%n_total1+self%n_total0+j*self%maxwell_solver%n_dofs(1)) = 0._f64
471  end do
472  end if
473 
474  self%efield_filter = self%efield_dofs
475  call self%filter%apply_inplace(self%efield_filter(1:self%n_total1))
476  call self%filter%apply_inplace(self%efield_filter(self%n_total1+1:self%n_total1+self%n_total0))
477  call self%filter%apply_inplace(self%efield_filter(self%n_total1+self%n_total0+1:self%n_total1+2*self%n_total0))
478 
479 
480 
481  ! Second particle loop (second half step of particle propagation)
482  do i_sp = 1, self%particle_group%n_species
483  qoverm = self%particle_group%group(i_sp)%species%q_over_m();
484  do i_part = 1, self%particle_group%group(i_sp)%n_particles
485  vi = self%particle_group%group(i_sp)%get_v(i_part)
486  xi = self%particle_group%group(i_sp)%get_x(i_part)
487 
488  ! Evaluate E at particle position and propagate v a half step
489  call self%particle_mesh_coupling%evaluate &
490  (xi, [self%spline_degree(1)-1, self%spline_degree(2), self%spline_degree(3)], &
491  self%efield_filter(1:self%n_total1), efield(1))
492  call self%particle_mesh_coupling%evaluate &
493  (xi, [self%spline_degree(1), self%spline_degree(2)-1, self%spline_degree(3)], &
494  self%efield_filter(self%n_total1+1:self%n_total1+self%n_total0), efield(2))
495  call self%particle_mesh_coupling%evaluate &
496  (xi, [self%spline_degree(1), self%spline_degree(2), self%spline_degree(3)-1], &
497  self%efield_filter(self%n_total1+self%n_total0+1:self%n_total1+2*self%n_total0), efield(3))
498 
499  vi = vi + dt* 0.5_f64* qoverm * efield
500 
501  call self%particle_group%group(i_sp)%set_v( i_part, vi )
502  if (self%particle_group%group(i_sp)%n_weights == 3 ) then
503  ! Update particle weights
504  wall = self%particle_group%group(i_sp)%get_weights(i_part)
505  wall(3) = self%control_variate%cv(i_sp)%update_df_weight( xi, vi, 0.0_f64, wall(1), wall(2) )
506  call self%particle_group%group(i_sp)%set_weights( i_part, wall )
507 
508  end if
509  end do
510  end do
511 
512  end subroutine advect_e_pic_vm_3d3v
513 
514 
515  !---------------------------------------------------------------------------!
520  ! TODO: Here we need to merge at least the steps for the particle mass to improve the scaling
521  subroutine advect_ex_pic_vm_3d3v( self, dt )
522  class(sll_t_time_propagator_pic_vm_3d3v_helper), intent(inout) :: self
523  sll_real64, intent(in) :: dt
524  ! local variables
525  sll_int32 :: i_part, i_sp
526  sll_real64 :: vi(3), xi(3), wi(1), mi(1), xnew(3), vbar(3), vh(3), xmid(3), xbar, dx
527  sll_real64 :: sign, wall(3)
528  sll_real64 :: efield(3)
529  sll_int32 :: niter
530  sll_real64 :: residual(8), residual_local(8)
531 
532  self%efield_dofs_new = self%efield_dofs
533  self%phi_dofs_new = self%phi_dofs
534  do i_sp=1,self%particle_group%n_species
535  do i_part = 1,self%particle_group%group(i_sp)%n_particles
536  self%vnew(i_sp,:, i_part) = self%particle_group%group(i_sp)%get_v(i_part)
537  self%xnew(i_sp,:, i_part) = self%particle_group%group(i_sp)%get_x(i_part)
538  end do
539  end do
540 
541  niter = 0
542  residual = 1.0_f64
543 
544  do while ( (residual(7) > self%iter_tolerance) .and. niter < self%max_iter )
545  niter = niter+1
546 
547  self%efield_dofs_work = 0.5_f64*( self%efield_dofs + self%efield_dofs_new )
548  self%j_dofs_local = 0.0_f64
549  residual_local = 0._f64
550  ! Particle loop
551  do i_sp=1,self%particle_group%n_species
552  sign = dt*self%particle_group%group(i_sp)%species%q_over_m();
553  do i_part = 1,self%particle_group%group(i_sp)%n_particles
554  vi = self%particle_group%group(i_sp)%get_v(i_part)
555  xi = self%particle_group%group(i_sp)%get_x(i_part)
556 
557  ! Get charge for accumulation of j
558  wi = self%particle_group%group(i_sp)%get_charge(i_part, self%i_weight)
559  mi = self%particle_group%group(i_sp)%get_mass(i_part, self%i_weight)
560 
561  vbar = 0.5_f64*(vi+self%vnew(i_sp,:, i_part))
562 
563  xnew = xi + dt * vbar
564  if(xnew(1) < -self%x_max(1) .or. xnew(1) > 2._f64*self%x_max(1) ) then
565  print*, xnew
566  sll_error("particle boundary", "particle out of bound")
567  else if(xnew(1) < self%x_min(1) .or. xnew(1) > self%x_max(1) )then
568  if(xnew(1) < self%x_min(1) )then
569  xbar = self%x_min(1)
570  else if(xnew(1) > self%x_max(1))then
571  xbar = self%x_max(1)
572  end if
573  dx = (xbar- xi(1))/(xnew(1)-xi(1))
574  xmid = xi + dx * (xnew-xi)
575  xmid(1) = xbar
576 
577  vh = (xmid - xi)*wi(1)
578  call self%particle_mesh_coupling%add_current_evaluate( xi, xmid, vh, self%efield_dofs_work, &
579  self%j_dofs_local, efield )
580  vi = vi + dx* sign* efield
581 
582  select case(self%boundary_particles)
584  xnew(1) = 2._f64*xbar-xnew(1)
585  vi(1) = - vi(1)
587  call self%particle_mesh_coupling%add_charge(xmid, wi(1), self%spline_degree, self%rhob)
588  xnew(1) = xmid(1) + ((xbar-self%x_min(1))/self%Lx(1)-0.5_f64) * 1.9_f64*self%iter_tolerance
590  xnew(1) = self%x_min(1) + modulo(xnew(1)-self%x_min(1), self%Lx(1))
591  xmid(1) = self%x_max(1)+self%x_min(1)-xbar
592  case default
593  xnew(1) = self%x_min(1) + modulo(xnew(1)-self%x_min(1), self%Lx(1))
594  xmid(1) = self%x_max(1)+self%x_min(1)-xbar
595  end select
596  vh = (xnew - xmid)*wi(1)
597  call self%particle_mesh_coupling%add_current_evaluate( xmid, xnew, vh, self%efield_dofs_work, &
598  self%j_dofs_local, efield )
599  vi = vi + (1._f64-dx) * sign * efield
600  if(self%boundary_particles == sll_p_boundary_particles_reflection) then
601  vi(1) = - vi(1)
602  end if
603  else
604  vh = (xnew - xi)*wi(1)
605  call self%particle_mesh_coupling%add_current_evaluate( xi, xnew, vh, self%efield_dofs_work, &
606  self%j_dofs_local, efield )
607  vi = vi + sign * efield
608  end if
609  xnew(2:3) = self%x_min(2:3) + modulo(xnew(2:3)-self%x_min(2:3), self%Lx(2:3))
610 
611  residual_local(1) = residual_local(1) + (xnew(1)/self%Lx(1)-self%xnew(i_sp,1,i_part)/self%Lx(1))**2*abs(wi(1))
612  residual_local(2) = residual_local(2) + (xnew(2)/self%Lx(2)-self%xnew(i_sp,2,i_part)/self%Lx(2))**2*abs(wi(1))
613  residual_local(3) = residual_local(3) + (xnew(3)/self%Lx(3)-self%xnew(i_sp,3,i_part)/self%Lx(3))**2*abs(wi(1))
614 
615  residual_local(4) = residual_local(4) + (vi(1)-self%vnew(i_sp,1,i_part))**2*abs(wi(1))
616  residual_local(5) = residual_local(5) + (vi(2)-self%vnew(i_sp,2,i_part))**2*abs(wi(1))
617  residual_local(6) = residual_local(6) + (vi(3)-self%vnew(i_sp,3,i_part))**2*abs(wi(1))
618 
619  residual_local(8) = residual_local(8) + mi(1)*0.5_f64*( vi(1)**2 + vi(2)**2 + vi(3)**2-self%vnew(i_sp,1,i_part)**2 -self%vnew(i_sp,2,i_part)**2 -self%vnew(i_sp,3,i_part)**2 )
620 
621  self%xnew(i_sp, :, i_part) = xnew
622  self%vnew(i_sp, :, i_part) = vi
623  end do
624  end do
625 
626  self%j_dofs = 0.0_f64
627  ! MPI to sum up contributions from each processor
628  call sll_o_collective_allreduce( sll_v_world_collective, self%j_dofs_local, &
629  self%n_total1+self%n_total0*2, mpi_sum, self%j_dofs )
630 
631  if( self%adiabatic_electrons) then
632  self%phi_dofs_work = self%phi_dofs
633  call self%maxwell_solver%compute_phi_from_j( self%j_dofs, self%phi_dofs_work, self%efield_dofs_work )
634 
635  ! Compute residuals based on phi
636  residual_local(8) = residual_local(8) + 0.5_f64*(self%maxwell_solver%inner_product( self%phi_dofs_work, self%phi_dofs_work, 0 ) - self%maxwell_solver%inner_product( self%phi_dofs_new, self%phi_dofs_new, 0 ) )
637  residual_local(7) = (sum((self%phi_dofs_work-self%phi_dofs_new)**2))*product(self%particle_mesh_coupling%delta_x)
638  else
639  self%efield_dofs_work = self%efield_dofs
640  self%j_dofs=self%j_dofs*self%betar(2)
641  call self%maxwell_solver%compute_E_from_j( self%j_dofs, self%efield_dofs_work )
642 
643  ! Compute residuals based on e
644  residual_local(8) = residual_local(8) + 0.5_f64*(self%maxwell_solver%L2norm_squared( self%efield_dofs_work(1:self%n_total1), 1, 1 ) + self%maxwell_solver%L2norm_squared( self%efield_dofs_work(1+self%n_total1:self%n_total1+self%n_total0), 1, 2 ) +self%maxwell_solver%L2norm_squared( self%efield_dofs_work(1+self%n_total1 +self%n_total0:self%n_total1+2*self%n_total0), 1, 3 ) - self%maxwell_solver%L2norm_squared( self%efield_dofs_work(1:self%n_total1), 1, 1 ) + self%maxwell_solver%L2norm_squared( self%efield_dofs_new(1+self%n_total1:self%n_total1+self%n_total0), 1, 2 ) + self%maxwell_solver%L2norm_squared( self%efield_dofs_new(1+self%n_total1 +self%n_total0:self%n_total1+2*self%n_total0), 1, 3 ) )
645 
646  residual_local(7) = (sum((self%efield_dofs_work-self%efield_dofs_new)**2))*product(self%particle_mesh_coupling%delta_x)
647  end if
648 
649  call sll_o_collective_allreduce( sll_v_world_collective, residual_local, 8, mpi_max, residual )
650  residual(1:7) = sqrt(residual(1:7))
651  residual(8) = abs(residual(8))
652  self%phi_dofs_new = self%phi_dofs_work
653  self%efield_dofs_new = self%efield_dofs_work
654  end do
655 
656  if ( residual(7) > self%iter_tolerance ) then
657  !maxval(residual(1:7))
658  print*, 'Warning: Iteration no.', self%iter_counter+1 ,'did not converge.', residual, niter
659  self%n_failed = self%n_failed+1
660  end if
661 
662  self%efield_dofs_work = 0.5_f64*( self%efield_dofs + self%efield_dofs_new )
663  self%j_dofs_local = 0.0_f64
664 
665  ! Particle loop
666  do i_sp=1,self%particle_group%n_species
667  sign = dt*self%particle_group%group(i_sp)%species%q_over_m();
668  do i_part = 1,self%particle_group%group(i_sp)%n_particles
669  vi = self%particle_group%group(i_sp)%get_v(i_part)
670  xi = self%particle_group%group(i_sp)%get_x(i_part)
671 
672  ! Get charge for accumulation of j
673  wi = self%particle_group%group(i_sp)%get_charge(i_part, self%i_weight)
674  vbar = 0.5_f64*(vi+self%vnew(i_sp,:, i_part))
675 
676  xnew = xi + dt * vbar
677 
678  call compute_particle_boundary_current_evaluate( self, xi, xnew, vi, wi, sign )
679  call self%particle_group%group(i_sp)%set_v( i_part, vi )
680  call self%particle_group%group(i_sp)%set_x( i_part, xnew )
681  ! Update particle weights
682  if (self%particle_group%group(i_sp)%n_weights == 3 ) then
683  wall = self%particle_group%group(i_sp)%get_weights(i_part)
684  wall(3) = self%control_variate%cv(i_sp)%update_df_weight( xnew, vi, 0.0_f64, wall(1), wall(2) )
685  call self%particle_group%group(i_sp)%set_weights( i_part, wall )
686  end if
687  end do
688  end do
689 
690  self%j_dofs = 0.0_f64
691  ! MPI to sum up contributions from each processor
692  call sll_o_collective_allreduce( sll_v_world_collective, self%j_dofs_local, &
693  self%n_total1+self%n_total0*2, mpi_sum, self%j_dofs )
694 
695  if( self%adiabatic_electrons) then
696  call self%maxwell_solver%compute_phi_from_j( self%j_dofs, self%phi_dofs, self%efield_dofs )
697  else
698  self%j_dofs=self%j_dofs*self%betar(2)
699  call self%maxwell_solver%compute_E_from_j( self%j_dofs, self%efield_dofs )
700 
701  end if
702 
703  self%iter_counter = self%iter_counter + 1
704  self%niter(self%iter_counter) = niter
705 
706  end subroutine advect_ex_pic_vm_3d3v
707 
708 
710  subroutine compute_particle_boundary_current_evaluate( self, xi, xnew, vi, wi, sign )
711  class(sll_t_time_propagator_pic_vm_3d3v_helper), intent( inout ) :: self
712  sll_real64, intent( in ) :: xi(3)
713  sll_real64, intent( inout ) :: xnew(3)
714  sll_real64, intent( inout ) :: vi(3)
715  sll_real64, intent( in ) :: wi(1)
716  sll_real64, intent( in ) :: sign
717  !local variables
718  sll_real64 :: xmid(3), vh(3), xbar, dx, efield(3)
719 
720 
721  if(xnew(1) < -self%x_max(1) .or. xnew(1) > 2._f64*self%x_max(1) ) then
722  print*, xnew
723  sll_error("particle boundary", "particle out of bound")
724  else if(xnew(1) < self%x_min(1) .or. xnew(1) > self%x_max(1) )then
725  if(xnew(1) < self%x_min(1) )then
726  xbar = self%x_min(1)
727  self%counter_left = self%counter_left+1
728  else if(xnew(1) > self%x_max(1))then
729  xbar = self%x_max(1)
730  self%counter_right = self%counter_right+1
731  end if
732  dx = (xbar- xi(1))/(xnew(1)-xi(1))
733  xmid = xi + dx * (xnew-xi)
734  xmid(1) = xbar
735 
736  vh = (xmid - xi)*wi(1)
737  call self%particle_mesh_coupling%add_current_evaluate( xi, xmid, vh, self%efield_dofs_work, &
738  self%j_dofs_local, efield )
739  vi = vi + sign* dx* efield
740  select case(self%boundary_particles)
742  xnew(1) = 2._f64*xbar-xnew(1)
743  vi(1) = - vi(1)
745  call self%particle_mesh_coupling%add_charge(xmid, wi(1), self%spline_degree, self%rhob)
747  xnew(1) = self%x_min(1) + modulo(xnew(1)-self%x_min(1), self%Lx(1))
748  xmid(1) = self%x_max(1)+self%x_min(1)-xbar
749  case default
750  xnew(1) = self%x_min(1) + modulo(xnew(1)-self%x_min(1), self%Lx(1))
751  xmid(1) = self%x_max(1)+self%x_min(1)-xbar
752  end select
753  vh = (xnew - xmid)*wi(1)
754  call self%particle_mesh_coupling%add_current_evaluate( xmid, xnew, vh, self%efield_dofs_work, &
755  self%j_dofs_local, efield )
756  vi = vi + sign * (1._f64-dx)* efield
757  else
758  vh = (xnew - xi)*wi(1)
759  call self%particle_mesh_coupling%add_current_evaluate( xi, xnew, vh, self%efield_dofs_work, &
760  self%j_dofs_local, efield )
761  vi = vi + sign * efield
762  end if
763  xnew(2:3) = self%x_min(2:3) + modulo(xnew(2:3)-self%x_min(2:3), self%Lx(2:3))
764 
766 
767 
768  !---------------------------------------------------------------------------!
771  subroutine advect_e_pic_vm_3d3v_trunc( self, dt )
772  class(sll_t_time_propagator_pic_vm_3d3v_helper), intent(inout) :: self
773  sll_real64, intent(in) :: dt
774  ! local variables
775  sll_int32 :: i_part, i_sp
776  sll_real64 :: vi(3), xi(3), wi(1)
777  sll_real64 :: qoverm
778  sll_real64 :: efield(3)
779 
780  ! Set to zero
781  self%j_dofs_local = 0.0_f64
782  self%particle_mass_1_local = 0.0_f64
783  self%particle_mass_2_local = 0.0_f64
784  self%particle_mass_3_local = 0.0_f64
785 
786 
787  ! First particle loop
788  do i_sp = 1, self%particle_group%n_species
789  qoverm = self%particle_group%group(i_sp)%species%q_over_m();
790  do i_part = 1,self%particle_group%group(i_sp)%n_particles
791  vi = self%particle_group%group(i_sp)%get_v(i_part)
792  xi = self%particle_group%group(i_sp)%get_x(i_part)
793 
794  ! Get charge for accumulation of j
795  wi = self%particle_group%group(i_sp)%get_charge(i_part, self%i_weight)
796 
797  ! Accumulate j
798  call self%particle_mesh_coupling%add_charge( xi, wi(1)*vi(1), &
799  [self%spline_degree(1)-1, self%spline_degree(2), self%spline_degree(3)], &
800  self%j_dofs_local(1:self%n_total1) )
801  call self%particle_mesh_coupling%add_charge( xi, wi(1)*vi(2), &
802  [self%spline_degree(1), self%spline_degree(2)-1, self%spline_degree(3)], &
803  self%j_dofs_local(1+self%n_total1:self%n_total1+self%n_total0) )
804  call self%particle_mesh_coupling%add_charge( xi, wi(1)*vi(3), &
805  [self%spline_degree(1), self%spline_degree(2), self%spline_degree(3)-1], &
806  self%j_dofs_local(1+self%n_total1+self%n_total0:self%n_total1+2*self%n_total0) )
807 
808 
809  ! Evaulate E at particle position and propagate v a half step
810  call self%particle_mesh_coupling%evaluate &
811  (xi, [self%spline_degree(1)-1, self%spline_degree(2), self%spline_degree(3)], self%efield_dofs(1:self%n_total1), efield(1))
812  call self%particle_mesh_coupling%evaluate &
813  (xi, [self%spline_degree(1), self%spline_degree(2)-1, self%spline_degree(3)],self%efield_dofs(self%n_total1+1:self%n_total1+self%n_total0), efield(2))
814  call self%particle_mesh_coupling%evaluate &
815  (xi, [self%spline_degree(1), self%spline_degree(2), self%spline_degree(3)-1],self%efield_dofs(self%n_total1+self%n_total0+1:self%n_total1+2*self%n_total0), efield(3))
816 
817  vi = vi + dt* 0.5_f64* qoverm * efield
818  call self%particle_group%group(i_sp)%set_v( i_part, vi )
819  end do
820 
821  end do
822  ! Update d_n
823  self%j_dofs = 0.0_f64
824  ! MPI to sum up contributions from each processor
825  call sll_o_collective_allreduce( sll_v_world_collective, self%j_dofs_local, &
826  self%n_total1+self%n_total0*2, mpi_sum, self%j_dofs )
827 
828 
829  ! Ex part
830  ! Multiply E by mass matrix and add current
831  call self%maxwell_solver%multiply_mass( [2,1,1], &
832  self%efield_dofs(1:self%n_total1 ), self%j_dofs_local(1:self%n_total1) )
833  self%j_dofs_local( 1:self%n_total1 ) = (1.0_f64-dt**2*0.25_f64*qoverm)*&
834  self%j_dofs_local( 1:self%n_total1 ) - dt* self%j_dofs( 1:self%n_total1 )
835 
836  ! Ey part
837  ! Multiply E by mass matrix and add current
838  call self%maxwell_solver%multiply_mass( [1,2,1], &
839  self%efield_dofs(1+self%n_total1:self%n_total1+self%n_total0 ), self%j_dofs_local(1+self%n_total1:self%n_total1+self%n_total0) )
840  self%j_dofs_local( 1+self%n_total1:self%n_total1+self%n_total0 ) = (1.0_f64-dt**2*0.25_f64*qoverm)*&
841  self%j_dofs_local( 1+self%n_total1:self%n_total1+self%n_total0 ) - dt* self%j_dofs( 1+self%n_total1:self%n_total1+self%n_total0 )
842 
843  ! Ez part
844  ! Multiply E by mass matrix and add current
845  call self%maxwell_solver%multiply_mass( [1,1,2], self%efield_dofs(1+self%n_total1+self%n_total0:self%n_total1+2*self%n_total0 ), &
846  self%j_dofs_local(1+self%n_total1+self%n_total0:self%n_total1+2*self%n_total0) )
847  self%j_dofs_local( 1+self%n_total1+self%n_total0:self%n_total1+2*self%n_total0 ) = (1.0_f64-dt**2*0.25_f64*qoverm)*&
848  self%j_dofs_local( 1+self%n_total1+self%n_total0:self%n_total1+2*self%n_total0 ) - dt* self%j_dofs( 1+self%n_total1+self%n_total0:self%n_total1+2*self%n_total0 )
849 
850  ! Solve Schur complement
851  ! Solve efield components together
852  call self%maxwell_solver%multiply_mass_inverse( 1, self%j_dofs_local, self%efield_dofs )
853 
854  ! Second particle loop (second half step of particle propagation)
855  do i_sp = 1, self%particle_group%n_species
856  do i_part = 1, self%particle_group%group(i_sp)%n_particles
857 
858  vi = self%particle_group%group(i_sp)%get_v(i_part)
859  xi = self%particle_group%group(i_sp)%get_x(i_part)
860 
861  call self%particle_mesh_coupling%evaluate &
862  (xi, [self%spline_degree(1)-1, self%spline_degree(2), self%spline_degree(3)], self%efield_dofs(1:self%n_total1), efield(1))
863  call self%particle_mesh_coupling%evaluate &
864  (xi, [self%spline_degree(1), self%spline_degree(2)-1, self%spline_degree(3)],self%efield_dofs(self%n_total1+1:self%n_total1+self%n_total0), efield(2))
865  call self%particle_mesh_coupling%evaluate &
866  (xi, [self%spline_degree(1), self%spline_degree(2), self%spline_degree(3)-1],self%efield_dofs(self%n_total1+self%n_total0+1:self%n_total1+2*self%n_total0), efield(3))
867 
868  vi = vi + dt* 0.5_f64* qoverm * efield
869  call self%particle_group%group(i_sp)%set_v( i_part, vi )
870 
871  end do
872  end do
873 
874  end subroutine advect_e_pic_vm_3d3v_trunc
875 
876 
877  !---------------------------------------------------------------------------!
880  self, &
881  maxwell_solver, &
882  particle_mesh_coupling, &
883  particle_group, &
884  phi_dofs, &
885  efield_dofs, &
886  bfield_dofs, &
887  x_min, &
888  Lx, &
889  filter, &
890  boundary_particles, &
891  solver_tolerance, &
892  iter_tolerance, max_iter, &
893  betar, &
894  force_sign, &
895  rhob, &
896  control_variate, &
897  jmean)
898  class(sll_t_time_propagator_pic_vm_3d3v_helper), intent( out ) :: self
899  class(sll_c_maxwell_3d_base), target, intent( in ) :: maxwell_solver
900  class(sll_c_particle_mesh_coupling_3d), target, intent(in) :: particle_mesh_coupling
901  class(sll_t_particle_array), target, intent( in ) :: particle_group
902  sll_real64, target, intent( in ) :: phi_dofs(:)
903  sll_real64, target, intent( in ) :: efield_dofs(:)
904  sll_real64, target, intent( in ) :: bfield_dofs(:)
905  sll_real64, intent( in ) :: x_min(3)
906  sll_real64, intent( in ) :: lx(3)
907  class(sll_c_filter_base_3d), target :: filter
908  sll_int32, optional, intent( in ) :: boundary_particles
909  sll_real64, optional, intent( in ) :: solver_tolerance
910  sll_real64, optional, intent( in ) :: iter_tolerance
911  sll_int32, optional, intent( in ) :: max_iter
912  sll_real64, optional, intent(in) :: betar(2)
913  sll_real64, optional, intent(in) :: force_sign
914  sll_real64, optional, target, intent( in ) :: rhob(:)
915  class(sll_t_control_variates), optional, target, intent(in) :: control_variate
916  logical, optional, intent(in) :: jmean
917  !local variables
918  sll_int32 :: ierr, i_sp, j
919  sll_real64, allocatable :: vec1(:)
920  sll_real64, allocatable :: vec2(:)
921 
922  if (present(solver_tolerance) ) then
923  self%solver_tolerance = solver_tolerance
924  end if
925 
926  if (present(iter_tolerance) ) then
927  self%iter_tolerance = iter_tolerance
928  self%max_iter = max_iter
929  end if
930 
931  if( present(force_sign) )then
932  self%force_sign = force_sign
933  end if
934 
935  if (self%force_sign == 1._f64) then
936  if( particle_group%group(1)%species%q > 0._f64) self%adiabatic_electrons = .true.
937  end if
938 
939  if (present(jmean)) then
940  self%jmean = jmean
941  end if
942 
943  if (present(boundary_particles) ) then
944  self%boundary_particles = boundary_particles
945  end if
946 
947  if( present(rhob) )then
948  self%rhob => rhob
949  end if
950 
951  self%maxwell_solver => maxwell_solver
952  self%particle_mesh_coupling => particle_mesh_coupling
953  self%particle_group => particle_group
954  self%phi_dofs => phi_dofs
955  self%efield_dofs => efield_dofs
956  self%bfield_dofs => bfield_dofs
957  self%n_total0 = self%maxwell_solver%n_total0
958  self%n_total1 = self%maxwell_solver%n_total1
959  self%spline_degree = self%particle_mesh_coupling%spline_degree
960  self%x_min = x_min
961  self%x_max = x_min + lx
962  self%Lx = lx
963  self%filter => filter
964 
965  self%nspan(1) = (2*self%spline_degree(1)-1)*(2*self%spline_degree(2)+1)*(2*self%spline_degree(3)+1)
966  self%nspan(2) = (2*self%spline_degree(1)+1)*(2*self%spline_degree(2)-1)*(2*self%spline_degree(3)+1)
967  self%nspan(3) = (2*self%spline_degree(1)+1)*(2*self%spline_degree(2)+1)*(2*self%spline_degree(3)-1)
968 
969  sll_allocate( vec1(1:self%n_total1+self%n_total0*2), ierr )
970  sll_allocate( vec2(1:self%n_total1+self%n_total0*2), ierr )
971  vec1 = 0._f64
972  vec2 = 0._f64
973 
974  sll_allocate( self%j_dofs(1:self%n_total1+self%n_total0*2), ierr )
975  sll_allocate( self%j_dofs_local(1:self%n_total1+self%n_total0*2), ierr )
976 
977  sll_allocate(self%particle_mass_1_local(self%nspan(1), self%n_total0), ierr )
978  sll_allocate(self%particle_mass_2_local(self%nspan(2), self%n_total0), ierr )
979  sll_allocate(self%particle_mass_3_local(self%nspan(3), self%n_total0), ierr )
980  self%j_dofs = 0.0_f64
981  self%j_dofs_local = 0.0_f64
982  self%particle_mass_1_local = 0.0_f64
983  self%particle_mass_2_local = 0.0_f64
984  self%particle_mass_3_local = 0.0_f64
985 
986  if( self%particle_mesh_coupling%n_cells(1)+self%spline_degree(1) == self%maxwell_solver%n_dofs(1) ) then
987  self%boundary = .true.
988  allocate( sll_t_linear_operator_particle_mass_cl_3d_diag :: self%particle_mass_1 )
989  allocate( sll_t_linear_operator_particle_mass_cl_3d_diag :: self%particle_mass_2 )
990  allocate( sll_t_linear_operator_particle_mass_cl_3d_diag :: self%particle_mass_3 )
991  else
992  allocate( sll_t_linear_operator_particle_mass_3d_diag :: self%particle_mass_1 )
993  allocate( sll_t_linear_operator_particle_mass_3d_diag :: self%particle_mass_2 )
994  allocate( sll_t_linear_operator_particle_mass_3d_diag :: self%particle_mass_3 )
995  end if
996 
997  call self%particle_mass_1%create( self%particle_mesh_coupling%n_cells, [self%spline_degree(1)-1, self%spline_degree(2), self%spline_degree(3)] )
998  call self%particle_mass_2%create( self%particle_mesh_coupling%n_cells, [self%spline_degree(1), self%spline_degree(2)-1, self%spline_degree(3)] )
999  call self%particle_mass_3%create( self%particle_mesh_coupling%n_cells, [self%spline_degree(1), self%spline_degree(2), self%spline_degree(3)-1] )
1000 
1001  call self%particle_mass_op%create( 3, 3 )
1002  if( self%adiabatic_electrons ) then
1003  call self%linear_op_schur_phiv%create( self%maxwell_solver, self%particle_mass_op )
1004  call self%linear_solver_schur_phiv%create( self%linear_op_schur_phiv )
1005  self%linear_solver_schur_phiv%atol = self%solver_tolerance
1006  !self%linear_solver_schur_phiv%verbose = .true.
1007  else
1008  call self%linear_op_schur_ev%create( self%maxwell_solver, self%particle_mass_op)
1009  call self%preconditioner_fft%init( self%maxwell_solver%Lx, self%maxwell_solver%n_cells, self%maxwell_solver%s_deg_0, self%boundary )
1010  if( self%boundary ) then
1011  vec1 = 1._f64
1012  call self%maxwell_solver%multiply_mass( [1], vec1, vec2 )
1013  do j = 1, self%maxwell_solver%n_dofs(2)*self%maxwell_solver%n_dofs(3)
1014  vec2(self%n_total1+1+(j-1)*self%maxwell_solver%n_dofs(1)) = 1._f64
1015  vec2(self%n_total1+j*self%maxwell_solver%n_dofs(1)) = 1._f64
1016  vec2(self%n_total1+self%n_total0+1+(j-1)*self%maxwell_solver%n_dofs(1)) = 1._f64
1017  vec2(self%n_total1+self%n_total0+j*self%maxwell_solver%n_dofs(1)) = 1._f64
1018  end do
1019  vec2 = 1._f64/sqrt(abs(vec2))
1020 
1021  call self%preconditioner1%create( self%preconditioner_fft%inverse_mass1_3d, vec2, 2*self%n_total0+self%n_total1 )
1022 
1023  call self%linear_solver_schur_ev%create( self%linear_op_schur_ev, self%preconditioner1)
1024  else
1025  call self%linear_solver_schur_ev%create( self%linear_op_schur_ev )
1026  end if
1027  self%linear_solver_schur_ev%atol = self%solver_tolerance
1028  !self%linear_solver_schur_ev%verbose = .true.
1029  self%linear_solver_schur_ev%n_maxiter = 2000
1030 
1031  end if
1032 
1033  if (present(betar)) then
1034  self%betar = betar
1035  end if
1036 
1037 
1038  self%n_particles_max = self%particle_group%group(1)%n_particles
1039  do i_sp = 2,self%particle_group%n_species
1040  self%n_particles_max = max(self%n_particles_max, self%particle_group%group(i_sp)%n_particles )
1041  end do
1042 
1043  sll_allocate( self%xnew(self%particle_group%n_species,3,self%n_particles_max), ierr )
1044  sll_allocate( self%vnew(self%particle_group%n_species,3,self%n_particles_max), ierr )
1045  sll_allocate( self%efield_dofs_new(self%n_total1+2*self%n_total0), ierr )
1046  sll_allocate( self%efield_dofs_work(self%n_total1+2*self%n_total0), ierr )
1047  sll_allocate( self%phi_dofs_work(self%n_total0), ierr )
1048  sll_allocate( self%phi_dofs_new(self%n_total0), ierr )
1049  sll_allocate(self%efield_filter(self%n_total1+2*self%n_total0), ierr)
1050  sll_allocate(self%bfield_filter(self%n_total0+2*self%n_total1), ierr)
1051 
1052  if (present(control_variate)) then
1053  allocate(self%control_variate )
1054  allocate(self%control_variate%cv(self%particle_group%n_species) )
1055  self%control_variate => control_variate
1056  self%i_weight = self%particle_group%group(1)%n_weights
1057  end if
1058 
1059  end subroutine initialize_pic_vm_3d3v
1060 
1061 
1062 
1065  self, &
1066  maxwell_solver, &
1067  particle_mesh_coupling, &
1068  particle_group, &
1069  phi_dofs, &
1070  efield_dofs, &
1071  bfield_dofs, &
1072  x_min, &
1073  Lx, &
1074  filename, &
1075  filter, &
1076  boundary_particles, &
1077  betar, &
1078  force_sign, &
1079  rhob, &
1080  control_variate, &
1081  jmean)
1082  class(sll_t_time_propagator_pic_vm_3d3v_helper), intent( out ) :: self
1083  class(sll_c_maxwell_3d_base), target, intent( in ) :: maxwell_solver
1084  class(sll_c_particle_mesh_coupling_3d), target, intent(in) :: particle_mesh_coupling
1085  class(sll_t_particle_array), target, intent( in ) :: particle_group
1086  sll_real64, target, intent( in ) :: phi_dofs(:)
1087  sll_real64, target, intent( in ) :: efield_dofs(:)
1088  sll_real64, target, intent( in ) :: bfield_dofs(:)
1089  sll_real64, intent( in ) :: x_min(3)
1090  sll_real64, intent( in ) :: lx(3)
1091  character(len=*), intent( in ) :: filename
1092  class(sll_c_filter_base_3d), target, optional :: filter
1093  sll_int32, optional, intent( in ) :: boundary_particles
1094  sll_real64, optional, intent( in ) :: betar(2)
1095  sll_real64, optional, intent(in) :: force_sign
1096  sll_real64, optional, target, intent( in ) :: rhob(:)
1097  class(sll_t_control_variates), optional, target, intent(in) :: control_variate
1098  logical, optional, intent(in) :: jmean
1099  !local variables
1100  character(len=256) :: file_prefix
1101  sll_int32 :: input_file, rank
1102  sll_int32 :: io_stat, io_stat0, io_stat1, file_id
1103  sll_real64 :: maxwell_tolerance, iter_tolerance, betar_set(2), force_sign_set
1104  sll_int32 :: max_iter, boundary_particles_set
1105  logical :: jmean_set
1106 
1107  namelist /output/ file_prefix
1108  namelist /time_solver/ maxwell_tolerance
1109  namelist /time_iterate/ iter_tolerance, max_iter
1110 
1112 
1113  if( present(boundary_particles) )then
1114  boundary_particles_set = boundary_particles
1115  else
1116  boundary_particles_set = 100
1117  end if
1118 
1119  if( present(betar) )then
1120  betar_set = betar
1121  else
1122  betar_set = 1._f64
1123  end if
1124 
1125  if( present(force_sign) )then
1126  force_sign_set = force_sign
1127  else
1128  force_sign_set = 1._f64
1129  end if
1130 
1131  if (present(jmean)) then
1132  jmean_set = jmean
1133  else
1134  jmean_set = .false.
1135  end if
1136 
1137  if( present(control_variate) ) then
1138  ! Read in solver tolerance
1139  open(newunit = input_file, file=filename, status='old',iostat=io_stat)
1140  if (io_stat /= 0) then
1141  if (rank == 0 ) then
1142  print*, 'sll_m_time_propagator_pic_vm_3d3v_helper: Input file does not exist. Set default tolerance.'
1143  open(newunit=file_id, file=trim(file_prefix)//'_parameters_used.dat', position = 'append', status='old', action='write', iostat=io_stat)
1144  write(file_id, *) 'field solver tolerance:', 1d-12
1145  write(file_id, *) 'iter_tolerance:', 1d-10
1146  write(file_id, *) 'max_iter:', 20
1147  write(file_id, *) 'control_variate:', .true.
1148  close(file_id)
1149  end if
1150  call self%init( maxwell_solver, &
1151  particle_mesh_coupling, &
1152  particle_group, &
1153  phi_dofs, &
1154  efield_dofs, &
1155  bfield_dofs, &
1156  x_min, &
1157  lx, &
1158  filter, &
1159  boundary_particles = boundary_particles_set, &
1160  betar=betar_set, &
1161  force_sign=force_sign_set, &
1162  rhob = rhob, &
1163  control_variate = control_variate,&
1164  jmean=jmean_set)
1165  else
1166  read(input_file, output,iostat=io_stat0)
1167  read(input_file, time_solver,iostat=io_stat)
1168  read(input_file, time_iterate,iostat=io_stat1)
1169  if (io_stat /= 0.and. io_stat1 /= 0) then
1170  if (rank == 0 ) then
1171  print*, 'sll_m_time_propagator_pic_vm_3d3v_helper: Input parameter does not exist. Set default tolerance.'
1172  open(newunit=file_id, file=trim(file_prefix)//'_parameters_used.dat', position = 'append', status='old', action='write', iostat=io_stat)
1173  write(file_id, *) 'field solver tolerance:', 1d-12
1174  write(file_id, *) 'iter_tolerance:', 1d-10
1175  write(file_id, *) 'max_iter:', 20
1176  write(file_id, *) 'control_variate:', .true.
1177  close(file_id)
1178  end if
1179  call self%init( maxwell_solver, &
1180  particle_mesh_coupling, &
1181  particle_group, &
1182  phi_dofs, &
1183  efield_dofs, &
1184  bfield_dofs, &
1185  x_min, &
1186  lx, &
1187  filter, &
1188  boundary_particles = boundary_particles_set, &
1189  betar=betar_set, &
1190  force_sign=force_sign_set, &
1191  rhob = rhob, &
1192  control_variate = control_variate,&
1193  jmean=jmean_set)
1194  else if (io_stat == 0 .and. io_stat1 /= 0) then
1195  if (rank == 0 ) then
1196  open(newunit=file_id, file=trim(file_prefix)//'_parameters_used.dat', position = 'append', status='old', action='write', iostat=io_stat)
1197  write(file_id, *) 'field solver tolerance:', maxwell_tolerance
1198  write(file_id, *) 'iter_tolerance:', 1d-10
1199  write(file_id, *) 'max_iter:', 20
1200  write(file_id, *) 'control_variate:', .true.
1201  close(file_id)
1202  end if
1203  call self%init( maxwell_solver, &
1204  particle_mesh_coupling, &
1205  particle_group, &
1206  phi_dofs, &
1207  efield_dofs, &
1208  bfield_dofs, &
1209  x_min, &
1210  lx, &
1211  filter, &
1212  boundary_particles_set, &
1213  maxwell_tolerance, &
1214  betar=betar_set, &
1215  force_sign=force_sign_set, &
1216  rhob = rhob, &
1217  control_variate = control_variate,&
1218  jmean=jmean_set)
1219  else if (io_stat /= 0 .and. io_stat1 == 0) then
1220  if (rank == 0 ) then
1221  open(newunit=file_id, file=trim(file_prefix)//'_parameters_used.dat', position = 'append', status='old', action='write', iostat=io_stat)
1222  write(file_id, *) 'field solver tolerance:', 1d-12
1223  write(file_id, *) 'iter_tolerance:', iter_tolerance
1224  write(file_id, *) 'max_iter:', max_iter
1225  write(file_id, *) 'control_variate:', .true.
1226  close(file_id)
1227  end if
1228  call self%init( maxwell_solver, &
1229  particle_mesh_coupling, &
1230  particle_group, &
1231  phi_dofs, &
1232  efield_dofs, &
1233  bfield_dofs, &
1234  x_min, &
1235  lx, &
1236  filter, &
1237  boundary_particles = boundary_particles_set, &
1238  iter_tolerance=iter_tolerance, max_iter=max_iter, &
1239  betar=betar_set, &
1240  force_sign=force_sign_set, &
1241  rhob = rhob, &
1242  control_variate = control_variate,&
1243  jmean=jmean_set)
1244  else
1245  if (rank == 0 ) then
1246  open(newunit=file_id, file=trim(file_prefix)//'_parameters_used.dat', position = 'append', status='old', action='write', iostat=io_stat)
1247  write(file_id, *) 'field solver tolerance:', maxwell_tolerance
1248  write(file_id, *) 'iter_tolerance:', iter_tolerance
1249  write(file_id, *) 'max_iter:', max_iter
1250  write(file_id, *) 'control_variate:', .true.
1251  close(file_id)
1252  end if
1253  call self%init( maxwell_solver, &
1254  particle_mesh_coupling, &
1255  particle_group, &
1256  phi_dofs, &
1257  efield_dofs, &
1258  bfield_dofs, &
1259  x_min, &
1260  lx, &
1261  filter, &
1262  boundary_particles_set, &
1263  maxwell_tolerance, &
1264  iter_tolerance, max_iter, &
1265  betar_set, &
1266  force_sign=force_sign_set, &
1267  rhob = rhob, &
1268  control_variate = control_variate,&
1269  jmean=jmean_set)
1270  end if
1271  close(input_file)
1272  end if
1273 
1274  else
1275  ! Read in solver tolerance
1276  open(newunit = input_file, file=filename, status='old',iostat=io_stat)
1277  if (io_stat /= 0) then
1278  if (rank == 0 ) then
1279  print*, 'sll_m_time_propagator_pic_vm_3d3v_helper: Input file does not exist. Set default tolerance.'
1280  open(newunit=file_id, file=trim(file_prefix)//'_parameters_used.dat', position = 'append', status='old', action='write', iostat=io_stat)
1281  write(file_id, *) 'field solver tolerance:', 1d-12
1282  write(file_id, *) 'iter_tolerance:', 1d-10
1283  write(file_id, *) 'max_iter:', 20
1284  close(file_id)
1285  end if
1286  call self%init( maxwell_solver, &
1287  particle_mesh_coupling, &
1288  particle_group, &
1289  phi_dofs, &
1290  efield_dofs, &
1291  bfield_dofs, &
1292  x_min, &
1293  lx, &
1294  filter, &
1295  boundary_particles = boundary_particles_set, &
1296  betar=betar_set, &
1297  force_sign=force_sign_set,&
1298  rhob = rhob, &
1299  jmean=jmean_set)
1300  else
1301  read(input_file, output,iostat=io_stat0)
1302  read(input_file, time_solver,iostat=io_stat)
1303  read(input_file, time_iterate,iostat=io_stat1)
1304  if (io_stat /= 0.and. io_stat1 /= 0) then
1305  if (rank == 0 ) then
1306  print*, 'sll_m_time_propagator_pic_vm_3d3v_helper: Input parameter does not exist. Set default tolerance.'
1307  open(newunit=file_id, file=trim(file_prefix)//'_parameters_used.dat', position = 'append', status='old', action='write', iostat=io_stat)
1308  write(file_id, *) 'field solver tolerance:', 1d-12
1309  write(file_id, *) 'iter_tolerance:', 1d-10
1310  write(file_id, *) 'max_iter:', 20
1311  close(file_id)
1312  end if
1313  call self%init( maxwell_solver, &
1314  particle_mesh_coupling, &
1315  particle_group, &
1316  phi_dofs, &
1317  efield_dofs, &
1318  bfield_dofs, &
1319  x_min, &
1320  lx, &
1321  filter, &
1322  boundary_particles = boundary_particles_set, &
1323  betar=betar_set, &
1324  force_sign=force_sign_set,&
1325  rhob = rhob, &
1326  jmean=jmean_set)
1327  else if (io_stat == 0 .and. io_stat1 /= 0) then
1328  if (rank == 0 ) then
1329  open(newunit=file_id, file=trim(file_prefix)//'_parameters_used.dat', position = 'append', status='old', action='write', iostat=io_stat)
1330  write(file_id, *) 'field solver tolerance:', maxwell_tolerance
1331  write(file_id, *) 'iter_tolerance:', 1d-10
1332  write(file_id, *) 'max_iter:', 20
1333  close(file_id)
1334  end if
1335  call self%init( maxwell_solver, &
1336  particle_mesh_coupling, &
1337  particle_group, &
1338  phi_dofs, &
1339  efield_dofs, &
1340  bfield_dofs, &
1341  x_min, &
1342  lx, &
1343  filter, &
1344  boundary_particles_set, &
1345  maxwell_tolerance, &
1346  betar=betar_set, &
1347  force_sign=force_sign_set, &
1348  rhob = rhob)
1349  else if (io_stat /= 0 .and. io_stat1 == 0) then
1350  if (rank == 0 ) then
1351  open(newunit=file_id, file=trim(file_prefix)//'_parameters_used.dat', position = 'append', status='old', action='write', iostat=io_stat)
1352  write(file_id, *) 'field solver tolerance:', 1d-12
1353  write(file_id, *) 'iter_tolerance:', iter_tolerance
1354  write(file_id, *) 'max_iter:', max_iter
1355  close(file_id)
1356  end if
1357  call self%init( maxwell_solver, &
1358  particle_mesh_coupling, &
1359  particle_group, &
1360  phi_dofs, &
1361  efield_dofs, &
1362  bfield_dofs, &
1363  x_min, &
1364  lx, &
1365  filter, &
1366  boundary_particles = boundary_particles_set, &
1367  iter_tolerance=iter_tolerance, max_iter=max_iter, &
1368  betar=betar_set, &
1369  force_sign=force_sign_set,&
1370  rhob = rhob, &
1371  jmean=jmean_set)
1372  else
1373  if (rank == 0 ) then
1374  open(newunit=file_id, file=trim(file_prefix)//'_parameters_used.dat', position = 'append', status='old', action='write', iostat=io_stat)
1375  write(file_id, *) 'solver tolerance:', maxwell_tolerance
1376  write(file_id, *) 'iter_tolerance:', iter_tolerance
1377  write(file_id, *) 'max_iter:', max_iter
1378  close(file_id)
1379  end if
1380  call self%init( maxwell_solver, &
1381  particle_mesh_coupling, &
1382  particle_group, &
1383  phi_dofs, &
1384  efield_dofs, &
1385  bfield_dofs, &
1386  x_min, &
1387  lx, &
1388  filter, &
1389  boundary_particles_set, &
1390  maxwell_tolerance, &
1391  iter_tolerance, max_iter, &
1392  betar_set, &
1393  force_sign=force_sign_set,&
1394  rhob = rhob, &
1395  jmean=jmean_set)
1396  end if
1397  close(input_file)
1398  end if
1399  end if
1400 
1401  end subroutine initialize_file_pic_vm_3d3v
1402 
1403 
1405  subroutine delete_pic_vm_3d3v(self)
1406  class(sll_t_time_propagator_pic_vm_3d3v_helper), intent( inout ) :: self
1407  !local variables
1408  sll_int32 :: file_id, j
1409 
1410  if( self%boundary ) then
1411  print*, 'left boundary', self%counter_left
1412  print*, 'right boundary', self%counter_right
1413  end if
1414 
1415  if(self%adiabatic_electrons) then
1416  call self%linear_solver_schur_phiv%free()
1417  call self%linear_op_schur_phiv%free()
1418  else
1419  call self%linear_solver_schur_ev%free()
1420  call self%linear_op_schur_ev%free()
1421  end if
1422 
1423  call self%particle_mass_op%free()
1424  call self%particle_mass_1%free()
1425  call self%particle_mass_2%free()
1426  call self%particle_mass_3%free()
1427  deallocate( self%particle_mass_1 )
1428  deallocate( self%particle_mass_2 )
1429  deallocate( self%particle_mass_3 )
1430 
1431  deallocate(self%j_dofs)
1432  deallocate(self%j_dofs_local)
1433  deallocate( self%particle_mass_1_local )
1434  deallocate( self%particle_mass_2_local )
1435  deallocate( self%particle_mass_3_local )
1436  self%maxwell_solver => null()
1437  self%particle_mesh_coupling => null()
1438  self%particle_group => null()
1439  self%phi_dofs => null()
1440  self%efield_dofs => null()
1441  self%bfield_dofs => null()
1442 
1443  deallocate( self%vnew )
1444  deallocate( self%xnew )
1445  deallocate( self%efield_dofs_new )
1446  deallocate( self%efield_dofs_work )
1447  deallocate( self%phi_dofs_new )
1448  deallocate( self%phi_dofs_work )
1449 
1450 
1451  print*, 'No. of failed iterations:', self%n_failed
1452 
1453  open(newunit=file_id, file="outer-iters.dat", action='write')
1454  do j=1,self%iter_counter
1455  write(file_id,*) self%niter(j)
1456  end do
1457 
1458  end subroutine delete_pic_vm_3d3v
1459 
1460 
Combines values from all processes and distributes the result back to all processes.
Parallelizing facility.
integer(kind=i32) function, public sll_f_get_collective_rank(col)
Determines the rank of the calling process in the communicator.
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.
Parameters to define common initial distributions.
module for a block linear operator
module for conjugate gradient method in pure form
Module interface to solve Maxwell's equations in 3D.
Base class for kernel smoothers for accumulation and field evaluation in PIC.
Module interface to solve Maxwell's equations with coordinate transformation in 3D The linear systems...
Module interface to solve Maxwell's equations with coordinate transformation in 3D The linear systems...
functions for initial profile of the particle distribution function
Particle pusher based on antisymmetric splitting with AVF for 3d3v Vlasov-Maxwell with coordinate tra...
Particle pusher based on antisymmetric splitting with AVF for 3d3v Vlasov-Maxwell.
subroutine advect_e_pic_vm_3d3v(self, dt)
advect_e: Equations to be solved Solution with Schur complement: $ S_{+}=M_1+\frac{\Delta t^2 q^2}{4 ...
subroutine compute_particle_boundary_current_evaluate(self, xi, xnew, vi, wi, sign)
Helper function for advect_ex.
subroutine advect_vb_pic_vm_3d3v(self, dt)
advect_vb: Equations to be solved $(\mathbb{I}-\Delta \frac{\Delta t q}{2 m} \mathbb{B}(X^n,...
subroutine initialize_pic_vm_3d3v(self, maxwell_solver, particle_mesh_coupling, particle_group, phi_dofs, efield_dofs, bfield_dofs, x_min, Lx, filter, boundary_particles, solver_tolerance, iter_tolerance, max_iter, betar, force_sign, rhob, control_variate, jmean)
Constructor.
subroutine advect_ex_pic_vm_3d3v(self, dt)
advect_ex: Equations to be solved $\frac{X^{n+1}-X^n}{\Delta t}=\frac{V^{n+1}+V^n}{2}$ $\frac{V^{n+1}...
subroutine advect_e_pic_vm_3d3v_trunc(self, dt)
advect_e_trunc: This is a version of advect_e where the Particle Mass is approximated by the mass mat...
subroutine compute_particle_boundary(self, xold, xnew, vi)
Helper function for advect_x.
subroutine advect_eb_schur_pic_vm_3d3v(self, dt)
advect_eb: Equations to be solved Solution with Schur complement: $ S=M_1+\frac{\Delta t^2}{4} C^\top...
subroutine initialize_file_pic_vm_3d3v(self, maxwell_solver, particle_mesh_coupling, particle_group, phi_dofs, efield_dofs, bfield_dofs, x_min, Lx, filename, filter, boundary_particles, betar, force_sign, rhob, control_variate, jmean)
Constructor.
real(kind=f64) function, dimension(size(particle, 2)) control_variate(particle)
    Report Typos and Errors