subroutine frantic_radius(ispline,inxb_user,zxb_user,zvolxb_user,zrmajxb_user)

  use iso_c_binding, only: fp => c_double
  use ezspline_type, only: ezspline1
  use ezspline, only: ezspline_init, ezspline_free, ezspline_setup, ezspline_interp
  use frantic_module

  !  set up FRANTIC radial grid, based on zone volumes...

  implicit none

  integer, intent(in) :: ispline     ! =1 for splines of Vol(x), Rmaj(x)
  ! =0 for Hermite.
  ! if =1 enforce Vol'(0)=0, Rmaj'(0)=0.

  integer, intent(in) :: inxb_user   ! no. of user grid bdys
  real(fp), intent(in) :: zxb_user(inxb_user)  ! grid bdys (asdending in [0,1])
  real(fp), intent(in) :: zvolxb_user(inxb_user)  ! volumes inside bdys (m**3)
  real(fp), intent(in) :: zRmajxb_user(inxb_user) ! major radius at bdys (m)

  !--------------------------------------
  ! local

  real(fp) :: zpi,zr0vec(nzonec+1)
  integer :: iok,j,bcs(2)

  type(ezspline1) :: spline_vol   ! V(x) spline
  type(ezspline1) :: spline_rmj   ! R(x) spline

  zpi = 3.1415926535897931_fp

  !--------------------------------------
  !  allocate module arrays & copy data

  nxb_user = inxb_user
  nx_user = nxb_user - 1
  allocate(x_user(nx_user),xb_user(nxb_user))
  allocate(volxb_user(nxb_user),Rmajxb_user(nxb_user))

  xb_user = zxb_user
  x_user = (xb_user(1:nxb_user-1)+xb_user(2:nxb_user))/2
  volxb_user = zvolxb_user*1.0e6_fp   ! cm**3 inside FRANTIC
  Rmajxb_user = zRmajxb_user*1.0e2_fp ! cm inside FRANTIC

  !--------------------------------------
  !  create splines of Vol(x) and Rmaj(x)

  if(ispline.eq.1) then
    bcs = (/1, 0/)
  else
    bcs = (/0, 0/)
  end if

  call ezspline_init(spline_vol,nxb_user,bcs,iok)
  if(iok.ne.0) call errmsg_exit(' ?ezspline_init error in frantic.')
  call ezspline_init(spline_rmj,nxb_user,bcs,iok)
  if(iok.ne.0) call errmsg_exit(' ?ezspline_init error in frantic.')

  if(ispline.eq.1) then
    spline_vol%bcval1min=0  ! dV/dx = 0
    spline_rmj%bcval1min=0  ! dR/dx = 0
  else
    spline_vol%isHermite=1
    spline_rmj%isHermite=1
  end if

  spline_vol%x1 = xb_user
  spline_rmj%x1 = xb_user

  call ezspline_setup(spline_vol,volxb_user,iok)
  if(iok.ne.0) call errmsg_exit(' ?ezspline_setup error in frantic.')
  call ezspline_setup(spline_rmj,rmajxb_user,iok)
  if(iok.ne.0) call errmsg_exit(' ?ezspline_setup error in frantic.')

  !--------------------------------------

  call ezspline_interp(spline_vol,nzonec+1,xb,tvol,iok)
  if(iok.ne.0) call errmsg_exit('?frantic_radius: ezspline_interp error(vol).')

  call ezspline_interp(spline_rmj,nzonec+1,xb,zr0vec,iok)
  if(iok.ne.0) call errmsg_exit('?frantic_radius: ezspline_interp error(rmj).')

  rbound(1)=0
  do j=1,nzonec
    rbound(j+1)=sqrt(tvol(j+1)/(zpi*2*zpi*zr0vec(j+1)))
    ! minor radii of surfaces...(cm) ~ sqrt ( [V/(2*pi*R)]/pi )
    VOL(j)=zpi*(RBOUND(j+1)*RBOUND(j+1)-RBOUND(j)*RBOUND(j))
  end do

  aminor = rbound(nzonec+1)

  !  Radius at zone centers-- for angular momentum treatment

  call ezspline_interp(spline_rmj,nzonec,x,rmjrmp,iok)
  if(iok.ne.0) call errmsg_exit('?frantic_radius: ezspline_interp error(rmj).')

  call ezspline_free(spline_rmj,iok)
  if(iok.ne.0) call errmsg_exit('?frantic_radius: ezspline_free error(rmj).')
  call ezspline_free(spline_vol,iok)
  if(iok.ne.0) call errmsg_exit('?frantic_radius: ezspline_free error(vol).')

  return
