amun-code/sources/forcing.F90

1809 lines
50 KiB
Fortran
Raw Normal View History

!!******************************************************************************
!!
!! 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-2022 Grzegorz Kowal <grzegorz@amuncode.org>
!!
!! 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 <http://www.gnu.org/licenses/>.
!!
!!*****************************************************************************
!!
!! module: FORCING
!!
!! This module provides energy injection, e.g. turbulence driving, supernova
!! explosions, etc.
!!
!!*****************************************************************************
!
module forcing
implicit none
! interfaces for procedure pointers
!
abstract interface
subroutine update_forcing_iface(dt)
real(kind=8), intent(in) :: dt
end subroutine
end interface
! pointers to the forcing methods
!
procedure(update_forcing_iface), pointer, save :: update_forcing => null()
! flag indicating if the energy injection is enabled and if it is done in
! the Fourier space
!
logical, save :: forcing_enabled = .false.
logical, save :: forcing_fourier = .false.
logical, save :: energy_profile = .false.
! implemented injection methods
!
integer, parameter :: injection_none = 0
integer, parameter :: injection_eddy = 1
integer, parameter :: injection_oh = 2
integer, parameter :: injection_alvelius = 3
! implemented driving force spectral profiles
!
integer, parameter :: profile_normal = 1
integer, parameter :: profile_parabolic = 2
integer, parameter :: profile_powerlaw = 3
! driving force method and spectral profile
!
integer, save :: injection_method = injection_none
integer, save :: injection_profile = profile_normal
! forcing parameters
!
real(kind=8), save :: power = 1.0d+00
real(kind=8), save :: rate = 1.0d+03
real(kind=8), save :: amp = 1.0d-03
real(kind=8), save :: del = 1.0d-01
real(kind=8), save :: xlo = 9.9d+99
real(kind=8), save :: xup = 9.9d+99
real(kind=8), save :: xth = 0.0d+00
real(kind=8), save :: xfc = 1.0d+00
real(kind=8), save :: ylo = 9.9d+99
real(kind=8), save :: yup = 9.9d+99
real(kind=8), save :: yth = 0.0d+00
real(kind=8), save :: yfc = 1.0d+00
#if NDIMS == 3
real(kind=8), save :: zlo = 9.9d+99
real(kind=8), save :: zup = 9.9d+99
real(kind=8), save :: zth = 0.0d+00
real(kind=8), save :: zfc = 1.0d+00
#endif /* NDIMS == 3 */
! Fourier profile parameters
!
integer , save :: nmodes = 0
real(kind=8), save :: fpower = 1.0d+00
real(kind=8), save :: lscale = 5.0d-01
real(kind=8), save :: tscale = 1.0d+00
real(kind=8), save :: vscale = 5.0d-01
real(kind=8), save :: ascale = 5.0d-01
real(kind=8), save :: kf = 2.0d+00
real(kind=8), save :: kl = 0.0d+00
real(kind=8), save :: ku = 4.0d+00
real(kind=8), save :: kc = 5.0d-01
real(kind=8), save :: pidx = - 5.0d+00 / 3.0d+00
real(kind=8), save :: fmin = 1.0d-08
! module parameters
!
real(kind=8), save :: dinj = 0.0d+00
real(kind=8), save :: einj = 0.0d+00
real(kind=8), save :: rinj = 0.0d+00
real(kind=8), save :: arms = 0.0d+00
! bound for the driving mode wave number
!
integer, save :: kmax = 4
! arrays for driving mode positions and amplitudes
!
integer , dimension(:,: ) , allocatable :: kmodes
real(kind=8), dimension(:) , allocatable :: fmodes
! vector of driving directions in Alfvelius method
!
real(kind=8), dimension(:,:) , allocatable :: e1vecs, e2vecs
! array for driving mode coefficients
!
complex(kind=8), dimension(:,:), allocatable, target :: fcoefs
! velocity Fourier coefficients in Alfvelius method
!
complex(kind=8), dimension(:,:), allocatable :: vcoefs
! by default everything is private
!
private
! declare public subroutines
!
public :: initialize_forcing, finalize_forcing, print_forcing
public :: update_forcing
public :: forcing_enabled, einj, rinj, arms, nmodes, fcoefs
!- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
!
contains
!
!===============================================================================
!!
!!*** PUBLIC SUBROUTINES *****************************************************
!!
!===============================================================================
!
!===============================================================================
!
! subroutine INITIALIZE_FORCING:
! -----------------------------
!
! Subroutine initializes module FORCING.
!
! Arguments:
!
! verbose - flag determining if the subroutine should be verbose;
! status - return flag of the procedure execution status;
!
!===============================================================================
!
subroutine initialize_forcing(verbose, status)
use constants , only : pi2
use helpers , only : print_message
use parameters, only : get_parameter
use random , only : randuni, randnorz
implicit none
logical, intent(in) :: verbose
integer, intent(out) :: status
character(len=32) :: wfmt = "(1x,'WARNING:',1x,a)"
character(len=64) :: injection = "none"
character(len=64) :: profile_type = "gauss"
character(len=64) :: profile_energy = "off"
integer :: i, j, l, k2
#if NDIMS == 3
integer :: k = 0
#endif /* NDIMS == 3 */
real(kind=8) :: kl2, ku2, kv2, kv
real(kind=8) :: fa, fi, uu
#if NDIMS == 3
real(kind=8) :: phi
#endif /* NDIMS == 3 */
real(kind=8), dimension(NDIMS) :: u, v
integer, dimension(:), allocatable :: kcount
character(len=*), parameter :: loc = 'FORCING::initialize_forcing()'
!-------------------------------------------------------------------------------
!
status = 0
! obtain the chosen injection method
!
call get_parameter("driving_method", injection)
! get the injection profile parameters
!
call get_parameter("driving_x_lower", xlo)
call get_parameter("driving_x_upper", xup)
call get_parameter("driving_y_lower", ylo)
call get_parameter("driving_y_upper", yup)
#if NDIMS == 3
call get_parameter("driving_z_lower", zlo)
call get_parameter("driving_z_upper", zup)
#endif /* NDIMS == 3 */
xth = xup - xlo
if (xth > 0.0d+00) then
xfc = 2.5d-01 * pi2 / xth
else
xfc = 1.0d+00
end if
yth = yup - ylo
if (yth > 0.0d+00) then
yfc = 2.5d-01 * pi2 / yth
else
yfc = 1.0d+00
end if
#if NDIMS == 3
zth = zup - zlo
if (zth > 0.0d+00) then
zfc = 2.5d-01 * pi2 / zth
else
zfc = 1.0d+00
end if
#endif /* NDIMS == 3 */
! select the energy injection method
!
select case(trim(injection))
case("eddies", "eddy")
forcing_enabled = .true.
injection_method = injection_eddy
update_forcing => update_forcing_eddies
call get_parameter("injection_power", power)
call get_parameter("injection_rate" , rate )
call get_parameter("eddy_amplitude" , amp )
call get_parameter("eddy_width" , del )
case("OrnsteinUhlenbeck", "ornsteinuhlenbeck", "oh")
forcing_enabled = .true.
forcing_fourier = .true.
injection_method = injection_oh
update_forcing => update_forcing_oh
! get the driving power
!
call get_parameter("driving_timescale", tscale)
if (tscale <= 0.0d+00) then
if (verbose) then
write(*,*)
write(*,wfmt) "'driving_timescale' must be larger than zero!"
write(*,wfmt) "Resetting it to the default value: 1.0!"
end if
tscale = 1.0d+00
end if
case("Alvelius", "alvelius", "al")
forcing_enabled = .true.
forcing_fourier = .true.
injection_method = injection_alvelius
update_forcing => update_forcing_alvelius
! get the driving power
!
call get_parameter("driving_power", fpower)
if (fpower <= 0.0d+00) then
if (verbose) then
write(*,*)
write(*,wfmt) "'driving_power' must be larger than zero!"
write(*,wfmt) "Resetting it to the default value: 1.0!"
end if
fpower = 1.0d+00
end if
case default
injection_method = injection_none
if (verbose .and. injection /= "none") then
write(*,*)
write(*,wfmt) "Unknown injection method! Forcing disabled!"
end if
end select
! initialization of the Fourier space driving
!
if (forcing_fourier) then
call get_parameter("driving_wavenumber" , kf)
call get_parameter("driving_profile_width", kc)
call get_parameter("driving_profile_lower", kl)
call get_parameter("driving_profile_upper", ku)
call get_parameter("amplitude_threshold" , fmin)
if (kf < 1.0d+00) then
if (verbose) then
write(*,*)
write(*,wfmt) "'driving_wavenumber' must be larger than 1.0!"
write(*,wfmt) "Resetting it to the default value: 2.0!"
end if
kf = 2.0d+00
end if
if (kc <= 0.0d+00) then
if (verbose) then
write(*,*)
write(*,wfmt) "'driving_profile_width' must be larger than 0.0!"
write(*,wfmt) "Resetting it to the default value: 1.0!"
end if
kc = 1.0d+00
end if
if (kc > kf) then
if (verbose) then
write(*,*)
write(*,wfmt) "'driving_profile_width' must be smaller than " // &
"'driving_wavenumber'!"
write(*,wfmt) "Resetting it to the half of 'driving_wavenumber'!"
end if
kc = 5.0d-01 * kf
end if
if (kl >= kf) then
if (verbose) then
write(*,*)
write(*,wfmt) "'driving_profile_lower' must be smaller than " // &
"'driving_wavenumber'!"
write(*,wfmt) "Setting it to 'driving_wavenumber' " // &
"- 'driving_profile_width'!"
end if
kl = kf - kc
end if
if (ku <= kf) then
if (verbose) then
write(*,*)
write(*,wfmt) "'driving_profile_upper' must be larger than " // &
"'driving_wavenumber'!"
write(*,wfmt) "Setting it to 'driving_wavenumber' " // &
"+ 'driving_profile_width'!"
end if
ku = kf + kc
end if
fmin = max(1.0d-08, fmin)
kl2 = kl**2
ku2 = ku**2
! get the upper bound for the wave number
!
kmax = ceiling(ku)
! allocate arrays to count the modes with the same wave number
!
allocate(kcount(NDIMS * kmax**2), stat = status)
if (status == 0) then
! initialize mode counts
!
kcount(:) = 0
! count the modes with the same k, get the minimum sufficient number of
! driving modes
!
#if NDIMS == 3
do i = 1, kmax
do j = -kmax, kmax
do k = -kmax, kmax
k2 = i**2 + j**2 + k**2
kv2 = 1.0d+00 * k2
if (kl2 <= kv2 .and. kv2 <= ku2) then
nmodes = nmodes + 1
kcount(k2) = kcount(k2) + 2
end if
end do
end do
end do
do j = 1, kmax
do k = -kmax, kmax
k2 = j**2 + k**2
kv2 = 1.0d+00 * k2
if (kl2 <= kv2 .and. kv2 <= ku2) then
nmodes = nmodes + 1
kcount(k2) = kcount(k2) + 2
end if
end do
end do
do k = 1, kmax
k2 = k**2
kv2 = 1.0d+00 * k2
if (kl2 <= kv2 .and. kv2 <= ku2) then
nmodes = nmodes + 1
kcount(k2) = kcount(k2) + 2
end if
end do
#else /* NDIMS == 3 */
do i = 1, kmax
do j = -kmax, kmax
k2 = i**2 + j**2
kv2 = 1.0d+00 * k2
if (kl2 <= kv2 .and. kv2 <= ku2) then
nmodes = nmodes + 1
kcount(k2) = kcount(k2) + 2
end if
end do
end do
do j = 1, kmax
k2 = j**2
kv2 = 1.0d+00 * k2
if (kl2 <= kv2 .and. kv2 <= ku2) then
nmodes = nmodes + 1
kcount(k2) = kcount(k2) + 2
end if
end do
#endif /* NDIMS == 3 */
! allocate arrays for driving modes (positions, amplitudes and coefficients)
!
allocate(e1vecs(nmodes,NDIMS), e2vecs(nmodes,NDIMS), &
kmodes(nmodes,NDIMS), fmodes(nmodes), &
fcoefs(nmodes,NDIMS), stat = status)
if (status == 0) then
! prepare wave vectors
!
l = 0
#if NDIMS == 3
do i = 1, kmax
do j = -kmax, kmax
do k = -kmax, kmax
k2 = i**2 + j**2 + k**2
kv2 = 1.0d+00 * k2
if (kl2 <= kv2 .and. kv2 <= ku2) then
l = l + 1
kmodes(l,1) = i
kmodes(l,2) = j
kmodes(l,3) = k
end if
end do
end do
end do
do j = 1, kmax
do k = -kmax, kmax
k2 = j**2 + k**2
kv2 = 1.0d+00 * k2
if (kl2 <= kv2 .and. kv2 <= ku2) then
l = l + 1
kmodes(l,1) = 0
kmodes(l,2) = j
kmodes(l,3) = k
end if
end do
end do
do k = 1, kmax
k2 = k**2
kv2 = 1.0d+00 * k2
if (kl2 <= kv2 .and. kv2 <= ku2) then
l = l + 1
kmodes(l,1) = 0
kmodes(l,2) = 0
kmodes(l,3) = k
end if
end do
#else /* NDIMS == 3 */
do i = 1, kmax
do j = -kmax, kmax
k2 = i**2 + j**2
kv2 = 1.0d+00 * k2
if (kl2 <= kv2 .and. kv2 <= ku2) then
l = l + 1
kmodes(l,1) = i
kmodes(l,2) = j
end if
end do
end do
do j = 1, kmax
k2 = j**2
kv2 = 1.0d+00 * k2
if (kl2 <= kv2 .and. kv2 <= ku2) then
l = l + 1
kmodes(l,1) = 0
kmodes(l,2) = j
end if
end do
#endif /* NDIMS == 3 */
! generate random vectors, perpendicular to the wave vector
!
do l = 1, nmodes
u(:) = kmodes(l,:)
#if NDIMS == 3
if (abs(kmodes(l,3)) < abs(kmodes(l,1))) then
v(:) = (/ kmodes(l,2), - kmodes(l,1), 0 /)
else
v(:) = (/ 0, - kmodes(l,3), kmodes(l,2) /)
end if
#else /* NDIMS == 3 */
v(:) = (/ kmodes(l,2), - kmodes(l,1) /)
#endif /* NDIMS == 3 */
uu = dot_product(v(:), v(:))
if (uu > 0.0d+00) then
e1vecs(l,:) = v(:)
#if NDIMS == 3
e2vecs(l,1) = u(2) * v(3) - u(3) * v(2)
e2vecs(l,2) = u(3) * v(1) - u(1) * v(3)
e2vecs(l,3) = u(1) * v(2) - u(2) * v(1)
#endif /* NDIMS == 3 */
e1vecs(l,:) = e1vecs(l,:) / sqrt(sum(e1vecs(l,:)**2))
#if NDIMS == 3
e2vecs(l,:) = e2vecs(l,:) / sqrt(sum(e2vecs(l,:)**2))
#else /* NDIMS == 3 */
e2vecs(l,:) = 0.0d+00
#endif /* NDIMS == 3 */
else
call print_message(loc, "v(:) is ZERO!")
end if
end do
! determine if the profile is set by energy or amplitude
!
call get_parameter("profile_by_energy", profile_energy)
select case(trim(profile_energy))
case ("on", "ON", "t", "T", "y", "Y", "true", "TRUE", "yes", "YES")
energy_profile = .true.
case default
energy_profile = .false.
kcount(:) = 1
end select
! initialize driving mode amplitudes depending on the chosen profile
!
call get_parameter("driving_profile", profile_type)
select case(trim(profile_type))
! --- power-law profile ---
!
case("power-law", "powerlaw", "band")
injection_profile = profile_powerlaw
! get the power index of the power-law distribution
!
call get_parameter("powerlaw_index", pidx)
! calculate amplitudes of driving modes
!
fi = 0.0d+00
do l = 1, nmodes
k2 = sum(kmodes(l,:)**2)
kv = sqrt(1.0d+00 * k2)
fa = kv**pidx / kcount(k2)
fi = fi + fa
fmodes(l) = sqrt(fa)
end do
fmodes(:) = fmodes(:) / sqrt(fi)
! --- normal distribution profile ---
!
case("gauss", "gaussian", "normal")
injection_profile = profile_normal
! calculate amplitudes of driving modes
!
fi = 0.0d+00
do l = 1, nmodes
k2 = sum(kmodes(l,:)**2)
kv = sqrt(1.0d+00 * k2)
fa = exp(- 0.5d+00 * ((kv - kf) / kc)**2) / kcount(k2)
fi = fi + fa
fmodes(l) = sqrt(fa)
end do
fmodes(:) = fmodes(:) / sqrt(fi)
! --- parabolic profile ---
!
case("parabolic")
injection_profile = profile_parabolic
! correct the injection scale and width
!
kf = 0.5d+00 * (ku + kl)
kc = 0.5d+00 * (ku - kl)
! calculate amplitudes of driving modes
!
fi = 0.0d+00
do l = 1, nmodes
k2 = sum(kmodes(l,:)**2)
kv = sqrt(1.0d+00 * k2)
fa = 4.0d+00 * (kv - kl) * (ku - kv) / (ku - kl)**2
fa = max(0.0d+00, fa)**2 / kcount(k2)
fi = fi + fa
fmodes(l) = sqrt(fa)
end do
fmodes(:) = fmodes(:) / sqrt(fi)
! --- normal distribution profile ---
!
case default
! warn if the specified profile is not implemented and the normal one is used
!
if (verbose .and. .not. (profile_type == "normal" .or. &
profile_type == "gauss" .or. &
profile_type == "gaussian")) then
write(*,*)
write(*,wfmt) "Unknown spectral profile of driving!"
write(*,wfmt) "Available profiles: " // &
"'power-law', 'normal', 'parabolic'."
write(*,wfmt) "Using default normal distribution profile " // &
"'gaussian'."
end if
injection_profile = profile_normal
! calculate amplitudes of driving modes
!
fi = 0.0d+00
do l = 1, nmodes
k2 = sum(kmodes(l,:)**2)
kv = sqrt(1.0d+00 * k2)
fa = exp(- 0.5d+00 * ((kv - kf) / kc)**2) / kcount(k2)
fi = fi + fa
fmodes(l) = sqrt(fa)
end do
do l = 1, nmodes
k2 = sum(kmodes(l,:)**2)
fmodes(l) = fmodes(l) / sqrt(fi)
end do
end select
! === OrnsteinUhlenbeck driving ===
!
if (injection_method == injection_oh) then
! get the characteristic driving length, velocity, acceleration scales, and
! determine the estimated driving power (since the main injection is through
! the forcing-velocity correlation, it may not represent the actual
! instantenous injection rate)
!
lscale = 1.0d+00 / kf
vscale = lscale / tscale
ascale = sqrt(2.0d+00) * lscale / tscale**2 / sqrt(tscale)
fpower = vscale**2 / tscale
! normalize the driving amplitude profile to the driving power
!
fmodes(:) = ascale * fmodes(:)
! generate the initial driving mode coefficients
!
do l = 1, nmodes
#if NDIMS == 3
phi = pi2 * randuni()
fcoefs(l,:) = fmodes(l) * (e1vecs(l,:) * cos(phi) &
+ e2vecs(l,:) * sin(phi)) * randnorz()
#else /* NDIMS == 3 */
fcoefs(l,:) = fmodes(l) * e1vecs(l,:) * randnorz()
#endif /* NDIMS == 3 */
end do ! l = 1, nmodes
! calculate the rms of acceleration
!
arms = sqrt(5.0d-01 * sum(real(fcoefs(:,:))**2 &
+ aimag(fcoefs(:,:))**2))
end if ! injection = 'OH'
! === Alvelius driving ===
!
if (injection_method == injection_alvelius) then
! get the characteristic driving length, velocity, time, and acceleration scales
!
lscale = 1.0d+00 / kf
tscale = sqrt(lscale / sqrt(2.0d+00 * fpower))
vscale = lscale / tscale
ascale = vscale / tscale
! normalize the driving amplitude profile to the driving power
!
fmodes(:) = sqrt(2.0d+00) * ascale * fmodes(:)
! reset the driving coefficients and calculate the rms of acceleration
!
fcoefs(:,:) = cmplx(0.0d+00, 0.0d+00, kind=8)
arms = 0.0d+00
! allocate vectors of velocity Fourier coefficients
!
allocate(vcoefs(nmodes,NDIMS), stat = status)
if (status /= 0) &
call print_message(loc, "Cannot allocate vcoefs(:,:)!")
end if ! injection = 'Alvelius'
end if ! allocation of driving mode arrays
end if ! allocation of kcount
! deallocate mode counter
!
if (allocated(kcount)) deallocate(kcount)
end if ! Fourier forcing
!-------------------------------------------------------------------------------
!
end subroutine initialize_forcing
!
!===============================================================================
!
! subroutine FINALIZE_FORCING:
! ---------------------------
!
! Subroutine releases memory used by the module variables.
!
! Arguments:
!
! status - return flag of the procedure execution status;
!
!===============================================================================
!
subroutine finalize_forcing(status)
implicit none
integer, intent(out) :: status
!-------------------------------------------------------------------------------
!
status = 0
if (allocated(kmodes)) deallocate(kmodes)
if (allocated(fmodes)) deallocate(fmodes)
if (allocated(fcoefs)) deallocate(fcoefs)
if (allocated(e1vecs)) deallocate(e1vecs)
if (allocated(e2vecs)) deallocate(e2vecs)
if (allocated(vcoefs)) deallocate(vcoefs)
!-------------------------------------------------------------------------------
!
end subroutine finalize_forcing
!
!===============================================================================
!
! subroutine PRINT_FORCING:
! ------------------------
!
! Subroutine prints module parameters.
!
! Arguments:
!
! verbose - a logical flag turning the information printing;
!
!===============================================================================
!
subroutine print_forcing(verbose)
use coordinates, only : periodic
use helpers , only : print_section, print_parameter
implicit none
logical, intent(in) :: verbose
character(len=64) :: injection = "none"
character(len=64) :: profile_type = "gaussian"
!-------------------------------------------------------------------------------
!
if (verbose) then
select case(injection_method)
case(injection_eddy)
injection = "random eddies"
case(injection_oh)
injection = "OrnsteinUhlenbeck"
case(injection_alvelius)
injection = "Alvelius"
case default
injection = "none"
end select
call print_section(verbose, "Forcing")
call print_parameter(verbose, "method", injection)
select case(injection_method)
case(injection_eddy)
call print_parameter(verbose, "injection power", power)
call print_parameter(verbose, "injection rate" , rate)
call print_parameter(verbose, "eddy amplitude" , amp)
call print_parameter(verbose, "eddy width" , del)
end select
if (forcing_fourier) then
if (injection_method == injection_oh) then
call print_parameter(verbose, "power (estimated)", fpower)
end if
if (injection_method == injection_alvelius) then
call print_parameter(verbose, "power" , fpower)
end if
call print_parameter(verbose, "length scale" , lscale)
call print_parameter(verbose, "time scale" , tscale)
call print_parameter(verbose, "velocity scale" , vscale)
call print_parameter(verbose, "acceleration scale", ascale)
select case(injection_profile)
case(profile_powerlaw)
profile_type = "power-law"
case(profile_normal)
profile_type = "gaussian"
case(profile_parabolic)
profile_type = "parabolic"
case default
profile_type = "unknown"
end select
call print_parameter(verbose, "spectrum profile", profile_type)
if (energy_profile) then
call print_parameter(verbose, "profile of", "energy")
else
call print_parameter(verbose, "profile of", "amplitude")
end if
call print_parameter(verbose, "number of driving modes", nmodes)
call print_parameter(verbose, "driving wavenumber" , kf)
call print_parameter(verbose, "lower wavenumber limit" , kl)
call print_parameter(verbose, "upper wavenumber limit" , ku)
if (injection_profile == profile_powerlaw) then
call print_parameter(verbose, "power-law index", pidx)
end if
if (injection_profile == profile_normal) then
call print_parameter(verbose, "spectrum profile width", kc)
end if
call print_parameter(verbose, "amplitude threshold", fmin)
end if
if (.not. periodic(1)) &
call print_parameter(verbose, "profile X transition between", xlo, xup)
if (.not. periodic(2)) &
call print_parameter(verbose, "profile Y transition between", ylo, yup)
#if NDIMS == 3
if (.not. periodic(3)) &
call print_parameter(verbose, "profile Z transition between", zlo, zup)
#endif /* NDIMS == 3 */
end if
!-------------------------------------------------------------------------------
!
end subroutine print_forcing
!
!===============================================================================
!
! subroutine UPDATE_FORCING_EDDIES:
! --------------------------------
!
! Subroutine adds the energy injection terms.
!
! Arguments:
!
! dt - time increment;
!
!===============================================================================
!
subroutine update_forcing_eddies(dt)
use coordinates, only : xmin, ymin, zmin, xlen, ylen, zlen
use random , only : randuni, randsym
implicit none
real(kind=8), intent(in) :: dt
integer :: ni, n
#if NDIMS == 3
real(kind=8) :: tmp
#endif /* NDIMS == 3 */
real(kind=8), dimension(3) :: xp, ap
!-------------------------------------------------------------------------------
!
! calculate the number of eddies to be injected during this interval
!
dinj = dinj + rate * dt
ni = int(floor(dinj))
dinj = dinj - 1.0d+00 * ni
! inject the required number of eddies
!
if (ni > 0) then
do n = 1, ni
! get random position
!
xp(1) = xmin + xlen * randuni()
xp(2) = ymin + ylen * randuni()
xp(3) = zmin + zlen * randuni()
! get random orientation
!
#if NDIMS == 3
tmp = 0.0d+00
do while(tmp <= tiny(0.0d+00))
ap(1) = randsym()
ap(2) = randsym()
ap(3) = randsym()
tmp = sqrt(ap(1)**2 + ap(2)**2 + ap(3)**2)
end do
ap(:) = amp * (ap(:) / tmp) / del
#else /* NDIMS == 3 */
ap(:) = sign(1.0d+00, randsym()) * (/ 0.0d+00, 0.0d+00, amp / del /)
#endif /* NDIMS == 3 */
ap(:) = ap(:) * dt
! iterate over data blocks and add forcing components
!
call inject_eddy(xp(:), ap(:))
end do
end if
!-------------------------------------------------------------------------------
!
end subroutine update_forcing_eddies
!
!===============================================================================
!
! subroutine UPDATE_FORCING_OH:
! ----------------------------
!
! Subroutine adds the energy injection terms for OrnsteinUhlenbeck driving.
!
! Arguments:
!
! dt - time increment;
!
!===============================================================================
!
subroutine update_forcing_oh(dt)
#if NDIMS == 3
use constants, only : pi2
#endif /* NDIMS == 3 */
use random , only : randuni, randnorz
implicit none
real(kind=8), intent(in) :: dt
integer :: l
real(kind=8) :: acoeff, dcoeff
#if NDIMS == 3
real(kind=8) :: phi
#endif /* NDIMS == 3 */
real(kind=8) :: dinj
complex(kind=8), dimension(NDIMS) :: finj
!-------------------------------------------------------------------------------
!
! calculate drift and diffusion coefficients
!
acoeff = exp(- dt / tscale)
dcoeff = sqrt(1.0d+00 - acoeff**2)
! iterate over driving modes
!
do l = 1, nmodes
! generate gaussian random vector along the driving modes
!
#if NDIMS == 3
phi = pi2 * randuni()
finj(:) = fmodes(l) * (e1vecs(l,:) * cos(phi) &
+ e2vecs(l,:) * sin(phi)) * randnorz()
#else /* NDIMS == 3 */
finj(:) = fmodes(l) * e1vecs(l,:) * randnorz()
#endif /* NDIMS == 3 */
! advance the driving force modes
!
fcoefs(l,:) = acoeff * fcoefs(l,:) + dcoeff * finj(:)
end do
! calculate the rms of acceleration
!
arms = sqrt(5.0d-01 * sum(real(fcoefs(:,:))**2 + aimag(fcoefs(:,:))**2))
! store previously injected energy
!
dinj = einj
! inject driving modes
!
call inject_fmodes(dt)
! calculate the energy injected during this step
!
dinj = einj - dinj
! calculate the injection rate
!
rinj = dinj / dt
!-------------------------------------------------------------------------------
!
end subroutine update_forcing_oh
!
!===============================================================================
!
! subroutine UPDATE_FORCING_ALVELIUS:
! ----------------------------------
!
! Subroutine adds the energy injection terms for Alvelius driving.
!
! Arguments:
!
! dt - time increment;
!
!===============================================================================
!
subroutine update_forcing_alvelius(dt)
use constants, only : pi2
use random , only : randuni
implicit none
real(kind=8), intent(in) :: dt
integer :: l
real(kind=8) :: th1, dinj, sqdt
#if NDIMS == 3
real(kind=8) :: th2, phi, psi, ga, gb
#endif /* NDIMS == 3 */
complex(kind=8) :: aran
#if NDIMS == 3
complex(kind=8) :: bran
complex(kind=8) :: xi1, xi2
#endif /* NDIMS == 3 */
!-------------------------------------------------------------------------------
!
! determine velocify coefficients in Fourier space
!
call get_vcoefs()
! square root of dt
!
sqdt = sqrt(dt)
! iterate over driving modes
!
do l = 1, nmodes
#if NDIMS == 3
! get xi1 and xi2
!
xi1 = dot_product(vcoefs(l,:), e1vecs(l,:))
xi2 = dot_product(vcoefs(l,:), e2vecs(l,:))
! get random angles phi and psi and determine th1 and th2 to reduce
! velocity-force correlation
!
phi = pi2 * randuni()
ga = sin(phi)
gb = cos(phi)
psi = pi2 * randuni()
th1 = - ga * aimag(xi1) + gb * (sin(psi) * real(xi2) &
- cos(psi) * aimag(xi2))
if (abs(th1) > epsilon(0.0d+00)) then
th1 = atan((ga * real(xi1) + gb * (sin(psi) * aimag(xi2) &
+ cos(psi) * real(xi2))) / th1)
end if
th2 = psi + th1
! calculate amplitudes
!
aran = cmplx(cos(th1), sin(th1), kind=8) * ga
bran = cmplx(cos(th2), sin(th2), kind=8) * gb
! calculate driving mode coefficient
!
fcoefs(l,:) = fmodes(l) * (aran * e1vecs(l,:) &
+ bran * e2vecs(l,:)) / sqdt
#else /* NDIMS == 3 */
! get random phase
!
th1 = pi2 * randuni()
aran = cmplx(cos(th1), sin(th1), kind=8)
! calculate driving mode coefficient
!
fcoefs(l,:) = fmodes(l) * aran * e1vecs(l,:) / sqdt
#endif /* NDIMS == 3 */
end do
! calculate the rms of acceleration
!
arms = sqrt(5.0d-01 * sum(real(fcoefs(:,:))**2 + aimag(fcoefs(:,:))**2))
! store previously injected energy
!
dinj = einj
! inject driving modes
!
call inject_fmodes(dt)
! calculate the energy injected during this step
!
dinj = einj - dinj
! calculate the injection rate
!
rinj = dinj / dt
!-------------------------------------------------------------------------------
!
end subroutine update_forcing_alvelius
!
!===============================================================================
!!
!!*** PRIVATE SUBROUTINES ****************************************************
!!
!===============================================================================
!
!===============================================================================
!
! subroutine INJECT_EDDY:
! ----------------------
!
! Subroutine injects one eddy to the domain at given position.
!
! Arguments:
!
! xp, ap - eddy position and amplitude vectors;
!
!===============================================================================
!
subroutine inject_eddy(xp, ap)
use blocks, only : block_data, list_data
implicit none
real(kind=8), dimension(3), intent(in) :: xp, ap
type(block_data), pointer :: pdata
!-------------------------------------------------------------------------------
!
! assign pdata with the first block on the data block list
!
pdata => list_data
! iterate over all data blocks
!
do while (associated(pdata))
! inject eddy into the current block
!
call inject_eddy_block(pdata, xp(:), ap(:))
! assign pdata to the next block
!
pdata => pdata%next
end do ! over data blocks
!-------------------------------------------------------------------------------
!
end subroutine inject_eddy
!
!===============================================================================
!
! subroutine INJECT_EDDY_BLOCK:
! ----------------------------
!
! Subroutine injects one eddy to the local block.
!
! Arguments:
!
! xp, ap - eddy position and amplitude vectors;
!
!===============================================================================
!
subroutine inject_eddy_block(pdata, xp, ap)
use blocks , only : block_data
use coordinates, only : nm => bcells
use coordinates, only : ax, ay, xlen, ylen
#if NDIMS == 3
use coordinates, only : az, zlen
#endif /* NDIMS == 3 */
use coordinates, only : periodic
use equations , only : idn, imx, imy, imz, ien
implicit none
type(block_data), pointer , intent(inout) :: pdata
real(kind=8), dimension(3), intent(in) :: xp, ap
integer :: i, j, k
real(kind=8) :: x2, y2, r2
#if NDIMS == 3
real(kind=8) :: z2
#endif /* NDIMS == 3 */
real(kind=8) :: fx, fy, fz, fp, e1, e2
real(kind=8), dimension(nm) :: x, y, z
!-------------------------------------------------------------------------------
!
! prepare block coordinates
!
if (periodic(1)) then
fx = 0.5d+00 * xlen
x(:) = pdata%meta%xmin + ax(pdata%meta%level,:) - xp(1)
x(:) = abs(x(:) / fx)
x(:) = (abs(1.0d+00 - abs(1.0d+00 - abs(x(:))))) * fx / del
else
x(:) = (pdata%meta%xmin + ax(pdata%meta%level,:) - xp(1)) / del
end if
if (periodic(2)) then
fy = 0.5d+00 * ylen
y(:) = pdata%meta%ymin + ay(pdata%meta%level,:) - xp(2)
y(:) = abs(y(:) / fy)
y(:) = (abs(1.0d+00 - abs(1.0d+00 - abs(y(:))))) * fy / del
else
y(:) = (pdata%meta%ymin + ay(pdata%meta%level,:) - xp(2)) / del
end if
#if NDIMS == 3
if (periodic(3)) then
fz = 0.5d+00 * zlen
z(:) = pdata%meta%zmin + az(pdata%meta%level,:) - xp(3)
z(:) = abs(z(:) / fz)
z(:) = (abs(1.0d+00 - abs(1.0d+00 - abs(z(:))))) * fz / del
else
z(:) = (pdata%meta%zmin + az(pdata%meta%level,:) - xp(3)) / del
end if
#else /* NDIMS == 3 */
z(:) = 0.0d+00
#endif /* NDIMS == 3 */
#if NDIMS == 2
k = 1
#endif /* NDIMS == 2 */
! iterate over the block coordinates
!
if (ien > 0) then
#if NDIMS == 3
do k = 1, nm
z2 = z(k)**2
#endif /* NDIMS == 3 */
do j = 1, nm
y2 = y(j)**2
do i = 1, nm
x2 = x(i)**2
#if NDIMS == 3
r2 = x2 + y2 + z2
#else /* NDIMS == 3 */
r2 = x2 + y2
#endif /* NDIMS == 3 */
fp = pdata%u(idn,i,j,k) * exp(- 0.5d+00 * r2)
#if NDIMS == 3
fx = (- ap(3) * y(j) + ap(2) * z(k)) * fp
fy = (- ap(1) * z(k) + ap(3) * x(i)) * fp
fz = (- ap(2) * x(i) + ap(1) * y(j)) * fp
#else /* NDIMS == 3 */
fx = (- ap(3) * y(j)) * fp
fy = (+ ap(3) * x(i)) * fp
fz = 0.0d+00
#endif /* NDIMS == 3 */
e1 = 0.5d+00 * sum(pdata%u(imx:imx,i,j,k)**2) / pdata%u(idn,i,j,k)
pdata%u(imx,i,j,k) = pdata%u(imx,i,j,k) + fx
pdata%u(imy,i,j,k) = pdata%u(imy,i,j,k) + fy
pdata%u(imz,i,j,k) = pdata%u(imz,i,j,k) + fz
e2 = 0.5d+00 * sum(pdata%u(imx:imz,i,j,k)**2) / pdata%u(idn,i,j,k)
pdata%u(ien,i,j,k) = pdata%u(ien,i,j,k) + (e2 - e1)
end do
end do
#if NDIMS == 3
end do
#endif /* NDIMS == 3 */
else
#if NDIMS == 3
do k = 1, nm
z2 = z(k)**2
#endif /* NDIMS == 3 */
do j = 1, nm
y2 = y(j)**2
do i = 1, nm
x2 = x(i)**2
#if NDIMS == 3
r2 = x2 + y2 + z2
#else /* NDIMS == 3 */
r2 = x2 + y2
#endif /* NDIMS == 3 */
fp = pdata%u(idn,i,j,k) * exp(- 0.5d+00 * r2)
#if NDIMS == 3
fx = (- ap(3) * y(j) + ap(2) * z(k)) * fp
fy = (- ap(1) * z(k) + ap(3) * x(i)) * fp
fz = (- ap(2) * x(i) + ap(1) * y(j)) * fp
#else /* NDIMS == 3 */
fx = (- ap(3) * y(j)) * fp
fy = (+ ap(3) * x(i)) * fp
fz = 0.0d+00
#endif /* NDIMS == 3 */
pdata%u(imx,i,j,k) = pdata%u(imx,i,j,k) + fx
pdata%u(imy,i,j,k) = pdata%u(imy,i,j,k) + fy
pdata%u(imz,i,j,k) = pdata%u(imz,i,j,k) + fz
end do
end do
#if NDIMS == 3
end do
#endif /* NDIMS == 3 */
end if
!-------------------------------------------------------------------------------
!
end subroutine inject_eddy_block
!
!===============================================================================
!
! subroutine INJECT_FMODES:
! ------------------------
!
! Subroutine injects Fourier driving modes.
!
! Arguments:
!
! dt - the time increment;
!
!===============================================================================
!
subroutine inject_fmodes(dt)
use blocks, only : block_data, list_data
implicit none
real(kind=8), intent(in) :: dt
type(block_data), pointer :: pdata
!-------------------------------------------------------------------------------
!
! assign pdata with the first block on the data block list
!
pdata => list_data
! iterate over all data blocks
!
do while (associated(pdata))
! inject eddy into the current block
!
call inject_fmodes_block(pdata, dt)
! assign pdata to the next block
!
pdata => pdata%next
end do ! over data blocks
!-------------------------------------------------------------------------------
!
end subroutine inject_fmodes
!
!===============================================================================
!
! subroutine INJECT_FMODES_BLOCK:
! ------------------------------
!
! Subroutine injects Fourier driving modes to the local block.
!
! Arguments:
!
! pdata - a pointer to the data block;
! dt - the time increment;
!
!===============================================================================
!
subroutine inject_fmodes_block(pdata, dt)
use blocks , only : block_data
use constants , only : pi2
use coordinates, only : nn => bcells, nb, ne
use coordinates, only : ax, ay, advol
#if NDIMS == 3
use coordinates, only : az
#endif /* NDIMS == 3 */
use coordinates, only : periodic
use equations , only : idn, imx, imy, imz, ien
use helpers , only : print_message
use workspace , only : resize_workspace, work, work_in_use
implicit none
type(block_data), pointer, intent(inout) :: pdata
real(kind=8) , intent(in) :: dt
logical, save :: first = .true.
integer :: i, j, k, l, n, status
real(kind=8) :: cs, sn
#if NDIMS == 3
real(kind=8) :: tt
#endif /* NDIMS == 3 */
real(kind=8) :: dvol
real(kind=8), dimension(nn):: x, y, z
real(kind=8), dimension(nn):: kx, ky, snkx, snky, cskx, csky
#if NDIMS == 3
real(kind=8), dimension(nn):: kz, snkz, cskz
#endif /* NDIMS == 3 */
real(kind=8), dimension(:,:,:,:), pointer, save :: acc
real(kind=8), dimension(:,:,:) , pointer, save :: den
integer, save :: nt
!$ integer :: omp_get_thread_num
!$omp threadprivate(first, nt, acc, den)
character(len=*), parameter :: loc = 'FORCING:inject_fmodes_block()'
!-------------------------------------------------------------------------------
!
nt = 0
!$ nt = omp_get_thread_num()
if (first) then
i = 3 * nn**NDIMS
j = 4 * nn**NDIMS
call resize_workspace(j, status)
if (status /= 0) then
call print_message(loc, "Could not resize the workspace!")
go to 100
end if
#if NDIMS == 3
acc(1:3,1:nn,1:nn,1:nn) => work( 1:i,nt)
den(1:nn,1:nn,1:nn) => work(i+1:j,nt)
#else /* NDIMS == 3 */
acc(1:2,1:nn,1:nn,1: 1) => work( 1:i,nt)
den(1:nn,1:nn,1: 1) => work(i+1:j,nt)
#endif /* NDIMS == 3 */
first = .false.
end if
#if NDIMS == 2
k = 1
#endif /* NDIMS == 2 */
x(:) = - pi2 * (pdata%meta%xmin + ax(pdata%meta%level,:))
y(:) = - pi2 * (pdata%meta%ymin + ay(pdata%meta%level,:))
#if NDIMS == 3
z(:) = - pi2 * (pdata%meta%zmin + az(pdata%meta%level,:))
#else /* NDIMS == 3 */
z(:) = 0.0d+00
#endif /* NDIMS == 3 */
dvol = advol(pdata%meta%level)
if (work_in_use(nt)) &
call print_message(loc, "Workspace is being used right now! " // &
"Corruptions can occur!")
work_in_use(nt) = .true.
acc(1:NDIMS,:,:,:) = 0.0d+00
! iterate over driving modes and compute the acceleration in the real space
!
do l = 1, nmodes
if (fmodes(l) > fmin) then
kx(:) = kmodes(l,1) * x(:)
ky(:) = kmodes(l,2) * y(:)
#if NDIMS == 3
kz(:) = kmodes(l,3) * z(:)
#endif /* NDIMS == 3 */
cskx(:) = cos(kx(:))
snkx(:) = sin(kx(:))
csky(:) = cos(ky(:))
snky(:) = sin(ky(:))
#if NDIMS == 3
cskz(:) = cos(kz(:))
snkz(:) = sin(kz(:))
#endif /* NDIMS == 3 */
#if NDIMS == 3
do k = 1, nn
#endif /* NDIMS == 3 */
do j = 1, nn
do i = 1, nn
cs = cskx(i) * csky(j) - snkx(i) * snky(j)
sn = cskx(i) * snky(j) + snkx(i) * csky(j)
#if NDIMS == 3
tt = cs
cs = tt * cskz(k) - sn * snkz(k)
sn = tt * snkz(k) + sn * cskz(k)
#endif /* NDIMS == 3 */
acc(1:NDIMS,i,j,k) = acc(1:NDIMS,i,j,k) &
+ (real(fcoefs(l,:)) * cs &
+ aimag(fcoefs(l,:)) * sn)
end do
end do
#if NDIMS == 3
end do
#endif /* NDIMS == 3 */
end if
end do ! l = 1, nmodes
! apply the injection profiles
!
if (.not. periodic(1)) then
do i = 1, nn
if (abs(x(i)) > xup) then
acc(1:NDIMS,i,:,:) = 0.0d+00
else if (abs(x(i)) >= xlo) then
acc(1:NDIMS,i,:,:) = acc(1:NDIMS,i,:,:) &
* cos(xfc * (abs(x(i)) - xlo))**2
end if
end do
end if
if (.not. periodic(2)) then
do j = 1, nn
if (abs(y(j)) > yup) then
acc(1:NDIMS,:,j,:) = 0.0d+00
else if (abs(y(j)) >= ylo) then
acc(1:NDIMS,:,j,:) = acc(1:NDIMS,:,j,:) &
* cos(yfc * (abs(y(j)) - ylo))**2
end if
end do
end if
#if NDIMS == 3
if (.not. periodic(3)) then
do k = 1, nn
if (abs(z(k)) > zup) then
acc(1:NDIMS,:,:,k) = 0.0d+00
else if (abs(z(k)) >= zlo) then
acc(1:NDIMS,:,:,k) = acc(1:NDIMS,:,:,k) &
* cos(zfc * (abs(z(k)) - zlo))**2
end if
end do
end if
#endif /* NDIMS == 3 */
! multiply the acceleration by density and time step
!
do n = 1, NDIMS
acc(n,:,:,:) = pdata%u(idn,:,:,:) * acc(n,:,:,:) * dt
end do
! calculate the kinetic energy before the injection
!
den(:,:,:) = sum(pdata%u(imx:imz,:,:,:)**2, 1)
! add the momentum increment
!
pdata%u(imx,:,:,:) = pdata%u(imx,:,:,:) + acc(1,:,:,:)
pdata%u(imy,:,:,:) = pdata%u(imy,:,:,:) + acc(2,:,:,:)
#if NDIMS == 3
pdata%u(imz,:,:,:) = pdata%u(imz,:,:,:) + acc(3,:,:,:)
#endif /* NDIMS == 3 */
! calculate the injected energy
!
den(:,:,:) = 5.0d-01 * (sum(pdata%u(imx:imz,:,:,:)**2, 1) &
- den(:,:,:)) / pdata%u(idn,:,:,:)
! calculate the injected energy
!
#if NDIMS == 3
einj = einj + sum(den(nb:ne,nb:ne,nb:ne)) * dvol
#else /* NDIMS == 3 */
einj = einj + sum(den(nb:ne,nb:ne, 1 )) * dvol
#endif /* NDIMS == 3 */
! add the energy increment
!
if (ien > 0) then
pdata%u(ien,:,:,:) = pdata%u(ien,:,:,:) + den(:,:,:)
end if
work_in_use(nt) = .false.
100 continue
!-------------------------------------------------------------------------------
!
end subroutine inject_fmodes_block
!
!===============================================================================
!
! subroutine GET_VCOEFS:
! ---------------------
!
! Subroutine determines velocity Fourier coefficients.
!
!
!===============================================================================
!
subroutine get_vcoefs()
use blocks , only : block_data, list_data
#ifdef MPI
use mpitools, only : reduce_sum
#endif /* MPI */
implicit none
type(block_data), pointer :: pdata
!-------------------------------------------------------------------------------
!
! reset vcoefs
!
vcoefs(:,:) = cmplx(0.0d+00, 0.0d+00, kind=8)
! assign pdata with the first block on the data block list
!
pdata => list_data
! iterate over all data blocks
!
do while (associated(pdata))
! get contribution of velocity coefficients from the current block
!
call get_vcoefs_block(pdata)
! assign pdata to the next block
!
pdata => pdata%next
end do ! over data blocks
#ifdef MPI
! reduce velocity coefficients over all processes
!
call reduce_sum(vcoefs)
#endif /* MPI */
!-------------------------------------------------------------------------------
!
end subroutine get_vcoefs
!
!===============================================================================
!
! subroutine GET_VCOEFS_BLOCK:
! ---------------------------
!
! Subroutine determines Fourier coefficients of velocity.
!
! Arguments:
!
! pdata - a pointer to the data block;
!
!===============================================================================
!
subroutine get_vcoefs_block(pdata)
use blocks , only : block_data
use constants , only : pi2
use coordinates, only : nm => bcells, nb, ne
use coordinates, only : ax, ay, advol
#if NDIMS == 3
use coordinates, only : az
#endif /* NDIMS == 3 */
use equations , only : ivx, ivy
#if NDIMS == 3
use equations , only : ivz
#endif /* NDIMS == 3 */
implicit none
type(block_data), pointer, intent(inout) :: pdata
integer :: i, j, k, l
real(kind=8) :: cs, sn, dvol
#if NDIMS == 3
real(kind=8) :: tt
#endif /* NDIMS == 3 */
complex(kind=8) :: cf
real(kind=8), dimension(nm):: x, y, z
real(kind=8), dimension(nm):: kx, ky, snkx, snky, cskx, csky
#if NDIMS == 3
real(kind=8), dimension(nm):: kz, snkz, cskz
#endif /* NDIMS == 3 */
!-------------------------------------------------------------------------------
!
#if NDIMS == 2
k = 1
#endif /* NDIMS == 2 */
! prepare block coordinates
!
x(:) = pi2 * (pdata%meta%xmin + ax(pdata%meta%level,:))
y(:) = pi2 * (pdata%meta%ymin + ay(pdata%meta%level,:))
#if NDIMS == 3
z(:) = pi2 * (pdata%meta%zmin + az(pdata%meta%level,:))
#else /* NDIMS == 3 */
z(:) = 0.0d+00
#endif /* NDIMS == 3 */
dvol = advol(pdata%meta%level)
! iterate over driving modes and compute the velocity Fourier coefficients
!
do l = 1, nmodes
kx(:) = kmodes(l,1) * x(:)
ky(:) = kmodes(l,2) * y(:)
#if NDIMS == 3
kz(:) = kmodes(l,3) * z(:)
#endif /* NDIMS == 3 */
cskx(:) = cos(kx(:))
snkx(:) = sin(kx(:))
csky(:) = cos(ky(:))
snky(:) = sin(ky(:))
#if NDIMS == 3
cskz(:) = cos(kz(:))
snkz(:) = sin(kz(:))
#endif /* NDIMS == 3 */
#if NDIMS == 3
do k = nb, ne
#endif /* NDIMS == 3 */
do j = nb, ne
do i = nb, ne
cs = cskx(i) * csky(j) - snkx(i) * snky(j)
sn = cskx(i) * snky(j) + snkx(i) * csky(j)
#if NDIMS == 3
tt = cs
cs = tt * cskz(k) - sn * snkz(k)
sn = tt * snkz(k) + sn * cskz(k)
#endif /* NDIMS == 3 */
cf = cmplx(cs, sn, kind=8) * dvol
vcoefs(l,1) = vcoefs(l,1) + pdata%q(ivx,i,j,k) * cf
vcoefs(l,2) = vcoefs(l,2) + pdata%q(ivy,i,j,k) * cf
#if NDIMS == 3
vcoefs(l,3) = vcoefs(l,3) + pdata%q(ivz,i,j,k) * cf
#endif /* NDIMS == 3 */
end do
end do
#if NDIMS == 3
end do
#endif /* NDIMS == 3 */
end do ! l = 1, nmodes
!-------------------------------------------------------------------------------
!
end subroutine get_vcoefs_block
!===============================================================================
!
end module forcing