Report Typos and Errors    
Semi-Lagrangian Library
Modular library for kinetic and gyrokinetic simulations of plasmas in fusion energy devices.
sll_m_maxwell_3d_fem.F90
Go to the documentation of this file.
1 
9 
11  !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
12 #include "sll_assert.h"
13 #include "sll_memory.h"
14 #include "sll_working_precision.h"
15 
16  use sll_m_collective, only: &
19 
22 
23  use sll_m_linear_operator_block, only : &
25 
26  use sll_m_linear_operator_kron, only : &
28 
31 
34 
37 
38  use sll_m_linear_solver_cg, only : &
40 
41  use sll_m_linear_solver_kron, only : &
43 
44  use sll_m_low_level_bsplines, only: &
45  sll_s_uniform_bsplines_eval_basis
46 
47  use sll_m_matrix_csr, only: &
49 
50  use sll_m_maxwell_3d_base, only: &
53 
54  use sll_m_preconditioner_fft, only : &
56 
57  use sll_m_profile_functions, only: &
59 
60  use sll_m_spline_fem_utilities, only : &
67 
71 
75 
76 
77 
78  implicit none
79 
80  public :: &
82 
83  private
84  !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
85 
87 
88  sll_real64, allocatable :: work0(:)
89  sll_real64, allocatable :: work(:)
90  sll_real64, allocatable :: work3d(:,:,:)
91  sll_real64, allocatable :: work2(:)
92  sll_real64, allocatable :: work_d1(:)
93  sll_real64, allocatable :: work_d2_in(:)
94  sll_real64, allocatable :: work_d2_out(:)
95  sll_real64, allocatable :: work_d3_in(:)
96  sll_real64, allocatable :: work_d3_out(:)
97 
98  type(sll_t_matrix_csr) :: mass0
99  type(sll_t_matrix_csr) :: mass1d(3,3)
100  type(sll_t_linear_solver_cg) :: mass1d_solver(2,3)
101  type(sll_t_linear_solver_kron) :: mass_1_solver(3)
102  type(sll_t_linear_solver_kron) :: mass_2_solver(3)
103 
104  type(sll_t_linear_operator_kron) :: mass1(3)
105  type(sll_t_linear_operator_kron) :: mass2(3)
106  type(sll_t_linear_operator_block) :: mass1_operator
107  type(sll_t_linear_operator_block) :: mass2_operator
108  type(sll_t_linear_solver_cg) :: mass0_solver
109  type(sll_t_linear_solver_cg) :: mass1_solver
110  type(sll_t_preconditioner_fft) :: preconditioner_fft
111  type(sll_t_linear_operator_poisson_3d) :: poisson_matrix
112  type(sll_t_linear_operator_penalized) :: poisson_operator
113  type(sll_t_linear_solver_cg) :: poisson_solver
114  type( sll_t_linear_operator_schur_eb_3d ) :: linear_op_schur_eb
115  type( sll_t_linear_solver_cg ) :: linear_solver_schur_eb
116  logical :: adiabatic_electrons = .false.
117 
118  contains
119  procedure :: &
120  compute_e_from_b => sll_s_compute_e_from_b_3d_fem
121  procedure :: &
122  compute_b_from_e => sll_s_compute_b_from_e_3d_fem
123  procedure :: &
124  compute_curl_part => sll_s_compute_curl_part_3d_fem
125  procedure :: &
126  compute_e_from_rho => sll_s_compute_e_from_rho_3d_fem
127  procedure :: &
128  compute_rho_from_e => sll_s_compute_rho_from_e_3d_fem
129  procedure :: &
130  compute_e_from_j => sll_s_compute_e_from_j_3d_fem
131  procedure :: &
132  compute_phi_from_rho => sll_s_compute_phi_from_rho_3d_fem
133  procedure :: &
134  compute_phi_from_j => sll_s_compute_phi_from_j_3d_fem
135  procedure :: &
136  compute_rhs_from_function => sll_s_compute_rhs_fem
137  procedure :: &
138  l2projection => l2projection_3d_fem
139  procedure :: &
140  l2norm_squared => l2norm_squared_3d_fem
141  procedure :: &
143  procedure :: &
144  init => init_3d_fem
145  procedure :: &
146  init_from_file => init_from_file_3d_fem
147  procedure :: &
148  free => free_3d_fem
149  procedure :: &
150  multiply_g
151  procedure :: &
153  procedure :: &
154  multiply_c
155  procedure :: &
157  procedure :: &
159  procedure :: &
161  procedure :: &
163 
164  end type sll_t_maxwell_3d_fem
165 
166 contains
167 
168 
170  subroutine sll_s_compute_e_from_b_3d_fem(self, delta_t, field_in, field_out)
171  class(sll_t_maxwell_3d_fem) :: self
172  sll_real64, intent(in) :: delta_t
173  sll_real64, intent(in) :: field_in(:)
174  sll_real64, intent(inout) :: field_out(:)
175 
176  call self%mass2_operator%dot( field_in, self%work )
177  call multiply_ct(self, self%work, self%work2)
178 
179  call self%mass1_solver%solve( self%work2, self%work )
180  ! Update b from self value
181  field_out = field_out + delta_t*self%work
182 
183  end subroutine sll_s_compute_e_from_b_3d_fem
184 
185 
188  subroutine sll_s_compute_b_from_e_3d_fem(self, delta_t, field_in, field_out)
189  class(sll_t_maxwell_3d_fem) :: self
190  sll_real64, intent(in) :: delta_t
191  sll_real64, intent(in) :: field_in(:) ! E
192  sll_real64, intent(inout) :: field_out(:) ! B
193 
194  call multiply_c(self, field_in, self%work)
195  ! Update b from self value
196  field_out = field_out - delta_t * self%work
197 
198  end subroutine sll_s_compute_b_from_e_3d_fem
199 
200 
202  subroutine sll_s_compute_curl_part_3d_fem( self, delta_t, efield, bfield, betar )
203  class(sll_t_maxwell_3d_fem) :: self
204  sll_real64, intent( in ) :: delta_t
205  sll_real64, intent( inout ) :: efield(:)
206  sll_real64, intent( inout ) :: bfield(:)
207  sll_real64, optional :: betar
208  !local variables
209  sll_real64 :: factor
210 
211  if( present(betar) ) then
212  factor = betar
213  else
214  factor = 1._f64
215  end if
216 
217  ! Compute C^T M2 b
218  call self%mass2_operator%dot( bfield, self%work )
219  call self%multiply_ct( self%work, self%work2 )
220 
221  self%linear_op_schur_eb%sign = -delta_t**2*factor*0.25_f64
222  call self%linear_op_schur_eb%dot( efield, self%work )
223  self%work = self%work + delta_t*factor*self%work2
224 
225  ! Save efield dofs from previous time step for B field update
226  self%work2 = efield
227 
228  ! Invert Schur complement matrix
229  self%linear_op_schur_eb%sign = delta_t**2*factor*0.25_f64
230  call self%linear_solver_schur_eb%set_guess( efield )
231  call self%linear_solver_schur_eb%solve( self%work, efield)
232 
233  ! Update B field
234  self%work2 = self%work2 + efield
235  call self%compute_b_from_e( delta_t*0.5_f64, self%work2, bfield)
236 
237  end subroutine sll_s_compute_curl_part_3d_fem
238 
239 
241  subroutine sll_s_compute_e_from_rho_3d_fem( self, field_in, field_out )
242  class(sll_t_maxwell_3d_fem) :: self
243  sll_real64, intent( in ) :: field_in(:)
244  sll_real64, intent( out ) :: field_out(:)
245 
246  ! Version with iterative solver
247  call self%poisson_solver%solve( field_in, self%work(1:self%n_total) )
248  call multiply_g(self, self%work(1:self%n_total), field_out)
249  field_out = -field_out
250 
251  end subroutine sll_s_compute_e_from_rho_3d_fem
252 
253 
255  subroutine sll_s_compute_rho_from_e_3d_fem( self, field_in, field_out )
256  class(sll_t_maxwell_3d_fem) :: self
257  sll_real64, intent( in ) :: field_in(:)
258  sll_real64, intent( out ) :: field_out(:)
259 
260  call self%mass1_operator%dot( field_in, self%work )
261 
262  call multiply_gt( self, self%work, field_out )
263  field_out = - field_out
264 
265  end subroutine sll_s_compute_rho_from_e_3d_fem
266 
267 
269  subroutine sll_s_compute_e_from_j_3d_fem( self, current, E, component )
270  class(sll_t_maxwell_3d_fem) :: self
271  sll_real64, intent( in ) :: current(:)
272  sll_real64, intent( inout ) :: e(:)
273  sll_int32, intent( in ), optional :: component
274 
275  if(present(component)) then
276  call self%mass_1_solver(component)%solve( current, self%work(1:self%n_total) )
277  e = e - self%work(1:self%n_total)
278  else
279  call self%mass1_solver%solve( current, self%work )
280  e = e - self%work
281  end if
282 
283  end subroutine sll_s_compute_e_from_j_3d_fem
284 
285 
287  subroutine sll_s_compute_phi_from_rho_3d_fem( self, field_in, field_out, efield_dofs )
288  class(sll_t_maxwell_3d_fem) :: self
289  sll_real64, intent( in ) :: field_in(:)
290  sll_real64, intent( inout ) :: field_out(:)
291  sll_real64, intent( out ) :: efield_dofs(:)
292 
293  call self%mass0_solver%solve( field_in, field_out )
294  call self%multiply_g( field_out, efield_dofs )
295  efield_dofs = -efield_dofs
296 
297  end subroutine sll_s_compute_phi_from_rho_3d_fem
298 
299 
301  subroutine sll_s_compute_phi_from_j_3d_fem( self, field_in, field_out, efield_dofs )
302  class(sll_t_maxwell_3d_fem) :: self
303  sll_real64, intent( in ) :: field_in(:)
304  sll_real64, intent( inout ) :: field_out(:)
305  sll_real64, intent( out ) :: efield_dofs(:)
306 
307  self%work0 = 0._f64
308  self%work = 0._f64
309  call self%multiply_gt( field_in, self%work0 )
310  call self%mass0_solver%solve( self%work0, self%work(1:self%n_total) )
311  field_out = field_out + self%work(1:self%n_total)
312 
313  call self%multiply_g( field_out, efield_dofs )
314  efield_dofs = -efield_dofs
315 
316  end subroutine sll_s_compute_phi_from_j_3d_fem
317 
318 
321  subroutine sll_s_compute_rhs_fem(self, form, component, coefs_dofs, func1, func2, func3)
322  class(sll_t_maxwell_3d_fem) :: self
323  sll_int32, intent( in ) :: form
324  sll_int32, intent( in ) :: component
325  sll_real64, intent( out ) :: coefs_dofs(:)
326  procedure(sll_i_function_3d_real64) :: func1
327  procedure(sll_i_function_3d_real64), optional :: func2
328  procedure(sll_i_function_3d_real64), optional :: func3
329  ! local variables
330  sll_int32 :: i1, i2, i3,j1, j2, j3, k1, k2, k3, q(3), counter
331  sll_int32 :: degree(3)
332  sll_real64 :: c(3)
333  sll_real64 :: coef
334  sll_real64, allocatable :: xw_gauss_d1(:,:), xw_gauss_d2(:,:), xw_gauss_d3(:,:)
335  sll_real64, allocatable :: bspl_d1(:,:), bspl_d2(:,:), bspl_d3(:,:)
336  sll_real64 :: scratch(maxval(self%s_deg_0+1))
337 
338  ! Define the spline degree in the 3 dimensions, depending on form and component of the form
339  if ( form == 0 ) then
340  degree = self%s_deg_0
341  elseif (form == 1 ) then
342  degree = self%s_deg_0
343  degree(component) = self%s_deg_1(component)
344  elseif( form == 2) then
345  degree = self%s_deg_1
346  degree(component) = self%s_deg_0(component)
347  elseif( form == 3) then
348  degree = self%s_deg_1
349  else
350  print*, 'Wrong form.'
351  end if
352 
353  ! take enough Gauss points so that projection is exact for splines of degree deg
354  q = degree+1
355  ! rescale on [0,1] for compatibility with B-splines
356  allocate(xw_gauss_d1(2,q(1)))
357  allocate(bspl_d1(q(1), degree(1)+1))
358  xw_gauss_d1 = sll_f_gauss_legendre_points_and_weights(q(1), 0.0_f64, 1.0_f64)
359  ! Compute bsplines at gauss_points
360  do k1 = 1, q(1)
361  call sll_s_uniform_bsplines_eval_basis(degree(1),xw_gauss_d1(1,k1), scratch(1:degree(1)+1))
362  bspl_d1(k1,:) = scratch(1:degree(1)+1)
363  end do
364 
365  allocate(xw_gauss_d2(2,q(2)))
366  allocate(bspl_d2(q(2), degree(2)+1))
367  xw_gauss_d2 = sll_f_gauss_legendre_points_and_weights(q(2), 0.0_f64, 1.0_f64)
368  ! Compute bsplines at gauss_points
369  do k2 = 1, q(2)
370  call sll_s_uniform_bsplines_eval_basis(degree(2),xw_gauss_d2(1,k2), scratch(1:degree(2)+1))
371  bspl_d2(k2,:) = scratch(1:degree(2)+1)
372  end do
373 
374  allocate(xw_gauss_d3(2,q(3)))
375  allocate(bspl_d3(q(3), degree(3)+1))
376  xw_gauss_d3 = sll_f_gauss_legendre_points_and_weights(q(3), 0.0_f64, 1.0_f64)
377  ! Compute bsplines at gauss_points
378  do k3 = 1, q(3)
379  call sll_s_uniform_bsplines_eval_basis(degree(3),xw_gauss_d3(1,k3), scratch(1:degree(3)+1))
380  bspl_d3(k3,:) = scratch(1:degree(3)+1)
381  end do
382 
383  counter = 1
384  ! Compute coefs_dofs = int f(x)N_i(x)
385  do i3 = 1, self%n_dofs(3)
386  do i2 = 1, self%n_dofs(2)
387  do i1 = 1, self%n_dofs(1)
388  coef=0.0_f64
389  ! loop over support of B spline
390  do j3 = 1, degree(3)+1
391  do j2 = 1, degree(2)+1
392  do j1 = 1, degree(1)+1
393  ! loop over Gauss points
394  do k3 = 1, q(3)
395  c(3) = self%delta_x(3)*(xw_gauss_d3(1,k3) + real(i3 + j3 - 2,f64))
396  do k2 = 1, q(2)
397  c(2) = self%delta_x(2)*(xw_gauss_d2(1,k2) + real(i2 + j2 - 2,f64))
398  do k1 = 1, q(1)
399  c(1) = self%delta_x(1)*(xw_gauss_d1(1,k1) + real(i1 + j1 - 2,f64))
400  coef = coef + xw_gauss_d1(2,k1)* xw_gauss_d2(2,k2)* &
401  xw_gauss_d3(2,k3) *&
402  func1( c ) * &
403  bspl_d1(k1,degree(1)+2-j1)*&
404  bspl_d2(k2,degree(2)+2-j2)*&
405  bspl_d3(k3,degree(3)+2-j3)
406 
407  enddo
408  enddo
409  end do
410  end do
411  end do
412  end do
413  ! rescale by cell size
414  coefs_dofs(counter) = coef*self%volume
415  counter = counter+1
416  enddo
417  end do
418  end do
419 
420  end subroutine sll_s_compute_rhs_fem
421 
422 
424  subroutine l2projection_3d_fem(self, form, component, coefs_dofs, func1, func2, func3 )
425  class(sll_t_maxwell_3d_fem) :: self
426  sll_int32, intent( in ) :: form
427  sll_int32, intent( in ) :: component
428  sll_real64, intent( out ) :: coefs_dofs(:)
429  procedure(sll_i_function_3d_real64) :: func1
430  procedure(sll_i_function_3d_real64), optional :: func2
431  procedure(sll_i_function_3d_real64), optional :: func3
432 
433  ! Compute right-hand-side
434  call sll_s_compute_rhs_fem( self, form, component, self%work(1:self%n_total), func1 )
435 
436  select case( form )
437  case( 0 )
438  call self%mass0_solver%solve( self%work(1:self%n_total), coefs_dofs )
439  case( 1 )
440  call self%mass_1_solver(component)%solve( self%work(1:self%n_total), coefs_dofs )
441  case( 2 )
442  call self%mass_2_solver(component)%solve( self%work(1:self%n_total), coefs_dofs )
443  case default
444  print*, 'L2projection for', form, '-form not implemented.'
445  end select
446 
447  end subroutine l2projection_3d_fem
448 
449  subroutine compute_dofs(self, form, component, coefs_dofs, func1, func2, func3 )
450  class(sll_t_maxwell_3d_fem) :: self
451  sll_int32, intent( in ) :: form
452  sll_int32, intent( in ) :: component
453  sll_real64, intent( out ) :: coefs_dofs(:)
454  procedure(sll_i_function_3d_real64) :: func1
455  procedure(sll_i_function_3d_real64), optional :: func2
456  procedure(sll_i_function_3d_real64), optional :: func3
457 
458  select case( form )
459  case( 0 )
460  !call sll_compute_point_integral( self, func1, coefs_dofs )
461  case( 1 )
462  call sll_compute_edge_integral( self, func1, func2, func3, coefs_dofs )
463  case( 2 )
464  !call sll_compute_face_integral( self, func1, func2, func3, coefs_dofs )
465  case default
466  print*, 'projection for', form, '-form not implemented.'
467  end select
468 
469  end subroutine compute_dofs
470 
471  subroutine sll_compute_edge_integral( self, func1, func2, func3, coefs_dofs )
472  class(sll_t_maxwell_3d_fem) :: self
473  procedure(sll_i_function_3d_real64) :: func1
474  procedure(sll_i_function_3d_real64) :: func2
475  procedure(sll_i_function_3d_real64) :: func3
476  sll_real64, intent( out ) :: coefs_dofs(:)
477  !local variables
478  sll_int32 :: i1, i2, i3, j, q, ind
479  sll_real64 :: c(3), e(3)
480  sll_real64, allocatable :: xw_gauss(:,:)
481 
482  q = 4
483  ! rescale on [0,1] for compatibility with B-splines
484  allocate(xw_gauss(2,q))
485  xw_gauss = sll_f_gauss_legendre_points_and_weights(q, 0.0_f64, 1.0_f64)
486 
487  ind = 0
488  do i3 = 1, self%n_dofs(3)
489  c(3) = self%delta_x(3)* real(i3-1,f64)
490  do i2 = 1, self%n_dofs(2)
491  c(2) = self%delta_x(2)* real(i2-1,f64)
492  do i1 = 1, self%n_dofs(1)
493  c(1) = self%delta_x(1)* real(i1-1,f64)
494  ind = ind +1 !i + (j-1)*self%n_dofs(1) + (k-1)*self%n_dofs(1)*self%n_dofs(2)
495  do j = 1, q
496  e(1) = self%delta_x(1)* (xw_gauss(1,q) + real(i1-1,f64) )
497  e(2) = self%delta_x(2)* (xw_gauss(1,q) + real(i2-1,f64) )
498  e(3) = self%delta_x(3)* (xw_gauss(1,q) + real(i3-1,f64) )
499  coefs_dofs(ind) = coefs_dofs(ind) + self%delta_x(1)*xw_gauss(2,j)*c(1)*func1([e(1),c(2),c(3)])
500  coefs_dofs(ind+self%n_total) = coefs_dofs(ind+self%n_total) + self%delta_x(2)*xw_gauss(2,j)*c(2)*func2([c(1),e(2),c(3)])
501  coefs_dofs(ind+2*self%n_total) = coefs_dofs(ind+2*self%n_total) + self%delta_x(3)*xw_gauss(2,j)*c(3)*func3([c(1),c(2),e(3)])
502  end do
503  end do
504  end do
505  end do
506 
507  end subroutine sll_compute_edge_integral
508 
509 
511  function l2norm_squared_3d_fem( self, coefs, form, component) result (r)
512  class(sll_t_maxwell_3d_fem) :: self
513  sll_real64 :: coefs(:)
514  sll_int32 :: form
515  sll_int32 :: component
516  sll_real64 :: r
517 
518 
519  r = inner_product_3d_fem(self, coefs, coefs, form, component)
520 
521 
522  end function l2norm_squared_3d_fem
523 
524 
526  function inner_product_3d_fem( self, coefs1, coefs2, form, component ) result ( r )
527  class(sll_t_maxwell_3d_fem) :: self
528  sll_real64 :: coefs1(:)
529  sll_real64 :: coefs2(:)
530  sll_int32 :: form
531  sll_int32, optional :: component
532  sll_real64 :: r
533  !local variables
534  sll_int32 :: deg(3)
535 
536  if ( form == 0 ) then
537  !deg = 0
538  call self%multiply_mass( [0], coefs2, self%work0 )
539  elseif (form == 1 ) then
540  deg = 1
541  deg(component) = 2
542  call multiply_mass_3dkron( self, deg, coefs2, self%work0 )
543  elseif( form == 2) then
544  deg = 2
545  deg(component) = 1
546  call multiply_mass_3dkron( self, deg, coefs2, self%work0 )
547  elseif( form == 3) then
548  deg = 2
549  call multiply_mass_3dkron( self, deg, coefs2, self%work0 )
550  else
551  print*, 'Wrong form.'
552  end if
553 
554 
555  r = sum(coefs1*self%work0)
556 
557  end function inner_product_3d_fem
558 
559 
561  subroutine init_3d_fem( self, domain, n_dofs, s_deg_0, mass_tolerance, poisson_tolerance, solver_tolerance, adiabatic_electrons, profile )
562  class(sll_t_maxwell_3d_fem), intent(inout) :: self
563  sll_real64, intent(in) :: domain(3,2)
564  sll_int32, intent(in) :: n_dofs(3)
565  sll_int32, intent(in) :: s_deg_0(3)
566  sll_real64, intent(in), optional :: mass_tolerance
567  sll_real64, intent(in), optional :: poisson_tolerance
568  sll_real64, intent(in), optional :: solver_tolerance
569  logical, intent(in), optional :: adiabatic_electrons
570  type(sll_t_profile_functions), intent(in), optional :: profile
571  ! local variables
572  sll_int32 :: j,k
573  sll_real64, allocatable :: mass_line_0(:), mass_line_1(:), mass_line_mixed(:)
574  sll_real64, allocatable :: nullspace(:,:)
575 
576  if (present( mass_tolerance) ) then
577  self%mass_solver_tolerance = mass_tolerance
578  else
579  self%mass_solver_tolerance = 1d-12
580  end if
581  if (present( poisson_tolerance) ) then
582  self%poisson_solver_tolerance = poisson_tolerance
583  else
584  self%poisson_solver_tolerance = 1d-12
585  end if
586 
587  if (present( solver_tolerance) ) then
588  self%solver_tolerance = solver_tolerance
589  else
590  self%solver_tolerance = 1d-12
591  end if
592 
593  if( present( adiabatic_electrons ) ) then
594  self%adiabatic_electrons = adiabatic_electrons
595  end if
596 
597  if( present( profile ) ) then
598  self%profile = profile
599  end if
600 
601  self%n_cells = n_dofs
602  self%n_dofs = n_dofs
603  self%n_total = product(n_dofs)
604  self%n_total0 = self%n_total
605  self%n_total1 = self%n_total
606 
607  self%Lx = domain(:,2) - domain(:,1)
608  self%delta_x = self%Lx / real(n_dofs, f64)
609  self%s_deg_0 = s_deg_0
610  self%s_deg_1 = s_deg_0 - 1
611 
612  self%volume = product(self%delta_x)
613 
614  ! Allocate scratch data
615  allocate( self%work3d(n_dofs(1), n_dofs(2), n_dofs(3)) )
616  allocate( self%work0(self%n_total) )
617  allocate( self%work(self%n_total*3) )
618  allocate( self%work2(self%n_total*3) )
619  allocate( self%work_d1( n_dofs(1) ) )
620  allocate( self%work_d2_in( n_dofs(2) ) )
621  allocate( self%work_d2_out( n_dofs(2) ) )
622  allocate( self%work_d3_in( n_dofs(3) ) )
623  allocate( self%work_d3_out( n_dofs(3) ) )
624 
625  ! Sparse matrices
626  ! Assemble the mass matrices
627  ! First assemble a mass line for both degrees
628  ! Next put together the 1d parts of the 3d Kronecker product
629  do j=1, 3
630  allocate( mass_line_0(s_deg_0(j)+1) )
631  allocate( mass_line_1(s_deg_0(j)) )
632  allocate( mass_line_mixed(s_deg_0(j)*2) )
633  call sll_s_spline_fem_mass_line ( self%s_deg_0(j), mass_line_0 )
634  call sll_s_spline_fem_mass_line ( self%s_deg_1(j), mass_line_1 )
635 
636  call sll_s_spline_fem_mixedmass_line ( self%s_deg_0(j), mass_line_mixed )
637 
638 
639  call sll_s_spline_fem_mass1d( self%n_dofs(j), self%s_deg_0(j), mass_line_0, self%mass1d(1,j) )
640  call sll_s_spline_fem_mass1d( self%n_dofs(j), self%s_deg_1(j), mass_line_1, self%mass1d(2,j) )
641  self%mass1d(1,j)%arr_a = self%mass1d(1,j)%arr_a*self%delta_x(j)
642  self%mass1d(2,j)%arr_a = self%mass1d(2,j)%arr_a*self%delta_x(j)
643  call self%mass1d_solver(1,j)%create( self%mass1d(1,j) )
644  call self%mass1d_solver(2,j)%create( self%mass1d(2,j) )
645 
646  call sll_s_spline_fem_mixedmass1d( self%n_dofs(j), self%s_deg_0(j), mass_line_mixed*self%delta_x(j), &
647  self%mass1d(3,j) )
648 
649  deallocate( mass_line_0 )
650  deallocate( mass_line_1 )
651  deallocate( mass_line_mixed )
652  end do
653 
654  do j=1,3
655  do k=1,2
656  self%mass1d_solver(k,j)%atol = self%mass_solver_tolerance
657  !self%mass1d_solver(k,j)%verbose = .true.
658  end do
659  end do
660 
661  ! Put together the componentwise matrices as Kronecker products
662  call self%mass_1_solver(1)%create( linear_solver_a=self%mass1d_solver(2,1), &
663  linear_solver_b=self%mass1d_solver(1,2), &
664  linear_solver_c=self%mass1d_solver(1,3) )
665  call self%mass_1_solver(2)%create( linear_solver_a=self%mass1d_solver(1,1), &
666  linear_solver_b=self%mass1d_solver(2,2), &
667  linear_solver_c=self%mass1d_solver(1,3) )
668  call self%mass_1_solver(3)%create( linear_solver_a=self%mass1d_solver(1,1), &
669  linear_solver_b=self%mass1d_solver(1,2), &
670  linear_solver_c=self%mass1d_solver(2,3))
671  call self%mass_2_solver(1)%create( linear_solver_a=self%mass1d_solver(1,1), &
672  linear_solver_b=self%mass1d_solver(2,2), &
673  linear_solver_c=self%mass1d_solver(2,3) )
674  call self%mass_2_solver(2)%create( linear_solver_a=self%mass1d_solver(2,1), &
675  linear_solver_b=self%mass1d_solver(1,2), &
676  linear_solver_c=self%mass1d_solver(2,3) )
677  call self%mass_2_solver(3)%create( linear_solver_a=self%mass1d_solver(2,1), &
678  linear_solver_b=self%mass1d_solver(2,2), &
679  linear_solver_c=self%mass1d_solver(1,3) )
680 
681 
682  if(self%adiabatic_electrons) then
683  call sll_s_spline_fem_mass3d( self%n_dofs, s_deg_0, -1, self%mass0, profile_m0 )
684  else
685  call sll_s_spline_fem_mass3d( self%n_dofs, s_deg_0, 0, self%mass0, profile_0 )
686  end if
687  call self%mass0_solver%create( self%mass0 )
688  self%mass0_solver%atol = self%mass_solver_tolerance
689  !self%mass0_solver%verbose = .true.
690 
691  call self%mass1(1)%create( linop_a=self%mass1d(1,3), &
692  linop_b=self%mass1d(1,2), &
693  linop_c=self%mass1d(2,1) )
694  call self%mass1(2)%create( linop_a=self%mass1d(1,3), &
695  linop_b=self%mass1d(2,2), &
696  linop_c=self%mass1d(1,1) )
697  call self%mass1(3)%create( linop_a=self%mass1d(2,3), &
698  linop_b=self%mass1d(1,2), &
699  linop_c=self%mass1d(1,1))
700 
701  call self%mass2(1)%create( linop_a=self%mass1d(2,3), &
702  linop_b=self%mass1d(2,2), &
703  linop_c=self%mass1d(1,1) )
704  call self%mass2(2)%create( linop_a=self%mass1d(2,3), &
705  linop_b=self%mass1d(1,2), &
706  linop_c=self%mass1d(2,1) )
707  call self%mass2(3)%create( linop_a=self%mass1d(1,3), &
708  linop_b=self%mass1d(2,2), &
709  linop_c=self%mass1d(2,1))
710 
711  call self%mass1_operator%create( 3, 3 )
712  call self%mass2_operator%create( 3, 3 )
713  do j= 1, 3
714  call self%mass1_operator%set( j, j, self%mass1(j) )
715  call self%mass2_operator%set( j, j, self%mass2(j) )
716  end do
717  call self%preconditioner_fft%init( self%Lx, n_dofs, s_deg_0 )
718  call self%mass1_solver%create( self%mass1_operator, self%preconditioner_fft%inverse_mass1_3d)
719  self%mass1_solver%atol = self%mass_solver_tolerance
720  !self%mass1_solver%verbose = .true.
721 
722  call self%poisson_matrix%create( self%mass1_operator, self%n_dofs, self%delta_x )
723  ! Penalized Poisson operator
724  allocate(nullspace(1,1:3*self%n_total))
725  nullspace(1,:) = 1.0_f64
726  call self%poisson_operator%create( linear_operator=self%poisson_matrix, vecs=nullspace(:,1:self%n_total), n_dim_nullspace=1 )
727  ! Poisson solver
728  call self%poisson_solver%create( self%poisson_operator )
729  self%poisson_solver%null_space = .true.
730  self%poisson_solver%atol = self%poisson_solver_tolerance
731  !self%poisson_solver%verbose = .true.
732  self%poisson_solver%n_maxiter=40000
733 
734 
735  ! Only for Schur complement eb solver
736  call self%linear_op_schur_eb%create( self%mass1_operator, self%mass2_operator, self%n_total, self%n_dofs, self%delta_x )
737  call self%linear_solver_schur_eb%create( self%linear_op_schur_eb, self%preconditioner_fft%inverse_mass1_3d )
738  self%linear_solver_schur_eb%atol = self%solver_tolerance
739  !self%linear_solver_schur_eb%verbose = .true.
740 
741 
742  contains
743  function profile_m0( x, component)
744  sll_real64 :: profile_m0
745  sll_real64, intent(in) :: x(3)
746  sll_int32, optional, intent(in) :: component(:)
747 
748  profile_m0 = product(self%Lx) * self%profile%rho_0( x(1) )/self%profile%T_e( x(1) )
749 
750  end function profile_m0
751 
752  function profile_0( x, component)
753  sll_real64 :: profile_0
754  sll_real64, intent(in) :: x(3)
755  sll_int32, optional, intent(in) :: component(:)
756 
757  profile_0 = product(self%Lx)
758 
759  end function profile_0
760 
761 
762  end subroutine init_3d_fem
763 
764 
766  subroutine init_from_file_3d_fem( self, domain, n_dofs, s_deg_0, nml_file, adiabatic_electrons, profile )
767  class(sll_t_maxwell_3d_fem), intent(inout) :: self
768  sll_real64, intent(in) :: domain(3,2)
769  sll_int32, intent(in) :: n_dofs(3)
770  sll_int32, intent(in) :: s_deg_0(3)
771  character(len=*), intent(in) :: nml_file
772  logical, intent(in), optional :: adiabatic_electrons
773  type(sll_t_profile_functions), intent(in), optional :: profile
774  ! local variables
775  character(len=256) :: file_prefix
776  sll_int32 :: input_file
777  sll_int32 :: io_stat, io_stat0, io_stat1, rank, file_id
778  sll_real64 :: mass_tolerance
779  sll_real64 :: poisson_tolerance
780  sll_real64 :: maxwell_tolerance
781 
782  namelist /output/ file_prefix
783  namelist /maxwell_solver/ mass_tolerance, poisson_tolerance
784 
785  namelist /time_solver/ maxwell_tolerance
786 
788 
789  if( present( adiabatic_electrons ) ) then
790  self%adiabatic_electrons = adiabatic_electrons
791  end if
792 
793  if( present( profile ) ) then
794  self%profile = profile
795  end if
796 
797  ! Read in solver tolerance
798  open(newunit = input_file, file=nml_file, status='old',iostat=io_stat)
799  if (io_stat /= 0) then
800  if (rank == 0 ) then
801  print*, 'sll_m_maxwell_3d_fem: Input file does not exist. Set default tolerance.'
802  open(newunit=file_id, file=trim(file_prefix)//'_parameters_used.dat', position = 'append', status='old', action='write', iostat=io_stat)
803  write(file_id, *) 'mass solver tolerance:', 1d-12
804  write(file_id, *) 'poisson solver tolerance:', 1d-12
805  close(file_id)
806  end if
807  call self%init( domain, n_dofs, s_deg_0 )
808  else
809  read(input_file, output,iostat=io_stat0)
810  read(input_file, maxwell_solver,iostat=io_stat)
811  read(input_file, time_solver,iostat=io_stat1)
812  if (io_stat /= 0) then
813  if (rank == 0 ) then
814  print*, 'sll_m_maxwell_3d_fem: Input parameter does not exist. Set default tolerance.'
815  open(newunit=file_id, file=trim(file_prefix)//'_parameters_used.dat', position = 'append', status='old', action='write', iostat=io_stat)
816  write(file_id, *) 'mass solver tolerance:', 1d-12
817  write(file_id, *) 'poisson solver tolerance:', 1d-12
818  close(file_id)
819  end if
820  call self%init( domain, n_dofs, s_deg_0 )
821  else
822  if (rank == 0 ) then
823  open(newunit=file_id, file=trim(file_prefix)//'_parameters_used.dat', position = 'append', status='old', action='write', iostat=io_stat)
824  write(file_id, *) 'mass solver tolerance:', mass_tolerance
825  write(file_id, *) 'poisson solver tolerance:', poisson_tolerance
826  close(file_id)
827  end if
828  call self%init( domain, n_dofs, s_deg_0, mass_tolerance, poisson_tolerance, maxwell_tolerance )
829  end if
830  close(input_file)
831  end if
832 
833  end subroutine init_from_file_3d_fem
834 
835 
837  subroutine free_3d_fem(self)
838  class(sll_t_maxwell_3d_fem) :: self
839  !local variable
840  sll_int32 :: j
841 
842  !call self%poisson_fft%free()
843  call self%poisson_solver%free()
844  call self%poisson_operator%free()
845  call self%poisson_matrix%free
846  do j=1, 3
847  call self%mass_1_solver(j)%free()
848  call self%mass_2_solver(j)%free()
849  call self%mass1d_solver(1,j)%free()
850  call self%mass1d_solver(2,j)%free()
851  call self%mass1d(1,j)%free()
852  call self%mass1d(2,j)%free()
853  call self%mass1d(3,j)%free()
854  end do
855  call self%mass1_solver%free()
856  call self%mass1_operator%free()
857  call self%mass2_operator%free()
858  do j=1, 3
859  call self%mass1(j)%free()
860  call self%mass2(j)%free()
861  end do
862 
863  call self%linear_solver_schur_eb%free()
864  call self%linear_op_schur_eb%free()
865 
866  deallocate( self%work3d )
867  deallocate( self%work0 )
868  deallocate( self%work )
869  deallocate( self%work2 )
870  deallocate( self%work_d1 )
871  deallocate( self%work_d2_in )
872  deallocate( self%work_d2_out )
873  deallocate( self%work_d3_in )
874  deallocate( self%work_d3_out )
875 
876  end subroutine free_3d_fem
877 
878 
880  subroutine multiply_g( self, field_in, field_out )
881  class(sll_t_maxwell_3d_fem) :: self
882  sll_real64, intent( in ) :: field_in(:)
883  sll_real64, intent( out ) :: field_out(:)! G*field_in
884 
885  call sll_s_multiply_g(self%n_dofs, self%delta_x, field_in, field_out)
886 
887  end subroutine multiply_g
888 
889 
891  subroutine multiply_gt(self, field_in, field_out)
892  class(sll_t_maxwell_3d_fem) :: self
893  sll_real64, intent( in ) :: field_in(:)
894  sll_real64, intent( out ) :: field_out(:)
895 
896  call sll_s_multiply_gt(self%n_dofs, self%delta_x, field_in, field_out)
897 
898  end subroutine multiply_gt
899 
900 
902  subroutine multiply_c(self, field_in, field_out)
903  class(sll_t_maxwell_3d_fem) :: self
904  sll_real64, intent( in ) :: field_in(:)
905  sll_real64, intent( out ) :: field_out(:)
906 
907  call sll_s_multiply_c(self%n_dofs, self%delta_x, field_in, field_out)
908 
909  end subroutine multiply_c
910 
911 
913  subroutine multiply_ct(self, field_in, field_out)
914  class(sll_t_maxwell_3d_fem) :: self
915  sll_real64, intent( in ) :: field_in(:)
916  sll_real64, intent( out ) :: field_out(:)
917 
918  call sll_s_multiply_ct(self%n_dofs, self%delta_x, field_in, field_out)
919 
920  end subroutine multiply_ct
921 
922 
924  subroutine multiply_mass_3d_fem( self, deg, coefs_in, coefs_out )
925  class(sll_t_maxwell_3d_fem) :: self
926  sll_int32, intent( in ) :: deg(:)
927  sll_real64, intent( in ) :: coefs_in(:)
928  sll_real64, intent( out ) :: coefs_out(:)
929 
930 
931  if( size(deg) ==1 )then
932  select case(deg(1))
933  case(0)
934  call self%mass0%dot( coefs_in, coefs_out )
935  case(1)
936  call self%mass1_operator%dot( coefs_in, coefs_out )
937  case(2)
938  call self%mass2_operator%dot( coefs_in, coefs_out )
939  case default
940  print*, 'multiply mass for other form not yet implemented'
941  stop
942  end select
943  else if( size(deg) == 3 ) then
944  call multiply_mass_3dkron( self, deg, coefs_in, coefs_out )
945  end if
946 
947  end subroutine multiply_mass_3d_fem
948 
949 
951  subroutine multiply_mass_3dkron( self, deg, coefs_in, coefs_out )
952  class(sll_t_maxwell_3d_fem) :: self
953  sll_int32, intent( in ) :: deg(:)
954  sll_real64, intent( in ) :: coefs_in(:)
955  sll_real64, intent( out ) :: coefs_out(:)
956  ! Local variables
957  sll_int32 :: i,j,k,istart,iend
958 
959  if( deg(1) == 0 ) then
960  call self%mass0%dot( coefs_in, coefs_out )
961  else
962  istart = 1
963  iend = self%n_dofs(1)
964  do k=1,self%n_dofs(3)
965  do j=1,self%n_dofs(2)
966 
967  call self%mass1d(deg(1),1)%dot( coefs_in(istart:iend), self%work_d1 )
968  self%work3d(:,j,k) = self%work_d1
969  istart = iend+1
970  iend = iend + self%n_dofs(1)
971  end do
972  end do
973 
974  do k=1,self%n_dofs(3)
975  do i =1,self%n_dofs(1)
976  self%work_d2_in = self%work3d(i,:,k)
977  call self%mass1d(deg(2),2)%dot( self%work_d2_in, self%work_d2_out )
978  self%work3d(i,:,k) = self%work_d2_out
979  end do
980  end do
981 
982  istart = 1
983  do j=1,self%n_dofs(2)
984  do i =1,self%n_dofs(1)
985  self%work_d3_in = self%work3d(i,j,:)
986  call self%mass1d(deg(3),3)%dot( self%work_d3_in, self%work_d3_out )
987  do k=1,self%n_dofs(3)
988  coefs_out(istart+(k-1)*self%n_dofs(1)*self%n_dofs(2)) = self%work_d3_out(k)
989  end do
990  istart = istart +1
991  end do
992  end do
993  end if
994 
995 
996  end subroutine multiply_mass_3dkron
997 
998 
1000  subroutine multiply_mass_inverse_3dkron( self, form, coefs_in, coefs_out )
1001  class(sll_t_maxwell_3d_fem) :: self
1002  sll_int32, intent( in ) :: form
1003  sll_real64, intent( in ) :: coefs_in(:)
1004  sll_real64, intent( out ) :: coefs_out(:)
1005  ! Local variables
1006  sll_int32 :: comp, istart, iend
1007 
1008  select case(form)
1009  case(1)
1010  do comp=1,3
1011  istart = 1+(comp-1)*self%n_total
1012  iend = comp*self%n_total
1013  call self%mass_1_solver(comp)%solve( coefs_in(istart:iend), coefs_out(istart:iend) )
1014  end do
1015  case(2)
1016  do comp=1,3
1017  istart = 1+(comp-1)*self%n_total
1018  iend = comp*self%n_total
1019  call self%mass_2_solver(comp)%solve( coefs_in(istart:iend), coefs_out(istart:iend) )
1020  end do
1021  case default
1022  print*, 'multiply inverse mass for other form not yet implemented'
1023  stop
1024  end select
1025 
1026 
1027  end subroutine multiply_mass_inverse_3dkron
1028 
1029 
1031  subroutine compute_field_energy( self, efield_dofs, bfield_dofs, energy)
1032  class(sll_t_maxwell_3d_fem) :: self
1033  sll_real64, intent( in ) :: efield_dofs(:)
1034  sll_real64, intent( in ) :: bfield_dofs(:)
1035  sll_real64, intent( out ) :: energy
1036  !local variables
1037  sll_real64 :: field_energy(6)
1038 
1039 
1040  field_energy(1) = self%l2norm_squared &
1041  ( efield_dofs(1:self%n_total), 1, 1 )
1042  field_energy(2) = self%l2norm_squared &
1043  ( efield_dofs(self%n_total+1:2*self%n_total), 1, 2 )
1044  field_energy(3) = self%l2norm_squared &
1045  ( efield_dofs(2*self%n_total+1:3*self%n_total), 1, 3 )
1046  field_energy(4) = self%l2norm_squared &
1047  ( bfield_dofs(1:self%n_total), 2, 1 )
1048  field_energy(5) = self%l2norm_squared &
1049  ( bfield_dofs(self%n_total+1:2*self%n_total), 2, 2 )
1050  field_energy(6) =self%l2norm_squared &
1051  ( bfield_dofs(2*self%n_total+1:3*self%n_total), 2, 3 )
1052 
1053 
1054  energy = 0.5_f64*sum(field_energy)
1055 
1056  end subroutine compute_field_energy
1057 
1058 
1059 end module sll_m_maxwell_3d_fem
Parallelizing facility.
integer(kind=i32) function, public sll_f_get_collective_rank(col)
Determines the rank of the calling process in the communicator.
type(sll_t_collective_t), pointer, public sll_v_world_collective
Control of subset assignment. Processes with the same color are in the same new communicator.
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 [...
module for a block linear operator
module for a linear operator of kronecker solver
module for a penalized linear operator
module for conjugate gradient method in pure form
module for kronecker linear solver
Low level arbitrary degree splines.
module for Compressed Sparse Row Matrix (CSR)
Module interface to solve Maxwell's equations in 3D.
Module interface to solve Maxwell's equations in 3D The linear systems are solved using iterative lin...
subroutine multiply_ct(self, field_in, field_out)
Multiply by transpose of discrete curl matrix.
subroutine free_3d_fem(self)
Finalization.
subroutine multiply_mass_inverse_3dkron(self, form, coefs_in, coefs_out)
Multiply by the inverse mass matrix.
subroutine multiply_g(self, field_in, field_out)
Multiply by dicrete gradient matrix.
subroutine sll_compute_edge_integral(self, func1, func2, func3, coefs_dofs)
subroutine multiply_gt(self, field_in, field_out)
Multiply by transpose of dicrete gradient matrix.
real(kind=f64) function inner_product_3d_fem(self, coefs1, coefs2, form, component)
Compute inner product.
subroutine init_from_file_3d_fem(self, domain, n_dofs, s_deg_0, nml_file, adiabatic_electrons, profile)
Initialization from nml file.
subroutine sll_s_compute_e_from_j_3d_fem(self, current, E, component)
Compute E_i from j_i integrated over the time interval using weak Ampere formulation.
subroutine sll_s_compute_e_from_rho_3d_fem(self, field_in, field_out)
Compute E_i from rho_i integrated over the time interval using weak Poisson's equation.
real(kind=f64) function l2norm_squared_3d_fem(self, coefs, form, component)
Compute square of the L2norm.
subroutine sll_s_compute_b_from_e_3d_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_mass_3d_fem(self, deg, coefs_in, coefs_out)
Multiply by the mass matrix.
subroutine sll_s_compute_rhs_fem(self, form, component, coefs_dofs, func1, func2, func3)
Compute the FEM right-hand-side for a given function f and periodic splines of given degree Its compo...
subroutine sll_s_compute_phi_from_rho_3d_fem(self, field_in, field_out, efield_dofs)
Compute phi from rho_i integrated over the time interval.
subroutine sll_s_compute_curl_part_3d_fem(self, delta_t, efield, bfield, betar)
Solve curl part of Maxwell's equations.
subroutine compute_field_energy(self, efield_dofs, bfield_dofs, energy)
Compute field energy.
subroutine l2projection_3d_fem(self, form, component, coefs_dofs, func1, func2, func3)
Compute the L2 projection of a given function f on periodic splines of given degree.
subroutine sll_s_compute_rho_from_e_3d_fem(self, field_in, field_out)
compute rho from e using weak Gauss law ( rho = G^T M_1 e )
subroutine init_3d_fem(self, domain, n_dofs, s_deg_0, mass_tolerance, poisson_tolerance, solver_tolerance, adiabatic_electrons, profile)
Initialization.
subroutine sll_s_compute_phi_from_j_3d_fem(self, field_in, field_out, efield_dofs)
Compute phi from j_i integrated over the time interval, delta_t is already included.
subroutine multiply_mass_3dkron(self, deg, coefs_in, coefs_out)
Multiply by the mass matrix.
subroutine sll_s_compute_e_from_b_3d_fem(self, delta_t, field_in, field_out)
compute Ey from Bz using weak Ampere formulation
subroutine compute_dofs(self, form, component, coefs_dofs, func1, func2, func3)
subroutine multiply_c(self, field_in, field_out)
Multiply by discrete curl matrix.
Module interface to solve Maxwell's equations with coordinate transformation in 3D The linear systems...
functions for initial profile of the particle distribution function
Utilites for 3D Maxwell solvers with spline finite elements.
subroutine, public sll_s_spline_fem_mass3d(n_cells, deg, component, matrix, profile, n_cells_min, n_cells_max)
Set up 3d mass matrix for specific spline degrees and profile function.
subroutine, public sll_s_spline_fem_mixedmass3d(n_cells, deg1, deg2, component, matrix, profile, n_cells_min, n_cells_max)
Set up 3d mixed mass matrix for specific spline degree and profile function.
Utilites for Maxwell solver's with spline finite elements using sparse matrix linear solvers.
subroutine, public sll_s_spline_fem_mass1d(n_cells, s_deg, mass_line, matrix)
Set up 1d mass matrix (sparsity pattern and values)
subroutine, public sll_s_spline_fem_mixedmass1d(n_cells, s_deg, mass_line, matrix)
Utilites for Maxwell solver's with spline finite elements.
subroutine, public sll_s_multiply_g(n_dofs, delta_x, field_in, field_out)
Multiply by dicrete gradient matrix (3d version)
subroutine, public sll_s_multiply_c(n_dofs, delta_x, field_in, field_out)
Multiply by discrete curl matrix.
subroutine, public sll_s_multiply_ct(n_dofs, delta_x, field_in, field_out)
Multiply by transpose of discrete curl matrix.
subroutine, public sll_s_multiply_gt(n_dofs, delta_x, field_in, field_out)
Multiply by transpose of dicrete gradient matrix (3d version)
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.
real(kind=f64) function profile_0(x)
real(kind=f64) function profile_m0(x)
    Report Typos and Errors