MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_particle_base.t
Go to the documentation of this file.
1!> Module with shared functionality for all the particle movers
3 use mod_global_parameters, only: name_len, std_len
5 use mod_random
7 use mod_comm_lib, only: mpistop
8
9 !> Normalization factor for velocity in the integrator
10 double precision :: integrator_velocity_factor(3)
11 !> Time limit of particles
12 double precision :: tmax_particles
13 !> Minimum time of all particles
14 double precision :: min_particle_time
15 !> Time interval to save particles
16 double precision :: dtsave_particles
17 !> If positive, a constant time step for the particles
18 double precision :: const_dt_particles
19 !> Particle CFL safety factor
20 double precision :: particles_cfl
21 !> Time to write next particle output
22 double precision :: t_next_output
23 !> Resistivity
24 double precision :: particles_eta, particles_etah
25 double precision :: dtheta
26 !> Particle downsampling factor in CSV output
28 !> Maximum number of particles
29 integer :: nparticleshi
30 !> Maximum number of particles in one processor
32 !> Number of default payload variables for a particle
33 integer :: ndefpayload
34 !> Number of user-defined payload variables for a particle
35 integer :: nusrpayload
36 !> Number of total payload variables for a particle
37 integer :: npayload
38 !> Number of variables for grid field
39 integer :: ngridvars
40 !> Number of particles
41 integer :: num_particles
42 !> Identity number and total number of particles
43 integer :: nparticles
44 !> Iteration number of paritcles
45 integer :: it_particles
46 !> Output count for ensembles of destroyed particles
48
49 ! these two save the list of neighboring cpus:
50 integer, allocatable :: ipe_neighbor(:)
51 integer :: npe_neighbors
52
53 integer :: type_particle
54 !> set the current igrid for the particle integrator:
55 integer :: igrid_working
56 !> set the current ipart for the particle integrator:
57 integer :: ipart_working
58
59 integer, parameter :: unitparticles=15
60
61 !> Array of identity numbers of particles in current processor
62 integer, dimension(:), allocatable :: particles_on_mype
63 !> Array of identity numbers of active particles in current processor
64 integer, dimension(:), allocatable :: particles_active_on_mype
65 !> Number of particles in current processor
67 !> Number of active particles in current processor
69 !> Integrator to be used for particles
70 integer :: integrator
71
72 !> Variable index for magnetic field
73 integer, allocatable :: bp(:)
74 !> Variable index for electric field
75 integer, allocatable :: ep(:)
76 !> Variable index for fluid velocity
77 integer, allocatable :: vp(:)
78 !> Variable index for current
79 integer, allocatable :: jp(:)
80 !> Variable index for density
81 integer :: rhop
82 !> Use a relativistic particle mover?
83 logical :: relativistic
84 !> Whether to write individual particle output (followed particles)
86 !> Whether to write ensemble output
87 logical :: write_ensemble
88 !> Whether to write particle snapshots
89 logical :: write_snapshot
90 logical :: losses
91 !> String describing the particle interpolation type
92 character(len=name_len) :: interp_type_particles = ""
93 !> String describing the particle physics type
94 character(len=name_len) :: physics_type_particles = ""
95 !> String describing the particle integrator type
96 character(len=name_len) :: integrator_type_particles = ""
97 !> Header string used in CSV files
98 character(len=400) :: csv_header
99 !> Format string used in CSV files
100 character(len=60) :: csv_format
101
103 type(particle_t), pointer :: self => null()
104 !> extra information carried by the particle
105 double precision, allocatable :: payload(:)
106 integer :: igrid, ipe
107 end type particle_ptr
108
110 !> follow the history of the particle
111 logical :: follow
112 !> identity number
113 integer :: index
114 !> charge
115 double precision :: q
116 !> mass
117 double precision :: m
118 !> time
119 double precision :: time
120 !> time step
121 double precision :: dt
122 !> coordinates
123 double precision :: x(3)
124 !> velocity, momentum, or special ones
125 double precision :: u(3)
126 end type particle_t
127
128 type(particle_t), allocatable :: exchange_send(:),exchange_recv(:)
129 double precision, allocatable :: exchange_send_payload(:,:),exchange_recv_payload(:,:)
130 integer, allocatable :: exchange_ids(:),exchange_requests(:),exchange_status(:,:)
132
133 ! Array containing all particles
134 type(particle_ptr), dimension(:), allocatable :: particle
135
136 ! The pseudo-random number generator
137 type(rng_t) :: rng
138
139 ! Pointers for user-defined stuff
140 procedure(sub_fill_gridvars), pointer :: particles_fill_gridvars => null()
143 procedure(sub_integrate), pointer :: particles_integrate => null()
144 procedure(fun_destroy), pointer :: usr_destroy_particle => null()
145
146 abstract interface
147
149 end subroutine sub_fill_gridvars
150
151 subroutine sub_define_additional_gridvars(ngridvars)
152 integer, intent(inout) :: ngridvars
153 end subroutine sub_define_additional_gridvars
154
156 end subroutine sub_fill_additional_gridvars
157
158 subroutine sub_integrate(end_time)
159 double precision, intent(in) :: end_time
160 end subroutine sub_integrate
161
162 function fun_destroy(myparticle)
163 import particle_ptr
164 logical :: fun_destroy
165 type(particle_ptr), intent(in) :: myparticle
166 end function fun_destroy
167
168 end interface
169
170 !> Cached global activity and last replicated forest generation.
171 logical :: particle_any_active=.false.
172 integer(kind=8) :: particle_last_mesh_epoch=-1_8
173contains
174 !> Exchange only migrating particles, reusing geometrically grown buffers.
175 !> All ranks participate; a global decision selects the non-neighbor fallback.
176 !> Payload columns and particle slices stay contiguous until MPI_WAITALL.
177 subroutine particle_exchange(outgoing_ids, destinations, nout, global_exchange)
179 integer, intent(in) :: nout, outgoing_ids(:), destinations(:)
180 logical, intent(in) :: global_exchange
181 integer :: sc(0:npe-1),rc(0:npe-1),so(0:npe-1),ro(0:npe-1),cursor(0:npe-1)
182 integer :: p,j,k,nrecv,nreq,ngrow,idx,igrid_particle,ipe_particle,tag_s,tag_r
183 integer :: local_flags(2),global_flags(2)
184 logical :: neighbor_mask(0:npe-1)
185
186 sc=0;rc=0;neighbor_mask=.false.
187 if(.not.global_exchange) neighbor_mask(ipe_neighbor(1:npe_neighbors))=.true.
188 do j=1,nout
189 p=destinations(j)
190 if(p<0.or.p>=npe.or.p==mype) call mpistop('Invalid particle migration destination')
191 sc(p)=sc(p)+1
192 end do
193 if(npe==1) return
194
195 ! Globally agreed empty-migration shortcut and safe non-neighbor fallback.
196 ! One two-integer reduction replaces zero neighbor messages on empty steps.
197 if(.not.global_exchange) then
198 local_flags(1)=nout
199 local_flags(2)=count(sc>0.and..not.neighbor_mask)
200 call mpi_allreduce(local_flags,global_flags,2,mpi_integer,mpi_sum,icomm,ierrmpi)
201 if(global_flags(1)==0) then
202 return
203 end if
204 end if
205
206 if(.not.allocated(exchange_requests)) then
207 allocate(exchange_requests(max(1,4*(npe-1))))
208 allocate(exchange_status(mpi_status_size,max(1,4*(npe-1))))
209 end if
210 if(global_exchange) then
211 call mpi_alltoall(sc,1,mpi_integer,rc,1,mpi_integer,icomm,ierrmpi)
212 else if(global_flags(2)>0) then
213 call mpi_alltoall(sc,1,mpi_integer,rc,1,mpi_integer,icomm,ierrmpi)
214 else
215 nreq=0
216 do j=1,npe_neighbors
217 p=ipe_neighbor(j);nreq=nreq+1
218 call mpi_irecv(rc(p),1,mpi_integer,p,p*npe+mype,icomm,exchange_requests(nreq),ierrmpi)
219 end do
220 do j=1,npe_neighbors
221 p=ipe_neighbor(j);nreq=nreq+1
222 call mpi_isend(sc(p),1,mpi_integer,p,mype*npe+p,icomm,exchange_requests(nreq),ierrmpi)
223 end do
224 call mpi_waitall(nreq,exchange_requests,exchange_status,ierrmpi)
225 end if
226 nrecv=sum(rc)
227
228 if(nout>exchange_send_capacity) then
230 ngrow=max(nout,max(16,2*exchange_send_capacity))
231 allocate(exchange_send(ngrow),exchange_send_payload(npayload,ngrow),exchange_ids(ngrow))
233 end if
234 if(nrecv>exchange_recv_capacity) then
235 if(allocated(exchange_recv)) deallocate(exchange_recv,exchange_recv_payload)
236 ngrow=max(nrecv,max(16,2*exchange_recv_capacity))
237 allocate(exchange_recv(ngrow),exchange_recv_payload(npayload,ngrow))
239 end if
240 so(0)=0;ro(0)=0
241 do p=1,npe-1
242 so(p)=so(p-1)+sc(p-1);ro(p)=ro(p-1)+rc(p-1)
243 end do
244 cursor=so
245 do j=1,nout
246 p=destinations(j);cursor(p)=cursor(p)+1;k=cursor(p);idx=outgoing_ids(j)
247 exchange_ids(k)=idx
248 exchange_send(k)=particle(idx)%self
249 exchange_send_payload(:,k)=particle(idx)%payload
250 end do
251 ! Publish every receive before any data send; zero-count peers are absent.
252 nreq=0
253 do p=0,npe-1
254 if(rc(p)==0) cycle
255 tag_r=p*npe+mype;k=ro(p)+1
256 nreq=nreq+1
257 call mpi_irecv(exchange_recv(k:k+rc(p)-1),rc(p),type_particle,p,tag_r,icomm,exchange_requests(nreq),ierrmpi)
258 nreq=nreq+1
259 call mpi_irecv(exchange_recv_payload(:,k:k+rc(p)-1),npayload*rc(p), &
260 mpi_double_precision,p,tag_r,icomm,exchange_requests(nreq),ierrmpi)
261 end do
262 do p=0,npe-1
263 if(sc(p)==0) cycle
264 tag_s=mype*npe+p;k=so(p)+1
265 nreq=nreq+1
266 call mpi_isend(exchange_send(k:k+sc(p)-1),sc(p),type_particle,p,tag_s,icomm,exchange_requests(nreq),ierrmpi)
267 nreq=nreq+1
268 call mpi_isend(exchange_send_payload(:,k:k+sc(p)-1),npayload*sc(p), &
269 mpi_double_precision,p,tag_s,icomm,exchange_requests(nreq),ierrmpi)
270 end do
271 call mpi_waitall(nreq,exchange_requests,exchange_status,ierrmpi)
272 ! Preserve each original routine's deletion/insertion ordering exactly.
273 if(.not.global_exchange) then
274 do p=0,npe-1
275 do k=so(p)+1,so(p)+sc(p)
276 idx=exchange_ids(k)
277 deallocate(particle(idx)%self,particle(idx)%payload)
278 nullify(particle(idx)%self)
280 end do
281 end do
282 end if
283 do p=0,npe-1
284 if(global_exchange) then
285 do k=so(p)+1,so(p)+sc(p)
286 idx=exchange_ids(k)
287 deallocate(particle(idx)%self,particle(idx)%payload)
288 nullify(particle(idx)%self)
290 end do
291 end if
292 do k=ro(p)+1,ro(p)+rc(p)
293 idx=exchange_recv(k)%index
294 if(idx<1.or.idx>size(particle)) call mpistop('Received particle index out of bounds')
295 if(associated(particle(idx)%self)) call mpistop('Duplicate particle received')
296 allocate(particle(idx)%self,particle(idx)%payload(npayload))
297 particle(idx)%self=exchange_recv(k)
298 particle(idx)%payload=exchange_recv_payload(:,k)
300 call find_particle_ipe(particle(idx)%self%x,igrid_particle,ipe_particle)
301 if(ipe_particle/=mype.or.igrid_particle<1) call mpistop('Received particle has wrong owner')
302 particle(idx)%igrid=igrid_particle;particle(idx)%ipe=ipe_particle
303 end do
304 end do
305 end subroutine particle_exchange
306
309 integer :: j,idx,igrid_particle,ipe_particle,nout
310 integer :: outgoing_ids(max(1,nparticles_on_mype)),destinations(max(1,nparticles_on_mype))
311 nout=0
312 do j=1,nparticles_on_mype
313 idx=particles_on_mype(j)
314 call find_particle_ipe(particle(idx)%self%x,igrid_particle,ipe_particle)
315 if(ipe_particle<0.or.igrid_particle<1) call mpistop('Global relocation outside particle domain')
316 particle(idx)%igrid=igrid_particle;particle(idx)%ipe=ipe_particle
317 if(ipe_particle/=mype) then
318 nout=nout+1;outgoing_ids(nout)=idx;destinations(nout)=ipe_particle
319 end if
320 end do
321 call particle_exchange(outgoing_ids,destinations,nout,.true.)
322 end subroutine comm_particles_global
323
324 subroutine comm_particles()
326 integer :: j,idx,igrid_particle,ipe_particle,nout,ndestroy
327 integer :: outgoing_ids(max(1,nparticles_on_mype)),destinations(max(1,nparticles_on_mype))
328 integer :: destroyed(max(1,nparticles_on_mype))
329 logical :: BC_applied
330 nout=0;ndestroy=0
331 do j=1,nparticles_on_mype
332 idx=particles_on_mype(j)
333 if(associated(usr_destroy_particle)) then
334 if(usr_destroy_particle(particle(idx))) then
335 ndestroy=ndestroy+1;destroyed(ndestroy)=idx
336 cycle
337 end if
338 end if
339 if(.not.particle_in_igrid(idx,particle(idx)%igrid)) then
340 call find_particle_ipe(particle(idx)%self%x,igrid_particle,ipe_particle)
341 if(igrid_particle==-1) then
342 call apply_periodb(particle(idx)%self,igrid_particle,ipe_particle,bc_applied)
343 if(.not.bc_applied.or.igrid_particle==-1) then
344 ndestroy=ndestroy+1;destroyed(ndestroy)=idx
345 cycle
346 end if
347 end if
348 particle(idx)%igrid=igrid_particle;particle(idx)%ipe=ipe_particle
349 if(npe>1.and.ipe_particle/=mype) then
350 nout=nout+1;outgoing_ids(nout)=idx;destinations(nout)=ipe_particle
351 end if
352 end if
353 end do
354 call destroy_particles(ndestroy,destroyed(1:ndestroy))
355 call particle_exchange(outgoing_ids,destinations,nout,.false.)
356 end subroutine comm_particles
357
358
359 !> Finalize the particles module
360 subroutine particles_finish()
362
364
366 if(allocated(exchange_recv)) deallocate(exchange_recv,exchange_recv_payload)
367 if(allocated(exchange_requests)) deallocate(exchange_requests,exchange_status)
370 particle_any_active=.false.
371
372 ! Clean up particle type
373 call mpi_type_free(type_particle,ierrmpi)
374
375 ! Clean up variables
376 deallocate(particle)
377 deallocate(ipe_neighbor)
378 deallocate(particles_on_mype)
379 deallocate(particles_active_on_mype)
380 deallocate(gridvars)
381
382 end subroutine particles_finish
383
384 !> Read this module's parameters from a file
385 subroutine particles_params_read(files)
387 character(len=*), intent(in) :: files(:)
388 integer :: n
389
390 namelist /particles_list/ physics_type_particles, nparticleshi, nparticles_per_cpu_hi, &
397
398 do n = 1, size(files)
399 open(unitpar, file=trim(files(n)), status="old")
400 read(unitpar, particles_list, end=111)
401111 close(unitpar)
402 end do
403
404 end subroutine particles_params_read
405
406 !> Give initial values to parameters
409 integer :: n
410 integer, parameter :: i8 = selected_int_kind(18)
411 integer(i8) :: seed(2)
412 integer :: idir
413 character(len=20) :: strdata
414
416 particle_any_active=.false.
417
418 physics_type_particles = 'advect'
419 nparticleshi = 10000000
420 nparticles_per_cpu_hi = 1000000
421 num_particles = 1000
422 ndefpayload = 1
423 nusrpayload = 0
424 tmax_particles = bigdouble
425 dtsave_particles = bigdouble
426 const_dt_particles = -bigdouble
427 particles_cfl = 0.5
428 write_individual = .true.
429 write_ensemble = .true.
430 write_snapshot = .true.
432 relativistic = .false.
433 particles_eta = -1.d0
434 particles_etah = -1.d0
435 t_next_output = 0.0d0
436 dtheta = 2.0d0*dpi / 60.0d0
437 losses = .false.
438 nparticles = 0
439 it_particles = 0
445 interp_type_particles = 'default'
446
448
449 ! If sampling, ndefpayload = nw:
450 if (physics_type_particles == 'sample') ndefpayload = nw
451 ! Total number of payloads
453
454 ! initialise the random number generator
455 seed = [310952_i8, 8948923749821_i8]
456 call rng%set_seed(seed)
457
458 allocate(particle(1:nparticleshi))
459 allocate(ipe_neighbor(0:npe-1))
462
463 particles_on_mype(:) = 0
465
466 ! Generate header for CSV files
467 csv_header = ' time, dt, x1, x2, x3, u1, u2, u3,'
468 ! If sampling, name the payloads like the fluid quantities
469 if (physics_type_particles == 'sample') then
470 do n = 1, nw
471 write(strdata,"(a,a,a)") ' ', trim(prim_wnames(n)), ','
472 csv_header = trim(csv_header) // trim(strdata)
473 end do
474 else ! Otherwise, payloads are called pl01, pl02, ...
475 do n = 1, ndefpayload
476 write(strdata,"(a,i2.2,a)") ' pl', n, ','
477 csv_header = trim(csv_header) // trim(strdata)
478 end do
479 end if
480 ! User-defined payloads are called usrpl01, usrpl02, ...
481 if (nusrpayload > 0) then
482 do n = 1, nusrpayload
483 write(strdata,"(a,i2.2,a)") ' usrpl', n, ','
484 csv_header = trim(csv_header) // trim(strdata)
485 end do
486 end if
487 csv_header = trim(csv_header) // ' ipe, iteration, index'
488
489 ! Generate format string for CSV files
490 write(csv_format, '(A,I0,A,A)') '(', 8 + npayload, '(es14.6,", "),', &
491 'i8.7,", ",i11.10,", ",i8.7)'
492
493 end subroutine particle_base_init
494
495 !> Initialize communicators for particles
498
499 integer :: oldtypes(0:7), offsets(0:7), blocklengths(0:7)
500
501 oldtypes(0) = mpi_logical
502 oldtypes(1) = mpi_integer
503 oldtypes(2:7) = mpi_double_precision
504
505 blocklengths(0:5)=1
506 blocklengths(6)=3
507 blocklengths(7)=3
508
509 offsets(0) = 0
510 offsets(1) = size_logical * blocklengths(0)
511 offsets(2) = offsets(1) + size_int * blocklengths(1)
512 offsets(3) = offsets(2) + size_double * blocklengths(2)
513 offsets(4) = offsets(3) + size_double * blocklengths(3)
514 offsets(5) = offsets(4) + size_double * blocklengths(4)
515 offsets(6) = offsets(5) + size_double * blocklengths(5)
516 offsets(7) = offsets(6) + size_double * blocklengths(6)
517
518 call mpi_type_struct(8,blocklengths,offsets,oldtypes,type_particle,ierrmpi)
519 call mpi_type_commit(type_particle,ierrmpi)
520
521 end subroutine init_particles_com
522
523 subroutine get_maxwellian_velocity(v, velocity)
524 double precision, intent(out) :: v(3)
525 double precision, intent(in) :: velocity
526 double precision :: vtmp(3), vnorm
527
528 vtmp(1) = velocity * rng%normal()
529 vtmp(2) = velocity * rng%normal()
530 vtmp(3) = velocity * rng%normal()
531 vnorm = norm2(vtmp)
532 v = rng%sphere(vnorm)
533 end subroutine get_maxwellian_velocity
534
535 subroutine get_uniform_pos(x)
537 double precision, intent(out) :: x(3)
538
539 call rng%unif_01_vec(x)
540
541 {^d&x(^d) = xprobmin^d + x(^d) * (xprobmax^d - xprobmin^d)\}
542 x(ndim+1:) = 0.0d0
543 end subroutine get_uniform_pos
544
545 !> Initialize grid variables for particles
546 subroutine init_gridvars()
548 use mod_timing
549
550 integer :: igrid, iigrid
551
552 tpartc_grid_0=mpi_wtime()
553
554 do iigrid=1,igridstail; igrid=igrids(iigrid);
555 allocate(gridvars(igrid)%w(ixg^t,1:ngridvars),gridvars(igrid)%wold(ixg^t,1:ngridvars))
556 gridvars(igrid)%w = 0.0d0
557 gridvars(igrid)%wold = 0.0d0
558 end do
559
561 if (associated(particles_define_additional_gridvars)) then
563 end if
564
565 tpartc_grid = tpartc_grid + (mpi_wtime()-tpartc_grid_0)
566
567 end subroutine init_gridvars
568
569 !> Deallocate grid variables for particles
570 subroutine finish_gridvars()
572
573 integer :: iigrid, igrid
574
575 do iigrid=1,igridstail; igrid=igrids(iigrid);
576 deallocate(gridvars(igrid)%w,gridvars(igrid)%wold)
577 end do
578
579 end subroutine finish_gridvars
580
581 !> This routine fills the particle grid variables with the default for mhd, i.e. only E and B
585
586 double precision :: E(ixG^T, ndir)
587 double precision :: B(ixG^T, ndir)
588 integer :: igrid, iigrid
589
590 do iigrid=1,igridstail; igrid=igrids(iigrid);
591 if (associated(usr_particle_fields)) then ! FILL ONLY E AND B
592 call usr_particle_fields(ps(igrid)%w, ps(igrid)%x, e, b)
593 gridvars(igrid)%w(ixg^t,ep(:)) = e
594 gridvars(igrid)%w(ixg^t,bp(:)) = b
595 else
596 call fields_from_mhd(igrid, ps(igrid)%w, gridvars(igrid)%w)
597 end if
598 end do
599
600 end subroutine fill_gridvars_default
601
602 !> Determine fields from MHD variables
603 subroutine fields_from_mhd(igrid, w_mhd, w_part)
605 use mod_geometry
606 integer, intent(in) :: igrid
607 double precision, intent(in) :: w_mhd(ixG^T,nw)
608 double precision, intent(inout) :: w_part(ixG^T,ngridvars)
609
610 double precision :: current(ixG^T,7-2*ndir:3)
611 double precision :: w(ixG^T,1:nw)
612 integer :: idirmin, idir
613
614 ^d&dxlevel(^d)=rnode(rpdx^d_,igrid);
615
616 w(ixg^t,1:nw) = w_mhd(ixg^t,1:nw)
617 w_part(ixg^t,bp(1):bp(3)) = 0.0d0
618 w_part(ixg^t,ep(1):ep(3)) = 0.0d0
619
620 call phys_to_primitive(ixg^ll,ixg^ll,w,ps(igrid)%x)
621
622 ! fill with density:
623 w_part(ixg^t,rhop) = w(ixg^t,iw_rho)
624
625 ! fill with velocity:
626 w_part(ixg^t,vp(:)) = w(ixg^t,iw_mom(:))
627
628 ! fill with magnetic field:
629 if (b0field) then
630 do idir = 1, ndir
631 w_part(ixg^t,bp(idir))=w(ixg^t,iw_mag(idir))+ps(igrid)%B0(ixg^t,idir,0)
632 end do
633 else
634 w_part(ixg^t,bp(:)) = w(ixg^t,iw_mag(:))
635 end if
636
637 ! fill with current
638 current = zero
639 call particle_get_current(w,ixg^ll,ixg^llim^d^lsub1,idirmin,current)
640 w_part(ixg^t,jp(:)) = current(ixg^t,:)
641
642 ! fill with electric field
643 select case (coordinate)
645 w_part(ixg^t,ep(1)) = w_part(ixg^t,bp(2)) * &
646 w(ixg^t,iw_mom(3)) - w_part(ixg^t,bp(3)) * &
647 w(ixg^t,iw_mom(2)) + particles_eta * current(ixg^t,1)
648 w_part(ixg^t,ep(2)) = w_part(ixg^t,bp(3)) * &
649 w(ixg^t,iw_mom(1)) - w_part(ixg^t,bp(1)) * &
650 w(ixg^t,iw_mom(3)) + particles_eta * current(ixg^t,2)
651 w_part(ixg^t,ep(3)) = w_part(ixg^t,bp(1)) * &
652 w(ixg^t,iw_mom(2)) - w_part(ixg^t,bp(2)) * &
653 w(ixg^t,iw_mom(1)) + particles_eta * current(ixg^t,3)
654 case (cylindrical)
655 w_part(ixg^t,ep(r_)) = w_part(ixg^t,bp(phi_)) * &
656 w(ixg^t,iw_mom(z_)) - w_part(ixg^t,bp(z_)) * &
657 w(ixg^t,iw_mom(phi_)) + particles_eta * current(ixg^t,r_)
658 w_part(ixg^t,ep(phi_)) = w_part(ixg^t,bp(z_)) * &
659 w(ixg^t,iw_mom(r_)) - w_part(ixg^t,bp(r_)) * &
660 w(ixg^t,iw_mom(z_)) + particles_eta * current(ixg^t,phi_)
661 w_part(ixg^t,ep(z_)) = w_part(ixg^t,bp(r_)) * &
662 w(ixg^t,iw_mom(phi_)) - w_part(ixg^t,bp(phi_)) * &
663 w(ixg^t,iw_mom(r_)) + particles_eta * current(ixg^t,z_)
664 end select
665
666 ! Hall term
667 if (particles_etah > zero) then
668 select case (coordinate)
670 w_part(ixg^t,ep(1)) = w_part(ixg^t,ep(1)) + particles_etah/w(ixg^t,iw_rho) * &
671 (current(ixg^t,2) * w_part(ixg^t,bp(3)) - current(ixg^t,3) * w_part(ixg^t,bp(2)))
672 w_part(ixg^t,ep(2)) = w_part(ixg^t,ep(2)) + particles_etah/w(ixg^t,iw_rho) * &
673 (-current(ixg^t,1) * w_part(ixg^t,bp(3)) + current(ixg^t,3) * w_part(ixg^t,bp(1)))
674 w_part(ixg^t,ep(3)) = w_part(ixg^t,ep(3)) + particles_etah/w(ixg^t,iw_rho) * &
675 (current(ixg^t,1) * w_part(ixg^t,bp(2)) - current(ixg^t,2) * w_part(ixg^t,bp(1)))
676 case (cylindrical)
677 w_part(ixg^t,ep(r_)) = w_part(ixg^t,ep(r_)) + particles_etah/w(ixg^t,iw_rho) * &
678 (current(ixg^t,phi_) * w_part(ixg^t,bp(z_)) - current(ixg^t,z_) * w_part(ixg^t,bp(phi_)))
679 w_part(ixg^t,ep(phi_)) = w_part(ixg^t,ep(phi_)) + particles_etah/w(ixg^t,iw_rho) * &
680 (-current(ixg^t,r_) * w_part(ixg^t,bp(z_)) + current(ixg^t,z_) * w_part(ixg^t,bp(r_)))
681 w_part(ixg^t,ep(z_)) = w_part(ixg^t,ep(z_)) + particles_etah/w(ixg^t,iw_rho) * &
682 (current(ixg^t,r_) * w_part(ixg^t,bp(phi_)) - current(ixg^t,phi_) * w_part(ixg^t,bp(r_)))
683 end select
684 end if
685
686 end subroutine fields_from_mhd
687
688 !> Calculate idirmin and the idirmin:3 components of the common current array
689 !> make sure that dxlevel(^D) is set correctly.
690 subroutine particle_get_current(w,ixI^L,ixO^L,idirmin,current)
692 use mod_geometry
693
694 integer :: ixO^L, idirmin, ixI^L
695 double precision :: w(ixI^S,1:nw)
696 ! For ndir=2 only 3rd component of J can exist, ndir=1 is impossible for MHD
697 double precision :: current(ixI^S,7-2*ndir:3),bvec(ixI^S,1:ndir)
698 integer :: idir
699 integer :: idirmin0
700
701 idirmin0 = 7-2*ndir
702
703 if (b0field) then
704 do idir = 1, ndir
705 bvec(ixi^s,idir)=w(ixi^s,iw_mag(idir))+block%B0(ixi^s,idir,0)
706 end do
707 else
708 do idir = 1, ndir
709 bvec(ixi^s,idir)=w(ixi^s,iw_mag(idir))
710 end do
711 end if
712
713 call curlvector(bvec,ixi^l,ixo^l,current,idirmin,idirmin0,ndir)
714
715 end subroutine particle_get_current
716
717 !> update grid variables for particles
718 subroutine update_gridvars()
720 use mod_timing
721
722 integer :: igrid, iigrid
723
724 tpartc_grid_0=mpi_wtime()
725
726 do iigrid=1,igridstail; igrid=igrids(iigrid);
727 gridvars(igrid)%wold=gridvars(igrid)%w
728 end do
729
731 if (associated(particles_define_additional_gridvars)) then
733 end if
734
735 tpartc_grid = tpartc_grid + (mpi_wtime()-tpartc_grid_0)
736
737 end subroutine update_gridvars
738
739 !> Let particles evolve in time. The routine also handles grid variables and
740 !> output.
741 subroutine handle_particles()
743 use mod_timing
745
746 integer :: steps_taken, nparticles_left
747
748 tpartc0 = mpi_wtime()
749
750 ! Forest changes are replicated on every rank, so all ranks take the
751 ! same relocation branch, including ranks without particles.
753
754 if (time_advance) then
756 call update_gridvars()
757 else
759 end if
760 tpartc_com0=mpi_wtime()
764 end if
765 tpartc_com=tpartc_com + (mpi_wtime()-tpartc_com0)
766
767
768 ! main integration loop
769 do
770 if (tmax_particles >= t_next_output) then
771 call advance_particles(t_next_output, steps_taken)
772 tpartc_io_0 = mpi_wtime()
774 if(convert .and. write_snapshot) then
775 if(mype==0) print*, "Writing particle output at time",t_next_output
777 end if
778 timeio_tot = timeio_tot+(mpi_wtime()-tpartc_io_0)
779 tpartc_io = tpartc_io+(mpi_wtime()-tpartc_io_0)
780
781 ! If we are using a constant time step, this prevents multiple outputs
782 ! at the same time
785 else
786 call advance_particles(tmax_particles, steps_taken)
787! call write_particle_output()
788 exit
789 end if
790
791 call mpi_allreduce(nparticles_on_mype, nparticles_left, 1, mpi_integer, &
792 mpi_sum, icomm, ierrmpi)
793 if (nparticles_left == 0 .and. convert) call mpistop('No particles left')
794
795 end do
796
797 tpartc = tpartc + (mpi_wtime() - tpartc0)
798
799 end subroutine handle_particles
800
801 !> Routine handles the particle evolution
802 subroutine advance_particles(end_time, steps_taken)
803 use mod_timing
805 double precision, intent(in) :: end_time !< Advance at most up to this time
806 integer, intent(out) :: steps_taken
807
808 steps_taken = 0
809
810 do
811 call select_active_particles(end_time)
812
813 ! The combined MIN reduction also reports whether any rank is active.
814 if (.not.particle_any_active) exit
815
816 tpartc_int_0=mpi_wtime()
817 call particles_integrate(end_time)
818 steps_taken = steps_taken + 1
820
821 tpartc_com0=mpi_wtime()
822 call comm_particles()
823 tpartc_com=tpartc_com + (mpi_wtime()-tpartc_com0)
824
826 end do
827
828 end subroutine advance_particles
829
830 pure subroutine limit_dt_endtime(t_left, dt_p)
831 double precision, intent(in) :: t_left
832 double precision, intent(inout) :: dt_p
833 double precision :: n_steps
834 double precision, parameter :: eps = 1d-10
835
836 n_steps = t_left / dt_p
837
838 if (n_steps < 1+eps) then
839 ! Last step
840 dt_p = t_left
841 else if (n_steps < 2-eps) then
842 ! Divide time left over two steps, to prevent one tiny time step
843 dt_p = 0.5d0 * t_left
844 end if
845
846 end subroutine limit_dt_endtime
847
848 ! Get a generic vector quantity stored on the grid at the particle position
849 ! NOTE: DON'T GET E OR B IN THIS WAY, USE FUNCTIONS BELOW!
850 subroutine get_vec(ix,igrid,x,tloc,vec)
853
854 integer,intent(in) :: ix(ndir) !< Indices in gridvars
855 integer,intent(in) :: igrid
856 double precision,dimension(3), intent(in) :: x
857 double precision, intent(in) :: tloc
858 double precision,dimension(ndir), intent(out) :: vec
859 double precision,dimension(ndir) :: vec1, vec2
860 double precision :: td, bb
861 integer :: ic^D,idir
862
863 if (associated(usr_particle_analytic)) then
864 call usr_particle_analytic(ix, x, tloc, vec)
865 else
866 do idir=1,ndir
867 call interpolate_var(igrid,ixg^ll,ixm^ll,gridvars(igrid)%w(ixg^t,ix(idir)), &
868 ps(igrid)%x(ixg^t,1:ndim),x,vec(idir))
869 end do
870
871 if (time_advance) then
872 do idir=1,ndir
873 call interpolate_var(igrid,ixg^ll,ixm^ll,gridvars(igrid)%wold(ixg^t,ix(idir)), &
874 ps(igrid)%x(ixg^t,1:ndim),x,vec2(idir))
875 end do
876
877 ! Interpolate in time
878 td = (tloc - global_time) / dt
879 vec(:) = vec2(:) * (1.0d0 - td) + vec(:) * td
880 end if
881
882 ! Fabio June 2024: Renormalize vector by its grid-evaluated norm
883 if (interp_type_particles=='renormalized') then
884 if (time_advance) then
885 if (sqrt(sum(vec(:)**2)) .gt. 0.d0) then
886 call interpolate_var(igrid,ixg^ll,ixm^ll, &
887 sum(gridvars(igrid)%w(ixg^t,ix(:))**2,dim=ndim+1)*td &
888 +sum(gridvars(igrid)%wold(ixg^t,ix(:))**2,dim=ndim+1)*(1.d0-td), &
889 ps(igrid)%x(ixg^t,1:ndim),x,bb)
890 end if
891 else
892 if (sqrt(sum(vec(:)**2)) .gt. 0.d0) then
893 call interpolate_var(igrid,ixg^ll,ixm^ll, &
894 sum(gridvars(igrid)%w(ixg^t,ix(:))**2,dim=ndim+1), &
895 ps(igrid)%x(ixg^t,1:ndim),x,bb)
896 end if
897 vec = vec/sqrt(sum(vec(:)**2))*sqrt(bb)
898 end if
899 end if
900 end if
901
902 end subroutine get_vec
903
904 subroutine get_bfield(igrid,x,tloc,b)
907
908 integer,intent(in) :: igrid
909 double precision,dimension(3), intent(in) :: x
910 double precision, intent(in) :: tloc
911 double precision,dimension(ndir), intent(out) :: b
912 double precision,dimension(ndir) :: vec1, vec2, vec
913 double precision :: td, bb
914 integer :: ic^D,idir
915
916 if (associated(usr_particle_analytic)) then
917 call usr_particle_analytic(bp, x, tloc, vec)
918 else if (associated(usr_particle_fields)) then
919 call get_vec(bp, igrid, x, tloc, vec)
920 else
921 do idir=1,ndir
922 call interpolate_var(igrid,ixg^ll,ixm^ll,gridvars(igrid)%w(ixg^t,bp(idir)), &
923 ps(igrid)%x(ixg^t,1:ndim),x,vec(idir))
924 end do
925
926 if (time_advance) then
927 do idir=1,ndir
928 call interpolate_var(igrid,ixg^ll,ixm^ll,gridvars(igrid)%wold(ixg^t,bp(idir)), &
929 ps(igrid)%x(ixg^t,1:ndim),x,vec2(idir))
930 end do
931
932 ! Interpolate in time
933 td = (tloc - global_time) / dt
934 vec(:) = vec2(:) * (1.0d0 - td) + vec(:) * td
935 end if
936
937 ! Fabio June 2024: Renormalize vector by its grid-evaluated norm
938 if (interp_type_particles=='renormalized') then
939 if (time_advance) then
940 if (sqrt(sum(vec(:)**2)) .gt. 0.d0) then
941 call interpolate_var(igrid,ixg^ll,ixm^ll, &
942 sum(gridvars(igrid)%w(ixg^t,bp(:))**2,dim=ndim+1)*td &
943 +sum(gridvars(igrid)%wold(ixg^t,bp(:))**2,dim=ndim+1)*(1.d0-td), &
944 ps(igrid)%x(ixg^t,1:ndim),x,bb)
945 end if
946 else
947 if (sqrt(sum(vec(:)**2)) .gt. 0.d0) then
948 call interpolate_var(igrid,ixg^ll,ixm^ll, &
949 sum(gridvars(igrid)%w(ixg^t,bp(:))**2,dim=ndim+1), &
950 ps(igrid)%x(ixg^t,1:ndim),x,bb)
951 end if
952 vec = vec/sqrt(sum(vec(:)**2))*sqrt(bb)
953 end if
954 end if
955
956 end if
957
958 b(:) = vec(:)
959
960 end subroutine get_bfield
961
962 ! Shorthand for getting specifically the E field at the particle position
963 subroutine get_efield(igrid,x,tloc,e)
966 use mod_geometry
967
968 integer,intent(in) :: igrid
969 double precision,dimension(3), intent(in) :: x
970 double precision, intent(in) :: tloc
971 double precision,dimension(ndir), intent(out) :: e
972 double precision,dimension(ndir) :: vec1, vec2, vec
973 double precision,dimension(ndir) :: vfluid, b
974 double precision,dimension(ndir) :: current
975 double precision :: rho = 1.d0, rho2 = 1.d0
976 double precision :: td
977 integer :: ic^D,idir
978
979 current(:) = 0.d0
980
981 if (associated(usr_particle_analytic)) then
982 call usr_particle_analytic(ep, x, tloc, vec)
983 else if (associated(usr_particle_fields)) then
984 call get_vec(ep, igrid, x, tloc, vec)
985 else
986 call get_bfield(igrid, x, tloc, b)
987 call get_vec(vp, igrid, x, tloc, vfluid)
988 if (particles_eta > zero) then
989 call get_vec(jp, igrid, x, tloc, current)
990 end if
991 if (particles_etah > zero) then
992 call interpolate_var(igrid,ixg^ll,ixm^ll,gridvars(igrid)%w(ixg^t,rhop),ps(igrid)%x(ixg^t,1:ndim),x,rho)
993 if (time_advance) then
994 call interpolate_var(igrid,ixg^ll,ixm^ll,gridvars(igrid)%wold(ixg^t,rhop),ps(igrid)%x(ixg^t,1:ndim),x,rho2)
995 td = (tloc - global_time) / dt
996 rho = rho2 * (1.0d0 - td) + rho * td
997 end if
998 end if
999
1000 ! Build E
1001 select case (coordinate)
1003 vec(1) = -vfluid(2)*b(3)+vfluid(3)*b(2) + particles_eta*current(1) + particles_etah/rho*(current(2)*b(3) - current(3)*b(2))
1004 vec(2) = vfluid(1)*b(3)-vfluid(3)*b(1) + particles_eta*current(2) + particles_etah/rho*(-current(1)*b(3) + current(3)*b(1))
1005 vec(3) = -vfluid(1)*b(2)+vfluid(2)*b(1) + particles_eta*current(3) + particles_etah/rho*(current(1)*b(2) - current(2)*b(1))
1006 case (cylindrical)
1007 vec(r_) = -vfluid(phi_)*b(z_)+vfluid(z_)*b(phi_) + particles_eta*current(r_) + particles_etah/rho*(current(phi_)*b(z_) - current(z_)*b(phi_))
1008 vec(phi_) = vfluid(r_)*b(z_)-vfluid(z_)*b(r_) + particles_eta*current(phi_) + particles_etah/rho*(-current(r_)*b(z_) + current(z_)*b(r_))
1009 vec(z_) = -vfluid(r_)*b(phi_)+vfluid(phi_)*b(r_) + particles_eta*current(z_) + particles_etah/rho*(current(r_)*b(phi_) - current(phi_)*b(r_))
1010 end select
1011 end if
1012
1013 e(:) = vec(:)
1014
1015 end subroutine get_efield
1016
1017 !> Get Lorentz factor from relativistic momentum
1018 pure subroutine get_lfac(u,lfac)
1020 double precision,dimension(3), intent(in) :: u
1021 double precision, intent(out) :: lfac
1022
1023 if (relativistic) then
1024 lfac = sqrt(1.0d0 + sum(u(:)**2)/c_norm**2)
1025 else
1026 lfac = 1.0d0
1027 end if
1028
1029 end subroutine get_lfac
1030
1031 !> Get Lorentz factor from velocity
1032 pure subroutine get_lfac_from_velocity(v,lfac)
1034 double precision,dimension(3), intent(in) :: v
1035 double precision, intent(out) :: lfac
1036
1037 if (relativistic) then
1038 lfac = 1.0d0 / sqrt(1.0d0 - sum(v(:)**2)/c_norm**2)
1039 else
1040 lfac = 1.0d0
1041 end if
1042
1043 end subroutine get_lfac_from_velocity
1044
1045 subroutine cross(a,b,c)
1047 use mod_geometry
1048 double precision, dimension(ndir), intent(in) :: a,b
1049 double precision, dimension(ndir), intent(out) :: c
1050
1051 ! ndir needs to be three for this to work!!!
1052 if(ndir==3) then
1053 select case(coordinate)
1055 c(1) = a(2)*b(3) - a(3)*b(2)
1056 c(2) = a(3)*b(1) - a(1)*b(3)
1057 c(3) = a(1)*b(2) - a(2)*b(1)
1058 case (cylindrical)
1059 c(r_) = a(phi_)*b(z_) - a(z_)*b(phi_)
1060 c(phi_) = a(z_)*b(r_) - a(r_)*b(z_)
1061 c(z_) = a(r_)*b(phi_) - a(phi_)*b(r_)
1062 case default
1063 call mpistop('geometry not implemented in cross(a,b,c)')
1064 end select
1065 else
1066 call mpistop("cross in particles needs to be run with three components!")
1067 end if
1068
1069 end subroutine cross
1070
1071 subroutine interpolate_var(igrid,ixI^L,ixO^L,gf,x,xloc,gfloc)
1073
1074 integer, intent(in) :: igrid,ixI^L, ixO^L
1075 double precision, intent(in) :: gf(ixI^S)
1076 double precision, intent(in) :: x(ixI^S,1:ndim)
1077 double precision, intent(in) :: xloc(1:3)
1078 double precision, intent(out) :: gfloc
1079 double precision :: xd^D, myq, dx1
1080 {^iftwod
1081 double precision :: c00, c10
1082 }
1083 {^ifthreed
1084 double precision :: c0, c1, c00, c10, c01, c11
1085 }
1086 integer :: ic^D, ic1^D, ic2^D, idir
1087
1088 {
1089 if(stretch_type(^d)==stretch_uni) then
1090 ! uniform stretch from xprobmin
1091 ic^d = ceiling(dlog((xloc(^d)-xprobmin^d)*(qstretch(ps(igrid)%level,^d)-one)/&
1092 dxfirst(ps(igrid)%level,^d)+one)/dlog(qstretch(ps(igrid)%level,^d)))-&
1093 (node(pig^d_,igrid)-1)*block_nx^d+nghostcells
1094 else if(stretch_type(^d)==stretch_symm) then
1095 ! symmetric stretch about 0.5*(xprobmin+xprobmax)
1096 if(xloc(^d)<xprobmin^d+xstretch^d) then
1097 ! stretch to left from xprobmin+xstretch
1098 ic^d = block_nx^d-(int(dlog((xprobmin^d+xstretch^d-xloc(^d))*(qstretch(ps(igrid)%level,^d)-one)/&
1099 dxfirst(ps(igrid)%level,^d)+one)/dlog(qstretch(ps(igrid)%level,^d)))-&
1100 (nstretchedblocks(ps(igrid)%level,^d)/2-node(pig^d_,igrid))*block_nx^d)+nghostcells
1101 else if(xloc(^d)>xprobmax^d-xstretch^d) then
1102 ! stretch to right from xprobmax-xstretch
1103 ic^d = ceiling(dlog((xloc(^d)-xprobmax^d+xstretch^d)*(qstretch(ps(igrid)%level,^d)-one)/&
1104 dxfirst(ps(igrid)%level,^d)+one)/dlog(qstretch(ps(igrid)%level,^d)))-&
1105 ((node(pig^d_,igrid)-1+nstretchedblocks(ps(igrid)%level,^d)/2)*block_nx^d-&
1106 domain_nx^d*2**(ps(igrid)%level-1))+nghostcells
1107 else
1108 ! possible non-stretched central part
1109 ic^d = int((xloc(^d)-rnode(rpxmin^d_,igrid))/rnode(rpdx^d_,igrid)) + 1 + nghostcells
1110 end if
1111 else
1112 ! no stretch
1113 ic^d = int((xloc(^d)-rnode(rpxmin^d_,igrid))/rnode(rpdx^d_,igrid)) + 1 + nghostcells
1114 end if
1115 \}
1116
1117 ! linear interpolation:
1118 {
1119 if (x({ic^dd},^d) .lt. xloc(^d)) then
1120 ic1^d = ic^d
1121 else
1122 ic1^d = ic^d -1
1123 end if
1124 ic2^d = ic1^d + 1
1125 \}
1126
1127 {^d&
1128 if(ic1^d.lt.ixglo^d+1 .or. ic2^d.gt.ixghi^d-1) then
1129 print *, 'direction: ',^d
1130 print *, 'position: ',xloc(1:ndim)
1131 print *, 'indices:', ic1^d,ic2^d
1132 call mpistop('Trying to interpolate from out of grid!')
1133 end if
1134 \}
1135
1136 {^ifoned
1137 xd1 = (xloc(1)-x(ic11,1)) / (x(ic21,1) - x(ic11,1))
1138 gfloc = gf(ic11) * (1.0d0 - xd1) + gf(ic21) * xd1
1139 }
1140 {^iftwod
1141 xd1 = (xloc(1)-x(ic11,ic12,1)) / (x(ic21,ic12,1) - x(ic11,ic12,1))
1142 xd2 = (xloc(2)-x(ic11,ic12,2)) / (x(ic11,ic22,2) - x(ic11,ic12,2))
1143 c00 = gf(ic11,ic12) * (1.0d0 - xd1) + gf(ic21,ic12) * xd1
1144 c10 = gf(ic11,ic22) * (1.0d0 - xd1) + gf(ic21,ic22) * xd1
1145 gfloc = c00 * (1.0d0 - xd2) + c10 * xd2
1146 }
1147 {^ifthreed
1148 xd1 = (xloc(1)-x(ic11,ic12,ic13,1)) / (x(ic21,ic12,ic13,1) - x(ic11,ic12,ic13,1))
1149 xd2 = (xloc(2)-x(ic11,ic12,ic13,2)) / (x(ic11,ic22,ic13,2) - x(ic11,ic12,ic13,2))
1150 xd3 = (xloc(3)-x(ic11,ic12,ic13,3)) / (x(ic11,ic12,ic23,3) - x(ic11,ic12,ic13,3))
1151
1152 c00 = gf(ic11,ic12,ic13) * (1.0d0 - xd1) + gf(ic21,ic12,ic13) * xd1
1153 c10 = gf(ic11,ic22,ic13) * (1.0d0 - xd1) + gf(ic21,ic22,ic13) * xd1
1154 c01 = gf(ic11,ic12,ic23) * (1.0d0 - xd1) + gf(ic21,ic12,ic23) * xd1
1155 c11 = gf(ic11,ic22,ic23) * (1.0d0 - xd1) + gf(ic21,ic22,ic23) * xd1
1156
1157 c0 = c00 * (1.0d0 - xd2) + c10 * xd2
1158 c1 = c01 * (1.0d0 - xd2) + c11 * xd2
1159
1160 gfloc = c0 * (1.0d0 - xd3) + c1 * xd3
1161 }
1162
1163 end subroutine interpolate_var
1164
1166 use mod_timing
1168
1169 double precision :: tpartc_avg, tpartc_int_avg, tpartc_io_avg, tpartc_com_avg, tpartc_grid_avg
1170
1171 call mpi_reduce(tpartc,tpartc_avg,1,mpi_double_precision,mpi_sum,0,icomm,ierrmpi)
1172 call mpi_reduce(tpartc_int,tpartc_int_avg,1,mpi_double_precision,mpi_sum,0,icomm,ierrmpi)
1173 call mpi_reduce(tpartc_io,tpartc_io_avg,1,mpi_double_precision,mpi_sum,0,icomm,ierrmpi)
1174 call mpi_reduce(tpartc_com,tpartc_com_avg,1,mpi_double_precision,mpi_sum,0,icomm,ierrmpi)
1175 call mpi_reduce(tpartc_grid,tpartc_grid_avg,1,mpi_double_precision,mpi_sum,0,icomm,ierrmpi)
1176
1177 if (mype ==0) then
1178 tpartc_avg = tpartc_avg/dble(npe)
1179 tpartc_int_avg = tpartc_int_avg/dble(npe)
1180 tpartc_io_avg = tpartc_io_avg/dble(npe)
1181 tpartc_com_avg = tpartc_com_avg/dble(npe)
1182 tpartc_grid_avg = tpartc_grid_avg/dble(npe)
1183 write(*,'(a,f12.3,a)')' Particle handling took : ',tpartc,' sec'
1184 write(*,'(a,f12.2,a)')' Percentage: ',100.0d0*tpartc/(timeloop+smalldouble),' %'
1185 write(*,'(a,f12.3,a)')' Particle IO took : ',tpartc_io_avg,' sec'
1186 write(*,'(a,f12.2,a)')' Percentage: ',100.0d0*tpartc_io_avg/(timeloop+smalldouble),' %'
1187 write(*,'(a,f12.3,a)')' Particle COM took : ',tpartc_com_avg,' sec'
1188 write(*,'(a,f12.2,a)')' Percentage: ',100.0d0*tpartc_com_avg/(timeloop+smalldouble),' %'
1189 write(*,'(a,f12.3,a)')' Particle integration took : ',tpartc_int_avg,' sec'
1190 write(*,'(a,f12.2,a)')' Percentage: ',100.0d0*tpartc_int_avg/(timeloop+smalldouble),' %'
1191 write(*,'(a,f12.3,a)')' Particle init grid took : ',tpartc_grid_avg,' sec'
1192 write(*,'(a,f12.2,a)')' Percentage: ',100.0d0*tpartc_grid_avg/(timeloop+smalldouble),' %'
1193 end if
1194
1195 end subroutine time_spent_on_particles
1196
1197 subroutine read_particles_snapshot(file_exists)
1199
1200 logical,intent(out) :: file_exists
1201 character(len=std_len) :: filename
1202 integer :: mynpayload, mynparticles, pos, index_latest
1203
1204 ! some initialisations:
1206 mynparticles = 0
1207 nparticles = 0
1208 it_particles = 0
1209
1210 ! open the snapshot file on the headnode
1211 file_exists=.false.
1212 if (mype == 0) then
1213 ! Strip restart_from_filename of the ending
1214 pos = scan(restart_from_file, '.dat', back=.true.)
1215 do index_latest=9999,0,-1
1216 write(filename,"(a,a,i6.6,a)") trim(restart_from_file(1:pos-8)),'_particles',index_latest,'.dat'
1217 INQUIRE(file=filename, exist=file_exists)
1218 if(file_exists) exit
1219 end do
1220 if (.not. file_exists) then
1221 write(*,*) 'WARNING: File '//trim(filename)//' with particle data does not exist.'
1222 write(*,*) 'Initialising particles from user or default routines'
1223 else
1224 open(unit=unitparticles,file=filename,form='unformatted',action='read',status='unknown',access='stream')
1225 read(unitparticles) nparticles,it_particles,mynpayload
1226 if (mynpayload .ne. npayload) &
1227 call mpistop('npayload in restart file does not match npayload in mod_particles')
1228 end if
1229 end if
1230
1231 call mpi_bcast(file_exists,1,mpi_logical,0,icomm,ierrmpi)
1232 if (.not. file_exists) return
1233
1234 call mpi_bcast(nparticles,1,mpi_integer,0,icomm,ierrmpi)
1235 call mpi_bcast(it_particles,1,mpi_integer,0,icomm,ierrmpi)
1236
1237 do while (mynparticles .lt. nparticles)
1238 if (mype == 0) then
1240 .and. mynparticles .lt. nparticles)
1242 mynparticles = mynparticles + 1
1243 end do
1244 end if ! mype==0
1245 call mpi_bcast(mynparticles,1,mpi_integer,0,icomm,ierrmpi)
1247 end do
1248
1249 if (mype == 0) close(unit=unitparticles)
1250
1252
1253 end subroutine read_particles_snapshot
1254
1256
1257 integer :: index,igrid_particle,ipe_particle
1258
1259 read(unitparticles) index
1260 allocate(particle(index)%self)
1261 allocate(particle(index)%payload(1:npayload))
1262 particle(index)%self%index = index
1263
1264 read(unitparticles) particle(index)%self%follow
1265 read(unitparticles) particle(index)%self%q
1266 read(unitparticles) particle(index)%self%m
1267 read(unitparticles) particle(index)%self%time
1268 read(unitparticles) particle(index)%self%dt
1269 read(unitparticles) particle(index)%self%x
1270 read(unitparticles) particle(index)%self%u
1271 read(unitparticles) particle(index)%payload(1:npayload)
1272
1273! if (particle(index)%self%follow) print*, 'follow index:', index
1274
1275 call find_particle_ipe(particle(index)%self%x,igrid_particle,ipe_particle)
1276 particle(index)%igrid = igrid_particle
1277 particle(index)%ipe = ipe_particle
1278
1280
1281 end subroutine read_from_snapshot
1282
1285
1286 character(len=std_len) :: filename
1287! type(particle_t), dimension(nparticles_per_cpu_hi) :: send_particles
1288! type(particle_t), dimension(nparticles_per_cpu_hi) :: receive_particles
1289! double precision, dimension(npayload,nparticles_per_cpu_hi) :: send_payload
1290! double precision, dimension(npayload,nparticles_per_cpu_hi) :: receive_payload
1291 type(particle_t), allocatable, dimension(:) :: send_particles
1292 type(particle_t), allocatable, dimension(:) :: receive_particles
1293 double precision, allocatable, dimension(:,:) :: send_payload
1294 double precision, allocatable, dimension(:,:) :: receive_payload
1295 integer :: status(MPI_STATUS_SIZE)
1296 integer,dimension(0:npe-1) :: receive_n_particles_for_output_from_ipe
1297 integer :: ipe, ipart, iipart, send_n_particles_for_output
1298 logical,save :: file_exists=.false.
1299 integer :: snapshotnumber
1300
1301 if (time_advance) then
1302 snapshotnumber = snapshotnext
1303 else
1304 snapshotnumber = nint(t_next_output/dtsave_particles)
1305 end if
1306
1307 receive_n_particles_for_output_from_ipe(:) = 0
1308
1309 ! open the snapshot file on the headnode
1310 if (mype .eq. 0) then
1311 write(filename,"(a,a,i6.6,a)") trim(base_filename),'_particles',snapshotnumber,'.dat'
1312 INQUIRE(file=filename, exist=file_exists)
1313 if (.not. file_exists) then
1314 open(unit=unitparticles,file=filename,form='unformatted',status='new',access='stream')
1315 else
1316 open(unit=unitparticles,file=filename,form='unformatted',status='replace',access='stream')
1317 end if
1319 end if
1320 if (npe==1) then
1321 do iipart=1,nparticles_on_mype;ipart=particles_on_mype(iipart);
1322 call append_to_snapshot(particle(ipart)%self,particle(ipart)%payload)
1323 end do
1324 return
1325 end if
1326
1327 if (mype .ne. 0) then
1328 call mpi_send(nparticles_on_mype,1,mpi_integer,0,mype,icomm,ierrmpi)
1329 ! fill the send_buffer
1330 send_n_particles_for_output = nparticles_on_mype
1331 allocate(send_particles(1:send_n_particles_for_output))
1332 allocate(send_payload(1:npayload,1:send_n_particles_for_output))
1333 do iipart=1,nparticles_on_mype;ipart=particles_on_mype(iipart);
1334 send_particles(iipart) = particle(ipart)%self
1335 send_payload(1:npayload,iipart) = particle(ipart)%payload(1:npayload)
1336 end do
1337 else
1338 ! get number of particles on other ipes
1339 do ipe=1,npe-1
1340 call mpi_recv(receive_n_particles_for_output_from_ipe(ipe),1,mpi_integer,ipe,ipe,icomm,status,ierrmpi)
1341 end do
1342 end if
1343
1344 if (mype .ne. 0) then
1345 call mpi_send(send_particles,send_n_particles_for_output,type_particle,0,mype,icomm,ierrmpi)
1346 call mpi_send(send_payload,npayload*send_n_particles_for_output,mpi_double_precision,0,mype,icomm,ierrmpi)
1347 deallocate(send_particles)
1348 deallocate(send_payload)
1349 end if
1350
1351 if (mype==0) then
1352 ! first output own particles (already in receive buffer)
1353 do iipart=1,nparticles_on_mype;ipart=particles_on_mype(iipart);
1354 call append_to_snapshot(particle(ipart)%self,particle(ipart)%payload)
1355 end do
1356 ! now output the particles sent from the other ipes
1357 do ipe=1,npe-1
1358 allocate(receive_particles(1:receive_n_particles_for_output_from_ipe(ipe)))
1359 allocate(receive_payload(1:npayload,1:receive_n_particles_for_output_from_ipe(ipe)))
1360 call mpi_recv(receive_particles,receive_n_particles_for_output_from_ipe(ipe),type_particle,ipe,ipe,icomm,status,ierrmpi)
1361 call mpi_recv(receive_payload,npayload*receive_n_particles_for_output_from_ipe(ipe),mpi_double_precision,ipe,ipe,icomm,status,ierrmpi)
1362 do ipart=1,receive_n_particles_for_output_from_ipe(ipe)
1363 call append_to_snapshot(receive_particles(ipart),receive_payload(1:npayload,ipart))
1364 end do ! ipart
1365 deallocate(receive_particles)
1366 deallocate(receive_payload)
1367 end do ! ipe
1368 close(unit=unitparticles)
1369 end if ! mype == 0
1370
1371 end subroutine write_particles_snapshot
1372
1373 subroutine append_to_snapshot(myparticle,mypayload)
1374
1375 type(particle_t),intent(in) :: myparticle
1376 double precision :: mypayload(1:npayload)
1377
1378 write(unitparticles) myparticle%index
1379 write(unitparticles) myparticle%follow
1380 write(unitparticles) myparticle%q
1381 write(unitparticles) myparticle%m
1382 write(unitparticles) myparticle%time
1383 write(unitparticles) myparticle%dt
1384 write(unitparticles) myparticle%x
1385 write(unitparticles) myparticle%u
1386 write(unitparticles) mypayload(1:npayload)
1387
1388 end subroutine append_to_snapshot
1389
1392
1393 integer :: iipart, ipart, icomp
1394 character(len=std_len) :: filename
1395 character(len=1024) :: line, strdata
1396
1397 do iipart=1,nparticles_on_mype;ipart=particles_on_mype(iipart);
1398 if (particle(ipart)%self%follow) then
1399 filename = make_particle_filename(particle(ipart)%self%time,particle(ipart)%self%index,'individual')
1400 open(unit=unitparticles,file=filename,status='replace')
1401 line=''
1402 do icomp=1, npayload
1403 write(strdata,"(a,i2.2,a)") 'payload',icomp,','
1404 line = trim(line)//trim(strdata)
1405 end do
1406 write(unitparticles,"(a,a,a)") 'time,dt,x1,x2,x3,u1,u2,u3,',trim(line),'ipe,iteration,index'
1407 close(unit=unitparticles)
1408 end if
1409 end do
1410
1411 end subroutine init_particles_output
1412
1413 subroutine select_active_particles(end_time)
1415 double precision, intent(in) :: end_time
1416
1417 double precision :: t_min_mype
1418 double precision :: local_selection(2),global_selection(2)
1419 integer :: ipart, iipart
1420 logical :: activate
1421
1422 t_min_mype = bigdouble
1424
1425 do iipart = 1, nparticles_on_mype
1426 ipart = particles_on_mype(iipart);
1427 activate = (particle(ipart)%self%time < end_time)
1428 t_min_mype = min(t_min_mype, particle(ipart)%self%time)
1429
1430 if (activate) then
1433 end if
1434 end do
1435
1436 local_selection(1)=t_min_mype
1437 local_selection(2)=1.d0
1438 if(nparticles_active_on_mype>0) local_selection(2)=0.d0
1439 call mpi_allreduce(local_selection,global_selection,2,mpi_double_precision, &
1440 mpi_min,icomm,ierrmpi)
1441 min_particle_time=global_selection(1)
1442 particle_any_active=(global_selection(2)==0.d0)
1443
1444 end subroutine select_active_particles
1445
1446 subroutine locate_particle(index,igrid_particle,ipe_particle)
1447 ! given the particles unique index, tell me on which cpu and igrid it is
1448 ! returns -1,-1 if particle was not found
1450
1451 integer, intent(in) :: index
1452 integer, intent(out) :: igrid_particle, ipe_particle
1453 integer :: iipart,ipart,ipe_has_particle,ipe
1454 integer,dimension(0:1) :: buff
1455 logical :: has_particle(0:npe-1)
1456
1457 has_particle(:) = .false.
1458 do iipart=1,nparticles_on_mype;ipart=particles_on_mype(iipart);
1459 if (particle(ipart)%self%index == index) then
1460 has_particle(mype) = .true.
1461 exit
1462 end if
1463 end do
1464
1465 if (has_particle(mype)) then
1466 buff(0) = particle(ipart)%igrid
1467 buff(1) = mype
1468 end if
1469
1470 if (npe>0) call mpi_allgather(has_particle(mype),1,mpi_logical,has_particle,1,mpi_logical,icomm,ierrmpi)
1471
1472 ipe_has_particle = -1
1473 do ipe=0,npe-1
1474 if (has_particle(ipe) .eqv. .true.) then
1475 ipe_has_particle = ipe
1476 exit
1477 end if
1478 end do
1479
1480 if (ipe_has_particle .ne. -1) then
1481 if (npe>0) call mpi_bcast(buff,2,mpi_integer,ipe_has_particle,icomm,ierrmpi)
1482 igrid_particle=buff(0)
1483 ipe_particle = buff(1)
1484 else
1485 igrid_particle=-1
1486 ipe_particle = -1
1487 end if
1488
1489 end subroutine locate_particle
1490
1491 subroutine find_particle_ipe(x,igrid_particle,ipe_particle)
1493 use mod_slice, only: get_igslice
1495
1496 double precision, intent(in) :: x(3)
1497 integer, intent(out) :: igrid_particle, ipe_particle
1498 integer :: ig(ndir,nlevelshi), ig_lvl(nlevelshi)
1499 integer :: idim, ic(ndim)
1500 type(tree_node_ptr) :: branch
1501
1502 ! first check if the particle is in the domain
1503 if (.not. particle_in_domain(x)) then
1504 igrid_particle = -1
1505 ipe_particle = -1
1506 return
1507 end if
1508
1509 ! get the index on each level
1510 do idim = 1, ndim
1511 call get_igslice(idim,x(idim), ig_lvl)
1512 ig(idim,:) = ig_lvl
1513 end do
1514
1515 ! traverse the tree until leaf is found
1516 branch=tree_root({ig(^d,1)})
1517 do while (.not.branch%node%leaf)
1518 {ic(^d)=ig(^d,branch%node%level+1) - 2 * branch%node%ig^d +2\}
1519 branch%node => branch%node%child({ic(^d)})%node
1520 end do
1521
1522 igrid_particle = branch%node%igrid
1523 ipe_particle = branch%node%ipe
1524
1525 end subroutine find_particle_ipe
1526
1527 !> Check if particle is inside computational domain
1528 logical function particle_in_domain(x)
1530 double precision, dimension(ndim), intent(in) :: x
1531
1532 particle_in_domain = all(x >= [ xprobmin^d ]) .and. &
1533 all(x < [ xprobmax^d ]) ! Jannis: as before < instead of <= here, necessary?
1534 end function particle_in_domain
1535
1536 !> Quick check if particle is still in igrid
1537 logical function particle_in_igrid(ipart, igrid)
1539 integer, intent(in) :: igrid, ipart
1540 double precision :: x(ndim), grid_rmin(ndim), grid_rmax(ndim)
1541
1542 ! First check if the igrid is still there
1543 if (.not. allocated(ps(igrid)%w)) then
1544 particle_in_igrid = .false.
1545 else
1546 grid_rmin = [ {rnode(rpxmin^d_,igrid)} ]
1547 grid_rmax = [ {rnode(rpxmax^d_,igrid)} ]
1548 x = particle(ipart)%self%x(1:ndim)
1549 particle_in_igrid = all(x >= grid_rmin) .and. all(x < grid_rmax)
1550 end if
1551 end function particle_in_igrid
1552
1553 subroutine set_neighbor_ipe()
1555
1556 integer :: igrid, iigrid,ipe
1557 integer :: my_neighbor_type, i^D
1558 logical :: ipe_is_neighbor(0:npe-1)
1559
1560 ipe_is_neighbor(:) = .false.
1561 do iigrid=1,igridstail; igrid=igrids(iigrid);
1562
1563 {do i^db=-1,1\}
1564 if (i^d==0|.and.) then
1565 cycle
1566 end if
1567 my_neighbor_type=neighbor_type(i^d,igrid)
1568
1569 select case (my_neighbor_type)
1570 case (neighbor_boundary) ! boundary
1571 ! do nothing
1572 case (neighbor_coarse) ! fine-coarse
1573 call ipe_fc(i^d,igrid,ipe_is_neighbor)
1574 case (neighbor_sibling) ! same level
1575 call ipe_srl(i^d,igrid,ipe_is_neighbor)
1576 case (neighbor_fine) ! coarse-fine
1577 call ipe_cf(i^d,igrid,ipe_is_neighbor)
1578 end select
1579
1580 {end do\}
1581
1582 end do
1583
1584 ! remove self as neighbor
1585 ipe_is_neighbor(mype) = .false.
1586
1587 ! Now make the list of neighbors
1588 npe_neighbors = 0
1589 do ipe=0,npe-1
1590 if (ipe_is_neighbor(ipe)) then
1593 end if
1594 end do ! ipe
1595
1596 end subroutine set_neighbor_ipe
1597
1598 subroutine ipe_fc(i^D,igrid,ipe_is_neighbor)
1600 integer, intent(in) :: i^D, igrid
1601 logical, intent(inout) :: ipe_is_neighbor(0:npe-1)
1602
1603 ipe_is_neighbor(neighbor(2,i^d,igrid)) = .true.
1604
1605 end subroutine ipe_fc
1606
1607 subroutine ipe_srl(i^D,igrid,ipe_is_neighbor)
1609 integer, intent(in) :: i^D, igrid
1610 logical, intent(inout) :: ipe_is_neighbor(0:npe-1)
1611
1612 ipe_is_neighbor(neighbor(2,i^d,igrid)) = .true.
1613
1614 end subroutine ipe_srl
1615
1616 subroutine ipe_cf(i^D,igrid,ipe_is_neighbor)
1618 integer, intent(in) :: i^D, igrid
1619 logical, intent(inout) :: ipe_is_neighbor(0:npe-1)
1620 integer :: ic^D, inc^D
1621
1622 {do ic^db=1+int((1-i^db)/2),2-int((1+i^db)/2)
1623 inc^db=2*i^db+ic^db
1624 \}
1625 ipe_is_neighbor( neighbor_child(2,inc^d,igrid) ) = .true.
1626
1627 {end do\}
1628
1629 end subroutine ipe_cf
1630
1633
1634 character(len=std_len) :: filename
1635! type(particle_t), dimension(nparticles_per_cpu_hi) :: send_particles
1636! double precision, dimension(npayload,nparticles_per_cpu_hi) :: send_payload
1637 type(particle_t), allocatable, dimension(:) :: send_particles
1638 double precision, allocatable, dimension(:,:) :: send_payload
1639 double precision :: tout
1640 integer :: send_n_particles_for_output, cc
1641 integer :: nout
1642 integer :: ipart,iipart
1643
1644 if (write_individual) then
1645 call output_individual()
1646 end if
1647
1648 if (write_ensemble) then
1649 send_n_particles_for_output = 0
1650
1651 do iipart=1,nparticles_on_mype
1652 ipart=particles_on_mype(iipart);
1653
1654 ! have to send particle to rank zero for output
1655 if (mod(particle(ipart)%self%index,downsample_particles) .eq. 0) then
1656 send_n_particles_for_output = send_n_particles_for_output + 1
1657 end if
1658 end do
1659
1660 allocate(send_particles(1:send_n_particles_for_output))
1661 allocate(send_payload(npayload,1:send_n_particles_for_output))
1662 cc = 1
1663 do iipart=1,nparticles_on_mype
1664 ipart=particles_on_mype(iipart);
1665
1666 ! have to send particle to rank zero for output
1667 if (mod(particle(ipart)%self%index,downsample_particles) .eq. 0) then
1668 send_particles(cc) = particle(ipart)%self
1669 send_payload(1:npayload,cc) = particle(ipart)%payload(1:npayload)
1670 cc=cc+1
1671 end if
1672 end do
1673
1674 call output_ensemble(send_n_particles_for_output,send_particles, &
1675 send_payload,'ensemble')
1676 deallocate(send_particles)
1677 deallocate(send_payload)
1678 end if
1679
1680 end subroutine write_particle_output
1681
1682 character(len=128) function make_particle_filename(tout,index,type)
1684
1685 character(len=*), intent(in) :: type
1686 double precision, intent(in) :: tout
1687 integer, intent(in) :: index
1688 integer :: nout, mysnapshot
1689
1690 select case(type)
1691 case ('ensemble')
1692 nout = nint(tout / dtsave_particles)
1693 write(make_particle_filename,"(a,a,i6.6,a)") trim(base_filename),'_ensemble',nout,'.csv'
1694 case ('destroy')
1695 write(make_particle_filename,"(a,a)") trim(base_filename),'_destroyed.csv'
1696 case ('individual')
1697 write(make_particle_filename,"(a,a,i6.6,a)") trim(base_filename),'_particle',index,'.csv'
1698 end select
1699
1700 end function make_particle_filename
1701
1702 subroutine output_ensemble(send_n_particles_for_output,send_particles,send_payload,typefile)
1704
1705 integer, intent(in) :: send_n_particles_for_output
1706 type(particle_t), dimension(send_n_particles_for_output), intent(in) :: send_particles
1707 double precision, dimension(npayload,send_n_particles_for_output), intent(in) :: send_payload
1708 character(len=*), intent(in) :: typefile
1709 character(len=std_len) :: filename
1710 type(particle_t), allocatable, dimension(:) :: receive_particles
1711 double precision, allocatable, dimension(:,:) :: receive_payload
1712 integer :: status(MPI_STATUS_SIZE)
1713 integer,dimension(0:npe-1) :: receive_n_particles_for_output_from_ipe
1714 integer :: ipe, ipart, nout
1715 logical :: file_exists
1716
1717 receive_n_particles_for_output_from_ipe(:) = 0
1718
1719 call mpi_allgather(send_n_particles_for_output, 1, mpi_integer, &
1720 receive_n_particles_for_output_from_ipe, 1, mpi_integer, icomm, ierrmpi)
1721
1722 ! If there are no particles to be written, skip the output
1723 if (sum(receive_n_particles_for_output_from_ipe(:)) == 0) return
1724
1725 if (mype > 0) then
1726 call mpi_send(send_particles,send_n_particles_for_output,type_particle,0,mype,icomm,ierrmpi)
1727 call mpi_send(send_payload,npayload*send_n_particles_for_output,mpi_double_precision,0,mype,icomm,ierrmpi)
1728 else
1729 ! Create file and write header
1730 if(typefile=='destroy') then ! Destroyed file
1731 write(filename,"(a,a,i6.6,a)") trim(base_filename) // '_', &
1732 trim(typefile) // '.csv'
1734 inquire(file=filename, exist=file_exists)
1735
1736 if (.not. file_exists) then
1737 open(unit=unitparticles, file=filename)
1738 write(unitparticles,"(a)") trim(csv_header)
1739 else
1740 open(unit=unitparticles, file=filename, position='append')
1741 end if
1742
1743 else ! Ensemble file
1744 write(filename,"(a,a,i6.6,a)") trim(base_filename) // '_', &
1745 trim(typefile) // '_', nint(t_next_output/dtsave_particles),'.csv'
1746 open(unit=unitparticles,file=filename)
1747 write(unitparticles,"(a)") trim(csv_header)
1748 end if
1749
1750 ! Write own particles
1751 do ipart=1,send_n_particles_for_output
1752 call output_particle(send_particles(ipart),send_payload(1:npayload,ipart),0,unitparticles)
1753 end do
1754
1755 ! Write particles from other tasks
1756 do ipe=1,npe-1
1757 allocate(receive_particles(1:receive_n_particles_for_output_from_ipe(ipe)))
1758 allocate(receive_payload(1:npayload,1:receive_n_particles_for_output_from_ipe(ipe)))
1759 call mpi_recv(receive_particles,receive_n_particles_for_output_from_ipe(ipe),type_particle,ipe,ipe,icomm,status,ierrmpi)
1760 call mpi_recv(receive_payload,npayload*receive_n_particles_for_output_from_ipe(ipe),mpi_double_precision,ipe,ipe,icomm,status,ierrmpi)
1761 do ipart=1,receive_n_particles_for_output_from_ipe(ipe)
1762 call output_particle(receive_particles(ipart),receive_payload(1:npayload,ipart),ipe,unitparticles)
1763 end do ! ipart
1764 deallocate(receive_particles)
1765 deallocate(receive_payload)
1766 end do ! ipe
1767
1768 close(unitparticles)
1769 end if
1770
1771 end subroutine output_ensemble
1772
1775 character(len=std_len) :: filename
1776 integer :: ipart,iipart,ipe
1777 logical :: file_exists
1778
1779 do iipart=1,nparticles_on_mype
1780 ipart=particles_on_mype(iipart)
1781
1782 ! should the particle be dumped?
1783 if (particle(ipart)%self%follow) then
1784 write(filename,"(a,a,i6.6,a)") trim(base_filename), '_particle_', &
1785 particle(ipart)%self%index, '.csv'
1786 inquire(file=filename, exist=file_exists)
1787
1788 ! Create empty file and write header on first iteration, or when the
1789 ! file does not exist yet
1790 if (it_particles == 0 .or. .not. file_exists) then
1791 open(unit=unitparticles, file=filename)
1792 write(unitparticles,"(a)") trim(csv_header)
1793 else
1794 open(unit=unitparticles, file=filename, position='append')
1795 end if
1796
1797 call output_particle(particle(ipart)%self,&
1798 particle(ipart)%payload(1:npayload),mype,unitparticles)
1799
1800 close(unitparticles)
1801 end if
1802 end do
1803
1804 end subroutine output_individual
1805
1806 subroutine output_particle(myparticle,payload,ipe,file_unit)
1808 use mod_geometry
1809
1810 type(particle_t),intent(in) :: myparticle
1811 double precision, intent(in) :: payload(npayload)
1812 integer, intent(in) :: ipe
1813 integer, intent(in) :: file_unit
1814 double precision :: x(3), u(3)
1815 integer :: icomp
1816
1817 ! convert and normalize the position
1818 if (nocartesian) then
1819 select case(coordinate)
1821 x = myparticle%x*length_convert_factor
1822 case(cylindrical)
1823 x(r_) = myparticle%x(r_)*length_convert_factor
1824 x(z_) = myparticle%x(z_)*length_convert_factor
1825 x(phi_) = myparticle%x(phi_)
1826 case(spherical)
1827 x(:) = myparticle%x(:)
1828 x(1) = x(1)*length_convert_factor
1829 end select
1830 else
1831 call partcoord_to_cartesian(myparticle%x,x)
1832 x(:) = x(:)*length_convert_factor
1833 end if
1834
1835 u = myparticle%u(1:3) * integrator_velocity_factor
1836
1837 write(file_unit, csv_format) myparticle%time, myparticle%dt, x(1:3), &
1838 u, payload(1:npayload), ipe, it_particles, &
1839 myparticle%index
1840
1841 end subroutine output_particle
1842
1843 !> Convert position to Cartesian coordinates
1844 subroutine partcoord_to_cartesian(xp,xpcart)
1845 ! conversion of particle coordinate from cylindrical/spherical to cartesian coordinates done here
1847 use mod_geometry
1848
1849 double precision, intent(in) :: xp(1:3)
1850 double precision, intent(out) :: xpcart(1:3)
1851
1852 select case (coordinate)
1854 xpcart(1:3) = xp(1:3)
1855 case (cylindrical)
1856 xpcart(1) = xp(r_)*cos(xp(phi_))
1857 xpcart(2) = xp(r_)*sin(xp(phi_))
1858 xpcart(3) = xp(z_)
1859 case (spherical)
1860 xpcart(1) = xp(1)*sin(xp(2))*cos(xp(3))
1861 {^iftwod
1862 xpcart(2) = xp(1)*cos(xp(2))
1863 xpcart(3) = xp(1)*sin(xp(2))*sin(xp(3))}
1864 {^ifthreed
1865 xpcart(2) = xp(1)*sin(xp(2))*sin(xp(3))
1866 xpcart(3) = xp(1)*cos(xp(2))}
1867 case default
1868 write(*,*) "No converter for coordinate=",coordinate
1869 end select
1870
1871 end subroutine partcoord_to_cartesian
1872
1873 !> Convert to noncartesian coordinates
1874 subroutine partcoord_from_cartesian(xp,xpcart)
1875 ! conversion of particle coordinate from Cartesian to cylindrical/spherical coordinates done here
1877 use mod_geometry
1878
1879 double precision, intent(in) :: xpcart(1:3)
1880 double precision, intent(out) :: xp(1:3)
1881 double precision :: xx, yy, zz
1882 double precision :: rr, tth, pphi
1883
1884 select case (coordinate)
1886 xp(1:3)=xpcart(1:3)
1887 case (cylindrical)
1888 xp(r_) = sqrt(xpcart(1)**2 + xpcart(2)**2)
1889 xp(phi_) = atan2(xpcart(2),xpcart(1))
1890 if (xp(phi_) .lt. 0.d0) xp(phi_) = 2.0d0*dpi + xp(phi_)
1891 xp(z_) = xpcart(3)
1892 case (spherical)
1893 xx = xpcart(1)
1894 {^iftwod
1895 yy = xpcart(3)
1896 zz = xpcart(2)}
1897 {^ifthreed
1898 yy = xpcart(2)
1899 zz = xpcart(3)}
1900 rr = sqrt(xx**2 + yy**2 + zz**2)
1901 tth = acos(zz/rr)
1902 if (yy > 0.d0) then
1903 pphi = acos(xx/sqrt(xx**2+yy**2))
1904 else
1905 pphi = 2.d0*dpi - acos(xx/sqrt(xx**2+yy**2))
1906 endif
1907 xp(1:3) = (/ rr, tth, pphi /)
1908 case default
1909 write(*,*) "No converter for coordinate=",coordinate
1910 end select
1911
1912 end subroutine partcoord_from_cartesian
1913
1914 !> Convert vector to Cartesian coordinates
1915 subroutine partvec_to_cartesian(xp,up,upcart)
1916 ! conversion of a generic vector from cylindrical/spherical to cartesian coordinates done here
1918 use mod_geometry
1919
1920 double precision, intent(in) :: xp(1:3),up(1:3)
1921 double precision, intent(out) :: upcart(1:3)
1922
1923 select case (coordinate)
1925 upcart(1:3)=up(1:3)
1926 case (cylindrical)
1927 upcart(1) = cos(xp(phi_)) * up(r_) - sin(xp(phi_)) * up(phi_)
1928 upcart(2) = cos(xp(phi_)) * up(phi_) + sin(xp(phi_)) * up(r_)
1929 upcart(3) = up(z_)
1930 case (spherical)
1931 upcart(1) = up(1)*sin(xp(2))*cos(xp(3)) + up(2)*cos(xp(2))*cos(xp(3)) - up(3)*sin(xp(3))
1932 {^iftwod
1933 upcart(2) = up(1)*cos(xp(2)) - up(2)*sin(xp(2))
1934 upcart(3) = up(1)*sin(xp(2))*sin(xp(3)) + up(2)*cos(xp(2))*sin(xp(3)) + up(3)*cos(xp(3))}
1935 {^ifthreed
1936 upcart(2) = up(1)*sin(xp(2))*sin(xp(3)) + up(2)*cos(xp(2))*sin(xp(3)) + up(3)*cos(xp(3))
1937 upcart(3) = up(1)*cos(xp(2)) - up(2)*sin(xp(2))}
1938 case default
1939 write(*,*) "No converter for coordinate=",coordinate
1940 end select
1941
1942 end subroutine partvec_to_cartesian
1943
1944 !> Convert vector from Cartesian coordinates
1945 subroutine partvec_from_cartesian(xp,up,upcart)
1946 ! conversion of a generic vector to cylindrical/spherical from cartesian coordinates done here
1948 use mod_geometry
1949
1950 double precision, intent(in) :: xp(1:3),upcart(1:3)
1951 double precision, intent(out) :: up(1:3)
1952
1953 select case (coordinate)
1955 up(1:3) = upcart(1:3)
1956 case (cylindrical)
1957 up(r_) = cos(xp(phi_)) * upcart(1) + sin(xp(phi_)) * upcart(2)
1958 up(phi_) = -sin(xp(phi_)) * upcart(1) + cos(xp(phi_)) * upcart(2)
1959 up(z_) = upcart(3)
1960 case (spherical)
1961 {^iftwod
1962 up(1) = upcart(1)*sin(xp(2))*cos(xp(3)) + upcart(3)*sin(xp(2))*sin(xp(3)) + upcart(2)*cos(xp(2))
1963 up(2) = upcart(1)*cos(xp(2))*cos(xp(3)) + upcart(3)*cos(xp(2))*sin(xp(3)) - upcart(2)*sin(xp(2))
1964 up(3) = -upcart(1)*sin(xp(3)) + upcart(3)*cos(xp(3))}
1965 {^ifthreed
1966 up(1) = upcart(1)*sin(xp(2))*cos(xp(3)) + upcart(2)*sin(xp(2))*sin(xp(3)) + upcart(3)*cos(xp(2))
1967 up(2) = upcart(1)*cos(xp(2))*cos(xp(3)) + upcart(2)*cos(xp(2))*sin(xp(3)) - upcart(3)*sin(xp(2))
1968 up(3) = -upcart(1)*sin(xp(3)) + upcart(2)*cos(xp(3))}
1969 case default
1970 write(*,*) "No converter for coordinate=",coordinate
1971 end select
1972
1973 end subroutine partvec_from_cartesian
1974
1975 !> Fix particle position when crossing the 0,2pi boundary in noncartesian coordinates
1976 recursive subroutine fix_phi_crossing(xp,igrid)
1978 use mod_geometry
1979
1980 integer, intent(in) :: igrid
1981 double precision, intent(inout) :: xp(1:3)
1982 double precision :: xpmod(1:3)
1983 integer :: phiind
1984
1985 select case (coordinate)
1987 ! Do nothing
1988 return
1989 case (cylindrical)
1990 phiind = phi_
1991 case (spherical)
1992 phiind = 3
1993 end select
1994
1995 xpmod = xp
1996
1997 ! Fix phi-boundary crossing
1998 ! Case 1: particle has crossed from ~2pi to ~0
1999 xpmod(phiind) = xp(phiind) + 2.d0*dpi
2000 if ((.not. point_in_igrid_ghostc(xp,igrid,0)) &
2001 .and. (point_in_igrid_ghostc(xpmod,igrid,0))) then
2002 xp(phiind) = xpmod(phiind)
2003 return
2004 end if
2005 ! Case 2: particle has crossed from ~0 to ~2pi
2006 xpmod(phiind) = xp(phiind) - 2.d0*dpi
2007 if ((.not. point_in_igrid_ghostc(xp,igrid,0)) &
2008 .and. (point_in_igrid_ghostc(xpmod,igrid,0))) then
2009 xp(phiind) = xpmod(phiind)
2010 return
2011 end if
2012
2013 end subroutine fix_phi_crossing
2014
2015 !> Quick check if particle coordinate is inside igrid
2016 !> (ghost cells included, except the last ngh)
2017 recursive logical function point_in_igrid_ghostc(x, igrid, ngh)
2019 use mod_geometry
2020 integer, intent(in) :: igrid, ngh
2021 double precision, intent(in) :: x(ndim)
2022 double precision :: grid_rmin(ndim), grid_rmax(ndim)
2023
2024 ! TODO: this does not work with stretched coordinates!
2025 ! TODO: but phi should not be stretched in principle...
2026 grid_rmin = [ {rnode(rpxmin^d_,igrid)-rnode(rpdx^d_,igrid)*(dble(nghostcells-ngh)-0.5d0)} ]
2027 grid_rmax = [ {rnode(rpxmax^d_,igrid)+rnode(rpdx^d_,igrid)*(dble(nghostcells-ngh)-0.5d0)} ]
2028 point_in_igrid_ghostc = all(x >= grid_rmin) .and. all(x < grid_rmax)
2029 end function point_in_igrid_ghostc
2030
2031
2032 !=============================================================================
2033
2034
2035! subroutine comm_particles_old()
2036! use mod_global_parameters
2037!
2038! integer :: ipart, iipart, igrid_particle, ipe_particle, ipe, iipe
2039! integer :: index
2040! integer :: tag_send, tag_receive, tag_send_p, tag_receive_p, send_buff, rcv_buff
2041! integer :: status(MPI_STATUS_SIZE)
2042! integer, dimension(0:npe-1) :: send_n_particles_to_ipe
2043! integer, dimension(0:npe-1) :: receive_n_particles_from_ipe
2044! type(particle_t), dimension(nparticles_per_cpu_hi) :: send_particles
2045! type(particle_t), dimension(nparticles_per_cpu_hi) :: receive_particles
2046! double precision, dimension(npayload,nparticles_per_cpu_hi) :: send_payload
2047! double precision, dimension(npayload,nparticles_per_cpu_hi) :: receive_payload
2048! integer, dimension(nparticles_per_cpu_hi,0:npe-1) :: particle_index_to_be_sent_to_ipe
2049! integer, dimension(nparticles_per_cpu_hi,0:npe-1) :: particle_index_to_be_received_from_ipe
2050! integer, dimension(nparticles_per_cpu_hi) :: particle_index_to_be_destroyed
2051! integer :: destroy_n_particles_mype
2052! logical :: BC_applied
2053! integer, allocatable, dimension(:) :: sndrqst, rcvrqst
2054! integer :: isnd, ircv
2055! integer,dimension(npe_neighbors,nparticles_per_cpu_hi) :: payload_index
2056! send_n_particles_to_ipe(:) = 0
2057! receive_n_particles_from_ipe(:) = 0
2058! destroy_n_particles_mype = 0
2059!
2060! ! check if and where to send each particle, destroy if necessary
2061! do iipart=1,nparticles_on_mype;ipart=particles_on_mype(iipart);
2062!
2063! ! first check if the particle should be destroyed because t>tmax in a fixed snapshot
2064! if ( .not.time_advance .and. particle(ipart)%self%time .gt. tmax_particles ) then
2065! destroy_n_particles_mype = destroy_n_particles_mype + 1
2066! particle_index_to_be_destroyed(destroy_n_particles_mype) = ipart
2067! cycle
2068! end if
2069!
2070! ! Then check dostroy due to user-defined condition
2071! if (associated(usr_destroy_particle)) then
2072! if (usr_destroy_particle(particle(ipart))) then
2073! destroy_n_particles_mype = destroy_n_particles_mype + 1
2074! particle_index_to_be_destroyed(destroy_n_particles_mype) = ipart
2075! cycle
2076! end if
2077! end if
2078!
2079! ! is my particle still in the same igrid?
2080! if (.not.particle_in_igrid(ipart,particle(ipart)%igrid)) then
2081! call find_particle_ipe(particle(ipart)%self%x,igrid_particle,ipe_particle)
2082!
2083! ! destroy particle if out of domain (signalled by return value of -1)
2084! if (igrid_particle == -1 )then
2085! call apply_periodB(particle(ipart)%self,igrid_particle,ipe_particle,BC_applied)
2086! if (.not. BC_applied .or. igrid_particle == -1) then
2087! destroy_n_particles_mype = destroy_n_particles_mype + 1
2088! particle_index_to_be_destroyed(destroy_n_particles_mype) = ipart
2089! cycle
2090! end if
2091! end if
2092!
2093! ! particle still present
2094! particle(ipart)%igrid = igrid_particle
2095! particle(ipart)%ipe = ipe_particle
2096!
2097! ! if we have more than one core, is it on another cpu?
2098! if (npe .gt. 1 .and. particle(ipart)%ipe .ne. mype) then
2099! send_n_particles_to_ipe(ipe_particle) = &
2100! send_n_particles_to_ipe(ipe_particle) + 1
2101! particle_index_to_be_sent_to_ipe(send_n_particles_to_ipe(ipe_particle),ipe_particle) = ipart
2102! end if ! ipe_particle
2103!
2104! end if ! particle_in_grid
2105!
2106! end do ! ipart
2107!
2108! call destroy_particles(destroy_n_particles_mype,particle_index_to_be_destroyed(1:destroy_n_particles_mype))
2109!
2110! ! get out when only one core:
2111! if (npe == 1) return
2112!
2113! ! communicate amount of particles to be sent/received
2114! allocate( sndrqst(1:npe_neighbors), rcvrqst(1:npe_neighbors) )
2115! sndrqst = MPI_REQUEST_NULL; rcvrqst = MPI_REQUEST_NULL;
2116! isnd = 0; ircv = 0;
2117! do iipe=1,npe_neighbors;ipe=ipe_neighbor(iipe);
2118! tag_send = mype * npe + ipe
2119! tag_receive = ipe * npe + mype
2120! isnd = isnd + 1; ircv = ircv + 1;
2121! call MPI_ISEND(send_n_particles_to_ipe(ipe),1,MPI_INTEGER, &
2122! ipe,tag_send,icomm,sndrqst(isnd),ierrmpi)
2123! call MPI_IRECV(receive_n_particles_from_ipe(ipe),1,MPI_INTEGER, &
2124! ipe,tag_receive,icomm,rcvrqst(ircv),ierrmpi)
2125! end do
2126! call MPI_WAITALL(isnd,sndrqst,MPI_STATUSES_IGNORE,ierrmpi)
2127! call MPI_WAITALL(ircv,rcvrqst,MPI_STATUSES_IGNORE,ierrmpi)
2128! deallocate( sndrqst, rcvrqst )
2129!
2130! ! Communicate index of the particles to be sent/received
2131! allocate( sndrqst(1:npe_neighbors*nparticles_per_cpu_hi), rcvrqst(1:npe_neighbors*nparticles_per_cpu_hi) )
2132! sndrqst = MPI_REQUEST_NULL; rcvrqst = MPI_REQUEST_NULL;
2133! isnd = 0; ircv = 0;
2134! do iipe=1,npe_neighbors;ipe=ipe_neighbor(iipe);
2135! if (send_n_particles_to_ipe(ipe) > 0) then
2136! tag_send = mype * npe + ipe
2137! isnd = isnd + 1
2138! call MPI_ISEND(particle_index_to_be_sent_to_ipe(:,ipe),send_n_particles_to_ipe(ipe),MPI_INTEGER, &
2139! ipe,tag_send,icomm,sndrqst(isnd),ierrmpi)
2140! end if
2141! if (receive_n_particles_from_ipe(ipe) > 0) then
2142! tag_receive = ipe * npe + mype
2143! ircv = ircv + 1
2144! call MPI_IRECV(particle_index_to_be_received_from_ipe(:,ipe),receive_n_particles_from_ipe(ipe),MPI_INTEGER, &
2145! ipe,tag_receive,icomm,rcvrqst(ircv),ierrmpi)
2146! end if
2147! end do
2148! call MPI_WAITALL(isnd,sndrqst,MPI_STATUSES_IGNORE,ierrmpi)
2149! call MPI_WAITALL(ircv,rcvrqst,MPI_STATUSES_IGNORE,ierrmpi)
2150! deallocate( sndrqst, rcvrqst )
2151!
2152! ! Send and receive the data of the particles
2153! allocate( sndrqst(1:npe_neighbors*nparticles_per_cpu_hi), rcvrqst(1:npe_neighbors*nparticles_per_cpu_hi) )
2154! sndrqst = MPI_REQUEST_NULL; rcvrqst = MPI_REQUEST_NULL;
2155! isnd = 0; ircv = 0;
2156! do iipe=1,npe_neighbors;ipe=ipe_neighbor(iipe);
2157!
2158! ! should i send some particles to ipe?
2159! if (send_n_particles_to_ipe(ipe) .gt. 0) then
2160!
2161! do ipart = 1, send_n_particles_to_ipe(ipe)
2162! tag_send = mype * npe + ipe + ipart
2163! isnd = isnd+1
2164! call MPI_ISEND(particle(particle_index_to_be_sent_to_ipe(ipart,ipe))%self, &
2165! 1,type_particle,ipe,tag_send,icomm,sndrqst(isnd),ierrmpi)
2166! end do ! ipart
2167! end if ! send .gt. 0
2168!
2169! ! should i receive some particles from ipe?
2170! if (receive_n_particles_from_ipe(ipe) .gt. 0) then
2171!
2172! do ipart = 1, receive_n_particles_from_ipe(ipe)
2173! tag_receive = ipe * npe + mype + ipart
2174! ircv = ircv+1
2175! index = particle_index_to_be_received_from_ipe(ipart,ipe)
2176! allocate(particle(index)%self)
2177! call MPI_IRECV(particle(index)%self,1,type_particle,ipe,tag_receive,icomm,rcvrqst(ircv),ierrmpi)
2178! end do ! ipart
2179!
2180! end if ! receive .gt. 0
2181! end do ! ipe
2182! call MPI_WAITALL(isnd,sndrqst,MPI_STATUSES_IGNORE,ierrmpi)
2183! call MPI_WAITALL(ircv,rcvrqst,MPI_STATUSES_IGNORE,ierrmpi)
2184! deallocate( sndrqst, rcvrqst )
2185!
2186! ! Send and receive the payloads
2187! ! NON-BLOCKING: one communication for each particle. Needed for whatever reason
2188! allocate( sndrqst(1:npe_neighbors*nparticles_per_cpu_hi), rcvrqst(1:npe_neighbors*nparticles_per_cpu_hi) )
2189! sndrqst = MPI_REQUEST_NULL; rcvrqst = MPI_REQUEST_NULL;
2190! isnd = 0; ircv = 0;
2191! do iipe=1,npe_neighbors;ipe=ipe_neighbor(iipe);
2192!
2193! ! should i send some particles to ipe?
2194! if (send_n_particles_to_ipe(ipe) .gt. 0) then
2195!
2196! do ipart = 1, send_n_particles_to_ipe(ipe)
2197! tag_send = mype * npe + ipe + ipart
2198! isnd = isnd+1
2199! call MPI_ISEND(particle(particle_index_to_be_sent_to_ipe(ipart,ipe))%payload(1:npayload), &
2200! npayload,MPI_DOUBLE_PRECISION,ipe,tag_send,icomm,sndrqst(isnd),ierrmpi)
2201! end do ! ipart
2202! end if ! send .gt. 0
2203!
2204! ! should i receive some particles from ipe?
2205! if (receive_n_particles_from_ipe(ipe) .gt. 0) then
2206! do ipart = 1, receive_n_particles_from_ipe(ipe)
2207! tag_receive = ipe * npe + mype + ipart
2208! ircv = ircv+1
2209! index = particle_index_to_be_received_from_ipe(ipart,ipe)
2210! allocate(particle(index)%payload(npayload))
2211! call MPI_IRECV(particle(index)%payload(1:npayload),npayload, &
2212! MPI_DOUBLE_PRECISION,ipe,tag_receive,icomm,rcvrqst(ircv),ierrmpi)
2213! end do ! ipart
2214! end if ! receive .gt. 0
2215! end do ! ipe
2216! call MPI_WAITALL(isnd,sndrqst,MPI_STATUSES_IGNORE,ierrmpi)
2217! call MPI_WAITALL(ircv,rcvrqst,MPI_STATUSES_IGNORE,ierrmpi)
2218! deallocate( sndrqst, rcvrqst )
2219!
2220! ! Deeallocate sent particles
2221! do iipe=1,npe_neighbors;ipe=ipe_neighbor(iipe);
2222! if (send_n_particles_to_ipe(ipe) .gt. 0) then
2223! do ipart = 1, send_n_particles_to_ipe(ipe)
2224! deallocate(particle(particle_index_to_be_sent_to_ipe(ipart,ipe))%self)
2225! deallocate(particle(particle_index_to_be_sent_to_ipe(ipart,ipe))%payload)
2226! call pull_particle_from_particles_on_mype(particle_index_to_be_sent_to_ipe(ipart,ipe))
2227! end do ! ipart
2228! end if ! send .gt. 0
2229! end do
2230!
2231! ! Relocate received particles
2232! do iipe=1,npe_neighbors;ipe=ipe_neighbor(iipe);
2233! if (receive_n_particles_from_ipe(ipe) .gt. 0) then
2234! do ipart = 1, receive_n_particles_from_ipe(ipe)
2235! index = particle_index_to_be_received_from_ipe(ipart,ipe)
2236! call push_particle_into_particles_on_mype(index)
2237! ! since we don't send the igrid, need to re-locate it
2238! call find_particle_ipe(particle(index)%self%x,igrid_particle,ipe_particle)
2239! particle(index)%igrid = igrid_particle
2240! particle(index)%ipe = ipe_particle
2241! end do ! ipart
2242! end if ! receive .gt. 0
2243! end do ! ipe
2244!
2245! end subroutine comm_particles_old
2246!
2247! subroutine comm_particles_global_old()
2248! use mod_global_parameters
2249!
2250! integer :: ipart, iipart, igrid_particle, ipe_particle, ipe, iipe
2251! integer :: index
2252! integer :: tag_send, tag_receive, send_buff, rcv_buff
2253! integer :: status(MPI_STATUS_SIZE)
2254! integer, dimension(0:npe-1) :: send_n_particles_to_ipe
2255! integer, dimension(0:npe-1) :: receive_n_particles_from_ipe
2256! type(particle_t), dimension(nparticles_per_cpu_hi) :: send_particles
2257! type(particle_t), dimension(nparticles_per_cpu_hi) :: receive_particles
2258! double precision, dimension(npayload,nparticles_per_cpu_hi) :: send_payload
2259! double precision, dimension(npayload,nparticles_per_cpu_hi) :: receive_payload
2260! integer, dimension(nparticles_per_cpu_hi,0:npe-1) :: particle_index_to_be_sent_to_ipe
2261! integer, dimension(nparticles_per_cpu_hi) :: particle_index_to_be_destroyed
2262! integer :: destroy_n_particles_mype
2263! logical :: BC_applied
2264!
2265! send_n_particles_to_ipe(:) = 0
2266! receive_n_particles_from_ipe(:) = 0
2267! destroy_n_particles_mype = 0
2268!
2269! ! check if and where to send each particle, relocate all of them (in case grid has changed)
2270! do iipart=1,nparticles_on_mype;ipart=particles_on_mype(iipart);
2271!
2272! call find_particle_ipe(particle(ipart)%self%x,igrid_particle,ipe_particle)
2273!
2274! particle(ipart)%igrid = igrid_particle
2275! particle(ipart)%ipe = ipe_particle
2276!
2277! ! if we have more than one core, is it on another cpu?
2278! if (particle(ipart)%ipe .ne. mype) then
2279! send_n_particles_to_ipe(ipe_particle) = &
2280! send_n_particles_to_ipe(ipe_particle) + 1
2281! particle_index_to_be_sent_to_ipe(send_n_particles_to_ipe(ipe_particle),ipe_particle) = ipart
2282! end if ! ipe_particle
2283!
2284! end do ! ipart
2285!
2286! ! get out when only one core:
2287! if (npe == 1) return
2288!
2289! ! communicate amount of particles to be sent/received
2290! do ipe=0,npe-1;if (ipe .eq. mype) cycle;
2291! tag_send = mype * npe + ipe
2292! tag_receive = ipe * npe + mype
2293! call MPI_SEND(send_n_particles_to_ipe(ipe),1,MPI_INTEGER,ipe,tag_send,icomm,ierrmpi)
2294! call MPI_RECV(receive_n_particles_from_ipe(ipe),1,MPI_INTEGER,ipe,tag_receive,icomm,status,ierrmpi)
2295! end do
2296!
2297! ! send and receive the data of the particles
2298! do ipe=0,npe-1;if (ipe .eq. mype) cycle;
2299! tag_send = mype * npe + ipe
2300! tag_receive = ipe * npe + mype
2301!
2302! ! should i send some particles to ipe?
2303! if (send_n_particles_to_ipe(ipe) .gt. 0) then
2304!
2305! ! create the send buffer
2306! do ipart = 1, send_n_particles_to_ipe(ipe)
2307! send_particles(ipart) = particle(particle_index_to_be_sent_to_ipe(ipart,ipe))%self
2308! send_payload(1:npayload,ipart) = particle(particle_index_to_be_sent_to_ipe(ipart,ipe))%payload(1:npayload)
2309! end do ! ipart
2310! call MPI_SEND(send_particles,send_n_particles_to_ipe(ipe),type_particle,ipe,tag_send,icomm,ierrmpi)
2311! call MPI_SEND(send_payload,npayload*send_n_particles_to_ipe(ipe),MPI_DOUBLE_PRECISION,ipe,tag_send,icomm,ierrmpi)
2312! do ipart = 1, send_n_particles_to_ipe(ipe)
2313! deallocate(particle(particle_index_to_be_sent_to_ipe(ipart,ipe))%self)
2314! deallocate(particle(particle_index_to_be_sent_to_ipe(ipart,ipe))%payload)
2315! call pull_particle_from_particles_on_mype(particle_index_to_be_sent_to_ipe(ipart,ipe))
2316! end do ! ipart
2317!
2318! end if ! send .gt. 0
2319!
2320! ! should i receive some particles from ipe?
2321! if (receive_n_particles_from_ipe(ipe) .gt. 0) then
2322!
2323! call MPI_RECV(receive_particles,receive_n_particles_from_ipe(ipe),type_particle,ipe,tag_receive,icomm,status,ierrmpi)
2324! call MPI_RECV(receive_payload,npayload*receive_n_particles_from_ipe(ipe),MPI_DOUBLE_PRECISION,ipe,tag_receive,icomm,status,ierrmpi)
2325!
2326! do ipart = 1, receive_n_particles_from_ipe(ipe)
2327!
2328! index = receive_particles(ipart)%index
2329! allocate(particle(index)%self)
2330! particle(index)%self = receive_particles(ipart)
2331! allocate(particle(index)%payload(npayload))
2332! particle(index)%payload(1:npayload) = receive_payload(1:npayload,ipart)
2333! call push_particle_into_particles_on_mype(index)
2334!
2335! ! since we don't send the igrid, need to re-locate it
2336! call find_particle_ipe(particle(index)%self%x,igrid_particle,ipe_particle)
2337! particle(index)%igrid = igrid_particle
2338! particle(index)%ipe = ipe_particle
2339!
2340! end do ! ipart
2341!
2342! end if ! receive .gt. 0
2343! end do ! ipe
2344!
2345! end subroutine comm_particles_global_old
2346
2347 subroutine apply_periodb(particle,igrid_particle,ipe_particle,BC_applied)
2349
2350 type(particle_t), intent(inout) :: particle
2351 integer, intent(inout) :: igrid_particle, ipe_particle
2352 logical,intent(out) :: BC_applied
2353 integer :: idim
2354
2355 bc_applied = .false.
2356 ! get out if we don't have any periodic BC:
2357 if (.not. any(periodb(1:ndim))) return
2358
2359 ! go through dimensions and try re-inject the particle at the other side
2360 do idim=1,ndim
2361
2362 if (.not. periodb(idim)) cycle
2363
2364 select case(idim)
2365 {case (^d)
2366 if (particle%x(^d) .lt. xprobmin^d) then
2367 particle%x(^d) = particle%x(^d) + (xprobmax^d - xprobmin^d)
2368 bc_applied = .true.
2369 end if
2370 if (particle%x(^d) .ge. xprobmax^d) then
2371 particle%x(^d) = particle%x(^d) - (xprobmax^d - xprobmin^d)
2372 bc_applied = .true.
2373 end if
2374 \}
2375 end select
2376
2377 end do
2378
2379 call find_particle_ipe(particle%x,igrid_particle,ipe_particle)
2380
2381 end subroutine apply_periodb
2382
2383 !> clean up destroyed particles on all cores
2384 subroutine destroy_particles(destroy_n_particles_mype,particle_index_to_be_destroyed)
2386
2387 integer, intent(in) :: destroy_n_particles_mype
2388 integer, dimension(1:destroy_n_particles_mype), intent(in) :: particle_index_to_be_destroyed
2389 type(particle_t), dimension(destroy_n_particles_mype):: destroy_particles_mype
2390 double precision, dimension(npayload,destroy_n_particles_mype):: destroy_payload_mype
2391 integer :: iipart,ipart,destroy_n_particles
2392
2393 destroy_n_particles = 0
2394
2395 ! append the particle to list of destroyed particles
2396 do iipart=1,destroy_n_particles_mype;ipart=particle_index_to_be_destroyed(iipart);
2397 destroy_particles_mype(iipart) = particle(ipart)%self
2398 destroy_payload_mype(1:npayload,iipart) = particle(ipart)%payload(1:npayload)
2399 end do
2400
2401 call output_ensemble(destroy_n_particles_mype,destroy_particles_mype,destroy_payload_mype,'destroy')
2402
2403 if (npe > 1) then
2404 call mpi_allreduce(destroy_n_particles_mype,destroy_n_particles,1,mpi_integer,mpi_sum,icomm,ierrmpi)
2405 else
2406 destroy_n_particles = destroy_n_particles_mype
2407 end if
2408
2409 nparticles = nparticles - destroy_n_particles
2410
2411 do iipart=1,destroy_n_particles_mype;ipart=particle_index_to_be_destroyed(iipart);
2412 particle(ipart)%igrid = -1
2413 particle(ipart)%ipe = -1
2414 if(time_advance) then
2415 write(*,*) particle(ipart)%self%time, ': particle',ipart,' has left at it ',it_particles,' on pe', mype
2416 write(*,*) particle(ipart)%self%time, ': particles remaining:',nparticles, '(total)', nparticles_on_mype-1, 'on pe', mype
2417 endif
2418 deallocate(particle(ipart)%self)
2419 nullify(particle(ipart)%self)
2420 deallocate(particle(ipart)%payload)
2422 end do
2423
2424 end subroutine destroy_particles
2425
2427 implicit none
2428
2429 integer, intent(in) :: ipart
2430
2433
2435
2437 implicit none
2438
2439 integer, intent(in) :: ipart
2440 integer :: i
2441
2442 do i=1,nparticles_on_mype
2443 if (particles_on_mype(i) == ipart) then
2445 exit
2446 end if
2447 end do
2448
2450
2452
2453end module mod_particle_base
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
Module for physical and numeric constants.
Module with basic grid data structures.
Definition mod_forest.t:2
integer(kind=8) particle_mesh_epoch
Replicated forest generation: topology or ownership changes invalidate cached particle locations and ...
Definition mod_forest.t:6
type(tree_node_ptr), dimension(:^d &), allocatable, save tree_root
Pointers to the coarse grid.
Definition mod_forest.t:32
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 cartesian
Definition mod_geometry.t:8
integer, parameter cylindrical
integer, parameter cartesian_expansion
integer, parameter cartesian_stretched
Definition mod_geometry.t:9
subroutine curlvector(qvec, ixil, ixol, curlvec, idirmin, idirmin0, ndir0, fourthorder)
Calculate curl of a vector qvec within ixL Options to employ standard second order CD evaluations use...
This module contains definitions of global parameters and variables and some generic functions/subrou...
type(state), pointer block
Block pointer for using one block and its previous state.
logical nocartesian
IO switches for conversion.
integer ixghi
Upper index of grid block arrays.
integer, parameter stretch_uni
Unidirectional stretching from a side.
integer domain_nx
number of cells for each dimension in level-one mesh
integer, parameter unitpar
file handle for IO
double precision global_time
The global simulation time.
double precision time_max
End time for the simulation.
double precision xstretch
physical extent of stretched border in symmetric stretching
integer, parameter ndim
Number of spatial dimensions for grid variables.
integer, dimension(:,:), allocatable nstretchedblocks
(even) number of (symmetrically) stretched blocks per level and dimension
character(len=std_len), dimension(:), allocatable par_files
Which par files are used as input.
integer icomm
The MPI communicator.
integer mype
The rank of the current MPI task.
integer block_nx
number of cells for each dimension in grid block excluding ghostcells
double precision dt
global time step
double precision length_convert_factor
Conversion factor for length unit.
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
double precision, dimension(:,:), allocatable qstretch
Stretching factors and first cell size for each AMR level and dimension.
integer snapshotnext
IO: snapshot and collapsed views output numbers/labels.
integer npe
The number of MPI tasks.
logical time_advance
do time evolving
character(len=std_len) restart_from_file
If not 'unavailable', resume from snapshot with this base file name.
logical, dimension(ndim) periodb
True for dimensions with periodic boundaries.
double precision c_norm
Normalised speed of light.
logical b0field
split magnetic field as background B0 field
double precision, dimension(:,:), allocatable rnode
Corner coordinates.
integer nghostcells
Number of ghost cells surrounding a grid.
logical convert
If true and restart_from_file is given, convert snapshots to other file formats.
integer, dimension(ndim) stretch_type
What kind of stretching is used per dimension.
double precision, dimension(^nd) dxlevel
store unstretched cell size of current level
character(len=std_len) base_filename
Base file name for simulation output, which will be followed by a number.
integer, parameter stretch_symm
Symmetric stretching around the center.
integer r_
Indices for cylindrical coordinates FOR TESTS, negative value when not used:
double precision, dimension(:,:), allocatable dxfirst
integer, dimension(:,:), allocatable node
integer, parameter ixglo
Lower index of grid block arrays (always 1).
Module with shared functionality for all the particle movers.
subroutine get_maxwellian_velocity(v, velocity)
subroutine partcoord_from_cartesian(xp, xpcart)
Convert to noncartesian coordinates.
double precision, dimension(3) integrator_velocity_factor
Normalization factor for velocity in the integrator.
integer nparticles_per_cpu_hi
Maximum number of particles in one processor.
subroutine particle_get_current(w, ixil, ixol, idirmin, current)
Calculate idirmin and the idirmin:3 components of the common current array make sure that dxlevel(^D)...
integer nparticleshi
Maximum number of particles.
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 output_individual()
integer n_output_destroyed
Output count for ensembles of destroyed particles.
integer it_particles
Iteration number of paritcles.
subroutine fields_from_mhd(igrid, w_mhd, w_part)
Determine fields from MHD variables.
subroutine particles_params_read(files)
Read this module's parameters from a file.
double precision min_particle_time
Minimum time of all particles.
subroutine output_ensemble(send_n_particles_for_output, send_particles, send_payload, typefile)
subroutine get_uniform_pos(x)
logical write_individual
Whether to write individual particle output (followed particles).
subroutine write_particle_output()
subroutine init_particles_com()
Initialize communicators for particles.
logical function particle_in_igrid(ipart, igrid)
Quick check if particle is still in igrid.
integer, dimension(:), allocatable particles_on_mype
Array of identity numbers of particles in current processor.
double precision dtsave_particles
Time interval to save particles.
subroutine output_particle(myparticle, payload, ipe, file_unit)
subroutine update_gridvars()
update grid variables for particles
integer ngridvars
Number of variables for grid field.
type(particle_t), dimension(:), allocatable exchange_send
subroutine ipe_fc(id, igrid, ipe_is_neighbor)
subroutine get_efield(igrid, x, tloc, e)
recursive logical function point_in_igrid_ghostc(x, igrid, ngh)
Quick check if particle coordinate is inside igrid (ghost cells included, except the last ngh).
double precision particles_etah
subroutine destroy_particles(destroy_n_particles_mype, particle_index_to_be_destroyed)
clean up destroyed particles on all cores
integer(kind=8) particle_last_mesh_epoch
integer nusrpayload
Number of user-defined payload variables for a particle.
subroutine select_active_particles(end_time)
subroutine write_particles_snapshot()
double precision particles_eta
Resistivity.
subroutine particles_finish()
Finalize the particles module.
logical write_snapshot
Whether to write particle snapshots.
pure subroutine get_lfac(u, lfac)
Get Lorentz factor from relativistic momentum.
subroutine find_particle_ipe(x, igrid_particle, ipe_particle)
subroutine pull_particle_from_particles_on_mype(ipart)
logical particle_any_active
Cached global activity and last replicated forest generation.
subroutine init_particles_output()
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 handle_particles()
Let particles evolve in time. The routine also handles grid variables and output.
subroutine partvec_from_cartesian(xp, up, upcart)
Convert vector from Cartesian coordinates.
subroutine fill_gridvars_default
This routine fills the particle grid variables with the default for mhd, i.e. only E and B.
character(len=400) csv_header
Header string used in CSV files.
subroutine finish_gridvars()
Deallocate grid variables for particles.
subroutine ipe_srl(id, igrid, ipe_is_neighbor)
subroutine get_bfield(igrid, x, tloc, b)
integer npayload
Number of total payload variables for a particle.
integer, parameter unitparticles
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 set_neighbor_ipe()
subroutine time_spent_on_particles()
subroutine get_vec(ix, igrid, x, tloc, vec)
logical write_ensemble
Whether to write ensemble output.
character(len=name_len) interp_type_particles
String describing the particle interpolation type.
integer downsample_particles
Particle downsampling factor in CSV output.
subroutine locate_particle(index, igrid_particle, ipe_particle)
subroutine push_particle_into_particles_on_mype(ipart)
subroutine read_from_snapshot()
integer, dimension(:), allocatable exchange_ids
integer nparticles_active_on_mype
Number of active particles in current processor.
procedure(sub_define_additional_gridvars), pointer particles_define_additional_gridvars
subroutine partcoord_to_cartesian(xp, xpcart)
Convert position to Cartesian coordinates.
integer nparticles
Identity number and total number of particles.
integer, dimension(:), allocatable exchange_requests
recursive subroutine fix_phi_crossing(xp, igrid)
Fix particle position when crossing the 0,2pi boundary in noncartesian coordinates.
integer nparticles_on_mype
Number of particles in current processor.
integer, dimension(:), allocatable bp
Variable index for magnetic field.
subroutine comm_particles()
double precision tmax_particles
Time limit of particles.
type(particle_ptr), dimension(:), allocatable particle
subroutine particle_exchange(outgoing_ids, destinations, nout, global_exchange)
Exchange only migrating particles, reusing geometrically grown buffers. All ranks participate; a glob...
procedure(fun_destroy), pointer usr_destroy_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
double precision t_next_output
Time to write next particle output.
subroutine advance_particles(end_time, steps_taken)
Routine handles the particle evolution.
subroutine partvec_to_cartesian(xp, up, upcart)
Convert vector to Cartesian coordinates.
integer ndefpayload
Number of default payload variables for a particle.
subroutine particle_base_init()
Give initial values to parameters.
integer, dimension(:,:), allocatable exchange_status
integer, dimension(:), allocatable ipe_neighbor
integer igrid_working
set the current igrid for the particle integrator:
character(len=60) csv_format
Format string used in CSV files.
integer, dimension(:), allocatable jp
Variable index for current.
subroutine append_to_snapshot(myparticle, mypayload)
subroutine apply_periodb(particle, igrid_particle, ipe_particle, bc_applied)
integer ipart_working
set the current ipart for the particle integrator:
subroutine interpolate_var(igrid, ixil, ixol, gf, x, xloc, gfloc)
double precision particles_cfl
Particle CFL safety factor.
procedure(sub_fill_additional_gridvars), pointer particles_fill_additional_gridvars
subroutine ipe_cf(id, igrid, ipe_is_neighbor)
logical function particle_in_domain(x)
Check if particle is inside computational domain.
character(len=name_len) physics_type_particles
String describing the particle physics type.
subroutine init_gridvars()
Initialize grid variables for particles.
character(len=128) function make_particle_filename(tout, index, type)
subroutine comm_particles_global()
double precision, dimension(:,:), allocatable exchange_recv_payload
subroutine read_particles_snapshot(file_exists)
double precision, dimension(:,:), allocatable exchange_send_payload
type(particle_t), dimension(:), allocatable exchange_recv
integer rhop
Variable index for density.
This module defines the procedures of a physics module. It contains function pointers for the various...
Definition mod_physics.t:4
Module for pseudo random number generation. The internal pseudo random generator is the xoroshiro128p...
Definition mod_random.t:3
Writes D-1 slice, can do so in various formats, depending on slice_type.
Definition mod_slice.t:2
subroutine get_igslice(dir, x, igslice)
Definition mod_slice.t:1280
double precision tpartc_int_0
Definition mod_timing.t:21
double precision timeio_tot
Definition mod_timing.t:10
double precision tpartc_com
Definition mod_timing.t:20
double precision tpartc
Definition mod_timing.t:20
double precision tpartc0
Definition mod_timing.t:21
double precision tpartc_grid_0
Definition mod_timing.t:21
double precision tpartc_io
Definition mod_timing.t:20
double precision timeloop
Definition mod_timing.t:11
double precision tpartc_com0
Definition mod_timing.t:21
double precision tpartc_grid
Definition mod_timing.t:20
double precision tpartc_io_0
Definition mod_timing.t:21
double precision tpartc_int
Definition mod_timing.t:20
Module with all the methods that users can customize in AMRVAC.
procedure(particle_analytic), pointer usr_particle_analytic
procedure(particle_fields), pointer usr_particle_fields
Pointer to a tree_node.
Definition mod_forest.t:9
Random number generator type, which contains the state.
Definition mod_random.t:20