mod_particle_advect.f90 Source File


This file depends on

sourcefile~~mod_particle_advect.f90~~EfferentGraph sourcefile~mod_particle_advect.f90 mod_particle_advect.f90 sourcefile~mod_geometry.f90 mod_geometry.f90 sourcefile~mod_particle_advect.f90->sourcefile~mod_geometry.f90 sourcefile~mod_global_parameters.f90 mod_global_parameters.f90 sourcefile~mod_particle_advect.f90->sourcefile~mod_global_parameters.f90 sourcefile~mod_odeint.f90 mod_odeint.f90 sourcefile~mod_particle_advect.f90->sourcefile~mod_odeint.f90 sourcefile~mod_particle_base.f90 mod_particle_base.f90 sourcefile~mod_particle_advect.f90->sourcefile~mod_particle_base.f90 sourcefile~mod_usr_methods.f90 mod_usr_methods.f90 sourcefile~mod_particle_advect.f90->sourcefile~mod_usr_methods.f90 sourcefile~mod_geometry.f90->sourcefile~mod_global_parameters.f90 sourcefile~mod_comm_lib.f90 mod_comm_lib.f90 sourcefile~mod_geometry.f90->sourcefile~mod_comm_lib.f90 sourcefile~mod_limiter.f90 mod_limiter.f90 sourcefile~mod_geometry.f90->sourcefile~mod_limiter.f90 sourcefile~mod_basic_types.f90 mod_basic_types.f90 sourcefile~mod_global_parameters.f90->sourcefile~mod_basic_types.f90 sourcefile~mod_connectivity.f90 mod_connectivity.f90 sourcefile~mod_global_parameters.f90->sourcefile~mod_connectivity.f90 sourcefile~mod_constants.f90 mod_constants.f90 sourcefile~mod_global_parameters.f90->sourcefile~mod_constants.f90 sourcefile~mod_physicaldata.f90 mod_physicaldata.f90 sourcefile~mod_global_parameters.f90->sourcefile~mod_physicaldata.f90 sourcefile~mod_variables.f90 mod_variables.f90 sourcefile~mod_global_parameters.f90->sourcefile~mod_variables.f90 sourcefile~mod_odeint.f90->sourcefile~mod_comm_lib.f90 sourcefile~mod_particle_base.f90->sourcefile~mod_geometry.f90 sourcefile~mod_particle_base.f90->sourcefile~mod_global_parameters.f90 sourcefile~mod_particle_base.f90->sourcefile~mod_usr_methods.f90 sourcefile~mod_particle_base.f90->sourcefile~mod_comm_lib.f90 sourcefile~mod_particle_base.f90->sourcefile~mod_constants.f90 sourcefile~mod_forest.f90 mod_forest.f90 sourcefile~mod_particle_base.f90->sourcefile~mod_forest.f90 sourcefile~mod_mpi_wrapper.f90 mod_mpi_wrapper.f90 sourcefile~mod_particle_base.f90->sourcefile~mod_mpi_wrapper.f90 sourcefile~mod_random.f90 mod_random.f90 sourcefile~mod_particle_base.f90->sourcefile~mod_random.f90 sourcefile~mod_slice.f90 mod_slice.f90 sourcefile~mod_particle_base.f90->sourcefile~mod_slice.f90 sourcefile~mod_timing.f90 mod_timing.f90 sourcefile~mod_particle_base.f90->sourcefile~mod_timing.f90 sourcefile~mod_usr_methods.f90->sourcefile~mod_global_parameters.f90 sourcefile~mod_comm_lib.f90->sourcefile~mod_global_parameters.f90 sourcefile~mod_limiter.f90->sourcefile~mod_global_parameters.f90 sourcefile~mod_limiter.f90->sourcefile~mod_comm_lib.f90 sourcefile~mod_slice.f90->sourcefile~mod_geometry.f90 sourcefile~mod_slice.f90->sourcefile~mod_global_parameters.f90 sourcefile~mod_slice.f90->sourcefile~mod_basic_types.f90 sourcefile~mod_slice.f90->sourcefile~mod_comm_lib.f90 sourcefile~mod_slice.f90->sourcefile~mod_forest.f90 sourcefile~mod_slice.f90->sourcefile~mod_mpi_wrapper.f90 sourcefile~mod_calculate_xw.f90 mod_calculate_xw.f90 sourcefile~mod_slice.f90->sourcefile~mod_calculate_xw.f90 sourcefile~mod_input_output_helper.f90 mod_input_output_helper.f90 sourcefile~mod_slice.f90->sourcefile~mod_input_output_helper.f90 sourcefile~mod_variables.f90->sourcefile~mod_basic_types.f90 sourcefile~mod_calculate_xw.f90->sourcefile~mod_geometry.f90 sourcefile~mod_calculate_xw.f90->sourcefile~mod_global_parameters.f90 sourcefile~mod_calculate_xw.f90->sourcefile~mod_usr_methods.f90 sourcefile~mod_calculate_xw.f90->sourcefile~mod_comm_lib.f90 sourcefile~mod_calculate_xw.f90->sourcefile~mod_limiter.f90 sourcefile~mod_input_output_helper.f90->sourcefile~mod_global_parameters.f90 sourcefile~mod_input_output_helper.f90->sourcefile~mod_comm_lib.f90 sourcefile~mod_input_output_helper.f90->sourcefile~mod_forest.f90