end subroutine frantic_radius

subroutine frantic_profile_set(indx,spline_n,spline_t,spline_v)

  use iso_c_binding, only: fp => c_double
  use physconst_mod, only: zero
  use ezspline, only: ezspline_interp
  use ezspline_type, only: ezspline1
  use frantic_module

  !  set up FRANTIC density temperature and velocity profile for
  ! indicated specie:
  !    indx=0 for ELECTRONS
  !    indx=ig for thermal specie #ig
  !    indx=mig+ix for IMPURITY

  implicit none

  integer, intent(in) :: indx       ! specie index
  type(ezspline1), intent(in) :: spline_n   ! n(x) spline
  type(ezspline1), intent(in) :: spline_t   ! T(x) spline
  type(ezspline1), intent(in) :: spline_v   ! v(x) spline

  !-----------------------------------

  integer :: iok,indu,indux,i
  logical :: impur,irenorm
  real(fp) :: zbuf(nzonec),zbufn(nzonec),znlim
  real(fp), parameter :: C100TH = 0.010_fp

  !-----------------------------------
  if(indx.eq.0) then
    indu=1  ! electron data
    impur=.FALSE.
  else if(indx.le.mig) then
    indu=indx+2  ! non-impurity ion data
    impur=.FALSE.
  else if(indx.le.mix-2) then
    indu=2       ! impurity data
    indux=indx+2
    impur=.TRUE.
  else
    write(lun6,*) ' %FRANTIC -- frantic_profile_set warning: ', &
         'unexpected specie index: ',indx
    return
  end if

  if(impur) then
    if(npro_set(indux)) then
      write(lun6,*) ' %FRANTIC -- frantic_profile_set warning: ', &
           'profiles already set for impurity: ',indux
      return
    end if
  else
    if(npro_set(indu)) then
      write(lun6,*) ' %FRANTIC -- frantic_profile_set warning: ', &
           'profiles already set for specie: ',indx
      return
    end if
  end if

  call ezspline_interp(spline_n,nzonec,x,zbufn,iok)  ! in m**-3
  if(iok.ne.0) call errmsg_exit( &
       '?frantic_profile_set: ezspline_interp error on spline_n')
  if(impur) then
    lzpflag=.TRUE.
    zbufn=1.0e-6_fp*zbufn
    den(1:nzonec,2)=den(1:nzonec,2)+zbufn
    xzimpj(1:nzonec)=xzimpj(1:nzonec)+xzplas(indux)*zbufn
    aimpj(1:nzonec)=aimpj(1:nzonec)+aplas(indux)*zbufn
  else
    den(1:nzonec,indu)=1.0e-6_fp*zbufn               ! convert to cm**-3
    if(nimp.eq.0) den(1:nzonec,2)=0
  end if

  call ezspline_interp(spline_t,nzonec,x,zbuf,iok) ! in KeV
  if(iok.ne.0) call errmsg_exit( &
       '?frantic_profile_set: ezspline_interp error on spline_t')
  if(impur) then
    temp(1:nzonec,indu)=temp(1:nzonec,indu)+zbufn*zbuf
  else
    temp(1:nzonec,indu)=zbuf
    if(nimp.eq.0) temp(1:nzonec,2)=zbuf
  end if

  call ezspline_interp(spline_v,nzonec,x,zbuf,iok) ! in m/sec
  if(iok.ne.0) call errmsg_exit( &
       '?frantic_profile_set: ezspline_interp error on spline_v')
  if(impur) then
    vphiz(1:nzonec,indu)=vphiz(1:nzonec,indu)+100.0_fp*zbuf*zbufn
  else
    vphiz(1:nzonec,indu)=100.0_fp*zbuf             ! convert to cm/sec
    if(nimp.eq.0) vphiz(1:nzonec,2)=100.0_fp*zbuf
  end if

  if(impur) then
    npro_set(indux)=.TRUE.
    irenorm=.TRUE.
    do i=mig+3,mig+2+nimp
      irenorm=irenorm.AND.npro_set(i)
    end do
    if(irenorm) then
      npro_set(indu)=.TRUE.  ! impurity data all in hand now
      do i=1,nzonec
        if(den(i,indu).le.0) then
          temp(i,indu)=1
          vphiz(i,indu)=0
          xzimpj(i)=8.888888888_fp
          aimpj(i)=2*xzimpj(i)
        else
          temp(i,indu)=temp(i,indu)/den(i,indu)
          vphiz(i,indu)=vphiz(i,indu)/den(i,indu)
          xzimpj(i)=xzimpj(i)/den(i,indu)
          aimpj(i)=aimpj(i)/den(i,indu)
        end if
      end do
    end if
  else
    npro_set(indu)=.TRUE.
  end if

  !
  ! set limits on temperature
  !
  temp(1:nzonec,indu) = max(C100TH, temp(1:nzonec,indu))  ! limit by 10 eV

  !
  ! set limits on density
  !
  znlim = 1.e-4*max(ZERO, maxval(den(1:nzonec,indu)))    ! max dynamic range = 1.e-4
  den(1:nzonec,indu) = max(znlim, den(1:nzonec,indu))

  return
