From c3e24483baac8eb5aa0bad90de620e3f03d97ab4 Mon Sep 17 00:00:00 2001
From: Antoine Hoffmann <antoine.hoffmann@epfl.ch>
Date: Fri, 14 Oct 2022 16:59:14 +0200
Subject: [PATCH] Miller geometry is implemented, not benchmarked yet

---
 src/geometry_mod.F90                       |  45 +-
 src/{lag_interp.F90 => lag_interp_mod.F90} |  60 +-
 src/miller_geometry.F90                    | 589 -------------------
 src/miller_mod.F90                         | 645 +++++++++++++++++++++
 4 files changed, 710 insertions(+), 629 deletions(-)
 rename src/{lag_interp.F90 => lag_interp_mod.F90} (78%)
 delete mode 100644 src/miller_geometry.F90
 create mode 100644 src/miller_mod.F90

diff --git a/src/geometry_mod.F90 b/src/geometry_mod.F90
index 665f18ba..9840cc0b 100644
--- a/src/geometry_mod.F90
+++ b/src/geometry_mod.F90
@@ -9,17 +9,34 @@ module geometry
   use fields
   use basic
   use calculus, ONLY: simpson_rule_z
-
+  use miller, ONLY: set_miller_parameters, get_miller
 implicit none
   PRIVATE
-  ! Geometry parameters
+  ! Geometry input parameters
   CHARACTER(len=16), &
                PUBLIC, PROTECTED :: geom
-  REAL(dp),    PUBLIC, PROTECTED :: q0       = 1.4_dp  ! safety factor
-  REAL(dp),    PUBLIC, PROTECTED :: shear    = 0._dp   ! magnetic field shear
-  REAL(dp),    PUBLIC, PROTECTED :: eps      = 0.18_dp ! inverse aspect ratio
-  LOGICAL,     PUBLIC, PROTECTED :: SHEARED  = .false.     ! flag for shear magn. geom or not
-  ! Geometrical operators
+  REAL(dp),    PUBLIC, PROTECTED :: q0        = 1.4_dp  ! safety factor
+  REAL(dp),    PUBLIC, PROTECTED :: shear     = 0._dp   ! magnetic field shear
+  REAL(dp),    PUBLIC, PROTECTED :: eps       = 0.18_dp ! inverse aspect ratio
+  REAL(dp),    PUBLIC, PROTECTED :: alpha_MHD = 0 ! shafranov shift effect alpha = -q2 R dbeta/dr
+  ! parameters for Miller geometry
+  REAL(dp),    PUBLIC, PROTECTED :: kappa     = 1._dp ! elongation
+  REAL(dp),    PUBLIC, PROTECTED :: s_kappa   = 0._dp ! r normalized derivative skappa = r/kappa dkappa/dr
+  REAL(dp),    PUBLIC, PROTECTED :: delta     = 0._dp ! triangularity
+  REAL(dp),    PUBLIC, PROTECTED :: s_delta   = 0._dp ! '' sdelta = r/sqrt(1-delta2) ddelta/dr
+  REAL(dp),    PUBLIC, PROTECTED :: zeta      = 0._dp ! squareness
+  REAL(dp),    PUBLIC, PROTECTED :: s_zeta    = 0._dp ! '' szeta = r dzeta/dr
+
+  ! GENE unused additional parameters for miller_mod
+  REAL(dp), PUBLIC, PROTECTED :: edge_opt      = 0 ! meant to redistribute the points in z
+  REAL(dp), PUBLIC, PROTECTED :: major_R       = 1 ! major radius
+  REAL(dp), PUBLIC, PROTECTED :: major_Z       = 0 ! vertical elevation
+  REAL(dp), PUBLIC, PROTECTED :: dpdx_pm_geom  = 0 ! amplitude mag. eq. pressure grad.
+  REAL(dp), PUBLIC, PROTECTED ::          C_y  = 0 ! defines y coordinate : Cy (q theta - phi)
+  REAL(dp), PUBLIC, PROTECTED ::         C_xy  = 0 ! defines x coordinate : B = Cxy Vx x Vy
+
+  ! Geometrical auxiliary variables
+  LOGICAL,     PUBLIC, PROTECTED :: SHEARED  = .false. ! flag for shear magn. geom or not
   ! Curvature
   REAL(dp),    PUBLIC, DIMENSION(:,:,:,:), ALLOCATABLE :: Ckxky  ! dimensions: kx, ky, z, odd/even p
   ! Jacobian
@@ -38,6 +55,7 @@ implicit none
   REAL(dp),    PUBLIC, DIMENSION(:,:) , ALLOCATABLE :: gradz_coeff  ! 1 / [ J_{xyz} \hat{B} ]
   ! Array to map the index of mode (kx,ky,-pi) to (kx+2pi*s*ky,ky,pi) for sheared periodic boundary condition
   INTEGER,     PUBLIC, DIMENSION(:,:), ALLOCATABLE :: ikx_zBC_L, ikx_zBC_R
+
   ! Functions
   PUBLIC :: geometry_readinputs, geometry_outputinputs,&
             eval_magnetic_geometry, set_ikx_zBC_map
@@ -47,7 +65,8 @@ CONTAINS
   SUBROUTINE geometry_readinputs
     ! Read the input parameters
     IMPLICIT NONE
-    NAMELIST /GEOMETRY/ geom, q0, shear, eps
+    NAMELIST /GEOMETRY/ geom, q0, shear, eps,&
+      kappa, s_kappa,delta, s_delta, zeta, s_zeta ! For miller
     READ(lu_in,geometry)
     IF(shear .NE. 0._dp) SHEARED = .true.
 
@@ -78,8 +97,14 @@ CONTAINS
           IF( my_id .eq. 0 ) WRITE(*,*) 'Z-pinch geometry'
           call eval_zpinch_geometry
           SHEARED = .FALSE.
+        CASE('miller')
+          IF( my_id .eq. 0 ) WRITE(*,*) 'Miller geometry'
+          call set_miller_parameters(kappa,s_kappa,delta,s_delta,zeta,s_zeta)
+          call get_miller(eps,major_R,major_Z,q0,shear,alpha_MHD,edge_opt,&
+               C_y,C_xy,dpdx_pm_geom,gxx,gyy,gzz,gxy,gxz,gyz,&
+               gradxB,gradyB,hatB,jacobian,gradzB,hatR,hatZ,dxdR,dxdZ)
         CASE DEFAULT
-          ERROR STOP 'Error stop: geometry not recognized!!'
+          STOP 'geometry not recognized!!'
         END SELECT
     ENDIF
     !
@@ -116,7 +141,7 @@ CONTAINS
   SUBROUTINE eval_salphaB_geometry
   ! evaluate s-alpha geometry model
   implicit none
-  REAL(dp) :: z, kx, ky, alpha_MHD, Gx, Gy
+  REAL(dp) :: z, kx, ky, Gx, Gy
   alpha_MHD = 0._dp
 
   parity: DO eo = 0,1
diff --git a/src/lag_interp.F90 b/src/lag_interp_mod.F90
similarity index 78%
rename from src/lag_interp.F90
rename to src/lag_interp_mod.F90
index db2afc48..8415df5d 100644
--- a/src/lag_interp.F90
+++ b/src/lag_interp_mod.F90
@@ -1,8 +1,8 @@
-#include "redef.h"
 !! This source is taken from GENE https://genecode.org/ !!
 !>lagrange_interpolation contains subroutines to perform
 !!a mid-point lagrange interpolation of order 3
 MODULE lagrange_interpolation
+  USE prec_const
   IMPLICIT NONE
   PUBLIC:: lag3interp, lag3deriv, lag3interp_2d
   PUBLIC:: lag3interp_complex
@@ -21,11 +21,11 @@ CONTAINS
   !> Third order lagrange interpolation
   SUBROUTINE lag3interp_scalar(y_in,x_in,n_in,y_out,x_out)
     INTEGER, INTENT(IN) :: n_in
-    REAL, DIMENSION(n_in), INTENT(IN) :: y_in,x_in
-    REAL, INTENT(IN) :: x_out
-    REAL, INTENT(OUT) :: y_out
+    REAL(dp), DIMENSION(n_in), INTENT(IN) :: y_in,x_in
+    REAL(dp), INTENT(IN) :: x_out
+    REAL(dp), INTENT(OUT) :: y_out
 
-    REAL, DIMENSION(1) :: xout_wrap, yout_wrap
+    REAL(dp), DIMENSION(1) :: xout_wrap, yout_wrap
 
     xout_wrap = x_out
     call lag3interp_array(y_in,x_in,n_in,yout_wrap,xout_wrap,1)
@@ -36,11 +36,11 @@ CONTAINS
   !> Third order lagrange interpolation
   subroutine lag3interp_array(y_in,x_in,n_in,y_out,x_out,n_out)
     INTEGER, INTENT(IN) :: n_in,n_out
-    REAL, DIMENSION(n_in), INTENT(IN) :: y_in,x_in
-    REAL, DIMENSION(n_out), INTENT(IN) :: x_out
-    REAL, DIMENSION(n_out), INTENT(OUT) :: y_out
+    REAL(dp), DIMENSION(n_in), INTENT(IN) :: y_in,x_in
+    REAL(dp), DIMENSION(n_out), INTENT(IN) :: x_out
+    REAL(dp), DIMENSION(n_out), INTENT(OUT) :: y_out
 
-    REAL :: x,aintm,aint0,aint1,aint2,xm,x0,x1,x2
+    REAL(dp) :: x,aintm,aint0,aint1,aint2,xm,x0,x1,x2
     INTEGER :: j,jm,j0,j1,j2
     INTEGER :: jstart,jfirst,jlast,jstep
 
@@ -91,11 +91,11 @@ CONTAINS
   SUBROUTINE lag3interp_complex(y_in,x_in,n_in,y_out,x_out,n_out)
     INTEGER, INTENT(IN) :: n_in,n_out
     COMPLEX, DIMENSION(n_in), INTENT(IN) :: y_in
-    REAL, DIMENSION(n_in), INTENT(IN) :: x_in
+    REAL(dp), DIMENSION(n_in), INTENT(IN) :: x_in
     COMPLEX, DIMENSION(n_out), INTENT(OUT) :: y_out
-    REAL, DIMENSION(n_out), INTENT(IN) :: x_out
+    REAL(dp), DIMENSION(n_out), INTENT(IN) :: x_out
 
-    REAL :: x,aintm,aint0,aint1,aint2,xm,x0,x1,x2
+    REAL(dp) :: x,aintm,aint0,aint1,aint2,xm,x0,x1,x2
     INTEGER :: j,jm,j0,j1,j2
     INTEGER :: jstart,jfirst,jlast,jstep
 
@@ -143,22 +143,22 @@ CONTAINS
 
 
   !>2D interpolation
-  !\TODO check whether a "real" 2D interpolation would
+  !\TODO check whether a "REAL(dp)" 2D interpolation would
   !! be more appropriate
   SUBROUTINE lag3interp_2d(y_in,x1_in,n1_in,x2_in,n2_in,&
        &y_out,x1_out,n1_out,x2_out,n2_out)
     INTEGER, INTENT(IN) :: n1_in,n2_in,n1_out,n2_out
