program trxpl2ps

  !  DMC Nov 2010: added machine description input capability
  !  DMC July 2010: generate plasma states from TRANSP archives

  !  The functionality is similar to "trxpl", but, this program has no
  !  graphics and no interactive interface.  It is controlled entirely
  !  with command line arguments.  It uses a simple set of trxplib routines
  !  to connect to a run and select the state output times and option--
  !  it illustrates a "subroutine library" interface to TRANSP data in
  !  Plasma State format

  !  this program takes command line arguments.  Syntax:

  !     trxpl2ps FILE|MDS+ <data-path> <output-path> [-mhdeq] [-test] \
  !                 [-mdescr:<mdescr_filename> (multiple -mdescr files OK)] \
  !                 [-prefix:<prefix>  -all_times]
  !                 <time1> <file1>   <time2> <file2>  ... ...

  !  these argument(s) are optional and can be positioned anywhere
  !      -mhdeq   (if present, write Plasma States with all available MHD
  !                equilibrium data; if not, write reduced states with 1d
  !                profiles only).
  !      -test    (if present some additional debugging tests are done;
  !                this is for the benefit of code developers).
  !  (additional option arguments may be added, see error message printout...)

  !  these arguments are required and must be in order.  It what follows
  !  "first (second) (third) argument" means first (second) (third) 
  !  non-optional argument.

  !     1st argument:  literal, either FILE or MDS+
  !     indicates path to TRANSP data is through FILES or through an MDS+
  !     server

  !     2nd argument:  <data-path> -- path to TRANSP data; syntax depends
  !     on 1st argument.  
  !        If 1st argument is "FILE": 2nd argument is of
  !     the form <directory-path>/<runid> and the run files are expected
  !     to be found in <runid>* at the indicated <directory-path>.  If the
  !     <directory-path> is omitted, the program looks in the current 
  !     working directory for files <runid>*.  The files that will be
  !     read (as of July 2010) are:  <runid>.CDF  <runid>PH.CDF  <runid>TR.DAT
  !     It is usually possible to download these files, for sites without
  !     MDS+ client capability.
  !        If 1st argument is "MDS+": 2nd argument is a TRANSP-enhanced
  !     MDS+ path.  This has a general syntax of <server>:<tree>(<runid>)
  !     but some details vary depending on the server:
  !        ...PPPL server: transpgrid.pppl.gov:TRANSP_<tok>(<tok>,<runid>)
  !        ...MIT server:  cmoda.psfc.mit.edu:<treename>(<shot-number>)
  !        ...GA server:   atlas.gat.com:TRANSP(<transp-number>)
  !     Caution: as of July 2010 only the PPPL path has been tested!
  !     Here are specific examples of valid paths:
  !        "transpgrid.pppl.gov:TRANSP_TFTR(TFTR,37065P35)"
  !        "transpgrid.pppl.gov:TRANSP_D3D(D3D,133137B04)"
  !     NOTE, to find a TRANSP appropriate for research use, it is important
  !     to consult with the run author or somebody with detailed familiarity
  !     with the data.

  !     3rd argument:  <output-path> -- directory where output data can
  !     be written.  Can be "."; but in any case, a valid, writable path 
  !     must be provided.

  !     remaining arguments come in pairs and there can be as many or as
  !     few as desired:

  !     4th and 5th arguments  <time1> <file1> 
  !     ...the time at which to extract data, and
  !     ...the file root name to write
  !     Example:  1.234  ps1x234
  !        ---> data from time t=1.234 seconds in the TRANSP run is written
  !             into a Plasma State NetCDF file <output-path>/ps1x234.cdf

  !     6th and 7th arguments  <time2> <file2>

  !     8th and 9th arguments  <time3> <file3>

  !     10th and 11th arguments  <time4> <file4>

  !         --etc--

  !--------------------------------------------------------------------------
  USE plasma_state_mod
  implicit NONE

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

  type (plasma_state) :: ss

  integer :: iarg,nargs   ! command line arguments
  integer :: iargct       ! non-option command argument counter
  integer :: ilen         ! non-blank length of argument string

  integer :: ierr         ! error indicator

  character*250 :: argval,save_argval ! argument value
  character*250 :: dpath,opath  ! <data-path> and <output-path>

  logical :: mds_mode     ! .TRUE. if 1st arg is MDS+
  character*4 :: mode_str

  logical :: test_flag    ! -test indicator
  logical :: mhdeq_flag   ! -mhdeq indicator
  logical :: all_times    ! -all_times indicator

  integer :: iBccw        ! B field direction option
  integer :: iJccw        ! current direction option

  logical :: found_filename
  integer :: istat,ierloc

  character*60 ztest,all_times_prefix
  character*20 zvalr8

  integer :: ntimes  ! number of time values found
  integer :: itime
  real*8, dimension(:), allocatable :: timevals  ! the time values
  character*200, dimension(:), allocatable :: filenams  ! the filename roots

  integer, parameter :: nonlin = 6  ! output unit for messages

  !-------------------------------------------
  !  Part 1: parse command line arguments
  !-------------------------------------------

  write(nonlin,*) ' => this is trxpl2ps version DMC 16-Dec-2010'
  write(nonlin,*) '    (TRANSP archives Plasma State Extractor)'
  test_flag = .FALSE.
  mhdeq_flag = .FALSE.
  mds_mode = .FALSE.
  all_times = .FALSE.
  all_times_prefix = "trps_"

  iBccw = 0
  iJccw = 0

  dpath = '.'
  opath = '.'

  call trxplib_clear_mdescr

  call get_arg_count(nargs)

  ntimes=0

  !  here, more are allocated than are needed...
  allocate(timevals(nargs),filenams(nargs))
  timevals=0.0d0
  filenams=" "

  ierr = 0
  iargct = 0

  iarg = 0
  do 
     iarg = iarg+1
     if(iarg.gt.nargs) exit

     call get_arg(iarg,argval)
     ilen = len(trim(argval))

     ! parse arguments

     if(argval(1:1).eq.'-') then
        ztest=argval(2:)
        call opt_parse
        cycle
     endif

     !  not a "-" optional argument 
     iargct = iargct + 1

     if(iargct.eq.1) then
        !  MDS+ or FILE expected...

        ztest=argval
        if(ztest.eq.'MDS+') then
           mds_mode = .TRUE.
        else if(ztest.eq.'FILE') then
           mds_mode = .FALSE.
        else
           write(nonlin,*) ' ?trxpl2ps: invalid lead argument (expected: FILE or MDS+)'
           ierr=ierr+1
           exit
        endif
     endif

     if(iargct.eq.2) then
        !  path to run...
        dpath = argval
        cycle
     endif

     if(iargct.eq.3) then
        !  output path...
        opath = argval
        cycle
     endif

     if(iargct.gt.3) then
        !  looking for <time> <state_file> pairs...
        
        call time_parse    ! increment ntimes, store value, if successful
        if(ierr.ne.0) exit
        
        !  look for matching argument, skipping over -<opt> arguments
        
        save_argval = argval
        found_filename = .FALSE.
        
        do 
           iarg = iarg+1
           if(iarg.gt.nargs) exit
           
           call get_arg(iarg,argval)
           ilen = len(trim(argval))

           !  check here, too, for -opt arguments
           if(argval(1:1).eq.'-') then
              ztest=argval(2:)
              call opt_parse
              cycle
           endif

           !  OK, have filename argument

           filenams(ntimes) = trim(opath)//'/'//trim(argval)
           found_filename = .TRUE.
           exit
        enddo

        if(.NOT.found_filename) then
           write(nonlin,*) ' ?trxpl2ps: no filename argument found to match time: '//trim(save_argval)
           ierr=1
           exit
        endif

     endif

  enddo

  !-------------------------------------------
  !  2. check results of parse; write messages, exit if necessary
  !-------------------------------------------

  if(ierr.eq.0) then
     if(iargct.lt.3) then
        write(nonlin,*) ' ?trxpl2ps: too few arguments... '
        ierr = 1
     endif
     if(iargct.eq.3) then
        write(nonlin,*) ' %trxpl2ps: no output files specified (OK).'
     endif
  endif

  if(ierr.gt.0) then
     write(nonlin,*) ' '
     write(nonlin,*) 'trxpl2ps syntax:'
     write(nonlin,*) '  trxpl2ps  FILE|MDS+  <data-path>  <output-directory> \ '
     write(nonlin,*) '          [-test]  \ (for extra message output) '
     write(nonlin,*) '          [-mhdeq] \ (to include 2d MHD flux surfaces in state file output)'
     write(nonlin,*) '          [-Bccw]   [-Bcw] \ (to specify toroidal B field orientation) '
     write(nonlin,*) '             (if neither is set, use TRANSP data)'
     write(nonlin,*) '          [-Jccw]   [-Jcw] \ (to specify toroidal current orientation) '
     write(nonlin,*) '             (if neither is set, use TRANSP data)'
     write(nonlin,*) '          [-all_times] \ (for all available times in <prefix><timestamp>.cdf'
     write(nonlin,*) '              state files)'
     write(nonlin,*) '          [-prefix:<prefix> \ (prefix for -all_times files; default "trps_")'
     write(nonlin,*) '          [-mdescr:<machine-description-filename>] \ '
     write(nonlin,*) '             (more than one can be given)'
     write(nonlin,*) '          <time1> <state_file1> \ (".cdf" appended to filenames)'
     write(nonlin,*) '          <time2> <state_file2> \ '
     write(nonlin,*) '          <time3> <state_file3> \ '
     write(nonlin,*) '          <time4> <state_file4> \ '
     write(nonlin,*) '            ...     ...'
     write(nonlin,*) '  FILE <data-path> has form <directory>/<runid>, for example:'
     write(nonlin,*) '     FILE /p/transpusers/dmccune/output/TFTR.88/37065Y06'
     write(nonlin,*) '  MDS+ <data-path> syntax depends on server; PPPL example:'
     write(nonlin,*) '     MDS+ "transpgrid.pppl.gov:TRANSP_TFTR(TFTR,37065P36)"'
     write(nonlin,*) '     (hint: enclose path in quotes)'
     write(nonlin,*) '  if -all_times is used, <output>/protimes.dat contains the times'
     write(nonlin,*) '  at which state files are written, along with the file names.'
     call exit(ierr)
  endif

  if(test_flag) then
     write(nonlin,*) ' '
     if(mds_mode) then
        write(nonlin,*) ' data mode: MDS+ '
     else
        write(nonlin,*) ' data mode: FILE '
     endif
     write(nonlin,*) ' data path: '//trim(dpath)
     write(nonlin,*) ' output path: '//trim(opath)

     if(mhdeq_flag) then
        write(nonlin,*) ' -mhdeq option: SET'
     else
        write(nonlin,*) ' -mhdeq option: INACTIVE'
     endif

     if(ntimes.gt.0) then
        write(nonlin,*) ' '
        write(nonlin,*) '   #    time          filename_root'
     endif

     do itime=1,ntimes
        write(nonlin,'(1x,i4,4x,1pe12.5,3x,a)') itime,timevals(itime),filenams(itime)
     enddo
  endif

  !-------------------------------------------
  !  3.  OK: init working state; set options & connect to run
  !--------------------------------------------

  call ps_init_user_state(ss, "trxpl2ps", ierr)
  if(ierr.ne.0) then
     write(nonlin,*) ' ?trxpl2ps: work state initialization failure.'
     call exit(10)
  endif

  call trxplib_ps_reset
  call trxplib_mhdeq_opt(mhdeq_flag)
  call trxplib_ccw_options(iBccw,iJccw)

  if(mds_mode) then
     mode_str = "MDS+"
  else
     mode_str = "FILE"
  endif

  call trxplib_ps_open(mode_str, dpath, opath, all_times_prefix, ierr)
  if(ierr.ne.0) then
     write(nonlin,*) ' ?trxpl2ps: connect failure.'
     call exit(11)
  endif

  !-------------------------------------------
  !  4.  OK: process requested times of interest
  !      (explicitly requested on command line)
  !-------------------------------------------

  do itime=1,ntimes

     call trxplib_ps_get(timevals(itime), ss, ierr)
     if(ierr.ne.0) exit

     call ps_store_plasma_state(ierr, state=ss, &
          filename=trim(filenams(itime))//'.cdf')
     if(ierr.ne.0) exit

  enddo

  if(ierr.ne.0) then
     write(nonlin,*) ' ?trxpl2ps: error during state extraction time loop. '
     call exit(12)
  endif

  !-------------------------------------------
  !  5.  OK: process requested times of interest
  !      (via -all_times command line option)
  !-------------------------------------------

  if(all_times) then
     call load_times

     do itime=1,ntimes

        call trxplib_ps_get(timevals(itime), ss, ierr)
        if(ierr.ne.0) exit

        call ps_store_plasma_state(ierr, state=ss, &
             filename=trim(filenams(itime))//'.cdf')
        if(ierr.ne.0) exit

     enddo

     if(ierr.ne.0) then
        write(nonlin,*) ' ?trxpl2ps: error during all_times time loop. '
        call exit(13)
     endif
  endif

  !-------------------------------------------
  !  6.  Cleanup and exit
  !-------------------------------------------

  call ps_free_user_state(ss, ierloc)
  if(ierloc.ne.0) then
     write(nonlin,*) ' ?trxpl2ps: error deallocating work state (ignored).'
  endif

  call trxplib_ps_close
  stop

  !---------------------------------------------------
CONTAINS
  !---------------------------------------------------
  !  parse subroutines

  subroutine opt_parse

    integer :: ierloc

    if(ztest.eq.'mhdeq') then
       mhdeq_flag = .TRUE.
    else if(ztest.eq.'test') then
       test_flag = .TRUE.
    else if(ztest.eq.'Bccw') then
       iBccw=1
    else if(ztest.eq.'Bcw') then
       iBccw=-1
    else if(ztest.eq.'Jccw') then
       iJccw=1
    else if(ztest.eq.'Jcw') then
       iJccw=-1
    else if(ztest.eq.'all_times') then
       all_times=.TRUE.
    else if(ztest(1:7).eq.'mdescr:') then
       if(ztest(8:8).eq.' ') then
          write(nonlin,*) ' ?trxpl2ps: -mdescr: missing name.'
          ierr=ierr+1
       else
          call trxplib_add_mdescr(argval(9:),ierloc)
          if(ierloc.ne.0) then
             write(nonlin,*) ' ?trxpl2ps: machine description not found: '// &
                  trim(argval)
             ierr=ierr+1
          endif
       endif
    else if(ztest(1:7).eq.'prefix:') then
       all_times_prefix = ztest(8:)
    else
       ierr=ierr+1
       write(nonlin,*) ' ?trxpl2ps: unrecognized: '//trim(argval)
    endif

  end subroutine opt_parse

  subroutine time_parse

    real*8 :: r8val

    if(ilen.gt.20) then
       ierr=ierr+1
       write(nonlin,*) ' ?trxpl2ps: time-value argument too long: '//trim(argval)
    else
       zvalr8=' '
       zvalr8(20-ilen+1:20) = argval(1:ilen)
       read(zvalr8,'(G20.0)',iostat=istat) r8val
       if(istat.ne.0) then
          ierr=ierr+1
          write(nonlin,*) ' ?trxpl2ps: decode failed on time-value argument: '//trim(argval)
       else
          ntimes = ntimes + 1
          timevals(ntimes) = r8val
       endif
    endif

  end subroutine time_parse

  subroutine load_times

    ! read <output>/protimes.dat

    integer :: ilun,istat,it,idot
    character*50 ptfile,tmpfile

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

    ierr = 0

    ntimes=0
    if(allocated(timevals)) deallocate(timevals)
    if(allocated(filenams)) deallocate(filenams)

    call find_io_unit(ilun)
    ptfile = trim(opath)//'/'//'protimes.dat'
    open(ilun,file=ptfile,status='old',iostat=istat)

    if(istat.ne.0) then
       write(nonlin,*) ' ?trxpl2ps: open failure: '//trim(ptfile)
       ierr = 1
       return
    endif

    ! get #pts

    read(unit=ilun,fmt='(1x,i5)',iostat=istat) ntimes
    if(istat.ne.0) then
       write(nonlin,*) ' ?trxpl2ps: #times read failure: '//trim(ptfile)
       close(unit=ilun)
       ierr = 1
       return
    endif

    allocate(timevals(ntimes))
    allocate(filenams(ntimes))

    do it=1,ntimes
       read(unit=ilun,fmt='(1x,1pe13.6,6x,a)',iostat=istat) &
            timevals(it),tmpfile
       if(istat.ne.0) then
          write(nonlin,*) ' ?trxpl2ps: time point read failure: '//trim(ptfile)
          write(nonlin,*) '  time index counter: ',it
          ierr = 1
          exit
       endif
       ! trim suffix
       idot = index(tmpfile,'.cdf')
       if(idot.gt.0) then
          tmpfile(idot:) = ' '
       endif
       filenams(it) = trim(opath)//'/'//trim(tmpfile)
    enddo

    close(unit=ilun)

  end subroutine load_times

end program trxpl2ps
