Report Typos and Errors    
Semi-Lagrangian Library
Modular library for kinetic and gyrokinetic simulations of plasmas in fusion energy devices.
sll_m_maxwell_2d_fem_fft.F90
Go to the documentation of this file.
1 
9 
10 
11 ! TODO: Write FFT-based mass solver: There is such a solver already defined as linear_solver_mass1 in particle_methods. Reuse? Can we put the parallelization in this solver?
12 ! Remove all parts that belong the PLF
13 ! Add also solver for combined e and b (first step for AVF-based algorithm)
14 
15 
17 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
18 #include "sll_assert.h"
19 #include "sll_memory.h"
20 #include "sll_working_precision.h"
21 
22  use sll_m_low_level_bsplines, only: &
23  sll_s_uniform_bsplines_eval_basis
24 
27 
28  use sll_m_poisson_2d_fem_fft, only : &
30 
31  use sll_m_spline_fem_utilities, only : &
37 
40 
41  implicit none
42 
43  public :: &
45 
46  private
47 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
48 
50 
51  sll_real64 :: lx(2)
52  sll_real64 :: delta_x(2)
53  sll_real64 :: volume
54  sll_int32 :: n_dofs(2)
55  sll_int32 :: n_total
56  sll_int32 :: s_deg_0
57  sll_int32 :: s_deg_1
58  sll_real64, allocatable :: wsave(:)
59  sll_real64, allocatable :: work(:)
60  sll_real64, allocatable :: work2d(:,:)
61  sll_real64, allocatable :: work_d1(:)
62  sll_real64, allocatable :: work_d2_in(:)
63  sll_real64, allocatable :: work_d2_out(:)
64  sll_real64, allocatable :: work2(:)
65  sll_real64, allocatable :: mass_line_0(:,:)
66  sll_real64, allocatable :: mass_line_1(:,:)
67  sll_real64, allocatable :: mass_line_mixed(:,:)
68  type(sll_t_linear_solver_spline_mass_2d_fft) :: inverse_mass_1(3)
69  type(sll_t_linear_solver_spline_mass_2d_fft) :: inverse_mass_2(3)
70  type(sll_t_poisson_2d_fem_fft ) :: poisson_fft
71 
72  contains
73  procedure :: &
74  compute_e_from_b => sll_s_compute_e_from_b_2d_fem
75  procedure :: &
76  compute_b_from_e => sll_s_compute_b_from_e_2d_fem
77  procedure :: &
78  compute_e_from_rho => sll_s_compute_e_from_rho_2d_fem
79  procedure :: &
80  compute_e_from_j => compute_e_from_j_2d_fem
81  procedure :: &
82  compute_rhs_from_function => sll_s_compute_fem_rhs
83  procedure :: &
84  l2norm_squared => l2norm_squared_2d_fem
85  procedure :: &
86  inner_product => inner_product_2d_fem
87  procedure :: &
88  l2projection => l2projection_2d_fem
89  procedure :: &
90  free => free_2d_fem
91  procedure :: &
92  init => init_2d_fem
93  procedure :: &
94  compute_rho_from_e => compute_rho_from_e_2d_fem
95 
96  procedure :: &
98  procedure :: &
100  procedure :: &
101  multiply_mass => multiply_mass_all
102 
103  end type sll_t_maxwell_2d_fem_fft
104 
105 !---------------------------------------------------------------------------!
106  abstract interface
107 
109  use sll_m_working_precision ! can't pass a header file because the
110  ! preprocessor prevents double inclusion.
111  ! It is very rare.
112  sll_real64 :: sll_i_function_2d_real64
113  sll_real64, intent(in) :: x(2)
114  end function sll_i_function_2d_real64
115  end interface
116 contains
117 
119  subroutine compute_rho_from_e_2d_fem(self, efield, rho)
120  class(sll_t_maxwell_2d_fem_fft) :: self
121  sll_real64, intent(in) :: efield(:)
122  sll_real64, intent(inout) :: rho(:)
123 
124 
125  call multiply_mass_1form( self, efield, self%work )
126 
127 
128  call multiply_gt( self, self%work, rho )
129  rho = - rho
130 
131 
132  end subroutine compute_rho_from_e_2d_fem
133 
134 
135 
137  subroutine sll_s_compute_e_from_b_2d_fem(self, delta_t, field_in, field_out)
138  class(sll_t_maxwell_2d_fem_fft) :: self
139  sll_real64, intent(in) :: delta_t
140  sll_real64, intent(in) :: field_in(:)
141  sll_real64, intent(inout) :: field_out(:)
142  ! local variables
143  sll_real64 :: coef
144  sll_int32 :: comp, istart, iend
145 
146  call multiply_mass_2form( self, field_in, self%work )
147 
148  call multiply_ct(self, self%work, self%work2)
149 
150  do comp=1,3
151  istart = 1+(comp-1)*self%n_total
152  iend = comp*self%n_total
153  call self%inverse_mass_1(comp)%solve( self%work2(istart:iend), self%work(istart:iend) )
154  end do
155  ! Update b from self value
156  coef = delta_t
157  field_out = field_out + coef*self%work
158 
159  end subroutine sll_s_compute_e_from_b_2d_fem
160 
163  subroutine sll_s_compute_b_from_e_2d_fem(self, delta_t, field_in, field_out)
164  class(sll_t_maxwell_2d_fem_fft) :: self
165  sll_real64, intent(in) :: delta_t
166  sll_real64, intent(in) :: field_in(:) ! ey
167  sll_real64, intent(inout) :: field_out(:) ! bz
168 
169  call multiply_c(self, field_in, self%work)
170 
171  field_out = field_out - delta_t * self%work
172 
173 
174  end subroutine sll_s_compute_b_from_e_2d_fem
175 
176 
178  subroutine compute_e_from_j_2d_fem(self, current, component, E)
179  class(sll_t_maxwell_2d_fem_fft) :: self
180  sll_real64,dimension(:),intent(in) :: current
181  sll_int32, intent(in) :: component
182  sll_real64,dimension(:),intent(inout) :: e
183 
184  call self%inverse_mass_1(component)%solve( current, self%work(1:self%n_total) )
185 
186  e = e - self%work(1:self%n_total)
187 
188  ! Account for scaling in the mass matrix by dx
189  !E = E/self%volume
190 
191 
192  end subroutine compute_e_from_j_2d_fem
193 
194  subroutine sll_s_compute_e_from_rho_2d_fem(self, rho, E )
195  class(sll_t_maxwell_2d_fem_fft) :: self
196  sll_real64,dimension(:),intent(in) :: rho
197  sll_real64,dimension(:),intent(out) :: e
198 
199 
200  call self%poisson_fft%compute_e_from_rho( rho, e )
201 
202  end subroutine sll_s_compute_e_from_rho_2d_fem
203 
206  subroutine sll_s_compute_fem_rhs(self, func , component, form, coefs_dofs)
207  class(sll_t_maxwell_2d_fem_fft) :: self
208  procedure(sll_i_function_2d_real64) :: func
209  sll_int32 :: component
210  sll_int32 :: form
211  sll_real64, intent(out) :: coefs_dofs(:) ! Finite Element right-hand-side
212 
213  ! local variables
214  sll_int32 :: i1, i2, j1, j2, k1, k2, k, counter
215  sll_int32 :: degree(2)
216  sll_real64 :: coef
217  sll_real64, allocatable :: xw_gauss_d1(:,:), xw_gauss_d2(:,:)
218  sll_real64, allocatable :: bspl_d1(:,:), bspl_d2(:,:)
219 
220  ! Define the spline degree in the 3 dimensions, depending on form and component of the form
221  if ( form == 0 ) then
222  degree = self%s_deg_0
223  elseif (form == 1 ) then
224  degree = self%s_deg_0
225  if (component<3) then
226  degree(component) = self%s_deg_1
227  end if
228  elseif( form == 2) then
229  degree = self%s_deg_1
230  if (component<3) then
231  degree(component) = self%s_deg_0
232  end if
233  elseif( form == 3) then
234  degree = self%s_deg_1
235  else
236  print*, 'Wrong form.'
237  end if
238 
239  ! take enough Gauss points so that projection is exact for splines of degree deg
240  ! rescale on [0,1] for compatibility with B-splines
241  allocate(xw_gauss_d1(2,degree(1)+1))
242  allocate(bspl_d1(degree(1)+1, degree(1)+1))
243  xw_gauss_d1 = sll_f_gauss_legendre_points_and_weights(degree(1)+1, 0.0_f64, 1.0_f64)
244  ! Compute bsplines at gauss_points
245  do k=1,degree(1)+1
246  call sll_s_uniform_bsplines_eval_basis(degree(1),xw_gauss_d1(1,k), bspl_d1(k,:))
247  end do
248 
249  allocate(xw_gauss_d2(2,degree(2)+1))
250  allocate(bspl_d2(degree(2)+1, degree(2)+1))
251  xw_gauss_d2 = sll_f_gauss_legendre_points_and_weights(degree(2)+1, 0.0_f64, 1.0_f64)
252  ! Compute bsplines at gauss_points
253  do k=1,degree(2)+1
254  call sll_s_uniform_bsplines_eval_basis(degree(2),xw_gauss_d2(1,k), bspl_d2(k,:))
255  end do
256 
257  counter = 1
258  ! Compute coefs_dofs = int f(x)N_i(x)
259  !do i3 = 1, self%n_dofs(3)
260  do i2 = 1, self%n_dofs(2)
261  do i1 = 1, self%n_dofs(1)
262  coef=0.0_f64
263  ! loop over support of B spline
264  do j1 = 1, degree(1)+1
265  do j2 = 1, degree(2)+1
266  !do j3 = 1, degree(3)+1
267  ! loop over Gauss points
268  do k1=1, degree(1)+1
269  do k2=1, degree(2)+1
270  !do k3=1, degree(3)+1
271  coef = coef + xw_gauss_d1(2,k1)* xw_gauss_d2(2,k2)* &
272  func([self%delta_x(1)*(xw_gauss_d1(1,k1) + real(i1 + j1 - 2, f64)), &
273  self%delta_x(2)*(xw_gauss_d2(1,k2) + real(i2 + j2 - 2, f64))] ) * &
274  bspl_d1(k1,degree(1)+2-j1)*&
275  bspl_d2(k2,degree(2)+2-j2)
276 
277  !enddo
278  enddo
279  end do
280  !end do
281  end do
282  end do
283  ! rescale by cell size
284  coefs_dofs(counter) = coef*self%volume
285  counter = counter+1
286  enddo
287  end do
288  !end do
289 
290  end subroutine sll_s_compute_fem_rhs
291 
292 
294  subroutine l2projection_2d_fem(self, func, component, form, coefs_dofs)
295  class(sll_t_maxwell_2d_fem_fft) :: self
296  procedure(sll_i_function_2d_real64) :: func
297  sll_int32 :: component
298  sll_int32 :: form
299  sll_real64, intent(out) :: coefs_dofs(:) ! spline coefficients of projection
300  ! local variables
301  !sll_real64 :: coef
302  !sll_real64, dimension(2,degree+1) :: xw_gauss
303  !sll_real64, dimension(degree+1,degree+1) :: bspl
304 
305  ! Compute right-hand-side
306  call sll_s_compute_fem_rhs(self, func, component, form, self%work(1:self%n_total) )
307 
308  select case( form )
309  case( 1 )
310  call self%inverse_mass_1(component)%solve( self%work(1:self%n_total), coefs_dofs )
311  case( 2 )
312  call self%inverse_mass_2(component)%solve( self%work(1:self%n_total), coefs_dofs )
313  case default
314  print*, 'L2projection for', form, '-form not implemented.'
315  end select
316 
317  ! Account for scaling in the mass matrix by dx
318  !coefs_dofs = coefs_dofs/self%volume
319 
320  end subroutine l2projection_2d_fem
321 
323  function l2norm_squared_2d_fem(self, coefs_dofs, component, form) result (r)
324  class(sll_t_maxwell_2d_fem_fft) :: self
325  sll_real64 :: coefs_dofs(:)
326  sll_int32 :: component
327  sll_int32 :: form
328  sll_real64 :: r
329 
330 
331  r = inner_product_2d_fem(self, coefs_dofs, coefs_dofs, component, form)
332 
333 
334  end function l2norm_squared_2d_fem
335 
336  !tmp: OK
337  function inner_product_2d_fem(self, coefs1_dofs, coefs2_dofs, component, form) result (r)
338  class(sll_t_maxwell_2d_fem_fft) :: self
339  sll_real64 :: coefs1_dofs(:)
340  sll_real64 :: coefs2_dofs(:)
341  sll_int32 :: component
342  sll_int32 :: form
343  sll_real64 :: r
344 
345 
346  if ( form == 0 ) then
347  call multiply_mass_2dkron( self, self%mass_line_0(:,1), &
348  self%mass_line_0(:,2), &
349  coefs2_dofs, self%work(1:self%n_total) )
350  elseif (form == 1 ) then
351  select case(component)
352  case (1)
353  call multiply_mass_2dkron( self, self%mass_line_1(:,1), &
354  self%mass_line_0(:,2), &
355  coefs2_dofs, self%work(1:self%n_total) )
356  case(2)
357  call multiply_mass_2dkron( self, self%mass_line_0(:,1), &
358  self%mass_line_1(:,2), &
359  coefs2_dofs, self%work(1:self%n_total) )
360  case(3)
361  call multiply_mass_2dkron( self, self%mass_line_0(:,1), &
362  self%mass_line_0(:,2), &
363  coefs2_dofs, self%work(1:self%n_total) )
364  case default
365  print*, 'wrong component.'
366  end select
367  elseif( form == 2) then
368  select case(component)
369  case (1)
370  call multiply_mass_2dkron( self, self%mass_line_0(:,1), &
371  self%mass_line_1(:,2), &
372  coefs2_dofs, self%work(1:self%n_total) )
373  case(2)
374  call multiply_mass_2dkron( self, self%mass_line_1(:,1), &
375  self%mass_line_0(:,2), &
376  coefs2_dofs, self%work(1:self%n_total) )
377  case(3)
378  call multiply_mass_2dkron( self, self%mass_line_1(:,1), &
379  self%mass_line_1(:,2), &
380  coefs2_dofs, self%work(1:self%n_total) )
381  case default
382  print*, 'wrong component.'
383  end select
384  elseif( form == 3) then
385  call multiply_mass_2dkron( self, self%mass_line_1(:,1), &
386  self%mass_line_1(:,2), &
387  coefs2_dofs, self%work(1:self%n_total) )
388  else
389  print*, 'Wrong form.'
390  end if
391 
392  r = sum(coefs1_dofs*self%work(1:self%n_total))
393 
394 
395  end function inner_product_2d_fem
396 
397 
398  subroutine init_2d_fem( self, domain, n_dofs, s_deg_0 )
399  class(sll_t_maxwell_2d_fem_fft), intent(out) :: self
400  sll_real64, intent(in) :: domain(2,2) ! xmin, xmax
401  sll_int32, intent(in) :: n_dofs(2) ! number of degrees of freedom (here number of cells and grid points)
402  !sll_real64 :: delta_x ! cell size
403  sll_int32, intent(in) :: s_deg_0 ! highest spline degree
404 
405  ! local variables
406  sll_int32 :: j
407  sll_real64 :: mass_line_0(s_deg_0+1), mass_line_1(s_deg_0), mass_line_mixed(s_deg_0*2)
408  sll_real64, allocatable :: eig_values_mass_0_1(:)
409  sll_real64, allocatable :: eig_values_mass_0_2(:)
410  sll_real64, allocatable :: eig_values_mass_1_1(:)
411  sll_real64, allocatable :: eig_values_mass_1_2(:)
412 
413  self%n_dofs = n_dofs
414  self%n_total = product(n_dofs)
415 
416  self%Lx = domain(:,2) - domain(:,1)
417  self%delta_x = self%Lx /real( n_dofs, f64 )
418  self%s_deg_0 = s_deg_0
419  self%s_deg_1 = s_deg_0 - 1
420 
421  self%volume = product(self%delta_x)
422 
423  ! Allocate scratch data
424  allocate( self%work2d(n_dofs(1), n_dofs(2)) )
425  allocate( self%work(self%n_total*3) )
426  allocate( self%work2(self%n_total*3) )
427  allocate( self%work_d1( n_dofs(1) ) )
428  allocate( self%work_d2_in( n_dofs(2) ) )
429  allocate( self%work_d2_out( n_dofs(2) ) )
430 
431 
432  ! Sparse matrices
433  ! Assemble the mass matrices
434  ! First assemble a mass line for both degrees
435  allocate( self%mass_line_0(s_deg_0+1,2) )
436  allocate( self%mass_line_1(s_deg_0,2) )
437  allocate( self%mass_line_mixed(2*s_deg_0,2) )
438  call sll_s_spline_fem_mass_line ( self%s_deg_0, mass_line_0 )
439  call sll_s_spline_fem_mass_line ( self%s_deg_1, mass_line_1 )
440 
441  call sll_s_spline_fem_mixedmass_line ( self%s_deg_0, mass_line_mixed )
442 
443  ! Next put together the 1d parts of the 2d Kronecker product
444  do j=1, 2
445  self%mass_line_0(:,j) = self%delta_x(j) * mass_line_0
446  self%mass_line_1(:,j) = self%delta_x(j) * mass_line_1
447  self%mass_line_mixed(:,j) = self%delta_x(j) * mass_line_mixed
448  end do
449 
450  allocate( eig_values_mass_0_1( n_dofs(1) ) )
451  allocate( eig_values_mass_0_2( n_dofs(2) ) )
452  allocate( eig_values_mass_1_1( n_dofs(1) ) )
453  allocate( eig_values_mass_1_2( n_dofs(2) ) )
454  call sll_s_spline_fem_compute_mass_eig( n_dofs(1), s_deg_0, mass_line_0*self%delta_x(1), &
455  eig_values_mass_0_1 )
456  call sll_s_spline_fem_compute_mass_eig( n_dofs(2), s_deg_0, mass_line_0*self%delta_x(2), &
457  eig_values_mass_0_2 )
458  call sll_s_spline_fem_compute_mass_eig( n_dofs(1), s_deg_0-1, mass_line_1*self%delta_x(1), &
459  eig_values_mass_1_1 )
460  call sll_s_spline_fem_compute_mass_eig( n_dofs(2), s_deg_0-1, mass_line_1*self%delta_x(2), &
461  eig_values_mass_1_2 )
462 
463  call self%inverse_mass_1(1)%create( n_dofs, eig_values_mass_1_1, eig_values_mass_0_2 )
464  call self%inverse_mass_1(2)%create( n_dofs, eig_values_mass_0_1, eig_values_mass_1_2 )
465  call self%inverse_mass_1(3)%create( n_dofs, eig_values_mass_0_1, eig_values_mass_0_2 )
466 
467  call self%inverse_mass_2(1)%create( n_dofs, eig_values_mass_0_1, eig_values_mass_1_2 )
468  call self%inverse_mass_2(2)%create( n_dofs, eig_values_mass_1_1, eig_values_mass_0_2 )
469  call self%inverse_mass_2(3)%create( n_dofs, eig_values_mass_1_1, eig_values_mass_1_2 )
470 
471 
472  ! Poisson solver based on fft inversion
473  call self%poisson_fft%init( self%n_dofs, self%s_deg_0, self%delta_x )
474 
475 
476  end subroutine init_2d_fem
477 
478 
479  subroutine free_2d_fem(self)
480  class(sll_t_maxwell_2d_fem_fft) :: self
481 
482 
483  deallocate(self%work)
484 
485  end subroutine free_2d_fem
486 
487 
488  !tmp:OK
490  subroutine multiply_c(self, field_in, field_out)
491  class(sll_t_maxwell_2d_fem_fft) :: self
492  sll_real64, intent(in) :: field_in(:)
493  sll_real64, intent(inout) :: field_out(:)
494  ! local variables
495  sll_real64 :: coef(2)
496  sll_int32 :: stride(2), jump(2), indp(2)
497  sll_int32 :: i,j, ind2d, ind2d_1, ind2d_2
498 
499 
500  ! TODO: Avoid the IF for periodic boundaries
501  ! First component
502  coef(1) = 1.0_f64/ self%delta_x(2)
503  !coef(2) = -1.0_f64/ self%delta_x(3)
504 
505  stride(1) = self%n_dofs(1)
506  stride(2) = self%n_dofs(1)*self%n_dofs(2)
507 
508  !jump(1) = self%n_total
509  jump(2) = 2*self%n_total
510 
511 
512  ind2d = 0
513  do j=1,self%n_dofs(2)
514  if ( j==1 ) then
515  indp(1) = stride(1)*(self%n_dofs(2)-1)
516  else
517  indp(1) = - stride(1)
518  end if
519  do i=1,self%n_dofs(1)
520  ind2d = ind2d + 1
521 
522  ind2d_1 = ind2d +indp(1)+jump(2)
523  field_out(ind2d) = &
524  coef(1) * ( field_in( ind2d+jump(2) ) -&
525  field_in( ind2d_1 ))
526 
527  end do
528  end do
529 
530 
531  ! Second component
532  !coef(1) = 1.0_f64/ self%delta_x(3)
533  coef(2) = -1.0_f64/ self%delta_x(1)
534 
535  stride(2) = 1
536  !stride(1) = self%n_dofs(1)*self%n_dofs(2)
537 
538  jump(1) = self%n_total
539  !jump(2) = -self%n_total
540 
541 
542  do j=1,self%n_dofs(2)
543  do i=1,self%n_dofs(1)
544  if ( i==1 ) then
545  indp(2) = stride(2)*(self%n_dofs(1)-1)
546  else
547  indp(2) = - stride(2)
548  end if
549  ind2d = ind2d + 1
550 
551  ind2d_2 = ind2d +indp(2)+jump(1)
552 
553  field_out(ind2d) = &
554  coef(2) * ( field_in(ind2d+jump(1) ) - &
555  field_in( ind2d_2 ))
556 
557  end do
558  end do
559 
560  ! Third component
561  coef(1) = 1.0_f64/ self%delta_x(1)
562  coef(2) = -1.0_f64/ self%delta_x(2)
563 
564  stride(1) = 1
565  stride(2) = self%n_dofs(1)
566 
567  jump(1) = -2*self%n_total
568  jump(2) = -self%n_total
569 
570 
571  do j=1,self%n_dofs(2)
572  if (j == 1) then
573  indp(2) = stride(2)*(self%n_dofs(2)-1)
574  else
575  indp(2) = - stride(2)
576  end if
577  do i=1,self%n_dofs(1)
578  if ( i==1 ) then
579  indp(1) = stride(1)*(self%n_dofs(1)-1)
580  else
581  indp(1) = - stride(1)
582  end if
583  ind2d = ind2d + 1
584 
585  ind2d_1 = ind2d +indp(1)+jump(2)
586  ind2d_2 = ind2d +indp(2)+jump(1)
587 
588  field_out(ind2d) = &
589  coef(1) * ( field_in( ind2d+jump(2) ) -&
590  field_in( ind2d_1 ) )+ &
591  coef(2) * ( field_in(ind2d+jump(1) ) - &
592  field_in( ind2d_2 ) )
593 
594  end do
595  end do
596 
597  end subroutine multiply_c
598 
599  !tmp:OK
601  subroutine multiply_ct(self, field_in, field_out)
602  class(sll_t_maxwell_2d_fem_fft) :: self
603  sll_real64, intent(in) :: field_in(:)
604  sll_real64, intent(inout) :: field_out(:)
605  ! local variables
606  sll_real64 :: coef(2)
607  sll_int32 :: stride(2), jump(2), indp(2)
608  sll_int32 :: i,j, ind2d, ind2d_1, ind2d_2
609 
610 
611  ! TODO: Avoid the IF for periodic boundaries
612  ! First component
613  coef(1) = -1.0_f64/ self%delta_x(2)
614  !coef(2) = 1.0_f64/ self%delta_x(3)
615 
616  stride(1) = self%n_dofs(1)
617  !stride(2) = self%n_dofs(1)*self%n_dofs(2)
618 
619  !jump(1) = self%n_total
620  jump(2) = 2*self%n_total
621 
622 
623  ind2d = 0
624  do j=1,self%n_dofs(2)
625  if ( j== self%n_dofs(2)) then
626  indp(1) = -stride(1)*(self%n_dofs(2)-1)
627  else
628  indp(1) = stride(1)
629  end if
630  do i=1,self%n_dofs(1)
631  ind2d = ind2d + 1
632 
633  ind2d_1 = ind2d +indp(1)+jump(2)
634 
635  field_out(ind2d) = &
636  coef(1) * ( field_in( ind2d+jump(2) ) -&
637  field_in( ind2d_1 ))
638 
639  end do
640  end do
641 
642 
643  ! Second component
644  !coef(1) = -1.0_f64/ self%delta_x(3)
645  coef(2) = 1.0_f64/ self%delta_x(1)
646 
647  stride(2) = 1
648  !stride(1) = self%n_dofs(1)*self%n_dofs(2)
649 
650  jump(1) = self%n_total
651  !jump(2) = -self%n_total
652 
653 
654  do j=1,self%n_dofs(2)
655  do i=1,self%n_dofs(1)
656  if ( i==self%n_dofs(1) ) then
657  indp(2) = -stride(2)*(self%n_dofs(1)-1)
658  else
659  indp(2) = stride(2)
660  end if
661  ind2d = ind2d + 1
662  ind2d_2 = ind2d +indp(2)+jump(1)
663 
664  field_out(ind2d) = &
665  coef(2) * ( field_in(ind2d+jump(1) ) - &
666  field_in( ind2d_2 ))
667 
668  end do
669  end do
670 
671  ! Third component
672  coef(1) = -1.0_f64/ self%delta_x(1)
673  coef(2) = 1.0_f64/ self%delta_x(2)
674 
675  stride(1) = 1
676  stride(2) = self%n_dofs(1)
677 
678  jump(1) = -2*self%n_total
679  jump(2) = -self%n_total
680 
681 
682  do j=1,self%n_dofs(2)
683  if (j == self%n_dofs(2)) then
684  indp(2) = -stride(2)*(self%n_dofs(2)-1)
685  else
686  indp(2) = stride(2)
687  end if
688  do i=1,self%n_dofs(1)
689  if ( i==self%n_dofs(1) ) then
690  indp(1) = -stride(1)*(self%n_dofs(1)-1)
691  else
692  indp(1) = stride(1)
693  end if
694  ind2d = ind2d + 1
695 
696  ind2d_1 = ind2d +indp(1)+jump(2)
697  ind2d_2 = ind2d +indp(2)+jump(1)
698 
699  field_out(ind2d) = &
700  coef(1) * ( field_in( ind2d+jump(2) ) -&
701  field_in( ind2d_1 ) )+ &
702  coef(2) * ( field_in(ind2d+jump(1) ) - &
703  field_in( ind2d_2 ) )
704 
705  end do
706  end do
707 
708  end subroutine multiply_ct
709 
710 
712  subroutine multiply_gt(self, field_in, field_out)
713  class(sll_t_maxwell_2d_fem_fft) :: self
714  sll_real64, intent(in) :: field_in(:)
715  sll_real64, intent(inout) :: field_out(:)
716 
717  sll_real64 :: coef
718  sll_int32 :: jump, jump_end
719  sll_int32 :: ind2d, ind2d_1
720  sll_int32 :: i,j
721 
722 
723  coef = 1.0_f64/ self%delta_x(1)
724  jump = 1
725  jump_end = 1-self%n_dofs(1)
726 
727 
728  ind2d = 0
729  do j=1,self%n_dofs(2)
730  do i=1,self%n_dofs(1)-1
731  ind2d = ind2d + 1
732 
733  field_out(ind2d) = &
734  coef * ( field_in(ind2d) - field_in(ind2d+jump) )
735 
736  end do
737  ind2d = ind2d + 1
738  field_out(ind2d) = coef * ( field_in(ind2d) - field_in(ind2d+jump_end) )
739  end do
740 
741  coef = 1.0_f64/ self%delta_x(2)
742  jump = self%n_dofs(1)
743  jump_end = (1-self%n_dofs(2))*self%n_dofs(1)
744 
745  ind2d_1 = 0
746  do j=1,self%n_dofs(2)-1
747  do i=1,self%n_dofs(1)
748  ind2d = ind2d + 1
749  ind2d_1 = ind2d_1 + 1
750 
751  field_out(ind2d_1) = field_out(ind2d_1) + &
752  coef * ( field_in(ind2d) - field_in(ind2d+jump) )
753  end do
754  end do
755  do i=1,self%n_dofs(1)
756  ind2d = ind2d + 1
757  ind2d_1 = ind2d_1 + 1
758 
759  field_out(ind2d_1) = field_out(ind2d_1) + &
760  coef * ( field_in(ind2d) - field_in(ind2d+jump_end) )
761 
762  end do
763 
764 
765  end subroutine multiply_gt
766 
767  !tmp:OK
768  subroutine multiply_mass_1form( self, coefs_in, coefs_out )
769  class(sll_t_maxwell_2d_fem_fft), intent( inout ) :: self
770  sll_real64, intent(in) :: coefs_in(:)
771  sll_real64, intent(out) :: coefs_out(:)
772 
773  sll_int32:: iend, istart
774 
775  istart = 1
776  iend = self%n_total
777  call multiply_mass_2dkron( self, self%mass_line_1(:,1), &
778  self%mass_line_0(:,2), &
779  coefs_in(istart:iend), coefs_out(istart:iend) )
780  istart = iend+1
781  iend = iend + self%n_total
782  call multiply_mass_2dkron( self, self%mass_line_0(:,1), &
783  self%mass_line_1(:,2), &
784  coefs_in(istart:iend), coefs_out(istart:iend) )
785  istart = iend+1
786  iend = iend + self%n_total
787  call multiply_mass_2dkron( self, self%mass_line_0(:,1), &
788  self%mass_line_0(:,2), &
789  coefs_in(istart:iend), coefs_out(istart:iend) )
790 
791 
792  end subroutine multiply_mass_1form
793 
794  !tmp:OK
795  subroutine multiply_mass_2form( self, coefs_in, coefs_out )
796  class(sll_t_maxwell_2d_fem_fft), intent( inout ) :: self
797  sll_real64, intent(in) :: coefs_in(:)
798  sll_real64, intent(out) :: coefs_out(:)
799 
800  sll_int32:: iend, istart
801 
802  istart = 1
803  iend = self%n_total
804  call multiply_mass_2dkron( self, self%mass_line_0(:,1), &
805  self%mass_line_1(:,2), &
806  coefs_in(istart:iend), coefs_out(istart:iend) )
807  istart = iend+1
808  iend = iend + self%n_total
809  call multiply_mass_2dkron( self, self%mass_line_1(:,1), &
810  self%mass_line_0(:,2), &
811  coefs_in(istart:iend), coefs_out(istart:iend) )
812  istart = iend+1
813  iend = iend + self%n_total
814  call multiply_mass_2dkron( self, self%mass_line_1(:,1), &
815  self%mass_line_1(:,2), &
816  coefs_in(istart:iend), coefs_out(istart:iend) )
817 
818 
819  end subroutine multiply_mass_2form
820 
821 
822  !tmp:OK
824  subroutine multiply_mass_2dkron( self, mass_line_1, mass_line_2, coefs_in, coefs_out )
825  class(sll_t_maxwell_2d_fem_fft), intent( inout ) :: self
826  sll_real64, intent(in) :: mass_line_1(:)
827  sll_real64, intent(in) :: mass_line_2(:)
828  sll_real64, intent(in) :: coefs_in(:)
829  sll_real64, intent(out) :: coefs_out(:)
830 
831  ! Local variables
832  sll_int32 :: i,j,istart,iend
833  sll_int32 :: deg(2)
834 
835  deg(1) = size(mass_line_1)-1
836  deg(2) = size(mass_line_2)-1
837 
838  istart = 1
839  iend = self%n_dofs(1)
840  do j=1,self%n_dofs(2)
841  call sll_s_spline_fem_multiply_mass ( self%n_dofs(1), deg(1), &
842  mass_line_1, coefs_in(istart:iend), self%work_d1 )
843  self%work2d(:,j) = self%work_d1
844  istart = iend+1
845  iend = iend + self%n_dofs(1)
846  end do
847 
848  istart = 1
849  do i =1,self%n_dofs(1)
850  self%work_d2_in = self%work2d(i,:)
851  call sll_s_spline_fem_multiply_mass ( self%n_dofs(2), deg(2), &
852  mass_line_2, self%work_d2_in, self%work_d2_out )
853  do j=1,self%n_dofs(2)
854  coefs_out(istart+(j-1)*self%n_dofs(1)) = self%work_d2_out(j)
855  end do
856  istart = istart+1
857  end do
858 
859  end subroutine multiply_mass_2dkron
860 
861  !tmp:OK
863  subroutine multiply_mass_all( self, deg, coefs_in, coefs_out )
864  class(sll_t_maxwell_2d_fem_fft), intent( inout ) :: self
865  sll_int32, intent( in ) :: deg(2)
866  sll_real64, intent(in) :: coefs_in(:)
867  sll_real64, intent(out) :: coefs_out(:)
868 
869  ! Local variables
870  sll_int32 :: i,j,istart,iend
871 
872  istart = 1
873  iend = self%n_dofs(1)
874  do j=1,self%n_dofs(2)
875  select case ( deg(1) )
876  case( 1 )
877  call sll_s_spline_fem_multiply_mass ( self%n_dofs(1), self%s_deg_0, &
878  self%mass_line_0(:,1), coefs_in(istart:iend), self%work_d1 )
879  case( 2 )
880  call sll_s_spline_fem_multiply_mass ( self%n_dofs(1), self%s_deg_1, &
881  self%mass_line_1(:,1), coefs_in(istart:iend), self%work_d1 )
882  case ( 3 )
883  call sll_s_spline_fem_multiply_massmixed ( self%n_dofs(1), self%s_deg_0, &
884  self%mass_line_mixed(:,1), coefs_in(istart:iend), self%work_d1 )
885  case default
886  print*, 'not implemented.'
887  end select
888  self%work2d(:,j) = self%work_d1
889  istart = iend+1
890  iend = iend + self%n_dofs(1)
891  end do
892 
893  istart = 1
894  do i =1,self%n_dofs(1)
895  self%work_d2_in = self%work2d(i,:)
896  select case ( deg(2) )
897  case( 1 )
898  call sll_s_spline_fem_multiply_mass ( self%n_dofs(2), self%s_deg_0, &
899  self%mass_line_0(:,2), self%work_d2_in, self%work_d2_out )
900  case( 2 )
901  call sll_s_spline_fem_multiply_mass ( self%n_dofs(2), self%s_deg_1, &
902  self%mass_line_1(:,2), self%work_d2_in, self%work_d2_out )
903  case ( 3 )
904  call sll_s_spline_fem_multiply_massmixed ( self%n_dofs(2), self%s_deg_0, &
905  self%mass_line_mixed(:,2), self%work_d2_in, self%work_d2_out )
906  case default
907  print*, 'not implemented.'
908  end select
909 
910 
911  do j=1,self%n_dofs(2)
912  coefs_out(istart+(j-1)*self%n_dofs(1)) = self%work_d2_out(j)
913  end do
914  istart = istart+1
915  end do
916 
917  end subroutine multiply_mass_all
918 
919  end module sll_m_maxwell_2d_fem_fft
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 [...
Invert a circulant matrix based on diagonalization in Fourier space (2d version)
Low level arbitrary degree splines.
Module interface to solve Maxwell's equations in 2D The linear systems are solved based on FFT diagno...
subroutine multiply_mass_1form(self, coefs_in, coefs_out)
subroutine compute_rho_from_e_2d_fem(self, efield, rho)
compute rho from e using weak Gauss law ( rho = G^T M_1 e )
subroutine sll_s_compute_fem_rhs(self, func, component, form, coefs_dofs)
Compute the FEM right-hand-side for a given function f and periodic splines of given degree Its compo...
subroutine sll_s_compute_b_from_e_2d_fem(self, delta_t, field_in, field_out)
Compute Bz from Ey using strong 1D Faraday equation for spline coefficients $B_z^{new}(x_j) = B_z^{ol...
subroutine multiply_c(self, field_in, field_out)
Multiply by discrete curl matrix.
subroutine sll_s_compute_e_from_b_2d_fem(self, delta_t, field_in, field_out)
compute Ey from Bz using weak Ampere formulation
subroutine sll_s_compute_e_from_rho_2d_fem(self, rho, E)
subroutine l2projection_2d_fem(self, func, component, form, coefs_dofs)
Compute the L2 projection of a given function f on periodic splines of given degree.
subroutine multiply_mass_2dkron(self, mass_line_1, mass_line_2, coefs_in, coefs_out)
Multiply by the mass matrix.
real(kind=f64) function inner_product_2d_fem(self, coefs1_dofs, coefs2_dofs, component, form)
subroutine init_2d_fem(self, domain, n_dofs, s_deg_0)
subroutine multiply_gt(self, field_in, field_out)
Multiply by transpose of dicrete gradient matrix.
subroutine multiply_ct(self, field_in, field_out)
Multiply by transpose of discrete curl matrix.
real(kind=f64) function l2norm_squared_2d_fem(self, coefs_dofs, component, form)
Compute square of the L2norm.
subroutine compute_e_from_j_2d_fem(self, current, component, E)
Compute E_i from j_i integrated over the time interval using weak Ampere formulation.
subroutine multiply_mass_all(self, deg, coefs_in, coefs_out)
Multiply by the mass matrix.
subroutine multiply_mass_2form(self, coefs_in, coefs_out)
Utilites for Maxwell solver's with spline finite elements.
subroutine, public sll_s_spline_fem_multiply_massmixed(n_cells, degree, mass, invec, outvec)
Multiplication of the mix mass matrix given by a mass line mass.
subroutine, public sll_s_spline_fem_multiply_mass(n_cells, degree, mass, invec, outvec)
Multiply the vector invec with the spline FEM mass matrix with mass line mass.
subroutine, public sll_s_spline_fem_mixedmass_line(deg, mass_line)
Helper function to find line of mass matrix ( N_i^p N_j^{p-1}))
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.
Data type for a linear solver inverting a 2d tensor product of circulant matrices based on FFT.
    Report Typos and Errors