-    REAL, DIMENSION(n1_in,n2_in), INTENT(IN) :: y_in
-    REAL, DIMENSION(n1_in) :: x1_in
-    REAL, DIMENSION(n2_in) :: x2_in
-    REAL, DIMENSION(n1_out), INTENT(IN) :: x1_out
-    REAL, DIMENSION(n2_out), INTENT(IN) :: x2_out
-    REAL, DIMENSION(n1_out,n2_out), INTENT(OUT) :: y_out
+    REAL(dp), DIMENSION(n1_in,n2_in), INTENT(IN) :: y_in
+    REAL(dp), DIMENSION(n1_in) :: x1_in
+    REAL(dp), DIMENSION(n2_in) :: x2_in
+    REAL(dp), DIMENSION(n1_out), INTENT(IN) :: x1_out
+    REAL(dp), DIMENSION(n2_out), INTENT(IN) :: x2_out
+    REAL(dp), DIMENSION(n1_out,n2_out), INTENT(OUT) :: y_out
 
     !local variables
-    REAL, DIMENSION(n2_in) :: y2_in_tmp
-    REAL, DIMENSION(n2_out) :: y2_out_tmp
-    REAL, DIMENSION(n1_in,n2_out) :: y_tmp
+    REAL(dp), DIMENSION(n2_in) :: y2_in_tmp
+    REAL(dp), DIMENSION(n2_out) :: y2_out_tmp
+    REAL(dp), DIMENSION(n1_in,n2_out) :: y_tmp
     INTEGER :: i
 
     DO i=1,n1_in
@@ -181,11 +181,11 @@ CONTAINS
     IMPLICIT NONE
 
     INTEGER, INTENT(IN) :: n_in
-    REAL, DIMENSION(n_in), INTENT(IN) :: y_in,x_in
-    REAL, INTENT(IN) :: x_out
-    REAL, INTENT(OUT) :: dydx_out
+    REAL(dp), DIMENSION(n_in), INTENT(IN) :: y_in,x_in
+    REAL(dp), INTENT(IN) :: x_out
+    REAL(dp), INTENT(OUT) :: dydx_out
 
-    REAL, DIMENSION(1) :: xout_wrap, dydxout_wrap
+    REAL(dp), DIMENSION(1) :: xout_wrap, dydxout_wrap
 
     xout_wrap = x_out
     call lag3deriv_array(y_in,x_in,n_in,dydxout_wrap,xout_wrap,1)
@@ -198,11 +198,11 @@ CONTAINS
 !>Returns Derivative based on a 3rd order lagrange interpolation
   subroutine lag3deriv_array(y_in,x_in,n_in,dydx_out,x_out,n_out)
     INTEGER :: n_in,n_out
-    REAL, DIMENSION(n_in), INTENT(IN) :: y_in,x_in
-    REAL, DIMENSION(n_out), INTENT(IN) :: x_out
-    REAL, DIMENSION(n_out), INTENT(OUT) :: dydx_out
+    REAL(dp), DIMENSION(n_in), INTENT(IN) :: y_in,x_in
+    REAL(dp), DIMENSION(n_out), INTENT(IN) :: x_out
+    REAL(dp), DIMENSION(n_out), INTENT(OUT) :: dydx_out
 
-    REAL :: x,aintm,aint0,aint1,aint2,xm,x0,x1,x2
+    REAL(dp) :: x,aintm,aint0,aint1,aint2,xm,x0,x1,x2
     INTEGER :: j,jm,j0,j1,j2
     INTEGER :: jstart,jfirst,jlast,jstep
 
