!!****************************************************************************** !! !! This file is part of the AMUN source code, a program to perform !! Newtonian or relativistic magnetohydrodynamical simulations on uniform or !! adaptive mesh. !! !! Copyright (C) 2017-2020 Grzegorz Kowal !! !! This program is free software: you can redistribute it and/or modify !! it under the terms of the GNU General Public License as published by !! the Free Software Foundation, either version 3 of the License, or !! (at your option) any later version. !! !! This program is distributed in the hope that it will be useful, !! but WITHOUT ANY WARRANTY; without even the implied warranty of !! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the !! GNU General Public License for more details. !! !! You should have received a copy of the GNU General Public License !! along with this program. If not, see . !! !!***************************************************************************** !! !! module: USER_PROBLEM !! !! This module provides subroutines to setup custom problem. !! !!***************************************************************************** ! module user_problem #ifdef PROFILE ! include external procedures ! use timers, only : set_timer, start_timer, stop_timer #endif /* PROFILE */ ! module variables are not implicit by default ! implicit none #ifdef PROFILE ! timer indices ! integer, save :: imi, imp, ims, imu, img, imb #endif /* PROFILE */ ! default problem parameter values ! real(kind=8), save :: beta = 1.00d+00 real(kind=8), save :: zeta = 0.00d+00 real(kind=8), save :: eta = 0.00d+00 real(kind=8), save :: dens = 1.00d+00 real(kind=8), save :: bamp = 1.00d+00 real(kind=8), save :: bgui = 0.00d+00 real(kind=8), save :: pres = 5.00d-01 real(kind=8), save :: pmag = 5.00d-01 real(kind=8), save :: ptot = 1.00d+00 real(kind=8), save :: valf = 1.00d+00 real(kind=8), save :: lund = 1.00d+00 real(kind=8), save :: dlta = 1.00d-16 real(kind=8), save :: blim = 1.00d+00 integer , save :: pert = 0 integer , save :: nper = 10 real(kind=8), save :: bper = 0.00d+00 real(kind=8), save :: vper = 0.00d+00 real(kind=8), save :: kper = 1.00d+00 real(kind=8), save :: kvec = 1.00d+00 real(kind=8), save :: xcut = 1.00d+99 real(kind=8), save :: ycut = 1.00d+99 real(kind=8), save :: xdec = 1.00d-01 real(kind=8), save :: ydec = 1.00d-01 ! flag indicating if the gravitational source term is enabled ! logical, save :: gravity_enabled_user = .false. ! allocatable arrays for velocity perturbation ! real(kind=8), dimension(:), allocatable :: kx, ky, kz, ux, uy, uz, ph !- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! contains ! !=============================================================================== ! ! subroutine INITIALIZE_USER_PROBLEM: ! ---------------------------------- ! ! Subroutine initializes user problem. It could read problem parameters which ! are used in all subroutines defining this specific problem. ! ! Arguments: ! ! problem - the problem name ! verbose - a logical flag turning the information printing; ! status - an integer flag for error return value; ! !=============================================================================== ! subroutine initialize_user_problem(problem, verbose, status) ! include external procedures and variables ! #if NDIMS == 3 use constants , only : pi #endif /* NDIMS == 3 */ use constants , only : pi2 use coordinates, only : ng => nghosts, ady use equations , only : magnetized, csnd, csnd2 use helpers , only : print_section, print_parameter use parameters , only : get_parameter use random , only : randuni, randsym ! local variables are not implicit by default ! implicit none ! subroutine arguments ! character(len=64), intent(in) :: problem logical , intent(in) :: verbose integer , intent(out) :: status ! local variables ! character(len=64) :: perturbation = "noise" integer :: n real(kind=8) :: thh, fc #if NDIMS == 3 real(kind=8) :: thv, tx, ty, tz, tt #endif /* NDIMS == 3 */ ! !------------------------------------------------------------------------------- ! #ifdef PROFILE ! set timer descriptions ! call set_timer('user_problem:: initialize' , imi) call set_timer('user_problem:: problem setup', imp) call set_timer('user_problem:: shape' , ims) call set_timer('user_problem:: sources' , imu) call set_timer('user_problem:: gravity' , img) call set_timer('user_problem:: boundaries' , imb) ! start accounting time for module initialization/finalization ! call start_timer(imi) #endif /* PROFILE */ ! reset the status flag ! status = 0 ! this problem does not work with not magnetized set of equations ! if (.not. magnetized) then if (verbose) then write(*,*) write(*,"(1x,a)") "ERROR!" write(*,"(1x,a)") "The problem " // trim(problem) // & " requires magnetized set of equations:" // & " 'MHD', or 'SR-MHD'." write(*,*) end if status = 1 end if ! proceed if no errors ! if (status == 0) then ! get the reconnection equilibrium parameters ! call get_parameter("beta", beta) if (beta <= 0.0d+00) then if (verbose) then write(*,*) write(*,"(1x,a)") "ERROR!" write(*,"(1x,a)") "Parameter 'beta' must be positive (beta > 0.0)!" write(*,*) end if status = 1 end if call get_parameter("zeta", zeta) if (zeta < 0.0d+00 .or. zeta > 1.0d+00) then if (verbose) then write(*,*) write(*,"(1x,a)") "ERROR!" write(*,"(1x,a)") "Parameter 'zeta' must be between 0.0 and 1.0!" write(*,*) end if status = 1 end if call get_parameter("resistivity", eta) if (eta < 0.0d+00) then if (verbose) then write(*,*) write(*,"(1x,a)") "ERROR!" write(*,"(1x,a)") "Resistivity cannot be negative!" write(*,*) end if status = 1 end if call get_parameter("dens", dens) if (dens <= 0.0d+00) then if (verbose) then write(*,*) write(*,"(1x,a)") "ERROR!" write(*,"(1x,a)") "Parameter 'dens' must be positive (dens > 0.0)!" write(*,*) end if status = 1 end if call get_parameter("bamp", bamp) call get_parameter("bgui", bgui) ! calculate the maximum magnetic pressure, thermal pressure from the plasma-β ! parameters, and the sound speed in the case of isothermal equations of state ! pmag = 0.5d+00 * (bamp**2 + bgui**2) pres = beta * pmag ptot = pres + pmag csnd2 = pres / dens csnd = sqrt(csnd2) valf = sqrt(2.0d+00 * pmag / dens) lund = valf / max(tiny(eta), eta) dlta = lund**(- 1.0d+00 / 3.0d+00) ! get the geometry parameters ! call get_parameter("delta", dlta) if (dlta < 0.0d+00) then if (verbose) then write(*,*) write(*,"(1x,a)") "ERROR!" write(*,"(1x,a)") "Parameter 'delta' must be equal or bigger than zero!" write(*,*) end if status = 1 end if call get_parameter("blimit", blim) ! lower limit for blim ! blim = max(blim, ng * ady(1)) end if ! status ! proceed if no errors ! if (status == 0) then ! get the perturbation parameters ! call get_parameter("perturbation", perturbation) call get_parameter("nper" , nper) call get_parameter("bper" , bper) call get_parameter("vper" , vper) call get_parameter("kper" , kper) call get_parameter("xcut" , xcut) call get_parameter("ycut" , ycut) call get_parameter("xdec" , xdec) call get_parameter("ydec" , ydec) ! choose the perturbation type ! select case(perturbation) case('noise', 'random') pert = 0 case('mode', 'one mode', 'one-mode', 'one_mode') pert = 1 case('multi-wave', 'random waves', 'random-waves', 'random_waves') pert = 2 case default perturbation = 'mode' pert = 1 end select ! prepare the wave vector of the perturbation ! kvec = pi2 * kper ! prepare the wave vectors for multi-wave perturbation ! if (pert == 2) then ! allocate phase and wave vector components ! allocate(kx(nper), ky(nper), kz(nper), ux(nper), uy(nper), uz(nper), & ph(nper), stat = status) if (status == 0) then ! choose random wave vector directions ! fc = 1.0d+00 / sqrt(1.0d+00 * nper) do n = 1, nper thh = pi2 * randuni() #if NDIMS == 3 thv = pi * randsym() ux(n) = cos(thh) * cos(thv) uy(n) = sin(thh) * cos(thv) uz(n) = sin(thv) kx(n) = pi2 * nint(kper * ux(n)) ky(n) = pi2 * nint(kper * uy(n)) kz(n) = pi2 * nint(kper * uz(n)) tt = 0.0d+00 do while(tt < 1.0d-08) thh = pi2 * randuni() thv = pi * randsym() tx = cos(thh) * cos(thv) ty = sin(thh) * cos(thv) tz = sin(thv) ux(n) = ty * kz(n) - tz * ky(n) uy(n) = tz * kx(n) - tx * kz(n) uz(n) = tx * ky(n) - ty * kx(n) tt = sqrt(ux(n)**2 + uy(n)**2 + uz(n)**2) end do ux(n) = fc * ux(n) / tt uy(n) = fc * uy(n) / tt uz(n) = fc * uz(n) / tt #else /* NDIMS == 3 */ kx(n) = pi2 * nint(kper * cos(thh)) ky(n) = pi2 * nint(kper * sin(thh)) kz(n) = 0.0d+00 ux(n) = fc * sin(thh) uy(n) = fc * cos(thh) uz(n) = 0.0d+00 #endif /* NDIMS == 3 */ ph(n) = pi2 * randuni() end do end if ! status end if end if ! status ! proceed if no errors ! if (status == 0) then ! print information about the user problem setup ! call print_section(verbose, "Parameters (* - set, otherwise calculated)") call print_parameter(verbose, '(*) beta (plasma-beta)' , beta) call print_parameter(verbose, '(*) zeta' , zeta) call print_parameter(verbose, '(*) dens' , dens) call print_parameter(verbose, '(*) bamp' , bamp) call print_parameter(verbose, '(*) bgui (guide field)' , bgui) call print_parameter(verbose, '( ) pres (thermal pres.)' , pres) call print_parameter(verbose, '( ) pmag (magnetic pres.)', pmag) call print_parameter(verbose, '( ) ptot (total pressure)', ptot) call print_parameter(verbose, '( ) csnd (sound speed)' , csnd) call print_parameter(verbose, '( ) Valf (Alfven speed)' , valf) if (eta > 0.0d+00) then call print_parameter(verbose, '( ) S (Lundquist number)', lund) end if call print_parameter(verbose, '(*) delta (thickness)', dlta) call print_parameter(verbose, '(*) blim' , blim) call print_parameter(verbose, '(*) perturbation', perturbation) call print_parameter(verbose, '(*) bper' , bper) call print_parameter(verbose, '(*) vper' , vper) if (pert >= 1) then call print_parameter(verbose, '(*) kper' , kper) end if if (pert == 2) then call print_parameter(verbose, '(*) nper' , nper) end if call print_parameter(verbose, '(*) xcut' , xcut) call print_parameter(verbose, '(*) ycut' , ycut) call print_parameter(verbose, '(*) xdec' , xdec) call print_parameter(verbose, '(*) ydec' , ydec) end if ! status #ifdef PROFILE ! stop accounting time for module initialization/finalization ! call stop_timer(imi) #endif /* PROFILE */ !------------------------------------------------------------------------------- ! end subroutine initialize_user_problem ! !=============================================================================== ! ! subroutine FINALIZE_USER_PROBLEM: ! -------------------------------- ! ! Subroutine releases memory used by the module. ! ! Arguments: ! ! status - an integer flag for error return value; ! !=============================================================================== ! subroutine finalize_user_problem(status) ! local variables are not implicit by default ! implicit none ! subroutine arguments ! integer, intent(out) :: status ! !------------------------------------------------------------------------------- ! #ifdef PROFILE ! start accounting time for module initialization/finalization ! call start_timer(imi) #endif /* PROFILE */ ! reset the status flag ! status = 0 ! deallocate wave vector components, random directions, and random phase ! if (allocated(kx)) deallocate(kx, ky, kz, stat = status) if (allocated(ux)) deallocate(ux, uy, uz, stat = status) if (allocated(ph)) deallocate(ph, stat = status) #ifdef PROFILE ! stop accounting time for module initialization/finalization ! call stop_timer(imi) #endif /* PROFILE */ !------------------------------------------------------------------------------- ! end subroutine finalize_user_problem ! !=============================================================================== ! ! subroutine SETUP_PROBLEM_USER: ! ----------------------------- ! ! Subroutine sets the initial conditions for the user specific problem. ! ! Arguments: ! ! pdata - pointer to the datablock structure of the currently initialized ! block; ! !=============================================================================== ! subroutine setup_problem_user(pdata) ! include external procedures and variables ! use blocks , only : block_data use constants , only : pi, pi2 use coordinates, only : nn => bcells use coordinates, only : ax, ay, adx, ady, ylen #if NDIMS == 3 use coordinates, only : az, adz #endif /* NDIMS == 3 */ use equations , only : prim2cons use equations , only : nv, ns use equations , only : idn, ivx, ivy, ivz, ipr, ibx, iby, ibz, ibp, isl use equations , only : csnd2 use operators , only : curl use random , only : randsym ! local variables are not implicit by default ! implicit none ! input arguments ! type(block_data), pointer, intent(inout) :: pdata ! local variables ! integer :: i, j, k = 1, n real(kind=8) :: xpl, xpu, ypl, ypu real(kind=8) :: xp, yp real(kind=8) :: yrat real(kind=8) :: kv, fv real(kind=8) :: vx = 0.0d+00, vy = 0.0d+00, vv, va #if NDIMS == 3 real(kind=8) :: vz = 0.0d+00 #endif /* NDIMS == 3 */ real(kind=8) :: pz = 0.0d+00, pp, ba #if NDIMS == 3 real(kind=8) :: px = 0.0d+00, py = 0.0d+00 #endif /* NDIMS == 3 */ ! local arrays ! #if NDIMS == 3 real(kind=8), dimension(nv,nn,nn,nn) :: qpert real(kind=8), dimension(3 ,nn,nn,nn) :: pot #else /* NDIMS == 3 */ real(kind=8), dimension(nv,nn,nn, 1) :: qpert real(kind=8), dimension(3 ,nn,nn, 1) :: pot #endif /* NDIMS == 3 */ real(kind=8), dimension(nv,nn) :: q, u, qprof real(kind=8), dimension(nn) :: xl, xu, xc, fx real(kind=8), dimension(nn) :: yl, yu, yc, fy #if NDIMS == 3 real(kind=8), dimension(nn) :: zc #endif /* NDIMS == 3 */ real(kind=8), dimension(3) :: dh ! !------------------------------------------------------------------------------- ! #ifdef PROFILE ! start accounting time for the problem setup ! call start_timer(imp) #endif /* PROFILE */ ! prepare cell sizes ! dh(1) = adx(pdata%meta%level) dh(2) = ady(pdata%meta%level) #if NDIMS == 3 dh(3) = adz(pdata%meta%level) #endif /* NDIMS == 3 */ ! prepare block coordinates ! xc(:) = pdata%meta%xmin + ax(pdata%meta%level,:) yc(:) = pdata%meta%ymin + ay(pdata%meta%level,:) #if NDIMS == 3 zc(:) = pdata%meta%zmin + az(pdata%meta%level,:) #endif /* NDIMS == 3 */ xl(:) = pdata%meta%xmin + ax(pdata%meta%level,:) - 5.0d-01 * dh(1) xu(:) = xl(:) + dh(1) yl(:) = pdata%meta%ymin + ay(pdata%meta%level,:) - 5.0d-01 * dh(2) yu(:) = yl(:) + dh(2) ! set the equilibrium profile ! do j = 1, nn qprof(ibx,j) = bamp * max(-1.0d+00, min(1.0d+00, & dlta * (log_cosh(yu(j) / dlta) & - log_cosh(yl(j) / dlta)) / dh(2))) end do qprof(iby,:) = 0.0d+00 qprof(ibz,:) = zeta * sqrt(bamp**2 - qprof(ibx,:)**2) + bgui if (ipr > 0) then qprof(idn,:) = dens qprof(ipr,:) = ptot - 0.5d+00 * sum(qprof(ibx:ibz,:)**2, 1) else qprof(idn,:) = (ptot - 0.5d+00 * sum(qprof(ibx:ibz,:)**2, 1)) / csnd2 end if qprof(ivx,:) = 0.0d+00 qprof(ivy,:) = 0.0d+00 qprof(ivz,:) = 0.0d+00 qprof(ibp,:) = 0.0d+00 if (ns > 0) then qprof(isl,:) = sign(1.0d+00, yc(:)) end if ! ratio of the current sheet thickness to the cell size ! yrat = dlta / dh(2) ! prepare decaying factors ! fv = 0.5d+00 * pi do i = 1, nn xp = fv * min(1.0d+00, max(0.0d+00, abs(xc(i)) - xcut) / xdec) fx(i) = cos(xp)**2 end do ! i = 1, nn do j = 1, nn yp = fv * min(1.0d+00, max(0.0d+00, abs(yc(j)) - ycut) / ydec) fy(j) = cos(yp)**2 end do ! i = 1, nn ! reset the perturbation matrix ! qpert(:,:,:,:) = 0.0d+00 ! the random perturbation ! if (pert == 0) then ! of velocity ! if (abs(vper) > 0.0d+00) then ! initiate the random velocity components ! #if NDIMS == 3 do k = 1, nn #endif /* NDIMS == 3 */ do j = 1, nn if (fy(j) > 0.0d+00) then do i = 1, nn if (fx(i) > 0.0d+00) then ! calculate the profile of perturbation amplitude ! va = vper * fx(i) * fy(j) ! get the random direction ! vv = 0.0d+00 do while(vv < 1.0d-08) vx = randsym() vy = randsym() #if NDIMS == 3 vz = randsym() #endif /* NDIMS == 3 */ #if NDIMS == 3 vv = sqrt(vx * vx + vy * vy + vz * vz) #else /* NDIMS == 3 */ vv = sqrt(vx * vx + vy * vy) #endif /* NDIMS == 3 */ end do qpert(ivx,i,j,k) = va * (vx / vv) qpert(ivy,i,j,k) = va * (vy / vv) #if NDIMS == 3 qpert(ivz,i,j,k) = va * (vz / vv) #endif /* NDIMS == 3 */ end if ! |x| < xcut end do ! i = 1, nn end if ! |y| < ycut end do ! j = 1, nn #if NDIMS == 3 end do ! k = 1, nn #endif /* NDIMS == 3 */ end if ! vper /= 0.0 ! of magnetic field ! if (abs(bper) > 0.0d+00) then ! reset the potential ! pot(:,:,:,:) = 0.0d+00 ! initiate the random magnetic field components ! #if NDIMS == 3 do k = 1, nn #endif /* NDIMS == 3 */ do j = 1, nn if (fy(j) > 0.0d+00) then do i = 1, nn if (fx(i) > 0.0d+00) then ! calculate the profile of perturbation amplitude ! ba = dh(1) * bper * fx(i) * fy(j) ! get the random direction ! pp = 0.0d+00 do while(pp < 1.0d-08) #if NDIMS == 3 px = randsym() py = randsym() #endif /* NDIMS == 3 */ pz = randsym() #if NDIMS == 3 pp = sqrt(px * px + py * py + pz * pz) #else /* NDIMS == 3 */ pp = abs(pz) #endif /* NDIMS == 3 */ end do #if NDIMS == 3 pot(1,i,j,k) = ba * (px / pp) pot(2,i,j,k) = ba * (py / pp) #endif /* NDIMS == 3 */ pot(3,i,j,k) = ba * (pz / pp) end if ! |x| < xcut end do ! i = 1, nn end if ! |y| < ycut end do ! j = 1, nn #if NDIMS == 3 end do ! k = 1, nn #endif /* NDIMS == 3 */ ! calculate magnetic field perturbation components from vector potential ! call curl(dh(1:3), pot(:,:,:,:), qpert(ibx:ibz,:,:,:)) end if ! bper /= 0.0 end if ! pert == 0 ! one-mode perturbation ! if (pert == 1) then ! of velocity ! if (abs(vper) > 0.0d+00) then ! reset the potential ! pot(:,:,:,:) = 0.0d+00 ! calculate the perturbation factor ! fv = vper * ylen / (pi2 * pi2 * pi * dh(1) * dh(2) * kper) ! calculate the perturbation profile ! #if NDIMS == 3 do k = 1, nn #endif /* NDIMS == 3 */ do j = 1, nn if (fy(j) > 0.0d+00) then ypl = pi * yl(j) / ylen ypu = pi * yu(j) / ylen do i = 1, nn if (fx(i) > 0.0d+00) then xpl = pi2 * kper * xl(i) xpu = pi2 * kper * xu(i) pot(3,i,j,k) = fv * fx(i) * fy(j) * (cos(xpl) - cos(xpu)) & * (cos(ypl) - cos(ypu)) end if ! |x| < xcut end do ! i = 1, nn end if ! |y| < ycut end do ! j = 1, nn #if NDIMS == 3 end do ! k = 1, nn #endif /* NDIMS == 3 */ ! calculate magnetic field components from vector potential ! call curl(dh(1:3), pot(:,:,:,:), qpert(ivx:ivz,:,:,:)) end if ! vper /= 0.0 ! of magnetic field ! if (abs(bper) > 0.0d+00) then ! reset the potential ! pot(:,:,:,:) = 0.0d+00 ! calculate the perturbation factor ! fv = bper * ylen / (pi2 * pi2 * pi * dh(1) * dh(2) * kper) ! calculate the perturbation profile ! #if NDIMS == 3 do k = 1, nn #endif /* NDIMS == 3 */ do j = 1, nn if (fy(j) > 0.0d+00) then ypl = pi * yl(j) / ylen ypu = pi * yu(j) / ylen do i = 1, nn if (fx(i) > 0.0d+00) then xpl = pi2 * kper * xl(i) xpu = pi2 * kper * xu(i) pot(3,i,j,k) = fv * fx(i) * fy(j) * (sin(xpl) - sin(xpu)) & * (sin(ypl) - sin(ypu)) end if ! |x| < xcut end do ! i = 1, nn end if ! |y| < ycut end do ! j = 1, nn #if NDIMS == 3 end do ! k = 1, nn #endif /* NDIMS == 3 */ ! calculate magnetic field components from vector potential ! call curl(dh(1:3), pot(:,:,:,:), qpert(ibx:ibz,:,:,:)) end if ! bper /= 0.0 end if ! pert == 1 ! prepare the random perturbation of velocity ! if (pert == 2) then if (abs(vper) > 0.0d+00) then ! iterate over the block position and initiate the velocity perturbation ! #if NDIMS == 3 do k = 1, nn #endif /* NDIMS == 3 */ do j = 1, nn if (fy(j) > 0.0d+00) then do i = 1, nn if (fx(i) > 0.0d+00) then ! calculate the velocity amplitude profile ! fv = vper * fx(i) * fy(j) ! add perturbation components ! do n = 1, nper #if NDIMS == 3 kv = kx(n) * xc(i) + ky(n) * yc(j) + kz(n) * zc(k) + ph(n) #else /* NDIMS == 3 */ kv = kx(n) * xc(i) + ky(n) * yc(j) + ph(n) #endif /* NDIMS == 3 */ va = fv * sin(kv) qpert(ivx,i,j,k) = qpert(ivx,i,j,k) + va * ux(n) qpert(ivy,i,j,k) = qpert(ivy,i,j,k) + va * uy(n) #if NDIMS == 3 qpert(ivz,i,j,k) = qpert(ivz,i,j,k) + va * uz(n) #endif /* NDIMS == 3 */ end do end if ! fx > 0.0 end do ! i = 1, nn end if ! fy > 0.0 end do ! j = 1, nn #if NDIMS == 3 end do ! k = 1, nn #endif /* NDIMS == 3 */ end if ! vper /= 0.0 end if ! pert == 2 ! iterate over all positions in the XZ plane ! #if NDIMS == 3 do k = 1, nn #endif /* NDIMS == 3 */ do i = 1, nn ! set the variable profiles with perturbations ! q(:,:) = qprof(:,:) + qpert(:,i,:,k) ! convert the primitive variables to the conservative ones ! call prim2cons(q(:,:), u(:,:), .true.) ! copy the primitive variables to the current block ! pdata%q(:,i,:,k) = q(:,:) ! copy the conserved variables to the current block ! pdata%u(:,i,:,k) = u(:,:) end do ! i = 1, nn #if NDIMS == 3 end do ! k = 1, nn #endif /* NDIMS == 3 */ #ifdef PROFILE ! stop accounting time for the problems setup ! call stop_timer(imp) #endif /* PROFILE */ !------------------------------------------------------------------------------- ! end subroutine setup_problem_user ! !=============================================================================== ! ! subroutine LOG_COSH: ! ------------------- ! ! Function calculates the logarithm of the hyperbolic cosine, which is ! the result of the integration of tanh(x). Direct calculation using ! Fortran intrinsic subroutines fails for large values of x, therefore ! the logarithm of cosh is approximated as |x| + log(1/2) for ! |x| > threshold. ! ! Arguments: ! ! x - function argument; ! !=============================================================================== ! function log_cosh(x) result(y) ! local variables are not implicit by default ! implicit none ! function arguments ! real(kind=8), intent(in) :: x real(kind=8) :: y ! local parameters ! real(kind=8), parameter :: th = acosh(huge(x)), lh = log(0.5d+00) ! !------------------------------------------------------------------------------- ! if (abs(x) < th) then y = log(cosh(x)) else y = abs(x) + lh end if !------------------------------------------------------------------------------- ! end function log_cosh ! !=============================================================================== ! ! subroutine UPDATE_SHAPES_USER: ! ----------------------------- ! ! Subroutine defines the regions updated by user. ! ! Arguments: ! ! pdata - pointer to the data block structure of the currently initialized ! block; ! time - time at the moment of update; ! dt - time step since the last update; ! !=============================================================================== ! subroutine update_shapes_user(pdata, time, dt) ! include external procedures and variables ! use blocks, only : block_data ! local variables are not implicit by default ! implicit none ! subroutine arguments ! type(block_data), pointer, intent(inout) :: pdata real(kind=8) , intent(in) :: time, dt ! !------------------------------------------------------------------------------- ! #ifdef PROFILE ! start accounting time for the shape update ! call start_timer(ims) #endif /* PROFILE */ #ifdef PROFILE ! stop accounting time for the shape update ! call stop_timer(ims) #endif /* PROFILE */ !------------------------------------------------------------------------------- ! end subroutine update_shapes_user ! !=============================================================================== ! ! subroutine UPDATE_SOURCES_USER: ! ------------------------------ ! ! Subroutine adds the user defined source terms. ! ! Arguments: ! ! pdata - the pointer to a data block; ! t, dt - the time and time increment; ! du - the array of variable increment; ! !=============================================================================== ! subroutine update_sources_user(pdata, t, dt, du) ! include external variables ! use blocks, only : block_data ! local variables are not implicit by default ! implicit none ! subroutine arguments ! type(block_data), pointer , intent(inout) :: pdata real(kind=8) , intent(in) :: t, dt real(kind=8), dimension(:,:,:,:), intent(inout) :: du ! !------------------------------------------------------------------------------- ! #ifdef PROFILE ! start accounting time for source terms ! call start_timer(imu) #endif /* PROFILE */ #ifdef PROFILE ! stop accounting time for source terms ! call stop_timer(imu) #endif /* PROFILE */ !------------------------------------------------------------------------------- ! end subroutine update_sources_user ! !=============================================================================== ! ! subroutine GRAVITATIONAL_ACCELERATION_USER: ! ------------------------------------------ ! ! Subroutine returns the user defined gravitational acceleration. ! ! Arguments: ! ! t, dt - time and the time increment; ! x, y, z - rectangular coordinates; ! acc - vector of the gravitational acceleration; ! !=============================================================================== ! subroutine gravitational_acceleration_user(t, dt, x, y, z, acc) ! include external procedures and variables ! use parameters, only : get_parameter ! local variables are not implicit by default ! implicit none ! subroutine arguments ! real(kind=8) , intent(in) :: t, dt real(kind=8) , intent(in) :: x, y, z real(kind=8), dimension(3), intent(out) :: acc ! !------------------------------------------------------------------------------- ! #ifdef PROFILE ! start accounting time for the gravitational acceleration calculation ! call start_timer(img) #endif /* PROFILE */ ! reset gravitational acceleration ! acc(:) = 0.0d+00 #ifdef PROFILE ! stop accounting time for the gravitational acceleration calculation ! call stop_timer(img) #endif /* PROFILE */ !------------------------------------------------------------------------------- ! end subroutine gravitational_acceleration_user ! !=============================================================================== ! ! subroutine BOUNDARY_USER_X: ! -------------------------- ! ! Subroutine updates ghost zones within the specific region along ! the X direction. ! ! Arguments: ! ! ic - the block side along the X direction for the ghost zone update; ! jl, ju - the cell index limits for the Y direction; ! kl, ku - the cell index limits for the Z direction; ! t, dt - time and time increment; ! x, y, z - the block coordinates; ! qn - the array of variables to update; ! !=============================================================================== ! subroutine boundary_user_x(ic, jl, ju, kl, ku, t, dt, x, y, z, qn) ! import external procedures and variables ! use coordinates , only : nn => bcells, nb, ne, nbl, neu use equations , only : ivx, ibx, iby, ibp #if NDIMS == 3 use equations , only : ibz #endif /* NDIMS == 3 */ ! local variables are not implicit by default ! implicit none ! subroutine arguments ! integer , intent(in) :: ic integer , intent(in) :: jl, ju integer , intent(in) :: kl, ku real(kind=8) , intent(in) :: t, dt real(kind=8), dimension(:) , intent(in) :: x real(kind=8), dimension(:) , intent(in) :: y real(kind=8), dimension(:) , intent(in) :: z real(kind=8), dimension(:,:,:,:), intent(inout) :: qn ! local variables ! integer :: im2, im1, i , ip1, ip2 integer :: jm2, jm1, j , jp1, jp2 integer :: k = 1 #if NDIMS == 3 integer :: km2, km1, kp1, kp2 #endif /* NDIMS == 3 */ real(kind=8) :: dx, dy, dxy #if NDIMS == 3 real(kind=8) :: dz, dxz #endif /* NDIMS == 3 */ ! !------------------------------------------------------------------------------- ! #ifdef PROFILE ! start accounting time for the boundary update ! call start_timer(imb) #endif /* PROFILE */ ! process case with magnetic field, otherwise revert to standard outflow ! if (ibx > 0) then ! get the cell sizes and their ratios ! dx = x(2) - x(1) dy = y(2) - y(1) #if NDIMS == 3 dz = z(2) - z(1) #endif /* NDIMS == 3 */ dxy = dx / dy #if NDIMS == 3 dxz = dx / dz #endif /* NDIMS == 3 */ ! process left and right side boundary separatelly ! if (ic == 1) then ! iterate over left-side ghost layers ! do i = nbl, 1, -1 ! calculate neighbor cell indices ! ip1 = min(nn, i + 1) ip2 = min(nn, i + 2) ! iterate over boundary layer ! #if NDIMS == 3 do k = kl, ku km2 = max( 1, k - 2) km1 = max( 1, k - 1) kp1 = min(nn, k + 1) kp2 = min(nn, k + 2) #endif /* NDIMS == 3 */ do j = jl, ju jm2 = max( 1, j - 2) jm1 = max( 1, j - 1) jp1 = min(nn, j + 1) jp2 = min(nn, j + 2) ! make the normal derivative zero ! qn(:,i,j,k) = qn(:,nb,j,k) ! prevent the inflow ! qn(ivx,i,j,k) = min(0.0d+00, qn(ivx,nb,j,k)) ! update the normal component of magnetic field from divergence-free condition ! qn(ibx,i,j,k) = qn(ibx,ip2,j,k) & + (qn(iby,ip1,jp1,k) - qn(iby,ip1,jm1,k)) * dxy #if NDIMS == 3 qn(ibx,i,j,k) = qn(ibx,i ,j,k) & + (qn(ibz,ip1,j,kp1) - qn(ibz,ip1,j,km1)) * dxz #endif /* NDIMS == 3 */ qn(ibp,i,j,k) = 0.0d+00 end do ! j = jl, ju #if NDIMS == 3 end do ! k = kl, ku #endif /* NDIMS == 3 */ end do ! i = nbl, 1, -1 else ! ic == 1 ! iterate over right-side ghost layers ! do i = neu, nn ! calculate neighbor cell indices ! im1 = max( 1, i - 1) im2 = max( 1, i - 2) ! iterate over boundary layer ! #if NDIMS == 3 do k = kl, ku km1 = max( 1, k - 1) kp1 = min(nn, k + 1) km2 = max( 1, k - 2) kp2 = min(nn, k + 2) #endif /* NDIMS == 3 */ do j = jl, ju jm1 = max( 1, j - 1) jp1 = min(nn, j + 1) jm2 = max( 1, j - 2) jp2 = min(nn, j + 2) ! make the normal derivative zero ! qn(:,i,j,k) = qn(:,ne,j,k) ! prevent the inflow ! qn(ivx,i,j,k) = max(0.0d+00, qn(ivx,ne,j,k)) ! update the normal component of magnetic field from divergence-free condition ! qn(ibx,i,j,k) = qn(ibx,im2,j,k) & + (qn(iby,im1,jm1,k) - qn(iby,im1,jp1,k)) * dxy #if NDIMS == 3 qn(ibx,i,j,k) = qn(ibx,i ,j,k) & + (qn(ibz,im1,j,km1) - qn(ibz,im1,j,kp1)) * dxz #endif /* NDIMS == 3 */ qn(ibp,i,j,k) = 0.0d+00 end do ! j = jl, ju #if NDIMS == 3 end do ! k = kl, ku #endif /* NDIMS == 3 */ end do ! i = neu, nn end if ! ic == 1 else ! ibx > 0 if (ic == 1) then do i = nbl, 1, -1 #if NDIMS == 3 qn( : ,i,jl:ju,kl:ku) = qn( : ,nb,jl:ju,kl:ku) qn(ivx,i,jl:ju,kl:ku) = min(qn(ivx,nb,jl:ju,kl:ku), 0.0d+00) #else /* NDIMS == 3 */ qn( : ,i,jl:ju, : ) = qn( : ,nb,jl:ju, : ) qn(ivx,i,jl:ju, : ) = min(qn(ivx,nb,jl:ju, : ), 0.0d+00) #endif /* NDIMS == 3 */ end do ! i = nbl, 1, -1 else do i = neu, nn #if NDIMS == 3 qn( : ,i,jl:ju,kl:ku) = qn( : ,ne,jl:ju,kl:ku) qn(ivx,i,jl:ju,kl:ku) = max(qn(ivx,ne,jl:ju,kl:ku), 0.0d+00) #else /* NDIMS == 3 */ qn( : ,i,jl:ju, : ) = qn( : ,ne,jl:ju, : ) qn(ivx,i,jl:ju, : ) = max(qn(ivx,ne,jl:ju, : ), 0.0d+00) #endif /* NDIMS == 3 */ end do ! i = neu, nn end if end if ! ibx > 0 #ifdef PROFILE ! stop accounting time for the boundary update ! call stop_timer(imb) #endif /* PROFILE */ !------------------------------------------------------------------------------- ! end subroutine boundary_user_x ! !=============================================================================== ! ! subroutine BOUNDARY_USER_Y: ! -------------------------- ! ! Subroutine updates ghost zones within the specific region along ! the Y direction. ! ! Arguments: ! ! jc - the block side along the Y direction for the ghost zone update; ! il, iu - the cell index limits for the X direction; ! kl, ku - the cell index limits for the Z direction; ! t, dt - time and time increment; ! x, y, z - the block coordinates; ! qn - the array of variables to update; ! !=============================================================================== ! subroutine boundary_user_y(jc, il, iu, kl, ku, t, dt, x, y, z, qn) ! import external procedures and variables ! use coordinates , only : nn => bcells, nb, ne, nbl, neu use equations , only : nv use equations , only : idn, ivy, ipr, ibx, iby, ibz, ibp ! local variables are not implicit by default ! implicit none ! subroutine arguments ! integer , intent(in) :: jc integer , intent(in) :: il, iu integer , intent(in) :: kl, ku real(kind=8) , intent(in) :: t, dt real(kind=8), dimension(:) , intent(in) :: x real(kind=8), dimension(:) , intent(in) :: y real(kind=8), dimension(:) , intent(in) :: z real(kind=8), dimension(:,:,:,:), intent(inout) :: qn ! local variables ! integer :: i, im1, ip1 integer :: j, jm1, jp1, jm2, jp2 integer :: k = 1 #if NDIMS == 3 integer :: km1, kp1 #endif /* NDIMS == 3 */ real(kind=8) :: dx, dy, dyx #if NDIMS == 3 real(kind=8) :: dz, dyz #endif /* NDIMS == 3 */ real(kind=8) :: fl, fr ! !------------------------------------------------------------------------------- ! #ifdef PROFILE ! start accounting time for the boundary update ! call start_timer(imb) #endif /* PROFILE */ ! process case with magnetic field, otherwise revert to standard outflow ! if (ibx > 0) then ! get the cell sizes and their ratios ! dx = x(2) - x(1) dy = y(2) - y(1) #if NDIMS == 3 dz = z(2) - z(1) #endif /* NDIMS == 3 */ dyx = dy / dx #if NDIMS == 3 dyz = dy / dz #endif /* NDIMS == 3 */ ! process left and right side boundary separatelly ! if (jc == 1) then ! iterate over left-side ghost layers ! do j = nbl, 1, -1 ! calculate neighbor cell indices ! jp1 = min(nn, j + 1) jp2 = min(nn, j + 2) ! calculate variable decay coefficients ! fr = (dy * (nb - j - 5.0d-01)) / blim fl = 1.0d+00 - fr ! iterate over boundary layer ! #if NDIMS == 3 do k = kl, ku km1 = max( 1, k - 1) kp1 = min(nn, k + 1) #endif /* NDIMS == 3 */ do i = il, iu im1 = max( 1, i - 1) ip1 = min(nn, i + 1) ! make normal derivatives zero ! qn(1:nv,i,j,k) = qn(1:nv,i,nb,k) ! decay density and pressure to their limits ! qn(idn,i,j,k) = fl * qn(idn,i,nb,k) + fr * dens if (ipr > 0) qn(ipr,i,j,k) = fl * qn(ipr,i,nb,k) + fr * pres ! decay magnetic field to its limit ! qn(ibx,i,j,k) = fl * qn(ibx,i,nb,k) - fr * bamp qn(ibz,i,j,k) = fl * qn(ibz,i,nb,k) + fr * bgui ! update By from div(B)=0 ! qn(iby,i,j,k) = qn(iby,i,jp2,k) & + (qn(ibx,ip1,jp1,k) - qn(ibx,im1,jp1,k)) * dyx #if NDIMS == 3 qn(iby,i,j,k) = qn(iby,i,j ,k) & + (qn(ibz,i,jp1,kp1) - qn(ibz,i,jp1,km1)) * dyz #endif /* NDIMS == 3 */ qn(ibp,i,j,k) = 0.0d+00 end do ! i = il, iu #if NDIMS == 3 end do ! k = kl, ku #endif /* NDIMS == 3 */ end do ! j = nbl, 1, -1 else ! jc = 1 ! iterate over right-side ghost layers ! do j = neu, nn ! calculate neighbor cell indices ! jm1 = max( 1, j - 1) jm2 = max( 1, j - 2) ! calculate variable decay coefficients ! fr = (dy * (j - ne - 5.0d-01)) / blim fl = 1.0d+00 - fr ! iterate over boundary layer ! #if NDIMS == 3 do k = kl, ku km1 = max( 1, k - 1) kp1 = min(nn, k + 1) #endif /* NDIMS == 3 */ do i = il, iu im1 = max( 1, i - 1) ip1 = min(nn, i + 1) ! make normal derivatives zero ! qn(1:nv,i,j,k) = qn(1:nv,i,ne,k) ! decay density and pressure to their limits ! qn(idn,i,j,k) = fl * qn(idn,i,ne,k) + fr * dens if (ipr > 0) qn(ipr,i,j,k) = fl * qn(ipr,i,ne,k) + fr * pres ! decay magnetic field to its limit ! qn(ibx,i,j,k) = fl * qn(ibx,i,ne,k) + fr * bamp qn(ibz,i,j,k) = fl * qn(ibz,i,ne,k) + fr * bgui ! update By from div(B)=0 ! qn(iby,i,j,k) = qn(iby,i,jm2,k) & + (qn(ibx,im1,jm1,k) - qn(ibx,ip1,jm1,k)) * dyx #if NDIMS == 3 qn(iby,i,j,k) = qn(iby,i,j ,k) & + (qn(ibz,i,jm1,km1) - qn(ibz,i,jm1,kp1)) * dyz #endif /* NDIMS == 3 */ qn(ibp,i,j,k) = 0.0d+00 end do ! i = il, iu #if NDIMS == 3 end do ! k = kl, ku #endif /* NDIMS == 3 */ end do ! j = neu, nn end if ! jc = 1 else ! ibx > 0 if (jc == 1) then do j = nbl, 1, -1 #if NDIMS == 3 qn(1:nv,il:iu,j,kl:ku) = qn(1:nv,il:iu,nb,kl:ku) qn(ivy ,il:iu,j,kl:ku) = min(0.0d+00, qn(ivy,il:iu,nb,kl:ku)) #else /* NDIMS == 3 */ qn(1:nv,il:iu,j, : ) = qn(1:nv,il:iu,nb, : ) qn(ivy ,il:iu,j, : ) = min(0.0d+00, qn(ivy,il:iu,nb, : )) #endif /* NDIMS == 3 */ end do ! j = nbl, 1, -1 else do j = neu, nn #if NDIMS == 3 qn(1:nv,il:iu,j,kl:ku) = qn(1:nv,il:iu,ne,kl:ku) qn(ivy ,il:iu,j,kl:ku) = max(0.0d+00, qn(ivy,il:iu,ne,kl:ku)) #else /* NDIMS == 3 */ qn(1:nv,il:iu,j, : ) = qn(1:nv,il:iu,ne, : ) qn(ivy ,il:iu,j, : ) = max(0.0d+00, qn(ivy,il:iu,ne, : )) #endif /* NDIMS == 3 */ end do ! j = neu, nn end if end if ! ibx > 0 #ifdef PROFILE ! stop accounting time for the boundary update ! call stop_timer(imb) #endif /* PROFILE */ !------------------------------------------------------------------------------- ! end subroutine boundary_user_y ! !=============================================================================== ! ! subroutine BOUNDARY_USER_Z: ! -------------------------- ! ! Subroutine updates ghost zones within the specific region along ! the Z direction. ! ! Arguments: ! ! kc - the block side along the Z direction for the ghost zone update; ! il, iu - the cell index limits for the X direction; ! jl, ju - the cell index limits for the Y direction; ! t, dt - time and time increment; ! x, y, z - the block coordinates; ! qn - the array of variables to update; ! !=============================================================================== ! subroutine boundary_user_z(kc, il, iu, jl, ju, t, dt, x, y, z, qn) ! local variables are not implicit by default ! implicit none ! subroutine arguments ! integer , intent(in) :: kc integer , intent(in) :: il, iu integer , intent(in) :: jl, ju real(kind=8) , intent(in) :: t, dt real(kind=8), dimension(:) , intent(in) :: x real(kind=8), dimension(:) , intent(in) :: y real(kind=8), dimension(:) , intent(in) :: z real(kind=8), dimension(:,:,:,:), intent(inout) :: qn ! !------------------------------------------------------------------------------- ! #ifdef PROFILE ! start accounting time for the boundary update ! call start_timer(imb) #endif /* PROFILE */ #ifdef PROFILE ! stop accounting time for the boundary update ! call stop_timer(imb) #endif /* PROFILE */ !------------------------------------------------------------------------------- ! end subroutine boundary_user_z !=============================================================================== ! end module user_problem