PB3D  [2.45]
Ideal linear high-n MHD stability in 3-D
eq_utilities.f90
Go to the documentation of this file.
1 !------------------------------------------------------------------------------!
3 !------------------------------------------------------------------------------!
5 #include <PB3D_macros.h>
6  use str_utilities
7  use output_ops
8  use messages
9  use num_vars, only: pi, dp, max_str_ln, max_deriv
10  use grid_vars, only: grid_type
11  use eq_vars, only: eq_1_type, eq_2_type
12 #if ldebug
13  use num_utilities, only: check_deriv
14 #endif
15 
16  implicit none
17  private
20 #if ldebug
22 #endif
23 
24  ! global variables
25 #if ldebug
26 
27  logical :: debug_calc_inv_met_ind = .false.
28 #endif
29 
30  ! interfaces
31 
36  interface calc_f_derivs
38  module procedure calc_f_derivs_1
40  module procedure calc_f_derivs_2
41  end interface
42 
61  interface calc_inv_met
63  module procedure calc_inv_met_ind
65  module procedure calc_inv_met_arr
67  module procedure calc_inv_met_ind_0d
69  module procedure calc_inv_met_arr_0d
70  end interface
71 
118  interface transf_deriv
120  module procedure transf_deriv_3_ind
122  module procedure transf_deriv_3_arr
124  module procedure transf_deriv_3_arr_2d
126  module procedure transf_deriv_1_ind
127  end interface
128 
129 contains
131  integer function calc_inv_met_ind(X,Y,deriv) result(ierr)
133 #if ldebug
134  use num_vars, only: n_procs
135 #endif
136 
137  character(*), parameter :: rout_name = 'calc_inv_met_ind'
138 
139  ! input / output
140  real(dp), intent(inout) :: x(1:,1:,1:,1:,0:,0:,0:)
141  real(dp), intent(in) :: y(1:,1:,1:,1:,0:,0:,0:)
142  integer, intent(in) :: deriv(:)
143 
144  ! local variables
145  integer :: r, t, z ! counters for derivatives
146  integer :: m1, m2, m3 ! alias for deriv(i)
147  integer :: dims(3) ! dimensions of coordinates
148  real(dp) :: bin_fac(3) ! binomial factor for norm., pol. and tor. sum
149  real(dp), allocatable :: dum1(:,:,:,:) ! dummy variable representing metric matrix for some derivatives
150  real(dp), allocatable :: dum2(:,:,:,:) ! dummy variable representing metric matrix for some derivatives
151  integer :: nn ! size of matrices
152  character(len=max_str_ln) :: err_msg ! error message
153 #if ldebug
154  real(dp), allocatable :: xy(:,:,:,:) ! to check if XY is indeed unity
155 #endif
156 
157  ! initialize ierr
158  ierr = 0
159 
160  ! set nn
161  nn = size(x,4)
162 
163  ! tests
164  if (size(y,4).ne.nn) then
165  ierr = 1
166  err_msg = 'Both matrices X and Y have to be either in full or &
167  &lower diagonal storage'
168  chckerr(err_msg)
169  end if
170 
171  ! set mi
172  m1 = deriv(1)
173  m2 = deriv(2)
174  m3 = deriv(3)
175 
176  ! set dims
177  dims = [size(x,1),size(x,2),size(x,3)]
178 
179  ! initialize the requested derivative to zero
180  x(:,:,:,:,m1,m2,m3) = 0._dp
181 
182  ! set up dummy variables
183  allocate(dum1(dims(1),dims(2),dims(3),9))
184  allocate(dum2(dims(1),dims(2),dims(3),9))
185 
186  ! initialize dum2
187  dum2 = 0._dp
188 
189  ! calculate terms
190  if (m1.eq.0 .and. m2.eq.0 .and. m3.eq.0) then ! direct inverse
191  ierr = calc_inv(x(:,:,:,:,0,0,0),y(:,:,:,:,0,0,0),3)
192  chckerr('')
193 
194 #if ldebug
195  ! check if X*Y is unity indeed if debugging
196  if (debug_calc_inv_met_ind) then
197  if (n_procs.gt.1) call writo('Debugging of calc_inv_met_ind &
198  &should be done with one processor only',warning=.true.)
199  call writo('checking if X*Y = 1')
200  call lvl_ud(1)
201  allocate(xy(dims(1),dims(2),dims(3),9))
202  ierr = calc_mult(x(:,:,:,:,0,0,0),y(:,:,:,:,0,0,0),xy,3)
203  chckerr('')
204  do r = 1,3
205  do t = 1,3
206  call writo(&
207  &trim(r2strt(minval(xy(:,1,:,c([r,t],.false.)))))//&
208  &' < element ('//trim(i2str(t))//','//&
209  &trim(i2str(r))//') < '//&
210  &trim(r2strt(maxval(xy(:,1,:,c([r,t],.false.))))))
211  end do
212  end do
213  deallocate(xy)
214  call lvl_ud(-1)
215  end if
216 #endif
217  else ! calculate using the other inverses
218  do z = 0,m3 ! derivs in third coord
219  if (z.eq.0) then ! first term in sum
220  bin_fac(3) = 1.0_dp
221  else
222  bin_fac(3) = bin_fac(3)*(m3-(z-1))/z
223  end if
224  do t = 0,m2 ! derivs in second coord
225  if (t.eq.0) then ! first term in sum
226  bin_fac(2) = 1.0_dp
227  else
228  bin_fac(2) = bin_fac(2)*(m2-(t-1))/t
229  end if
230  do r = 0,m1 ! derivs in first coord
231  if (r.eq.0) then ! first term in sum
232  bin_fac(1) = 1.0_dp
233  else
234  bin_fac(1) = bin_fac(1)*(m1-(r-1))/r
235  end if
236  if (z+t+r .lt. m1+m2+m3) then ! only add if not all r,t,z are equal to m1,m2,m3
237  ! multiply X(r,t,z) and Y(m1-r,m2-t,m3-z)
238  ierr = calc_mult(x(:,:,:,:,r,t,z),&
239  &y(:,:,:,:,m1-r,m2-t,m3-z),dum1,3)
240  chckerr('')
241  ! add result, multiplied by bin_fac, to dum2
242  dum2 = dum2 - product(bin_fac)*dum1
243  end if
244  end do
245  end do
246  end do
247 
248  ! right-multiply by X(0,0,0) and save in dum1
249  ierr = calc_mult(dum2,x(:,:,:,:,0,0,0),dum1,3)
250  chckerr('')
251  ! save result in X(m1,m2,m3), converting if necessary
252  ierr = conv_mat(dum1,x(:,:,:,:,m1,m2,m3),3)
253  chckerr('')
254 
255  ! deallocate local variables
256  deallocate(dum1,dum2)
257  end if
258  end function calc_inv_met_ind
260  integer function calc_inv_met_ind_0d(X,Y,deriv) result(ierr)
261  character(*), parameter :: rout_name = 'calc_inv_met_ind_0D'
262 
263  ! input / output
264  real(dp), intent(inout) :: x(1:,1:,1:,0:,0:,0:)
265  real(dp), intent(in) :: y(1:,1:,1:,0:,0:,0:)
266  integer, intent(in) :: deriv(:)
267 
268  ! local variables
269  integer :: r, t, z ! counters for derivatives
270  integer :: m1, m2, m3 ! alias for deriv(i)
271  real(dp) :: bin_fac(3) ! binomial factor for norm., pol. and tor. sum
272 
273  ! initialize ierr
274  ierr = 0
275 
276  m1 = deriv(1)
277  m2 = deriv(2)
278  m3 = deriv(3)
279 
280  ! initialize the requested derivative
281  x(:,:,:,m1,m2,m3) = 0.0_dp
282 
283  ! calculate terms
284  if (m1.eq.0 .and. m2.eq.0 .and. m3.eq.0) then ! direct inverse
285  x(:,:,:,0,0,0) = 1._dp/y(:,:,:,0,0,0)
286  else ! calculate using the other inverses
287  do z = 0,m3 ! derivs in third coord
288  if (z.eq.0) then ! first term in sum
289  bin_fac(3) = 1.0_dp
290  else
291  bin_fac(3) = bin_fac(3)*(m3-(z-1))/z
292  ! ADD SOME ERROR CHECKING HERE !!!!
293  chckerr('')
294  end if
295  do t = 0,m2 ! derivs in second coord
296  if (t.eq.0) then ! first term in sum
297  bin_fac(2) = 1.0_dp
298  else
299  bin_fac(2) = bin_fac(2)*(m2-(t-1))/t
300  end if
301  do r = 0,m1 ! derivs in first coord
302  if (r.eq.0) then ! first term in sum
303  bin_fac(1) = 1.0_dp
304  else
305  bin_fac(1) = bin_fac(1)*(m1-(r-1))/r
306  end if
307  if (z+t+r .lt. m1+m2+m3) then ! only add if not all r,t,z are equal to m1,m2,m3
308  x(:,:,:,m1,m2,m3) = x(:,:,:,m1,m2,m3)-&
309  &bin_fac(1)*bin_fac(2)*bin_fac(3)* &
310  &x(:,:,:,r,t,z)*y(:,:,:,m1-r,m2-t,m3-z)
311  end if
312  end do
313  end do
314  end do
315 
316  ! right-multiply by X(0,0,0)
317  x(:,:,:,m1,m2,m3) = x(:,:,:,m1,m2,m3)*x(:,:,:,0,0,0)
318  end if
319  end function calc_inv_met_ind_0d
320 
321  integer function calc_inv_met_arr(X,Y,deriv) result(ierr)
322  character(*), parameter :: rout_name = 'calc_inv_met_arr'
323 
324  ! input / output
325  real(dp), intent(inout) :: x(1:,1:,1:,1:,0:,0:,0:)
326  real(dp), intent(in) :: y(1:,1:,1:,1:,0:,0:,0:)
327  integer, intent(in) :: deriv(:,:)
328 
329  ! local variables
330  integer :: id
331 
332  ! initialize ierr
333  ierr = 0
334 
335  do id = 1, size(deriv,2)
336  ierr = calc_inv_met_ind(x,y,deriv(:,id))
337  chckerr('')
338  end do
339  end function calc_inv_met_arr
340 
341  integer function calc_inv_met_arr_0d(X,Y,deriv) result(ierr)
342  character(*), parameter :: rout_name = 'calc_inv_met_arr_0D'
343 
344  ! input / output
345  real(dp), intent(inout) :: x(1:,1:,1:,0:,0:,0:)
346  real(dp), intent(in) :: y(1:,1:,1:,0:,0:,0:)
347  integer, intent(in) :: deriv(:,:)
348 
349  ! local variables
350  integer :: id
351 
352  ! initialize ierr
353  ierr = 0
354 
355  do id = 1, size(deriv,2)
356  ierr = calc_inv_met_ind_0d(x,y,deriv(:,id))
357  chckerr('')
358  end do
359  end function calc_inv_met_arr_0d
360 
362  !metric coefficients in a coordinate system A and the ! transformation
363  !matrix \f$\overline{\text{T}}_\text{B}^\text{A}\f$.
364  !!
365  !! This is based on the general treatment using the formula described in
366  !! \cite Weyens3D :
367  !! \f[ \vec{D}_1^{m_1} \vec{D}_2^{m_2} \vec{D}_3^{m_3} g_\text{B}
368  !! = \sum_1 \sum_2 \sum_3 C_1 C_2 C_3 \vec{D}^{i_1,j_1,k_1}
369  !! \overline{\text{T}}_\text{B}^\text{A}
370  !! \vec{D}^{i_2,j_2,k_2} g_\text{A} \vec{D}^{i_3,j_3,k_3}
371  !! \left(\overline{\text{T}}_\text{B}^\text{A}\right)^T \ ,
372  !! \f]
373  !! with
374  !! \f[ k_l = m_l-i_l-j_l \ , \f]
375  !! and with the \f$\sum_i\f$ double summations, with the first one going
376  !! from \f$0\f$ to \f$m_i\f$ and the second one from \f$m_j\f$ to \f$0\f$.
377  !!
378  !! The coefficients \f$C_l\f$ are calculated as
379  !! \f[
380  !! C_l = \left(\begin{array}{c}m_l\\i_l,j_l,k_l\end{array}\right)
381  !! \equiv \frac{m_l!}{i_l!j_l!(m_l-i_l-j_l)!} \ .
382  !! \f]
383  !!
384  !! \note It is assumed that the lower order derivatives have been calculated
385  !! already. If not, the results will be incorrect. This is not checked.
386  integer function calc_g(g_A,T_BA,g_B,deriv,max_deriv) result(ierr)
388 
389  character(*), parameter :: rout_name = 'calc_g'
390 
391  ! input / output
392  real(dp), intent(in) :: g_a(:,:,:,:,0:,0:,0:)
393  real(dp), intent(in) :: t_ba(:,:,:,:,0:,0:,0:)
394  real(dp), intent(inout) :: g_b(:,:,:,:,0:,0:,0:)
395  integer, intent(in) :: deriv(3)
396  integer, intent(in) :: max_deriv
397 
398  ! local variables
399  integer :: i1, j1, i2, j2, i3, j3, k1, k2, k3 ! counters
400  integer :: m1, m2, m3 ! alias for deriv
401  real(dp), allocatable :: c1(:), c2(:), c3(:) ! coeff. for (il)
402  real(dp), allocatable :: dum1(:,:,:,:) ! dummy variable representing metric matrix for some derivatives
403  real(dp), allocatable :: dum2(:,:,:,:) ! dummy variable representing metric matrix for some derivatives
404 
405  ! initialize ierr
406  ierr = 0
407 
408 #if ldebug
409  ! check the derivatives requested
410  ierr = check_deriv(deriv,max_deriv,'calc_g')
411  chckerr('')
412 #endif
413 
414  ! set ml
415  m1 = deriv(1)
416  m2 = deriv(2)
417  m3 = deriv(3)
418 
419  ! set up dummies
420  allocate(dum1(size(g_a,1),size(g_a,2),size(g_a,3),9))
421  allocate(dum2(size(g_a,1),size(g_a,2),size(g_a,3),9))
422 
423  ! initialize the requested derivative to zero
424  g_b(:,:,:,:,m1,m2,m3) = 0.0_dp
425 
426  ! calculate the terms in the summation
427  d1: do j1 = 0,m1 ! derivatives in first coordinate
428  call calc_c(m1,j1,c1) ! calculate coeff. C1
429  do i1 = m1-j1,0,-1
430  d2: do j2 = 0,m2 ! derivatives in second coordinate
431  call calc_c(m2,j2,c2) ! calculate coeff. C2
432  do i2 = m2-j2,0,-1
433  d3: do j3 = 0,m3 ! derivatives in third coordinate
434  call calc_c(m3,j3,c3) ! calculate coeff. C3
435  do i3 = m3-j3,0,-1
436  ! set up ki
437  k1 = m1 - j1 - i1
438  k2 = m2 - j2 - i2
439  k3 = m3 - j3 - i3
440  ! calculate term to add to the summation
441  ierr = calc_mult(g_a(:,:,:,:,j1,j2,j3),&
442  &t_ba(:,:,:,:,k1,k2,k3),dum1,3,&
443  &transp=[.false.,.true.])
444  chckerr('')
445  ierr = calc_mult(t_ba(:,:,:,:,i1,i2,i3),&
446  &dum1,dum2,3)
447  chckerr('')
448  ! convert result to lower-diagonal storage
449  ierr = conv_mat(dum2,dum1(:,:,:,1:6),3)
450  chckerr('')
451  g_b(:,:,:,:,m1,m2,m3) = g_b(:,:,:,:,m1,m2,m3) &
452  &+c1(i1)*c2(i2)*c3(i3)*dum1(:,:,:,1:6)
453  end do
454  end do d3
455  end do
456  end do d2
457  end do
458  end do d1
459 
460  ! deallocate local variables
461  deallocate(dum1,dum2)
462  contains
463  ! Calculate the coeff. C at the all i values for the current value for
464  ! j, optionally making use of the coeff. C at previous value for j:
465  ! - If it is the very first value (0,m), then it is just equal to 1
466  ! - If it is the first value in the current i-summation, then it is
467  ! calculated from the first coeff. of the previous i-summation:
468  ! C(j,m) = C(j-1,m) * (m-j+1)/j
469  ! - If it is not the first value in the current i-summation, then it is
470  ! calculated from the previous value of the current i-summation
471  ! C(j,i) = C(j,i+1) * (i+1)/(m-i-j)
472  ! - If it is the last value in the current i-summation, then it is
473  ! divided by 2 if (m-j) is even (see [ADD REF])
475  subroutine calc_c(m,j,C)
476  ! input / output
477  integer, intent(in) :: m, j
478  real(dp), intent(inout), allocatable :: c(:)
479 
480  ! local variables
481  integer :: i ! counter
482  real(dp) :: cm_prev
483 
484  ! if j>0, save the value Cm_prev
485  if (j.gt.0) cm_prev = c(m-j+1)
486 
487  ! allocate C
488  if (allocated(c)) deallocate (c)
489  allocate(c(0:m-j))
490 
491  ! first value i = m-j
492  if (j.eq.0) then ! first coeff., for j = 0
493  c(m) = 1.0_dp
494  else ! first coeff., for j > 0 -> need value Cm_prev from previous array
495  c(m-j) = (m-j+1.0_dp)/j * cm_prev
496  end if
497 
498  ! all other values i = m-1 .. 0
499  do i = m-j-1,0,-1
500  c(i) = (i+1.0_dp)/(m-i-j) * c(i+1)
501  end do
502  end subroutine
503  end function calc_g
504 
506  integer recursive function transf_deriv_3_ind(x_a,t_ba,x_b,max_deriv,&
507  &deriv_B,deriv_A_input) result(ierr)
509 
510  character(*), parameter :: rout_name = 'transf_deriv_3_ind'
511 
512  ! input / output
513  real(dp), intent(in) :: x_a(1:,1:,1:,0:,0:,0:)
514  real(dp), intent(in) :: t_ba(1:,1:,1:,1:,0:,0:,0:)
515  real(dp), intent(inout) :: x_b(1:,1:,1:)
516  integer, intent(in) :: max_deriv
517  integer, intent(in) :: deriv_b(:)
518  integer, intent(in), optional :: deriv_a_input(:)
519 
520  ! local variables
521  integer :: id, jd, kd, ld ! counters
522  integer :: deriv_id_b ! holds the deriv. currently calculated
523  integer, allocatable :: deriv_a(:) ! holds either deriv_A or 0
524  real(dp), allocatable :: x_b_x(:,:,:,:,:,:) ! X_B and derivs. D^p-q_A with extra 1 exchanged deriv. D_A
525  integer, allocatable :: deriv_a_x(:) ! holds A derivs. for exchanged X_B_x
526  integer, allocatable :: deriv_b_x(:) ! holds B derivs. for exchanged X_B_x
527 
528  ! initialize ierr
529  ierr = 0
530 
531  ! setup deriv_A
532  allocate(deriv_a(size(deriv_b)))
533  if (present(deriv_a_input)) then
534  deriv_a = deriv_a_input
535  else
536  deriv_a = 0
537  end if
538 
539 #if ldebug
540  ! check the derivatives requested
541  ! (every B deriv. needs all the A derivs. -> sum(deriv_B) needed)
542  ierr = check_deriv(deriv_a + sum(deriv_b),max_deriv,'transf_deriv')
543  chckerr('')
544 #endif
545 
546  ! detect first deriv. in the B coord. system that needs to be exchanged,
547  ! with derivs in the A coord. system going from B coord. 1 to B coord. 2
548  ! to B coord. 3
549  ! if equal to zero on termination, this means that no more exchange is
550  ! necessary
551  deriv_id_b = 0
552  do id = 1,3
553  if (deriv_b(id).gt.0) then
554  deriv_id_b = id
555  exit
556  end if
557  end do
558 
559  ! calculate the derivative in coord. deriv_id_B of coord. system B of
560  ! one order lower than requested here
561  ! check if we have reached the zeroth derivative in coord. B
562  if (deriv_id_b.eq.0) then ! return the function with its required A derivs.
563  x_b = x_a(:,:,:,deriv_a(1),deriv_a(2),deriv_a(3))
564  else ! apply the formula to obtain X_B using exchanged derivs.
565  ! allocate the exchanged X_B_x and the derivs. in A and B,
566  ! corresponding to D^m-1_B X
567  allocate(x_b_x(size(x_b,1),size(x_b,2),size(x_b,3),&
568  &0:deriv_a(1),0:deriv_a(2),0:deriv_a(3)))
569  allocate(deriv_a_x(size(deriv_a)))
570  allocate(deriv_b_x(size(deriv_b)))
571 
572  ! initialize X_B
573  x_b = 0.0_dp
574 
575  ! loop over the 3 terms in the sum due to the transf. mat.
576  do id = 1,3
577  ! calculate X_B_x for orders 0 up to deriv_A
578  do jd = 0,deriv_a(1)
579  do kd = 0,deriv_a(2)
580  do ld = 0,deriv_a(3)
581  ! calculate the derivs. in A for X_B_x: for every
582  ! component in the sum due to the transf. mat., the
583  ! deriv. is one order higher than [jd,kd,ld] at the
584  ! corresponding coord. id
585  deriv_a_x = [jd,kd,ld]
586  deriv_a_x(id) = deriv_a_x(id) + 1
587 
588  ! calculate the derivs. in B for X_B_x: this is one
589  ! order lower in the coordinate deriv_id_B
590  deriv_b_x = deriv_b
591  deriv_b_x(deriv_id_b) = deriv_b_x(deriv_id_b) - 1
592 
593  ! recursively call the subroutine again to calculate
594  ! X_B_x for the current derivatives
595  ierr = transf_deriv_3_ind(x_a,t_ba,&
596  &x_b_x(:,:,:,jd,kd,ld),max_deriv,deriv_b_x,&
597  &deriv_a_x)
598  chckerr('')
599  end do
600  end do
601  end do
602 
603  ! use X_B_x at this term in summation due to the transf. mat. to
604  ! update X_B using the formula
605  ierr = add_arr_mult(&
606  &t_ba(:,:,:,c([deriv_id_b,id],.false.),0:,0:,0:),&
607  &x_b_x(:,:,:,0:,0:,0:),x_b,deriv_a)
608  chckerr('')
609  end do
610  end if
611  end function transf_deriv_3_ind
613  integer function transf_deriv_3_arr(X_A,T_BA,X_B,max_deriv,derivs) &
614  &result(ierr)
615  character(*), parameter :: rout_name = 'transf_deriv_3_arr'
616 
617  ! input / output
618  real(dp), intent(in) :: x_a(1:,1:,1:,0:,0:,0:)
619  real(dp), intent(in) :: t_ba(1:,1:,1:,1:,0:,0:,0:)
620  real(dp), intent(inout) :: x_b(1:,1:,1:,0:,0:,0:)
621  integer, intent(in) :: max_deriv
622  integer, intent(in) :: derivs(:,:)
623 
624  ! local variables
625  integer :: id ! counter
626 
627  ! initialize ierr
628  ierr = 0
629 
630  do id = 1, size(derivs,2)
631  ierr = transf_deriv_3_ind(x_a,t_ba,&
632  &x_b(:,:,:,derivs(1,id),derivs(2,id),derivs(3,id)),&
633  &max_deriv,derivs(:,id))
634  chckerr('')
635  end do
636  end function transf_deriv_3_arr
638  integer function transf_deriv_3_arr_2d(X_A,T_BA,X_B,max_deriv,derivs) &
639  &result(ierr)
640  use num_utilities, only: is_sym, c
641 
642  character(*), parameter :: rout_name = 'transf_deriv_3_arr_2D'
643 
644  ! input / output
645  real(dp), intent(in) :: x_a(1:,1:,1:,1:,0:,0:,0:)
646  real(dp), intent(in) :: t_ba(1:,1:,1:,1:,0:,0:,0:)
647  real(dp), intent(inout) :: x_b(1:,1:,1:,1:,0:,0:,0:)
648  integer, intent(in) :: max_deriv
649  integer, intent(in) :: derivs(:,:)
650 
651  ! local variables
652  integer :: id, kd ! counters
653  character(len=max_str_ln) :: err_msg ! error message
654  logical :: sym ! sym
655 
656  ! initialize ierr
657  ierr = 0
658 
659  ! test whether the dimensions of X_A and X_B agree
660  if (size(x_a,4).ne.size(x_b,4)) then
661  err_msg = 'X_A and X_B need to have the same sizes'
662  ierr = 1
663  chckerr(err_msg)
664  end if
665 
666  ierr = is_sym(3,size(x_a,4),sym)
667  chckerr('')
668 
669  do kd = 1,size(x_a,4)
670  do id = 1, size(derivs,2)
671  ierr = transf_deriv_3_ind(x_a(:,:,:,kd,:,:,:),t_ba,&
672  &x_b(:,:,:,kd,derivs(1,id),derivs(2,id),derivs(3,id)),&
673  &max_deriv,derivs(:,id))
674  chckerr('')
675  end do
676  end do
677  end function transf_deriv_3_arr_2d
679  integer recursive function transf_deriv_1_ind(x_a,t_ba,x_b,max_deriv,&
680  &deriv_B,deriv_A_input) result(ierr)
681  use num_utilities, only: add_arr_mult
682 
683  character(*), parameter :: rout_name = 'transf_deriv_1_ind'
684 
685  ! input / output
686  real(dp), intent(in) :: x_a(1:,0:)
687  real(dp), intent(inout) :: x_b(1:)
688  real(dp), intent(in) :: t_ba(1:,0:)
689  integer, intent(in), optional :: deriv_a_input
690  integer, intent(in) :: deriv_b
691  integer, intent(in) :: max_deriv
692 
693  ! local variables
694  integer :: jd ! counters
695  integer :: deriv_a ! holds either deriv_A or 0
696  real(dp), allocatable :: x_b_x(:,:) ! X_B and derivs. D^p-q_A with extra 1 exchanged deriv. D_A
697  integer :: deriv_a_x ! holds A derivs. for exchanged X_B_x
698  integer :: deriv_b_x ! holds B derivs. for exchanged X_B_x
699 
700  ! initialize ierr
701  ierr = 0
702 
703  ! setup deriv_A
704  if (present(deriv_a_input)) then
705  deriv_a = deriv_a_input
706  else
707  deriv_a = 0
708  end if
709 
710 #if ldebug
711  ! check the derivatives requested
712  ! (every B deriv. needs all the A derivs. -> sum(deriv_B) needed)
713  ierr = check_deriv([deriv_a+deriv_b,0,0],max_deriv,'transf_deriv')
714  chckerr('')
715 #endif
716 
717  ! calculate the derivative in coord. deriv_id_B of coord. system B of
718  ! one order lower than requested here
719  ! check if we have reached the zeroth derivative in coord. B
720  if (deriv_b.eq.0) then ! return the function with its required A derivs.
721  x_b = x_a(:,deriv_a)
722  else ! apply the formula to obtain X_B using exchanged derivs.
723  ! allocate the exchanged X_B_x and the derivs. in A and B,
724  ! corresponding to D^m-1_B X
725  allocate(x_b_x(size(x_b,1),0:deriv_a))
726 
727  ! initialize X_B
728  x_b = 0.0_dp
729 
730  ! calculate X_B_x for orders 0 up to deriv_A
731  do jd = 0,deriv_a
732  ! calculate the derivs. in A for X_B_x: for every component in
733  ! the sum due to the transf. mat., the deriv. is one order
734  ! higher than jd
735  deriv_a_x = jd+1
736 
737  ! calculate the derivs. in B for X_B_x: this is one order lower
738  deriv_b_x = deriv_b-1
739 
740  ! recursively call the subroutine again to calculate X_B_x for
741  ! the current derivatives
742  ierr = transf_deriv_1_ind(x_a,t_ba,x_b_x(:,jd),max_deriv,&
743  &deriv_b_x,deriv_a_input=deriv_a_x)
744  chckerr('')
745  end do
746 
747  ! use X_B_x to calculate X_B using the formula
748  ierr = add_arr_mult(t_ba(:,0:),x_b_x(:,0:),x_b,[deriv_a,0,0])
749  chckerr('')
750  end if
751  end function transf_deriv_1_ind
752 
754  integer function calc_f_derivs_1(grid_eq,eq) result(ierr)
756  use num_utilities, only: derivs, c, fac
757  use eq_vars, only: max_flux_e
758 
759  character(*), parameter :: rout_name = 'calc_F_derivs_1'
760 
761  ! input / output
762  type(grid_type), intent(in) :: grid_eq
763  type(eq_1_type), intent(inout) :: eq
764 
765  ! local variables
766  integer :: id
767  real(dp), allocatable :: t_fe_loc(:,:) ! T_FE(2,1)
768 
769  ! initialize ierr
770  ierr = 0
771 
772  ! Transform depending on equilibrium style being used:
773  ! 1: VMEC
774  ! 2: HELENA
775  select case (eq_style)
776  case (1) ! VMEC
777  ! user output
778  call writo('Transform flux equilibrium quantities to flux &
779  &coordinates')
780 
781  call lvl_ud(1)
782 
783  ! set up local T_FE
784  allocate(t_fe_loc(grid_eq%loc_n_r,0:max_deriv+1))
785  if (use_pol_flux_f) then
786  do id = 0,max_deriv+1
787  t_fe_loc(:,id) = 2*pi/max_flux_e*eq%q_saf_E(:,id)
788  end do
789  else
790  t_fe_loc(:,0) = -2*pi/max_flux_e
791  t_fe_loc(:,1:max_deriv+1) = 0._dp
792  end if
793 
794  ! Transform the derivatives in E coordinates to derivatives in
795  ! the F coordinates.
796  do id = 0,max_deriv+1
797  ! pres_FD
798  ierr = transf_deriv(eq%pres_E,t_fe_loc,eq%pres_FD(:,id),&
799  &max_deriv+1,id)
800  chckerr('')
801 
802  ! flux_p_FD
803  ierr = transf_deriv(eq%flux_p_E,t_fe_loc,&
804  &eq%flux_p_FD(:,id),max_deriv+1,id)
805  chckerr('')
806 
807  ! flux_t_FD
808  ierr = transf_deriv(eq%flux_t_E,t_fe_loc,&
809  &eq%flux_t_FD(:,id),max_deriv+1,id)
810  chckerr('')
811  eq%flux_t_FD(:,id) = -eq%flux_t_FD(:,id) ! multiply by plus minus one
812 
813  ! q_saf_FD
814  ierr = transf_deriv(eq%q_saf_E,t_fe_loc,eq%q_saf_FD(:,id),&
815  &max_deriv+1,id)
816  chckerr('')
817  eq%q_saf_FD(:,id) = -eq%q_saf_FD(:,id) ! multiply by plus minus one
818 
819  ! rot_t_FD
820  ierr = transf_deriv(eq%rot_t_E,t_fe_loc,eq%rot_t_FD(:,id),&
821  &max_deriv+1,id)
822  chckerr('')
823  eq%rot_t_FD(:,id) = -eq%rot_t_FD(:,id) ! multiply by plus minus one
824  end do
825 
826  call lvl_ud(-1)
827  case (2) ! HELENA
828  ! E derivatives are equal to F derivatives
829  eq%flux_p_FD = eq%flux_p_E
830  eq%flux_t_FD = eq%flux_t_E
831  eq%pres_FD = eq%pres_E
832  eq%q_saf_FD = eq%q_saf_E
833  eq%rot_t_FD = eq%rot_t_E
834  end select
835  end function calc_f_derivs_1
837  integer function calc_f_derivs_2(eq) result(ierr)
838  use num_vars, only: eq_style
839  use num_utilities, only: derivs, c
840 
841  character(*), parameter :: rout_name = 'calc_F_derivs_2'
842 
843  ! input / output
844  type(eq_2_type), intent(inout) :: eq
845 
846  ! local variables
847  integer :: id
848  integer :: pmone ! plus or minus one
849 
850  ! initialize ierr
851  ierr = 0
852 
853  ! Set up pmone, depending on equilibrium style being used
854  ! 1: VMEC
855  ! 2: HELENA
856  select case (eq_style)
857  case (1) ! VMEC
858  pmone = -1 ! conversion VMEC LH -> RH coord. system
859  case (2) ! HELENA
860  pmone = 1
861  end select
862 
863  ! user output
864  call writo('Transform metric equilibrium quantities to flux &
865  &coordinates')
866 
867  call lvl_ud(1)
868 
869  ! Transform the derivatives in E coordinates to derivatives in
870  ! the F coordinates.
871  do id = 0,max_deriv
872  ! g_FD
873  ierr = transf_deriv(eq%g_F,eq%T_FE,eq%g_FD,max_deriv,derivs(id))
874  chckerr('')
875 
876  ! h_FD
877  ierr = transf_deriv(eq%h_F,eq%T_FE,eq%h_FD,max_deriv,derivs(id))
878  chckerr('')
879 
880  ! jac_FD
881  ierr = transf_deriv(eq%jac_F,eq%T_FE,eq%jac_FD,max_deriv,&
882  &derivs(id))
883  chckerr('')
884  end do
885 
886  call lvl_ud(-1)
887  end function calc_f_derivs_2
888 
906  integer function calc_memory_eq(arr_size,n_par,mem_size) result(ierr)
907  use iso_c_binding
908 
909  character(*), parameter :: rout_name = 'calc_memory_eq'
910 
911  ! input / output
912  integer, intent(in) :: arr_size
913  integer, intent(in) :: n_par
914  real(dp), intent(inout) :: mem_size
915 
916  ! local variables
917  integer(C_SIZE_T) :: dp_size ! size of dp
918 
919  ! initialize ierr
920  ierr = 0
921 
922  call lvl_ud(1)
923 
924  ! get size of real variable
925  dp_size = sizeof(1._dp)
926 
927  ! set memory size for metric equilibrium variables
928  mem_size = 13._dp*arr_size
929  mem_size = mem_size*n_par*(1+max_deriv)**3
930  mem_size = mem_size*dp_size
931 
932  ! convert B to MB
933  mem_size = mem_size*1.e-6_dp
934 
935  ! apply 50% safety factor (empirical)
936  mem_size = mem_size*1.5_dp
937 
938  ! test overflow
939  if (mem_size.lt.0) then
940  ierr = 1
941  chckerr('Overflow occured')
942  end if
943 
944  call lvl_ud(-1)
945  end function calc_memory_eq
946 
948  logical function do_eq()
950  &jump_to_sol
951  use rich_vars, only: rich_lvl
952 
953  ! increment equilibrium job nr.
954  eq_job_nr = eq_job_nr + 1
955 
956  if (rich_lvl.eq.rich_restart_lvl .and. jump_to_sol) then ! jumping to solution for restart level
957  if (eq_job_nr.eq.1) then
958  do_eq = .true.
959  else
960  do_eq = .false.
961  end if
962  else
963  if (eq_job_nr.le.size(eq_jobs_lims,2)) then ! normal behavior, not yet at last equilibrium job
964  do_eq = .true.
965  else ! normal behavior, at last equilibrium job
966  do_eq = .false.
967  end if
968  end if
969  end function do_eq
970 
973  elemental character(len=max_str_ln) function eq_info()
975 
976  if (size(eq_jobs_lims,2).gt.1) then
977  eq_info = ' for Equilibrium job '//trim(i2str(eq_job_nr))//&
978  &' of '//trim(i2str(size(eq_jobs_lims,2)))
979  else
980  eq_info = ''
981  end if
982  end function eq_info
983 
985  subroutine print_info_eq(n_par_X_rich)
987 
988  ! input / output
989  integer, intent(in) :: n_par_x_rich
990 
991  ! user output
992  if (size(eq_jobs_lims,2).gt.1) then
993  call writo('but parallel limits for this job only ['//&
994  &trim(i2str(eq_jobs_lims(1,eq_job_nr)))//'..'//&
995  &trim(i2str(eq_jobs_lims(2,eq_job_nr)))//&
996  &'] of [1..'//trim(i2str(n_par_x_rich))//']')
997  end if
998  end subroutine print_info_eq
999 end module eq_utilities
num_utilities::c
integer function, public c(ij, sym, n, lim_n)
Convert 2-D coordinates (i,j) to the storage convention used in matrices.
Definition: num_utilities.f90:2556
num_vars::dp
integer, parameter, public dp
double precision
Definition: num_vars.f90:46
eq_vars
Variables that have to do with equilibrium quantities and the grid used in the calculations:
Definition: eq_vars.f90:27
num_vars
Numerical variables used by most other modules.
Definition: num_vars.f90:4
num_vars::max_str_ln
integer, parameter, public max_str_ln
maximum length of strings
Definition: num_vars.f90:50
eq_utilities::transf_deriv
Calculates derivatives in a coordinate system B from derivatives in a coordinates system A,...
Definition: eq_utilities.f90:118
eq_utilities::calc_g
integer function, public calc_g(g_A, T_BA, g_B, deriv, max_deriv)
Calculate the metric coefficients in a coordinate system B ! using the.
Definition: eq_utilities.f90:387
rich_vars
Variables concerning Richardson extrapolation.
Definition: rich_vars.f90:4
str_utilities::i2str
elemental character(len=max_str_ln) function, public i2str(k)
Convert an integer to string.
Definition: str_utilities.f90:18
eq_utilities::calc_inv_met
Calculate from and where and , according to .
Definition: eq_utilities.f90:61
num_vars::eq_job_nr
integer, public eq_job_nr
nr. of eq job
Definition: num_vars.f90:79
num_vars::n_procs
integer, public n_procs
nr. of MPI processes
Definition: num_vars.f90:69
str_utilities
Operations on strings.
Definition: str_utilities.f90:4
grid_vars::grid_type
Type for grids.
Definition: grid_vars.f90:59
eq_vars::max_flux_e
real(dp), public max_flux_e
max. flux in Equilibrium coordinates, set in calc_norm_range_PB3D_in
Definition: eq_vars.f90:49
str_utilities::r2strt
elemental character(len=max_str_ln) function, public r2strt(k)
Convert a real (double) to string.
Definition: str_utilities.f90:54
num_utilities::calc_mult
Calculate matrix multiplication of two square matrices .
Definition: num_utilities.f90:95
eq_vars::eq_1_type
flux equilibrium type
Definition: eq_vars.f90:63
eq_utilities
Numerical utilities related to equilibrium variables.
Definition: eq_utilities.f90:4
eq_utilities::debug_calc_inv_met_ind
logical, public debug_calc_inv_met_ind
plot debug information for calc_inv_met_ind()
Definition: eq_utilities.f90:27
num_vars::rich_restart_lvl
integer, public rich_restart_lvl
starting Richardson level (0: none [default])
Definition: num_vars.f90:173
eq_utilities::calc_f_derivs
Transforms derivatives of the equilibrium quantities in E coordinates to derivatives in the F coordin...
Definition: eq_utilities.f90:36
num_utilities::calc_inv
Calculate inverse of square matrix A.
Definition: num_utilities.f90:81
num_utilities::derivs
integer function, dimension(:,:), allocatable, public derivs(order, dims)
Returns derivatives of certain order.
Definition: num_utilities.f90:2246
eq_utilities::eq_info
elemental character(len=max_str_ln) function, public eq_info()
Returns string with possible extension with equilibrium job as well as parallel job,...
Definition: eq_utilities.f90:974
num_vars::eq_style
integer, public eq_style
either 1 (VMEC) or 2 (HELENA)
Definition: num_vars.f90:89
num_utilities::conv_mat
Converts a (symmetric) matrix A with the storage convention described in eq_vars.eq_2_type.
Definition: num_utilities.f90:123
eq_utilities::calc_memory_eq
integer function, public calc_memory_eq(arr_size, n_par, mem_size)
Calculate memory in MB necessary for variables in equilibrium job.
Definition: eq_utilities.f90:907
num_utilities::check_deriv
integer function, public check_deriv(deriv, max_deriv, sr_name)
checks whether the derivatives requested for a certain subroutine are valid
Definition: num_utilities.f90:2260
num_vars::max_deriv
integer, parameter, public max_deriv
highest derivatives for metric factors in Flux coords.
Definition: num_vars.f90:52
num_utilities
Numerical utilities.
Definition: num_utilities.f90:4
messages::writo
subroutine, public writo(input_str, persistent, error, warning, alert)
Write output to file identified by output_i.
Definition: messages.f90:275
num_utilities::is_sym
integer function, public is_sym(n, nn, sym)
Determines whether a matrix making use of the storage convention in eq_vars.eq_2_type is symmetric or...
Definition: num_utilities.f90:2626
messages
Numerical utilities related to giving output.
Definition: messages.f90:4
num_vars::pi
real(dp), parameter, public pi
Definition: num_vars.f90:83
num_vars::jump_to_sol
logical, public jump_to_sol
jump to solution
Definition: num_vars.f90:141
grid_vars
Variables pertaining to the different grids used.
Definition: grid_vars.f90:4
messages::lvl_ud
subroutine, public lvl_ud(inc)
Increases/decreases lvl of output.
Definition: messages.f90:254
num_utilities::fac
recursive integer function, public fac(n)
Calculate factorial.
Definition: num_utilities.f90:2609
num_vars::use_pol_flux_f
logical, public use_pol_flux_f
whether poloidal flux is used in F coords.
Definition: num_vars.f90:114
rich_vars::rich_lvl
integer, public rich_lvl
current level of Richardson extrapolation
Definition: rich_vars.f90:19
eq_utilities::do_eq
logical function, public do_eq()
If this equilibrium job should be done, also increment eq_job_nr.
Definition: eq_utilities.f90:949
num_vars::eq_jobs_lims
integer, dimension(:,:), allocatable, public eq_jobs_lims
data about eq jobs: [ , ] for all jobs
Definition: num_vars.f90:77
output_ops
Operations concerning giving output, on the screen as well as in output files.
Definition: output_ops.f90:5
eq_utilities::print_info_eq
subroutine, public print_info_eq(n_par_X_rich)
Prints information for equilibrium parallel job.
Definition: eq_utilities.f90:986
num_utilities::add_arr_mult
Add to an array (3) the product of arrays (1) and (2).
Definition: num_utilities.f90:39
eq_vars::eq_2_type
metric equilibrium type
Definition: eq_vars.f90:114