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_sps_dense.F90
Go to the documentation of this file.
2 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 #include "sll_assert.h"
4 
6 
7  use sll_m_bsplines, only: sll_c_bsplines
8 
10 
12 
14 
16 
18 
20 
22 
24 
26 
30 
31  implicit none
32 
34 
35  private
36 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
37 
38  ! Working precision
39  integer, parameter :: wp = f64
40 
41  ! Abstract interface for right hand side
42  abstract interface
43  sll_pure function i_fun_rhs(x) result(rhs)
44  import wp
45  real(wp), intent(in) :: x(2)
46  real(wp) :: rhs
47  end function i_fun_rhs
48  end interface
49 
51 
52  ! To initialize B-splines (p1,p2 degrees)
53  integer :: mm, n1, n2, ncells1, ncells2
54 
55  ! Analytical and discrete mappings
56  type(sll_t_singular_mapping_discrete), pointer :: mapping
57 
58  ! Number of finite elements
59  integer :: nk1, nk2
60 
61  ! Number of Gauss-Legendre quadrature points
62  integer :: nq1, nq2
63 
64  ! Quadrature points
65  real(wp), allocatable :: quad_points_eta1(:, :)
66  real(wp), allocatable :: quad_points_eta2(:, :)
67 
68  ! 1D data (B-splines values and derivatives)
69  real(wp), allocatable :: data_1d_eta1(:, :, :, :)
70  real(wp), allocatable :: data_1d_eta2(:, :, :, :)
71 
72  ! 2D data (inverse metric tensor, integral volume, right hand side)
73  real(wp), allocatable :: int_volume(:, :, :, :)
74  real(wp), allocatable :: inv_metric(:, :, :, :, :, :)
75  real(wp), allocatable :: data_2d_rhs(:, :, :, :)
76 
77  ! Stiffness and mass matrices and C1 projections
78  real(wp), allocatable :: a(:, :)
79  real(wp), allocatable :: m(:, :)
80  real(wp), allocatable :: ap(:, :)
81  real(wp), allocatable :: mp(:, :)
82 
83  ! Matrix for barycentric coordinates
84  real(wp), allocatable :: l(:, :)
85 
86  ! Right hand side vector and C1 projection
87  real(wp), allocatable :: b(:)
88  real(wp), allocatable :: bp(:)
89 
90  ! Solution and C1 projection
91  real(wp), allocatable :: x(:)
92  real(wp), allocatable :: xp(:)
93 
94  ! Weak form
96 
97  ! Assembler
99 
100  ! C1 projector
102 
103  ! Linear solver
107  type(sll_t_conjugate_gradient) :: cjsolver
108  real(wp) :: tol = 1.0e-14_wp ! default value, can be overwritten from init method
109  logical :: verbose = .false. ! default value, can be overwritten from init method
110 
111  contains
112 
113  procedure :: init => s_poisson_2d_fem_sps_dense__init
114  procedure :: free => s_poisson_2d_fem_sps_dense__free
115 
116  ! Generic procedure with multiple implementations
117  procedure :: solve_1 => s_poisson_2d_fem_sps_dense__solve_1
118  procedure :: solve_2 => s_poisson_2d_fem_sps_dense__solve_2
119  generic :: solve => solve_1, solve_2
120 
122 
123 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
124 contains
125 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
126 
127  ! Initializer
129  self, &
130  bsplines_eta1, &
131  bsplines_eta2, &
132  breaks_eta1, &
133  breaks_eta2, &
134  mapping, &
135  tol, &
136  verbose)
137  class(sll_t_poisson_2d_fem_sps_dense), intent(inout) :: self
138  class(sll_c_bsplines), target, intent(in) :: bsplines_eta1
139  class(sll_c_bsplines), target, intent(in) :: bsplines_eta2
140  real(wp), allocatable, intent(in) :: breaks_eta1(:)
141  real(wp), allocatable, intent(in) :: breaks_eta2(:)
142  type(sll_t_singular_mapping_discrete), target, intent(in) :: mapping
143  real(wp), optional, intent(in) :: tol
144  logical, optional, intent(in) :: verbose
145 
146  ! Polar B-splines
147  type(sll_t_polar_bsplines_2d) :: polar_bsplines
148 
149  ! Quadrature weights
150  real(wp), allocatable :: quad_weights_eta1(:, :)
151  real(wp), allocatable :: quad_weights_eta2(:, :)
152 
153  ! Auxiliary variables
154  integer :: i, j, k, idx, k1, k2, q1, q2, p1, p2
155  real(wp) :: cx, cy, jdet, eta(2), jmat(2, 2)
156 
157  if (present(tol)) self%tol = tol
158  if (present(verbose)) self%verbose = verbose
159 
160  ! Smooth spline mapping
161  self%mapping => mapping
162 
163  ! Polar B-splines
164  call polar_bsplines%init(bsplines_eta1, bsplines_eta2, mapping)
165 
166  ! Number of degrees of freedom and spline degrees
167  self%n1 = bsplines_eta1%nbasis
168  self%n2 = bsplines_eta2%nbasis
169 
170  !---------------------------------------------------------------------------
171  ! Allocations
172  !---------------------------------------------------------------------------
173 
174  ! Number of finite elements
175  self%Nk1 = size(breaks_eta1) - 1
176  self%Nk2 = size(breaks_eta2) - 1
177 
178  ! Spline degrees
179  p1 = bsplines_eta1%degree
180  p2 = bsplines_eta2%degree
181 
182  ! Number of Gauss-Legendre quadrature points
183  self%Nq1 = 1 + p1
184  self%Nq2 = 1 + p2
185 
186  associate(n1 => self%n1, &
187  n2 => self%n2, &
188  nn => 3 + (self%n1 - 2)*self%n2, &
189  nk1 => self%Nk1, &
190  nk2 => self%Nk2, &
191  nq1 => self%Nq1, &
192  nq2 => self%Nq2)
193 
194  ! Quadrature points
195  allocate (self%quad_points_eta1(nq1, nk1))
196  allocate (self%quad_points_eta2(nq2, nk2))
197 
198  ! Quadrature weights
199  allocate (quad_weights_eta1(nq1, nk1))
200  allocate (quad_weights_eta2(nq2, nk2))
201 
202  ! 1D data
203  allocate (self%data_1d_eta1(nq1, 2, 1 + p1, nk1))
204  allocate (self%data_1d_eta2(nq2, 2, 1 + p2, nk2))
205 
206  ! 2D data
207  allocate (self%int_volume(nq1, nq2, nk1, nk2))
208  allocate (self%inv_metric(nq1, nq2, nk1, nk2, 2, 2))
209  allocate (self%data_2d_rhs(nq1, nq2, nk1, nk2))
210 
211  ! Barycentric coordinates
212  allocate (self%L(2*n2, 3))
213 
214  ! Stiffness and mass matrices
215  allocate (self%A(n1*n2, n1*n2))
216  allocate (self%M(n1*n2, n1*n2))
217 
218  ! Right hand side
219  allocate (self%b(n1*n2))
220 
221  ! Solution
222  allocate (self%x(n1*n2))
223 
224  ! C1 projections of stiffness and mass matrices
225  allocate (self%Ap(nn, nn))
226  allocate (self%Mp(nn, nn))
227 
228  ! C1 projection of right hand side
229  allocate (self%bp(nn))
230 
231  ! C1 projection of solution
232  allocate (self%xp(nn))
233 
234  !-------------------------------------------------------------------------
235  ! Initialize quadrature points and weights
236  !-------------------------------------------------------------------------
237 
238  ! Quadrature points and weights along s
239  do k1 = 1, nk1
240  self%quad_points_eta1(:, k1) = sll_f_gauss_legendre_points(nq1, breaks_eta1(k1), breaks_eta1(k1 + 1))
241  quad_weights_eta1(:, k1) = sll_f_gauss_legendre_weights(nq1, breaks_eta1(k1), breaks_eta1(k1 + 1))
242  end do
243 
244  ! Quadrature points and weights along theta
245  do k2 = 1, nk2
246  self%quad_points_eta2(:, k2) = sll_f_gauss_legendre_points(nq2, breaks_eta2(k2), breaks_eta2(k2 + 1))
247  quad_weights_eta2(:, k2) = sll_f_gauss_legendre_weights(nq2, breaks_eta2(k2), breaks_eta2(k2 + 1))
248  end do
249 
250  !-------------------------------------------------------------------------
251  ! Fill in 1D data
252  !-------------------------------------------------------------------------
253 
254  ! 1D data along s
255  do k1 = 1, nk1
256  do q1 = 1, nq1
257  call bsplines_eta1%eval_basis_and_n_derivs( &
258  x=self%quad_points_eta1(q1, k1), &
259  n=1, &
260  derivs=self%data_1d_eta1(q1, :, :, k1), &
261  jmin=i)
262  end do
263  end do
264 
265  ! 1D data along theta
266  do k2 = 1, nk2
267  do q2 = 1, nq2
268  call bsplines_eta2%eval_basis_and_n_derivs( &
269  x=self%quad_points_eta2(q2, k2), &
270  n=1, &
271  derivs=self%data_1d_eta2(q2, :, :, k2), &
272  jmin=i)
273  end do
274  end do
275 
276  !-------------------------------------------------------------------------
277  ! Fill in 2D data
278  !-------------------------------------------------------------------------
279 
280  ! 2D data
281  do k2 = 1, nk2
282  do k1 = 1, nk1
283  do q2 = 1, nq2
284  do q1 = 1, nq1
285 
286  eta = [self%quad_points_eta1(q1, k1), self%quad_points_eta2(q2, k2)]
287 
288  jdet = mapping%jdet(eta)
289  jmat = mapping%jmat(eta)
290 
291  ! Area associated to each quadrature point
292  self%int_volume(q1, q2, k1, k2) = abs(jdet)*quad_weights_eta1(q1, k1)*quad_weights_eta2(q2, k2)
293 
294  ! Inverse metric tensor
295  self%inv_metric(q1, q2, k1, k2, 1, 1) = jmat(1, 2)**2 + jmat(2, 2)**2
296  self%inv_metric(q1, q2, k1, k2, 1, 2) = -jmat(1, 1)*jmat(1, 2) - jmat(2, 1)*jmat(2, 2)
297  self%inv_metric(q1, q2, k1, k2, 2, 1) = self%inv_metric(q1, q2, k1, k2, 1, 2) ! symmetry
298  self%inv_metric(q1, q2, k1, k2, 2, 2) = jmat(1, 1)**2 + jmat(2, 1)**2
299  self%inv_metric(q1, q2, k1, k2, :, :) = self%inv_metric(q1, q2, k1, k2, :, :)/jdet**2
300 
301  end do
302  end do
303  end do
304  end do
305 
306  !-------------------------------------------------------------------------
307  ! Matrix of barycentric coordinates
308  !-------------------------------------------------------------------------
309 
310  do i = 1, 2*n2
311 
312  ! Indices to access control points
313  j = (i - 1)/n2 + 1
314  k = modulo((i - 1), n2) + 1
315 
316  cx = mapping%spline_2d_x1%bcoef(j, k)
317  cy = mapping%spline_2d_x2%bcoef(j, k)
318 
319  self%L(i, 1) = polar_bsplines%eval_l0(cx, cy)
320  self%L(i, 2) = polar_bsplines%eval_l1(cx, cy)
321  self%L(i, 3) = polar_bsplines%eval_l2(cx, cy)
322 
323  end do
324 
325  !-------------------------------------------------------------------------
326  ! Initialize assembler and projector
327  !-------------------------------------------------------------------------
328 
329  call self%assembler%init(n1, n2, self%weak_form)
330  call self%projector%init(n1, n2, self%L)
331 
332  !-------------------------------------------------------------------------
333  ! Fill in stiffness and mass matrices
334  !-------------------------------------------------------------------------
335 
336  self%A = 0.0_wp
337  self%M = 0.0_wp
338 
339  ! Cycle over finite elements
340  do k2 = 1, nk2
341  do k1 = 1, nk1
342  call self%assembler%add_element_mat( &
343  k1, &
344  k2, &
345  self%data_1d_eta1, &
346  self%data_1d_eta2, &
347  self%int_volume, &
348  self%inv_metric, &
349  self%A, &
350  self%M)
351  end do
352  end do
353 
354  !-------------------------------------------------------------------------
355  ! Compute C1 projections of stiffness and mass matrices
356  !-------------------------------------------------------------------------
357 
358  call self%projector%change_basis_matrix(self%A, self%Ap)
359  call self%projector%change_basis_matrix(self%M, self%Mp)
360 
361  !-------------------------------------------------------------------------
362  ! Initialize linear system (homogeneous Dirichlet boundary conditions)
363  !-------------------------------------------------------------------------
364 
365  idx = 3 + (n1 - 3)*n2
366 
367  ! Construct linear operator from matrix Ap
368  call self%Ap_linop%init(idx, idx)
369  self%Ap_linop%A = self%Ap(:idx, :idx)
370 
371  ! Construct vector space for solution
372  self%xp = 0.0_wp
373  allocate (self%xp_vecsp%array(size(self%xp(:idx))), source=self%xp(:idx))
374 
375  ! Initialize conjugate gradient solver
376  call self%cjsolver%init( &
377  tol=self%tol, &
378  verbose=self%verbose, &
379  template_vector=self%xp_vecsp)
380 
381  end associate
382 
383  call polar_bsplines%free()
384 
385  end subroutine s_poisson_2d_fem_sps_dense__init
386 
387  ! Solver: signature #1
388  subroutine s_poisson_2d_fem_sps_dense__solve_1(self, rhs, sol)
389  class(sll_t_poisson_2d_fem_sps_dense), intent(inout) :: self
390  procedure(i_fun_rhs) :: rhs
391  type(sll_t_spline_2d), intent(inout) :: sol
392 
393  ! Auxiliary variables
394  integer :: idx, k1, k2, q1, q2
395  real(wp) :: eta(2), x(2)
396 
397  associate(n1 => self%n1, &
398  n2 => self%n2, &
399  nn => 3 + (self%n1 - 2)*self%n2, &
400  nk1 => self%Nk1, &
401  nk2 => self%Nk2, &
402  nq1 => self%Nq1, &
403  nq2 => self%Nq2)
404 
405  ! 2D discrete RHS
406  do k2 = 1, nk2
407  do k1 = 1, nk1
408  do q2 = 1, nq2
409  do q1 = 1, nq1
410 
411  eta = [self%quad_points_eta1(q1, k1), self%quad_points_eta2(q2, k2)]
412 
413  x = self%mapping%eval(eta)
414 
415  self%data_2d_rhs(q1, q2, k1, k2) = rhs(x)
416 
417  end do
418  end do
419  end do
420  end do
421 
422  !-------------------------------------------------------------------------
423  ! Fill in right hand side
424  !-------------------------------------------------------------------------
425 
426  self%b = 0.0_wp
427 
428  ! Cycle over finite elements
429  do k2 = 1, nk2
430  do k1 = 1, nk1
431  call self%assembler%add_element_rhs( &
432  k1, &
433  k2, &
434  self%data_1d_eta1, &
435  self%data_1d_eta2, &
436  self%data_2d_rhs, &
437  self%int_volume, &
438  self%b)
439  end do
440  end do
441 
442  !-------------------------------------------------------------------------
443  ! Compute C1 projection of right hand side
444  !-------------------------------------------------------------------------
445 
446  call self%projector%change_basis_vector(self%b, self%bp)
447 
448  !-------------------------------------------------------------------------
449  ! Solve linear system (homogeneous Dirichlet boundary conditions)
450  !-------------------------------------------------------------------------
451 
452  idx = 3 + (n1 - 3)*n2
453 
454  ! Construct vector space from vector bp
455  allocate (self%bp_vecsp%array(size(self%bp(:idx))), source=self%bp(:idx))
456 
457  ! Solve linear system Ap*xp=bp
458  call self%cjsolver%solve( &
459  a=self%Ap_linop, &
460  b=self%bp_vecsp, &
461  x=self%xp_vecsp)
462 
463  ! Copy solution into array xp
464  self%xp(:idx) = self%xp_vecsp%array
465 
466  !-------------------------------------------------------------------------
467  ! Compute solution in tensor-product space
468  !-------------------------------------------------------------------------
469 
470  call self%projector%change_basis_vector_inv(self%xp, self%x)
471 
472  sol%bcoef = reshape(self%x, (/n2, n1/))
473 
474  end associate
475 
477 
478  ! Solver: signature #2
479  subroutine s_poisson_2d_fem_sps_dense__solve_2(self, rhs, sol)
480  class(sll_t_poisson_2d_fem_sps_dense), intent(inout) :: self
481  type(sll_t_spline_2d), intent(in) :: rhs
482  type(sll_t_spline_2d), intent(inout) :: sol
483 
484  integer :: idx, i, i1, i2
485 
486  associate(n1 => self%n1, n2 => self%n2)
487 
488  ! Store temporarily spline coefficients of right hand side into self % b
489  do i2 = 1, n2
490  do i1 = 1, n1
491  i = (i1 - 1)*n2 + i2
492  self%b(i) = rhs%bcoef(i1, i2)
493  end do
494  end do
495 
496  ! Compute right hand side
497  self%b = matmul(self%M, self%b)
498 
499  ! Compute C1 projection of right hand side
500  call self%projector%change_basis_vector(self%b, self%bp)
501 
502  !-------------------------------------------------------------------------
503  ! Solve linear system (homogeneous Dirichlet boundary conditions)
504  !-------------------------------------------------------------------------
505 
506  idx = 3 + (n1 - 3)*n2
507 
508  ! Construct vector space from vector bp
509  allocate (self%bp_vecsp%array(size(self%bp(:idx))), source=self%bp(:idx))
510 
511  ! Solve linear system Ap*xp=bp
512  call self%cjsolver%solve( &
513  a=self%Ap_linop, &
514  b=self%bp_vecsp, &
515  x=self%xp_vecsp)
516 
517  ! Copy solution into array xp
518  self%xp(:idx) = self%xp_vecsp%array
519 
520  !-------------------------------------------------------------------------
521  ! Compute solution in tensor-product space
522  !-------------------------------------------------------------------------
523 
524  call self%projector%change_basis_vector_inv(self%xp, self%x)
525 
526  sol%bcoef = reshape(self%x, (/n2, n1/))
527 
528  end associate
529 
531 
532  ! Free
534  class(sll_t_poisson_2d_fem_sps_dense), intent(inout) :: self
535 
536  deallocate (self%quad_points_eta1)
537  deallocate (self%quad_points_eta2)
538  deallocate (self%data_1d_eta1)
539  deallocate (self%data_1d_eta2)
540  deallocate (self%int_volume)
541  deallocate (self%inv_metric)
542  deallocate (self%A)
543  deallocate (self%M)
544  deallocate (self%Ap)
545  deallocate (self%Mp)
546  deallocate (self%b)
547  deallocate (self%bp)
548  deallocate (self%L)
549 
550  call self%projector%free()
551 
552  call self%cjsolver%free()
553  call self%Ap_linop%free()
554  deallocate (self%bp_vecsp%array)
555  deallocate (self%xp_vecsp%array)
556 
557  end subroutine s_poisson_2d_fem_sps_dense__free
558 
Access point to B-splines of arbitrary degree providing factory function.
real(kind=f64) function, dimension(1:npoints), public sll_f_gauss_legendre_points(npoints, a, b)
real(kind=f64) function, dimension(1:npoints), public sll_f_gauss_legendre_weights(npoints, a, b)
subroutine s_poisson_2d_fem_sps_dense__init(self, bsplines_eta1, bsplines_eta2, breaks_eta1, breaks_eta2, mapping, tol, verbose)
subroutine s_poisson_2d_fem_sps_dense__solve_2(self, rhs, sol)
subroutine s_poisson_2d_fem_sps_dense__solve_1(self, rhs, sol)
Module for tensor-product 2D splines.
Vector space for wrapping 1D Fortran real arrays.
Module to select the kind parameter.
integer, parameter, public f64
f64 is the kind type for 64-bit reals (double precision)
Type containing new 2D polar basis functions.
2D tensor-product spline
    Report Typos and Errors