end subroutine frantic_profile_set

subroutine frantic_impurity_zaprof(spline_Z,spline_A,ierr)

  use iso_c_binding, only: fp => c_double
  use ezspline_type, only: ezspline1
  use ezspline, only: ezspline_interp
  use frantic_module

  !  set up FRANTIC impurity avg Z and avg A profiles

  implicit none

  type(ezspline1), intent(in) :: spline_Z   ! Z(x) spline
  type(ezspline1), intent(in) :: spline_A   ! A(x) spline
  integer, intent(out) :: ierr

  !-----------------------------------

  integer iok

  !-----------------------------------

  ierr=0

  if(nimp.ne.1) then
    write(lun6,*) ' ?frantic_impurity_zaprof:  Nimp = ',nimp
    write(lun6,*) '  this routine can only be used if the number ', &
         'of impurities is exactly one.'
    ierr=99
    return
  end if

  call ezspline_interp(spline_Z,nzonec,x,xzimpj(1:nzonec),iok)
  if(iok.ne.0) call errmsg_exit( &
       '?frantic_impurity_zaprof: ezspline_interp error on spline_Z')

  call ezspline_interp(spline_A,nzonec,x,aimpj(1:nzonec),iok)
  if(iok.ne.0) call errmsg_exit( &
       '?frantic_impurity_zaprof: ezspline_interp error on spline_A')

  if((aimpj(1).lt.1.5*xzimpj(1)).or.xzimpj(1)<1.) then
    write(lun6,*) ' ?frantic_impurity_zaprof:  A < 1.5*Z on axis:'
    write(lun6,*) '  aimpj(1)=',aimpj(1),'  xzimpj(1)=',xzimpj(1)
    write(lun6,*) ' ***> likely error in profile spline arguments <*** '
    ierr=1
  else
    lzpflag = .TRUE.   ! signals presence of Z & A impurity profile data.
    xzplas(2)=6.66666666666666_fp
    aplas(2)=2*xzplas(2)
  end if

  return
end subroutine frantic_impurity_zaprof

