Neko  0.9.0
A portable framework for high-order spectral element flow simulations
cpr.f90
Go to the documentation of this file.
1 ! Copyright (c) 2022, The Neko Authors
2 ! All rights reserved.
3 !
4 ! Redistribution and use in source and binary forms, with or without
5 ! modification, are permitted provided that the following conditions
6 ! are met:
7 !
8 ! * Redistributions of source code must retain the above copyright
9 ! notice, this list of conditions and the following disclaimer.
10 !
11 ! * Redistributions in binary form must reproduce the above
12 ! copyright notice, this list of conditions and the following
13 ! disclaimer in the documentation and/or other materials provided
14 ! with the distribution.
15 !
16 ! * Neither the name of the authors nor the names of its
17 ! contributors may be used to endorse or promote products derived
18 ! from this software without specific prior written permission.
19 !
20 ! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 ! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 ! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 ! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 ! COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 ! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 ! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 ! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 ! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 ! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 ! POSSIBILITY OF SUCH DAMAGE.
32 !
34 module cpr
35  use gather_scatter
36  use neko_config
37  use num_types
38  use field, only : field_t
39  use space, only : space_t
40  use math
41  use mesh, only : mesh_t
42  use coefs, only : coef_t
43  use tensor
44  use mxm_wrapper
45  use logger
46  use dofmap, only : dofmap_t
47  implicit none
48  private
49 
51  type, public :: cpr_t
52  real(kind=rp), allocatable :: v(:,:)
53 
54  real(kind=rp), allocatable :: vt(:,:)
55  real(kind=rp), allocatable :: vinv(:,:)
56  real(kind=rp), allocatable :: vinvt(:,:)
58  real(kind=rp), allocatable :: w(:,:)
59 
60  real(kind=rp), allocatable :: fldhat(:,:,:,:)
61 
62  type(field_t), pointer :: fld => null()
63  type(space_t), pointer :: xh => null()
64  type(mesh_t), pointer :: msh => null()
65  type(dofmap_t), pointer :: dof => null()
66 
67  end type cpr_t
68 
69  interface cpr_init
70  module procedure cpr_init_all
71  end interface cpr_init
72 
73  public :: cpr_init, cpr_free
74 
75 contains
76 
78  subroutine cpr_init_all(cpr, u)
79  type(cpr_t), intent(inout) :: cpr
80  type(field_t), intent(in), target :: u
81 
82  call cpr_free(cpr)
83 
84  cpr%fld => u
85  cpr%msh => u%msh
86  cpr%Xh => u%Xh
87  cpr%dof => u%dof
88 
89 
90  ! Allocate arrays
91  allocate(cpr%v(cpr%Xh%lx, cpr%Xh%lx))
92  allocate(cpr%vt(cpr%Xh%lx, cpr%Xh%lx))
93  allocate(cpr%vinv(cpr%Xh%lx, cpr%Xh%lx))
94  allocate(cpr%vinvt(cpr%Xh%lx, cpr%Xh%lx))
95  allocate(cpr%w(cpr%Xh%lx, cpr%Xh%lx))
96  allocate(cpr%fldhat(cpr%Xh%lx, cpr%Xh%ly, cpr%Xh%lz, cpr%msh%nelv))
97 
98  ! Initialize all the matrices
100 
101  ! Generate the uhat field (legendre coeff)
102  call cpr_goto_space(cpr,'spec')
103 
104  end subroutine cpr_init_all
105 
106 
108  subroutine cpr_free(cpr)
109  type(cpr_t), intent(inout) :: cpr
110 
111  if(allocated(cpr%v)) then
112  deallocate(cpr%v)
113  end if
114 
115  if(allocated(cpr%vt)) then
116  deallocate(cpr%vt)
117  end if
118 
119  if(allocated(cpr%vinv)) then
120  deallocate(cpr%vinv)
121  end if
122 
123  if(allocated(cpr%vinvt)) then
124  deallocate(cpr%vinvt)
125  end if
126 
127  if(allocated(cpr%w)) then
128  deallocate(cpr%w)
129  end if
130 
131  if(allocated(cpr%fldhat)) then
132  deallocate(cpr%fldhat)
133  end if
134 
135  nullify(cpr%fld)
136  nullify(cpr%msh)
137  nullify(cpr%Xh)
138  nullify(cpr%dof)
139 
140 
141  end subroutine cpr_free
142 
143 
145  subroutine cpr_generate_specmat(cpr)
146  type(cpr_t), intent(inout) :: cpr
147  real(kind=rp) :: l(0:cpr%Xh%lx-1)
148  real(kind=rp) :: delta(cpr%Xh%lx)
149  integer :: i, kj, j, j2, kk
150 
151  associate(xh => cpr%Xh, v=> cpr%v, vt => cpr%vt, &
152  vinv => cpr%vinv, vinvt => cpr%vinvt, w => cpr%w)
153  ! Get the Legendre polynomials for each point
154  ! Then proceed to compose the transform matrix
155  kj = 0
156  do j = 1, xh%lx
157  l(0) = 1.
158  l(1) = xh%zg(j,1)
159  do j2 = 2, xh%lx-1
160  l(j2) = ( (2*j2-1) * xh%zg(j,1) * l(j2-1) &
161  - (j2-1) * l(j2-2) ) / j2
162  end do
163  do kk = 1, xh%lx
164  kj = kj+1
165  v(kj,1) = l(kk-1)
166  end do
167  end do
168 
169  ! transpose the matrix
170  call trsp1(v, xh%lx)
171 
172  ! Calculate the nominal scaling factors
173  do i = 1, xh%lx
174  delta(i) = 2.0_rp / (2*(i-1)+1)
175  end do
176  ! modify last entry
177  delta(xh%lx) = 2.0_rp / (xh%lx-1)
178 
179  ! calculate the inverse to multiply the matrix
180  do i = 1, xh%lx
181  delta(i) = sqrt(1.0_rp / delta(i))
182  end do
183  ! scale the matrix
184  do i = 1, xh%lx
185  do j = 1, xh%lx
186  v(i,j) = v(i,j) * delta(j) ! orthogonal wrt weights
187  end do
188  end do
189 
190  ! get the trasposed
191  call copy(vt, v, xh%lx * xh%lx)
192  call trsp1(vt, xh%lx)
193 
194  !populate the mass matrix
195  kk = 1
196  do i = 1, xh%lx
197  do j = 1, xh%lx
198  if (i .eq. j) then
199  w(i,j) = xh%wx(kk)
200  kk = kk+1
201  else
202  cpr%w(i,j) = 0
203  end if
204  end do
205  end do
206 
207  !Get the inverse of the transform matrix
208  call mxm(vt, xh%lx, w, xh%lx, vinv, xh%lx)
209 
210  !get the transposed of the inverse
211  call copy(vinvt, vinv, xh%lx * xh%lx)
212  call trsp1(vinvt, xh%lx)
213  end associate
214 
215  end subroutine cpr_generate_specmat
216 
217 
219  !the result of the transform is given in fldhat
220  subroutine cpr_goto_space(cpr, space)
221  type(cpr_t), intent(inout) :: cpr
222  real(kind=rp) :: w2(cpr%Xh%lx,cpr%Xh%lx,cpr%Xh%lx)
223  real(kind=rp) :: w1(cpr%Xh%lx,cpr%Xh%lx,cpr%Xh%lx)
224  real(kind=rp) :: specmat(cpr%Xh%lx,cpr%Xh%lx)
225  real(kind=rp) :: specmatt(cpr%Xh%lx,cpr%Xh%lx)
226  integer :: k, e, nxyz, nelv
227  character(len=4) :: space
228 
229  ! define some constants
230  nxyz = cpr%Xh%lx*cpr%Xh%lx*cpr%Xh%lx
231  nelv = cpr%msh%nelv
232 
233  ! Define the matrix according to which transform to do
234  if (space .eq. 'spec') then
235  call copy(specmat, cpr%vinv, cpr%Xh%lx*cpr%Xh%lx)
236  call copy(specmatt, cpr%vinvt, cpr%Xh%lx*cpr%Xh%lx)
237  endif
238  if (space .eq. 'phys') then
239  call copy(specmat, cpr%v, cpr%Xh%lx*cpr%Xh%lx)
240  call copy(specmatt, cpr%vt, cpr%Xh%lx*cpr%Xh%lx)
241  endif
242 
243  ! Apply the operator (transform to given space)
244  do e=1,nelv
245  if (space .eq. 'spec') then
246  call copy(w2, cpr%fld%x(1,1,1,e), nxyz) ! start from phys field
247  else
248  call copy(w2, cpr%fldhat(1,1,1,e), nxyz) ! start from spec coeff
249  endif
250  ! apply the operator to the x direction, result in w1
251  call mxm(specmat, cpr%Xh%lx, w2, cpr%Xh%lx, w1, cpr%Xh%lx*cpr%Xh%lx)
252  ! apply matrix to y direction, result in w2
253  do k=1,cpr%Xh%lx
254  call mxm(w1(1,1,k),cpr%Xh%lx,specmatt,cpr%Xh%lx,w2(1,1,k),cpr%Xh%lx)
255  enddo
256  ! apply matrix to z direction, result always in fldhat
257  call mxm (w2, cpr%Xh%lx*cpr%Xh%lx, specmatt, cpr%Xh%lx,&
258  cpr%fldhat(1,1,1,e), cpr%Xh%lx)
259  enddo
260 
261  end subroutine cpr_goto_space
262 
264  subroutine cpr_truncate_wn(cpr, coef)
265  type(cpr_t), intent(inout) :: cpr
266  type(coef_t), intent(inout) :: coef
267  real(kind=rp) :: w2(cpr%Xh%lx, cpr%Xh%lx, cpr%Xh%lx)
268  real(kind=rp) :: w1(cpr%Xh%lx, cpr%Xh%lx, cpr%Xh%lx)
269  real(kind=rp) :: vsort(cpr%Xh%lx * cpr%Xh%lx * cpr%Xh%lx)
270  real(kind=rp) :: vtrunc(cpr%Xh%lx, cpr%Xh%lx, cpr%Xh%lx)
271  real(kind=rp) :: vtemp(cpr%Xh%lx * cpr%Xh%lx * cpr%Xh%lx)
272  real(kind=rp) :: errvec(cpr%Xh%lx, cpr%Xh%lx, cpr%Xh%lx)
273  real(kind=rp) :: fx(cpr%Xh%lx, cpr%Xh%lx)
274  real(kind=rp) :: fy(cpr%Xh%lx, cpr%Xh%lx)
275  real(kind=rp) :: fz(cpr%Xh%lx, cpr%Xh%lx)
276  real(kind=rp) :: l2norm, oldl2norm, targeterr
277  integer :: isort(cpr%Xh%lx * cpr%Xh%lx * cpr%Xh%lx)
278  integer :: i, j, k, e, nxyz, nelv
279  integer :: kut, kutx, kuty, kutz, nx
280  character(len=LOG_SIZE) :: log_buf
281 
282  ! define some constants
283  nx = cpr%Xh%lx
284  nxyz = cpr%Xh%lx*cpr%Xh%lx*cpr%Xh%lx
285  nelv = cpr%msh%nelv
286  targeterr = 1e-3_rp
287 
288  !truncate for every element
289  do e = 1, nelv
290  ! create temp vector where the trunc coeff will be
291  call copy(vtemp, cpr%fldhat(1,1,1,e), nxyz)
292  call copy(vtrunc, cpr%fldhat(1,1,1,e), nxyz)
293  ! sort the coefficients by absolute value
294  call sortcoeff(vsort, cpr%fldhat(1,1,1,e), isort, nxyz)
295  ! initialize values for iterative procedure
296  l2norm = 0.0_rp
297  kut = 0
298 
299  do while (l2norm .le. targeterr .and. kut .le. (cpr%Xh%lx-1)*3)
300  ! save value from prev it since it met requirements to enter
301  call copy(vtrunc, vtemp, nxyz)
302  oldl2norm = l2norm
303 
304  ! advance kut
305  kut = kut+1
306 
307  ! create filters
308  call build_filter_tf(fx, fy, fz, kut, cpr%Xh%lx)
309 
310  ! truncate, remember that transposed of diag mat is the same
311  call copy(w2, vsort, nxyz)
312  ! apply the operator to the x direction, result in w1
313  call mxm(fx, nx, w2, nx, w1, nx*nx)
314  ! apply matrix to y direction, result in w2
315  do k=1,nx
316  call mxm(w1(1,1,k), nx, fy, nx, w2(1,1,k), nx)
317  enddo
318  ! apply matrix to z direction, result in vtemp
319  call mxm (w2, nx*nx, fz, nx, vtemp, nx)
320 
321  ! vtemp is sorted, so bring it back to real order
322  ! by inverting the swap operation in sortcoeff
323  call reord(vtemp, isort, nxyz)
324 
325  ! calculate the error vector
326  call sub3(errvec, cpr%fldhat(1,1,1,e), vtemp, nxyz)
327 
328  ! get the norm of the error
329  l2norm = get_elem_l2norm(errvec, coef, 'spec' ,e)
330 
331  end do
332 
333  ! copy the truncated field back to the main object
334  call copy(cpr%fldhat(1,1,1,e), vtrunc, nxyz)
335 
336 
337  !================debugging info
338 
339  !just to check that all is good, resort vsort
340  if (e .eq. 50) then
341  write(log_buf, '(A)') &
342  'Debugging info for e=50. Do not forget to delete'
343  call neko_log%message(log_buf)
344 
345  call reord(vsort,isort,nxyz)
346 
347  write(log_buf, '(A)') &
348  'Norm'
349  call neko_log%message(log_buf)
350  !chech that the copy is fine in one entry
351  write(log_buf, '(A,E15.7)') &
352  'l2norm:', oldl2norm
353  call neko_log%message(log_buf)
354  write(log_buf, '(A)') &
355  'cut off level'
356  call neko_log%message(log_buf)
357  !chech that the copy is fine in one entry
358  write(log_buf, '(A,I5)') &
359  'kut:', kut
360  call neko_log%message(log_buf)
361  write(log_buf, '(A)') &
362  'spectral coefficients'
363  call neko_log%message(log_buf)
364  do i = 1, 10
365  write(log_buf, '(A,E15.7,A,E15.7)') &
366  'org coeff:', vsort(i), ' truncated coeff', &
367  cpr%fldhat(i,1,1,e)
368  call neko_log%message(log_buf)
369  end do
370  end if
371  end do
372 
373  end subroutine cpr_truncate_wn
374 
377  subroutine sortcoeff(vsort, v, isort, nxyz)
378  integer, intent(in) :: nxyz
379  real(kind=rp), intent(inout) :: vsort(nxyz)
380  real(kind=rp), intent(inout) :: v(nxyz)
381  integer, intent(inout) :: isort(nxyz)
382  integer :: i
383 
384  ! copy absolute values to sort by magnitude
385  do i =1, nxyz
386  vsort(i) = abs(v(i))
387  isort(i) = i
388  end do
389 
390  ! sort the absolute values of the vectors, here the index is the
391  ! important part (isort)
392  call sort(vsort, isort, nxyz)
393 
394  ! Flip the indices so they are in a descending order
395  call flipv(vsort, isort, nxyz)
396 
397  ! now re order the fields (non abs value) based on the indices
398  call copy(vsort, v, nxyz)
399  call swap(vsort, isort, nxyz)
400 
401  end subroutine sortcoeff
402 
404  subroutine build_filter_tf(fx, fy, fz, kut, lx)
405  integer, intent(in) :: lx
406  integer, intent(in) :: kut
407  real(kind=rp), intent(inout) :: fx(lx,lx)
408  real(kind=rp), intent(inout) :: fy(lx,lx)
409  real(kind=rp), intent(inout) :: fz(lx,lx)
410  integer :: i, j, k, k0, kk, kutx, kuty, kutz
411 
412  ! set the values acording to kut
413  if (kut .eq. 0) then
414  kutz = 0
415  kuty = 0
416  kutx = 0
417  end if
418  if (kut .gt. 0 .and. kut .le. (lx-1)) then
419  kutz = kut
420  kuty = 0
421  kutx = 0
422  end if
423  if (kut .gt. (lx-1) .and. kut .le. (lx-1)*2) then
424  kutz = lx-1
425  kuty = kut-(lx-1)
426  kutx = 0
427  end if
428  if (kut .gt. (lx-1)*2 .and. kut .le. (lx-1)*3) then
429  kutz = lx-1
430  kuty = lx-1
431  kutx = kut-(lx-1)*2
432  end if
433 
434  ! create identity matrices
435  do i = 1, lx
436  do j = 1, lx
437  if (i .eq. j) then
438  fx(i,j) = 1
439  fy(i,j) = 1
440  fz(i,j) = 1
441  else
442  fx(i,j) = 0
443  fy(i,j) = 0
444  fz(i,j) = 0
445  end if
446  end do
447  end do
448 
449  ! truncate the matrices acording to input kut
450  k0 = lx-kutx
451  do k = k0+1, lx
452  kk = k+lx*(k-1)
453  fx(kk,1) = 0
454  end do
455  k0 = lx-kuty
456  do k = k0+1, lx
457  kk = k+lx*(k-1)
458  fy(kk,1) = 0
459  end do
460  k0 = lx-kutz
461  do k = k0+1, lx
462  kk = k+lx*(k-1)
463  fz(kk,1) = 0
464  end do
465 
466  end subroutine build_filter_tf
467 
468 
469  function get_elem_l2norm(elemdata, coef, space, e) result(l2e)
470  type(coef_t), intent(in) :: coef
471  real(kind=rp) :: elemdata(coef%Xh%lx, coef%Xh%lx, coef%Xh%lx)
472  real(kind=rp) :: vole, suma, l2e
473  integer i, e, nxyz
474  character(len=4) :: space
475 
476  ! Get the volume of the element
477  nxyz = coef%Xh%lx*coef%Xh%lx*coef%Xh%lx
478  vole=0
479  do i = 1, nxyz
480  vole = vole+coef%B(i,1,1,e)
481  end do
482 
483  ! Get the weighted l2 norm of the element
484  suma = 0
485  if (space .eq. 'spec') then
486  do i = 1, nxyz
487  suma = suma+elemdata(i,1,1)*elemdata(i,1,1)*coef%jac(i,1,1,e)
488  end do
489  end if
490  if (space .eq. 'phys') then
491  do i = 1, nxyz
492  suma = suma+elemdata(i,1,1)*elemdata(i,1,1)*coef%B(i,1,1,e)
493  end do
494  end if
495 
496  l2e = sqrt(suma)/sqrt(vole)
497 
498  end function get_elem_l2norm
499 
500 
501 end module cpr
Coefficients.
Definition: coef.f90:34
Compression.
Definition: cpr.f90:34
subroutine build_filter_tf(fx, fy, fz, kut, lx)
create filter transfer function
Definition: cpr.f90:405
real(kind=rp) function get_elem_l2norm(elemdata, coef, space, e)
Definition: cpr.f90:470
subroutine cpr_truncate_wn(cpr, coef)
Truncate the frequencies.
Definition: cpr.f90:265
subroutine cpr_init_all(cpr, u)
Initialize cpr.
Definition: cpr.f90:79
subroutine sortcoeff(vsort, v, isort, nxyz)
Sort the spectral coefficient in descending order array vsort. The original indices are stored in the...
Definition: cpr.f90:378
subroutine cpr_goto_space(cpr, space)
Tranform to spectral space (using tensor product)
Definition: cpr.f90:221
subroutine cpr_generate_specmat(cpr)
Generate spectral tranform matrices.
Definition: cpr.f90:146
subroutine, public cpr_free(cpr)
Deallocate coefficients.
Definition: cpr.f90:109
Defines a mapping of the degrees of freedom.
Definition: dofmap.f90:35
Defines a field.
Definition: field.f90:34
Gather-scatter.
Logging routines.
Definition: log.f90:34
Definition: math.f90:60
subroutine, public copy(a, b, n)
Copy a vector .
Definition: math.f90:239
Defines a mesh.
Definition: mesh.f90:34
Wrapper for all matrix-matrix product implementations.
Definition: mxm_wrapper.F90:2
subroutine, public mxm(a, n1, b, n2, c, n3)
Compute matrix-matrix product for contiguously packed matrices A,B, and C.
Definition: mxm_wrapper.F90:29
Build configurations.
Definition: neko_config.f90:34
integer, parameter, public rp
Global precision used in computations.
Definition: num_types.f90:12
Defines a function space.
Definition: space.f90:34
Tensor operations.
Definition: tensor.f90:61
subroutine, public trsp1(a, n)
In-place transpose of a square tensor.
Definition: tensor.f90:137
Coefficients defined on a given (mesh, ) tuple. Arrays use indices (i,j,k,e): element e,...
Definition: coef.f90:55
include information needed for compressing fields
Definition: cpr.f90:51
The function space for the SEM solution fields.
Definition: space.f90:62