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_stencil_new.F90
Go to the documentation of this file.
2 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 #include "sll_assert.h"
4 #include "sll_errors.h"
5 
7 
8  use sll_m_bsplines, only: sll_c_bsplines
9 
11 
13 
15 
17 
19 
21 
23 
25 
27 
29 
31 
35 
36  use sll_m_boundary_condition_descriptors, only: sll_p_dirichlet
37 
38  implicit none
39 
41 
42  private
43 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
44 
45  ! Working precision
46  integer, parameter :: wp = f64
47 
48  ! Abstract interface for right hand side
49  abstract interface
50  sll_pure function i_fun_rhs(x) result(rhs)
51  import wp
52  real(wp), intent(in) :: x(2)
53  real(wp) :: rhs
54  end function i_fun_rhs
55  end interface
56 
58 
59  ! To initialize B-splines
60  integer :: mm, n1, n2, ncells1, ncells2, p1, p2
61 
62  class(sll_c_bsplines), pointer :: bsplines_eta1 => null()
63  class(sll_c_bsplines), pointer :: bsplines_eta2 => null()
64 
65  ! Analytical and discrete mappings
66  type(sll_t_singular_mapping_discrete), pointer :: mapping
67 
68  ! Number of finite elements
69  integer :: nk1, nk2
70 
71  ! Number of Gauss-Legendre quadrature points
72  integer :: nq1, nq2
73 
74  ! Quadrature points
75  real(wp), allocatable :: quad_points_eta1(:, :)
76  real(wp), allocatable :: quad_points_eta2(:, :)
77 
78  ! 1D data (B-splines values and derivatives)
79  real(wp), allocatable :: data_1d_eta1(:, :, :, :)
80  real(wp), allocatable :: data_1d_eta2(:, :, :, :)
81 
82  ! 2D data (inverse metric tensor, integral volume, right hand side)
83  real(wp), allocatable :: int_volume(:, :, :, :)
84  real(wp), allocatable :: inv_metric(:, :, :, :, :, :)
85  real(wp), allocatable :: data_2d_rhs(:, :, :, :)
86 
87  ! Stiffness and mass matrices and C1 projections
90 
91  ! Matrix for barycentric coordinates
92  real(wp), allocatable :: l(:, :, :)
93 
94  ! Right hand side vector and C1 projection
97 
98  ! Solution and C1 projection
99  real(wp), allocatable :: x(:)
100 
101  ! Allocatables for accumulation of point charge density
102  real(wp), allocatable :: bspl1(:)
103  real(wp), allocatable :: bspl2(:)
104 
105  ! Weak form
107 
108  ! Assembler
110 
111  ! C1 projector
113 
114  ! Linear solver
115  type(sll_t_conjugate_gradient) :: cjsolver
116  real(wp) :: tol = 1.0e-14_wp ! default value, can be overwritten from init method
117  logical :: verbose = .false. ! default value, can be overwritten from init method
118 
119  ! New C1 block format
120  type(sll_t_linear_operator_matrix_c1_block_new) :: ap_linop_c1_block
121  type(sll_t_linear_operator_matrix_c1_block_new) :: mp_linop_c1_block
122  type(sll_t_vector_space_c1_block) :: xp_vecsp_c1_block
123  type(sll_t_vector_space_c1_block) :: bp_vecsp_c1_block
124 
125  contains
126 
128  procedure :: set_boundary_conditions => s_poisson_2d_fem_sps_stencil_new__set_boundary_conditions
132 
133  ! Accumulate charge: generic procedure with multiple implementations
134  procedure :: accumulate_charge_1 => s_poisson_2d_fem_sps_stencil_new__accumulate_charge_1
135  procedure :: accumulate_charge_2 => s_poisson_2d_fem_sps_stencil_new__accumulate_charge_2
136  procedure :: accumulate_charge_3 => s_poisson_2d_fem_sps_stencil_new__accumulate_charge_3
137  generic :: accumulate_charge => accumulate_charge_1, accumulate_charge_2, accumulate_charge_3
138 
140 
141 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
142 contains
143 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
144 
145  ! Initializer
147  self, &
148  bsplines_eta1, &
149  bsplines_eta2, &
150  breaks_eta1, &
151  breaks_eta2, &
152  mapping, &
153  tol, &
154  verbose)
155  class(sll_t_poisson_2d_fem_sps_stencil_new), intent(inout) :: self
156  class(sll_c_bsplines), target, intent(in) :: bsplines_eta1
157  class(sll_c_bsplines), target, intent(in) :: bsplines_eta2
158  real(wp), allocatable, intent(in) :: breaks_eta1(:)
159  real(wp), allocatable, intent(in) :: breaks_eta2(:)
160  type(sll_t_singular_mapping_discrete), target, intent(in) :: mapping
161  real(wp), optional, intent(in) :: tol
162  logical, optional, intent(in) :: verbose
163 
164  ! Polar B-splines
165  type(sll_t_polar_bsplines_2d) :: polar_bsplines
166 
167  ! Quadrature weights
168  real(wp), allocatable :: quad_weights_eta1(:, :)
169  real(wp), allocatable :: quad_weights_eta2(:, :)
170 
171  ! Auxiliary variables
172  integer :: i, j, k, i1, i2, k1, k2, q1, q2
173  real(wp) :: cx, cy, jdet, eta(2), jmat(2, 2)
174 
175  if (present(tol)) self%tol = tol
176  if (present(verbose)) self%verbose = verbose
177 
178  ! B-splines
179  self%bsplines_eta1 => bsplines_eta1
180  self%bsplines_eta2 => bsplines_eta2
181 
182  ! Smooth spline mapping
183  self%mapping => mapping
184 
185  ! Polar B-splines
186  call polar_bsplines%init(bsplines_eta1, bsplines_eta2, mapping)
187 
188  ! Number of degrees of freedom and spline degrees
189  self%n1 = bsplines_eta1%nbasis
190  self%n2 = bsplines_eta2%nbasis
191 
192  !---------------------------------------------------------------------------
193  ! Allocations
194  !---------------------------------------------------------------------------
195 
196  ! Number of finite elements
197  self%Nk1 = size(breaks_eta1) - 1
198  self%Nk2 = size(breaks_eta2) - 1
199 
200  ! Spline degrees
201  self%p1 = bsplines_eta1%degree
202  self%p2 = bsplines_eta2%degree
203 
204  ! Number of Gauss-Legendre quadrature points
205  self%Nq1 = 1 + self%p1
206  self%Nq2 = 1 + self%p2
207 
208  associate(n1 => self%n1, &
209  n2 => self%n2, &
210  p1 => self%p1, &
211  p2 => self%p2, &
212  nn => 3 + (self%n1 - 2)*self%n2, &
213  nk1 => self%Nk1, &
214  nk2 => self%Nk2, &
215  nq1 => self%Nq1, &
216  nq2 => self%Nq2)
217 
218  ! Quadrature points
219  allocate (self%quad_points_eta1(nq1, nk1))
220  allocate (self%quad_points_eta2(nq2, nk2))
221 
222  ! Quadrature weights
223  allocate (quad_weights_eta1(nq1, nk1))
224  allocate (quad_weights_eta2(nq2, nk2))
225 
226  ! 1D data
227  allocate (self%data_1d_eta1(nq1, 2, 1 + p1, nk1))
228  allocate (self%data_1d_eta2(nq2, 2, 1 + p2, nk2))
229 
230  ! 2D data
231  allocate (self%int_volume(nq1, nq2, nk1, nk2))
232  allocate (self%inv_metric(nq1, nq2, nk1, nk2, 2, 2))
233  allocate (self%data_2d_rhs(nq1, nq2, nk1, nk2))
234 
235  ! Barycentric coordinates
236  allocate (self%L(2, n2, 3))
237 
238  ! Right hand side
239  allocate (self%bs%array(1 - p1:n1 + p1, 1 - p2:n2 + p2))
240  allocate (self%bs_tmp%array(1 - p1:n1 + p1, 1 - p2:n2 + p2))
241 
242  ! Solution
243  allocate (self%x(n1*n2))
244 
245  ! Allocatables for accumulation of point charge density
246  allocate (self%bspl1(1 + p1))
247  allocate (self%bspl2(1 + p2))
248 
249  !-------------------------------------------------------------------------
250  ! Initialize quadrature points and weights
251  !-------------------------------------------------------------------------
252 
253  ! Quadrature points and weights along s
254  do k1 = 1, nk1
255  self%quad_points_eta1(:, k1) = sll_f_gauss_legendre_points(nq1, breaks_eta1(k1), breaks_eta1(k1 + 1))
256  quad_weights_eta1(:, k1) = sll_f_gauss_legendre_weights(nq1, breaks_eta1(k1), breaks_eta1(k1 + 1))
257  end do
258 
259  ! Quadrature points and weights along theta
260  do k2 = 1, nk2
261  self%quad_points_eta2(:, k2) = sll_f_gauss_legendre_points(nq2, breaks_eta2(k2), breaks_eta2(k2 + 1))
262  quad_weights_eta2(:, k2) = sll_f_gauss_legendre_weights(nq2, breaks_eta2(k2), breaks_eta2(k2 + 1))
263  end do
264 
265  !-------------------------------------------------------------------------
266  ! Fill in 1D data
267  !-------------------------------------------------------------------------
268 
269  ! 1D data along s
270  do k1 = 1, nk1
271  do q1 = 1, nq1
272  call bsplines_eta1%eval_basis_and_n_derivs( &
273  x=self%quad_points_eta1(q1, k1), &
274  n=1, &
275  derivs=self%data_1d_eta1(q1, :, :, k1), &
276  jmin=i)
277  end do
278  end do
279 
280  ! 1D data along theta
281  do k2 = 1, nk2
282  do q2 = 1, nq2
283  call bsplines_eta2%eval_basis_and_n_derivs( &
284  x=self%quad_points_eta2(q2, k2), &
285  n=1, &
286  derivs=self%data_1d_eta2(q2, :, :, k2), &
287  jmin=i)
288  end do
289  end do
290 
291  !-------------------------------------------------------------------------
292  ! Fill in 2D data
293  !-------------------------------------------------------------------------
294 
295  ! 2D data
296  do k2 = 1, nk2
297  do k1 = 1, nk1
298  do q2 = 1, nq2
299  do q1 = 1, nq1
300 
301  eta = [self%quad_points_eta1(q1, k1), self%quad_points_eta2(q2, k2)]
302 
303  jdet = mapping%jdet(eta)
304  jmat = mapping%jmat(eta)
305 
306  ! Area associated to each quadrature point
307  self%int_volume(q1, q2, k1, k2) = abs(jdet)*quad_weights_eta1(q1, k1)*quad_weights_eta2(q2, k2)
308 
309  ! Inverse metric tensor
310  self%inv_metric(q1, q2, k1, k2, 1, 1) = jmat(1, 2)**2 + jmat(2, 2)**2
311  self%inv_metric(q1, q2, k1, k2, 1, 2) = -jmat(1, 1)*jmat(1, 2) - jmat(2, 1)*jmat(2, 2)
312  self%inv_metric(q1, q2, k1, k2, 2, 1) = self%inv_metric(q1, q2, k1, k2, 1, 2) ! symmetry
313  self%inv_metric(q1, q2, k1, k2, 2, 2) = jmat(1, 1)**2 + jmat(2, 1)**2
314  self%inv_metric(q1, q2, k1, k2, :, :) = self%inv_metric(q1, q2, k1, k2, :, :)/jdet**2
315 
316  end do
317  end do
318  end do
319  end do
320 
321  !-------------------------------------------------------------------------
322  ! Matrix of barycentric coordinates
323  !-------------------------------------------------------------------------
324 
325  do i2 = 1, n2
326  do i1 = 1, 2
327 
328  ! Indices to access control points
329  i = (i1 - 1)*n2 + i2
330  j = (i - 1)/n2 + 1
331  k = modulo((i - 1), n2) + 1
332 
333  cx = mapping%spline_2d_x1%bcoef(j, k)
334  cy = mapping%spline_2d_x2%bcoef(j, k)
335 
336  self%L(i1, i2, 1) = polar_bsplines%eval_l0(cx, cy)
337  self%L(i1, i2, 2) = polar_bsplines%eval_l1(cx, cy)
338  self%L(i1, i2, 3) = polar_bsplines%eval_l2(cx, cy)
339 
340  end do
341  end do
342 
343  !-------------------------------------------------------------------------
344  ! Initialize assembler and projector
345  !-------------------------------------------------------------------------
346 
347  call self%assembler%init(n1, n2, p1, p2, self%weak_form)
348  call self%projector%init(n1, n2, p1, p2, self%L)
349 
350  !-------------------------------------------------------------------------
351  ! Fill in stiffness and mass matrices
352  !-------------------------------------------------------------------------
353 
354  ! Construct stencil linear operators
355  call self%A_linop_stencil%init(n1, n2, p1, p2)
356  call self%M_linop_stencil%init(n1, n2, p1, p2)
357 
358  ! Cycle over finite elements
359  do k2 = 1, nk2
360  do k1 = 1, nk1
361  call self%assembler%add_element_mat( &
362  k1, &
363  k2, &
364  self%data_1d_eta1, &
365  self%data_1d_eta2, &
366  self%int_volume, &
367  self%inv_metric, &
368  self%A_linop_stencil%A, &
369  self%M_linop_stencil%A)
370  end do
371  end do
372 
373  !-------------------------------------------------------------------------
374  ! Initialize linear system
375  !-------------------------------------------------------------------------
376 
377  ! Construct C1 block linear operators
378 
379  call self%Ap_linop_c1_block%init(n1, n2, p1, p2)
380  call self%Mp_linop_c1_block%init(n1, n2, p1, p2)
381 
382  !-------------------------------------------------------------------------
383  ! Compute C1 projections of stiffness and mass matrices
384  !-------------------------------------------------------------------------
385 
386  call self%projector%change_basis_matrix(self%A_linop_stencil, self%Ap_linop_c1_block)
387  call self%projector%change_basis_matrix(self%M_linop_stencil, self%Mp_linop_c1_block)
388 
389  ! Construct C1 block vector space from vector xp
390 
391  call self%xp_vecsp_c1_block%init(n1 - 2, n2, p1, p2)
392 
393  allocate (self%xp_vecsp_c1_block%vd%array(1:3))
394  allocate (self%xp_vecsp_c1_block%vs%array(1 - p1:(n1 - 2) + p1, 1 - p2:n2 + p2))
395  self%xp_vecsp_c1_block%vd%array(:) = 0.0_wp
396  self%xp_vecsp_c1_block%vs%array(:, :) = 0.0_wp
397 
398  ! Initialize conjugate gradient solver
399  call self%cjsolver%init( &
400  tol=self%tol, &
401  verbose=self%verbose, &
402  template_vector=self%xp_vecsp_c1_block)
403 
404  ! Construct C1 block vector space for right hand side
405  call self%bp_vecsp_c1_block%init(n1 - 2, n2, p1, p2)
406  allocate (self%bp_vecsp_c1_block%vd%array(1:3))
407  allocate (self%bp_vecsp_c1_block%vs%array(1 - p1:(n1 - 2) + p1, 1 - p2:n2 + p2))
408 
409  end associate
410 
411  call polar_bsplines%free()
412 
414 
415  ! Set boundary conditions
417  class(sll_t_poisson_2d_fem_sps_stencil_new), intent(inout) :: self
418  integer, intent(in) :: bc
419 
420  integer :: i, j, i1, i2, j1, j2, k1, k2, nb
421 
422  character(len=*), parameter :: this_sub_name = "sll_t_poisson_2d_fem_sps_stencil_new % set_boundary_conditions"
423 
424  if (bc == sll_p_dirichlet) then
425 
426  associate(n1 => self%n1, &
427  n2 => self%n2, &
428  p1 => self%p1, &
429  p2 => self%p2)
430 
431  nb = (n1 - 2)*n2
432 
433  ! Homogeneous Dirichlet boundary conditions
434  do i2 = 1, n2
435  do i1 = 1, n1 - 2
436  do k2 = -p2, p2
437  do k1 = -p1, p1
438  j1 = i1 + k1
439  j2 = modulo(i2 - 1 + k2, n2) + 1
440  i = (i1 - 1)*n2 + i2
441  j = (j1 - 1)*n2 + j2
442  ! Check range of indices i and j
443  if (i > nb - n2 .or. j > nb - n2) &
444  self%Ap_linop_c1_block%block4%A(k1, k2, i1, i2) = 0.0_wp
445  end do
446  end do
447  end do
448  end do
449 
450  end associate
451 
452  else
453  sll_error(this_sub_name, "Boundary conditions not implemented")
454 
455  end if
456 
458 
460  class(sll_t_poisson_2d_fem_sps_stencil_new), intent(inout) :: self
461 
462  self%bs%array(:, :) = 0.0_wp
463 
465 
466  ! Accumulate charge: signature #1
468  class(sll_t_poisson_2d_fem_sps_stencil_new), intent(inout) :: self
469  procedure(i_fun_rhs) :: rhs
470 
471  ! Auxiliary variables
472  integer :: k1, k2, q1, q2
473  real(wp) :: eta(2), x(2)
474 
475  associate(n1 => self%n1, &
476  n2 => self%n2, &
477  nk1 => self%Nk1, &
478  nk2 => self%Nk2, &
479  nq1 => self%Nq1, &
480  nq2 => self%Nq2)
481 
482  ! 2D discrete RHS
483  do k2 = 1, nk2
484  do k1 = 1, nk1
485  do q2 = 1, nq2
486  do q1 = 1, nq1
487  eta = (/self%quad_points_eta1(q1, k1), self%quad_points_eta2(q2, k2)/)
488  x = self%mapping%eval(eta)
489  self%data_2d_rhs(q1, q2, k1, k2) = rhs(x)
490  end do
491  end do
492  end do
493  end do
494 
495  ! Cycle over finite elements
496  do k2 = 1, nk2
497  do k1 = 1, nk1
498  call self%assembler%add_element_rhs( &
499  k1, &
500  k2, &
501  self%data_1d_eta1, &
502  self%data_1d_eta2, &
503  self%data_2d_rhs, &
504  self%int_volume, &
505  self%bs%array(1:n1, 1:n2))
506  end do
507  end do
508 
509  end associate
510 
512 
513  ! Accumulate charge: signature #2
515  class(sll_t_poisson_2d_fem_sps_stencil_new), intent(inout) :: self
516  type(sll_t_spline_2d), intent(in) :: rhs
517 
518  associate(n1 => self%n1, n2 => self%n2)
519 
520  ! Store temporarily spline coefficients of right hand side into self % b
521  self%bs_tmp%array(:, :) = 0.0_wp
522  self%bs_tmp%array(1:n1, 1:n2) = rhs%bcoef(1:n1, 1:n2)
523 
524  call self%M_linop_stencil%dot_incr(self%bs_tmp, self%bs)
525 
526  end associate
527 
529 
530  ! Accumulate charge: signature #3
531  subroutine s_poisson_2d_fem_sps_stencil_new__accumulate_charge_3(self, intensity, location)
532  class(sll_t_poisson_2d_fem_sps_stencil_new), intent(inout) :: self
533  real(wp), intent(in) :: intensity
534  real(wp), intent(in) :: location(2)
535 
536  integer :: i1, i2, j1, j2, jmin1, jmin2
537 
538  associate(n1 => self%n1, &
539  n2 => self%n2, &
540  p1 => self%p1, &
541  p2 => self%p2, &
542  qc => intensity, &
543  sc => location(1), &
544  tc => location(2))
545 
546  call self%bsplines_eta1%eval_basis(sc, self%bspl1(:), jmin1)
547  call self%bsplines_eta2%eval_basis(tc, self%bspl2(:), jmin2)
548 
549  i2 = 1
550  do j2 = jmin2, jmin2 + p2
551  i1 = 1
552  do j1 = jmin1, jmin1 + p1
553  self%bs%array(j1, j2) = self%bs%array(j1, j2) + qc*self%bspl1(i1)*self%bspl2(i2)
554  i1 = i1 + 1
555  end do
556  i2 = i2 + 1
557  end do
558 
559  end associate
560 
562 
563  ! Solver
565  class(sll_t_poisson_2d_fem_sps_stencil_new), intent(inout) :: self
566  type(sll_t_spline_2d), intent(inout) :: sol
567 
568  integer :: j2, i1, i2, i
569 
570  associate(n1 => self%n1, &
571  n2 => self%n2, &
572  p1 => self%p1, &
573  p2 => self%p2)
574 
575  ! Compute C1 projection of right hand side
576  call self%projector%change_basis_vector(self%bs%array(1:n1, 1:n2), self%bp_vecsp_c1_block)
577 
578  ! Homogeneous Dirichlet boundary conditions
579  do j2 = 1, n2
580  self%bp_vecsp_c1_block%vs%array(n1 - 2, j2) = 0.0_wp
581  end do
582 
583  ! Update buffer regions
584  self%bp_vecsp_c1_block%vs%array(1 - p1:0, :) = 0.0_wp ! no periodicity
585  self%bp_vecsp_c1_block%vs%array((n1 - 2) + 1:(n1 - 2) + p1, :) = 0.0_wp ! no periodicity
586  self%bp_vecsp_c1_block%vs%array(:, 1 - p2:0) = self%bp_vecsp_c1_block%vs%array(:, n2 - p2 + 1:n2)
587  self%bp_vecsp_c1_block%vs%array(:, n2 + 1:n2 + p2) = self%bp_vecsp_c1_block%vs%array(:, 1:p2)
588 
589  ! Set output vector (and first guess) to zero (no garbage)
590  self%xp_vecsp_c1_block%vs%array(:, :) = 0.0_wp
591  self%xp_vecsp_c1_block%vd%array(:) = 0.0_wp
592 
593  ! Solve linear system Ap*xp=bp
594  call self%cjsolver%solve( &
595  a=self%Ap_linop_c1_block, &
596  b=self%bp_vecsp_c1_block, &
597  x=self%xp_vecsp_c1_block)
598 
599  ! Compute solution in tensor-product space
600  call self%projector%change_basis_vector_inv(self%xp_vecsp_c1_block, self%x)
601 
602  do i2 = 1, n2
603  do i1 = 1, n1
604  i = (i1 - 1)*n2 + i2
605  sol%bcoef(i1, i2) = self%x(i)
606  end do
607  end do
608  sol%bcoef(:, n2 + 1:n2 + p2) = sol%bcoef(:, 1:p2)
609 
610  end associate
611 
613 
614  ! Free
616  class(sll_t_poisson_2d_fem_sps_stencil_new), intent(inout) :: self
617 
618  deallocate (self%quad_points_eta1)
619  deallocate (self%quad_points_eta2)
620  deallocate (self%data_1d_eta1)
621  deallocate (self%data_1d_eta2)
622  deallocate (self%int_volume)
623  deallocate (self%inv_metric)
624  deallocate (self%L)
625  deallocate (self%x)
626  deallocate (self%bs%array)
627  deallocate (self%bs_tmp%array)
628 
629  call self%Ap_linop_c1_block%free()
630  call self%Mp_linop_c1_block%free()
631  deallocate (self%bp_vecsp_c1_block%vd%array)
632  deallocate (self%bp_vecsp_c1_block%vs%array)
633  deallocate (self%xp_vecsp_c1_block%vd%array)
634  deallocate (self%xp_vecsp_c1_block%vs%array)
635 
636  call self%projector%free()
637 
638  call self%cjsolver%free()
639 
641 
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_stencil_new__init(self, bsplines_eta1, bsplines_eta2, breaks_eta1, breaks_eta2, mapping, tol, verbose)
subroutine s_poisson_2d_fem_sps_stencil_new__accumulate_charge_3(self, intensity, location)
Module for tensor-product 2D splines.
Vector space for wrapping 2D Fortran real arrays.
Vector space for wrapping 2D 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