subroutine frantic_neutral_specie(z0,a0,i0map)

  use iso_c_binding, only: fp => c_double
  use frantic_module

  implicit none

  !  define species of neutral source;
  !  return table indicating which neutral species will be created
  !  (via charge exchange) due to this source...

  real(fp), intent(in) :: z0           ! Z of neutral specie
  real(fp), intent(in) :: a0           ! A of neutral specie

  integer, intent(out) :: i0map(mig) ! list of neutral species:

  !  if i0map(ig)=0 on output, then, gas specie (ig) does not have
  !     neutrals created due to this neutral source (because Z(ig) > z0).
  !  if i0map(ig)=ik then gas specie (ig) is the ik'th neutal specie
  !     created due to this neutral source.

  !-------------------------------------------------

  integer ig,ik,iz0
  real(fp) zgmin

  real(fp), parameter :: zsmall = 0.1_fp

  !-------------------------------------------------

  i0map=0

  nspec=0
  ik=0

  ninldx=0
  zgmin=999999  ! a big atomic number

  nneut_s0p=nneut_s0
  do ig=1,mig
    if(xzplas(ig+2).le.(z0+zsmall)) then
      ik=ik+1
      i0map(ig)=ik
      nspec(ik)=ig+2  ! corresponding ion specie index
      if(xzplas(ig+2).lt.zgmin) then
        zgmin=xzplas(ig+2)
        ninldx=ik    ! index to lowest Z neutral specie
      end if
      if((abs(xzplas(ig+2)-z0).le.zsmall).and. &
           (abs(aplas(ig+2)-a0).le.zsmall)) then
        nneut_s0=ik
      end if
    end if
  end do

  nneut=ik

  iz0=z0+zsmall

  if((nneut_s0p.ne.nneut_s0).or.(nneut_z0p.ne.iz0)) then
    mcflag=.TRUE.  ! update transfer matrix
  else
    mcflag=.FALSE.
  end if

  nneut_z0p=iz0

  ! clear fast ion rate arrays

  bcx=0; bcxs=0; bii=0

  return
end subroutine frantic_neutral_specie

subroutine frantic_bsink_ii(ik,spline_bii)

  use iso_c_binding, only: fp => c_double
  use ezspline_type, only: ezspline1
  use ezspline, only: ezspline_interp
  use frantic_module

  implicit none

  !  set up ionization sink due to effect of fast ions
  !  this quantity summed over all fast ion species

  integer, intent(in) :: ik    ! neutral specie index
  type (ezspline1), intent(in) :: spline_bii   ! bii(x) spline

  integer :: iok  ! error flag

  !-------------------------------------------------------

  call ezspline_interp(spline_bii,nzonec,x,bii(1:nzonec,ik),iok)
  if(iok.ne.0) call errmsg_exit('?frantic_bsink: ezspline_interp (bii) error!')

  return
end subroutine frantic_bsink_ii

subroutine frantic_bsink_cx(ik,isb,spline_bcx)

  use iso_c_binding, only: fp => c_double
  use ezspline_type, only: ezspline1
  use ezspline, only: ezspline_interp
  use frantic_module

  implicit none

  !  set up cx neutral sink due to effect of a single fast ion specie

  integer, intent(in) :: ik,isb    ! neutral specie index; fast specie index
  type (ezspline1), intent(in) :: spline_bcx   ! bcx(x) spline

  integer :: iok  ! error flag

  !-------------------------------------------------------

  call ezspline_interp(spline_bcx,nzonec,x,bcxs(1:nzonec,ik,isb),iok)
  if(iok.ne.0) call errmsg_exit('?frantic_bsink: ezspline_interp (bcx) error!')

  ! compute sum

  bcx(1:nzonec,ik)=bcx(1:nzonec,ik)+bcxs(1:nzonec,ik,isb)

  return
end subroutine frantic_bsink_cx