Files dependent on this one

sourcefile~~mod_particle_advect.f90~~AfferentGraph sourcefile~mod_particle_advect.f90 mod_particle_advect.f90 sourcefile~mod_particles.f90 mod_particles.f90 sourcefile~mod_particles.f90->sourcefile~mod_particle_advect.f90 sourcefile~agile.f90 agile.f90 sourcefile~agile.f90->sourcefile~mod_particles.f90 sourcefile~mod_advance.f90 mod_advance.f90 sourcefile~agile.f90->sourcefile~mod_advance.f90 sourcefile~mod_amr_grid.f90 mod_amr_grid.f90 sourcefile~agile.f90->sourcefile~mod_amr_grid.f90 sourcefile~mod_input_output.f90 mod_input_output.f90 sourcefile~agile.f90->sourcefile~mod_input_output.f90 sourcefile~mod_initialize.f90 mod_initialize.f90 sourcefile~agile.f90->sourcefile~mod_initialize.f90 sourcefile~mod_advance.f90->sourcefile~mod_particles.f90 sourcefile~mod_amr_grid.f90->sourcefile~mod_particles.f90 sourcefile~mod_input_output.f90->sourcefile~mod_particles.f90 sourcefile~mod_initialize.f90->sourcefile~mod_input_output.f90

Source Code

!> Tracer for advected particles moving with fluid flows
!> By Jannis Teunissen, Bart Ripperda, Oliver Porth, and Fabio Bacchini (2017-2020)
module mod_particle_advect
  use mod_particle_base

  private

  public :: advect_init
  public :: advect_create_particles
  integer, parameter :: Euler = 1, RK4=2, ARK4=3

  ! Variables
  public :: vp, rhop

