PB3D [2.47]
Ideal linear high-n MHD stability in 3-D
Loading...
Searching...
No Matches
eq_ops.f90
Go to the documentation of this file.
1!------------------------------------------------------------------------------!
2!> Operations on the equilibrium variables.
3!------------------------------------------------------------------------------!
4module eq_ops
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
21#if ldebug
23#endif
24
25 ! global variables
26 integer :: fund_n_par !< fundamental interval width
27 logical :: BR_normalization_provided(2) ! used to export HELENA to VMEC
28#if ldebug
29 !> \ldebug
30 logical :: debug_calc_derived_q = .false. !< plot debug information for calc_derived_q()
31 !> \ldebug
32 logical :: debug_j_plot = .false. !< plot debug information for j_plot()
33 !> \ldebug
34 logical :: debug_create_vmec_input = .false. !< plot debug information for create_vmec_input()
35#endif
36
37 ! interfaces
38
39 !> \public Calculate the equilibrium quantities on a grid determined by
40 !! straight field lines.
41 !!
42 !! This grid has the dimensions \c (n_par,loc_n_r).
43 !!
44 !! Optionally, for \c eq_2, the used variables can be deallocated on the
45 !! fly, to limit memory usage.
46 !!
47 !! \return ierr
48 interface calc_eq
49 !> \public
50 module procedure calc_eq_1
51 !> \public
52 module procedure calc_eq_2
53 end interface
54
55 !> \public Print equilibrium quantities to an output file:
56 !!
57 !! - flux:
58 !! - \c pres_FD,
59 !! - \c q_saf_FD,
60 !! - \c rot_t_FD,
61 !! - \c flux_p_FD,
62 !! - \c flux_t_FD,
63 !! - \c rho,
64 !! - \c S,
65 !! - \c kappa_n,
66 !! - \c kappa_g,
67 !! - \c sigma
68 !! - metric:
69 !! - \c g_FD,
70 !! - \c h_FD,
71 !! - \c jac_FD
72 !!
73 !! If \c rich_lvl is provided, \c "_R_[rich_lvl]" is appended to the data
74 !! name if it is \c >0 (only for \c eq_2).
75 !!
76 !! Optionally, for \c eq_2, it can be specified that this is a divided
77 !! parallel grid, corresponding to the variable \c eq_jobs_lims with index
78 !! \c eq_job_nr. In this case, the total grid size is adjusted to the one
79 !! specified by \c eq_jobs_lims and the grid is written as a subset.
80 !! \note
81 !! -# The equilibrium quantities are outputted in Flux coordinates.
82 !! -# The metric equilibrium quantities can be deallocated on the fly,
83 !! which is useful if this routine is followed by a deallocation any way,
84 !! so that memory usage does not almost double.
85 !! -# \c print_output_eq_2 is only used by HELENA now, as for VMEC it is
86 !! too slow since there are often multiple VMEC equilibrium jobs, while for
87 !! HELENA this is explicitely forbidden.
88 !!
89 !! \return ierr
91 !> \public
92 module procedure print_output_eq_1
93 !> \public
94 module procedure print_output_eq_2
95 end interface
96
97 !> \public Redistribute the equilibrium variables, but only the Flux
98 !! variables are saved.
99 !!
100 !! \see redistribute_output_grid()
101 !!
102 !! \return ierr
104 !> \public
105 module procedure redistribute_output_eq_1
106 !> \public
107 module procedure redistribute_output_eq_2
108 end interface
109
110 !> \public Calculate \f$R\f$, \f$Z\f$ \& \f$\lambda\f$ and derivatives in
111 !! VMEC coordinates.
112 !!
113 !! This is done at the grid points given by the variables \c theta_E and \c
114 !! zeta_E (contained in \c trigon_factors) and at every normal point.
115 !!
116 !! The derivatives are indicated by the variable \c deriv which has 3
117 !! indices
118 !!
119 !! \note There is no HELENA equivalent because for HELENA simulations,
120 !! \f$R\f$ and \f$Z\f$ are not necessary for calculation of the metric
121 !! coefficients, and \f$\lambda\f$ does not exist.
122 !!
123 !! \see calc_trigon_factors()
124 !!
125 !! \return ierr
126 interface calc_rzl
127 !> \public
128 module procedure calc_rzl_ind
129 !> \public
130 module procedure calc_rzl_arr
131 end interface
132
133 !> \public Calculate the lower metric elements in the C(ylindrical)
134 !! coordinate system.
135 !!
136 !! This is done directly using the formula's in \cite Weyens3D
137 !!
138 !! \return ierr
139 interface calc_g_c
140 !> \public
141 module procedure calc_g_c_ind
142 !> \public
143 module procedure calc_g_c_arr
144 end interface
145
146 !> \public Calculate the lower metric coefficients in the equilibrium V(MEC)
147 !! coordinate system.
148 !!
149 !! This is done using the metric coefficients in the C(ylindrical)
150 !! coordinate system and the transformation matrices
151 !!
152 !! \note It is assumed that the lower order derivatives have been calculated
153 !! already. If not, the results will be incorrect.
154 !!
155 !! \return ierr
156 interface calc_g_v
157 !> \public
158 module procedure calc_g_v_ind
159 !> \public
160 module procedure calc_g_v_arr
161 end interface
162
163 !> \public Calculate the lower metric coefficients in the equilibrium
164 !! H(ELENA) coordinate system.
165 !!
166 !! This is done using the HELENA output
167 !!
168 !! \return ierr
169 interface calc_g_h
170 !> \public
171 module procedure calc_g_h_ind
172 !> \public
173 module procedure calc_g_h_arr
174 end interface
175
176 !> \public Calculate the metric coefficients in the F(lux) coordinate
177 !! system.
178 !!
179 !! This is done using the metric coefficients in the equilibrium coordinate
180 !! system and the transformation matrices.
181 !!
182 !! \return ierr
183 interface calc_g_f
184 !> \public
185 module procedure calc_g_f_ind
186 !> \public
187 module procedure calc_g_f_arr
188 end interface
189
190 !> \public Calculate \f$\mathcal{J}_\text{V}\f$, the jacobian in V(MEC)
191 !! coordinates.
192 !!
193 !! This is done using VMEC output.
194 !!
195 !! \return ierr
196 interface calc_jac_v
197 !> \public
198 module procedure calc_jac_v_ind
199 !> \public
200 module procedure calc_jac_v_arr
201 end interface
202
203 !> \public Calculate \f$\mathcal{J}_\text{H}\f$, the jacobian in HELENA
204 !! coordinates.
205 !!
206 !! This is done directly from
207 !! \f[\mathcal{J}_\text{H} = q \frac{R^2}{F} \f]
208 !!
209 !! \note It is assumed that the lower order derivatives have been calculated
210 !! already. If not, the results will be incorrect.
211 !!
212 !! \return ierr
213 interface calc_jac_h
214 !> \public
215 module procedure calc_jac_h_ind
216 !> \public
217 module procedure calc_jac_h_arr
218 end interface
219
220 !> \public Calculate \f$\mathcal{J}_\text{F}\f$, the jacobian in Flux
221 !! coordinates.
222 !!
223 !! This is done directly from
224 !! \f[ \mathcal{J}_\text{F} =
225 !! \text{det}\left(\overline{\text{T}}_\text{F}^\text{E}\right)
226 !! \mathcal{J}_\text{E} \f]
227 !!
228 !! \note It is assumed that the lower order derivatives have been calculated
229 !! already. If not, the results will be incorrect.
230 !!
231 !! \return ierr
232 interface calc_jac_f
233 !> \public
234 module procedure calc_jac_f_ind
235 !> \public
236 module procedure calc_jac_f_arr
237 end interface
238
239 !> \public Calculate \f$\overline{\text{T}}_\text{C}^\text{V}\f$, the
240 !! transformation matrix between C(ylindrical) and V(mec) coordinate system.
241 !!
242 !! This is done directly using the formula's in \cite Weyens3D
243 !!
244 !! \return ierr
245 interface calc_t_vc
246 !> \public
247 module procedure calc_t_vc_ind
248 !> \public
249 module procedure calc_t_vc_arr
250 end interface
251
252 !> \public Calculate \f$\overline{\text{T}}_\text{V}^\text{F}\f$, the
253 !! transformation matrix between V(MEC) and F(lux) coordinate systems.
254 !!
255 !! This is done directly using the formula's in \cite Weyens3D
256 !!
257 !! \return ierr
258 interface calc_t_vf
259 !> \public
260 module procedure calc_t_vf_ind
261 !> \public
262 module procedure calc_t_vf_arr
263 end interface
264
265 !> \public Calculate \f$\overline{\text{T}}_\text{H}^\text{F}\f$, the
266 !! transformation matrix between H(ELENA) and F(lux) coordinate systems.
267 !!
268 !! This is done directly using the formula's in \cite Weyens3D
269 !!
270 !! \return ierr
271 interface calc_t_hf
272 !> \public
273 module procedure calc_t_hf_ind
274 !> \public
275 module procedure calc_t_hf_arr
276 end interface
277
278contains
279 !> \private flux version
280 integer function calc_eq_1(grid_eq,eq) result(ierr)
283 use eq_vars, only: rho_0
284 use num_utilities, only: derivs
285 use eq_utilities, only: calc_f_derivs
286
287 character(*), parameter :: rout_name = 'calc_eq_1'
288
289 ! input / output
290 type(grid_type), intent(inout) :: grid_eq !< equilibrium grid
291 type(eq_1_type), intent(inout) :: eq !< flux equilibrium variables
292
293 ! local variables
294 character(len=max_str_ln) :: err_msg ! error message
295 character(len=max_str_ln) :: file_name ! file name
296 character(len=max_str_ln) :: plot_title ! plot title
297
298 ! initialize ierr
299 ierr = 0
300
301 ! user output
302 call writo('Start setting up flux equilibrium quantities')
303
304 call lvl_ud(1)
305
306 ! create equilibrium
307 call eq%init(grid_eq)
308
309 ! choose which equilibrium style is being used:
310 ! 1: VMEC
311 ! 2: HELENA
312 select case (eq_style)
313 case (1) ! VMEC
314 call calc_flux_q_vmec()
315 case (2) ! HELENA
316 call calc_flux_q_hel()
317 end select
318
319 ! tests
320 err_msg = 'Check out the FAQ at &
321 &https://pb3d.github.io/Doxygen/html/page_faq.html'
322
323 ! check whether the coordinate is increasing
324 if (grid_eq%r_F(grid_eq%n(3)) .le. grid_eq%r_F(1)) then
325 file_name = 'r_F'
326 plot_title = 'Flux normal coordinate'
327 call print_ex_2d(plot_title,file_name,grid_eq%r_F,draw=.false.)
328 call draw_ex([plot_title],file_name,1,1,.false.)
329 ierr = 3
330 call writo('The code has not been tested satisfactorily for &
331 &decreasing normal coordinate. See plot '// trim(file_name),&
332 &alert=.true.)
333 chckerr(err_msg)
334 end if
335
336 ! check whether there are reversed shear regions
337 if (maxval(eq%q_saf_E(:,1))*minval(eq%q_saf_E(:,1)) .lt. 0._dp) then
338 file_name = 'Dq_saf'
339 plot_title = 'derivative of safety factor'
340 call print_ex_2d(plot_title,file_name,eq%q_saf_E(:,1),draw=.false.)
341 call draw_ex([plot_title],file_name,1,1,.false.)
342 ierr = 3
343 call writo('The code has to be adapted for reversed-shear regions &
344 &still. See plot ' // trim(file_name),alert=.true.)
345 chckerr(err_msg)
346 end if
347
348 ! take local variables
349 grid_eq%loc_r_E = grid_eq%r_E(grid_eq%i_min:grid_eq%i_max)
350 grid_eq%loc_r_F = grid_eq%r_F(grid_eq%i_min:grid_eq%i_max)
351
352 ! Transform flux equilibrium E into F derivatives
353 ierr = calc_f_derivs(grid_eq,eq)
354 chckerr('')
355
356 ! Calculate particle density rho
357 ! choose which density style is being used:
358 ! 1: constant, equal to rho_0
359 select case (rho_style)
360 case (1) ! arbitrarily constant (normalized value)
361 eq%rho = rho_0
362 case default
363 err_msg = 'No density style associated with '//&
364 &trim(i2str(rho_style))
365 ierr = 1
366 chckerr(err_msg)
367 end select
368 ! normalize rho if necessary
369 if (use_normalization) eq%rho = eq%rho/rho_0
370
371 call lvl_ud(-1)
372
373 call writo('Done setting up flux equilibrium quantities')
374 contains
375 ! VMEC version
376 ! The VMEC normal coord. is the toroidal (or poloidal) flux, normalized
377 ! wrt. to the maximum flux, equidistantly, so the step size is
378 ! 1/(n(3)-1)
379 !> \private
380 subroutine calc_flux_q_vmec()
382 use eq_vars, only: max_flux_e
383
384 ! copy flux variables
385 eq%flux_p_E = flux_p_v(grid_eq%i_min:grid_eq%i_max,:)
386 eq%flux_t_E = flux_t_v(grid_eq%i_min:grid_eq%i_max,:)
387 eq%pres_E = pres_v(grid_eq%i_min:grid_eq%i_max,:)
388 eq%q_saf_E = q_saf_v(grid_eq%i_min:grid_eq%i_max,:)
389 eq%rot_t_E = rot_t_v(grid_eq%i_min:grid_eq%i_max,:)
390
391 ! max flux and normal coord. of eq grid in Equilibrium coordinates
392 ! (uses poloidal flux by default)
393 if (use_pol_flux_e) then
394 grid_eq%r_E = flux_p_v(:,0)/max_flux_e
395 else
396 grid_eq%r_E = flux_t_v(:,0)/max_flux_e
397 end if
398
399 ! max flux and normal coord. of eq grid in Flux coordinates
400 if (use_pol_flux_f) then
401 grid_eq%r_F = flux_p_v(:,0)/(2*pi) ! psi_F = flux_p/2pi
402 else
403 grid_eq%r_F = - flux_t_v(:,0)/(2*pi) ! psi_F = flux_t/2pi, conversion VMEC LH -> PB3D RH
404 end if
405 end subroutine calc_flux_q_vmec
406
407 ! HELENA version
408 ! The HELENA normal coord. is the poloidal flux divided by 2pi
409 !> \private
410 subroutine calc_flux_q_hel()
412 use num_utilities, only: calc_int
413
414 ! copy flux variables
415 eq%flux_p_E = flux_p_h(grid_eq%i_min:grid_eq%i_max,:)
416 eq%flux_t_E = flux_t_h(grid_eq%i_min:grid_eq%i_max,:)
417 eq%pres_E = pres_h(grid_eq%i_min:grid_eq%i_max,:)
418 eq%q_saf_E = q_saf_h(grid_eq%i_min:grid_eq%i_max,:)
419 eq%rot_t_E = rot_t_h(grid_eq%i_min:grid_eq%i_max,:)
420
421 ! max flux and normal coord. of eq grid in Equilibrium coordinates
422 ! (uses poloidal flux by default)
423 grid_eq%r_E = flux_p_h(:,0)/(2*pi)
424
425 ! max flux and normal coord. of eq grid in Flux coordinates
426 if (use_pol_flux_f) then
427 grid_eq%r_F = flux_p_h(:,0)/(2*pi) ! psi_F = flux_p/2pi
428 else
429 grid_eq%r_F = flux_t_h(:,0)/(2*pi) ! psi_F = flux_t/2pi
430 end if
431 end subroutine calc_flux_q_hel
432 end function calc_eq_1
433 !> \private metric version
434 integer function calc_eq_2(grid_eq,eq_1,eq_2,dealloc_vars) result(ierr)
436 use num_utilities, only: derivs, c
439#if ldebug
440 use num_vars, only: ltest
443 use helena_vars, only: chi_h
444#endif
445
446 character(*), parameter :: rout_name = 'calc_eq_2'
447
448 ! input / output
449 type(grid_type), intent(inout) :: grid_eq !< equilibrium grid
450 type(eq_1_type), intent(in) :: eq_1 !< metric equilibrium variables
451 type(eq_2_type), intent(inout) :: eq_2 !< metric equilibrium variables
452 logical, intent(in), optional :: dealloc_vars !< deallocate variables on the fly after writing
453
454 ! local variables
455 integer :: id, jd, kd ! counters
456 integer :: pmone ! plus or minus one
457 logical :: dealloc_vars_loc ! local dealloc_vars
458 character(len=max_str_ln) :: err_msg ! error message
459
460 ! initialize ierr
461 ierr = 0
462
463 ! user output
464 call writo('Start setting up metric equilibrium quantities')
465
466 call lvl_ud(1)
467
468 ! set up local dealloc_vars
469 dealloc_vars_loc = .false.
470 if (present(dealloc_vars)) dealloc_vars_loc = dealloc_vars
471
472 ! create metric equilibrium variables
473 call eq_2%init(grid_eq)
474
475 ! do some preparations depending on equilibrium style used
476 ! 1: VMEC
477 ! 2: HELENA
478 select case (eq_style)
479 case (1) ! VMEC
480 ! calculate the cylindrical variables R, Z and λ and
481 ! derivatives if not yet done
482 call writo('Calculate R, Z, λ, ...')
483 if (.not.allocated(grid_eq%trigon_factors)) then
484 ierr = calc_trigon_factors(grid_eq%theta_E,grid_eq%zeta_E,&
485 &grid_eq%trigon_factors)
486 chckerr('')
487 end if
488 do id = 0,max_deriv+1
489 ierr = calc_rzl(grid_eq,eq_2,derivs(id))
490 chckerr('')
491 end do
492 case (2) ! HELENA
493 ! do nothing
494 end select
495
496 ! Calcalations depending on equilibrium style being used:
497 ! 1: VMEC
498 ! 2: HELENA
499 select case (eq_style)
500 case (1) ! VMEC
501 ! calculate the metrics in the cylindrical coordinate system
502 call writo('Calculate g_C') ! h_C is not necessary
503 do id = 0,max_deriv
504 ierr = calc_g_c(eq_2,derivs(id))
505 chckerr('')
506 end do
507
508 ! calculate the transformation matrix C(ylindrical) -> V(MEC)
509 call writo('Calculate T_VC')
510 do id = 0,max_deriv
511 ierr = calc_t_vc(eq_2,derivs(id))
512 chckerr('')
513 end do
514
515 ! calculate the metric factors in the VMEC coordinate system
516 call writo('Calculate g_V')
517 do id = 0,max_deriv
518 ierr = calc_g_v(eq_2,derivs(id))
519 chckerr('')
520 end do
521
522 ! calculate the jacobian in the VMEC coordinate system
523 call writo('Calculate jac_V')
524 do id = 0,max_deriv
525 ierr = calc_jac_v(grid_eq,eq_2,derivs(id))
526 chckerr('')
527 end do
528
529#if ldebug
530 if (ltest) then
531 call writo('Test calculation of g_V?')
532 if(get_log(.false.)) then
533 ierr = test_g_v(grid_eq,eq_2)
534 chckerr('')
535 call pause_prog
536 end if
537 call writo('Test calculation of jac_V?')
538 if(get_log(.false.)) then
539 ierr = test_jac_v(grid_eq,eq_2)
540 chckerr('')
541 call pause_prog
542 end if
543 end if
544#endif
545
546 ! calculate the transformation matrix V(MEC) -> F(lux)
547 call writo('Calculate T_VF')
548 do id = 0,max_deriv
549 ierr = calc_t_vf(grid_eq,eq_1,eq_2,derivs(id))
550 chckerr('')
551 end do
552
553 ! set up plus minus one
554 pmone = -1 ! conversion VMEC LH -> RH coord. system
555
556 ! possibly deallocate
557 if (dealloc_vars_loc) then
558 deallocate(eq_2%g_C)
559 end if
560 case (2) ! HELENA
561#if ldebug
562 ! Check whether poloidal grid is indeed chi_H
563 ! If not, the boundary conditions used in the splines of the
564 ! following routines are not correct.
565 ! A solution to this problem would be to set up the HELENA
566 ! variables in a full periodic grid, even when they are
567 ! top-bottom symmetric.
568 do kd = 1,grid_eq%loc_n_r
569 do jd = 1,grid_eq%n(2)
570 do id = 1,grid_eq%n(1)
571 if (abs(grid_eq%theta_E(id,jd,kd)-chi_h(id)).gt.&
572 &tol_zero) then
573 ierr = 1
574 err_msg = 'theta_E is not identical to chi_H'
575 chckerr(err_msg)
576 end if
577 if (abs(grid_eq%zeta_E(id,jd,kd)-0._dp).gt.&
578 &tol_zero) then
579 ierr = 1
580 err_msg = 'zeta_E is not identical to 0'
581 chckerr(err_msg)
582 end if
583 end do
584 end do
585 end do
586
587 if (ltest) then
588 call writo('Test consistency of metric factors?')
589 if(get_log(.false.,ind=.true.)) then
590 ierr = test_metrics_h()
591 chckerr('')
592 call pause_prog(ind=.true.)
593 end if
594
595 call writo('Test harmonic content of HELENA output?')
596 if(get_log(.false.,ind=.true.)) then
597 ierr = test_harm_cont_h()
598 chckerr('')
599 call pause_prog(ind=.true.)
600 end if
601 end if
602#endif
603
604 ! calculate the jacobian in the HELENA coordinate system
605 call writo('Calculate jac_H')
606 do id = 0,max_deriv
607 ierr = calc_jac_h(grid_eq,eq_1,eq_2,derivs(id))
608 chckerr('')
609 end do
610
611 ! calculate the metric factors in the HELENA coordinate system
612 call writo('Calculate g_H')
613 do id = 0,max_deriv
614 ierr = calc_g_h(grid_eq,eq_2,derivs(id))
615 chckerr('')
616 end do
617
618 ! calculate the inverse h_H of the metric factors g_H
619 call writo('Calculate h_H')
620 do id = 0,max_deriv
621 ierr = calc_inv_met(eq_2%h_E,eq_2%g_E,derivs(id))
622 chckerr('')
623 end do
624
625#if ldebug
626 if (ltest) then
627 call writo('Test calculation of D1 D2 h_H?')
628 if(get_log(.false.)) then
629 ierr = test_d12h_h(grid_eq,eq_2)
630 chckerr('')
631 call pause_prog
632 end if
633 end if
634#endif
635
636 ! export for VMEC port
637 if (export_hel) then
638 call writo('Exporting HELENA equilibrium for VMEC porting')
639 call lvl_ud(1)
640 ierr = create_vmec_input(grid_eq,eq_1)
641 chckerr('')
642 call lvl_ud(-1)
643 call writo('Done exporting')
644 end if
645
646 ! calculate the transformation matrix H(ELENA) -> F(lux)
647 call writo('Calculate T_HF')
648 do id = 0,max_deriv
649 ierr = calc_t_hf(grid_eq,eq_1,eq_2,derivs(id))
650 chckerr('')
651 end do
652
653 ! set up plus minus one
654 pmone = 1
655
656 ! possibly deallocate
657 if (dealloc_vars_loc) then
658 deallocate(eq_2%h_E)
659 end if
660 end select
661
662#if ldebug
663 if (ltest) then
664 call writo('Test calculation of T_EF?')
665 if(get_log(.false.)) then
666 ierr = test_t_ef(grid_eq,eq_1,eq_2)
667 chckerr('')
668 call pause_prog
669 end if
670 end if
671#endif
672
673 ! calculate the inverse of the transformation matrix T_EF
674 call writo('Calculate T_FE')
675 do id = 0,max_deriv
676 ierr = calc_inv_met(eq_2%T_FE,eq_2%T_EF,derivs(id))
677 chckerr('')
678 ierr = calc_inv_met(eq_2%det_T_FE,eq_2%det_T_EF,derivs(id))
679 chckerr('')
680 end do
681
682 ! calculate the metric factors in the Flux coordinate system
683 call writo('Calculate g_F')
684 do id = 0,max_deriv
685 ierr = calc_g_f(eq_2,derivs(id))
686 chckerr('')
687 end do
688
689 ! calculate the inverse h_F of the metric factors g_F
690 call writo('Calculate h_F')
691 do id = 0,max_deriv
692 ierr = calc_inv_met(eq_2%h_F,eq_2%g_F,derivs(id))
693 chckerr('')
694 end do
695
696 ! calculate the jacobian in the Flux coordinate system
697 call writo('Calculate jac_F')
698 do id = 0,max_deriv
699 ierr = calc_jac_f(eq_2,derivs(id))
700 chckerr('')
701 end do
702
703 ! limit Jacobians to small value to avoid infinities
704 eq_2%jac_F(:,:,:,0,0,0) = sign(&
705 &max(tol_zero,abs(eq_2%jac_F(:,:,:,0,0,0))),eq_2%jac_F(:,:,:,0,0,0))
706 eq_2%jac_E(:,:,:,0,0,0) = sign(&
707 &max(tol_zero,abs(eq_2%jac_E(:,:,:,0,0,0))),eq_2%jac_E(:,:,:,0,0,0))
708
709 ! possibly deallocate
710 if (dealloc_vars_loc) then
711 deallocate(eq_2%g_E)
712 end if
713
714 ! Transform metric equilibrium E into F derivatives
715 ierr = calc_f_derivs(eq_2)
716 chckerr('')
717
718 ! possibly deallocate
719 if (dealloc_vars_loc) then
720 deallocate(eq_2%g_F,eq_2%h_F,eq_2%jac_F)
721 deallocate(eq_2%det_T_EF,eq_2%det_T_FE)
722 end if
723
724 ! Calculate derived metric quantities
725 ierr = calc_derived_q(grid_eq,eq_1,eq_2)
726 chckerr('')
727
728#if ldebug
729 if (ltest) then
730 call writo('Test Jacobian in Flux coordinates?')
731 if(get_log(.false.)) then
732 ierr = test_jac_f(grid_eq,eq_1,eq_2)
733 chckerr('')
734 call pause_prog
735 end if
736 call writo('Test calculation of B_F?')
737 if(get_log(.false.)) then
738 ierr = test_b_f(grid_eq,eq_1,eq_2)
739 chckerr('')
740 call pause_prog
741 end if
742 call writo('Test consistency with given pressure?')
743 if(get_log(.false.)) then
744 ierr = test_p(grid_eq,eq_1,eq_2)
745 chckerr('')
746 call pause_prog
747 end if
748 end if
749#endif
750
751 call lvl_ud(-1)
752
753 call writo('Done setting up metric equilibrium quantities')
754 end function calc_eq_2
755
756 !> Creates a VMEC input file.
757 !!
758 !! Optionally, a perturbation can be added: Either the displacement of the
759 !! plasma position can be described (\c pert_style 1), or ripple in the
760 !! toroidal magnetic field (\c pert_style 2), with a fixed toroidal mode
761 !! number.
762 !!
763 !! Both perturbation styles can have various prescription types:
764 !! -# file with Fourier modes in the geometrical angular coordinate
765 !! -# same but manually
766 !! -# file with perturbation from a 2-D map in the geometric angular
767 !! coordinate.
768 !!
769 !! For \c pert_style 2, a file has to be provided that describes the
770 !! translation between position perturbation and magnetic perturbation for
771 !! curves of constant geometrical angle. This file can be generated for an
772 !! already existing ripple case using POST with <tt>--compare_tor_pos</tt>
773 !! with <tt>n_zeta_plot = 3</tt> and \c min_theta_plot and \c max_theta_plot
774 !! indicating half a ripple period.
775 !!
776 !! The output from this VMEC run can then be used to iteratively create a
777 !! new file to translate toroidal magnetic field ripple to position
778 !! perturbation.
779 !!
780 !! \note Meaning of the indices of \c B_F, \c B_F_dum:
781 !! - <tt>(pol modes, cos/sin)</tt> for \c B_F_dum
782 !! - <tt>(tor_modes, pol modes, cos/sin (m theta), R/Z)</tt> for \c B_F
783 integer function create_vmec_input(grid_eq,eq_1) result(ierr)
784 use eq_vars, only: pres_0, r_0, psi_0, b_0, vac_perm
785 !use eq_vars, only: max_flux_E
786 use grid_vars, only: n_r_eq
788 use helena_vars, only: nchi, r_h, z_h, ias, rbphi_h, flux_p_h, &
790 use x_vars, only: min_r_sol, max_r_sol
798 use ezspline_obj
799 use ezspline
800#if ldebug
801 use num_vars, only: ltest
802#endif
803
804 character(*), parameter :: rout_name = 'create_VMEC_input'
805
806 ! input / output
807 type(grid_type), intent(in) :: grid_eq !< equilibrium grid varibles
808 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium quantities
809
810 ! local variables
811 type(ezspline2_r8) :: f_spl ! spline object for interpolation
812 integer :: id, jd, kd, ld ! counters
813 integer :: n_b ! nr. of points in Fourier series
814 integer :: nfp ! scale factor for toroidal mode numbers
815 integer :: tot_nr_pert ! total number of perturbations combinations (N,M)
816 integer :: nr_n ! number of different N in perturbation
817 integer :: id_n_0 ! index where zero N is situated in B_F
818 integer :: n_loc, m_loc ! local n and m of perturbation
819 integer :: pert_map_n_loc ! n_loc for perturbation map
820 integer :: n_id ! index in bundled n_pert
821 integer :: n_pert_map(2) ! number of points in perturbation map
822 integer :: plot_dim(2) ! plot dimensions
823 integer :: rec_min_m ! recommended minimum of poloidal modes
824 integer :: pert_style ! style of perturbation (1: r, 2: B_tor)
825 integer :: pert_type ! type of perturbation prescription
826 integer :: max_n_b_output ! max. nr. of modes written in output file (constant in VMEC)
827 integer :: n_prop_b_tor ! n of angular poins in prop_B_tor
828 integer :: nr_m_max ! maximum nr. of poloidal mode numbers
829 integer :: ncurr ! ncurr VMEC flag (0: prescribe iota, 1: prescribe tor. current)
830 integer :: bcs(2,2) ! boundary conditions
831 integer :: m_range(2) ! range in poloidal modes
832 integer, allocatable :: n_pert(:) ! tor. mode numbers and pol. mode numbers for each of them
833 integer, allocatable :: n_pert_copy(:) ! copy of n_pert, for sorting
834 integer, allocatable :: piv(:) ! pivots for sorting
835 character(len=1) :: pm(3) ! "+ " or "-"
836 character(len=8) :: flux_name(2) ! "poloidal" or "toroidal"
837 character(len=6) :: path_prefix = '../../' ! prefix of path
838 character(len=max_str_ln) :: hel_pert_file_name ! name of perturbation file
839 character(len=max_str_ln) :: err_msg ! error message
840 character(len=max_str_ln) :: file_name ! name of file
841 character(len=max_str_ln) :: plot_name(2) ! name of plot file
842 character(len=max_str_ln) :: plot_title(2) ! name of plot
843 character(len=max_str_ln) :: prop_b_tor_file_name ! name of B_tor proportionality file
844 real(dp) :: eq_vert_shift ! vertical shift in equilibrium
845 real(dp) :: pert_map_vert_shift ! vertical shift in perturbation map
846 real(dp) :: max_pert_on_axis ! maximum perturbation on axis (theta = 0)
847 real(dp) :: m_tol = 1.e-7_dp ! tolerance for Fourier mode strength
848 real(dp) :: delta_loc(2) ! local delta
849 real(dp) :: plot_lims(2,2) ! limits of plot dims [pi]
850 real(dp) :: norm_b_h ! normalization for R and Z Fourier modes
851 real(dp) :: mult_fac ! global multiplication factor
852 real(dp) :: prop_b_tor_smooth ! smoothing of prop_B_tor_interp via Fourier transform
853 real(dp) :: rz_b_0(2) ! origin of R and Z of boundary
854 real(dp) :: s_vj(99) ! normal coordinate s for writing
855 real(dp) :: pres_vj(99,0:1) ! pressure for writing
856 real(dp) :: rot_t_v(99) ! rotational transform for writing
857 real(dp) :: f_vj(99) ! F for writing
858 real(dp) :: ffp_vj(99) ! FF' for writing
859 real(dp) :: flux_j(99) ! normalized flux for writing
860 real(dp) :: q_saf_vj(99) ! q_saf for writing
861 real(dp) :: i_tor_v(99) ! enclosed toroidal current for VMEC
862 real(dp) :: i_tor_j(99) ! enclosed toroidal current for JOREK
863 real(dp) :: norm_j(3) ! normalization factors for Jorek (R_geo, Z_geo, F0)
864 real(dp), allocatable :: psi_t(:) ! normalized toroidal flux
865 real(dp), allocatable :: ffp(:) ! FF' in total grid
866 real(dp), allocatable :: i_tor(:) ! toroidal current within flux surfaces
867 real(dp), allocatable :: norm_transf(:,:) ! transformation of normal coordinates between MISHKA and VMEC or JOREK
868 real(dp), allocatable :: i_tor_int(:) ! integrated I_tor
869 real(dp), allocatable :: rrint(:) ! R^2 integrated poloidally
870 real(dp), allocatable :: rrint_loc(:) ! local RRint
871 real(dp), allocatable :: r_plot(:,:,:) ! R for plotting of ripple map
872 real(dp), allocatable :: z_plot(:,:,:) ! Z for plotting of ripple map
873 real(dp), allocatable :: pert_map_r(:) ! R of perturbation map
874 real(dp), allocatable :: pert_map_z(:) ! Z of perturbation map
875 real(dp), allocatable :: pert_map(:,:) ! perturbation map
876 real(dp), allocatable :: pert_map_interp(:) ! interpolated perturbation from map
877 real(dp), allocatable :: pert_map_interp_f(:,:) ! Fourier coefficients of pert_map_interp
878 real(dp), allocatable :: r_h_loc(:,:) ! local R_H
879 real(dp), allocatable :: z_h_loc(:,:) ! local Z_H
880 real(dp), allocatable :: delta(:,:,:) ! amplitudes of perturbations (N,M,c/s)
881 real(dp), allocatable :: delta_copy(:,:,:) ! copy of delta, for sorting
882 real(dp), allocatable :: bh_0(:,:) ! R and Z at unperturbed bounday
883 real(dp), allocatable :: b_f(:,:,:,:) ! cos and sin Fourier components
884 real(dp), allocatable :: b_f_copy(:,:,:,:) ! copy of B_F
885 real(dp), allocatable :: b_f_dum(:,:) ! dummy B_F
886 real(dp), allocatable :: b_f_dum2(:,:) ! other dummy B_F
887 real(dp), allocatable :: b_f_dum3(:,:) ! other dummy B_F
888 real(dp), allocatable :: theta_b(:) ! geometric pol. angle
889 real(dp), allocatable :: theta_geo(:,:,:) ! geometric pol. angle, for plotting
890 real(dp), allocatable :: prop_b_tor(:,:) ! proportionality between delta B_tor and delta_norm
891 real(dp), allocatable :: prop_b_tor_ord(:,:) ! ordered prop_B_tor
892 real(dp), allocatable :: prop_b_tor_interp(:) ! proportionality between delta B_tor and delta_norm
893 real(dp), allocatable :: prop_b_tor_interp_f(:,:) ! Fourier modes of prop_B_tor_interp
894 real(dp), allocatable :: xyz_plot(:,:,:,:) ! plotting X, Y and Z
895 logical :: zero_n_pert ! there is a perturbation with N = 0
896 logical :: pert_eq ! whether equilibrium is perturbed
897 logical :: stel_sym ! whether there is stellarator symmetry
898 logical :: change_max_n_b_output ! whether to change max_n_B_output
899 logical :: found ! whether something was found
900#if ldebug
901 real(dp), allocatable :: bh_0_alt(:,:) ! reconstructed R and Z
902#endif
903
904 ! initialize ierr
905 ierr = 0
906
907 ! test if full range
908 if (min_r_sol.gt.0 .or. max_r_sol.lt.1) then
909 ierr = 1
910 err_msg = 'This routine should be run with the full normal &
911 &range!'
912 chckerr(err_msg)
913 end if
914
915 ! test if normalization constants chosen
916 if (.not.all(br_normalization_provided,1)) &
917 &call writo('No normalization factors were provided. &
918 &Are you sure this is correct?',warning=.true.)
919
920 ! set up local R_H and Z_H
921 allocate(r_h_loc(nchi,n_r_eq))
922 allocate(z_h_loc(nchi,n_r_eq))
923 r_h_loc = r_h
924 z_h_loc = z_h
925 if (use_normalization) then
926 r_h_loc = r_h*r_0
927 z_h_loc = z_h*r_0
928 end if
929
930 ! ask for vertical shift
931 ! Note: HELENA automatically shifts it zo Zvac = 0
932 call writo('Was the equilibrium shifted vertically?')
933 eq_vert_shift = 0._dp
934 if (get_log(.false.)) then
935 call writo('How much higher [m] should the &
936 &equilibrium be in the real world?')
937 eq_vert_shift = get_real()
938 call writo('The equilibrium will be shifted by '//&
939 &trim(r2strt(eq_vert_shift))//'m to compensate for this')
940 z_h_loc = z_h_loc + eq_vert_shift
941 end if
942
943 ! set up F, FF' and <R^2>
944 allocate(ffp(n_r_eq))
945 allocate(rrint(n_r_eq))
946 allocate(rrint_loc(nchi)) ! includes overlap, as necessary for integration
947 ierr = spline(flux_p_h(:,0)/(2*pi),rbphi_h(:,0)**2*0.5_dp,&
948 &flux_p_h(:,0)/(2*pi),ffp,ord=norm_disc_prec_eq,deriv=1)
949 chckerr('')
950 do kd = 1,n_r_eq
951 ierr = calc_int(r_h(:,kd)**2,chi_h,rrint_loc)
952 chckerr('')
953 rrint(kd) = rrint_loc(nchi)
954 end do
955 if (ias.eq.0) rrint = 2*rrint
956
957 ! set up toroidal current:
958 ! This is the toroidal current between neighbouring flux surfaces (see
959 ! first commentary in VMEC2013/Sources/General/add_fluxes.f90:
960 ! <J^zeta J> = - [2pi FF'/mu_0 <J/R^2> + p'<J>]
961 ! = - [2pi F'q/mu_0 + p' <R^2>q/F] ,
962 ! which all have units 1/m. Here, <.> means integration in a
963 ! full poloidal period.
964 ! As VMEC wants the toroidal current within two infintesimally
965 ! separated flux surface as a function of the VMEC normal
966 ! coordinate, above has to be multiplied by
967 ! dpsi_PB3D/dpsi_V = psi_tor(edge)/(2pi q)
968 ! Finally, there is an extra factor -1 because the toroidal coordinate
969 ! in VMEC is oposite.
970 ! For JOREK, the same is done with the poloidal flux instead of the
971 ! toroidal and without the change of sign.
972 allocate(i_tor(n_r_eq))
973 allocate(norm_transf(n_r_eq,2))
974 norm_transf(:,1) = -flux_t_h(n_r_eq,0)/(2*pi*eq_1%q_saf_E(:,0))
975 norm_transf(:,2) = flux_p_h(n_r_eq,0)/(2*pi)
976 i_tor = - (eq_1%pres_E(:,1)*rrint*eq_1%q_saf_E(:,0)/rbphi_h(:,0) + &
977 &2*pi*ffp*eq_1%q_saf_E(:,0)/(rbphi_h(:,0)*vac_perm))
978
979 ! calculate total toroidal current
980 allocate(i_tor_int(size(i_tor)))
981 ierr = calc_int(i_tor*norm_transf(:,1),&
982 &flux_t_h(:,0)/flux_t_h(n_r_eq,0),i_tor_int)
983 if (use_normalization) i_tor_int = i_tor_int * r_0*b_0/mu_0_original
984 chckerr('')
985
986 ! user output
987 call writo('Initialize boundary')
988 call lvl_ud(1)
989
990 ! full 0..2pi boundary
991 if (ias.eq.0) then ! symmetric (so nchi is odd)
992 n_b = nchi*2-2 ! -2 because of overlapping points at 0, pi
993 else ! asymmetric (so nchi is aumented by one)
994 n_b = nchi-1 ! -1 because of overlapping points at 0
995 end if
996 allocate(bh_0(n_b,2)) ! HELENA coords (no overlap at 0)
997 allocate(theta_b(n_b)) ! geometrical poloidal angle at boundary (overlap at 0)
998 if (ias.eq.0) then ! symmetric
999 bh_0(1:nchi,1) = r_h_loc(:,n_r_eq)
1000 bh_0(1:nchi,2) = z_h_loc(:,n_r_eq)
1001 do id = 1,nchi-2
1002 bh_0(nchi+id,1) = r_h_loc(nchi-id,n_r_eq)
1003 bh_0(nchi+id,2) = -z_h_loc(nchi-id,n_r_eq)
1004 end do
1005 else
1006 bh_0(:,1) = r_h_loc(1:n_b,n_r_eq)
1007 bh_0(:,2) = z_h_loc(1:n_b,n_r_eq)
1008 end if
1009 rz_b_0(1) = sum(r_h_loc(:,1))/size(r_h_loc,1)
1010 rz_b_0(2) = sum(z_h_loc(:,1))/size(z_h_loc,1)
1011 theta_b = atan2(bh_0(:,2)-rz_b_0(2),bh_0(:,1)-rz_b_0(1))
1012 where (theta_b.lt.0) theta_b = theta_b + 2*pi
1013 call writo('Magnetic axis used for geometrical coordinates:')
1014 call lvl_ud(1)
1015 call writo('('//trim(r2str(rz_b_0(1)))//'m, '//&
1016 &trim(r2str(rz_b_0(2)))//'m)')
1017 call lvl_ud(-1)
1018
1019#if ldebug
1020 if (ltest) then
1021 call writo('Test with circular tokamak?')
1022 call lvl_ud(1)
1023 if(get_log(.false.)) then
1024 call writo('Testing with circular tokamak:')
1025 call lvl_ud(1)
1026 call writo('R = 3/2 + 1/2 cos(θ)')
1027 call writo('Z = 1/2 sin(θ)')
1028 call writo('with θ equidistant')
1029 call lvl_ud(-1)
1030 theta_b = [((id-1._dp)/n_b*2*pi,id=1,n_b)]
1031 bh_0(:,1) = 1.5_dp + 0.5*cos(theta_b)
1032 bh_0(:,2) = 0.5*sin(theta_b)
1033 end if
1034 call lvl_ud(-1)
1035 end if
1036#endif
1037
1038 ! plot with HDF5
1039 allocate(theta_geo(grid_eq%n(1),1,grid_eq%loc_n_r))
1040 do kd = 1,grid_eq%loc_n_r
1041 theta_geo(:,1,kd) = atan2(z_h_loc(:,kd)-rz_b_0(2),&
1042 &r_h_loc(:,kd)-rz_b_0(1))
1043 end do
1044 where (theta_geo.lt.0._dp) theta_geo = theta_geo + 2*pi
1045 allocate(xyz_plot(grid_eq%n(1),1,grid_eq%loc_n_r,3))
1046 ierr = calc_xyz_grid(grid_eq,grid_eq,xyz_plot(:,:,:,1),&
1047 &xyz_plot(:,:,:,2),xyz_plot(:,:,:,3))
1048 chckerr('')
1049 call plot_hdf5('theta_geo','theta_geo',theta_geo,&
1050 &tot_dim=[grid_eq%n(1),1,grid_eq%n(3),3],&
1051 &loc_offset=[0,0,grid_eq%i_min-1,0],&
1052 &x=xyz_plot(:,:,:,1),y=xyz_plot(:,:,:,2),z=xyz_plot(:,:,:,3),&
1053 &descr='geometric poloidal angle used to create the VMEC &
1054 &input file')
1055 deallocate(xyz_plot)
1056
1057 ! plot R and Z
1058 plot_name(1) = 'RZ'
1059 plot_title = ['R_H','Z_H']
1060 call print_ex_2d(plot_title(1:2),plot_name(1),bh_0,&
1061 &x=reshape(theta_b,[n_b,1])/pi,&
1062 &draw=.false.)
1063 call draw_ex(plot_title(1:2),plot_name(1),2,1,.false.)
1064
1065 ! set up auxiliary variable
1066 flux_name = ['poloidal','toroidal']
1067
1068 call lvl_ud(-1)
1069
1070 ! plot properties
1071 plot_dim = [100,20]
1072 plot_lims(:,1) = [0.0_dp,2.0_dp]
1073 plot_lims(:,2) = [0.5_dp,2.0_dp]
1074 call writo('Change plot properties from defaults?')
1075 call lvl_ud(1)
1076 do id = 1,2
1077 call writo(trim(i2str(plot_dim(id)))//' geometrical '//&
1078 &flux_name(id)//' points on range '//&
1079 &trim(r2strt(plot_lims(1,id)))//' pi .. '//&
1080 &trim(r2strt(plot_lims(2,id)))//' pi')
1081 end do
1082 call lvl_ud(-1)
1083 if (get_log(.false.)) then
1084 call lvl_ud(1)
1085 do id = 1,2
1086 call writo('number of '//flux_name(id)//' points?')
1087 plot_dim(id) = get_int(lim_lo=2)
1088 call writo('lower limit of '//flux_name(id)//&
1089 &' range [pi]?')
1090 plot_lims(1,id) = get_real()
1091 call writo('upper limit of '//flux_name(id)//&
1092 &' range [pi]?')
1093 plot_lims(2,id) = get_real()
1094 end do
1095 call lvl_ud(-1)
1096 end if
1097
1098 ! get ncurr
1099 call writo('VMEC current style?')
1100 call lvl_ud(1)
1101 call writo('0: prescribe iota')
1102 call writo('1: prescribe toroidal current')
1103 ncurr = get_int(lim_lo=0,lim_hi=1)
1104 call lvl_ud(-1)
1105
1106 ! get input about equilibrium perturbation
1107 ! Note: negative M values will be converted to negative N values and
1108 ! Possibly negative delta's at the end.
1109 call writo('Do you want to perturb the equilibrium?')
1110 pert_eq = get_log(.false.)
1111
1112 zero_n_pert = .false.
1113 if (pert_eq) then
1114 ! get perturbation style
1115 call writo('Which perturbation do you want to describe?')
1116 call lvl_ud(1)
1117 call writo('1: plasma boundary position')
1118 call writo('2: B_tor magnetic ripple with fixed N')
1119 call lvl_ud(-1)
1120 pert_style = get_int(lim_lo=1,lim_hi=2)
1121
1122 ! for style 2, get proportionality file
1123 if (pert_style.eq.2) then
1124 ! find file
1125 found = .false.
1126 do while (.not.found)
1127 call writo('Proportionality file name?')
1128 call lvl_ud(1)
1129 read(*,*,iostat=ierr) prop_b_tor_file_name
1130 err_msg = 'failed to read prop_B_tor_file_name'
1131 chckerr(err_msg)
1132
1133 ! open
1134 do kd = 0,2
1135 call writo('Trying "'//path_prefix(1:kd*3)//&
1136 &trim(prop_b_tor_file_name)//'"')
1137 open(prop_b_tor_i,file=path_prefix(1:kd*3)//&
1138 &trim(prop_b_tor_file_name),iostat=ierr,&
1139 &status='old')
1140 if (ierr.eq.0) then
1141 call lvl_ud(1)
1142 call writo("Success")
1143 call lvl_ud(-1)
1144 exit
1145 end if
1146 end do
1147 if (ierr.eq.0) then
1148 found = .true.
1149 else
1150 call writo('Could not open file "'//&
1151 &trim(prop_b_tor_file_name)//'"')
1152 end if
1153
1154 call lvl_ud(-1)
1155 end do
1156
1157 ! read file
1158 call writo('Analyzing perturbation proportionality file "'&
1159 &//trim(prop_b_tor_file_name)//'"')
1160 call lvl_ud(1)
1161 n_prop_b_tor = count_lines(prop_b_tor_i)
1162 allocate(prop_b_tor(n_prop_b_tor,2))
1163 ierr = skip_comment(prop_b_tor_i,&
1164 &file_name=prop_b_tor_file_name)
1165 chckerr('')
1166 do id = 1,n_prop_b_tor
1167 read(prop_b_tor_i,*,iostat=ierr) prop_b_tor(id,:)
1168 end do
1169
1170 ! user info
1171 call writo(trim(i2str(n_prop_b_tor))//&
1172 &' poloidal points '//&
1173 &trim(r2strt(minval(prop_b_tor(:,1))))//'..'//&
1174 &trim(r2strt(maxval(prop_b_tor(:,1)))))
1175 call writo('The proportionality factor should be tabulated &
1176 &in a geometrical angle that has the SAME origin as &
1177 &the one used here',alert=.true.)
1178
1179 ! interpolate the proportionality factor on the geometrical
1180 ! poloidal angle used here (as opposed to the one in which
1181 ! it was tabulated)
1182 ierr = order_per_fun(prop_b_tor,prop_b_tor_ord,1,& ! overlap of 1 is enough for splines
1183 &tol=0.5_dp*pi/n_prop_b_tor) ! set appropriate tolerance: a quarter of a equidistant grid
1184 chckerr('')
1185 deallocate(prop_b_tor)
1186 allocate(prop_b_tor_interp(n_b))
1187 ierr = spline(prop_b_tor_ord(:,1),prop_b_tor_ord(:,2),&
1188 &theta_b,prop_b_tor_interp,ord=norm_disc_prec_eq,&
1189 &bcs=[-1,-1])
1190 chckerr('')
1191 deallocate(prop_b_tor_ord)
1192 if (grid_eq%n(2).ne.1) call writo('There should be only 1 &
1193 &geodesical position, but there are '//&
1194 &trim(i2str(grid_eq%n(2))),warning=.true.)
1195
1196 ! smooth prop_B_tor_interp
1197 prop_b_tor_smooth = 1._dp
1198 call writo('Do you want to smooth the perturbation?')
1199 if (get_log(.false.)) prop_b_tor_smooth = &
1200 &get_real(lim_lo=0._dp,lim_hi=1._dp)
1201
1202 ! calculate NUFFT
1203 ierr = nufft(theta_b,prop_b_tor_interp,prop_b_tor_interp_f)
1204 chckerr('')
1205 deallocate(prop_b_tor_interp)
1206
1207 call lvl_ud(-1)
1208 end if
1209
1210 ! get perturbation type
1211 call writo('How do you want to prescribe the perturbation?')
1212 call lvl_ud(1)
1213 call writo('1: through a file with Fourier modes in &
1214 &geometrical coordinates')
1215 call lvl_ud(1)
1216 call writo('It should provide N M delta_cos delta_sin in four &
1217 &columns')
1218 call writo('Rows starting with "#" are ignored')
1219 call lvl_ud(-1)
1220 call writo('2: same as 1 but interactively')
1221 call writo('3: through a 2-D map in R, Z for constant &
1222 &geometrical coordinates')
1223 call lvl_ud(1)
1224 call writo('It should provide R, Z and delta')
1225 call writo('Rows starting with "#" are ignored')
1226 call lvl_ud(-1)
1227 pert_type = get_int(lim_lo=1,lim_hi=3)
1228 call lvl_ud(-1)
1229
1230 select case (pert_type)
1231 case (1,3) ! files
1232 found = .false.
1233 do while (.not.found)
1234 ! user input
1235 call writo('File name?')
1236 call lvl_ud(1)
1237 read(*,*,iostat=ierr) hel_pert_file_name
1238 err_msg = 'failed to read HEL_pert_file_name'
1239 chckerr(err_msg)
1240
1241 ! open
1242 do kd = 0,2
1243 call writo('Trying "'//path_prefix(1:kd*3)//&
1244 &trim(hel_pert_file_name)//'"')
1245 open(hel_pert_i,file=path_prefix(1:kd*3)//&
1246 &trim(hel_pert_file_name),iostat=ierr,&
1247 &status='old')
1248 if (ierr.eq.0) then
1249 call lvl_ud(1)
1250 call writo("Success")
1251 call lvl_ud(-1)
1252 exit
1253 end if
1254 end do
1255 if (ierr.eq.0) then
1256 found = .true.
1257 else
1258 call writo('Could not open file "'//&
1259 &trim(hel_pert_file_name)//'"')
1260 end if
1261
1262 call lvl_ud(-1)
1263 end do
1264
1265 call writo('Parsing "'//trim(hel_pert_file_name)//'"')
1266 call lvl_ud(1)
1267
1268 if (pert_type.eq.1) then ! Fourier modes in geometrical coordinates
1269 ! get number of lines
1270 tot_nr_pert = count_lines(hel_pert_i)
1271 else if (pert_type.eq.3) then ! 2-D map of perturbation
1272 ! ask for vertical shift
1273 call writo('Was the equilibrium shifted &
1274 &vertically?')
1275 if (abs(eq_vert_shift).gt.0._dp) then
1276 call lvl_ud(1)
1277 call writo('Note: If you already shifted just &
1278 &now, don''t do it again!',alert=.true.)
1279 call lvl_ud(-1)
1280 end if
1281 pert_map_vert_shift = 0._dp
1282 if (get_log(.false.)) then
1283 call writo('How much higher [m] should the &
1284 &equilibrium be in the real world?')
1285 pert_map_vert_shift = get_real()
1286 call writo('The perturbation map will be &
1287 &shifted by '//&
1288 &trim(r2strt(-pert_map_vert_shift))//&
1289 &'m to compensate for this')
1290 end if
1291
1292 ! get map
1293 ierr = skip_comment(hel_pert_i,&
1294 &file_name=hel_pert_file_name)
1295 chckerr('')
1296 read(hel_pert_i,*) n_pert_map(1) ! nr. of points in R
1297 allocate(pert_map_r(n_pert_map(1)))
1298 do kd = 1,n_pert_map(1)
1299 read(hel_pert_i,*) pert_map_r(kd)
1300 end do
1301 ierr = skip_comment(hel_pert_i,&
1302 &file_name=hel_pert_file_name)
1303 chckerr('')
1304 read(hel_pert_i,*) n_pert_map(2) ! nr. of points in R
1305 allocate(pert_map_z(n_pert_map(2)))
1306 do kd = 1,n_pert_map(2)
1307 read(hel_pert_i,*) pert_map_z(kd)
1308 end do
1309 pert_map_z = pert_map_z - pert_map_vert_shift
1310 ierr = skip_comment(hel_pert_i,&
1311 &file_name=hel_pert_file_name)
1312 chckerr('')
1313 allocate(pert_map(n_pert_map(1),n_pert_map(2)))
1314 do kd = 1,n_pert_map(1)
1315 read(hel_pert_i,*) pert_map(kd,:)
1316 end do
1317
1318 ! plot map
1319 allocate(r_plot(n_pert_map(1),n_pert_map(2),1))
1320 allocate(z_plot(n_pert_map(1),n_pert_map(2),1))
1321 do kd = 1,n_pert_map(2)
1322 r_plot(:,kd,1) = pert_map_r
1323 end do
1324 do kd = 1,n_pert_map(1)
1325 z_plot(kd,:,1) = pert_map_z
1326 end do
1327 call plot_hdf5('pert_map','pert_map',&
1328 &reshape(pert_map,[n_pert_map,1]),&
1329 &x=r_plot,y=z_plot,descr=&
1330 &'perturbation map for '//&
1331 &trim(hel_pert_file_name))
1332 deallocate(r_plot,z_plot)
1333
1334 ! test
1335 if (minval(r_h_loc).lt.minval(pert_map_r) .or. &
1336 &maxval(r_h_loc).gt.maxval(pert_map_r) .or. &
1337 &minval(z_h_loc).lt.minval(pert_map_z) .or. &
1338 &maxval(z_h_loc).gt.maxval(pert_map_z)) then
1339 ierr = 1
1340 call writo('Are you sure you have specified &
1341 &a normalization factor R_0?',alert=.true.)
1342 err_msg = 'R and Z are not contained in &
1343 &perturbation map'
1344 chckerr(err_msg)
1345 end if
1346
1347 ! set up 2-D spline
1348 allocate(pert_map_interp(n_b))
1349 bcs(:,1) = [0,0] ! not a knot
1350 bcs(:,2) = [0,0] ! not a knot
1351 call ezspline_init(f_spl,n_pert_map(1),n_pert_map(2),&
1352 &bcs(:,1),bcs(:,2),ierr)
1353 call ezspline_error(ierr)
1354 chckerr('')
1355
1356 ! set grid
1357 f_spl%x1 = pert_map_r
1358 f_spl%x2 = pert_map_z
1359
1360 ! set up
1361 call ezspline_setup(f_spl,pert_map,ierr,&
1362 &exact_dim=.true.) ! match exact dimensions, none of them old Fortran bullsh*t!
1363 call ezspline_error(ierr)
1364 chckerr('')
1365
1366 ! interpolate
1367 do id = 1,n_b
1368 call ezspline_interp(f_spl,bh_0(id,1),bh_0(id,2),&
1369 &pert_map_interp(id),ierr)
1370 call ezspline_error(ierr)
1371 chckerr('')
1372 end do
1373 call print_ex_2d('ripple','pert_map_interp',&
1374 &pert_map_interp,x=theta_b,draw=.false.)
1375 call draw_ex(['ripple'],'pert_map_interp',&
1376 &1,1,.false.)
1377
1378 ! calculate NUFFT
1379 ierr = nufft(theta_b,pert_map_interp,&
1380 &pert_map_interp_f)
1381 chckerr('')
1382 tot_nr_pert = size(pert_map_interp_f,1)*2 ! need both positive and negative n
1383
1384 ! get toroidal mode number
1385 call writo('toiroidal period for 2-D map?')
1386 pert_map_n_loc = get_int(lim_lo=0)
1387 end if
1388
1389 call lvl_ud(-1)
1390 case (2) ! interactively
1391 ! user input
1392 call writo('Prescribe interactively')
1393 call writo('How many different combinations of &
1394 &toroidal and poloidal mode numbers (N,M)?')
1395 tot_nr_pert = get_int(lim_lo=1)
1396 end select
1397
1398 ! set up n, m and delta
1399 allocate(n_pert(tot_nr_pert+1))
1400 allocate(delta(tot_nr_pert+1,0:0,2)) ! start with only M = 0
1401 delta = 0._dp
1402 nr_n = 0
1403 do jd = 1,tot_nr_pert
1404 select case (pert_type)
1405 case (1) ! file with Fourier modes in geometrical coordinates
1406 ierr = skip_comment(hel_pert_i,&
1407 &file_name=hel_pert_file_name)
1408 chckerr('')
1409 read(hel_pert_i,*,iostat=ierr) n_loc, m_loc, &
1410 &delta_loc
1411 err_msg = 'Could not read file '//&
1412 &trim(hel_pert_file_name)
1413 chckerr(err_msg)
1414 case (2) ! same as 1 but interactively
1415 call writo('For mode '//trim(i2str(jd))//'/'//&
1416 &trim(i2str(tot_nr_pert))//':')
1417 call lvl_ud(1)
1418
1419 ! input
1420 call writo('Toroidal mode number N?')
1421 n_loc = get_int()
1422 call writo('Poloidal mode number M?')
1423 m_loc = get_int()
1424 call writo('Perturbation strength ~ cos('//&
1425 &trim(i2str(m_loc))//' θ - '//&
1426 &trim(i2str(n_loc))//' ζ)?')
1427 delta_loc(1) = get_real()
1428 call writo('Perturbation strength ~ sin('//&
1429 &trim(i2str(m_loc))//' θ - '//&
1430 &trim(i2str(n_loc))//' ζ)?')
1431 delta_loc(2) = get_real()
1432
1433 call lvl_ud(-1)
1434 case (3) ! file with Fourier modes in general coordinates
1435 m_loc = (jd-1)/2 ! so the output is 0,0,1,1,2,2,3,3,4,4,...
1436 delta_loc = pert_map_interp_f(m_loc+1,:)*0.5_dp ! both + and - helicity, so need to divide amplitude by 2
1437 n_loc = pert_map_n_loc
1438 if (mod(jd,2).eq.0) n_loc = -n_loc
1439 end select
1440
1441 ! has N = 0 been prescribed?
1442 if (n_loc.eq.0) zero_n_pert = .true.
1443
1444 ! bundle into n_pert
1445 n_id = nr_n+1 ! default new N in next index
1446 do id = 1,nr_n
1447 if (n_loc.eq.n_pert(id)) n_id = id
1448 end do
1449 if (n_id.gt.nr_n) then ! new N
1450 nr_n = n_id ! increment number of N
1451 n_pert(n_id) = n_loc ! with value n_loc
1452 end if
1453 m_range = [lbound(delta,2),ubound(delta,2)]
1454 if (m_loc.gt.m_range(1) .or. m_loc.lt.m_range(2)) then ! enlarge delta
1455 allocate(delta_copy(tot_nr_pert+1,&
1456 &m_range(1):m_range(2),2))
1457 delta_copy = delta
1458 deallocate(delta)
1459 allocate(delta(tot_nr_pert+1,&
1460 &min(m_range(1),m_loc):max(m_range(2),m_loc),2))
1461 delta = 0._dp
1462 delta(:,m_range(1):m_range(2),:) = delta_copy
1463 deallocate(delta_copy)
1464 end if
1465 delta(n_id,m_loc,:) = delta(n_id,m_loc,:) + delta_loc ! and perturbation amplitude for cos and sin with value delta_loc
1466
1467#if ldebug
1468 if (debug_create_vmec_input) then
1469 call writo('perturbation '//trim(i2str(jd))//&
1470 &' at position '//trim(i2str(n_id))//', adding ('//&
1471 &trim(r2strt(delta(n_id,m_loc,1)))//', '//&
1472 &trim(r2strt(delta(n_id,m_loc,2)))//') at (n,m) = ('//&
1473 &trim(i2str(n_loc))//', '//trim(i2str(m_loc))//')')
1474 call lvl_ud(1)
1475 do kd = lbound(delta,2),ubound(delta,2)
1476 call writo('delta ('//trim(i2str(kd))//') = ('//&
1477 &trim(r2strt(delta(n_id,kd,1)))//', '//&
1478 &trim(r2strt(delta(n_id,kd,2)))//')')
1479 end do
1480 call lvl_ud(-1)
1481 end if
1482#endif
1483 end do
1484
1485 if (pert_type.eq.1 .or. pert_type.eq.3) close(hel_pert_i)
1486
1487 ! ask for global rescaling
1488 ! Note: This is only valid if R(θ=0) is the place with the
1489 ! maximum perturbation
1490 max_pert_on_axis = sum(sqrt(sum(delta**2,3)))
1491 select case (pert_style)
1492 case (1) ! plasma boundary position
1493 call writo('Maximum absolute perturbation on axis: '//&
1494 &trim(r2strt(100*max_pert_on_axis))//'cm')
1495 call writo('Rescale to some value [cm]?')
1496 case (2) ! B_tor magnetic ripple with fixed N
1497 call writo('Maximum relative perturbation on axis: '//&
1498 &trim(r2strt(100*max_pert_on_axis))//'%')
1499 call writo('Rescale to some value [%]?')
1500 end select
1501 mult_fac = max_pert_on_axis
1502 if (get_log(.false.)) then
1503 mult_fac = get_real()
1504 mult_fac = mult_fac * 0.01_dp
1505 end if
1506 delta = delta * mult_fac / max_pert_on_axis
1507
1508 ! add zero to the peturbations if it's not yet there
1509 ! (for the bubble sort)
1510 if (.not.zero_n_pert) then
1511 nr_n = nr_n+1
1512 n_pert(nr_n) = 0
1513 end if
1514 else
1515 nr_n = 1
1516 end if
1517
1518 ! user output
1519 call writo('Set up axisymmetric data')
1520 call lvl_ud(1)
1521
1522 ! calculate output for unperturbed R and Z
1523 nr_m_max = (n_b-1)/2 ! maximum perturbation
1524 if (pert_eq) then
1525 nr_m_max = max(nr_m_max, max(-lbound(delta,2),ubound(delta,2))) ! correct if less than delta
1526 if (pert_style.eq.2) &
1527 &nr_m_max = max(nr_m_max, size(prop_b_tor_interp_f,1)-1) ! correct if less than proportionality map
1528 end if
1529 plot_name(1) = 'R_F'
1530 plot_name(2) = 'Z_F'
1531 allocate(b_f(nr_n,-nr_m_max:nr_m_max,2,2)) ! (tor modes, pol modes, cos/sin (m θ), R/Z)
1532 ! Note: B_F will later be reallocated to run from 0:nr_m_max in index 2
1533 b_f = 0._dp
1534 do kd = 1,2
1535 ! user output
1536 call writo('analyzing '//trim(plot_name(kd)))
1537 call lvl_ud(1)
1538
1539 ! NUFFT
1540 ierr = nufft(theta_b,bh_0(:,kd),b_f_dum,plot_name(kd))
1541 chckerr('')
1542 b_f(1,0:size(b_f_dum,1)-1,:,kd) = b_f_dum
1543
1544#if ldebug
1545 if (debug_create_vmec_input) then
1546 allocate(bh_0_alt(size(bh_0,1),size(b_f_dum,1)))
1547
1548 call writo('Comparing R or Z with reconstruction &
1549 &through Fourier coefficients')
1550 bh_0_alt(:,1) = b_f_dum(1,1)
1551 do id = 1,size(b_f_dum,1)-1
1552 bh_0_alt(:,id+1) = bh_0_alt(:,id) + &
1553 &b_f_dum(id+1,1)*cos(id*theta_b) + &
1554 &b_f_dum(id+1,2)*sin(id*theta_b)
1555 end do
1556 call print_ex_2d(['orig BH','alt BH '],'',&
1557 &reshape([bh_0(:,kd),bh_0_alt(:,size(b_f_dum,1))],&
1558 &[size(bh_0,1),2]),x=&
1559 &reshape([theta_b],[size(bh_0,1),1]))
1560
1561 call writo('Plotting Fourier approximation')
1562 call print_ex_2d(['alt BH'],'TEST_'//trim(plot_name(kd))//&
1563 &'_F_series',bh_0_alt,&
1564 &x=reshape([theta_b],[size(bh_0,1),1]))
1565 call draw_ex(['alt BH'],'TEST_'//trim(plot_name(kd))//&
1566 &'_F_series',size(b_f_dum,1),1,.false.)
1567
1568 call writo('Making animation')
1569 call print_ex_2d(['alt BH'],'TEST_'//trim(plot_name(kd))//&
1570 &'_F_series_anim',bh_0_alt,&
1571 &x=reshape([theta_b],[size(bh_0,1),1]))
1572 call draw_ex(['alt BH'],'TEST_'//trim(plot_name(kd))//&
1573 &'_F_series_anim',size(b_f_dum,1),1,.false.,&
1574 &is_animated=.true.)
1575
1576 deallocate(bh_0_alt)
1577 end if
1578#endif
1579 deallocate(b_f_dum)
1580
1581 call lvl_ud(-1)
1582 end do
1583
1584 ! plot axisymetric boundary in 3D
1585 ! Note: Still using -nr_m_max:nr_m_max for poloidal modes
1586 call plot_boundary(b_f(1:1,:,:,:),[-nr_m_max,nr_m_max],[0],&
1587 &'last_fs',plot_dim,plot_lims)
1588
1589 call lvl_ud(-1)
1590
1591 ! sort and output
1592 if (pert_eq) then
1593 ! user output
1594 call writo('Set up perturbed data')
1595 call lvl_ud(1)
1596
1597 ! sort
1598 allocate(n_pert_copy(nr_n))
1599 allocate(delta_copy(nr_n,-nr_m_max:nr_m_max,2))
1600 delta_copy = 0.0_dp
1601 n_pert_copy = n_pert(1:nr_n)
1602 delta_copy(:,lbound(delta,2):ubound(delta,2),:) = delta(1:nr_n,:,:)
1603 deallocate(n_pert); allocate(n_pert(nr_n)); n_pert = n_pert_copy
1604 deallocate(delta); allocate(delta(nr_n,-nr_m_max:nr_m_max,2))
1605 deallocate(n_pert_copy)
1606 allocate(piv(nr_n))
1607 call bubble_sort(n_pert,piv) ! sort n
1608 do jd = 1,nr_n
1609 delta(jd,:,:) = delta_copy(piv(jd),:,:)
1610 end do
1611 deallocate(piv)
1612
1613 ! update the index of N = 0 in B_F
1614 id_n_0 = minloc(abs(n_pert),1)
1615 if (id_n_0.gt.1) then
1616 b_f(id_n_0,:,:,:) = b_f(1,:,:,:)
1617 b_f(1,:,:,:) = 0._dp
1618 end if
1619
1620 ! output
1621 call writo('Summary of perturbation form:')
1622 call lvl_ud(1)
1623 do jd = 1,nr_n
1624 do id = -nr_m_max,nr_m_max
1625 if (maxval(abs(delta(jd,id,:))).lt.tol_zero) cycle ! this mode has no amplitude
1626 if (n_pert(jd).ge.0) then
1627 pm(1) = '-'
1628 else
1629 pm(1) = '+'
1630 end if
1631 if (delta(jd,id,1).ge.0) then
1632 pm(2) = '+'
1633 else
1634 pm(2) = '-'
1635 end if
1636 if (jd.eq.1 .and. id.eq.1) pm(2) = ''
1637 if (delta(jd,id,2).ge.0) then
1638 pm(3) = '+'
1639 else
1640 pm(3) = '-'
1641 end if
1642 call writo(&
1643 &pm(2)//' '//trim(r2strt(abs(delta(jd,id,1))))//&
1644 &' cos('//trim(i2str(id))//' θ '//pm(1)//' '//&
1645 &trim(i2str(abs(n_pert(jd))))//' ζ) '//&
1646 &pm(3)//' '//trim(r2strt(abs(delta(jd,id,2))))//&
1647 &' sin('//trim(i2str(id))//' θ '//pm(1)//' '//&
1648 &trim(i2str(abs(n_pert(jd))))//' ζ)')
1649 end do
1650 end do
1651 call lvl_ud(-1)
1652
1653 ! loop over all toroidal modes N and include the corresponding
1654 ! perturbation, consisting of terms of the form:
1655 ! δc cos(M θ - N ζ) + δs sin(M θ - N ζ) =
1656 ! δc [cos(M θ)cos(N ζ) + sin(M θ) sin(N ζ) ] +
1657 ! δs [sin(M θ)cos(N ζ) - cos(M θ) sin(N ζ) ].
1658 ! First, however, the δc and δs have to be translated from
1659 ! an absolute modification of the radius r = sqrt(R^2 + Z^2) to an
1660 ! absolute modification of R and Z:
1661 ! ΔR = δ(m) cos(m θ)
1662 ! ΔZ = δ(m) sin(m θ),
1663 ! which simply shifts up and down the index m by one.
1664 ! Additionally, for perturbation type 2 (2D map), there is an extra
1665 ! shift before this.
1666 !
1667 ! These shifts are done separately for each toroidal mode number N,
1668 ! so that the factors cos(θ M) and sin(θ M) are shifted and the
1669 ! resulting corresponding modal amplitudes become δc' and δs' for
1670 ! the term proportional to cos(N ζ):
1671 ! δc cos(M θ) + δs sin(M θ),
1672 ! and δc" and δs" for the term proportional to sin(N ζ):
1673 ! -δs cos(M θ) + δc sin(M θ).
1674 !
1675 ! Finally recombining the results into terms proportional to cos(m
1676 ! θ - N ζ) and sin(m θ - N ζ) then results in
1677 ! (δc'+δs")/2 cos(m θ - N ζ) + (δc'-δs")/2 cos(-m θ - N ζ) +
1678 ! (δs'-δc")/2 sin(m θ - N ζ) - (δs'+δc")/2 sin(-m θ - N ζ),
1679 ! which means that the different toroidal modes N can be treated
1680 ! independently.
1681 !
1682 ! Note that if there is no shifting at all, δc = δc' = δs" and
1683 ! δs = δs' = -δc" and m = M, which indeed reduces to
1684 ! δc cos(M θ - N ζ) + δs sin(M θ - N ζ).
1685 m_range = [-nr_m_max,nr_m_max]
1686 pert_n: do jd = 1,nr_n
1687 ! initialize
1688 allocate(b_f_dum(-nr_m_max:nr_m_max,2))
1689 allocate(b_f_dum2(-nr_m_max:nr_m_max,2))
1690 allocate(b_f_dum3(-nr_m_max:nr_m_max,2))
1691
1692 ! for R, shift due to cos(θ), for Z, shift due to sin(θ)
1693 do kd = 1,2 ! iterate over R (kd = 1), then Z (kd = 2)
1694 b_f_dum = 0._dp
1695 b_f_dum(1,kd) = 1._dp ! cos(θ) for kd = 1, sin(θ) for kd = 2
1696 if (pert_style.eq.2) then ! shift due to proportionality map
1697 b_f_dum2(0:ubound(prop_b_tor_interp_f,1),:) = &
1698 &prop_b_tor_interp_f
1699 call shift_f(m_range,m_range,m_range,&
1700 &b_f_dum,b_f_dum2,b_f_dum3)
1701 b_f_dum = b_f_dum3
1702 end if
1703
1704 ! calculate δ' due to δc cos(M θ) + δs sin(M θ)
1705 b_f_dum2(:,1) = delta(jd,:,1) ! ~ cos
1706 b_f_dum2(:,2) = delta(jd,:,2) ! ~ sin
1707 call shift_f(m_range,m_range,m_range,&
1708 &b_f_dum,b_f_dum2,b_f_dum3)
1709 b_f_dum3 = b_f_dum3*0.5_dp
1710 ! (δc'+δs")/2 cos(m θ - N ζ)
1711 b_f(jd,:,1,kd) = b_f(jd,:,1,kd) + b_f_dum3(:,1)
1712 ! (δs'-δc")/2 sin(m θ - N ζ)
1713 b_f(jd,:,2,kd) = b_f(jd,:,2,kd) + b_f_dum3(:,2)
1714 ! (δc'-δs")/2 cos(-m θ - N ζ)
1715 b_f(jd,:,1,kd) = b_f(jd,:,1,kd) + &
1716 &b_f_dum3(nr_m_max:-nr_m_max:-1,1)
1717 ! -(δs'+δc")/2 sin(-m θ - N ζ)
1718 b_f(jd,:,2,kd) = b_f(jd,:,2,kd) - &
1719 &b_f_dum3(nr_m_max:-nr_m_max:-1,2)
1720
1721 ! calculate δ' due to -δs cos(M θ) + δc sin(M θ)
1722 b_f_dum2(:,1) = -delta(jd,:,2) ! ~ cos
1723 b_f_dum2(:,2) = delta(jd,:,1) ! ~ sin
1724 call shift_f(m_range,m_range,m_range,&
1725 &b_f_dum,b_f_dum2,b_f_dum3)
1726 b_f_dum3 = b_f_dum3*0.5_dp
1727 ! (δc'+δs")/2 cos(m θ - N ζ)
1728 b_f(jd,:,1,kd) = b_f(jd,:,1,kd) + b_f_dum3(:,2)
1729 ! (δs'-δc")/2 sin(m θ - N ζ)
1730 b_f(jd,:,2,kd) = b_f(jd,:,2,kd) - b_f_dum3(:,1)
1731 ! (δc'-δs")/2 cos(-m θ - N ζ)
1732 b_f(jd,:,1,kd) = b_f(jd,:,1,kd) - &
1733 &b_f_dum3(nr_m_max:-nr_m_max:-1,2)
1734 ! -(δs'+δc")/2 sin(-m θ - N ζ)
1735 b_f(jd,:,2,kd) = b_f(jd,:,2,kd) - &
1736 &b_f_dum3(nr_m_max:-nr_m_max:-1,1)
1737 end do
1738
1739 deallocate(b_f_dum)
1740 deallocate(b_f_dum2)
1741 deallocate(b_f_dum3)
1742 end do pert_n
1743
1744 allocate(b_f_copy(2*nr_n,0:nr_m_max,2,2)) ! swap negative m for inverse n
1745 allocate(n_pert_copy(2*nr_n))
1746 b_f_copy = 0._dp
1747 n_pert_copy = 0
1748 nr_n = 0 ! start at zero again
1749 allocate(b_f_dum(0:nr_m_max,2))
1750 pert_n_convert: do jd = 1,size(b_f,1)
1751 do kd = 1,2 ! loop over negative and positive ranges
1752 ! loop over R and Z
1753 do ld = 1,2
1754 ! initialize B_F dummy, then set it up
1755 b_f_dum = 0._dp
1756 if (kd.eq.1) then
1757 ! set local n for negative range: invert n and m
1758 n_loc = -n_pert(jd)
1759 b_f_dum(0:nr_m_max,1) = b_f(jd,0:-nr_m_max:-1,1,ld) ! do not invert cosine factors
1760 b_f_dum(0:nr_m_max,2) = -b_f(jd,0:-nr_m_max:-1,2,ld)! invert sine factors
1761 else
1762 ! set local n for positive range
1763 n_loc = n_pert(jd)
1764 b_f_dum(0:nr_m_max,:) = b_f(jd,0:nr_m_max,:,ld)
1765 end if
1766 b_f_dum(0,:) = b_f_dum(0,:)*0.5_dp ! positive and negative range share half each
1767
1768 if (maxval(abs(b_f_dum)).gt.tol_zero) then
1769 ! bundle into n_pert
1770 n_id = nr_n+1 ! default new N in next index
1771 do id = 1,nr_n ! check all previous, though
1772 if (n_loc.eq.n_pert_copy(id)) n_id = id
1773 end do
1774 if (n_id.gt.nr_n) then ! new N
1775 nr_n = n_id ! increment number of N
1776 n_pert_copy(n_id) = n_loc ! with value n_loc
1777 end if
1778#if ldebug
1779 if (debug_create_vmec_input) then
1780 call writo('putting n = '//trim(i2str(n_loc))//&
1781 &' in index '//trim(i2str(n_id))//':')
1782 if (kd.eq.1) then
1783 call writo('(negative range)')
1784 else
1785 call writo('(positive range)')
1786 end if
1787 if (ld.eq.1) then
1788 call writo('(for R)')
1789 else
1790 call writo('(for Z)')
1791 end if
1792 call lvl_ud(1)
1793 do id = 0,nr_m_max
1794 if (maxval(abs(b_f_dum(id,:)))&
1795 &.gt.tol_zero) then
1796 call writo('B_F ('//trim(i2str(id))//&
1797 &') = ('//&
1798 &trim(r2strt(b_f_dum(id,1)))//&
1799 &', '//&
1800 &trim(r2strt(b_f_dum(id,2)))//')')
1801 end if
1802 end do
1803 call lvl_ud(-1)
1804 end if
1805#endif
1806
1807 ! update B_F_copy
1808 b_f_copy(n_id,:,:,ld) = b_f_copy(n_id,:,:,ld) + &
1809 &b_f_dum
1810 end if
1811 end do
1812 end do
1813 end do pert_n_convert
1814
1815 ! copy back to B_F and n_pert
1816 deallocate(b_f)
1817 deallocate(n_pert)
1818 allocate(b_f(nr_n,0:nr_m_max,2,2))
1819 allocate(n_pert(nr_n))
1820 b_f = b_f_copy(1:nr_n,:,:,:)
1821 n_pert = n_pert_copy(1:nr_n)
1822 deallocate(b_f_copy)
1823 deallocate(n_pert_copy)
1824
1825 ! plot boundary in 3D
1826 ! Note: Using 0:nr_m_max for poloidal modes
1827 call plot_boundary(b_f,[0,nr_m_max],n_pert(1:nr_n),'last_fs_pert',&
1828 &plot_dim,plot_lims)
1829 call lvl_ud(-1)
1830
1831 ! set nfp: save greatest common denominator of N into nfp, excluding
1832 ! N=0
1833 do jd = 2,nr_n
1834 if (jd.eq.2) then
1835 nfp = n_pert(jd)
1836 else
1837 nfp = gcd(nfp,n_pert(jd))
1838 end if
1839 end do
1840 if (nfp.ne.0) then
1841 n_pert = n_pert/nfp
1842 else
1843 nfp = 1
1844 end if
1845 else
1846 ! migrate negative poloidal modes to positive in B_F_copy
1847 allocate(b_f_copy(1,0:nr_m_max,2,2)) ! (tor modes, pol modes, cos/sin (m θ), R/Z)
1848 do id = 0,nr_m_max
1849 b_f_copy(1,id,1,:) = b_f(1,id,1,:) + b_f(1,-id,1,:) ! do not invert cosine factors
1850 b_f_copy(1,id,2,:) = b_f(1,id,2,:) - b_f(1,-id,2,:) ! invert sine factors
1851 end do
1852 b_f_copy(1,0,:,:) = b_f_copy(1,0,:,:)*0.5_dp ! positive and negative range share half each
1853 deallocate(b_f)
1854 allocate(b_f(1,0:nr_m_max,2,2))
1855 b_f = b_f_copy
1856 deallocate(b_f_copy)
1857
1858 ! set the index of N = 0 in B_F
1859 id_n_0 = 1
1860
1861 ! set nfp to one
1862 nfp = 1
1863 end if
1864
1865 ! Note: From now on the dimensions of B_F are (nr_n,0:nr_m_max,2,2)
1866
1867 ! user output
1868 file_name = "input."//trim(eq_name)
1869 if (pert_eq) then
1870 select case (pert_type)
1871 case (1) ! Fourier modes in geometrical coordinates
1872 file_name = trim(file_name)//'_'//&
1873 &trim(hel_pert_file_name)
1874 case (2) ! specified manually
1875 do jd = 1,nr_n
1876 file_name = trim(file_name)//'_N'//&
1877 &trim(i2str(n_pert(jd)))
1878 do kd = 0,nr_m_max
1879 file_name = trim(file_name)//'M'//&
1880 &trim(i2str(kd))
1881 end do
1882 end do
1883 case (3) ! 2-D map
1884 file_name = trim(file_name)//'_2DMAP'
1885 end select
1886 end if
1887 call writo('Generate VMEC input file "'//trim(file_name)//'"')
1888 call writo('This can be used for VMEC porting')
1889 call lvl_ud(1)
1890
1891 ! detect whether there is stellarator symmetry
1892 stel_sym = .false.
1893 if (maxval(abs(b_f(:,:,2,1)))/maxval(abs(b_f(:,:,1,1))) &
1894 &.lt. m_tol .and. & ! R_s << R_c
1895 maxval(abs(b_f(:,:,1,2)))/maxval(abs(b_f(:,:,2,2))) &
1896 &.lt. m_tol) & ! R_c << Z_s
1897 &stel_sym = .true.
1898 if (stel_sym) then
1899 call writo("The equilibrium configuration has stellarator &
1900 &symmetry")
1901 else
1902 call writo("The equilibrium configuration does not have &
1903 &stellarator symmetry")
1904 end if
1905
1906 ! find out how many poloidal modes would be necessary
1907 rec_min_m = 1
1908 norm_b_h = maxval(abs(b_f))
1909 do id = 0,nr_m_max
1910 if (maxval(abs(b_f(:,id,:,1)/norm_b_h)).gt.m_tol .or. & ! for R
1911 &maxval(abs(b_f(:,id,:,2)/norm_b_h)).gt.m_tol) & ! for Z
1912 &rec_min_m = id
1913 end do
1914 call writo("Detected recommended number of poloidal modes: "//&
1915 &trim(i2str(rec_min_m)))
1916
1917 ! possibly get maximum number of modes to plot
1918 max_n_b_output = rec_min_m
1919 call writo('Do you want to change the maximum number of modes &
1920 &from current '//trim(i2str(max_n_b_output))//'?')
1921 change_max_n_b_output = get_log(.false.)
1922 if (change_max_n_b_output) then
1923 call writo('Maximum number of modes to output?')
1924 max_n_b_output = get_int(lim_lo=1)
1925 end if
1926
1927 ! interpolate for VMEC (V) and Jorek (J) output
1928 s_vj = [((kd-1._dp)/(size(s_vj)-1),kd=1,size(s_vj))]
1929 allocate(psi_t(grid_eq%n(3)))
1930 psi_t = eq_1%flux_t_E(:,0)/eq_1%flux_t_E(grid_eq%n(3),0)
1931 do id = 0,1
1932 ierr = spline(psi_t,eq_1%pres_E(:,id),s_vj,pres_vj(:,id),&
1933 &ord=norm_disc_prec_eq)
1934 chckerr('')
1935 end do
1936 if (use_normalization) then
1937 pres_vj = pres_vj*pres_0
1938 pres_vj(:,1) = pres_vj(:,1) / psi_0
1939 end if
1940 ierr = spline(psi_t,rbphi_h(:,0),s_vj,f_vj,ord=norm_disc_prec_eq)
1941 chckerr('')
1942 if (use_normalization) f_vj = f_vj * r_0*b_0
1943 ierr = spline(psi_t,ffp,s_vj,ffp_vj,ord=norm_disc_prec_eq)
1944 chckerr('')
1945 if (use_normalization) then
1946 ffp_vj = ffp_vj * (r_0*b_0)**2/psi_0
1947 end if
1948 ierr = spline(psi_t,flux_p_h(:,0),s_vj,flux_j,ord=norm_disc_prec_eq)
1949 chckerr('')
1950 if (use_normalization) flux_j = flux_j*psi_0
1951 ierr = spline(psi_t,eq_1%q_saf_E(:,0),s_vj,q_saf_vj,&
1952 &ord=norm_disc_prec_eq)
1953 chckerr('')
1954 ierr = spline(psi_t,-eq_1%rot_t_E(:,0),s_vj,rot_t_v,&
1955 &ord=norm_disc_prec_eq)
1956 chckerr('')
1957 ierr = spline(psi_t,i_tor*norm_transf(:,1),s_vj,i_tor_v,&
1958 &ord=norm_disc_prec_eq)
1959 chckerr('')
1960 ierr = spline(psi_t,i_tor*norm_transf(:,2),s_vj,i_tor_j,&
1961 &ord=norm_disc_prec_eq)
1962 chckerr('')
1963 if (use_normalization) i_tor_v = i_tor_v * r_0*b_0/mu_0_original
1964 if (use_normalization) i_tor_j = i_tor_j * r_0*b_0/mu_0_original
1965
1966 ! output to VMEC input file
1967 open(hel_export_i,status='replace',file=trim(file_name),&
1968 &iostat=ierr)
1969 chckerr('Failed to open file')
1970
1971 write(hel_export_i,"(A)") "!----- General Parameters -----"
1972 write(hel_export_i,"(A)") "&INDATA"
1973 write(hel_export_i,"(A)") "MGRID_FILE = 'NONE',"
1974 write(hel_export_i,"(A)") "PRECON_TYPE = 'GMRES'"
1975 write(hel_export_i,"(A)") "PREC2D_THRESHOLD = 1.E-9"
1976 write(hel_export_i,"(A)") "DELT = 1.00E+00,"
1977 write(hel_export_i,"(A)") "NS_ARRAY = 19, 39, 79, 159, 319"
1978 write(hel_export_i,"(A)") "LRFP = F"
1979 write(hel_export_i,"(A,L1)") "LASYM = ", .not.stel_sym
1980 write(hel_export_i,"(A)") "LFREEB = F"
1981 write(hel_export_i,"(A)") "NTOR = "//trim(i2str(nr_n-1)) ! -NTOR .. NTOR
1982 write(hel_export_i,"(A)") "MPOL = "//trim(i2str(max_n_b_output)) ! 0 .. MPOL-1
1983 write(hel_export_i,"(A)") "TCON0 = 1"
1984 write(hel_export_i,"(A)") "FTOL_ARRAY = 1.E-6, 1.E-6, 1.E-8, 1.E-10, &
1985 &5.000E-18,"
1986 write(hel_export_i,"(A)") "NITER = 100000,"
1987 write(hel_export_i,"(A)") "NSTEP = 200,"
1988 write(hel_export_i,"(A)") "NFP = "//trim(i2str(nfp))
1989 if (use_normalization) then
1990 write(hel_export_i,"(A)") "PHIEDGE = "//&
1991 &trim(r2str(-eq_1%flux_t_E(grid_eq%n(3),0)*psi_0))
1992 else
1993 write(hel_export_i,"(A)") "PHIEDGE = "//&
1994 &trim(r2str(-eq_1%flux_t_E(grid_eq%n(3),0)))
1995 end if
1996 write(hel_export_i,"(A)") "CURTOR = "//&
1997 &trim(r2str(i_tor_int(size(i_tor_int))))
1998
1999 write(hel_export_i,"(A)") ""
2000 write(hel_export_i,"(A)") ""
2001
2002 write(hel_export_i,"(A)") "!----- Pressure Parameters -----"
2003 write(hel_export_i,"(A)") "PMASS_TYPE = 'cubic_spline'"
2004 write(hel_export_i,"(A)") "GAMMA = 0.00000000000000E+00"
2005 write(hel_export_i,"(A)") ""
2006 write(hel_export_i,"(A)",advance="no") "AM_AUX_S ="
2007 do kd = 1,size(s_vj)
2008 write(hel_export_i,"(A1,ES23.16)") " ", s_vj(kd)
2009 end do
2010 write(hel_export_i,"(A)") ""
2011 write(hel_export_i,"(A)",advance="no") "AM_AUX_F ="
2012 do kd = 1,size(pres_vj,1)
2013 write(hel_export_i,"(A1,ES23.16)") " ", pres_vj(kd,0)
2014 end do
2015
2016 write(hel_export_i,"(A)") ""
2017 write(hel_export_i,"(A)") ""
2018
2019 write(hel_export_i,"(A)") &
2020 &"!----- Current/Iota Parameters -----"
2021 write(hel_export_i,"(A)") "NCURR = "//trim(i2str(ncurr))
2022 write(hel_export_i,"(A)") ""
2023
2024 ! iota (is used if ncur = 0)
2025 write(hel_export_i,"(A)") "PIOTA_TYPE = 'Cubic_spline'"
2026 write(hel_export_i,"(A)") ""
2027 write(hel_export_i,"(A)",advance="no") "AI_AUX_S ="
2028 do kd = 1,size(s_vj)
2029 write(hel_export_i,"(A1,ES23.16)") " ", s_vj(kd)
2030 end do
2031 write(hel_export_i,"(A)") ""
2032 write(hel_export_i,"(A)",advance="no") "AI_AUX_F ="
2033 do kd = 1,size(rot_t_v)
2034 write(hel_export_i,"(A1,ES23.16)") " ", rot_t_v(kd)
2035 end do
2036 write(hel_export_i,"(A)") ""
2037
2038 ! I_tor (is used if ncur = 1)
2039 write(hel_export_i,"(A)") "PCURR_TYPE = 'cubic_spline_Ip'"
2040 write(hel_export_i,"(A)") ""
2041 write(hel_export_i,"(A)",advance="no") "AC_AUX_S ="
2042 do kd = 1,size(s_vj)
2043 write(hel_export_i,"(A1,ES23.16)") " ", s_vj(kd)
2044 end do
2045 write(hel_export_i,"(A)") ""
2046 write(hel_export_i,"(A)",advance="no") "AC_AUX_F ="
2047 do kd = 1,size(rot_t_v)
2048 write(hel_export_i,"(A1,ES23.16)") " ", i_tor_v(kd)
2049 end do
2050
2051 write(hel_export_i,"(A)") ""
2052 write(hel_export_i,"(A)") ""
2053
2054 write(hel_export_i,"(A)") &
2055 &"!----- Boundary Shape Parameters -----"
2056 ierr = print_mode_numbers(hel_export_i,b_f(id_n_0,:,:,:),0,&
2057 &max_n_b_output)
2058 chckerr('')
2059 if (pert_eq) then
2060 do jd = 1,nr_n
2061 if (jd.eq.id_n_0) cycle ! skip n = 0 as it is already written
2062 ierr = print_mode_numbers(hel_export_i,&
2063 &b_f(jd,:,:,:),n_pert(jd),max_n_b_output)
2064 chckerr('')
2065 end do
2066 end if
2067 write(hel_export_i,"(A,ES23.16)") "RAXIS = ", rz_b_0(1)
2068 write(hel_export_i,"(A,ES23.16)") "ZAXIS = ", rz_b_0(2)
2069 write(hel_export_i,"(A)") "&END"
2070
2071 close(hel_export_i)
2072
2073 ! output to output file for free-boundary coil fitting in JOREK
2074 file_name = "flux_and_boundary_quantities_"//trim(eq_name)//'.jorek'
2075 open(hel_export_i,status='replace',file=trim(file_name),&
2076 &iostat=ierr)
2077 chckerr('Failed to open file')
2078
2079 ! The normal derivatives in FFp are transformed for JOREK output.
2080 ! - The difference between the HELENA coordinate system (used in the E
2081 ! quantities here), and the JOREK coordinate system is the fact that
2082 ! HELENA uses psi_pol/2pi as the norma coordinate while JOREK uses
2083 ! psi_pol/psi_pol(s). This step therefore introduces a factor
2084 ! d/dpsi_J = psi_pol(s)/(2*pi) d/dpsi_H
2085 ! However, JOREK then asks for a quantity that has this exact factor
2086 ! multiplied back into it, so that the end results does not require to
2087 ! be transformed back. The following lines are therefore commented out.
2088 !!FFp_VJ = FFp_VJ * max_flux_E/(2*pi)
2089 !!if (use_normalization) FFp_VJ = FFp_VJ * psi_0 ! to scale max_flux_E
2090 if (use_normalization) then
2091 norm_j = [r_0*rmtog_h,rz_b_0(2),r_0*rmtog_h*b_0*bmtog_h]
2092 else
2093 norm_j = [1._dp,1._dp,1._dp]
2094 end if
2095 write(hel_export_i,"('! ',A)") 'normalization factors:'
2096 write(hel_export_i,"('R_geo = ',ES23.16,' ! [m]')") norm_j(1)
2097 write(hel_export_i,"('Z_geo = ',ES23.16,' ! [m]')") norm_j(2)
2098 write(hel_export_i,"('F0 = ',ES23.16,' ! [Tm]')") norm_j(3)
2099 write(hel_export_i,*) ''
2100 write(hel_export_i,"('! ',7(A23,' '))") 'pol. flux [Tm^2]', &
2101 &'normalized pol. flux []', 'mu_0 pressure [T^2]', 'F [Tm]', &
2102 &'-FFp [T^2 m^2/(Wb/rad)]', 'safety factor []', 'I_tor [A]'
2103 do kd = 1,size(flux_j)
2104 write(hel_export_i,"(' ',7(ES23.16,' '))") flux_j(kd), &
2105 &flux_j(kd)/flux_j(size(flux_j)), mu_0_original*pres_vj(kd,0), &
2106 &f_vj(kd), -ffp_vj(kd), q_saf_vj(kd), i_tor_j(kd)
2107 end do
2108 write(hel_export_i,*) ''
2109 write(hel_export_i,*) '! R [m] and Z [m] of boundary at psi [ ]'
2110 do kd = 1,n_b
2111 write(hel_export_i,"('R_boundary(',I4,') = ',ES23.16,&
2112 &', Z_boundary(',I4,') = ',ES23.16,&
2113 &', psi_boundary(',I4,') = ',ES23.16)") &
2114 &kd, bh_0(kd,1), kd, bh_0(kd,2), kd, 1._dp
2115 end do
2116
2117 close(hel_export_i)
2118
2119 ! output different files
2120 file_name = "jorek_ffprime"
2121 open(hel_export_i,status='replace',file=trim(file_name),&
2122 &iostat=ierr)
2123 chckerr('Failed to open file')
2124 !write(HEL_export_i,"('! ',2(A23,' '))") 'psi_norm [ ]', '-FFp [T]'
2125 do kd = 1,size(flux_j)
2126 write(hel_export_i,"(' ',2(ES23.16,' '))") &
2127 &flux_j(kd)/flux_j(size(flux_j)), -ffp_vj(kd)
2128 end do
2129 close(hel_export_i)
2130 file_name = "jorek_temperature"
2131 open(hel_export_i,status='replace',file=trim(file_name),&
2132 &iostat=ierr)
2133 chckerr('Failed to open file')
2134 !write(HEL_export_i,"('! ',2(A23,' '))") 'psi_norm [ ]', &
2135 !&'mu_0 pressure [T^2]'
2136 do kd = 1,size(flux_j)
2137 write(hel_export_i,"(' ',2(ES23.16,' '))") &
2138 &flux_j(kd)/flux_j(size(flux_j)), mu_0_original*pres_vj(kd,0)
2139 end do
2140 close(hel_export_i)
2141
2142 ! user output
2143 call lvl_ud(-1)
2144
2145 call writo('Done')
2146
2147 ! wait
2148 call pause_prog
2149 contains
2150 ! plots the boundary of a toroidal configuration
2151 !> \private
2152 subroutine plot_boundary(B,m_lims,n,plot_name,plot_dim,plot_lims)
2153 ! input / output
2154 real(dp), intent(in) :: b(1:,0:,1:,1:) ! cosine and sine of fourier series (tor modes, pol modes, cos/sin (m theta_geo), R/Z)
2155 integer, intent(in) :: m_lims(2) ! limits of poloidal mode numbers
2156 integer, intent(in) :: n(:) ! toroidal mode numbers
2157 character(len=*), intent(in) :: plot_name ! name of plot
2158 integer, intent(in) :: plot_dim(2) ! plot dimensions in geometrical angle
2159 real(dp), intent(in) :: plot_lims(2,2) ! limits of plot dimensions [pi]
2160
2161 ! local variables
2162 real(dp), allocatable :: ang_plot(:,:,:) ! angles of plot (theta,zeta)
2163 real(dp), allocatable :: xyz_plot(:,:,:) ! coordinates of boundary (X,Y,Z)
2164 integer :: kd, id ! counters
2165 integer :: m ! local poloidal mode number
2166
2167 ! set variables
2168 allocate(ang_plot(plot_dim(1),plot_dim(2),2)) ! theta and zeta (geometrical)
2169 allocate(xyz_plot(plot_dim(1),plot_dim(2),3)) ! X, Y and Z
2170 xyz_plot = 0._dp
2171
2172 ! create grid
2173 do id = 1,plot_dim(1)
2174 ang_plot(id,:,1) = pi * (plot_lims(1,1) + (id-1._dp)/&
2175 &(plot_dim(1)-1)*(plot_lims(2,1)-plot_lims(1,1)))
2176 end do
2177 do kd = 1,plot_dim(2)
2178 ang_plot(:,kd,2) = pi * (plot_lims(1,2) + (kd-1._dp)/&
2179 &(plot_dim(2)-1)*(plot_lims(2,2)-plot_lims(1,2)))
2180 end do
2181
2182 ! inverse Fourier transform
2183 do id = 1,size(n) ! toroidal modes
2184 do kd = 0,m_lims(2)-m_lims(1) ! poloidal modes
2185 m = m_lims(1)+kd
2186
2187 ! R
2188 xyz_plot(:,:,1) = xyz_plot(:,:,1) + &
2189 &b(id,kd,1,1)*cos(m*ang_plot(:,:,1)-&
2190 &n(id)*ang_plot(:,:,2)) + &
2191 &b(id,kd,2,1)*sin(m*ang_plot(:,:,1)-&
2192 &n(id)*ang_plot(:,:,2))
2193 ! Z
2194 xyz_plot(:,:,3) = xyz_plot(:,:,3) + &
2195 &b(id,kd,1,2)*cos(m*ang_plot(:,:,1)-&
2196 &n(id)*ang_plot(:,:,2)) + &
2197 &b(id,kd,2,2)*sin(m*ang_plot(:,:,1)-&
2198 &n(id)*ang_plot(:,:,2))
2199 end do
2200 end do
2201
2202 ! print cross-section (R,Z)
2203 call print_ex_2d(['cross_section_'//trim(plot_name)],&
2204 &'cross_section_'//trim(plot_name),xyz_plot(:,:,3),&
2205 &x=xyz_plot(:,:,1),draw=.false.)
2206 call draw_ex(['cross_section_'//trim(plot_name)],&
2207 &'cross_section_'//trim(plot_name),plot_dim(2),1,.false.)
2208 call print_ex_2d(['cross_section_'//trim(plot_name)//'_inv'],&
2209 &'cross_section_'//trim(plot_name)//'_inv',&
2210 &transpose(xyz_plot(:,:,3)),x=transpose(xyz_plot(:,:,1)),&
2211 &draw=.false.)
2212 call draw_ex(['cross_section_'//trim(plot_name)//'_inv'],&
2213 &'cross_section_'//trim(plot_name)//'_inv',plot_dim(1),1,&
2214 &.false.)
2215
2216 ! convert to X, Y, Z
2217 xyz_plot(:,:,2) = xyz_plot(:,:,1)*sin(ang_plot(:,:,2))
2218 xyz_plot(:,:,1) = xyz_plot(:,:,1)*cos(ang_plot(:,:,2))
2219
2220 ! print
2221 call print_ex_3d('plasma boundary',trim(plot_name),xyz_plot(:,:,3),&
2222 &x=xyz_plot(:,:,1),y=xyz_plot(:,:,2),draw=.false.)
2223 call draw_ex(['plasma boundary'],trim(plot_name),1,2,.false.)
2224 end subroutine plot_boundary
2225
2226 ! print the mode numbers
2227 !> \private
2228 integer function print_mode_numbers(file_i,B,n_pert,max_n_B_output) &
2229 &result(ierr)
2230 character(*), parameter :: rout_name = 'print_mode_numbers'
2231
2232 ! input / output
2233 integer, intent(in) :: file_i ! file to output flux quantities for VMEC export
2234 real(dp), intent(in) :: b(:,:,:) ! cosine and sine of fourier series for R and Z
2235 integer, intent(in) :: n_pert ! toroidal mode number
2236 integer, intent(in) :: max_n_b_output ! maximum number of modes to output
2237
2238 ! local variables
2239 integer :: kd ! counter
2240 integer :: m ! counter
2241 character :: var_name ! name of variables (R or Z)
2242 character(len=2*max_str_ln) :: temp_output_str ! temporary output string
2243
2244 ! initialize ierr
2245 ierr = 0
2246
2247 do kd = 1,2 ! R and Z
2248 select case (kd)
2249 case (1)
2250 var_name = 'R'
2251 case (2)
2252 var_name = 'Z'
2253 case default
2254 var_name = '?'
2255 end select
2256
2257 ! output to file
2258 do m = 0,min(size(b,1)-1,max_n_b_output)
2259 write(temp_output_str,"(' ',A,'BC(',I4,', ',I4,') = ',&
2260 &ES23.16,' ',A,'BS(',I4,', ',I4,') = ',ES23.16)") &
2261 &var_name,n_pert,m,b(m+1,1,kd), &
2262 &var_name,n_pert,m,b(m+1,2,kd)
2263 write(unit=file_i,fmt='(A)',iostat=ierr) &
2264 &trim(temp_output_str)
2265 chckerr('Failed to write')
2266 end do
2267 write(unit=file_i,fmt="(A)",iostat=ierr) ""
2268 chckerr('Failed to write')
2269 end do
2270 end function print_mode_numbers
2271 end function create_vmec_input
2272
2273 !> \private flux version
2274 integer function print_output_eq_1(grid_eq,eq,data_name) result(ierr)
2275 use num_vars, only: pb3d_name
2276 use hdf5_ops, only: print_hdf5_arrs
2277 use hdf5_vars, only: dealloc_var_1d, var_1d_type, &
2279 use grid_utilities, only: trim_grid
2280
2281 character(*), parameter :: rout_name = 'print_output_eq_1'
2282
2283 ! input / output
2284 type(grid_type), intent(in) :: grid_eq !< equilibrium grid variables
2285 type(eq_1_type), intent(in) :: eq !< flux equilibrium variables
2286 character(len=*), intent(in) :: data_name !< name under which to store
2287
2288 ! local variables
2289 integer :: norm_id(2) ! untrimmed normal indices for trimmed grids
2290 type(var_1d_type), allocatable, target :: eq_1d(:) ! 1D equivalent of eq. variables
2291 type(var_1d_type), pointer :: eq_1d_loc => null() ! local element in eq_1D
2292 type(grid_type) :: grid_trim ! trimmed grid
2293 integer :: id ! counter
2294 integer :: loc_size ! local size
2295
2296 ! initialize ierr
2297 ierr = 0
2298
2299 ! user output
2300 call writo('Write flux equilibrium variables to output file')
2301 call lvl_ud(1)
2302
2303 ! trim grids
2304 ierr = trim_grid(grid_eq,grid_trim,norm_id)
2305 chckerr('')
2306
2307 ! set up the 1D equivalents of the equilibrium variables
2308 allocate(eq_1d(max_dim_var_1d))
2309
2310 ! Set up common variables eq_1D
2311 id = 1
2312
2313 ! pres_FD
2314 eq_1d_loc => eq_1d(id); id = id+1
2315 eq_1d_loc%var_name = 'pres_FD'
2316 allocate(eq_1d_loc%tot_i_min(2),eq_1d_loc%tot_i_max(2))
2317 allocate(eq_1d_loc%loc_i_min(2),eq_1d_loc%loc_i_max(2))
2318 eq_1d_loc%tot_i_min = [1,0]
2319 eq_1d_loc%tot_i_max = [grid_trim%n(3),size(eq%pres_FD,2)-1]
2320 eq_1d_loc%loc_i_min = [grid_trim%i_min,0]
2321 eq_1d_loc%loc_i_max = [grid_trim%i_max,size(eq%pres_FD,2)-1]
2322 loc_size = size(eq%pres_FD(norm_id(1):norm_id(2),:))
2323 allocate(eq_1d_loc%p(loc_size))
2324 eq_1d_loc%p = reshape(eq%pres_FD(norm_id(1):norm_id(2),:),[loc_size])
2325
2326 ! q_saf_FD
2327 eq_1d_loc => eq_1d(id); id = id+1
2328 eq_1d_loc%var_name = 'q_saf_FD'
2329 allocate(eq_1d_loc%tot_i_min(2),eq_1d_loc%tot_i_max(2))
2330 allocate(eq_1d_loc%loc_i_min(2),eq_1d_loc%loc_i_max(2))
2331 eq_1d_loc%tot_i_min = [1,0]
2332 eq_1d_loc%tot_i_max = [grid_trim%n(3),size(eq%q_saf_FD,2)-1]
2333 eq_1d_loc%loc_i_min = [grid_trim%i_min,0]
2334 eq_1d_loc%loc_i_max = [grid_trim%i_max,size(eq%q_saf_FD,2)-1]
2335 loc_size = size(eq%q_saf_FD(norm_id(1):norm_id(2),:))
2336 allocate(eq_1d_loc%p(loc_size))
2337 eq_1d_loc%p = reshape(eq%q_saf_FD(norm_id(1):norm_id(2),:),[loc_size])
2338
2339 ! rot_t_FD
2340 eq_1d_loc => eq_1d(id); id = id+1
2341 eq_1d_loc%var_name = 'rot_t_FD'
2342 allocate(eq_1d_loc%tot_i_min(2),eq_1d_loc%tot_i_max(2))
2343 allocate(eq_1d_loc%loc_i_min(2),eq_1d_loc%loc_i_max(2))
2344 eq_1d_loc%tot_i_min = [1,0]
2345 eq_1d_loc%tot_i_max = [grid_trim%n(3),size(eq%rot_t_FD,2)-1]
2346 eq_1d_loc%loc_i_min = [grid_trim%i_min,0]
2347 eq_1d_loc%loc_i_max = [grid_trim%i_max,size(eq%rot_t_FD,2)-1]
2348 loc_size = size(eq%rot_t_FD(norm_id(1):norm_id(2),:))
2349 allocate(eq_1d_loc%p(loc_size))
2350 eq_1d_loc%p = reshape(eq%rot_t_FD(norm_id(1):norm_id(2),:),[loc_size])
2351
2352 ! flux_p_FD
2353 eq_1d_loc => eq_1d(id); id = id+1
2354 eq_1d_loc%var_name = 'flux_p_FD'
2355 allocate(eq_1d_loc%tot_i_min(2),eq_1d_loc%tot_i_max(2))
2356 allocate(eq_1d_loc%loc_i_min(2),eq_1d_loc%loc_i_max(2))
2357 eq_1d_loc%tot_i_min = [1,0]
2358 eq_1d_loc%tot_i_max = [grid_trim%n(3),size(eq%flux_p_FD,2)-1]
2359 eq_1d_loc%loc_i_min = [grid_trim%i_min,0]
2360 eq_1d_loc%loc_i_max = [grid_trim%i_max,size(eq%flux_p_FD,2)-1]
2361 loc_size = size(eq%flux_p_FD(norm_id(1):norm_id(2),:))
2362 allocate(eq_1d_loc%p(loc_size))
2363 eq_1d_loc%p = reshape(eq%flux_p_FD(norm_id(1):norm_id(2),:),[loc_size])
2364
2365 ! flux_t_FD
2366 eq_1d_loc => eq_1d(id); id = id+1
2367 eq_1d_loc%var_name = 'flux_t_FD'
2368 allocate(eq_1d_loc%tot_i_min(2),eq_1d_loc%tot_i_max(2))
2369 allocate(eq_1d_loc%loc_i_min(2),eq_1d_loc%loc_i_max(2))
2370 eq_1d_loc%tot_i_min = [1,0]
2371 eq_1d_loc%tot_i_max = [grid_trim%n(3),size(eq%flux_t_FD,2)-1]
2372 eq_1d_loc%loc_i_min = [grid_trim%i_min,0]
2373 eq_1d_loc%loc_i_max = [grid_trim%i_max,size(eq%flux_t_FD,2)-1]
2374 loc_size = size(eq%flux_t_FD(norm_id(1):norm_id(2),:))
2375 allocate(eq_1d_loc%p(loc_size))
2376 eq_1d_loc%p = reshape(eq%flux_t_FD(norm_id(1):norm_id(2),:),[loc_size])
2377
2378 ! rho
2379 eq_1d_loc => eq_1d(id); id = id+1
2380 eq_1d_loc%var_name = 'rho'
2381 allocate(eq_1d_loc%tot_i_min(1),eq_1d_loc%tot_i_max(1))
2382 allocate(eq_1d_loc%loc_i_min(1),eq_1d_loc%loc_i_max(1))
2383 eq_1d_loc%tot_i_min = 1
2384 eq_1d_loc%tot_i_max = grid_trim%n(3)
2385 eq_1d_loc%loc_i_min = grid_trim%i_min
2386 eq_1d_loc%loc_i_max = grid_trim%i_max
2387 loc_size = size(eq%rho(norm_id(1):norm_id(2)))
2388 allocate(eq_1d_loc%p(loc_size))
2389 eq_1d_loc%p = eq%rho(norm_id(1):norm_id(2))
2390
2391
2392 ! write
2393 ierr = print_hdf5_arrs(eq_1d(1:id-1),pb3d_name,trim(data_name),&
2394 &ind_print=.not.grid_trim%divided)
2395 chckerr('')
2396
2397 ! clean up
2398 call grid_trim%dealloc()
2399 call dealloc_var_1d(eq_1d)
2400 nullify(eq_1d_loc)
2401
2402 ! user output
2403 call lvl_ud(-1)
2404 end function print_output_eq_1
2405 !> \private metric version
2406 integer function print_output_eq_2(grid_eq,eq,data_name,rich_lvl,par_div,&
2407 &dealloc_vars) result(ierr)
2409 use hdf5_ops, only: print_hdf5_arrs
2410 use hdf5_vars, only: dealloc_var_1d, var_1d_type, &
2412 use grid_utilities, only: trim_grid
2413
2414 character(*), parameter :: rout_name = 'print_output_eq_2'
2415
2416 ! input / output
2417 type(grid_type), intent(in) :: grid_eq !< equilibrium grid variables
2418 type(eq_2_type), intent(inout) :: eq !< metric equilibrium variables
2419 character(len=*), intent(in) :: data_name !< name under which to store
2420 integer, intent(in), optional :: rich_lvl !< Richardson level to print
2421 logical, intent(in), optional :: par_div !< is a parallely divided grid
2422 logical, intent(in), optional :: dealloc_vars !< deallocate variables on the fly after writing
2423
2424 ! local variables
2425 integer :: n_tot(3) ! total n
2426 integer :: par_id(2) ! local parallel interval
2427 integer :: norm_id(2) ! untrimmed normal indices for trimmed grids
2428 type(var_1d_type), allocatable, target :: eq_1d(:) ! 1D equivalent of eq. variables
2429 type(var_1d_type), pointer :: eq_1d_loc => null() ! local element in eq_1D
2430 type(grid_type) :: grid_trim ! trimmed grid
2431 integer :: id ! counter
2432 logical :: par_div_loc ! local par_div
2433 integer :: loc_size ! local size
2434 logical :: dealloc_vars_loc ! local dealloc_vars
2435
2436 ! initialize ierr
2437 ierr = 0
2438
2439 ! user output
2440 call writo('Write metric equilibrium variables to output file')
2441 call lvl_ud(1)
2442
2443 ! trim grids
2444 ierr = trim_grid(grid_eq,grid_trim,norm_id)
2445 chckerr('')
2446
2447 ! set local par_div
2448 par_div_loc = .false.
2449 if (present(par_div)) par_div_loc = par_div
2450
2451 ! set total n and parallel interval
2452 n_tot = grid_trim%n
2453 par_id = [1,n_tot(1)]
2454 if (grid_trim%n(1).gt.0 .and. par_div_loc) then ! total grid includes all equilibrium jobs
2455 n_tot(1) = maxval(eq_jobs_lims)-minval(eq_jobs_lims)+1
2456 par_id = eq_jobs_lims(:,eq_job_nr)
2457 end if
2458
2459 ! set up local dealloc_vars
2460 dealloc_vars_loc = .false.
2461 if (present(dealloc_vars)) dealloc_vars_loc = dealloc_vars
2462
2463 ! set up the 1D equivalents of the equilibrium variables
2464 allocate(eq_1d(max_dim_var_1d))
2465
2466 ! Set up common variables eq_1D
2467 id = 1
2468
2469 ! g_FD
2470 eq_1d_loc => eq_1d(id); id = id+1
2471 eq_1d_loc%var_name = 'g_FD'
2472 allocate(eq_1d_loc%tot_i_min(7),eq_1d_loc%tot_i_max(7))
2473 allocate(eq_1d_loc%loc_i_min(7),eq_1d_loc%loc_i_max(7))
2474 eq_1d_loc%tot_i_min = [1,1,1,1,0,0,0]
2475 eq_1d_loc%tot_i_max = [n_tot,6,size(eq%g_FD,5)-1,size(eq%g_FD,6)-1,&
2476 &size(eq%g_FD,7)-1]
2477 eq_1d_loc%loc_i_min = [par_id(1),1,grid_trim%i_min,1,0,0,0]
2478 eq_1d_loc%loc_i_max = [par_id(2),n_tot(2),grid_trim%i_max,6,&
2479 &size(eq%g_FD,5)-1,size(eq%g_FD,6)-1,size(eq%g_FD,7)-1]
2480 loc_size = size(eq%g_FD(:,:,norm_id(1):norm_id(2),:,:,:,:))
2481 allocate(eq_1d_loc%p(loc_size))
2482 eq_1d_loc%p = reshape(eq%g_FD(:,:,norm_id(1):norm_id(2),:,:,:,:),&
2483 &[loc_size])
2484 if (dealloc_vars_loc) deallocate(eq%g_FD)
2485
2486 ! h_FD
2487 eq_1d_loc => eq_1d(id); id = id+1
2488 eq_1d_loc%var_name = 'h_FD'
2489 allocate(eq_1d_loc%tot_i_min(7),eq_1d_loc%tot_i_max(7))
2490 allocate(eq_1d_loc%loc_i_min(7),eq_1d_loc%loc_i_max(7))
2491 eq_1d_loc%tot_i_min = [1,1,1,1,0,0,0]
2492 eq_1d_loc%tot_i_max = [n_tot,6,size(eq%h_FD,5)-1,size(eq%h_FD,6)-1,&
2493 &size(eq%h_FD,7)-1]
2494 eq_1d_loc%loc_i_min = [par_id(1),1,grid_trim%i_min,1,0,0,0]
2495 eq_1d_loc%loc_i_max = [par_id(2),n_tot(2),grid_trim%i_max,6,&
2496 &size(eq%h_FD,5)-1,size(eq%h_FD,6)-1,size(eq%h_FD,7)-1]
2497 loc_size = size(eq%h_FD(:,:,norm_id(1):norm_id(2),:,:,:,:))
2498 allocate(eq_1d_loc%p(loc_size))
2499 eq_1d_loc%p = reshape(eq%h_FD(:,:,norm_id(1):norm_id(2),:,:,:,:),&
2500 &[loc_size])
2501 if (dealloc_vars_loc) deallocate(eq%h_FD)
2502
2503 ! jac_FD
2504 eq_1d_loc => eq_1d(id); id = id+1
2505 eq_1d_loc%var_name = 'jac_FD'
2506 allocate(eq_1d_loc%tot_i_min(6),eq_1d_loc%tot_i_max(6))
2507 allocate(eq_1d_loc%loc_i_min(6),eq_1d_loc%loc_i_max(6))
2508 eq_1d_loc%tot_i_min = [1,1,1,0,0,0]
2509 eq_1d_loc%tot_i_max = [n_tot,size(eq%jac_FD,4)-1,size(eq%jac_FD,5)-1,&
2510 &size(eq%jac_FD,6)-1]
2511 eq_1d_loc%loc_i_min = [par_id(1),1,grid_trim%i_min,0,0,0]
2512 eq_1d_loc%loc_i_max = [par_id(2),n_tot(2),grid_trim%i_max,&
2513 &size(eq%jac_FD,4)-1,size(eq%jac_FD,5)-1,size(eq%jac_FD,6)-1]
2514 loc_size = size(eq%jac_FD(:,:,norm_id(1):norm_id(2),:,:,:))
2515 allocate(eq_1d_loc%p(loc_size))
2516 eq_1d_loc%p = reshape(eq%jac_FD(:,:,norm_id(1):norm_id(2),:,:,:),&
2517 &[loc_size])
2518 if (dealloc_vars_loc) deallocate(eq%jac_FD)
2519
2520 ! S
2521 eq_1d_loc => eq_1d(id); id = id+1
2522 eq_1d_loc%var_name = 'S'
2523 allocate(eq_1d_loc%tot_i_min(3),eq_1d_loc%tot_i_max(3))
2524 allocate(eq_1d_loc%loc_i_min(3),eq_1d_loc%loc_i_max(3))
2525 eq_1d_loc%tot_i_min = [1,1,1]
2526 eq_1d_loc%tot_i_max = n_tot
2527 eq_1d_loc%loc_i_min = [par_id(1),1,grid_trim%i_min]
2528 eq_1d_loc%loc_i_max = [par_id(2),n_tot(2),grid_trim%i_max]
2529 loc_size = size(eq%S(:,:,norm_id(1):norm_id(2)))
2530 allocate(eq_1d_loc%p(loc_size))
2531 eq_1d_loc%p = reshape(eq%S(:,:,norm_id(1):norm_id(2)),&
2532 &[loc_size])
2533 if (dealloc_vars_loc) deallocate(eq%S)
2534
2535 ! kappa_n
2536 eq_1d_loc => eq_1d(id); id = id+1
2537 eq_1d_loc%var_name = 'kappa_n'
2538 allocate(eq_1d_loc%tot_i_min(3),eq_1d_loc%tot_i_max(3))
2539 allocate(eq_1d_loc%loc_i_min(3),eq_1d_loc%loc_i_max(3))
2540 eq_1d_loc%tot_i_min = [1,1,1]
2541 eq_1d_loc%tot_i_max = n_tot
2542 eq_1d_loc%loc_i_min = [par_id(1),1,grid_trim%i_min]
2543 eq_1d_loc%loc_i_max = [par_id(2),n_tot(2),grid_trim%i_max]
2544 loc_size = size(eq%kappa_n(:,:,norm_id(1):norm_id(2)))
2545 allocate(eq_1d_loc%p(loc_size))
2546 eq_1d_loc%p = reshape(eq%kappa_n(:,:,norm_id(1):norm_id(2)),&
2547 &[loc_size])
2548 if (dealloc_vars_loc) deallocate(eq%kappa_n)
2549
2550 ! kappa_g
2551 eq_1d_loc => eq_1d(id); id = id+1
2552 eq_1d_loc%var_name = 'kappa_g'
2553 allocate(eq_1d_loc%tot_i_min(3),eq_1d_loc%tot_i_max(3))
2554 allocate(eq_1d_loc%loc_i_min(3),eq_1d_loc%loc_i_max(3))
2555 eq_1d_loc%tot_i_min = [1,1,1]
2556 eq_1d_loc%tot_i_max = n_tot
2557 eq_1d_loc%loc_i_min = [par_id(1),1,grid_trim%i_min]
2558 eq_1d_loc%loc_i_max = [par_id(2),n_tot(2),grid_trim%i_max]
2559 loc_size = size(eq%kappa_g(:,:,norm_id(1):norm_id(2)))
2560 allocate(eq_1d_loc%p(loc_size))
2561 eq_1d_loc%p = reshape(eq%kappa_g(:,:,norm_id(1):norm_id(2)),&
2562 &[loc_size])
2563 if (dealloc_vars_loc) deallocate(eq%kappa_g)
2564
2565 ! sigma
2566 eq_1d_loc => eq_1d(id); id = id+1
2567 eq_1d_loc%var_name = 'sigma'
2568 allocate(eq_1d_loc%tot_i_min(3),eq_1d_loc%tot_i_max(3))
2569 allocate(eq_1d_loc%loc_i_min(3),eq_1d_loc%loc_i_max(3))
2570 eq_1d_loc%tot_i_min = [1,1,1]
2571 eq_1d_loc%tot_i_max = n_tot
2572 eq_1d_loc%loc_i_min = [par_id(1),1,grid_trim%i_min]
2573 eq_1d_loc%loc_i_max = [par_id(2),n_tot(2),grid_trim%i_max]
2574 loc_size = size(eq%sigma(:,:,norm_id(1):norm_id(2)))
2575 allocate(eq_1d_loc%p(loc_size))
2576 eq_1d_loc%p = reshape(eq%sigma(:,:,norm_id(1):norm_id(2)),&
2577 &[loc_size])
2578 if (dealloc_vars_loc) deallocate(eq%sigma)
2579
2580 ! write
2581 ierr = print_hdf5_arrs(eq_1d(1:id-1),pb3d_name,trim(data_name),&
2582 &rich_lvl=rich_lvl,ind_print=.not.grid_trim%divided)
2583 chckerr('')
2584
2585 ! clean up
2586 call grid_trim%dealloc()
2587 call dealloc_var_1d(eq_1d)
2588 nullify(eq_1d_loc)
2589
2590 ! user output
2591 call lvl_ud(-1)
2592 end function print_output_eq_2
2593
2594 !> \private flux version
2595 integer function redistribute_output_eq_1(grid,grid_out,eq,eq_out) &
2596 &result(ierr)
2598
2599 character(*), parameter :: rout_name = 'redistribute_output_eq_1'
2600
2601 ! input / output
2602 type(grid_type), intent(in) :: grid !< equilibrium grid variables
2603 type(grid_type), intent(in) :: grid_out !< redistributed equilibrium grid variables
2604 type(eq_1_type), intent(in) :: eq !< flux equilibrium variables
2605 type(eq_1_type), intent(inout) :: eq_out !< flux equilibrium variables in redistributed grid
2606
2607 ! local variables
2608 integer :: id ! counter
2609
2610 ! initialize ierr
2611 ierr = 0
2612
2613 ! user output
2614 call writo('Redistribute flux equilibrium variables')
2615 call lvl_ud(1)
2616
2617 ! test
2618 if (grid%n(1).ne.grid_out%n(1) .or. grid%n(2).ne.grid_out%n(2)) then
2619 ierr = 1
2620 chckerr('grid and grid_out are not compatible')
2621 end if
2622
2623 ! create redistributed flux equilibrium variables
2624 call eq_out%init(grid_out,setup_e=.false.,setup_f=.true.)
2625
2626 ! for all derivatives
2627 do id = 0,max_deriv+1
2628 ! pres_FD
2629 ierr = redistribute_var(eq%pres_FD(:,id),eq_out%pres_FD(:,id),&
2630 &[grid%i_min,grid%i_max],[grid_out%i_min,grid_out%i_max])
2631 chckerr('')
2632
2633 ! q_saf_FD
2634 ierr = redistribute_var(eq%q_saf_FD(:,id),eq_out%q_saf_FD(:,id),&
2635 &[grid%i_min,grid%i_max],[grid_out%i_min,grid_out%i_max])
2636 chckerr('')
2637
2638 ! rot_t_FD
2639 ierr = redistribute_var(eq%rot_t_FD(:,id),eq_out%rot_t_FD(:,id),&
2640 &[grid%i_min,grid%i_max],[grid_out%i_min,grid_out%i_max])
2641 chckerr('')
2642
2643 ! flux_p_FD
2644 ierr = redistribute_var(eq%flux_p_FD(:,id),eq_out%flux_p_FD(:,id),&
2645 &[grid%i_min,grid%i_max],[grid_out%i_min,grid_out%i_max])
2646 chckerr('')
2647
2648 ! flux_t_FD
2649 ierr = redistribute_var(eq%flux_t_FD(:,id),eq_out%flux_t_FD(:,id),&
2650 &[grid%i_min,grid%i_max],[grid_out%i_min,grid_out%i_max])
2651 chckerr('')
2652 end do
2653
2654 ! rho
2655 ierr = redistribute_var(eq%rho,eq_out%rho,&
2656 &[grid%i_min,grid%i_max],[grid_out%i_min,grid_out%i_max])
2657 chckerr('')
2658
2659 ! user output
2660 call lvl_ud(-1)
2661 end function redistribute_output_eq_1
2662 !> \private metric version
2663 integer function redistribute_output_eq_2(grid,grid_out,eq,eq_out) &
2664 &result(ierr)
2666
2667 character(*), parameter :: rout_name = 'redistribute_output_eq_2'
2668
2669 ! input / output
2670 type(grid_type), intent(in) :: grid !< equilibrium grid variables
2671 type(grid_type), intent(in) :: grid_out !< redistributed equilibrium grid variables
2672 type(eq_2_type), intent(in) :: eq !< metric equilibrium variables
2673 type(eq_2_type), intent(inout) :: eq_out !< metric equilibrium variables in redistributed grid
2674
2675 ! local variables
2676 integer :: id, jd, kd, ld ! counters
2677 integer :: lims(2), lims_dis(2) ! limits and distributed limits, taking into account the angular extent
2678 integer :: siz(3), siz_dis(3) ! size for geometric part of variable
2679 real(dp), allocatable :: temp_var(:) ! temporary variable
2680
2681 ! initialize ierr
2682 ierr = 0
2683
2684 ! user output
2685 call writo('Redistribute metric equilibrium variables')
2686 call lvl_ud(1)
2687
2688 ! test
2689 if (grid%n(1).ne.grid_out%n(1) .or. grid%n(2).ne.grid_out%n(2)) then
2690 ierr = 1
2691 chckerr('grid and grid_out are not compatible')
2692 end if
2693
2694 ! create redistributed metric equilibrium variables
2695 call eq_out%init(grid_out,setup_e=.false.,setup_f=.true.)
2696
2697 ! set up limits taking into account angular extent and temporary var
2698 lims(1) = product(grid%n(1:2))*(grid%i_min-1)+1
2699 lims(2) = product(grid%n(1:2))*grid%i_max
2700 lims_dis(1) = product(grid%n(1:2))*(grid_out%i_min-1)+1
2701 lims_dis(2) = product(grid%n(1:2))*grid_out%i_max
2702 siz = [grid%n(1:2),grid%loc_n_r]
2703 siz_dis = [grid_out%n(1:2),grid_out%loc_n_r]
2704 allocate(temp_var(product(siz_dis)))
2705
2706 ! for all derivatives and metric factors
2707 do jd = 0,max_deriv
2708 do kd = 0,max_deriv
2709 do ld = 0,max_deriv
2710 do id = 1,size(eq%g_FD,4)
2711 ! g_FD
2712 ierr = redistribute_var(reshape(&
2713 &eq%g_FD(:,:,:,id,jd,kd,ld),[product(siz)]),&
2714 &temp_var,lims,lims_dis)
2715 chckerr('')
2716 eq_out%g_FD(:,:,:,id,jd,kd,ld) = &
2717 &reshape(temp_var,siz_dis)
2718
2719 ! h_FD
2720 ierr = redistribute_var(reshape(&
2721 &eq%h_FD(:,:,:,id,jd,kd,ld),[product(siz)]),&
2722 &temp_var,lims,lims_dis)
2723 chckerr('')
2724 eq_out%h_FD(:,:,:,id,jd,kd,ld) = &
2725 &reshape(temp_var,siz_dis)
2726 end do
2727 ! jac_FD
2728 ierr = redistribute_var(reshape(&
2729 &eq%jac_FD(:,:,:,jd,kd,ld),[product(siz)]),&
2730 &temp_var,lims,lims_dis)
2731 chckerr('')
2732 eq_out%jac_FD(:,:,:,jd,kd,ld) = &
2733 &reshape(temp_var,siz_dis)
2734 end do
2735 end do
2736 end do
2737
2738 ! S
2739 ierr = redistribute_var(reshape(eq%S,[product(siz)]),temp_var,&
2740 &lims,lims_dis)
2741 chckerr('')
2742 eq_out%S = reshape(temp_var,siz_dis)
2743
2744 ! kappa_n
2745 ierr = redistribute_var(reshape(eq%kappa_n,[product(siz)]),temp_var,&
2746 &lims,lims_dis)
2747 chckerr('')
2748 eq_out%kappa_n = reshape(temp_var,siz_dis)
2749
2750 ! kappa_g
2751 ierr = redistribute_var(reshape(eq%kappa_g,[product(siz)]),temp_var,&
2752 &lims,lims_dis)
2753 chckerr('')
2754 eq_out%kappa_g = reshape(temp_var,siz_dis)
2755
2756 ! sigma
2757 ierr = redistribute_var(reshape(eq%sigma,[product(siz)]),temp_var,&
2758 &lims,lims_dis)
2759 chckerr('')
2760 eq_out%sigma = reshape(temp_var,siz_dis)
2761
2762 ! user output
2763 call lvl_ud(-1)
2764 end function redistribute_output_eq_2
2765
2766 !> \private individual version
2767 integer function calc_rzl_ind(grid_eq,eq,deriv) result(ierr)
2768 use vmec_utilities, only: fourier2real
2769 use vmec_vars, only: r_v_c, r_v_s, z_v_c, z_v_s, l_v_c, l_v_s, is_asym_v
2770
2771 character(*), parameter :: rout_name = 'calc_RZL_ind'
2772
2773 ! input / output
2774 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
2775 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
2776 integer, intent(in) :: deriv(3) !< derivatives
2777
2778 ! initialize ierr
2779 ierr = 0
2780
2781#if ldebug
2782 ! check the derivatives requested
2783 ierr = check_deriv(deriv,max_deriv+1,'calc_RZL')
2784 chckerr('')
2785#endif
2786
2787 ! calculate the variables R,Z and their derivatives
2788 ierr = fourier2real(r_v_c(:,grid_eq%i_min:grid_eq%i_max,deriv(1)),&
2789 &r_v_s(:,grid_eq%i_min:grid_eq%i_max,deriv(1)),&
2790 &grid_eq%trigon_factors,eq%R_E(:,:,:,deriv(1),deriv(2),deriv(3)),&
2791 &sym=[.true.,is_asym_v],deriv=[deriv(2),deriv(3)])
2792 chckerr('')
2793 ierr = fourier2real(z_v_c(:,grid_eq%i_min:grid_eq%i_max,deriv(1)),&
2794 &z_v_s(:,grid_eq%i_min:grid_eq%i_max,deriv(1)),&
2795 &grid_eq%trigon_factors,eq%Z_E(:,:,:,deriv(1),deriv(2),deriv(3)),&
2796 &sym=[is_asym_v,.true.],deriv=[deriv(2),deriv(3)])
2797 chckerr('')
2798 ierr = fourier2real(l_v_c(:,grid_eq%i_min:grid_eq%i_max,deriv(1)),&
2799 &l_v_s(:,grid_eq%i_min:grid_eq%i_max,deriv(1)),&
2800 &grid_eq%trigon_factors,eq%L_E(:,:,:,deriv(1),deriv(2),deriv(3)),&
2801 &sym=[is_asym_v,.true.],deriv=[deriv(2),deriv(3)])
2802 chckerr('')
2803 end function calc_rzl_ind
2804 !> \private array version
2805 integer function calc_rzl_arr(grid_eq,eq,deriv) result(ierr)
2806 character(*), parameter :: rout_name = 'calc_RZL_arr'
2807
2808 ! input / output
2809 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
2810 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
2811 integer, intent(in) :: deriv(:,:) !< derivatives
2812
2813 ! local variables
2814 integer :: id
2815
2816 ! initialize ierr
2817 ierr = 0
2818
2819 do id = 1, size(deriv,2)
2820 ierr = calc_rzl_ind(grid_eq,eq,deriv(:,id))
2821 chckerr('')
2822 end do
2823 end function calc_rzl_arr
2824
2825 !> \private individual version
2826 integer function calc_g_c_ind(eq,deriv) result(ierr)
2827 use num_utilities, only: add_arr_mult, c
2828
2829 character(*), parameter :: rout_name = 'calc_g_C_ind'
2830
2831 ! input / output
2832 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
2833 integer, intent(in) :: deriv(:) !< derivatives
2834
2835 ! initialize ierr
2836 ierr = 0
2837
2838#if ldebug
2839 ! check the derivatives requested
2840 ierr = check_deriv(deriv,max_deriv,'calc_g_C')
2841 chckerr('')
2842#endif
2843
2844 ! initialize
2845 eq%g_C(:,:,:,:,deriv(1),deriv(2),deriv(3)) = 0._dp
2846
2847 ! calculate
2848 if (sum(deriv).eq.0) then
2849 eq%g_C(:,:,:,c([1,1],.true.),deriv(1),deriv(2),deriv(3)) = 1.0_dp
2850 eq%g_C(:,:,:,c([3,3],.true.),deriv(1),deriv(2),deriv(3)) = 1.0_dp
2851 end if
2852 ierr = add_arr_mult(eq%R_E,eq%R_E,&
2853 &eq%g_C(:,:,:,c([2,2],.true.),deriv(1),deriv(2),deriv(3)),deriv)
2854 chckerr('')
2855 end function calc_g_c_ind
2856 !> \private array version
2857 integer function calc_g_c_arr(eq,deriv) result(ierr)
2858 character(*), parameter :: rout_name = 'calc_g_C_arr'
2859
2860 ! input / output
2861 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
2862 integer, intent(in) :: deriv(:,:) !< derivatives
2863
2864 ! local variables
2865 integer :: id
2866
2867 ! initialize ierr
2868 ierr = 0
2869
2870 do id = 1, size(deriv,2)
2871 ierr = calc_g_c_ind(eq,deriv(:,id))
2872 chckerr('')
2873 end do
2874 end function calc_g_c_arr
2875
2876 !> \private individual version
2877 integer function calc_g_v_ind(eq,deriv) result(ierr)
2878 use eq_utilities, only: calc_g
2879
2880 character(*), parameter :: rout_name = 'calc_g_V_ind'
2881
2882 ! input / output
2883 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
2884 integer, intent(in) :: deriv(:) !< derivatives
2885
2886 ! initialize ierr
2887 ierr = 0
2888
2889#if ldebug
2890 ! check the derivatives requested
2891 ierr = check_deriv(deriv,max_deriv,'calc_g_V')
2892 chckerr('')
2893#endif
2894
2895 ierr = calc_g(eq%g_C,eq%T_VC,eq%g_E,deriv,max_deriv)
2896 chckerr('')
2897 end function calc_g_v_ind
2898 !> \private array version
2899 integer function calc_g_v_arr(eq,deriv) result(ierr)
2900 character(*), parameter :: rout_name = 'calc_g_V_arr'
2901
2902 ! input / output
2903 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
2904 integer, intent(in) :: deriv(:,:) !< derivatives
2905
2906 ! local variables
2907 integer :: id
2908
2909 ! initialize ierr
2910 ierr = 0
2911
2912 do id = 1, size(deriv,2)
2913 ierr = calc_g_v_ind(eq,deriv(:,id))
2914 chckerr('')
2915 end do
2916 end function calc_g_v_arr
2917
2918 !> \private individual version
2919 integer function calc_g_h_ind(grid_eq,eq,deriv) result(ierr)
2920 use num_vars, only: norm_disc_prec_eq
2921 use helena_vars, only: ias, nchi, r_h, z_h, chi_h, h_h_33
2922 use num_utilities, only: c, spline
2923 use ezspline_obj
2924 use ezspline
2925
2926 character(*), parameter :: rout_name = 'calc_g_H_ind'
2927
2928 ! input / output
2929 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
2930 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
2931 integer, intent(in) :: deriv(:) !< derivatives
2932
2933 ! local variables
2934 type(ezspline2_r8) :: f_spl(2) ! spline object for interpolation, even and odd
2935 character(len=max_str_ln) :: err_msg ! error message
2936 integer :: id, jd, kd, ld ! counters
2937 integer :: bcs(2,3) ! boundary conditions (theta(even), theta(odd), r)
2938 integer :: bc_ld(6) ! boundary condition type for each metric element
2939 real(dp) :: bcs_val(2,3) ! values for boundary conditions
2940 real(dp), allocatable :: rchi(:,:), rpsi(:,:) ! chi and psi derivatives of R
2941 real(dp), allocatable :: zchi(:,:), zpsi(:,:) ! chi and psi derivatives of Z
2942
2943 ! initialize ierr
2944 ierr = 0
2945
2946#if ldebug
2947 ! check the derivatives requested
2948 ierr = check_deriv(deriv,max_deriv,'calc_g_H')
2949 chckerr('')
2950#endif
2951
2952 ! set up boundary conditions
2953 if (ias.eq.0) then ! top-bottom symmetric
2954 bcs(:,1) = [1,1] ! theta(even): zero first derivative
2955 bcs(:,2) = [2,2] ! theta(odd): zero first derivative
2956 else
2957 bcs(:,1) = [-1,-1] ! theta(even): periodic
2958 bcs(:,2) = [-1,-1] ! theta(odd): periodic
2959 end if
2960 bcs(:,3) = [0,0] ! r: not-a-knot
2961 bcs_val = 0._dp
2962
2963 ! boundary condition type for each metric element
2964 bc_ld = [1,2,0,1,0,1] ! 1: even, 2: odd, 0: zero
2965
2966 ! initialize
2967 eq%g_E(:,:,:,:,deriv(1),deriv(2),deriv(3)) = 0._dp
2968
2969 if (sum(deriv).eq.0) then ! no derivatives
2970 ! initialize variables
2971 allocate(rchi(nchi,grid_eq%loc_n_r),rpsi(nchi,grid_eq%loc_n_r))
2972 allocate(zchi(nchi,grid_eq%loc_n_r),zpsi(nchi,grid_eq%loc_n_r))
2973
2974 ! normal derivatives
2975 do id = 1,grid_eq%n(1)
2976 ierr = spline(grid_eq%loc_r_E,&
2977 &r_h(id,grid_eq%i_min:grid_eq%i_max),grid_eq%loc_r_E,&
2978 &rpsi(id,:),ord=norm_disc_prec_eq,deriv=1,bcs=bcs(:,3),&
2979 &bcs_val=bcs_val(:,3))
2980 chckerr('')
2981 ierr = spline(grid_eq%loc_r_E,&
2982 &z_h(id,grid_eq%i_min:grid_eq%i_max),grid_eq%loc_r_E,&
2983 &zpsi(id,:),ord=norm_disc_prec_eq,deriv=1,bcs=bcs(:,3),&
2984 &bcs_val=bcs_val(:,3))
2985 chckerr('')
2986 end do
2987
2988 ! poloidal derivatives
2989 do kd = 1,grid_eq%loc_n_r
2990 ierr = spline(chi_h,r_h(:,grid_eq%i_min-1+kd),chi_h,&
2991 &rchi(:,kd),ord=norm_disc_prec_eq,deriv=1,bcs=bcs(:,1),&
2992 &bcs_val=bcs_val(:,1)) ! even
2993 chckerr('')
2994 ierr = spline(chi_h,z_h(:,grid_eq%i_min-1+kd),chi_h,&
2995 &zchi(:,kd),ord=norm_disc_prec_eq,deriv=1,bcs=bcs(:,2),&
2996 &bcs_val=bcs_val(:,2)) ! odd
2997 chckerr('')
2998 end do
2999
3000 ! set up g_H
3001 do jd = 1,grid_eq%n(2)
3002 eq%g_E(:,jd,:,c([1,1],.true.),0,0,0) = rpsi*rpsi+zpsi*zpsi
3003 eq%g_E(:,jd,:,c([1,2],.true.),0,0,0) = rpsi*rchi+zpsi*zchi
3004 eq%g_E(:,jd,:,c([2,2],.true.),0,0,0) = rpsi*rchi+zpsi*zchi
3005 eq%g_E(:,jd,:,c([2,2],.true.),0,0,0) = rchi*rchi+zchi*zchi
3006 eq%g_E(:,jd,:,c([3,3],.true.),0,0,0) = &
3007 &1._dp/h_h_33(:,grid_eq%i_min:grid_eq%i_max)
3008 end do
3009
3010 ! clean up
3011 deallocate(zchi,rchi,zpsi,rpsi)
3012 else if (deriv(3).ne.0) then ! axisymmetry: deriv. in tor. coord. is zero
3013 !eq%g_E(:,:,:,:,deriv(1),deriv(2),deriv(3)) = 0.0_dp
3014 else if (deriv(1).le.2 .and. deriv(2).le.2) then
3015 ! initialize 2-D cubic spline for even (1) and odd (2) quantities
3016 do ld = 1,2
3017 call ezspline_init(f_spl(ld),grid_eq%n(1),grid_eq%loc_n_r,&
3018 &bcs(:,ld),bcs(:,3),ierr)
3019 call ezspline_error(ierr)
3020 chckerr('')
3021
3022 ! set grid
3023 f_spl(ld)%x1 = chi_h
3024 f_spl(ld)%x2 = grid_eq%loc_r_E
3025
3026 ! set boundary conditions
3027 f_spl(ld)%bcval1min = bcs_val(1,ld)
3028 f_spl(ld)%bcval1max = bcs_val(2,ld)
3029 f_spl(ld)%bcval2min = bcs_val(1,3)
3030 f_spl(ld)%bcval2max = bcs_val(2,3)
3031 end do
3032
3033 do ld = 1,6
3034 do jd = 1,grid_eq%n(2)
3035 if (bc_ld(ld).eq.0) cycle ! quantity is zero
3036
3037 ! set up
3038 call ezspline_setup(f_spl(bc_ld(ld)),&
3039 &eq%g_E(:,jd,:,ld,0,0,0),ierr,exact_dim=.true.) ! match exact dimensions, none of them old Fortran bullsh*t!
3040 call ezspline_error(ierr)
3041 chckerr('')
3042
3043 ! derivate
3044 ! Note: deriv is the other way around because poloidal
3045 ! indices come before normal
3046 call ezspline_derivative(f_spl(bc_ld(ld)),deriv(2),&
3047 &deriv(1),grid_eq%n(1),grid_eq%loc_n_r,chi_h,&
3048 &grid_eq%loc_r_E,eq%g_E(:,jd,:,ld,deriv(1),deriv(2),0),&
3049 &ierr)
3050 call ezspline_error(ierr)
3051 chckerr('')
3052 end do
3053 end do
3054
3055 ! free
3056 do ld = 1,2
3057 call ezspline_free(f_spl(ld),ierr)
3058 call ezspline_error(ierr)
3059 end do
3060 else
3061 ierr = 1
3062 err_msg = 'Derivative of order ('//trim(i2str(deriv(1)))//','//&
3063 &trim(i2str(deriv(2)))//','//trim(i2str(deriv(3)))//'&
3064 &) not supported'
3065 chckerr(err_msg)
3066 end if
3067 end function calc_g_h_ind
3068 !> \private array version
3069 integer function calc_g_h_arr(grid_eq,eq,deriv) result(ierr)
3070 character(*), parameter :: rout_name = 'calc_g_H_arr'
3071
3072 ! input / output
3073 type(grid_type), intent(in) :: grid_eq !< equilibirum grid
3074 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
3075 integer, intent(in) :: deriv(:,:) !< derivatives
3076
3077 ! local variables
3078 integer :: id
3079
3080 ! initialize ierr
3081 ierr = 0
3082
3083 do id = 1, size(deriv,2)
3084 ierr = calc_g_h_ind(grid_eq,eq,deriv(:,id))
3085 chckerr('')
3086 end do
3087 end function calc_g_h_arr
3088
3089 !> \private individual version
3090 integer function calc_g_f_ind(eq,deriv) result(ierr)
3091 use eq_utilities, only: calc_g
3092
3093 character(*), parameter :: rout_name = 'calc_g_F_ind'
3094
3095 ! input / output
3096 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
3097 integer, intent(in) :: deriv(:) !< derivatives
3098
3099 ! initialize ierr
3100 ierr = 0
3101
3102#if ldebug
3103 ! check the derivatives requested
3104 ierr = check_deriv(deriv,max_deriv,'calc_g_F')
3105 chckerr('')
3106#endif
3107
3108 ! calculate g_F
3109 ierr = calc_g(eq%g_E,eq%T_FE,eq%g_F,deriv,max_deriv)
3110 chckerr('')
3111 end function calc_g_f_ind
3112 !> \private array version
3113 integer function calc_g_f_arr(eq,deriv) result(ierr)
3114 character(*), parameter :: rout_name = 'calc_g_F_arr'
3115
3116 ! input / output
3117 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
3118 integer, intent(in) :: deriv(:,:) !< derivatives
3119
3120 ! local variables
3121 integer :: id
3122
3123 ! initialize ierr
3124 ierr = 0
3125
3126 do id = 1, size(deriv,2)
3127 ierr = calc_g_f_ind(eq,deriv(:,id))
3128 chckerr('')
3129 end do
3130 end function calc_g_f_arr
3131
3132 !> \private individual version
3133 integer function calc_jac_v_ind(grid,eq,deriv) result(ierr)
3134 use vmec_utilities, only: fourier2real
3135 use vmec_vars, only: jac_v_c, jac_v_s, is_asym_v
3136
3137 character(*), parameter :: rout_name = 'calc_jac_V_ind'
3138
3139 ! input / output
3140 type(grid_type), intent(in) :: grid !< grid for which to calculate Jacobian
3141 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
3142 integer, intent(in) :: deriv(:) !< derivatives
3143
3144 ! initialize ierr
3145 ierr = 0
3146
3147#if ldebug
3148 ! check the derivatives requested
3149 ierr = check_deriv(deriv,max_deriv,'calc_J_V')
3150 chckerr('')
3151#endif
3152
3153 ! initialize
3154 eq%jac_E(:,:,:,deriv(1),deriv(2),deriv(3)) = 0.0_dp
3155
3156 ! calculate the Jacobian and its derivatives
3157 ierr = fourier2real(jac_v_c(:,grid%i_min:grid%i_max,deriv(1)),&
3158 &jac_v_s(:,grid%i_min:grid%i_max,deriv(1)),grid%trigon_factors,&
3159 &eq%jac_E(:,:,:,deriv(1),deriv(2),deriv(3)),&
3160 &sym=[.true.,is_asym_v],deriv=[deriv(2),deriv(3)])
3161 chckerr('')
3162 end function calc_jac_v_ind
3163 !> \private array version
3164 integer function calc_jac_v_arr(grid,eq,deriv) result(ierr)
3165 character(*), parameter :: rout_name = 'calc_jac_V_arr'
3166
3167 ! input / output
3168 type(grid_type), intent(in) :: grid !< grid for which to calculate Jacobian
3169 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
3170 integer, intent(in) :: deriv(:,:) !< derivatives
3171
3172 ! local variables
3173 integer :: id
3174
3175 ! initialize ierr
3176 ierr = 0
3177
3178 do id = 1, size(deriv,2)
3179 ierr = calc_jac_v_ind(grid,eq,deriv(:,id))
3180 chckerr('')
3181 end do
3182 end function calc_jac_v_arr
3183
3184 !> \private individual version
3185 integer function calc_jac_h_ind(grid_eq,eq_1,eq_2,deriv) result(ierr)
3186 use helena_vars, only: ias, h_h_33, rbphi_h, chi_h
3187 use ezspline_obj
3188 use ezspline
3189
3190 character(*), parameter :: rout_name = 'calc_jac_H_ind'
3191
3192 ! input / output
3193 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
3194 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium
3195 type(eq_2_type), intent(inout) :: eq_2 !< metric equilibrium
3196 integer, intent(in) :: deriv(:) !< derivatives
3197
3198 ! local variables
3199 character(len=max_str_ln) :: err_msg ! error message
3200 type(ezspline2_r8) :: f_spl ! spline object for interpolation
3201 integer :: jd, kd ! counters
3202 integer :: bcs(2,2) ! boundary conditions (theta(even), r)
3203 real(dp) :: bcs_val(2,2) ! values for boundary conditions
3204
3205 ! initialize ierr
3206 ierr = 0
3207
3208#if ldebug
3209 ! check the derivatives requested
3210 ierr = check_deriv(deriv,max_deriv,'calc_jac_H')
3211 chckerr('')
3212#endif
3213
3214 ! set up boundary conditions
3215 if (ias.eq.0) then ! top-bottom symmetric
3216 bcs(:,1) = [1,1] ! theta(even): zero first derivative
3217 else
3218 bcs(:,1) = [-1,-1] ! periodic
3219 end if
3220 bcs(:,2) = [0,0] ! r: not-a-knot
3221 bcs_val = 0._dp
3222
3223 ! calculate determinant
3224 if (sum(deriv).eq.0) then ! no derivatives
3225 do kd = 1,grid_eq%loc_n_r
3226 do jd = 1,grid_eq%n(2)
3227 eq_2%jac_E(:,jd,kd,0,0,0) = eq_1%q_saf_E(kd,0)/&
3228 &(h_h_33(:,grid_eq%i_min-1+kd)*&
3229 &rbphi_h(grid_eq%i_min-1+kd,0))
3230 end do
3231 end do
3232 else if (deriv(3).ne.0) then ! axisymmetry: deriv. in tor. coord. is zero
3233 eq_2%jac_E(:,:,:,deriv(1),deriv(2),deriv(3)) = 0.0_dp
3234 else if (deriv(1).le.2 .and. deriv(2).le.2) then
3235 ! initialize 2-D cubic spline
3236 call ezspline_init(f_spl,grid_eq%n(1),grid_eq%loc_n_r,&
3237 &bcs(:,1),bcs(:,2),ierr)
3238 call ezspline_error(ierr)
3239 chckerr('')
3240
3241
3242 ! set grid
3243 f_spl%x1 = chi_h
3244 f_spl%x2 = grid_eq%loc_r_E
3245
3246 ! set boundary conditions
3247 f_spl%bcval1min = bcs_val(1,1)
3248 f_spl%bcval1max = bcs_val(2,1)
3249 f_spl%bcval2min = bcs_val(1,2)
3250 f_spl%bcval2max = bcs_val(2,2)
3251
3252 do jd = 1,grid_eq%n(2)
3253 ! set up
3254 call ezspline_setup(f_spl,eq_2%jac_E(:,jd,:,0,0,0),ierr,&
3255 &exact_dim=.true.) ! match exact dimensions, none of them old Fortran bullsh*t!
3256 call ezspline_error(ierr)
3257 chckerr('')
3258
3259 ! derivate
3260 ! Note: deriv is the other way around because poloidal indices
3261 ! come before normal
3262 call ezspline_derivative(f_spl,deriv(2),deriv(1),grid_eq%n(1),&
3263 &grid_eq%loc_n_r,chi_h,grid_eq%loc_r_E,&
3264 &eq_2%jac_E(:,jd,:,deriv(1),deriv(2),0),ierr)
3265 call ezspline_error(ierr)
3266 chckerr('')
3267 end do
3268
3269 ! free
3270 call ezspline_free(f_spl,ierr)
3271 call ezspline_error(ierr)
3272 else
3273 ierr = 1
3274 err_msg = 'Derivative of order ('//trim(i2str(deriv(1)))//','//&
3275 &trim(i2str(deriv(2)))//','//trim(i2str(deriv(3)))//'&
3276 &) not supported'
3277 chckerr(err_msg)
3278 end if
3279 end function calc_jac_h_ind
3280 !> \private array version
3281 integer function calc_jac_h_arr(grid_eq,eq_1,eq_2,deriv) result(ierr)
3282 character(*), parameter :: rout_name = 'calc_jac_H_arr'
3283
3284 ! input / output
3285 type(grid_type), intent(in) :: grid_eq !< equilibrim grid
3286 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium
3287 type(eq_2_type), intent(inout) :: eq_2 !< metric equilibrium
3288 integer, intent(in) :: deriv(:,:) !< derivatives
3289
3290 ! local variables
3291 integer :: id
3292
3293 ! initialize ierr
3294 ierr = 0
3295
3296 do id = 1, size(deriv,2)
3297 ierr = calc_jac_h_ind(grid_eq,eq_1,eq_2,deriv(:,id))
3298 chckerr('')
3299 end do
3300 end function calc_jac_h_arr
3301
3302 !> \private individual version
3303 integer function calc_jac_f_ind(eq,deriv) result(ierr)
3304 use num_utilities, only: add_arr_mult
3305
3306 character(*), parameter :: rout_name = 'calc_jac_F_ind'
3307
3308 ! input / output
3309 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
3310 integer, intent(in) :: deriv(:) !< derivatives
3311
3312 ! initialize ierr
3313 ierr = 0
3314
3315#if ldebug
3316 ! check the derivatives requested
3317 ierr = check_deriv(deriv,max_deriv,'calc_J_F')
3318 chckerr('')
3319#endif
3320
3321 ! initialize
3322 eq%jac_F(:,:,:,deriv(1),deriv(2),deriv(3)) = 0.0_dp
3323
3324 ! calculate determinant
3325 ierr = add_arr_mult(eq%jac_E,eq%det_T_FE,&
3326 &eq%jac_F(:,:,:,deriv(1),deriv(2),deriv(3)),deriv)
3327 chckerr('')
3328 end function calc_jac_f_ind
3329 !> \private array version
3330 integer function calc_jac_f_arr(eq,deriv) result(ierr)
3331 character(*), parameter :: rout_name = 'calc_jac_F_arr'
3332
3333 ! input / output
3334 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
3335 integer, intent(in) :: deriv(:,:) !< derivatives
3336
3337 ! local variables
3338 integer :: id
3339
3340 ! initialize ierr
3341 ierr = 0
3342
3343 do id = 1, size(deriv,2)
3344 ierr = calc_jac_f_ind(eq,deriv(:,id))
3345 chckerr('')
3346 end do
3347 end function calc_jac_f_arr
3348
3349 !> \private individual version
3350 integer function calc_t_vc_ind(eq,deriv) result(ierr)
3351 use num_utilities, only: add_arr_mult, c
3352
3353 character(*), parameter :: rout_name = 'calc_T_VC_ind'
3354
3355 ! input / output
3356 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
3357 integer, intent(in) :: deriv(:) !< derivatives
3358
3359
3360 ! initialize ierr
3361 ierr = 0
3362#if ldebug
3363 ! check the derivatives requested
3364 ierr = check_deriv(deriv,max_deriv,'calc_T_VC')
3365 chckerr('')
3366#endif
3367
3368 ! initialize
3369 eq%T_VC(:,:,:,:,deriv(1),deriv(2),deriv(3)) = 0._dp
3370
3371 ! calculate transformation matrix T_V^C
3372 eq%T_VC(:,:,:,c([1,1],.false.),deriv(1),deriv(2),deriv(3)) = &
3373 &eq%R_E(:,:,:,deriv(1)+1,deriv(2),deriv(3))
3374 !eq%T_VC(:,:,:,c([1,2],.false.),deriv(1),deriv(2),deriv(3)) = 0
3375 eq%T_VC(:,:,:,c([1,3],.false.),deriv(1),deriv(2),deriv(3)) = &
3376 &eq%Z_E(:,:,:,deriv(1)+1,deriv(2),deriv(3))
3377 eq%T_VC(:,:,:,c([2,1],.false.),deriv(1),deriv(2),deriv(3)) = &
3378 &eq%R_E(:,:,:,deriv(1),deriv(2)+1,deriv(3))
3379 !eq%T_VC(:,:,:,c([2,2],.false.),deriv(1),deriv(2),deriv(3)) = 0
3380 eq%T_VC(:,:,:,c([2,3],.false.),deriv(1),deriv(2),deriv(3)) = &
3381 &eq%Z_E(:,:,:,deriv(1),deriv(2)+1,deriv(3))
3382 eq%T_VC(:,:,:,c([3,1],.false.),deriv(1),deriv(2),deriv(3)) = &
3383 &eq%R_E(:,:,:,deriv(1),deriv(2),deriv(3)+1)
3384 if (sum(deriv).eq.0) then
3385 eq%T_VC(:,:,:,c([3,2],.false.),deriv(1),deriv(2),deriv(3)) = 1.0_dp
3386 else
3387 !eq%T_VC(:,:,:,c([3,2],.false.),deriv(1),deriv(2),deriv(3)) = 0
3388 end if
3389 eq%T_VC(:,:,:,c([3,3],.false.),deriv(1),deriv(2),deriv(3)) = &
3390 &eq%Z_E(:,:,:,deriv(1),deriv(2),deriv(3)+1)
3391 end function calc_t_vc_ind
3392 !> \private array version
3393 integer function calc_t_vc_arr(eq,deriv) result(ierr)
3394 character(*), parameter :: rout_name = 'calc_T_VC_arr'
3395
3396 ! input / output
3397 type(eq_2_type), intent(inout) :: eq !< metric equilibrium
3398 integer, intent(in) :: deriv(:,:) !< derivatives
3399
3400 ! local variables
3401 integer :: id
3402
3403 ! initialize ierr
3404 ierr = 0
3405
3406 do id = 1, size(deriv,2)
3407 ierr = calc_t_vc_ind(eq,deriv(:,id))
3408 chckerr('')
3409 end do
3410 end function calc_t_vc_arr
3411
3412 !> \private individual version
3413 integer function calc_t_vf_ind(grid_eq,eq_1,eq_2,deriv) result(ierr)
3414 use num_vars, only: use_pol_flux_f
3415 use num_utilities, only: add_arr_mult, c
3416
3417 character(*), parameter :: rout_name = 'calc_T_VF_ind'
3418
3419 ! input / output
3420 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
3421 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium
3422 type(eq_2_type), intent(inout) :: eq_2 !< metric equilibrium
3423 integer, intent(in) :: deriv(:) !< derivatives
3424
3425 ! local variables
3426 integer :: kd ! counter
3427 real(dp), allocatable :: theta_s(:,:,:,:,:,:) ! theta_F and derivatives
3428 real(dp), allocatable :: zeta_s(:,:,:,:,:,:) ! - zeta_F and derivatives
3429 integer :: dims(3) ! dimensions
3430 integer :: c1 ! 2D coordinate in met_type storage convention
3431
3432 ! initialize ierr
3433 ierr = 0
3434
3435#if ldebug
3436 ! check the derivatives requested
3437 ierr = check_deriv(deriv,max_deriv,'calc_T_VF')
3438 chckerr('')
3439#endif
3440
3441 ! set up dims
3442 dims = [grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r]
3443
3444 ! initialize
3445 eq_2%T_EF(:,:,:,:,deriv(1),deriv(2),deriv(3)) = 0._dp
3446
3447 ! set up theta_s
3448 allocate(theta_s(dims(1),dims(2),dims(3),&
3449 &0:deriv(1)+1,0:deriv(2)+1,0:deriv(3)+1))
3450 theta_s = 0.0_dp
3451 ! start from theta_E
3452 theta_s(:,:,:,0,0,0) = grid_eq%theta_E
3453 theta_s(:,:,:,0,1,0) = 1.0_dp
3454 ! add the deformation described by lambda
3455 theta_s = theta_s + &
3456 &eq_2%L_E(:,:,:,0:deriv(1)+1,0:deriv(2)+1,0:deriv(3)+1)
3457
3458 if (use_pol_flux_f) then
3459 ! calculate transformation matrix T_V^F
3460 ! (1,1)
3461 ierr = add_arr_mult(theta_s,eq_1%q_saf_E(:,1:),&
3462 &eq_2%T_EF(:,:,:,c([1,1],.false.),deriv(1),deriv(2),deriv(3)),&
3463 &deriv)
3464 chckerr('')
3465 ierr = add_arr_mult(eq_2%L_E(:,:,:,1:,0:,0:),eq_1%q_saf_E(:,0:),&
3466 &eq_2%T_EF(:,:,:,c([1,1],.false.),deriv(1),deriv(2),deriv(3)),&
3467 &deriv)
3468 chckerr('')
3469 ! (1,2)
3470 if (deriv(2).eq.0 .and. deriv(3).eq.0) then
3471 do kd = 1,dims(3)
3472 eq_2%T_EF(:,:,kd,c([1,2],.false.),deriv(1),0,0) = &
3473 &eq_1%flux_p_E(kd,deriv(1)+1)/(2*pi)
3474 end do
3475 !else
3476 !eq_2%T_EF(:,:,:,c([1,2],.false.),deriv(1),deriv(2),deriv(3)) &
3477 !&= 0.0_dp
3478 end if
3479 ! (1,3)
3480 eq_2%T_EF(:,:,:,c([1,3],.false.),deriv(1),deriv(2),deriv(3)) = &
3481 &eq_2%L_E(:,:,:,deriv(1)+1,deriv(2),deriv(3))
3482 ! (2,1)
3483 ierr = add_arr_mult(theta_s(:,:,:,0:,1:,0:),eq_1%q_saf_E,&
3484 &eq_2%T_EF(:,:,:,c([2,1],.false.),deriv(1),deriv(2),deriv(3)),&
3485 &deriv)
3486 chckerr('')
3487 ! (2,2)
3488 !eq_2%T_EF(:,:,:,c([2,2],.false.),deriv(1),deriv(2),deriv(3)) = &
3489 !&0.0_dp
3490 ! (2,3)
3491 eq_2%T_EF(:,:,:,c([2,3],.false.),deriv(1),deriv(2),deriv(3)) = &
3492 &theta_s(:,:,:,deriv(1),deriv(2)+1,deriv(3))
3493 ! (3,1)
3494 if (sum(deriv).eq.0) then
3495 eq_2%T_EF(:,:,:,c([3,1],.false.),0,0,0) = -1.0_dp
3496 end if
3497 ierr = add_arr_mult(eq_2%L_E(:,:,:,0:,0:,1:),eq_1%q_saf_E,&
3498 &eq_2%T_EF(:,:,:,c([3,1],.false.),deriv(1),deriv(2),deriv(3)),&
3499 &deriv)
3500 chckerr('')
3501 ! (3,2)
3502 !eq_2%T_EF(:,:,:,c([3,2],.false.),deriv(1),deriv(2),deriv(3)) = &
3503 !&0.0_dp
3504 ! (3,3)
3505 eq_2%T_EF(:,:,:,c([3,3],.false.),deriv(1),deriv(2),deriv(3)) = &
3506 &eq_2%L_E(:,:,:,deriv(1),deriv(2),deriv(3)+1)
3507
3508 ! determinant
3509 eq_2%det_T_EF(:,:,:,deriv(1),deriv(2),deriv(3)) = 0.0_dp
3510 ierr = add_arr_mult(-theta_s(:,:,:,0:,1:,0:),&
3511 &eq_1%flux_p_E(:,1:)/(2*pi),&
3512 &eq_2%det_T_EF(:,:,:,deriv(1),deriv(2),deriv(3)),deriv)
3513 chckerr('')
3514 else
3515 ! set up zeta_s
3516 allocate(zeta_s(dims(1),dims(2),dims(3),0:deriv(1)+1,&
3517 &0:deriv(2)+1,0:deriv(3)+1))
3518 zeta_s = 0.0_dp
3519 ! start from zeta_E
3520 zeta_s(:,:,:,0,0,0) = grid_eq%zeta_E
3521 zeta_s(:,:,:,0,0,1) = 1.0_dp
3522
3523 ! calculate transformation matrix T_V^F
3524 ! (1,1)
3525 eq_2%T_EF(:,:,:,c([1,1],.false.),deriv(1),deriv(2),deriv(3)) = &
3526 &-theta_s(:,:,:,deriv(1)+1,deriv(2),deriv(3))
3527 ierr = add_arr_mult(zeta_s,eq_1%rot_t_E(:,1:),&
3528 &eq_2%T_EF(:,:,:,c([1,1],.false.),deriv(1),deriv(2),deriv(3)),&
3529 &deriv)
3530 chckerr('')
3531 ! (1,2)
3532 if (deriv(2).eq.0 .and. deriv(3).eq.0) then
3533 do kd = 1,dims(3)
3534 eq_2%T_EF(:,:,kd,c([1,2],.false.),deriv(1),0,0) = &
3535 &-eq_1%flux_t_E(kd,deriv(1)+1)/(2*pi)
3536 end do
3537 !else
3538 !eq_2%T_EF(:,:,:,c([1,2],.false.),deriv(1),deriv(2),deriv(3)) 7
3539 !&= 0.0_dp
3540 end if
3541 ! (1,3)
3542 !eq_2%T_EF(:,:,:,c([1,3],.false.),deriv(1),deriv(2),deriv(3)) = &
3543 !&0.0_dp
3544 ! (2,1)
3545 eq_2%T_EF(:,:,:,c([2,1],.false.),deriv(1),deriv(2),deriv(3)) = &
3546 &-theta_s(:,:,:,deriv(1),deriv(2)+1,deriv(3))
3547 ! (2,2)
3548 !eq_2%T_EF(:,:,:,c([2,2],.false.),deriv(1),deriv(2),deriv(3)) = &
3549 !&0.0_dp
3550 ! (2,3)
3551 !eq_2%T_EF(:,:,:,c([2,3],.false.),deriv(1),deriv(2),deriv(3)) = &
3552 !&0.0_dp
3553 ! (3,1)
3554 if (deriv(2).eq.0 .and. deriv(3).eq.0) then
3555 do kd = 1,dims(3)
3556 eq_2%T_EF(:,:,kd,c([3,1],.false.),deriv(1),0,0) = &
3557 &eq_1%rot_t_E(kd,deriv(1))
3558 end do
3559 end if
3560 c1 = c([3,1],.false.) ! to avoid compiler crash (as of 26-02-2015)
3561 eq_2%T_EF(:,:,:,c1,deriv(1),deriv(2),deriv(3)) = &
3562 &eq_2%T_EF(:,:,:,c1,deriv(1),deriv(2),deriv(3)) &
3563 &-theta_s(:,:,:,deriv(1),deriv(2),deriv(3)+1)
3564 ! (3,2)
3565 !eq_2%T_EF(:,:,:,c([3,2],.false.),deriv(1),deriv(2),deriv(3)) = &
3566 !&0.0_dp
3567 ! (3,3)
3568 if (sum(deriv).eq.0) then
3569 eq_2%T_EF(:,:,:,c([3,3],.false.),0,0,0) = -1.0_dp
3570 !else
3571 !eq_2%T_EF(:,:,:,c([3,3],.false.)deriv(1),deriv(2),deriv(3)) = &
3572 !&0.0_dp
3573 end if
3574
3575 ! determinant
3576 eq_2%det_T_EF(:,:,:,deriv(1),deriv(2),deriv(3)) = 0.0_dp
3577 ierr = add_arr_mult(theta_s(:,:,:,0:,1:,0:),&
3578 &eq_1%flux_t_E(:,1:)/(2*pi),&
3579 &eq_2%det_T_EF(:,:,:,deriv(1),deriv(2),deriv(3)),deriv)
3580 chckerr('')
3581 end if
3582 end function calc_t_vf_ind
3583 !> \private array version
3584 integer function calc_t_vf_arr(grid_eq,eq_1,eq_2,deriv) result(ierr)
3585 character(*), parameter :: rout_name = 'calc_T_VF_arr'
3586
3587 ! input / output
3588 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
3589 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium
3590 type(eq_2_type), intent(inout) :: eq_2 !< metric equilibrium
3591 integer, intent(in) :: deriv(:,:) !< derivatives
3592
3593 ! local variables
3594 integer :: id
3595
3596 ! initialize ierr
3597 ierr = 0
3598
3599 do id = 1, size(deriv,2)
3600 ierr = calc_t_vf_ind(grid_eq,eq_1,eq_2,deriv(:,id))
3601 chckerr('')
3602 end do
3603 end function calc_t_vf_arr
3604
3605 !> \private individual version
3606 integer function calc_t_hf_ind(grid_eq,eq_1,eq_2,deriv) result(ierr)
3607 use num_vars, only: use_pol_flux_f
3608 use num_utilities, only: add_arr_mult, c
3609
3610 character(*), parameter :: rout_name = 'calc_T_HF_ind'
3611
3612 ! input / output
3613 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
3614 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium
3615 type(eq_2_type), intent(inout) :: eq_2 !< metric equilibrium
3616 integer, intent(in) :: deriv(:) !< derivatives
3617
3618 ! local variables
3619 integer :: kd ! counter
3620 real(dp), allocatable :: theta_s(:,:,:,:,:,:) ! theta_F and derivatives
3621 real(dp), allocatable :: zeta_s(:,:,:,:,:,:) ! - zeta_F and derivatives
3622 integer :: dims(3) ! dimensions
3623
3624 ! initialize ierr
3625 ierr = 0
3626
3627#if ldebug
3628 ! check the derivatives requested
3629 ierr = check_deriv(deriv,max_deriv,'calc_T_HF')
3630 chckerr('')
3631#endif
3632
3633 ! set up dims
3634 dims = [grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r]
3635
3636 ! initialize
3637 eq_2%T_EF(:,:,:,:,deriv(1),deriv(2),deriv(3)) = 0._dp
3638
3639 ! set up theta_s
3640 allocate(theta_s(dims(1),dims(2),dims(3),&
3641 &0:deriv(1)+1,0:deriv(2)+1,0:deriv(3)+1))
3642 theta_s = 0.0_dp
3643 theta_s(:,:,:,0,0,0) = grid_eq%theta_E
3644 theta_s(:,:,:,0,1,0) = 1.0_dp
3645
3646 if (use_pol_flux_f) then
3647 ! calculate transformation matrix T_H^F
3648 ! (1,1)
3649 ierr = add_arr_mult(theta_s,-eq_1%q_saf_E(:,1:),&
3650 &eq_2%T_EF(:,:,:,c([1,1],.false.),deriv(1),deriv(2),deriv(3)),&
3651 &deriv)
3652 chckerr('')
3653 ! (1,2)
3654 if (sum(deriv).eq.0) then
3655 eq_2%T_EF(:,:,:,c([1,2],.false.),0,0,0) = 1._dp
3656 !else
3657 !eq_2%T_EF(:,:,:,c([1,2],.false.),deriv(1),deriv(2),deriv(3)) &
3658 !&= 0.0_dp
3659 end if
3660 ! (1,3)
3661 !eq_2%T_EF(:,:,:,c([1,3],.false.),deriv(1),deriv(2),deriv(3)) = &
3662 !&0.0_dp
3663 ! (2,1)
3664 if (deriv(2).eq.0 .and. deriv(3).eq.0) then
3665 do kd = 1,dims(3)
3666 eq_2%T_EF(:,:,kd,c([2,1],.false.),deriv(1),0,0) = &
3667 &-eq_1%q_saf_E(kd,deriv(1))
3668 end do
3669 !else
3670 !eq_2%T_EF(:,:,:,c([2,1],.false.),deriv(1),deriv(2),deriv(3)) &
3671 !&= 0.0_dp
3672 end if
3673 ! (2,2)
3674 !eq_2%T_EF(:,:,:,c([2,2],.false.),deriv(1),deriv(2),deriv(3)) = &
3675 !&0.0_dp
3676 ! (2,3)
3677 if (sum(deriv).eq.0) then
3678 eq_2%T_EF(:,:,:,c([2,3],.false.),0,0,0) = 1.0_dp
3679 !else
3680 !eq_2%T_EF(:,:,:,c([2,3],.false.),deriv(1),deriv(2),deriv(3)) &
3681 !&= 0.0_dp
3682 end if
3683 ! (3,1)
3684 if (sum(deriv).eq.0) then
3685 eq_2%T_EF(:,:,:,c([3,1],.false.),0,0,0) = 1.0_dp
3686 !else
3687 !eq_2%T_EF(:,:,:,c([3,1],.false.),deriv(1),deriv(2),deriv(3)) &
3688 !&= 0.0_dp
3689 end if
3690 ! (3,2)
3691 !eq_2%T_EF(:,:,:,c([3,2],.false),deriv(1),deriv(2),deriv(3)) = &
3692 !&0.0_dp
3693 ! (3,3)
3694 !eq_2%T_EF(:,:,:,c([3,3],.false),deriv(1),deriv(2),deriv(3)) = &
3695 !&0.0_dp
3696
3697 ! determinant
3698 eq_2%det_T_EF(:,:,:,deriv(1),deriv(2),deriv(3)) = 0.0_dp
3699 if (sum(deriv).eq.0) then
3700 eq_2%det_T_EF(:,:,:,deriv(1),0,0) = 1._dp
3701 !else
3702 !eq_2%det_T_EF(:,:,deriv(1),deriv(2),deriv(3)) = 0.0_dp
3703 end if
3704 else
3705 ! set up zeta_s
3706 allocate(zeta_s(dims(1),dims(2),dims(3),&
3707 &0:deriv(1)+1,0:deriv(2)+1,0:deriv(3)+1))
3708 zeta_s = 0.0_dp
3709 ! start from zeta_E
3710 zeta_s(:,:,:,0,0,0) = grid_eq%zeta_E
3711 zeta_s(:,:,:,0,0,1) = 1.0_dp
3712
3713 ! calculate transformation matrix T_H^F
3714 ! (1,1)
3715 ierr = add_arr_mult(zeta_s,eq_1%rot_t_E(:,1:),&
3716 &eq_2%T_EF(:,:,:,c([1,1],.false.),deriv(1),deriv(2),deriv(3)),&
3717 &deriv)
3718 chckerr('')
3719 ! (1,2)
3720 if (deriv(2).eq.0 .and. deriv(3).eq.0) then
3721 do kd = 1,dims(3)
3722 eq_2%T_EF(:,:,kd,c([1,2],.false.),deriv(1),0,0) = &
3723 &eq_1%q_saf_E(kd,deriv(1))
3724 end do
3725 !else
3726 !eq_2%T_EF(:,:,:,c([1,2],.false.),deriv(1),deriv(2),deriv(3)) &
3727 !&= 0.0_dp
3728 end if
3729 ! (1,3)
3730 !eq_2%T_EF(:,:,:,c([1,3],.false.),deriv(1),deriv(2),deriv(3)) = &
3731 !&0.0_dp
3732 ! (2,1)
3733 if (sum(deriv).eq.0) then
3734 eq_2%T_EF(:,:,:,c([2,1],.false.),0,0,0) = -1.0_dp
3735 !else
3736 !eq_2%T_EF(:,:,:,c([2,1],.false.),deriv(1),deriv(2),deriv(3)) &
3737 !&= 0.0_dp
3738 end if
3739 ! (2,2)
3740 !eq_2%T_EF(:,:,:,c([2,2],.false.),deriv(1),deriv(2),deriv(3)) = &
3741 !&0.0_dp
3742 ! (2,3)
3743 !eq_2%T_EF(:,:,:,c([2,3],.false.),deriv(1),deriv(2),deriv(3)) = &
3744 !&0.0_dp
3745 ! (3,1)
3746 if (deriv(2).eq.0 .and. deriv(3).eq.0) then
3747 do kd = 1,dims(3)
3748 eq_2%T_EF(:,:,kd,c([3,1],.false.),deriv(1),0,0) = &
3749 &eq_1%rot_t_E(kd,deriv(1))
3750 end do
3751 !else
3752 !eq_2%T_EF(:,:,:,c([3,1],.false.),deriv(1),deriv(2),deriv(3)) &
3753 !&= 0.0_dp
3754 end if
3755 ! (3,2)
3756 !eq_2%T_EF(:,:,:,c([3,2],.false.),deriv(1),deriv(2),deriv(3)) = &
3757 !&0.0_dp
3758 ! (3,3)
3759 if (sum(deriv).eq.0) then
3760 eq_2%T_EF(:,:,:,c([3,3],.false.),deriv(1),0,0) = 1.0_dp
3761 !else
3762 !eq_2%T_EF(:,:,:,c([3,3],.false.),deriv(1),deriv(2),deriv(3)) &
3763 !&= 0.0_dp
3764 end if
3765
3766 ! determinant
3767 eq_2%det_T_EF(:,:,:,deriv(1),deriv(2),deriv(3)) = 0.0_dp
3768 if (deriv(2).eq.0 .and. deriv(3).eq.0) then
3769 do kd = 1,dims(3)
3770 eq_2%det_T_EF(:,:,kd,deriv(1),0,0) = &
3771 &eq_1%q_saf_E(kd,deriv(1))
3772 end do
3773 !else
3774 !eq_2%det_T_EF(:,:,deriv(1),deriv(2),deriv(3)) = 0.0_dp
3775 end if
3776 end if
3777 end function calc_t_hf_ind
3778 !> \private array version
3779 integer function calc_t_hf_arr(grid_eq,eq_1,eq_2,deriv) result(ierr)
3780 character(*), parameter :: rout_name = 'calc_T_HF_arr'
3781
3782 ! input / output
3783 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
3784 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium
3785 type(eq_2_type), intent(inout) :: eq_2 !< metric equilibrium
3786 integer, intent(in) :: deriv(:,:) !< derivatives
3787
3788 ! local variables
3789 integer :: id
3790
3791 ! initialize ierr
3792 ierr = 0
3793
3794 do id = 1, size(deriv,2)
3795 ierr = calc_t_hf_ind(grid_eq,eq_1,eq_2,deriv(:,id))
3796 chckerr('')
3797 end do
3798 end function calc_t_hf_arr
3799
3800 !> Plots the flux quantities in the solution grid.
3801 !!
3802 !! - safety factor \c q_saf
3803 !! - rotational transform \c rot_t
3804 !! - pressure \c pres
3805 !! - poloidal flux \c flux_p
3806 !! - toroidal flux \c flux_t
3807 !!
3808 !! \return ierr
3809 integer function flux_q_plot(grid_eq,eq) result(ierr)
3811 use grid_utilities, only: trim_grid
3812 use mpi_utilities, only: get_ser_var
3813 use eq_vars, only: pres_0, psi_0
3814
3815 character(*), parameter :: rout_name = 'flux_q_plot'
3816
3817 ! input / output
3818 type(grid_type), intent(in) :: grid_eq !< normal grid
3819 type(eq_1_type), intent(in) :: eq !< flux equilibrium variables
3820
3821 ! local variables
3822 integer :: norm_id(2) ! untrimmed normal indices for trimmed grids
3823 integer :: id ! counter
3824 integer :: n_vars = 5 ! nr. of variables to plot
3825 character(len=max_str_ln), allocatable :: plot_titles(:) ! plot titles
3826 type(grid_type) :: grid_trim ! trimmed grid
3827 real(dp), allocatable :: x_plot_2d(:,:) ! x values of 2D plot
3828 real(dp), allocatable :: y_plot_2d(:,:) ! y values of 2D plot
3829
3830 ! initialize ierr
3831 ierr = 0
3832
3833 ! bypass plots if no_plots
3834 if (no_plots) return
3835
3836 ! user output
3837 call writo('Plotting flux quantities')
3838
3839 call lvl_ud(1)
3840
3841 ! trim grid
3842 ierr = trim_grid(grid_eq,grid_trim,norm_id)
3843 chckerr('')
3844
3845 ! initialize plot titles and file names
3846 allocate(plot_titles(n_vars))
3847 plot_titles(1) = 'safety factor []'
3848 plot_titles(2) = 'rotational transform []'
3849 plot_titles(3) = 'pressure [pa]'
3850 plot_titles(4) = 'poloidal flux [Tm^2]'
3851 plot_titles(5) = 'toroidal flux [Tm^2]'
3852
3853 ! plot using HDF5
3854 ierr = flux_q_plot_hdf5()
3855 chckerr('')
3856
3857 ! plot using external program
3858 ierr = flux_q_plot_ex()
3859 chckerr('')
3860
3861 ! clean up
3862 call grid_trim%dealloc()
3863
3864 call lvl_ud(-1)
3865 contains
3866 ! plots the flux quantities in HDF5
3867 !> \private
3868 integer function flux_q_plot_hdf5() result(ierr)
3869 use num_vars, only: eq_style
3870 use output_ops, only: plot_hdf5
3873
3874 character(*), parameter :: rout_name = 'flux_q_plot_HDF5'
3875
3876 ! local variables
3877 integer :: kd ! counter
3878 real(dp), allocatable :: x_plot(:,:,:,:) ! x values of total plot
3879 real(dp), allocatable :: y_plot(:,:,:,:) ! y values of total plot
3880 real(dp), allocatable :: z_plot(:,:,:,:) ! z values of total plot
3881 real(dp), allocatable :: f_plot(:,:,:,:) ! values of variable of total plot
3882 integer :: plot_dim(4) ! total plot dimensions
3883 integer :: plot_offset(4) ! plot offset
3884 type(grid_type) :: grid_plot ! grid for plotting
3885 character(len=max_str_ln) :: file_name ! file name
3886
3887 ! initialize ierr
3888 ierr = 0
3889
3890 ! set up file name
3891 file_name = 'flux_quantities'
3892
3893 ! fill the 2D version of the plot
3894 allocate(y_plot_2d(grid_trim%loc_n_r,n_vars))
3895
3896 y_plot_2d(:,1) = eq%q_saf_FD(norm_id(1):norm_id(2),0)
3897 y_plot_2d(:,2) = eq%rot_t_FD(norm_id(1):norm_id(2),0)
3898 y_plot_2d(:,3) = eq%pres_FD(norm_id(1):norm_id(2),0)
3899 y_plot_2d(:,4) = eq%flux_p_FD(norm_id(1):norm_id(2),0)
3900 y_plot_2d(:,5) = eq%flux_t_FD(norm_id(1):norm_id(2),0)
3901
3902 ! extend trimmed equilibrium grid
3903 ierr = extend_grid_f(grid_trim,grid_plot,grid_eq=grid_eq)
3904 chckerr('')
3905
3906 ! if VMEC, calculate trigonometric factors of plot grid
3907 if (eq_style.eq.1) then
3908 ierr = calc_trigon_factors(grid_plot%theta_E,grid_plot%zeta_E,&
3909 &grid_plot%trigon_factors)
3910 chckerr('')
3911 end if
3912
3913 ! set up plot_dim and plot_offset
3914 plot_dim = [grid_plot%n(1),grid_plot%n(2),grid_plot%n(3),n_vars]
3915 plot_offset = [0,0,grid_plot%i_min-1,n_vars]
3916
3917 ! set up total plot variables
3918 allocate(x_plot(grid_plot%n(1),grid_plot%n(2),grid_plot%loc_n_r,&
3919 &n_vars))
3920 allocate(y_plot(grid_plot%n(1),grid_plot%n(2),grid_plot%loc_n_r,&
3921 &n_vars))
3922 allocate(z_plot(grid_plot%n(1),grid_plot%n(2),grid_plot%loc_n_r,&
3923 &n_vars))
3924 allocate(f_plot(grid_plot%n(1),grid_plot%n(2),grid_plot%loc_n_r,&
3925 &n_vars))
3926
3927 ! calculate 3D X,Y and Z
3928 ierr = calc_xyz_grid(grid_eq,grid_plot,x_plot(:,:,:,1),&
3929 &y_plot(:,:,:,1),z_plot(:,:,:,1))
3930 chckerr('')
3931 do id = 2,n_vars
3932 x_plot(:,:,:,id) = x_plot(:,:,:,1)
3933 y_plot(:,:,:,id) = y_plot(:,:,:,1)
3934 z_plot(:,:,:,id) = z_plot(:,:,:,1)
3935 end do
3936
3937 do kd = 1,grid_plot%loc_n_r
3938 f_plot(:,:,kd,1) = y_plot_2d(kd,1) ! safey factor
3939 f_plot(:,:,kd,2) = y_plot_2d(kd,2) ! rotational transform
3940 f_plot(:,:,kd,3) = y_plot_2d(kd,3) ! pressure
3941 f_plot(:,:,kd,4) = y_plot_2d(kd,4) ! poloidal flux
3942 f_plot(:,:,kd,5) = y_plot_2d(kd,5) ! toroidal flux
3943 end do
3944
3945 ! rescale if normalized
3946 if (use_normalization) then
3947 f_plot(:,:,:,3) = f_plot(:,:,:,3)*pres_0 ! pressure
3948 f_plot(:,:,:,4) = f_plot(:,:,:,4)*psi_0 ! flux_p
3949 f_plot(:,:,:,5) = f_plot(:,:,:,5)*psi_0 ! flux_t
3950 end if
3951
3952 ! print the output using HDF5
3953 call plot_hdf5(plot_titles,file_name,f_plot,plot_dim,plot_offset,&
3954 &x_plot,y_plot,z_plot,col=1,descr='Flux quantities')
3955
3956 ! deallocate and destroy grid
3957 deallocate(y_plot_2d)
3958 deallocate(x_plot,y_plot,z_plot,f_plot)
3959 call grid_plot%dealloc()
3960 end function flux_q_plot_hdf5
3961
3962 ! plots the pressure and fluxes in external program
3963 !> \private
3964 integer function flux_q_plot_ex() result(ierr)
3965 use eq_vars, only: max_flux_f
3966
3967 character(*), parameter :: rout_name = 'flux_q_plot_ex'
3968
3969 ! local variables
3970 character(len=max_str_ln), allocatable :: file_name(:) ! file_name
3971 real(dp), allocatable :: ser_var_loc(:) ! local serial var
3972
3973 ! initialize ierr
3974 ierr = 0
3975
3976 ! allocate variabels if master
3977 if (rank.eq.0) then
3978 allocate(x_plot_2d(grid_trim%n(3),n_vars))
3979 allocate(y_plot_2d(grid_trim%n(3),n_vars))
3980 end if
3981
3982 ! fill the 2D version of the plot
3983 !ierr = get_ser_var(eq%q_saf_FD(norm_id(1):norm_id(2),0),ser_var_loc)
3984 !CHCKERR('')
3985 !if (rank.eq.0) Y_plot_2D(:,1) = ser_var_loc
3986 !ierr = get_ser_var(eq%rot_t_FD(norm_id(1):norm_id(2),0),ser_var_loc)
3987 !CHCKERR('')
3988 !if (rank.eq.0) Y_plot_2D(:,2) = ser_var_loc
3989 ierr = get_ser_var(eq%pres_FD(norm_id(1):norm_id(2),0),ser_var_loc)
3990 chckerr('')
3991 if (rank.eq.0) y_plot_2d(:,3) = ser_var_loc
3992 ierr = get_ser_var(eq%flux_p_FD(norm_id(1):norm_id(2),0),&
3993 &ser_var_loc)
3994 chckerr('')
3995 if (rank.eq.0) y_plot_2d(:,4) = ser_var_loc
3996 ierr = get_ser_var(eq%flux_t_FD(norm_id(1):norm_id(2),0),&
3997 &ser_var_loc)
3998 chckerr('')
3999 if (rank.eq.0) y_plot_2d(:,5) = ser_var_loc
4000
4001 ! rescale if normalized
4002 if (use_normalization .and. rank.eq.0) then
4003 y_plot_2d(:,3) = y_plot_2d(:,3)*pres_0 ! pressure
4004 y_plot_2d(:,4) = y_plot_2d(:,4)*psi_0 ! flux_p
4005 y_plot_2d(:,5) = y_plot_2d(:,5)*psi_0 ! flux_t
4006 end if
4007
4008 ! continue the plot if master
4009 if (rank.eq.0) then
4010 ! deallocate local serial variables
4011 deallocate(ser_var_loc)
4012
4013 ! set up file names
4014 allocate(file_name(2))
4015 file_name(1) = 'pres'
4016 file_name(2) = 'flux'
4017
4018 ! 2D normal variable (normalized F coordinate)
4019 x_plot_2d(:,1) = grid_trim%r_F*2*pi/max_flux_f
4020 do id = 2,n_vars
4021 x_plot_2d(:,id) = x_plot_2d(:,1)
4022 end do
4023
4024 ! plot the individual 2D output of this process (except q_saf
4025 ! and rot_t, as they are already in plot_resonance)
4026 call print_ex_2d(plot_titles(3),file_name(1),&
4027 &y_plot_2d(:,3),x_plot_2d(:,3),draw=.false.)
4028 ! fluxes
4029 call print_ex_2d(plot_titles(4:5),file_name(2),&
4030 &y_plot_2d(:,4:5),x_plot_2d(:,4:5),draw=.false.)
4031
4032 ! draw plot
4033 call draw_ex(plot_titles(3:3),file_name(1),1,1,.false.) ! pressure
4034 call draw_ex(plot_titles(4:5),file_name(2),2,1,.false.) ! fluxes
4035
4036 ! user output
4037 call writo('Safety factor and rotational transform are plotted &
4038 &using "plot_resonance"')
4039
4040 ! clean up
4041 deallocate(x_plot_2d,y_plot_2d)
4042 end if
4043 end function flux_q_plot_ex
4044 end function flux_q_plot
4045
4046 !> Calculates derived equilibrium quantities system.
4047 !!
4048 !! - magnetic shear \c S
4049 !! - normal curvature \c kappa_n
4050 !! - geodesic curvature \c kappa_g
4051 !! - parallel current \c sigma
4052 !!
4053 !! Naive implementations of these quantities, with the exception of \c S,
4054 !! give numerically very unfavorable results, so special care must be taken
4055 !! in this procedure.
4056 !!
4057 !! This is an important issue as these derived equilbrium quantities are
4058 !! important building blocks of the perturbed potential energy, including
4059 !! the drivers of instabilities.
4060 !!
4061 !! The general formulas are derived under the first section, VMEC. For
4062 !! axisymmetric HELENA, however, simplified equations are possible and these
4063 !! are presented afterwards.
4064 !!
4065 !! VMEC
4066 !! ====
4067 !!
4068 !! The shear
4069 !! ---------
4070 !!
4071 !! The local shear \f$S\f$ is calculated using equation 3.22 from \cite
4072 !! Weyens3D :
4073 !!
4074 !! \f$S = - \frac{1}{\mathcal{J}} \frac{\partial}{\partial \theta}
4075 !! \left(\frac{ h^{\psi\alpha}}{ h^{\psi \psi}}\right)\f$
4076 !!
4077 !! which doesn't pose any particular problems as there are only angular
4078 !! derivatives.
4079 !!
4080 !! The curvature
4081 !! -------------
4082 !!
4083 !! This is calculated using the parallel derivative of the parallel unit
4084 !! vector (i.e. theta if using poloidal flux and zeta if using toroidal
4085 !! flux).
4086 !!
4087 !! By taking instead the derivative of the covariant basis vector and
4088 !! realizing that the difference with the real curvature lies solely in a
4089 !! component along the parallel direction, which cancels out when taking the
4090 !! normal or geodesic components, the situation becomes easier.
4091 !!
4092 !! Asuming the poloidal flux is used as normal coordinate, the taks is
4093 !! therefore how to calculate
4094 !! \f$\frac{1}{\left|\vec{e}_\theta\right|^2} \frac{\partial}{\partial \theta} \vec{e}_\theta\f$.
4095 !!
4096 !! By transforming both the derivative as well as the basis vector to
4097 !! Equilibrium coordinates, this can be written as
4098 !!
4099 !! \f$\sum_{i=2,3} \sum_{j=2,3} \mathcal{T}_\text{F}^\text{E} \left(3,i\right))
4100 !! \frac{\partial}{\partial u^i_\text{E}} \left(
4101 !! \mathcal{T}_\text{F}^\text{E} \left(3,j\right) \vec{e}_{j,\text{E}}\right)\f$
4102 !!
4103 !! where the summations only run over the angular coordinates
4104 !! because \f$\vec{e}_\theta\f$ never has any component along
4105 !! \f$\vec{e}_{\psi,\text{E}}\f$.
4106 !!
4107 !! This double summation formula can then be written as a matrix equation
4108 !!
4109 !! \f$\begin{pmatrix}\mathcal{T}_\text{F}^\text{E}\left(3,2\right) &
4110 !! \mathcal{T}_\text{F}^\text{E}\left(3,3\right)\end{pmatrix}
4111 !! \left[
4112 !! \begin{pmatrix} \frac{\partial}{\partial u^2} \vec{e}_{2} &
4113 !! \frac{\partial}{\partial u^2} \vec{e}_{3} \\
4114 !! \frac{\partial}{\partial u^3} \vec{e}_{2} &
4115 !! \frac{\partial}{\partial u^3} \vec{e}_{3}\end{pmatrix}_\text{E}
4116 !! \begin{pmatrix}\mathcal{T}_\text{F}^\text{E}\left(3,2\right) \\
4117 !! \mathcal{T}_\text{F}^\text{E}\left(3,3\right)\end{pmatrix}
4118 !! +
4119 !! \begin{pmatrix}
4120 !! \frac{\partial}{\partial u^2} \mathcal{T}_\text{F}^\text{E}\left(3,2\right) &
4121 !! \frac{\partial}{\partial u^2} \mathcal{T}_\text{F}^\text{E}\left(3,3\right) \\
4122 !! \frac{\partial}{\partial u^3} \mathcal{T}_\text{F}^\text{E}\left(3,2\right) &
4123 !! \frac{\partial}{\partial u^3} \mathcal{T}_\text{F}^\text{E}\left(3,3\right)
4124 !! \end{pmatrix}
4125 !! \begin{pmatrix}\vec{e}_2 \\ \vec{e}_3\end{pmatrix}
4126 !! \right]\f$
4127 !!
4128 !! which can be represented shorthand as
4129 !! \f$\mathcal{T}_\text{F}^\text{E}\left(3,2:3\right) \left[
4130 !! \mathcal{D}\vec{e}_\text{E} \mathcal{T}_\text{F}^\text{E}\left(3,2:3\right)^T
4131 !! +\mathcal{D} \mathcal{T} \vec{e}_\text{E}^T \right]\f$
4132 !!
4133 !! It is relatively easy to set up the matrix
4134 !! \f$\mathcal{D}\vec{e}_\text{E}\f$ as a function of covariant basis
4135 !! vectors in the Cylindrical coordinate system.
4136 !! The derivatives of the transformation matrix itself can likewise be
4137 !! found.
4138 !!
4139 !! The steps used in this routine are therefore
4140 !! -# Set up \f$\mathcal{D}\vec{e}_\text{E}\f$, i.e. as a function of the
4141 !! three cylindrical covariant basis vectors.
4142 !! -# Set up correctcion by the derivatives of the transformation matrix,
4143 !! with a single summation.
4144 !! -# Decompose the normal \f$\frac{\nabla \psi}{\left|\nabla\psi\right|^2}\f$
4145 !! and geodesic vectors \f$\frac{\nabla \psi \times \vec{B}}{B^2}\f$ as a
4146 !! function of the cylindrical contravariant basis vectors.
4147 !! -# Double summation to reduce term ~ \f$\mathcal{D}\vec{e}_\text{E}\f$
4148 !! and correction by single summation to reduce term ~
4149 !! \f$\mathcal{D}\mathcal{T}\f$.
4150 !! -# Dot these for each of the four (actually three because of symmetry)
4151 !! elements of \f$\mathcal{D}\vec{e}_\text{E}\f$.
4152 !! -# Divide by \f$\left|\vec{e}_\theta\right|^2\f$.
4153 !! -# Possibly correct for toroidal flux.
4154 !!
4155 !! An advantage of using this formulation is that no normal derivatives are
4156 !! needed, so that nothing has to implicitely cancel out.
4157 !!
4158 !! The parallel current
4159 !! --------------------
4160 !!
4161 !! The parallel current is calculated from the shear with the help of
4162 !! equation 3.29 of \cite Weyens3D :
4163 !!
4164 !! \f$\mu_0 \sigma = -\frac{1}{B_\theta} \left[
4165 !! 2 \frac{\nabla \psi \times \vec{B}}{\left|\nabla \psi\right|^2} \cdot
4166 !! \frac{\partial \left(\nabla \psi\right)}{\partial \theta} +
4167 !! \mathcal{J} \left|\nabla \psi\right|^2 S \right]\f$ .
4168 !!
4169 !! where a similar technique can be used as above, for the calculation of
4170 !! the curvature: As
4171 !!
4172 !! \f$\frac{\nabla \psi \times \vec{B}}{\left|\nabla \psi\right|^2}
4173 !! = \frac{B_\theta \vec{e}_\alpha - B_\alpha \vec{e}_\theta}
4174 !! {\mathcal{J} \left|\nabla\psi\right|^2}\f$ ,
4175 !!
4176 !! The parallel derivative of \f$\nabla \psi\f$ can be rewritten in terms of
4177 !! contravariant components of derivatives of covariant basis vectors:
4178 !!
4179 !! \f$\left\{ \begin{aligned}
4180 !! \vec{e}_\alpha \cdot \frac{\partial \left(\nabla \psi\right)}{\partial\theta}
4181 !! = - \nabla \psi \cdot \frac{\partial \vec{e}_\theta}{\partial \alpha} \\
4182 !! \vec{e}_\theta \cdot \frac{\partial \left(\nabla \psi\right)}{\partial\theta}
4183 !! = - \nabla \psi \cdot \frac{\partial \vec{e}_\theta}{\partial \theta} \\
4184 !! \end{aligned}\right. \f$ ,
4185 !!
4186 !! so that the result is:
4187 !!
4188 !! \f$\frac{\nabla \psi \times \vec{B}}{\left|\nabla \psi\right|^2} \cdot
4189 !! \frac{\partial \left(\nabla \psi\right)}{\partial \theta}
4190 !! = \frac{1}{\mathcal{J}^2} \frac{\nabla \psi}{\left|\nabla\psi\right|^2} \cdot
4191 !! \left[ g_{\alpha\theta} \frac{\partial \vec{e}_\theta}{\partial \theta}
4192 !! - g_{\theta\theta} \frac{\partial \vec{e}_\theta}{\partial \alpha} \right]
4193 !! \f$ ,
4194 !!
4195 !! which is given by a formula similar to the one used above for the
4196 !! geodesical curvature.
4197 !!
4198 !! In debug mode, it can be checked whether the current is indeed
4199 !! divergence-free, with the help of equation 3.33 of \cite Weyens3D.
4200 !!
4201 !! \f$ -2 p' \int_{\theta_0}^\theta \mathcal{J} \kappa_g \text{d}{\theta} =
4202 !! \sigma\left(\theta\right) - \sigma_0\f$
4203 !!
4204 !! and whether a direct, naive implementation of the parallel current from
4205 !! equation 3.26 of \cite Weyens3D agrees with the more accurate results
4206 !! used here:
4207 !!
4208 !! \f$\mu_0 \sigma = \frac{\partial B_\psi}{\partial \alpha} -
4209 !! \frac{\partial B_\alpha}{\partial \psi} -
4210 !! \mu_0 p' \mathcal{J} \frac{B_\alpha}{B_\theta}\f$ .
4211 !!
4212 !! The reason why they are generally different is that this implementation
4213 !! relies on the cancellation of large terms.
4214 !!
4215 !! HELENA
4216 !! ======
4217 !!
4218 !! The parallel current
4219 !! --------------------
4220 !!
4221 !! As \f$B_\alpha = F\left(\psi\right)\f$ and
4222 !! \f$\vec{e}_{\alpha,\text{F}} = \vec{e}_{\phi,\text{H}}\f$, the naive
4223 !! expression for the shear becomes simply
4224 !!
4225 !! \f$\sigma = -F' - \mu_0 p' \frac{F}{B^2}\f$ ,
4226 !!
4227 !! which can be used like that.
4228 !!
4229 !!
4230 !! The shear
4231 !! ---------
4232 !!
4233 !! The calculate the local shear \f$S\f$ is looks like it is best to use
4234 !! equation 3.22 from \cite Weyens3D , just as in the VMEC case:
4235 !!
4236 !! As a test, however, equation 3.29 of \cite Weyens3D can be used in stead:
4237 !!
4238 !! \f$\mathcal{J}\left|\nabla \psi\right|^2 S + \mu_0 \mathcal{J}B^2 \sigma
4239 !! = - 2 \frac{F}{R} \left( \frac{Z_\theta}{R} +
4240 !! \frac{Z_\theta R_{\theta\theta} - R_\theta Z_{\theta\theta}}{R_\theta^2 + Z_\theta^2} \right)\f$.
4241 !!
4242 !! from which \f$\sigma\f$ can be obtained.
4243 !!
4244 !! The curvature
4245 !! -------------
4246 !!
4247 !! Also the curvature expressions can be simplified for axisymmetric
4248 !! situations. The result is given by
4249 !!
4250 !! \f$\kappa_n = \frac{q R}{F}
4251 !! \frac{Z_\theta \left( R_{\theta\theta} - q^2 R\right) - R_\theta Z_{\theta\theta}}
4252 !! {\left(R_\theta^2 + Z_\theta^2 + \left(q R\right)^2\right) \left( R_\theta^2 + Z_\theta^2 \right)} \f$
4253 !!
4254 !! \f$\kappa_g = q R
4255 !! \frac{R_\theta \left( 2 \left(R_\theta^2 + Z_\theta^2\right) + \left(qR\right)^2 \right)
4256 !! - R \left(R_\theta R_{\theta\theta} + Z_\theta Z_{\theta\theta}\right)}
4257 !! {\left(R_\theta^2 + Z_\theta^2 + \left(q R\right)^2\right)^2} \f$
4258 !!
4259 !!
4260 !! \note
4261 !! -# If the toroidal flux is used instead, the actual curvature obviously
4262 !! is unchanged, which implies that the normal component has to be
4263 !! multiplied by the safety factor and the geodesic component has to be
4264 !! divided by it.
4265 !! -# The formulas for the normal and geodesic basis vectors for VMEC are
4266 !! * \f$\frac{\nabla \psi}{\left|\nabla\psi\right|^2} =
4267 !! -\frac{\mathcal{J} \left(1 + \lambda_\theta\right)}
4268 !! {Z_\theta^2 + R_\theta^2 + \left(R_\zeta Z_\theta - R_\theta Z_\zeta \right)/R^2} \frac{1}{R}
4269 !! \left( -Z_\theta , R_\zeta Z_\theta - R_\theta Z_\zeta, R_\theta\right)_\text{C}\f$
4270 !! * \f$\frac{\nabla \psi \times \vec{B}}{B^2} =
4271 !! \frac{1}{g_{\theta\theta}}
4272 !! \left(P R_\theta - Q R_\zeta, -Q R^2, P Z_\theta - Q Z_\zeta\right)_\text{C}\f$
4273 !! *\f$Q = g_{\theta\theta} - q g_{\theta\alpha}\f$ (using right-handed
4274 !! flux \f$q_\text{F}\f$, which is the inverse of the left-handed VMEC
4275 !! \f$q_\text{V}\f$)
4276 !! *\f$P = \frac{Q \lambda_\zeta - g_{\alpha\theta}}{1+\lambda_\theta}\f$
4277 !! (where the derivatives of \f$R\f$, \f$Z\f$ and \f$\lambda\f$ are in
4278 !! VMEC coordinates).
4279 !! -# The formulas for the normal and geodesic basis vectors for HELENA are
4280 !! * \f$\frac{\nabla \psi}{\left|\nabla\psi\right|^2} =
4281 !! -\frac{1}{Z_\theta^2 + R_\theta^2} \frac{q R}{F}
4282 !! \left( -Z_\theta , 0, R_\theta\right)_\text{C}\f$
4283 !! * \f$\frac{\nabla \psi \times \vec{B}}{B^2} =
4284 !! \frac{q R^2}{R_\theta^2 + Z_\theta^2 + q^2 R^2}
4285 !! \left(-R_\theta, -\frac{R_\theta^2 + Z_\theta^2}{q}, -Z_\theta\right)_\text{C}\f$
4286 integer function calc_derived_q(grid_eq,eq_1,eq_2) result(ierr)
4287 use eq_vars, only: vac_perm, max_flux_f
4288 use num_vars, only: eq_style, use_pol_flux_f
4289 use helena_vars, only: r_h, z_h, chi_h, ias
4290 use num_utilities, only: spline
4291
4292 character(*), parameter :: rout_name = 'calc_derived_q'
4293
4294 ! input / output
4295 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
4296 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium variables
4297 type(eq_2_type), intent(inout), target :: eq_2 !< metric equilibrium variables
4298
4299 ! local variables
4300 integer :: id, jd, kd, ld ! counters
4301 integer :: kd_h ! kd in Helena tables
4302 integer :: bcs(2,2) ! boundary conditions (theta(even), theta(odd))
4303 real(dp) :: bcs_val(2,2) ! values for boundary conditions
4304 real(dp), allocatable :: d1_epar(:,:,:,:) ! cylindrical contravariant components of alpha derivative of parallel basis vector
4305 real(dp), allocatable :: d3_epar(:,:,:,:) ! cylindrical contravariant components of theta derivative of parallel basis vector
4306 real(dp), allocatable :: b_n(:,:,:,:) ! covariant Cylindrical components of normal basis vector
4307 real(dp), allocatable :: b_g(:,:,:,:) ! covariant Cylindrical components of geodesic basis vector
4308 real(dp), allocatable :: de(:,:,:,:,:,:) ! derivs of cov. unit vector in E (space,deriv.,unitvec,output)
4309 real(dp), allocatable :: d_de(:,:,:,:,:) ! derivs of transf. matrix in E (space,deriv.,output)
4310 real(dp), allocatable :: rchi(:,:,:) ! chi and chi^2 derivatives of R
4311 real(dp), allocatable :: zchi(:,:,:) ! chi and chi^2 derivatives of Z
4312
4313 ! initialize ierr
4314 ierr = 0
4315
4316 ! user output
4317 call writo('Set up derived quantities in flux coordinates')
4318
4319 call lvl_ud(1)
4320
4321 ! initialize helper variables
4322 allocate(de(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r,2,2,3))
4323 allocate(d_de(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r,2,3))
4324 allocate(b_n(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r,3))
4325 allocate(b_g(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r,3))
4326 allocate(d1_epar(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r,3))
4327 allocate(d3_epar(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r,3))
4328
4329 select case (eq_style)
4330 case (1) ! VMEC
4331 ! Calculate the shear S
4332 call calc_derived_s_direct(eq_2,eq_2%S)
4333
4334 ! Equilibrium contravariant components of angular derivatives of
4335 ! covariant parallel basis vector
4336 call calc_derived_de_epar_vmec(grid_eq,eq_2,de,d_de,b_n,b_g)
4337 case (2) ! HELENA
4338 ! Calculate parallel current sigma
4339 call calc_derived_sigma_hel(grid_eq,eq_2,eq_2%sigma)
4340
4341 ! set up boundary conditions for theta derivatives
4342 if (ias.eq.0) then ! top-bottom symmetric
4343 bcs(:,1) = [1,1] ! theta(even): zero first derivative
4344 bcs(:,2) = [2,2] ! theta(odd): zero first derivative
4345 else
4346 bcs(:,1) = [-1,-1] ! theta(even): periodic
4347 bcs(:,2) = [-1,-1] ! theta(odd): periodic
4348 end if
4349 bcs_val = 0._dp
4350
4351 ! initialize output
4352 de = 0._dp
4353 d_de = 0._dp
4354 b_n = 0._dp
4355 b_g = 0._dp
4356
4357 ! calculate the derivatives of covariant unit vectors in
4358 ! cEquilibrium oordinates
4359 allocate(rchi(grid_eq%n(1),grid_eq%loc_n_r,0:2))
4360 allocate(zchi(grid_eq%n(1),grid_eq%loc_n_r,0:2))
4361 rchi(:,:,0) = r_h(:,grid_eq%i_min:grid_eq%i_max)
4362 zchi(:,:,0) = z_h(:,grid_eq%i_min:grid_eq%i_max)
4363 do kd = 1,grid_eq%loc_n_r
4364 kd_h = grid_eq%i_min-1+kd
4365 do id = 1,2
4366 ierr = spline(chi_h,r_h(:,kd_h),chi_h,rchi(:,kd,id),&
4367 &ord=3,deriv=id,bcs=bcs(:,1),bcs_val=bcs_val(:,1)) ! even
4368 chckerr('')
4369 ierr = spline(chi_h,z_h(:,kd_h),chi_h,zchi(:,kd,id),&
4370 &ord=3,deriv=id,bcs=bcs(:,2),bcs_val=bcs_val(:,2)) ! odd
4371 chckerr('')
4372 end do
4373 end do
4374
4375 ! Equilibrium contravariant components of angular derivatives of
4376 ! covariant parallel basis vector
4377 call calc_derived_de_epar_hel(grid_eq,eq_1,rchi,zchi,&
4378 &de,d_de,b_n,b_g)
4379 end select
4380
4381 ! calculate cylindrical contravariant components of angular derivatives
4382 ! of covariant parallel basis vector
4383 call calc_derived_dc_epar(de,d_de,eq_2%T_FE(:,:,:,:,0,0,0),&
4384 &d1_epar,d3_epar)
4385
4386 ! clean up
4387 deallocate(de,d_de)
4388
4389 ! calculate normal and geodesic curvature
4390 call calc_derived_kappa_from_e(grid_eq,eq_1,eq_2,b_n,b_g,&
4391 eq_2%kappa_n,eq_2%kappa_g)
4392
4393 select case (eq_style)
4394 case (1) ! VMEC
4395 ! calculate parallel current
4396 call calc_derived_sigma_from_e(eq_2,b_n,eq_2%sigma)
4397 case (2) ! HELENA
4398 ! Calculate the shear S
4399 ierr = calc_derived_s_from_deriv_hel(grid_eq,eq_2,bcs(:,1),&
4400 &bcs_val(:,1),eq_2%S) ! even
4401 chckerr('')
4402 end select
4403
4404 ! clean up
4405 deallocate(b_n,b_g)
4406
4407#if ldebug
4408 if (debug_calc_derived_q) then
4409 ! plot them
4410 ierr = plot_derived_q(grid_eq,eq_2)
4411 chckerr('')
4412
4413 call writo('Testing consistency of derived quantities')
4414 call lvl_ud(1)
4415
4416 ! test consistency sigma and kappa_g
4417 ierr = test_sigma_with_kappa_g(grid_eq,eq_1,eq_2)
4418 chckerr('')
4419
4420 ! test comparison with naive implementations
4421 ierr = test_kappa(grid_eq,eq_1,eq_2)
4422 chckerr('')
4423 select case (eq_style)
4424 case (1) ! VMEC
4425 ierr = test_sigma_vmec(grid_eq,eq_1,eq_2)
4426 chckerr('')
4427 case (2) ! HELENA
4428 call test_s_hel(grid_eq,eq_2,rchi,zchi)
4429 end select
4430
4431 call lvl_ud(-1)
4432 call writo('Testing done')
4433 end if
4434#endif
4435
4436 call lvl_ud(-1)
4437 contains
4438 !> \private Calculate shear from direct formula
4439 subroutine calc_derived_s_direct(eq_2,S)
4440 use num_utilities, only: c
4441 use num_vars, only: tol_zero
4442
4443 ! input / output
4444 type(eq_2_type), intent(in), target :: eq_2 !< metric equilibrium variables
4445 real(dp), intent(out) :: s(:,:,:) !< shear
4446
4447 ! local variables
4448 real(dp), pointer :: j(:,:,:) => null() ! jac
4449 real(dp), pointer :: h12(:,:,:) => null() ! h^alpha,psi
4450 real(dp), pointer :: d3h12(:,:,:) => null() ! D_theta h^alpha,psi
4451 real(dp), pointer :: h22(:,:,:) => null() ! h^psi,psi
4452 real(dp), pointer :: d3h22(:,:,:) => null() ! D_theta h^psi,psi
4453 real(dp), allocatable :: h22_corrected(:,:,:) ! to avoid division by zero
4454
4455 ! set up submatrices
4456 j => eq_2%jac_FD(:,:,:,0,0,0)
4457 h12 => eq_2%h_FD(:,:,:,c([1,2],.true.),0,0,0)
4458 d3h12 => eq_2%h_FD(:,:,:,c([1,2],.true.),0,0,1)
4459 h22 => eq_2%h_FD(:,:,:,c([2,2],.true.),0,0,0)
4460 d3h22 => eq_2%h_FD(:,:,:,c([2,2],.true.),0,0,1)
4461 allocate(h22_corrected(size(s,1),size(s,2),size(s,3)))
4462 h22_corrected = sign(max(abs(h22),tol_zero),h22)
4463
4464 ! Calculate the shear S
4465 s = -(d3h12/h22_corrected - d3h22*h12/h22_corrected**2)/j
4466
4467 ! clean up
4468 nullify(j,h12,d3h12,h22,d3h22)
4469 end subroutine calc_derived_s_direct
4470
4471 !> \private Calculate shear by numerically derivating for HELENA.
4472 integer function calc_derived_s_from_deriv_hel(grid_eq,eq_2,bcs,&
4473 &bcs_val,S) result(ierr)
4474
4475 use num_utilities, only: c, spline
4476 use helena_vars, only: chi_h
4477
4478 character(*), parameter :: rout_name = &
4479 &'calc_derived_S_from_deriv_HEL'
4480
4481 ! input / output
4482 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
4483 type(eq_2_type), intent(in), target :: eq_2 !< metric equilibrium variables
4484 integer :: bcs(2) !< boundary conditions (theta(even), theta(odd))
4485 real(dp) :: bcs_val(2) !< values for boundary conditions
4486 real(dp), intent(out) :: s(:,:,:) !< shear
4487
4488 ! local variables
4489 integer :: jd, kd ! counters
4490 real(dp), pointer :: j(:,:,:) => null() ! jac
4491 real(dp), pointer :: h12(:,:,:) => null() ! h^alpha,psi
4492 real(dp), pointer :: h22(:,:,:) => null() ! h^psi,psi
4493
4494 ! initialize ierr
4495 ierr = 0
4496
4497 ! set up submatrices
4498 j => eq_2%jac_FD(:,:,:,0,0,0)
4499 h12 => eq_2%h_FD(:,:,:,c([1,2],.true.),0,0,0)
4500 h22 => eq_2%h_FD(:,:,:,c([2,2],.true.),0,0,0)
4501
4502 ! Calculate the shear S
4503 do kd = 1,grid_eq%loc_n_r
4504 do jd = 1,grid_eq%n(2)
4505 ierr = spline(chi_h,h12(:,jd,kd)/h22(:,jd,kd),chi_h,&
4506 &s(:,jd,kd),ord=3,deriv=1,bcs=bcs,bcs_val=bcs_val)
4507 chckerr('')
4508 end do
4509 end do
4510 s = -s/j
4511
4512 ! clean up
4513 nullify(j,h12,h22)
4514 end function calc_derived_s_from_deriv_hel
4515
4516 !> \private Calculate shear from sigma using identity
4517 !! J|nabla psi|^2 S + mu_0 J B^2 sigma = K
4518 !! with K = -2F/R (Z(1)/R(0) + (Z(1)R(2)-R(1)Z(2))/(R(1)^2+Z(1)^2)
4519 subroutine calc_derived_s_from_sigma_hel(grid_eq,eq_2,Rchi,Zchi,S)
4520 use num_utilities, only: c
4521 use helena_vars, only: rbphi_h
4522
4523 ! input / output
4524 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
4525 type(eq_2_type), intent(in), target :: eq_2 !< metric equilibrium variables
4526 real(dp), intent(in) :: rchi(:,:,0:) !< chi and chi^2 derivatives of R
4527 real(dp), intent(in) :: zchi(:,:,0:) !< chi and chi^2 derivatives of Z
4528 real(dp), intent(out) :: s(:,:,:) !< shear
4529
4530 ! local variables
4531 integer :: jd, kd ! counters
4532 integer :: kd_h ! kd in Helena tables
4533 real(dp), allocatable :: k(:,:,:) ! RHS
4534 real(dp), pointer :: j(:,:,:) => null() ! jac
4535 real(dp), pointer :: g33(:,:,:) => null() ! g^theta,theta
4536 real(dp), pointer :: h22(:,:,:) => null() ! h^psi,psi
4537
4538 ! set up submatrices
4539 j => eq_2%jac_FD(:,:,:,0,0,0)
4540 g33 => eq_2%g_FD(:,:,:,c([3,3],.true.),0,0,0)
4541 h22 => eq_2%h_FD(:,:,:,c([2,2],.true.),0,0,0)
4542
4543 ! set up RHS K
4544 allocate(k(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r))
4545 do jd = 1,grid_eq%n(2)
4546 k(:,jd,:) = zchi(:,:,1)/rchi(:,:,0) + &
4547 &(zchi(:,:,1)*rchi(:,:,2)-rchi(:,:,1)*zchi(:,:,2))/&
4548 &(rchi(:,:,1)**2+zchi(:,:,1)**2)
4549 do kd = 1,grid_eq%loc_n_r
4550 kd_h = grid_eq%i_min-1+kd
4551 k(:,jd,kd) = -2._dp*rbphi_h(kd_h,0)/rchi(:,kd,0) * &
4552 &k(:,jd,kd)
4553 end do
4554 end do
4555
4556 ! subtract mu_0 J B^2 sigma
4557 s = k - vac_perm*eq_2%sigma*g33/j
4558
4559 ! divide by J |nabla psi|^2
4560 s = s/(j*h22)
4561
4562 ! clean up
4563 nullify(j,g33,h22)
4564 end subroutine calc_derived_s_from_sigma_hel
4565
4566 !> \private Calculate Cylindrical contravariant components of angular
4567 !! derivatives of covariant parallel basis vector
4568 subroutine calc_derived_dc_epar(de,D_de,T_FE,D1_epar,D3_epar)
4569 use num_utilities, only: c
4570
4571 ! input / output
4572 real(dp), intent(in) :: de(:,:,:,:,:,:) !< derivs of cov. unit vector in E (space,deriv.,unitvec,output)
4573 real(dp), intent(in) :: d_de(:,:,:,:,:) !< derivs of transf. matrix in E (space,deriv.,output)
4574 real(dp), intent(in) :: t_fe(:,:,:,:) !< transformation matrix from F to E
4575 real(dp), intent(out) :: d1_epar(:,:,:,:) !< cylindrical contravariant components of alpha derivative of parallel basis vector
4576 real(dp), intent(out) :: d3_epar(:,:,:,:) !< cylindrical contravariant components of theta derivative of parallel basis vector
4577
4578 ! initialize output
4579 d1_epar = 0._dp
4580 d3_epar = 0._dp
4581
4582 ! transform
4583 do ld = 1,3
4584 do id = 1,2
4585 do jd = 1,2
4586 ! d/dalpha
4587 d1_epar(:,:,:,ld) = d1_epar(:,:,:,ld) + &
4588 &de(:,:,:,id,jd,ld) * &
4589 &t_fe(:,:,:,c([1,1+id],.false.)) * & ! 1 for alpha
4590 &t_fe(:,:,:,c([3,1+jd],.false.))
4591 ! d/dtheta
4592 d3_epar(:,:,:,ld) = d3_epar(:,:,:,ld) + &
4593 &de(:,:,:,id,jd,ld) * &
4594 &t_fe(:,:,:,c([3,1+id],.false.)) * & ! 3 for theta
4595 &t_fe(:,:,:,c([3,1+jd],.false.))
4596 end do
4597 ! d/dalpha
4598 d1_epar(:,:,:,ld) = d1_epar(:,:,:,ld) + &
4599 &d_de(:,:,:,id,ld) * &
4600 &t_fe(:,:,:,c([1,1+id],.false.)) ! 1 for alpha
4601 ! d/dtheta
4602 d3_epar(:,:,:,ld) = d3_epar(:,:,:,ld) + &
4603 &d_de(:,:,:,id,ld) * &
4604 &t_fe(:,:,:,c([3,1+id],.false.)) ! 3 for theta
4605 end do
4606 end do
4607 end subroutine calc_derived_dc_epar
4608
4609 !> \private Calculate Equilibrium contravariant components of angular
4610 !! derivatives of covariant parallel basis vector. Also sets up the
4611 !! covariant Cylindrical components of the normal and geodesic basis
4612 !! vectors.
4613 subroutine calc_derived_de_epar_vmec(grid_eq,eq_2,de,D_de,b_n,b_g)
4614 use num_utilities, only: c
4615 use num_vars, only: tol_zero
4616
4617 ! input / output
4618 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
4619 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium variables
4620 real(dp), intent(out) :: de(:,:,:,:,:,:) !< derivs of cov. unit vector in E (space,deriv.,unitvec,output)
4621 real(dp), intent(out) :: d_de(:,:,:,:,:) !< derivs of transf. matrix in E (space,deriv.,output)
4622 real(dp), intent(out) :: b_n(:,:,:,:) !< covariant Cylindrical components of normal basis vector
4623 real(dp), intent(out) :: b_g(:,:,:,:) !< covariant Cylindrical components of geodesic basis vector
4624
4625 ! initialize output
4626 de = 0._dp
4627 d_de = 0._dp
4628 b_n = 0._dp
4629 b_g = 0._dp
4630
4631 ! calculate the derivatives of covariant unit vectors in Equilibrium
4632 ! coordinates
4633
4634 ! d/dtheta e_theta
4635 de(:,:,:,1,1,1) = eq_2%R_E(:,:,:,0,2,0) ! ~ e_R
4636 !de(:,:,:,1,1,2) = 0._dp ! ~ e_phi
4637 de(:,:,:,1,1,3) = eq_2%Z_E(:,:,:,0,2,0) ! ~ e_Z
4638
4639 ! d/dzeta e_theta
4640 de(:,:,:,2,1,1) = eq_2%R_E(:,:,:,0,1,1) ! ~ e_R
4641 de(:,:,:,2,1,2) = eq_2%R_E(:,:,:,0,1,0)/eq_2%R_E(:,:,:,0,0,0) ! ~ e_phi
4642 de(:,:,:,2,1,3) = eq_2%Z_E(:,:,:,0,1,1) ! ~ e_Z
4643
4644 ! d/dtheta e_zeta
4645 de(:,:,:,1,2,1) = eq_2%R_E(:,:,:,0,1,1) ! ~ e_R
4646 de(:,:,:,1,2,2) = eq_2%R_E(:,:,:,0,1,0)/eq_2%R_E(:,:,:,0,0,0) ! ~ e_phi
4647 de(:,:,:,1,2,3) = eq_2%Z_E(:,:,:,0,1,1) ! ~ e_Z
4648
4649 ! d/dzeta e_zeta
4650 de(:,:,:,2,2,1) = eq_2%R_E(:,:,:,0,0,2)-eq_2%R_E(:,:,:,0,0,0) ! ~ e_R
4651 de(:,:,:,2,2,2) = 2*eq_2%R_E(:,:,:,0,0,1)/eq_2%R_E(:,:,:,0,0,0) ! ~ e_phi
4652 de(:,:,:,2,2,3) = eq_2%Z_E(:,:,:,0,0,2) ! ~ e_Z
4653
4654 ! calculate derivatives of transformation matrix in Equilibrium
4655 ! coordinates
4656
4657 ! ~d/dtheta
4658 d_de(:,:,:,1,1) = eq_2%R_E(:,:,:,0,1,0)*&
4659 &eq_2%T_FE(:,:,:,c([3,2],.false.),0,1,0) + &
4660 &eq_2%R_E(:,:,:,0,0,1)*&
4661 &eq_2%T_FE(:,:,:,c([3,3],.false.),0,1,0) ! ~ e_R
4662 d_de(:,:,:,1,2) = eq_2%T_FE(:,:,:,c([3,3],.false.),0,1,0) ! ~ e_phi
4663 d_de(:,:,:,1,3) = eq_2%Z_E(:,:,:,0,1,0)*&
4664 &eq_2%T_FE(:,:,:,c([3,2],.false.),0,1,0) + &
4665 &eq_2%Z_E(:,:,:,0,0,1)*&
4666 &eq_2%T_FE(:,:,:,c([3,3],.false.),0,1,0) ! ~ e_Z
4667
4668 ! ~d/dzeta
4669 d_de(:,:,:,2,1) = eq_2%R_E(:,:,:,0,1,0)*&
4670 &eq_2%T_FE(:,:,:,c([3,2],.false.),0,0,1) + &
4671 &eq_2%R_E(:,:,:,0,0,1)*&
4672 &eq_2%T_FE(:,:,:,c([3,3],.false.),0,0,1) ! ~ e_R
4673 d_de(:,:,:,2,2) = eq_2%T_FE(:,:,:,c([3,3],.false.),0,0,1) ! ~ e_phi
4674 d_de(:,:,:,2,3) = eq_2%Z_E(:,:,:,0,1,0)*&
4675 &eq_2%T_FE(:,:,:,c([3,2],.false.),0,0,1) + &
4676 &eq_2%Z_E(:,:,:,0,0,1)*&
4677 &eq_2%T_FE(:,:,:,c([3,3],.false.),0,0,1) ! ~ e_Z
4678
4679 ! Decompose normal and geodesic basis vectors into contravariant
4680 ! cylindrical basis vectors.
4681
4682 ! b_n
4683 b_n(:,:,:,2) = (eq_2%R_E(:,:,:,0,0,1)*eq_2%Z_E(:,:,:,0,1,0) - &
4684 &eq_2%R_E(:,:,:,0,1,0)*eq_2%Z_E(:,:,:,0,0,1)) ! store RzetaZtheta - RthetaZeta
4685 b_n(:,:,:,1) = &
4686 &-eq_2%jac_FD(:,:,:,0,0,0) * (1+eq_2%L_E(:,:,:,0,1,0)) / &
4687 &max(tol_zero, ( (b_n(:,:,:,2)/eq_2%R_E(:,:,:,0,0,0))**2 + &
4688 &eq_2%R_E(:,:,:,0,1,0)**2 + eq_2%Z_E(:,:,:,0,1,0)**2 )) / &
4689 &eq_2%R_E(:,:,:,0,0,0) ! set up common factor of all b_n
4690 b_n(:,:,:,2) = b_n(:,:,:,1) * b_n(:,:,:,2) ! finalize b_n(2)
4691 b_n(:,:,:,3) = b_n(:,:,:,1) * eq_2%R_E(:,:,:,0,1,0) ! finalize b_n(3)
4692 b_n(:,:,:,1) = -b_n(:,:,:,1) * eq_2%Z_E(:,:,:,0,1,0) ! finalize b_n(1)
4693
4694 ! b_g
4695 do kd = 1,grid_eq%loc_n_r
4696 b_g(:,:,kd,2) = eq_2%g_FD(:,:,kd,c([3,3],.true.),0,0,0) - &
4697 &eq_2%g_FD(:,:,kd,c([1,3],.true.),0,0,0) * &
4698 &eq_1%q_saf_FD(kd,0) ! store J(B_theta + B_alpha q)
4699 b_g(:,:,kd,1) = (b_g(:,:,kd,2)*eq_2%L_E(:,:,kd,0,0,1)-&
4700 &eq_2%g_FD(:,:,kd,c([1,3],.true.),0,0,0)) / &
4701 &(1+eq_2%L_E(:,:,kd,0,1,0)) ! store J((B_theta + B_alpha q) L_zeta - B_alpha)/(1+L_theta)
4702 end do
4703 b_g(:,:,:,3) = b_g(:,:,:,1)*eq_2%Z_E(:,:,:,0,1,0) - &
4704 &b_g(:,:,:,2)*eq_2%Z_E(:,:,:,0,0,1) ! finalize b_g(3) (apart from pre-factor)
4705 b_g(:,:,:,1) = b_g(:,:,:,1)*eq_2%R_E(:,:,:,0,1,0) - &
4706 &b_g(:,:,:,2)*eq_2%R_E(:,:,:,0,0,1) ! finalize b_g(1) (apart from pre-factor)
4707 b_g(:,:,:,2) = -b_g(:,:,:,2)*eq_2%R_E(:,:,:,0,0,0)**2 ! finalize b_g(2) (apart from pre-factor)
4708 do ld = 1,3
4709 b_g(:,:,:,ld) = b_g(:,:,:,ld) / &
4710 &eq_2%g_FD(:,:,:,c([3,3],.true.),0,0,0) ! prefactor 1/g_thetatheta
4711 end do
4712 end subroutine calc_derived_de_epar_vmec
4713 subroutine calc_derived_de_epar_hel(grid_eq,eq_1,Rchi,Zchi,&
4714 &de,D_de,b_n,b_g)
4715
4716 use helena_vars, only: rbphi_h
4717
4718 ! input / output
4719 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
4720 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium variables
4721 real(dp), intent(in) :: rchi(:,:,0:) !< chi and chi^2 derivatives of R
4722 real(dp), intent(in) :: zchi(:,:,0:) !< chi and chi^2 derivatives of Z
4723 real(dp), intent(out) :: de(:,:,:,:,:,:) !< derivs of cov. unit vector in E (space,deriv.,unitvec,output)
4724 real(dp), intent(out) :: d_de(:,:,:,:,:) !< derivs of transf. matrix in E (space,deriv.,output)
4725 real(dp), intent(out) :: b_n(:,:,:,:) !< covariant Cylindrical components of normal basis vector
4726 real(dp), intent(out) :: b_g(:,:,:,:) !< covariant Cylindrical components of geodesic basis vector
4727
4728 ! local variables
4729 integer :: kd_h ! kd in Helena tables
4730 real(dp), allocatable :: dum1(:,:) ! dummy variable
4731 real(dp), allocatable :: dum2(:,:) ! dummy variable
4732
4733 ! initialize output
4734 de = 0._dp
4735 d_de = 0._dp
4736 b_n = 0._dp
4737 b_g = 0._dp
4738
4739 ! calculate the derivatives of covariant unit vectors in Equilibrium
4740 ! coordinates
4741
4742 ! d/dtheta e_theta
4743 de(:,1,:,1,1,1) = rchi(:,:,2) ! ~ e_R
4744 !de(:,1,:,1,1,2) = 0._dp ! ~ e_phi
4745 de(:,1,:,1,1,3) = zchi(:,:,2) ! ~ e_Z
4746
4747 ! d/dzeta e_theta
4748 !de(:,1,:,2,1,1) = 0._dp ! ~ e_R
4749 de(:,1,:,2,1,2) = -rchi(:,:,1)/rchi(:,:,0) ! ~ e_phi
4750 !de(:,1,:,2,1,3) = 0._dp ! ~ e_Z
4751
4752 ! d/dtheta e_zeta
4753 !de(:,1,:,1,2,1) = 0._dp ! ~ e_R
4754 de(:,1,:,1,2,2) = -rchi(:,:,1)/rchi(:,:,0) ! ~ e_phi
4755 !de(:,1,:,1,2,3) = 0._dp ! ~ e_Z
4756
4757 ! d/dzeta e_zeta
4758 de(:,1,:,2,2,1) = -rchi(:,:,0) ! ~ e_R
4759 !de(:,1,:,2,2,2) = 0._dp ! ~ e_phi
4760 !de(:,1,:,2,2,3) = 0._dp ! ~ e_Z
4761
4762 ! Decompose normal and geodesic basis vectors into contravariant
4763 ! cylindrical basis vectors.
4764 do jd = 1,grid_eq%n(2)
4765 ! auxiliary variables
4766 allocate(dum1(grid_eq%n(1),grid_eq%loc_n_r)) ! Rchi^2 + Zchi^2
4767 allocate(dum2(grid_eq%n(1),grid_eq%loc_n_r)) ! Rchi^2 + Zchi^2 + (qR)^2
4768 dum1 = rchi(:,:,1)**2 + zchi(:,:,1)**2
4769 do kd = 1,grid_eq%loc_n_r
4770 dum2(:,kd) = dum1(:,kd) + &
4771 &(eq_1%q_saf_FD(kd,0)*rchi(:,kd,0))**2
4772 end do
4773
4774 ! b_n
4775 b_n(:,jd,:,1) = rchi(:,:,0)*zchi(:,:,1)/dum1
4776 b_n(:,jd,:,3) = -rchi(:,:,0)*rchi(:,:,1)/dum1
4777
4778 ! b_g
4779 b_g(:,jd,:,1) = -rchi(:,:,0)**2 * rchi(:,:,1)/dum2
4780 b_g(:,jd,:,2) = -rchi(:,:,0)**2 * dum1/dum2
4781 b_g(:,jd,:,3) = -rchi(:,:,0)**2 * zchi(:,:,1)/dum2
4782
4783 ! clean up
4784 deallocate(dum1,dum2)
4785 end do
4786 do kd = 1,grid_eq%loc_n_r
4787 kd_h = grid_eq%i_min-1+kd
4788 b_n(:,:,kd,:) = b_n(:,:,kd,:) * &
4789 &eq_1%q_saf_FD(kd,0)/rbphi_h(kd_h,0)
4790 b_g(:,:,kd,1:3:2) = b_g(:,:,kd,1:3:2) * eq_1%q_saf_FD(kd,0) ! not b_g(2)
4791 end do
4792 end subroutine calc_derived_de_epar_hel
4793
4794 !> \private Calculate normal and geodesic curvature components using
4795 !! VMEC method.
4796 subroutine calc_derived_kappa_from_e(grid_eq,eq_1,eq_2,b_n,b_g,&
4797 &kappa_n,kappa_g)
4798
4799 use num_utilities, only: c
4800
4801 ! input / output
4802 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
4803 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium variables
4804 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium variables
4805 real(dp), intent(in) :: b_n(:,:,:,:) !< covariant Cylindrical components of normal basis vector
4806 real(dp), intent(in) :: b_g(:,:,:,:) !< covariant Cylindrical components of geodesic basis vector
4807 real(dp), intent(out) :: kappa_n(:,:,:) !< normal curvature
4808 real(dp), intent(out) :: kappa_g(:,:,:) !< geodesic curvature
4809
4810 ! calculate curvature: dot d/dtheta e_theta and basis vectors
4811 kappa_n = 0._dp
4812 kappa_g = 0._dp
4813 do ld = 1,3
4814 kappa_n = kappa_n + d3_epar(:,:,:,ld) * b_n(:,:,:,ld)
4815 kappa_g = kappa_g + d3_epar(:,:,:,ld) * b_g(:,:,:,ld)
4816 end do
4817
4818 ! divide by |e_theta|^2
4819 kappa_n = kappa_n / eq_2%g_FD(:,:,:,c([3,3],.true.),0,0,0)
4820 kappa_g = kappa_g / eq_2%g_FD(:,:,:,c([3,3],.true.),0,0,0)
4821
4822 ! possibly correct for toroidal flux
4823 if (.not.use_pol_flux_f) then
4824 do kd = 1,grid_eq%loc_n_r
4825 kappa_n(:,:,kd) = kappa_n(:,:,kd) * eq_1%q_saf_E(kd,0)
4826 kappa_g(:,:,kd) = kappa_g(:,:,kd) / eq_1%q_saf_E(kd,0)
4827 end do
4828 end if
4829 end subroutine calc_derived_kappa_from_e
4830
4831 !> \private Calculate parallel current using VMEC method.
4832 subroutine calc_derived_sigma_from_e(eq_2,b_n,sigma)
4833 use num_utilities, only: c
4834
4835 ! input / output
4836 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium variables
4837 real(dp), intent(in) :: b_n(:,:,:,:) !< covariant Cylindrical components of normal basis vector
4838 real(dp), intent(out) :: sigma(:,:,:) !< parallel current
4839
4840 ! calculate the parallel current
4841 sigma = 0._dp
4842 do ld = 1,3
4843 sigma = sigma + 2._dp * b_n(:,:,:,ld) * &
4844 &(d3_epar(:,:,:,ld) * &
4845 &eq_2%g_FD(:,:,:,c([1,3],.true.),0,0,0) - &
4846 &d1_epar(:,:,:,ld) * &
4847 &eq_2%g_FD(:,:,:,c([3,3],.true.),0,0,0)) / &
4848 &eq_2%jac_FD(:,:,:,0,0,0)**2
4849 end do
4850
4851 ! add shear term and divide by -B_theta mu_0
4852 sigma = -(sigma + eq_2%jac_FD(:,:,:,0,0,0) * &
4853 &eq_2%h_FD(:,:,:,c([2,2],.true.),0,0,0)*eq_2%S) * &
4854 &eq_2%jac_FD(:,:,:,0,0,0)/&
4855 &(vac_perm*eq_2%g_FD(:,:,:,c([3,3],.true.),0,0,0))
4856 end subroutine calc_derived_sigma_from_e
4857
4858 !> \private Calculate parallel current using HELENA method.
4859 subroutine calc_derived_sigma_hel(grid_eq,eq_2,sigma)
4860 use helena_vars, only: rbphi_h
4861 use num_utilities, only: c
4862
4863 ! input / output
4864 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
4865 type(eq_2_type), intent(in), target :: eq_2 !< metric equilibrium variables
4866 real(dp), intent(out) :: sigma(:,:,:) !< parallel current
4867
4868 ! local variables
4869 integer :: kd ! counter
4870 integer :: kd_h ! kd in Helena tables
4871 real(dp), pointer :: j(:,:,:) => null() ! jac
4872 real(dp), pointer :: g13(:,:,:) => null() ! g_alpha,theta
4873 real(dp), pointer :: g33(:,:,:) => null() ! g_theta,theta
4874
4875 ! set up submatrices
4876 j => eq_2%jac_FD(:,:,:,0,0,0)
4877 g13 => eq_2%g_FD(:,:,:,c([1,3],.true.),0,0,0)
4878 g33 => eq_2%g_FD(:,:,:,c([3,3],.true.),0,0,0)
4879
4880 ! calculate parallel current using direct formula
4881 do kd = 1,grid_eq%loc_n_r
4882 kd_h = grid_eq%i_min-1+kd
4883 sigma(:,:,kd) = -rbphi_h(kd_h,1) / vac_perm - &
4884 &eq_1%pres_FD(kd,1)*j(:,:,kd)*g13(:,:,kd)/g33(:,:,kd)
4885 end do
4886
4887 ! clean up
4888 nullify(j,g13,g33)
4889 end subroutine calc_derived_sigma_hel
4890
4891#if ldebug
4892 !> \private Plot derived equilibrium quantities for debug.
4893 integer function plot_derived_q(grid_eq,eq_2) result(ierr)
4897 use grid_vars, only: alpha
4898 use eq_vars, only: max_flux_f, b_0, r_0
4899
4900 character(*), parameter :: rout_name = 'plot_derived_q'
4901
4902 ! input / output
4903 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
4904 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium variables
4905
4906 ! local variables
4907 type(grid_type) :: grid_trim ! trimmed equilibrium grid
4908 real(dp) :: norm_factors(4) ! normalization factors
4909 real(dp), allocatable :: x_plot(:,:,:) ! x values of total plot
4910 real(dp), allocatable :: y_plot(:,:,:) ! y values of total plot
4911 real(dp), allocatable :: z_plot(:,:,:) ! z values of total plot
4912 real(dp), pointer :: ang_par_f(:,:,:) => null() ! parallel angle theta_F or zeta_F
4913 integer :: norm_id(2) ! untrimmed normal indices for trimmed grids
4914 integer :: plot_dim(3) ! dimensions of plot
4915 integer :: plot_offset(3) ! local offset of plot
4916 logical :: cont_plot ! continued plot
4917
4918 ! initialize ierr
4919 ierr = 0
4920
4921 call writo('Plotting derived equilibrium quantities')
4922 call lvl_ud(1)
4923
4924 ! trim equilibrium grid
4925 ierr = trim_grid(grid_eq,grid_trim,norm_id)
4926 chckerr('')
4927
4928 ! allocate variables
4929 allocate(x_plot(grid_trim%n(1),grid_trim%n(2),grid_trim%loc_n_r))
4930 allocate(y_plot(grid_trim%n(1),grid_trim%n(2),grid_trim%loc_n_r))
4931 allocate(z_plot(grid_trim%n(1),grid_trim%n(2),grid_trim%loc_n_r))
4932
4933 ! point parallel angle
4934 if (use_pol_flux_f) then
4935 ang_par_f => grid_eq%theta_F
4936 else
4937 ang_par_f => grid_eq%zeta_F
4938 end if
4939
4940 ! calculate grid
4941 select case (prog_style)
4942 case (1) ! PB3D
4943 select case (eq_style)
4944 case (1) ! VMEC
4945 x_plot = ang_par_f(:,:,norm_id(1):norm_id(2))
4946 do jd = 1,grid_trim%n(2)
4947 y_plot(:,jd,:) = alpha(jd)
4948 end do
4949 do kd = 1,grid_trim%loc_n_r
4950 z_plot(:,:,kd) = &
4951 &grid_trim%r_F(kd)*2*pi/max_flux_f
4952 end do
4953 case (2) ! HELENA
4954 ierr = calc_xyz_grid(grid_eq,grid_trim,x_plot,&
4955 &y_plot,z_plot)
4956 chckerr('')
4957 end select
4958 case (2) ! POST
4959 ierr = calc_xyz_grid(grid_eq,grid_trim,x_plot,y_plot,z_plot)
4960 chckerr('')
4961 end select
4962
4963 ! set up plot dimensions and offset
4964 select case (eq_style)
4965 case (1) ! VMEC
4966 plot_dim = [eq_jobs_lims(2,size(eq_jobs_lims,2)) - &
4967 &eq_jobs_lims(1,1) + 1, grid_trim%n(2:3)]
4968 plot_offset = [eq_jobs_lims(1,eq_job_nr)-1,0,&
4969 &grid_trim%i_min-1]
4970 cont_plot = eq_job_nr.gt.1
4971 case (2) ! HELENA
4972 plot_dim = grid_trim%n
4973 plot_offset = [0,0,grid_trim%i_min-1]
4974 cont_plot = .false.
4975 end select
4976
4977 ! set up normalization factors
4978 norm_factors = 1._dp
4979 if (use_normalization) then
4980 norm_factors(1) = 1._dp/(mu_0_original*r_0) ! sigma
4981 norm_factors(2) = 1._dp/(r_0**3) ! S
4982 norm_factors(3) = 1._dp/(r_0*b_0) ! kappa_n
4983 norm_factors(4) = 1._dp ! kappa_g
4984 end if
4985
4986 ! plot sigma
4987 call plot_hdf5('sigma','TEST_sigma',&
4988 &eq_2%sigma(:,:,norm_id(1):norm_id(2))*norm_factors(1),&
4989 &tot_dim=plot_dim,loc_offset=plot_offset,cont_plot=cont_plot,&
4990 &x=x_plot,y=y_plot,z=z_plot)
4991
4992 ! plot shear
4993 call plot_hdf5('shear','TEST_S',&
4994 &eq_2%S(:,:,norm_id(1):norm_id(2))*norm_factors(2),&
4995 &tot_dim=plot_dim,loc_offset=plot_offset,cont_plot=cont_plot,&
4996 &x=x_plot,y=y_plot,z=z_plot)
4997
4998 ! plot kappa_n
4999 call plot_hdf5('kappa_n','TEST_kappa_n',&
5000 &eq_2%kappa_n(:,:,norm_id(1):norm_id(2))*norm_factors(3),&
5001 &tot_dim=plot_dim,loc_offset=plot_offset,cont_plot=cont_plot,&
5002 &x=x_plot,y=y_plot,z=z_plot)
5003
5004 ! plot kappa_g
5005 call plot_hdf5('kappa_g','TEST_kappa_g',&
5006 &eq_2%kappa_g(:,:,norm_id(1):norm_id(2))*norm_factors(4),&
5007 &tot_dim=plot_dim,loc_offset=plot_offset,cont_plot=cont_plot,&
5008 &x=x_plot,y=y_plot,z=z_plot)
5009
5010 ! clean up
5011 nullify(ang_par_f)
5012 call grid_trim%dealloc()
5013
5014 call lvl_ud(-1)
5015 end function plot_derived_q
5016
5017 !> \private test whether -2 p' J kappa_g = D3sigma
5018 integer function test_sigma_with_kappa_g(grid_eq,eq_1,eq_2) result(ierr)
5019 use num_utilities, only: spline, calc_int
5020 use num_vars, only: norm_disc_prec_eq
5021
5022 character(*), parameter :: rout_name = 'test_sigma_with_kappa_g'
5023
5024 ! input / output
5025 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
5026 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium variables
5027 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium variables
5028
5029 ! local variables
5030 real(dp), allocatable :: sigma_alt(:,:,:) ! alternative sigma
5031 real(dp), allocatable :: d3sigma(:,:,:) ! D_theta sigma
5032 real(dp), allocatable :: d3sigma_alt(:,:,:) ! alternative D_theta sigma
5033 real(dp), pointer :: ang_par_f(:,:,:) => null() ! parallel angle theta_F or zeta_F
5034
5035 ! initialize ierr
5036 ierr = 0
5037
5038 call writo('Testing whether -2 p'' J kappa_g = D3sigma')
5039 call lvl_ud(1)
5040
5041 ! point parallel angle
5042 if (use_pol_flux_f) then
5043 ang_par_f => grid_eq%theta_F
5044 else
5045 ang_par_f => grid_eq%zeta_F
5046 end if
5047
5048 ! allocate variables
5049 allocate(d3sigma(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r))
5050 allocate(d3sigma_alt(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r))
5051 allocate(sigma_alt(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r))
5052
5053 ! get derivative of sigma
5054 do kd = 1,grid_eq%loc_n_r
5055 do jd = 1,grid_eq%n(2)
5056 ierr = spline(ang_par_f(:,jd,kd),eq_2%sigma(:,jd,kd),&
5057 &ang_par_f(:,jd,kd),d3sigma(:,jd,kd),&
5058 &ord=norm_disc_prec_eq,deriv=1)
5059 chckerr('')
5060 end do
5061 end do
5062
5063 ! calculate alternatively derived sigma
5064 do kd = 1,grid_eq%loc_n_r
5065 d3sigma_alt(:,:,kd) = -2*eq_1%pres_FD(kd,1)*&
5066 &eq_2%kappa_g(:,:,kd)*eq_2%jac_FD(:,:,kd,0,0,0)
5067 end do
5068
5069 ! calculate alternative sigma by integration
5070 ! Note: there is an undetermined constant of integration that
5071 ! depends on the normal coordinate and the geodesic coordinate.
5072 do kd = 1,grid_eq%loc_n_r
5073 do jd = 1,grid_eq%n(2)
5074 ierr = calc_int(d3sigma_alt(:,jd,kd),ang_par_f(:,jd,kd),&
5075 &sigma_alt(:,jd,kd))
5076 chckerr('')
5077 end do
5078 sigma_alt(:,:,kd) = eq_2%sigma(1,1,kd) + sigma_alt(:,:,kd)
5079 end do
5080
5081 ! plot output
5082 call plot_diff_hdf5(d3sigma,d3sigma_alt,&
5083 &'TEST_diff_D3sigma_through_kappa_g',&
5084 &grid_eq%n,[0,0,grid_eq%i_min-1],&
5085 &descr='To test whether -2 p'' J kappa_g = D3sigma',&
5086 &output_message=.true.)
5087 call plot_diff_hdf5(eq_2%sigma,sigma_alt,&
5088 &'TEST_diff_sigma_through_kappa_g',&
5089 &grid_eq%n,[0,0,grid_eq%i_min-1],descr='To test whether &
5090 &int(-2 p'' J kappa_g) = sigma',output_message=.true.)
5091
5092 ! clean up
5093 nullify(ang_par_f)
5094
5095 call lvl_ud(-1)
5096 end function test_sigma_with_kappa_g
5097
5098 !> \private test agreement between curvatures and naive implementation
5099 integer function test_kappa(grid_eq,eq_1,eq_2) result(ierr)
5100 use num_utilities, only: c
5101
5102 character(*), parameter :: rout_name = 'test_kappa'
5103
5104 ! input / output
5105 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
5106 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium variables
5107 type(eq_2_type), intent(in), target :: eq_2 !< metric equilibrium variables
5108
5109 ! local variables
5110 real(dp), allocatable :: kappa_alt(:,:,:,:) ! alternative kappa_n (1) and kappa_g(2)
5111 real(dp), pointer :: j(:,:,:) => null() ! jac
5112 real(dp), pointer :: d1j(:,:,:) => null() ! D_alpha jac
5113 real(dp), pointer :: d2j(:,:,:) => null() ! D_psi jac
5114 real(dp), pointer :: d3j(:,:,:) => null() ! D_theta jac
5115 real(dp), pointer :: g13(:,:,:) => null() ! g_alpha,theta
5116 real(dp), pointer :: g33(:,:,:) => null() ! g_theta,theta
5117 real(dp), pointer :: d1g33(:,:,:) => null() ! D_alpha g_theta,theta
5118 real(dp), pointer :: d2g33(:,:,:) => null() ! D_psi g_theta,theta
5119 real(dp), pointer :: d3g33(:,:,:) => null() ! D_theta g_theta,theta
5120 real(dp), pointer :: h12(:,:,:) => null() ! h_alpha,psi
5121 real(dp), pointer :: h22(:,:,:) => null() ! h_psi,psi
5122 real(dp), pointer :: h23(:,:,:) => null() ! h_psi,theta
5123
5124 ! initialize ierr
5125 ierr = 0
5126
5127 call writo('Testing whether kappa agrees with naive implementation')
5128 call lvl_ud(1)
5129
5130 ! set up submatrices
5131 j => eq_2%jac_FD(:,:,:,0,0,0)
5132 d1j => eq_2%jac_FD(:,:,:,1,0,0)
5133 d2j => eq_2%jac_FD(:,:,:,0,1,0)
5134 d3j => eq_2%jac_FD(:,:,:,0,0,1)
5135 g13 => eq_2%g_FD(:,:,:,c([1,3],.true.),0,0,0)
5136 g33 => eq_2%g_FD(:,:,:,c([3,3],.true.),0,0,0)
5137 d1g33 => eq_2%g_FD(:,:,:,c([3,3],.true.),1,0,0)
5138 d2g33 => eq_2%g_FD(:,:,:,c([3,3],.true.),0,1,0)
5139 d3g33 => eq_2%g_FD(:,:,:,c([3,3],.true.),0,0,1)
5140 h12 => eq_2%h_FD(:,:,:,c([1,2],.true.),0,0,0)
5141 h22 => eq_2%h_FD(:,:,:,c([2,2],.true.),0,0,0)
5142 h23 => eq_2%h_FD(:,:,:,c([2,3],.true.),0,0,0)
5143
5144 ! initialize
5145 allocate(kappa_alt(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r,2))
5146 kappa_alt = 0._dp
5147
5148 ! Calculate naive normal curvature kappa_n
5149 do kd = 1,grid_eq%loc_n_r
5150 kappa_alt(:,:,kd,1) = &
5151 &vac_perm*j(:,:,kd)**2*eq_1%pres_FD(kd,1)/g33(:,:,kd) + &
5152 &1._dp/(2*h22(:,:,kd)) * ( &
5153 &h12(:,:,kd) * ( d1g33(:,:,kd)/g33(:,:,kd) - &
5154 &2*d1j(:,:,kd)/j(:,:,kd) ) + &
5155 &h22(:,:,kd) * ( d2g33(:,:,kd)/g33(:,:,kd) - &
5156 &2*d2j(:,:,kd)/j(:,:,kd) ) + &
5157 &h23(:,:,kd) * ( d3g33(:,:,kd)/g33(:,:,kd) - &
5158 &2*d3j(:,:,kd)/j(:,:,kd) ) )
5159 end do
5160
5161 ! Calculate naive geodesic curvature kappa_g
5162 kappa_alt(:,:,:,2) = (0.5*d1g33/g33 - d1j/j) - &
5163 &g13/g33*(0.5*d3g33/g33 - d3j/j)
5164
5165 call plot_diff_hdf5(eq_2%kappa_n,kappa_alt(:,:,:,1),&
5166 &'TEST_diff_kappa_n',grid_eq%n,[0,0,grid_eq%i_min-1],&
5167 &descr='To test whether kappa_n agrees with naive calculation',&
5168 &output_message=.true.)
5169 call plot_diff_hdf5(eq_2%kappa_g,kappa_alt(:,:,:,2),&
5170 &'TEST_diff_kappa_g',grid_eq%n,[0,0,grid_eq%i_min-1],&
5171 &descr='To test whether kappa_g agrees with naive calculation',&
5172 &output_message=.true.)
5173
5174 !!! temporary: for 2018 paper
5175 !!ierr = plot_diff_for_paper(grid_eq%loc_r_F,eq_2%kappa_n,&
5176 !!&kappa_ALT(:,:,:,1),'kappa_n')
5177 !!CHCKERR('')
5178 !!ierr = plot_diff_for_paper(grid_eq%loc_r_F,eq_2%kappa_g,&
5179 !!&kappa_ALT(:,:,:,2),'kappa_g')
5180 !!CHCKERR('')
5181
5182 ! clean up
5183 nullify(j,d1j,d2j,d3j,g13,g33,d1g33,d2g33,d3g33,h12,h22,h23)
5184
5185 call lvl_ud(-1)
5186 end function test_kappa
5187
5188 !> \private test agreement between parallel current and naive
5189 !! implementation for VMEC
5190 integer function test_sigma_vmec(grid_eq,eq_1,eq_2) result(ierr)
5191 use num_utilities, only: spline, calc_int, c
5192 use num_vars, only: norm_disc_prec_eq
5193 use vmec_vars, only: b_v_sub_s, b_v_sub_c, is_asym_v
5194 use vmec_utilities, only: fourier2real
5195
5196 character(*), parameter :: rout_name = 'test_sigma_VMEC'
5197
5198 ! input / output
5199 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
5200 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium variables
5201 type(eq_2_type), intent(in), target :: eq_2 !< metric equilibrium variables
5202
5203 ! local variables
5204 real(dp), allocatable :: sigma_alt(:,:,:) ! alternative sigma
5205 real(dp), allocatable :: b_v(:,:,:,:) ! magnetic field in VMEC coordinates
5206 real(dp), allocatable :: b_alpha(:,:,:) ! B_alpha Flux coordinates
5207 real(dp), pointer :: j(:,:,:) => null() ! jac
5208 real(dp), pointer :: d1j(:,:,:) => null() ! D_alpha jac
5209 real(dp), pointer :: g13(:,:,:) => null() ! g_alpha,theta
5210 real(dp), pointer :: g23(:,:,:) => null() ! g_psi,theta
5211 real(dp), pointer :: d1g23(:,:,:) => null() ! D_alpha g_psi,theta
5212 real(dp), pointer :: g33(:,:,:) => null() ! g_theta,theta
5213
5214 ! initialize ierr
5215 ierr = 0
5216
5217 call writo('Testing whether sigma agrees with naive implementation')
5218 call lvl_ud(1)
5219
5220 ! set up submatrices
5221 j => eq_2%jac_FD(:,:,:,0,0,0)
5222 d1j => eq_2%jac_FD(:,:,:,1,0,0)
5223 g13 => eq_2%g_FD(:,:,:,c([1,3],.true.),0,0,0)
5224 g23 => eq_2%g_FD(:,:,:,c([2,3],.true.),0,0,0)
5225 d1g23 => eq_2%g_FD(:,:,:,c([2,3],.true.),1,0,0)
5226 g33 => eq_2%g_FD(:,:,:,c([3,3],.true.),0,0,0)
5227
5228 ! initialize
5229 allocate(sigma_alt(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r))
5230 sigma_alt = 0._dp
5231
5232 ! magnetic field in VMEC coordinates
5233 allocate(b_v(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r,2:3))
5234 do id = 2,3 ! only angular V components count for e_alpha
5235 ierr = fourier2real(&
5236 &b_v_sub_c(:,grid_eq%i_min:grid_eq%i_max,id),&
5237 &b_v_sub_s(:,grid_eq%i_min:grid_eq%i_max,id),&
5238 &grid_eq%trigon_factors,b_v(:,:,:,id),&
5239 &[.true.,is_asym_v])
5240 chckerr('')
5241 end do
5242
5243 ! transform them to Flux coord. system
5244 allocate(b_alpha(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r))
5245 b_alpha = 0._dp
5246 do kd = 2,3
5247 b_alpha = b_alpha + b_v(:,:,:,kd) * &
5248 &eq_2%T_FE(:,:,:,c([1,kd],.false.),0,0,0)
5249 end do
5250
5251 !!! more elegant but less accurate alternative:
5252 !!B_alpha = g13/J
5253
5254 ! derivate in normal direction
5255 do jd = 1,grid_eq%n(2)
5256 do id = 1,grid_eq%n(1)
5257 ierr = spline(grid_eq%loc_r_F,b_alpha(id,jd,:),&
5258 &grid_eq%loc_r_F,sigma_alt(id,jd,:),&
5259 &ord=norm_disc_prec_eq,deriv=1)
5260 chckerr('')
5261 end do
5262 end do
5263
5264 ! contribute to sigma
5265 sigma_alt = (d1g23 - g23*d1j/j)/j - sigma_alt
5266
5267 !!! equally elegant but also inaccurate second alternative:
5268 !!sigma_ALT = (D1g23 - g23*D1J/J)/J - (D2g13 - g13*D2J/J)/J
5269 do kd = 1,grid_eq%loc_n_r
5270 sigma_alt(:,:,kd) = sigma_alt(:,:,kd) / vac_perm - &
5271 &eq_1%pres_FD(kd,1)*j(:,:,kd)*g13(:,:,kd)/g33(:,:,kd)
5272 end do
5273
5274 call plot_diff_hdf5(eq_2%sigma,sigma_alt,'TEST_diff_sigma',&
5275 &grid_eq%n,[0,0,grid_eq%i_min-1],descr='To test whether &
5276 &sigma agrees with naive calculation',output_message=.true.)
5277
5278 !!! temporary: for 2018 paper
5279 !!ierr = plot_diff_for_paper(grid_eq%loc_r_F,eq_2%sigma,sigma_ALT,&
5280 !!&'sigma')
5281 !!CHCKERR('')
5282
5283 ! clean up
5284 nullify(j,d1j,g13,g23,d1g23,g33)
5285
5286 call lvl_ud(-1)
5287 end function test_sigma_vmec
5288
5289 !> \private test agreement between shear and implementation using
5290 !! identity to relate to sigma
5291 subroutine test_s_hel(grid_eq,eq_2,Rchi,Zchi)
5292 ! input / output
5293 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
5294 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium variables
5295 real(dp), intent(in) :: rchi(:,:,0:) !< chi and chi^2 derivatives of R
5296 real(dp), intent(in) :: zchi(:,:,0:) !< chi and chi^2 derivatives of Z
5297
5298 ! local variables
5299 real(dp), allocatable :: s_alt(:,:,:) ! alternative shear
5300
5301 call writo('Testing whether shear agrees with alternative &
5302 &implementation using sigma')
5303 call lvl_ud(1)
5304
5305 allocate(s_alt(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r))
5306
5307 ! calculate implementation with identity using sigma
5308 call calc_derived_s_from_sigma_hel(grid_eq,eq_2,rchi,zchi,s_alt)
5309 call plot_diff_hdf5(eq_2%S,s_alt,'TEST_diff_S_from_sigma',&
5310 &grid_eq%n,[0,0,grid_eq%i_min-1],descr='To test whether &
5311 &sigma agrees with naive calculation',output_message=.true.)
5312
5313 ! calculate naive implementation
5314 call calc_derived_s_direct(eq_2,s_alt)
5315 call plot_diff_hdf5(eq_2%S,s_alt,'TEST_diff_S_naive',&
5316 &grid_eq%n,[0,0,grid_eq%i_min-1],descr='To test whether &
5317 &sigma agrees with naive calculation',output_message=.true.)
5318
5319 call lvl_ud(-1)
5320 end subroutine test_s_hel
5321
5322 !> \private make plot for 2018 paper; requires one process only.
5323 integer function plot_diff_for_paper(r, A, B, title) result(ierr)
5324 use num_vars, only: n_procs
5325
5326 character(*), parameter :: rout_name = 'plot_diff_for_paper'
5327
5328 ! input / output
5329 real(dp), intent(in) :: r(:) !< r at which tabulated
5330 real(dp), intent(in) :: a(:,:,:) !< GOOD value (optimized implementation)
5331 real(dp), intent(in) :: b(:,:,:) !< BAD value (naive implementation)
5332 character(len=*), intent(in) :: title !< plot title
5333
5334 ! local variables
5335 integer :: jd, kd ! counters
5336 integer :: n_r ! size of r
5337 integer :: n_par ! number of parallel points
5338 character(len=max_str_ln) :: plot_title(3) ! titles of plot (GOOD, BAD, diff)
5339
5340 ! initialize ierr
5341 ierr = 0
5342
5343 if (n_procs.gt.1) then
5344 ierr = 1
5345 chckerr('Need 1 process')
5346 end if
5347
5348 n_r = size(r)
5349 n_par = size(a, 1)
5350
5351 do jd = 1,size(a,2)
5352 plot_title(1) = trim(title)//'_GOOD'
5353 plot_title(2) = trim(title)//'_BAD'
5354 plot_title(3) = trim(title)//'_DIFF'
5355 if (jd.gt.1) then
5356 do kd = 1,3
5357 plot_title(kd) = trim(plot_title(kd))//'_'//&
5358 &trim(i2str(kd))
5359 end do
5360 end if
5361 call print_ex_2d([''],plot_title(1),transpose(a(:,jd,:)),&
5362 &x=reshape(r*2*pi/max_flux_f,[n_r,1]),draw=.false.)
5363 call draw_ex([''],plot_title(1),n_par,1,.false.)
5364 call print_ex_2d([''],plot_title(2),transpose(b(:,jd,:)),&
5365 &x=reshape(r*2*pi/max_flux_f,[n_r,1]),draw=.false.)
5366 call draw_ex([''],plot_title(2),n_par,1,.false.)
5367 call print_ex_2d([''],plot_title(3),&
5368 &transpose(abs(b(:,jd,:)-a(:,jd,:))/&
5369 &(abs(b(:,jd,:))+abs(a(:,jd,:)))),&
5370 &x=reshape(r*2*pi/max_flux_f,[n_r,1]),draw=.false.)
5371 call draw_ex([''],plot_title(3),n_par,1,.false.)
5372 end do
5373 end function plot_diff_for_paper
5374#endif
5375 end function calc_derived_q
5376
5377 !> Sets up normalization constants.
5378 !!
5379 !! - VMEC version (\c eq_style=1):\n
5380 !! Normalization depends on \c norm_style:
5381 !! -# MISHKA
5382 !! - R_0: major radius (= average magnetic axis)
5383 !! - B_0: B on magnetic axis (theta = zeta = 0)
5384 !! - pres_0: reference pressure (= B_0^2/mu_0)
5385 !! - psi_0: reference flux (= R_0^2 B_0)
5386 !! - rho_0: reference mass density
5387 !! -# COBRA
5388 !! - R_0: major radius (= average geometric axis)
5389 !! - pres_0: pressure on magnetic axis
5390 !! - B_0: reference magnetic field (= sqrt(2pres_0mu_0 / beta))
5391 !! - psi_0: reference flux (= R_0^2 B_0 / aspr^2)
5392 !! - rho_0: reference mass density
5393 !! where aspr (aspect ratio) and beta are given by VMEC.
5394 !! - HELENA version (\c eq_style=2):\n
5395 !! MISHKA Normalization is used by default and does not depend
5396 !! on \c norm_style
5397 !!
5398 !! \see read_hel()
5399 !!
5400 !! \note \c rho_0 is not given through by the equilibrium codes and should
5401 !! be user-supplied
5402 !!
5403 !! \return ierr
5404 integer function calc_normalization_const() result(ierr)
5407 use eq_vars, only: t_0, b_0, pres_0, psi_0, r_0, rho_0
5408
5409 character(*), parameter :: rout_name = 'calc_normalization_const'
5410
5411 ! local variables
5412 integer :: nr_overridden_const ! nr. of user-overridden constants, to print warning if > 0
5413 character(len=max_str_ln) :: err_msg ! error message
5414
5415 ! initialize ierr
5416 ierr = 0
5417
5418 ! initialize BR_normalization_provided
5419 br_normalization_provided = [.false.,.false.]
5420
5421 if (use_normalization) then
5422 ! user output
5423 call writo('Calculating the normalization constants')
5424 call lvl_ud(1)
5425
5426 ! initialize nr_overridden_const
5427 nr_overridden_const = 0
5428
5429 ! calculation
5430 if (rank.eq.0) then
5431 ! choose which equilibrium style is being used:
5432 ! 1: VMEC
5433 ! 2: HELENA
5434 select case (eq_style)
5435 case (1) ! VMEC
5436 call calc_normalization_const_vmec
5437 case (2) ! HELENA
5438 call calc_normalization_const_hel
5439 end select
5440 end if
5441
5442 ! print warning if user-overridden
5443 if (nr_overridden_const.gt.0) &
5444 &call writo(trim(i2str(nr_overridden_const))//&
5445 &' constants were overridden by user. Consistency is NOT &
5446 &checked!',warning=.true.)
5447
5448 ! print constants
5449 call print_normalization_const(r_0,rho_0,b_0,pres_0,psi_0,&
5451
5452 ! check whether it is physically consistent
5453 if (t_0.lt.0._dp) then
5454 ierr = 1
5455 err_msg = 'Alfven time is negative. Are you sure the &
5456 &equilibrium is consistent?'
5457 chckerr(err_msg)
5458 end if
5459
5460 ! user output
5461 call lvl_ud(-1)
5462 call writo('Normalization constants calculated')
5463 else if (rich_restart_lvl.eq.1) then ! only for first Richardson leevel
5464 ! user output
5465 call writo('Normalization not used')
5466 end if
5467 contains
5468 ! VMEC version
5469 !> \private
5470 subroutine calc_normalization_const_vmec
5471 use num_vars, only: norm_style
5472 use vmec_vars, only: r_v_c, b_0_v, rmax_surf, rmin_surf, pres_v, &
5473 &beta_v
5474 !use VMEC_vars, only: aspr_V
5475
5476 select case (norm_style)
5477 ! Note that PB3D does not run with the exact COBRA normalization
5478 ! (for style 2), as it is not a pure nondimensionalization, that
5479 ! would modify the equations by intrucing extra factors. The
5480 ! diffference between the COBRA normalization used here and the
5481 ! one used in COBRA is given by:
5482 ! - B_0 = sqrt(pres_0 mu_0) here
5483 ! versus
5484 ! B_0 = sqrt(2 pres_0 mu_0 / beta) in COBRA,
5485 ! - psi_0 = B_0 R_0^2 here
5486 ! versus
5487 ! psi_0 = B_0 (R_0/aspr)^2 in COBRA,
5488 ! This results in a difference in the factor T_0:
5489 ! - T_0 = sqrt(rho_0/pres_0) R_0 here
5490 ! versus
5491 ! T_0 = sqrt(rho_0/pres_0) R_0 sqrt(beta/2) in COBRA.
5492 ! Therefore, the Eigenvalues here should be rescaled as
5493 ! - EV_COBRA = EV_PB3D beta/2. This is done automatically.
5494 case (1) ! MISHKA
5495 ! user output
5496 call writo('Using MISHKA normalization')
5497
5498 ! set the major radius as the average value of R_V on the
5499 ! magnetic axis
5500 if (r_0.ge.huge(1._dp)) then ! user did not provide a value
5501 r_0 = r_v_c(1,1,0)
5502 else
5503 nr_overridden_const = nr_overridden_const + 1
5504 end if
5505
5506 ! set B_0 from magnetic field on axis
5507 if (b_0.ge.huge(1._dp)) then ! user did not provide a value
5508 b_0 = b_0_v
5509 else
5510 nr_overridden_const = nr_overridden_const + 1
5511 end if
5512
5513 ! set reference pres_0 from B_0
5514 if (pres_0.ge.huge(1._dp)) then ! user did not provide a value
5516 else
5517 nr_overridden_const = nr_overridden_const + 1
5518 end if
5519
5520 ! set reference flux from R_0 and B_0
5521 if (psi_0.ge.huge(1._dp)) then ! user did not provide a value
5522 psi_0 = r_0**2 * b_0
5523 else
5524 nr_overridden_const = nr_overridden_const + 1
5525 end if
5526 case (2) ! COBRA
5527 ! user output
5528 call writo('Using COBRA normalization')
5529
5530 ! set the major radius as the average geometric axis
5531 if (r_0.ge.huge(1._dp)) then ! user did not provide a value
5532 r_0 = 0.5_dp*(rmin_surf+rmax_surf)
5533 else
5534 nr_overridden_const = nr_overridden_const + 1
5535 end if
5536
5537 ! set pres_0 from pressure on axis
5538 if (pres_0.ge.huge(1._dp)) then ! user did not provide a value
5539 pres_0 = pres_v(1,0)
5540 else
5541 nr_overridden_const = nr_overridden_const + 1
5542 end if
5543
5544 ! set the reference value for B_0 from pres_0 and beta
5545 if (b_0.ge.huge(1._dp)) then ! user did not provide a value
5546 !B_0 = sqrt(2._dp*pres_0*mu_0_original/beta_V) ! exact COBRA
5547 b_0 = sqrt(pres_0*mu_0_original) ! pure modified COBRA
5548 else
5549 nr_overridden_const = nr_overridden_const + 1
5550 end if
5551
5552 ! set reference flux from R_0, B_0 and aspr
5553 if (psi_0.ge.huge(1._dp)) then ! user did not provide a value
5554 !psi_0 = B_0 * (R_0/aspr_V)**2 ! exact COBRA
5555 psi_0 = b_0 * r_0**2 ! pure modified COBRA
5556 else
5557 nr_overridden_const = nr_overridden_const + 1
5558 end if
5559
5560 ! user output concerning pure modified COBRA
5561 call writo('Exact COBRA normalization is substituted by &
5562 &"pure", modified version',warning=.true.)
5563 call writo('This leaves the equations unmodified',&
5564 &alert=.true.)
5565 call writo('To translate to exact COBRA, manually multiply &
5566 &PB3D Eigenvalues by',alert=.true.)
5567 call lvl_ud(1)
5568 call writo(trim(r2str(beta_v/2)),alert=.true.)
5569 call lvl_ud(-1)
5570 end select
5571
5572 ! rho_0 is set up through an input variable with the same name
5573
5574 ! set Alfven time
5575 if (t_0.ge.huge(1._dp)) then ! user did not provide a value
5576 t_0 = sqrt(mu_0_original*rho_0)*r_0/b_0
5577 else
5578 nr_overridden_const = nr_overridden_const + 1
5579 end if
5580 end subroutine calc_normalization_const_vmec
5581
5582 ! HELENA version
5583 !> \private
5584 subroutine calc_normalization_const_hel
5585 ! user output
5586 call writo('Using MISHKA normalization')
5587
5588 if (r_0.ge.huge(1._dp)) then ! user did not provide a value
5589 r_0 = 1._dp
5590 else
5591 br_normalization_provided(1) = .true.
5592 end if
5593 if (b_0.ge.huge(1._dp)) then ! user did not provide a value
5594 b_0 = 1._dp
5595 else
5596 br_normalization_provided(2) = .true.
5597 end if
5598 if (pres_0.ge.huge(1._dp)) then ! user did not provide a value
5599 pres_0 = b_0**2/mu_0_original
5600 else
5601 nr_overridden_const = nr_overridden_const + 1
5602 end if
5603 if (psi_0.ge.huge(1._dp)) then ! user did not provide a value
5604 psi_0 = r_0**2 * b_0
5605 else
5606 nr_overridden_const = nr_overridden_const + 1
5607 end if
5608 if (t_0.ge.huge(1._dp)) t_0 = sqrt(mu_0_original*rho_0)*r_0/b_0 ! only if user did not provide a value
5609 end subroutine calc_normalization_const_hel
5610
5611 ! prints the Normalization factors
5612 !> \private
5613 subroutine print_normalization_const(R_0,rho_0,B_0,pres_0,psi_0,&
5614 &mu_0,T_0)
5615 ! input / output
5616 real(dp), intent(in), optional :: r_0
5617 real(dp), intent(in), optional :: rho_0
5618 real(dp), intent(in), optional :: b_0
5619 real(dp), intent(in), optional :: pres_0
5620 real(dp), intent(in), optional :: psi_0
5621 real(dp), intent(in), optional :: mu_0
5622 real(dp), intent(in), optional :: t_0
5623
5624 ! user output
5625 if (present(r_0)) call writo('R_0 = '//trim(r2str(r_0))//' m')
5626 if (present(rho_0)) call writo('rho_0 = '//trim(r2str(rho_0))//&
5627 &' kg/m^3')
5628 if (present(b_0)) call writo('B_0 = '//trim(r2str(b_0))//' T')
5629 if (present(pres_0)) call writo('pres_0 = '//trim(r2str(pres_0))//&
5630 &' Pa')
5631 if (present(psi_0)) call writo('psi_0 = '//trim(r2str(psi_0))//&
5632 &' Tm^2')
5633 if (present(mu_0)) call writo('mu_0 = '//&
5634 &trim(r2str(mu_0))//' Tm/A')
5635 if (present(t_0)) call writo('T_0 = '//trim(r2str(t_0))//' s')
5636 end subroutine print_normalization_const
5637 end function calc_normalization_const
5638
5639 !> Normalize input quantities.
5640 !!
5641 !! \see calc_normalization_const()
5642 subroutine normalize_input()
5644 use vmec_ops, only: normalize_vmec
5645 use eq_vars, only: vac_perm
5646
5647 ! only normalize if needed
5648 if (use_normalization) then
5649 ! user output
5650 call writo('Start normalizing the input variables')
5651 call lvl_ud(1)
5652
5653 ! normalize common variables
5655
5656 ! choose which equilibrium style is being used:
5657 ! 1: VMEC
5658 ! 2: HELENA
5659 select case (eq_style)
5660 case (1) ! VMEC
5661 call normalize_vmec
5662 case (2) ! HELENA
5663 ! other HELENA input already normalized
5664 call writo('HELENA input is already normalized with MISHKA &
5665 &normalization')
5666 end select
5667
5668 ! user output
5669 call lvl_ud(-1)
5670 call writo('Normalization done')
5671 end if
5672 end subroutine normalize_input
5673
5674 !> Plots the magnetic fields.
5675 !!
5676 !! If multiple equilibrium parallel jobs, every job does its piece, and the
5677 !! results are joined automatically by plot_HDF5.
5678 !!
5679 !! The outputs are given in contra- and covariant components and magnitude
5680 !! in multiple coordinate systems, as indicated in calc_vec_comp().
5681 !!
5682 !! The starting point is the fact that the magnetic field is given by
5683 !! \f[\vec{B} = \frac{\vec{e}_{\theta}}{\mathcal{J}}, \f]
5684 !! in F coordinates. The F covariant components are therefore given by
5685 !! \f[B_i = \frac{g_{i3}}{\mathcal{J}} =
5686 !! \frac{\vec{e}_i \cdot \vec{e}_3}{\mathcal{J}}, \f]
5687 !! and the only non-vanishing contravariant component is
5688 !! \f[B^3 = \frac{1}{\mathcal{J}}. \f]
5689 !!
5690 !! These are then all be transformed to the other coordinate systems.
5691 !!
5692 !! \note
5693 !! -# Vector plots for different Richardson levels can be combined to show
5694 !! the total grid by just plotting them all individually.
5695 !! -# The metric factors and transformation matrices have to be allocated.
5696 !!
5697 !! \return ierr
5698 integer function b_plot(grid_eq,eq_1,eq_2,rich_lvl,plot_fluxes,XYZ) &
5699 &result(ierr)
5700
5701 use grid_utilities, only: calc_vec_comp
5702 use eq_utilities, only: calc_inv_met
5704 use num_utilities, only: c
5705 use eq_vars, only: b_0
5706
5707 character(*), parameter :: rout_name = 'B_plot'
5708
5709 ! input / output
5710 type(grid_type), intent(inout) :: grid_eq !< equilibrium grid
5711 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium variables
5712 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium variables
5713 integer, intent(in), optional :: rich_lvl !< Richardson level
5714 logical, intent(in), optional :: plot_fluxes !< plot the fluxes
5715 real(dp), intent(in), optional :: xyz(:,:,:,:) !< X, Y and Z of grid
5716
5717 ! local variables
5718 integer :: id ! counter
5719 real(dp), allocatable :: b_com(:,:,:,:,:) ! covariant and contravariant components of B (dim1,dim2,dim3,3,2)
5720 real(dp), allocatable :: b_mag(:,:,:) ! magnitude of B (dim1,dim2,dim3)
5721 real(dp), allocatable, save :: b_flux_tor(:,:), b_flux_pol(:,:) ! fluxes
5722 character(len=10) :: base_name ! base name
5723 logical :: plot_fluxes_loc ! local plot_fluxes
5724
5725 ! initialize ierr
5726 ierr = 0
5727
5728 ! tests
5729 if (eq_style.eq.1 .and. .not.allocated(grid_eq%trigon_factors)) then
5730 ierr = 1
5731 chckerr('trigonometric factors not allocated')
5732 end if
5733
5734 ! set up local plot_fluxes
5735 plot_fluxes_loc = .false.
5736 if (present(plot_fluxes)) plot_fluxes_loc = plot_fluxes
5737
5738 ! set up components and magnitude of B
5739 allocate(b_com(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r,3,2))
5740 allocate(b_mag(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r))
5741 b_com = 0._dp
5742 b_mag = 0._dp
5743 do id = 1,3
5744 b_com(:,:,:,id,1) = eq_2%g_FD(:,:,:,c([3,id],.true.),0,0,0)/&
5745 &eq_2%jac_FD(:,:,:,0,0,0)
5746 end do
5747 b_com(:,:,:,3,2) = 1._dp/eq_2%jac_FD(:,:,:,0,0,0)
5748
5749 ! transform back to unnormalized quantity
5750 if (use_normalization) b_com = b_com * b_0
5751
5752 ! set plot variables
5753 base_name = 'B'
5754 if (present(rich_lvl)) then
5755 if (rich_lvl.gt.0) base_name = trim(base_name)//'_R_'//&
5756 &trim(i2str(rich_lvl))
5757 end if
5758
5759 ! transform coordinates, including the flux
5760 if (plot_fluxes_loc) then
5761 ierr = calc_vec_comp(grid_eq,grid_eq,eq_1,eq_2,b_com,&
5762 &norm_disc_prec_eq,v_mag=b_mag,base_name=base_name,&
5763 &v_flux_tor=b_flux_tor,v_flux_pol=b_flux_pol,xyz=xyz)
5764 chckerr('')
5765 else
5766 ierr = calc_vec_comp(grid_eq,grid_eq,eq_1,eq_2,b_com,&
5767 &norm_disc_prec_eq,v_mag=b_mag,base_name=base_name,xyz=xyz)
5768 chckerr('')
5769 end if
5770 end function b_plot
5771
5772 !> Plots the current.
5773 !!
5774 !! If multiple equilibrium parallel jobs, every job does its piece, and the
5775 !! results are joined automatically by plot_HDF5.
5776 !!
5777 !! The outputs are given in contra- and covariant components and magnitude
5778 !! in multiple coordinate systems, as indicated in calc_vec_comp().
5779 !!
5780 !! The starting point is the pressure balance
5781 !! \f[ \nabla p = \vec{J} \times \vec{B}, \f]
5782 !! which, using \f$\vec{B} = \frac{\vec{e}_{\theta}}{\mathcal{J}}\f$,
5783 !! reduces to
5784 !! \f[J^\alpha = -p'. \f]
5785 !! Furthermore, the current has to lie in the magnetic flux surfaces:
5786 !! \f[J^\psi = 0. \f]
5787 !! Finally, the parallel current \f$\sigma\f$ gives an expression for the
5788 !! last contravariant component:
5789 !! \f[J^\theta =
5790 !! \frac{\sigma}{\mathcal{J}} + p' \frac{B_\alpha}{B_\theta}. \f]
5791 !! From these, the contravariant components can be calculated as
5792 !! \f[J_i = J^\alpha g_{\alpha,i} + J^\theta g_{\theta,i}. \f]
5793 !!
5794 !! These are then all be transformed to the other coordinate systems.
5795 !! \note
5796 !! -# Vector plots for different Richardson levels can be combined to show
5797 !! the total grid by just plotting them all individually.
5798 !! -# The metric factors and transformation matrices have to be allocated.
5799 !!
5800 !! \return ierr
5801 integer function j_plot(grid_eq,eq_1,eq_2,rich_lvl,plot_fluxes,XYZ) &
5802 &result(ierr)
5803
5804 use grid_utilities, only: calc_vec_comp
5805 use eq_utilities, only: calc_inv_met
5807 use num_utilities, only: c
5808 use eq_vars, only: b_0, r_0, pres_0
5809#if ldebug
5810 use eq_vars, only: max_flux_f
5811 use vmec_vars, only: j_v_sup_int
5812 use num_utilities, only: calc_int
5813#endif
5814
5815 character(*), parameter :: rout_name = 'J_plot'
5816
5817 ! input / output
5818 type(grid_type), intent(inout) :: grid_eq !< equilibrium grid
5819 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium variables
5820 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium variables
5821 integer, intent(in), optional :: rich_lvl !< Richardson level
5822 logical, intent(in), optional :: plot_fluxes !< plot the fluxes
5823 real(dp), intent(in), optional :: xyz(:,:,:,:) !< X, Y and Z of grid
5824
5825 ! local variables
5826 integer :: id, kd ! counters
5827 real(dp), allocatable :: j_com(:,:,:,:,:) ! covariant and contravariant components of J (dim1,dim2,dim3,3,2)
5828 real(dp), allocatable :: j_mag(:,:,:) ! magnitude of J (dim1,dim2,dim3)
5829 character(len=10) :: base_name ! base name
5830 real(dp), allocatable, save :: j_flux_tor(:,:), j_flux_pol(:,:) ! fluxes
5831 logical :: plot_fluxes_loc ! local plot_fluxes
5832 character(len=max_str_ln) :: plot_name ! name of plot
5833 character(len=max_str_ln) :: plot_titles(2) ! titles of plot
5834#if ldebug
5835 real(dp), allocatable :: j_v_sup_int2(:,:) ! integrated J_V_sup_int
5836#endif
5837
5838 ! initialize ierr
5839 ierr = 0
5840
5841 ! tests
5842 if (eq_style.eq.1 .and. .not.allocated(grid_eq%trigon_factors)) then
5843 ierr = 1
5844 chckerr('trigonometric factors not allocated')
5845 end if
5846
5847 ! set up local plot_fluxes
5848 plot_fluxes_loc = .false.
5849 if (present(plot_fluxes)) plot_fluxes_loc = plot_fluxes
5850
5851 ! set up components and magnitude of J
5852 allocate(j_com(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r,3,2))
5853 allocate(j_mag(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r))
5854 j_com = 0._dp
5855 j_mag = 0._dp
5856#if ldebug
5857 if (debug_j_plot) then
5858 j_com(:,:,:,1,2) = eq_2%g_FD(:,:,:,c([3,3],.true.),0,1,0) - &
5859 &eq_2%g_FD(:,:,:,c([3,3],.true.),0,0,0)*&
5860 &eq_2%jac_FD(:,:,:,0,1,0)/&
5861 &eq_2%jac_FD(:,:,:,0,0,0) - &
5862 &eq_2%g_FD(:,:,:,c([2,3],.true.),0,0,1) + &
5863 &eq_2%g_FD(:,:,:,c([2,3],.true.),0,0,0)*&
5864 &eq_2%jac_FD(:,:,:,0,0,1)/&
5865 &eq_2%jac_FD(:,:,:,0,0,0)
5866 j_com(:,:,:,1,2) = j_com(:,:,:,1,2)/eq_2%jac_FD(:,:,:,0,0,0)**2
5867 j_com(:,:,:,3,2) = eq_2%g_FD(:,:,:,c([2,3],.true.),1,0,0) - &
5868 &eq_2%g_FD(:,:,:,c([2,3],.true.),0,0,0)*&
5869 &eq_2%jac_FD(:,:,:,1,0,0)/&
5870 &eq_2%jac_FD(:,:,:,0,0,0) - &
5871 &eq_2%g_FD(:,:,:,c([1,3],.true.),0,1,0) + &
5872 &eq_2%g_FD(:,:,:,c([1,3],.true.),0,0,0)*&
5873 &eq_2%jac_FD(:,:,:,0,1,0)/&
5874 &eq_2%jac_FD(:,:,:,0,0,0)
5875 j_com(:,:,:,3,2) = j_com(:,:,:,3,2)/eq_2%jac_FD(:,:,:,0,0,0)**2
5876 else
5877#endif
5878 do kd = 1,grid_eq%loc_n_r
5879 j_com(:,:,kd,1,2) = -eq_1%pres_FD(kd,1)
5880 j_com(:,:,kd,3,2) = eq_2%sigma(:,:,kd)/&
5881 &eq_2%jac_FD(:,:,kd,0,0,0) + eq_1%pres_FD(kd,1)*&
5882 &eq_2%g_FD(:,:,kd,c([1,3],.true.),0,0,0) / &
5883 &eq_2%g_FD(:,:,kd,c([3,3],.true.),0,0,0)
5884 end do
5885#if ldebug
5886 end if
5887#endif
5888 do id = 1,3
5889 j_com(:,:,:,id,1) = &
5890 &j_com(:,:,:,1,2)*eq_2%g_FD(:,:,:,c([1,id],.true.),0,0,0) + &
5891 &j_com(:,:,:,3,2)*eq_2%g_FD(:,:,:,c([3,id],.true.),0,0,0)
5892 end do
5893
5894 ! transform back to unnormalized quantity
5895 if (use_normalization) j_com = j_com * pres_0/(r_0*b_0)
5896
5897 ! set plot variables
5898 base_name = 'J'
5899 if (present(rich_lvl)) then
5900 if (rich_lvl.gt.0) base_name = trim(base_name)//'_R_'//&
5901 &trim(i2str(rich_lvl))
5902 end if
5903
5904 ! transform coordinates, including the flux
5905 if (plot_fluxes_loc) then
5906 ierr = calc_vec_comp(grid_eq,grid_eq,eq_1,eq_2,j_com,&
5907 &norm_disc_prec_eq,v_mag=j_mag,base_name=base_name,&
5908 &v_flux_tor=j_flux_tor,v_flux_pol=j_flux_pol,xyz=xyz)
5909 chckerr('')
5910 else
5911 ierr = calc_vec_comp(grid_eq,grid_eq,eq_1,eq_2,j_com,&
5912 &norm_disc_prec_eq,v_mag=j_mag,base_name=base_name,xyz=xyz)
5913 chckerr('')
5914 end if
5915
5916#if ldebug
5917 if (eq_style.eq.1) then
5918 ! transform back to unnormalized quantity
5920
5921 ! plot the fluxes from VMEC
5922 plot_name = 'J_V_sup_int'
5923 plot_titles = ['J^theta_V','J^phi_V ']
5924 call print_ex_2d(plot_titles,plot_name,j_v_sup_int,&
5925 &x=reshape(grid_eq%r_F*2*pi/max_flux_f,&
5926 &[size(grid_eq%r_F),1]),draw=.false.)
5927 call draw_ex(plot_titles,plot_name,2,1,.false.)
5928
5929 ! integrate
5930 allocate(j_v_sup_int2(size(j_v_sup_int,1),2))
5931 do id = 1,2
5932 ierr = calc_int(j_v_sup_int(:,id),grid_eq%r_E,&
5933 &j_v_sup_int2(:,id))
5934 chckerr('')
5935 end do
5936 plot_name = 'J_V_sup_int2'
5937 plot_titles = ['J^theta_V','J^phi_V ']
5938 call print_ex_2d(plot_titles,plot_name,j_v_sup_int2,&
5939 &x=reshape(grid_eq%r_F*2*pi/max_flux_f,&
5940 &[size(grid_eq%r_F),1]),draw=.false.)
5941 call draw_ex(plot_titles,plot_name,2,1,.false.)
5942 end if
5943#endif
5944 end function j_plot
5945
5946 !> Plots the curvature.
5947 !!
5948 !! If multiple equilibrium parallel jobs, every job does its piece, and the
5949 !! results are joined automatically by plot_HDF5.
5950 !!
5951 !! The outputs are given in contra- and covariant components and magnitude
5952 !! in multiple coordinate systems, as indicated in calc_vec_comp().
5953 !!
5954 !! The starting point is the curvature, given by
5955 !! \f[\vec{\kappa} =
5956 !! \kappa_n \frac{\nabla \psi}{ \left|\nabla \psi\right|^2 } +
5957 !! \kappa_g \frac{\nabla \psi \times \vec{B}}{B^2} , \f]
5958 !! which can be used to find the covariant and contravariant components in
5959 !! Flux coordinates.
5960 !!
5961 !! These are then transformed to Cartesian coordinates and plotted.
5962 !!
5963 !! \note
5964 !! -# Vector plots for different Richardson levels can be combined to show
5965 !! the total grid by just plotting them all individually.
5966 !! -# The metric factors and transformation matrices have to be allocated.
5967 !!
5968 !! \return ierr
5969 integer function kappa_plot(grid_eq,eq_1,eq_2,rich_lvl,XYZ) &
5970 &result(ierr)
5971
5972 use grid_utilities, only: calc_vec_comp
5973 use eq_vars, only: eq_1_type, eq_2_type
5974 use eq_utilities, only: calc_inv_met
5976 use num_utilities, only: c
5977 use eq_vars, only: r_0
5979#if ldebug
5981 use input_utilities, only: get_log
5983#endif
5984
5985 character(*), parameter :: rout_name = 'kappa_plot'
5986
5987 ! input / output
5988 type(grid_type), intent(inout) :: grid_eq !< equilibrium grid
5989 type(eq_1_type), intent(in) :: eq_1 !< metric equilibrium variables
5990 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium variables
5991 integer, intent(in), optional :: rich_lvl !< Richardson level
5992 real(dp), intent(in), optional :: xyz(:,:,:,:) !< X, Y and Z of grid
5993
5994 ! local variables
5995 real(dp), allocatable :: k_com(:,:,:,:,:) ! covariant and contravariant components of kappa (dim1,dim2,dim3,3,2)
5996 real(dp), allocatable :: k_mag(:,:,:) ! magnitude of kappa (dim1,dim2,dim3)
5997 character(len=15) :: base_name ! base name
5998#if ldebug
5999 type(grid_type) :: grid_trim ! trimmed equilibrium grid
6000 integer :: id ! counter
6001 integer :: norm_id(2) ! untrimmed normal indices for trimmed grids
6002 integer :: plot_dim(4) ! dimensions of plot
6003 integer :: plot_offset(4) ! local offset of plot
6004 real(dp), allocatable :: xyz_loc(:,:,:,:,:) ! X, Y and Z of surface in cylindrical coordinates, trimmed grid
6005 real(dp), allocatable :: k_com_inv(:,:,:,:) ! inverted cartesian components of curvature
6006 logical, save :: asked_for_testing = .false. ! whether we have been asked to test
6007 logical, save :: testing = .false. ! whether we are testing
6008#endif
6009
6010 ! initialize ierr
6011 ierr = 0
6012
6013 ! tests
6014 if (eq_style.eq.1 .and. .not.allocated(grid_eq%trigon_factors)) then
6015 ierr = 1
6016 chckerr('trigonometric factors not allocated')
6017 end if
6018
6019 ! set up components and magnitude of kappa
6020 allocate(k_com(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r,3,2))
6021 allocate(k_mag(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r))
6022 k_com = 0._dp
6023 k_mag = 0._dp
6024
6025 ! covariant components:
6026 ! kappa . e_alpha = kappa_g
6027 ! kappa . e_psi = kappa_n - kappa_g h12/h22
6028 ! kappa . e_theta = 0
6029 ! and similar for Q
6030 k_com(:,:,:,1,1) = eq_2%kappa_g
6031 k_com(:,:,:,2,1) = eq_2%kappa_n - &
6032 &eq_2%kappa_g * &
6033 &eq_2%h_FD(:,:,:,c([1,2],.true.),0,0,0)/&
6034 &eq_2%h_FD(:,:,:,c([2,2],.true.),0,0,0)
6035 k_com(:,:,:,3,1) = 0._dp
6036
6037 ! contravariant components:
6038 ! kappa . nabla alpha = kappa_n h12 + kappa_g g33/(J^2 h22)
6039 ! kappa . nabla psi = kappa_n h22
6040 ! kappa . nabla theta = kappa_n h23 - kappa_g g13/(J^2 h22)
6041 ! and similar for Q
6042 k_com(:,:,:,1,2) = eq_2%kappa_n * &
6043 &eq_2%h_FD(:,:,:,c([1,2],.true.),0,0,0) + &
6044 &eq_2%kappa_g * &
6045 &eq_2%g_FD(:,:,:,c([3,3],.true.),0,0,0) / &
6046 &(eq_2%jac_FD(:,:,:,0,0,0)**2*&
6047 &eq_2%h_FD(:,:,:,c([2,2],.true.),0,0,0))
6048 k_com(:,:,:,2,2) = eq_2%kappa_n * &
6049 &eq_2%h_FD(:,:,:,c([2,2],.true.),0,0,0)
6050 k_com(:,:,:,3,2) = eq_2%kappa_n * &
6051 &eq_2%h_FD(:,:,:,c([3,2],.true.),0,0,0) - &
6052 &eq_2%kappa_g * &
6053 &eq_2%g_FD(:,:,:,c([3,1],.true.),0,0,0) / &
6054 &(eq_2%jac_FD(:,:,:,0,0,0)**2*&
6055 &eq_2%h_FD(:,:,:,c([2,2],.true.),0,0,0))
6056
6057 ! transform back to unnormalized quantity
6058 if (use_normalization) k_com = k_com / r_0
6059
6060 ! set plot variables
6061 base_name = 'kappa'
6062 if (present(rich_lvl)) then
6063 if (rich_lvl.gt.0) base_name = trim(base_name)//'_R_'//&
6064 &trim(i2str(rich_lvl))
6065 end if
6066
6067 ! transform coordinates
6068 ierr = calc_vec_comp(grid_eq,grid_eq,eq_1,eq_2,k_com,&
6069 &norm_disc_prec_eq,v_mag=k_mag,base_name=base_name,xyz=xyz)
6070 chckerr('')
6071
6072#if ldebug
6073 if (ltest) then
6074 if (.not.asked_for_testing) then
6075 call writo('Do you want to plot the inversion in kappa?')
6076 call lvl_ud(1)
6077 testing = get_log(.false.)
6078 asked_for_testing = .true.
6079 call lvl_ud(-1)
6080 end if
6081 if (testing) then
6082 call writo('Every point in the grid is transformed by &
6083 &displacing it in the direction of the curvature,')
6084 call writo('by a distance equal to the inverse of the &
6085 &curvature. I.e. the point is displaced to the center &
6086 &of curvature.')
6087 call lvl_ud(1)
6088
6089 ! trim equilibrium grid
6090 ierr = trim_grid(grid_eq,grid_trim,norm_id)
6091 chckerr('')
6092
6093 ! set up plot dimensions and local dimensions
6094 plot_dim = [grid_trim%n,3]
6095 plot_offset = [0,0,grid_trim%i_min-1,0]
6096
6097 ! possibly modify if multiple equilibrium parallel jobs
6098 if (size(eq_jobs_lims,2).gt.1) then
6099 plot_dim(1) = eq_jobs_lims(2,size(eq_jobs_lims,2)) - &
6100 &eq_jobs_lims(1,1) + 1
6101 plot_offset(1) = eq_jobs_lims(1,eq_job_nr) - 1
6102 end if
6103
6104 ! set up inverted cartesian components
6105 allocate(k_com_inv(grid_trim%n(1),grid_trim%n(2),&
6106 &grid_trim%loc_n_r,3))
6107 do id = 1,3
6108 k_com_inv(:,:,:,id) = &
6109 &k_com(:,:,norm_id(1):norm_id(2),id,1)/&
6110 &(k_mag(:,:,norm_id(1):norm_id(2))**2)
6111 end do
6112
6113 ! if VMEC, calculate trigonometric factors of trimmed grid
6114 if (eq_style.eq.1) then
6115 ierr = calc_trigon_factors(grid_trim%theta_E,&
6116 &grid_trim%zeta_E,grid_trim%trigon_factors)
6117 chckerr('')
6118 end if
6119
6120 ! calculate X, Y and Z
6121 allocate(xyz_loc(grid_trim%n(1),grid_trim%n(2),&
6122 &grid_trim%loc_n_r,3,3))
6123 ierr = calc_xyz_grid(grid_eq,grid_trim,xyz_loc(:,:,:,1,1),&
6124 &xyz_loc(:,:,:,1,2),xyz_loc(:,:,:,1,3))
6125 chckerr('')
6126
6127 ! produce a plot of center of curvature for every point
6128 call plot_hdf5(['cen_of_curv'],'TEST_cen_of_curv_vec',&
6129 &k_com_inv,tot_dim=plot_dim,loc_offset=plot_offset,&
6130 &x=xyz_loc(:,:,:,:,1),y=xyz_loc(:,:,:,:,2),&
6131 &z=xyz_loc(:,:,:,:,3),col=4,cont_plot=eq_job_nr.gt.1,&
6132 &descr='center of curvature')
6133
6134 ! displace the points to the center of curvature
6135 do id = 1,3
6136 xyz_loc(:,:,:,1,id) = xyz_loc(:,:,:,1,id) + &
6137 &k_com_inv(:,:,:,id)
6138 end do
6139 xyz_loc(:,:,:,2,:) = xyz_loc(:,:,:,1,:)
6140 xyz_loc(:,:,:,3,:) = xyz_loc(:,:,:,3,:)
6141
6142 ! produce an inverted plot
6143 call plot_hdf5(['cen_of_curv_inv'],'TEST_cen_of_curv_inv_vec',&
6144 &-k_com_inv,tot_dim=plot_dim,loc_offset=plot_offset,&
6145 &x=xyz_loc(:,:,:,:,1),y=xyz_loc(:,:,:,:,2),&
6146 &z=xyz_loc(:,:,:,:,3),col=4,cont_plot=eq_job_nr.gt.1,&
6147 &descr='center of curvature')
6148
6149 ! clean up
6150 call grid_trim%dealloc()
6151
6152 call lvl_ud(-1)
6153 end if
6154 end if
6155#endif
6156 end function kappa_plot
6157
6158 !> Plots \b HALF of the change in the position vectors for 2 different
6159 !! toroidal positions, which can correspond to a ripple.
6160 !!
6161 !! Also calculates \b HALF of the relative magnetic perturbation, which also
6162 !! corresponds to a ripple.
6163 !!
6164 !! Finally, if the output grid contains a fundamental interval \f$2\pi\f$,
6165 !! the proportionality between both is written to a file.
6166 !!
6167 !! \note The metric factors and transformation matrices have to be
6168 !! allocated.
6169 !!
6170 !! \return ierr
6171 integer function delta_r_plot(grid_eq,eq_1,eq_2,XYZ,rich_lvl) &
6172 &result(ierr)
6173
6176 use eq_utilities, only: calc_inv_met
6179 &tol_zero
6180 use eq_vars, only: b_0
6182 use mpi_utilities, only: get_ser_var
6183
6184 character(*), parameter :: rout_name = 'delta_r_plot'
6185
6186 ! input / output
6187 type(grid_type), intent(inout) :: grid_eq !< equilibrium grid
6188 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium variables
6189 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium variables
6190 real(dp), intent(in) :: xyz(:,:,:,:) !< X, Y and Z of grid
6191 integer, intent(in), optional :: rich_lvl !< Richardson level
6192
6193 ! local variables
6194 integer :: id, kd ! counters
6195 integer :: norm_id(2) ! untrimmed normal indices for trimmed grids
6196 integer :: r_lim_2d(2) ! limits of r to plot in 2-D
6197 integer :: max_m ! maximum mode number
6198 real(dp), allocatable :: xyz_loc(:,:,:,:) ! X, Y and Z of surface in trimmed grid
6199 real(dp), allocatable :: b_com(:,:,:,:,:) ! covariant and contravariant components of B (dim1,dim2,dim3,3,2)
6200 real(dp), allocatable :: delta_b_tor(:,:,:) ! delta_B/B
6201 real(dp), allocatable :: prop_b_tor_tot(:,:) ! delta_r / delta_B/B in total grid
6202 real(dp), allocatable :: prop_b_tor(:,:,:) ! delta_r / delta_B/B
6203 real(dp), allocatable :: var_tot_loc(:) ! auxilliary variable
6204 real(dp), allocatable :: x_2d(:,:) ! x for 2-D plotting
6205 real(dp), allocatable :: delta_r(:,:,:) ! normal displacement
6206 real(dp), allocatable :: theta_geo(:,:,:) ! geometrical poloidal angle for proportionality factor output
6207 real(dp), allocatable :: r_geo(:,:,:) ! geometrical radius for proportionality factor output
6208 real(dp), allocatable :: prop_b_tor_plot(:,:) ! prop_B_tor and angle at last normal position for plotting
6209 real(dp), allocatable :: delta_r_f(:,:) ! Fourier components of delta_r at last normal position
6210 logical :: new_file_found ! name for new file found
6211 character(len=25) :: base_name ! base name
6212 character(len=25) :: plot_name ! plot name
6213 character(len=max_str_ln) :: prop_b_tor_file_name ! name of B_tor proportionality file
6214 type(grid_type) :: grid_trim ! trimmed equilibrium grid
6215
6216 ! initialize ierr
6217 ierr = 0
6218
6219 ! tests
6220 if (eq_style.eq.1 .and. .not.allocated(grid_eq%trigon_factors)) then
6221 ierr = 1
6222 chckerr('trigonometric factors not allocated')
6223 end if
6224
6225 call writo('Calculate normal displacement')
6226 call lvl_ud(1)
6227
6228 ! trim grid
6229 ierr = trim_grid(grid_eq,grid_trim,norm_id)
6230 chckerr('')
6231
6232 ! calculate local X, Y and Z
6233 ! (Can be different from provided one for different plot grid styles)
6234 allocate(xyz_loc(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r,4))
6235 ierr = calc_xyz_grid(grid_eq,grid_eq,xyz_loc(:,:,:,1),xyz_loc(:,:,:,2),&
6236 &xyz_loc(:,:,:,3),r=xyz_loc(:,:,:,4))
6237 chckerr('')
6238
6239 ! calculate geometrical poloidal angle and radius
6240 allocate(theta_geo(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r))
6241 allocate(r_geo(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r))
6242 theta_geo = atan2(xyz_loc(:,:,:,3)-rz_0(2),&
6243 &xyz_loc(:,:,:,4)-rz_0(1))
6244 where (theta_geo.lt.0._dp) theta_geo = theta_geo + 2*pi
6245 r_geo = sqrt((xyz_loc(:,:,:,3)-rz_0(2))**2 + &
6246 &(xyz_loc(:,:,:,4)-rz_0(1))**2)
6247
6248 ! plot
6249 call plot_hdf5('theta_geo','theta_geo',&
6250 &theta_geo(:,:,norm_id(1):norm_id(2)),&
6251 &tot_dim=[grid_trim%n(1),grid_trim%n(2),grid_trim%n(3)],&
6252 &loc_offset=[0,0,grid_trim%i_min-1],&
6253 &x=xyz(:,:,norm_id(1):norm_id(2),1),&
6254 &y=xyz(:,:,norm_id(1):norm_id(2),2),&
6255 &z=xyz(:,:,norm_id(1):norm_id(2),3),cont_plot=eq_job_nr.gt.1,&
6256 &descr='geometrical poloidal angle')
6257 call plot_hdf5('r_geo','r_geo',&
6258 &r_geo(:,:,norm_id(1):norm_id(2)),&
6259 &tot_dim=[grid_trim%n(1),grid_trim%n(2),grid_trim%n(3)],&
6260 &loc_offset=[0,0,grid_trim%i_min-1],&
6261 &x=xyz(:,:,norm_id(1):norm_id(2),1),&
6262 &y=xyz(:,:,norm_id(1):norm_id(2),2),&
6263 &z=xyz(:,:,norm_id(1):norm_id(2),3),cont_plot=eq_job_nr.gt.1,&
6264 &descr='geometrical radius')
6265
6266 ! calculate perturbation delta_r on radius
6267 ierr = calc_tor_diff(r_geo,theta_geo,norm_disc_prec_eq,&
6268 &absolute=.true.,r=grid_eq%loc_r_F)
6269 chckerr('')
6270 allocate(delta_r(grid_eq%n(1),1,grid_eq%loc_n_r))
6271 delta_r(:,1,:) = r_geo(:,2,:)*0.5_dp ! take factor half as this is absolute
6272 deallocate(r_geo)
6273
6274 ! set plot variables for delta_r
6275 base_name = 'delta_r'
6276 plot_name = 'delta_r'
6277 if (present(rich_lvl)) then
6278 if (rich_lvl.gt.0) base_name = trim(base_name)//'_R_'//&
6279 &trim(i2str(rich_lvl))
6280 end if
6281
6282 ! plot
6283 call plot_hdf5(trim(plot_name),trim(base_name),&
6284 &delta_r(:,:,norm_id(1):norm_id(2)),&
6285 &tot_dim=[grid_trim%n(1),1,grid_trim%n(3)],&
6286 &loc_offset=[0,0,grid_trim%i_min-1],&
6287 &x=xyz(:,2:2,norm_id(1):norm_id(2),1),&
6288 &y=xyz(:,2:2,norm_id(1):norm_id(2),2),&
6289 &z=xyz(:,2:2,norm_id(1):norm_id(2),3),&
6290 &cont_plot=eq_job_nr.gt.1,&
6291 &descr='plasma position displacement')
6292
6293 ! calculate NUFFT for last point
6294 if (rank.eq.n_procs-1) then
6295 ! plot delta_r at last normal position
6296 call print_ex_2d(trim(plot_name),trim(base_name),&
6297 &delta_r(1:grid_eq%n(1)-1,1,grid_eq%loc_n_r),&
6298 &x=theta_geo(1:grid_eq%n(1)-1,2,grid_eq%loc_n_r),&
6299 &draw=.false.)
6300 call draw_ex([trim(plot_name)],trim(base_name),1,1,.false.)
6301
6302 ! calculate and plot Fourier modes
6303 ierr = nufft(theta_geo(1:grid_eq%n(1)-1,2,grid_eq%loc_n_r),&
6304 &delta_r(1:grid_eq%n(1)-1,1,grid_eq%loc_n_r),delta_r_f)
6305 chckerr('')
6306 base_name = trim(base_name)//'_F'
6307 call print_ex_2d(['delta_r cos','delta_r sin'],trim(base_name),&
6308 &delta_r_f,draw=.false.)
6309 call draw_ex(['delta_r cos','delta_r sin'],trim(base_name),2,1,&
6310 &.false.)
6311
6312 ! mode outputs, (p)recycle "prop_B_tor_file_*"
6313 new_file_found = .false.
6314 prop_b_tor_file_name = base_name
6315 kd = 1
6316 do while (.not.new_file_found)
6317 open(prop_b_tor_i,file=trim(prop_b_tor_file_name)//'_'//&
6318 &trim(i2str(kd))//'.dat',iostat=ierr,status='old')
6319 if (ierr.eq.0) then
6320 kd = kd + 1
6321 else
6322 prop_b_tor_file_name = trim(prop_b_tor_file_name)//&
6323 &'_'//trim(i2str(kd))//'.dat'
6324 new_file_found = .true.
6325 open(prop_b_tor_i,file=trim(prop_b_tor_file_name),&
6326 &iostat=ierr,status='new')
6327 chckerr('Failed to open file')
6328 end if
6329 end do
6330
6331 ! write to output file
6332 max_m = 20
6333 write(prop_b_tor_i,'("# ",2(A5," "),2(A23," "))') &
6334 &'N', 'M', 'delta_c', 'delta_s'
6335 do id = 1,min(size(delta_r_f,1),max_m+1)
6336 write(prop_b_tor_i,'(" ",2(I5," "),2(ES23.16," "))') &
6337 &18, id-1, delta_r_f(id,:)
6338 end do
6339
6340 ! close
6341 close(prop_b_tor_i)
6342 end if
6343
6344 call lvl_ud(-1)
6345
6346 call writo('Calculate magnetic field ripple')
6347 call lvl_ud(1)
6348
6349 ! Calculate cylindrical components of B
6350 allocate(b_com(grid_eq%n(1),grid_eq%n(2),grid_eq%loc_n_r,3,2))
6351 b_com = 0._dp
6352 do id = 1,3
6353 b_com(:,:,:,id,1) = eq_2%g_FD(:,:,:,c([3,id],.true.),0,0,0)/&
6354 &eq_2%jac_FD(:,:,:,0,0,0)
6355 end do
6356 b_com(:,:,:,3,2) = 1._dp/eq_2%jac_FD(:,:,:,0,0,0)
6357 if (use_normalization) b_com = b_com * b_0
6358 ierr = calc_vec_comp(grid_eq,grid_eq,eq_1,eq_2,b_com,norm_disc_prec_eq,&
6359 &max_transf=4)
6360 chckerr('')
6361
6362 ! calculate perturbation
6363 ierr = calc_tor_diff(b_com,theta_geo,norm_disc_prec_eq,&
6364 &r=grid_eq%loc_r_F)
6365 chckerr('')
6366 allocate(delta_b_tor(grid_eq%n(1),1,grid_eq%loc_n_r))
6367 delta_b_tor(:,1,:) = 0.5_dp*sum(b_com(:,2,:,2,:),3)
6368 deallocate(b_com)
6369
6370 ! set plot variables for delta_B_tor
6371 base_name = 'delta_B_tor'
6372 plot_name = 'delta_B_tor'
6373 if (present(rich_lvl)) then
6374 if (rich_lvl.gt.0) base_name = trim(base_name)//'_R_'//&
6375 &trim(i2str(rich_lvl))
6376 end if
6377
6378 ! plot with HDF5
6379 call plot_hdf5(trim(plot_name),trim(base_name),&
6380 &delta_b_tor(:,:,norm_id(1):norm_id(2)),&
6381 &tot_dim=[grid_trim%n(1),1,grid_trim%n(3)],&
6382 &loc_offset=[0,0,grid_trim%i_min-1],&
6383 &x=xyz(:,2:2,norm_id(1):norm_id(2),1),&
6384 &y=xyz(:,2:2,norm_id(1):norm_id(2),2),&
6385 &z=xyz(:,2:2,norm_id(1):norm_id(2),3),cont_plot=eq_job_nr.gt.1,&
6386 &descr='plasma position displacement')
6387
6388 call lvl_ud(-1)
6389
6390 call writo('Calculate proportionality factor')
6391 call lvl_ud(1)
6392
6393 allocate(prop_b_tor(grid_eq%n(1),1,grid_eq%loc_n_r))
6394 prop_b_tor = 0.0_dp
6395 where (abs(delta_b_tor).gt.tol_zero) prop_b_tor = delta_r / delta_b_tor
6396
6397 ! set plot variables for prop_B_tor
6398 base_name = 'prop_B_tor'
6399 plot_name = trim(base_name)
6400 if (present(rich_lvl)) then
6401 if (rich_lvl.gt.0) base_name = trim(base_name)//'_R_'//&
6402 &trim(i2str(rich_lvl))
6403 end if
6404
6405 ! plot with HDF5
6406 call plot_hdf5(plot_name,trim(base_name),&
6407 &prop_b_tor(:,:,norm_id(1):norm_id(2)),&
6408 &tot_dim=[grid_trim%n(1),1,grid_trim%n(3)],&
6409 &loc_offset=[0,0,grid_trim%i_min-1],&
6410 &x=xyz(:,2:2,norm_id(1):norm_id(2),1),&
6411 &y=xyz(:,2:2,norm_id(1):norm_id(2),2),&
6412 &z=xyz(:,2:2,norm_id(1):norm_id(2),3),&
6413 &cont_plot=eq_job_nr.gt.1,&
6414 &descr='delta_r divided by delta_B_tor')
6415
6416 ! plot in 2-D
6417 r_lim_2d = [1,grid_trim%n(3)]
6418 if (ex_plot_style.eq.2) r_lim_2d(1) = &
6419 &max(r_lim_2d(1),r_lim_2d(2)-127+1) ! Bokeh can only handle 255/2 input arguments
6420 if (rank.eq.0) then
6421 allocate(x_2d(grid_trim%n(1),grid_trim%n(3)))
6422 end if
6423 allocate(prop_b_tor_tot(grid_trim%n(1),grid_trim%n(3)))
6424 do id = 1,grid_trim%n(1)
6425 ! prop_B_tor (all procs)
6426 ierr = get_ser_var(prop_b_tor(id,1,norm_id(1):norm_id(2)),&
6427 &var_tot_loc,scatter=.true.)
6428 chckerr('')
6429 prop_b_tor_tot(id,:) = max(min(var_tot_loc,2._dp),-2._dp) ! limit to avoid division by small delta_B_tor values
6430 deallocate(var_tot_loc)
6431
6432 ! theta_geo (only master)
6433 ierr = get_ser_var(theta_geo(id,2,norm_id(1):norm_id(2)),&
6434 &var_tot_loc)
6435 chckerr('')
6436 if (rank.eq.0) x_2d(id,:) = var_tot_loc/pi
6437 deallocate(var_tot_loc)
6438 end do
6439 if (rank.eq.0) then
6440 call print_ex_2d([trim(plot_name)],trim(base_name),&
6441 &prop_b_tor_tot(:,r_lim_2d(1):r_lim_2d(2)),&
6442 &x=x_2d(:,r_lim_2d(1):r_lim_2d(2)),draw=.false.)
6443 call draw_ex([trim(plot_name)],trim(base_name),&
6444 &r_lim_2d(2)-r_lim_2d(1)+1,1,.false.)
6445 end if
6446
6447 call lvl_ud(-1)
6448
6449 call writo('Output to file as function of poloidal flux angle')
6450 call lvl_ud(1)
6451
6452 ! only last rank outputs to file (it has the last normal position)
6453 if (rank.eq.n_procs-1) then
6454 ! find new file name
6455 new_file_found = .false.
6456 prop_b_tor_file_name = 'prop_B_tor'
6457 kd = 1
6458 do while (.not.new_file_found)
6459 open(prop_b_tor_i,file=trim(prop_b_tor_file_name)//'_'//&
6460 &trim(i2str(kd))//'.dat',iostat=ierr,status='old')
6461 if (ierr.eq.0) then
6462 kd = kd + 1
6463 else
6464 prop_b_tor_file_name = trim(prop_b_tor_file_name)//&
6465 &'_'//trim(i2str(kd))//'.dat'
6466 new_file_found = .true.
6467 open(prop_b_tor_i,file=trim(prop_b_tor_file_name),&
6468 &iostat=ierr,status='new')
6469 chckerr('Failed to open file')
6470 end if
6471 end do
6472
6473 ! user output
6474 call writo('Save toroidal field proportionality factor in &
6475 &file "'//trim(prop_b_tor_file_name)//'"',persistent=.true.)
6476
6477 ! order output, taking away last point as it is equivalent to
6478 ! the first
6479 ierr = order_per_fun(reshape([&
6480 &theta_geo(1:grid_trim%n(1)-1,1,grid_trim%loc_n_r),&
6481 &prop_b_tor_tot(1:grid_trim%n(1)-1,grid_trim%n(3))],&
6482 &[grid_trim%n(1)-1,2]),prop_b_tor_plot,0)
6483 chckerr('')
6484
6485 ! write to output file
6486 write(prop_b_tor_i,'("# ",2(A23," "))') &
6487 &'pol. angle [ ]', 'prop. factor [ ]'
6488 do id = 1,grid_trim%n(1)-1
6489 write(prop_b_tor_i,'(" ",2(ES23.16," "))') &
6490 &prop_b_tor_plot(id,:)
6491 end do
6492
6493 ! close
6494 close(prop_b_tor_i)
6495 end if
6496
6497 call lvl_ud(-1)
6498
6499 ! clean up
6500 call grid_trim%dealloc()
6501 end function delta_r_plot
6502
6503 !> Divides the equilibrium jobs.
6504 !!
6505 !! For PB3D, the entire parallel range has to be calculated, but due to
6506 !! memory limits this has to be split up in pieces. Every piece has to be
6507 !! able to contain the equilibrium variables (see note below), as well as
6508 !! the vectorial perturbation variables. These are later combined into
6509 !! tensorial variables and integrated.
6510 !!
6511 !! The equilibrium variables have to be operated on to calculate them,
6512 !! which translates to a scale factor \c mem_scale_fac. However, in the
6513 !! perturbation phase, when they are just used, this scale factor is not
6514 !! needed.
6515 !!
6516 !! In its most extreme form, the division in equilibrium jobs would be the
6517 !! individual calculation on a fundamental integration integral of the
6518 !! parallel points:
6519 !! - for \c magn_int_style=1 (trapezoidal), this is 1 point,
6520 !! - for \c magn_int_style=2 (Simpson 3/8), this is 3 points.
6521 !!
6522 !! For HELENA, the parallel derivatives are calculated discretely, the
6523 !! equilibrium and vectorial perturbation variables are tabulated first in
6524 !! this HELENA grid. This happens in the first Richardson level. In all
6525 !! Richardson levels, afterwards, these variables are interpolated in the
6526 !! angular directions. In this case, therefore, there can be no division of
6527 !! this HELENA output interval for the first Richardson level.
6528 !!
6529 !! This procedure does the job of dividing the grids setting the global
6530 !! variables \c eq_jobs_lims.
6531 !!
6532 !! The integration of the tensorial perturbation variables is adjusted:
6533 !! - If the first job of the parallel jobs and not the first Richardson
6534 !! level: add half of the integrated tensorial perturbation quantities of
6535 !! the previous level.
6536 !! - If not the first job of the parallel jobs, add the integrated
6537 !! tensorial perturbation quantities to those of the previous parallel job,
6538 !! same Richardson level.
6539 !!
6540 !! In fact, the equilibrium jobs have much in common with the Richardson
6541 !! levels, as is attested by the existence of the routines do_eq() and
6542 !! eq_info(), which are equivalent to do_rich() and rich_info().
6543 !!
6544 !! In POST, finally, the situation is slightly different for HELENA, as
6545 !! all the requested variables have to fit, including the interpolated
6546 !! variables, as they are stored whereas in PB3D they are not. The parallel
6547 !! range to be taken is then the one of the output grid, including a base
6548 !! range for the variables tabulated on the HELENA grid. Also, for extended
6549 !! output grids, the size of the grid in the secondary angle has to be
6550 !! included in \c n_par_X (i.e. toroidal when poloidal flux is used and vice
6551 !! versa). Furthermore, multiple equilibrium jobs are allowed.
6552 !!
6553 !! To this end, optionally, a base number can be provided for \c n_par_X,
6554 !! that is always added to the number of points in the divided \c n_par_X.
6555 !!
6556 !! \note For PB3D, only the variables \c g_FD, \c h_FD and \c jac_FD are
6557 !! counted, as the equilibrium variables and the transformation matrices are
6558 !! deleted after use. Also, \c S, \c sigma, \c kappa_n and \c kappa_g can be
6559 !! neglected as they do not contain derivatives and are therefore much
6560 !! smaller. in both routines calc_memory_eq() and calc_memory_x(), however,
6561 !! a 50% safety factor is used to account for this somewhat.
6562 !!
6563 !! \return ierr
6564 integer function divide_eq_jobs(n_par_X,arr_size,n_div,n_div_max,&
6565 &n_par_X_base,range_name) result(ierr)
6566
6569 use rich_vars, only: rich_lvl
6570 use eq_utilities, only: calc_memory_eq
6571 use x_utilities, only: calc_memory_x
6572 use x_vars, only: n_mod_x
6573
6574 character(*), parameter :: rout_name = 'divide_eq_jobs'
6575
6576 ! input / output
6577 integer, intent(in) :: n_par_x !< number of parallel points to be divided
6578 integer, intent(in) :: arr_size(2) !< array size (using loc_n_r) for eq_2 and X_1 variables
6579 integer, intent(inout) :: n_div !< final number of divisions
6580 integer, intent(in), optional :: n_div_max !< maximum n_div
6581 integer, intent(in), optional :: n_par_x_base !< base n_par_X, undivisible
6582 character(len=*), intent(in), optional :: range_name !< name of range
6583
6584 ! local variables
6585 real(dp) :: mem_size(2) ! approximation of memory required for eq_2 and X_1 variables
6586 integer :: n_par_x_base_loc ! local n_par_X_base
6587 integer :: max_mem_req ! maximum memory required
6588 integer :: n_div_max_loc ! maximum n_div
6589 integer :: n_par_range ! nr. of points in range
6590 character(len=max_str_ln) :: range_message ! message about how many ranges
6591 character(len=max_str_ln) :: err_msg ! error message
6592 character(len=max_str_ln) :: range_name_loc ! local range name
6593
6594 ! initialize ierr
6595 ierr = 0
6596
6597 ! user output
6598 call writo('Dividing the equilibrium jobs')
6599 call lvl_ud(1)
6600
6601 ! set up width of fundamental interval
6602 select case (magn_int_style)
6603 case (1) ! Trapezoidal rule
6604 fund_n_par = 1
6605 case (2) ! Simpson's 3/8 rule
6606 fund_n_par = 3
6607 end select
6608
6609 ! set up local n_par_X_base
6610 n_par_x_base_loc = 0
6611 if (present(n_par_x_base)) n_par_x_base_loc = n_par_x_base
6612
6613 ! set up local range name
6614 range_name_loc = 'parallel points'
6615 if (present(range_name)) range_name_loc = trim(range_name)
6616
6617 ! setup auxilliary variables
6618 n_div = 0
6619 mem_size = huge(1._dp)*0.49_dp
6620 if (rich_lvl.eq.1) then
6621 n_div_max_loc = (n_par_x-1)/fund_n_par
6622 else
6623 n_div_max_loc = n_par_x/fund_n_par
6624 end if
6625 if (present(n_div_max)) n_div_max_loc = n_div_max
6626 n_div_max_loc = max(1,n_div_max_loc) ! cannot have less than 1 piece
6627
6628 ! calculate largest possible range of parallel points fitting in memory
6629 do while (max_tot_mem.lt.sum(mem_size)) ! combined mem_size has to be smaller than total memory
6630 n_div = n_div + 1
6631 n_par_range = ceiling(n_par_x*1._dp/n_div + n_par_x_base_loc)
6632 ierr = calc_memory_eq(arr_size(1),n_par_range,mem_size(1))
6633 chckerr('')
6634 mem_size(1) = mem_size(1)*mem_scale_fac ! operations have to be done on eq, it is not just stored.
6635 ierr = calc_memory_x(1,arr_size(2)*n_par_range,n_mod_x,mem_size(2)) ! all modes have to be stored
6636 chckerr('')
6637 if (n_div.gt.n_div_max_loc) then ! still not enough memory
6638 ierr = 1
6639 err_msg = 'The memory limit is too low, need more than '//&
6640 &trim(i2str(max_mem_req))//'MB'
6641 chckerr(err_msg)
6642 end if
6643 max_mem_req = ceiling(sum(mem_size))
6644 end do
6645 if (n_div.gt.1) then
6646 range_message = 'The '//trim(i2str(n_par_x))//' '//&
6647 &trim(range_name_loc)//' are split into '//&
6648 &trim(i2str(n_div))//' and '//trim(i2str(n_div))//&
6649 &' collective jobs are done serially'
6650 else
6651 range_message = 'The '//trim(i2str(n_par_x))//' '//&
6652 &trim(range_name_loc)//' can be done without splitting them'
6653 end if
6654 call writo(range_message)
6655 call writo('The total memory for all processes together is estimated &
6656 &to be about '//trim(i2str(ceiling(sum(mem_size))))//'MB')
6657 call lvl_ud(1)
6658 call writo('(maximum: '//trim(i2str(ceiling(max_tot_mem)))//'&
6659 &MB, user specified)')
6660 call lvl_ud(-1)
6661
6662 if (prog_style.eq.1) then
6663 ! calculate max memory available for perturbation calculations
6664 mem_size(1) = mem_size(1)/mem_scale_fac ! rescale by mem_scale_fac as eq is now just stored
6665 max_x_mem = max_tot_mem - sum(mem_size)
6666 call writo('In the perturbation phase, the equilibrium variables &
6667 &are not being operated on:')
6668 call lvl_ud(1)
6669 call writo('This translates to a scale factor 1/'//&
6670 &trim(r2strt(mem_scale_fac)))
6671 call writo('Therefore, the memory left for the perturbation phase &
6672 &is '//trim(i2str(ceiling(max_x_mem)))//'MB')
6673 call lvl_ud(-1)
6674 end if
6675
6676 ! user output
6677 call lvl_ud(-1)
6678 call writo('Equilibrium jobs divided')
6679 end function divide_eq_jobs
6680
6681 !> Calculate \c eq_jobs_lims.
6682 !!
6683 !! Take into account that every job has to start and end at the
6684 !! start and end of a fundamental integration interval, as discussed in
6685 !! divide_eq_jobs():
6686 !! - for \c magn_int_style=1 (trapezoidal), this is 1 point,
6687 !! - for \c magn_int_style=2 (Simpson 3/8), this is 3 points.
6688 !!
6689 !! for POST, there are no Richardson levels, and there has to be overlap of
6690 !! one always, in order to have correct composite integrals of the different
6691 !! regions.
6692 !!
6693 !! \note The \c n_par_X passed into this procedure refers to the quantity
6694 !! that is already possibly halved if the Richardson level is higher than 1.
6695 !! This information is then reflected in the eq_jobs_lims, which refer
6696 !! to the local limits, i.e. only the parallel points currently under
6697 !! consideration.
6698 !!
6699 !! \return ierr
6700 integer function calc_eq_jobs_lims(n_par_X,n_div) result(ierr)
6702 use rich_vars, only: rich_lvl
6703
6704 character(*), parameter :: rout_name = 'calc_eq_jobs_lims'
6705
6706 ! input / output
6707 integer, intent(in) :: n_par_x !< number of parallel points in this Richardson level
6708 integer, intent(in) :: n_div !< nr. of divisions of parallel ranges
6709
6710 ! local variables
6711 integer :: id ! counter
6712 integer :: ol_width ! overlap width
6713 integer, allocatable :: n_par(:) ! number of points per range
6714 character(len=max_str_ln) :: err_msg ! error message
6715
6716 ! initialize ierr
6717 ierr = 0
6718
6719 ! Also initialize eq_job_nr to 0 as it is incremented by "do_eq".
6720 eq_job_nr = 0
6721
6722 allocate(n_par(n_div))
6723 n_par = n_par_x/n_div ! number of parallel points on this processor
6724 n_par(1:mod(n_par_x,n_div)) = n_par(1:mod(n_par_x,n_div)) + 1 ! add a point to if there is a remainder
6725 chckerr('')
6726
6727 ! (re)allocate equilibrium jobs limits
6728 if (allocated(eq_jobs_lims)) deallocate(eq_jobs_lims)
6729 allocate(eq_jobs_lims(2,n_div))
6730
6731 ! set overlap width
6732 select case (prog_style)
6733 case (1) ! PB3D
6734 if (rich_lvl.eq.1) then
6735 ol_width = 1
6736 else
6737 ol_width = 0
6738 end if
6739 case (2) ! POST
6740 ol_width = 1
6741 end select
6742
6743 ! loop over divisions
6744 do id = 1,n_div
6745 ! setup first guess, without taking into account fundamental
6746 ! integration intervals
6747 if (id.eq.1) then
6748 eq_jobs_lims(1,id) = 1
6749 else
6750 eq_jobs_lims(1,id) = eq_jobs_lims(2,id-1) + 1 - ol_width
6751 end if
6752 eq_jobs_lims(2,id) = sum(n_par(1:id))
6753
6754 ! take into account fundamental interval
6755 if (n_div.gt.1) then
6756 eq_jobs_lims(2,id) = eq_jobs_lims(1,id) - 1 + ol_width + &
6757 &fund_n_par * max(1,nint(&
6758 &(eq_jobs_lims(2,id)-eq_jobs_lims(1,id)+1._dp-ol_width)/&
6759 &fund_n_par))
6760 end if
6761 end do
6762
6763 ! test whether end coincides with sum of n_par
6764 if (eq_jobs_lims(2,n_div).ne.sum(n_par)) then
6765 ierr = 1
6766 err_msg = 'Limits don''t match, try with more memory or lower &
6767 &magn_int_style'
6768 chckerr(err_msg)
6769 end if
6770 end function calc_eq_jobs_lims
6771
6772#if ldebug
6773 !> See if \c T_EF it complies with the theory of \cite Weyens3D.
6774 !!
6775 !! \note Debug version only
6776 !!
6777 !! \return ierr
6778 integer function test_t_ef(grid_eq,eq_1,eq_2) result(ierr)
6779 use num_vars, only: use_pol_flux_f, eq_style
6780 use grid_utilities, only: trim_grid
6781 use num_utilities, only: c
6782 use output_ops, only: plot_diff_hdf5
6783
6784 character(*), parameter :: rout_name = 'test_T_EF'
6785
6786 ! input / output
6787 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
6788 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium
6789 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium
6790
6791 ! local variables
6792 integer :: id, kd ! counter
6793 integer :: norm_id(2) ! untrimmed normal indices for trimmed grids
6794 real(dp), allocatable :: res(:,:,:,:) ! calculated result
6795 character(len=max_str_ln) :: file_name ! name of plot file
6796 character(len=max_str_ln) :: description ! description of plot
6797 integer :: tot_dim(3), loc_offset(3) ! total dimensions and local offset
6798 type(grid_type) :: grid_trim ! trimmed equilibrium grid
6799
6800 ! initialize ierr
6801 ierr = 0
6802
6803 ! output
6804 call writo('Going to test whether T_EF complies with the theory')
6805 call lvl_ud(1)
6806
6807 ! trim extended grid into plot grid
6808 ierr = trim_grid(grid_eq,grid_trim,norm_id)
6809 chckerr('')
6810
6811 ! set total and local dimensions and local offset
6812 tot_dim = [grid_trim%n(1),grid_trim%n(2),grid_trim%n(3)]
6813 loc_offset = [0,0,grid_trim%i_min-1]
6814
6815 ! set up res
6816 allocate(res(grid_trim%n(1),grid_trim%n(2),grid_trim%loc_n_r,9))
6817 res = 0.0_dp
6818
6819 ! calc res, depending on flux used in F coordinates and on which
6820 ! equilibrium style is being used:
6821 ! 1: VMEC
6822 ! 2: HELENA
6823 select case (eq_style)
6824 case (1) ! VMEC
6825 if (use_pol_flux_f) then ! using poloidal flux
6826 ! calculate T_EF(1,1)
6827 do kd = norm_id(1),norm_id(2)
6828 res(:,:,kd-norm_id(1)+1,1) = &
6829 &grid_eq%theta_F(:,:,kd)*eq_1%q_saf_E(kd,1) + &
6830 &eq_2%L_E(:,:,kd,1,0,0)*eq_1%q_saf_E(kd,0)
6831 end do
6832 ! calculate T_EF(2,1)
6833 do kd = norm_id(1),norm_id(2)
6834 res(:,:,kd-norm_id(1)+1,2) = &
6835 &(1._dp + eq_2%L_E(:,:,kd,0,1,0))*eq_1%q_saf_E(kd,0)
6836 end do
6837 ! calculate T_EF(3,1)
6838 do kd = norm_id(1),norm_id(2)
6839 res(:,:,kd-norm_id(1)+1,3) = -1._dp + &
6840 &eq_2%L_E(:,:,kd,0,0,1)*eq_1%q_saf_E(kd,0)
6841 end do
6842 ! calculate T_EF(1,2)
6843 do kd = norm_id(1),norm_id(2)
6844 res(:,:,kd-norm_id(1)+1,4) = eq_1%flux_p_E(kd,1)/(2*pi)
6845 end do
6846 ! calculate T_EF(1,3)
6847 res(:,:,:,7) = eq_2%L_E(:,:,norm_id(1):norm_id(2),1,0,0)
6848 ! calculate T_EF(2,3)
6849 res(:,:,:,8) = 1._dp + &
6850 &eq_2%L_E(:,:,norm_id(1):norm_id(2),0,1,0)
6851 ! calculate T_EF(3,3)
6852 res(:,:,:,9) = eq_2%L_E(:,:,norm_id(1):norm_id(2),0,0,1)
6853 else ! using toroidal flux
6854 ! calculate T_EF(1,1)
6855 do kd = norm_id(1),norm_id(2)
6856 res(:,:,kd-norm_id(1)+1,1) = - eq_2%L_E(:,:,kd,1,0,0) &
6857 &+ grid_eq%zeta_E(:,:,kd)*eq_1%rot_t_E(kd,1)
6858 end do
6859 ! calculate T_EF(2,1)
6860 res(:,:,:,2) = &
6861 &- (1._dp + eq_2%L_E(:,:,norm_id(1):norm_id(2),0,1,0))
6862 ! calculate T_EF(3,1)
6863 do kd = norm_id(1),norm_id(2)
6864 res(:,:,kd-norm_id(1)+1,3) = &
6865 &eq_1%rot_t_E(kd,0) - eq_2%L_E(:,:,kd,0,0,1)
6866 end do
6867 ! calculate T_EF(1,2)
6868 do kd = norm_id(1),norm_id(2)
6869 res(:,:,kd-norm_id(1)+1,4) = &
6870 &- eq_1%flux_t_E(kd,1)/(2*pi)
6871 end do
6872 ! calculate T_EF(3,3)
6873 res(:,:,:,9) = -1._dp
6874 end if
6875 case (2) ! HELENA
6876 if (use_pol_flux_f) then ! using poloidal flux
6877 ! calculate T_EF(1,1)
6878 do kd = norm_id(1),norm_id(2)
6879 res(:,:,kd-norm_id(1)+1,1) = &
6880 &- eq_1%q_saf_E(kd,1)*grid_eq%theta_E(:,:,kd)
6881 end do
6882 ! calculate T_EF(2,1)
6883 do kd = norm_id(1),norm_id(2)
6884 res(:,:,kd-norm_id(1)+1,2) = - eq_1%q_saf_E(kd,0)
6885 end do
6886 ! calculate T_EF(3,1)
6887 res(:,:,:,3) = 1._dp
6888 ! calculate T_EF(1,2)
6889 do kd = norm_id(1),norm_id(2)
6890 res(:,:,kd-norm_id(1)+1,4) = eq_1%flux_p_E(kd,1)/(2*pi)
6891 end do
6892 ! calculate T_EF(2,3)
6893 res(:,:,:,8) = 1._dp
6894 else ! using toroidal flux
6895 ! calculate T_EF(1,1)
6896 do kd = norm_id(1),norm_id(2)
6897 res(:,:,kd-norm_id(1)+1,1) = &
6898 &eq_1%rot_t_E(kd,1)*grid_eq%zeta_E(:,:,kd)
6899 end do
6900 ! calculate T_EF(2,1)
6901 res(:,:,:,2) = -1._dp
6902 ! calculate T_EF(3,1)
6903 do kd = norm_id(1),norm_id(2)
6904 res(:,:,kd-norm_id(1)+1,3) = eq_1%rot_t_E(kd,0)
6905 end do
6906 ! calculate T_EF(1,2)
6907 do kd = norm_id(1),norm_id(2)
6908 res(:,:,kd-norm_id(1)+1,4) = eq_1%flux_t_E(kd,1)/(2*pi)
6909 end do
6910 ! calculate T_EF(3,3)
6911 res(:,:,:,9) = 1._dp
6912 end if
6913 end select
6914
6915 ! set up plot variables for calculated values
6916 do id = 1,3
6917 do kd = 1,3
6918 ! user output
6919 call writo('Testing T_EF('//trim(i2str(kd))//','//&
6920 &trim(i2str(id))//')')
6921 call lvl_ud(1)
6922
6923 ! set some variables
6924 file_name = 'TEST_T_EF_'//trim(i2str(kd))//'_'//trim(i2str(id))
6925 description = 'Testing calculated with given value for T_EF('//&
6926 &trim(i2str(kd))//','//trim(i2str(id))//')'
6927
6928 ! plot difference
6929 call plot_diff_hdf5(res(:,:,:,c([kd,id],.false.)),&
6930 &eq_2%T_EF(:,:,norm_id(1):norm_id(2),c([kd,id],.false.),&
6931 &0,0,0),file_name,tot_dim,loc_offset,description,&
6932 &output_message=.true.)
6933
6934 call lvl_ud(-1)
6935 end do
6936 end do
6937
6938 ! clean up
6939 call grid_trim%dealloc()
6940
6941 ! user output
6942 call lvl_ud(-1)
6943 call writo('Test complete')
6944 end function test_t_ef
6945
6946 !> Tests whether \f$ \frac{\partial^2}{\partial u_i \partial u_j} h_\text{H}
6947 !! \f$ is calculated correctly.
6948 !!
6949 !! \note Debug version only
6950 !!
6951 !! \return ierr
6952 integer function test_d12h_h(grid_eq,eq) result(ierr)
6953 use grid_utilities, only: trim_grid
6954 use num_utilities, only: c, spline
6955 use num_vars, only: norm_disc_prec_eq
6956 use helena_vars, only: ias
6957
6958 character(*), parameter :: rout_name = 'test_D12h_H'
6959
6960 ! input / output
6961 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
6962 type(eq_2_type), intent(in) :: eq !< metric equilibrium
6963
6964 ! local variables
6965 integer :: norm_id(2) ! untrimmed normal indices for trimmed grids
6966 integer :: id, jd, kd, ld ! counters
6967 integer :: bcs(2,2) ! boundary conditions (theta(even), theta(odd))
6968 integer :: bc_ld(6) ! boundary condition type for each metric element
6969 real(dp) :: bcs_val(2,3) ! values for boundary conditions
6970 real(dp), allocatable :: res(:,:,:,:) ! result variable
6971 character(len=max_str_ln) :: file_name ! name of plot file
6972 character(len=max_str_ln) :: description ! description of plot
6973 integer :: tot_dim(3), loc_offset(3) ! total dimensions and local offset
6974 type(grid_type) :: grid_trim ! trimmed equilibrium grid
6975
6976 ! initialize ierr
6977 ierr = 0
6978
6979 ! output
6980 call writo('Going to test whether D1 D2 h_H is calculated correctly')
6981 call lvl_ud(1)
6982
6983 ! trim extended grid into plot grid
6984 ierr = trim_grid(grid_eq,grid_trim,norm_id)
6985 chckerr('')
6986
6987 ! set up res
6988 allocate(res(grid_trim%n(1),grid_trim%n(2),grid_trim%loc_n_r,6))
6989
6990 ! set total and local dimensions and local offset
6991 tot_dim = [grid_trim%n(1),grid_trim%n(2),grid_trim%n(3)]
6992 loc_offset = [0,0,grid_trim%i_min-1]
6993
6994 ! set up boundary conditions
6995 if (ias.eq.0) then ! top-bottom symmetric
6996 bcs(:,1) = [1,1] ! theta(even): zero first derivative
6997 bcs(:,2) = [2,2] ! theta(odd): zero first derivative
6998 else
6999 bcs(:,1) = [-1,-1] ! theta(even): periodic
7000 bcs(:,2) = [-1,-1] ! theta(odd): periodic
7001 end if
7002 bcs_val = 0._dp
7003
7004 ! boundary condition type for each metric element
7005 bc_ld = [1,2,0,1,0,1] ! 1: even, 2: odd, 0: zero
7006
7007 ! calculate D1 D2 h_H alternatively
7008 do ld = 1,6
7009 do kd = norm_id(1),norm_id(2)
7010 do jd = 1,grid_trim%n(2)
7011 if (bc_ld(ld).eq.0) cycle ! quantity is zero
7012
7013 ierr = spline(grid_eq%theta_E(:,jd,kd),&
7014 &eq%h_E(:,jd,kd,ld,0,1,0),grid_eq%theta_E(:,jd,kd),&
7015 &res(:,jd,kd-norm_id(1)+1,ld),ord=norm_disc_prec_eq,&
7016 &deriv=1,bcs=bcs(:,bc_ld(ld)),&
7017 &bcs_val=bcs_val(:,bc_ld(ld)))
7018 chckerr('')
7019 end do
7020 end do
7021 end do
7022
7023 ! set up plot variables for calculated values
7024 do id = 1,3
7025 do kd = 1,3
7026 ! user output
7027 call writo('Testing h_H('//trim(i2str(kd))//','//&
7028 &trim(i2str(id))//')')
7029 call lvl_ud(1)
7030
7031 ! set some variables
7032 file_name = 'TEST_D12h_H_'//trim(i2str(kd))//'_'//&
7033 &trim(i2str(id))
7034 description = 'Testing calculated with given value for D12h_H('&
7035 &//trim(i2str(kd))//','//trim(i2str(id))//')'
7036
7037 ! plot difference
7038 call plot_diff_hdf5(res(:,:,:,c([kd,id],.true.)),&
7039 &eq%h_E(:,:,norm_id(1):norm_id(2),&
7040 &c([kd,id],.true.),1,1,0),file_name,tot_dim,loc_offset,&
7041 &description,output_message=.true.)
7042
7043 call lvl_ud(-1)
7044 end do
7045 end do
7046
7047 ! clean up
7048 call grid_trim%dealloc()
7049
7050 ! user output
7051 call lvl_ud(-1)
7052 call writo('Test complete')
7053 end function test_d12h_h
7054
7055 !> Performs tests on \f$ \mathcal{J}_\text{F}\f$.
7056 !!
7057 !! - comparing it with the determinant of \f$g_\text{F}\f$
7058 !! - comparing it with the direct formula
7059 !!
7060 !! \note Debug version only
7061 !!
7062 !! \return ierr
7063 integer function test_jac_f(grid_eq,eq_1,eq_2) result(ierr)
7064 use num_vars, only: eq_style, use_pol_flux_f
7065 use grid_utilities, only: trim_grid
7066 use num_utilities, only: calc_det
7067 use helena_vars, only: h_h_33, rbphi_h
7068
7069 character(*), parameter :: rout_name = 'test_jac_F'
7070
7071 ! input / output
7072 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
7073 type(eq_1_type), intent(in), target :: eq_1 !< flux equilibrium
7074 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium
7075
7076 ! local variables
7077 integer :: norm_id(2) ! untrimmed normal indices for trimmed grids
7078 real(dp), allocatable :: res(:,:,:) ! result variable
7079 integer :: jd, kd ! counters
7080 character(len=max_str_ln) :: file_name ! name of plot file
7081 character(len=max_str_ln) :: description ! description of plot
7082 integer :: tot_dim(3), loc_offset(3) ! total dimensions and local offset
7083 type(grid_type) :: grid_trim ! trimmed equilibrium grid
7084 real(dp), pointer :: dflux(:) => null() ! points to D flux_p or D flux_t in E coordinates
7085 integer :: pmone ! plus or minus one
7086
7087 ! initialize ierr
7088 ierr = 0
7089
7090 ! output
7091 call writo('Going to test the calculation of the Jacobian in Flux &
7092 &coordinates')
7093 call lvl_ud(1)
7094
7095 ! trim extended grid into plot grid
7096 ierr = trim_grid(grid_eq,grid_trim,norm_id)
7097 chckerr('')
7098
7099 ! set up res
7100 allocate(res(grid_trim%n(1),grid_trim%n(2),grid_trim%loc_n_r))
7101
7102 ! set total and local dimensions and local offset
7103 tot_dim = [grid_trim%n(1),grid_trim%n(2),grid_trim%n(3)]
7104 loc_offset = [0,0,grid_trim%i_min-1]
7105
7106 ! 1. Compare with determinant of g_F
7107
7108 ! calculate Jacobian from determinant of g_F
7109 ierr = calc_det(res,eq_2%g_F(:,:,norm_id(1):norm_id(2),:,0,0,0),3)
7110 chckerr('')
7111
7112 ! set some variables
7113 file_name = 'TEST_jac_F_1'
7114 description = 'Testing whether the Jacobian in Flux coordinates is &
7115 &consistent with determinant of metric matrix'
7116
7117 ! plot difference
7118 call plot_diff_hdf5(res(:,:,:),&
7119 &eq_2%jac_F(:,:,norm_id(1):norm_id(2),0,0,0)**2,&
7120 &file_name,tot_dim,loc_offset,description,output_message=.true.)
7121
7122 ! 2. Compare with explicit formula
7123
7124 ! set up Dflux and pmone
7125 if (use_pol_flux_f) then ! using poloidal flux
7126 dflux => eq_1%flux_p_E(:,1)
7127 pmone = 1 ! flux_p_V = flux_p_F
7128 else
7129 dflux => eq_1%flux_t_E(:,1)
7130 pmone = -1 ! flux_t_V = - flux_t_F
7131 end if
7132
7133 ! calculate jac_F
7134 ! choose which equilibrium style is being used:
7135 ! 1: VMEC
7136 ! 2: HELENA
7137 select case (eq_style)
7138 case (1) ! VMEC
7139 do kd = norm_id(1),norm_id(2)
7140 res(:,:,kd-norm_id(1)+1) = pmone * 2*pi * &
7141 &eq_2%R_E(:,:,kd,0,0,0)*&
7142 &(eq_2%R_E(:,:,kd,1,0,0)*eq_2%Z_E(:,:,kd,0,1,0) - &
7143 &eq_2%Z_E(:,:,kd,1,0,0)*eq_2%R_E(:,:,kd,0,1,0)) / &
7144 &(dflux(kd)*(1+eq_2%L_E(:,:,kd,0,1,0)))
7145 end do
7146 case (2) ! HELENA
7147 do kd = norm_id(1),norm_id(2)
7148 do jd = 1,grid_trim%n(2)
7149 res(:,jd,kd-norm_id(1)+1) = eq_1%q_saf_E(kd,0)/&
7150 &(h_h_33(:,kd+grid_eq%i_min-1)*&
7151 &rbphi_h(kd+grid_eq%i_min-1,0)) ! h_H_33 = 1/R^2 and RBphi_H = F are tabulated in eq. grid
7152 end do
7153 end do
7154 end select
7155
7156 ! set some variables
7157 file_name = 'TEST_jac_F_2'
7158 description = 'Testing whether the Jacobian in Flux coordinates is &
7159 &consistent with explicit formula'
7160
7161 ! plot difference
7162 call plot_diff_hdf5(res(:,:,:),&
7163 &eq_2%jac_F(:,:,norm_id(1):norm_id(2),0,0,0),file_name,tot_dim,&
7164 &loc_offset,description,output_message=.true.)
7165
7166 ! clean up
7167 nullify(dflux)
7168 call grid_trim%dealloc()
7169
7170 ! user output
7171 call lvl_ud(-1)
7172 call writo('Test complete')
7173 end function test_jac_f
7174
7175 !> Tests whether \f$g_\text{V}\f$ is calculated correctly.
7176 !!
7177 !! \note Debug version only
7178 !!
7179 !! \return ierr
7180 integer function test_g_v(grid_eq,eq) result(ierr)
7181 use grid_utilities, only: trim_grid
7182 use num_utilities, only: c
7183
7184 character(*), parameter :: rout_name = 'test_g_V'
7185
7186 ! input / output
7187 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
7188 type(eq_2_type), intent(in) :: eq !< metric equilibrium
7189
7190 ! local variables
7191 integer :: norm_id(2) ! untrimmed normal indices for trimmed grids
7192 integer :: id, kd ! counters
7193 real(dp), allocatable :: res(:,:,:,:) ! result variable
7194 character(len=max_str_ln) :: file_name ! name of plot file
7195 character(len=max_str_ln) :: description ! description of plot
7196 integer :: tot_dim(3), loc_offset(3) ! total dimensions and local offset
7197 type(grid_type) :: grid_trim ! trimmed equilibrium grid
7198
7199 ! initialize ierr
7200 ierr = 0
7201
7202 ! output
7203 call writo('Going to test whether g_V is calculated correctly')
7204 call lvl_ud(1)
7205
7206 ! trim extended grid into plot grid
7207 ierr = trim_grid(grid_eq,grid_trim,norm_id)
7208 chckerr('')
7209
7210 ! set up res
7211 allocate(res(grid_trim%n(1),grid_trim%n(2),grid_trim%loc_n_r,6))
7212
7213 ! set total and local dimensions and local offset
7214 tot_dim = [grid_trim%n(1),grid_trim%n(2),grid_trim%n(3)]
7215 loc_offset = [0,0,grid_trim%i_min-1]
7216
7217 ! calculate g_V(1,1)
7218 res(:,:,:,1) = eq%R_E(:,:,norm_id(1):norm_id(2),1,0,0)**2 + &
7219 &eq%Z_E(:,:,norm_id(1):norm_id(2),1,0,0)**2
7220 ! calculate g_V(2,1)
7221 res(:,:,:,2) = eq%R_E(:,:,norm_id(1):norm_id(2),1,0,0)*&
7222 &eq%R_E(:,:,norm_id(1):norm_id(2),0,1,0) + &
7223 &eq%Z_E(:,:,norm_id(1):norm_id(2),1,0,0)*&
7224 &eq%Z_E(:,:,norm_id(1):norm_id(2),0,1,0)
7225 ! calculate g_V(3,1)
7226 res(:,:,:,3) = eq%R_E(:,:,norm_id(1):norm_id(2),1,0,0)*&
7227 &eq%R_E(:,:,norm_id(1):norm_id(2),0,0,1) + &
7228 &eq%Z_E(:,:,norm_id(1):norm_id(2),1,0,0)*&
7229 &eq%Z_E(:,:,norm_id(1):norm_id(2),0,0,1)
7230 ! calculate g_V(2,2)
7231 res(:,:,:,4) = eq%R_E(:,:,norm_id(1):norm_id(2),0,1,0)**2 + &
7232 &eq%Z_E(:,:,norm_id(1):norm_id(2),0,1,0)**2
7233 ! calculate g_V(3,2)
7234 res(:,:,:,5) = eq%R_E(:,:,norm_id(1):norm_id(2),0,1,0)*&
7235 &eq%R_E(:,:,norm_id(1):norm_id(2),0,0,1) + &
7236 &eq%Z_E(:,:,norm_id(1):norm_id(2),0,1,0)*&
7237 &eq%Z_E(:,:,norm_id(1):norm_id(2),0,0,1)
7238 ! calculate g_V(3,3)
7239 res(:,:,:,6) = eq%R_E(:,:,norm_id(1):norm_id(2),0,0,1)**2 + &
7240 &eq%Z_E(:,:,norm_id(1):norm_id(2),0,0,1)**2 + &
7241 &eq%R_E(:,:,norm_id(1):norm_id(2),0,0,0)**2
7242
7243 ! set up plot variables for calculated values
7244 do id = 1,3
7245 do kd = 1,3
7246 ! user output
7247 call writo('Testing g_V('//trim(i2str(kd))//','//&
7248 &trim(i2str(id))//')')
7249 call lvl_ud(1)
7250
7251 ! set some variables
7252 file_name = 'TEST_g_V_'//trim(i2str(kd))//'_'//trim(i2str(id))
7253 description = 'Testing calculated with given value for g_V('//&
7254 &trim(i2str(kd))//','//trim(i2str(id))//')'
7255
7256 ! plot difference
7257 call plot_diff_hdf5(res(:,:,:,c([kd,id],.true.)),&
7258 &eq%g_E(:,:,norm_id(1):norm_id(2),c([kd,id],.true.),&
7259 &0,0,0),file_name,tot_dim,loc_offset,description,&
7260 &output_message=.true.)
7261
7262 call lvl_ud(-1)
7263 end do
7264 end do
7265
7266 ! clean up
7267 call grid_trim%dealloc()
7268
7269 ! user output
7270 call lvl_ud(-1)
7271 call writo('Test complete')
7272 end function test_g_v
7273
7274 !> Tests whether \f$\mathcal{J}_\text{V}\f$ is calculated correctly.
7275 !!
7276 !! \note Debug version only
7277 !!
7278 !! \return ierr
7279 integer function test_jac_v(grid_eq,eq) result(ierr)
7280 use grid_utilities, only: trim_grid
7281 use num_utilities, only: calc_det
7282
7283 character(*), parameter :: rout_name = 'test_jac_V'
7284
7285 ! input / output
7286 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
7287 type(eq_2_type), intent(in) :: eq !< metric equilibrium
7288
7289 ! local variables
7290 integer :: norm_id(2) ! untrimmed normal indices for trimmed grids
7291 real(dp), allocatable :: res(:,:,:) ! result variable
7292 character(len=max_str_ln) :: file_name ! name of plot file
7293 character(len=max_str_ln) :: description ! description of plot
7294 integer :: tot_dim(3), loc_offset(3) ! total dimensions and local offset
7295 type(grid_type) :: grid_trim ! trimmed equilibrium grid
7296
7297 ! initialize ierr
7298 ierr = 0
7299
7300 ! output
7301 call writo('Going to test the calculation of the Jacobian in the VMEC &
7302 &coords.')
7303 call lvl_ud(1)
7304
7305 ! trim extended grid into plot grid
7306 ierr = trim_grid(grid_eq,grid_trim,norm_id)
7307 chckerr('')
7308
7309 ! set up res
7310 allocate(res(grid_trim%n(1),grid_trim%n(2),grid_trim%loc_n_r))
7311
7312 ! set total and local dimensions and local offset
7313 tot_dim = [grid_trim%n(1),grid_trim%n(2),grid_trim%n(3)]
7314 loc_offset = [0,0,grid_trim%i_min-1]
7315
7316 ! calculate Jacobian from determinant of g_V
7317 ierr = calc_det(res,eq%g_E(:,:,norm_id(1):norm_id(2),:,0,0,0),3)
7318 chckerr('')
7319
7320 ! set some variables
7321 file_name = 'TEST_jac_V'
7322 description = 'Testing whether the Jacobian in VMEC coordinates is &
7323 &consistent with determinant of metric matrix'
7324
7325 ! plot difference
7326 call plot_diff_hdf5(-sqrt(res),&
7327 &eq%jac_E(:,:,norm_id(1):norm_id(2),0,0,0),&
7328 &file_name,tot_dim,loc_offset,description,output_message=.true.)
7329
7330 ! clean up
7331 call grid_trim%dealloc()
7332
7333 ! user output
7334 call lvl_ud(-1)
7335 call writo('Test complete')
7336 end function test_jac_v
7337
7338 !> Tests whether \f$\vec{B}_\text{F}\f$ is calculated correctly.
7339 !!
7340 !! \note Debug version only
7341 !!
7342 !! \return ierr
7343 integer function test_b_f(grid_eq,eq_1,eq_2) result(ierr)
7344 use num_vars, only: eq_style
7345 use grid_utilities, only: trim_grid
7346 use num_utilities, only: c
7347 use vmec_utilities, only: fourier2real
7348 use vmec_vars, only: b_v_sub_s, b_v_sub_c, b_v_c, b_v_s, is_asym_v
7349
7350 character(*), parameter :: rout_name = 'test_B_F'
7351
7352 ! input / output
7353 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
7354 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium
7355 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium
7356
7357 ! local variables
7358 integer :: norm_id(2) ! untrimmed normal indices for trimmed grids
7359 integer :: norm_id_f(2) ! norm_id transposed to full grid
7360 integer :: id, kd ! counters
7361 real(dp), allocatable :: res(:,:,:,:) ! result variable
7362 real(dp), allocatable :: res2(:,:,:,:) ! result variable
7363 character(len=max_str_ln) :: file_name ! name of plot file
7364 character(len=max_str_ln) :: description ! description of plot
7365 integer :: tot_dim(3), loc_offset(3) ! total dimensions and local offset
7366 type(grid_type) :: grid_trim ! trimmed equilibrium grid
7367
7368 ! initialize ierr
7369 ierr = 0
7370
7371 ! output
7372 call writo('Going to test whether the magnetic field is calculated &
7373 &correctly')
7374 call lvl_ud(1)
7375
7376 ! trim extended grid into plot grid
7377 ierr = trim_grid(grid_eq,grid_trim,norm_id)
7378 chckerr('')
7379
7380 ! set norm_id_f for quantities tabulated on full grid
7381 norm_id_f = grid_eq%i_min+norm_id-1
7382
7383 ! set up res and res2
7384 allocate(res(grid_trim%n(1),grid_trim%n(2),grid_trim%loc_n_r,4))
7385 allocate(res2(grid_trim%n(1),grid_trim%n(2),grid_trim%loc_n_r,4))
7386
7387 ! set total and local dimensions and local offset
7388 tot_dim = [grid_trim%n(1),grid_trim%n(2),grid_trim%n(3)]
7389 loc_offset = [0,0,grid_trim%i_min-1]
7390
7391 ! get covariant components and magnitude of B_E
7392 ! choose which equilibrium style is being used:
7393 ! 1: VMEC
7394 ! 2: HELENA
7395 select case (eq_style)
7396 case (1) ! VMEC
7397 do id = 1,3
7398 ierr = fourier2real(&
7399 &b_v_sub_c(:,norm_id_f(1):norm_id_f(2),id),&
7400 &b_v_sub_s(:,norm_id_f(1):norm_id_f(2),id),&
7401 &grid_eq%trigon_factors(:,:,:,norm_id(1):&
7402 &norm_id(2),:),res(:,:,:,id))
7403 chckerr('')
7404 end do
7405 ierr = fourier2real(b_v_c(:,norm_id_f(1):norm_id_f(2)),&
7406 &b_v_s(:,norm_id_f(1):norm_id_f(2)),&
7407 &grid_eq%trigon_factors(:,:,:,norm_id(1):norm_id(2),:),&
7408 &res(:,:,:,4),[.true.,is_asym_v])
7409 chckerr('')
7410 case (2) ! HELENA
7411 res(:,:,:,1) = &
7412 &eq_2%g_E(:,:,norm_id(1):norm_id(2),c([1,2],.true.),0,0,0)
7413 res(:,:,:,2) = &
7414 &eq_2%g_E(:,:,norm_id(1):norm_id(2),c([2,2],.true.),0,0,0)
7415 do kd = norm_id(1),norm_id(2)
7416 res(:,:,kd-norm_id(1)+1,3) = eq_1%q_saf_E(kd,0)*&
7417 &eq_2%g_E(:,:,kd,c([3,3],.true.),0,0,0)
7418 res(:,:,kd-norm_id(1)+1,4) = &
7419 &sqrt(eq_2%g_E(:,:,kd,c([2,2],.true.),0,0,0) + &
7420 &eq_1%q_saf_E(kd,0)**2*&
7421 &eq_2%g_E(:,:,kd,c([3,3],.true.),0,0,0))
7422 end do
7423 do id = 1,4
7424 do kd = norm_id(1),norm_id(2)
7425 res(:,:,kd-norm_id(1)+1,id) = &
7426 &res(:,:,kd-norm_id(1)+1,id)/&
7427 &eq_2%jac_E(:,:,kd,0,0,0)
7428 end do
7429 end do
7430 end select
7431
7432 ! transform them to Flux coord. system
7433 res2 = 0._dp
7434 do id = 1,3
7435 do kd = 1,3
7436 res2(:,:,:,id) = res2(:,:,:,id) + &
7437 &res(:,:,:,kd) * eq_2%T_FE(:,:,norm_id(1):&
7438 &norm_id(2),c([id,kd],.false.),0,0,0)
7439 end do
7440 end do
7441 res2(:,:,:,4) = res(:,:,:,4)
7442
7443 ! set up plot variables for calculated values
7444 do id = 1,4
7445 ! user output
7446 if (id.lt.4) then
7447 call writo('Testing B_F_'//trim(i2str(id)))
7448 else
7449 call writo('Testing B_F')
7450 end if
7451 call lvl_ud(1)
7452
7453 ! set some variables
7454 if (id.lt.4) then
7455 file_name = 'TEST_B_F_'//trim(i2str(id))
7456 description = 'Testing calculated with given value for B_F_'//&
7457 &trim(i2str(id))
7458 else
7459 file_name = 'TEST_B_F'
7460 description = 'Testing calculated with given value for B_F'
7461 end if
7462
7463 ! plot difference
7464 if (id.lt.4) then
7465 call plot_diff_hdf5(res2(:,:,:,id),&
7466 &eq_2%g_F(:,:,norm_id(1):norm_id(2),c([3,id],.true.),0,0,0)&
7467 &/eq_2%jac_F(:,:,norm_id(1):norm_id(2),0,0,0),file_name,&
7468 &tot_dim,loc_offset,description,output_message=.true.)
7469 else
7470 call plot_diff_hdf5(res2(:,:,:,id),sqrt(&
7471 &eq_2%g_F(:,:,norm_id(1):norm_id(2),c([3,3],.true.),0,0,0))&
7472 &/eq_2%jac_F(:,:,norm_id(1):norm_id(2),0,0,0),file_name,&
7473 &tot_dim,loc_offset,description,output_message=.true.)
7474 end if
7475
7476 call lvl_ud(-1)
7477 end do
7478
7479 ! clean up
7480 call grid_trim%dealloc()
7481
7482 ! user output
7483 call lvl_ud(-1)
7484 call writo('Test complete')
7485 end function test_b_f
7486
7487 !> Performs tests on pressure balance.
7488 !!
7489 !! \f[\mu_0 \frac{\partial p}{\partial u^2} = \frac{1}{\mathcal{J}}
7490 !! \left(\frac{\partial B_2}{\partial u^3} -
7491 !! \frac{\partial B_3}{\partial u^2}\right)\f]
7492 !! \f[\mu_0 \mathcal{J} \frac{\partial p}{\partial u^3} = 0 \rightarrow
7493 !! \left(\frac{\partial B_1}{\partial u^3} =
7494 !! \frac{\partial B_3}{\partial u^1}\right), \f]
7495 !! working in the (modified) Flux coordinates
7496 !! \f$\left(\alpha,\psi,\theta\right)_\text{F}\f$
7497 !!
7498 !! \note Debug version only
7499 !!
7500 !! \return ierr
7501 integer function test_p(grid_eq,eq_1,eq_2) result(ierr)
7502 use num_utilities, only: c, spline
7503 use grid_utilities, only: trim_grid
7504 use eq_vars, only: vac_perm
7506 use helena_vars, only: rbphi_h, h_h_11, h_h_12
7507
7508 character(*), parameter :: rout_name = 'test_p'
7509
7510 ! input / output
7511 type(grid_type), intent(in) :: grid_eq !< equilibrium grid
7512 type(eq_1_type), intent(in) :: eq_1 !< flux equilibrium variables
7513 type(eq_2_type), intent(in) :: eq_2 !< metric equilibrium variables
7514
7515 ! local variables
7516 integer :: norm_id(2) ! untrimmed normal indices for trimmed grids
7517 integer :: norm_id_f(2) ! norm_id transposed to full grid
7518 real(dp), allocatable :: res(:,:,:,:) ! result variable
7519 integer :: id, jd, kd ! counters
7520 character(len=max_str_ln) :: file_name ! name of plot file
7521 character(len=max_str_ln) :: description ! description of plot
7522 integer :: tot_dim(3), loc_offset(3) ! total dimensions and local offset
7523 type(grid_type) :: grid_trim ! trimmed equilibrium grid
7524
7525 ! initialize ierr
7526 ierr = 0
7527
7528 ! output
7529 call writo('Going to test the consistency of the equilibrium variables &
7530 &with the given pressure')
7531 call lvl_ud(1)
7532
7533 ! trim extended grid into plot grid
7534 ierr = trim_grid(grid_eq,grid_trim,norm_id)
7535 chckerr('')
7536
7537 ! set up res
7538 allocate(res(grid_trim%n(1),grid_trim%n(2),grid_trim%loc_n_r,2))
7539
7540 ! user output
7541 call writo('Checking if mu_0 D2 p = 1/J (D3 B_2 - D2_B3)')
7542 call lvl_ud(1)
7543
7544 ! set total and local dimensions and local offset
7545 tot_dim = [grid_trim%n(1),grid_trim%n(2),grid_trim%n(3)]
7546 loc_offset = [0,0,grid_trim%i_min-1]
7547
7548 ! calculate mu_0 D2p
7549 res(:,:,:,1) = &
7550 &(eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([2,3],.true.),0,0,1) - &
7551 &eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([3,3],.true.),0,1,0))/&
7552 &eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,0) - &
7553 &(eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([2,3],.true.),0,0,0)*&
7554 &eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,1) - &
7555 &eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([3,3],.true.),0,0,0)*&
7556 &eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,1,0))/ &
7557 &(eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,0)**2)
7558 res(:,:,:,1) = res(:,:,:,1)/eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,0)
7559 !!call plot_HDF5('var','TEST_Dg_FD_23',eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([2,3],.true.),0,0,1))
7560 !!call plot_HDF5('var','TEST_g_FD_23',eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([2,3],.true.),0,0,0))
7561 !!call plot_HDF5('var','TEST_Dg_FD_33',eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([3,3],.true.),0,1,0))
7562 !!call plot_HDF5('var','TEST_g_FD_33',eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([3,3],.true.),0,0,0))
7563 !!call plot_HDF5('var','TEST_Dg_FD',eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,1))
7564 !!call plot_HDF5('var','TEST_g_FD',eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,0))
7565
7566 ! save mu_0 D2p in res
7567 do kd = norm_id(1),norm_id(2)
7568 res(:,:,kd-norm_id(1)+1,2) = vac_perm*eq_1%pres_FD(kd,1)
7569 end do
7570
7571 ! set some variables
7572 file_name = 'TEST_D2p'
7573 description = 'Testing whether mu_0 D2 p = 1/J (D3 B_2 - D2_B3)'
7574
7575 ! plot difference
7576 call plot_diff_hdf5(res(:,:,:,1),res(:,:,:,2),file_name,tot_dim,&
7577 &loc_offset,description,output_message=.true.)
7578
7579 call lvl_ud(-1)
7580
7581 ! user output
7582 call writo('Checking if mu_0 J D1p = 0 => D3 B_1 = D2 B_3')
7583 call lvl_ud(1)
7584
7585 ! calculate D3 B1
7586 res(:,:,:,1) = &
7587 &eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([1,3],.true.),0,0,1)/&
7588 &eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,0) - &
7589 &eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([1,3],.true.),0,0,0)*&
7590 &eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,1) / &
7591 &eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,0)**2
7592
7593 ! calculate D1 B3
7594 res(:,:,:,2) = &
7595 &eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([3,3],.true.),1,0,0)/&
7596 &eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,0) - &
7597 &eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([3,3],.true.),0,0,0)*&
7598 &eq_2%jac_FD(:,:,norm_id(1):norm_id(2),1,0,0) / &
7599 &eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,0)**2
7600
7601 ! set some variables
7602 file_name = 'TEST_D1p'
7603 description = 'Testing whether D3 B_1 = D1 B_3'
7604
7605 ! plot difference
7606 call plot_diff_hdf5(res(:,:,:,1),res(:,:,:,2),file_name,tot_dim,&
7607 &loc_offset,description,output_message=.true.)
7608
7609 call lvl_ud(-1)
7610
7611 ! extra testing if Helena
7612 if (eq_style.eq.2) then
7613 ! set norm_id_f for quantities tabulated on full grid
7614 norm_id_f = grid_eq%i_min+norm_id-1
7615
7616 ! user output
7617 call writo('Checking if D2 B_3 |F = D1 (qF+qh_11/F) |H')
7618 call lvl_ud(1)
7619
7620 ! calculate if D2 B_3 = D1 (qF) + D1 (q/F h_11)
7621 res(:,:,:,1) = &
7622 &(eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([3,3],.true.),0,1,0)/&
7623 &eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,0) - &
7624 &eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([3,3],.true.),0,0,0)*&
7625 &eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,1,0)/ &
7626 &(eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,0)**2))
7627 do jd = 1,grid_trim%n(2)
7628 do id = 1,grid_trim%n(1)
7629 ierr = spline(grid_trim%loc_r_E,&
7630 &eq_1%q_saf_E(norm_id(1):norm_id(2),0)*&
7631 &rbphi_h(norm_id_f(1):norm_id_f(2),0)+&
7632 &eq_1%q_saf_E(norm_id(1):norm_id(2),0)*&
7633 &h_h_11(id,norm_id_f(1):norm_id_f(2))/&
7634 &rbphi_h(norm_id_f(1):norm_id_f(2),0),&
7635 &grid_trim%loc_r_E,res(id,jd,:,2),&
7636 &ord=norm_disc_prec_eq,deriv=1)
7637 chckerr('')
7638 end do
7639 end do
7640
7641 ! set some variables
7642 file_name = 'TEST_D2B_3'
7643 description = 'Testing whether D2 B_3 |F = D1 (qF+qh_11/F) |H'
7644
7645 ! plot difference
7646 call plot_diff_hdf5(res(:,:,:,1),res(:,:,:,2),file_name,tot_dim,&
7647 &loc_offset,description,output_message=.true.)
7648
7649 call lvl_ud(-1)
7650
7651 ! user output
7652 call writo('Checking if D3 B_2 |F = -q/F D2 h_11 + F q'' |H')
7653 call lvl_ud(1)
7654
7655 ! calculate if D3 B_2 = -q/F D2 h_11 + F D1 q
7656 res(:,:,:,1) = &
7657 &eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([2,3],.true.),0,0,1)/&
7658 &eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,0) - &
7659 &eq_2%g_FD(:,:,norm_id(1):norm_id(2),c([2,3],.true.),0,0,0)*&
7660 &eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,1)/ &
7661 &(eq_2%jac_FD(:,:,norm_id(1):norm_id(2),0,0,0)**2)
7662 do kd = norm_id(1),norm_id(2)
7663 do jd = 1,grid_trim%n(2)
7664 ierr = spline(grid_eq%theta_E(:,jd,kd),&
7665 &h_h_12(:,kd+grid_eq%i_min-1),grid_eq%theta_E(:,jd,kd),&
7666 &res(:,jd,kd-norm_id(1)+1,2),&
7667 &ord=norm_disc_prec_eq,deriv=1)
7668 chckerr('')
7669 end do
7670 res(:,:,kd-norm_id(1)+1,2) = &
7671 &-eq_1%q_saf_E(kd,0)/rbphi_h(kd+grid_eq%i_min-1,0) * &
7672 &res(:,:,kd-norm_id(1)+1,2) + &
7673 &rbphi_h(kd+grid_eq%i_min-1,0) * &
7674 &eq_1%q_saf_E(kd,1)
7675 end do
7676
7677 ! set some variables
7678 file_name = 'TEST_D3B_2'
7679 description = 'Testing whether D3 B_2 |F = -D2 (qh_12/F) + f q'' |H'
7680
7681 ! plot difference
7682 call plot_diff_hdf5(res(:,:,:,1),res(:,:,:,2),file_name,tot_dim,&
7683 &loc_offset,description,output_message=.true.)
7684
7685 call lvl_ud(-1)
7686 end if
7687
7688 ! clean up
7689 call grid_trim%dealloc()
7690
7691 ! user output
7692 call lvl_ud(-1)
7693 call writo('Test complete')
7694 end function test_p
7695#endif
7696end module eq_ops
Calculate the equilibrium quantities on a grid determined by straight field lines.
Definition eq_ops.f90:48
Calculate the lower metric elements in the C(ylindrical) coordinate system.
Definition eq_ops.f90:139
Calculate the metric coefficients in the F(lux) coordinate system.
Definition eq_ops.f90:183
Calculate the lower metric coefficients in the equilibrium H(ELENA) coordinate system.
Definition eq_ops.f90:169
Calculate the lower metric coefficients in the equilibrium V(MEC) coordinate system.
Definition eq_ops.f90:156
Calculate , the jacobian in Flux coordinates.
Definition eq_ops.f90:232
Calculate , the jacobian in HELENA coordinates.
Definition eq_ops.f90:213
Calculate , the jacobian in V(MEC) coordinates.
Definition eq_ops.f90:196
Calculate , & and derivatives in VMEC coordinates.
Definition eq_ops.f90:126
Calculate , the transformation matrix between H(ELENA) and F(lux) coordinate systems.
Definition eq_ops.f90:271
Calculate , the transformation matrix between C(ylindrical) and V(mec) coordinate system.
Definition eq_ops.f90:245
Calculate , the transformation matrix between V(MEC) and F(lux) coordinate systems.
Definition eq_ops.f90:258
Print equilibrium quantities to an output file:
Definition eq_ops.f90:90
Redistribute the equilibrium variables, but only the Flux variables are saved.
Definition eq_ops.f90:103
Transforms derivatives of the equilibrium quantities in E coordinates to derivatives in the F coordin...
Calculate from and where and , according to weyens3d.
Calculates the toroidal difference for a magnitude calculated on three toroidal points: two extremiti...
Deallocates 1D variable.
Definition HDF5_vars.f90:68
Gather parallel variable in serial version on group master.
Add to an array (3) the product of arrays (1) and (2).
Sorting with the bubble sort routine.
Calculate determinant of a matrix.
Integrates a function using the trapezoidal rule.
Order a periodic function to include and an overlap.
Wrapper to the pspline library, making it easier to use for 1-D applications where speed is not the m...
Prints variables vars with names var_names in an HDF5 file with name c file_name and accompanying XDM...
Print 2-D output on a file.
Print 3-D output on a file.
Inverse Fourier transformation, from VMEC.
Operations on the equilibrium variables.
Definition eq_ops.f90:4
logical, public debug_create_vmec_input
plot debug information for create_vmec_input()
Definition eq_ops.f90:34
integer function, public calc_derived_q(grid_eq, eq_1, eq_2)
Calculates derived equilibrium quantities system.
Definition eq_ops.f90:4287
subroutine, public normalize_input()
Normalize input quantities.
Definition eq_ops.f90:5643
logical, public debug_calc_derived_q
plot debug information for calc_derived_q()
Definition eq_ops.f90:30
integer function, public delta_r_plot(grid_eq, eq_1, eq_2, xyz, rich_lvl)
Plots HALF of the change in the position vectors for 2 different toroidal positions,...
Definition eq_ops.f90:6173
logical, public debug_j_plot
plot debug information for j_plot()
Definition eq_ops.f90:32
integer function, public kappa_plot(grid_eq, eq_1, eq_2, rich_lvl, xyz)
Plots the curvature.
Definition eq_ops.f90:5971
integer function, public divide_eq_jobs(n_par_x, arr_size, n_div, n_div_max, n_par_x_base, range_name)
Divides the equilibrium jobs.
Definition eq_ops.f90:6566
integer function, public calc_eq_jobs_lims(n_par_x, n_div)
Calculate eq_jobs_lims.
Definition eq_ops.f90:6701
integer function, public calc_normalization_const()
Sets up normalization constants.
Definition eq_ops.f90:5405
integer function, public j_plot(grid_eq, eq_1, eq_2, rich_lvl, plot_fluxes, xyz)
Plots the current.
Definition eq_ops.f90:5803
integer function, public b_plot(grid_eq, eq_1, eq_2, rich_lvl, plot_fluxes, xyz)
Plots the magnetic fields.
Definition eq_ops.f90:5700
integer function, public flux_q_plot(grid_eq, eq)
Plots the flux quantities in the solution grid.
Definition eq_ops.f90:3810
Numerical utilities related to equilibrium variables.
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.
Variables that have to do with equilibrium quantities and the grid used in the calculations:
Definition eq_vars.f90:27
real(dp), public r_0
independent normalization constant for nondimensionalization
Definition eq_vars.f90:42
real(dp), public psi_0
derived normalization constant for nondimensionalization
Definition eq_vars.f90:46
real(dp), public max_flux_f
max. flux in Flux coordinates, set in calc_norm_range_PB3D_in
Definition eq_vars.f90:50
real(dp), public t_0
derived normalization constant for nondimensionalization
Definition eq_vars.f90:47
real(dp), public max_flux_e
max. flux in Equilibrium coordinates, set in calc_norm_range_PB3D_in
Definition eq_vars.f90:49
real(dp), public rho_0
independent normalization constant for nondimensionalization
Definition eq_vars.f90:44
real(dp), public pres_0
independent normalization constant for nondimensionalization
Definition eq_vars.f90:43
real(dp), public vac_perm
either usual mu_0 (default) or normalized
Definition eq_vars.f90:48
real(dp), public b_0
derived normalization constant for nondimensionalization
Definition eq_vars.f90:45
Numerical utilities related to files.
integer function, public count_lines(file_i)
Count non-comment lines in a file.
integer function, public skip_comment(file_i, file_name)
Skips comment when reading a file.
Numerical utilities related to the grids and different coordinate systems.
integer function, public extend_grid_f(grid_in, grid_ext, grid_eq, n_theta_plot, n_zeta_plot, lim_theta_plot, lim_zeta_plot)
Extend a grid angularly.
integer function, public trim_grid(grid_in, grid_out, norm_id)
Trim a grid, removing any overlap between the different regions.
integer function, public calc_xyz_grid(grid_eq, grid_xyz, x, y, z, l, r)
Calculates , and on a grid grid_XYZ, determined through its E(quilibrium) coordinates.
integer function, public calc_vec_comp(grid, grid_eq, eq_1, eq_2, v_com, norm_disc_prec, v_mag, base_name, max_transf, v_flux_tor, v_flux_pol, xyz, compare_tor_pos)
Calculates contra- and covariant components of a vector in multiple coordinate systems.
integer function, public nufft(x, f, f_f, plot_name)
calculates the cosine and sine mode numbers of a function defined on a non-regular grid.
Variables pertaining to the different grids used.
Definition grid_vars.f90:4
real(dp), dimension(:), allocatable, public alpha
field line label alpha
Definition grid_vars.f90:28
integer, public n_r_eq
nr. of normal points in equilibrium grid
Definition grid_vars.f90:20
Operations on HDF5 and XDMF variables.
Definition HDF5_ops.f90:27
integer function, public print_hdf5_arrs(vars, pb3d_name, head_name, rich_lvl, disp_info, ind_print, remove_previous_arrs)
Prints a series of arrays, in the form of an array of pointers, to an HDF5 file.
Variables pertaining to HDF5 and XDMF.
Definition HDF5_vars.f90:4
integer, parameter, public max_dim_var_1d
maximum dimension of var_1D
Definition HDF5_vars.f90:21
Operations on HELENA variables.
Definition HELENA_ops.f90:4
integer function, public test_metrics_h()
Checks whether the metric elements provided by HELENA are consistent with a direct calculation using ...
integer function, public test_harm_cont_h()
Investaige harmonic content of the HELENA variables.
Variables that have to do with HELENA quantities.
integer, public ias
0 if top-bottom symmetric, 1 if not
integer, public nchi
nr. of poloidal points
real(dp), dimension(:,:), allocatable, public r_h
major radius (xout)
real(dp), dimension(:,:), allocatable, public h_h_33
upper metric factor (1 / gem12)
real(dp), dimension(:,:), allocatable, public rbphi_h
real(dp), dimension(:,:), allocatable, public pres_h
pressure profile
real(dp), dimension(:,:), allocatable, public z_h
height (yout)
real(dp), dimension(:,:), allocatable, public q_saf_h
safety factor
real(dp), dimension(:), allocatable, public chi_h
poloidal angle
real(dp), public bmtog_h
B_geo/B_mag.
real(dp), dimension(:,:), allocatable, public h_h_12
upper metric factor (gem12)
real(dp), dimension(:,:), allocatable, public flux_p_h
poloidal flux
real(dp), dimension(:,:), allocatable, public h_h_11
upper metric factor (gem11)
real(dp), dimension(:,:), allocatable, public rot_t_h
rotational transform
real(dp), public rmtog_h
R_geo/R_mag.
real(dp), dimension(:,:), allocatable, public flux_t_h
toroidal flux
Numerical utilities related to input.
integer function, public get_int(lim_lo, lim_hi, ind)
Queries for user input for an integer value, where allowable range can be provided as well.
real(dp) function, public get_real(lim_lo, lim_hi, ind)
Queries for user input for a real value, where allowable range can be provided as well.
subroutine, public pause_prog(ind)
Pauses the running of the program.
logical function, public get_log(yes, ind)
Queries for a logical value yes or no, where the default answer is also to be provided.
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 related to MPI.
integer function, public redistribute_var(var, dis_var, lims, lims_dis)
Redistribute variables according to new limits.
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 gcd(u, v)
Returns least denominator using the GCD.
integer function, public c(ij, sym, n, lim_n)
Convert 2-D coordinates (i,j) to the storage convention used in matrices.
subroutine, public shift_f(al, bl, cl, a, b, c)
Calculate multiplication through shifting of fourier modes A and B into C.
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
logical, public ltest
whether or not to call the testing routines
Definition num_vars.f90:112
real(dp), parameter, public pi
Definition num_vars.f90:83
real(dp), parameter, public mem_scale_fac
empirical scale factor of memory to calculate eq compared to just storing it
Definition num_vars.f90:80
integer, public n_procs
nr. of MPI processes
Definition num_vars.f90:69
character(len=max_str_ln), public eq_name
name of equilibrium file from VMEC or HELENA
Definition num_vars.f90:138
integer, parameter, public max_str_ln
maximum length of strings
Definition num_vars.f90:50
integer, public prog_style
program style (1: PB3D, 2: PB3D_POST)
Definition num_vars.f90:53
real(dp), parameter, public mu_0_original
permeability of free space
Definition num_vars.f90:84
logical, public use_normalization
whether to use normalization or not
Definition num_vars.f90:115
integer, dimension(:,:), allocatable, public eq_jobs_lims
data about eq jobs: [ , ] for all jobs
Definition num_vars.f90:77
integer, public rho_style
style for equilibrium density profile
Definition num_vars.f90:90
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, parameter, public hel_pert_i
file number of HELENA equilibrium perturbation file
Definition num_vars.f90:191
character(len=max_str_ln), public pb3d_name
name of PB3D output file
Definition num_vars.f90:139
integer, public norm_disc_prec_eq
precision for normal discretization for equilibrium
Definition num_vars.f90:120
logical, public export_hel
export HELENA
Definition num_vars.f90:142
integer, public ex_plot_style
external plot style (1: GNUPlot, 2: Bokeh for 2D, Mayavi for 3D)
Definition num_vars.f90:175
real(dp), dimension(2), public rz_0
origin of geometrical poloidal coordinate
Definition num_vars.f90:179
integer, public rank
MPI rank.
Definition num_vars.f90:68
integer, public rich_restart_lvl
starting Richardson level (0: none [default])
Definition num_vars.f90:173
integer, parameter, public hel_export_i
file number of output of HELENA equilibrium export file
Definition num_vars.f90:190
integer, public norm_style
style for normalization
Definition num_vars.f90:92
integer, parameter, public prop_b_tor_i
file number of proportionality factor file
Definition num_vars.f90:192
logical, public use_pol_flux_e
whether poloidal flux is used in E coords.
Definition num_vars.f90:113
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
real(dp), public tol_zero
tolerance for zeros
Definition num_vars.f90:133
real(dp), public max_tot_mem
maximum total memory for all processes [MB]
Definition num_vars.f90:74
integer, public magn_int_style
style for magnetic integrals (1: trapezoidal, 2: Simpson 3/8)
Definition num_vars.f90:124
real(dp), public max_x_mem
maximum memory for perturbation calculations for all processes [MB]
Definition num_vars.f90:75
logical, public no_plots
no plots made
Definition num_vars.f90:140
Operations concerning giving output, on the screen as well as in output files.
Definition output_ops.f90:5
subroutine, public plot_diff_hdf5(a, b, file_name, tot_dim, loc_offset, descr, output_message)
Takes two input vectors and plots these as well as the relative and absolute difference in a HDF5 fil...
subroutine, public draw_ex(var_names, draw_name, nplt, draw_dim, plot_on_screen, ex_plot_style, data_name, draw_ops, extra_ops, is_animated, ranges, delay, persistent)
Use external program to draw a plot.
Variables concerning Richardson extrapolation.
Definition rich_vars.f90:4
integer, public rich_lvl
current level of Richardson extrapolation
Definition rich_vars.f90:19
integer, public n_par_x
nr. of parallel points in field-aligned grid
Definition rich_vars.f90:20
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 r2str(k)
Convert a real (double) to string.
elemental character(len=max_str_ln) function, public r2strt(k)
Convert a real (double) to string.
Operations that concern the output of VMEC.
Definition VMEC_ops.f90:4
subroutine, public normalize_vmec
Normalizes VMEC input.
Definition VMEC_ops.f90:328
Numerical utilities related to the output of VMEC.
integer function, public calc_trigon_factors(theta, zeta, trigon_factors)
Calculate the trigonometric cosine and sine factors.
Variables that concern the output of VMEC.
Definition VMEC_vars.f90:4
real(dp), dimension(:,:), allocatable, public q_saf_v
safety factor
Definition VMEC_vars.f90:38
real(dp), dimension(:,:,:), allocatable, public jac_v_c
Coeff. of in sine series (HM and FM) and norm. deriv.
Definition VMEC_vars.f90:45
real(dp), dimension(:,:,:), allocatable, public b_v_sub_c
Coeff. of B_i in sine series (r,theta,phi) (FM).
Definition VMEC_vars.f90:47
real(dp), dimension(:,:,:), allocatable, public l_v_s
Coeff. of in cosine series (HM) and norm. deriv.
Definition VMEC_vars.f90:44
real(dp), dimension(:,:,:), allocatable, public z_v_c
Coeff. of in sine series (FM) and norm. deriv.
Definition VMEC_vars.f90:41
real(dp), dimension(:,:), allocatable, public rot_t_v
rotational transform
Definition VMEC_vars.f90:37
real(dp), public b_0_v
the magnitude of B at the magnetic axis,
Definition VMEC_vars.f90:33
real(dp), dimension(:,:,:), allocatable, public jac_v_s
Coeff. of in cosine series (HM and FM) and norm. deriv.
Definition VMEC_vars.f90:46
real(dp), dimension(:,:,:), allocatable, public r_v_c
Coeff. of in sine series (FM) and norm. deriv.
Definition VMEC_vars.f90:39
real(dp), dimension(:,:), allocatable, public pres_v
pressure
Definition VMEC_vars.f90:36
real(dp), dimension(:,:), allocatable, public j_v_sup_int
Integrated poloidal and toroidal current (FM).
Definition VMEC_vars.f90:52
real(dp), dimension(:,:,:), allocatable, public b_v_sub_s
Coeff. of B_i in cosine series (r,theta,phi) (FM).
Definition VMEC_vars.f90:48
real(dp), dimension(:,:), allocatable, public b_v_s
Coeff. of magnitude of B in cosine series (HM and FM).
Definition VMEC_vars.f90:51
real(dp), dimension(:,:), allocatable, public flux_t_v
toroidal flux
Definition VMEC_vars.f90:34
real(dp), dimension(:,:,:), allocatable, public z_v_s
Coeff. of in cosine series (FM) and norm. deriv.
Definition VMEC_vars.f90:42
real(dp), dimension(:,:,:), allocatable, public r_v_s
Coeff. of in cosine series (FM) and norm. deriv.
Definition VMEC_vars.f90:40
real(dp), dimension(:,:,:), allocatable, public l_v_c
Coeff. of in sine series (HM) and norm. deriv.
Definition VMEC_vars.f90:43
real(dp), dimension(:,:), allocatable, public b_v_c
Coeff. of magnitude of B in sine series (HM and FM).
Definition VMEC_vars.f90:50
real(dp), dimension(:,:), allocatable, public flux_p_v
poloidal flux
Definition VMEC_vars.f90:35
Numerical utilities related to perturbation operations.
integer function, public calc_memory_x(ord, arr_size, n_mod, mem_size)
Calculate memory in MB necessary for X variables.
Variables pertaining to the perturbation quantities.
Definition X_vars.f90:4
real(dp), public max_r_sol
max. normal range for pert.
Definition X_vars.f90:136
integer, public n_mod_x
size of m_X (pol. flux) or n_X (tor. flux)
Definition X_vars.f90:129
real(dp), public min_r_sol
min. normal range for pert.
Definition X_vars.f90:135
flux equilibrium type
Definition eq_vars.f90:63
metric equilibrium type
Definition eq_vars.f90:114
Type for grids.
Definition grid_vars.f90:59
1D equivalent of multidimensional variables, used for internal HDF5 storage.
Definition HDF5_vars.f90:48