MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_particle_advect.t
Go to the documentation of this file.
1!> Tracer for advected particles moving with fluid flows
2!> By Jannis Teunissen, Bart Ripperda, Oliver Porth, and Fabio Bacchini (2017-2020)
5
6 private
7
8 public :: advect_init
10 integer, parameter :: Euler = 1, rk4=2, ark4=3
11
12 ! Variables
13 public :: vp, rhop
14
15contains
16
17 subroutine advect_init()
19 integer :: idir
20
21 ngridvars=0
22 allocate(vp(ndir))
23 do idir = 1, ndir
24 vp(idir) = idir
25 end do
27 if(ndefpayload>0) then
28 rhop=vp(ndir)+1
30 end if
31
32 particles_fill_gridvars => advect_fill_gridvars
33
34 if (associated(particles_define_additional_gridvars)) then
36 end if
37
38 select case(integrator_type_particles)
39 case('Euler','euler')
40 integrator = euler
41 case('RK4','Rk4','rk4')
43 case('ARK4','ARk4','Ark4','ark4')
44 integrator = ark4
45 case default
46 integrator = ark4
47 end select
48
49 particles_integrate => advect_integrate_particles
50
51 end subroutine advect_init
52
54 ! initialise the particles
57
58 double precision :: x(3, num_particles)
59 double precision :: v(3, num_particles)
60 double precision :: q(num_particles)
61 double precision :: m(num_particles)
62 double precision :: rrd(num_particles,ndir)
63 double precision :: w(ixg^t,1:nw)
64 double precision :: defpayload(ndefpayload)
65 double precision :: usrpayload(nusrpayload)
66 integer :: n, idir, igrid, ipe_particle, nparticles_local
67 logical :: follow(num_particles), check
68
69 follow = .false.
70 x = 0.0d0
71 v = 0.0d0
72 q = 0.0d0
73 m = 1.0d0
74
75 if (mype==0) then
76 if (.not. associated(usr_create_particles)) then
77 ! Randomly distributed
78 do idir=1,ndir
79 do n = 1, num_particles
80 rrd(n,idir) = rng%unif_01()
81 end do
82 end do
83 do n=1, num_particles
84 {^d&x(^d,n) = xprobmin^d + rrd(n,^d) * (xprobmax^d - xprobmin^d)\}
85 end do
86 else
87 call usr_create_particles(num_particles, x, v, q, m, follow)
88 end if
89 end if
90
91 call mpi_bcast(x,3*num_particles,mpi_double_precision,0,icomm,ierrmpi)
92 call mpi_bcast(follow,num_particles,mpi_logical,0,icomm,ierrmpi)
93 ! User callbacks on every owner rank need defined seed metadata.
94 call mpi_bcast(v,3*num_particles,mpi_double_precision,0,icomm,ierrmpi)
95 call mpi_bcast(q,num_particles,mpi_double_precision,0,icomm,ierrmpi)
96 call mpi_bcast(m,num_particles,mpi_double_precision,0,icomm,ierrmpi)
97
98 nparticles_local = 0
99
100 do n=1,num_particles
101 call find_particle_ipe(x(:,n),igrid,ipe_particle)
102 particle(n)%igrid = igrid
103 particle(n)%ipe = ipe_particle
104
105 if(ipe_particle == mype) then
106 check = .true.
107
108 ! Check for user-defined modifications or rejection conditions
109 if (associated(usr_check_particle)) call usr_check_particle(igrid, x(:,n), v(:,n), q(n), m(n), follow(n), check)
110 if (check) then
112 else
113 cycle
114 end if
115
116 nparticles_local = nparticles_local + 1
117
118 allocate(particle(n)%self)
119 particle(n)%self%follow = follow(n)
120 particle(n)%self%index = n
121 particle(n)%self%q = q(n)
122 particle(n)%self%m = m(n)
123 particle(n)%self%time = global_time
124 particle(n)%self%dt = 0.0d0
125 particle(n)%self%x = 0.d0
126 particle(n)%self%x(:) = x(:,n)
127 w=ps(igrid)%w
128 call phys_to_primitive(ixg^ll,ixg^ll,w,ps(igrid)%x)
129 do idir=1,ndir
130 call interpolate_var(igrid,ixg^ll,ixm^ll,&
131 w(ixg^t,iw_mom(idir)),ps(igrid)%x,x(:,n),v(idir,n))
132 end do
133 particle(n)%self%u(:) = 0.d0
134 particle(n)%self%u(1:ndir) = v(1:ndir,n)
135
136 ! Compute default and user-defined payloads
137 allocate(particle(n)%payload(npayload))
138 if(ndefpayload>0) then
139 call advect_update_payload(igrid,x(:,n),v(:,n),q(n),m(n),defpayload,ndefpayload,0.d0)
140 particle(n)%payload(1:ndefpayload)=defpayload
141 end if
142 if (associated(usr_update_payload)) then
143 call usr_update_payload(igrid,x(:,n),v(:,n),q(n),m(n),usrpayload,nusrpayload,0.d0)
144 particle(n)%payload(ndefpayload+1:npayload)=usrpayload
145 end if
146 end if
147
148 end do
149
150 call mpi_allreduce(nparticles_local,nparticles,1,mpi_integer,mpi_sum,icomm,ierrmpi)
151
152 ! write the first csv file of particles
156
157 end subroutine advect_create_particles
158
159 subroutine advect_fill_gridvars
161
162 double precision, dimension(ixG^T,1:nw) :: w
163 integer :: igrid, iigrid, idir
164
165 ! Fill fluid velocity only
166 do iigrid=1,igridstail; igrid=igrids(iigrid);
167 gridvars(igrid)%w(ixg^t,1:ngridvars) = 0.0d0
168 w(ixg^t,1:nw) = ps(igrid)%w(ixg^t,1:nw)
169 call phys_to_primitive(ixg^ll,ixg^ll,w,ps(igrid)%x)
170 gridvars(igrid)%w(ixg^t,vp(:)) = w(ixg^t,iw_mom(:))
171 gridvars(igrid)%w(ixg^t,rhop) = w(ixg^t,iw_rho)
172 end do
173
174 end subroutine advect_fill_gridvars
175
176 subroutine advect_integrate_particles(end_time)
177 ! this solves dx/dt=v for particles
178 use mod_odeint
181 double precision, intent(in) :: end_time
182
183 double precision, dimension(1:ndir) :: v, x
184 double precision :: defpayload(ndefpayload)
185 double precision :: usrpayload(nusrpayload)
186 double precision :: tloc, tlocnew, dt_p, h1, tk
187 double precision, dimension(1:ndir) :: yk, k1, k2, k3, k4
188 double precision,parameter :: eps=1.0d-6, hmin=1.0d-8
189 integer :: ipart, iipart, igrid
190 integer :: nok, nbad, ierror
191
192 do iipart=1,nparticles_active_on_mype
193 ipart = particles_active_on_mype(iipart);
194 igrid = particle(ipart)%igrid
195 igrid_working = igrid
196 dt_p = advect_get_particle_dt(particle(ipart), end_time)
197 particle(ipart)%self%dt = dt_p
198
199 tloc = particle(ipart)%self%time
200 x(1:ndir) = particle(ipart)%self%x(1:ndir)
201 tlocnew = tloc+dt_p
202
203 ! Position update
204 select case (integrator)
205 case (euler)
206 ! Simple forward Euler
207 call derivs_advect(tloc,x,v)
208 particle(ipart)%self%x(1:ndir) = particle(ipart)%self%x(1:ndir) + dt_p * v(1:ndir)
209
210 case (rk4)
211 ! Runge-Kutta order 4
212 tk = tloc
213 yk = x
214 call derivs_advect(tk,yk,k1)
215 tk = tloc + dt_p/2.d0
216 yk = x + dt_p*k1/2.d0
217 call derivs_advect(tk,yk,k2)
218 tk = tloc + dt_p/2.d0
219 yk = x + dt_p*k2/2.d0
220 call derivs_advect(tk,yk,k3)
221 tk = tloc + dt_p
222 yk = x + dt_p*k3
223 call derivs_advect(tloc,yk,k4)
224 x = x + dt_p/6.d0*(k1 + 2.d0*k2 + 2.d0*k3 + k4)
225
226 case (ark4)
227 ! Adaptive stepwidth RK4:
228 h1 = dt_p/2.0d0
229 call odeint(x,ndir,tloc,tlocnew,eps,h1,hmin,nok,nbad,derivs_advect,rkqs,ierror)
230
231 if (ierror /= 0) then
232 print *, "odeint returned error code", ierror
233 print *, "1 means hmin too small, 2 means MAXSTP exceeded"
234 print *, "Having a problem with particle", iipart
235 end if
236 end select
237 particle(ipart)%self%x(1:ndir) = x(1:ndir)
238
239 ! Velocity update
240 call get_vec_advect(igrid,x,tlocnew,v,vp(1),vp(ndir))
241 particle(ipart)%self%u(1:ndir) = v(1:ndir)
242
243 ! Time update
244 particle(ipart)%self%time = tlocnew
245
246 ! Update payload
247 if(ndefpayload>0) then
248 call advect_update_payload(igrid,x,v,0.d0,0.d0,defpayload,ndefpayload,tlocnew)
249 particle(ipart)%payload(1:ndefpayload) = defpayload
250 end if
251 if (associated(usr_update_payload)) then
252 call usr_update_payload(igrid,x,v,0.d0,0.d0,usrpayload,nusrpayload,tlocnew)
253 particle(ipart)%payload(ndefpayload+1:npayload) = usrpayload
254 end if
255
256 end do
257
258 end subroutine advect_integrate_particles
259
260 !> Payload update
261 subroutine advect_update_payload(igrid,xpart,upart,qpart,mpart,mypayload,mynpayload,particle_time)
263 integer, intent(in) :: igrid,mynpayload
264 double precision, intent(in) :: xpart(1:ndir),upart(1:ndir),qpart,mpart,particle_time
265 double precision, intent(out) :: mypayload(mynpayload)
266 double precision :: rho, rho1, rho2, td
267
268 td = (particle_time - global_time) / dt
269
270 ! Payload 1 is density
271 if (mynpayload > 0 ) then
272 if (time_advance) then
273 call interpolate_var(igrid,ixg^ll,ixm^ll,gridvars(igrid)%wold(ixg^t,rhop),ps(igrid)%x,xpart,rho1)
274 call interpolate_var(igrid,ixg^ll,ixm^ll,gridvars(igrid)%w(ixg^t,rhop),ps(igrid)%x,xpart,rho2)
275 rho = rho1 * (1.0d0 - td) + rho2 * td
276 else
277 call interpolate_var(igrid,ixg^ll,ixm^ll,gridvars(igrid)%w(ixg^t,rhop),ps(igrid)%x,xpart,rho)
278 end if
279 mypayload(1) = rho * w_convert_factor(1)
280 end if
281
282 end subroutine advect_update_payload
283
284 subroutine derivs_advect(t_s,x,dxdt)
286 use mod_geometry
287 double precision :: t_s, x(ndir)
288 double precision :: dxdt(ndir)
289
290 double precision :: v(ndir)
291
292 call get_vec_advect(igrid_working,x,t_s,v,vp(1),vp(ndir))
293 select case (coordinate)
294 case (cylindrical)
295 v(phi_) = v(phi_)/x(r_)
296 case (spherical)
297 v(2) = v(2)/x(1)
298 v(3) = v(3)/(x(1)*sin(x(2)))
299 end select
300 dxdt(:) = v(:)
301
302 end subroutine derivs_advect
303
304 function advect_get_particle_dt(partp, end_time) result(dt_p)
306 type(particle_ptr), intent(in) :: partp
307 double precision, intent(in) :: end_time
308 double precision :: dt_p
309 double precision :: v(1:ndir),dtdims(1:ndim)
310 integer :: ipart, iipart, idims
311
312 if (const_dt_particles > 0) then
313 dt_p = const_dt_particles
314 return
315 end if
316
317 ! make sure we step only one cell at a time:
318 call derivs_advect(partp%self%time,partp%self%x,v)
319 do idims=1,ndim
320 dtdims(idims)=minval(ps(partp%igrid)%ds(ixm^t,idims))/(max(abs(v(idims)),smalldouble))
321 end do
322
323 dt_p = particles_cfl*minval(dtdims)
324
325 ! Make sure we do not advance beyond end_time
326 call limit_dt_endtime(end_time - partp%self%time, dt_p)
327
328 end function advect_get_particle_dt
329
330 subroutine get_vec_advect(igrid,x,tloc,var,ibeg,iend)
332
333 integer,intent(in) :: igrid, ibeg, iend
334 double precision,dimension(ndir), intent(in) :: x
335 double precision, intent(in) :: tloc
336 double precision,dimension(iend-ibeg+1), intent(out) :: var
337 double precision,dimension(iend-ibeg+1) :: e1, e2
338 double precision :: td
339 integer :: ivar, iloc
340
341 if(.not.time_advance) then
342 do ivar=ibeg,iend
343 iloc = ivar-ibeg+1
344 call interpolate_var(igrid,ixg^ll,ixm^ll,gridvars(igrid)%w(ixg^t,ivar),ps(igrid)%x(ixg^t,1:ndim),x,var(iloc))
345 end do
346 else
347 td = (tloc - global_time) / dt
348 do ivar=ibeg,iend
349 iloc = ivar-ibeg+1
350 call interpolate_var(igrid,ixg^ll,ixm^ll,gridvars(igrid)%wold(ixg^t,ivar),ps(igrid)%x(ixg^t,1:ndim),x,e1(iloc))
351 call interpolate_var(igrid,ixg^ll,ixm^ll,gridvars(igrid)%w(ixg^t,ivar),ps(igrid)%x(ixg^t,1:ndim),x,e2(iloc))
352 var(iloc) = e1(iloc) * (1.0d0 - td) + e2(iloc) * td
353 end do
354 end if
355
356 end subroutine get_vec_advect
357
358end module mod_particle_advect
Module with geometry-related routines (e.g., divergence, curl).
Definition mod_geometry.t:2
integer coordinate
Definition mod_geometry.t:7
integer, parameter spherical
integer, parameter cylindrical
This module contains definitions of global parameters and variables and some generic functions/subrou...
double precision, dimension(:), allocatable w_convert_factor
Conversion factors the primitive variables.
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 dt
global time step
integer ndir
Number of spatial dimensions (components) for vector variables.
integer ixm
the mesh range of a physical block without ghost cells
integer ierrmpi
A global MPI error return code.
double precision, dimension(:), allocatable, parameter d
logical time_advance
do time evolving
integer r_
Indices for cylindrical coordinates FOR TESTS, negative value when not used:
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
Tracer for advected particles moving with fluid flows By Jannis Teunissen, Bart Ripperda,...
subroutine, public advect_init()
subroutine, public advect_create_particles()
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.
subroutine write_particle_output()
double precision dtsave_particles
Time interval to save particles.
integer ngridvars
Number of variables for grid field.
integer nusrpayload
Number of user-defined payload variables for a particle.
subroutine find_particle_ipe(x, igrid_particle, ipe_particle)
integer integrator
Integrator to be used for particles.
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 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.
type(particle_ptr), dimension(:), allocatable particle
integer, dimension(:), allocatable vp
Variable index for fluid velocity.
procedure(sub_integrate), pointer particles_integrate
procedure(sub_fill_gridvars), pointer particles_fill_gridvars
double precision t_next_output
Time to write next particle output.
integer ndefpayload
Number of default payload variables for a particle.
integer igrid_working
set the current igrid for the particle integrator:
subroutine interpolate_var(igrid, ixil, ixol, gf, x, xloc, gfloc)
double precision particles_cfl
Particle CFL safety factor.
integer rhop
Variable index for density.
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