-
Notifications
You must be signed in to change notification settings - Fork 68
/
derivatives.f90
315 lines (263 loc) · 9.38 KB
/
derivatives.f90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
!!
!! Copyright (C) 2010-2017 Johns Hopkins University
!!
!! This file is part of lesgo.
!!
!! lesgo 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.
!!
!! lesgo 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 lesgo. If not, see <http://www.gnu.org/licenses/>.
!!
!*******************************************************************************
module derivatives
!*******************************************************************************
!
! This module contains all of the major subroutines used for computing
! derivatives.
!
implicit none
save
private
public ddx, ddy, ddxy, filt_da, ddz_uv, ddz_w
contains
!*******************************************************************************
subroutine ddx(f,dfdx,lbz)
!*******************************************************************************
!
! This subroutine computes the partial derivative of f with respect to
! x using spectral decomposition.
!
use types, only : rprec
use param, only : ld, nx, ny, nz
use fft
use emul_complex, only : OPERATOR(.MULI.)
implicit none
integer, intent(in) :: lbz
real(rprec), dimension(:,:,lbz:), intent(in) :: f
real(rprec), dimension(:,:,lbz:), intent(inout) :: dfdx
real(rprec) :: const
integer :: jz
const = 1._rprec / ( nx * ny )
! Loop through horizontal slices
do jz = lbz, nz
! Use dfdx to hold f; since we are doing in place FFTs this is required
dfdx(:,:,jz) = const*f(:,:,jz)
call dfftw_execute_dft_r2c(forw, dfdx(:,:,jz),dfdx(:,:,jz))
! Zero padded region and Nyquist frequency
dfdx(ld-1:ld,:,jz) = 0._rprec
dfdx(:,ny/2+1,jz) = 0._rprec
! Use complex emulation of dfdx to perform complex multiplication
! Optimized version for real(eye*kx)=0
! only passing imaginary part of eye*kx
dfdx(:,:,jz) = dfdx(:,:,jz) .MULI. kx
! Perform inverse transform to get pseudospectral derivative
call dfftw_execute_dft_c2r(back, dfdx(:,:,jz), dfdx(:,:,jz))
enddo
end subroutine ddx
!*******************************************************************************
subroutine ddy(f,dfdy, lbz)
!*******************************************************************************
!
! This subroutine computes the partial derivative of f with respect to
! y using spectral decomposition.
!
use types, only : rprec
use param, only : ld, nx, ny, nz
use fft
use emul_complex, only : OPERATOR(.MULI.)
implicit none
integer, intent(in) :: lbz
real(rprec), dimension(:,:,lbz:), intent(in) :: f
real(rprec), dimension(:,:,lbz:), intent(inout) :: dfdy
real(rprec) :: const
integer :: jz
const = 1._rprec / ( nx * ny )
! Loop through horizontal slices
do jz = lbz, nz
! Use dfdy to hold f; since we are doing in place FFTs this is required
dfdy(:,:,jz) = const * f(:,:,jz)
call dfftw_execute_dft_r2c(forw, dfdy(:,:,jz), dfdy(:,:,jz))
! Zero padded region and Nyquist frequency
dfdy(ld-1:ld,:,jz) = 0._rprec
dfdy(:,ny/2+1,jz) = 0._rprec
! Use complex emulation of dfdy to perform complex multiplication
! Optimized version for real(eye*ky)=0
! only passing imaginary part of eye*ky
dfdy(:,:,jz) = dfdy(:,:,jz) .MULI. ky
! Perform inverse transform to get pseudospectral derivative
call dfftw_execute_dft_c2r(back, dfdy(:,:,jz), dfdy(:,:,jz))
end do
end subroutine ddy
!*******************************************************************************
subroutine ddxy (f, dfdx, dfdy, lbz)
!*******************************************************************************
!
! This subroutine computes the partial derivative of f with respect to
! x and y using spectral decomposition.
!
use types, only : rprec
use param, only : ld, nx, ny, nz
use fft
use emul_complex, only : OPERATOR(.MULI.)
implicit none
integer, intent(in) :: lbz
real(rprec), dimension(:,:,lbz:), intent(in) :: f
real(rprec), dimension(:,:,lbz:), intent(inout) :: dfdx, dfdy
real(rprec) :: const
integer :: jz
const = 1._rprec / ( nx * ny )
! Loop through horizontal slices
do jz = lbz, nz
! Use dfdy to hold f; since we are doing in place FFTs this is required
dfdx(:,:,jz) = const*f(:,:,jz)
call dfftw_execute_dft_r2c(forw, dfdx(:,:,jz), dfdx(:,:,jz))
! Zero padded region and Nyquist frequency
dfdx(ld-1:ld,:,jz) = 0._rprec
dfdx(:,ny/2+1,jz) = 0._rprec
! Derivatives: must to y's first here, because we're using dfdx as storage
! Use complex emulation of dfdy to perform complex multiplication
! Optimized version for real(eye*ky)=0
! only passing imaginary part of eye*ky
dfdy(:,:,jz) = dfdx(:,:,jz) .MULI. ky
dfdx(:,:,jz) = dfdx(:,:,jz) .MULI. kx
! Perform inverse transform to get pseudospectral derivative
call dfftw_execute_dft_c2r(back, dfdx(:,:,jz), dfdx(:,:,jz))
call dfftw_execute_dft_c2r(back, dfdy(:,:,jz), dfdy(:,:,jz))
end do
end subroutine ddxy
!*******************************************************************************
subroutine filt_da(f,dfdx,dfdy, lbz)
!*******************************************************************************
!
! This subroutine kills the oddball components in f and computes the partial
! derivative of f with respect to x and y using spectral decomposition.
!
use types, only : rprec
use param, only : ld, nx, ny, nz
use fft
use emul_complex, only : OPERATOR(.MULI.)
implicit none
integer, intent(in) :: lbz
real(rprec), dimension(:,:,lbz:), intent(inout) :: f
real(rprec), dimension(:,:,lbz:), intent(inout) :: dfdx, dfdy
real(rprec) :: const
integer :: jz
const = 1._rprec/(nx*ny)
! loop through horizontal slices
do jz = lbz, nz
! Calculate FFT in place
f(:,:,jz) = const*f(:,:,jz)
call dfftw_execute_dft_r2c(forw, f(:,:,jz), f(:,:,jz))
! Kill oddballs in zero padded region and Nyquist frequency
f(ld-1:ld,:,jz) = 0._rprec
f(:,ny/2+1,jz) = 0._rprec
! Use complex emulation of dfdy to perform complex multiplication
! Optimized version for real(eye*ky)=0
! only passing imaginary part of eye*ky
dfdx(:,:,jz) = f(:,:,jz) .MULI. kx
dfdy(:,:,jz) = f(:,:,jz) .MULI. ky
! Perform inverse transform to get pseudospectral derivative
! The oddballs for derivatives should already be dead, since they are for f
! inverse transform
call dfftw_execute_dft_c2r(back, f(:,:,jz), f(:,:,jz))
call dfftw_execute_dft_c2r(back, dfdx(:,:,jz), dfdx(:,:,jz))
call dfftw_execute_dft_c2r(back, dfdy(:,:,jz), dfdy(:,:,jz))
end do
end subroutine filt_da
!*******************************************************************************
subroutine ddz_uv(f, dfdz, lbz)
!*******************************************************************************
!
! This subroutine computes the partial derivative of f with respect to z using
! 2nd order finite differencing. f is on the uv grid and dfdz is on the w grid.
! The serial version provides dfdz(:,:,2:nz), and the value at jz=1 is not
! touched. The MPI version provides dfdz(:,:,1:nz), except at the bottom
! process it only supplies 2:nz
!
use types, only : rprec
use param, only : nx, ny, nz, dz, BOGUS
#ifdef PPSAFETYMODE
use param, only : nproc, coord
#endif
implicit none
integer, intent(in) :: lbz
real(rprec), dimension(:,:,lbz:), intent(in) :: f
real(rprec), dimension(:,:,lbz:), intent(inout) :: dfdz
integer :: jx, jy, jz
real(rprec) :: const
const = 1._rprec/dz
#if defined(PPMPI) && defined(PPSAFETYMODE)
dfdz(:,:,0) = BOGUS
#endif
! Calculate derivative.
! The ghost node information is available here
! if coord == 0, dudz(1) will be set in wallstress
do jz = lbz+1, nz
do jy = 1, ny
do jx = 1, nx
dfdz(jx,jy,jz) = const*(f(jx,jy,jz)-f(jx,jy,jz-1))
end do
end do
end do
! Not necessarily accurate at top and bottom boundary
! Set to BOGUS just to be safe
#ifdef PPSAFETYMODE
if (coord == 0) then
dfdz(:,:,1) = BOGUS
end if
if (coord == nproc-1) then
dfdz(:,:,nz) = BOGUS
end if
#endif
end subroutine ddz_uv
!*******************************************************************************
subroutine ddz_w(f, dfdz, lbz)
!*******************************************************************************
!
! This subroutine computes the partial derivative of f with respect to z using
! 2nd order finite differencing. f is on the w grid and dfdz is on the uv grid.
! The serial version provides dfdz(:,:,1:nz-1), and the value at jz=1 is not
! touched. The MPI version provides dfdz(:,:,0:nz-1), except at the top and
! bottom processes, which each has has 0:nz, and 1:nz-1, respectively.
!
use types, only : rprec
use param, only : nx, ny, nz, dz, BOGUS
#ifdef PPSAFETYMODE
#ifdef PPMPI
use param, only : coord
#endif
#endif
implicit none
real(rprec), dimension(:,:,lbz:), intent(in) :: f
real(rprec), dimension(:,:,lbz:), intent(inout) :: dfdz
integer, intent(in) :: lbz
real(rprec)::const
integer :: jx, jy, jz
const = 1._rprec/dz
do jz = lbz, nz-1
do jy = 1, ny
do jx = 1, nx
dfdz(jx,jy,jz) = const*(f(jx,jy,jz+1)-f(jx,jy,jz))
end do
end do
end do
#ifdef PPSAFETYMODE
#ifdef PPMPI
! bottom process cannot calculate dfdz(jz=0)
if (coord == 0) then
dfdz(:,:,lbz) = BOGUS
endif
#endif
! All processes cannot calculate dfdz(jz=nz)
dfdz(:,:,nz) = BOGUS
#endif
end subroutine ddz_w
end module derivatives