MPI-AMRVAC 3.1
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_particle_gca.t
Go to the documentation of this file.
1!> Particle mover with Newtonian/relativistic Guiding Center Approximation (GCA)
2!> By Jannis Teunissen, Bart Ripperda, Oliver Porth, and Fabio Bacchini (2016-2020)
5
6 private
7
8 !> Variable index for gradient B, with relativistic correction 1/kappa
9 !> where kappa = 1/sqrt(1 - E_perp^2/B^2)
10 integer, protected, allocatable :: grad_kappa_b(:)
11 !> Variable index for (B . grad)B (curvature B drift)
12 integer, protected, allocatable :: b_dot_grad_b(:)
13
14 ! ExB related drifts (vE = ExB/B^2)
15 !> Variable index for curvature drift
16 integer, protected, allocatable :: ve_dot_grad_b(:)
17 !> Variable index for polarization drift
18 integer, protected, allocatable :: b_dot_grad_ve(:)
19 !> Variable index for polarization drift
20 integer, protected, allocatable :: ve_dot_grad_ve(:)
21
22 public :: gca_init
23 public :: gca_create_particles
24 integer, parameter :: rk4=1, ark4=2
25
26 ! Variables
27 public :: bp, ep, grad_kappa_b, b_dot_grad_b
29 public :: vp, jp, rhop
30
31contains
32
33 subroutine gca_init()
35 integer :: idir, nwx
36
37 if (physics_type/='mhd') call mpistop("GCA particles need magnetic field!")
38 if (ndir/=3) call mpistop("GCA particles need ndir=3!")
39
40 nwx = 0
41 allocate(bp(ndir))
42 do idir = 1, ndir
43 nwx = nwx + 1
44 bp(idir) = nwx
45 end do
46 allocate(ep(ndir))
47 do idir = 1, ndir
48 nwx = nwx + 1
49 ep(idir) = nwx
50 end do
51 allocate(grad_kappa_b(ndir))
52 do idir = 1, ndir
53 nwx = nwx + 1
54 grad_kappa_b(idir) = nwx
55 end do
56 allocate(b_dot_grad_b(ndir))
57 do idir = 1, ndir
58 nwx = nwx + 1
59 b_dot_grad_b(idir) = nwx
60 end do
61 allocate(ve_dot_grad_b(ndir))
62 do idir = 1, ndir
63 nwx = nwx + 1
64 ve_dot_grad_b(idir) = nwx
65 end do
66 allocate(b_dot_grad_ve(ndir))
67 do idir = 1, ndir
68 nwx = nwx + 1
69 b_dot_grad_ve(idir) = nwx
70 end do
71 allocate(ve_dot_grad_ve(ndir))
72 do idir = 1, ndir
73 nwx = nwx + 1
74 ve_dot_grad_ve(idir) = nwx
75 end do
76 allocate(vp(ndir))
77 do idir = 1, ndir
78 nwx = nwx + 1
79 vp(idir) = nwx
80 end do
81 allocate(jp(ndir))
82 do idir = 1, ndir
83 nwx = nwx + 1
84 jp(idir) = nwx
85 end do
86 nwx = nwx + 1 ! density
87 rhop = nwx
88
89 ngridvars=nwx
90
91 particles_fill_gridvars => gca_fill_gridvars
92
93 if (associated(particles_define_additional_gridvars)) then
95 end if
96
97 select case(integrator_type_particles)
98 case('RK4','Rk4','rk4')
100 case('ARK4','ARk4','Ark4','ark4')
101 integrator = ark4
102 case default
103 integrator = ark4
104 end select
105
106 particles_integrate => gca_integrate_particles
107
108 end subroutine gca_init
109
111 ! initialise the particles
114
115 double precision :: b(ndir), u(ndir), magmom
116 double precision :: bnorm, lfac, vnorm, vperp, vpar
117 double precision :: x(3, num_particles)
118 double precision :: v(3, num_particles)
119 double precision :: q(num_particles)
120 double precision :: m(num_particles)
121 double precision :: rrd(num_particles,ndir)
122 double precision :: defpayload(ndefpayload)
123 double precision :: usrpayload(nusrpayload)
124 integer :: igrid, ipe_particle
125 integer :: n, idir, nparticles_local
126 logical :: follow(num_particles), check
127
128 if (mype==0) then
129 if (.not. associated(usr_create_particles)) then
130 ! Randomly distributed
131 do idir=1,ndir
132 do n = 1, num_particles
133 rrd(n,idir) = rng%unif_01()
134 end do
135 end do
136 do n=1, num_particles
137 {^d&x(^d,n) = xprobmin^d + rrd(n+1,^d) * (xprobmax^d - xprobmin^d)\}
138 end do
139 else
140 call usr_create_particles(num_particles, x, v, q, m, follow)
141 end if
142 end if
143
144 call mpi_bcast(x,3*num_particles,mpi_double_precision,0,icomm,ierrmpi)
145 call mpi_bcast(v,3*num_particles,mpi_double_precision,0,icomm,ierrmpi)
146 call mpi_bcast(q,num_particles,mpi_double_precision,0,icomm,ierrmpi)
147 call mpi_bcast(m,num_particles,mpi_double_precision,0,icomm,ierrmpi)
148 call mpi_bcast(follow,num_particles,mpi_logical,0,icomm,ierrmpi)
149
150 nparticles_local = 0
151
152 ! first find ipe and igrid responsible for particle
153 do n = 1, num_particles
154 call find_particle_ipe(x(:, n),igrid,ipe_particle)
155
156 particle(n)%igrid = igrid
157 particle(n)%ipe = ipe_particle
158
159 if(ipe_particle == mype) then
160 check = .true.
161
162 ! Check for user-defined modifications or rejection conditions
163 if (associated(usr_check_particle)) call usr_check_particle(igrid, x(:,n), v(:,n), q(n), m(n), follow(n), check)
164 if (check) then
166 else
167 cycle
168 end if
169
170 nparticles_local = nparticles_local + 1
171
172 call get_lfac_from_velocity(v(:, n), lfac)
173
174 allocate(particle(n)%self)
175 particle(n)%self%x = x(:, n)
176 particle(n)%self%q = q(n)
177 particle(n)%self%m = m(n)
178 particle(n)%self%follow = follow(n)
179 particle(n)%self%index = n
180 particle(n)%self%time = global_time
181 particle(n)%self%dt = 0.0d0
182
183 call get_vec(bp, igrid, x(:, n), particle(n)%self%time, b)
184
185 bnorm = norm2(b(:))
186 vnorm = norm2(v(:, n))
187 vpar = sum(v(:, n) * b/bnorm)
188 vperp = sqrt(vnorm**2 - vpar**2)
189
190 ! The momentum vector u(1:3) is filled with the following components
191
192 ! parallel momentum component (gamma v||)
193 particle(n)%self%u(1) = lfac * vpar
194
195 ! Mr: the conserved magnetic moment
196 magmom = m(n) * (vperp * lfac)**2 / (2.0d0 * bnorm)
197 particle(n)%self%u(2) = magmom
198
199 ! Lorentz factor
200 particle(n)%self%u(3) = lfac
201
202 ! initialise payloads for GCA module
203 allocate(particle(n)%payload(npayload))
204 call gca_update_payload(igrid,x(:,n),particle(n)%self%u(:),q(n),m(n),defpayload,ndefpayload,0.d0)
205 particle(n)%payload(1:ndefpayload) = defpayload
206 if (associated(usr_update_payload)) then
207 call usr_update_payload(igrid,x(:,n),particle(n)%self%u(:),q(n),m(n),usrpayload,nusrpayload,0.d0)
208 particle(n)%payload(ndefpayload+1:npayload) = usrpayload
209 end if
210 end if
211 end do
212
213 call mpi_allreduce(nparticles_local,nparticles,1,mpi_integer,mpi_sum,icomm,ierrmpi)
214
215 end subroutine gca_create_particles
216
217 subroutine gca_fill_gridvars
220 use mod_geometry
221
222 double precision, dimension(ixG^T,1:ndir) :: beta
223 double precision, dimension(ixG^T,1:nw) :: w,wold
224 double precision :: current(ixg^t,7-2*ndir:3)
225 double precision, dimension(ixG^T,1:ndir) :: ve, bhat
226 double precision, dimension(ixG^T) :: kappa, kappa_b, absb, tmp
227 integer :: igrid, iigrid, idir, idim
228 integer :: idirmin
229
231
232 do iigrid=1,igridstail; igrid=igrids(iigrid);
233 w(ixg^t,1:nw) = ps(igrid)%w(ixg^t,1:nw)
234 call phys_to_primitive(ixg^ll,ixg^ll,w,ps(igrid)%x)
235
236 ! grad(kappa B)
237 absb(ixg^t) = sqrt(sum(gridvars(igrid)%w(ixg^t,bp(:))**2,dim=ndim+1))
238 ve(ixg^t,1) = gridvars(igrid)%w(ixg^t,ep(2)) * gridvars(igrid)%w(ixg^t,bp(3)) &
239 - gridvars(igrid)%w(ixg^t,ep(3)) * gridvars(igrid)%w(ixg^t,bp(2))
240 ve(ixg^t,2) = gridvars(igrid)%w(ixg^t,ep(3)) * gridvars(igrid)%w(ixg^t,bp(1)) &
241 - gridvars(igrid)%w(ixg^t,ep(1)) * gridvars(igrid)%w(ixg^t,bp(3))
242 ve(ixg^t,3) = gridvars(igrid)%w(ixg^t,ep(1)) * gridvars(igrid)%w(ixg^t,bp(2)) &
243 - gridvars(igrid)%w(ixg^t,ep(2)) * gridvars(igrid)%w(ixg^t,bp(1))
244 do idir=1,ndir
245 where (absb(ixg^t) .gt. 0.d0)
246 ve(ixg^t,idir) = ve(ixg^t,idir) / absb(ixg^t)**2
247 elsewhere
248 ve(ixg^t,idir) = 0.d0
249 end where
250 end do
251 if (any(sum(ve(ixg^t,:)**2,dim=ndim+1) .ge. c_norm**2) .and. relativistic) then
252 call mpistop("GCA FILL GRIDVARS: vE>c! ABORTING...")
253 end if
254 if (any(ve .ne. ve)) then
255 call mpistop("GCA FILL GRIDVARS: NaNs IN vE! ABORTING...")
256 end if
257
258 if (relativistic) then
259 kappa(ixg^t) = 1.d0/sqrt(1.0d0 - sum(ve(ixg^t,:)**2,dim=ndim+1)/c_norm**2)
260 else
261 kappa(ixg^t) = 1.d0
262 end if
263 kappa_b(ixg^t) = absb(ixg^t) / kappa(ixg^t)
264
265 if (any(kappa_b .ne. kappa_b)) then
266 call mpistop("GCA FILL GRIDVARS: NaNs IN kappa_B! ABORTING...")
267 end if
268
269 tmp=0.d0
270 do idim=1,ndim
271 call gradient(kappa_b,ixg^ll,ixg^ll^lsub1,idim,tmp)
272 gridvars(igrid)%w(ixg^t,grad_kappa_b(idim)) = tmp(ixg^t)
273 end do
274
275 ! bhat
276 do idir=1,ndir
277 where (absb(ixg^t) .gt. 0.d0)
278 bhat(ixg^t,idir) = gridvars(igrid)%w(ixg^t,bp(idir)) / absb(ixg^t)
279 elsewhere
280 bhat(ixg^t,idir) = 0.d0
281 end where
282 end do
283 if (any(bhat .ne. bhat)) then
284 call mpistop("GCA FILL GRIDVARS: NaNs IN bhat! ABORTING...")
285 end if
286
287 do idir=1,ndir
288 ! (b dot grad) b and the other directional derivatives
289 do idim=1,ndim
290 call gradient(bhat(ixg^t,idir),ixg^ll,ixg^ll^lsub1,idim,tmp)
291 gridvars(igrid)%w(ixg^t,b_dot_grad_b(idir)) = gridvars(igrid)%w(ixg^t,b_dot_grad_b(idir)) &
292 + bhat(ixg^t,idim) * tmp(ixg^t)
293 gridvars(igrid)%w(ixg^t,ve_dot_grad_b(idir)) = gridvars(igrid)%w(ixg^t,ve_dot_grad_b(idir)) &
294 + ve(ixg^t,idim) * tmp(ixg^t)
295 call gradient(ve(ixg^t,idir),ixg^ll,ixg^ll^lsub1,idim,tmp)
296 gridvars(igrid)%w(ixg^t,b_dot_grad_ve(idir)) = gridvars(igrid)%w(ixg^t,b_dot_grad_ve(idir)) &
297 + bhat(ixg^t,idim) * tmp(ixg^t)
298 gridvars(igrid)%w(ixg^t,ve_dot_grad_ve(idir)) = gridvars(igrid)%w(ixg^t,ve_dot_grad_ve(idir)) &
299 + ve(ixg^t,idim) * tmp(ixg^t)
300 end do
301 end do
302
303 end do
304
305 end subroutine gca_fill_gridvars
306
307 subroutine gca_integrate_particles(end_time)
308 use mod_odeint
311 double precision, intent(in) :: end_time
312
313 double precision :: lfac, abss
314 double precision :: defpayload(ndefpayload)
315 double precision :: usrpayload(nusrpayload)
316 double precision :: dt_p, tloc, y(ndir+2),dydt(ndir+2),ytmp(ndir+2), euler_cfl, int_factor
317 double precision :: tk, k1(ndir+2),k2(ndir+2),k3(ndir+2),k4(ndir+2)
318 double precision, dimension(1:ndir) :: x, ve, e, b, bhat, x_new, vfluid, current
319 double precision, dimension(1:ndir) :: drift1, drift2
320 double precision, dimension(1:ndir) :: drift3, drift4, drift5, drift6, drift7
321 double precision, dimension(1:ndir) :: bdotgradb, vedotgradb, gradkappab
322 double precision, dimension(1:ndir) :: bdotgradve, vedotgradve
323 double precision, dimension(1:ndir) :: gradbdrift, reldrift, bdotgradbdrift
324 double precision, dimension(1:ndir) :: vedotgradbdrift, bdotgradvedrift
325 double precision, dimension(1:ndir) :: vedotgradvedrift
326 double precision :: kappa, mr, upar, m, absb, gamma, q, mompar, vpar, veabs
327 double precision :: gradbdrift_abs, reldrift_abs, epar
328 double precision :: bdotgradbdrift_abs, vedotgradbdrift_abs
329 double precision :: bdotgradvedrift_abs, vedotgradvedrift_abs
330 double precision :: momentumpar1, momentumpar2, momentumpar3, momentumpar4
331 ! Precision of time-integration:
332 double precision,parameter :: eps=1.0d-6
333 ! for odeint:
334 double precision :: h1, hmin, h_old
335 integer :: nok, nbad, ic1^d, ic2^d, ierror, nvar
336 integer :: ipart, iipart, seed, ic^d,igrid_particle, ipe_particle, ipe_working
337 logical :: int_choice
338 logical :: bc_applied
339
340 nvar=ndir+2
341
342 do iipart=1,nparticles_active_on_mype
343 ipart = particles_active_on_mype(iipart)
344 int_choice = .false.
345 dt_p = gca_get_particle_dt(particle(ipart), end_time)
346 particle(ipart)%self%dt = dt_p
347
348 igrid_working = particle(ipart)%igrid
349 ipart_working = particle(ipart)%self%index
350 tloc = particle(ipart)%self%time
351 x(1:ndir) = particle(ipart)%self%x(1:ndir)
352
353 select case (integrator)
354 case (rk4)
355 ! Runge-Kutta order 4
356 tk = tloc
357 y(1:ndir) = x(1:ndir) ! position of guiding center
358 y(ndir+1) = particle(ipart)%self%u(1) ! parallel momentum component (gamma v||)
359 y(ndir+2) = particle(ipart)%self%u(2) ! conserved magnetic moment Mr
360 call derivs_gca_rk(tk,y,k1)
361 tk = tloc + dt_p/2.d0
362 y(1:ndir) = x + dt_p*k1(1:ndir)/2.d0
363 y(ndir+1) = particle(ipart)%self%u(1) + dt_p*k1(ndir+1)/2.d0
364 call derivs_gca_rk(tk,y,k2)
365 tk = tloc + dt_p/2.d0
366 y(1:ndir) = x + dt_p*k2(1:ndir)/2.d0
367 y(ndir+1) = particle(ipart)%self%u(1) + dt_p*k2(ndir+1)/2.d0
368 call derivs_gca_rk(tk,y,k3)
369 tk = tloc + dt_p
370 y(1:ndir) = x + dt_p*k3(1:ndir)
371 y(ndir+1) = particle(ipart)%self%u(1) + dt_p*k3(ndir+1)
372 call derivs_gca_rk(tk,y,k4)
373 y(1:ndir) = x(1:ndir) ! position of guiding center
374 y(ndir+1) = particle(ipart)%self%u(1) ! parallel momentum component (gamma v||)
375 y = y + dt_p/6.d0*(k1 + 2.d0*k2 + 2.d0*k3 + k4)
376 particle(ipart)%self%x(1:ndir) = y(1:ndir)
377 particle(ipart)%self%u(1) = y(ndir+1)
378
379 case (ark4)
380 ! Adaptive stepwidth RK4:
381 ! initial solution vector:
382 y(1:ndir) = x(1:ndir) ! position of guiding center
383 y(ndir+1) = particle(ipart)%self%u(1) ! parallel momentum component (gamma v||)
384 y(ndir+2) = particle(ipart)%self%u(2) ! conserved magnetic moment Mr
385 ! y(ndir+3) = particle(ipart)%self%u(3) ! Lorentz factor of particle
386
387 ! we temporarily save the solution vector, to replace the one from the euler
388 ! timestep after euler integration
389 ytmp=y
390
391 call derivs_gca(particle(ipart)%self%time,y,dydt)
392
393 ! make an Euler step with the proposed timestep:
394 ! factor to ensure we capture all particles near the internal ghost cells.
395 ! Can be adjusted during a run, after an interpolation error.
396 euler_cfl=2.5d0
397
398 ! new solution vector:
399 y(1:ndir+2) = y(1:ndir+2) + euler_cfl * dt_p * dydt(1:ndir+2)
400 particle(ipart)%self%x(1:ndir) = y(1:ndir) ! position of guiding center
401 particle(ipart)%self%u(1) = y(ndir+1) ! parallel momentum component(gamma v||)
402 particle(ipart)%self%u(2) = y(ndir+2) ! conserved magnetic moment
403
404 ! check if the particle is in the internal ghost cells
405 int_factor =1.0d0
406
408 ! if particle is not in the grid an euler timestep is taken instead of a RK4
409 ! timestep. Then based on that we do an interpolation and check how much further
410 ! the timestep for the RK4 has to be restricted.
411 ! factor to make integration more accurate for particles near the internal
412 ! ghost cells. This factor can be changed during integration after an
413 ! interpolation error. But one should be careful with timesteps for i/o
414
415 ! flat interpolation:
416 {ic^d = int((y(^d)-rnode(rpxmin^d_,igrid_working))/rnode(rpdx^d_,igrid_working)) + 1 + nghostcells\}
417
418 ! linear interpolation:
419 {
420 if (ps(igrid_working)%x({ic^dd},^d) .lt. y(^d)) then
421 ic1^d = ic^d
422 else
423 ic1^d = ic^d -1
424 end if
425 ic2^d = ic1^d + 1
426 \}
427
428 int_factor =0.5d0
429
430 {^d&
431 if (ic1^d .le. ixglo^d-2 .or. ic2^d .ge. ixghi^d+2) then
432 int_factor = 0.05d0
433 end if
434 \}
435
436 {^d&
437 if (ic1^d .eq. ixglo^d-1 .or. ic2^d .eq. ixghi^d+1) then
438 int_factor = 0.1d0
439 end if
440 \}
441
442 dt_p=int_factor*dt_p
443 end if
444
445 ! replace the solution vector with the original as it was before the Euler timestep
446 y(1:ndir+2) = ytmp(1:ndir+2)
447
448 particle(ipart)%self%x(1:ndir) = ytmp(1:ndir) ! position of guiding center
449 particle(ipart)%self%u(1) = ytmp(ndir+1) ! parallel momentum component (gamma v||)
450 particle(ipart)%self%u(2) = ytmp(ndir+2) ! conserved magnetic moment
451
452 ! specify a minimum step hmin. If the timestep reaches this minimum, multiply by
453 ! a factor 100 to make sure the RK integration doesn't crash
454 h1 = dt_p/2.0d0; hmin=1.0d-9; h_old=dt_p/2.0d0
455
456 if(h1 .lt. hmin)then
457 h1=hmin
458 dt_p=2.0d0*h1
459 endif
460
461 if (any(y .ne. y)) then
462 call mpistop("NaNs DETECTED IN GCA_INTEGRATE BEFORE ODEINT CALL! ABORTING...")
463 end if
464
465 ! RK4 integration with adaptive stepwidth
466 call odeint(y,nvar,tloc,tloc+dt_p,eps,h1,hmin,nok,nbad,derivs_gca_rk,rkqs,ierror)
467
468 if (ierror /= 0) then
469 print *, "odeint returned error code", ierror
470 print *, "1 means hmin too small, 2 means MAXSTP exceeded"
471 print *, "Having a problem with particle", iipart
472 end if
473
474 if (any(y .ne. y)) then
475 call mpistop("NaNs DETECTED IN GCA_INTEGRATE AFTER ODEINT CALL! ABORTING...")
476 end if
477
478 ! original RK integration without interpolation in ghost cells
479 ! call odeint(y,nvar,tloc,tloc+dt_p,eps,h1,hmin,nok,nbad,derivs_gca,rkqs)
480
481 ! final solution vector after rk integration
482 particle(ipart)%self%x(1:ndir) = y(1:ndir)
483 particle(ipart)%self%u(1) = y(ndir+1)
484 particle(ipart)%self%u(2) = y(ndir+2)
485 !particle(ipart)%self%u(3) = y(ndir+3)
486 end select
487
488 ! now calculate other quantities, mean Lorentz factor, drifts, perpendicular velocity:
489 call get_bfield(igrid_working, y(1:ndir), tloc+dt_p, b)
490 call get_efield(igrid_working, y(1:ndir), tloc+dt_p, e)
491! call get_vec(bp, igrid_working,y(1:ndir),tloc+dt_p,b)
492! call get_vec(vp, igrid_working,y(1:ndir),tloc+dt_p,vfluid)
493! call get_vec(jp, igrid_working,y(1:ndir),tloc+dt_p,current)
494! e(1) = -vfluid(2)*b(3)+vfluid(3)*b(2) + particles_eta*current(1)
495! e(2) = vfluid(1)*b(3)-vfluid(3)*b(1) + particles_eta*current(2)
496! e(3) = -vfluid(1)*b(2)+vfluid(2)*b(1) + particles_eta*current(3)
497
498 absb = sqrt(sum(b(:)**2))
499 bhat(1:ndir) = b(1:ndir) / absb
500
501 epar = sum(e(:)*bhat(:))
502
503 call cross(e,bhat,ve)
504
505 ve(1:ndir) = ve(1:ndir) / absb
506 veabs = sqrt(sum(ve(:)**2))
507 if (relativistic) then
508 kappa = 1.d0/sqrt(1.0d0 - sum(ve(:)**2)/c_norm**2)
509 else
510 kappa = 1.d0
511 end if
512 mr = y(ndir+2); upar = y(ndir+1); m=particle(ipart)%self%m; q=particle(ipart)%self%q
513 if (relativistic) then
514 gamma = sqrt(1.0d0+upar**2/c_norm**2+2.0d0*mr*absb/m/c_norm**2)*kappa
515 else
516 gamma = 1.d0
517 end if
518
519 particle(ipart)%self%u(3) = gamma
520
521 ! Time update
522 particle(ipart)%self%time = particle(ipart)%self%time + dt_p
523
524 ! Update payload
525 call gca_update_payload(igrid_working,&
526 particle(ipart)%self%x,particle(ipart)%self%u,q,m,defpayload,ndefpayload,particle(ipart)%self%time)
527 particle(ipart)%payload(1:ndefpayload) = defpayload
528 if (associated(usr_update_payload)) then
530 particle(ipart)%self%x,particle(ipart)%self%u,q,m,usrpayload,nusrpayload,particle(ipart)%self%time)
531 particle(ipart)%payload(ndefpayload+1:npayload) = usrpayload
532 end if
533
534 end do
535
536 end subroutine gca_integrate_particles
537
538 subroutine derivs_gca_rk(t_s,y,dydt)
540
541 double precision :: t_s, y(ndir+2)
542 double precision :: dydt(ndir+2)
543
544 double precision,dimension(ndir):: ve, b, e, x, bhat, bdotgradb, vedotgradb, gradkappab, vfluid, current
545 double precision,dimension(ndir):: bdotgradve, vedotgradve, u, utmp1, utmp2, utmp3
546 double precision :: upar, mr, gamma, absb, q, m, epar, kappa
547 integer :: ic^d
548
549 ! Here the terms in the guiding centre equations of motion are interpolated for
550 ! the RK integration. The interpolation is also done in the ghost cells such
551 ! that the RK integration does not give an error
552
553 q = particle(ipart_working)%self%q
554 m = particle(ipart_working)%self%m
555
556 x(1:ndir) = y(1:ndir)
557 upar = y(ndir+1) ! gamma v||
558 mr = y(ndir+2)
559 !gamma = y(ndir+3)
560
561 if (any(x .ne. x)) then
562 write(*,*) "ERROR IN DERIVS_GCA_RK: NaNs IN X OR Y!"
563 write(*,*) "x",x
564 write(*,*) "y",y(ndir+1:ndir+2)
565 call mpistop("ABORTING...")
566 end if
567
568 call get_bfield(igrid_working, x, t_s, b)
569 call get_efield(igrid_working, x, t_s, e)
570! call get_vec(bp, igrid_working,x,t_s,b)
571! call get_vec(vp, igrid_working,x,t_s,vfluid)
572! call get_vec(jp, igrid_working,x,t_s,current)
573! e(1) = -vfluid(2)*b(3)+vfluid(3)*b(2) + particles_eta*current(1)
574! e(2) = vfluid(1)*b(3)-vfluid(3)*b(1) + particles_eta*current(2)
575! e(3) = -vfluid(1)*b(2)+vfluid(2)*b(1) + particles_eta*current(3)
576 call get_vec(b_dot_grad_b, igrid_working,x,t_s,bdotgradb)
577 call get_vec(ve_dot_grad_b, igrid_working,x,t_s,vedotgradb)
578 call get_vec(grad_kappa_b, igrid_working,x,t_s,gradkappab)
579 call get_vec(b_dot_grad_ve, igrid_working,x,t_s,bdotgradve)
580 call get_vec(ve_dot_grad_ve, igrid_working,x,t_s,vedotgradve)
581
582 if (any(b .ne. b) .or. any(e .ne. e) &
583 .or. any(bdotgradb .ne. bdotgradb) .or. any(vedotgradb .ne. vedotgradb) &
584 .or. any(gradkappab .ne. gradkappab) .or. any(bdotgradve .ne. bdotgradve) &
585 .or. any(vedotgradve .ne. vedotgradve)) then
586 write(*,*) "ERROR IN DERIVS_GCA_RK: NaNs IN FIELD QUANTITIES!"
587 write(*,*) "b",b
588 write(*,*) "e",e
589 write(*,*) "bdotgradb",bdotgradb
590 write(*,*) "vEdotgradb",vedotgradb
591 write(*,*) "gradkappaB",gradkappab
592 write(*,*) "bdotgradvE",bdotgradve
593 write(*,*) "vEdotgradvE",vedotgradve
594 call mpistop("ABORTING...")
595 end if
596
597 absb = sqrt(sum(b(:)**2))
598 if (absb .gt. 0.d0) then
599 bhat(1:ndir) = b(1:ndir) / absb
600 else
601 bhat = 0.d0
602 end if
603 epar = sum(e(:)*bhat(:))
604
605 call cross(e,bhat,ve)
606 if (absb .gt. 0.d0) then
607 ve(1:ndir) = ve(1:ndir) / absb
608 else
609 ve(1:ndir) = 0.d0
610 end if
611
612 if (relativistic) then
613 kappa = 1.d0/sqrt(1.0d0 - sum(ve(:)**2)/c_norm**2)
614 gamma = sqrt(1.0d0+upar**2/c_norm**2+2.0d0*mr*absb/m/c_norm**2)*kappa
615 else
616 kappa = 1.d0
617 gamma = 1.d0
618 end if
619
620 if (absb .gt. 0.d0) then
621 utmp1(1:ndir) = bhat(1:ndir)/(absb/kappa**2)
622 else
623 utmp1 = 0.d0
624 end if
625 utmp2(1:ndir) = mr/(gamma*q)*gradkappab(1:ndir) &
626 + m/q* (upar**2/gamma*bdotgradb(1:ndir) + upar*vedotgradb(1:ndir) &
627 + upar*bdotgradve(1:ndir) + gamma*vedotgradve(1:ndir))
628 if (relativistic) then
629 utmp2(1:ndir) = utmp2(1:ndir) + upar*epar/(gamma)*ve(1:ndir)
630 end if
631
632 call cross(utmp1,utmp2,utmp3)
633 u(1:ndir) = ve(1:ndir) + utmp3(1:ndir)
634
635 ! done assembling the terms, now write rhs:
636 dydt(1:ndir) = ( u(1:ndir) + upar/gamma * bhat(1:ndir) )
637 dydt(ndir+1) = q/m*epar - mr/(m*gamma) * sum(bhat(:)*gradkappab(:)) &
638 + sum(ve(:)*(upar*bdotgradb(:)+gamma*vedotgradb(:)))
639 dydt(ndir+2) = 0.0d0 ! magnetic moment is conserved
640
641 end subroutine derivs_gca_rk
642
643 subroutine derivs_gca(t_s,y,dydt)
645
646 double precision :: t_s, y(ndir+2)
647 double precision :: dydt(ndir+2)
648
649 double precision,dimension(ndir):: ve, b, e, x, bhat, bdotgradb, vedotgradb, gradkappab, vfluid, current
650 double precision,dimension(ndir):: bdotgradve, vedotgradve, u, utmp1, utmp2, utmp3
651 double precision :: upar, mr, gamma, absb, q, m, epar, kappa
652
653 ! Here the normal interpolation is done for the terms in the GCA equations of motion
654
655 q = particle(ipart_working)%self%q
656 m = particle(ipart_working)%self%m
657
658 x(1:ndir) = y(1:ndir)
659 upar = y(ndir+1) ! gamma v||
660 mr = y(ndir+2)
661 !gamma = y(ndir+3)
662
663 if (any(x .ne. x)) then
664 call mpistop("ERROR IN DERIVS_GCA: NaNs IN X! ABORTING...")
665 end if
666
667 call get_bfield(igrid_working, x, t_s, b)
668 call get_efield(igrid_working, x, t_s, e)
669! call get_vec(bp, igrid_working,x,t_s,b)
670! call get_vec(vp, igrid_working,x,t_s,vfluid)
671! call get_vec(jp, igrid_working,x,t_s,current)
672! e(1) = -vfluid(2)*b(3)+vfluid(3)*b(2) + particles_eta*current(1)
673! e(2) = vfluid(1)*b(3)-vfluid(3)*b(1) + particles_eta*current(2)
674! e(3) = -vfluid(1)*b(2)+vfluid(2)*b(1) + particles_eta*current(3)
675 call get_vec(b_dot_grad_b, igrid_working,x,t_s,bdotgradb)
676 call get_vec(ve_dot_grad_b, igrid_working,x,t_s,vedotgradb)
677 call get_vec(grad_kappa_b, igrid_working,x,t_s,gradkappab)
678 call get_vec(b_dot_grad_ve, igrid_working,x,t_s,bdotgradve)
679 call get_vec(ve_dot_grad_ve, igrid_working,x,t_s,vedotgradve)
680
681 absb = sqrt(sum(b(:)**2))
682 if (absb .gt. 0.d0) then
683 bhat(1:ndir) = b(1:ndir) / absb
684 else
685 bhat = 0.d0
686 end if
687
688 epar = sum(e(:)*bhat(:))
689 call cross(e,bhat,ve)
690 if (absb .gt. 0.d0) ve(1:ndir) = ve(1:ndir) / absb
691
692 if (relativistic) then
693 kappa = 1.d0/sqrt(1.0d0 - sum(ve(:)**2)/c_norm**2)
694 gamma = sqrt(1.0d0+upar**2/c_norm**2+2.0d0*mr*absb/m/c_norm**2)*kappa
695 else
696 kappa = 1.d0
697 gamma = 1.d0
698 end if
699 if (absb .gt. 0.d0) then
700 utmp1(1:ndir) = bhat(1:ndir)/(absb/kappa**2)
701 else
702 utmp1 = 0.d0
703 end if
704 utmp2(1:ndir) = mr/(gamma*q)*gradkappab(1:ndir) &
705 + m/q* (upar**2/gamma*bdotgradb(1:ndir) + upar*vedotgradb(1:ndir) &
706 + upar*bdotgradve(1:ndir) + gamma*vedotgradve(1:ndir))
707 if (relativistic) then
708 utmp2(1:ndir) = utmp2(1:ndir) + upar*epar/(gamma)*ve(1:ndir)
709 end if
710
711 call cross(utmp1,utmp2,utmp3)
712 u(1:ndir) = ve(1:ndir) + utmp3(1:ndir)
713
714 ! done assembling the terms, now write rhs:
715 dydt(1:ndir) = ( u(1:ndir) + upar/gamma * bhat(1:ndir) )
716 dydt(ndir+1) = q/m*epar - mr/(m*gamma) * sum(bhat(:)*gradkappab(:)) &
717 + sum(ve(:)*(upar*bdotgradb(:)+gamma*vedotgradb(:)))
718 dydt(ndir+2) = 0.0d0 ! magnetic moment is conserved
719
720 end subroutine derivs_gca
721
722 !> Update payload subroutine
723 subroutine gca_update_payload(igrid,xpart,upart,qpart,mpart,mypayload,mynpayload,particle_time)
725 integer, intent(in) :: igrid,mynpayload
726 double precision, intent(in) :: xpart(1:ndir),upart(1:ndir),qpart,mpart,particle_time
727 double precision, intent(out) :: mypayload(mynpayload)
728 double precision, dimension(1:ndir) :: ve, e, b, bhat, vfluid, current
729 double precision, dimension(1:ndir) :: drift1, drift2
730 double precision, dimension(1:ndir) :: drift3, drift4, drift5, drift6, drift7
731 double precision, dimension(1:ndir) :: bdotgradb, vedotgradb, gradkappab
732 double precision, dimension(1:ndir) :: bdotgradve, vedotgradve
733 double precision, dimension(1:ndir) :: gradbdrift, reldrift, bdotgradbdrift
734 double precision, dimension(1:ndir) :: vedotgradbdrift, bdotgradvedrift
735 double precision, dimension(1:ndir) :: vedotgradvedrift
736 double precision :: kappa, upar, absb, gamma, vpar, veabs
737 double precision :: gradbdrift_abs, reldrift_abs, epar
738 double precision :: bdotgradbdrift_abs, vedotgradbdrift_abs
739 double precision :: bdotgradvedrift_abs, vedotgradvedrift_abs
740 double precision :: momentumpar1, momentumpar2, momentumpar3, momentumpar4
741
742 call get_bfield(igrid, xpart(1:ndir), particle_time, b)
743 call get_efield(igrid, xpart(1:ndir), particle_time, e)
744! call get_vec(bp, igrid,xpart(1:ndir),particle_time,b)
745! call get_vec(vp, igrid,xpart(1:ndir),particle_time,vfluid)
746! call get_vec(jp, igrid,xpart(1:ndir),particle_time,current)
747! e(1) = -vfluid(2)*b(3)+vfluid(3)*b(2) + particles_eta*current(1)
748! e(2) = vfluid(1)*b(3)-vfluid(3)*b(1) + particles_eta*current(2)
749! e(3) = -vfluid(1)*b(2)+vfluid(2)*b(1) + particles_eta*current(3)
750
751 absb = sqrt(sum(b(:)**2))
752 bhat(1:ndir) = b(1:ndir) / absb
753 epar = sum(e(:)*bhat(:))
754 call cross(e,bhat,ve)
755 ve(1:ndir) = ve(1:ndir) / absb
756 veabs = sqrt(sum(ve(:)**2))
757 if (relativistic) then
758 kappa = 1.d0/sqrt(1.0d0 - sum(ve(:)**2)/c_norm**2)
759 else
760 kappa = 1.d0
761 end if
762 vpar = upart(1)/upart(3)
763 upar = upart(1)
764
765 call get_vec(b_dot_grad_b, igrid,xpart(1:ndir),particle_time,bdotgradb)
766 call get_vec(ve_dot_grad_b, igrid,xpart(1:ndir),particle_time,vedotgradb)
767 call get_vec(grad_kappa_b, igrid,xpart(1:ndir),particle_time,gradkappab)
768 call get_vec(b_dot_grad_ve, igrid,xpart(1:ndir),particle_time,bdotgradve)
769 call get_vec(ve_dot_grad_ve, igrid,xpart(1:ndir),particle_time,vedotgradve)
770
771 drift1(1:ndir) = bhat(1:ndir)/(absb/kappa**2)
772 drift2(1:ndir) = upart(2)/(upart(3)*qpart)*gradkappab(1:ndir)
773
774 call cross(drift1,drift2,gradbdrift)
775 gradbdrift_abs = sqrt(sum(gradbdrift(:)**2))
776
777 drift3(1:ndir) = upar*epar/upart(3)*ve(1:ndir)
778 call cross(drift1,drift3,reldrift)
779 reldrift_abs = sqrt(sum(reldrift(:)**2))
780
781 drift4(1:ndir) = mpart/qpart* ( upar**2/upart(3)*bdotgradb(1:ndir))
782 call cross(drift1,drift4,bdotgradbdrift)
783 bdotgradbdrift_abs = sqrt(sum(bdotgradbdrift(:)**2))
784
785 drift5(1:ndir) = mpart/qpart* ( upar*vedotgradb(1:ndir))
786 call cross(drift1,drift5,vedotgradbdrift)
787 vedotgradbdrift_abs = sqrt(sum(vedotgradbdrift(:)**2))
788
789 drift6(1:ndir) = mpart/qpart* ( upar*bdotgradve(1:ndir))
790 call cross(drift1,drift6,bdotgradvedrift)
791 bdotgradvedrift_abs = sqrt(sum(bdotgradvedrift(:)**2))
792
793 drift7(1:ndir) = mpart/qpart* (upart(3)*vedotgradve(1:ndir))
794 call cross(drift1,drift7,vedotgradvedrift)
795 vedotgradvedrift_abs = sqrt(sum(vedotgradvedrift(:)**2))
796
797 momentumpar1 = qpart/mpart*epar
798 momentumpar2 = -(upart(2)/mpart/upart(3))*sum(bhat(:)*gradkappab(:))
799 momentumpar3 = upar*sum(ve(:)*bdotgradb(:))
800 momentumpar4 = upart(3)*sum(ve(:)*vedotgradb(:))
801
802 ! Payload update
803 if (mynpayload > 0) then
804 ! current gyroradius
805 mypayload(1) = sqrt(2.0d0*mpart*upart(2)*absb)/abs(qpart*absb)
806 end if
807 if (mynpayload > 1) then
808 ! pitch angle
809 mypayload(2) = atan2(sqrt((2.0d0*upart(2)*absb)/(mpart*upart(3)**2)),vpar)
810 end if
811 if (mynpayload > 2) then
812 ! particle v_perp
813 mypayload(3) = sqrt((2.0d0*upart(2)*absb)/(mpart*upart(3)**2))
814 end if
815 if (mynpayload > 3) then
816 ! particle parallel momentum term 1
817 mypayload(4) = momentumpar1
818 end if
819 if (mynpayload > 4) then
820 ! particle parallel momentum term 2
821 mypayload(5) = momentumpar2
822 end if
823 if (mynpayload > 5) then
824 ! particle parallel momentum term 3
825 mypayload(6) = momentumpar3
826 end if
827 if (mynpayload > 6) then
828 ! particle parallel momentum term 4
829 mypayload(7) = momentumpar4
830 end if
831 if (mynpayload > 7) then
832 ! particle ExB drift
833 mypayload(8) = veabs
834 end if
835 if (mynpayload > 8) then
836 ! relativistic drift
837 mypayload(9) = gradbdrift_abs
838 end if
839 if (mynpayload > 9) then
840 ! gradB drift
841 mypayload(10) = reldrift_abs
842 end if
843 if (mynpayload > 10) then
844 ! bdotgradb drift
845 mypayload(11) = bdotgradbdrift_abs
846 end if
847 if (mynpayload > 11) then
848 ! vEdotgradb drift
849 mypayload(12) = vedotgradbdrift_abs
850 end if
851 if (mynpayload > 12) then
852 ! bdotgradvE drift
853 mypayload(13) = bdotgradvedrift_abs
854 end if
855 if (mynpayload > 13) then
856 ! vEdotgradvE drift
857 mypayload(14) = vedotgradvedrift_abs
858 end if
859
860 end subroutine gca_update_payload
861
862 function gca_get_particle_dt(partp, end_time) result(dt_p)
863 use mod_odeint
865 type(particle_ptr), intent(in) :: partp
866 double precision, intent(in) :: end_time
867 double precision :: dt_p
868
869 double precision :: tout, dt_particles_mype, dt_cfl0, dt_cfl1, dt_a
870 double precision :: dxmin, vp, a, gammap
871 double precision :: v(ndir), y(ndir+2),ytmp(ndir+2), dydt(ndir+2), v0(ndir), v1(ndir), dydt1(ndir+2)
872 double precision :: ap0, ap1, dt_cfl_ap0, dt_cfl_ap1, dt_cfl_ap
873 double precision :: dt_euler, dt_tmp
874 ! make these particle cfl conditions more restrictive if you are interpolating out of the grid
875 double precision :: cfl, uparcfl
876 double precision, parameter :: uparmin=1.0d-6*const_c
877 integer :: ipart, iipart, nout, ic^d, igrid_particle, ipe_particle, ipe
878 logical :: bc_applied
879
880 if (const_dt_particles > 0) then
881 dt_p = const_dt_particles
882 return
883 end if
884
885 cfl = particles_cfl
886 uparcfl = particles_cfl
887
888 igrid_working = partp%igrid
889 ipart_working = partp%self%index
890 dt_tmp = (end_time - partp%self%time)
891 if(dt_tmp .le. 0.0d0) dt_tmp = smalldouble
892 ! make sure we step only one cell at a time, first check CFL at current location
893 ! then we make an Euler step to the new location and check the new CFL
894 ! we simply take the minimum of the two timesteps.
895 ! added safety factor cfl:
896 dxmin = min({rnode(rpdx^d_,igrid_working)},bigdouble)*cfl
897 ! initial solution vector:
898 y(1:ndir) = partp%self%x(1:ndir) ! position of guiding center
899 y(ndir+1) = partp%self%u(1) ! parallel momentum component (gamma v||)
900 y(ndir+2) = partp%self%u(2) ! conserved magnetic moment
901 ytmp=y
902 !y(ndir+3) = partp%self%u(3) ! Lorentz factor of guiding centre
903
904 call derivs_gca(partp%self%time,y,dydt)
905 v0(1:ndir) = dydt(1:ndir)
906 ap0 = dydt(ndir+1)
907
908 ! guiding center velocity:
909 v(1:ndir) = abs(dydt(1:ndir))
910 vp = sqrt(sum(v(:)**2))
911
912 dt_cfl0 = dxmin / max(vp, smalldouble)
913 dt_cfl_ap0 = uparcfl * abs(max(abs(y(ndir+1)),uparmin) / max(abs(ap0), smalldouble))
914 !dt_cfl_ap0 = min(dt_cfl_ap0, uparcfl * sqrt(abs(unit_length*dxmin/(ap0+smalldouble))) )
915
916 ! make an Euler step with the proposed timestep:
917 ! new solution vector:
918 dt_euler = min(dt_tmp,dt_cfl0,dt_cfl_ap0)
919 y(1:ndir+2) = y(1:ndir+2) + dt_euler * dydt(1:ndir+2)
920
921 partp%self%x(1:ndir) = y(1:ndir) ! position of guiding center
922 partp%self%u(1) = y(ndir+1) ! parallel momentum component (gamma v||)
923 partp%self%u(2) = y(ndir+2) ! conserved magnetic moment
924
925 ! first check if the particle is outside the physical domain or in the ghost cells
927 y(1:ndir+2) = ytmp(1:ndir+2)
928 end if
929
930 call derivs_gca_rk(partp%self%time+dt_euler,y,dydt)
931 !call derivs_gca(partp%self%time+dt_euler,y,dydt)
932
933 v1(1:ndir) = dydt(1:ndir)
934 ap1 = dydt(ndir+1)
935
936 ! guiding center velocity:
937 v(1:ndir) = abs(dydt(1:ndir))
938 vp = sqrt(sum(v(:)**2))
939
940 dt_cfl1 = dxmin / max(vp, smalldouble)
941 dt_cfl_ap1 = uparcfl * abs(max(abs(y(ndir+1)),uparmin) / max(abs(ap1), smalldouble))
942 !dt_cfl_ap1 = min(dt_cfl_ap1, uparcfl * sqrt(abs(unit_length*dxmin/(ap1+smalldouble))) )
943
944 dt_tmp = min(dt_euler, dt_cfl1, dt_cfl_ap1)
945
946 partp%self%x(1:ndir) = ytmp(1:ndir) ! position of guiding center
947 partp%self%u(1) = ytmp(ndir+1) ! parallel momentum component (gamma v||)
948 partp%self%u(2) = ytmp(ndir+2) ! conserved magnetic moment
949 !dt_tmp = min(dt_cfl1, dt_cfl_ap1)
950
951 ! time step due to parallel acceleration:
952 ! The standard thing, dt=sqrt(dx/a) where we compute a from d(gamma v||)/dt and d(gamma)/dt
953 ! dt_ap = sqrt(abs(dxmin*unit_length*y(ndir+3)/( dydt(ndir+1) - y(ndir+1)/y(ndir+3)*dydt(ndir+3) ) ) )
954 ! vp = sqrt(sum(v(1:ndir)**))
955 ! gammap = sqrt(1.0d0/(1.0d0-(vp/const_c)**2))
956 ! ap = const_c**2/vp*gammap**(-3)*dydt(ndir+3)
957 ! dt_ap = sqrt(dxmin*unit_length/ap)
958
959 !dt_a = bigdouble
960 !if (dt_euler .gt. smalldouble) then
961 ! a = sqrt(sum((v1(1:ndir)-v0(1:ndir))**2))/dt_euler
962 ! dt_a = min(sqrt(dxmin/a),bigdouble)
963 !end if
964
965 !dt_p = min(dt_tmp , dt_a)
966 dt_p = dt_tmp
967
968 ! Make sure we do not advance beyond end_time
969 call limit_dt_endtime(end_time - partp%self%time, dt_p)
970
971 end function gca_get_particle_dt
972
973end module mod_particle_gca
Module with geometry-related routines (e.g., divergence, curl)
Definition mod_geometry.t:2
subroutine gradient(q, ixil, ixol, idir, gradq, nth_in)
This module contains definitions of global parameters and variables and some generic functions/subrou...
integer ixghi
Upper index of grid block arrays.
double precision global_time
The global simulation time.
integer, parameter ndim
Number of spatial dimensions for grid variables.
integer icomm
The MPI communicator.
integer mype
The rank of the current MPI task.
double precision, dimension(:), allocatable, parameter d
integer ndir
Number of spatial dimensions (components) for vector variables.
integer ierrmpi
A global MPI error return code.
double precision c_norm
Normalised speed of light.
double precision, dimension(:,:), allocatable rnode
Corner coordinates.
integer nghostcells
Number of ghost cells surrounding a grid.
integer, parameter ixglo
Lower index of grid block arrays (always 1)
This module packages odeint from numerical recipes.
Definition mod_odeint.t:2
subroutine odeint(ystart, nvar, x1, x2, eps, h1, hmin, nok, nbad, derivs, rkqs, ierror)
Definition mod_odeint.t:13
subroutine rkqs(y, dydx, n, x, htry, eps, yscal, hdid, hnext, derivs)
Definition mod_odeint.t:116
Module with shared functionality for all the particle movers.
pure subroutine limit_dt_endtime(t_left, dt_p)
integer num_particles
Number of particles.
double precision const_dt_particles
If positive, a constant time step for the particles.
logical function particle_in_igrid(ipart, igrid)
Quick check if particle is still in igrid.
integer ngridvars
Number of variables for grid field.
subroutine get_efield(igrid, x, tloc, e)
integer nusrpayload
Number of user-defined payload variables for a particle.
subroutine find_particle_ipe(x, igrid_particle, ipe_particle)
pure subroutine get_lfac_from_velocity(v, lfac)
Get Lorentz factor from velocity.
integer integrator
Integrator to be used for particles.
integer, dimension(:), allocatable ep
Variable index for electric field.
subroutine fill_gridvars_default
This routine fills the particle grid variables with the default for mhd, i.e. only E and B.
subroutine get_bfield(igrid, x, tloc, b)
integer npayload
Number of total payload variables for a particle.
integer, dimension(:), allocatable particles_active_on_mype
Array of identity numbers of active particles in current processor.
logical relativistic
Use a relativistic particle mover?
character(len=name_len) integrator_type_particles
String describing the particle integrator type.
subroutine get_vec(ix, igrid, x, tloc, vec)
subroutine push_particle_into_particles_on_mype(ipart)
integer nparticles_active_on_mype
Number of active particles in current processor.
procedure(sub_define_additional_gridvars), pointer particles_define_additional_gridvars
integer nparticles
Identity number and total number of particles.
integer, dimension(:), allocatable bp
Variable index for magnetic field.
type(particle_ptr), dimension(:), allocatable particle
integer, dimension(:), allocatable vp
Variable index for fluid velocity.
procedure(sub_integrate), pointer particles_integrate
subroutine cross(a, b, c)
procedure(sub_fill_gridvars), pointer particles_fill_gridvars
integer ndefpayload
Number of default payload variables for a particle.
integer igrid_working
set the current igrid for the particle integrator:
integer, dimension(:), allocatable jp
Variable index for current.
integer ipart_working
set the current ipart for the particle integrator:
double precision particles_cfl
Particle CFL safety factor.
integer rhop
Variable index for density.
Particle mover with Newtonian/relativistic Guiding Center Approximation (GCA) By Jannis Teunissen,...
integer, dimension(:), allocatable, public, protected b_dot_grad_b
Variable index for (B . grad)B (curvature B drift)
subroutine, public gca_init()
subroutine, public gca_create_particles()
integer, dimension(:), allocatable, public, protected b_dot_grad_ve
Variable index for polarization drift.
integer, dimension(:), allocatable, public, protected ve_dot_grad_ve
Variable index for polarization drift.
integer, dimension(:), allocatable, public, protected grad_kappa_b
Variable index for gradient B, with relativistic correction 1/kappa where kappa = 1/sqrt(1 - E_perp^2...
integer, dimension(:), allocatable, public, protected ve_dot_grad_b
Variable index for curvature drift.
Module with all the methods that users can customize in AMRVAC.
procedure(check_particle), pointer usr_check_particle
procedure(create_particles), pointer usr_create_particles
procedure(update_payload), pointer usr_update_payload
procedure(particle_fields), pointer usr_particle_fields