diagnostics_final_routines.f90 Source File


Contents


Source Code

!> Diagnostic routines that are called at the end of the simulation,
!> rather than during the run
module diagnostics_final_routines
  implicit none

  private

  public :: init_par_filter, do_write_gs
  public :: do_write_final_antot, do_write_final_moments, do_write_final_db
  public :: do_write_final_epar, do_write_kpar, do_write_final_fields

contains

  !> Setup the FFTs for calculating the parallel spectrum
  subroutine init_par_filter
    use theta_grid, only: ntgrid
    use gs2_transforms, only: init_zf
    use kt_grids, only: naky, ntheta0
    use mp, only: proc0
    implicit none

    if (naky * ntheta0 == 0) then
      if (proc0) print *,"WARNING: kt_grids used in init_par_filter before initialised?"
    end if

    call init_zf (ntgrid, ntheta0*naky)
  end subroutine init_par_filter

  !> Calculate the parallel spectrum
  subroutine par_spectrum(an, an2)
    use gs2_transforms, only: kz_spectrum
    use theta_grid, only: ntgrid
    implicit none
    complex, dimension(:,:,:), intent(in) :: an
    complex, dimension(:,:,:), intent(out) :: an2
    complex, dimension(:,:,:), allocatable :: tmp
    real :: scale
    ! Copy the input data as the ffts can potentially modify input
    allocate(tmp, source = an)
    call kz_spectrum (tmp, an2)
    scale = 1./real(4*ntgrid**2)
    an2 = an2*scale
  end subroutine par_spectrum

  !> Write \(\phi, A_\parallel, B_\parallel\), overwriting existing values
  subroutine do_write_final_fields(write_text, file_id)
    use mp, only: proc0
    use file_utils, only: open_output_file, close_output_file
    use gs2_io, only: nc_final_fields
    use kt_grids, only: naky, ntheta0, aky, akx, theta0
    use theta_grid, only: ntgrid, theta
    use fields_arrays, only: phi, apar, bpar
    implicit none
    !> Write to text file
    logical, intent(in) :: write_text
    !> NetCDF ID of the file to write to
    integer, intent(in) :: file_id

    integer :: ik, it, ig, unit

    if (.not. proc0) return

    call nc_final_fields(file_id)

    if (write_text) then
       call open_output_file (unit, ".fields")
       do ik = 1, naky
          do it = 1, ntheta0
             do ig = -ntgrid, ntgrid
                write (unit, "(15(1x,e12.5))") &
                     theta(ig), aky(ik), akx(it), &
                     phi(ig,it,ik), &
                     apar(ig,it,ik), &
                     bpar(ig,it,ik), &
                     theta(ig) - theta0(it,ik), &
                     abs(phi(ig,it,ik))
             end do
             write (unit, "()")
          end do
       end do
       call close_output_file (unit)
    end if
  end subroutine do_write_final_fields

  !> Write the parallel spectrum of \(\phi, A_\parallel, B_\parallel\), overwriting existing values
  !>
  !> @note No netCDF output
  subroutine do_write_kpar(write_text)
    use mp, only: proc0
    use file_utils, only: open_output_file, close_output_file
    use theta_grid, only: ntgrid, gradpar, nperiod
    use kt_grids, only: naky, ntheta0, aky, akx
    use run_parameters, only: has_phi, has_apar, has_bpar
    use fields_arrays, only: phi, apar, bpar
    implicit none

    !> Write to text file
    logical, intent(in) :: write_text

    complex, dimension (:,:,:), allocatable :: phi2, apar2, bpar2
    real, dimension (2*ntgrid) :: kpar
    integer :: ig, ik, it, unit

    if (.not. proc0) return
    if (.not. write_text) return

    allocate (phi2(-ntgrid:ntgrid,ntheta0,naky))
    allocate (apar2(-ntgrid:ntgrid,ntheta0,naky))
    allocate (bpar2(-ntgrid:ntgrid,ntheta0,naky))

    if (has_phi) then
      call par_spectrum(phi, phi2)
    else
      phi2=0.
    end if
    if (has_apar) then
      call par_spectrum(apar, apar2)
    else
      apar2=0.
    endif
    if (has_bpar) then
      call par_spectrum(bpar, bpar2)
    else
      bpar2=0.
    endif

    call open_output_file (unit, ".kpar")
    do ig = 1, ntgrid
      kpar(ig) = (ig-1)*gradpar(ig)/real(2*nperiod-1)
      kpar(2*ntgrid-ig+1)=-(ig)*gradpar(ig)/real(2*nperiod-1)
    end do
    do ik = 1, naky
      do it = 1, ntheta0
        do ig = ntgrid+1,2*ntgrid
          write (unit, "(9(1x,e12.5))") &
               kpar(ig), aky(ik), akx(it), &
               phi2(ig-ntgrid-1,it,ik), &
               apar2(ig-ntgrid-1,it,ik), &
               bpar2(ig-ntgrid-1,it,ik)
        end do
        do ig = 1, ntgrid
          write (unit, "(9(1x,e12.5))") &
               kpar(ig), aky(ik), akx(it), &
               phi2(ig-ntgrid-1,it,ik), &
               apar2(ig-ntgrid-1,it,ik), &
               bpar2(ig-ntgrid-1,it,ik)
        end do
        write (unit, "()")
      end do
    end do
    call close_output_file (unit)

    deallocate (phi2, apar2, bpar2)
  end subroutine do_write_kpar

  !> Calculate and write \(E_\parallel\), overwriting existing values
  subroutine do_write_final_epar(write_text, file_id)
    use mp, only: proc0
    use file_utils, only: open_output_file, close_output_file
    use kt_grids, only: naky, ntheta0, theta0, aky, akx
    use theta_grid, only: theta, ntgrid
    use fields_arrays, only: phi, apar, phinew, aparnew
    use gs2_io, only: nc_final_epar
    implicit none
    !> Write to text file
    logical, intent(in) :: write_text
    !> NetCDF ID of the file to write to
    integer, intent(in) :: file_id
    complex, dimension (:,:,:), allocatable :: epar
    integer :: ik, it, ig, unit

    if (.not. proc0) return

    allocate (epar(-ntgrid:ntgrid,ntheta0,naky))
    call get_epar (phi, apar, phinew, aparnew, epar)
    call nc_final_epar (file_id, epar)

    if (write_text) then
       call open_output_file (unit, ".epar")
       do ik = 1, naky
          do it = 1, ntheta0
             do ig = -ntgrid, ntgrid-1
                write (unit, "(6(1x,e12.5))") &
                     theta(ig), aky(ik), akx(it), &
                     epar(ig,it,ik), &
                     theta(ig) - theta0(it,ik)
             end do
             write (unit, "()")
          end do
       end do
       call close_output_file (unit)
    end if
    deallocate (epar)
  end subroutine do_write_final_epar

  !> Calculate \(E_\parallel\)
  !>
  !> @note This subroutine only returns epar correctly for linear runs.
  !>
  !> FIXME: Add equation
  subroutine get_epar (phi, apar, phinew, aparnew, epar)

    use theta_grid, only: ntgrid, delthet, gradpar
    use run_parameters, only: fphi, fapar
    use gs2_time, only: code_dt, tunits
    use kt_grids, only: naky, ntheta0
    implicit none
    complex, dimension(-ntgrid:,:,:), intent(in) :: phi, apar, phinew, aparnew
    complex, dimension(-ntgrid:,:,:), intent(out) :: epar
    complex :: phi_m, apar_m

    integer :: ig, ik, it

    do ik = 1, naky
       do it = 1, ntheta0
          do ig = -ntgrid, ntgrid-1
             ! ignoring decentering in time and space for now
             phi_m = 0.5*(phi(ig+1,it,ik)-phi(ig,it,ik) + &
                  phinew(ig+1,it,ik)-phinew(ig,it,ik))*fphi
             apar_m = 0.5*(aparnew(ig+1,it,ik)+aparnew(ig,it,ik) &
                  -apar(ig+1,it,ik)-apar(ig,it,ik))*fapar

             epar(ig,it,ik) = -phi_m/delthet(ig)* &
                  0.5*(abs(gradpar(ig)) + abs(gradpar(ig+1))) &
                  -apar_m/tunits(ik)/code_dt
          end do
       end do
    end do

  end subroutine get_epar

  !> Calculate and write \(\delta B\), overwriting existing values
  !>
  !> @note No netCDF output
  subroutine do_write_final_db(write_text)
    use file_utils, only: open_output_file, close_output_file
    use mp, only: proc0
    use theta_grid, only: ntgrid, gradpar, delthet, bmag, theta
    use kt_grids, only: ntheta0, naky, aky, akx
    use fields_arrays, only: phinew, aparnew, apar
    use gs2_time, only: code_dt
    implicit none
    !> Write to text file
    logical, intent(in) :: write_text

    complex, dimension (-ntgrid:ntgrid, ntheta0, naky) :: db
    complex, dimension (ntheta0, naky) :: dbfac
    integer :: ik, it, ig, unit

    if (.not. proc0) return
    if (.not. write_text) return

    !Calculate db
    do ik = 1, naky
      do it = 1, ntheta0
        if (abs(apar(1, it, ik)) <= epsilon(0.0) &
            .or. abs(aparnew(1, it, ik)) <= epsilon(0.0)) then
          db(:, it, ik) = 0.0
          cycle
        end if

        dbfac(it,ik) = 1./sum(delthet/bmag/gradpar)/maxval(abs(phinew(:,it,ik)),1) &
             * abs(log(aparnew(1,it,ik)/apar(1,it,ik)))/code_dt
        ig = -ntgrid
        db(ig, it, ik) = aparnew(ig,it,ik)*delthet(ig)/bmag(ig)/gradpar(ig)*dbfac(it,ik)
        do ig = -ntgrid+1, ntgrid-1
          db(ig, it, ik) = db(ig-1, it, ik) + aparnew(ig,it,ik)*delthet(ig)/bmag(ig)/gradpar(ig)*dbfac(it,ik)
        end do
      end do
    end do

    call open_output_file (unit, ".db")
    do ik = 1, naky
      do it = 1, ntheta0
        do ig = -ntgrid, ntgrid-1
          write (unit, "(5(1x,e12.5))") &
               theta(ig), aky(ik), akx(it), real(db(ig, it,ik)), aimag(db(ig, it, ik))
        end do
        write (unit, "()")
      end do
    end do
    call close_output_file (unit)
  end subroutine do_write_final_db

  !> Calculate and write the various low-order moments, overwriting existing values
  !>
  !> Text files contain moments normalised by phase factor (see
  !> [[gs2_diagnostics_knobs:write_eigenfunc]] for details), the magnitudes, and
  !> field-line averages.
  !>
  !> NetCDF files contain just the full values
  subroutine do_write_final_moments(phi0_in, use_normalisation, write_text, file_id)
    use theta_grid, only: ntgrid, theta
    use kt_grids, only: ntheta0, naky, aky, akx, theta0
    use species, only: nspec, spec
    use diagnostics_moments, only: getmoms
    use file_utils, only: open_output_file, close_output_file
    use mp, only: proc0
    use fields_arrays, only: phinew, bparnew
    use gs2_io, only: nc_final_moments
    use volume_averages, only: average_theta
    implicit none
    !> Pre-calculated phase
    complex, dimension (:, :), intent(in) :: phi0_in
    !> Use [[phi0_in]] as normalisation
    logical, intent(in) :: use_normalisation
    !> Write to text file
    logical, intent(in) :: write_text
    !> NetCDF ID of the file to write to
    integer, intent(in) :: file_id

    complex, dimension (:,:,:,:), allocatable :: ntot, density, upar, tpar, tperp
    complex, dimension (:,:,:,:), allocatable :: qparflux, pperpj1, qpperpj1
    complex, dimension (:,:), allocatable :: phi0
    complex :: phi_tmp, ntot_tmp, density_tmp, upar_tmp, tpar_tmp, tperp_tmp
    real, dimension (:, :), allocatable :: phi02
    integer :: is, ik, it, ig, unit

    allocate (ntot(-ntgrid:ntgrid,ntheta0,naky,nspec))
    allocate (density(-ntgrid:ntgrid,ntheta0,naky,nspec))
    allocate (upar(-ntgrid:ntgrid,ntheta0,naky,nspec))
    allocate (tpar(-ntgrid:ntgrid,ntheta0,naky,nspec))
    allocate (tperp(-ntgrid:ntgrid,ntheta0,naky,nspec))
    allocate (qparflux(-ntgrid:ntgrid,ntheta0,naky,nspec))
    allocate (pperpj1(-ntgrid:ntgrid,ntheta0,naky,nspec))
    allocate (qpperpj1(-ntgrid:ntgrid,ntheta0,naky,nspec))

    ! Calculate moments -- NOTE: This contains MPI collectives, so we
    ! can't return early if we're not proc0
    call getmoms (phinew, bparnew, ntot, density, upar, tpar, tperp, qparflux, pperpj1, qpperpj1)

    if (.not. proc0) return

    call nc_final_moments (file_id, ntot, density, upar, tpar, tperp, qparflux, pperpj1, qpperpj1)

    if (write_text) then
      !Setup the phase factor
      allocate(phi0(ntheta0,naky),phi02(ntheta0,naky))
      if (use_normalisation) then
        phi0 = phi0_in
      else
        phi0 = 1.
      endif
      phi02 = real(phi0*conjg(phi0))

      !Write out the moments normalised by phase factor
      call open_output_file (unit, ".moments")
      do is  = 1, nspec
        do ik = 1, naky
          do it = 1, ntheta0
            do ig = -ntgrid, ntgrid
              write (unit, "(15(1x,e12.5))") &
                   theta(ig), aky(ik), akx(it), &
                   ntot(ig,it,ik,is)/phi0(it,ik), &
                   density(ig,it,ik,is)/phi0(it,ik), &
                   upar(ig,it,ik,is)/phi0(it,ik), &
                   tpar(ig,it,ik,is)/phi0(it,ik), &
                   tperp(ig,it,ik,is)/phi0(it,ik), &
                   theta(ig) - theta0(it,ik), &
                   real(is)
            end do
            write (unit, "()")
          end do
        end do
      end do
      call close_output_file (unit)

      !Write out magnitude of moments
      call open_output_file (unit, ".mom2")
      do is  = 1, nspec
        do ik = 1, naky
          do it = 1, ntheta0
            do ig = -ntgrid, ntgrid
              write (unit, "(15(1x,e12.5))") &
                   theta(ig), aky(ik), akx(it), &
                   real(ntot(ig,it,ik,is)*conjg(ntot(ig,it,ik,is)))/phi02(it,ik), &
                   real(density(ig,it,ik,is)*conjg(density(ig,it,ik,is)))/phi02(it,ik), &
                   real(upar(ig,it,ik,is)*conjg(upar(ig,it,ik,is)))/phi02(it,ik), &
                   real(tpar(ig,it,ik,is)*conjg(tpar(ig,it,ik,is)))/phi02(it,ik), &
                   real(tperp(ig,it,ik,is)*conjg(tperp(ig,it,ik,is)))/phi02(it,ik), &
                   theta(ig) - theta0(it,ik), &
                   real(is)
            end do
            write (unit, "()")
          end do
        end do
      end do
      call close_output_file (unit)

      !Write out the field line averaged moments
      call open_output_file (unit, ".amoments")
      write (unit,*) 'type    kx     re(phi)    im(phi)    re(ntot)   im(ntot)   ',&
           &'re(dens)   im(dens)   re(upar)   im(upar)   re(tpar)',&
           &'   im(tpar)   re(tperp)  im(tperp)'

      do is  = 1, nspec
        do it = 2, ntheta0/2+1
          call average_theta(phinew(-ntgrid:ntgrid,it,1),phi_tmp)
          call average_theta(ntot(-ntgrid:ntgrid,it,1,is),ntot_tmp)
          call average_theta(density(-ntgrid:ntgrid,it,1,is),density_tmp)
          call average_theta(upar(-ntgrid:ntgrid,it,1,is),upar_tmp)
          call average_theta(tpar(-ntgrid:ntgrid,it,1,is),tpar_tmp)
          call average_theta(tperp(-ntgrid:ntgrid,it,1,is),tperp_tmp)

          write (unit, "(i2,14(1x,e10.3))") spec(is)%type, akx(it), &
               phi_tmp, ntot_tmp, density_tmp, upar_tmp, tpar_tmp, tperp_tmp
        end do
      end do
      call close_output_file (unit)

      deallocate(phi0,phi02)
    end if
    
    deallocate (ntot, density, upar, tpar, tperp, qparflux, pperpj1, qpperpj1)
  end subroutine do_write_final_moments

  !> Write the right-hand sides of the field equations, overwriting
  !> existing values
  subroutine do_write_final_antot(write_text, file_id)
    use file_utils, only: open_output_file, close_output_file
    use mp, only: proc0
    use kt_grids, only: ntheta0, naky, theta0, aky
    use theta_grid, only: theta, ntgrid
    use gs2_io, only: nc_final_an
    use dist_fn, only: getan
    use dist_fn_arrays, only: antot, antota, antotp
    implicit none
    !> Write to text file
    logical, intent(in) :: write_text
    !> NetCDF ID of the file to write to
    integer, intent(in) :: file_id
    integer :: ik, it, ig, unit

    call getan (antot, antota, antotp)

    if (.not. proc0) return
    call nc_final_an (file_id, antot, antota, antotp)

    if (write_text) then
      call open_output_file (unit, ".antot")
      do ik = 1, naky
        do it = 1, ntheta0
          do ig = -ntgrid, ntgrid
            write (unit, "(10(1x,e12.5))") &
                 theta(ig), theta0(it,ik), aky(ik), &
                 antot(ig,it,ik), &
                 antota(ig,it,ik), &
                 antotp(ig,it,ik), &
                 theta(ig) - theta0(it,ik)
          end do
          write (unit, "()")
        end do
      end do
      call close_output_file (unit)
    end if
  end subroutine do_write_final_antot

  !> FIXME : Add documentation
  subroutine do_write_gs(write_text)
    use file_utils, only: open_output_file, close_output_file
    use mp, only: proc0, sum_reduce, iproc, nproc
    use gs2_transforms, only: transform2, inverse2
    use fields_arrays, only: apar, phi
    use kt_grids, only: naky, ntheta0, akx, aky, nx, ny
    use theta_grid, only: ntgrid, delthet, jacob, gradpar, nperiod
    use constants, only: pi, zi
    use splines, only: fitp_surf1, fitp_surf2
    implicit none
    !> Write to text file
    logical, intent(in) :: write_text

    real, dimension (:), allocatable :: total
    real, dimension (:,:,:), allocatable :: bxf, byf, vxf, vyf, bxfsavg, byfsavg
    real, dimension (:,:,:), allocatable :: bxfs, byfs, vxfs, vyfs, rvx, rvy, rx, ry
    real, dimension (:), allocatable :: xx4, yy4, dz
    real, dimension (:,:), allocatable :: bxs, bys, vxs, vys
    real, dimension (:,:), allocatable :: bxsavg, bysavg
    complex, dimension (:,:,:), allocatable :: bx, by, vx, vy, vx2, vy2
    real, dimension (:), allocatable :: stemp, zx1, zxm, zy1, zyn, xx, yy
    real :: zxy11, zxym1, zxy1n, zxymn, rxt, ryt, bxt, byt, L_x, L_y
    real, dimension (2*ntgrid) :: kpar
    integer :: nnx, nny, nnx4, nny4, ulim, llim, iblock, i, unit
    integer :: ik, it, ig, ierr

    if (.not. write_text) return

    nny = 2*ny
    nnx = 2*nx
    nnx4 = nnx+4
    nny4 = nny+4

    allocate (bx(-ntgrid:ntgrid,ntheta0,naky))
    allocate (by(-ntgrid:ntgrid,ntheta0,naky))
    allocate (vx(-ntgrid:ntgrid,ntheta0,naky))
    allocate (vy(-ntgrid:ntgrid,ntheta0,naky))

    do ik=1,naky
       do it=1,ntheta0
          do ig=-ntgrid, ntgrid
             bx(ig,it,ik) =  zi*aky(ik)*apar(ig,it,ik)
             by(ig,it,ik) = -zi*akx(it)*apar(ig,it,ik)
             vx(ig,it,ik) = -zi*aky(ik)*phi(ig,it,ik)
             vy(ig,it,ik) =  zi*akx(it)*phi(ig,it,ik)
          end do
       end do
    end do

    allocate (bxf(nnx,nny,-ntgrid:ntgrid))
    allocate (byf(nnx,nny,-ntgrid:ntgrid))
    allocate (vxf(nnx,nny,-ntgrid:ntgrid))
    allocate (vyf(nnx,nny,-ntgrid:ntgrid))

    call transform2 (bx, bxf, nny, nnx)
    call transform2 (by, byf, nny, nnx)
    call transform2 (vx, vxf, nny, nnx)
    call transform2 (vy, vyf, nny, nnx)

    ! fields come out as (x, y, z)

    deallocate (bx, by)

    allocate (   bxfs(nnx4, nny4, -ntgrid:ntgrid))
    allocate (   byfs(nnx4, nny4, -ntgrid:ntgrid))
    allocate (bxfsavg(nnx4, nny4, -ntgrid:ntgrid))
    allocate (byfsavg(nnx4, nny4, -ntgrid:ntgrid))
    allocate (   vxfs(nnx4, nny4, -ntgrid:ntgrid))
    allocate (   vyfs(nnx4, nny4, -ntgrid:ntgrid))

    do ig=-ntgrid,ntgrid
       do ik=1,2
          do it=3,nnx4-2
             bxfs(it,ik,ig) = bxf(it-2,nny-2+ik,ig)
             byfs(it,ik,ig) = byf(it-2,nny-2+ik,ig)
             vxfs(it,ik,ig) = vxf(it-2,nny-2+ik,ig)
             vyfs(it,ik,ig) = vyf(it-2,nny-2+ik,ig)

             bxfs(it,nny4-2+ik,ig) = bxf(it-2,ik,ig)
             byfs(it,nny4-2+ik,ig) = byf(it-2,ik,ig)
             vxfs(it,nny4-2+ik,ig) = vxf(it-2,ik,ig)
             vyfs(it,nny4-2+ik,ig) = vyf(it-2,ik,ig)
          end do
       end do
       do ik=3,nny4-2
          do it=3,nnx4-2
             bxfs(it,ik,ig) = bxf(it-2,ik-2,ig)
             byfs(it,ik,ig) = byf(it-2,ik-2,ig)
             vxfs(it,ik,ig) = vxf(it-2,ik-2,ig)
             vyfs(it,ik,ig) = vyf(it-2,ik-2,ig)
          end do
       end do
       do ik=1,nny4
          do it=1,2
             bxfs(it,ik,ig) = bxfs(nnx4-4+it,ik,ig)
             byfs(it,ik,ig) = byfs(nnx4-4+it,ik,ig)
             vxfs(it,ik,ig) = vxfs(nnx4-4+it,ik,ig)
             vyfs(it,ik,ig) = vyfs(nnx4-4+it,ik,ig)

             bxfs(nnx4-2+it,ik,ig) = bxfs(it+2,ik,ig)
             byfs(nnx4-2+it,ik,ig) = byfs(it+2,ik,ig)
             vxfs(nnx4-2+it,ik,ig) = vxfs(it+2,ik,ig)
             vyfs(nnx4-2+it,ik,ig) = vyfs(it+2,ik,ig)
          end do
       end do
    end do

    deallocate (vxf, vyf)

    allocate (xx4(nnx4), xx(nnx))
    allocate (yy4(nny4), yy(nny))

    L_x = 2.0*pi/akx(2)
    L_y = 2.0*pi/aky(2)

    do it = 1, nnx
       xx4(it+2) = real(it-1)*L_x/real(nnx)
       xx(it) = real(it-1)*L_x/real(nnx)
    end do
    do it=1,2
       xx4(it) = xx4(nnx4-4+it)-L_x
       xx4(nnx4-2+it) = xx4(it+2)+L_x
    end do

    do ik = 1, nny
       yy4(ik+2) = real(ik-1)*L_y/real(nny)
       yy(ik)    = real(ik-1)*L_y/real(nny)
    end do
    do ik=1,2
       yy4(ik) = yy4(nny4-4+ik)-L_y
       yy4(nny4-2+ik) = yy4(ik+2)+L_y
    end do

    allocate (dz(-ntgrid:ntgrid))
    dz = delthet*jacob

    allocate (   bxs(3*nnx4*nny4,-ntgrid:ntgrid)) ; bxs = 0.
    allocate (   bys(3*nnx4*nny4,-ntgrid:ntgrid)) ; bys = 0.
    allocate (   vxs(3*nnx4*nny4,-ntgrid:ntgrid)) ; vxs = 0.
    allocate (   vys(3*nnx4*nny4,-ntgrid:ntgrid)) ; vys = 0.

    allocate (bxsavg(3*nnx4*nny4,-ntgrid:ntgrid))
    allocate (bysavg(3*nnx4*nny4,-ntgrid:ntgrid))

    allocate (stemp(nnx4+2*nny4))
    allocate (zx1(nny4), zxm(nny4), zy1(nnx4), zyn(nnx4))

    do ig=-ntgrid, ntgrid
       call fitp_surf1(nnx4, nny4, xx4, yy4, bxfs(:,:,ig), &
            nnx4, zx1, zxm, zy1, zyn, zxy11, zxym1, zxy1n, zxymn, &
            255, bxs(:,ig), stemp, 1., ierr)

       call fitp_surf1(nnx4, nny4, xx4, yy4, byfs(:,:,ig), &
            nnx4, zx1, zxm, zy1, zyn, zxy11, zxym1, zxy1n, zxymn, &
            255, bys(:,ig), stemp, 1., ierr)

       call fitp_surf1(nnx4, nny4, xx4, yy4, vxfs(:,:,ig), &
            nnx4, zx1, zxm, zy1, zyn, zxy11, zxym1, zxy1n, zxymn, &
            255, vxs(:,ig), stemp, 1., ierr)

       call fitp_surf1(nnx4, nny4, xx4, yy4, vyfs(:,:,ig), &
            nnx4, zx1, zxm, zy1, zyn, zxy11, zxym1, zxy1n, zxymn, &
            255, vys(:,ig), stemp, 1., ierr)
    end do

    deallocate (zx1, zxm, zy1, zyn, stemp)

    do ig=-ntgrid, ntgrid-1
       bxsavg(:,ig) = 0.5*(bxs(:,ig)+bxs(:,ig+1))
       bysavg(:,ig) = 0.5*(bys(:,ig)+bys(:,ig+1))

       bxfsavg(:,:,ig) = 0.5*(bxfs(:,:,ig)+bxfs(:,:,ig+1))
       byfsavg(:,:,ig) = 0.5*(byfs(:,:,ig)+byfs(:,:,ig+1))
    end do

    ! now, integrate to find a field line

    allocate ( rx(nnx,nny,-ntgrid:ntgrid))
    allocate ( ry(nnx,nny,-ntgrid:ntgrid))
    allocate (rvx(nnx,nny,-ntgrid:ntgrid)) ; rvx = 0.
    allocate (rvy(nnx,nny,-ntgrid:ntgrid)) ; rvy = 0.

    do ik=1,nny
       do it=1,nnx
          rx(it,ik,-ntgrid) = xx(it)
          ry(it,ik,-ntgrid) = yy(ik)
       end do
    end do

    !Should these not come from layouts object?
    iblock = (nnx*nny-1)/nproc + 1
    llim = 1 + iblock * iproc
    ulim = min(nnx*nny, llim+iblock-1)

    do i=llim, ulim
       it = 1 + mod(i-1, nnx)
       ik = 1 + mod((i-1)/nnx, nny)

       ig = -ntgrid

       rxt = rx(it,ik,ig)
       ryt = ry(it,ik,ig)

       rvx(it,ik,ig) = fitp_surf2(rxt, ryt, nnx4, nny4, xx4, yy4, vxfs(:,:,ig), nnx4, vxs(:,ig), 1.)
       rvy(it,ik,ig) = fitp_surf2(rxt, ryt, nnx4, nny4, xx4, yy4, vyfs(:,:,ig), nnx4, vys(:,ig), 1.)

       do ig=-ntgrid,ntgrid-1

          bxt = fitp_surf2(rxt, ryt, nnx4, nny4, xx4, yy4, bxfs(:,:,ig), nnx4, bxs(:,ig), 1.)
          byt = fitp_surf2(rxt, ryt, nnx4, nny4, xx4, yy4, byfs(:,:,ig), nnx4, bys(:,ig), 1.)

          rxt = rx(it,ik,ig) + 0.5*dz(ig)*bxt
          ryt = ry(it,ik,ig) + 0.5*dz(ig)*byt

          if (rxt > L_x) rxt = rxt - L_x
          if (ryt > L_y) ryt = ryt - L_y

          if (rxt < 0.) rxt = rxt + L_x
          if (ryt < 0.) ryt = ryt + L_y

          bxt = fitp_surf2(rxt, ryt, nnx4, nny4, xx4, yy4, bxfsavg(:,:,ig), nnx4, bxsavg(:,ig), 1.)
          byt = fitp_surf2(rxt, ryt, nnx4, nny4, xx4, yy4, byfsavg(:,:,ig), nnx4, bysavg(:,ig), 1.)

          rxt = rx(it,ik,ig) + dz(ig)*bxt
          ryt = ry(it,ik,ig) + dz(ig)*byt

          if (rxt > L_x) rxt = rxt - L_x
          if (ryt > L_y) ryt = ryt - L_y

          if (rxt < 0.) rxt = rxt + L_x
          if (ryt < 0.) ryt = ryt + L_y

          rx(it,ik,ig+1) = rxt
          ry(it,ik,ig+1) = ryt

          rvx(it,ik,ig+1) = fitp_surf2(rxt, ryt, nnx4, nny4, xx4, yy4, vxfs(:,:,ig+1), nnx4, vxs(:,ig+1), 1.)
          rvy(it,ik,ig+1) = fitp_surf2(rxt, ryt, nnx4, nny4, xx4, yy4, vyfs(:,:,ig+1), nnx4, vys(:,ig+1), 1.)
       end do
    end do

    deallocate (bxfs, byfs, bxfsavg, byfsavg, vxfs, vyfs)
    deallocate (rx, ry, bxs, bys, vxs, vys, bxsavg, bysavg)

    allocate (total(2*nnx*nny*(2*ntgrid+1)))

    i=1
    do ig=-ntgrid,ntgrid
       do ik=1,nny
          do it=1,nnx
             total(i) = rvx(it,ik,ig)
             total(i+1) = rvy(it,ik,ig)
             i = i + 2
          end do
       end do
    end do

    call sum_reduce(total, 0)

    i=1
    do ig=-ntgrid,ntgrid
       do ik=1,nny
          do it=1,nnx
             rvx(it,ik,ig) = total(i)
             rvy(it,ik,ig) = total(i+1)
             i = i + 2
          end do
       end do
    end do

    call inverse2 (rvx, vx, nny, nnx)
    call inverse2 (rvy, vy, nny, nnx)

    allocate (vx2(-ntgrid:ntgrid,ntheta0,naky))
    allocate (vy2(-ntgrid:ntgrid,ntheta0,naky))

    call par_spectrum (vx, vx2)
    call par_spectrum (vy, vy2)

    if (.not. proc0) return

    call open_output_file (unit, ".gs")
    do ig = 1, ntgrid
      kpar(ig) = (ig-1)*gradpar(ig)/real(2*nperiod-1)
      kpar(2*ntgrid-ig+1)=-(ig)*gradpar(ig)/real(2*nperiod-1)
    end do
    do ik = 1, naky
      do it = 1, ntheta0
        do ig = ntgrid+1,2*ntgrid
          write (unit, "(9(1x,e12.5))") &
               kpar(ig), aky(ik), akx(it), &
               real(vx2(ig-ntgrid-1,it,ik)), &
               real(vy2(ig-ntgrid-1,it,ik))
        end do
        do ig = 1, ntgrid
          write (unit, "(9(1x,e12.5))") &
               kpar(ig), aky(ik), akx(it), &
               real(vx2(ig-ntgrid-1,it,ik)), &
               real(vy2(ig-ntgrid-1,it,ik))
        end do
        write (unit, "()")
      end do
    end do
    call close_output_file (unit)
    deallocate (vx2, vy2)

    deallocate (bxf, byf, xx4, xx, yy4, yy, dz, total)
    deallocate (vx, vy, rvx, rvy)
  end subroutine do_write_gs
end module diagnostics_final_routines