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_cef.F90
Go to the documentation of this file.
1 
7  !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 #include "sll_assert.h"
9 #include "sll_errors.h"
10 #include "sll_memory.h"
11 #include "sll_working_precision.h"
12 
13  use sll_m_collective, only: &
16 
17  use sll_m_control_variate, only: &
19 
20  use sll_m_time_propagator_base, only: &
22 
28 
29  use sll_m_initial_distribution, only: &
32 
33  use sll_m_maxwell_3d_base, only: &
35 
36  use mpi, only: &
37  mpi_sum
38 
39  use sll_m_particle_group_base, only: &
41 
44 
45  use sll_m_profile_functions, only: &
47 
48 
49  implicit none
50 
51  public :: &
53 
54  private
55  !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
56 
59  class(sll_c_maxwell_3d_base), pointer :: maxwell_solver
60  class(sll_c_particle_mesh_coupling_3d), pointer :: particle_mesh_coupling
61  class(sll_t_particle_array), pointer :: particle_group
62 
63  sll_int32 :: spline_degree(3)
64  sll_real64 :: lx(3)
65  sll_real64 :: x_min(3)
66  sll_real64 :: x_max(3)
67  sll_int32 :: n_total0
68  sll_int32 :: n_total1
69 
70  sll_real64 :: betar(2)
71 
72  sll_real64, pointer :: phi_dofs(:)
73  sll_real64, pointer :: efield_dofs(:)
74  sll_real64, pointer :: bfield_dofs(:)
75  sll_real64, allocatable :: j_dofs(:)
76  sll_real64, allocatable :: j_dofs_local(:)
77 
78  sll_int32 :: boundary_particles = 100
79  sll_int32 :: counter_left = 0
80  sll_int32 :: counter_right = 0
81  sll_real64, pointer :: rhob(:) => null()
82 
83  logical :: electrostatic = .false.
84  logical :: adiabatic_electrons = .false.
85  logical :: lindf = .false.
86 
87  ! For control variate
88  class(sll_t_control_variates), pointer :: control_variate => null()
89  sll_int32 :: i_weight = 1
90 
91 
92  contains
93  procedure :: operatorhp => operatorhp_pic_vm_3d3v
94  procedure :: operatorhe => operatorhe_pic_vm_3d3v
95  procedure :: operatorhb => operatorhb_pic_vm_3d3v
96  procedure :: lie_splitting => lie_splitting_pic_vm_3d3v
97  procedure :: lie_splitting_back => lie_splitting_back_pic_vm_3d3v
98  procedure :: strang_splitting => strang_splitting_pic_vm_3d3v
99 
100  procedure :: init => initialize_pic_vm_3d3v
101  procedure :: free => delete_pic_vm_3d3v
102 
104 
105 contains
106 
107 
109  subroutine strang_splitting_pic_vm_3d3v(self,dt, number_steps)
110  class(sll_t_time_propagator_pic_vm_3d3v_cef), intent(inout) :: self
111  sll_real64, intent(in) :: dt
112  sll_int32, intent(in) :: number_steps
113  !local variables
114  sll_int32 :: i_step
115 
116  do i_step = 1, number_steps
117  call self%operatorHB(0.5_f64*dt)
118  call self%operatorHE(0.5_f64*dt)
119  call self%operatorHp(dt)
120  call self%operatorHE(0.5_f64*dt)
121  call self%operatorHB(0.5_f64*dt)
122  end do
123 
124  end subroutine strang_splitting_pic_vm_3d3v
125 
126 
128  subroutine lie_splitting_pic_vm_3d3v(self,dt, number_steps)
129  class(sll_t_time_propagator_pic_vm_3d3v_cef), intent(inout) :: self
130  sll_real64, intent(in) :: dt
131  sll_int32, intent(in) :: number_steps
132  !local variables
133  sll_int32 :: i_step
134 
135  do i_step = 1,number_steps
136  call self%operatorHB(dt)
137  call self%operatorHE(dt)
138  call self%operatorHp(dt)
139  end do
140 
141 
142  end subroutine lie_splitting_pic_vm_3d3v
143 
144 
146  subroutine lie_splitting_back_pic_vm_3d3v(self,dt, number_steps)
147  class(sll_t_time_propagator_pic_vm_3d3v_cef), intent(inout) :: self
148  sll_real64, intent(in) :: dt
149  sll_int32, intent(in) :: number_steps
150  !local variables
151  sll_int32 :: i_step
152 
153  do i_step = 1,number_steps
154  call self%operatorHp(dt)
155  call self%operatorHE(dt)
156  call self%operatorHB(dt)
157  end do
158 
159  end subroutine lie_splitting_back_pic_vm_3d3v
160 
161 
162  !---------------------------------------------------------------------------!
166  subroutine operatorhp_pic_vm_3d3v(self, dt)
167  class(sll_t_time_propagator_pic_vm_3d3v_cef), intent( inout ) :: self
168  sll_real64, intent( in ) :: dt
169  !local variables
170  sll_int32 :: i_part, i_sp
171  sll_real64 :: xnew(3), vi(3), wi(1), xi(3), wall(3)
172 
173  self%j_dofs_local = 0.0_f64
174  do i_sp = 1, self%particle_group%n_species
175  do i_part = 1, self%particle_group%group(i_sp)%n_particles
176  ! Read out particle position and velocity
177  xi = self%particle_group%group(i_sp)%get_x(i_part)
178  vi = self%particle_group%group(i_sp)%get_v(i_part)
179 
180  ! Then update particle position: X_new = X_old + dt * V
181  xnew = xi + dt * vi
182 
183  ! Get charge for accumulation of j
184  wi = self%particle_group%group(i_sp)%get_charge(i_part, self%i_weight)
185 
186  call compute_particle_boundary( self, xi, xnew, vi, wi)
187 
188  call self%particle_group%group(i_sp)%set_x(i_part, xnew)
189  call self%particle_group%group(i_sp)%set_v(i_part, vi)
190  ! Update particle weights
191  if (self%particle_group%group(i_sp)%n_weights == 3 ) then
192  wall = self%particle_group%group(i_sp)%get_weights(i_part)
193  wall(3) = self%control_variate%cv(i_sp)%update_df_weight( xnew, vi, 0.0_f64, wall(1), wall(2) )
194  call self%particle_group%group(i_sp)%set_weights( i_part, wall )
195  end if
196  end do
197  end do
198  self%j_dofs = 0.0_f64
199  ! MPI to sum up contributions from each processor
200  call sll_o_collective_allreduce( sll_v_world_collective, self%j_dofs_local, &
201  self%n_total1+2*self%n_total0, mpi_sum, self%j_dofs )
202 
203 
204  if( self%adiabatic_electrons) then
205  call self%maxwell_solver%compute_phi_from_j( self%j_dofs, self%phi_dofs, self%efield_dofs )
206  else
207  call self%maxwell_solver%compute_E_from_j( self%j_dofs, self%efield_dofs )
208  end if
209  end subroutine operatorhp_pic_vm_3d3v
210 
211 
213  subroutine compute_particle_boundary( self, xold, xnew, vi, wi )
214  class(sll_t_time_propagator_pic_vm_3d3v_cef), intent( inout ) :: self
215  sll_real64, intent( in ) :: xold(3)
216  sll_real64, intent( inout ) :: xnew(3)
217  sll_real64, intent( inout ) :: vi(3)
218  sll_real64, intent( in ) :: wi(1)
219  !local variables
220  sll_real64 :: xmid(3), xbar, dx, vh(3)
221 
222  if(xnew(1) < -self%x_max(1) .or. xnew(1) > 2._f64*self%x_max(1) ) then
223  print*, xnew
224  sll_error("particle boundary", "particle out of bound")
225  else if(xnew(1) < self%x_min(1) .or. xnew(1) > self%x_max(1) )then
226  if(xnew(1) < self%x_min(1) )then
227  xbar = self%x_min(1)
228  self%counter_left = self%counter_left+1
229  else if(xnew(1) > self%x_max(1))then
230  xbar = self%x_max(1)
231  self%counter_right = self%counter_right+1
232  end if
233 
234  dx = (xbar- xold(1))/(xnew(1)-xold(1))
235  xmid = xold + dx * (xnew-xold)
236  xmid(1) = xbar
237  vh = (xmid - xold)*wi(1)
238  call self%particle_mesh_coupling%add_current( xold, xmid, vh, self%j_dofs_local )
239  select case(self%boundary_particles)
241  xnew(1) = 2._f64*xbar-xnew(1)
242  vi(1) = -vi(1)
244  call self%particle_mesh_coupling%add_charge(xmid, wi(1), self%spline_degree, self%rhob)
246  call self%particle_mesh_coupling%add_charge(xmid, wi(1), self%spline_degree, self%rhob)
247  xnew(1) = self%x_min(1) + modulo(xnew(1)-self%x_min(1), self%Lx(1))
248  xmid(1) = self%x_max(1)+self%x_min(1)-xbar
249  call self%particle_mesh_coupling%add_charge(xmid, -wi(1), self%spline_degree, self%rhob)
250  case default
251  xnew(1) = self%x_min(1) + modulo(xnew(1)-self%x_min(1), self%Lx(1))
252  xmid(1) = self%x_max(1)+self%x_min(1)-xbar
253  end select
254  else
255  xmid = xold
256  end if
257  vh = (xnew - xmid)*wi(1)
258  call self%particle_mesh_coupling%add_current( xmid, xnew, vh, self%j_dofs_local )
259  xnew(2:3) = self%x_min(2:3) + modulo(xnew(2:3)-self%x_min(2:3), self%Lx(2:3))
260 
261  end subroutine compute_particle_boundary
262 
263 
264  !---------------------------------------------------------------------------!
268  subroutine operatorhb_pic_vm_3d3v(self, dt)
269  class(sll_t_time_propagator_pic_vm_3d3v_cef), intent(inout) :: self
270  sll_real64, intent(in) :: dt
271 
272  !local variables
273  sll_int32 :: i_part, i_sp
274  sll_real64 :: qmdt
275  sll_real64 :: vi(3), vnew(3), xi(3)
276  sll_real64 :: bfield(3), bsq(3), denom, wall(3)
277 
278  do i_sp = 1, self%particle_group%n_species
279  ! Advect dV/dt=VxB
280  qmdt = self%particle_group%group(i_sp)%species%q_over_m()*dt*0.5_f64;
281  do i_part = 1, self%particle_group%group(i_sp)%n_particles
282  vi = self%particle_group%group(i_sp)%get_v(i_part)
283  xi = self%particle_group%group(i_sp)%get_x(i_part)
284  call self%particle_mesh_coupling%evaluate &
285  (xi, [self%spline_degree(1), self%spline_degree(2)-1, self%spline_degree(3)-1], self%bfield_dofs(1:self%n_total0), bfield(1))
286  call self%particle_mesh_coupling%evaluate &
287  (xi, [self%spline_degree(1)-1, self%spline_degree(2), self%spline_degree(3)-1],self%bfield_dofs(self%n_total0+1:self%n_total0+self%n_total1), bfield(2))
288  call self%particle_mesh_coupling%evaluate &
289  (xi, [self%spline_degree(1)-1, self%spline_degree(2)-1, self%spline_degree(3)],self%bfield_dofs(self%n_total0+self%n_total1+1:self%n_total0+2*self%n_total1), bfield(3))
290 
291  bfield = qmdt*bfield
292  bsq = bfield**2
293  denom = sum(bsq)+1.0_f64
294 
295  vnew(1) = ( (bsq(1)-bsq(2)-bsq(3)+1.0_f64)*vi(1) + &
296  2.0_f64*(bfield(1)*bfield(2)+bfield(3))*vi(2) + &
297  2.0_f64*(bfield(1)*bfield(3)-bfield(2))*vi(3) )/denom
298  vnew(2) = ( 2.0_f64*(bfield(1)*bfield(2)-bfield(3))*vi(1) + &
299  (-bsq(1)+bsq(2)-bsq(3)+1.0_f64)*vi(2) + &
300  2.0_f64*(bfield(2)*bfield(3)+bfield(1))*vi(3) )/denom
301  vnew(3) = ( 2.0_f64*(bfield(1)*bfield(3)+bfield(2))*vi(1) + &
302  2.0_f64*(bfield(2)*bfield(3)-bfield(1))*vi(2) + &
303  (-bsq(1)-bsq(2)+bsq(3)+1.0_f64)*vi(3) )/denom
304 
305  call self%particle_group%group(i_sp)%set_v( i_part, vnew )
306  ! Update particle weights
307  if (self%particle_group%group(i_sp)%n_weights == 3 ) then
308  wall = self%particle_group%group(i_sp)%get_weights(i_part)
309  wall(3) = self%control_variate%cv(i_sp)%update_df_weight( xi, vnew, 0.0_f64, wall(1), wall(2) )
310  call self%particle_group%group(i_sp)%set_weights( i_part, wall )
311  end if
312  end do
313  end do
314 
315  if(self%electrostatic .eqv. .false.) then
316  ! Update efield2
317  call self%maxwell_solver%compute_E_from_B(&
318  self%betar(1)*dt, self%bfield_dofs, self%efield_dofs)
319  end if
320  end subroutine operatorhb_pic_vm_3d3v
321 
322 
323  !---------------------------------------------------------------------------!
327  subroutine operatorhe_pic_vm_3d3v(self, dt)
328  class(sll_t_time_propagator_pic_vm_3d3v_cef), intent(inout) :: self
329  sll_real64, intent(in) :: dt
330  !local variables
331  sll_int32 :: i_part, i_sp
332  sll_real64 :: vnew(3), xi(3), wall(self%i_weight), rx(3)
333  sll_real64 :: efield(3)
334  sll_real64 :: qoverm, q, m
335  sll_int32 :: i_weight
336 
337  i_weight = self%i_weight
338 
339  ! TODO: Optimize using evaluate multiple
340  do i_sp = 1, self%particle_group%n_species
341  qoverm = self%particle_group%group(i_sp)%species%q_over_m();
342  q = self%particle_group%group(i_sp)%species%q
343  m = self%particle_group%group(i_sp)%species%m
344  ! V_new = V_old + dt * E
345  do i_part=1,self%particle_group%group(i_sp)%n_particles
346  xi = self%particle_group%group(i_sp)%get_x(i_part)
347  vnew = self%particle_group%group(i_sp)%get_v(i_part)
348  ! Evaluate efields at particle position
349  call self%particle_mesh_coupling%evaluate &
350  (xi, [self%spline_degree(1)-1, self%spline_degree(2), self%spline_degree(3)], self%efield_dofs(1:self%n_total1), efield(1))
351  call self%particle_mesh_coupling%evaluate &
352  (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))
353  call self%particle_mesh_coupling%evaluate &
354  (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))
355  if( self%lindf .eqv. .false.) then
356  vnew = vnew + dt* qoverm * efield
357  call self%particle_group%group(i_sp)%set_v(i_part, vnew)
358  ! Update particle weights
359  if (self%particle_group%group(i_sp)%n_weights == 3 ) then
360  wall = self%particle_group%group(i_sp)%get_weights(i_part)
361  wall(3) = self%control_variate%cv(i_sp)%update_df_weight( xi, vnew, 0.0_f64, wall(1), wall(2) )
362  call self%particle_group%group(i_sp)%set_weights( i_part, wall )
363  end if
364  else
365  ! Update particle weights
366  wall = self%particle_group%group(i_sp)%get_weights(i_part)
367  select type(p => self%control_variate%cv(i_sp)%control_variate_distribution_params)
369  rx = xi
370  rx(1) = rx(1) + m*vnew(2)/q
371  rx = (rx-self%x_min)/self%Lx
372 
373  wall(1) = wall(1) + dt* (q/p%profile%T_i(rx(1)) * sum(efield * vnew)-&
374  efield(2)*(p%profile%drho_0(rx(1))/p%profile%rho_0(rx(1))+(0.5_f64*m*sum(vnew**2)/p%profile%T_i(rx(1)) - 1.5_f64)* p%profile%dT_i(rx(1))/p%profile%T_i(rx(1)) ) ) *&
375  self%control_variate%cv(i_sp)%control_variate(rx, vnew, 0._f64)/p%eval_v_density(vnew, xi, m)*product(self%Lx)
377  rx = xi
378  wall(1) = wall(1) + dt* (q/p%profile%T_i(rx(1)) * sum(efield * vnew)-&
379  efield(2)*(p%profile%drho_0(rx(1))/p%profile%rho_0(rx(1))+(0.5_f64*m*sum(vnew**2)/p%profile%T_i(rx(1)) - 1.5_f64)* p%profile%dT_i(rx(1))/p%profile%T_i(rx(1)) ) ) *product(self%Lx)
380  class default
381  wall(1) = wall(1) + dt* qoverm* sum(efield * vnew) *product(self%Lx)
382  end select
383  call self%particle_group%group(i_sp)%set_weights( i_part, wall )
384  end if
385  end do
386  end do
387 
388  if(self%electrostatic .eqv. .false.) then
389  ! Update bfield
390  call self%maxwell_solver%compute_B_from_E( &
391  dt, self%efield_dofs, self%bfield_dofs)
392  end if
393  end subroutine operatorhe_pic_vm_3d3v
394 
395 
396  !---------------------------------------------------------------------------!
399  self, &
400  maxwell_solver, &
401  particle_mesh_coupling, &
402  particle_group, &
403  phi_dofs, &
404  efield_dofs, &
405  bfield_dofs, &
406  x_min, &
407  Lx, &
408  boundary_particles, &
409  betar, &
410  electrostatic, &
411  rhob, &
412  control_variate)
413  class(sll_t_time_propagator_pic_vm_3d3v_cef), intent(out) :: self
414  class(sll_c_maxwell_3d_base), target, intent(in) :: maxwell_solver
415  class(sll_c_particle_mesh_coupling_3d), target, intent(in) :: particle_mesh_coupling
416  class(sll_t_particle_array), target, intent(in) :: particle_group
417  sll_real64, target, intent( in ) :: phi_dofs(:)
418  sll_real64, target, intent(in) :: efield_dofs(:)
419  sll_real64, target, intent(in) :: bfield_dofs(:)
420  sll_real64, intent(in) :: x_min(3)
421  sll_real64, intent(in) :: lx(3)
422  sll_int32, optional, intent( in ) :: boundary_particles
423  sll_real64, optional, intent(in) :: betar(2)
424  logical, optional, intent(in) :: electrostatic
425  sll_real64, optional, target, intent( in ) :: rhob(:)
426  class(sll_t_control_variates), optional, target, intent(in) :: control_variate
427  !local variables
428  sll_int32 :: ierr
429 
430  if( present(electrostatic) )then
431  self%electrostatic = electrostatic
432  end if
433 
434  if (present(boundary_particles) ) then
435  self%boundary_particles = boundary_particles
436  end if
437 
438  if( present(rhob) )then
439  self%rhob => rhob
440  end if
441 
442  if( particle_group%group(1)%species%q > 0._f64) self%adiabatic_electrons = .true.
443 
444  self%maxwell_solver => maxwell_solver
445  self%particle_mesh_coupling => particle_mesh_coupling
446  self%particle_group => particle_group
447  self%phi_dofs => phi_dofs
448  self%efield_dofs => efield_dofs
449  self%bfield_dofs => bfield_dofs
450 
451  self%n_total0 = self%maxwell_solver%n_total0
452  self%n_total1 = self%maxwell_solver%n_total1
453 
454  sll_allocate( self%j_dofs(1:self%n_total1+self%n_total0*2), ierr )
455  sll_allocate( self%j_dofs_local(1:self%n_total1+self%n_total0*2), ierr )
456 
457  self%spline_degree = self%particle_mesh_coupling%spline_degree
458  self%x_min = x_min
459  self%x_max = x_min + lx
460  self%Lx = lx
461 
462 
463  if (present(control_variate)) then
464  allocate(self%control_variate )
465  allocate(self%control_variate%cv(self%particle_group%n_species) )
466  self%control_variate => control_variate
467  if(self%particle_group%group(1)%n_weights == 1 ) self%lindf = .true.
468  self%i_weight = self%particle_group%group(1)%n_weights
469  end if
470 
471  if (present(betar)) then
472  self%betar = betar!32.89_f64
473  else
474  self%betar = 1.0_f64
475  end if
476 
477  end subroutine initialize_pic_vm_3d3v
478 
479 
480  !---------------------------------------------------------------------------!
482  subroutine delete_pic_vm_3d3v(self)
483  class(sll_t_time_propagator_pic_vm_3d3v_cef), intent( inout ) :: self
484 
485  deallocate(self%j_dofs)
486  deallocate(self%j_dofs_local)
487  self%maxwell_solver => null()
488  self%particle_mesh_coupling => null()
489  self%particle_group => null()
490  self%phi_dofs => null()
491  self%efield_dofs => null()
492  self%bfield_dofs => null()
493 
494  end subroutine delete_pic_vm_3d3v
495 
496 
Combines values from all processes and distributes the result back to all processes.
Parallelizing facility.
type(sll_t_collective_t), pointer, public sll_v_world_collective
Control of subset assignment. Processes with the same color are in the same new communicator.
Parameters to define common initial distributions.
Module interface to solve Maxwell's equations in 3D.
Base class for kernel smoothers for accumulation and field evaluation in PIC.
functions for initial profile of the particle distribution function
Base class for Hamiltonian splittings.
Particle pusher based on Hamiltonian splitting using in Crouseilles, Einkemmer, Faou for 3d3v Vlasov-...
subroutine lie_splitting_pic_vm_3d3v(self, dt, number_steps)
Lie splitting.
subroutine operatorhb_pic_vm_3d3v(self, dt)
Push H_B: Equations to be solved $(\mathbb{I}-\Delta \frac{\Delta t q}{2 m} \mathbb{B}(X^n,...
subroutine operatorhe_pic_vm_3d3v(self, dt)
Push H_E: Equations to be solved $V^{n+1}=V^n+\Delta t\mathbb{W}_{\frac{q}{m}} \mathbb{Lambda}^1(X^n)...
subroutine strang_splitting_pic_vm_3d3v(self, dt, number_steps)
Finalization.
subroutine operatorhp_pic_vm_3d3v(self, dt)
Push H_p: Equations to be solved $X^{n+1}=X^n+\Delta t V^n$ $M_1 e^n= M_1 e^n- \int_{t^n}^{t^{n+1}} \...
subroutine initialize_pic_vm_3d3v(self, maxwell_solver, particle_mesh_coupling, particle_group, phi_dofs, efield_dofs, bfield_dofs, x_min, Lx, boundary_particles, betar, electrostatic, rhob, control_variate)
Constructor.
subroutine lie_splitting_back_pic_vm_3d3v(self, dt, number_steps)
Lie splitting (oposite ordering)
subroutine compute_particle_boundary(self, xold, xnew, vi, wi)
Helper function for operatorHp.
Particle pusher based on antisymmetric splitting with AVF for 3d3v Vlasov-Maxwell with coordinate tra...
real(kind=f64) function, dimension(size(particle, 2)) control_variate(particle)
    Report Typos and Errors