diff --git a/src/miller_geometry.F90 b/src/miller_geometry.F90
deleted file mode 100644
index 15f70539..00000000
--- a/src/miller_geometry.F90
+++ /dev/null
@@ -1,589 +0,0 @@
-#include "redef.h"
-!! This source has been adapted from GENE https://genecode.org/ !!
-!>Implementation of the local equilibrium model of [R.L. Miller et al., PoP 5, 973 (1998)
-!>and [J. Candy, PPCF 51, 105009 (2009)]
-MODULE miller_mod
-  ! use coordinates,only: gcoor, get_dzprimedz
-  ! use discretization
-  use lagrange_interpolation
-  ! use par_geom
-  ! use par_in, only: beta, sign_Ip_CW, sign_Bt_CW, n_pol
-  ! use par_other, only: print_ini_msg
-
-  implicit none
-  public:: get_miller, set_miller_defaults
-  public:: rho, kappa, delta, s_kappa, s_delta, drR, drZ, zeta, s_zeta
-  public:: thetaShift
-  public:: mMode, nMode
-  public:: thetak, thetad
-  public:: aSurf, Delta2, Delta3, theta2, theta3, Raxis, Zaxis
-  public:: Deltam, Deltan, s_Deltam, s_Deltan, thetam, thetan
-  public:: cN_m, sN_m, cNdr_m, sNdr_m
-
-  private
-
-  REAL :: rho, kappa, delta, s_kappa, s_delta, drR, drZ, zeta, s_zeta
-
-  INTEGER :: mMode, nMode
-  REAL :: thetaShift
-  REAL :: thetak, thetad
-  REAL :: aSurf, Delta2, Delta3, theta2, theta3, Raxis, Zaxis
-  REAL :: Deltam, Deltan, s_Deltam, s_Deltan, thetam, thetan
-
-  INTEGER, PARAMETER :: IND_M=32
-  REAL, DIMENSION(0:IND_M-1) :: cN_m, sN_m, cNdr_m, sNdr_m
-
-CONTAINS
-
-  !>Set defaults for miller parameters
-  subroutine set_miller_defaults
-    rho = -1.0
-    kappa = 1.0
-    s_kappa = 0.0
-    delta = 0.0
-    s_delta = 0.0
-    drR = 0.0
-    drZ = 0.0
-    zeta = 0.0
-    s_zeta = 0.0
-
-
-    thetak = 0.0
-    thetad = 0.0
-
-    aSurf = 0.54
-    Delta2 = 1.0
-    Delta3 = 1.0
-    theta2 = 0.0
-    theta3 = 0.0
-    Raxis = 1.0
-    Zaxis = 0.0
-
-    mMode = 2
-    nMode = 3
-    Deltam = 1.0
-    Deltan = 1.0
-    s_Deltam = 0.0
-    s_Deltan = 0.0
-    thetam = 0.0
-    thetan = 0.0
-
-    cN_m = 0.0
-    sN_m = 0.0
-    cNdr_m = 0.0
-    sNdr_m = 0.0
-  end subroutine set_miller_defaults
-
-  !>Get Miller metric, magnetic field, jacobian etc.
-  subroutine get_miller(geom,edge_opt)  !previously: get_miller_a
-    type(geomtype),intent(inout):: geom
-    real, intent(in):: edge_opt
-    integer:: np, np_s, n_pol_ext, n_pol_s
-
-    real, dimension(500*(n_pol+2)):: R,Z,R_rho,Z_rho,R_theta,Z_theta,R_theta_theta,Z_theta_theta,dlp,Rc,cosu,sinu,Bphi
-    real, dimension(500*(n_pol+2)):: drRcirc, drRelong, drRelongTilt, drRtri, drRtriTilt, drZcirc, drZelong, drZelongTilt
-    real, dimension(500*(n_pol+2)):: drZtri, drZtriTilt, dtdtRcirc, dtdtRelong, dtdtRelongTilt, dtdtRtri, dtdtRtriTilt
-    real, dimension(500*(n_pol+2)):: dtdtZcirc, dtdtZelong, dtdtZelongTilt, dtdtZtri, dtdtZtriTilt, dtRcirc, dtRelong
-    real, dimension(500*(n_pol+2)):: dtRelongTilt, dtRtri, dtRtriTilt, dtZcirc, dtZelong, dtZelongTilt, dtZtri, dtZtriTilt
-    real, dimension(500*(n_pol+2)):: Rcirc, Relong, RelongTilt, Rtri, RtriTilt, Zcirc, Zelong, ZelongTilt, Ztri, ZtriTilt
-    real, dimension(500*(n_pol+2)):: drrShape, drrAng, drxAng, dryAng, dtdtrShape, dtdtrAng, dtdtxAng
-    real, dimension(500*(n_pol+2)):: dtdtyAng, dtrShape, dtrAng, dtxAng, dtyAng, rShape, rAng, xAng, yAng
-    real, dimension(500*(n_pol+2)):: theta, thAdj, J_r, B, Bp, D0, D1, D2, D3, nu, chi, psi1, nu1
-    real, dimension(500*(n_pol+2)):: tmp_reverse, theta_reverse, tmp_arr
-
-    real, dimension(500*(n_pol+1)):: theta_s, thAdj_s, chi_s, theta_s_reverse
-    real, dimension(500*(n_pol+1)):: R_s, Z_s, R_theta_s, Z_theta_s, Rc_s, cosu_s, sinu_s, Bphi_s, B_s, Bp_s, dlp_s
-    real, dimension(500*(n_pol+1)):: dtRcirc_s, dtRelong_s, dtRelongTilt_s, dtRtri_s, dtRtriTilt_s, dtZcirc_s
-    real, dimension(500*(n_pol+1)):: dtZelong_s, dtZelongTilt_s, dtZtri_s, dtZtriTilt_s, Rcirc_s, Relong_s, RelongTilt_s
-    real, dimension(500*(n_pol+1)):: Rtri_s, RtriTilt_s, Zcirc_s, Zelong_s, ZelongTilt_s, Ztri_s, ZtriTilt_s, dtrShape_s
-    real, dimension(500*(n_pol+1)):: dtrAng_s, dtxAng_s, dtyAng_s, rShape_s, rAng_s, xAng_s, yAng_s
-    real, dimension(500*(n_pol+1)):: psi1_s, nu1_s, dchidx_s, dB_drho_s, dB_dl_s, dnu_drho_s, dnu_dl_s, grad_nu_s
-    real, dimension(500*(n_pol+1)):: gxx, gxy, gxz, gyy, gyz, gzz, dtheta_dchi_s, dBp_dchi_s, jacobian, dBdx, dBdz
-    real, dimension(500*(n_pol+1)):: g_xx, g_xy, g_xz, g_yy, g_yz, g_zz, tmp_arr_s, dxdR_s, dxdZ_s, K_x, K_y !tmp_arr2
-
-    real, dimension(0:nz0-1):: gxx_out,gxy_out,gxz_out,gyy_out,gyz_out,gzz_out,Bfield_out,jacobian_out, dBdx_out, dBdz_out, chi_out
-    real, dimension(0:nz0-1):: R_out, Z_out, dxdR_out, dxdZ_out
-    real:: d_inv, drPsi, dxPsi, dq_dx, dq_dpsi, R0, Z0, B0, F, D0_full, D1_full, D2_full, D3_full
-    !real :: Lnorm, Psi0 ! currently module-wide defined anyway
-    real:: pprime, ffprime, D0_mid, D1_mid, D2_mid, D3_mid, dx_drho, pi, mu_0, dzprimedz
-    real:: rho_a, psiN, drpsiN, CN2, CN3, Rcenter, Zcenter, drRcenter, drZcenter
-    logical:: bMaxShift
-    integer:: i, k, iBmax
-
-    n_pol_ext = n_pol+2
-    n_pol_s   = n_pol+1
-    np   = 500*n_pol_ext
-    np_s = 500*n_pol_s
-
-    if (rho.lt.0.0) rho = trpeps*major_R
-    if (rho.le.0.0) stop 'flux surface radius not defined'
-    trpeps = rho/major_R
-    gcoor%x0=rho
-
-    q0 = sign_Ip_CW * sign_Bt_CW * abs(q0)
-
-    R0=major_R
-    B0=1.0*sign_Bt_CW
-    F=R0*B0
-    Z0=major_Z
-    pi = acos(-1.0)
-    mu_0=4.0*pi
-
-    theta=linspace(-pi*n_pol_ext,pi*n_pol_ext-2*pi*n_pol_ext/np,np)
-    d_inv=asin(delta)
-
-    thetaShift = 0.0
-    iBmax = 1
-
-    !flux surface parametrization
-    thAdj = theta + thetaShift
-    select case (magn_geometry)
-    case ('miller')
-      if (zeta/=0.0 .or. s_zeta/=0.0) then
-         R = R0 + rho*Cos(thAdj + d_inv*Sin(thAdj))
-         Z = Z0 + kappa*rho*Sin(thAdj + zeta*Sin(2*thAdj))
-
-         R_rho = drR + Cos(thAdj + d_inv*Sin(thAdj)) - s_delta*Sin(thAdj)*Sin(thAdj + d_inv*Sin(thAdj))
-         Z_rho = drZ + kappa*s_zeta*Cos(thAdj + zeta*Sin(2*thAdj))*Sin(2*thAdj) &
-              + kappa*Sin(thAdj + zeta*Sin(2*thAdj)) + kappa*s_kappa*Sin(thAdj + zeta*Sin(2*thAdj))
-
-         R_theta = -(rho*(1 + d_inv*Cos(thAdj))*Sin(thAdj + d_inv*Sin(thAdj)))
-         Z_theta = kappa*rho*(1 + 2*zeta*Cos(2*thAdj))*Cos(thAdj + zeta*Sin(2*thAdj))
-
-         R_theta_theta = -(rho*(1 + d_inv*Cos(thAdj))**2*Cos(thAdj + d_inv*Sin(thAdj))) &
-              + d_inv*rho*Sin(thAdj)*Sin(thAdj + d_inv*Sin(thAdj))
-         Z_theta_theta = -4*kappa*rho*zeta*Cos(thAdj + zeta*Sin(2*thAdj))*Sin(2*thAdj) &
-              - kappa*rho*(1 + 2*zeta*Cos(2*thAdj))**2*Sin(thAdj + zeta*Sin(2*thAdj))
-      else
-         Rcirc = rho*Cos(thAdj - thetad + thetak)
-         Zcirc = rho*Sin(thAdj - thetad + thetak)
-         Relong = Rcirc
-         Zelong = Zcirc + (-1 + kappa)*rho*Sin(thAdj - thetad + thetak)
-         RelongTilt = Relong*Cos(thetad - thetak) - Zelong*Sin(thetad - thetak)
-         ZelongTilt = Zelong*Cos(thetad - thetak) + Relong*Sin(thetad - thetak)
-         Rtri = RelongTilt - rho*Cos(thAdj) + rho*Cos(thAdj + delta*Sin(thAdj))
-         Ztri = ZelongTilt
-         RtriTilt = Rtri*Cos(thetad) + Ztri*Sin(thetad)
-         ZtriTilt = Ztri*Cos(thetad) - Rtri*Sin(thetad)
-         R = R0 + RtriTilt
-         Z = Z0 + ZtriTilt
-
-         drRcirc = Cos(thAdj - thetad + thetak)
-         drZcirc = Sin(thAdj - thetad + thetak)
-         drRelong = drRcirc
-         drZelong = drZcirc - (1 - kappa - kappa*s_kappa)*Sin(thAdj - thetad + thetak)
-         drRelongTilt = drRelong*Cos(thetad - thetak) - drZelong*Sin(thetad - thetak)
-         drZelongTilt = drZelong*Cos(thetad - thetak) + drRelong*Sin(thetad - thetak)
-         drRtri = drRelongTilt - Cos(thAdj) + Cos(thAdj + delta*Sin(thAdj)) &
-              - s_delta*Sin(thAdj)*Sin(thAdj + delta*Sin(thAdj))
-         drZtri = drZelongTilt
-         drRtriTilt = drRtri*Cos(thetad) + drZtri*Sin(thetad)
-         drZtriTilt = drZtri*Cos(thetad) - drRtri*Sin(thetad)
-         R_rho = drR + drRtriTilt
-         Z_rho = drZ + drZtriTilt
-
-         dtRcirc = -(rho*Sin(thAdj - thetad + thetak))
-         dtZcirc = rho*Cos(thAdj - thetad + thetak)
-         dtRelong = dtRcirc
-         dtZelong = dtZcirc + (-1 + kappa)*rho*Cos(thAdj - thetad + thetak)
-         dtRelongTilt = dtRelong*Cos(thetad - thetak) - dtZelong*Sin(thetad - thetak)
-         dtZelongTilt = dtZelong*Cos(thetad - thetak) + dtRelong*Sin(thetad - thetak)
-         dtRtri = dtRelongTilt + rho*Sin(thAdj) - rho*(1 + delta*Cos(thAdj))*Sin(thAdj + delta*Sin(thAdj))
-         dtZtri = dtZelongTilt
-         dtRtriTilt = dtRtri*Cos(thetad) + dtZtri*Sin(thetad)
-         dtZtriTilt = dtZtri*Cos(thetad) - dtRtri*Sin(thetad)
-         R_theta = dtRtriTilt
-         Z_theta = dtZtriTilt
-
-         dtdtRcirc = -(rho*Cos(thAdj - thetad + thetak))
-         dtdtZcirc = -(rho*Sin(thAdj - thetad + thetak))
-         dtdtRelong = dtdtRcirc
-         dtdtZelong = dtdtZcirc - (-1 + kappa)*rho*Sin(thAdj - thetad + thetak)
-         dtdtRelongTilt = dtdtRelong*Cos(thetad - thetak) - dtdtZelong*Sin(thetad - thetak)
-         dtdtZelongTilt = dtdtZelong*Cos(thetad - thetak) + dtdtRelong*Sin(thetad - thetak)
-         dtdtRtri = dtdtRelongTilt + rho*Cos(thAdj) - rho*(1 + delta*Cos(thAdj))**2*Cos(thAdj + delta*Sin(thAdj)) &
-              + delta*rho*Sin(thAdj)*Sin(thAdj + delta*Sin(thAdj))
-         dtdtZtri = dtdtZelongTilt
-         dtdtRtriTilt = dtdtRtri*Cos(thetad) + dtdtZtri*Sin(thetad)
-         dtdtZtriTilt = dtdtZtri*Cos(thetad) - dtdtRtri*Sin(thetad)
-         R_theta_theta = dtdtRtriTilt
-         Z_theta_theta = dtdtZtriTilt
-      endif
-    case default
-      write (*,*) "ERROR: invalid analytic geometry specification"
-    end select
-
-    !dl/dtheta
-    dlp=(R_theta**2+Z_theta**2)**0.5
-
-    !curvature radius
-    Rc=dlp**3*(R_theta*Z_theta_theta-Z_theta*R_theta_theta)**(-1)
-
-    ! some useful quantities (see papers for definition of u)
-    cosu=Z_theta/dlp
-    sinu=-R_theta/dlp
-
-    !Jacobian J_r = (dPsi/dr) J_psi = (dPsi/dr) / [(nabla phi x nabla psi)* nabla theta]
-    !             = R * (dR/drho dZ/dtheta - dR/dtheta dZ/drho) = R dlp / |nabla r|
-    J_r=R*(R_rho*Z_theta-R_theta*Z_rho)
-
-    !From definition of q = 1/(2 pi) int (B nabla phi) / (B nabla theta) dtheta:
-    !dPsi/dr = sign_Bt sign_Ip / (2 pi q) int F / R^2 J_r dtheta
-    !        = F / (2 pi |q|) int J_r/R^2 dtheta
-    tmp_arr=J_r/R**2
-    drPsi=sign_Ip_CW*F/(2.*pi*n_pol_ext*q0)*sum(tmp_arr)*2*pi*n_pol_ext/np !dlp_int(tmp_arr,1.0)
-
-    !Poloidal field (Bp = Bvec * nabla l)
-    Bp=sign_Ip_CW * drPsi / J_r * dlp
-
-    !toroidal field
-    Bphi=F/R
-
-    !total modulus of Bfield
-    B=sqrt(Bphi**2+Bp**2)
-
-    bMaxShift = .false.
-    if (thetaShift==0.0.and.trim(magn_geometry).ne.'miller_general') then
-      do i = 2,np-1
-         if (B(iBmax)<B(i)) then
-            iBmax = i
-         end if
-      enddo
-      if (iBmax/=1) then
-         bMaxShift = .true.
-         thetaShift = theta(iBmax)-theta(1)
-      end if
-    end if
-
-    !definition of radial coordinate! dx_drho=1 --> x = r
-    dx_drho=1. !drPsi/Psi0*Lnorm*q0
-    if (mype==0.and.print_ini_msg) write(*,"(A,ES12.4)") 'Using radial coordinate with dx/dr = ',dx_drho
-    dxPsi=drPsi/dx_drho
-    geom%C_y=dxPsi*sign_Ip_CW
-    geom%Cyq0_x0=geom%C_y(gpdisc%pi1gl)*q0/rho
-    geom%C_xy=abs(B0*dxPsi/geom%C_y)
-
-    if (mype==0.and.print_ini_msg) then
-       write(*,"(A,ES12.4,A,ES12.4,A,ES12.4)") &
-            "Setting C_xy = ",geom%C_xy(gpdisc%pi1gl),' C_y = ', geom%C_y(gpdisc%pi1gl)," C_x' = ", 1./dxPsi
-       write(*,'(A,ES12.4)') "B_unit/Bref conversion factor = ", q0/rho*drPsi
-       write(*,'(A,ES12.4)') "dPsi/dr = ", drPsi
-       if (thetaShift.ne.0.0) write(*,'(A,ES12.4)') "thetaShift = ", thetaShift
-    endif
-
-
-    !--------shear is expected to be defined as rho/q*dq/drho--------!
-    dq_dx=shat*q0/rho/dx_drho
-    dq_dpsi=dq_dx/dxPsi
-    pprime=-amhd/q0**2/R0/(2*mu_0)*B0**2/drPsi
-
-    !neg. dpdx normalized to magnetic pressure for pressure term
-    geom%dpdx_pm_geom=amhd/q0**2/R0/dx_drho
-
-    !first coefficient of psi in varrho expansion
-    psi1 = R*Bp*sign_Ip_CW
-
-    !integrals for ffprime evaluation
-    do i=1,np
-       tmp_arr=(2./Rc-2.*cosu/R)/(R*psi1**2)
-       D0(i)=-F*dlp_int_ind(tmp_arr,dlp,i)
-       tmp_arr=B**2*R/psi1**3
-       D1(i)=-dlp_int_ind(tmp_arr,dlp,i)/F
-       tmp_arr=mu_0*R/psi1**3
-       D2(i)=-dlp_int_ind(tmp_arr,dlp,i)*F
-       tmp_arr=1./(R*psi1)
-       D3(i)=-dlp_int_ind(tmp_arr,dlp,i)*F
-    enddo
-    tmp_arr=(2./Rc-2.*cosu/R)/(R*psi1**2)
-    D0_full=-F*dlp_int(tmp_arr,dlp)
-    tmp_arr=B**2*R/psi1**3
-    D1_full=-dlp_int(tmp_arr,dlp)/F
-    tmp_arr=mu_0*R/psi1**3
-    D2_full=-dlp_int(tmp_arr,dlp)*F
-    tmp_arr=1./(R*psi1)
-    D3_full=-dlp_int(tmp_arr,dlp)*F
-    D0_mid=D0(np/2+1)
-    D1_mid=D1(np/2+1)
-    D2_mid=D2(np/2+1)
-    D3_mid=D3(np/2+1)
-
-    ffprime=-(sign_Ip_CW*dq_dpsi*2.*pi*n_pol_ext+D0_full+D2_full*pprime)/D1_full
-
-    if (mype==0.and.print_ini_msg) then
-       write(*,'(A,ES12.4)') "ffprime = ", ffprime
-    endif
-    D0=D0-D0_mid
-    D1=D1-D1_mid
-    D2=D2-D2_mid
-    nu=D3-D3_mid
-
-    nu1=psi1*(D0+D1*ffprime+D2*pprime)
-
-    !straight field line angle defined on equidistant theta grid
-    !alpha = phi + nu = - (q chi - phi) => chi = -nu / q
-    chi=-nu/q0
-
-    !correct small scaling error (<0.5%, due to finite integration resolution)
-    chi=chi*(maxval(theta)-minval(theta))/(maxval(chi)-minval(chi))
-
-    !new grid equidistant in straight field line angle
-    chi_s = linspace(-pi*n_pol_s,pi*n_pol_s-2*pi*n_pol_s/np_s,np_s)
-
-    if (sign_Ip_CW.lt.0.0) then !make chi increasing function to not confuse lag3interp
-       tmp_reverse = chi(np:1:-1)
-       theta_reverse = theta(np:1:-1)
-       call lag3interp(theta_reverse,tmp_reverse,np,theta_s,chi_s,np_s)
-       theta_s_reverse = theta_s(np_s:1:-1)
-    else
-       !lag3interp(y_in,x_in,n_in,y_out,x_out,n_out)
-       call lag3interp(theta,chi,np,theta_s,chi_s,np_s)
-    endif
-    dtheta_dchi_s=deriv_fd(theta_s,chi_s,np_s)
-
-    !arrays equidistant in straight field line angle
-    thAdj_s = theta_s + thetaShift
-
-    select case (magn_geometry)
-    case ('miller')
-       if (zeta/=0.0 .or. s_zeta/=0.0) then
-          R_s = R0 + rho*Cos(thAdj_s + d_inv*Sin(thAdj_s))
-          Z_s = Z0 + kappa*rho*Sin(thAdj_s + zeta*Sin(2*thAdj_s))
-
-          R_theta_s = -(dtheta_dchi_s*rho*(1 + d_inv*Cos(thAdj_s))*Sin(thAdj_s + d_inv*Sin(thAdj_s)))
-          Z_theta_s = dtheta_dchi_s*kappa*rho*(1 + 2*zeta*Cos(2*thAdj_s))*Cos(thAdj_s + zeta*Sin(2*thAdj_s))
-       else
-          Rcirc_s = rho*Cos(thAdj_s - thetad + thetak)
-          Zcirc_s = rho*Sin(thAdj_s - thetad + thetak)
-          Relong_s = Rcirc_s
-          Zelong_s = Zcirc_s + (-1 + kappa)*rho*Sin(thAdj_s - thetad + thetak)
-          RelongTilt_s = Relong_s*Cos(thetad - thetak) - Zelong_s*Sin(thetad - thetak)
-          ZelongTilt_s = Zelong_s*Cos(thetad - thetak) + Relong_s*Sin(thetad - thetak)
-          Rtri_s = RelongTilt_s - rho*Cos(thAdj_s) + rho*Cos(thAdj_s + delta*Sin(thAdj_s))
-          Ztri_s = ZelongTilt_s
-          RtriTilt_s = Rtri_s*Cos(thetad) + Ztri_s*Sin(thetad)
-          ZtriTilt_s = Ztri_s*Cos(thetad) - Rtri_s*Sin(thetad)
-          R_s = R0 + RtriTilt_s
-          Z_s = Z0 + ZtriTilt_s
-
-          dtRcirc_s = -(rho*Sin(thAdj_s - thetad + thetak))
-          dtZcirc_s =   rho*Cos(thAdj_s - thetad + thetak)
-          dtRelong_s = dtRcirc_s
-          dtZelong_s = dtZcirc_s + (-1 + kappa)*rho*Cos(thAdj_s - thetad + thetak)
-          dtRelongTilt_s = dtRelong_s*Cos(thetad - thetak) - dtZelong_s*Sin(thetad - thetak)
-          dtZelongTilt_s = dtZelong_s*Cos(thetad - thetak) + dtRelong_s*Sin(thetad - thetak)
-          dtRtri_s = dtRelongTilt_s + rho*Sin(thAdj_s) &
-               - rho*(1 + delta*Cos(thAdj_s))*Sin(thAdj_s + delta*Sin(thAdj_s))
-          dtZtri_s = dtZelongTilt_s
-          dtRtriTilt_s = dtRtri_s*Cos(thetad) + dtZtri_s*Sin(thetad)
-          dtZtriTilt_s = dtZtri_s*Cos(thetad) - dtRtri_s*Sin(thetad)
-          R_theta_s = dtheta_dchi_s*dtRtriTilt_s
-          Z_theta_s = dtheta_dchi_s*dtZtriTilt_s
-       endif
-    case default
-       write (*,*) "ERROR: invalid analytic geometry specification"
-    end select
-
-    if (sign_Ip_CW.lt.0.0) then
-       call lag3interp(nu1,theta,np,tmp_arr_s,theta_s_reverse,np_s)
-       nu1_s = tmp_arr_s(np_s:1:-1)
-       call lag3interp(Bp,theta,np,tmp_arr_s,theta_s_reverse,np_s)
-       Bp_s = tmp_arr_s(np_s:1:-1)
-       call lag3interp(dlp,theta,np,tmp_arr_s,theta_s_reverse,np_s)
-       dlp_s = tmp_arr_s(np_s:1:-1)
-       call lag3interp(Rc,theta,np,tmp_arr_s,theta_s_reverse,np_s)
-       Rc_s = tmp_arr_s(np_s:1:-1)
-    else
-       call lag3interp(nu1,theta,np,nu1_s,theta_s,np_s)
-       call lag3interp(Bp,theta,np,Bp_s,theta_s,np_s)
-       call lag3interp(dlp,theta,np,dlp_s,theta_s,np_s)
-       call lag3interp(Rc,theta,np,Rc_s,theta_s,np_s)
-    endif
-
-    psi1_s = R_s*Bp_s*sign_Ip_CW
-
-    dBp_dchi_s=deriv_fd(Bp_s,chi_s,np_s)
-
-    Bphi_s=F/R_s
-    B_s=sqrt(Bphi_s**2+Bp_s**2)
-    cosu_s=Z_theta_s/dlp_s/dtheta_dchi_s
-    sinu_s=-R_theta_s/dlp_s/dtheta_dchi_s
-
-    !radial derivative of straight field line angle
-    dchidx_s=-(nu1_s/psi1_s*dxPsi+chi_s*dq_dx)/q0
-
-    !Bfield derivatives in Mercier-Luc coordinates (varrho,l,phi)
-    dB_drho_s=-1./B_s*(F**2/R_s**3*cosu_s+Bp_s**2/Rc_s+mu_0*psi1_s*pprime)
-    dB_dl_s=1./B_s*(Bp_s*dBp_dchi_s/dtheta_dchi_s/dlp_s+F**2/R_s**3*sinu_s)
-
-    dnu_drho_s=nu1_s
-    dnu_dl_s=-F/(R_s*psi1_s)
-    grad_nu_s=sqrt(dnu_drho_s**2+dnu_dl_s**2)
-
-    !contravariant metric coefficients (varrho,l,phi)->(x,y,z)
-    gxx=(psi1_s/dxPsi)**2
-    gxy=-psi1_s/dxPsi*geom%C_y(gpdisc%pi1gl)*sign_Ip_CW*nu1_s
-    gxz=-psi1_s/dxPsi*(nu1_s+psi1_s*dq_dpsi*chi_s)/q0
-    gyy=geom%C_y(gpdisc%pi1gl)**2*(grad_nu_s**2+1/R_s**2)
-    gyz=sign_Ip_CW*geom%C_y(gpdisc%pi1gl)/q0*(grad_nu_s**2+dq_dpsi*nu1_s*psi1_s*chi_s)
-    gzz=1./q0**2*(grad_nu_s**2+2.*dq_dpsi*nu1_s*psi1_s*chi_s+(dq_dpsi*psi1_s*chi_s)**2)
-
-    jacobian=1./sqrt(gxx*gyy*gzz + 2.*gxy*gyz*gxz - gxz**2*gyy - gyz**2*gxx - gzz*gxy**2)
-
-    !covariant metric coefficients
-    g_xx=jacobian**2*(gyy*gzz-gyz**2)
-    g_xy=jacobian**2*(gxz*gyz-gxy*gzz)
-    g_xz=jacobian**2*(gxy*gyz-gxz*gyy)
-    g_yy=jacobian**2*(gxx*gzz-gxz**2)
-    g_yz=jacobian**2*(gxz*gxy-gxx*gyz)
-    g_zz=jacobian**2*(gxx*gyy-gxy**2)
-
-    !Bfield derivatives
-    !dBdx = e_x * nabla B = J (nabla y x nabla z) * nabla B
-    dBdx=jacobian*geom%C_y(gpdisc%pi1gl)/(q0*R_s)*(F/(R_s*psi1_s)*dB_drho_s+(nu1_s+dq_dpsi*chi_s*psi1_s)*dB_dl_s)
-    dBdz=1./B_s*(Bp_s*dBp_dchi_s-F**2/R_s**3*R_theta_s)
-
-    !curvature terms (these are just local and will be recalculated in geometry.F90)
-    K_x = (0.-g_yz/g_zz*dBdz)
-    K_y = (dBdx-g_xz/g_zz*dBdz)
-
-    !(R,Z) derivatives for visualization
-    dxdR_s = dx_drho/drPsi*psi1_s*cosu_s
-    dxdZ_s = dx_drho/drPsi*psi1_s*sinu_s
-
-    if (edge_opt==0.0) then
-       !gene z-grid
-       chi_out=linspace(-pi*n_pol,pi*n_pol-2*pi*n_pol/nz0,nz0)
-    else
-       !new parallel coordinate chi_out==zprime
-       !see also tracer_aux.F90
-       if (n_pol>1) STOP "ERROR: n_pol>1 has not been implemented for edge_opt=\=0.0"
-       do k=0,nz0-1
-          chi_out(k)=sinh((-pi+k*2.*pi/nz0)*log(edge_opt*pi+sqrt(edge_opt**2*pi**2+1))/pi)/edge_opt
-       enddo
-       !transform metrics according to chain rule
-       do k=1,np_s
-          dzprimedz=get_dzprimedz(chi_s(k))
-          gxz(k)=gxz(k)*dzprimedz
-          gyz(k)=gyz(k)*dzprimedz
-          gzz(k)=gzz(k)*dzprimedz**2
-          jacobian(k)=jacobian(k)/dzprimedz
-          dBdz(k)=dBdz(k)/dzprimedz
-       enddo
-    endif !edge_opt
-
-    !interpolate down to GENE z-grid
-    call lag3interp(gxx,chi_s,np_s,gxx_out,chi_out,nz0)
-    call lag3interp(gxy,chi_s,np_s,gxy_out,chi_out,nz0)
-    call lag3interp(gxz,chi_s,np_s,gxz_out,chi_out,nz0)
-    call lag3interp(gyy,chi_s,np_s,gyy_out,chi_out,nz0)
-    call lag3interp(gyz,chi_s,np_s,gyz_out,chi_out,nz0)
-    call lag3interp(gzz,chi_s,np_s,gzz_out,chi_out,nz0)
-    call lag3interp(B_s,chi_s,np_s,Bfield_out,chi_out,nz0)
-    call lag3interp(jacobian,chi_s,np_s,jacobian_out,chi_out,nz0)
-    call lag3interp(dBdx,chi_s,np_s,dBdx_out,chi_out,nz0)
-    call lag3interp(dBdz,chi_s,np_s,dBdz_out,chi_out,nz0)
-    call lag3interp(R_s,chi_s,np_s,R_out,chi_out,nz0)
-    call lag3interp(Z_s,chi_s,np_s,Z_out,chi_out,nz0)
-    call lag3interp(dxdR_s,chi_s,np_s,dxdR_out,chi_out,nz0)
-    call lag3interp(dxdZ_s,chi_s,np_s,dxdZ_out,chi_out,nz0)
-
-    !select local k range
-    do i=gpdisc%pi1gl,gpdisc%pi2gl
-       if (gdisc%yx_order) then
-          geom%gii(i,lk1:lk2)=gyy_out(lk1:lk2)
-          geom%gjj(i,lk1:lk2)=gxx_out(lk1:lk2)
-          geom%giz(i,lk1:lk2)=gyz_out(lk1:lk2)
-          geom%gjz(i,lk1:lk2)=gxz_out(lk1:lk2)
-          geom%dBdi(i,lk1:lk2)=0.
-          geom%dBdj(i,lk1:lk2)=dBdx_out(lk1:lk2)
-       else
-          geom%gii(i,lk1:lk2)=gxx_out(lk1:lk2)
-          geom%gjj(i,lk1:lk2)=gyy_out(lk1:lk2)
-          geom%giz(i,lk1:lk2)=gxz_out(lk1:lk2)
-          geom%gjz(i,lk1:lk2)=gyz_out(lk1:lk2)
-          geom%dBdi(i,lk1:lk2)=dBdx_out(lk1:lk2)
-          geom%dBdj(i,lk1:lk2)=0.
-       endif
-       geom%gij(i,lk1:lk2)=gxy_out(lk1:lk2)
-       geom%gzz(i,lk1:lk2)=gzz_out(lk1:lk2)
-       geom%Bfield(i,lk1:lk2)=Bfield_out(lk1:lk2)
-       geom%jacobian(i,lk1:lk2)=jacobian_out(lk1:lk2)
-       geom%dBdz(i,lk1:lk2)=dBdz_out(lk1:lk2)
-       if (Lref.gt.0.) then
-          geom%R(i,lk1:lk2)=R_out(lk1:lk2)*Lref
-          geom%Z(i,lk1:lk2)=Z_out(lk1:lk2)*Lref
-       else
-          geom%R(i,lk1:lk2)=R_out(lk1:lk2)
-          geom%Z(i,lk1:lk2)=Z_out(lk1:lk2)
-       endif
-       geom%R_hat(i,lk1:lk2)=R_out(lk1:lk2)
-       geom%Z_hat(i,lk1:lk2)=Z_out(lk1:lk2)
-       geom%dxdR(i,lk1:lk2)= dxdR_out(lk1:lk2)
-       geom%dxdZ(i,lk1:lk2)= dxdZ_out(lk1:lk2)
-    enddo
-
-    geom%q_prof=q0
-    geom%dqdx_prof=shat*q0/gcoor%x0
-
-  contains
-
-    !> Generate an equidistant array from min to max with n points
-    function linspace(min,max,n) result(out)
-      real:: min, max
-      integer:: n
-      real, dimension(n):: out
-
-      do i=1,n
-         out(i)=min+(i-1)*(max-min)/(n-1)
-      enddo
-    end function linspace
-
-    !> Weighted average
-    real function average(var,weight)
-      real, dimension(np):: var, weight
-      average=sum(var*weight)/sum(weight)
-    end function average
-
-    !> full theta integral with weight function dlp
-    real function dlp_int(var,dlp)
-      real, dimension(np):: var, dlp
-      dlp_int=sum(var*dlp)*2*pi*n_pol_ext/np
-    end function dlp_int
-
-    !> theta integral with weight function dlp, up to index 'ind'
-    real function dlp_int_ind(var,dlp,ind)
-      real, dimension(np):: var, dlp
-      integer:: ind
-
-      dlp_int_ind=0.
-      if (ind.gt.1) then
-         dlp_int_ind=dlp_int_ind+var(1)*dlp(1)*pi*n_pol_ext/np
-         dlp_int_ind=dlp_int_ind+(sum(var(2:ind-1)*dlp(2:ind-1)))*2*pi*n_pol_ext/np
-         dlp_int_ind=dlp_int_ind+var(ind)*dlp(ind)*pi*n_pol_ext/np
-      endif
-    end function dlp_int_ind
-
-    !> 1st derivative with 2nd order finite differences
-    function deriv_fd(y,x,n) result(out)
-      integer, intent(in) :: n
-      real, dimension(n):: x,y,out,dx
-
-      !call lag3deriv(y,x,n,out,x,n)
-
-      out=0.
-      do i=2,n-1
-         out(i)=out(i)-y(i-1)/2
-         out(i)=out(i)+y(i+1)/2
-      enddo
-      out(1)=y(2)-y(1)
-      out(n)=y(n)-y(n-1)
-      dx=x(2)-x(1)
-      out=out/dx
-
-    end function deriv_fd
-
-
-  end subroutine get_miller
-
-
-END MODULE miller_mod
diff --git a/src/miller_mod.F90 b/src/miller_mod.F90
new file mode 100644
index 00000000..f75a075e
--- /dev/null
+++ b/src/miller_mod.F90
@@ -0,0 +1,645 @@
+!! This source has been adapted from GENE https://genecode.org/ !!
+!>Implementation of the local equilibrium model of [R.L. Miller et al., PoP 5, 973 (1998)
+!>and [J. Candy, PPCF 51, 105009 (2009)]
+MODULE miller
+  USE prec_const
+  USE basic
+  ! use coordinates,only: gcoor, get_dzprimedz
+  USE grid
+  ! use discretization
+  USE lagrange_interpolation
+  ! use par_in, only: beta, sign_Ip_CW, sign_Bt_CW, Npol
+  USE model
+
+  implicit none
+  public:: get_miller, set_miller_parameters
+  public:: rho, kappa, delta, s_kappa, s_delta, drR, drZ, zeta, s_zeta
+  public:: thetaShift
+  public:: mMode, nMode
+  public:: thetak, thetad
+  public:: aSurf, Delta2, Delta3, theta2, theta3, Raxis, Zaxis
+  public:: Deltam, Deltan, s_Deltam, s_Deltan, thetam, thetan
+  public:: cN_m, sN_m, cNdr_m, sNdr_m
+
+  private
+
+  real(dp) :: rho, kappa, delta, s_kappa, s_delta, drR, drZ, zeta, s_zeta
+
+  INTEGER :: mMode, nMode
+  real(dp) :: thetaShift
+  real(dp) :: thetak, thetad
+  real(dp) :: aSurf, Delta2, Delta3, theta2, theta3, Raxis, Zaxis
+  real(dp) :: Deltam, Deltan, s_Deltam, s_Deltan, thetam, thetan
+
+  INTEGER, PARAMETER :: IND_M=32
+  real(dp), DIMENSION(0:IND_M-1) :: cN_m, sN_m, cNdr_m, sNdr_m
+
+CONTAINS
+
+  !>Set defaults for miller parameters
+  subroutine set_miller_parameters(kappa_,s_kappa_,delta_,s_delta_,zeta_,s_zeta_)
+    real(dp), INTENT(IN) :: kappa_,s_kappa_,delta_,s_delta_,zeta_,s_zeta_
+    rho     = -1.0
+    kappa   = kappa_
+    s_kappa = s_kappa_
+    delta   = delta_
+    s_delta = s_delta_
+    zeta    = zeta_
+    s_zeta  = s_zeta_
+    drR     = 0.0
+    drZ     = 0.0
+
+    thetak = 0.0
+    thetad = 0.0
+
+    aSurf = 0.54
+    Delta2 = 1.0
+    Delta3 = 1.0
+    theta2 = 0.0
+    theta3 = 0.0
+    Raxis = 1.0
+    Zaxis = 0.0
+
+    mMode = 2
+    nMode = 3
+    Deltam = 1.0
+    Deltan = 1.0
+    s_Deltam = 0.0
+    s_Deltan = 0.0
+    thetam = 0.0
+    thetan = 0.0
+
+    cN_m = 0.0
+    sN_m = 0.0
+    cNdr_m = 0.0
+    sNdr_m = 0.0
+  end subroutine set_miller_parameters
+
+  !>Get Miller metric, magnetic field, jacobian etc.
+  subroutine get_miller(trpeps,major_R,major_Z,q0,shat,amhd,edge_opt,&
+       C_y,C_xy,dpdx_pm_geom,gxx_,gyy_,gzz_,gxy_,gxz_,gyz_,dBdx_,dBdy_,&
+       Bfield_,jacobian_,dBdz_,R_hat_,Z_hat_,dxdR_,dxdZ_)
+    !!!!!!!!!!!!!!!! GYACOMO INTERFACE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    real(dp), INTENT(INOUT) :: trpeps          ! eps in gyacomo (inverse aspect ratio)
+    real(dp), INTENT(INOUT) :: major_R         ! major radius
+    real(dp), INTENT(INOUT) :: major_Z         ! major Z
+    real(dp), INTENT(INOUT) :: q0              ! safetyfactor
+    real(dp), INTENT(INOUT) :: shat            ! safetyfactor
+    real(dp), INTENT(INOUT) :: amhd            ! alpha mhd
+    real(dp), INTENT(INOUT) :: edge_opt        ! alpha mhd
+    real(dp), INTENT(INOUT) :: dpdx_pm_geom    ! amplitude mag. eq. pressure grad.
+    real(dp), INTENT(INOUT) :: C_y, C_xy
+
+    real(dp), dimension(izgs:izge,0:1), INTENT(INOUT) :: &
+                                              gxx_,gyy_,gzz_,gxy_,gxz_,gyz_,&
+                                              dBdx_,dBdy_,Bfield_,jacobian_,&
+                                              dBdz_,R_hat_,Z_hat_,dxdR_,dxdZ_
+    ! No parameter in gyacomo yet
+    integer  :: ikxgs     =1 ! the left ghost gpdisc%pi1gl
+    real(dp) :: sign_Ip_CW=1 ! current sign (only normal current)
+    real(dp) :: sign_Bt_CW=1 ! current sign (only normal current)
+    !!!!!!!!!!!!!! END GYACOMO INTERFACE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+    integer:: np, np_s, Npol_ext, Npol_s
+
+    real(dp), dimension(500*(Npol+2)):: R,Z,R_rho,Z_rho,R_theta,Z_theta,R_theta_theta,Z_theta_theta,dlp,Rc,cosu,sinu,Bphi
+    real(dp), dimension(500*(Npol+2)):: drRcirc, drRelong, drRelongTilt, drRtri, drRtriTilt, drZcirc, drZelong, drZelongTilt
+    real(dp), dimension(500*(Npol+2)):: drZtri, drZtriTilt, dtdtRcirc, dtdtRelong, dtdtRelongTilt, dtdtRtri, dtdtRtriTilt
+    real(dp), dimension(500*(Npol+2)):: dtdtZcirc, dtdtZelong, dtdtZelongTilt, dtdtZtri, dtdtZtriTilt, dtRcirc, dtRelong
+    real(dp), dimension(500*(Npol+2)):: dtRelongTilt, dtRtri, dtRtriTilt, dtZcirc, dtZelong, dtZelongTilt, dtZtri, dtZtriTilt
+    real(dp), dimension(500*(Npol+2)):: Rcirc, Relong, RelongTilt, Rtri, RtriTilt, Zcirc, Zelong, ZelongTilt, Ztri, ZtriTilt
+    real(dp), dimension(500*(Npol+2)):: drrShape, drrAng, drxAng, dryAng, dtdtrShape, dtdtrAng, dtdtxAng
+    real(dp), dimension(500*(Npol+2)):: dtdtyAng, dtrShape, dtrAng, dtxAng, dtyAng, rShape, rAng, xAng, yAng
+    real(dp), dimension(500*(Npol+2)):: theta, thAdj, J_r, B, Bp, D0, D1, D2, D3, nu, chi, psi1, nu1
+    real(dp), dimension(500*(Npol+2)):: tmp_reverse, theta_reverse, tmp_arr
+
+    real(dp), dimension(500*(Npol+1)):: theta_s, thAdj_s, chi_s, theta_s_reverse
+    real(dp), dimension(500*(Npol+1)):: R_s, Z_s, R_theta_s, Z_theta_s, Rc_s, cosu_s, sinu_s, Bphi_s, B_s, Bp_s, dlp_s
+    real(dp), dimension(500*(Npol+1)):: dtRcirc_s, dtRelong_s, dtRelongTilt_s, dtRtri_s, dtRtriTilt_s, dtZcirc_s
+    real(dp), dimension(500*(Npol+1)):: dtZelong_s, dtZelongTilt_s, dtZtri_s, dtZtriTilt_s, Rcirc_s, Relong_s, RelongTilt_s
+    real(dp), dimension(500*(Npol+1)):: Rtri_s, RtriTilt_s, Zcirc_s, Zelong_s, ZelongTilt_s, Ztri_s, ZtriTilt_s, dtrShape_s
+    real(dp), dimension(500*(Npol+1)):: dtrAng_s, dtxAng_s, dtyAng_s, rShape_s, rAng_s, xAng_s, yAng_s
+    real(dp), dimension(500*(Npol+1)):: psi1_s, nu1_s, dchidx_s, dB_drho_s, dB_dl_s, dnu_drho_s, dnu_dl_s, grad_nu_s
+    real(dp), dimension(500*(Npol+1)):: gxx, gxy, gxz, gyy, gyz, gzz, dtheta_dchi_s, dBp_dchi_s, jacobian, dBdx, dBdz
+    real(dp), dimension(500*(Npol+1)):: g_xx, g_xy, g_xz, g_yy, g_yz, g_zz, tmp_arr_s, dxdR_s, dxdZ_s, K_x, K_y !tmp_arr2
+
+    real(dp), dimension(1:Nz):: gxx_out,gxy_out,gxz_out,gyy_out,gyz_out,gzz_out,Bfield_out,jacobian_out, dBdx_out, dBdz_out, chi_out
+    real(dp), dimension(1:Nz):: R_out, Z_out, dxdR_out, dxdZ_out
+    real(dp):: d_inv, drPsi, dxPsi, dq_dx, dq_dpsi, R0, Z0, B0, F, D0_full, D1_full, D2_full, D3_full
+    !real(dp) :: Lnorm, Psi0 ! currently module-wide defined anyway
+    real(dp):: pprime, ffprime, D0_mid, D1_mid, D2_mid, D3_mid, dx_drho, pi, mu_0, dzprimedz
+    real(dp):: rho_a, psiN, drpsiN, CN2, CN3, Rcenter, Zcenter, drRcenter, drZcenter
+    logical:: bMaxShift
+    integer:: i, k, iBmax
+
+    Npol_ext = Npol+2
+    Npol_s   = Npol+1
+    np   = 500*Npol_ext
+    np_s = 500*Npol_s
+
+    rho = trpeps*major_R
+    if (rho.le.0.0) stop 'flux surface radius not defined'
+    trpeps = rho/major_R
+
+    q0 = sign_Ip_CW * sign_Bt_CW * abs(q0)
+
+    R0=major_R
+    B0=1.0*sign_Bt_CW
+    F=R0*B0
+    Z0=major_Z
+    pi = acos(-1.0)
+    mu_0=4.0*pi
+
+    theta=linspace(-pi*Npol_ext,pi*Npol_ext-2._dp*pi*Npol_ext/np,np)
+    d_inv=asin(delta)
+
+    thetaShift = 0.0
+    iBmax = 1
+
+    !flux surface parametrization
+    thAdj = theta + thetaShift
+    if (zeta/=0.0 .or. s_zeta/=0.0) then
+       R = R0 + rho*Cos(thAdj + d_inv*Sin(thAdj))
+       Z = Z0 + kappa*rho*Sin(thAdj + zeta*Sin(2*thAdj))
+
+       R_rho = drR + Cos(thAdj + d_inv*Sin(thAdj)) - s_delta*Sin(thAdj)*Sin(thAdj + d_inv*Sin(thAdj))
+       Z_rho = drZ + kappa*s_zeta*Cos(thAdj + zeta*Sin(2*thAdj))*Sin(2*thAdj) &
+            + kappa*Sin(thAdj + zeta*Sin(2*thAdj)) + kappa*s_kappa*Sin(thAdj + zeta*Sin(2*thAdj))
+
+       R_theta = -(rho*(1 + d_inv*Cos(thAdj))*Sin(thAdj + d_inv*Sin(thAdj)))
+       Z_theta = kappa*rho*(1 + 2*zeta*Cos(2*thAdj))*Cos(thAdj + zeta*Sin(2*thAdj))
+
+       R_theta_theta = -(rho*(1 + d_inv*Cos(thAdj))**2*Cos(thAdj + d_inv*Sin(thAdj))) &
+            + d_inv*rho*Sin(thAdj)*Sin(thAdj + d_inv*Sin(thAdj))
+       Z_theta_theta = -4*kappa*rho*zeta*Cos(thAdj + zeta*Sin(2*thAdj))*Sin(2*thAdj) &
+            - kappa*rho*(1 + 2*zeta*Cos(2*thAdj))**2*Sin(thAdj + zeta*Sin(2*thAdj))
+    else
+       Rcirc = rho*Cos(thAdj - thetad + thetak)
+       Zcirc = rho*Sin(thAdj - thetad + thetak)
+       Relong = Rcirc
+       Zelong = Zcirc + (-1 + kappa)*rho*Sin(thAdj - thetad + thetak)
+       RelongTilt = Relong*Cos(thetad - thetak) - Zelong*Sin(thetad - thetak)
+       ZelongTilt = Zelong*Cos(thetad - thetak) + Relong*Sin(thetad - thetak)
+       Rtri = RelongTilt - rho*Cos(thAdj) + rho*Cos(thAdj + delta*Sin(thAdj))
+       Ztri = ZelongTilt
+       RtriTilt = Rtri*Cos(thetad) + Ztri*Sin(thetad)
+       ZtriTilt = Ztri*Cos(thetad) - Rtri*Sin(thetad)
+       R = R0 + RtriTilt
+       Z = Z0 + ZtriTilt
+
+       drRcirc = Cos(thAdj - thetad + thetak)
+       drZcirc = Sin(thAdj - thetad + thetak)
+       drRelong = drRcirc
+       drZelong = drZcirc - (1 - kappa - kappa*s_kappa)*Sin(thAdj - thetad + thetak)
+       drRelongTilt = drRelong*Cos(thetad - thetak) - drZelong*Sin(thetad - thetak)
+       drZelongTilt = drZelong*Cos(thetad - thetak) + drRelong*Sin(thetad - thetak)
+       drRtri = drRelongTilt - Cos(thAdj) + Cos(thAdj + delta*Sin(thAdj)) &
+            - s_delta*Sin(thAdj)*Sin(thAdj + delta*Sin(thAdj))
+       drZtri = drZelongTilt
+       drRtriTilt = drRtri*Cos(thetad) + drZtri*Sin(thetad)
+       drZtriTilt = drZtri*Cos(thetad) - drRtri*Sin(thetad)
+       R_rho = drR + drRtriTilt
+       Z_rho = drZ + drZtriTilt
+
+       dtRcirc = -(rho*Sin(thAdj - thetad + thetak))
+       dtZcirc = rho*Cos(thAdj - thetad + thetak)
+       dtRelong = dtRcirc
+       dtZelong = dtZcirc + (-1 + kappa)*rho*Cos(thAdj - thetad + thetak)
+       dtRelongTilt = dtRelong*Cos(thetad - thetak) - dtZelong*Sin(thetad - thetak)
+       dtZelongTilt = dtZelong*Cos(thetad - thetak) + dtRelong*Sin(thetad - thetak)
+       dtRtri = dtRelongTilt + rho*Sin(thAdj) - rho*(1 + delta*Cos(thAdj))*Sin(thAdj + delta*Sin(thAdj))
+       dtZtri = dtZelongTilt
+       dtRtriTilt = dtRtri*Cos(thetad) + dtZtri*Sin(thetad)
+       dtZtriTilt = dtZtri*Cos(thetad) - dtRtri*Sin(thetad)
+       R_theta = dtRtriTilt
+       Z_theta = dtZtriTilt
+
+       dtdtRcirc = -(rho*Cos(thAdj - thetad + thetak))
+       dtdtZcirc = -(rho*Sin(thAdj - thetad + thetak))
+       dtdtRelong = dtdtRcirc
+       dtdtZelong = dtdtZcirc - (-1 + kappa)*rho*Sin(thAdj - thetad + thetak)
+       dtdtRelongTilt = dtdtRelong*Cos(thetad - thetak) - dtdtZelong*Sin(thetad - thetak)
+       dtdtZelongTilt = dtdtZelong*Cos(thetad - thetak) + dtdtRelong*Sin(thetad - thetak)
+       dtdtRtri = dtdtRelongTilt + rho*Cos(thAdj) - rho*(1 + delta*Cos(thAdj))**2*Cos(thAdj + delta*Sin(thAdj)) &
+            + delta*rho*Sin(thAdj)*Sin(thAdj + delta*Sin(thAdj))
+       dtdtZtri = dtdtZelongTilt
+       dtdtRtriTilt = dtdtRtri*Cos(thetad) + dtdtZtri*Sin(thetad)
+       dtdtZtriTilt = dtdtZtri*Cos(thetad) - dtdtRtri*Sin(thetad)
+       R_theta_theta = dtdtRtriTilt
+       Z_theta_theta = dtdtZtriTilt
+    endif
+
+    !dl/dtheta
+    dlp=(R_theta**2+Z_theta**2)**0.5
+
+    !curvature radius
+    Rc=dlp**3*(R_theta*Z_theta_theta-Z_theta*R_theta_theta)**(-1)
+
+    ! some useful quantities (see papers for definition of u)
+    cosu=Z_theta/dlp
+    sinu=-R_theta/dlp
+
+    !Jacobian J_r = (dPsi/dr) J_psi = (dPsi/dr) / [(nabla fz x nabla psi)* nabla theta]
+    !             = R * (dR/drho dZ/dtheta - dR/dtheta dZ/drho) = R dlp / |nabla r|
+    J_r=R*(R_rho*Z_theta-R_theta*Z_rho)
+
+    !From definition of q = 1/(2 pi) int (B nabla fz) / (B nabla theta) dtheta:
+    !dPsi/dr = sign_Bt sign_Ip / (2 pi q) int F / R^2 J_r dtheta
+    !        = F / (2 pi |q|) int J_r/R^2 dtheta
+    tmp_arr=J_r/R**2
+    drPsi=sign_Ip_CW*F/(2.*pi*Npol_ext*q0)*sum(tmp_arr)*2*pi*Npol_ext/np !dlp_int(tmp_arr,1.0)
+
+    !Poloidal field (Bp = Bvec * nabla l)
+    Bp=sign_Ip_CW * drPsi / J_r * dlp
+
+    !toroidal field
+    Bphi=F/R
+
+    !total modulus of Bfield
+    B=sqrt(Bphi**2+Bp**2)
+
+    bMaxShift = .false.
+    ! if (thetaShift==0.0.and.trim(magn_geometry).ne.'miller_general') then
+    if (thetaShift==0.0) then
+      do i = 2,np-1
+         if (B(iBmax)<B(i)) then
+            iBmax = i
+         end if
+      enddo
+      if (iBmax/=1) then
+         bMaxShift = .true.
+         thetaShift = theta(iBmax)-theta(1)
+      end if
+    end if
+
+    !definition of radial coordinate! dx_drho=1 --> x = r
+    dx_drho=1. !drPsi/Psi0*Lnorm*q0
+    if (my_id==0) write(*,"(A,ES12.4)") 'Using radial coordinate with dx/dr = ',dx_drho
+    dxPsi  = drPsi/dx_drho
+    C_y    = dxPsi*sign_Ip_CW
+    C_xy   = abs(B0*dxPsi/C_y)
+
+    if (my_id==0) then
+       write(*,"(A,ES12.4,A,ES12.4,A,ES12.4)") &
+            "Setting C_xy = ",C_xy,' C_y = ', C_y," C_x' = ", 1./dxPsi
+       write(*,'(A,ES12.4)') "B_unit/Bref conversion factor = ", q0/rho*drPsi
+       write(*,'(A,ES12.4)') "dPsi/dr = ", drPsi
+       if (thetaShift.ne.0.0) write(*,'(A,ES12.4)') "thetaShift = ", thetaShift
+    endif
+
+
+    !--------shear is expected to be defined as rho/q*dq/drho--------!
+    dq_dx=shat*q0/rho/dx_drho
+    dq_dpsi=dq_dx/dxPsi
+    pprime=-amhd/q0**2/R0/(2*mu_0)*B0**2/drPsi
+
+    !neg. dpdx normalized to magnetic pressure for pressure term
+    dpdx_pm_geom=amhd/q0**2/R0/dx_drho
+
+    !first coefficient of psi in varrho expansion
+    psi1 = R*Bp*sign_Ip_CW
+
+    !integrals for ffprime evaluation
+    do i=1,np
+       tmp_arr=(2./Rc-2.*cosu/R)/(R*psi1**2)
+       D0(i)=-F*dlp_int_ind(tmp_arr,dlp,i)
+       tmp_arr=B**2*R/psi1**3
+       D1(i)=-dlp_int_ind(tmp_arr,dlp,i)/F
+       tmp_arr=mu_0*R/psi1**3
+       D2(i)=-dlp_int_ind(tmp_arr,dlp,i)*F
+       tmp_arr=1./(R*psi1)
+       D3(i)=-dlp_int_ind(tmp_arr,dlp,i)*F
+    enddo
+    tmp_arr=(2./Rc-2.*cosu/R)/(R*psi1**2)
+    D0_full=-F*dlp_int(tmp_arr,dlp)
+    tmp_arr=B**2*R/psi1**3
+    D1_full=-dlp_int(tmp_arr,dlp)/F
+    tmp_arr=mu_0*R/psi1**3
+    D2_full=-dlp_int(tmp_arr,dlp)*F
+    tmp_arr=1./(R*psi1)
+    D3_full=-dlp_int(tmp_arr,dlp)*F
+    D0_mid=D0(np/2+1)
+    D1_mid=D1(np/2+1)
+    D2_mid=D2(np/2+1)
+    D3_mid=D3(np/2+1)
+
+    ffprime=-(sign_Ip_CW*dq_dpsi*2.*pi*Npol_ext+D0_full+D2_full*pprime)/D1_full
+
+    if (my_id==0) then
+       write(*,'(A,ES12.4)') "ffprime = ", ffprime
+    endif
+    D0=D0-D0_mid
+    D1=D1-D1_mid
+    D2=D2-D2_mid
+    nu=D3-D3_mid
+
+    nu1=psi1*(D0+D1*ffprime+D2*pprime)
+
+    !straight field line angle defined on equidistant theta grid
+    !alpha = fz + nu = - (q chi - fz) => chi = -nu / q
+    chi=-nu/q0
+
+    !correct small scaling error (<0.5%, due to finite integration resolution)
+    chi=chi*(maxval(theta)-minval(theta))/(maxval(chi)-minval(chi))
+
+    !new grid equidistant in straight field line angle
+    chi_s = linspace(-pi*Npol_s,pi*Npol_s-2*pi*Npol_s/np_s,np_s)
+
+    if (sign_Ip_CW.lt.0.0) then !make chi increasing function to not confuse lag3interp
+       tmp_reverse = chi(np:1:-1)
+       theta_reverse = theta(np:1:-1)
+       call lag3interp(theta_reverse,tmp_reverse,np,theta_s,chi_s,np_s)
+       theta_s_reverse = theta_s(np_s:1:-1)
+    else
+       !lag3interp(y_in,x_in,n_in,y_out,x_out,n_out)
+       call lag3interp(theta,chi,np,theta_s,chi_s,np_s)
+    endif
+    dtheta_dchi_s=deriv_fd(theta_s,chi_s,np_s)
+
+    !arrays equidistant in straight field line angle
+    thAdj_s = theta_s + thetaShift
+
+    if (zeta/=0.0 .or. s_zeta/=0.0) then
+      R_s = R0 + rho*Cos(thAdj_s + d_inv*Sin(thAdj_s))
+      Z_s = Z0 + kappa*rho*Sin(thAdj_s + zeta*Sin(2*thAdj_s))
+
+      R_theta_s = -(dtheta_dchi_s*rho*(1 + d_inv*Cos(thAdj_s))*Sin(thAdj_s + d_inv*Sin(thAdj_s)))
+      Z_theta_s = dtheta_dchi_s*kappa*rho*(1 + 2*zeta*Cos(2*thAdj_s))*Cos(thAdj_s + zeta*Sin(2*thAdj_s))
+    else
+      Rcirc_s = rho*Cos(thAdj_s - thetad + thetak)
+      Zcirc_s = rho*Sin(thAdj_s - thetad + thetak)
+      Relong_s = Rcirc_s
+      Zelong_s = Zcirc_s + (-1 + kappa)*rho*Sin(thAdj_s - thetad + thetak)
+      RelongTilt_s = Relong_s*Cos(thetad - thetak) - Zelong_s*Sin(thetad - thetak)
+      ZelongTilt_s = Zelong_s*Cos(thetad - thetak) + Relong_s*Sin(thetad - thetak)
+      Rtri_s = RelongTilt_s - rho*Cos(thAdj_s) + rho*Cos(thAdj_s + delta*Sin(thAdj_s))
+      Ztri_s = ZelongTilt_s
+      RtriTilt_s = Rtri_s*Cos(thetad) + Ztri_s*Sin(thetad)
+      ZtriTilt_s = Ztri_s*Cos(thetad) - Rtri_s*Sin(thetad)
+      R_s = R0 + RtriTilt_s
+      Z_s = Z0 + ZtriTilt_s
+
+      dtRcirc_s = -(rho*Sin(thAdj_s - thetad + thetak))
+      dtZcirc_s =   rho*Cos(thAdj_s - thetad + thetak)
+      dtRelong_s = dtRcirc_s
+      dtZelong_s = dtZcirc_s + (-1 + kappa)*rho*Cos(thAdj_s - thetad + thetak)
+      dtRelongTilt_s = dtRelong_s*Cos(thetad - thetak) - dtZelong_s*Sin(thetad - thetak)
+      dtZelongTilt_s = dtZelong_s*Cos(thetad - thetak) + dtRelong_s*Sin(thetad - thetak)
+      dtRtri_s = dtRelongTilt_s + rho*Sin(thAdj_s) &
+           - rho*(1 + delta*Cos(thAdj_s))*Sin(thAdj_s + delta*Sin(thAdj_s))
+      dtZtri_s = dtZelongTilt_s
+      dtRtriTilt_s = dtRtri_s*Cos(thetad) + dtZtri_s*Sin(thetad)
+      dtZtriTilt_s = dtZtri_s*Cos(thetad) - dtRtri_s*Sin(thetad)
+      R_theta_s = dtheta_dchi_s*dtRtriTilt_s
+      Z_theta_s = dtheta_dchi_s*dtZtriTilt_s
+    endif
+    if (sign_Ip_CW.lt.0.0) then
+       call lag3interp(nu1,theta,np,tmp_arr_s,theta_s_reverse,np_s)
+       nu1_s = tmp_arr_s(np_s:1:-1)
+       call lag3interp(Bp,theta,np,tmp_arr_s,theta_s_reverse,np_s)
+       Bp_s = tmp_arr_s(np_s:1:-1)
+       call lag3interp(dlp,theta,np,tmp_arr_s,theta_s_reverse,np_s)
+       dlp_s = tmp_arr_s(np_s:1:-1)
+       call lag3interp(Rc,theta,np,tmp_arr_s,theta_s_reverse,np_s)
+       Rc_s = tmp_arr_s(np_s:1:-1)
+    else
+       call lag3interp(nu1,theta,np,nu1_s,theta_s,np_s)
+       call lag3interp(Bp,theta,np,Bp_s,theta_s,np_s)
+       call lag3interp(dlp,theta,np,dlp_s,theta_s,np_s)
+       call lag3interp(Rc,theta,np,Rc_s,theta_s,np_s)
+    endif
+
+    psi1_s = R_s*Bp_s*sign_Ip_CW
+
+    dBp_dchi_s=deriv_fd(Bp_s,chi_s,np_s)
+
+    Bphi_s=F/R_s
+    B_s=sqrt(Bphi_s**2+Bp_s**2)
+    cosu_s=Z_theta_s/dlp_s/dtheta_dchi_s
+    sinu_s=-R_theta_s/dlp_s/dtheta_dchi_s
+
+    !radial derivative of straight field line angle
+    dchidx_s=-(nu1_s/psi1_s*dxPsi+chi_s*dq_dx)/q0
+
+    !Bfield derivatives in Mercier-Luc coordinates (varrho,l,fz)
+    dB_drho_s=-1./B_s*(F**2/R_s**3*cosu_s+Bp_s**2/Rc_s+mu_0*psi1_s*pprime)
+    dB_dl_s=1./B_s*(Bp_s*dBp_dchi_s/dtheta_dchi_s/dlp_s+F**2/R_s**3*sinu_s)
+
+    dnu_drho_s=nu1_s
+    dnu_dl_s=-F/(R_s*psi1_s)
+    grad_nu_s=sqrt(dnu_drho_s**2+dnu_dl_s**2)
+
+    !contravariant metric coefficients (varrho,l,fz)->(x,y,z)
+    gxx=(psi1_s/dxPsi)**2
+    gxy=-psi1_s/dxPsi*C_y*sign_Ip_CW*nu1_s
+    gxz=-psi1_s/dxPsi*(nu1_s+psi1_s*dq_dpsi*chi_s)/q0
+    gyy=C_y**2*(grad_nu_s**2+1/R_s**2)
+    gyz=sign_Ip_CW*C_y/q0*(grad_nu_s**2+dq_dpsi*nu1_s*psi1_s*chi_s)
+    gzz=1./q0**2*(grad_nu_s**2+2.*dq_dpsi*nu1_s*psi1_s*chi_s+(dq_dpsi*psi1_s*chi_s)**2)
+
+    jacobian=1./sqrt(gxx*gyy*gzz + 2.*gxy*gyz*gxz - gxz**2*gyy - gyz**2*gxx - gzz*gxy**2)
+
+    !covariant metric coefficients
+    g_xx=jacobian**2*(gyy*gzz-gyz**2)
+    g_xy=jacobian**2*(gxz*gyz-gxy*gzz)
+    g_xz=jacobian**2*(gxy*gyz-gxz*gyy)
+    g_yy=jacobian**2*(gxx*gzz-gxz**2)
+    g_yz=jacobian**2*(gxz*gxy-gxx*gyz)
+    g_zz=jacobian**2*(gxx*gyy-gxy**2)
+
+    !Bfield derivatives
+    !dBdx = e_x * nabla B = J (nabla y x nabla z) * nabla B
+    dBdx=jacobian*C_y/(q0*R_s)*(F/(R_s*psi1_s)*dB_drho_s+(nu1_s+dq_dpsi*chi_s*psi1_s)*dB_dl_s)
+    dBdz=1./B_s*(Bp_s*dBp_dchi_s-F**2/R_s**3*R_theta_s)
+
+    !curvature terms (these are just local and will be recalculated in geometry.F90)
+    K_x = (0.-g_yz/g_zz*dBdz)
+    K_y = (dBdx-g_xz/g_zz*dBdz)
+
+    !(R,Z) derivatives for visualization
+    dxdR_s = dx_drho/drPsi*psi1_s*cosu_s
+    dxdZ_s = dx_drho/drPsi*psi1_s*sinu_s
+
+    if (edge_opt==0.0) then
+       !gene z-grid
+       chi_out=linspace(-pi*Npol,pi*Npol-2*pi*Npol/Nz,Nz)
+    else
+       !new parallel coordinate chi_out==zprime
+       !see also tracer_aux.F90
+       if (Npol>1) STOP "ERROR: Npol>1 has not been implemented for edge_opt=\=0.0"
+       do k=izs,ize
+          chi_out(k)=sinh((-pi+k*2.*pi/Nz)*log(edge_opt*pi+sqrt(edge_opt**2*pi**2+1))/pi)/edge_opt
+       enddo
+       !transform metrics according to chain rule
+       do k=1,np_s
+         !>dz'/dz conversion for edge_opt as function of z
+          if (edge_opt.gt.0) then
+             dzprimedz = edge_opt*pi/log(edge_opt*pi+sqrt((edge_opt*pi)**2+1))/&
+                  sqrt((edge_opt*chi_s(k))**2+1)
+          else
+             dzprimedz = 1.0
+          endif
+          gxz(k)=gxz(k)*dzprimedz
+          gyz(k)=gyz(k)*dzprimedz
+          gzz(k)=gzz(k)*dzprimedz**2
+          jacobian(k)=jacobian(k)/dzprimedz
+          dBdz(k)=dBdz(k)/dzprimedz
+       enddo
+    endif !edge_opt
+
+    !interpolate down to GENE z-grid
+    call lag3interp(gxx,chi_s,np_s,gxx_out,chi_out,Nz)
+    call lag3interp(gxy,chi_s,np_s,gxy_out,chi_out,Nz)
+    call lag3interp(gxz,chi_s,np_s,gxz_out,chi_out,Nz)
+    call lag3interp(gyy,chi_s,np_s,gyy_out,chi_out,Nz)
+    call lag3interp(gyz,chi_s,np_s,gyz_out,chi_out,Nz)
+    call lag3interp(gzz,chi_s,np_s,gzz_out,chi_out,Nz)
+    call lag3interp(B_s,chi_s,np_s,Bfield_out,chi_out,Nz)
+    call lag3interp(jacobian,chi_s,np_s,jacobian_out,chi_out,Nz)
+    call lag3interp(dBdx,chi_s,np_s,dBdx_out,chi_out,Nz)
+    call lag3interp(dBdz,chi_s,np_s,dBdz_out,chi_out,Nz)
+    call lag3interp(R_s,chi_s,np_s,R_out,chi_out,Nz)
+    call lag3interp(Z_s,chi_s,np_s,Z_out,chi_out,Nz)
+    call lag3interp(dxdR_s,chi_s,np_s,dxdR_out,chi_out,Nz)
+    call lag3interp(dxdZ_s,chi_s,np_s,dxdZ_out,chi_out,Nz)
+    ! Fill the geom arrays with the results
+    do eo=0,1
+    gxx_(izs:ize,eo)      =gxx_out(izs:ize)
+    gyy_(izs:ize,eo)      =gyy_out(izs:ize)
+    gxz_(izs:ize,eo)      =gxz_out(izs:ize)
+    gyz_(izs:ize,eo)      =gyz_out(izs:ize)
+    dBdx_(izs:ize,eo)     =dBdx_out(izs:ize)
+    dBdy_(izs:ize,eo)     =0.
+    gxy_(izs:ize,eo)      =gxy_out(izs:ize)
+    gzz_(izs:ize,eo)      =gzz_out(izs:ize)
+    Bfield_(izs:ize,eo)   =Bfield_out(izs:ize)
+    jacobian_(izs:ize,eo) =jacobian_out(izs:ize)
+    dBdz_(izs:ize,eo)     =dBdz_out(izs:ize)
+    R_hat_(izs:ize,eo)    =R_out(izs:ize)
+    Z_hat_(izs:ize,eo)    =Z_out(izs:ize)
+    dxdR_(izs:ize,eo)     = dxdR_out(izs:ize)
+    dxdZ_(izs:ize,eo)     = dxdZ_out(izs:ize)
+
+    !! UPDATE GHOSTS VALUES (since the miller function in GENE does not)
+    CALL update_ghosts_z(gxx_(:,eo))
+    CALL update_ghosts_z(gyy_(:,eo))
+    CALL update_ghosts_z(gxz_(:,eo))
+    CALL update_ghosts_z(dBdx_(:,eo))
+    CALL update_ghosts_z(dBdy_(:,eo))
+    CALL update_ghosts_z(gxy_(:,eo))
+    CALL update_ghosts_z(gzz_(:,eo))
+    CALL update_ghosts_z(Bfield_(:,eo))
+    CALL update_ghosts_z(jacobian_(:,eo))
+    CALL update_ghosts_z(dBdz_(:,eo))
+    CALL update_ghosts_z(R_hat_(:,eo))
+    CALL update_ghosts_z(Z_hat_(:,eo))
+    CALL update_ghosts_z(dxdR_(:,eo))
+    CALL update_ghosts_z(dxdZ_(:,eo))
+  enddo
+
+  contains
+
+
+    SUBROUTINE update_ghosts_z(fz_)
+      IMPLICIT NONE
+      ! INTEGER,  INTENT(IN) :: nztot_
+      REAL(dp), DIMENSION(izgs:izge), INTENT(INOUT) :: fz_
+      REAL(dp), DIMENSION(-2:2) :: buff
+      INTEGER :: status(MPI_STATUS_SIZE), count
+
+      IF(Nz .GT. 1) THEN
+        IF (num_procs_z .GT. 1) THEN
+          CALL MPI_BARRIER(MPI_COMM_WORLD,ierr)
+            count = 1 ! one point to exchange
+            !!!!!!!!!!! Send ghost to up neighbour !!!!!!!!!!!!!!!!!!!!!!
+            CALL mpi_sendrecv(fz_(ize), count, MPI_DOUBLE, nbr_U, 0, & ! Send to Up the last
+                              buff(-1), count, MPI_DOUBLE, nbr_D, 0, & ! Receive from Down the first-1
+                              comm0, status, ierr)
+
+            CALL mpi_sendrecv(fz_(ize-1), count, MPI_DOUBLE, nbr_U, 0, & ! Send to Up the last
+                                buff(-2), count, MPI_DOUBLE, nbr_D, 0, & ! Receive from Down the first-2
+                              comm0, status, ierr)
+
+            !!!!!!!!!!! Send ghost to down neighbour !!!!!!!!!!!!!!!!!!!!!!
+            CALL mpi_sendrecv(fz_(izs), count, MPI_DOUBLE, nbr_D, 0, & ! Send to Down the first
+                              buff(+1), count, MPI_DOUBLE, nbr_U, 0, & ! Recieve from Up the last+1
+                              comm0, status, ierr)
+
+            CALL mpi_sendrecv(fz_(izs+1), count, MPI_DOUBLE, nbr_D, 0, & ! Send to Down the first
+                                buff(+2), count, MPI_DOUBLE, nbr_U, 0, & ! Recieve from Up the last+2
+                              comm0, status, ierr)
+         ELSE
+           buff(-1) = fz_(ize  )
+           buff(-2) = fz_(ize-1)
+           buff(+1) = fz_(izs  )
+           buff(+2) = fz_(izs+1)
+         ENDIF
+         fz_(ize+1) = buff(+1)
+         fz_(ize+2) = buff(+2)
+         fz_(izs-1) = buff(-1)
+         fz_(izs-2) = buff(-2)
+      ENDIF
+    END SUBROUTINE update_ghosts_z
+
+
+    !> Generate an equidistant array from min to max with n points
+    function linspace(min,max,n) result(out)
+      real(dp):: min, max
+      integer:: n
+      real(dp), dimension(n):: out
+
+      do i=1,n
+         out(i)=min+(i-1)*(max-min)/(n-1)
+      enddo
+    end function linspace
+
+    !> Weighted average
+    real(dp) function average(var,weight)
+      real(dp), dimension(np):: var, weight
+      average=sum(var*weight)/sum(weight)
+    end function average
+
+    !> full theta integral with weight function dlp
+    real(dp) function dlp_int(var,dlp)
+      real(dp), dimension(np):: var, dlp
+      dlp_int=sum(var*dlp)*2*pi*Npol_ext/np
+    end function dlp_int
+
+    !> theta integral with weight function dlp, up to index 'ind'
+    real(dp) function dlp_int_ind(var,dlp,ind)
+      real(dp), dimension(np):: var, dlp
+      integer:: ind
+
+      dlp_int_ind=0.
+      if (ind.gt.1) then
+         dlp_int_ind=dlp_int_ind+var(1)*dlp(1)*pi*Npol_ext/np
+         dlp_int_ind=dlp_int_ind+(sum(var(2:ind-1)*dlp(2:ind-1)))*2*pi*Npol_ext/np
+         dlp_int_ind=dlp_int_ind+var(ind)*dlp(ind)*pi*Npol_ext/np
+      endif
+    end function dlp_int_ind
+
+    !> 1st derivative with 2nd order finite differences
+    function deriv_fd(y,x,n) result(out)
+      integer, intent(in) :: n
+      real(dp), dimension(n):: x,y,out,dx
+
+      !call lag3deriv(y,x,n,out,x,n)
+
+      out=0.
+      do i=2,n-1
+         out(i)=out(i)-y(i-1)/2
+         out(i)=out(i)+y(i+1)/2
+      enddo
+      out(1)=y(2)-y(1)
+      out(n)=y(n)-y(n-1)
+      dx=x(2)-x(1)
+      out=out/dx
+
+    end function deriv_fd
+
+
+  end subroutine get_miller
+
+
+END MODULE miller
-- 
GitLab