using fortran from within NCL

From: Dennis Shea (shea AT XXXXXX)
Date: Tue Apr 16 2002 - 13:30:53 MDT

  • Next message: Mary Haley: "Re: pressure/height vs lon"

    Hello to all,

    This is not in response to any specific question. However,
    I have received several questions regarding using fortran
    codes from NCL. I thought I would post several emails about
    this subject. The emails will appear several days apart.

    The first post uses a fortran 77 subroutines to:
    (i) read data from an ascii file, (2) do some (trivial) computations
    and (3) then print out the data/results.

    The examples are simple but illustrate the basic concepts.

    The following URL has additional details:
      http://www.cgd.ucar.edu/csm/support/Document/pdf/fortran.pdf

    For convenience:
        (1) I will present the various steps in a C-shell script (see below).
            The pedantic advantage of this approach is that
            the fortran, WRAPIT and NCL are contained in one script.

        (2) I will assume that users have a relatively recent
            version of NCL. FYI: as of 10 April 2002 the release is
            4.2.0.a023

    When might a user want to use fortran from an NCL script?

        NCL (like IDL, Matlab, ferret, yorick) is an interpreted
        language. As such, use of 'do loops' should be minimized
        because interpreted languages do not optimize loops. Thus,
        when multi-level do loops are required and time is
        important using fortran is the best option.

        Reading 'complicated' ascii files may be best done via
        fortran formatted read statements.

        Formatting text via NCL is possible but can be tedious.
        This approach would involve using NCL's built-in
        functions "sprinti" and "sprintf". However, creating nice
        tabular output via fortran is often easier.

        Sometimes you may have an existing fortran subroutine that
        performs a calculation and you do not want to rewrite it in NCL.

        etc
    -----------------
    Example 1: Reading an ascii file that has imbedded characters.
        Consider the following ascii file:

     YY/MM/DD HH:MM HH.MM Kd Kn Max Ratio
     01/08/12 00:00 0.00 547.5 439.3 528.9 1.04
     01/08/12 00:05 0.08 414.2 321.6 517.5 0.80
     01/08/12 00:10 0.17 562.6 445.5 494.5 1.14
     01/08/12 00:15 0.25 426.3 338.7 482.7 0.88
     01/08/12 00:20 0.33 486.2 382.8 470.9 1.03
     01/08/12 00:25 0.42 480.9 383.2 459.0 1.05
     01/08/12 00:30 0.50 339.6 262.6 434.8 0.78
     01/08/12 00:35 0.58 382.8 296.0 422.6 0.91
     01/08/12 00:40 0.67 360.7 284.4 410.3 0.88
     01/08/12 00:45 0.75 308.9 237.7 397.9 0.78
     01/08/12 00:50 0.83 407.4 314.5 372.8 1.09
     01/08/12 00:55 0.92 357.3 275.9 360.2 0.99
     01/08/12 01:00 1.00 249.2 187.5 347.5 0.72
     01/08/12 01:05 1.08 242.9 182.8 334.7 0.73

    Yes, you can read this via NCL but for illustration we
    will read it via fortran.

    The following comments are associated with the script below:

     (1) Note the delimiters 'C NCLFORTSTART' and 'C NCLEND'
         in the beginning of the subroutines. These
         enclose all declarations pertaining to the arguments
         being passed. They should appear only in subroutines
         being called from NCL. To the fortran compiler,
         they are merely comment lines.

     (2) After 'C NCLEND' the code may include other declarations
         (eg: data statement, common blocks, additional local memory, etc)

     (3) You may have any number of fortran subroutines.

     (4) WRAPIT is a script which is distributed with more recent versions
         of NCL. It performs the various steps necessary
         to create the shared object (.so). In addition, WRAPIT
         removes any temporary files. Incidentally, the suffix
         '.so' is merely a common convention.

         By default the shared object prefix will be the same prefix
         as the 1st file (here "sub"). You can use the -n option
         to specify a different name.

         WRAPIT runs on a number of different systems.

         Mark Stevens (CGD) provides many examples within
         the WRAPIT function. Feel free to look at it.
         It is located in $NCARG_ROOT/bin/WRAPIT

     (5) Use "new" to preallocate memory for the
         variables being returned from the fortran subroutine.
         The variable type (eg: float, double) should agree between
         NCL and fortran: integer-to-integer, float-to-real,
         double-to-double precision.

     (6) the external line in the NCL script provides an *arbitrary*
         identifier to the shared object. By convention it is capitalized
         but this is not required. This identifier can have any name.

    #!/usr/bin/csh

    # ===========================FORTRAN===================================
     
    cat >! sub.f << "END_FORTRAN"
    C NCLFORTSTART
          subroutine rddata(filnam,nn ,yy ,mo ,dd ,hh ,mm
         + ,hhmm, kd, kn, zmax, rat)
          implicit none
          character*(*) filnam
          integer nn
          integer yy(nn), mo(nn), dd(nn),hh(nn), mm(nn)
          real hhmm(nn), kd(nn), kn(nn), zmax(nn), rat(nn)
    C NCLEND
          integer k
          character*80 skip

          open (unit=10,file=filnam,form="formatted",access="sequential")

    c read/ignore the first record. This is just ascii header stuff
          read (10,"(a)") skip

    c read the desired variables
          do k=1,nn
             read(10,"(1x,5(i2,1x),f5.2,f7.2,3(1x,f7.2) )")
         + yy(k), mo(k), dd(k), hh(k), mm(k)
         + ,hhmm(k), kd(k), kn(k), zmax(k), rat(k)
          end do

          return
          end

    C NCLFORTSTART
          subroutine compute(nn,kd, zmax, kd2, ratio)
          implicit none
          integer nn
          real kd(nn), zmax(nn)
          real kd2(nn), ratio(nn)
    C NCLEND
          integer k

    C trivial computations
          do k=1,nn
             kd2(k) = 2.*kd(k)
             ratio(k) = zmax(k)/kd2(k)
          end do

          return
          end

    C NCLFORTSTART
          subroutine prdata(head ,nn ,yy ,mo ,dd ,hh ,mm
         + ,hhmm, kd, kn, zmax, rat)
          implicit none
          character*(*) head
          integer nn
          integer yy(nn), mo(nn), dd(nn),hh(nn), mm(nn)
          real hhmm(nn), kd(nn), kn(nn), zmax(nn), rat(nn)
    C NCLEND
          integer k

    c space down 3 lines then print out header
          write (*,"(///,10x,a)") head

    c print column headers
          write(*,"(3x,'YY/MM/DD',1x,'HH:MM',1x,'hh.mm',5x,'Kd*2'
         + 3x,'sqrt(Kn)',5x,'Max',2x,'NewRatio')")

    c write the desired variables
          do k=1,nn
             write(*,"(3x,5(i2,1x),f5.2,2x,f7.2,2x,f8.2,2(2x,f7.2) )")
         + yy(k), mo(k), dd(k), hh(k), mm(k)
         + ,hhmm(k), kd(k), kn(k), zmax(k), rat(k)
          end do

          return
          end
    "END_FORTRAN"

    # =========================== WRAPIT===================================

    # $NCARG_ROOT/bin/WRAPIT sub.f # create sub.so
      WRAPIT sub.f

    # ==============================NCL====================================

    cat >! main.ncl << "END_NCL"
                            ; commented out cuz not used here
    ; load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/gsn_code.ncl"
    ; load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/gsn_csm.ncl"
    ; load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/contributed.ncl"

    external SUB "./sub.so" ; fortran shared object produced by WRAPIT

    begin
      diri = "./"
      fili = "ascii.data"
      fname= diri+fili
                            ; access the file, determine # records
      a = asciiread(fname, -1, "string")
      nRow = dimsizes(a) ; # rows (records)

      nrow = nRow-1 ; remember: skip the 1st rec
      delete(a)
                            ; preallocate to hold data
                            ; returned from fortran
      yy = new (nrow, "integer")
      mo = new (nrow, "integer")
      dd = new (nrow, "integer")
      hh = new (nrow, "integer")
      mm = new (nrow, "integer")

      hhmm = new (nrow, "float")
      Kd = new (nrow, "float")
      Kn = new (nrow, "float")
      zmax = new (nrow, "float")
      rat = new (nrow, "float")

      SUB::rddata(fname, nrow ,yy ,mo ,dd ,hh ,mm \
                 ,hhmm, Kd, Kn, zmax, rat)

      print ("======> NCL print <=====")
      print (yy+" "+mo+" "+dd+" "+hh+" "+mm+" " \
            +hhmm+" "+Kd+" "+Kn+" "+zmax+" "+rat)

      Kn = sqrt(Kn) ; overwrite Kn with sqrt (array syntax)
     
      Kd2 = new (dimsizes(Kd), typeof(Kd) ) ; Kd2 = 2.*Kd
      RAT = new (dimsizes(Kd), typeof(Kd) ) ; RAT = zmax/Kd2

      SUB::compute(nrow,Kd,zmax,Kd2,RAT)

      title = "+++ Fortran print of Bogus New Data +++"

      SUB::prdata(title, nrow ,yy ,mo ,dd ,hh ,mm \
                 ,hhmm, Kd2, Kn, zmax, RAT)
    end
    "END_NCL"

    # ============================EXECUTE==================================

    ncl < main.ncl
    ; ncl < main.ncl >&! z.out # send ncl output to a specific file

    # ====================CLEAN UP TEMPORARY FILES=========================

    /usr/bin/rm sub.f
    /usr/bin/rm sub.so
    /usr/bin/rm main.ncl
    exit

    ++++++++++++++++++++++++++++++++++++++++++++=
    Here is the output produced by the above script.
    ++++++++++++++++++++++++++++++++++++++++++++=

     See http://ngwww.ucar.edu/ncl/ for more details.
    (0) ======> NCL print <=====
    (0) 1 8 12 0 0 0 547.5 439.3 528.9 1.04
    (1) 1 8 12 0 5 0.08 414.2 321.6 517.5 0.8
    (2) 1 8 12 0 10 0.17 562.6 445.5 494.5 1.14
    (3) 1 8 12 0 15 0.25 426.3 338.7 482.7 0.88
    (4) 1 8 12 0 20 0.33 486.2 382.8 470.9 1.03
    (5) 1 8 12 0 25 0.42 480.9 383.2 459 1.05
    (6) 1 8 12 0 30 0.5 339.6 262.6 434.8 0.78
    (7) 1 8 12 0 35 0.58 382.8 296 422.6 0.91
    (8) 1 8 12 0 40 0.67 360.7 284.4 410.3 0.88
    (9) 1 8 12 0 45 0.75 308.9 237.7 397.9 0.78
    (10) 1 8 12 0 50 0.83 407.4 314.5 372.8 1.09
    (11) 1 8 12 0 55 0.92 357.3 275.9 360.2 0.99
    (12) 1 8 12 1 0 1 249.2 187.5 347.5 0.72
    (13) 1 8 12 1 5 1.08 242.9 182.8 334.7 0.73
    (0) ========

              +++ Fortran print of Bogus New Data +++
       YY/MM/DD HH:MM hh.mm Kd*2 sqrt(Kn) Max NewRatio
        1 8 12 0 0 0.00 1095.00 439.30 528.90 0.48
        1 8 12 0 5 0.08 828.40 321.60 517.50 0.62
        1 8 12 0 10 0.17 1125.20 445.50 494.50 0.44
        1 8 12 0 20 0.33 972.40 382.80 470.90 0.48
        1 8 12 0 25 0.42 961.80 383.20 459.00 0.48
        1 8 12 0 30 0.50 679.20 262.60 434.80 0.64
        1 8 12 0 35 0.58 765.60 296.00 422.60 0.55
        1 8 12 0 40 0.67 721.40 284.40 410.30 0.57
        1 8 12 0 45 0.75 617.80 237.70 397.90 0.64
        1 8 12 0 50 0.83 814.80 314.50 372.80 0.46
        1 8 12 0 55 0.92 714.60 275.90 360.20 0.50
        1 8 12 1 0 1.00 498.40 187.50 347.50 0.70
        1 8 12 1 5 1.08 485.80 182.80 334.70 0.69



    This archive was generated by hypermail 2b29 : Tue Apr 16 2002 - 14:06:53 MDT