subroutine frantic_edge_source(zflux,zengy)

  use iso_c_binding, only: fp => c_double
  use frantic_module

  implicit none

  !  set up edge source

  real(fp), intent(in) :: zflux   ! ptcls/m**2/sec
  real(fp), intent(in) :: zengy   ! "Temperature" (KeV) of source

  !------------------------------------------------

  esflag=.TRUE.
  vsflag=.FALSE.

  fluxi0=0
  twall=zengy
  vphi_wall=0
  volsor=0

  fluxi0(nneut_s0)=zflux*1.0e-4_fp   ! ptcls/cm**2/sec in FRANTIC

  return
end subroutine frantic_edge_source

subroutine frantic_edge_vphi(zveloc)

  use iso_c_binding, only: fp => c_double
  use frantic_module

  implicit none

  !  set edge source v_phi
  !  This routine to be called IMMEDIATELY AFTER call to frantic_edge_source
  !  If it is not called, the edge source v_phi is left at zero.

  real(fp), intent(in) :: zveloc  ! toroidal velocity, m/sec

  !------------------------------------------------

  vphi_wall = 100.0_fp*zveloc  ! cm/sec in FRANTIC

  return
end subroutine frantic_edge_vphi

subroutine frantic_volume_source(zflux,spline_volsor)

  use iso_c_binding, only: fp => c_double
  use ezspline_type, only: ezspline1
  use ezspline, only: ezspline_interp
  use frantic_module

  implicit none

  ! set up volume source

  real(fp), intent(in) :: zflux   ! ptcls/m**2/sec edge flux equivalent
  ! to this volume source
  ! if zflux<=0, consider spline_volsor
  ! to already be normalized;

  type(ezspline1), intent(in) :: spline_volsor  
  ! volume source profile shape
  ! renormalized to zflux if zflux > 0
  ! if zflux <= 0, the profile is not
  ! renormalized: units of #/m^3/sec expected!

  !------------------------------------------------

  integer :: iok  ! spline error flag
  integer :: ik,i

  real(fp) :: zvoli,zsumi,zpi,zfac

  !------------------------------------------------
  !  set up volume source

  esflag=.FALSE.
  vsflag=.TRUE.

  fluxi0=0
  twall=0
  volsor=0

  fluxi0(nneut_s0)=zflux*1.0e-4_fp   ! ptcls/cm**2/sec in FRANTIC
  ! normalized equivalent volume source

  ik=nneut_s0

  call ezspline_interp(spline_volsor,nzonec,x,volsor(1:nzonec,ik),iok)
  if(iok.ne.0) call errmsg_exit('?frantic_volume_source: ezspline error.')

  ! renormalize the profile

  zsumi=0
  do i=1,nzonec
    zvoli=vol(i)  ! volume per unit length in cylinder...
    zsumi=zsumi+zvoli*volsor(i,ik)
  end do
  zsumi=zsumi/(aminor*6.2831853071795862_fp) ! convert to edge flux...

  zfac=0.0_fp
  if(zflux.gt.0.0_fp) then
    if(zsumi.gt.0) zfac = fluxi0(nneut_s0)/zsumi
    volsor(1:nzonec,ik)=zfac*volsor(1:nzonec,ik)
  else
    ! NOT renormalized to zflux
    zfac=1.0e-6_fp  ! so convert now to #/cm^3/sec
    volsor(1:nzonec,ik)=zfac*volsor(1:nzonec,ik)
    fluxi0(nneut_s0)=zsumi*zfac
  end if

  return
end subroutine frantic_volume_source

