MPI-AMRVAC  3.0
The MPI - Adaptive Mesh Refinement - Versatile Advection Code
mod_particle_lorentz.t
Go to the documentation of this file.
1 !> Particle mover with Newtonian/relativistic Boris scheme for Lorentz dynamics
2 !> By Jannis Teunissen, Bart Ripperda, Oliver Porth, and Fabio Bacchini (2016-2020)
5 
6  private
7 
8  public :: lorentz_init
10  integer, parameter :: Boris=1, vay=2, hc=3, lm=4
11 
12  ! Variables
13  public :: bp, ep, vp, jp
14 
15 contains
16 
17  subroutine lorentz_init()
19  integer :: idir, nwx
20 
21  if(physics_type/='mhd') call mpistop("Lorentz particles need magnetic field!")
22  if(ndir/=3) call mpistop("Lorentz particles need ndir=3!")
23 
24  ! The first 6 gridvars are always B and E
25  ngridvars = ndir*2
26  nwx = 0
27 
28  allocate(bp(ndir))
29  do idir = 1, ndir
30  nwx = nwx + 1
31  bp(idir) = nwx
32  end do
33  allocate(ep(ndir))
34  do idir = 1, ndir
35  nwx = nwx + 1
36  ep(idir) = nwx
37  end do
38  allocate(vp(ndir))
39  do idir = 1, ndir
40  nwx = nwx + 1
41  vp(idir) = nwx
42  end do
43  allocate(jp(ndir))
44  do idir = 1, ndir
45  nwx = nwx + 1
46  jp(idir) = nwx
47  end do
48  ngridvars=nwx
49 
51 
52  if (associated(particles_define_additional_gridvars)) then
54  end if
55 
56  select case(integrator_type_particles)
57  case('Boris','boris')
58  integrator = boris
59  case('Vay','vay')
60  integrator = vay
61  case('HC','hc','higueracary')
62  integrator = hc
63  case('LM','lm','lapentamarkidis')
64  integrator = lm
65  end select
66 
68 
69  end subroutine lorentz_init
70 
72 
75 
76  integer :: n, idir, igrid, ipe_particle, nparticles_local
77  double precision :: lfac
78  double precision :: x(3, num_particles)
79  double precision :: v(3, num_particles)
80  double precision :: q(num_particles)
81  double precision :: m(num_particles)
82  double precision :: rrd(num_particles,ndir)
83  double precision :: defpayload(ndefpayload)
84  double precision :: usrpayload(nusrpayload)
85  logical :: follow(num_particles), check
86 
87  follow = .false.
88  x = 0.0d0
89 
90  if (mype==0) then
91  if (.not. associated(usr_create_particles)) then
92  ! Randomly distributed
93  do idir=1,ndir
94  do n = 1, num_particles
95  rrd(n,idir) = rng%unif_01()
96  end do
97  end do
98  do n=1, num_particles
99  {^d&x(^d,n) = xprobmin^d + rrd(n+1,^d) * (xprobmax^d - xprobmin^d)\}
100  end do
101  else
102  call usr_create_particles(num_particles, x, v, q, m, follow)
103  end if
104  end if
105 
106  call mpi_bcast(x,3*num_particles,mpi_double_precision,0,icomm,ierrmpi)
107  call mpi_bcast(v,3*num_particles,mpi_double_precision,0,icomm,ierrmpi)
108  call mpi_bcast(q,num_particles,mpi_double_precision,0,icomm,ierrmpi)
109  call mpi_bcast(m,num_particles,mpi_double_precision,0,icomm,ierrmpi)
110  call mpi_bcast(follow,num_particles,mpi_logical,0,icomm,ierrmpi)
111 
112  nparticles_local = 0
113 
114  ! Find ipe and igrid responsible for particle
115  do n = 1, num_particles
116  call find_particle_ipe(x(:, n),igrid,ipe_particle)
117 
118  particle(n)%igrid = igrid
119  particle(n)%ipe = ipe_particle
120 
121  if (ipe_particle == mype) then
122  check = .true.
123 
124  ! Check for user-defined modifications or rejection conditions
125  if (associated(usr_check_particle)) call usr_check_particle(igrid, x(:,n), v(:,n), q(n), m(n), follow(n), check)
126  if (check) then
128  else
129  cycle
130  end if
131 
132  nparticles_local = nparticles_local + 1
133 
134  call get_lfac_from_velocity(v(:,n), lfac)
135 
136  allocate(particle(n)%self)
137  particle(n)%self%x = x(:,n)
138  particle(n)%self%u = v(:,n) * lfac
139  particle(n)%self%q = q(n)
140  particle(n)%self%m = m(n)
141  particle(n)%self%follow = follow(n)
142  particle(n)%self%index = n
143  particle(n)%self%time = global_time
144  particle(n)%self%dt = 0.0d0
145 
146  ! initialise payloads for Lorentz module
147  allocate(particle(n)%payload(npayload))
148  call lorentz_update_payload(igrid,ps(igrid)%w,pso(igrid)%w,ps(igrid)%x,x(:,n),v(:,n)*lfac,q(n),m(n),defpayload,ndefpayload,0.d0)
149  particle(n)%payload(1:ndefpayload) = defpayload
150  if (associated(usr_update_payload)) then
151  call usr_update_payload(igrid,ps(igrid)%w,pso(igrid)%w,ps(igrid)%x,x(:,n),v(:,n)*lfac,q(n),m(n),usrpayload,nusrpayload,0.d0)
152  particle(n)%payload(ndefpayload+1:npayload) = usrpayload
153  end if
154  end if
155  end do
156 
157  call mpi_allreduce(nparticles_local,nparticles,1,mpi_integer,mpi_sum,icomm,ierrmpi)
158 
159  end subroutine lorentz_create_particles
160 
164  use mod_geometry
165 
166  integer :: igrid, iigrid, idir, idim
167  double precision, dimension(ixG^T,1:ndir) :: beta
168  double precision, dimension(ixG^T,1:nw) :: w,wold
169  double precision :: current(ixG^T,7-2*ndir:3)
170  integer :: idirmin
171  double precision, dimension(ixG^T,1:ndir) :: vE, bhat
172  double precision, dimension(ixG^T) :: kappa, kappa_B, absB, tmp
173 
174  call fill_gridvars_default()
175 
176  do iigrid=1,igridstail; igrid=igrids(iigrid);
177  w(ixg^t,1:nw) = ps(igrid)%w(ixg^t,1:nw)
178  call phys_to_primitive(ixg^ll,ixg^ll,w,ps(igrid)%x)
179  ! fill with velocity:
180  gridvars(igrid)%w(ixg^t,vp(:)) = w(ixg^t,iw_mom(:))
181 
182  if(time_advance) then
183  ! Fluid velocity
184  w(ixg^t,1:nw) = pso(igrid)%w(ixg^t,1:nw)
185  call phys_to_primitive(ixg^ll,ixg^ll,w,ps(igrid)%x)
186  gridvars(igrid)%wold(ixg^t,vp(:)) = w(ixg^t,iw_mom(:))
187  end if
188  end do
189 
190  end subroutine lorentz_fill_gridvars
191 
192  !> Relativistic particle integrator
193  subroutine lorentz_integrate_particles(end_time)
195  use mod_geometry
197  double precision, intent(in) :: end_time
198  double precision :: defpayload(ndefpayload)
199  double precision :: usrpayload(nusrpayload)
200  integer :: ipart, iipart
201  double precision :: lfac, q, m, dt_p, cosphi, sinphi, phi1, phi2, r, re
202  double precision, dimension(ndir) :: b, e, emom, uminus, t_geom, s, udash, tmp, uplus, xcart1, xcart2, ucart2, radmom, vfluid, current
203 
204  do iipart=1,nparticles_active_on_mype
205  ipart = particles_active_on_mype(iipart);
206  q = particle(ipart)%self%q
207  m = particle(ipart)%self%m
208 
209  dt_p = lorentz_get_particle_dt(particle(ipart), end_time)
210  particle(ipart)%self%dt = dt_p
211 
212  ! Push particle over half time step
213  call get_lfac(particle(ipart)%self%u,lfac)
214  particle(ipart)%self%x(1:ndir) = particle(ipart)%self%x(1:ndir) &
215  + 0.5d0 * dt_p * particle(ipart)%self%u(1:ndir)/lfac
216 
217  ! Get E, B at new position
218  call get_vec(bp, particle(ipart)%igrid, &
219  particle(ipart)%self%x,particle(ipart)%self%time,b)
220  call get_vec(vp, particle(ipart)%igrid, &
221  particle(ipart)%self%x,particle(ipart)%self%time,vfluid)
222  call get_vec(jp, particle(ipart)%igrid, &
223  particle(ipart)%self%x,particle(ipart)%self%time,current)
224  e(1) = -vfluid(2)*b(3)+vfluid(3)*b(2) + particles_eta*current(1)
225  e(2) = vfluid(1)*b(3)-vfluid(3)*b(1) + particles_eta*current(2)
226  e(3) = -vfluid(1)*b(2)+vfluid(2)*b(1) + particles_eta*current(3)
227 
228  ! 'Kick' particle (update velocity) based on the chosen integrator
229  call lorentz_kick(particle(ipart)%self%x,particle(ipart)%self%u,e,b,q,m,dt_p)
230 
231  ! Push particle over half time step at end
232  call get_lfac(particle(ipart)%self%u,lfac)
233  particle(ipart)%self%x(1:ndir) = particle(ipart)%self%x(1:ndir) &
234  + 0.5d0 * dt_p * particle(ipart)%self%u(1:ndir)/lfac
235 
236  ! Time update
237  particle(ipart)%self%time = particle(ipart)%self%time + dt_p
238 
239  ! Update payload
240  call lorentz_update_payload(particle(ipart)%igrid,ps(particle(ipart)%igrid)%w,pso(particle(ipart)%igrid)%w,ps(particle(ipart)%igrid)%x, &
241  particle(ipart)%self%x,particle(ipart)%self%u,q,m,defpayload,ndefpayload,particle(ipart)%self%time)
242  particle(ipart)%payload(1:ndefpayload) = defpayload
243  if (associated(usr_update_payload)) then
244  call usr_update_payload(particle(ipart)%igrid,ps(particle(ipart)%igrid)%w,pso(particle(ipart)%igrid)%w,ps(particle(ipart)%igrid)%x,&
245  particle(ipart)%self%x,particle(ipart)%self%u,q,m,usrpayload,nusrpayload,particle(ipart)%self%time)
246  particle(ipart)%payload(ndefpayload+1:npayload) = usrpayload
247  end if
248 
249  end do ! ipart loop
250 
251  end subroutine lorentz_integrate_particles
252 
253  !> Momentum update subroutine for full Lorentz dynamics
254  subroutine lorentz_kick(xpart,upart,e,b,q,m,dtp)
256  use mod_geometry
257  double precision, intent(in) :: e(ndir), b(ndir), q, m, dtp
258  double precision, intent(inout) :: xpart(ndim), upart(ndir)
259  double precision :: lfac, cosphi, sinphi, phi1, phi2, r, re, sigma
260  double precision, dimension(ndir) :: emom, uprime, tau, s, tmp, uplus, xcart1, xcart2, ucart2, radmom
261  double precision, dimension(ndir) :: upartk, vbar, Fk, C1, C2, dupartk
262  double precision :: abserr, tol, lfack, J11, J12, J13, J21, J22, J23, J31, J32, J33
263  double precision :: iJ11, iJ12, iJ13, iJ21, iJ22, iJ23, iJ31, iJ32, iJ33, Det
264  integer :: nk, nkmax
265 
266  ! Perform momentum update based on the chosen integrator
267  select case(integrator)
268 
269  ! Boris integrator (works in Cartesian and cylindrical)
270  case(boris)
271 
272  select case(coordinate)
273 
274  ! CARTESIAN COORDINATES
275  case(cartesian)!,Cartesian_stretched)
276  ! TODO: Adjust and document Cartesian_stretched
277 
278  ! Momentum update
279  emom = q * e * dtp /(2.0d0 * m)
280  !!!!!! TODO: Adjust and document losses
281 ! if(losses) then
282 ! call get_lfac(particle(ipart)%self%u,lfac)
283 ! re = abs(q)**2 / (m * const_c**2)
284 ! call cross(particle(ipart)%self%u,b,tmp)
285 ! radmom = - third * re**2 * lfac &
286 ! * ( sum((e(:)+tmp(:)/lfac)**2) &
287 ! - (sum(e(:)*particle(ipart)%self%u(:))/lfac)**2 ) &
288 ! * particle(ipart)%self%u / m / const_c * dt_p
289 ! else
290  radmom = 0.0d0
291 ! end if
292 
293  uprime = upart + emom + radmom
294 
295  call get_lfac(uprime,lfac)
296  tau = q * b * dtp / (2.0d0 * lfac * m)
297  s = 2.0d0 * tau / (1.0d0+sum(tau(:)**2))
298 
299  call cross(uprime,tau,tmp)
300  call cross(uprime+tmp,s,tmp)
301  uplus = uprime + tmp
302 
303  !!!!!! TODO: Adjust and document losses
304 ! if(losses) then
305 ! call cross(uplus,b,tmp)
306 ! radmom = - third * re**2 * lfac &
307 ! * ( sum((e(:)+tmp(:)/lfac)**2) &
308 ! - (sum(e(:)*uplus(:))/lfac)**2 ) &
309 ! * uplus / m / const_c * dt_p
310 ! else
311  radmom = 0.0d0
312 ! end if
313 
314  ! Update momentum
315  upart = uplus + emom + radmom
316 
317  ! CYLINDRICAL COORDINATES
318  case (cylindrical)
319 
320  ! Momentum update
321  emom = q * e * dtp /(2.0d0 * m)
322 
323  !!!!!! TODO: Adjust and document losses
324 ! if(losses) then
325 ! call get_lfac(particle(ipart)%self%u,lfac)
326 ! re = abs(q)**2 / (m * const_c**2)
327 ! call cross(particle(ipart)%self%u,b,tmp)
328 ! radmom = - third * re**2 * lfac &
329 ! * ( sum((e(:)+tmp(:)/lfac)**2) &
330 ! - (sum(e(:)*particle(ipart)%self%u(:))/lfac)**2 ) &
331 ! * particle(ipart)%self%u / m / const_c * dt_p
332 ! else
333  radmom = 0.0d0
334 ! end if
335 
336  uprime = upart + emom + radmom
337 
338  call get_lfac(uprime,lfac)
339  tau = q * b * dtp / (2.0d0 * lfac * m)
340  s = 2.0d0 * tau / (1.0d0+sum(tau(:)**2))
341 
342  call cross(uprime,tau,tmp)
343  call cross(uprime+tmp,s,tmp)
344  uplus = uprime + tmp
345 
346  !!!!!! TODO: Adjust and document losses
347 ! if(losses) then
348 ! call cross(uplus,b,tmp)
349 ! radmom = - third * re**2 * lfac &
350 ! * ( sum((e(:)+tmp(:)/lfac)**2) &
351 ! - (sum(e(:)*uplus(:))/lfac)**2 ) &
352 ! * uplus / m / const_c * dt_p
353 ! else
354  radmom = 0.0d0
355 ! end if
356 
357  upart = uplus + emom + radmom
358  ! Position update
359  ! Get cartesian coordinates:
360  phi1 = xpart(phi_)
361  cosphi = cos(phi1)
362  sinphi = sin(phi1)
363 
364  xcart1(1) = xpart(r_) * cosphi
365  xcart1(2) = xpart(r_) * sinphi
366  xcart1(3) = xpart(z_)
367 
368  ucart2(1) = cosphi * upart(r_) - sinphi * upart(phi_)
369  ucart2(2) = cosphi * upart(phi_) + sinphi * upart(r_)
370  ucart2(3) = upart(z_)
371 
372  ! update position
373  xcart2(1:ndir) = xcart1(1:ndir) &
374  + dtp * ucart2(1:ndir)/lfac
375 
376  ! back to cylindrical coordinates
377  phi2 = atan2(xcart2(2),xcart2(1))
378  if(phi2 .lt. 0.0d0) phi2 = 2.0d0*dpi + phi2
379  r = sqrt(xcart2(1)**2 + xcart2(2)**2)
380  xpart(r_) = r
381  xpart(phi_) = phi2
382  xpart(z_) = xcart2(3)
383 
384  ! Rotate the momentum to the new cooridnates
385  ! rotate velocities
386  cosphi = cos(phi2-phi1)
387  sinphi = sin(phi2-phi1)
388 
389  tmp = upart
390  upart(r_) = cosphi * tmp(r_) + sinphi * tmp(phi_)
391  upart(phi_) = cosphi * tmp(phi_) - sinphi * tmp(r_)
392  upart(z_) = tmp(z_)
393 
394  case default
395  call mpistop("Boris particle pusher incompatible with the chosen geometry")
396  end select
397 
398  ! Vay integrator (works in Cartesian)
399  case(vay)
400 
401  select case(coordinate)
402 
403  ! CARTESIAN COORDINATES
404  case(cartesian)!,Cartesian_stretched)
405  ! TODO: Adjust and document Cartesian_stretched
406 
407  call get_lfac(upart,lfac)
408  emom = q * e * dtp /(2.0d0 * m)
409  tau = q * b * dtp / (2.0d0 * m)
410 
411  call cross(upart,tau,tmp)
412  uprime = upart + 2.d0*emom + tmp/lfac
413 
414  call get_lfac(uprime,lfac)
415  sigma = lfac**2 - sum(tau(:)*tau(:))
416  lfac = sqrt((sigma + sqrt(sigma**2 &
417  + 4.d0 * (sum(tau(:)*tau(:)) + sum(uprime(:)*tau(:)/c_norm)**2))) / 2.d0)
418 
419  call cross(uprime,tau,tmp)
420  upart = (uprime + sum(uprime(:)*tau(:))*tau/lfac**2 + tmp/lfac) / (1.d0+sum(tau(:)*tau(:))/lfac**2)
421 
422  case default
423  call mpistop("Vay particle pusher incompatible with the chosen geometry")
424  end select
425 
426  ! Higuera-Cary integrator (works in Cartesian)
427  case(hc)
428 
429  select case(coordinate)
430 
431  ! CARTESIAN COORDINATES
432  case(cartesian)!,Cartesian_stretched)
433  ! TODO: Adjust and document Cartesian_stretched
434 
435  call get_lfac(upart,lfac)
436  emom = q * e * dtp /(2.0d0 * m)
437  tau = q * b * dtp / (2.0d0 * m)
438  uprime = upart + emom
439 
440  call get_lfac(uprime,lfac)
441  sigma = lfac**2 - sum(tau(:)*tau(:))
442  lfac = sqrt((sigma + sqrt(sigma**2 &
443  + 4.d0 * (sum(tau(:)*tau(:)) + sum(uprime(:)*tau(:)/c_norm)**2))) / 2.d0)
444 
445  call cross(uprime,tau,tmp)
446  upart = (uprime + sum(uprime(:)*tau(:))*tau/lfac**2 + tmp/lfac) / (1.d0+sum(tau(:)*tau(:))/lfac**2) &
447  + emom + tmp/lfac
448 
449  case default
450  call mpistop("Higuera-Cary particle pusher incompatible with the chosen geometry")
451  end select
452 
453  ! Lapenta-Markidis integrator (works in Cartesian)
454  case(lm)
455 
456  select case(coordinate)
457 
458  ! CARTESIAN COORDINATES
459  case(cartesian)!,Cartesian_stretched)
460  ! TODO: Adjust and document Cartesian_stretched
461 
462  ! Initialise iteration quantities
463  call get_lfac(upart,lfac)
464  upartk = upart
465 
466  ! START OF THE NONLINEAR CYCLE
467  abserr = 1.d0
468  tol=1.d-14
469  nkmax=10
470  nk=0
471  do while(abserr > tol .and. nk < nkmax)
472 
473  nk=nk+1
474 
475  call get_lfac(upartk,lfack)
476  vbar = (upart + upartk) / (lfac + lfack)
477  call cross(vbar,b,tmp)
478 
479  ! Compute residual vector
480  fk = upartk - upart - q*dtp/m * (e + tmp)
481 
482  ! Compute auxiliary coefficients
483  c1 = (lfack + lfac - upartk(1:ndim) / lfack / c_norm**2 * (upartk + upart)) / (lfack + lfac)**2
484  c2 = - upartk / lfack / c_norm**2 / (lfack + lfac)**2
485 
486  ! Compute Jacobian
487  j11 = 1. - q*dtp/m * (c2(1) * (upartk(2) + upart(2)) * b(3) - c2(1) * (upartk(3) + upart(3)) * b(2))
488  j12 = - q*dtp/m * (c1(2) * b(3) - c2(2) * (upartk(3) + upart(3)) * b(2))
489  j13 = - q*dtp/m * (c2(3) * (upartk(2) + upart(2)) * b(3) - c1(3) * b(2))
490  j21 = - q*dtp/m * (- c1(1) * b(3) + c2(1) * (upartk(3) + upart(3)) * b(1))
491  j22 = 1. - q*dtp/m * (- c2(2) * (upartk(1) + upart(1)) * b(3) + c2(2) * (upartk(3) + upart(3)) * b(1))
492  j23 = - q*dtp/m * (- c2(3) * (upartk(1) + upart(1)) * b(3) + c1(3) * b(1))
493  j31 = - q*dtp/m * (c1(1) * b(2) - c2(1) * (upartk(2) + upart(2)) * b(1))
494  j32 = - q*dtp/m * (c2(2) * (upartk(1) + upart(1)) * b(2) - c1(2) * b(1))
495  j33 = 1. - q*dtp/m * (c2(3) * (upartk(1) + upart(1)) * b(2) - c2(3) * (upartk(2) + upart(2)) * b(1))
496 
497  ! Compute inverse Jacobian
498  det = j11*j22*j33 + j21*j32*j13 + j31*j12*j23 - j11*j32*j23 - j31*j22*j13 - j21*j12*j33
499  ij11 = (j22*j33 - j23*j32) / det
500  ij12 = (j13*j32 - j12*j33) / det
501  ij13 = (j12*j23 - j13*j22) / det
502  ij21 = (j23*j31 - j21*j33) / det
503  ij22 = (j11*j33 - j13*j31) / det
504  ij23 = (j13*j21 - j11*j23) / det
505  ij31 = (j21*j32 - j22*j31) / det
506  ij32 = (j12*j31 - j11*j32) / det
507  ij33 = (j11*j22 - j12*j21) / det
508 
509  ! Compute new upartk = upartk - J^(-1) * F(upartk)
510  dupartk(1) = - (ij11 * fk(1) + ij12 * fk(2) + ij13 * fk(3))
511  dupartk(2) = - (ij21 * fk(1) + ij22 * fk(2) + ij23 * fk(3))
512  dupartk(3) = - (ij31 * fk(1) + ij32 * fk(2) + ij33 * fk(3))
513 
514  ! Check convergence
515  upartk=upartk+dupartk
516  abserr=sqrt(sum(dupartk(:)*dupartk(:)))
517 
518  end do
519  ! END OF THE NONLINEAR CYCLE
520 
521  ! Update velocity
522  upart = upartk
523 
524  case default
525  call mpistop("Lapenta-Markidis particle pusher incompatible with the chosen geometry")
526  end select
527 
528  end select
529 
530  end subroutine lorentz_kick
531 
532  !> Update payload subroutine
533  subroutine lorentz_update_payload(igrid,w,wold,xgrid,xpart,upart,qpart,mpart,mypayload,mynpayload,particle_time)
535  integer, intent(in) :: igrid,mynpayload
536  double precision, intent(in) :: w(ixG^T,1:nw),wold(ixG^T,1:nw)
537  double precision, intent(in) :: xgrid(ixG^T,1:ndim),xpart(1:ndir),upart(1:ndir),qpart,mpart,particle_time
538  double precision, intent(out) :: mypayload(mynpayload)
539  double precision :: b(3), e(3), tmp(3), lfac, vfluid(3), current(3)
540 
541  call get_vec(bp, igrid, xpart,particle_time,b)
542  call get_vec(vp, igrid, xpart,particle_time,vfluid)
543  call get_vec(jp, igrid, xpart,particle_time,current)
544  e(1) = -vfluid(2)*b(3)+vfluid(3)*b(2) + particles_eta*current(1)
545  e(2) = vfluid(1)*b(3)-vfluid(3)*b(1) + particles_eta*current(2)
546  e(3) = -vfluid(1)*b(2)+vfluid(2)*b(1) + particles_eta*current(3)
547 
548  ! Payload update
549  ! Lorentz factor
550  if (mynpayload > 0) then
551  call get_lfac(upart,lfac)
552  mypayload(1) = lfac
553  end if
554 
555  ! current gyroradius
556  if (mynpayload > 1) then
557  call cross(upart,b,tmp)
558  tmp = tmp / sqrt(sum(b(:)**2))
559  mypayload(2) = mpart/abs(qpart)*sqrt(sum(tmp(:)**2)) / sqrt(sum(b(:)**2))
560  end if
561 
562  ! magnetic moment
563  if (mynpayload > 2) then
564  mypayload(3) = mpart*sum(tmp(:)**2)/(2.0d0*sqrt(sum(b(:)**2)))
565  end if
566 
567  ! e.b
568  if (mynpayload > 3) then
569  mypayload(4) = sum(e(:)*b(:))
570  end if
571 
572  end subroutine lorentz_update_payload
573 
574  function lorentz_get_particle_dt(partp, end_time) result(dt_p)
576  use mod_geometry
577  type(particle_ptr), intent(in) :: partp
578  double precision, intent(in) :: end_time
579  double precision :: dt_p
580  integer :: ipart, iipart, nout
581  double precision,dimension(ndir) :: b,v
582  double precision :: lfac,absb,dt_cfl
583  double precision :: tout
584  double precision, parameter :: cfl = 0.5d0
585 
586  if (const_dt_particles > 0) then
587  dt_p = const_dt_particles
588  return
589  end if
590 
591  call get_vec(bp, partp%igrid,partp%self%x,partp%self%time,b)
592  absb = sqrt(sum(b(:)**2))
593  call get_lfac(partp%self%u,lfac)
594 
595  ! CFL timestep
596  ! make sure we step only one cell at a time:
597  v(:) = abs(partp%self%u(:) / lfac)
598 
599  ! convert to angular velocity:
600  if(coordinate ==cylindrical.and.phi_>0) then
601  v(phi_) = abs(v(phi_)/partp%self%x(r_))
602  end if
603 
604  dt_cfl = min(bigdouble, &
605  {rnode(rpdx^d_,partp%igrid)/max(v(^d), smalldouble)})
606 
607  if(coordinate ==cylindrical.and.phi_>0) then
608  ! phi-momentum leads to radial velocity:
609  if(phi_ .gt. ndim) dt_cfl = min(dt_cfl, &
610  sqrt(rnode(rpdx1_,partp%igrid)/partp%self%x(r_)) &
611  / v(phi_))
612  ! limit the delta phi of the orbit (just for aesthetic reasons):
613  dt_cfl = min(dt_cfl,0.1d0/max(v(phi_), smalldouble))
614  ! take some care at the axis:
615  dt_cfl = min(dt_cfl,(partp%self%x(r_)+smalldouble)/max(v(r_), smalldouble))
616  end if
617 
618  dt_cfl = dt_cfl * cfl
619 
620  ! bound by gyro-rotation:
621  dt_p = abs( dtheta * partp%self%m * lfac &
622  / (partp%self%q * absb) )
623 
624  dt_p = min(dt_p, dt_cfl)
625 
626  ! Make sure we don't advance beyond end_time
627  call limit_dt_endtime(end_time - partp%self%time, dt_p)
628 
629  end function lorentz_get_particle_dt
630 
631 end module mod_particle_lorentz
subroutine mpistop(message)
Exit MPI-AMRVAC with an error message.
Definition: comm_lib.t:194
Module with geometry-related routines (e.g., divergence, curl)
Definition: mod_geometry.t:2
integer coordinate
Definition: mod_geometry.t:6
integer, parameter cartesian
Definition: mod_geometry.t:7
integer, parameter cylindrical
Definition: mod_geometry.t:9
This module contains definitions of global parameters and variables and some generic functions/subrou...
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.
integer, 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 r_
Indices for cylindrical coordinates FOR TESTS, negative value when not used:
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.
integer ngridvars
Number of variables for grid field.
integer nusrpayload
Number of user-defined payload variables for a particle.
double precision particles_eta
Resistivity.
pure subroutine get_lfac(u, lfac)
Get Lorentz factor from relativistic momentum.
subroutine find_particle_ipe(x, igrid_particle, ipe_particle)
double precision dtheta
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.
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.
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, dimension(:), allocatable jp
Variable index for current.
Particle mover with Newtonian/relativistic Boris scheme for Lorentz dynamics By Jannis Teunissen,...
double precision function lorentz_get_particle_dt(partp, end_time)
subroutine lorentz_kick(xpart, upart, e, b, q, m, dtp)
Momentum update subroutine for full Lorentz dynamics.
subroutine, public lorentz_init()
subroutine lorentz_integrate_particles(end_time)
Relativistic particle integrator.
subroutine lorentz_update_payload(igrid, w, wold, xgrid, xpart, upart, qpart, mpart, mypayload, mynpayload, particle_time)
Update payload subroutine.
subroutine, public lorentz_create_particles()
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