PB3D  [2.45]
Ideal linear high-n MHD stability in 3-D
eq_ops.f90
Go to the documentation of this file.
1 !------------------------------------------------------------------------------!
3 !------------------------------------------------------------------------------!
4 module eq_ops
5 #include <PB3D_macros.h>
6  use str_utilities
7  use output_ops
8  use messages
9  use num_vars, only: pi, dp, max_str_ln, max_deriv
10  use grid_vars, only: grid_type
11  use eq_vars, only: eq_1_type, eq_2_type
12 #if ldebug
13  use num_utilities, only: check_deriv
14 #endif
15 
16  implicit none
17  private
21 #if ldebug
23 #endif
24 
25  ! global variables
26  integer :: fund_n_par
27  logical :: BR_normalization_provided(2) ! used to export HELENA to VMEC
28 #if ldebug
29 
30  logical :: debug_calc_derived_q = .false.
32  logical :: debug_j_plot = .false.
34  logical :: debug_create_vmec_input = .false.
35 #endif
36 
37  ! interfaces
38 
48  interface calc_eq
50  module procedure calc_eq_1
52  module procedure calc_eq_2
53  end interface
54 
90  interface print_output_eq
92  module procedure print_output_eq_1
94  module procedure print_output_eq_2
95  end interface
96 
105  module procedure redistribute_output_eq_1
107  module procedure redistribute_output_eq_2
108  end interface
109 
126  interface calc_rzl
128  module procedure calc_rzl_ind
130  module procedure calc_rzl_arr
131  end interface
132 
139  interface calc_g_c
141  module procedure calc_g_c_ind
143  module procedure calc_g_c_arr
144  end interface
145 
156  interface calc_g_v
158  module procedure calc_g_v_ind
160  module procedure calc_g_v_arr
161  end interface
162 
169  interface calc_g_h
171  module procedure calc_g_h_ind
173  module procedure calc_g_h_arr
174  end interface
175 
183  interface calc_g_f
185  module procedure calc_g_f_ind
187  module procedure calc_g_f_arr
188  end interface
189 
196  interface calc_jac_v
198  module procedure calc_jac_v_ind
200  module procedure calc_jac_v_arr
201  end interface
202 
213  interface calc_jac_h
215  module procedure calc_jac_h_ind
217  module procedure calc_jac_h_arr
218  end interface
219 
232  interface calc_jac_f
234  module procedure calc_jac_f_ind
236  module procedure calc_jac_f_arr
237  end interface
238 
245  interface calc_t_vc
247  module procedure calc_t_vc_ind
249  module procedure calc_t_vc_arr
250  end interface
251 
258  interface calc_t_vf
260  module procedure calc_t_vf_ind
262  module procedure calc_t_vf_arr
263  end interface
264 
271  interface calc_t_hf
273  module procedure calc_t_hf_ind
275  module procedure calc_t_hf_arr
276  end interface
277 
278 contains
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
291  type(eq_1_type), intent(inout) :: eq
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)
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
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
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
450  type(eq_1_type), intent(in) :: eq_1
451  type(eq_2_type), intent(inout) :: eq_2
452  logical, intent(in), optional :: dealloc_vars
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 
783  integer function create_vmec_input(grid_eq,eq_1) result(ierr)
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
794  use num_vars, only: eq_name, hel_pert_i, hel_export_i, &
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
808  type(eq_1_type), intent(in) :: eq_1
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
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
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 
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
2285  type(eq_1_type), intent(in) :: eq
2286  character(len=*), intent(in) :: data_name
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
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
2418  type(eq_2_type), intent(inout) :: eq
2419  character(len=*), intent(in) :: data_name
2420  integer, intent(in), optional :: rich_lvl
2421  logical, intent(in), optional :: par_div
2422  logical, intent(in), optional :: dealloc_vars
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 
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
2603  type(grid_type), intent(in) :: grid_out
2604  type(eq_1_type), intent(in) :: eq
2605  type(eq_1_type), intent(inout) :: eq_out
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
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
2671  type(grid_type), intent(in) :: grid_out
2672  type(eq_2_type), intent(in) :: eq
2673  type(eq_2_type), intent(inout) :: eq_out
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 
2767  integer function calc_rzl_ind(grid_eq,eq,deriv) result(ierr)
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
2775  type(eq_2_type), intent(inout) :: eq
2776  integer, intent(in) :: deriv(3)
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
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
2810  type(eq_2_type), intent(inout) :: eq
2811  integer, intent(in) :: deriv(:,:)
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 
2826  integer function calc_g_c_ind(eq,deriv) result(ierr)
2828 
2829  character(*), parameter :: rout_name = 'calc_g_C_ind'
2830 
2831  ! input / output
2832  type(eq_2_type), intent(inout) :: eq
2833  integer, intent(in) :: deriv(:)
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
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
2862  integer, intent(in) :: deriv(:,:)
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 
2877  integer function calc_g_v_ind(eq,deriv) result(ierr)
2879 
2880  character(*), parameter :: rout_name = 'calc_g_V_ind'
2881 
2882  ! input / output
2883  type(eq_2_type), intent(inout) :: eq
2884  integer, intent(in) :: deriv(:)
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
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
2904  integer, intent(in) :: deriv(:,:)
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 
2919  integer function calc_g_h_ind(grid_eq,eq,deriv) result(ierr)
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
2930  type(eq_2_type), intent(inout) :: eq
2931  integer, intent(in) :: deriv(:)
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
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
3074  type(eq_2_type), intent(inout) :: eq
3075  integer, intent(in) :: deriv(:,:)
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 
3090  integer function calc_g_f_ind(eq,deriv) result(ierr)
3092 
3093  character(*), parameter :: rout_name = 'calc_g_F_ind'
3094 
3095  ! input / output
3096  type(eq_2_type), intent(inout) :: eq
3097  integer, intent(in) :: deriv(:)
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
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
3118  integer, intent(in) :: deriv(:,:)
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 
3133  integer function calc_jac_v_ind(grid,eq,deriv) result(ierr)
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
3141  type(eq_2_type), intent(inout) :: eq
3142  integer, intent(in) :: deriv(:)
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
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
3169  type(eq_2_type), intent(inout) :: eq
3170  integer, intent(in) :: deriv(:,:)
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 
3185  integer function calc_jac_h_ind(grid_eq,eq_1,eq_2,deriv) result(ierr)
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
3194  type(eq_1_type), intent(in) :: eq_1
3195  type(eq_2_type), intent(inout) :: eq_2
3196  integer, intent(in) :: deriv(:)
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
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
3286  type(eq_1_type), intent(in) :: eq_1
3287  type(eq_2_type), intent(inout) :: eq_2
3288  integer, intent(in) :: deriv(:,:)
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 
3303  integer function calc_jac_f_ind(eq,deriv) result(ierr)
3305 
3306  character(*), parameter :: rout_name = 'calc_jac_F_ind'
3307 
3308  ! input / output
3309  type(eq_2_type), intent(inout) :: eq
3310  integer, intent(in) :: deriv(:)
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
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
3335  integer, intent(in) :: deriv(:,:)
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 
3350  integer function calc_t_vc_ind(eq,deriv) result(ierr)
3352 
3353  character(*), parameter :: rout_name = 'calc_T_VC_ind'
3354 
3355  ! input / output
3356  type(eq_2_type), intent(inout) :: eq
3357  integer, intent(in) :: deriv(:)
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
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
3398  integer, intent(in) :: deriv(:,:)
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 
3413  integer function calc_t_vf_ind(grid_eq,eq_1,eq_2,deriv) result(ierr)
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
3421  type(eq_1_type), intent(in) :: eq_1
3422  type(eq_2_type), intent(inout) :: eq_2
3423  integer, intent(in) :: deriv(:)
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
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
3589  type(eq_1_type), intent(in) :: eq_1
3590  type(eq_2_type), intent(inout) :: eq_2
3591  integer, intent(in) :: deriv(:,:)
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 
3606  integer function calc_t_hf_ind(grid_eq,eq_1,eq_2,deriv) result(ierr)
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
3614  type(eq_1_type), intent(in) :: eq_1
3615  type(eq_2_type), intent(inout) :: eq_2
3616  integer, intent(in) :: deriv(:)
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
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
3784  type(eq_1_type), intent(in) :: eq_1
3785  type(eq_2_type), intent(inout) :: eq_2
3786  integer, intent(in) :: deriv(:,:)
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 
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
3819  type(eq_1_type), intent(in) :: eq
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
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
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 
4286  integer function calc_derived_q(grid_eq,eq_1,eq_2) result(ierr)
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
4296  type(eq_1_type), intent(in) :: eq_1
4297  type(eq_2_type), intent(inout), target :: eq_2
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
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
4445  real(dp), intent(out) :: s(:,:,:)
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 
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
4483  type(eq_2_type), intent(in), target :: eq_2
4484  integer :: bcs(2)
4485  real(dp) :: bcs_val(2)
4486  real(dp), intent(out) :: s(:,:,:)
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 
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
4525  type(eq_2_type), intent(in), target :: eq_2
4526  real(dp), intent(in) :: rchi(:,:,0:)
4527  real(dp), intent(in) :: zchi(:,:,0:)
4528  real(dp), intent(out) :: s(:,:,:)
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 
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(:,:,:,:,:,:)
4573  real(dp), intent(in) :: d_de(:,:,:,:,:)
4574  real(dp), intent(in) :: t_fe(:,:,:,:)
4575  real(dp), intent(out) :: d1_epar(:,:,:,:)
4576  real(dp), intent(out) :: d3_epar(:,:,:,:)
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 
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
4619  type(eq_2_type), intent(in) :: eq_2
4620  real(dp), intent(out) :: de(:,:,:,:,:,:)
4621  real(dp), intent(out) :: d_de(:,:,:,:,:)
4622  real(dp), intent(out) :: b_n(:,:,:,:)
4623  real(dp), intent(out) :: b_g(:,:,:,:)
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)
4716  use helena_vars, only: rbphi_h
4717 
4718  ! input / output
4719  type(grid_type), intent(in) :: grid_eq
4720  type(eq_1_type), intent(in) :: eq_1
4721  real(dp), intent(in) :: Rchi(:,:,0:)
4722  real(dp), intent(in) :: Zchi(:,:,0:)
4723  real(dp), intent(out) :: de(:,:,:,:,:,:)
4724  real(dp), intent(out) :: D_de(:,:,:,:,:)
4725  real(dp), intent(out) :: b_n(:,:,:,:)
4726  real(dp), intent(out) :: b_g(:,:,:,:)
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 
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
4803  type(eq_1_type), intent(in) :: eq_1
4804  type(eq_2_type), intent(in) :: eq_2
4805  real(dp), intent(in) :: b_n(:,:,:,:)
4806  real(dp), intent(in) :: b_g(:,:,:,:)
4807  real(dp), intent(out) :: kappa_n(:,:,:)
4808  real(dp), intent(out) :: kappa_g(:,:,:)
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 
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
4837  real(dp), intent(in) :: b_n(:,:,:,:)
4838  real(dp), intent(out) :: sigma(:,:,:)
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 
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
4865  type(eq_2_type), intent(in), target :: eq_2
4866  real(dp), intent(out) :: sigma(:,:,:)
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 
4893  integer function plot_derived_q(grid_eq,eq_2) result(ierr)
4894  use num_vars, only: prog_style, eq_jobs_lims, eq_job_nr, &
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
4904  type(eq_2_type), intent(in) :: eq_2
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 
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
5026  type(eq_1_type), intent(in) :: eq_1
5027  type(eq_2_type), intent(in) :: eq_2
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 
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
5106  type(eq_1_type), intent(in) :: eq_1
5107  type(eq_2_type), intent(in), target :: eq_2
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 
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
5200  type(eq_1_type), intent(in) :: eq_1
5201  type(eq_2_type), intent(in), target :: eq_2
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 
5291  subroutine test_s_hel(grid_eq,eq_2,Rchi,Zchi)
5292  ! input / output
5293  type(grid_type), intent(in) :: grid_eq
5294  type(eq_2_type), intent(in) :: eq_2
5295  real(dp), intent(in) :: Rchi(:,:,0:)
5296  real(dp), intent(in) :: Zchi(:,:,0:)
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 
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(:)
5330  real(dp), intent(in) :: A(:,:,:)
5331  real(dp), intent(in) :: B(:,:,:)
5332  character(len=*), intent(in) :: 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 
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,&
5450  &mu_0_original,t_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
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
5515  pres_0 = b_0**2/mu_0_original
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
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
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 
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 
5698  integer function b_plot(grid_eq,eq_1,eq_2,rich_lvl,plot_fluxes,XYZ) &
5699  &result(ierr)
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
5711  type(eq_1_type), intent(in) :: eq_1
5712  type(eq_2_type), intent(in) :: eq_2
5713  integer, intent(in), optional :: rich_lvl
5714  logical, intent(in), optional :: plot_fluxes
5715  real(dp), intent(in), optional :: xyz(:,:,:,:)
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 
5801  integer function j_plot(grid_eq,eq_1,eq_2,rich_lvl,plot_fluxes,XYZ) &
5802  &result(ierr)
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
5819  type(eq_1_type), intent(in) :: eq_1
5820  type(eq_2_type), intent(in) :: eq_2
5821  integer, intent(in), optional :: rich_lvl
5822  logical, intent(in), optional :: plot_fluxes
5823  real(dp), intent(in), optional :: xyz(:,:,:,:)
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
5919  if (use_normalization) j_v_sup_int = j_v_sup_int * pres_0/(r_0*b_0)
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 
5969  integer function kappa_plot(grid_eq,eq_1,eq_2,rich_lvl,XYZ) &
5970  &result(ierr)
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
5980  use num_vars, only: ltest, eq_jobs_lims, eq_job_nr
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
5989  type(eq_1_type), intent(in) :: eq_1
5990  type(eq_2_type), intent(in) :: eq_2
5991  integer, intent(in), optional :: rich_lvl
5992  real(dp), intent(in), optional :: xyz(:,:,:,:)
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 
6171  integer function delta_r_plot(grid_eq,eq_1,eq_2,XYZ,rich_lvl) &
6172  &result(ierr)
6176  use eq_utilities, only: calc_inv_met
6179  &tol_zero
6180  use eq_vars, only: b_0
6181  use num_utilities, only: c, calc_int, order_per_fun
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
6188  type(eq_1_type), intent(in) :: eq_1
6189  type(eq_2_type), intent(in) :: eq_2
6190  real(dp), intent(in) :: xyz(:,:,:,:)
6191  integer, intent(in), optional :: rich_lvl
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 
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)
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
6578  integer, intent(in) :: arr_size(2)
6579  integer, intent(inout) :: n_div
6580  integer, intent(in), optional :: n_div_max
6581  integer, intent(in), optional :: n_par_x_base
6582  character(len=*), intent(in), optional :: range_name
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 
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
6708  integer, intent(in) :: n_div
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 
6778  integer function test_t_ef(grid_eq,eq_1,eq_2) result(ierr)
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
6788  type(eq_1_type), intent(in) :: eq_1
6789  type(eq_2_type), intent(in) :: eq_2
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 
6952  integer function test_d12h_h(grid_eq,eq) result(ierr)
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
6962  type(eq_2_type), intent(in) :: eq
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 
7063  integer function test_jac_f(grid_eq,eq_1,eq_2) result(ierr)
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
7073  type(eq_1_type), intent(in), target :: eq_1
7074  type(eq_2_type), intent(in) :: eq_2
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 
7180  integer function test_g_v(grid_eq,eq) result(ierr)
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
7188  type(eq_2_type), intent(in) :: eq
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 
7279  integer function test_jac_v(grid_eq,eq) result(ierr)
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
7287  type(eq_2_type), intent(in) :: eq
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 
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
7354  type(eq_1_type), intent(in) :: eq_1
7355  type(eq_2_type), intent(in) :: eq_2
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 
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
7505  use num_vars, only: eq_style, norm_disc_prec_eq
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
7512  type(eq_1_type), intent(in) :: eq_1
7513  type(eq_2_type), intent(in) :: eq_2
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
7696 end module eq_ops
num_utilities::c
integer function, public c(ij, sym, n, lim_n)
Convert 2-D coordinates (i,j) to the storage convention used in matrices.
Definition: num_utilities.f90:2556
mpi_utilities::get_ser_var
Gather parallel variable in serial version on group master.
Definition: MPI_utilities.f90:55
eq_ops::calc_rzl
Calculate , & and derivatives in VMEC coordinates.
Definition: eq_ops.f90:126
input_utilities::get_real
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.
Definition: input_utilities.f90:77
num_utilities::gcd
recursive integer function, public gcd(u, v)
Returns least denominator using the GCD.
Definition: num_utilities.f90:2669
num_vars::ex_plot_style
integer, public ex_plot_style
external plot style (1: GNUPlot, 2: Bokeh for 2D, Mayavi for 3D)
Definition: num_vars.f90:175
output_ops::plot_diff_hdf5
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...
Definition: output_ops.f90:1765
num_utilities::calc_int
Integrates a function using the trapezoidal rule.
Definition: num_utilities.f90:160
hdf5_vars::dealloc_var_1d
Deallocates 1D variable.
Definition: HDF5_vars.f90:68
num_vars::mu_0_original
real(dp), parameter, public mu_0_original
permeability of free space
Definition: num_vars.f90:84
num_vars::hel_pert_i
integer, parameter, public hel_pert_i
file number of HELENA equilibrium perturbation file
Definition: num_vars.f90:191
vmec_ops
Operations that concern the output of VMEC.
Definition: VMEC_ops.f90:4
eq_ops::calc_t_vc
Calculate , the transformation matrix between C(ylindrical) and V(mec) coordinate system.
Definition: eq_ops.f90:245
helena_vars::rbphi_h
real(dp), dimension(:,:), allocatable, public rbphi_h
Definition: HELENA_vars.f90:29
eq_ops::divide_eq_jobs
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
num_vars::dp
integer, parameter, public dp
double precision
Definition: num_vars.f90:46
eq_ops::calc_eq_jobs_lims
integer function, public calc_eq_jobs_lims(n_par_X, n_div)
Calculate eq_jobs_lims.
Definition: eq_ops.f90:6701
eq_vars
Variables that have to do with equilibrium quantities and the grid used in the calculations:
Definition: eq_vars.f90:27
num_vars::use_normalization
logical, public use_normalization
whether to use normalization or not
Definition: num_vars.f90:115
eq_vars::vac_perm
real(dp), public vac_perm
either usual mu_0 (default) or normalized
Definition: eq_vars.f90:48
mpi_utilities
Numerical utilities related to MPI.
Definition: MPI_utilities.f90:20
eq_ops::test_g_v
integer function test_g_v(grid_eq, eq)
Tests whether is calculated correctly.
Definition: eq_ops.f90:7181
num_vars
Numerical variables used by most other modules.
Definition: num_vars.f90:4
input_utilities::get_log
logical function, public get_log(yes, ind)
Queries for a logical value yes or no, where the default answer is also to be provided.
Definition: input_utilities.f90:22
files_utilities::skip_comment
integer function, public skip_comment(file_i, file_name)
Skips comment when reading a file.
Definition: files_utilities.f90:55
eq_ops::delta_r_plot
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
num_vars::max_str_ln
integer, parameter, public max_str_ln
maximum length of strings
Definition: num_vars.f90:50
eq_ops::test_d12h_h
integer function test_d12h_h(grid_eq, eq)
Tests whether is calculated correctly.
Definition: eq_ops.f90:6953
eq_utilities::calc_g
integer function, public calc_g(g_A, T_BA, g_B, deriv, max_deriv)
Calculate the metric coefficients in a coordinate system B ! using the.
Definition: eq_utilities.f90:387
helena_vars::rot_t_h
real(dp), dimension(:,:), allocatable, public rot_t_h
rotational transform
Definition: HELENA_vars.f90:28
eq_ops::calc_g_f
Calculate the metric coefficients in the F(lux) coordinate system.
Definition: eq_ops.f90:183
rich_vars
Variables concerning Richardson extrapolation.
Definition: rich_vars.f90:4
num_vars::norm_disc_prec_eq
integer, public norm_disc_prec_eq
precision for normal discretization for equilibrium
Definition: num_vars.f90:120
helena_vars::q_saf_h
real(dp), dimension(:,:), allocatable, public q_saf_h
safety factor
Definition: HELENA_vars.f90:27
str_utilities::i2str
elemental character(len=max_str_ln) function, public i2str(k)
Convert an integer to string.
Definition: str_utilities.f90:18
hdf5_ops
Operations on HDF5 and XDMF variables.
Definition: HDF5_ops.f90:27
eq_ops::calc_eq
Calculate the equilibrium quantities on a grid determined by straight field lines.
Definition: eq_ops.f90:48
eq_ops::normalize_input
subroutine, public normalize_input()
Normalize input quantities.
Definition: eq_ops.f90:5643
eq_ops::calc_jac_h
Calculate , the jacobian in HELENA coordinates.
Definition: eq_ops.f90:213
helena_vars::flux_p_h
real(dp), dimension(:,:), allocatable, public flux_p_h
poloidal flux
Definition: HELENA_vars.f90:24
vmec_vars::b_v_c
real(dp), dimension(:,:), allocatable, public b_v_c
Coeff. of magnitude of B in sine series (HM and FM)
Definition: VMEC_vars.f90:50
x_vars::min_r_sol
real(dp), public min_r_sol
min. normal range for pert.
Definition: X_vars.f90:135
vmec_vars::j_v_sup_int
real(dp), dimension(:,:), allocatable, public j_v_sup_int
Integrated poloidal and toroidal current (FM)
Definition: VMEC_vars.f90:52
eq_ops::debug_create_vmec_input
logical, public debug_create_vmec_input
plot debug information for create_vmec_input()
Definition: eq_ops.f90:34
eq_utilities::calc_inv_met
Calculate from and where and , according to .
Definition: eq_utilities.f90:61
eq_ops::test_jac_f
integer function test_jac_f(grid_eq, eq_1, eq_2)
Performs tests on .
Definition: eq_ops.f90:7064
num_vars::eq_job_nr
integer, public eq_job_nr
nr. of eq job
Definition: num_vars.f90:79
eq_vars::t_0
real(dp), public t_0
derived normalization constant for nondimensionalization
Definition: eq_vars.f90:47
eq_ops::b_plot
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
vmec_utilities::calc_trigon_factors
integer function, public calc_trigon_factors(theta, zeta, trigon_factors)
Calculate the trigonometric cosine and sine factors.
Definition: VMEC_utilities.f90:275
num_utilities::order_per_fun
Order a periodic function to include and an overlap.
Definition: num_utilities.f90:248
eq_ops::test_p
integer function test_p(grid_eq, eq_1, eq_2)
Performs tests on pressure balance.
Definition: eq_ops.f90:7502
num_vars::n_procs
integer, public n_procs
nr. of MPI processes
Definition: num_vars.f90:69
hdf5_vars
Variables pertaining to HDF5 and XDMF.
Definition: HDF5_vars.f90:4
grid_vars::n_r_eq
integer, public n_r_eq
nr. of normal points in equilibrium grid
Definition: grid_vars.f90:20
vmec_vars::jac_v_s
real(dp), dimension(:,:,:), allocatable, public jac_v_s
Coeff. of in cosine series (HM and FM) and norm. deriv.
Definition: VMEC_vars.f90:46
output_ops::print_ex_2d
Print 2-D output on a file.
Definition: output_ops.f90:47
str_utilities
Operations on strings.
Definition: str_utilities.f90:4
vmec_vars::b_v_sub_s
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
vmec_vars::flux_p_v
real(dp), dimension(:,:), allocatable, public flux_p_v
poloidal flux
Definition: VMEC_vars.f90:35
eq_ops::debug_j_plot
logical, public debug_j_plot
plot debug information for j_plot()
Definition: eq_ops.f90:32
eq_ops::redistribute_output_eq
Redistribute the equilibrium variables, but only the Flux variables are saved.
Definition: eq_ops.f90:103
eq_ops::calc_t_vf
Calculate , the transformation matrix between V(MEC) and F(lux) coordinate systems.
Definition: eq_ops.f90:258
grid_vars::grid_type
Type for grids.
Definition: grid_vars.f90:59
eq_vars::max_flux_e
real(dp), public max_flux_e
max. flux in Equilibrium coordinates, set in calc_norm_range_PB3D_in
Definition: eq_vars.f90:49
str_utilities::r2strt
elemental character(len=max_str_ln) function, public r2strt(k)
Convert a real (double) to string.
Definition: str_utilities.f90:54
num_vars::prog_style
integer, public prog_style
program style (1: PB3D, 2: PB3D_POST)
Definition: num_vars.f90:53
eq_vars::r_0
real(dp), public r_0
independent normalization constant for nondimensionalization
Definition: eq_vars.f90:42
eq_vars::eq_1_type
flux equilibrium type
Definition: eq_vars.f90:63
eq_utilities
Numerical utilities related to equilibrium variables.
Definition: eq_utilities.f90:4
eq_ops::j_plot
integer function, public j_plot(grid_eq, eq_1, eq_2, rich_lvl, plot_fluxes, XYZ)
Plots the current.
Definition: eq_ops.f90:5803
num_vars::ltest
logical, public ltest
whether or not to call the testing routines
Definition: num_vars.f90:112
calc_derived_de_epar_hel
subroutine calc_derived_de_epar_hel(grid_eq, eq_1, Rchi, Zchi, de, D_de, b_n, b_g)
Definition: eq_ops.f90:4715
eq_ops::test_b_f
integer function test_b_f(grid_eq, eq_1, eq_2)
Tests whether is calculated correctly.
Definition: eq_ops.f90:7344
grid_vars::alpha
real(dp), dimension(:), allocatable, public alpha
field line label alpha
Definition: grid_vars.f90:28
eq_ops::calc_g_c
Calculate the lower metric elements in the C(ylindrical) coordinate system.
Definition: eq_ops.f90:139
eq_ops::debug_calc_derived_q
logical, public debug_calc_derived_q
plot debug information for calc_derived_q()
Definition: eq_ops.f90:30
helena_vars::h_h_33
real(dp), dimension(:,:), allocatable, public h_h_33
upper metric factor (1 / gem12)
Definition: HELENA_vars.f90:32
helena_vars::r_h
real(dp), dimension(:,:), allocatable, public r_h
major radius (xout)
Definition: HELENA_vars.f90:33
eq_ops::calc_t_hf
Calculate , the transformation matrix between H(ELENA) and F(lux) coordinate systems.
Definition: eq_ops.f90:271
eq_ops::calc_normalization_const
integer function, public calc_normalization_const()
Sets up normalization constants.
Definition: eq_ops.f90:5405
helena_vars::flux_t_h
real(dp), dimension(:,:), allocatable, public flux_t_h
toroidal flux
Definition: HELENA_vars.f90:25
hdf5_vars::max_dim_var_1d
integer, parameter, public max_dim_var_1d
maximum dimension of var_1D
Definition: HDF5_vars.f90:21
helena_vars::h_h_12
real(dp), dimension(:,:), allocatable, public h_h_12
upper metric factor (gem12)
Definition: HELENA_vars.f90:31
vmec_vars::b_v_sub_c
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
helena_ops::test_harm_cont_h
integer function, public test_harm_cont_h()
Investaige harmonic content of the HELENA variables.
Definition: HELENA_ops.f90:1485
x_vars::max_r_sol
real(dp), public max_r_sol
max. normal range for pert.
Definition: X_vars.f90:136
eq_ops::test_t_ef
integer function test_t_ef(grid_eq, eq_1, eq_2)
See if T_EF it complies with the theory of .
Definition: eq_ops.f90:6779
num_vars::rich_restart_lvl
integer, public rich_restart_lvl
starting Richardson level (0: none [default])
Definition: num_vars.f90:173
helena_vars::z_h
real(dp), dimension(:,:), allocatable, public z_h
height (yout)
Definition: HELENA_vars.f90:34
grid_utilities::extend_grid_f
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.
Definition: grid_utilities.f90:1096
hdf5_vars::var_1d_type
1D equivalent of multidimensional variables, used for internal HDF5 storage.
Definition: HDF5_vars.f90:48
eq_ops::print_output_eq
Print equilibrium quantities to an output file:
Definition: eq_ops.f90:90
num_vars::export_hel
logical, public export_hel
export HELENA
Definition: num_vars.f90:142
eq_utilities::calc_f_derivs
Transforms derivatives of the equilibrium quantities in E coordinates to derivatives in the F coordin...
Definition: eq_utilities.f90:36
num_utilities::spline
Wrapper to the pspline library, making it easier to use for 1-D applications where speed is not the m...
Definition: num_utilities.f90:276
output_ops::draw_ex
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.
Definition: output_ops.f90:1079
num_utilities::shift_f
subroutine, public shift_f(Al, Bl, Cl, A, B, C)
Calculate multiplication through shifting of fourier modes A and B into C.
Definition: num_utilities.f90:2696
num_vars::rz_0
real(dp), dimension(2), public rz_0
origin of geometrical poloidal coordinate
Definition: num_vars.f90:179
num_utilities::derivs
integer function, dimension(:,:), allocatable, public derivs(order, dims)
Returns derivatives of certain order.
Definition: num_utilities.f90:2246
helena_vars::pres_h
real(dp), dimension(:,:), allocatable, public pres_h
pressure profile
Definition: HELENA_vars.f90:26
output_ops::plot_hdf5
Prints variables vars with names var_names in an HDF5 file with name c file_name and accompanying XDM...
Definition: output_ops.f90:137
vmec_vars::flux_t_v
real(dp), dimension(:,:), allocatable, public flux_t_v
toroidal flux
Definition: VMEC_vars.f90:34
vmec_utilities
Numerical utilities related to the output of VMEC.
Definition: VMEC_utilities.f90:4
num_vars::eq_style
integer, public eq_style
either 1 (VMEC) or 2 (HELENA)
Definition: num_vars.f90:89
hdf5_ops::print_hdf5_arrs
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.
Definition: HDF5_ops.f90:1132
vmec_vars::pres_v
real(dp), dimension(:,:), allocatable, public pres_v
pressure
Definition: VMEC_vars.f90:36
num_vars::rho_style
integer, public rho_style
style for equilibrium density profile
Definition: num_vars.f90:90
x_vars
Variables pertaining to the perturbation quantities.
Definition: X_vars.f90:4
eq_utilities::calc_memory_eq
integer function, public calc_memory_eq(arr_size, n_par, mem_size)
Calculate memory in MB necessary for variables in equilibrium job.
Definition: eq_utilities.f90:907
num_vars::use_pol_flux_e
logical, public use_pol_flux_e
whether poloidal flux is used in E coords.
Definition: num_vars.f90:113
eq_vars::max_flux_f
real(dp), public max_flux_f
max. flux in Flux coordinates, set in calc_norm_range_PB3D_in
Definition: eq_vars.f90:50
num_utilities::check_deriv
integer function, public check_deriv(deriv, max_deriv, sr_name)
checks whether the derivatives requested for a certain subroutine are valid
Definition: num_utilities.f90:2260
helena_ops
Operations on HELENA variables.
Definition: HELENA_ops.f90:4
x_utilities
Numerical utilities related to perturbation operations.
Definition: X_utilities.f90:4
num_vars::tol_zero
real(dp), public tol_zero
tolerance for zeros
Definition: num_vars.f90:133
eq_ops::calc_jac_f
Calculate , the jacobian in Flux coordinates.
Definition: eq_ops.f90:232
num_vars::norm_style
integer, public norm_style
style for normalization
Definition: num_vars.f90:92
helena_vars::h_h_11
real(dp), dimension(:,:), allocatable, public h_h_11
upper metric factor (gem11)
Definition: HELENA_vars.f90:30
eq_vars::rho_0
real(dp), public rho_0
independent normalization constant for nondimensionalization
Definition: eq_vars.f90:44
num_vars::magn_int_style
integer, public magn_int_style
style for magnetic integrals (1: trapezoidal, 2: Simpson 3/8)
Definition: num_vars.f90:124
num_vars::max_deriv
integer, parameter, public max_deriv
highest derivatives for metric factors in Flux coords.
Definition: num_vars.f90:52
num_utilities
Numerical utilities.
Definition: num_utilities.f90:4
vmec_vars::rot_t_v
real(dp), dimension(:,:), allocatable, public rot_t_v
rotational transform
Definition: VMEC_vars.f90:37
vmec_vars::b_v_s
real(dp), dimension(:,:), allocatable, public b_v_s
Coeff. of magnitude of B in cosine series (HM and FM)
Definition: VMEC_vars.f90:51
str_utilities::r2str
elemental character(len=max_str_ln) function, public r2str(k)
Convert a real (double) to string.
Definition: str_utilities.f90:42
messages::writo
subroutine, public writo(input_str, persistent, error, warning, alert)
Write output to file identified by output_i.
Definition: messages.f90:275
messages
Numerical utilities related to giving output.
Definition: messages.f90:4
vmec_vars::z_v_s
real(dp), dimension(:,:,:), allocatable, public z_v_s
Coeff. of in cosine series (FM) and norm. deriv.
Definition: VMEC_vars.f90:42
helena_vars
Variables that have to do with HELENA quantities.
Definition: HELENA_vars.f90:4
num_vars::pi
real(dp), parameter, public pi
Definition: num_vars.f90:83
grid_utilities
Numerical utilities related to the grids and different coordinate systems.
Definition: grid_utilities.f90:4
files_utilities::count_lines
integer function, public count_lines(file_i)
Count non-comment lines in a file.
Definition: files_utilities.f90:170
vmec_vars::q_saf_v
real(dp), dimension(:,:), allocatable, public q_saf_v
safety factor
Definition: VMEC_vars.f90:38
num_vars::mem_scale_fac
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
eq_ops::calc_g_h
Calculate the lower metric coefficients in the equilibrium H(ELENA) coordinate system.
Definition: eq_ops.f90:169
helena_vars::bmtog_h
real(dp), public bmtog_h
B_geo/B_mag.
Definition: HELENA_vars.f90:22
eq_ops::kappa_plot
integer function, public kappa_plot(grid_eq, eq_1, eq_2, rich_lvl, XYZ)
Plots the curvature.
Definition: eq_ops.f90:5971
num_vars::pb3d_name
character(len=max_str_ln), public pb3d_name
name of PB3D output file
Definition: num_vars.f90:139
vmec_vars::r_v_c
real(dp), dimension(:,:,:), allocatable, public r_v_c
Coeff. of in sine series (FM) and norm. deriv.
Definition: VMEC_vars.f90:39
eq_ops::test_jac_v
integer function test_jac_v(grid_eq, eq)
Tests whether is calculated correctly.
Definition: eq_ops.f90:7280
eq_ops::create_vmec_input
integer function create_vmec_input(grid_eq, eq_1)
Creates a VMEC input file.
Definition: eq_ops.f90:784
grid_vars
Variables pertaining to the different grids used.
Definition: grid_vars.f90:4
grid_utilities::calc_vec_comp
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.
Definition: grid_utilities.f90:1859
helena_vars::ias
integer, public ias
0 if top-bottom symmetric, 1 if not
Definition: HELENA_vars.f90:36
eq_ops::calc_g_v
Calculate the lower metric coefficients in the equilibrium V(MEC) coordinate system.
Definition: eq_ops.f90:156
messages::lvl_ud
subroutine, public lvl_ud(inc)
Increases/decreases lvl of output.
Definition: messages.f90:254
input_utilities::get_int
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.
Definition: input_utilities.f90:152
vmec_ops::normalize_vmec
subroutine, public normalize_vmec
Normalizes VMEC input.
Definition: VMEC_ops.f90:328
input_utilities::pause_prog
subroutine, public pause_prog(ind)
Pauses the running of the program.
Definition: input_utilities.f90:226
output_ops::print_ex_3d
Print 3-D output on a file.
Definition: output_ops.f90:65
num_vars::no_plots
logical, public no_plots
no plots made
Definition: num_vars.f90:140
x_vars::n_mod_x
integer, public n_mod_x
size of m_X (pol. flux) or n_X (tor. flux)
Definition: X_vars.f90:129
vmec_vars
Variables that concern the output of VMEC.
Definition: VMEC_vars.f90:4
vmec_vars::l_v_c
real(dp), dimension(:,:,:), allocatable, public l_v_c
Coeff. of in sine series (HM) and norm. deriv.
Definition: VMEC_vars.f90:43
num_vars::use_pol_flux_f
logical, public use_pol_flux_f
whether poloidal flux is used in F coords.
Definition: num_vars.f90:114
x_utilities::calc_memory_x
integer function, public calc_memory_x(ord, arr_size, n_mod, mem_size)
Calculate memory in MB necessary for X variables.
Definition: X_utilities.f90:236
helena_ops::test_metrics_h
integer function, public test_metrics_h()
Checks whether the metric elements provided by HELENA are consistent with a direct calculation using ...
Definition: HELENA_ops.f90:1289
eq_vars::psi_0
real(dp), public psi_0
derived normalization constant for nondimensionalization
Definition: eq_vars.f90:46
vmec_vars::r_v_s
real(dp), dimension(:,:,:), allocatable, public r_v_s
Coeff. of in cosine series (FM) and norm. deriv.
Definition: VMEC_vars.f90:40
rich_vars::rich_lvl
integer, public rich_lvl
current level of Richardson extrapolation
Definition: rich_vars.f90:19
vmec_vars::jac_v_c
real(dp), dimension(:,:,:), allocatable, public jac_v_c
Coeff. of in sine series (HM and FM) and norm. deriv.
Definition: VMEC_vars.f90:45
num_vars::eq_jobs_lims
integer, dimension(:,:), allocatable, public eq_jobs_lims
data about eq jobs: [ , ] for all jobs
Definition: num_vars.f90:77
helena_vars::chi_h
real(dp), dimension(:), allocatable, public chi_h
poloidal angle
Definition: HELENA_vars.f90:23
output_ops
Operations concerning giving output, on the screen as well as in output files.
Definition: output_ops.f90:5
eq_vars::pres_0
real(dp), public pres_0
independent normalization constant for nondimensionalization
Definition: eq_vars.f90:43
vmec_vars::b_0_v
real(dp), public b_0_v
the magnitude of B at the magnetic axis,
Definition: VMEC_vars.f90:33
grid_utilities::calc_tor_diff
Calculates the toroidal difference for a magnitude calculated on three toroidal points: two extremiti...
Definition: grid_utilities.f90:109
num_vars::rank
integer, public rank
MPI rank.
Definition: num_vars.f90:68
eq_ops::flux_q_plot
integer function, public flux_q_plot(grid_eq, eq)
Plots the flux quantities in the solution grid.
Definition: eq_ops.f90:3810
grid_utilities::nufft
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.
Definition: grid_utilities.f90:2901
num_vars::prop_b_tor_i
integer, parameter, public prop_b_tor_i
file number of proportionality factor file
Definition: num_vars.f90:192
num_utilities::bubble_sort
Sorting with the bubble sort routine.
Definition: num_utilities.f90:237
num_vars::max_tot_mem
real(dp), public max_tot_mem
maximum total memory for all processes [MB]
Definition: num_vars.f90:74
input_utilities
Numerical utilities related to input.
Definition: input_utilities.f90:4
files_utilities
Numerical utilities related to files.
Definition: files_utilities.f90:4
vmec_utilities::fourier2real
Inverse Fourier transformation, from VMEC.
Definition: VMEC_utilities.f90:56
num_vars::eq_name
character(len=max_str_ln), public eq_name
name of equilibrium file from VMEC or HELENA
Definition: num_vars.f90:138
num_vars::hel_export_i
integer, parameter, public hel_export_i
file number of output of HELENA equilibrium export file
Definition: num_vars.f90:190
vmec_vars::l_v_s
real(dp), dimension(:,:,:), allocatable, public l_v_s
Coeff. of in cosine series (HM) and norm. deriv.
Definition: VMEC_vars.f90:44
num_utilities::add_arr_mult
Add to an array (3) the product of arrays (1) and (2).
Definition: num_utilities.f90:39
eq_ops::calc_jac_v
Calculate , the jacobian in V(MEC) coordinates.
Definition: eq_ops.f90:196
num_vars::max_x_mem
real(dp), public max_x_mem
maximum memory for perturbation calculations for all processes [MB]
Definition: num_vars.f90:75
eq_vars::eq_2_type
metric equilibrium type
Definition: eq_vars.f90:114
helena_vars::nchi
integer, public nchi
nr. of poloidal points
Definition: HELENA_vars.f90:35
eq_ops::calc_derived_q
integer function, public calc_derived_q(grid_eq, eq_1, eq_2)
Calculates derived equilibrium quantities system.
Definition: eq_ops.f90:4287
vmec_vars::z_v_c
real(dp), dimension(:,:,:), allocatable, public z_v_c
Coeff. of in sine series (FM) and norm. deriv.
Definition: VMEC_vars.f90:41
helena_vars::rmtog_h
real(dp), public rmtog_h
R_geo/R_mag.
Definition: HELENA_vars.f90:21
eq_ops
Operations on the equilibrium variables.
Definition: eq_ops.f90:4
num_utilities::calc_det
Calculate determinant of a matrix.
Definition: num_utilities.f90:63
grid_utilities::calc_xyz_grid
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.
Definition: grid_utilities.f90:799
mpi_utilities::redistribute_var
integer function, public redistribute_var(var, dis_var, lims, lims_dis)
Redistribute variables according to new limits.
Definition: MPI_utilities.f90:330
grid_utilities::trim_grid
integer function, public trim_grid(grid_in, grid_out, norm_id)
Trim a grid, removing any overlap between the different regions.
Definition: grid_utilities.f90:1636
eq_vars::b_0
real(dp), public b_0
derived normalization constant for nondimensionalization
Definition: eq_vars.f90:45