subroutine frantic_source_echo(zA,zZ,zFlux,zEngy,zVphi,ivs_flag,ik)

  use iso_c_binding, only: fp => c_double
  use frantic_module

  implicit none

  real(fp), intent(out) :: zA,zZ   ! specie of neutral source (AMU,Atomic #)
  real(fp), intent(out) :: zFlux   ! magnitude of source (#/m**2/sec)
  real(fp), intent(out) :: zEngy   ! <energy> of source (KeV)
  real(fp), intent(out) :: zVphi   ! toroidal velocity (m/sec)
  logical, intent(out) :: ivs_flag ! volume source flag (T/F)
  integer, intent(out) :: ik     ! index to source neutral specie

  !----------------------------------------

  integer :: i,ink

  real(fp) :: zpi,zvoli,zVsum

  !----------------------------------------

  ik=nneut_s0
  ink=nspec(ik)
  zA=aplas(ink)
  zZ=xzplas(ink)
  zFlux=fluxi0(ik)*1.0e4_fp  ! to MKS

  ivs_flag=vsflag

  if(.not.ivs_flag) then
    zEngy=1.5_fp*twall(ik)
    zVphi=0.01_fp*vphi_wall(ik)  ! to m/s
  else
    zEngy=0
    zVphi=0
    zVsum=0
    do i=1,nzonec
      zvoli=tvol(i+1)-tvol(i)
      zVsum=zVsum+zvoli*volsor(i,ik)
      zEngy=zEngy+zvoli*volsor(i,ik)*1.5_fp*temp(i,ink)
      zVphi=zVphi+zvoli*volsor(i,ik)*vphiz(i,ink)*0.01_fp  ! v_phi -> m/s
    end do
    if(zVsum.gt.0) then
      zEngy=zEngy/zVsum
      zVphi=zVphi/zVsum
    else
      zEngy=0
      zVphi=0
    end if
  end if

  return
end subroutine frantic_source_echo

subroutine frantic_nneut_echo(ineut)

  !  return number of neutral species

  use frantic_module
  implicit none

  integer, intent(out) :: ineut  ! the number

  ineut = nneut

  return
end subroutine frantic_nneut_echo

subroutine frantic_iindx_echo(ik,ink)

  !  return ion species index, given neutral species index

  use frantic_module
  implicit none

  integer, intent(in) :: ik         ! selected neutral specie
  integer, intent(out) :: ink       ! corresponding ion specie returned

  !-----------------------------

  if((ik.ge.1).and.(ik.le.nneut)) then
    ink=nspec(ik)
  else
    ink=0
  end if

  return
end subroutine frantic_iindx_echo

subroutine frantic_za_echo(ik,zA,zZ,zFlux)

  !  return A,Z, flux of indicated neutral specie

  use iso_c_binding, only: fp => c_double
  use frantic_module
  implicit none

  integer, intent(in) :: ik         ! selected neutral specie
  real(fp), intent(out) :: zA,zZ    ! A & Z
  real(fp), intent(out) :: zFlux    ! inFlux (0 except for source specie)

  integer :: ink

  !------------------------------

  if((ik.ge.1).and.(ik.le.nneut)) then
    ink=nspec(ik)

    zA=Aplas(ink)
    zZ=xZplas(ink)

    zFlux=fluxi0(ik)*1.0e4_fp  ! to MKS

  else
    zA=0
    zZ=0
    zFlux=0
  end if

  return
end subroutine frantic_za_echo

subroutine frantic_get_nx_user(izones)

  use frantic_module
  implicit none
  integer, intent(out) :: izones  ! no. of user zones

  ! this is the number of zones in FRANTIC output profiles
  ! after interpolation

  izones = nx_user

  return
end subroutine frantic_get_nx_user

subroutine frantic_set_atomic(i_atomicdata)
! set frantic module with frantic_atomic data model
! default frantic_atomic=1 -- legacy of PREACT atomic physics
!
                        ! 2 -- ADAS atomic physics for H and He beams.
                        !      He beam -- only ground state model available
                        ! 3 -- PREACT ground state atomic physics
                        !      ADAS interaction with impurities
                        ! 4 -- ADAS ground state atomic physics
                        !      PREACT interaction with impurities

  use frantic_module
  implicit none
  integer, intent(in) :: i_atomicdata

  frantic_atomic=i_atomicdata

  return
end subroutine frantic_set_atomic
