PB3D [2.47]
Ideal linear high-n MHD stability in 3-D
Loading...
Searching...
No Matches
eq_utilities.f90
Go to the documentation of this file.
1!------------------------------------------------------------------------------!
2!> Numerical utilities related to equilibrium variables.
3!------------------------------------------------------------------------------!
5#include <PB3D_macros.h>
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 !> \ldebug
27 logical :: debug_calc_inv_met_ind = .false. !< plot debug information for calc_inv_met_ind()
28#endif
29
30 ! interfaces
31
32 !> \public Transforms derivatives of the equilibrium quantities in E
33 !! coordinates to derivatives in the F coordinates.
34 !!
35 !! \return ierr
36 interface calc_f_derivs
37 !> \public
38 module procedure calc_f_derivs_1
39 !> \public
40 module procedure calc_f_derivs_2
41 end interface
42
43 !> \public Calculate
44 !! \f$D_1^{m_1} D_2^{m_2} D_3^{m_3} X\f$
45 !! from
46 !! \f$D_1^{i_1} D_2^{i_2} D_3^{i_3} X\f$
47 !! and
48 !! \f$D_1^{j_1} D_2^{j_2} D_3^{j_3} Y\f$
49 !! where \f$XY=1\f$ and \f$i,j = 0\ldots m\f$, according to \cite Weyens3D.
50 !!
51 !!
52 !! \f$D_1^{m_1} D_2^{m_2} D_3^{m_3} \f$ is defined as
53 !! \f$\left(\frac{\partial}{\partial u^1}\right)^{m_1}
54 !! \left(\frac{\partial}{\partial u^2}\right)^{m_2}
55 !! \left(\frac{\partial}{\partial u^3}\right)^{m_3} \f$
56 !!
57 !! \note It is assumed that the lower order derivatives have been calculated
58 !! already. If not, the results will be incorrect.
59 !!
60 !! \return ierr
61 interface calc_inv_met
62 !> \public
63 module procedure calc_inv_met_ind
64 !> \public
65 module procedure calc_inv_met_arr
66 !> \public
67 module procedure calc_inv_met_ind_0d
68 !> \public
69 module procedure calc_inv_met_arr_0d
70 end interface
71
72 !> \public Calculates derivatives in a coordinate system B from derivatives
73 !! in a coordinates system A, making use of the transformation matrix
74 !! \f$\overline{\text{T}}_\text{B}^\text{A}\f$.
75 !!
76 !! The routine works by exchanging the derivatives in the coordinates B for
77 !! derivatives in coordinates A using the formula
78 !! \f[\mathbf{D}_\text{B}^m X = \overline{\text{T}}_\text{B}^\text{A}
79 !! \mathbf{D}^1_\text{A} \left(\mathbf{D}^{m-1}_\text{B} X\right) , \f]
80 !! where \f$\mathbf{D}^m\f$ is a tensor of rank \f$m\f$ that contains all
81 !! the derivatives of total rank \f$m\f$. For example,
82 !! \f[\mathbf{D}^1 = \vec{D} =
83 !! \left(\begin{array}{c}\frac{\partial}{\partial u^1} \\
84 !! \frac{\partial}{\partial u^2} \\ \frac{\partial}{\partial u^3}
85 !! \end{array}\right) \f]
86 !!
87 !! This is done for the derivatives in each of the coordinates B until
88 !! degree 0 is reached.
89 !!
90 !! Furthermore, each of these degrees of derivatives in coordinates B can be
91 !! be derived optionally in the original coordinate system A, which yields
92 !! the formula:
93 !! \f[ \mathbf{D}^p_\text{A} \left(\mathbf{D}^m_\text{B} X \right) =
94 !! \sum_q \left(\begin{array}{c}p\\q\end{array}\right)
95 !! \mathbf{D}^q_\text{A}
96 !! \left(\overline{\text{T}}_\text{B}^\text{A}\right)
97 !! \left(\mathbf{D}^{p-q}_\text{A} \mathbf{D}^1_\text{A}\right)
98 !! \left( \mathbf{D}^{m-1}_\text{B} X \right)\f]
99 !!
100 !! This way, ultimately the desired derivatives in the coordinates B can be
101 !! obtained recursively from the lower orders in the coordinates B and
102 !! higher orders in the coordinates A.
103 !!
104 !! For example:
105 !! - For order \f$M\f$, the formula can be used with \f$p=0\f$.
106 !! - For this, it is necessary that \f$\mathbf{D}^1_\text{A}
107 !! \mathbf{D}^{m-1}_\text{B} X\f$ be precalculated, which can be done using
108 !! the formula with \f$p=1\f$ and \f$m=M-1\f$.
109 !! - For this, it is necessary that \f$\mathbf{D}^1_\text{A}
110 !! \mathbf{D}^{M-1}_\text{B} X\f$ and \f$\mathbf{D}^2_\text{A}
111 !! \mathbf{D}^{M-1}_\text{B} X\f$ are precalculated, which can be done with
112 !! \f$p=1\f$ and \f$m=M-1\f$ as well as \f$p=2\f$ and \f$m=M-1\f$.
113 !! - etc.
114 !!
115 !! \see \cite Weyens3D for more detailed information.
116 !!
117 !! \return ierr
118 interface transf_deriv
119 !> \public
120 module procedure transf_deriv_3_ind
121 !> \public
122 module procedure transf_deriv_3_arr
123 !> \public
124 module procedure transf_deriv_3_arr_2d
125 !> \public
126 module procedure transf_deriv_1_ind
127 end interface
128
129contains
130 !> \private individual matrix version
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:) !< X
141 real(dp), intent(in) :: y(1:,1:,1:,1:,0:,0:,0:) !< Y
142 integer, intent(in) :: deriv(:) !< derivatives
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
259 !> \private individual scalar version
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:) !< X
265 real(dp), intent(in) :: y(1:,1:,1:,0:,0:,0:) !< Y
266 integer, intent(in) :: deriv(:) !< derivatives
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 !< \private array matrix version
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:) !< X
326 real(dp), intent(in) :: y(1:,1:,1:,1:,0:,0:,0:) !< Y
327 integer, intent(in) :: deriv(:,:) !< derivatives
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 !< \private array scalar version
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:) !< X
346 real(dp), intent(in) :: y(1:,1:,1:,0:,0:,0:) !< Y
347 integer, intent(in) :: deriv(:,:) !< derivatives
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
361 !> Calculate the metric coefficients in a coordinate system B ! using the
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:) !< \f$g_\text{A}\f$
393 real(dp), intent(in) :: t_ba(:,:,:,:,0:,0:,0:) !< \f$\overline{\text{T}}_\text{B}^\text{A}\f$
394 real(dp), intent(inout) :: g_b(:,:,:,:,0:,0:,0:) !< \f$g_\text{B}\f$
395 integer, intent(in) :: deriv(3) !< derivatives
396 integer, intent(in) :: max_deriv !< maximum derivative
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])
474 !> \private
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
505 !> \private 3-D scalar version with one derivative
506 integer recursive function transf_deriv_3_ind(x_a,t_ba,x_b,max_deriv,&
507 &deriv_B,deriv_A_input) result(ierr)
508 use num_utilities, only: add_arr_mult, c
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:) !< variable and derivs. in coord. system A
514 real(dp), intent(in) :: t_ba(1:,1:,1:,1:,0:,0:,0:) !< transf. mat. and derivs. between coord. systems A and B
515 real(dp), intent(inout) :: x_b(1:,1:,1:) !< requested derivs. of variable in coord. system B
516 integer, intent(in) :: max_deriv !< maximum degrees of derivs.
517 integer, intent(in) :: deriv_b(:) !< derivs. in coord. system B
518 integer, intent(in), optional :: deriv_a_input(:) !< derivs. in coord. system A (optional)
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
612 !> \private 3-D scalar version with multiple derivatives
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:) !< variable and derivs. in coord. system A
619 real(dp), intent(in) :: t_ba(1:,1:,1:,1:,0:,0:,0:) !< transf. mat. and derivs. between coord. systems A and B
620 real(dp), intent(inout) :: x_b(1:,1:,1:,0:,0:,0:) !< requested derivs. of variable in coord. system B
621 integer, intent(in) :: max_deriv !< maximum degrees of derivs.
622 integer, intent(in) :: derivs(:,:) !< series of derivs. (in coordinate system B)
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
637 !> \private 3-D matrix version with multiple derivatives
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:) !< variable and derivs. in coord. system A
646 real(dp), intent(in) :: t_ba(1:,1:,1:,1:,0:,0:,0:) !< transf. mat. and derivs. between coord. systems A and B
647 real(dp), intent(inout) :: x_b(1:,1:,1:,1:,0:,0:,0:) !< requested derivs. of variable in coord. system B
648 integer, intent(in) :: max_deriv !< maximum degrees of derivs.
649 integer, intent(in) :: derivs(:,:) !< series of derivs. (in coordinate system B)
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
678 !> \private 1-D scalar version with one derivative
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:) !< variable and derivs. in coord. system A
687 real(dp), intent(inout) :: x_b(1:) !< requested derivs. of variable in coord. system B
688 real(dp), intent(in) :: t_ba(1:,0:) !< transf. mat. and derivs. between coord. systems A and B
689 integer, intent(in), optional :: deriv_a_input !< derivs. in coord. system A (optional)
690 integer, intent(in) :: deriv_b !< derivs. in coord. system B
691 integer, intent(in) :: max_deriv !< maximum degrees of derivs.
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
753 !> \private flux version
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 !< equilibrium grid
763 type(eq_1_type), intent(inout) :: eq !< flux equilibrium variables
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
836 !> \private metric version
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 !< metric equilibrium variables
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
889 !> Calculate memory in MB necessary for variables in equilibrium job.
890 !!
891 !! The size of these variables is equal to the product of the non-parallel
892 !! dimensions (e.g. \c n_geo x \c loc_n_r), times the number of variables.
893 !!
894 !! The latter should be:
895 !! - PB3D: only take into account (\f$2\cdot6+1 = 13\f$) equilibrium
896 !! variables \c g_FD, \c h_FD and \c jac_FD, as the perturbation variables
897 !! are divided in jobs occupying the remaning space. These equilibrium
898 !! variables are tabulated on the equilibrium grid. Note that they contain
899 !! derivatives in extra dimensions, so that their size should be multiplied
900 !! by (\c max_deriv+1)^3.
901 !! - POST: take into account these 13 equilibrium variables, as well as 4
902 !! variables \c U and \c DU, with double size due to being complex, and the
903 !! additional dimension equal to \c n_mod_X, but without derivatives.
904 !!
905 !! \return ierr
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 !< size of part of X array
913 integer, intent(in) :: n_par !< number of parallel points
914 real(dp), intent(inout) :: mem_size !< total 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
947 !> If this equilibrium job should be done, also increment \c eq_job_nr.
948 logical function do_eq()
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
971 !> Returns string with possible extension with equilibrium job as well as
972 !! parallel job, or nothing if only one level and one parallel job.
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
984 !> Prints information for equilibrium parallel job.
985 subroutine print_info_eq(n_par_X_rich)
987
988 ! input / output
989 integer, intent(in) :: n_par_x_rich !< number of parallel points in this Richardson level
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
999end module eq_utilities
Transforms derivatives of the equilibrium quantities in E coordinates to derivatives in the F coordin...
Calculate from and where and , according to weyens3d.
Calculates derivatives in a coordinate system B from derivatives in a coordinates system A,...
Calculate inverse of square matrix A.
Calculate matrix multiplication of two square matrices .
Converts a (symmetric) matrix A with the storage convention described in eq_vars.eq_2_type.
Numerical utilities related to equilibrium variables.
elemental character(len=max_str_ln) function, public eq_info()
Returns string with possible extension with equilibrium job as well as parallel job,...
logical function, public do_eq()
If this equilibrium job should be done, also increment eq_job_nr.
subroutine, public print_info_eq(n_par_x_rich)
Prints information for equilibrium parallel job.
integer function, public calc_memory_eq(arr_size, n_par, mem_size)
Calculate memory in MB necessary for variables in equilibrium job.
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.
logical, public debug_calc_inv_met_ind
plot debug information for calc_inv_met_ind()
Variables that have to do with equilibrium quantities and the grid used in the calculations:
Definition eq_vars.f90:27
real(dp), public max_flux_e
max. flux in Equilibrium coordinates, set in calc_norm_range_PB3D_in
Definition eq_vars.f90:49
Variables pertaining to the different grids used.
Definition grid_vars.f90:4
Numerical utilities related to giving output.
Definition messages.f90:4
subroutine, public lvl_ud(inc)
Increases/decreases lvl of output.
Definition messages.f90:254
subroutine, public writo(input_str, persistent, error, warning, alert)
Write output to file identified by output_i.
Definition messages.f90:275
Numerical utilities.
integer function, public check_deriv(deriv, max_deriv, sr_name)
checks whether the derivatives requested for a certain subroutine are valid
recursive integer function, public fac(n)
Calculate factorial.
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...
integer function, public c(ij, sym, n, lim_n)
Convert 2-D coordinates (i,j) to the storage convention used in matrices.
integer function, dimension(:,:), allocatable, public derivs(order, dims)
Returns derivatives of certain order.
integer, dimension(:,:), allocatable, public m
1-D array indices of metric indices
Numerical variables used by most other modules.
Definition num_vars.f90:4
integer, parameter, public dp
double precision
Definition num_vars.f90:46
real(dp), parameter, public pi
Definition num_vars.f90:83
integer, public n_procs
nr. of MPI processes
Definition num_vars.f90:69
integer, parameter, public max_str_ln
maximum length of strings
Definition num_vars.f90:50
integer, dimension(:,:), allocatable, public eq_jobs_lims
data about eq jobs: [ , ] for all jobs
Definition num_vars.f90:77
integer, parameter, public max_deriv
highest derivatives for metric factors in Flux coords.
Definition num_vars.f90:52
integer, public eq_style
either 1 (VMEC) or 2 (HELENA)
Definition num_vars.f90:89
integer, public rich_restart_lvl
starting Richardson level (0: none [default])
Definition num_vars.f90:173
logical, public jump_to_sol
jump to solution
Definition num_vars.f90:141
integer, public eq_job_nr
nr. of eq job
Definition num_vars.f90:79
logical, public use_pol_flux_f
whether poloidal flux is used in F coords.
Definition num_vars.f90:114
Operations concerning giving output, on the screen as well as in output files.
Definition output_ops.f90:5
Variables concerning Richardson extrapolation.
Definition rich_vars.f90:4
integer, public rich_lvl
current level of Richardson extrapolation
Definition rich_vars.f90:19
Operations on strings.
elemental character(len=max_str_ln) function, public i2str(k)
Convert an integer to string.
elemental character(len=max_str_ln) function, public r2strt(k)
Convert a real (double) to string.
flux equilibrium type
Definition eq_vars.f90:63
metric equilibrium type
Definition eq_vars.f90:114
Type for grids.
Definition grid_vars.f90:59