contains

  subroutine advect_init()
    use mod_global_parameters
    integer :: idir

    ngridvars=0
    allocate(vp(ndir))
    do idir = 1, ndir
      vp(idir) = idir
    end do
    ngridvars=ngridvars+ndir
    if(ndefpayload>0) then
      rhop=vp(ndir)+1
      ngridvars=ngridvars+ndefpayload
    end if

    particles_fill_gridvars => advect_fill_gridvars

    if (associated(particles_define_additional_gridvars)) then
      call particles_define_additional_gridvars(ngridvars)
    end if

    select case(integrator_type_particles)
    case('Euler','euler')
      integrator = Euler
    case('RK4','Rk4','rk4')
      integrator = RK4
    case('ARK4','ARk4','Ark4','ark4')
      integrator = ARK4
    case default
      integrator = ARK4
    end select

    particles_integrate  => advect_integrate_particles

  end subroutine advect_init

  subroutine advect_create_particles()
    ! initialise the particles
    use mod_global_parameters
    use mod_usr_methods, only: usr_create_particles, usr_update_payload,&
        usr_check_particle

    integer          :: n, idir, igrid, ipe_particle, nparticles_local
    double precision :: x(3, num_particles)
    double precision :: v(3, num_particles)
    double precision :: q(num_particles)
    double precision :: m(num_particles)
    double precision :: rrd(num_particles,ndir)
    double precision :: w(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,1:nw)
    double precision :: defpayload(ndefpayload)
    double precision :: usrpayload(nusrpayload)
    logical          :: follow(num_particles), check

    follow = .false.
    x      = 0.0d0

    if (mype==0) then
      if (.not. associated(usr_create_particles)) then
        ! Randomly distributed
        do idir=1,ndir
          do n = 1, num_particles
            rrd(n,idir) = rng%unif_01()
          end do
        end do
        do n=1, num_particles
          x(1,n) = xprobmin1 + rrd(n,1) * (xprobmax1 - xprobmin1)
          x(2,n) = xprobmin2 + rrd(n,2) * (xprobmax2 - xprobmin2)
          x(3,n) = xprobmin3 + rrd(n,3) * (xprobmax3 - xprobmin3)
        end do
      else
        call usr_create_particles(num_particles, x, v, q, m, follow)
      end if
    end if

    call MPI_BCAST(x,3*num_particles,MPI_DOUBLE_PRECISION,0,icomm,ierrmpi)
    call MPI_BCAST(follow,num_particles,MPI_LOGICAL,0,icomm,ierrmpi)

    nparticles_local = 0

    do n=1,num_particles
      call find_particle_ipe(x(:,n),igrid,ipe_particle)
      particle(n)%igrid  = igrid
      particle(n)%ipe    = ipe_particle

      if(ipe_particle == mype) then
        check = .true.

        ! Check for user-defined modifications or rejection conditions
        if (associated(usr_check_particle)) call usr_check_particle(igrid, x(:,&
           n), v(:,n), q(n), m(n), follow(n), check)
        if (check) then
          call push_particle_into_particles_on_mype(n)
        else
          cycle
        end if

        nparticles_local = nparticles_local + 1

        allocate(particle(n)%self)
        particle(n)%self%follow = follow(n)
        particle(n)%self%index  = n
        particle(n)%self%time   = global_time
        particle(n)%self%dt     = 0.0d0
        particle(n)%self%x      = 0.d0
        particle(n)%self%x(:)   = x(:,n)
        w=ps(igrid)%w
        call phys_to_primitive(ixGlo1,ixGlo2,ixGlo3,ixGhi1,ixGhi2,ixGhi3,&
           ixGlo1,ixGlo2,ixGlo3,ixGhi1,ixGhi2,ixGhi3,w,ps(igrid)%x)
        do idir=1,ndir
          call interpolate_var(igrid,ixGlo1,ixGlo2,ixGlo3,ixGhi1,ixGhi2,ixGhi3,&
             ixMlo1,ixMlo2,ixMlo3,ixMhi1,ixMhi2,ixMhi3,w(ixGlo1:ixGhi1,&
             ixGlo2:ixGhi2,ixGlo3:ixGhi3,iw_mom(idir)),ps(igrid)%x,x(:,n),&
             v(idir,n))
        end do
        particle(n)%self%u(:) = 0.d0
        particle(n)%self%u(1:ndir) = v(1:ndir,n)

        ! Compute default and user-defined payloads
        allocate(particle(n)%payload(npayload))
        if(ndefpayload>0) then
          call advect_update_payload(igrid,x(:,n),v(:,n),q(n),m(n),defpayload,&
             ndefpayload,0.d0)
          particle(n)%payload(1:ndefpayload)=defpayload
        end if
        if (associated(usr_update_payload)) then
          call usr_update_payload(igrid,x(:,n),v(:,n),q(n),m(n),usrpayload,&
             nusrpayload,0.d0)
          particle(n)%payload(ndefpayload+1:npayload)=usrpayload
        end if
      end if

    end do

    call MPI_ALLREDUCE(nparticles_local,nparticles,1,MPI_INTEGER,MPI_SUM,icomm,&
       ierrmpi)

  end subroutine advect_create_particles

  subroutine advect_fill_gridvars
    use mod_global_parameters

    integer                                   :: igrid, iigrid, idir
    double precision, dimension(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,&
       1:nw)   :: w

    ! Fill fluid velocity only
    do iigrid=1,igridstail; igrid=igrids(iigrid);
      gridvars(igrid)%w(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,&
         1:ngridvars) = 0.0d0
      w(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,&
         1:nw) = ps(igrid)%w(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,1:nw)
      call phys_to_primitive(ixGlo1,ixGlo2,ixGlo3,ixGhi1,ixGhi2,ixGhi3,ixGlo1,&
         ixGlo2,ixGlo3,ixGhi1,ixGhi2,ixGhi3,w,ps(igrid)%x)
      gridvars(igrid)%w(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,&
         vp(:)) = w(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,iw_mom(:))
      gridvars(igrid)%w(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,&
         rhop) = w(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,iw_rho)
    end do

  end subroutine advect_fill_gridvars

  subroutine advect_integrate_particles(end_time)
    ! this solves dx/dt=v for particles
    use mod_odeint
    use mod_global_parameters
    use mod_usr_methods, only: usr_create_particles, usr_update_payload
    double precision, intent(in) :: end_time

    double precision, dimension(1:ndir) :: v, x
    double precision                 :: defpayload(ndefpayload)
    double precision                 :: usrpayload(nusrpayload)
    double precision                 :: tloc, tlocnew, dt_p, h1, tk
    double precision, dimension(1:ndir) :: yk, k1, k2, k3, k4
    double precision,parameter       :: eps=1.0d-6, hmin=1.0d-8
    integer                          :: ipart, iipart, igrid
    integer                          :: nok, nbad, ierror

    do iipart=1,nparticles_active_on_mype
      ipart                   = particles_active_on_mype(iipart);
      igrid                   = particle(ipart)%igrid
      igrid_working           = igrid
      dt_p                    = advect_get_particle_dt(particle(ipart),&
          end_time)
      particle(ipart)%self%dt = dt_p

      tloc                    = particle(ipart)%self%time
      x(1:ndir)               = particle(ipart)%self%x(1:ndir)
      tlocnew                 = tloc+dt_p

      ! Position update
      select case (integrator)
      case (Euler) 
        ! Simple forward Euler 
        call derivs_advect(tloc,x,v)
        particle(ipart)%self%x(1:ndir) = particle(ipart)%self%x(1:ndir) + dt_p &
           * v(1:ndir)

      case (RK4)
        ! Runge-Kutta order 4
        tk = tloc
        yk = x
        call derivs_advect(tk,yk,k1)
        tk = tloc + dt_p/2.d0
        yk = x + dt_p*k1/2.d0
        call derivs_advect(tk,yk,k2)
        tk = tloc + dt_p/2.d0
        yk = x + dt_p*k2/2.d0
        call derivs_advect(tk,yk,k3)
        tk = tloc + dt_p
        yk = x + dt_p*k3
        call derivs_advect(tloc,yk,k4)
        x = x + dt_p/6.d0*(k1 + 2.d0*k2 + 2.d0*k3 + k4)
      
      case (ARK4)
        ! Adaptive stepwidth RK4:
        h1 = dt_p/2.0d0
        call odeint(x,ndir,tloc,tlocnew,eps,h1,hmin,nok,nbad,derivs_advect,&
           rkqs,ierror)
  
        if (ierror /= 0) then
          print *, "odeint returned error code", ierror
          print *, "1 means hmin too small, 2 means MAXSTP exceeded"
          print *, "Having a problem with particle", iipart
        end if
      end select
      particle(ipart)%self%x(1:ndir) = x(1:ndir)

      ! Velocity update
      call get_vec_advect(igrid,x,tlocnew,v,vp(1),vp(ndir))
      particle(ipart)%self%u(1:ndir) = v(1:ndir)

      ! Time update
      particle(ipart)%self%time = tlocnew

      ! Update payload
      if(ndefpayload>0) then
        call advect_update_payload(igrid,x,v,0.d0,0.d0,defpayload,ndefpayload,&
           tlocnew)
        particle(ipart)%payload(1:ndefpayload) = defpayload
      end if
      if (associated(usr_update_payload)) then
        call usr_update_payload(igrid,x,v,0.d0,0.d0,usrpayload,nusrpayload,&
           tlocnew)
        particle(ipart)%payload(ndefpayload+1:npayload) = usrpayload
      end if

    end do

  end subroutine advect_integrate_particles

  !> Payload update
  subroutine advect_update_payload(igrid,xpart,upart,qpart,mpart,mypayload,&
     mynpayload,particle_time)
    use mod_global_parameters
    integer, intent(in)           :: igrid,mynpayload
    double precision, intent(in)  :: xpart(1:ndir),upart(1:ndir),qpart,mpart,&
       particle_time
    double precision, intent(out) :: mypayload(mynpayload)
    double precision              :: rho, rho1, rho2, td

    td = (particle_time - global_time) / dt

    ! Payload 1 is density
    if (mynpayload > 0 ) then
      if (time_advance) then
        call interpolate_var(igrid,ixGlo1,ixGlo2,ixGlo3,ixGhi1,ixGhi2,ixGhi3,&
           ixMlo1,ixMlo2,ixMlo3,ixMhi1,ixMhi2,ixMhi3,&
           gridvars(igrid)%wold(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,&
           rhop),ps(igrid)%x,xpart,rho1)
        call interpolate_var(igrid,ixGlo1,ixGlo2,ixGlo3,ixGhi1,ixGhi2,ixGhi3,&
           ixMlo1,ixMlo2,ixMlo3,ixMhi1,ixMhi2,ixMhi3,&
           gridvars(igrid)%w(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,rhop),&
           ps(igrid)%x,xpart,rho2)
        rho = rho1 * (1.0d0 - td) + rho2 * td
      else
        call interpolate_var(igrid,ixGlo1,ixGlo2,ixGlo3,ixGhi1,ixGhi2,ixGhi3,&
           ixMlo1,ixMlo2,ixMlo3,ixMhi1,ixMhi2,ixMhi3,&
           gridvars(igrid)%w(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,rhop),&
           ps(igrid)%x,xpart,rho)
      end if
      mypayload(1) = rho * w_convert_factor(1)
    end if

  end subroutine advect_update_payload

  subroutine derivs_advect(t_s,x,dxdt)
    use mod_global_parameters
    use mod_geometry
    double precision :: t_s, x(ndir)
    double precision :: dxdt(ndir)

    double precision :: v(ndir)

    call get_vec_advect(igrid_working,x,t_s,v,vp(1),vp(ndir))
    select case (coordinate)
    case (cylindrical)
      v(phi_) = v(phi_)/x(r_)
    case (spherical)
      v(2) = v(2)/x(1)
      v(3) = v(3)/(x(1)*sin(x(2)))
    end select
    dxdt(:) = v(:)

  end subroutine derivs_advect

  function advect_get_particle_dt(partp, end_time) result(dt_p)
    use mod_global_parameters
    type(particle_ptr), intent(in) :: partp
    double precision, intent(in)   :: end_time
    double precision               :: dt_p
    integer                        :: ipart, iipart, idims
    double precision               :: v(1:ndir),dtdims(1:ndim)

    if (const_dt_particles > 0) then
      dt_p = const_dt_particles
      return
    end if

    ! make sure we step only one cell at a time:
    call derivs_advect(partp%self%time,partp%self%x,v)
    do idims=1,ndim
      dtdims(idims)=minval(ps(partp%igrid)%ds(ixMlo1:ixMhi1,ixMlo2:ixMhi2,&
         ixMlo3:ixMhi3,idims))/(max(abs(v(idims)),smalldouble))
    end do

    dt_p = particles_cfl*minval(dtdims)

    ! Make sure we do not advance beyond end_time
    call limit_dt_endtime(end_time - partp%self%time, dt_p)

  end function advect_get_particle_dt

  subroutine get_vec_advect(igrid,x,tloc,var,ibeg,iend)
    use mod_global_parameters

    integer,intent(in)                                   :: igrid, ibeg, iend
    double precision,dimension(ndir), intent(in)         :: x
    double precision, intent(in)                         :: tloc
    double precision,dimension(iend-ibeg+1), intent(out) :: var
    double precision,dimension(iend-ibeg+1)              :: e1, e2
    integer                                              :: ivar, iloc
    double precision                                     :: td

    if(.not.time_advance) then
      do ivar=ibeg,iend
        iloc = ivar-ibeg+1
        call interpolate_var(igrid,ixGlo1,ixGlo2,ixGlo3,ixGhi1,ixGhi2,ixGhi3,&
           ixMlo1,ixMlo2,ixMlo3,ixMhi1,ixMhi2,ixMhi3,&
           gridvars(igrid)%w(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,ivar),&
           ps(igrid)%x(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,1:ndim),x,&
           var(iloc))
      end do
    else
      td = (tloc - global_time) / dt
      do ivar=ibeg,iend
        iloc = ivar-ibeg+1
        call interpolate_var(igrid,ixGlo1,ixGlo2,ixGlo3,ixGhi1,ixGhi2,ixGhi3,&
           ixMlo1,ixMlo2,ixMlo3,ixMhi1,ixMhi2,ixMhi3,&
           gridvars(igrid)%wold(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,&
           ivar),ps(igrid)%x(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,1:ndim),&
           x,e1(iloc))
        call interpolate_var(igrid,ixGlo1,ixGlo2,ixGlo3,ixGhi1,ixGhi2,ixGhi3,&
           ixMlo1,ixMlo2,ixMlo3,ixMhi1,ixMhi2,ixMhi3,&
           gridvars(igrid)%w(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,ivar),&
           ps(igrid)%x(ixGlo1:ixGhi1,ixGlo2:ixGhi2,ixGlo3:ixGhi3,1:ndim),x,&
           e2(iloc))
        var(iloc) = e1(iloc) * (1.0d0 - td) + e2(iloc) * td
      end do
    end if

  end subroutine get_vec_advect

end module mod_particle_advect