Report Typos and Errors    
Semi-Lagrangian Library
Modular library for kinetic and gyrokinetic simulations of plasmas in fusion energy devices.
sll_m_poisson_2d_fem_fft.F90
Go to the documentation of this file.
2 
3 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 #include "sll_working_precision.h"
5 
6  use sll_m_low_level_bsplines, only: &
7  sll_s_uniform_bsplines_eval_basis
8 
9  use sll_m_constants, only : &
11 
12  use sll_m_fft
13 
16 
18 
19  implicit none
20 
21  public :: &
23 
24  private
25 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
26 
27 
28 
29 
31 
32  sll_int32 :: n_dofs(2)
33  sll_int32 :: degree
34  sll_real64 :: delta_x(2)
35 
36  sll_real64, allocatable :: eig_values_dtm1d_1(:)
37  sll_real64, allocatable :: eig_values_dtm1d_2(:)
38  sll_comp64, allocatable :: eig_values_d1(:)
39  sll_comp64, allocatable :: eig_values_d2(:)
40  sll_real64, allocatable :: eig_values_mass_0_1(:)
41  sll_real64, allocatable :: eig_values_mass_0_2(:)
42 
43  type(sll_t_fft) :: fft1
44  type(sll_t_fft) :: fft2
45  type(sll_t_fft) :: ifft1
46  type(sll_t_fft) :: ifft2
47 
48  ! Scratch data
49  sll_comp64, allocatable :: array1d_x(:)
50  sll_comp64, allocatable :: array1d_y(:)
51  sll_comp64, allocatable :: scratch(:,:)
52  sll_comp64, allocatable :: scratchx(:,:)
53  sll_comp64, allocatable :: scratchy(:,:)
54 
55  contains
56  procedure :: compute_phi_from_rho => compute_phi_from_rho_fft
57  procedure :: compute_e_from_rho => compute_e_from_rho_fft
58  procedure :: free => free_fft
59  procedure :: init => init_fft
60  procedure :: compute_rhs_from_function => compute_rhs_from_function_fft
61 
63 
64  abstract interface
65 
67  use sll_m_working_precision ! can't pass a header file because the
68  ! preprocessor prevents double inclusion.
69  ! It is very rare.
70  sll_real64 :: sll_i_function_3d_real64
71  sll_real64, intent(in) :: x(2)
72  end function sll_i_function_2d_real64
73  end interface
74 
75 contains
76 
77  subroutine compute_e_from_rho_fft( self, rho, efield )
78  class(sll_t_poisson_2d_fem_fft), intent( inout ) :: self
79  sll_real64, intent(in) :: rho(:)
80  sll_real64, intent(out) :: efield(:)
81 
82  sll_int32 :: n_dofs(2), ntotal
83  sll_int32 :: i,j
84  sll_real64 :: eig_val
85 
86  n_dofs = self%n_dofs
87  ntotal = product(n_dofs)
88 
89  ! Compute Fourier transform
90  call fft2d( self, rho )
91 
92  ! Apply inverse matrix of eigenvalues on mode
93  do j=1,n_dofs(2)
94  do i=1,n_dofs(1)
95  if ( i == 1 .and. j==1 ) then
96  self%scratch(i,j) = cmplx(0.0_f64, 0.0_f64, f64)
97  else
98  eig_val = self%eig_values_dtm1d_1(i) * self%eig_values_mass_0_2(j) + &
99  self%eig_values_mass_0_1(i) * self%eig_values_dtm1d_2(j)
100  self%scratch(i,j) = self%scratch(i,j) / cmplx(eig_val,0._f64, f64)
101  end if
102 
103  self%scratchx(i,j) = -self%scratch(i,j)* self%eig_values_d1(i)
104  self%scratchy(i,j) = -self%scratch(i,j)* self%eig_values_d2(j)
105 
106  end do
107  end do
108 
109 
110  ! Compute inverse Fourier transfrom
111 
112 
113  call ifft2d( self, self%scratchx, efield(1:ntotal) )
114  call ifft2d( self, self%scratchy, efield(1+ntotal:2*ntotal) )
115 
116 
117  end subroutine compute_e_from_rho_fft
118 
119  subroutine compute_phi_from_rho_fft( self, rho, phi )
120  class(sll_t_poisson_2d_fem_fft), intent( inout ) :: self
121  sll_real64, intent(in) :: rho(:)
122  sll_real64, intent(out) :: phi(:)
123 
124  sll_int32 :: n_dofs(2)
125  sll_int32 :: i,j
126  sll_real64 :: eig_val
127 
128 
129  n_dofs = self%n_dofs
130 
131  ! Compute Fourier transform
132  call fft2d( self, rho )
133 
134  ! Apply inverse matrix of eigenvalues on mode
135  do j=1,n_dofs(2)
136  do i=1,n_dofs(1)
137  if ( i == 1 .and. j==1 ) then
138  self%scratch(i,j) = cmplx(0.0_f64, 0.0_f64, f64)
139  else
140  eig_val = self%eig_values_dtm1d_1(i) * self%eig_values_mass_0_2(j) + &
141  self%eig_values_mass_0_1(i) * self%eig_values_dtm1d_2(j)
142  self%scratch(i,j) = self%scratch(i,j) / cmplx(eig_val, 0.0_f64, f64)
143  end if
144  end do
145  end do
146 
147  call ifft2d( self, self%scratch, phi )
148 
149 
150  end subroutine compute_phi_from_rho_fft
151 
152 
153 
154  subroutine free_fft( self )
155  class(sll_t_poisson_2d_fem_fft), intent( inout ) :: self
156 
157  call sll_s_fft_free( self%fft1 )
158 
159 
160  end subroutine free_fft
161 
162  subroutine init_fft( self, n_dofs, degree, delta_x)
163  class(sll_t_poisson_2d_fem_fft), intent( out ) :: self
164  sll_int32, intent( in ) :: n_dofs(2)
165  sll_int32, intent( in ) :: degree
166  sll_real64, intent( in ) :: delta_x(2)
167 
168 
169  sll_real64 :: mass_line_0(degree+1)
170  sll_real64 :: mass_line_1(degree)
171  sll_real64 :: eig_values_mass_1_1(n_dofs(1))
172  sll_real64 :: eig_values_mass_1_2(n_dofs(2))
173  sll_real64 :: angle
174  sll_int32 :: j
175 
176  self%n_dofs = n_dofs
177 
178  allocate(self%array1d_x(n_dofs(1)))
179  allocate(self%array1d_y(n_dofs(2)))
180  allocate(self%scratch(n_dofs(1), n_dofs(2)))
181  allocate(self%scratchx(n_dofs(1), n_dofs(2)))
182  allocate(self%scratchy(n_dofs(1), n_dofs(2)))
183 
184 
185  call sll_s_fft_init_c2c_1d( self%fft1, n_dofs(1), self%array1d_x, self%array1d_x, sll_p_fft_forward)
186  call sll_s_fft_init_c2c_1d( self%fft2, n_dofs(2), self%array1d_y, self%array1d_y, sll_p_fft_forward)
187  call sll_s_fft_init_c2c_1d( self%ifft1, n_dofs(1), self%array1d_x, self%array1d_x, &
188  sll_p_fft_backward, normalized=.true.)
189  call sll_s_fft_init_c2c_1d( self%ifft2, n_dofs(2), self%array1d_y, self%array1d_y, &
190  sll_p_fft_backward, normalized=.true.)
191 
192  ! Eigenvalues of mass matrices
193  call sll_s_spline_fem_mass_line( degree, mass_line_0 )
194  call sll_s_spline_fem_mass_line( degree-1, mass_line_1 )
195 
196 
197  allocate( self%eig_values_mass_0_1(n_dofs(1)) )
198  call sll_s_spline_fem_compute_mass_eig( n_dofs(1), degree, mass_line_0*delta_x(1), self%eig_values_mass_0_1 )
199  call sll_s_spline_fem_compute_mass_eig( n_dofs(1), degree-1, mass_line_1*delta_x(1), eig_values_mass_1_1 )
200 
201  allocate( self%eig_values_mass_0_2(n_dofs(2)) )
202  call sll_s_spline_fem_compute_mass_eig( n_dofs(2), degree, mass_line_0*delta_x(2), self%eig_values_mass_0_2 )
203  call sll_s_spline_fem_compute_mass_eig( n_dofs(2), degree-1, mass_line_1*delta_x(2), eig_values_mass_1_2 )
204 
205 
206  allocate( self%eig_values_d1(n_dofs(1)) )
207  allocate( self%eig_values_dtm1d_1(n_dofs(1)) )
208 
209  self%eig_values_d1(1) = cmplx(0.0_f64, 0.0_f64, f64)
210  self%eig_values_dtm1d_1(1) = 0.0_f64
211  do j=2,n_dofs(1)
212  angle = sll_p_twopi*real(j-1,f64)/real(n_dofs(1), f64)
213 
214  self%eig_values_d1(j) = cmplx((1.0_f64 - cos(angle))/delta_x(1),sin(angle)/delta_x(1), f64 )
215  self%eig_values_dtm1d_1(j) = 2.0_f64/delta_x(1)**2*(1.0_f64-cos(angle))* eig_values_mass_1_1(j)
216 
217  end do
218 
219  allocate( self%eig_values_d2(n_dofs(2)) )
220  allocate( self%eig_values_dtm1d_2(n_dofs(2)) )
221 
222  self%eig_values_d2(1) = cmplx(0.0_f64, 0.0_f64, f64)
223  self%eig_values_dtm1d_2(1) = 0.0_f64
224  do j=2,n_dofs(2)
225  angle = sll_p_twopi*real(j-1,f64)/real(n_dofs(2), f64)
226 
227  self%eig_values_d2(j) = cmplx((1.0_f64 - cos(angle))/delta_x(2),sin(angle)/delta_x(2), f64 )
228  self%eig_values_dtm1d_2(j) = 2.0_f64/delta_x(2)**2*(1.0_f64-cos(angle))* eig_values_mass_1_2(j)
229 
230  end do
231 
232  self%degree = degree
233  self%delta_x = delta_x
234 
235  end subroutine init_fft
236 
237 
238 
241  subroutine compute_rhs_from_function_fft(self, func, coefs_dofs)
242  class(sll_t_poisson_2d_fem_fft) :: self
243  procedure(sll_i_function_2d_real64) :: func
244  sll_real64, intent(out) :: coefs_dofs(:) ! Finite Element right-hand-side
245 
246  ! local variables
247  sll_int32 :: i1, i2, j1, j2, k1, k2, k, counter
248  sll_real64 :: coef
249  sll_real64, allocatable :: xw_gauss_d1(:,:)
250  sll_real64, allocatable :: bspl_d1(:,:)
251  sll_real64 :: volume
252 
253  volume = product(self%delta_x)
254 
255  ! take enough Gauss points so that projection is exact for splines of degree deg
256  ! rescale on [0,1] for compatibility with B-splines
257  allocate(xw_gauss_d1(2,self%degree+1))
258  allocate(bspl_d1(self%degree+1, self%degree+1))
259  xw_gauss_d1 = sll_f_gauss_legendre_points_and_weights(self%degree+1, 0.0_f64, 1.0_f64)
260  ! Compute bsplines at gauss_points
261  do k=1,self%degree+1
262  call sll_s_uniform_bsplines_eval_basis(self%degree,xw_gauss_d1(1,k), bspl_d1(k,:))
263  end do
264 
265 
266  counter = 1
267  ! Compute coefs_dofs = int f(x)N_i(x)
268  do i2 = 1, self%n_dofs(2)
269  do i1 = 1, self%n_dofs(1)
270  coef=0.0_f64
271  ! loop over support of B spline
272  do j1 = 1, self%degree+1
273  do j2 = 1, self%degree+1
274  ! loop over Gauss points
275  do k1=1, self%degree+1
276  do k2=1, self%degree+1
277  coef = coef + xw_gauss_d1(2,k1)* xw_gauss_d1(2,k2)* &
278  func([self%delta_x(1)*(xw_gauss_d1(1,k1) + real(i1 + j1 - 2,f64)), self%delta_x(2)*(xw_gauss_d1(1,k2) + real(i2 + j2 - 2,f64))] ) * &
279  bspl_d1(k1,self%degree+2-j1)*&
280  bspl_d1(k2,self%degree+2-j2)
281 
282  end do
283  end do
284  end do
285  end do
286  ! rescale by cell size
287  coefs_dofs(counter) = coef*volume
288  counter = counter+1
289  end do
290  end do
291 
292  end subroutine compute_rhs_from_function_fft
293 
294 
296 
297  subroutine ifft2d( self, inval, outval )
298  class(sll_t_poisson_2d_fem_fft),intent( inout) :: self
299  sll_comp64, intent( inout ) :: inval(:,:)
300  sll_real64, intent( out ) :: outval(:)
301 
302  sll_int32 :: i,j,ind
303  sll_int32 :: n_dofs(2)
304 
305  n_dofs = self%n_dofs
306 
307  ! Compute inverse Fourier transfrom
308  !do k=1,n_dofs(3)
309  do i=1,n_dofs(1)
310  do j=1,n_dofs(2)
311  self%array1d_y(j) = inval(i,j)
312  end do
313  call sll_s_fft_exec_c2c_1d( self%ifft2, self%array1d_y, self%array1d_y)
314  do j=1,n_dofs(2)
315  inval(i,j) = self%array1d_y(j)
316  end do
317  end do
318  !end do
319 
320  ind=0
321  !do k=1,n_dofs(3)
322  do j=1,n_dofs(2)
323  do i=1,n_dofs(1)
324  self%array1d_x(i) = inval(i,j)
325  end do
326  call sll_s_fft_exec_c2c_1d( self%ifft1, self%array1d_x, self%array1d_x)
327 
328  do i=1,n_dofs(1)
329  ind = ind+1
330  outval(ind) = real( self%array1d_x(i), kind=f64 )
331  end do
332  end do
333  !end do
334 
335  end subroutine ifft2d
336 
337 
338  subroutine fft2d( self, rho )
339  class(sll_t_poisson_2d_fem_fft),intent( inout) :: self
340  sll_real64, intent( in ) :: rho(:)
341 
342  sll_int32 :: i,j,ind
343  sll_int32 :: n_dofs(2)
344 
345  n_dofs = self%n_dofs
346 
347 
348  ind=0
349  do j=1,n_dofs(2)
350  do i=1,n_dofs(1)
351  ind = ind+1
352  self%array1d_x(i) = cmplx( rho(ind), 0_f64, kind=f64)
353  end do
354  call sll_s_fft_exec_c2c_1d( self%fft1, self%array1d_x, self%array1d_x)
355  do i=1,n_dofs(1)
356  self%scratch(i,j) = self%array1d_x(i)
357  end do
358  end do
359 
360 
361  do i=1,n_dofs(1)
362  do j=1,n_dofs(2)
363  self%array1d_y(j) = self%scratch(i,j)
364  end do
365  call sll_s_fft_exec_c2c_1d( self%fft2, self%array1d_y, self%array1d_y)
366  do j=1,n_dofs(2)
367  self%scratch(i,j) = self%array1d_y(j)
368  end do
369  end do
370 
371 
372  end subroutine fft2d
373 
374 
375 end module sll_m_poisson_2d_fem_fft
Fortran module where set some physical and mathematical constants.
real(kind=f64), parameter, public sll_p_twopi
FFT interface for FFTW.
subroutine, public sll_s_fft_free(plan)
Delete a plan.
integer, parameter, public sll_p_fft_forward
Flag to specify forward FFT (negative sign) [value -1].
subroutine, public sll_s_fft_exec_c2c_1d(plan, array_in, array_out)
Compute fast Fourier transform in complex to complex mode.
subroutine, public sll_s_fft_init_c2c_1d(plan, nx, array_in, array_out, direction, normalized, aligned, optimization)
Create new 1d complex to complex plan.
integer, parameter, public sll_p_fft_backward
Flag to specify backward FFT (positive sign) [value 1].
real(kind=f64) function, dimension(2, 1:npoints), public sll_f_gauss_legendre_points_and_weights(npoints, a, b)
Returns a 2d array of size (2,npoints) containing gauss-legendre points and weights in the interval [...
Low level arbitrary degree splines.
subroutine init_fft(self, n_dofs, degree, delta_x)
subroutine compute_phi_from_rho_fft(self, rho, phi)
subroutine compute_e_from_rho_fft(self, rho, efield)
subroutine ifft2d(self, inval, outval)
Helper function for 3d fft.
subroutine compute_rhs_from_function_fft(self, func, coefs_dofs)
Compute the FEM right-hand-side for a given function f and periodic splines of given degree Its compo...
Utilites for Maxwell solver's with spline finite elements.
subroutine, public sll_s_spline_fem_mass_line(degree, mass_line)
Computes the mass line for a mass matrix with degree splines.
subroutine, public sll_s_spline_fem_compute_mass_eig(n_cells, degree, mass_line, eig_values_mass)
Compute eigenvalues of mass matrix with given line mass_line.
Module to select the kind parameter.
Type for FFT plan in SeLaLib.
    Report Typos and Errors